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