diff --git a/include/frontend_settings.hpp b/include/frontend_settings.hpp index fa98175f..6fa8f2e1 100644 --- a/include/frontend_settings.hpp +++ b/include/frontend_settings.hpp @@ -28,6 +28,7 @@ struct FrontendSettings { WindowIcon icon = WindowIcon::Rpog; std::string language = "en"; bool showImGuiDebugPanel = true; + bool stretchImGuiOutputToWindow = false; static Theme themeFromString(std::string inString); static const char* themeToString(Theme theme); diff --git a/include/panda_sdl/frontend_sdl.hpp b/include/panda_sdl/frontend_sdl.hpp index 2a44dfc1..7d131899 100644 --- a/include/panda_sdl/frontend_sdl.hpp +++ b/include/panda_sdl/frontend_sdl.hpp @@ -35,6 +35,9 @@ class FrontendSDL { bool loadROM(const std::filesystem::path& path); void run(); std::optional selectGame(); + #ifdef IMGUI_FRONTEND + bool consumeReturnToSelector(); + #endif u32 getMapping(InputMappings::Scancode scancode) { return keyboardMappings.getMapping(scancode); } SDL_Window* window = nullptr; @@ -60,6 +63,7 @@ class FrontendSDL { bool keyboardAnalogX = false; bool keyboardAnalogY = false; bool emuPaused = false; + bool returnToSelector = false; private: void setupControllerSensors(SDL_GameController* controller); diff --git a/include/panda_sdl/imgui_layer.hpp b/include/panda_sdl/imgui_layer.hpp index dbc7498a..a7341f3b 100644 --- a/include/panda_sdl/imgui_layer.hpp +++ b/include/panda_sdl/imgui_layer.hpp @@ -26,6 +26,7 @@ class ImGuiLayer { void setPaused(bool paused) { isPaused = paused; } void setPauseCallback(std::function callback) { onPauseChange = std::move(callback); } void setVsyncCallback(std::function callback) { onVsyncChange = std::move(callback); } + void setExitToSelectorCallback(std::function callback) { onExitToSelector = std::move(callback); } private: void drawDebugPanel(); @@ -45,6 +46,7 @@ class ImGuiLayer { std::function onPauseChange; std::function onVsyncChange; + std::function onExitToSelector; }; #endif diff --git a/src/config.cpp b/src/config.cpp index 0baaf6a0..6ebfe269 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -153,6 +153,7 @@ void EmulatorConfig::load() { frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or(ui, "WindowIcon", "rpog")); frontendSettings.language = toml::find_or(ui, "Language", "en"); frontendSettings.showImGuiDebugPanel = toml::find_or(ui, "ShowImGuiDebugPanel", true); + frontendSettings.stretchImGuiOutputToWindow = toml::find_or(ui, "StretchImGuiOutputToWindow", false); } } } @@ -222,6 +223,7 @@ void EmulatorConfig::save() { data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon)); data["UI"]["Language"] = frontendSettings.language; data["UI"]["ShowImGuiDebugPanel"] = frontendSettings.showImGuiDebugPanel; + data["UI"]["StretchImGuiOutputToWindow"] = frontendSettings.stretchImGuiOutputToWindow; std::ofstream file(path, std::ios::out); file << data; diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index 37cbbf95..a4681f4d 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -124,6 +124,24 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC ScreenLayout::calculateCoordinates( screenCoordinates, u32(windowWidth), u32(windowHeight), emu.getConfig().topScreenSize, emu.getConfig().screenLayout ); + #ifdef IMGUI_FRONTEND + if (emu.getConfig().frontendSettings.stretchImGuiOutputToWindow) { + int drawableW = 0; + int drawableH = 0; + SDL_Window* currentWindow = existingWindow ? existingWindow : SDL_GL_GetCurrentWindow(); + if (currentWindow) { + SDL_GL_GetDrawableSize(currentWindow, &drawableW, &drawableH); + if (drawableW > 0 && drawableH > 0) { + windowWidth = u32(drawableW); + windowHeight = u32(drawableH); + emu.setOutputSize(windowWidth, windowHeight); + ScreenLayout::calculateCoordinates( + screenCoordinates, windowWidth, windowHeight, emu.getConfig().topScreenSize, emu.getConfig().screenLayout + ); + } + } + } + #endif if (needOpenGL) { #ifdef IMGUI_FRONTEND @@ -266,6 +284,12 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC imgui->init(); imgui->setPauseCallback([this](bool paused) { setPaused(paused); }); imgui->setVsyncCallback([this](bool enabled) { SDL_GL_SetSwapInterval(enabled ? 1 : 0); }); + imgui->setExitToSelectorCallback([this]() { + returnToSelector = true; + programRunning = false; + emu.reset(Emulator::ReloadOption::NoReload); + emu.romType = ROMType::None; + }); #endif } @@ -287,6 +311,23 @@ void FrontendSDL::run() { holdingRightClick = false; while (programRunning) { + #ifdef IMGUI_FRONTEND + const auto& cfg = emu.getConfig(); + if (cfg.frontendSettings.stretchImGuiOutputToWindow) { + int drawableW = 0; + int drawableH = 0; + SDL_GL_GetDrawableSize(window, &drawableW, &drawableH); + if (drawableW > 0 && drawableH > 0) { + windowWidth = u32(drawableW); + windowHeight = u32(drawableH); + emu.setOutputSize(windowWidth, windowHeight); + ScreenLayout::calculateCoordinates( + screenCoordinates, windowWidth, windowHeight, cfg.topScreenSize, cfg.screenLayout + ); + glViewport(0, 0, drawableW, drawableH); + } + } + #endif #ifdef PANDA3DS_ENABLE_HTTP_SERVER httpServer.processActions(); #endif @@ -559,12 +600,19 @@ void FrontendSDL::run() { case SDL_WINDOWEVENT: { auto type = event.window.event; if (type == SDL_WINDOWEVENT_RESIZED) { - windowWidth = event.window.data1; - windowHeight = event.window.data2; + int drawableW = event.window.data1; + int drawableH = event.window.data2; + #ifdef IMGUI_FRONTEND + if (emu.getConfig().frontendSettings.stretchImGuiOutputToWindow) { + SDL_GL_GetDrawableSize(window, &drawableW, &drawableH); + } + #endif + windowWidth = u32(drawableW); + windowHeight = u32(drawableH); const auto& config = emu.getConfig(); ScreenLayout::calculateCoordinates( - screenCoordinates, u32(windowWidth), u32(windowHeight), emu.getConfig().topScreenSize, emu.getConfig().screenLayout + screenCoordinates, windowWidth, windowHeight, config.topScreenSize, config.screenLayout ); emu.setOutputSize(windowWidth, windowHeight); @@ -640,13 +688,23 @@ void FrontendSDL::run() { } #ifdef IMGUI_FRONTEND - if (imgui) { + if (imgui && !returnToSelector) { imgui->shutdown(); imgui.reset(); } #endif } +#ifdef IMGUI_FRONTEND +bool FrontendSDL::consumeReturnToSelector() { + if (!returnToSelector) { + return false; + } + returnToSelector = false; + return true; +} +#endif + void FrontendSDL::setupControllerSensors(SDL_GameController* controller) { bool haveGyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; bool haveAccelerometer = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; diff --git a/src/panda_sdl/imgui_layer.cpp b/src/panda_sdl/imgui_layer.cpp index 3225fbb6..9a5b70a4 100644 --- a/src/panda_sdl/imgui_layer.cpp +++ b/src/panda_sdl/imgui_layer.cpp @@ -407,9 +407,13 @@ void ImGuiLayer::drawPausePanel() { showSettings = true; } if (ImGui::Button("Quit", ImVec2(-1, 0))) { - SDL_Event quit{}; - quit.type = SDL_QUIT; - SDL_PushEvent(&quit); + if (onExitToSelector) { + onExitToSelector(); + } else { + SDL_Event quit{}; + quit.type = SDL_QUIT; + SDL_PushEvent(&quit); + } } ImGui::End(); @@ -518,6 +522,7 @@ void ImGuiLayer::drawSettingsPanel() { cfg.frontendSettings.showImGuiDebugPanel = showDebugPanel; showDebug = showDebugPanel; } + ImGui::Checkbox("Stretch Output To Window", &cfg.frontendSettings.stretchImGuiOutputToWindow); } if (ImGui::CollapsingHeader("Graphics")) { diff --git a/src/panda_sdl/main.cpp b/src/panda_sdl/main.cpp index 876193c9..570aa57b 100644 --- a/src/panda_sdl/main.cpp +++ b/src/panda_sdl/main.cpp @@ -35,6 +35,30 @@ int emu_main(int argc, char *argv[]) { FrontendSDL app; #endif + #ifdef IMGUI_FRONTEND + std::optional forcedRom; + if (argc > 1) { + forcedRom = std::filesystem::current_path() / argv[1]; + } + + while (true) { + std::optional selected = forcedRom; + if (!selected.has_value()) { + selected = app.selectGame(); + if (!selected.has_value()) { + return 0; + } + } + if (!app.loadROM(*selected)) { + Helpers::panic("Failed to load ROM file: %s", selected->string().c_str()); + } + forcedRom.reset(); + app.run(); + if (!app.consumeReturnToSelector()) { + break; + } + } + #else if (argc > 1) { auto romPath = std::filesystem::current_path() / argv[1]; if (!app.loadROM(romPath)) { @@ -42,20 +66,11 @@ int emu_main(int argc, char *argv[]) { Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); } } else { - #ifdef IMGUI_FRONTEND - auto selected = app.selectGame(); - if (!selected.has_value()) { - return 0; - } - if (!app.loadROM(*selected)) { - Helpers::panic("Failed to load ROM file: %s", selected->string().c_str()); - } - #else printf("No ROM inserted! Load a ROM by dragging and dropping it into the emulator window!\n"); - #endif } app.run(); + #endif return 0; }