package window import "core:container/queue" import "core:fmt" import "core:log" import "base:runtime" import win "core:sys/windows" Backend_Info :: struct { instance: win.HINSTANCE, 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_w := win.utf8_to_wstring(WINDOW_CLASS_NAME) cls := win.WNDCLASSW { lpfnWndProc = wnd_proc, lpszClassName = class_name_w, hInstance = instance, hCursor = win.LoadCursorA(nil, win.IDC_ARROW), } class := win.RegisterClassW(&cls) assert(class != 0, "Class creation failed") screen_width := win.GetSystemMetrics(win.SM_CXSCREEN) screen_height := win.GetSystemMetrics(win.SM_CYSCREEN) window_style := win.WS_OVERLAPPEDWINDOW | win.WS_VISIBLE | win.CS_OWNDC // TODO: SS - Allow customizing these? rect := win.RECT { 0, 0, i32(window.width), i32(window.height) } win.AdjustWindowRectEx(&rect, window_style, win.FALSE, 0) actual_window_width := rect.right - rect.left actual_window_height := rect.bottom - rect.top hwnd := win.CreateWindowW( 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, nWidth = actual_window_width, nHeight = actual_window_height, hWndParent = nil, hMenu = nil, hInstance = instance, lpParam = &window.event_queue ) assert(hwnd != nil, "Window creation failed") window.backend = { instance = instance, hwnd = hwnd, } // fmt.printfln("Window size: %vx%v.", actual_window_width, actual_window_height) return true } set_title_windows :: proc(window: ^Window) { assert(window != nil) win.SetWindowTextW(window.backend.hwnd, win.utf8_to_wstring(window.title)) } update_windows :: proc(window: ^Window) { assert(window != nil) { // 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) } } } 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 { switch vk { case win.VK_0: return .Number_0 case win.VK_1: return .Number_1 case win.VK_2: return .Number_2 case win.VK_3: return .Number_3 case win.VK_4: return .Number_4 case win.VK_5: return .Number_5 case win.VK_6: return .Number_6 case win.VK_7: return .Number_7 case win.VK_8: return .Number_8 case win.VK_9: return .Number_9 case win.VK_A: return .Letter_A case win.VK_B: return .Letter_B case win.VK_C: return .Letter_C case win.VK_D: return .Letter_D case win.VK_E: return .Letter_E case win.VK_F: return .Letter_F case win.VK_G: return .Letter_G case win.VK_H: return .Letter_H case win.VK_I: return .Letter_I case win.VK_J: return .Letter_J case win.VK_K: return .Letter_K case win.VK_L: return .Letter_L case win.VK_M: return .Letter_M case win.VK_N: return .Letter_N case win.VK_O: return .Letter_O case win.VK_P: return .Letter_P case win.VK_Q: return .Letter_Q case win.VK_R: return .Letter_R case win.VK_S: return .Letter_S case win.VK_T: return .Letter_T case win.VK_U: return .Letter_U case win.VK_V: return .Letter_V case win.VK_W: return .Letter_W case win.VK_X: return .Letter_X case win.VK_Y: return .Letter_Y case win.VK_Z: return .Letter_Z case win.VK_SPACE: return .Space case win.VK_RETURN: return .Enter case win.VK_ESCAPE: return .Escape case win.VK_TAB: return .Tab case win.VK_BACK: return .Backspace case win.VK_CAPITAL: return .CapsLock case win.VK_SHIFT: return .Shift case win.VK_CONTROL: return .Control case win.VK_MENU: return .Alt case win.VK_LEFT: return .Arrow_Left case win.VK_RIGHT: return .Arrow_Right case win.VK_UP: return .Arrow_Up case win.VK_DOWN: return .Arrow_Down case: return .Unknown } }