2018-09-29 21:06:55 +00:00
|
|
|
#include "startstream.h"
|
|
|
|
#include "backend/computermanager.h"
|
2018-12-06 02:45:28 +00:00
|
|
|
#include "backend/computerseeker.h"
|
2018-09-29 23:18:46 +00:00
|
|
|
#include "streaming/session.h"
|
2018-09-29 21:06:55 +00:00
|
|
|
|
2018-12-06 02:45:28 +00:00
|
|
|
#include <QCoreApplication>
|
2018-09-29 21:06:55 +00:00
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
#define COMPUTER_SEEK_TIMEOUT 10000
|
|
|
|
#define APP_SEEK_TIMEOUT 10000
|
|
|
|
|
|
|
|
namespace CliStartStream
|
|
|
|
{
|
|
|
|
|
|
|
|
enum State {
|
|
|
|
StateInit,
|
|
|
|
StateSeekComputer,
|
|
|
|
StateSeekApp,
|
|
|
|
StateStartSession,
|
|
|
|
StateFailure,
|
|
|
|
};
|
|
|
|
|
|
|
|
class Event
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum Type {
|
2018-12-06 02:45:28 +00:00
|
|
|
AppQuitCompleted,
|
|
|
|
AppQuitRequested,
|
|
|
|
ComputerFound,
|
2018-09-29 21:06:55 +00:00
|
|
|
ComputerUpdated,
|
|
|
|
Executed,
|
|
|
|
Timedout,
|
|
|
|
};
|
|
|
|
|
|
|
|
Event(Type type)
|
|
|
|
: type(type), computerManager(nullptr), computer(nullptr) {}
|
|
|
|
|
|
|
|
Type type;
|
|
|
|
ComputerManager *computerManager;
|
|
|
|
NvComputer *computer;
|
2018-12-06 02:45:28 +00:00
|
|
|
QString errorMessage;
|
2018-09-29 21:06:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class LauncherPrivate
|
|
|
|
{
|
|
|
|
Q_DECLARE_PUBLIC(Launcher)
|
|
|
|
|
|
|
|
public:
|
|
|
|
LauncherPrivate(Launcher *q) : q_ptr(q) {}
|
|
|
|
|
|
|
|
void handleEvent(Event event)
|
|
|
|
{
|
|
|
|
Q_Q(Launcher);
|
|
|
|
Session* session;
|
|
|
|
NvApp app;
|
|
|
|
|
|
|
|
switch (event.type) {
|
2018-12-06 02:45:28 +00:00
|
|
|
// Occurs when CliStartStreamSegue becomes visible and the UI calls launcher's execute()
|
2018-09-29 21:06:55 +00:00
|
|
|
case Event::Executed:
|
|
|
|
if (m_State == StateInit) {
|
|
|
|
m_State = StateSeekComputer;
|
|
|
|
m_ComputerManager = event.computerManager;
|
2018-12-06 02:45:28 +00:00
|
|
|
|
|
|
|
m_ComputerSeeker = new ComputerSeeker(m_ComputerManager, m_ComputerName, q);
|
|
|
|
q->connect(m_ComputerSeeker, &ComputerSeeker::computerFound,
|
|
|
|
q, &Launcher::onComputerFound);
|
|
|
|
q->connect(m_ComputerSeeker, &ComputerSeeker::errorTimeout,
|
|
|
|
q, &Launcher::onTimeout);
|
|
|
|
m_ComputerSeeker->start(COMPUTER_SEEK_TIMEOUT);
|
|
|
|
|
2018-09-29 21:06:55 +00:00
|
|
|
q->connect(m_ComputerManager, &ComputerManager::computerStateChanged,
|
|
|
|
q, &Launcher::onComputerUpdated);
|
2018-12-06 02:45:28 +00:00
|
|
|
q->connect(m_ComputerManager, &ComputerManager::quitAppCompleted,
|
|
|
|
q, &Launcher::onQuitAppCompleted);
|
|
|
|
|
2018-09-29 21:06:55 +00:00
|
|
|
emit q->searchingComputer();
|
|
|
|
}
|
|
|
|
break;
|
2018-12-06 02:45:28 +00:00
|
|
|
// Occurs when searched computer is found
|
|
|
|
case Event::ComputerFound:
|
2018-09-29 21:06:55 +00:00
|
|
|
if (m_State == StateSeekComputer) {
|
2018-12-06 02:45:28 +00:00
|
|
|
if (event.computer->pairState == NvComputer::PS_PAIRED) {
|
|
|
|
m_State = StateSeekApp;
|
|
|
|
m_Computer = event.computer;
|
|
|
|
m_TimeoutTimer->start(APP_SEEK_TIMEOUT);
|
|
|
|
emit q->searchingApp();
|
|
|
|
} else {
|
|
|
|
m_State = StateFailure;
|
|
|
|
QString msg = QString("Computer %1 has not been paired. "
|
|
|
|
"Please open Moonlight to pair before streaming.")
|
|
|
|
.arg(event.computer->name);
|
|
|
|
emit q->failed(msg);
|
2018-09-29 21:06:55 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-06 02:45:28 +00:00
|
|
|
break;
|
|
|
|
// Occurs when a computer is updated
|
|
|
|
case Event::ComputerUpdated:
|
2018-09-29 21:06:55 +00:00
|
|
|
if (m_State == StateSeekApp) {
|
|
|
|
int index = getAppIndex();
|
|
|
|
if (-1 != index) {
|
|
|
|
app = m_Computer->appList[index];
|
2018-12-06 02:45:28 +00:00
|
|
|
m_TimeoutTimer->stop();
|
2018-09-29 21:06:55 +00:00
|
|
|
if (isNotStreaming() || isStreamingApp(app)) {
|
|
|
|
m_State = StateStartSession;
|
|
|
|
session = new Session(m_Computer, app, m_Preferences);
|
|
|
|
emit q->sessionCreated(app.name, session);
|
|
|
|
} else {
|
2018-12-06 02:45:28 +00:00
|
|
|
emit q->appQuitRequired(getCurrentAppName());
|
2018-09-29 21:06:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-12-06 02:45:28 +00:00
|
|
|
// Occurs when there was another app running on computer and user accepted quit
|
|
|
|
// confirmation dialog
|
|
|
|
case Event::AppQuitRequested:
|
|
|
|
if (m_State == StateSeekApp) {
|
|
|
|
m_ComputerManager->quitRunningApp(m_Computer);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// Occurs when the previous app quit has been completed, handles quitting errors if any
|
|
|
|
// happended. ComputerUpdated event's handler handles session start when previous app has
|
|
|
|
// quit.
|
|
|
|
case Event::AppQuitCompleted:
|
|
|
|
if (m_State == StateSeekApp && !event.errorMessage.isEmpty()) {
|
|
|
|
m_State = StateFailure;
|
|
|
|
emit q->failed(QString("Quitting app failed, reason: %1").arg(event.errorMessage));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// Occurs when computer or app search timed out
|
2018-09-29 21:06:55 +00:00
|
|
|
case Event::Timedout:
|
|
|
|
if (m_State == StateSeekComputer) {
|
|
|
|
m_State = StateFailure;
|
|
|
|
emit q->failed(QString("Failed to connect to %1").arg(m_ComputerName));
|
|
|
|
}
|
|
|
|
if (m_State == StateSeekApp) {
|
|
|
|
m_State = StateFailure;
|
|
|
|
emit q->failed(QString("Failed to find application %1").arg(m_AppName));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getAppIndex() const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < m_Computer->appList.length(); i++) {
|
|
|
|
if (m_Computer->appList[i].name.toLower() == m_AppName.toLower()) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isNotStreaming() const
|
|
|
|
{
|
|
|
|
return m_Computer->currentGameId == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isStreamingApp(NvApp app) const
|
|
|
|
{
|
|
|
|
return m_Computer->currentGameId == app.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString getCurrentAppName() const
|
|
|
|
{
|
|
|
|
for (NvApp app : m_Computer->appList) {
|
|
|
|
if (m_Computer->currentGameId == app.id) {
|
|
|
|
return app.name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "<UNKNOWN>";
|
|
|
|
}
|
|
|
|
|
|
|
|
Launcher *q_ptr;
|
|
|
|
QString m_ComputerName;
|
|
|
|
QString m_AppName;
|
|
|
|
StreamingPreferences *m_Preferences;
|
|
|
|
ComputerManager *m_ComputerManager;
|
2018-12-06 02:45:28 +00:00
|
|
|
ComputerSeeker *m_ComputerSeeker;
|
2018-09-29 21:06:55 +00:00
|
|
|
NvComputer *m_Computer;
|
|
|
|
State m_State;
|
|
|
|
QTimer *m_TimeoutTimer;
|
|
|
|
};
|
|
|
|
|
|
|
|
Launcher::Launcher(QString computer, QString app,
|
|
|
|
StreamingPreferences* preferences, QObject *parent)
|
|
|
|
: QObject(parent),
|
|
|
|
m_DPtr(new LauncherPrivate(this))
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
d->m_ComputerName = computer;
|
|
|
|
d->m_AppName = app;
|
|
|
|
d->m_Preferences = preferences;
|
|
|
|
d->m_State = StateInit;
|
|
|
|
d->m_TimeoutTimer = new QTimer(this);
|
|
|
|
d->m_TimeoutTimer->setSingleShot(true);
|
|
|
|
connect(d->m_TimeoutTimer, &QTimer::timeout,
|
|
|
|
this, &Launcher::onTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
Launcher::~Launcher()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Launcher::execute(ComputerManager *manager)
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::Executed);
|
|
|
|
event.computerManager = manager;
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:45:28 +00:00
|
|
|
void Launcher::quitRunningApp()
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::AppQuitRequested);
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Launcher::isExecuted() const
|
|
|
|
{
|
|
|
|
Q_D(const Launcher);
|
|
|
|
return d->m_State != StateInit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Launcher::onComputerFound(NvComputer *computer)
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::ComputerFound);
|
|
|
|
event.computer = computer;
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
2018-09-29 21:06:55 +00:00
|
|
|
void Launcher::onComputerUpdated(NvComputer *computer)
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::ComputerUpdated);
|
|
|
|
event.computer = computer;
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Launcher::onTimeout()
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::Timedout);
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
2018-12-06 02:45:28 +00:00
|
|
|
void Launcher::onQuitAppCompleted(QVariant error)
|
|
|
|
{
|
|
|
|
Q_D(Launcher);
|
|
|
|
Event event(Event::AppQuitCompleted);
|
|
|
|
event.errorMessage = error.toString();
|
|
|
|
d->handleEvent(event);
|
|
|
|
}
|
|
|
|
|
2018-09-29 21:06:55 +00:00
|
|
|
}
|