diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-25 15:45:41 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-25 15:45:41 -0600 |
| commit | 0e48de1021562679680aaf46a871776a38fca7dd (patch) | |
| tree | 086399945f979f5de8595dee4b4f50717b851141 /src | |
| parent | 3ba65f7cbf81ae47074c5f1f9305ebd97a2769f7 (diff) | |
Fix process tracking for Wine/Proton game processes
findTrackedProcess was only checking /proc/<pid>/comm to match game
executables. This fails for Wine/Proton because:
1. /proc/comm is truncated to 15 chars (TASK_COMM_LEN), so
"FalloutNVLauncher.exe" becomes "FalloutNVLaunch" — no match
2. Wine processes show "wine64-preloader" or "start.exe" in comm,
not the actual game executable name
3. With UMU_ID set, Proton-GE launches via "start.exe /unix <game>"
adding another indirection layer
Now also checks /proc/<pid>/cmdline which contains the full untruncated
command line including the game .exe path. Handles both Windows-style
backslash paths (c:\...\game.exe) and Unix paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/processrunner.cpp | 68 |
1 files changed, 61 insertions, 7 deletions
diff --git a/src/src/processrunner.cpp b/src/src/processrunner.cpp index dc64927..2446f86 100644 --- a/src/src/processrunner.cpp +++ b/src/src/processrunner.cpp @@ -497,6 +497,64 @@ QString readProcComm(pid_t pid) { return QString::fromUtf8(f.readAll()).trimmed();
}
+// Read /proc/<pid>/cmdline (NUL-separated) and return all argv entries.
+QStringList readProcCmdline(pid_t pid) {
+ QFile f(QString("/proc/%1/cmdline").arg(pid));
+ if (!f.open(QIODevice::ReadOnly)) {
+ return {};
+ }
+
+ const QByteArray data = f.readAll();
+ QStringList parts;
+ for (const QByteArray &part : data.split('\0')) {
+ if (!part.isEmpty()) {
+ parts.push_back(QString::fromUtf8(part));
+ }
+ }
+ return parts;
+}
+
+// Check whether any of the expected executable names appear in a process's
+// comm or cmdline. Wine processes often show "wine64-preload" or "start.exe"
+// in /proc/comm while the actual game executable only appears in cmdline.
+// Also handles the 15-char TASK_COMM_LEN truncation in /proc/comm.
+bool processMatchesExpected(pid_t pid, const QStringList &expected,
+ QString *matchedNameOut) {
+ // 1. Check /proc/comm (fast path).
+ const QString comm = readProcComm(pid);
+ if (!comm.isEmpty()) {
+ const QString lower = comm.toLower();
+ for (const QString &exp : expected) {
+ if (lower == exp) {
+ if (matchedNameOut) *matchedNameOut = comm;
+ return true;
+ }
+ // Handle TASK_COMM_LEN truncation (15 chars): if the expected name
+ // is longer than 15 chars, check if comm matches its first 15 chars.
+ if (exp.size() > 15 && lower == exp.left(15)) {
+ if (matchedNameOut) *matchedNameOut = exp;
+ return true;
+ }
+ }
+ }
+
+ // 2. Check /proc/cmdline — Wine/Proton processes carry the .exe name here
+ // even when comm shows wine64-preloader or start.exe.
+ const QStringList cmdline = readProcCmdline(pid);
+ for (const QString &arg : cmdline) {
+ // Extract just the filename from paths like
+ // "c:\windows\system32\start.exe" or "/home/.../FalloutNV.exe"
+ const QString normalized = QString(arg).replace('\\', '/');
+ const QString base = QFileInfo(normalized).fileName().toLower();
+ if (expected.contains(base)) {
+ if (matchedNameOut) *matchedNameOut = QFileInfo(normalized).fileName();
+ return true;
+ }
+ }
+
+ return false;
+}
+
std::unordered_map<pid_t, std::vector<pid_t>> buildProcChildrenMap() {
std::unordered_map<pid_t, std::vector<pid_t>> children;
DIR *proc = opendir("/proc");
@@ -605,14 +663,10 @@ pid_t findTrackedProcess(pid_t rootPid, const QStringList &expected, pid_t best = 0;
QString bestName;
for (pid_t pid : descendants) {
- const QString comm = readProcComm(pid);
- if (comm.isEmpty()) {
- continue;
- }
- const QString lower = comm.toLower();
- if (expected.contains(lower)) {
+ QString matched;
+ if (processMatchesExpected(pid, expected, &matched)) {
best = pid;
- bestName = comm;
+ bestName = matched;
break;
}
}
|
