From b698f5166b226e48730c789cf1e3e5500921a801 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 18 Jun 2024 01:33:50 -0400 Subject: [PATCH] Base input pass updated for sokol possibly done (untested) Still need to figure out input event consumption, I don't want to do it with the event ring. I would like to setup input binding layers and then have the push/pop input contextes with a set of bindings. If the bindings are detected it should "consume" that binding from further use for the buffered time period. This will be really important with how heavily model this app will be.I --- code/grime/hot_reload.odin | 18 ++ code/grime/mappings.odin | 23 ++ code/grime/queue.odin | 61 +++++ code/host/host.odin | 2 +- code/sectr/app/screen.odin | 8 +- code/sectr/app/state.odin | 2 + code/sectr/engine/client_api.odin | 33 ++- .../engine/client_api_sokol_callbacks.odin | 32 ++- code/sectr/engine/update.odin | 7 +- code/sectr/grime/mappings.odin | 67 ++++- code/sectr/input/event.odin | 63 ----- code/sectr/input/events.odin | 259 ++++++++++++++++++ code/sectr/input/input.odin | 14 +- code/sectr/input/input_sokol.odin | 49 +--- 14 files changed, 487 insertions(+), 151 deletions(-) create mode 100644 code/grime/hot_reload.odin create mode 100644 code/grime/queue.odin delete mode 100644 code/sectr/input/event.odin create mode 100644 code/sectr/input/events.odin diff --git a/code/grime/hot_reload.odin b/code/grime/hot_reload.odin new file mode 100644 index 0000000..f0af0a8 --- /dev/null +++ b/code/grime/hot_reload.odin @@ -0,0 +1,18 @@ +package grime + +import "base:runtime" + +reload_array :: proc( self : [dynamic]$Type, allocator : Allocator ) { + raw := transmute(runtime.Raw_Dynamic_Array) self + raw.allocator = allocator +} + +reload_queue :: proc( self : Queue($Type), allocator : Allocator ) { + raw_array := transmute(runtime.Raw_Dynamic_Array) self.data + raw_array.allocator = allocator +} + +reload_map :: proc( self : map [$KeyType] $EntryType, allocator : Allocator ) { + raw := transmute(runtime.Raw_Map) self + raw.allocator = allocator +} diff --git a/code/grime/mappings.odin b/code/grime/mappings.odin index e4bee15..824789a 100644 --- a/code/grime/mappings.odin +++ b/code/grime/mappings.odin @@ -29,6 +29,9 @@ import "base:runtime" import c "core:c/libc" mem_fmt :: c.memset +import "core:container/queue" + Queue :: queue.Queue + import "core:dynlib" import "core:hash" @@ -143,11 +146,17 @@ is_power_of_two :: proc { is_power_of_two_uintptr, } +iterator :: proc { + iterator_queue, +} + make :: proc { array_init, hmap_chained_init, hmap_zpl_init, + make_queue, + // Usual make_slice, make_dynamic_array, @@ -157,10 +166,24 @@ make :: proc { make_multi_pointer, } +next :: proc { + next_queue_iterator, +} + push :: proc { stack_push, } +space_left :: proc { + queue.space, +} + +reload :: proc { + reload_array, + reload_queue, + reload_map, +} + to_runes :: proc { string_to_runes, } diff --git a/code/grime/queue.odin b/code/grime/queue.odin new file mode 100644 index 0000000..465f98f --- /dev/null +++ b/code/grime/queue.odin @@ -0,0 +1,61 @@ +package grime + +import "core:container/queue" + +make_queue :: proc( $QueueType : typeid/Queue($Type), capacity := queue.DEFAULT_CAPACITY, allocator := context.allocator ) -> (result : Queue(Type), error : AllocatorError) +{ + queue.init( & result, capacity, allocator ) + return +} + +push_back_slice_queue :: proc( self : ^$QueueType / Queue($Type), slice : []Type ) -> ( error : AllocatorError ) +{ + num := cast(uint) len(slice) + + if uint( space_left( self^ )) < num { + error = queue._grow( self, self.len + num ) + if error != .None do return + } + + size := uint(len(self.data)) + insert_from := (self.offset + self.len) % size + insert_to := num + + if insert_from + insert_to > size { + insert_to = size - insert_from + } + + copy( self.data[ insert_from : ], slice[ : insert_to ]) + copy( self.data[ : insert_from ], slice[ insert_to : ]) + return +} + +QueueIterator :: struct( $Type : typeid ) { + data : []Type, + length : uint, + offset : uint, + index : uint, +} + +iterator_queue :: proc( queue : $QueueType / Queue($Type) ) -> QueueIterator(Type) +{ + iter := QueueIterator(Type) { + data = queue.data[:], + length = queue.len, + offset = queue.offset, + index = 0, + } + return iter +} + +next_queue_iterator :: proc( iter : ^QueueIterator($Type) ) -> ^Type +{ + using iter + front_id := (length + offset ) % len(data) + elem_id := (length + offset - index) % len(data) + if elem_id == front_id do return nil + + elem := & data[ elem_id ] + index += 1 + return elem +} diff --git a/code/host/host.odin b/code/host/host.odin index d781e0b..b6fa708 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -181,7 +181,7 @@ load_sectr_api :: proc( version_id : i32 ) -> (loaded_module : sectr.ModuleAPI) startup := cast( type_of( sectr.startup )) os_lib_get_proc( lib, "startup" ) shutdown := cast( type_of( sectr.sectr_shutdown )) os_lib_get_proc( lib, "sectr_shutdown" ) - reload := cast( type_of( sectr.reload )) os_lib_get_proc( lib, "reload" ) + reload := cast( type_of( sectr.hot_reload )) os_lib_get_proc( lib, "hot_reload" ) tick := cast( type_of( sectr.tick )) os_lib_get_proc( lib, "tick" ) clean_frame := cast( type_of( sectr.clean_frame )) os_lib_get_proc( lib, "clean_frame" ) diff --git a/code/sectr/app/screen.odin b/code/sectr/app/screen.odin index f086773..0bdcd96 100644 --- a/code/sectr/app/screen.odin +++ b/code/sectr/app/screen.odin @@ -252,8 +252,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b } if input_box.active { - array_append( & value_str, input.codes_pressed ) - array_clear( input.codes_pressed ) + append( & value_str, input_events.codes_pressed ) + clear( input_events.codes_pressed ) } else if input_box.was_active { @@ -264,8 +264,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b } else { - array_clear( value_str) - array_append( & value_str, to_runes(str_fmt("%v", config.engine_refresh_hz))) + clear( value_str) + append( & value_str, to_runes(str_fmt("%v", config.engine_refresh_hz))) } ui_parent(input_box) diff --git a/code/sectr/app/state.odin b/code/sectr/app/state.odin index dcfc4d6..2e13971 100644 --- a/code/sectr/app/state.odin +++ b/code/sectr/app/state.odin @@ -210,6 +210,8 @@ State :: struct { input_prev : ^InputState, input : ^InputState, + input_events : InputEvents, + // Note(Ed): Do not modify directly, use its interface in app/event.odin staged_input_events : Array(InputEvent), // TODO(Ed): Add a multi-threaded guard for accessing or mutating staged_input_events. diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index a3b6a58..fb8fab7 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -30,7 +30,7 @@ ModuleAPI :: struct { startup : type_of( startup ), shutdown : type_of( sectr_shutdown ), - reload : type_of( reload ), + reload : type_of( hot_reload ), tick : type_of( tick ), clean_frame : type_of( clean_frame ), } @@ -107,24 +107,23 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem { input = & input_data[1] input_prev = & input_data[0] - for & input in input_data { - using input - error : AllocatorError - events, error = make( Array(InputEvent), Kilo, persistent_slab_allocator() ) - ensure(error == AllocatorError.None, "Failed to allocate input.events array") + using input_events - key_events, error = make( Array(InputKeyEvent), Kilo, persistent_slab_allocator() ) - ensure(error == AllocatorError.None, "Failed to allocate key_events array") + error : AllocatorError + events, error = make( Queue(InputEvent), 4 * Kilo, persistent_allocator() ) + ensure(error == AllocatorError.None, "Failed to allocate input.events array") - mouse_events, error = make( Array(InputMouseEvent), Kilo, persistent_slab_allocator() ) - ensure(error == AllocatorError.None, "Failed to allocate mouse_events array") + key_events, error = make( Queue(InputKeyEvent), Kilo, persistent_allocator() ) + ensure(error == AllocatorError.None, "Failed to allocate key_events array") - codes_pressed, error = make( Array(rune), Kilo, persistent_slab_allocator() ) - ensure(error == AllocatorError.None, "Failed to allocate codes_pressed array") - } + mouse_events, error = make( Queue(InputMouseEvent), 2 * Kilo, persistent_allocator() ) + ensure(error == AllocatorError.None, "Failed to allocate mouse_events array") - input_staged_events, error := make( Array(InputEvent), Kilo, persistent_slab_allocator() ) + codes_pressed, error = make( Array(rune), Kilo, persistent_allocator() ) + ensure(error == AllocatorError.None, "Failed to allocate codes_pressed array") + + staged_input_events, error = make( Array(InputEvent), 8 * Kilo, persistent_allocator() ) ensure(error == AllocatorError.None, "Failed to allocate input_staged_events array") } @@ -396,7 +395,7 @@ sectr_shutdown :: proc() } @export -reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger ) +hot_reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger ) { spall.SCOPED_EVENT( & prof.ctx, & prof.buffer, #procedure ) set_profiler_module_context( prof ) @@ -484,7 +483,6 @@ tick :: proc( host_delta_time_ms : f64, host_delta_ns : Duration ) -> b32 return ! should_close } - // Lifted out of tick so that sokol_app_frame_callback can do it as well. tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32 { @@ -542,6 +540,7 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32 // Lifted out of tick so that sokol_app_frame_callback can do it as well. tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_ms : f64, host_delta_ns : Duration ) { + profile(#procedure) state := get_state(); using state context.allocator = frame_slab_allocator() context.temp_allocator = transient_allocator() @@ -598,7 +597,7 @@ tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_ @export clean_frame :: proc() { - // profile( #procedure) + profile( #procedure) state := get_state(); using state context.logger = to_odin_logger( & Memory_App.logger ) diff --git a/code/sectr/engine/client_api_sokol_callbacks.odin b/code/sectr/engine/client_api_sokol_callbacks.odin index ae8cfe9..68ef894 100644 --- a/code/sectr/engine/client_api_sokol_callbacks.odin +++ b/code/sectr/engine/client_api_sokol_callbacks.odin @@ -15,10 +15,12 @@ sokol_app_init_callback :: proc "c" () { // This is being filled in but we're directly controlling the lifetime of sokol_app's execution. // So this will only get called during window pan or resize events (on Win32 at least) -sokol_app_frame_callback :: proc "c" () { +sokol_app_frame_callback :: proc "c" () +{ context = get_state().sokol_context - state := get_state() + profile(#procedure) + state := get_state() should_close : b32 sokol_width := sokol_app.widthf() @@ -38,10 +40,12 @@ sokol_app_frame_callback :: proc "c" () { sokol_delta_ms := sokol_app.frame_delta() sokol_delta_ns := transmute(Duration) sokol_delta_ms * MS_To_NS + profile_begin("Client Tick") client_tick := time.tick_now() should_close |= tick_work_frame( sokol_delta_ms ) - tick_frametime( & client_tick, sokol_delta_ms, sokol_delta_ns ) + profile_end() + tick_frametime( & client_tick, sokol_delta_ms, sokol_delta_ns ) window.resized = false } @@ -118,43 +122,52 @@ sokol_app_event_callback :: proc "c" (sokol_event : ^sokol_app.Event) type = .Key_Pressed key = to_key_from_sokol( sokol_event.key_code ) modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .KEY_UP: type = .Key_Released key = to_key_from_sokol( sokol_event.key_code ) modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .CHAR: type = .Unicode codepoint = transmute(rune) sokol_event.char_code modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_DOWN: type = .Mouse_Pressed mouse.btn = to_mouse_btn_from_sokol( sokol_event.mouse_button ) modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_UP: type = .Mouse_Released mouse.btn = to_mouse_btn_from_sokol( sokol_event.mouse_button ) modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_SCROLL: type = .Mouse_Scroll mouse.scroll = { sokol_event.scroll_x, sokol_event.scroll_y } modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_MOVE: type = .Mouse_Move modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_ENTER: type = .Mouse_Enter modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() case .MOUSE_LEAVE: type = .Mouse_Leave modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers ) + sokol_app.consume_event() // TODO(Ed): Add support case .TOUCHES_BEGAN: @@ -163,30 +176,43 @@ sokol_app_event_callback :: proc "c" (sokol_event : ^sokol_app.Event) case .TOUCHES_CANCELLED: case .RESIZED: + sokol_app.consume_event() case .ICONIFIED: + sokol_app.consume_event() case .RESTORED: + sokol_app.consume_event() case .FOCUSED: + sokol_app.consume_event() case .UNFOCUSED: + sokol_app.consume_event() case .SUSPENDED: + sokol_app.consume_event() case .RESUMED: + sokol_app.consume_event() case .QUIT_REQUESTED: + sokol_app.consume_event() case .CLIPBOARD_PASTED: + sokol_app.consume_event() case .FILES_DROPPED: + sokol_app.consume_event() case .DISPLAY_CHANGED: logf("sokol_app - event: Display changed") logf("refresh rate: %v", sokol_app.refresh_rate()) monitor_refresh_hz := sokol_app.refresh_rate() + sokol_app.consume_event() } + + append_staged_input_events( event ) } #endregion("Sokol App") diff --git a/code/sectr/engine/update.odin b/code/sectr/engine/update.odin index df3c5d6..06f3fe6 100644 --- a/code/sectr/engine/update.odin +++ b/code/sectr/engine/update.odin @@ -79,7 +79,8 @@ update :: proc( delta_time : f64 ) -> b32 } state.input, state.input_prev = swap( state.input, state.input_prev ) - pull_staged_input_events( state.input, & state.staged_input_events ) + pull_staged_input_events( state.input, & state.input_events, state.staged_input_events ) + poll_input_events( state.input, state.input_prev, state.input_events ) debug_actions : DebugActions = {} // poll_debug_actions( & debug_actions, state.input ) @@ -165,7 +166,7 @@ update :: proc( delta_time : f64 ) -> b32 switch config.cam_zoom_mode { case .Smooth: - zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_smooth + zoom_delta := input.mouse.scroll.y * config.cam_zoom_sensitivity_smooth workspace.zoom_target *= 1 + zoom_delta * f32(delta_time) workspace.zoom_target = clamp(workspace.zoom_target, config.cam_min_zoom, config.cam_max_zoom) @@ -174,7 +175,7 @@ update :: proc( delta_time : f64 ) -> b32 cam.zoom += (workspace.zoom_target - cam.zoom) * lerp_factor * f32(delta_time) cam.zoom = clamp(cam.zoom, config.cam_min_zoom, config.cam_max_zoom) // Ensure cam.zoom stays within bounds case .Digital: - zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_digital + zoom_delta := input.mouse.scroll.y * config.cam_zoom_sensitivity_digital workspace.zoom_target = clamp(workspace.zoom_target + zoom_delta, config.cam_min_zoom, config.cam_max_zoom) cam.zoom = workspace.zoom_target } diff --git a/code/sectr/grime/mappings.odin b/code/sectr/grime/mappings.odin index df61292..fe37289 100644 --- a/code/sectr/grime/mappings.odin +++ b/code/sectr/grime/mappings.odin @@ -30,6 +30,9 @@ import "base:runtime" import c "core:c/libc" +import "core:container/queue" + Queue :: queue.Queue + // import "core:dynlib" import "core:hash" @@ -49,6 +52,9 @@ import fmt_io "core:fmt" import "core:math" +import "core:math/bits" + u64_max :: bits.U64_MAX + import "core:mem" align_forward_int :: mem.align_forward_int align_forward_uint :: mem.align_forward_uint @@ -158,6 +164,7 @@ import "codebase:grime" array_to_slice :: grime.array_to_slice array_init :: grime.array_init array_append :: grime.array_append + array_append_array :: grime.array_append_array array_append_at :: grime.array_append_at array_clear :: grime.array_clear array_free :: grime.array_free @@ -181,6 +188,10 @@ import "codebase:grime" hmap_zpl_reload :: grime.hmap_zpl_reload hmap_zpl_set :: grime.hmap_zpl_set + make_queue :: grime.make_queue + + next_queue_iterator :: grime.next_queue_iterator + Pool :: grime.Pool Slab :: grime.Slab @@ -308,6 +319,10 @@ bivec3 :: proc { vec3_to_bivec3, } +clear :: proc{ + grime.array_clear, +} + cm_to_pixels :: proc { f32_cm_to_pixels, vec2_cm_to_pixels, @@ -352,10 +367,15 @@ is_power_of_two :: proc { is_power_of_two_uintptr, } +iterator :: proc { + grime.iterator_queue, +} + make :: proc { array_init, hmap_chained_init, hmap_zpl_init, + make_queue, // Usual make_slice, @@ -375,6 +395,14 @@ mov_avg_exp :: proc { mov_avg_exp_f64, } +next :: proc { + next_queue_iterator, +} + +peek_front :: proc { + queue.peek_front, +} + pixels_to_cm :: proc { f32_pixels_to_cm, vec2_pixels_to_cm, @@ -412,6 +440,9 @@ pressed :: proc { } push :: proc { + queue.push_back, + grime.push_back_slice_queue, + stack_push, stack_allocator_push, @@ -435,6 +466,29 @@ released :: proc { btn_released, } +reload :: proc { + grime.reload_array, + grime.reload_queue, + grime.reload_map, +} + +space_left :: proc { + queue.space, +} + +scope :: proc { + ui_layout_scope_via_layout, + ui_layout_scope_via_combo, + + ui_style_scope_via_style, + ui_style_scope_via_combo, + + ui_theme_scope_via_layout_style, + ui_theme_scope_via_combos, + ui_theme_scope_via_proc, + ui_theme_scope_via_theme, +} + sqrt :: proc{ math.sqrt_f16, math.sqrt_f16le, @@ -451,19 +505,6 @@ inverse_sqrt :: proc { inverse_sqrt_f32, } -scope :: proc { - ui_layout_scope_via_layout, - ui_layout_scope_via_combo, - - ui_style_scope_via_style, - ui_style_scope_via_combo, - - ui_theme_scope_via_layout_style, - ui_theme_scope_via_combos, - ui_theme_scope_via_proc, - ui_theme_scope_via_theme, -} - sub :: proc { sub_point3, sub_range2, diff --git a/code/sectr/input/event.odin b/code/sectr/input/event.odin deleted file mode 100644 index b0b2c18..0000000 --- a/code/sectr/input/event.odin +++ /dev/null @@ -1,63 +0,0 @@ -package sectr - -InputEventType :: enum u32 { - Key_Pressed, - Key_Released, - Mouse_Pressed, - Mouse_Released, - Mouse_Scroll, - Mouse_Move, - Mouse_Enter, - Mouse_Leave, - Unicode, -} - -InputEvent :: struct -{ - frame_id : u64, - type : InputEventType, - key : KeyCode, - modifiers : ModifierCodeFlags, - mouse : struct { - btn : MouseBtn, - pos : Vec2, - delta : Vec2, - scroll : Vec2, - }, - codepoint : rune, - - // num_touches : u32, - // touches : Touchpoint, - - _sokol_frame_id : u64, -} - -InputKeyEvent :: struct { - frame_id : u64, - type : InputEventType, - key : KeyCode, - modifiers : ModifierCodeFlags, -} - -InputMouseEvent :: struct { - frame_id : u64, - type : InputEventType, - key : KeyCode, - modifiers : ModifierCodeFlags, -} - -// Note(Ed): There is a staged_input_events : Array(InputEvent), in the state.odin's State struct - -append_staged_input_events :: #force_inline proc() { - // TODO(Ed) : Add guards for multi-threading - - state := get_state() - array_append( & state.staged_input_events, event ) -} - -pull_staged_input_events :: proc( input : ^InputState, staged_events : ^Array(InputEvent) ) -{ - // TODO(Ed) : Add guards for multi-threading - - -} diff --git a/code/sectr/input/events.odin b/code/sectr/input/events.odin new file mode 100644 index 0000000..4a8fc87 --- /dev/null +++ b/code/sectr/input/events.odin @@ -0,0 +1,259 @@ +package sectr + +InputEventType :: enum u32 { + Key_Pressed, + Key_Released, + Mouse_Pressed, + Mouse_Released, + Mouse_Scroll, + Mouse_Move, + Mouse_Enter, + Mouse_Leave, + Unicode, +} + +InputEvent :: struct +{ + frame_id : u64, + type : InputEventType, + key : KeyCode, + modifiers : ModifierCodeFlags, + mouse : struct { + btn : MouseBtn, + pos : Vec2, + delta : Vec2, + scroll : Vec2, + }, + codepoint : rune, + + // num_touches : u32, + // touches : Touchpoint, + + _sokol_frame_id : u64, +} + +// TODO(Ed): May just use input event exclusively in the future and have pointers for key and mouse event filters +// I'm on the fence about this as I don't want to force + +InputKeyEvent :: struct { + frame_id : u64, + type : InputEventType, + key : KeyCode, + modifiers : ModifierCodeFlags, +} + +InputMouseEvent :: struct { + frame_id : u64, + type : InputEventType, + btn : MouseBtn, + pos : Vec2, + delta : Vec2, + scroll : Vec2, + modifiers : ModifierCodeFlags, +} + +InputEvents :: struct { + events : Queue(InputEvent), + key_events : Queue(InputKeyEvent), + mouse_events : Queue(InputMouseEvent), + + codes_pressed : Array(rune), +} + +// Note(Ed): There is a staged_input_events : Array(InputEvent), in the state.odin's State struct + +append_staged_input_events :: #force_inline proc( event : InputEvent ) { + // TODO(Ed) : Add guards for multi-threading + + state := get_state() + append( & state.staged_input_events, event ) +} + +pull_staged_input_events :: proc( input : ^InputState, input_events : ^InputEvents, staged_events : Array(InputEvent) ) +{ + // TODO(Ed) : Add guards for multi-threading + + staged_events_slice := array_to_slice(staged_events) + push( & input_events.events, staged_events_slice ) + + using input_events + + for event in staged_events_slice + { + switch event.type { + case .Key_Pressed: + push( & key_events, InputKeyEvent { + frame_id = event.frame_id, + type = event.type, + key = event.key, + modifiers = event.modifiers + }) + + case .Key_Released: + push( & key_events, InputKeyEvent { + frame_id = event.frame_id, + type = event.type, + key = event.key, + modifiers = event.modifiers + }) + + case .Unicode: + append( & codes_pressed, event.codepoint ) + + case .Mouse_Pressed: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + + case .Mouse_Released: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + + case .Mouse_Scroll: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + case .Mouse_Move: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + + case .Mouse_Enter: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + + case .Mouse_Leave: + push( & mouse_events, InputMouseEvent { + frame_id = event.frame_id, + type = event.type, + btn = event.mouse.btn, + pos = event.mouse.pos, + delta = event.mouse.delta, + scroll = event.mouse.scroll, + modifiers = event.modifiers, + }) + } + } + + clear( staged_events ) +} + +poll_input_events :: proc( input, prev_input : ^InputState, input_events : InputEvents ) +{ + input.keyboard = {} + input.mouse = {} + + input_events := input_events + using input_events + + @static prev_frame : u64 = u64_max + + last_frame := peek_front( & events).frame_id + + // No new events, don't update + if prev_frame != 0 && last_frame == prev_frame do return + + Iterate_Key_Events: + { + iter_obj := iterator( key_events ); iter := & iter_obj + for event := next( iter ); event != nil; event = next( iter ) + { + if last_frame == event.frame_id do return + + key := & input.keyboard.keys[event.key] + prev_key := prev_input.keyboard.keys[event.key] + + first_transition := key.half_transitions == 0 + + #partial switch event.type { + case .Key_Pressed: + key.half_transitions += 1 + key.ended_down = true + + case .Key_Released: + key.half_transitions += 1 + key.ended_down = false + } + + frame_transition := first_transition && prev_key.ended_down != key.ended_down ? i32(1) : i32(0) + key.half_transitions += frame_transition + } + } + + Iterate_Mouse_Events: + { + iter_obj := iterator( mouse_events ); iter := & iter_obj + for event := next( iter ); event != nil; event = next( iter ) + { + if last_frame == event.frame_id do return + + process_digital_btn :: proc( btn : ^DigitalBtn, prev_btn : DigitalBtn, ended_down : b32 ) + { + first_transition := btn.half_transitions == 0 + + btn.half_transitions += 1 + btn.ended_down = ended_down + + frame_transition := first_transition && prev_btn.ended_down != btn.ended_down ? i32(1) : i32(0) + btn.half_transitions += frame_transition + } + + #partial switch event.type { + case .Mouse_Pressed: + btn := & input.mouse.btns[event.btn] + prev_btn := prev_input.mouse.btns[event.btn] + process_digital_btn( btn, prev_btn, true ) + + case .Mouse_Released: + btn := & input.mouse.btns[event.btn] + prev_btn := prev_input.mouse.btns[event.btn] + process_digital_btn( btn, prev_btn, false ) + + case .Mouse_Scroll: + input.mouse.scroll += event.scroll.x + + case .Mouse_Move: + case .Mouse_Enter: + case .Mouse_Leave: + // Handled elsewhere + } + + input.mouse.pos = event.pos + input.mouse.delta = event.delta + } + } + + prev_frame = last_frame +} diff --git a/code/sectr/input/input.odin b/code/sectr/input/input.odin index 27d9d65..605e6eb 100644 --- a/code/sectr/input/input.odin +++ b/code/sectr/input/input.odin @@ -10,7 +10,7 @@ AnalogStick :: struct { DigitalBtn :: struct { half_transitions : i32, - ended_down : b32 + ended_down : b32, } btn_pressed :: proc( btn : DigitalBtn ) -> b32 { @@ -167,11 +167,11 @@ MouseState :: struct { btns : [16] DigitalBtn, using individual : struct { left, middle, right : DigitalBtn, - side, forward, back, extra : DigitalBtn + side, forward, back, extra : DigitalBtn, } }, - raw_pos, pos, delta : Vec2, - vertical_wheel, horizontal_wheel : AnalogAxis + raw_pos, pos, delta : Vec2, + scroll : [2]AnalogAxis, } mouse_world_delta :: #force_inline proc "contextless" () -> Vec2 { @@ -183,10 +183,4 @@ mouse_world_delta :: #force_inline proc "contextless" () -> Vec2 { InputState :: struct { keyboard : KeyboardState, mouse : MouseState, - - events : Array(InputEvent), - key_events : Array(InputKeyEvent), - mouse_events : Array(InputMouseEvent), - - codes_pressed : Array(rune), } diff --git a/code/sectr/input/input_sokol.odin b/code/sectr/input/input_sokol.odin index 602af13..92762a9 100644 --- a/code/sectr/input/input_sokol.odin +++ b/code/sectr/input/input_sokol.odin @@ -7,43 +7,18 @@ import sokol_app "thirdparty:sokol/app" to_modifiers_code_from_sokol :: proc( sokol_modifiers : u32 ) -> ( modifiers : ModifierCodeFlags ) { - if sokol_modifiers & sokol_app.MODIFIER_SHIFT != 0 { - modifiers |= { .Shift } - } - if sokol_modifiers & sokol_app.MODIFIER_CTRL != 0 { - modifiers |= { .Control } - } - if sokol_modifiers & sokol_app.MODIFIER_ALT != 0 { - modifiers |= { .Alt } - } - if sokol_modifiers & sokol_app.MODIFIER_LMB != 0 { - modifiers |= { .Left_Mouse } - } - if sokol_modifiers & sokol_app.MODIFIER_RMB != 0 { - modifiers |= { .Right_Mouse } - } - if sokol_modifiers & sokol_app.MODIFIER_MMB != 0 { - modifiers |= { .Middle_Mouse } - } - if sokol_modifiers & sokol_app.MODIFIER_LSHIFT != 0 { - modifiers |= { .Left_Shift } - } - if sokol_modifiers & sokol_app.MODIFIER_RSHIFT != 0 { - modifiers |= { .Right_Shift } - } - if sokol_modifiers & sokol_app.MODIFIER_LCTRL != 0 { - modifiers |= { .Left_Control } - } - if sokol_modifiers & sokol_app.MODIFIER_RCTRL != 0 { - modifiers |= { .Right_Control } - } - if sokol_modifiers & sokol_app.MODIFIER_LALT != 0 { - modifiers |= { .Left_Alt } - } - if sokol_modifiers & sokol_app.MODIFIER_RALT != 0 { - modifiers |= { .Right_Alt } - } - + if sokol_modifiers & sokol_app.MODIFIER_SHIFT != 0 do modifiers |= { .Shift } + if sokol_modifiers & sokol_app.MODIFIER_CTRL != 0 do modifiers |= { .Control } + if sokol_modifiers & sokol_app.MODIFIER_ALT != 0 do modifiers |= { .Alt } + if sokol_modifiers & sokol_app.MODIFIER_LMB != 0 do modifiers |= { .Left_Mouse } + if sokol_modifiers & sokol_app.MODIFIER_RMB != 0 do modifiers |= { .Right_Mouse } + if sokol_modifiers & sokol_app.MODIFIER_MMB != 0 do modifiers |= { .Middle_Mouse } + if sokol_modifiers & sokol_app.MODIFIER_LSHIFT != 0 do modifiers |= { .Left_Shift } + if sokol_modifiers & sokol_app.MODIFIER_RSHIFT != 0 do modifiers |= { .Right_Shift } + if sokol_modifiers & sokol_app.MODIFIER_LCTRL != 0 do modifiers |= { .Left_Control } + if sokol_modifiers & sokol_app.MODIFIER_RCTRL != 0 do modifiers |= { .Right_Control } + if sokol_modifiers & sokol_app.MODIFIER_LALT != 0 do modifiers |= { .Left_Alt } + if sokol_modifiers & sokol_app.MODIFIER_RALT != 0 do modifiers |= { .Right_Alt } return }