Files
window/window_windows.odin
2025-11-14 11:55:19 +01:00

209 lines
6.8 KiB
Odin

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
}
}