Make StreamingPreferences a proper singleton

This removes the need for several hacks in SettingsView to force updates and improves performance by not reloading preferences all over the place.
This commit is contained in:
Cameron Gutman 2024-04-14 13:01:30 -05:00
parent ebe270bec5
commit d1ccd19fcc
9 changed files with 68 additions and 56 deletions

View file

@ -1,7 +1,7 @@
#include "computermanager.h"
#include "boxartmanager.h"
#include "nvhttp.h"
#include "settings/streamingpreferences.h"
#include "nvpairingmanager.h"
#include <Limelight.h>
#include <QtEndian>
@ -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,

View file

@ -1,7 +1,7 @@
#pragma once
#include "nvcomputer.h"
#include "nvpairingmanager.h"
#include "settings/streamingpreferences.h"
#include "settings/compatfetcher.h"
#include <qmdnsengine/server.h>
@ -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<QString, NvComputer*> m_KnownHosts;

View file

@ -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
}
}
}

View file

@ -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;

View file

@ -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<SDL_GameController*> m_Gamepads;
bool m_Enabled;
bool m_UiNavMode;
bool m_FirstPoll;
Uint32 m_LastAxisNavigationEventTime;
StreamingPreferences m_Prefs;
};

View file

@ -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>("Session", 1, 0, "Session", "Session cannot be created from QML");
qmlRegisterSingletonType<ComputerManager>("ComputerManager", 1, 0,
"ComputerManager",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new ComputerManager();
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
return new ComputerManager(StreamingPreferences::get(qmlEngine));
});
qmlRegisterSingletonType<AutoUpdateChecker>("AutoUpdateChecker", 1, 0,
"AutoUpdateChecker",
@ -651,13 +650,13 @@ int main(int argc, char *argv[])
});
qmlRegisterSingletonType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0,
"SdlGamepadKeyNavigation",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new SdlGamepadKeyNavigation();
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
return new SdlGamepadKeyNavigation(StreamingPreferences::get(qmlEngine));
});
qmlRegisterSingletonType<StreamingPreferences>("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;
}

View file

@ -5,6 +5,7 @@
#include <QTranslator>
#include <QCoreApplication>
#include <QLocale>
#include <QReadWriteLock>
#include <QtMath>
#include <QtDebug>
@ -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()

View file

@ -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;

View file

@ -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),