279 lines
9.1 KiB
Odin
279 lines
9.1 KiB
Odin
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)
|
|
|
|
set_event_queue(hwnd, event_queue)
|
|
|
|
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
|
|
}
|
|
} |