Compare commits

..
84 Commits
Author SHA1 Message Date
Cameron Gutman 6182553d79 Version 0.10.0 2019-04-07 13:21:02 -07:00
Cameron Gutman 905a0cdac1 Make GFE-reported errors more clear 2019-04-07 12:21:05 -07:00
Cameron Gutman f20ddcf26d Fix long delay in LiStopConnection() that can cause a hang on quit 2019-04-06 15:28:48 -07:00
Cameron Gutman fe26098c2b Fix quitting via Command-Q on macOS 2019-04-06 15:22:14 -07:00
Cameron Gutman 2354a0a640 Fix unresponsive GUI on quit if a polling operation is in progress 2019-04-06 13:11:36 -07:00
Cameron Gutman 7c3f09c70c Update poor connection behavior 2019-04-06 12:25:35 -07:00
Cameron Gutman caf5350fe6 Update SDL_GameControllerDB to e411d95fcda34c19270c2d12335fc47512a87dec 2019-04-06 12:06:52 -07:00
Cameron Gutman 84084835ce Refactor and fix the GridView centering code to avoid flipping between states 2019-04-06 11:48:58 -07:00
Cameron Gutman 7f38a67ede Fix brown-paper-bag bug in 948910bfa2 2019-04-03 21:13:12 -07:00
Cameron Gutman 54383b84ec Fix random deadlocks on window resize with the software decoder due to processing window messages off the render thread 2019-04-03 20:44:50 -07:00
Cameron Gutman 948910bfa2 Don't open the app context menu for mouse clicks 2019-04-03 20:27:00 -07:00
Cameron Gutman ce5dac07d3 Ensure the anchor hack stays in sync with changing margins prior to Qt 5.12 2019-04-01 22:12:49 -07:00
Cameron Gutman d4b0057423 Minor tweak to the label layout of running apps 2019-04-01 20:55:11 -07:00
Cameron Gutman de8b12f594 Fix grid view margins and scroll bar positioning on Qt 5.9 2019-04-01 20:40:52 -07:00
Cameron Gutman 69e31a0a37 Redesign the app view UI to feature larger game artwork and less text 2019-04-01 19:49:33 -07:00
Cameron Gutman c383cd39f4 Fix text wrapping with giant error messages 2019-04-01 18:31:13 -07:00
Cameron Gutman e88d2f0ab2 Add files and scripts to build for Steam Link 2019-03-31 23:26:17 -07:00
Cameron Gutman 32535578a0 Use newlines for the help text separator on the unmapped gamepad dialog 2019-03-31 20:10:56 -07:00
Cameron Gutman 2e9af8fef0 Fix focus and rejection of Yes/No dialogs on SL 2019-03-31 19:40:30 -07:00
Cameron Gutman 0aa87aacc6 Fix focus issues after dismissal of consecutive dialogs (like Add PC -> Add PC error) 2019-03-31 19:32:17 -07:00
Cameron Gutman bc38c302ed Cap the maximum width of the error dialogs 2019-03-31 19:05:38 -07:00
Cameron Gutman c724902508 Clean up code for new QC2 dialogs and remove some unused imports 2019-03-31 17:44:19 -07:00
Cameron Gutman 73604020d7 Rewrite dialog code to use Quick Controls 2 2019-03-31 17:24:25 -07:00
Cameron Gutman 060a00b9c7 Consolidate help handling into ErrorMessageDialog 2019-03-31 15:16:48 -07:00
Cameron Gutman 2cd32bf25a Move the cursor into the lower right corner on SL 2019-03-31 13:58:27 -07:00
Cameron Gutman 81ad1a18fa Fix error dialog focus issues after dismissal on Steam Link 2019-03-31 13:57:57 -07:00
Cameron Gutman 1d94903eef Add a hack to ensure the layout anchors work properly on Qt 5.11 and earlier 2019-03-30 12:28:18 -07:00
Cameron Gutman 2d7dcb495c Remove CAPABILITY_DIRECT_SUBMIT for the video decoder because it can block for significant time periods when D3D9 is reconfiguring display settings (and holding the D3D global lock)
This can cause false reports of packet loss.
2019-03-29 23:38:23 -07:00
Cameron Gutman bc580c9aaa Ensure SdlGamepadKeyNavigation is only re-enabled after the sessionFinished() callback to avoid conflicts with SdlInputHandler 2019-03-29 23:20:43 -07:00
Cameron Gutman f8e2740532 Add placeholder text when no PCs are present 2019-03-27 18:13:20 -07:00
Cameron Gutman a079ecd20b Improve transitions and eliminate conflicting anchor warnings 2019-03-27 01:28:46 -07:00
Cameron Gutman 89c8725766 Improve Add PC dialog readability on Windows 2019-03-27 00:39:25 -07:00
Cameron Gutman da7d532564 Move Add PC button to toolbar 2019-03-26 21:31:51 -07:00
Cameron Gutman 4752d4966d Improve Add PC dialog to have a proper text field 2019-03-26 21:12:53 -07:00
Cameron Gutman 50f47f1ed5 Fix handling of screen saver after fa4c0e82bd and reset background events hint for UI after streaming 2019-03-26 21:11:24 -07:00
Cameron Gutman 26bdc3e1d5 Avoid using ANGLE on debug builds due to assert issues on Qt 5.12.2 2019-03-24 19:23:02 -07:00
Cameron Gutman 764360c0f3 Avoid doing non-trivial work in onCheckedChanged on settings page load 2019-03-24 19:15:35 -07:00
Cameron Gutman 08482577eb Fix connection termination dialog regression from 03c0be0720 2019-03-24 15:19:38 -07:00
Cameron Gutman cabbba2448 Move status update overlay to bottom left for SDL 2019-03-23 21:14:21 -07:00
Cameron Gutman c48ed7bb0d Fix undefined variable usage after 2703efedef 2019-03-23 20:25:19 -07:00
Cameron Gutman fa4c0e82bd Only initialize the video subsystem once per launch 2019-03-23 17:46:42 -07:00
Cameron Gutman c0bf8b9c25 Remove redundant initializtion of SDL video 2019-03-23 14:26:08 -07:00
Cameron Gutman d2cff86c97 Refactor SdlGamepadKeyNavigation to avoid multiple reinitializations of SDL 2019-03-23 14:15:55 -07:00
Cameron Gutman 03c0be0720 Only display help options on platforms with web browsers 2019-03-23 13:51:34 -07:00
Cameron Gutman a3361363e4 Remove duplicate data files on Linux 2019-03-23 12:20:01 -07:00
Cameron Gutman 40e142c5c7 Add force feedback to feature list 2019-03-23 12:14:43 -07:00
Cameron Gutman 2703efedef Improve performance of looking up static system properties 2019-03-23 12:05:08 -07:00
Cameron Gutman c313f1a20b Embed our data files inside the binary with QRC 2019-03-23 10:45:44 -07:00
Cameron Gutman b7116657d9 Add SLAudio renderer for Steam Link 2019-03-22 23:08:10 -07:00
Cameron Gutman 2bf3f1b94b Refactor Steam Link support files 2019-03-22 22:51:08 -07:00
Cameron Gutman b78a7971d9 Fix the launch warning toasts on Qt 5.9 2019-03-22 21:30:06 -07:00
Cameron Gutman 5079bc4976 Fix SDL video initialization on Steam Link 2019-03-22 21:11:17 -07:00
Cameron Gutman 555032c15c Add fallback font hack for Steam Link 2019-03-22 20:53:02 -07:00
Cameron Gutman bcff327565 Use QRC URLs for the initialView value to fix loading from the QRC blob 2019-03-22 19:21:04 -07:00
Cameron Gutman e95ff989b1 Allow DXVA2 renderer blacklists to be bypassed 2019-03-21 20:44:30 -07:00
Cameron Gutman 018e8999e2 Add connection status overlay for the VT renderer 2019-03-18 20:56:53 -07:00
Cameron Gutman f624a1817e Add preference and tweak connection warning 2019-03-18 20:21:52 -07:00
Cameron Gutman 202a643e5a Fix size of the status update overlay on DXVA2 2019-03-17 15:12:25 -07:00
Cameron Gutman c75b9c9221 Add connection status overlay for DXVA2 and SDL 2019-03-17 15:08:21 -07:00
Cameron Gutman 9a6f5ba1a8 Improve speed of process termination on exit 2019-03-17 12:23:13 -07:00
Cameron Gutman 052194714a Adjust thread priorities based on recent refactoring 2019-03-17 00:45:31 -07:00
Cameron Gutman 5ccb4a8b7f Improve busy PC error text 2019-03-17 00:33:52 -07:00
Cameron Gutman 7c3cec0451 Add EZFRD32.dll and EZFRD64.dll to blacklist based on crash reports 2019-03-17 00:23:32 -07:00
Cameron Gutman b79d3b31b3 Disable minimize on focus loss by default 2019-03-17 00:20:47 -07:00
Cameron Gutman bf5d43d0cf Use SDL_HINT_OVERRIDE for critical hints 2019-03-17 00:18:15 -07:00
Cameron Gutman f2ac570949 Rebuild SDL2 (5dc13016cf) with USE_D3D9EX 2019-03-16 23:52:40 -07:00
Cameron Gutman 37636ef1eb Allow other SSL errors as long as the cert is correct 2019-03-16 19:01:55 -07:00
Cameron Gutman c540bec824 Remove logging hack now that Qt 5.12.2 fixes the bug 2019-03-16 16:59:49 -07:00
Cameron Gutman 9288cfa684 Basic support for building as UWP app 2019-03-13 01:02:04 -07:00
Cameron Gutman b1de8b703b Update bug report template for Snap package 2019-03-06 21:32:51 -08:00
Cameron Gutman ccd0777ce9 Use a Loader to be able to use StackView.onActivated to trigger stream start 2019-03-02 20:55:50 -08:00
Cameron Gutman 49b67b3e18 Adjust QGnomePlatform hack to avoid disabling desktop settings awareness. Fixes #185 2019-03-02 19:50:05 -08:00
Cameron Gutman bc97168dc7 Update moonlight-common-c 2019-03-02 14:14:54 -08:00
Cameron Gutman 0ec04df334 Hide hint text when the connection completes 2019-03-02 14:13:32 -08:00
Cameron Gutman 26d7946b4a Fix toolbar hiding in quit segue 2019-03-02 14:13:14 -08:00
Cameron Gutman 292cc579cf Partially revert 8037fb76fc to fix mouse polling issues on Windows 2019-03-02 14:11:30 -08:00
Cameron Gutman 8492482e55 Add a script to create a zip of Qt PDBs 2019-02-26 19:10:17 -08:00
Cameron Gutman ac8183feee Fix selection loss when going back caused by previous commit 2019-02-22 22:44:09 -08:00
Cameron Gutman 8037fb76fc Use StackView activation callbacks to perform init and cleanup since visibility callbacks aren't guaranteed to run in any order 2019-02-22 22:14:06 -08:00
Cameron Gutman 754a14941b Update Windows build to OpenSSL 1.1.0k to fix App Verifier TLS warning 2019-02-22 21:14:40 -08:00
Konstantin Budnikov e32bc1a0a3 SDL_HapticRumblePlay rumble for gamepad without SDL_HAPTIC_LEFTRIGHT support (#181) 2019-02-20 19:22:38 -08:00
Cameron Gutman e5a8b49c93 Limit the number of queued frames in Pacer to prevent excessive memory consumption if the renderer gets blocked for a while 2019-02-18 12:13:45 -08:00
Cameron Gutman b6872456cd Version 0.9.1 2019-02-17 13:13:57 -08:00
Cameron Gutman 2a12b018da Fix special key combos on non-QWERTY latin keyboards 2019-02-17 13:07:54 -08:00
111 changed files with 1764 additions and 1114 deletions

No files matched your search

+5 -4
View File
@@ -22,18 +22,19 @@ List the games you've tried that exhibit the issue. To see if the issue is game-
- Does the problem still occur after reverting settings back to default? - Does the problem still occur after reverting settings back to default?
**Client PC details (please complete the following information)** **Client PC details (please complete the following information)**
- OS: [e.g. Windows 10 1803] - OS: [e.g. Windows 10 1809]
- Moonlight Version: [e.g. v0.2.0] - Moonlight Version: [e.g. v0.9.0]
**Server PC details (please complete the following information)** **Server PC details (please complete the following information)**
- OS: [e.g. Windows 10 1803] - OS: [e.g. Windows 10 1809]
- GeForce Experience version: [e.g. 3.16.0.140] - GeForce Experience version: [e.g. 3.16.0.140]
- Nvidia GPU driver: [e.g. 417.35] - Nvidia GPU driver: [e.g. 417.35]
**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.
- On macOS, `Moonlight-###.log` files can be found in `/tmp`. In Finder, press Cmd+Shift+G, then type `/tmp` to navigate there. - On macOS, `Moonlight-###.log` files can be found in `/tmp`. In Finder, press Cmd+Shift+G, then type `/tmp` to navigate there.
- On Linux, logs print to the terminal when running via `flatpak run com.moonlight_stream.Moonlight` - On Linux with the Flatpak, logs print to the terminal when running with the command: `flatpak run com.moonlight_stream.Moonlight`
- On Linux with the Snap, logs print to the terminal when running with the command: `moonlight`
**Additional context** **Additional context**
Anything else you think may be relevant to the issue Anything else you think may be relevant to the issue
+5 -1
View File
@@ -134,7 +134,11 @@ private:
// This DLL shipped with ASUS Sonic Radar 3 improperly handles // This DLL shipped with ASUS Sonic Radar 3 improperly handles
// D3D9 exclusive fullscreen in a way that causes CreateDeviceEx() // D3D9 exclusive fullscreen in a way that causes CreateDeviceEx()
// to deadlock. https://github.com/moonlight-stream/moonlight-qt/issues/102 // to deadlock. https://github.com/moonlight-stream/moonlight-qt/issues/102
L"NahimicOSD.dll" L"NahimicOSD.dll",
// This DLL has been seen in several crash reports. Some Googling
// suggests it's highly unstable and causes issues in many games.
L"EZFRD32.dll",
L"EZFRD64.dll",
}; };
}; };
+11 -5
View File
@@ -16,10 +16,10 @@ You can follow development on our [Discord server](https://discord.gg/6ERtzFY).
- 5.1 surround sound audio - 5.1 surround sound audio
- HEVC support for better image quality at reduced bandwidth - HEVC support for better image quality at reduced bandwidth
- Keyboard and mouse support - Keyboard and mouse support
- Gamepad support with SDL gamepad mappings - Gamepad support with force feedback for up to 4 players
## Downloads ## Downloads
- [Windows and macOS](https://github.com/moonlight-stream/moonlight-qt/releases) - [Windows, macOS, and Steam Link](https://github.com/moonlight-stream/moonlight-qt/releases)
- [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)
@@ -27,7 +27,7 @@ You can follow development on our [Discord server](https://discord.gg/6ERtzFY).
### General Build Requirements ### General Build Requirements
* Qt 5.9 SDK or later * Qt 5.9 SDK or later
### Windows-specific Requirements ### Windows-specific Build Requirements
* Windows 7 or later * Windows 7 or later
* [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2015 or later (Community edition is fine) * [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2015 or later (Community edition is fine)
* Select MSVC Desktop toolchain during Qt installation * Select MSVC Desktop toolchain during Qt installation
@@ -35,25 +35,31 @@ You can follow development on our [Discord server](https://discord.gg/6ERtzFY).
* [7-Zip](https://www.7-zip.org/) (only if building installers for non-development PCs) * [7-Zip](https://www.7-zip.org/) (only if building installers for non-development PCs)
* [WiX Toolset](http://wixtoolset.org/releases/) v3.11 or later (only if building installers for non-development PCs) * [WiX Toolset](http://wixtoolset.org/releases/) v3.11 or later (only if building installers for non-development PCs)
### Mac-specific Requirements ### Mac-specific Build Requirements
* macOS Sierra (10.12) or later * macOS Sierra (10.12) or later
* Xcode 10 * Xcode 10
* [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-specific Requirements ### Linux-specific Build Requirements
* GCC or Clang * GCC or Clang
* Install your distro equivalents of: `openssl-devel qt5-devel SDL2-devel ffmpeg-devel qt5-qtquickcontrols2-devel libva-devel libvdpau-devel opus-devel pulseaudio-libs-devel alsa-lib-devel SDL2_ttf-devel` * Install your distro equivalents of: `openssl-devel qt5-devel SDL2-devel ffmpeg-devel qt5-qtquickcontrols2-devel libva-devel libvdpau-devel opus-devel pulseaudio-libs-devel alsa-lib-devel SDL2_ttf-devel`
* FFmpeg 4.0 is required to build. If your distro doesn't package FFmpeg 4.0, you can build and install it from source on http://ffmpeg.org/ * FFmpeg 4.0 is required to build. If your distro doesn't package FFmpeg 4.0, you can build and install it from source on http://ffmpeg.org/
### Steam Link-specific Build Requirements
* [Steam Link SDK](https://github.com/ValveSoftware/steamlink-sdk) cloned on your build system
* STEAMLINK_SDK_PATH environment variable set to the Steam Link SDK path
### Build Setup Steps ### Build Setup Steps
1. Install the latest Qt SDK (and optionally, the Qt Creator IDE) from https://www.qt.io/download 1. Install the latest Qt SDK (and optionally, the Qt Creator IDE) from https://www.qt.io/download
* You can install Qt via Homebrew on macOS, but you will need to use `brew install qt --with-debug` to be able to create debug builds of Moonlight. * You can install Qt via Homebrew on macOS, but you will need to use `brew install qt --with-debug` to be able to create debug builds of Moonlight.
* You may also use your Linux distro's package manager for the Qt SDK as long as the packages are Qt 5.9 or later. * You may also use your Linux distro's package manager for the Qt SDK as long as the packages are Qt 5.9 or later.
* This step is not required for building on Steam Link, because the Steam Link SDK includes Qt 5.9.
2. Run `git submodule update --init --recursive` from within `moonlight-qt/` 2. Run `git submodule update --init --recursive` from within `moonlight-qt/`
3. Open the project in Qt Creator or build from qmake on the command line. 3. Open the project in Qt Creator or build from qmake on the command line.
* To build a binary for use on non-development machines, use the scripts in the `scripts` folder. * To build a binary for use on non-development machines, use the scripts in the `scripts` folder.
* For Windows builds, use `scripts\generate-installers.bat`. Execute this script from the root of the repository within a Qt command prompt. Ensure WiX and 7-Zip binary directories are in your `%PATH%`. * For Windows builds, use `scripts\generate-installers.bat`. Execute this script from the root of the repository within a Qt command prompt. Ensure WiX and 7-Zip binary directories are 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 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.
* To build from the command line for development use, run `qmake moonlight-qt.pro` then `make debug` or `make release` * To build from the command line for development use, run `qmake moonlight-qt.pro` then `make debug` or `make release`
## Contribute ## Contribute
+45 -50
View File
@@ -60,15 +60,21 @@ macx {
} }
unix:!macx { unix:!macx {
CONFIG += link_pkgconfig soundio CONFIG += link_pkgconfig
PKGCONFIG += openssl sdl2 SDL2_ttf opus PKGCONFIG += openssl sdl2 SDL2_ttf opus
# For libsoundio # SLAudio is used on Steam Link
packagesExist(libpulse) { !config_SL {
PKGCONFIG += libpulse CONFIG += soundio
} }
packagesExist(alsa) {
PKGCONFIG += alsa soundio {
packagesExist(libpulse) {
PKGCONFIG += libpulse
}
packagesExist(alsa) {
PKGCONFIG += alsa
}
} }
packagesExist(libavcodec) { packagesExist(libavcodec) {
@@ -92,7 +98,10 @@ unix:!macx {
} }
win32 { win32 {
LIBS += -llibssl -llibcrypto -lSDL2 -lSDL2_ttf -lavcodec -lavutil -lopus -ld3dx9 LIBS += -llibssl -llibcrypto -lSDL2 -lSDL2_ttf -lavcodec -lavutil -lopus -ld3dx9
CONFIG += ffmpeg soundio CONFIG += ffmpeg
}
win32:!winrt {
CONFIG += soundio
} }
macx { macx {
LIBS += -lssl -lcrypto -lavcodec.58 -lavutil.56 -lopus -framework SDL2 -framework SDL2_ttf LIBS += -lssl -lcrypto -lavcodec.58 -lavutil.56 -lopus -framework SDL2 -framework SDL2_ttf
@@ -128,7 +137,8 @@ SOURCES += \
path.cpp \ path.cpp \
settings/mappingmanager.cpp \ settings/mappingmanager.cpp \
gui/sdlgamepadkeynavigation.cpp \ gui/sdlgamepadkeynavigation.cpp \
streaming/video/overlaymanager.cpp streaming/video/overlaymanager.cpp \
backend/systemproperties.cpp
HEADERS += \ HEADERS += \
utils.h \ utils.h \
@@ -155,7 +165,8 @@ HEADERS += \
path.h \ path.h \
settings/mappingmanager.h \ settings/mappingmanager.h \
gui/sdlgamepadkeynavigation.h \ gui/sdlgamepadkeynavigation.h \
streaming/video/overlaymanager.h streaming/video/overlaymanager.h \
backend/systemproperties.h
# Platform-specific renderers and decoders # Platform-specific renderers and decoders
ffmpeg { ffmpeg {
@@ -202,16 +213,20 @@ libvdpau {
SOURCES += streaming/video/ffmpeg-renderers/vdpau.cpp SOURCES += streaming/video/ffmpeg-renderers/vdpau.cpp
HEADERS += streaming/video/ffmpeg-renderers/vdpau.h HEADERS += streaming/video/ffmpeg-renderers/vdpau.h
} }
config_SLVideo { config_SL {
message(SLVideo decoder/renderer selected) message(Steam Link build configuration selected)
DEFINES += HAVE_SLVIDEO DEFINES += STEAM_LINK HAVE_SLVIDEO HAVE_SLAUDIO
LIBS += -lSLVideo LIBS += -lSLVideo -lSLAudio
SOURCES += streaming/video/sl.cpp SOURCES += \
HEADERS += streaming/video/sl.h streaming/video/slvid.cpp \
streaming/audio/renderers/slaud.cpp
HEADERS += \
streaming/video/slvid.h \
streaming/audio/renderers/slaud.h
} }
win32 { win32:!winrt {
message(DXVA2 renderer selected) message(DXVA2 renderer selected)
SOURCES += \ SOURCES += \
@@ -265,12 +280,14 @@ else:unix: LIBS += -L$$OUT_PWD/../qmdnsengine/ -lqmdnsengine
INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
DEPENDPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine DEPENDPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../soundio/release/ -lsoundio soundio {
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../soundio/debug/ -lsoundio win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../soundio/release/ -lsoundio
else:unix: LIBS += -L$$OUT_PWD/../soundio/ -lsoundio else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../soundio/debug/ -lsoundio
else:unix: LIBS += -L$$OUT_PWD/../soundio/ -lsoundio
INCLUDEPATH += $$PWD/../soundio/libsoundio INCLUDEPATH += $$PWD/../soundio/libsoundio
DEPENDPATH += $$PWD/../soundio/libsoundio DEPENDPATH += $$PWD/../soundio/libsoundio
}
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/release/ -lh264bitstream win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/release/ -lh264bitstream
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/debug/ -lh264bitstream else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/debug/ -lh264bitstream
@@ -279,22 +296,12 @@ else:unix: LIBS += -L$$OUT_PWD/../h264bitstream/ -lh264bitstream
INCLUDEPATH += $$PWD/../h264bitstream/h264bitstream INCLUDEPATH += $$PWD/../h264bitstream/h264bitstream
DEPENDPATH += $$PWD/../h264bitstream/h264bitstream DEPENDPATH += $$PWD/../h264bitstream/h264bitstream
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AntiHooking/release/ -lAntiHooking !winrt {
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AntiHooking/debug/ -lAntiHooking win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AntiHooking/release/ -lAntiHooking
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AntiHooking/debug/ -lAntiHooking
INCLUDEPATH += $$PWD/../AntiHooking INCLUDEPATH += $$PWD/../AntiHooking
DEPENDPATH += $$PWD/../AntiHooking DEPENDPATH += $$PWD/../AntiHooking
defineTest(copyToDestDir) {
files = $$1
for(FILE, files) {
!equals(PWD, $$OUT_PWD) {
QMAKE_POST_LINK += $$QMAKE_COPY $$shell_quote($$shell_path($$PWD/$$FILE)) $$shell_quote($$shell_path($$OUT_PWD)) $$escape_expand(\\n\\t)
}
}
export(QMAKE_POST_LINK)
} }
unix:!macx: { unix:!macx: {
@@ -319,16 +326,7 @@ unix:!macx: {
appstream.files = deploy/linux/com.moonlight_stream.Moonlight.appdata.xml appstream.files = deploy/linux/com.moonlight_stream.Moonlight.appdata.xml
appstream.path = $$PREFIX/$$DATADIR/metainfo/ appstream.path = $$PREFIX/$$DATADIR/metainfo/
appdata.files = SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf INSTALLS += target desktop icons appstream
appdata.path = "$$PREFIX/$$DATADIR/Moonlight Game Streaming Project/Moonlight/"
CONFIG(debug, debug|release) {
# Copy the required data files into the application directory only for debug builds.
# Release builds will use the INSTALLS variable to place these in the correct folders.
copyToDestDir(SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf)
}
INSTALLS += target appdata desktop icons appstream
} }
win32 { win32 {
RC_ICONS = moonlight.ico RC_ICONS = moonlight.ico
@@ -336,9 +334,6 @@ win32 {
QMAKE_TARGET_DESCRIPTION = Moonlight Game Streaming Client QMAKE_TARGET_DESCRIPTION = Moonlight Game Streaming Client
QMAKE_TARGET_PRODUCT = Moonlight QMAKE_TARGET_PRODUCT = Moonlight
# Copy the required data files into the application directory
copyToDestDir(SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf)
CONFIG -= embed_manifest_exe CONFIG -= embed_manifest_exe
QMAKE_LFLAGS += /MANIFEST:embed /MANIFESTINPUT:$${PWD}/Moonlight.exe.manifest QMAKE_LFLAGS += /MANIFEST:embed /MANIFESTINPUT:$${PWD}/Moonlight.exe.manifest
} }
@@ -349,7 +344,7 @@ macx {
QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist
APP_BUNDLE_RESOURCES.files = moonlight.icns SDL_GameControllerDB/gamecontrollerdb.txt ModeSeven.ttf 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) APP_BUNDLE_FRAMEWORKS.files = $$files(../libs/mac/Frameworks/*.framework, true)
+26 -3
View File
@@ -7,6 +7,7 @@
#include <QThread> #include <QThread>
#include <QThreadPool> #include <QThreadPool>
#include <QCoreApplication>
#define SER_HOSTS "hosts" #define SER_HOSTS "hosts"
@@ -130,8 +131,12 @@ private:
emit computerStateChanged(m_Computer); emit computerStateChanged(m_Computer);
} }
// Wait a bit to poll again // Wait a bit to poll again, but do it in 100 ms chunks
QThread::sleep(3); // so we can be interrupted reasonably quickly.
// FIXME: QWaitCondition would be better.
for (int i = 0; i < 30 && !isInterruptionRequested(); i++) {
QThread::msleep(100);
}
} }
} }
@@ -157,6 +162,13 @@ ComputerManager::ComputerManager(QObject *parent)
m_KnownHosts[computer->uuid] = computer; m_KnownHosts[computer->uuid] = computer;
} }
settings.endArray(); settings.endArray();
// To quit in a timely manner, we must block additional requests
// after we receive the aboutToQuit() signal. This is neccessary
// because NvHTTP uses aboutToQuit() to abort requests in progres
// while quitting, however this is a one time signal - additional
// requests would not be aborted and block termination.
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(handleAboutToQuit()));
} }
ComputerManager::~ComputerManager() ComputerManager::~ComputerManager()
@@ -339,6 +351,17 @@ void ComputerManager::deleteHost(NvComputer* computer)
QThreadPool::globalInstance()->start(new DeferredHostDeletionTask(this, computer)); QThreadPool::globalInstance()->start(new DeferredHostDeletionTask(this, computer));
} }
void ComputerManager::handleAboutToQuit()
{
QWriteLocker lock(&m_Lock);
// Interrupt polling threads immediately, so they
// avoid making additional requests while quitting
for (ComputerPollingEntry* entry : m_PollEntries) {
entry->interrupt();
}
}
class PendingPairingTask : public QObject, public QRunnable class PendingPairingTask : public QObject, public QRunnable
{ {
Q_OBJECT Q_OBJECT
@@ -378,7 +401,7 @@ private:
break; break;
} }
} catch (const GfeHttpResponseException& e) { } catch (const GfeHttpResponseException& e) {
emit pairingCompleted(m_Computer, e.toQString()); emit pairingCompleted(m_Computer, "GeForce Experience returned error: " + e.toQString());
} catch (const QtNetworkReplyException& e) { } catch (const QtNetworkReplyException& e) {
emit pairingCompleted(m_Computer, e.toQString()); emit pairingCompleted(m_Computer, e.toQString());
} }
+2
View File
@@ -161,6 +161,8 @@ signals:
void quitAppCompleted(QVariant error); void quitAppCompleted(QVariant error);
private slots: private slots:
void handleAboutToQuit();
void handleComputerStateChanged(NvComputer* computer); void handleComputerStateChanged(NvComputer* computer);
void handleMdnsServiceResolved(MdnsPendingComputer* computer, const QHostAddress& address); void handleMdnsServiceResolved(MdnsPendingComputer* computer, const QHostAddress& address);
+27 -19
View File
@@ -33,6 +33,8 @@ NvHTTP::NvHTTP(QString address, QSslCertificate serverCert) :
// Never use a proxy server // Never use a proxy server
QNetworkProxy noProxy(QNetworkProxy::NoProxy); QNetworkProxy noProxy(QNetworkProxy::NoProxy);
m_Nam.setProxy(noProxy); m_Nam.setProxy(noProxy);
connect(&m_Nam, &QNetworkAccessManager::sslErrors, this, &NvHTTP::handleSslErrors);
} }
void NvHTTP::setServerCert(QSslCertificate serverCert) void NvHTTP::setServerCert(QSslCertificate serverCert)
@@ -377,6 +379,28 @@ NvHTTP::getXmlString(QString xml,
return nullptr; return nullptr;
} }
void NvHTTP::handleSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
{
bool ignoreErrors = true;
if (m_ServerCert.isNull()) {
// We should never make an HTTPS request without a cert
Q_ASSERT(!m_ServerCert.isNull());
return;
}
for (auto error : errors) {
if (m_ServerCert != error.certificate()) {
ignoreErrors = false;
break;
}
}
if (ignoreErrors) {
reply->ignoreSslErrors(errors);
}
}
QString QString
NvHTTP::openConnectionToString(QUrl baseUrl, NvHTTP::openConnectionToString(QUrl baseUrl,
QString command, QString command,
@@ -413,33 +437,17 @@ NvHTTP::openConnection(QUrl baseUrl,
QUuid::createUuid().toRfc4122().toHex() + QUuid::createUuid().toRfc4122().toHex() +
((arguments != nullptr) ? ("&" + arguments) : "")); ((arguments != nullptr) ? ("&" + arguments) : ""));
QNetworkRequest request = QNetworkRequest(url); QNetworkRequest request(url);
// Add our client certificate // Add our client certificate
request.setSslConfiguration(IdentityManager::get()->getSslConfig()); request.setSslConfiguration(IdentityManager::get()->getSslConfig());
QNetworkReply* reply = m_Nam.get(request); QNetworkReply* reply = m_Nam.get(request);
// Assert that we always have a server cert for HTTPS, since
// the request will fail anyway if we do not.
Q_ASSERT(!m_ServerCert.isNull() || baseUrl == m_BaseUrlHttp);
if (!m_ServerCert.isNull()) {
// Pin the server certificate received during pairing
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(QSslError(QSslError::HostNameMismatch, m_ServerCert));
expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, m_ServerCert));
// The SecureTransport backend for Qt TLS on macOS throws CertificateUntrusted
// instead of SelfSignedCertificate, so we will need to allow that error too.
expectedSslErrors.append(QSslError(QSslError::CertificateUntrusted, m_ServerCert));
reply->ignoreSslErrors(expectedSslErrors);
}
// Run the request with a timeout if requested // Run the request with a timeout if requested
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), &loop, SLOT(quit()));
if (timeoutMs) { if (timeoutMs) {
QTimer::singleShot(timeoutMs, &loop, SLOT(quit())); QTimer::singleShot(timeoutMs, &loop, SLOT(quit()));
} }
+6 -1
View File
@@ -113,8 +113,10 @@ private:
QString m_ErrorText; QString m_ErrorText;
}; };
class NvHTTP class NvHTTP : public QObject
{ {
Q_OBJECT
public: public:
enum NvLogLevel { enum NvLogLevel {
NVLL_NONE, NVLL_NONE,
@@ -184,6 +186,9 @@ public:
QUrl m_BaseUrlHttp; QUrl m_BaseUrlHttp;
QUrl m_BaseUrlHttps; QUrl m_BaseUrlHttps;
private: private:
void
handleSslErrors(QNetworkReply* reply, const QList<QSslError>& errors);
QNetworkReply* QNetworkReply*
openConnection(QUrl baseUrl, openConnection(QUrl baseUrl,
QString command, QString command,
+104
View File
@@ -0,0 +1,104 @@
#include "systemproperties.h"
#include "streaming/session.h"
#include "streaming/streamutils.h"
SystemProperties::SystemProperties()
{
isRunningWayland = qgetenv("XDG_SESSION_TYPE") == "wayland";
#ifdef Q_OS_WIN32
isWow64 = QSysInfo::currentCpuArchitecture() != QSysInfo::buildCpuArchitecture();
#else
isWow64 = false;
#endif
#ifndef STEAM_LINK
hasBrowser = true;
#else
hasBrowser = false;
#endif
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
// Populate data that requires talking to SDL. We do it all in one shot
// and cache the results to speed up future queries on this data.
querySdlVideoInfo();
Q_ASSERT(maximumStreamingFrameRate >= 60);
Q_ASSERT(!monitorDesktopResolutions.isEmpty());
Q_ASSERT(!monitorNativeResolutions.isEmpty());
}
QRect SystemProperties::getDesktopResolution(int displayIndex)
{
// Returns default constructed QRect if out of bounds
return monitorDesktopResolutions.value(displayIndex);
}
QRect SystemProperties::getNativeResolution(int displayIndex)
{
// Returns default constructed QRect if out of bounds
return monitorNativeResolutions.value(displayIndex);
}
void SystemProperties::querySdlVideoInfo()
{
monitorDesktopResolutions.clear();
monitorNativeResolutions.clear();
hasHardwareAcceleration = false;
// Never let the maximum drop below 60 FPS
maximumStreamingFrameRate = 60;
SDL_DisplayMode bestMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
SDL_DisplayMode desktopMode;
int err;
err = SDL_GetDesktopDisplayMode(displayIndex, &desktopMode);
if (err == 0) {
monitorDesktopResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDesktopDisplayMode() failed: %s",
SDL_GetError());
}
if (StreamUtils::getRealDesktopMode(displayIndex, &desktopMode)) {
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
// Start at desktop mode and work our way up
bestMode = desktopMode;
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
SDL_DisplayMode mode;
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
if (mode.w == desktopMode.w && mode.h == desktopMode.h) {
if (mode.refresh_rate > bestMode.refresh_rate) {
bestMode = mode;
}
}
}
}
maximumStreamingFrameRate = qMax(maximumStreamingFrameRate, bestMode.refresh_rate);
}
}
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
return;
}
hasHardwareAcceleration =
Session::isHardwareDecodeAvailable(testWindow,
StreamingPreferences::VDS_AUTO,
VIDEO_FORMAT_H264,
1920, 1080, 60);
SDL_DestroyWindow(testWindow);
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <QObject>
#include <QRect>
class SystemProperties : public QObject
{
Q_OBJECT
public:
SystemProperties();
Q_PROPERTY(bool hasHardwareAcceleration MEMBER hasHardwareAcceleration CONSTANT)
Q_PROPERTY(bool isRunningWayland MEMBER isRunningWayland CONSTANT)
Q_PROPERTY(bool isWow64 MEMBER isWow64 CONSTANT)
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT)
Q_INVOKABLE QRect getDesktopResolution(int displayIndex);
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
signals:
void unmappedGamepadsChanged();
private:
void querySdlVideoInfo();
bool hasHardwareAcceleration;
bool isRunningWayland;
bool isWow64;
bool hasBrowser;
QString unmappedGamepads;
int maximumStreamingFrameRate;
QList<QRect> monitorDesktopResolutions;
QList<QRect> monitorNativeResolutions;
};
+1 -1
View File
@@ -59,7 +59,7 @@ public:
void showMessage(QString message, MessageType type) const void showMessage(QString message, MessageType type) const
{ {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN32)
UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND; UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND;
flags |= (type == Info ? MB_ICONINFORMATION : MB_ICONERROR); flags |= (type == Info ? MB_ICONINFORMATION : MB_ICONERROR);
QString title = "Moonlight"; QString title = "Moonlight";
@@ -15,7 +15,7 @@
<li>Hardware accelerated video decoding with VAAPI and VDPAU support</li> <li>Hardware accelerated video decoding with VAAPI and VDPAU support</li>
<li>5.1 surround sound audio support</li> <li>5.1 surround sound audio support</li>
<li>HEVC support for better video compression efficiency</li> <li>HEVC support for better video compression efficiency</li>
<li>Gamepad support with SDL mapping compatibility</li> <li>Gamepad support with force feedback for up to 4 players</li>
</ul> </ul>
</description> </description>
@@ -33,7 +33,27 @@
</screenshots> </screenshots>
<releases> <releases>
<release version="0.9.0" date="2019-02-17"> <release version="0.10.0" date="2019-04-07">
<description>
<p>New features:</p>
<ul>
<li>Redesigned App Grid with larger game artwork and less blank space</li>
<li>Redesigned UI dialogs using Material Design</li>
<li>Added a connection warning overlay (which can be disabled in settings)</li>
<li>Streaming window no longer minimizes when it loses focus in full-screen mode</li>
<li>The Steam Link device is now a supported platform</li>
</ul>
<p>Bugfixes:</p>
<ul>
<li>Fixed several bugs that could cause UI hangs or lag</li>
<li>Fixed rumble support on gamepads with limited rumble motor control</li>
<li>Fixed increased memory usage if the software renderer gets delayed</li>
<li>Fixed crashes on certain desktop configurations when loading the UI</li>
<li>Fixed handling of pairing certificates with a bogus date</li>
</ul>
</description>
</release>
<release version="0.9.1" date="2019-02-17">
<description> <description>
<p>New features:</p> <p>New features:</p>
<ul> <ul>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
# The default HOME is not persistent, so override
# it to a path on the onboard flash. Otherwise our
# pairing data will be lost each reboot.
HOME=/usr/local/moonlight
# Write output to a logfile in /tmp
exec ./bin/moonlight > /tmp/moonlight.log
+3
View File
@@ -0,0 +1,3 @@
name=Moonlight
icon=moonlight.png
run=moonlight.sh
+77 -65
View File
@@ -1,29 +1,20 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Dialogs 1.2
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import AppModel 1.0 import AppModel 1.0
import ComputerManager 1.0 import ComputerManager 1.0
import SdlGamepadKeyNavigation 1.0
GridView { CenteredGridView {
property int computerIndex property int computerIndex
property AppModel appModel : createModel() property AppModel appModel : createModel()
property bool activated
id: appGrid id: appGrid
focus: true focus: true
activeFocusOnTab: true activeFocusOnTab: true
anchors.fill: parent topMargin: 20
anchors.leftMargin: (parent.width % (cellWidth + anchors.rightMargin)) / 2 bottomMargin: 5
anchors.topMargin: 20 cellWidth: 230; cellHeight: 297;
anchors.rightMargin: 5
anchors.bottomMargin: 5
cellWidth: 225; cellHeight: 385;
// The StackView will trigger a visibility change when
// we're pushed onto it, causing our onVisibleChanged
// routine to run, but only if we start as invisible
visible: false
function computerLost() function computerLost()
{ {
@@ -32,23 +23,20 @@ GridView {
} }
Component.onCompleted: { Component.onCompleted: {
// Don't show any highlighted item until interacting with them // Don't show any highlighted item until interacting with them.
// We do this here instead of onActivated to avoid losing the user's
// selection when backing out of a different page of the app.
currentIndex = -1 currentIndex = -1
} }
SdlGamepadKeyNavigation { StackView.onActivated: {
id: gamepadKeyNav appModel.computerLost.connect(computerLost)
activated = true
} }
onVisibleChanged: { StackView.onDeactivating: {
if (visible) { appModel.computerLost.disconnect(computerLost)
appModel.computerLost.connect(computerLost) activated = false
gamepadKeyNav.enable()
}
else {
appModel.computerLost.disconnect(computerLost)
gamepadKeyNav.disable()
}
} }
function createModel() function createModel()
@@ -61,26 +49,44 @@ GridView {
model: appModel model: appModel
delegate: NavigableItemDelegate { delegate: NavigableItemDelegate {
width: 200; height: 335; width: 220; height: 287;
grid: appGrid grid: appGrid
Image { Image {
property bool isPlaceholder: false
id: appIcon id: appIcon
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
y: 20 y: 10
source: model.boxart source: model.boxart
sourceSize {
width: 150 onSourceSizeChanged: {
height: 200 if ((width == 130 && height == 180) || // GFE 2.0 placeholder image
(width == 628 && height == 888) || // GFE 3.0 placeholder image
(width == 200 && height == 266)) // Our no_app_image.png
{
isPlaceholder = true
}
else
{
isPlaceholder = false
}
width = 200
height = 267
} }
width: sourceSize.width
height: sourceSize.height // Display a tooltip with the full name if it's truncated
fillMode: Image.Pad ToolTip.text: model.name
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: (parent.hovered || parent.highlighted) && (!appNameText.visible || appNameText.truncated)
} }
ToolButton { ToolButton {
id: resumeButton id: resumeButton
anchors.verticalCenterOffset: -50 anchors.horizontalCenterOffset: appIcon.isPlaceholder ? -47 : 0
anchors.verticalCenterOffset: appIcon.isPlaceholder ? -75 : -60
anchors.centerIn: appIcon anchors.centerIn: appIcon
visible: model.running visible: model.running
implicitWidth: 125 implicitWidth: 125
@@ -107,7 +113,8 @@ GridView {
ToolButton { ToolButton {
id: quitButton id: quitButton
anchors.verticalCenterOffset: 50 anchors.horizontalCenterOffset: appIcon.isPlaceholder ? 47 : 0
anchors.verticalCenterOffset: appIcon.isPlaceholder ? -75 : 60
anchors.centerIn: appIcon anchors.centerIn: appIcon
visible: model.running visible: model.running
implicitWidth: 125 implicitWidth: 125
@@ -134,20 +141,18 @@ GridView {
Label { Label {
id: appNameText id: appNameText
visible: appIcon.isPlaceholder
text: model.name text: model.name
width: parent.width width: appIcon.width
height: 125 height: model.running ? 175 : appIcon.height
anchors.top: appIcon.bottom anchors.left: appIcon.left
anchors.right: appIcon.right
anchors.bottom: appIcon.bottom
font.pointSize: 22 font.pointSize: 22
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
elide: Text.ElideRight elide: Text.ElideRight
// Display a tooltip with the full name if it's truncated
ToolTip.text: model.name
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: (parent.hovered || parent.highlighted) && truncated
} }
function launchOrResumeSelectedApp() function launchOrResumeSelectedApp()
@@ -168,19 +173,29 @@ GridView {
} }
onClicked: { onClicked: {
// Only allow clicking on the box art for non-running games.
// For running games, buttons will appear to resume or quit which
// will handle starting the game and clicks on the box art will
// be ignored.
if (!model.running) {
launchOrResumeSelectedApp()
}
}
Keys.onReturnPressed: {
// Open the app context menu if activated via the gamepad or keyboard
// for running games. If the game isn't running, the above onClicked
// method will handle the launch.
if (model.running) { if (model.running) {
// This will primarily be keyboard/gamepad driven so use // This will be keyboard/gamepad driven so use
// open() instead of popup() // open() instead of popup()
appContextMenu.open() appContextMenu.open()
} }
else {
launchOrResumeSelectedApp()
}
} }
Keys.onMenuPressed: { Keys.onMenuPressed: {
if (model.running) { if (model.running) {
// This will primarily be keyboard/gamepad driven so use // This will be keyboard/gamepad driven so use
// open() instead of popup() // open() instead of popup()
appContextMenu.open() appContextMenu.open()
} }
@@ -197,10 +212,7 @@ GridView {
NavigableMenuItem { NavigableMenuItem {
parentMenu: appContextMenu parentMenu: appContextMenu
text: model.running ? "Resume Game" : "Launch Game" text: model.running ? "Resume Game" : "Launch Game"
onTriggered: { onTriggered: launchOrResumeSelectedApp()
appContextMenu.close()
launchOrResumeSelectedApp()
}
} }
NavigableMenuItem { NavigableMenuItem {
parentMenu: appContextMenu parentMenu: appContextMenu
@@ -211,15 +223,14 @@ GridView {
} }
} }
MessageDialog { NavigableMessageDialog {
id: quitAppDialog id: quitAppDialog
modality:Qt.WindowModal
property string appName : "" property string appName : ""
property bool segueToStream : false property bool segueToStream : false
property string nextAppName: "" property string nextAppName: ""
property int nextAppIndex: 0 property int nextAppIndex: 0
text:"Are you sure you want to quit " + appName +"? Any unsaved progress will be lost." text:"Are you sure you want to quit " + appName +"? Any unsaved progress will be lost."
standardButtons: StandardButton.Yes | StandardButton.No standardButtons: Dialog.Yes | Dialog.No
function quitApp() { function quitApp() {
var component = Qt.createComponent("QuitSegue.qml") var component = Qt.createComponent("QuitSegue.qml")
@@ -241,20 +252,21 @@ GridView {
appModel.quitRunningApp() appModel.quitRunningApp()
} }
onYes: quitApp()
// For keyboard/gamepad navigation
onAccepted: quitApp() onAccepted: quitApp()
} }
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
// Manually hide the scrollbar to prevent it from being drawn on top
// of the StreamSegue during the transition. It can sometimes get stuck
// there since we're not consistently pumping the event loop while
// starting the stream.
visible: activated
parent: appGrid.parent parent: appGrid.parent
anchors { anchors {
top: appGrid.top top: parent.top
left: appGrid.right right: parent.right
bottom: appGrid.bottom bottom: parent.bottom
leftMargin: -10
} }
} }
} }
+41
View File
@@ -0,0 +1,41 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
GridView {
// Detect Qt 5.11 or earlier using presence of synchronousDrag.
// Prior to 5.12, the leftMargin and rightMargin values did not work.
property bool hasBrokenMargins: this.synchronousDrag === undefined
property int minMargin: 10
property real availableWidth: (parent.width - 2 * minMargin)
property int itemsPerRow: availableWidth / cellWidth
property real horizontalMargin: itemsPerRow < count && availableWidth >= cellWidth ?
(availableWidth % cellWidth) / 2 : minMargin
function updateMargins() {
leftMargin = horizontalMargin
rightMargin = horizontalMargin
if (hasBrokenMargins) {
anchors.leftMargin = leftMargin
anchors.rightMargin = rightMargin
}
}
onHorizontalMarginChanged: {
updateMargins()
}
Component.onCompleted: {
if (hasBrokenMargins) {
// This will cause an anchor conflict with the parent StackView
// which breaks animation, but otherwise the grid will not be
// centered in the window.
anchors.fill = parent
anchors.topMargin = topMargin
anchors.bottomMargin = bottomMargin
}
updateMargins()
}
}
+5 -18
View File
@@ -1,13 +1,10 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import ComputerManager 1.0 import ComputerManager 1.0
import Session 1.0 import Session 1.0
Item { Item {
anchors.fill: parent
function onSearchingComputer() { function onSearchingComputer() {
stageLabel.text = "Establishing connection to PC..." stageLabel.text = "Establishing connection to PC..."
} }
@@ -21,13 +18,8 @@ Item {
errorDialog.open() errorDialog.open()
} }
// The StackView will trigger a visibility change when StackView.onActivated: {
// we're pushed onto it, causing our onVisibleChanged if (!launcher.isExecuted()) {
// routine to run, but only if we start as invisible
visible: false
onVisibleChanged: {
if (visible && !launcher.isExecuted()) {
toolBar.visible = false toolBar.visible = false
launcher.searchingComputer.connect(onSearchingComputer) launcher.searchingComputer.connect(onSearchingComputer)
launcher.quittingApp.connect(onQuittingApp) launcher.quittingApp.connect(onQuittingApp)
@@ -55,16 +47,11 @@ Item {
} }
} }
MessageDialog { ErrorMessageDialog {
id: errorDialog id: errorDialog
modality:Qt.WindowModal
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
onVisibleChanged: { onClosed: {
if (!visible) { Qt.quit()
Qt.quit()
}
} }
} }
} }
+9 -43
View File
@@ -1,13 +1,9 @@
import QtQml 2.2
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import ComputerManager 1.0 import ComputerManager 1.0
Item { Item {
visible: false
function onSearchingComputer() { function onSearchingComputer() {
stageLabel.text = "Establishing connection to PC..." stageLabel.text = "Establishing connection to PC..."
} }
@@ -36,8 +32,8 @@ Item {
quitAppDialog.open() quitAppDialog.open()
} }
onVisibleChanged: { StackView.onActivated: {
if (visible && !launcher.isExecuted()) { if (!launcher.isExecuted()) {
toolBar.visible = false toolBar.visible = false
launcher.searchingComputer.connect(onSearchingComputer) launcher.searchingComputer.connect(onSearchingComputer)
launcher.searchingApp.connect(onSearchingApp) launcher.searchingApp.connect(onSearchingApp)
@@ -66,26 +62,18 @@ Item {
} }
} }
MessageDialog { ErrorMessageDialog {
id: errorDialog id: errorDialog
modality:Qt.WindowModal
icon: StandardIcon.Critical onClosed: {
standardButtons: StandardButton.Ok | StandardButton.Help Qt.quit();
onHelp: {
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
}
onVisibleChanged: {
if (!visible) {
Qt.quit();
}
} }
} }
MessageDialog { NavigableMessageDialog {
id: quitAppDialog id: quitAppDialog
modality:Qt.WindowModal
text:"Are you sure you want to quit " + appName +"? Any unsaved progress will be lost." text:"Are you sure you want to quit " + appName +"? Any unsaved progress will be lost."
standardButtons: StandardButton.Yes | StandardButton.No standardButtons: Dialog.Yes | Dialog.No
property string appName : "" property string appName : ""
function quitApp() { function quitApp() {
@@ -96,29 +84,7 @@ Item {
launcher.quitRunningApp() launcher.quitRunningApp()
} }
onYes: quitApp()
// For keyboard/gamepad navigation
onAccepted: quitApp() onAccepted: quitApp()
onRejected: Qt.quit()
// Exit process if app quit is rejected (reacts also to closing of the
// dialog from title bar's close button).
// Note: this depends on undocumented behavior of visibleChanged()
// signal being emitted before yes() or accepted() has been emitted.
onVisibleChanged: {
if (!visible) {
quitTimer.start()
}
}
Component.onCompleted: {
yes.connect(quitTimer.stop)
accepted.connect(quitTimer.stop)
}
}
Timer {
id: quitTimer
interval: 100
onTriggered: Qt.quit()
} }
} }
+8
View File
@@ -0,0 +1,8 @@
import QtQuick 2.0
import QtQuick.Controls 2.2
import SystemProperties 1.0
NavigableMessageDialog {
standardButtons: Dialog.Ok | (SystemProperties.hasBrowser ? Dialog.Help : 0)
}
+17
View File
@@ -0,0 +1,17 @@
import QtQuick 2.0
import QtQuick.Controls 2.2
Dialog {
parent: overlay
x: Math.round((parent.width - width) / 2)
y: Math.round((parent.height - height) / 2)
onAboutToHide: {
// We must force focus back to the last item for platforms without
// support for more than one active window like Steam Link. If
// we don't, gamepad and keyboard navigation will break after a
// dialog appears.
stackView.forceActiveFocus()
}
}
+7
View File
@@ -11,6 +11,13 @@ MenuItem {
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
focusPolicy: visible ? Qt.TabFocus : Qt.NoFocus focusPolicy: visible ? Qt.TabFocus : Qt.NoFocus
onTriggered: {
// We must close the context menu first or
// it can steal focus from any dialogs that
// onTriggered may spawn.
parentMenu.close()
}
Keys.onReturnPressed: { Keys.onReturnPressed: {
triggered() triggered()
} }
+65
View File
@@ -0,0 +1,65 @@
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
NavigableDialog {
id: dialog
property alias text: dialogLabel.dialogText
property string helpText
property string helpUrl : "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"
property string helpTextSeparator : " "
onOpened: {
// Force keyboard focus on the label so keyboard navigation works
dialogLabel.forceActiveFocus()
}
RowLayout {
spacing: 10
Image {
source: (standardButtons & Dialog.Yes) ?
"qrc:/res/baseline-help_outline-24px.svg" :
"qrc:/res/baseline-error_outline-24px.svg"
sourceSize {
// The icon should be square so use the height as the width too
width: 50
height: 50
}
}
Label {
property string dialogText
id: dialogLabel
text: dialogText + ((helpText && (standardButtons & Dialog.Help)) ? (helpTextSeparator + helpText) : "")
wrapMode: Text.Wrap
elide: Label.ElideRight
// Cap the width so the dialog doesn't grow horizontally forever. This
// will cause word wrap to kick in.
Layout.maximumWidth: 400
Layout.maximumHeight: 400
Keys.onReturnPressed: {
accept()
}
Keys.onEscapePressed: {
reject()
}
}
}
footer: DialogButtonBox {
id: dialogButtonBox
standardButtons: dialog.standardButtons
onHelpRequested: {
Qt.openUrlExternally(helpUrl)
close()
}
}
}
+67 -118
View File
@@ -1,57 +1,36 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
import ComputerModel 1.0 import ComputerModel 1.0
import ComputerManager 1.0 import ComputerManager 1.0
import StreamingPreferences 1.0 import StreamingPreferences 1.0
import SdlGamepadKeyNavigation 1.0
GridView { CenteredGridView {
property ComputerModel computerModel : createModel() property ComputerModel computerModel : createModel()
id: pcGrid id: pcGrid
focus: true focus: true
activeFocusOnTab: true activeFocusOnTab: true
anchors.fill: parent topMargin: 20
anchors.leftMargin: (parent.width % (cellWidth + anchors.rightMargin)) / 2 bottomMargin: 5
anchors.topMargin: 20 cellWidth: 310; cellHeight: 350;
anchors.rightMargin: 5
anchors.bottomMargin: 5
cellWidth: 350; cellHeight: 350;
objectName: "Computers" objectName: "Computers"
// The StackView will trigger a visibility change when
// we're pushed onto it, causing our onVisibleChanged
// routine to run, but only if we start as invisible
visible: false
StreamingPreferences {
id: prefs
}
SdlGamepadKeyNavigation {
id: gamepadKeyNav
}
onVisibleChanged: {
if (visible) {
gamepadKeyNav.enable()
}
else {
gamepadKeyNav.disable()
}
}
Component.onCompleted: { Component.onCompleted: {
// Don't show any highlighted item until interacting with them.
// We do this here instead of onActivated to avoid losing the user's
// selection when backing out of a different page of the app.
currentIndex = -1
}
StackView.onActivated: {
// Setup signals on CM // Setup signals on CM
ComputerManager.computerAddCompleted.connect(addComplete) ComputerManager.computerAddCompleted.connect(addComplete)
}
// Don't show any highlighted item until interacting with them StackView.onDeactivating: {
currentIndex = -1 ComputerManager.computerAddCompleted.disconnect(addComplete)
} }
function pairingComplete(error) function pairingComplete(error)
@@ -62,6 +41,7 @@ GridView {
// Display a failed dialog if we got an error // Display a failed dialog if we got an error
if (error !== undefined) { if (error !== undefined) {
errorDialog.text = error errorDialog.text = error
errorDialog.helpText = ""
errorDialog.open() errorDialog.open()
} }
} }
@@ -69,7 +49,8 @@ GridView {
function addComplete(success) function addComplete(success)
{ {
if (!success) { if (!success) {
errorDialog.text = "Unable to connect to the specified PC. Click the Help button for possible solutions." errorDialog.text = "Unable to connect to the specified PC."
errorDialog.helpText = "Click the Help button for possible solutions."
errorDialog.open() errorDialog.open()
} }
} }
@@ -82,6 +63,27 @@ GridView {
return model return model
} }
Row {
anchors.centerIn: parent
spacing: 5
visible: pcGrid.count === 0
BusyIndicator {
id: searchSpinner
visible: StreamingPreferences.enableMdns
}
Label {
height: searchSpinner.height
elide: Label.ElideRight
text: StreamingPreferences.enableMdns ? "Searching for PCs with NVIDIA GameStream enabled..."
: "Automatic PC discovery is disabled. Add your PC manually."
font.pointSize: 20
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
}
}
model: computerModel model: computerModel
delegate: NavigableItemDelegate { delegate: NavigableItemDelegate {
@@ -91,9 +93,7 @@ GridView {
Image { Image {
id: pcIcon id: pcIcon
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
source: { source: "qrc:/res/ic_tv_white_48px.svg"
model.addPc ? "qrc:/res/ic_add_to_queue_white_48px.svg" : "qrc:/res/ic_tv_white_48px.svg"
}
sourceSize { sourceSize {
width: 200 width: 200
height: 200 height: 200
@@ -106,7 +106,7 @@ GridView {
anchors.horizontalCenter: pcIcon.horizontalCenter anchors.horizontalCenter: pcIcon.horizontalCenter
anchors.verticalCenter: pcIcon.verticalCenter anchors.verticalCenter: pcIcon.verticalCenter
anchors.verticalCenterOffset: -10 anchors.verticalCenterOffset: -10
visible: !model.addPc && !model.statusUnknown && (!model.online || !model.paired) visible: !model.statusUnknown && (!model.online || !model.paired)
source: !model.online ? "qrc:/res/baseline-warning-24px.svg" : "qrc:/res/baseline-lock-24px.svg" source: !model.online ? "qrc:/res/baseline-warning-24px.svg" : "qrc:/res/baseline-lock-24px.svg"
sourceSize { sourceSize {
width: 75 width: 75
@@ -121,7 +121,7 @@ GridView {
anchors.verticalCenterOffset: -10 anchors.verticalCenterOffset: -10
width: 75 width: 75
height: 75 height: 75
visible: !model.addPc && model.statusUnknown visible: model.statusUnknown
} }
Label { Label {
@@ -150,15 +150,12 @@ GridView {
parentMenu: pcContextMenu parentMenu: pcContextMenu
text: "Wake PC" text: "Wake PC"
onTriggered: computerModel.wakeComputer(index) onTriggered: computerModel.wakeComputer(index)
visible: !model.addPc && !model.online && model.wakeable visible: !model.online && model.wakeable
} }
} }
onClicked: { onClicked: {
if (model.addPc) { if (model.online) {
addPcDialog.open()
}
else if (model.online) {
if (model.paired) { if (model.paired) {
// go to game view // go to game view
var component = Qt.createComponent("AppView.qml") var component = Qt.createComponent("AppView.qml")
@@ -178,7 +175,8 @@ GridView {
} }
else { else {
// cannot pair while something is streaming or attempting to pair // cannot pair while something is streaming or attempting to pair
errorDialog.text = "This PC is currently busy. Make sure to quit any running games and try again." errorDialog.text = "You cannot pair while a previous session is still running on the host PC. Quit any running games or reboot the host PC, then try pairing again."
errorDialog.helpText = ""
errorDialog.open() errorDialog.open()
} }
} }
@@ -189,15 +187,13 @@ GridView {
} }
onPressAndHold: { onPressAndHold: {
if (!model.addPc) { // popup() ensures the menu appears under the mouse cursor
// popup() ensures the menu appears under the mouse cursor if (pcContextMenu.popup) {
if (pcContextMenu.popup) { pcContextMenu.popup()
pcContextMenu.popup() }
} else {
else { // Qt 5.9 doesn't have popup()
// Qt 5.9 doesn't have popup() pcContextMenu.open()
pcContextMenu.open()
}
} }
} }
@@ -210,99 +206,52 @@ GridView {
} }
Keys.onMenuPressed: { Keys.onMenuPressed: {
if (!model.addPc) { // We must use open() here so the menu is positioned on
// We must use open() here so the menu is positioned on // the ItemDelegate and not where the mouse cursor is
// the ItemDelegate and not where the mouse cursor is pcContextMenu.open()
pcContextMenu.open()
}
} }
} }
MessageDialog { ErrorMessageDialog {
id: errorDialog id: errorDialog
// don't allow edits to the rest of the window while open
modality:Qt.WindowModal // Using Setup-Guide here instead of Troubleshooting because it's likely that users
icon: StandardIcon.Critical // will arrive here by forgetting to enable GameStream or not forwarding ports.
standardButtons: StandardButton.Ok | StandardButton.Help helpUrl: "https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide"
onHelp: {
// Using Setup-Guide here instead of Troubleshooting because it's likely that users
// will arrive here by forgetting to enable GameStream or not forwarding ports.
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
}
} }
MessageDialog { NavigableMessageDialog {
id: pairDialog id: pairDialog
// don't allow edits to the rest of the window while open // don't allow edits to the rest of the window while open
modality:Qt.WindowModal
property string pin : "0000" property string pin : "0000"
text:"Please enter " + pin + " on your GameStream PC. This dialog will close when pairing is completed." text:"Please enter " + pin + " on your GameStream PC. This dialog will close when pairing is completed."
standardButtons: StandardButton.Cancel standardButtons: Dialog.Cancel
onRejected: { onRejected: {
// FIXME: We should interrupt pairing here // FIXME: We should interrupt pairing here
} }
} }
MessageDialog { NavigableMessageDialog {
id: deletePcDialog id: deletePcDialog
// don't allow edits to the rest of the window while open // don't allow edits to the rest of the window while open
modality:Qt.WindowModal
property int pcIndex : -1; property int pcIndex : -1;
text:"Are you sure you want to remove this PC?" text:"Are you sure you want to remove this PC?"
standardButtons: StandardButton.Yes | StandardButton.No standardButtons: Dialog.Yes | Dialog.No
function deletePc() { function deletePc() {
console.log("deleting PC pairing for PC at index: " + pcIndex) console.log("deleting PC pairing for PC at index: " + pcIndex)
computerModel.deleteComputer(pcIndex); computerModel.deleteComputer(pcIndex);
} }
onYes: deletePc()
// For keyboard/gamepad activation
onAccepted: deletePc() onAccepted: deletePc()
} }
Dialog {
id: addPcDialog
property string label: "Enter the IP address of your GameStream PC"
property string hint: "192.168.1.100"
property alias editText : editTextItem
standardButtons: StandardButton.Ok | StandardButton.Cancel
onVisibleChanged: {
editTextItem.focus = true
editTextItem.selectAll()
}
onAccepted: {
ComputerManager.addNewHost(editText.text, false)
}
ColumnLayout {
Text {
id: addPcDialogLabel
text: addPcDialog.label
}
Rectangle {
implicitWidth: parent.parent.width
height: editTextItem.height
TextInput {
id: editTextItem
inputMethodHints: Qt.ImhPreferUppercase
text: addPcDialog.hint
}
}
}
}
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
parent: pcGrid.parent parent: pcGrid.parent
anchors { anchors {
top: pcGrid.top top: parent.top
left: pcGrid.right right: parent.right
bottom: pcGrid.bottom bottom: parent.bottom
leftMargin: -10
} }
} }
} }
+14 -26
View File
@@ -1,6 +1,5 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import ComputerManager 1.0 import ComputerManager 1.0
import Session 1.0 import Session 1.0
@@ -12,13 +11,6 @@ Item {
property string stageText : "Quitting " + appName + "..." property string stageText : "Quitting " + appName + "..."
anchors.fill: parent
// The StackView will trigger a visibility change when
// we're pushed onto it, causing our onVisibleChanged
// routine to run, but only if we start as invisible
visible: false
function quitAppCompleted(error) function quitAppCompleted(error)
{ {
// Display a failed dialog if we got an error // Display a failed dialog if we got an error
@@ -34,26 +26,25 @@ Item {
stackView.replace(segue) stackView.replace(segue)
} }
else { else {
// Show the toolbar again
toolBar.visible = true
// Exit this view // Exit this view
stackView.pop() stackView.pop()
} }
} }
onVisibleChanged: { StackView.onActivated: {
if (visible) { // Hide the toolbar before we start loading
// Hide the toolbar before we start loading toolBar.visible = false
toolBar.visible = false
// Connect the quit completion signal // Connect the quit completion signal
ComputerManager.quitAppCompleted.connect(quitAppCompleted) ComputerManager.quitAppCompleted.connect(quitAppCompleted)
} }
else {
// Disconnect the signal StackView.onDeactivating: {
ComputerManager.quitAppCompleted.disconnect(quitAppCompleted) // Show the toolbar again
} toolBar.visible = true
// Disconnect the signal
ComputerManager.quitAppCompleted.disconnect(quitAppCompleted)
} }
Row { Row {
@@ -75,10 +66,7 @@ Item {
} }
} }
MessageDialog { ErrorMessageDialog {
id: errorDialog id: errorDialog
modality:Qt.WindowModal
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
} }
} }
+88 -83
View File
@@ -4,11 +4,11 @@ import QtQuick.Controls 2.2
import StreamingPreferences 1.0 import StreamingPreferences 1.0
import ComputerManager 1.0 import ComputerManager 1.0
import SdlGamepadKeyNavigation 1.0 import SdlGamepadKeyNavigation 1.0
import SystemProperties 1.0
Flickable { Flickable {
id: settingsPage id: settingsPage
objectName: "Settings" objectName: "Settings"
anchors.fill: parent
contentWidth: settingsColumn1.width > settingsColumn2.width ? settingsColumn1.width : settingsColumn2.width contentWidth: settingsColumn1.width > settingsColumn2.width ? settingsColumn1.width : settingsColumn2.width
contentHeight: settingsColumn1.height > settingsColumn2.height ? settingsColumn1.height : settingsColumn2.height contentHeight: settingsColumn1.height > settingsColumn2.height ? settingsColumn1.height : settingsColumn2.height
@@ -24,31 +24,15 @@ Flickable {
} }
} }
StreamingPreferences { StackView.onActivated: {
id: prefs SdlGamepadKeyNavigation.setSettingsMode(true)
} }
// The StackView will trigger a visibility change when StackView.onDeactivating: {
// we're pushed onto it, causing our onVisibleChanged SdlGamepadKeyNavigation.setSettingsMode(false)
// routine to run, but only if we start as invisible
visible: false
SdlGamepadKeyNavigation { // Save the prefs so the Session can observe the changes
id: gamepadKeyNav StreamingPreferences.save()
}
onVisibleChanged: {
if (visible) {
gamepadKeyNav.setSettingsMode(true)
gamepadKeyNav.enable()
}
else {
gamepadKeyNav.disable()
}
}
Component.onDestruction: {
prefs.save()
} }
Column { Column {
@@ -98,10 +82,10 @@ Flickable {
// Some platforms have different desktop resolutions // Some platforms have different desktop resolutions
// and native resolutions (like macOS with Retina displays) // and native resolutions (like macOS with Retina displays)
if (displayResIndex == 0) { if (displayResIndex == 0) {
screenRect = prefs.getDesktopResolution(displayIndex) screenRect = SystemProperties.getDesktopResolution(displayIndex)
} }
else { else {
screenRect = prefs.getNativeResolution(displayIndex) screenRect = SystemProperties.getNativeResolution(displayIndex)
} }
if (screenRect.width === 0) { if (screenRect.width === 0) {
@@ -140,8 +124,8 @@ Flickable {
// load the saved width/height, and iterate through the ComboBox until a match is found // load the saved width/height, and iterate through the ComboBox until a match is found
// and set it to that index. // and set it to that index.
var saved_width = prefs.width var saved_width = StreamingPreferences.width
var saved_height = prefs.height var saved_height = StreamingPreferences.height
currentIndex = 0 currentIndex = 0
for (var i = 0; i < resolutionListModel.count; i++) { for (var i = 0; i < resolutionListModel.count; i++) {
var el_width = parseInt(resolutionListModel.get(i).video_width); var el_width = parseInt(resolutionListModel.get(i).video_width);
@@ -190,12 +174,14 @@ Flickable {
var selectedHeight = parseInt(resolutionListModel.get(currentIndex).video_height) var selectedHeight = parseInt(resolutionListModel.get(currentIndex).video_height)
// Only modify the bitrate if the values actually changed // Only modify the bitrate if the values actually changed
if (prefs.width !== selectedWidth || prefs.height !== selectedHeight) { if (StreamingPreferences.width !== selectedWidth || StreamingPreferences.height !== selectedHeight) {
prefs.width = selectedWidth StreamingPreferences.width = selectedWidth
prefs.height = selectedHeight StreamingPreferences.height = selectedHeight
prefs.bitrateKbps = prefs.getDefaultBitrate(prefs.width, prefs.height, prefs.fps); StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width,
slider.value = prefs.bitrateKbps StreamingPreferences.height,
StreamingPreferences.fps);
slider.value = StreamingPreferences.bitrateKbps
} }
} }
} }
@@ -204,15 +190,14 @@ Flickable {
function createModel() { function createModel() {
var fpsListModel = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', parent, '') var fpsListModel = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', parent, '')
// Get the max supported FPS on this system var max_fps = SystemProperties.maximumStreamingFrameRate
var max_fps = prefs.getMaximumStreamingFrameRate();
// Default entries // Default entries
fpsListModel.append({"text": "30 FPS", "video_fps": "30"}) fpsListModel.append({"text": "30 FPS", "video_fps": "30"})
fpsListModel.append({"text": "60 FPS", "video_fps": "60"}) fpsListModel.append({"text": "60 FPS", "video_fps": "60"})
// Add unsupported FPS values that come before the display max FPS // Add unsupported FPS values that come before the display max FPS
if (prefs.unsupportedFps) { if (StreamingPreferences.unsupportedFps) {
if (max_fps > 90) { if (max_fps > 90) {
fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"}) fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"})
} }
@@ -225,7 +210,7 @@ Flickable {
// handle wonky displays that report just over 60 Hz. // handle wonky displays that report just over 60 Hz.
if (max_fps > 64) { if (max_fps > 64) {
// Mark any FPS value greater than 120 as unsupported // Mark any FPS value greater than 120 as unsupported
if (prefs.unsupportedFps && max_fps > 120) { if (StreamingPreferences.unsupportedFps && max_fps > 120) {
fpsListModel.append({"text": max_fps+" FPS (Unsupported)", "video_fps": ""+max_fps}) fpsListModel.append({"text": max_fps+" FPS (Unsupported)", "video_fps": ""+max_fps})
} }
else if (max_fps > 120) { else if (max_fps > 120) {
@@ -237,7 +222,7 @@ Flickable {
} }
// Add unsupported FPS values that come after the display max FPS // Add unsupported FPS values that come after the display max FPS
if (prefs.unsupportedFps) { if (StreamingPreferences.unsupportedFps) {
if (max_fps < 90) { if (max_fps < 90) {
fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"}) fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"})
} }
@@ -252,7 +237,7 @@ Flickable {
function reinitialize() { function reinitialize() {
model = createModel() model = createModel()
var saved_fps = prefs.fps var saved_fps = StreamingPreferences.fps
currentIndex = 0 currentIndex = 0
for (var i = 0; i < model.count; i++) { for (var i = 0; i < model.count; i++) {
var el_fps = parseInt(model.get(i).video_fps); var el_fps = parseInt(model.get(i).video_fps);
@@ -279,11 +264,13 @@ Flickable {
var selectedFps = parseInt(model.get(currentIndex).video_fps) var selectedFps = parseInt(model.get(currentIndex).video_fps)
// Only modify the bitrate if the values actually changed // Only modify the bitrate if the values actually changed
if (prefs.fps !== selectedFps) { if (StreamingPreferences.fps !== selectedFps) {
prefs.fps = selectedFps StreamingPreferences.fps = selectedFps
prefs.bitrateKbps = prefs.getDefaultBitrate(prefs.width, prefs.height, prefs.fps); StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width,
slider.value = prefs.bitrateKbps StreamingPreferences.height,
StreamingPreferences.fps);
slider.value = StreamingPreferences.bitrateKbps
} }
} }
} }
@@ -308,7 +295,7 @@ Flickable {
Slider { Slider {
id: slider id: slider
value: prefs.bitrateKbps value: StreamingPreferences.bitrateKbps
stepSize: 500 stepSize: 500
from : 500 from : 500
@@ -319,7 +306,7 @@ Flickable {
onValueChanged: { onValueChanged: {
bitrateTitle.text = "Video bitrate: " + (value / 1000.0) + " Mbps" bitrateTitle.text = "Video bitrate: " + (value / 1000.0) + " Mbps"
prefs.bitrateKbps = value StreamingPreferences.bitrateKbps = value
} }
} }
@@ -334,7 +321,7 @@ Flickable {
AutoResizingComboBox { AutoResizingComboBox {
// 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: {
var savedWm = prefs.windowMode var savedWm = StreamingPreferences.windowMode
currentIndex = 0 currentIndex = 0
for (var i = 0; i < windowModeListModel.count; i++) { for (var i = 0; i < windowModeListModel.count; i++) {
var thisWm = windowModeListModel.get(i).val; var thisWm = windowModeListModel.get(i).val;
@@ -365,7 +352,7 @@ Flickable {
} }
} }
onActivated: { onActivated: {
prefs.windowMode = windowModeListModel.get(currentIndex).val StreamingPreferences.windowMode = windowModeListModel.get(currentIndex).val
} }
ToolTip.delay: 1000 ToolTip.delay: 1000
@@ -379,9 +366,9 @@ Flickable {
hoverEnabled: true hoverEnabled: true
text: "V-Sync" text: "V-Sync"
font.pointSize: 12 font.pointSize: 12
checked: prefs.enableVsync checked: StreamingPreferences.enableVsync
onCheckedChanged: { onCheckedChanged: {
prefs.enableVsync = checked StreamingPreferences.enableVsync = checked
} }
ToolTip.delay: 1000 ToolTip.delay: 1000
@@ -395,10 +382,10 @@ Flickable {
hoverEnabled: true hoverEnabled: true
text: "Frame pacing" text: "Frame pacing"
font.pointSize: 12 font.pointSize: 12
enabled: prefs.enableVsync enabled: StreamingPreferences.enableVsync
checked: prefs.enableVsync && prefs.framePacing checked: StreamingPreferences.enableVsync && StreamingPreferences.framePacing
onCheckedChanged: { onCheckedChanged: {
prefs.framePacing = checked StreamingPreferences.framePacing = checked
} }
ToolTip.delay: 1000 ToolTip.delay: 1000
ToolTip.timeout: 5000 ToolTip.timeout: 5000
@@ -431,7 +418,7 @@ Flickable {
AutoResizingComboBox { AutoResizingComboBox {
// 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: {
var saved_audio = prefs.audioConfig var saved_audio = StreamingPreferences.audioConfig
currentIndex = 0 currentIndex = 0
for (var i = 0; i < audioListModel.count; i++) { for (var i = 0; i < audioListModel.count; i++) {
var el_audio = audioListModel.get(i).val; var el_audio = audioListModel.get(i).val;
@@ -458,7 +445,7 @@ Flickable {
} }
// ::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 : {
prefs.audioConfig = audioListModel.get(currentIndex).val StreamingPreferences.audioConfig = audioListModel.get(currentIndex).val
} }
} }
@@ -480,9 +467,19 @@ Flickable {
id: startMaximizedCheck id: startMaximizedCheck
text: "Maximize Moonlight window on startup" text: "Maximize Moonlight window on startup"
font.pointSize: 12 font.pointSize: 12
checked: !prefs.startWindowed checked: !StreamingPreferences.startWindowed
onCheckedChanged: { onCheckedChanged: {
prefs.startWindowed = !checked StreamingPreferences.startWindowed = !checked
}
}
CheckBox {
id: connectionWarningsCheck
text: "Show connection quality warnings"
font.pointSize: 12
checked: StreamingPreferences.connectionWarnings
onCheckedChanged: {
StreamingPreferences.connectionWarnings = checked
} }
} }
} }
@@ -510,9 +507,9 @@ Flickable {
id: singleControllerCheck id: singleControllerCheck
text: "Force gamepad #1 always present" text: "Force gamepad #1 always present"
font.pointSize: 12 font.pointSize: 12
checked: !prefs.multiController checked: !StreamingPreferences.multiController
onCheckedChanged: { onCheckedChanged: {
prefs.multiController = !checked StreamingPreferences.multiController = !checked
} }
ToolTip.delay: 1000 ToolTip.delay: 1000
@@ -527,9 +524,9 @@ Flickable {
hoverEnabled: true hoverEnabled: true
text: "Raw mouse input" text: "Raw mouse input"
font.pointSize: 12 font.pointSize: 12
checked: !prefs.mouseAcceleration checked: !StreamingPreferences.mouseAcceleration
onCheckedChanged: { onCheckedChanged: {
prefs.mouseAcceleration = !checked StreamingPreferences.mouseAcceleration = !checked
} }
ToolTip.delay: 1000 ToolTip.delay: 1000
@@ -555,9 +552,9 @@ Flickable {
id: optimizeGameSettingsCheck id: optimizeGameSettingsCheck
text: "Optimize game settings" text: "Optimize game settings"
font.pointSize: 12 font.pointSize: 12
checked: prefs.gameOptimizations checked: StreamingPreferences.gameOptimizations
onCheckedChanged: { onCheckedChanged: {
prefs.gameOptimizations = checked StreamingPreferences.gameOptimizations = checked
} }
} }
@@ -565,9 +562,9 @@ Flickable {
id: audioPcCheck id: audioPcCheck
text: "Play audio on host PC" text: "Play audio on host PC"
font.pointSize: 12 font.pointSize: 12
checked: prefs.playAudioOnHost checked: StreamingPreferences.playAudioOnHost
onCheckedChanged: { onCheckedChanged: {
prefs.playAudioOnHost = checked StreamingPreferences.playAudioOnHost = checked
} }
} }
} }
@@ -595,7 +592,7 @@ Flickable {
AutoResizingComboBox { AutoResizingComboBox {
// 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: {
var saved_vds = prefs.videoDecoderSelection var saved_vds = StreamingPreferences.videoDecoderSelection
currentIndex = 0 currentIndex = 0
for (var i = 0; i < decoderListModel.count; i++) { for (var i = 0; i < decoderListModel.count; i++) {
var el_vds = decoderListModel.get(i).val; var el_vds = decoderListModel.get(i).val;
@@ -626,7 +623,7 @@ Flickable {
} }
// ::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 : {
prefs.videoDecoderSelection = decoderListModel.get(currentIndex).val StreamingPreferences.videoDecoderSelection = decoderListModel.get(currentIndex).val
} }
} }
@@ -641,7 +638,7 @@ Flickable {
AutoResizingComboBox { AutoResizingComboBox {
// 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: {
var saved_vcc = prefs.videoCodecConfig var saved_vcc = StreamingPreferences.videoCodecConfig
currentIndex = 0 currentIndex = 0
for(var i = 0; i < codecListModel.count; i++) { for(var i = 0; i < codecListModel.count; i++) {
var el_vcc = codecListModel.get(i).val; var el_vcc = codecListModel.get(i).val;
@@ -679,7 +676,7 @@ Flickable {
} }
// ::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 : {
prefs.videoCodecConfig = codecListModel.get(currentIndex).val StreamingPreferences.videoCodecConfig = codecListModel.get(currentIndex).val
} }
} }
@@ -687,13 +684,17 @@ Flickable {
id: unlockUnsupportedFps id: unlockUnsupportedFps
text: "Unlock unsupported FPS options" text: "Unlock unsupported FPS options"
font.pointSize: 12 font.pointSize: 12
checked: prefs.unsupportedFps checked: StreamingPreferences.unsupportedFps
onCheckedChanged: { onCheckedChanged: {
prefs.unsupportedFps = checked // This is called on init, so only do the work if we've
// actually changed the value.
if (StreamingPreferences.unsupportedFps != checked) {
StreamingPreferences.unsupportedFps = checked
// The selectable FPS values depend on whether // The selectable FPS values depend on whether
// this option is enabled or not // this option is enabled or not
fpsComboBox.reinitialize() fpsComboBox.reinitialize()
}
} }
} }
@@ -701,18 +702,22 @@ Flickable {
id: enableMdns id: enableMdns
text: "Automatically find PCs on the local network (Recommended)" text: "Automatically find PCs on the local network (Recommended)"
font.pointSize: 12 font.pointSize: 12
checked: prefs.enableMdns checked: StreamingPreferences.enableMdns
onCheckedChanged: { onCheckedChanged: {
prefs.enableMdns = checked // This is called on init, so only do the work if we've
// actually changed the value.
if (StreamingPreferences.enableMdns != checked) {
StreamingPreferences.enableMdns = checked
// We must save the updated preference to ensure // We must save the updated preference to ensure
// ComputerManager can observe the change internally. // ComputerManager can observe the change internally.
prefs.save() 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()
ComputerManager.startPolling() ComputerManager.startPolling()
}
} }
} }
} }
@@ -721,9 +726,9 @@ Flickable {
id: quitAppAfter id: quitAppAfter
text: "Quit app after quitting session" text: "Quit app after quitting session"
font.pointSize: 12 font.pointSize: 12
checked: prefs.quitAppAfter checked: StreamingPreferences.quitAppAfter
onCheckedChanged: { onCheckedChanged: {
prefs.quitAppAfter = checked StreamingPreferences.quitAppAfter = checked
} }
} }
} }
+41 -46
View File
@@ -1,9 +1,7 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import ComputerManager 1.0
import SdlGamepadKeyNavigation 1.0 import SdlGamepadKeyNavigation 1.0
import Session 1.0 import Session 1.0
@@ -13,13 +11,6 @@ Item {
property string stageText : "Starting " + appName + "..." property string stageText : "Starting " + appName + "..."
property bool quitAfter : false property bool quitAfter : false
anchors.fill: parent
// The StackView will trigger a visibility change when
// we're pushed onto it, causing our onVisibleChanged
// routine to run, but only if we start as invisible
visible: false
function stageStarting(stage) function stageStarting(stage)
{ {
// Update the spinner text // Update the spinner text
@@ -28,8 +19,8 @@ Item {
function stageFailed(stage, errorCode) function stageFailed(stage, errorCode)
{ {
// Display the error dialog after Session::exec() returns
errorDialog.text = "Starting " + stage + " failed: Error " + errorCode errorDialog.text = "Starting " + stage + " failed: Error " + errorCode
errorDialog.open()
} }
function connectionStarted() function connectionStarted()
@@ -38,6 +29,7 @@ Item {
// see them briefly when we pop off the StackView // see them briefly when we pop off the StackView
stageSpinner.visible = false stageSpinner.visible = false
stageLabel.visible = false stageLabel.visible = false
hintText.visible = false
// Hide the window now that streaming has begun // Hide the window now that streaming has begun
window.visible = false window.visible = false
@@ -54,7 +46,7 @@ Item {
// This toast appears for 3 seconds, just shorter than how long // This toast appears for 3 seconds, just shorter than how long
// Session will wait for it to be displayed. This gives it time // Session will wait for it to be displayed. This gives it time
// to transition to invisible before continuing. // to transition to invisible before continuing.
var toast = Qt.createQmlObject('import QtQuick.Controls 2.3; ToolTip {}', parent, '') var toast = Qt.createQmlObject('import QtQuick.Controls 2.2; ToolTip {}', parent, '')
toast.text = text toast.text = text
toast.timeout = 3000 toast.timeout = 3000
toast.visible = true toast.visible = true
@@ -72,6 +64,9 @@ Item {
function sessionFinished() function sessionFinished()
{ {
// Enable GUI gamepad usage now
SdlGamepadKeyNavigation.enable()
if (quitAfter) { if (quitAfter) {
if (errorDialog.text) { if (errorDialog.text) {
// Quit when the error dialog is acknowledged // Quit when the error dialog is acknowledged
@@ -98,42 +93,53 @@ Item {
} }
} }
// It's important that we don't call enable() here StackView.onDeactivating: {
// or it may interfere with the Session instance // Show the toolbar again when popped off the stack
// getting notified of initial connected gamepads. toolBar.visible = true
SdlGamepadKeyNavigation {
id: gamepadKeyNav // Enable GUI gamepad usage now
SdlGamepadKeyNavigation.enable()
} }
onVisibleChanged: { StackView.onActivated: {
if (visible) { // Hide the toolbar before we start loading
// Hide the toolbar before we start loading toolBar.visible = false
toolBar.visible = false
// Hook up our signals
session.stageStarting.connect(stageStarting)
session.stageFailed.connect(stageFailed)
session.connectionStarted.connect(connectionStarted)
session.displayLaunchError.connect(displayLaunchError)
session.displayLaunchWarning.connect(displayLaunchWarning)
session.quitStarting.connect(quitStarting)
session.sessionFinished.connect(sessionFinished)
// Kick off the stream
streamLoader.active = true
}
Loader {
id: streamLoader
active: false
asynchronous: true
onLoaded: {
// Set the hint text. We do this here rather than // Set the hint text. We do this here rather than
// in the hintText control itself to synchronize // in the hintText control itself to synchronize
// with Session.exec() which requires no concurrent // with Session.exec() which requires no concurrent
// gamepad usage. // gamepad usage.
hintText.text = gamepadKeyNav.getConnectedGamepads() > 0 ? hintText.text = SdlGamepadKeyNavigation.getConnectedGamepads() > 0 ?
"Tip: Press Start+Select+L1+R1 to disconnect your session" : "Tip: Press Start+Select+L1+R1 to disconnect your session" :
"Tip: Press Ctrl+Alt+Shift+Q to disconnect your session" "Tip: Press Ctrl+Alt+Shift+Q to disconnect your session"
// Hook up our signals // Stop GUI gamepad usage now
session.stageStarting.connect(stageStarting) SdlGamepadKeyNavigation.disable()
session.stageFailed.connect(stageFailed)
session.connectionStarted.connect(connectionStarted)
session.displayLaunchError.connect(displayLaunchError)
session.displayLaunchWarning.connect(displayLaunchWarning)
session.quitStarting.connect(quitStarting)
session.sessionFinished.connect(sessionFinished)
// Run the streaming session to completion // Run the streaming session to completion
session.exec(Screen.virtualX, Screen.virtualY) session.exec(Screen.virtualX, Screen.virtualY)
} }
else if (!quitAfter) {
// Show the toolbar again when we become hidden sourceComponent: Item {}
toolBar.visible = true
}
} }
Row { Row {
@@ -166,21 +172,10 @@ Item {
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
MessageDialog { ErrorMessageDialog {
id: errorDialog id: errorDialog
modality:Qt.WindowModal
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok | StandardButton.Help
onAccepted: {
if (quitAfter) {
Qt.quit()
}
}
onHelp: {
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
onClosed: {
if (quitAfter) { if (quitAfter) {
Qt.quit() Qt.quit()
} }
+1 -17
View File
@@ -22,18 +22,6 @@ QVariant ComputerModel::data(const QModelIndex& index, int role) const
return QVariant(); return QVariant();
} }
if (index.row() == m_Computers.count()) {
// We insert a synthetic item at the end for the Add PC option
switch (role) {
case NameRole:
return "Add PC";
case AddPcRole:
return true;
default:
return QVariant();
}
}
Q_ASSERT(index.row() < m_Computers.count()); Q_ASSERT(index.row() < m_Computers.count());
NvComputer* computer = m_Computers[index.row()]; NvComputer* computer = m_Computers[index.row()];
@@ -50,8 +38,6 @@ QVariant ComputerModel::data(const QModelIndex& index, int role) const
return computer->currentGameId != 0; return computer->currentGameId != 0;
case WakeableRole: case WakeableRole:
return !computer->macAddress.isEmpty(); return !computer->macAddress.isEmpty();
case AddPcRole:
return false;
case StatusUnknownRole: case StatusUnknownRole:
return computer->state == NvComputer::CS_UNKNOWN; return computer->state == NvComputer::CS_UNKNOWN;
default: default:
@@ -67,8 +53,7 @@ int ComputerModel::rowCount(const QModelIndex& parent) const
return 0; return 0;
} }
// Add PC placeholder counts as 1 return m_Computers.count();
return m_Computers.count() + 1;
} }
QHash<int, QByteArray> ComputerModel::roleNames() const QHash<int, QByteArray> ComputerModel::roleNames() const
@@ -79,7 +64,6 @@ QHash<int, QByteArray> ComputerModel::roleNames() const
names[OnlineRole] = "online"; names[OnlineRole] = "online";
names[PairedRole] = "paired"; names[PairedRole] = "paired";
names[BusyRole] = "busy"; names[BusyRole] = "busy";
names[AddPcRole] = "addPc";
names[WakeableRole] = "wakeable"; names[WakeableRole] = "wakeable";
names[StatusUnknownRole] = "statusUnknown"; names[StatusUnknownRole] = "statusUnknown";
+1 -2
View File
@@ -14,8 +14,7 @@ class ComputerModel : public QAbstractListModel
PairedRole, PairedRole,
BusyRole, BusyRole,
WakeableRole, WakeableRole,
StatusUnknownRole, StatusUnknownRole
AddPcRole
}; };
public: public:
+95 -47
View File
@@ -1,12 +1,13 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import ComputerManager 1.0 import ComputerManager 1.0
import AutoUpdateChecker 1.0 import AutoUpdateChecker 1.0
import StreamingPreferences 1.0 import StreamingPreferences 1.0
import SystemProperties 1.0
import SdlGamepadKeyNavigation 1.0
ApplicationWindow { ApplicationWindow {
property bool pollingActive: false property bool pollingActive: false
@@ -16,10 +17,10 @@ ApplicationWindow {
width: 1280 width: 1280
height: 600 height: 600
visibility: prefs.startWindowed ? "Windowed" : "Maximized" visibility: StreamingPreferences.startWindowed ? "Windowed" : "Maximized"
StreamingPreferences { Component.onCompleted: {
id: prefs SdlGamepadKeyNavigation.enable()
} }
StackView { StackView {
@@ -137,19 +138,18 @@ ApplicationWindow {
// onAfterRendering call and potentially reenter this code. // onAfterRendering call and potentially reenter this code.
initialized = true; initialized = true;
if (prefs.isRunningWayland()) { if (SystemProperties.isRunningWayland) {
waylandDialog.open() waylandDialog.open()
} }
else if (prefs.isWow64()) { else if (SystemProperties.isWow64) {
wow64Dialog.open() wow64Dialog.open()
} }
else if (!prefs.hasAnyHardwareAcceleration()) { else if (!SystemProperties.hasHardwareAcceleration) {
noHwDecoderDialog.open() noHwDecoderDialog.open()
} }
var unmappedGamepads = prefs.getUnmappedGamepads() if (SystemProperties.unmappedGamepads) {
if (unmappedGamepads) { unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
unmappedGamepadDialog.unmappedGamepads = unmappedGamepads
unmappedGamepadDialog.open() unmappedGamepadDialog.open()
} }
} }
@@ -213,6 +213,39 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
} }
NavigableToolButton {
id: addPcButton
visible: stackView.currentItem.objectName === "Computers"
Image {
source: "qrc:/res/ic_add_to_queue_white_48px.svg"
anchors.centerIn: parent
sourceSize {
width: toolBar.height - toolBar.anchors.bottomMargin - toolBar.anchors.topMargin
height: toolBar.height - toolBar.anchors.bottomMargin - toolBar.anchors.topMargin
}
}
ToolTip.delay: 1000
ToolTip.timeout: 3000
ToolTip.visible: hovered
ToolTip.text: "Add PC manually" + (newPcShortcut.nativeText ? (" ("+newPcShortcut.nativeText+")") : "")
Shortcut {
id: newPcShortcut
sequence: StandardKey.New
onActivated: addPcButton.clicked()
}
onClicked: {
addPcDialog.open()
}
Keys.onDownPressed: {
stackView.currentItem.forceActiveFocus(Qt.TabFocus)
}
}
NavigableToolButton { NavigableToolButton {
property string browserUrl: "" property string browserUrl: ""
@@ -256,6 +289,7 @@ ApplicationWindow {
NavigableToolButton { NavigableToolButton {
id: helpButton id: helpButton
visible: SystemProperties.hasBrowser
Image { Image {
source: "qrc:/res/question_mark.svg" source: "qrc:/res/question_mark.svg"
@@ -342,67 +376,81 @@ ApplicationWindow {
} }
} }
MessageDialog { ErrorMessageDialog {
id: noHwDecoderDialog id: noHwDecoderDialog
modality:Qt.WindowModal
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok | StandardButton.Help
text: "No functioning hardware accelerated H.264 video decoder was detected by Moonlight. " + text: "No functioning hardware accelerated H.264 video decoder was detected by Moonlight. " +
"Your streaming performance may be severely degraded in this configuration. " + "Your streaming performance may be severely degraded in this configuration."
"Click the Help button for more information on solving this problem." helpText: "Click the Help button for more information on solving this problem."
onHelp: { helpUrl: "https://github.com/moonlight-stream/moonlight-docs/wiki/Fixing-Hardware-Decoding-Problems"
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Fixing-Hardware-Decoding-Problems");
}
} }
MessageDialog { ErrorMessageDialog {
id: waylandDialog id: waylandDialog
modality:Qt.WindowModal
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok | StandardButton.Help
text: "Moonlight does not support hardware acceleration on Wayland. Continuing on Wayland may result in poor streaming performance. " + text: "Moonlight does not support hardware acceleration on Wayland. Continuing on Wayland may result in poor streaming performance. " +
"Please switch to an X session for optimal performance." "Please switch to an X session for optimal performance."
onHelp: { helpText: "Click the Help button for more information."
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Fixing-Hardware-Decoding-Problems"); helpUrl: "https://github.com/moonlight-stream/moonlight-docs/wiki/Fixing-Hardware-Decoding-Problems"
}
} }
MessageDialog { NavigableMessageDialog {
id: wow64Dialog id: wow64Dialog
modality:Qt.WindowModal standardButtons: Dialog.Ok | Dialog.Cancel
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok | StandardButton.Cancel
text: "This PC is running a 64-bit version of Windows. Please download the x64 version of Moonlight for the best streaming performance." text: "This PC is running a 64-bit version of Windows. Please download the x64 version of Moonlight for the best streaming performance."
onAccepted: { onAccepted: {
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-qt/releases"); Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-qt/releases");
} }
} }
MessageDialog { ErrorMessageDialog {
id: unmappedGamepadDialog id: unmappedGamepadDialog
property string unmappedGamepads : "" property string unmappedGamepads : ""
modality:Qt.WindowModal text: "Moonlight detected gamepads without a mapping:\n" + unmappedGamepads
icon: StandardIcon.Warning helpTextSeparator: "\n\n"
standardButtons: StandardButton.Ok | StandardButton.Help helpText: "Click the Help button for information on how to map your gamepads."
text: "Moonlight detected gamepads without a proper mapping. " + helpUrl: "https://github.com/moonlight-stream/moonlight-docs/wiki/Gamepad-Mapping"
"The following gamepads will not function until this is resolved: " + unmappedGamepads + "\n\n" +
"Click the Help button for information on how to map your gamepads."
onHelp: {
Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Gamepad-Mapping");
}
} }
// This dialog appears when quitting via keyboard or gamepad button // This dialog appears when quitting via keyboard or gamepad button
MessageDialog { NavigableMessageDialog {
id: quitConfirmationDialog id: quitConfirmationDialog
modality:Qt.WindowModal standardButtons: Dialog.Yes | Dialog.No
icon: StandardIcon.Warning
standardButtons: StandardButton.Yes | StandardButton.No
text: "Are you sure you want to quit?" text: "Are you sure you want to quit?"
onYes: Qt.quit()
// For keyboard/gamepad navigation // For keyboard/gamepad navigation
onAccepted: Qt.quit() onAccepted: Qt.quit()
} }
NavigableDialog {
id: addPcDialog
property string label: "Enter the IP address of your GameStream PC:"
standardButtons: Dialog.Ok | Dialog.Cancel
onOpened: {
// Force keyboard focus on the textbox so keyboard navigation works
editText.forceActiveFocus()
}
onAccepted: {
if (editText.text) {
ComputerManager.addNewHost(editText.text, false)
}
}
ColumnLayout {
Label {
text: addPcDialog.label
font.bold: true
}
TextField {
id: editText
Layout.fillWidth: true
focus: true
Keys.onReturnPressed: {
addPcDialog.accept()
}
}
}
}
} }
+7 -12
View File
@@ -91,6 +91,12 @@ void SdlGamepadKeyNavigation::onPollingTimerFired()
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
switch (event.type) { switch (event.type) {
case SDL_QUIT:
// SDL may send us a quit event since we initialize
// the video subsystem on startup. If we get one,
// forward it on for Qt to take care of.
QCoreApplication::instance()->quit();
break;
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONUP:
{ {
@@ -247,17 +253,7 @@ void SdlGamepadKeyNavigation::setSettingsMode(bool settingsMode)
int SdlGamepadKeyNavigation::getConnectedGamepads() int SdlGamepadKeyNavigation::getConnectedGamepads()
{ {
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) { Q_ASSERT(m_Enabled);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
SDL_GetError());
return 0;
}
// Applying mappings is necessary to ensure gamepad without
// a built-in mapping are properly counted.
MappingManager mappingManager;
mappingManager.applyMappings();
int count = 0; int count = 0;
for (int i = 0; i < SDL_NumJoysticks(); i++) { for (int i = 0; i < SDL_NumJoysticks(); i++) {
@@ -266,6 +262,5 @@ int SdlGamepadKeyNavigation::getConnectedGamepads()
} }
} }
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
return count; return count;
} }
+78 -33
View File
@@ -6,6 +6,9 @@
#include <QMutex> #include <QMutex>
#include <QtDebug> #include <QtDebug>
#include <QNetworkProxyFactory> #include <QNetworkProxyFactory>
#include <QPalette>
#include <QFont>
#include <QCursor>
// 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
@@ -28,6 +31,7 @@
#include "gui/computermodel.h" #include "gui/computermodel.h"
#include "gui/appmodel.h" #include "gui/appmodel.h"
#include "backend/autoupdatechecker.h" #include "backend/autoupdatechecker.h"
#include "backend/systemproperties.h"
#include "streaming/session.h" #include "streaming/session.h"
#include "settings/streamingpreferences.h" #include "settings/streamingpreferences.h"
#include "gui/sdlgamepadkeynavigation.h" #include "gui/sdlgamepadkeynavigation.h"
@@ -136,14 +140,6 @@ void qtLogToDiskHandler(QtMsgType type, const QMessageLogContext&, const QString
break; break;
} }
// HACK: Avoid printing this internal Qt warning which seems to print every time we
// start our event loop for the QNetworkReply. I have found this warning in many
// unrelated bug reports, but none actually tackle this warning itself. It seems to be
// new in Qt 5.12.
if (msg.startsWith("QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once")) {
return;
}
QTime logTime = QTime::fromMSecsSinceStartOfDay(s_LoggerTime.elapsed()); QTime logTime = QTime::fromMSecsSinceStartOfDay(s_LoggerTime.elapsed());
QString txt = QString("%1 - Qt %2: %3\n").arg(logTime.toString()).arg(typeTxt).arg(msg); QString txt = QString("%1 - Qt %2: %3\n").arg(logTime.toString()).arg(typeTxt).arg(msg);
@@ -231,6 +227,8 @@ LONG WINAPI UnhandledExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
SDL_SetMainReady();
// Set the app version for the QCommandLineParser's showVersion() command // Set the app version for the QCommandLineParser's showVersion() command
QCoreApplication::setApplicationVersion(VERSION_STR); QCoreApplication::setApplicationVersion(VERSION_STR);
@@ -292,7 +290,10 @@ int main(int argc, char *argv[])
// password prompts on macOS. // password prompts on macOS.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1"); qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
#ifdef Q_OS_WIN32 // The debug build of ANGLE on Qt 5.12.2 triggers all sorts of asserts
// even simply creating an empty Qt Quick window and then trying to close
// the program. Avoid using ANGLE for debug builds until the issue is fixed.
#if defined(Q_OS_WIN32) && !defined(QT_DEBUG)
if (!qEnvironmentVariableIsSet("QT_OPENGL")) { if (!qEnvironmentVariableIsSet("QT_OPENGL")) {
// On Windows, use ANGLE so we don't have to load OpenGL // On Windows, use ANGLE so we don't have to load OpenGL
// user-mode drivers into our app. OGL drivers (especially Intel) // user-mode drivers into our app. OGL drivers (especially Intel)
@@ -311,19 +312,57 @@ int main(int argc, char *argv[])
// Register custom metatypes for use in signals // Register custom metatypes for use in signals
qRegisterMetaType<NvApp>("NvApp"); qRegisterMetaType<NvApp>("NvApp");
// Disable desktop settings awareness due to issues with QGnomePlatform // Allow the display to sleep by default. We will manually use SDL_DisableScreenSaver()
// breaking our dialog rendering on Linux. // and SDL_EnableScreenSaver() when appropriate. This hint must be set before
QGuiApplication::setDesktopSettingsAware(false); // initializing the SDL video subsystem to have any effect.
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
// Steam Link requires that we initialize video before creating our
// QGuiApplication in order to configure the framebuffer correctly.
// It's fine to do on other platforms too, and it can save some time
// doing initialization and teardown of the video subsystem after streaming.
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) failed: %s",
SDL_GetError());
return -1;
}
// Use atexit() to ensure SDL_Quit() is called. This avoids
// racing with object destruction where SDL may be used.
atexit(SDL_Quit);
// Avoid the default behavior of changing the timer resolution to 1 ms.
// We don't want this all the time that Moonlight is open. We will set
// it manually when we start streaming.
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0");
// Disable minimize on focus loss by default. Users seem to want this off by default.
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
#ifdef STEAM_LINK
// Qt 5.9 from the Steam Link SDK is not able to load any fonts
// since the Steam Link doesn't include any of the ones it looks
// for. We know it has NotoSans so we will explicitly ask for that.
if (app.font().family().isEmpty()) {
qWarning() << "SL HACK: No default font - using NotoSans";
QFont fon("NotoSans");
app.setFont(fon);
}
// Move the mouse to the bottom right so it's invisible when using
// gamepad-only navigation.
QCursor().setPos(0xFFFF, 0xFFFF);
#endif
app.setWindowIcon(QIcon(":/res/moonlight.svg")); app.setWindowIcon(QIcon(":/res/moonlight.svg"));
// Register our C++ types for QML // Register our C++ types for QML
qmlRegisterType<ComputerModel>("ComputerModel", 1, 0, "ComputerModel"); qmlRegisterType<ComputerModel>("ComputerModel", 1, 0, "ComputerModel");
qmlRegisterType<AppModel>("AppModel", 1, 0, "AppModel"); qmlRegisterType<AppModel>("AppModel", 1, 0, "AppModel");
qmlRegisterType<StreamingPreferences>("StreamingPreferences", 1, 0, "StreamingPreferences");
qmlRegisterType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0, "SdlGamepadKeyNavigation");
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",
@@ -335,11 +374,33 @@ int main(int argc, char *argv[])
[](QQmlEngine*, QJSEngine*) -> QObject* { [](QQmlEngine*, QJSEngine*) -> QObject* {
return new AutoUpdateChecker(); return new AutoUpdateChecker();
}); });
qmlRegisterSingletonType<SystemProperties>("SystemProperties", 1, 0,
"SystemProperties",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new SystemProperties();
});
qmlRegisterSingletonType<SdlGamepadKeyNavigation>("SdlGamepadKeyNavigation", 1, 0,
"SdlGamepadKeyNavigation",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new SdlGamepadKeyNavigation();
});
qmlRegisterSingletonType<StreamingPreferences>("StreamingPreferences", 1, 0,
"StreamingPreferences",
[](QQmlEngine*, QJSEngine*) -> QObject* {
return new StreamingPreferences();
});
#ifndef Q_OS_WINRT
// Use the dense material dark theme by default // Use the dense material dark theme by default
if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")) { if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")) {
qputenv("QT_QUICK_CONTROLS_STYLE", "Material"); qputenv("QT_QUICK_CONTROLS_STYLE", "Material");
} }
#else
// Use universal dark on WinRT
if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")) {
qputenv("QT_QUICK_CONTROLS_STYLE", "Universal");
}
#endif
if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MATERIAL_THEME")) { if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MATERIAL_THEME")) {
qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", "Dark"); qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", "Dark");
} }
@@ -362,11 +423,11 @@ int main(int argc, char *argv[])
GlobalCommandLineParser parser; GlobalCommandLineParser parser;
switch (parser.parse(app.arguments())) { switch (parser.parse(app.arguments())) {
case GlobalCommandLineParser::NormalStartRequested: case GlobalCommandLineParser::NormalStartRequested:
initialView = "PcView.qml"; initialView = "qrc:/gui/PcView.qml";
break; break;
case GlobalCommandLineParser::StreamRequested: case GlobalCommandLineParser::StreamRequested:
{ {
initialView = "CliStartStreamSegue.qml"; initialView = "qrc:/gui/CliStartStreamSegue.qml";
StreamingPreferences* preferences = new StreamingPreferences(&app); StreamingPreferences* preferences = new StreamingPreferences(&app);
StreamCommandLineParser streamParser; StreamCommandLineParser streamParser;
streamParser.parse(app.arguments(), preferences); streamParser.parse(app.arguments(), preferences);
@@ -378,7 +439,7 @@ int main(int argc, char *argv[])
} }
case GlobalCommandLineParser::QuitRequested: case GlobalCommandLineParser::QuitRequested:
{ {
initialView = "CliQuitStreamSegue.qml"; initialView = "qrc:/gui/CliQuitStreamSegue.qml";
QuitCommandLineParser quitParser; QuitCommandLineParser quitParser;
quitParser.parse(app.arguments()); quitParser.parse(app.arguments());
auto launcher = new CliQuitStream::Launcher(quitParser.getHost(), &app); auto launcher = new CliQuitStream::Launcher(quitParser.getHost(), &app);
@@ -394,22 +455,6 @@ int main(int argc, char *argv[])
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())
return -1; return -1;
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_TIMER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_Init() failed: %s",
SDL_GetError());
}
// Use atexit() to ensure SDL_Quit() is called. This avoids
// racing with object destruction where SDL may be used.
atexit(SDL_Quit);
// Avoid the default behavior of changing the timer resolution to 1 ms.
// We don't want this all the time that Moonlight is open. We will set
// it manually when we start streaming.
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0");
int err = app.exec(); int err = app.exec();
// Give worker tasks time to properly exit. Fixes PendingQuitTask // Give worker tasks time to properly exit. Fixes PendingQuitTask
+14 -12
View File
@@ -21,6 +21,13 @@ QString Path::getBoxArtCacheDir()
return s_BoxArtCacheDir; return s_BoxArtCacheDir;
} }
QByteArray Path::readDataFile(QString fileName)
{
QFile dataFile(getDataFilePath(fileName));
dataFile.open(QIODevice::ReadOnly);
return dataFile.readAll();
}
QString Path::getDataFilePath(QString fileName) QString Path::getDataFilePath(QString fileName)
{ {
QString candidatePath; QString candidatePath;
@@ -28,33 +35,28 @@ QString Path::getDataFilePath(QString fileName)
// Check the current directory first // Check the current directory first
candidatePath = QDir(QDir::currentPath()).absoluteFilePath(fileName); candidatePath = QDir(QDir::currentPath()).absoluteFilePath(fileName);
if (QFile::exists(candidatePath)) { if (QFile::exists(candidatePath)) {
qInfo() << "Found" << fileName << "at" << candidatePath;
return candidatePath; return candidatePath;
} }
// Now check the data directories (for Linux, in particular) // Now check the data directories (for Linux, in particular)
candidatePath = QStandardPaths::locate(QStandardPaths::DataLocation, fileName); candidatePath = QStandardPaths::locate(QStandardPaths::DataLocation, fileName);
if (!candidatePath.isEmpty() && QFile::exists(candidatePath)) { if (!candidatePath.isEmpty() && QFile::exists(candidatePath)) {
qInfo() << "Found" << fileName << "at" << candidatePath;
return candidatePath; return candidatePath;
} }
// Now try the directory of our app installation (for Windows, if current dir doesn't find it) // Now try the directory of our app installation (for Windows, if current dir doesn't find it)
candidatePath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(fileName); candidatePath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(fileName);
if (QFile::exists(candidatePath)) { if (QFile::exists(candidatePath)) {
qInfo() << "Found" << fileName << "at" << candidatePath;
return candidatePath; return candidatePath;
} }
// Now try the Resources folder in our app bundle for macOS // Return the QRC embedded copy
QDir dir = QDir(QCoreApplication::applicationDirPath()); candidatePath = ":/data/" + fileName;
dir.cdUp(); qInfo() << "Found" << fileName << "at" << candidatePath;
dir.cd("Resources"); return QString(candidatePath);
dir.filePath(fileName);
candidatePath = dir.absoluteFilePath(fileName);
if (QFile::exists(candidatePath)) {
return candidatePath;
}
// Couldn't find it
return QString();
} }
void Path::initialize(bool portable) void Path::initialize(bool portable)
+3
View File
@@ -9,6 +9,9 @@ public:
static QString getBoxArtCacheDir(); static QString getBoxArtCacheDir();
static QByteArray readDataFile(QString fileName);
// Only safe to use directly for Qt classes
static QString getDataFilePath(QString fileName); static QString getDataFilePath(QString fileName);
static void initialize(bool portable); static void initialize(bool portable);
+4
View File
@@ -14,5 +14,9 @@
<file>gui/CliStartStreamSegue.qml</file> <file>gui/CliStartStreamSegue.qml</file>
<file>gui/AutoResizingComboBox.qml</file> <file>gui/AutoResizingComboBox.qml</file>
<file>gui/NavigableMenu.qml</file> <file>gui/NavigableMenu.qml</file>
<file>gui/ErrorMessageDialog.qml</file>
<file>gui/NavigableMessageDialog.qml</file>
<file>gui/NavigableDialog.qml</file>
<file>gui/CenteredGridView.qml</file>
</qresource> </qresource>
</RCC> </RCC>
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#FFFFFF"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></svg>

After

Width:  |  Height:  |  Size: 323 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#FFFFFF"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>

After

Width:  |  Height:  |  Size: 397 B

+6
View File
@@ -15,5 +15,11 @@
<file>res/question_mark.svg</file> <file>res/question_mark.svg</file>
<file>res/moonlight.svg</file> <file>res/moonlight.svg</file>
<file>res/update.svg</file> <file>res/update.svg</file>
<file>res/baseline-help_outline-24px.svg</file>
<file>res/baseline-error_outline-24px.svg</file>
</qresource>
<qresource prefix="/data">
<file alias="gamecontrollerdb.txt">SDL_GameControllerDB/gamecontrollerdb.txt</file>
<file alias="ModeSeven.ttf">ModeSeven.ttf</file>
</qresource> </qresource>
</RCC> </RCC>
+6 -11
View File
@@ -54,15 +54,10 @@ void MappingManager::save()
void MappingManager::applyMappings() void MappingManager::applyMappings()
{ {
QString mappingFile = Path::getDataFilePath("gamecontrollerdb.txt"); QByteArray mappingData = Path::readDataFile("gamecontrollerdb.txt");
if (!mappingFile.isEmpty()) { if (!mappingData.isEmpty()) {
std::string mappingFileNative = QDir::toNativeSeparators(mappingFile).toStdString(); int newMappings = SDL_GameControllerAddMappingsFromRW(
SDL_RWFromConstMem(mappingData.constData(), mappingData.size()), 1);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Loading gamepad mappings from: %s",
mappingFileNative.c_str());
int newMappings = SDL_GameControllerAddMappingsFromFile(mappingFileNative.c_str());
if (newMappings < 0) { if (newMappings < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error loading gamepad mappings: %s", "Error loading gamepad mappings: %s",
@@ -75,8 +70,8 @@ void MappingManager::applyMappings()
} }
} }
else { else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"No gamepad mapping file found"); "Unable to load gamepad mapping file");
} }
QList<SdlGamepadMapping> mappings = m_Mappings.values(); QList<SdlGamepadMapping> mappings = m_Mappings.values();
+3 -123
View File
@@ -1,6 +1,4 @@
#include "streamingpreferences.h" #include "streamingpreferences.h"
#include "streaming/session.h"
#include "streaming/streamutils.h"
#include <QSettings> #include <QSettings>
@@ -24,6 +22,7 @@
#define SER_MOUSEACCELERATION "mouseacceleration" #define SER_MOUSEACCELERATION "mouseacceleration"
#define SER_STARTWINDOWED "startwindowed" #define SER_STARTWINDOWED "startwindowed"
#define SER_FRAMEPACING "framepacing" #define SER_FRAMEPACING "framepacing"
#define SER_CONNWARNINGS "connwarnings"
StreamingPreferences::StreamingPreferences(QObject *parent) StreamingPreferences::StreamingPreferences(QObject *parent)
: QObject(parent) : QObject(parent)
@@ -49,6 +48,7 @@ void StreamingPreferences::reload()
mouseAcceleration = settings.value(SER_MOUSEACCELERATION, false).toBool(); mouseAcceleration = settings.value(SER_MOUSEACCELERATION, false).toBool();
startWindowed = settings.value(SER_STARTWINDOWED, false).toBool(); startWindowed = settings.value(SER_STARTWINDOWED, false).toBool();
framePacing = settings.value(SER_FRAMEPACING, false).toBool(); framePacing = settings.value(SER_FRAMEPACING, false).toBool();
connectionWarnings = settings.value(SER_CONNWARNINGS, true).toBool();
audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG, audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG,
static_cast<int>(AudioConfig::AC_STEREO)).toInt()); static_cast<int>(AudioConfig::AC_STEREO)).toInt());
videoCodecConfig = static_cast<VideoCodecConfig>(settings.value(SER_VIDEOCFG, videoCodecConfig = static_cast<VideoCodecConfig>(settings.value(SER_VIDEOCFG,
@@ -79,133 +79,13 @@ void StreamingPreferences::save()
settings.setValue(SER_MOUSEACCELERATION, mouseAcceleration); settings.setValue(SER_MOUSEACCELERATION, mouseAcceleration);
settings.setValue(SER_STARTWINDOWED, startWindowed); settings.setValue(SER_STARTWINDOWED, startWindowed);
settings.setValue(SER_FRAMEPACING, framePacing); settings.setValue(SER_FRAMEPACING, framePacing);
settings.setValue(SER_CONNWARNINGS, connectionWarnings);
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig)); settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig)); settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));
settings.setValue(SER_VIDEODEC, static_cast<int>(videoDecoderSelection)); settings.setValue(SER_VIDEODEC, static_cast<int>(videoDecoderSelection));
settings.setValue(SER_WINDOWMODE, static_cast<int>(windowMode)); settings.setValue(SER_WINDOWMODE, static_cast<int>(windowMode));
} }
bool StreamingPreferences::hasAnyHardwareAcceleration()
{
// Always use VDS_AUTO to avoid spamming the user with warnings
// if they've forced software decoding.
return Session::isHardwareDecodeAvailable(VDS_AUTO,
VIDEO_FORMAT_H264,
1920, 1080, 60);
}
bool StreamingPreferences::isRunningWayland()
{
return qgetenv("XDG_SESSION_TYPE") == QByteArray("wayland");
}
bool StreamingPreferences::isWow64()
{
#ifdef Q_OS_WIN32
return QSysInfo::currentCpuArchitecture() != QSysInfo::buildCpuArchitecture();
#else
return false;
#endif
}
QString StreamingPreferences::getUnmappedGamepads()
{
return SdlInputHandler::getUnmappedGamepads();
}
int StreamingPreferences::getMaximumStreamingFrameRate()
{
// Never let the maximum drop below 60 FPS
int maxFrameRate = 60;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return maxFrameRate;
}
SDL_DisplayMode mode, bestMode, desktopMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
// Get the native desktop resolution
if (StreamUtils::getRealDesktopMode(displayIndex, &desktopMode)) {
// Start at desktop mode and work our way up
bestMode = desktopMode;
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
if (mode.w == desktopMode.w && mode.h == desktopMode.h) {
if (mode.refresh_rate > bestMode.refresh_rate) {
bestMode = mode;
}
}
}
}
maxFrameRate = qMax(maxFrameRate, bestMode.refresh_rate);
}
}
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return maxFrameRate;
}
QRect StreamingPreferences::getDesktopResolution(int displayIndex)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return QRect();
}
if (displayIndex >= SDL_GetNumVideoDisplays()) {
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return QRect();
}
SDL_DisplayMode mode;
int err = SDL_GetDesktopDisplayMode(displayIndex, &mode);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
if (err == 0) {
return QRect(0, 0, mode.w, mode.h);
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDesktopDisplayMode() failed: %s",
SDL_GetError());
return QRect();
}
}
QRect StreamingPreferences::getNativeResolution(int displayIndex)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return QRect();
}
if (displayIndex >= SDL_GetNumVideoDisplays()) {
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return QRect();
}
SDL_DisplayMode mode;
bool success = StreamUtils::getRealDesktopMode(displayIndex, &mode);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
if (success) {
return QRect(0, 0, mode.w, mode.h);
}
else {
return QRect();
}
}
int StreamingPreferences::getDefaultBitrate(int width, int height, int fps) int StreamingPreferences::getDefaultBitrate(int width, int height, int fps)
{ {
// This table prefers 16:10 resolutions because they are // This table prefers 16:10 resolutions because they are
+3 -14
View File
@@ -15,20 +15,6 @@ public:
Q_INVOKABLE void save(); Q_INVOKABLE void save();
Q_INVOKABLE static bool hasAnyHardwareAcceleration();
Q_INVOKABLE static bool isRunningWayland();
Q_INVOKABLE static bool isWow64();
Q_INVOKABLE static int getMaximumStreamingFrameRate();
Q_INVOKABLE QRect getDesktopResolution(int displayIndex);
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
Q_INVOKABLE QString getUnmappedGamepads();
void reload(); void reload();
enum AudioConfig enum AudioConfig
@@ -77,6 +63,7 @@ public:
Q_PROPERTY(bool mouseAcceleration MEMBER mouseAcceleration NOTIFY mouseAccelerationChanged) Q_PROPERTY(bool mouseAcceleration MEMBER mouseAcceleration NOTIFY mouseAccelerationChanged)
Q_PROPERTY(bool startWindowed MEMBER startWindowed NOTIFY startWindowedChanged) Q_PROPERTY(bool startWindowed MEMBER startWindowed NOTIFY startWindowedChanged)
Q_PROPERTY(bool framePacing MEMBER framePacing NOTIFY framePacingChanged) Q_PROPERTY(bool framePacing MEMBER framePacing NOTIFY framePacingChanged)
Q_PROPERTY(bool connectionWarnings MEMBER connectionWarnings NOTIFY connectionWarningsChanged)
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(VideoDecoderSelection videoDecoderSelection MEMBER videoDecoderSelection NOTIFY videoDecoderSelectionChanged) Q_PROPERTY(VideoDecoderSelection videoDecoderSelection MEMBER videoDecoderSelection NOTIFY videoDecoderSelectionChanged)
@@ -97,6 +84,7 @@ public:
bool mouseAcceleration; bool mouseAcceleration;
bool startWindowed; bool startWindowed;
bool framePacing; bool framePacing;
bool connectionWarnings;
AudioConfig audioConfig; AudioConfig audioConfig;
VideoCodecConfig videoCodecConfig; VideoCodecConfig videoCodecConfig;
VideoDecoderSelection videoDecoderSelection; VideoDecoderSelection videoDecoderSelection;
@@ -119,5 +107,6 @@ signals:
void windowModeChanged(); void windowModeChanged();
void startWindowedChanged(); void startWindowedChanged();
void framePacingChanged(); void framePacingChanged();
void connectionWarningsChanged();
}; };
+7 -1
View File
@@ -5,18 +5,24 @@
#include "renderers/soundioaudiorenderer.h" #include "renderers/soundioaudiorenderer.h"
#endif #endif
#ifdef HAVE_SLAUDIO
#include "renderers/slaud.h"
#endif
#include "renderers/sdl.h" #include "renderers/sdl.h"
#include <Limelight.h> #include <Limelight.h>
IAudioRenderer* Session::createAudioRenderer() IAudioRenderer* Session::createAudioRenderer()
{ {
#ifdef HAVE_SOUNDIO #if defined(HAVE_SOUNDIO)
if (qgetenv("ML_AUDIO") == "SDL") { if (qgetenv("ML_AUDIO") == "SDL") {
return new SdlAudioRenderer(); return new SdlAudioRenderer();
} }
return new SoundIoAudioRenderer(); return new SoundIoAudioRenderer();
#elif defined(HAVE_SLAUDIO)
return new SLAudioRenderer();
#else #else
return new SdlAudioRenderer(); return new SdlAudioRenderer();
#endif #endif
+58
View File
@@ -0,0 +1,58 @@
#include "slaud.h"
#include <SDL.h>
SLAudioRenderer::SLAudioRenderer()
: m_AudioContext(nullptr),
m_AudioStream(nullptr)
{
}
bool SLAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
{
m_AudioContext = SLAudio_CreateContext();
if (m_AudioContext == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SLAudio_CreateContext() failed");
return false;
}
m_AudioStream = SLAudio_CreateStream(m_AudioContext,
opusConfig->sampleRate,
opusConfig->channelCount,
SAMPLES_PER_FRAME * sizeof(short) * opusConfig->channelCount,
1);
if (m_AudioStream == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SLAudio_CreateStream() failed");
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using SLAudio renderer");
return true;
}
SLAudioRenderer::~SLAudioRenderer()
{
if (m_AudioStream != nullptr) {
SLAudio_FreeStream(m_AudioStream);
}
if (m_AudioContext != nullptr) {
SLAudio_FreeContext(m_AudioContext);
}
}
bool SLAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
{
void* outputBuffer = SLAudio_BeginFrame(m_AudioStream);
if (outputBuffer != nullptr) {
memcpy(outputBuffer, audioBuffer, audioSize);
SLAudio_SubmitFrame(m_AudioStream);
}
return true;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "renderer.h"
#include <SLAudio.h>
class SLAudioRenderer : public IAudioRenderer
{
public:
SLAudioRenderer();
virtual ~SLAudioRenderer();
virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
virtual bool submitAudio(short* audioBuffer, int audioSize);
private:
CSLAudioContext* m_AudioContext;
CSLAudioStream* m_AudioStream;
};
+111 -24
View File
@@ -54,8 +54,9 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
// is via normal motion events that are influenced by mouse acceleration). // is via normal motion events that are influenced by mouse acceleration).
// Otherwise, we'll use raw input capture which is straight from the device // Otherwise, we'll use raw input capture which is straight from the device
// without modification by the OS. // without modification by the OS.
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
prefs.mouseAcceleration ? "1" : "0"); prefs.mouseAcceleration ? "1" : "0",
SDL_HINT_OVERRIDE);
// We must initialize joystick explicitly before gamecontroller in order // We must initialize joystick explicitly before gamecontroller in order
// to ensure we receive gamecontroller attach events for gamepads where // to ensure we receive gamecontroller attach events for gamepads where
@@ -127,6 +128,9 @@ SdlInputHandler::~SdlInputHandler()
SDL_QuitSubSystem(SDL_INIT_JOYSTICK); SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK)); SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK));
// Return background event handling to off
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
} }
void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
@@ -139,10 +143,69 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
(event->keysym.mod & KMOD_CTRL) && (event->keysym.mod & KMOD_CTRL) &&
(event->keysym.mod & KMOD_ALT) && (event->keysym.mod & KMOD_ALT) &&
(event->keysym.mod & KMOD_SHIFT)) { (event->keysym.mod & KMOD_SHIFT)) {
// First we test the SDLK combos for matches,
// that way we ensure that latin keyboard users
// can match to the key they see on their keyboards.
// If nothing matches that, we'll then go on to
// checking scancodes so non-latin keyboard users
// can have working hotkeys (though possibly in
// odd positions). We must do all SDLK tests before
// any scancode tests to avoid issues in cases
// where the SDLK for one shortcut collides with
// the scancode of another.
// Check for quit combo (Ctrl+Alt+Shift+Q) // Check for quit combo (Ctrl+Alt+Shift+Q)
if (event->keysym.scancode == SDL_SCANCODE_Q) { if (event->keysym.sym == SDLK_q) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected quit key combo"); "Detected quit key combo (SDLK)");
// Push a quit event to the main loop
SDL_Event event;
event.type = SDL_QUIT;
event.quit.timestamp = SDL_GetTicks();
SDL_PushEvent(&event);
return;
}
// Check for the unbind combo (Ctrl+Alt+Shift+Z)
else if (event->keysym.sym == SDLK_z) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected mouse capture toggle combo (SDLK)");
// Stop handling future input
SDL_SetRelativeMouseMode((SDL_bool)!SDL_GetRelativeMouseMode());
// Force raise all keys to ensure they aren't stuck,
// since we won't get their key up events.
raiseAllKeys();
return;
}
else if (event->keysym.sym == SDLK_x) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected full-screen toggle combo (SDLK)");
Session::s_ActiveSession->toggleFullscreen();
// Force raise all keys just be safe across this full-screen/windowed
// transition just in case key events get lost.
raiseAllKeys();
return;
}
else if (event->keysym.sym == SDLK_s) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected stats toggle combo (SDLK)");
// Toggle the stats overlay
Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug,
!Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug));
// Force raise all keys just be safe across this full-screen/windowed
// transition just in case key events get lost.
raiseAllKeys();
return;
}
// Check for quit combo (Ctrl+Alt+Shift+Q)
else if (event->keysym.scancode == SDL_SCANCODE_Q) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected quit key combo (scancode)");
// Push a quit event to the main loop // Push a quit event to the main loop
SDL_Event event; SDL_Event event;
@@ -154,7 +217,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
// Check for the unbind combo (Ctrl+Alt+Shift+Z) // Check for the unbind combo (Ctrl+Alt+Shift+Z)
else if (event->keysym.scancode == SDL_SCANCODE_Z) { else if (event->keysym.scancode == SDL_SCANCODE_Z) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected mouse capture toggle combo"); "Detected mouse capture toggle combo (scancode)");
// Stop handling future input // Stop handling future input
SDL_SetRelativeMouseMode((SDL_bool)!SDL_GetRelativeMouseMode()); SDL_SetRelativeMouseMode((SDL_bool)!SDL_GetRelativeMouseMode());
@@ -167,7 +230,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
// Check for the full-screen combo (Ctrl+Alt+Shift+X) // Check for the full-screen combo (Ctrl+Alt+Shift+X)
else if (event->keysym.scancode == SDL_SCANCODE_X) { else if (event->keysym.scancode == SDL_SCANCODE_X) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected full-screen toggle combo"); "Detected full-screen toggle combo (scancode)");
Session::s_ActiveSession->toggleFullscreen(); Session::s_ActiveSession->toggleFullscreen();
// Force raise all keys just be safe across this full-screen/windowed // Force raise all keys just be safe across this full-screen/windowed
@@ -177,7 +240,7 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event)
} }
else if (event->keysym.scancode == SDL_SCANCODE_S) { else if (event->keysym.scancode == SDL_SCANCODE_S) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Detected stats toggle combo"); "Detected stats toggle combo (scancode)");
// Toggle the stats overlay // Toggle the stats overlay
Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug, Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug,
@@ -716,9 +779,21 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller)); state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller));
state->haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(state->controller)); state->haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(state->controller));
state->hapticEffectId = -1; state->hapticEffectId = -1;
if (state->haptic != nullptr && (SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) { state->hapticMethod = GAMEPAD_HAPTIC_METHOD_NONE;
SDL_HapticClose(state->haptic); if (state->haptic != nullptr) {
state->haptic = nullptr; if ((SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
if (SDL_HapticRumbleSupported(state->haptic)) {
if (SDL_HapticRumbleInit(state->haptic) == 0) {
state->hapticMethod = GAMEPAD_HAPTIC_METHOD_SIMPLERUMBLE;
}
}
if (state->hapticMethod == GAMEPAD_HAPTIC_METHOD_NONE) {
SDL_HapticClose(state->haptic);
state->haptic = nullptr;
}
} else {
state->hapticMethod = GAMEPAD_HAPTIC_METHOD_LEFTRIGHT;
}
} }
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(state->controller)), SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(state->controller)),
@@ -824,8 +899,12 @@ void SdlInputHandler::rumble(unsigned short controllerNumber, unsigned short low
} }
// Stop the last effect we played // Stop the last effect we played
if (m_GamepadState[controllerNumber].hapticEffectId >= 0) { if (m_GamepadState[controllerNumber].hapticMethod == GAMEPAD_HAPTIC_METHOD_LEFTRIGHT) {
SDL_HapticDestroyEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId); if (m_GamepadState[controllerNumber].hapticEffectId >= 0) {
SDL_HapticDestroyEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId);
}
} else if (m_GamepadState[controllerNumber].hapticMethod == GAMEPAD_HAPTIC_METHOD_SIMPLERUMBLE) {
SDL_HapticRumbleStop(haptic);
} }
// If this callback is telling us to stop both motors, don't bother queuing a new effect // If this callback is telling us to stop both motors, don't bother queuing a new effect
@@ -833,22 +912,30 @@ void SdlInputHandler::rumble(unsigned short controllerNumber, unsigned short low
return; return;
} }
SDL_HapticEffect effect; if (m_GamepadState[controllerNumber].hapticMethod == GAMEPAD_HAPTIC_METHOD_LEFTRIGHT) {
SDL_memset(&effect, 0, sizeof(effect)); SDL_HapticEffect effect;
effect.type = SDL_HAPTIC_LEFTRIGHT; SDL_memset(&effect, 0, sizeof(effect));
effect.type = SDL_HAPTIC_LEFTRIGHT;
// The effect should last until we are instructed to stop or change it // The effect should last until we are instructed to stop or change it
effect.leftright.length = SDL_HAPTIC_INFINITY; effect.leftright.length = SDL_HAPTIC_INFINITY;
// SDL haptics range from 0-32767 but XInput uses 0-65535, so divide by 2 to correct for SDL's scaling // SDL haptics range from 0-32767 but XInput uses 0-65535, so divide by 2 to correct for SDL's scaling
effect.leftright.large_magnitude = lowFreqMotor / 2; effect.leftright.large_magnitude = lowFreqMotor / 2;
effect.leftright.small_magnitude = highFreqMotor / 2; effect.leftright.small_magnitude = highFreqMotor / 2;
// Play the new effect // Play the new effect
m_GamepadState[controllerNumber].hapticEffectId = SDL_HapticNewEffect(haptic, &effect); m_GamepadState[controllerNumber].hapticEffectId = SDL_HapticNewEffect(haptic, &effect);
if (m_GamepadState[controllerNumber].hapticEffectId >= 0) { if (m_GamepadState[controllerNumber].hapticEffectId >= 0) {
SDL_HapticRunEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId, 1); SDL_HapticRunEffect(haptic, m_GamepadState[controllerNumber].hapticEffectId, 1);
}
} else if (m_GamepadState[controllerNumber].hapticMethod == GAMEPAD_HAPTIC_METHOD_SIMPLERUMBLE) {
SDL_HapticRumblePlay(haptic,
std::min(1.0, (GAMEPAD_HAPTIC_SIMPLE_HIFREQ_MOTOR_WEIGHT*highFreqMotor +
GAMEPAD_HAPTIC_SIMPLE_LOWFREQ_MOTOR_WEIGHT*lowFreqMotor) / 65535.0),
SDL_HAPTIC_INFINITY);
} }
} }
void SdlInputHandler::handleTouchFingerEvent(SDL_TouchFingerEvent* event) void SdlInputHandler::handleTouchFingerEvent(SDL_TouchFingerEvent* event)
+8
View File
@@ -9,6 +9,7 @@ struct GamepadState {
SDL_GameController* controller; SDL_GameController* controller;
SDL_JoystickID jsId; SDL_JoystickID jsId;
SDL_Haptic* haptic; SDL_Haptic* haptic;
int hapticMethod;
int hapticEffectId; int hapticEffectId;
short index; short index;
@@ -21,6 +22,13 @@ struct GamepadState {
#define MAX_GAMEPADS 4 #define MAX_GAMEPADS 4
#define MAX_FINGERS 2 #define MAX_FINGERS 2
#define GAMEPAD_HAPTIC_METHOD_NONE 0
#define GAMEPAD_HAPTIC_METHOD_LEFTRIGHT 1
#define GAMEPAD_HAPTIC_METHOD_SIMPLERUMBLE 2
#define GAMEPAD_HAPTIC_SIMPLE_HIFREQ_MOTOR_WEIGHT 0.33
#define GAMEPAD_HAPTIC_SIMPLE_LOWFREQ_MOTOR_WEIGHT 0.8
class SdlInputHandler class SdlInputHandler
{ {
public: public:
+73 -95
View File
@@ -11,7 +11,7 @@
#endif #endif
#ifdef HAVE_SLVIDEO #ifdef HAVE_SLVIDEO
#include "video/sl.h" #include "video/slvid.h"
#endif #endif
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
@@ -39,7 +39,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = {
nullptr, nullptr,
nullptr, nullptr,
Session::clLogMessage, Session::clLogMessage,
Session::clRumble Session::clRumble,
Session::clConnectionStatusUpdate
}; };
AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = { AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = {
@@ -114,6 +115,34 @@ void Session::clRumble(unsigned short controllerNumber, unsigned short lowFreqMo
SDL_AtomicUnlock(&s_ActiveSession->m_InputHandlerLock); SDL_AtomicUnlock(&s_ActiveSession->m_InputHandlerLock);
} }
void Session::clConnectionStatusUpdate(int connectionStatus)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Connection status update: %d",
connectionStatus);
if (!s_ActiveSession->m_Preferences->connectionWarnings) {
return;
}
switch (connectionStatus)
{
case CONN_STATUS_POOR:
if (s_ActiveSession->m_StreamConfig.bitrate > 5000) {
strcpy(s_ActiveSession->m_OverlayManager.getOverlayText(Overlay::OverlayStatusUpdate), "Slow connection to PC\nReduce your bitrate");
}
else {
strcpy(s_ActiveSession->m_OverlayManager.getOverlayText(Overlay::OverlayStatusUpdate), "Poor connection to PC");
}
s_ActiveSession->m_OverlayManager.setOverlayTextUpdated(Overlay::OverlayStatusUpdate);
s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, true);
break;
case CONN_STATUS_OKAY:
s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, false);
break;
}
}
#define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing) #define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing)
bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,
@@ -215,30 +244,13 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
} }
} }
bool Session::isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, bool Session::isHardwareDecodeAvailable(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate) int videoFormat, int width, int height, int frameRate)
{ {
IVideoDecoder* decoder; IVideoDecoder* decoder;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return false;
}
SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN);
if (!window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) { if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) {
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false; return false;
} }
@@ -246,39 +258,16 @@ bool Session::isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelect
delete decoder; delete decoder;
// This must be called after the decoder is deleted, because
// the renderer may want to interact with the window
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return ret; return ret;
} }
int Session::getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection vds, int Session::getDecoderCapabilities(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate) int videoFormat, int width, int height, int frameRate)
{ {
IVideoDecoder* decoder; IVideoDecoder* decoder;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return false;
}
SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN);
if (!window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) { if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) {
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false; return false;
} }
@@ -286,12 +275,6 @@ int Session::getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection
delete decoder; delete decoder;
// This must be called after the decoder is deleted, because
// the renderer may want to interact with the window
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return caps; return caps;
} }
@@ -327,8 +310,17 @@ Session::~Session()
s_ActiveSessionSemaphore.release(); s_ActiveSessionSemaphore.release();
} }
void Session::initialize() bool Session::initialize()
{ {
// Create a hidden window to use for decoder initialization tests
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
return false;
}
qInfo() << "Server GPU:" << m_Computer->gpuModel; qInfo() << "Server GPU:" << m_Computer->gpuModel;
qInfo() << "Server GFE version:" << m_Computer->gfeVersion; qInfo() << "Server GFE version:" << m_Computer->gfeVersion;
@@ -381,7 +373,8 @@ void Session::initialize()
case StreamingPreferences::VCC_AUTO: case StreamingPreferences::VCC_AUTO:
// TODO: Determine if HEVC is better depending on the decoder // TODO: Determine if HEVC is better depending on the decoder
m_StreamConfig.supportsHevc = m_StreamConfig.supportsHevc =
isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@@ -418,6 +411,15 @@ void Session::initialize()
break; break;
} }
// Add the capability flags from the chosen decoder/renderer
// Requires m_StreamConfig.supportsHevc to be initialized
m_VideoCallbacks.capabilities |= getDecoderCapabilities(testWindow,
m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps);
switch (m_Preferences->windowMode) switch (m_Preferences->windowMode)
{ {
case StreamingPreferences::WM_FULLSCREEN_DESKTOP: case StreamingPreferences::WM_FULLSCREEN_DESKTOP:
@@ -428,6 +430,14 @@ void Session::initialize()
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN; m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
break; break;
} }
// Check for validation errors/warnings and emit
// signals for them, if appropriate
bool ret = validateLaunch(testWindow);
SDL_DestroyWindow(testWindow);
return ret;
} }
void Session::emitLaunchWarning(QString text) void Session::emitLaunchWarning(QString text)
@@ -446,7 +456,7 @@ void Session::emitLaunchWarning(QString text)
} }
} }
bool Session::validateLaunch() bool Session::validateLaunch(SDL_Window* testWindow)
{ {
QStringList warningList; QStringList warningList;
@@ -466,7 +476,8 @@ bool Session::validateLaunch()
bool hevcForced = m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC || bool hevcForced = m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC ||
m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC_HDR; m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC_HDR;
if (!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, if (!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@@ -507,7 +518,8 @@ bool Session::validateLaunch()
emitLaunchWarning("Your host PC GPU doesn't support HDR streaming. " emitLaunchWarning("Your host PC GPU doesn't support HDR streaming. "
"A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming."); "A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming.");
} }
else if (!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, else if (!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_MAIN10, VIDEO_FORMAT_H265_MAIN10,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@@ -556,7 +568,8 @@ bool Session::validateLaunch()
} }
if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_FORCE_HARDWARE && if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_FORCE_HARDWARE &&
!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, !isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264, m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@@ -572,13 +585,6 @@ bool Session::validateLaunch()
return false; return false;
} }
// Add the capability flags from the chosen decoder/renderer
m_VideoCallbacks.capabilities |= getDecoderCapabilities(m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps);
return true; return true;
} }
@@ -800,11 +806,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
// Complete initialization in this deferred context to avoid // Complete initialization in this deferred context to avoid
// calling expensive functions in the constructor (during the // calling expensive functions in the constructor (during the
// process of loading the StreamSegue). // process of loading the StreamSegue).
initialize(); if (!initialize()) {
// Check for validation errors/warnings and emit
// signals for them, if appropriate
if (!validateLaunch()) {
emit sessionFinished(); emit sessionFinished();
return; return;
} }
@@ -864,7 +866,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
} catch (const GfeHttpResponseException& e) { } catch (const GfeHttpResponseException& e) {
delete m_InputHandler; delete m_InputHandler;
m_InputHandler = nullptr; m_InputHandler = nullptr;
emit displayLaunchError(e.toQString()); emit displayLaunchError("GeForce Experience returned error: " + e.toQString());
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this)); QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return; return;
} catch (const QtNetworkReplyException& e) { } catch (const QtNetworkReplyException& e) {
@@ -875,18 +877,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
return; return;
} }
SDL_assert(!SDL_WasInit(SDL_INIT_VIDEO));
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
delete m_InputHandler;
m_InputHandler = nullptr;
emit displayLaunchError(QString::fromLocal8Bit(SDL_GetError()));
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return;
}
QByteArray hostnameStr = m_Computer->activeAddress.toLatin1(); QByteArray hostnameStr = m_Computer->activeAddress.toLatin1();
QByteArray siAppVersion = m_Computer->appVersion.toLatin1(); QByteArray siAppVersion = m_Computer->appVersion.toLatin1();
@@ -912,7 +902,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
// listener. // listener.
delete m_InputHandler; delete m_InputHandler;
m_InputHandler = nullptr; m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this)); QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return; return;
} }
@@ -936,7 +925,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
SDL_GetError()); SDL_GetError());
delete m_InputHandler; delete m_InputHandler;
m_InputHandler = nullptr; m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this)); QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return; return;
} }
@@ -1007,14 +995,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
// sleep precision and more accurate callback timing. // sleep precision and more accurate callback timing.
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1");
// Raise the priority of the main thread, since it handles
// time-sensitive video rendering
if (SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH) < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to set main thread to high priority: %s",
SDL_GetError());
}
int currentDisplayIndex = SDL_GetWindowDisplayIndex(m_Window); int currentDisplayIndex = SDL_GetWindowDisplayIndex(m_Window);
// Now that we're about to stream, any SDL_QUIT event is expected // Now that we're about to stream, any SDL_QUIT event is expected
@@ -1260,8 +1240,6 @@ DispatchDeferredCleanup:
if (iconSurface != nullptr) { if (iconSurface != nullptr) {
SDL_FreeSurface(iconSurface); SDL_FreeSurface(iconSurface);
} }
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SDL_assert(!SDL_WasInit(SDL_INIT_VIDEO));
// Cleanup can take a while, so dispatch it to a worker thread. // Cleanup can take a while, so dispatch it to a worker thread.
// When it is complete, it will release our s_ActiveSessionSemaphore // When it is complete, it will release our s_ActiveSessionSemaphore
+11 -8
View File
@@ -25,13 +25,10 @@ public:
Q_INVOKABLE void exec(int displayOriginX, int displayOriginY); Q_INVOKABLE void exec(int displayOriginX, int displayOriginY);
static static
bool isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, bool isHardwareDecodeAvailable(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate); int videoFormat, int width, int height, int frameRate);
static
int getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate);
static Session* get() static Session* get()
{ {
return s_ActiveSession; return s_ActiveSession;
@@ -58,13 +55,16 @@ signals:
void sessionFinished(); void sessionFinished();
private: private:
void initialize(); bool initialize();
bool validateLaunch(); bool validateLaunch(SDL_Window* testWindow);
void emitLaunchWarning(QString text); void emitLaunchWarning(QString text);
int getDecoderCapabilities(); static
int getDecoderCapabilities(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate);
IAudioRenderer* createAudioRenderer(); IAudioRenderer* createAudioRenderer();
@@ -101,6 +101,9 @@ private:
static static
void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor); void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
static
void clConnectionStatusUpdate(int connectionStatus);
static static
int arInit(int audioConfiguration, int arInit(int audioConfiguration,
const POPUS_MULTISTREAM_CONFIGURATION opusConfig, const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
+78 -21
View File
@@ -28,6 +28,7 @@ DXVA2Renderer::DXVA2Renderer() :
m_Processor(nullptr), m_Processor(nullptr),
m_FrameIndex(0), m_FrameIndex(0),
m_DebugOverlayFont(nullptr), m_DebugOverlayFont(nullptr),
m_StatusOverlayFont(nullptr),
m_BlockingPresent(false) m_BlockingPresent(false)
{ {
RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces)); RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces));
@@ -48,6 +49,7 @@ DXVA2Renderer::~DXVA2Renderer()
SAFE_COM_RELEASE(m_ProcService); SAFE_COM_RELEASE(m_ProcService);
SAFE_COM_RELEASE(m_Processor); SAFE_COM_RELEASE(m_Processor);
SAFE_COM_RELEASE(m_DebugOverlayFont); SAFE_COM_RELEASE(m_DebugOverlayFont);
SAFE_COM_RELEASE(m_StatusOverlayFont);
for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) { for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) {
SAFE_COM_RELEASE(m_DecSurfaces[i]); SAFE_COM_RELEASE(m_DecSurfaces[i]);
@@ -330,6 +332,12 @@ bool DXVA2Renderer::isDXVideoProcessorAPIBlacklisted()
HRESULT hr; HRESULT hr;
bool result = false; bool result = false;
if (qgetenv("DXVA2_DISABLE_VIDPROC_BLACKLIST") == "1") {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"IDirectXVideoProcessor blacklist is disabled");
return false;
}
hr = m_Device->GetDirect3D(&d3d9); hr = m_Device->GetDirect3D(&d3d9);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
D3DCAPS9 caps; D3DCAPS9 caps;
@@ -377,6 +385,12 @@ bool DXVA2Renderer::isDecoderBlacklisted()
// TODO: Update for HEVC Main10 // TODO: Update for HEVC Main10
SDL_assert(m_VideoFormat != VIDEO_FORMAT_H265_MAIN10); SDL_assert(m_VideoFormat != VIDEO_FORMAT_H265_MAIN10);
if (qgetenv("DXVA2_DISABLE_DECODER_BLACKLIST") == "1") {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"DXVA2 decoder blacklist is disabled");
return false;
}
hr = m_Device->GetDirect3D(&d3d9); hr = m_Device->GetDirect3D(&d3d9);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
D3DCAPS9 caps; D3DCAPS9 caps;
@@ -685,7 +699,7 @@ bool DXVA2Renderer::initialize(SDL_Window* window, int videoFormat, int width, i
// input focus. This glitches out the renderer and a bunch of other stuff. // input focus. This glitches out the renderer and a bunch of other stuff.
// Direct3D9Ex itself seems to have this minimize on focus loss behavior on its // Direct3D9Ex itself seems to have this minimize on focus loss behavior on its
// own, so just disable SDL's handling of the focus loss event. // own, so just disable SDL's handling of the focus loss event.
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); SDL_SetHintWithPriority(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0", SDL_HINT_OVERRIDE);
return true; return true;
} }
@@ -707,30 +721,61 @@ IFFmpegRenderer::FramePacingConstraint DXVA2Renderer::getFramePacingConstraint()
return PACING_ANY; return PACING_ANY;
} }
void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType) void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type)
{ {
HRESULT hr; HRESULT hr;
// Initialize the overlay font if it's not already created switch (type)
if (m_DebugOverlayFont == nullptr) { {
hr = D3DXCreateFontA(m_Device, case Overlay::OverlayDebug:
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug), if (m_DebugOverlayFont == nullptr) {
0, hr = D3DXCreateFontA(m_Device,
FW_HEAVY, Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug),
1, 0,
false, FW_HEAVY,
ANSI_CHARSET, 1,
OUT_DEFAULT_PRECIS, false,
DEFAULT_QUALITY, ANSI_CHARSET,
DEFAULT_PITCH | FF_DONTCARE, OUT_DEFAULT_PRECIS,
"", DEFAULT_QUALITY,
&m_DebugOverlayFont); DEFAULT_PITCH | FF_DONTCARE,
if (FAILED(hr)) { "",
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, &m_DebugOverlayFont);
"D3DXCreateFontA() failed: %x", if (FAILED(hr)) {
hr); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
m_DebugOverlayFont = nullptr; "D3DXCreateFontA() failed: %x",
hr);
m_DebugOverlayFont = nullptr;
}
} }
break;
case Overlay::OverlayStatusUpdate:
if (m_StatusOverlayFont == nullptr) {
hr = D3DXCreateFontA(m_Device,
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayStatusUpdate),
0,
FW_HEAVY,
1,
false,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"",
&m_StatusOverlayFont);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"D3DXCreateFontA() failed: %x",
hr);
m_StatusOverlayFont = nullptr;
}
}
break;
default:
SDL_assert(false);
break;
} }
} }
@@ -929,6 +974,18 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
} }
} }
if (m_StatusOverlayFont != nullptr) {
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayStatusUpdate)) {
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayStatusUpdate);
m_StatusOverlayFont->DrawTextA(nullptr,
Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayStatusUpdate),
-1,
&sample.DstRect,
DT_RIGHT | DT_NOCLIP,
D3DCOLOR_ARGB(color.a, color.r, color.g, color.b));
}
}
hr = m_Device->EndScene(); hr = m_Device->EndScene();
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -71,5 +71,6 @@ private:
DXVA2_VideoDesc m_Desc; DXVA2_VideoDesc m_Desc;
REFERENCE_TIME m_FrameIndex; REFERENCE_TIME m_FrameIndex;
LPD3DXFONT m_DebugOverlayFont; LPD3DXFONT m_DebugOverlayFont;
LPD3DXFONT m_StatusOverlayFont;
bool m_BlockingPresent; bool m_BlockingPresent;
}; };
@@ -76,7 +76,11 @@ int DxVsyncSource::vsyncThread(void* context)
{ {
DxVsyncSource* me = reinterpret_cast<DxVsyncSource*>(context); DxVsyncSource* me = reinterpret_cast<DxVsyncSource*>(context);
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
#else
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {}; D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
HMONITOR lastMonitor = nullptr; HMONITOR lastMonitor = nullptr;
@@ -33,7 +33,11 @@ int NullThreadedVsyncSource::vsyncThread(void* context)
{ {
NullThreadedVsyncSource* me = reinterpret_cast<NullThreadedVsyncSource*>(context); NullThreadedVsyncSource* me = reinterpret_cast<NullThreadedVsyncSource*>(context);
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
#else
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif
while (SDL_AtomicGet(&me->m_Stopping) == 0) { while (SDL_AtomicGet(&me->m_Stopping) == 0) {
me->m_Pacer->vsyncCallback(1000 / me->m_DisplayFps); me->m_Pacer->vsyncCallback(1000 / me->m_DisplayFps);
@@ -14,6 +14,10 @@
#include "dxvsyncsource.h" #include "dxvsyncsource.h"
#endif #endif
// Limit the number of queued frames to prevent excessive memory consumption
// if the V-Sync source or renderer is blocked for a while.
#define MAX_QUEUED_FRAMES 8
// We may be woken up slightly late so don't go all the way // We may be woken up slightly late so don't go all the way
// up to the next V-sync since we may accidentally step into // up to the next V-sync since we may accidentally step into
// the next V-sync period. It also takes some amount of time // the next V-sync period. It also takes some amount of time
@@ -61,6 +65,12 @@ int Pacer::renderThread(void* context)
{ {
Pacer* me = reinterpret_cast<Pacer*>(context); Pacer* me = reinterpret_cast<Pacer*>(context);
if (SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH) < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to set render thread to high priority: %s",
SDL_GetError());
}
while (!me->m_Stopping) { while (!me->m_Stopping) {
// Acquire the frame queue lock to protect the queue and // Acquire the frame queue lock to protect the queue and
// the not empty condition // the not empty condition
@@ -162,6 +172,7 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
} }
// Place the first frame on the render queue // Place the first frame on the render queue
dropFrameForEnqueue(m_RenderQueue);
m_RenderQueue.enqueue(m_PacingQueue.dequeue()); m_RenderQueue.enqueue(m_PacingQueue.dequeue());
m_FrameQueueLock.unlock(); m_FrameQueueLock.unlock();
m_RenderQueueNotEmpty.wakeOne(); m_RenderQueueNotEmpty.wakeOne();
@@ -253,6 +264,15 @@ void Pacer::renderFrame(AVFrame* frame)
m_FrameQueueLock.unlock(); m_FrameQueueLock.unlock();
} }
void Pacer::dropFrameForEnqueue(QQueue<AVFrame*>& queue)
{
SDL_assert(queue.size() <= MAX_QUEUED_FRAMES);
if (queue.size() == MAX_QUEUED_FRAMES) {
AVFrame* frame = queue.dequeue();
av_frame_free(&frame);
}
}
void Pacer::submitFrame(AVFrame* frame) void Pacer::submitFrame(AVFrame* frame)
{ {
// Make sure initialize() has been called // Make sure initialize() has been called
@@ -261,9 +281,11 @@ void Pacer::submitFrame(AVFrame* frame)
// Queue the frame and possibly wake up the render thread // Queue the frame and possibly wake up the render thread
m_FrameQueueLock.lock(); m_FrameQueueLock.lock();
if (m_VsyncSource != nullptr) { if (m_VsyncSource != nullptr) {
dropFrameForEnqueue(m_PacingQueue);
m_PacingQueue.enqueue(frame); m_PacingQueue.enqueue(frame);
} }
else { else {
dropFrameForEnqueue(m_RenderQueue);
m_RenderQueue.enqueue(frame); m_RenderQueue.enqueue(frame);
} }
m_FrameQueueLock.unlock(); m_FrameQueueLock.unlock();
@@ -31,6 +31,8 @@ private:
void renderFrame(AVFrame* frame); void renderFrame(AVFrame* frame);
void dropFrameForEnqueue(QQueue<AVFrame*>& queue);
QQueue<AVFrame*> m_RenderQueue; QQueue<AVFrame*> m_RenderQueue;
QQueue<AVFrame*> m_PacingQueue; QQueue<AVFrame*> m_PacingQueue;
QQueue<int> m_PacingQueueHistory; QQueue<int> m_PacingQueueHistory;
+106 -67
View File
@@ -9,10 +9,7 @@
SdlRenderer::SdlRenderer() SdlRenderer::SdlRenderer()
: m_Renderer(nullptr), : m_Renderer(nullptr),
m_Texture(nullptr), m_Texture(nullptr)
m_DebugOverlayFont(nullptr),
m_DebugOverlaySurface(nullptr),
m_DebugOverlayTexture(nullptr)
{ {
SDL_assert(TTF_WasInit() == 0); SDL_assert(TTF_WasInit() == 0);
if (TTF_Init() != 0) { if (TTF_Init() != 0) {
@@ -21,23 +18,33 @@ SdlRenderer::SdlRenderer()
TTF_GetError()); TTF_GetError());
return; return;
} }
SDL_zero(m_OverlayFonts);
SDL_zero(m_OverlaySurfaces);
SDL_zero(m_OverlayTextures);
} }
SdlRenderer::~SdlRenderer() SdlRenderer::~SdlRenderer()
{ {
if (m_DebugOverlayFont != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
TTF_CloseFont(m_DebugOverlayFont); if (m_OverlayFonts[i] != nullptr) {
TTF_CloseFont(m_OverlayFonts[i]);
}
} }
TTF_Quit(); TTF_Quit();
SDL_assert(TTF_WasInit() == 0); SDL_assert(TTF_WasInit() == 0);
if (m_DebugOverlayTexture != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
SDL_DestroyTexture(m_DebugOverlayTexture); if (m_OverlayTextures[i] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[i]);
}
} }
if (m_DebugOverlaySurface != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
SDL_FreeSurface(m_DebugOverlaySurface); if (m_OverlaySurfaces[i] != nullptr) {
SDL_FreeSurface(m_OverlaySurfaces[i]);
}
} }
if (m_Texture != nullptr) { if (m_Texture != nullptr) {
@@ -79,45 +86,42 @@ IFFmpegRenderer::FramePacingConstraint SdlRenderer::getFramePacingConstraint()
void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type) void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
{ {
if (type == Overlay::OverlayDebug) { // Construct the required font to render the overlay
if (m_DebugOverlayFont == nullptr) { if (m_OverlayFonts[type] == nullptr) {
QByteArray fontPath = QDir::toNativeSeparators(Path::getDataFilePath("ModeSeven.ttf")).toUtf8(); QByteArray fontData = Path::readDataFile("ModeSeven.ttf");
if (fontPath.isEmpty()) { if (fontData.isEmpty()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to locate SDL overlay font"); "Unable to load SDL overlay font");
return; return;
}
m_DebugOverlayFont = TTF_OpenFont(fontPath.data(),
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug));
if (m_DebugOverlayFont == nullptr) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"TTF_OpenFont() failed: %s",
TTF_GetError());
// Can't proceed without a font
return;
}
} }
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface); m_OverlayFonts[type] = TTF_OpenFontRW(SDL_RWFromConstMem(fontData.constData(), fontData.size()),
1,
Session::get()->getOverlayManager().getOverlayFontSize(type));
if (m_OverlayFonts[type] == nullptr) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"TTF_OpenFont() failed: %s",
TTF_GetError());
// Free the old surface // Can't proceed without a font
if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, oldSurface, nullptr)) { return;
SDL_FreeSurface(oldSurface);
}
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// The _Wrapped variant is required for line breaks to work
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_DebugOverlayFont,
Session::get()->getOverlayManager().getOverlayText(type),
Session::get()->getOverlayManager().getOverlayColor(type),
1000);
SDL_AtomicSetPtr((void**)&m_DebugOverlaySurface, surface);
} }
} }
else {
SDL_assert(false); SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
// Free the old surface
if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], oldSurface, nullptr)) {
SDL_FreeSurface(oldSurface);
}
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// The _Wrapped variant is required for line breaks to work
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_OverlayFonts[type],
Session::get()->getOverlayManager().getOverlayText(type),
Session::get()->getOverlayManager().getOverlayColor(type),
1000);
SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], surface);
} }
} }
@@ -139,6 +143,14 @@ bool SdlRenderer::initialize(SDL_Window* window,
} }
} }
#ifdef Q_OS_WIN32
// We render on a different thread than the main thread which is handling window
// messages. Without D3DCREATE_MULTITHREADED, this can cause a deadlock by blocking
// on a window message being processed while the main thread is blocked waiting for
// the render thread to finish.
SDL_SetHintWithPriority(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, "1", SDL_HINT_OVERRIDE);
#endif
m_Renderer = SDL_CreateRenderer(window, -1, rendererFlags); m_Renderer = SDL_CreateRenderer(window, -1, rendererFlags);
if (!m_Renderer) { if (!m_Renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -168,9 +180,57 @@ bool SdlRenderer::initialize(SDL_Window* window,
return false; return false;
} }
#ifdef Q_OS_WIN32
// For some reason, using Direct3D9Ex breaks this with multi-monitor setups.
// When focus is lost, the window is minimized then immediately restored without
// input focus. This glitches out the renderer and a bunch of other stuff.
// Direct3D9Ex itself seems to have this minimize on focus loss behavior on its
// own, so just disable SDL's handling of the focus loss event.
SDL_SetHintWithPriority(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0", SDL_HINT_OVERRIDE);
#endif
return true; return true;
} }
void SdlRenderer::renderOverlay(Overlay::OverlayType type)
{
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// If a new surface has been created for updated overlay data, convert it into a texture.
// NB: We have to do this conversion at render-time because we can only interact
// with the renderer on a single thread.
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], surface, nullptr)) {
if (m_OverlayTextures[type] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[type]);
}
if (type == Overlay::OverlayStatusUpdate) {
// Bottom Left
int unused;
SDL_RenderGetLogicalSize(m_Renderer, &unused, &m_OverlayRects[type].y);
m_OverlayRects[type].x = 0;
m_OverlayRects[type].y -= surface->h;
}
else if (type == Overlay::OverlayDebug) {
// Top left
m_OverlayRects[type].x = 0;
m_OverlayRects[type].y = 0;
}
m_OverlayRects[type].w = surface->w;
m_OverlayRects[type].h = surface->h;
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface);
SDL_FreeSurface(surface);
}
// If we have an overlay texture, render it too
if (m_OverlayTextures[type] != nullptr) {
SDL_RenderCopy(m_Renderer, m_OverlayTextures[type], nullptr, &m_OverlayRects[type]);
}
}
}
void SdlRenderer::renderFrame(AVFrame* frame) void SdlRenderer::renderFrame(AVFrame* frame)
{ {
SDL_UpdateYUVTexture(m_Texture, nullptr, SDL_UpdateYUVTexture(m_Texture, nullptr,
@@ -187,29 +247,8 @@ void SdlRenderer::renderFrame(AVFrame* frame)
SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr); SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr);
// Draw the overlays // Draw the overlays
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)) { for (int i = 0; i < Overlay::OverlayMax; i++) {
// If a new surface has been created for updated overlay data, convert it into a texture. renderOverlay((Overlay::OverlayType)i);
// NB: We have to do this conversion at render-time because we can only interact
// with the renderer on a single thread.
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface);
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, surface, nullptr)) {
if (m_DebugOverlayTexture != nullptr) {
SDL_DestroyTexture(m_DebugOverlayTexture);
}
m_DebugOverlayRect.x = 0;
m_DebugOverlayRect.y = 0;
m_DebugOverlayRect.w = surface->w;
m_DebugOverlayRect.h = surface->h;
m_DebugOverlayTexture = SDL_CreateTextureFromSurface(m_Renderer, surface);
SDL_FreeSurface(surface);
}
// If we have a debug overlay texture, render it too
if (m_DebugOverlayTexture != nullptr) {
SDL_RenderCopy(m_Renderer, m_DebugOverlayTexture, nullptr, &m_DebugOverlayRect);
}
} }
SDL_RenderPresent(m_Renderer); SDL_RenderPresent(m_Renderer);
@@ -22,11 +22,13 @@ public:
virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
private: private:
void renderOverlay(Overlay::OverlayType type);
SDL_Renderer* m_Renderer; SDL_Renderer* m_Renderer;
SDL_Texture* m_Texture; SDL_Texture* m_Texture;
TTF_Font* m_DebugOverlayFont; TTF_Font* m_OverlayFonts[Overlay::OverlayMax];
SDL_Surface* m_DebugOverlaySurface; SDL_Surface* m_OverlaySurfaces[Overlay::OverlayMax];
SDL_Texture* m_DebugOverlayTexture; SDL_Texture* m_OverlayTextures[Overlay::OverlayMax];
SDL_Rect m_DebugOverlayRect; SDL_Rect m_OverlayRects[Overlay::OverlayMax];
}; };
+57 -25
View File
@@ -22,12 +22,12 @@ public:
: m_HwContext(nullptr), : m_HwContext(nullptr),
m_DisplayLayer(nullptr), m_DisplayLayer(nullptr),
m_FormatDesc(nullptr), m_FormatDesc(nullptr),
m_StreamView(nullptr), m_StreamView(nullptr)
m_DebugOverlayTextField(nullptr)
{ {
SDL_zero(m_OverlayTextFields);
} }
virtual ~VTRenderer() virtual ~VTRenderer() override
{ {
if (m_HwContext != nullptr) { if (m_HwContext != nullptr) {
av_buffer_unref(&m_HwContext); av_buffer_unref(&m_HwContext);
@@ -37,8 +37,10 @@ public:
CFRelease(m_FormatDesc); CFRelease(m_FormatDesc);
} }
if (m_DebugOverlayTextField != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
[m_DebugOverlayTextField removeFromSuperview]; if (m_OverlayTextFields[i] != nullptr) {
[m_OverlayTextFields[i] removeFromSuperview];
}
} }
if (m_StreamView != nullptr) { if (m_StreamView != nullptr) {
@@ -197,38 +199,68 @@ public:
return true; return true;
} }
void updateOverlayOnMainThread(Overlay::OverlayType type)
{
// Lazy initialization for the overlay
if (m_OverlayTextFields[type] == nullptr) {
m_OverlayTextFields[type] = [[NSTextField alloc] initWithFrame:m_StreamView.bounds];
[m_OverlayTextFields[type] setBezeled:NO];
[m_OverlayTextFields[type] setDrawsBackground:NO];
[m_OverlayTextFields[type] setEditable:NO];
[m_OverlayTextFields[type] setSelectable:NO];
switch (type) {
case Overlay::OverlayDebug:
[m_OverlayTextFields[type] setAlignment:NSLeftTextAlignment];
break;
case Overlay::OverlayStatusUpdate:
[m_OverlayTextFields[type] setAlignment:NSRightTextAlignment];
break;
default:
break;
}
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(type);
[m_OverlayTextFields[type] setTextColor:[NSColor colorWithSRGBRed:color.r / 255.0 green:color.g / 255.0 blue:color.b / 255.0 alpha:color.a / 255.0]];
[m_OverlayTextFields[type] setFont:[NSFont messageFontOfSize:Session::get()->getOverlayManager().getOverlayFontSize(type)]];
[m_StreamView addSubview: m_OverlayTextFields[type]];
}
// Update text contents
[m_OverlayTextFields[type] setStringValue: [NSString stringWithUTF8String:Session::get()->getOverlayManager().getOverlayText(type)]];
// Unhide if it's enabled
[m_OverlayTextFields[type] setHidden: !Session::get()->getOverlayManager().isOverlayEnabled(type)];
}
static void updateDebugOverlayOnMainThread(void* context) static void updateDebugOverlayOnMainThread(void* context)
{ {
VTRenderer* me = (VTRenderer*)context; VTRenderer* me = (VTRenderer*)context;
// Lazy initialization for the overlay me->updateOverlayOnMainThread(Overlay::OverlayDebug);
if (me->m_DebugOverlayTextField == nullptr) { }
me->m_DebugOverlayTextField = [[NSTextField alloc] initWithFrame:me->m_StreamView.bounds];
[me->m_DebugOverlayTextField setBezeled:NO];
[me->m_DebugOverlayTextField setDrawsBackground:NO];
[me->m_DebugOverlayTextField setEditable:NO];
[me->m_DebugOverlayTextField setSelectable:NO];
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayDebug); static void updateStatusOverlayOnMainThread(void* context)
[me->m_DebugOverlayTextField setTextColor:[NSColor colorWithSRGBRed:color.r / 255.0 green:color.g / 255.0 blue:color.b / 255.0 alpha:color.a / 255.0]]; {
[me->m_DebugOverlayTextField setFont:[NSFont messageFontOfSize:Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug)]]; VTRenderer* me = (VTRenderer*)context;
[me->m_StreamView addSubview: me->m_DebugOverlayTextField]; me->updateOverlayOnMainThread(Overlay::OverlayStatusUpdate);
}
// Update text contents
[me->m_DebugOverlayTextField setStringValue: [NSString stringWithUTF8String:Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayDebug)]];
// Unhide if it's enabled
[me->m_DebugOverlayTextField setHidden: !Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)];
} }
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override virtual void notifyOverlayUpdated(Overlay::OverlayType type) override
{ {
// We must do the actual UI updates on the main thread, so queue an // We must do the actual UI updates on the main thread, so queue an
// async callback on the main thread via GCD to do the UI update. // async callback on the main thread via GCD to do the UI update.
if (type == Overlay::OverlayDebug) { switch (type) {
case Overlay::OverlayDebug:
dispatch_async_f(dispatch_get_main_queue(), this, updateDebugOverlayOnMainThread); dispatch_async_f(dispatch_get_main_queue(), this, updateDebugOverlayOnMainThread);
break;
case Overlay::OverlayStatusUpdate:
dispatch_async_f(dispatch_get_main_queue(), this, updateStatusOverlayOnMainThread);
break;
default:
break;
} }
} }
@@ -269,7 +301,7 @@ private:
AVSampleBufferDisplayLayer* m_DisplayLayer; AVSampleBufferDisplayLayer* m_DisplayLayer;
CMVideoFormatDescriptionRef m_FormatDesc; CMVideoFormatDescriptionRef m_FormatDesc;
NSView* m_StreamView; NSView* m_StreamView;
NSTextField* m_DebugOverlayTextField; NSTextField* m_OverlayTextFields[Overlay::OverlayMax];
}; };
IFFmpegRenderer* VTRendererFactory::createRenderer() { IFFmpegRenderer* VTRendererFactory::createRenderer() {
+1 -1
View File
@@ -37,7 +37,7 @@ bool FFmpegVideoDecoder::isHardwareAccelerated()
int FFmpegVideoDecoder::getDecoderCapabilities() int FFmpegVideoDecoder::getDecoderCapabilities()
{ {
return CAPABILITY_DIRECT_SUBMIT | m_Renderer->getDecoderCapabilities(); return m_Renderer->getDecoderCapabilities();
} }
enum AVPixelFormat FFmpegVideoDecoder::ffGetFormat(AVCodecContext* context, enum AVPixelFormat FFmpegVideoDecoder::ffGetFormat(AVCodecContext* context,
+3
View File
@@ -9,6 +9,9 @@ OverlayManager::OverlayManager() :
m_Overlays[OverlayType::OverlayDebug].color = {0xD0, 0xD0, 0x00, 0xFF}; m_Overlays[OverlayType::OverlayDebug].color = {0xD0, 0xD0, 0x00, 0xFF};
m_Overlays[OverlayType::OverlayDebug].fontSize = 20; m_Overlays[OverlayType::OverlayDebug].fontSize = 20;
m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF};
m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36;
} }
bool OverlayManager::isOverlayEnabled(OverlayType type) bool OverlayManager::isOverlayEnabled(OverlayType type)
+1 -2
View File
@@ -8,8 +8,7 @@ namespace Overlay {
enum OverlayType { enum OverlayType {
OverlayDebug, OverlayDebug,
OverlayMinorNotification, OverlayStatusUpdate,
OverlayMajorNotification,
OverlayMax OverlayMax
}; };
@@ -1,4 +1,4 @@
#include "sl.h" #include "slvid.h"
SLVideoDecoder::SLVideoDecoder(bool) SLVideoDecoder::SLVideoDecoder(bool)
: m_VideoContext(nullptr), : m_VideoContext(nullptr),
File renamed without changes.
+1 -1
View File
@@ -1 +1 @@
0.9.0 0.10.0
+3
View File
@@ -0,0 +1,3 @@
SOURCES = main.cpp
LIBS += -lSLVideo -lSLAudio
@@ -1,7 +1,9 @@
#include <SLVideo.h> #include <SLVideo.h>
#include <SLAudio.h>
int main() int main()
{ {
SLVideo_CreateContext(); SLVideo_CreateContext();
SLAudio_CreateContext();
return 0; return 0;
} }
-3
View File
@@ -1,3 +0,0 @@
SOURCES = main.cpp
LIBS += -lSLVideo
+1
View File
@@ -956,6 +956,7 @@ int ERR_load_ASN1_strings(void);
# define ASN1_F_DO_BUF 142 # define ASN1_F_DO_BUF 142
# define ASN1_F_DO_TCREATE 222 # define ASN1_F_DO_TCREATE 222
# define ASN1_F_I2D_ASN1_BIO_STREAM 211 # define ASN1_F_I2D_ASN1_BIO_STREAM 211
# define ASN1_F_I2D_ASN1_OBJECT 143
# define ASN1_F_I2D_DSA_PUBKEY 161 # define ASN1_F_I2D_DSA_PUBKEY 161
# define ASN1_F_I2D_EC_PUBKEY 181 # define ASN1_F_I2D_EC_PUBKEY 181
# define ASN1_F_I2D_PRIVATEKEY 163 # define ASN1_F_I2D_PRIVATEKEY 163
+8 -8
View File
@@ -730,26 +730,26 @@ __bio_h__attr__((__format__(__printf__, 3, 0)));
BIO_METHOD *BIO_meth_new(int type, const char *name); BIO_METHOD *BIO_meth_new(int type, const char *name);
void BIO_meth_free(BIO_METHOD *biom); void BIO_meth_free(BIO_METHOD *biom);
int (*BIO_meth_get_write(BIO_METHOD *biom)) (BIO *, const char *, int); int (*BIO_meth_get_write(const BIO_METHOD *biom)) (BIO *, const char *, int);
int BIO_meth_set_write(BIO_METHOD *biom, int BIO_meth_set_write(BIO_METHOD *biom,
int (*write) (BIO *, const char *, int)); int (*write) (BIO *, const char *, int));
int (*BIO_meth_get_read(BIO_METHOD *biom)) (BIO *, char *, int); int (*BIO_meth_get_read(const BIO_METHOD *biom)) (BIO *, char *, int);
int BIO_meth_set_read(BIO_METHOD *biom, int BIO_meth_set_read(BIO_METHOD *biom,
int (*read) (BIO *, char *, int)); int (*read) (BIO *, char *, int));
int (*BIO_meth_get_puts(BIO_METHOD *biom)) (BIO *, const char *); int (*BIO_meth_get_puts(const BIO_METHOD *biom)) (BIO *, const char *);
int BIO_meth_set_puts(BIO_METHOD *biom, int BIO_meth_set_puts(BIO_METHOD *biom,
int (*puts) (BIO *, const char *)); int (*puts) (BIO *, const char *));
int (*BIO_meth_get_gets(BIO_METHOD *biom)) (BIO *, char *, int); int (*BIO_meth_get_gets(const BIO_METHOD *biom)) (BIO *, char *, int);
int BIO_meth_set_gets(BIO_METHOD *biom, int BIO_meth_set_gets(BIO_METHOD *biom,
int (*gets) (BIO *, char *, int)); int (*gets) (BIO *, char *, int));
long (*BIO_meth_get_ctrl(BIO_METHOD *biom)) (BIO *, int, long, void *); long (*BIO_meth_get_ctrl(const BIO_METHOD *biom)) (BIO *, int, long, void *);
int BIO_meth_set_ctrl(BIO_METHOD *biom, int BIO_meth_set_ctrl(BIO_METHOD *biom,
long (*ctrl) (BIO *, int, long, void *)); long (*ctrl) (BIO *, int, long, void *));
int (*BIO_meth_get_create(BIO_METHOD *bion)) (BIO *); int (*BIO_meth_get_create(const BIO_METHOD *bion)) (BIO *);
int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *)); int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *));
int (*BIO_meth_get_destroy(BIO_METHOD *biom)) (BIO *); int (*BIO_meth_get_destroy(const BIO_METHOD *biom)) (BIO *);
int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *)); int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *));
long (*BIO_meth_get_callback_ctrl(BIO_METHOD *biom)) long (*BIO_meth_get_callback_ctrl(const BIO_METHOD *biom))
(BIO *, int, BIO_info_cb *); (BIO *, int, BIO_info_cb *);
int BIO_meth_set_callback_ctrl(BIO_METHOD *biom, int BIO_meth_set_callback_ctrl(BIO_METHOD *biom,
long (*callback_ctrl) (BIO *, int, long (*callback_ctrl) (BIO *, int,
+70 -19
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -119,25 +119,76 @@ void *BN_GENCB_get_arg(BN_GENCB *cb);
* on the size of the number */ * on the size of the number */
/* /*
* number of Miller-Rabin iterations for an error rate of less than 2^-80 for * BN_prime_checks_for_size() returns the number of Miller-Rabin iterations
* random 'b'-bit input, b >= 100 (taken from table 4.4 in the Handbook of * that will be done for checking that a random number is probably prime. The
* Applied Cryptography [Menezes, van Oorschot, Vanstone; CRC Press 1996]; * error rate for accepting a composite number as prime depends on the size of
* original paper: Damgaard, Landrock, Pomerance: Average case error * the prime |b|. The error rates used are for calculating an RSA key with 2 primes,
* estimates for the strong probable prime test. -- Math. Comp. 61 (1993) * and so the level is what you would expect for a key of double the size of the
* 177-194) * prime.
*
* This table is generated using the algorithm of FIPS PUB 186-4
* Digital Signature Standard (DSS), section F.1, page 117.
* (https://dx.doi.org/10.6028/NIST.FIPS.186-4)
*
* The following magma script was used to generate the output:
* securitybits:=125;
* k:=1024;
* for t:=1 to 65 do
* for M:=3 to Floor(2*Sqrt(k-1)-1) do
* S:=0;
* // Sum over m
* for m:=3 to M do
* s:=0;
* // Sum over j
* for j:=2 to m do
* s+:=(RealField(32)!2)^-(j+(k-1)/j);
* end for;
* S+:=2^(m-(m-1)*t)*s;
* end for;
* A:=2^(k-2-M*t);
* B:=8*(Pi(RealField(32))^2-6)/3*2^(k-2)*S;
* pkt:=2.00743*Log(2)*k*2^-k*(A+B);
* seclevel:=Floor(-Log(2,pkt));
* if seclevel ge securitybits then
* printf "k: %5o, security: %o bits (t: %o, M: %o)\n",k,seclevel,t,M;
* break;
* end if;
* end for;
* if seclevel ge securitybits then break; end if;
* end for;
*
* It can be run online at:
* http://magma.maths.usyd.edu.au/calc
*
* And will output:
* k: 1024, security: 129 bits (t: 6, M: 23)
*
* k is the number of bits of the prime, securitybits is the level we want to
* reach.
*
* prime length | RSA key size | # MR tests | security level
* -------------+--------------|------------+---------------
* (b) >= 6394 | >= 12788 | 3 | 256 bit
* (b) >= 3747 | >= 7494 | 3 | 192 bit
* (b) >= 1345 | >= 2690 | 4 | 128 bit
* (b) >= 1080 | >= 2160 | 5 | 128 bit
* (b) >= 852 | >= 1704 | 5 | 112 bit
* (b) >= 476 | >= 952 | 5 | 80 bit
* (b) >= 400 | >= 800 | 6 | 80 bit
* (b) >= 347 | >= 694 | 7 | 80 bit
* (b) >= 308 | >= 616 | 8 | 80 bit
* (b) >= 55 | >= 110 | 27 | 64 bit
* (b) >= 6 | >= 12 | 34 | 64 bit
*/ */
# define BN_prime_checks_for_size(b) ((b) >= 1300 ? 2 : \
(b) >= 850 ? 3 : \ # define BN_prime_checks_for_size(b) ((b) >= 3747 ? 3 : \
(b) >= 650 ? 4 : \ (b) >= 1345 ? 4 : \
(b) >= 550 ? 5 : \ (b) >= 476 ? 5 : \
(b) >= 450 ? 6 : \ (b) >= 400 ? 6 : \
(b) >= 400 ? 7 : \ (b) >= 347 ? 7 : \
(b) >= 350 ? 8 : \ (b) >= 308 ? 8 : \
(b) >= 300 ? 9 : \ (b) >= 55 ? 27 : \
(b) >= 250 ? 12 : \ /* b >= 6 */ 34)
(b) >= 200 ? 15 : \
(b) >= 150 ? 18 : \
/* b >= 100 */ 27)
# define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) # define BN_num_bytes(a) ((BN_num_bits(a)+7)/8)
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
+2 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -187,7 +187,7 @@ void DH_meth_free(DH_METHOD *dhm);
DH_METHOD *DH_meth_dup(const DH_METHOD *dhm); DH_METHOD *DH_meth_dup(const DH_METHOD *dhm);
const char *DH_meth_get0_name(const DH_METHOD *dhm); const char *DH_meth_get0_name(const DH_METHOD *dhm);
int DH_meth_set1_name(DH_METHOD *dhm, const char *name); int DH_meth_set1_name(DH_METHOD *dhm, const char *name);
int DH_meth_get_flags(DH_METHOD *dhm); int DH_meth_get_flags(const DH_METHOD *dhm);
int DH_meth_set_flags(DH_METHOD *dhm, int flags); int DH_meth_set_flags(DH_METHOD *dhm, int flags);
void *DH_meth_get0_app_data(const DH_METHOD *dhm); void *DH_meth_get0_app_data(const DH_METHOD *dhm);
int DH_meth_set0_app_data(DH_METHOD *dhm, void *app_data); int DH_meth_set0_app_data(DH_METHOD *dhm, void *app_data);
+7 -5
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -146,10 +146,12 @@ int DSAparams_print_fp(FILE *fp, const DSA *x);
int DSA_print_fp(FILE *bp, const DSA *x, int off); int DSA_print_fp(FILE *bp, const DSA *x, int off);
# endif # endif
# define DSS_prime_checks 50 # define DSS_prime_checks 64
/* /*
* Primality test according to FIPS PUB 186[-1], Appendix 2.1: 50 rounds of * Primality test according to FIPS PUB 186-4, Appendix C.3. Since we only
* Rabin-Miller * have one value here we set the number of checks to 64 which is the 128 bit
* security level that is the highest level and valid for creating a 3072 bit
* DSA key.
*/ */
# define DSA_is_prime(n, callback, cb_arg) \ # define DSA_is_prime(n, callback, cb_arg) \
BN_is_prime(n, DSS_prime_checks, callback, NULL, cb_arg) BN_is_prime(n, DSS_prime_checks, callback, NULL, cb_arg)
@@ -186,7 +188,7 @@ void DSA_meth_free(DSA_METHOD *dsam);
DSA_METHOD *DSA_meth_dup(const DSA_METHOD *dsam); DSA_METHOD *DSA_meth_dup(const DSA_METHOD *dsam);
const char *DSA_meth_get0_name(const DSA_METHOD *dsam); const char *DSA_meth_get0_name(const DSA_METHOD *dsam);
int DSA_meth_set1_name(DSA_METHOD *dsam, const char *name); int DSA_meth_set1_name(DSA_METHOD *dsam, const char *name);
int DSA_meth_get_flags(DSA_METHOD *dsam); int DSA_meth_get_flags(const DSA_METHOD *dsam);
int DSA_meth_set_flags(DSA_METHOD *dsam, int flags); int DSA_meth_set_flags(DSA_METHOD *dsam, int flags);
void *DSA_meth_get0_app_data(const DSA_METHOD *dsam); void *DSA_meth_get0_app_data(const DSA_METHOD *dsam);
int DSA_meth_set0_app_data(DSA_METHOD *dsam, void *app_data); int DSA_meth_set0_app_data(DSA_METHOD *dsam, void *app_data);
+2 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2002-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -1424,6 +1424,7 @@ int ERR_load_EC_strings(void);
# define EC_F_EC_GFP_NIST_FIELD_MUL 200 # define EC_F_EC_GFP_NIST_FIELD_MUL 200
# define EC_F_EC_GFP_NIST_FIELD_SQR 201 # define EC_F_EC_GFP_NIST_FIELD_SQR 201
# define EC_F_EC_GFP_NIST_GROUP_SET_CURVE 202 # define EC_F_EC_GFP_NIST_GROUP_SET_CURVE 202
# define EC_F_EC_GFP_SIMPLE_BLIND_COORDINATES 287
# define EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT 165 # define EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT 165
# define EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE 166 # define EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE 166
# define EC_F_EC_GFP_SIMPLE_MAKE_AFFINE 102 # define EC_F_EC_GFP_SIMPLE_MAKE_AFFINE 102
+14 -14
View File
@@ -1351,34 +1351,34 @@ void EVP_PKEY_meth_set_ctrl(EVP_PKEY_METHOD *pmeth,
const char *type, const char *type,
const char *value)); const char *value));
void EVP_PKEY_meth_get_init(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_init(const EVP_PKEY_METHOD *pmeth,
int (**pinit) (EVP_PKEY_CTX *ctx)); int (**pinit) (EVP_PKEY_CTX *ctx));
void EVP_PKEY_meth_get_copy(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_copy(const EVP_PKEY_METHOD *pmeth,
int (**pcopy) (EVP_PKEY_CTX *dst, int (**pcopy) (EVP_PKEY_CTX *dst,
EVP_PKEY_CTX *src)); EVP_PKEY_CTX *src));
void EVP_PKEY_meth_get_cleanup(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_cleanup(const EVP_PKEY_METHOD *pmeth,
void (**pcleanup) (EVP_PKEY_CTX *ctx)); void (**pcleanup) (EVP_PKEY_CTX *ctx));
void EVP_PKEY_meth_get_paramgen(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_paramgen(const EVP_PKEY_METHOD *pmeth,
int (**pparamgen_init) (EVP_PKEY_CTX *ctx), int (**pparamgen_init) (EVP_PKEY_CTX *ctx),
int (**pparamgen) (EVP_PKEY_CTX *ctx, int (**pparamgen) (EVP_PKEY_CTX *ctx,
EVP_PKEY *pkey)); EVP_PKEY *pkey));
void EVP_PKEY_meth_get_keygen(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_keygen(const EVP_PKEY_METHOD *pmeth,
int (**pkeygen_init) (EVP_PKEY_CTX *ctx), int (**pkeygen_init) (EVP_PKEY_CTX *ctx),
int (**pkeygen) (EVP_PKEY_CTX *ctx, int (**pkeygen) (EVP_PKEY_CTX *ctx,
EVP_PKEY *pkey)); EVP_PKEY *pkey));
void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_sign(const EVP_PKEY_METHOD *pmeth,
int (**psign_init) (EVP_PKEY_CTX *ctx), int (**psign_init) (EVP_PKEY_CTX *ctx),
int (**psign) (EVP_PKEY_CTX *ctx, int (**psign) (EVP_PKEY_CTX *ctx,
unsigned char *sig, size_t *siglen, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, const unsigned char *tbs,
size_t tbslen)); size_t tbslen));
void EVP_PKEY_meth_get_verify(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_verify(const EVP_PKEY_METHOD *pmeth,
int (**pverify_init) (EVP_PKEY_CTX *ctx), int (**pverify_init) (EVP_PKEY_CTX *ctx),
int (**pverify) (EVP_PKEY_CTX *ctx, int (**pverify) (EVP_PKEY_CTX *ctx,
const unsigned char *sig, const unsigned char *sig,
@@ -1386,7 +1386,7 @@ void EVP_PKEY_meth_get_verify(EVP_PKEY_METHOD *pmeth,
const unsigned char *tbs, const unsigned char *tbs,
size_t tbslen)); size_t tbslen));
void EVP_PKEY_meth_get_verify_recover(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_verify_recover(const EVP_PKEY_METHOD *pmeth,
int (**pverify_recover_init) (EVP_PKEY_CTX int (**pverify_recover_init) (EVP_PKEY_CTX
*ctx), *ctx),
int (**pverify_recover) (EVP_PKEY_CTX int (**pverify_recover) (EVP_PKEY_CTX
@@ -1398,7 +1398,7 @@ void EVP_PKEY_meth_get_verify_recover(EVP_PKEY_METHOD *pmeth,
char *tbs, char *tbs,
size_t tbslen)); size_t tbslen));
void EVP_PKEY_meth_get_signctx(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_signctx(const EVP_PKEY_METHOD *pmeth,
int (**psignctx_init) (EVP_PKEY_CTX *ctx, int (**psignctx_init) (EVP_PKEY_CTX *ctx,
EVP_MD_CTX *mctx), EVP_MD_CTX *mctx),
int (**psignctx) (EVP_PKEY_CTX *ctx, int (**psignctx) (EVP_PKEY_CTX *ctx,
@@ -1406,7 +1406,7 @@ void EVP_PKEY_meth_get_signctx(EVP_PKEY_METHOD *pmeth,
size_t *siglen, size_t *siglen,
EVP_MD_CTX *mctx)); EVP_MD_CTX *mctx));
void EVP_PKEY_meth_get_verifyctx(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_verifyctx(const EVP_PKEY_METHOD *pmeth,
int (**pverifyctx_init) (EVP_PKEY_CTX *ctx, int (**pverifyctx_init) (EVP_PKEY_CTX *ctx,
EVP_MD_CTX *mctx), EVP_MD_CTX *mctx),
int (**pverifyctx) (EVP_PKEY_CTX *ctx, int (**pverifyctx) (EVP_PKEY_CTX *ctx,
@@ -1414,7 +1414,7 @@ void EVP_PKEY_meth_get_verifyctx(EVP_PKEY_METHOD *pmeth,
int siglen, int siglen,
EVP_MD_CTX *mctx)); EVP_MD_CTX *mctx));
void EVP_PKEY_meth_get_encrypt(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_encrypt(const EVP_PKEY_METHOD *pmeth,
int (**pencrypt_init) (EVP_PKEY_CTX *ctx), int (**pencrypt_init) (EVP_PKEY_CTX *ctx),
int (**pencryptfn) (EVP_PKEY_CTX *ctx, int (**pencryptfn) (EVP_PKEY_CTX *ctx,
unsigned char *out, unsigned char *out,
@@ -1422,7 +1422,7 @@ void EVP_PKEY_meth_get_encrypt(EVP_PKEY_METHOD *pmeth,
const unsigned char *in, const unsigned char *in,
size_t inlen)); size_t inlen));
void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_decrypt(const EVP_PKEY_METHOD *pmeth,
int (**pdecrypt_init) (EVP_PKEY_CTX *ctx), int (**pdecrypt_init) (EVP_PKEY_CTX *ctx),
int (**pdecrypt) (EVP_PKEY_CTX *ctx, int (**pdecrypt) (EVP_PKEY_CTX *ctx,
unsigned char *out, unsigned char *out,
@@ -1430,13 +1430,13 @@ void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth,
const unsigned char *in, const unsigned char *in,
size_t inlen)); size_t inlen));
void EVP_PKEY_meth_get_derive(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_derive(const EVP_PKEY_METHOD *pmeth,
int (**pderive_init) (EVP_PKEY_CTX *ctx), int (**pderive_init) (EVP_PKEY_CTX *ctx),
int (**pderive) (EVP_PKEY_CTX *ctx, int (**pderive) (EVP_PKEY_CTX *ctx,
unsigned char *key, unsigned char *key,
size_t *keylen)); size_t *keylen));
void EVP_PKEY_meth_get_ctrl(EVP_PKEY_METHOD *pmeth, void EVP_PKEY_meth_get_ctrl(const EVP_PKEY_METHOD *pmeth,
int (**pctrl) (EVP_PKEY_CTX *ctx, int type, int p1, int (**pctrl) (EVP_PKEY_CTX *ctx, int type, int p1,
void *p2), void *p2),
int (**pctrl_str) (EVP_PKEY_CTX *ctx, int (**pctrl_str) (EVP_PKEY_CTX *ctx,
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
+3 -4
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -92,7 +92,6 @@ typedef struct ocsp_resp_bytes_st OCSP_RESPBYTES;
# define V_OCSP_RESPID_KEY 1 # define V_OCSP_RESPID_KEY 1
DEFINE_STACK_OF(OCSP_RESPID) DEFINE_STACK_OF(OCSP_RESPID)
DECLARE_ASN1_FUNCTIONS(OCSP_RESPID)
typedef struct ocsp_revoked_info_st OCSP_REVOKEDINFO; typedef struct ocsp_revoked_info_st OCSP_REVOKEDINFO;
@@ -159,8 +158,6 @@ int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it,
int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, ASN1_VALUE **pval, int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, ASN1_VALUE **pval,
const ASN1_ITEM *it); const ASN1_ITEM *it);
BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx); BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx);
int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it,
ASN1_VALUE *val);
int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path); int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path);
int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req); int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req);
int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
@@ -194,6 +191,8 @@ int OCSP_response_status(OCSP_RESPONSE *resp);
OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp); OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp);
const ASN1_OCTET_STRING *OCSP_resp_get0_signature(const OCSP_BASICRESP *bs); const ASN1_OCTET_STRING *OCSP_resp_get0_signature(const OCSP_BASICRESP *bs);
const X509_ALGOR *OCSP_resp_get0_tbs_sigalg(const OCSP_BASICRESP *bs);
const OCSP_RESPDATA *OCSP_resp_get0_respdata(const OCSP_BASICRESP *bs);
int OCSP_resp_get0_signer(OCSP_BASICRESP *bs, X509 **signer, int OCSP_resp_get0_signer(OCSP_BASICRESP *bs, X509 **signer,
STACK_OF(X509) *extra_certs); STACK_OF(X509) *extra_certs);
+15 -6
View File
@@ -26,6 +26,9 @@ extern "C" {
# define OPENSSL_SYS_WIN64A 1 # define OPENSSL_SYS_WIN64A 1
#endif #endif
#define OPENSSL_MIN_API 0x10100000L #define OPENSSL_MIN_API 0x10100000L
#ifndef OPENSSL_NO_COMP
# define OPENSSL_NO_COMP
#endif
#ifndef OPENSSL_NO_MD2 #ifndef OPENSSL_NO_MD2
# define OPENSSL_NO_MD2 # define OPENSSL_NO_MD2
#endif #endif
@@ -106,12 +109,18 @@ extern "C" {
* still won't see them if the library has been built to disable deprecated * still won't see them if the library has been built to disable deprecated
* functions. * functions.
*/ */
#if defined(OPENSSL_NO_DEPRECATED) #ifndef DECLARE_DEPRECATED
# define DECLARE_DEPRECATED(f) # if defined(OPENSSL_NO_DEPRECATED)
#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0) # define DECLARE_DEPRECATED(f)
# define DECLARE_DEPRECATED(f) f __attribute__ ((deprecated)); # else
#else # define DECLARE_DEPRECATED(f) f;
# define DECLARE_DEPRECATED(f) f; # ifdef __GNUC__
# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)
# undef DECLARE_DEPRECATED
# define DECLARE_DEPRECATED(f) f __attribute__ ((deprecated));
# endif
# endif
# endif
#endif #endif
#ifndef OPENSSL_FILE #ifndef OPENSSL_FILE
+3 -3
View File
@@ -39,11 +39,11 @@ extern "C" {
* (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for
* major minor fix final patch/beta) * major minor fix final patch/beta)
*/ */
# define OPENSSL_VERSION_NUMBER 0x10100090L # define OPENSSL_VERSION_NUMBER 0x101000b0L
# ifdef OPENSSL_FIPS # ifdef OPENSSL_FIPS
# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.0i-fips-dev xx XXX xxxx" # define OPENSSL_VERSION_TEXT "OpenSSL 1.1.0k-fips-dev xx XXX xxxx"
# else # else
# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.0i-dev xx XXX xxxx" # define OPENSSL_VERSION_TEXT "OpenSSL 1.1.0k-dev xx XXX xxxx"
# endif # endif
/*- /*-
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
+4 -4
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -374,7 +374,7 @@ void RSA_meth_free(RSA_METHOD *meth);
RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth); RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth);
const char *RSA_meth_get0_name(const RSA_METHOD *meth); const char *RSA_meth_get0_name(const RSA_METHOD *meth);
int RSA_meth_set1_name(RSA_METHOD *meth, const char *name); int RSA_meth_set1_name(RSA_METHOD *meth, const char *name);
int RSA_meth_get_flags(RSA_METHOD *meth); int RSA_meth_get_flags(const RSA_METHOD *meth);
int RSA_meth_set_flags(RSA_METHOD *meth, int flags); int RSA_meth_set_flags(RSA_METHOD *meth, int flags);
void *RSA_meth_get0_app_data(const RSA_METHOD *meth); void *RSA_meth_get0_app_data(const RSA_METHOD *meth);
int RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data); int RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data);
@@ -407,9 +407,9 @@ int RSA_meth_set_priv_dec(RSA_METHOD *rsa,
unsigned char *to, RSA *rsa, unsigned char *to, RSA *rsa,
int padding)); int padding));
int (*RSA_meth_get_mod_exp(const RSA_METHOD *meth)) int (*RSA_meth_get_mod_exp(const RSA_METHOD *meth))
(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx); (BIGNUM *r0, const BIGNUM *i, RSA *rsa, BN_CTX *ctx);
int RSA_meth_set_mod_exp(RSA_METHOD *rsa, int RSA_meth_set_mod_exp(RSA_METHOD *rsa,
int (*mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, int (*mod_exp) (BIGNUM *r0, const BIGNUM *i, RSA *rsa,
BN_CTX *ctx)); BN_CTX *ctx));
int (*RSA_meth_get_bn_mod_exp(const RSA_METHOD *meth)) int (*RSA_meth_get_bn_mod_exp(const RSA_METHOD *meth))
(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, (BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+1 -1
View File
@@ -967,8 +967,8 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count);
# define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 # define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
# define SSL_VERIFY_CLIENT_ONCE 0x04 # define SSL_VERIFY_CLIENT_ONCE 0x04
# define OpenSSL_add_ssl_algorithms() SSL_library_init()
# if OPENSSL_API_COMPAT < 0x10100000L # if OPENSSL_API_COMPAT < 0x10100000L
# define OpenSSL_add_ssl_algorithms() SSL_library_init()
# define SSLeay_add_ssl_algorithms() SSL_library_init() # define SSLeay_add_ssl_algorithms() SSL_library_init()
# endif # endif
+9 -3
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -252,9 +252,15 @@ extern "C" {
# define SSL3_CT_FORTEZZA_DMS 20 # define SSL3_CT_FORTEZZA_DMS 20
/* /*
* SSL3_CT_NUMBER is used to size arrays and it must be large enough to * SSL3_CT_NUMBER is used to size arrays and it must be large enough to
* contain all of the cert types defined either for SSLv3 and TLSv1. * contain all of the cert types defined for *either* SSLv3 and TLSv1.
*/ */
# define SSL3_CT_NUMBER 9 # define SSL3_CT_NUMBER 10
# if defined(TLS_CT_NUMBER)
# if TLS_CT_NUMBER != SSL3_CT_NUMBER
# error "SSL/TLS CT_NUMBER values do not match"
# endif
# endif
# define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001 # define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
+1 -16
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -28,21 +28,6 @@
# undef i2d_ECPKPARAMETERS # undef i2d_ECPKPARAMETERS
# define i2d_ECPKPARAMETERS i2d_UC_ECPKPARAMETERS # define i2d_ECPKPARAMETERS i2d_UC_ECPKPARAMETERS
/*
* These functions do not seem to exist! However, I'm paranoid... Original
* command in x509v3.h: These functions are being redefined in another
* directory, and clash when the linker is case-insensitive, so let's hide
* them a little, by giving them an extra 'o' at the beginning of the name...
*/
# undef X509v3_cleanup_extensions
# define X509v3_cleanup_extensions oX509v3_cleanup_extensions
# undef X509v3_add_extension
# define X509v3_add_extension oX509v3_add_extension
# undef X509v3_add_netscape_extensions
# define X509v3_add_netscape_extensions oX509v3_add_netscape_extensions
# undef X509v3_add_standard_extensions
# define X509v3_add_standard_extensions oX509v3_add_standard_extensions
/* This one clashes with CMS_data_create */ /* This one clashes with CMS_data_create */
# undef cms_Data_create # undef cms_Data_create
# define cms_Data_create priv_cms_Data_create # define cms_Data_create priv_cms_Data_create
+8 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -883,7 +883,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB,(void (*)(void))cb)
* when correcting this number, correct also SSL3_CT_NUMBER in ssl3.h (see * when correcting this number, correct also SSL3_CT_NUMBER in ssl3.h (see
* comment there) * comment there)
*/ */
# define TLS_CT_NUMBER 9 # define TLS_CT_NUMBER 10
# if defined(SSL3_CT_NUMBER)
# if TLS_CT_NUMBER != SSL3_CT_NUMBER
# error "SSL/TLS CT_NUMBER values do not match"
# endif
# endif
# define TLS1_FINISH_MAC_LENGTH 12 # define TLS1_FINISH_MAC_LENGTH 12
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
+2 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* *
* Licensed under the OpenSSL license (the "License"). You may not use * Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy * this file except in compliance with the License. You can obtain a copy
@@ -397,7 +397,7 @@ int (*X509_LOOKUP_meth_get_new_item(const X509_LOOKUP_METHOD* method))
(X509_LOOKUP *ctx); (X509_LOOKUP *ctx);
int X509_LOOKUP_meth_set_free(X509_LOOKUP_METHOD *method, int X509_LOOKUP_meth_set_free(X509_LOOKUP_METHOD *method,
void (*free) (X509_LOOKUP *ctx)); void (*free_fn) (X509_LOOKUP *ctx));
void (*X509_LOOKUP_meth_get_free(const X509_LOOKUP_METHOD* method)) void (*X509_LOOKUP_meth_get_free(const X509_LOOKUP_METHOD* method))
(X509_LOOKUP *ctx); (X509_LOOKUP *ctx);
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more