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_SDL
9
10 #if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
11 # if ENTRY_CONFIG_USE_WAYLAND
12 # include <wayland-egl.h>
13 # endif
14 #elif BX_PLATFORM_WINDOWS
15 # define SDL_MAIN_HANDLED
16 #endif
17
18 #include <bx/os.h>
19
20 #include <SDL2/SDL.h>
21
22 BX_PRAGMA_DIAGNOSTIC_PUSH()
23 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wextern-c-compat")
24 #include <SDL2/SDL_syswm.h>
25 BX_PRAGMA_DIAGNOSTIC_POP()
26
27 #include <bgfx/platform.h>
28 #if defined(None) // X11 defines this...
29 # undef None
30 #endif // defined(None)
31
32 #include <bx/mutex.h>
33 #include <bx/thread.h>
34 #include <bx/handlealloc.h>
35 #include <bx/readerwriter.h>
36 #include <tinystl/allocator.h>
37 #include <tinystl/string.h>
38
39 namespace entry
40 {
41 ///
sdlNativeWindowHandle(SDL_Window * _window)42 static void* sdlNativeWindowHandle(SDL_Window* _window)
43 {
44 SDL_SysWMinfo wmi;
45 SDL_VERSION(&wmi.version);
46 if (!SDL_GetWindowWMInfo(_window, &wmi) )
47 {
48 return NULL;
49 }
50
51 # if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
52 # if ENTRY_CONFIG_USE_WAYLAND
53 wl_egl_window *win_impl = (wl_egl_window*)SDL_GetWindowData(_window, "wl_egl_window");
54 if(!win_impl)
55 {
56 int width, height;
57 SDL_GetWindowSize(_window, &width, &height);
58 struct wl_surface* surface = wmi.info.wl.surface;
59 if(!surface)
60 return nullptr;
61 win_impl = wl_egl_window_create(surface, width, height);
62 SDL_SetWindowData(_window, "wl_egl_window", win_impl);
63 }
64 return (void*)(uintptr_t)win_impl;
65 # else
66 return (void*)wmi.info.x11.window;
67 # endif
68 # elif BX_PLATFORM_OSX
69 return wmi.info.cocoa.window;
70 # elif BX_PLATFORM_WINDOWS
71 return wmi.info.win.window;
72 # elif BX_PLATFORM_STEAMLINK
73 return wmi.info.vivante.window;
74 # endif // BX_PLATFORM_
75 }
76
sdlSetWindow(SDL_Window * _window)77 inline bool sdlSetWindow(SDL_Window* _window)
78 {
79 SDL_SysWMinfo wmi;
80 SDL_VERSION(&wmi.version);
81 if (!SDL_GetWindowWMInfo(_window, &wmi) )
82 {
83 return false;
84 }
85
86 bgfx::PlatformData pd;
87 # if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
88 # if ENTRY_CONFIG_USE_WAYLAND
89 pd.ndt = wmi.info.wl.display;
90 # else
91 pd.ndt = wmi.info.x11.display;
92 # endif
93 # elif BX_PLATFORM_OSX
94 pd.ndt = NULL;
95 # elif BX_PLATFORM_WINDOWS
96 pd.ndt = NULL;
97 # elif BX_PLATFORM_STEAMLINK
98 pd.ndt = wmi.info.vivante.display;
99 # endif // BX_PLATFORM_
100 pd.nwh = sdlNativeWindowHandle(_window);
101
102 pd.context = NULL;
103 pd.backBuffer = NULL;
104 pd.backBufferDS = NULL;
105 bgfx::setPlatformData(pd);
106
107 return true;
108 }
109
sdlDestroyWindow(SDL_Window * _window)110 static void sdlDestroyWindow(SDL_Window* _window)
111 {
112 if(!_window)
113 return;
114 # if BX_PLATFORM_LINUX || BX_PLATFORM_BSD
115 # if ENTRY_CONFIG_USE_WAYLAND
116 wl_egl_window *win_impl = (wl_egl_window*)SDL_GetWindowData(_window, "wl_egl_window");
117 if(win_impl)
118 {
119 SDL_SetWindowData(_window, "wl_egl_window", nullptr);
120 wl_egl_window_destroy(win_impl);
121 }
122 # endif
123 # endif
124 SDL_DestroyWindow(_window);
125 }
126
translateKeyModifiers(uint16_t _sdl)127 static uint8_t translateKeyModifiers(uint16_t _sdl)
128 {
129 uint8_t modifiers = 0;
130 modifiers |= _sdl & KMOD_LALT ? Modifier::LeftAlt : 0;
131 modifiers |= _sdl & KMOD_RALT ? Modifier::RightAlt : 0;
132 modifiers |= _sdl & KMOD_LCTRL ? Modifier::LeftCtrl : 0;
133 modifiers |= _sdl & KMOD_RCTRL ? Modifier::RightCtrl : 0;
134 modifiers |= _sdl & KMOD_LSHIFT ? Modifier::LeftShift : 0;
135 modifiers |= _sdl & KMOD_RSHIFT ? Modifier::RightShift : 0;
136 modifiers |= _sdl & KMOD_LGUI ? Modifier::LeftMeta : 0;
137 modifiers |= _sdl & KMOD_RGUI ? Modifier::RightMeta : 0;
138 return modifiers;
139 }
140
translateKeyModifierPress(uint16_t _key)141 static uint8_t translateKeyModifierPress(uint16_t _key)
142 {
143 uint8_t modifier;
144 switch (_key)
145 {
146 case SDL_SCANCODE_LALT: { modifier = Modifier::LeftAlt; } break;
147 case SDL_SCANCODE_RALT: { modifier = Modifier::RightAlt; } break;
148 case SDL_SCANCODE_LCTRL: { modifier = Modifier::LeftCtrl; } break;
149 case SDL_SCANCODE_RCTRL: { modifier = Modifier::RightCtrl; } break;
150 case SDL_SCANCODE_LSHIFT: { modifier = Modifier::LeftShift; } break;
151 case SDL_SCANCODE_RSHIFT: { modifier = Modifier::RightShift; } break;
152 case SDL_SCANCODE_LGUI: { modifier = Modifier::LeftMeta; } break;
153 case SDL_SCANCODE_RGUI: { modifier = Modifier::RightMeta; } break;
154 default: { modifier = 0; } break;
155 }
156
157 return modifier;
158 }
159
160 static uint8_t s_translateKey[256];
161
initTranslateKey(uint16_t _sdl,Key::Enum _key)162 static void initTranslateKey(uint16_t _sdl, Key::Enum _key)
163 {
164 BX_CHECK(_sdl < BX_COUNTOF(s_translateKey), "Out of bounds %d.", _sdl);
165 s_translateKey[_sdl&0xff] = (uint8_t)_key;
166 }
167
translateKey(SDL_Scancode _sdl)168 static Key::Enum translateKey(SDL_Scancode _sdl)
169 {
170 return (Key::Enum)s_translateKey[_sdl&0xff];
171 }
172
173 static uint8_t s_translateGamepad[256];
174
initTranslateGamepad(uint8_t _sdl,Key::Enum _button)175 static void initTranslateGamepad(uint8_t _sdl, Key::Enum _button)
176 {
177 s_translateGamepad[_sdl] = _button;
178 }
179
translateGamepad(uint8_t _sdl)180 static Key::Enum translateGamepad(uint8_t _sdl)
181 {
182 return Key::Enum(s_translateGamepad[_sdl]);
183 }
184
185 static uint8_t s_translateGamepadAxis[256];
186
initTranslateGamepadAxis(uint8_t _sdl,GamepadAxis::Enum _axis)187 static void initTranslateGamepadAxis(uint8_t _sdl, GamepadAxis::Enum _axis)
188 {
189 s_translateGamepadAxis[_sdl] = uint8_t(_axis);
190 }
191
translateGamepadAxis(uint8_t _sdl)192 static GamepadAxis::Enum translateGamepadAxis(uint8_t _sdl)
193 {
194 return GamepadAxis::Enum(s_translateGamepadAxis[_sdl]);
195 }
196
197 struct AxisDpadRemap
198 {
199 Key::Enum first;
200 Key::Enum second;
201 };
202
203 static AxisDpadRemap s_axisDpad[] =
204 {
205 { Key::GamepadLeft, Key::GamepadRight },
206 { Key::GamepadUp, Key::GamepadDown },
207 { Key::None, Key::None },
208 { Key::GamepadLeft, Key::GamepadRight },
209 { Key::GamepadUp, Key::GamepadDown },
210 { Key::None, Key::None },
211 };
212
213 struct GamepadSDL
214 {
GamepadSDLentry::GamepadSDL215 GamepadSDL()
216 : m_controller(NULL)
217 , m_jid(INT32_MAX)
218 {
219 bx::memSet(m_value, 0, sizeof(m_value) );
220
221 // Deadzone values from xinput.h
222 m_deadzone[GamepadAxis::LeftX ] =
223 m_deadzone[GamepadAxis::LeftY ] = 7849;
224 m_deadzone[GamepadAxis::RightX] =
225 m_deadzone[GamepadAxis::RightY] = 8689;
226 m_deadzone[GamepadAxis::LeftZ ] =
227 m_deadzone[GamepadAxis::RightZ] = 30;
228 }
229
createentry::GamepadSDL230 void create(const SDL_JoyDeviceEvent& _jev)
231 {
232 m_joystick = SDL_JoystickOpen(_jev.which);
233 SDL_Joystick* joystick = m_joystick;
234 m_jid = SDL_JoystickInstanceID(joystick);
235 }
236
createentry::GamepadSDL237 void create(const SDL_ControllerDeviceEvent& _cev)
238 {
239 m_controller = SDL_GameControllerOpen(_cev.which);
240 SDL_Joystick* joystick = SDL_GameControllerGetJoystick(m_controller);
241 m_jid = SDL_JoystickInstanceID(joystick);
242 }
243
updateentry::GamepadSDL244 void update(EventQueue& _eventQueue, WindowHandle _handle, GamepadHandle _gamepad, GamepadAxis::Enum _axis, int32_t _value)
245 {
246 if (filter(_axis, &_value) )
247 {
248 _eventQueue.postAxisEvent(_handle, _gamepad, _axis, _value);
249
250 if (Key::None != s_axisDpad[_axis].first)
251 {
252 if (_value == 0)
253 {
254 _eventQueue.postKeyEvent(_handle, s_axisDpad[_axis].first, 0, false);
255 _eventQueue.postKeyEvent(_handle, s_axisDpad[_axis].second, 0, false);
256 }
257 else
258 {
259 _eventQueue.postKeyEvent(_handle
260 , 0 > _value ? s_axisDpad[_axis].first : s_axisDpad[_axis].second
261 , 0
262 , true
263 );
264 }
265 }
266 }
267 }
268
destroyentry::GamepadSDL269 void destroy()
270 {
271 if (NULL != m_controller)
272 {
273 SDL_GameControllerClose(m_controller);
274 m_controller = NULL;
275 }
276
277 if (NULL != m_joystick)
278 {
279 SDL_JoystickClose(m_joystick);
280 m_joystick = NULL;
281 }
282
283 m_jid = INT32_MAX;
284 }
285
filterentry::GamepadSDL286 bool filter(GamepadAxis::Enum _axis, int32_t* _value)
287 {
288 const int32_t old = m_value[_axis];
289 const int32_t deadzone = m_deadzone[_axis];
290 int32_t value = *_value;
291 value = value > deadzone || value < -deadzone ? value : 0;
292 m_value[_axis] = value;
293 *_value = value;
294 return old != value;
295 }
296
297 int32_t m_value[GamepadAxis::Count];
298 int32_t m_deadzone[GamepadAxis::Count];
299
300 SDL_Joystick* m_joystick;
301 SDL_GameController* m_controller;
302 // SDL_Haptic* m_haptic;
303 SDL_JoystickID m_jid;
304 };
305
306 struct MainThreadEntry
307 {
308 int m_argc;
309 char** m_argv;
310
311 static int32_t threadFunc(bx::Thread* _thread, void* _userData);
312 };
313
314 struct Msg
315 {
Msgentry::Msg316 Msg()
317 : m_x(0)
318 , m_y(0)
319 , m_width(0)
320 , m_height(0)
321 , m_flags(0)
322 , m_flagsEnabled(false)
323 {
324 }
325
326 int32_t m_x;
327 int32_t m_y;
328 uint32_t m_width;
329 uint32_t m_height;
330 uint32_t m_flags;
331 tinystl::string m_title;
332 bool m_flagsEnabled;
333 };
334
335 static uint32_t s_userEventStart;
336
337 enum SDL_USER_WINDOW
338 {
339 SDL_USER_WINDOW_CREATE,
340 SDL_USER_WINDOW_DESTROY,
341 SDL_USER_WINDOW_SET_TITLE,
342 SDL_USER_WINDOW_SET_FLAGS,
343 SDL_USER_WINDOW_SET_POS,
344 SDL_USER_WINDOW_SET_SIZE,
345 SDL_USER_WINDOW_TOGGLE_FRAME,
346 SDL_USER_WINDOW_TOGGLE_FULL_SCREEN,
347 SDL_USER_WINDOW_MOUSE_LOCK,
348 };
349
sdlPostEvent(SDL_USER_WINDOW _type,WindowHandle _handle,Msg * _msg=NULL,uint32_t _code=0)350 static void sdlPostEvent(SDL_USER_WINDOW _type, WindowHandle _handle, Msg* _msg = NULL, uint32_t _code = 0)
351 {
352 SDL_Event event;
353 SDL_UserEvent& uev = event.user;
354 uev.type = s_userEventStart + _type;
355
356 union { void* p; WindowHandle h; } cast;
357 cast.h = _handle;
358 uev.data1 = cast.p;
359
360 uev.data2 = _msg;
361 uev.code = _code;
362 SDL_PushEvent(&event);
363 }
364
getWindowHandle(const SDL_UserEvent & _uev)365 static WindowHandle getWindowHandle(const SDL_UserEvent& _uev)
366 {
367 union { void* p; WindowHandle h; } cast;
368 cast.p = _uev.data1;
369 return cast.h;
370 }
371
372 struct Context
373 {
Contextentry::Context374 Context()
375 : m_width(ENTRY_DEFAULT_WIDTH)
376 , m_height(ENTRY_DEFAULT_HEIGHT)
377 , m_aspectRatio(16.0f/9.0f)
378 , m_mx(0)
379 , m_my(0)
380 , m_mz(0)
381 , m_mouseLock(false)
382 , m_fullscreen(false)
383 {
384 bx::memSet(s_translateKey, 0, sizeof(s_translateKey) );
385 initTranslateKey(SDL_SCANCODE_ESCAPE, Key::Esc);
386 initTranslateKey(SDL_SCANCODE_RETURN, Key::Return);
387 initTranslateKey(SDL_SCANCODE_TAB, Key::Tab);
388 initTranslateKey(SDL_SCANCODE_BACKSPACE, Key::Backspace);
389 initTranslateKey(SDL_SCANCODE_SPACE, Key::Space);
390 initTranslateKey(SDL_SCANCODE_UP, Key::Up);
391 initTranslateKey(SDL_SCANCODE_DOWN, Key::Down);
392 initTranslateKey(SDL_SCANCODE_LEFT, Key::Left);
393 initTranslateKey(SDL_SCANCODE_RIGHT, Key::Right);
394 initTranslateKey(SDL_SCANCODE_PAGEUP, Key::PageUp);
395 initTranslateKey(SDL_SCANCODE_PAGEDOWN, Key::PageDown);
396 initTranslateKey(SDL_SCANCODE_HOME, Key::Home);
397 initTranslateKey(SDL_SCANCODE_END, Key::End);
398 initTranslateKey(SDL_SCANCODE_PRINTSCREEN, Key::Print);
399 initTranslateKey(SDL_SCANCODE_KP_PLUS, Key::Plus);
400 initTranslateKey(SDL_SCANCODE_EQUALS, Key::Plus);
401 initTranslateKey(SDL_SCANCODE_KP_MINUS, Key::Minus);
402 initTranslateKey(SDL_SCANCODE_MINUS, Key::Minus);
403 initTranslateKey(SDL_SCANCODE_GRAVE, Key::Tilde);
404 initTranslateKey(SDL_SCANCODE_KP_COMMA, Key::Comma);
405 initTranslateKey(SDL_SCANCODE_COMMA, Key::Comma);
406 initTranslateKey(SDL_SCANCODE_KP_PERIOD, Key::Period);
407 initTranslateKey(SDL_SCANCODE_PERIOD, Key::Period);
408 initTranslateKey(SDL_SCANCODE_SLASH, Key::Slash);
409 initTranslateKey(SDL_SCANCODE_F1, Key::F1);
410 initTranslateKey(SDL_SCANCODE_F2, Key::F2);
411 initTranslateKey(SDL_SCANCODE_F3, Key::F3);
412 initTranslateKey(SDL_SCANCODE_F4, Key::F4);
413 initTranslateKey(SDL_SCANCODE_F5, Key::F5);
414 initTranslateKey(SDL_SCANCODE_F6, Key::F6);
415 initTranslateKey(SDL_SCANCODE_F7, Key::F7);
416 initTranslateKey(SDL_SCANCODE_F8, Key::F8);
417 initTranslateKey(SDL_SCANCODE_F9, Key::F9);
418 initTranslateKey(SDL_SCANCODE_F10, Key::F10);
419 initTranslateKey(SDL_SCANCODE_F11, Key::F11);
420 initTranslateKey(SDL_SCANCODE_F12, Key::F12);
421 initTranslateKey(SDL_SCANCODE_KP_0, Key::NumPad0);
422 initTranslateKey(SDL_SCANCODE_KP_1, Key::NumPad1);
423 initTranslateKey(SDL_SCANCODE_KP_2, Key::NumPad2);
424 initTranslateKey(SDL_SCANCODE_KP_3, Key::NumPad3);
425 initTranslateKey(SDL_SCANCODE_KP_4, Key::NumPad4);
426 initTranslateKey(SDL_SCANCODE_KP_5, Key::NumPad5);
427 initTranslateKey(SDL_SCANCODE_KP_6, Key::NumPad6);
428 initTranslateKey(SDL_SCANCODE_KP_7, Key::NumPad7);
429 initTranslateKey(SDL_SCANCODE_KP_8, Key::NumPad8);
430 initTranslateKey(SDL_SCANCODE_KP_9, Key::NumPad9);
431 initTranslateKey(SDL_SCANCODE_0, Key::Key0);
432 initTranslateKey(SDL_SCANCODE_1, Key::Key1);
433 initTranslateKey(SDL_SCANCODE_2, Key::Key2);
434 initTranslateKey(SDL_SCANCODE_3, Key::Key3);
435 initTranslateKey(SDL_SCANCODE_4, Key::Key4);
436 initTranslateKey(SDL_SCANCODE_5, Key::Key5);
437 initTranslateKey(SDL_SCANCODE_6, Key::Key6);
438 initTranslateKey(SDL_SCANCODE_7, Key::Key7);
439 initTranslateKey(SDL_SCANCODE_8, Key::Key8);
440 initTranslateKey(SDL_SCANCODE_9, Key::Key9);
441 initTranslateKey(SDL_SCANCODE_A, Key::KeyA);
442 initTranslateKey(SDL_SCANCODE_B, Key::KeyB);
443 initTranslateKey(SDL_SCANCODE_C, Key::KeyC);
444 initTranslateKey(SDL_SCANCODE_D, Key::KeyD);
445 initTranslateKey(SDL_SCANCODE_E, Key::KeyE);
446 initTranslateKey(SDL_SCANCODE_F, Key::KeyF);
447 initTranslateKey(SDL_SCANCODE_G, Key::KeyG);
448 initTranslateKey(SDL_SCANCODE_H, Key::KeyH);
449 initTranslateKey(SDL_SCANCODE_I, Key::KeyI);
450 initTranslateKey(SDL_SCANCODE_J, Key::KeyJ);
451 initTranslateKey(SDL_SCANCODE_K, Key::KeyK);
452 initTranslateKey(SDL_SCANCODE_L, Key::KeyL);
453 initTranslateKey(SDL_SCANCODE_M, Key::KeyM);
454 initTranslateKey(SDL_SCANCODE_N, Key::KeyN);
455 initTranslateKey(SDL_SCANCODE_O, Key::KeyO);
456 initTranslateKey(SDL_SCANCODE_P, Key::KeyP);
457 initTranslateKey(SDL_SCANCODE_Q, Key::KeyQ);
458 initTranslateKey(SDL_SCANCODE_R, Key::KeyR);
459 initTranslateKey(SDL_SCANCODE_S, Key::KeyS);
460 initTranslateKey(SDL_SCANCODE_T, Key::KeyT);
461 initTranslateKey(SDL_SCANCODE_U, Key::KeyU);
462 initTranslateKey(SDL_SCANCODE_V, Key::KeyV);
463 initTranslateKey(SDL_SCANCODE_W, Key::KeyW);
464 initTranslateKey(SDL_SCANCODE_X, Key::KeyX);
465 initTranslateKey(SDL_SCANCODE_Y, Key::KeyY);
466 initTranslateKey(SDL_SCANCODE_Z, Key::KeyZ);
467
468 bx::memSet(s_translateGamepad, uint8_t(Key::Count), sizeof(s_translateGamepad) );
469 initTranslateGamepad(SDL_CONTROLLER_BUTTON_A, Key::GamepadA);
470 initTranslateGamepad(SDL_CONTROLLER_BUTTON_B, Key::GamepadB);
471 initTranslateGamepad(SDL_CONTROLLER_BUTTON_X, Key::GamepadX);
472 initTranslateGamepad(SDL_CONTROLLER_BUTTON_Y, Key::GamepadY);
473 initTranslateGamepad(SDL_CONTROLLER_BUTTON_LEFTSTICK, Key::GamepadThumbL);
474 initTranslateGamepad(SDL_CONTROLLER_BUTTON_RIGHTSTICK, Key::GamepadThumbR);
475 initTranslateGamepad(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, Key::GamepadShoulderL);
476 initTranslateGamepad(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, Key::GamepadShoulderR);
477 initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_UP, Key::GamepadUp);
478 initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_DOWN, Key::GamepadDown);
479 initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_LEFT, Key::GamepadLeft);
480 initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, Key::GamepadRight);
481 initTranslateGamepad(SDL_CONTROLLER_BUTTON_BACK, Key::GamepadBack);
482 initTranslateGamepad(SDL_CONTROLLER_BUTTON_START, Key::GamepadStart);
483 initTranslateGamepad(SDL_CONTROLLER_BUTTON_GUIDE, Key::GamepadGuide);
484
485 bx::memSet(s_translateGamepadAxis, uint8_t(GamepadAxis::Count), sizeof(s_translateGamepadAxis) );
486 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_LEFTX, GamepadAxis::LeftX);
487 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_LEFTY, GamepadAxis::LeftY);
488 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, GamepadAxis::LeftZ);
489 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_RIGHTX, GamepadAxis::RightX);
490 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_RIGHTY, GamepadAxis::RightY);
491 initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, GamepadAxis::RightZ);
492 }
493
runentry::Context494 int run(int _argc, char** _argv)
495 {
496 m_mte.m_argc = _argc;
497 m_mte.m_argv = _argv;
498
499 SDL_Init(0
500 | SDL_INIT_GAMECONTROLLER
501 );
502
503 m_windowAlloc.alloc();
504 m_window[0] = SDL_CreateWindow("bgfx"
505 , SDL_WINDOWPOS_UNDEFINED
506 , SDL_WINDOWPOS_UNDEFINED
507 , m_width
508 , m_height
509 , SDL_WINDOW_SHOWN
510 | SDL_WINDOW_RESIZABLE
511 );
512
513 m_flags[0] = 0
514 | ENTRY_WINDOW_FLAG_ASPECT_RATIO
515 | ENTRY_WINDOW_FLAG_FRAME
516 ;
517
518 s_userEventStart = SDL_RegisterEvents(7);
519
520 sdlSetWindow(m_window[0]);
521 bgfx::renderFrame();
522
523 m_thread.init(MainThreadEntry::threadFunc, &m_mte);
524
525 // Force window resolution...
526 WindowHandle defaultWindow = { 0 };
527 setWindowSize(defaultWindow, m_width, m_height, true);
528
529 SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
530
531 bx::FileReaderI* reader = NULL;
532 while (NULL == reader)
533 {
534 reader = getFileReader();
535 bx::sleep(100);
536 }
537
538 if (bx::open(reader, "gamecontrollerdb.txt") )
539 {
540 bx::AllocatorI* allocator = getAllocator();
541 uint32_t size = (uint32_t)bx::getSize(reader);
542 void* data = BX_ALLOC(allocator, size + 1);
543 bx::read(reader, data, size);
544 bx::close(reader);
545 ((char*)data)[size] = '\0';
546
547 if (SDL_GameControllerAddMapping( (char*)data) < 0) {
548 DBG("SDL game controller add mapping failed: %s", SDL_GetError());
549 }
550
551 BX_FREE(allocator, data);
552 }
553
554 bool exit = false;
555 SDL_Event event;
556 while (!exit)
557 {
558 bgfx::renderFrame();
559
560 while (SDL_PollEvent(&event) )
561 {
562 switch (event.type)
563 {
564 case SDL_QUIT:
565 m_eventQueue.postExitEvent();
566 exit = true;
567 break;
568
569 case SDL_MOUSEMOTION:
570 {
571 const SDL_MouseMotionEvent& mev = event.motion;
572 m_mx = mev.x;
573 m_my = mev.y;
574
575 WindowHandle handle = findHandle(mev.windowID);
576 if (isValid(handle) )
577 {
578 m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_mz);
579 }
580 }
581 break;
582
583 case SDL_MOUSEBUTTONDOWN:
584 case SDL_MOUSEBUTTONUP:
585 {
586 const SDL_MouseButtonEvent& mev = event.button;
587 WindowHandle handle = findHandle(mev.windowID);
588 if (isValid(handle) )
589 {
590 MouseButton::Enum button;
591 switch (mev.button)
592 {
593 default:
594 case SDL_BUTTON_LEFT: button = MouseButton::Left; break;
595 case SDL_BUTTON_MIDDLE: button = MouseButton::Middle; break;
596 case SDL_BUTTON_RIGHT: button = MouseButton::Right; break;
597 }
598
599 m_eventQueue.postMouseEvent(handle
600 , mev.x
601 , mev.y
602 , m_mz
603 , button
604 , mev.type == SDL_MOUSEBUTTONDOWN
605 );
606 }
607 }
608 break;
609
610 case SDL_MOUSEWHEEL:
611 {
612 const SDL_MouseWheelEvent& mev = event.wheel;
613 m_mz += mev.y;
614
615 WindowHandle handle = findHandle(mev.windowID);
616 if (isValid(handle) )
617 {
618 m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_mz);
619 }
620 }
621 break;
622
623 case SDL_TEXTINPUT:
624 {
625 const SDL_TextInputEvent& tev = event.text;
626 WindowHandle handle = findHandle(tev.windowID);
627 if (isValid(handle) )
628 {
629 m_eventQueue.postCharEvent(handle, 1, (const uint8_t*)tev.text);
630 }
631 }
632 break;
633
634 case SDL_KEYDOWN:
635 {
636 const SDL_KeyboardEvent& kev = event.key;
637 WindowHandle handle = findHandle(kev.windowID);
638 if (isValid(handle) )
639 {
640 uint8_t modifiers = translateKeyModifiers(kev.keysym.mod);
641 Key::Enum key = translateKey(kev.keysym.scancode);
642
643 #if 0
644 DBG("SDL scancode %d, key %d, name %s, key name %s"
645 , kev.keysym.scancode
646 , key
647 , SDL_GetScancodeName(kev.keysym.scancode)
648 , SDL_GetKeyName(kev.keysym.scancode)
649 );
650 #endif // 0
651
652 /// If you only press (e.g.) 'shift' and nothing else, then key == 'shift', modifier == 0.
653 /// Further along, pressing 'shift' + 'ctrl' would be: key == 'shift', modifier == 'ctrl.
654 if (0 == key && 0 == modifiers)
655 {
656 modifiers = translateKeyModifierPress(kev.keysym.scancode);
657 }
658
659 if (Key::Esc == key)
660 {
661 uint8_t pressedChar[4];
662 pressedChar[0] = 0x1b;
663 m_eventQueue.postCharEvent(handle, 1, pressedChar);
664 }
665 else if (Key::Return == key)
666 {
667 uint8_t pressedChar[4];
668 pressedChar[0] = 0x0d;
669 m_eventQueue.postCharEvent(handle, 1, pressedChar);
670 }
671 else if (Key::Backspace == key)
672 {
673 uint8_t pressedChar[4];
674 pressedChar[0] = 0x08;
675 m_eventQueue.postCharEvent(handle, 1, pressedChar);
676 }
677
678 m_eventQueue.postKeyEvent(handle, key, modifiers, kev.state == SDL_PRESSED);
679 }
680 }
681 break;
682
683 case SDL_KEYUP:
684 {
685 const SDL_KeyboardEvent& kev = event.key;
686 WindowHandle handle = findHandle(kev.windowID);
687 if (isValid(handle) )
688 {
689 uint8_t modifiers = translateKeyModifiers(kev.keysym.mod);
690 Key::Enum key = translateKey(kev.keysym.scancode);
691 m_eventQueue.postKeyEvent(handle, key, modifiers, kev.state == SDL_PRESSED);
692 }
693 }
694 break;
695
696 case SDL_WINDOWEVENT:
697 {
698 const SDL_WindowEvent& wev = event.window;
699 switch (wev.event)
700 {
701 case SDL_WINDOWEVENT_RESIZED:
702 case SDL_WINDOWEVENT_SIZE_CHANGED:
703 {
704 WindowHandle handle = findHandle(wev.windowID);
705 setWindowSize(handle, wev.data1, wev.data2);
706 }
707 break;
708
709 case SDL_WINDOWEVENT_SHOWN:
710 case SDL_WINDOWEVENT_HIDDEN:
711 case SDL_WINDOWEVENT_EXPOSED:
712 case SDL_WINDOWEVENT_MOVED:
713 case SDL_WINDOWEVENT_MINIMIZED:
714 case SDL_WINDOWEVENT_MAXIMIZED:
715 case SDL_WINDOWEVENT_RESTORED:
716 case SDL_WINDOWEVENT_ENTER:
717 case SDL_WINDOWEVENT_LEAVE:
718 case SDL_WINDOWEVENT_FOCUS_GAINED:
719 case SDL_WINDOWEVENT_FOCUS_LOST:
720 break;
721
722 case SDL_WINDOWEVENT_CLOSE:
723 {
724 WindowHandle handle = findHandle(wev.windowID);
725 if (0 == handle.idx)
726 {
727 m_eventQueue.postExitEvent();
728 exit = true;
729 }
730 }
731 break;
732 }
733 }
734 break;
735
736 case SDL_JOYAXISMOTION:
737 {
738 const SDL_JoyAxisEvent& jev = event.jaxis;
739 GamepadHandle handle = findGamepad(jev.which);
740 if (isValid(handle) )
741 {
742 GamepadAxis::Enum axis = translateGamepadAxis(jev.axis);
743 m_gamepad[handle.idx].update(m_eventQueue, defaultWindow, handle, axis, jev.value);
744 }
745 }
746 break;
747
748 case SDL_CONTROLLERAXISMOTION:
749 {
750 const SDL_ControllerAxisEvent& aev = event.caxis;
751 GamepadHandle handle = findGamepad(aev.which);
752 if (isValid(handle) )
753 {
754 GamepadAxis::Enum axis = translateGamepadAxis(aev.axis);
755 m_gamepad[handle.idx].update(m_eventQueue, defaultWindow, handle, axis, aev.value);
756 }
757 }
758 break;
759
760 case SDL_JOYBUTTONDOWN:
761 case SDL_JOYBUTTONUP:
762 {
763 const SDL_JoyButtonEvent& bev = event.jbutton;
764 GamepadHandle handle = findGamepad(bev.which);
765
766 if (isValid(handle) )
767 {
768 Key::Enum key = translateGamepad(bev.button);
769 if (Key::Count != key)
770 {
771 m_eventQueue.postKeyEvent(defaultWindow, key, 0, event.type == SDL_JOYBUTTONDOWN);
772 }
773 }
774 }
775 break;
776
777 case SDL_CONTROLLERBUTTONDOWN:
778 case SDL_CONTROLLERBUTTONUP:
779 {
780 const SDL_ControllerButtonEvent& bev = event.cbutton;
781 GamepadHandle handle = findGamepad(bev.which);
782 if (isValid(handle) )
783 {
784 Key::Enum key = translateGamepad(bev.button);
785 if (Key::Count != key)
786 {
787 m_eventQueue.postKeyEvent(defaultWindow, key, 0, event.type == SDL_CONTROLLERBUTTONDOWN);
788 }
789 }
790 }
791 break;
792
793 case SDL_JOYDEVICEADDED:
794 {
795 GamepadHandle handle = { m_gamepadAlloc.alloc() };
796 if (isValid(handle) )
797 {
798 const SDL_JoyDeviceEvent& jev = event.jdevice;
799 m_gamepad[handle.idx].create(jev);
800 m_eventQueue.postGamepadEvent(defaultWindow, handle, true);
801 }
802 }
803 break;
804
805 case SDL_JOYDEVICEREMOVED:
806 {
807 const SDL_JoyDeviceEvent& jev = event.jdevice;
808 GamepadHandle handle = findGamepad(jev.which);
809 if (isValid(handle) )
810 {
811 m_gamepad[handle.idx].destroy();
812 m_gamepadAlloc.free(handle.idx);
813 m_eventQueue.postGamepadEvent(defaultWindow, handle, false);
814 }
815 }
816 break;
817
818 case SDL_CONTROLLERDEVICEADDED:
819 {
820 GamepadHandle handle = { m_gamepadAlloc.alloc() };
821 if (isValid(handle) )
822 {
823 const SDL_ControllerDeviceEvent& cev = event.cdevice;
824 m_gamepad[handle.idx].create(cev);
825 m_eventQueue.postGamepadEvent(defaultWindow, handle, true);
826 }
827 }
828 break;
829
830 case SDL_CONTROLLERDEVICEREMAPPED:
831 {
832
833 }
834 break;
835
836 case SDL_CONTROLLERDEVICEREMOVED:
837 {
838 const SDL_ControllerDeviceEvent& cev = event.cdevice;
839 GamepadHandle handle = findGamepad(cev.which);
840 if (isValid(handle) )
841 {
842 m_gamepad[handle.idx].destroy();
843 m_gamepadAlloc.free(handle.idx);
844 m_eventQueue.postGamepadEvent(defaultWindow, handle, false);
845 }
846 }
847 break;
848
849 case SDL_DROPFILE:
850 {
851 const SDL_DropEvent& dev = event.drop;
852 WindowHandle handle = defaultWindow; //findHandle(dev.windowID);
853 if (isValid(handle) )
854 {
855 m_eventQueue.postDropFileEvent(handle, dev.file);
856 SDL_free(dev.file);
857 }
858 }
859 break;
860
861 default:
862 {
863 const SDL_UserEvent& uev = event.user;
864 switch (uev.type - s_userEventStart)
865 {
866 case SDL_USER_WINDOW_CREATE:
867 {
868 WindowHandle handle = getWindowHandle(uev);
869 Msg* msg = (Msg*)uev.data2;
870
871 m_window[handle.idx] = SDL_CreateWindow(msg->m_title.c_str()
872 , msg->m_x
873 , msg->m_y
874 , msg->m_width
875 , msg->m_height
876 , SDL_WINDOW_SHOWN
877 | SDL_WINDOW_RESIZABLE
878 );
879
880 m_flags[handle.idx] = msg->m_flags;
881
882 void* nwh = sdlNativeWindowHandle(m_window[handle.idx]);
883 if (NULL != nwh)
884 {
885 m_eventQueue.postSizeEvent(handle, msg->m_width, msg->m_height);
886 m_eventQueue.postWindowEvent(handle, nwh);
887 }
888
889 delete msg;
890 }
891 break;
892
893 case SDL_USER_WINDOW_DESTROY:
894 {
895 WindowHandle handle = getWindowHandle(uev);
896 if (isValid(handle) )
897 {
898 m_eventQueue.postWindowEvent(handle);
899 sdlDestroyWindow(m_window[handle.idx]);
900 m_window[handle.idx] = NULL;
901 }
902 }
903 break;
904
905 case SDL_USER_WINDOW_SET_TITLE:
906 {
907 WindowHandle handle = getWindowHandle(uev);
908 Msg* msg = (Msg*)uev.data2;
909 if (isValid(handle) )
910 {
911 SDL_SetWindowTitle(m_window[handle.idx], msg->m_title.c_str() );
912 }
913 delete msg;
914 }
915 break;
916
917 case SDL_USER_WINDOW_SET_FLAGS:
918 {
919 WindowHandle handle = getWindowHandle(uev);
920 Msg* msg = (Msg*)uev.data2;
921
922 if (msg->m_flagsEnabled)
923 {
924 m_flags[handle.idx] |= msg->m_flags;
925 }
926 else
927 {
928 m_flags[handle.idx] &= ~msg->m_flags;
929 }
930
931 delete msg;
932 }
933 break;
934
935 case SDL_USER_WINDOW_SET_POS:
936 {
937 WindowHandle handle = getWindowHandle(uev);
938 Msg* msg = (Msg*)uev.data2;
939 SDL_SetWindowPosition(m_window[handle.idx], msg->m_x, msg->m_y);
940 delete msg;
941 }
942 break;
943
944 case SDL_USER_WINDOW_SET_SIZE:
945 {
946 WindowHandle handle = getWindowHandle(uev);
947 Msg* msg = (Msg*)uev.data2;
948 if (isValid(handle) )
949 {
950 setWindowSize(handle, msg->m_width, msg->m_height);
951 }
952 delete msg;
953 }
954 break;
955
956 case SDL_USER_WINDOW_TOGGLE_FRAME:
957 {
958 WindowHandle handle = getWindowHandle(uev);
959 if (isValid(handle) )
960 {
961 m_flags[handle.idx] ^= ENTRY_WINDOW_FLAG_FRAME;
962 SDL_SetWindowBordered(m_window[handle.idx], (SDL_bool)!!(m_flags[handle.idx] & ENTRY_WINDOW_FLAG_FRAME) );
963 }
964 }
965 break;
966
967 case SDL_USER_WINDOW_TOGGLE_FULL_SCREEN:
968 {
969 WindowHandle handle = getWindowHandle(uev);
970 m_fullscreen = !m_fullscreen;
971 SDL_SetWindowFullscreen(m_window[handle.idx], m_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
972 }
973 break;
974
975 case SDL_USER_WINDOW_MOUSE_LOCK:
976 {
977 SDL_SetRelativeMouseMode(!!uev.code ? SDL_TRUE : SDL_FALSE);
978 }
979 break;
980
981 default:
982 break;
983 }
984 }
985 break;
986 }
987 }
988 }
989
990 while (bgfx::RenderFrame::NoContext != bgfx::renderFrame() ) {};
991 m_thread.shutdown();
992
993 sdlDestroyWindow(m_window[0]);
994 SDL_Quit();
995
996 return m_thread.getExitCode();
997 }
998
findHandleentry::Context999 WindowHandle findHandle(uint32_t _windowId)
1000 {
1001 SDL_Window* window = SDL_GetWindowFromID(_windowId);
1002 return findHandle(window);
1003 }
1004
findHandleentry::Context1005 WindowHandle findHandle(SDL_Window* _window)
1006 {
1007 bx::MutexScope scope(m_lock);
1008 for (uint32_t ii = 0, num = m_windowAlloc.getNumHandles(); ii < num; ++ii)
1009 {
1010 uint16_t idx = m_windowAlloc.getHandleAt(ii);
1011 if (_window == m_window[idx])
1012 {
1013 WindowHandle handle = { idx };
1014 return handle;
1015 }
1016 }
1017
1018 WindowHandle invalid = { UINT16_MAX };
1019 return invalid;
1020 }
1021
setWindowSizeentry::Context1022 void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height, bool _force = false)
1023 {
1024 if (_width != m_width
1025 || _height != m_height
1026 || _force)
1027 {
1028 m_width = _width;
1029 m_height = _height;
1030
1031 SDL_SetWindowSize(m_window[_handle.idx], m_width, m_height);
1032 m_eventQueue.postSizeEvent(_handle, m_width, m_height);
1033 }
1034 }
1035
findGamepadentry::Context1036 GamepadHandle findGamepad(SDL_JoystickID _jid)
1037 {
1038 for (uint32_t ii = 0, num = m_gamepadAlloc.getNumHandles(); ii < num; ++ii)
1039 {
1040 uint16_t idx = m_gamepadAlloc.getHandleAt(ii);
1041 if (_jid == m_gamepad[idx].m_jid)
1042 {
1043 GamepadHandle handle = { idx };
1044 return handle;
1045 }
1046 }
1047
1048 GamepadHandle invalid = { UINT16_MAX };
1049 return invalid;
1050 }
1051
1052 MainThreadEntry m_mte;
1053 bx::Thread m_thread;
1054
1055 EventQueue m_eventQueue;
1056 bx::Mutex m_lock;
1057
1058 bx::HandleAllocT<ENTRY_CONFIG_MAX_WINDOWS> m_windowAlloc;
1059 SDL_Window* m_window[ENTRY_CONFIG_MAX_WINDOWS];
1060 uint32_t m_flags[ENTRY_CONFIG_MAX_WINDOWS];
1061
1062 bx::HandleAllocT<ENTRY_CONFIG_MAX_GAMEPADS> m_gamepadAlloc;
1063 GamepadSDL m_gamepad[ENTRY_CONFIG_MAX_GAMEPADS];
1064
1065 uint32_t m_width;
1066 uint32_t m_height;
1067 float m_aspectRatio;
1068
1069 int32_t m_mx;
1070 int32_t m_my;
1071 int32_t m_mz;
1072 bool m_mouseLock;
1073 bool m_fullscreen;
1074 };
1075
1076 static Context s_ctx;
1077
poll()1078 const Event* poll()
1079 {
1080 return s_ctx.m_eventQueue.poll();
1081 }
1082
poll(WindowHandle _handle)1083 const Event* poll(WindowHandle _handle)
1084 {
1085 return s_ctx.m_eventQueue.poll(_handle);
1086 }
1087
release(const Event * _event)1088 void release(const Event* _event)
1089 {
1090 s_ctx.m_eventQueue.release(_event);
1091 }
1092
createWindow(int32_t _x,int32_t _y,uint32_t _width,uint32_t _height,uint32_t _flags,const char * _title)1093 WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title)
1094 {
1095 bx::MutexScope scope(s_ctx.m_lock);
1096 WindowHandle handle = { s_ctx.m_windowAlloc.alloc() };
1097
1098 if (UINT16_MAX != handle.idx)
1099 {
1100 Msg* msg = new Msg;
1101 msg->m_x = _x;
1102 msg->m_y = _y;
1103 msg->m_width = _width;
1104 msg->m_height = _height;
1105 msg->m_title = _title;
1106 msg->m_flags = _flags;
1107
1108 sdlPostEvent(SDL_USER_WINDOW_CREATE, handle, msg);
1109 }
1110
1111 return handle;
1112 }
1113
destroyWindow(WindowHandle _handle)1114 void destroyWindow(WindowHandle _handle)
1115 {
1116 if (UINT16_MAX != _handle.idx)
1117 {
1118 sdlPostEvent(SDL_USER_WINDOW_DESTROY, _handle);
1119
1120 bx::MutexScope scope(s_ctx.m_lock);
1121 s_ctx.m_windowAlloc.free(_handle.idx);
1122 }
1123 }
1124
setWindowPos(WindowHandle _handle,int32_t _x,int32_t _y)1125 void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y)
1126 {
1127 Msg* msg = new Msg;
1128 msg->m_x = _x;
1129 msg->m_y = _y;
1130
1131 sdlPostEvent(SDL_USER_WINDOW_SET_POS, _handle, msg);
1132 }
1133
setWindowSize(WindowHandle _handle,uint32_t _width,uint32_t _height)1134 void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
1135 {
1136 Msg* msg = new Msg;
1137 msg->m_width = _width;
1138 msg->m_height = _height;
1139
1140 sdlPostEvent(SDL_USER_WINDOW_SET_SIZE, _handle, msg);
1141 }
1142
setWindowTitle(WindowHandle _handle,const char * _title)1143 void setWindowTitle(WindowHandle _handle, const char* _title)
1144 {
1145 Msg* msg = new Msg;
1146 msg->m_title = _title;
1147
1148 sdlPostEvent(SDL_USER_WINDOW_SET_TITLE, _handle, msg);
1149 }
1150
setWindowFlags(WindowHandle _handle,uint32_t _flags,bool _enabled)1151 void setWindowFlags(WindowHandle _handle, uint32_t _flags, bool _enabled)
1152 {
1153 Msg* msg = new Msg;
1154 msg->m_flags = _flags;
1155 msg->m_flagsEnabled = _enabled;
1156 sdlPostEvent(SDL_USER_WINDOW_SET_FLAGS, _handle, msg);
1157 }
1158
toggleFullscreen(WindowHandle _handle)1159 void toggleFullscreen(WindowHandle _handle)
1160 {
1161 sdlPostEvent(SDL_USER_WINDOW_TOGGLE_FULL_SCREEN, _handle);
1162 }
1163
setMouseLock(WindowHandle _handle,bool _lock)1164 void setMouseLock(WindowHandle _handle, bool _lock)
1165 {
1166 sdlPostEvent(SDL_USER_WINDOW_MOUSE_LOCK, _handle, NULL, _lock);
1167 }
1168
threadFunc(bx::Thread * _thread,void * _userData)1169 int32_t MainThreadEntry::threadFunc(bx::Thread* _thread, void* _userData)
1170 {
1171 BX_UNUSED(_thread);
1172
1173 MainThreadEntry* self = (MainThreadEntry*)_userData;
1174 int32_t result = main(self->m_argc, self->m_argv);
1175
1176 SDL_Event event;
1177 SDL_QuitEvent& qev = event.quit;
1178 qev.type = SDL_QUIT;
1179 SDL_PushEvent(&event);
1180 return result;
1181 }
1182
1183 } // namespace entry
1184
main(int _argc,char ** _argv)1185 int main(int _argc, char** _argv)
1186 {
1187 using namespace entry;
1188 return s_ctx.run(_argc, _argv);
1189 }
1190
1191 #endif // ENTRY_CONFIG_USE_SDL
1192