mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-01 15:26:09 +00:00
Start work on launching a stream from QML and rip out remaining QtWidgets
This commit is contained in:
parent
d5cc07f107
commit
60ad95bb7b
@ -1,9 +1,6 @@
|
||||
QT += core quick network
|
||||
CONFIG += c++11
|
||||
|
||||
# TODO: Rid ourselves of QtWidgets
|
||||
QT += widgets
|
||||
|
||||
TARGET = moonlight-qt
|
||||
TEMPLATE = app
|
||||
|
||||
|
@ -327,9 +327,7 @@ private:
|
||||
break;
|
||||
}
|
||||
} catch (const GfeHttpResponseException& e) {
|
||||
emit pairingCompleted(m_Computer,
|
||||
QString::fromLocal8Bit(e.getStatusMessage()) +
|
||||
" (Error " + QString::number(e.getStatusCode()));
|
||||
emit pairingCompleted(m_Computer, e.toQString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,11 @@ public:
|
||||
return m_StatusCode;
|
||||
}
|
||||
|
||||
QString toQString() const
|
||||
{
|
||||
return m_StatusMessage + " (Error " + QString::number(m_StatusCode) + ")";
|
||||
}
|
||||
|
||||
private:
|
||||
int m_StatusCode;
|
||||
QString m_StatusMessage;
|
||||
|
@ -7,6 +7,7 @@ import ComputerManager 1.0
|
||||
|
||||
GridView {
|
||||
property int computerIndex
|
||||
property AppModel appModel : createModel()
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 5
|
||||
@ -33,7 +34,7 @@ GridView {
|
||||
return model
|
||||
}
|
||||
|
||||
model: createModel()
|
||||
model: appModel
|
||||
|
||||
delegate: Item {
|
||||
width: 200; height: 300;
|
||||
@ -64,7 +65,8 @@ GridView {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
parent.GridView.view.currentIndex = index
|
||||
var session = appModel.createSessionForGame(index);
|
||||
session.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,14 @@ void AppModel::initialize(ComputerManager* computerManager, int computerIndex)
|
||||
m_CurrentGameId = m_Computer->currentGameId;
|
||||
}
|
||||
|
||||
Session* AppModel::createSessionForApp(int appIndex)
|
||||
{
|
||||
Q_ASSERT(appIndex < m_Apps.count());
|
||||
NvApp app = m_Apps.at(appIndex);
|
||||
|
||||
return new Session(m_Computer, app);
|
||||
}
|
||||
|
||||
int AppModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
// For list models only the root node (an invalid parent) should return the list's size. For all
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "backend/boxartmanager.h"
|
||||
#include "backend/computermanager.h"
|
||||
#include "streaming/session.hpp"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
@ -22,6 +23,8 @@ public:
|
||||
// Must be called before any QAbstractListModel functions
|
||||
Q_INVOKABLE void initialize(ComputerManager* computerManager, int computerIndex);
|
||||
|
||||
Q_INVOKABLE Session* createSessionForApp(int appIndex);
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
|
@ -78,6 +78,26 @@ QHash<int, QByteArray> ComputerModel::roleNames() const
|
||||
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());
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "backend/computermanager.h"
|
||||
#include "streaming/session.hpp"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
@ -33,6 +34,8 @@ public:
|
||||
|
||||
Q_INVOKABLE bool wakeComputer(int computerIndex);
|
||||
|
||||
Q_INVOKABLE Session* createSessionForCurrentGame(int computerIndex);
|
||||
|
||||
signals:
|
||||
void pairingCompleted(QVariant error);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "gui/computermodel.h"
|
||||
#include "gui/appmodel.h"
|
||||
#include "streaming/session.hpp"
|
||||
|
||||
// Don't let SDL hook our main function, since Qt is already
|
||||
// doing the same thing
|
||||
@ -30,6 +31,7 @@ int main(int argc, char *argv[])
|
||||
// Register our C++ types for QML
|
||||
qmlRegisterType<ComputerModel>("ComputerModel", 1, 0, "ComputerModel");
|
||||
qmlRegisterType<AppModel>("AppModel", 1, 0, "AppModel");
|
||||
qmlRegisterUncreatableType<Session>("Session", 1, 0, "Session", "Session cannot be created from QML");
|
||||
qmlRegisterSingletonType<ComputerManager>("ComputerManager", 1, 0,
|
||||
"ComputerManager",
|
||||
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include <QRandomGenerator>
|
||||
#include <QMessageBox>
|
||||
#include <QtEndian>
|
||||
#include <QCoreApplication>
|
||||
|
||||
@ -35,28 +34,19 @@ Session* Session::s_ActiveSession;
|
||||
|
||||
void Session::clStageStarting(int stage)
|
||||
{
|
||||
char buffer[512];
|
||||
sprintf(buffer, "Starting %s...", LiGetStageName(stage));
|
||||
|
||||
// We know this is called on the same thread as LiStartConnection()
|
||||
// which happens to be the main thread, so it's cool to interact
|
||||
// with the GUI in these callbacks.
|
||||
s_ActiveSession->m_ProgressBox.setText(buffer);
|
||||
emit s_ActiveSession->stageStarting(QString::fromLocal8Bit(LiGetStageName(stage)));
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
|
||||
void Session::clStageFailed(int stage, long errorCode)
|
||||
{
|
||||
s_ActiveSession->m_ProgressBox.close();
|
||||
|
||||
// TODO: Open error dialog
|
||||
char buffer[512];
|
||||
sprintf(buffer, "Failed %s with error: %ld",
|
||||
LiGetStageName(stage), errorCode);
|
||||
|
||||
// We know this is called on the same thread as LiStartConnection()
|
||||
// which happens to be the main thread, so it's cool to interact
|
||||
// with the GUI in these callbacks.
|
||||
emit s_ActiveSession->stageFailed(QString::fromLocal8Bit(LiGetStageName(stage)), errorCode);
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
|
||||
@ -87,8 +77,7 @@ void Session::clLogMessage(const char* format, ...)
|
||||
|
||||
Session::Session(NvComputer* computer, NvApp& app)
|
||||
: m_Computer(computer),
|
||||
m_App(app),
|
||||
m_ProgressBox(nullptr)
|
||||
m_App(app)
|
||||
{
|
||||
StreamingPreferences prefs;
|
||||
|
||||
@ -138,13 +127,7 @@ Session::Session(NvComputer* computer, NvApp& app)
|
||||
}
|
||||
}
|
||||
|
||||
QString Session::checkForFatalValidationError()
|
||||
{
|
||||
// Nothing here yet
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QStringList Session::checkForAdvisoryValidationError()
|
||||
bool Session::validateLaunch()
|
||||
{
|
||||
NvHTTP http(m_Computer->activeAddress);
|
||||
QStringList warningList;
|
||||
@ -155,12 +138,12 @@ QStringList Session::checkForAdvisoryValidationError()
|
||||
|
||||
// Check that the app supports HDR
|
||||
if (!m_App.hdrSupported) {
|
||||
warningList.append(m_App.name + " doesn't support HDR10.");
|
||||
emit displayLaunchWarning(m_App.name + " doesn't support HDR10.");
|
||||
}
|
||||
// Check that the server GPU supports HDR
|
||||
else if (!(m_Computer->serverCodecModeSupport & 0x200)) {
|
||||
warningList.append("Your host PC GPU doesn't support HDR streaming. "
|
||||
"A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming.");
|
||||
emit displayLaunchWarning("Your host PC GPU doesn't support HDR streaming. "
|
||||
"A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming.");
|
||||
}
|
||||
else {
|
||||
// TODO: Also validate client decoder and display capabilites
|
||||
@ -183,11 +166,21 @@ QStringList Session::checkForAdvisoryValidationError()
|
||||
// TODO: Validate HEVC support based on decoder caps
|
||||
}
|
||||
|
||||
return warningList;
|
||||
// Always allow the launch to proceed for now
|
||||
return true;
|
||||
}
|
||||
|
||||
void Session::exec()
|
||||
{
|
||||
// Check for validation errors/warnings and emit
|
||||
// signals for them, if appropriate
|
||||
if (!validateLaunch()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Manually pump the UI thread for the view
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
// We're now active
|
||||
s_ActiveSession = this;
|
||||
|
||||
@ -195,21 +188,13 @@ void Session::exec()
|
||||
StreamingPreferences prefs;
|
||||
SdlInputHandler inputHandler(prefs.multiController);
|
||||
|
||||
m_ProgressBox.setStandardButtons(QMessageBox::Cancel);
|
||||
m_ProgressBox.setText("Launching "+m_App.name+"...");
|
||||
m_ProgressBox.open();
|
||||
|
||||
// Ensure the progress box is immediately visible
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
NvHTTP http(m_Computer->activeAddress);
|
||||
|
||||
// The UI should have ensured the old game was already quit
|
||||
// if we decide to stream a different game.
|
||||
Q_ASSERT(m_Computer->currentGameId == 0 ||
|
||||
m_Computer->currentGameId == m_App.id);
|
||||
|
||||
try {
|
||||
NvHTTP http(m_Computer->activeAddress);
|
||||
if (m_Computer->currentGameId != 0) {
|
||||
http.resumeApp(&m_StreamConfig);
|
||||
}
|
||||
@ -220,12 +205,10 @@ void Session::exec()
|
||||
inputHandler.getAttachedGamepadMask());
|
||||
}
|
||||
} catch (const GfeHttpResponseException& e) {
|
||||
m_ProgressBox.close();
|
||||
// TODO: display error dialog
|
||||
emit displayLaunchError(e.toQString());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QByteArray hostnameStr = m_Computer->activeAddress.toLatin1();
|
||||
QByteArray siAppVersion = m_Computer->appVersion.toLatin1();
|
||||
|
||||
@ -251,9 +234,8 @@ void Session::exec()
|
||||
return;
|
||||
}
|
||||
|
||||
// Before we get into our SDL loop, close the message box used to
|
||||
// display progress
|
||||
m_ProgressBox.close();
|
||||
// Pump the message loop to update the UI
|
||||
emit connectionStarted();
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
SDL_Window* wnd = SDL_CreateWindow("SDL Test Window", 0, 0, 1280, 720, SDL_WINDOW_INPUT_GRABBED);
|
||||
|
@ -5,20 +5,29 @@
|
||||
#include "backend/computermanager.h"
|
||||
#include "input.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
class Session
|
||||
class Session : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Session(NvComputer* computer, NvApp& app);
|
||||
|
||||
QString checkForFatalValidationError();
|
||||
Q_INVOKABLE void exec();
|
||||
|
||||
QStringList checkForAdvisoryValidationError();
|
||||
signals:
|
||||
void stageStarting(QString stage);
|
||||
|
||||
void exec();
|
||||
void stageFailed(QString stage, long errorCode);
|
||||
|
||||
void connectionStarted();
|
||||
|
||||
void displayLaunchError(QString text);
|
||||
|
||||
void displayLaunchWarning(QString text);
|
||||
|
||||
private:
|
||||
bool validateLaunch();
|
||||
|
||||
static
|
||||
void clStageStarting(int stage);
|
||||
|
||||
@ -54,7 +63,6 @@ private:
|
||||
STREAM_CONFIGURATION m_StreamConfig;
|
||||
NvComputer* m_Computer;
|
||||
NvApp m_App;
|
||||
QMessageBox m_ProgressBox;
|
||||
|
||||
static SDL_AudioDeviceID s_AudioDevice;
|
||||
static OpusMSDecoder* s_OpusDecoder;
|
||||
|
Loading…
x
Reference in New Issue
Block a user