diff --git a/src/event/event.odin b/src/event/event.odin new file mode 100644 index 0000000..0a6fd05 --- /dev/null +++ b/src/event/event.odin @@ -0,0 +1,115 @@ +package event + +import "core:fmt" +// ----------------------------------------- + +// event category, bitfield (?) + +Event :: union { + Window_Close_Event, + Window_Resize_Event, + Joystick_Connect_Event, + Joystick_Disconnect_Event, + Key_Press_Event, + Key_Release_Event, + Key_Repeat_Event, + Mouse_Button_Press_Event, + Mouse_Button_Release_Event, + Mouse_Position_Event, + Mouse_Scroll_Event, +} + +Window_Close_Event :: struct { + handled: bool, +} + +Window_Resize_Event :: struct { + handled: bool, + width: i32, + height: i32, +} + +Joystick_Connect_Event :: struct { + handled: bool, + id: i32, +} + +Joystick_Disconnect_Event :: struct { + handled: bool, + id: i32, +} + +Key_Press_Event :: struct { + handled: bool, + key: i32, + mods: i32, +} + +Key_Release_Event :: struct { + handled: bool, + key: i32, + mods: i32, +} + +Key_Repeat_Event :: struct { + handled: bool, + key: i32, + mods: i32, +} + +Mouse_Button_Press_Event :: struct { + handled: bool, + button: i32, + mods: i32, +} + +Mouse_Button_Release_Event :: struct { + handled: bool, + button: i32, + mods: i32, +} + +Mouse_Position_Event :: struct { + handled: bool, + x_pos: f32, + y_pos: f32, +} + +Mouse_Scroll_Event :: struct { + handled: bool, + x_offset: f32, + y_offset: f32, +} + +// ----------------------------------------- + +// Dispatcher(Event) -> forwards to the right function +on_event :: proc(event: Event) { + fmt.println("toby!") + + #partial switch e in event { + case Window_Close_Event: + fmt.println("Close") + case Window_Resize_Event: + fmt.println("Resize:", e.width, e.height) + + case Key_Press_Event: + fmt.println("Key press", e.key, e.mods) + case Key_Release_Event: + fmt.println("Key release", e.key, e.mods) + case Key_Repeat_Event: + fmt.println("Key repeat", e.key, e.mods) + + case Mouse_Button_Press_Event: + fmt.println("Mouse press", e.button, e.mods) + case Mouse_Button_Release_Event: + fmt.println("Mouse release", e.button, e.mods) + case Mouse_Position_Event: + fmt.printfln("Mouse pos: {}x{}", e.x_pos, e.y_pos) + + case Joystick_Connect_Event: + fmt.println("Joy con:", e.id) + case Joystick_Disconnect_Event: + fmt.println("Joy dis:", e.id) + } +} diff --git a/src/glfw.odin b/src/glfw.odin index 3b77fcc..0c5e32b 100644 --- a/src/glfw.odin +++ b/src/glfw.odin @@ -1,5 +1,6 @@ package sindri +import "core:fmt" import "core:strings" import "vendor:glfw" @@ -7,21 +8,28 @@ import "wgpu:wgpu" import "wgpu:wgpu/glfwglue" import "sindri:core" +import "sindri:event" // ----------------------------------------- +// Types OS :: struct { window: glfw.WindowHandle, } // ----------------------------------------- +// Constructor / destructor os_init :: proc(settings: core.Settings) { if !glfw.Init() { panic("[glfw] init failure") } - glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) + // Set window properties + glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) // do not create OpenGL context + glfw.WindowHint(glfw.RESIZABLE, glfw.FALSE) + + // Create GLFW window state.os.window = glfw.CreateWindow( i32(settings.width), i32(settings.height), @@ -30,9 +38,30 @@ os_init :: proc(settings: core.Settings) { nil, ) + glfw.SetErrorCallback(error_callback) + glfw.SetWindowCloseCallback(state.os.window, window_close_callback) + // NOTE: SetFramebufferSize over SetWindowSize, to handle different DPIs glfw.SetFramebufferSizeCallback(state.os.window, size_callback) + glfw.SetKeyCallback(state.os.window, key_callback) + glfw.SetMouseButtonCallback(state.os.window, mouse_button_callback) + glfw.SetCursorPosCallback(state.os.window, cursor_pos_callback) + glfw.SetScrollCallback(state.os.window, scroll_callback) + glfw.SetJoystickCallback(joystick_callback) + + // TODO: Figure out proper vsync, found 3 spots so far + // - glfw.SwapInterval(0) this is only for OpenGL it seems? + // - glfw.SetWindowMonitor(refresh) + // - wgpu presentMode = .Fifo } +os_destroy :: proc() { + glfw.DestroyWindow(state.os.window) + glfw.Terminate() +} + +// ----------------------------------------- +// Public functions + os_set_monitor :: proc(settings: core.Settings) { monitor := glfw.GetPrimaryMonitor() x_pos: i32 @@ -86,6 +115,10 @@ os_set_monitor :: proc(settings: core.Settings) { ) } +os_set_callback_proc :: proc(window: rawptr) { + glfw.SetWindowUserPointer(state.os.window, window) +} + os_should_close :: proc() -> bool { return bool(glfw.WindowShouldClose(state.os.window)) } @@ -98,11 +131,6 @@ os_poll_events :: proc() { glfw.PollEvents() } -os_destroy :: proc() { - glfw.DestroyWindow(state.os.window) - glfw.Terminate() -} - os_get_framebuffer_size :: proc() -> (width, height: u32) { iw, ih := glfw.GetFramebufferSize(state.os.window) return u32(iw), u32(ih) @@ -112,7 +140,102 @@ os_get_surface :: proc(instance: wgpu.Instance) -> wgpu.Surface { return glfwglue.GetSurface(instance, state.os.window) } +// ----------------------------------------- +// Private functions + +// Error callback +@(private = "file") +error_callback :: proc "c" (error: i32, description: cstring) { + context = state.ctx + fmt.eprintfln("error: GLFW {}: {}", error, description) +} // GLFWerrorfun + +// Window close callback +@(private = "file") +window_close_callback :: proc "c" (window: glfw.WindowHandle) { + context = state.ctx + event.on_event(event.Window_Close_Event{}) +} // GLFWwindowclosefun + +// Window resize callback @(private = "file") size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) { resize() -} +} // GLFWframebuffersizefun + +// Keyboard callback +@(private = "file") +key_callback :: proc "c" ( + window: glfw.WindowHandle, + key, scancode, action, mods: i32, +) { + context = state.ctx + if action == glfw.PRESS { + event.on_event(event.Key_Press_Event{key = key, mods = mods}) + } + if action == glfw.RELEASE { + event.on_event(event.Key_Release_Event{key = key, mods = mods}) + } + if action == glfw.REPEAT { + event.on_event(event.Key_Repeat_Event{key = key, mods = mods}) + } + +} // GLFWkeyfun + +// Mouse button callback +@(private = "file") +mouse_button_callback :: proc "c" ( + window: glfw.WindowHandle, + button, action, mods: i32, +) { + context = state.ctx + if action == glfw.PRESS { + event.on_event(event.Mouse_Button_Press_Event{button = button}) + } + if action == glfw.RELEASE { + event.on_event(event.Mouse_Button_Release_Event{button = button}) + } +} // GLFWmousebuttonfun + +// Mouse position callback +@(private = "file") +cursor_pos_callback :: proc "c" ( + window: glfw.WindowHandle, + x_pos, y_pos: f64, +) { + context = state.ctx + event.on_event( + event.Mouse_Position_Event{x_pos = f32(x_pos), y_pos = f32(y_pos)}, + ) +} // GLFWcursorposfun + +// Mouse scroll callback +@(private = "file") +scroll_callback :: proc "c" ( + window: glfw.WindowHandle, + x_offset, y_offset: f64, +) { + context = state.ctx + event.on_event( + event.Mouse_Scroll_Event { + x_offset = f32(x_offset), + y_offset = f32(y_offset), + }, + ) +} // GLFWscrollfun + +// Joystick connected / disconnected callback +@(private = "file") +joystick_callback :: proc "c" (id, connected: i32) { + context = state.ctx + if connected == glfw.CONNECTED { + event.on_event(event.Joystick_Connect_Event{id = id}) + } else { + event.on_event(event.Joystick_Disconnect_Event{id = id}) + } +} // GLFWjoystickfun + +// References: +// - https://www.glfw.org/docs/latest/group__init.html +// - https://www.glfw.org/docs/latest/group__window.html +// - https://www.glfw.org/docs/latest/group__input.html