diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-23 17:44:12 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-23 17:44:12 -0600 |
| commit | c360dbf9c42f71e7931c56b7a396ba7f7e9982e6 (patch) | |
| tree | 12b44a194db2f8a44a3052b02fced46766a5deee /libs/uibase | |
| parent | a8a287cd102001dddff30deec66140136da422d5 (diff) | |
Remove umu-run, keep game as child process for Steam tracking
- Remove umu-run entirely (crashed due to pressure-vessel/FUSE
incompatibility) — use raw `proton run` instead
- Replace startDetached() with QProcess::start() so the game stays in
Fluorine's process tree. This lets Steam track the game lifetime and
makes the "close game" button work when Fluorine is added as a
non-Steam game.
- Add writeIniValueDirect/readIniValueDirect for safe Bethesda INI
handling without QSettings corruption
- Fix prefix_setup.rs to skip umu-run bootstrapping
- Clean up settings UI, build scripts, and docs for umu-run removal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/uibase')
| -rw-r--r-- | libs/uibase/include/uibase/registry.h | 6 | ||||
| -rw-r--r-- | libs/uibase/src/registry.cpp | 81 | ||||
| -rw-r--r-- | libs/uibase/src/utility.cpp | 41 |
3 files changed, 123 insertions, 5 deletions
diff --git a/libs/uibase/include/uibase/registry.h b/libs/uibase/include/uibase/registry.h index 00013df..6a93f9d 100644 --- a/libs/uibase/include/uibase/registry.h +++ b/libs/uibase/include/uibase/registry.h @@ -28,6 +28,12 @@ namespace MOBase QDLLEXPORT bool WriteRegistryValue(const QString& appName, const QString& keyName, const QString& value, const QString& fileName); +// Removes a key from a Bethesda-style INI file without QSettings. +// Uses a safe line-by-line approach that does not corrupt backslashes +// or URL-encode spaces in key names. +QDLLEXPORT bool RemoveRegistryValue(const QString& section, const QString& key, + const QString& fileName); + #ifdef _WIN32 // Windows-specific overload using wide strings QDLLEXPORT bool WriteRegistryValue(const wchar_t* appName, const wchar_t* keyName, diff --git a/libs/uibase/src/registry.cpp b/libs/uibase/src/registry.cpp index 0b6834f..5832075 100644 --- a/libs/uibase/src/registry.cpp +++ b/libs/uibase/src/registry.cpp @@ -174,6 +174,87 @@ bool WriteRegistryValue(const QString& appName, const QString& keyName, return false; } +bool RemoveRegistryValue(const QString& section, const QString& key, + const QString& fileName) +{ + if (!QFileInfo::exists(fileName)) { + return true; // nothing to remove + } + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + QStringList lines; + QTextStream in(&file); + while (!in.atEnd()) { + lines.append(in.readLine()); + } + file.close(); + + const QString sectionHeader = "[" + section + "]"; + int sectionStart = -1; + int sectionEnd = lines.size(); + + for (int i = 0; i < lines.size(); ++i) { + QString trimmed = lines[i].trimmed(); + if (trimmed.compare(sectionHeader, Qt::CaseInsensitive) == 0) { + sectionStart = i; + for (int j = i + 1; j < lines.size(); ++j) { + QString t = lines[j].trimmed(); + if (t.startsWith('[') && t.endsWith(']')) { + sectionEnd = j; + break; + } + } + break; + } + } + + if (sectionStart < 0) { + return true; // section not found, nothing to remove + } + + // Find and remove the key line + bool found = false; + for (int i = sectionStart + 1; i < sectionEnd; ++i) { + QString trimmed = lines[i].trimmed(); + if (trimmed.isEmpty() || trimmed.startsWith(';') || trimmed.startsWith('#')) { + continue; + } + int eqPos = trimmed.indexOf('='); + if (eqPos > 0) { + QString existingKey = trimmed.left(eqPos).trimmed(); + if (existingKey.compare(key, Qt::CaseInsensitive) == 0) { + lines.removeAt(i); + found = true; + break; + } + } + } + + if (!found) { + return true; // key not found, nothing to remove + } + + // Write back + QFile outFile(fileName); + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + return false; + } + QTextStream out(&outFile); + for (int i = 0; i < lines.size(); ++i) { + out << lines[i]; + if (i < lines.size() - 1) { + out << '\n'; + } + } + out << '\n'; + outFile.close(); + + return true; +} + #ifdef _WIN32 bool WriteRegistryValue(const wchar_t* appName, const wchar_t* keyName, const wchar_t* value, const wchar_t* fileName) diff --git a/libs/uibase/src/utility.cpp b/libs/uibase/src/utility.cpp index e6f53b8..33f227d 100644 --- a/libs/uibase/src/utility.cpp +++ b/libs/uibase/src/utility.cpp @@ -388,11 +388,42 @@ namespace shell } } + // Launch an external host process with AppImage environment variables + // cleaned up, so tools like xdg-open/kde-open use the host's own + // libraries and Qt plugins instead of the bundled ones. + static bool startDetachedHostProcess(const QString& program, + const QStringList& args) + { + QProcess proc; + auto env = QProcessEnvironment::systemEnvironment(); + + // Restore original LD_LIBRARY_PATH (saved by AppRun.sh) + if (env.contains(QStringLiteral("FLUORINE_ORIG_LD_LIBRARY_PATH"))) { + const auto orig = env.value(QStringLiteral("FLUORINE_ORIG_LD_LIBRARY_PATH")); + if (orig.isEmpty()) + env.remove(QStringLiteral("LD_LIBRARY_PATH")); + else + env.insert(QStringLiteral("LD_LIBRARY_PATH"), orig); + } + + // Remove AppImage-specific Qt/path variables that would confuse host apps + env.remove(QStringLiteral("QT_PLUGIN_PATH")); + env.remove(QStringLiteral("QT_QPA_PLATFORM_PLUGIN_PATH")); + env.remove(QStringLiteral("QTWEBENGINEPROCESS_PATH")); + env.remove(QStringLiteral("QTWEBENGINE_RESOURCES_PATH")); + env.remove(QStringLiteral("QTWEBENGINE_LOCALES_PATH")); + + proc.setProgram(program); + proc.setArguments(args); + proc.setProcessEnvironment(env); + return proc.startDetached(); + } + Result ExploreDirectory(const QFileInfo& info) { const auto path = info.absoluteFilePath(); // Use xdg-open on Linux - if (QProcess::startDetached("xdg-open", {path})) { + if (startDetachedHostProcess("xdg-open", {path})) { return Result::makeSuccess(); } return Result::makeFailure(ERROR_FILE_NOT_FOUND, QObject::tr("Failed to open directory")); @@ -403,7 +434,7 @@ namespace shell // Try to use the system file manager to highlight the file // dbus method for Nautilus/Files, fallback to xdg-open on parent dir const auto dir = info.absolutePath(); - if (QProcess::startDetached("xdg-open", {dir})) { + if (startDetachedHostProcess("xdg-open", {dir})) { return Result::makeSuccess(); } return Result::makeFailure(ERROR_FILE_NOT_FOUND, QObject::tr("Failed to open file manager")); @@ -437,7 +468,7 @@ namespace shell Result Open(const QString& path) { - if (QProcess::startDetached("xdg-open", {path})) { + if (startDetachedHostProcess("xdg-open", {path})) { return Result::makeSuccess(); } return Result::makeFailure(ERROR_FILE_NOT_FOUND, QObject::tr("Failed to open file")); @@ -448,7 +479,7 @@ namespace shell log::debug("opening url '{}'", url.toString()); if (g_urlHandler.isEmpty()) { - if (QProcess::startDetached("xdg-open", {url.toString()})) { + if (startDetachedHostProcess("xdg-open", {url.toString()})) { return Result::makeSuccess(); } return Result::makeFailure(ERROR_FILE_NOT_FOUND, QObject::tr("Failed to open URL")); @@ -456,7 +487,7 @@ namespace shell // Custom URL handler QString cmd = g_urlHandler; cmd.replace("%1", url.toString()); - if (QProcess::startDetached("/bin/sh", {"-c", cmd})) { + if (startDetachedHostProcess("/bin/sh", {"-c", cmd})) { return Result::makeSuccess(); } return Result::makeFailure(ERROR_FILE_NOT_FOUND, |
