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

756 lines
19 KiB

  1. /*
  2. * Copyright (C) 2021 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 "animation/frame-scheduler.hpp"
  20. #include "application.hpp"
  21. #include "debug/logger.hpp"
  22. #include "debug/performance-sampler.hpp"
  23. #include "event/event-dispatcher.hpp"
  24. #include "event/window-events.hpp"
  25. #include "input/scancode.hpp"
  26. #include "input/sdl-game-controller-tables.hpp"
  27. #include "input/sdl-scancode-table.hpp"
  28. #include "resources/image.hpp"
  29. #include <SDL2/SDL.h>
  30. #include <glad/glad.h>
  31. #include <stdexcept>
  32. #include <utility>
  33. #include <thread>
  34. #include <stb/stb_image_write.h>
  35. #include <iostream>
  36. #include <iomanip>
  37. application::application():
  38. closed(false),
  39. exit_status(EXIT_SUCCESS),
  40. current_state{std::string(), nullptr, nullptr},
  41. queued_state{std::string(), nullptr, nullptr},
  42. update_callback(nullptr),
  43. render_callback(nullptr),
  44. fullscreen(true),
  45. vsync(true),
  46. cursor_visible(true),
  47. display_dimensions({0, 0}),
  48. window_dimensions({0, 0}),
  49. viewport_dimensions({0, 0}),
  50. mouse_position({0, 0}),
  51. update_rate(60.0),
  52. logger(nullptr),
  53. sdl_window(nullptr),
  54. sdl_gl_context(nullptr)
  55. {
  56. // Setup logging
  57. logger = new debug::logger();
  58. // Get SDL compiled version
  59. SDL_version sdl_compiled_version;
  60. SDL_VERSION(&sdl_compiled_version);
  61. std::string sdl_compiled_version_string = std::to_string(sdl_compiled_version.major) + "." + std::to_string(sdl_compiled_version.minor) + "." + std::to_string(sdl_compiled_version.patch);
  62. logger->log("Compiled against SDL " + sdl_compiled_version_string);
  63. // Get SDL linked version
  64. SDL_version sdl_linked_version;
  65. SDL_GetVersion(&sdl_linked_version);
  66. std::string sdl_linked_version_string = std::to_string(sdl_linked_version.major) + "." + std::to_string(sdl_linked_version.minor) + "." + std::to_string(sdl_linked_version.patch);
  67. logger->log("Linking against SDL " + sdl_linked_version_string);
  68. // Init SDL
  69. logger->push_task("Initializing SDL");
  70. if (SDL_Init(SDL_INIT_VIDEO) != 0)
  71. {
  72. logger->pop_task(EXIT_FAILURE);
  73. throw std::runtime_error("Failed to initialize SDL");
  74. }
  75. else
  76. {
  77. logger->pop_task(EXIT_SUCCESS);
  78. }
  79. // Load default OpenGL library
  80. logger->push_task("Loading OpenGL library");
  81. if (SDL_GL_LoadLibrary(nullptr) != 0)
  82. {
  83. logger->pop_task(EXIT_FAILURE);
  84. }
  85. else
  86. {
  87. logger->pop_task(EXIT_SUCCESS);
  88. }
  89. // Set window creation hints
  90. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  91. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  92. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
  93. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  94. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  95. // Get display dimensions
  96. SDL_DisplayMode sdl_desktop_display_mode;
  97. if (SDL_GetDesktopDisplayMode(0, &sdl_desktop_display_mode) != 0)
  98. {
  99. logger->error("Failed to get desktop display mode: \"" + std::string(SDL_GetError()) + "\"");
  100. throw std::runtime_error("Failed to detect desktop display mode");
  101. }
  102. else
  103. {
  104. logger->log("Detected " + std::to_string(sdl_desktop_display_mode.w) + "x" + std::to_string(sdl_desktop_display_mode.h) + " display");
  105. display_dimensions = {sdl_desktop_display_mode.w, sdl_desktop_display_mode.h};
  106. }
  107. // Create a hidden fullscreen window
  108. logger->push_task("Creating " + std::to_string(display_dimensions[0]) + "x" + std::to_string(display_dimensions[1]) + " window");
  109. sdl_window = SDL_CreateWindow
  110. (
  111. "",
  112. SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
  113. display_dimensions[0], display_dimensions[1],
  114. SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN
  115. );
  116. if (!sdl_window)
  117. {
  118. logger->pop_task(EXIT_FAILURE);
  119. throw std::runtime_error("Failed to create SDL window");
  120. }
  121. else
  122. {
  123. logger->pop_task(EXIT_SUCCESS);
  124. }
  125. // Create OpenGL context
  126. logger->push_task("Creating OpenGL 3.3 context");
  127. sdl_gl_context = SDL_GL_CreateContext(sdl_window);
  128. if (!sdl_gl_context)
  129. {
  130. logger->pop_task(EXIT_FAILURE);
  131. throw std::runtime_error("Failed to create OpenGL context");
  132. }
  133. else
  134. {
  135. logger->pop_task(EXIT_SUCCESS);
  136. }
  137. // Update window size and viewport size
  138. SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]);
  139. SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]);
  140. // Make OpenGL context current
  141. logger->push_task("Making OpenGL context current");
  142. if (SDL_GL_MakeCurrent(sdl_window, sdl_gl_context) != 0)
  143. {
  144. logger->pop_task(EXIT_FAILURE);
  145. throw std::runtime_error("Failed to make OpenGL context current");
  146. }
  147. else
  148. {
  149. logger->pop_task(EXIT_SUCCESS);
  150. }
  151. // Load OpenGL functions via GLAD
  152. logger->push_task("Loading OpenGL functions");
  153. if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
  154. {
  155. logger->pop_task(EXIT_FAILURE);
  156. throw std::runtime_error("Failed to load OpenGL functions");
  157. }
  158. else
  159. {
  160. logger->pop_task(EXIT_SUCCESS);
  161. }
  162. // Set v-sync mode
  163. int swap_interval = (vsync) ? 1 : 0;
  164. logger->push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync");
  165. if (SDL_GL_SetSwapInterval(swap_interval) != 0)
  166. {
  167. logger->pop_task(EXIT_FAILURE);
  168. }
  169. else
  170. {
  171. logger->pop_task(EXIT_SUCCESS);
  172. }
  173. // Init SDL joystick and gamepad subsystems
  174. logger->push_task("Initializing SDL Joystick and Game Controller subsystems");
  175. if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
  176. {
  177. logger->pop_task(EXIT_FAILURE);
  178. }
  179. else
  180. {
  181. logger->pop_task(EXIT_SUCCESS);
  182. }
  183. // Setup rasterizer
  184. rasterizer = new gl::rasterizer();
  185. // Setup events
  186. event_dispatcher = new ::event_dispatcher();
  187. // Setup input
  188. keyboard = new input::keyboard();
  189. keyboard->set_event_dispatcher(event_dispatcher);
  190. mouse = new input::mouse();
  191. mouse->set_event_dispatcher(event_dispatcher);
  192. // Connect gamepads
  193. translate_sdl_events();
  194. // Setup frame scheduler
  195. frame_scheduler = new ::frame_scheduler();
  196. frame_scheduler->set_update_callback(std::bind(&application::update, this, std::placeholders::_1, std::placeholders::_2));
  197. frame_scheduler->set_render_callback(std::bind(&application::render, this, std::placeholders::_1));
  198. frame_scheduler->set_update_rate(update_rate);
  199. frame_scheduler->set_max_frame_duration(0.25);
  200. // Setup performance sampling
  201. performance_sampler = new debug::performance_sampler();
  202. performance_sampler->set_sample_size(15);
  203. }
  204. application::~application()
  205. {
  206. // Destroy the OpenGL context
  207. SDL_GL_DeleteContext(sdl_gl_context);
  208. // Destroy the SDL window
  209. SDL_DestroyWindow(sdl_window);
  210. // Shutdown SDL
  211. SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
  212. SDL_Quit();
  213. }
  214. void application::close(int status)
  215. {
  216. closed = true;
  217. exit_status = status;
  218. }
  219. int application::execute(const application::state& initial_state)
  220. {
  221. try
  222. {
  223. // Enter initial application state
  224. change_state(initial_state);
  225. // Perform initial update
  226. update(0.0, 0.0);
  227. // Reset frame scheduler
  228. frame_scheduler->reset();
  229. // Schedule frames until closed
  230. while (!closed)
  231. {
  232. // Tick frame scheduler
  233. frame_scheduler->tick();
  234. // Sample frame duration
  235. performance_sampler->sample(frame_scheduler->get_frame_duration());
  236. }
  237. // Exit current state
  238. change_state({std::string(), nullptr, nullptr});
  239. }
  240. catch (const std::exception& e)
  241. {
  242. // Print exception to logger
  243. logger->error(std::string("Unhandled exception: \"") + e.what() + std::string("\""));
  244. // Show error message box with unhandled exception
  245. SDL_ShowSimpleMessageBox
  246. (
  247. SDL_MESSAGEBOX_ERROR,
  248. "Unhandled Exception",
  249. e.what(),
  250. sdl_window
  251. );
  252. // Set exit status to failure
  253. exit_status = EXIT_FAILURE;
  254. }
  255. return exit_status;
  256. }
  257. void application::change_state(const application::state& next_state)
  258. {
  259. // Exit current state
  260. if (current_state.exit)
  261. {
  262. logger->push_task("Exiting application state \"" + current_state.name + "\"");
  263. try
  264. {
  265. current_state.exit();
  266. }
  267. catch (...)
  268. {
  269. logger->pop_task(EXIT_FAILURE);
  270. throw;
  271. }
  272. logger->pop_task(EXIT_SUCCESS);
  273. }
  274. current_state = next_state;
  275. // Enter next state
  276. if (current_state.enter)
  277. {
  278. logger->push_task("Entering application state \"" + current_state.name + "\"");
  279. try
  280. {
  281. current_state.enter();
  282. }
  283. catch (...)
  284. {
  285. logger->pop_task(EXIT_FAILURE);
  286. throw;
  287. }
  288. logger->pop_task(EXIT_SUCCESS);
  289. }
  290. // Enter queued state (if any)
  291. if (queued_state.enter != nullptr || queued_state.exit != nullptr)
  292. {
  293. // Make a copy of the queued state
  294. application::state queued_state_copy = queued_state;
  295. // Clear the queued state
  296. queued_state = {std::string(), nullptr, nullptr};
  297. // Enter the queued state
  298. change_state(queued_state_copy);
  299. }
  300. }
  301. void application::queue_state(const application::state& next_state)
  302. {
  303. queued_state = next_state;
  304. logger->log("Queued application state \"" + queued_state.name + "\"");
  305. }
  306. std::shared_ptr<image> application::capture_frame() const
  307. {
  308. int w = viewport_dimensions[0];
  309. int h = viewport_dimensions[1];
  310. std::shared_ptr<image> frame = std::make_shared<image>();
  311. frame->format(3, false);
  312. frame->resize(w, h);
  313. // Read pixel data from framebuffer into image
  314. glReadBuffer(GL_BACK);
  315. glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, frame->get_pixels());
  316. return std::move(frame);
  317. }
  318. void application::save_frame(const std::string& path) const
  319. {
  320. logger->push_task("Saving screenshot to \"" + path + "\"");
  321. auto frame = capture_frame();
  322. std::thread
  323. (
  324. [frame, path]
  325. {
  326. stbi_flip_vertically_on_write(1);
  327. stbi_write_png(path.c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->get_pixels(), frame->get_width() * frame->get_channel_count());
  328. }
  329. ).detach();
  330. logger->pop_task(EXIT_SUCCESS);
  331. }
  332. void application::set_update_callback(const update_callback_type& callback)
  333. {
  334. update_callback = callback;
  335. }
  336. void application::set_render_callback(const render_callback_type& callback)
  337. {
  338. render_callback = callback;
  339. }
  340. void application::set_update_rate(double frequency)
  341. {
  342. update_rate = frequency;
  343. frame_scheduler->set_update_rate(update_rate);
  344. }
  345. void application::set_title(const std::string& title)
  346. {
  347. SDL_SetWindowTitle(sdl_window, title.c_str());
  348. }
  349. void application::set_cursor_visible(bool visible)
  350. {
  351. SDL_ShowCursor((visible) ? SDL_ENABLE : SDL_DISABLE);
  352. cursor_visible = visible;
  353. }
  354. void application::set_relative_mouse_mode(bool enabled)
  355. {
  356. if (enabled)
  357. {
  358. SDL_GetMouseState(&mouse_position[0], &mouse_position[1]);
  359. SDL_ShowCursor(SDL_DISABLE);
  360. SDL_SetRelativeMouseMode(SDL_TRUE);
  361. }
  362. else
  363. {
  364. SDL_SetRelativeMouseMode(SDL_FALSE);
  365. SDL_WarpMouseInWindow(sdl_window, mouse_position[0], mouse_position[1]);
  366. if (cursor_visible)
  367. SDL_ShowCursor(SDL_ENABLE);
  368. }
  369. }
  370. void application::resize_window(int width, int height)
  371. {
  372. int x = (display_dimensions[0] >> 1) - (width >> 1);
  373. int y = (display_dimensions[1] >> 1) - (height >> 1);
  374. // Resize and center window
  375. SDL_SetWindowPosition(sdl_window, x, y);
  376. SDL_SetWindowSize(sdl_window, width, height);
  377. window_resized();
  378. }
  379. void application::set_fullscreen(bool fullscreen)
  380. {
  381. if (this->fullscreen != fullscreen)
  382. {
  383. this->fullscreen = fullscreen;
  384. if (fullscreen)
  385. {
  386. SDL_HideWindow(sdl_window);
  387. SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
  388. SDL_ShowWindow(sdl_window);
  389. }
  390. else
  391. {
  392. SDL_SetWindowFullscreen(sdl_window, 0);
  393. SDL_SetWindowBordered(sdl_window, SDL_TRUE);
  394. SDL_SetWindowResizable(sdl_window, SDL_TRUE);
  395. }
  396. }
  397. }
  398. void application::set_vsync(bool vsync)
  399. {
  400. if (this->vsync != vsync)
  401. {
  402. this->vsync = vsync;
  403. SDL_GL_SetSwapInterval((vsync) ? 1 : 0);
  404. }
  405. }
  406. void application::set_window_opacity(float opacity)
  407. {
  408. SDL_SetWindowOpacity(sdl_window, opacity);
  409. }
  410. void application::swap_buffers()
  411. {
  412. SDL_GL_SwapWindow(sdl_window);
  413. }
  414. void application::show_window()
  415. {
  416. SDL_ShowWindow(sdl_window);
  417. }
  418. void application::hide_window()
  419. {
  420. SDL_HideWindow(sdl_window);
  421. }
  422. void application::add_game_controller_mappings(const void* mappings, std::size_t size)
  423. {
  424. logger->push_task("Adding SDL game controller mappings");
  425. int mapping_count = SDL_GameControllerAddMappingsFromRW(SDL_RWFromConstMem(mappings, size), 0);
  426. if (mapping_count == -1)
  427. {
  428. logger->pop_task(EXIT_FAILURE);
  429. }
  430. else
  431. {
  432. logger->log("Added " + std::to_string(mapping_count) + " SDL game controller mappings");
  433. logger->pop_task(EXIT_SUCCESS);
  434. }
  435. }
  436. void application::update(double t, double dt)
  437. {
  438. translate_sdl_events();
  439. event_dispatcher->update(t);
  440. if (update_callback)
  441. {
  442. update_callback(t, dt);
  443. }
  444. /*
  445. static int frame = 0;
  446. if (frame % 60 == 0)
  447. {
  448. std::cout << std::fixed;
  449. std::cout << std::setprecision(2);
  450. std::cout << performance_sampler->mean_frame_duration() * 1000.0 << "\n";
  451. }
  452. ++frame;
  453. */
  454. }
  455. void application::render(double alpha)
  456. {
  457. /*
  458. std::cout << std::fixed;
  459. std::cout << std::setprecision(2);
  460. std::cout << performance_sampler->mean_frame_duration() * 1000.0 << std::endl;
  461. */
  462. if (render_callback)
  463. {
  464. render_callback(alpha);
  465. }
  466. SDL_GL_SwapWindow(sdl_window);
  467. }
  468. void application::translate_sdl_events()
  469. {
  470. // Mouse motion event accumulators
  471. bool mouse_motion = false;
  472. int mouse_x;
  473. int mouse_y;
  474. int mouse_dx = 0;
  475. int mouse_dy = 0;
  476. SDL_Event sdl_event;
  477. while (SDL_PollEvent(&sdl_event))
  478. {
  479. switch (sdl_event.type)
  480. {
  481. case SDL_KEYDOWN:
  482. case SDL_KEYUP:
  483. {
  484. if (sdl_event.key.repeat == 0)
  485. {
  486. input::scancode scancode = input::scancode::unknown;
  487. if (sdl_event.key.keysym.scancode <= SDL_SCANCODE_APP2)
  488. {
  489. scancode = input::sdl_scancode_table[sdl_event.key.keysym.scancode];
  490. }
  491. if (sdl_event.type == SDL_KEYDOWN)
  492. keyboard->press(scancode);
  493. else
  494. keyboard->release(scancode);
  495. }
  496. break;
  497. }
  498. case SDL_MOUSEMOTION:
  499. {
  500. // More than one mouse motion event is often generated per frame, and may be a source of lag.
  501. // Mouse events are accumulated here to prevent excess function calls and allocations
  502. mouse_motion = true;
  503. mouse_x = sdl_event.motion.x;
  504. mouse_y = sdl_event.motion.y;
  505. mouse_dx += sdl_event.motion.xrel;
  506. mouse_dy += sdl_event.motion.yrel;
  507. break;
  508. }
  509. case SDL_MOUSEBUTTONDOWN:
  510. {
  511. mouse->press(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y);
  512. break;
  513. }
  514. case SDL_MOUSEBUTTONUP:
  515. {
  516. mouse->release(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y);
  517. break;
  518. }
  519. case SDL_MOUSEWHEEL:
  520. {
  521. int direction = (sdl_event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1 : 1;
  522. mouse->scroll(sdl_event.wheel.x * direction, sdl_event.wheel.y * direction);
  523. break;
  524. }
  525. case SDL_CONTROLLERBUTTONDOWN:
  526. {
  527. if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  528. {
  529. if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end())
  530. {
  531. input::gamepad_button button = input::sdl_button_table[sdl_event.cbutton.button];
  532. it->second->press(button);
  533. }
  534. }
  535. break;
  536. }
  537. case SDL_CONTROLLERBUTTONUP:
  538. {
  539. if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  540. {
  541. if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end())
  542. {
  543. input::gamepad_button button = input::sdl_button_table[sdl_event.cbutton.button];
  544. it->second->release(button);
  545. }
  546. }
  547. break;
  548. }
  549. case SDL_CONTROLLERAXISMOTION:
  550. {
  551. if (sdl_event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID)
  552. {
  553. if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end())
  554. {
  555. input::gamepad_axis axis = input::sdl_axis_table[sdl_event.caxis.axis];
  556. float value = sdl_event.caxis.value;
  557. static const float min = static_cast<float>(std::numeric_limits<std::int16_t>::min());
  558. static const float max = static_cast<float>(std::numeric_limits<std::int16_t>::max());
  559. value /= (value < 0.0f) ? -min : max;
  560. it->second->move(axis, value);
  561. }
  562. }
  563. break;
  564. }
  565. case SDL_CONTROLLERDEVICEADDED:
  566. {
  567. if (SDL_IsGameController(sdl_event.cdevice.which))
  568. {
  569. SDL_GameController* sdl_controller = SDL_GameControllerOpen(sdl_event.cdevice.which);
  570. std::string controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which);
  571. if (sdl_controller)
  572. {
  573. if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end())
  574. {
  575. logger->log("Reconnected gamepad \"" + controller_name + "\"");
  576. it->second->connect(true);
  577. }
  578. else
  579. {
  580. // Get gamepad GUID
  581. SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller);
  582. SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick);
  583. char guid_buffer[33];
  584. std::memset(guid_buffer, '\0', 33);
  585. SDL_JoystickGetGUIDString(sdl_guid, &guid_buffer[0], 33);
  586. std::string guid(guid_buffer);
  587. logger->log("Connected gamepad \"" + controller_name + "\" with GUID: " + guid + "");
  588. // Create new gamepad
  589. input::gamepad* gamepad = new input::gamepad();
  590. gamepad->set_event_dispatcher(event_dispatcher);
  591. gamepad->set_guid(guid);
  592. // Add gamepad to list and map
  593. gamepads.push_back(gamepad);
  594. gamepad_map[sdl_event.cdevice.which] = gamepad;
  595. // Send controller connected event
  596. gamepad->connect(false);
  597. }
  598. }
  599. else
  600. {
  601. logger->error("Failed to connected gamepad \"" + controller_name + "\"");
  602. }
  603. }
  604. break;
  605. }
  606. case SDL_CONTROLLERDEVICEREMOVED:
  607. {
  608. SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(sdl_event.cdevice.which);
  609. if (sdl_controller)
  610. {
  611. SDL_GameControllerClose(sdl_controller);
  612. logger->log("Disconnected gamepad");
  613. if (auto it = gamepad_map.find(sdl_event.cdevice.which); it != gamepad_map.end())
  614. {
  615. it->second->disconnect();
  616. }
  617. }
  618. break;
  619. }
  620. case SDL_WINDOWEVENT:
  621. {
  622. if (sdl_event.window.event == SDL_WINDOWEVENT_RESIZED)
  623. {
  624. window_resized();
  625. }
  626. break;
  627. }
  628. case SDL_QUIT:
  629. {
  630. close(EXIT_SUCCESS);
  631. break;
  632. }
  633. }
  634. }
  635. // Process accumulated mouse motion events
  636. if (mouse_motion)
  637. {
  638. mouse->move(mouse_x, mouse_y, mouse_dx, mouse_dy);
  639. }
  640. }
  641. void application::window_resized()
  642. {
  643. // Update window size and viewport size
  644. SDL_GetWindowSize(sdl_window, &window_dimensions[0], &window_dimensions[1]);
  645. SDL_GL_GetDrawableSize(sdl_window, &viewport_dimensions[0], &viewport_dimensions[1]);
  646. rasterizer->context_resized(viewport_dimensions[0], viewport_dimensions[1]);
  647. window_resized_event event;
  648. event.w = window_dimensions[0];
  649. event.h = window_dimensions[1];
  650. event_dispatcher->queue(event);
  651. }