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

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