diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index a4add576c..e04073454 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -200,6 +200,18 @@ DEFINE_int32(recent_titles_entry_amount, 10, "Allows user to define how many titles is saved in list of " "recently played titles.", "General"); + +#if XE_PLATFORM_LINUX +DEFINE_bool( + use_mangohud, false, + "Enable MangoHUD overlay when launching games in separate processes.\n" + "MangoHUD must be installed on your system for this to work.", + "Linux"); +DEFINE_bool(use_gamemode, false, + "Use Feral GameMode when launching games in separate processes.\n" + "GameMode must be installed on your system for this to work.", + "Linux"); +#endif namespace xe { namespace app { @@ -2238,8 +2250,23 @@ void EmulatorWindow::LaunchTitleInNewProcess( if (pid == 0) { // Child process + + // Set MangoHUD environment variable if enabled + if (cvars::use_mangohud) { + setenv("MANGOHUD", "1", 1); + } + std::vector argv; - argv.push_back(executable_path.c_str()); + std::string gamemode_cmd; + + // If GameMode is enabled, use gamemoderun as the executable + if (cvars::use_gamemode) { + gamemode_cmd = "gamemoderun"; + argv.push_back(gamemode_cmd.c_str()); + argv.push_back(executable_path.c_str()); + } else { + argv.push_back(executable_path.c_str()); + } // Pass the config file if one is being used std::string config_arg; @@ -2270,10 +2297,16 @@ void EmulatorWindow::LaunchTitleInNewProcess( argv.push_back(nullptr); // Execute the new process - execv(executable_path.c_str(), const_cast(argv.data())); + if (cvars::use_gamemode) { + // Use execvp to search PATH for gamemoderun + execvp(gamemode_cmd.c_str(), const_cast(argv.data())); + } else { + execv(executable_path.c_str(), const_cast(argv.data())); + } - // If execv returns, it failed - XELOGE("Failed to execute: {}", executable_path.string()); + // If exec returns, it failed + XELOGE("Failed to execute: {}", + cvars::use_gamemode ? gamemode_cmd : executable_path.string()); std::exit(1); } else if (pid < 0) { // Fork failed diff --git a/src/xenia/ui/config_dialog_qt.cc b/src/xenia/ui/config_dialog_qt.cc index acf0b049e..903185ae3 100644 --- a/src/xenia/ui/config_dialog_qt.cc +++ b/src/xenia/ui/config_dialog_qt.cc @@ -29,6 +29,10 @@ #include "xenia/config.h" #include "xenia/ui/config_helpers.h" +#if XE_PLATFORM_LINUX +#include +#endif + namespace xe { namespace app { @@ -37,6 +41,28 @@ namespace { // Use the shared enum options from config_helpers.h using xe::ui::GetKnownEnumOptions; +#if XE_PLATFORM_LINUX +// Check if a command exists in PATH by searching each directory +bool IsCommandAvailable(const std::string& command) { + std::string path_env = std::getenv("PATH") ? std::getenv("PATH") : ""; + if (path_env.empty()) { + return false; + } + + // Split PATH by ':' + std::istringstream path_stream(path_env); + std::string path_dir; + while (std::getline(path_stream, path_dir, ':')) { + std::filesystem::path full_path = std::filesystem::path(path_dir) / command; + if (std::filesystem::exists(full_path) && + access(full_path.c_str(), X_OK) == 0) { + return true; + } + } + return false; +} +#endif + } // namespace ConfigDialogQt::ConfigDialogQt(QWidget* parent, EmulatorWindow* emulator_window) @@ -246,6 +272,23 @@ QWidget* ConfigDialogQt::CreateEditorWidget(ConfigVarInfo* var_info) { checkbox->setChecked(var_info->pending_value == "true"); connect(checkbox, &QCheckBox::checkStateChanged, this, &ConfigDialogQt::OnValueChanged); + +#if XE_PLATFORM_LINUX + // Disable Linux-specific options if the required tools aren't installed + if (var_info->name == "use_mangohud" && !IsCommandAvailable("mangohud")) { + checkbox->setEnabled(false); + checkbox->setToolTip( + "MangoHUD is not installed or not found in PATH. Install MangoHUD " + "to use this feature."); + } else if (var_info->name == "use_gamemode" && + !IsCommandAvailable("gamemoderun")) { + checkbox->setEnabled(false); + checkbox->setToolTip( + "GameMode is not installed or not found in PATH. Install GameMode " + "to use this feature."); + } +#endif + return checkbox; } else if (enum_it != enum_options.end()) { // Dropdown for enum-like cvars