aboutsummaryrefslogtreecommitdiff
path: root/libs/uibase
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-23 17:44:12 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-23 17:44:12 -0600
commitc360dbf9c42f71e7931c56b7a396ba7f7e9982e6 (patch)
tree12b44a194db2f8a44a3052b02fced46766a5deee /libs/uibase
parenta8a287cd102001dddff30deec66140136da422d5 (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.h6
-rw-r--r--libs/uibase/src/registry.cpp81
-rw-r--r--libs/uibase/src/utility.cpp41
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,