window_win32.odin 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. #+build windows
  2. #+private file
  3. package karl2d
  4. @(private="package")
  5. WINDOW_INTERFACE_WIN32 :: Window_Interface {
  6. state_size = win32_state_size,
  7. init = win32_init,
  8. window_handle = win32_window_handle,
  9. process_events = win32_process_events,
  10. get_events = win32_get_events,
  11. clear_events = win32_clear_events,
  12. set_position = win32_set_position,
  13. set_size = win32_set_size,
  14. set_flags = win32_set_flags,
  15. get_gamepad_axis = win32_get_gamepad_axis,
  16. set_gamepad_vibration = win32_set_gamepad_vibration,
  17. set_internal_state = win32_set_internal_state,
  18. }
  19. import win32 "core:sys/windows"
  20. import "base:runtime"
  21. win32_state_size :: proc() -> int {
  22. return size_of(Win32_State)
  23. }
  24. win32_init :: proc(window_state: rawptr, window_width: int, window_height: int, window_title: string,
  25. flags: Window_Flags, allocator: runtime.Allocator) {
  26. assert(window_state != nil)
  27. s = (^Win32_State)(window_state)
  28. s.allocator = allocator
  29. s.events = make([dynamic]Window_Event, allocator)
  30. s.custom_context = context
  31. win32.SetProcessDPIAware()
  32. CLASS_NAME :: "karl2d"
  33. instance := win32.HINSTANCE(win32.GetModuleHandleW(nil))
  34. cls := win32.WNDCLASSW {
  35. lpfnWndProc = window_proc,
  36. lpszClassName = CLASS_NAME,
  37. hInstance = instance,
  38. hCursor = win32.LoadCursorA(nil, win32.IDC_ARROW),
  39. }
  40. win32.RegisterClassW(&cls)
  41. r: win32.RECT
  42. r.right = i32(window_width)
  43. r.bottom = i32(window_height)
  44. s.flags = flags
  45. style := style_from_flags(flags)
  46. win32.AdjustWindowRect(&r, style, false)
  47. hwnd := win32.CreateWindowW(CLASS_NAME,
  48. win32.utf8_to_wstring(window_title),
  49. style,
  50. 100, 10, r.right - r.left, r.bottom - r.top,
  51. nil, nil, instance, nil,
  52. )
  53. win32.XInputEnable(true)
  54. assert(hwnd != nil, "Failed creating window")
  55. s.hwnd = hwnd
  56. }
  57. win32_shutdown :: proc() {
  58. delete(s.events)
  59. win32.DestroyWindow(s.hwnd)
  60. }
  61. win32_window_handle :: proc() -> Window_Handle {
  62. return Window_Handle(s.hwnd)
  63. }
  64. win32_process_events :: proc() {
  65. msg: win32.MSG
  66. for win32.PeekMessageW(&msg, nil, 0, 0, win32.PM_REMOVE) {
  67. win32.TranslateMessage(&msg)
  68. win32.DispatchMessageW(&msg)
  69. }
  70. for gamepad in 0..<4 {
  71. gp_event: win32.XINPUT_KEYSTROKE
  72. for win32.XInputGetKeystroke(win32.XUSER(gamepad), 0, &gp_event) == .SUCCESS {
  73. button: Maybe(Gamepad_Button)
  74. #partial switch gp_event.VirtualKey {
  75. case .DPAD_UP: button = .Left_Face_Up
  76. case .DPAD_DOWN: button = .Left_Face_Down
  77. case .DPAD_LEFT: button = .Left_Face_Left
  78. case .DPAD_RIGHT: button = .Left_Face_Right
  79. case .Y: button = .Right_Face_Up
  80. case .A: button = .Right_Face_Down
  81. case .X: button = .Right_Face_Left
  82. case .B: button = .Right_Face_Right
  83. case .LSHOULDER: button = .Left_Shoulder
  84. case .LTRIGGER: button = .Left_Trigger
  85. case .RSHOULDER: button = .Right_Shoulder
  86. case .RTRIGGER: button = .Right_Trigger
  87. case .BACK: button = .Middle_Face_Left
  88. // Not sure you can get the "middle button" with XInput (the one that goe to dashboard)
  89. case .START: button = .Middle_Face_Right
  90. case .LTHUMB_PRESS: button = .Left_Stick_Press
  91. case .RTHUMB_PRESS: button = .Right_Stick_Press
  92. }
  93. b := button.? or_continue
  94. evt: Window_Event
  95. if .KEYDOWN in gp_event.Flags {
  96. evt = Window_Event_Gamepad_Button_Went_Down {
  97. gamepad = gamepad,
  98. button = b,
  99. }
  100. } else if .KEYUP in gp_event.Flags {
  101. evt = Window_Event_Gamepad_Button_Went_Up {
  102. gamepad = gamepad,
  103. button = b,
  104. }
  105. }
  106. if evt != nil {
  107. append(&s.events, evt)
  108. }
  109. }
  110. }
  111. }
  112. win32_get_events :: proc() -> []Window_Event {
  113. return s.events[:]
  114. }
  115. win32_clear_events :: proc() {
  116. runtime.clear(&s.events)
  117. }
  118. win32_set_position :: proc(x: int, y: int) {
  119. // TODO: Does x, y respect monitor DPI?
  120. win32.SetWindowPos(
  121. s.hwnd,
  122. {},
  123. i32(x),
  124. i32(y),
  125. 0,
  126. 0,
  127. win32.SWP_NOACTIVATE | win32.SWP_NOZORDER | win32.SWP_NOSIZE,
  128. )
  129. }
  130. win32_set_size :: proc(w, h: int) {
  131. win32.SetWindowPos(
  132. s.hwnd,
  133. {},
  134. 0,
  135. 0,
  136. i32(w),
  137. i32(h),
  138. win32.SWP_NOACTIVATE | win32.SWP_NOZORDER | win32.SWP_NOMOVE,
  139. )
  140. }
  141. win32_set_flags :: proc(flags: Window_Flags) {
  142. s.flags = flags
  143. style := style_from_flags(flags)
  144. win32.SetWindowLongW(s.hwnd, win32.GWL_STYLE, i32(style))
  145. }
  146. win32_get_gamepad_axis :: proc(gamepad: int, axis: Gamepad_Axis) -> f32 {
  147. if gamepad < 0 || gamepad >= MAX_GAMEPADS {
  148. return 0
  149. }
  150. gp_state: win32.XINPUT_STATE
  151. if win32.XInputGetState(win32.XUSER(gamepad), &gp_state) == .SUCCESS {
  152. gp := gp_state.Gamepad
  153. // Numbers from https://learn.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad
  154. STICK_MAX :: 32767
  155. TRIGGER_MAX :: 255
  156. switch axis {
  157. case .Left_Stick_X: return f32(gp.sThumbLX) / STICK_MAX
  158. case .Left_Stick_Y: return -f32(gp.sThumbLY) / STICK_MAX
  159. case .Right_Stick_X: return f32(gp.sThumbRX) / STICK_MAX
  160. case .Right_Stick_Y: return -f32(gp.sThumbRY) / STICK_MAX
  161. case .Left_Trigger: return f32(gp.bLeftTrigger) / TRIGGER_MAX
  162. case .Right_Trigger: return f32(gp.bRightTrigger) / TRIGGER_MAX
  163. }
  164. }
  165. return 0
  166. }
  167. win32_set_gamepad_vibration :: proc(gamepad: int, left: f32, right: f32) {
  168. if gamepad < 0 || gamepad >= MAX_GAMEPADS {
  169. return
  170. }
  171. vib := win32.XINPUT_VIBRATION {
  172. wLeftMotorSpeed = win32.WORD(left * 65535),
  173. wRightMotorSpeed = win32.WORD(right * 65535),
  174. }
  175. win32.XInputSetState(win32.XUSER(gamepad), &vib)
  176. }
  177. win32_set_internal_state :: proc(state: rawptr) {
  178. assert(state != nil)
  179. s = (^Win32_State)(state)
  180. }
  181. Win32_State :: struct {
  182. allocator: runtime.Allocator,
  183. custom_context: runtime.Context,
  184. hwnd: win32.HWND,
  185. flags: Window_Flags,
  186. events: [dynamic]Window_Event,
  187. }
  188. style_from_flags :: proc(flags: Window_Flags) -> win32.DWORD {
  189. style := win32.WS_OVERLAPPED | win32.WS_CAPTION | win32.WS_SYSMENU |
  190. win32.WS_MINIMIZEBOX | win32.WS_MAXIMIZEBOX | win32.WS_VISIBLE
  191. if .Resizable in flags {
  192. style |= win32.WS_THICKFRAME
  193. }
  194. return style
  195. }
  196. s: ^Win32_State
  197. window_proc :: proc "stdcall" (hwnd: win32.HWND, msg: win32.UINT, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT {
  198. context = s.custom_context
  199. switch msg {
  200. case win32.WM_DESTROY:
  201. win32.PostQuitMessage(0)
  202. case win32.WM_CLOSE:
  203. append(&s.events, Window_Event_Close_Wanted{})
  204. case win32.WM_KEYDOWN:
  205. key := key_from_event_params(wparam, lparam)
  206. append(&s.events, Window_Event_Key_Went_Down {
  207. key = key,
  208. })
  209. return 0
  210. case win32.WM_KEYUP:
  211. key := key_from_event_params(wparam, lparam)
  212. append(&s.events, Window_Event_Key_Went_Up {
  213. key = key,
  214. })
  215. return 0
  216. case win32.WM_MOUSEMOVE:
  217. x := win32.GET_X_LPARAM(lparam)
  218. y := win32.GET_Y_LPARAM(lparam)
  219. append(&s.events, Window_Event_Mouse_Move {
  220. position = {f32(x), f32(y)},
  221. })
  222. return 0
  223. case win32.WM_MOUSEWHEEL:
  224. delta := f32(win32.GET_WHEEL_DELTA_WPARAM(wparam))/win32.WHEEL_DELTA
  225. append(&s.events, Window_Event_Mouse_Wheel {
  226. delta = delta,
  227. })
  228. case win32.WM_LBUTTONDOWN:
  229. append(&s.events, Window_Event_Mouse_Button_Went_Down {
  230. button = .Left,
  231. })
  232. case win32.WM_LBUTTONUP:
  233. append(&s.events, Window_Event_Mouse_Button_Went_Up {
  234. button = .Left,
  235. })
  236. case win32.WM_MBUTTONDOWN:
  237. append(&s.events, Window_Event_Mouse_Button_Went_Down {
  238. button = .Middle,
  239. })
  240. case win32.WM_MBUTTONUP:
  241. append(&s.events, Window_Event_Mouse_Button_Went_Up {
  242. button = .Middle,
  243. })
  244. case win32.WM_RBUTTONDOWN:
  245. append(&s.events, Window_Event_Mouse_Button_Went_Down {
  246. button = .Right,
  247. })
  248. case win32.WM_RBUTTONUP:
  249. append(&s.events, Window_Event_Mouse_Button_Went_Up {
  250. button = .Right,
  251. })
  252. case win32.WM_SIZE:
  253. width := win32.LOWORD(lparam)
  254. height := win32.HIWORD(lparam)
  255. append(&s.events, Window_Event_Resize {
  256. width = int(width),
  257. height = int(height),
  258. })
  259. }
  260. return win32.DefWindowProcW(hwnd, msg, wparam, lparam)
  261. }
  262. key_from_event_params :: proc(wparam: win32.WPARAM, lparam: win32.LPARAM) -> Keyboard_Key{
  263. if wparam == win32.VK_RETURN && win32.HIWORD(lparam) & win32.KF_EXTENDED != 0 {
  264. return .NP_Enter
  265. }
  266. return WIN32_VK_MAP[wparam]
  267. }
  268. WIN32_VK_MAP := [255]Keyboard_Key {
  269. win32.VK_0 = .N0,
  270. win32.VK_1 = .N1,
  271. win32.VK_2 = .N2,
  272. win32.VK_3 = .N3,
  273. win32.VK_4 = .N4,
  274. win32.VK_5 = .N5,
  275. win32.VK_6 = .N6,
  276. win32.VK_7 = .N7,
  277. win32.VK_8 = .N8,
  278. win32.VK_9 = .N9,
  279. win32.VK_A = .A,
  280. win32.VK_B = .B,
  281. win32.VK_C = .C,
  282. win32.VK_D = .D,
  283. win32.VK_E = .E,
  284. win32.VK_F = .F,
  285. win32.VK_G = .G,
  286. win32.VK_H = .H,
  287. win32.VK_I = .I,
  288. win32.VK_J = .J,
  289. win32.VK_K = .K,
  290. win32.VK_L = .L,
  291. win32.VK_M = .M,
  292. win32.VK_N = .N,
  293. win32.VK_O = .O,
  294. win32.VK_P = .P,
  295. win32.VK_Q = .Q,
  296. win32.VK_R = .R,
  297. win32.VK_S = .S,
  298. win32.VK_T = .T,
  299. win32.VK_U = .U,
  300. win32.VK_V = .V,
  301. win32.VK_W = .W,
  302. win32.VK_X = .X,
  303. win32.VK_Y = .Y,
  304. win32.VK_Z = .Z,
  305. win32.VK_OEM_7 = .Apostrophe,
  306. win32.VK_OEM_COMMA = .Comma,
  307. win32.VK_OEM_MINUS = .Minus,
  308. win32.VK_OEM_PERIOD = .Period,
  309. win32.VK_OEM_2 = .Slash,
  310. win32.VK_OEM_1 = .Semicolon,
  311. win32.VK_OEM_PLUS = .Equal,
  312. win32.VK_OEM_4 = .Left_Bracket,
  313. win32.VK_OEM_5 = .Backslash,
  314. win32.VK_OEM_6 = .Right_Bracket,
  315. win32.VK_OEM_3 = .Grave_Accent,
  316. win32.VK_SPACE = .Space,
  317. win32.VK_ESCAPE = .Escape,
  318. win32.VK_RETURN = .Enter,
  319. win32.VK_TAB = .Tab,
  320. win32.VK_BACK = .Backspace,
  321. win32.VK_INSERT = .Insert,
  322. win32.VK_DELETE = .Delete,
  323. win32.VK_RIGHT = .Right,
  324. win32.VK_LEFT = .Left,
  325. win32.VK_DOWN = .Down,
  326. win32.VK_UP = .Up,
  327. win32.VK_PRIOR = .Page_Up,
  328. win32.VK_NEXT = .Page_Down,
  329. win32.VK_HOME = .Home,
  330. win32.VK_END = .End,
  331. win32.VK_CAPITAL = .Caps_Lock,
  332. win32.VK_SCROLL = .Scroll_Lock,
  333. win32.VK_NUMLOCK = .Num_Lock,
  334. win32.VK_PRINT = .Print_Screen,
  335. win32.VK_PAUSE = .Pause,
  336. win32.VK_F1 = .F1,
  337. win32.VK_F2 = .F2,
  338. win32.VK_F3 = .F3,
  339. win32.VK_F4 = .F4,
  340. win32.VK_F5 = .F5,
  341. win32.VK_F6 = .F6,
  342. win32.VK_F7 = .F7,
  343. win32.VK_F8 = .F8,
  344. win32.VK_F9 = .F9,
  345. win32.VK_F10 = .F10,
  346. win32.VK_F11 = .F11,
  347. win32.VK_F12 = .F12,
  348. win32.VK_LSHIFT = .Left_Shift,
  349. win32.VK_LCONTROL = .Left_Control,
  350. win32.VK_LMENU = .Left_Alt,
  351. win32.VK_MENU = .Left_Alt,
  352. win32.VK_LWIN = .Left_Super,
  353. win32.VK_RSHIFT = .Right_Shift,
  354. win32.VK_RCONTROL = .Right_Control,
  355. win32.VK_RMENU = .Right_Alt,
  356. win32.VK_RWIN = .Right_Super,
  357. win32.VK_APPS = .Menu,
  358. win32.VK_NUMPAD0 = .NP_0,
  359. win32.VK_NUMPAD1 = .NP_1,
  360. win32.VK_NUMPAD2 = .NP_2,
  361. win32.VK_NUMPAD3 = .NP_3,
  362. win32.VK_NUMPAD4 = .NP_4,
  363. win32.VK_NUMPAD5 = .NP_5,
  364. win32.VK_NUMPAD6 = .NP_6,
  365. win32.VK_NUMPAD7 = .NP_7,
  366. win32.VK_NUMPAD8 = .NP_8,
  367. win32.VK_NUMPAD9 = .NP_9,
  368. win32.VK_DECIMAL = .NP_Decimal,
  369. win32.VK_DIVIDE = .NP_Divide,
  370. win32.VK_MULTIPLY = .NP_Multiply,
  371. win32.VK_SUBTRACT = .NP_Subtract,
  372. win32.VK_ADD = .NP_Add,
  373. // NP_Enter is handled separately
  374. win32.VK_OEM_NEC_EQUAL = .NP_Equal,
  375. }