mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-02 07:46:07 +00:00
Implement pairing via GUI
This commit is contained in:
parent
1b1ad86271
commit
6687936e2f
@ -4,6 +4,7 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
|
#include <QThreadPool>
|
||||||
|
|
||||||
#define SER_HOSTS "hosts"
|
#define SER_HOSTS "hosts"
|
||||||
#define SER_NAME "hostname"
|
#define SER_NAME "hostname"
|
||||||
@ -331,27 +332,54 @@ QVector<NvComputer*> ComputerManager::getComputers()
|
|||||||
return QVector<NvComputer*>::fromList(m_KnownHosts.values());
|
return QVector<NvComputer*>::fromList(m_KnownHosts.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComputerManager::deleteHost(NvComputer* computer)
|
class DeferredHostDeletionTask : public QRunnable
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&m_Lock);
|
public:
|
||||||
|
DeferredHostDeletionTask(ComputerManager* cm, NvComputer* computer)
|
||||||
|
: m_Computer(computer),
|
||||||
|
m_ComputerManager(cm) {}
|
||||||
|
|
||||||
QThread* pollingThread = m_PollThreads[computer->uuid];
|
void run()
|
||||||
if (pollingThread != nullptr) {
|
{
|
||||||
pollingThread->requestInterruption();
|
QWriteLocker lock(&m_ComputerManager->m_Lock);
|
||||||
|
|
||||||
// We must wait here because we're going to delete computer
|
QThread* pollingThread = m_ComputerManager->m_PollThreads[m_Computer->uuid];
|
||||||
// and we can't do that out from underneath the poller.
|
if (pollingThread != nullptr) {
|
||||||
pollingThread->wait();
|
pollingThread->requestInterruption();
|
||||||
|
|
||||||
// The thread is safe to delete now
|
// We must wait here because we're going to delete computer
|
||||||
Q_ASSERT(pollingThread->isFinished());
|
// and we can't do that out from underneath the poller.
|
||||||
delete pollingThread;
|
pollingThread->wait();
|
||||||
|
|
||||||
|
// The thread is safe to delete now
|
||||||
|
Q_ASSERT(pollingThread->isFinished());
|
||||||
|
delete pollingThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ComputerManager->m_PollThreads.remove(m_Computer->uuid);
|
||||||
|
m_ComputerManager->m_KnownHosts.remove(m_Computer->uuid);
|
||||||
|
|
||||||
|
delete m_Computer;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_PollThreads.remove(computer->uuid);
|
private:
|
||||||
m_KnownHosts.remove(computer->uuid);
|
NvComputer* m_Computer;
|
||||||
|
ComputerManager* m_ComputerManager;
|
||||||
|
};
|
||||||
|
|
||||||
delete computer;
|
void ComputerManager::deleteHost(NvComputer* computer)
|
||||||
|
{
|
||||||
|
// Punt to a worker thread to avoid stalling the
|
||||||
|
// UI while waiting for the polling thread to die
|
||||||
|
QThreadPool::globalInstance()->start(new DeferredHostDeletionTask(this, computer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputerManager::pairHost(NvComputer* computer, QString pin)
|
||||||
|
{
|
||||||
|
// Punt to a worker thread to avoid stalling the
|
||||||
|
// UI while waiting for pairing to complete
|
||||||
|
PendingPairingTask* pairing = new PendingPairingTask(this, computer, pin);
|
||||||
|
QThreadPool::globalInstance()->start(pairing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComputerManager::stopPollingAsync()
|
void ComputerManager::stopPollingAsync()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "nvhttp.h"
|
#include "nvhttp.h"
|
||||||
|
#include "nvpairingmanager.h"
|
||||||
|
|
||||||
#include <qmdnsengine/server.h>
|
#include <qmdnsengine/server.h>
|
||||||
#include <qmdnsengine/cache.h>
|
#include <qmdnsengine/cache.h>
|
||||||
@ -10,6 +11,7 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QRunnable>
|
||||||
|
|
||||||
class NvComputer
|
class NvComputer
|
||||||
{
|
{
|
||||||
@ -239,6 +241,8 @@ class ComputerManager : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class DeferredHostDeletionTask;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ComputerManager(QObject *parent = nullptr);
|
explicit ComputerManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
@ -248,6 +252,8 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE bool addNewHost(QString address, bool mdns);
|
Q_INVOKABLE bool addNewHost(QString address, bool mdns);
|
||||||
|
|
||||||
|
void pairHost(NvComputer* computer, QString pin);
|
||||||
|
|
||||||
QVector<NvComputer*> getComputers();
|
QVector<NvComputer*> getComputers();
|
||||||
|
|
||||||
// computer is deleted inside this call
|
// computer is deleted inside this call
|
||||||
@ -256,6 +262,8 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void computerStateChanged(NvComputer* computer);
|
void computerStateChanged(NvComputer* computer);
|
||||||
|
|
||||||
|
void pairingCompleted(NvComputer* computer, QString error);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleComputerStateChanged(NvComputer* computer);
|
void handleComputerStateChanged(NvComputer* computer);
|
||||||
|
|
||||||
@ -275,3 +283,54 @@ private:
|
|||||||
QMdnsEngine::Cache m_MdnsCache;
|
QMdnsEngine::Cache m_MdnsCache;
|
||||||
QVector<MdnsPendingComputer*> m_PendingResolution;
|
QVector<MdnsPendingComputer*> m_PendingResolution;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PendingPairingTask : public QObject, public QRunnable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PendingPairingTask(ComputerManager* computerManager, NvComputer* computer, QString pin)
|
||||||
|
: m_ComputerManager(computerManager),
|
||||||
|
m_Computer(computer),
|
||||||
|
m_Pin(pin)
|
||||||
|
{
|
||||||
|
connect(this, &PendingPairingTask::pairingCompleted,
|
||||||
|
computerManager, &ComputerManager::pairingCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pairingCompleted(NvComputer* computer, QString error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
NvPairingManager pairingManager(m_Computer->activeAddress);
|
||||||
|
|
||||||
|
try {
|
||||||
|
NvPairingManager::PairState result = pairingManager.pair(m_Computer->appVersion, m_Pin);
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case NvPairingManager::PairState::PIN_WRONG:
|
||||||
|
emit pairingCompleted(m_Computer, "The PIN from the PC didn't match. Please try again.");
|
||||||
|
break;
|
||||||
|
case NvPairingManager::PairState::FAILED:
|
||||||
|
emit pairingCompleted(m_Computer, "Pairing failed. Please try again.");
|
||||||
|
break;
|
||||||
|
case NvPairingManager::PairState::ALREADY_IN_PROGRESS:
|
||||||
|
emit pairingCompleted(m_Computer, "Another pairing attempt is already in progress.");
|
||||||
|
break;
|
||||||
|
case NvPairingManager::PairState::PAIRED:
|
||||||
|
emit pairingCompleted(m_Computer, nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const GfeHttpResponseException& e) {
|
||||||
|
emit pairingCompleted(m_Computer,
|
||||||
|
QString::fromLocal8Bit(e.getStatusMessage()) +
|
||||||
|
" (Error " + QString::number(e.getStatusCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputerManager* m_ComputerManager;
|
||||||
|
NvComputer* m_Computer;
|
||||||
|
QString m_Pin;
|
||||||
|
};
|
||||||
|
@ -25,9 +25,8 @@ NvHTTP::NvHTTP(QString address) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
QVector<int>
|
QVector<int>
|
||||||
NvHTTP::getServerVersionQuad(QString serverInfo)
|
NvHTTP::parseQuad(QString quad)
|
||||||
{
|
{
|
||||||
QString quad = getXmlString(serverInfo, "appversion");
|
|
||||||
QStringList parts = quad.split(".");
|
QStringList parts = quad.split(".");
|
||||||
QVector<int> ret;
|
QVector<int> ret;
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ public:
|
|||||||
|
|
||||||
static
|
static
|
||||||
QVector<int>
|
QVector<int>
|
||||||
getServerVersionQuad(QString serverInfo);
|
parseQuad(QString quad);
|
||||||
|
|
||||||
void
|
void
|
||||||
quitApp();
|
quitApp();
|
||||||
|
@ -42,12 +42,6 @@ NvPairingManager::~NvPairingManager()
|
|||||||
EVP_PKEY_free(m_PrivateKey);
|
EVP_PKEY_free(m_PrivateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
|
||||||
NvPairingManager::generatePinString()
|
|
||||||
{
|
|
||||||
return QString::asprintf("%04d", QRandomGenerator::global()->bounded(10000));
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray
|
QByteArray
|
||||||
NvPairingManager::generateRandomBytes(int length)
|
NvPairingManager::generateRandomBytes(int length)
|
||||||
{
|
{
|
||||||
@ -167,9 +161,9 @@ NvPairingManager::saltPin(QByteArray salt, QString pin)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NvPairingManager::PairState
|
NvPairingManager::PairState
|
||||||
NvPairingManager::pair(QString serverInfo, QString pin)
|
NvPairingManager::pair(QString appVersion, QString pin)
|
||||||
{
|
{
|
||||||
int serverMajorVersion = NvHTTP::getServerVersionQuad(serverInfo).at(0);
|
int serverMajorVersion = NvHTTP::parseQuad(appVersion).at(0);
|
||||||
qDebug() << "Pairing with server generation: " << serverMajorVersion;
|
qDebug() << "Pairing with server generation: " << serverMajorVersion;
|
||||||
|
|
||||||
QCryptographicHash::Algorithm hashAlgo;
|
QCryptographicHash::Algorithm hashAlgo;
|
||||||
|
@ -23,11 +23,8 @@ public:
|
|||||||
|
|
||||||
~NvPairingManager();
|
~NvPairingManager();
|
||||||
|
|
||||||
QString
|
|
||||||
generatePinString();
|
|
||||||
|
|
||||||
PairState
|
PairState
|
||||||
pair(QString serverInfo, QString pin);
|
pair(QString appVersion, QString pin);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray
|
QByteArray
|
||||||
|
@ -8,6 +8,8 @@ import ComputerModel 1.0
|
|||||||
import ComputerManager 1.0
|
import ComputerManager 1.0
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
|
property ComputerModel computerModel : createModel()
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 5
|
anchors.leftMargin: 5
|
||||||
anchors.topMargin: 5
|
anchors.topMargin: 5
|
||||||
@ -26,10 +28,26 @@ GridView {
|
|||||||
ComputerManager.stopPollingAsync()
|
ComputerManager.stopPollingAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pairingComplete(error)
|
||||||
|
{
|
||||||
|
// Close the PIN dialog
|
||||||
|
pairDialog.close()
|
||||||
|
|
||||||
|
// Start polling again
|
||||||
|
ComputerManager.startPolling()
|
||||||
|
|
||||||
|
// Display a failed dialog if we got an error
|
||||||
|
if (error !== null) {
|
||||||
|
pairingFailedDialog.text = error
|
||||||
|
pairingFailedDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createModel()
|
function createModel()
|
||||||
{
|
{
|
||||||
var model = Qt.createQmlObject('import ComputerModel 1.0; ComputerModel {}', parent, '')
|
var model = Qt.createQmlObject('import ComputerModel 1.0; ComputerModel {}', parent, '')
|
||||||
model.initialize(ComputerManager)
|
model.initialize(ComputerManager)
|
||||||
|
model.pairingCompleted.connect(pairingComplete)
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,12 +132,20 @@ GridView {
|
|||||||
else {
|
else {
|
||||||
if (!model.busy) {
|
if (!model.busy) {
|
||||||
var pin = ("0000" + Math.floor(Math.random() * 10000)).slice(-4)
|
var pin = ("0000" + Math.floor(Math.random() * 10000)).slice(-4)
|
||||||
|
|
||||||
|
// Stop polling, since pairing may make GFE unresponsive
|
||||||
|
ComputerManager.stopPollingAsync()
|
||||||
|
|
||||||
|
// Kick off pairing in the background
|
||||||
|
computerModel.pairComputer(index, pin)
|
||||||
|
|
||||||
|
// Display the pairing dialog
|
||||||
pairDialog.pin = pin
|
pairDialog.pin = pin
|
||||||
// TODO: initiate pairing request
|
|
||||||
pairDialog.open()
|
pairDialog.open()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// cannot pair while something is streaming or attempting to pair
|
// cannot pair while something is streaming or attempting to pair
|
||||||
|
pairingFailedDialog.text = "This PC is currently busy. Make sure to quit any running games and try again."
|
||||||
pairingFailedDialog.open()
|
pairingFailedDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,8 +161,7 @@ GridView {
|
|||||||
id: pairingFailedDialog
|
id: pairingFailedDialog
|
||||||
// don't allow edits to the rest of the window while open
|
// don't allow edits to the rest of the window while open
|
||||||
modality:Qt.WindowModal
|
modality:Qt.WindowModal
|
||||||
|
icon: StandardIcon.Critical
|
||||||
text:"This PC is busy: make sure no game is streaming, and try again"
|
|
||||||
standardButtons: StandardButton.Ok
|
standardButtons: StandardButton.Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "computermodel.h"
|
#include "computermodel.h"
|
||||||
#include "backend/nvpairingmanager.h"
|
|
||||||
|
|
||||||
ComputerModel::ComputerModel(QObject* object)
|
ComputerModel::ComputerModel(QObject* object)
|
||||||
: QAbstractListModel(object) {}
|
: QAbstractListModel(object) {}
|
||||||
@ -9,6 +8,8 @@ void ComputerModel::initialize(ComputerManager* computerManager)
|
|||||||
m_ComputerManager = computerManager;
|
m_ComputerManager = computerManager;
|
||||||
connect(m_ComputerManager, &ComputerManager::computerStateChanged,
|
connect(m_ComputerManager, &ComputerManager::computerStateChanged,
|
||||||
this, &ComputerModel::handleComputerStateChanged);
|
this, &ComputerModel::handleComputerStateChanged);
|
||||||
|
connect(m_ComputerManager, &ComputerManager::pairingCompleted,
|
||||||
|
this, &ComputerModel::handlePairingCompleted);
|
||||||
|
|
||||||
m_Computers = m_ComputerManager->getComputers();
|
m_Computers = m_ComputerManager->getComputers();
|
||||||
}
|
}
|
||||||
@ -92,6 +93,18 @@ void ComputerModel::deleteComputer(int computerIndex)
|
|||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComputerModel::pairComputer(int computerIndex, QString pin)
|
||||||
|
{
|
||||||
|
Q_ASSERT(computerIndex < m_Computers.count());
|
||||||
|
|
||||||
|
m_ComputerManager->pairHost(m_Computers[computerIndex], pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputerModel::handlePairingCompleted(NvComputer*, QString error)
|
||||||
|
{
|
||||||
|
emit pairingCompleted(error.isNull() ? QVariant() : error);
|
||||||
|
}
|
||||||
|
|
||||||
void ComputerModel::handleComputerStateChanged(NvComputer* computer)
|
void ComputerModel::handleComputerStateChanged(NvComputer* computer)
|
||||||
{
|
{
|
||||||
// If this is an existing computer, we can report the data changed
|
// If this is an existing computer, we can report the data changed
|
||||||
|
@ -29,9 +29,16 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void deleteComputer(int computerIndex);
|
Q_INVOKABLE void deleteComputer(int computerIndex);
|
||||||
|
|
||||||
|
Q_INVOKABLE void pairComputer(int computerIndex, QString pin);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pairingCompleted(QVariant error);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleComputerStateChanged(NvComputer* computer);
|
void handleComputerStateChanged(NvComputer* computer);
|
||||||
|
|
||||||
|
void handlePairingCompleted(NvComputer* computer, QString error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<NvComputer*> m_Computers;
|
QVector<NvComputer*> m_Computers;
|
||||||
ComputerManager* m_ComputerManager;
|
ComputerManager* m_ComputerManager;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user