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_GLFW
9 
10 #define GLFW_INCLUDE_NONE
11 #include <GLFW/glfw3.h>
12 
13 #if GLFW_VERSION_MINOR < 2
14 #	error "GLFW 3.2 or later is required"
15 #endif // GLFW_VERSION_MINOR < 2
16 
17 #if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
18 #	if ENTRY_CONFIG_USE_WAYLAND
19 #		include <wayland-egl.h>
20 #		define GLFW_EXPOSE_NATIVE_WAYLAND
21 #	else
22 #		define GLFW_EXPOSE_NATIVE_X11
23 #		define GLFW_EXPOSE_NATIVE_GLX
24 #	endif
25 #elif BX_PLATFORM_OSX
26 #	define GLFW_EXPOSE_NATIVE_COCOA
27 #	define GLFW_EXPOSE_NATIVE_NSGL
28 #elif BX_PLATFORM_WINDOWS
29 #	define GLFW_EXPOSE_NATIVE_WIN32
30 #	define GLFW_EXPOSE_NATIVE_WGL
31 #endif //
32 #include <GLFW/glfw3native.h>
33 
34 #include <bgfx/platform.h>
35 
36 #include <bx/handlealloc.h>
37 #include <bx/thread.h>
38 #include <bx/mutex.h>
39 #include <tinystl/string.h>
40 
41 #include "dbg.h"
42 
43 namespace entry
44 {
glfwNativeWindowHandle(GLFWwindow * _window)45 	static void* glfwNativeWindowHandle(GLFWwindow* _window)
46 	{
47 #	if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
48 # 		if ENTRY_CONFIG_USE_WAYLAND
49 		wl_egl_window *win_impl = (wl_egl_window*)glfwGetWindowUserPointer(_window);
50 		if(!win_impl)
51 		{
52 			int width, height;
53 			glfwGetWindowSize(_window, &width, &height);
54 			struct wl_surface* surface = (struct wl_surface*)glfwGetWaylandWindow(_window);
55 			if(!surface)
56 				return nullptr;
57 			win_impl = wl_egl_window_create(surface, width, height);
58 			glfwSetWindowUserPointer(_window, (void*)(uintptr_t)win_impl);
59 		}
60 		return (void*)(uintptr_t)win_impl;
61 #		else
62 		return (void*)(uintptr_t)glfwGetX11Window(_window);
63 #		endif
64 #	elif BX_PLATFORM_OSX
65 		return glfwGetCocoaWindow(_window);
66 #	elif BX_PLATFORM_WINDOWS
67 		return glfwGetWin32Window(_window);
68 #	endif // BX_PLATFORM_
69 	}
70 
glfwDestroyWindowImpl(GLFWwindow * _window)71 	static void glfwDestroyWindowImpl(GLFWwindow *_window)
72 	{
73 		if(!_window)
74 			return;
75 #	if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
76 #		if ENTRY_CONFIG_USE_WAYLAND
77 		wl_egl_window *win_impl = (wl_egl_window*)glfwGetWindowUserPointer(_window);
78 		if(win_impl)
79 		{
80 			glfwSetWindowUserPointer(_window, nullptr);
81 			wl_egl_window_destroy(win_impl);
82 		}
83 #		endif
84 #	endif
85 		glfwDestroyWindow(_window);
86 	}
87 
glfwSetWindow(GLFWwindow * _window)88 	static void glfwSetWindow(GLFWwindow* _window)
89 	{
90 		bgfx::PlatformData pd;
91 #	if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
92 # 		if ENTRY_CONFIG_USE_WAYLAND
93 		pd.ndt      = glfwGetWaylandDisplay();
94 #		else
95 		pd.ndt      = glfwGetX11Display();
96 		#endif
97 #	elif BX_PLATFORM_OSX
98 		pd.ndt      = NULL;
99 #	elif BX_PLATFORM_WINDOWS
100 		pd.ndt      = NULL;
101 #	endif // BX_PLATFORM_WINDOWS
102 		pd.nwh          = glfwNativeWindowHandle(_window);
103 		pd.context      = NULL;
104 		pd.backBuffer   = NULL;
105 		pd.backBufferDS = NULL;
106 		bgfx::setPlatformData(pd);
107 	}
108 
translateKeyModifiers(int _glfw)109 	static uint8_t translateKeyModifiers(int _glfw)
110 	{
111 		uint8_t modifiers = 0;
112 
113 		if (_glfw & GLFW_MOD_ALT)
114 		{
115 			modifiers |= Modifier::LeftAlt;
116 		}
117 
118 		if (_glfw & GLFW_MOD_CONTROL)
119 		{
120 			modifiers |= Modifier::LeftCtrl;
121 		}
122 
123 		if (_glfw & GLFW_MOD_SUPER)
124 		{
125 			modifiers |= Modifier::LeftMeta;
126 		}
127 
128 		if (_glfw & GLFW_MOD_SHIFT)
129 		{
130 			modifiers |= Modifier::LeftShift;
131 		}
132 
133 		return modifiers;
134 	}
135 
136 	static Key::Enum s_translateKey[GLFW_KEY_LAST + 1];
137 
translateKey(int _key)138 	static Key::Enum translateKey(int _key)
139 	{
140 		return s_translateKey[_key];
141 	}
142 
translateMouseButton(int _button)143 	static MouseButton::Enum translateMouseButton(int _button)
144 	{
145 		if (_button == GLFW_MOUSE_BUTTON_LEFT)
146 		{
147 			return MouseButton::Left;
148 		}
149 		else if (_button == GLFW_MOUSE_BUTTON_RIGHT)
150 		{
151 			return MouseButton::Right;
152 		}
153 
154 		return MouseButton::Middle;
155 	}
156 
translateGamepadAxis(int _axis)157 	static GamepadAxis::Enum translateGamepadAxis(int _axis)
158 	{
159 		// HACK: Map XInput 360 controller until GLFW gamepad API
160 
161 		static GamepadAxis::Enum axes[] =
162 		{
163 			GamepadAxis::LeftX,
164 			GamepadAxis::LeftY,
165 			GamepadAxis::RightX,
166 			GamepadAxis::RightY,
167 			GamepadAxis::LeftZ,
168 			GamepadAxis::RightZ,
169 		};
170 		return axes[_axis];
171 	}
172 
translateGamepadButton(int _button)173 	static Key::Enum translateGamepadButton(int _button)
174 	{
175 		// HACK: Map XInput 360 controller until GLFW gamepad API
176 
177 		static Key::Enum buttons[] =
178 		{
179 			Key::GamepadA,
180 			Key::GamepadB,
181 			Key::GamepadX,
182 			Key::GamepadY,
183 			Key::GamepadShoulderL,
184 			Key::GamepadShoulderR,
185 			Key::GamepadBack,
186 			Key::GamepadStart,
187 			Key::GamepadThumbL,
188 			Key::GamepadThumbR,
189 			Key::GamepadUp,
190 			Key::GamepadRight,
191 			Key::GamepadDown,
192 			Key::GamepadLeft,
193 			Key::GamepadGuide,
194 		};
195 		return buttons[_button];
196 	}
197 
198 	struct GamepadGLFW
199 	{
GamepadGLFWentry::GamepadGLFW200 		GamepadGLFW()
201 			: m_connected(false)
202 		{
203 			bx::memSet(m_axes, 0, sizeof(m_axes));
204 			bx::memSet(m_buttons, 0, sizeof(m_buttons));
205 		}
206 
updateentry::GamepadGLFW207 		void update(EventQueue& _eventQueue)
208 		{
209 			int numButtons, numAxes;
210 			const unsigned char* buttons = glfwGetJoystickButtons(m_handle.idx, &numButtons);
211 			const float* axes = glfwGetJoystickAxes(m_handle.idx, &numAxes);
212 
213 			if (NULL == buttons || NULL == axes)
214 			{
215 				return;
216 			}
217 
218 			if (numAxes > GamepadAxis::Count)
219 			{
220 				numAxes = GamepadAxis::Count;
221 			}
222 
223 			if (numButtons > Key::Count - Key::GamepadA)
224 			{
225 				numButtons = Key::Count - Key::GamepadA;
226 			}
227 
228 			WindowHandle defaultWindow = { 0 };
229 
230 			for (int ii = 0; ii < numAxes; ++ii)
231 			{
232 				GamepadAxis::Enum axis = translateGamepadAxis(ii);
233 				int32_t value = (int32_t) (axes[ii] * 32768.f);
234 				if (GamepadAxis::LeftY == axis || GamepadAxis::RightY == axis)
235 				{
236 					value = -value;
237 				}
238 
239 				if (m_axes[ii] != value)
240 				{
241 					m_axes[ii] = value;
242 					_eventQueue.postAxisEvent(defaultWindow
243 						, m_handle
244 						, axis
245 						, value);
246 				}
247 			}
248 
249 			for (int ii = 0; ii < numButtons; ++ii)
250 			{
251 				Key::Enum key = translateGamepadButton(ii);
252 				if (m_buttons[ii] != buttons[ii])
253 				{
254 					m_buttons[ii] = buttons[ii];
255 					_eventQueue.postKeyEvent(defaultWindow
256 						, key
257 						, 0
258 						, buttons[ii] != 0);
259 				}
260 			}
261 		}
262 
263 		bool m_connected;
264 		GamepadHandle m_handle;
265 		int32_t m_axes[GamepadAxis::Count];
266 		uint8_t m_buttons[Key::Count - Key::GamepadA];
267 	};
268 
269 	struct MainThreadEntry
270 	{
271 		int m_argc;
272 		const char* const* m_argv;
273 
274 		static int32_t threadFunc(bx::Thread* _thread, void* _userData);
275 	};
276 
277 	enum MsgType
278 	{
279 		GLFW_WINDOW_CREATE,
280 		GLFW_WINDOW_DESTROY,
281 		GLFW_WINDOW_SET_TITLE,
282 		GLFW_WINDOW_SET_POS,
283 		GLFW_WINDOW_SET_SIZE,
284 		GLFW_WINDOW_TOGGLE_FRAME,
285 		GLFW_WINDOW_TOGGLE_FULL_SCREEN,
286 		GLFW_WINDOW_MOUSE_LOCK,
287 	};
288 
289 	struct Msg
290 	{
Msgentry::Msg291 		Msg(MsgType _type)
292 			: m_type(_type)
293 			, m_x(0)
294 			, m_y(0)
295 			, m_width(0)
296 			, m_height(0)
297 			, m_value(false)
298 		{
299 		}
300 
301 		MsgType  m_type;
302 		int32_t  m_x;
303 		int32_t  m_y;
304 		uint32_t m_width;
305 		uint32_t m_height;
306 		uint32_t m_flags;
307 		bool	 m_value;
308 		tinystl::string m_title;
309 		WindowHandle m_handle;
310 	};
311 
errorCb(int _error,const char * _description)312 	static void errorCb(int _error, const char* _description)
313 	{
314 		DBG("GLFW error %d: %s", _error, _description);
315 	}
316 
317 	static void joystickCb(int _jid, int _action);
318 
319 	// Based on cutef8 by Jeff Bezanson (Public Domain)
encodeUTF8(uint8_t _chars[4],uint32_t _scancode)320 	static uint8_t encodeUTF8(uint8_t _chars[4], uint32_t _scancode)
321 	{
322 		uint8_t length = 0;
323 
324 		if (_scancode < 0x80)
325 		{
326 			_chars[length++] = (char) _scancode;
327 		}
328 		else if (_scancode < 0x800)
329 		{
330 			_chars[length++] =  (_scancode >>  6)         | 0xc0;
331 			_chars[length++] =  (_scancode        & 0x3f) | 0x80;
332 		}
333 		else if (_scancode < 0x10000)
334 		{
335 			_chars[length++] =  (_scancode >> 12)         | 0xe0;
336 			_chars[length++] = ((_scancode >>  6) & 0x3f) | 0x80;
337 			_chars[length++] =  (_scancode        & 0x3f) | 0x80;
338 		}
339 		else if (_scancode < 0x110000)
340 		{
341 			_chars[length++] =  (_scancode >> 18)         | 0xf0;
342 			_chars[length++] = ((_scancode >> 12) & 0x3f) | 0x80;
343 			_chars[length++] = ((_scancode >>  6) & 0x3f) | 0x80;
344 			_chars[length++] =  (_scancode        & 0x3f) | 0x80;
345 		}
346 
347 		return length;
348 	}
349 
350 	struct Context
351 	{
Contextentry::Context352 		Context()
353 			: m_msgs(getAllocator() )
354 			, m_scrollPos(0.0f)
355 		{
356 			bx::memSet(s_translateKey, 0, sizeof(s_translateKey));
357 			s_translateKey[GLFW_KEY_ESCAPE]       = Key::Esc;
358 			s_translateKey[GLFW_KEY_ENTER]        = Key::Return;
359 			s_translateKey[GLFW_KEY_TAB]          = Key::Tab;
360 			s_translateKey[GLFW_KEY_BACKSPACE]    = Key::Backspace;
361 			s_translateKey[GLFW_KEY_SPACE]        = Key::Space;
362 			s_translateKey[GLFW_KEY_UP]           = Key::Up;
363 			s_translateKey[GLFW_KEY_DOWN]         = Key::Down;
364 			s_translateKey[GLFW_KEY_LEFT]         = Key::Left;
365 			s_translateKey[GLFW_KEY_RIGHT]        = Key::Right;
366 			s_translateKey[GLFW_KEY_PAGE_UP]      = Key::PageUp;
367 			s_translateKey[GLFW_KEY_PAGE_DOWN]    = Key::PageDown;
368 			s_translateKey[GLFW_KEY_HOME]         = Key::Home;
369 			s_translateKey[GLFW_KEY_END]          = Key::End;
370 			s_translateKey[GLFW_KEY_PRINT_SCREEN] = Key::Print;
371 			s_translateKey[GLFW_KEY_KP_ADD]       = Key::Plus;
372 			s_translateKey[GLFW_KEY_EQUAL]        = Key::Plus;
373 			s_translateKey[GLFW_KEY_KP_SUBTRACT]  = Key::Minus;
374 			s_translateKey[GLFW_KEY_MINUS]        = Key::Minus;
375 			s_translateKey[GLFW_KEY_COMMA]        = Key::Comma;
376 			s_translateKey[GLFW_KEY_PERIOD]       = Key::Period;
377 			s_translateKey[GLFW_KEY_SLASH]        = Key::Slash;
378 			s_translateKey[GLFW_KEY_F1]           = Key::F1;
379 			s_translateKey[GLFW_KEY_F2]           = Key::F2;
380 			s_translateKey[GLFW_KEY_F3]           = Key::F3;
381 			s_translateKey[GLFW_KEY_F4]           = Key::F4;
382 			s_translateKey[GLFW_KEY_F5]           = Key::F5;
383 			s_translateKey[GLFW_KEY_F6]           = Key::F6;
384 			s_translateKey[GLFW_KEY_F7]           = Key::F7;
385 			s_translateKey[GLFW_KEY_F8]           = Key::F8;
386 			s_translateKey[GLFW_KEY_F9]           = Key::F9;
387 			s_translateKey[GLFW_KEY_F10]          = Key::F10;
388 			s_translateKey[GLFW_KEY_F11]          = Key::F11;
389 			s_translateKey[GLFW_KEY_F12]          = Key::F12;
390 			s_translateKey[GLFW_KEY_KP_0]         = Key::NumPad0;
391 			s_translateKey[GLFW_KEY_KP_1]         = Key::NumPad1;
392 			s_translateKey[GLFW_KEY_KP_2]         = Key::NumPad2;
393 			s_translateKey[GLFW_KEY_KP_3]         = Key::NumPad3;
394 			s_translateKey[GLFW_KEY_KP_4]         = Key::NumPad4;
395 			s_translateKey[GLFW_KEY_KP_5]         = Key::NumPad5;
396 			s_translateKey[GLFW_KEY_KP_6]         = Key::NumPad6;
397 			s_translateKey[GLFW_KEY_KP_7]         = Key::NumPad7;
398 			s_translateKey[GLFW_KEY_KP_8]         = Key::NumPad8;
399 			s_translateKey[GLFW_KEY_KP_9]         = Key::NumPad9;
400 			s_translateKey[GLFW_KEY_0]            = Key::Key0;
401 			s_translateKey[GLFW_KEY_1]            = Key::Key1;
402 			s_translateKey[GLFW_KEY_2]            = Key::Key2;
403 			s_translateKey[GLFW_KEY_3]            = Key::Key3;
404 			s_translateKey[GLFW_KEY_4]            = Key::Key4;
405 			s_translateKey[GLFW_KEY_5]            = Key::Key5;
406 			s_translateKey[GLFW_KEY_6]            = Key::Key6;
407 			s_translateKey[GLFW_KEY_7]            = Key::Key7;
408 			s_translateKey[GLFW_KEY_8]            = Key::Key8;
409 			s_translateKey[GLFW_KEY_9]            = Key::Key9;
410 			s_translateKey[GLFW_KEY_A]            = Key::KeyA;
411 			s_translateKey[GLFW_KEY_B]            = Key::KeyB;
412 			s_translateKey[GLFW_KEY_C]            = Key::KeyC;
413 			s_translateKey[GLFW_KEY_D]            = Key::KeyD;
414 			s_translateKey[GLFW_KEY_E]            = Key::KeyE;
415 			s_translateKey[GLFW_KEY_F]            = Key::KeyF;
416 			s_translateKey[GLFW_KEY_G]            = Key::KeyG;
417 			s_translateKey[GLFW_KEY_H]            = Key::KeyH;
418 			s_translateKey[GLFW_KEY_I]            = Key::KeyI;
419 			s_translateKey[GLFW_KEY_J]            = Key::KeyJ;
420 			s_translateKey[GLFW_KEY_K]            = Key::KeyK;
421 			s_translateKey[GLFW_KEY_L]            = Key::KeyL;
422 			s_translateKey[GLFW_KEY_M]            = Key::KeyM;
423 			s_translateKey[GLFW_KEY_N]            = Key::KeyN;
424 			s_translateKey[GLFW_KEY_O]            = Key::KeyO;
425 			s_translateKey[GLFW_KEY_P]            = Key::KeyP;
426 			s_translateKey[GLFW_KEY_Q]            = Key::KeyQ;
427 			s_translateKey[GLFW_KEY_R]            = Key::KeyR;
428 			s_translateKey[GLFW_KEY_S]            = Key::KeyS;
429 			s_translateKey[GLFW_KEY_T]            = Key::KeyT;
430 			s_translateKey[GLFW_KEY_U]            = Key::KeyU;
431 			s_translateKey[GLFW_KEY_V]            = Key::KeyV;
432 			s_translateKey[GLFW_KEY_W]            = Key::KeyW;
433 			s_translateKey[GLFW_KEY_X]            = Key::KeyX;
434 			s_translateKey[GLFW_KEY_Y]            = Key::KeyY;
435 			s_translateKey[GLFW_KEY_Z]            = Key::KeyZ;
436 		}
437 
runentry::Context438 		int run(int _argc, const char* const* _argv)
439 		{
440 			m_mte.m_argc = _argc;
441 			m_mte.m_argv = _argv;
442 
443 			glfwSetErrorCallback(errorCb);
444 
445 			if (!glfwInit() )
446 			{
447 				DBG("glfwInit failed!");
448 				return bx::kExitFailure;
449 			}
450 
451 			glfwSetJoystickCallback(joystickCb);
452 
453 			glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
454 
455 			WindowHandle handle = { m_windowAlloc.alloc() };
456 			m_windows[0] = glfwCreateWindow(ENTRY_DEFAULT_WIDTH
457 				, ENTRY_DEFAULT_HEIGHT
458 				, "bgfx"
459 				, NULL
460 				, NULL
461 				);
462 
463 			if (!m_windows[0])
464 			{
465 				DBG("glfwCreateWindow failed!");
466 				glfwTerminate();
467 				return bx::kExitFailure;
468 			}
469 
470 			glfwSetKeyCallback(m_windows[0], keyCb);
471 			glfwSetCharCallback(m_windows[0], charCb);
472 			glfwSetScrollCallback(m_windows[0], scrollCb);
473 			glfwSetCursorPosCallback(m_windows[0], cursorPosCb);
474 			glfwSetMouseButtonCallback(m_windows[0], mouseButtonCb);
475 			glfwSetWindowSizeCallback(m_windows[0], windowSizeCb);
476 			glfwSetDropCallback(m_windows[0], dropFileCb);
477 
478 			glfwSetWindow(m_windows[0]);
479 			m_eventQueue.postSizeEvent(handle, ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT);
480 
481 			for (uint32_t ii = 0; ii < ENTRY_CONFIG_MAX_GAMEPADS; ++ii)
482 			{
483 				m_gamepad[ii].m_handle.idx = ii;
484 				if (glfwJoystickPresent(ii))
485 				{
486 					m_gamepad[ii].m_connected = true;
487 					m_eventQueue.postGamepadEvent(handle
488 						, m_gamepad[ii].m_handle
489 						, true);
490 				}
491 			}
492 
493 			m_thread.init(MainThreadEntry::threadFunc, &m_mte);
494 
495 			while (NULL != m_windows[0]
496 			&&     !glfwWindowShouldClose(m_windows[0]))
497 			{
498 				glfwWaitEvents();
499 
500 				for (uint32_t ii = 0; ii < ENTRY_CONFIG_MAX_GAMEPADS; ++ii)
501 				{
502 					if (m_gamepad[ii].m_connected)
503 					{
504 						m_gamepad[ii].update(m_eventQueue);
505 					}
506 				}
507 
508 				while (Msg* msg = m_msgs.pop())
509 				{
510 					switch (msg->m_type)
511 					{
512 					case GLFW_WINDOW_CREATE:
513 						{
514 							GLFWwindow* window = glfwCreateWindow(msg->m_width
515 								, msg->m_height
516 								, msg->m_title.c_str()
517 								, NULL
518 								, NULL);
519 							if (!window)
520 							{
521 								break;
522 							}
523 
524 							glfwSetWindowPos(window, msg->m_x, msg->m_y);
525 							if (msg->m_flags & ENTRY_WINDOW_FLAG_ASPECT_RATIO)
526 							{
527 								glfwSetWindowAspectRatio(window, msg->m_width, msg->m_height);
528 							}
529 
530 							glfwSetKeyCallback(window, keyCb);
531 							glfwSetCharCallback(window, charCb);
532 							glfwSetScrollCallback(window, scrollCb);
533 							glfwSetCursorPosCallback(window, cursorPosCb);
534 							glfwSetMouseButtonCallback(window, mouseButtonCb);
535 							glfwSetWindowSizeCallback(window, windowSizeCb);
536 							glfwSetDropCallback(window, dropFileCb);
537 
538 							m_windows[msg->m_handle.idx] = window;
539 							m_eventQueue.postSizeEvent(msg->m_handle, msg->m_width, msg->m_height);
540 							m_eventQueue.postWindowEvent(msg->m_handle, glfwNativeWindowHandle(window));
541 						}
542 						break;
543 
544 					case GLFW_WINDOW_DESTROY:
545 						{
546 							if (isValid(msg->m_handle) )
547 							{
548 								GLFWwindow* window = m_windows[msg->m_handle.idx];
549 								m_eventQueue.postWindowEvent(msg->m_handle);
550 								glfwDestroyWindowImpl(window);
551 								m_windows[msg->m_handle.idx] = NULL;
552 							}
553 						}
554 						break;
555 
556 					case GLFW_WINDOW_SET_TITLE:
557 						{
558 							GLFWwindow* window = m_windows[msg->m_handle.idx];
559 							glfwSetWindowTitle(window, msg->m_title.c_str());
560 						}
561 						break;
562 
563 					case GLFW_WINDOW_SET_POS:
564 						{
565 							GLFWwindow* window = m_windows[msg->m_handle.idx];
566 							glfwSetWindowPos(window, msg->m_x, msg->m_y);
567 						}
568 						break;
569 
570 					case GLFW_WINDOW_SET_SIZE:
571 						{
572 							GLFWwindow* window = m_windows[msg->m_handle.idx];
573 							glfwSetWindowSize(window, msg->m_width, msg->m_height);
574 						}
575 						break;
576 
577 					case GLFW_WINDOW_TOGGLE_FRAME:
578 						{
579 							// Wait for glfwSetWindowDecorated to exist
580 						}
581 						break;
582 
583 					case GLFW_WINDOW_TOGGLE_FULL_SCREEN:
584 						{
585 							GLFWwindow* window = m_windows[msg->m_handle.idx];
586 							if (glfwGetWindowMonitor(window) )
587 							{
588 								glfwSetWindowMonitor(window
589 									, NULL
590 									, m_oldX
591 									, m_oldY
592 									, m_oldWidth
593 									, m_oldHeight
594 									, 0
595 									);
596 							}
597 							else
598 							{
599 								GLFWmonitor* monitor = glfwGetPrimaryMonitor();
600 								if (NULL != monitor)
601 								{
602 									glfwGetWindowPos(window, &m_oldX, &m_oldY);
603 									glfwGetWindowSize(window, &m_oldWidth, &m_oldHeight);
604 
605 									const GLFWvidmode* mode = glfwGetVideoMode(monitor);
606 									glfwSetWindowMonitor(window
607 										, monitor
608 										, 0
609 										, 0
610 										, mode->width
611 										, mode->height
612 										, mode->refreshRate
613 										);
614 								}
615 
616 							}
617 						}
618 						break;
619 
620 					case GLFW_WINDOW_MOUSE_LOCK:
621 						{
622 							GLFWwindow* window = m_windows[msg->m_handle.idx];
623 							if (msg->m_value)
624 							{
625 								glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
626 							}
627 							else
628 							{
629 								glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
630 							}
631 						}
632 						break;
633 					}
634 
635 					delete msg;
636 				}
637 			}
638 
639 			m_eventQueue.postExitEvent();
640 			m_thread.shutdown();
641 
642 			glfwDestroyWindowImpl(m_windows[0]);
643 			glfwTerminate();
644 
645 			return m_thread.getExitCode();
646 		}
647 
findHandleentry::Context648 		WindowHandle findHandle(GLFWwindow* _window)
649 		{
650 			bx::MutexScope scope(m_lock);
651 			for (uint32_t ii = 0, num = m_windowAlloc.getNumHandles(); ii < num; ++ii)
652 			{
653 				uint16_t idx = m_windowAlloc.getHandleAt(ii);
654 				if (_window == m_windows[idx])
655 				{
656 					WindowHandle handle = { idx };
657 					return handle;
658 				}
659 			}
660 
661 			WindowHandle invalid = { UINT16_MAX };
662 			return invalid;
663 		}
664 
665 		static void keyCb(GLFWwindow* _window, int32_t _key, int32_t _scancode, int32_t _action, int32_t _mods);
666 		static void charCb(GLFWwindow* _window, uint32_t _scancode);
667 		static void scrollCb(GLFWwindow* _window, double _dx, double _dy);
668 		static void cursorPosCb(GLFWwindow* _window, double _mx, double _my);
669 		static void mouseButtonCb(GLFWwindow* _window, int32_t _button, int32_t _action, int32_t _mods);
670 		static void windowSizeCb(GLFWwindow* _window, int32_t _width, int32_t _height);
671 		static void dropFileCb(GLFWwindow* _window, int32_t _count, const char** _filePaths);
672 
673 		MainThreadEntry m_mte;
674 		bx::Thread m_thread;
675 
676 		EventQueue m_eventQueue;
677 		bx::Mutex m_lock;
678 
679 		GLFWwindow* m_windows[ENTRY_CONFIG_MAX_WINDOWS];
680 		bx::HandleAllocT<ENTRY_CONFIG_MAX_WINDOWS> m_windowAlloc;
681 
682 		GamepadGLFW m_gamepad[ENTRY_CONFIG_MAX_GAMEPADS];
683 
684 		bx::SpScUnboundedQueueT<Msg> m_msgs;
685 
686 		int32_t m_oldX;
687 		int32_t m_oldY;
688 		int32_t m_oldWidth;
689 		int32_t m_oldHeight;
690 
691 		double m_scrollPos;
692 	};
693 
694 	Context s_ctx;
695 
keyCb(GLFWwindow * _window,int32_t _key,int32_t _scancode,int32_t _action,int32_t _mods)696 	void Context::keyCb(GLFWwindow* _window, int32_t _key, int32_t _scancode, int32_t _action, int32_t _mods)
697 	{
698 		BX_UNUSED(_scancode);
699 		if (_key == GLFW_KEY_UNKNOWN)
700 		{
701 			return;
702 		}
703 		WindowHandle handle = s_ctx.findHandle(_window);
704 		int mods = translateKeyModifiers(_mods);
705 		Key::Enum key = translateKey(_key);
706 		bool down = (_action == GLFW_PRESS || _action == GLFW_REPEAT);
707 		s_ctx.m_eventQueue.postKeyEvent(handle, key, mods, down);
708 	}
709 
charCb(GLFWwindow * _window,uint32_t _scancode)710 	void Context::charCb(GLFWwindow* _window, uint32_t _scancode)
711 	{
712 		WindowHandle handle = s_ctx.findHandle(_window);
713 		uint8_t chars[4];
714 		uint8_t length = encodeUTF8(chars, _scancode);
715 		if (!length)
716 		{
717 			return;
718 		}
719 
720 		s_ctx.m_eventQueue.postCharEvent(handle, length, chars);
721 	}
722 
scrollCb(GLFWwindow * _window,double _dx,double _dy)723 	void Context::scrollCb(GLFWwindow* _window, double _dx, double _dy)
724 	{
725 		BX_UNUSED(_dx);
726 		WindowHandle handle = s_ctx.findHandle(_window);
727 		double mx, my;
728 		glfwGetCursorPos(_window, &mx, &my);
729 		s_ctx.m_scrollPos += _dy;
730 		s_ctx.m_eventQueue.postMouseEvent(handle
731 			, (int32_t) mx
732 			, (int32_t) my
733 			, (int32_t) s_ctx.m_scrollPos
734 			);
735 	}
736 
cursorPosCb(GLFWwindow * _window,double _mx,double _my)737 	void Context::cursorPosCb(GLFWwindow* _window, double _mx, double _my)
738 	{
739 		WindowHandle handle = s_ctx.findHandle(_window);
740 		s_ctx.m_eventQueue.postMouseEvent(handle
741 			, (int32_t) _mx
742 			, (int32_t) _my
743 			, (int32_t) s_ctx.m_scrollPos
744 			);
745 	}
746 
mouseButtonCb(GLFWwindow * _window,int32_t _button,int32_t _action,int32_t _mods)747 	void Context::mouseButtonCb(GLFWwindow* _window, int32_t _button, int32_t _action, int32_t _mods)
748 	{
749 		BX_UNUSED(_mods);
750 		WindowHandle handle = s_ctx.findHandle(_window);
751 		bool down = _action == GLFW_PRESS;
752 		double mx, my;
753 		glfwGetCursorPos(_window, &mx, &my);
754 		s_ctx.m_eventQueue.postMouseEvent(handle
755 			, (int32_t) mx
756 			, (int32_t) my
757 			, (int32_t) s_ctx.m_scrollPos
758 			, translateMouseButton(_button)
759 			, down
760 			);
761 	}
762 
windowSizeCb(GLFWwindow * _window,int32_t _width,int32_t _height)763 	void Context::windowSizeCb(GLFWwindow* _window, int32_t _width, int32_t _height)
764 	{
765 		WindowHandle handle = s_ctx.findHandle(_window);
766 		s_ctx.m_eventQueue.postSizeEvent(handle, _width, _height);
767 	}
768 
dropFileCb(GLFWwindow * _window,int32_t _count,const char ** _filePaths)769 	void Context::dropFileCb(GLFWwindow* _window, int32_t _count, const char** _filePaths)
770 	{
771 		WindowHandle handle = s_ctx.findHandle(_window);
772 		for (int32_t ii = 0; ii < _count; ++ii)
773 		{
774 			s_ctx.m_eventQueue.postDropFileEvent(handle, _filePaths[ii]);
775 		}
776 	}
777 
joystickCb(int _jid,int _action)778 	static void joystickCb(int _jid, int _action)
779 	{
780 		if (_jid >= ENTRY_CONFIG_MAX_GAMEPADS)
781 		{
782 			return;
783 		}
784 
785 		WindowHandle defaultWindow = { 0 };
786 		GamepadHandle handle = { (uint16_t) _jid };
787 
788 		if (_action == GLFW_CONNECTED)
789 		{
790 			s_ctx.m_gamepad[_jid].m_connected = true;
791 			s_ctx.m_eventQueue.postGamepadEvent(defaultWindow, handle, true);
792 		}
793 		else if (_action == GLFW_DISCONNECTED)
794 		{
795 			s_ctx.m_gamepad[_jid].m_connected = false;
796 			s_ctx.m_eventQueue.postGamepadEvent(defaultWindow, handle, false);
797 		}
798 	}
799 
poll()800 	const Event* poll()
801 	{
802 		glfwPostEmptyEvent();
803 		return s_ctx.m_eventQueue.poll();
804 	}
805 
poll(WindowHandle _handle)806 	const Event* poll(WindowHandle _handle)
807 	{
808 		glfwPostEmptyEvent();
809 		return s_ctx.m_eventQueue.poll(_handle);
810 	}
811 
release(const Event * _event)812 	void release(const Event* _event)
813 	{
814 		s_ctx.m_eventQueue.release(_event);
815 	}
816 
createWindow(int32_t _x,int32_t _y,uint32_t _width,uint32_t _height,uint32_t _flags,const char * _title)817 	WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title)
818 	{
819 		Msg* msg = new Msg(GLFW_WINDOW_CREATE);
820 		msg->m_x = _x;
821 		msg->m_y = _y;
822 		msg->m_width = _width;
823 		msg->m_height = _height;
824 		msg->m_flags = _flags;
825 		msg->m_title = _title;
826 		msg->m_handle.idx = s_ctx.m_windowAlloc.alloc();
827 		s_ctx.m_msgs.push(msg);
828 		glfwPostEmptyEvent();
829 		return msg->m_handle;
830 	}
831 
destroyWindow(WindowHandle _handle)832 	void destroyWindow(WindowHandle _handle)
833 	{
834 		Msg* msg = new Msg(GLFW_WINDOW_DESTROY);
835 		msg->m_handle = _handle;
836 		s_ctx.m_msgs.push(msg);
837 		glfwPostEmptyEvent();
838 	}
839 
setWindowPos(WindowHandle _handle,int32_t _x,int32_t _y)840 	void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y)
841 	{
842 		Msg* msg = new Msg(GLFW_WINDOW_SET_POS);
843 		msg->m_x = _x;
844 		msg->m_y = _y;
845 		msg->m_handle = _handle;
846 		s_ctx.m_msgs.push(msg);
847 		glfwPostEmptyEvent();
848 	}
849 
setWindowSize(WindowHandle _handle,uint32_t _width,uint32_t _height)850 	void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
851 	{
852 		Msg* msg = new Msg(GLFW_WINDOW_SET_SIZE);
853 		msg->m_width = _width;
854 		msg->m_height = _height;
855 		msg->m_handle = _handle;
856 		s_ctx.m_msgs.push(msg);
857 		glfwPostEmptyEvent();
858 	}
859 
setWindowTitle(WindowHandle _handle,const char * _title)860 	void setWindowTitle(WindowHandle _handle, const char* _title)
861 	{
862 		Msg* msg = new Msg(GLFW_WINDOW_SET_TITLE);
863 		msg->m_title = _title;
864 		msg->m_handle = _handle;
865 		s_ctx.m_msgs.push(msg);
866 		glfwPostEmptyEvent();
867 	}
868 
setWindowFlags(WindowHandle _handle,uint32_t _flags,bool _enabled)869 	void setWindowFlags(WindowHandle _handle, uint32_t _flags, bool _enabled)
870 	{
871 		BX_UNUSED(_handle, _flags, _enabled);
872 	}
873 
toggleFullscreen(WindowHandle _handle)874 	void toggleFullscreen(WindowHandle _handle)
875 	{
876 		Msg* msg = new Msg(GLFW_WINDOW_TOGGLE_FULL_SCREEN);
877 		msg->m_handle = _handle;
878 		s_ctx.m_msgs.push(msg);
879 		glfwPostEmptyEvent();
880 	}
881 
setMouseLock(WindowHandle _handle,bool _lock)882 	void setMouseLock(WindowHandle _handle, bool _lock)
883 	{
884 		Msg* msg = new Msg(GLFW_WINDOW_MOUSE_LOCK);
885 		msg->m_value = _lock;
886 		msg->m_handle = _handle;
887 		s_ctx.m_msgs.push(msg);
888 		glfwPostEmptyEvent();
889 	}
890 
threadFunc(bx::Thread * _thread,void * _userData)891 	int32_t MainThreadEntry::threadFunc(bx::Thread* _thread, void* _userData)
892 	{
893 		BX_UNUSED(_thread);
894 
895 		MainThreadEntry* self = (MainThreadEntry*)_userData;
896 		int32_t result = main(self->m_argc, self->m_argv);
897 
898 		// Destroy main window on exit...
899 		Msg* msg = new Msg(GLFW_WINDOW_DESTROY);
900 		msg->m_handle.idx = 0;
901 		s_ctx.m_msgs.push(msg);
902 		glfwPostEmptyEvent();
903 
904 		return result;
905 	}
906 }
907 
main(int _argc,const char * const * _argv)908 int main(int _argc, const char* const* _argv)
909 {
910 	using namespace entry;
911 	return s_ctx.run(_argc, _argv);
912 }
913 
914 #endif // ENTRY_CONFIG_USE_GLFW
915