341 строка
9.4 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2014 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. #include <lol/engine-internal.h>
  11. #if USE_SDL || USE_OLD_SDL
  12. # if HAVE_SDL2_SDL_H
  13. # include <SDL2/SDL.h>
  14. # elif HAVE_SDL_SDL_H
  15. # include <SDL/SDL.h>
  16. # else
  17. # include <SDL.h>
  18. # endif
  19. #endif
  20. #include "sdlinput.h"
  21. #include "input/input_internal.h"
  22. /* We force joystick polling because no events are received when
  23. * there is no SDL display (eg. on the Raspberry Pi). */
  24. #define SDL_FORCE_POLL_JOYSTICK 1
  25. #if EMSCRIPTEN
  26. # define MOUSE_SPEED_MOD 10.f
  27. #else
  28. # define MOUSE_SPEED_MOD 100.f
  29. #endif
  30. namespace lol
  31. {
  32. #if USE_OLD_SDL
  33. /* Quick and dirty for now... This is deprecated anyway. */
  34. static int sdl12_to_scancode(int ch, int sc)
  35. {
  36. if (ch >= 'a' && ch <= 'z')
  37. ch = ch - 'a' + 'A';
  38. # define _SC(id, str, name) if (ch == str[0]) return id;
  39. # include "input/keys.h"
  40. return 0;
  41. }
  42. #endif
  43. /*
  44. * SDL Input implementation class
  45. */
  46. class SdlInputData
  47. {
  48. friend class SdlInput;
  49. private:
  50. void Tick(float seconds);
  51. static ivec2 GetMousePos();
  52. static void SetMousePos(ivec2 position);
  53. SdlInputData(int app_w, int app_h, int screen_w, int screen_h) :
  54. m_prevmouse(ivec2::zero),
  55. m_app(vec2((float)app_w, (float)app_h)),
  56. m_screen(vec2((float)screen_w, (float)screen_h)),
  57. m_mousecapture(false),
  58. m_tick_in_draw_thread(false)
  59. { }
  60. #if USE_SDL || USE_OLD_SDL
  61. array<SDL_Joystick *, InputDeviceInternal *> m_joysticks;
  62. InputDeviceInternal *m_mouse;
  63. InputDeviceInternal *m_keyboard;
  64. #endif // USE_SDL
  65. ivec2 m_prevmouse;
  66. vec2 m_app;
  67. vec2 m_screen;
  68. bool m_mousecapture;
  69. bool m_tick_in_draw_thread;
  70. };
  71. /*
  72. * Public SdlInput class
  73. */
  74. SdlInput::SdlInput(int app_w, int app_h, int screen_w, int screen_h)
  75. : m_data(new SdlInputData(app_w, app_h, screen_w, screen_h))
  76. {
  77. #if _WIN32
  78. m_data->m_tick_in_draw_thread = true;
  79. #endif
  80. #if USE_OLD_SDL
  81. /* Enable Unicode translation of keyboard events */
  82. SDL_EnableUNICODE(1);
  83. #endif
  84. #if USE_SDL || USE_OLD_SDL
  85. SDL_Init(SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
  86. #endif
  87. m_data->m_keyboard = InputDeviceInternal::CreateStandardKeyboard();
  88. m_data->m_mouse = InputDeviceInternal::CreateStandardMouse();
  89. #if USE_SDL || USE_OLD_SDL
  90. # if !EMSCRIPTEN
  91. # if SDL_FORCE_POLL_JOYSTICK
  92. SDL_JoystickEventState(SDL_QUERY);
  93. # else
  94. SDL_JoystickEventState(SDL_ENABLE);
  95. # endif //SDL_FORCE_POLL_JOYSTICK
  96. /* Register all the joysticks we can find, and let the input
  97. * system decide what it wants to track. */
  98. for (int i = 0; i < SDL_NumJoysticks(); i++)
  99. {
  100. SDL_Joystick *sdlstick = SDL_JoystickOpen(i);
  101. /* Blacklist some devices:
  102. * - HDAPS, it's not a real joystick.
  103. * - X360 controllers, Xinput handles them better since
  104. * it won't think there is only one trigger axis. */
  105. # if USE_SDL
  106. char const *name = SDL_JoystickName(sdlstick);
  107. # elif USE_OLD_SDL
  108. char const *name = SDL_JoystickName(i);
  109. # endif
  110. if (strstr(name, "HDAPS")
  111. # if USE_XINPUT
  112. || strstr(name, "XBOX 360 For Windows")
  113. # endif //USE_XINPUT
  114. || false)
  115. {
  116. SDL_JoystickClose(sdlstick);
  117. continue;
  118. }
  119. InputDeviceInternal* stick = new InputDeviceInternal(String::Printf("Joystick%d", i+1).C());
  120. for (int j = 0; j < SDL_JoystickNumAxes(sdlstick); ++j)
  121. stick->AddAxis(String::Printf("Axis%d", j + 1).C());
  122. for (int j = 0; j < SDL_JoystickNumButtons(sdlstick); ++j)
  123. stick->AddKey(String::Printf("Button%d", j + 1).C());
  124. m_data->m_joysticks.Push(sdlstick, stick);
  125. }
  126. # endif //EMSCRIPTEN
  127. #endif
  128. m_gamegroup = GAMEGROUP_BEFORE;
  129. }
  130. SdlInput::~SdlInput()
  131. {
  132. #if (USE_SDL || USE_OLD_SDL) && !EMSCRIPTEN
  133. /* Unregister all the joysticks we added */
  134. while (m_data->m_joysticks.Count())
  135. {
  136. SDL_JoystickClose(m_data->m_joysticks[0].m1);
  137. delete m_data->m_joysticks[0].m2;
  138. m_data->m_joysticks.Remove(0);
  139. }
  140. #endif
  141. delete m_data;
  142. }
  143. void SdlInput::TickGame(float seconds)
  144. {
  145. Entity::TickGame(seconds);
  146. if (!m_data->m_tick_in_draw_thread)
  147. m_data->Tick(seconds);
  148. }
  149. void SdlInput::TickDraw(float seconds, Scene &scene)
  150. {
  151. Entity::TickDraw(seconds, scene);
  152. if (m_data->m_tick_in_draw_thread)
  153. m_data->Tick(seconds);
  154. }
  155. void SdlInputData::Tick(float seconds)
  156. {
  157. #if USE_SDL || USE_OLD_SDL
  158. /* Pump all joystick events because no event is coming to us. */
  159. # if SDL_FORCE_POLL_JOYSTICK && !EMSCRIPTEN
  160. SDL_JoystickUpdate();
  161. for (int j = 0; j < m_joysticks.Count(); j++)
  162. {
  163. for (int i = 0; i < SDL_JoystickNumButtons(m_joysticks[j].m1); i++)
  164. m_joysticks[j].m2->SetKey(i, SDL_JoystickGetButton(m_joysticks[j].m1, i) != 0);
  165. for (int i = 0; i < SDL_JoystickNumAxes(m_joysticks[j].m1); i++)
  166. m_joysticks[j].m2->SetAxis(i, (float)SDL_JoystickGetAxis(m_joysticks[j].m1, i) / 32768.f);
  167. }
  168. # endif
  169. m_mouse->SetAxis(4, 0);
  170. /* Handle keyboard and WM events */
  171. SDL_Event event;
  172. while (SDL_PollEvent(&event))
  173. {
  174. switch (event.type)
  175. {
  176. case SDL_QUIT:
  177. Ticker::Shutdown();
  178. break;
  179. case SDL_KEYDOWN:
  180. case SDL_KEYUP:
  181. #if USE_OLD_SDL
  182. if (int sc = sdl12_to_scancode(event.key.keysym.sym,
  183. event.key.keysym.scancode))
  184. #else
  185. if (int sc = event.key.keysym.scancode)
  186. #endif
  187. m_keyboard->SetKey(sc, event.type == SDL_KEYDOWN);
  188. else
  189. Log::Error("unknown keypress (sym 0x%02x, scancode %0d)\n",
  190. event.key.keysym.sym, event.key.keysym.scancode);
  191. break;
  192. case SDL_MOUSEBUTTONDOWN:
  193. case SDL_MOUSEBUTTONUP:
  194. {
  195. # if USE_OLD_SDL
  196. if (event.button.button != SDL_BUTTON_WHEELUP && event.button.button != SDL_BUTTON_WHEELDOWN)
  197. m_mouse->SetKey(event.button.button - 1, event.type == SDL_MOUSEBUTTONDOWN);
  198. else
  199. m_mouse->SetAxis(4, (event.button.button != SDL_BUTTON_WHEELUP) ? (1) : (-1));
  200. # endif
  201. // TODO: mouse wheel as axis
  202. break;
  203. }
  204. # if !SDL_FORCE_POLL_JOYSTICK
  205. case SDL_JOYAXISMOTION:
  206. m_joysticks[event.jaxis.which].m2->SetAxis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
  207. break;
  208. case SDL_JOYBUTTONUP:
  209. case SDL_JOYBUTTONDOWN:
  210. m_joysticks[event.jbutton.which].m2->SetKey(event.jbutton.button, event.jbutton.state);
  211. break;
  212. # endif
  213. }
  214. }
  215. /* Handle mouse input */
  216. ivec2 mouse = SdlInputData::GetMousePos();
  217. if (InputDeviceInternal::GetMouseCapture() != m_mousecapture)
  218. {
  219. m_mousecapture = InputDeviceInternal::GetMouseCapture();
  220. # if USE_SDL
  221. SDL_SetRelativeMouseMode(m_mousecapture ? SDL_TRUE : SDL_FALSE);
  222. # elif USE_OLD_SDL
  223. SDL_WM_GrabInput(m_mousecapture ? SDL_GRAB_ON : SDL_GRAB_OFF);
  224. # endif
  225. mouse = (ivec2)m_app / 2;
  226. SdlInputData::SetMousePos(mouse);
  227. //SDL_ShowCursor(m_mousecapture ? SDL_DISABLE : SDL_ENABLE);
  228. }
  229. if (mouse.x >= 0 && mouse.x < m_app.x && mouse.y >= 0 && mouse.y < m_app.y)
  230. {
  231. //We need the max if we want coherent mouse speed between axis
  232. float max_screen_size = lol::max(m_screen.x, m_screen.y);
  233. vec2 vmouse = vec2(mouse);
  234. vec2 vprevmouse = vec2(m_prevmouse);
  235. m_mouse->SetCursor(0, vmouse / m_app, mouse);
  236. // Note: 100.0f is an arbitrary value that makes it feel about the same than an xbox controller joystick
  237. m_mouse->SetAxis(0, (mouse.x - vprevmouse.x) * MOUSE_SPEED_MOD / max_screen_size);
  238. // Y Axis is also negated to match the usual joystick Y axis (negatives values are for the upper direction)
  239. m_mouse->SetAxis(1,-(mouse.y - vprevmouse.y) * MOUSE_SPEED_MOD / max_screen_size);
  240. //Pixel movement
  241. m_mouse->SetAxis(2, (mouse.x - vprevmouse.x));
  242. m_mouse->SetAxis(3,-(mouse.y - vprevmouse.y));
  243. }
  244. //Mouse is focused, Validate the InScreen Key
  245. //Hardcoded 3, not very nice.
  246. # if !EMSCRIPTEN && USE_OLD_SDL
  247. m_mouse->SetKey(3, !!(SDL_GetAppState() & SDL_APPMOUSEFOCUS));
  248. # else
  249. // Emscripten doesn't seem to handle SDL_APPMOUSEFOCUS
  250. // FIXME: SDL2 doesn't have SDL_APPMOUSEFOCUS either
  251. m_mouse->SetKey(3, true);
  252. # endif
  253. if (m_mousecapture)
  254. {
  255. mouse = ivec2(m_app * .5f);
  256. SdlInputData::SetMousePos(mouse);
  257. }
  258. m_prevmouse = mouse;
  259. #else
  260. UNUSED(seconds);
  261. #endif //USE_SDL
  262. }
  263. // NOTE: these two functions are pointless now and could be inlined directly
  264. ivec2 SdlInputData::GetMousePos()
  265. {
  266. ivec2 ret(-1, -1);
  267. #if USE_SDL || USE_OLD_SDL
  268. # if !EMSCRIPTEN && USE_OLD_SDL
  269. if (SDL_GetAppState() & SDL_APPMOUSEFOCUS)
  270. # endif
  271. {
  272. SDL_GetMouseState(&ret.x, &ret.y);
  273. ret.y = Video::GetSize().y - 1 - ret.y;
  274. }
  275. #endif
  276. return ret;
  277. }
  278. void SdlInputData::SetMousePos(ivec2 position)
  279. {
  280. #if USE_SDL
  281. // FIXME: how do I warped mouse?
  282. #elif USE_OLD_SDL
  283. SDL_WarpMouse((uint16_t)position.x, (uint16_t)position.y);
  284. #else
  285. UNUSED(position);
  286. #endif
  287. }
  288. } /* namespace lol */