├── .clang-format ├── .codeql-prebuild-cpp-Linux.sh ├── .codeql-prebuild-cpp-Windows.sh ├── .codeql-prebuild-cpp-macOS.sh ├── .dockerignore ├── .flake8 ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── config.yml ├── dependabot.yml ├── label-actions.yml ├── semantic.yml └── workflows │ ├── CI.yml │ ├── ci-copr.yml │ ├── ci-docker.yml │ ├── codeql.yml │ ├── common-lint.yml │ ├── issues.yml │ ├── localize.yml │ ├── release-notifier-moonlight.yml │ ├── release-notifier.yml │ ├── update-changelog.yml │ ├── update-docs.yml │ ├── update-flathub-repo.yml │ ├── update-homebrew-release.yml │ ├── update-pacman-repo.yml │ ├── update-pages.yml │ └── update-winget-release.yml ├── .gitignore ├── .gitmodules ├── .prettierrc.json ├── .readthedocs.yaml ├── .rstcheck.cfg ├── CMakeLists.txt ├── DOCKER_README.md ├── LICENSE ├── NOTICE ├── README.md ├── cmake ├── FindLIBCAP.cmake ├── FindLIBDRM.cmake ├── FindLibva.cmake ├── FindSystemd.cmake ├── FindUdev.cmake ├── FindWayland.cmake ├── compile_definitions │ ├── common.cmake │ ├── linux.cmake │ ├── macos.cmake │ ├── unix.cmake │ └── windows.cmake ├── dependencies │ ├── Boost_Sunshine.cmake │ ├── common.cmake │ ├── libevdev_Sunshine.cmake │ ├── linux.cmake │ ├── macos.cmake │ ├── nlohmann_json.cmake │ ├── unix.cmake │ └── windows.cmake ├── macros │ ├── common.cmake │ ├── linux.cmake │ ├── macos.cmake │ ├── unix.cmake │ └── windows.cmake ├── packaging │ ├── common.cmake │ ├── linux.cmake │ ├── macos.cmake │ ├── unix.cmake │ ├── windows.cmake │ ├── windows_nsis.cmake │ └── windows_wix.cmake ├── prep │ ├── build_version.cmake │ ├── constants.cmake │ ├── init.cmake │ ├── options.cmake │ └── special_package_configuration.cmake └── targets │ ├── common.cmake │ ├── linux.cmake │ ├── macos.cmake │ ├── unix.cmake │ └── windows.cmake ├── codecov.yml ├── crowdin.yml ├── docker ├── archlinux.dockerfile ├── clion-toolchain.dockerfile ├── debian-bookworm.dockerfile ├── ubuntu-22.04.dockerfile └── ubuntu-24.04.dockerfile ├── docs ├── Doxyfile ├── api.js ├── api.md ├── app_examples.md ├── awesome_sunshine.md ├── building.md ├── changelog.md ├── configuration.js ├── configuration.md ├── contributing.md ├── doc-styles.css ├── gamestream_migration.md ├── getting_started.md ├── guides.md ├── legal.md ├── performance_tuning.md ├── third_party_packages.md └── troubleshooting.md ├── gh-pages-template ├── .readthedocs.yaml ├── _config.yml ├── assets │ └── img │ │ ├── banners │ │ ├── AdobeStock_231616343.jpeg │ │ ├── AdobeStock_231616343_1920x1280.jpg │ │ ├── AdobeStock_303330124.jpeg │ │ ├── AdobeStock_303330124_1920x1280.jpg │ │ ├── AdobeStock_305732536.jpeg │ │ └── AdobeStock_305732536_1920x1280.jpg │ │ └── navbar-avatar.png └── index.html ├── package.json ├── packaging ├── linux │ ├── AppImage │ │ ├── AppRun │ │ └── sunshine.desktop │ ├── Arch │ │ ├── PKGBUILD │ │ └── sunshine.install │ ├── fedora │ │ ├── Sunshine.spec │ │ └── patches │ │ │ └── f42 │ │ │ ├── aarch64 │ │ │ └── 01-math_functions.patch │ │ │ └── x86_64 │ │ │ └── 01-math_functions.patch │ ├── flatpak │ │ ├── README.md │ │ ├── apps.json │ │ ├── dev.lizardbyte.app.Sunshine.metainfo.xml │ │ ├── dev.lizardbyte.app.Sunshine.yml │ │ ├── exceptions.json │ │ ├── flathub.json │ │ ├── modules │ │ │ ├── avahi.json │ │ │ ├── boost.json │ │ │ ├── cuda.json │ │ │ ├── libevdev.json │ │ │ ├── libnotify.json │ │ │ ├── miniupnpc.json │ │ │ ├── nlohmann_json.json │ │ │ ├── numactl.json │ │ │ └── xvfb │ │ │ │ ├── xvfb-run │ │ │ │ └── xvfb.json │ │ ├── scripts │ │ │ ├── additional-install.sh │ │ │ ├── remove-additional-install.sh │ │ │ └── sunshine.sh │ │ └── sunshine.desktop │ ├── sunshine.appdata.xml │ ├── sunshine.desktop │ ├── sunshine.service.in │ └── sunshine_terminal.desktop └── sunshine.rb ├── scripts ├── _locale.py ├── icons │ └── convert_and_pack.sh ├── linux_build.sh ├── requirements.txt └── update_clang_format.py ├── src ├── audio.cpp ├── audio.h ├── cbs.cpp ├── cbs.h ├── config.cpp ├── config.h ├── confighttp.cpp ├── confighttp.h ├── crypto.cpp ├── crypto.h ├── display_device.cpp ├── display_device.h ├── entry_handler.cpp ├── entry_handler.h ├── file_handler.cpp ├── file_handler.h ├── globals.cpp ├── globals.h ├── httpcommon.cpp ├── httpcommon.h ├── input.cpp ├── input.h ├── logging.cpp ├── logging.h ├── main.cpp ├── main.h ├── move_by_copy.h ├── network.cpp ├── network.h ├── nvenc │ ├── nvenc_base.cpp │ ├── nvenc_base.h │ ├── nvenc_colorspace.h │ ├── nvenc_config.h │ ├── nvenc_d3d11.cpp │ ├── nvenc_d3d11.h │ ├── nvenc_d3d11_native.cpp │ ├── nvenc_d3d11_native.h │ ├── nvenc_d3d11_on_cuda.cpp │ ├── nvenc_d3d11_on_cuda.h │ ├── nvenc_encoded_frame.h │ ├── nvenc_utils.cpp │ └── nvenc_utils.h ├── nvhttp.cpp ├── nvhttp.h ├── platform │ ├── common.h │ ├── linux │ │ ├── audio.cpp │ │ ├── cuda.cpp │ │ ├── cuda.cu │ │ ├── cuda.h │ │ ├── graphics.cpp │ │ ├── graphics.h │ │ ├── input │ │ │ ├── inputtino.cpp │ │ │ ├── inputtino_common.h │ │ │ ├── inputtino_gamepad.cpp │ │ │ ├── inputtino_gamepad.h │ │ │ ├── inputtino_keyboard.cpp │ │ │ ├── inputtino_keyboard.h │ │ │ ├── inputtino_mouse.cpp │ │ │ ├── inputtino_mouse.h │ │ │ ├── inputtino_pen.cpp │ │ │ ├── inputtino_pen.h │ │ │ ├── inputtino_touch.cpp │ │ │ └── inputtino_touch.h │ │ ├── kmsgrab.cpp │ │ ├── misc.cpp │ │ ├── misc.h │ │ ├── publish.cpp │ │ ├── vaapi.cpp │ │ ├── vaapi.h │ │ ├── wayland.cpp │ │ ├── wayland.h │ │ ├── wlgrab.cpp │ │ ├── x11grab.cpp │ │ └── x11grab.h │ ├── macos │ │ ├── av_audio.h │ │ ├── av_audio.m │ │ ├── av_img_t.h │ │ ├── av_video.h │ │ ├── av_video.m │ │ ├── display.mm │ │ ├── input.cpp │ │ ├── microphone.mm │ │ ├── misc.h │ │ ├── misc.mm │ │ ├── nv12_zero_device.cpp │ │ ├── nv12_zero_device.h │ │ └── publish.cpp │ └── windows │ │ ├── PolicyConfig.h │ │ ├── audio.cpp │ │ ├── display.h │ │ ├── display_base.cpp │ │ ├── display_ram.cpp │ │ ├── display_vram.cpp │ │ ├── display_wgc.cpp │ │ ├── input.cpp │ │ ├── keylayout.h │ │ ├── misc.cpp │ │ ├── misc.h │ │ ├── nvprefs │ │ ├── driver_settings.cpp │ │ ├── driver_settings.h │ │ ├── nvapi_opensource_wrapper.cpp │ │ ├── nvprefs_common.cpp │ │ ├── nvprefs_common.h │ │ ├── nvprefs_interface.cpp │ │ ├── nvprefs_interface.h │ │ ├── undo_data.cpp │ │ ├── undo_data.h │ │ ├── undo_file.cpp │ │ └── undo_file.h │ │ ├── publish.cpp │ │ └── windows.rc.in ├── process.cpp ├── process.h ├── round_robin.h ├── rswrapper.c ├── rswrapper.h ├── rtsp.cpp ├── rtsp.h ├── stat_trackers.cpp ├── stat_trackers.h ├── stream.cpp ├── stream.h ├── sync.h ├── system_tray.cpp ├── system_tray.h ├── task_pool.h ├── thread_pool.h ├── thread_safe.h ├── upnp.cpp ├── upnp.h ├── utility.h ├── uuid.h ├── version.h.in ├── video.cpp ├── video.h ├── video_colorspace.cpp └── video_colorspace.h ├── src_assets ├── common │ └── assets │ │ ├── box.png │ │ ├── desktop-alt.png │ │ ├── desktop.png │ │ ├── steam.png │ │ └── web │ │ ├── Checkbox.vue │ │ ├── Navbar.vue │ │ ├── PlatformLayout.vue │ │ ├── ResourceCard.vue │ │ ├── ThemeToggle.vue │ │ ├── apps.html │ │ ├── config.html │ │ ├── configs │ │ └── tabs │ │ │ ├── Advanced.vue │ │ │ ├── AudioVideo.vue │ │ │ ├── ContainerEncoders.vue │ │ │ ├── Files.vue │ │ │ ├── General.vue │ │ │ ├── Inputs.vue │ │ │ ├── Network.vue │ │ │ ├── audiovideo │ │ │ ├── AdapterNameSelector.vue │ │ │ ├── DisplayDeviceOptions.vue │ │ │ ├── DisplayModesSettings.vue │ │ │ └── DisplayOutputSelector.vue │ │ │ └── encoders │ │ │ ├── AmdAmfEncoder.vue │ │ │ ├── IntelQuickSyncEncoder.vue │ │ │ ├── NvidiaNvencEncoder.vue │ │ │ ├── SoftwareEncoder.vue │ │ │ ├── VAAPIEncoder.vue │ │ │ └── VideotoolboxEncoder.vue │ │ ├── index.html │ │ ├── init.js │ │ ├── locale.js │ │ ├── password.html │ │ ├── pin.html │ │ ├── platform-i18n.js │ │ ├── public │ │ ├── assets │ │ │ ├── css │ │ │ │ └── sunshine.css │ │ │ └── locale │ │ │ │ ├── bg.json │ │ │ │ ├── de.json │ │ │ │ ├── en.json │ │ │ │ ├── en_GB.json │ │ │ │ ├── en_US.json │ │ │ │ ├── es.json │ │ │ │ ├── fr.json │ │ │ │ ├── it.json │ │ │ │ ├── ja.json │ │ │ │ ├── ko.json │ │ │ │ ├── pl.json │ │ │ │ ├── pt.json │ │ │ │ ├── pt_BR.json │ │ │ │ ├── ru.json │ │ │ │ ├── sv.json │ │ │ │ ├── tr.json │ │ │ │ ├── uk.json │ │ │ │ └── zh.json │ │ └── images │ │ │ ├── logo-sunshine-16.png │ │ │ ├── logo-sunshine-45.png │ │ │ ├── sunshine-locked-16.png │ │ │ ├── sunshine-locked-45.png │ │ │ ├── sunshine-locked.ico │ │ │ ├── sunshine-locked.png │ │ │ ├── sunshine-locked.svg │ │ │ ├── sunshine-pausing-16.png │ │ │ ├── sunshine-pausing-45.png │ │ │ ├── sunshine-pausing.ico │ │ │ ├── sunshine-pausing.png │ │ │ ├── sunshine-pausing.svg │ │ │ ├── sunshine-playing-16.png │ │ │ ├── sunshine-playing-45.png │ │ │ ├── sunshine-playing.ico │ │ │ ├── sunshine-playing.png │ │ │ ├── sunshine-playing.svg │ │ │ └── sunshine.ico │ │ ├── sunshine_version.js │ │ ├── template_header.html │ │ ├── theme.js │ │ ├── troubleshooting.html │ │ └── welcome.html ├── linux │ ├── assets │ │ ├── apps.json │ │ └── shaders │ │ │ └── opengl │ │ │ ├── ConvertUV.frag │ │ │ ├── ConvertUV.vert │ │ │ ├── ConvertY.frag │ │ │ ├── Scene.frag │ │ │ └── Scene.vert │ └── misc │ │ ├── 60-sunshine.rules │ │ └── postinst ├── macos │ ├── assets │ │ ├── Info.plist │ │ └── apps.json │ └── misc │ │ └── uninstall_pkg.sh └── windows │ ├── assets │ ├── apps.json │ └── shaders │ │ └── directx │ │ ├── convert_yuv420_packed_uv_type0_ps.hlsl │ │ ├── convert_yuv420_packed_uv_type0_ps_linear.hlsl │ │ ├── convert_yuv420_packed_uv_type0_ps_perceptual_quantizer.hlsl │ │ ├── convert_yuv420_packed_uv_type0_vs.hlsl │ │ ├── convert_yuv420_packed_uv_type0s_ps.hlsl │ │ ├── convert_yuv420_packed_uv_type0s_ps_linear.hlsl │ │ ├── convert_yuv420_packed_uv_type0s_ps_perceptual_quantizer.hlsl │ │ ├── convert_yuv420_packed_uv_type0s_vs.hlsl │ │ ├── convert_yuv420_planar_y_ps.hlsl │ │ ├── convert_yuv420_planar_y_ps_linear.hlsl │ │ ├── convert_yuv420_planar_y_ps_perceptual_quantizer.hlsl │ │ ├── convert_yuv420_planar_y_vs.hlsl │ │ ├── convert_yuv444_packed_ayuv_ps.hlsl │ │ ├── convert_yuv444_packed_ayuv_ps_linear.hlsl │ │ ├── convert_yuv444_packed_vs.hlsl │ │ ├── convert_yuv444_packed_y410_ps.hlsl │ │ ├── convert_yuv444_packed_y410_ps_linear.hlsl │ │ ├── convert_yuv444_packed_y410_ps_perceptual_quantizer.hlsl │ │ ├── convert_yuv444_planar_ps.hlsl │ │ ├── convert_yuv444_planar_ps_linear.hlsl │ │ ├── convert_yuv444_planar_ps_perceptual_quantizer.hlsl │ │ ├── convert_yuv444_planar_vs.hlsl │ │ ├── cursor_ps.hlsl │ │ ├── cursor_ps_normalize_white.hlsl │ │ ├── cursor_vs.hlsl │ │ └── include │ │ ├── base_vs.hlsl │ │ ├── base_vs_types.hlsl │ │ ├── common.hlsl │ │ ├── convert_base.hlsl │ │ ├── convert_linear_base.hlsl │ │ ├── convert_perceptual_quantizer_base.hlsl │ │ ├── convert_yuv420_packed_uv_ps_base.hlsl │ │ ├── convert_yuv420_planar_y_ps_base.hlsl │ │ └── convert_yuv444_ps_base.hlsl │ └── misc │ ├── autostart │ └── autostart-service.bat │ ├── firewall │ ├── add-firewall-rule.bat │ └── delete-firewall-rule.bat │ ├── gamepad │ ├── install-gamepad.bat │ └── uninstall-gamepad.bat │ ├── migration │ └── migrate-config.bat │ ├── path │ └── update-path.bat │ └── service │ ├── install-service.bat │ └── uninstall-service.bat ├── sunshine.icns ├── sunshine.ico ├── sunshine.png ├── sunshine.svg ├── tests ├── CMakeLists.txt ├── fixtures │ └── http │ │ ├── hello-redirect.txt │ │ └── hello.txt ├── tests_common.h ├── tests_environment.h ├── tests_events.h ├── tests_log_checker.h ├── tests_main.cpp └── unit │ ├── platform │ └── test_common.cpp │ ├── test_audio.cpp │ ├── test_display_device.cpp │ ├── test_entry_handler.cpp │ ├── test_file_handler.cpp │ ├── test_http_pairing.cpp │ ├── test_httpcommon.cpp │ ├── test_logging.cpp │ ├── test_mouse.cpp │ ├── test_network.cpp │ ├── test_rswrapper.cpp │ ├── test_stream.cpp │ └── test_video.cpp ├── third-party ├── .clang-format-ignore ├── glad │ ├── include │ │ ├── EGL │ │ │ └── eglplatform.h │ │ ├── KHR │ │ │ └── khrplatform.h │ │ └── glad │ │ │ ├── egl.h │ │ │ └── gl.h │ └── src │ │ ├── egl.c │ │ └── gl.c └── nvfbc │ ├── NvFBC.h │ └── helper_math.h ├── tools ├── CMakeLists.txt ├── audio.cpp ├── dxgi.cpp └── sunshinesvc.cpp └── vite.config.js /.codeql-prebuild-cpp-Linux.sh: -------------------------------------------------------------------------------- 1 | # install dependencies for C++ analysis 2 | set -e 3 | 4 | chmod +x ./scripts/linux_build.sh 5 | ./scripts/linux_build.sh --skip-package --ubuntu-test-repo 6 | 7 | # Delete CUDA 8 | rm -rf ./build/cuda 9 | 10 | # skip autobuild 11 | echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" 12 | -------------------------------------------------------------------------------- /.codeql-prebuild-cpp-Windows.sh: -------------------------------------------------------------------------------- 1 | # install dependencies for C++ analysis 2 | set -e 3 | 4 | # update pacman 5 | pacman --noconfirm -Syu 6 | 7 | gcc_version="14.2.0-3" 8 | 9 | broken_deps=( 10 | "mingw-w64-ucrt-x86_64-gcc" 11 | "mingw-w64-ucrt-x86_64-gcc-libs" 12 | ) 13 | 14 | tarballs="" 15 | for dep in "${broken_deps[@]}"; do 16 | tarball="${dep}-${gcc_version}-any.pkg.tar.zst" 17 | 18 | # download and install working version 19 | wget https://repo.msys2.org/mingw/ucrt64/${tarball} 20 | 21 | tarballs="${tarballs} ${tarball}" 22 | done 23 | 24 | # install broken dependencies 25 | if [ -n "$tarballs" ]; then 26 | pacman -U --noconfirm ${tarballs} 27 | fi 28 | 29 | # install dependencies 30 | dependencies=( 31 | "git" 32 | "mingw-w64-ucrt-x86_64-cmake" 33 | "mingw-w64-ucrt-x86_64-cppwinrt" 34 | "mingw-w64-ucrt-x86_64-curl-winssl" 35 | "mingw-w64-ucrt-x86_64-MinHook" 36 | "mingw-w64-ucrt-x86_64-miniupnpc" 37 | "mingw-w64-ucrt-x86_64-nlohmann-json" 38 | "mingw-w64-ucrt-x86_64-nodejs" 39 | "mingw-w64-ucrt-x86_64-nsis" 40 | "mingw-w64-ucrt-x86_64-onevpl" 41 | "mingw-w64-ucrt-x86_64-openssl" 42 | "mingw-w64-ucrt-x86_64-opus" 43 | "mingw-w64-ucrt-x86_64-toolchain" 44 | ) 45 | 46 | pacman -Syu --noconfirm --ignore="$(IFS=,; echo "${broken_deps[*]}")" "${dependencies[@]}" 47 | 48 | # build 49 | mkdir -p build 50 | cmake \ 51 | -B build \ 52 | -G Ninja \ 53 | -S . \ 54 | -DBUILD_DOCS=OFF \ 55 | -DBUILD_WERROR=ON 56 | ninja -C build 57 | 58 | # skip autobuild 59 | echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" 60 | -------------------------------------------------------------------------------- /.codeql-prebuild-cpp-macOS.sh: -------------------------------------------------------------------------------- 1 | # install dependencies for C++ analysis 2 | set -e 3 | 4 | # setup homebrew for x86_64 5 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 6 | eval "$(/usr/local/bin/brew shellenv)" 7 | 8 | # install dependencies 9 | dependencies=( 10 | "cmake" 11 | "miniupnpc" 12 | "ninja" 13 | "node" 14 | "openssl@3" 15 | "opus" 16 | "pkg-config" 17 | ) 18 | brew install "${dependencies[@]}" 19 | 20 | # build 21 | mkdir -p build 22 | cmake \ 23 | -B build \ 24 | -G Ninja \ 25 | -S . \ 26 | -DBOOST_USE_STATIC=OFF \ 27 | -DBUILD_DOCS=OFF \ 28 | -DBUILD_WERROR=ON 29 | ninja -C build 30 | 31 | # skip autobuild 32 | echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" 33 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # ignore hidden files 2 | .* 3 | 4 | # do not ignore .git, needed for versioning 5 | !/.git 6 | 7 | # do not ignore .rstcheck.cfg, needed to test building docs 8 | !/.rstcheck.cfg 9 | 10 | # ignore repo directories and files 11 | docker/ 12 | gh-pages-template/ 13 | scripts/ 14 | tools/ 15 | crowdin.yml 16 | 17 | # don't ignore linux build script 18 | !scripts/linux_build.sh 19 | 20 | # ignore dev directories 21 | build/ 22 | cmake-*/ 23 | venv/ 24 | 25 | # ignore artifacts 26 | artifacts/ 27 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | filename = 3 | *.py 4 | max-line-length = 120 5 | extend-exclude = 6 | venv/ 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ensure dockerfiles are checked out with LF line endings 2 | Dockerfile text eol=lf 3 | *.dockerfile text eol=lf 4 | 5 | # ensure flatpak lint json files are checked out with LF line endings 6 | *flatpak-lint-*.json text eol=lf 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | blank_issues_enabled: false 7 | contact_links: 8 | - name: Discussions 9 | url: https://github.com/orgs/LizardByte/discussions 10 | about: Community discussions 11 | - name: Questions 12 | url: https://github.com/orgs/LizardByte/discussions 13 | about: Ask questions 14 | - name: Feature Requests 15 | url: https://github.com/orgs/LizardByte/discussions 16 | about: Request new features 17 | - name: Support Center 18 | url: https://app.lizardbyte.dev/support 19 | about: Official LizardByte support 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | time: "07:30" 13 | open-pull-requests-limit: 10 14 | 15 | - package-ecosystem: "docker" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | time: "08:00" 20 | open-pull-requests-limit: 10 21 | 22 | - package-ecosystem: "github-actions" 23 | directory: "/" 24 | schedule: 25 | interval: "daily" 26 | time: "08:30" 27 | open-pull-requests-limit: 10 28 | groups: 29 | docker-actions: 30 | applies-to: version-updates 31 | patterns: 32 | - "docker/*" 33 | github-actions: 34 | applies-to: version-updates 35 | patterns: 36 | - "actions/*" 37 | - "github/*" 38 | lizardbyte-actions: 39 | applies-to: version-updates 40 | patterns: 41 | - "LizardByte/*" 42 | 43 | - package-ecosystem: "npm" 44 | directory: "/" 45 | schedule: 46 | interval: "daily" 47 | time: "09:00" 48 | open-pull-requests-limit: 10 49 | groups: 50 | dev-dependencies: 51 | applies-to: version-updates 52 | dependency-type: "development" 53 | 54 | - package-ecosystem: "nuget" 55 | directory: "/" 56 | schedule: 57 | interval: "daily" 58 | time: "09:30" 59 | open-pull-requests-limit: 10 60 | 61 | - package-ecosystem: "pip" 62 | directory: "/" 63 | schedule: 64 | interval: "daily" 65 | time: "10:00" 66 | open-pull-requests-limit: 10 67 | groups: 68 | pytest-dependencies: 69 | applies-to: version-updates 70 | patterns: 71 | - "pytest*" 72 | 73 | - package-ecosystem: "gitsubmodule" 74 | directory: "/" 75 | schedule: 76 | interval: "daily" 77 | time: "10:30" 78 | open-pull-requests-limit: 10 79 | -------------------------------------------------------------------------------- /.github/label-actions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Configuration for Label Actions - https://github.com/dessant/label-actions 7 | 8 | added: 9 | comment: > 10 | This feature has been added and will be available in the next release. 11 | fixed: 12 | comment: > 13 | This issue has been fixed and will be available in the next release. 14 | invalid:duplicate: 15 | comment: > 16 | :wave: @{issue-author}, this appears to be a duplicate of a pre-existing issue. 17 | close: true 18 | lock: true 19 | unlabel: 'status:awaiting-triage' 20 | 21 | -invalid:duplicate: 22 | reopen: true 23 | unlock: true 24 | 25 | invalid:support: 26 | comment: > 27 | :wave: @{issue-author}, we use the issue tracker exclusively for bug reports. 28 | However, this issue appears to be a support request. Please use our 29 | [Support Center](https://app.lizardbyte.dev/support) for support issues. Thanks. 30 | close: true 31 | lock: true 32 | lock-reason: 'off-topic' 33 | unlabel: 'status:awaiting-triage' 34 | 35 | -invalid:support: 36 | reopen: true 37 | unlock: true 38 | 39 | invalid:template-incomplete: 40 | issues: 41 | comment: > 42 | :wave: @{issue-author}, please edit your issue to complete the template with 43 | all the required info. Your issue will be automatically closed in 5 days if 44 | the template is not completed. Thanks. 45 | prs: 46 | comment: > 47 | :wave: @{issue-author}, please edit your PR to complete the template with 48 | all the required info. Your PR will be automatically closed in 5 days if 49 | the template is not completed. Thanks. 50 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # This is the configuration file for https://github.com/Ezard/semantic-prs 7 | 8 | enabled: true 9 | titleOnly: true # We only use the PR title as we squash and merge 10 | commitsOnly: false 11 | titleAndCommits: false 12 | anyCommit: false 13 | allowMergeCommits: false 14 | allowRevertCommits: false 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-copr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI Copr 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - master 10 | types: 11 | - opened 12 | - synchronize 13 | - reopened 14 | release: 15 | types: 16 | - prereleased 17 | - released 18 | 19 | concurrency: 20 | group: "${{ github.workflow }}-${{ github.ref }}" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | call-copr-ci: 25 | uses: LizardByte/copr-ci/.github/workflows/copr-ci.yml@master 26 | with: 27 | copr_pr_webhook_token: "05fc9b07-a19b-4f83-89b2-ae1e7e0b5282" 28 | github_org_owner: LizardByte 29 | copr_ownername: lizardbyte 30 | auto_update_package: true 31 | job_timeout: 90 32 | secrets: 33 | COPR_BETA_WEBHOOK_TOKEN: ${{ secrets.COPR_BETA_WEBHOOK_TOKEN }} 34 | COPR_STABLE_WEBHOOK_TOKEN: ${{ secrets.COPR_STABLE_WEBHOOK_TOKEN }} 35 | COPR_CLI_CONFIG: ${{ secrets.COPR_CLI_CONFIG }} 36 | -------------------------------------------------------------------------------- /.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Label and un-label actions using `../label-actions.yml`. 7 | 8 | name: Issues 9 | permissions: {} 10 | 11 | on: 12 | issues: 13 | types: 14 | - labeled 15 | - unlabeled 16 | discussion: 17 | types: 18 | - labeled 19 | - unlabeled 20 | 21 | jobs: 22 | label: 23 | name: Label Actions 24 | if: startsWith(github.repository, 'LizardByte/') 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Label Actions 28 | uses: dessant/label-actions@v4 29 | with: 30 | github-token: ${{ secrets.GH_BOT_TOKEN }} 31 | -------------------------------------------------------------------------------- /.github/workflows/release-notifier-moonlight.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Notifications (Moonlight) 3 | permissions: {} 4 | 5 | on: 6 | release: 7 | types: 8 | - released # this triggers when a release is published, but does not include prereleases or drafts 9 | 10 | jobs: 11 | discord: 12 | if: github.repository_owner == 'LizardByte' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check if latest GitHub release 16 | id: check-release 17 | uses: actions/github-script@v7 18 | with: 19 | script: | 20 | const latestRelease = await github.rest.repos.getLatestRelease({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo 23 | }); 24 | 25 | core.setOutput('isLatestRelease', latestRelease.data.tag_name === context.payload.release.tag_name); 26 | 27 | - name: discord 28 | if: steps.check-release.outputs.isLatestRelease == 'true' 29 | uses: sarisia/actions-status-discord@v1 30 | with: 31 | avatar_url: ${{ vars.ORG_LOGO_URL }}256 32 | color: 0x${{ vars.COLOR_HEX_GREEN }} 33 | description: ${{ github.event.release.body }} 34 | nodetail: true 35 | nofail: false 36 | title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released 37 | username: ${{ secrets.DISCORD_USERNAME }} 38 | webhook: ${{ secrets.DISCORD_RELEASE_WEBHOOK_MOONLIGHT }} 39 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Update changelog on release events. 7 | 8 | name: Update changelog 9 | permissions: 10 | contents: read 11 | 12 | on: 13 | release: 14 | types: 15 | - created 16 | - edited 17 | - deleted 18 | workflow_dispatch: 19 | 20 | concurrency: 21 | group: "${{ github.workflow }}" 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | update-changelog: 26 | name: Update Changelog 27 | if: >- 28 | github.event_name == 'workflow_dispatch' || 29 | (!github.event.release.prerelease && !github.event.release.draft) 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Update Changelog 33 | uses: LizardByte/update-changelog-action@v2025.426.173858 34 | with: 35 | changelogBranch: changelog 36 | changelogFile: CHANGELOG.md 37 | token: ${{ secrets.GH_BOT_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/update-pages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build GH-Pages 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - master 10 | types: 11 | - opened 12 | - synchronize 13 | - reopened 14 | push: 15 | branches: 16 | - master 17 | workflow_dispatch: 18 | 19 | concurrency: 20 | group: "${{ github.workflow }}-${{ github.ref }}" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | prep: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Upload artifact 31 | uses: actions/upload-artifact@v4 32 | with: 33 | name: prep 34 | path: gh-pages-template/ 35 | if-no-files-found: error 36 | include-hidden-files: true 37 | retention-days: 1 38 | 39 | call-jekyll-build: 40 | needs: prep 41 | uses: LizardByte/LizardByte.github.io/.github/workflows/jekyll-build.yml@master 42 | secrets: 43 | GH_BOT_EMAIL: ${{ secrets.GH_BOT_EMAIL }} 44 | GH_BOT_NAME: ${{ secrets.GH_BOT_NAME }} 45 | GH_BOT_TOKEN: ${{ secrets.GH_BOT_TOKEN }} 46 | with: 47 | clean_gh_pages: true 48 | site_artifact: 'prep' 49 | target_branch: 'gh-pages' 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # JetBrains IDE 35 | .idea/ 36 | 37 | # VSCode IDE 38 | .vscode/ 39 | 40 | # build directories 41 | build/ 42 | cmake-*/ 43 | docs/doxyconfig* 44 | 45 | # npm 46 | node_modules/ 47 | package-lock.json 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Dummy macOS files 54 | .DS_Store 55 | 56 | # Python 57 | *.pyc 58 | venv/ 59 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # .readthedocs.yaml 3 | # Read the Docs configuration file 4 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 5 | 6 | version: 2 7 | 8 | build: 9 | os: ubuntu-24.04 10 | tools: 11 | python: "miniconda-latest" 12 | commands: 13 | - | 14 | if [ -f readthedocs_build.sh ]; then 15 | doxyconfig_dir="." 16 | else 17 | doxyconfig_dir="./third-party/doxyconfig" 18 | fi 19 | chmod +x "${doxyconfig_dir}/readthedocs_build.sh" 20 | export DOXYCONFIG_DIR="${doxyconfig_dir}" 21 | "${doxyconfig_dir}/readthedocs_build.sh" 22 | 23 | # using conda, we can get newer doxygen and graphviz than ubuntu provide 24 | # https://github.com/readthedocs/readthedocs.org/issues/8151#issuecomment-890359661 25 | conda: 26 | environment: third-party/doxyconfig/environment.yml 27 | 28 | submodules: 29 | include: all 30 | recursive: true 31 | -------------------------------------------------------------------------------- /.rstcheck.cfg: -------------------------------------------------------------------------------- 1 | # configuration file for rstcheck, an rst linting tool 2 | # https://rstcheck.readthedocs.io/en/latest/usage/config 3 | 4 | [rstcheck] 5 | ignore_directives = 6 | doxygenfile, 7 | include, 8 | mdinclude, 9 | tab, 10 | todo, 11 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ©2018 Valve Corporation. Steam and the Steam logo are trademarks and/or 2 | registered trademarks of Valve Corporation in the U.S. and/or other countries. All 3 | rights reserved. -------------------------------------------------------------------------------- /cmake/FindLIBCAP.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Libcap 2 | # Once done this will define 3 | # 4 | # LIBCAP_FOUND - system has Libcap 5 | # LIBCAP_INCLUDE_DIRS - the Libcap include directory 6 | # LIBCAP_LIBRARIES - the libraries needed to use Libcap 7 | # LIBCAP_DEFINITIONS - Compiler switches required for using Libcap 8 | 9 | # Use pkg-config to get the directories and then use these values 10 | # in the find_path() and find_library() calls 11 | find_package(PkgConfig) 12 | pkg_check_modules(PC_LIBCAP libcap) 13 | 14 | set(LIBCAP_DEFINITIONS ${PC_LIBCAP_CFLAGS}) 15 | 16 | find_path(LIBCAP_INCLUDE_DIRS sys/capability.h PATHS ${PC_LIBCAP_INCLUDEDIR} ${PC_LIBCAP_INCLUDE_DIRS}) 17 | find_library(LIBCAP_LIBRARIES NAMES libcap.so PATHS ${PC_LIBCAP_LIBDIR} ${PC_LIBCAP_LIBRARY_DIRS}) 18 | mark_as_advanced(LIBCAP_INCLUDE_DIRS LIBCAP_LIBRARIES) 19 | 20 | include(FindPackageHandleStandardArgs) 21 | find_package_handle_standard_args(LIBCAP REQUIRED_VARS LIBCAP_LIBRARIES LIBCAP_INCLUDE_DIRS) 22 | -------------------------------------------------------------------------------- /cmake/FindLIBDRM.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Libdrm 2 | # Once done this will define 3 | # 4 | # LIBDRM_FOUND - system has Libdrm 5 | # LIBDRM_INCLUDE_DIRS - the Libdrm include directory 6 | # LIBDRM_LIBRARIES - the libraries needed to use Libdrm 7 | # LIBDRM_DEFINITIONS - Compiler switches required for using Libdrm 8 | 9 | # Use pkg-config to get the directories and then use these values 10 | # in the find_path() and find_library() calls 11 | find_package(PkgConfig) 12 | pkg_check_modules(PC_LIBDRM libdrm) 13 | 14 | set(LIBDRM_DEFINITIONS ${PC_LIBDRM_CFLAGS}) 15 | 16 | find_path(LIBDRM_INCLUDE_DIRS drm.h PATHS ${PC_LIBDRM_INCLUDEDIR} ${PC_LIBDRM_INCLUDE_DIRS} PATH_SUFFIXES libdrm) 17 | find_library(LIBDRM_LIBRARIES NAMES libdrm.so PATHS ${PC_LIBDRM_LIBDIR} ${PC_LIBDRM_LIBRARY_DIRS}) 18 | mark_as_advanced(LIBDRM_INCLUDE_DIRS LIBDRM_LIBRARIES) 19 | 20 | include(FindPackageHandleStandardArgs) 21 | find_package_handle_standard_args(LIBDRM REQUIRED_VARS LIBDRM_LIBRARIES LIBDRM_INCLUDE_DIRS) 22 | -------------------------------------------------------------------------------- /cmake/FindSystemd.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Systemd 2 | # Once done this will define 3 | # 4 | # SYSTEMD_FOUND - system has systemd 5 | # SYSTEMD_USER_UNIT_INSTALL_DIR - the systemd system unit install directory 6 | # SYSTEMD_SYSTEM_UNIT_INSTALL_DIR - the systemd user unit install directory 7 | 8 | IF (NOT WIN32) 9 | 10 | find_package(PkgConfig QUIET) 11 | if(PKG_CONFIG_FOUND) 12 | pkg_check_modules(SYSTEMD "systemd") 13 | endif() 14 | 15 | if (SYSTEMD_FOUND) 16 | execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} 17 | --variable=systemduserunitdir systemd 18 | OUTPUT_VARIABLE SYSTEMD_USER_UNIT_INSTALL_DIR) 19 | 20 | string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_USER_UNIT_INSTALL_DIR 21 | "${SYSTEMD_USER_UNIT_INSTALL_DIR}") 22 | 23 | execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} 24 | --variable=systemdsystemunitdir systemd 25 | OUTPUT_VARIABLE SYSTEMD_SYSTEM_UNIT_INSTALL_DIR) 26 | 27 | string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SYSTEM_UNIT_INSTALL_DIR 28 | "${SYSTEMD_SYSTEM_UNIT_INSTALL_DIR}") 29 | 30 | mark_as_advanced(SYSTEMD_USER_UNIT_INSTALL_DIR SYSTEMD_SYSTEM_UNIT_INSTALL_DIR) 31 | 32 | endif () 33 | 34 | ENDIF () 35 | -------------------------------------------------------------------------------- /cmake/FindUdev.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Udev 2 | # Once done this will define 3 | # 4 | # UDEV_FOUND - system has udev 5 | # UDEV_RULES_INSTALL_DIR - the udev rules install directory 6 | 7 | IF (NOT WIN32) 8 | 9 | find_package(PkgConfig QUIET) 10 | if(PKG_CONFIG_FOUND) 11 | pkg_check_modules(UDEV "udev") 12 | endif() 13 | 14 | if (UDEV_FOUND) 15 | execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} 16 | --variable=udevdir udev 17 | OUTPUT_VARIABLE UDEV_RULES_INSTALL_DIR) 18 | 19 | string(REGEX REPLACE "[ \t\n]+" "" UDEV_RULES_INSTALL_DIR 20 | "${UDEV_RULES_INSTALL_DIR}") 21 | 22 | set(UDEV_RULES_INSTALL_DIR "${UDEV_RULES_INSTALL_DIR}/rules.d") 23 | 24 | mark_as_advanced(UDEV_RULES_INSTALL_DIR) 25 | 26 | endif () 27 | 28 | ENDIF () 29 | -------------------------------------------------------------------------------- /cmake/compile_definitions/macos.cmake: -------------------------------------------------------------------------------- 1 | # macos specific compile definitions 2 | 3 | add_compile_definitions(SUNSHINE_PLATFORM="macos") 4 | 5 | set(MACOS_LINK_DIRECTORIES 6 | /opt/homebrew/lib 7 | /opt/local/lib 8 | /usr/local/lib) 9 | 10 | foreach(dir ${MACOS_LINK_DIRECTORIES}) 11 | if(EXISTS ${dir}) 12 | link_directories(${dir}) 13 | endif() 14 | endforeach() 15 | 16 | if(NOT BOOST_USE_STATIC AND NOT FETCH_CONTENT_BOOST_USED) 17 | ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK) 18 | endif() 19 | 20 | list(APPEND SUNSHINE_EXTERNAL_LIBRARIES 21 | ${APP_KIT_LIBRARY} 22 | ${APP_SERVICES_LIBRARY} 23 | ${AV_FOUNDATION_LIBRARY} 24 | ${CORE_MEDIA_LIBRARY} 25 | ${CORE_VIDEO_LIBRARY} 26 | ${FOUNDATION_LIBRARY} 27 | ${VIDEO_TOOLBOX_LIBRARY}) 28 | 29 | set(APPLE_PLIST_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/Info.plist") 30 | 31 | # todo - tray is not working on macos 32 | set(SUNSHINE_TRAY 0) 33 | 34 | set(PLATFORM_TARGET_FILES 35 | "${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.h" 36 | "${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.m" 37 | "${CMAKE_SOURCE_DIR}/src/platform/macos/av_img_t.h" 38 | "${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.h" 39 | "${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.m" 40 | "${CMAKE_SOURCE_DIR}/src/platform/macos/display.mm" 41 | "${CMAKE_SOURCE_DIR}/src/platform/macos/input.cpp" 42 | "${CMAKE_SOURCE_DIR}/src/platform/macos/microphone.mm" 43 | "${CMAKE_SOURCE_DIR}/src/platform/macos/misc.mm" 44 | "${CMAKE_SOURCE_DIR}/src/platform/macos/misc.h" 45 | "${CMAKE_SOURCE_DIR}/src/platform/macos/nv12_zero_device.cpp" 46 | "${CMAKE_SOURCE_DIR}/src/platform/macos/nv12_zero_device.h" 47 | "${CMAKE_SOURCE_DIR}/src/platform/macos/publish.cpp" 48 | "${CMAKE_SOURCE_DIR}/third-party/TPCircularBuffer/TPCircularBuffer.c" 49 | "${CMAKE_SOURCE_DIR}/third-party/TPCircularBuffer/TPCircularBuffer.h" 50 | ${APPLE_PLIST_FILE}) 51 | 52 | if(SUNSHINE_ENABLE_TRAY) 53 | list(APPEND SUNSHINE_EXTERNAL_LIBRARIES 54 | ${COCOA}) 55 | list(APPEND PLATFORM_TARGET_FILES 56 | "${CMAKE_SOURCE_DIR}/third-party/tray/src/tray_darwin.m") 57 | endif() 58 | -------------------------------------------------------------------------------- /cmake/compile_definitions/unix.cmake: -------------------------------------------------------------------------------- 1 | # unix specific compile definitions 2 | # put anything here that applies to both linux and macos 3 | 4 | list(APPEND SUNSHINE_EXTERNAL_LIBRARIES 5 | ${CURL_LIBRARIES}) 6 | 7 | # add install prefix to assets path if not already there 8 | if(NOT SUNSHINE_ASSETS_DIR MATCHES "^${CMAKE_INSTALL_PREFIX}") 9 | set(SUNSHINE_ASSETS_DIR "${CMAKE_INSTALL_PREFIX}/${SUNSHINE_ASSETS_DIR}") 10 | endif() 11 | -------------------------------------------------------------------------------- /cmake/dependencies/libevdev_Sunshine.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Loads the libevdev library giving the priority to the system package first, with a fallback to ExternalProject 3 | # 4 | include_guard(GLOBAL) 5 | 6 | set(LIBEVDEV_VERSION libevdev-1.13.2) 7 | 8 | pkg_check_modules(PC_EVDEV libevdev) 9 | if(PC_EVDEV_FOUND) 10 | find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h 11 | HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR}) 12 | find_library(EVDEV_LIBRARY 13 | NAMES evdev libevdev) 14 | else() 15 | include(ExternalProject) 16 | 17 | ExternalProject_Add(libevdev 18 | URL http://www.freedesktop.org/software/libevdev/${LIBEVDEV_VERSION}.tar.xz 19 | PREFIX ${LIBEVDEV_VERSION} 20 | CONFIGURE_COMMAND /configure --prefix= 21 | BUILD_COMMAND "make" 22 | INSTALL_COMMAND "" 23 | ) 24 | 25 | ExternalProject_Get_Property(libevdev SOURCE_DIR) 26 | message(STATUS "libevdev source dir: ${SOURCE_DIR}") 27 | set(EVDEV_INCLUDE_DIR "${SOURCE_DIR}") 28 | 29 | ExternalProject_Get_Property(libevdev BINARY_DIR) 30 | message(STATUS "libevdev binary dir: ${BINARY_DIR}") 31 | set(EVDEV_LIBRARY "${BINARY_DIR}/libevdev/.libs/libevdev.a") 32 | 33 | # compile libevdev before sunshine 34 | set(SUNSHINE_TARGET_DEPENDENCIES ${SUNSHINE_TARGET_DEPENDENCIES} libevdev) 35 | 36 | set(EXTERNAL_PROJECT_LIBEVDEV_USED TRUE) 37 | endif() 38 | 39 | if(EVDEV_INCLUDE_DIR AND EVDEV_LIBRARY) 40 | message(STATUS "Found libevdev library: ${EVDEV_LIBRARY}") 41 | message(STATUS "Found libevdev include directory: ${EVDEV_INCLUDE_DIR}") 42 | 43 | include_directories(SYSTEM ${EVDEV_INCLUDE_DIR}) 44 | list(APPEND PLATFORM_LIBRARIES ${EVDEV_LIBRARY}) 45 | else() 46 | message(FATAL_ERROR "Couldn't find or fetch libevdev") 47 | endif() 48 | -------------------------------------------------------------------------------- /cmake/dependencies/linux.cmake: -------------------------------------------------------------------------------- 1 | # linux specific dependencies 2 | -------------------------------------------------------------------------------- /cmake/dependencies/macos.cmake: -------------------------------------------------------------------------------- 1 | # macos specific dependencies 2 | 3 | FIND_LIBRARY(APP_KIT_LIBRARY AppKit) 4 | FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices) 5 | FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation) 6 | FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia) 7 | FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo) 8 | FIND_LIBRARY(FOUNDATION_LIBRARY Foundation) 9 | FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox) 10 | 11 | if(SUNSHINE_ENABLE_TRAY) 12 | FIND_LIBRARY(COCOA Cocoa REQUIRED) 13 | endif() 14 | -------------------------------------------------------------------------------- /cmake/dependencies/nlohmann_json.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Loads the nlohmann_json library giving the priority to the system package first, with a fallback to FetchContent. 3 | # 4 | include_guard(GLOBAL) 5 | 6 | find_package(nlohmann_json 3.11 QUIET GLOBAL) 7 | if(NOT nlohmann_json_FOUND) 8 | message(STATUS "nlohmann_json v3.11.x package not found in the system. Falling back to FetchContent.") 9 | include(FetchContent) 10 | 11 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") 12 | cmake_policy(SET CMP0135 NEW) # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24 13 | endif() 14 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.31.0") 15 | cmake_policy(SET CMP0174 NEW) # Handle empty variables 16 | endif() 17 | 18 | FetchContent_Declare( 19 | json 20 | URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz 21 | URL_HASH MD5=c23a33f04786d85c29fda8d16b5f0efd 22 | DOWNLOAD_EXTRACT_TIMESTAMP 23 | ) 24 | FetchContent_MakeAvailable(json) 25 | endif() 26 | -------------------------------------------------------------------------------- /cmake/dependencies/unix.cmake: -------------------------------------------------------------------------------- 1 | # unix specific dependencies 2 | # put anything here that applies to both linux and macos 3 | -------------------------------------------------------------------------------- /cmake/dependencies/windows.cmake: -------------------------------------------------------------------------------- 1 | # windows specific dependencies 2 | 3 | # Make sure MinHook is installed 4 | find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED) 5 | find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED) 6 | 7 | add_library(minhook::minhook STATIC IMPORTED) 8 | set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY}) 9 | target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR}) 10 | -------------------------------------------------------------------------------- /cmake/macros/common.cmake: -------------------------------------------------------------------------------- 1 | # common macros 2 | # this file will also load platform specific macros 3 | 4 | # platform specific macros 5 | if(WIN32) 6 | include(${CMAKE_MODULE_PATH}/macros/windows.cmake) 7 | elseif(UNIX) 8 | include(${CMAKE_MODULE_PATH}/macros/unix.cmake) 9 | 10 | if(APPLE) 11 | include(${CMAKE_MODULE_PATH}/macros/macos.cmake) 12 | else() 13 | include(${CMAKE_MODULE_PATH}/macros/linux.cmake) 14 | endif() 15 | endif() 16 | 17 | # override find_package function 18 | macro(find_package) # cmake-lint: disable=C0103 19 | string(TOLOWER "${ARGV0}" ARGV0_LOWER) 20 | if( 21 | (("${ARGV0_LOWER}" STREQUAL "boost") AND DEFINED FETCH_CONTENT_BOOST_USED) OR 22 | (("${ARGV0_LOWER}" STREQUAL "libevdev") AND DEFINED EXTERNAL_PROJECT_LIBEVDEV_USED) 23 | ) 24 | # Do nothing, as the package has already been fetched 25 | else() 26 | # Call the original find_package function 27 | _find_package(${ARGV}) 28 | endif() 29 | endmacro() 30 | -------------------------------------------------------------------------------- /cmake/macros/linux.cmake: -------------------------------------------------------------------------------- 1 | # linux specific macros 2 | 3 | # GEN_WAYLAND: args = `filename` 4 | macro(GEN_WAYLAND wayland_directory subdirectory filename) 5 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated-src) 6 | 7 | message("wayland-scanner private-code \ 8 | ${wayland_directory}/${subdirectory}/${filename}.xml \ 9 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.c") 10 | message("wayland-scanner client-header \ 11 | ${wayland_directory}/${subdirectory}/${filename}.xml \ 12 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.h") 13 | execute_process( 14 | COMMAND wayland-scanner private-code 15 | ${wayland_directory}/${subdirectory}/${filename}.xml 16 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.c 17 | COMMAND wayland-scanner client-header 18 | ${wayland_directory}/${subdirectory}/${filename}.xml 19 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.h 20 | 21 | RESULT_VARIABLE EXIT_INT 22 | ) 23 | 24 | if(NOT ${EXIT_INT} EQUAL 0) 25 | message(FATAL_ERROR "wayland-scanner failed") 26 | endif() 27 | 28 | list(APPEND PLATFORM_TARGET_FILES 29 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.c 30 | ${CMAKE_BINARY_DIR}/generated-src/${filename}.h) 31 | endmacro() 32 | -------------------------------------------------------------------------------- /cmake/macros/macos.cmake: -------------------------------------------------------------------------------- 1 | # macos specific macros 2 | 3 | # ADD_FRAMEWORK: args = `fwname`, `appname` 4 | macro(ADD_FRAMEWORK fwname appname) 5 | find_library(FRAMEWORK_${fwname} 6 | NAMES ${fwname} 7 | PATHS ${CMAKE_OSX_SYSROOT}/System/Library 8 | PATH_SUFFIXES Frameworks 9 | NO_DEFAULT_PATH) 10 | if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND) 11 | MESSAGE(ERROR ": Framework ${fwname} not found") 12 | else() 13 | TARGET_LINK_LIBRARIES(${appname} "${FRAMEWORK_${fwname}}/${fwname}") 14 | MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") 15 | endif() 16 | endmacro(ADD_FRAMEWORK) 17 | -------------------------------------------------------------------------------- /cmake/macros/unix.cmake: -------------------------------------------------------------------------------- 1 | # unix specific macros 2 | # put anything here that applies to both linux and macos 3 | -------------------------------------------------------------------------------- /cmake/macros/windows.cmake: -------------------------------------------------------------------------------- 1 | # windows specific macros 2 | -------------------------------------------------------------------------------- /cmake/packaging/common.cmake: -------------------------------------------------------------------------------- 1 | # common packaging 2 | 3 | # common cpack options 4 | set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) 5 | set(CPACK_PACKAGE_VENDOR "LizardByte") 6 | string(REGEX REPLACE "^v" "" CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) # remove the v prefix if it exists 7 | set(CPACK_PACKAGE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cpack_artifacts) 8 | set(CPACK_PACKAGE_CONTACT "https://app.lizardbyte.dev") 9 | set(CPACK_PACKAGE_DESCRIPTION ${CMAKE_PROJECT_DESCRIPTION}) 10 | set(CPACK_PACKAGE_HOMEPAGE_URL ${CMAKE_PROJECT_HOMEPAGE_URL}) 11 | set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE) 12 | set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/sunshine.png) 13 | set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}") 14 | set(CPACK_STRIP_FILES YES) 15 | 16 | # install common assets 17 | install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" 18 | DESTINATION "${SUNSHINE_ASSETS_DIR}" 19 | PATTERN "web" EXCLUDE) 20 | # copy assets to build directory, for running without install 21 | file(GLOB_RECURSE ALL_ASSETS 22 | RELATIVE "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/*") 23 | list(FILTER ALL_ASSETS EXCLUDE REGEX "^web/.*$") # Filter out the web directory 24 | foreach(asset ${ALL_ASSETS}) # Copy assets to build directory, excluding the web directory 25 | file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/${asset}" 26 | DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/assets") 27 | endforeach() 28 | 29 | # install built vite assets 30 | install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/assets/web" 31 | DESTINATION "${SUNSHINE_ASSETS_DIR}") 32 | 33 | # platform specific packaging 34 | if(WIN32) 35 | include(${CMAKE_MODULE_PATH}/packaging/windows.cmake) 36 | elseif(UNIX) 37 | include(${CMAKE_MODULE_PATH}/packaging/unix.cmake) 38 | 39 | if(APPLE) 40 | include(${CMAKE_MODULE_PATH}/packaging/macos.cmake) 41 | else() 42 | include(${CMAKE_MODULE_PATH}/packaging/linux.cmake) 43 | endif() 44 | endif() 45 | 46 | include(CPack) 47 | -------------------------------------------------------------------------------- /cmake/packaging/macos.cmake: -------------------------------------------------------------------------------- 1 | # macos specific packaging 2 | 3 | # todo - bundle doesn't produce a valid .app use cpack -G DragNDrop 4 | set(CPACK_BUNDLE_NAME "${CMAKE_PROJECT_NAME}") 5 | set(CPACK_BUNDLE_PLIST "${APPLE_PLIST_FILE}") 6 | set(CPACK_BUNDLE_ICON "${PROJECT_SOURCE_DIR}/sunshine.icns") 7 | # set(CPACK_BUNDLE_STARTUP_COMMAND "${INSTALL_RUNTIME_DIR}/sunshine") 8 | 9 | if(SUNSHINE_PACKAGE_MACOS) # todo 10 | set(MAC_PREFIX "${CMAKE_PROJECT_NAME}.app/Contents") 11 | set(INSTALL_RUNTIME_DIR "${MAC_PREFIX}/MacOS") 12 | 13 | install(TARGETS sunshine 14 | BUNDLE DESTINATION . COMPONENT Runtime 15 | RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR} COMPONENT Runtime) 16 | else() 17 | install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/misc/uninstall_pkg.sh" 18 | DESTINATION "${SUNSHINE_ASSETS_DIR}") 19 | endif() 20 | 21 | install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" 22 | DESTINATION "${SUNSHINE_ASSETS_DIR}") 23 | # copy assets to build directory, for running without install 24 | file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" 25 | DESTINATION "${CMAKE_BINARY_DIR}/assets") 26 | -------------------------------------------------------------------------------- /cmake/packaging/unix.cmake: -------------------------------------------------------------------------------- 1 | # unix specific packaging 2 | # put anything here that applies to both linux and macos 3 | 4 | # return here if building a macos package 5 | if(SUNSHINE_PACKAGE_MACOS) 6 | return() 7 | endif() 8 | 9 | # Installation destination dir 10 | set(CPACK_SET_DESTDIR true) 11 | if(NOT CMAKE_INSTALL_PREFIX) 12 | set(CMAKE_INSTALL_PREFIX "/usr/share/sunshine") 13 | endif() 14 | 15 | install(TARGETS sunshine RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 16 | -------------------------------------------------------------------------------- /cmake/packaging/windows_wix.cmake: -------------------------------------------------------------------------------- 1 | # WIX Packaging 2 | # see options at: https://cmake.org/cmake/help/latest/cpack_gen/wix.html 3 | 4 | # TODO: Replace nsis with wix 5 | -------------------------------------------------------------------------------- /cmake/prep/constants.cmake: -------------------------------------------------------------------------------- 1 | # source assets will be installed from this directory 2 | set(SUNSHINE_SOURCE_ASSETS_DIR "${CMAKE_SOURCE_DIR}/src_assets") 3 | 4 | # enable system tray, we will disable this later if we cannot find the required package config on linux 5 | set(SUNSHINE_TRAY 1) 6 | -------------------------------------------------------------------------------- /cmake/prep/init.cmake: -------------------------------------------------------------------------------- 1 | if (WIN32) 2 | elseif (APPLE) 3 | elseif (UNIX) 4 | include(GNUInstallDirs) 5 | 6 | if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH) 7 | set(SUNSHINE_EXECUTABLE_PATH "sunshine") 8 | endif() 9 | 10 | if(SUNSHINE_BUILD_FLATPAK) 11 | set(SUNSHINE_SERVICE_START_COMMAND "ExecStart=flatpak run --command=sunshine ${PROJECT_FQDN}") 12 | set(SUNSHINE_SERVICE_STOP_COMMAND "ExecStop=flatpak kill ${PROJECT_FQDN}") 13 | else() 14 | set(SUNSHINE_SERVICE_START_COMMAND "ExecStart=${SUNSHINE_EXECUTABLE_PATH}") 15 | set(SUNSHINE_SERVICE_STOP_COMMAND "") 16 | endif() 17 | endif() 18 | -------------------------------------------------------------------------------- /cmake/targets/linux.cmake: -------------------------------------------------------------------------------- 1 | # linux specific target definitions 2 | -------------------------------------------------------------------------------- /cmake/targets/macos.cmake: -------------------------------------------------------------------------------- 1 | # macos specific target definitions 2 | target_link_options(sunshine PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${APPLE_PLIST_FILE}) 3 | # Tell linker to dynamically load these symbols at runtime, in case they're unavailable: 4 | target_link_options(sunshine PRIVATE -Wl,-U,_CGPreflightScreenCaptureAccess -Wl,-U,_CGRequestScreenCaptureAccess) 5 | -------------------------------------------------------------------------------- /cmake/targets/unix.cmake: -------------------------------------------------------------------------------- 1 | # unix specific target definitions 2 | # put anything here that applies to both linux and macos 3 | -------------------------------------------------------------------------------- /cmake/targets/windows.cmake: -------------------------------------------------------------------------------- 1 | # windows specific target definitions 2 | set_target_properties(sunshine PROPERTIES LINK_SEARCH_START_STATIC 1) 3 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") 4 | find_library(ZLIB ZLIB1) 5 | list(APPEND SUNSHINE_EXTERNAL_LIBRARIES 6 | Windowsapp.lib 7 | Wtsapi32.lib) 8 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | codecov: 3 | branch: master 4 | 5 | coverage: 6 | status: 7 | project: 8 | default: 9 | target: auto 10 | threshold: 10% 11 | 12 | comment: 13 | layout: "diff, flags, files" 14 | behavior: default 15 | require_changes: false # if true: only post the comment if coverage changes 16 | 17 | ignore: 18 | - "tests" 19 | - "third-party" 20 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "base_path": "." 3 | "base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only) 4 | "preserve_hierarchy": true # false will flatten tree on crowdin, but doesn't work with dest option 5 | "pull_request_title": "chore(l10n): update translations" 6 | "pull_request_labels": [ 7 | "crowdin", 8 | "l10n" 9 | ] 10 | 11 | "files": [ 12 | { 13 | "source": "/locale/*.po", 14 | "dest": "/%original_file_name%", 15 | "translation": "/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%", 16 | "languages_mapping": { 17 | "two_letters_code": { 18 | # map non-two letter codes here, left side is crowdin designation, right side is babel designation 19 | "en-GB": "en_GB", 20 | "en-US": "en_US" 21 | } 22 | }, 23 | "update_option": "update_as_unapproved" 24 | }, 25 | { 26 | "source": "/src_assets/common/assets/web/public/assets/locale/en.json", 27 | "dest": "/sunshine.json", 28 | "translation": "/src_assets/common/assets/web/public/assets/locale/%two_letters_code%.%file_extension%", 29 | "update_option": "update_as_unapproved" 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | Sunshine has a RESTful API which can be used to interact with the service. 4 | 5 | Unless otherwise specified, authentication is required for all API calls. You can authenticate using 6 | basic authentication with the admin username and password. 7 | 8 | @htmlonly 9 | 10 | @endhtmlonly 11 | 12 | ## GET /api/apps 13 | @copydoc confighttp::getApps() 14 | 15 | ## POST /api/apps 16 | @copydoc confighttp::saveApp() 17 | 18 | ## POST /api/apps/close 19 | @copydoc confighttp::closeApp() 20 | 21 | ## DELETE /api/apps/{index} 22 | @copydoc confighttp::deleteApp() 23 | 24 | ## GET /api/clients/list 25 | @copydoc confighttp::getClients() 26 | 27 | ## POST /api/clients/unpair 28 | @copydoc confighttp::unpair() 29 | 30 | ## POST /api/clients/unpair-all 31 | @copydoc confighttp::unpairAll() 32 | 33 | ## GET /api/config 34 | @copydoc confighttp::getConfig() 35 | 36 | ## GET /api/configLocale 37 | @copydoc confighttp::getLocale() 38 | 39 | ## POST /api/config 40 | @copydoc confighttp::saveConfig() 41 | 42 | ## POST /api/covers/upload 43 | @copydoc confighttp::uploadCover() 44 | 45 | ## GET /api/logs 46 | @copydoc confighttp::getLogs() 47 | 48 | ## POST /api/password 49 | @copydoc confighttp::savePassword() 50 | 51 | ## POST /api/pin 52 | @copydoc confighttp::savePin() 53 | 54 | ## POST /api/reset-display-device-persistence 55 | @copydoc confighttp::resetDisplayDevicePersistence() 56 | 57 | ## POST /api/restart 58 | @copydoc confighttp::restart() 59 | 60 |
61 | 62 | | Previous | Next | 63 | |:--------------------------------------------|--------------------------------------:| 64 | | [Performance Tuning](performance_tuning.md) | [Troubleshooting](troubleshooting.md) | 65 | 66 |
67 | 68 |
69 | 70 | [TOC] 71 |
72 | -------------------------------------------------------------------------------- /docs/awesome_sunshine.md: -------------------------------------------------------------------------------- 1 | # Awesome-Sunshine 2 | 3 | @htmlonly 4 | 5 | 9 | 10 | @endhtmlonly 11 | 12 |
13 | 14 | | Previous | Next | 15 | |:--------------------------------|--------------------:| 16 | | [App Examples](app_examples.md) | [Guides](guides.md) | 17 | 18 |
19 | 20 |
21 | 22 | [TOC] 23 |
24 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | @htmlonly 4 | 5 | 8 | 9 | @endhtmlonly 10 | 11 |
12 | 13 | | Previous | Next | 14 | |:--------------------------------------|------------------------------:| 15 | | [Getting Started](getting_started.md) | [Docker](../DOCKER_README.md) | 16 | 17 |
18 | 19 |
20 | 21 | [TOC] 22 |
23 | -------------------------------------------------------------------------------- /docs/configuration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief Add a button to open the configuration option for each table 3 | */ 4 | document.addEventListener("DOMContentLoaded", function() { 5 | const tables = document.querySelectorAll("table"); 6 | tables.forEach(table => { 7 | if (table.className !== "doxtable") { 8 | return; 9 | } 10 | 11 | let previousElement = table.previousElementSibling; 12 | while (previousElement && previousElement.tagName !== "H2") { 13 | previousElement = previousElement.previousElementSibling; 14 | } 15 | if (previousElement && previousElement.textContent) { 16 | const sectionId = previousElement.textContent.trim().toLowerCase(); 17 | const newRow = document.createElement("tr"); 18 | 19 | const newCell = document.createElement("td"); 20 | newCell.setAttribute("colspan", "3"); 21 | 22 | const newCode = document.createElement("code"); 23 | newCode.className = "open-button"; 24 | newCode.setAttribute("onclick", `window.open('https://${document.getElementById('host-authority').value}/config/#${sectionId}', '_blank')`); 25 | newCode.textContent = "Open"; 26 | 27 | newCell.appendChild(newCode); 28 | newRow.appendChild(newCell); 29 | 30 | // get the table body 31 | const tbody = table.querySelector("tbody"); 32 | 33 | // Insert at the beginning of the table 34 | tbody.insertBefore(newRow, tbody.firstChild); 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /docs/doc-styles.css: -------------------------------------------------------------------------------- 1 | /* A fake button as doxygen doesn't allow button elements */ 2 | .open-button { 3 | background: var(--primary-color); 4 | color: white; 5 | cursor: pointer; 6 | } 7 | -------------------------------------------------------------------------------- /docs/gamestream_migration.md: -------------------------------------------------------------------------------- 1 | # GameStream Migration 2 | Nvidia announced that their GameStream service for Nvidia Games clients will be discontinued in February 2023. 3 | Luckily, Sunshine performance is now equal to or better than Nvidia GameStream. 4 | 5 | ## Migration 6 | We have developed a simple migration tool to help you migrate your GameStream games and apps to Sunshine automatically. 7 | Please check out our [GSMS](https://github.com/LizardByte/GSMS) project if you're interested in an automated 8 | migration option. GSMS offers the ability to migrate your custom and auto-detected games and apps. The 9 | working directory, command, and image are all set in Sunshine's `apps.json` file. The box-art image is also copied 10 | to a specified directory. 11 | 12 | ## Internet Streaming 13 | If you are using the Moonlight Internet Hosting Tool, you can remove it from your system when you migrate to Sunshine. 14 | To stream over the Internet with Sunshine and a UPnP-capable router, enable the UPnP option in the Sunshine Web UI. 15 | 16 | @note{Running Sunshine together with versions of the Moonlight Internet Hosting Tool prior to v5.6 will cause UPnP 17 | port forwarding to become unreliable. Either uninstall the tool entirely or update it to v5.6 or later.} 18 | 19 | ## Limitations 20 | Sunshine does have some limitations, as compared to Nvidia GameStream. 21 | 22 | * Automatic game/application list. 23 | * Changing game settings automatically, to optimize streaming. 24 | 25 |
26 | 27 | | Previous | Next | 28 | |:------------------------------------------------|------------------:| 29 | | [Third-party Packages](third_party_packages.md) | [Legal](legal.md) | 30 | 31 |
32 | 33 |
34 | 35 | [TOC] 36 |
37 | -------------------------------------------------------------------------------- /docs/guides.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | @admonition{Community | A collection of guides written by the community is available on our 4 | [blog](https://app.lizardbyte.dev/blog). 5 | Feel free to contribute your own tips and trips by making a PR to 6 | [LizardByte.github.io](https://github.com/LizardByte/LizardByte.github.io).} 7 | 8 |
9 | 10 | | Previous | Next | 11 | |:----------------------------------------|--------------------------------------------:| 12 | | [Awesome-Sunshine](awesome_sunshine.md) | [Performance Tuning](performance_tuning.md) | 13 | 14 |
15 | 16 |
17 | 18 | [TOC] 19 |
20 | -------------------------------------------------------------------------------- /docs/legal.md: -------------------------------------------------------------------------------- 1 | # Legal 2 | @attention{This documentation is for informational purposes only and is not intended as legal advice. If you have 3 | any legal questions or concerns about using Sunshine, we recommend consulting with a lawyer.} 4 | 5 | Sunshine is licensed under the GPL-3.0 license, which allows for free use and modification of the software. 6 | The full text of the license can be reviewed [here](https://github.com/LizardByte/Sunshine/blob/master/LICENSE). 7 | 8 | ## Commercial Use 9 | Sunshine can be used in commercial applications without any limitations. This means that businesses and organizations 10 | can use Sunshine to create and sell products or services without needing to seek permission or pay a fee. 11 | 12 | However, it is important to note that the GPL-3.0 license does not grant any rights to distribute or sell the encoders 13 | contained within Sunshine. If you plan to sell access to Sunshine as part of their distribution, you are responsible 14 | for obtaining the necessary licenses to do so. This may include obtaining a license from the 15 | Motion Picture Experts Group (MPEG-LA) and/or any other necessary licensing requirements. 16 | 17 | In summary, while Sunshine is free to use, it is the user's responsibility to ensure compliance with all applicable 18 | licensing requirements when redistributing the software as part of a commercial offering. If you have any questions or 19 | concerns about using Sunshine in a commercial setting, we recommend consulting with a lawyer. 20 | 21 |
22 | 23 | | Previous | Next | 24 | |:------------------------------------------------|----------------------------------:| 25 | | [Gamestream Migration](gamestream_migration.md) | [Configuration](configuration.md) | 26 | 27 |
28 | 29 |
30 | 31 | [TOC] 32 |
33 | -------------------------------------------------------------------------------- /docs/performance_tuning.md: -------------------------------------------------------------------------------- 1 | # Performance Tuning 2 | In addition to the options available in the [Configuration](configuration.md) section, there are a few additional 3 | system options that can be used to help improve the performance of Sunshine. 4 | 5 | ## AMD 6 | 7 | In Windows, enabling *Enhanced Sync* in AMD's settings may help reduce the latency by an additional frame. This 8 | applies to `amfenc` and `libx264`. 9 | 10 | ## NVIDIA 11 | 12 | Enabling *Fast Sync* in Nvidia settings may help reduce latency. 13 | 14 |
15 | 16 | | Previous | Next | 17 | |:--------------------|--------------:| 18 | | [Guides](guides.md) | [API](api.md) | 19 | 20 |
21 | 22 |
23 | 24 | [TOC] 25 |
26 | -------------------------------------------------------------------------------- /gh-pages-template/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | version: 2 6 | 7 | build: 8 | os: ubuntu-24.04 9 | tools: 10 | ruby: "3.3" 11 | apt_packages: 12 | - 7zip 13 | - jq 14 | jobs: 15 | install: 16 | - | 17 | mkdir -p "./tmp" 18 | branch="master" 19 | base_url="https://raw.githubusercontent.com/LizardByte/LizardByte.github.io" 20 | url="${base_url}/refs/heads/${branch}/scripts/readthedocs_build.sh" 21 | curl -sSL -o "./tmp/readthedocs_build.sh" "${url}" 22 | chmod +x "./tmp/readthedocs_build.sh" 23 | build: 24 | html: 25 | - "./tmp/readthedocs_build.sh" 26 | -------------------------------------------------------------------------------- /gh-pages-template/_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # See https://github.com/daattali/beautiful-jekyll/blob/master/_config.yml for documented options 3 | 4 | avatar: "/Sunshine/assets/img/navbar-avatar.png" 5 | -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_231616343.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_231616343.jpeg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_231616343_1920x1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_231616343_1920x1280.jpg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_303330124.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_303330124.jpeg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_303330124_1920x1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_303330124_1920x1280.jpg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_305732536.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_305732536.jpeg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/banners/AdobeStock_305732536_1920x1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/banners/AdobeStock_305732536_1920x1280.jpg -------------------------------------------------------------------------------- /gh-pages-template/assets/img/navbar-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/gh-pages-template/assets/img/navbar-avatar.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sunshine", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "build": "vite build --debug", 6 | "build-clean": "vite build --debug --emptyOutDir", 7 | "dev": "vite build --watch", 8 | "serve": "serve ./tests/fixtures/http --no-port-switching" 9 | }, 10 | "dependencies": { 11 | "@lizardbyte/shared-web": "2025.326.11214", 12 | "vue": "3.5.14", 13 | "vue-i18n": "11.1.4" 14 | }, 15 | "devDependencies": { 16 | "@codecov/vite-plugin": "1.9.0", 17 | "@vitejs/plugin-vue": "4.6.2", 18 | "serve": "14.2.3", 19 | "vite": "4.5.14", 20 | "vite-plugin-ejs": "1.6.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packaging/linux/AppImage/sunshine.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=@PROJECT_NAME@ 4 | Exec=sunshine 5 | Version=1.0 6 | Comment=@PROJECT_DESCRIPTION@ 7 | Icon=sunshine 8 | Keywords=gamestream;stream;moonlight;remote play; 9 | Categories=AudioVideo;Network;RemoteAccess; 10 | Terminal=true 11 | X-AppImage-Name=sunshine 12 | X-AppImage-Version=@PROJECT_VERSION@ 13 | X-AppImage-Arch=x86_64 14 | -------------------------------------------------------------------------------- /packaging/linux/Arch/sunshine.install: -------------------------------------------------------------------------------- 1 | do_setcap() { 2 | setcap cap_sys_admin+p $(readlink -f $(which sunshine)) 3 | } 4 | 5 | do_udev_reload() { 6 | udevadm control --reload-rules 7 | udevadm trigger --property-match=DEVNAME=/dev/uinput 8 | udevadm trigger --property-match=DEVNAME=/dev/uhid 9 | modprobe uinput || true 10 | modprobe uhid || true 11 | } 12 | 13 | post_install() { 14 | do_setcap 15 | do_udev_reload 16 | } 17 | 18 | post_upgrade() { 19 | do_setcap 20 | do_udev_reload 21 | } 22 | 23 | -------------------------------------------------------------------------------- /packaging/linux/fedora/patches/f42/aarch64/01-math_functions.patch: -------------------------------------------------------------------------------- 1 | diff '--color=auto' -ur a/cuda/targets/sbsa-linux/include/crt/math_functions.h b/cuda/targets/sbsa-linux/include/crt/math_functions.h 2 | --- a/cuda/targets/sbsa-linux/include/crt/math_functions.h 2024-08-23 00:25:39.000000000 +0200 3 | +++ b/cuda/targets/sbsa-linux/include/crt/math_functions.h 2025-02-17 01:19:44.270292640 +0100 4 | @@ -2553,7 +2553,7 @@ 5 | * 6 | * \note_accuracy_double 7 | */ 8 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double sinpi(double x); 9 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double sinpi(double x) noexcept (true); 10 | /** 11 | * \ingroup CUDA_MATH_SINGLE 12 | * \brief Calculate the sine of the input argument 13 | @@ -2576,7 +2576,7 @@ 14 | * 15 | * \note_accuracy_single 16 | */ 17 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float sinpif(float x); 18 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float sinpif(float x) noexcept (true); 19 | /** 20 | * \ingroup CUDA_MATH_DOUBLE 21 | * \brief Calculate the cosine of the input argument 22 | @@ -2598,7 +2598,7 @@ 23 | * 24 | * \note_accuracy_double 25 | */ 26 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double cospi(double x); 27 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double cospi(double x) noexcept (true); 28 | /** 29 | * \ingroup CUDA_MATH_SINGLE 30 | * \brief Calculate the cosine of the input argument 31 | @@ -2620,7 +2620,7 @@ 32 | * 33 | * \note_accuracy_single 34 | */ 35 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float cospif(float x); 36 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float cospif(float x) noexcept (true); 37 | /** 38 | * \ingroup CUDA_MATH_DOUBLE 39 | * \brief Calculate the sine and cosine of the first input argument 40 | -------------------------------------------------------------------------------- /packaging/linux/fedora/patches/f42/x86_64/01-math_functions.patch: -------------------------------------------------------------------------------- 1 | diff '--color=auto' -ur a/cuda/targets/x86_64-linux/include/crt/math_functions.h b/cuda/targets/x86_64-linux/include/crt/math_functions.h 2 | --- a/cuda/targets/x86_64-linux/include/crt/math_functions.h 2024-08-23 00:25:39.000000000 +0200 3 | +++ b/cuda/targets/x86_64-linux/include/crt/math_functions.h 2025-02-17 01:19:44.270292640 +0100 4 | @@ -2553,7 +2553,7 @@ 5 | * 6 | * \note_accuracy_double 7 | */ 8 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double sinpi(double x); 9 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double sinpi(double x) noexcept (true); 10 | /** 11 | * \ingroup CUDA_MATH_SINGLE 12 | * \brief Calculate the sine of the input argument 13 | @@ -2576,7 +2576,7 @@ 14 | * 15 | * \note_accuracy_single 16 | */ 17 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float sinpif(float x); 18 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float sinpif(float x) noexcept (true); 19 | /** 20 | * \ingroup CUDA_MATH_DOUBLE 21 | * \brief Calculate the cosine of the input argument 22 | @@ -2598,7 +2598,7 @@ 23 | * 24 | * \note_accuracy_double 25 | */ 26 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double cospi(double x); 27 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double cospi(double x) noexcept (true); 28 | /** 29 | * \ingroup CUDA_MATH_SINGLE 30 | * \brief Calculate the cosine of the input argument 31 | @@ -2620,7 +2620,7 @@ 32 | * 33 | * \note_accuracy_single 34 | */ 35 | -extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float cospif(float x); 36 | +extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float cospif(float x) noexcept (true); 37 | /** 38 | * \ingroup CUDA_MATH_DOUBLE 39 | * \brief Calculate the sine and cosine of the first input argument 40 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Sunshine

4 |

Self-hosted game stream host for Moonlight.

5 |
6 | 7 |
8 | Flathub installs 9 | Flathub Version 10 |
11 | 12 | ## ℹ️ About 13 | 14 | Sunshine is a self-hosted game stream host for Moonlight. 15 | 16 | LizardByte has the full documentation hosted on [Read the Docs](https://docs.lizardbyte.dev/projects/sunshine) 17 | 18 | * [Stable](https://docs.lizardbyte.dev/projects/sunshine/latest/) 19 | * [Beta](https://docs.lizardbyte.dev/projects/sunshine/master/) 20 | 21 | This repo is synced from the upstream [Sunshine](https://github.com/LizardByte/Sunshine) repo. 22 | Please report issues and contribute to the upstream repo. 23 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "PATH": "$(PATH):$(HOME)/.local/bin" 4 | }, 5 | "apps": [ 6 | { 7 | "name": "Desktop", 8 | "image-path": "desktop.png" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/exceptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev.lizardbyte.app.Sunshine": [ 3 | "appstream-missing-screenshots", 4 | "appstream-screenshots-not-mirrored-in-ostree", 5 | "external-gitmodule-url-found", 6 | "finish-args-flatpak-spawn-access" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/flathub.json: -------------------------------------------------------------------------------- 1 | { 2 | "disable-external-data-checker": true 3 | } 4 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/avahi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "avahi", 3 | "cleanup": [ 4 | "/bin", 5 | "/lib/avahi", 6 | "/share" 7 | ], 8 | "config-opts": [ 9 | "--with-distro=none", 10 | "--disable-gobject", 11 | "--disable-introspection", 12 | "--disable-qt3", 13 | "--disable-qt4", 14 | "--disable-qt5", 15 | "--disable-gtk", 16 | "--disable-core-docs", 17 | "--disable-manpages", 18 | "--disable-libdaemon", 19 | "--disable-python", 20 | "--disable-pygobject", 21 | "--disable-mono", 22 | "--disable-monodoc", 23 | "--disable-autoipd", 24 | "--disable-doxygen-doc", 25 | "--disable-doxygen-dot", 26 | "--disable-doxygen-xml", 27 | "--disable-doxygen-html", 28 | "--disable-manpages", 29 | "--disable-xmltoman", 30 | "--disable-libevent" 31 | ], 32 | "sources": [ 33 | { 34 | "type": "git", 35 | "url": "https://salsa.debian.org/utopia-team/avahi.git", 36 | "commit": "1412c015d348166d58ea9c192239b00769eae24e", 37 | "tag": "debian/0.8-13", 38 | "x-checker-data": { 39 | "type": "git", 40 | "tag-pattern": "^debian\\/(\\d.+)$", 41 | "versions": { 42 | "<": "0.9" 43 | } 44 | } 45 | }, 46 | { 47 | "type": "shell", 48 | "commands": [ 49 | "autoreconf -ivf" 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/boost.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "boost", 3 | "buildsystem": "simple", 4 | "build-commands": [ 5 | "cd tools/build && bison -y -d -o src/engine/jamgram.cpp src/engine/jamgram.y", 6 | "./bootstrap.sh --prefix=$FLATPAK_DEST --with-libraries=filesystem,locale,log,program_options,system", 7 | "./b2 install variant=release link=shared runtime-link=shared cxxflags=\"$CXXFLAGS\"" 8 | ], 9 | "sources": [ 10 | { 11 | "type": "archive", 12 | "url": "https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.xz", 13 | "sha256": "7da75f171837577a52bbf217e17f8ea576c7c246e4594d617bfde7fafd408be5" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/cuda.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cuda", 3 | "build-options": { 4 | "no_debuginfo": true 5 | }, 6 | "buildsystem": "simple", 7 | "cleanup": [ 8 | "*" 9 | ], 10 | "build-commands": [ 11 | "chmod u+x ./cuda.run", 12 | "./cuda.run --silent --toolkit --toolkitpath=$FLATPAK_DEST/cuda --no-opengl-libs --no-man-page --no-drm --tmpdir=$FLATPAK_BUILDER_BUILDDIR", 13 | "rm -r $FLATPAK_DEST/cuda/nsight-systems-*", 14 | "rm ./cuda.run" 15 | ], 16 | "sources": [ 17 | { 18 | "type": "file", 19 | "only-arches": [ 20 | "x86_64" 21 | ], 22 | "url": "https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.35.03_linux.run", 23 | "sha256": "3729a89cb58f7ca6a46719cff110d6292aec7577585a8d71340f0dbac54fb237", 24 | "dest-filename": "cuda.run" 25 | }, 26 | { 27 | "type": "file", 28 | "only-arches": [ 29 | "aarch64" 30 | ], 31 | "url": "https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.35.03_linux_sbsa.run", 32 | "sha256": "2249408848b705c18b9eadfb5161b52e4e36fcc5753647329cce93db141e5466", 33 | "dest-filename": "cuda.run" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/libevdev.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libevdev", 3 | "buildsystem": "meson", 4 | "config-opts": [ 5 | "-Ddocumentation=disabled", 6 | "-Dtests=disabled" 7 | ], 8 | "cleanup": [ 9 | "/bin" 10 | ], 11 | "sources": [ 12 | { 13 | "type": "git", 14 | "url": "https://salsa.debian.org/debian/libevdev.git", 15 | "commit": "1aa7baa233d6df4cee6a66fbc61bb5ffc8b6e88d", 16 | "tag": "debian/1.13.0+dfsg-1", 17 | "x-checker-data": { 18 | "type": "git", 19 | "tag-pattern": "^debian\\/(\\d.\\d+\\.\\d+)", 20 | "versions": { 21 | "<": "1.13.1" 22 | } 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/libnotify.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libnotify", 3 | "buildsystem": "meson", 4 | "config-opts": [ 5 | "-Dtests=false", 6 | "-Dintrospection=disabled", 7 | "-Dman=false", 8 | "-Dgtk_doc=false", 9 | "-Ddocbook_docs=disabled" 10 | ], 11 | "sources": [ 12 | { 13 | "type": "git", 14 | "url": "https://salsa.debian.org/gnome-team/libnotify.git", 15 | "commit": "ccf2f62ef0a4b264dd4eff32cab70a3e213ceb1a", 16 | "tag": "debian/0.8.1-1", 17 | "x-checker-data": { 18 | "type": "git", 19 | "tag-pattern": "^debian\\/(\\d.+)$", 20 | "versions": { 21 | "<": "0.8.2" 22 | } 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/miniupnpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniupnpc", 3 | "buildsystem": "cmake-ninja", 4 | "config-opts": [ 5 | "-DCMAKE_BUILD_TYPE=Release", 6 | "-DUPNPC_BUILD_SAMPLE=OFF", 7 | "-DUPNPC_BUILD_SHARED=ON", 8 | "-DUPNPC_BUILD_TESTS=OFF" 9 | ], 10 | "sources": [ 11 | { 12 | "type": "git", 13 | "url": "https://salsa.debian.org/miniupnp-team/miniupnpc.git", 14 | "commit": "c5fe3aa794e92a503cecec6a4071eb6d310b4e42", 15 | "tag": "debian/2.2.4-1", 16 | "x-checker-data": { 17 | "type": "git", 18 | "tag-pattern": "^debian\\/(\\d.+)$", 19 | "versions": { 20 | "<": "2.2.5" 21 | } 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/nlohmann_json.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nlohmann_json", 3 | "buildsystem": "cmake-ninja", 4 | "config-opts": [ 5 | "-DJSON_MultipleHeaders=OFF", 6 | "-DJSON_BuildTests=OFF" 7 | ], 8 | "sources": [ 9 | { 10 | "type": "archive", 11 | "url": "https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz", 12 | "sha256": "d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/modules/numactl.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "numactl", 3 | "buildsystem": "autotools", 4 | "cleanup": [ 5 | "/bin" 6 | ], 7 | "sources": [ 8 | { 9 | "type": "git", 10 | "url": "https://salsa.debian.org/debian/numactl.git", 11 | "commit": "640bb34497702f9aaeb8af1b491f32b91d03ec80", 12 | "tag": "debian/2.0.16-1", 13 | "x-checker-data": { 14 | "type": "git", 15 | "tag-pattern": "^debian\\/(\\d.+)$", 16 | "versions": { 17 | "<": "2.0.17" 18 | } 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/scripts/additional-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # User Service 4 | mkdir -p ~/.config/systemd/user 5 | cp /app/share/sunshine/systemd/user/sunshine.service $HOME/.config/systemd/user/sunshine.service 6 | echo Sunshine User Service has been installed. 7 | echo Use [systemctl --user enable sunshine] once to autostart Sunshine on login. 8 | 9 | # Udev rule 10 | UDEV=$(cat /app/share/sunshine/udev/rules.d/60-sunshine.rules) 11 | echo Configuring mouse permission. 12 | flatpak-spawn --host pkexec sh -c "echo '$UDEV' > /etc/udev/rules.d/60-sunshine.rules" 13 | echo Restart computer for mouse permission to take effect. 14 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/scripts/remove-additional-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # User Service 4 | systemctl --user stop sunshine 5 | rm $HOME/.config/systemd/user/sunshine.service 6 | systemctl --user daemon-reload 7 | echo Sunshine User Service has been removed. 8 | 9 | # Udev rule 10 | flatpak-spawn --host pkexec sh -c "rm /etc/udev/rules.d/60-sunshine.rules" 11 | echo Input rules removed. Restart computer to take effect. 12 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/scripts/sunshine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PORT=47990 4 | 5 | if ! curl -k https://localhost:$PORT > /dev/null 2>&1; then 6 | (sleep 3 && xdg-open https://localhost:$PORT) & 7 | exec sunshine "$@" 8 | else 9 | echo "Sunshine is already running, opening the web interface..." 10 | xdg-open https://localhost:$PORT 11 | fi 12 | -------------------------------------------------------------------------------- /packaging/linux/flatpak/sunshine.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Categories=AudioVideo;Network;RemoteAccess; 3 | Comment=@PROJECT_DESCRIPTION@ 4 | Exec=sunshine.sh 5 | Icon=@SUNSHINE_DESKTOP_ICON@ 6 | Keywords=gamestream;stream;moonlight;remote play; 7 | Name=@PROJECT_NAME@ 8 | Type=Application 9 | Version=1.0 10 | -------------------------------------------------------------------------------- /packaging/linux/sunshine.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @PROJECT_NAME@.desktop 4 | @PROJECT_LICENSE@ 5 | @PROJECT_LICENSE@ 6 | @PROJECT_NAME@ 7 | @CMAKE_PROJECT_HOMEPAGE_URL@ 8 | @PROJECT_BRIEF_DESCRIPTION@ 9 | 10 |

11 | @PROJECT_LONG_DESCRIPTION@ 12 |

13 |
14 | 15 | 16 | https://app.lizardbyte.dev/Sunshine/assets/images/AdobeStock_305732536_1920x1280.jpg 17 | Sunshine 18 | 19 | 20 |
21 | -------------------------------------------------------------------------------- /packaging/linux/sunshine.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=@PROJECT_NAME@ 4 | Exec=/usr/bin/env systemctl start --u sunshine 5 | Version=1.0 6 | Comment=@PROJECT_DESCRIPTION@ 7 | Icon=@SUNSHINE_DESKTOP_ICON@ 8 | Keywords=gamestream;stream;moonlight;remote play; 9 | Categories=AudioVideo;Network;RemoteAccess; 10 | Actions=RunInTerminal; 11 | 12 | [Desktop Action RunInTerminal] 13 | Name=Run in Terminal 14 | Icon=application-x-executable 15 | Exec=gio launch @CMAKE_INSTALL_FULL_DATAROOTDIR@/applications/sunshine_terminal.desktop 16 | -------------------------------------------------------------------------------- /packaging/linux/sunshine.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=@PROJECT_DESCRIPTION@ 3 | StartLimitIntervalSec=500 4 | StartLimitBurst=5 5 | 6 | [Service] 7 | # Avoid starting Sunshine before the desktop is fully initialized. 8 | ExecStartPre=/bin/sleep 5 9 | @SUNSHINE_SERVICE_START_COMMAND@ 10 | @SUNSHINE_SERVICE_STOP_COMMAND@ 11 | Restart=on-failure 12 | RestartSec=5s 13 | 14 | [Install] 15 | WantedBy=xdg-desktop-autostart.target 16 | -------------------------------------------------------------------------------- /packaging/linux/sunshine_terminal.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=@PROJECT_NAME@ 3 | Exec=sunshine 4 | Terminal=true 5 | Type=Application 6 | NoDisplay=true 7 | -------------------------------------------------------------------------------- /scripts/icons/convert_and_pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! [ -x "$(command -v ./go-png2ico)" ]; then 4 | echo "./go-png2ico not found" 5 | echo "download the executable from https://github.com/J-Siu/go-png2ico" 6 | echo "and drop it in this folder" 7 | exit 1 8 | fi 9 | 10 | if ! [ -x "$(command -v ./oxipng)" ]; then 11 | echo "./oxipng executable not found" 12 | echo "download the executable from https://github.com/shssoichiro/oxipng" 13 | echo "and drop it in this folder" 14 | exit 1 15 | fi 16 | 17 | if ! [ -x "$(command -v inkscape)" ]; then 18 | echo "inkscape executable not found" 19 | exit 1 20 | fi 21 | 22 | icon_base_sizes=(16 64) 23 | icon_sizes_keys=() # associative array to prevent duplicates 24 | icon_sizes_keys[256]=1 25 | 26 | for icon_base_size in ${icon_base_sizes[@]}; do 27 | # increment in 25% till 400% 28 | icon_size_increment=$((icon_base_size / 4)) 29 | for ((i = 0; i <= 12; i++)); do 30 | icon_sizes_keys[$((icon_base_size + i * icon_size_increment))]=1 31 | done 32 | done 33 | 34 | # convert to normal array 35 | icon_sizes=${!icon_sizes_keys[@]} 36 | 37 | echo "using icon sizes:" 38 | echo ${icon_sizes[@]} 39 | 40 | src_vectors=("../../src_assets/common/assets/web/public/images/sunshine-locked.svg" 41 | "../../src_assets/common/assets/web/public/images/sunshine-pausing.svg" 42 | "../../src_assets/common/assets/web/public/images/sunshine-playing.svg" 43 | "../../sunshine.svg") 44 | 45 | echo "using sources vectors:" 46 | echo ${src_vectors[@]} 47 | 48 | for src_vector in ${src_vectors[@]}; do 49 | file_name=`basename "$src_vector" .svg` 50 | png_files=() 51 | for icon_size in ${icon_sizes[@]}; do 52 | png_file="${file_name}${icon_size}.png" 53 | echo "converting ${png_file}" 54 | inkscape -w $icon_size -h $icon_size "$src_vector" --export-filename "${png_file}" && 55 | ./oxipng -o max --strip safe --alpha "${png_file}" && 56 | png_files+=("${png_file}") 57 | done 58 | 59 | echo "packing ${file_name}.ico" 60 | ./go-png2ico "${png_files[@]}" "${file_name}.ico" 61 | done 62 | -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.17.0 2 | clang-format==20.* 3 | -------------------------------------------------------------------------------- /scripts/update_clang_format.py: -------------------------------------------------------------------------------- 1 | # standard imports 2 | import os 3 | import subprocess 4 | 5 | # variables 6 | directories = [ 7 | 'src', 8 | 'tests', 9 | 'tools', 10 | ] 11 | file_types = [ 12 | 'cpp', 13 | 'cu', 14 | 'h', 15 | 'hpp', 16 | 'm', 17 | 'mm' 18 | ] 19 | 20 | 21 | def clang_format(file: str): 22 | print(f'Formatting {file} ...') 23 | subprocess.run(['clang-format', '-i', file]) 24 | 25 | 26 | def main(): 27 | """ 28 | Main entry point. 29 | """ 30 | # walk the directories 31 | for directory in directories: 32 | for root, dirs, files in os.walk(directory): 33 | for file in files: 34 | file_path = os.path.join(root, file) 35 | if os.path.isfile(file_path) and file.rsplit('.')[-1] in file_types: 36 | clang_format(file=file_path) 37 | 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /src/cbs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/cbs.h 3 | * @brief Declarations for FFmpeg Coded Bitstream API. 4 | */ 5 | #pragma once 6 | 7 | // local includes 8 | #include "utility.h" 9 | 10 | struct AVPacket; 11 | struct AVCodecContext; 12 | 13 | namespace cbs { 14 | 15 | struct nal_t { 16 | util::buffer_t _new; 17 | util::buffer_t old; 18 | }; 19 | 20 | struct hevc_t { 21 | nal_t vps; 22 | nal_t sps; 23 | }; 24 | 25 | struct h264_t { 26 | nal_t sps; 27 | }; 28 | 29 | hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); 30 | h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); 31 | 32 | /** 33 | * @brief Validates the Sequence Parameter Set (SPS) of a given packet. 34 | * @param packet The packet to validate. 35 | * @param codec_id The ID of the codec used (either AV_CODEC_ID_H264 or AV_CODEC_ID_H265). 36 | * @return True if the SPS->VUI is present in the active SPS of the packet, false otherwise. 37 | */ 38 | bool validate_sps(const AVPacket *packet, int codec_id); 39 | } // namespace cbs 40 | -------------------------------------------------------------------------------- /src/confighttp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/confighttp.h 3 | * @brief Declarations for the Web UI Config HTTP server. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // local includes 11 | #include "thread_safe.h" 12 | 13 | #define WEB_DIR SUNSHINE_ASSETS_DIR "/web/" 14 | 15 | namespace confighttp { 16 | constexpr auto PORT_HTTPS = 1; 17 | void start(); 18 | } // namespace confighttp 19 | 20 | // mime types map 21 | const std::map mime_types = { 22 | {"css", "text/css"}, 23 | {"gif", "image/gif"}, 24 | {"htm", "text/html"}, 25 | {"html", "text/html"}, 26 | {"ico", "image/x-icon"}, 27 | {"jpeg", "image/jpeg"}, 28 | {"jpg", "image/jpeg"}, 29 | {"js", "application/javascript"}, 30 | {"json", "application/json"}, 31 | {"png", "image/png"}, 32 | {"svg", "image/svg+xml"}, 33 | {"ttf", "font/ttf"}, 34 | {"txt", "text/plain"}, 35 | {"woff2", "font/woff2"}, 36 | {"xml", "text/xml"}, 37 | }; 38 | -------------------------------------------------------------------------------- /src/file_handler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file file_handler.cpp 3 | * @brief Definitions for file handling functions. 4 | */ 5 | 6 | // standard includes 7 | #include 8 | #include 9 | 10 | // local includes 11 | #include "file_handler.h" 12 | #include "logging.h" 13 | 14 | namespace file_handler { 15 | std::string get_parent_directory(const std::string &path) { 16 | // remove any trailing path separators 17 | std::string trimmed_path = path; 18 | while (!trimmed_path.empty() && trimmed_path.back() == '/') { 19 | trimmed_path.pop_back(); 20 | } 21 | 22 | std::filesystem::path p(trimmed_path); 23 | return p.parent_path().string(); 24 | } 25 | 26 | bool make_directory(const std::string &path) { 27 | // first, check if the directory already exists 28 | if (std::filesystem::exists(path)) { 29 | return true; 30 | } 31 | 32 | return std::filesystem::create_directories(path); 33 | } 34 | 35 | std::string read_file(const char *path) { 36 | if (!std::filesystem::exists(path)) { 37 | BOOST_LOG(debug) << "Missing file: " << path; 38 | return {}; 39 | } 40 | 41 | std::ifstream in(path); 42 | return std::string {(std::istreambuf_iterator(in)), std::istreambuf_iterator()}; 43 | } 44 | 45 | int write_file(const char *path, const std::string_view &contents) { 46 | std::ofstream out(path); 47 | 48 | if (!out.is_open()) { 49 | return -1; 50 | } 51 | 52 | out << contents; 53 | 54 | return 0; 55 | } 56 | } // namespace file_handler 57 | -------------------------------------------------------------------------------- /src/file_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file file_handler.h 3 | * @brief Declarations for file handling functions. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | /** 11 | * @brief Responsible for file handling functions. 12 | */ 13 | namespace file_handler { 14 | /** 15 | * @brief Get the parent directory of a file or directory. 16 | * @param path The path of the file or directory. 17 | * @return The parent directory. 18 | * @examples 19 | * std::string parent_dir = get_parent_directory("path/to/file"); 20 | * @examples_end 21 | */ 22 | std::string get_parent_directory(const std::string &path); 23 | 24 | /** 25 | * @brief Make a directory. 26 | * @param path The path of the directory. 27 | * @return `true` on success, `false` on failure. 28 | * @examples 29 | * bool dir_created = make_directory("path/to/directory"); 30 | * @examples_end 31 | */ 32 | bool make_directory(const std::string &path); 33 | 34 | /** 35 | * @brief Read a file to string. 36 | * @param path The path of the file. 37 | * @return The contents of the file. 38 | * @examples 39 | * std::string contents = read_file("path/to/file"); 40 | * @examples_end 41 | */ 42 | std::string read_file(const char *path); 43 | 44 | /** 45 | * @brief Writes a file. 46 | * @param path The path of the file. 47 | * @param contents The contents to write. 48 | * @return ``0`` on success, ``-1`` on failure. 49 | * @examples 50 | * int write_status = write_file("path/to/file", "file contents"); 51 | * @examples_end 52 | */ 53 | int write_file(const char *path, const std::string_view &contents); 54 | } // namespace file_handler 55 | -------------------------------------------------------------------------------- /src/globals.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file globals.cpp 3 | * @brief Definitions for globally accessible variables and functions. 4 | */ 5 | // local includes 6 | #include "globals.h" 7 | 8 | safe::mail_t mail::man; 9 | thread_pool_util::ThreadPool task_pool; 10 | bool display_cursor = true; 11 | 12 | #ifdef _WIN32 13 | nvprefs::nvprefs_interface nvprefs_instance; 14 | #endif 15 | -------------------------------------------------------------------------------- /src/globals.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file globals.h 3 | * @brief Declarations for globally accessible variables and functions. 4 | */ 5 | #pragma once 6 | 7 | // local includes 8 | #include "entry_handler.h" 9 | #include "thread_pool.h" 10 | 11 | /** 12 | * @brief A thread pool for processing tasks. 13 | */ 14 | extern thread_pool_util::ThreadPool task_pool; 15 | 16 | /** 17 | * @brief A boolean flag to indicate whether the cursor should be displayed. 18 | */ 19 | extern bool display_cursor; 20 | 21 | #ifdef _WIN32 22 | // Declare global singleton used for NVIDIA control panel modifications 23 | #include "platform/windows/nvprefs/nvprefs_interface.h" 24 | 25 | /** 26 | * @brief A global singleton used for NVIDIA control panel modifications. 27 | */ 28 | extern nvprefs::nvprefs_interface nvprefs_instance; 29 | #endif 30 | 31 | /** 32 | * @brief Handles process-wide communication. 33 | */ 34 | namespace mail { 35 | #define MAIL(x) \ 36 | constexpr auto x = std::string_view { \ 37 | #x \ 38 | } 39 | 40 | /** 41 | * @brief A process-wide communication mechanism. 42 | */ 43 | extern safe::mail_t man; 44 | 45 | // Global mail 46 | MAIL(shutdown); 47 | MAIL(broadcast_shutdown); 48 | MAIL(video_packets); 49 | MAIL(audio_packets); 50 | MAIL(switch_display); 51 | 52 | // Local mail 53 | MAIL(touch_port); 54 | MAIL(idr); 55 | MAIL(invalidate_ref_frames); 56 | MAIL(gamepad_feedback); 57 | MAIL(hdr); 58 | #undef MAIL 59 | 60 | } // namespace mail 61 | -------------------------------------------------------------------------------- /src/httpcommon.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/httpcommon.h 3 | * @brief Declarations for common HTTP. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | 10 | // local includes 11 | #include "network.h" 12 | #include "thread_safe.h" 13 | 14 | namespace http { 15 | 16 | int init(); 17 | int create_creds(const std::string &pkey, const std::string &cert); 18 | int save_user_creds( 19 | const std::string &file, 20 | const std::string &username, 21 | const std::string &password, 22 | bool run_our_mouth = false 23 | ); 24 | 25 | int reload_user_creds(const std::string &file); 26 | bool download_file(const std::string &url, const std::string &file, long ssl_version = CURL_SSLVERSION_TLSv1_2); 27 | std::string url_escape(const std::string &url); 28 | std::string url_get_host(const std::string &url); 29 | 30 | extern std::string unique_id; 31 | extern net::net_e origin_web_ui_allowed; 32 | 33 | } // namespace http 34 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/input.h 3 | * @brief Declarations for gamepad, keyboard, and mouse input handling. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // local includes 11 | #include "platform/common.h" 12 | #include "thread_safe.h" 13 | 14 | namespace input { 15 | struct input_t; 16 | 17 | void print(void *input); 18 | void reset(std::shared_ptr &input); 19 | void passthrough(std::shared_ptr &input, std::vector &&input_data); 20 | 21 | [[nodiscard]] std::unique_ptr init(); 22 | 23 | bool probe_gamepads(); 24 | 25 | std::shared_ptr alloc(safe::mail_t mail); 26 | 27 | struct touch_port_t: public platf::touch_port_t { 28 | int env_width, env_height; 29 | 30 | // Offset x and y coordinates of the client 31 | float client_offsetX, client_offsetY; 32 | 33 | float scalar_inv; 34 | 35 | explicit operator bool() const { 36 | return width != 0 && height != 0 && env_width != 0 && env_height != 0; 37 | } 38 | }; 39 | 40 | /** 41 | * @brief Scale the ellipse axes according to the provided size. 42 | * @param val The major and minor axis pair. 43 | * @param rotation The rotation value from the touch/pen event. 44 | * @param scalar The scalar cartesian coordinate pair. 45 | * @return The major and minor axis pair. 46 | */ 47 | std::pair scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar); 48 | } // namespace input 49 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/main.h 3 | * @brief Declarations for the main entry point for Sunshine. 4 | */ 5 | #pragma once 6 | 7 | /** 8 | * @brief Main application entry point. 9 | * @param argc The number of arguments. 10 | * @param argv The arguments. 11 | * @examples 12 | * main(1, const char* args[] = {"sunshine", nullptr}); 13 | * @examples_end 14 | */ 15 | int main(int argc, char *argv[]); 16 | -------------------------------------------------------------------------------- /src/move_by_copy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/move_by_copy.h 3 | * @brief Declarations for the MoveByCopy utility class. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | /** 11 | * @brief Contains utilities for moving objects by copying them. 12 | */ 13 | namespace move_by_copy_util { 14 | /** 15 | * When a copy is made, it moves the object 16 | * This allows you to move an object when a move can't be done. 17 | */ 18 | template 19 | class MoveByCopy { 20 | public: 21 | typedef T move_type; 22 | 23 | private: 24 | move_type _to_move; 25 | 26 | public: 27 | explicit MoveByCopy(move_type &&to_move): 28 | _to_move(std::move(to_move)) { 29 | } 30 | 31 | MoveByCopy(MoveByCopy &&other) = default; 32 | 33 | MoveByCopy(const MoveByCopy &other) { 34 | *this = other; 35 | } 36 | 37 | MoveByCopy &operator=(MoveByCopy &&other) = default; 38 | 39 | MoveByCopy &operator=(const MoveByCopy &other) { 40 | this->_to_move = std::move(const_cast(other)._to_move); 41 | 42 | return *this; 43 | } 44 | 45 | operator move_type() { 46 | return std::move(_to_move); 47 | } 48 | }; 49 | 50 | template 51 | MoveByCopy cmove(T &movable) { 52 | return MoveByCopy(std::move(movable)); 53 | } 54 | 55 | // Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller 56 | template 57 | MoveByCopy const_cmove(const T &movable) { 58 | return MoveByCopy(std::move(const_cast(movable))); 59 | } 60 | } // namespace move_by_copy_util 61 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_colorspace.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_colorspace.h 3 | * @brief Declarations for NVENC YUV colorspace. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | 10 | namespace nvenc { 11 | 12 | /** 13 | * @brief YUV colorspace and color range. 14 | */ 15 | struct nvenc_colorspace_t { 16 | NV_ENC_VUI_COLOR_PRIMARIES primaries; 17 | NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; 18 | NV_ENC_VUI_MATRIX_COEFFS matrix; 19 | bool full_range; 20 | }; 21 | 22 | } // namespace nvenc 23 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_config.h 3 | * @brief Declarations for NVENC encoder configuration. 4 | */ 5 | #pragma once 6 | 7 | namespace nvenc { 8 | 9 | enum class nvenc_two_pass { 10 | disabled, ///< Single pass, the fastest and no extra vram 11 | quarter_resolution, ///< Larger motion vectors being caught, faster and uses less extra vram 12 | full_resolution, ///< Better overall statistics, slower and uses more extra vram 13 | }; 14 | 15 | /** 16 | * @brief NVENC encoder configuration. 17 | */ 18 | struct nvenc_config { 19 | // Quality preset from 1 to 7, higher is slower 20 | int quality_preset = 1; 21 | 22 | // Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores 23 | nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution; 24 | 25 | // Percentage increase of VBV/HRD from the default single frame, allows low-latency variable bitrate 26 | int vbv_percentage_increase = 0; 27 | 28 | // Improves fades compression, uses CUDA cores 29 | bool weighted_prediction = false; 30 | 31 | // Allocate more bitrate to flat regions since they're visually more perceptible, uses CUDA cores 32 | bool adaptive_quantization = false; 33 | 34 | // Don't use QP below certain value, limits peak image quality to save bitrate 35 | bool enable_min_qp = false; 36 | 37 | // Min QP value for H.264 when enable_min_qp is selected 38 | unsigned min_qp_h264 = 19; 39 | 40 | // Min QP value for HEVC when enable_min_qp is selected 41 | unsigned min_qp_hevc = 23; 42 | 43 | // Min QP value for AV1 when enable_min_qp is selected 44 | unsigned min_qp_av1 = 23; 45 | 46 | // Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here for historical reasons 47 | bool h264_cavlc = false; 48 | 49 | // Add filler data to encoded frames to stay at target bitrate, mainly for testing 50 | bool insert_filler_data = false; 51 | }; 52 | 53 | } // namespace nvenc 54 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_d3d11.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_d3d11.cpp 3 | * @brief Definitions for abstract Direct3D11 NVENC encoder. 4 | */ 5 | // local includes 6 | #include "src/logging.h" 7 | 8 | #ifdef _WIN32 9 | #include "nvenc_d3d11.h" 10 | 11 | namespace nvenc { 12 | 13 | nvenc_d3d11::nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type): 14 | nvenc_base(device_type) { 15 | async_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); 16 | } 17 | 18 | nvenc_d3d11::~nvenc_d3d11() { 19 | if (dll) { 20 | FreeLibrary(dll); 21 | dll = NULL; 22 | } 23 | if (async_event_handle) { 24 | CloseHandle(async_event_handle); 25 | } 26 | } 27 | 28 | bool nvenc_d3d11::init_library() { 29 | if (dll) { 30 | return true; 31 | } 32 | 33 | #ifdef _WIN64 34 | constexpr auto dll_name = "nvEncodeAPI64.dll"; 35 | #else 36 | constexpr auto dll_name = "nvEncodeAPI.dll"; 37 | #endif 38 | 39 | if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { 40 | if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) { 41 | auto new_nvenc = std::make_unique(); 42 | new_nvenc->version = min_struct_version(NV_ENCODE_API_FUNCTION_LIST_VER); 43 | if (nvenc_failed(create_instance(new_nvenc.get()))) { 44 | BOOST_LOG(error) << "NvEnc: NvEncodeAPICreateInstance() failed: " << last_nvenc_error_string; 45 | } else { 46 | nvenc = std::move(new_nvenc); 47 | return true; 48 | } 49 | } else { 50 | BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in " << dll_name; 51 | } 52 | } else { 53 | BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name; 54 | } 55 | 56 | if (dll) { 57 | FreeLibrary(dll); 58 | dll = NULL; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | bool nvenc_d3d11::wait_for_async_event(uint32_t timeout_ms) { 65 | return WaitForSingleObject(async_event_handle, timeout_ms) == WAIT_OBJECT_0; 66 | } 67 | 68 | } // namespace nvenc 69 | #endif 70 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_d3d11.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_d3d11.h 3 | * @brief Declarations for abstract Direct3D11 NVENC encoder. 4 | */ 5 | #pragma once 6 | #ifdef _WIN32 7 | 8 | // standard includes 9 | #include 10 | #include 11 | 12 | // local includes 13 | #include "nvenc_base.h" 14 | 15 | namespace nvenc { 16 | 17 | _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); 18 | _COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); 19 | _COM_SMARTPTR_TYPEDEF(IDXGIDevice, IID_IDXGIDevice); 20 | _COM_SMARTPTR_TYPEDEF(IDXGIAdapter, IID_IDXGIAdapter); 21 | 22 | /** 23 | * @brief Abstract Direct3D11 NVENC encoder. 24 | * Encapsulates common code used by native and interop implementations. 25 | */ 26 | class nvenc_d3d11: public nvenc_base { 27 | public: 28 | explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type); 29 | ~nvenc_d3d11(); 30 | 31 | /** 32 | * @brief Get input surface texture. 33 | * @return Input surface texture. 34 | */ 35 | virtual ID3D11Texture2D *get_input_texture() = 0; 36 | 37 | protected: 38 | bool init_library() override; 39 | bool wait_for_async_event(uint32_t timeout_ms) override; 40 | 41 | private: 42 | HMODULE dll = NULL; 43 | }; 44 | 45 | } // namespace nvenc 46 | #endif 47 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_d3d11_native.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_d3d11_native.h 3 | * @brief Declarations for native Direct3D11 NVENC encoder. 4 | */ 5 | #pragma once 6 | #ifdef _WIN32 7 | // standard includes 8 | #include 9 | #include 10 | 11 | // local includes 12 | #include "nvenc_d3d11.h" 13 | 14 | namespace nvenc { 15 | 16 | /** 17 | * @brief Native Direct3D11 NVENC encoder. 18 | */ 19 | class nvenc_d3d11_native final: public nvenc_d3d11 { 20 | public: 21 | /** 22 | * @param d3d_device Direct3D11 device used for encoding. 23 | */ 24 | explicit nvenc_d3d11_native(ID3D11Device *d3d_device); 25 | ~nvenc_d3d11_native(); 26 | 27 | ID3D11Texture2D *get_input_texture() override; 28 | 29 | private: 30 | bool create_and_register_input_buffer() override; 31 | 32 | const ID3D11DevicePtr d3d_device; 33 | ID3D11Texture2DPtr d3d_input_texture; 34 | }; 35 | 36 | } // namespace nvenc 37 | #endif 38 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_encoded_frame.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_encoded_frame.h 3 | * @brief Declarations for NVENC encoded frame. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | 11 | namespace nvenc { 12 | 13 | /** 14 | * @brief Encoded frame. 15 | */ 16 | struct nvenc_encoded_frame { 17 | std::vector data; 18 | uint64_t frame_index = 0; 19 | bool idr = false; 20 | bool after_ref_frame_invalidation = false; 21 | }; 22 | 23 | } // namespace nvenc 24 | -------------------------------------------------------------------------------- /src/nvenc/nvenc_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/nvenc/nvenc_utils.h 3 | * @brief Declarations for NVENC utilities. 4 | */ 5 | #pragma once 6 | 7 | // plafform includes 8 | #ifdef _WIN32 9 | #include 10 | #endif 11 | 12 | // lib includes 13 | #include 14 | 15 | // local includes 16 | #include "nvenc_colorspace.h" 17 | #include "src/platform/common.h" 18 | #include "src/video_colorspace.h" 19 | 20 | namespace nvenc { 21 | 22 | #ifdef _WIN32 23 | DXGI_FORMAT dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format); 24 | #endif 25 | 26 | NV_ENC_BUFFER_FORMAT nvenc_format_from_sunshine_format(platf::pix_fmt_e format); 27 | 28 | nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace); 29 | 30 | } // namespace nvenc 31 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_gamepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_gamepad.h 3 | * @brief Declarations for inputtino gamepad input handling. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | #include 10 | #include 11 | 12 | // local includes 13 | #include "inputtino_common.h" 14 | #include "src/platform/common.h" 15 | 16 | using namespace std::literals; 17 | 18 | namespace platf::gamepad { 19 | 20 | enum ControllerType { 21 | XboxOneWired, ///< Xbox One Wired Controller 22 | DualSenseWired, ///< DualSense Wired Controller 23 | SwitchProWired ///< Switch Pro Wired Controller 24 | }; 25 | 26 | int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); 27 | 28 | void free(input_raw_t *raw, int nr); 29 | 30 | void update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state); 31 | 32 | void touch(input_raw_t *raw, const gamepad_touch_t &touch); 33 | 34 | void motion(input_raw_t *raw, const gamepad_motion_t &motion); 35 | 36 | void battery(input_raw_t *raw, const gamepad_battery_t &battery); 37 | 38 | std::vector &supported_gamepads(input_t *input); 39 | } // namespace platf::gamepad 40 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_keyboard.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_keyboard.h 3 | * @brief Declarations for inputtino keyboard input handling. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | #include 10 | #include 11 | 12 | // local includes 13 | #include "inputtino_common.h" 14 | 15 | using namespace std::literals; 16 | 17 | namespace platf::keyboard { 18 | void update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags); 19 | 20 | void unicode(input_raw_t *raw, char *utf8, int size); 21 | } // namespace platf::keyboard 22 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_mouse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_mouse.h 3 | * @brief Declarations for inputtino mouse input handling. 4 | */ 5 | #pragma once 6 | // lib includes 7 | #include 8 | #include 9 | #include 10 | 11 | // local includes 12 | #include "inputtino_common.h" 13 | #include "src/platform/common.h" 14 | 15 | using namespace std::literals; 16 | 17 | namespace platf::mouse { 18 | void move(input_raw_t *raw, int deltaX, int deltaY); 19 | 20 | void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y); 21 | 22 | void button(input_raw_t *raw, int button, bool release); 23 | 24 | void scroll(input_raw_t *raw, int high_res_distance); 25 | 26 | void hscroll(input_raw_t *raw, int high_res_distance); 27 | 28 | util::point_t get_location(input_raw_t *raw); 29 | } // namespace platf::mouse 30 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_pen.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_pen.h 3 | * @brief Declarations for inputtino pen input handling. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | #include 10 | #include 11 | 12 | // local includes 13 | #include "inputtino_common.h" 14 | #include "src/platform/common.h" 15 | 16 | using namespace std::literals; 17 | 18 | namespace platf::pen { 19 | void update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen); 20 | } 21 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_touch.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_touch.cpp 3 | * @brief Definitions for inputtino touch input handling. 4 | */ 5 | // lib includes 6 | #include 7 | #include 8 | #include 9 | 10 | // local includes 11 | #include "inputtino_common.h" 12 | #include "inputtino_touch.h" 13 | #include "src/config.h" 14 | #include "src/logging.h" 15 | #include "src/platform/common.h" 16 | #include "src/utility.h" 17 | 18 | using namespace std::literals; 19 | 20 | namespace platf::touch { 21 | void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) { 22 | if (raw->touch) { 23 | switch (touch.eventType) { 24 | case LI_TOUCH_EVENT_HOVER: 25 | case LI_TOUCH_EVENT_DOWN: 26 | case LI_TOUCH_EVENT_MOVE: 27 | { 28 | // Convert our 0..360 range to -90..90 relative to Y axis 29 | int adjusted_angle = touch.rotation; 30 | 31 | if (adjusted_angle > 90 && adjusted_angle < 270) { 32 | // Lower hemisphere 33 | adjusted_angle = 180 - adjusted_angle; 34 | } 35 | 36 | // Wrap the value if it's out of range 37 | if (adjusted_angle > 90) { 38 | adjusted_angle -= 360; 39 | } else if (adjusted_angle < -90) { 40 | adjusted_angle += 360; 41 | } 42 | (*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle); 43 | break; 44 | } 45 | case LI_TOUCH_EVENT_CANCEL: 46 | case LI_TOUCH_EVENT_UP: 47 | case LI_TOUCH_EVENT_HOVER_LEAVE: 48 | { 49 | (*raw->touch).release_finger(touch.pointerId); 50 | break; 51 | } 52 | // TODO: LI_TOUCH_EVENT_CANCEL_ALL 53 | } 54 | } 55 | } 56 | } // namespace platf::touch 57 | -------------------------------------------------------------------------------- /src/platform/linux/input/inputtino_touch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/input/inputtino_touch.h 3 | * @brief Declarations for inputtino touch input handling. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | #include 10 | #include 11 | 12 | // local includes 13 | #include "inputtino_common.h" 14 | #include "src/platform/common.h" 15 | 16 | using namespace std::literals; 17 | 18 | namespace platf::touch { 19 | void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch); 20 | } 21 | -------------------------------------------------------------------------------- /src/platform/linux/misc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/misc.h 3 | * @brief Miscellaneous declarations for Linux. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | 11 | // local includes 12 | #include "src/utility.h" 13 | 14 | KITTY_USING_MOVE_T(file_t, int, -1, { 15 | if (el >= 0) { 16 | close(el); 17 | } 18 | }); 19 | 20 | enum class window_system_e { 21 | NONE, ///< No window system 22 | X11, ///< X11 23 | WAYLAND, ///< Wayland 24 | }; 25 | 26 | extern window_system_e window_system; 27 | 28 | namespace dyn { 29 | typedef void (*apiproc)(void); 30 | 31 | int load(void *handle, const std::vector> &funcs, bool strict = true); 32 | void *handle(const std::vector &libs); 33 | 34 | } // namespace dyn 35 | -------------------------------------------------------------------------------- /src/platform/linux/vaapi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/vaapi.h 3 | * @brief Declarations for VA-API hardware accelerated capture. 4 | */ 5 | #pragma once 6 | 7 | // local includes 8 | #include "misc.h" 9 | #include "src/platform/common.h" 10 | 11 | namespace egl { 12 | struct surface_descriptor_t; 13 | } 14 | 15 | namespace va { 16 | /** 17 | * Width --> Width of the image 18 | * Height --> Height of the image 19 | * offset_x --> Horizontal offset of the image in the texture 20 | * offset_y --> Vertical offset of the image in the texture 21 | * file_t card --> The file descriptor of the render device used for encoding 22 | */ 23 | std::unique_ptr make_avcodec_encode_device(int width, int height, bool vram); 24 | std::unique_ptr make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram); 25 | std::unique_ptr make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram); 26 | 27 | // Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured 28 | bool validate(int fd); 29 | } // namespace va 30 | -------------------------------------------------------------------------------- /src/platform/linux/x11grab.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/linux/x11grab.h 3 | * @brief Declarations for x11 capture. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // local includes 11 | #include "src/platform/common.h" 12 | #include "src/utility.h" 13 | 14 | // X11 Display 15 | extern "C" struct _XDisplay; 16 | 17 | namespace egl { 18 | class cursor_t; 19 | } 20 | 21 | namespace platf::x11 { 22 | struct cursor_ctx_raw_t; 23 | void freeCursorCtx(cursor_ctx_raw_t *ctx); 24 | void freeDisplay(_XDisplay *xdisplay); 25 | 26 | using cursor_ctx_t = util::safe_ptr; 27 | using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>; 28 | 29 | class cursor_t { 30 | public: 31 | static std::optional make(); 32 | 33 | void capture(egl::cursor_t &img); 34 | 35 | /** 36 | * Capture and blend the cursor into the image 37 | * 38 | * img <-- destination image 39 | * offsetX, offsetY <--- Top left corner of the virtual screen 40 | */ 41 | void blend(img_t &img, int offsetX, int offsetY); 42 | 43 | cursor_ctx_t ctx; 44 | }; 45 | 46 | xdisplay_t make_display(); 47 | } // namespace platf::x11 48 | -------------------------------------------------------------------------------- /src/platform/macos/av_audio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/macos/av_audio.h 3 | * @brief Declarations for audio capture on macOS. 4 | */ 5 | #pragma once 6 | 7 | // platform includes 8 | #import 9 | 10 | // lib includes 11 | #include "third-party/TPCircularBuffer/TPCircularBuffer.h" 12 | 13 | #define kBufferLength 4096 14 | 15 | @interface AVAudio: NSObject { 16 | @public 17 | TPCircularBuffer audioSampleBuffer; 18 | } 19 | 20 | @property (nonatomic, assign) AVCaptureSession *audioCaptureSession; 21 | @property (nonatomic, assign) AVCaptureConnection *audioConnection; 22 | @property (nonatomic, assign) NSCondition *samplesArrivedSignal; 23 | 24 | + (NSArray *)microphoneNames; 25 | + (AVCaptureDevice *)findMicrophone:(NSString *)name; 26 | 27 | - (int)setupMicrophone:(AVCaptureDevice *)device sampleRate:(UInt32)sampleRate frameSize:(UInt32)frameSize channels:(UInt8)channels; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /src/platform/macos/av_img_t.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/macos/av_img_t.h 3 | * @brief Declarations for AV image types on macOS. 4 | */ 5 | #pragma once 6 | 7 | // platform includes 8 | #include 9 | #include 10 | 11 | // local includes 12 | #include "src/platform/common.h" 13 | 14 | namespace platf { 15 | struct av_sample_buf_t { 16 | CMSampleBufferRef buf; 17 | 18 | explicit av_sample_buf_t(CMSampleBufferRef buf): 19 | buf((CMSampleBufferRef) CFRetain(buf)) { 20 | } 21 | 22 | ~av_sample_buf_t() { 23 | if (buf != nullptr) { 24 | CFRelease(buf); 25 | } 26 | } 27 | }; 28 | 29 | struct av_pixel_buf_t { 30 | CVPixelBufferRef buf; 31 | 32 | // Constructor 33 | explicit av_pixel_buf_t(CMSampleBufferRef sb): 34 | buf( 35 | CMSampleBufferGetImageBuffer(sb) 36 | ) { 37 | CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); 38 | } 39 | 40 | [[nodiscard]] uint8_t *data() const { 41 | return static_cast(CVPixelBufferGetBaseAddress(buf)); 42 | } 43 | 44 | // Destructor 45 | ~av_pixel_buf_t() { 46 | if (buf != nullptr) { 47 | CVPixelBufferUnlockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); 48 | } 49 | } 50 | }; 51 | 52 | struct av_img_t: img_t { 53 | std::shared_ptr sample_buffer; 54 | std::shared_ptr pixel_buffer; 55 | }; 56 | 57 | struct temp_retain_av_img_t { 58 | std::shared_ptr sample_buffer; 59 | std::shared_ptr pixel_buffer; 60 | uint8_t *data; 61 | 62 | temp_retain_av_img_t( 63 | std::shared_ptr sb, 64 | std::shared_ptr pb, 65 | uint8_t *dt 66 | ): 67 | sample_buffer(std::move(sb)), 68 | pixel_buffer(std::move(pb)), 69 | data(dt) { 70 | } 71 | }; 72 | } // namespace platf 73 | -------------------------------------------------------------------------------- /src/platform/macos/av_video.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/macos/av_video.h 3 | * @brief Declarations for video capture on macOS. 4 | */ 5 | #pragma once 6 | 7 | // platform includes 8 | #import 9 | #import 10 | 11 | struct CaptureSession { 12 | AVCaptureVideoDataOutput *output; 13 | NSCondition *captureStopped; 14 | }; 15 | 16 | @interface AVVideo: NSObject 17 | 18 | #define kMaxDisplays 32 19 | 20 | @property (nonatomic, assign) CGDirectDisplayID displayID; 21 | @property (nonatomic, assign) CMTime minFrameDuration; 22 | @property (nonatomic, assign) OSType pixelFormat; 23 | @property (nonatomic, assign) int frameWidth; 24 | @property (nonatomic, assign) int frameHeight; 25 | 26 | typedef bool (^FrameCallbackBlock)(CMSampleBufferRef); 27 | 28 | @property (nonatomic, assign) AVCaptureSession *session; 29 | @property (nonatomic, assign) NSMapTable *videoOutputs; 30 | @property (nonatomic, assign) NSMapTable *captureCallbacks; 31 | @property (nonatomic, assign) NSMapTable *captureSignals; 32 | 33 | + (NSArray *)displayNames; 34 | + (NSString *)getDisplayName:(CGDirectDisplayID)displayID; 35 | 36 | - (id)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate; 37 | 38 | - (void)setFrameWidth:(int)frameWidth frameHeight:(int)frameHeight; 39 | - (dispatch_semaphore_t)capture:(FrameCallbackBlock)frameCallback; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /src/platform/macos/misc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/macos/misc.h 3 | * @brief Miscellaneous declarations for macOS platform. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // platform includes 11 | #include 12 | 13 | namespace platf { 14 | bool is_screen_capture_allowed(); 15 | } 16 | 17 | namespace dyn { 18 | typedef void (*apiproc)(); 19 | 20 | int load(void *handle, const std::vector> &funcs, bool strict = true); 21 | void *handle(const std::vector &libs); 22 | 23 | } // namespace dyn 24 | -------------------------------------------------------------------------------- /src/platform/macos/nv12_zero_device.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/macos/nv12_zero_device.h 3 | * @brief Declarations for NV12 zero copy device on macOS. 4 | */ 5 | #pragma once 6 | 7 | // local includes 8 | #include "src/platform/common.h" 9 | 10 | struct AVFrame; 11 | 12 | namespace platf { 13 | void free_frame(AVFrame *frame); 14 | 15 | class nv12_zero_device: public avcodec_encode_device_t { 16 | // display holds a pointer to an av_video object. Since the namespaces of AVFoundation 17 | // and FFMPEG collide, we need this opaque pointer and cannot use the definition 18 | void *display; 19 | 20 | public: 21 | // this function is used to set the resolution on an av_video object that we cannot 22 | // call directly because of namespace collisions between AVFoundation and FFMPEG 23 | using resolution_fn_t = std::function; 24 | resolution_fn_t resolution_fn; 25 | using pixel_format_fn_t = std::function; 26 | 27 | int init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn); 28 | 29 | int convert(img_t &img) override; 30 | int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override; 31 | 32 | private: 33 | util::safe_ptr av_frame; 34 | }; 35 | 36 | } // namespace platf 37 | -------------------------------------------------------------------------------- /src/platform/windows/misc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/misc.h 3 | * @brief Miscellaneous declarations for Windows. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | 11 | // platform includes 12 | #include 13 | #include 14 | 15 | namespace platf { 16 | void print_status(const std::string_view &prefix, HRESULT status); 17 | HDESK syncThreadDesktop(); 18 | 19 | int64_t qpc_counter(); 20 | 21 | std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2); 22 | 23 | /** 24 | * @brief Convert a UTF-8 string into a UTF-16 wide string. 25 | * @param string The UTF-8 string. 26 | * @return The converted UTF-16 wide string. 27 | */ 28 | std::wstring from_utf8(const std::string &string); 29 | 30 | /** 31 | * @brief Convert a UTF-16 wide string into a UTF-8 string. 32 | * @param string The UTF-16 wide string. 33 | * @return The converted UTF-8 string. 34 | */ 35 | std::string to_utf8(const std::wstring &string); 36 | } // namespace platf 37 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/driver_settings.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/driver_settings.h 3 | * @brief Declarations for nvidia driver settings. 4 | */ 5 | #pragma once 6 | 7 | // nvapi headers 8 | // disable clang-format header reordering 9 | // as needs types from 10 | // clang-format off 11 | #include 12 | #include 13 | // clang-format on 14 | 15 | // local includes 16 | #include "undo_data.h" 17 | 18 | namespace nvprefs { 19 | 20 | class driver_settings_t { 21 | public: 22 | ~driver_settings_t(); 23 | 24 | bool init(); 25 | 26 | void destroy(); 27 | 28 | bool load_settings(); 29 | 30 | bool save_settings(); 31 | 32 | bool restore_global_profile_to_undo(const undo_data_t &undo_data); 33 | 34 | bool check_and_modify_global_profile(std::optional &undo_data); 35 | 36 | bool check_and_modify_application_profile(bool &modified); 37 | 38 | private: 39 | NvDRSSessionHandle session_handle = 0; 40 | }; 41 | 42 | } // namespace nvprefs 43 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/nvprefs_common.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/nvprefs_common.cpp 3 | * @brief Definitions for common nvidia preferences. 4 | */ 5 | // this include 6 | #include "nvprefs_common.h" 7 | 8 | // local includes 9 | #include "src/config.h" 10 | #include "src/logging.h" 11 | 12 | namespace nvprefs { 13 | 14 | void info_message(const std::wstring &message) { 15 | BOOST_LOG(info) << "nvprefs: " << message; 16 | } 17 | 18 | void info_message(const std::string &message) { 19 | BOOST_LOG(info) << "nvprefs: " << message; 20 | } 21 | 22 | void error_message(const std::wstring &message) { 23 | BOOST_LOG(error) << "nvprefs: " << message; 24 | } 25 | 26 | void error_message(const std::string &message) { 27 | BOOST_LOG(error) << "nvprefs: " << message; 28 | } 29 | 30 | nvprefs_options get_nvprefs_options() { 31 | nvprefs_options options; 32 | options.opengl_vulkan_on_dxgi = config::video.nv_opengl_vulkan_on_dxgi; 33 | options.sunshine_high_power_mode = config::video.nv_sunshine_high_power_mode; 34 | return options; 35 | } 36 | 37 | } // namespace nvprefs 38 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/nvprefs_common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/nvprefs_common.h 3 | * @brief Declarations for common nvidia preferences. 4 | */ 5 | #pragma once 6 | 7 | // platform includes 8 | // disable clang-format header reordering 9 | // clang-format off 10 | #include 11 | #include 12 | // clang-format on 13 | 14 | // local includes 15 | #include "src/utility.h" 16 | 17 | namespace nvprefs { 18 | 19 | struct safe_handle: public util::safe_ptr_v2 { 20 | using util::safe_ptr_v2::safe_ptr_v2; 21 | 22 | explicit operator bool() const { 23 | auto handle = get(); 24 | return handle != NULL && handle != INVALID_HANDLE_VALUE; 25 | } 26 | }; 27 | 28 | struct safe_hlocal_deleter { 29 | void operator()(void *p) { 30 | LocalFree(p); 31 | } 32 | }; 33 | 34 | template 35 | using safe_hlocal = util::uniq_ptr, safe_hlocal_deleter>; 36 | 37 | using safe_sid = util::safe_ptr_v2; 38 | 39 | void info_message(const std::wstring &message); 40 | 41 | void info_message(const std::string &message); 42 | 43 | void error_message(const std::wstring &message); 44 | 45 | void error_message(const std::string &message); 46 | 47 | struct nvprefs_options { 48 | bool opengl_vulkan_on_dxgi = true; 49 | bool sunshine_high_power_mode = true; 50 | }; 51 | 52 | nvprefs_options get_nvprefs_options(); 53 | 54 | } // namespace nvprefs 55 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/nvprefs_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/nvprefs_interface.h 3 | * @brief Declarations for nvidia preferences interface. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | namespace nvprefs { 11 | 12 | class nvprefs_interface { 13 | public: 14 | nvprefs_interface(); 15 | ~nvprefs_interface(); 16 | 17 | bool load(); 18 | 19 | void unload(); 20 | 21 | bool restore_from_and_delete_undo_file_if_exists(); 22 | 23 | bool modify_application_profile(); 24 | 25 | bool modify_global_profile(); 26 | 27 | bool owning_undo_file(); 28 | 29 | bool restore_global_profile(); 30 | 31 | private: 32 | struct impl; 33 | std::unique_ptr pimpl; 34 | }; 35 | 36 | } // namespace nvprefs 37 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/undo_data.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/undo_data.h 3 | * @brief Declarations for undoing changes to nvidia preferences. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace nvprefs { 14 | 15 | class undo_data_t { 16 | public: 17 | struct data_t { 18 | struct opengl_swapchain_t { 19 | uint32_t our_value; 20 | std::optional undo_value; 21 | }; 22 | 23 | std::optional opengl_swapchain; 24 | }; 25 | 26 | void set_opengl_swapchain(uint32_t our_value, std::optional undo_value); 27 | 28 | std::optional get_opengl_swapchain() const; 29 | 30 | std::string write() const; 31 | 32 | void read(const std::vector &buffer); 33 | 34 | void merge(const undo_data_t &newer_data); 35 | 36 | private: 37 | data_t data; 38 | }; 39 | 40 | } // namespace nvprefs 41 | -------------------------------------------------------------------------------- /src/platform/windows/nvprefs/undo_file.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/nvprefs/undo_file.h 3 | * @brief Declarations for the nvidia undo file. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // local includes 11 | #include "nvprefs_common.h" 12 | #include "undo_data.h" 13 | 14 | namespace nvprefs { 15 | 16 | class undo_file_t { 17 | public: 18 | static std::optional open_existing_file(std::filesystem::path file_path, bool &access_denied); 19 | 20 | static std::optional create_new_file(std::filesystem::path file_path); 21 | 22 | bool delete_file(); 23 | 24 | bool write_undo_data(const undo_data_t &undo_data); 25 | 26 | std::optional read_undo_data(); 27 | 28 | private: 29 | undo_file_t() = default; 30 | safe_handle file_handle; 31 | }; 32 | 33 | } // namespace nvprefs 34 | -------------------------------------------------------------------------------- /src/platform/windows/windows.rc.in: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/platform/windows/windows.rc.in 3 | * @brief Windows resource file template. 4 | * @note The final `windows.rc` is generated from this file during the CMake build. 5 | * @todo Use CMake definitions directly, instead of configuring this file. 6 | */ 7 | #include "winver.h" 8 | VS_VERSION_INFO VERSIONINFO 9 | FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 10 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 11 | FILEOS VOS__WINDOWS32 12 | FILETYPE VFT_APP 13 | FILESUBTYPE VFT2_UNKNOWN 14 | BEGIN 15 | BLOCK "StringFileInfo" 16 | BEGIN 17 | BLOCK "040904E4" 18 | BEGIN 19 | VALUE "CompanyName", "LizardByte\0" 20 | VALUE "FileDescription", "Sunshine\0" 21 | VALUE "FileVersion", "@PROJECT_VERSION@\0" 22 | VALUE "InternalName", "Sunshine\0" 23 | VALUE "LegalCopyright", "https://raw.githubusercontent.com/LizardByte/Sunshine/master/LICENSE\0" 24 | VALUE "ProductName", "Sunshine\0" 25 | VALUE "ProductVersion", "@PROJECT_VERSION@\0" 26 | END 27 | END 28 | 29 | BLOCK "VarFileInfo" 30 | BEGIN 31 | /* The following line should only be modified for localized versions. */ 32 | /* It consists of any number of WORD,WORD pairs, with each pair */ 33 | /* describing a language,codepage combination supported by the file. */ 34 | /* */ 35 | /* For example, a file might have values "0x409,1252" indicating that it */ 36 | /* supports English language (0x409) in the Windows ANSI codepage (1252). */ 37 | 38 | VALUE "Translation", 0x409, 1252 39 | 40 | END 41 | END 42 | SuperDuperAmazing ICON DISCARDABLE "@SUNSHINE_ICON_PATH@" 43 | -------------------------------------------------------------------------------- /src/rswrapper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/rswrapper.h 3 | * @brief Wrappers for nanors vectorization 4 | * @details This is a drop-in replacement for nanors rs.h 5 | */ 6 | #pragma once 7 | 8 | // standard includes 9 | #include 10 | 11 | typedef struct _reed_solomon reed_solomon; 12 | 13 | typedef reed_solomon *(*reed_solomon_new_t)(int data_shards, int parity_shards); 14 | typedef void (*reed_solomon_release_t)(reed_solomon *rs); 15 | typedef int (*reed_solomon_encode_t)(reed_solomon *rs, uint8_t **shards, int nr_shards, int bs); 16 | typedef int (*reed_solomon_decode_t)(reed_solomon *rs, uint8_t **shards, uint8_t *marks, int nr_shards, int bs); 17 | 18 | extern reed_solomon_new_t reed_solomon_new_fn; 19 | extern reed_solomon_release_t reed_solomon_release_fn; 20 | extern reed_solomon_encode_t reed_solomon_encode_fn; 21 | extern reed_solomon_decode_t reed_solomon_decode_fn; 22 | 23 | #define reed_solomon_new reed_solomon_new_fn 24 | #define reed_solomon_release reed_solomon_release_fn 25 | #define reed_solomon_encode reed_solomon_encode_fn 26 | #define reed_solomon_decode reed_solomon_decode_fn 27 | 28 | /** 29 | * @brief This initializes the RS function pointers to the best vectorized version available. 30 | * @details The streaming code will directly invoke these function pointers during encoding. 31 | */ 32 | void reed_solomon_init(void); 33 | -------------------------------------------------------------------------------- /src/rtsp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/rtsp.h 3 | * @brief Declarations for RTSP streaming. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // local includes 11 | #include "crypto.h" 12 | #include "thread_safe.h" 13 | 14 | namespace rtsp_stream { 15 | constexpr auto RTSP_SETUP_PORT = 21; 16 | 17 | struct launch_session_t { 18 | uint32_t id; 19 | 20 | crypto::aes_t gcm_key; 21 | crypto::aes_t iv; 22 | 23 | std::string av_ping_payload; 24 | uint32_t control_connect_data; 25 | 26 | bool host_audio; 27 | std::string unique_id; 28 | int width; 29 | int height; 30 | int fps; 31 | int gcmap; 32 | int appid; 33 | int surround_info; 34 | std::string surround_params; 35 | bool enable_hdr; 36 | bool enable_sops; 37 | 38 | std::optional rtsp_cipher; 39 | std::string rtsp_url_scheme; 40 | uint32_t rtsp_iv_counter; 41 | }; 42 | 43 | void launch_session_raise(std::shared_ptr launch_session); 44 | 45 | /** 46 | * @brief Clear state for the specified launch session. 47 | * @param launch_session_id The ID of the session to clear. 48 | */ 49 | void launch_session_clear(uint32_t launch_session_id); 50 | 51 | /** 52 | * @brief Get the number of active sessions. 53 | * @return Count of active sessions. 54 | */ 55 | int session_count(); 56 | 57 | /** 58 | * @brief Terminates all running streaming sessions. 59 | */ 60 | void terminate_sessions(); 61 | 62 | void rtpThread(); 63 | 64 | } // namespace rtsp_stream 65 | -------------------------------------------------------------------------------- /src/stat_trackers.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/stat_trackers.cpp 3 | * @brief Definitions for streaming statistic tracking. 4 | */ 5 | // local includes 6 | #include "stat_trackers.h" 7 | 8 | namespace stat_trackers { 9 | 10 | boost::format one_digit_after_decimal() { 11 | return boost::format("%1$.1f"); 12 | } 13 | 14 | boost::format two_digits_after_decimal() { 15 | return boost::format("%1$.2f"); 16 | } 17 | 18 | } // namespace stat_trackers 19 | -------------------------------------------------------------------------------- /src/stat_trackers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/stat_trackers.h 3 | * @brief Declarations for streaming statistic tracking. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | #include 11 | 12 | // lib includes 13 | #include 14 | 15 | namespace stat_trackers { 16 | 17 | boost::format one_digit_after_decimal(); 18 | 19 | boost::format two_digits_after_decimal(); 20 | 21 | template 22 | class min_max_avg_tracker { 23 | public: 24 | using callback_function = std::function; 25 | 26 | void collect_and_callback_on_interval(T stat, const callback_function &callback, std::chrono::seconds interval_in_seconds) { 27 | if (data.calls == 0) { 28 | data.last_callback_time = std::chrono::steady_clock::now(); 29 | } else if (std::chrono::steady_clock::now() > data.last_callback_time + interval_in_seconds) { 30 | callback(data.stat_min, data.stat_max, data.stat_total / data.calls); 31 | data = {}; 32 | } 33 | data.stat_min = std::min(data.stat_min, stat); 34 | data.stat_max = std::max(data.stat_max, stat); 35 | data.stat_total += stat; 36 | data.calls += 1; 37 | } 38 | 39 | void reset() { 40 | data = {}; 41 | } 42 | 43 | private: 44 | struct { 45 | std::chrono::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now(); 46 | T stat_min = std::numeric_limits::max(); 47 | T stat_max = std::numeric_limits::min(); 48 | double stat_total = 0; 49 | uint32_t calls = 0; 50 | } data; 51 | }; 52 | 53 | } // namespace stat_trackers 54 | -------------------------------------------------------------------------------- /src/stream.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/stream.h 3 | * @brief Declarations for the streaming protocols. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | 10 | // lib includes 11 | #include 12 | 13 | // local includes 14 | #include "audio.h" 15 | #include "crypto.h" 16 | #include "video.h" 17 | 18 | namespace stream { 19 | constexpr auto VIDEO_STREAM_PORT = 9; 20 | constexpr auto CONTROL_PORT = 10; 21 | constexpr auto AUDIO_STREAM_PORT = 11; 22 | 23 | struct session_t; 24 | 25 | struct config_t { 26 | audio::config_t audio; 27 | video::config_t monitor; 28 | 29 | int packetsize; 30 | int minRequiredFecPackets; 31 | int mlFeatureFlags; 32 | int controlProtocolType; 33 | int audioQosType; 34 | int videoQosType; 35 | 36 | uint32_t encryptionFlagsEnabled; 37 | 38 | std::optional gcmap; 39 | }; 40 | 41 | namespace session { 42 | enum class state_e : int { 43 | STOPPED, ///< The session is stopped 44 | STOPPING, ///< The session is stopping 45 | STARTING, ///< The session is starting 46 | RUNNING, ///< The session is running 47 | }; 48 | 49 | std::shared_ptr alloc(config_t &config, rtsp_stream::launch_session_t &launch_session); 50 | int start(session_t &session, const std::string &addr_string); 51 | void stop(session_t &session); 52 | void join(session_t &session); 53 | state_e state(session_t &session); 54 | } // namespace session 55 | } // namespace stream 56 | -------------------------------------------------------------------------------- /src/sync.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/sync.h 3 | * @brief Declarations for synchronization utilities. 4 | */ 5 | #pragma once 6 | 7 | // standard includes 8 | #include 9 | #include 10 | #include 11 | 12 | namespace sync_util { 13 | 14 | template 15 | class sync_t { 16 | public: 17 | using value_t = T; 18 | using mutex_t = M; 19 | 20 | std::lock_guard lock() { 21 | return std::lock_guard {_lock}; 22 | } 23 | 24 | template 25 | sync_t(Args &&...args): 26 | raw {std::forward(args)...} { 27 | } 28 | 29 | sync_t &operator=(sync_t &&other) noexcept { 30 | std::lock(_lock, other._lock); 31 | 32 | raw = std::move(other.raw); 33 | 34 | _lock.unlock(); 35 | other._lock.unlock(); 36 | 37 | return *this; 38 | } 39 | 40 | sync_t &operator=(sync_t &other) noexcept { 41 | std::lock(_lock, other._lock); 42 | 43 | raw = other.raw; 44 | 45 | _lock.unlock(); 46 | other._lock.unlock(); 47 | 48 | return *this; 49 | } 50 | 51 | template 52 | sync_t &operator=(V &&val) { 53 | auto lg = lock(); 54 | 55 | raw = val; 56 | 57 | return *this; 58 | } 59 | 60 | sync_t &operator=(const value_t &val) noexcept { 61 | auto lg = lock(); 62 | 63 | raw = val; 64 | 65 | return *this; 66 | } 67 | 68 | sync_t &operator=(value_t &&val) noexcept { 69 | auto lg = lock(); 70 | 71 | raw = std::move(val); 72 | 73 | return *this; 74 | } 75 | 76 | value_t *operator->() { 77 | return &raw; 78 | } 79 | 80 | value_t &operator*() { 81 | return raw; 82 | } 83 | 84 | const value_t &operator*() const { 85 | return raw; 86 | } 87 | 88 | value_t raw; 89 | 90 | private: 91 | mutex_t _lock; 92 | }; 93 | 94 | } // namespace sync_util 95 | -------------------------------------------------------------------------------- /src/upnp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/upnp.h 3 | * @brief Declarations for UPnP port mapping. 4 | */ 5 | #pragma once 6 | 7 | // lib includes 8 | #include 9 | 10 | // local includes 11 | #include "platform/common.h" 12 | 13 | /** 14 | * @brief UPnP port mapping. 15 | */ 16 | namespace upnp { 17 | constexpr auto INET6_ADDRESS_STRLEN = 46; 18 | constexpr auto IPv4 = 0; 19 | constexpr auto IPv6 = 1; 20 | constexpr auto PORT_MAPPING_LIFETIME = 3600s; 21 | constexpr auto REFRESH_INTERVAL = 120s; 22 | 23 | using device_t = util::safe_ptr; 24 | 25 | KITTY_USING_MOVE_T(urls_t, UPNPUrls, , { 26 | FreeUPNPUrls(&el); 27 | }); 28 | 29 | /** 30 | * @brief Get the valid IGD status. 31 | * @param device The device. 32 | * @param urls The URLs. 33 | * @param data The IGD data. 34 | * @param lan_addr The LAN address. 35 | * @return The UPnP Status. 36 | * @retval 0 No IGD found. 37 | * @retval 1 A valid connected IGD has been found. 38 | * @retval 2 A valid IGD has been found but it reported as not connected. 39 | * @retval 3 An UPnP device has been found but was not recognized as an IGD. 40 | */ 41 | int UPNP_GetValidIGDStatus(device_t &device, urls_t *urls, IGDdatas *data, std::array &lan_addr); 42 | 43 | [[nodiscard]] std::unique_ptr start(); 44 | } // namespace upnp 45 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | /** 2 | * @file src/version.h.in 3 | * @brief Version definitions for Sunshine. 4 | * @note The final `version.h` is generated from this file during the CMake build. 5 | * @todo Use CMake definitions directly, instead of configuring this file. 6 | */ 7 | #pragma once 8 | 9 | #define PROJECT_NAME "@PROJECT_NAME@" 10 | #define PROJECT_VER "@PROJECT_VERSION@" 11 | #define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@" 12 | #define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@" 13 | #define PROJECT_VER_PATCH "@PROJECT_VERSION_PATCH@" 14 | -------------------------------------------------------------------------------- /src_assets/common/assets/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/box.png -------------------------------------------------------------------------------- /src_assets/common/assets/desktop-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/desktop-alt.png -------------------------------------------------------------------------------- /src_assets/common/assets/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/desktop.png -------------------------------------------------------------------------------- /src_assets/common/assets/steam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/steam.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/PlatformLayout.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/ResourceCard.vue: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/ThemeToggle.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | 45 | 47 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/ContainerEncoders.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 64 | 65 | 67 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/audiovideo/AdapterNameSelector.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 40 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | 36 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/audiovideo/DisplayOutputSelector.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 54 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/encoders/IntelQuickSyncEncoder.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/encoders/SoftwareEncoder.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/configs/tabs/encoders/VideotoolboxEncoder.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/init.js: -------------------------------------------------------------------------------- 1 | import i18n from './locale' 2 | 3 | // must import even if not implicitly using here 4 | // https://github.com/aurelia/skeleton-navigation/issues/894 5 | // https://discourse.aurelia.io/t/bootstrap-import-bootstrap-breaks-dropdown-menu-in-navbar/641/9 6 | import 'bootstrap/dist/js/bootstrap' 7 | 8 | export function initApp(app, config) { 9 | //Wait for locale initialization, then render 10 | i18n().then(i18n => { 11 | app.use(i18n); 12 | app.provide('i18n', i18n.global) 13 | app.mount('#app'); 14 | if (config) { 15 | config(app) 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/locale.js: -------------------------------------------------------------------------------- 1 | import {createI18n} from "vue-i18n"; 2 | 3 | // Import only the fallback language files 4 | import en from './public/assets/locale/en.json' 5 | 6 | export default async function() { 7 | let r = await (await fetch("./api/configLocale")).json(); 8 | let locale = r.locale ?? "en"; 9 | document.querySelector('html').setAttribute('lang', locale); 10 | let messages = { 11 | en 12 | }; 13 | try { 14 | if (locale !== 'en') { 15 | let r = await (await fetch(`./assets/locale/${locale}.json`)).json(); 16 | messages[locale] = r; 17 | } 18 | } catch (e) { 19 | console.error("Failed to download translations", e); 20 | } 21 | const i18n = createI18n({ 22 | locale: locale, // set locale 23 | fallbackLocale: 'en', // set fallback locale 24 | messages: messages 25 | }) 26 | return i18n; 27 | } 28 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/assets/css/sunshine.css: -------------------------------------------------------------------------------- 1 | /* Hide pages while localization is loading */ 2 | [v-cloak] { 3 | display: none; 4 | } 5 | 6 | [data-bs-theme=dark] .element { 7 | color: var(--bs-primary-text-emphasis); 8 | background-color: var(--bs-primary-bg-subtle); 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | .element { 13 | color: var(--bs-primary-text-emphasis); 14 | background-color: var(--bs-primary-bg-subtle); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/logo-sunshine-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/logo-sunshine-16.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/logo-sunshine-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/logo-sunshine-45.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-locked-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-locked-16.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-locked-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-locked-45.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-locked.ico -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-locked.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-pausing-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-pausing-16.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-pausing-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-pausing-45.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-pausing.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-pausing.ico -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-pausing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-pausing.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-playing-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-playing-16.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-playing-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-playing-45.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-playing.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-playing.ico -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine-playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine-playing.png -------------------------------------------------------------------------------- /src_assets/common/assets/web/public/images/sunshine.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/src_assets/common/assets/web/public/images/sunshine.ico -------------------------------------------------------------------------------- /src_assets/common/assets/web/sunshine_version.js: -------------------------------------------------------------------------------- 1 | class SunshineVersion { 2 | constructor(release = null, version = null) { 3 | if (release) { 4 | this.release = release; 5 | this.version = release.tag_name; 6 | this.versionName = release.name; 7 | this.versionTag = release.tag_tag; 8 | } else if (version) { 9 | this.release = null; 10 | this.version = version; 11 | this.versionName = null; 12 | this.versionTag = null; 13 | } else { 14 | throw new Error('Either release or version must be provided'); 15 | } 16 | this.versionParts = this.parseVersion(this.version); 17 | this.versionMajor = this.versionParts ? this.versionParts[0] : null; 18 | this.versionMinor = this.versionParts ? this.versionParts[1] : null; 19 | this.versionPatch = this.versionParts ? this.versionParts[2] : null; 20 | } 21 | 22 | parseVersion(version) { 23 | if (!version) { 24 | return null; 25 | } 26 | let v = version; 27 | if (v.indexOf("v") === 0) { 28 | v = v.substring(1); 29 | } 30 | return v.split('.').map(Number); 31 | } 32 | 33 | isGreater(otherVersion) { 34 | let otherVersionParts; 35 | if (otherVersion instanceof SunshineVersion) { 36 | otherVersionParts = otherVersion.versionParts; 37 | } else if (typeof otherVersion === 'string') { 38 | otherVersionParts = this.parseVersion(otherVersion); 39 | } else { 40 | throw new Error('Invalid argument: otherVersion must be a SunshineVersion object or a version string'); 41 | } 42 | 43 | if (!this.versionParts || !otherVersionParts) { 44 | return false; 45 | } 46 | for (let i = 0; i < Math.min(3, this.versionParts.length, otherVersionParts.length); i++) { 47 | if (this.versionParts[i] !== otherVersionParts[i]) { 48 | return this.versionParts[i] > otherVersionParts[i]; 49 | } 50 | } 51 | return false; 52 | } 53 | } 54 | 55 | export default SunshineVersion; 56 | -------------------------------------------------------------------------------- /src_assets/common/assets/web/template_header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sunshine 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src_assets/linux/assets/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "PATH": "$(PATH):$(HOME)/.local/bin" 4 | }, 5 | "apps": [ 6 | { 7 | "name": "Desktop", 8 | "image-path": "desktop.png" 9 | }, 10 | { 11 | "name": "Low Res Desktop", 12 | "image-path": "desktop.png", 13 | "prep-cmd": [ 14 | { 15 | "do": "xrandr --output HDMI-1 --mode 1920x1080", 16 | "undo": "xrandr --output HDMI-1 --mode 1920x1200" 17 | } 18 | ] 19 | }, 20 | { 21 | "name": "Steam Big Picture", 22 | "detached": [ 23 | "setsid steam steam://open/bigpicture" 24 | ], 25 | "prep-cmd": [ 26 | { 27 | "do": "", 28 | "undo": "setsid steam steam://close/bigpicture" 29 | } 30 | ], 31 | "image-path": "steam.png" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /src_assets/linux/assets/shaders/opengl/ConvertUV.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | #ifdef GL_ES 4 | precision lowp float; 5 | #endif 6 | 7 | uniform sampler2D image; 8 | 9 | layout(shared) uniform ColorMatrix { 10 | vec4 color_vec_y; 11 | vec4 color_vec_u; 12 | vec4 color_vec_v; 13 | vec2 range_y; 14 | vec2 range_uv; 15 | }; 16 | 17 | in vec3 uuv; 18 | layout(location = 0) out vec2 color; 19 | 20 | //-------------------------------------------------------------------------------------- 21 | // Pixel Shader 22 | //-------------------------------------------------------------------------------------- 23 | void main() { 24 | vec3 rgb_left = texture(image, uuv.xz).rgb; 25 | vec3 rgb_right = texture(image, uuv.yz).rgb; 26 | vec3 rgb = (rgb_left + rgb_right) * 0.5; 27 | 28 | float u = dot(color_vec_u.xyz, rgb) + color_vec_u.w; 29 | float v = dot(color_vec_v.xyz, rgb) + color_vec_v.w; 30 | 31 | u = u * range_uv.x + range_uv.y; 32 | v = v * range_uv.x + range_uv.y; 33 | 34 | color = vec2(u, v); 35 | } -------------------------------------------------------------------------------- /src_assets/linux/assets/shaders/opengl/ConvertUV.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | #ifdef GL_ES 4 | precision mediump float; 5 | #endif 6 | 7 | uniform float width_i; 8 | 9 | out vec3 uuv; 10 | //-------------------------------------------------------------------------------------- 11 | // Vertex Shader 12 | //-------------------------------------------------------------------------------------- 13 | void main() 14 | { 15 | float idHigh = float(gl_VertexID >> 1); 16 | float idLow = float(gl_VertexID & int(1)); 17 | 18 | float x = idHigh * 4.0 - 1.0; 19 | float y = idLow * 4.0 - 1.0; 20 | 21 | float u_right = idHigh * 2.0; 22 | float u_left = u_right - width_i; 23 | float v = idLow * 2.0; 24 | 25 | uuv = vec3(u_left, u_right, v); 26 | gl_Position = vec4(x, y, 0.0, 1.0); 27 | } -------------------------------------------------------------------------------- /src_assets/linux/assets/shaders/opengl/ConvertY.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | #ifdef GL_ES 4 | precision lowp float; 5 | #endif 6 | 7 | uniform sampler2D image; 8 | 9 | layout(shared) uniform ColorMatrix { 10 | vec4 color_vec_y; 11 | vec4 color_vec_u; 12 | vec4 color_vec_v; 13 | vec2 range_y; 14 | vec2 range_uv; 15 | }; 16 | 17 | in vec2 tex; 18 | layout(location = 0) out float color; 19 | 20 | void main() 21 | { 22 | vec3 rgb = texture(image, tex).rgb; 23 | float y = dot(color_vec_y.xyz, rgb); 24 | 25 | color = y * range_y.x + range_y.y; 26 | } -------------------------------------------------------------------------------- /src_assets/linux/assets/shaders/opengl/Scene.frag: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | #ifdef GL_ES 4 | precision lowp float; 5 | #endif 6 | 7 | uniform sampler2D image; 8 | 9 | in vec2 tex; 10 | layout(location = 0) out vec4 color; 11 | void main() 12 | { 13 | color = texture(image, tex); 14 | } -------------------------------------------------------------------------------- /src_assets/linux/assets/shaders/opengl/Scene.vert: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | #ifdef GL_ES 4 | precision mediump float; 5 | #endif 6 | 7 | out vec2 tex; 8 | 9 | void main() 10 | { 11 | float idHigh = float(gl_VertexID >> 1); 12 | float idLow = float(gl_VertexID & int(1)); 13 | 14 | float x = idHigh * 4.0 - 1.0; 15 | float y = idLow * 4.0 - 1.0; 16 | 17 | float u = idHigh * 2.0; 18 | float v = idLow * 2.0; 19 | 20 | gl_Position = vec4(x, y, 0.0, 1.0); 21 | tex = vec2(u, v); 22 | } -------------------------------------------------------------------------------- /src_assets/linux/misc/60-sunshine.rules: -------------------------------------------------------------------------------- 1 | # Allows Sunshine to acces /dev/uinput 2 | KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", GROUP="input", MODE="0660", TAG+="uaccess" 3 | 4 | # Allows Sunshine to access /dev/uhid 5 | KERNEL=="uhid", GROUP="input", MODE="0660", TAG+="uaccess" 6 | 7 | # Joypads 8 | KERNEL=="hidraw*" ATTRS{name}=="Sunshine PS5 (virtual) pad" GROUP="input", MODE="0660", TAG+="uaccess" 9 | SUBSYSTEMS=="input", ATTRS{name}=="Sunshine X-Box One (virtual) pad", GROUP="input", MODE="0660", TAG+="uaccess" 10 | SUBSYSTEMS=="input", ATTRS{name}=="Sunshine gamepad (virtual) motion sensors", GROUP="input", MODE="0660", TAG+="uaccess" 11 | SUBSYSTEMS=="input", ATTRS{name}=="Sunshine Nintendo (virtual) pad", GROUP="input", MODE="0660", TAG+="uaccess" 12 | -------------------------------------------------------------------------------- /src_assets/linux/misc/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check if we're in an rpm-ostree environment 4 | if [ ! -x "$(command -v rpm-ostree)" ]; then 5 | echo "Not in an rpm-ostree environment, proceeding with post install steps." 6 | 7 | # Ensure Sunshine can grab images from KMS 8 | path_to_setcap=$(which setcap) 9 | path_to_sunshine=$(readlink -f "$(which sunshine)") 10 | if [ -x "$path_to_setcap" ] ; then 11 | echo "Setting CAP_SYS_ADMIN capability on Sunshine binary." 12 | echo "$path_to_setcap cap_sys_admin+p $path_to_sunshine" 13 | $path_to_setcap cap_sys_admin+p $path_to_sunshine 14 | echo "CAP_SYS_ADMIN capability set on Sunshine binary." 15 | else 16 | echo "error: setcap not found or not executable." 17 | fi 18 | 19 | # Trigger udev rule reload for /dev/uinput and /dev/uhid 20 | path_to_udevadm=$(which udevadm) 21 | if [ -x "$path_to_udevadm" ] ; then 22 | echo "Reloading udev rules." 23 | $path_to_udevadm control --reload-rules 24 | $path_to_udevadm trigger --property-match=DEVNAME=/dev/uinput 25 | $path_to_udevadm trigger --property-match=DEVNAME=/dev/uhid 26 | echo "Udev rules reloaded successfully." 27 | else 28 | echo "error: udevadm not found or not executable." 29 | fi 30 | else 31 | echo "rpm-ostree environment detected, skipping post install steps. Restart to apply the changes." 32 | fi 33 | -------------------------------------------------------------------------------- /src_assets/macos/assets/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | dev.lizardbyte.sunshine 7 | CFBundleName 8 | Sunshine 9 | NSMicrophoneUsageDescription 10 | This app requires access to your microphone to stream audio. 11 | 12 | 13 | -------------------------------------------------------------------------------- /src_assets/macos/assets/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "PATH": "$(PATH):$(HOME)/.local/bin" 4 | }, 5 | "apps": [ 6 | { 7 | "name": "Desktop", 8 | "image-path": "desktop.png" 9 | }, 10 | { 11 | "name": "Steam Big Picture", 12 | "detached": [ 13 | "open steam://open/bigpicture" 14 | ], 15 | "prep-cmd": [ 16 | { 17 | "do": "", 18 | "undo": "open steam://close/bigpicture" 19 | } 20 | ], 21 | "image-path": "steam.png" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src_assets/macos/misc/uninstall_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # note: this file was used to remove files when using the pkg/dmg, it is no longer used, but left for reference 4 | 5 | set -e 6 | 7 | package_name=org.macports.Sunshine 8 | 9 | echo "Removing files now..." 10 | FILES=$(pkgutil --files $package_name --only-files) 11 | 12 | for file in ${FILES}; do 13 | file="/$file" 14 | echo "removing: $file" 15 | rm -f "$file" 16 | done 17 | 18 | echo "Removing directories now..." 19 | DIRECTORIES=$(pkgutil --files org.macports.Sunshine --only-dirs) 20 | 21 | for dir in ${DIRECTORIES}; do 22 | dir="/$dir" 23 | echo "Checking if empty directory: $dir" 24 | 25 | # check if directory is empty... could just use ${DIRECTORIES} here if pkgutils added the `/` prefix 26 | empty_dir=$(find "$dir" -depth 0 -type d -empty) 27 | 28 | # remove the directory if it is empty 29 | if [[ $empty_dir != "" ]]; then # prevent the loop from running and failing if no directories found 30 | for i in "${empty_dir}"; do # don't split words as we already know this will be a single directory 31 | echo "Removing empty directory: ${i}" 32 | rmdir "${i}" 33 | done 34 | fi 35 | done 36 | 37 | echo "Forgetting Sunshine..." 38 | pkgutil --forget $package_name 39 | 40 | echo "Sunshine has been uninstalled..." 41 | -------------------------------------------------------------------------------- /src_assets/windows/assets/apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": {}, 3 | "apps": [ 4 | { 5 | "name": "Desktop", 6 | "image-path": "desktop.png" 7 | }, 8 | { 9 | "name": "Steam Big Picture", 10 | "cmd": "steam://open/bigpicture", 11 | "prep-cmd": [ 12 | { 13 | "do": "", 14 | "undo": "steam://close/bigpicture" 15 | } 16 | ], 17 | "auto-detach": true, 18 | "wait-all": true, 19 | "image-path": "steam.png" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0_ps_perceptual_quantizer.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_perceptual_quantizer_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer subsample_offset_cbuffer : register(b0) { 2 | float2 subsample_offset; 3 | }; 4 | 5 | cbuffer rotate_texture_steps_cbuffer : register(b1) { 6 | int rotate_texture_steps; 7 | }; 8 | 9 | #define LEFT_SUBSAMPLING 10 | #include "include/base_vs.hlsl" 11 | 12 | vertex_t main_vs(uint vertex_id : SV_VertexID) 13 | { 14 | return generate_fullscreen_triangle_vertex(vertex_id, subsample_offset, rotate_texture_steps); 15 | } 16 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0s_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING_SCALE 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0s_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING_SCALE 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0s_ps_perceptual_quantizer.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_perceptual_quantizer_base.hlsl" 2 | 3 | #define LEFT_SUBSAMPLING_SCALE 4 | 5 | #include "include/convert_yuv420_packed_uv_ps_base.hlsl" 6 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_packed_uv_type0s_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer subsample_offset_cbuffer : register(b0) { 2 | float2 subsample_offset; 3 | }; 4 | 5 | cbuffer rotate_texture_steps_cbuffer : register(b1) { 6 | int rotate_texture_steps; 7 | }; 8 | 9 | #define LEFT_SUBSAMPLING_SCALE 10 | #include "include/base_vs.hlsl" 11 | 12 | vertex_t main_vs(uint vertex_id : SV_VertexID) 13 | { 14 | return generate_fullscreen_triangle_vertex(vertex_id, subsample_offset, rotate_texture_steps); 15 | } 16 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_planar_y_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #include "include/convert_yuv420_planar_y_ps_base.hlsl" 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_planar_y_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #include "include/convert_yuv420_planar_y_ps_base.hlsl" 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_planar_y_ps_perceptual_quantizer.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_perceptual_quantizer_base.hlsl" 2 | 3 | #include "include/convert_yuv420_planar_y_ps_base.hlsl" 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv420_planar_y_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer rotate_texture_steps_cbuffer : register(b1) { 2 | int rotate_texture_steps; 3 | }; 4 | 5 | #include "include/base_vs.hlsl" 6 | 7 | vertex_t main_vs(uint vertex_id : SV_VertexID) 8 | { 9 | return generate_fullscreen_triangle_vertex(vertex_id, float2(0, 0), rotate_texture_steps); 10 | } 11 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_ayuv_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #include "include/convert_yuv444_ps_base.hlsl" 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_ayuv_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #include "include/convert_yuv444_ps_base.hlsl" 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer rotate_texture_steps_cbuffer : register(b1) { 2 | int rotate_texture_steps; 3 | }; 4 | 5 | #include "include/base_vs.hlsl" 6 | 7 | vertex_t main_vs(uint vertex_id : SV_VertexID) 8 | { 9 | return generate_fullscreen_triangle_vertex(vertex_id, float2(0, 0), rotate_texture_steps); 10 | } 11 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_y410_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #define Y410 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_y410_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #define Y410 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_packed_y410_ps_perceptual_quantizer.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_perceptual_quantizer_base.hlsl" 2 | 3 | #define Y410 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_planar_ps.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_base.hlsl" 2 | 3 | #define PLANAR_VIEWPORTS 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_planar_ps_linear.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_linear_base.hlsl" 2 | 3 | #define PLANAR_VIEWPORTS 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_planar_ps_perceptual_quantizer.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/convert_perceptual_quantizer_base.hlsl" 2 | 3 | #define PLANAR_VIEWPORTS 4 | #include "include/convert_yuv444_ps_base.hlsl" 5 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/convert_yuv444_planar_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer rotate_texture_steps_cbuffer : register(b1) { 2 | int rotate_texture_steps; 3 | }; 4 | 5 | cbuffer color_matrix_cbuffer : register(b3) { 6 | float4 color_vec_y; 7 | float4 color_vec_u; 8 | float4 color_vec_v; 9 | float2 range_y; 10 | float2 range_uv; 11 | }; 12 | 13 | #define PLANAR_VIEWPORTS 14 | #include "include/base_vs.hlsl" 15 | 16 | vertex_t main_vs(uint vertex_id : SV_VertexID) 17 | { 18 | vertex_t output = generate_fullscreen_triangle_vertex(vertex_id % 3, float2(0, 0), rotate_texture_steps); 19 | 20 | output.viewport = vertex_id / 3; 21 | 22 | if (output.viewport == 0) { 23 | output.color_vec = color_vec_y; 24 | } 25 | else if (output.viewport == 1) { 26 | output.color_vec = color_vec_u; 27 | } 28 | else { 29 | output.color_vec = color_vec_v; 30 | } 31 | 32 | return output; 33 | } 34 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/cursor_ps.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D cursor : register(t0); 2 | SamplerState def_sampler : register(s0); 3 | 4 | #include "include/base_vs_types.hlsl" 5 | 6 | float4 main_ps(vertex_t input) : SV_Target 7 | { 8 | return cursor.Sample(def_sampler, input.tex_coord, 0); 9 | } 10 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/cursor_ps_normalize_white.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D cursor : register(t0); 2 | SamplerState def_sampler : register(s0); 3 | 4 | cbuffer normalize_white_cbuffer : register(b1) { 5 | float white_multiplier; 6 | }; 7 | 8 | #include "include/base_vs_types.hlsl" 9 | 10 | float4 main_ps(vertex_t input) : SV_Target 11 | { 12 | float4 output = cursor.Sample(def_sampler, input.tex_coord, 0); 13 | 14 | output.rgb = output.rgb * white_multiplier; 15 | 16 | return output; 17 | } 18 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/cursor_vs.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer rotate_texture_steps_cbuffer : register(b2) { 2 | int rotate_texture_steps; 3 | }; 4 | 5 | #include "include/base_vs.hlsl" 6 | 7 | vertex_t main_vs(uint vertex_id : SV_VertexID) 8 | { 9 | return generate_fullscreen_triangle_vertex(vertex_id, float2(0, 0), rotate_texture_steps); 10 | } 11 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/base_vs_types.hlsl: -------------------------------------------------------------------------------- 1 | struct vertex_t 2 | { 3 | float4 viewpoint_pos : SV_Position; 4 | #if defined(LEFT_SUBSAMPLING) 5 | float3 tex_right_left_center : TEXCOORD; 6 | #elif defined(LEFT_SUBSAMPLING_SCALE) 7 | float4 tex_right_center_left_top : TEXCOORD0; 8 | float4 tex_right_center_left_bottom : TEXCOORD1; 9 | #elif defined(TOPLEFT_SUBSAMPLING) 10 | float3 tex_right_left_top : TEXCOORD0; 11 | float3 tex_right_left_bottom : TEXCOORD1; 12 | #else 13 | float2 tex_coord : TEXCOORD; 14 | #endif 15 | #ifdef PLANAR_VIEWPORTS 16 | uint viewport : SV_ViewportArrayIndex; 17 | nointerpolation float4 color_vec : COLOR0; 18 | #endif 19 | }; 20 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/common.hlsl: -------------------------------------------------------------------------------- 1 | // This is a fast sRGB approximation from Microsoft's ColorSpaceUtility.hlsli 2 | float3 ApplySRGBCurve(float3 x) 3 | { 4 | return x < 0.0031308 ? 12.92 * x : 1.13005 * sqrt(x - 0.00228) - 0.13448 * x + 0.005719; 5 | } 6 | 7 | float3 NitsToPQ(float3 L) 8 | { 9 | // Constants from SMPTE 2084 PQ 10 | static const float m1 = 2610.0 / 4096.0 / 4; 11 | static const float m2 = 2523.0 / 4096.0 * 128; 12 | static const float c1 = 3424.0 / 4096.0; 13 | static const float c2 = 2413.0 / 4096.0 * 32; 14 | static const float c3 = 2392.0 / 4096.0 * 32; 15 | 16 | float3 Lp = pow(saturate(L / 10000.0), m1); 17 | return pow((c1 + c2 * Lp) / (1 + c3 * Lp), m2); 18 | } 19 | 20 | float3 Rec709toRec2020(float3 rec709) 21 | { 22 | static const float3x3 ConvMat = 23 | { 24 | 0.627402, 0.329292, 0.043306, 25 | 0.069095, 0.919544, 0.011360, 26 | 0.016394, 0.088028, 0.895578 27 | }; 28 | return mul(ConvMat, rec709); 29 | } 30 | 31 | float3 scRGBTo2100PQ(float3 rgb) 32 | { 33 | // Convert from Rec 709 primaries (used by scRGB) to Rec 2020 primaries (used by Rec 2100) 34 | rgb = Rec709toRec2020(rgb); 35 | 36 | // 1.0f is defined as 80 nits in the scRGB colorspace 37 | rgb *= 80; 38 | 39 | // Apply the PQ transfer function on the raw color values in nits 40 | return NitsToPQ(rgb); 41 | } 42 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_base.hlsl: -------------------------------------------------------------------------------- 1 | #define CONVERT_FUNCTION saturate 2 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_linear_base.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/common.hlsl" 2 | 3 | float3 CONVERT_FUNCTION(float3 input) 4 | { 5 | return ApplySRGBCurve(saturate(input)); 6 | } 7 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_perceptual_quantizer_base.hlsl: -------------------------------------------------------------------------------- 1 | #include "include/common.hlsl" 2 | 3 | #define CONVERT_FUNCTION scRGBTo2100PQ 4 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_yuv420_packed_uv_ps_base.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D image : register(t0); 2 | SamplerState def_sampler : register(s0); 3 | 4 | cbuffer color_matrix_cbuffer : register(b0) { 5 | float4 color_vec_y; 6 | float4 color_vec_u; 7 | float4 color_vec_v; 8 | float2 range_y; 9 | float2 range_uv; 10 | }; 11 | 12 | #include "include/base_vs_types.hlsl" 13 | 14 | float2 main_ps(vertex_t input) : SV_Target 15 | { 16 | #if defined(LEFT_SUBSAMPLING) 17 | float3 rgb_left = image.Sample(def_sampler, input.tex_right_left_center.xz).rgb; 18 | float3 rgb_right = image.Sample(def_sampler, input.tex_right_left_center.yz).rgb; 19 | float3 rgb = CONVERT_FUNCTION((rgb_left + rgb_right) * 0.5); 20 | #elif defined(LEFT_SUBSAMPLING_SCALE) 21 | float3 rgb = image.Sample(def_sampler, input.tex_right_center_left_top.yw).rgb; // top-center 22 | rgb += image.Sample(def_sampler, input.tex_right_center_left_bottom.yw).rgb; // bottom-center 23 | rgb *= 2; 24 | rgb += image.Sample(def_sampler, input.tex_right_center_left_top.xw).rgb; // top-right 25 | rgb += image.Sample(def_sampler, input.tex_right_center_left_top.zw).rgb; // top-left 26 | rgb += image.Sample(def_sampler, input.tex_right_center_left_bottom.xw).rgb; // bottom-right 27 | rgb += image.Sample(def_sampler, input.tex_right_center_left_bottom.zw).rgb; // bottom-left 28 | rgb = CONVERT_FUNCTION(rgb * (1./8)); 29 | #elif defined(TOPLEFT_SUBSAMPLING) 30 | float3 rgb_top_left = image.Sample(def_sampler, input.tex_right_left_top.xz).rgb; 31 | float3 rgb_top_right = image.Sample(def_sampler, input.tex_right_left_top.yz).rgb; 32 | float3 rgb_bottom_left = image.Sample(def_sampler, input.tex_right_left_bottom.xz).rgb; 33 | float3 rgb_bottom_right = image.Sample(def_sampler, input.tex_right_left_bottom.yz).rgb; 34 | float3 rgb = CONVERT_FUNCTION((rgb_top_left + rgb_top_right + rgb_bottom_left + rgb_bottom_right) * 0.25); 35 | #endif 36 | 37 | float u = dot(color_vec_u.xyz, rgb) + color_vec_u.w; 38 | float v = dot(color_vec_v.xyz, rgb) + color_vec_v.w; 39 | 40 | u = u * range_uv.x + range_uv.y; 41 | v = v * range_uv.x + range_uv.y; 42 | 43 | return float2(u, v); 44 | } 45 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_yuv420_planar_y_ps_base.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D image : register(t0); 2 | SamplerState def_sampler : register(s0); 3 | 4 | cbuffer color_matrix_cbuffer : register(b0) { 5 | float4 color_vec_y; 6 | float4 color_vec_u; 7 | float4 color_vec_v; 8 | float2 range_y; 9 | float2 range_uv; 10 | }; 11 | 12 | #include "include/base_vs_types.hlsl" 13 | 14 | float main_ps(vertex_t input) : SV_Target 15 | { 16 | float3 rgb = CONVERT_FUNCTION(image.Sample(def_sampler, input.tex_coord, 0).rgb); 17 | 18 | float y = dot(color_vec_y.xyz, rgb) + color_vec_y.w; 19 | 20 | return y * range_y.x + range_y.y; 21 | } 22 | -------------------------------------------------------------------------------- /src_assets/windows/assets/shaders/directx/include/convert_yuv444_ps_base.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D image : register(t0); 2 | SamplerState def_sampler : register(s0); 3 | 4 | #ifndef PLANAR_VIEWPORTS 5 | cbuffer color_matrix_cbuffer : register(b0) { 6 | float4 color_vec_y; 7 | float4 color_vec_u; 8 | float4 color_vec_v; 9 | float2 range_y; 10 | float2 range_uv; 11 | }; 12 | #endif 13 | 14 | #include "include/base_vs_types.hlsl" 15 | 16 | #ifdef PLANAR_VIEWPORTS 17 | uint main_ps(vertex_t input) : SV_Target 18 | #else 19 | uint4 main_ps(vertex_t input) : SV_Target 20 | #endif 21 | { 22 | float3 rgb = CONVERT_FUNCTION(image.Sample(def_sampler, input.tex_coord, 0).rgb); 23 | 24 | #ifdef PLANAR_VIEWPORTS 25 | // Planar R16, 10 most significant bits store the value 26 | return uint(dot(input.color_vec.xyz, rgb) + input.color_vec.w) << 6; 27 | #else 28 | float y = dot(color_vec_y.xyz, rgb) + color_vec_y.w; 29 | float u = dot(color_vec_u.xyz, rgb) + color_vec_u.w; 30 | float v = dot(color_vec_v.xyz, rgb) + color_vec_v.w; 31 | 32 | #ifdef Y410 33 | return uint4(u, y, v, 0); 34 | #else 35 | // AYUV 36 | return uint4(v, u, y, 0); 37 | #endif 38 | #endif 39 | } 40 | -------------------------------------------------------------------------------- /src_assets/windows/misc/autostart/autostart-service.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Set the service to auto-start 4 | sc config SunshineService start= auto 5 | -------------------------------------------------------------------------------- /src_assets/windows/misc/firewall/add-firewall-rule.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Get sunshine root directory 4 | for %%I in ("%~dp0\..") do set "ROOT_DIR=%%~fI" 5 | 6 | set RULE_NAME=Sunshine 7 | set PROGRAM_BIN="%ROOT_DIR%\sunshine.exe" 8 | 9 | rem Add the rule 10 | netsh advfirewall firewall add rule name=%RULE_NAME% dir=in action=allow protocol=tcp program=%PROGRAM_BIN% enable=yes 11 | netsh advfirewall firewall add rule name=%RULE_NAME% dir=in action=allow protocol=udp program=%PROGRAM_BIN% enable=yes 12 | -------------------------------------------------------------------------------- /src_assets/windows/misc/firewall/delete-firewall-rule.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set RULE_NAME=Sunshine 4 | 5 | rem Delete the rule 6 | netsh advfirewall firewall delete rule name=%RULE_NAME% 7 | -------------------------------------------------------------------------------- /src_assets/windows/misc/gamepad/uninstall-gamepad.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Use wmic to get the uninstall Virtual Gamepad 4 | wmic product where name="ViGEm Bus Driver" call uninstall 5 | -------------------------------------------------------------------------------- /src_assets/windows/misc/migration/migrate-config.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Get sunshine root directory 4 | for %%I in ("%~dp0\..") do set "OLD_DIR=%%~fI" 5 | 6 | rem Create the config directory if it didn't already exist 7 | set "NEW_DIR=%OLD_DIR%\config" 8 | if not exist "%NEW_DIR%\" mkdir "%NEW_DIR%" 9 | icacls "%NEW_DIR%" /reset 10 | 11 | rem Migrate all files that aren't already present in the config dir 12 | if exist "%OLD_DIR%\apps.json" ( 13 | if not exist "%NEW_DIR%\apps.json" ( 14 | move "%OLD_DIR%\apps.json" "%NEW_DIR%\apps.json" 15 | icacls "%NEW_DIR%\apps.json" /reset 16 | ) 17 | ) 18 | if exist "%OLD_DIR%\sunshine.conf" ( 19 | if not exist "%NEW_DIR%\sunshine.conf" ( 20 | move "%OLD_DIR%\sunshine.conf" "%NEW_DIR%\sunshine.conf" 21 | icacls "%NEW_DIR%\sunshine.conf" /reset 22 | ) 23 | ) 24 | if exist "%OLD_DIR%\sunshine_state.json" ( 25 | if not exist "%NEW_DIR%\sunshine_state.json" ( 26 | move "%OLD_DIR%\sunshine_state.json" "%NEW_DIR%\sunshine_state.json" 27 | icacls "%NEW_DIR%\sunshine_state.json" /reset 28 | ) 29 | ) 30 | 31 | rem Migrate the credentials directory 32 | if exist "%OLD_DIR%\credentials\" ( 33 | if not exist "%NEW_DIR%\credentials\" ( 34 | move "%OLD_DIR%\credentials" "%NEW_DIR%\" 35 | ) 36 | ) 37 | 38 | rem Create the credentials directory if it wasn't migrated or already existing 39 | if not exist "%NEW_DIR%\credentials\" mkdir "%NEW_DIR%\credentials" 40 | 41 | rem Disallow read access to the credentials directory contents for normal users 42 | rem Note: We must use the SIDs directly because "Users" and "Administrators" are localized 43 | icacls "%NEW_DIR%\credentials" /inheritance:r 44 | icacls "%NEW_DIR%\credentials" /grant:r *S-1-5-32-544:(OI)(CI)(F) 45 | icacls "%NEW_DIR%\credentials" /grant:r *S-1-5-32-545:(R) 46 | 47 | rem Migrate the covers directory 48 | if exist "%OLD_DIR%\covers\" ( 49 | if not exist "%NEW_DIR%\covers\" ( 50 | move "%OLD_DIR%\covers" "%NEW_DIR%\" 51 | 52 | rem Fix apps.json image path values that point at the old covers directory 53 | powershell -c "(Get-Content '%NEW_DIR%\apps.json').replace('.\/covers\/', '.\/config\/covers\/') | Set-Content '%NEW_DIR%\apps.json'" 54 | ) 55 | ) 56 | 57 | rem Remove log files 58 | del "%OLD_DIR%\*.txt" 59 | del "%OLD_DIR%\*.log" 60 | -------------------------------------------------------------------------------- /src_assets/windows/misc/service/uninstall-service.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | set "SERVICE_CONFIG_DIR=%LOCALAPPDATA%\LizardByte\Sunshine" 5 | set "SERVICE_CONFIG_FILE=%SERVICE_CONFIG_DIR%\service_start_type.txt" 6 | 7 | rem Save the current service start type to a file if the service exists 8 | sc qc SunshineService >nul 2>&1 9 | if %ERRORLEVEL%==0 ( 10 | if not exist "%SERVICE_CONFIG_DIR%\" mkdir "%SERVICE_CONFIG_DIR%\" 11 | 12 | rem Get the start type 13 | for /f "tokens=3" %%i in ('sc qc SunshineService ^| findstr /C:"START_TYPE"') do ( 14 | set "CURRENT_START_TYPE=%%i" 15 | ) 16 | 17 | rem Set the content to write 18 | if "!CURRENT_START_TYPE!"=="2" ( 19 | sc qc SunshineService | findstr /C:"(DELAYED)" >nul 20 | if !ERRORLEVEL!==0 ( 21 | set "CONTENT=2-delayed" 22 | ) else ( 23 | set "CONTENT=2" 24 | ) 25 | ) else if "!CURRENT_START_TYPE!" NEQ "" ( 26 | set "CONTENT=!CURRENT_START_TYPE!" 27 | ) else ( 28 | set "CONTENT=unknown" 29 | ) 30 | 31 | rem Write content to file 32 | echo !CONTENT!> "%SERVICE_CONFIG_FILE%" 33 | ) 34 | 35 | rem Stop and delete the legacy SunshineSvc service 36 | net stop sunshinesvc 37 | sc delete sunshinesvc 38 | 39 | rem Stop and delete the new SunshineService service 40 | net stop SunshineService 41 | sc delete SunshineService 42 | -------------------------------------------------------------------------------- /sunshine.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/sunshine.icns -------------------------------------------------------------------------------- /sunshine.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/sunshine.ico -------------------------------------------------------------------------------- /sunshine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/sunshine.png -------------------------------------------------------------------------------- /sunshine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | -------------------------------------------------------------------------------- /tests/fixtures/http/hello-redirect.txt: -------------------------------------------------------------------------------- 1 | hello-redirect.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/http/hello.txt: -------------------------------------------------------------------------------- 1 | hello.txt 2 | -------------------------------------------------------------------------------- /tests/tests_common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/tests_common.h 3 | * @brief Common declarations. 4 | */ 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct PlatformTestSuite: testing::Test { 12 | static void SetUpTestSuite() { 13 | ASSERT_FALSE(platf_deinit); 14 | BOOST_LOG(tests) << "Setting up platform test suite"; 15 | platf_deinit = platf::init(); 16 | ASSERT_TRUE(platf_deinit); 17 | } 18 | 19 | static void TearDownTestSuite() { 20 | ASSERT_TRUE(platf_deinit); 21 | platf_deinit = {}; 22 | BOOST_LOG(tests) << "Tore down platform test suite"; 23 | } 24 | 25 | private: 26 | inline static std::unique_ptr platf_deinit; 27 | }; 28 | -------------------------------------------------------------------------------- /tests/tests_environment.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/tests_environment.h 3 | * @brief Declarations for SunshineEnvironment. 4 | */ 5 | #pragma once 6 | #include "tests_common.h" 7 | 8 | struct SunshineEnvironment: testing::Environment { 9 | void SetUp() override { 10 | mail::man = std::make_shared(); 11 | deinit_log = logging::init(0, "test_sunshine.log"); 12 | } 13 | 14 | void TearDown() override { 15 | deinit_log = {}; 16 | mail::man = {}; 17 | } 18 | 19 | std::unique_ptr deinit_log; 20 | }; 21 | -------------------------------------------------------------------------------- /tests/tests_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/tests_main.cpp 3 | * @brief Entry point definition. 4 | */ 5 | #include "tests_common.h" 6 | #include "tests_environment.h" 7 | #include "tests_events.h" 8 | 9 | int main(int argc, char **argv) { 10 | testing::InitGoogleTest(&argc, argv); 11 | testing::AddGlobalTestEnvironment(new SunshineEnvironment); 12 | testing::UnitTest::GetInstance()->listeners().Append(new SunshineEventListener); 13 | return RUN_ALL_TESTS(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/platform/test_common.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/platform/test_common.cpp 3 | * @brief Test src/platform/common.*. 4 | */ 5 | #include "../../tests_common.h" 6 | 7 | #include 8 | #include 9 | 10 | struct SetEnvTest: ::testing::TestWithParam> { 11 | protected: 12 | void TearDown() override { 13 | // Clean up environment variable after each test 14 | const auto &[name, value, expected] = GetParam(); 15 | platf::unset_env(name); 16 | } 17 | }; 18 | 19 | TEST_P(SetEnvTest, SetEnvironmentVariableTests) { 20 | const auto &[name, value, expected] = GetParam(); 21 | platf::set_env(name, value); 22 | 23 | const char *env_value = std::getenv(name.c_str()); 24 | if (expected == 0 && !value.empty()) { 25 | ASSERT_NE(env_value, nullptr); 26 | ASSERT_EQ(std::string(env_value), value); 27 | } else { 28 | ASSERT_EQ(env_value, nullptr); 29 | } 30 | } 31 | 32 | TEST_P(SetEnvTest, UnsetEnvironmentVariableTests) { 33 | const auto &[name, value, expected] = GetParam(); 34 | platf::unset_env(name); 35 | 36 | const char *env_value = std::getenv(name.c_str()); 37 | if (expected == 0) { 38 | ASSERT_EQ(env_value, nullptr); 39 | } 40 | } 41 | 42 | INSTANTIATE_TEST_SUITE_P( 43 | SetEnvTests, 44 | SetEnvTest, 45 | ::testing::Values( 46 | std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_0", 0), 47 | std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_1", 0), 48 | std::make_tuple("", "test_value", -1) 49 | ) 50 | ); 51 | 52 | TEST(HostnameTests, TestAsioEquality) { 53 | // These should be equivalent on all platforms for ASCII hostnames 54 | ASSERT_EQ(platf::get_host_name(), boost::asio::ip::host_name()); 55 | } 56 | -------------------------------------------------------------------------------- /tests/unit/test_audio.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_audio.cpp 3 | * @brief Test src/audio.*. 4 | */ 5 | #include "../tests_common.h" 6 | 7 | #include 8 | 9 | using namespace audio; 10 | 11 | struct AudioTest: PlatformTestSuite, testing::WithParamInterface, config_t>> { 12 | void SetUp() override { 13 | m_config = std::get<1>(GetParam()); 14 | m_mail = std::make_shared(); 15 | } 16 | 17 | config_t m_config; 18 | safe::mail_t m_mail; 19 | }; 20 | 21 | constexpr std::bitset config_flags(int flag = -1) { 22 | std::bitset<3> result = std::bitset(); 23 | if (flag >= 0) { 24 | result.set(flag); 25 | } 26 | return result; 27 | } 28 | 29 | INSTANTIATE_TEST_SUITE_P( 30 | Configurations, 31 | AudioTest, 32 | testing::Values( 33 | std::make_tuple("HIGH_STEREO", config_t {5, 2, 0x3, {0}, config_flags(config_t::HIGH_QUALITY)}), 34 | std::make_tuple("SURROUND51", config_t {5, 6, 0x3F, {0}, config_flags()}), 35 | std::make_tuple("SURROUND71", config_t {5, 8, 0x63F, {0}, config_flags()}), 36 | std::make_tuple("SURROUND51_CUSTOM", config_t {5, 6, 0x3F, {6, 4, 2, {0, 1, 4, 5, 2, 3}}, config_flags(config_t::CUSTOM_SURROUND_PARAMS)}) 37 | ), 38 | [](const auto &info) { 39 | return std::string(std::get<0>(info.param)); 40 | } 41 | ); 42 | 43 | TEST_P(AudioTest, TestEncode) { 44 | std::thread timer([&] { 45 | // Terminate the audio capture after 5 seconds. 46 | std::this_thread::sleep_for(5s); 47 | auto shutdown_event = m_mail->event(mail::shutdown); 48 | auto audio_packets = m_mail->queue(mail::audio_packets); 49 | shutdown_event->raise(true); 50 | audio_packets->stop(); 51 | }); 52 | std::thread capture([&] { 53 | auto packets = m_mail->queue(mail::audio_packets); 54 | auto shutdown_event = m_mail->event(mail::shutdown); 55 | while (auto packet = packets->pop()) { 56 | if (shutdown_event->peek()) { 57 | break; 58 | } 59 | auto packet_data = packet->second; 60 | if (packet_data.size() == 0) { 61 | FAIL() << "Empty packet data"; 62 | } 63 | } 64 | }); 65 | audio::capture(m_mail, m_config, nullptr); 66 | 67 | timer.join(); 68 | capture.join(); 69 | } 70 | -------------------------------------------------------------------------------- /tests/unit/test_entry_handler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_entry_handler.cpp 3 | * @brief Test src/entry_handler.*. 4 | */ 5 | #include "../tests_common.h" 6 | #include "../tests_log_checker.h" 7 | 8 | #include 9 | 10 | TEST(EntryHandlerTests, LogPublisherDataTest) { 11 | // call log_publisher_data 12 | log_publisher_data(); 13 | 14 | // check if specific log messages exist 15 | ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Package Publisher: ")); 16 | ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Publisher Website: ")); 17 | ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Get support: ")); 18 | } 19 | -------------------------------------------------------------------------------- /tests/unit/test_logging.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_logging.cpp 3 | * @brief Test src/logging.*. 4 | */ 5 | #include "../tests_common.h" 6 | #include "../tests_log_checker.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace { 12 | std::array log_levels = { 13 | std::tuple("verbose", &verbose), 14 | std::tuple("debug", &debug), 15 | std::tuple("info", &info), 16 | std::tuple("warning", &warning), 17 | std::tuple("error", &error), 18 | std::tuple("fatal", &fatal), 19 | }; 20 | 21 | constexpr auto log_file = "test_sunshine.log"; 22 | } // namespace 23 | 24 | struct LogLevelsTest: testing::TestWithParam {}; 25 | 26 | INSTANTIATE_TEST_SUITE_P( 27 | Logging, 28 | LogLevelsTest, 29 | testing::ValuesIn(log_levels), 30 | [](const auto &info) { 31 | return std::string(std::get<0>(info.param)); 32 | } 33 | ); 34 | 35 | TEST_P(LogLevelsTest, PutMessage) { 36 | auto [label, plogger] = GetParam(); 37 | ASSERT_TRUE(plogger); 38 | auto &logger = *plogger; 39 | 40 | std::random_device rand_dev; 41 | std::mt19937_64 rand_gen(rand_dev()); 42 | auto test_message = std::to_string(rand_gen()) + std::to_string(rand_gen()); 43 | BOOST_LOG(logger) << test_message; 44 | 45 | ASSERT_TRUE(log_checker::line_contains(log_file, test_message)); 46 | } 47 | -------------------------------------------------------------------------------- /tests/unit/test_network.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_network.cpp 3 | * @brief Test src/network.* 4 | */ 5 | #include "../tests_common.h" 6 | 7 | #include 8 | 9 | struct MdnsInstanceNameTest: testing::TestWithParam> {}; 10 | 11 | TEST_P(MdnsInstanceNameTest, Run) { 12 | auto [input, expected] = GetParam(); 13 | ASSERT_EQ(net::mdns_instance_name(input), expected); 14 | } 15 | 16 | INSTANTIATE_TEST_SUITE_P( 17 | MdnsInstanceNameTests, 18 | MdnsInstanceNameTest, 19 | testing::Values( 20 | std::make_tuple("shortname-123", "shortname-123"), 21 | std::make_tuple("space 123", "space-123"), 22 | std::make_tuple("hostname.domain.test", "hostname"), 23 | std::make_tuple("&", "Sunshine"), 24 | std::make_tuple("", "Sunshine"), 25 | std::make_tuple("😁", "Sunshine"), 26 | std::make_tuple(std::string(128, 'a'), std::string(63, 'a')) 27 | ) 28 | ); 29 | -------------------------------------------------------------------------------- /tests/unit/test_rswrapper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_rswrapper.cpp 3 | * @brief Test src/rswrapper.* 4 | */ 5 | extern "C" { 6 | #include 7 | } 8 | 9 | #include "../tests_common.h" 10 | 11 | TEST(ReedSolomonWrapperTests, InitTest) { 12 | reed_solomon_init(); 13 | 14 | // Ensure all function pointers were populated 15 | ASSERT_NE(reed_solomon_new, nullptr); 16 | ASSERT_NE(reed_solomon_release, nullptr); 17 | ASSERT_NE(reed_solomon_encode, nullptr); 18 | ASSERT_NE(reed_solomon_decode, nullptr); 19 | } 20 | 21 | TEST(ReedSolomonWrapperTests, EncodeTest) { 22 | reed_solomon_init(); 23 | 24 | auto rs = reed_solomon_new(1, 1); 25 | ASSERT_NE(rs, nullptr); 26 | 27 | uint8_t dataShard[16] = {}; 28 | uint8_t fecShard[16] = {}; 29 | 30 | // If we picked the incorrect ISA in our wrapper, we should crash here 31 | uint8_t *shardPtrs[2] = {dataShard, fecShard}; 32 | auto ret = reed_solomon_encode(rs, shardPtrs, 2, sizeof(dataShard)); 33 | ASSERT_EQ(ret, 0); 34 | 35 | reed_solomon_release(rs); 36 | } 37 | -------------------------------------------------------------------------------- /tests/unit/test_stream.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_stream.cpp 3 | * @brief Test src/stream.* 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace stream { 12 | std::vector concat_and_insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data1, const std::string_view &data2); 13 | } 14 | 15 | #include "../tests_common.h" 16 | 17 | TEST(ConcatAndInsertTests, ConcatNoInsertionTest) { 18 | char b1[] = {'a', 'b'}; 19 | char b2[] = {'c', 'd', 'e'}; 20 | auto res = stream::concat_and_insert(0, 2, std::string_view {b1, sizeof(b1)}, std::string_view {b2, sizeof(b2)}); 21 | auto expected = std::vector {'a', 'b', 'c', 'd', 'e'}; 22 | ASSERT_EQ(res, expected); 23 | } 24 | 25 | TEST(ConcatAndInsertTests, ConcatLargeStrideTest) { 26 | char b1[] = {'a', 'b'}; 27 | char b2[] = {'c', 'd', 'e'}; 28 | auto res = stream::concat_and_insert(1, sizeof(b1) + sizeof(b2) + 1, std::string_view {b1, sizeof(b1)}, std::string_view {b2, sizeof(b2)}); 29 | auto expected = std::vector {0, 'a', 'b', 'c', 'd', 'e'}; 30 | ASSERT_EQ(res, expected); 31 | } 32 | 33 | TEST(ConcatAndInsertTests, ConcatSmallStrideTest) { 34 | char b1[] = {'a', 'b'}; 35 | char b2[] = {'c', 'd', 'e'}; 36 | auto res = stream::concat_and_insert(1, 1, std::string_view {b1, sizeof(b1)}, std::string_view {b2, sizeof(b2)}); 37 | auto expected = std::vector {0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e'}; 38 | ASSERT_EQ(res, expected); 39 | } 40 | -------------------------------------------------------------------------------- /tests/unit/test_video.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tests/unit/test_video.cpp 3 | * @brief Test src/video.*. 4 | */ 5 | #include "../tests_common.h" 6 | 7 | #include 8 | 9 | struct EncoderTest: PlatformTestSuite, testing::WithParamInterface { 10 | void SetUp() override { 11 | auto &encoder = *GetParam(); 12 | if (!video::validate_encoder(encoder, false)) { 13 | // Encoder failed validation, 14 | // if it's software - fail, otherwise skip 15 | if (encoder.name == "software") { 16 | FAIL() << "Software encoder not available"; 17 | } else { 18 | GTEST_SKIP() << "Encoder not available"; 19 | } 20 | } 21 | } 22 | }; 23 | 24 | INSTANTIATE_TEST_SUITE_P( 25 | EncoderVariants, 26 | EncoderTest, 27 | testing::Values( 28 | #if !defined(__APPLE__) 29 | &video::nvenc, 30 | #endif 31 | #ifdef _WIN32 32 | &video::amdvce, 33 | &video::quicksync, 34 | #endif 35 | #ifdef __linux__ 36 | &video::vaapi, 37 | #endif 38 | #ifdef __APPLE__ 39 | &video::videotoolbox, 40 | #endif 41 | &video::software 42 | ), 43 | [](const auto &info) { 44 | return std::string(info.param->name); 45 | } 46 | ); 47 | 48 | TEST_P(EncoderTest, ValidateEncoder) { 49 | // todo:: test something besides fixture setup 50 | } 51 | -------------------------------------------------------------------------------- /third-party/.clang-format-ignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Sunshine/0de8cc864c2790196142b330735c74e434a754dd/third-party/.clang-format-ignore -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(sunshine_tools) 4 | 5 | include_directories("${CMAKE_SOURCE_DIR}") 6 | 7 | add_executable(dxgi-info dxgi.cpp) 8 | set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 20) 9 | target_link_libraries(dxgi-info 10 | ${CMAKE_THREAD_LIBS_INIT} 11 | dxgi 12 | ${PLATFORM_LIBRARIES}) 13 | target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) 14 | 15 | add_executable(audio-info audio.cpp) 16 | set_target_properties(audio-info PROPERTIES CXX_STANDARD 20) 17 | target_link_libraries(audio-info 18 | ${CMAKE_THREAD_LIBS_INIT} 19 | ksuser 20 | ${PLATFORM_LIBRARIES}) 21 | target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) 22 | 23 | add_executable(sunshinesvc sunshinesvc.cpp) 24 | set_target_properties(sunshinesvc PROPERTIES CXX_STANDARD 20) 25 | target_link_libraries(sunshinesvc 26 | ${CMAKE_THREAD_LIBS_INIT} 27 | wtsapi32 28 | ${PLATFORM_LIBRARIES}) 29 | target_compile_options(sunshinesvc PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) 30 | --------------------------------------------------------------------------------