Implement type-safe input handling

This commit is contained in:
Riyyi
2026-09-12 15:08:50 +02:00
parent 90602ff1ff
commit f1028acb10
8 changed files with 301 additions and 28 deletions
+13 -10
View File
@@ -1,6 +1,9 @@
package event
import "core:fmt"
import "sindri:input"
// -----------------------------------------
// event category, bitfield (?)
@@ -41,32 +44,32 @@ Joystick_Disconnect_Event :: struct {
Key_Press_Event :: struct {
handled: bool,
key: i32,
mods: i32,
key: input.Key,
mods: input.Mod_Set,
}
Key_Release_Event :: struct {
handled: bool,
key: i32,
mods: i32,
key: input.Key,
mods: input.Mod_Set,
}
Key_Repeat_Event :: struct {
handled: bool,
key: i32,
mods: i32,
key: input.Key,
mods: input.Mod_Set,
}
Mouse_Button_Press_Event :: struct {
handled: bool,
button: i32,
mods: i32,
button: input.Mouse_Button,
mods: input.Mod_Set,
}
Mouse_Button_Release_Event :: struct {
handled: bool,
button: i32,
mods: i32,
button: input.Mouse_Button,
mods: input.Mod_Set,
}
Mouse_Position_Event :: struct {
+15
View File
@@ -0,0 +1,15 @@
package input
// -----------------------------------------
// Public functions
// Returns the state of the keyboard key
// implementation in platform package, to prevent cyclic dependency
key_state: proc(key: Key) -> Action
// Returns the state of the mouse button
// implementation in platform package, to prevent cyclic dependency
mouse_button_state: proc(button: Mouse_Button) -> Action
// -----------------------------------------
// Private functions
+183
View File
@@ -0,0 +1,183 @@
package input
// -----------------------------------------
// Button/Key states
Action :: enum i8 {
None = -1,
Release = 0,
Press = 1,
Repeat = 2,
}
Key :: enum i16 {
// The unknown key
Key_Unknown = -1,
// --- Printable keys ---
// Named printable keys
Key_Space = 32,
Key_Apostrophe = 39, // '
Key_Comma = 44, // ,
Key_Minus = 45, // -
Key_Period = 46, // .
Key_Slash = 47, // /
Key_Semicolon = 59, // ;
Key_Equal = 61, // =
Key_Left_Bracket = 91, // [
Key_Backslash = 92, // \
Key_Right_Bracket = 93, // ]
Key_Grave_Accent = 96, // `
Key_World_1 = 161, // non-US #1
Key_World_2 = 162, // non-US #2
// Alphanumeric characters
Key_0 = 48,
Key_1 = 49,
Key_2 = 50,
Key_3 = 51,
Key_4 = 52,
Key_5 = 53,
Key_6 = 54,
Key_7 = 55,
Key_8 = 56,
Key_9 = 57,
Key_A = 65,
Key_B = 66,
Key_C = 67,
Key_D = 68,
Key_E = 69,
Key_F = 70,
Key_G = 71,
Key_H = 72,
Key_I = 73,
Key_J = 74,
Key_K = 75,
Key_L = 76,
Key_M = 77,
Key_N = 78,
Key_O = 79,
Key_P = 80,
Key_Q = 81,
Key_R = 82,
Key_S = 83,
Key_T = 84,
Key_U = 85,
Key_V = 86,
Key_W = 87,
Key_X = 88,
Key_Y = 89,
Key_Z = 90,
// --- Function keys ---
// Named non-printable keys
Key_Escape = 256,
Key_Enter = 257,
Key_Tab = 258,
Key_Backspace = 259,
Key_Insert = 260,
Key_Delete = 261,
Key_Right = 262,
Key_Left = 263,
Key_Down = 264,
Key_Up = 265,
Key_Page_Up = 266,
Key_Page_Down = 267,
Key_Home = 268,
Key_End = 269,
Key_Caps_Lock = 280,
Key_Scroll_Lock = 281,
Key_Num_Lock = 282,
Key_Print_Screen = 283,
Key_Pause = 284,
// Function keys
Key_F1 = 290,
Key_F2 = 291,
Key_F3 = 292,
Key_F4 = 293,
Key_F5 = 294,
Key_F6 = 295,
Key_F7 = 296,
Key_F8 = 297,
Key_F9 = 298,
Key_F10 = 299,
Key_F11 = 300,
Key_F12 = 301,
Key_F13 = 302,
Key_F14 = 303,
Key_F15 = 304,
Key_F16 = 305,
Key_F17 = 306,
Key_F18 = 307,
Key_F19 = 308,
Key_F20 = 309,
Key_F21 = 310,
Key_F22 = 311,
Key_F23 = 312,
Key_F24 = 313,
Key_F25 = 314,
// Keypad numbers
Key_KP_0 = 320,
Key_KP_1 = 321,
Key_KP_2 = 322,
Key_KP_3 = 323,
Key_KP_4 = 324,
Key_KP_5 = 325,
Key_KP_6 = 326,
Key_KP_7 = 327,
Key_KP_8 = 328,
Key_KP_9 = 329,
// Keypad named function keys
Key_KP_Decimal = 330,
Key_KP_Divide = 331,
Key_KP_Multiply = 332,
Key_KP_Subtract = 333,
Key_KP_Add = 334,
Key_KP_Enter = 335,
Key_KP_Equal = 336,
// Modifier keys
Key_Left_Shift = 340,
Key_Left_Control = 341,
Key_Left_Alt = 342,
Key_Left_Super = 343,
Key_Right_Shift = 344,
Key_Right_Control = 345,
Key_Right_Alt = 346,
Key_Right_Super = 347,
Key_Menu = 348,
Key_Last = Key_Menu,
}
// Bitmask for modifier keys
Mod :: enum i8 {
Shift = 0,
Control = 1,
Alt = 2,
Super = 3,
Caps_Lock = 4,
Num_Lock = 5,
}
Mod_Set :: bit_set[Mod]
// Mouse buttons
Mouse_Button :: enum i8 {
Button_1 = 0,
Button_2 = 1,
Button_3 = 2,
Button_4 = 3,
Button_5 = 4,
Button_6 = 5,
Button_7 = 6,
Button_8 = 7,
// Alias names
Last = Button_8,
Left = Button_1,
Right = Button_2,
Middle = Button_3,
}
+9 -8
View File
@@ -4,6 +4,7 @@ import "core:fmt"
import "core:time"
import "sindri:hot_reload"
import "sindri:platform"
VERSION :: #config(VERSION, "dev")
@@ -20,13 +21,13 @@ main :: proc() {
settings := api.settings()
// Initialize Window
os_init(settings)
os_set_monitor(settings)
defer os_destroy()
platform.os_init(settings)
platform.os_set_monitor(settings)
defer platform.os_destroy()
// Initialize GPU resources
instance_init()
defer instance_destroy()
platform.instance_init()
defer platform.instance_destroy()
api.init_once()
api.init()
@@ -34,13 +35,13 @@ main :: proc() {
gt: f64 = 0
dt: f32
for !os_should_close() && !hot_reload.should_close(&hr) {
for !platform.os_should_close() && !hot_reload.should_close(&hr) {
start := time.tick_now()
os_poll_events()
platform.os_poll_events()
api.update(dt)
frame(dt)
platform.frame(dt)
dt = f32(time.duration_seconds(time.tick_since(start)))
gt += f64(dt)
+70 -7
View File
@@ -1,4 +1,4 @@
package sindri
package platform
import "core:fmt"
import "core:strings"
@@ -9,6 +9,7 @@ import "wgpu:wgpu/glfwglue"
import "sindri:core"
import "sindri:event"
import "sindri:input"
// -----------------------------------------
// Types
@@ -48,6 +49,10 @@ os_init :: proc(settings: core.Settings) {
glfw.SetScrollCallback(state.os.window, scroll_callback)
glfw.SetJoystickCallback(joystick_callback)
// Register input functions
input.key_state = key_state
input.mouse_button_state = mouse_button_state
// TODO: Figure out proper vsync, found 3 spots so far
// - glfw.SwapInterval(0) this is only for OpenGL it seems?
// - glfw.SetWindowMonitor(refresh)
@@ -143,6 +148,40 @@ os_get_surface :: proc(instance: wgpu.Instance) -> wgpu.Surface {
// -----------------------------------------
// Private functions
@(private = "file")
key_state :: proc(key: input.Key) -> input.Action {
return input_action(glfw.GetKey(state.os.window, i32(key)))
}
@(private = "file")
mouse_button_state :: proc(button: input.Mouse_Button) -> input.Action {
return input_action(glfw.GetMouseButton(state.os.window, i32(button)))
}
@(private = "file")
input_action :: proc(action: i32) -> input.Action {
if action == glfw.RELEASE do return input.Action.Release
else if action == glfw.PRESS do return input.Action.Press
else if action == glfw.REPEAT do return input.Action.Repeat
when ODIN_DEBUG do panic("[glfw] unknown action")
return .None
}
@(private = "file")
input_key :: proc(key: i32) -> input.Key {
return input.Key(key) // values match
}
@(private = "file")
input_mod_set :: proc(mods: i32) -> (set: input.Mod_Set) {
return transmute(input.Mod_Set)i8(mods & 0x3f) // values match bit position
}
@(private = "file")
input_mouse_button :: proc(button: i32) -> input.Mouse_Button {
return input.Mouse_Button(button) // values match
}
// Error callback
@(private = "file")
error_callback :: proc "c" (error: i32, description: cstring) {
@@ -171,15 +210,29 @@ key_callback :: proc "c" (
) {
context = state.ctx
if action == glfw.PRESS {
event.on_event(event.Key_Press_Event{key = key, mods = mods})
event.on_event(
event.Key_Press_Event {
key = input_key(key),
mods = input_mod_set(mods),
},
)
}
if action == glfw.RELEASE {
event.on_event(event.Key_Release_Event{key = key, mods = mods})
event.on_event(
event.Key_Release_Event {
key = input_key(key),
mods = input_mod_set(mods),
},
)
}
if action == glfw.REPEAT {
event.on_event(event.Key_Repeat_Event{key = key, mods = mods})
event.on_event(
event.Key_Repeat_Event {
key = input_key(key),
mods = input_mod_set(mods),
},
)
}
} // GLFWkeyfun
// Mouse button callback
@@ -190,10 +243,20 @@ mouse_button_callback :: proc "c" (
) {
context = state.ctx
if action == glfw.PRESS {
event.on_event(event.Mouse_Button_Press_Event{button = button})
event.on_event(
event.Mouse_Button_Press_Event {
button = input_mouse_button(button),
mods = input_mod_set(mods),
},
)
}
if action == glfw.RELEASE {
event.on_event(event.Mouse_Button_Release_Event{button = button})
event.on_event(
event.Mouse_Button_Release_Event {
button = input_mouse_button(button),
mods = input_mod_set(mods),
},
)
}
} // GLFWmousebuttonfun
+1 -1
View File
@@ -1,4 +1,4 @@
package sindri
package platform
import "base:runtime"
import "core:fmt"