diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/application/StarMainApplication_sdl.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/application/StarMainApplication_sdl.cpp')
-rw-r--r-- | source/application/StarMainApplication_sdl.cpp | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/source/application/StarMainApplication_sdl.cpp b/source/application/StarMainApplication_sdl.cpp new file mode 100644 index 0000000..503a8c4 --- /dev/null +++ b/source/application/StarMainApplication_sdl.cpp @@ -0,0 +1,692 @@ +#include "StarMainApplication.hpp" +#include "StarLogging.hpp" +#include "StarSignalHandler.hpp" +#include "StarTickRateMonitor.hpp" +#include "StarRenderer_opengl20.hpp" + +#include "SDL.h" +#include "StarPlatformServices_pc.hpp" + +namespace Star { + +Maybe<Key> keyFromSdlKeyCode(SDL_Keycode sym) { + static HashMap<int, Key> KeyCodeMap{ + {SDLK_BACKSPACE, Key::Backspace}, + {SDLK_TAB, Key::Tab}, + {SDLK_CLEAR, Key::Clear}, + {SDLK_RETURN, Key::Return}, + {SDLK_PAUSE, Key::Pause}, + {SDLK_ESCAPE, Key::Escape}, + {SDLK_SPACE, Key::Space}, + {SDLK_EXCLAIM, Key::Exclaim}, + {SDLK_QUOTEDBL, Key::QuotedBL}, + {SDLK_HASH, Key::Hash}, + {SDLK_DOLLAR, Key::Dollar}, + {SDLK_AMPERSAND, Key::Ampersand}, + {SDLK_QUOTE, Key::Quote}, + {SDLK_LEFTPAREN, Key::LeftParen}, + {SDLK_RIGHTPAREN, Key::RightParen}, + {SDLK_ASTERISK, Key::Asterisk}, + {SDLK_PLUS, Key::Plus}, + {SDLK_COMMA, Key::Comma}, + {SDLK_MINUS, Key::Minus}, + {SDLK_PERIOD, Key::Period}, + {SDLK_SLASH, Key::Slash}, + {SDLK_0, Key::Zero}, + {SDLK_1, Key::One}, + {SDLK_2, Key::Two}, + {SDLK_3, Key::Three}, + {SDLK_4, Key::Four}, + {SDLK_5, Key::Five}, + {SDLK_6, Key::Six}, + {SDLK_7, Key::Seven}, + {SDLK_8, Key::Eight}, + {SDLK_9, Key::Nine}, + {SDLK_COLON, Key::Colon}, + {SDLK_SEMICOLON, Key::Semicolon}, + {SDLK_LESS, Key::Less}, + {SDLK_EQUALS, Key::Equals}, + {SDLK_GREATER, Key::Greater}, + {SDLK_QUESTION, Key::Question}, + {SDLK_AT, Key::At}, + {SDLK_LEFTBRACKET, Key::LeftBracket}, + {SDLK_BACKSLASH, Key::Backslash}, + {SDLK_RIGHTBRACKET, Key::RightBracket}, + {SDLK_CARET, Key::Caret}, + {SDLK_UNDERSCORE, Key::Underscore}, + {SDLK_BACKQUOTE, Key::Backquote}, + {SDLK_a, Key::A}, + {SDLK_b, Key::B}, + {SDLK_c, Key::C}, + {SDLK_d, Key::D}, + {SDLK_e, Key::E}, + {SDLK_f, Key::F}, + {SDLK_g, Key::G}, + {SDLK_h, Key::H}, + {SDLK_i, Key::I}, + {SDLK_j, Key::J}, + {SDLK_k, Key::K}, + {SDLK_l, Key::L}, + {SDLK_m, Key::M}, + {SDLK_n, Key::N}, + {SDLK_o, Key::O}, + {SDLK_p, Key::P}, + {SDLK_q, Key::Q}, + {SDLK_r, Key::R}, + {SDLK_s, Key::S}, + {SDLK_t, Key::T}, + {SDLK_u, Key::U}, + {SDLK_v, Key::V}, + {SDLK_w, Key::W}, + {SDLK_x, Key::X}, + {SDLK_y, Key::Y}, + {SDLK_z, Key::Z}, + {SDLK_DELETE, Key::Delete}, + {SDLK_KP_0, Key::Kp0}, + {SDLK_KP_1, Key::Kp1}, + {SDLK_KP_2, Key::Kp2}, + {SDLK_KP_3, Key::Kp3}, + {SDLK_KP_4, Key::Kp4}, + {SDLK_KP_5, Key::Kp5}, + {SDLK_KP_6, Key::Kp6}, + {SDLK_KP_7, Key::Kp7}, + {SDLK_KP_8, Key::Kp8}, + {SDLK_KP_9, Key::Kp9}, + {SDLK_KP_PERIOD, Key::Kp_period}, + {SDLK_KP_DIVIDE, Key::Kp_divide}, + {SDLK_KP_MULTIPLY, Key::Kp_multiply}, + {SDLK_KP_MINUS, Key::Kp_minus}, + {SDLK_KP_PLUS, Key::Kp_plus}, + {SDLK_KP_ENTER, Key::Kp_enter}, + {SDLK_KP_EQUALS, Key::Kp_equals}, + {SDLK_UP, Key::Up}, + {SDLK_DOWN, Key::Down}, + {SDLK_RIGHT, Key::Right}, + {SDLK_LEFT, Key::Left}, + {SDLK_INSERT, Key::Insert}, + {SDLK_HOME, Key::Home}, + {SDLK_END, Key::End}, + {SDLK_PAGEUP, Key::PageUp}, + {SDLK_PAGEDOWN, Key::PageDown}, + {SDLK_F1, Key::F1}, + {SDLK_F2, Key::F2}, + {SDLK_F3, Key::F3}, + {SDLK_F4, Key::F4}, + {SDLK_F5, Key::F5}, + {SDLK_F6, Key::F6}, + {SDLK_F7, Key::F7}, + {SDLK_F8, Key::F8}, + {SDLK_F9, Key::F9}, + {SDLK_F10, Key::F10}, + {SDLK_F11, Key::F11}, + {SDLK_F12, Key::F12}, + {SDLK_F13, Key::F13}, + {SDLK_F14, Key::F14}, + {SDLK_F15, Key::F15}, + {SDLK_NUMLOCKCLEAR, Key::NumLock}, + {SDLK_CAPSLOCK, Key::CapsLock}, + {SDLK_SCROLLLOCK, Key::ScrollLock}, + {SDLK_RSHIFT, Key::RShift}, + {SDLK_LSHIFT, Key::LShift}, + {SDLK_RCTRL, Key::RCtrl}, + {SDLK_LCTRL, Key::LCtrl}, + {SDLK_RALT, Key::RAlt}, + {SDLK_LALT, Key::LAlt}, + {SDLK_RGUI, Key::RGui}, + {SDLK_LGUI, Key::LGui}, + {SDLK_MODE, Key::AltGr}, + {SDLK_APPLICATION, Key::Compose}, + {SDLK_HELP, Key::Help}, + {SDLK_PRINTSCREEN, Key::PrintScreen}, + {SDLK_SYSREQ, Key::SysReq}, + {SDLK_PAUSE, Key::Pause}, + {SDLK_MENU, Key::Menu}, + {SDLK_POWER, Key::Power} + }; + + return KeyCodeMap.maybe(sym); +} + +KeyMod keyModsFromSdlKeyMods(uint16_t mod) { + KeyMod keyMod = KeyMod::NoMod; + + if (mod & KMOD_LSHIFT) + keyMod |= KeyMod::LShift; + if (mod & KMOD_RSHIFT) + keyMod |= KeyMod::RShift; + if (mod & KMOD_LCTRL) + keyMod |= KeyMod::LCtrl; + if (mod & KMOD_RCTRL) + keyMod |= KeyMod::RCtrl; + if (mod & KMOD_LALT) + keyMod |= KeyMod::LAlt; + if (mod & KMOD_RALT) + keyMod |= KeyMod::RAlt; + if (mod & KMOD_LGUI) + keyMod |= KeyMod::LGui; + if (mod & KMOD_RGUI) + keyMod |= KeyMod::RGui; + if (mod & KMOD_NUM) + keyMod |= KeyMod::Num; + if (mod & KMOD_CAPS) + keyMod |= KeyMod::Caps; + if (mod & KMOD_MODE) + keyMod |= KeyMod::AltGr; + + return keyMod; +} + +MouseButton mouseButtonFromSdlMouseButton(uint8_t button) { + if (button == SDL_BUTTON_LEFT) + return MouseButton::Left; + else if (button == SDL_BUTTON_MIDDLE) + return MouseButton::Middle; + else if (button == SDL_BUTTON_RIGHT) + return MouseButton::Right; + else if (button == SDL_BUTTON_X1) + return MouseButton::FourthButton; + else + return MouseButton::FifthButton; +} + +class SdlPlatform { +public: + SdlPlatform(ApplicationUPtr application, StringList cmdLineArgs) { + m_application = move(application); + + // extract application path from command line args + String applicationPath = cmdLineArgs.first(); + cmdLineArgs = cmdLineArgs.slice(1); + + StringList platformArguments; + eraseWhere(cmdLineArgs, [&platformArguments](String& argument) { + if (argument.beginsWith("+platform")) { + platformArguments.append(move(argument)); + return true; + } + return false; + }); + + Logger::info("Application: Initializing SDL"); + if (SDL_Init(0)) + throw ApplicationException(strf("Couldn't initialize SDL: %s", SDL_GetError())); + + if (char* basePath = SDL_GetBasePath()) { + File::changeDirectory(basePath); + SDL_free(basePath); + } + + m_signalHandler.setHandleInterrupt(true); + m_signalHandler.setHandleFatal(true); + + try { + Logger::info("Application: startup..."); + m_application->startup(cmdLineArgs); + } catch (std::exception const& e) { + throw ApplicationException("Application threw exception during startup", e); + } + + Logger::info("Application: Initializing SDL Video"); + if (SDL_InitSubSystem(SDL_INIT_VIDEO)) + throw ApplicationException(strf("Couldn't initialize SDL Video: %s", SDL_GetError())); + + Logger::info("Application: Initializing SDL Joystick"); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK)) + throw ApplicationException(strf("Couldn't initialize SDL Joystick: %s", SDL_GetError())); + + Logger::info("Application: Initializing SDL Sound"); + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) + throw ApplicationException(strf("Couldn't initialize SDL Sound: %s", SDL_GetError())); + + SDL_JoystickEventState(SDL_ENABLE); + + m_platformServices = PcPlatformServices::create(applicationPath, platformArguments); + if (!m_platformServices) + Logger::info("Application: No platform services available"); + + Logger::info("Application: Creating SDL Window"); + m_sdlWindow = SDL_CreateWindow(m_windowTitle.utf8Ptr(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + m_windowSize[0], m_windowSize[1], SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + if (!m_sdlWindow) + throw ApplicationException::format("Application: Could not create SDL window: %s", SDL_GetError()); + + SDL_ShowWindow(m_sdlWindow); + SDL_RaiseWindow(m_sdlWindow); + + int width; + int height; + SDL_GetWindowSize(m_sdlWindow, &width, &height); + m_windowSize = Vec2U(width, height); + + m_sdlGlContext = SDL_GL_CreateContext(m_sdlWindow); + if (!m_sdlGlContext) + throw ApplicationException::format("Application: Could not create OpenGL context: %s", SDL_GetError()); + + setVSyncEnabled(m_windowVSync); + + SDL_StopTextInput(); + + SDL_AudioSpec desired = {}; + desired.freq = 44100; + desired.format = AUDIO_S16SYS; + desired.samples = 2048; + desired.channels = 2; + desired.userdata = this; + desired.callback = [](void* userdata, Uint8* stream, int len) { + ((SdlPlatform*)(userdata))->getAudioData(stream, len); + }; + + SDL_AudioSpec obtained = {}; + if (SDL_OpenAudio(&desired, &obtained) < 0) { + Logger::error("Application: Could not open audio device, no sound available!"); + } else if (obtained.freq != desired.freq || obtained.channels != desired.channels || obtained.format != desired.format) { + SDL_CloseAudio(); + Logger::error("Application: Could not open 44.1khz / 16 bit stereo audio device, no sound available!"); + } else { + Logger::info("Application: Opened default audio device with 44.1khz / 16 bit stereo audio, %s sample size buffer", obtained.samples); + } + + m_renderer = make_shared<OpenGl20Renderer>(); + m_renderer->setScreenSize(m_windowSize); + } + + ~SdlPlatform() { + SDL_CloseAudio(); + + m_renderer.reset(); + + Logger::info("Application: Destroying SDL Window"); + SDL_DestroyWindow(m_sdlWindow); + + SDL_Quit(); + } + + void run() { + try { + Logger::info("Application: initialization..."); + m_application->applicationInit(make_shared<Controller>(this)); + + Logger::info("Application: renderer initialization..."); + m_application->renderInit(m_renderer); + + Logger::info("Application: main update loop..."); + + m_updateTicker.reset(); + m_renderTicker.reset(); + + bool quit = false; + while (true) { + for (auto const& event : processEvents()) + m_application->processInput(event); + + if (m_platformServices) + m_platformServices->update(); + + if (m_platformServices->overlayActive()) + SDL_ShowCursor(1); + else + SDL_ShowCursor(m_cursorVisible ? 1 : 0); + + int updatesBehind = max<int>(round(m_updateTicker.ticksBehind()), 1); + updatesBehind = min<int>(updatesBehind, m_maxFrameSkip + 1); + for (int i = 0; i < updatesBehind; ++i) { + m_application->update(); + m_updateRate = m_updateTicker.tick(); + } + + m_renderer->startFrame(); + m_application->render(); + m_renderer->finishFrame(); + SDL_GL_SwapWindow(m_sdlWindow); + m_renderRate = m_renderTicker.tick(); + + if (m_quitRequested) { + Logger::info("Application: quit requested"); + quit = true; + } + + if (m_signalHandler.interruptCaught()) { + Logger::info("Application: Interrupt caught"); + quit = true; + } + + if (quit) { + Logger::info("Application: quitting..."); + break; + } + + int64_t spareMilliseconds = round(m_updateTicker.spareTime() * 1000); + if (spareMilliseconds > 0) + Thread::sleepPrecise(spareMilliseconds); + } + } catch (std::exception const& e) { + Logger::error("Application: exception thrown, shutting down: %s", outputException(e, true)); + } + + try { + Logger::info("Application: shutdown..."); + m_application->shutdown(); + } catch (std::exception const& e) { + Logger::error("Application: threw exception during shutdown: %s", outputException(e, true)); + } + + SDL_CloseAudio(); + m_application.reset(); + } + +private: + struct Controller : public ApplicationController { + Controller(SdlPlatform* parent) + : parent(parent) {} + + Maybe<String> getClipboard() override { + if (SDL_HasClipboardText()) + return String(SDL_GetClipboardText()); + return {}; + } + + void setClipboard(String text) override { + SDL_SetClipboardText(text.utf8Ptr()); + } + + void setTargetUpdateRate(float targetUpdateRate) override { + parent->m_updateTicker.setTargetTickRate(targetUpdateRate); + } + + void setUpdateTrackWindow(float updateTrackWindow) override { + parent->m_updateTicker.setWindow(updateTrackWindow); + } + + void setApplicationTitle(String title) override { + parent->m_windowTitle = move(title); + if (parent->m_sdlWindow) + SDL_SetWindowTitle(parent->m_sdlWindow, parent->m_windowTitle.utf8Ptr()); + } + + void setFullscreenWindow(Vec2U fullScreenResolution) override { + if (parent->m_windowMode != WindowMode::Fullscreen || parent->m_windowSize != fullScreenResolution) { + SDL_DisplayMode requestedDisplayMode = {SDL_PIXELFORMAT_RGB888, (int)fullScreenResolution[0], (int)fullScreenResolution[1], 0, 0}; + int currentDisplayIndex = SDL_GetWindowDisplayIndex(parent->m_sdlWindow); + + SDL_DisplayMode targetDisplayMode; + if (SDL_GetClosestDisplayMode(currentDisplayIndex, &requestedDisplayMode, &targetDisplayMode) != NULL) { + if (SDL_SetWindowDisplayMode(parent->m_sdlWindow, &requestedDisplayMode) == 0) { + if (parent->m_windowMode == WindowMode::Fullscreen) + SDL_SetWindowFullscreen(parent->m_sdlWindow, 0); + else + parent->m_windowMode = WindowMode::Fullscreen; + SDL_SetWindowFullscreen(parent->m_sdlWindow, SDL_WINDOW_FULLSCREEN); + } else { + Logger::warn("Failed to set resolution %s, %s", (unsigned)requestedDisplayMode.w, (unsigned)requestedDisplayMode.h); + } + } else { + Logger::warn("Unable to set requested display resolution %s, %s", (int)fullScreenResolution[0], (int)fullScreenResolution[1]); + } + + SDL_DisplayMode actualDisplayMode; + if (SDL_GetWindowDisplayMode(parent->m_sdlWindow, &actualDisplayMode) == 0) { + parent->m_windowSize = {(unsigned)actualDisplayMode.w, (unsigned)actualDisplayMode.h}; + + // call these manually since no SDL_WindowEvent is triggered when changing between fullscreen resolutions for some reason + parent->m_renderer->setScreenSize(parent->m_windowSize); + parent->m_application->windowChanged(parent->m_windowMode, parent->m_windowSize); + } else { + Logger::error("Couldn't get window display mode!"); + } + } + } + + void setNormalWindow(Vec2U windowSize) override { + if (parent->m_windowMode != WindowMode::Normal || parent->m_windowSize != windowSize) { + if (parent->m_windowMode == WindowMode::Fullscreen || parent->m_windowMode == WindowMode::Borderless) + SDL_SetWindowFullscreen(parent->m_sdlWindow, 0); + else if (parent->m_windowMode == WindowMode::Maximized) + SDL_RestoreWindow(parent->m_sdlWindow); + + SDL_SetWindowSize(parent->m_sdlWindow, windowSize[0], windowSize[1]); + SDL_SetWindowPosition(parent->m_sdlWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + + parent->m_windowMode = WindowMode::Normal; + parent->m_windowSize = windowSize; + } + } + + void setMaximizedWindow() override { + if (parent->m_windowMode != WindowMode::Maximized) { + if (parent->m_windowMode == WindowMode::Fullscreen || parent->m_windowMode == WindowMode::Borderless) + SDL_SetWindowFullscreen(parent->m_sdlWindow, 0); + + SDL_MaximizeWindow(parent->m_sdlWindow); + parent->m_windowMode = WindowMode::Maximized; + } + } + + void setBorderlessWindow() override { + if (parent->m_windowMode != WindowMode::Borderless) { + SDL_SetWindowFullscreen(parent->m_sdlWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); + parent->m_windowMode = WindowMode::Borderless; + + SDL_DisplayMode actualDisplayMode; + if (SDL_GetWindowDisplayMode(parent->m_sdlWindow, &actualDisplayMode) == 0) { + parent->m_windowSize = {(unsigned)actualDisplayMode.w, (unsigned)actualDisplayMode.h}; + + parent->m_renderer->setScreenSize(parent->m_windowSize); + parent->m_application->windowChanged(parent->m_windowMode, parent->m_windowSize); + } else { + Logger::error("Couldn't get window display mode!"); + } + } + } + + void setVSyncEnabled(bool vSync) override { + if (parent->m_windowVSync != vSync) { + parent->setVSyncEnabled(vSync); + parent->m_windowVSync = vSync; + } + } + + void setMaxFrameSkip(unsigned maxFrameSkip) override { + parent->m_maxFrameSkip = maxFrameSkip; + } + + void setCursorVisible(bool cursorVisible) override { + parent->m_cursorVisible = cursorVisible; + } + + void setAcceptingTextInput(bool acceptingTextInput) override { + if (acceptingTextInput != parent->m_acceptingTextInput) { + if (acceptingTextInput) + SDL_StartTextInput(); + else + SDL_StopTextInput(); + + parent->m_acceptingTextInput = acceptingTextInput; + } + } + + AudioFormat enableAudio() override { + parent->m_audioEnabled = true; + SDL_PauseAudio(false); + return AudioFormat{44100, 2}; + } + + void disableAudio() override { + parent->m_audioEnabled = false; + SDL_PauseAudio(true); + } + + float updateRate() const override { + return parent->m_updateRate; + } + + float renderFps() const override { + return parent->m_renderRate; + } + + StatisticsServicePtr statisticsService() const override { + if (parent->m_platformServices) + return parent->m_platformServices->statisticsService(); + return {}; + } + + P2PNetworkingServicePtr p2pNetworkingService() const override { + if (parent->m_platformServices) + return parent->m_platformServices->p2pNetworkingService(); + return {}; + } + + UserGeneratedContentServicePtr userGeneratedContentService() const override { + if (parent->m_platformServices) + return parent->m_platformServices->userGeneratedContentService(); + return {}; + } + + DesktopServicePtr desktopService() const override { + if (parent->m_platformServices) + return parent->m_platformServices->desktopService(); + return {}; + } + + void quit() override { + parent->m_quitRequested = true; + } + + SdlPlatform* parent; + }; + + List<InputEvent> processEvents() { + List<InputEvent> inputEvents; + + SDL_Event event; + while (SDL_PollEvent(&event)) { + Maybe<InputEvent> starEvent; + if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_MAXIMIZED || event.window.event == SDL_WINDOWEVENT_RESTORED) { + auto windowFlags = SDL_GetWindowFlags(m_sdlWindow); + + if (windowFlags & SDL_WINDOW_MAXIMIZED) { + m_windowMode = WindowMode::Maximized; + } else if (windowFlags & SDL_WINDOW_FULLSCREEN || windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) { + if (m_windowMode != WindowMode::Fullscreen && m_windowMode != WindowMode::Borderless) + m_windowMode = WindowMode::Fullscreen; + } else { + m_windowMode = WindowMode::Normal; + } + + m_application->windowChanged(m_windowMode, m_windowSize); + + } else if (event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + m_windowSize = Vec2U(event.window.data1, event.window.data2); + m_renderer->setScreenSize(m_windowSize); + m_application->windowChanged(m_windowMode, m_windowSize); + } + + } else if (event.type == SDL_KEYDOWN) { + if (!event.key.repeat) { + if (auto key = keyFromSdlKeyCode(event.key.keysym.sym)) + starEvent.set(KeyDownEvent{*key, keyModsFromSdlKeyMods(event.key.keysym.mod)}); + } + + } else if (event.type == SDL_KEYUP) { + if (auto key = keyFromSdlKeyCode(event.key.keysym.sym)) + starEvent.set(KeyUpEvent{*key}); + + } else if (event.type == SDL_TEXTINPUT) { + starEvent.set(TextInputEvent{String(event.text.text)}); + + } else if (event.type == SDL_MOUSEMOTION) { + starEvent.set(MouseMoveEvent{ + {event.motion.xrel, -event.motion.yrel}, {event.motion.x, (int)m_windowSize[1] - event.motion.y}}); + + } else if (event.type == SDL_MOUSEBUTTONDOWN) { + starEvent.set(MouseButtonDownEvent{mouseButtonFromSdlMouseButton(event.button.button), + {event.button.x, (int)m_windowSize[1] - event.button.y}}); + + } else if (event.type == SDL_MOUSEBUTTONUP) { + starEvent.set(MouseButtonUpEvent{mouseButtonFromSdlMouseButton(event.button.button), + {event.button.x, (int)m_windowSize[1] - event.button.y}}); + + } else if (event.type == SDL_MOUSEWHEEL) { + int x, y; + SDL_GetMouseState(&x, &y); + starEvent.set(MouseWheelEvent{event.wheel.y < 0 ? MouseWheel::Down : MouseWheel::Up, {x, (int)m_windowSize[1] - y}}); + + } else if (event.type == SDL_QUIT) { + m_quitRequested = true; + starEvent.reset(); + } + + if (starEvent) + inputEvents.append(starEvent.take()); + } + + return inputEvents; + } + + void getAudioData(Uint8* stream, int len) { + if (m_audioEnabled) { + m_application->getAudioData((int16_t*)stream, len / 4); + } else { + for (int i = 0; i < len; ++i) + stream[i] = 0; + } + } + + void setVSyncEnabled(bool vsyncEnabled) { + if (vsyncEnabled) { + // If VSync is requested, try for late swap tearing first, then fall back + // to regular VSync + Logger::info("Application: Enabling VSync with late swap tearing"); + if (SDL_GL_SetSwapInterval(-1) < 0) { + Logger::info("Application: Enabling VSync late swap tearing failed, falling back to full VSync"); + SDL_GL_SetSwapInterval(1); + } + } else { + Logger::info("Application: Disabling VSync"); + SDL_GL_SetSwapInterval(0); + } + } + + SignalHandler m_signalHandler; + + TickRateApproacher m_updateTicker = TickRateApproacher(60.0f, 1.0f); + float m_updateRate = 0.0f; + TickRateMonitor m_renderTicker = TickRateMonitor(1.0f); + float m_renderRate = 0.0f; + + SDL_Window* m_sdlWindow = nullptr; + SDL_GLContext m_sdlGlContext = nullptr; + + Vec2U m_windowSize = {800, 600}; + WindowMode m_windowMode = WindowMode::Normal; + + String m_windowTitle = ""; + bool m_windowVSync = true; + unsigned m_maxFrameSkip = 5; + bool m_cursorVisible = true; + bool m_acceptingTextInput = false; + bool m_audioEnabled = false; + bool m_quitRequested = false; + + OpenGl20RendererPtr m_renderer; + ApplicationUPtr m_application; + PcPlatformServicesUPtr m_platformServices; +}; + +int runMainApplication(ApplicationUPtr application, StringList cmdLineArgs) { + try { + { + SdlPlatform platform(move(application), move(cmdLineArgs)); + platform.run(); + } + Logger::info("Application: stopped gracefully"); + return 0; + } catch (std::exception const& e) { + fatalException(e, true); + } catch (...) { + fatalError("Unknown Exception", true); + } + return 1; +} + +} |