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