| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- #+build windows
- #+private file
- package karl2d
- @(private="package")
- WINDOW_INTERFACE_WIN32 :: Window_Interface {
- state_size = win32_state_size,
- init = win32_init,
- shutdown = win32_shutdown,
- window_handle = win32_window_handle,
- process_events = win32_process_events,
- get_events = win32_get_events,
- get_width = win32_get_width,
- get_height = win32_get_height,
- clear_events = win32_clear_events,
- set_position = win32_set_position,
- set_size = win32_set_size,
- get_window_scale = win32_get_window_scale,
- set_window_mode = win32_set_window_mode,
- is_gamepad_active = win32_is_gamepad_active,
- get_gamepad_axis = win32_get_gamepad_axis,
- set_gamepad_vibration = win32_set_gamepad_vibration,
- set_internal_state = win32_set_internal_state,
- }
- import win32 "core:sys/windows"
- import "base:runtime"
- win32_state_size :: proc() -> int {
- return size_of(Win32_State)
- }
- win32_init :: proc(
- window_state: rawptr,
- screen_width: int,
- screen_height: int,
- window_title: string,
- init_options: Init_Options,
- allocator: runtime.Allocator,
- ) {
- assert(window_state != nil)
- s = (^Win32_State)(window_state)
- s.allocator = allocator
- s.events = make([dynamic]Window_Event, allocator)
- s.wanted_width = screen_width
- s.wanted_height = screen_height
- s.custom_context = context
-
- win32.SetProcessDpiAwarenessContext(win32.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
- win32.SetProcessDPIAware()
- CLASS_NAME :: "karl2d"
- instance := win32.HINSTANCE(win32.GetModuleHandleW(nil))
- cls := win32.WNDCLASSW {
- style = win32.CS_OWNDC,
- lpfnWndProc = window_proc,
- lpszClassName = CLASS_NAME,
- hInstance = instance,
- hCursor = win32.LoadCursorA(nil, win32.IDC_ARROW),
- }
- win32.RegisterClassW(&cls)
- // We create a window with default position and size. We set the correct size in
- // `win32_set_window_mode`.
- hwnd := win32.CreateWindowW(CLASS_NAME,
- win32.utf8_to_wstring(window_title),
- win32.WS_VISIBLE,
- win32.CW_USEDEFAULT, win32.CW_USEDEFAULT,
- win32.CW_USEDEFAULT, win32.CW_USEDEFAULT,
- nil, nil, instance, nil,
- )
- assert(hwnd != nil, "Failed creating window")
- s.hwnd = hwnd
-
- win32_set_window_mode(init_options.window_mode)
-
- win32.XInputEnable(true)
- }
- win32_shutdown :: proc() {
- delete(s.events)
- win32.DestroyWindow(s.hwnd)
- }
- win32_window_handle :: proc() -> Window_Handle {
- return Window_Handle(s.hwnd)
- }
- win32_process_events :: proc() {
- msg: win32.MSG
- for win32.PeekMessageW(&msg, nil, 0, 0, win32.PM_REMOVE) {
- win32.TranslateMessage(&msg)
- win32.DispatchMessageW(&msg)
- }
- // 4 is the limit set by microsoft, not by us. So I'm not using MAX_GAMEPADS here.
- for gamepad in 0..<4 {
- gp_event: win32.XINPUT_KEYSTROKE
- for win32.XInputGetKeystroke(win32.XUSER(gamepad), 0, &gp_event) == .SUCCESS {
- button: Maybe(Gamepad_Button)
- #partial switch gp_event.VirtualKey {
- case .DPAD_UP: button = .Left_Face_Up
- case .DPAD_DOWN: button = .Left_Face_Down
- case .DPAD_LEFT: button = .Left_Face_Left
- case .DPAD_RIGHT: button = .Left_Face_Right
- case .Y: button = .Right_Face_Up
- case .A: button = .Right_Face_Down
- case .X: button = .Right_Face_Left
- case .B: button = .Right_Face_Right
- case .LSHOULDER: button = .Left_Shoulder
- case .LTRIGGER: button = .Left_Trigger
- case .RSHOULDER: button = .Right_Shoulder
- case .RTRIGGER: button = .Right_Trigger
- case .BACK: button = .Middle_Face_Left
-
- // Not sure you can get the "middle button" with XInput (the one that goe to dashboard)
- case .START: button = .Middle_Face_Right
- case .LTHUMB_PRESS: button = .Left_Stick_Press
- case .RTHUMB_PRESS: button = .Right_Stick_Press
- }
- b := button.? or_continue
- evt: Window_Event
- if .KEYDOWN in gp_event.Flags {
- evt = Window_Event_Gamepad_Button_Went_Down {
- gamepad = gamepad,
- button = b,
- }
- } else if .KEYUP in gp_event.Flags {
- evt = Window_Event_Gamepad_Button_Went_Up {
- gamepad = gamepad,
- button = b,
- }
- }
- if evt != nil {
- append(&s.events, evt)
- }
- }
- }
-
- }
- win32_get_events :: proc() -> []Window_Event {
- return s.events[:]
- }
- win32_get_width :: proc() -> int {
- return s.current_width
- }
- win32_get_height :: proc() -> int {
- return s.current_height
- }
- win32_clear_events :: proc() {
- runtime.clear(&s.events)
- }
- win32_set_position :: proc(x: int, y: int) {
- // TODO: Does x, y respect monitor DPI?
- win32.SetWindowPos(
- s.hwnd,
- {},
- i32(x),
- i32(y),
- 0,
- 0,
- win32.SWP_NOACTIVATE | win32.SWP_NOZORDER | win32.SWP_NOSIZE,
- )
- }
- win32_set_size :: proc(w, h: int) {
- win32.SetWindowPos(
- s.hwnd,
- {},
- 0,
- 0,
- i32(w),
- i32(h),
- win32.SWP_NOACTIVATE | win32.SWP_NOZORDER | win32.SWP_NOMOVE,
- )
- }
- win32_get_window_scale :: proc() -> f32 {
- return f32(win32.GetDpiForWindow(s.hwnd))/96.0
- }
- win32_is_gamepad_active :: proc(gamepad: int) -> bool {
- if gamepad < 0 || gamepad >= MAX_GAMEPADS {
- return false
- }
- gp_state: win32.XINPUT_STATE
- return win32.XInputGetState(win32.XUSER(gamepad), &gp_state) == .SUCCESS
- }
- win32_get_gamepad_axis :: proc(gamepad: int, axis: Gamepad_Axis) -> f32 {
- if gamepad < 0 || gamepad >= MAX_GAMEPADS {
- return 0
- }
- gp_state: win32.XINPUT_STATE
- if win32.XInputGetState(win32.XUSER(gamepad), &gp_state) == .SUCCESS {
- gp := gp_state.Gamepad
- // Numbers from https://learn.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad
- STICK_MAX :: 32767
- TRIGGER_MAX :: 255
- switch axis {
- case .Left_Stick_X: return f32(gp.sThumbLX) / STICK_MAX
- case .Left_Stick_Y: return -f32(gp.sThumbLY) / STICK_MAX
- case .Right_Stick_X: return f32(gp.sThumbRX) / STICK_MAX
- case .Right_Stick_Y: return -f32(gp.sThumbRY) / STICK_MAX
- case .Left_Trigger: return f32(gp.bLeftTrigger) / TRIGGER_MAX
- case .Right_Trigger: return f32(gp.bRightTrigger) / TRIGGER_MAX
- }
- }
- return 0
- }
- win32_set_gamepad_vibration :: proc(gamepad: int, left: f32, right: f32) {
- if gamepad < 0 || gamepad >= MAX_GAMEPADS {
- return
- }
- vib := win32.XINPUT_VIBRATION {
- wLeftMotorSpeed = win32.WORD(left * 65535),
- wRightMotorSpeed = win32.WORD(right * 65535),
- }
- win32.XInputSetState(win32.XUSER(gamepad), &vib)
- }
- win32_set_internal_state :: proc(state: rawptr) {
- assert(state != nil)
- s = (^Win32_State)(state)
- }
- Win32_State :: struct {
- allocator: runtime.Allocator,
- custom_context: runtime.Context,
- hwnd: win32.HWND,
- window_mode: Window_Mode,
- current_width: int,
- current_height: int,
- wanted_width: int,
- wanted_height: int,
- // old (x,y) pos, good when returning to windowed mode
- windowed_pos_x: int,
- windowed_pos_y: int,
- events: [dynamic]Window_Event,
- }
- win32_set_window_mode :: proc(window_mode: Window_Mode) {
- s.window_mode = window_mode
- style: win32.DWORD
- switch window_mode {
- case .Windowed:
- style = win32.WS_OVERLAPPED |
- win32.WS_CAPTION |
- win32.WS_SYSMENU |
- win32.WS_MINIMIZEBOX |
- win32.WS_VISIBLE
- case .Windowed_Resizable:
- style = win32.WS_OVERLAPPED |
- win32.WS_CAPTION |
- win32.WS_SYSMENU |
- win32.WS_MINIMIZEBOX |
- win32.WS_VISIBLE |
- win32.WS_THICKFRAME |
- win32.WS_MAXIMIZEBOX
- case .Borderless_Fullscreen:
- style = win32.WS_VISIBLE
- }
- win32.SetWindowLongW(s.hwnd, win32.GWL_STYLE, i32(style))
- #partial switch window_mode {
- case .Windowed, .Windowed_Resizable:
- r: win32.RECT
- r.left = i32(s.windowed_pos_x)
- r.top = i32(s.windowed_pos_y)
- r.right = r.left + i32(s.wanted_width)
- r.bottom = r.top + i32(s.wanted_height)
- win32.AdjustWindowRect(&r, style, false)
- win32.SetWindowPos(
- s.hwnd,
- {},
- i32(r.left),
- i32(r.top),
- i32(r.right - r.left),
- i32(r.bottom - r.top),
- win32.SWP_NOACTIVATE | win32.SWP_NOZORDER,
- )
- case .Borderless_Fullscreen:
- mi := win32.MONITORINFO { cbSize = size_of (win32.MONITORINFO)}
- mon := win32.MonitorFromWindow(s.hwnd, .MONITOR_DEFAULTTONEAREST)
- if (win32.GetMonitorInfoW(mon, &mi)) {
- win32.SetWindowPos(s.hwnd, win32.HWND_TOP,
- mi.rcMonitor.left, mi.rcMonitor.top,
- mi.rcMonitor.right - mi.rcMonitor.left,
- mi.rcMonitor.bottom - mi.rcMonitor.top,
- win32.SWP_NOOWNERZORDER | win32.SWP_FRAMECHANGED)
- }
- }
- }
- s: ^Win32_State
- window_proc :: proc "stdcall" (hwnd: win32.HWND, msg: win32.UINT, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT {
- context = s.custom_context
- switch msg {
- case win32.WM_DESTROY:
- win32.PostQuitMessage(0)
- case win32.WM_CLOSE:
- append(&s.events, Window_Event_Close_Wanted{})
- case win32.WM_KEYDOWN:
- key := key_from_event_params(wparam, lparam)
- append(&s.events, Window_Event_Key_Went_Down {
- key = key,
- })
- return 0
- case win32.WM_KEYUP:
- key := key_from_event_params(wparam, lparam)
- append(&s.events, Window_Event_Key_Went_Up {
- key = key,
- })
- return 0
- case win32.WM_MOUSEMOVE:
- x := win32.GET_X_LPARAM(lparam)
- y := win32.GET_Y_LPARAM(lparam)
- append(&s.events, Window_Event_Mouse_Move {
- position = {f32(x), f32(y)},
- })
- return 0
- case win32.WM_MOUSEWHEEL:
- delta := f32(win32.GET_WHEEL_DELTA_WPARAM(wparam))/win32.WHEEL_DELTA
- append(&s.events, Window_Event_Mouse_Wheel {
- delta = delta,
- })
- case win32.WM_LBUTTONDOWN:
- append(&s.events, Window_Event_Mouse_Button_Went_Down {
- button = .Left,
- })
- case win32.WM_LBUTTONUP:
- append(&s.events, Window_Event_Mouse_Button_Went_Up {
- button = .Left,
- })
- case win32.WM_MBUTTONDOWN:
- append(&s.events, Window_Event_Mouse_Button_Went_Down {
- button = .Middle,
- })
- case win32.WM_MBUTTONUP:
- append(&s.events, Window_Event_Mouse_Button_Went_Up {
- button = .Middle,
- })
- case win32.WM_RBUTTONDOWN:
- append(&s.events, Window_Event_Mouse_Button_Went_Down {
- button = .Right,
- })
- case win32.WM_RBUTTONUP:
- append(&s.events, Window_Event_Mouse_Button_Went_Up {
- button = .Right,
- })
- case win32.WM_MOVE:
- if s.window_mode == .Windowed || s.window_mode == .Windowed_Resizable {
- x := win32.LOWORD(lparam)
- y := win32.HIWORD(lparam)
- s.windowed_pos_x = int(x)
- s.windowed_pos_y = int(y)
- }
- case win32.WM_SIZE:
- width := win32.LOWORD(lparam)
- height := win32.HIWORD(lparam)
- s.current_width = int(width)
- s.current_height = int(height)
- append(&s.events, Window_Event_Resize {
- width = int(width),
- height = int(height),
- })
- }
- return win32.DefWindowProcW(hwnd, msg, wparam, lparam)
- }
- key_from_event_params :: proc(wparam: win32.WPARAM, lparam: win32.LPARAM) -> Keyboard_Key{
- if wparam == win32.VK_RETURN && win32.HIWORD(lparam) & win32.KF_EXTENDED != 0 {
- return .NP_Enter
- }
- return WIN32_VK_MAP[wparam]
- }
- WIN32_VK_MAP := [255]Keyboard_Key {
- win32.VK_0 = .N0,
- win32.VK_1 = .N1,
- win32.VK_2 = .N2,
- win32.VK_3 = .N3,
- win32.VK_4 = .N4,
- win32.VK_5 = .N5,
- win32.VK_6 = .N6,
- win32.VK_7 = .N7,
- win32.VK_8 = .N8,
- win32.VK_9 = .N9,
- win32.VK_A = .A,
- win32.VK_B = .B,
- win32.VK_C = .C,
- win32.VK_D = .D,
- win32.VK_E = .E,
- win32.VK_F = .F,
- win32.VK_G = .G,
- win32.VK_H = .H,
- win32.VK_I = .I,
- win32.VK_J = .J,
- win32.VK_K = .K,
- win32.VK_L = .L,
- win32.VK_M = .M,
- win32.VK_N = .N,
- win32.VK_O = .O,
- win32.VK_P = .P,
- win32.VK_Q = .Q,
- win32.VK_R = .R,
- win32.VK_S = .S,
- win32.VK_T = .T,
- win32.VK_U = .U,
- win32.VK_V = .V,
- win32.VK_W = .W,
- win32.VK_X = .X,
- win32.VK_Y = .Y,
- win32.VK_Z = .Z,
- win32.VK_OEM_7 = .Apostrophe,
- win32.VK_OEM_COMMA = .Comma,
- win32.VK_OEM_MINUS = .Minus,
- win32.VK_OEM_PERIOD = .Period,
- win32.VK_OEM_2 = .Slash,
- win32.VK_OEM_1 = .Semicolon,
- win32.VK_OEM_PLUS = .Equal,
- win32.VK_OEM_4 = .Left_Bracket,
- win32.VK_OEM_5 = .Backslash,
- win32.VK_OEM_6 = .Right_Bracket,
- win32.VK_OEM_3 = .Backtick,
- win32.VK_SPACE = .Space,
- win32.VK_ESCAPE = .Escape,
- win32.VK_RETURN = .Enter,
- win32.VK_TAB = .Tab,
- win32.VK_BACK = .Backspace,
- win32.VK_INSERT = .Insert,
- win32.VK_DELETE = .Delete,
- win32.VK_RIGHT = .Right,
- win32.VK_LEFT = .Left,
- win32.VK_DOWN = .Down,
- win32.VK_UP = .Up,
- win32.VK_PRIOR = .Page_Up,
- win32.VK_NEXT = .Page_Down,
- win32.VK_HOME = .Home,
- win32.VK_END = .End,
- win32.VK_CAPITAL = .Caps_Lock,
- win32.VK_SCROLL = .Scroll_Lock,
- win32.VK_NUMLOCK = .Num_Lock,
- win32.VK_PRINT = .Print_Screen,
- win32.VK_PAUSE = .Pause,
- win32.VK_F1 = .F1,
- win32.VK_F2 = .F2,
- win32.VK_F3 = .F3,
- win32.VK_F4 = .F4,
- win32.VK_F5 = .F5,
- win32.VK_F6 = .F6,
- win32.VK_F7 = .F7,
- win32.VK_F8 = .F8,
- win32.VK_F9 = .F9,
- win32.VK_F10 = .F10,
- win32.VK_F11 = .F11,
- win32.VK_F12 = .F12,
- win32.VK_LSHIFT = .Left_Shift,
- win32.VK_LCONTROL = .Left_Control,
- win32.VK_LMENU = .Left_Alt,
- win32.VK_MENU = .Left_Alt,
- win32.VK_LWIN = .Left_Super,
- win32.VK_RSHIFT = .Right_Shift,
- win32.VK_RCONTROL = .Right_Control,
- win32.VK_RMENU = .Right_Alt,
- win32.VK_RWIN = .Right_Super,
- win32.VK_APPS = .Menu,
- win32.VK_NUMPAD0 = .NP_0,
- win32.VK_NUMPAD1 = .NP_1,
- win32.VK_NUMPAD2 = .NP_2,
- win32.VK_NUMPAD3 = .NP_3,
- win32.VK_NUMPAD4 = .NP_4,
- win32.VK_NUMPAD5 = .NP_5,
- win32.VK_NUMPAD6 = .NP_6,
- win32.VK_NUMPAD7 = .NP_7,
- win32.VK_NUMPAD8 = .NP_8,
- win32.VK_NUMPAD9 = .NP_9,
-
- win32.VK_DECIMAL = .NP_Decimal,
- win32.VK_DIVIDE = .NP_Divide,
- win32.VK_MULTIPLY = .NP_Multiply,
- win32.VK_SUBTRACT = .NP_Subtract,
- win32.VK_ADD = .NP_Add,
- // NP_Enter is handled separately
- win32.VK_OEM_NEC_EQUAL = .NP_Equal,
- }
|