entry_windows.cpp 28 KB

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