mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-10-22 08:07:22 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60fadcbf36 |
No files matched your search
@@ -37,13 +37,12 @@ List the games you've tried that exhibit the issue. To see if the issue is game-
|
|||||||
- OS: [e.g. Windows 10 1809]
|
- OS: [e.g. Windows 10 1809]
|
||||||
- Moonlight Version: [e.g. v0.9.0]
|
- Moonlight Version: [e.g. v0.9.0]
|
||||||
- GPU: [e.g. Intel HD Graphics 520]
|
- GPU: [e.g. Intel HD Graphics 520]
|
||||||
- Linux package type (if applicable): [e.g. Flatpak]
|
|
||||||
|
|
||||||
**Server PC details (please complete the following information)**
|
**Server PC details (please complete the following information)**
|
||||||
- OS: [e.g. Windows 10 1809]
|
- OS: [e.g. Windows 10 1809]
|
||||||
- Sunshine or GeForce Experience version: [e.g. Sunshine v0.21.0]
|
- GeForce Experience version: [e.g. 3.16.0.140]
|
||||||
- GPU: [e.g. AMD Radeon RX 7900 XT]
|
- Nvidia GPU driver: [e.g. 417.35]
|
||||||
- GPU driver: [e.g. 24.1.1]
|
- Antivirus and firewall software: [e.g. Windows Defender and Windows Firewall]
|
||||||
|
|
||||||
**Moonlight Logs (please attach)**
|
**Moonlight Logs (please attach)**
|
||||||
- On Windows, `Moonlight-###.log` files can be found in `%TEMP%`. Simply type that into the File Explorer path field to navigate there.
|
- On Windows, `Moonlight-###.log` files can be found in `%TEMP%`. Simply type that into the File Explorer path field to navigate there.
|
||||||
|
|||||||
@@ -158,6 +158,17 @@ private:
|
|||||||
L"EZFRD32.dll",
|
L"EZFRD32.dll",
|
||||||
L"EZFRD64.dll",
|
L"EZFRD64.dll",
|
||||||
|
|
||||||
|
// These are the newer dList DLLs for Optimus hybrid graphics DDI.
|
||||||
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/hybrid-system-ddi
|
||||||
|
//
|
||||||
|
// We forcefully block them from loading because Optimus has a bug that
|
||||||
|
// deadlocks DXVA2 when we present with D3DPRESENT_DONOTWAIT. This will prevent
|
||||||
|
// Optimus from ever using the dGPU even if the user has requested it.
|
||||||
|
// https://github.com/moonlight-stream/moonlight-qt/issues/240
|
||||||
|
// https://github.com/moonlight-stream/moonlight-qt/issues/235
|
||||||
|
L"nvdlist.dll",
|
||||||
|
L"nvdlistx.dll",
|
||||||
|
|
||||||
// These are the older dList/AppInit DLLs for Optimus hybrid graphics DDI.
|
// These are the older dList/AppInit DLLs for Optimus hybrid graphics DDI.
|
||||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/hybrid-system-ddi
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/hybrid-system-ddi
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ You can follow development on our [Discord server](https://moonlight-stream.org/
|
|||||||
- [Snap (for Ubuntu-based Linux distros)](https://snapcraft.io/moonlight)
|
- [Snap (for Ubuntu-based Linux distros)](https://snapcraft.io/moonlight)
|
||||||
- [Flatpak (for other Linux distros)](https://flathub.org/apps/details/com.moonlight_stream.Moonlight)
|
- [Flatpak (for other Linux distros)](https://flathub.org/apps/details/com.moonlight_stream.Moonlight)
|
||||||
- [AppImage](https://github.com/moonlight-stream/moonlight-qt/releases)
|
- [AppImage](https://github.com/moonlight-stream/moonlight-qt/releases)
|
||||||
- [Raspberry Pi 4 and 5](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Raspberry-Pi-4)
|
- [Raspberry Pi 4](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Raspberry-Pi-4)
|
||||||
- [Generic ARM 32-bit and 64-bit Debian packages](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-ARM%E2%80%90based-Single-Board-Computers) (not for Raspberry Pi)
|
- [Generic ARM SBC packages for Debian](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-ARM%E2%80%90based-Single-Board-Computers) (not for Raspberry Pi)
|
||||||
- [Experimental RISC-V Debian packages](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-RISC%E2%80%90V-Single-Board-Computers)
|
- [Experimental RISC-V SBC packages for Debian](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-RISC%E2%80%90V-Single-Board-Computers)
|
||||||
- [NVIDIA Jetson and Nintendo Switch (Ubuntu L4T)](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Linux4Tegra-(L4T)-Ubuntu)
|
- [NVIDIA Jetson and Nintendo Switch (Ubuntu L4T)](https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Linux4Tegra-(L4T)-Ubuntu)
|
||||||
|
|
||||||
#### Special Thanks
|
#### Special Thanks
|
||||||
@@ -50,13 +50,12 @@ Hosting for Moonlight's Debian and L4T package repositories is graciously provid
|
|||||||
* [create-dmg](https://github.com/sindresorhus/create-dmg) (only if building DMGs for use on non-development Macs)
|
* [create-dmg](https://github.com/sindresorhus/create-dmg) (only if building DMGs for use on non-development Macs)
|
||||||
|
|
||||||
### Linux/Unix Build Requirements
|
### Linux/Unix Build Requirements
|
||||||
* Qt 6 is recommended, but Qt 5.9 or later is also supported (replace `qmake6` with `qmake` when using Qt 5).
|
* Qt 5.9 SDK or later. Qt 6 is also supported.
|
||||||
* GCC or Clang
|
* GCC or Clang
|
||||||
* FFmpeg 4.0 or later
|
|
||||||
* Install the required packages:
|
* Install the required packages:
|
||||||
* Debian/Ubuntu: `libegl1-mesa-dev libgl1-mesa-dev libopus-dev libsdl2-dev libsdl2-ttf-dev libssl-dev libavcodec-dev libavformat-dev libva-dev libvdpau-dev libxkbcommon-dev wayland-protocols libdrm-dev qtbase6-dev qt6-declarative-dev libqt6svg6-dev qml6-module-qtquick-controls qml6-module-qtquick-templates qml6-module-qtquick-layouts qml6-module-qtqml-workerscript qml6-module-qtquick-window qml6-module-qtquick`
|
* Debian/Ubuntu: `libegl1-mesa-dev libgl1-mesa-dev libopus-dev libqt5svg5-dev libsdl2-dev libsdl2-ttf-dev libssl-dev libavcodec-dev libva-dev libvdpau-dev libxkbcommon-dev qtwayland5 qt5-qmake qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev wayland-protocols qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-window2 qml-module-qtquick2`
|
||||||
* RedHat/Fedora (RPM Fusion repo required): `openssl-devel SDL2-devel SDL2_ttf-devel ffmpeg-devel libva-devel libvdpau-devel opus-devel pulseaudio-libs-devel alsa-lib-devel libdrm-devel qt6-qtsvg-devel qt6-qtdeclarative-devel`
|
* RedHat/Fedora: `openssl-devel SDL2-devel SDL2_ttf-devel ffmpeg-devel qt5-qtsvg-devel qt5-qtquickcontrols2-devel libva-devel libvdpau-devel opus-devel pulseaudio-libs-devel alsa-lib-devel`
|
||||||
* Building the Vulkan HDR renderer requires a `libplacebo-dev`/`libplacebo-devel` version of at least v338.0 and FFmpeg 6.1 or later.
|
* FFmpeg 4.0 or later is required to build. If your distro doesn't package FFmpeg 4.0 or later, you can build and install it from source on https://ffmpeg.org/
|
||||||
|
|
||||||
### Steam Link Build Requirements
|
### Steam Link Build Requirements
|
||||||
* [Steam Link SDK](https://github.com/ValveSoftware/steamlink-sdk) cloned on your build system
|
* [Steam Link SDK](https://github.com/ValveSoftware/steamlink-sdk) cloned on your build system
|
||||||
@@ -73,10 +72,10 @@ Hosting for Moonlight's Debian and L4T package repositories is graciously provid
|
|||||||
* For Windows builds, use `scripts\build-arch.bat` and `scripts\generate-bundle.bat`. Execute these scripts from the root of the repository within a Qt command prompt. Ensure 7-Zip binary directory is on your `%PATH%`.
|
* For Windows builds, use `scripts\build-arch.bat` and `scripts\generate-bundle.bat`. Execute these scripts from the root of the repository within a Qt command prompt. Ensure 7-Zip binary directory is on your `%PATH%`.
|
||||||
* For macOS builds, use `scripts/generate-dmg.sh`. Execute this script from the root of the repository and ensure Qt's `bin` folder is in your `$PATH`.
|
* For macOS builds, use `scripts/generate-dmg.sh`. Execute this script from the root of the repository and ensure Qt's `bin` folder is in your `$PATH`.
|
||||||
* For Steam Link builds, run `scripts/build-steamlink-app.sh` from the root of the repository.
|
* For Steam Link builds, run `scripts/build-steamlink-app.sh` from the root of the repository.
|
||||||
* To build from the command line for development use on macOS or Linux, run `qmake6 moonlight-qt.pro` then `make debug` or `make release`
|
* To build from the command line for development use on macOS or Linux, run `qmake moonlight-qt.pro` then `make debug` or `make release`
|
||||||
* To create an embedded build for a single-purpose device, use `qmake6 "CONFIG+=embedded" moonlight-qt.pro` and build normally.
|
* To create an embedded build for a single-purpose device, use `qmake "CONFIG+=embedded" moonlight-qt.pro` and build normally.
|
||||||
* This build will lack windowed mode, Discord/Help links, and other features that don't make sense on an embedded device.
|
* This build will lack windowed mode, Discord/Help links, and other features that don't make sense on an embedded device.
|
||||||
* For platforms with poor GPU performance, add `"CONFIG+=gpuslow"` to prefer direct KMSDRM rendering over GL/Vulkan renderers. Direct KMSDRM rendering can use dedicated YUV/RGB conversion and scaling hardware rather than slower GPU shaders for these operations.
|
* For platforms with poor GL performance, add `"CONFIG+=glslow"` to prefer direct KMSDRM rendering over EGL/GLES renderers. Direct KMSDRM rendering can use dedicated YUV/RGB conversion and scaling hardware rather than slower GPU shaders for these operations.
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
1. Fork us
|
1. Fork us
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>GCSupportsControllerUserInteraction</key>
|
<key>GCSupportsControllerUserInteraction</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.games</string>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
|||||||
+1
-1
Submodule app/SDL_GameControllerDB updated: e5a5fa2ac6...dbcf31a670.
+13
-51
@@ -52,7 +52,7 @@ win32 {
|
|||||||
# Work around a conflict with math.h inclusion between SDL and Qt 6
|
# Work around a conflict with math.h inclusion between SDL and Qt 6
|
||||||
DEFINES += _USE_MATH_DEFINES
|
DEFINES += _USE_MATH_DEFINES
|
||||||
}
|
}
|
||||||
macx:!disable-prebuilts {
|
macx {
|
||||||
INCLUDEPATH += $$PWD/../libs/mac/include
|
INCLUDEPATH += $$PWD/../libs/mac/include
|
||||||
INCLUDEPATH += $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers
|
INCLUDEPATH += $$PWD/../libs/mac/Frameworks/SDL2.framework/Versions/A/Headers
|
||||||
INCLUDEPATH += $$PWD/../libs/mac/Frameworks/SDL2_ttf.framework/Versions/A/Headers
|
INCLUDEPATH += $$PWD/../libs/mac/Frameworks/SDL2_ttf.framework/Versions/A/Headers
|
||||||
@@ -64,7 +64,7 @@ macx:!disable-prebuilts {
|
|||||||
QMAKE_OBJECTIVE_CFLAGS += -F$$PWD/../libs/mac/Frameworks
|
QMAKE_OBJECTIVE_CFLAGS += -F$$PWD/../libs/mac/Frameworks
|
||||||
}
|
}
|
||||||
|
|
||||||
unix:if(!macx|disable-prebuilts) {
|
unix:!macx {
|
||||||
CONFIG += link_pkgconfig
|
CONFIG += link_pkgconfig
|
||||||
PKGCONFIG += openssl sdl2 SDL2_ttf opus
|
PKGCONFIG += openssl sdl2 SDL2_ttf opus
|
||||||
|
|
||||||
@@ -120,13 +120,6 @@ unix:if(!macx|disable-prebuilts) {
|
|||||||
CONFIG += cuda
|
CONFIG += cuda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
!disable-libplacebo {
|
|
||||||
packagesExist(libplacebo) {
|
|
||||||
PKGCONFIG += libplacebo
|
|
||||||
CONFIG += libplacebo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
!disable-wayland {
|
!disable-wayland {
|
||||||
@@ -152,17 +145,13 @@ win32:!winrt {
|
|||||||
CONFIG += soundio discord-rpc
|
CONFIG += soundio discord-rpc
|
||||||
}
|
}
|
||||||
macx {
|
macx {
|
||||||
!disable-prebuilts {
|
LIBS += -lssl -lcrypto -lavcodec.60 -lavutil.58 -lopus -framework SDL2 -framework SDL2_ttf
|
||||||
LIBS += -lssl -lcrypto -lavcodec.61 -lavutil.59 -lopus -framework SDL2 -framework SDL2_ttf
|
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal
|
||||||
CONFIG += discord-rpc
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal -framework QuartzCore
|
|
||||||
|
|
||||||
# For libsoundio
|
# For libsoundio
|
||||||
LIBS += -framework CoreAudio -framework AudioUnit
|
LIBS += -framework CoreAudio -framework AudioUnit
|
||||||
|
|
||||||
CONFIG += ffmpeg soundio
|
CONFIG += ffmpeg soundio discord-rpc
|
||||||
}
|
}
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@@ -302,7 +291,7 @@ mmal {
|
|||||||
# significantly better performance than EGL on the Pi. Setting
|
# significantly better performance than EGL on the Pi. Setting
|
||||||
# this option allows EGL usage even if built with MMAL support.
|
# this option allows EGL usage even if built with MMAL support.
|
||||||
#
|
#
|
||||||
# It is highly recommended to also build with 'gpuslow' to avoid
|
# It is highly recommended to also build with 'glslow' to avoid
|
||||||
# EGL being preferred if direct DRM rendering is available.
|
# EGL being preferred if direct DRM rendering is available.
|
||||||
allow-egl-with-mmal {
|
allow-egl-with-mmal {
|
||||||
message(Allowing EGL usage with MMAL enabled)
|
message(Allowing EGL usage with MMAL enabled)
|
||||||
@@ -333,16 +322,6 @@ cuda {
|
|||||||
# ffnvcodec uses libdl in cuda_load_functions()/cuda_free_functions()
|
# ffnvcodec uses libdl in cuda_load_functions()/cuda_free_functions()
|
||||||
LIBS += -ldl
|
LIBS += -ldl
|
||||||
}
|
}
|
||||||
libplacebo {
|
|
||||||
message(Vulkan support enabled via libplacebo)
|
|
||||||
|
|
||||||
DEFINES += HAVE_LIBPLACEBO_VULKAN
|
|
||||||
SOURCES += \
|
|
||||||
streaming/video/ffmpeg-renderers/plvk.cpp \
|
|
||||||
streaming/video/ffmpeg-renderers/plvk_c.c
|
|
||||||
HEADERS += \
|
|
||||||
streaming/video/ffmpeg-renderers/plvk.h
|
|
||||||
}
|
|
||||||
config_EGL {
|
config_EGL {
|
||||||
message(EGL renderer selected)
|
message(EGL renderer selected)
|
||||||
|
|
||||||
@@ -389,8 +368,7 @@ macx {
|
|||||||
message(VideoToolbox renderer selected)
|
message(VideoToolbox renderer selected)
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
streaming/video/ffmpeg-renderers/vt_avsamplelayer.mm \
|
streaming/video/ffmpeg-renderers/vt.mm
|
||||||
streaming/video/ffmpeg-renderers/vt_metal.mm
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
streaming/video/ffmpeg-renderers/vt.h
|
streaming/video/ffmpeg-renderers/vt.h
|
||||||
@@ -418,16 +396,6 @@ glslow {
|
|||||||
|
|
||||||
DEFINES += GL_IS_SLOW
|
DEFINES += GL_IS_SLOW
|
||||||
}
|
}
|
||||||
vkslow {
|
|
||||||
message(Vulkan slow build)
|
|
||||||
|
|
||||||
DEFINES += VULKAN_IS_SLOW
|
|
||||||
}
|
|
||||||
gpuslow {
|
|
||||||
message(GPU slow build)
|
|
||||||
|
|
||||||
DEFINES += GL_IS_SLOW VULKAN_IS_SLOW
|
|
||||||
}
|
|
||||||
wayland {
|
wayland {
|
||||||
message(Wayland extensions enabled)
|
message(Wayland extensions enabled)
|
||||||
|
|
||||||
@@ -464,9 +432,7 @@ TRANSLATIONS += \
|
|||||||
languages/qml_pt_BR.ts \
|
languages/qml_pt_BR.ts \
|
||||||
languages/qml_pl.ts \
|
languages/qml_pl.ts \
|
||||||
languages/qml_cs.ts \
|
languages/qml_cs.ts \
|
||||||
languages/qml_he.ts \
|
languages/qml_he.ts
|
||||||
languages/qml_ckb.ts \
|
|
||||||
languages/qml_lt.ts
|
|
||||||
|
|
||||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||||
QML_IMPORT_PATH =
|
QML_IMPORT_PATH =
|
||||||
@@ -555,19 +521,15 @@ macx {
|
|||||||
APP_BUNDLE_RESOURCES.files = moonlight.icns
|
APP_BUNDLE_RESOURCES.files = moonlight.icns
|
||||||
APP_BUNDLE_RESOURCES.path = Contents/Resources
|
APP_BUNDLE_RESOURCES.path = Contents/Resources
|
||||||
|
|
||||||
|
APP_BUNDLE_FRAMEWORKS.files = $$files(../libs/mac/Frameworks/*.framework, true) $$files(../libs/mac/lib/*.dylib, true)
|
||||||
|
APP_BUNDLE_FRAMEWORKS.path = Contents/Frameworks
|
||||||
|
|
||||||
APP_BUNDLE_PLIST.files = $$OUT_PWD/Info.plist
|
APP_BUNDLE_PLIST.files = $$OUT_PWD/Info.plist
|
||||||
APP_BUNDLE_PLIST.path = Contents
|
APP_BUNDLE_PLIST.path = Contents
|
||||||
|
|
||||||
QMAKE_BUNDLE_DATA += APP_BUNDLE_RESOURCES APP_BUNDLE_PLIST
|
QMAKE_BUNDLE_DATA += APP_BUNDLE_RESOURCES APP_BUNDLE_FRAMEWORKS APP_BUNDLE_PLIST
|
||||||
|
|
||||||
!disable-prebuilts {
|
QMAKE_RPATHDIR += @executable_path/../Frameworks
|
||||||
APP_BUNDLE_FRAMEWORKS.files = $$files(../libs/mac/Frameworks/*.framework, true) $$files(../libs/mac/lib/*.dylib, true)
|
|
||||||
APP_BUNDLE_FRAMEWORKS.path = Contents/Frameworks
|
|
||||||
|
|
||||||
QMAKE_BUNDLE_DATA += APP_BUNDLE_FRAMEWORKS
|
|
||||||
|
|
||||||
QMAKE_RPATHDIR += @executable_path/../Frameworks
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VERSION = "$$cat(version.txt)"
|
VERSION = "$$cat(version.txt)"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "computermanager.h"
|
#include "computermanager.h"
|
||||||
#include "boxartmanager.h"
|
#include "boxartmanager.h"
|
||||||
#include "nvhttp.h"
|
#include "nvhttp.h"
|
||||||
#include "nvpairingmanager.h"
|
#include "settings/streamingpreferences.h"
|
||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
@@ -144,8 +144,8 @@ private:
|
|||||||
NvComputer* m_Computer;
|
NvComputer* m_Computer;
|
||||||
};
|
};
|
||||||
|
|
||||||
ComputerManager::ComputerManager(StreamingPreferences* prefs)
|
ComputerManager::ComputerManager(QObject *parent)
|
||||||
: m_Prefs(prefs),
|
: QObject(parent),
|
||||||
m_PollingRef(0),
|
m_PollingRef(0),
|
||||||
m_MdnsBrowser(nullptr),
|
m_MdnsBrowser(nullptr),
|
||||||
m_CompatFetcher(nullptr),
|
m_CompatFetcher(nullptr),
|
||||||
@@ -352,7 +352,9 @@ void ComputerManager::startPolling()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Prefs->enableMdns) {
|
StreamingPreferences prefs;
|
||||||
|
|
||||||
|
if (prefs.enableMdns) {
|
||||||
// Start an MDNS query for GameStream hosts
|
// Start an MDNS query for GameStream hosts
|
||||||
m_MdnsServer.reset(new QMdnsEngine::Server());
|
m_MdnsServer.reset(new QMdnsEngine::Server());
|
||||||
m_MdnsBrowser = new QMdnsEngine::Browser(m_MdnsServer.data(), "_nvstream._tcp.local.");
|
m_MdnsBrowser = new QMdnsEngine::Browser(m_MdnsServer.data(), "_nvstream._tcp.local.");
|
||||||
@@ -782,9 +784,10 @@ private:
|
|||||||
return serverInfo;
|
return serverInfo;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
if (!m_Mdns) {
|
if (!m_Mdns) {
|
||||||
unsigned int portTestResult;
|
StreamingPreferences prefs;
|
||||||
|
int portTestResult;
|
||||||
|
|
||||||
if (m_ComputerManager->m_Prefs->detectNetworkBlocking) {
|
if (prefs.detectNetworkBlocking) {
|
||||||
// We failed to connect to the specified PC. Let's test to make sure this network
|
// We failed to connect to the specified PC. Let's test to make sure this network
|
||||||
// isn't blocking Moonlight, so we can tell the user about it.
|
// isn't blocking Moonlight, so we can tell the user about it.
|
||||||
portTestResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443,
|
portTestResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "nvcomputer.h"
|
#include "nvcomputer.h"
|
||||||
#include "settings/streamingpreferences.h"
|
#include "nvpairingmanager.h"
|
||||||
#include "settings/compatfetcher.h"
|
#include "settings/compatfetcher.h"
|
||||||
|
|
||||||
#include <qmdnsengine/server.h>
|
#include <qmdnsengine/server.h>
|
||||||
@@ -72,15 +72,9 @@ private slots:
|
|||||||
void handleResolvedTimeout()
|
void handleResolvedTimeout()
|
||||||
{
|
{
|
||||||
if (m_Addresses.isEmpty()) {
|
if (m_Addresses.isEmpty()) {
|
||||||
if (m_Retries-- > 0) {
|
// Try again
|
||||||
// Try again
|
qInfo() << "Resolving" << hostname() << "timed out. Retrying...";
|
||||||
qInfo() << "Resolving" << hostname() << "timed out. Retrying...";
|
resolve();
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qWarning() << "Giving up on resolving" << hostname() << "after repeated failures";
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Q_ASSERT(!m_Addresses.isEmpty());
|
Q_ASSERT(!m_Addresses.isEmpty());
|
||||||
@@ -98,7 +92,7 @@ signals:
|
|||||||
void resolvedHost(MdnsPendingComputer*,QVector<QHostAddress>&);
|
void resolvedHost(MdnsPendingComputer*,QVector<QHostAddress>&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void cleanup()
|
void resolve()
|
||||||
{
|
{
|
||||||
// Delete our resolver, so we're guaranteed that nothing is referencing m_Server.
|
// Delete our resolver, so we're guaranteed that nothing is referencing m_Server.
|
||||||
delete m_Resolver;
|
delete m_Resolver;
|
||||||
@@ -107,12 +101,6 @@ private:
|
|||||||
// Now delete our strong reference that we held on behalf of m_Resolver.
|
// Now delete our strong reference that we held on behalf of m_Resolver.
|
||||||
// The server may be destroyed after we make this call.
|
// The server may be destroyed after we make this call.
|
||||||
m_Server.reset();
|
m_Server.reset();
|
||||||
}
|
|
||||||
|
|
||||||
void resolve()
|
|
||||||
{
|
|
||||||
// Clean up any existing resolver object and server references
|
|
||||||
cleanup();
|
|
||||||
|
|
||||||
// Re-acquire a strong reference if the server still exists.
|
// Re-acquire a strong reference if the server still exists.
|
||||||
m_Server = m_ServerWeak.toStrongRef();
|
m_Server = m_ServerWeak.toStrongRef();
|
||||||
@@ -132,7 +120,6 @@ private:
|
|||||||
QSharedPointer<QMdnsEngine::Server> m_Server;
|
QSharedPointer<QMdnsEngine::Server> m_Server;
|
||||||
QMdnsEngine::Resolver* m_Resolver;
|
QMdnsEngine::Resolver* m_Resolver;
|
||||||
QVector<QHostAddress> m_Addresses;
|
QVector<QHostAddress> m_Addresses;
|
||||||
int m_Retries = 10;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ComputerPollingEntry
|
class ComputerPollingEntry
|
||||||
@@ -218,7 +205,7 @@ class ComputerManager : public QObject
|
|||||||
friend class DelayedFlushThread;
|
friend class DelayedFlushThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ComputerManager(StreamingPreferences* prefs);
|
explicit ComputerManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
virtual ~ComputerManager();
|
virtual ~ComputerManager();
|
||||||
|
|
||||||
@@ -270,7 +257,6 @@ private:
|
|||||||
|
|
||||||
void startPollingComputer(NvComputer* computer);
|
void startPollingComputer(NvComputer* computer);
|
||||||
|
|
||||||
StreamingPreferences* m_Prefs;
|
|
||||||
int m_PollingRef;
|
int m_PollingRef;
|
||||||
QReadWriteLock m_Lock;
|
QReadWriteLock m_Lock;
|
||||||
QMap<QString, NvComputer*> m_KnownHosts;
|
QMap<QString, NvComputer*> m_KnownHosts;
|
||||||
|
|||||||
@@ -6,13 +6,6 @@ ComputerSeeker::ComputerSeeker(ComputerManager *manager, QString computerName, Q
|
|||||||
: QObject(parent), m_ComputerManager(manager), m_ComputerName(computerName),
|
: QObject(parent), m_ComputerManager(manager), m_ComputerName(computerName),
|
||||||
m_TimeoutTimer(new QTimer(this))
|
m_TimeoutTimer(new QTimer(this))
|
||||||
{
|
{
|
||||||
// If we know this computer, send a WOL packet to wake it up in case it is asleep.
|
|
||||||
for (NvComputer * computer: m_ComputerManager->getComputers()) {
|
|
||||||
if (this->matchComputer(computer)) {
|
|
||||||
computer->wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_TimeoutTimer->setSingleShot(true);
|
m_TimeoutTimer->setSingleShot(true);
|
||||||
connect(m_TimeoutTimer, &QTimer::timeout,
|
connect(m_TimeoutTimer, &QTimer::timeout,
|
||||||
this, &ComputerSeeker::onTimeout);
|
this, &ComputerSeeker::onTimeout);
|
||||||
|
|||||||
@@ -373,7 +373,6 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
|
|||||||
s.connectToHost(copyOfActiveAddress.address(), copyOfActiveAddress.port());
|
s.connectToHost(copyOfActiveAddress.address(), copyOfActiveAddress.port());
|
||||||
if (s.waitForConnected(3000)) {
|
if (s.waitForConnected(3000)) {
|
||||||
Q_ASSERT(!s.localAddress().isNull());
|
Q_ASSERT(!s.localAddress().isNull());
|
||||||
Q_ASSERT(!s.peerAddress().isNull());
|
|
||||||
|
|
||||||
for (const QNetworkInterface& nic : QNetworkInterface::allInterfaces()) {
|
for (const QNetworkInterface& nic : QNetworkInterface::allInterfaces()) {
|
||||||
// Ensure the interface is up
|
// Ensure the interface is up
|
||||||
@@ -407,19 +406,6 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
|
|||||||
return ReachabilityType::RI_VPN;
|
return ReachabilityType::RI_VPN;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
if (nic.name().startsWith("iftype53_") || nic.name().startsWith("iftype131_")) {
|
|
||||||
// Match by NDIS interface type. These values are Microsoft's recommended values for VPN connections:
|
|
||||||
// https://learn.microsoft.com/en-US/troubleshoot/windows-client/networking/windows-connection-manager-disconnects-wlan#more-information
|
|
||||||
//
|
|
||||||
// The following VPNs use IF_TYPE_PROP_VIRTUAL under Windows:
|
|
||||||
// - WireguardNT VPNs
|
|
||||||
// - All WinTun-based VPNs (such as Slack Nebula)
|
|
||||||
// - OpenVPN with tap-windows6
|
|
||||||
return ReachabilityType::RI_VPN;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (nic.hardwareAddress().startsWith("00:FF", Qt::CaseInsensitive)) {
|
if (nic.hardwareAddress().startsWith("00:FF", Qt::CaseInsensitive)) {
|
||||||
// OpenVPN TAP interfaces have a MAC address starting with 00:FF on Windows
|
// OpenVPN TAP interfaces have a MAC address starting with 00:FF on Windows
|
||||||
return ReachabilityType::RI_VPN;
|
return ReachabilityType::RI_VPN;
|
||||||
@@ -435,9 +421,9 @@ NvComputer::ReachabilityType NvComputer::getActiveAddressReachability() const
|
|||||||
return ReachabilityType::RI_VPN;
|
return ReachabilityType::RI_VPN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't meet any of our VPN heuristics. Let's see if the peer address is on-link.
|
// Didn't meet any of our VPN heuristics. Let's see if it's on-link.
|
||||||
Q_ASSERT(addr.prefixLength() >= 0);
|
Q_ASSERT(addr.prefixLength() >= 0);
|
||||||
if (addr.prefixLength() >= 0 && s.localAddress().isInSubnet(s.peerAddress(), addr.prefixLength())) {
|
if (addr.prefixLength() >= 0 && s.localAddress().isInSubnet(addr.ip(), addr.prefixLength())) {
|
||||||
return ReachabilityType::RI_LAN;
|
return ReachabilityType::RI_LAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,8 +223,7 @@ NvHTTP::startApp(QString verb,
|
|||||||
"&surroundAudioInfo="+QString::number(SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(streamConfig->audioConfiguration))+
|
"&surroundAudioInfo="+QString::number(SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(streamConfig->audioConfiguration))+
|
||||||
"&remoteControllersBitmap="+QString::number(gamepadMask)+
|
"&remoteControllersBitmap="+QString::number(gamepadMask)+
|
||||||
"&gcmap="+QString::number(gamepadMask)+
|
"&gcmap="+QString::number(gamepadMask)+
|
||||||
"&gcpersist="+QString::number(persistGameControllersOnDisconnect ? 1 : 0)+
|
"&gcpersist="+QString::number(persistGameControllersOnDisconnect ? 1 : 0),
|
||||||
LiGetLaunchUrlQueryParameters(),
|
|
||||||
LAUNCH_TIMEOUT_MS);
|
LAUNCH_TIMEOUT_MS);
|
||||||
|
|
||||||
qInfo() << "Launch response:" << response;
|
qInfo() << "Launch response:" << response;
|
||||||
|
|||||||
@@ -298,8 +298,8 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
|
|||||||
}
|
}
|
||||||
|
|
||||||
QByteArray pairingSecret = NvHTTP::getXmlStringFromHex(respXml, "pairingsecret");
|
QByteArray pairingSecret = NvHTTP::getXmlStringFromHex(respXml, "pairingsecret");
|
||||||
QByteArray serverSecret = pairingSecret.left(16);
|
QByteArray serverSecret = QByteArray(pairingSecret.data(), 16);
|
||||||
QByteArray serverSignature = pairingSecret.mid(16);
|
QByteArray serverSignature = QByteArray(&pairingSecret.data()[16], 256);
|
||||||
|
|
||||||
if (!verifySignature(serverSecret,
|
if (!verifySignature(serverSecret,
|
||||||
serverSignature,
|
serverSignature,
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ SystemProperties::SystemProperties()
|
|||||||
|
|
||||||
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
||||||
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
||||||
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect SystemProperties::getNativeResolution(int displayIndex)
|
QRect SystemProperties::getNativeResolution(int displayIndex)
|
||||||
@@ -83,12 +82,6 @@ QRect SystemProperties::getNativeResolution(int displayIndex)
|
|||||||
return monitorNativeResolutions.value(displayIndex);
|
return monitorNativeResolutions.value(displayIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
|
||||||
{
|
|
||||||
// Returns default constructed QRect if out of bounds
|
|
||||||
return monitorSafeAreaResolutions.value(displayIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int SystemProperties::getRefreshRate(int displayIndex)
|
int SystemProperties::getRefreshRate(int displayIndex)
|
||||||
{
|
{
|
||||||
// Returns 0 if out of bounds
|
// Returns 0 if out of bounds
|
||||||
@@ -206,12 +199,10 @@ void SystemProperties::refreshDisplaysInternal()
|
|||||||
SDL_DisplayMode bestMode;
|
SDL_DisplayMode bestMode;
|
||||||
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
|
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
|
||||||
SDL_DisplayMode desktopMode;
|
SDL_DisplayMode desktopMode;
|
||||||
SDL_Rect safeArea;
|
|
||||||
|
|
||||||
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
|
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) {
|
||||||
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
|
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
|
||||||
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
|
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
|
||||||
monitorSafeAreaResolutions.insert(displayIndex, QRect(0, 0, safeArea.w, safeArea.h));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void refreshDisplays();
|
Q_INVOKABLE void refreshDisplays();
|
||||||
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
|
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
|
||||||
Q_INVOKABLE QRect getSafeAreaResolution(int displayIndex);
|
|
||||||
Q_INVOKABLE int getRefreshRate(int displayIndex);
|
Q_INVOKABLE int getRefreshRate(int displayIndex);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -53,7 +52,6 @@ private:
|
|||||||
QString unmappedGamepads;
|
QString unmappedGamepads;
|
||||||
QSize maximumResolution;
|
QSize maximumResolution;
|
||||||
QList<QRect> monitorNativeResolutions;
|
QList<QRect> monitorNativeResolutions;
|
||||||
QList<QRect> monitorSafeAreaResolutions;
|
|
||||||
QList<int> monitorRefreshRates;
|
QList<int> monitorRefreshRates;
|
||||||
QString versionString;
|
QString versionString;
|
||||||
bool supportsHdr;
|
bool supportsHdr;
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ StreamCommandLineParser::StreamCommandLineParser()
|
|||||||
{"auto", StreamingPreferences::VCC_AUTO},
|
{"auto", StreamingPreferences::VCC_AUTO},
|
||||||
{"H.264", StreamingPreferences::VCC_FORCE_H264},
|
{"H.264", StreamingPreferences::VCC_FORCE_H264},
|
||||||
{"HEVC", StreamingPreferences::VCC_FORCE_HEVC},
|
{"HEVC", StreamingPreferences::VCC_FORCE_HEVC},
|
||||||
{"AV1", StreamingPreferences::VCC_FORCE_AV1},
|
|
||||||
};
|
};
|
||||||
m_VideoDecoderMap = {
|
m_VideoDecoderMap = {
|
||||||
{"auto", StreamingPreferences::VDS_AUTO},
|
{"auto", StreamingPreferences::VDS_AUTO},
|
||||||
@@ -372,8 +371,6 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
|
|||||||
parser.addToggleOption("reverse-scroll-direction", "inverted scroll direction");
|
parser.addToggleOption("reverse-scroll-direction", "inverted scroll direction");
|
||||||
parser.addToggleOption("swap-gamepad-buttons", "swap A/B and X/Y gamepad buttons (Nintendo-style)");
|
parser.addToggleOption("swap-gamepad-buttons", "swap A/B and X/Y gamepad buttons (Nintendo-style)");
|
||||||
parser.addToggleOption("keep-awake", "prevent display sleep while streaming");
|
parser.addToggleOption("keep-awake", "prevent display sleep while streaming");
|
||||||
parser.addToggleOption("performance-overlay", "show performance overlay");
|
|
||||||
parser.addToggleOption("hdr", "HDR streaming");
|
|
||||||
parser.addChoiceOption("capture-system-keys", "capture system key combos", m_CaptureSysKeysModeMap.keys());
|
parser.addChoiceOption("capture-system-keys", "capture system key combos", m_CaptureSysKeysModeMap.keys());
|
||||||
parser.addChoiceOption("video-codec", "video codec", m_VideoCodecMap.keys());
|
parser.addChoiceOption("video-codec", "video codec", m_VideoCodecMap.keys());
|
||||||
parser.addChoiceOption("video-decoder", "video decoder", m_VideoDecoderMap.keys());
|
parser.addChoiceOption("video-decoder", "video decoder", m_VideoDecoderMap.keys());
|
||||||
@@ -412,8 +409,8 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
|
|||||||
// Resolve --fps option
|
// Resolve --fps option
|
||||||
if (parser.isSet("fps")) {
|
if (parser.isSet("fps")) {
|
||||||
preferences->fps = parser.getIntOption("fps");
|
preferences->fps = parser.getIntOption("fps");
|
||||||
if (!inRange(preferences->fps, 10, 480)) {
|
if (!inRange(preferences->fps, 30, 240)) {
|
||||||
parser.showError("FPS must be in range: 10 - 480");
|
parser.showError("FPS must be in range: 30 - 240");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,12 +485,6 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
|
|||||||
// Resolve --keep-awake and --no-keep-awake options
|
// Resolve --keep-awake and --no-keep-awake options
|
||||||
preferences->keepAwake = parser.getToggleOptionValue("keep-awake", preferences->keepAwake);
|
preferences->keepAwake = parser.getToggleOptionValue("keep-awake", preferences->keepAwake);
|
||||||
|
|
||||||
// Resolve --performance-overlay and --no-performance-overlay options
|
|
||||||
preferences->showPerformanceOverlay = parser.getToggleOptionValue("performance-overlay", preferences->showPerformanceOverlay);
|
|
||||||
|
|
||||||
// Resolve --hdr and --no-hdr options
|
|
||||||
preferences->enableHdr = parser.getToggleOptionValue("hdr", preferences->enableHdr);
|
|
||||||
|
|
||||||
// Resolve --capture-system-keys option
|
// Resolve --capture-system-keys option
|
||||||
if (parser.isSet("capture-system-keys")) {
|
if (parser.isSet("capture-system-keys")) {
|
||||||
preferences->captureSysKeysMode = mapValue(m_CaptureSysKeysModeMap, parser.getChoiceOptionValue("capture-system-keys"));
|
preferences->captureSysKeysMode = mapValue(m_CaptureSysKeysModeMap, parser.getChoiceOptionValue("capture-system-keys"));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#define COMPUTER_SEEK_TIMEOUT 30000
|
#define COMPUTER_SEEK_TIMEOUT 10000
|
||||||
#define APP_SEEK_TIMEOUT 10000
|
#define APP_SEEK_TIMEOUT 10000
|
||||||
|
|
||||||
namespace CliListApps
|
namespace CliListApps
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#define COMPUTER_SEEK_TIMEOUT 30000
|
#define COMPUTER_SEEK_TIMEOUT 10000
|
||||||
#define APP_SEEK_TIMEOUT 10000
|
#define APP_SEEK_TIMEOUT 10000
|
||||||
|
|
||||||
namespace CliStartStream
|
namespace CliStartStream
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
From 78261eb6e2fe728fa5d3843b5b8962b1ca559b7e Mon Sep 17 00:00:00 2001
|
|
||||||
From: David Rosca <nowrep@gmail.com>
|
|
||||||
Date: Sat, 16 Dec 2023 14:52:01 +0100
|
|
||||||
Subject: [PATCH] Vulkan: Don't try to reuse old swapchain
|
|
||||||
|
|
||||||
---
|
|
||||||
src/vulkan/swapchain.c | 12 ++++++++++--
|
|
||||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/vulkan/swapchain.c b/src/vulkan/swapchain.c
|
|
||||||
index 0646e57c..c0b07ce4 100644
|
|
||||||
--- a/src/vulkan/swapchain.c
|
|
||||||
+++ b/src/vulkan/swapchain.c
|
|
||||||
@@ -612,13 +612,21 @@ static bool vk_sw_recreate(pl_swapchain sw, int w, int h)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+ {
|
|
||||||
+ pl_gpu_flush(gpu);
|
|
||||||
+ vk_wait_idle(vk);
|
|
||||||
+ for (int i = 0; i < vk->pool_graphics->num_queues; i++)
|
|
||||||
+ vk->QueueWaitIdle(vk->pool_graphics->queues[i]);
|
|
||||||
+ vk->DestroySwapchainKHR(vk->dev, p->swapchain, PL_VK_ALLOC);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
// Calling `vkCreateSwapchainKHR` puts sinfo.oldSwapchain into a retired
|
|
||||||
// state whether the call succeeds or not, so we always need to garbage
|
|
||||||
// collect it afterwards - asynchronously as it may still be in use
|
|
||||||
- sinfo.oldSwapchain = p->swapchain;
|
|
||||||
+ /* sinfo.oldSwapchain = p->swapchain; */
|
|
||||||
p->swapchain = VK_NULL_HANDLE;
|
|
||||||
VkResult res = vk->CreateSwapchainKHR(vk->dev, &sinfo, PL_VK_ALLOC, &p->swapchain);
|
|
||||||
- vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain));
|
|
||||||
+ /* vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain)); */
|
|
||||||
PL_VK_ASSERT(res, "vk->CreateSwapchainKHR(...)");
|
|
||||||
|
|
||||||
// Get the new swapchain images
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
@@ -34,62 +34,6 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release version="6.0.1" date="2024-06-30">
|
|
||||||
<description>
|
|
||||||
<p>New features:</p>
|
|
||||||
<ul>
|
|
||||||
<li>HDR to SDR tonemapping is now supported by the Vulkan renderer on Linux</li>
|
|
||||||
</ul>
|
|
||||||
<p>Behavior Changes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>More time is now provided for PCs to wake up before displaying an error when starting a stream from the CLI</li>
|
|
||||||
<li>Moonlight will no longer try to guess VA driver names when running libva 2.20 or later</li>
|
|
||||||
</ul>
|
|
||||||
<p>Bugfixes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Fixed crash when the Vulkan renderer executed on a system with no Vulkan devices</li>
|
|
||||||
<li>Updated community-contributed translations from Weblate</li>
|
|
||||||
</ul>
|
|
||||||
</description>
|
|
||||||
</release>
|
|
||||||
<release version="6.0.0" date="2024-06-06">
|
|
||||||
<description>
|
|
||||||
<p>New features:</p>
|
|
||||||
<ul>
|
|
||||||
<li>HDR is now supported on Steam Deck and other HDR-capable Linux systems using a new Vulkan-based renderer</li>
|
|
||||||
<li>Vulkan Video decoding of H.264, HEVC, and AV1 is now supported using the new Vulkan renderer</li>
|
|
||||||
<li>Full E2E stream encryption is now supported when streaming from Sunshine v0.22 or later</li>
|
|
||||||
<li>Initiating a stream via the command-line will now automatically wake the target machine if it is sleeping</li>
|
|
||||||
<li>Error codes are now displayed in the connection termination dialog</li>
|
|
||||||
<li>Detailed PC state information is now available via the new "View Details" context menu option (similar to the Android client)</li>
|
|
||||||
<li>The performance overlay can now be configured to be shown by default when starting a stream</li>
|
|
||||||
</ul>
|
|
||||||
<p>Behavior Changes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>The selected bitrate is no longer silently adjusted by Moonlight based upon the video codec or HDR mode</li>
|
|
||||||
<li>The selected mouse mode when starting a stream no longer influences the acceleration curve when the mouse mode is toggled using the Ctrl+Alt+Shift+M key combo while streaming</li>
|
|
||||||
<li>Hardcoded fallback paths for VAAPI and VDPAU drivers are no longer used</li>
|
|
||||||
<li>Per-session keys (rikey and rikeyid values) are now automatically stripped from logs</li>
|
|
||||||
<li>Log messages will now be logged to stderr rather than stdout</li>
|
|
||||||
<li>EGL is now always used instead of GLX for loading OpenGL in X11 environments on non-x86 platforms</li>
|
|
||||||
</ul>
|
|
||||||
<p>Bugfixes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Improved reliability of streaming during momentary drops of WiFi connections</li>
|
|
||||||
<li>Improved performance with high polling rate mice</li>
|
|
||||||
<li>Improved input latency when a gamepad is connected</li>
|
|
||||||
<li>Added missing command-line options to control HDR and AV1 codec usage</li>
|
|
||||||
<li>Fixed instant connection termination error when streaming over the Internet behind certain Carrier-Grade NAT devices</li>
|
|
||||||
<li>Fixed incorrect MTU detection when streaming over the Internet without a VPN</li>
|
|
||||||
<li>Fixed recovery of audio when no audio output device is present when the stream starts</li>
|
|
||||||
<li>Fixed merging of gamepad state when multi-controller mode is disabled</li>
|
|
||||||
<li>Fixed indefinitely resending mDNS queries when a host could not be resolved</li>
|
|
||||||
<li>Fixed inability to pair when the host is not using a RSA-2048 key</li>
|
|
||||||
<li>Updated included gamepad mappings</li>
|
|
||||||
<li>Updated community-contributed translations from Weblate</li>
|
|
||||||
</ul>
|
|
||||||
</description>
|
|
||||||
</release>
|
|
||||||
<release version="5.0.1" date="2023-11-02">
|
<release version="5.0.1" date="2023-11-02">
|
||||||
<description>
|
<description>
|
||||||
<p>Bugfixes:</p>
|
<p>Bugfixes:</p>
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
# pairing data will be lost each reboot.
|
# pairing data will be lost each reboot.
|
||||||
HOME=/usr/local/moonlight
|
HOME=/usr/local/moonlight
|
||||||
|
|
||||||
# Enable Steam Controller detection in SDL
|
|
||||||
# https://github.com/moonlight-stream/moonlight-qt/issues/697
|
|
||||||
export SDL_ENABLE_STEAM_CONTROLLERS=1
|
|
||||||
|
|
||||||
# Renice PE_Single_CPU which seems to host A/V stuff
|
# Renice PE_Single_CPU which seems to host A/V stuff
|
||||||
renice -10 -p $(pidof PE_Single_CPU)
|
renice -10 -p $(pidof PE_Single_CPU)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -256,7 +256,7 @@ CenteredGridView {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.RightButton;
|
acceptedButtons: Qt.RightButton;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
parent.pressAndHold()
|
parent.onPressAndHold()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ CenteredGridView {
|
|||||||
|
|
||||||
function quitApp() {
|
function quitApp() {
|
||||||
var component = Qt.createComponent("QuitSegue.qml")
|
var component = Qt.createComponent("QuitSegue.qml")
|
||||||
var params = {"appName": appName, "quitRunningAppFn": () => appModel.quitRunningApp()}
|
var params = {"appName": appName, "quitRunningAppFn": appModel.quitRunningApp}
|
||||||
if (segueToStream) {
|
if (segueToStream) {
|
||||||
// Store the session and app name if we're going to stream after
|
// Store the session and app name if we're going to stream after
|
||||||
// successfully quitting the old app.
|
// successfully quitting the old app.
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ Item {
|
|||||||
|
|
||||||
function quitApp() {
|
function quitApp() {
|
||||||
var component = Qt.createComponent("QuitSegue.qml")
|
var component = Qt.createComponent("QuitSegue.qml")
|
||||||
var params = {"appName": appName, "quitRunningAppFn": () => launcher.quitRunningApp()}
|
var params = {"appName": appName, "quitRunningAppFn": launcher.quitRunningApp}
|
||||||
stackView.push(component.createObject(stackView, params))
|
stackView.push(component.createObject(stackView, params))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-18
@@ -6,7 +6,6 @@ import ComputerModel 1.0
|
|||||||
|
|
||||||
import ComputerManager 1.0
|
import ComputerManager 1.0
|
||||||
import StreamingPreferences 1.0
|
import StreamingPreferences 1.0
|
||||||
import SystemProperties 1.0
|
|
||||||
import SdlGamepadKeyNavigation 1.0
|
import SdlGamepadKeyNavigation 1.0
|
||||||
|
|
||||||
CenteredGridView {
|
CenteredGridView {
|
||||||
@@ -216,14 +215,6 @@ CenteredGridView {
|
|||||||
deletePcDialog.open()
|
deletePcDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigableMenuItem {
|
|
||||||
parentMenu: pcContextMenu
|
|
||||||
text: qsTr("View Details")
|
|
||||||
onTriggered: {
|
|
||||||
showPcDetailsDialog.pcDetails = model.details
|
|
||||||
showPcDetailsDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +262,7 @@ CenteredGridView {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.RightButton;
|
acceptedButtons: Qt.RightButton;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
parent.pressAndHold()
|
parent.onPressAndHold()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,13 +395,5 @@ CenteredGridView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigableMessageDialog {
|
|
||||||
id: showPcDetailsDialog
|
|
||||||
property string pcDetails : "";
|
|
||||||
text: showPcDetailsDialog.pcDetails
|
|
||||||
imageSrc: "qrc:/res/baseline-help_outline-24px.svg"
|
|
||||||
standardButtons: Dialog.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
}
|
}
|
||||||
+53
-65
@@ -133,45 +133,15 @@ Flickable {
|
|||||||
AutoResizingComboBox {
|
AutoResizingComboBox {
|
||||||
property int lastIndexValue
|
property int lastIndexValue
|
||||||
|
|
||||||
function addDetectedResolution(friendlyNamePrefix, rect) {
|
|
||||||
var indexToAdd = 0
|
|
||||||
for (var j = 0; j < resolutionComboBox.count; j++) {
|
|
||||||
var existing_width = parseInt(resolutionListModel.get(j).video_width);
|
|
||||||
var existing_height = parseInt(resolutionListModel.get(j).video_height);
|
|
||||||
|
|
||||||
if (rect.width === existing_width && rect.height === existing_height) {
|
|
||||||
// Duplicate entry, skip
|
|
||||||
indexToAdd = -1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
else if (rect.width * rect.height > existing_width * existing_height) {
|
|
||||||
// Candidate entrypoint after this entry
|
|
||||||
indexToAdd = j + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert this display's resolution if it's not a duplicate
|
|
||||||
if (indexToAdd >= 0) {
|
|
||||||
resolutionListModel.insert(indexToAdd,
|
|
||||||
{
|
|
||||||
"text": friendlyNamePrefix+" ("+rect.width+"x"+rect.height+")",
|
|
||||||
"video_width": ""+rect.width,
|
|
||||||
"video_height": ""+rect.height,
|
|
||||||
"is_custom": false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore setting the index at first, and actually set it when the component is loaded
|
// ignore setting the index at first, and actually set it when the component is loaded
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Refresh display data before using it to build the list
|
// Refresh display data before using it to build the list
|
||||||
SystemProperties.refreshDisplays()
|
SystemProperties.refreshDisplays()
|
||||||
|
|
||||||
// Add native and safe area resolutions for all attached displays
|
// Add native resolutions for all attached displays
|
||||||
var done = false
|
var done = false
|
||||||
for (var displayIndex = 0; !done; displayIndex++) {
|
for (var displayIndex = 0; !done; displayIndex++) {
|
||||||
var screenRect = SystemProperties.getNativeResolution(displayIndex);
|
var screenRect = SystemProperties.getNativeResolution(displayIndex);
|
||||||
var safeAreaRect = SystemProperties.getSafeAreaResolution(displayIndex);
|
|
||||||
|
|
||||||
if (screenRect.width === 0) {
|
if (screenRect.width === 0) {
|
||||||
// Exceeded max count of displays
|
// Exceeded max count of displays
|
||||||
@@ -179,8 +149,32 @@ Flickable {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
addDetectedResolution(qsTr("Native"), screenRect)
|
var indexToAdd = 0
|
||||||
addDetectedResolution(qsTr("Native (Excluding Notch)"), safeAreaRect)
|
for (var j = 0; j < resolutionComboBox.count; j++) {
|
||||||
|
var existing_width = parseInt(resolutionListModel.get(j).video_width);
|
||||||
|
var existing_height = parseInt(resolutionListModel.get(j).video_height);
|
||||||
|
|
||||||
|
if (screenRect.width === existing_width && screenRect.height === existing_height) {
|
||||||
|
// Duplicate entry, skip
|
||||||
|
indexToAdd = -1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else if (screenRect.width * screenRect.height > existing_width * existing_height) {
|
||||||
|
// Candidate entrypoint after this entry
|
||||||
|
indexToAdd = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert this display's resolution if it's not a duplicate
|
||||||
|
if (indexToAdd >= 0) {
|
||||||
|
resolutionListModel.insert(indexToAdd,
|
||||||
|
{
|
||||||
|
"text": "Native ("+screenRect.width+"x"+screenRect.height+")",
|
||||||
|
"video_width": ""+screenRect.width,
|
||||||
|
"video_height": ""+screenRect.height,
|
||||||
|
"is_custom": false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune resolutions that are over the decoder's maximum
|
// Prune resolutions that are over the decoder's maximum
|
||||||
@@ -216,7 +210,7 @@ Flickable {
|
|||||||
if (!index_set) {
|
if (!index_set) {
|
||||||
// We did not find a match. This must be a custom resolution.
|
// We did not find a match. This must be a custom resolution.
|
||||||
resolutionListModel.append({
|
resolutionListModel.append({
|
||||||
"text": qsTr("Custom")+" ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")",
|
"text": "Custom ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")",
|
||||||
"video_width": ""+StreamingPreferences.width,
|
"video_width": ""+StreamingPreferences.width,
|
||||||
"video_height": ""+StreamingPreferences.height,
|
"video_height": ""+StreamingPreferences.height,
|
||||||
"is_custom": true
|
"is_custom": true
|
||||||
@@ -225,7 +219,7 @@ Flickable {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolutionListModel.append({
|
resolutionListModel.append({
|
||||||
"text": qsTr("Custom"),
|
"text": "Custom",
|
||||||
"video_width": "",
|
"video_width": "",
|
||||||
"video_height": "",
|
"video_height": "",
|
||||||
"is_custom": true
|
"is_custom": true
|
||||||
@@ -695,7 +689,7 @@ Flickable {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Refresh the text after translations change
|
// Refresh the text after translations change
|
||||||
languageChanged.connect(valueChanged)
|
languageChanged.connect(onValueChanged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,10 +996,10 @@ Flickable {
|
|||||||
text: "한국어" // Korean
|
text: "한국어" // Korean
|
||||||
val: StreamingPreferences.LANG_KO
|
val: StreamingPreferences.LANG_KO
|
||||||
}
|
}
|
||||||
ListElement {
|
/* ListElement {
|
||||||
text: "Magyar" // Hungarian
|
text: "Magyar" // Hungarian
|
||||||
val: StreamingPreferences.LANG_HU
|
val: StreamingPreferences.LANG_HU
|
||||||
}
|
} */
|
||||||
ListElement {
|
ListElement {
|
||||||
text: "Nederlands" // Dutch
|
text: "Nederlands" // Dutch
|
||||||
val: StreamingPreferences.LANG_NL
|
val: StreamingPreferences.LANG_NL
|
||||||
@@ -1058,14 +1052,6 @@ Flickable {
|
|||||||
text: "עִבְרִית" // Hebrew
|
text: "עִבְרִית" // Hebrew
|
||||||
val: StreamingPreferences.LANG_HE
|
val: StreamingPreferences.LANG_HE
|
||||||
} */
|
} */
|
||||||
/* ListElement {
|
|
||||||
text: "کرمانجیی خواروو" // Central Kurdish
|
|
||||||
val: StreamingPreferences.LANG_CKB
|
|
||||||
} */
|
|
||||||
/* ListElement {
|
|
||||||
text: "Lietuvių kalba" // Lithuanian
|
|
||||||
val: StreamingPreferences.LANG_LT
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
||||||
onActivated : {
|
onActivated : {
|
||||||
@@ -1363,7 +1349,15 @@ Flickable {
|
|||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
checked: StreamingPreferences.swapFaceButtons
|
checked: StreamingPreferences.swapFaceButtons
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
StreamingPreferences.swapFaceButtons = checked
|
// Check if the value changed (this is called on init too)
|
||||||
|
if (StreamingPreferences.swapFaceButtons !== checked) {
|
||||||
|
StreamingPreferences.swapFaceButtons = checked
|
||||||
|
|
||||||
|
// Save and restart SdlGamepadKeyNavigation so it can pull the new value
|
||||||
|
StreamingPreferences.save()
|
||||||
|
SdlGamepadKeyNavigation.disable()
|
||||||
|
SdlGamepadKeyNavigation.enable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip.delay: 1000
|
ToolTip.delay: 1000
|
||||||
@@ -1627,6 +1621,10 @@ Flickable {
|
|||||||
if (StreamingPreferences.enableMdns != checked) {
|
if (StreamingPreferences.enableMdns != checked) {
|
||||||
StreamingPreferences.enableMdns = checked
|
StreamingPreferences.enableMdns = checked
|
||||||
|
|
||||||
|
// We must save the updated preference to ensure
|
||||||
|
// ComputerManager can observe the change internally.
|
||||||
|
StreamingPreferences.save()
|
||||||
|
|
||||||
// Restart polling so the mDNS change takes effect
|
// Restart polling so the mDNS change takes effect
|
||||||
if (window.pollingActive) {
|
if (window.pollingActive) {
|
||||||
ComputerManager.stopPollingAsync()
|
ComputerManager.stopPollingAsync()
|
||||||
@@ -1643,26 +1641,16 @@ Flickable {
|
|||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
checked: StreamingPreferences.detectNetworkBlocking
|
checked: StreamingPreferences.detectNetworkBlocking
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
StreamingPreferences.detectNetworkBlocking = checked
|
// This is called on init, so only do the work if we've
|
||||||
}
|
// actually changed the value.
|
||||||
}
|
if (StreamingPreferences.detectNetworkBlocking != checked) {
|
||||||
|
StreamingPreferences.detectNetworkBlocking = checked
|
||||||
|
|
||||||
CheckBox {
|
// We must save the updated preference to ensure
|
||||||
id: showPerformanceOverlay
|
// ComputerManager can observe the change internally.
|
||||||
width: parent.width
|
StreamingPreferences.save()
|
||||||
text: qsTr("Show performance stats while streaming")
|
}
|
||||||
font.pointSize: 12
|
|
||||||
checked: StreamingPreferences.showPerformanceOverlay
|
|
||||||
onCheckedChanged: {
|
|
||||||
StreamingPreferences.showPerformanceOverlay = checked
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip.delay: 1000
|
|
||||||
ToolTip.timeout: 5000
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Display real-time stream performance information while streaming.") + "\n\n" +
|
|
||||||
qsTr("You can toggle it at any time while streaming using Ctrl+Alt+Shift+S or Select+L1+R1+X.") + "\n\n" +
|
|
||||||
qsTr("The performance overlay is not supported on Steam Link or Raspberry Pi.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ Item {
|
|||||||
gc()
|
gc()
|
||||||
|
|
||||||
// Run the streaming session to completion
|
// Run the streaming session to completion
|
||||||
session.exec(Window.window)
|
session.exec(Screen.virtualX, Screen.virtualY)
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceComponent: Item {}
|
sourceComponent: Item {}
|
||||||
|
|||||||
@@ -42,46 +42,6 @@ QVariant ComputerModel::data(const QModelIndex& index, int role) const
|
|||||||
return computer->state == NvComputer::CS_UNKNOWN;
|
return computer->state == NvComputer::CS_UNKNOWN;
|
||||||
case ServerSupportedRole:
|
case ServerSupportedRole:
|
||||||
return computer->isSupportedServerVersion;
|
return computer->isSupportedServerVersion;
|
||||||
case DetailsRole: {
|
|
||||||
QString state, pairState;
|
|
||||||
|
|
||||||
switch (computer->state) {
|
|
||||||
case NvComputer::CS_ONLINE:
|
|
||||||
state = tr("Online");
|
|
||||||
break;
|
|
||||||
case NvComputer::CS_OFFLINE:
|
|
||||||
state = tr("Offline");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
state = tr("Unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (computer->pairState) {
|
|
||||||
case NvComputer::PS_PAIRED:
|
|
||||||
pairState = tr("Paired");
|
|
||||||
break;
|
|
||||||
case NvComputer::PS_NOT_PAIRED:
|
|
||||||
pairState = tr("Unpaired");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pairState = tr("Unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr("Name: %1").arg(computer->name) + '\n' +
|
|
||||||
tr("Status: %1").arg(state) + '\n' +
|
|
||||||
tr("Active Address: %1").arg(computer->activeAddress.toString()) + '\n' +
|
|
||||||
tr("UUID: %1").arg(computer->uuid) + '\n' +
|
|
||||||
tr("Local Address: %1").arg(computer->localAddress.toString()) + '\n' +
|
|
||||||
tr("Remote Address: %1").arg(computer->remoteAddress.toString()) + '\n' +
|
|
||||||
tr("IPv6 Address: %1").arg(computer->ipv6Address.toString()) + '\n' +
|
|
||||||
tr("Manual Address: %1").arg(computer->manualAddress.toString()) + '\n' +
|
|
||||||
tr("MAC Address: %1").arg(computer->macAddress.isEmpty() ? tr("Unknown") : QString(computer->macAddress.toHex(':'))) + '\n' +
|
|
||||||
tr("Pair State: %1").arg(pairState) + '\n' +
|
|
||||||
tr("Running Game ID: %1").arg(computer->state == NvComputer::CS_ONLINE ? QString::number(computer->currentGameId) : tr("Unknown")) + '\n' +
|
|
||||||
tr("HTTPS Port: %1").arg(computer->state == NvComputer::CS_ONLINE ? QString::number(computer->activeHttpsPort) : tr("Unknown"));
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@@ -109,7 +69,6 @@ QHash<int, QByteArray> ComputerModel::roleNames() const
|
|||||||
names[WakeableRole] = "wakeable";
|
names[WakeableRole] = "wakeable";
|
||||||
names[StatusUnknownRole] = "statusUnknown";
|
names[StatusUnknownRole] = "statusUnknown";
|
||||||
names[ServerSupportedRole] = "serverSupported";
|
names[ServerSupportedRole] = "serverSupported";
|
||||||
names[DetailsRole] = "details";
|
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ class ComputerModel : public QAbstractListModel
|
|||||||
BusyRole,
|
BusyRole,
|
||||||
WakeableRole,
|
WakeableRole,
|
||||||
StatusUnknownRole,
|
StatusUnknownRole,
|
||||||
ServerSupportedRole,
|
ServerSupportedRole
|
||||||
DetailsRole
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
+46
-33
@@ -19,48 +19,26 @@ ApplicationWindow {
|
|||||||
property bool clearOnBack: false
|
property bool clearOnBack: false
|
||||||
|
|
||||||
id: window
|
id: window
|
||||||
|
visible: true
|
||||||
width: 1280
|
width: 1280
|
||||||
height: 600
|
height: 600
|
||||||
|
|
||||||
|
// Override the background color to Material 2 colors for Qt 6.5+
|
||||||
|
// in order to improve contrast between GFE's placeholder box art
|
||||||
|
// and the background of the app grid.
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Override the background color to Material 2 colors for Qt 6.5+
|
|
||||||
// in order to improve contrast between GFE's placeholder box art
|
|
||||||
// and the background of the app grid.
|
|
||||||
if (SystemProperties.usesMaterial3Theme) {
|
if (SystemProperties.usesMaterial3Theme) {
|
||||||
Material.background = "#303030"
|
Material.background = "#303030"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show the window according to the user's preferences
|
visibility: {
|
||||||
if (SystemProperties.hasDesktopEnvironment) {
|
if (SystemProperties.hasDesktopEnvironment) {
|
||||||
if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_MAXIMIZED) {
|
if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_WINDOWED) return "Windowed"
|
||||||
window.showMaximized()
|
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_MAXIMIZED) return "Maximized"
|
||||||
}
|
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_FULLSCREEN) return "FullScreen"
|
||||||
else if (StreamingPreferences.uiDisplayMode == StreamingPreferences.UI_FULLSCREEN) {
|
|
||||||
window.showFullScreen()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
window.show()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
window.showFullScreen()
|
return "FullScreen"
|
||||||
}
|
|
||||||
|
|
||||||
// Display any modal dialogs for configuration warnings
|
|
||||||
if (SystemProperties.isWow64) {
|
|
||||||
wow64Dialog.open()
|
|
||||||
}
|
|
||||||
else if (!SystemProperties.hasHardwareAcceleration) {
|
|
||||||
if (SystemProperties.isRunningXWayland) {
|
|
||||||
xWaylandDialog.open()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
noHwDecoderDialog.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SystemProperties.unmappedGamepads) {
|
|
||||||
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
|
||||||
unmappedGamepadDialog.open()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +156,41 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property bool initialized: false
|
||||||
|
|
||||||
|
// BUG: Using onAfterSynchronizing: here causes very strange
|
||||||
|
// failures on Linux. Many shaders fail to compile and we
|
||||||
|
// eventually segfault deep inside the Qt OpenGL code.
|
||||||
|
onAfterRendering: {
|
||||||
|
// We use this callback to trigger dialog display because
|
||||||
|
// it only happens once the window is fully constructed.
|
||||||
|
// Doing it earlier can lead to the dialog appearing behind
|
||||||
|
// the window or otherwise without input focus.
|
||||||
|
if (!initialized) {
|
||||||
|
// Set initialized before calling anything else, because
|
||||||
|
// pumping the event loop can cause us to get another
|
||||||
|
// onAfterRendering call and potentially reenter this code.
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
if (SystemProperties.isWow64) {
|
||||||
|
wow64Dialog.open()
|
||||||
|
}
|
||||||
|
else if (!SystemProperties.hasHardwareAcceleration) {
|
||||||
|
if (SystemProperties.isRunningXWayland) {
|
||||||
|
xWaylandDialog.open()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
noHwDecoderDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SystemProperties.unmappedGamepads) {
|
||||||
|
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
||||||
|
unmappedGamepadDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for lack of instanceof in Qt 5.9.
|
// Workaround for lack of instanceof in Qt 5.9.
|
||||||
//
|
//
|
||||||
// Based on https://stackoverflow.com/questions/13923794/how-to-do-a-is-a-typeof-or-instanceof-in-qml
|
// Based on https://stackoverflow.com/questions/13923794/how-to-do-a-is-a-typeof-or-instanceof-in-qml
|
||||||
@@ -270,7 +283,7 @@ ApplicationWindow {
|
|||||||
visible: SystemProperties.hasBrowser &&
|
visible: SystemProperties.hasBrowser &&
|
||||||
qmltypeof(stackView.currentItem, "SettingsView")
|
qmltypeof(stackView.currentItem, "SettingsView")
|
||||||
|
|
||||||
iconSource: "qrc:/res/discord.svg"
|
iconSource: "qrc:/res/Discord-Logo-White.svg"
|
||||||
|
|
||||||
ToolTip.delay: 1000
|
ToolTip.delay: 1000
|
||||||
ToolTip.timeout: 3000
|
ToolTip.timeout: 3000
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
|
#include "settings/streamingpreferences.h"
|
||||||
#include "settings/mappingmanager.h"
|
#include "settings/mappingmanager.h"
|
||||||
|
|
||||||
#define AXIS_NAVIGATION_REPEAT_DELAY 150
|
#define AXIS_NAVIGATION_REPEAT_DELAY 150
|
||||||
|
|
||||||
SdlGamepadKeyNavigation::SdlGamepadKeyNavigation(StreamingPreferences* prefs)
|
SdlGamepadKeyNavigation::SdlGamepadKeyNavigation()
|
||||||
: m_Prefs(prefs),
|
: m_Enabled(false),
|
||||||
m_Enabled(false),
|
|
||||||
m_UiNavMode(false),
|
m_UiNavMode(false),
|
||||||
m_FirstPoll(false),
|
m_FirstPoll(false),
|
||||||
m_LastAxisNavigationEventTime(0)
|
m_LastAxisNavigationEventTime(0)
|
||||||
@@ -93,6 +93,7 @@ void SdlGamepadKeyNavigation::disable()
|
|||||||
void SdlGamepadKeyNavigation::onPollingTimerFired()
|
void SdlGamepadKeyNavigation::onPollingTimerFired()
|
||||||
{
|
{
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
StreamingPreferences prefs;
|
||||||
|
|
||||||
// Discard any pending button events on the first poll to avoid picking up
|
// Discard any pending button events on the first poll to avoid picking up
|
||||||
// stale input data from the stream session (like the quit combo).
|
// stale input data from the stream session (like the quit combo).
|
||||||
@@ -120,7 +121,7 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
|
|||||||
QEvent::Type::KeyPress : QEvent::Type::KeyRelease;
|
QEvent::Type::KeyPress : QEvent::Type::KeyRelease;
|
||||||
|
|
||||||
// Swap face buttons if needed
|
// Swap face buttons if needed
|
||||||
if (m_Prefs->swapFaceButtons) {
|
if (prefs.swapFaceButtons) {
|
||||||
switch (event.cbutton.button) {
|
switch (event.cbutton.button) {
|
||||||
case SDL_CONTROLLER_BUTTON_A:
|
case SDL_CONTROLLER_BUTTON_A:
|
||||||
event.cbutton.button = SDL_CONTROLLER_BUTTON_B;
|
event.cbutton.button = SDL_CONTROLLER_BUTTON_B;
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "settings/streamingpreferences.h"
|
|
||||||
|
|
||||||
class SdlGamepadKeyNavigation : public QObject
|
class SdlGamepadKeyNavigation : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SdlGamepadKeyNavigation(StreamingPreferences* prefs);
|
SdlGamepadKeyNavigation();
|
||||||
|
|
||||||
~SdlGamepadKeyNavigation();
|
~SdlGamepadKeyNavigation();
|
||||||
|
|
||||||
@@ -31,7 +29,6 @@ private slots:
|
|||||||
void onPollingTimerFired();
|
void onPollingTimerFired();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StreamingPreferences* m_Prefs;
|
|
||||||
QTimer* m_PollingTimer;
|
QTimer* m_PollingTimer;
|
||||||
QList<SDL_GameController*> m_Gamepads;
|
QList<SDL_GameController*> m_Gamepads;
|
||||||
bool m_Enabled;
|
bool m_Enabled;
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+244
-378
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+188
-322
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+215
-349
File diff suppressed because it is too large.
Load diff
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+182
-316
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+185
-319
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+234
-368
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+268
-402
File diff suppressed because it is too large.
Load diff
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+192
-326
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+194
-328
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+181
-315
File diff suppressed because it is too large.
Load diff
+37
-116
@@ -10,8 +10,7 @@
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QTemporaryFile>
|
#include <QFile>
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
// Don't let SDL hook our main function, since Qt is already
|
// Don't let SDL hook our main function, since Qt is already
|
||||||
// doing the same thing. This needs to be before any headers
|
// doing the same thing. This needs to be before any headers
|
||||||
@@ -25,9 +24,6 @@
|
|||||||
|
|
||||||
#if defined(Q_OS_WIN32)
|
#if defined(Q_OS_WIN32)
|
||||||
#include "antihookingprotection.h"
|
#include "antihookingprotection.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <Windows.h>
|
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX)
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -48,24 +44,26 @@
|
|||||||
#include "settings/streamingpreferences.h"
|
#include "settings/streamingpreferences.h"
|
||||||
#include "gui/sdlgamepadkeynavigation.h"
|
#include "gui/sdlgamepadkeynavigation.h"
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32)
|
#if !defined(QT_DEBUG) && defined(Q_OS_WIN32)
|
||||||
#define IS_UNSPECIFIED_HANDLE(x) ((x) == INVALID_HANDLE_VALUE || (x) == NULL)
|
// Log to file for release Windows builds
|
||||||
|
#define USE_CUSTOM_LOGGER
|
||||||
// Log to file or console dynamically for Windows builds
|
|
||||||
#define LOG_TO_FILE
|
#define LOG_TO_FILE
|
||||||
|
#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
|
||||||
|
// Use stdout logger on all Linux/BSD builds
|
||||||
|
#define USE_CUSTOM_LOGGER
|
||||||
#elif !defined(QT_DEBUG) && defined(Q_OS_DARWIN)
|
#elif !defined(QT_DEBUG) && defined(Q_OS_DARWIN)
|
||||||
// Log to file for release Mac builds
|
// Log to file for release Mac builds
|
||||||
|
#define USE_CUSTOM_LOGGER
|
||||||
#define LOG_TO_FILE
|
#define LOG_TO_FILE
|
||||||
#else
|
#else
|
||||||
// Log to console for debug Mac builds
|
// For debug Windows and Mac builds, use default logger
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_CUSTOM_LOGGER
|
||||||
static QElapsedTimer s_LoggerTime;
|
static QElapsedTimer s_LoggerTime;
|
||||||
static QTextStream s_LoggerStream(stderr);
|
static QTextStream s_LoggerStream(stdout);
|
||||||
static QMutex s_LoggerLock;
|
static QMutex s_LoggerLock;
|
||||||
static bool s_SuppressVerboseOutput;
|
static bool s_SuppressVerboseOutput;
|
||||||
static QRegularExpression k_RikeyRegex("&rikey=\\w+");
|
|
||||||
static QRegularExpression k_RikeyIdRegex("&rikeyid=[\\d-]+");
|
|
||||||
#ifdef LOG_TO_FILE
|
#ifdef LOG_TO_FILE
|
||||||
// Max log file size of 10 MB
|
// Max log file size of 10 MB
|
||||||
#define MAX_LOG_SIZE_BYTES (10 * 1024 * 1024)
|
#define MAX_LOG_SIZE_BYTES (10 * 1024 * 1024)
|
||||||
@@ -78,22 +76,6 @@ void logToLoggerStream(QString& message)
|
|||||||
{
|
{
|
||||||
QMutexLocker lock(&s_LoggerLock);
|
QMutexLocker lock(&s_LoggerLock);
|
||||||
|
|
||||||
#if defined(QT_DEBUG) && defined(Q_OS_WIN32)
|
|
||||||
// Output log messages to a debugger if attached
|
|
||||||
if (IsDebuggerPresent()) {
|
|
||||||
static QString lineBuffer;
|
|
||||||
lineBuffer += message;
|
|
||||||
if (message.endsWith('\n')) {
|
|
||||||
OutputDebugStringW(lineBuffer.toStdWString().c_str());
|
|
||||||
lineBuffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Strip session encryption keys and IVs from the logs
|
|
||||||
message.replace(k_RikeyRegex, "&rikey=REDACTED");
|
|
||||||
message.replace(k_RikeyIdRegex, "&rikeyid=REDACTED");
|
|
||||||
|
|
||||||
#ifdef LOG_TO_FILE
|
#ifdef LOG_TO_FILE
|
||||||
if (s_LogLimitReached) {
|
if (s_LogLimitReached) {
|
||||||
return;
|
return;
|
||||||
@@ -234,6 +216,8 @@ void ffmpegLogToDiskHandler(void* ptr, int level, const char* fmt, va_list vl)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -322,25 +306,13 @@ int main(int argc, char *argv[])
|
|||||||
qputenv("QML_DISK_CACHE_PATH", Path::getQmlCacheDir().toUtf8());
|
qputenv("QML_DISK_CACHE_PATH", Path::getQmlCacheDir().toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef USE_CUSTOM_LOGGER
|
||||||
// Grab the original std handles before we potentially redirect them later
|
|
||||||
HANDLE oldConOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
HANDLE oldConErr = GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef LOG_TO_FILE
|
#ifdef LOG_TO_FILE
|
||||||
QDir tempDir(Path::getLogDir());
|
QDir tempDir(Path::getLogDir());
|
||||||
|
s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch())));
|
||||||
#ifdef Q_OS_WIN32
|
if (s_LoggerFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
// Only log to a file if the user didn't redirect stderr somewhere else
|
QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl;
|
||||||
if (IS_UNSPECIFIED_HANDLE(oldConErr))
|
s_LoggerStream.setDevice(s_LoggerFile);
|
||||||
#endif
|
|
||||||
{
|
|
||||||
s_LoggerFile = new QFile(tempDir.filePath(QString("Moonlight-%1.log").arg(QDateTime::currentSecsSinceEpoch())));
|
|
||||||
if (s_LoggerFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
||||||
QTextStream(stderr) << "Redirecting log output to " << s_LoggerFile->fileName() << Qt::endl;
|
|
||||||
s_LoggerStream.setDevice(s_LoggerFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -352,6 +324,8 @@ int main(int argc, char *argv[])
|
|||||||
av_log_set_callback(ffmpegLogToDiskHandler);
|
av_log_set_callback(ffmpegLogToDiskHandler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
// Create a crash dump when we crash on Windows
|
// Create a crash dump when we crash on Windows
|
||||||
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
||||||
@@ -377,11 +351,6 @@ int main(int argc, char *argv[])
|
|||||||
SSL_free(nullptr);
|
SSL_free(nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We keep this at function scope to ensure it stays around while we're running,
|
|
||||||
// becaue the Qt QPA will need to read it. Since the temporary file is only
|
|
||||||
// created when open() is called, this doesn't do any harm for other platforms.
|
|
||||||
QTemporaryFile eglfsConfigFile("eglfs_override_XXXXXX.conf");
|
|
||||||
|
|
||||||
// Avoid using High DPI on EGLFS. It breaks font rendering.
|
// Avoid using High DPI on EGLFS. It breaks font rendering.
|
||||||
// https://bugreports.qt.io/browse/QTBUG-64377
|
// https://bugreports.qt.io/browse/QTBUG-64377
|
||||||
//
|
//
|
||||||
@@ -415,17 +384,6 @@ int main(int argc, char *argv[])
|
|||||||
qWarning() << "Unable to find a KMSDRM display device!";
|
qWarning() << "Unable to find a KMSDRM display device!";
|
||||||
qWarning() << "On the Raspberry Pi, you must enable the 'fake KMS' driver in raspi-config to use Moonlight outside of the GUI environment.";
|
qWarning() << "On the Raspberry Pi, you must enable the 'fake KMS' driver in raspi-config to use Moonlight outside of the GUI environment.";
|
||||||
}
|
}
|
||||||
else if (!qEnvironmentVariableIsSet("QT_QPA_EGLFS_KMS_CONFIG")) {
|
|
||||||
// HACK: Remove this when Qt is fixed to properly check for display support before picking a card
|
|
||||||
QString cardOverride = WMUtils::getDrmCardOverride();
|
|
||||||
if (!cardOverride.isEmpty()) {
|
|
||||||
if (eglfsConfigFile.open()) {
|
|
||||||
qInfo() << "Overriding default Qt EGLFS card selection to" << cardOverride;
|
|
||||||
QTextStream(&eglfsConfigFile) << "{ \"device\": \"" << cardOverride << "\" }";
|
|
||||||
qputenv("QT_QPA_EGLFS_KMS_CONFIG", eglfsConfigFile.fileName().toUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EGLFS uses OpenGLES 2.0, so we will too. Some embedded platforms may not
|
// EGLFS uses OpenGLES 2.0, so we will too. Some embedded platforms may not
|
||||||
@@ -435,18 +393,9 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(Q_PROCESSOR_X86) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL)
|
|
||||||
// Some ARM and RISC-V embedded devices don't have working GLX which can cause
|
|
||||||
// SDL to fail to find a working OpenGL implementation at all. Let's force EGL
|
|
||||||
// on non-x86 platforms, since GLX is deprecated anyway.
|
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
// This avoids using the default keychain for SSL, which may cause
|
// This avoids using the default keychain for SSL, which may cause
|
||||||
// password prompts on macOS.
|
// password prompts on macOS.
|
||||||
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
|
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if defined(Q_OS_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
if (!qEnvironmentVariableIsSet("QT_OPENGL")) {
|
if (!qEnvironmentVariableIsSet("QT_OPENGL")) {
|
||||||
@@ -469,12 +418,6 @@ int main(int argc, char *argv[])
|
|||||||
qputenv("QSG_RENDER_LOOP", "basic");
|
qputenv("QSG_RENDER_LOOP", "basic");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_DARWIN) && defined(QT_DEBUG)
|
|
||||||
// Enable Metal valiation for debug builds
|
|
||||||
qputenv("MTL_DEBUG_LAYER", "1");
|
|
||||||
qputenv("MTL_SHADER_VALIDATION", "1");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We don't want system proxies to apply to us
|
// We don't want system proxies to apply to us
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||||
|
|
||||||
@@ -553,13 +496,6 @@ int main(int argc, char *argv[])
|
|||||||
// SDL doing it for us behind our backs.
|
// SDL doing it for us behind our backs.
|
||||||
SDL_SetHint("SDL_MOUSE_AUTO_CAPTURE", "0");
|
SDL_SetHint("SDL_MOUSE_AUTO_CAPTURE", "0");
|
||||||
|
|
||||||
// SDL will try to lock the mouse cursor on Wayland if it's not visible in order to
|
|
||||||
// support applications that assume they can warp the cursor (which isn't possible
|
|
||||||
// on Wayland). We don't want this behavior because it interferes with seamless mouse
|
|
||||||
// mode when toggling between windowed and fullscreen modes by unexpectedly locking
|
|
||||||
// the mouse cursor.
|
|
||||||
SDL_SetHint("SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP", "0");
|
|
||||||
|
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
// Allow thread naming using exceptions on debug builds. SDL doesn't use SEH
|
// Allow thread naming using exceptions on debug builds. SDL doesn't use SEH
|
||||||
// when throwing the exceptions, so we don't enable it for release builds out
|
// when throwing the exceptions, so we don't enable it for release builds out
|
||||||
@@ -569,34 +505,25 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
#ifndef STEAM_LINK
|
|
||||||
// Force use of the KMSDRM backend for SDL when using Qt platform plugins
|
|
||||||
// that directly draw to the display without a windowing system.
|
|
||||||
if (QGuiApplication::platformName() == "eglfs" || QGuiApplication::platformName() == "linuxfb") {
|
|
||||||
qputenv("SDL_VIDEODRIVER", "kmsdrm");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GlobalCommandLineParser parser;
|
GlobalCommandLineParser parser;
|
||||||
GlobalCommandLineParser::ParseResult commandLineParserResult = parser.parse(app.arguments());
|
GlobalCommandLineParser::ParseResult commandLineParserResult = parser.parse(app.arguments());
|
||||||
switch (commandLineParserResult) {
|
switch (commandLineParserResult) {
|
||||||
case GlobalCommandLineParser::ListRequested:
|
case GlobalCommandLineParser::ListRequested:
|
||||||
|
#ifdef USE_CUSTOM_LOGGER
|
||||||
// Don't log to the console since it will jumble the command output
|
// Don't log to the console since it will jumble the command output
|
||||||
s_SuppressVerboseOutput = true;
|
s_SuppressVerboseOutput = true;
|
||||||
|
#endif
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
// If we don't have stdout or stderr handles (which will normally be the case
|
// Attach to the console to be able to print output.
|
||||||
// since we're a /SUBSYSTEM:WINDOWS app), attach to our parent console and use
|
// Since we're a /SUBSYSTEM:WINDOWS app, we won't be attached by default.
|
||||||
// that for stdout and stderr.
|
|
||||||
//
|
|
||||||
// If we do have stdout or stderr handles, that means the user has used standard
|
|
||||||
// handle redirection. In that case, we don't want to override those handles.
|
|
||||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
// If we didn't have an old stdout/stderr handle, use the new CONOUT$ handle
|
HANDLE conOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (IS_UNSPECIFIED_HANDLE(oldConOut)) {
|
if (conOut != INVALID_HANDLE_VALUE && conOut != NULL) {
|
||||||
freopen("CONOUT$", "w", stdout);
|
freopen("CONOUT$", "w", stdout);
|
||||||
setvbuf(stdout, NULL, _IONBF, 0);
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
}
|
}
|
||||||
if (IS_UNSPECIFIED_HANDLE(oldConErr)) {
|
HANDLE conErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
if (conErr != INVALID_HANDLE_VALUE && conErr != NULL) {
|
||||||
freopen("CONOUT$", "w", stderr);
|
freopen("CONOUT$", "w", stderr);
|
||||||
setvbuf(stderr, NULL, _IONBF, 0);
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
}
|
}
|
||||||
@@ -620,7 +547,8 @@ int main(int argc, char *argv[])
|
|||||||
runtimeVersion.major, runtimeVersion.minor, runtimeVersion.patch);
|
runtimeVersion.major, runtimeVersion.minor, runtimeVersion.patch);
|
||||||
|
|
||||||
// Apply the initial translation based on user preference
|
// Apply the initial translation based on user preference
|
||||||
StreamingPreferences::get()->retranslate();
|
StreamingPreferences prefs;
|
||||||
|
prefs.retranslate();
|
||||||
|
|
||||||
// Trickily declare the translation for dialog buttons
|
// Trickily declare the translation for dialog buttons
|
||||||
QCoreApplication::translate("QPlatformTheme", "&Yes");
|
QCoreApplication::translate("QPlatformTheme", "&Yes");
|
||||||
@@ -685,8 +613,8 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterUncreatableType<Session>("Session", 1, 0, "Session", "Session cannot be created from QML");
|
qmlRegisterUncreatableType<Session>("Session", 1, 0, "Session", "Session cannot be created from QML");
|
||||||
qmlRegisterSingletonType<ComputerManager>("ComputerManager", 1, 0,
|
qmlRegisterSingletonType<ComputerManager>("ComputerManager", 1, 0,
|
||||||
"ComputerManager",
|
"ComputerManager",
|
||||||
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
|
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
||||||
return new ComputerManager(StreamingPreferences::get(qmlEngine));
|
return new ComputerManager();
|
||||||
});
|
});
|
||||||
qmlRegisterSingletonType<AutoUpdateChecker>("AutoUpdateChecker", 1, 0,
|
qmlRegisterSingletonType<AutoUpdateChecker>("AutoUpdateChecker", 1, 0,
|
||||||
"AutoUpdateChecker",
|
"AutoUpdateChecker",
|
||||||
@@ -700,13 +628,13 @@ int main(int argc, char *argv[])
|
|||||||
});
|
});
|
||||||
qmlRegisterSingletonType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0,
|
qmlRegisterSingletonType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0,
|
||||||
"SdlGamepadKeyNavigation",
|
"SdlGamepadKeyNavigation",
|
||||||
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
|
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
||||||
return new SdlGamepadKeyNavigation(StreamingPreferences::get(qmlEngine));
|
return new SdlGamepadKeyNavigation();
|
||||||
});
|
});
|
||||||
qmlRegisterSingletonType<StreamingPreferences>("StreamingPreferences", 1, 0,
|
qmlRegisterSingletonType<StreamingPreferences>("StreamingPreferences", 1, 0,
|
||||||
"StreamingPreferences",
|
"StreamingPreferences",
|
||||||
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
|
[](QQmlEngine* qmlEngine, QJSEngine*) -> QObject* {
|
||||||
return StreamingPreferences::get(qmlEngine);
|
return new StreamingPreferences(qmlEngine);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the identity manager on the main thread
|
// Create the identity manager on the main thread
|
||||||
@@ -737,7 +665,7 @@ int main(int argc, char *argv[])
|
|||||||
case GlobalCommandLineParser::StreamRequested:
|
case GlobalCommandLineParser::StreamRequested:
|
||||||
{
|
{
|
||||||
initialView = "qrc:/gui/CliStartStreamSegue.qml";
|
initialView = "qrc:/gui/CliStartStreamSegue.qml";
|
||||||
StreamingPreferences* preferences = StreamingPreferences::get();
|
StreamingPreferences* preferences = new StreamingPreferences(&app);
|
||||||
StreamCommandLineParser streamParser;
|
StreamCommandLineParser streamParser;
|
||||||
streamParser.parse(app.arguments(), preferences);
|
streamParser.parse(app.arguments(), preferences);
|
||||||
QString host = streamParser.getHost();
|
QString host = streamParser.getHost();
|
||||||
@@ -769,7 +697,7 @@ int main(int argc, char *argv[])
|
|||||||
ListCommandLineParser listParser;
|
ListCommandLineParser listParser;
|
||||||
listParser.parse(app.arguments());
|
listParser.parse(app.arguments());
|
||||||
auto launcher = new CliListApps::Launcher(listParser.getHost(), listParser, &app);
|
auto launcher = new CliListApps::Launcher(listParser.getHost(), listParser, &app);
|
||||||
launcher->execute(new ComputerManager(StreamingPreferences::get()));
|
launcher->execute(new ComputerManager(&app));
|
||||||
hasGUI = false;
|
hasGUI = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -790,12 +718,5 @@ int main(int argc, char *argv[])
|
|||||||
// sometimes freezing and blocking process exit.
|
// sometimes freezing and blocking process exit.
|
||||||
QThreadPool::globalInstance()->waitForDone(30000);
|
QThreadPool::globalInstance()->waitForDone(30000);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
// Without an explicit flush, console redirection for the list command
|
|
||||||
// doesn't work reliably (sometimes the target file contains no text).
|
|
||||||
fflush(stderr);
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 245 240" fill="white"><path class="st0" d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"/><path class="st0" d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="white"><path d="M20.317 4.3698a19.7913 19.7913 0 0 0-4.8851-1.5152.0741.0741 0 0 0-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 0 0-.0785-.037 19.7363 19.7363 0 0 0-4.8852 1.515.0699.0699 0 0 0-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 0 0 .0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 0 0 .0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 0 0-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 0 1-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 0 1 .0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 0 1 .0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 0 1-.0066.1276 12.2986 12.2986 0 0 1-1.873.8914.0766.0766 0 0 0-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 0 0 .0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 0 0 .0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 0 0-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
+1
-6
@@ -17,7 +17,7 @@
|
|||||||
<file>res/baseline-help_outline-24px.svg</file>
|
<file>res/baseline-help_outline-24px.svg</file>
|
||||||
<file>res/baseline-error_outline-24px.svg</file>
|
<file>res/baseline-error_outline-24px.svg</file>
|
||||||
<file>res/baseline-check_circle_outline-24px.svg</file>
|
<file>res/baseline-check_circle_outline-24px.svg</file>
|
||||||
<file>res/discord.svg</file>
|
<file>res/Discord-Logo-White.svg</file>
|
||||||
<file>languages/qml_de.ts</file>
|
<file>languages/qml_de.ts</file>
|
||||||
<file>languages/qml_de.qm</file>
|
<file>languages/qml_de.qm</file>
|
||||||
<file>languages/qml_fr.ts</file>
|
<file>languages/qml_fr.ts</file>
|
||||||
@@ -64,10 +64,6 @@
|
|||||||
<file>languages/qml_cs.qm</file>
|
<file>languages/qml_cs.qm</file>
|
||||||
<file>languages/qml_he.ts</file>
|
<file>languages/qml_he.ts</file>
|
||||||
<file>languages/qml_he.qm</file>
|
<file>languages/qml_he.qm</file>
|
||||||
<file>languages/qml_ckb.ts</file>
|
|
||||||
<file>languages/qml_ckb.qm</file>
|
|
||||||
<file>languages/qml_lt.ts</file>
|
|
||||||
<file>languages/qml_lt.qm</file>
|
|
||||||
<!-- Don't include pt_BR until it is more complete -->
|
<!-- Don't include pt_BR until it is more complete -->
|
||||||
<!--file>languages/qml_pt_BR.qm</file-->
|
<!--file>languages/qml_pt_BR.qm</file-->
|
||||||
<!--file>languages/qml_pt_BR.ts</file-->
|
<!--file>languages/qml_pt_BR.ts</file-->
|
||||||
@@ -86,6 +82,5 @@
|
|||||||
<file alias="d3d11_genyuv_pixel.fxc">shaders/d3d11_genyuv_pixel.fxc</file>
|
<file alias="d3d11_genyuv_pixel.fxc">shaders/d3d11_genyuv_pixel.fxc</file>
|
||||||
<file alias="d3d11_bt601lim_pixel.fxc">shaders/d3d11_bt601lim_pixel.fxc</file>
|
<file alias="d3d11_bt601lim_pixel.fxc">shaders/d3d11_bt601lim_pixel.fxc</file>
|
||||||
<file alias="d3d11_bt2020lim_pixel.fxc">shaders/d3d11_bt2020lim_pixel.fxc</file>
|
<file alias="d3d11_bt2020lim_pixel.fxc">shaders/d3d11_bt2020lim_pixel.fxc</file>
|
||||||
<file alias="vt_renderer.metal">shaders/vt_renderer.metal</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QReadWriteLock>
|
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
@@ -38,7 +37,6 @@
|
|||||||
#define SER_DEFAULTVER "defaultver"
|
#define SER_DEFAULTVER "defaultver"
|
||||||
#define SER_PACKETSIZE "packetsize"
|
#define SER_PACKETSIZE "packetsize"
|
||||||
#define SER_DETECTNETBLOCKING "detectnetblocking"
|
#define SER_DETECTNETBLOCKING "detectnetblocking"
|
||||||
#define SER_SHOWPERFOVERLAY "showperfoverlay"
|
|
||||||
#define SER_SWAPMOUSEBUTTONS "swapmousebuttons"
|
#define SER_SWAPMOUSEBUTTONS "swapmousebuttons"
|
||||||
#define SER_MUTEONFOCUSLOSS "muteonfocusloss"
|
#define SER_MUTEONFOCUSLOSS "muteonfocusloss"
|
||||||
#define SER_BACKGROUNDGAMEPAD "backgroundgamepad"
|
#define SER_BACKGROUNDGAMEPAD "backgroundgamepad"
|
||||||
@@ -50,50 +48,18 @@
|
|||||||
|
|
||||||
#define CURRENT_DEFAULT_VER 2
|
#define CURRENT_DEFAULT_VER 2
|
||||||
|
|
||||||
static StreamingPreferences* s_GlobalPrefs;
|
StreamingPreferences::StreamingPreferences(QObject *parent)
|
||||||
static QReadWriteLock s_GlobalPrefsLock;
|
: QObject(parent),
|
||||||
|
m_QmlEngine(nullptr)
|
||||||
StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine)
|
|
||||||
: m_QmlEngine(qmlEngine)
|
|
||||||
{
|
{
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamingPreferences* StreamingPreferences::get(QQmlEngine *qmlEngine)
|
StreamingPreferences::StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_QmlEngine(qmlEngine)
|
||||||
{
|
{
|
||||||
{
|
reload();
|
||||||
QReadLocker readGuard(&s_GlobalPrefsLock);
|
|
||||||
|
|
||||||
// If we have a preference object and it's associated with a QML engine or
|
|
||||||
// if the caller didn't specify a QML engine, return the existing object.
|
|
||||||
if (s_GlobalPrefs && (s_GlobalPrefs->m_QmlEngine || !qmlEngine)) {
|
|
||||||
// The lifetime logic here relies on the QML engine also being a singleton.
|
|
||||||
Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine);
|
|
||||||
return s_GlobalPrefs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
QWriteLocker writeGuard(&s_GlobalPrefsLock);
|
|
||||||
|
|
||||||
// If we already have an preference object but the QML engine is now available,
|
|
||||||
// associate the QML engine with the preferences.
|
|
||||||
if (s_GlobalPrefs) {
|
|
||||||
if (!s_GlobalPrefs->m_QmlEngine) {
|
|
||||||
s_GlobalPrefs->m_QmlEngine = qmlEngine;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// We could reach this codepath if another thread raced with us
|
|
||||||
// and created the object while we were outside the pref lock.
|
|
||||||
Q_ASSERT(!qmlEngine || s_GlobalPrefs->m_QmlEngine == qmlEngine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s_GlobalPrefs = new StreamingPreferences(qmlEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s_GlobalPrefs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamingPreferences::reload()
|
void StreamingPreferences::reload()
|
||||||
@@ -131,7 +97,6 @@ void StreamingPreferences::reload()
|
|||||||
richPresence = settings.value(SER_RICHPRESENCE, true).toBool();
|
richPresence = settings.value(SER_RICHPRESENCE, true).toBool();
|
||||||
gamepadMouse = settings.value(SER_GAMEPADMOUSE, true).toBool();
|
gamepadMouse = settings.value(SER_GAMEPADMOUSE, true).toBool();
|
||||||
detectNetworkBlocking = settings.value(SER_DETECTNETBLOCKING, true).toBool();
|
detectNetworkBlocking = settings.value(SER_DETECTNETBLOCKING, true).toBool();
|
||||||
showPerformanceOverlay = settings.value(SER_SHOWPERFOVERLAY, false).toBool();
|
|
||||||
packetSize = settings.value(SER_PACKETSIZE, 0).toInt();
|
packetSize = settings.value(SER_PACKETSIZE, 0).toInt();
|
||||||
swapMouseButtons = settings.value(SER_SWAPMOUSEBUTTONS, false).toBool();
|
swapMouseButtons = settings.value(SER_SWAPMOUSEBUTTONS, false).toBool();
|
||||||
muteOnFocusLoss = settings.value(SER_MUTEONFOCUSLOSS, false).toBool();
|
muteOnFocusLoss = settings.value(SER_MUTEONFOCUSLOSS, false).toBool();
|
||||||
@@ -287,10 +252,6 @@ QString StreamingPreferences::getSuffixFromLanguage(StreamingPreferences::Langua
|
|||||||
return "cs";
|
return "cs";
|
||||||
case LANG_HE:
|
case LANG_HE:
|
||||||
return "he";
|
return "he";
|
||||||
case LANG_CKB:
|
|
||||||
return "ckb";
|
|
||||||
case LANG_LT:
|
|
||||||
return "lt";
|
|
||||||
case LANG_AUTO:
|
case LANG_AUTO:
|
||||||
default:
|
default:
|
||||||
return QLocale::system().name();
|
return QLocale::system().name();
|
||||||
@@ -319,7 +280,6 @@ void StreamingPreferences::save()
|
|||||||
settings.setValue(SER_GAMEPADMOUSE, gamepadMouse);
|
settings.setValue(SER_GAMEPADMOUSE, gamepadMouse);
|
||||||
settings.setValue(SER_PACKETSIZE, packetSize);
|
settings.setValue(SER_PACKETSIZE, packetSize);
|
||||||
settings.setValue(SER_DETECTNETBLOCKING, detectNetworkBlocking);
|
settings.setValue(SER_DETECTNETBLOCKING, detectNetworkBlocking);
|
||||||
settings.setValue(SER_SHOWPERFOVERLAY, showPerformanceOverlay);
|
|
||||||
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
|
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
|
||||||
settings.setValue(SER_HDR, enableHdr);
|
settings.setValue(SER_HDR, enableHdr);
|
||||||
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));
|
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class StreamingPreferences : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static StreamingPreferences* get(QQmlEngine *qmlEngine = nullptr);
|
StreamingPreferences(QObject *parent = nullptr);
|
||||||
|
StreamingPreferences(QQmlEngine *qmlEngine, QObject *parent = nullptr);
|
||||||
|
|
||||||
Q_INVOKABLE static int
|
Q_INVOKABLE static int
|
||||||
getDefaultBitrate(int width, int height, int fps);
|
getDefaultBitrate(int width, int height, int fps);
|
||||||
@@ -91,8 +92,6 @@ public:
|
|||||||
LANG_PL,
|
LANG_PL,
|
||||||
LANG_CS,
|
LANG_CS,
|
||||||
LANG_HE,
|
LANG_HE,
|
||||||
LANG_CKB,
|
|
||||||
LANG_LT,
|
|
||||||
};
|
};
|
||||||
Q_ENUM(Language);
|
Q_ENUM(Language);
|
||||||
|
|
||||||
@@ -121,7 +120,6 @@ public:
|
|||||||
Q_PROPERTY(bool richPresence MEMBER richPresence NOTIFY richPresenceChanged)
|
Q_PROPERTY(bool richPresence MEMBER richPresence NOTIFY richPresenceChanged)
|
||||||
Q_PROPERTY(bool gamepadMouse MEMBER gamepadMouse NOTIFY gamepadMouseChanged)
|
Q_PROPERTY(bool gamepadMouse MEMBER gamepadMouse NOTIFY gamepadMouseChanged)
|
||||||
Q_PROPERTY(bool detectNetworkBlocking MEMBER detectNetworkBlocking NOTIFY detectNetworkBlockingChanged)
|
Q_PROPERTY(bool detectNetworkBlocking MEMBER detectNetworkBlocking NOTIFY detectNetworkBlockingChanged)
|
||||||
Q_PROPERTY(bool showPerformanceOverlay MEMBER showPerformanceOverlay NOTIFY showPerformanceOverlayChanged)
|
|
||||||
Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged)
|
Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged)
|
||||||
Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged)
|
Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged)
|
||||||
Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged)
|
Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged)
|
||||||
@@ -158,7 +156,6 @@ public:
|
|||||||
bool richPresence;
|
bool richPresence;
|
||||||
bool gamepadMouse;
|
bool gamepadMouse;
|
||||||
bool detectNetworkBlocking;
|
bool detectNetworkBlocking;
|
||||||
bool showPerformanceOverlay;
|
|
||||||
bool swapMouseButtons;
|
bool swapMouseButtons;
|
||||||
bool muteOnFocusLoss;
|
bool muteOnFocusLoss;
|
||||||
bool backgroundGamepad;
|
bool backgroundGamepad;
|
||||||
@@ -199,7 +196,6 @@ signals:
|
|||||||
void richPresenceChanged();
|
void richPresenceChanged();
|
||||||
void gamepadMouseChanged();
|
void gamepadMouseChanged();
|
||||||
void detectNetworkBlockingChanged();
|
void detectNetworkBlockingChanged();
|
||||||
void showPerformanceOverlayChanged();
|
|
||||||
void mouseButtonsChanged();
|
void mouseButtonsChanged();
|
||||||
void muteOnFocusLossChanged();
|
void muteOnFocusLossChanged();
|
||||||
void backgroundGamepadChanged();
|
void backgroundGamepadChanged();
|
||||||
@@ -210,8 +206,6 @@ signals:
|
|||||||
void languageChanged();
|
void languageChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit StreamingPreferences(QQmlEngine *qmlEngine);
|
|
||||||
|
|
||||||
QString getSuffixFromLanguage(Language lang);
|
QString getSuffixFromLanguage(Language lang);
|
||||||
|
|
||||||
QQmlEngine* m_QmlEngine;
|
QQmlEngine* m_QmlEngine;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
#include "d3d11_video_pixel_start.hlsli"
|
#include "d3d11_video_pixel_start.hlsli"
|
||||||
|
|
||||||
cbuffer CSC_CONST_BUF : register(b1)
|
cbuffer CSC_CONST_BUF : register(b0)
|
||||||
{
|
{
|
||||||
min16float3x3 cscMatrix;
|
min16float3x3 cscMatrix;
|
||||||
min16float3 offsets;
|
min16float3 offsets;
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
min16float4 main(ShaderInput input) : SV_TARGET
|
min16float4 main(ShaderInput input) : SV_TARGET
|
||||||
{
|
{
|
||||||
// Clamp the chrominance texcoords to avoid sampling the row of texels adjacent to the alignment padding
|
|
||||||
min16float3 yuv = min16float3(luminancePlane.Sample(theSampler, input.tex),
|
min16float3 yuv = min16float3(luminancePlane.Sample(theSampler, input.tex),
|
||||||
chrominancePlane.Sample(theSampler, min(input.tex, chromaTexMax.rg)));
|
chrominancePlane.Sample(theSampler, input.tex));
|
||||||
|
|
||||||
// Subtract the YUV offset for limited vs full range
|
// Subtract the YUV offset for limited vs full range
|
||||||
yuv -= offsets;
|
yuv -= offsets;
|
||||||
|
|||||||
@@ -6,9 +6,4 @@ struct ShaderInput
|
|||||||
{
|
{
|
||||||
float4 pos : SV_POSITION;
|
float4 pos : SV_POSITION;
|
||||||
float2 tex : TEXCOORD0;
|
float2 tex : TEXCOORD0;
|
||||||
};
|
};
|
||||||
|
|
||||||
cbuffer ChromaLimitBuf : register(b0)
|
|
||||||
{
|
|
||||||
min16float3 chromaTexMax;
|
|
||||||
};
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using namespace metal;
|
|
||||||
|
|
||||||
struct Vertex
|
|
||||||
{
|
|
||||||
float4 position [[ position ]];
|
|
||||||
float2 texCoords;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CscParams
|
|
||||||
{
|
|
||||||
float3 matrix[3];
|
|
||||||
float3 offsets;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::linear);
|
|
||||||
|
|
||||||
vertex Vertex vs_draw(constant Vertex *vertices [[ buffer(0) ]], uint id [[ vertex_id ]])
|
|
||||||
{
|
|
||||||
return vertices[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment float4 ps_draw_biplanar(Vertex v [[ stage_in ]],
|
|
||||||
constant CscParams &cscParams [[ buffer(0) ]],
|
|
||||||
texture2d<float> luminancePlane [[ texture(0) ]],
|
|
||||||
texture2d<float> chrominancePlane [[ texture(1) ]])
|
|
||||||
{
|
|
||||||
float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r, chrominancePlane.sample(s, v.texCoords).rg);
|
|
||||||
yuv -= cscParams.offsets;
|
|
||||||
|
|
||||||
float3 rgb;
|
|
||||||
rgb.r = dot(yuv, cscParams.matrix[0]);
|
|
||||||
rgb.g = dot(yuv, cscParams.matrix[1]);
|
|
||||||
rgb.b = dot(yuv, cscParams.matrix[2]);
|
|
||||||
return float4(rgb, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment float4 ps_draw_rgb(Vertex v [[ stage_in ]],
|
|
||||||
texture2d<float> rgbTexture [[ texture(0) ]])
|
|
||||||
{
|
|
||||||
return rgbTexture.sample(s, v.texCoords);
|
|
||||||
}
|
|
||||||
@@ -64,48 +64,6 @@ IAudioRenderer* Session::createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATI
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::initializeAudioRenderer()
|
|
||||||
{
|
|
||||||
int error;
|
|
||||||
|
|
||||||
SDL_assert(m_OriginalAudioConfig.channelCount > 0);
|
|
||||||
SDL_assert(m_AudioRenderer == nullptr);
|
|
||||||
SDL_assert(m_OpusDecoder == nullptr);
|
|
||||||
|
|
||||||
m_AudioRenderer = createAudioRenderer(&m_OriginalAudioConfig);
|
|
||||||
|
|
||||||
// We may be unable to create an audio renderer right now
|
|
||||||
if (m_AudioRenderer == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
|
|
||||||
m_ActiveAudioConfig = m_OriginalAudioConfig;
|
|
||||||
m_AudioRenderer->remapChannels(&m_ActiveAudioConfig);
|
|
||||||
|
|
||||||
// Create the Opus decoder with the renderer's preferred channel mapping
|
|
||||||
m_OpusDecoder =
|
|
||||||
opus_multistream_decoder_create(m_ActiveAudioConfig.sampleRate,
|
|
||||||
m_ActiveAudioConfig.channelCount,
|
|
||||||
m_ActiveAudioConfig.streams,
|
|
||||||
m_ActiveAudioConfig.coupledStreams,
|
|
||||||
m_ActiveAudioConfig.mapping,
|
|
||||||
&error);
|
|
||||||
if (m_OpusDecoder == nullptr) {
|
|
||||||
delete m_AudioRenderer;
|
|
||||||
m_AudioRenderer = nullptr;
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Failed to create decoder: %d",
|
|
||||||
error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio stream has %d channels",
|
|
||||||
m_ActiveAudioConfig.channelCount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Session::getAudioRendererCapabilities(int audioConfiguration)
|
int Session::getAudioRendererCapabilities(int audioConfiguration)
|
||||||
{
|
{
|
||||||
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
|
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
|
||||||
@@ -150,8 +108,39 @@ int Session::arInit(int /* audioConfiguration */,
|
|||||||
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
||||||
void* /* arContext */, int /* arFlags */)
|
void* /* arContext */, int /* arFlags */)
|
||||||
{
|
{
|
||||||
SDL_memcpy(&s_ActiveSession->m_OriginalAudioConfig, opusConfig, sizeof(*opusConfig));
|
int error;
|
||||||
s_ActiveSession->initializeAudioRenderer();
|
|
||||||
|
SDL_memcpy(&s_ActiveSession->m_AudioConfig, opusConfig, sizeof(*opusConfig));
|
||||||
|
|
||||||
|
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
|
||||||
|
if (s_ActiveSession->m_AudioRenderer == nullptr) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
|
||||||
|
s_ActiveSession->m_AudioRenderer->remapChannels(&s_ActiveSession->m_AudioConfig);
|
||||||
|
|
||||||
|
// Create the Opus decoder with the renderer's preferred channel mapping
|
||||||
|
s_ActiveSession->m_OpusDecoder =
|
||||||
|
opus_multistream_decoder_create(s_ActiveSession->m_AudioConfig.sampleRate,
|
||||||
|
s_ActiveSession->m_AudioConfig.channelCount,
|
||||||
|
s_ActiveSession->m_AudioConfig.streams,
|
||||||
|
s_ActiveSession->m_AudioConfig.coupledStreams,
|
||||||
|
s_ActiveSession->m_AudioConfig.mapping,
|
||||||
|
&error);
|
||||||
|
if (s_ActiveSession->m_OpusDecoder == NULL) {
|
||||||
|
delete s_ActiveSession->m_AudioRenderer;
|
||||||
|
s_ActiveSession->m_AudioRenderer = nullptr;
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to create decoder: %d",
|
||||||
|
error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Audio stream has %d channels",
|
||||||
|
s_ActiveSession->m_AudioConfig.channelCount);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +194,7 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s_ActiveSession->m_AudioRenderer != nullptr) {
|
if (s_ActiveSession->m_AudioRenderer != nullptr) {
|
||||||
int desiredSize = sizeof(short) * s_ActiveSession->m_ActiveAudioConfig.samplesPerFrame * s_ActiveSession->m_ActiveAudioConfig.channelCount;
|
int desiredSize = sizeof(short) * s_ActiveSession->m_AudioConfig.samplesPerFrame * s_ActiveSession->m_AudioConfig.channelCount;
|
||||||
void* buffer = s_ActiveSession->m_AudioRenderer->getAudioBuffer(&desiredSize);
|
void* buffer = s_ActiveSession->m_AudioRenderer->getAudioBuffer(&desiredSize);
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -215,13 +204,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||||||
(unsigned char*)sampleData,
|
(unsigned char*)sampleData,
|
||||||
sampleLength,
|
sampleLength,
|
||||||
(short*)buffer,
|
(short*)buffer,
|
||||||
desiredSize / sizeof(short) / s_ActiveSession->m_ActiveAudioConfig.channelCount,
|
desiredSize / sizeof(short) / s_ActiveSession->m_AudioConfig.channelCount,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
// Update desiredSize with the number of bytes actually populated by the decoding operation
|
// Update desiredSize with the number of bytes actually populated by the decoding operation
|
||||||
if (samplesDecoded > 0) {
|
if (samplesDecoded > 0) {
|
||||||
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount));
|
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount));
|
||||||
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount;
|
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
desiredSize = 0;
|
desiredSize = 0;
|
||||||
@@ -231,9 +220,6 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Reinitializing audio renderer after failure");
|
"Reinitializing audio renderer after failure");
|
||||||
|
|
||||||
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
|
|
||||||
s_ActiveSession->m_OpusDecoder = nullptr;
|
|
||||||
|
|
||||||
delete s_ActiveSession->m_AudioRenderer;
|
delete s_ActiveSession->m_AudioRenderer;
|
||||||
s_ActiveSession->m_AudioRenderer = nullptr;
|
s_ActiveSession->m_AudioRenderer = nullptr;
|
||||||
}
|
}
|
||||||
@@ -248,13 +234,14 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||||||
// to drop samples to account for the time we've spent blocking audio rendering
|
// to drop samples to account for the time we've spent blocking audio rendering
|
||||||
// so we return to real-time playback and don't accumulate latency.
|
// so we return to real-time playback and don't accumulate latency.
|
||||||
Uint32 audioReinitStartTime = SDL_GetTicks();
|
Uint32 audioReinitStartTime = SDL_GetTicks();
|
||||||
if (s_ActiveSession->initializeAudioRenderer()) {
|
|
||||||
Uint32 audioReinitStopTime = SDL_GetTicks();
|
|
||||||
|
|
||||||
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
|
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio reinitialization took %d ms - starting drop window",
|
Uint32 audioReinitStopTime = SDL_GetTicks();
|
||||||
audioReinitStopTime - audioReinitStartTime);
|
|
||||||
}
|
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Audio reinitialization took %d ms - starting drop window",
|
||||||
|
audioReinitStopTime - audioReinitStartTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +100,12 @@ void* SdlAudioRenderer::getAudioBuffer(int*)
|
|||||||
|
|
||||||
bool SdlAudioRenderer::submitAudio(int bytesWritten)
|
bool SdlAudioRenderer::submitAudio(int bytesWritten)
|
||||||
{
|
{
|
||||||
|
// Our device may enter a permanent error status upon removal, so we need
|
||||||
|
// to recreate the audio device to pick up the new default audio device.
|
||||||
|
if (SDL_GetAudioDeviceStatus(m_AudioDevice) == SDL_AUDIO_STOPPED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (bytesWritten == 0) {
|
if (bytesWritten == 0) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
return true;
|
return true;
|
||||||
@@ -112,20 +118,8 @@ bool SdlAudioRenderer::submitAudio(int bytesWritten)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide backpressure on the queue to ensure too many frames don't build up
|
// Provide backpressure on the queue to ensure too many frames don't build up
|
||||||
// in SDL's audio queue, but don't wait forever to avoid a deadlock if the
|
// in SDL's audio queue.
|
||||||
// audio device fails.
|
while (SDL_GetQueuedAudioSize(m_AudioDevice) / m_FrameSize > 10) {
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
// Our device may enter a permanent error status upon removal, so we need
|
|
||||||
// to recreate the audio device to pick up the new default audio device.
|
|
||||||
if (SDL_GetAudioDeviceStatus(m_AudioDevice) == SDL_AUDIO_STOPPED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only queue more samples where there are 10 frames or less in SDL's queue
|
|
||||||
if (SDL_GetQueuedAudioSize(m_AudioDevice) / m_FrameSize <= 10) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Delay(1);
|
SDL_Delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,43 +112,16 @@ void SdlInputHandler::handleAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
|
|||||||
pointerId = (uint32_t)event->fingerId;
|
pointerId = (uint32_t)event->fingerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to send it as a native pen/touch event, otherwise fall back to our touch emulation
|
// Try to send it as a native touch event, otherwise fall back to our touch emulation
|
||||||
if (LiGetHostFeatureFlags() & LI_FF_PEN_TOUCH_EVENTS) {
|
if (LiSendTouchEvent(eventType, pointerId, vidrelx / dst.w, vidrely / dst.h, event->pressure,
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
0.0f, 0.0f, LI_ROT_UNKNOWN) == LI_ERR_UNSUPPORTED) {
|
||||||
bool isPen = false;
|
|
||||||
|
|
||||||
int numTouchDevices = SDL_GetNumTouchDevices();
|
|
||||||
for (int i = 0; i < numTouchDevices; i++) {
|
|
||||||
if (event->touchId == SDL_GetTouchDevice(i)) {
|
|
||||||
const char* touchName = SDL_GetTouchName(i);
|
|
||||||
|
|
||||||
// SDL will report "pen" as the name of pen input devices on Windows.
|
|
||||||
// https://github.com/libsdl-org/SDL/pull/5926
|
|
||||||
isPen = touchName && SDL_strcmp(touchName, "pen") == 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPen) {
|
|
||||||
LiSendPenEvent(eventType, LI_TOOL_TYPE_PEN, 0, vidrelx / dst.w, vidrely / dst.h, event->pressure,
|
|
||||||
0.0f, 0.0f, LI_ROT_UNKNOWN, LI_TILT_UNKNOWN);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
LiSendTouchEvent(eventType, pointerId, vidrelx / dst.w, vidrely / dst.h, event->pressure,
|
|
||||||
0.0f, 0.0f, LI_ROT_UNKNOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_DisabledTouchFeedback) {
|
|
||||||
// Disable touch feedback when passing touch natively
|
|
||||||
disableTouchFeedback();
|
|
||||||
m_DisabledTouchFeedback = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
emulateAbsoluteFingerEvent(event);
|
emulateAbsoluteFingerEvent(event);
|
||||||
}
|
}
|
||||||
|
else if (!m_DisabledTouchFeedback) {
|
||||||
|
// Disable touch feedback when passing touch natively
|
||||||
|
disableTouchFeedback();
|
||||||
|
m_DisabledTouchFeedback = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
|
void SdlInputHandler::emulateAbsoluteFingerEvent(SDL_TouchFingerEvent* event)
|
||||||
|
|||||||
@@ -68,48 +68,15 @@ void SdlInputHandler::sendGamepadState(GamepadState* state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char lt = state->lt;
|
|
||||||
unsigned char rt = state->rt;
|
|
||||||
short lsX = state->lsX;
|
|
||||||
short lsY = state->lsY;
|
|
||||||
short rsX = state->rsX;
|
|
||||||
short rsY = state->rsY;
|
|
||||||
|
|
||||||
// When in single controller mode, merge all gamepad state together
|
|
||||||
if (!m_MultiController) {
|
|
||||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
|
||||||
if (m_GamepadState[i].index == state->index) {
|
|
||||||
buttons |= m_GamepadState[i].buttons;
|
|
||||||
if (lt < m_GamepadState[i].lt) {
|
|
||||||
lt = m_GamepadState[i].lt;
|
|
||||||
}
|
|
||||||
if (rt < m_GamepadState[i].rt) {
|
|
||||||
rt = m_GamepadState[i].rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use abs() here instead of qAbs() for get proper integer promotion to
|
|
||||||
// correctly handle abs(-32768), which is not representable in a short.
|
|
||||||
if (abs(lsX) < abs(m_GamepadState[i].lsX) || abs(lsY) < abs(m_GamepadState[i].lsY)) {
|
|
||||||
lsX = m_GamepadState[i].lsX;
|
|
||||||
lsY = m_GamepadState[i].lsY;
|
|
||||||
}
|
|
||||||
if (abs(rsX) < abs(m_GamepadState[i].rsX) || abs(rsY) < abs(m_GamepadState[i].rsY)) {
|
|
||||||
rsX = m_GamepadState[i].rsX;
|
|
||||||
rsY = m_GamepadState[i].rsY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LiSendMultiControllerEvent(state->index,
|
LiSendMultiControllerEvent(state->index,
|
||||||
m_GamepadMask,
|
m_GamepadMask,
|
||||||
buttons,
|
buttons,
|
||||||
lt,
|
state->lt,
|
||||||
rt,
|
state->rt,
|
||||||
lsX,
|
state->lsX,
|
||||||
lsY,
|
state->lsY,
|
||||||
rsX,
|
state->rsX,
|
||||||
rsY);
|
state->rsY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlInputHandler::sendGamepadBatteryState(GamepadState* state, SDL_JoystickPowerLevel level)
|
void SdlInputHandler::sendGamepadBatteryState(GamepadState* state, SDL_JoystickPowerLevel level)
|
||||||
@@ -157,11 +124,11 @@ Uint32 SdlInputHandler::mouseEmulationTimerCallback(Uint32 interval, void *param
|
|||||||
{
|
{
|
||||||
auto gamepad = reinterpret_cast<GamepadState*>(param);
|
auto gamepad = reinterpret_cast<GamepadState*>(param);
|
||||||
|
|
||||||
int rawX;
|
short rawX;
|
||||||
int rawY;
|
short rawY;
|
||||||
|
|
||||||
// Determine which analog stick is currently receiving the strongest input
|
// Determine which analog stick is currently receiving the strongest input
|
||||||
if (abs(gamepad->lsX) + abs(gamepad->lsY) > abs(gamepad->rsX) + abs(gamepad->rsY)) {
|
if ((uint32_t)qAbs(gamepad->lsX) + qAbs(gamepad->lsY) > (uint32_t)qAbs(gamepad->rsX) + qAbs(gamepad->rsY)) {
|
||||||
rawX = gamepad->lsX;
|
rawX = gamepad->lsX;
|
||||||
rawY = -gamepad->lsY;
|
rawY = -gamepad->lsY;
|
||||||
}
|
}
|
||||||
@@ -955,10 +922,6 @@ QString SdlInputHandler::getUnmappedGamepads()
|
|||||||
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
|
|
||||||
// Flush stale events so they aren't processed by the main session event loop
|
|
||||||
SDL_FlushEvents(SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED);
|
|
||||||
SDL_FlushEvents(SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMAPPED);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
|
|||||||
// Allow gamepad input when the app doesn't have focus if requested
|
// Allow gamepad input when the app doesn't have focus if requested
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, prefs.backgroundGamepad ? "1" : "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, prefs.backgroundGamepad ? "1" : "0");
|
||||||
|
|
||||||
|
// If absolute mouse mode is enabled, use relative mode warp (which
|
||||||
|
// is via normal motion events that are influenced by mouse acceleration).
|
||||||
|
// Otherwise, we'll use raw input capture which is straight from the device
|
||||||
|
// without modification by the OS.
|
||||||
|
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
|
||||||
|
prefs.absoluteMouseMode ? "1" : "0",
|
||||||
|
SDL_HINT_OVERRIDE);
|
||||||
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 15)
|
#if !SDL_VERSION_ATLEAST(2, 0, 15)
|
||||||
// For older versions of SDL (2.0.14 and earlier), use SDL_HINT_GRAB_KEYBOARD
|
// For older versions of SDL (2.0.14 and earlier), use SDL_HINT_GRAB_KEYBOARD
|
||||||
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
|
SDL_SetHintWithPriority(SDL_HINT_GRAB_KEYBOARD,
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ void SdlInputHandler::performSpecialKeyCombo(KeyCombo combo)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send this text to the PC
|
// Send this text to the PC
|
||||||
LiSendUtf8TextEvent(text, (unsigned int)strlen(text));
|
LiSendUtf8TextEvent(text, strlen(text));
|
||||||
|
|
||||||
// SDL_GetClipboardText() allocates, so we must free
|
// SDL_GetClipboardText() allocates, so we must free
|
||||||
SDL_free((void*)text);
|
SDL_free((void*)text);
|
||||||
|
|||||||
@@ -79,24 +79,6 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Batch all pending mouse motion events to save CPU time
|
|
||||||
Sint32 x = event->x, y = event->y, xrel = event->xrel, yrel = event->yrel;
|
|
||||||
SDL_Event nextEvent;
|
|
||||||
while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
|
||||||
event = &nextEvent.motion;
|
|
||||||
|
|
||||||
// Ignore synthetic mouse events
|
|
||||||
if (event->which != SDL_TOUCH_MOUSEID) {
|
|
||||||
x = event->x;
|
|
||||||
y = event->y;
|
|
||||||
xrel += event->xrel;
|
|
||||||
yrel += event->yrel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should not reference the original event anymore
|
|
||||||
event = nullptr;
|
|
||||||
|
|
||||||
if (m_AbsoluteMouseMode) {
|
if (m_AbsoluteMouseMode) {
|
||||||
int windowWidth, windowHeight;
|
int windowWidth, windowHeight;
|
||||||
SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight);
|
SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight);
|
||||||
@@ -115,11 +97,14 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
|
|||||||
// Use the stream and window sizes to determine the video region
|
// Use the stream and window sizes to determine the video region
|
||||||
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
||||||
|
|
||||||
mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight);
|
mouseInVideoRegion = isMouseInVideoRegion(event->x,
|
||||||
|
event->y,
|
||||||
|
windowWidth,
|
||||||
|
windowHeight);
|
||||||
|
|
||||||
// Clamp motion to the video region
|
// Clamp motion to the video region
|
||||||
x = qMin(qMax(x - dst.x, 0), dst.w);
|
short x = qMin(qMax(event->x - dst.x, 0), dst.w);
|
||||||
y = qMin(qMax(y - dst.y, 0), dst.h);
|
short y = qMin(qMax(event->y - dst.y, 0), dst.h);
|
||||||
|
|
||||||
// Send the mouse position update if one of the following is true:
|
// Send the mouse position update if one of the following is true:
|
||||||
// a) it is in the video region now
|
// a) it is in the video region now
|
||||||
@@ -134,7 +119,7 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
||||||
LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h);
|
LiSendMousePositionEvent(x, y, dst.w, dst.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the cursor visibility if applicable
|
// Adjust the cursor visibility if applicable
|
||||||
@@ -150,7 +135,7 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event)
|
|||||||
m_MouseWasInVideoRegion = mouseInVideoRegion;
|
m_MouseWasInVideoRegion = mouseInVideoRegion;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LiSendMouseMoveEvent(xrel, yrel);
|
LiSendMouseMoveEvent(event->xrel, event->yrel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-216
@@ -52,7 +52,6 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <QScreen>
|
|
||||||
|
|
||||||
#define CONN_TEST_SERVER "qt.conntest.moonlight-stream.org"
|
#define CONN_TEST_SERVER "qt.conntest.moonlight-stream.org"
|
||||||
|
|
||||||
@@ -133,11 +132,7 @@ void Session::clConnectionTerminated(int errorCode)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
s_ActiveSession->m_UnexpectedTermination = true;
|
s_ActiveSession->m_UnexpectedTermination = true;
|
||||||
|
emit s_ActiveSession->displayLaunchError(tr("Connection terminated"));
|
||||||
// We'll assume large errors are hex values
|
|
||||||
bool hexError = qAbs(errorCode) > 1000;
|
|
||||||
emit s_ActiveSession->displayLaunchError(tr("Connection terminated") + "\n\n" +
|
|
||||||
tr("Error code: %1").arg(errorCode, hexError ? 8 : 0, hexError ? 16 : 10, QChar('0')));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,15 +536,17 @@ bool Session::populateDecoderProperties(SDL_Window* window)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *preferences)
|
Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *preferences)
|
||||||
: m_Preferences(preferences ? preferences : StreamingPreferences::get()),
|
: m_Preferences(preferences ? preferences : new StreamingPreferences(this)),
|
||||||
m_IsFullScreen(m_Preferences->windowMode != StreamingPreferences::WM_WINDOWED || !WMUtils::isRunningDesktopEnvironment()),
|
m_IsFullScreen(m_Preferences->windowMode != StreamingPreferences::WM_WINDOWED || !WMUtils::isRunningDesktopEnvironment()),
|
||||||
m_Computer(computer),
|
m_Computer(computer),
|
||||||
m_App(app),
|
m_App(app),
|
||||||
m_Window(nullptr),
|
m_Window(nullptr),
|
||||||
m_VideoDecoder(nullptr),
|
m_VideoDecoder(nullptr),
|
||||||
m_DecoderLock(0),
|
m_DecoderLock(0),
|
||||||
|
m_AudioDisabled(false),
|
||||||
m_AudioMuted(false),
|
m_AudioMuted(false),
|
||||||
m_QtWindow(nullptr),
|
m_DisplayOriginX(0),
|
||||||
|
m_DisplayOriginY(0),
|
||||||
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
||||||
m_InputHandler(nullptr),
|
m_InputHandler(nullptr),
|
||||||
m_MouseEmulationRefCount(0),
|
m_MouseEmulationRefCount(0),
|
||||||
@@ -565,48 +562,6 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
|
|||||||
|
|
||||||
bool Session::initialize()
|
bool Session::initialize()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_DARWIN
|
|
||||||
if (qEnvironmentVariableIntValue("I_WANT_BUGGY_FULLSCREEN") == 0) {
|
|
||||||
// If we have a notch and the user specified one of the two native display modes
|
|
||||||
// (notched or notchless), override the fullscreen mode to ensure it works as expected.
|
|
||||||
// - SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=0 will place the video underneath the notch
|
|
||||||
// - SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=1 will place the video below the notch
|
|
||||||
bool shouldUseFullScreenSpaces = m_Preferences->windowMode != StreamingPreferences::WM_FULLSCREEN;
|
|
||||||
SDL_DisplayMode desktopMode;
|
|
||||||
SDL_Rect safeArea;
|
|
||||||
for (int displayIndex = 0; StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea); displayIndex++) {
|
|
||||||
// Check if this display has a notch (safeArea != desktopMode)
|
|
||||||
if (desktopMode.h != safeArea.h || desktopMode.w != safeArea.w) {
|
|
||||||
// Check if we're trying to stream at the full native resolution (including notch)
|
|
||||||
if (m_Preferences->width == desktopMode.w && m_Preferences->height == desktopMode.h) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Overriding default fullscreen mode for native fullscreen resolution");
|
|
||||||
shouldUseFullScreenSpaces = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (m_Preferences->width == safeArea.w && m_Preferences->height == safeArea.h) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Overriding default fullscreen mode for native safe area resolution");
|
|
||||||
shouldUseFullScreenSpaces = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using modesetting on modern versions of macOS is extremely unreliable
|
|
||||||
// and leads to hangs, deadlocks, and other nasty stuff. The only time
|
|
||||||
// people seem to use it is to get the full screen on notched Macs,
|
|
||||||
// which setting SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES=1 also accomplishes
|
|
||||||
// with much less headache.
|
|
||||||
//
|
|
||||||
// https://github.com/moonlight-stream/moonlight-qt/issues/973
|
|
||||||
// https://github.com/moonlight-stream/moonlight-qt/issues/999
|
|
||||||
// https://github.com/moonlight-stream/moonlight-qt/issues/1211
|
|
||||||
// https://github.com/moonlight-stream/moonlight-qt/issues/1218
|
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, shouldUseFullScreenSpaces ? "1" : "0");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
||||||
@@ -643,18 +598,13 @@ bool Session::initialize()
|
|||||||
m_StreamConfig.height = m_Preferences->height;
|
m_StreamConfig.height = m_Preferences->height;
|
||||||
m_StreamConfig.fps = m_Preferences->fps;
|
m_StreamConfig.fps = m_Preferences->fps;
|
||||||
m_StreamConfig.bitrate = m_Preferences->bitrateKbps;
|
m_StreamConfig.bitrate = m_Preferences->bitrateKbps;
|
||||||
|
m_StreamConfig.hevcBitratePercentageMultiplier = 75;
|
||||||
|
m_StreamConfig.av1BitratePercentageMultiplier = 75;
|
||||||
|
|
||||||
#ifndef STEAM_LINK
|
#ifndef STEAM_LINK
|
||||||
// Opt-in to all encryption features if we detect that the platform
|
// Enable audio encryption as long as we're not on Steam Link.
|
||||||
// has AES cryptography acceleration instructions and more than 2 cores.
|
// That hardware can hardly handle Opus decoding at all.
|
||||||
if (StreamUtils::hasFastAes() && SDL_GetCPUCount() > 2) {
|
m_StreamConfig.encryptionFlags = ENCFLG_AUDIO;
|
||||||
m_StreamConfig.encryptionFlags = ENCFLG_ALL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Enable audio encryption as long as we're not on Steam Link.
|
|
||||||
// That hardware can hardly handle Opus decoding at all.
|
|
||||||
m_StreamConfig.encryptionFlags = ENCFLG_AUDIO;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@@ -768,15 +718,6 @@ bool Session::initialize()
|
|||||||
if (m_Preferences->enableHdr) {
|
if (m_Preferences->enableHdr) {
|
||||||
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
|
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll try to fall back to HEVC first if AV1 fails. We'd rather not fall back
|
|
||||||
// straight to H.264 if the user asked for AV1 and the host doesn't support it.
|
|
||||||
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN8) {
|
|
||||||
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265;
|
|
||||||
}
|
|
||||||
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
|
|
||||||
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -791,17 +732,7 @@ bool Session::initialize()
|
|||||||
}
|
}
|
||||||
// Fall-through
|
// Fall-through
|
||||||
case StreamingPreferences::WM_FULLSCREEN:
|
case StreamingPreferences::WM_FULLSCREEN:
|
||||||
#ifdef Q_OS_DARWIN
|
|
||||||
if (qEnvironmentVariableIntValue("I_WANT_BUGGY_FULLSCREEN") == 0) {
|
|
||||||
// Don't use "real" fullscreen on macOS by default. See comments above.
|
|
||||||
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
|
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,13 +814,21 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
|||||||
emitLaunchWarning(tr("Your host software or GPU doesn't support encoding AV1."));
|
emitLaunchWarning(tr("Your host software or GPU doesn't support encoding AV1."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll try to fall back to HEVC first if AV1 fails. We'd rather not fall back
|
||||||
|
// straight to H.264 if the user asked for AV1 and the host doesn't support it.
|
||||||
|
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN8) {
|
||||||
|
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265;
|
||||||
|
}
|
||||||
|
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
|
||||||
|
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
|
||||||
|
}
|
||||||
|
|
||||||
// Moonlight-common-c will handle this case already, but we want
|
// Moonlight-common-c will handle this case already, but we want
|
||||||
// to set this explicitly here so we can do our hardware acceleration
|
// to set this explicitly here so we can do our hardware acceleration
|
||||||
// check below.
|
// check below.
|
||||||
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_AV1;
|
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_AV1;
|
||||||
}
|
}
|
||||||
else if (!m_Preferences->enableHdr && // HDR is checked below
|
else if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
|
||||||
m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
|
|
||||||
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
|
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
|
||||||
!isHardwareDecodeAvailable(testWindow,
|
!isHardwareDecodeAvailable(testWindow,
|
||||||
m_Preferences->videoDecoderSelection,
|
m_Preferences->videoDecoderSelection,
|
||||||
@@ -912,8 +851,7 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
|||||||
// check below.
|
// check below.
|
||||||
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_H265;
|
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_MASK_H265;
|
||||||
}
|
}
|
||||||
else if (!m_Preferences->enableHdr && // HDR is checked below
|
else if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
|
||||||
m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_AUTO && // Force hardware decoding checked below
|
|
||||||
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
|
m_Preferences->videoCodecConfig != StreamingPreferences::VCC_AUTO && // Auto VCC is already checked in initialize()
|
||||||
!isHardwareDecodeAvailable(testWindow,
|
!isHardwareDecodeAvailable(testWindow,
|
||||||
m_Preferences->videoDecoderSelection,
|
m_Preferences->videoDecoderSelection,
|
||||||
@@ -1024,7 +962,8 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If nothing worked, warn the user that audio will not work
|
// If nothing worked, warn the user that audio will not work
|
||||||
if (!audioTestPassed) {
|
m_AudioDisabled = !audioTestPassed;
|
||||||
|
if (m_AudioDisabled) {
|
||||||
emitLaunchWarning(tr("Failed to open audio device. Audio will be unavailable during this session."));
|
emitLaunchWarning(tr("Failed to open audio device. Audio will be unavailable during this session."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1147,38 +1086,26 @@ void Session::getWindowDimensions(int& x, int& y,
|
|||||||
// Create our window on the same display that Qt's UI
|
// Create our window on the same display that Qt's UI
|
||||||
// was being displayed on.
|
// was being displayed on.
|
||||||
else {
|
else {
|
||||||
Q_ASSERT(m_QtWindow != nullptr);
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
if (m_QtWindow != nullptr) {
|
"Qt UI screen is at (%d,%d)",
|
||||||
QScreen* screen = m_QtWindow->screen();
|
m_DisplayOriginX, m_DisplayOriginY);
|
||||||
if (screen != nullptr) {
|
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
|
||||||
QRect displayRect = screen->geometry();
|
SDL_Rect displayBounds;
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
|
||||||
"Qt UI screen is at (%d,%d)",
|
if (displayBounds.x == m_DisplayOriginX &&
|
||||||
displayRect.x(), displayRect.y());
|
displayBounds.y == m_DisplayOriginY) {
|
||||||
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
SDL_Rect displayBounds;
|
"SDL found matching display %d",
|
||||||
|
i);
|
||||||
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
|
displayIndex = i;
|
||||||
if (displayBounds.x == displayRect.x() &&
|
break;
|
||||||
displayBounds.y == displayRect.y()) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL found matching display %d",
|
|
||||||
i);
|
|
||||||
displayIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL_GetDisplayBounds(%d) failed: %s",
|
|
||||||
i, SDL_GetError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Qt window is not associated with a QScreen!");
|
"SDL_GetDisplayBounds(%d) failed: %s",
|
||||||
|
i, SDL_GetError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1232,8 +1159,7 @@ void Session::updateOptimalWindowDisplayMode()
|
|||||||
// If this doesn't fit the selected resolution, use the native
|
// If this doesn't fit the selected resolution, use the native
|
||||||
// resolution of the panel (unscaled).
|
// resolution of the panel (unscaled).
|
||||||
if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) {
|
if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) {
|
||||||
SDL_Rect safeArea;
|
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) {
|
||||||
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1498,7 +1424,8 @@ bool Session::startConnectionAsync()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
|
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
|
||||||
&m_VideoCallbacks, &m_AudioCallbacks,
|
&m_VideoCallbacks,
|
||||||
|
m_AudioDisabled ? nullptr : &m_AudioCallbacks,
|
||||||
NULL, 0, NULL, 0);
|
NULL, 0, NULL, 0);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
// We already displayed an error dialog in the stage failure
|
// We already displayed an error dialog in the stage failure
|
||||||
@@ -1545,9 +1472,10 @@ public:
|
|||||||
Session* m_Session;
|
Session* m_Session;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Session::exec(QWindow* qtWindow)
|
void Session::exec(int displayOriginX, int displayOriginY)
|
||||||
{
|
{
|
||||||
m_QtWindow = qtWindow;
|
m_DisplayOriginX = displayOriginX;
|
||||||
|
m_DisplayOriginY = displayOriginY;
|
||||||
|
|
||||||
// Use a separate thread for the streaming session on X11 or Wayland
|
// Use a separate thread for the streaming session on X11 or Wayland
|
||||||
// to ensure we don't stomp on Qt's GL context. This breaks when using
|
// to ensure we don't stomp on Qt's GL context. This breaks when using
|
||||||
@@ -1651,26 +1579,16 @@ void Session::execInternal()
|
|||||||
// We always want a resizable window with High DPI enabled
|
// We always want a resizable window with High DPI enabled
|
||||||
Uint32 defaultWindowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
Uint32 defaultWindowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
||||||
|
|
||||||
// If we're starting in windowed mode and the Moonlight GUI is maximized or
|
// If we're starting in windowed mode and the Moonlight GUI is maximized,
|
||||||
// minimized, match that with the streaming window.
|
// match that with the streaming window.
|
||||||
if (!m_IsFullScreen && m_QtWindow != nullptr) {
|
if (!m_IsFullScreen) {
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
QWindowList windows = QGuiApplication::topLevelWindows();
|
||||||
// Qt 5.10+ can propagate multiple states together
|
for (const QWindow* window : windows) {
|
||||||
if (m_QtWindow->windowStates() & Qt::WindowMaximized) {
|
if (window->windowState() & Qt::WindowMaximized) {
|
||||||
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
|
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_QtWindow->windowStates() & Qt::WindowMinimized) {
|
|
||||||
defaultWindowFlags |= SDL_WINDOW_MINIMIZED;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Qt 5.9 only supports a single state at a time
|
|
||||||
if (m_QtWindow->windowState() == Qt::WindowMaximized) {
|
|
||||||
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
|
|
||||||
}
|
|
||||||
else if (m_QtWindow->windowState() == Qt::WindowMinimized) {
|
|
||||||
defaultWindowFlags |= SDL_WINDOW_MINIMIZED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use only the computer name on macOS to match Apple conventions where the
|
// We use only the computer name on macOS to match Apple conventions where the
|
||||||
@@ -1713,13 +1631,16 @@ void Session::execInternal()
|
|||||||
|
|
||||||
// HACK: Remove once proper Dark Mode support lands in SDL
|
// HACK: Remove once proper Dark Mode support lands in SDL
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
if (m_QtWindow != nullptr) {
|
{
|
||||||
BOOL darkModeEnabled;
|
BOOL darkModeEnabled = FALSE;
|
||||||
|
|
||||||
// Query whether dark mode is enabled for our Qt window (which tracks the OS dark mode state)
|
// Query whether dark mode is enabled for our Qt window (which tracks the OS dark mode state)
|
||||||
if (FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) &&
|
QWindowList windows = QGuiApplication::topLevelWindows();
|
||||||
FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
|
for (const QWindow* window : windows) {
|
||||||
darkModeEnabled = FALSE;
|
if (SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) ||
|
||||||
|
SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SysWMinfo info;
|
SDL_SysWMinfo info;
|
||||||
@@ -1851,9 +1772,6 @@ void Session::execInternal()
|
|||||||
// Start rich presence to indicate we're in game
|
// Start rich presence to indicate we're in game
|
||||||
RichPresenceManager presence(*m_Preferences, m_App.name);
|
RichPresenceManager presence(*m_Preferences, m_App.name);
|
||||||
|
|
||||||
// Toggle the stats overlay if requested by the user
|
|
||||||
m_OverlayManager.setOverlayState(Overlay::OverlayDebug, m_Preferences->showPerformanceOverlay);
|
|
||||||
|
|
||||||
// Hijack this thread to be the SDL main thread. We have to do this
|
// 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.
|
// because we want to suspend all Qt processing until the stream is over.
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@@ -1961,8 +1879,7 @@ void Session::execInternal()
|
|||||||
// We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event.
|
// We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event.
|
||||||
// We use SDL_WINDOWEVENT_SIZE_CHANGED rather than SDL_WINDOWEVENT_RESIZED because the latter doesn't
|
// We use SDL_WINDOWEVENT_SIZE_CHANGED rather than SDL_WINDOWEVENT_RESIZED because the latter doesn't
|
||||||
// seem to fire when switching from windowed to full-screen on X11.
|
// seem to fire when switching from windowed to full-screen on X11.
|
||||||
if (event.window.event != SDL_WINDOWEVENT_SIZE_CHANGED &&
|
if (event.window.event != SDL_WINDOWEVENT_SIZE_CHANGED && event.window.event != SDL_WINDOWEVENT_SHOWN) {
|
||||||
(event.window.event != SDL_WINDOWEVENT_SHOWN || m_VideoDecoder != nullptr)) {
|
|
||||||
// Check that the window display hasn't changed. If it has, we want
|
// Check that the window display hasn't changed. If it has, we want
|
||||||
// to recreate the decoder to allow it to adapt to the new display.
|
// to recreate the decoder to allow it to adapt to the new display.
|
||||||
// This will allow Pacer to pull the new display refresh rate.
|
// This will allow Pacer to pull the new display refresh rate.
|
||||||
@@ -1998,52 +1915,6 @@ void Session::execInternal()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow the renderer to handle the state change without being recreated
|
|
||||||
if (m_VideoDecoder) {
|
|
||||||
bool forceRecreation = false;
|
|
||||||
|
|
||||||
WINDOW_STATE_CHANGE_INFO windowChangeInfo = {};
|
|
||||||
windowChangeInfo.window = m_Window;
|
|
||||||
|
|
||||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
|
||||||
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_SIZE;
|
|
||||||
|
|
||||||
windowChangeInfo.width = event.window.data1;
|
|
||||||
windowChangeInfo.height = event.window.data2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
|
|
||||||
if (newDisplayIndex != currentDisplayIndex) {
|
|
||||||
windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_DISPLAY;
|
|
||||||
|
|
||||||
windowChangeInfo.displayIndex = newDisplayIndex;
|
|
||||||
|
|
||||||
// If the refresh rates have changed, we will need to go through the full
|
|
||||||
// decoder recreation path to ensure Pacer is switched to the new display
|
|
||||||
// and that we apply any V-Sync disablement rules that may be needed for
|
|
||||||
// this display.
|
|
||||||
SDL_DisplayMode oldMode, newMode;
|
|
||||||
if (SDL_GetCurrentDisplayMode(currentDisplayIndex, &oldMode) < 0 ||
|
|
||||||
SDL_GetCurrentDisplayMode(newDisplayIndex, &newMode) < 0 ||
|
|
||||||
oldMode.refresh_rate != newMode.refresh_rate) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Forcing renderer recreation due to refresh rate change between displays");
|
|
||||||
forceRecreation = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!forceRecreation && m_VideoDecoder->notifyWindowChanged(&windowChangeInfo)) {
|
|
||||||
// Update the window display mode based on our current monitor
|
|
||||||
// NB: Avoid a useless modeset by only doing this if it changed.
|
|
||||||
if (newDisplayIndex != currentDisplayIndex) {
|
|
||||||
currentDisplayIndex = newDisplayIndex;
|
|
||||||
updateOptimalWindowDisplayMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Recreating renderer for window event: %d (%d %d)",
|
"Recreating renderer for window event: %d (%d %d)",
|
||||||
event.window.event,
|
event.window.event,
|
||||||
@@ -2216,31 +2087,6 @@ DispatchDeferredCleanup:
|
|||||||
m_VideoDecoder = nullptr;
|
m_VideoDecoder = nullptr;
|
||||||
SDL_AtomicUnlock(&m_DecoderLock);
|
SDL_AtomicUnlock(&m_DecoderLock);
|
||||||
|
|
||||||
// Propagate state changes from the SDL window back to the Qt window
|
|
||||||
//
|
|
||||||
// NB: We're making a conscious decision not to propagate the maximized
|
|
||||||
// or normal state of the window here. The thinking is that users may
|
|
||||||
// routinely maximize the streaming window simply to view the stream
|
|
||||||
// in a larger window, but they don't necessarily want the UI in such
|
|
||||||
// a large window.
|
|
||||||
if (!m_IsFullScreen && m_QtWindow != nullptr && m_Window != nullptr) {
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
||||||
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
|
|
||||||
m_QtWindow->setWindowStates(m_QtWindow->windowStates() | Qt::WindowMinimized);
|
|
||||||
}
|
|
||||||
else if (m_QtWindow->windowStates() & Qt::WindowMinimized) {
|
|
||||||
m_QtWindow->setWindowStates(m_QtWindow->windowStates() & ~Qt::WindowMinimized);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_MINIMIZED) {
|
|
||||||
m_QtWindow->setWindowState(Qt::WindowMinimized);
|
|
||||||
}
|
|
||||||
else if (m_QtWindow->windowState() & Qt::WindowMinimized) {
|
|
||||||
m_QtWindow->setWindowState(Qt::WindowNoState);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This must be called after the decoder is deleted, because
|
// This must be called after the decoder is deleted, because
|
||||||
// the renderer may want to interact with the window
|
// the renderer may want to interact with the window
|
||||||
SDL_DestroyWindow(m_Window);
|
SDL_DestroyWindow(m_Window);
|
||||||
|
|||||||
Loaded 100 of 148 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user