Started to setup input events based off sokol

Will replace the input polling done with raylib.

Going to also provide the more robust input tracking for consuming events with the UI interactions
This commit is contained in:
Edward R. Gonzalez 2024-06-17 03:35:53 -04:00
parent 05ffaf432d
commit 425a642fd3
16 changed files with 382 additions and 52 deletions

View File

@ -0,0 +1,3 @@
package sectr

View File

@ -252,8 +252,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
}
if input_box.active {
array_append( & value_str, input.keyboard_events.chars_pressed )
array_clear( input.keyboard_events.chars_pressed )
array_append( & value_str, input.codes_pressed )
array_clear( input.codes_pressed )
}
else if input_box.was_active
{

View File

@ -210,6 +210,10 @@ State :: struct {
input_prev : ^InputState,
input : ^InputState,
// 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.
debug : DebugData,
project : Project,
@ -226,6 +230,7 @@ State :: struct {
// using frametime : FrameTime,
sleep_is_granular : b32,
frame : u64,
frametime_delta_seconds : f64,
frametime_delta_ms : f64,
frametime_delta_ns : Duration,

View File

@ -110,19 +110,30 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
for & input in input_data {
using input
error : AllocatorError
keyboard_events.keys_pressed, error = make( Array(KeyCode), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.keys_pressed array")
keyboard_events.chars_pressed, error = make( Array(rune), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.chars_pressed array")
events, error = make( Array(InputEvent), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate input.events array")
key_events, error = make( Array(InputKeyEvent), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate key_events array")
mouse_events, error = make( Array(InputMouseEvent), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate mouse_events array")
codes_pressed, error = make( Array(rune), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate codes_pressed array")
}
input_staged_events, error := make( Array(InputEvent), Kilo, persistent_slab_allocator() )
ensure(error == AllocatorError.None, "Failed to allocate input_staged_events array")
}
// Configuration Load
// TODO(Ed): Make this actually load from an ini
{
using config
resolution_width = 1000
resolution_height = 600
resolution_width = 1600
resolution_height = 900
refresh_rate = 0
cam_min_zoom = 0.10
@ -281,19 +292,19 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
// font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" )
// path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" } )
// font_firacode = font_load( path_firacode, 24.0, "FiraCode" )
path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" } )
font_firacode = font_load( path_firacode, 24.0, "FiraCode" )
// path_open_sans := strings.concatenate( { Path_Assets, "OpenSans-Regular.ttf" } )
// font_open_sans = font_load( path_open_sans, 24.0, "OpenSans" )
path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } )
font_noto_sans = font_load( path_noto_sans, 24.0, "NotoSans" )
// path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } )
// font_noto_sans = font_load( path_noto_sans, 24.0, "NotoSans" )
path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
font_arial_unicode_ms = font_load( path_arial_unicode_ms, 24.0, "Arial_Unicode_MS" )
// path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
// font_arial_unicode_ms = font_load( path_arial_unicode_ms, 24.0, "Arial_Unicode_MS" )
default_font = font_arial_unicode_ms
default_font = font_firacode
log( "Default font loaded" )
}
@ -391,18 +402,19 @@ reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem,
set_profiler_module_context( prof )
context.logger = to_odin_logger( & Memory_App.logger )
using Memory_App;
persistent = persistent_mem
frame = frame_mem
transient = transient_mem
files_buffer = files_buffer_mem
{
using Memory_App;
persistent = persistent_mem
frame = frame_mem
transient = transient_mem
files_buffer = files_buffer_mem
}
context.allocator = transient_allocator()
context.temp_allocator = transient_allocator()
Memory_App.state = get_state()
using state
using Memory_App.state
sokol_context = context
@ -503,7 +515,7 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32
debug.draw_UI_padding_bounds = false
debug.draw_ui_content_bounds = false
config.engine_refresh_hz = 165
// config.engine_refresh_hz = 165
// config.color_theme = App_Thm_Light
// config.color_theme = App_Thm_Dusk
@ -535,8 +547,9 @@ tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_
context.temp_allocator = transient_allocator()
// profile("Client tick timing processing")
// config.engine_refresh_hz = uint(monitor_refresh_hz)
// config.engine_refresh_hz = 6
config.engine_refresh_hz = uint(monitor_refresh_hz)
// config.engine_refresh_hz = 10
frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS
sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS
@ -578,6 +591,8 @@ tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_
if frametime_elapsed_ms > 60.0 {
log( str_fmt("Big tick! %v ms", frametime_elapsed_ms), LogLevel.Warning )
}
frame += 1
}
@export

View File

@ -93,14 +93,99 @@ sokol_app_log_callback :: proc "c" (
logf( "%-80s %s::%v", cloned_msg, cloned_tag, line_nr, level = odin_level )
}
sokol_app_event_callback :: proc "c" (event : ^sokol_app.Event)
// TODO(Ed): This needs to queue to a job stask for a event callback handling thread to deal with.
sokol_app_event_callback :: proc "c" (sokol_event : ^sokol_app.Event)
{
state := get_state(); using state
context = sokol_context
if event.type == sokol_app.Event_Type.DISPLAY_CHANGED {
logf("sokol_app - event: Display changed")
logf("refresh rate: %v", sokol_app.refresh_rate());
monitor_refresh_hz := sokol_app.refresh_rate()
event : InputEvent
using event
_sokol_frame_id = sokol_event.frame_count
frame_id = frame
mouse.pos = { sokol_event.mouse_x, sokol_event.mouse_y }
mouse.delta = { sokol_event.mouse_dx, sokol_event.mouse_dy }
switch sokol_event.type
{
case .INVALID:
logf("sokol_app - event: INVALID?")
logf("%v", sokol_event)
case .KEY_DOWN:
type = .Key_Pressed
key = to_key_from_sokol( sokol_event.key_code )
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
case .KEY_UP:
type = .Key_Released
key = to_key_from_sokol( sokol_event.key_code )
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
case .CHAR:
type = .Unicode
codepoint = transmute(rune) sokol_event.char_code
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
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 )
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 )
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 )
case .MOUSE_MOVE:
type = .Mouse_Move
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
case .MOUSE_ENTER:
type = .Mouse_Enter
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
case .MOUSE_LEAVE:
type = .Mouse_Leave
modifiers = to_modifiers_code_from_sokol( sokol_event.modifiers )
// TODO(Ed): Add support
case .TOUCHES_BEGAN:
case .TOUCHES_MOVED:
case .TOUCHES_ENDED:
case .TOUCHES_CANCELLED:
case .RESIZED:
case .ICONIFIED:
case .RESTORED:
case .FOCUSED:
case .UNFOCUSED:
case .SUSPENDED:
case .RESUMED:
case .QUIT_REQUESTED:
case .CLIPBOARD_PASTED:
case .FILES_DROPPED:
case .DISPLAY_CHANGED:
logf("sokol_app - event: Display changed")
logf("refresh rate: %v", sokol_app.refresh_rate())
monitor_refresh_hz := sokol_app.refresh_rate()
}
}

View File

@ -1,6 +1,7 @@
package sectr
import ve "codebase:font/VEFontCache"
import sokol_app "thirdparty:sokol/app"
import sokol_gfx "thirdparty:sokol/gfx"
import sokol_glue "thirdparty:sokol/glue"
import "core:time"
@ -80,8 +81,7 @@ render :: proc()
// "Draw text" using immediate mode api
{
@static index : i32
index += 1
text_test_str := str_fmt("frametime: %0.2f\nframe id : %d", frametime_avg_ms, index )
text_test_str := str_fmt("frametime : %0.6f\nframe id : %d\nsokol_frame: %d", frametime_avg_ms, frame, sokol_app.frame_count() )
// log(text_test_str)
// text_test_str := str_fmt("HELLO VE FONT CACHE!")
// text_test_str := str_fmt("C")
@ -95,7 +95,7 @@ render :: proc()
ve.set_colour( & ve_font_cache, { 1.0, 1.0, 1.0, 1.0 } )
ve.configure_snap( & ve_font_cache, u32(state.app_window.extent.x * 2.0), u32(state.app_window.extent.y * 2.0) )
ve.draw_text( & ve_font_cache, fdef.ve_id, text_test_str, {0.1, 0.2}, Vec2{1 / width, 1 / height} )
ve.draw_text( & ve_font_cache, fdef.ve_id, text_test_str, {0.0, 0.975}, Vec2{1 / width, 1 / height} )
}

View File

@ -79,7 +79,7 @@ update :: proc( delta_time : f64 ) -> b32
}
state.input, state.input_prev = swap( state.input, state.input_prev )
// poll_input( state.input_prev, state.input )
pull_staged_input_events( state.input, & state.staged_input_events )
debug_actions : DebugActions = {}
// poll_debug_actions( & debug_actions, state.input )

View File

@ -545,7 +545,7 @@ font_load :: proc(path_file : string,
def.path_file = path_file
// TODO(Ed): Load even sizes from 8px to upper bound.
def.ve_id = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, 80.0 )
def.ve_id = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, 18.0 )
fid := FontID { key, desired_id }
return fid

View File

@ -297,6 +297,12 @@ add :: proc {
add_range2,
}
append :: proc {
grime.array_append_array,
grime.array_append_slice,
grime.array_append_value,
}
bivec3 :: proc {
bivec3_via_f32s,
vec3_to_bivec3,

View File

@ -1 +1,12 @@
package sectr
package sectr
InputBindCallback :: #type proc(user_ptr : rawptr)
InputBind :: struct
{
user_ptr : rawptr,
callback : InputBindCallback,
}

View File

@ -1 +1,63 @@
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
}

View File

@ -31,6 +31,8 @@ MouseBtn :: enum u32 {
Back = 0x5,
Extra = 0x6,
Invalid = 0x100,
count
}
@ -40,7 +42,11 @@ KeyboardState :: struct #raw_union {
ignored : DigitalBtn,
__0x02_0x07_Unassigned__ : [ 6 * size_of( DigitalBtn)] u8,
// GFLW / Sokol
menu,
world_1, world_2 : DigitalBtn,
__0x05_0x07_Unassigned__ : [ 3 * size_of( DigitalBtn)] u8,
tab, backspace : DigitalBtn,
@ -61,8 +67,7 @@ KeyboardState :: struct #raw_union {
right_shift,
right_control : DigitalBtn,
__0x19_Unassigned__ : [ 1 * size_of( DigitalBtn )] u8,
print_screen,
pause,
escape,
home,
@ -136,9 +141,27 @@ KeyboardState :: struct #raw_union {
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 : DigitalBtn,
insert, delete : DigitalBtn,
F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25 : DigitalBtn,
}
}
ModifierCode :: enum u32 {
Shift,
Control,
Alt,
Left_Mouse,
Right_Mouse,
Middle_Mouse,
Left_Shift,
Right_Shift,
Left_Control,
Right_Control,
Left_Alt,
Right_Alt,
}
ModifierCodeFlags :: bit_set[ModifierCode; u32]
MouseState :: struct {
using _ : struct #raw_union {
btns : [16] DigitalBtn,
@ -161,11 +184,9 @@ InputState :: struct {
keyboard : KeyboardState,
mouse : MouseState,
keyboard_events : KeyboardEvents,
}
events : Array(InputEvent),
key_events : Array(InputKeyEvent),
mouse_events : Array(InputMouseEvent),
// TODO(Ed): Whats the lifetime of these events? (So far we're picking per full-frame)
KeyboardEvents :: struct {
keys_pressed : Array(KeyCode),
chars_pressed : Array(rune),
codes_pressed : Array(rune),
}

View File

@ -1 +1,109 @@
package sectr
package sectr
import "base:runtime"
import "core:os"
import "core:c/libc"
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 }
}
return
}
to_key_from_sokol :: proc( sokol_key : sokol_app.Keycode ) -> ( key : KeyCode )
{
world_code_offset :: i32(sokol_app.Keycode.WORLD_1) - i32(KeyCode.world_1)
arrow_code_offset :: i32(sokol_app.Keycode.RIGHT) - i32(KeyCode.right)
func_row_code_offset :: i32(sokol_app.Keycode.F1) - i32(KeyCode.F1)
func_extra_code_offset :: i32(sokol_app.Keycode.F13) - i32(KeyCode.F25)
keypad_num_offset :: i32(sokol_app.Keycode.KP_0) - i32(KeyCode.kpad_0)
switch sokol_key {
case .INVALID ..= .GRAVE_ACCENT : key = transmute(KeyCode) sokol_key
case .WORLD_1, .WORLD_2 : key = transmute(KeyCode) (i32(sokol_key) - world_code_offset)
case .ESCAPE : key = .escape
case .ENTER : key = .enter
case .TAB : key = .tab
case .BACKSPACE : key = .backspace
case .INSERT : key = .insert
case .DELETE : key = .delete
case .RIGHT ..= .UP : key = transmute(KeyCode) (i32(sokol_key) - arrow_code_offset)
case .PAGE_UP : key = .page_up
case .PAGE_DOWN : key = .page_down
case .HOME : key = .home
case .END : key = .end
case .CAPS_LOCK : key = .caps_lock
case .SCROLL_LOCK : key = .scroll_lock
case .NUM_LOCK : key = .num_lock
case .PRINT_SCREEN : key = .print_screen
case .PAUSE : key = .pause
case .F1 ..= .F12 : key = transmute(KeyCode) (i32(sokol_key) - func_row_code_offset)
case .F13 ..= .F25 : key = transmute(KeyCode) (i32(sokol_key) - func_extra_code_offset)
case .KP_0 ..= .KP_9 : key = transmute(KeyCode) (i32(sokol_key) - keypad_num_offset)
case .KP_DECIMAL : key = .kpad_decimal
case .KP_DIVIDE : key = .kpad_divide
case .KP_MULTIPLY : key = .kpad_multiply
case .KP_SUBTRACT : key = .kpad_minus
case .KP_ADD : key = .kpad_plus
case .KP_ENTER : key = .kpad_enter
case .KP_EQUAL : key = .kpad_equals
case .LEFT_SHIFT : key = .left_shift
case .LEFT_CONTROL : key = .left_control
case .LEFT_ALT : key = .left_alt
case .LEFT_SUPER : key = .ignored
case .RIGHT_SHIFT : key = .right_shift
case .RIGHT_CONTROL : key = .right_control
case .RIGHT_ALT : key = .right_alt
case .RIGHT_SUPER : key = .ignored
case .MENU : key = .menu
}
return
}
to_mouse_btn_from_sokol :: proc( sokol_mouse : sokol_app.Mousebutton ) -> ( btn : MouseBtn )
{
switch sokol_mouse {
case .LEFT : btn = .Left
case .MIDDLE : btn = .Middle
case .RIGHT : btn = .Right
case .INVALID : btn = .Invalid
}
return
}

View File

@ -6,9 +6,10 @@ KeyCode :: enum u32 {
null = 0x00,
ignored = 0x01,
// 0x02
// 0x03
// 0x04
menu = 0x02,
world_1 = 0x03,
world_2 = 0x04,
// 0x05
// 0x06
// 0x07
@ -36,8 +37,7 @@ KeyCode :: enum u32 {
right_shift = 0x17,
right_control = 0x18,
// 0x19
print_screen = 0x19,
pause = 0x1A,
escape = '\x1B', // 0x1B
home = 0x1C,
@ -150,5 +150,19 @@ KeyCode :: enum u32 {
insert = 0x7E,
delete = 0x7F,
count = 0x80,
F13 = 0x80,
F14 = 0x81,
F15 = 0x82,
F16 = 0x83,
F17 = 0x84,
F18 = 0x85,
F19 = 0x86,
F20 = 0x87,
F21 = 0x88,
F22 = 0x89,
F23 = 0x8A,
F24 = 0x8B,
F25 = 0x8C,
count = 0x8D,
}