2018-07-04 23:40:21 +00:00
|
|
|
#include "computermodel.h"
|
|
|
|
|
2018-09-05 23:10:32 +00:00
|
|
|
#include <QThreadPool>
|
|
|
|
|
2020-07-12 20:19:26 +00:00
|
|
|
#include <random>
|
|
|
|
|
2018-07-04 23:40:21 +00:00
|
|
|
ComputerModel::ComputerModel(QObject* object)
|
2018-07-06 05:08:55 +00:00
|
|
|
: QAbstractListModel(object) {}
|
|
|
|
|
|
|
|
void ComputerModel::initialize(ComputerManager* computerManager)
|
2018-07-04 23:40:21 +00:00
|
|
|
{
|
2018-07-06 05:08:55 +00:00
|
|
|
m_ComputerManager = computerManager;
|
|
|
|
connect(m_ComputerManager, &ComputerManager::computerStateChanged,
|
2018-07-04 23:40:21 +00:00
|
|
|
this, &ComputerModel::handleComputerStateChanged);
|
2018-07-06 06:12:55 +00:00
|
|
|
connect(m_ComputerManager, &ComputerManager::pairingCompleted,
|
|
|
|
this, &ComputerModel::handlePairingCompleted);
|
2018-07-04 23:40:21 +00:00
|
|
|
|
2018-07-06 05:08:55 +00:00
|
|
|
m_Computers = m_ComputerManager->getComputers();
|
2018-07-04 23:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant ComputerModel::data(const QModelIndex& index, int role) const
|
|
|
|
{
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2018-07-06 03:11:35 +00:00
|
|
|
Q_ASSERT(index.row() < m_Computers.count());
|
2018-07-04 23:40:21 +00:00
|
|
|
|
|
|
|
NvComputer* computer = m_Computers[index.row()];
|
|
|
|
QReadLocker lock(&computer->lock);
|
|
|
|
|
|
|
|
switch (role) {
|
|
|
|
case NameRole:
|
|
|
|
return computer->name;
|
|
|
|
case OnlineRole:
|
|
|
|
return computer->state == NvComputer::CS_ONLINE;
|
|
|
|
case PairedRole:
|
2018-07-06 03:41:17 +00:00
|
|
|
return computer->pairState == NvComputer::PS_PAIRED;
|
2018-07-04 23:40:21 +00:00
|
|
|
case BusyRole:
|
|
|
|
return computer->currentGameId != 0;
|
2018-08-01 05:10:38 +00:00
|
|
|
case WakeableRole:
|
|
|
|
return !computer->macAddress.isEmpty();
|
2018-08-05 19:13:08 +00:00
|
|
|
case StatusUnknownRole:
|
|
|
|
return computer->state == NvComputer::CS_UNKNOWN;
|
2018-07-04 23:40:21 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ComputerModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
// We should not return a count for valid index values,
|
|
|
|
// only the parent (which will not have a "valid" index).
|
|
|
|
if (parent.isValid()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-27 04:31:51 +00:00
|
|
|
return m_Computers.count();
|
2018-07-04 23:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QHash<int, QByteArray> ComputerModel::roleNames() const
|
|
|
|
{
|
|
|
|
QHash<int, QByteArray> names;
|
|
|
|
|
|
|
|
names[NameRole] = "name";
|
|
|
|
names[OnlineRole] = "online";
|
|
|
|
names[PairedRole] = "paired";
|
|
|
|
names[BusyRole] = "busy";
|
2018-08-01 05:10:38 +00:00
|
|
|
names[WakeableRole] = "wakeable";
|
2018-08-05 19:13:08 +00:00
|
|
|
names[StatusUnknownRole] = "statusUnknown";
|
2018-07-04 23:40:21 +00:00
|
|
|
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
2018-07-07 23:30:26 +00:00
|
|
|
Session* ComputerModel::createSessionForCurrentGame(int computerIndex)
|
|
|
|
{
|
|
|
|
Q_ASSERT(computerIndex < m_Computers.count());
|
|
|
|
|
|
|
|
NvComputer* computer = m_Computers[computerIndex];
|
|
|
|
|
|
|
|
// We must currently be streaming a game to use this function
|
|
|
|
Q_ASSERT(computer->currentGameId != 0);
|
|
|
|
|
|
|
|
for (NvApp& app : computer->appList) {
|
|
|
|
if (app.id == computer->currentGameId) {
|
|
|
|
return new Session(computer, app);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a current running app but it's not in our app list
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-06 05:08:55 +00:00
|
|
|
void ComputerModel::deleteComputer(int computerIndex)
|
|
|
|
{
|
|
|
|
Q_ASSERT(computerIndex < m_Computers.count());
|
|
|
|
|
|
|
|
beginRemoveRows(QModelIndex(), computerIndex, computerIndex);
|
|
|
|
|
|
|
|
// m_Computer[computerIndex] will be deleted by this call
|
|
|
|
m_ComputerManager->deleteHost(m_Computers[computerIndex]);
|
|
|
|
|
|
|
|
// Remove the now invalid item
|
|
|
|
m_Computers.removeAt(computerIndex);
|
|
|
|
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
|
2018-09-05 23:10:32 +00:00
|
|
|
class DeferredWakeHostTask : public QRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DeferredWakeHostTask(NvComputer* computer)
|
|
|
|
: m_Computer(computer) {}
|
|
|
|
|
|
|
|
void run()
|
|
|
|
{
|
|
|
|
m_Computer->wake();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
NvComputer* m_Computer;
|
|
|
|
};
|
|
|
|
|
|
|
|
void ComputerModel::wakeComputer(int computerIndex)
|
2018-07-06 07:34:16 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(computerIndex < m_Computers.count());
|
|
|
|
|
2018-09-05 23:10:32 +00:00
|
|
|
DeferredWakeHostTask* wakeTask = new DeferredWakeHostTask(m_Computers[computerIndex]);
|
|
|
|
QThreadPool::globalInstance()->start(wakeTask);
|
2020-05-02 01:34:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ComputerModel::renameComputer(int computerIndex, QString name)
|
|
|
|
{
|
|
|
|
Q_ASSERT(computerIndex < m_Computers.count());
|
|
|
|
|
|
|
|
m_ComputerManager->renameHost(m_Computers[computerIndex], name);
|
2020-07-12 20:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Use QRandomGenerator when we drop Qt 5.9 support
|
|
|
|
QString ComputerModel::generatePinString()
|
|
|
|
{
|
|
|
|
std::uniform_int_distribution<int> dist(0, 9999);
|
|
|
|
std::random_device rd;
|
|
|
|
std::mt19937 engine(rd());
|
|
|
|
|
|
|
|
return QString::asprintf("%04u", dist(engine));
|
2018-07-06 07:34:16 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 03:25:26 +00:00
|
|
|
class DeferredTestConnectionTask : public QObject, public QRunnable
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
void run()
|
|
|
|
{
|
|
|
|
unsigned int portTestResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443, ML_PORT_FLAG_ALL);
|
|
|
|
if (portTestResult == ML_TEST_RESULT_INCONCLUSIVE) {
|
|
|
|
emit connectionTestCompleted(-1, QString());
|
|
|
|
}
|
|
|
|
else {
|
2020-12-23 20:08:20 +00:00
|
|
|
char blockedPorts[512];
|
|
|
|
LiStringifyPortFlags(portTestResult, "\n", blockedPorts, sizeof(blockedPorts));
|
|
|
|
emit connectionTestCompleted(portTestResult, QString(blockedPorts));
|
2020-08-09 03:25:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void connectionTestCompleted(int result, QString blockedPorts);
|
|
|
|
};
|
|
|
|
|
|
|
|
void ComputerModel::testConnectionForComputer(int)
|
|
|
|
{
|
|
|
|
DeferredTestConnectionTask* testConnectionTask = new DeferredTestConnectionTask();
|
|
|
|
QObject::connect(testConnectionTask, &DeferredTestConnectionTask::connectionTestCompleted,
|
|
|
|
this, &ComputerModel::connectionTestCompleted);
|
|
|
|
QThreadPool::globalInstance()->start(testConnectionTask);
|
|
|
|
}
|
|
|
|
|
2018-07-06 06:12:55 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-09-15 01:36:15 +00:00
|
|
|
emit pairingCompleted(error.isEmpty() ? QVariant() : error);
|
2018-07-06 06:12:55 +00:00
|
|
|
}
|
|
|
|
|
2018-07-04 23:40:21 +00:00
|
|
|
void ComputerModel::handleComputerStateChanged(NvComputer* computer)
|
|
|
|
{
|
|
|
|
// If this is an existing computer, we can report the data changed
|
|
|
|
int index = m_Computers.indexOf(computer);
|
|
|
|
if (index >= 0) {
|
|
|
|
// Let the view know that this specific computer changed
|
|
|
|
emit dataChanged(createIndex(index, 0), createIndex(index, 0));
|
|
|
|
}
|
|
|
|
else {
|
2018-07-06 07:42:07 +00:00
|
|
|
// This is a new PC which may be inserted at an arbitrary point
|
|
|
|
// in our computer list (since it comes from CM's QMap). Reload
|
|
|
|
// the whole model state to ensure it stays consistent.
|
|
|
|
beginResetModel();
|
|
|
|
m_Computers = m_ComputerManager->getComputers();
|
|
|
|
endResetModel();
|
2018-07-04 23:40:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-09 03:25:26 +00:00
|
|
|
#include "computermodel.moc"
|