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

498 lines
16 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-window-manager.hpp>
  20. #include <engine/app/sdl/sdl-window.hpp>
  21. #include <engine/debug/log.hpp>
  22. #include <engine/config.hpp>
  23. #include <stdexcept>
  24. namespace app {
  25. sdl_window_manager::sdl_window_manager()
  26. {
  27. // Init SDL events and video subsystems
  28. debug::log::trace("Initializing SDL events and video subsystems...");
  29. if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0)
  30. {
  31. debug::log::fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError());
  32. throw std::runtime_error("Failed to initialize SDL events and video subsystems");
  33. }
  34. debug::log::trace("Initialized SDL events and video subsystems");
  35. // Query displays
  36. const int display_count = SDL_GetNumVideoDisplays();
  37. if (display_count < 1)
  38. {
  39. debug::log::warning("No displays detected: {}", SDL_GetError());
  40. }
  41. else
  42. {
  43. // Allocate displays
  44. displays.resize(display_count);
  45. debug::log::info("Display count: {}", display_count);
  46. for (int i = 0; i < display_count; ++i)
  47. {
  48. // Update display state
  49. update_display(i);
  50. // Log display information
  51. display& display = displays[i];
  52. const auto display_resolution = display.get_bounds().size();
  53. debug::log::info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
  54. }
  55. }
  56. // Load OpenGL library
  57. debug::log::trace("Loading OpenGL library...");
  58. if (SDL_GL_LoadLibrary(nullptr) != 0)
  59. {
  60. debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError());
  61. throw std::runtime_error("Failed to load OpenGL library");
  62. }
  63. debug::log::trace("Loaded OpenGL library");
  64. // Set OpenGL-related window creation hints
  65. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  66. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  67. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major);
  68. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor);
  69. SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
  70. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
  71. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size);
  72. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size);
  73. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config::opengl_min_blue_size);
  74. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config::opengl_min_alpha_size);
  75. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config::opengl_min_depth_size);
  76. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config::opengl_min_stencil_size);
  77. }
  78. sdl_window_manager::~sdl_window_manager()
  79. {
  80. // Quit SDL video subsystem
  81. debug::log::trace("Quitting SDL video subsystem...");
  82. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  83. debug::log::trace("Quit SDL video subsystem");
  84. }
  85. window* sdl_window_manager::create_window
  86. (
  87. const std::string& title,
  88. const math::vector<int, 2>& windowed_position,
  89. const math::vector<int, 2>& windowed_size,
  90. bool maximized,
  91. bool fullscreen,
  92. bool v_sync
  93. )
  94. {
  95. // Create new window
  96. app::sdl_window* window = new app::sdl_window
  97. (
  98. title,
  99. windowed_position,
  100. windowed_size,
  101. maximized,
  102. fullscreen,
  103. v_sync
  104. );
  105. // Map internal SDL window to window
  106. window_map[window->internal_window] = window;
  107. return window;
  108. }
  109. void sdl_window_manager::update()
  110. {
  111. // Gather SDL events from event queue
  112. SDL_PumpEvents();
  113. for (;;)
  114. {
  115. // Get next window or display event
  116. SDL_Event event;
  117. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DISPLAYEVENT, SDL_SYSWMEVENT);
  118. if (!status)
  119. {
  120. break;
  121. }
  122. else if (status < 0)
  123. {
  124. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  125. throw std::runtime_error("Failed to peep SDL events");
  126. }
  127. // Handle event
  128. if (event.type == SDL_WINDOWEVENT)
  129. {
  130. switch (event.window.event)
  131. {
  132. case SDL_WINDOWEVENT_SIZE_CHANGED:
  133. {
  134. // Get window
  135. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  136. app::sdl_window* window = get_window(internal_window);
  137. // Update window state
  138. window->size = {event.window.data1, event.window.data2};
  139. const auto window_flags = SDL_GetWindowFlags(internal_window);
  140. if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)))
  141. {
  142. window->windowed_size = window->size;
  143. }
  144. SDL_GL_GetDrawableSize(internal_window, &window->viewport_size.x(), &window->viewport_size.y());
  145. window->rasterizer->context_resized(window->viewport_size.x(), window->viewport_size.y());
  146. // Log window resized event
  147. debug::log::debug("Window {} resized to {}x{}", event.window.windowID, event.window.data1, event.window.data2);
  148. // Publish window resized event
  149. window->resized_publisher.publish({window, window->size});
  150. break;
  151. }
  152. case SDL_WINDOWEVENT_MOVED:
  153. {
  154. // Get window
  155. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  156. app::sdl_window* window = get_window(internal_window);
  157. // Update window state
  158. window->position = {event.window.data1, event.window.data2};
  159. const auto window_flags = SDL_GetWindowFlags(internal_window);
  160. if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)))
  161. {
  162. window->windowed_position = window->position;
  163. }
  164. // Log window moved event
  165. debug::log::debug("Window {} moved to ({}, {})", event.window.windowID, event.window.data1, event.window.data2);
  166. // Publish window moved event
  167. window->moved_publisher.publish({window, window->position});
  168. break;
  169. }
  170. case SDL_WINDOWEVENT_FOCUS_GAINED:
  171. {
  172. // Get window
  173. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  174. app::sdl_window* window = get_window(internal_window);
  175. // Log window focus gained event
  176. debug::log::debug("Window {} gained focus", event.window.windowID);
  177. // Publish window focus gained event
  178. window->focus_changed_publisher.publish({window, true});
  179. break;
  180. }
  181. case SDL_WINDOWEVENT_FOCUS_LOST:
  182. {
  183. // Get window
  184. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  185. app::sdl_window* window = get_window(internal_window);
  186. // Log window focus lost event
  187. debug::log::debug("Window {} lost focus", event.window.windowID);
  188. // Publish window focus lost event
  189. window->focus_changed_publisher.publish({window, false});
  190. break;
  191. }
  192. case SDL_WINDOWEVENT_MAXIMIZED:
  193. {
  194. // Get window
  195. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  196. app::sdl_window* window = get_window(internal_window);
  197. // Update window state
  198. window->maximized = true;
  199. // Log window focus gained event
  200. debug::log::debug("Window {} maximized", event.window.windowID);
  201. // Publish window maximized event
  202. window->maximized_publisher.publish({window});
  203. break;
  204. }
  205. case SDL_WINDOWEVENT_RESTORED:
  206. {
  207. // Get window
  208. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  209. app::sdl_window* window = get_window(internal_window);
  210. // Update window state
  211. window->maximized = false;
  212. // Log window restored event
  213. debug::log::debug("Window {} restored", event.window.windowID);
  214. // Publish window restored event
  215. window->restored_publisher.publish({window});
  216. break;
  217. }
  218. case SDL_WINDOWEVENT_MINIMIZED:
  219. {
  220. // Get window
  221. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  222. app::sdl_window* window = get_window(internal_window);
  223. // Log window focus gained event
  224. debug::log::debug("Window {} minimized", event.window.windowID);
  225. // Publish window minimized event
  226. window->minimized_publisher.publish({window});
  227. break;
  228. }
  229. [[unlikely]] case SDL_WINDOWEVENT_CLOSE:
  230. {
  231. // Get window
  232. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  233. app::sdl_window* window = get_window(internal_window);
  234. // Log window closed event
  235. debug::log::debug("Window {} closed", event.window.windowID);
  236. // Publish window closed event
  237. window->closed_publisher.publish({window});
  238. break;
  239. }
  240. default:
  241. break;
  242. }
  243. }
  244. else if (event.type == SDL_DISPLAYEVENT)
  245. {
  246. switch (event.display.event)
  247. {
  248. case SDL_DISPLAYEVENT_CONNECTED:
  249. if (event.display.display < displays.size())
  250. {
  251. // Get previously connected display
  252. display& display = displays[event.display.display];
  253. // Update display state
  254. display.connected = true;
  255. // Log display (re)connected event
  256. debug::log::info("Reconnected display {}", event.display.display);
  257. // Publish display (re)connected event
  258. display.connected_publisher.publish({&display});
  259. }
  260. else if (event.display.display == displays.size())
  261. {
  262. // Allocate new display
  263. displays.resize(displays.size() + 1);
  264. display& display = displays[event.display.display];
  265. // Update display state
  266. update_display(event.display.display);
  267. // Log display info
  268. const auto display_resolution = display.get_bounds().size();
  269. debug::log::info("Connected display {}; name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", event.display.display, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi());
  270. // Publish display connected event
  271. display.connected_publisher.publish({&display});
  272. }
  273. else
  274. {
  275. debug::log::error("Index of connected display ({}) out of range", event.display.display);
  276. }
  277. break;
  278. case SDL_DISPLAYEVENT_DISCONNECTED:
  279. if (event.display.display < displays.size())
  280. {
  281. // Get display
  282. display& display = displays[event.display.display];
  283. // Update display state
  284. display.connected = false;
  285. // Log display disconnected event
  286. debug::log::info("Disconnected display {}", event.display.display);
  287. // Publish display disconnected event
  288. display.disconnected_publisher.publish({&display});
  289. }
  290. else
  291. {
  292. debug::log::error("Index of disconnected display ({}) out of range", event.display.display);
  293. }
  294. break;
  295. case SDL_DISPLAYEVENT_ORIENTATION:
  296. if (event.display.display < displays.size())
  297. {
  298. // Get display
  299. display& display = displays[event.display.display];
  300. // Update display state
  301. switch (event.display.data1)
  302. {
  303. case SDL_ORIENTATION_LANDSCAPE:
  304. display.set_orientation(display_orientation::landscape);
  305. break;
  306. case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
  307. display.set_orientation(display_orientation::landscape_flipped);
  308. break;
  309. case SDL_ORIENTATION_PORTRAIT:
  310. display.set_orientation(display_orientation::portrait);
  311. break;
  312. case SDL_ORIENTATION_PORTRAIT_FLIPPED:
  313. display.set_orientation(display_orientation::portrait_flipped);
  314. break;
  315. default:
  316. display.set_orientation(display_orientation::unknown);
  317. break;
  318. }
  319. // Log display orientation changed event
  320. debug::log::info("Display {} orientation changed", event.display.display);
  321. // Publish display orientation changed event
  322. display.orientation_changed_publisher.publish({&display, display.get_orientation()});
  323. }
  324. else
  325. {
  326. debug::log::error("Index of orientation-changed display ({}) out of range", event.display.display);
  327. }
  328. break;
  329. default:
  330. break;
  331. }
  332. }
  333. }
  334. }
  335. sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window)
  336. {
  337. sdl_window* window = nullptr;
  338. if (auto i = window_map.find(internal_window); i != window_map.end())
  339. {
  340. window = i->second;
  341. }
  342. else
  343. {
  344. throw std::runtime_error("SDL window unrecognized by SDL window manager");
  345. }
  346. return window;
  347. }
  348. std::size_t sdl_window_manager::get_display_count() const
  349. {
  350. return displays.size();
  351. }
  352. const display& sdl_window_manager::get_display(std::size_t index) const
  353. {
  354. return displays[index];
  355. }
  356. void sdl_window_manager::update_display(int sdl_display_index)
  357. {
  358. // Query display mode
  359. SDL_DisplayMode sdl_display_mode;
  360. if (SDL_GetDesktopDisplayMode(sdl_display_index, &sdl_display_mode) != 0)
  361. {
  362. debug::log::error("Failed to get mode of display {}: {}", sdl_display_index, SDL_GetError());
  363. SDL_ClearError();
  364. sdl_display_mode = {0, 0, 0, 0, nullptr};
  365. }
  366. // Query display name
  367. const char* sdl_display_name = SDL_GetDisplayName(sdl_display_index);
  368. if (!sdl_display_name)
  369. {
  370. debug::log::warning("Failed to get name of display {}: {}", sdl_display_index, SDL_GetError());
  371. SDL_ClearError();
  372. sdl_display_name = nullptr;
  373. }
  374. // Query display bounds
  375. SDL_Rect sdl_display_bounds;
  376. if (SDL_GetDisplayBounds(sdl_display_index, &sdl_display_bounds) != 0)
  377. {
  378. debug::log::warning("Failed to get bounds of display {}: {}", sdl_display_index, SDL_GetError());
  379. SDL_ClearError();
  380. sdl_display_bounds = {0, 0, sdl_display_mode.w, sdl_display_mode.h};
  381. }
  382. // Query display usable bounds
  383. SDL_Rect sdl_display_usable_bounds;
  384. if (SDL_GetDisplayUsableBounds(sdl_display_index, &sdl_display_usable_bounds) != 0)
  385. {
  386. debug::log::warning("Failed to get usable bounds of display {}: {}", sdl_display_index, SDL_GetError());
  387. SDL_ClearError();
  388. sdl_display_usable_bounds = sdl_display_bounds;
  389. }
  390. // Query display DPI
  391. float sdl_display_dpi;
  392. if (SDL_GetDisplayDPI(sdl_display_index, &sdl_display_dpi, nullptr, nullptr) != 0)
  393. {
  394. debug::log::warning("Failed to get DPI of display {}: {}", sdl_display_index, SDL_GetError());
  395. SDL_ClearError();
  396. sdl_display_dpi = 0.0f;
  397. }
  398. // Query display orientation
  399. SDL_DisplayOrientation sdl_display_orientation = SDL_GetDisplayOrientation(sdl_display_index);
  400. // Update display properties
  401. display& display = displays[sdl_display_index];
  402. display.set_index(static_cast<std::size_t>(sdl_display_index));
  403. display.set_name(sdl_display_name ? sdl_display_name : std::string());
  404. display.set_bounds({{sdl_display_bounds.x, sdl_display_bounds.y}, {sdl_display_bounds.x + sdl_display_bounds.w, sdl_display_bounds.y + sdl_display_bounds.h}});
  405. display.set_usable_bounds({{sdl_display_usable_bounds.x, sdl_display_usable_bounds.y}, {sdl_display_usable_bounds.x + sdl_display_usable_bounds.w, sdl_display_usable_bounds.y + sdl_display_usable_bounds.h}});
  406. display.set_refresh_rate(sdl_display_mode.refresh_rate);
  407. display.set_dpi(sdl_display_dpi);
  408. switch (sdl_display_orientation)
  409. {
  410. case SDL_ORIENTATION_LANDSCAPE:
  411. display.set_orientation(display_orientation::landscape);
  412. break;
  413. case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
  414. display.set_orientation(display_orientation::landscape_flipped);
  415. break;
  416. case SDL_ORIENTATION_PORTRAIT:
  417. display.set_orientation(display_orientation::portrait);
  418. break;
  419. case SDL_ORIENTATION_PORTRAIT_FLIPPED:
  420. display.set_orientation(display_orientation::portrait_flipped);
  421. break;
  422. default:
  423. display.set_orientation(display_orientation::unknown);
  424. break;
  425. }
  426. display.connected = true;
  427. }
  428. } // namespace app