From 3ba65f7cbf81ae47074c5f1f9305ebd97a2769f7 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Wed, 25 Feb 2026 06:16:13 -0600 Subject: 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 --- src/src/processrunner.cpp | 43 +++++++++++++++++++++++++++++++++++-------- src/src/protonlauncher.cpp | 8 ++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) (limited to 'src') 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"); -- cgit v1.3.1