471 lines
14 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2016 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 || LOL_USE_OLD_SDL
  14. # if HAVE_SDL2_SDL_H
  15. # include <SDL2/SDL.h>
  16. # elif HAVE_SDL_SDL_H
  17. # include <SDL/SDL.h>
  18. # else
  19. # include <SDL.h>
  20. # endif
  21. #endif
  22. #include "sdlinput.h"
  23. #include "input/input_internal.h"
  24. /* We force joystick polling because no events are received when
  25. * there is no SDL display (eg. on the Raspberry Pi). */
  26. #define SDL_FORCE_POLL_JOYSTICK 1
  27. #if EMSCRIPTEN
  28. # define MOUSE_SPEED_MOD 10.f
  29. #else
  30. # define MOUSE_SPEED_MOD 100.f
  31. #endif
  32. namespace lol
  33. {
  34. #if LOL_USE_OLD_SDL
  35. /* Quick and dirty for now... This is deprecated anyway. */
  36. static int sdl12_to_scancode(int ch, int sc)
  37. {
  38. if (ch >= 'a' && ch <= 'z')
  39. ch = ch - 'a' + 'A';
  40. # define _SC(id, str, name) if (ch == str[0]) return id;
  41. # include "input/keys.h"
  42. return 0;
  43. }
  44. #else
  45. //-------------------------------------------------------------------------
  46. # define _SC(id, str, name) static const uint16_t SDLOL_##name = id;
  47. # include "input/keys.h"
  48. //-------------------------------------------------------------------------
  49. static bool ScanCodeIsValid(int sc)
  50. {
  51. switch (sc)
  52. {
  53. # define _SC(id, str, name) \
  54. case id: return true;
  55. # include "input/keys.h"
  56. default: return false;
  57. }
  58. return false;
  59. }
  60. //-------------------------------------------------------------------------
  61. static String ScanCodeToText(int sc)
  62. {
  63. switch (sc)
  64. {
  65. # define _SC(id, str, name) \
  66. case id: return String(str);
  67. # include "input/keys.h"
  68. default:
  69. msg::error("ScanCodeToText unknown scancode %0d\n", sc);
  70. }
  71. return String();
  72. }
  73. //-------------------------------------------------------------------------
  74. static String ScanCodeToName(int sc)
  75. {
  76. switch (sc)
  77. {
  78. # define _SC(id, str, name) \
  79. case id: return String(#name);
  80. # include "input/keys.h"
  81. default:
  82. msg::error("ScanCodeToText unknown scancode %0d\n", sc);
  83. }
  84. return String();
  85. }
  86. #endif
  87. /*
  88. * SDL Input implementation class
  89. */
  90. class SdlInputData
  91. {
  92. friend class SdlInput;
  93. private:
  94. void Tick(float seconds);
  95. static ivec2 GetMousePos();
  96. static void SetMousePos(ivec2 position);
  97. SdlInputData(int app_w, int app_h, int screen_w, int screen_h)
  98. : m_mouse(nullptr),
  99. m_keyboard(nullptr),
  100. m_prevmouse(ivec2::zero),
  101. m_app(vec2((float)app_w, (float)app_h)),
  102. m_screen(vec2((float)screen_w, (float)screen_h)),
  103. m_mousecapture(false),
  104. m_tick_in_draw_thread(false)
  105. { }
  106. #if LOL_USE_SDL || LOL_USE_OLD_SDL
  107. array<SDL_Joystick *, InputDeviceInternal *> m_joysticks;
  108. InputDeviceInternal *m_mouse;
  109. InputDeviceInternal *m_keyboard;
  110. #endif // LOL_USE_SDL
  111. ivec2 m_prevmouse;
  112. vec2 m_app;
  113. vec2 m_screen;
  114. bool m_mousecapture;
  115. bool m_tick_in_draw_thread;
  116. };
  117. /*
  118. * Public SdlInput class
  119. */
  120. SdlInput::SdlInput(int app_w, int app_h, int screen_w, int screen_h)
  121. : m_data(new SdlInputData(app_w, app_h, screen_w, screen_h))
  122. {
  123. #if _WIN32
  124. m_data->m_tick_in_draw_thread = true;
  125. #endif
  126. #if LOL_USE_OLD_SDL
  127. /* Enable Unicode translation of keyboard events */
  128. SDL_EnableUNICODE(1);
  129. #endif
  130. #if LOL_USE_SDL || LOL_USE_OLD_SDL
  131. SDL_Init(SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
  132. #endif
  133. m_data->m_keyboard = InputDeviceInternal::CreateStandardKeyboard();
  134. m_data->m_mouse = InputDeviceInternal::CreateStandardMouse();
  135. #if LOL_USE_SDL || LOL_USE_OLD_SDL
  136. # if !EMSCRIPTEN
  137. # if SDL_FORCE_POLL_JOYSTICK
  138. SDL_JoystickEventState(SDL_QUERY);
  139. # else
  140. SDL_JoystickEventState(SDL_ENABLE);
  141. # endif //SDL_FORCE_POLL_JOYSTICK
  142. /* Register all the joysticks we can find, and let the input
  143. * system decide what it wants to track. */
  144. for (int i = 0; i < SDL_NumJoysticks(); i++)
  145. {
  146. SDL_Joystick *sdlstick = SDL_JoystickOpen(i);
  147. /* Blacklist some devices:
  148. * - HDAPS, it's not a real joystick.
  149. * - X360 controllers, Xinput handles them better since
  150. * it won't think there is only one trigger axis. */
  151. # if LOL_USE_SDL
  152. char const *name = SDL_JoystickName(sdlstick);
  153. # elif LOL_USE_OLD_SDL
  154. char const *name = SDL_JoystickName(i);
  155. # endif
  156. if (strstr(name, "HDAPS")
  157. # if LOL_USE_XINPUT
  158. || strstr(name, "XBOX 360 For Windows")
  159. # endif //LOL_USE_XINPUT
  160. || false)
  161. {
  162. SDL_JoystickClose(sdlstick);
  163. continue;
  164. }
  165. //String::format("Joystick%d", i + 1).C()
  166. InputDeviceInternal* stick = new InputDeviceInternal(g_name_joystick(i + 1));
  167. for (int j = 0; j < SDL_JoystickNumAxes(sdlstick); ++j)
  168. stick->AddAxis(String::format("Axis%d", j + 1).C());
  169. for (int j = 0; j < SDL_JoystickNumButtons(sdlstick); ++j)
  170. stick->AddKey(String::format("Button%d", j + 1).C());
  171. m_data->m_joysticks.push(sdlstick, stick);
  172. }
  173. # endif //EMSCRIPTEN
  174. #endif
  175. m_gamegroup = GAMEGROUP_INPUT;
  176. }
  177. SdlInput::~SdlInput()
  178. {
  179. #if (LOL_USE_SDL || LOL_USE_OLD_SDL) && !EMSCRIPTEN
  180. /* Unregister all the joysticks we added */
  181. while (m_data->m_joysticks.count())
  182. {
  183. SDL_JoystickClose(m_data->m_joysticks[0].m1);
  184. delete m_data->m_joysticks[0].m2;
  185. m_data->m_joysticks.remove(0);
  186. }
  187. #endif
  188. delete m_data;
  189. }
  190. void SdlInput::TickGame(float seconds)
  191. {
  192. Entity::TickGame(seconds);
  193. if (!m_data->m_tick_in_draw_thread)
  194. m_data->Tick(seconds);
  195. }
  196. void SdlInput::TickDraw(float seconds, Scene &scene)
  197. {
  198. Entity::TickDraw(seconds, scene);
  199. if (m_data->m_tick_in_draw_thread)
  200. m_data->Tick(seconds);
  201. }
  202. void SdlInputData::Tick(float seconds)
  203. {
  204. #if LOL_USE_SDL || LOL_USE_OLD_SDL
  205. /* Pump all joystick events because no event is coming to us. */
  206. # if SDL_FORCE_POLL_JOYSTICK && !EMSCRIPTEN
  207. SDL_JoystickUpdate();
  208. for (int j = 0; j < m_joysticks.count(); j++)
  209. {
  210. for (int i = 0; i < SDL_JoystickNumButtons(m_joysticks[j].m1); i++)
  211. m_joysticks[j].m2->SetKey(i, SDL_JoystickGetButton(m_joysticks[j].m1, i) != 0);
  212. for (int i = 0; i < SDL_JoystickNumAxes(m_joysticks[j].m1); i++)
  213. m_joysticks[j].m2->SetAxis(i, (float)SDL_JoystickGetAxis(m_joysticks[j].m1, i) / 32768.f);
  214. }
  215. # endif
  216. m_mouse->SetAxis(4, 0);
  217. # if !LOL_USE_OLD_SDL
  218. if (m_keyboard->IsTextInputActive())
  219. SDL_StartTextInput();
  220. else
  221. SDL_StopTextInput();
  222. # endif
  223. /* Handle keyboard and WM events */
  224. SDL_Event event;
  225. while (SDL_PollEvent(&event))
  226. {
  227. switch (event.type)
  228. {
  229. case SDL_QUIT:
  230. Ticker::Shutdown();
  231. break;
  232. case SDL_KEYDOWN:
  233. case SDL_KEYUP:
  234. # if LOL_USE_OLD_SDL
  235. switch (int sc = sdl12_to_scancode(event.key.keysym.sym,
  236. event.key.keysym.scancode))
  237. # else
  238. switch (int sc = event.key.keysym.scancode)
  239. # endif
  240. {
  241. //Lock management
  242. case SDLOL_CapsLock:
  243. case SDLOL_ScrollLock:
  244. case SDLOL_NumLockClear:
  245. # if defined SDLOL_CapsLock && defined SDLOL_ScrollLock && defined SDLOL_NumLockClear
  246. //Update status on key down only
  247. if (event.type == SDL_KEYDOWN)
  248. {
  249. int sc2 = sc;
  250. switch (sc)
  251. {
  252. case SDLOL_CapsLock:
  253. sc2 = SDLOL_CapsLockStatus;
  254. break;
  255. case SDLOL_ScrollLock:
  256. sc2 = SDLOL_ScrollLockStatus;
  257. break;
  258. case SDLOL_NumLockClear:
  259. sc2 = SDLOL_NumLockClearStatus;
  260. break;
  261. }
  262. m_keyboard->SetKey(sc2, !m_keyboard->GetKey(sc2));
  263. /* DEBUG STUFF
  264. msg::info("Repeat: 0x%02x : %s/%s/%s/%i\n",
  265. (int)m_keyboard, ScanCodeToText(sc2).C(), ScanCodeToName(sc2).C(),
  266. m_keyboard->GetKey(sc2) ? "up" : "down", event.key.repeat);
  267. */
  268. }
  269. # endif
  270. default:
  271. # if LOL_USE_OLD_SDL
  272. m_keyboard->SetKey(sc ? sc : event.key.keysym.scancode,
  273. event.type == SDL_KEYDOWN);
  274. # else
  275. if (ScanCodeIsValid(sc))
  276. {
  277. //Set key updates the corresponding key
  278. m_keyboard->SetKey(sc, event.type == SDL_KEYDOWN);
  279. /* DEBUG STUFF
  280. msg::info("Repeat: 0x%02x : %s/%s/%s/%i\n",
  281. (int)m_keyboard, ScanCodeToText(sc).C(), ScanCodeToName(sc).C(),
  282. event.type == SDL_KEYDOWN ? "up" : "down", event.key.repeat);
  283. */
  284. }
  285. /* DEBUG STUFF
  286. else
  287. msg::error("unknown keypress (sym 0x%02x, scancode %0d)\n",
  288. event.key.keysym.sym, event.key.keysym.scancode);
  289. */
  290. # endif
  291. }
  292. break;
  293. # if !LOL_USE_OLD_SDL
  294. //case SDL_TEXTEDITING: //TODO: handle that ?
  295. case SDL_TEXTINPUT:
  296. m_keyboard->AddText(event.text.text);
  297. break;
  298. # endif
  299. # if LOL_USE_OLD_SDL
  300. case SDL_MOUSEBUTTONDOWN:
  301. case SDL_MOUSEBUTTONUP:
  302. if (event.button.button != SDL_BUTTON_WHEELUP && event.button.button != SDL_BUTTON_WHEELDOWN)
  303. m_mouse->SetKey(event.button.button - 1, event.type == SDL_MOUSEBUTTONDOWN);
  304. else
  305. m_mouse->SetAxis(4, (event.button.button != SDL_BUTTON_WHEELUP) ? (1) : (-1));
  306. break;
  307. # else
  308. case SDL_MOUSEBUTTONDOWN:
  309. case SDL_MOUSEBUTTONUP:
  310. //event.button.which
  311. m_mouse->SetKey(event.button.button - 1, event.type == SDL_MOUSEBUTTONDOWN);
  312. break;
  313. case SDL_MOUSEWHEEL:
  314. m_mouse->SetAxis(4, (float)event.button.y);
  315. break;
  316. case SDL_WINDOWEVENT:
  317. {
  318. switch (event.window.event)
  319. {
  320. case SDL_WINDOWEVENT_ENTER:
  321. case SDL_WINDOWEVENT_FOCUS_GAINED:
  322. m_mouse->SetKey(3, true);
  323. break;
  324. case SDL_WINDOWEVENT_LEAVE:
  325. case SDL_WINDOWEVENT_FOCUS_LOST:
  326. m_mouse->SetKey(3, false);
  327. break;
  328. }
  329. break;
  330. }
  331. # endif
  332. # if !SDL_FORCE_POLL_JOYSTICK
  333. case SDL_JOYAXISMOTION:
  334. m_joysticks[event.jaxis.which].m2->SetAxis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
  335. break;
  336. case SDL_JOYBUTTONUP:
  337. case SDL_JOYBUTTONDOWN:
  338. m_joysticks[event.jbutton.which].m2->SetKey(event.jbutton.button, event.jbutton.state);
  339. break;
  340. # endif
  341. }
  342. }
  343. /* Handle mouse input */
  344. ivec2 mouse = SdlInputData::GetMousePos();
  345. if (InputDeviceInternal::GetMouseCapture() != m_mousecapture)
  346. {
  347. m_mousecapture = InputDeviceInternal::GetMouseCapture();
  348. # if LOL_USE_SDL
  349. SDL_SetRelativeMouseMode(m_mousecapture ? SDL_TRUE : SDL_FALSE);
  350. # elif LOL_USE_OLD_SDL
  351. SDL_WM_GrabInput(m_mousecapture ? SDL_GRAB_ON : SDL_GRAB_OFF);
  352. # endif
  353. mouse = (ivec2)m_app / 2;
  354. SdlInputData::SetMousePos(mouse);
  355. //SDL_ShowCursor(m_mousecapture ? SDL_DISABLE : SDL_ENABLE);
  356. }
  357. if (mouse.x >= 0 && mouse.x < m_app.x && mouse.y >= 0 && mouse.y < m_app.y)
  358. {
  359. //We need the max if we want coherent mouse speed between axis
  360. float max_screen_size = lol::max(m_screen.x, m_screen.y);
  361. vec2 vmouse = vec2(mouse);
  362. vec2 vprevmouse = vec2(m_prevmouse);
  363. m_mouse->SetCursor(0, vmouse / m_app, mouse);
  364. // Note: 100.0f is an arbitrary value that makes it feel about the same than an xbox controller joystick
  365. m_mouse->SetAxis(0, (mouse.x - vprevmouse.x) * MOUSE_SPEED_MOD / max_screen_size);
  366. // Y Axis is also negated to match the usual joystick Y axis (negatives values are for the upper direction)
  367. m_mouse->SetAxis(1,-(mouse.y - vprevmouse.y) * MOUSE_SPEED_MOD / max_screen_size);
  368. //Pixel movement
  369. m_mouse->SetAxis(2, (mouse.x - vprevmouse.x));
  370. m_mouse->SetAxis(3,-(mouse.y - vprevmouse.y));
  371. }
  372. //Mouse is focused, Validate the InScreen Key
  373. //Hardcoded 3, not very nice.
  374. # if !EMSCRIPTEN && LOL_USE_OLD_SDL
  375. m_mouse->SetKey(3, !!(SDL_GetAppState() & SDL_APPMOUSEFOCUS));
  376. # else
  377. //Handled in PollEvent
  378. # endif
  379. if (m_mousecapture)
  380. {
  381. mouse = ivec2(m_app * .5f);
  382. SdlInputData::SetMousePos(mouse);
  383. }
  384. m_prevmouse = mouse;
  385. #else
  386. UNUSED(seconds);
  387. #endif //LOL_USE_SDL
  388. }
  389. // NOTE: these two functions are pointless now and could be inlined directly
  390. ivec2 SdlInputData::GetMousePos()
  391. {
  392. ivec2 ret(-1, -1);
  393. #if LOL_USE_SDL || LOL_USE_OLD_SDL
  394. # if !EMSCRIPTEN && LOL_USE_OLD_SDL
  395. if (SDL_GetAppState() & SDL_APPMOUSEFOCUS)
  396. # endif
  397. {
  398. SDL_GetMouseState(&ret.x, &ret.y);
  399. ret.y = Video::GetSize().y - 1 - ret.y;
  400. }
  401. #endif
  402. return ret;
  403. }
  404. void SdlInputData::SetMousePos(ivec2 position)
  405. {
  406. #if LOL_USE_SDL
  407. // FIXME: how do I warped mouse?
  408. #elif LOL_USE_OLD_SDL
  409. SDL_WarpMouse((uint16_t)position.x, (uint16_t)position.y);
  410. #else
  411. UNUSED(position);
  412. #endif
  413. }
  414. } /* namespace lol */