#include "computermodel.h" #include #include ComputerModel::ComputerModel(QObject* object) : QAbstractListModel(object) {} void ComputerModel::initialize(ComputerManager* computerManager) { m_ComputerManager = computerManager; connect(m_ComputerManager, &ComputerManager::computerStateChanged, this, &ComputerModel::handleComputerStateChanged); connect(m_ComputerManager, &ComputerManager::pairingCompleted, this, &ComputerModel::handlePairingCompleted); m_Computers = m_ComputerManager->getComputers(); } QVariant ComputerModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } Q_ASSERT(index.row() < m_Computers.count()); 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: return computer->pairState == NvComputer::PS_PAIRED; case BusyRole: return computer->currentGameId != 0; case WakeableRole: return !computer->macAddress.isEmpty(); case StatusUnknownRole: return computer->state == NvComputer::CS_UNKNOWN; case ServerSupportedRole: return computer->isSupportedServerVersion; 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; } return m_Computers.count(); } QHash ComputerModel::roleNames() const { QHash names; names[NameRole] = "name"; names[OnlineRole] = "online"; names[PairedRole] = "paired"; names[BusyRole] = "busy"; names[WakeableRole] = "wakeable"; names[StatusUnknownRole] = "statusUnknown"; names[ServerSupportedRole] = "serverSupported"; return names; } 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; } 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(); } 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) { Q_ASSERT(computerIndex < m_Computers.count()); DeferredWakeHostTask* wakeTask = new DeferredWakeHostTask(m_Computers[computerIndex]); QThreadPool::globalInstance()->start(wakeTask); } void ComputerModel::renameComputer(int computerIndex, QString name) { Q_ASSERT(computerIndex < m_Computers.count()); m_ComputerManager->renameHost(m_Computers[computerIndex], name); } // TODO: Use QRandomGenerator when we drop Qt 5.9 support QString ComputerModel::generatePinString() { std::uniform_int_distribution dist(0, 9999); std::random_device rd; std::mt19937 engine(rd()); return QString::asprintf("%04u", dist(engine)); } 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 { char blockedPorts[512]; LiStringifyPortFlags(portTestResult, "\n", blockedPorts, sizeof(blockedPorts)); emit connectionTestCompleted(portTestResult, QString(blockedPorts)); } } 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); } 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.isEmpty() ? QVariant() : error); } 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 { // 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(); } } #include "computermodel.moc"