package platform import "core:time" import bk "../backend" import window "window:window" // Cursor mode re-export for vex API Cursor_Mode :: window.Cursor_Mode Platform_State :: struct { event_loop: ^window.Event_Loop, win: ^window.Window, should_close: bool, was_resized: bool, new_size: window.Physical_Size, owns_window: bool, // true when platform created the window, false when external // Input input: Input_State, actions: Action_State, // Timing time: Time_State, } init_platform :: proc(width, height: i32, title: string) -> (state: Platform_State, ok: bool) { loop, loop_err := window.create_event_loop() if loop_err != .None { return {}, false } attrs := window.default_window_attributes() attrs.title = title attrs.size = {u32(width), u32(height)} win, win_err := window.create_window(loop, attrs) if win_err != .None { window.destroy_event_loop(loop) return {}, false } state.event_loop = loop state.win = win state.should_close = false state.was_resized = false state.new_size = attrs.size state.owns_window = true init_input(&state.input) init_time(&state.time) return state, true } // Initialize platform state using an externally-owned window and event loop. // The caller is responsible for creating and destroying the window. init_platform_external :: proc( win: ^window.Window, loop: ^window.Event_Loop, ) -> (state: Platform_State, ok: bool) { if win == nil || loop == nil { return {}, false } state.event_loop = loop state.win = win state.should_close = false state.was_resized = false state.new_size = window.surface_size(win) state.owns_window = false init_input(&state.input) init_time(&state.time) return state, true } shutdown_platform :: proc(state: ^Platform_State) { g_polling_state = nil if state.owns_window { if state.win != nil { window.destroy_window(state.win) state.win = nil } if state.event_loop != nil { window.destroy_event_loop(state.event_loop) state.event_loop = nil } } } // Process all pending events in polling mode poll_events :: proc(state: ^Platform_State) { set_polling_state(state) // Swap input buffers for pressed/released detection begin_input_frame(&state.input) // Poll all pending events window.poll(state.event_loop, proc(event: window.Event, loop: ^window.Event_Loop) -> window.Control_Flow { // We access platform state via the event loop's user data mechanism // For now, use a module-level pointer set before polling handle_event(event) return window.Poll{} }) } // Module-level pointer to current platform state for event handler callback @(private) g_polling_state: ^Platform_State @(private) handle_event :: proc(event: window.Event) { state := g_polling_state if state == nil { return } switch e in event { case window.Window_Event_Data: switch we in e.event { case window.Close_Requested: state.should_close = true case window.Resized: state.was_resized = true state.new_size = we.size case window.Key_Input: handle_key_input(&state.input, we) case window.Mouse_Button_Input: handle_mouse_button_input(&state.input, we) case window.Pointer_Moved: handle_pointer_moved(&state.input, we) case window.Mouse_Wheel: handle_mouse_wheel(&state.input, we) case window.Focused: case window.Unfocused: case window.Moved: case window.Destroyed: case window.Char_Input: case window.Pointer_Entered: case window.Pointer_Left: case window.Scale_Factor_Changed: case window.Redraw_Requested: } case window.Device_Event_Data: switch de in e.event { case window.Pointer_Motion: handle_raw_motion(&state.input, de) case window.Device_Button: case window.Device_Key: } case window.New_Events: case window.About_To_Wait: case window.Loop_Exiting: } } // Must be called before poll_events to set the state pointer @(private) set_polling_state :: proc(state: ^Platform_State) { g_polling_state = state } // Window queries get_window_width :: proc(state: ^Platform_State) -> i32 { if state.win == nil { return 0 } size := window.surface_size(state.win) return i32(size.width) } get_window_height :: proc(state: ^Platform_State) -> i32 { if state.win == nil { return 0 } size := window.surface_size(state.win) return i32(size.height) } toggle_fullscreen :: proc(state: ^Platform_State) { if state.win == nil { return } current := window.get_fullscreen(state.win) if current == nil { window.set_fullscreen(state.win, window.Borderless{nil}) } else { window.set_fullscreen(state.win, nil) } } set_cursor_mode :: proc(state: ^Platform_State, mode: Cursor_Mode) { if state.win != nil { window.set_cursor_mode(state.win, mode) } } // Raw window handle for Vulkan surface creation get_raw_window_handle :: proc(state: ^Platform_State) -> window.Raw_Window_Handle { if state.win == nil { return nil } return window.raw_window_handle(state.win) } get_raw_display_handle :: proc(state: ^Platform_State) -> window.Raw_Display_Handle { if state.event_loop == nil { return nil } return window.raw_display_handle(state.event_loop) } surface_desc :: proc(state: ^Platform_State) -> bk.Surface_Desc { if state == nil || state.win == nil { return {} } handle := window.raw_window_handle(state.win) switch h in handle { case window.X11_Window_Handle: return { kind = .X11, x11 = {display = h.display, window = h.window}, } case window.Wayland_Window_Handle: return { kind = .Wayland, wayland = {display = h.display, surface = h.surface}, } case window.Win32_Window_Handle: return { kind = .Win32, win32 = {hwnd = h.hwnd, hinstance = h.hinstance}, } } return {} }