diff --git a/window.odin b/window.odin index a2bad34..ac3fb27 100644 --- a/window.odin +++ b/window.odin @@ -1,13 +1,19 @@ package window +import "core:container/queue" import "core:fmt" + Window :: struct { title: string, width, height: u16, backend: Backend_Info, + + event_queue: Event_Queue, } +Event_Queue :: queue.Queue(Event) + Event :: union { Event_Quit, Event_Input, @@ -54,7 +60,6 @@ Event_Input :: union { } Event_Resize :: struct { - old_width, old_height: u16, new_width, new_height: u16, } @@ -64,6 +69,7 @@ create :: proc(width, height: u16) -> (^Window, bool) { w := new(Window) w.width = width w.height = height + queue.init(&w.event_queue, 1024) when ODIN_OS == .Windows { if !init_window_windows(w) { @@ -95,7 +101,7 @@ set_title :: proc(window: ^Window, title: string) { get_pointer_to_surface :: proc(window: ^Window) -> rawptr { when ODIN_OS == .Windows { - // fmt.printfln("HWND: %v.", window.backend.hwnd) + fmt.printfln("HWND: %v.", window.backend.hwnd) return window.backend.hwnd } else { @@ -105,29 +111,38 @@ get_pointer_to_surface :: proc(window: ^Window) -> rawptr { update :: proc(window: ^Window, out_event: ^Event) -> bool { assert(window != nil) + assert(out_event != nil) - event: Event when ODIN_OS == .Windows { - event = update_windows(window) + update_windows(window) } else { #assert(false, "Missing implementation for 'set_title'.") } + + out_event^ = nil - out_event^ = event - - if out_event^ == nil { + event, ok := queue.pop_front_safe(&window.event_queue) + if !ok { return false } + assert(event != nil) + out_event^ = event + return true } destroy :: proc(window: ^Window) { assert(window != nil) - destroy_windows(window) - + when ODIN_OS == .Windows { + destroy_windows(window) + } + else { + #assert(false, "Missing implementation for 'destroy'.") + } + + queue.destroy(&window.event_queue) free(window) - window^ = {} } \ No newline at end of file diff --git a/window_windows.odin b/window_windows.odin index a922769..0fcfc3b 100644 --- a/window_windows.odin +++ b/window_windows.odin @@ -1,6 +1,9 @@ package window +import "core:container/queue" import "core:fmt" +import "core:log" +import "base:runtime" import win "core:sys/windows" Backend_Info :: struct { @@ -8,31 +11,142 @@ Backend_Info :: struct { hwnd: win.HWND, } +@(private="file") set_event_queue :: #force_inline proc(hwnd: win.HWND, event_queue: ^Event_Queue) { + win.SetWindowLongPtrW(hwnd, win.GWLP_USERDATA, win.LONG_PTR(uintptr(event_queue))) +} +@(private="file") get_event_queue :: #force_inline proc(hwnd: win.HWND) -> ^Event_Queue { + return (^Event_Queue)(rawptr(uintptr(win.GetWindowLongPtrW(hwnd, win.GWLP_USERDATA)))) +} + +@(private="file") wm_create :: proc(hwnd: win.HWND, lparam: win.LPARAM) -> win.LRESULT { + pcs := (^win.CREATESTRUCTW)(rawptr(uintptr(lparam))) + assert(pcs != nil) + + event_queue := (^Event_Queue)(pcs.lpCreateParams) + assert(event_queue != nil) + + fmt.printfln("Event queue (%v): %v ", &event_queue, event_queue) + + set_event_queue(hwnd, event_queue) + + fmt.printfln("CREATE") + return 0 +} + +@(private="file") wnd_proc :: proc "system" (hwnd: win.HWND, msg: win.UINT, wparam: win.WPARAM, lparam: win.LPARAM) -> win.LRESULT { + context = runtime.default_context() + + event_queue := get_event_queue(hwnd) + switch(msg) { + case win.WM_CREATE: return wm_create(hwnd, lparam) + case win.WM_KEYDOWN, win.WM_SYSKEYDOWN: { + input_event: Event_Input = Event_Keyboard { + virtual_key = get_virtual_key_windows(i32(wparam)), + state = .Down + } + + assert(event_queue != nil) + queue.push_back(event_queue, input_event) + return 0 + } + case win.WM_KEYUP, win.WM_SYSKEYUP: { + input_event: Event_Input = Event_Keyboard { + virtual_key = get_virtual_key_windows(i32(wparam)), + state = .Up + } + + assert(event_queue != nil) + queue.push_back(event_queue, input_event) + return 0 + } + // Mouse. + case win.WM_MOUSEMOVE: { + x := win.GET_X_LPARAM(lparam) + y := win.GET_Y_LPARAM(lparam) + + assert(x >= 0) + assert(y >= 0) + + mouse_event: Event_Mouse = Event_Mouse_Move { + x = u16(x), + y = u16(y), + } + + assert(event_queue != nil) + queue.push_back(event_queue, Event_Input(mouse_event)) + return 0 + } + case win.WM_LBUTTONDOWN, win.WM_LBUTTONUP: { + mouse_event: Event_Mouse = Event_Mouse_Button { + button = .Left, + state = msg == win.WM_LBUTTONDOWN ? .Down : .Up + } + + assert(event_queue != nil) + queue.push_back(event_queue, Event_Input(mouse_event)) + return 0 + } + case win.WM_MBUTTONDOWN, win.WM_MBUTTONUP: { + mouse_event: Event_Mouse = Event_Mouse_Button { + button = .Middle, + state = msg == win.WM_MBUTTONDOWN ? .Down : .Up + } + + assert(event_queue != nil) + queue.push_back(event_queue, Event_Input(mouse_event)) + return 0 + } + case win.WM_RBUTTONDOWN, win.WM_RBUTTONUP: { + mouse_event: Event_Mouse = Event_Mouse_Button { + button = .Right, + state = msg == win.WM_RBUTTONDOWN ? .Down : .Up + } + + assert(event_queue != nil) + queue.push_back(event_queue, Event_Input(mouse_event)) + return 0 + } + case win.WM_SIZE: { + new_width := u16(win.LOWORD(lparam)) + new_height := u16(win.HIWORD(lparam)) + + assert(event_queue != nil) + queue.push_back(event_queue, Event_Resize { new_width, new_height, }) + return 0; + } + case win.WM_CLOSE: { + assert(event_queue != nil) + queue.push_back(event_queue, Event_Quit {}) + win.DestroyWindow(hwnd) + } + case win.WM_DESTROY: { + win.PostQuitMessage(0) + return 0 + } + case win.WM_ERASEBKGND: { + return 1 // paint should fill out the client area so no need to erase the background + } + case: { + return win.DefWindowProcW(hwnd, msg, wparam, lparam) + } + } + + return 0 + } + +WINDOW_CLASS_NAME :: "ENSENN_WINDOW" + init_window_windows :: proc(window: ^Window) -> bool { assert(window != nil) instance := win.HINSTANCE(win.GetModuleHandleW(nil)) assert(instance != nil, "Failed to fetch current instance") - CLASS_NAME :: "ENSENN_WINDOW" + class_name_w := win.utf8_to_wstring(WINDOW_CLASS_NAME) cls := win.WNDCLASSW { - lpfnWndProc = proc "stdcall" (hwnd: win.HWND, msg: win.UINT, wparam: win.WPARAM, lparam: win.LPARAM) -> win.LRESULT { - switch(msg) { - case win.WM_CLOSE: { - win.DestroyWindow(hwnd) - } - case win.WM_DESTROY: { - win.PostQuitMessage(0) - } - case: { - return win.DefWindowProcW(hwnd, msg, wparam, lparam) - } - } - - return 0 - }, - lpszClassName = win.utf8_to_wstring(CLASS_NAME), + lpfnWndProc = wnd_proc, + lpszClassName = class_name_w, hInstance = instance, hCursor = win.LoadCursorA(nil, win.IDC_ARROW), } @@ -52,8 +166,8 @@ init_window_windows :: proc(window: ^Window) -> bool { actual_window_height := rect.bottom - rect.top hwnd := win.CreateWindowW( - lpClassName = win.L(CLASS_NAME), - lpWindowName = win.L(CLASS_NAME), + lpClassName = win.L(WINDOW_CLASS_NAME), + lpWindowName = win.L(WINDOW_CLASS_NAME), dwStyle = window_style, X = (screen_width - i32(window.width)) / 2, Y = (screen_height - i32(window.height)) / 2, @@ -62,7 +176,7 @@ init_window_windows :: proc(window: ^Window) -> bool { hWndParent = nil, hMenu = nil, hInstance = instance, - lpParam = nil + lpParam = &window.event_queue ) assert(hwnd != nil, "Window creation failed") @@ -81,124 +195,30 @@ set_title_windows :: proc(window: ^Window) { win.SetWindowTextW(window.backend.hwnd, win.utf8_to_wstring(window.title)) } -update_windows :: proc(window: ^Window) -> Event { - msg: win.MSG +update_windows :: proc(window: ^Window) { + assert(window != nil) - { // Resizing. - rect: win.RECT - win.GetClientRect(window.backend.hwnd, &rect) - - new_width := u16(rect.right - rect.left) - new_height := u16(rect.bottom - rect.top) - - if new_width != window.width || new_height != window.height { - old_width := window.width - old_height := window.height - - window.width = new_width - window.height = new_height - - if new_width > 0 && new_height > 0 { - return Event_Resize { - old_width, old_height, - window.width, window.height, - } - } + { // Message loop. + msg: win.MSG + for win.PeekMessageW(lpMsg = &msg, hWnd = nil, wMsgFilterMin = 0, wMsgFilterMax = 0, wRemoveMsg = win.PM_REMOVE) == win.TRUE { + win.TranslateMessage(&msg) + win.DispatchMessageW(&msg) } } - - win.CoInitializeEx(nil) - - ok := win.PeekMessageW( - lpMsg = &msg, - hWnd = nil, // NOTE: SS - Don't like that this needs to be nil but apparently it does for this window to receive the WM_CLOSE and WM_QUIT messages. - // If wMsgFilterMin and wMsgFilterMax are both zero, - // PeekMessage returns all available messages (that is, no range filtering is performed). - wMsgFilterMin = 0, wMsgFilterMax = 0, - wRemoveMsg = win.PM_REMOVE // If 'PM_REMOVE', messages are removed from the queue after processing by PeekMessage. - ) - if !ok { - // No messages. - return nil - } - - win.TranslateMessage(&msg) - win.DispatchMessageW(&msg) - - // fmt.printfln("Message type: %x", msg.message) - - switch msg.message { - // Keyboard. - case win.WM_KEYDOWN, win.WM_SYSKEYDOWN: { - input_event: Event_Input = Event_Keyboard { - virtual_key = get_virtual_key_windows(i32(msg.wParam)), - state = .Down - } - - return input_event - } - case win.WM_KEYUP, win.WM_SYSKEYUP: { - input_event: Event_Input = Event_Keyboard { - virtual_key = get_virtual_key_windows(i32(msg.wParam)), - state = .Up - } - - return input_event - } - // Mouse. - case win.WM_MOUSEMOVE: { - x := win.GET_X_LPARAM(msg.lParam) - y := win.GET_Y_LPARAM(msg.lParam) - - assert(x >= 0) - assert(y >= 0) - - mouse_event: Event_Mouse = Event_Mouse_Move{ - x = u16(x), - y = u16(y), - } - - return Event_Input(mouse_event) - } - case win.WM_LBUTTONDOWN, win.WM_LBUTTONUP: { - mouse_event: Event_Mouse = Event_Mouse_Button { - button = .Left, - state = msg.message == win.WM_LBUTTONDOWN ? .Down : .Up - } - - return Event_Input(mouse_event) - } - case win.WM_MBUTTONDOWN, win.WM_MBUTTONUP: { - mouse_event: Event_Mouse = Event_Mouse_Button { - button = .Middle, - state = msg.message == win.WM_MBUTTONDOWN ? .Down : .Up - } - - return Event_Input(mouse_event) - } - case win.WM_RBUTTONDOWN, win.WM_RBUTTONUP: { - mouse_event: Event_Mouse = Event_Mouse_Button { - button = .Right, - state = msg.message == win.WM_RBUTTONDOWN ? .Down : .Up - } - - return Event_Input(mouse_event) - } - - case win.WM_QUIT: { - return Event_Quit {} - } - } - - return nil } destroy_windows :: proc(window: ^Window) { assert(window != nil) assert(window.backend.hwnd != nil) + win.SetWindowLongPtrW(window.backend.hwnd, win.GWLP_USERDATA, 0) + win.DestroyWindow(window.backend.hwnd) window.backend.hwnd = nil + + class_name_w := win.utf8_to_wstring(WINDOW_CLASS_NAME) + win.UnregisterClassW(class_name_w, window.backend.instance) + } @(private) get_virtual_key_windows :: proc(vk: i32) -> Virtual_Key {