aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-25 06:16:13 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-25 06:16:13 -0600
commit3ba65f7cbf81ae47074c5f1f9305ebd97a2769f7 (patch)
tree5f65d05a3232ccbb2bd9143c07b9281d26d25c60 /src
parentc360dbf9c42f71e7931c56b7a396ba7f7e9982e6 (diff)
Fix non-Steam game launch: skip steam bridge, fix process tracking
Set UMU_ID=fluorine when Steam DRM is disabled so Proton-GE launches the game directly instead of through steam.exe (which asserts for non-Steam executables like GOG games). Fix VFS premature shutdown by tracking the game PID across process reparenting. When the proton root process exits, wine/game processes get reparented to PID 1 and disappear from the descendant tree. Now we remember the tracked game PID and poll it directly, keeping VFS mounted until the actual game exits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/src/processrunner.cpp43
-rw-r--r--src/src/protonlauncher.cpp8
2 files changed, 43 insertions, 8 deletions
diff --git a/src/src/processrunner.cpp b/src/src/processrunner.cpp
index 892fd79..dc64927 100644
--- a/src/src/processrunner.cpp
+++ b/src/src/processrunner.cpp
@@ -669,6 +669,7 @@ ProcessRunner::Results waitForPid(pid_t pid, LPDWORD exitCode,
}
bool seenTrackedProcess = false;
+ pid_t lastTrackedPid = 0;
while (true) {
QString trackedName;
@@ -677,14 +678,25 @@ ProcessRunner::Results waitForPid(pid_t pid, LPDWORD exitCode,
const pid_t tracked = findTrackedProcess(pid, expected, &trackedName);
if (tracked > 0) {
seenTrackedProcess = true;
+ lastTrackedPid = tracked;
displayPid = tracked;
displayName = trackedName;
} else if (seenTrackedProcess) {
- if (exitCode != nullptr) {
- *exitCode = 0;
+ // The tracked process is no longer a descendant of the root PID.
+ // This can happen when the root (proton) exits and wine/game processes
+ // get reparented to PID 1. Before declaring the game exited, check
+ // if the last tracked PID is still alive.
+ if (lastTrackedPid > 0 && ::kill(lastTrackedPid, 0) == 0) {
+ displayPid = lastTrackedPid;
+ displayName = readProcComm(lastTrackedPid);
+ log::debug("tracked process {} reparented but still alive", lastTrackedPid);
+ } else {
+ if (exitCode != nullptr) {
+ *exitCode = 0;
+ }
+ log::debug("tracked child process {} for root {} exited", lastTrackedPid, pid);
+ return ProcessRunner::Completed;
}
- log::debug("tracked child process for root {} exited", pid);
- return ProcessRunner::Completed;
}
if (ls != nullptr) {
@@ -693,18 +705,23 @@ ProcessRunner::Results waitForPid(pid_t pid, LPDWORD exitCode,
}
if (useKillPoll) {
- // Poll for process existence via kill(pid, 0)
- if (::kill(pid, 0) != 0) {
+ // Poll for process existence via kill(pid, 0).
+ // When we have a tracked game PID, monitor that instead of the root
+ // (proton) PID which may have already exited.
+ const pid_t pollPid = (seenTrackedProcess && lastTrackedPid > 0)
+ ? lastTrackedPid
+ : pid;
+ if (::kill(pollPid, 0) != 0) {
if (errno == ESRCH) {
if (exitCode != nullptr) {
*exitCode = 0;
}
- log::debug("process {} completed", pid);
+ log::debug("process {} completed", pollPid);
return ProcessRunner::Completed;
}
// EPERM means the process exists but we can't signal it; keep waiting
else if (errno != EPERM) {
- log::error("failed checking process {}, errno={}", pid, errno);
+ log::error("failed checking process {}, errno={}", pollPid, errno);
return ProcessRunner::Error;
}
}
@@ -713,6 +730,16 @@ ProcessRunner::Results waitForPid(pid_t pid, LPDWORD exitCode,
const pid_t waitResult = ::waitpid(pid, &status, WNOHANG);
if (waitResult == pid) {
+ // Root process (proton) exited. If we have a tracked game process
+ // that is still alive, switch to polling the game PID directly
+ // rather than declaring the game finished.
+ if (seenTrackedProcess && lastTrackedPid > 0 &&
+ ::kill(lastTrackedPid, 0) == 0) {
+ log::debug("root process {} exited but tracked game {} still alive, "
+ "switching to kill-poll", pid, lastTrackedPid);
+ useKillPoll = true;
+ continue;
+ }
if (exitCode != nullptr) {
*exitCode = exitCodeFromWaitStatus(status);
}
diff --git a/src/src/protonlauncher.cpp b/src/src/protonlauncher.cpp
index 4c5e39a..dbe9e20 100644
--- a/src/src/protonlauncher.cpp
+++ b/src/src/protonlauncher.cpp
@@ -381,6 +381,14 @@ bool ProtonLauncher::launchWithProton(qint64& pid) const
env.insert("SteamGameId", appId);
}
+ // When Steam DRM is disabled (e.g. GOG games), set UMU_ID so that
+ // Proton-GE skips the built-in steam.exe bridge. Without this, Proton
+ // tries to initialise the Steam client which causes an assertion failure
+ // for non-Steam executables.
+ if (!m_useSteamDrm) {
+ env.insert("UMU_ID", "fluorine");
+ }
+
env.insert("DOTNET_ROOT", "");
env.insert("DOTNET_MULTILEVEL_LOOKUP", "0");