feat: add support for stretching ImGui output to window and return to selector functionality
This commit is contained in:
@@ -28,6 +28,7 @@ struct FrontendSettings {
|
|||||||
WindowIcon icon = WindowIcon::Rpog;
|
WindowIcon icon = WindowIcon::Rpog;
|
||||||
std::string language = "en";
|
std::string language = "en";
|
||||||
bool showImGuiDebugPanel = true;
|
bool showImGuiDebugPanel = true;
|
||||||
|
bool stretchImGuiOutputToWindow = false;
|
||||||
|
|
||||||
static Theme themeFromString(std::string inString);
|
static Theme themeFromString(std::string inString);
|
||||||
static const char* themeToString(Theme theme);
|
static const char* themeToString(Theme theme);
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class FrontendSDL {
|
|||||||
bool loadROM(const std::filesystem::path& path);
|
bool loadROM(const std::filesystem::path& path);
|
||||||
void run();
|
void run();
|
||||||
std::optional<std::filesystem::path> selectGame();
|
std::optional<std::filesystem::path> selectGame();
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
bool consumeReturnToSelector();
|
||||||
|
#endif
|
||||||
u32 getMapping(InputMappings::Scancode scancode) { return keyboardMappings.getMapping(scancode); }
|
u32 getMapping(InputMappings::Scancode scancode) { return keyboardMappings.getMapping(scancode); }
|
||||||
|
|
||||||
SDL_Window* window = nullptr;
|
SDL_Window* window = nullptr;
|
||||||
@@ -60,6 +63,7 @@ class FrontendSDL {
|
|||||||
bool keyboardAnalogX = false;
|
bool keyboardAnalogX = false;
|
||||||
bool keyboardAnalogY = false;
|
bool keyboardAnalogY = false;
|
||||||
bool emuPaused = false;
|
bool emuPaused = false;
|
||||||
|
bool returnToSelector = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupControllerSensors(SDL_GameController* controller);
|
void setupControllerSensors(SDL_GameController* controller);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class ImGuiLayer {
|
|||||||
void setPaused(bool paused) { isPaused = paused; }
|
void setPaused(bool paused) { isPaused = paused; }
|
||||||
void setPauseCallback(std::function<void(bool)> callback) { onPauseChange = std::move(callback); }
|
void setPauseCallback(std::function<void(bool)> callback) { onPauseChange = std::move(callback); }
|
||||||
void setVsyncCallback(std::function<void(bool)> callback) { onVsyncChange = std::move(callback); }
|
void setVsyncCallback(std::function<void(bool)> callback) { onVsyncChange = std::move(callback); }
|
||||||
|
void setExitToSelectorCallback(std::function<void()> callback) { onExitToSelector = std::move(callback); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawDebugPanel();
|
void drawDebugPanel();
|
||||||
@@ -45,6 +46,7 @@ class ImGuiLayer {
|
|||||||
|
|
||||||
std::function<void(bool)> onPauseChange;
|
std::function<void(bool)> onPauseChange;
|
||||||
std::function<void(bool)> onVsyncChange;
|
std::function<void(bool)> onVsyncChange;
|
||||||
|
std::function<void()> onExitToSelector;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ void EmulatorConfig::load() {
|
|||||||
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or<std::string>(ui, "WindowIcon", "rpog"));
|
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or<std::string>(ui, "WindowIcon", "rpog"));
|
||||||
frontendSettings.language = toml::find_or<std::string>(ui, "Language", "en");
|
frontendSettings.language = toml::find_or<std::string>(ui, "Language", "en");
|
||||||
frontendSettings.showImGuiDebugPanel = toml::find_or<toml::boolean>(ui, "ShowImGuiDebugPanel", true);
|
frontendSettings.showImGuiDebugPanel = toml::find_or<toml::boolean>(ui, "ShowImGuiDebugPanel", true);
|
||||||
|
frontendSettings.stretchImGuiOutputToWindow = toml::find_or<toml::boolean>(ui, "StretchImGuiOutputToWindow", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,6 +223,7 @@ void EmulatorConfig::save() {
|
|||||||
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));
|
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));
|
||||||
data["UI"]["Language"] = frontendSettings.language;
|
data["UI"]["Language"] = frontendSettings.language;
|
||||||
data["UI"]["ShowImGuiDebugPanel"] = frontendSettings.showImGuiDebugPanel;
|
data["UI"]["ShowImGuiDebugPanel"] = frontendSettings.showImGuiDebugPanel;
|
||||||
|
data["UI"]["StretchImGuiOutputToWindow"] = frontendSettings.stretchImGuiOutputToWindow;
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::out);
|
std::ofstream file(path, std::ios::out);
|
||||||
file << data;
|
file << data;
|
||||||
|
|||||||
@@ -124,6 +124,24 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC
|
|||||||
ScreenLayout::calculateCoordinates(
|
ScreenLayout::calculateCoordinates(
|
||||||
screenCoordinates, u32(windowWidth), u32(windowHeight), emu.getConfig().topScreenSize, emu.getConfig().screenLayout
|
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) {
|
if (needOpenGL) {
|
||||||
#ifdef IMGUI_FRONTEND
|
#ifdef IMGUI_FRONTEND
|
||||||
@@ -266,6 +284,12 @@ void FrontendSDL::initialize(SDL_Window* existingWindow, SDL_GLContext existingC
|
|||||||
imgui->init();
|
imgui->init();
|
||||||
imgui->setPauseCallback([this](bool paused) { setPaused(paused); });
|
imgui->setPauseCallback([this](bool paused) { setPaused(paused); });
|
||||||
imgui->setVsyncCallback([this](bool enabled) { SDL_GL_SetSwapInterval(enabled ? 1 : 0); });
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +311,23 @@ void FrontendSDL::run() {
|
|||||||
holdingRightClick = false;
|
holdingRightClick = false;
|
||||||
|
|
||||||
while (programRunning) {
|
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
|
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||||
httpServer.processActions();
|
httpServer.processActions();
|
||||||
#endif
|
#endif
|
||||||
@@ -559,12 +600,19 @@ void FrontendSDL::run() {
|
|||||||
case SDL_WINDOWEVENT: {
|
case SDL_WINDOWEVENT: {
|
||||||
auto type = event.window.event;
|
auto type = event.window.event;
|
||||||
if (type == SDL_WINDOWEVENT_RESIZED) {
|
if (type == SDL_WINDOWEVENT_RESIZED) {
|
||||||
windowWidth = event.window.data1;
|
int drawableW = event.window.data1;
|
||||||
windowHeight = event.window.data2;
|
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();
|
const auto& config = emu.getConfig();
|
||||||
ScreenLayout::calculateCoordinates(
|
ScreenLayout::calculateCoordinates(
|
||||||
screenCoordinates, u32(windowWidth), u32(windowHeight), emu.getConfig().topScreenSize, emu.getConfig().screenLayout
|
screenCoordinates, windowWidth, windowHeight, config.topScreenSize, config.screenLayout
|
||||||
);
|
);
|
||||||
|
|
||||||
emu.setOutputSize(windowWidth, windowHeight);
|
emu.setOutputSize(windowWidth, windowHeight);
|
||||||
@@ -640,13 +688,23 @@ void FrontendSDL::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IMGUI_FRONTEND
|
#ifdef IMGUI_FRONTEND
|
||||||
if (imgui) {
|
if (imgui && !returnToSelector) {
|
||||||
imgui->shutdown();
|
imgui->shutdown();
|
||||||
imgui.reset();
|
imgui.reset();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
bool FrontendSDL::consumeReturnToSelector() {
|
||||||
|
if (!returnToSelector) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
returnToSelector = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void FrontendSDL::setupControllerSensors(SDL_GameController* controller) {
|
void FrontendSDL::setupControllerSensors(SDL_GameController* controller) {
|
||||||
bool haveGyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
|
bool haveGyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
|
||||||
bool haveAccelerometer = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
|
bool haveAccelerometer = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
|
||||||
|
|||||||
@@ -407,9 +407,13 @@ void ImGuiLayer::drawPausePanel() {
|
|||||||
showSettings = true;
|
showSettings = true;
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Quit", ImVec2(-1, 0))) {
|
if (ImGui::Button("Quit", ImVec2(-1, 0))) {
|
||||||
SDL_Event quit{};
|
if (onExitToSelector) {
|
||||||
quit.type = SDL_QUIT;
|
onExitToSelector();
|
||||||
SDL_PushEvent(&quit);
|
} else {
|
||||||
|
SDL_Event quit{};
|
||||||
|
quit.type = SDL_QUIT;
|
||||||
|
SDL_PushEvent(&quit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@@ -518,6 +522,7 @@ void ImGuiLayer::drawSettingsPanel() {
|
|||||||
cfg.frontendSettings.showImGuiDebugPanel = showDebugPanel;
|
cfg.frontendSettings.showImGuiDebugPanel = showDebugPanel;
|
||||||
showDebug = showDebugPanel;
|
showDebug = showDebugPanel;
|
||||||
}
|
}
|
||||||
|
ImGui::Checkbox("Stretch Output To Window", &cfg.frontendSettings.stretchImGuiOutputToWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::CollapsingHeader("Graphics")) {
|
if (ImGui::CollapsingHeader("Graphics")) {
|
||||||
|
|||||||
@@ -35,6 +35,30 @@ int emu_main(int argc, char *argv[]) {
|
|||||||
FrontendSDL app;
|
FrontendSDL app;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef IMGUI_FRONTEND
|
||||||
|
std::optional<std::filesystem::path> forcedRom;
|
||||||
|
if (argc > 1) {
|
||||||
|
forcedRom = std::filesystem::current_path() / argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::optional<std::filesystem::path> 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) {
|
if (argc > 1) {
|
||||||
auto romPath = std::filesystem::current_path() / argv[1];
|
auto romPath = std::filesystem::current_path() / argv[1];
|
||||||
if (!app.loadROM(romPath)) {
|
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());
|
Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} 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");
|
printf("No ROM inserted! Load a ROM by dragging and dropping it into the emulator window!\n");
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.run();
|
app.run();
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user