diff --git a/app/app.pro b/app/app.pro index ae986f5a..e744049e 100644 --- a/app/app.pro +++ b/app/app.pro @@ -58,7 +58,9 @@ SOURCES += \ gui/popupmanager.cpp \ http/identitymanager.cpp \ http/nvhttp.cpp \ - http/nvpairingmanager.cpp + http/nvpairingmanager.cpp \ + streaming/video.c \ + streaming/connection.cpp HEADERS += \ utils.h \ @@ -66,13 +68,14 @@ HEADERS += \ gui/popupmanager.h \ http/identitymanager.h \ http/nvhttp.h \ - http/nvpairingmanager.h + http/nvpairingmanager.h \ + streaming/streaming.h FORMS += \ gui/mainwindow.ui RESOURCES += \ - resources.qrc + gui/resources.qrc win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../moonlight-common-c/release/ -lmoonlight-common-c else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../moonlight-common-c/debug/ -lmoonlight-common-c diff --git a/app/gui/mainwindow.cpp b/app/gui/mainwindow.cpp index 66484f87..095eb3bc 100644 --- a/app/gui/mainwindow.cpp +++ b/app/gui/mainwindow.cpp @@ -20,23 +20,6 @@ MainWindow::MainWindow(QWidget *parent) : // connect(myButton, &QAbstractButton::clicked, this, &MainWindow::on_actionExit_triggered); } -void MainWindow::closeEvent(QCloseEvent *event) -{ - const QMessageBox::StandardButton ret - = QMessageBox::warning(this, tr("Application"), - tr("something-something-close?"), - QMessageBox::Yes | QMessageBox::No); - switch (ret) { - case QMessageBox::Yes: - event->accept(); - break; - case QMessageBox::No: - default: - event->ignore(); - break; - } -} - MainWindow::~MainWindow() { delete ui; diff --git a/app/gui/mainwindow.h b/app/gui/mainwindow.h index 58affa92..bbec0e04 100644 --- a/app/gui/mainwindow.h +++ b/app/gui/mainwindow.h @@ -16,9 +16,6 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); -protected: - void closeEvent(QCloseEvent *event) override; - private slots: void on_actionExit_triggered(); void on_newHostBtn_clicked(); diff --git a/app/resources.qrc b/app/gui/resources.qrc similarity index 100% rename from app/resources.qrc rename to app/gui/resources.qrc diff --git a/app/http/nvpairingmanager.cpp b/app/http/nvpairingmanager.cpp index 4b6bd534..fef66b4c 100644 --- a/app/http/nvpairingmanager.cpp +++ b/app/http/nvpairingmanager.cpp @@ -153,7 +153,7 @@ NvPairingManager::signMessage(QByteArray message) size_t signatureLength = 0; EVP_DigestSignFinal(ctx, NULL, &signatureLength); - QByteArray signature(signatureLength, 0); + QByteArray signature((int)signatureLength, 0); EVP_DigestSignFinal(ctx, reinterpret_cast(signature.data()), &signatureLength); EVP_MD_CTX_destroy(ctx); diff --git a/app/main.cpp b/app/main.cpp index 4934a392..13d648a8 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,6 +1,11 @@ #include "gui/mainwindow.h" #include +// Don't let SDL hook our main function, since Qt is already +// doing the same thing +#define SDL_MAIN_HANDLED +#include + int main(int argc, char *argv[]) { // MacOS directive to prevent the menu bar from being merged into the native bar @@ -15,6 +20,20 @@ int main(int argc, char *argv[]) MainWindow w; w.show(); - return a.exec(); + // Ensure that SDL is always initialized since we may need to use it + // for non-streaming purposes (like checking on audio devices) + SDL_SetMainReady(); + if (SDL_Init(SDL_INIT_VIDEO | + SDL_INIT_AUDIO | + SDL_INIT_GAMECONTROLLER) != 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_Init() failed: %s", + SDL_GetError()); + } + int err = a.exec(); + + SDL_Quit(); + + return err; } diff --git a/app/streaming/audio.c b/app/streaming/audio.c index 1c093c3e..133f33fc 100644 --- a/app/streaming/audio.c +++ b/app/streaming/audio.c @@ -128,3 +128,12 @@ void SdlAudioDecodeAndPlaySample(char* sampleData, int sampleLength) } } } + +AUDIO_RENDERER_CALLBACKS k_AudioCallbacks = { + SdlAudioInit, + SdlAudioStart, + SdlAudioStop, + SdlAudioCleanup, + SdlAudioDecodeAndPlaySample, + CAPABILITY_DIRECT_SUBMIT +}; diff --git a/app/streaming/connection.cpp b/app/streaming/connection.cpp new file mode 100644 index 00000000..f1bfd02c --- /dev/null +++ b/app/streaming/connection.cpp @@ -0,0 +1,138 @@ +#include "streaming.h" +#include +#include + +#include + +bool g_StreamActive; +QMessageBox* g_ProgressBox; + +static +void +ClStageStarting(int stage) +{ + char buffer[512]; + sprintf(buffer, "Starting %s...", LiGetStageName(stage)); + g_ProgressBox->setText(buffer); +} + +static +void +ClStageFailed(int stage, long errorCode) +{ + char buffer[512]; + sprintf(buffer, "Failed %s with error: %ld", + LiGetStageName(stage), errorCode); + g_ProgressBox->setText(buffer); +} + +static +void +ClConnectionTerminated(long errorCode) +{ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Connection terminated: %ld", + errorCode); + + // Push a quit event to the main loop + SDL_Event event; + event.type = SDL_QUIT; + event.quit.timestamp = SDL_GetTicks(); + SDL_PushEvent(&event); +} + +static +void +ClLogMessage(const char* format, ...) +{ + va_list ap; + + va_start(ap, format); + SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_PRIORITY_INFO, + format, + ap); + va_end(ap); +} + +int +StartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION streamConfig, QMessageBox* progressBox) +{ + CONNECTION_LISTENER_CALLBACKS listener; + + // Hold onto this for our callbacks + g_ProgressBox = progressBox; + + if (streamConfig->audioConfiguration < 0) { + // This signals to us that we should auto-detect + streamConfig->audioConfiguration = SdlDetermineAudioConfiguration(); + } + + LiInitializeConnectionCallbacks(&listener); + listener.stageStarting = ClStageStarting; + listener.stageFailed = ClStageFailed; + listener.connectionTerminated = ClConnectionTerminated; + listener.logMessage = ClLogMessage; + + int err = LiStartConnection(serverInfo, streamConfig, &listener, + &k_VideoCallbacks, &k_AudioCallbacks, + NULL, 0, NULL, 0); + if (err != 0) { + SDL_Quit(); + return err; + } + + // Before we get into our loop, close the message box used to + // display progress + Q_ASSERT(g_ProgressBox == progressBox); + progressBox->close(); + delete progressBox; + g_ProgressBox = nullptr; + + // Hijack this thread to be the SDL main thread. We have to do this + // because we want to suspend all Qt processing until the stream is over. + SDL_Event event; + while (SDL_WaitEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Quit event received"); + goto Exit; + case SDL_KEYUP: + case SDL_KEYDOWN: + SdlHandleKeyEvent(&event.key); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + SdlHandleMouseButtonEvent(&event.button); + break; + case SDL_MOUSEMOTION: + SdlHandleMouseMotionEvent(&event.motion); + break; + case SDL_MOUSEWHEEL: + SdlHandleMouseWheelEvent(&event.wheel); + break; + case SDL_CONTROLLERAXISMOTION: + SdlHandleControllerAxisEvent(&event.caxis); + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + SdlHandleControllerButtonEvent(&event.cbutton); + break; + case SDL_CONTROLLERDEVICEADDED: + case SDL_CONTROLLERDEVICEREMOVED: + SdlHandleControllerDeviceEvent(&event.cdevice); + break; + } + } + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_WaitEvent() failed: %s", + SDL_GetError()); + +Exit: + g_StreamActive = false; + LiStopConnection(); + + return 0; +} diff --git a/app/streaming/input.c b/app/streaming/input.c index 95b927d5..9af88643 100644 --- a/app/streaming/input.c +++ b/app/streaming/input.c @@ -24,11 +24,11 @@ typedef struct _GAMEPAD_STATE { } GAMEPAD_STATE, *PGAMEPAD_STATE; #define MAX_GAMEPADS 4 -GAMEPAD_STATE g_GamepadState[MAX_GAMEPADS]; -unsigned short g_GamepadMask; -bool g_MultiController; +static GAMEPAD_STATE g_GamepadState[MAX_GAMEPADS]; +static unsigned short g_GamepadMask; +static bool g_MultiController; -const short k_ButtonMap[] = { +static const short k_ButtonMap[] = { A_FLAG, B_FLAG, X_FLAG, Y_FLAG, BACK_FLAG, SPECIAL_FLAG, PLAY_FLAG, LS_CLK_FLAG, RS_CLK_FLAG, @@ -320,7 +320,7 @@ static PGAMEPAD_STATE FindStateForGamepad(SDL_JoystickID id) return NULL; } -void SendGamepadState(PGAMEPAD_STATE state) +static void SendGamepadState(PGAMEPAD_STATE state) { SDL_assert(g_GamepadMask == 0x1 || g_MultiController); LiSendMultiControllerEvent(state->index, diff --git a/app/streaming/streaming.h b/app/streaming/streaming.h new file mode 100644 index 00000000..74309de8 --- /dev/null +++ b/app/streaming/streaming.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern AUDIO_RENDERER_CALLBACKS k_AudioCallbacks; +extern DECODER_RENDERER_CALLBACKS k_VideoCallbacks; + +int SdlDetermineAudioConfiguration(void); + +void SdlInitializeGamepad(bool multiController); +void SdlHandleControllerDeviceEvent(SDL_ControllerDeviceEvent* event); +void SdlHandleControllerButtonEvent(SDL_ControllerButtonEvent* event); +void SdlHandleControllerAxisEvent(SDL_ControllerAxisEvent* event); +void SdlHandleMouseWheelEvent(SDL_MouseWheelEvent* event); +void SdlHandleMouseMotionEvent(SDL_MouseMotionEvent* event); +void SdlHandleMouseButtonEvent(SDL_MouseButtonEvent* event); +void SdlHandleKeyEvent(SDL_KeyboardEvent* event); + +#ifdef __cplusplus +} +#endif + +// This function uses C++ linkage +int StartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION streamConfig, QMessageBox* progressBox); diff --git a/app/streaming/video.c b/app/streaming/video.c new file mode 100644 index 00000000..7d8e976e --- /dev/null +++ b/app/streaming/video.c @@ -0,0 +1,3 @@ +#include + +DECODER_RENDERER_CALLBACKS k_VideoCallbacks;