diff --git a/.codeql-prebuild-cpp-Windows.sh b/.codeql-prebuild-cpp-Windows.sh index e03f8740..b860c9e8 100644 --- a/.codeql-prebuild-cpp-Windows.sh +++ b/.codeql-prebuild-cpp-Windows.sh @@ -4,6 +4,28 @@ set -e # update pacman pacman --noconfirm -Syu +gcc_version="14.2.0-3" + +broken_deps=( + "mingw-w64-ucrt-x86_64-gcc" + "mingw-w64-ucrt-x86_64-gcc-libs" +) + +tarballs="" +for dep in "${broken_deps[@]}"; do + tarball="${dep}-${gcc_version}-any.pkg.tar.zst" + + # download and install working version + wget https://repo.msys2.org/mingw/ucrt64/${tarball} + + tarballs="${tarballs} ${tarball}" +done + +# install broken dependencies +if [ -n "$tarballs" ]; then + pacman -U --noconfirm ${tarballs} +fi + # install dependencies dependencies=( "git" @@ -20,7 +42,8 @@ dependencies=( "mingw-w64-ucrt-x86_64-opus" "mingw-w64-ucrt-x86_64-toolchain" ) -pacman -S --noconfirm "${dependencies[@]}" + +pacman -Syu --noconfirm --ignore="$(IFS=,; echo "${broken_deps[*]}")" "${dependencies[@]}" # build mkdir -p build diff --git a/docs/Doxyfile b/docs/Doxyfile index 5fa4ee6c..8549dbba 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -49,6 +49,7 @@ INPUT = ../README.md \ legal.md \ configuration.md \ app_examples.md \ + awesome_sunshine.md \ guides.md \ performance_tuning.md \ api.md \ diff --git a/docs/app_examples.md b/docs/app_examples.md index 0a814f75..782681fd 100644 --- a/docs/app_examples.md +++ b/docs/app_examples.md @@ -23,25 +23,28 @@ process is killed.} @tabs{ @tab{Linux | - \| Field \| Value \| - \|-------------------\|-----------------------------------------------------\| - \| Application Name \| @code{}Steam Big Picture@endcode \| - \| Detached Commands \| @code{}setsid steam steam://open/bigpicture@endcode \| - \| Image \| @code{}steam.png@endcode \| + \| Field \| Value \| + \|------------------------------\|------------------------------------------------------\| + \| Application Name \| @code{}Steam Big Picture@endcode \| + \| Command Preporations -> Undo \| @code{}setsid steam steam://close/bigpicture@endcode \| + \| Detached Commands \| @code{}setsid steam steam://open/bigpicture@endcode \| + \| Image \| @code{}steam.png@endcode \| } @tab{macOS | - \| Field \| Value \| - \|-------------------\|---------------------------------------------------\| - \| Application Name \| @code{}Steam Big Picture@endcode \| - \| Detached Commands \| @code{}open steam steam://open/bigpicture@endcode \| - \| Image \| @code{}steam.png@endcode \| + \| Field \| Value \| + \|------------------------------\|------------------------------------------------\| + \| Application Name \| @code{}Steam Big Picture@endcode \| + \| Command Preporations -> Undo \| @code{}open steam://close/bigpicture@endcode \| + \| Detached Commands \| @code{}open steam://open/bigpicture@endcode \| + \| Image \| @code{}steam.png@endcode \| } @tab{Windows | - \| Field \| Value \| - \|-------------------\|----------------------------------------\| - \| Application Name \| @code{}Steam Big Picture@endcode \| - \| Detached Commands \| @code{}steam://open/bigpicture@endcode \| - \| Image \| @code{}steam.png@endcode \| + \| Field \| Value \| + \|------------------------------\|-------------------------------------------\| + \| Application Name \| @code{}Steam Big Picture@endcode \| + \| Command Preporations -> Undo \| @code{}steam://close/bigpicture@endcode \| + \| Detached Commands \| @code{}steam://open/bigpicture@endcode \| + \| Image \| @code{}steam.png@endcode \| } } @@ -210,7 +213,7 @@ xrandr --output ${display_output} --primary --mode ${mode_alias} --pos 0x0 --rot ``` } -###### Wayland +###### Wayland (wlroots, e.g. hyprland) | Prep Step | Command | |-----------|------------------------------------------------------------------------------------------------------------------------------------------| @@ -219,17 +222,30 @@ xrandr --output ${display_output} --primary --mode ${mode_alias} --pos 0x0 --rot @hint{`wlr-xrandr` only works with wlroots-based compositors.} -###### Gnome (Wayland, X11) +###### Gnome (X11) | Prep Step | Command | |-----------|---------------------------------------------------------------------------------------------------------------------------------------| | Do | @code{}sh -c "xrandr --output HDMI-1 --mode ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --rate ${SUNSHINE_CLIENT_FPS}"@endcode | | Undo | @code{}xrandr --output HDMI-1 --mode 3840x2160 --rate 120@endcode | -The commands above are valid for an X11 session but won't work for -Wayland. In that case `xrandr` must be replaced by [gnome-randr.py](https://gitlab.com/Oschowa/gnome-randr). -This script is intended as a drop-in replacement with the same syntax. (It can be saved in -`/usr/local/bin` and needs to be made executable.) +###### Gnome (Wayland) + +| Prep Step | Command | +|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Do | @code{}sh -c "displayconfig-mutter set --connector HDMI-1 --resolution ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --refresh-rate ${SUNSHINE_CLIENT_FPS} --hdr ${SUNSHINE_CLIENT_HDR}"@endcode | +| Undo | @code{}displayconfig-mutter set --connector HDMI-1 --resolution 3840x2160 --refresh-rate 120 --hdr false@endcode | + +Installation instructions for displayconfig-mutter can be [found here](https://github.com/eaglesemanation/displayconfig-mutter). Alternatives include +[gnome-randr-rust](https://github.com/maxwellainatchi/gnome-randr-rust) and [gnome-randr.py](https://gitlab.com/Oschowa/gnome-randr), but both of those are +unmaintained and do not support newer Mutter features such as HDR and VRR. + +@hint{HDR support has been added to Gnome 48, to check if your display supports it you can run this: +``` +displayconfig-mutter list +``` +If it doesn't, then remove ``--hdr`` flag from both ``Do`` and ``Undo`` steps. +} ###### KDE Plasma (Wayland, X11) @@ -322,9 +338,9 @@ UAC prompt.
-| Previous | Next | -|:----------------------------------|--------------------:| -| [Configuration](configuration.md) | [Guides](guides.md) | +| Previous | Next | +|:----------------------------------|----------------------------------------:| +| [Configuration](configuration.md) | [Awesome-Sunshine](awesome_sunshine.md) |
diff --git a/docs/awesome_sunshine.md b/docs/awesome_sunshine.md new file mode 100644 index 00000000..554c8113 --- /dev/null +++ b/docs/awesome_sunshine.md @@ -0,0 +1,23 @@ +# Awesome-Sunshine + +@htmlonly + + + +@endhtmlonly + +
+ +| Previous | Next | +|:--------------------------------|--------------------:| +| [App Examples](app_examples.md) | [Guides](guides.md) | + +
+ +
+ + [TOC] +
diff --git a/docs/configuration.md b/docs/configuration.md index 315ea7e2..ef248643 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -770,6 +770,29 @@ editing the `conf` file in a text editor. Use the examples as reference. +### stream_audio + + + + + + + + + + + + + + +
Description + Whether to stream audio or not. Disabling this can be useful for streaming headless displays as second monitors. +
Default@code{} + enabled + @endcode
Example@code{} + stream_audio = disabled + @endcode
+ ### install_steam_audio_drivers @@ -1947,7 +1970,8 @@ editing the `conf` file in a text editor. Use the examples as reference. - diff --git a/docs/guides.md b/docs/guides.md index 1b9f654a..8f3d056d 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -7,9 +7,9 @@ Feel free to contribute your own tips and trips by making a PR to
-| Previous | Next | -|:--------------------------------|--------------------------------------------:| -| [App Examples](app_examples.md) | [Performance Tuning](performance_tuning.md) | +| Previous | Next | +|:----------------------------------------|--------------------------------------------:| +| [Awesome-Sunshine](awesome_sunshine.md) | [Performance Tuning](performance_tuning.md) |
diff --git a/gh-pages-template/index.html b/gh-pages-template/index.html index 24a97352..9b0c2f5e 100644 --- a/gh-pages-template/index.html +++ b/gh-pages-template/index.html @@ -56,7 +56,7 @@ ext-js:
- Moonlight + Moonlight
Moonlight Support
@@ -163,7 +163,7 @@ ext-js:
- + Android
@@ -182,7 +182,7 @@ ext-js: Get it on Google Play + height="60"/>
@@ -190,14 +190,14 @@ ext-js: Available at Amazon Appstore + style="padding: 10px;"/>
@@ -210,7 +210,7 @@ ext-js:
- + Chrome Web Store
@@ -229,7 +229,7 @@ ext-js: Available in the Chrome Web Store + height="30"/>
@@ -242,7 +242,8 @@ ext-js:
- + iOS + Apple TV
@@ -261,14 +262,14 @@ ext-js: Download on the App Store + height="40"/>
@@ -281,10 +282,10 @@ ext-js:
- - - - + Linux + macOS + Windows + Steam
@@ -314,7 +315,7 @@ ext-js:
- + Raspberry Pi
@@ -344,7 +345,7 @@ ext-js:
- + Xbox
@@ -363,7 +364,7 @@ ext-js: Get it from Microsoft + height="40"/>
@@ -376,7 +377,7 @@ ext-js:
- + PlayStation Vita
@@ -406,12 +407,16 @@ ext-js:
- + Nintendo Switch + Android + Apple TV + iOS + macOS
@@ -436,7 +441,7 @@ ext-js:
- + Wii U
@@ -466,7 +471,7 @@ ext-js:
- + LG webOS TV
@@ -514,7 +519,7 @@ ext-js:
@@ -537,27 +542,37 @@ ext-js:
diff --git a/package.json b/package.json index 96fc8fc8..f504e49c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "vue-i18n": "11.1.3" }, "devDependencies": { + "@codecov/vite-plugin": "1.9.0", "@vitejs/plugin-vue": "4.6.2", "serve": "14.2.3", "vite": "4.5.9", diff --git a/packaging/linux/flatpak/deps/flatpak-builder-tools b/packaging/linux/flatpak/deps/flatpak-builder-tools index aac65cf4..bf91cb0b 160000 --- a/packaging/linux/flatpak/deps/flatpak-builder-tools +++ b/packaging/linux/flatpak/deps/flatpak-builder-tools @@ -1 +1 @@ -Subproject commit aac65cf44cd4e008594a9d9ac1db08e2025067a6 +Subproject commit bf91cb0bee7ce0c8021e223e3ea9c5110ebb82de diff --git a/packaging/sunshine.rb b/packaging/sunshine.rb index 81e42b09..f13a5a38 100644 --- a/packaging/sunshine.rb +++ b/packaging/sunshine.rb @@ -29,6 +29,7 @@ class @PROJECT_NAME@ < Formula depends_on "cmake" => :build depends_on "doxygen" => :build depends_on "graphviz" => :build + depends_on "ninja" => :build depends_on "node" => :build depends_on "pkg-config" => :build depends_on "curl" @@ -38,6 +39,24 @@ class @PROJECT_NAME@ < Formula depends_on "icu4c" => :recommended on_linux do + # the "build" dependencies are for libayatana-appindicator + depends_on "at-spi2-core" => :build + depends_on "cairo" => :build + depends_on "fontconfig" => :build + depends_on "freetype" => :build + depends_on "fribidi" => :build + depends_on "gettext" => :build + depends_on "gobject-introspection" => :build + depends_on "graphite2" => :build + depends_on "gtk+3" => :build + depends_on "harfbuzz" => :build + depends_on "intltool" => :build + depends_on "libepoxy" => :build + depends_on "libxdamage" => :build + depends_on "libxkbcommon" => :build + depends_on "pango" => :build + depends_on "perl" => :build + depends_on "pixman" => :build depends_on "avahi" depends_on "libcap" depends_on "libdrm" @@ -56,6 +75,128 @@ class @PROJECT_NAME@ < Formula depends_on "pulseaudio" depends_on "systemd" depends_on "wayland" + + # resources that do not have brew packages + resource "libayatana-appindicator" do + url "https://github.com/AyatanaIndicators/libayatana-appindicator/archive/refs/tags/0.5.94.tar.gz" + sha256 "884a6bc77994c0b58c961613ca4c4b974dc91aa0f804e70e92f38a542d0d0f90" + end + + resource "libdbusmenu" do + url "https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz" + sha256 "b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a" + + patch 'From 729546c51806a1b3ea6cb6efb7a115b1baa811f1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Stefan=20Br=C3=BCns?= +Date: Mon, 18 Nov 2019 19:58:53 +0100 +Subject: [PATCH 1/1] Fix HAVE_VALGRIND AM_CONDITIONAL + +The AM_CONDITIONAL should also be run with --disable-tests, otherwise +HAVE_VALGRIND is undefined. +--- + configure | 4 ++-- + configure.ac | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/configure b/configure +index 831a3bb..8913b9b 100644 +--- a/configure ++++ b/configure +@@ -14801,6 +14801,8 @@ else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + $as_echo "yes" >&6; } + have_valgrind=yes ++fi ++ + fi + if test "x$have_valgrind" = "xyes"; then + HAVE_VALGRIND_TRUE= +@@ -14811,8 +14813,6 @@ else + fi + + +-fi +- + + + +diff --git a/configure.ac b/configure.ac +index ace54d1..cbd38a6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -120,8 +120,8 @@ PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION + [have_tests=yes] + ) + PKG_CHECK_MODULES(DBUSMENUTESTSVALGRIND, valgrind, have_valgrind=yes, have_valgrind=no) +-AM_CONDITIONAL([HAVE_VALGRIND], [test "x$have_valgrind" = "xyes"]) + ]) ++AM_CONDITIONAL([HAVE_VALGRIND], [test "x$have_valgrind" = "xyes"]) + + AC_SUBST(DBUSMENUTESTS_CFLAGS) + AC_SUBST(DBUSMENUTESTS_LIBS) +-- +2.46.2 + + +' + end + + resource "ayatana-ido" do + url "https://github.com/AyatanaIndicators/ayatana-ido/archive/refs/tags/0.10.4.tar.gz" + sha256 "bd59abd5f1314e411d0d55ce3643e91cef633271f58126be529de5fb71c5ab38" + + patch 'From 8a09e6ad33c58c017c0c8fd756da036fc39428ea Mon Sep 17 00:00:00 2001 +From: Alexander Koskovich +Date: Sun, 29 Sep 2024 13:47:54 -0400 +Subject: [PATCH 1/1] Make introspection configurable + +--- + CMakeLists.txt | 1 + + src/CMakeLists.txt | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0e13fcd..f3e9ec0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -12,6 +12,7 @@ endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + option(ENABLE_TESTS "Enable all tests and checks" OFF) + option(ENABLE_COVERAGE "Enable coverage reports (includes enabling all tests and checks)" OFF) + option(ENABLE_WERROR "Treat all build warnings as errors" OFF) ++option(ENABLE_INTROSPECTION "Enable introspection" ON) + + if(ENABLE_COVERAGE) + set(ENABLE_TESTS ON) +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 5b3638d..aca9481 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -108,6 +108,8 @@ install(TARGETS "ayatana-ido3-0.4" LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIB + + # AyatanaIdo3-0.4.gir + ++if (ENABLE_INTROSPECTION) ++ + find_package(GObjectIntrospection REQUIRED QUIET) + + if (INTROSPECTION_FOUND) +@@ -183,3 +185,5 @@ if (INTROSPECTION_FOUND) + endif () + + endif () ++ ++endif () +-- +2.46.2 + + +' + end + + resource "libayatana-indicator" do + url "https://github.com/AyatanaIndicators/libayatana-indicator/archive/refs/tags/0.9.4.tar.gz" + sha256 "a18d3c682e29afd77db24366f8475b26bda22b0e16ff569a2ec71cd6eb4eac95" + end end def install @@ -65,12 +206,12 @@ class @PROJECT_NAME@ < Formula args = %W[ -DBUILD_WERROR=ON + -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=#{prefix} -DHOMEBREW_ALLOW_FETCHCONTENT=ON -DOPENSSL_ROOT_DIR=#{Formula["openssl"].opt_prefix} -DSUNSHINE_ASSETS_DIR=sunshine/assets -DSUNSHINE_BUILD_HOMEBREW=ON - -DSUNSHINE_ENABLE_TRAY=OFF -DSUNSHINE_PUBLISHER_NAME='LizardByte' -DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' -DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support' @@ -105,16 +246,69 @@ class @PROJECT_NAME@ < Formula end args << "-DCUDA_FAIL_ON_MISSING=OFF" if OS.linux? + args << "-DSUNSHINE_ENABLE_TRAY=OFF" if OS.mac? - system "cmake", "-S", ".", "-B", "build", *std_cmake_args, *args + # Handle system tray on Linux + if OS.linux? + # Build and install libayatana components - cd "build" do - system "make" - system "make", "install" + # Build libdbusmenu + resource("libdbusmenu").stage do + system "./configure", + "--prefix=#{prefix}", + "--with-gtk=3", + "--disable-dumper", + "--disable-static", + "--disable-tests", + "--disable-gtk-doc", + "--enable-introspection=no", + "--disable-vala" + system "make", "install" + end - bin.install "tests/test_sunshine" + # Build ayatana-ido + resource("ayatana-ido").stage do + system "cmake", "-S", ".", "-B", "build", "-G", "Ninja", + "-DCMAKE_INSTALL_PREFIX=#{prefix}", + "-DENABLE_INTROSPECTION=OFF", + *std_cmake_args + system "ninja", "-C", "build" + system "ninja", "-C", "build", "install" + end + + # Build libayatana-indicator + resource("libayatana-indicator").stage do + ENV.append_path "PKG_CONFIG_PATH", "#{lib}/pkgconfig" + ENV.append "LDFLAGS", "-L#{lib}" + + system "cmake", "-S", ".", "-B", "build", "-G", "Ninja", + "-DCMAKE_INSTALL_PREFIX=#{prefix}", + *std_cmake_args + system "ninja", "-C", "build" + system "ninja", "-C", "build", "install" + end + + # Build libayatana-appindicator + resource("libayatana-appindicator").stage do + system "cmake", "-S", ".", "-B", "build", "-G", "Ninja", + "-DCMAKE_INSTALL_PREFIX=#{prefix}", + "-DENABLE_BINDINGS_MONO=OFF", + "-DENABLE_BINDINGS_VALA=OFF", + "-DENABLE_GTKDOC=OFF", + *std_cmake_args + system "ninja", "-C", "build" + system "ninja", "-C", "build", "install" + end end + system "cmake", "-S", ".", "-B", "build", "-G", "Unix Makefiles", + *std_cmake_args, + *args + + system "make", "-C", "build" + system "make", "-C", "build", "install" + bin.install "build/tests/test_sunshine" + # codesign the binary on intel macs system "codesign", "-s", "-", "--force", "--deep", bin/"sunshine" if OS.mac? && Hardware::CPU.intel? @@ -157,6 +351,7 @@ class @PROJECT_NAME@ < Formula system bin/"sunshine", "--version" # run the test suite - system bin/"test_sunshine", "--gtest_color=yes" + system bin/"test_sunshine", "--gtest_color=yes", "--gtest_output=xml:test_results.xml" + assert_path_exists testpath/"test_results.xml" end end diff --git a/scripts/linux_build.sh b/scripts/linux_build.sh index f80ed7bf..eb901b8b 100644 --- a/scripts/linux_build.sh +++ b/scripts/linux_build.sh @@ -90,6 +90,52 @@ shift $((OPTIND -1)) # dependencies array to build out dependencies=() +function add_arch_deps() { + dependencies+=( + 'avahi' + 'base-devel' + 'cmake' + 'curl' + "gcc${gcc_version}" + "gcc${gcc_version}-libs" + 'git' + 'libayatana-appindicator' + 'libcap' + 'libdrm' + 'libevdev' + 'libmfx' + 'libnotify' + 'libpulse' + 'libva' + 'libx11' + 'libxcb' + 'libxfixes' + 'libxrandr' + 'libxtst' + 'miniupnpc' + 'ninja' + 'nodejs' + 'npm' + 'numactl' + 'openssl' + 'opus' + 'udev' + 'wayland' + ) + + if [ "$skip_libva" == 0 ]; then + dependencies+=( + "libva" # VA-API + ) + fi + + if [ "$skip_cuda" == 0 ]; then + dependencies+=( + "cuda" # VA-API + ) + fi +} + function add_debian_based_deps() { dependencies+=( "bison" # required if we need to compile doxygen @@ -157,8 +203,8 @@ function add_fedora_deps() { dependencies+=( "cmake" "doxygen" - "gcc" - "g++" + "gcc${gcc_version}" + "gcc${gcc_version}-c++" "git" "graphviz" "libappindicator-gtk3-devel" @@ -198,9 +244,15 @@ function add_fedora_deps() { } function install_cuda() { + nvcc_path=$(command -v nvcc 2>/dev/null) || true + if [ -n "$nvcc_path" ]; then + echo "found system cuda" + return + fi # check if we need to install cuda if [ -f "${build_dir}/cuda/bin/nvcc" ]; then - echo "cuda already installed" + nvcc_path="${build_dir}/cuda/bin/nvcc" + echo "found local cuda" return fi @@ -237,11 +289,13 @@ function install_cuda() { chmod a+x "${build_dir}/cuda.run" "${build_dir}/cuda.run" --silent --toolkit --toolkitpath="${build_dir}/cuda" --no-opengl-libs --no-man-page --no-drm rm "${build_dir}/cuda.run" + nvcc_path="${build_dir}/cuda/bin/nvcc" } function check_version() { local package_name=$1 local min_version=$2 + local max_version=$3 local installed_version echo "Checking if $package_name is installed and at least version $min_version" @@ -250,6 +304,8 @@ function check_version() { installed_version=$(dpkg -s "$package_name" 2>/dev/null | grep '^Version:' | awk '{print $2}') elif [ "$distro" == "fedora" ]; then installed_version=$(rpm -q --queryformat '%{VERSION}' "$package_name" 2>/dev/null) + elif [ "$distro" == "arch" ]; then + installed_version=$(pacman -Q "$package_name" | awk '{print $2}' ) else echo "Unsupported Distro" return 1 @@ -260,11 +316,12 @@ function check_version() { return 1 fi - if [ "$(printf '%s\n' "$installed_version" "$min_version" | sort -V | head -n1)" = "$min_version" ]; then - echo "$package_name version $installed_version is at least $min_version" +if [[ "$(printf '%s\n' "$installed_version" "$min_version" | sort -V | head -n1)" = "$min_version" ]] && \ + [[ "$(printf '%s\n' "$installed_version" "$max_version" | sort -V | head -n1)" = "$installed_version" ]]; then + echo "Installed version is within range" return 0 else - echo "$package_name version $installed_version is less than $min_version" + echo "$package_name version $installed_version is out of range" return 1 fi } @@ -305,7 +362,9 @@ function run_install() { # Update the package list $package_update_command - if [ "$distro" == "debian" ]; then + if [ "$distro" == "arch" ]; then + add_arch_deps + elif [ "$distro" == "debian" ]; then add_debian_deps elif [ "$distro" == "ubuntu" ]; then add_ubuntu_deps @@ -314,7 +373,7 @@ function run_install() { ${sudo_cmd} dnf group install "development-tools" -y elif [ "$distro" == "fedora" ] && [ "$version" <= "40" ]; then add_fedora_deps - ${sudo_cmd} dnf group install "Development Tools" -y + ${sudo_cmd} dnf group install "$dev_tools_group" -y fi @@ -333,8 +392,11 @@ function run_install() { "gcc-ranlib" ) - # update alternatives for gcc and g++ if a debian based distro - if [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then + #set gcc version based on distros + if [ "$distro" == "arch" ]; then + export CC=gcc-14 + export CXX=g++-14 + elif [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then for file in "${gcc_alternative_files[@]}"; do file_path="/etc/alternatives/$file" if [ -e "$file_path" ]; then @@ -353,7 +415,7 @@ function run_install() { # compile cmake if the version is too low cmake_min="3.25.0" target_cmake_version="3.30.1" - if ! check_version "cmake" "$cmake_min"; then + if ! check_version "cmake" "$cmake_min" "inf"; then cmake_prefix="https://github.com/Kitware/CMake/releases/download/v" if [ "$architecture" == "x86_64" ]; then cmake_arch="x86_64" @@ -371,7 +433,8 @@ function run_install() { # compile doxygen if version is too low doxygen_min="1.10.0" _doxygen_min="1_10_0" - if ! check_version "doxygen" "$doxygen_min"; then + doxygen_max="1.12.0" + if ! check_version "doxygen" "$doxygen_min" "$doxygen_max"; then if [ "${SUNSHINE_COMPILE_DOXYGEN}" == "true" ]; then echo "Compiling doxygen" doxygen_url="https://github.com/doxygen/doxygen/releases/download/Release_${_doxygen_min}/doxygen-${doxygen_min}.src.tar.gz" @@ -383,7 +446,7 @@ function run_install() { ninja -C "build" -j"${num_processors}" ninja -C "build" install else - echo "Doxygen version too low, skipping docs" + echo "Doxygen version not in range, skipping docs" cmake_args+=("-DBUILD_DOCS=OFF") fi fi @@ -399,12 +462,10 @@ function run_install() { fi # run the cuda install - if [ -n "$cuda_version" ] && [ "$skip_cuda" == 0 ]; then + if [ "$skip_cuda" == 0 ]; then install_cuda cmake_args+=("-DSUNSHINE_ENABLE_CUDA=ON") - cmake_args+=("-DCMAKE_CUDA_COMPILER:PATH=${build_dir}/cuda/bin/nvcc") - else - cmake_args+=("-DSUNSHINE_ENABLE_CUDA=OFF") + cmake_args+=("-DCMAKE_CUDA_COMPILER:PATH=$nvcc_path") fi # Cmake stuff here @@ -444,7 +505,15 @@ function run_install() { # Determine the OS and call the appropriate function cat /etc/os-release -if grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then + +if grep -q "Arch Linux" /etc/os-release; then + distro="arch" + version="" + package_update_command="${sudo_cmd} pacman -Syu --noconfirm" + package_install_command="${sudo_cmd} pacman -Sy --needed" + nvm_node=0 + gcc_version="14" +elif grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then distro="debian" version="12" package_update_command="${sudo_cmd} apt-get update" @@ -453,33 +522,36 @@ if grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then cuda_build="525.60.13" gcc_version="12" nvm_node=0 -elif grep -q "PLATFORM_ID=\"platform:f39\"" /etc/os-release; then - distro="fedora" - version="39" - package_update_command="${sudo_cmd} dnf update -y" - package_install_command="${sudo_cmd} dnf install -y" - cuda_version="12.4.0" - cuda_build="550.54.14" - gcc_version="13" - nvm_node=0 elif grep -q "PLATFORM_ID=\"platform:f40\"" /etc/os-release; then distro="fedora" version="40" package_update_command="${sudo_cmd} dnf update -y" package_install_command="${sudo_cmd} dnf install -y" - cuda_version= - cuda_build= + cuda_version=12.6.3 + cuda_build=560.35.05 gcc_version="13" nvm_node=0 + dev_tools_group="Development Tools" elif grep -q "PLATFORM_ID=\"platform:f41\"" /etc/os-release; then distro="fedora" version="41" package_update_command="${sudo_cmd} dnf update -y" package_install_command="${sudo_cmd} dnf install -y" - cuda_version= - cuda_build= + cuda_version=12.6.3 + cuda_build=560.35.05 gcc_version="13" nvm_node=0 + dev_tools_group="development-tools" +elif grep -q "PLATFORM_ID=\"platform:f42\"" /etc/os-release; then + distro="fedora" + version="42" + package_update_command="${sudo_cmd} dnf update -y" + package_install_command="${sudo_cmd} dnf install -y" + cuda_version=12.8.1 + cuda_build=570.124.06 + gcc_version="14" + nvm_node=0 + dev_tools_group="development-tools" elif grep -q "Ubuntu 22.04" /etc/os-release; then distro="ubuntu" version="22.04" diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 7cfbae48..b2c50014 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,2 +1,2 @@ Babel==2.17.0 -clang-format +clang-format==20.* diff --git a/src/audio.cpp b/src/audio.cpp index 8eaa7f76..18cf7f05 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -129,13 +129,10 @@ namespace audio { void capture(safe::mail_t mail, config_t config, void *channel_data) { auto shutdown_event = mail->event(mail::shutdown); - - if (config.input_only) { - BOOST_LOG(info) << "Input only session, audio will not be captured."sv; + if (!config::audio.stream || config.input_only) { shutdown_event->view(); return; } - auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) { apply_surround_params(stream, config.customStreamParams); diff --git a/src/config.cpp b/src/config.cpp index 8eca6ee1..31e5c452 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -416,12 +416,7 @@ namespace config { auto final_resolution = entry.template get_optional("final_resolution"s); auto final_refresh_rate = entry.template get_optional("final_refresh_rate"s); - output_field.push_back(video_t::dd_t::mode_remapping_entry_t { - requested_resolution.value_or(""), - requested_fps.value_or(""), - final_resolution.value_or(""), - final_refresh_rate.value_or("") - }); + output_field.push_back(video_t::dd_t::mode_remapping_entry_t {requested_resolution.value_or(""), requested_fps.value_or(""), final_resolution.value_or(""), final_refresh_rate.value_or("")}); } }}; @@ -523,6 +518,7 @@ namespace config { audio_t audio { {}, // audio_sink {}, // virtual_sink + true, // stream audio true, // install_steam_drivers true, // keep_sink_default true, // auto_capture @@ -1215,6 +1211,7 @@ namespace config { string_f(vars, "audio_sink", audio.sink); string_f(vars, "virtual_sink", audio.virtual_sink); + bool_f(vars, "stream_audio", audio.stream); bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers); bool_f(vars, "keep_sink_default", audio.keep_default); bool_f(vars, "auto_capture_sink", audio.auto_capture); diff --git a/src/config.h b/src/config.h index 0e5a2a76..e77acfb6 100644 --- a/src/config.h +++ b/src/config.h @@ -152,6 +152,7 @@ namespace config { struct audio_t { std::string sink; std::string virtual_sink; + bool stream; bool install_steam_drivers; bool keep_default; bool auto_capture; diff --git a/src/network.cpp b/src/network.cpp index 070a4c44..d388afd6 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -186,7 +186,7 @@ namespace net { std::uint16_t map_port(int port) { // calculate the port from the config port - auto mapped_port = (std::uint16_t)((int) config::sunshine.port + port); + auto mapped_port = (std::uint16_t) ((int) config::sunshine.port + port); // Ensure port is in the range of 1024-65535 if (mapped_port < 1024 || mapped_port > 65535) { diff --git a/src/platform/linux/graphics.cpp b/src/platform/linux/graphics.cpp index 568067f3..245addb6 100644 --- a/src/platform/linux/graphics.cpp +++ b/src/platform/linux/graphics.cpp @@ -19,7 +19,7 @@ extern "C" { // There aren't that many DRM_FORMAT I need to use, so define them here // // They aren't likely to change any time soon. -#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) +#define fourcc_code(a, b, c, d) ((std::uint32_t) (a) | ((std::uint32_t) (b) << 8) | ((std::uint32_t) (c) << 16) | ((std::uint32_t) (d) << 24)) #define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) #define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1)) diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp index a166d3b0..04bfe198 100644 --- a/src/platform/windows/audio.cpp +++ b/src/platform/windows/audio.cpp @@ -117,15 +117,26 @@ namespace { using virtual_sink_waveformats_t = std::vector; + /** + * @brief List of supported waveformats for an N-channel virtual audio device + * @tparam channel_count Number of virtual audio channels + * @returns std::vector + * @note The list of virtual formats returned are sorted in preference order and the first valid + * format will be used. All bits-per-sample options are listed because we try to match + * this to the default audio device. See also: set_format() below. + */ template virtual_sink_waveformats_t create_virtual_sink_waveformats() { if constexpr (channel_count == 2) { auto channel_mask = waveformat_mask_stereo; - // only choose 24 or 16-bit formats to avoid clobbering existing Dolby/DTS spatial audio settings + // The 32-bit formats are a lower priority for stereo because using one will disable Dolby/DTS + // spatial audio mode if the user enabled it on the Steam speaker. return { create_waveformat(sample_format_e::s24in32, channel_count, channel_mask), create_waveformat(sample_format_e::s24, channel_count, channel_mask), create_waveformat(sample_format_e::s16, channel_count, channel_mask), + create_waveformat(sample_format_e::f32, channel_count, channel_mask), + create_waveformat(sample_format_e::s32, channel_count, channel_mask), }; } else if (channel_count == 6) { auto channel_mask1 = waveformat_mask_surround51_with_backspeakers; @@ -298,6 +309,10 @@ namespace platf::audio { auto waveformatext_pointer = reinterpret_cast(mixer_waveformat.get()); capture_waveformat.dwChannelMask = waveformatext_pointer->dwChannelMask; } + + BOOST_LOG(info) << "Audio mixer format is "sv << mixer_waveformat->wBitsPerSample << "-bit, "sv + << mixer_waveformat->nSamplesPerSec << " Hz, "sv + << ((mixer_waveformat->nSamplesPerSec != 48000) ? "will be resampled to 48000 by Windows"sv : "no resampling needed"sv); } status = audio_client->Initialize( @@ -315,7 +330,7 @@ namespace platf::audio { return nullptr; } - BOOST_LOG(info) << "Audio capture format is " << logging::bracket(waveformat_to_pretty_string(capture_waveformat)); + BOOST_LOG(info) << "Audio capture format is "sv << logging::bracket(waveformat_to_pretty_string(capture_waveformat)); return audio_client; } @@ -795,6 +810,22 @@ namespace platf::audio { } } + // When switching to a Steam virtual speaker device, try to retain the bit depth of the + // default audio device. Switching from a 16-bit device to a 24-bit one has been known to + // cause glitches for some users. + int wanted_bits_per_sample = 32; + auto current_default_dev = default_device(device_enum); + if (current_default_dev) { + audio::prop_t prop; + prop_var_t current_device_format; + + if (SUCCEEDED(current_default_dev->OpenPropertyStore(STGM_READ, &prop)) && SUCCEEDED(prop->GetValue(PKEY_AudioEngine_DeviceFormat, ¤t_device_format.prop))) { + auto *format = (WAVEFORMATEXTENSIBLE *) current_device_format.prop.blob.pBlobData; + wanted_bits_per_sample = format->Samples.wValidBitsPerSample; + BOOST_LOG(info) << "Virtual audio device will use "sv << wanted_bits_per_sample << "-bit to match default device"sv; + } + } + auto &device_id = virtual_sink_info->first; auto &waveformats = virtual_sink_info->second.get().virtual_sink_waveformats; for (const auto &waveformat : waveformats) { @@ -804,6 +835,10 @@ namespace platf::audio { auto waveformat_copy = waveformat; auto waveformat_copy_pointer = reinterpret_cast(&waveformat_copy); + if (wanted_bits_per_sample != waveformat.Samples.wValidBitsPerSample) { + continue; + } + WAVEFORMATEXTENSIBLE p {}; if (SUCCEEDED(policy->SetDeviceFormat(device_id_copy.c_str(), waveformat_copy_pointer, (WAVEFORMATEX *) &p))) { BOOST_LOG(info) << "Changed virtual audio sink format to " << logging::bracket(waveformat_to_pretty_string(waveformat)); diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index bf85af4f..7a8b0705 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -219,7 +219,7 @@ namespace platf::dxgi { { util::buffer_t cursor_img = img_data; std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF); if (alpha == 0xFF) { // Pixels with 0xFF alpha will be XOR-blended as is. } else if (alpha == 0x00) { @@ -286,7 +286,7 @@ namespace platf::dxgi { { util::buffer_t cursor_img = img_data; std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF); if (alpha == 0xFF) { // Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image(). // We make them transparent for the alpha-blended cursor image. diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index 53591bd7..50f8aab8 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -92,10 +92,10 @@ namespace platf { constexpr float EARTH_G = 9.80665f; -#define MPS2_TO_DS4_ACCEL(x) (int32_t)(((x) / EARTH_G) * 8192) -#define DPS_TO_DS4_GYRO(x) (int32_t)((x) * (1024 / 64)) +#define MPS2_TO_DS4_ACCEL(x) (int32_t) (((x) / EARTH_G) * 8192) +#define DPS_TO_DS4_GYRO(x) (int32_t) ((x) * (1024 / 64)) -#define APPLY_CALIBRATION(val, bias, scale) (int32_t)(((float) (val) + (bias)) / (scale)) +#define APPLY_CALIBRATION(val, bias, scale) (int32_t) (((float) (val) + (bias)) / (scale)) constexpr DS4_TOUCH ds4_touch_unused = { .bPacketCounter = 0, diff --git a/src/rtsp.cpp b/src/rtsp.cpp index 9a1529ab..b6998ebf 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -112,7 +112,7 @@ namespace rtsp_stream { boost::asio::async_read(sock, boost::asio::buffer(begin, sizeof(encrypted_rtsp_header_t)), boost::bind(&socket_t::handle_read_encrypted_header, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } else { sock.async_read_some( - boost::asio::buffer(begin, (std::size_t)(std::end(msg_buf) - begin)), + boost::asio::buffer(begin, (std::size_t) (std::end(msg_buf) - begin)), boost::bind( &socket_t::handle_read_plaintext, shared_from_this(), @@ -253,7 +253,7 @@ namespace rtsp_stream { } sock.async_read_some( - boost::asio::buffer(begin, (std::size_t)(std::end(msg_buf) - begin)), + boost::asio::buffer(begin, (std::size_t) (std::end(msg_buf) - begin)), boost::bind( &socket_t::handle_plaintext_payload, shared_from_this(), @@ -289,7 +289,7 @@ namespace rtsp_stream { auto end = socket->begin + bytes; msg_t req {new msg_t::element_type {}}; - if (auto status = parseRtspMessage(req.get(), socket->msg_buf.data(), (std::size_t)(end - socket->msg_buf.data()))) { + if (auto status = parseRtspMessage(req.get(), socket->msg_buf.data(), (std::size_t) (end - socket->msg_buf.data()))) { BOOST_LOG(error) << "Malformed RTSP message: ["sv << status << ']'; respond(socket->sock, *socket->session, nullptr, 400, "BAD REQUEST", 0, {}); @@ -321,7 +321,7 @@ namespace rtsp_stream { if (end - socket->crlf >= content_length) { if (end - socket->crlf > content_length) { - BOOST_LOG(warning) << "(end - socket->crlf) > content_length -- "sv << (std::size_t)(end - socket->crlf) << " > "sv << content_length; + BOOST_LOG(warning) << "(end - socket->crlf) > content_length -- "sv << (std::size_t) (end - socket->crlf) << " > "sv << content_length; } fg.disable(); diff --git a/src/utility.h b/src/utility.h index 8e4ab136..63b16433 100644 --- a/src/utility.h +++ b/src/utility.h @@ -377,7 +377,7 @@ namespace util { return (std::uint8_t) ch - '0'; } - return (std::uint8_t)(ch | (char) 32) - 'a' + (char) 10; + return (std::uint8_t) (ch | (char) 32) - 'a' + (char) 10; }; std::fill_n(buf + buf_size, padding, 0); @@ -431,7 +431,7 @@ namespace util { return (std::uint8_t) ch - '0'; } - return (std::uint8_t)(ch | (char) 32) - 'a' + (char) 10; + return (std::uint8_t) (ch | (char) 32) - 'a' + (char) 10; }; for (auto &el : buf) { @@ -509,12 +509,12 @@ namespace util { std::int64_t res {}; std::int64_t mul = 1; while (begin != --end) { - res += (std::int64_t)(*end - '0') * mul; + res += (std::int64_t) (*end - '0') * mul; mul *= 10; } - return *begin != '-' ? res + (std::int64_t)(*begin - '0') * mul : -res; + return *begin != '-' ? res + (std::int64_t) (*begin - '0') * mul : -res; } inline std::int64_t from_view(const std::string_view &number) { @@ -981,7 +981,7 @@ namespace util { template std::string_view view(It begin, It end) { - return std::string_view {(const char *) begin, (std::size_t)(end - begin)}; + return std::string_view {(const char *) begin, (std::size_t) (end - begin)}; } template diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index f6a303b8..f8d9576a 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -100,6 +100,13 @@ const validateFallbackMode = (event) => { + +
wlrCapture for wlroots based Wayland compositors via DMA-BUF. + Capture for wlroots based Wayland compositors via wlr-screencopy-unstable-v1. It is possible to capture + virtual displays in e.g. Hyprland using this method. @note{Applies to Linux only.}