diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-25 06:16:13 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-25 06:16:13 -0600 |
| commit | 3ba65f7cbf81ae47074c5f1f9305ebd97a2769f7 (patch) | |
| tree | 5f65d05a3232ccbb2bd9143c07b9281d26d25c60 /src | |
| parent | c360dbf9c42f71e7931c56b7a396ba7f7e9982e6 (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.cpp | 43 | ||||
| -rw-r--r-- | src/src/protonlauncher.cpp | 8 |
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"); |
