Initial commit.
This commit is contained in:
117
window.odin
Normal file
117
window.odin
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package window
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
Window :: struct {
|
||||||
|
title: string,
|
||||||
|
width, height: u16,
|
||||||
|
|
||||||
|
backend: Backend_Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
Event :: union {
|
||||||
|
Event_Quit,
|
||||||
|
Event_Key,
|
||||||
|
Event_Resize,
|
||||||
|
}
|
||||||
|
|
||||||
|
Event_Quit :: struct {}
|
||||||
|
|
||||||
|
Event_Key_State :: enum {
|
||||||
|
Down,
|
||||||
|
Up,
|
||||||
|
}
|
||||||
|
Virtual_Key :: enum {
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
Number_0, Number_1, Number_2, Number_3, Number_4, Number_5, Number_6, Number_7, Number_8, Number_9,
|
||||||
|
|
||||||
|
Letter_A, Letter_B, Letter_C, Letter_D, Letter_E, Letter_F, Letter_G, Letter_H, Letter_I, Letter_J,
|
||||||
|
Letter_K, Letter_L, Letter_M, Letter_N, Letter_O, Letter_P, Letter_Q, Letter_R, Letter_S, Letter_T,
|
||||||
|
Letter_U, Letter_V, Letter_W, Letter_X, Letter_Y, Letter_Z,
|
||||||
|
|
||||||
|
Space, Enter, Escape, Tab, Backspace, CapsLock,
|
||||||
|
Shift, Control, Alt,
|
||||||
|
Arrow_Up, Arrow_Down, Arrow_Left, Arrow_Right,
|
||||||
|
}
|
||||||
|
Event_Key :: struct {
|
||||||
|
virtual_key: Virtual_Key,
|
||||||
|
state: Event_Key_State,
|
||||||
|
}
|
||||||
|
Event_Resize :: struct {
|
||||||
|
old_width, old_height: u16,
|
||||||
|
new_width, new_height: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
create :: proc(width, height: u16) -> (^Window, bool) {
|
||||||
|
assert(width > 0 && height > 0)
|
||||||
|
|
||||||
|
w := new(Window)
|
||||||
|
w.width = width
|
||||||
|
w.height = height
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
if !init_window_windows(w) {
|
||||||
|
free(w)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#assert(false, "Missing implementation for 'create'.")
|
||||||
|
}
|
||||||
|
|
||||||
|
set_title(w, "Window")
|
||||||
|
|
||||||
|
return w, true
|
||||||
|
}
|
||||||
|
|
||||||
|
set_title :: proc(window: ^Window, title: string) {
|
||||||
|
assert(window != nil)
|
||||||
|
|
||||||
|
window.title = title
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
set_title_windows(window)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#assert(false, "Missing implementation for 'set_title'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_pointer_to_surface :: proc(window: ^Window) -> rawptr {
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
// fmt.printfln("HWND: %v.", window.backend.hwnd)
|
||||||
|
return window.backend.hwnd
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#assert(false, "Missing implementation for 'get_pointer_to_surface'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update :: proc(window: ^Window, out_event: ^Event) -> bool {
|
||||||
|
assert(window != nil)
|
||||||
|
|
||||||
|
event: Event
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
event = update_windows(window)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#assert(false, "Missing implementation for 'set_title'.")
|
||||||
|
}
|
||||||
|
|
||||||
|
out_event^ = event
|
||||||
|
|
||||||
|
if out_event^ == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy :: proc(window: ^Window) {
|
||||||
|
assert(window != nil)
|
||||||
|
|
||||||
|
destroy_windows(window)
|
||||||
|
|
||||||
|
free(window)
|
||||||
|
window^ = {}
|
||||||
|
}
|
||||||
209
window_windows.odin
Normal file
209
window_windows.odin
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user