From b26da9d80d70cf3a90ea617f89997c528ab553f7 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Nov 2024 22:57:39 +0100 Subject: [PATCH] (Android) Added experimental support for running in multi-window mode --- es-app/src/SystemData.cpp | 7 +++ es-app/src/main.cpp | 23 +++++++++ es-app/src/views/ViewController.cpp | 79 +++++++++++++++++++++++++++++ es-app/src/views/ViewController.h | 6 +++ es-core/src/Window.cpp | 5 +- es-core/src/Window.h | 2 +- es-core/src/renderers/Renderer.cpp | 20 ++++++++ es-core/src/renderers/Renderer.h | 2 + es-core/src/resources/Font.cpp | 10 ++++ es-core/src/resources/Font.h | 31 ++++++++--- 10 files changed, 177 insertions(+), 8 deletions(-) diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 21836568b..94889f704 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -596,6 +596,13 @@ bool SystemData::loadConfig() sStartupExitSignal = true; return true; } +#if defined(__ANDROID__) + if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + ViewController::getInstance()->setWindowSizeChanged( + static_cast(event.window.data1), static_cast(event.window.data2)); + } +#endif }; std::string name; diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 7c91a7cf1..d1763446e 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -503,6 +503,15 @@ void applicationLoop() if (SDL_PollEvent(&event)) { do { #if defined(__ANDROID__) + if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + // This covers switching to/from multi-window mode. Note that the reload + // mechanism is rather ungraceful as it just forcekills any open windows, which + // is problematic if the scraper or theme downloader is running for instance. + ViewController::getInstance()->setWindowSizeChanged( + static_cast(event.window.data1), static_cast(event.window.data2)); + ViewController::getInstance()->checkWindowSizeChanged(); + } // Prevent that button presses get registered immediately when entering the // foreground (which most commonly mean we're returning from a game). // Also perform some other tasks on resume such as resetting timers. @@ -1101,7 +1110,16 @@ int main(int argc, char* argv[]) if (Settings::getInstance()->getBool("SplashScreen")) window->renderSplashScreen(Window::SplashScreenState::SCANNING, 0.0f); +#if defined(__ANDROID__) + while (SDL_PollEvent(&event)) { + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + ViewController::getInstance()->setWindowSizeChanged( + static_cast(event.window.data1), static_cast(event.window.data2)); + } + }; +#else while (SDL_PollEvent(&event)) {}; +#endif #if defined(_WIN64) // Hide taskbar if the setting for this is enabled. @@ -1249,6 +1267,11 @@ int main(int argc, char* argv[]) // Main application loop. +#if defined(__ANDROID__) + // If the window size changed during startup then we need to resize and reload. + ViewController::getInstance()->checkWindowSizeChanged(); +#endif + if (!SystemData::sStartupExitSignal) { #if defined(__EMSCRIPTEN__) emscripten_set_main_loop(&applicationLoop, 0, 1); diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index d08507a6b..86a419f19 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -13,6 +13,7 @@ #include "views/ViewController.h" #include "ApplicationUpdater.h" +#include "AudioManager.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" #include "InputManager.h" @@ -35,6 +36,10 @@ #include "views/GamelistView.h" #include "views/SystemView.h" +#if defined(__ANDROID__) +#include "utils/PlatformUtilAndroid.h" +#endif + ViewController::ViewController() noexcept : mRenderer {Renderer::getInstance()} , mNoGamesMessageBox {nullptr} @@ -49,6 +54,8 @@ ViewController::ViewController() noexcept , mFadeOpacity {0} , mCancelledTransition {false} , mNextSystem {false} + , mWindowChangedWidth {0} + , mWindowChangedHeight {0} { mState.viewing = ViewMode::NOTHING; mState.previouslyViewed = ViewMode::NOTHING; @@ -1373,6 +1380,13 @@ void ViewController::preload() SystemData::sStartupExitSignal = true; return; } +#if defined(__ANDROID__) + if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + setWindowSizeChanged(static_cast(event.window.data1), + static_cast(event.window.data2)); + } +#endif }; const std::string entryType {(*it)->isCustomCollection() ? "custom collection" : "system"}; @@ -1551,6 +1565,71 @@ void ViewController::reloadAll() updateHelpPrompts(); } +void ViewController::setWindowSizeChanged(const int width, const int height) +{ +#if defined(__ANDROID__) + const std::pair windowSize {Utils::Platform::Android::getWindowSize()}; + + if (windowSize.first == static_cast(mRenderer->getScreenWidth()) && + windowSize.second == static_cast(mRenderer->getScreenHeight())) { + mWindowChangedWidth = 0; + mWindowChangedHeight = 0; + } + else { + mWindowChangedWidth = windowSize.first; + mWindowChangedHeight = windowSize.second; + } +#endif +} + +void ViewController::checkWindowSizeChanged() +{ + if (mWindowChangedWidth == 0 || mWindowChangedHeight == 0) + return; + + LOG(LogInfo) << "Window size has changed from " << mRenderer->getScreenWidth() << "x" + << mRenderer->getScreenHeight() << " to " << mWindowChangedWidth << "x" + << mWindowChangedHeight << ", reloading..."; + + mWindowChangedWidth = 0; + mWindowChangedHeight = 0; + + mWindow->stopInfoPopup(); + + if (mState.viewing != ViewController::ViewMode::NOTHING) { + mWindow->stopScreensaver(); + mWindow->stopMediaViewer(); + mWindow->stopPDFViewer(); + } + + // This is done quite ungracefully, essentially forcekilling all open windows. + while (mWindow->getGuiStackSize() > 1) + mWindow->removeGui(mWindow->peekGui()); + + AudioManager::getInstance().deinit(); + mWindow->deinit(); + SDL_Delay(20); + AudioManager::getInstance().init(); + mWindow->init(true); + + mWindow->setLaunchedGame(false); + mWindow->invalidateCachedBackground(); + mWindow->renderSplashScreen(Window::SplashScreenState::RELOADING, 0.0f); + + if (mState.viewing != ViewController::ViewMode::NOTHING) { + reloadAll(); + goToStart(false); + resetCamera(); + } + else { + noGamesDialog(); + } + +#if defined(__ANDROID__) + InputOverlay::getInstance().init(); +#endif +} + void ViewController::rescanROMDirectory() { mWindow->setBlockInput(true); diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index 78a567abd..775cb6569 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -56,6 +56,10 @@ public: // Reload everything with a theme, used when the "Theme" setting changes. void reloadAll(); + // On window size changes we need to deinit/init the application, reload the systems etc. + void setWindowSizeChanged(const int width, const int height); + void checkWindowSizeChanged(); + // Rescan the ROM directory for any changes to games and systems. void rescanROMDirectory(); @@ -199,6 +203,8 @@ private: float mFadeOpacity; bool mCancelledTransition; // Needed only for the Fade transition style. bool mNextSystem; + int mWindowChangedWidth; + int mWindowChangedHeight; }; #endif // ES_APP_VIEWS_VIEW_CONTROLLER_H diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index af310e6f0..11dc0540e 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -107,7 +107,7 @@ GuiComponent* Window::peekGui() return mGuiStack.back(); } -bool Window::init() +bool Window::init(bool resized) { if (!mRenderer->init()) { LOG(LogError) << "Renderer failed to initialize."; @@ -133,6 +133,9 @@ bool Window::init() mDefaultFonts.push_back(Font::get(FONT_SIZE_LARGE_FIXED)); } + if (resized) + Font::updateFontSizes(); + if (mRenderer->getIsVerticalOrientation()) mSplash->setResize(mRenderer->getScreenWidth() * 0.8f, 0.0f); else diff --git a/es-core/src/Window.h b/es-core/src/Window.h index ff2d0b626..5fd4a7c55 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -93,7 +93,7 @@ public: int getGuiStackSize() { return static_cast(mGuiStack.size()); } bool isBackgroundDimmed(); - bool init(); + bool init(bool resized = false); void deinit(); void input(InputConfig* config, Input input); diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index 2a51ad3e1..281b7c70a 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -15,6 +15,10 @@ #include "renderers/ShaderOpenGL.h" #include "resources/ResourceManager.h" +#if defined(__ANDROID__) +#include "utils/PlatformUtilAndroid.h" +#endif + #if defined(_WIN64) #include #endif @@ -101,12 +105,26 @@ bool Renderer::createWindow() displayMode.h = displayBounds.h; #endif +#if defined(__ANDROID__) + const std::pair windowSize {Utils::Platform::Android::getWindowSize()}; + + if (windowSize.first != 0 && windowSize.second != 0) { + sScreenWidth = windowSize.first; + sScreenHeight = windowSize.second; + } + else { + sScreenWidth = displayMode.w; + sScreenHeight = displayMode.h; + } +#else sScreenWidth = Settings::getInstance()->getInt("ScreenWidth") ? Settings::getInstance()->getInt("ScreenWidth") : displayMode.w; sScreenHeight = Settings::getInstance()->getInt("ScreenHeight") ? Settings::getInstance()->getInt("ScreenHeight") : displayMode.h; +#endif + mScreenOffsetX = glm::clamp((Settings::getInstance()->getInt("ScreenOffsetX") ? Settings::getInstance()->getInt("ScreenOffsetX") : 0), @@ -228,6 +246,8 @@ bool Renderer::createWindow() return false; } + mDisplayIndex = displayIndex; + #if defined(__APPLE__) // The code below is required as the high DPI scaling on macOS is very bizarre and is // measured in "points" rather than pixels (even though the naming convention sure looks diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 7d624df06..f898a7c98 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -183,6 +183,7 @@ public: const glm::mat4& getProjectionMatrix() { return mProjectionMatrix; } const glm::mat4& getProjectionMatrixNormal() { return mProjectionMatrixNormal; } SDL_Window* getSDLWindow() { return mSDLWindow; } + const int getDisplayIndex() { return mDisplayIndex; } const int getScreenRotation() { return mScreenRotation; } static const bool getIsVerticalOrientation() { return sIsVerticalOrientation; } static const float getScreenWidth() { return static_cast(sScreenWidth); } @@ -249,6 +250,7 @@ private: static inline int sScreenWidth {0}; static inline int sScreenHeight {0}; + int mDisplayIndex {0}; int mScreenRotation {0}; bool mInitialCursorState {true}; static inline bool sIsVerticalOrientation {false}; diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 30f224b42..624452a02 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -118,6 +118,16 @@ std::shared_ptr Font::get(float size, const std::string& path) return font; } +void Font::updateFontSizes() +{ + getMiniFont(true); + getSmallFont(true); + getMediumFont(true); + getMediumFixedFont(true); + getLargeFont(true); + getLargeFixedFont(true); +} + glm::vec2 Font::sizeText(std::string text, float lineSpacing) { if (text == "") diff --git a/es-core/src/resources/Font.h b/es-core/src/resources/Font.h index 9c8bc33d4..31be1b2e4 100644 --- a/es-core/src/resources/Font.h +++ b/es-core/src/resources/Font.h @@ -37,46 +37,65 @@ class Font : public IReloadable public: virtual ~Font(); static std::shared_ptr get(float size, const std::string& path = getDefaultPath()); - static float getMiniFont() + static float getMiniFont(bool forceUpdate = false) { static float sMiniFont {0.030f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sMiniFont = 0.030f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sMiniFont; } - static float getSmallFont() + static float getSmallFont(bool forceUpdate = false) { static float sSmallFont {0.035f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sSmallFont = 0.035f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sSmallFont; } - static float getMediumFont() + static float getMediumFont(bool forceUpdate = false) { static float sMediumFont { (Renderer::getIsVerticalOrientation() ? 0.040f : 0.045f) * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sMediumFont = (Renderer::getIsVerticalOrientation() ? 0.040f : 0.045f) * + std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sMediumFont; } - static float getMediumFixedFont() + static float getMediumFixedFont(bool forceUpdate = false) { // Fixed size regardless of screen orientation. static float sMediumFixedFont { 0.045f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sMediumFixedFont = + 0.045f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sMediumFixedFont; } - static float getLargeFont() + static float getLargeFont(bool forceUpdate = false) { static float sLargeFont {(Renderer::getIsVerticalOrientation() ? 0.080f : 0.085f) * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sLargeFont = (Renderer::getIsVerticalOrientation() ? 0.080f : 0.085f) * + std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sLargeFont; } - static float getLargeFixedFont() + static float getLargeFixedFont(bool forceUpdate = false) { // Fixed size regardless of screen orientation. static float sLargeFixedFont { 0.085f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())}; + if (forceUpdate) + sLargeFixedFont = + 0.085f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()); return sLargeFixedFont; } + // Needed for when the application window has been resized. + static void updateFontSizes(); + // Returns the size of shaped text without applying any wrapping or abbreviations. glm::vec2 sizeText(std::string text, float lineSpacing = 1.5f);