Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

297 linhas
9.4 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. auto stick = input::joystick(i);
  74. for (int j = 0; j < SDL_JoystickNumAxes(sdlstick); ++j)
  75. stick->internal_add_axis((input::axis)(j + 1), 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. m_joysticks.remove(0);
  91. }
  92. #endif
  93. }
  94. void SdlInput::tick_game(float seconds)
  95. {
  96. Entity::tick_game(seconds);
  97. if (!m_tick_in_draw_thread)
  98. tick(seconds);
  99. }
  100. void SdlInput::tick_draw(float seconds, Scene &scene)
  101. {
  102. Entity::tick_draw(seconds, scene);
  103. if (m_tick_in_draw_thread)
  104. tick(seconds);
  105. }
  106. void SdlInput::tick(float seconds)
  107. {
  108. #if LOL_USE_SDL
  109. /* FIXME: maybe we should make use of this? */
  110. UNUSED(seconds);
  111. auto keyboard = input::keyboard();
  112. auto mouse = input::mouse();
  113. /* Pump all joystick events because no event is coming to us. */
  114. # if SDL_FORCE_POLL_JOYSTICK && !__EMSCRIPTEN__
  115. SDL_JoystickUpdate();
  116. for (int j = 0; j < m_joysticks.count(); j++)
  117. {
  118. for (int i = 0; i < SDL_JoystickNumButtons(m_joysticks[j].m1); i++)
  119. m_joysticks[j].m2->internal_set_button((input::button)i, SDL_JoystickGetButton(m_joysticks[j].m1, i) != 0);
  120. for (int i = 0; i < SDL_JoystickNumAxes(m_joysticks[j].m1); i++)
  121. m_joysticks[j].m2->internal_set_axis((input::axis)i, (float)SDL_JoystickGetAxis(m_joysticks[j].m1, i) / 32768.f);
  122. }
  123. # endif
  124. keyboard->internal_begin_frame();
  125. float mouse_wheel = 0.f;
  126. if (keyboard->capture_text())
  127. SDL_StartTextInput();
  128. else
  129. SDL_StopTextInput();
  130. /* Handle keyboard and WM events */
  131. SDL_Event event;
  132. while (SDL_PollEvent(&event))
  133. {
  134. switch (event.type)
  135. {
  136. case SDL_QUIT:
  137. Ticker::Shutdown();
  138. break;
  139. case SDL_KEYDOWN:
  140. case SDL_KEYUP:
  141. switch (auto sc = (input::key)event.key.keysym.scancode)
  142. {
  143. // Lock management
  144. case input::key::SC_CapsLock:
  145. case input::key::SC_ScrollLock:
  146. case input::key::SC_NumLockClear:
  147. // Update status on key down only
  148. if (event.type == SDL_KEYDOWN)
  149. {
  150. auto sc2 = sc;
  151. if (sc == input::key::SC_CapsLock)
  152. sc2 = input::key::SC_CapsLockStatus;
  153. else if (sc == input::key::SC_ScrollLock)
  154. sc2 = input::key::SC_ScrollLockStatus;
  155. else if (sc == input::key::SC_NumLockClear)
  156. sc2 = input::key::SC_NumLockClearStatus;
  157. keyboard->internal_set_key(sc2, !keyboard->key(sc2));
  158. }
  159. LOL_ATTR_FALLTHROUGH
  160. default:
  161. // Set key updates the corresponding key
  162. keyboard->internal_set_key(sc, event.type == SDL_KEYDOWN);
  163. break;
  164. }
  165. break;
  166. //case SDL_TEXTEDITING: //TODO: handle that?
  167. case SDL_TEXTINPUT:
  168. keyboard->internal_add_text(event.text.text);
  169. break;
  170. case SDL_MOUSEBUTTONDOWN:
  171. case SDL_MOUSEBUTTONUP:
  172. //event.button.which
  173. mouse->internal_set_button((input::button)((int)input::button::BTN_Left + event.button.button - 1),
  174. event.type == SDL_MOUSEBUTTONDOWN);
  175. break;
  176. case SDL_MOUSEWHEEL:
  177. mouse_wheel += (float)event.button.y;
  178. break;
  179. case SDL_WINDOWEVENT:
  180. {
  181. switch (event.window.event)
  182. {
  183. case SDL_WINDOWEVENT_ENTER:
  184. case SDL_WINDOWEVENT_FOCUS_GAINED:
  185. mouse->internal_set_button(input::button::BTN_Focus, true);
  186. break;
  187. case SDL_WINDOWEVENT_LEAVE:
  188. case SDL_WINDOWEVENT_FOCUS_LOST:
  189. mouse->internal_set_button(input::button::BTN_Focus, false);
  190. break;
  191. case SDL_WINDOWEVENT_RESIZED:
  192. Video::Resize(ivec2(event.window.data1, event.window.data2));
  193. break;
  194. }
  195. break;
  196. }
  197. # if !SDL_FORCE_POLL_JOYSTICK
  198. case SDL_JOYAXISMOTION:
  199. m_joysticks[event.jaxis.which].m2->internal_set_axis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
  200. break;
  201. case SDL_JOYBUTTONUP:
  202. case SDL_JOYBUTTONDOWN:
  203. m_joysticks[event.jbutton.which].m2->internal_set_key(event.jbutton.button, event.jbutton.state);
  204. break;
  205. # endif
  206. }
  207. }
  208. /* Handle mouse input */
  209. mouse->internal_set_axis(input::axis::Wheel, mouse_wheel);
  210. ivec2 window_size;
  211. // FIXME: get actual window size
  212. //SDL_GetWindowSize(m_window, &window_size.x, &window_size.y);
  213. window_size = (ivec2)m_app;
  214. ivec2 mouse_pos(-1, -1);
  215. SDL_GetMouseState(&mouse_pos.x, &mouse_pos.y);
  216. mouse_pos.y = Video::GetSize().y - 1 - mouse_pos.y;
  217. if (mouse->capture() != m_mousecapture)
  218. {
  219. m_mousecapture = mouse->capture();
  220. SDL_SetRelativeMouseMode(m_mousecapture ? SDL_TRUE : SDL_FALSE);
  221. // FIXME: get handle to window
  222. //SDL_WarpMouseInWindow(m_window, window_size.x / 2, window_size.y / 2);
  223. SDL_ShowCursor(m_mousecapture ? SDL_DISABLE : SDL_ENABLE);
  224. }
  225. if (mouse_pos.x >= 0 && mouse_pos.x < window_size.x && mouse_pos.y >= 0 && mouse_pos.y < window_size.y)
  226. {
  227. //We need the max if we want coherent mouse speed between axis
  228. float max_screen_size = lol::max(m_screen.x, m_screen.y);
  229. vec2 vmouse = vec2(mouse_pos);
  230. vec2 vprevmouse = vec2(m_prev_mouse_pos);
  231. mouse->internal_set_axis(input::axis::X, vmouse.x / window_size.x);
  232. mouse->internal_set_axis(input::axis::Y, vmouse.y / window_size.y);
  233. mouse->internal_set_axis(input::axis::ScreenX, mouse_pos.x);
  234. mouse->internal_set_axis(input::axis::ScreenY, mouse_pos.y);
  235. // Note: 100.0f is an arbitrary value that makes it feel about the same than an xbox controller joystick
  236. mouse->internal_set_axis(input::axis::MoveX, (mouse_pos.x - vprevmouse.x) * MOUSE_SPEED_MOD / max_screen_size);
  237. // Y Axis is also negated to match the usual joystick Y axis (negatives values are for the upper direction)
  238. mouse->internal_set_axis(input::axis::MoveY,-(mouse_pos.y - vprevmouse.y) * MOUSE_SPEED_MOD / max_screen_size);
  239. //Pixel movement
  240. mouse->internal_set_axis(input::axis::ScreenMoveX, (mouse_pos.x - vprevmouse.x));
  241. mouse->internal_set_axis(input::axis::ScreenMoveY,-(mouse_pos.y - vprevmouse.y));
  242. }
  243. if (m_mousecapture)
  244. {
  245. //mouse_pos = ivec2(window_size * .5f);
  246. //SDL_WarpMouse((uint16_t)mouse_pos.x, (uint16_t)mouse_pos.y);
  247. }
  248. m_prev_mouse_pos = mouse_pos;
  249. #else
  250. UNUSED(seconds);
  251. #endif //LOL_USE_SDL
  252. }
  253. } /* namespace lol */