event.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #include "event.h"
  2. #include "simple/sdlcore/utils.hpp"
  3. using simple::geom::vector;
  4. namespace simple::interactive
  5. {
  6. template <typename T, size_t N, size_t... I>
  7. constexpr auto copy_array_n(T(&arr)[N], std::index_sequence<I...>)
  8. {
  9. return std::array{arr[I]...};
  10. }
  11. template <typename T, size_t N>
  12. constexpr auto to_array(T(&arr)[N])
  13. {
  14. return copy_array_n(arr, std::make_index_sequence<N>{});
  15. }
  16. template <typename WindowEvent>
  17. auto make_window_event(const SDL_Event& event)
  18. {
  19. return WindowEvent
  20. {
  21. std::chrono::milliseconds(event.window.timestamp),
  22. event.window.windowID,
  23. };
  24. }
  25. template <typename WindowEvent>
  26. auto make_window_vector_event(const SDL_Event& event)
  27. {
  28. return WindowEvent
  29. {
  30. std::chrono::milliseconds(event.window.timestamp),
  31. event.window.windowID,
  32. vector{event.window.data1, event.window.data2}
  33. };
  34. }
  35. std::optional<event> next_event() noexcept
  36. {
  37. SDL_Event event;
  38. while(SDL_PollEvent(&event))
  39. {
  40. switch(event.type)
  41. {
  42. case SDL_KEYDOWN:
  43. return key_pressed
  44. {
  45. std::chrono::milliseconds(event.key.timestamp),
  46. event.key.windowID,
  47. static_cast<keycode>(event.key.keysym.sym),
  48. static_cast<scancode>(event.key.keysym.scancode),
  49. static_cast<keystate>(event.key.state),
  50. event.key.repeat
  51. };
  52. case SDL_KEYUP:
  53. return key_released
  54. {
  55. std::chrono::milliseconds(event.key.timestamp),
  56. event.key.windowID,
  57. static_cast<keycode>(event.key.keysym.sym),
  58. static_cast<scancode>(event.key.keysym.scancode),
  59. static_cast<keystate>(event.key.state),
  60. event.key.repeat
  61. };
  62. case SDL_MOUSEBUTTONDOWN:
  63. return mouse_down
  64. {
  65. std::chrono::milliseconds(event.button.timestamp),
  66. event.button.windowID,
  67. event.button.which,
  68. vector{event.button.x, event.button.y},
  69. static_cast<mouse_button>(event.button.button),
  70. static_cast<keystate>(event.button.state),
  71. event.button.clicks
  72. };
  73. case SDL_MOUSEBUTTONUP:
  74. return mouse_up
  75. {
  76. std::chrono::milliseconds(event.button.timestamp),
  77. event.button.windowID,
  78. event.button.which,
  79. vector{event.button.x, event.button.y},
  80. static_cast<mouse_button>(event.button.button),
  81. static_cast<keystate>(event.button.state),
  82. #if SDL_VERSION_ATLEAST(2,0,2)
  83. event.button.clicks
  84. #endif
  85. };
  86. case SDL_MOUSEMOTION:
  87. return mouse_motion
  88. {
  89. std::chrono::milliseconds(event.motion.timestamp),
  90. event.motion.windowID,
  91. event.motion.which,
  92. vector{event.motion.x, event.motion.y},
  93. vector{event.motion.xrel, event.motion.yrel},
  94. static_cast<mouse_button_mask>(event.motion.state),
  95. };
  96. case SDL_MOUSEWHEEL:
  97. return mouse_wheel
  98. {
  99. std::chrono::milliseconds(event.wheel.timestamp),
  100. event.wheel.windowID,
  101. event.wheel.which,
  102. vector{event.wheel.x, event.wheel.y},
  103. #if SDL_VERSION_ATLEAST(2,0,4)
  104. static_cast<wheel_direction>(event.wheel.direction),
  105. #endif
  106. };
  107. case SDL_TEXTINPUT:
  108. return text_input
  109. {
  110. std::chrono::milliseconds(event.text.timestamp),
  111. event.text.windowID,
  112. to_array(event.text.text)
  113. };
  114. case SDL_TEXTEDITING:
  115. return text_edit
  116. {
  117. std::chrono::milliseconds(event.edit.timestamp),
  118. event.edit.windowID,
  119. to_array(event.edit.text),
  120. {event.edit.start, event.edit.start + event.edit.length}
  121. };
  122. case SDL_FINGERMOTION:
  123. return pointer_motion
  124. {
  125. std::chrono::milliseconds(event.tfinger.timestamp),
  126. event.tfinger.touchId,
  127. event.tfinger.fingerId,
  128. {event.tfinger.x, event.tfinger.y},
  129. {event.tfinger.dx, event.tfinger.dy},
  130. event.tfinger.pressure
  131. };
  132. case SDL_FINGERDOWN:
  133. return pointer_down
  134. {
  135. std::chrono::milliseconds(event.tfinger.timestamp),
  136. event.tfinger.touchId,
  137. event.tfinger.fingerId,
  138. {event.tfinger.x, event.tfinger.y},
  139. {event.tfinger.dx, event.tfinger.dy},
  140. event.tfinger.pressure
  141. };
  142. case SDL_FINGERUP:
  143. return pointer_up
  144. {
  145. std::chrono::milliseconds(event.tfinger.timestamp),
  146. event.tfinger.touchId,
  147. event.tfinger.fingerId,
  148. {event.tfinger.x, event.tfinger.y},
  149. {event.tfinger.dx, event.tfinger.dy},
  150. event.tfinger.pressure
  151. };
  152. case SDL_WINDOWEVENT: switch(event.window.event)
  153. {
  154. case SDL_WINDOWEVENT_SHOWN:
  155. return make_window_event<window_shown>(event);
  156. case SDL_WINDOWEVENT_HIDDEN:
  157. return make_window_event<window_hidden>(event);
  158. case SDL_WINDOWEVENT_EXPOSED:
  159. return make_window_event<window_exposed>(event);
  160. case SDL_WINDOWEVENT_MOVED:
  161. return make_window_vector_event<window_moved>(event);
  162. case SDL_WINDOWEVENT_RESIZED:
  163. return make_window_vector_event<window_resized>(event);
  164. case SDL_WINDOWEVENT_SIZE_CHANGED:
  165. return make_window_vector_event<window_size_changed>(event);
  166. case SDL_WINDOWEVENT_MINIMIZED:
  167. return make_window_event<window_minimized>(event);
  168. case SDL_WINDOWEVENT_MAXIMIZED:
  169. return make_window_event<window_maximized>(event);
  170. case SDL_WINDOWEVENT_RESTORED:
  171. return make_window_event<window_restored>(event);
  172. case SDL_WINDOWEVENT_ENTER:
  173. return make_window_event<window_entered>(event);
  174. case SDL_WINDOWEVENT_LEAVE:
  175. return make_window_event<window_left>(event);
  176. case SDL_WINDOWEVENT_FOCUS_GAINED:
  177. return make_window_event<window_focus_gained>(event);
  178. case SDL_WINDOWEVENT_FOCUS_LOST:
  179. return make_window_event<window_focus_lost>(event);
  180. case SDL_WINDOWEVENT_CLOSE:
  181. return make_window_event<window_closed>(event);
  182. #if SDL_VERSION_ATLEAST(2, 0, 5)
  183. case SDL_WINDOWEVENT_TAKE_FOCUS:
  184. return make_window_event<window_take_focus>(event);
  185. case SDL_WINDOWEVENT_HIT_TEST:
  186. return make_window_event<window_hit_test>(event);
  187. #endif
  188. }
  189. break;
  190. case SDL_QUIT:
  191. return quit_request{};
  192. }
  193. }
  194. return std::nullopt;
  195. }
  196. #if SDL_VERSION_ATLEAST(2,0,4)
  197. int2 mouse_wheel::motion() const noexcept
  198. {
  199. return data.direction == wheel_direction::flipped ? -data.position : data.position;
  200. }
  201. #endif
  202. bool relative_mouse_mode() noexcept
  203. {
  204. return SDL_GetRelativeMouseMode();
  205. }
  206. bool relative_mouse_mode(bool enable) noexcept
  207. {
  208. return !sdlcore::utils::check_error(SDL_SetRelativeMouseMode(SDL_bool(enable)));
  209. }
  210. void require_mouse_mode(bool enable) noexcept
  211. {
  212. sdlcore::utils::throw_error(SDL_SetRelativeMouseMode(SDL_bool(enable)));
  213. }
  214. std::optional<float2> window_normalized(uint32_t window_id, int2 position) noexcept
  215. {
  216. float2 result = static_cast<float2>(position);
  217. auto window = SDL_GetWindowFromID(window_id);
  218. if(!window)
  219. return std::nullopt;
  220. int2 window_size;
  221. SDL_GetWindowSize(window, &window_size.x(), &window_size.y());
  222. if( std::any_of(window_size.begin(), window_size.end(), [](auto c){ return c == 0;}) )
  223. return std::nullopt;
  224. result /= static_cast<float2>(window_size);
  225. return result;
  226. }
  227. std::optional<float2> window_normalized_position(const mouse_data& data) noexcept
  228. {
  229. return window_normalized(data.window_id, data.position);
  230. }
  231. std::optional<float2> window_normalized_motion(const mouse_motion_data& data) noexcept
  232. {
  233. return window_normalized(data.window_id, data.motion);
  234. }
  235. std::optional<float2> screen_normalized(uint32_t window_id, int2 position, bool absolute = true) noexcept
  236. {
  237. float2 result = static_cast<float2>(position);
  238. auto window = SDL_GetWindowFromID(window_id);
  239. if(!window)
  240. return std::nullopt;
  241. if(absolute)
  242. {
  243. int2 window_position;
  244. SDL_GetWindowPosition(window, &window_position.x(), &window_position.y());
  245. result += static_cast<float2>(window_position);
  246. }
  247. SDL_DisplayMode mode;
  248. if(SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(window), &mode) < 0)
  249. return std::nullopt;
  250. const int2 screen_size{mode.w, mode.h};
  251. result /= static_cast<float2>(screen_size);
  252. return result;
  253. }
  254. std::optional<float2> screen_normalized_position(const mouse_data& data) noexcept
  255. {
  256. return screen_normalized(data.window_id, data.position);
  257. }
  258. std::optional<float2> screen_normalized_motion(const mouse_motion_data& data) noexcept
  259. {
  260. return screen_normalized(data.window_id, data.motion, false);
  261. }
  262. // NOTE: the SDL API functions here (and a number of others) are just getters for one internal structure. that also contains the device/mouse ID which is not exposed... why just not expose said struct? it also requires a self defeating PumpEvents call, i mean how hard is it to just handle the event and store the state on the user side? your brain on C i guess... very hard to do anything there
  263. mouse_motion_data last_mouse_state() noexcept
  264. {
  265. mouse_motion_data data;
  266. data.button_state = static_cast<mouse_button_mask>
  267. (SDL_GetMouseState(
  268. &data.position.x(), &data.position.y()));
  269. SDL_GetRelativeMouseState(&data.motion.x(), &data.motion.y());
  270. data.window_id = SDL_GetWindowID(SDL_GetMouseFocus());
  271. return data;
  272. }
  273. #if SDL_VERSION_ATLEAST(2,0,4)
  274. // better to use expected<bool, error>
  275. bool mouse_capture(bool enable) noexcept
  276. {
  277. SDL_PumpEvents();
  278. return !sdlcore::utils::check_error(SDL_CaptureMouse(SDL_bool(enable)));
  279. }
  280. void require_mouse_capture(bool enable)
  281. {
  282. SDL_PumpEvents();
  283. sdlcore::utils::throw_error(SDL_CaptureMouse(SDL_bool(enable)));
  284. }
  285. #endif
  286. } // namespace simple::interactive