diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/editexecutablesdialog.cpp | 26 | ||||
| -rw-r--r-- | src/src/editexecutablesdialog.ui | 23 | ||||
| -rw-r--r-- | src/src/executableslist.cpp | 20 | ||||
| -rw-r--r-- | src/src/executableslist.h | 6 | ||||
| -rw-r--r-- | src/src/processrunner.cpp | 3 | ||||
| -rw-r--r-- | src/src/protonlauncher.cpp | 73 | ||||
| -rw-r--r-- | src/src/protonlauncher.h | 2 | ||||
| -rw-r--r-- | src/src/spawn.cpp | 82 | ||||
| -rw-r--r-- | src/src/spawn.h | 2 |
9 files changed, 197 insertions, 40 deletions
diff --git a/src/src/editexecutablesdialog.cpp b/src/src/editexecutablesdialog.cpp index e5dcda9..4c6819d 100644 --- a/src/src/editexecutablesdialog.cpp +++ b/src/src/editexecutablesdialog.cpp @@ -126,6 +126,12 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, int sel, connect(ui->hide, &QCheckBox::toggled, [&] {
save();
});
+ connect(ui->useProton, &QCheckBox::toggled, [&] {
+ save();
+ });
+ connect(ui->useTerminal, &QCheckBox::toggled, [&] {
+ save();
+ });
connect(ui->list->model(), &QAbstractItemModel::rowsMoved, [&] {
saveOrder();
});
@@ -391,6 +397,10 @@ void EditExecutablesDialog::clearEdits() ui->minimizeToSystemTray->setChecked(false);
ui->hide->setEnabled(false);
ui->hide->setChecked(false);
+ ui->useProton->setEnabled(false);
+ ui->useProton->setChecked(true);
+ ui->useTerminal->setEnabled(false);
+ ui->useTerminal->setChecked(false);
m_lastGoodTitle = "";
}
@@ -407,6 +417,8 @@ void EditExecutablesDialog::setEdits(const Executable& e) ui->useApplicationIcon->setChecked(e.usesOwnIcon());
ui->minimizeToSystemTray->setChecked(e.minimizeToSystemTray());
ui->hide->setChecked(e.hide());
+ ui->useProton->setChecked(e.useProton());
+ ui->useTerminal->setChecked(e.useTerminal());
m_lastGoodTitle = e.title();
@@ -453,6 +465,8 @@ void EditExecutablesDialog::setEdits(const Executable& e) ui->forceLoadLibraries->setEnabled(true);
ui->minimizeToSystemTray->setEnabled(true);
ui->hide->setEnabled(true);
+ ui->useProton->setEnabled(true);
+ ui->useTerminal->setEnabled(true);
}
void EditExecutablesDialog::save()
@@ -524,6 +538,18 @@ void EditExecutablesDialog::save() e->flags(e->flags() & (~Executable::Hide));
}
+ if (ui->useProton->isChecked()) {
+ e->flags(e->flags() | Executable::UseProton);
+ } else {
+ e->flags(e->flags() & (~Executable::UseProton));
+ }
+
+ if (ui->useTerminal->isChecked()) {
+ e->flags(e->flags() | Executable::UseTerminal);
+ } else {
+ e->flags(e->flags() & (~Executable::UseTerminal));
+ }
+
setDirty(true);
}
diff --git a/src/src/editexecutablesdialog.ui b/src/src/editexecutablesdialog.ui index 005f3a1..8ac01f6 100644 --- a/src/src/editexecutablesdialog.ui +++ b/src/src/editexecutablesdialog.ui @@ -465,6 +465,29 @@ Right now the only case I know of where this needs to be overwritten is for the </widget>
</item>
<item>
+ <widget class="QCheckBox" name="useProton">
+ <property name="toolTip">
+ <string>Launch this executable through Proton (Wine compatibility layer). Disable for native Linux executables.</string>
+ </property>
+ <property name="text">
+ <string>Use Proton</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="useTerminal">
+ <property name="toolTip">
+ <string>Open a terminal window showing the executable's output. Useful for debugging.</string>
+ </property>
+ <property name="text">
+ <string>Open in terminal</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>(*) Profile specific</string>
diff --git a/src/src/executableslist.cpp b/src/src/executableslist.cpp index ea7a2e5..5410c81 100644 --- a/src/src/executableslist.cpp +++ b/src/src/executableslist.cpp @@ -88,6 +88,14 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, const Settings& s) if (map["hide"].toBool())
flags |= Executable::Hide;
+ // Default to UseProton=true for backward compat (existing executables
+ // were always launched with Proton). Only set false if explicitly saved.
+ if (map.contains("useProton") ? map["useProton"].toBool() : true)
+ flags |= Executable::UseProton;
+
+ if (map["useTerminal"].toBool())
+ flags |= Executable::UseTerminal;
+
if (map.contains("custom")) {
// the "custom" setting only exists in older versions
needsUpgrade = true;
@@ -126,6 +134,8 @@ void ExecutablesList::store(Settings& s) map["workingDirectory"] = item.workingDirectory();
map["steamAppID"] = item.steamAppID();
map["minimizeToSystemTray"] = item.minimizeToSystemTray();
+ map["useProton"] = item.useProton();
+ map["useTerminal"] = item.useTerminal();
v.push_back(std::move(map));
}
@@ -482,6 +492,16 @@ bool Executable::hide() const return m_flags.testFlag(Hide);
}
+bool Executable::useProton() const
+{
+ return m_flags.testFlag(UseProton);
+}
+
+bool Executable::useTerminal() const
+{
+ return m_flags.testFlag(UseTerminal);
+}
+
void Executable::mergeFrom(const Executable& other)
{
// this happens after executables are loaded from settings and plugin
diff --git a/src/src/executableslist.h b/src/src/executableslist.h index b402461..9ab3927 100644 --- a/src/src/executableslist.h +++ b/src/src/executableslist.h @@ -48,7 +48,9 @@ public: ShowInToolbar = 0x02,
UseApplicationIcon = 0x04,
Hide = 0x08,
- MinimizeToSystemTray = 0x16
+ MinimizeToSystemTray = 0x16,
+ UseProton = 0x20,
+ UseTerminal = 0x40
};
Q_DECLARE_FLAGS(Flags, Flag);
@@ -79,6 +81,8 @@ public: bool usesOwnIcon() const;
bool minimizeToSystemTray() const;
bool hide() const;
+ bool useProton() const;
+ bool useTerminal() const;
void mergeFrom(const Executable& other);
diff --git a/src/src/processrunner.cpp b/src/src/processrunner.cpp index 724886b..dee7b73 100644 --- a/src/src/processrunner.cpp +++ b/src/src/processrunner.cpp @@ -1099,6 +1099,9 @@ ProcessRunner &ProcessRunner::setFromExecutable(const Executable &exe) { setCustomOverwrite(customOverwrite);
setForcedLibraries(forcedLibraries);
+ m_sp.useProton = exe.useProton();
+ m_sp.useTerminal = exe.useTerminal();
+
return *this;
}
diff --git a/src/src/protonlauncher.cpp b/src/src/protonlauncher.cpp index e2895e2..3d1741e 100644 --- a/src/src/protonlauncher.cpp +++ b/src/src/protonlauncher.cpp @@ -7,6 +7,7 @@ #include <QFileInfo> #include <QProcess> #include <QProcessEnvironment> +#include <QStandardPaths> #include <log.h> @@ -134,6 +135,64 @@ QString detectSteamPath() return {}; } +// Detect an available terminal emulator on the system. +QString findTerminal() +{ + static const char* candidates[] = { + "konsole", "gnome-terminal", "xfce4-terminal", "alacritty", + "kitty", "foot", "xterm"}; + for (const auto* name : candidates) { + const QString path = QStandardPaths::findExecutable(name); + if (!path.isEmpty()) + return path; + } + return {}; +} + +// Wrap a program + args in a terminal emulator. +// Modifies program and arguments in-place. +void wrapInTerminal(QString& program, QStringList& arguments) +{ + const QString term = findTerminal(); + if (term.isEmpty()) { + MOBase::log::warn("No terminal emulator found; launching without terminal"); + return; + } + + const QString termName = QFileInfo(term).fileName(); + + // Build a shell command for the inner program + args. + // Append a read prompt so the terminal stays open after exit. + QString innerCmd = "'" + QString(program).replace("'", "'\\''") + "'"; + for (const auto& a : arguments) + innerCmd += " '" + QString(a).replace("'", "'\\''") + "'"; + innerCmd += "; echo; echo '--- Process exited with code '$?' ---'; " + "echo 'Press Enter to close...'; read"; + + QStringList termArgs; + if (termName == "konsole") { + termArgs << "--hold" << "-e" << "bash" << "-c" << innerCmd; + } else if (termName == "gnome-terminal") { + termArgs << "--" << "bash" << "-c" << innerCmd; + } else if (termName == "xfce4-terminal") { + termArgs << "--hold" << "-e" << ("bash -c " + innerCmd); + } else if (termName == "alacritty") { + termArgs << "-e" << "bash" << "-c" << innerCmd; + } else if (termName == "kitty") { + termArgs << "bash" << "-c" << innerCmd; + } else if (termName == "foot") { + termArgs << "bash" << "-c" << innerCmd; + } else { + // xterm and others + termArgs << "-e" << "bash" << "-c" << innerCmd; + } + + MOBase::log::info("Launching in terminal: {} {}", term, + termArgs.join(' ')); + program = term; + arguments = termArgs; +} + bool startWithEnv(const QString& program, const QStringList& arguments, const QString& workingDir, const QProcessEnvironment& environment, qint64& pid) @@ -321,6 +380,12 @@ ProtonLauncher& ProtonLauncher::addEnvVar(const QString& key, const QString& val return *this; } +ProtonLauncher& ProtonLauncher::setUseTerminal(bool useTerminal) +{ + m_useTerminal = useTerminal; + return *this; +} + std::pair<bool, qint64> ProtonLauncher::launch() const { qint64 pid = -1; @@ -416,6 +481,10 @@ bool ProtonLauncher::launchWithProton(qint64& pid) const env.insert("PWD", m_workingDir); } + if (m_useTerminal) { + wrapInTerminal(program, arguments); + } + return startWithEnv(program, arguments, m_workingDir, env, pid); } @@ -439,6 +508,10 @@ bool ProtonLauncher::launchDirect(qint64& pid) const env.insert(it.key(), it.value()); } + if (m_useTerminal) { + wrapInTerminal(program, arguments); + } + return startWithEnv(program, arguments, m_workingDir, env, pid); } diff --git a/src/src/protonlauncher.h b/src/src/protonlauncher.h index 363d92b..11d942a 100644 --- a/src/src/protonlauncher.h +++ b/src/src/protonlauncher.h @@ -23,6 +23,7 @@ public: ProtonLauncher& setSteamDrm(bool useSteamDrm); ProtonLauncher& setStoreVariant(const QString& variant); ProtonLauncher& addEnvVar(const QString& key, const QString& value); + ProtonLauncher& setUseTerminal(bool useTerminal); // Launch dispatch: Proton -> Direct std::pair<bool, qint64> launch() const; @@ -43,6 +44,7 @@ private: QString m_storeVariant; // "GOG", "Epic", or empty for Steam QMap<QString, QString> m_envVars; QMap<QString, QString> m_wrapperEnvVars; + bool m_useTerminal = false; }; #endif // PROTONLAUNCHER_H diff --git a/src/src/spawn.cpp b/src/src/spawn.cpp index 4c91fbe..b0e7eea 100644 --- a/src/src/spawn.cpp +++ b/src/src/spawn.cpp @@ -636,54 +636,58 @@ int spawn(const SpawnParameters &sp, pid_t &processId) { logSpawning(sp, bin + " " + sp.arguments);
- // Read per-instance settings from the instance INI (not the global
- // QSettings).
- const Settings *instanceForLaunch = Settings::maybeInstance();
- bool useSteamDrm = true; // default
- QString storeVariant;
- if (instanceForLaunch) {
- const QSettings instanceIni(instanceForLaunch->filename(),
- QSettings::IniFormat);
- useSteamDrm = instanceIni.value("fluorine/steam_drm", true).toBool();
- // settingName() maps "General" section keys to top-level (no section
- // prefix), so game_edition is stored as a plain key under [General] in the
- // INI.
- storeVariant = instanceIni.value("game_edition").toString().trimmed();
- }
-
ProtonLauncher launcher;
launcher.setBinary(bin)
.setArguments(argList)
.setWorkingDir(cwd)
- .setSteamAppId(parseSteamAppId(sp.steamAppID))
- .setSteamDrm(useSteamDrm)
- .setStoreVariant(storeVariant);
+ .setSteamAppId(parseSteamAppId(sp.steamAppID));
- const QString prefixPath = resolvePrefixPath();
- if (prefixPath.isEmpty()) {
- MOBase::log::warn(
- "No Wine prefix configured - games may not launch correctly. "
- "Configure a prefix in Settings > Proton or via fluorine-manager.");
- } else if (!QDir(QDir(prefixPath).filePath("drive_c")).exists()) {
- MOBase::log::warn(
- "Wine prefix '{}' does not contain drive_c/ - prefix may be invalid",
- prefixPath);
- } else {
- MOBase::log::info("Using Wine prefix: {}", prefixPath);
- launcher.setPrefix(prefixPath);
- }
+ if (sp.useProton) {
+ // Read per-instance settings from the instance INI (not the global
+ // QSettings).
+ const Settings *instanceForLaunch = Settings::maybeInstance();
+ bool useSteamDrm = true; // default
+ QString storeVariant;
+ if (instanceForLaunch) {
+ const QSettings instanceIni(instanceForLaunch->filename(),
+ QSettings::IniFormat);
+ useSteamDrm = instanceIni.value("fluorine/steam_drm", true).toBool();
+ storeVariant = instanceIni.value("game_edition").toString().trimmed();
+ }
- const QString protonPath = resolveProtonPath();
- if (!protonPath.isEmpty()) {
- launcher.setProtonPath(protonPath);
- }
+ launcher.setSteamDrm(useSteamDrm)
+ .setStoreVariant(storeVariant);
- const QString wrapper =
- QSettings().value("fluorine/launch_wrapper").toString().trimmed();
- if (!wrapper.isEmpty()) {
- launcher.setWrapper(wrapper);
+ const QString prefixPath = resolvePrefixPath();
+ if (prefixPath.isEmpty()) {
+ MOBase::log::warn(
+ "No Wine prefix configured - games may not launch correctly. "
+ "Configure a prefix in Settings > Proton or via fluorine-manager.");
+ } else if (!QDir(QDir(prefixPath).filePath("drive_c")).exists()) {
+ MOBase::log::warn(
+ "Wine prefix '{}' does not contain drive_c/ - prefix may be invalid",
+ prefixPath);
+ } else {
+ MOBase::log::info("Using Wine prefix: {}", prefixPath);
+ launcher.setPrefix(prefixPath);
+ }
+
+ const QString protonPath = resolveProtonPath();
+ if (!protonPath.isEmpty()) {
+ launcher.setProtonPath(protonPath);
+ }
+
+ const QString wrapper =
+ QSettings().value("fluorine/launch_wrapper").toString().trimmed();
+ if (!wrapper.isEmpty()) {
+ launcher.setWrapper(wrapper);
+ }
+ } else {
+ MOBase::log::info("Proton disabled for this executable, launching directly");
}
+ launcher.setUseTerminal(sp.useTerminal);
+
const auto [ok, pid] = launcher.launch();
if (!ok) {
return (errno != 0 ? errno : EIO);
diff --git a/src/src/spawn.h b/src/src/spawn.h index d922ddc..378c20f 100644 --- a/src/src/spawn.h +++ b/src/src/spawn.h @@ -57,6 +57,8 @@ struct SpawnParameters QDir currentDirectory;
QString steamAppID;
bool hooked = false;
+ bool useProton = true;
+ bool useTerminal = false;
#ifdef _WIN32
HANDLE stdOut = INVALID_HANDLE_VALUE;
HANDLE stdErr = INVALID_HANDLE_VALUE;
|
