288 line
9.0 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #if LOL_USE_SDL
  14. # if HAVE_SDL2_SDL_H
  15. # include <SDL2/SDL.h>
  16. # elif HAVE_SDL_H
  17. # include <SDL.h>
  18. # endif
  19. #endif
  20. #if HAVE_EMSCRIPTEN_HTML5_H
  21. # include <emscripten/html5.h>
  22. #endif
  23. #include "ui/sdl-input.h"
  24. #include "ui/input.h"
  25. /* We force joystick polling because no events are received when
  26. * there is no SDL display (eg. on the Raspberry Pi). */
  27. #define SDL_FORCE_POLL_JOYSTICK 1
  28. #if __EMSCRIPTEN__
  29. # define MOUSE_SPEED_MOD 10.f
  30. #else
  31. # define MOUSE_SPEED_MOD 100.f
  32. #endif
  33. namespace lol
  34. {
  35. /*
  36. * Public SdlInput class
  37. */
  38. SdlInput::SdlInput(int app_w, int app_h, int screen_w, int screen_h)
  39. : m_app(vec2((float)app_w, (float)app_h)),
  40. m_screen(vec2((float)screen_w, (float)screen_h))
  41. {
  42. #if _WIN32
  43. m_tick_in_draw_thread = true;
  44. #endif
  45. #if __EMSCRIPTEN__
  46. emscripten_sample_gamepad_data();
  47. #endif
  48. #if LOL_USE_SDL
  49. SDL_Init(SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
  50. #endif
  51. // XXX: another option for emscripten is to properly support gamepads
  52. #if LOL_USE_SDL && !__EMSCRIPTEN__
  53. SDL_JoystickEventState(SDL_FORCE_POLL_JOYSTICK ? SDL_QUERY : SDL_ENABLE);
  54. /* Register all the joysticks we can find, and let the input
  55. * system decide what it wants to track. */
  56. for (int i = 0; i < SDL_NumJoysticks(); i++)
  57. {
  58. SDL_Joystick *sdlstick = SDL_JoystickOpen(i);
  59. /* Blacklist some devices:
  60. * - HDAPS, it's not a real joystick.
  61. * - X360 controllers, Xinput handles them better since
  62. * it won't think there is only one trigger axis. */
  63. char const *name = SDL_JoystickName(sdlstick);
  64. if (strstr(name, "HDAPS")
  65. # if LOL_USE_XINPUT
  66. || strstr(name, "XBOX 360 For Windows")
  67. # endif //LOL_USE_XINPUT
  68. || false)
  69. {
  70. SDL_JoystickClose(sdlstick);
  71. continue;
  72. }
  73. InputDevice* stick = new InputDevice(g_name_joystick(i + 1));
  74. for (int j = 0; j < SDL_JoystickNumAxes(sdlstick); ++j)
  75. stick->AddAxis(format("Axis%d", j + 1).c_str());
  76. for (int j = 0; j < SDL_JoystickNumButtons(sdlstick); ++j)
  77. stick->internal_add_button((input::button)(j + 1), format("Button%d", j + 1).c_str());
  78. m_joysticks.push(sdlstick, stick);
  79. }
  80. #endif
  81. m_gamegroup = tickable::group::game::input;
  82. }
  83. SdlInput::~SdlInput()
  84. {
  85. #if LOL_USE_SDL && !__EMSCRIPTEN__
  86. /* Unregister all the joysticks we added */
  87. while (m_joysticks.count())
  88. {
  89. SDL_JoystickClose(m_joysticks[0].m1);
  90. delete m_joysticks[0].m2;
  91. m_joysticks.remove(0);
  92. }
  93. #endif
  94. }
  95. void SdlInput::tick_game(float seconds)
  96. {
  97. Entity::tick_game(seconds);
  98. if (!m_tick_in_draw_thread)
  99. tick(seconds);
  100. }
  101. void SdlInput::tick_draw(float seconds, Scene &scene)
  102. {
  103. Entity::tick_draw(seconds, scene);
  104. if (m_tick_in_draw_thread)
  105. tick(seconds);
  106. }
  107. void SdlInput::tick(float seconds)
  108. {
  109. #if LOL_USE_SDL
  110. /* FIXME: maybe we should make use of this? */
  111. UNUSED(seconds);
  112. auto keyboard = input::keyboard();
  113. auto mouse = input::mouse();
  114. /* Pump all joystick events because no event is coming to us. */
  115. # if SDL_FORCE_POLL_JOYSTICK && !__EMSCRIPTEN__
  116. SDL_JoystickUpdate();
  117. for (int j = 0; j < m_joysticks.count(); j++)
  118. {
  119. for (int i = 0; i < SDL_JoystickNumButtons(m_joysticks[j].m1); i++)
  120. m_joysticks[j].m2->internal_set_button((input::button)i, SDL_JoystickGetButton(m_joysticks[j].m1, i) != 0);
  121. for (int i = 0; i < SDL_JoystickNumAxes(m_joysticks[j].m1); i++)
  122. m_joysticks[j].m2->internal_set_axis(i, (float)SDL_JoystickGetAxis(m_joysticks[j].m1, i) / 32768.f);
  123. }
  124. # endif
  125. keyboard->internal_begin_frame();
  126. mouse->internal_set_axis(4, 0);
  127. if (keyboard->capture_text())
  128. SDL_StartTextInput();
  129. else
  130. SDL_StopTextInput();
  131. /* Handle keyboard and WM events */
  132. SDL_Event event;
  133. while (SDL_PollEvent(&event))
  134. {
  135. switch (event.type)
  136. {
  137. case SDL_QUIT:
  138. Ticker::Shutdown();
  139. break;
  140. case SDL_KEYDOWN:
  141. case SDL_KEYUP:
  142. switch (auto sc = (input::key)event.key.keysym.scancode)
  143. {
  144. // Lock management
  145. case input::key::SC_CapsLock:
  146. case input::key::SC_ScrollLock:
  147. case input::key::SC_NumLockClear:
  148. // Update status on key down only
  149. if (event.type == SDL_KEYDOWN)
  150. {
  151. auto sc2 = sc;
  152. if (sc == input::key::SC_CapsLock)
  153. sc2 = input::key::SC_CapsLockStatus;
  154. else if (sc == input::key::SC_ScrollLock)
  155. sc2 = input::key::SC_ScrollLockStatus;
  156. else if (sc == input::key::SC_NumLockClear)
  157. sc2 = input::key::SC_NumLockClearStatus;
  158. keyboard->internal_set_key(sc2, !keyboard->key(sc2));
  159. }
  160. LOL_ATTR_FALLTHROUGH
  161. default:
  162. // Set key updates the corresponding key
  163. keyboard->internal_set_key(sc, event.type == SDL_KEYDOWN);
  164. break;
  165. }
  166. break;
  167. //case SDL_TEXTEDITING: //TODO: handle that?
  168. case SDL_TEXTINPUT:
  169. keyboard->internal_add_text(event.text.text);
  170. break;
  171. case SDL_MOUSEBUTTONDOWN:
  172. case SDL_MOUSEBUTTONUP:
  173. //event.button.which
  174. mouse->internal_set_button((input::button)((int)input::button::BTN_Left + event.button.button - 1),
  175. event.type == SDL_MOUSEBUTTONDOWN);
  176. break;
  177. case SDL_MOUSEWHEEL:
  178. mouse->internal_set_axis(4, (float)event.button.y);
  179. break;
  180. case SDL_WINDOWEVENT:
  181. {
  182. switch (event.window.event)
  183. {
  184. case SDL_WINDOWEVENT_ENTER:
  185. case SDL_WINDOWEVENT_FOCUS_GAINED:
  186. mouse->internal_set_button(input::button::BTN_Focus, true);
  187. break;
  188. case SDL_WINDOWEVENT_LEAVE:
  189. case SDL_WINDOWEVENT_FOCUS_LOST:
  190. mouse->internal_set_button(input::button::BTN_Focus, false);
  191. break;
  192. case SDL_WINDOWEVENT_RESIZED:
  193. Video::Resize(ivec2(event.window.data1, event.window.data2));
  194. break;
  195. }
  196. break;
  197. }
  198. # if !SDL_FORCE_POLL_JOYSTICK
  199. case SDL_JOYAXISMOTION:
  200. m_joysticks[event.jaxis.which].m2->internal_set_axis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
  201. break;
  202. case SDL_JOYBUTTONUP:
  203. case SDL_JOYBUTTONDOWN:
  204. m_joysticks[event.jbutton.which].m2->internal_set_key(event.jbutton.button, event.jbutton.state);
  205. break;
  206. # endif
  207. }
  208. }
  209. /* Handle mouse input */
  210. ivec2 mouse_pos(-1, -1);
  211. SDL_GetMouseState(&mouse_pos.x, &mouse_pos.y);
  212. mouse_pos.y = Video::GetSize().y - 1 - mouse_pos.y;
  213. if (mouse->capture() != m_mousecapture)
  214. {
  215. m_mousecapture = mouse->capture();
  216. SDL_SetRelativeMouseMode(m_mousecapture ? SDL_TRUE : SDL_FALSE);
  217. mouse_pos = ivec2(m_app * .5f);
  218. // FIXME: how do I warped mouse?
  219. //SDL_WarpMouse((uint16_t)mouse_pos.x, (uint16_t)mouse_pos.y);
  220. //SDL_ShowCursor(m_mousecapture ? SDL_DISABLE : SDL_ENABLE);
  221. }
  222. if (mouse_pos.x >= 0 && mouse_pos.x < m_app.x && mouse_pos.y >= 0 && mouse_pos.y < m_app.y)
  223. {
  224. //We need the max if we want coherent mouse speed between axis
  225. float max_screen_size = lol::max(m_screen.x, m_screen.y);
  226. vec2 vmouse = vec2(mouse_pos);
  227. vec2 vprevmouse = vec2(m_prev_mouse_pos);
  228. mouse->internal_set_cursor(0, vmouse / m_app, mouse_pos);
  229. // Note: 100.0f is an arbitrary value that makes it feel about the same than an xbox controller joystick
  230. mouse->internal_set_axis(0, (mouse_pos.x - vprevmouse.x) * MOUSE_SPEED_MOD / max_screen_size);
  231. // Y Axis is also negated to match the usual joystick Y axis (negatives values are for the upper direction)
  232. mouse->internal_set_axis(1,-(mouse_pos.y - vprevmouse.y) * MOUSE_SPEED_MOD / max_screen_size);
  233. //Pixel movement
  234. mouse->internal_set_axis(2, (mouse_pos.x - vprevmouse.x));
  235. mouse->internal_set_axis(3,-(mouse_pos.y - vprevmouse.y));
  236. }
  237. if (m_mousecapture)
  238. {
  239. mouse_pos = ivec2(m_app * .5f);
  240. //SDL_WarpMouse((uint16_t)mouse_pos.x, (uint16_t)mouse_pos.y);
  241. }
  242. m_prev_mouse_pos = mouse_pos;
  243. #else
  244. UNUSED(seconds);
  245. #endif //LOL_USE_SDL
  246. }
  247. } /* namespace lol */