diff --git a/app/backend/computermanager.cpp b/app/backend/computermanager.cpp index 1400f60c..7e03d61d 100644 --- a/app/backend/computermanager.cpp +++ b/app/backend/computermanager.cpp @@ -1,7 +1,7 @@ #include "computermanager.h" #include "boxartmanager.h" #include "nvhttp.h" -#include "settings/streamingpreferences.h" +#include "nvpairingmanager.h" #include #include @@ -144,8 +144,8 @@ private: NvComputer* m_Computer; }; -ComputerManager::ComputerManager(QObject *parent) - : QObject(parent), +ComputerManager::ComputerManager(StreamingPreferences* prefs) + : m_Prefs(prefs), m_PollingRef(0), m_MdnsBrowser(nullptr), m_CompatFetcher(nullptr), @@ -352,9 +352,7 @@ void ComputerManager::startPolling() return; } - StreamingPreferences prefs; - - if (prefs.enableMdns) { + if (m_Prefs->enableMdns) { // Start an MDNS query for GameStream hosts m_MdnsServer.reset(new QMdnsEngine::Server()); m_MdnsBrowser = new QMdnsEngine::Browser(m_MdnsServer.data(), "_nvstream._tcp.local."); @@ -784,10 +782,9 @@ private: return serverInfo; } catch (...) { if (!m_Mdns) { - StreamingPreferences prefs; unsigned int portTestResult; - if (prefs.detectNetworkBlocking) { + if (m_ComputerManager->m_Prefs->detectNetworkBlocking) { // We failed to connect to the specified PC. Let's test to make sure this network // isn't blocking Moonlight, so we can tell the user about it. portTestResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443, diff --git a/app/backend/computermanager.h b/app/backend/computermanager.h index 36749b6d..91881592 100644 --- a/app/backend/computermanager.h +++ b/app/backend/computermanager.h @@ -1,7 +1,7 @@ #pragma once #include "nvcomputer.h" -#include "nvpairingmanager.h" +#include "settings/streamingpreferences.h" #include "settings/compatfetcher.h" #include @@ -218,7 +218,7 @@ class ComputerManager : public QObject friend class DelayedFlushThread; public: - explicit ComputerManager(QObject *parent = nullptr); + explicit ComputerManager(StreamingPreferences* prefs); virtual ~ComputerManager(); @@ -270,6 +270,7 @@ private: void startPollingComputer(NvComputer* computer); + StreamingPreferences* m_Prefs; int m_PollingRef; QReadWriteLock m_Lock; QMap m_KnownHosts; diff --git a/app/gui/SettingsView.qml b/app/gui/SettingsView.qml index 5bdb2b5d..57c3ef22 100644 --- a/app/gui/SettingsView.qml +++ b/app/gui/SettingsView.qml @@ -1349,15 +1349,7 @@ Flickable { font.pointSize: 12 checked: StreamingPreferences.swapFaceButtons onCheckedChanged: { - // Check if the value changed (this is called on init too) - if (StreamingPreferences.swapFaceButtons !== checked) { - StreamingPreferences.swapFaceButtons = checked - - // Save and restart SdlGamepadKeyNavigation so it can pull the new value - StreamingPreferences.save() - SdlGamepadKeyNavigation.disable() - SdlGamepadKeyNavigation.enable() - } + StreamingPreferences.swapFaceButtons = checked } ToolTip.delay: 1000 @@ -1621,10 +1613,6 @@ Flickable { if (StreamingPreferences.enableMdns != checked) { StreamingPreferences.enableMdns = checked - // We must save the updated preference to ensure - // ComputerManager can observe the change internally. - StreamingPreferences.save() - // Restart polling so the mDNS change takes effect if (window.pollingActive) { ComputerManager.stopPollingAsync() @@ -1641,15 +1629,7 @@ Flickable { font.pointSize: 12 checked: StreamingPreferences.detectNetworkBlocking onCheckedChanged: { - // This is called on init, so only do the work if we've - // actually changed the value. - if (StreamingPreferences.detectNetworkBlocking != checked) { - StreamingPreferences.detectNetworkBlocking = checked - - // We must save the updated preference to ensure - // ComputerManager can observe the change internally. - StreamingPreferences.save() - } + StreamingPreferences.detectNetworkBlocking = checked } } } diff --git a/app/gui/sdlgamepadkeynavigation.cpp b/app/gui/sdlgamepadkeynavigation.cpp index f0bd3433..e51eb776 100644 --- a/app/gui/sdlgamepadkeynavigation.cpp +++ b/app/gui/sdlgamepadkeynavigation.cpp @@ -8,8 +8,9 @@ #define AXIS_NAVIGATION_REPEAT_DELAY 150 -SdlGamepadKeyNavigation::SdlGamepadKeyNavigation() - : m_Enabled(false), +SdlGamepadKeyNavigation::SdlGamepadKeyNavigation(StreamingPreferences* prefs) + : m_Prefs(prefs), + m_Enabled(false), m_UiNavMode(false), m_FirstPoll(false), m_LastAxisNavigationEventTime(0) @@ -119,7 +120,7 @@ void SdlGamepadKeyNavigation::onPollingTimerFired() QEvent::Type::KeyPress : QEvent::Type::KeyRelease; // Swap face buttons if needed - if (m_Prefs.swapFaceButtons) { + if (m_Prefs->swapFaceButtons) { switch (event.cbutton.button) { case SDL_CONTROLLER_BUTTON_A: event.cbutton.button = SDL_CONTROLLER_BUTTON_B; diff --git a/app/gui/sdlgamepadkeynavigation.h b/app/gui/sdlgamepadkeynavigation.h index feb6cf3c..af283be1 100644 --- a/app/gui/sdlgamepadkeynavigation.h +++ b/app/gui/sdlgamepadkeynavigation.h @@ -12,7 +12,7 @@ class SdlGamepadKeyNavigation : public QObject Q_OBJECT public: - SdlGamepadKeyNavigation(); + SdlGamepadKeyNavigation(StreamingPreferences* prefs); ~SdlGamepadKeyNavigation(); @@ -31,11 +31,11 @@ private slots: void onPollingTimerFired(); private: + StreamingPreferences* m_Prefs; QTimer* m_PollingTimer; QList m_Gamepads; bool m_Enabled; bool m_UiNavMode; bool m_FirstPoll; Uint32 m_LastAxisNavigationEventTime; - StreamingPreferences m_Prefs; }; diff --git a/app/main.cpp b/app/main.cpp index ca13eddc..cc8dd381 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -570,8 +570,7 @@ int main(int argc, char *argv[]) runtimeVersion.major, runtimeVersion.minor, runtimeVersion.patch); // Apply the initial translation based on user preference - StreamingPreferences prefs; - prefs.retranslate(); + StreamingPreferences::get()->retranslate(); // Trickily declare the translation for dialog buttons QCoreApplication::translate("QPlatformTheme", "&Yes"); @@ -636,8 +635,8 @@ int main(int argc, char *argv[]) qmlRegisterUncreatableType("Session", 1, 0, "Session", "Session cannot be created from QML"); qmlRegisterSingletonType("ComputerManager", 1, 0, "ComputerManager", - [](QQmlEngine*, QJSEngine*) -> QObject* { - return new ComputerManager(); + [](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* { + return new ComputerManager(StreamingPreferences::get(qmlEngine)); }); qmlRegisterSingletonType("AutoUpdateChecker", 1, 0, "AutoUpdateChecker", @@ -651,13 +650,13 @@ int main(int argc, char *argv[]) }); qmlRegisterSingletonType("SdlGamepadKeyNavigation", 1, 0, "SdlGamepadKeyNavigation", - [](QQmlEngine*, QJSEngine*) -> QObject* { - return new SdlGamepadKeyNavigation(); + [](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* { + return new SdlGamepadKeyNavigation(StreamingPreferences::get(qmlEngine)); }); qmlRegisterSingletonType("StreamingPreferences", 1, 0, "StreamingPreferences", [](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* { - return new StreamingPreferences(qmlEngine); + return StreamingPreferences::get(qmlEngine); }); // Create the identity manager on the main thread @@ -688,7 +687,7 @@ int main(int argc, char *argv[]) case GlobalCommandLineParser::StreamRequested: { initialView = "qrc:/gui/CliStartStreamSegue.qml"; - StreamingPreferences* preferences = new StreamingPreferences(&app); + StreamingPreferences* preferences = StreamingPreferences::get(); StreamCommandLineParser streamParser; streamParser.parse(app.arguments(), preferences); QString host = streamParser.getHost(); @@ -720,7 +719,7 @@ int main(int argc, char *argv[]) ListCommandLineParser listParser; listParser.parse(app.arguments()); auto launcher = new CliListApps::Launcher(listParser.getHost(), listParser, &app); - launcher->execute(new ComputerManager(&app)); + launcher->execute(new ComputerManager(StreamingPreferences::get())); hasGUI = false; break; } diff --git a/app/settings/streamingpreferences.cpp b/app/settings/streamingpreferences.cpp index 6a883bc0..a2539835 100644 --- a/app/settings/streamingpreferences.cpp +++ b/app/settings/streamingpreferences.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -48,18 +49,50 @@ #define CURRENT_DEFAULT_VER 2 -StreamingPreferences::StreamingPreferences(QObject *parent) - : QObject(parent), - m_QmlEngine(nullptr) +static StreamingPreferences* s_GlobalPrefs; +static QReadWriteLock s_GlobalPrefsLock; + +StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine) + : m_QmlEngine(qmlEngine) { reload(); } -StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent) - : QObject(parent), - m_QmlEngine(qmlEngine) +StreamingPreferences* StreamingPreferences::get(QQmlEngine *qmlEngine) { - reload(); + { + QReadLocker readGuard(&s_GlobalPrefsLock); + + // If we have a preference object and it's associated with a QML engine or + // if the caller didn't specify a QML engine, return the existing object. + if (s_GlobalPrefs && (s_GlobalPrefs->m_QmlEngine || !qmlEngine)) { + // The lifetime logic here relies on the QML engine also being a singleton. + Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine); + return s_GlobalPrefs; + } + } + + { + QWriteLocker writeGuard(&s_GlobalPrefsLock); + + // If we already have an preference object but the QML engine is now available, + // associate the QML engine with the preferences. + if (s_GlobalPrefs) { + if (!s_GlobalPrefs->m_QmlEngine) { + s_GlobalPrefs->m_QmlEngine = qmlEngine; + } + else { + // We could reach this codepath if another thread raced with us + // and created the object while we were outside the pref lock. + Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine); + } + } + else { + s_GlobalPrefs = new StreamingPreferences(qmlEngine); + } + + return s_GlobalPrefs; + } } void StreamingPreferences::reload() diff --git a/app/settings/streamingpreferences.h b/app/settings/streamingpreferences.h index 0e2cf365..4ca778c4 100644 --- a/app/settings/streamingpreferences.h +++ b/app/settings/streamingpreferences.h @@ -9,8 +9,7 @@ class StreamingPreferences : public QObject Q_OBJECT public: - StreamingPreferences(QObject *parent = nullptr); - StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent = nullptr); + static StreamingPreferences* get(QQmlEngine *qmlEngine = nullptr); Q_INVOKABLE static int getDefaultBitrate(int width, int height, int fps); @@ -206,6 +205,8 @@ signals: void languageChanged(); private: + explicit StreamingPreferences(QQmlEngine *qmlEngine); + QString getSuffixFromLanguage(Language lang); QQmlEngine* m_QmlEngine; diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 285aab47..c10b97a4 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -541,7 +541,7 @@ bool Session::populateDecoderProperties(SDL_Window* window) } Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *preferences) - : m_Preferences(preferences ? preferences : new StreamingPreferences(this)), + : m_Preferences(preferences ? preferences : StreamingPreferences::get()), m_IsFullScreen(m_Preferences->windowMode != StreamingPreferences::WM_WINDOWED || !WMUtils::isRunningDesktopEnvironment()), m_Computer(computer), m_App(app),