1 /*
2 * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3 * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
4 */
5
6 #include "entry_p.h"
7
8 #if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_WINDOWS
9
10 #include <bgfx/platform.h>
11
12 #include <bx/mutex.h>
13 #include <bx/handlealloc.h>
14 #include <bx/os.h>
15 #include <bx/thread.h>
16 #include <bx/timer.h>
17 #include <bx/uint32_t.h>
18
19 #include <tinystl/allocator.h>
20 #include <tinystl/string.h>
21 #include <tinystl/vector.h>
22
23 #include <windows.h>
24 #include <windowsx.h>
25 #include <xinput.h>
26
27 #ifndef XINPUT_GAMEPAD_GUIDE
28 # define XINPUT_GAMEPAD_GUIDE 0x400
29 #endif // XINPUT_GAMEPAD_GUIDE
30
31 #ifndef XINPUT_DLL_A
32 # define XINPUT_DLL_A "xinput.dll"
33 #endif // XINPUT_DLL_A
34
35 namespace entry
36 {
37 typedef tinystl::vector<WCHAR> WSTRING;
38
UTF8ToUTF16(const char * utf8_str)39 inline WSTRING UTF8ToUTF16(const char *utf8_str)
40 {
41 int len = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);
42 WSTRING utf16(len);
43 MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, utf16.data(), len);
44 return utf16;
45 }
46
47 ///
winSetHwnd(::HWND _window)48 inline void winSetHwnd(::HWND _window)
49 {
50 bgfx::PlatformData pd;
51 bx::memSet(&pd, 0, sizeof(pd) );
52 pd.nwh = _window;
53 bgfx::setPlatformData(pd);
54 }
55
56 typedef DWORD (WINAPI* PFN_XINPUT_GET_STATE)(DWORD dwUserIndex, XINPUT_STATE* pState);
57 typedef void (WINAPI* PFN_XINPUT_ENABLE)(BOOL enable); // 1.4+
58
59 PFN_XINPUT_GET_STATE XInputGetState;
60 PFN_XINPUT_ENABLE XInputEnable;
61
62 struct XInputRemap
63 {
64 uint16_t m_bit;
65 Key::Enum m_key;
66 };
67
68 static XInputRemap s_xinputRemap[] =
69 {
70 { XINPUT_GAMEPAD_DPAD_UP, Key::GamepadUp },
71 { XINPUT_GAMEPAD_DPAD_DOWN, Key::GamepadDown },
72 { XINPUT_GAMEPAD_DPAD_LEFT, Key::GamepadLeft },
73 { XINPUT_GAMEPAD_DPAD_RIGHT, Key::GamepadRight },
74 { XINPUT_GAMEPAD_START, Key::GamepadStart },
75 { XINPUT_GAMEPAD_BACK, Key::GamepadBack },
76 { XINPUT_GAMEPAD_LEFT_THUMB, Key::GamepadThumbL },
77 { XINPUT_GAMEPAD_RIGHT_THUMB, Key::GamepadThumbR },
78 { XINPUT_GAMEPAD_LEFT_SHOULDER, Key::GamepadShoulderL },
79 { XINPUT_GAMEPAD_RIGHT_SHOULDER, Key::GamepadShoulderR },
80 { XINPUT_GAMEPAD_GUIDE, Key::GamepadGuide },
81 { XINPUT_GAMEPAD_A, Key::GamepadA },
82 { XINPUT_GAMEPAD_B, Key::GamepadB },
83 { XINPUT_GAMEPAD_X, Key::GamepadX },
84 { XINPUT_GAMEPAD_Y, Key::GamepadY },
85 };
86
87 struct XInput
88 {
XInputentry::XInput89 XInput()
90 : m_xinputdll(NULL)
91 {
92 bx::memSet(m_connected, 0, sizeof(m_connected) );
93 bx::memSet(m_state, 0, sizeof(m_state) );
94
95 m_deadzone[GamepadAxis::LeftX ] =
96 m_deadzone[GamepadAxis::LeftY ] = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
97 m_deadzone[GamepadAxis::RightX] =
98 m_deadzone[GamepadAxis::RightY] = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
99 m_deadzone[GamepadAxis::LeftZ ] =
100 m_deadzone[GamepadAxis::RightZ] = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
101
102 bx::memSet(m_flip, 1, sizeof(m_flip) );
103 m_flip[GamepadAxis::LeftY ] =
104 m_flip[GamepadAxis::RightY] = -1;
105 }
106
initentry::XInput107 void init()
108 {
109 m_xinputdll = bx::dlopen(XINPUT_DLL_A);
110
111 if (NULL != m_xinputdll)
112 {
113 XInputGetState = (PFN_XINPUT_GET_STATE)bx::dlsym(m_xinputdll, "XInputGetState");
114 // XInputEnable = (PFN_XINPUT_ENABLE )bx::dlsym(m_xinputdll, "XInputEnable" );
115
116 if (NULL == XInputGetState)
117 {
118 shutdown();
119 }
120 }
121 }
122
shutdownentry::XInput123 void shutdown()
124 {
125 if (NULL != m_xinputdll)
126 {
127 bx::dlclose(m_xinputdll);
128 m_xinputdll = NULL;
129 }
130 }
131
filterentry::XInput132 bool filter(GamepadAxis::Enum _axis, int32_t _old, int32_t* _value)
133 {
134 const int32_t deadzone = m_deadzone[_axis];
135 int32_t value = *_value;
136 value = value > deadzone || value < -deadzone ? value : 0;
137 *_value = value * m_flip[_axis];
138 return _old != value;
139 }
140
updateentry::XInput141 void update(EventQueue& _eventQueue)
142 {
143 int64_t now = bx::getHPCounter();
144 static int64_t next = now;
145
146 if (now < next)
147 {
148 return;
149 }
150
151 const int64_t timerFreq = bx::getHPFrequency();
152 next = now + timerFreq/60;
153
154 if (NULL == m_xinputdll)
155 {
156 return;
157 }
158
159 WindowHandle defaultWindow = { 0 };
160
161 for (uint16_t ii = 0; ii < BX_COUNTOF(m_state); ++ii)
162 {
163 XINPUT_STATE state;
164 DWORD result = XInputGetState(ii, &state);
165
166 GamepadHandle handle = { ii };
167
168 bool connected = ERROR_SUCCESS == result;
169 if (connected != m_connected[ii])
170 {
171 _eventQueue.postGamepadEvent(defaultWindow, handle, connected);
172 }
173
174 m_connected[ii] = connected;
175
176 if (connected
177 && m_state[ii].dwPacketNumber != state.dwPacketNumber)
178 {
179 XINPUT_GAMEPAD& gamepad = m_state[ii].Gamepad;
180 const uint16_t changed = gamepad.wButtons ^ state.Gamepad.wButtons;
181 const uint16_t current = gamepad.wButtons;
182 if (0 != changed)
183 {
184 for (uint32_t jj = 0; jj < BX_COUNTOF(s_xinputRemap); ++jj)
185 {
186 uint16_t bit = s_xinputRemap[jj].m_bit;
187 if (bit & changed)
188 {
189 _eventQueue.postKeyEvent(defaultWindow, s_xinputRemap[jj].m_key, 0, 0 == (current & bit) );
190 }
191 }
192
193 gamepad.wButtons = state.Gamepad.wButtons;
194 }
195
196 if (gamepad.bLeftTrigger != state.Gamepad.bLeftTrigger)
197 {
198 int32_t value = state.Gamepad.bLeftTrigger;
199 if (filter(GamepadAxis::LeftZ, gamepad.bLeftTrigger, &value) )
200 {
201 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftZ, value);
202 }
203
204 gamepad.bLeftTrigger = state.Gamepad.bLeftTrigger;
205 }
206
207 if (gamepad.bRightTrigger != state.Gamepad.bRightTrigger)
208 {
209 int32_t value = state.Gamepad.bRightTrigger;
210 if (filter(GamepadAxis::RightZ, gamepad.bRightTrigger, &value) )
211 {
212 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightZ, value);
213 }
214
215 gamepad.bRightTrigger = state.Gamepad.bRightTrigger;
216 }
217
218 if (gamepad.sThumbLX != state.Gamepad.sThumbLX)
219 {
220 int32_t value = state.Gamepad.sThumbLX;
221 if (filter(GamepadAxis::LeftX, gamepad.sThumbLX, &value) )
222 {
223 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftX, value);
224 }
225
226 gamepad.sThumbLX = state.Gamepad.sThumbLX;
227 }
228
229 if (gamepad.sThumbLY != state.Gamepad.sThumbLY)
230 {
231 int32_t value = state.Gamepad.sThumbLY;
232 if (filter(GamepadAxis::LeftY, gamepad.sThumbLY, &value) )
233 {
234 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::LeftY, value);
235 }
236
237 gamepad.sThumbLY = state.Gamepad.sThumbLY;
238 }
239
240 if (gamepad.sThumbRX != state.Gamepad.sThumbRX)
241 {
242 int32_t value = state.Gamepad.sThumbRX;
243 if (filter(GamepadAxis::RightX, gamepad.sThumbRX, &value) )
244 {
245 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightX, value);
246 }
247
248 gamepad.sThumbRX = state.Gamepad.sThumbRX;
249 }
250
251 if (gamepad.sThumbRY != state.Gamepad.sThumbRY)
252 {
253 int32_t value = state.Gamepad.sThumbRY;
254 if (filter(GamepadAxis::RightY, gamepad.sThumbRY, &value) )
255 {
256 _eventQueue.postAxisEvent(defaultWindow, handle, GamepadAxis::RightY, value);
257 }
258
259 gamepad.sThumbRY = state.Gamepad.sThumbRY;
260 }
261 }
262 }
263 }
264
265 void* m_xinputdll;
266
267 int32_t m_deadzone[GamepadAxis::Count];
268 int8_t m_flip[GamepadAxis::Count];
269 XINPUT_STATE m_state[ENTRY_CONFIG_MAX_GAMEPADS];
270 bool m_connected[ENTRY_CONFIG_MAX_GAMEPADS];
271 };
272
273 XInput s_xinput;
274
275 enum
276 {
277 WM_USER_WINDOW_CREATE = WM_USER,
278 WM_USER_WINDOW_DESTROY,
279 WM_USER_WINDOW_SET_TITLE,
280 WM_USER_WINDOW_SET_FLAGS,
281 WM_USER_WINDOW_SET_POS,
282 WM_USER_WINDOW_SET_SIZE,
283 WM_USER_WINDOW_TOGGLE_FRAME,
284 WM_USER_WINDOW_MOUSE_LOCK,
285 };
286
287 struct TranslateKeyModifiers
288 {
289 int m_vk;
290 Modifier::Enum m_modifier;
291 };
292
293 static const TranslateKeyModifiers s_translateKeyModifiers[8] =
294 {
295 { VK_LMENU, Modifier::LeftAlt },
296 { VK_RMENU, Modifier::RightAlt },
297 { VK_LCONTROL, Modifier::LeftCtrl },
298 { VK_RCONTROL, Modifier::RightCtrl },
299 { VK_LSHIFT, Modifier::LeftShift },
300 { VK_RSHIFT, Modifier::RightShift },
301 { VK_LWIN, Modifier::LeftMeta },
302 { VK_RWIN, Modifier::RightMeta },
303 };
304
translateKeyModifiers()305 static uint8_t translateKeyModifiers()
306 {
307 uint8_t modifiers = 0;
308 for (uint32_t ii = 0; ii < BX_COUNTOF(s_translateKeyModifiers); ++ii)
309 {
310 const TranslateKeyModifiers& tkm = s_translateKeyModifiers[ii];
311 modifiers |= 0 > GetKeyState(tkm.m_vk) ? tkm.m_modifier : Modifier::None;
312 }
313 return modifiers;
314 }
315
316 static uint8_t s_translateKey[256];
317
translateKey(WPARAM _wparam)318 static Key::Enum translateKey(WPARAM _wparam)
319 {
320 return (Key::Enum)s_translateKey[_wparam&0xff];
321 }
322
323 struct MainThreadEntry
324 {
325 int m_argc;
326 const char* const* m_argv;
327
328 static int32_t threadFunc(bx::Thread* _thread, void* _userData);
329 };
330
331 struct Msg
332 {
Msgentry::Msg333 Msg()
334 : m_x(0)
335 , m_y(0)
336 , m_width(0)
337 , m_height(0)
338 , m_flags(0)
339 , m_flagsEnabled(false)
340 {
341 }
342
343 int32_t m_x;
344 int32_t m_y;
345 uint32_t m_width;
346 uint32_t m_height;
347 uint32_t m_flags;
348 tinystl::string m_title;
349 bool m_flagsEnabled;
350 };
351
mouseCapture(HWND _hwnd,bool _capture)352 static void mouseCapture(HWND _hwnd, bool _capture)
353 {
354 if (_capture)
355 {
356 SetCapture(_hwnd);
357 }
358 else
359 {
360 ReleaseCapture();
361 }
362 }
363
364 struct Context
365 {
Contextentry::Context366 Context()
367 : m_mz(0)
368 , m_frame(true)
369 , m_mouseLock(NULL)
370 , m_init(false)
371 , m_exit(false)
372 {
373 m_surrogate = 0;
374 bx::memSet(s_translateKey, 0, sizeof(s_translateKey) );
375 s_translateKey[VK_ESCAPE] = Key::Esc;
376 s_translateKey[VK_RETURN] = Key::Return;
377 s_translateKey[VK_TAB] = Key::Tab;
378 s_translateKey[VK_BACK] = Key::Backspace;
379 s_translateKey[VK_SPACE] = Key::Space;
380 s_translateKey[VK_UP] = Key::Up;
381 s_translateKey[VK_DOWN] = Key::Down;
382 s_translateKey[VK_LEFT] = Key::Left;
383 s_translateKey[VK_RIGHT] = Key::Right;
384 s_translateKey[VK_INSERT] = Key::Insert;
385 s_translateKey[VK_DELETE] = Key::Delete;
386 s_translateKey[VK_HOME] = Key::Home;
387 s_translateKey[VK_END] = Key::End;
388 s_translateKey[VK_PRIOR] = Key::PageUp;
389 s_translateKey[VK_NEXT] = Key::PageDown;
390 s_translateKey[VK_SNAPSHOT] = Key::Print;
391 s_translateKey[VK_OEM_PLUS] = Key::Plus;
392 s_translateKey[VK_OEM_MINUS] = Key::Minus;
393 s_translateKey[VK_OEM_4] = Key::LeftBracket;
394 s_translateKey[VK_OEM_6] = Key::RightBracket;
395 s_translateKey[VK_OEM_1] = Key::Semicolon;
396 s_translateKey[VK_OEM_7] = Key::Quote;
397 s_translateKey[VK_OEM_COMMA] = Key::Comma;
398 s_translateKey[VK_OEM_PERIOD] = Key::Period;
399 s_translateKey[VK_DECIMAL] = Key::Period;
400 s_translateKey[VK_OEM_2] = Key::Slash;
401 s_translateKey[VK_OEM_5] = Key::Backslash;
402 s_translateKey[VK_OEM_3] = Key::Tilde;
403 s_translateKey[VK_F1] = Key::F1;
404 s_translateKey[VK_F2] = Key::F2;
405 s_translateKey[VK_F3] = Key::F3;
406 s_translateKey[VK_F4] = Key::F4;
407 s_translateKey[VK_F5] = Key::F5;
408 s_translateKey[VK_F6] = Key::F6;
409 s_translateKey[VK_F7] = Key::F7;
410 s_translateKey[VK_F8] = Key::F8;
411 s_translateKey[VK_F9] = Key::F9;
412 s_translateKey[VK_F10] = Key::F10;
413 s_translateKey[VK_F11] = Key::F11;
414 s_translateKey[VK_F12] = Key::F12;
415 s_translateKey[VK_NUMPAD0] = Key::NumPad0;
416 s_translateKey[VK_NUMPAD1] = Key::NumPad1;
417 s_translateKey[VK_NUMPAD2] = Key::NumPad2;
418 s_translateKey[VK_NUMPAD3] = Key::NumPad3;
419 s_translateKey[VK_NUMPAD4] = Key::NumPad4;
420 s_translateKey[VK_NUMPAD5] = Key::NumPad5;
421 s_translateKey[VK_NUMPAD6] = Key::NumPad6;
422 s_translateKey[VK_NUMPAD7] = Key::NumPad7;
423 s_translateKey[VK_NUMPAD8] = Key::NumPad8;
424 s_translateKey[VK_NUMPAD9] = Key::NumPad9;
425 s_translateKey[uint8_t('0')] = Key::Key0;
426 s_translateKey[uint8_t('1')] = Key::Key1;
427 s_translateKey[uint8_t('2')] = Key::Key2;
428 s_translateKey[uint8_t('3')] = Key::Key3;
429 s_translateKey[uint8_t('4')] = Key::Key4;
430 s_translateKey[uint8_t('5')] = Key::Key5;
431 s_translateKey[uint8_t('6')] = Key::Key6;
432 s_translateKey[uint8_t('7')] = Key::Key7;
433 s_translateKey[uint8_t('8')] = Key::Key8;
434 s_translateKey[uint8_t('9')] = Key::Key9;
435 s_translateKey[uint8_t('A')] = Key::KeyA;
436 s_translateKey[uint8_t('B')] = Key::KeyB;
437 s_translateKey[uint8_t('C')] = Key::KeyC;
438 s_translateKey[uint8_t('D')] = Key::KeyD;
439 s_translateKey[uint8_t('E')] = Key::KeyE;
440 s_translateKey[uint8_t('F')] = Key::KeyF;
441 s_translateKey[uint8_t('G')] = Key::KeyG;
442 s_translateKey[uint8_t('H')] = Key::KeyH;
443 s_translateKey[uint8_t('I')] = Key::KeyI;
444 s_translateKey[uint8_t('J')] = Key::KeyJ;
445 s_translateKey[uint8_t('K')] = Key::KeyK;
446 s_translateKey[uint8_t('L')] = Key::KeyL;
447 s_translateKey[uint8_t('M')] = Key::KeyM;
448 s_translateKey[uint8_t('N')] = Key::KeyN;
449 s_translateKey[uint8_t('O')] = Key::KeyO;
450 s_translateKey[uint8_t('P')] = Key::KeyP;
451 s_translateKey[uint8_t('Q')] = Key::KeyQ;
452 s_translateKey[uint8_t('R')] = Key::KeyR;
453 s_translateKey[uint8_t('S')] = Key::KeyS;
454 s_translateKey[uint8_t('T')] = Key::KeyT;
455 s_translateKey[uint8_t('U')] = Key::KeyU;
456 s_translateKey[uint8_t('V')] = Key::KeyV;
457 s_translateKey[uint8_t('W')] = Key::KeyW;
458 s_translateKey[uint8_t('X')] = Key::KeyX;
459 s_translateKey[uint8_t('Y')] = Key::KeyY;
460 s_translateKey[uint8_t('Z')] = Key::KeyZ;
461 }
462
runentry::Context463 int32_t run(int _argc, const char* const* _argv)
464 {
465 SetDllDirectoryA(".");
466
467 s_xinput.init();
468
469 HINSTANCE instance = (HINSTANCE)GetModuleHandle(NULL);
470
471 WNDCLASSEXW wnd;
472 bx::memSet(&wnd, 0, sizeof(wnd) );
473 wnd.cbSize = sizeof(wnd);
474 wnd.style = CS_HREDRAW | CS_VREDRAW;
475 wnd.lpfnWndProc = wndProc;
476 wnd.hInstance = instance;
477 wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
478 wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
479 wnd.lpszClassName = L"bgfx";
480 wnd.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
481 RegisterClassExW(&wnd);
482
483 m_windowAlloc.alloc();
484 m_hwnd[0] = CreateWindowExA(
485 WS_EX_ACCEPTFILES
486 , "bgfx"
487 , "BGFX"
488 , WS_OVERLAPPEDWINDOW|WS_VISIBLE
489 , 0
490 , 0
491 , ENTRY_DEFAULT_WIDTH
492 , ENTRY_DEFAULT_HEIGHT
493 , NULL
494 , NULL
495 , instance
496 , 0
497 );
498
499 m_flags[0] = 0
500 | ENTRY_WINDOW_FLAG_ASPECT_RATIO
501 | ENTRY_WINDOW_FLAG_FRAME
502 ;
503
504 winSetHwnd(m_hwnd[0]);
505
506 adjust(m_hwnd[0], ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT, true);
507 clear(m_hwnd[0]);
508
509 m_width = ENTRY_DEFAULT_WIDTH;
510 m_height = ENTRY_DEFAULT_HEIGHT;
511 m_oldWidth = ENTRY_DEFAULT_WIDTH;
512 m_oldHeight = ENTRY_DEFAULT_HEIGHT;
513
514 MainThreadEntry mte;
515 mte.m_argc = _argc;
516 mte.m_argv = _argv;
517
518 bgfx::renderFrame();
519
520 bx::Thread thread;
521 thread.init(mte.threadFunc, &mte);
522 m_init = true;
523
524 m_eventQueue.postSizeEvent(findHandle(m_hwnd[0]), m_width, m_height);
525
526 MSG msg;
527 msg.message = WM_NULL;
528
529 while (!m_exit)
530 {
531 bgfx::renderFrame();
532
533 s_xinput.update(m_eventQueue);
534 WaitForInputIdle(GetCurrentProcess(), 16);
535
536 while (0 != PeekMessageW(&msg, NULL, 0U, 0U, PM_REMOVE) )
537 {
538 TranslateMessage(&msg);
539 DispatchMessageW(&msg);
540 }
541 }
542
543 while (bgfx::RenderFrame::NoContext != bgfx::renderFrame() ) {};
544
545 thread.shutdown();
546
547 DestroyWindow(m_hwnd[0]);
548
549 s_xinput.shutdown();
550
551 return thread.getExitCode();
552 }
553
processentry::Context554 LRESULT process(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam)
555 {
556 if (m_init)
557 {
558 switch (_id)
559 {
560 case WM_USER_WINDOW_CREATE:
561 {
562 Msg* msg = (Msg*)_lparam;
563 HWND hwnd = CreateWindowW(L"bgfx"
564 , UTF8ToUTF16(msg->m_title.c_str()).data()
565 , WS_OVERLAPPEDWINDOW|WS_VISIBLE
566 , msg->m_x
567 , msg->m_y
568 , msg->m_width
569 , msg->m_height
570 , NULL
571 , NULL
572 , (HINSTANCE)GetModuleHandle(NULL)
573 , 0
574 );
575 clear(hwnd);
576
577 m_hwnd[_wparam] = hwnd;
578 m_flags[_wparam] = msg->m_flags;
579 WindowHandle handle = { (uint16_t)_wparam };
580 m_eventQueue.postSizeEvent(handle, msg->m_width, msg->m_height);
581 m_eventQueue.postWindowEvent(handle, hwnd);
582
583 delete msg;
584 }
585 break;
586
587 case WM_USER_WINDOW_DESTROY:
588 {
589 WindowHandle handle = { (uint16_t)_wparam };
590 m_eventQueue.postWindowEvent(handle);
591 DestroyWindow(m_hwnd[_wparam]);
592 m_hwnd[_wparam] = 0;
593
594 if (0 == handle.idx)
595 {
596 m_exit = true;
597 m_eventQueue.postExitEvent();
598 }
599 }
600 break;
601
602 case WM_USER_WINDOW_SET_TITLE:
603 {
604 Msg* msg = (Msg*)_lparam;
605 SetWindowTextW(m_hwnd[_wparam], UTF8ToUTF16(msg->m_title.c_str()).data() );
606 delete msg;
607 }
608 break;
609
610 case WM_USER_WINDOW_SET_FLAGS:
611 {
612 Msg* msg = (Msg*)_lparam;
613
614 if (msg->m_flagsEnabled)
615 {
616 m_flags[_wparam] |= msg->m_flags;
617 }
618 else
619 {
620 m_flags[_wparam] &= ~msg->m_flags;
621 }
622
623 delete msg;
624 }
625 break;
626
627 case WM_USER_WINDOW_SET_POS:
628 {
629 Msg* msg = (Msg*)_lparam;
630 SetWindowPos(m_hwnd[_wparam], 0, msg->m_x, msg->m_y, 0, 0
631 , SWP_NOACTIVATE
632 | SWP_NOOWNERZORDER
633 | SWP_NOSIZE
634 );
635 delete msg;
636 }
637 break;
638
639 case WM_USER_WINDOW_SET_SIZE:
640 {
641 uint32_t width = GET_X_LPARAM(_lparam);
642 uint32_t height = GET_Y_LPARAM(_lparam);
643 adjust(m_hwnd[_wparam], width, height, true);
644 }
645 break;
646
647 case WM_USER_WINDOW_TOGGLE_FRAME:
648 {
649 if (m_frame)
650 {
651 m_oldWidth = m_width;
652 m_oldHeight = m_height;
653 }
654 adjust(m_hwnd[_wparam], m_oldWidth, m_oldHeight, !m_frame);
655 }
656 break;
657
658 case WM_USER_WINDOW_MOUSE_LOCK:
659 setMouseLock(m_hwnd[_wparam], !!_lparam);
660 break;
661
662 case WM_DESTROY:
663 break;
664
665 case WM_QUIT:
666 case WM_CLOSE:
667 destroyWindow(findHandle(_hwnd) );
668 // Don't process message. Window will be destroyed later.
669 return 0;
670
671 case WM_SIZING:
672 {
673 WindowHandle handle = findHandle(_hwnd);
674
675 if (isValid(handle)
676 && ENTRY_WINDOW_FLAG_ASPECT_RATIO & m_flags[handle.idx])
677 {
678 RECT& rect = *(RECT*)_lparam;
679 uint32_t width = rect.right - rect.left - m_frameWidth;
680 uint32_t height = rect.bottom - rect.top - m_frameHeight;
681
682 // Recalculate size according to aspect ratio
683 switch (_wparam)
684 {
685 case WMSZ_LEFT:
686 case WMSZ_RIGHT:
687 {
688 float aspectRatio = 1.0f/m_aspectRatio;
689 width = bx::uint32_max(ENTRY_DEFAULT_WIDTH/4, width);
690 height = uint32_t(float(width)*aspectRatio);
691 }
692 break;
693
694 default:
695 {
696 float aspectRatio = m_aspectRatio;
697 height = bx::uint32_max(ENTRY_DEFAULT_HEIGHT/4, height);
698 width = uint32_t(float(height)*aspectRatio);
699 }
700 break;
701 }
702
703 // Recalculate position using different anchor points
704 switch (_wparam)
705 {
706 case WMSZ_TOPLEFT:
707 rect.left = rect.right - width - m_frameWidth;
708 rect.top = rect.bottom - height - m_frameHeight;
709 break;
710
711 case WMSZ_TOP:
712 case WMSZ_TOPRIGHT:
713 rect.right = rect.left + width + m_frameWidth;
714 rect.top = rect.bottom - height - m_frameHeight;
715 break;
716
717 case WMSZ_LEFT:
718 case WMSZ_BOTTOMLEFT:
719 rect.left = rect.right - width - m_frameWidth;
720 rect.bottom = rect.top + height + m_frameHeight;
721 break;
722
723 default:
724 rect.right = rect.left + width + m_frameWidth;
725 rect.bottom = rect.top + height + m_frameHeight;
726 break;
727 }
728
729 m_eventQueue.postSizeEvent(findHandle(_hwnd), width, height);
730 }
731 }
732 return 0;
733
734 case WM_SIZE:
735 {
736 WindowHandle handle = findHandle(_hwnd);
737 if (isValid(handle) )
738 {
739 uint32_t width = GET_X_LPARAM(_lparam);
740 uint32_t height = GET_Y_LPARAM(_lparam);
741
742 m_width = width;
743 m_height = height;
744 m_eventQueue.postSizeEvent(handle, m_width, m_height);
745 }
746 }
747 break;
748
749 case WM_SYSCOMMAND:
750 switch (_wparam)
751 {
752 case SC_MINIMIZE:
753 case SC_RESTORE:
754 {
755 HWND parent = GetWindow(_hwnd, GW_OWNER);
756 if (NULL != parent)
757 {
758 PostMessage(parent, _id, _wparam, _lparam);
759 }
760 }
761 }
762 break;
763
764 case WM_MOUSEMOVE:
765 {
766 int32_t mx = GET_X_LPARAM(_lparam);
767 int32_t my = GET_Y_LPARAM(_lparam);
768
769 if (_hwnd == m_mouseLock)
770 {
771 mx -= m_mx;
772 my -= m_my;
773
774 if (0 == mx
775 && 0 == my)
776 {
777 break;
778 }
779
780 setMousePos(_hwnd, m_mx, m_my);
781 }
782
783 m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz);
784 }
785 break;
786
787 case WM_MOUSEWHEEL:
788 {
789 POINT pt = { GET_X_LPARAM(_lparam), GET_Y_LPARAM(_lparam) };
790 ScreenToClient(_hwnd, &pt);
791 int32_t mx = pt.x;
792 int32_t my = pt.y;
793 m_mz += GET_WHEEL_DELTA_WPARAM(_wparam)/WHEEL_DELTA;
794 m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz);
795 }
796 break;
797
798 case WM_LBUTTONDOWN:
799 case WM_LBUTTONUP:
800 case WM_LBUTTONDBLCLK:
801 {
802 mouseCapture(_hwnd, _id == WM_LBUTTONDOWN);
803 int32_t mx = GET_X_LPARAM(_lparam);
804 int32_t my = GET_Y_LPARAM(_lparam);
805 m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Left, _id == WM_LBUTTONDOWN);
806 }
807 break;
808
809 case WM_MBUTTONDOWN:
810 case WM_MBUTTONUP:
811 case WM_MBUTTONDBLCLK:
812 {
813 mouseCapture(_hwnd, _id == WM_MBUTTONDOWN);
814 int32_t mx = GET_X_LPARAM(_lparam);
815 int32_t my = GET_Y_LPARAM(_lparam);
816 m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Middle, _id == WM_MBUTTONDOWN);
817 }
818 break;
819
820 case WM_RBUTTONDOWN:
821 case WM_RBUTTONUP:
822 case WM_RBUTTONDBLCLK:
823 {
824 mouseCapture(_hwnd, _id == WM_RBUTTONDOWN);
825 int32_t mx = GET_X_LPARAM(_lparam);
826 int32_t my = GET_Y_LPARAM(_lparam);
827 m_eventQueue.postMouseEvent(findHandle(_hwnd), mx, my, m_mz, MouseButton::Right, _id == WM_RBUTTONDOWN);
828 }
829 break;
830
831 case WM_KEYDOWN:
832 case WM_SYSKEYDOWN:
833 case WM_KEYUP:
834 case WM_SYSKEYUP:
835 {
836 uint8_t modifiers = translateKeyModifiers();
837 Key::Enum key = translateKey(_wparam);
838
839 WindowHandle handle = findHandle(_hwnd);
840
841 if (Key::Print == key
842 && 0x3 == ( (uint32_t)(_lparam)>>30) )
843 {
844 // VK_SNAPSHOT doesn't generate keydown event. Fire on down event when previous
845 // key state bit is set to 1 and transition state bit is set to 1.
846 //
847 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280%28v=vs.85%29.aspx
848 m_eventQueue.postKeyEvent(handle, key, modifiers, true);
849 }
850
851 m_eventQueue.postKeyEvent(handle, key, modifiers, _id == WM_KEYDOWN || _id == WM_SYSKEYDOWN);
852 }
853 break;
854
855 case WM_CHAR:
856 {
857 WCHAR utf16[2] = { (WCHAR)_wparam };
858 uint8_t utf8[4] = {};
859
860 if (utf16[0] >= 0xD800 && utf16[0] <= 0xDBFF) {
861 m_surrogate = utf16[0];
862 } else {
863 int utf16_len;
864 if (utf16[0] >= 0xDC00 && utf16[0] <= 0xDFFF) {
865 utf16[1] = utf16[0];
866 utf16[0] = m_surrogate;
867 m_surrogate = 0;
868 utf16_len = 2;
869 } else {
870 utf16_len = 1;
871 }
872
873 uint8_t len = (uint8_t)WideCharToMultiByte(CP_UTF8
874 , 0
875 , utf16
876 , utf16_len
877 , (LPSTR)utf8
878 , BX_COUNTOF(utf8)
879 , NULL
880 , NULL
881 );
882 if (0 != len)
883 {
884 WindowHandle handle = findHandle(_hwnd);
885 m_eventQueue.postCharEvent(handle, len, utf8);
886 }
887 }
888 }
889 break;
890
891 case WM_DROPFILES:
892 {
893 HDROP drop = (HDROP)_wparam;
894 char tmp[bx::kMaxFilePath];
895 WCHAR utf16[bx::kMaxFilePath];
896 uint32_t result = DragQueryFileW(drop, 0, utf16, bx::kMaxFilePath);
897 BX_UNUSED(result);
898 WideCharToMultiByte(CP_UTF8, 0, utf16, -1, tmp, bx::kMaxFilePath, NULL, NULL);
899 WindowHandle handle = findHandle(_hwnd);
900 m_eventQueue.postDropFileEvent(handle, tmp);
901 }
902 break;
903
904 default:
905 break;
906 }
907 }
908
909 return DefWindowProcW(_hwnd, _id, _wparam, _lparam);
910 }
911
findHandleentry::Context912 WindowHandle findHandle(HWND _hwnd)
913 {
914 bx::MutexScope scope(m_lock);
915 for (uint16_t ii = 0, num = m_windowAlloc.getNumHandles(); ii < num; ++ii)
916 {
917 uint16_t idx = m_windowAlloc.getHandleAt(ii);
918 if (_hwnd == m_hwnd[idx])
919 {
920 WindowHandle handle = { idx };
921 return handle;
922 }
923 }
924
925 WindowHandle invalid = { UINT16_MAX };
926 return invalid;
927 }
928
clearentry::Context929 void clear(HWND _hwnd)
930 {
931 RECT rect;
932 GetWindowRect(_hwnd, &rect);
933 HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0) );
934 HDC hdc = GetDC(_hwnd);
935 SelectObject(hdc, brush);
936 FillRect(hdc, &rect, brush);
937 ReleaseDC(_hwnd, hdc);
938 }
939
adjustentry::Context940 void adjust(HWND _hwnd, uint32_t _width, uint32_t _height, bool _windowFrame)
941 {
942 m_width = _width;
943 m_height = _height;
944 m_aspectRatio = float(_width)/float(_height);
945
946 ShowWindow(_hwnd, SW_SHOWNORMAL);
947 RECT rect;
948 RECT newrect = {0, 0, (LONG)_width, (LONG)_height};
949 DWORD style = WS_POPUP|WS_SYSMENU;
950
951 if (m_frame)
952 {
953 GetWindowRect(_hwnd, &m_rect);
954 m_style = GetWindowLong(_hwnd, GWL_STYLE);
955 }
956
957 if (_windowFrame)
958 {
959 rect = m_rect;
960 style = m_style;
961 }
962 else
963 {
964 HMONITOR monitor = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST);
965 MONITORINFO mi;
966 mi.cbSize = sizeof(mi);
967 GetMonitorInfo(monitor, &mi);
968 newrect = mi.rcMonitor;
969 rect = mi.rcMonitor;
970 m_aspectRatio = float(newrect.right - newrect.left)/float(newrect.bottom - newrect.top);
971 }
972
973 SetWindowLong(_hwnd, GWL_STYLE, style);
974 uint32_t prewidth = newrect.right - newrect.left;
975 uint32_t preheight = newrect.bottom - newrect.top;
976 AdjustWindowRect(&newrect, style, FALSE);
977 m_frameWidth = (newrect.right - newrect.left) - prewidth;
978 m_frameHeight = (newrect.bottom - newrect.top ) - preheight;
979 UpdateWindow(_hwnd);
980
981 if (rect.left == -32000
982 || rect.top == -32000)
983 {
984 rect.left = 0;
985 rect.top = 0;
986 }
987
988 int32_t left = rect.left;
989 int32_t top = rect.top;
990 int32_t width = (newrect.right-newrect.left);
991 int32_t height = (newrect.bottom-newrect.top);
992
993 if (!_windowFrame)
994 {
995 float aspectRatio = 1.0f/m_aspectRatio;
996 width = bx::uint32_max(ENTRY_DEFAULT_WIDTH/4, width);
997 height = uint32_t(float(width)*aspectRatio);
998
999 left = newrect.left+(newrect.right -newrect.left-width)/2;
1000 top = newrect.top +(newrect.bottom-newrect.top-height)/2;
1001 }
1002
1003 SetWindowPos(_hwnd
1004 , HWND_TOP
1005 , left
1006 , top
1007 , width
1008 , height
1009 , SWP_SHOWWINDOW
1010 );
1011
1012 ShowWindow(_hwnd, SW_RESTORE);
1013
1014 m_frame = _windowFrame;
1015 }
1016
setMousePosentry::Context1017 void setMousePos(HWND _hwnd, int32_t _mx, int32_t _my)
1018 {
1019 POINT pt = { _mx, _my };
1020 ClientToScreen(_hwnd, &pt);
1021 SetCursorPos(pt.x, pt.y);
1022 }
1023
setMouseLockentry::Context1024 void setMouseLock(HWND _hwnd, bool _lock)
1025 {
1026 if (_hwnd != m_mouseLock)
1027 {
1028 if (_lock)
1029 {
1030 m_mx = m_width/2;
1031 m_my = m_height/2;
1032 ShowCursor(false);
1033 setMousePos(_hwnd, m_mx, m_my);
1034 }
1035 else
1036 {
1037 setMousePos(_hwnd, m_mx, m_my);
1038 ShowCursor(true);
1039 }
1040
1041 m_mouseLock = _hwnd;
1042 }
1043 }
1044
1045 static LRESULT CALLBACK wndProc(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam);
1046
1047 EventQueue m_eventQueue;
1048 WCHAR m_surrogate;
1049 bx::Mutex m_lock;
1050
1051 bx::HandleAllocT<ENTRY_CONFIG_MAX_WINDOWS> m_windowAlloc;
1052
1053 HWND m_hwnd[ENTRY_CONFIG_MAX_WINDOWS];
1054 uint32_t m_flags[ENTRY_CONFIG_MAX_WINDOWS];
1055 RECT m_rect;
1056 DWORD m_style;
1057 uint32_t m_width;
1058 uint32_t m_height;
1059 uint32_t m_oldWidth;
1060 uint32_t m_oldHeight;
1061 uint32_t m_frameWidth;
1062 uint32_t m_frameHeight;
1063 float m_aspectRatio;
1064
1065 int32_t m_mx;
1066 int32_t m_my;
1067 int32_t m_mz;
1068
1069 bool m_frame;
1070 HWND m_mouseLock;
1071 bool m_init;
1072 bool m_exit;
1073
1074 };
1075
1076 static Context s_ctx;
1077
wndProc(HWND _hwnd,UINT _id,WPARAM _wparam,LPARAM _lparam)1078 LRESULT CALLBACK Context::wndProc(HWND _hwnd, UINT _id, WPARAM _wparam, LPARAM _lparam)
1079 {
1080 return s_ctx.process(_hwnd, _id, _wparam, _lparam);
1081 }
1082
poll()1083 const Event* poll()
1084 {
1085 return s_ctx.m_eventQueue.poll();
1086 }
1087
poll(WindowHandle _handle)1088 const Event* poll(WindowHandle _handle)
1089 {
1090 return s_ctx.m_eventQueue.poll(_handle);
1091 }
1092
release(const Event * _event)1093 void release(const Event* _event)
1094 {
1095 s_ctx.m_eventQueue.release(_event);
1096 }
1097
createWindow(int32_t _x,int32_t _y,uint32_t _width,uint32_t _height,uint32_t _flags,const char * _title)1098 WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title)
1099 {
1100 bx::MutexScope scope(s_ctx.m_lock);
1101 WindowHandle handle = { s_ctx.m_windowAlloc.alloc() };
1102
1103 if (UINT16_MAX != handle.idx)
1104 {
1105 Msg* msg = new Msg;
1106 msg->m_x = _x;
1107 msg->m_y = _y;
1108 msg->m_width = _width;
1109 msg->m_height = _height;
1110 msg->m_title = _title;
1111 msg->m_flags = _flags;
1112 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_CREATE, handle.idx, (LPARAM)msg);
1113 }
1114
1115 return handle;
1116 }
1117
destroyWindow(WindowHandle _handle)1118 void destroyWindow(WindowHandle _handle)
1119 {
1120 if (UINT16_MAX != _handle.idx)
1121 {
1122 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_DESTROY, _handle.idx, 0);
1123
1124 bx::MutexScope scope(s_ctx.m_lock);
1125 s_ctx.m_windowAlloc.free(_handle.idx);
1126 }
1127 }
1128
setWindowPos(WindowHandle _handle,int32_t _x,int32_t _y)1129 void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y)
1130 {
1131 Msg* msg = new Msg;
1132 msg->m_x = _x;
1133 msg->m_y = _y;
1134 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_SET_POS, _handle.idx, (LPARAM)msg);
1135 }
1136
setWindowSize(WindowHandle _handle,uint32_t _width,uint32_t _height)1137 void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
1138 {
1139 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_SET_SIZE, _handle.idx, (_height<<16) | (_width&0xffff) );
1140 }
1141
setWindowTitle(WindowHandle _handle,const char * _title)1142 void setWindowTitle(WindowHandle _handle, const char* _title)
1143 {
1144 Msg* msg = new Msg;
1145 msg->m_title = _title;
1146 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_SET_TITLE, _handle.idx, (LPARAM)msg);
1147 }
1148
setWindowFlags(WindowHandle _handle,uint32_t _flags,bool _enabled)1149 void setWindowFlags(WindowHandle _handle, uint32_t _flags, bool _enabled)
1150 {
1151 Msg* msg = new Msg;
1152 msg->m_flags = _flags;
1153 msg->m_flagsEnabled = _enabled;
1154 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_SET_FLAGS, _handle.idx, (LPARAM)msg);
1155 }
1156
toggleFullscreen(WindowHandle _handle)1157 void toggleFullscreen(WindowHandle _handle)
1158 {
1159 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_TOGGLE_FRAME, _handle.idx, 0);
1160 }
1161
setMouseLock(WindowHandle _handle,bool _lock)1162 void setMouseLock(WindowHandle _handle, bool _lock)
1163 {
1164 PostMessage(s_ctx.m_hwnd[0], WM_USER_WINDOW_MOUSE_LOCK, _handle.idx, _lock);
1165 }
1166
threadFunc(bx::Thread *,void * _userData)1167 int32_t MainThreadEntry::threadFunc(bx::Thread* /*_thread*/, void* _userData)
1168 {
1169 MainThreadEntry* self = (MainThreadEntry*)_userData;
1170 int32_t result = main(self->m_argc, self->m_argv);
1171 PostMessage(s_ctx.m_hwnd[0], WM_QUIT, 0, 0);
1172 return result;
1173 }
1174
1175 } // namespace entry
1176
main(int _argc,const char * const * _argv)1177 int main(int _argc, const char* const* _argv)
1178 {
1179 using namespace entry;
1180 return s_ctx.run(_argc, _argv);
1181 }
1182
1183 #endif // BX_PLATFORM_WINDOWS
1184