💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
8.7 KiB

  1. /*
  2. * Copyright (C) 2023 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "app/sdl/sdl-input-manager.hpp"
  20. #include "debug/log.hpp"
  21. #include "math/map.hpp"
  22. #include <SDL2/SDL.h>
  23. #include <stdexcept>
  24. namespace app {
  25. sdl_input_manager::sdl_input_manager()
  26. {
  27. // Init SDL joystick and controller subsystems
  28. debug::log::trace("Initializing SDL joystick and controller subsystems...");
  29. if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
  30. {
  31. debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError());
  32. throw std::runtime_error("Failed to initialize SDL joystick and controller subsytems");
  33. }
  34. else
  35. {
  36. debug::log::trace("Initialized SDL joystick and controller subsystems");
  37. }
  38. // Register keyboard and mouse
  39. register_keyboard(keyboard);
  40. register_mouse(mouse);
  41. // Generate keyboard and mouse device connected events
  42. keyboard.connect();
  43. mouse.connect();
  44. }
  45. sdl_input_manager::~sdl_input_manager()
  46. {
  47. // Quit SDL joystick and controller subsystems
  48. debug::log::trace("Quitting SDL joystick and controller subsystems...");
  49. SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
  50. debug::log::trace("Quit SDL joystick and controller subsystems...");
  51. }
  52. void sdl_input_manager::update()
  53. {
  54. // Active modifier keys
  55. std::uint16_t sdl_key_mod = KMOD_NONE;
  56. std::uint16_t modifier_keys = input::modifier_key::none;
  57. // Gather SDL events from event queue
  58. SDL_PumpEvents();
  59. // Handle OS events
  60. for (;;)
  61. {
  62. // Get next display or window event
  63. SDL_Event event;
  64. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LOCALECHANGED);
  65. if (!status)
  66. {
  67. break;
  68. }
  69. else if (status < 0)
  70. {
  71. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  72. throw std::runtime_error("Failed to peep SDL events");
  73. }
  74. switch (event.type)
  75. {
  76. case SDL_QUIT:
  77. debug::log::debug("Application quit requested");
  78. this->event_queue.enqueue<input::application_quit_event>({});
  79. break;
  80. default:
  81. break;
  82. }
  83. }
  84. // Handle keyboard, mouse, and gamepad events
  85. for (;;)
  86. {
  87. // Get next display or window event
  88. SDL_Event event;
  89. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_LASTEVENT);
  90. if (!status)
  91. {
  92. break;
  93. }
  94. else if (status < 0)
  95. {
  96. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  97. throw std::runtime_error("Failed to peep SDL events");
  98. }
  99. switch (event.type)
  100. {
  101. [[likely]] case SDL_MOUSEMOTION:
  102. {
  103. mouse.move({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel});
  104. break;
  105. }
  106. case SDL_KEYDOWN:
  107. case SDL_KEYUP:
  108. {
  109. // Get scancode of key
  110. const input::scancode scancode = static_cast<input::scancode>(event.key.keysym.scancode);
  111. // Rebuild modifier keys bit mask
  112. if (event.key.keysym.mod != sdl_key_mod)
  113. {
  114. sdl_key_mod = event.key.keysym.mod;
  115. modifier_keys = input::modifier_key::none;
  116. if (sdl_key_mod & KMOD_LSHIFT)
  117. modifier_keys |= input::modifier_key::left_shift;
  118. if (sdl_key_mod & KMOD_RSHIFT)
  119. modifier_keys |= input::modifier_key::right_shift;
  120. if (sdl_key_mod & KMOD_LCTRL)
  121. modifier_keys |= input::modifier_key::left_ctrl;
  122. if (sdl_key_mod & KMOD_RCTRL)
  123. modifier_keys |= input::modifier_key::right_ctrl;
  124. if (sdl_key_mod & KMOD_LGUI)
  125. modifier_keys |= input::modifier_key::left_gui;
  126. if (sdl_key_mod & KMOD_RGUI)
  127. modifier_keys |= input::modifier_key::right_gui;
  128. if (sdl_key_mod & KMOD_NUM)
  129. modifier_keys |= input::modifier_key::num_lock;
  130. if (sdl_key_mod & KMOD_CAPS)
  131. modifier_keys |= input::modifier_key::caps_lock;
  132. if (sdl_key_mod & KMOD_SCROLL)
  133. modifier_keys |= input::modifier_key::scroll_lock;
  134. if (sdl_key_mod & KMOD_MODE)
  135. modifier_keys |= input::modifier_key::alt_gr;
  136. }
  137. // Determine if event was generated from a key repeat
  138. const bool repeat = event.key.repeat > 0;
  139. if (event.type == SDL_KEYDOWN)
  140. {
  141. keyboard.press(scancode, repeat, modifier_keys);
  142. }
  143. else
  144. {
  145. keyboard.release(scancode, repeat, modifier_keys);
  146. }
  147. break;
  148. }
  149. case SDL_MOUSEWHEEL:
  150. {
  151. const float flip = (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f;
  152. mouse.scroll({event.wheel.preciseX * flip, event.wheel.preciseY * flip});
  153. break;
  154. }
  155. case SDL_MOUSEBUTTONDOWN:
  156. {
  157. mouse.press(static_cast<input::mouse_button>(event.button.button));
  158. break;
  159. }
  160. case SDL_MOUSEBUTTONUP:
  161. {
  162. mouse.release(static_cast<input::mouse_button>(event.button.button));
  163. break;
  164. }
  165. [[likely]] case SDL_CONTROLLERAXISMOTION:
  166. {
  167. if (event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID)
  168. {
  169. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  170. {
  171. // Map axis position onto `[-1, 1]`.
  172. const float position = math::map
  173. (
  174. static_cast<float>(event.caxis.value),
  175. static_cast<float>(std::numeric_limits<decltype(event.caxis.value)>::min()),
  176. static_cast<float>(std::numeric_limits<decltype(event.caxis.value)>::max()),
  177. -1.0f,
  178. 1.0f
  179. );
  180. // Generate gamepad axis moved event
  181. it->second->move(static_cast<input::gamepad_axis>(event.caxis.axis), position);
  182. }
  183. }
  184. break;
  185. }
  186. case SDL_CONTROLLERBUTTONDOWN:
  187. {
  188. if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  189. {
  190. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  191. {
  192. it->second->press(static_cast<input::gamepad_button>(event.cbutton.button));
  193. }
  194. }
  195. break;
  196. }
  197. case SDL_CONTROLLERBUTTONUP:
  198. {
  199. if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  200. {
  201. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  202. {
  203. it->second->release(static_cast<input::gamepad_button>(event.cbutton.button));
  204. }
  205. }
  206. break;
  207. }
  208. [[unlikely]] case SDL_CONTROLLERDEVICEADDED:
  209. {
  210. if (SDL_IsGameController(event.cdevice.which))
  211. {
  212. SDL_GameController* sdl_controller = SDL_GameControllerOpen(event.cdevice.which);
  213. const char* controller_name = SDL_GameControllerNameForIndex(event.cdevice.which);
  214. if (sdl_controller)
  215. {
  216. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  217. {
  218. // Gamepad reconnected
  219. debug::log::info("Reconnected gamepad \"{}\"", controller_name);
  220. it->second->connect();
  221. }
  222. else
  223. {
  224. // Get gamepad GUID
  225. SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller);
  226. SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick);
  227. // Copy into UUID struct
  228. ::uuid gamepad_uuid;
  229. std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size());
  230. debug::log::info("Connected gamepad \"{}\" with UUID {}", controller_name, gamepad_uuid.string());
  231. // Create new gamepad
  232. input::gamepad* gamepad = new input::gamepad();
  233. gamepad->set_uuid(gamepad_uuid);
  234. // Add gamepad to gamepad map
  235. gamepad_map[event.cdevice.which] = gamepad;
  236. // Register gamepad
  237. register_device(*gamepad);
  238. // Generate gamepad connected event
  239. gamepad->connect();
  240. }
  241. }
  242. else
  243. {
  244. debug::log::error("Failed to connected gamepad \"{}\": {}", controller_name, SDL_GetError());
  245. }
  246. }
  247. break;
  248. }
  249. [[unlikely]] case SDL_CONTROLLERDEVICEREMOVED:
  250. {
  251. SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(event.cdevice.which);
  252. if (sdl_controller)
  253. {
  254. const char* controller_name = SDL_GameControllerNameForIndex(event.cdevice.which);
  255. SDL_GameControllerClose(sdl_controller);
  256. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  257. {
  258. it->second->disconnect();
  259. }
  260. debug::log::info("Disconnected gamepad \"{}\"", controller_name);
  261. }
  262. break;
  263. }
  264. default:
  265. break;
  266. }
  267. }
  268. // Flush event queue
  269. this->event_queue.flush();
  270. }
  271. } // namespace app