package window import "core:fmt" import win "core:sys/windows" Backend_Info :: struct { instance: win.HINSTANCE, hwnd: win.HWND, } 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" 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), 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(CLASS_NAME), lpWindowName = win.L(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 = nil ) 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) -> Event { msg: win.MSG { // 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, } } } } 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 { case win.WM_KEYDOWN, win.WM_SYSKEYDOWN: { return Event_Key { virtual_key = get_virtual_key_windows(i32(msg.wParam)), state = .Down } } case win.WM_KEYUP, win.WM_SYSKEYUP: { return Event_Key { virtual_key = get_virtual_key_windows(i32(msg.wParam)), state = .Up } } case win.WM_QUIT: { return Event_Quit {} } } return nil } destroy_windows :: proc(window: ^Window) { assert(window != nil) assert(window.backend.hwnd != nil) win.DestroyWindow(window.backend.hwnd) window.backend.hwnd = nil } @(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 } }