💿🐜 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.

340 lines
9.3 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 <engine/app/sdl/sdl-input-manager.hpp>
  20. #include <engine/input/application-events.hpp>
  21. #include <engine/debug/log.hpp>
  22. #include <engine/math/map.hpp>
  23. #include <SDL2/SDL.h>
  24. #include <stdexcept>
  25. namespace app {
  26. sdl_input_manager::sdl_input_manager()
  27. {
  28. // Init SDL joystick and controller subsystems
  29. debug::log::trace("Initializing SDL joystick and controller subsystems...");
  30. if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
  31. {
  32. debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError());
  33. throw std::runtime_error("Failed to initialize SDL joystick and controller subsytems");
  34. }
  35. else
  36. {
  37. debug::log::trace("Initialized SDL joystick and controller subsystems");
  38. }
  39. // Register keyboard and mouse
  40. register_keyboard(keyboard);
  41. register_mouse(mouse);
  42. // Generate keyboard and mouse device connected events
  43. keyboard.connect();
  44. mouse.connect();
  45. }
  46. sdl_input_manager::~sdl_input_manager()
  47. {
  48. // Quit SDL joystick and controller subsystems
  49. debug::log::trace("Quitting SDL joystick and controller subsystems...");
  50. SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
  51. debug::log::trace("Quit SDL joystick and controller subsystems...");
  52. }
  53. void sdl_input_manager::update()
  54. {
  55. // Active modifier keys
  56. std::uint16_t sdl_key_mod = KMOD_NONE;
  57. std::uint16_t modifier_keys = input::modifier_key::none;
  58. // Gather SDL events from event queue
  59. SDL_PumpEvents();
  60. // Handle OS events
  61. for (;;)
  62. {
  63. // Get next display or window event
  64. SDL_Event event;
  65. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LOCALECHANGED);
  66. if (!status)
  67. {
  68. break;
  69. }
  70. else if (status < 0)
  71. {
  72. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  73. throw std::runtime_error("Failed to peep SDL events");
  74. }
  75. switch (event.type)
  76. {
  77. case SDL_QUIT:
  78. debug::log::debug("Application quit requested");
  79. this->event_queue.enqueue<input::application_quit_event>({});
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. // Handle keyboard, mouse, and gamepad events
  86. for (;;)
  87. {
  88. // Get next display or window event
  89. SDL_Event event;
  90. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_LASTEVENT);
  91. if (!status)
  92. {
  93. break;
  94. }
  95. else if (status < 0)
  96. {
  97. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  98. throw std::runtime_error("Failed to peep SDL events");
  99. }
  100. switch (event.type)
  101. {
  102. [[likely]] case SDL_MOUSEMOTION:
  103. {
  104. mouse.move({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel});
  105. break;
  106. }
  107. [[likely]] case SDL_KEYDOWN:
  108. case SDL_KEYUP:
  109. {
  110. // Get scancode of key
  111. const input::scancode scancode = static_cast<input::scancode>(event.key.keysym.scancode);
  112. // Rebuild modifier keys bit mask
  113. if (sdl_key_mod != event.key.keysym.mod)
  114. {
  115. sdl_key_mod = event.key.keysym.mod;
  116. modifier_keys = input::modifier_key::none;
  117. if (sdl_key_mod & KMOD_LSHIFT)
  118. modifier_keys |= input::modifier_key::left_shift;
  119. if (sdl_key_mod & KMOD_RSHIFT)
  120. modifier_keys |= input::modifier_key::right_shift;
  121. if (sdl_key_mod & KMOD_LCTRL)
  122. modifier_keys |= input::modifier_key::left_ctrl;
  123. if (sdl_key_mod & KMOD_RCTRL)
  124. modifier_keys |= input::modifier_key::right_ctrl;
  125. if (sdl_key_mod & KMOD_LALT)
  126. modifier_keys |= input::modifier_key::left_alt;
  127. if (sdl_key_mod & KMOD_RALT)
  128. modifier_keys |= input::modifier_key::right_alt;
  129. if (sdl_key_mod & KMOD_LGUI)
  130. modifier_keys |= input::modifier_key::left_gui;
  131. if (sdl_key_mod & KMOD_RGUI)
  132. modifier_keys |= input::modifier_key::right_gui;
  133. if (sdl_key_mod & KMOD_NUM)
  134. modifier_keys |= input::modifier_key::num_lock;
  135. if (sdl_key_mod & KMOD_CAPS)
  136. modifier_keys |= input::modifier_key::caps_lock;
  137. if (sdl_key_mod & KMOD_SCROLL)
  138. modifier_keys |= input::modifier_key::scroll_lock;
  139. if (sdl_key_mod & KMOD_MODE)
  140. modifier_keys |= input::modifier_key::alt_gr;
  141. }
  142. if (event.type == SDL_KEYDOWN)
  143. {
  144. keyboard.press(scancode, modifier_keys, (event.key.repeat > 0));
  145. }
  146. else
  147. {
  148. keyboard.release(scancode, modifier_keys);
  149. }
  150. break;
  151. }
  152. case SDL_MOUSEWHEEL:
  153. {
  154. const float flip = (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f;
  155. mouse.scroll({event.wheel.preciseX * flip, event.wheel.preciseY * flip});
  156. break;
  157. }
  158. case SDL_MOUSEBUTTONDOWN:
  159. {
  160. mouse.press(static_cast<input::mouse_button>(event.button.button));
  161. break;
  162. }
  163. case SDL_MOUSEBUTTONUP:
  164. {
  165. mouse.release(static_cast<input::mouse_button>(event.button.button));
  166. break;
  167. }
  168. [[likely]] case SDL_CONTROLLERAXISMOTION:
  169. {
  170. if (event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID)
  171. {
  172. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  173. {
  174. // Map axis position onto `[-1, 1]`.
  175. const float position = math::map
  176. (
  177. static_cast<float>(event.caxis.value),
  178. static_cast<float>(std::numeric_limits<decltype(event.caxis.value)>::min()),
  179. static_cast<float>(std::numeric_limits<decltype(event.caxis.value)>::max()),
  180. -1.0f,
  181. 1.0f
  182. );
  183. // Generate gamepad axis moved event
  184. it->second->move(static_cast<input::gamepad_axis>(event.caxis.axis), position);
  185. }
  186. }
  187. break;
  188. }
  189. case SDL_CONTROLLERBUTTONDOWN:
  190. {
  191. if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  192. {
  193. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  194. {
  195. it->second->press(static_cast<input::gamepad_button>(event.cbutton.button));
  196. }
  197. }
  198. break;
  199. }
  200. case SDL_CONTROLLERBUTTONUP:
  201. {
  202. if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  203. {
  204. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  205. {
  206. it->second->release(static_cast<input::gamepad_button>(event.cbutton.button));
  207. }
  208. }
  209. break;
  210. }
  211. [[unlikely]] case SDL_CONTROLLERDEVICEADDED:
  212. {
  213. if (SDL_IsGameController(event.cdevice.which))
  214. {
  215. SDL_GameController* sdl_controller = SDL_GameControllerOpen(event.cdevice.which);
  216. if (sdl_controller)
  217. {
  218. // Get gamepad name
  219. const char* controller_name = SDL_GameControllerNameForIndex(event.cdevice.which);
  220. if (!controller_name)
  221. {
  222. controller_name = "";
  223. }
  224. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  225. {
  226. // Gamepad reconnected
  227. debug::log::info("Reconnected gamepad {}", event.cdevice.which);
  228. it->second->connect();
  229. }
  230. else
  231. {
  232. // Get gamepad GUID
  233. SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller);
  234. SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick);
  235. // Copy into UUID struct
  236. ::uuid gamepad_uuid;
  237. std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size());
  238. debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string());
  239. // Create new gamepad
  240. input::gamepad* gamepad = new input::gamepad();
  241. gamepad->set_uuid(gamepad_uuid);
  242. // Add gamepad to gamepad map
  243. gamepad_map[event.cdevice.which] = gamepad;
  244. // Register gamepad
  245. register_device(*gamepad);
  246. // Generate gamepad connected event
  247. gamepad->connect();
  248. }
  249. }
  250. else
  251. {
  252. debug::log::error("Failed to connect gamepad {}: {}", event.cdevice.which, SDL_GetError());
  253. SDL_ClearError();
  254. }
  255. }
  256. break;
  257. }
  258. [[unlikely]] case SDL_CONTROLLERDEVICEREMOVED:
  259. {
  260. SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(event.cdevice.which);
  261. if (sdl_controller)
  262. {
  263. SDL_GameControllerClose(sdl_controller);
  264. if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end())
  265. {
  266. it->second->disconnect();
  267. }
  268. debug::log::info("Disconnected gamepad {}", event.cdevice.which);
  269. }
  270. break;
  271. }
  272. default:
  273. break;
  274. }
  275. }
  276. // Flush event queue
  277. this->event_queue.flush();
  278. }
  279. void sdl_input_manager::show_cursor()
  280. {
  281. if (SDL_ShowCursor(SDL_ENABLE) < 0)
  282. {
  283. debug::log::error("Failed to show cursor: \"{}\"", SDL_GetError());
  284. SDL_ClearError();
  285. }
  286. }
  287. void sdl_input_manager::hide_cursor()
  288. {
  289. if (SDL_ShowCursor(SDL_DISABLE) < 0)
  290. {
  291. debug::log::error("Failed to hide cursor: \"{}\"", SDL_GetError());
  292. SDL_ClearError();
  293. }
  294. }
  295. } // namespace app