moonlight-qt/app/gui/computermodel.cpp
Cameron Gutman 0735f164a8 Revert back to model reset for updating the computer list
It's possible that multiple changes occur in parallel, so we can't rely on receiving them one at a time
even if the callbacks are synchronized. Handling this substantially complicates the logic such that
it's not really worth doing individual insertions and deletions anymore.

This reverts 94d821a4a9 and replaces it with a solution
that is basically the old code except that it properly handles changes to the structure of the list.
2023-09-08 02:02:21 -05:00

204 lines
5.7 KiB
C++

#include "computermodel.h"
#include <QThreadPool>
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<int, QByteArray> ComputerModel::roleNames() const
{
QHash<int, QByteArray> 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);
}
QString ComputerModel::generatePinString()
{
return m_ComputerManager->generatePinString();
}
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)
{
QVector<NvComputer*> newComputerList = m_ComputerManager->getComputers();
// Reset the model if the structural layout of the list has changed
if (m_Computers != newComputerList) {
beginResetModel();
m_Computers = newComputerList;
endResetModel();
}
else {
// Let the view know that this specific computer changed
int index = m_Computers.indexOf(computer);
emit dataChanged(createIndex(index, 0), createIndex(index, 0));
}
}
#include "computermodel.moc"