├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
├── auto-comment.yml
├── no-response.yml
└── stale.yml
├── .gitignore
├── .gitmodules
├── AntiHooking
├── AntiHooking.pro
├── antihookingprotection.cpp
└── antihookingprotection.h
├── LICENSE
├── README.md
├── app
├── Info.plist
├── ModeSeven.ttf
├── Moonlight.exe.manifest
├── SDL_compat.h
├── app.pro
├── backend
│ ├── autoupdatechecker.cpp
│ ├── autoupdatechecker.h
│ ├── boxartmanager.cpp
│ ├── boxartmanager.h
│ ├── computermanager.cpp
│ ├── computermanager.h
│ ├── computerseeker.cpp
│ ├── computerseeker.h
│ ├── identitymanager.cpp
│ ├── identitymanager.h
│ ├── nvaddress.cpp
│ ├── nvaddress.h
│ ├── nvapp.cpp
│ ├── nvapp.h
│ ├── nvcomputer.cpp
│ ├── nvcomputer.h
│ ├── nvhttp.cpp
│ ├── nvhttp.h
│ ├── nvpairingmanager.cpp
│ ├── nvpairingmanager.h
│ ├── richpresencemanager.cpp
│ ├── richpresencemanager.h
│ ├── systemproperties.cpp
│ └── systemproperties.h
├── cli
│ ├── commandlineparser.cpp
│ ├── commandlineparser.h
│ ├── listapps.cpp
│ ├── listapps.h
│ ├── pair.cpp
│ ├── pair.h
│ ├── quitstream.cpp
│ ├── quitstream.h
│ ├── startstream.cpp
│ └── startstream.h
├── deploy
│ ├── linux
│ │ ├── appimage
│ │ │ └── 0001-Vulkan-Don-t-try-to-reuse-old-swapchain.patch
│ │ ├── com.moonlight_stream.Moonlight.appdata.xml
│ │ └── com.moonlight_stream.Moonlight.desktop
│ └── steamlink
│ │ ├── moonlight.png
│ │ ├── moonlight.sh
│ │ └── toc.txt
├── gui
│ ├── AppView.qml
│ ├── AutoResizingComboBox.qml
│ ├── CenteredGridView.qml
│ ├── CliPair.qml
│ ├── CliQuitStreamSegue.qml
│ ├── CliStartStreamSegue.qml
│ ├── ErrorMessageDialog.qml
│ ├── GamepadMapper.qml
│ ├── NavigableDialog.qml
│ ├── NavigableItemDelegate.qml
│ ├── NavigableMenu.qml
│ ├── NavigableMenuItem.qml
│ ├── NavigableMessageDialog.qml
│ ├── NavigableToolButton.qml
│ ├── PcView.qml
│ ├── QuitSegue.qml
│ ├── SettingsView.qml
│ ├── StreamSegue.qml
│ ├── appmodel.cpp
│ ├── appmodel.h
│ ├── computermodel.cpp
│ ├── computermodel.h
│ ├── main.qml
│ ├── sdlgamepadkeynavigation.cpp
│ └── sdlgamepadkeynavigation.h
├── languages
│ ├── qml_bg.qm
│ ├── qml_bg.ts
│ ├── qml_ckb.qm
│ ├── qml_ckb.ts
│ ├── qml_cs.qm
│ ├── qml_cs.ts
│ ├── qml_de.qm
│ ├── qml_de.ts
│ ├── qml_el.qm
│ ├── qml_el.ts
│ ├── qml_eo.qm
│ ├── qml_eo.ts
│ ├── qml_es.qm
│ ├── qml_es.ts
│ ├── qml_et.qm
│ ├── qml_et.ts
│ ├── qml_fr.qm
│ ├── qml_fr.ts
│ ├── qml_he.qm
│ ├── qml_he.ts
│ ├── qml_hi.qm
│ ├── qml_hi.ts
│ ├── qml_hu.qm
│ ├── qml_hu.ts
│ ├── qml_it.qm
│ ├── qml_it.ts
│ ├── qml_ja.qm
│ ├── qml_ja.ts
│ ├── qml_ko.qm
│ ├── qml_ko.ts
│ ├── qml_lt.qm
│ ├── qml_lt.ts
│ ├── qml_nb_NO.qm
│ ├── qml_nb_NO.ts
│ ├── qml_nl.qm
│ ├── qml_nl.ts
│ ├── qml_pl.qm
│ ├── qml_pl.ts
│ ├── qml_pt.qm
│ ├── qml_pt.ts
│ ├── qml_pt_BR.qm
│ ├── qml_pt_BR.ts
│ ├── qml_ru.qm
│ ├── qml_ru.ts
│ ├── qml_sv.qm
│ ├── qml_sv.ts
│ ├── qml_ta.qm
│ ├── qml_ta.ts
│ ├── qml_th.qm
│ ├── qml_th.ts
│ ├── qml_tr.qm
│ ├── qml_tr.ts
│ ├── qml_uk.qm
│ ├── qml_uk.ts
│ ├── qml_vi.qm
│ ├── qml_vi.ts
│ ├── qml_zh_CN.qm
│ ├── qml_zh_CN.ts
│ ├── qml_zh_TW.qm
│ └── qml_zh_TW.ts
├── main.cpp
├── masterhook.c
├── masterhook_internal.c
├── moonlight.icns
├── moonlight.ico
├── moonlight_wix.png
├── path.cpp
├── path.h
├── qml.qrc
├── qt_qt5.conf
├── res
│ ├── arrow_left.svg
│ ├── baseline-check_circle_outline-24px.svg
│ ├── baseline-error_outline-24px.svg
│ ├── baseline-help_outline-24px.svg
│ ├── baseline-lock-24px.svg
│ ├── baseline-warning-24px.svg
│ ├── desktop_windows-48px.svg
│ ├── discord.svg
│ ├── ic_add_to_queue_white_48px.svg
│ ├── ic_videogame_asset_white_48px.svg
│ ├── moonlight.svg
│ ├── no_app_image.png
│ ├── play_arrow_FILL1_wght700_GRAD200_opsz48.svg
│ ├── question_mark.svg
│ ├── settings.svg
│ ├── stop_FILL1_wght700_GRAD200_opsz48.svg
│ ├── update.svg
│ └── warning_FILL1_wght300_GRAD200_opsz24.svg
├── resources.qrc
├── settings
│ ├── compatfetcher.cpp
│ ├── compatfetcher.h
│ ├── mappingfetcher.cpp
│ ├── mappingfetcher.h
│ ├── mappingmanager.cpp
│ ├── mappingmanager.h
│ ├── streamingpreferences.cpp
│ └── streamingpreferences.h
├── shaders
│ ├── build_hlsl.bat
│ ├── d3d11_ayuv_pixel.fxc
│ ├── d3d11_ayuv_pixel.hlsl
│ ├── d3d11_bt2020lim_pixel.fxc
│ ├── d3d11_bt2020lim_pixel.hlsl
│ ├── d3d11_bt601lim_pixel.fxc
│ ├── d3d11_bt601lim_pixel.hlsl
│ ├── d3d11_genyuv_pixel.fxc
│ ├── d3d11_genyuv_pixel.hlsl
│ ├── d3d11_overlay_pixel.fxc
│ ├── d3d11_overlay_pixel.hlsl
│ ├── d3d11_vertex.fxc
│ ├── d3d11_vertex.hlsl
│ ├── d3d11_video_pixel_end.hlsli
│ ├── d3d11_video_pixel_start.hlsli
│ ├── d3d11_y410_pixel.fxc
│ ├── d3d11_y410_pixel.hlsl
│ ├── d3d11_yuv444_pixel_end.hlsli
│ ├── d3d11_yuv444_pixel_start.hlsli
│ ├── egl_nv12.frag
│ ├── egl_nv12.vert
│ ├── egl_opaque.frag
│ ├── egl_opaque.vert
│ ├── egl_overlay.frag
│ ├── egl_overlay.vert
│ └── vt_renderer.metal
├── streaming
│ ├── audio
│ │ ├── audio.cpp
│ │ └── renderers
│ │ │ ├── renderer.h
│ │ │ ├── sdl.h
│ │ │ ├── sdlaud.cpp
│ │ │ ├── slaud.cpp
│ │ │ ├── slaud.h
│ │ │ ├── soundioaudiorenderer.cpp
│ │ │ └── soundioaudiorenderer.h
│ ├── input
│ │ ├── abstouch.cpp
│ │ ├── gamepad.cpp
│ │ ├── input.cpp
│ │ ├── input.h
│ │ ├── keyboard.cpp
│ │ ├── mouse.cpp
│ │ └── reltouch.cpp
│ ├── session.cpp
│ ├── session.h
│ ├── streamutils.cpp
│ ├── streamutils.h
│ └── video
│ │ ├── decoder.h
│ │ ├── ffmpeg-renderers
│ │ ├── cuda.cpp
│ │ ├── cuda.h
│ │ ├── d3d11va.cpp
│ │ ├── d3d11va.h
│ │ ├── drm.cpp
│ │ ├── drm.h
│ │ ├── dxutil.h
│ │ ├── dxva2.cpp
│ │ ├── dxva2.h
│ │ ├── egl_extensions.cpp
│ │ ├── eglimagefactory.cpp
│ │ ├── eglimagefactory.h
│ │ ├── eglvid.cpp
│ │ ├── eglvid.h
│ │ ├── genhwaccel.cpp
│ │ ├── genhwaccel.h
│ │ ├── mmal.cpp
│ │ ├── mmal.h
│ │ ├── pacer
│ │ │ ├── dxvsyncsource.cpp
│ │ │ ├── dxvsyncsource.h
│ │ │ ├── pacer.cpp
│ │ │ ├── pacer.h
│ │ │ ├── waylandvsyncsource.cpp
│ │ │ └── waylandvsyncsource.h
│ │ ├── plvk.cpp
│ │ ├── plvk.h
│ │ ├── plvk_c.c
│ │ ├── renderer.h
│ │ ├── sdlvid.cpp
│ │ ├── sdlvid.h
│ │ ├── swframemapper.cpp
│ │ ├── swframemapper.h
│ │ ├── vaapi.cpp
│ │ ├── vaapi.h
│ │ ├── vdpau.cpp
│ │ ├── vdpau.h
│ │ ├── vt.h
│ │ ├── vt_avsamplelayer.mm
│ │ ├── vt_base.mm
│ │ └── vt_metal.mm
│ │ ├── ffmpeg.cpp
│ │ ├── ffmpeg.h
│ │ ├── ffmpeg_videosamples.cpp
│ │ ├── overlaymanager.cpp
│ │ ├── overlaymanager.h
│ │ ├── slvid.cpp
│ │ └── slvid.h
├── utils.h
├── version.txt
└── wm.cpp
├── appveyor.yml
├── config.tests
├── EGL
│ ├── EGL.pro
│ └── main.cpp
└── SL
│ ├── SL.pro
│ └── main.cpp
├── globaldefs.pri
├── h264bitstream
└── h264bitstream.pro
├── moonlight-common-c
└── moonlight-common-c.pro
├── moonlight-qt.pro
├── qmdnsengine
├── qmdnsengine.pro
└── qmdnsengine_export.h
├── scripts
├── appveyor
│ ├── qmake.bat
│ ├── qtpaths.bat
│ └── target_qt.conf
├── build-appimage.sh
├── build-arch.bat
├── build-steamlink-app.sh
├── clean-libs.sh
├── create-qt-pdb-zip.bat
├── generate-bundle.bat
├── generate-dmg.sh
├── generate-ico.sh
├── generate-src.sh
├── git-archive-all.sh
├── jom.exe
├── svg2icns
├── update-msvcredist.ps1
└── vswhere.exe
├── soundio
├── config.h
└── soundio.pro
└── wix
├── Moonlight.sln
├── Moonlight
├── Moonlight.wixproj
└── Product.wxs
└── MoonlightSetup
├── Bundle.wxs
├── MoonlightSetup.wixproj
├── RtfTheme.xml
└── license.rtf
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Follow the troubleshooting guide before reporting a bug
4 |
5 | ---
6 | **READ ME FIRST!**
7 | If you're here because something basic is not working (like gamepad input, video, or similar), it's probably something specific to your setup, so make sure you've gone through the Troubleshooting Guide first: https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting
8 |
9 | If you still have trouble with basic functionality after following the guide, join our Discord server where there are many other volunteers who can help (or direct you back here if it looks like a Moonlight bug after all). https://moonlight-stream.org/discord
10 |
11 | **Describe the bug**
12 | A clear and concise description of what the bug is.
13 |
14 | **Steps to reproduce**
15 | Any special steps that are required for the bug to appear.
16 |
17 | **Screenshots**
18 | If applicable, add screenshots to help explain your problem. If the issue is related to video glitching or poor quality, please include screenshots.
19 |
20 | **Affected games**
21 | List the games you've tried that exhibit the issue. To see if the issue is game-specific, try streaming Steam Big Picture with Moonlight and see if the issue persists there.
22 |
23 | **Other Moonlight clients**
24 | - Does the issue occur when using Moonlight on iOS or Android?
25 |
26 | **Moonlight settings (please complete the following information)**
27 | - Have any settings been adjusted from defaults?
28 | - If so, which settings have been changed?
29 | - Does the problem still occur after reverting settings back to default?
30 |
31 | **Gamepad-related issues (please complete if problem is gamepad-related)**
32 | - Do you have any gamepads connected to your host PC directly?
33 | - Does the problem still remain if you stream the desktop and use https://html5gamepad.com to test your gamepad?
34 | - Instructions for streaming the desktop can be found here: https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide
35 |
36 | **Client PC details (please complete the following information)**
37 | - OS: [e.g. Windows 10 1809]
38 | - Moonlight Version: [e.g. v0.9.0]
39 | - GPU: [e.g. Intel HD Graphics 520]
40 | - Linux package type (if applicable): [e.g. Flatpak]
41 |
42 | **Server PC details (please complete the following information)**
43 | - OS: [e.g. Windows 10 1809]
44 | - Sunshine or GeForce Experience version: [e.g. Sunshine v0.21.0]
45 | - GPU: [e.g. AMD Radeon RX 7900 XT]
46 | - GPU driver: [e.g. 24.1.1]
47 |
48 | **Moonlight Logs (please attach)**
49 | - On Windows, `Moonlight-###.log` files can be found in `%TEMP%`. Simply type that into the File Explorer path field to navigate there.
50 | - On macOS, `Moonlight-###.log` files can be found in `/tmp`. In Finder, press Cmd+Shift+G, then type `/tmp` to navigate there.
51 | - On Linux with the Flatpak, logs print to the terminal when running with the command: `flatpak run com.moonlight_stream.Moonlight`
52 | - On Linux with the Snap, logs print to the terminal when running with the command: `moonlight`
53 |
54 | **Additional context**
55 | Anything else you think may be relevant to the issue
56 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/auto-comment.yml:
--------------------------------------------------------------------------------
1 | issuesOpened: >
2 | If this is a question about Moonlight or you need help troubleshooting a streaming problem, please use the help channels on our [Discord server](https://moonlight-stream.org/discord) instead of GitHub issues. There are many more people available on Discord to help you and answer your questions.
3 | This issue tracker should only be used for specific bugs or feature requests.
4 | Thank you, and happy streaming!
5 |
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # ProBot No Response (https://probot.github.io/apps/no-response/)
2 |
3 | daysUntilClose: 7
4 | responseRequiredLabel: 'need more info'
5 | closeComment: >
6 | This issue has been automatically closed because there was no response to a
7 | request for more information from the issue opener. Please leave a comment or
8 | open a new issue if you have additional information related to this issue.
9 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # ProBot Stale (https://probot.github.io/apps/stale/)
2 |
3 | daysUntilStale: 90
4 | daysUntilClose: 7
5 | exemptLabels:
6 | - accepted
7 | - bug
8 | - enhancement
9 | - meta
10 | staleLabel: stale
11 | markComment: >
12 | This issue has been automatically marked as stale because it has not had
13 | recent activity. It will be closed if no further activity occurs.
14 | closeComment: false
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *pro.user
2 |
3 |
4 | **/.vs/
5 | build/
6 | config.tests/*/.qmake.stash
7 | config.tests/*/Makefile
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "moonlight-common-c/moonlight-common-c"]
2 | path = moonlight-common-c/moonlight-common-c
3 | url = https://github.com/moonlight-stream/moonlight-common-c.git
4 | [submodule "qmdnsengine/qmdnsengine"]
5 | path = qmdnsengine/qmdnsengine
6 | url = https://github.com/cgutman/qmdnsengine.git
7 | [submodule "app/SDL_GameControllerDB"]
8 | path = app/SDL_GameControllerDB
9 | url = https://github.com/gabomdq/SDL_GameControllerDB.git
10 | [submodule "soundio/libsoundio"]
11 | path = soundio/libsoundio
12 | url = https://github.com/cgutman/libsoundio.git
13 | [submodule "h264bitstream/h264bitstream"]
14 | path = h264bitstream/h264bitstream
15 | url = https://github.com/aizvorski/h264bitstream.git
16 | [submodule "libs"]
17 | path = libs
18 | url = https://github.com/cgutman/moonlight-qt-prebuilts.git
19 | shallow = true
20 |
--------------------------------------------------------------------------------
/AntiHooking/AntiHooking.pro:
--------------------------------------------------------------------------------
1 | QT -= core gui
2 |
3 | TARGET = AntiHooking
4 | TEMPLATE = lib
5 |
6 | include(../globaldefs.pri)
7 |
8 | INCLUDEPATH += $$PWD/../libs/windows/include
9 | contains(QT_ARCH, i386) {
10 | LIBS += -L$$PWD/../libs/windows/lib/x86
11 | }
12 | contains(QT_ARCH, x86_64) {
13 | LIBS += -L$$PWD/../libs/windows/lib/x64
14 | }
15 | contains(QT_ARCH, arm64) {
16 | LIBS += -L$$PWD/../libs/windows/lib/arm64
17 | }
18 |
19 | LIBS += -ldetours
20 | DEFINES += ANTIHOOKING_LIBRARY
21 | SOURCES += antihookingprotection.cpp
22 | HEADERS += antihookingprotection.h
23 |
--------------------------------------------------------------------------------
/AntiHooking/antihookingprotection.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef ANTIHOOKING_LIBRARY
4 | #define AH_EXPORT extern "C" __declspec(dllexport)
5 | #else
6 | #define AH_EXPORT extern "C" __declspec(dllimport)
7 | #endif
8 |
9 | AH_EXPORT void AntiHookingDummyImport();
10 |
11 |
--------------------------------------------------------------------------------
/app/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleExecutable
6 | Moonlight
7 | CFBundleGetInfoString
8 | Stream games and other applications from another PC
9 | CFBundleIconFile
10 | moonlight
11 | CFBundleIdentifier
12 | com.moonlight-stream.Moonlight
13 | NSHighResolutionCapable
14 |
15 | CFBundlePackageType
16 | APPL
17 | CFBundleSignature
18 | ????
19 | LSMinimumSystemVersion
20 | 11.0.0
21 | NSPrincipalClass
22 | NSApplication
23 | NSSupportsAutomaticGraphicsSwitching
24 |
25 | GCSupportedGameControllers
26 |
27 |
28 | ProfileName
29 | ExtendedGamepad
30 |
31 |
32 | GCSupportsControllerUserInteraction
33 |
34 | NSAppTransportSecurity
35 |
36 | NSAllowsArbitraryLoads
37 |
38 |
39 | NSLocalNetworkUsageDescription
40 | Moonlight uses the local network to connect to your gaming PC for streaming.
41 | CFBundleVersion
42 | VERSION
43 | CFBundleShortVersionString
44 | VERSION
45 | CFBundleDisplayName
46 | Moonlight
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/ModeSeven.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/ModeSeven.ttf
--------------------------------------------------------------------------------
/app/Moonlight.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Moonlight Game Streaming
11 |
12 |
13 | true/pm
14 | permonitor
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/SDL_compat.h:
--------------------------------------------------------------------------------
1 | //
2 | // Compatibility header for older version of SDL.
3 | // Include this instead of SDL.h directly.
4 | //
5 |
6 | #pragma once
7 |
8 | #include
9 |
10 | // SDL_FRect wasn't added until 2.0.10
11 | #if !SDL_VERSION_ATLEAST(2, 0, 10)
12 | typedef struct SDL_FRect
13 | {
14 | float x;
15 | float y;
16 | float w;
17 | float h;
18 | } SDL_FRect;
19 | #endif
20 |
21 | #ifndef SDL_HINT_VIDEO_X11_FORCE_EGL
22 | #define SDL_HINT_VIDEO_X11_FORCE_EGL "SDL_VIDEO_X11_FORCE_EGL"
23 | #endif
24 |
25 | #ifndef SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER
26 | #define SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER "SDL_KMSDRM_REQUIRE_DRM_MASTER"
27 | #endif
28 |
29 | #ifndef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED
30 | #define SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"
31 | #endif
32 |
33 | #ifndef SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE
34 | #define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"
35 | #endif
36 |
37 | #ifndef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE
38 | #define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"
39 | #endif
40 |
41 | #ifndef SDL_HINT_WINDOWS_USE_D3D9EX
42 | #define SDL_HINT_WINDOWS_USE_D3D9EX "SDL_WINDOWS_USE_D3D9EX"
43 | #endif
44 |
45 | #ifndef SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS
46 | #define SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS "SDL_GAMECONTROLLER_USE_BUTTON_LABELS"
47 | #endif
48 |
49 | #ifndef SDL_HINT_MOUSE_RELATIVE_SCALING
50 | #define SDL_HINT_MOUSE_RELATIVE_SCALING "SDL_MOUSE_RELATIVE_SCALING"
51 | #endif
52 |
53 | #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
54 | #define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
55 | #endif
56 |
57 | #ifndef SDL_HINT_APP_NAME
58 | #define SDL_HINT_APP_NAME "SDL_APP_NAME"
59 | #endif
60 |
61 | #ifndef SDL_HINT_MOUSE_AUTO_CAPTURE
62 | #define SDL_HINT_MOUSE_AUTO_CAPTURE "SDL_MOUSE_AUTO_CAPTURE"
63 | #endif
64 |
65 | #ifndef SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP
66 | #define SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP "SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP"
67 | #endif
68 |
--------------------------------------------------------------------------------
/app/backend/autoupdatechecker.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class AutoUpdateChecker : public QObject
7 | {
8 | Q_OBJECT
9 | public:
10 | explicit AutoUpdateChecker(QObject *parent = nullptr);
11 |
12 | Q_INVOKABLE void start();
13 |
14 | signals:
15 | void onUpdateAvailable(QString newVersion, QString url);
16 |
17 | private slots:
18 | void handleUpdateCheckRequestFinished(QNetworkReply* reply);
19 |
20 | private:
21 | void parseStringToVersionQuad(QString& string, QVector& version);
22 |
23 | int compareVersion(QVector& version1, QVector& version2);
24 |
25 | QString getPlatform();
26 |
27 | QVector m_CurrentVersionQuad;
28 | QNetworkAccessManager* m_Nam;
29 | };
30 |
--------------------------------------------------------------------------------
/app/backend/boxartmanager.cpp:
--------------------------------------------------------------------------------
1 | #include "boxartmanager.h"
2 | #include "../path.h"
3 |
4 | #include
5 | #include
6 |
7 | BoxArtManager::BoxArtManager(QObject *parent) :
8 | QObject(parent),
9 | m_BoxArtDir(Path::getBoxArtCacheDir()),
10 | m_ThreadPool(this)
11 | {
12 | // 4 is a good balance between fast loading for large
13 | // app grids and not crushing GFE with tons of requests
14 | // and causing UI jank from constantly stalling to decode
15 | // new images.
16 | m_ThreadPool.setMaxThreadCount(4);
17 | if (!m_BoxArtDir.exists()) {
18 | m_BoxArtDir.mkpath(".");
19 | }
20 | }
21 |
22 | QString
23 | BoxArtManager::getFilePathForBoxArt(NvComputer* computer, int appId)
24 | {
25 | QDir dir = m_BoxArtDir;
26 |
27 | // Create the cache directory if it did not already exist
28 | if (!dir.exists(computer->uuid)) {
29 | dir.mkdir(computer->uuid);
30 | }
31 |
32 | // Change to this computer's box art cache folder
33 | dir.cd(computer->uuid);
34 |
35 | // Try to open the cached file
36 | return dir.filePath(QString::number(appId) + ".png");
37 | }
38 |
39 | class NetworkBoxArtLoadTask : public QObject, public QRunnable
40 | {
41 | Q_OBJECT
42 |
43 | public:
44 | NetworkBoxArtLoadTask(BoxArtManager* boxArtManager, NvComputer* computer, NvApp& app)
45 | : m_Bam(boxArtManager),
46 | m_Computer(computer),
47 | m_App(app)
48 | {
49 | connect(this, &NetworkBoxArtLoadTask::boxArtFetchCompleted,
50 | boxArtManager, &BoxArtManager::handleBoxArtLoadComplete);
51 | }
52 |
53 | signals:
54 | void boxArtFetchCompleted(NvComputer* computer, NvApp app, QUrl image);
55 |
56 | private:
57 | void run()
58 | {
59 | QUrl image = m_Bam->loadBoxArtFromNetwork(m_Computer, m_App.id);
60 | if (image.isEmpty()) {
61 | // Give it another shot if it fails once
62 | image = m_Bam->loadBoxArtFromNetwork(m_Computer, m_App.id);
63 | }
64 | emit boxArtFetchCompleted(m_Computer, m_App, image);
65 | }
66 |
67 | BoxArtManager* m_Bam;
68 | NvComputer* m_Computer;
69 | NvApp m_App;
70 | };
71 |
72 | QUrl BoxArtManager::loadBoxArt(NvComputer* computer, NvApp& app)
73 | {
74 | // Try to open the cached file if it exists and contains data
75 | QFile cacheFile(getFilePathForBoxArt(computer, app.id));
76 | if (cacheFile.exists() && cacheFile.size() > 0) {
77 | return QUrl::fromLocalFile(cacheFile.fileName());
78 | }
79 |
80 | // If we get here, we need to fetch asynchronously.
81 | // Kick off a worker on our thread pool to do just that.
82 | NetworkBoxArtLoadTask* netLoadTask = new NetworkBoxArtLoadTask(this, computer, app);
83 | m_ThreadPool.start(netLoadTask);
84 |
85 | // Return the placeholder then we can notify the caller
86 | // later when the real image is ready.
87 | return QUrl("qrc:/res/no_app_image.png");
88 | }
89 |
90 | void BoxArtManager::deleteBoxArt(NvComputer* computer)
91 | {
92 | QDir dir(Path::getBoxArtCacheDir());
93 |
94 | // Delete everything in this computer's box art directory
95 | if (dir.cd(computer->uuid)) {
96 | dir.removeRecursively();
97 | }
98 | }
99 |
100 | void BoxArtManager::handleBoxArtLoadComplete(NvComputer* computer, NvApp app, QUrl image)
101 | {
102 | if (!image.isEmpty()) {
103 | emit boxArtLoadComplete(computer, app, image);
104 | }
105 | }
106 |
107 | QUrl BoxArtManager::loadBoxArtFromNetwork(NvComputer* computer, int appId)
108 | {
109 | NvHTTP http(computer);
110 |
111 | QString cachePath = getFilePathForBoxArt(computer, appId);
112 | QImage image;
113 | try {
114 | image = http.getBoxArt(appId);
115 | } catch (...) {}
116 |
117 | // Cache the box art on disk if it loaded
118 | if (!image.isNull()) {
119 | if (image.save(cachePath)) {
120 | return QUrl::fromLocalFile(cachePath);
121 | }
122 | else {
123 | // A failed save() may leave a zero byte file. Make sure that's removed.
124 | QFile(cachePath).remove();
125 | }
126 | }
127 |
128 | return QUrl();
129 | }
130 |
131 | #include "boxartmanager.moc"
132 |
--------------------------------------------------------------------------------
/app/backend/boxartmanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "computermanager.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class BoxArtManager : public QObject
10 | {
11 | Q_OBJECT
12 |
13 | friend class NetworkBoxArtLoadTask;
14 |
15 | public:
16 | explicit BoxArtManager(QObject *parent = nullptr);
17 |
18 | QUrl
19 | loadBoxArt(NvComputer* computer, NvApp& app);
20 |
21 | static
22 | void
23 | deleteBoxArt(NvComputer* computer);
24 |
25 | signals:
26 | void
27 | boxArtLoadComplete(NvComputer* computer, NvApp app, QUrl image);
28 |
29 | public slots:
30 |
31 | private slots:
32 | void
33 | handleBoxArtLoadComplete(NvComputer* computer, NvApp app, QUrl image);
34 |
35 | private:
36 | QUrl
37 | loadBoxArtFromNetwork(NvComputer* computer, int appId);
38 |
39 | QString
40 | getFilePathForBoxArt(NvComputer* computer, int appId);
41 |
42 | QDir m_BoxArtDir;
43 | QThreadPool m_ThreadPool;
44 | };
45 |
--------------------------------------------------------------------------------
/app/backend/computerseeker.cpp:
--------------------------------------------------------------------------------
1 | #include "computerseeker.h"
2 | #include "computermanager.h"
3 | #include
4 |
5 | ComputerSeeker::ComputerSeeker(ComputerManager *manager, QString computerName, QObject *parent)
6 | : QObject(parent), m_ComputerManager(manager), m_ComputerName(computerName),
7 | m_TimeoutTimer(new QTimer(this))
8 | {
9 | // If we know this computer, send a WOL packet to wake it up in case it is asleep.
10 | for (NvComputer * computer: m_ComputerManager->getComputers()) {
11 | if (this->matchComputer(computer)) {
12 | computer->wake();
13 | }
14 | }
15 |
16 | m_TimeoutTimer->setSingleShot(true);
17 | connect(m_TimeoutTimer, &QTimer::timeout,
18 | this, &ComputerSeeker::onTimeout);
19 | connect(m_ComputerManager, &ComputerManager::computerStateChanged,
20 | this, &ComputerSeeker::onComputerUpdated);
21 | }
22 |
23 | void ComputerSeeker::start(int timeout)
24 | {
25 | m_TimeoutTimer->start(timeout);
26 | // Seek desired computer by both connecting to it directly (this may fail
27 | // if m_ComputerName is UUID, or the name that doesn't resolve to an IP
28 | // address) and by polling it using mDNS, hopefully one of these methods
29 | // would find the host
30 | m_ComputerManager->addNewHostManually(m_ComputerName);
31 | m_ComputerManager->startPolling();
32 | }
33 |
34 | void ComputerSeeker::onComputerUpdated(NvComputer *computer)
35 | {
36 | if (!m_TimeoutTimer->isActive()) {
37 | return;
38 | }
39 | if (matchComputer(computer) && isOnline(computer)) {
40 | m_ComputerManager->stopPollingAsync();
41 | m_TimeoutTimer->stop();
42 | emit computerFound(computer);
43 | }
44 | }
45 |
46 | bool ComputerSeeker::matchComputer(NvComputer *computer) const
47 | {
48 | QString value = m_ComputerName.toLower();
49 |
50 | if (computer->name.toLower() == value || computer->uuid.toLower() == value) {
51 | return true;
52 | }
53 |
54 | for (const NvAddress& addr : computer->uniqueAddresses()) {
55 | if (addr.address().toLower() == value || addr.toString().toLower() == value) {
56 | return true;
57 | }
58 | }
59 |
60 | return false;
61 | }
62 |
63 | bool ComputerSeeker::isOnline(NvComputer *computer) const
64 | {
65 | return computer->state == NvComputer::CS_ONLINE;
66 | }
67 |
68 | void ComputerSeeker::onTimeout()
69 | {
70 | m_TimeoutTimer->stop();
71 | m_ComputerManager->stopPollingAsync();
72 | emit errorTimeout();
73 | }
74 |
--------------------------------------------------------------------------------
/app/backend/computerseeker.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class ComputerManager;
6 | class NvComputer;
7 | class QTimer;
8 |
9 | class ComputerSeeker : public QObject
10 | {
11 | Q_OBJECT
12 | public:
13 | explicit ComputerSeeker(ComputerManager *manager, QString computerName, QObject *parent = nullptr);
14 |
15 | void start(int timeout);
16 |
17 | signals:
18 | void computerFound(NvComputer *computer);
19 | void errorTimeout();
20 |
21 | private slots:
22 | void onComputerUpdated(NvComputer *computer);
23 | void onTimeout();
24 |
25 | private:
26 | bool matchComputer(NvComputer *computer) const;
27 | bool isOnline(NvComputer *computer) const;
28 |
29 | private:
30 | ComputerManager *m_ComputerManager;
31 | QString m_ComputerName;
32 | QTimer *m_TimeoutTimer;
33 | };
34 |
--------------------------------------------------------------------------------
/app/backend/identitymanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | class IdentityManager
9 | {
10 | public:
11 | QString
12 | getUniqueId();
13 |
14 | QByteArray
15 | getCertificate();
16 |
17 | QByteArray
18 | getPrivateKey();
19 |
20 | QSslConfiguration
21 | getSslConfig();
22 |
23 | static
24 | IdentityManager*
25 | get();
26 |
27 | private:
28 | IdentityManager();
29 |
30 | QSslCertificate
31 | getSslCertificate();
32 |
33 | QSslKey
34 | getSslKey();
35 |
36 | void
37 | createCredentials(QSettings& settings);
38 |
39 | // Initialized in constructor
40 | QByteArray m_CachedPrivateKey;
41 | QByteArray m_CachedPemCert;
42 |
43 | // Lazy initialized
44 | QString m_CachedUniqueId;
45 | QSslCertificate m_CachedSslCert;
46 | QSslKey m_CachedSslKey;
47 |
48 | static IdentityManager* s_Im;
49 | };
50 |
--------------------------------------------------------------------------------
/app/backend/nvaddress.cpp:
--------------------------------------------------------------------------------
1 | #include "nvaddress.h"
2 |
3 | #include
4 |
5 | NvAddress::NvAddress()
6 | {
7 | setAddress(nullptr);
8 | setPort(0);
9 | }
10 |
11 | NvAddress::NvAddress(QString addr, uint16_t port)
12 | {
13 | setAddress(addr);
14 | setPort(port);
15 | }
16 |
17 | NvAddress::NvAddress(QHostAddress addr, uint16_t port)
18 | {
19 | setAddress(addr);
20 | setPort(port);
21 | }
22 |
23 | uint16_t NvAddress::port() const
24 | {
25 | return m_Port;
26 | }
27 |
28 | QString NvAddress::address() const
29 | {
30 | return m_Address;
31 | }
32 |
33 | void NvAddress::setPort(uint16_t port)
34 | {
35 | m_Port = port;
36 | }
37 |
38 | void NvAddress::setAddress(QString addr)
39 | {
40 | m_Address = addr;
41 | }
42 |
43 | void NvAddress::setAddress(QHostAddress addr)
44 | {
45 | m_Address = addr.toString();
46 | }
47 |
48 | bool NvAddress::isNull() const
49 | {
50 | return m_Address.isEmpty();
51 | }
52 |
53 | QString NvAddress::toString() const
54 | {
55 | if (m_Address.isEmpty()) {
56 | return "";
57 | }
58 |
59 | if (QHostAddress(m_Address).protocol() == QAbstractSocket::IPv6Protocol) {
60 | return QString("[%1]:%2").arg(m_Address).arg(m_Port);
61 | }
62 | else {
63 | return QString("%1:%2").arg(m_Address).arg(m_Port);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/backend/nvaddress.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #define DEFAULT_HTTP_PORT 47989
6 | #define DEFAULT_HTTPS_PORT 47984
7 |
8 | class NvAddress
9 | {
10 | public:
11 | NvAddress();
12 | explicit NvAddress(QString addr, uint16_t port);
13 | explicit NvAddress(QHostAddress addr, uint16_t port);
14 |
15 | uint16_t port() const;
16 | void setPort(uint16_t port);
17 |
18 | QString address() const;
19 | void setAddress(QString addr);
20 | void setAddress(QHostAddress addr);
21 |
22 | bool isNull() const;
23 | QString toString() const;
24 |
25 | bool operator==(const NvAddress& other) const
26 | {
27 | return m_Address == other.m_Address &&
28 | m_Port == other.m_Port;
29 | }
30 |
31 | bool operator!=(const NvAddress& other) const
32 | {
33 | return !operator==(other);
34 | }
35 |
36 | private:
37 | QString m_Address;
38 | uint16_t m_Port;
39 | };
40 |
--------------------------------------------------------------------------------
/app/backend/nvapp.cpp:
--------------------------------------------------------------------------------
1 | #include "nvapp.h"
2 |
3 | #define SER_APPNAME "name"
4 | #define SER_APPID "id"
5 | #define SER_APPHDR "hdr"
6 | #define SER_APPCOLLECTOR "appcollector"
7 | #define SER_HIDDEN "hidden"
8 | #define SER_DIRECTLAUNCH "directlaunch"
9 |
10 | NvApp::NvApp(QSettings& settings)
11 | {
12 | name = settings.value(SER_APPNAME).toString();
13 | id = settings.value(SER_APPID).toInt();
14 | hdrSupported = settings.value(SER_APPHDR).toBool();
15 | isAppCollectorGame = settings.value(SER_APPCOLLECTOR).toBool();
16 | hidden = settings.value(SER_HIDDEN).toBool();
17 | directLaunch = settings.value(SER_DIRECTLAUNCH).toBool();
18 | }
19 |
20 | void NvApp::serialize(QSettings& settings) const
21 | {
22 | settings.setValue(SER_APPNAME, name);
23 | settings.setValue(SER_APPID, id);
24 | settings.setValue(SER_APPHDR, hdrSupported);
25 | settings.setValue(SER_APPCOLLECTOR, isAppCollectorGame);
26 | settings.setValue(SER_HIDDEN, hidden);
27 | settings.setValue(SER_DIRECTLAUNCH, directLaunch);
28 | }
29 |
--------------------------------------------------------------------------------
/app/backend/nvapp.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class NvApp
6 | {
7 | public:
8 | NvApp() {}
9 | explicit NvApp(QSettings& settings);
10 |
11 | bool operator==(const NvApp& other) const
12 | {
13 | return id == other.id &&
14 | name == other.name &&
15 | hdrSupported == other.hdrSupported &&
16 | isAppCollectorGame == other.isAppCollectorGame &&
17 | hidden == other.hidden &&
18 | directLaunch == other.directLaunch;
19 | }
20 |
21 | bool operator!=(const NvApp& other) const
22 | {
23 | return !operator==(other);
24 | }
25 |
26 | bool isInitialized()
27 | {
28 | return id != 0 && !name.isEmpty();
29 | }
30 |
31 | void
32 | serialize(QSettings& settings) const;
33 |
34 | int id = 0;
35 | QString name;
36 | bool hdrSupported = false;
37 | bool isAppCollectorGame = false;
38 | bool hidden = false;
39 | bool directLaunch = false;
40 | };
41 |
42 | Q_DECLARE_METATYPE(NvApp)
43 |
--------------------------------------------------------------------------------
/app/backend/nvcomputer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "nvhttp.h"
4 | #include "nvaddress.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class CopySafeReadWriteLock : public QReadWriteLock
12 | {
13 | public:
14 | CopySafeReadWriteLock() = default;
15 |
16 | // Don't actually copy the QReadWriteLock
17 | CopySafeReadWriteLock(const CopySafeReadWriteLock&) : QReadWriteLock() {}
18 | CopySafeReadWriteLock& operator=(const CopySafeReadWriteLock &) { return *this; }
19 | };
20 |
21 | class NvComputer
22 | {
23 | friend class PcMonitorThread;
24 | friend class ComputerManager;
25 | friend class PendingQuitTask;
26 |
27 | private:
28 | void sortAppList();
29 |
30 | bool updateAppList(QVector newAppList);
31 |
32 | bool pendingQuit;
33 |
34 | public:
35 | NvComputer() = default;
36 |
37 | // Caller is responsible for synchronizing read access to the other host
38 | NvComputer(const NvComputer&) = default;
39 |
40 | // Caller is responsible for synchronizing read access to the other host
41 | NvComputer& operator=(const NvComputer &) = default;
42 |
43 | explicit NvComputer(NvHTTP& http, QString serverInfo);
44 |
45 | explicit NvComputer(QSettings& settings);
46 |
47 | void
48 | setRemoteAddress(QHostAddress);
49 |
50 | bool
51 | update(const NvComputer& that);
52 |
53 | bool
54 | wake() const;
55 |
56 | enum ReachabilityType
57 | {
58 | RI_UNKNOWN,
59 | RI_LAN,
60 | RI_VPN,
61 | };
62 |
63 | ReachabilityType
64 | getActiveAddressReachability() const;
65 |
66 | QVector
67 | uniqueAddresses() const;
68 |
69 | void
70 | serialize(QSettings& settings, bool serializeApps) const;
71 |
72 | // Caller is responsible for synchronizing read access to both hosts
73 | bool
74 | isEqualSerialized(const NvComputer& that) const;
75 |
76 | enum PairState
77 | {
78 | PS_UNKNOWN,
79 | PS_PAIRED,
80 | PS_NOT_PAIRED
81 | };
82 |
83 | enum ComputerState
84 | {
85 | CS_UNKNOWN,
86 | CS_ONLINE,
87 | CS_OFFLINE
88 | };
89 |
90 | // Ephemeral traits
91 | ComputerState state;
92 | PairState pairState;
93 | NvAddress activeAddress;
94 | uint16_t activeHttpsPort;
95 | int currentGameId;
96 | QString gfeVersion;
97 | QString appVersion;
98 | QVector displayModes;
99 | int maxLumaPixelsHEVC;
100 | int serverCodecModeSupport;
101 | QString gpuModel;
102 | bool isSupportedServerVersion;
103 |
104 | // Persisted traits
105 | NvAddress localAddress;
106 | NvAddress remoteAddress;
107 | NvAddress ipv6Address;
108 | NvAddress manualAddress;
109 | QByteArray macAddress;
110 | QString name;
111 | bool hasCustomName;
112 | QString uuid;
113 | QSslCertificate serverCert;
114 | QVector appList;
115 | bool isNvidiaServerSoftware;
116 | // Remember to update isEqualSerialized() when adding fields here!
117 |
118 | // Synchronization
119 | mutable CopySafeReadWriteLock lock;
120 |
121 | private:
122 | uint16_t externalPort;
123 | };
124 |
--------------------------------------------------------------------------------
/app/backend/nvpairingmanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "identitymanager.h"
4 | #include "nvhttp.h"
5 |
6 | #include
7 | #include
8 |
9 | class NvPairingManager
10 | {
11 | public:
12 | enum PairState
13 | {
14 | PAIRED,
15 | PIN_WRONG,
16 | FAILED,
17 | ALREADY_IN_PROGRESS
18 | };
19 |
20 | explicit NvPairingManager(NvComputer* computer);
21 |
22 | ~NvPairingManager();
23 |
24 | PairState
25 | pair(QString appVersion, QString pin, QSslCertificate& serverCert);
26 |
27 | private:
28 | QByteArray
29 | generateRandomBytes(int length);
30 |
31 | QByteArray
32 | saltPin(const QByteArray& salt, QString pin);
33 |
34 | QByteArray
35 | encrypt(const QByteArray& plaintext, const QByteArray& key);
36 |
37 | QByteArray
38 | decrypt(const QByteArray& ciphertext, const QByteArray& key);
39 |
40 | QByteArray
41 | getSignatureFromPemCert(const QByteArray& certificate);
42 |
43 | bool
44 | verifySignature(const QByteArray& data, const QByteArray& signature, const QByteArray& serverCertificate);
45 |
46 | QByteArray
47 | signMessage(const QByteArray& message);
48 |
49 | NvHTTP m_Http;
50 | X509* m_Cert;
51 | EVP_PKEY* m_PrivateKey;
52 | };
53 |
--------------------------------------------------------------------------------
/app/backend/richpresencemanager.cpp:
--------------------------------------------------------------------------------
1 | #include "richpresencemanager.h"
2 |
3 | #include
4 |
5 | RichPresenceManager::RichPresenceManager(StreamingPreferences& prefs, QString gameName)
6 | : m_DiscordActive(false)
7 | {
8 | #ifdef HAVE_DISCORD
9 | if (prefs.richPresence) {
10 | DiscordEventHandlers handlers = {};
11 | handlers.ready = discordReady;
12 | handlers.disconnected = discordDisconnected;
13 | handlers.errored = discordErrored;
14 | Discord_Initialize("594668102021677159", &handlers, 0, nullptr);
15 | m_DiscordActive = true;
16 | }
17 |
18 | if (m_DiscordActive) {
19 | QByteArray stateStr = (QString("Streaming ") + gameName).toUtf8();
20 |
21 | DiscordRichPresence discordPresence = {};
22 | discordPresence.state = stateStr.data();
23 | discordPresence.startTimestamp = time(nullptr);
24 | discordPresence.largeImageKey = "icon";
25 | Discord_UpdatePresence(&discordPresence);
26 | }
27 | #else
28 | Q_UNUSED(prefs)
29 | Q_UNUSED(gameName)
30 | #endif
31 | }
32 |
33 | RichPresenceManager::~RichPresenceManager()
34 | {
35 | #ifdef HAVE_DISCORD
36 | if (m_DiscordActive) {
37 | Discord_ClearPresence();
38 | Discord_Shutdown();
39 | }
40 | #endif
41 | }
42 |
43 | void RichPresenceManager::runCallbacks()
44 | {
45 | #ifdef HAVE_DISCORD
46 | if (m_DiscordActive) {
47 | Discord_RunCallbacks();
48 | }
49 | #endif
50 | }
51 |
52 | #ifdef HAVE_DISCORD
53 | void RichPresenceManager::discordReady(const DiscordUser* request)
54 | {
55 | qInfo() << "Discord integration ready for user:" << request->username;
56 | }
57 |
58 | void RichPresenceManager::discordDisconnected(int errorCode, const char *message)
59 | {
60 | qInfo() << "Discord integration disconnected:" << errorCode << message;
61 | }
62 |
63 | void RichPresenceManager::discordErrored(int errorCode, const char *message)
64 | {
65 | qWarning() << "Discord integration error:" << errorCode << message;
66 | }
67 | #endif
68 |
--------------------------------------------------------------------------------
/app/backend/richpresencemanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "settings/streamingpreferences.h"
4 |
5 | #ifdef HAVE_DISCORD
6 | #include
7 | #endif
8 |
9 | class RichPresenceManager
10 | {
11 | public:
12 | RichPresenceManager(StreamingPreferences& prefs, QString gameName);
13 | ~RichPresenceManager();
14 |
15 | void runCallbacks();
16 |
17 | private:
18 | #ifdef HAVE_DISCORD
19 | static void discordReady(const DiscordUser* request);
20 | static void discordDisconnected(int errorCode, const char* message);
21 | static void discordErrored(int errorCode, const char* message);
22 | #endif
23 |
24 | bool m_DiscordActive;
25 | };
26 |
27 |
--------------------------------------------------------------------------------
/app/backend/systemproperties.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class SystemProperties : public QObject
7 | {
8 | Q_OBJECT
9 |
10 | friend class QuerySdlVideoThread;
11 | friend class RefreshDisplaysThread;
12 |
13 | public:
14 | SystemProperties();
15 |
16 | Q_PROPERTY(bool hasHardwareAcceleration MEMBER hasHardwareAcceleration CONSTANT)
17 | Q_PROPERTY(bool rendererAlwaysFullScreen MEMBER rendererAlwaysFullScreen CONSTANT)
18 | Q_PROPERTY(bool isRunningWayland MEMBER isRunningWayland CONSTANT)
19 | Q_PROPERTY(bool isRunningXWayland MEMBER isRunningXWayland CONSTANT)
20 | Q_PROPERTY(bool isWow64 MEMBER isWow64 CONSTANT)
21 | Q_PROPERTY(QString friendlyNativeArchName MEMBER friendlyNativeArchName CONSTANT)
22 | Q_PROPERTY(bool hasDesktopEnvironment MEMBER hasDesktopEnvironment CONSTANT)
23 | Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
24 | Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
25 | Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
26 | Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT)
27 | Q_PROPERTY(QString versionString MEMBER versionString CONSTANT)
28 | Q_PROPERTY(bool supportsHdr MEMBER supportsHdr CONSTANT)
29 | Q_PROPERTY(bool usesMaterial3Theme MEMBER usesMaterial3Theme CONSTANT)
30 |
31 | Q_INVOKABLE void refreshDisplays();
32 | Q_INVOKABLE QRect getNativeResolution(int displayIndex);
33 | Q_INVOKABLE QRect getSafeAreaResolution(int displayIndex);
34 | Q_INVOKABLE int getRefreshRate(int displayIndex);
35 |
36 | signals:
37 | void unmappedGamepadsChanged();
38 |
39 | private:
40 | void querySdlVideoInfo();
41 | void querySdlVideoInfoInternal();
42 | void refreshDisplaysInternal();
43 |
44 | bool hasHardwareAcceleration;
45 | bool rendererAlwaysFullScreen;
46 | bool isRunningWayland;
47 | bool isRunningXWayland;
48 | bool isWow64;
49 | QString friendlyNativeArchName;
50 | bool hasDesktopEnvironment;
51 | bool hasBrowser;
52 | bool hasDiscordIntegration;
53 | QString unmappedGamepads;
54 | QSize maximumResolution;
55 | QList monitorNativeResolutions;
56 | QList monitorSafeAreaResolutions;
57 | QList monitorRefreshRates;
58 | QString versionString;
59 | bool supportsHdr;
60 | bool usesMaterial3Theme;
61 | };
62 |
63 |
--------------------------------------------------------------------------------
/app/cli/commandlineparser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "settings/streamingpreferences.h"
4 |
5 | #include
6 | #include
7 |
8 | class GlobalCommandLineParser
9 | {
10 | public:
11 | enum ParseResult {
12 | NormalStartRequested,
13 | StreamRequested,
14 | QuitRequested,
15 | PairRequested,
16 | ListRequested,
17 | };
18 |
19 | GlobalCommandLineParser();
20 | virtual ~GlobalCommandLineParser();
21 |
22 | ParseResult parse(const QStringList &args);
23 |
24 | };
25 |
26 | class QuitCommandLineParser
27 | {
28 | public:
29 | QuitCommandLineParser();
30 | virtual ~QuitCommandLineParser();
31 |
32 | void parse(const QStringList &args);
33 |
34 | QString getHost() const;
35 |
36 | private:
37 | QString m_Host;
38 | };
39 |
40 | class PairCommandLineParser
41 | {
42 | public:
43 | PairCommandLineParser();
44 | virtual ~PairCommandLineParser();
45 |
46 | void parse(const QStringList &args);
47 |
48 | QString getHost() const;
49 | QString getPredefinedPin() const;
50 |
51 | private:
52 | QString m_Host;
53 | QString m_PredefinedPin;
54 | };
55 |
56 | class StreamCommandLineParser
57 | {
58 | public:
59 | StreamCommandLineParser();
60 | virtual ~StreamCommandLineParser();
61 |
62 | void parse(const QStringList &args, StreamingPreferences *preferences);
63 |
64 | QString getHost() const;
65 | QString getAppName() const;
66 |
67 | private:
68 | QString m_Host;
69 | QString m_AppName;
70 | QMap m_WindowModeMap;
71 | QMap m_AudioConfigMap;
72 | QMap m_VideoCodecMap;
73 | QMap m_VideoDecoderMap;
74 | QMap m_CaptureSysKeysModeMap;
75 | };
76 |
77 | class ListCommandLineParser
78 | {
79 | public:
80 | ListCommandLineParser();
81 | virtual ~ListCommandLineParser();
82 |
83 | void parse(const QStringList &args);
84 |
85 | QString getHost() const;
86 | bool isPrintCSV() const;
87 | bool isVerbose() const;
88 |
89 | private:
90 | QString m_Host;
91 | bool m_PrintCSV;
92 | bool m_Verbose;
93 | };
94 |
--------------------------------------------------------------------------------
/app/cli/listapps.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "commandlineparser.h"
4 |
5 | #include
6 | #include
7 |
8 | class ComputerManager;
9 | class NvComputer;
10 |
11 | namespace CliListApps
12 | {
13 |
14 | class LauncherPrivate;
15 |
16 | class Launcher : public QObject
17 | {
18 | Q_OBJECT
19 | Q_DECLARE_PRIVATE_D(m_DPtr, Launcher)
20 |
21 | public:
22 | explicit Launcher(QString computer, ListCommandLineParser arguments, QObject *parent = nullptr);
23 | ~Launcher();
24 |
25 | Q_INVOKABLE void execute(ComputerManager *manager);
26 | Q_INVOKABLE bool isExecuted() const;
27 |
28 | private slots:
29 | void onComputerFound(NvComputer *computer);
30 | void onComputerUpdated(NvComputer *computer);
31 | void onComputerSeekTimeout();
32 |
33 | private:
34 | QScopedPointer m_DPtr;
35 | ListCommandLineParser m_Arguments;
36 | };
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/cli/pair.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class ComputerManager;
7 | class NvComputer;
8 | class Session;
9 | class StreamingPreferences;
10 |
11 | namespace CliPair
12 | {
13 |
14 | class Event;
15 | class LauncherPrivate;
16 |
17 | class Launcher : public QObject
18 | {
19 | Q_OBJECT
20 | Q_DECLARE_PRIVATE_D(m_DPtr, Launcher)
21 |
22 | public:
23 | explicit Launcher(QString computer, QString predefinedPin,
24 | QObject *parent = nullptr);
25 | ~Launcher();
26 | Q_INVOKABLE void execute(ComputerManager *manager);
27 | Q_INVOKABLE bool isExecuted() const;
28 |
29 | signals:
30 | void searchingComputer();
31 | void pairing(QString pcName, QString pin);
32 | void failed(QString text);
33 | void success();
34 |
35 | private slots:
36 | void onComputerFound(NvComputer *computer);
37 | void onPairingCompleted(NvComputer *computer, QString error);
38 | void onTimeout();
39 |
40 | private:
41 | QScopedPointer m_DPtr;
42 | };
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/cli/quitstream.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class ComputerManager;
7 | class NvComputer;
8 |
9 | namespace CliQuitStream
10 | {
11 |
12 | class LauncherPrivate;
13 |
14 | class Launcher : public QObject
15 | {
16 | Q_OBJECT
17 | Q_DECLARE_PRIVATE_D(m_DPtr, Launcher)
18 |
19 | public:
20 | explicit Launcher(QString computer, QObject *parent = nullptr);
21 | ~Launcher();
22 |
23 | Q_INVOKABLE void execute(ComputerManager *manager);
24 | Q_INVOKABLE bool isExecuted() const;
25 |
26 | signals:
27 | void searchingComputer();
28 | void quittingApp();
29 | void failed(QString text);
30 |
31 | private slots:
32 | void onComputerFound(NvComputer *computer);
33 | void onComputerSeekTimeout();
34 | void onQuitAppCompleted(QVariant error);
35 |
36 | private:
37 | QScopedPointer m_DPtr;
38 | };
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/cli/startstream.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class ComputerManager;
7 | class NvComputer;
8 | class Session;
9 | class StreamingPreferences;
10 |
11 | namespace CliStartStream
12 | {
13 |
14 | class Event;
15 | class LauncherPrivate;
16 |
17 | class Launcher : public QObject
18 | {
19 | Q_OBJECT
20 | Q_DECLARE_PRIVATE_D(m_DPtr, Launcher)
21 |
22 | public:
23 | explicit Launcher(QString computer, QString app,
24 | StreamingPreferences* preferences,
25 | QObject *parent = nullptr);
26 | ~Launcher();
27 | Q_INVOKABLE void execute(ComputerManager *manager);
28 | Q_INVOKABLE void quitRunningApp();
29 | Q_INVOKABLE bool isExecuted() const;
30 |
31 | signals:
32 | void searchingComputer();
33 | void searchingApp();
34 | void sessionCreated(QString appName, Session *session);
35 | void failed(QString text);
36 | void appQuitRequired(QString appName);
37 |
38 | private slots:
39 | void onComputerFound(NvComputer *computer);
40 | void onComputerUpdated(NvComputer *computer);
41 | void onTimeout();
42 | void onQuitAppCompleted(QVariant error);
43 |
44 | private:
45 | QScopedPointer m_DPtr;
46 | };
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/deploy/linux/appimage/0001-Vulkan-Don-t-try-to-reuse-old-swapchain.patch:
--------------------------------------------------------------------------------
1 | From 78261eb6e2fe728fa5d3843b5b8962b1ca559b7e Mon Sep 17 00:00:00 2001
2 | From: David Rosca
3 | Date: Sat, 16 Dec 2023 14:52:01 +0100
4 | Subject: [PATCH] Vulkan: Don't try to reuse old swapchain
5 |
6 | ---
7 | src/vulkan/swapchain.c | 12 ++++++++++--
8 | 1 file changed, 10 insertions(+), 2 deletions(-)
9 |
10 | diff --git a/src/vulkan/swapchain.c b/src/vulkan/swapchain.c
11 | index 0646e57c..c0b07ce4 100644
12 | --- a/src/vulkan/swapchain.c
13 | +++ b/src/vulkan/swapchain.c
14 | @@ -612,13 +612,21 @@ static bool vk_sw_recreate(pl_swapchain sw, int w, int h)
15 | }
16 | #endif
17 |
18 | + {
19 | + pl_gpu_flush(gpu);
20 | + vk_wait_idle(vk);
21 | + for (int i = 0; i < vk->pool_graphics->num_queues; i++)
22 | + vk->QueueWaitIdle(vk->pool_graphics->queues[i]);
23 | + vk->DestroySwapchainKHR(vk->dev, p->swapchain, PL_VK_ALLOC);
24 | + }
25 | +
26 | // Calling `vkCreateSwapchainKHR` puts sinfo.oldSwapchain into a retired
27 | // state whether the call succeeds or not, so we always need to garbage
28 | // collect it afterwards - asynchronously as it may still be in use
29 | - sinfo.oldSwapchain = p->swapchain;
30 | + /* sinfo.oldSwapchain = p->swapchain; */
31 | p->swapchain = VK_NULL_HANDLE;
32 | VkResult res = vk->CreateSwapchainKHR(vk->dev, &sinfo, PL_VK_ALLOC, &p->swapchain);
33 | - vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain));
34 | + /* vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain)); */
35 | PL_VK_ASSERT(res, "vk->CreateSwapchainKHR(...)");
36 |
37 | // Get the new swapchain images
38 | --
39 | 2.43.0
40 |
41 |
--------------------------------------------------------------------------------
/app/deploy/linux/com.moonlight_stream.Moonlight.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Moonlight
3 | Comment=Stream games and other applications from another PC running Sunshine or GeForce Experience
4 | Exec=moonlight
5 | Icon=moonlight
6 | Terminal=false
7 | Type=Application
8 | Categories=Qt;Game;Network;RemoteAccess;
9 | Keywords=nvidia;gamestream;stream;sunshine;remote play;
10 |
--------------------------------------------------------------------------------
/app/deploy/steamlink/moonlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/deploy/steamlink/moonlight.png
--------------------------------------------------------------------------------
/app/deploy/steamlink/moonlight.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # The default HOME is not persistent, so override
4 | # it to a path on the onboard flash. Otherwise our
5 | # pairing data will be lost each reboot.
6 | HOME=/usr/local/moonlight
7 |
8 | # Enable Steam Controller detection in SDL
9 | # https://github.com/moonlight-stream/moonlight-qt/issues/697
10 | export SDL_ENABLE_STEAM_CONTROLLERS=1
11 |
12 | # Renice PE_Single_CPU which seems to host A/V stuff
13 | renice -10 -p $(pidof PE_Single_CPU)
14 |
15 | # Renice Moonlight itself to avoid preemption by background tasks
16 | # Write output to a logfile in /tmp
17 | exec nice -n -10 ./bin/moonlight > /tmp/moonlight.log 2>&1
18 |
--------------------------------------------------------------------------------
/app/deploy/steamlink/toc.txt:
--------------------------------------------------------------------------------
1 | name=Moonlight
2 | icon=moonlight.png
3 | run=moonlight.sh
4 |
--------------------------------------------------------------------------------
/app/gui/AutoResizingComboBox.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.9
2 | import QtQuick.Controls 2.2
3 |
4 | import SdlGamepadKeyNavigation 1.0
5 | import SystemProperties 1.0
6 |
7 | // https://stackoverflow.com/questions/45029968/how-do-i-set-the-combobox-width-to-fit-the-largest-item
8 | ComboBox {
9 | property int textWidth
10 | property int desiredWidth : leftPadding + textWidth + indicator.width + rightPadding
11 | property int maximumWidth : parent.width
12 |
13 | implicitWidth: desiredWidth < maximumWidth ? desiredWidth : maximumWidth
14 |
15 | TextMetrics {
16 | id: popupMetrics
17 | }
18 |
19 | TextMetrics {
20 | id: textMetrics
21 | }
22 |
23 | function recalculateWidth() {
24 | textMetrics.font = font
25 | popupMetrics.font = popup.font
26 | textWidth = 0
27 | for (var i = 0; i < count; i++){
28 | textMetrics.text = textAt(i)
29 | popupMetrics.text = textAt(i)
30 | textWidth = Math.max(textMetrics.width, textWidth)
31 | textWidth = Math.max(popupMetrics.width, textWidth)
32 | }
33 | }
34 |
35 | // We call this every time the options change (and init)
36 | // so we can adjust the combo box width here too
37 | onActivated: recalculateWidth()
38 |
39 | popup.onAboutToShow: {
40 | // Switch to normal navigation for combo boxes
41 | SdlGamepadKeyNavigation.setUiNavMode(false)
42 |
43 | // Override the popup color to improve contrast with the overridden
44 | // Material 2 background color set in main.qml.
45 | if (SystemProperties.usesMaterial3Theme) {
46 | popup.background.color = "#424242"
47 | }
48 | }
49 |
50 | popup.onAboutToHide: {
51 | SdlGamepadKeyNavigation.setUiNavMode(true)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/gui/CenteredGridView.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.9
2 | import QtQuick.Controls 2.2
3 |
4 | GridView {
5 | // Detect Qt 5.11 or earlier using presence of synchronousDrag.
6 | // Prior to 5.12, the leftMargin and rightMargin values did not work.
7 | property bool hasBrokenMargins: this.synchronousDrag === undefined
8 |
9 | property int minMargin: 10
10 | property real availableWidth: (parent.width - 2 * minMargin)
11 | property int itemsPerRow: availableWidth / cellWidth
12 | property real horizontalMargin: itemsPerRow < count && availableWidth >= cellWidth ?
13 | (availableWidth % cellWidth) / 2 : minMargin
14 |
15 | function updateMargins() {
16 | leftMargin = horizontalMargin
17 | rightMargin = horizontalMargin
18 |
19 | if (hasBrokenMargins) {
20 | anchors.leftMargin = leftMargin
21 | anchors.rightMargin = rightMargin
22 | }
23 | }
24 |
25 | onHorizontalMarginChanged: {
26 | updateMargins()
27 | }
28 |
29 | Component.onCompleted: {
30 | if (hasBrokenMargins) {
31 | // This will cause an anchor conflict with the parent StackView
32 | // which breaks animation, but otherwise the grid will not be
33 | // centered in the window.
34 | anchors.fill = parent
35 | anchors.topMargin = topMargin
36 | anchors.bottomMargin = bottomMargin
37 | }
38 |
39 | updateMargins()
40 | }
41 |
42 | boundsBehavior: Flickable.OvershootBounds
43 | }
44 |
--------------------------------------------------------------------------------
/app/gui/CliPair.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | import ComputerManager 1.0
5 |
6 | Item {
7 | function onSearchingComputer() {
8 | stageLabel.text = qsTr("Establishing connection to PC...")
9 | }
10 |
11 | function onPairing(pcName, pin) {
12 | stageLabel.text = qsTr("Pairing... Please enter '%1' on %2.").arg(pin).arg(pcName)
13 | }
14 |
15 | function onFailed(message) {
16 | stageIndicator.visible = false
17 | errorDialog.text = message
18 | errorDialog.open()
19 | }
20 |
21 | function onSuccess(appName) {
22 | stageIndicator.visible = false
23 | pairCompleteDialog.open()
24 | }
25 |
26 | // Allow user to back out of pairing
27 | Keys.onEscapePressed: {
28 | Qt.quit()
29 | }
30 | Keys.onBackPressed: {
31 | Qt.quit()
32 | }
33 | Keys.onCancelPressed: {
34 | Qt.quit()
35 | }
36 |
37 | StackView.onActivated: {
38 | if (!launcher.isExecuted()) {
39 | toolBar.visible = false
40 |
41 | launcher.searchingComputer.connect(onSearchingComputer)
42 | launcher.pairing.connect(onPairing)
43 | launcher.failed.connect(onFailed)
44 | launcher.success.connect(onSuccess)
45 | launcher.execute(ComputerManager)
46 | }
47 | }
48 |
49 | Row {
50 | anchors.centerIn: parent
51 | spacing: 5
52 | id: stageIndicator
53 |
54 | BusyIndicator {
55 | id: stageSpinner
56 | }
57 |
58 | Label {
59 | id: stageLabel
60 | height: stageSpinner.height
61 | font.pointSize: 20
62 | verticalAlignment: Text.AlignVCenter
63 |
64 | wrapMode: Text.Wrap
65 | }
66 | }
67 |
68 | ErrorMessageDialog {
69 | id: errorDialog
70 |
71 | onClosed: {
72 | Qt.quit();
73 | }
74 | }
75 |
76 | NavigableMessageDialog {
77 | id: pairCompleteDialog
78 | closePolicy: Popup.CloseOnEscape
79 |
80 | text:qsTr("Pairing completed successfully")
81 | standardButtons: Dialog.Ok
82 | onClosed: {
83 | Qt.quit()
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/gui/CliQuitStreamSegue.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | import ComputerManager 1.0
5 | import Session 1.0
6 |
7 | Item {
8 | function onSearchingComputer() {
9 | stageLabel.text = qsTr("Establishing connection to PC...")
10 | }
11 |
12 | function onQuittingApp() {
13 | stageLabel.text = qsTr("Quitting app...")
14 | }
15 |
16 | function onFailure(message) {
17 | errorDialog.text = message
18 | errorDialog.open()
19 | }
20 |
21 | StackView.onActivated: {
22 | if (!launcher.isExecuted()) {
23 | toolBar.visible = false
24 | launcher.searchingComputer.connect(onSearchingComputer)
25 | launcher.quittingApp.connect(onQuittingApp)
26 | launcher.failed.connect(onFailure)
27 | launcher.execute(ComputerManager)
28 | }
29 | }
30 |
31 | Row {
32 | anchors.centerIn: parent
33 | spacing: 5
34 |
35 | BusyIndicator {
36 | id: stageSpinner
37 | }
38 |
39 | Label {
40 | id: stageLabel
41 | height: stageSpinner.height
42 | text: stageText
43 | font.pointSize: 20
44 | verticalAlignment: Text.AlignVCenter
45 |
46 | wrapMode: Text.Wrap
47 | }
48 | }
49 |
50 | ErrorMessageDialog {
51 | id: errorDialog
52 |
53 | onClosed: {
54 | Qt.quit()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/gui/CliStartStreamSegue.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | import ComputerManager 1.0
5 |
6 | Item {
7 | function onSearchingComputer() {
8 | stageLabel.text = qsTr("Establishing connection to PC...")
9 | }
10 |
11 | function onSearchingApp() {
12 | stageLabel.text = qsTr("Loading app list...")
13 | }
14 |
15 | function onSessionCreated(appName, session) {
16 | var component = Qt.createComponent("StreamSegue.qml")
17 | var segue = component.createObject(stackView, {
18 | "appName": appName,
19 | "session": session,
20 | "quitAfter": true
21 | })
22 | stackView.push(segue)
23 | }
24 |
25 | function onLaunchFailed(message) {
26 | errorDialog.text = message
27 | errorDialog.open()
28 | console.error(message)
29 | }
30 |
31 | function onAppQuitRequired(appName) {
32 | quitAppDialog.appName = appName
33 | quitAppDialog.open()
34 | }
35 |
36 | StackView.onActivated: {
37 | if (!launcher.isExecuted()) {
38 | toolBar.visible = false
39 |
40 | launcher.searchingComputer.connect(onSearchingComputer)
41 | launcher.searchingApp.connect(onSearchingApp)
42 | launcher.sessionCreated.connect(onSessionCreated)
43 | launcher.failed.connect(onLaunchFailed)
44 | launcher.appQuitRequired.connect(onAppQuitRequired)
45 | launcher.execute(ComputerManager)
46 | }
47 | }
48 |
49 | Row {
50 | anchors.centerIn: parent
51 | spacing: 5
52 |
53 | BusyIndicator {
54 | id: stageSpinner
55 | }
56 |
57 | Label {
58 | id: stageLabel
59 | height: stageSpinner.height
60 | font.pointSize: 20
61 | verticalAlignment: Text.AlignVCenter
62 |
63 | wrapMode: Text.Wrap
64 | }
65 | }
66 |
67 | ErrorMessageDialog {
68 | id: errorDialog
69 |
70 | onClosed: {
71 | Qt.quit();
72 | }
73 | }
74 |
75 | NavigableMessageDialog {
76 | id: quitAppDialog
77 | text:qsTr("Are you sure you want to quit %1? Any unsaved progress will be lost.").arg(appName)
78 | standardButtons: Dialog.Yes | Dialog.No
79 | property string appName : ""
80 |
81 | function quitApp() {
82 | var component = Qt.createComponent("QuitSegue.qml")
83 | var params = {"appName": appName, "quitRunningAppFn": function() { launcher.quitRunningApp() }}
84 | stackView.push(component.createObject(stackView, params))
85 | }
86 |
87 | onAccepted: quitApp()
88 | onRejected: Qt.quit()
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/gui/ErrorMessageDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | import SystemProperties 1.0
5 |
6 | NavigableMessageDialog {
7 | standardButtons: Dialog.Ok | (SystemProperties.hasBrowser ? Dialog.Help : 0)
8 | }
9 |
--------------------------------------------------------------------------------
/app/gui/GamepadMapper.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 |
3 | Item {
4 | objectName: qsTr("Gamepad Mapping")
5 | }
6 |
--------------------------------------------------------------------------------
/app/gui/NavigableDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | Dialog {
5 | // We should use Overlay.overlay here but that's not available in Qt 5.9 :(
6 | parent: ApplicationWindow.contentItem
7 |
8 | x: Math.round((parent.width - width) / 2)
9 | y: Math.round((parent.height - height) / 2)
10 |
11 | onAboutToHide: {
12 | // We must force focus back to the last item for platforms without
13 | // support for more than one active window like Steam Link. If
14 | // we don't, gamepad and keyboard navigation will break after a
15 | // dialog appears.
16 | stackView.forceActiveFocus()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/gui/NavigableItemDelegate.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | ItemDelegate {
5 | property GridView grid
6 |
7 | highlighted: grid.activeFocus && grid.currentItem === this
8 |
9 | Keys.onLeftPressed: {
10 | grid.moveCurrentIndexLeft()
11 | }
12 | Keys.onRightPressed: {
13 | grid.moveCurrentIndexRight()
14 | }
15 | Keys.onDownPressed: {
16 | grid.moveCurrentIndexDown()
17 | }
18 | Keys.onUpPressed: {
19 | grid.moveCurrentIndexUp()
20 | }
21 | Keys.onReturnPressed: {
22 | clicked()
23 | }
24 | Keys.onEnterPressed: {
25 | clicked()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/gui/NavigableMenu.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | Menu {
5 | onOpened: {
6 | // Give focus to the first visible and enabled menu item
7 | for (var i = 0; i < count; i++) {
8 | var item = itemAt(i)
9 | if (item.visible && item.enabled) {
10 | item.forceActiveFocus(Qt.TabFocusReason)
11 | break
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/gui/NavigableMenuItem.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | MenuItem {
5 | // Qt 5.10 has a menu property, but we need to support 5.9
6 | // so we must make our own.
7 | property Menu parentMenu
8 |
9 | // Ensure focus can't be given to an invisible item
10 | enabled: visible
11 | height: visible ? implicitHeight : 0
12 | focusPolicy: visible ? Qt.TabFocus : Qt.NoFocus
13 |
14 | onTriggered: {
15 | // We must close the context menu first or
16 | // it can steal focus from any dialogs that
17 | // onTriggered may spawn.
18 | parentMenu.close()
19 | }
20 |
21 | Keys.onReturnPressed: {
22 | triggered()
23 | }
24 |
25 | Keys.onEnterPressed: {
26 | triggered()
27 | }
28 |
29 | Keys.onEscapePressed: {
30 | parentMenu.close()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/gui/NavigableMessageDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 | import QtQuick.Layouts 1.2
4 |
5 | NavigableDialog {
6 | id: dialog
7 |
8 | property alias text: dialogLabel.dialogText
9 | property alias showSpinner: dialogSpinner.visible
10 | property alias imageSrc: dialogImage.source
11 |
12 | property string helpText
13 | property string helpUrl : "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"
14 | property string helpTextSeparator : " "
15 |
16 | onOpened: {
17 | // Force keyboard focus on the label so keyboard navigation works
18 | dialogLabel.forceActiveFocus()
19 | }
20 |
21 | RowLayout {
22 | spacing: 10
23 |
24 | BusyIndicator {
25 | id: dialogSpinner
26 | visible: false
27 | }
28 |
29 | Image {
30 | id: dialogImage
31 | source: (standardButtons & Dialog.Yes) ?
32 | "qrc:/res/baseline-help_outline-24px.svg" :
33 | "qrc:/res/baseline-error_outline-24px.svg"
34 | sourceSize {
35 | // The icon should be square so use the height as the width too
36 | width: 50
37 | height: 50
38 | }
39 | visible: !showSpinner
40 | }
41 |
42 | Label {
43 | property string dialogText
44 |
45 | id: dialogLabel
46 | text: dialogText + ((helpText && (standardButtons & Dialog.Help)) ? (helpTextSeparator + helpText) : "")
47 | wrapMode: Text.Wrap
48 | elide: Label.ElideRight
49 |
50 | // Cap the width so the dialog doesn't grow horizontally forever. This
51 | // will cause word wrap to kick in.
52 | Layout.maximumWidth: 400
53 | Layout.maximumHeight: 400
54 |
55 | Keys.onReturnPressed: {
56 | accept()
57 | }
58 |
59 | Keys.onEnterPressed: {
60 | accept()
61 | }
62 |
63 | Keys.onEscapePressed: {
64 | reject()
65 | }
66 | }
67 | }
68 |
69 | footer: DialogButtonBox {
70 | id: dialogButtonBox
71 | standardButtons: dialog.standardButtons
72 |
73 | onHelpRequested: {
74 | Qt.openUrlExternally(helpUrl)
75 | close()
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/gui/NavigableToolButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 | import QtQuick.Layouts 1.3
4 |
5 | ToolButton {
6 | property string iconSource
7 |
8 | activeFocusOnTab: true
9 |
10 | // FIXME: We're using an Image here rather than icon.source because
11 | // icons don't work on Qt 5.9 LTS.
12 | Image {
13 | id: image
14 | source: iconSource
15 | anchors.centerIn: parent.background
16 | sourceSize {
17 | width: parent.background.width * 1.10
18 | height: parent.background.height * 1.10
19 | }
20 | }
21 |
22 | // This determines the size of the Material highlight. We increase it
23 | // from the default because we use larger than normal icons for TV readability.
24 | Layout.preferredHeight: parent.height
25 |
26 | Keys.onReturnPressed: {
27 | clicked()
28 | }
29 |
30 | Keys.onEnterPressed: {
31 | clicked()
32 | }
33 |
34 | Keys.onRightPressed: {
35 | nextItemInFocusChain(true).forceActiveFocus(Qt.TabFocus)
36 | }
37 |
38 | Keys.onLeftPressed: {
39 | nextItemInFocusChain(false).forceActiveFocus(Qt.TabFocus)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/gui/QuitSegue.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.0
2 | import QtQuick.Controls 2.2
3 |
4 | import ComputerManager 1.0
5 | import Session 1.0
6 |
7 | Item {
8 | property string appName
9 | property var quitRunningAppFn
10 | property Session nextSession : null
11 | property string nextAppName : ""
12 |
13 | property string stageText : qsTr("Quitting %1...").arg(appName)
14 |
15 | function quitAppCompleted(error)
16 | {
17 | // Display a failed dialog if we got an error
18 | if (error !== undefined) {
19 | errorDialog.text = error
20 | errorDialog.open()
21 | console.error(error)
22 | }
23 |
24 | // If we're supposed to launch another game after this, do so now
25 | if (error === undefined && nextSession !== null) {
26 | var component = Qt.createComponent("StreamSegue.qml")
27 | var segue = component.createObject(stackView, {"appName": nextAppName, "session": nextSession})
28 | stackView.replace(segue)
29 | }
30 | else {
31 | // Exit this view
32 | stackView.pop()
33 | }
34 | }
35 |
36 | StackView.onActivated: {
37 | // Hide the toolbar before we start loading
38 | toolBar.visible = false
39 |
40 | // Connect the quit completion signal
41 | ComputerManager.quitAppCompleted.connect(quitAppCompleted)
42 |
43 | // Start the quit operation if requested
44 | if (quitRunningAppFn) {
45 | quitRunningAppFn()
46 | }
47 | }
48 |
49 | StackView.onDeactivating: {
50 | // Show the toolbar again
51 | toolBar.visible = true
52 |
53 | // Disconnect the signal
54 | ComputerManager.quitAppCompleted.disconnect(quitAppCompleted)
55 | }
56 |
57 | Row {
58 | anchors.centerIn: parent
59 | spacing: 5
60 |
61 | BusyIndicator {
62 | id: stageSpinner
63 | }
64 |
65 | Label {
66 | id: stageLabel
67 | height: stageSpinner.height
68 | text: stageText
69 | font.pointSize: 20
70 | verticalAlignment: Text.AlignVCenter
71 |
72 | wrapMode: Text.Wrap
73 | }
74 | }
75 |
76 | ErrorMessageDialog {
77 | id: errorDialog
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/gui/appmodel.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "backend/boxartmanager.h"
4 | #include "backend/computermanager.h"
5 | #include "streaming/session.h"
6 |
7 | #include
8 |
9 | class AppModel : public QAbstractListModel
10 | {
11 | Q_OBJECT
12 |
13 | enum Roles
14 | {
15 | NameRole = Qt::UserRole,
16 | RunningRole,
17 | BoxArtRole,
18 | HiddenRole,
19 | AppIdRole,
20 | DirectLaunchRole,
21 | AppCollectorGameRole,
22 | };
23 |
24 | public:
25 | explicit AppModel(QObject *parent = nullptr);
26 |
27 | // Must be called before any QAbstractListModel functions
28 | Q_INVOKABLE void initialize(ComputerManager* computerManager, int computerIndex, bool showHiddenGames);
29 |
30 | Q_INVOKABLE Session* createSessionForApp(int appIndex);
31 |
32 | Q_INVOKABLE int getDirectLaunchAppIndex();
33 |
34 | Q_INVOKABLE int getRunningAppId();
35 |
36 | Q_INVOKABLE QString getRunningAppName();
37 |
38 | Q_INVOKABLE void quitRunningApp();
39 |
40 | Q_INVOKABLE void setAppHidden(int appIndex, bool hidden);
41 |
42 | Q_INVOKABLE void setAppDirectLaunch(int appIndex, bool directLaunch);
43 |
44 | QVariant data(const QModelIndex &index, int role) const override;
45 |
46 | int rowCount(const QModelIndex &parent) const override;
47 |
48 | virtual QHash roleNames() const override;
49 |
50 | private slots:
51 | void handleComputerStateChanged(NvComputer* computer);
52 |
53 | void handleBoxArtLoaded(NvComputer* computer, NvApp app, QUrl image);
54 |
55 | signals:
56 | void computerLost();
57 |
58 | private:
59 | void updateAppList(QVector newList);
60 |
61 | QVector getVisibleApps(const QVector& appList);
62 |
63 | bool isAppCurrentlyVisible(const NvApp& app);
64 |
65 | NvComputer* m_Computer;
66 | BoxArtManager m_BoxArtManager;
67 | ComputerManager* m_ComputerManager;
68 | QVector m_VisibleApps, m_AllApps;
69 | int m_CurrentGameId;
70 | bool m_ShowHiddenGames;
71 | };
72 |
--------------------------------------------------------------------------------
/app/gui/computermodel.h:
--------------------------------------------------------------------------------
1 | #include "backend/computermanager.h"
2 | #include "streaming/session.h"
3 |
4 | #include
5 |
6 | class ComputerModel : public QAbstractListModel
7 | {
8 | Q_OBJECT
9 |
10 | enum Roles
11 | {
12 | NameRole = Qt::UserRole,
13 | OnlineRole,
14 | PairedRole,
15 | BusyRole,
16 | WakeableRole,
17 | StatusUnknownRole,
18 | ServerSupportedRole,
19 | DetailsRole
20 | };
21 |
22 | public:
23 | explicit ComputerModel(QObject* object = nullptr);
24 |
25 | // Must be called before any QAbstractListModel functions
26 | Q_INVOKABLE void initialize(ComputerManager* computerManager);
27 |
28 | QVariant data(const QModelIndex &index, int role) const override;
29 |
30 | int rowCount(const QModelIndex &parent) const override;
31 |
32 | virtual QHash roleNames() const override;
33 |
34 | Q_INVOKABLE void deleteComputer(int computerIndex);
35 |
36 | Q_INVOKABLE QString generatePinString();
37 |
38 | Q_INVOKABLE void pairComputer(int computerIndex, QString pin);
39 |
40 | Q_INVOKABLE void testConnectionForComputer(int computerIndex);
41 |
42 | Q_INVOKABLE void wakeComputer(int computerIndex);
43 |
44 | Q_INVOKABLE void renameComputer(int computerIndex, QString name);
45 |
46 | Q_INVOKABLE Session* createSessionForCurrentGame(int computerIndex);
47 |
48 | signals:
49 | void pairingCompleted(QVariant error);
50 | void connectionTestCompleted(int result, QString blockedPorts);
51 |
52 | private slots:
53 | void handleComputerStateChanged(NvComputer* computer);
54 |
55 | void handlePairingCompleted(NvComputer* computer, QString error);
56 |
57 | private:
58 | QVector m_Computers;
59 | ComputerManager* m_ComputerManager;
60 | };
61 |
--------------------------------------------------------------------------------
/app/gui/sdlgamepadkeynavigation.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "SDL_compat.h"
7 |
8 | #include "settings/streamingpreferences.h"
9 |
10 | class SdlGamepadKeyNavigation : public QObject
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | SdlGamepadKeyNavigation(StreamingPreferences* prefs);
16 |
17 | ~SdlGamepadKeyNavigation();
18 |
19 | Q_INVOKABLE void enable();
20 |
21 | Q_INVOKABLE void disable();
22 |
23 | Q_INVOKABLE void notifyWindowFocus(bool hasFocus);
24 |
25 | Q_INVOKABLE void setUiNavMode(bool settingsMode);
26 |
27 | Q_INVOKABLE int getConnectedGamepads();
28 |
29 | private:
30 | void sendKey(QEvent::Type type, Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
31 |
32 | void updateTimerState();
33 |
34 | private slots:
35 | void onPollingTimerFired();
36 |
37 | private:
38 | StreamingPreferences* m_Prefs;
39 | QTimer* m_PollingTimer;
40 | QList m_Gamepads;
41 | bool m_Enabled;
42 | bool m_UiNavMode;
43 | bool m_FirstPoll;
44 | bool m_HasFocus;
45 | Uint32 m_LastAxisNavigationEventTime;
46 | };
47 |
--------------------------------------------------------------------------------
/app/languages/qml_bg.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_bg.qm
--------------------------------------------------------------------------------
/app/languages/qml_ckb.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_ckb.qm
--------------------------------------------------------------------------------
/app/languages/qml_cs.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_cs.qm
--------------------------------------------------------------------------------
/app/languages/qml_de.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_de.qm
--------------------------------------------------------------------------------
/app/languages/qml_el.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_el.qm
--------------------------------------------------------------------------------
/app/languages/qml_eo.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_eo.qm
--------------------------------------------------------------------------------
/app/languages/qml_es.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_es.qm
--------------------------------------------------------------------------------
/app/languages/qml_et.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_et.qm
--------------------------------------------------------------------------------
/app/languages/qml_fr.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_fr.qm
--------------------------------------------------------------------------------
/app/languages/qml_he.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_he.qm
--------------------------------------------------------------------------------
/app/languages/qml_hi.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_hi.qm
--------------------------------------------------------------------------------
/app/languages/qml_hu.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_hu.qm
--------------------------------------------------------------------------------
/app/languages/qml_it.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_it.qm
--------------------------------------------------------------------------------
/app/languages/qml_ja.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_ja.qm
--------------------------------------------------------------------------------
/app/languages/qml_ko.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_ko.qm
--------------------------------------------------------------------------------
/app/languages/qml_lt.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_lt.qm
--------------------------------------------------------------------------------
/app/languages/qml_nb_NO.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_nb_NO.qm
--------------------------------------------------------------------------------
/app/languages/qml_nl.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_nl.qm
--------------------------------------------------------------------------------
/app/languages/qml_pl.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_pl.qm
--------------------------------------------------------------------------------
/app/languages/qml_pt.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_pt.qm
--------------------------------------------------------------------------------
/app/languages/qml_pt_BR.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_pt_BR.qm
--------------------------------------------------------------------------------
/app/languages/qml_ru.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_ru.qm
--------------------------------------------------------------------------------
/app/languages/qml_sv.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_sv.qm
--------------------------------------------------------------------------------
/app/languages/qml_ta.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_ta.qm
--------------------------------------------------------------------------------
/app/languages/qml_th.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_th.qm
--------------------------------------------------------------------------------
/app/languages/qml_tr.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_tr.qm
--------------------------------------------------------------------------------
/app/languages/qml_uk.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_uk.qm
--------------------------------------------------------------------------------
/app/languages/qml_vi.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_vi.qm
--------------------------------------------------------------------------------
/app/languages/qml_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_zh_CN.qm
--------------------------------------------------------------------------------
/app/languages/qml_zh_TW.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/languages/qml_zh_TW.qm
--------------------------------------------------------------------------------
/app/moonlight.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/moonlight.icns
--------------------------------------------------------------------------------
/app/moonlight.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/moonlight.ico
--------------------------------------------------------------------------------
/app/moonlight_wix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/moonlight_wix.png
--------------------------------------------------------------------------------
/app/path.cpp:
--------------------------------------------------------------------------------
1 | #include "path.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | QString Path::s_CacheDir;
10 | QString Path::s_LogDir;
11 | QString Path::s_BoxArtCacheDir;
12 | QString Path::s_QmlCacheDir;
13 |
14 | QString Path::getLogDir()
15 | {
16 | Q_ASSERT(!s_LogDir.isEmpty());
17 | return s_LogDir;
18 | }
19 |
20 | QString Path::getBoxArtCacheDir()
21 | {
22 | Q_ASSERT(!s_BoxArtCacheDir.isEmpty());
23 | return s_BoxArtCacheDir;
24 | }
25 |
26 | QString Path::getQmlCacheDir()
27 | {
28 | Q_ASSERT(!s_QmlCacheDir.isEmpty());
29 | return s_QmlCacheDir;
30 | }
31 |
32 | QByteArray Path::readDataFile(QString fileName)
33 | {
34 | QFile dataFile(getDataFilePath(fileName));
35 | dataFile.open(QIODevice::ReadOnly);
36 | return dataFile.readAll();
37 | }
38 |
39 | void Path::writeCacheFile(QString fileName, QByteArray data)
40 | {
41 | QDir cacheDir(s_CacheDir);
42 |
43 | // Create the cache path if it does not exist
44 | if (!cacheDir.exists()) {
45 | cacheDir.mkpath(".");
46 | }
47 |
48 | QFile dataFile(cacheDir.absoluteFilePath(fileName));
49 | dataFile.open(QIODevice::WriteOnly);
50 | dataFile.write(data);
51 | }
52 |
53 | void Path::deleteCacheFile(QString fileName)
54 | {
55 | QFile dataFile(QDir(s_CacheDir).absoluteFilePath(fileName));
56 | dataFile.remove();
57 | }
58 |
59 | QFileInfo Path::getCacheFileInfo(QString fileName)
60 | {
61 | return QFileInfo(QDir(s_CacheDir), fileName);
62 | }
63 |
64 | QString Path::getDataFilePath(QString fileName)
65 | {
66 | QString candidatePath;
67 |
68 | // Check the cache location first (used by Path::writeDataFile())
69 | candidatePath = QDir(s_CacheDir).absoluteFilePath(fileName);
70 | if (QFile::exists(candidatePath)) {
71 | qInfo() << "Found" << fileName << "at" << candidatePath;
72 | return candidatePath;
73 | }
74 |
75 | // Check the current directory
76 | candidatePath = QDir(QDir::currentPath()).absoluteFilePath(fileName);
77 | if (QFile::exists(candidatePath)) {
78 | qInfo() << "Found" << fileName << "at" << candidatePath;
79 | return candidatePath;
80 | }
81 |
82 | // Now check the data directories (for Linux, in particular)
83 | candidatePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName);
84 | if (!candidatePath.isEmpty() && QFile::exists(candidatePath)) {
85 | qInfo() << "Found" << fileName << "at" << candidatePath;
86 | return candidatePath;
87 | }
88 |
89 | // Now try the directory of our app installation (for Windows, if current dir doesn't find it)
90 | candidatePath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(fileName);
91 | if (QFile::exists(candidatePath)) {
92 | qInfo() << "Found" << fileName << "at" << candidatePath;
93 | return candidatePath;
94 | }
95 |
96 | // Return the QRC embedded copy
97 | candidatePath = ":/data/" + fileName;
98 | qInfo() << "Found" << fileName << "at" << candidatePath;
99 | return QString(candidatePath);
100 | }
101 |
102 | void Path::initialize(bool portable)
103 | {
104 | if (portable) {
105 | s_LogDir = QDir::currentPath();
106 | s_BoxArtCacheDir = QDir::currentPath() + "/boxart";
107 | s_QmlCacheDir = QDir::currentPath() + "/qmlcache";
108 |
109 | // In order for the If-Modified-Since logic to work in MappingFetcher,
110 | // the cache directory must be different than the current directory.
111 | s_CacheDir = QDir::currentPath() + "/cache";
112 | }
113 | else {
114 | #ifdef Q_OS_DARWIN
115 | // On macOS, $TMPDIR is some random folder under /var/folders/ that nobody can
116 | // easily find, so use the system's global tmp directory instead.
117 | s_LogDir = "/tmp";
118 | #else
119 | s_LogDir = QDir::tempPath();
120 | #endif
121 | s_CacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
122 | s_BoxArtCacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/boxart";
123 | s_QmlCacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/qmlcache";
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/path.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class Path
7 | {
8 | public:
9 | static QString getLogDir();
10 | static QString getBoxArtCacheDir();
11 | static QString getQmlCacheDir();
12 |
13 | static QByteArray readDataFile(QString fileName);
14 | static void writeCacheFile(QString fileName, QByteArray data);
15 | static void deleteCacheFile(QString fileName);
16 | static QFileInfo getCacheFileInfo(QString fileName);
17 |
18 | // Only safe to use directly for Qt classes
19 | static QString getDataFilePath(QString fileName);
20 |
21 | static void initialize(bool portable);
22 |
23 | private:
24 | static QString s_CacheDir;
25 | static QString s_LogDir;
26 | static QString s_BoxArtCacheDir;
27 | static QString s_QmlCacheDir;
28 | };
29 |
--------------------------------------------------------------------------------
/app/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | gui/main.qml
4 | gui/PcView.qml
5 | gui/AppView.qml
6 | gui/SettingsView.qml
7 | gui/StreamSegue.qml
8 | gui/GamepadMapper.qml
9 | gui/QuitSegue.qml
10 | gui/NavigableToolButton.qml
11 | gui/NavigableItemDelegate.qml
12 | gui/NavigableMenuItem.qml
13 | gui/CliQuitStreamSegue.qml
14 | gui/CliStartStreamSegue.qml
15 | gui/AutoResizingComboBox.qml
16 | gui/NavigableMenu.qml
17 | gui/ErrorMessageDialog.qml
18 | gui/NavigableMessageDialog.qml
19 | gui/NavigableDialog.qml
20 | gui/CenteredGridView.qml
21 | gui/CliPair.qml
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/qt_qt5.conf:
--------------------------------------------------------------------------------
1 | [Platforms]
2 | WindowsArguments = darkmode=1
--------------------------------------------------------------------------------
/app/res/arrow_left.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/res/baseline-check_circle_outline-24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/baseline-error_outline-24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/res/baseline-help_outline-24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/res/baseline-lock-24px.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/res/baseline-warning-24px.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/res/desktop_windows-48px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/discord.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/ic_add_to_queue_white_48px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/ic_videogame_asset_white_48px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/moonlight.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/no_app_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/res/no_app_image.png
--------------------------------------------------------------------------------
/app/res/play_arrow_FILL1_wght700_GRAD200_opsz48.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/question_mark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/res/settings.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/res/stop_FILL1_wght700_GRAD200_opsz48.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/update.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/res/warning_FILL1_wght300_GRAD200_opsz24.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/settings/compatfetcher.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class CompatFetcher : public QObject
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | explicit CompatFetcher(QObject *parent = nullptr);
12 |
13 | void start();
14 |
15 | static bool isGfeVersionSupported(QString gfeVersion);
16 |
17 | private slots:
18 | void handleCompatInfoFetched(QNetworkReply* reply);
19 |
20 | private:
21 | QNetworkAccessManager* m_Nam;
22 | };
23 |
--------------------------------------------------------------------------------
/app/settings/mappingfetcher.cpp:
--------------------------------------------------------------------------------
1 | #include "mappingfetcher.h"
2 | #include "path.h"
3 |
4 | #include
5 |
6 | MappingFetcher::MappingFetcher(QObject *parent) :
7 | QObject(parent)
8 | {
9 | m_Nam = new QNetworkAccessManager(this);
10 |
11 | // Never communicate over HTTP
12 | m_Nam->setStrictTransportSecurityEnabled(true);
13 |
14 | // Allow HTTP redirects
15 | m_Nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
16 |
17 | connect(m_Nam, &QNetworkAccessManager::finished,
18 | this, &MappingFetcher::handleMappingListFetched);
19 | }
20 |
21 | void MappingFetcher::start()
22 | {
23 | if (!m_Nam) {
24 | Q_ASSERT(m_Nam);
25 | return;
26 | }
27 |
28 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 1) && !defined(QT_NO_BEARERMANAGEMENT)
29 | // HACK: Set network accessibility to work around QTBUG-80947 (introduced in Qt 5.14.0 and fixed in Qt 5.15.1)
30 | QT_WARNING_PUSH
31 | QT_WARNING_DISABLE_DEPRECATED
32 | m_Nam->setNetworkAccessible(QNetworkAccessManager::Accessible);
33 | QT_WARNING_POP
34 | #endif
35 |
36 | QUrl url("https://moonlight-stream.org/SDL_GameControllerDB/gamecontrollerdb.txt");
37 | QNetworkRequest request(url);
38 |
39 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
40 | request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
41 | #else
42 | request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
43 | #endif
44 |
45 | // Qt 5.12 introduced QNetworkRequest::IfModifiedSinceHeader. We _could_ implement it
46 | // by hand (including QDateTime conversion to the correct string form) for earlier Qt
47 | // versions, but that's a pain and this is just an optimization anyway. We'll do a bit
48 | // of extra work on those legacy Qt versions by fetching the GCDB each time we launch.
49 | #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
50 | // Only download the file if it's newer than what we have
51 | QFileInfo existingFileInfo = Path::getCacheFileInfo("gamecontrollerdb.txt");
52 | if (existingFileInfo.exists()) {
53 | // Make sure the cached file looks reasonable. It should have some data and
54 | // the last modified time should not be in the future.
55 | QDateTime lastModifiedTime = existingFileInfo.lastModified().toUTC();
56 | if (existingFileInfo.size() > 0 && lastModifiedTime <= QDateTime::currentDateTimeUtc()) {
57 | request.setHeader(QNetworkRequest::IfModifiedSinceHeader, existingFileInfo.lastModified().toUTC());
58 | }
59 | else {
60 | Path::deleteCacheFile("gamecontrollerdb.txt");
61 | }
62 | }
63 | #endif
64 |
65 | // We'll get a callback when this is finished
66 | m_Nam->get(request);
67 | }
68 |
69 | void MappingFetcher::handleMappingListFetched(QNetworkReply* reply)
70 | {
71 | Q_ASSERT(reply->isFinished());
72 |
73 | // Delete the QNetworkAccessManager to free resources and
74 | // prevent the bearer plugin from polling in the background.
75 | m_Nam->deleteLater();
76 | m_Nam = nullptr;
77 |
78 | if (reply->error() == QNetworkReply::NoError) {
79 | // Queue the reply for deletion
80 | reply->deleteLater();
81 |
82 | // If we get a 304 back, Qt will happily just tell us our request was
83 | // successful and let us try to write our empty response to disk. Check
84 | // the status code directly to prevent this.
85 | if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 304) {
86 | qInfo() << "Gamepad mappings are up to date";
87 | }
88 | else {
89 | // Update the cached data on disk for next call to applyMappings()
90 | QByteArray data = reply->readAll();
91 | if (!data.isEmpty()) {
92 | Path::writeCacheFile("gamecontrollerdb.txt", data);
93 | }
94 |
95 | qInfo() << "Downloaded updated gamepad mappings";
96 | }
97 | }
98 | else {
99 | qWarning() << "Failed to download updated gamepad mappings:" << reply->error();
100 | reply->deleteLater();
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/settings/mappingfetcher.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class MappingFetcher : public QObject
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | explicit MappingFetcher(QObject *parent = nullptr);
12 |
13 | void start();
14 |
15 | private slots:
16 | void handleMappingListFetched(QNetworkReply* reply);
17 |
18 | private:
19 | QNetworkAccessManager* m_Nam;
20 | };
21 |
--------------------------------------------------------------------------------
/app/settings/mappingmanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "mappingfetcher.h"
4 |
5 | #include
6 |
7 | class SdlGamepadMapping
8 | {
9 | public:
10 | SdlGamepadMapping() {}
11 |
12 | SdlGamepadMapping(QString string)
13 | {
14 | QStringList mapping = string.split(",");
15 | if (!mapping.isEmpty()) {
16 | m_Guid = mapping[0];
17 |
18 | string.remove(0, m_Guid.length() + 1);
19 | m_Mapping = string;
20 | }
21 | }
22 |
23 | SdlGamepadMapping(QString guid, QString mapping)
24 | : m_Guid(guid),
25 | m_Mapping(mapping)
26 | {
27 |
28 | }
29 |
30 | bool operator==(const SdlGamepadMapping& other) const
31 | {
32 | return m_Guid == other.m_Guid && m_Mapping == other.m_Mapping;
33 | }
34 |
35 | QString getGuid() const
36 | {
37 | return m_Guid;
38 | }
39 |
40 | QString getMapping() const
41 | {
42 | return m_Mapping;
43 | }
44 |
45 | QString getSdlMappingString() const
46 | {
47 | if (m_Guid.isEmpty() || m_Mapping.isEmpty()) {
48 | return "";
49 | }
50 | else {
51 | return m_Guid + "," + m_Mapping;
52 | }
53 | }
54 |
55 | private:
56 | QString m_Guid;
57 | QString m_Mapping;
58 | };
59 |
60 | class MappingManager
61 | {
62 | public:
63 | MappingManager();
64 |
65 | void addMapping(QString gamepadString);
66 |
67 | void addMapping(SdlGamepadMapping& gamepadMapping);
68 |
69 | void applyMappings();
70 |
71 | void save();
72 |
73 | private:
74 | QMap m_Mappings;
75 |
76 | static MappingFetcher* s_MappingFetcher;
77 | };
78 |
79 |
--------------------------------------------------------------------------------
/app/shaders/build_hlsl.bat:
--------------------------------------------------------------------------------
1 | fxc /T vs_4_0_level_9_3 /Fo d3d11_vertex.fxc d3d11_vertex.hlsl
2 |
3 | fxc /T ps_4_0_level_9_3 /Fo d3d11_overlay_pixel.fxc d3d11_overlay_pixel.hlsl
4 | fxc /T ps_4_0_level_9_3 /Fo d3d11_genyuv_pixel.fxc d3d11_genyuv_pixel.hlsl
5 | fxc /T ps_4_0_level_9_3 /Fo d3d11_bt601lim_pixel.fxc d3d11_bt601lim_pixel.hlsl
6 | fxc /T ps_4_0_level_9_3 /Fo d3d11_bt2020lim_pixel.fxc d3d11_bt2020lim_pixel.hlsl
7 | fxc /T ps_4_0_level_9_3 /Fo d3d11_ayuv_pixel.fxc d3d11_ayuv_pixel.hlsl
8 | fxc /T ps_4_0_level_9_3 /Fo d3d11_y410_pixel.fxc d3d11_y410_pixel.hlsl
--------------------------------------------------------------------------------
/app/shaders/d3d11_ayuv_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_ayuv_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_ayuv_pixel.hlsl:
--------------------------------------------------------------------------------
1 | #include "d3d11_yuv444_pixel_start.hlsli"
2 |
3 | min16float3 swizzle(min16float3 input)
4 | {
5 | // AYUV SRVs are in VUYA order
6 | return input.bgr;
7 | }
8 |
9 | #include "d3d11_yuv444_pixel_end.hlsli"
--------------------------------------------------------------------------------
/app/shaders/d3d11_bt2020lim_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_bt2020lim_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_bt2020lim_pixel.hlsl:
--------------------------------------------------------------------------------
1 | #include "d3d11_video_pixel_start.hlsli"
2 |
3 | static const min16float3x3 cscMatrix =
4 | {
5 | 1.1644, 1.1644, 1.1644,
6 | 0.0, -0.1874, 2.1418,
7 | 1.6781, -0.6505, 0.0,
8 | };
9 |
10 | static const min16float3 offsets =
11 | {
12 | 16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0
13 | };
14 |
15 | #include "d3d11_video_pixel_end.hlsli"
--------------------------------------------------------------------------------
/app/shaders/d3d11_bt601lim_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_bt601lim_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_bt601lim_pixel.hlsl:
--------------------------------------------------------------------------------
1 | #include "d3d11_video_pixel_start.hlsli"
2 |
3 | static const min16float3x3 cscMatrix =
4 | {
5 | 1.1644, 1.1644, 1.1644,
6 | 0.0, -0.3917, 2.0172,
7 | 1.5960, -0.8129, 0.0,
8 | };
9 |
10 | static const min16float3 offsets =
11 | {
12 | 16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0
13 | };
14 |
15 | #include "d3d11_video_pixel_end.hlsli"
--------------------------------------------------------------------------------
/app/shaders/d3d11_genyuv_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_genyuv_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_genyuv_pixel.hlsl:
--------------------------------------------------------------------------------
1 | #include "d3d11_video_pixel_start.hlsli"
2 |
3 | cbuffer CSC_CONST_BUF : register(b1)
4 | {
5 | min16float3x3 cscMatrix;
6 | min16float3 offsets;
7 | };
8 |
9 | #include "d3d11_video_pixel_end.hlsli"
--------------------------------------------------------------------------------
/app/shaders/d3d11_overlay_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_overlay_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_overlay_pixel.hlsl:
--------------------------------------------------------------------------------
1 | Texture2D theTexture : register(t0);
2 | SamplerState theSampler : register(s0);
3 |
4 | struct ShaderInput
5 | {
6 | float4 pos : SV_POSITION;
7 | float2 tex : TEXCOORD0;
8 | };
9 |
10 | min16float4 main(ShaderInput input) : SV_TARGET
11 | {
12 | return theTexture.Sample(theSampler, input.tex);
13 | }
--------------------------------------------------------------------------------
/app/shaders/d3d11_vertex.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_vertex.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_vertex.hlsl:
--------------------------------------------------------------------------------
1 | struct ShaderInput
2 | {
3 | float2 pos : POSITION;
4 | float2 tex : TEXCOORD0;
5 | };
6 |
7 | struct ShaderOutput
8 | {
9 | float4 pos : SV_POSITION;
10 | float2 tex : TEXCOORD0;
11 | };
12 |
13 | ShaderOutput main(ShaderInput input)
14 | {
15 | ShaderOutput output;
16 | output.pos = float4(input.pos, 0.0, 1.0);
17 | output.tex = input.tex;
18 | return output;
19 | }
--------------------------------------------------------------------------------
/app/shaders/d3d11_video_pixel_end.hlsli:
--------------------------------------------------------------------------------
1 | min16float4 main(ShaderInput input) : SV_TARGET
2 | {
3 | // Clamp the chrominance texcoords to avoid sampling the row of texels adjacent to the alignment padding
4 | min16float3 yuv = min16float3(luminancePlane.Sample(theSampler, input.tex),
5 | chrominancePlane.Sample(theSampler, min(input.tex, chromaTexMax.rg)));
6 |
7 | // Subtract the YUV offset for limited vs full range
8 | yuv -= offsets;
9 |
10 | // Multiply by the conversion matrix for this colorspace
11 | yuv = mul(yuv, cscMatrix);
12 |
13 | return min16float4(yuv, 1.0);
14 | }
--------------------------------------------------------------------------------
/app/shaders/d3d11_video_pixel_start.hlsli:
--------------------------------------------------------------------------------
1 | Texture2D luminancePlane : register(t0);
2 | Texture2D chrominancePlane : register(t1);
3 | SamplerState theSampler : register(s0);
4 |
5 | struct ShaderInput
6 | {
7 | float4 pos : SV_POSITION;
8 | float2 tex : TEXCOORD0;
9 | };
10 |
11 | cbuffer ChromaLimitBuf : register(b0)
12 | {
13 | min16float3 chromaTexMax;
14 | };
15 |
--------------------------------------------------------------------------------
/app/shaders/d3d11_y410_pixel.fxc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/app/shaders/d3d11_y410_pixel.fxc
--------------------------------------------------------------------------------
/app/shaders/d3d11_y410_pixel.hlsl:
--------------------------------------------------------------------------------
1 | #include "d3d11_yuv444_pixel_start.hlsli"
2 |
3 | min16float3 swizzle(min16float3 input)
4 | {
5 | // Y410 SRVs are in UYVA order
6 | return input.grb;
7 | }
8 |
9 | #include "d3d11_yuv444_pixel_end.hlsli"
--------------------------------------------------------------------------------
/app/shaders/d3d11_yuv444_pixel_end.hlsli:
--------------------------------------------------------------------------------
1 | min16float4 main(ShaderInput input) : SV_TARGET
2 | {
3 | // Clamp the texcoords to avoid sampling the row of texels adjacent to the alignment padding
4 | min16float3 yuv = swizzle(videoTex.Sample(theSampler, min(input.tex, chromaTexMax.rg)));
5 |
6 | // Subtract the YUV offset for limited vs full range
7 | yuv -= offsets;
8 |
9 | // Multiply by the conversion matrix for this colorspace
10 | yuv = mul(yuv, cscMatrix);
11 |
12 | return min16float4(yuv, 1.0);
13 | }
--------------------------------------------------------------------------------
/app/shaders/d3d11_yuv444_pixel_start.hlsli:
--------------------------------------------------------------------------------
1 | Texture2D videoTex : register(t0);
2 | SamplerState theSampler : register(s0);
3 |
4 | struct ShaderInput
5 | {
6 | float4 pos : SV_POSITION;
7 | float2 tex : TEXCOORD0;
8 | };
9 |
10 | cbuffer ChromaLimitBuf : register(b0)
11 | {
12 | min16float3 chromaTexMax;
13 | };
14 |
15 | cbuffer CSC_CONST_BUF : register(b1)
16 | {
17 | min16float3x3 cscMatrix;
18 | min16float3 offsets;
19 | };
--------------------------------------------------------------------------------
/app/shaders/egl_nv12.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | #extension GL_OES_EGL_image_external : require
3 | precision mediump float;
4 | out vec4 FragColor;
5 |
6 | in vec2 vTextCoord;
7 |
8 | uniform mat3 yuvmat;
9 | uniform vec3 offset;
10 | uniform samplerExternalOES plane1;
11 | uniform samplerExternalOES plane2;
12 |
13 | void main() {
14 | vec3 YCbCr = vec3(
15 | texture2D(plane1, vTextCoord)[0],
16 | texture2D(plane2, vTextCoord).xy
17 | );
18 |
19 | YCbCr -= offset;
20 | FragColor = vec4(clamp(yuvmat * YCbCr, 0.0, 1.0), 1.0f);
21 | }
22 |
--------------------------------------------------------------------------------
/app/shaders/egl_nv12.vert:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | layout (location = 0) in vec2 aPosition; // 2D: X,Y
4 | layout (location = 1) in vec2 aTexCoord;
5 | out vec2 vTextCoord;
6 |
7 | void main() {
8 | vTextCoord = aTexCoord;
9 | gl_Position = vec4(aPosition, 0, 1);
10 | }
11 |
--------------------------------------------------------------------------------
/app/shaders/egl_opaque.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | #extension GL_OES_EGL_image_external : require
3 | precision mediump float;
4 | out vec4 FragColor;
5 |
6 | in vec2 vTextCoord;
7 |
8 | uniform samplerExternalOES uTexture;
9 |
10 | void main() {
11 | FragColor = texture2D(uTexture, vTextCoord);
12 | }
13 |
--------------------------------------------------------------------------------
/app/shaders/egl_opaque.vert:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | layout (location = 0) in vec2 aPosition; // 2D: X,Y
4 | layout (location = 1) in vec2 aTexCoord;
5 | out vec2 vTextCoord;
6 |
7 | void main() {
8 | vTextCoord = aTexCoord;
9 | gl_Position = vec4(aPosition, 0, 1);
10 | }
11 |
--------------------------------------------------------------------------------
/app/shaders/egl_overlay.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | uniform sampler2D uTexture;
3 | varying vec2 vTexCoord;
4 |
5 | void main() {
6 | vec4 abgr = texture2D(uTexture, vTexCoord);
7 |
8 | gl_FragColor = abgr;
9 | gl_FragColor.r = abgr.b;
10 | gl_FragColor.b = abgr.r;
11 | }
12 |
--------------------------------------------------------------------------------
/app/shaders/egl_overlay.vert:
--------------------------------------------------------------------------------
1 | attribute vec2 aPosition; // 2D: X,Y
2 | attribute vec2 aTexCoord;
3 | varying vec2 vTexCoord;
4 |
5 | void main() {
6 | vTexCoord = aTexCoord;
7 | gl_Position = vec4(aPosition, 0, 1);
8 | }
9 |
--------------------------------------------------------------------------------
/app/shaders/vt_renderer.metal:
--------------------------------------------------------------------------------
1 | using namespace metal;
2 |
3 | struct Vertex
4 | {
5 | float4 position [[ position ]];
6 | float2 texCoords;
7 | };
8 |
9 | struct CscParams
10 | {
11 | float3 matrix[3];
12 | float3 offsets;
13 | float bitnessScaleFactor;
14 | };
15 |
16 | constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::linear);
17 |
18 | vertex Vertex vs_draw(constant Vertex *vertices [[ buffer(0) ]], uint id [[ vertex_id ]])
19 | {
20 | return vertices[id];
21 | }
22 |
23 | fragment float4 ps_draw_biplanar(Vertex v [[ stage_in ]],
24 | constant CscParams &cscParams [[ buffer(0) ]],
25 | texture2d luminancePlane [[ texture(0) ]],
26 | texture2d chrominancePlane [[ texture(1) ]])
27 | {
28 | float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r,
29 | chrominancePlane.sample(s, v.texCoords).rg);
30 | yuv *= cscParams.bitnessScaleFactor;
31 | yuv -= cscParams.offsets;
32 |
33 | float3 rgb;
34 | rgb.r = dot(yuv, cscParams.matrix[0]);
35 | rgb.g = dot(yuv, cscParams.matrix[1]);
36 | rgb.b = dot(yuv, cscParams.matrix[2]);
37 | return float4(rgb, 1.0f);
38 | }
39 |
40 | fragment float4 ps_draw_triplanar(Vertex v [[ stage_in ]],
41 | constant CscParams &cscParams [[ buffer(0) ]],
42 | texture2d luminancePlane [[ texture(0) ]],
43 | texture2d chrominancePlaneU [[ texture(1) ]],
44 | texture2d chrominancePlaneV [[ texture(2) ]])
45 | {
46 | float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r,
47 | chrominancePlaneU.sample(s, v.texCoords).r,
48 | chrominancePlaneV.sample(s, v.texCoords).r);
49 | yuv *= cscParams.bitnessScaleFactor;
50 | yuv -= cscParams.offsets;
51 |
52 | float3 rgb;
53 | rgb.r = dot(yuv, cscParams.matrix[0]);
54 | rgb.g = dot(yuv, cscParams.matrix[1]);
55 | rgb.b = dot(yuv, cscParams.matrix[2]);
56 | return float4(rgb, 1.0f);
57 | }
58 |
59 | fragment float4 ps_draw_rgb(Vertex v [[ stage_in ]],
60 | texture2d rgbTexture [[ texture(0) ]])
61 | {
62 | return rgbTexture.sample(s, v.texCoords);
63 | }
64 |
--------------------------------------------------------------------------------
/app/streaming/audio/renderers/renderer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class IAudioRenderer
7 | {
8 | public:
9 | virtual ~IAudioRenderer() {}
10 |
11 | virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig) = 0;
12 |
13 | virtual void* getAudioBuffer(int* size) = 0;
14 |
15 | // Return false if an unrecoverable error has occurred and the renderer must be reinitialized
16 | virtual bool submitAudio(int bytesWritten) = 0;
17 |
18 | virtual int getCapabilities() = 0;
19 |
20 | virtual void remapChannels(POPUS_MULTISTREAM_CONFIGURATION) {
21 | // Use default channel mapping:
22 | // 0 - Front Left
23 | // 1 - Front Right
24 | // 2 - Center
25 | // 3 - LFE
26 | // 4 - Surround Left
27 | // 5 - Surround Right
28 | }
29 |
30 | enum class AudioFormat {
31 | Sint16NE, // 16-bit signed integer (native endian)
32 | Float32NE, // 32-bit floating point (native endian)
33 | };
34 | virtual AudioFormat getAudioBufferFormat() = 0;
35 |
36 | int getAudioBufferSampleSize() {
37 | switch (getAudioBufferFormat()) {
38 | case IAudioRenderer::AudioFormat::Sint16NE:
39 | return sizeof(short);
40 | case IAudioRenderer::AudioFormat::Float32NE:
41 | return sizeof(float);
42 | default:
43 | Q_UNREACHABLE();
44 | }
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/app/streaming/audio/renderers/sdl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 | #include "SDL_compat.h"
5 |
6 | class SdlAudioRenderer : public IAudioRenderer
7 | {
8 | public:
9 | SdlAudioRenderer();
10 |
11 | virtual ~SdlAudioRenderer();
12 |
13 | virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
14 |
15 | virtual void* getAudioBuffer(int* size);
16 |
17 | virtual bool submitAudio(int bytesWritten);
18 |
19 | virtual int getCapabilities();
20 |
21 | virtual AudioFormat getAudioBufferFormat();
22 |
23 | private:
24 | SDL_AudioDeviceID m_AudioDevice;
25 | void* m_AudioBuffer;
26 | int m_FrameSize;
27 | };
28 |
--------------------------------------------------------------------------------
/app/streaming/audio/renderers/slaud.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 | #include
5 |
6 | class SLAudioRenderer : public IAudioRenderer
7 | {
8 | public:
9 | SLAudioRenderer();
10 |
11 | virtual ~SLAudioRenderer();
12 |
13 | virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
14 |
15 | virtual void* getAudioBuffer(int* size);
16 |
17 | virtual bool submitAudio(int bytesWritten);
18 |
19 | virtual int getCapabilities();
20 |
21 | virtual AudioFormat getAudioBufferFormat();
22 |
23 | virtual void remapChannels(POPUS_MULTISTREAM_CONFIGURATION opusConfig);
24 |
25 | private:
26 | static void slLogCallback(void* context, ESLAudioLog logLevel, const char* message);
27 |
28 | CSLAudioContext* m_AudioContext;
29 | CSLAudioStream* m_AudioStream;
30 |
31 | void* m_AudioBuffer;
32 | int m_AudioBufferSize;
33 | int m_MaxQueuedAudioMs;
34 | };
35 |
--------------------------------------------------------------------------------
/app/streaming/audio/renderers/soundioaudiorenderer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #include
6 |
7 | class SoundIoAudioRenderer : public IAudioRenderer
8 | {
9 | public:
10 | SoundIoAudioRenderer();
11 |
12 | ~SoundIoAudioRenderer();
13 |
14 | virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
15 |
16 | virtual void* getAudioBuffer(int* size);
17 |
18 | virtual bool submitAudio(int bytesWritten);
19 |
20 | virtual int getCapabilities();
21 |
22 | virtual AudioFormat getAudioBufferFormat();
23 |
24 | private:
25 | int scoreChannelLayout(const struct SoundIoChannelLayout* layout, const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
26 |
27 | static void sioErrorCallback(struct SoundIoOutStream* stream, int err);
28 |
29 | static void sioWriteCallback(struct SoundIoOutStream* stream, int frameCountMin, int frameCountMax);
30 |
31 | static void sioBackendDisconnect(struct SoundIo* soundio, int err);
32 |
33 | static void sioDevicesChanged(SoundIo* soundio);
34 |
35 | int m_OpusChannelCount;
36 | struct SoundIo* m_SoundIo;
37 | struct SoundIoDevice* m_Device;
38 | struct SoundIoOutStream* m_OutputStream;
39 | struct SoundIoRingBuffer* m_RingBuffer;
40 | struct SoundIoChannelLayout m_EffectiveLayout;
41 | double m_AudioPacketDuration;
42 | double m_Latency;
43 | bool m_Errored;
44 | };
45 |
--------------------------------------------------------------------------------
/app/streaming/streamutils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "SDL_compat.h"
4 |
5 | class StreamUtils
6 | {
7 | public:
8 | static
9 | Uint32 getPlatformWindowFlags();
10 |
11 | static
12 | void scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst);
13 |
14 | static
15 | void screenSpaceToNormalizedDeviceCoords(SDL_FRect* rect, int viewportWidth, int viewportHeight);
16 |
17 | static
18 | void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight);
19 |
20 | static
21 | bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea);
22 |
23 | static
24 | int getDisplayRefreshRate(SDL_Window* window);
25 |
26 | static
27 | bool hasFastAes();
28 |
29 | static
30 | int getDrmFdForWindow(SDL_Window* window, bool* needsClose);
31 |
32 | static
33 | int getDrmFd(bool preferRenderNode);
34 | };
35 |
--------------------------------------------------------------------------------
/app/streaming/video/decoder.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "SDL_compat.h"
5 | #include "settings/streamingpreferences.h"
6 |
7 | #define SDL_CODE_FRAME_READY 0
8 |
9 | #define MAX_SLICES 4
10 |
11 | typedef struct _VIDEO_STATS {
12 | uint32_t receivedFrames;
13 | uint32_t decodedFrames;
14 | uint32_t renderedFrames;
15 | uint32_t totalFrames;
16 | uint32_t networkDroppedFrames;
17 | uint32_t pacerDroppedFrames;
18 | uint16_t minHostProcessingLatency;
19 | uint16_t maxHostProcessingLatency;
20 | uint32_t totalHostProcessingLatency;
21 | uint32_t framesWithHostProcessingLatency;
22 | uint32_t totalReassemblyTime;
23 | uint32_t totalDecodeTime;
24 | uint32_t totalPacerTime;
25 | uint32_t totalRenderTime;
26 | uint32_t lastRtt;
27 | uint32_t lastRttVariance;
28 | float totalFps;
29 | float receivedFps;
30 | float decodedFps;
31 | float renderedFps;
32 | uint32_t measurementStartTimestamp;
33 | } VIDEO_STATS, *PVIDEO_STATS;
34 |
35 | typedef struct _DECODER_PARAMETERS {
36 | SDL_Window* window;
37 | StreamingPreferences::VideoDecoderSelection vds;
38 |
39 | int videoFormat;
40 | int width;
41 | int height;
42 | int frameRate;
43 | bool enableVsync;
44 | bool enableFramePacing;
45 | bool testOnly;
46 | } DECODER_PARAMETERS, *PDECODER_PARAMETERS;
47 |
48 | #define WINDOW_STATE_CHANGE_SIZE 0x01
49 | #define WINDOW_STATE_CHANGE_DISPLAY 0x02
50 |
51 | typedef struct _WINDOW_STATE_CHANGE_INFO {
52 | SDL_Window* window;
53 | uint32_t stateChangeFlags;
54 |
55 | // Populated if WINDOW_STATE_CHANGE_SIZE is set
56 | int width;
57 | int height;
58 |
59 | // Populated if WINDOW_STATE_CHANGE_DISPLAY is set
60 | int displayIndex;
61 | } WINDOW_STATE_CHANGE_INFO, *PWINDOW_STATE_CHANGE_INFO;
62 |
63 | class IVideoDecoder {
64 | public:
65 | virtual ~IVideoDecoder() {}
66 | virtual bool initialize(PDECODER_PARAMETERS params) = 0;
67 | virtual bool isHardwareAccelerated() = 0;
68 | virtual bool isAlwaysFullScreen() = 0;
69 | virtual bool isHdrSupported() = 0;
70 | virtual int getDecoderCapabilities() = 0;
71 | virtual int getDecoderColorspace() = 0;
72 | virtual int getDecoderColorRange() = 0;
73 | virtual QSize getDecoderMaxResolution() = 0;
74 | virtual int submitDecodeUnit(PDECODE_UNIT du) = 0;
75 | virtual void renderFrameOnMainThread() = 0;
76 | virtual void setHdrMode(bool enabled) = 0;
77 | virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) = 0;
78 | };
79 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/cuda.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #include
6 |
7 | extern "C" {
8 | #include
9 | }
10 |
11 | class CUDARenderer : public IFFmpegRenderer {
12 | public:
13 | CUDARenderer();
14 | virtual ~CUDARenderer() override;
15 | virtual bool initialize(PDECODER_PARAMETERS) override;
16 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
17 | virtual void renderFrame(AVFrame* frame) override;
18 | virtual bool needsTestFrame() override;
19 | virtual bool isDirectRenderingSupported() override;
20 | virtual int getDecoderCapabilities() override;
21 |
22 | private:
23 | AVBufferRef* m_HwContext;
24 | };
25 |
26 | #define NV12_PLANES 2
27 |
28 | // Helper class used by SDLRenderer to read our CUDA frame
29 | class CUDAGLInteropHelper {
30 | public:
31 | CUDAGLInteropHelper(AVHWDeviceContext* context);
32 | ~CUDAGLInteropHelper();
33 |
34 | bool registerBoundTextures();
35 | void unregisterTextures();
36 |
37 | bool copyCudaFrameToTextures(AVFrame* frame);
38 |
39 | private:
40 | CudaFunctions* m_Funcs;
41 | AVCUDADeviceContext* m_Context;
42 | CUgraphicsResource m_Resources[NV12_PLANES];
43 | };
44 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/d3d11va.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #include
6 | #include
7 |
8 | extern "C" {
9 | #include
10 | }
11 |
12 | #include
13 |
14 | class D3D11VARenderer : public IFFmpegRenderer
15 | {
16 | public:
17 | D3D11VARenderer(int decoderSelectionPass);
18 | virtual ~D3D11VARenderer() override;
19 | virtual bool initialize(PDECODER_PARAMETERS params) override;
20 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary**) override;
21 | virtual bool prepareDecoderContextInGetFormat(AVCodecContext* context, AVPixelFormat pixelFormat) override;
22 | virtual void renderFrame(AVFrame* frame) override;
23 | virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
24 | virtual int getRendererAttributes() override;
25 | virtual int getDecoderCapabilities() override;
26 | virtual bool needsTestFrame() override;
27 | virtual InitFailureReason getInitFailureReason() override;
28 |
29 | enum PixelShaders {
30 | GENERIC_YUV_420,
31 | BT_601_LIMITED_YUV_420,
32 | BT_2020_LIMITED_YUV_420,
33 | GENERIC_AYUV,
34 | GENERIC_Y410,
35 | _COUNT
36 | };
37 |
38 | private:
39 | static void lockContext(void* lock_ctx);
40 | static void unlockContext(void* lock_ctx);
41 |
42 | bool setupRenderingResources();
43 | std::vector getVideoTextureSRVFormats();
44 | bool setupVideoTexture(); // for !m_BindDecoderOutputTextures
45 | bool setupTexturePoolViews(AVD3D11VAFramesContext* frameContext); // for m_BindDecoderOutputTextures
46 | void renderOverlay(Overlay::OverlayType type);
47 | void bindColorConversion(AVFrame* frame);
48 | void renderVideo(AVFrame* frame);
49 | bool checkDecoderSupport(IDXGIAdapter* adapter);
50 | bool createDeviceByAdapterIndex(int adapterIndex, bool* adapterNotFound = nullptr);
51 |
52 | int m_DecoderSelectionPass;
53 | int m_DevicesWithFL11Support;
54 | int m_DevicesWithCodecSupport;
55 |
56 | enum class SupportedFenceType {
57 | None,
58 | NonMonitored,
59 | Monitored,
60 | };
61 |
62 | Microsoft::WRL::ComPtr m_Factory;
63 | Microsoft::WRL::ComPtr m_Device;
64 | Microsoft::WRL::ComPtr m_SwapChain;
65 | Microsoft::WRL::ComPtr m_DeviceContext;
66 | Microsoft::WRL::ComPtr m_RenderTargetView;
67 | SupportedFenceType m_FenceType;
68 | SDL_mutex* m_ContextLock;
69 | bool m_BindDecoderOutputTextures;
70 | bool m_UseFenceHack;
71 |
72 | DECODER_PARAMETERS m_DecoderParams;
73 | int m_TextureAlignment;
74 | DXGI_FORMAT m_TextureFormat;
75 | int m_DisplayWidth;
76 | int m_DisplayHeight;
77 | int m_LastColorSpace;
78 | bool m_LastFullRange;
79 | AVColorTransferCharacteristic m_LastColorTrc;
80 |
81 | bool m_AllowTearing;
82 |
83 | std::array, PixelShaders::_COUNT> m_VideoPixelShaders;
84 | Microsoft::WRL::ComPtr m_VideoVertexBuffer;
85 |
86 | // Only valid if !m_BindDecoderOutputTextures
87 | Microsoft::WRL::ComPtr m_VideoTexture;
88 |
89 | // Only index 0 is valid if !m_BindDecoderOutputTextures
90 | #define DECODER_BUFFER_POOL_SIZE 17
91 | std::array, 2>, DECODER_BUFFER_POOL_SIZE> m_VideoTextureResourceViews;
92 |
93 | SDL_SpinLock m_OverlayLock;
94 | std::array, Overlay::OverlayMax> m_OverlayVertexBuffers;
95 | std::array, Overlay::OverlayMax> m_OverlayTextures;
96 | std::array, Overlay::OverlayMax> m_OverlayTextureResourceViews;
97 | Microsoft::WRL::ComPtr m_OverlayPixelShader;
98 |
99 | AVBufferRef* m_HwDeviceContext;
100 | AVBufferRef* m_HwFramesContext;
101 | };
102 |
103 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/drm.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 | #include "swframemapper.h"
5 |
6 | #ifdef HAVE_EGL
7 | #include "eglimagefactory.h"
8 | #endif
9 |
10 | #include
11 | #include
12 |
13 | #include
14 |
15 | // Newer libdrm headers have these HDR structs, but some older ones don't.
16 | namespace DrmDefs
17 | {
18 | // HDR structs is copied from linux include/linux/hdmi.h
19 | struct hdr_metadata_infoframe
20 | {
21 | uint8_t eotf;
22 | uint8_t metadata_type;
23 |
24 | struct
25 | {
26 | uint16_t x, y;
27 | } display_primaries[3];
28 |
29 | struct
30 | {
31 | uint16_t x, y;
32 | } white_point;
33 |
34 | uint16_t max_display_mastering_luminance;
35 | uint16_t min_display_mastering_luminance;
36 |
37 | uint16_t max_cll;
38 | uint16_t max_fall;
39 | };
40 |
41 | struct hdr_output_metadata
42 | {
43 | uint32_t metadata_type;
44 |
45 | union {
46 | struct hdr_metadata_infoframe hdmi_metadata_type1;
47 | };
48 | };
49 | }
50 |
51 | class DrmRenderer : public IFFmpegRenderer {
52 | public:
53 | DrmRenderer(AVHWDeviceType hwDeviceType = AV_HWDEVICE_TYPE_NONE, IFFmpegRenderer *backendRenderer = nullptr);
54 | virtual ~DrmRenderer() override;
55 | virtual bool initialize(PDECODER_PARAMETERS params) override;
56 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
57 | virtual void prepareToRender() override;
58 | virtual void renderFrame(AVFrame* frame) override;
59 | virtual enum AVPixelFormat getPreferredPixelFormat(int videoFormat) override;
60 | virtual bool isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) override;
61 | virtual int getRendererAttributes() override;
62 | virtual bool needsTestFrame() override;
63 | virtual bool testRenderFrame(AVFrame* frame) override;
64 | virtual bool isDirectRenderingSupported() override;
65 | virtual int getDecoderColorspace() override;
66 | virtual void setHdrMode(bool enabled) override;
67 | #ifdef HAVE_EGL
68 | virtual bool canExportEGL() override;
69 | virtual AVPixelFormat getEGLImagePixelFormat() override;
70 | virtual bool initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) override;
71 | virtual ssize_t exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) override;
72 | virtual void freeEGLImages(EGLDisplay dpy, EGLImage[EGL_MAX_PLANES]) override;
73 | #endif
74 |
75 | private:
76 | bool getPropertyByName(drmModeObjectPropertiesPtr props, const char* name, uint64_t *value);
77 | const char* getDrmColorEncodingValue(AVFrame* frame);
78 | const char* getDrmColorRangeValue(AVFrame* frame);
79 | bool mapSoftwareFrame(AVFrame* frame, AVDRMFrameDescriptor* mappedFrame);
80 | bool addFbForFrame(AVFrame* frame, uint32_t* newFbId, bool testMode);
81 | static bool drmFormatMatchesVideoFormat(uint32_t drmFormat, int videoFormat);
82 |
83 | IFFmpegRenderer* m_BackendRenderer;
84 | SDL_Window* m_Window;
85 | bool m_DrmPrimeBackend;
86 | AVHWDeviceType m_HwDeviceType;
87 | AVBufferRef* m_HwContext;
88 | int m_DrmFd;
89 | bool m_DrmIsMaster;
90 | bool m_MustCloseDrmFd;
91 | bool m_SupportsDirectRendering;
92 | int m_VideoFormat;
93 | uint32_t m_ConnectorId;
94 | uint32_t m_EncoderId;
95 | uint32_t m_CrtcId;
96 | uint32_t m_PlaneId;
97 | uint32_t m_CurrentFbId;
98 | bool m_LastFullRange;
99 | int m_LastColorSpace;
100 | drmModePlanePtr m_Plane;
101 | drmModePropertyPtr m_ColorEncodingProp;
102 | drmModePropertyPtr m_ColorRangeProp;
103 | drmModePropertyPtr m_HdrOutputMetadataProp;
104 | drmModePropertyPtr m_ColorspaceProp;
105 | drmVersionPtr m_Version;
106 | uint32_t m_HdrOutputMetadataBlobId;
107 | SDL_Rect m_OutputRect;
108 | std::set m_SupportedPlaneFormats;
109 |
110 | static constexpr int k_SwFrameCount = 2;
111 | SwFrameMapper m_SwFrameMapper;
112 | int m_CurrentSwFrameIdx;
113 | struct {
114 | uint32_t handle;
115 | uint32_t pitch;
116 | uint64_t size;
117 | uint8_t* mapping;
118 | int primeFd;
119 | } m_SwFrame[k_SwFrameCount];
120 |
121 | #ifdef HAVE_EGL
122 | EglImageFactory m_EglImageFactory;
123 | #endif
124 | };
125 |
126 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/dxutil.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class DXUtil
6 | {
7 | public:
8 | static bool isFormatHybridDecodedByHardware(int videoFormat, unsigned int vendorId, unsigned int deviceId) {
9 | if (vendorId == 0x8086) {
10 | // Intel seems to encode the series in the high byte of
11 | // the device ID. We want to avoid the "Partial" acceleration
12 | // support explicitly. Those will claim to have HW acceleration
13 | // but perform badly.
14 | // https://en.wikipedia.org/wiki/Intel_Graphics_Technology#Capabilities_(GPU_video_acceleration)
15 | // https://raw.githubusercontent.com/GameTechDev/gpudetect/master/IntelGfx.cfg
16 | switch (deviceId & 0xFF00) {
17 | case 0x0400: // Haswell
18 | case 0x0A00: // Haswell
19 | case 0x0D00: // Haswell
20 | case 0x1600: // Broadwell
21 | case 0x2200: // Cherry Trail and Braswell
22 | // Block these for HEVC to avoid hybrid decode
23 | return (videoFormat & VIDEO_FORMAT_MASK_H265) != 0;
24 | case 0x1900: // Skylake
25 | // Blacklist these for HEVC Main10 to avoid hybrid decode.
26 | // Regular HEVC Main is fine though.
27 | if (videoFormat == VIDEO_FORMAT_H265_MAIN10) {
28 | return true;
29 | }
30 | default:
31 | break;
32 | }
33 | }
34 | else if (vendorId == 0x10DE) {
35 | // For NVIDIA, we wait to avoid those GPUs with Feature Set E
36 | // for HEVC decoding, since that's hybrid. It appears that Kepler GPUs
37 | // also had some hybrid decode support (per DXVA2 Checker) so we'll
38 | // blacklist those too.
39 | // https://en.wikipedia.org/wiki/Nvidia_PureVideo
40 | // https://bluesky23.yukishigure.com/en/dxvac/deviceInfo/decoder.html
41 | // http://envytools.readthedocs.io/en/latest/hw/pciid.html (missing GM200)
42 | if ((deviceId >= 0x1180 && deviceId <= 0x11BF) || // GK104
43 | (deviceId >= 0x11C0 && deviceId <= 0x11FF) || // GK106
44 | (deviceId >= 0x0FC0 && deviceId <= 0x0FFF) || // GK107
45 | (deviceId >= 0x1000 && deviceId <= 0x103F) || // GK110/GK110B
46 | (deviceId >= 0x1280 && deviceId <= 0x12BF) || // GK208
47 | (deviceId >= 0x1340 && deviceId <= 0x137F) || // GM108
48 | (deviceId >= 0x1380 && deviceId <= 0x13BF) || // GM107
49 | (deviceId >= 0x13C0 && deviceId <= 0x13FF) || // GM204
50 | (deviceId >= 0x1617 && deviceId <= 0x161A) || // GM204
51 | (deviceId == 0x1667) || // GM204
52 | (deviceId >= 0x17C0 && deviceId <= 0x17FF)) { // GM200
53 | // Avoid HEVC on Feature Set E GPUs
54 | return (videoFormat & VIDEO_FORMAT_MASK_H265) != 0;
55 | }
56 | }
57 |
58 | return false;
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/dxva2.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 | #include "pacer/pacer.h"
5 |
6 | #include
7 | #include
8 |
9 | extern "C" {
10 | #include
11 | }
12 |
13 | #include
14 |
15 | class DXVA2Renderer : public IFFmpegRenderer
16 | {
17 | public:
18 | DXVA2Renderer(int decoderSelectionPass);
19 | virtual ~DXVA2Renderer() override;
20 | virtual bool initialize(PDECODER_PARAMETERS params) override;
21 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
22 | virtual void renderFrame(AVFrame* frame) override;
23 | virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
24 | virtual int getDecoderColorspace() override;
25 | virtual int getDecoderCapabilities() override;
26 |
27 | private:
28 | bool initializeDecoder();
29 | bool initializeRenderer();
30 | bool initializeDevice(SDL_Window* window, bool enableVsync);
31 | bool isDecoderBlacklisted();
32 | bool initializeQuirksForAdapter(IDirect3D9Ex* d3d9ex, int adapterIndex);
33 | void renderOverlay(Overlay::OverlayType type);
34 |
35 | #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 68, 0)
36 | #define FF_POOL_SIZE_TYPE size_t
37 | #else
38 | #define FF_POOL_SIZE_TYPE int
39 | #endif
40 |
41 | static
42 | AVBufferRef* ffPoolAlloc(void* opaque, FF_POOL_SIZE_TYPE size);
43 |
44 | static
45 | void ffPoolDummyDelete(void*, uint8_t*);
46 |
47 | static
48 | int ffGetBuffer2(AVCodecContext* context, AVFrame* frame, int flags);
49 |
50 | int m_DecoderSelectionPass;
51 |
52 | int m_VideoFormat;
53 | int m_VideoWidth;
54 | int m_VideoHeight;
55 |
56 | int m_DisplayWidth;
57 | int m_DisplayHeight;
58 |
59 | struct dxva_context m_DXVAContext;
60 | std::array, 19> m_DecSurfaces;
61 | std::array m_DecSurfacesRaw; // Referenced by m_DecSurfaces
62 | DXVA2_ConfigPictureDecode m_Config;
63 | Microsoft::WRL::ComPtr m_DecService;
64 | Microsoft::WRL::ComPtr m_Decoder;
65 | int m_SurfacesUsed;
66 | AVBufferPool* m_Pool;
67 |
68 | SDL_SpinLock m_OverlayLock;
69 | std::array, Overlay::OverlayMax> m_OverlayVertexBuffers;
70 | std::array, Overlay::OverlayMax> m_OverlayTextures;
71 |
72 | Microsoft::WRL::ComPtr m_Device;
73 | Microsoft::WRL::ComPtr m_RenderTarget;
74 | Microsoft::WRL::ComPtr m_ProcService;
75 | Microsoft::WRL::ComPtr m_Processor;
76 | DXVA2_ValueRange m_BrightnessRange;
77 | DXVA2_ValueRange m_ContrastRange;
78 | DXVA2_ValueRange m_HueRange;
79 | DXVA2_ValueRange m_SaturationRange;
80 | DXVA2_VideoDesc m_Desc;
81 | REFERENCE_TIME m_FrameIndex;
82 | bool m_BlockingPresent;
83 |
84 | #define DXVA2_QUIRK_NO_VP 0x01
85 | #define DXVA2_QUIRK_SET_DEST_FORMAT 0x02
86 | #define DXVA2_QUIRK_WDDM_20_PLUS 0x04 // Unused
87 | #define DXVA2_QUIRK_MULTI_GPU 0x08
88 | int m_DeviceQuirks;
89 | };
90 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/egl_extensions.cpp:
--------------------------------------------------------------------------------
1 | #include "renderer.h"
2 |
3 | static QStringList egl_get_extensions(EGLDisplay dpy) {
4 | const auto EGLExtensionsStr = eglQueryString(dpy, EGL_EXTENSIONS);
5 | if (!EGLExtensionsStr) {
6 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unable to get EGL extensions: %d", eglGetError());
7 | return QStringList();
8 | }
9 | return QString(EGLExtensionsStr).split(" ");
10 | }
11 |
12 | EGLExtensions::EGLExtensions(EGLDisplay dpy) :
13 | m_Extensions(egl_get_extensions(dpy))
14 | {}
15 |
16 | bool EGLExtensions::isSupported(const QString &extension) const {
17 | return m_Extensions.contains(extension);
18 | }
19 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/eglimagefactory.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #ifdef HAVE_LIBVA
6 | #include
7 | #endif
8 |
9 | class EglImageFactory
10 | {
11 | public:
12 | EglImageFactory(IFFmpegRenderer* renderer);
13 | bool initializeEGL(EGLDisplay, const EGLExtensions &ext);
14 |
15 | #ifdef HAVE_DRM
16 | ssize_t exportDRMImages(AVFrame* frame, AVDRMFrameDescriptor* drmFrame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]);
17 | #endif
18 |
19 | #ifdef HAVE_LIBVA
20 | ssize_t exportVAImages(AVFrame* frame, VADRMPRIMESurfaceDescriptor* vaFrame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]);
21 | #endif
22 |
23 | bool supportsImportingFormat(EGLDisplay dpy, EGLint format);
24 | bool supportsImportingModifier(EGLDisplay dpy, EGLint format, EGLuint64KHR modifier);
25 |
26 | void freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]);
27 |
28 | private:
29 | IFFmpegRenderer* m_Renderer;
30 | bool m_EGLExtDmaBuf;
31 | PFNEGLCREATEIMAGEPROC m_eglCreateImage;
32 | PFNEGLDESTROYIMAGEPROC m_eglDestroyImage;
33 | PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR;
34 | PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR;
35 | PFNEGLQUERYDMABUFFORMATSEXTPROC m_eglQueryDmaBufFormatsEXT;
36 | PFNEGLQUERYDMABUFMODIFIERSEXTPROC m_eglQueryDmaBufModifiersEXT;
37 | };
38 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/eglvid.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #define SDL_USE_BUILTIN_OPENGL_DEFINITIONS 1
6 | #include
7 | #include
8 |
9 | class EGLRenderer : public IFFmpegRenderer {
10 | public:
11 | EGLRenderer(IFFmpegRenderer *backendRenderer);
12 | virtual ~EGLRenderer() override;
13 | virtual bool initialize(PDECODER_PARAMETERS params) override;
14 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
15 | virtual void cleanupRenderContext() override;
16 | virtual void waitToRender() override;
17 | virtual void prepareToRender() override;
18 | virtual void renderFrame(AVFrame* frame) override;
19 | virtual bool testRenderFrame(AVFrame* frame) override;
20 | virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
21 | virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
22 | virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
23 | virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override;
24 |
25 | private:
26 |
27 | void renderOverlay(Overlay::OverlayType type, int viewportWidth, int viewportHeight);
28 | unsigned compileShader(const char* vertexShaderSrc, const char* fragmentShaderSrc);
29 | bool compileShaders();
30 | bool specialize();
31 | const float *getColorOffsets(const AVFrame* frame);
32 | const float *getColorMatrix(const AVFrame* frame);
33 | static int loadAndBuildShader(int shaderType, const char *filename);
34 |
35 | AVPixelFormat m_EGLImagePixelFormat;
36 | void *m_EGLDisplay;
37 | unsigned m_Textures[EGL_MAX_PLANES];
38 | unsigned m_OverlayTextures[Overlay::OverlayMax];
39 | unsigned m_OverlayVbos[Overlay::OverlayMax];
40 | SDL_atomic_t m_OverlayHasValidData[Overlay::OverlayMax];
41 | unsigned m_ShaderProgram;
42 | unsigned m_OverlayShaderProgram;
43 | SDL_GLContext m_Context;
44 | SDL_Window *m_Window;
45 | IFFmpegRenderer *m_Backend;
46 | unsigned int m_VAO;
47 | bool m_BlockingSwapBuffers;
48 | EGLSync m_LastRenderSync;
49 | AVFrame* m_LastFrame;
50 | PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
51 | PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
52 | PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
53 | PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
54 | PFNEGLCREATESYNCPROC m_eglCreateSync;
55 | PFNEGLCREATESYNCKHRPROC m_eglCreateSyncKHR;
56 | PFNEGLDESTROYSYNCPROC m_eglDestroySync;
57 | PFNEGLCLIENTWAITSYNCPROC m_eglClientWaitSync;
58 | int m_GlesMajorVersion;
59 | int m_GlesMinorVersion;
60 | bool m_HasExtUnpackSubimage;
61 |
62 | #define NV12_PARAM_YUVMAT 0
63 | #define NV12_PARAM_OFFSET 1
64 | #define NV12_PARAM_PLANE1 2
65 | #define NV12_PARAM_PLANE2 3
66 | #define OPAQUE_PARAM_TEXTURE 0
67 | int m_ShaderProgramParams[4];
68 |
69 | #define OVERLAY_PARAM_TEXTURE 0
70 | int m_OverlayShaderProgramParams[1];
71 |
72 | int m_OldContextProfileMask;
73 | int m_OldContextMajorVersion;
74 | int m_OldContextMinorVersion;
75 |
76 | SDL_Renderer *m_DummyRenderer;
77 | };
78 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/genhwaccel.cpp:
--------------------------------------------------------------------------------
1 | #include "genhwaccel.h"
2 |
3 | GenericHwAccelRenderer::GenericHwAccelRenderer(AVHWDeviceType hwDeviceType)
4 | : IFFmpegRenderer(RendererType::Unknown),
5 | m_HwDeviceType(hwDeviceType),
6 | m_HwContext(nullptr)
7 | {
8 |
9 | }
10 |
11 | GenericHwAccelRenderer::~GenericHwAccelRenderer()
12 | {
13 | if (m_HwContext != nullptr) {
14 | av_buffer_unref(&m_HwContext);
15 | }
16 | }
17 |
18 | bool GenericHwAccelRenderer::initialize(PDECODER_PARAMETERS)
19 | {
20 | int err;
21 |
22 | err = av_hwdevice_ctx_create(&m_HwContext, m_HwDeviceType, nullptr, nullptr, 0);
23 | if (err != 0) {
24 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
25 | "av_hwdevice_ctx_create(%u) failed: %d",
26 | m_HwDeviceType,
27 | err);
28 | return false;
29 | }
30 |
31 | return true;
32 | }
33 |
34 | bool GenericHwAccelRenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary**)
35 | {
36 | context->hw_device_ctx = av_buffer_ref(m_HwContext);
37 |
38 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
39 | "Using generic FFmpeg hwaccel backend (type: %u). Performance may not be optimal!",
40 | m_HwDeviceType);
41 |
42 | return true;
43 | }
44 |
45 | void GenericHwAccelRenderer::renderFrame(AVFrame*)
46 | {
47 | // We only support indirect rendering
48 | SDL_assert(false);
49 | }
50 |
51 | bool GenericHwAccelRenderer::needsTestFrame()
52 | {
53 | return true;
54 | }
55 |
56 | bool GenericHwAccelRenderer::isDirectRenderingSupported()
57 | {
58 | // We only support rendering via read-back
59 | return false;
60 | }
61 |
62 | int GenericHwAccelRenderer::getDecoderCapabilities()
63 | {
64 | bool ok;
65 | int caps = qEnvironmentVariableIntValue("GENHWACCEL_CAPS", &ok);
66 | if (ok) {
67 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
68 | "Using GENHWACCEL_CAPS for decoder capabilities: %x",
69 | caps);
70 | }
71 | else {
72 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
73 | "Assuming default decoder capabilities. Set GENHWACCEL_CAPS to override.");
74 | caps = 0;
75 | }
76 |
77 | return caps;
78 | }
79 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/genhwaccel.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | class GenericHwAccelRenderer : public IFFmpegRenderer
6 | {
7 | public:
8 | GenericHwAccelRenderer(AVHWDeviceType hwDeviceType);
9 | virtual ~GenericHwAccelRenderer() override;
10 | virtual bool initialize(PDECODER_PARAMETERS) override;
11 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
12 | virtual void renderFrame(AVFrame* frame) override;
13 | virtual bool needsTestFrame() override;
14 | virtual bool isDirectRenderingSupported() override;
15 | virtual int getDecoderCapabilities() override;
16 |
17 | private:
18 | AVHWDeviceType m_HwDeviceType;
19 | AVBufferRef* m_HwContext;
20 | };
21 |
22 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/mmal.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | class MmalRenderer : public IFFmpegRenderer {
11 | public:
12 | MmalRenderer();
13 | virtual ~MmalRenderer() override;
14 | virtual bool initialize(PDECODER_PARAMETERS params) override;
15 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
16 | virtual void prepareToRender() override;
17 | virtual void renderFrame(AVFrame* frame) override;
18 | virtual enum AVPixelFormat getPreferredPixelFormat(int videoFormat) override;
19 | virtual bool needsTestFrame() override;
20 | virtual int getRendererAttributes() override;
21 | virtual int getDecoderColorspace() override;
22 |
23 | private:
24 | static void InputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer);
25 | bool getDtDeviceStatus(QString name, bool ifUnknown);
26 | bool isMmalOverlaySupported();
27 | void updateDisplayRegion();
28 |
29 | MMAL_COMPONENT_T* m_Renderer;
30 | MMAL_PORT_T* m_InputPort;
31 |
32 | SDL_Renderer* m_BackgroundRenderer;
33 | SDL_Window* m_Window;
34 | int m_VideoWidth, m_VideoHeight;
35 | int m_LastWindowPosX, m_LastWindowPosY;
36 | };
37 |
38 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/pacer/dxvsyncsource.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pacer.h"
4 |
5 | #include
6 |
7 | // from
8 | typedef LONG NTSTATUS;
9 | typedef UINT D3DKMT_HANDLE;
10 | typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
11 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
12 | #define STATUS_GRAPHICS_PRESENT_OCCLUDED ((NTSTATUS)0xC01E0006L)
13 | typedef struct _D3DKMT_OPENADAPTERFROMHDC {
14 | HDC hDc;
15 | D3DKMT_HANDLE hAdapter;
16 | LUID AdapterLuid;
17 | D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
18 | } D3DKMT_OPENADAPTERFROMHDC;
19 | typedef struct _D3DKMT_CLOSEADAPTER {
20 | D3DKMT_HANDLE hAdapter;
21 | } D3DKMT_CLOSEADAPTER;
22 | typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT {
23 | D3DKMT_HANDLE hAdapter;
24 | D3DKMT_HANDLE hDevice;
25 | D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
26 | } D3DKMT_WAITFORVERTICALBLANKEVENT;
27 | typedef NTSTATUS(APIENTRY* PFND3DKMTOPENADAPTERFROMHDC)(D3DKMT_OPENADAPTERFROMHDC*);
28 | typedef NTSTATUS(APIENTRY* PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER*);
29 | typedef NTSTATUS(APIENTRY* PFND3DKMTWAITFORVERTICALBLANKEVENT)(D3DKMT_WAITFORVERTICALBLANKEVENT*);
30 |
31 | class DxVsyncSource : public IVsyncSource
32 | {
33 | public:
34 | DxVsyncSource(Pacer* pacer);
35 |
36 | virtual ~DxVsyncSource();
37 |
38 | virtual bool initialize(SDL_Window* window, int) override;
39 |
40 | virtual bool isAsync() override;
41 |
42 | virtual void waitForVsync() override;
43 |
44 | private:
45 | Pacer* m_Pacer;
46 | HMODULE m_Gdi32Handle;
47 | HWND m_Window;
48 | HMONITOR m_LastMonitor;
49 | D3DKMT_WAITFORVERTICALBLANKEVENT m_WaitForVblankEventParams;
50 |
51 | PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
52 | PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
53 | PFND3DKMTWAITFORVERTICALBLANKEVENT m_D3DKMTWaitForVerticalBlankEvent;
54 | };
55 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/pacer/pacer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../../decoder.h"
4 | #include "../renderer.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class IVsyncSource {
11 | public:
12 | virtual ~IVsyncSource() {}
13 | virtual bool initialize(SDL_Window* window, int displayFps) = 0;
14 |
15 | // Asynchronous sources produce callbacks on their own, while synchronous
16 | // sources require calls to waitForVsync().
17 | virtual bool isAsync() = 0;
18 |
19 | virtual void waitForVsync() {
20 | // Synchronous sources must implement waitForVsync()!
21 | SDL_assert(false);
22 | }
23 | };
24 |
25 | class Pacer
26 | {
27 | public:
28 | Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats);
29 |
30 | ~Pacer();
31 |
32 | void submitFrame(AVFrame* frame);
33 |
34 | bool initialize(SDL_Window* window, int maxVideoFps, bool enablePacing);
35 |
36 | void signalVsync();
37 |
38 | void renderOnMainThread();
39 |
40 | private:
41 | static int vsyncThread(void* context);
42 |
43 | static int renderThread(void* context);
44 |
45 | void handleVsync(int timeUntilNextVsyncMillis);
46 |
47 | void enqueueFrameForRenderingAndUnlock(AVFrame* frame);
48 |
49 | void renderFrame(AVFrame* frame);
50 |
51 | void dropFrameForEnqueue(QQueue& queue);
52 |
53 | QQueue m_RenderQueue;
54 | QQueue m_PacingQueue;
55 | QQueue m_PacingQueueHistory;
56 | QQueue m_RenderQueueHistory;
57 | QMutex m_FrameQueueLock;
58 | QWaitCondition m_RenderQueueNotEmpty;
59 | QWaitCondition m_PacingQueueNotEmpty;
60 | QWaitCondition m_VsyncSignalled;
61 | SDL_Thread* m_RenderThread;
62 | SDL_Thread* m_VsyncThread;
63 | bool m_Stopping;
64 |
65 | IVsyncSource* m_VsyncSource;
66 | IFFmpegRenderer* m_VsyncRenderer;
67 | int m_MaxVideoFps;
68 | int m_DisplayFps;
69 | PVIDEO_STATS m_VideoStats;
70 | int m_RendererAttributes;
71 | };
72 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/pacer/waylandvsyncsource.cpp:
--------------------------------------------------------------------------------
1 | #include "waylandvsyncsource.h"
2 |
3 | #include
4 |
5 | #ifndef SDL_VIDEO_DRIVER_WAYLAND
6 | #warning Unable to use WaylandVsyncSource without SDL support
7 | #else
8 |
9 | const struct wl_callback_listener WaylandVsyncSource::s_FrameListener = {
10 | .done = WaylandVsyncSource::frameDone,
11 | };
12 |
13 | WaylandVsyncSource::WaylandVsyncSource(Pacer* pacer)
14 | : m_Pacer(pacer),
15 | m_Display(nullptr),
16 | m_Surface(nullptr),
17 | m_Callback(nullptr)
18 | {
19 |
20 | }
21 |
22 | WaylandVsyncSource::~WaylandVsyncSource()
23 | {
24 | if (m_Callback != nullptr) {
25 | wl_callback_destroy(m_Callback);
26 | wl_display_roundtrip(m_Display);
27 | }
28 | }
29 |
30 | bool WaylandVsyncSource::initialize(SDL_Window* window, int)
31 | {
32 | SDL_SysWMinfo info;
33 |
34 | SDL_VERSION(&info.version);
35 |
36 | if (!SDL_GetWindowWMInfo(window, &info)) {
37 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
38 | "SDL_GetWindowWMInfo() failed: %s",
39 | SDL_GetError());
40 | return false;
41 | }
42 |
43 | // Pacer should not create us for non-Wayland windows
44 | SDL_assert(info.subsystem == SDL_SYSWM_WAYLAND);
45 |
46 | m_Display = info.info.wl.display;
47 | m_Surface = info.info.wl.surface;
48 |
49 | // Enqueue our first frame callback
50 | m_Callback = wl_surface_frame(m_Surface);
51 | wl_callback_add_listener(m_Callback, &s_FrameListener, this);
52 | wl_surface_commit(m_Surface);
53 |
54 | return true;
55 | }
56 |
57 | bool WaylandVsyncSource::isAsync()
58 | {
59 | // Wayland frame callbacks are asynchronous
60 | return true;
61 | }
62 |
63 | void WaylandVsyncSource::frameDone(void* data, struct wl_callback* oldCb, uint32_t)
64 | {
65 | auto me = (WaylandVsyncSource*)data;
66 |
67 | // Free this callback
68 | SDL_assert(oldCb == me->m_Callback);
69 | wl_callback_destroy(oldCb);
70 |
71 | // Wake the Pacer Vsync thread
72 | me->m_Pacer->signalVsync();
73 |
74 | // Register for another callback
75 | me->m_Callback = wl_surface_frame(me->m_Surface);
76 | wl_callback_add_listener(me->m_Callback, &s_FrameListener, data);
77 | wl_surface_commit(me->m_Surface);
78 | wl_display_flush(me->m_Display);
79 | }
80 |
81 | #endif
82 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/pacer/waylandvsyncsource.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pacer.h"
4 |
5 | #include
6 | #include
7 |
8 | class WaylandVsyncSource : public IVsyncSource
9 | {
10 | public:
11 | WaylandVsyncSource(Pacer* pacer);
12 |
13 | virtual ~WaylandVsyncSource();
14 |
15 | virtual bool initialize(SDL_Window* window, int displayFps) override;
16 |
17 | virtual bool isAsync() override;
18 |
19 | private:
20 | static void frameDone(void* data, struct wl_callback* oldCb, uint32_t time);
21 |
22 | static const struct wl_callback_listener s_FrameListener;
23 |
24 | Pacer* m_Pacer;
25 | wl_display* m_Display;
26 | wl_surface* m_Surface;
27 | wl_callback* m_Callback;
28 | };
29 |
30 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/plvk_c.c:
--------------------------------------------------------------------------------
1 | // This compilation unit contains the implementations of libplacebo header-only libraries.
2 | // These must be compiled as C code, so they cannot be placed inside plvk.cpp.
3 |
4 | #ifdef _MSC_VER
5 | #pragma warning(push)
6 | #pragma warning(disable: 4068) // unknown pragma
7 | #pragma warning(disable: 4244) // double -> float truncation warning
8 | #pragma warning(disable: 4267) // size_t -> int truncation warning
9 | #endif
10 |
11 | #define PL_LIBAV_IMPLEMENTATION 1
12 | #include
13 |
14 | #ifdef _MSC_VER
15 | #pragma warning(pop)
16 | #endif
17 |
18 | // Provide a dummy implementation of av_stream_get_side_data() to avoid having to link with libavformat
19 | uint8_t *av_stream_get_side_data(const AVStream *stream, enum AVPacketSideDataType type, size_t *size)
20 | {
21 | (void)stream;
22 | (void)type;
23 | (void)size;
24 | return NULL;
25 | }
26 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/sdlvid.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 | #include "swframemapper.h"
5 |
6 | #ifdef HAVE_CUDA
7 | #include "cuda.h"
8 | #endif
9 |
10 | extern "C" {
11 | #include
12 | }
13 |
14 | class SdlRenderer : public IFFmpegRenderer {
15 | public:
16 | SdlRenderer();
17 | virtual ~SdlRenderer() override;
18 | virtual bool initialize(PDECODER_PARAMETERS params) override;
19 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
20 | virtual void prepareToRender() override;
21 | virtual void renderFrame(AVFrame* frame) override;
22 | virtual bool isRenderThreadSupported() override;
23 | virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
24 | virtual bool testRenderFrame(AVFrame* frame) override;
25 | virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
26 |
27 | private:
28 | void renderOverlay(Overlay::OverlayType type);
29 |
30 | static void ffNoopFree(void *opaque, uint8_t *data);
31 |
32 | int m_VideoFormat;
33 | SDL_Renderer* m_Renderer;
34 | SDL_Texture* m_Texture;
35 | int m_ColorSpace;
36 | SDL_Texture* m_OverlayTextures[Overlay::OverlayMax];
37 | SDL_Rect m_OverlayRects[Overlay::OverlayMax];
38 |
39 | // Used for CPU conversion of YUV to RGB if needed
40 | bool m_NeedsYuvToRgbConversion;
41 | SwsContext* m_SwsContext;
42 | AVFrame* m_RgbFrame;
43 |
44 | SwFrameMapper m_SwFrameMapper;
45 |
46 | #ifdef HAVE_CUDA
47 | CUDAGLInteropHelper* m_CudaGLHelper;
48 | #endif
49 | };
50 |
51 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/swframemapper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | class SwFrameMapper
6 | {
7 | public:
8 | explicit SwFrameMapper(IFFmpegRenderer* renderer);
9 | void setVideoFormat(int videoFormat);
10 | AVFrame* getSwFrameFromHwFrame(AVFrame* hwFrame);
11 |
12 | private:
13 | bool initializeReadBackFormat(AVBufferRef* hwFrameCtxRef, AVFrame* testFrame);
14 |
15 | IFFmpegRenderer* m_Renderer;
16 | int m_VideoFormat;
17 | enum AVPixelFormat m_SwPixelFormat;
18 | bool m_MapFrame;
19 | };
20 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/vdpau.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | extern "C" {
6 | #include
7 | #include
8 | #include
9 | }
10 |
11 | class VDPAURenderer : public IFFmpegRenderer
12 | {
13 | public:
14 | VDPAURenderer(int decoderSelectionPass);
15 | virtual ~VDPAURenderer() override;
16 | virtual bool initialize(PDECODER_PARAMETERS params) override;
17 | virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
18 | virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
19 | virtual void waitToRender() override;
20 | virtual void renderFrame(AVFrame* frame) override;
21 | virtual bool needsTestFrame() override;
22 | virtual int getDecoderColorspace() override;
23 | virtual int getDecoderCapabilities() override;
24 |
25 | private:
26 | void renderOverlay(VdpOutputSurface destination, Overlay::OverlayType type);
27 |
28 | int m_DecoderSelectionPass;
29 | uint32_t m_VideoWidth, m_VideoHeight;
30 | uint32_t m_DisplayWidth, m_DisplayHeight;
31 | AVBufferRef* m_HwContext;
32 | VdpPresentationQueueTarget m_PresentationQueueTarget;
33 | VdpPresentationQueue m_PresentationQueue;
34 | VdpVideoMixer m_VideoMixer;
35 | VdpRGBAFormat m_OutputSurfaceFormat;
36 | VdpDevice m_Device;
37 |
38 | // We just have a single mutex to protect all overlay slots.
39 | // This is fine because the majority of time spent in the mutex
40 | // is by the render thread, which cannot contend with itself
41 | // because overlays are rendered sequentially.
42 | SDL_mutex* m_OverlayMutex;
43 | VdpBitmapSurface m_OverlaySurface[Overlay::OverlayMax];
44 | VdpRect m_OverlayRect[Overlay::OverlayMax];
45 | VdpOutputSurfaceRenderBlendState m_OverlayBlendState;
46 |
47 | #define OUTPUT_SURFACE_COUNT 3
48 | VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT];
49 | int m_NextSurfaceIndex;
50 |
51 | #define OUTPUT_SURFACE_FORMAT_COUNT 2
52 | static const VdpRGBAFormat k_OutputFormats8Bit[OUTPUT_SURFACE_FORMAT_COUNT];
53 | static const VdpRGBAFormat k_OutputFormats10Bit[OUTPUT_SURFACE_FORMAT_COUNT];
54 |
55 | VdpGetErrorString* m_VdpGetErrorString;
56 | VdpPresentationQueueTargetDestroy* m_VdpPresentationQueueTargetDestroy;
57 | VdpVideoMixerCreate* m_VdpVideoMixerCreate;
58 | VdpVideoMixerDestroy* m_VdpVideoMixerDestroy;
59 | VdpVideoMixerRender* m_VdpVideoMixerRender;
60 | VdpPresentationQueueCreate* m_VdpPresentationQueueCreate;
61 | VdpPresentationQueueDestroy* m_VdpPresentationQueueDestroy;
62 | VdpPresentationQueueDisplay* m_VdpPresentationQueueDisplay;
63 | VdpPresentationQueueSetBackgroundColor* m_VdpPresentationQueueSetBackgroundColor;
64 | VdpPresentationQueueBlockUntilSurfaceIdle* m_VdpPresentationQueueBlockUntilSurfaceIdle;
65 | VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate;
66 | VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy;
67 | VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities;
68 | VdpBitmapSurfaceCreate* m_VdpBitmapSurfaceCreate;
69 | VdpBitmapSurfaceDestroy* m_VdpBitmapSurfaceDestroy;
70 | VdpBitmapSurfacePutBitsNative* m_VdpBitmapSurfacePutBitsNative;
71 | VdpOutputSurfaceRenderBitmapSurface* m_VdpOutputSurfaceRenderBitmapSurface;
72 | VdpVideoSurfaceGetParameters* m_VdpVideoSurfaceGetParameters;
73 | VdpGetInformationString* m_VdpGetInformationString;
74 |
75 | // X11 stuff
76 | VdpPresentationQueueTargetCreateX11* m_VdpPresentationQueueTargetCreateX11;
77 | };
78 |
79 |
80 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/vt.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "renderer.h"
4 |
5 | #ifdef __OBJC__
6 | #import
7 | class VTBaseRenderer : public IFFmpegRenderer {
8 | public:
9 | VTBaseRenderer(IFFmpegRenderer::RendererType type) : IFFmpegRenderer(type) {}
10 | bool checkDecoderCapabilities(id device, PDECODER_PARAMETERS params);
11 | };
12 | #endif
13 |
14 | // A factory is required to avoid pulling in
15 | // incompatible Objective-C headers.
16 |
17 | class VTMetalRendererFactory {
18 | public:
19 | static
20 | IFFmpegRenderer* createRenderer(bool hwAccel);
21 | };
22 |
23 | class VTRendererFactory {
24 | public:
25 | static
26 | IFFmpegRenderer* createRenderer();
27 | };
28 |
--------------------------------------------------------------------------------
/app/streaming/video/ffmpeg-renderers/vt_base.mm:
--------------------------------------------------------------------------------
1 | // Nasty hack to avoid conflict between AVFoundation and
2 | // libavutil both defining AVMediaType
3 | #define AVMediaType AVMediaType_FFmpeg
4 | #include "vt.h"
5 | #undef AVMediaType
6 |
7 | #import
8 | #import
9 | #import
10 | #import
11 |
12 | bool VTBaseRenderer::checkDecoderCapabilities(id device, PDECODER_PARAMETERS params) {
13 | if (params->videoFormat & VIDEO_FORMAT_MASK_H264) {
14 | if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_H264)) {
15 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
16 | "No HW accelerated H.264 decode via VT");
17 | return false;
18 | }
19 | }
20 | else if (params->videoFormat & VIDEO_FORMAT_MASK_H265) {
21 | if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) {
22 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
23 | "No HW accelerated HEVC decode via VT");
24 | return false;
25 | }
26 |
27 | // HEVC Main10 requires more extensive checks because there's no
28 | // simple API to check for Main10 hardware decoding, and if we don't
29 | // have it, we'll silently get software decoding with horrible performance.
30 | if (params->videoFormat == VIDEO_FORMAT_H265_MAIN10) {
31 | // Exclude all GPUs earlier than macOSGPUFamily2
32 | // https://developer.apple.com/documentation/metal/mtlfeatureset/mtlfeatureset_macos_gpufamily2_v1
33 | if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]) {
34 | if ([device.name containsString:@"Intel"]) {
35 | // 500-series Intel GPUs are Skylake and don't support Main10 hardware decoding
36 | if ([device.name containsString:@" 5"]) {
37 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
38 | "No HEVC Main10 support on Skylake iGPU");
39 | return false;
40 | }
41 | }
42 | else if ([device.name containsString:@"AMD"]) {
43 | // FirePro D, M200, and M300 series GPUs don't support Main10 hardware decoding
44 | if ([device.name containsString:@"FirePro D"] ||
45 | [device.name containsString:@" M2"] ||
46 | [device.name containsString:@" M3"]) {
47 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
48 | "No HEVC Main10 support on AMD GPUs until Polaris");
49 | return false;
50 | }
51 | }
52 | }
53 | else {
54 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
55 | "No HEVC Main10 support on macOS GPUFamily1 GPUs");
56 | return false;
57 | }
58 | }
59 | }
60 | else if (params->videoFormat & VIDEO_FORMAT_MASK_AV1) {
61 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000
62 | if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1)) {
63 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
64 | "No HW accelerated AV1 decode via VT");
65 | return false;
66 | }
67 |
68 | // 10-bit is part of the Main profile for AV1, so it will always
69 | // be present on hardware that supports 8-bit.
70 | #else
71 | SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
72 | "AV1 requires building with Xcode 14 or later");
73 | return false;
74 | #endif
75 | }
76 |
77 | return true;
78 | }
79 |
--------------------------------------------------------------------------------
/app/streaming/video/overlaymanager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "SDL_compat.h"
6 | #include
7 |
8 | namespace Overlay {
9 |
10 | enum OverlayType {
11 | OverlayDebug,
12 | OverlayStatusUpdate,
13 | OverlayMax
14 | };
15 |
16 | class IOverlayRenderer
17 | {
18 | public:
19 | virtual ~IOverlayRenderer() = default;
20 |
21 | virtual void notifyOverlayUpdated(OverlayType type) = 0;
22 | };
23 |
24 | class OverlayManager
25 | {
26 | public:
27 | OverlayManager();
28 | ~OverlayManager();
29 |
30 | bool isOverlayEnabled(OverlayType type);
31 | char* getOverlayText(OverlayType type);
32 | void updateOverlayText(OverlayType type, const char* text);
33 | int getOverlayMaxTextLength();
34 | void setOverlayTextUpdated(OverlayType type);
35 | void setOverlayState(OverlayType type, bool enabled);
36 | SDL_Color getOverlayColor(OverlayType type);
37 | int getOverlayFontSize(OverlayType type);
38 | SDL_Surface* getUpdatedOverlaySurface(OverlayType type);
39 |
40 | void setOverlayRenderer(IOverlayRenderer* renderer);
41 |
42 | private:
43 | void notifyOverlayUpdated(OverlayType type);
44 |
45 | struct {
46 | bool enabled;
47 | int fontSize;
48 | SDL_Color color;
49 | char text[512];
50 |
51 | TTF_Font* font;
52 | SDL_Surface* surface;
53 | } m_Overlays[OverlayMax];
54 | IOverlayRenderer* m_Renderer;
55 | QByteArray m_FontData;
56 | };
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/streaming/video/slvid.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "decoder.h"
4 | #include "overlaymanager.h"
5 |
6 | #include
7 |
8 | class SLVideoDecoder : public IVideoDecoder, public Overlay::IOverlayRenderer
9 | {
10 | public:
11 | SLVideoDecoder(bool testOnly);
12 | virtual ~SLVideoDecoder();
13 | virtual bool initialize(PDECODER_PARAMETERS params) override;
14 | virtual bool isHardwareAccelerated() override;
15 | virtual bool isAlwaysFullScreen() override;
16 | virtual int getDecoderCapabilities() override;
17 | virtual int getDecoderColorspace() override;
18 | virtual int getDecoderColorRange() override;
19 | virtual QSize getDecoderMaxResolution() override;
20 | virtual int submitDecodeUnit(PDECODE_UNIT du) override;
21 | virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
22 |
23 | // Unused since rendering is done directly from the decode thread
24 | virtual void renderFrameOnMainThread() override {}
25 |
26 | // HDR is not supported by SLVideo
27 | virtual void setHdrMode(bool) override {}
28 | virtual bool isHdrSupported() override {
29 | return false;
30 | }
31 |
32 | // Window state changes are not supported by SLVideo
33 | virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) {
34 | return false;
35 | }
36 |
37 | private:
38 | static void slLogCallback(void* context, ESLVideoLog logLevel, const char* message);
39 |
40 | CSLVideoContext* m_VideoContext;
41 | CSLVideoStream* m_VideoStream;
42 | CSLVideoOverlay* m_Overlay;
43 |
44 | int m_ViewportWidth;
45 | int m_ViewportHeight;
46 | };
47 |
--------------------------------------------------------------------------------
/app/utils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #define THROW_BAD_ALLOC_IF_NULL(x) \
6 | if ((x) == nullptr) throw std::bad_alloc()
7 |
8 | namespace WMUtils {
9 | bool isRunningX11();
10 | bool isRunningWayland();
11 | bool isRunningWindowManager();
12 | bool isRunningDesktopEnvironment();
13 | QString getDrmCardOverride();
14 | }
15 |
--------------------------------------------------------------------------------
/app/version.txt:
--------------------------------------------------------------------------------
1 | 6.1.0
--------------------------------------------------------------------------------
/app/wm.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "utils.h"
5 |
6 | #include "SDL_compat.h"
7 |
8 | #ifdef HAS_X11
9 | #include
10 | #endif
11 |
12 | #ifdef HAS_WAYLAND
13 | #include
14 | #endif
15 |
16 | #ifdef HAVE_DRM
17 | #include
18 | #include
19 | #endif
20 |
21 | #define VALUE_SET 0x01
22 | #define VALUE_TRUE 0x02
23 |
24 | bool WMUtils::isRunningX11()
25 | {
26 | #ifdef HAS_X11
27 | static SDL_atomic_t isRunningOnX11;
28 |
29 | // If the value is not set yet, populate it now.
30 | int val = SDL_AtomicGet(&isRunningOnX11);
31 | if (!(val & VALUE_SET)) {
32 | Display* display = XOpenDisplay(nullptr);
33 | if (display != nullptr) {
34 | XCloseDisplay(display);
35 | }
36 |
37 | // Populate the value to return and have for next time.
38 | // This can race with another thread populating the same data,
39 | // but that's no big deal.
40 | val = VALUE_SET | ((display != nullptr) ? VALUE_TRUE : 0);
41 | SDL_AtomicSet(&isRunningOnX11, val);
42 | }
43 |
44 | return !!(val & VALUE_TRUE);
45 | #endif
46 |
47 | return false;
48 | }
49 |
50 | bool WMUtils::isRunningWayland()
51 | {
52 | #ifdef HAS_WAYLAND
53 | static SDL_atomic_t isRunningOnWayland;
54 |
55 | // If the value is not set yet, populate it now.
56 | int val = SDL_AtomicGet(&isRunningOnWayland);
57 | if (!(val & VALUE_SET)) {
58 | struct wl_display* display = wl_display_connect(nullptr);
59 | if (display != nullptr) {
60 | wl_display_disconnect(display);
61 | }
62 |
63 | // Populate the value to return and have for next time.
64 | // This can race with another thread populating the same data,
65 | // but that's no big deal.
66 | val = VALUE_SET | ((display != nullptr) ? VALUE_TRUE : 0);
67 | SDL_AtomicSet(&isRunningOnWayland, val);
68 | }
69 |
70 | return !!(val & VALUE_TRUE);
71 | #endif
72 |
73 | return false;
74 | }
75 |
76 | bool WMUtils::isRunningWindowManager()
77 | {
78 | #if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
79 | // Windows and macOS are always running a window manager
80 | return true;
81 | #else
82 | // On Unix OSes, look for Wayland or X
83 | return WMUtils::isRunningWayland() || WMUtils::isRunningX11();
84 | #endif
85 | }
86 |
87 | bool WMUtils::isRunningDesktopEnvironment()
88 | {
89 | if (qEnvironmentVariableIsSet("HAS_DESKTOP_ENVIRONMENT")) {
90 | return qEnvironmentVariableIntValue("HAS_DESKTOP_ENVIRONMENT");
91 | }
92 |
93 | #if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
94 | // Windows and macOS are always running a desktop environment
95 | return true;
96 | #elif defined(EMBEDDED_BUILD)
97 | // Embedded systems don't run desktop environments
98 | return false;
99 | #else
100 | // On non-embedded systems, assume we have a desktop environment
101 | // if we have a WM running.
102 | return isRunningWindowManager();
103 | #endif
104 | }
105 |
106 | QString WMUtils::getDrmCardOverride()
107 | {
108 | #ifdef HAVE_DRM
109 | QDir dir("/dev/dri");
110 | QStringList cardList = dir.entryList(QStringList("card*"), QDir::Files | QDir::System);
111 | if (cardList.length() == 0) {
112 | return QString();
113 | }
114 |
115 | bool needsOverride = false;
116 | for (const QString& card : cardList) {
117 | QFile cardFd(dir.filePath(card));
118 | if (!cardFd.open(QFile::ReadOnly)) {
119 | continue;
120 | }
121 |
122 | auto resources = drmModeGetResources(cardFd.handle());
123 | if (resources == nullptr) {
124 | // If we find a card that doesn't have a display before a card that
125 | // has one, we'll need to override Qt's EGLFS config because they
126 | // don't properly handle cards without displays.
127 | needsOverride = true;
128 | }
129 | else {
130 | // We found a card with a display
131 | drmModeFreeResources(resources);
132 | if (needsOverride) {
133 | // Override the default card with this one
134 | return dir.filePath(card);
135 | }
136 | else {
137 | return QString();
138 | }
139 | }
140 | }
141 | #endif
142 |
143 | return QString();
144 | }
145 |
--------------------------------------------------------------------------------
/config.tests/EGL/EGL.pro:
--------------------------------------------------------------------------------
1 | SOURCES = main.cpp
2 |
3 | CONFIG += link_pkgconfig
4 |
5 | # This adds /opt/vc/include to the include path which
6 | # pulls in the BRCM GLES and EGL libraries. If we don't
7 | # add this, we'll get the system's headers which expose
8 | # functionality the runtime GL implementation won't have.
9 | packagesExist(mmal) {
10 | PKGCONFIG += mmal
11 | }
12 |
13 | PKGCONFIG += sdl2 egl libavcodec libavutil
--------------------------------------------------------------------------------
/config.tests/EGL/main.cpp:
--------------------------------------------------------------------------------
1 | #define SDL_USE_BUILTIN_OPENGL_DEFINITIONS 1
2 |
3 | #include
4 | #include
5 |
6 | #ifndef EGL_VERSION_1_4
7 | #error EGLRenderer requires EGL 1.4
8 | #endif
9 |
10 | #ifndef GL_ES_VERSION_2_0
11 | #error EGLRenderer requires OpenGL ES 2.0
12 | #endif
13 |
14 | int main() {
15 | return 0;
16 | }
--------------------------------------------------------------------------------
/config.tests/SL/SL.pro:
--------------------------------------------------------------------------------
1 | SOURCES = main.cpp
2 | LIBS += -lSLVideo -lSLAudio
3 |
4 |
--------------------------------------------------------------------------------
/config.tests/SL/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int main()
5 | {
6 | SLVideo_CreateContext();
7 | SLAudio_CreateContext();
8 | return 0;
9 | }
10 |
--------------------------------------------------------------------------------
/globaldefs.pri:
--------------------------------------------------------------------------------
1 | # Support debug and release builds from command line for CI
2 | CONFIG += debug_and_release
3 |
4 | # Ensure symbols are always generated
5 | CONFIG += force_debug_info
6 |
7 | # Disable asserts on release builds
8 | CONFIG(release, debug|release) {
9 | DEFINES += NDEBUG
10 | }
11 |
12 | # Enable ASan for Linux or macOS
13 | #CONFIG += sanitizer sanitize_address
14 |
15 | # Enable ASan for Windows
16 | #QMAKE_CFLAGS += -fsanitize=address
17 | #QMAKE_CXXFLAGS += -fsanitize=address
18 | #QMAKE_LFLAGS += -incremental:no -wholearchive:clang_rt.asan_dynamic-x86_64.lib -wholearchive:clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
19 |
--------------------------------------------------------------------------------
/h264bitstream/h264bitstream.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2018-10-12T15:50:59
4 | #
5 | #-------------------------------------------------
6 |
7 |
8 | QT -= core gui
9 |
10 | TARGET = h264bitstream
11 | TEMPLATE = lib
12 |
13 | # Build a static library
14 | CONFIG += staticlib
15 |
16 | # Disable warnings
17 | CONFIG += warn_off
18 |
19 | # Include global qmake defs
20 | include(../globaldefs.pri)
21 |
22 | # Older GCC versions defaulted to GNU89
23 | *-g++ {
24 | QMAKE_CFLAGS += -std=gnu99
25 | }
26 |
27 | SRC_DIR = $$PWD/h264bitstream
28 |
29 | SOURCES += \
30 | $$SRC_DIR/h264_nal.c \
31 | $$SRC_DIR/h264_sei.c \
32 | $$SRC_DIR/h264_stream.c
33 |
34 | HEADERS += \
35 | $$SRC_DIR/bs.h \
36 | $$SRC_DIR/h264_sei.h \
37 | $$SRC_DIR/h264_stream.h
38 |
39 | INCLUDEPATH += $$INC_DIR
40 |
--------------------------------------------------------------------------------
/moonlight-common-c/moonlight-common-c.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2018-05-05T17:41:00
4 | #
5 | #-------------------------------------------------
6 |
7 | QT -= core gui
8 |
9 | TARGET = moonlight-common-c
10 | TEMPLATE = lib
11 |
12 | # Build a static library
13 | CONFIG += staticlib
14 |
15 | # Include global qmake defs
16 | include(../globaldefs.pri)
17 |
18 | win32 {
19 | contains(QT_ARCH, i386) {
20 | INCLUDEPATH += $$PWD/../libs/windows/include/x86
21 | }
22 | contains(QT_ARCH, x86_64) {
23 | INCLUDEPATH += $$PWD/../libs/windows/include/x64
24 | }
25 | contains(QT_ARCH, arm64) {
26 | INCLUDEPATH += $$PWD/../libs/windows/include/arm64
27 | }
28 |
29 | INCLUDEPATH += $$PWD/../libs/windows/include
30 | DEFINES += HAS_QOS_FLOWID=1 HAS_PQOS_FLOWID=1
31 | }
32 | macx {
33 | INCLUDEPATH += $$PWD/../libs/mac/include
34 | }
35 | unix:!macx {
36 | CONFIG += link_pkgconfig
37 | PKGCONFIG += openssl
38 | DEFINES += HAVE_CLOCK_GETTIME=1
39 | }
40 |
41 | COMMON_C_DIR = $$PWD/moonlight-common-c
42 | ENET_DIR = $$COMMON_C_DIR/enet
43 | RS_DIR = $$COMMON_C_DIR/reedsolomon
44 | SOURCES += \
45 | $$RS_DIR/rs.c \
46 | $$ENET_DIR/callbacks.c \
47 | $$ENET_DIR/compress.c \
48 | $$ENET_DIR/host.c \
49 | $$ENET_DIR/list.c \
50 | $$ENET_DIR/packet.c \
51 | $$ENET_DIR/peer.c \
52 | $$ENET_DIR/protocol.c \
53 | $$ENET_DIR/unix.c \
54 | $$ENET_DIR/win32.c \
55 | $$COMMON_C_DIR/src/AudioStream.c \
56 | $$COMMON_C_DIR/src/ByteBuffer.c \
57 | $$COMMON_C_DIR/src/Connection.c \
58 | $$COMMON_C_DIR/src/ConnectionTester.c \
59 | $$COMMON_C_DIR/src/ControlStream.c \
60 | $$COMMON_C_DIR/src/FakeCallbacks.c \
61 | $$COMMON_C_DIR/src/InputStream.c \
62 | $$COMMON_C_DIR/src/LinkedBlockingQueue.c \
63 | $$COMMON_C_DIR/src/Misc.c \
64 | $$COMMON_C_DIR/src/Platform.c \
65 | $$COMMON_C_DIR/src/PlatformCrypto.c \
66 | $$COMMON_C_DIR/src/PlatformSockets.c \
67 | $$COMMON_C_DIR/src/RtpAudioQueue.c \
68 | $$COMMON_C_DIR/src/RtpVideoQueue.c \
69 | $$COMMON_C_DIR/src/RtspConnection.c \
70 | $$COMMON_C_DIR/src/RtspParser.c \
71 | $$COMMON_C_DIR/src/SdpGenerator.c \
72 | $$COMMON_C_DIR/src/SimpleStun.c \
73 | $$COMMON_C_DIR/src/VideoDepacketizer.c \
74 | $$COMMON_C_DIR/src/VideoStream.c
75 | HEADERS += \
76 | $$COMMON_C_DIR/src/Limelight.h
77 | INCLUDEPATH += \
78 | $$RS_DIR \
79 | $$ENET_DIR/include \
80 | $$COMMON_C_DIR/src
81 | DEFINES += HAS_SOCKLEN_T
82 |
83 | CONFIG(debug, debug|release) {
84 | # Enable asserts on debug builds
85 | DEFINES += LC_DEBUG
86 | }
87 |
88 | # Older GCC versions defaulted to GNU89
89 | *-g++ {
90 | QMAKE_CFLAGS += -std=gnu99
91 | }
92 |
93 | # Disable unused parameter warnings on GCC and Clang
94 | *-g++|*-clang* {
95 | QMAKE_CFLAGS_WARN_ON += -Wno-unused-parameter
96 | }
97 |
--------------------------------------------------------------------------------
/moonlight-qt.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 | SUBDIRS = \
3 | moonlight-common-c \
4 | qmdnsengine \
5 | app \
6 | h264bitstream
7 |
8 | # Build the dependencies in parallel before the final app
9 | app.depends = qmdnsengine moonlight-common-c h264bitstream
10 | win32:!winrt {
11 | SUBDIRS += AntiHooking
12 | app.depends += AntiHooking
13 | }
14 | !winrt:win32|macx {
15 | SUBDIRS += soundio
16 | app.depends += soundio
17 | }
18 |
19 | # Support debug and release builds from command line for CI
20 | CONFIG += debug_and_release
21 |
22 | # Run our compile tests
23 | load(configure)
24 | qtCompileTest(SL)
25 | qtCompileTest(EGL)
26 |
--------------------------------------------------------------------------------
/qmdnsengine/qmdnsengine.pro:
--------------------------------------------------------------------------------
1 | QT -= gui
2 | QT += network
3 |
4 | TARGET = qmdnsengine
5 | TEMPLATE = lib
6 |
7 | # Build a static library
8 | CONFIG += staticlib
9 |
10 | # Disable warnings
11 | CONFIG += warn_off
12 |
13 | # C++11 is required to build
14 | CONFIG += c++11
15 |
16 | # Include global qmake defs
17 | include(../globaldefs.pri)
18 |
19 | QMDNSE_DIR = $$PWD/qmdnsengine/src
20 | DEFINES += \
21 | QT_NO_SIGNALS_SLOTS_KEYWORDS
22 |
23 | # These are all required to be defined here not just
24 | # to get them loaded into the IDE nicely, but more
25 | # importantly, to get MOC to run on them properly.
26 | HEADERS += \
27 | $$QMDNSE_DIR/include/qmdnsengine/abstractserver.h \
28 | $$QMDNSE_DIR/include/qmdnsengine/bitmap.h \
29 | $$QMDNSE_DIR/include/qmdnsengine/browser.h \
30 | $$QMDNSE_DIR/include/qmdnsengine/cache.h \
31 | $$QMDNSE_DIR/include/qmdnsengine/dns.h \
32 | $$QMDNSE_DIR/include/qmdnsengine/hostname.h \
33 | $$QMDNSE_DIR/include/qmdnsengine/mdns.h \
34 | $$QMDNSE_DIR/include/qmdnsengine/message.h \
35 | $$QMDNSE_DIR/include/qmdnsengine/prober.h \
36 | $$QMDNSE_DIR/include/qmdnsengine/provider.h \
37 | $$QMDNSE_DIR/include/qmdnsengine/query.h \
38 | $$QMDNSE_DIR/include/qmdnsengine/record.h \
39 | $$QMDNSE_DIR/include/qmdnsengine/resolver.h \
40 | $$QMDNSE_DIR/include/qmdnsengine/server.h \
41 | $$QMDNSE_DIR/include/qmdnsengine/service.h \
42 | $$QMDNSE_DIR/src/bitmap_p.h \
43 | $$QMDNSE_DIR/src/browser_p.h \
44 | $$QMDNSE_DIR/src/cache_p.h \
45 | $$QMDNSE_DIR/src/hostname_p.h \
46 | $$QMDNSE_DIR/src/message_p.h \
47 | $$QMDNSE_DIR/src/prober_p.h \
48 | $$QMDNSE_DIR/src/provider_p.h \
49 | $$QMDNSE_DIR/src/query_p.h \
50 | $$QMDNSE_DIR/src/record_p.h \
51 | $$QMDNSE_DIR/src/resolver_p.h \
52 | $$QMDNSE_DIR/src/server_p.h \
53 | $$QMDNSE_DIR/src/service_p.h
54 |
55 | SOURCES += \
56 | $$QMDNSE_DIR/src/abstractserver.cpp \
57 | $$QMDNSE_DIR/src/bitmap.cpp \
58 | $$QMDNSE_DIR/src/browser.cpp \
59 | $$QMDNSE_DIR/src/cache.cpp \
60 | $$QMDNSE_DIR/src/dns.cpp \
61 | $$QMDNSE_DIR/src/hostname.cpp \
62 | $$QMDNSE_DIR/src/mdns.cpp \
63 | $$QMDNSE_DIR/src/message.cpp \
64 | $$QMDNSE_DIR/src/prober.cpp \
65 | $$QMDNSE_DIR/src/provider.cpp \
66 | $$QMDNSE_DIR/src/query.cpp \
67 | $$QMDNSE_DIR/src/record.cpp \
68 | $$QMDNSE_DIR/src/resolver.cpp \
69 | $$QMDNSE_DIR/src/server.cpp \
70 | $$QMDNSE_DIR/src/service.cpp
71 |
72 | INCLUDEPATH += \
73 | $$QMDNSE_DIR/include \
74 | $$PWD/qmdnsengine
75 |
--------------------------------------------------------------------------------
/qmdnsengine/qmdnsengine_export.h:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2017 Nathan Osman
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to
8 | * deal in the Software without restriction, including without limitation the
9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | * sell copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 | * IN THE SOFTWARE.
23 | */
24 |
25 | #ifndef QMDNSENGINE_EXPORT_H
26 | #define QMDNSENGINE_EXPORT_H
27 |
28 | #include
29 |
30 | #if defined(BUILD_SHARED_LIBS)
31 | # if defined(QMDNSENGINE_LIBRARY)
32 | # define QMDNSENGINE_EXPORT Q_DECL_EXPORT
33 | # else
34 | # define QMDNSENGINE_EXPORT Q_DECL_IMPORT
35 | # endif
36 | #else
37 | # define QMDNSENGINE_EXPORT
38 | #endif
39 |
40 | #endif // QMDNSENGINE_EXPORT_H
41 |
--------------------------------------------------------------------------------
/scripts/appveyor/qmake.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | %QTDIR%\msvc2022_64\bin\qmake.exe -qtconf "%~dp0\target_qt.conf" %*
3 |
--------------------------------------------------------------------------------
/scripts/appveyor/qtpaths.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | %QTDIR%\msvc2022_64\bin\qtpaths.exe -qtconf "%~dp0\target_qt.conf" %*
3 |
--------------------------------------------------------------------------------
/scripts/appveyor/target_qt.conf:
--------------------------------------------------------------------------------
1 | [DevicePaths]
2 | Prefix=C:/Qt/Qt-6.8
3 | [Paths]
4 | Prefix=../
5 | Documentation=./doc
6 | Headers=include
7 | Libraries=lib
8 | LibraryExecutables=./bin
9 | Binaries=bin
10 | Plugins=./plugins
11 | QmlImports=./qml
12 | ArchData=.
13 | Data=.
14 | Translations=./translations
15 | Examples=examples
16 | Tests=tests
17 | Settings=etc/xdg
18 | HostPrefix=../../msvc2022_64
19 | HostBinaries=bin
20 | HostLibraries=lib
21 | HostLibraryExecutables=./bin
22 | HostData=../msvc2022_arm64
23 | Sysroot=
24 | SysrootifyPrefix=false
25 | TargetSpec=win32-arm64-msvc
26 | HostSpec=
27 | Documentation=../../Docs/Qt-6.8
28 | Examples=../../Examples/Qt-6.8
--------------------------------------------------------------------------------
/scripts/build-appimage.sh:
--------------------------------------------------------------------------------
1 | BUILD_CONFIG="release"
2 |
3 | fail()
4 | {
5 | echo "$1" 1>&2
6 | exit 1
7 | }
8 |
9 | BUILD_ROOT=$PWD/build
10 | SOURCE_ROOT=$PWD
11 | BUILD_FOLDER=$BUILD_ROOT/build-$BUILD_CONFIG
12 | DEPLOY_FOLDER=$BUILD_ROOT/deploy-$BUILD_CONFIG
13 | INSTALLER_FOLDER=$BUILD_ROOT/installer-$BUILD_CONFIG
14 | VERSION=`cat $SOURCE_ROOT/app/version.txt`
15 |
16 | command -v qmake >/dev/null 2>&1 || fail "Unable to find 'qmake' in your PATH!"
17 | command -v linuxdeployqt >/dev/null 2>&1 || fail "Unable to find 'linuxdeployqt' in your PATH!"
18 |
19 | echo Cleaning output directories
20 | rm -rf $BUILD_FOLDER
21 | rm -rf $DEPLOY_FOLDER
22 | rm -rf $INSTALLER_FOLDER
23 | mkdir $BUILD_ROOT
24 | mkdir $BUILD_FOLDER
25 | mkdir $DEPLOY_FOLDER
26 | mkdir $INSTALLER_FOLDER
27 |
28 | echo Configuring the project
29 | pushd $BUILD_FOLDER
30 | # Building with Wayland support will cause linuxdeployqt to include libwayland-client.so in the AppImage.
31 | # Since we always use the host implementation of EGL, this can cause libEGL_mesa.so to fail to load due
32 | # to missing symbols from the host's version of libwayland-client.so that aren't present in the older
33 | # version of libwayland-client.so from our AppImage build environment. When this happens, EGL fails to
34 | # work even in X11. To avoid this, we will disable Wayland support for the AppImage.
35 | #
36 | # We disable DRM support because linuxdeployqt doesn't bundle the appropriate libraries for Qt EGLFS.
37 | qmake $SOURCE_ROOT/moonlight-qt.pro CONFIG+=disable-wayland CONFIG+=disable-libdrm CONFIG+=disable-cuda PREFIX=$DEPLOY_FOLDER/usr DEFINES+=APP_IMAGE || fail "Qmake failed!"
38 | popd
39 |
40 | echo Compiling Moonlight in $BUILD_CONFIG configuration
41 | pushd $BUILD_FOLDER
42 | make -j$(nproc) $(echo "$BUILD_CONFIG" | tr '[:upper:]' '[:lower:]') || fail "Make failed!"
43 | popd
44 |
45 | echo Deploying to staging directory
46 | pushd $BUILD_FOLDER
47 | make install || fail "Make install failed!"
48 | popd
49 |
50 | echo Creating AppImage
51 | pushd $INSTALLER_FOLDER
52 | VERSION=$VERSION linuxdeployqt $DEPLOY_FOLDER/usr/share/applications/com.moonlight_stream.Moonlight.desktop -qmldir=$SOURCE_ROOT/app/gui -appimage || fail "linuxdeployqt failed!"
53 | popd
54 |
55 | echo Build successful
--------------------------------------------------------------------------------
/scripts/build-steamlink-app.sh:
--------------------------------------------------------------------------------
1 | BUILD_CONFIG="release"
2 | QT_514_COMMIT="609d4aaccb503298e7fa9cef45e0ddc4c4afd63c"
3 |
4 | fail()
5 | {
6 | echo "$1" 1>&2
7 | exit 1
8 | }
9 |
10 | if [ "$STEAMLINK_SDK_PATH" == "" ]; then
11 | fail "You must set STEAMLINK_SDK_PATH to build for Steam Link"
12 | fi
13 |
14 | BUILD_ROOT=$PWD/build
15 | SOURCE_ROOT=$PWD
16 | BUILD_FOLDER=$BUILD_ROOT/build-$BUILD_CONFIG
17 | DEPLOY_FOLDER=$BUILD_ROOT/deploy-$BUILD_CONFIG
18 | INSTALLER_FOLDER=$BUILD_ROOT/installer-$BUILD_CONFIG
19 | VERSION=`cat $SOURCE_ROOT/app/version.txt`
20 |
21 | echo Cleaning output directories
22 | rm -rf $BUILD_FOLDER
23 | rm -rf $DEPLOY_FOLDER
24 | rm -rf $INSTALLER_FOLDER
25 | mkdir $BUILD_ROOT
26 | mkdir $BUILD_FOLDER
27 | mkdir $DEPLOY_FOLDER
28 | mkdir $INSTALLER_FOLDER
29 |
30 | echo Initializing Steam Link SDK
31 | source $STEAMLINK_SDK_PATH/setenv.sh || fail "SL SDK initialization failed!"
32 |
33 | echo Configuring the project
34 | pushd $BUILD_FOLDER
35 | qmake $SOURCE_ROOT/moonlight-qt.pro QMAKE_CFLAGS_ISYSTEM= || fail "Qmake failed!"
36 | popd
37 |
38 | echo Compiling Moonlight in $BUILD_CONFIG configuration
39 | pushd $BUILD_FOLDER
40 | make -j$(nproc) $(echo "$BUILD_CONFIG" | tr '[:upper:]' '[:lower:]') || fail "Make failed!"
41 | popd
42 |
43 | echo Creating app bundle
44 | mkdir -p $DEPLOY_FOLDER/steamlink/apps/moonlight/bin
45 | cp $BUILD_FOLDER/app/moonlight $DEPLOY_FOLDER/steamlink/apps/moonlight/bin/ || fail "Binary copy failed!"
46 | cp $SOURCE_ROOT/app/deploy/steamlink/* $DEPLOY_FOLDER/steamlink/apps/moonlight/ || fail "Metadata copy failed!"
47 | pushd $DEPLOY_FOLDER
48 | zip -r $INSTALLER_FOLDER/Moonlight-SteamLink-$VERSION.zip . || fail "Zip failed!"
49 | popd
50 |
51 | echo Build completed
--------------------------------------------------------------------------------
/scripts/clean-libs.sh:
--------------------------------------------------------------------------------
1 | LIB_PATH=$(pwd)/libs
2 |
3 | while [[ "$#" -gt 0 ]]; do
4 | echo $1
5 | case "$1" in
6 | --sdl2_win)
7 | rm $LIB_PATH/windows/include/*/begin_code.h $LIB_PATH/windows/include/*/close_code.h $LIB_PATH/windows/include/*/SDL*.h $LIB_PATH/windows/lib/*/SDL2.* $LIB_PATH/windows/lib/*/SDL2main.*
8 | shift
9 | ;;
10 | --sdl2_mac)
11 | rm -r $LIB_PATH/mac/Frameworks/SDL2.framework
12 | shift
13 | ;;
14 | --sdl_ttf_win)
15 | rm $LIB_PATH/windows/include/SDL_ttf.h $LIB_PATH/windows/lib/*/SDL2_ttf.*
16 | shift
17 | ;;
18 | --detours_win)
19 | rm $LIB_PATH/windows/include/detver.h $LIB_PATH/windows/include/detours.h $LIB_PATH/windows/lib/*/detours.*
20 | shift
21 | ;;
22 | --discord-rpc_win)
23 | rm $LIB_PATH/windows/include/discord_*.h $LIB_PATH/windows/lib/*/discord-rpc.*
24 | shift
25 | ;;
26 | --discord-rpc_mac)
27 | rm $LIB_PATH/mac/include/discord_*.h $LIB_PATH/mac/lib/libdiscord-rpc.a
28 | shift
29 | ;;
30 | --opus_win)
31 | rm $LIB_PATH/windows/include/opus*.h $LIB_PATH/windows/lib/*/opus.*
32 | shift
33 | ;;
34 | --opus_mac)
35 | rm $LIB_PATH/mac/include/opus*.h $LIB_PATH/mac/lib/libopus.a
36 | shift
37 | ;;
38 | --openssl_win)
39 | rm -r $LIB_PATH/windows/include/*/openssl
40 | rm $LIB_PATH/windows/lib/*/libcrypto* $LIB_PATH/windows/lib/*/libssl*
41 | shift
42 | ;;
43 | --openssl_mac)
44 | rm -r $LIB_PATH/mac/include/openssl
45 | rm $LIB_PATH/mac/lib/libssl*.dylib $LIB_PATH/mac/lib/libcrypto*.dylib
46 | shift
47 | ;;
48 | --ffmpeg_win)
49 | rm -r $LIB_PATH/windows/include/*/libavcodec $LIB_PATH/windows/include/*/libavutil $LIB_PATH/windows/include/*/libavformat $LIB_PATH/windows/include/*/libswscale
50 | rm $LIB_PATH/windows/lib/*/avcodec* $LIB_PATH/windows/lib/*/avutil* $LIB_PATH/windows/lib/*/avformat* $LIB_PATH/windows/lib/*/swscale*
51 | shift
52 | ;;
53 | --dav1d_win)
54 | rm $LIB_PATH/windows/lib/*/dav1d*
55 | shift
56 | ;;
57 | --ffmpeg_mac)
58 | rm -r $LIB_PATH/mac/include/libavcodec $LIB_PATH/mac/include/libavutil $LIB_PATH/mac/include/libavformat $LIB_PATH/mac/include/libswscale
59 | rm $LIB_PATH/mac/lib/libavcodec* $LIB_PATH/mac/lib/libavutil* $LIB_PATH/mac/lib/libavformat* $LIB_PATH/mac/lib/libswscale*
60 | shift
61 | ;;
62 | --libplacebo_win)
63 | rm -r $LIB_PATH/windows/include/*/libplacebo
64 | rm $LIB_PATH/windows/lib/*/libplacebo*
65 | shift
66 | ;;
67 | --)
68 | shift;
69 | break
70 | ;;
71 | *)
72 | echo "Unexpected option: $1"
73 | exit
74 | ;;
75 | esac
76 | done
--------------------------------------------------------------------------------
/scripts/create-qt-pdb-zip.bat:
--------------------------------------------------------------------------------
1 | rem Run from Qt command prompt without changing directories
2 |
3 | del symbols.zip
4 | 7z a symbols.zip *.pdb -r -xr!Qt5WebEngineCore*
--------------------------------------------------------------------------------
/scripts/generate-bundle.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | setlocal enableDelayedExpansion
3 |
4 | rem Run from Qt command prompt with working directory set to root of repo
5 |
6 | set BUILD_CONFIG=%1
7 |
8 | rem Convert to lower case for windeployqt
9 | if /I "%BUILD_CONFIG%"=="debug" (
10 | set BUILD_CONFIG=debug
11 | set WIX_MUMS=10
12 | ) else (
13 | if /I "%BUILD_CONFIG%"=="release" (
14 | set BUILD_CONFIG=release
15 | set WIX_MUMS=10
16 | ) else (
17 | if /I "%BUILD_CONFIG%"=="signed-release" (
18 | set BUILD_CONFIG=release
19 | set SIGN=1
20 | set MUST_DEPLOY_SYMBOLS=1
21 |
22 | rem Fail if there are unstaged changes
23 | git diff-index --quiet HEAD --
24 | if !ERRORLEVEL! NEQ 0 (
25 | echo Signed release builds must not have unstaged changes!
26 | exit /b 1
27 | )
28 | ) else (
29 | echo Invalid build configuration - expected 'debug' or 'release'
30 | exit /b 1
31 | )
32 | )
33 | )
34 |
35 | set SIGNTOOL_PARAMS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /sha1 8b9d0d682ad9459e54f05a79694bc10f9876e297 /v
36 |
37 | set BUILD_ROOT=%cd%\build
38 | set SOURCE_ROOT=%cd%
39 | set BUILD_FOLDER=%BUILD_ROOT%\build-%BUILD_CONFIG%
40 | set INSTALLER_FOLDER=%BUILD_ROOT%\installer-%BUILD_CONFIG%
41 | set /p VERSION=<%SOURCE_ROOT%\app\version.txt
42 |
43 | rem Ensure that all architectures have been built before the final bundle
44 | if not exist "%BUILD_ROOT%\build-x64-%BUILD_CONFIG%\Moonlight.msi" (
45 | echo Unable to build bundle - missing binaries for %BUILD_CONFIG% x64
46 | echo You must run 'build-arch.bat %BUILD_CONFIG% x64' first
47 | exit /b 1
48 | )
49 | if not exist "%BUILD_ROOT%\build-arm64-%BUILD_CONFIG%\Moonlight.msi" (
50 | echo Unable to build bundle - missing binaries for %BUILD_CONFIG% arm64
51 | echo You must run 'build-arch.bat %BUILD_CONFIG% arm64' first
52 | exit /b 1
53 | )
54 |
55 | echo Cleaning output directories
56 | rmdir /s /q %BUILD_FOLDER%
57 | rmdir /s /q %INSTALLER_FOLDER%
58 | mkdir %BUILD_FOLDER%
59 | mkdir %INSTALLER_FOLDER%
60 |
61 | rem Find Visual Studio and run vcvarsall.bat
62 | set VSWHERE="%SOURCE_ROOT%\scripts\vswhere.exe"
63 | for /f "usebackq delims=" %%i in (`%VSWHERE% -latest -property installationPath`) do (
64 | call "%%i\VC\Auxiliary\Build\vcvarsall.bat" x86
65 | )
66 | if !ERRORLEVEL! NEQ 0 goto Error
67 |
68 | echo Building bundle
69 | rem Bundles are always x86 binaries
70 | msbuild -Restore %SOURCE_ROOT%\wix\MoonlightSetup\MoonlightSetup.wixproj /p:Configuration=%BUILD_CONFIG% /p:Platform=x86 /p:MSBuildProjectExtensionsPath=%BUILD_FOLDER%\
71 | if !ERRORLEVEL! NEQ 0 goto Error
72 |
73 | rem Rename the installer to match the publishing convention
74 | ren %INSTALLER_FOLDER%\MoonlightSetup.exe MoonlightSetup-%VERSION%.exe
75 |
76 | echo Build successful for Moonlight v%VERSION% installer!
77 | exit /b 0
78 |
79 | :Error
80 | echo Build failed!
81 | exit /b !ERRORLEVEL!
82 |
--------------------------------------------------------------------------------
/scripts/generate-dmg.sh:
--------------------------------------------------------------------------------
1 | # This script requires create-dmg to be installed from https://github.com/sindresorhus/create-dmg
2 | BUILD_CONFIG=$1
3 |
4 | fail()
5 | {
6 | echo "$1" 1>&2
7 | exit 1
8 | }
9 |
10 | if [ "$BUILD_CONFIG" != "Debug" ] && [ "$BUILD_CONFIG" != "Release" ]; then
11 | fail "Invalid build configuration - expected 'Debug' or 'Release'"
12 | fi
13 |
14 | BUILD_ROOT=$PWD/build
15 | SOURCE_ROOT=$PWD
16 | BUILD_FOLDER=$BUILD_ROOT/build-$BUILD_CONFIG
17 | INSTALLER_FOLDER=$BUILD_ROOT/installer-$BUILD_CONFIG
18 | VERSION=`cat $SOURCE_ROOT/app/version.txt`
19 |
20 | if [ "$SIGNING_PROVIDER_SHORTNAME" == "" ]; then
21 | SIGNING_PROVIDER_SHORTNAME=$SIGNING_IDENTITY
22 | fi
23 | if [ "$SIGNING_IDENTITY" == "" ]; then
24 | SIGNING_IDENTITY=$SIGNING_PROVIDER_SHORTNAME
25 | fi
26 |
27 | [ "$SIGNING_IDENTITY" == "" ] || git diff-index --quiet HEAD -- || fail "Signed release builds must not have unstaged changes!"
28 |
29 | echo Cleaning output directories
30 | rm -rf $BUILD_FOLDER
31 | rm -rf $INSTALLER_FOLDER
32 | mkdir $BUILD_ROOT
33 | mkdir $BUILD_FOLDER
34 | mkdir $INSTALLER_FOLDER
35 |
36 | echo Configuring the project
37 | pushd $BUILD_FOLDER
38 | qmake $SOURCE_ROOT/moonlight-qt.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64 arm64" || fail "Qmake failed!"
39 | popd
40 |
41 | echo Compiling Moonlight in $BUILD_CONFIG configuration
42 | pushd $BUILD_FOLDER
43 | make -j$(sysctl -n hw.logicalcpu) $(echo "$BUILD_CONFIG" | tr '[:upper:]' '[:lower:]') || fail "Make failed!"
44 | popd
45 |
46 | echo Saving dSYM file
47 | pushd $BUILD_FOLDER
48 | dsymutil app/Moonlight.app/Contents/MacOS/Moonlight -o Moonlight-$VERSION.dsym || fail "dSYM creation failed!"
49 | cp -R Moonlight-$VERSION.dsym $INSTALLER_FOLDER || fail "dSYM copy failed!"
50 | popd
51 |
52 | echo Creating app bundle
53 | EXTRA_ARGS=
54 | if [ "$BUILD_CONFIG" == "Debug" ]; then EXTRA_ARGS="$EXTRA_ARGS -use-debug-libs"; fi
55 | echo Extra deployment arguments: $EXTRA_ARGS
56 | macdeployqt $BUILD_FOLDER/app/Moonlight.app $EXTRA_ARGS -qmldir=$SOURCE_ROOT/app/gui -appstore-compliant || fail "macdeployqt failed!"
57 |
58 | echo Removing dSYM files from app bundle
59 | find $BUILD_FOLDER/app/Moonlight.app/ -name '*.dSYM' | xargs rm -rf
60 |
61 | if [ "$SIGNING_IDENTITY" != "" ]; then
62 | echo Signing app bundle
63 | codesign --force --deep --options runtime --timestamp --sign "$SIGNING_IDENTITY" $BUILD_FOLDER/app/Moonlight.app || fail "Signing failed!"
64 | fi
65 |
66 | echo Creating DMG
67 | if [ "$SIGNING_IDENTITY" != "" ]; then
68 | create-dmg $BUILD_FOLDER/app/Moonlight.app $INSTALLER_FOLDER --identity="$SIGNING_IDENTITY" || fail "create-dmg failed!"
69 | else
70 | create-dmg $BUILD_FOLDER/app/Moonlight.app $INSTALLER_FOLDER
71 | case $? in
72 | 0) ;;
73 | 2) ;;
74 | *) fail "create-dmg failed!";;
75 | esac
76 | fi
77 |
78 | if [ "$NOTARY_KEYCHAIN_PROFILE" != "" ]; then
79 | echo Uploading to App Notary service
80 | xcrun notarytool submit --keychain-profile "$NOTARY_KEYCHAIN_PROFILE" --wait $INSTALLER_FOLDER/Moonlight\ $VERSION.dmg || fail "Notary submission failed"
81 |
82 | echo Stapling notary ticket to DMG
83 | xcrun stapler staple -v $INSTALLER_FOLDER/Moonlight\ $VERSION.dmg || fail "Notary ticket stapling failed!"
84 | fi
85 |
86 | mv $INSTALLER_FOLDER/Moonlight\ $VERSION.dmg $INSTALLER_FOLDER/Moonlight-$VERSION.dmg
87 | echo Build successful
--------------------------------------------------------------------------------
/scripts/generate-ico.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # The ImageMagick conversion tool doesn't seem to always generate
4 | # ICO files with background transparency properly. Please validate
5 | # that the output has a transparent background.
6 |
7 | convert -density 256 -background none -define icon:auto-resize ../app/res/moonlight.svg ../app/moonlight.ico
8 | convert -density 256 -background none -size 64x64 ../app/res/moonlight.svg ../app/moonlight_wix.png
9 |
10 | echo IMPORTANT: Validate the icon has a transparent background before committing!
11 |
--------------------------------------------------------------------------------
/scripts/generate-src.sh:
--------------------------------------------------------------------------------
1 | # Run from root of the Git repo to archive a source tarball
2 |
3 | fail()
4 | {
5 | echo "$1" 1>&2
6 | exit 1
7 | }
8 |
9 | git diff-index --quiet HEAD -- || fail "Source archives must not have unstaged changes!"
10 |
11 | BUILD_ROOT=$PWD/build
12 | ARCHIVE_FOLDER=$BUILD_ROOT/source
13 | VERSION=`cat app/version.txt`
14 |
15 | echo Cleaning output directories
16 | rm -rf $ARCHIVE_FOLDER
17 | mkdir $BUILD_ROOT
18 | mkdir $ARCHIVE_FOLDER
19 |
20 | scripts/git-archive-all.sh --format tar.gz $ARCHIVE_FOLDER/MoonlightSrc-$VERSION.tar.gz || fail "Archive failed"
21 |
22 | echo Archive successful
--------------------------------------------------------------------------------
/scripts/jom.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/scripts/jom.exe
--------------------------------------------------------------------------------
/scripts/svg2icns:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # https://gist.github.com/zlbruce/883605a635df8d5964bab11ed75e46ad
3 | echo "*** SVG 2 ICNS ***"
4 | if [ $# -ne 1 ]; then
5 | echo "Usage: svg2icns filename.svg"
6 | exit 100
7 | fi
8 | filename="$1"
9 | name=${filename%.*}
10 | ext=${filename##*.}
11 | echo "processing: $name"
12 | dest="$name".iconset
13 | mkdir "$dest"
14 |
15 | convert -background none -resize '!16x16' "$1" "$dest/icon_16x16.png"
16 | convert -background none -resize '!32x32' "$1" "$dest/icon_16x16@2x.png"
17 | cp "$dest/icon_16x16@2x.png" "$dest/icon_32x32.png"
18 | convert -background none -resize '!64x64' "$1" "$dest/icon_32x32@2x.png"
19 | convert -background none -resize '!128x128' "$1" "$dest/icon_128x128.png"
20 | convert -background none -resize '!256x256' "$1" "$dest/icon_128x128@2x.png"
21 | cp "$dest/icon_128x128@2x.png" "$dest/icon_256x256.png"
22 | convert -background none -resize '!512x512' "$1" "$dest/icon_256x256@2x.png"
23 | cp "$dest/icon_256x256@2x.png" "$dest/icon_512x512.png"
24 | convert -background none -resize '!1024x1024' "$1" "$dest/icon_512x512@2x.png"
25 |
26 | iconutil -c icns "$dest"
27 | rm -R "$dest"
28 |
--------------------------------------------------------------------------------
/scripts/update-msvcredist.ps1:
--------------------------------------------------------------------------------
1 | $Urls = [ordered]@{ "X64" = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
2 | "ARM64" = "https://aka.ms/vs/17/release/vc_redist.arm64.exe"; }
3 |
4 | $UpgradeCodes = @{ "X64" = "36F68A90-239C-34DF-B58C-64B30153CE35";
5 | "ARM64" = "DC9BAE42-810B-423A-9E25-E4073F1C7B00"; }
6 |
7 | function Get-RedirectTarget([string]$Url) {
8 | return (Invoke-WebRequest -Method Get -Uri $Url -MaximumRedirection 0 -ErrorAction SilentlyContinue).Headers.Location
9 | }
10 |
11 | function Print-WixForArch([string]$Arch) {
12 | $targetUrl = Get-RedirectTarget $Urls[$Arch]
13 |
14 | $file = "$env:TEMP\\vc_redist.tmp"
15 | Invoke-WebRequest -Method Get -Uri $targetUrl -o $file
16 |
17 | $targetSize = (Get-Item $file).Length
18 | $targetVersion = (Get-Command $file).Version
19 | $targetSha512 = (Get-FileHash -Path $file -Algorithm SHA512).Hash
20 |
21 | Write-Output ""
22 | Write-Output ""
23 | Write-Output ""
24 | Write-Output ""
25 | Write-Output ""
26 |
27 | Remove-Item $file
28 | }
29 |
30 | foreach ($arch in $Urls.Keys) {
31 | Print-WixForArch $arch
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/vswhere.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/scripts/vswhere.exe
--------------------------------------------------------------------------------
/soundio/config.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonlight-stream/moonlight-qt/1dbdcb5279b3c2bce756e6eff3b97d3f12a38092/soundio/config.h
--------------------------------------------------------------------------------
/soundio/soundio.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2018-10-02T15:27:07
4 | #
5 | #-------------------------------------------------
6 |
7 | QT -= core gui
8 |
9 | TARGET = soundio
10 | TEMPLATE = lib
11 |
12 | # Build a static library
13 | CONFIG += staticlib
14 |
15 | # Disable warnings
16 | CONFIG += warn_off
17 |
18 | # Include global qmake defs
19 | include(../globaldefs.pri)
20 |
21 | # Force MSVC to compile C as C++ for atomic support
22 | *-msvc* {
23 | QMAKE_CFLAGS += /TP
24 | }
25 |
26 | # Older GCC versions defaulted to GNU89
27 | *-g++ {
28 | QMAKE_CFLAGS += -std=gnu99
29 | }
30 |
31 | unix:!macx {
32 | CONFIG += link_pkgconfig
33 |
34 | packagesExist(libpulse) {
35 | PKGCONFIG += libpulse
36 | CONFIG += pulseaudio
37 | }
38 | packagesExist(alsa) {
39 | PKGCONFIG += alsa
40 | CONFIG += alsa
41 | }
42 | }
43 |
44 | DEFINES += \
45 | SOUNDIO_STATIC_LIBRARY \
46 | SOUNDIO_VERSION_MAJOR=1 \
47 | SOUNDIO_VERSION_MINOR=1 \
48 | SOUNDIO_VERSION_PATCH=0 \
49 | SOUNDIO_VERSION_STRING=\\\"1.1.0\\\"
50 |
51 | SRC_DIR = $$PWD/libsoundio/src
52 | INC_DIR = $$PWD/libsoundio
53 |
54 | SOURCES += \
55 | $$SRC_DIR/channel_layout.c \
56 | $$SRC_DIR/dummy.c \
57 | $$SRC_DIR/os.c \
58 | $$SRC_DIR/ring_buffer.c \
59 | $$SRC_DIR/soundio.c \
60 | $$SRC_DIR/util.c
61 |
62 | HEADERS += \
63 | $$SRC_DIR/atomics.h \
64 | $$SRC_DIR/dummy.h \
65 | $$SRC_DIR/list.h \
66 | $$SRC_DIR/os.h \
67 | $$SRC_DIR/ring_buffer.h \
68 | $$SRC_DIR/soundio_internal.h \
69 | $$SRC_DIR/soundio_private.h \
70 | $$SRC_DIR/util.h \
71 | $$INC_DIR/soundio/soundio.h \
72 | $$INC_DIR/soundio/endian.h
73 |
74 | INCLUDEPATH += $$INC_DIR
75 |
76 | win32 {
77 | message(WASAPI backend selected)
78 |
79 | DEFINES += SOUNDIO_HAVE_WASAPI
80 | SOURCES += $$SRC_DIR/wasapi.c
81 | HEADERS += $$SRC_DIR/wasapi.h
82 | }
83 | macx {
84 | message(CoreAudio backend selected)
85 |
86 | DEFINES += SOUNDIO_HAVE_COREAUDIO
87 | SOURCES += $$SRC_DIR/coreaudio.c
88 | HEADERS += $$SRC_DIR/coreaudio.h
89 | }
90 | pulseaudio {
91 | message(PulseAudio backend selected)
92 |
93 | DEFINES += SOUNDIO_HAVE_PULSEAUDIO
94 | SOURCES += $$SRC_DIR/pulseaudio.c
95 | HEADERS += $$SRC_DIR/pulseaudio.h
96 | }
97 | alsa {
98 | message(ALSA backend selected)
99 |
100 | DEFINES += SOUNDIO_HAVE_ALSA
101 | SOURCES += $$SRC_DIR/alsa.c
102 | HEADERS += $$SRC_DIR/alsa.h
103 | }
104 |
--------------------------------------------------------------------------------
/wix/Moonlight.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2035
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "MoonlightSetup", "MoonlightSetup\MoonlightSetup.wixproj", "{466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}"
7 | EndProject
8 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Moonlight", "Moonlight\Moonlight.wixproj", "{8468EF94-3BB8-47A5-AF15-0E314AFE664D}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Debug|x64.ActiveCfg = Debug|x86
19 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Debug|x64.Build.0 = Debug|x86
20 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Debug|x86.ActiveCfg = Debug|x86
21 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Debug|x86.Build.0 = Debug|x86
22 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Release|x64.ActiveCfg = Release|x86
23 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Release|x64.Build.0 = Release|x86
24 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Release|x86.ActiveCfg = Release|x86
25 | {466FA35D-4BE4-40EF-9CE5-AFADC3B63BC5}.Release|x86.Build.0 = Release|x86
26 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Debug|x64.ActiveCfg = Debug|x64
27 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Debug|x64.Build.0 = Debug|x64
28 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Debug|x86.ActiveCfg = Debug|x86
29 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Debug|x86.Build.0 = Debug|x86
30 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Release|x64.ActiveCfg = Release|x64
31 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Release|x64.Build.0 = Release|x64
32 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Release|x86.ActiveCfg = Release|x86
33 | {8468EF94-3BB8-47A5-AF15-0E314AFE664D}.Release|x86.Build.0 = Release|x86
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {7EEF5F41-49B8-4803-9608-6EA127BE9AE1}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/wix/Moonlight/Moonlight.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 |
5 |
6 | $(BUILD_FOLDER)\
7 | $(BUILD_FOLDER)\
8 |
9 |
10 | DeployDir=$(DEPLOY_FOLDER);BuildDir=$(BUILD_FOLDER)
11 |
12 |
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/wix/MoonlightSetup/MoonlightSetup.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Bundle
4 |
5 |
6 | Debug
7 |
8 |
9 | $(INSTALLER_FOLDER)\
10 | $(BUILD_FOLDER)\
11 |
12 |
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------