From 319c5c04b55a28107d472e753baaa15d20d04b43 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:51:35 -0400 Subject: [PATCH 1/6] Remove LauncherCore::isWindows property This is only used in QML and is pretty much what Qt.platform.os already does. --- launcher/include/launchercore.h | 2 -- launcher/src/launchercore.cpp | 12 +++--------- launcher/ui/Settings/ProfileSettings.qml | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h index 2be1caa..b647d74 100755 --- a/launcher/include/launchercore.h +++ b/launcher/include/launchercore.h @@ -48,7 +48,6 @@ class LauncherCore : public QObject QML_SINGLETON Q_PROPERTY(bool loadingFinished READ isLoadingFinished NOTIFY loadingFinished) - Q_PROPERTY(bool isWindows READ isWindows CONSTANT) Q_PROPERTY(Config *config READ config CONSTANT) Q_PROPERTY(ProfileManager *profileManager READ profileManager CONSTANT) Q_PROPERTY(AccountManager *accountManager READ accountManager CONSTANT) @@ -85,7 +84,6 @@ class LauncherCore : public QObject void setupIgnoreSSL(QNetworkReply *reply); [[nodiscard]] bool isLoadingFinished() const; - [[nodiscard]] static bool isWindows(); [[nodiscard]] static bool needsCompatibilityTool(); [[nodiscard]] Q_INVOKABLE bool isPatching() const; diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp index f47480e..1993f16 100755 --- a/launcher/src/launchercore.cpp +++ b/launcher/src/launchercore.cpp @@ -188,18 +188,12 @@ bool LauncherCore::isLoadingFinished() const return m_loadingFinished; } -bool LauncherCore::isWindows() +bool LauncherCore::needsCompatibilityTool() { -#if defined(Q_OS_WIN) - return true; -#else +#ifdef Q_OS_WINDOWS return false; #endif -} - -bool LauncherCore::needsCompatibilityTool() -{ - return !isWindows(); + return true; } bool LauncherCore::isPatching() const diff --git a/launcher/ui/Settings/ProfileSettings.qml b/launcher/ui/Settings/ProfileSettings.qml index 93fec38..eede6e2 100644 --- a/launcher/ui/Settings/ProfileSettings.qml +++ b/launcher/ui/Settings/ProfileSettings.qml @@ -43,7 +43,7 @@ FormCard.FormCardPage { Kirigami.Action { id: wineAction text: i18n("Wine") - visible: !LauncherCore.isWindows + visible: Qt.platform.os !== "windows" icon.name: "wine-symbolic" }, Kirigami.Action { From 9e055946bf46bc13b1d3144257bc2f9e7c1a1ac8 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:52:53 -0400 Subject: [PATCH 2/6] Remove now unused BUILD_FLATPAK build option Note that this *will* clear your saved password. You can enter it again to fix this. --- CMakeLists.txt | 3 --- launcher/CMakeLists.txt | 4 ---- launcher/src/account.cpp | 8 -------- zone.xiv.umbra.yml | 1 - 4 files changed, 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69512ae..184e0d4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,6 @@ cmake_minimum_required(VERSION 3.25) project(Umbra VERSION 0.2.0 LANGUAGES CXX) -# build options used for distributors -option(BUILD_FLATPAK "Build for Flatpak." OFF) - set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 39aa41a..0c734eb 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -88,10 +88,6 @@ if (NOT MSVC) target_compile_options(umbra_static PUBLIC -fexceptions) endif () -if (BUILD_FLATPAK) - target_compile_definitions(umbra_static PUBLIC FLATPAK) -endif () - # For screensaver inhibition on Linux if (TARGET Qt6::DBus) target_link_libraries(umbra_static PRIVATE Qt6::DBus) diff --git a/launcher/src/account.cpp b/launcher/src/account.cpp index d770d1a..a05414a 100644 --- a/launcher/src/account.cpp +++ b/launcher/src/account.cpp @@ -56,11 +56,7 @@ void Account::setKeychainValue(const QString &key, const QString &value) { auto job = new QKeychain::WritePasswordJob(QStringLiteral("Umbra"), this); job->setTextData(value); -#ifdef FLATPAK - job->setKey(QStringLiteral("flatpak-") + m_key + QStringLiteral("-") + key); -#else job->setKey(m_key + QStringLiteral("-") + key); -#endif job->start(); connect(job, &QKeychain::WritePasswordJob::finished, this, [job] { @@ -73,11 +69,7 @@ void Account::setKeychainValue(const QString &key, const QString &value) QCoro::Task Account::getKeychainValue(const QString &key) { auto job = new QKeychain::ReadPasswordJob(QStringLiteral("Umbra"), this); -#ifdef FLATPAK - job->setKey(QStringLiteral("flatpak-") + m_key + QStringLiteral("-") + key); -#else job->setKey(m_key + QStringLiteral("-") + key); -#endif job->start(); co_await qCoro(job, &QKeychain::ReadPasswordJob::finished); diff --git a/zone.xiv.umbra.yml b/zone.xiv.umbra.yml index 6b5217b..3f2ea18 100644 --- a/zone.xiv.umbra.yml +++ b/zone.xiv.umbra.yml @@ -68,7 +68,6 @@ modules: buildsystem: cmake-ninja config-opts: - -DRust_COMPILER=/usr/lib/sdk/rust-stable/bin/rustc - - -DBUILD_FLATPAK=ON - -DCMAKE_INSTALL_LIBDIR=/app/lib - -DLIB_INSTALL_DIR=/app/lib - -DBUILD_TESTING=OFF From d3884aa76ab9f33acd2c5b6450935c3b6eb33706 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:55:40 -0400 Subject: [PATCH 3/6] Translate Flatpak Documents API to real host paths Several different users were confused by the weird /run/user/0000-esque paths as a result of Flatpak sandboxing, but I literally learned *today* that we can translate those back into useful paths. --- CMakeLists.txt | 3 +++ launcher/CMakeLists.txt | 3 +++ launcher/include/launchercore.h | 7 +++++++ launcher/src/launchercore.cpp | 15 +++++++++++++++ launcher/ui/Settings/AccountSettings.qml | 2 +- launcher/ui/Settings/ProfileSettings.qml | 2 +- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 184e0d4..7950992 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,9 @@ find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami I18n Config Core find_package(KF6KirigamiAddons 1.7.0 REQUIRED) find_package(QCoro6 REQUIRED COMPONENTS Core Network Qml) qcoro_enable_coroutines() +if (LINUX) + find_package(KF6FileMetaData ${KF_MIN_VERSION} REQUIRED) +endif () qt_policy(SET QTP0001 NEW) qt_policy(SET QTP0004 NEW) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 0c734eb..f6e0dab 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -79,6 +79,9 @@ target_link_libraries(umbra_static PUBLIC QCoro::Network QCoro::Qml physis) +if (LINUX) + target_link_libraries(umbra_static PUBLIC KF6::FileMetaData) +endif() kconfig_target_kcfg_file(umbra_static FILE config.kcfg CLASS_NAME Config MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE USE_ENUM_TYPES) kconfig_target_kcfg_file(umbra_static FILE accountconfig.kcfg CLASS_NAME AccountConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE USE_ENUM_TYPES) kconfig_target_kcfg_file(umbra_static FILE profileconfig.kcfg CLASS_NAME ProfileConfig MUTATORS GENERATE_PROPERTIES GENERATE_MOC DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR QML_REGISTRATION QML_UNCREATABLE USE_ENUM_TYPES) diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h index b647d74..3b09a09 100755 --- a/launcher/include/launchercore.h +++ b/launcher/include/launchercore.h @@ -106,6 +106,13 @@ class LauncherCore : public QObject Q_INVOKABLE void resetServerConfiguration(Account *account); + /** + * @brief Translates this path from its sandbox form (if applicable) to the host path. + * + * @note This should be used to display any and all paths, if users are exposed to the Documents API they are (rightfully) confused! + */ + Q_INVOKABLE QString readHostPath(const QString &path); + Q_SIGNALS: void loadingFinished(); void successfulLaunch(); diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp index 1993f16..c42d1d8 100755 --- a/launcher/src/launchercore.cpp +++ b/launcher/src/launchercore.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later +#ifdef Q_OS_LINUX +#include +#endif #include #include #include @@ -350,4 +353,16 @@ void LauncherCore::resetServerConfiguration(Account *account) Q_EMIT account->resetConfiguration(); } +QString LauncherCore::readHostPath(const QString &path) +{ +#ifdef Q_OS_LINUX + KFileMetaData::UserMetaData metadata(path); + if (metadata.hasAttribute(QStringLiteral("document-portal.host-path"))) { + return metadata.attribute(QStringLiteral("document-portal.host-path")); + } +#endif + + return path; +} + #include "moc_launchercore.cpp" diff --git a/launcher/ui/Settings/AccountSettings.qml b/launcher/ui/Settings/AccountSettings.qml index e31dd9b..d8cb4d4 100644 --- a/launcher/ui/Settings/AccountSettings.qml +++ b/launcher/ui/Settings/AccountSettings.qml @@ -106,7 +106,7 @@ FormCard.FormCardPage { text: i18n("Wine Prefix Folder") folder: page.account.config.winePrefixPath - displayText: page.account.isWinePrefixDefault ? i18n("Default Location") : folder + displayText: page.account.isWinePrefixDefault ? i18n("Default Location") : LauncherCore.readHostPath(folder) visible: LauncherCore.config.showDevTools onAccepted: (path) => { diff --git a/launcher/ui/Settings/ProfileSettings.qml b/launcher/ui/Settings/ProfileSettings.qml index eede6e2..9a84c8d 100644 --- a/launcher/ui/Settings/ProfileSettings.qml +++ b/launcher/ui/Settings/ProfileSettings.qml @@ -84,7 +84,7 @@ FormCard.FormCardPage { text: i18n("Game Folder") folder: page.profile.config.gamePath - displayText: page.profile.isGamePathDefault ? i18n("Default Location") : folder + displayText: page.profile.isGamePathDefault ? i18n("Default Location") : LauncherCore.readHostPath(folder) onAccepted: (folder) => { page.profile.config.gamePath = folder; From 0550d3c63a8a33c7d7cbeebc94cc84b3d83e19d1 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:57:23 -0400 Subject: [PATCH 4/6] Switch to KSystemInhibitor for inhibiting suspend/sleep This uses the portal API in the Flatpak and we even gain Windows support! --- CMakeLists.txt | 5 +---- launcher/CMakeLists.txt | 9 ++------ launcher/include/launchercore.h | 3 ++- launcher/src/launchercore.cpp | 39 +++++---------------------------- 4 files changed, 10 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7950992..466d699 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,11 +37,8 @@ find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS Concurrent Test QmlPrivate) # QmlPrivate is here to work around a QCoro issue with 6.10, remove later -if (LINUX) - find_package(Qt6 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS DBus) -endif () -find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami I18n Config CoreAddons Archive IconThemes) +find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami I18n Config CoreAddons Archive IconThemes GuiAddons) find_package(KF6KirigamiAddons 1.7.0 REQUIRED) find_package(QCoro6 REQUIRED COMPONENTS Core Network Qml) qcoro_enable_coroutines() diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index f6e0dab..089e098 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -78,7 +78,8 @@ target_link_libraries(umbra_static PUBLIC QCoro::Core QCoro::Network QCoro::Qml - physis) + physis + KF6::GuiAddons) if (LINUX) target_link_libraries(umbra_static PUBLIC KF6::FileMetaData) endif() @@ -91,12 +92,6 @@ if (NOT MSVC) target_compile_options(umbra_static PUBLIC -fexceptions) endif () -# For screensaver inhibition on Linux -if (TARGET Qt6::DBus) - target_link_libraries(umbra_static PRIVATE Qt6::DBus) - target_compile_definitions(umbra_static PRIVATE -DHAS_DBUS) -endif () - add_executable(umbra) target_sources(umbra PRIVATE diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h index 3b09a09..cbe4766 100755 --- a/launcher/include/launchercore.h +++ b/launcher/include/launchercore.h @@ -15,6 +15,7 @@ class SquareEnixLogin; class AssetUpdater; class GameRunner; +class KSystemInhibitor; class LoginInformation : public QObject { @@ -163,5 +164,5 @@ class LauncherCore : public QObject int m_currentProfileIndex = 0; - unsigned int screenSaverDbusCookie = 0; + KSystemInhibitor *m_inhibitor = nullptr; }; diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp index c42d1d8..0ade413 100755 --- a/launcher/src/launchercore.cpp +++ b/launcher/src/launchercore.cpp @@ -5,6 +5,7 @@ #include #endif #include +#include #include #include #include @@ -22,12 +23,6 @@ #include "squareenixlogin.h" #include "utility.h" -#ifdef HAS_DBUS -#include -#include -#include -#endif - using namespace Qt::StringLiterals; LauncherCore::LauncherCore() @@ -287,38 +282,14 @@ QCoro::Task<> LauncherCore::beginAutoConfiguration(Account *account, QString url void LauncherCore::inhibitSleep() { -#ifdef HAS_DBUS - if (screenSaverDbusCookie != 0) - return; - - QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"), - QStringLiteral("/ScreenSaver"), - QStringLiteral("org.freedesktop.ScreenSaver"), - QStringLiteral("Inhibit")); - message << QGuiApplication::desktopFileName(); - message << i18n("Playing FFXIV"); - - const QDBusReply reply = QDBusConnection::sessionBus().call(message); - if (reply.isValid()) { - screenSaverDbusCookie = reply.value(); - } -#endif + uninhibitSleep(); // clean up the previous one (if any) + m_inhibitor = new KSystemInhibitor(i18n("Playing a game"), KSystemInhibitor::Type::Suspend, nullptr, this); } void LauncherCore::uninhibitSleep() { -#ifdef HAS_DBUS - if (screenSaverDbusCookie == 0) - return; - - QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"), - QStringLiteral("/ScreenSaver"), - QStringLiteral("org.freedesktop.ScreenSaver"), - QStringLiteral("UnInhibit")); - message << static_cast(screenSaverDbusCookie); - screenSaverDbusCookie = 0; - QDBusConnection::sessionBus().send(message); -#endif + delete m_inhibitor; + m_inhibitor = nullptr; } QString LauncherCore::currentProfileId() const From a17783c0ac82301763de1504b0812dc787059f86 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:58:00 -0400 Subject: [PATCH 5/6] Read host path for the Wine Executable setting too --- launcher/ui/Settings/ProfileSettings.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/Settings/ProfileSettings.qml b/launcher/ui/Settings/ProfileSettings.qml index 9a84c8d..15f80a7 100644 --- a/launcher/ui/Settings/ProfileSettings.qml +++ b/launcher/ui/Settings/ProfileSettings.qml @@ -147,7 +147,7 @@ FormCard.FormCardPage { id: winePathDelegate text: i18n("Wine Executable") - file: page.profile.winePath + file: LauncherCore.readHostPath(page.profile.winePath) visible: page.profile.config.wineType !== Profile.BuiltIn onAccepted: (path) => { From 886ae00746627497a01b5454d0ed42b5e2352546 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 28 May 2026 19:58:33 -0400 Subject: [PATCH 6/6] Remove usage of isSteamDeck which doesn't exist in Umbra --- launcher/ui/Main.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/launcher/ui/Main.qml b/launcher/ui/Main.qml index 6ceef2c..c0be1d9 100644 --- a/launcher/ui/Main.qml +++ b/launcher/ui/Main.qml @@ -88,11 +88,7 @@ Kirigami.ApplicationWindow { } function pushDialogLayer(url: string): void { - if (LauncherCore.isSteamDeck) { - pageStack.layers.push(url) - } else { - pageStack.pushDialogLayer(url) - } + pageStack.pushDialogLayer(url); } function openUrl(url: string): void {