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

324 lines
9.4 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-window-manager.hpp"
  20. #include "app/sdl/sdl-window.hpp"
  21. #include "debug/log.hpp"
  22. #include "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. debug::log::info("Display count: {}", display_count);
  44. displays.resize(display_count);
  45. for (int i = 0; i < display_count; ++i)
  46. {
  47. // Query display mode
  48. SDL_DisplayMode display_mode;
  49. if (SDL_GetDesktopDisplayMode(i, &display_mode) != 0)
  50. {
  51. debug::log::error("Failed to get mode of display {}: {}", i, SDL_GetError());
  52. SDL_ClearError();
  53. continue;
  54. }
  55. // Query display name
  56. const char* display_name = SDL_GetDisplayName(i);
  57. if (!display_name)
  58. {
  59. debug::log::warning("Failed to get name of display {}: {}", i, SDL_GetError());
  60. SDL_ClearError();
  61. display_name = "";
  62. }
  63. // Query display DPI
  64. float display_dpi;
  65. if (SDL_GetDisplayDPI(i, &display_dpi, nullptr, nullptr) != 0)
  66. {
  67. const float default_dpi = 96.0f;
  68. debug::log::warning("Failed to get DPI of display {}: {}; Defaulting to {} DPI", i, SDL_GetError(), default_dpi);
  69. SDL_ClearError();
  70. }
  71. // Update display properties
  72. display& display = displays[i];
  73. display.set_index(i);
  74. display.set_name(display_name);
  75. display.set_size({display_mode.w, display_mode.h});
  76. display.set_refresh_rate(display_mode.refresh_rate);
  77. display.set_dpi(display_dpi);
  78. // Log display information
  79. debug::log::info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display_name, display_mode.w, display_mode.h, display_mode.refresh_rate, display_dpi);
  80. }
  81. }
  82. // Load OpenGL library
  83. debug::log::trace("Loading OpenGL library...");
  84. if (SDL_GL_LoadLibrary(nullptr) != 0)
  85. {
  86. debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError());
  87. throw std::runtime_error("Failed to load OpenGL library");
  88. }
  89. debug::log::trace("Loaded OpenGL library");
  90. // Set OpenGL-related window creation hints
  91. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  92. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  93. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major);
  94. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor);
  95. SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
  96. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
  97. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size);
  98. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size);
  99. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config::opengl_min_blue_size);
  100. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config::opengl_min_alpha_size);
  101. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config::opengl_min_depth_size);
  102. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config::opengl_min_stencil_size);
  103. }
  104. sdl_window_manager::~sdl_window_manager()
  105. {
  106. // Quit SDL video subsystem
  107. debug::log::trace("Quitting SDL video subsystem...");
  108. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  109. debug::log::trace("Quit SDL video subsystem");
  110. }
  111. window* sdl_window_manager::create_window
  112. (
  113. const std::string& title,
  114. const math::vector<int, 2>& windowed_position,
  115. const math::vector<int, 2>& windowed_size,
  116. bool maximized,
  117. bool fullscreen,
  118. bool v_sync
  119. )
  120. {
  121. // Create new window
  122. app::sdl_window* window = new app::sdl_window
  123. (
  124. title,
  125. windowed_position,
  126. windowed_size,
  127. maximized,
  128. fullscreen,
  129. v_sync
  130. );
  131. // Map internal SDL window to window
  132. window_map[window->internal_window] = window;
  133. return window;
  134. }
  135. void sdl_window_manager::update()
  136. {
  137. // Gather SDL events from event queue
  138. SDL_PumpEvents();
  139. for (;;)
  140. {
  141. // Get next window or display event
  142. SDL_Event event;
  143. int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DISPLAYEVENT, SDL_SYSWMEVENT);
  144. if (!status)
  145. {
  146. break;
  147. }
  148. else if (status < 0)
  149. {
  150. debug::log::error("Failed to peep SDL events: {}", SDL_GetError());
  151. throw std::runtime_error("Failed to peep SDL events");
  152. }
  153. // Handle event
  154. if (event.type == SDL_WINDOWEVENT)
  155. {
  156. switch (event.window.event)
  157. {
  158. case SDL_WINDOWEVENT_SIZE_CHANGED:
  159. {
  160. // Get window
  161. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  162. app::sdl_window* window = get_window(internal_window);
  163. // Update window state
  164. window->size = {event.window.data1, event.window.data2};
  165. const auto window_flags = SDL_GetWindowFlags(internal_window);
  166. if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & SDL_WINDOW_FULLSCREEN))
  167. {
  168. window->windowed_size = window->size;
  169. }
  170. SDL_GL_GetDrawableSize(internal_window, &window->viewport_size.x(), &window->viewport_size.y());
  171. window->rasterizer->context_resized(window->viewport_size.x(), window->viewport_size.y());
  172. // Publish window resized event
  173. window->resized_publisher.publish({window, window->size});
  174. break;
  175. }
  176. case SDL_WINDOWEVENT_MOVED:
  177. {
  178. // Get window
  179. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  180. app::sdl_window* window = get_window(internal_window);
  181. // Update window state
  182. window->position = {event.window.data1, event.window.data2};
  183. const auto window_flags = SDL_GetWindowFlags(internal_window);
  184. if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & SDL_WINDOW_FULLSCREEN))
  185. {
  186. window->windowed_position = window->position;
  187. }
  188. // Publish window moved event
  189. window->moved_publisher.publish({window, window->position});
  190. break;
  191. }
  192. case SDL_WINDOWEVENT_FOCUS_GAINED:
  193. {
  194. // Get window
  195. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  196. app::sdl_window* window = get_window(internal_window);
  197. // Publish window focus gained event
  198. window->focus_changed_publisher.publish({window, true});
  199. break;
  200. }
  201. case SDL_WINDOWEVENT_FOCUS_LOST:
  202. {
  203. // Get window
  204. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  205. app::sdl_window* window = get_window(internal_window);
  206. // Publish window focus lost event
  207. window->focus_changed_publisher.publish({window, false});
  208. break;
  209. }
  210. case SDL_WINDOWEVENT_MAXIMIZED:
  211. {
  212. // Get window
  213. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  214. app::sdl_window* window = get_window(internal_window);
  215. // Update window state
  216. window->maximized = true;
  217. // Publish window maximized event
  218. window->maximized_publisher.publish({window});
  219. break;
  220. }
  221. case SDL_WINDOWEVENT_RESTORED:
  222. {
  223. // Get window
  224. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  225. app::sdl_window* window = get_window(internal_window);
  226. // Update window state
  227. window->maximized = false;
  228. // Publish window restored event
  229. window->restored_publisher.publish({window});
  230. break;
  231. }
  232. case SDL_WINDOWEVENT_MINIMIZED:
  233. {
  234. // Get window
  235. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  236. app::sdl_window* window = get_window(internal_window);
  237. // Publish window minimized event
  238. window->minimized_publisher.publish({window});
  239. break;
  240. }
  241. [[unlikely]] case SDL_WINDOWEVENT_CLOSE:
  242. {
  243. // Get window
  244. SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID);
  245. app::sdl_window* window = get_window(internal_window);
  246. // Publish window closed event
  247. window->closed_publisher.publish({window});
  248. break;
  249. }
  250. default:
  251. break;
  252. }
  253. }
  254. else if (event.type == SDL_DISPLAYEVENT)
  255. {
  256. }
  257. }
  258. }
  259. sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window)
  260. {
  261. sdl_window* window = nullptr;
  262. if (auto i = window_map.find(internal_window); i != window_map.end())
  263. {
  264. window = i->second;
  265. }
  266. else
  267. {
  268. throw std::runtime_error("SDL window unrecognized by SDL window manager");
  269. }
  270. return window;
  271. }
  272. std::size_t sdl_window_manager::get_display_count() const
  273. {
  274. return displays.size();
  275. }
  276. const display& sdl_window_manager::get_display(std::size_t index) const
  277. {
  278. return displays[index];
  279. }
  280. } // namespace app