Compare commits

...

84 Commits

Author SHA1 Message Date
TheElixZammuto
3888ec8da0 Merge branch 'nightly' into feat/new-session-system 2024-04-27 15:09:45 +02:00
Tejas Rao
7fb8c76590 Use C++20. (#2322) 2024-04-26 15:49:15 -04:00
TimmyOVO
9288775351 feat(macos/capture): support for capture display other than main display (#2449) 2024-04-22 14:16:26 -04:00
Cameron Gutman
067efc7912 Bump version to v0.23.1 (#2452) 2024-04-20 20:20:09 -04:00
ReenigneArcher
68c0f53bfc New Crowdin updates (#2392) 2024-04-20 16:38:27 -04:00
Hugo Locurcio
dc22e24744 Autofocus PIN input on Web UI pin page
This makes it faster to input the PIN after clicking on the PIN
notification on the desktop, since you no longer need to click
the PIN input or focus on it by pressing Tab many times.
2024-04-20 14:36:59 -05:00
ReenigneArcher
8eead6597e chore: repo updates (#2416) 2024-04-20 12:49:15 -04:00
ReenigneArcher
6c0b01737f ci(codecov): skip search (#2430) 2024-04-20 11:36:40 -04:00
dependabot[bot]
b4e6873649 build(deps): bump vue from 3.4.5 to 3.4.23 (#2434)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 23:05:44 -04:00
dependabot[bot]
24597178c7 build(deps): bump vue-i18n from 9.11.0 to 9.13.0 (#2442)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 21:41:18 -04:00
dependabot[bot]
05416bb9c2 build(deps): bump LizardByte/homebrew-release-action from 2024.409.24405 to 2024.417.220943 (#2441)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 20:34:41 -04:00
dependabot[bot]
15386f386a build(deps): bump third-party/wayland-protocols from 46f201b to 08d1c72 (#2443)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 19:33:17 -04:00
dependabot[bot]
38fa794009 build(deps): bump third-party/wlr-protocols from 4264185 to 2b8d433 (#2067)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 18:28:01 -04:00
dependabot[bot]
75fce21761 build(deps): bump packaging/linux/flatpak/deps/shared-modules from d022995 to ec91811 (#2428)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 17:26:14 -04:00
dependabot[bot]
abe256144a build(deps): bump actions-js/push from 1.4 to 1.5 (#2440)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 16:03:37 -04:00
dependabot[bot]
ad2483416d build(deps): bump packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp from 6e295e6 to 5532d43 (#2444)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 15:00:44 -04:00
Conn O'Griofa
87def6db85 fix(windows/amf): Revert RC/HRD defaults; improve documentation & config parsing (#2419) 2024-04-19 08:31:56 -04:00
Rick
9e0182be9c docs(linux): add guide for discord audio (#2447)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
2024-04-18 23:06:20 -04:00
LizardByte-bot
50a02dbce5 chore: update global workflows (#2446) 2024-04-18 15:35:56 -04:00
ReenigneArcher
69191cafe9 fix: make version update check more robust (#2437) 2024-04-18 15:35:49 -04:00
ReenigneArcher
c896dabb82 ci: skip coverage upload if not in LizardByte org (#2436) 2024-04-17 18:50:23 -04:00
ReenigneArcher
ec8170cb40 ci: fix codeql prebuild steps for unix OSes (#2431) 2024-04-16 23:00:10 -04:00
ReenigneArcher
5db8af8a3f ci: update codeql to handle multiple OSes (#2425) 2024-04-16 17:41:56 -04:00
ReenigneArcher
76d08eb883 ci: fix coverage ignore directories (#2420) 2024-04-14 13:38:01 -04:00
ReenigneArcher
fb4d4f50ec fix(ui): fix quicksync locale strings (#2418) 2024-04-14 09:34:22 -04:00
Gilles Schintgen
358bb30c3c debug: fix codec debugging code 2024-04-13 19:52:22 -05:00
Cameron Gutman
25d8e2b478 Fix discarded std::clamp() result compiler warning 2024-04-13 18:53:00 -05:00
Cameron Gutman
a4d9ee3fa4 Fix crash when receiving abs input events prior to the display touchport 2024-04-13 18:53:00 -05:00
dependabot[bot]
f87bc86b4a build(deps): bump LizardByte/homebrew-release-action from 2024.314.134529 to 2024.409.24405 (#2394)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-12 20:52:00 -04:00
Gilles Schintgen
fcd4c07bd0 Improve frametiming for linux capture (#2333) 2024-04-12 19:36:56 -04:00
hdL6c
5c1bad7155 Musl Linux fixes (#2401) 2024-04-11 22:34:26 -05:00
dependabot[bot]
d14323244e build(deps): bump codecov/codecov-action from 3 to 4 (#2297)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 20:49:52 -04:00
ReenigneArcher
6a01e58f61 build(deps): use token for codecov/codecov-action (#2404) 2024-04-10 22:28:23 -04:00
ReenigneArcher
ad66fcb243 build(linux): fix ubuntu 24.04 build (#2402) 2024-04-10 19:59:11 -04:00
ReenigneArcher
ad5b816261 fix(linux): use correct setap command in logs (#2400) 2024-04-10 18:12:03 -04:00
ReenigneArcher
7602fa110c fix(ltray): re-order tray dep search (#2397) 2024-04-09 21:40:31 -04:00
ReenigneArcher
7e26d2fd30 build(tests): ensure tests can be disabled during build (#2386) 2024-04-08 19:17:19 -04:00
ReenigneArcher
116e59292a build(deps): remove libavdevice (#2380) 2024-04-07 22:23:32 -04:00
ReenigneArcher
1a48244a0a chore: bump version to v0.23.0 (#2367) 2024-04-06 21:41:09 -04:00
ReenigneArcher
33ba03c679 New Crowdin updates (#2372) 2024-04-06 20:31:40 -04:00
Cameron Gutman
93e622342c Quote the path to sunshinesvc.exe when launching the termination helper (#2379) 2024-04-06 19:21:03 -04:00
hdL6c
7f795f0e19 fix(linux/capture): fix logical comparison of texture size (#2349)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
2024-04-06 18:03:54 -04:00
ReenigneArcher
b7aa8119f1 fix(security): ensure unpairing takes effect without restart (#2365) 2024-04-06 16:39:16 -04:00
ReenigneArcher
3c13027a61 build(linux)!: disable arm64 builds for Fedora (#2377) 2024-04-06 15:22:09 -04:00
ReenigneArcher
1e77d0a509 build(docker): increase job timeout (#2376) 2024-04-06 07:03:57 -04:00
ReenigneArcher
59ce8deb97 build(cmake): add prep/init.cmake (#2375)
Co-authored-by: James Le Cuirot <chewi@aura-online.co.uk>
2024-04-05 22:48:13 -04:00
ReenigneArcher
1ed22ab3b2 build: fix ubuntu 24.04 deps (#2374) 2024-04-05 21:45:15 -04:00
dependabot[bot]
42b4192a04 build(deps): bump vue-i18n from 9.10.2 to 9.11.0 (#2361)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 16:20:37 -04:00
ReenigneArcher
4fe3848d32 New Crowdin updates (#2290) 2024-04-05 13:21:13 -04:00
ReenigneArcher
89207a13f2 fix(docker): fix arm64 builds (#2368) 2024-04-04 23:12:17 -04:00
ReenigneArcher
cd465652f0 ci(macos): do not always run tests for macports build (#2360) 2024-04-04 09:36:40 -04:00
ReenigneArcher
699b2c160e build(linux)!: drop ubuntu 20.04 (#2327) 2024-04-03 22:52:37 -04:00
ReenigneArcher
e1588787f0 build(linux): add ubuntu 24.04 (#2326) 2024-04-03 21:50:03 -04:00
dependabot[bot]
73fe31dbd2 build(deps): bump @fortawesome/fontawesome-free from 6.5.1 to 6.5.2 (#2356)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-03 17:17:16 -04:00
ReenigneArcher
2da6fb050a fix(logging): add logging namespace and create logging::init method (#2336) 2024-04-02 20:57:57 -04:00
ReenigneArcher
a1edc246f5 fix(i18n): use correct key for welcome_success (#2354) 2024-04-02 18:22:30 -04:00
dependabot[bot]
bb7c2d50ef build(deps): bump third-party/build-deps from 6e23b58 to efd3a38 (#2346)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 08:56:31 -04:00
ReenigneArcher
6d1805b3fa docs(readme): use csv tables (#2341) 2024-03-31 15:44:45 -04:00
ReenigneArcher
9020c2c229 build(cmake): build web-ui target with cmd instead of bash on windows (#2340) 2024-03-31 13:00:01 -04:00
ReenigneArcher
b4739b05c6 build(cmake): copy asset files to build dir (#2338) 2024-03-31 11:52:25 -04:00
Zane Chua
1329c510b1 docs: update curl flag for macos portfile installation (#2337) 2024-03-31 09:19:11 -04:00
Lukas Senionis
991fab9370 Add mising _win postfix (#2330) 2024-03-30 13:52:37 -04:00
ReenigneArcher
376a2822bd fix(ui): could not submit pin due to localization updates (#2324)
Co-authored-by: Elia Zammuto <theelixzammuto@gmail.com>
2024-03-30 11:35:29 -04:00
ReenigneArcher
2b059c6797 fix(i18n): update localization docs and js strings (#2325) 2024-03-30 10:01:48 -04:00
Conn O'Griofa
ae71a6ad83 AMF: rate control improvements (#2251) 2024-03-29 21:07:24 -04:00
ReenigneArcher
2af0ce364d Revert "Add capture using WinRT Windows.Graphics.Capture API." (#2320) 2024-03-29 12:43:44 -04:00
ReenigneArcher
3b6a59af05 ci(macos): automatically determine default branch for homebrew formula (#2317) 2024-03-28 19:18:49 -04:00
KuleRucket
526121d81d Populate host latency for kms/x11 grab (#2273) 2024-03-28 17:52:53 -04:00
dependabot[bot]
e5ef0375f3 build(deps): bump packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp from 644487f to 6e295e6 (#2309)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-28 16:29:32 -04:00
ReenigneArcher
de97eacd30 ci(codecov): update codecov default branch (#2313) 2024-03-28 11:06:43 -04:00
lns103
1bd75bbeb0 Correct typo in Chinese Simplified locale option (#2308) 2024-03-28 09:32:55 -04:00
Tejas Rao
8f1692a5ac Add capture using WinRT Windows.Graphics.Capture API. (#2149)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
2024-03-27 23:47:26 -04:00
dependabot[bot]
8eb3ea4fa3 build(deps): bump rstcheck[sphinx] from 6.2.0 to 6.2.1 (#2299)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-27 08:32:42 -04:00
ReenigneArcher
13aea3cbd8 ci(windows): update devcon (#2296) 2024-03-26 22:47:29 -04:00
ReenigneArcher
89e8b9628c ci(tests): add test framework (#1603) 2024-03-24 19:52:24 -04:00
ReenigneArcher
934f81182a fix(i18n): match two__letters code to crowdin (#2289) 2024-03-22 21:33:56 -04:00
ReenigneArcher
87774333f3 feat(i18n): add ui localization (#2279)
Co-authored-by: TheElixZammuto <6505622+TheElixZammuto@users.noreply.github.com>
2024-03-22 19:54:12 -04:00
Elia Zammuto
8ba64ffa32 Made C++ format Happy 2024-03-17 16:33:30 +01:00
Elia Zammuto
968b7963ee Migrated jwt-cpp to stable release, style fixes 2024-03-17 16:33:30 +01:00
Elia Zammuto
61df838356 clang-format 2024-03-17 16:33:30 +01:00
Elia Zammuto
d1845df0ea Logout 2024-03-17 16:33:30 +01:00
Elia Zammuto
9ef63ca829 First Working Draft of JWT Login System 2024-03-17 16:33:30 +01:00
ReenigneArcher
8316f44e10 ci(linux): refactor linux build (#2275) 2024-03-17 00:07:18 -04:00
ReenigneArcher
7534fa1023 refactor(video): move encoder declarations to header (#2185) 2024-03-16 09:04:29 -04:00
145 changed files with 8741 additions and 1730 deletions

View File

@@ -1,4 +1,5 @@
# install dependencies for C++ analysis
set -e
sudo apt-get update -y
sudo apt-get install -y \
@@ -54,3 +55,12 @@ sudo wget \
sudo chmod a+x /root/cuda.run
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr --no-opengl-libs --no-man-page --no-drm
sudo rm /root/cuda.run
# build
mkdir -p build
cd build || exit 1
cmake -G "Unix Makefiles" ..
make -j"$(nproc)"
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,34 @@
# install dependencies for C++ analysis
set -e
# update pacman
pacman --noconfirm -Suy
# install dependencies
pacman --noconfirm -S \
base-devel \
diffutils \
gcc \
git \
make \
mingw-w64-x86_64-binutils \
mingw-w64-x86_64-boost \
mingw-w64-x86_64-cmake \
mingw-w64-x86_64-curl \
mingw-w64-x86_64-miniupnpc \
mingw-w64-x86_64-nlohmann-json \
mingw-w64-x86_64-nodejs \
mingw-w64-x86_64-onevpl \
mingw-w64-x86_64-openssl \
mingw-w64-x86_64-opus \
mingw-w64-x86_64-rust \
mingw-w64-x86_64-toolchain
# build
mkdir -p build
cd build || exit 1
cmake -G "MinGW Makefiles" ..
mingw32-make -j"$(nproc)"
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,20 @@
# install dependencies for C++ analysis
set -e
# install dependencies
brew install \
boost \
cmake \
miniupnpc \
node \
opus \
pkg-config
# build
mkdir -p build
cd build || exit 1
cmake -G "Unix Makefiles" ..
make -j"$(sysctl -n hw.logicalcpu)"
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@@ -4,8 +4,11 @@
# do not ignore .git, needed for versioning
!/.git
# do not ignore .rstcheck.cfg, needed to test building docs
!/.rstcheck.cfg
# ignore repo directories and files
docs/
docker/
gh-pages-template/
scripts/
tools/
@@ -13,6 +16,7 @@ crowdin.yml
# ignore dev directories
build/
cmake-*/
venv/
# ignore artifacts

View File

@@ -165,16 +165,12 @@ jobs:
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'false'
remove-docker-images: 'true'
- name: Checkout
uses: actions/checkout@v4
- name: Checkout Flathub Shared Modules
uses: actions/checkout@v4
with:
repository: flathub/shared-modules
path: build/shared-modules
submodules: recursive
- name: Setup Dependencies Linux Flatpak
run: |
@@ -185,8 +181,10 @@ jobs:
cmake \
flatpak \
qemu-user-static
sudo su $(whoami) -c "flatpak --user remote-add --if-not-exists flathub \
https://flathub.org/repo/flathub.flatpakrepo"
sudo su $(whoami) -c "flatpak --user install -y flathub \
org.flatpak.Builder \
org.freedesktop.Platform/${{ matrix.arch }}/${PLATFORM_VERSION} \
@@ -213,6 +211,7 @@ jobs:
then
echo "This is a PUSH event"
branch=${{ github.ref_name }}
build_version=${{ needs.check_changelog.outputs.next_version }}
commit=${{ github.sha }}
clone_url=${{ github.event.repository.clone_url }}
else
@@ -229,6 +228,7 @@ jobs:
cd build
cmake -DGITHUB_CLONE_URL=${clone_url} \
-DBUILD_VERSION=${build_version} \
-DGITHUB_BRANCH=${branch} \
-DGITHUB_COMMIT=${commit} \
-DSUNSHINE_CONFIGURE_FLATPAK_MAN=ON \
@@ -280,7 +280,7 @@ jobs:
include: # package these differently
- type: AppImage
EXTRA_ARGS: '-DSUNSHINE_BUILD_APPIMAGE=ON'
dist: 20.04
dist: 22.04
steps:
- name: Maximize build space
@@ -291,65 +291,57 @@ jobs:
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'false'
remove-docker-images: 'true'
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Dependencies Linux
- name: Install wget
run: |
sudo apt-get update -y
sudo apt-get install -y \
wget
- name: Install CUDA
env:
CUDA_VERSION: 11.8.0
CUDA_BUILD: 520.61.05
timeout-minutes: 4
run: |
url_base="https://developer.download.nvidia.com/compute/cuda/${CUDA_VERSION}/local_installers"
url="${url_base}/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux.run"
sudo wget -q -O /root/cuda.run ${url}
sudo chmod a+x /root/cuda.run
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr/local/cuda --no-opengl-libs --no-man-page --no-drm
sudo rm /root/cuda.run
- name: Setup Dependencies Linux
timeout-minutes: 5
run: |
# allow newer gcc
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
if [[ ${{ matrix.dist }} == "18.04" ]]; then
# Ubuntu 18.04 packages
sudo add-apt-repository ppa:savoury1/boost-defaults-1.71 -y
sudo apt-get update -y
sudo apt-get install -y \
libboost-filesystem1.71-dev \
libboost-locale1.71-dev \
libboost-log1.71-dev \
libboost-regex1.71-dev \
libboost-program-options1.71-dev
# Install cmake
wget https://cmake.org/files/v3.22/cmake-3.22.2-linux-x86_64.sh
chmod +x cmake-3.22.2-linux-x86_64.sh
mkdir /opt/cmake
./cmake-3.22.2-linux-x86_64.sh --prefix=/opt/cmake --skip-license
ln --force --symbolic /opt/cmake/bin/cmake /usr/local/bin/cmake
cmake --version
# install newer tar from focal... appimagelint fails on 18.04 without this
echo "original tar version"
tar --version
wget -O tar.deb http://security.ubuntu.com/ubuntu/pool/main/t/tar/tar_1.30+dfsg-7ubuntu0.20.04.3_amd64.deb
sudo apt-get -y install -f ./tar.deb
echo "new tar version"
tar --version
else
# Ubuntu 20.04+ packages
sudo apt-get update -y
sudo apt-get install -y \
cmake \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-log-dev \
libboost-program-options-dev
fi
# allow libfuse2 for appimage on 22.04
sudo add-apt-repository universe
sudo apt-get install -y \
build-essential \
cmake \
gcc-10 \
g++-10 \
libayatana-appindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-log-dev \
libboost-program-options-dev \
libcap-dev \
libcurl4-openssl-dev \
libdrm-dev \
libevdev-dev \
libfuse2 \
libminiupnpc-dev \
libmfx-dev \
libnotify-dev \
@@ -367,7 +359,7 @@ jobs:
libxfixes-dev \
libxrandr-dev \
libxtst-dev \
wget
python3
# clean apt cache
sudo apt-get clean
@@ -382,20 +374,21 @@ jobs:
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
# Install CUDA
sudo wget \
https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run \
--progress=bar:force:noscroll -q --show-progress -O /root/cuda.run
sudo chmod a+x /root/cuda.run
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr --no-opengl-libs --no-man-page --no-drm
sudo rm /root/cuda.run
- name: Setup python
id: python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build Linux
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version }}
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
timeout-minutes: 5
run: |
echo "nproc: $(nproc)"
mkdir -p build
mkdir -p artifacts
@@ -403,6 +396,7 @@ jobs:
cmake \
-DBUILD_WERROR=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CUDA_COMPILER:PATH=/usr/local/cuda/bin/nvcc \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
@@ -412,20 +406,7 @@ jobs:
-DSUNSHINE_ENABLE_CUDA=ON \
${{ matrix.EXTRA_ARGS }} \
..
make -j ${nproc}
- name: Package Linux - CPACK
# todo - this is no longer used
if: ${{ matrix.type == 'cpack' }}
working-directory: build
run: |
cpack -G DEB
mv ./cpack_artifacts/Sunshine.deb ../artifacts/sunshine-${{ matrix.dist }}.deb
if [[ ${{ matrix.dist }} == "20.04" ]]; then
cpack -G RPM
mv ./cpack_artifacts/Sunshine.rpm ../artifacts/sunshine.rpm
fi
make -j $(expr $(nproc) - 1) # use all but one core
- name: Set AppImage Version
if: |
@@ -452,12 +433,12 @@ jobs:
# AppImage
# https://docs.appimage.org/packaging-guide/index.html
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
chmod +x linuxdeploy-x86_64.AppImage
# https://github.com/linuxdeploy/linuxdeploy-plugin-gtk
sudo apt-get install libgtk-3-dev librsvg2-dev -y
wget https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
wget -q https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
chmod +x linuxdeploy-plugin-gtk.sh
export DEPLOY_GTK_VERSION=3
@@ -475,14 +456,17 @@ jobs:
# permissions
chmod +x ../artifacts/sunshine.AppImage
- name: Delete cuda
# free up space on the runner
run: |
sudo rm -rf /usr/local/cuda
- name: Verify AppImage
if: ${{ matrix.type == 'AppImage' }}
run: |
wget https://github.com/TheAssassin/appimagelint/releases/download/continuous/appimagelint-x86_64.AppImage
chmod +x appimagelint-x86_64.AppImage
# rm -rf ~/.cache/appimagelint/
./appimagelint-x86_64.AppImage ./artifacts/sunshine.AppImage
- name: Upload Artifacts
@@ -491,6 +475,56 @@ jobs:
name: sunshine-linux-${{ matrix.type }}-${{ matrix.dist }}
path: artifacts/
- name: Install test deps
run: |
sudo apt-get update -y
sudo apt-get install -y \
doxygen \
graphviz \
python3-venv \
x11-xserver-utils \
xvfb
# clean apt cache
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
- name: Run tests
id: test
working-directory: build/tests
run: |
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
- name: Generate gcov report
# any except canceled or skipped
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
id: test_report
working-directory: build
run: |
${{ steps.python.outputs.python-path }} -m pip install gcovr
${{ steps.python.outputs.python-path }} -m gcovr -r .. \
--exclude '.*tests/.*' \
--exclude '.*tests/.*' \
--xml-pretty \
-o coverage.xml
- name: Upload coverage
# any except canceled or skipped
if: >-
always() &&
(steps.test_report.outcome == 'success') &&
startsWith(github.repository, 'LizardByte/')
uses: codecov/codecov-action@v4
with:
disable_search: true
fail_ci_if_error: true
files: ./build/coverage.xml
flags: ${{ runner.os }}
token: ${{ secrets.CODECOV_TOKEN }}
- name: Create/Update GitHub Release
if: ${{ needs.setup_release.outputs.create_release == 'true' }}
uses: ncipollo/release-action@v1
@@ -538,12 +572,17 @@ jobs:
if [ -z "$branch" ]
then
echo "This is a PUSH event"
build_version=${{ needs.check_changelog.outputs.next_version }}
clone_url=${{ github.event.repository.clone_url }}
branch="${{ github.ref_name }}"
commit=${{ github.sha }}
default_branch="${{ github.event.repository.default_branch }}"
else
echo "This is a PR event"
clone_url=${{ github.event.pull_request.head.repo.clone_url }}
branch="${{ github.event.pull_request.head.ref }}"
commit=${{ github.event.pull_request.head.sha }}
default_branch="${{ github.event.pull_request.head.repo.default_branch }}"
fi
echo "Branch: ${branch}"
echo "Clone URL: ${clone_url}"
@@ -551,8 +590,11 @@ jobs:
mkdir build
cd build
cmake \
-DBUILD_VERSION="${build_version}" \
-DGITHUB_BRANCH="${branch}" \
-DGITHUB_COMMIT="${commit}" \
-DGITHUB_CLONE_URL="${clone_url}" \
-DGITHUB_DEFAULT_BRANCH="${default_branch}" \
-DSUNSHINE_CONFIGURE_HOMEBREW=ON \
-DSUNSHINE_CONFIGURE_ONLY=ON \
..
@@ -588,7 +630,7 @@ jobs:
echo "publish=${PUBLISH}" >> $GITHUB_OUTPUT
- name: Validate and Publish Homebrew Formula
uses: LizardByte/homebrew-release-action@v2024.314.134529
uses: LizardByte/homebrew-release-action@v2024.417.220943
with:
formula_file: ${{ github.workspace }}/homebrew/sunshine.rb
git_email: ${{ secrets.GH_BOT_EMAIL }}
@@ -633,6 +675,12 @@ jobs:
# install dependencies using homebrew
brew install cmake
- name: Setup python
id: python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Configure Portfile
run: |
# variables for Portfile
@@ -642,6 +690,8 @@ jobs:
if [ -z "$branch" ]
then
echo "This is a PUSH event"
branch="${{ github.ref_name }}"
build_version=${{ needs.check_changelog.outputs.next_version }}
commit=${{ github.sha }}
clone_url=${{ github.event.repository.clone_url }}
else
@@ -655,6 +705,8 @@ jobs:
mkdir build
cd build
cmake \
-DBUILD_VERSION=${build_version} \
-DGITHUB_BRANCH=${branch} \
-DGITHUB_COMMIT=${commit} \
-DGITHUB_CLONE_URL=${clone_url} \
-DSUNSHINE_CONFIGURE_PORTFILE=ON \
@@ -690,6 +742,7 @@ jobs:
- name: Build port
env:
subportlist: ${{ steps.subportlist.outputs.subportlist }}
id: build
run: |
subport="Sunshine"
@@ -711,6 +764,13 @@ jobs:
"$subport"
echo "::endgroup::"
- name: Build Logs
if: always()
run: |
logfile="/opt/local/var/macports/logs/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/main.log"
cat "$logfile"
sudo mv "${logfile}" "${logfile}.bak"
- name: Upload Artifacts
if: ${{ matrix.release }}
uses: actions/upload-artifact@v4
@@ -718,6 +778,91 @@ jobs:
name: sunshine-macports
path: artifacts/
- name: Fix screen capture permissions
if: ${{ matrix.os_version != 12 }} # macOS-12 is okay
# can be removed if the following is fixed in the runner image
# https://github.com/actions/runner-images/issues/9529
# https://github.com/actions/runner-images/pull/9530
run: |
# https://apple.stackexchange.com/questions/362865/macos-list-apps-authorized-for-full-disk-access
# permissions for screen capture
values="'kTCCServiceScreenCapture','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
if [[ "${{ matrix.os_version }}" == "14" ]]; then
# TCC access table in Sonoma has extra 4 columns: pid, pid_version, boot_uuid, last_reminded
values="${values},NULL,NULL,'UNUSED',${values##*,}"
fi
# system and user databases
dbPaths=(
"/Library/Application Support/com.apple.TCC/TCC.db"
"$HOME/Library/Application Support/com.apple.TCC/TCC.db"
)
sqlQuery="INSERT OR IGNORE INTO access VALUES($values);"
for dbPath in "${dbPaths[@]}"; do
echo "Column names for $dbPath"
echo "-------------------"
sudo sqlite3 "$dbPath" "PRAGMA table_info(access);"
echo "Current permissions for $dbPath"
echo "-------------------"
sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';"
sudo sqlite3 "$dbPath" "$sqlQuery"
echo "Updated permissions for $dbPath"
echo "-------------------"
sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';"
done
- name: Run tests
id: test
timeout-minutes: 10
run: |
sudo port test "Sunshine"
- name: Test Logs
if: always()
run: |
logfile="/opt/local/var/macports/logs/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/main.log"
cat "$logfile"
- name: Generate gcov report
# any except canceled or skipped
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
id: test_report
working-directory:
/opt/local/var/macports/build/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/work
run: |
base_dir=$(pwd)
build_dir=${base_dir}/build
# get the directory name that starts with Sunshine-*
dir=$(ls -d Sunshine-*)
cd ${build_dir}
${{ steps.python.outputs.python-path }} -m pip install gcovr
sudo ${{ steps.python.outputs.python-path }} -m gcovr -r ../${dir} \
--exclude '.*${dir}/tests/.*' \
--exclude '.*${dir}/third-party/.*' \
--gcov-object-directory $(pwd) \
--verbose \
--xml-pretty \
-o ${{ github.workspace }}/build/coverage.xml
- name: Upload coverage
# any except canceled or skipped
if: >-
always() &&
(steps.test_report.outcome == 'success') &&
startsWith(github.repository, 'LizardByte/')
uses: codecov/codecov-action@v4
with:
disable_search: true
fail_ci_if_error: false # todo: re-enable this when action is fixed
files: ./build/coverage.xml
flags: ${{ runner.os }}-${{ matrix.os_version }}
token: ${{ secrets.CODECOV_TOKEN }}
- name: Create/Update GitHub Release
if: ${{ needs.setup_release.outputs.create_release == 'true' && matrix.release }}
uses: ncipollo/release-action@v1
@@ -743,6 +888,110 @@ jobs:
with:
submodules: recursive
- name: Prepare tests
id: prepare-tests
if: false # todo: DirectX11 is not available, so even software encoder fails
run: |
# function to download and extract a zip file
function DownloadAndExtract {
param (
[string]$Uri,
[string]$OutFile
)
$maxRetries = 5
$retryCount = 0
$success = $false
while (-not $success -and $retryCount -lt $maxRetries) {
$retryCount++
Write-Host "Downloading $Uri to $OutFile, attempt $retryCount of $maxRetries"
try {
Invoke-WebRequest -Uri $Uri -OutFile $OutFile
$success = $true
} catch {
Write-Host "Attempt $retryCount of $maxRetries failed with error: $($_.Exception.Message). Retrying..."
Start-Sleep -Seconds 5
}
}
if (-not $success) {
Write-Host "Failed to download the file after $maxRetries attempts."
exit 1
}
# use .NET to get the base name of the file
$baseName = (Get-Item $OutFile).BaseName
# Extract the zip file
Expand-Archive -Path $OutFile -DestinationPath $baseName
}
# virtual display driver
DownloadAndExtract `
-Uri "https://www.amyuni.com/downloads/usbmmidd_v2.zip" `
-OutFile "usbmmidd_v2.zip"
# install
Set-Location -Path usbmmidd_v2/usbmmidd_v2
./deviceinstaller64 install usbmmidd.inf usbmmidd
# create the virtual display
./deviceinstaller64 enableidd 1
# move up a directory
Set-Location -Path ../..
# install devcon
DownloadAndExtract `
-Uri "https://github.com/Drawbackz/DevCon-Installer/releases/download/1.4-rc/Devcon.Installer.zip" `
-OutFile "Devcon.Installer.zip"
Set-Location -Path Devcon.Installer
# hash needs to match OS version
# https://github.com/Drawbackz/DevCon-Installer/blob/master/devcon_sources.json
Start-Process -FilePath "./Devcon Installer.exe" -Wait -ArgumentList `
'install', `
'-hash', '54004C83EE34F6A55380528A8B29F4C400E61FBB947A19E0AB9E5A193D7D961E', `
'-addpath', `
'-update', `
'-dir', 'C:\Windows\System32'
# disable Hyper-V Video
# https://stackoverflow.com/a/59490940
C:\Windows\System32\devcon.exe disable "VMBUS\{da0a7802-e377-4aac-8e77-0558eb1073f8}"
# move up a directory
Set-Location -Path ..
# multi monitor tool
DownloadAndExtract `
-Uri "http://www.nirsoft.net/utils/multimonitortool-x64.zip" `
-OutFile "multimonitortool.zip"
# enable the virtual display
# http://www.nirsoft.net/utils/multi_monitor_tool.html
Set-Location -Path multimonitortool
# Original Hyper-V is \\.\DISPLAY1, it will recreate itself as \\.\DISPLAY6 (or something higher than 2)
# USB Mobile Monitor Virtual Display is \\.\DISPLAY2
# these don't seem to work if not using runAs
# todo: do they work if not using runAs?
Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /enable \\.\DISPLAY2'
Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /SetPrimary \\.\DISPLAY2'
# wait a few seconds
Start-Sleep -s 5
# list monitors
./MultiMonitorTool.exe /stext monitor_list.txt
# wait a few seconds
Start-Sleep -s 5
# print the monitor list
Get-Content -Path monitor_list.txt
- name: Setup Dependencies Windows
uses: msys2/setup-msys2@v2
with:
@@ -750,12 +999,14 @@ jobs:
install: >-
base-devel
diffutils
doxygen
git
make
mingw-w64-x86_64-binutils
mingw-w64-x86_64-boost
mingw-w64-x86_64-cmake
mingw-w64-x86_64-curl
mingw-w64-x86_64-graphviz
mingw-w64-x86_64-miniupnpc
mingw-w64-x86_64-nlohmann-json
mingw-w64-x86_64-nodejs
@@ -768,11 +1019,29 @@ jobs:
wget
yasm
- name: Setup python
# use this instead of msys2 python due to known issues using wheels, https://www.msys2.org/docs/python/
id: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Python Path
id: python-path
shell: msys2 {0}
run: |
# replace backslashes with double backslashes
python_path=$(echo "${{ steps.setup-python.outputs.python-path }}" | sed 's/\\/\\\\/g')
# step output
echo "python-path=${python_path}"
echo "python-path=${python_path}" >> $GITHUB_OUTPUT
- name: Build Windows
shell: msys2 {0}
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version }}
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
mkdir build
@@ -781,6 +1050,8 @@ jobs:
-DBUILD_WERROR=ON \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DSUNSHINE_ASSETS_DIR=assets \
-DTESTS_PYTHON_EXECUTABLE='${{ steps.python-path.outputs.python-path }}' \
-DTESTS_SOFTWARE_ENCODER_UNAVAILABLE='skip' \
-G "MinGW Makefiles" \
..
mingw32-make -j$(nproc)
@@ -799,6 +1070,41 @@ jobs:
mv ./cpack_artifacts/Sunshine.exe ../artifacts/sunshine-windows-installer.exe
mv ./cpack_artifacts/Sunshine.zip ../artifacts/sunshine-windows-portable.zip
- name: Run tests
id: test
shell: msys2 {0}
working-directory: build/tests
run: |
./test_sunshine.exe --gtest_color=yes
- name: Generate gcov report
# any except canceled or skipped
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
id: test_report
shell: msys2 {0}
working-directory: build
run: |
${{ steps.python-path.outputs.python-path }} -m pip install gcovr
${{ steps.python-path.outputs.python-path }} -m gcovr -r .. \
--exclude '.*tests/.*' \
--exclude '.*tests/.*' \
--xml-pretty \
-o coverage.xml
- name: Upload coverage
# any except canceled or skipped
if: >-
always() &&
(steps.test_report.outcome == 'success') &&
startsWith(github.repository, 'LizardByte/')
uses: codecov/codecov-action@v4
with:
disable_search: true
fail_ci_if_error: true
files: ./build/coverage.xml
flags: ${{ runner.os }}
token: ${{ secrets.CODECOV_TOKEN }}
- name: Package Windows Debug Info
working-directory: build
run: |

View File

@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Autoapproving
uses: hmarr/auto-approve-action@v3
uses: hmarr/auto-approve-action@v4
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
@@ -49,7 +49,7 @@ jobs:
steps:
- name: Automerging
uses: pascalgn/automerge-action@v0.15.6
uses: pascalgn/automerge-action@v0.16.3
env:
BASE_BRANCHES: nightly
GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }}

View File

@@ -16,7 +16,7 @@ on:
- cron: '00 12 * * 0' # every Sunday at 12:00 UTC
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
@@ -57,10 +57,25 @@ jobs:
console.log(`Remapping language: ${key} to ${remap_languages[key.toLowerCase()]}`)
key = remap_languages[key.toLowerCase()]
}
if (supported_languages.includes(key.toLowerCase()) &&
!matrix['include'].includes({"language": key.toLowerCase()})) {
if (supported_languages.includes(key.toLowerCase())) {
console.log(`Found supported language: ${key}`)
matrix['include'].push({"language": key.toLowerCase()})
let osList = ['ubuntu-latest'];
if (key.toLowerCase() === 'swift') {
osList = ['macos-latest'];
} else if (key.toLowerCase() === 'cpp') {
osList = ['macos-latest', 'ubuntu-latest', 'windows-latest'];
}
for (let os of osList) {
// set name for matrix
if (osList.length == 1) {
name = key.toLowerCase()
} else {
name = `${key.toLowerCase()}, ${os}`
}
// add to matrix
matrix['include'].push({"language": key.toLowerCase(), "os": os, "name": name})
}
}
}
@@ -84,10 +99,15 @@ jobs:
}
analyze:
name: Analyze
name: Analyze (${{ matrix.name }})
if: ${{ needs.languages.outputs.continue == 'true' }}
defaults:
run:
shell: ${{ matrix.os == 'windows-latest' && 'msys2 {0}' || 'bash' }}
env:
GITHUB_CODEQL_BUILD: true
needs: [languages]
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
@@ -100,6 +120,7 @@ jobs:
steps:
- name: Maximize build space
if: runner.os == 'Linux'
uses: easimon/maximize-build-space@v8
with:
root-reserve-mb: 20480
@@ -114,6 +135,12 @@ jobs:
with:
submodules: recursive
- name: Setup msys2
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
update: true
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
@@ -129,16 +156,20 @@ jobs:
# Pre autobuild
# create a file named .codeql-prebuild-${{ matrix.language }}.sh in the root of your repository
# create a file named .codeql-build-${{ matrix.language }}.sh in the root of your repository
- name: Prebuild
id: prebuild
run: |
# check if .qodeql-prebuild-${{ matrix.language }}.sh exists
if [ -f "./.codeql-prebuild-${{ matrix.language }}.sh" ]; then
echo "Running .codeql-prebuild-${{ matrix.language }}.sh"
./.codeql-prebuild-${{ matrix.language }}.sh
# check if prebuild script exists
filename=".codeql-prebuild-${{ matrix.language }}-${{ runner.os }}.sh"
if [ -f "./${filename}" ]; then
echo "Running prebuild script: ${filename}"
./${filename}
fi
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
- name: Autobuild
if: steps.prebuild.outputs.skip_autobuild != 'true'
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis

View File

@@ -55,7 +55,7 @@ jobs:
- name: Clang format lint
if: ${{ steps.find_files.outputs.found_files }}
uses: DoozyX/clang-format-lint-action@v0.16.2
uses: DoozyX/clang-format-lint-action@v0.17
with:
source: ${{ steps.find_files.outputs.found_files }}
extensions: 'cpp,h,m,mm'

View File

@@ -51,7 +51,7 @@ jobs:
if: >-
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_dispatch')
uses: actions-js/push@v1.4
uses: actions-js/push@v1.5
with:
github_token: ${{ secrets.GH_BOT_TOKEN }}
author_email: ${{ secrets.GH_BOT_EMAIL }}

50
.gitignore vendored
View File

@@ -1,13 +1,45 @@
build
cmake-build*
.DS_Store
.vscode
.vs
*.swp
*.kdev4
# Prerequisites
*.d
.cache
.idea
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# JetBrains IDE
.idea/
# VSCode IDE
.vscode/
# build directories
build/
cmake-*/
# npm
node_modules/

16
.gitmodules vendored
View File

@@ -1,7 +1,23 @@
[submodule "packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp"]
path = packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp
url = https://github.com/flathub/org.flatpak.Builder.BaseApp
branch = branch/23.08
[submodule "packaging/linux/flatpak/deps/shared-modules"]
path = packaging/linux/flatpak/deps/shared-modules
url = https://github.com/flathub/shared-modules
branch = master
[submodule "third-party/build-deps"]
path = third-party/build-deps
url = https://github.com/LizardByte/build-deps.git
branch = dist
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest/
branch = v1.14.x
[submodule "third-party/jwt-cpp"]
path = third-party/jwt-cpp
url = https://github.com/Thalhammer/jwt-cpp.git
branch = master
[submodule "third-party/moonlight-common-c"]
path = third-party/moonlight-common-c
url = https://github.com/moonlight-stream/moonlight-common-c.git

View File

@@ -1,5 +1,59 @@
# Changelog
## [0.23.1] - 2024-04-20
**Fixed**
- (Capture/Windows) Disable HRD and CBR encoding options by default for AMD GPUs due to video quality regressions in v0.23.0
- (UI) Fix incorrect strings for QuickSync 'fast' and 'faster' presets
- (UI/Linux) Fix update prompt appearing even when running the latest version
- (Input) Fix crash when absolute input events are received prior to the display viewport being set
- (Input/Linux) Fix missing clamping of rumble intensity to valid range
- (Build/Tests) Fix error when attempting to disable compilation of tests
- (Build/Linux) Fix some compilation errors when using Musl libc
- (Logging) Fix broken debug messages for codec capability flags
- (Logging/Linux) Fix log messages to include the correct setcap command for resolving KMS permission errors
**Added**
- (Capture/Linux) Improve frame time consistency for all capture backends
- (UI) Set focus to the PIN textbox when navigating to the PIN tab
**Dependencies**
- Remove libavdevice dependency
**Misc**
- (Linux) Prefer ayatana-appindicator3 over appindicator3 if both are available
## [0.23.0] - 2024-04-06
Attention, this release contains critical security fixes. Please update as soon as possible.
**Breaking**
- (Linux) Drop support for Ubuntu 20.04
- (Linux) No longer provide arm64 rpm packages, due to extreme compile time on GitHub hosted runners
**Fixed**
- (Network) Ensure unpairing takes effect without restart
- (Capture/Linux) Fix logical comparison of texture size
- (Service/Windows) Quote the path to sunshinesvc.exe when launching the termination helper
**Added**
- (WebUI) Localization support
- (Capture/Linux) Populate host latency for kmx/x11 grab
- (Capture/Windows) AMF rate control improvements
- (Linux) Add support for Ubuntu 24.04 (x86_64 only)
**Dependencies**
- Bump rstcheck from 6.2.0 to 6.2.1
- Bump org.flatpak.Builder.BaseApp from 644487f to 6e295e6
- Bump ffmpeg
- Bump @fortawesome/fontawesome-free from 6.5.1 to 6.5.2
**Misc**
- (Style) Refactored video encoder declarations
- (CI) Refactored Linux build in CI
- (CI) Added unit testing and code coverage
- (Docs/macOS) Update curl command for Portfile install
- (Style) Refactor logging initialization
## [0.22.2] - 2024-03-15
**Fixed**
- (Tray/Windows) Fix broken system tray icon on some systems
@@ -766,3 +820,5 @@ settings. In v0.17.0, games now run under your user account without elevated pri
[0.22.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.0
[0.22.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.1
[0.22.2]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.2
[0.23.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.23.0
[0.23.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.23.1

View File

@@ -1,9 +1,10 @@
cmake_minimum_required(VERSION 3.18)
# `CMAKE_CUDA_ARCHITECTURES` requires 3.18
# set_source_files_properties requires 3.18
# todo - set this conditionally
# todo - set version to 0.0.0 once confident in automated versioning
project(Sunshine VERSION 0.22.2
project(Sunshine VERSION 0.23.1
DESCRIPTION "Self-hosted game stream host for Moonlight"
HOMEPAGE_URL "https://app.lizardbyte.dev/Sunshine")
@@ -28,6 +29,9 @@ include(${CMAKE_MODULE_PATH}/prep/build_version.cmake)
# cmake build flags
include(${CMAKE_MODULE_PATH}/prep/options.cmake)
# initial prep
include(${CMAKE_MODULE_PATH}/prep/init.cmake)
# configure special package files, such as sunshine.desktop, Flatpak manifest, Portfile , etc.
include(${CMAKE_MODULE_PATH}/prep/special_package_configuration.cmake)

View File

@@ -17,69 +17,48 @@ System Requirements
**Minimum Requirements**
+------------+------------------------------------------------------------+
| GPU | AMD: VCE 1.0 or higher, see `obs-amd hardware support`_ |
| +------------------------------------------------------------+
| | Intel: VAAPI-compatible, see: `VAAPI hardware support`_ |
| +------------------------------------------------------------+
| | Nvidia: NVENC enabled cards, see `nvenc support matrix`_ |
+------------+------------------------------------------------------------+
| CPU | AMD: Ryzen 3 or higher |
| +------------------------------------------------------------+
| | Intel: Core i3 or higher |
+------------+------------------------------------------------------------+
| RAM | 4GB or more |
+------------+------------------------------------------------------------+
| OS | Windows: 10+ (Windows Server not supported) |
| +------------------------------------------------------------+
| | macOS: 12+ |
| +------------------------------------------------------------+
| | Linux/Debian: 11 (bullseye) |
| +------------------------------------------------------------+
| | Linux/Fedora: 38+ |
| +------------------------------------------------------------+
| | Linux/Ubuntu: 20.04+ (focal) |
+------------+------------------------------------------------------------+
| Network | Host: 5GHz, 802.11ac |
| +------------------------------------------------------------+
| | Client: 5GHz, 802.11ac |
+------------+------------------------------------------------------------+
.. csv-table::
:widths: 15, 60
"GPU", "AMD: VCE 1.0 or higher, see: `obs-amd hardware support <https://github.com/obsproject/obs-amd-encoder/wiki/Hardware-Support>`_"
"", "Intel: VAAPI-compatible, see: `VAAPI hardware support <https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html>`_"
"", "Nvidia: NVENC enabled cards, see: `nvenc support matrix <https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new>`_"
"CPU", "AMD: Ryzen 3 or higher"
"", "Intel: Core i3 or higher"
"RAM", "4GB or more"
"OS", "Windows: 10+ (Windows Server does not support virtual gamepads)"
"", "macOS: 12+"
"", "Linux/Debian: 11 (bullseye)"
"", "Linux/Fedora: 38+"
"", "Linux/Ubuntu: 22.04+ (jammy)"
"Network", "Host: 5GHz, 802.11ac"
"", "Client: 5GHz, 802.11ac"
**4k Suggestions**
+------------+------------------------------------------------------------+
| GPU | AMD: Video Coding Engine 3.1 or higher |
| +------------------------------------------------------------+
| | Intel: HD Graphics 510 or higher |
| +------------------------------------------------------------+
| | Nvidia: GeForce GTX 1080 or higher |
+------------+------------------------------------------------------------+
| CPU | AMD: Ryzen 5 or higher |
| +------------------------------------------------------------+
| | Intel: Core i5 or higher |
+------------+------------------------------------------------------------+
| Network | Host: CAT5e ethernet or better |
| +------------------------------------------------------------+
| | Client: CAT5e ethernet or better |
+------------+------------------------------------------------------------+
.. csv-table::
:widths: 15, 60
"GPU", "AMD: Video Coding Engine 3.1 or higher"
"", "Intel: HD Graphics 510 or higher"
"", "Nvidia: GeForce GTX 1080 or higher"
"CPU", "AMD: Ryzen 5 or higher"
"", "Intel: Core i5 or higher"
"Network", "Host: CAT5e ethernet or better"
"", "Client: CAT5e ethernet or better"
**HDR Suggestions**
+------------+------------------------------------------------------------+
| GPU | AMD: Video Coding Engine 3.4 or higher |
| +------------------------------------------------------------+
| | Intel: UHD Graphics 730 or higher |
| +------------------------------------------------------------+
| | Nvidia: Pascal-based GPU (GTX 10-series) or higher |
+------------+------------------------------------------------------------+
| CPU | AMD: todo |
| +------------------------------------------------------------+
| | Intel: todo |
+------------+------------------------------------------------------------+
| Network | Host: CAT5e ethernet or better |
| +------------------------------------------------------------+
| | Client: CAT5e ethernet or better |
+------------+------------------------------------------------------------+
.. csv-table::
:widths: 15, 60
"GPU", "AMD: Video Coding Engine 3.4 or higher"
"", "Intel: UHD Graphics 730 or higher"
"", "Nvidia: Pascal-based GPU (GTX 10-series) or higher"
"CPU", "AMD: todo"
"", "Intel: todo"
"Network", "Host: CAT5e ethernet or better"
"", "Client: CAT5e ethernet or better"
Integrations
------------
@@ -96,6 +75,10 @@ Integrations
:alt: Read the Docs
:target: http://sunshinestream.readthedocs.io/
.. image:: https://img.shields.io/codecov/c/gh/LizardByte/Sunshine?token=SMGXQ5NVMJ&style=for-the-badge&logo=codecov&label=codecov
:alt: Codecov
:target: https://codecov.io/gh/LizardByte/Sunshine
Support
-------
@@ -122,7 +105,3 @@ Stats
.. image:: https://img.shields.io/github/stars/lizardbyte/sunshine.svg?logo=github&style=for-the-badge
:alt: GitHub stars
:target: https://github.com/LizardByte/Sunshine
.. _nvenc support matrix: https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new
.. _obs-amd hardware support: https://github.com/obsproject/obs-amd-encoder/wiki/Hardware-Support
.. _VAAPI hardware support: https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html

View File

@@ -110,11 +110,6 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/stat_trackers.cpp"
${PLATFORM_TARGET_FILES})
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/upnp.cpp" PROPERTIES COMPILE_FLAGS -Wno-pedantic)
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/nanors/rs.c"
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
if(NOT SUNSHINE_ASSETS_DIR_DEF)
set(SUNSHINE_ASSETS_DIR_DEF "${SUNSHINE_ASSETS_DIR}")
endif()
@@ -130,19 +125,11 @@ include_directories(
"${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet/include"
"${CMAKE_SOURCE_DIR}/third-party/nanors"
"${CMAKE_SOURCE_DIR}/third-party/nanors/deps/obl"
"${CMAKE_SOURCE_DIR}/third-party/jwt-cpp/include"
${FFMPEG_INCLUDE_DIRS}
${PLATFORM_INCLUDE_DIRS}
)
string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE)
if("${BUILD_TYPE}" STREQUAL "XDEBUG")
if(WIN32)
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/nvhttp.cpp" PROPERTIES COMPILE_FLAGS -O2)
endif()
else()
add_definitions(-DNDEBUG)
endif()
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${MINIUPNP_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}

View File

@@ -8,10 +8,6 @@ if(${SUNSHINE_BUILD_APPIMAGE})
string(REPLACE "${CMAKE_INSTALL_PREFIX}" ".${CMAKE_INSTALL_PREFIX}" SUNSHINE_ASSETS_DIR_DEF ${SUNSHINE_ASSETS_DIR})
endif()
if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH)
set(SUNSHINE_EXECUTABLE_PATH "sunshine")
endif()
# cuda
set(CUDA_FOUND OFF)
if(${SUNSHINE_ENABLE_CUDA})
@@ -203,13 +199,13 @@ endif()
# tray icon
if(${SUNSHINE_ENABLE_TRAY})
pkg_check_modules(APPINDICATOR appindicator3-0.1)
pkg_check_modules(APPINDICATOR ayatana-appindicator3-0.1)
if(APPINDICATOR_FOUND)
list(APPEND SUNSHINE_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1)
list(APPEND SUNSHINE_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1)
else()
pkg_check_modules(APPINDICATOR ayatana-appindicator3-0.1)
pkg_check_modules(APPINDICATOR appindicator3-0.1)
if(APPINDICATOR_FOUND)
list(APPEND SUNSHINE_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1)
list(APPEND SUNSHINE_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1)
endif ()
endif()
pkg_check_modules(LIBNOTIFY libnotify)

View File

@@ -8,12 +8,13 @@ link_directories(/opt/homebrew/lib)
ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK)
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${APP_KIT_LIBRARY}
${APP_SERVICES_LIBRARY}
${AV_FOUNDATION_LIBRARY}
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY}
${FOUNDATION_LIBRARY})
${FOUNDATION_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY})
set(PLATFORM_INCLUDE_DIRS
${Boost_INCLUDE_DIR})

View File

@@ -29,16 +29,6 @@ file(GLOB NVPREFS_FILES CONFIGURE_DEPENDS
# vigem
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include")
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650")
set(VIGEM_COMPILE_FLAGS "")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unknown-pragmas ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-misleading-indentation ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-class-memaccess ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-function ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-variable ")
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
PROPERTIES COMPILE_FLAGS ${VIGEM_COMPILE_FLAGS})
# sunshine icon
if(NOT DEFINED SUNSHINE_ICON_PATH)

View File

@@ -1,5 +1,6 @@
# macos specific dependencies
FIND_LIBRARY(APP_KIT_LIBRARY AppKit)
FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices)
FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation)
FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia)

View File

@@ -12,10 +12,18 @@ set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/sunshine.png)
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}")
set(CPACK_STRIP_FILES YES)
#install common assets
# install common assets
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}"
PATTERN "web" EXCLUDE)
# copy assets to build directory, for running without install
file(GLOB_RECURSE ALL_ASSETS
RELATIVE "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/*")
list(FILTER ALL_ASSETS EXCLUDE REGEX "^web/.*$") # Filter out the web directory
foreach(asset ${ALL_ASSETS}) # Copy assets to build directory, excluding the web directory
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/${asset}"
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/assets")
endforeach()
# install built vite assets
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/assets/web"

View File

@@ -2,6 +2,9 @@
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}")
# copy assets to build directory, for running without install
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/"
DESTINATION "${CMAKE_BINARY_DIR}/assets")
if(${SUNSHINE_BUILD_APPIMAGE} OR ${SUNSHINE_BUILD_FLATPAK})
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/60-sunshine.rules"
DESTINATION "${SUNSHINE_ASSETS_DIR}/udev/rules.d")

View File

@@ -10,15 +10,16 @@ if(SUNSHINE_PACKAGE_MACOS) # todo
set(MAC_PREFIX "${CMAKE_PROJECT_NAME}.app/Contents")
set(INSTALL_RUNTIME_DIR "${MAC_PREFIX}/MacOS")
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}")
install(TARGETS sunshine
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR} COMPONENT Runtime)
else()
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}")
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/misc/uninstall_pkg.sh"
DESTINATION "${SUNSHINE_ASSETS_DIR}")
endif()
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}")
# copy assets to build directory, for running without install
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
DESTINATION "${CMAKE_BINARY_DIR}/assets")

View File

@@ -39,6 +39,9 @@ install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/gamepad/"
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/"
DESTINATION "${SUNSHINE_ASSETS_DIR}"
COMPONENT assets)
# copy assets to build directory, for running without install
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/"
DESTINATION "${CMAKE_BINARY_DIR}/assets")
# set(CPACK_NSIS_MUI_HEADERIMAGE "") # TODO: image should be 150x57 bmp
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}\\\\sunshine.ico")

9
cmake/prep/init.cmake Normal file
View File

@@ -0,0 +1,9 @@
if (WIN32)
elseif (APPLE)
elseif (UNIX)
include(GNUInstallDirs)
if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH)
set(SUNSHINE_EXECUTABLE_PATH "sunshine")
endif()
endif ()

View File

@@ -1,3 +1,10 @@
option(BUILD_TESTS "Build tests" ON)
option(TESTS_ENABLE_PYTHON_TESTS "Enable Python tests" ON)
# DirectX11 is not available in GitHub runners, so even software encoding fails
set(TESTS_SOFTWARE_ENCODER_UNAVAILABLE "fail"
CACHE STRING "How to handle unavailable software encoders in tests. 'fail/skip'")
option(BUILD_WERROR "Enable -Werror flag." OFF)
# if this option is set, the build will exit after configuring special package configuration files

View File

@@ -6,8 +6,6 @@ if (APPLE)
configure_file(packaging/macos/sunshine.rb sunshine.rb @ONLY)
endif()
elseif (UNIX)
include(GNUInstallDirs) # this needs to be included prior to configuring the desktop files
# configure the .desktop file
if(${SUNSHINE_BUILD_APPIMAGE})
configure_file(packaging/linux/AppImage/sunshine.desktop sunshine.desktop @ONLY)
@@ -35,6 +33,7 @@ elseif (UNIX)
# configure the flatpak manifest
if(${SUNSHINE_CONFIGURE_FLATPAK_MAN})
configure_file(packaging/linux/flatpak/dev.lizardbyte.sunshine.yml dev.lizardbyte.sunshine.yml @ONLY)
file(COPY packaging/linux/flatpak/deps/ DESTINATION ${CMAKE_BINARY_DIR})
endif()
endif()

View File

@@ -3,6 +3,18 @@
add_executable(sunshine ${SUNSHINE_TARGET_FILES})
# Homebrew build fails the vite build if we set these environment variables
# this block must be before the platform specific code
if(${SUNSHINE_BUILD_HOMEBREW})
set(NPM_SOURCE_ASSETS_DIR "")
set(NPM_ASSETS_DIR "")
set(NPM_BUILD_HOMEBREW "true")
else()
set(NPM_SOURCE_ASSETS_DIR ${SUNSHINE_SOURCE_ASSETS_DIR})
set(NPM_ASSETS_DIR ${CMAKE_BINARY_DIR})
set(NPM_BUILD_HOMEBREW "")
endif()
# platform specific target definitions
if(WIN32)
include(${CMAKE_MODULE_PATH}/targets/windows.cmake)
@@ -24,7 +36,7 @@ endif()
target_link_libraries(sunshine ${SUNSHINE_EXTERNAL_LIBRARIES} ${EXTRA_LIBS})
target_compile_definitions(sunshine PUBLIC ${SUNSHINE_DEFINITIONS})
set_target_properties(sunshine PROPERTIES CXX_STANDARD 17
set_target_properties(sunshine PROPERTIES CXX_STANDARD 20
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
@@ -37,19 +49,50 @@ endif()
target_compile_options(sunshine PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301
# Homebrew build fails the vite build if we set these environment variables
if(${SUNSHINE_BUILD_HOMEBREW})
set(NPM_SOURCE_ASSETS_DIR "")
set(NPM_ASSETS_DIR "")
set(NPM_BUILD_HOMEBREW "true")
else()
set(NPM_SOURCE_ASSETS_DIR ${SUNSHINE_SOURCE_ASSETS_DIR})
set(NPM_ASSETS_DIR ${CMAKE_BINARY_DIR})
set(NPM_BUILD_HOMEBREW "")
# tests
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
#WebUI build
add_custom_target(web-ui ALL
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Installing NPM Dependencies and Building the Web UI"
COMMAND bash -c \"npm install && SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW} SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR} SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR} npm run build\") # cmake-lint: disable=C0301
# custom compile flags, must be after adding tests
if (NOT BUILD_TESTS)
set(TEST_DIR "")
else()
set(TEST_DIR "${CMAKE_SOURCE_DIR}/tests")
endif()
# src/upnp
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/upnp.cpp"
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
PROPERTIES COMPILE_FLAGS -Wno-pedantic)
# third-party/nanors
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/nanors/rs.c"
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
# third-party/ViGEmClient
set(VIGEM_COMPILE_FLAGS "")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unknown-pragmas ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-misleading-indentation ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-class-memaccess ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-function ")
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-variable ")
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
PROPERTIES
COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650"
COMPILE_FLAGS ${VIGEM_COMPILE_FLAGS})
# src/nvhttp
string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE)
if("${BUILD_TYPE}" STREQUAL "XDEBUG")
if(WIN32)
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/nvhttp.cpp"
DIRECTORY "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests"
PROPERTIES COMPILE_FLAGS -O2)
endif()
else()
add_definitions(-DNDEBUG)
endif()

View File

@@ -1,2 +1,8 @@
# unix specific target definitions
# put anything here that applies to both linux and macos
#WebUI build
add_custom_target(web-ui ALL
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Installing NPM Dependencies and Building the Web UI"
COMMAND sh -c \"npm install && SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW} SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR} SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR} npm run build\") # cmake-lint: disable=C0301

View File

@@ -4,3 +4,9 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(ZLIB ZLIB1)
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
Wtsapi32.lib)
#WebUI build
add_custom_target(web-ui ALL
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Installing NPM Dependencies and Building the Web UI"
COMMAND cmd /C "npm install && set \"SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR}\" && set \"SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR}\" && npm run build") # cmake-lint: disable=C0301

19
codecov.yml Normal file
View File

@@ -0,0 +1,19 @@
---
codecov:
branch: nightly
coverage:
status:
project:
default:
target: auto
threshold: 10%
comment:
layout: "diff, flags, files"
behavior: default
require_changes: false # if true: only post the comment if coverage changes
ignore:
- "tests"
- "third-party"

View File

@@ -1,7 +1,7 @@
---
"base_path": "."
"base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only)
"preserve_hierarchy": false # flatten tree on crowdin
"preserve_hierarchy": true # false will flatten tree on crowdin, but doesn't work with dest option
"pull_request_labels": [
"crowdin",
"l10n"
@@ -10,6 +10,7 @@
"files": [
{
"source": "/locale/*.po",
"dest": "/%original_file_name%",
"translation": "/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%",
"languages_mapping": {
"two_letters_code": {
@@ -17,6 +18,13 @@
"en-GB": "en_GB",
"en-US": "en_US"
}
}
},
"update_option": "update_as_unapproved"
},
{
"source": "/src_assets/common/assets/web/public/assets/locale/en.json",
"dest": "/sunshine.json",
"translation": "/src_assets/common/assets/web/public/assets/locale/%two_letters_code%.%file_extension%",
"update_option": "update_as_unapproved"
}
]

View File

@@ -43,7 +43,8 @@ pacman -Syu --disable-download-timeout --needed --noconfirm \
cmake \
cuda \
git \
namcap
namcap \
xorg-server-xvfb
_DEPS
# Setup builder user
@@ -84,6 +85,8 @@ RUN mv /build/sunshine/build/sunshine.install .
RUN <<_PKGBUILD
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
namcap -i PKGBUILD
makepkg -si --noconfirm
rm -f /build/sunshine/pkg/sunshine-debug*.pkg.tar.zst

View File

@@ -14,6 +14,8 @@ FROM toolchain-base as toolchain
ARG TARGETPLATFORM
RUN echo "target_platform: ${TARGETPLATFORM}"
ENV DISPLAY=:0
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install dependencies
RUN <<_DEPS
@@ -24,12 +26,13 @@ apt-get install -y --no-install-recommends \
build-essential \
cmake=3.22.* \
ca-certificates \
doxygen \
gcc=4:11.2.* \
g++=4:11.2.* \
gdb \
git \
graphviz \
libayatana-appindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev=1.74.* \
libboost-locale-dev=1.74.* \
libboost-log-dev=1.74.* \
@@ -54,8 +57,12 @@ apt-get install -y --no-install-recommends \
libxfixes-dev \
libxrandr-dev \
libxtst-dev \
python3.10 \
python3.10-venv \
udev \
wget
wget \
x11-xserver-utils \
xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
apt-get install -y --no-install-recommends \
libmfx-dev
@@ -98,3 +105,28 @@ chmod a+x ./cuda.run
./cuda.run --silent --toolkit --toolkitpath=/usr/local --no-opengl-libs --no-man-page --no-drm
rm ./cuda.run
_INSTALL_CUDA
WORKDIR /
# Write a shell script that starts Xvfb and then runs a shell
RUN <<_ENTRYPOINT
#!/bin/bash
set -e
cat <<EOF > /entrypoint.sh
#!/bin/bash
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
if [ "\$#" -eq 0 ]; then
exec "/bin/bash"
else
exec "\$@"
fi
EOF
_ENTRYPOINT
# Make the script executable
RUN chmod +x /entrypoint.sh
# Note about CLion
RUN echo "ATTENTION: CLion will override the entrypoint, you can disable this in the toolchain settings"
# Use the shell script as the entrypoint
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -32,8 +32,9 @@ apt-get update -y
apt-get install -y --no-install-recommends \
build-essential \
cmake=3.25.* \
doxygen \
git \
libavdevice-dev \
graphviz \
libayatana-appindicator3-dev \
libboost-filesystem-dev=1.74.* \
libboost-locale-dev=1.74.* \
@@ -61,8 +62,12 @@ apt-get install -y --no-install-recommends \
libxtst-dev \
nodejs \
npm \
python3.11 \
python3.11-venv \
udev \
wget
wget \
x11-xserver-utils \
xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
apt-get install -y --no-install-recommends \
libmfx-dev
@@ -120,6 +125,17 @@ make -j "$(nproc)"
cpack -G DEB
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG

View File

@@ -33,8 +33,9 @@ apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
cmake=3.18.* \
doxygen \
git \
libavdevice-dev \
graphviz \
libayatana-appindicator3-dev \
libboost-filesystem-dev=1.74.* \
libboost-locale-dev=1.74.* \
@@ -60,8 +61,12 @@ apt-get install -y --no-install-recommends \
libxfixes-dev \
libxrandr-dev \
libxtst-dev \
python3.9 \
python3.9-venv \
udev \
wget
wget \
x11-xserver-utils \
xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
apt-get install -y --no-install-recommends \
libmfx-dev
@@ -134,6 +139,17 @@ make -j "$(nproc)"
cpack -G DEB
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
# artifacts: true
# platforms: linux/amd64,linux/arm64/v8
# platforms: linux/amd64
# platforms_pr: linux/amd64
# no-cache-filters: sunshine-base,artifacts,sunshine
ARG BASE=fedora
@@ -32,9 +32,11 @@ dnf -y group install "Development Tools"
dnf -y install \
boost-devel-1.78.0* \
cmake-3.27.* \
doxygen \
gcc-13.2.* \
gcc-c++-13.2.* \
git \
graphviz \
libappindicator-gtk3-devel \
libcap-devel \
libcurl-devel \
@@ -58,9 +60,11 @@ dnf -y install \
openssl-devel \
opus-devel \
pulseaudio-libs-devel \
python3.10 \
rpm-build \
wget \
which
which \
xorg-x11-server-Xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
dnf -y install intel-mediasdk-devel
fi
@@ -117,6 +121,17 @@ make -j "$(nproc)"
cpack -G RPM
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
# artifacts: true
# platforms: linux/amd64,linux/arm64/v8
# platforms: linux/amd64
# platforms_pr: linux/amd64
# no-cache-filters: sunshine-base,artifacts,sunshine
ARG BASE=fedora
@@ -32,9 +32,11 @@ dnf -y group install "Development Tools"
dnf -y install \
boost-devel-1.81.0* \
cmake-3.27.* \
doxygen \
gcc-13.2.* \
gcc-c++-13.2.* \
git \
graphviz \
libappindicator-gtk3-devel \
libcap-devel \
libcurl-devel \
@@ -58,9 +60,11 @@ dnf -y install \
openssl-devel \
opus-devel \
pulseaudio-libs-devel \
python3.11 \
rpm-build \
wget \
which
which \
xorg-x11-server-Xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
dnf -y install intel-mediasdk-devel
fi
@@ -124,6 +128,17 @@ make -j "$(nproc)"
cpack -G RPM
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG

View File

@@ -33,9 +33,10 @@ apt-get install -y --no-install-recommends \
build-essential \
cmake=3.22.* \
ca-certificates \
doxygen \
git \
graphviz \
libayatana-appindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev=1.74.* \
libboost-locale-dev=1.74.* \
libboost-log-dev=1.74.* \
@@ -60,8 +61,12 @@ apt-get install -y --no-install-recommends \
libxfixes-dev \
libxrandr-dev \
libxtst-dev \
python3.10 \
python3.10-venv \
udev \
wget
wget \
x11-xserver-utils \
xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
apt-get install -y --no-install-recommends \
libmfx-dev
@@ -135,6 +140,17 @@ make -j "$(nproc)"
cpack -G DEB
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG

View File

@@ -1,10 +1,10 @@
# syntax=docker/dockerfile:1.4
# artifacts: true
# platforms: linux/amd64,linux/arm64/v8
# platforms: linux/amd64
# platforms_pr: linux/amd64
# no-cache-filters: sunshine-base,artifacts,sunshine
ARG BASE=ubuntu
ARG TAG=20.04
ARG TAG=24.04
FROM ${BASE}:${TAG} AS sunshine-base
ENV DEBIAN_FRONTEND=noninteractive
@@ -31,16 +31,18 @@ set -e
apt-get update -y
apt-get install -y --no-install-recommends \
build-essential \
cmake=3.28.* \
ca-certificates \
gcc-10=10.5.* \
g++-10=10.5.* \
doxygen \
gcc-11 \
g++-11 \
git \
graphviz \
libayatana-appindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev=1.71.* \
libboost-locale-dev=1.71.* \
libboost-log-dev=1.71.* \
libboost-program-options-dev=1.71.* \
libboost-filesystem-dev=1.83.* \
libboost-locale-dev=1.83.* \
libboost-log-dev=1.83.* \
libboost-program-options-dev=1.83.* \
libcap-dev \
libcurl4-openssl-dev \
libdrm-dev \
@@ -61,8 +63,12 @@ apt-get install -y --no-install-recommends \
libxfixes-dev \
libxrandr-dev \
libxtst-dev \
python3.12 \
python3.12-venv \
udev \
wget
wget \
x11-xserver-utils \
xvfb
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
apt-get install -y --no-install-recommends \
libmfx-dev
@@ -71,6 +77,7 @@ apt-get clean
rm -rf /var/lib/apt/lists/*
_DEPS
#Install Node
# hadolint ignore=SC1091
RUN <<_INSTALL_NODE
@@ -88,35 +95,13 @@ RUN <<_GCC_ALIAS
#!/bin/bash
set -e
update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-10 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-10 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
/usr/bin/gcc gcc /usr/bin/gcc-11 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-11 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-11 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-11 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-11
_GCC_ALIAS
# install cmake
# sunshine requires cmake >= 3.18
WORKDIR /build/cmake
# https://cmake.org/download/
ENV CMAKE_VERSION="3.25.1"
# hadolint ignore=SC3010
RUN <<_INSTALL_CMAKE
#!/bin/bash
set -e
cmake_prefix="https://github.com/Kitware/CMake/releases/download/v"
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
cmake_arch="x86_64"
elif [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
cmake_arch="aarch64"
fi
url="${cmake_prefix}${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-${cmake_arch}.sh"
echo "cmake url: ${url}"
wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cmake.sh
sh ./cmake.sh --prefix=/usr/local --skip-license
rm ./cmake.sh
_INSTALL_CMAKE
# install cuda
WORKDIR /build/cuda
# versions: https://developer.nvidia.com/cuda-toolkit-archive
@@ -154,6 +139,7 @@ set -e
#Set Node version
source "$HOME/.nvm/nvm.sh"
nvm use 20.9.0
#Actually build
cmake \
-DBUILD_WERROR=ON \
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
@@ -170,6 +156,17 @@ make -j "$(nproc)"
cpack -G DEB
_MAKE
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
./test_sunshine --gtest_color=yes
_TEST
FROM scratch AS artifacts
ARG BASE
ARG TAG
@@ -197,9 +194,9 @@ EXPOSE 48010
EXPOSE 47998-48000/udp
# setup user
ARG PGID=1000
ARG PGID=1001
ENV PGID=${PGID}
ARG PUID=1000
ARG PUID=1001
ENV PUID=${PUID}
ENV TZ="UTC"
ARG UNAME=lizard

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.6
# Doxyfile 1.10.0
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "Sunshine"
PROJECT_NAME = Sunshine
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@@ -63,6 +63,12 @@ PROJECT_BRIEF = "Sunshine is a Gamestream host for Moonlight."
PROJECT_LOGO = ../sunshine.png
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
# when the HTML document is shown. Doxygen will copy the logo to the output
# directory.
PROJECT_ICON =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
@@ -365,6 +371,17 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
# generate identifiers for the Markdown headings. Note: Every identifier is
# unique.
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
# sequence number starting at 0 and GITHUB use the lower case version of title
# with any whitespace replaced by '-' and punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = DOXYGEN
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
@@ -489,6 +506,14 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 0
# If the TIMESTAMP tag is set different from NO then each generated page will
# contain the date or date and time when the page was generated. Setting this to
# NO can help when comparing the output of multiple runs.
# Possible values are: YES, NO, DATETIME and DATE.
# The default value is: NO.
TIMESTAMP = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -874,7 +899,14 @@ WARN_IF_UNDOC_ENUM_VAL = NO
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
# write the warning messages in between other messages but write them at the end
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
# besides being in the defined file also be shown at the end of a run, unless
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -953,12 +985,12 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cc \
@@ -1043,9 +1075,6 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
@@ -1159,7 +1188,8 @@ FORTRAN_COMMENT_AFTER = 72
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# multi-line macros, enums or list initialized variables directly into the
# documentation.
# The default value is: NO.
INLINE_SOURCES = NO
@@ -1428,15 +1458,6 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
@@ -1456,6 +1477,33 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
# the top right corner of code and text fragments that allows the user to copy
# its content to the clipboard. Note this only works if supported by the browser
# and the web page is served via a secure context (see:
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
# protocol.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COPY_CLIPBOARD = YES
# Doxygen stores a couple of settings persistently in the browser (via e.g.
# cookies). By default these settings apply to all HTML pages generated by
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
# the settings under a project specific key, such that the user preferences will
# be stored separately.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_PROJECT_COOKIE =
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1586,6 +1634,16 @@ BINARY_TOC = NO
TOC_EXPAND = NO
# The SITEMAP_URL tag is used to specify the full URL of the place where the
# generated documentation will be placed on the server by the user during the
# deployment of the documentation. The generated sitemap is called sitemap.xml
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
# is specified no sitemap is generated. For information about the sitemap
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -2074,9 +2132,16 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
# hit at every error; missing files that TeX tries to input or request from
# keyboard input (\read on a not open input stream) cause the job to abort,
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
# but there is no possibility of user interaction just like in batch mode,
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
# each error, asking for user intervention.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2097,14 +2162,6 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -2270,13 +2327,39 @@ DOCBOOK_OUTPUT = doxydocbook
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: NO.
GENERATE_SQLITE3 = NO
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_OUTPUT = sqlite3
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
# database file will be recreated with each doxygen run. If set to NO, doxygen
# will warn if a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_RECREATE_DB = YES
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2419,15 +2502,15 @@ TAGFILES =
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
# will be listed in the class and namespace index. If set to NO, only the
# inherited external classes will be listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# in the topic index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
@@ -2441,16 +2524,9 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
# Configuration options related to diagram generator tools
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
@@ -2459,7 +2535,7 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
@@ -2512,13 +2588,19 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
# generate a graph for each documented class showing the direct and indirect
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
# relations will be shown as texts / links. Explicit enabling an inheritance
# graph or choosing a different representation for an inheritance graph of a
# specific class, can be accomplished by means of the command \inheritancegraph.
# Disabling an inheritance graph can be accomplished by means of the command
# \hideinheritancegraph.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES.
CLASS_GRAPH = YES
@@ -2526,15 +2608,21 @@ CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# class with other documented classes. Explicit enabling a collaboration graph,
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# groups, showing the direct groups dependencies. Explicit enabling a group
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2576,8 +2664,8 @@ DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# significantly it will be wrapped across multiple lines. Some heuristics are
# applied to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2594,7 +2682,9 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2603,7 +2693,10 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2643,7 +2736,10 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# files in the directories. Explicit enabling a directory graph, when
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2659,7 +2755,7 @@ DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# https://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
@@ -2696,11 +2792,12 @@ DOT_PATH =
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
MSCFILE_DIRS =
DIA_PATH =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
@@ -2777,3 +2874,19 @@ GENERATE_LEGEND = YES
# The default value is: YES.
DOT_CLEANUP = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
# use a built-in version of mscgen tool to produce the charts. Alternatively,
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
# specifying prog as the value, doxygen will call the tool as prog -T
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
# output file formats "png", "eps", "svg", and "ismap".
MSCGEN_TOOL =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =

View File

@@ -3,7 +3,7 @@
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXOPTS ?= -W --keep-going
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

View File

@@ -9,6 +9,7 @@ if "%SPHINXBUILD%" == "" (
)
set SOURCEDIR=source
set BUILDDIR=build
set "SPHINXOPTS=-W --keep-going"
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
@@ -25,11 +26,11 @@ if errorlevel 9009 (
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL%
:end
popd

View File

@@ -1,8 +1,9 @@
breathe==4.35.0
furo==2024.1.29
m2r2==0.3.3.post2
rstcheck[sphinx]==6.2.0
rstcheck[sphinx]==6.2.1
rstfmt==0.0.14
setuptools # required by m2r2, Ubuntu 24.04 doesn't include this
Sphinx==7.2.6
sphinx-copybutton==0.5.2
sphinx_inline_tabs==2023.4.21

View File

@@ -47,6 +47,42 @@ editing the `conf` file in a text editor. Use the examples as reference.
`General <https://localhost:47990/config/#general>`__
-----------------------------------------------------
`locale <https://localhost:47990/config/#locale>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The locale used for Sunshine's user interface.
**Choices**
.. table::
:widths: auto
======= ===========
Value Description
======= ===========
de German
en English
en_GB English (UK)
en_US English (United States)
es Spanish
fr French
it Italian
ja Japanese
pt Portuguese
ru Russian
sv Swedish
zh Chinese (Simplified)
======= ===========
**Default**
``en``
**Example**
.. code-block:: text
locale = en
`sunshine_name <https://localhost:47990/config/#sunshine_name>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -540,20 +576,29 @@ keybindings
.. tip:: To find the name of the appropriate values follow these instructions.
**Linux**
During Sunshine startup, you should see the list of detected monitors:
During Sunshine startup, you should see the list of detected displays:
.. code-block:: text
Info: Detecting connected monitors
Info: Detected monitor 0: DVI-D-0, connected: false
Info: Detected monitor 1: HDMI-0, connected: true
Info: Detected monitor 2: DP-0, connected: true
Info: Detected monitor 3: DP-1, connected: false
Info: Detected monitor 4: DVI-D-1, connected: false
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
You need to use the value before the colon in the output, e.g. ``1``.
You need to use the id value inside the parenthesis, e.g. ``1``.
.. todo:: macOS
**macOS**
During Sunshine startup, you should see the list of detected displays:
.. code-block:: text
Info: Detecting displays
Info: Detected display: Monitor-0 (id: 3) connected: true
Info: Detected display: Monitor-1 (id: 2) connected: true
You need to use the id value inside the parenthesis, e.g. ``3``.
**Windows**
.. code-block:: batch
@@ -569,7 +614,10 @@ keybindings
output_name = 0
.. todo:: macOS
**macOS**
.. code-block:: text
output_name = 3
**Windows**
.. code-block:: text
@@ -1437,11 +1485,115 @@ keybindings
`AMD AMF Encoder <https://localhost:47990/config/#amd-amf-encoder>`__
---------------------------------------------------------------------
`amd_usage <https://localhost:47990/config/#amd_usage>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The encoder usage profile is used to set the base set of encoding
parameters.
.. note:: This option only applies when using amdvce `encoder`_.
.. note:: The other AMF options that follow will override a subset
of the settings applied by your usage profile, but there are
hidden parameters set in usage profiles that cannot be
overridden elsewhere.
**Choices**
.. table::
:widths: auto
======================= ===========
Value Description
======================= ===========
transcoding transcoding (slowest)
webcam webcam (slow)
lowlatency_high_quality low latency, high quality (fast)
lowlatency low latency (faster)
ultralowlatency ultra low latency (fastest)
======================= ===========
**Default**
``ultralowlatency``
**Example**
.. code-block:: text
amd_usage = ultralowlatency
`amd_rc <https://localhost:47990/config/#amd_rc>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The encoder rate control.
.. note:: This option only applies when using amdvce `encoder`_.
.. warning:: The 'vbr_latency' option generally works best, but
some bitrate overshoots may still occur. Enabling HRD allows
all bitrate based rate controls to better constrain peak bitrate,
but may result in encoding artifacts depending on your card.
**Choices**
.. table::
:widths: auto
=========== ===========
Value Description
=========== ===========
cqp constant qp mode
cbr constant bitrate
vbr_latency variable bitrate, latency constrained
vbr_peak variable bitrate, peak constrained
=========== ===========
**Default**
``vbr_latency``
**Example**
.. code-block:: text
amd_rc = vbr_latency
`amd_enforce_hrd <https://localhost:47990/config/#amd_enforce_hrd>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
Enable Hypothetical Reference Decoder (HRD) enforcement to help constrain the target bitrate.
.. note:: This option only applies when using amdvce `encoder`_.
.. warning:: HRD is known to cause encoding artifacts or negatively affect
encoding quality on certain cards.
**Choices**
.. table::
:widths: auto
======== ===========
Value Description
======== ===========
enabled enable HRD
disabled disable HRD
======== ===========
**Default**
``disabled``
**Example**
.. code-block:: text
amd_enforce_hrd = disabled
`amd_quality <https://localhost:47990/config/#amd_quality>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The encoder preset to use.
The quality profile controls the tradeoff between
speed and quality of encoding.
.. note:: This option only applies when using amdvce `encoder`_.
@@ -1466,65 +1618,6 @@ keybindings
amd_quality = balanced
`amd_rc <https://localhost:47990/config/#amd_rc>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The encoder rate control.
.. note:: This option only applies when using amdvce `encoder`_.
**Choices**
.. table::
:widths: auto
=========== ===========
Value Description
=========== ===========
cqp constant qp mode
cbr constant bitrate
vbr_latency variable bitrate, latency constrained
vbr_peak variable bitrate, peak constrained
=========== ===========
**Default**
``vbr_latency``
**Example**
.. code-block:: text
amd_rc = vbr_latency
`amd_usage <https://localhost:47990/config/#amd_usage>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
The encoder usage profile, used to balance latency with encoding quality.
.. note:: This option only applies when using amdvce `encoder`_.
**Choices**
.. table::
:widths: auto
=============== ===========
Value Description
=============== ===========
transcoding transcoding (slowest)
webcam webcam (slow)
lowlatency low latency (fast)
ultralowlatency ultra low latency (fastest)
=============== ===========
**Default**
``ultralowlatency``
**Example**
.. code-block:: text
amd_usage = ultralowlatency
`amd_preanalysis <https://localhost:47990/config/#amd_preanalysis>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1546,7 +1639,9 @@ keybindings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Description**
Variance Based Adaptive Quantization (VBAQ) can increase subjective visual quality.
Variance Based Adaptive Quantization (VBAQ) can increase subjective
visual quality by prioritizing allocation of more bits to smooth
areas compared to more textured areas.
.. note:: This option only applies when using amdvce `encoder`_.

View File

@@ -0,0 +1,30 @@
How to not stream Discord call audio
====================================
#. Set your normal `Sound Output` volume to 100%
.. image:: ../../../images/discord_calls_01.png
#. Start Sunshine
#. Set `Sound Output` to `sink-sunshine-stereo` (if it isn't automatic)
.. image:: ../../../images/discord_calls_02.png
#. In Discord - `Right Click` - `Deafen` - Select your normal `Output Device`
This is also where you will need to adjust output volume for Discord calls
.. image:: ../../../images/discord_calls_03.png
#. Open `qpwgraph`
.. image:: ../../../images/discord_calls_04.png
#. Connect `sunshine [sunshine-record]` to your normal `Output Device`
* Drag `monitor_FL` to `playback_FL`
* Drag `monitor_FR` to `playback_FR`
.. image:: ../../../images/discord_calls_05.png

View File

@@ -45,8 +45,8 @@ Install
sunshine-debian-bullseye-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
sunshine-fedora-38-{arch}.rpm 12.4.0 525.60.13 50;52;60;61;62;70;75;80;86;90
sunshine-fedora-39-{arch}.rpm 12.4.0 525.60.13 50;52;60;61;62;70;75;80;86;90
sunshine-ubuntu-20.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
sunshine-ubuntu-22.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
sunshine-ubuntu-24.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
=========================================== ============== ============== ================================
.. tab:: AppImage
@@ -307,7 +307,7 @@ Install
mkdir -p ~/ports/multimedia/sunshine
cd ~/ports/multimedia/sunshine
curl -O https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile
curl -OL https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile
cd ~/ports
portindex
sudo port install sunshine

View File

@@ -15,7 +15,6 @@ Install Requirements
sudo apt update && sudo apt install \
build-essential \
cmake \
libavdevice-dev \
libayatana-appindicator3-dev \
libboost-filesystem-dev \
libboost-locale-dev \
@@ -88,61 +87,8 @@ Install Requirements
wget \ # necessary for cuda install with `run` file
which # necessary for cuda install with `run` file
Ubuntu 20.04
^^^^^^^^^^^^
End of Life: April 2030
Install Requirements
.. code-block:: bash
sudo apt update && sudo apt install \
build-essential \
cmake \
g++-10 \
libayatana-appindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-log-dev \
libboost-program-options-dev \
libcap-dev \ # KMS
libcurl4-openssl-dev \
libdrm-dev \ # KMS
libevdev-dev \
libminiupnpc-dev \
libmfx-dev \ # x86_64 only
libnotify-dev \
libnuma-dev \
libopus-dev \
libpulse-dev \
libssl-dev \
libva-dev \ # VA-API
libvdpau-dev \
libwayland-dev \ # Wayland
libx11-dev \ # X11
libxcb-shm0-dev \ # X11
libxcb-xfixes0-dev \ # X11
libxcb1-dev \ # X11
libxfixes-dev \ # X11
libxrandr-dev \ # X11
libxtst-dev \ # X11
nodejs \
npm \
wget # necessary for cuda install with `run` file
Update gcc alias
.. code-block:: bash
update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-10 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-10 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
Ubuntu 22.04
^^^^^^^^^^^^
End of Life: April 2027
Install Requirements
.. code-block:: bash
@@ -151,7 +97,6 @@ Install Requirements
build-essential \
cmake \
libappindicator3-dev \
libavdevice-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-log-dev \
@@ -181,11 +126,63 @@ Install Requirements
nvidia-cuda-dev \ # CUDA, NvFBC
nvidia-cuda-toolkit # CUDA, NvFBC
Ubuntu 24.04
^^^^^^^^^^^^
Install Requirements
.. code-block:: bash
sudo apt update && sudo apt install \
build-essential \
cmake \
gcc-11 \
g++-11 \
libappindicator3-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-log-dev \
libboost-program-options-dev \
libcap-dev \ # KMS
libcurl4-openssl-dev \
libdrm-dev \ # KMS
libevdev-dev \
libminiupnpc-dev \
libmfx-dev \ # x86_64 only
libnotify-dev \
libnuma-dev \
libopus-dev \
libpulse-dev \
libssl-dev \
libva-dev \ # VA-API
libwayland-dev \ # Wayland
libx11-dev \ # X11
libxcb-shm0-dev \ # X11
libxcb-xfixes0-dev \ # X11
libxcb1-dev \ # X11
libxfixes-dev \ # X11
libxrandr-dev \ # X11
libxtst-dev \ # X11
nodejs \
npm \
nvidia-cuda-dev \ # CUDA, NvFBC
nvidia-cuda-toolkit # CUDA, NvFBC
Update gcc alias
.. code-block:: bash
update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-11 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-11 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-11 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-11 \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-11
CUDA
----
If the version of CUDA available from your distro is not adequate, manually install CUDA.
.. tip:: The version of CUDA you use will determine compatibility with various GPU generations.
At the time of writing, the recommended version to use is CUDA ~11.8.
See `CUDA compatibility <https://docs.nvidia.com/deploy/cuda-compatibility/index.html>`__ for more info.
Select the appropriate run file based on your desired CUDA version and architecture according to

View File

@@ -12,14 +12,14 @@ MacPorts
Install Requirements
.. code-block:: bash
sudo port install avahi boost180 cmake curl libopus miniupnpc npm9 pkgconfig
sudo port install avahi boost180 cmake curl doxygen graphviz libopus miniupnpc npm9 pkgconfig python311 py311-pip
Homebrew
""""""""
Install Requirements
.. code-block:: bash
brew install boost cmake miniupnpc node opus pkg-config
brew install boost cmake doxygen graphviz miniupnpc node opus pkg-config python@3.11
If there are issues with an SSL header that is not found:
.. tab:: Intel
@@ -45,7 +45,7 @@ Build
.. code-block:: bash
cmake ..
make -j ${nproc}
make -j $(sysctl -n hw.ncpu)
cpack -G DragNDrop # optionally, create a macOS dmg package

View File

@@ -18,6 +18,7 @@ Install dependencies:
base-devel \
cmake \
diffutils \
doxygen \
gcc \
git \
make \
@@ -25,13 +26,17 @@ Install dependencies:
mingw-w64-x86_64-boost \
mingw-w64-x86_64-cmake \
mingw-w64-x86_64-curl \
mingw-w64-x86_64-graphviz \
mingw-w64-x86_64-miniupnpc \
mingw-w64-x86_64-nlohmann-json \
mingw-w64-x86_64-nodejs \
mingw-w64-x86_64-onevpl \
mingw-w64-x86_64-openssl \
mingw-w64-x86_64-opus \
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-rust \
mingw-w64-x86_64-toolchain \
python \
python-pip
Build
-----

View File

@@ -7,7 +7,6 @@
# standard imports
from datetime import datetime
import os
import re
import subprocess
@@ -27,16 +26,8 @@ project_copyright = f'{datetime.now ().year}, {project}'
author = 'ReenigneArcher'
# The full version, including alpha/beta/rc tags
with open(os.path.join(root_dir, 'CMakeLists.txt'), 'r') as f:
version = re.search(r"project\(Sunshine VERSION ((\d+)\.(\d+)\.(\d+))", str(f.read())).group(1)
"""
To use cmake method for obtaining version instead of regex,
1. Within CMakeLists.txt add the following line without backticks:
``configure_file(docs/source/conf.py.in "${CMAKE_CURRENT_SOURCE_DIR}/docs/source/conf.py" @ONLY)``
2. Rename this file to ``conf.py.in``
3. Uncomment the next line
"""
# version = '@PROJECT_VERSION@' # use this for cmake configure_file method
# https://docs.readthedocs.io/en/stable/reference/environment-variables.html#envvar-READTHEDOCS_VERSION
version = os.getenv('READTHEDOCS_VERSION', 'dirty')
# -- General configuration ---------------------------------------------------
@@ -105,6 +96,17 @@ doxy_proc = subprocess.run('doxygen --version', shell=True, cwd=source_dir, capt
doxy_version = doxy_proc.stdout.decode('utf-8').strip()
print('doxygen version: ' + doxy_version)
# create build directories, as doxygen fails to create it in macports and docker
directories = [
os.path.join(source_dir, 'build'),
os.path.join(source_dir, 'build', 'doxyxml'),
]
for d in directories:
os.makedirs(
name=d,
exist_ok=True,
)
# run doxygen
doxy_proc = subprocess.run('doxygen Doxyfile', shell=True, cwd=source_dir)
if doxy_proc.returncode != 0:

View File

@@ -30,42 +30,84 @@ localization there.
Extraction
----------
There should be minimal cases where strings need to be extracted from source code; however it may be necessary in some
situations. For example if a system tray icon is added it should be localized as it is user interfacing.
- Wrap the string to be extracted in a function as shown.
.. code-block:: cpp
.. tab:: UI
#include <boost/locale.hpp>
#include <string>
Sunshine uses `Vue I18n <https://vue-i18n.intlify.dev/>`__ for localizing the UI.
The following is a simple example of how to use it.
std::string msg = boost::locale::translate("Hello world!");
- Add the string to `src_assets/common/assets/web/public/assets/locale/en.json`, in English.
.. code-block:: json
.. tip:: More examples can be found in the documentation for
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.
{
"index": {
"welcome": "Hello, Sunshine!"
}
}
.. warning:: This is for information only. Contributors should never include manually updated template files, or
manually compiled language files in Pull Requests.
.. note:: The json keys should be sorted alphabetically. You can use `jsonabc <https://novicelab.org/jsonabc/>`__
to sort the keys.
Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
used by CrowdIn to generate language specific template files. The file is generated using the
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
any of the following paths are modified.
.. attention:: Due to the integration with Crowdin, it is important to only add strings to the `en.json` file,
and to not modify any other language files. After the PR is merged, the translations can take place
on `CrowdIn <https://translate.lizardbyte.dev/>`__. Once the translations are complete, a PR will be made
to merge the translations into Sunshine.
.. code-block:: yaml
- Use the string in a Vue component.
.. code-block:: html
- 'src/**'
<template>
<div>
<p>{{ $t('index.welcome') }}</p>
</div>
</template>
When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.
.. tip:: More formatting examples can be found in the
`Vue I18n guide <https://kazupon.github.io/vue-i18n/guide/formatting.html>`__.
**Extract, initialize, and update**
.. code-block:: bash
.. tab:: C++
python ./scripts/_locale.py --extract --init --update
There should be minimal cases where strings need to be extracted from C++ source code; however it may be necessary in
some situations. For example the system tray icon could be localized as it is user interfacing.
**Compile**
.. code-block:: bash
- Wrap the string to be extracted in a function as shown.
.. code-block:: cpp
python ./scripts/_locale.py --compile
#include <boost/locale.hpp>
#include <string>
std::string msg = boost::locale::translate("Hello world!");
.. tip:: More examples can be found in the documentation for
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.
.. warning:: This is for information only. Contributors should never include manually updated template files, or
manually compiled language files in Pull Requests.
Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
used by CrowdIn to generate language specific template files. The file is generated using the
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
any of the following paths are modified.
.. code-block:: yaml
- 'src/**'
When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.
**Extract, initialize, and update**
.. code-block:: bash
python ./scripts/_locale.py --extract --init --update
**Compile**
.. code-block:: bash
python ./scripts/_locale.py --compile
.. attention:: Due to the integration with Crowdin, it is important to not include any extracted or compiled files in
Pull Requests. The files are automatically generated and updated by the workflow. Once the PR is merged, the
translations can take place on `CrowdIn <https://translate.lizardbyte.dev/>`__. Once the translations are
complete, a PR will be made to merge the translations into Sunshine.

View File

@@ -59,5 +59,81 @@ Format inplace with rstfmt
Unit Testing
------------
.. todo:: Sunshine does not currently have any unit tests. If you would like to help us improve please get in contact
with us, or make a PR with suggested changes.
Sunshine uses `Google Test <https://github.com/google/googletest>`__ for unit testing. Google Test is included in the
repo as a submodule. The test sources are located in the `./tests` directory.
The tests need to be compiled into an executable, and then run. The tests are built using the normal build process, but
can be disabled by setting the `BUILD_TESTS` CMake option to `OFF`.
To run the tests, execute the following command from the build directory:
.. tab:: Linux
.. code-block:: bash
pushd tests
./test_sunshine
popd
.. tab:: macOS
.. code-block:: bash
pushd tests
./test_sunshine
popd
.. tab:: Windows
.. code-block:: bash
pushd tests
test_sunshine.exe
popd
To see all available options, run the tests with the `--help` option.
.. tab:: Linux
.. code-block:: bash
pushd tests
./test_sunshine --help
popd
.. tab:: macOS
.. code-block:: bash
pushd tests
./test_sunshine --help
popd
.. tab:: Windows
.. code-block:: bash
pushd tests
test_sunshine.exe --help
popd
Some tests rely on Python to run. CMake will search for Python and enable the docs tests if it is found, otherwise
cmake will fail. You can manually disable the tests by setting the `TESTS_ENABLE_PYTHON_TESTS` CMake option to
`OFF`.
.. tip::
See the googletest `FAQ <https://google.github.io/googletest/faq.html>`__ for more information on how to use
Google Test.
We use `gcovr <https://www.gcovr.com/>`__ to generate code coverage reports,
and `Codecov <https://about.codecov.io/>`__ to analyze the reports for all PRs and commits.
Codecov will fail a PR if the total coverage is reduced too much, or if not enough of the diff is covered by tests.
In some cases, the code cannot be covered when running the tests inside of GitHub runners. For example, any test that
needs access to the GPU will not be able to run. In these cases, the coverage can be omitted by adding comments to the
code. See the `gcovr documentation <https://gcovr.com/en/stable/guide/exclusion-markers.html#exclusion-markers>`__ for
more information.
Even if your changes cannot be covered in the CI, we still encourage you to write the tests for them. This will allow
maintainers to run the tests locally.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -4,12 +4,13 @@
"dev": "vite build --watch"
},
"dependencies": {
"@fortawesome/fontawesome-free": "6.5.1",
"@fortawesome/fontawesome-free": "6.5.2",
"@popperjs/core": "2.11.8",
"@vitejs/plugin-vue": "4.6.2",
"bootstrap": "5.3.3",
"vite": "4.5.2",
"vite-plugin-ejs": "1.6.4",
"vue": "3.4.5"
"vue": "3.4.23",
"vue-i18n": "9.13.0"
}
}

View File

@@ -7,7 +7,7 @@ pkgrel=1
pkgdesc="@PROJECT_DESCRIPTION@"
arch=('x86_64' 'aarch64')
url=@PROJECT_HOMEPAGE_URL@
license=('GPL3')
license=('GPL-3.0-only')
install=sunshine.install
depends=('avahi'
@@ -31,16 +31,21 @@ depends=('avahi'
'numactl'
'openssl'
'opus'
'python'
'udev')
checkdepends=('doxygen'
'graphviz')
makedepends=('boost'
'cmake'
'gcc12'
'git'
'make'
'nodejs'
'npm')
optdepends=('cuda: Nvidia GPU encoding support'
'libva-mesa-driver: AMD GPU encoding support'
'intel-media-driver: Intel GPU encoding support')
'intel-media-driver: Intel GPU encoding support'
'xorg-server-xvfb: Virtual X server for headless testing')
provides=('sunshine')
@@ -57,6 +62,9 @@ build() {
export BUILD_VERSION="@GITHUB_BUILD_VERSION@"
export COMMIT="@GITHUB_COMMIT@"
export CC=gcc-12
export CXX=g++-12
export CFLAGS="${CFLAGS/-Werror=format-security/}"
export CXXFLAGS="${CXXFLAGS/-Werror=format-security/}"
@@ -72,6 +80,14 @@ build() {
make -C build
}
check() {
export CC=gcc-12
export CXX=g++-12
cd "${srcdir}/build/tests"
./test_sunshine --gtest_color=yes
}
package() {
make -C build install DESTDIR="$pkgdir"
}

View File

@@ -34,6 +34,8 @@ build-options:
prepend-ld-library-path: /usr/lib/sdk/vala/lib
modules:
- "org.flatpak.Builder.BaseApp/xvfb.json"
- name: boost
disabled: false
buildsystem: simple
@@ -327,6 +329,9 @@ modules:
build-args:
- --share=network
env:
BUILD_VERSION: "@BUILD_VERSION@"
BRANCH: "@GITHUB_BRANCH@"
COMMIT: "@GITHUB_COMMIT@"
npm_config_nodedir: /usr/lib/sdk/node18
NPM_CONFIG_LOGLEVEL: info
config-opts:
@@ -341,6 +346,7 @@ modules:
- -DSUNSHINE_ENABLE_DRM=ON
- -DSUNSHINE_ENABLE_CUDA=ON
- -DSUNSHINE_BUILD_FLATPAK=ON
- -DTESTS_ENABLE_PYTHON_TESTS=OFF
sources:
- type: git
url: "@GITHUB_CLONE_URL@"
@@ -358,3 +364,7 @@ modules:
's%/app/bin/sunshine%flatpak run dev.lizardbyte.sunshine\nExecStop=flatpak kill dev.lizardbyte.sunshine%g'
/app/share/sunshine/systemd/user/sunshine.service
- install -D $FLATPAK_BUILDER_BUILDDIR/packaging/linux/flatpak/scripts/* /app/bin
run-tests: true
test-rule: "" # empty to disable
test-commands:
- xvfb-run tests/test_sunshine --gtest_color=yes

View File

@@ -31,13 +31,19 @@ post-fetch {
system -W ${worksrcpath} "${git.cmd} submodule update --init --recursive"
}
# https://guide.macports.org/chunked/reference.dependencies.html
depends_build-append port:npm9 \
port:pkgconfig
depends_lib port:avahi \
port:curl \
port:libopus \
port:miniupnpc
port:miniupnpc \
port:python311 \
port:py311-pip
depends_test port:doxygen \
port:graphviz
boost.version 1.81
@@ -45,6 +51,10 @@ configure.args -DBUILD_WERROR=ON \
-DCMAKE_INSTALL_PREFIX=${prefix} \
-DSUNSHINE_ASSETS_DIR=etc/sunshine/assets
configure.env-append BRANCH=@GITHUB_BRANCH@
configure.env-append BUILD_VERSION=@BUILD_VERSION@
configure.env-append COMMIT=@GITHUB_COMMIT@
startupitem.create yes
startupitem.executable "${prefix}/bin/{$name}"
startupitem.location LaunchDaemons
@@ -62,3 +72,9 @@ notes-append "Run @PROJECT_NAME@ by executing 'sunshine <path to user config>',
notes-append "The config file will be created if it doesn't exist."
notes-append "It is recommended to set a location for the apps file in the config."
notes-append "See our documentation at 'https://docs.lizardbyte.dev/projects/sunshine/en/v@PROJECT_VERSION@/' for further info."
test.run yes
test.dir ${build.dir}/tests
test.target ""
test.cmd ./test_sunshine
test.args --gtest_color=yes

View File

@@ -7,30 +7,36 @@ class @PROJECT_NAME@ < Formula
tag: "@GITHUB_BRANCH@"
version "@PROJECT_VERSION@"
license all_of: ["GPL-3.0-only"]
head "@GITHUB_CLONE_URL@", branch: "nightly"
head "@GITHUB_CLONE_URL@", branch: "@GITHUB_DEFAULT_BRANCH@"
depends_on "boost" => :build
depends_on "cmake" => :build
depends_on "node" => :build
depends_on "pkg-config" => :build
depends_on "curl"
depends_on "miniupnpc"
depends_on "node"
depends_on "openssl"
depends_on "opus"
def install
ENV["BRANCH"] = "@GITHUB_BRANCH@"
ENV["BUILD_VERSION"] = "@BUILD_VERSION@"
ENV["COMMIT"] = "@GITHUB_COMMIT@"
args = %W[
-DBUIld_WERROR=ON
-DBUILD_WERROR=ON
-DCMAKE_INSTALL_PREFIX=#{prefix}
-DOPENSSL_ROOT_DIR=#{Formula["openssl"].opt_prefix}
-DSUNSHINE_ASSETS_DIR=sunshine/assets
-DSUNSHINE_BUILD_HOMEBREW=ON
-DTESTS_ENABLE_PYTHON_TESTS=OFF
]
system "cmake", "-S", ".", "-B", "build", *std_cmake_args, *args
cd "build" do
system "make", "-j"
system "make", "install"
bin.install "tests/test_sunshine"
end
end
@@ -54,9 +60,10 @@ class @PROJECT_NAME@ < Formula
test do
# test that the binary runs at all
output = shell_output("#{bin}/sunshine --version").strip
puts output
system "#{bin}/sunshine", "--version"
# TODO: add unit tests
# run the test suite
# cannot build tests with python tests because homebrew destroys the source directory
system "#{bin}/test_sunshine", "--gtest_color=yes"
end
end

View File

@@ -22,16 +22,20 @@ project_dir = os.path.join(root_dir, 'src')
year = datetime.datetime.now().year
# retroarcher target locales
# target locales
target_locales = [
'de', # Deutsch
'de', # German
'en', # English
'en_GB', # English (United Kingdom)
'en_US', # English (United States)
'es', # español
'fr', # français
'it', # italiano
'ru', # русский
'es', # Spanish
'fr', # French
'it', # Italian
'ja', # Japanese
'pt', # Portuguese
'ru', # Russian
'sv', # Swedish
'zh', # Chinese
]

View File

@@ -1 +1,2 @@
Babel==2.14.0
clang-format

View File

@@ -5,6 +5,7 @@ import subprocess
# variables
directories = [
'src',
'tests',
'tools',
os.path.join('third-party', 'glad'),
os.path.join('third-party', 'nvfbc'),

View File

@@ -9,6 +9,7 @@
#include <iostream>
#include <thread>
#include <unordered_map>
#include <utility>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
@@ -85,14 +86,17 @@ namespace config {
#define AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY 1
#define AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY 2
#define AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM 3
#define AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING 0
#define AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY_HIGH_QUALITY 5
#define AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING 0
#define AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY 1
#define AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY 2
#define AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM 3
#define AMF_VIDEO_ENCODER_USAGE_TRANSCONDING 0
#define AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY 5
#define AMF_VIDEO_ENCODER_USAGE_TRANSCODING 0
#define AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY 1
#define AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY 2
#define AMF_VIDEO_ENCODER_USAGE_WEBCAM 3
#define AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY 5
#define AMF_VIDEO_ENCODER_UNDEFINED 0
#define AMF_VIDEO_ENCODER_CABAC 1
#define AMF_VIDEO_ENCODER_CALV 2
@@ -121,43 +125,46 @@ namespace config {
};
enum class rc_av1_e : int {
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};
enum class rc_hevc_e : int {
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};
enum class rc_h264_e : int {
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR,
cqp = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP,
vbr_latency = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
};
enum class usage_av1_e : int {
transcoding = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING,
webcam = AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM,
lowlatency_high_quality = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY_HIGH_QUALITY,
lowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY,
ultralowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY
};
enum class usage_hevc_e : int {
transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING,
transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING,
webcam = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM,
lowlatency_high_quality = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY,
lowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY,
ultralowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY
};
enum class usage_h264_e : int {
transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING,
transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCODING,
webcam = AMF_VIDEO_ENCODER_USAGE_WEBCAM,
lowlatency_high_quality = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY,
lowlatency = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY,
ultralowlatency = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY
};
@@ -170,40 +177,41 @@ namespace config {
template <class T>
std::optional<int>
quality_from_view(const std::string_view &quality_type) {
quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (quality_type == #x##sv) return (int) T::x
_CONVERT_(balanced);
_CONVERT_(quality);
_CONVERT_(speed);
_CONVERT_(balanced);
#undef _CONVERT_
return std::nullopt;
return original;
}
template <class T>
std::optional<int>
rc_from_view(const std::string_view &rc) {
rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (rc == #x##sv) return (int) T::x
_CONVERT_(cbr);
_CONVERT_(cqp);
_CONVERT_(vbr_latency);
_CONVERT_(vbr_peak);
_CONVERT_(cbr);
#undef _CONVERT_
return std::nullopt;
return original;
}
template <class T>
std::optional<int>
usage_from_view(const std::string_view &rc) {
usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) {
#define _CONVERT_(x) \
if (rc == #x##sv) return (int) T::x
_CONVERT_(transcoding);
_CONVERT_(webcam);
if (usage == #x##sv) return (int) T::x
_CONVERT_(lowlatency);
_CONVERT_(lowlatency_high_quality);
_CONVERT_(transcoding);
_CONVERT_(ultralowlatency);
_CONVERT_(webcam);
#undef _CONVERT_
return std::nullopt;
return original;
}
int
@@ -212,7 +220,7 @@ namespace config {
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
return -1;
return _auto;
}
} // namespace amd
@@ -343,15 +351,16 @@ namespace config {
}, // qsv
{
(int) amd::quality_h264_e::balanced, // quality (h264)
(int) amd::quality_hevc_e::balanced, // quality (hevc)
(int) amd::quality_av1_e::balanced, // quality (av1)
(int) amd::rc_h264_e::vbr_latency, // rate control (h264)
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc)
(int) amd::rc_av1_e::vbr_latency, // rate control (av1)
(int) amd::usage_h264_e::ultralowlatency, // usage (h264)
(int) amd::usage_hevc_e::ultralowlatency, // usage (hevc)
(int) amd::usage_av1_e::ultralowlatency, // usage (av1)
(int) amd::rc_h264_e::vbr_latency, // rate control (h264)
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc)
(int) amd::rc_av1_e::vbr_latency, // rate control (av1)
0, // enforce_hrd
(int) amd::quality_h264_e::balanced, // quality (h264)
(int) amd::quality_hevc_e::balanced, // quality (hevc)
(int) amd::quality_av1_e::balanced, // quality (av1)
0, // preanalysis
1, // vbaq
(int) amd::coder_e::_auto, // coder
@@ -441,6 +450,7 @@ namespace config {
};
sunshine_t sunshine {
"en", // locale
2, // min_log_level
0, // flags
{}, // User file
@@ -973,30 +983,31 @@ namespace config {
std::string quality;
string_f(vars, "amd_quality", quality);
if (!quality.empty()) {
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality);
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality);
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality);
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality, video.amd.amd_quality_h264);
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality, video.amd.amd_quality_hevc);
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality, video.amd.amd_quality_av1);
}
std::string rc;
string_f(vars, "amd_rc", rc);
int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view);
if (!rc.empty()) {
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc);
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc);
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc);
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc, video.amd.amd_rc_h264);
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc, video.amd.amd_rc_hevc);
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc, video.amd.amd_rc_av1);
}
std::string usage;
string_f(vars, "amd_usage", usage);
if (!usage.empty()) {
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(rc);
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(rc);
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(rc);
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(usage, video.amd.amd_usage_h264);
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(usage, video.amd.amd_usage_hevc);
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(usage, video.amd.amd_usage_av1);
}
bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis);
bool_f(vars, "amd_vbaq", (bool &) video.amd.amd_vbaq);
bool_f(vars, "amd_enforce_hrd", (bool &) video.amd.amd_enforce_hrd);
int_f(vars, "vt_coder", video.vt.vt_coder, vt::coder_from_view);
int_f(vars, "vt_software", video.vt.vt_allow_sw, vt::allow_software_from_view);
@@ -1101,6 +1112,21 @@ namespace config {
config::sunshine.flags[config::flag::UPNP].flip();
}
string_restricted_f(vars, "locale", config::sunshine.locale, {
"de"sv, // German
"en"sv, // English
"en_GB"sv, // English (UK)
"en_US"sv, // English (US)
"es"sv, // Spanish
"fr"sv, // French
"it"sv, // Italian
"ja"sv, // Japanese
"pt"sv, // Portuguese
"ru"sv, // Russian
"sv"sv, // Swedish
"zh"sv, // Chinese
});
std::string log_level_string;
string_f(vars, "min_log_level", log_level_string);
@@ -1161,7 +1187,7 @@ namespace config {
auto line = argv[x];
if (line == "--help"sv) {
print_help(*argv);
logging::print_help(*argv);
return 1;
}
#ifdef _WIN32
@@ -1181,7 +1207,7 @@ namespace config {
break;
}
if (apply_flags(line + 1)) {
print_help(*argv);
logging::print_help(*argv);
return -1;
}
}
@@ -1195,7 +1221,7 @@ namespace config {
else {
TUPLE_EL(var, 1, parse_option(line, line_end));
if (!var) {
print_help(*argv);
logging::print_help(*argv);
return -1;
}

View File

@@ -48,15 +48,16 @@ namespace config {
} qsv;
struct {
std::optional<int> amd_quality_h264;
std::optional<int> amd_quality_hevc;
std::optional<int> amd_quality_av1;
std::optional<int> amd_rc_h264;
std::optional<int> amd_rc_hevc;
std::optional<int> amd_rc_av1;
std::optional<int> amd_usage_h264;
std::optional<int> amd_usage_hevc;
std::optional<int> amd_usage_av1;
std::optional<int> amd_rc_h264;
std::optional<int> amd_rc_hevc;
std::optional<int> amd_rc_av1;
std::optional<int> amd_enforce_hrd;
std::optional<int> amd_quality_h264;
std::optional<int> amd_quality_hevc;
std::optional<int> amd_quality_av1;
std::optional<int> amd_preanalysis;
std::optional<int> amd_vbaq;
int amd_coder;
@@ -160,6 +161,7 @@ namespace config {
bool elevated;
};
struct sunshine_t {
std::string locale;
int min_log_level;
std::bitset<flag::FLAG_SIZE> flags;
std::string credentials_file;

View File

@@ -25,6 +25,7 @@
#include <Simple-Web-Server/crypto.hpp>
#include <Simple-Web-Server/server_https.hpp>
#include <boost/asio/ssl/context_base.hpp>
#include <jwt-cpp/jwt.h>
#include "config.h"
#include "confighttp.h"
@@ -47,6 +48,8 @@ namespace confighttp {
namespace fs = std::filesystem;
namespace pt = boost::property_tree;
std::string jwt_key;
using https_server_t = SimpleWeb::Server<SimpleWeb::HTTPS>;
using args_t = SimpleWeb::CaseInsensitiveMultimap;
@@ -64,7 +67,7 @@ namespace confighttp {
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
for (auto &[name, val] : request->header) {
BOOST_LOG(debug) << name << " -- " << (name == "Authorization" ? "CREDENTIALS REDACTED" : val);
BOOST_LOG(debug) << name << " -- " << (name == "Cookie" || name == "Authorization" ? "SENSIBLE HEADER REDACTED" : val);
}
BOOST_LOG(debug) << " [--] "sv;
@@ -80,9 +83,7 @@ namespace confighttp {
send_unauthorized(resp_https_t response, req_https_t request) {
auto address = net::addr_to_normalized_string(request->remote_endpoint().address());
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv;
const SimpleWeb::CaseInsensitiveMultimap headers {
{ "WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")" }
};
const SimpleWeb::CaseInsensitiveMultimap headers {};
response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers);
}
@@ -114,29 +115,46 @@ namespace confighttp {
}
auto fg = util::fail_guard([&]() {
send_unauthorized(response, request);
BOOST_LOG(info) << request->path;
std::string apiPrefix = "/api";
if (request->path.compare(0, apiPrefix.length(), apiPrefix) == 0) {
send_unauthorized(response, request);
}
// Redirect to login, but only once
else if (request->path.compare("/login") != 0) {
send_redirect(response, request, "/login");
}
});
auto auth = request->header.find("authorization");
auto auth = request->header.find("cookie");
if (auth == request->header.end()) {
return false;
}
auto &rawAuth = auth->second;
auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length()));
std::istringstream iss(rawAuth);
std::string token, cookie_name = "sunshine_session=", cookie_value = "";
int index = authData.find(':');
if (index >= authData.size() - 1) {
return false;
while (std::getline(iss, token, ';')) {
// Left Trim Cookie
token.erase(token.begin(), std::find_if(token.begin(), token.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
// Compare that the cookie name is sunshine_session
if (token.compare(0, cookie_name.length(), cookie_name) == 0) {
cookie_value = token.substr(cookie_name.length());
break;
}
}
auto username = authData.substr(0, index);
auto password = authData.substr(index + 1);
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
if (cookie_value.length() == 0) return false;
auto decoded = jwt::decode(cookie_value);
auto verifier = jwt::verify()
.with_issuer("sunshine-" + http::unique_id)
.with_claim("sub", jwt::claim(std::string(config::sunshine.username)))
.allow_algorithm(jwt::algorithm::hs256 { jwt_key });
if (!boost::iequals(username, config::sunshine.username) || hash != config::sunshine.password) {
return false;
}
verifier.verify(decoded);
fg.disable();
return true;
@@ -181,6 +199,16 @@ namespace confighttp {
response->write(content, headers);
}
void
getLoginPage(resp_https_t response, req_https_t request) {
print_req(request);
std::string content = file_handler::read_file(WEB_DIR "login.html");
SimpleWeb::CaseInsensitiveMultimap headers;
headers.emplace("Content-Type", "text/html; charset=utf-8");
response->write(content, headers);
}
void
getAppsPage(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;
@@ -550,6 +578,24 @@ namespace confighttp {
}
}
void
getLocale(resp_https_t response, req_https_t request) {
// we need to return the locale whether authenticated or not
print_req(request);
pt::ptree outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(data.str());
});
outputTree.put("status", "true");
outputTree.put("locale", config::sunshine.locale);
}
void
saveConfig(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;
@@ -637,6 +683,8 @@ namespace confighttp {
else {
http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword);
http::reload_user_creds(config::sunshine.credentials_file);
// Regen the JWT Key to invalidate sessions
jwt_key = crypto::rand_alphabet(64);
outputTree.put("status", true);
}
}
@@ -720,16 +768,112 @@ namespace confighttp {
outputTree.put("status", true);
}
void
login(resp_https_t response, req_https_t request) {
auto address = net::addr_to_normalized_string(request->remote_endpoint().address());
auto ip_type = net::from_address(address);
if (ip_type > http::origin_web_ui_allowed) {
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- denied"sv;
response->write(SimpleWeb::StatusCode::client_error_forbidden);
return;
}
std::stringstream ss;
ss << request->content.rdbuf();
pt::ptree inputTree, outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(data.str());
});
try {
// TODO: Input Validation
pt::read_json(ss, inputTree);
auto username = inputTree.get<std::string>("username");
auto password = inputTree.get<std::string>("password");
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
if (!boost::iequals(username, config::sunshine.username) || hash != config::sunshine.password) {
outputTree.put("status", "false");
return;
}
outputTree.put("status", "true");
auto token = jwt::create().set_type("JWT").set_issued_at(std::chrono::system_clock::now()).set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds { 3600 }).set_issuer("sunshine-" + http::unique_id).set_payload_claim("sub", jwt::claim(std::string(config::sunshine.username))).sign(jwt::algorithm::hs256 { jwt_key });
std::stringstream cookie_stream;
cookie_stream << "sunshine_session=";
cookie_stream << token;
cookie_stream << "; Secure; HttpOnly; SameSite=Strict; Path=/";
const SimpleWeb::CaseInsensitiveMultimap headers {
{ "Set-Cookie", cookie_stream.str() }
};
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(SimpleWeb::StatusCode::success_ok, data.str(), headers);
g.disable();
return;
}
catch (std::exception &e) {
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
outputTree.put("status", "false");
outputTree.put("error", "Invalid Input JSON");
return;
}
outputTree.put("status", "true");
}
void
logout(resp_https_t response, req_https_t request) {
pt::ptree outputTree;
try {
if (!authenticate(response, request)) return;
print_req(request);
auto g = util::fail_guard([&]() {
std::ostringstream data;
pt::write_json(data, outputTree);
response->write(data.str());
});
const SimpleWeb::CaseInsensitiveMultimap headers {
{ "Set-Cookie", "sunshine_session=redacted; expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict; Path=/" }
};
std::ostringstream data;
outputTree.put("status", true);
pt::write_json(data, outputTree);
response->write(SimpleWeb::StatusCode::success_ok, data.str(), headers);
g.disable();
}
catch (std::exception &e) {
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
outputTree.put("status", "false");
outputTree.put("error", "Invalid Input JSON");
return;
}
}
void
start() {
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
// On each server start, create a randomized jwt_key
jwt_key = crypto::rand_alphabet(64);
auto port_https = net::map_port(PORT_HTTPS);
auto address_family = net::af_from_enum_string(config::sunshine.address_family);
https_server_t server { config::nvhttp.cert, config::nvhttp.pkey };
server.default_resource["GET"] = not_found;
server.resource["^/$"]["GET"] = getIndexPage;
server.resource["^/login/?$"]["GET"] = getLoginPage;
server.resource["^/pin/?$"]["GET"] = getPinPage;
server.resource["^/apps/?$"]["GET"] = getAppsPage;
server.resource["^/clients/?$"]["GET"] = getClientsPage;
@@ -743,12 +887,15 @@ namespace confighttp {
server.resource["^/api/apps$"]["POST"] = saveApp;
server.resource["^/api/config$"]["GET"] = getConfig;
server.resource["^/api/config$"]["POST"] = saveConfig;
server.resource["^/api/configLocale$"]["GET"] = getLocale;
server.resource["^/api/restart$"]["POST"] = restart;
server.resource["^/api/password$"]["POST"] = savePassword;
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
server.resource["^/api/clients/unpair$"]["POST"] = unpairAll;
server.resource["^/api/apps/close$"]["POST"] = closeApp;
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
server.resource["^/api/logout$"]["POST"] = logout;
server.resource["^/api/login$"]["POST"] = login;
server.resource["^/images/sunshine.ico$"]["GET"] = getFaviconImage;
server.resource["^/images/logo-sunshine-45.png$"]["GET"] = getSunshineLogoImage;
server.resource["^/assets\\/.+$"]["GET"] = getNodeModules;

View File

@@ -17,6 +17,10 @@ namespace crypto {
X509_STORE_add_cert(x509_store.get(), cert.get());
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
}
void
cert_chain_t::clear() {
_certs.clear();
}
static int
openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {

View File

@@ -73,6 +73,9 @@ namespace crypto {
void
add(x509_t &&cert);
void
clear();
const char *
verify(x509_t::element_type *cert);

View File

@@ -87,12 +87,12 @@ namespace args {
*
* EXAMPLES:
* ```cpp
* print_help("sunshine", 0, nullptr);
* help("sunshine", 0, nullptr);
* ```
*/
int
help(const char *name, int argc, char *argv[]) {
print_help(name);
logging::print_help(name);
return 0;
}
@@ -109,7 +109,7 @@ namespace args {
*/
int
version(const char *name, int argc, char *argv[]) {
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl;
// version was already logged at startup
return 0;
}

View File

@@ -7,6 +7,7 @@
#include "process.h"
#include <filesystem>
#include <utility>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

View File

@@ -469,15 +469,19 @@ namespace input {
* @param input The input context.
* @param val The cartesian coordinate pair to convert.
* @param size The size of the client's surface containing the value.
* @return The host-relative coordinate pair.
* @return The host-relative coordinate pair if a touchport is available.
*/
std::pair<float, float>
std::optional<std::pair<float, float>>
client_to_touchport(std::shared_ptr<input_t> &input, const std::pair<float, float> &val, const std::pair<float, float> &size) {
auto &touch_port_event = input->touch_port_event;
auto &touch_port = input->touch_port;
if (touch_port_event->peek()) {
touch_port = *touch_port_event->pop();
}
if (!touch_port) {
BOOST_LOG(verbose) << "Ignoring early absolute input without a touch port"sv;
return std::nullopt;
}
auto scalarX = touch_port.width / size.first;
auto scalarY = touch_port.height / size.second;
@@ -491,7 +495,7 @@ namespace input {
x = std::clamp(x, offsetX, (size.first * scalarX) - offsetX);
y = std::clamp(y, offsetY, (size.second * scalarY) - offsetY);
return { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv };
return std::pair { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv };
}
/**
@@ -561,6 +565,9 @@ namespace input {
auto height = (float) util::endian::big(packet->height);
auto tpcoords = client_to_touchport(input, { x, y }, { width, height });
if (!tpcoords) {
return;
}
auto &touch_port = input->touch_port;
platf::touch_port_t abs_port {
@@ -568,7 +575,7 @@ namespace input {
touch_port.env_width, touch_port.env_height
};
platf::abs_mouse(platf_input, abs_port, tpcoords.first, tpcoords.second);
platf::abs_mouse(platf_input, abs_port, tpcoords->first, tpcoords->second);
}
void
@@ -918,6 +925,9 @@ namespace input {
{ from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f,
from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f },
{ 65535.f, 65535.f });
if (!coords) {
return;
}
auto &touch_port = input->touch_port;
platf::touch_port_t abs_port {
@@ -926,8 +936,8 @@ namespace input {
};
// Renormalize the coordinates
coords.first /= abs_port.width;
coords.second /= abs_port.height;
coords->first /= abs_port.width;
coords->second /= abs_port.height;
// Normalize rotation value to 0-359 degree range
auto rotation = util::endian::little(packet->rotation);
@@ -946,8 +956,8 @@ namespace input {
packet->eventType,
rotation,
util::endian::little(packet->pointerId),
coords.first,
coords.second,
coords->first,
coords->second,
from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f),
contact_area.first,
contact_area.second,
@@ -972,6 +982,9 @@ namespace input {
{ from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f,
from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f },
{ 65535.f, 65535.f });
if (!coords) {
return;
}
auto &touch_port = input->touch_port;
platf::touch_port_t abs_port {
@@ -980,8 +993,8 @@ namespace input {
};
// Renormalize the coordinates
coords.first /= abs_port.width;
coords.second /= abs_port.height;
coords->first /= abs_port.width;
coords->second /= abs_port.height;
// Normalize rotation value to 0-359 degree range
auto rotation = util::endian::little(packet->rotation);
@@ -1002,8 +1015,8 @@ namespace input {
packet->penButtons,
packet->tilt,
rotation,
coords.first,
coords.second,
coords->first,
coords->second,
from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f),
contact_area.first,
contact_area.second,

View File

@@ -32,6 +32,11 @@ namespace input {
float client_offsetX, client_offsetY;
float scalar_inv;
explicit
operator bool() const {
return width != 0 && height != 0 && env_width != 0 && env_height != 0;
}
};
std::pair<float, float>

View File

@@ -4,9 +4,11 @@
*/
// standard includes
#include <fstream>
#include <iostream>
// lib includes
#include <boost/core/null_deleter.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
@@ -16,6 +18,10 @@
// local includes
#include "logging.h"
extern "C" {
#include <libavutil/log.h>
}
using namespace std::literals;
namespace bl = boost::log;
@@ -29,45 +35,182 @@ bl::sources::severity_logger<int> warning(3); // Strange events
bl::sources::severity_logger<int> error(4); // Recoverable errors
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors
/**
* @brief Flush the log.
*
* EXAMPLES:
* ```cpp
* log_flush();
* ```
*/
void
log_flush() {
sink->flush();
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
/**
* @brief Print help to stdout.
* @param name The name of the program.
*
* EXAMPLES:
* ```cpp
* print_help("sunshine");
* ```
*/
void
print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
namespace logging {
/**
* @brief A destructor that restores the initial state.
*/
deinit_t::~deinit_t() {
deinit();
}
/**
* @brief Deinitialize the logging system.
*
* EXAMPLES:
* ```cpp
* deinit();
* ```
*/
void
deinit() {
log_flush();
bl::core::get()->remove_sink(sink);
sink.reset();
}
/**
* @brief Initialize the logging system.
* @param min_log_level The minimum log level to output.
* @param log_file The log file to write to.
* @returns A deinit_t object that will deinitialize the logging system when it goes out of scope.
*
* EXAMPLES:
* ```cpp
* log_init(2, "sunshine.log");
* ```
*/
[[nodiscard]] std::unique_ptr<deinit_t>
init(int min_log_level, const std::string &log_file) {
if (sink) {
// Deinitialize the logging system before reinitializing it. This can probably only ever be hit in tests.
deinit();
}
setup_av_logging(min_log_level);
sink = boost::make_shared<text_sink>();
boost::shared_ptr<std::ostream> stream { &std::cout, boost::null_deleter() };
sink->locked_backend()->add_stream(stream);
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(log_file));
sink->set_filter(severity >= min_log_level);
sink->set_formatter([](const bl::record_view &view, bl::formatting_ostream &os) {
constexpr const char *message = "Message";
constexpr const char *severity = "Severity";
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
auto log_level = view.attribute_values()[severity].extract<int>().get();
std::string_view log_type;
switch (log_level) {
case 0:
log_type = "Verbose: "sv;
break;
case 1:
log_type = "Debug: "sv;
break;
case 2:
log_type = "Info: "sv;
break;
case 3:
log_type = "Warning: "sv;
break;
case 4:
log_type = "Error: "sv;
break;
case 5:
log_type = "Fatal: "sv;
break;
};
char _date[DATE_BUFFER_SIZE];
std::time_t t = std::time(nullptr);
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));
os << _date << log_type << view.attribute_values()[message].extract<std::string>();
});
// Flush after each log record to ensure log file contents on disk isn't stale.
// This is particularly important when running from a Windows service.
sink->locked_backend()->auto_flush(true);
bl::core::get()->add_sink(sink);
return std::make_unique<deinit_t>();
}
/**
* @brief Setup AV logging.
* @param min_log_level The log level.
*/
void
setup_av_logging(int min_log_level) {
if (min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
}
else {
av_log_set_level(AV_LOG_DEBUG);
}
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
static int print_prefix = 1;
char buffer[1024];
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
if (level <= AV_LOG_ERROR) {
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
// are expected in some cases, such as lack of codec support or similar things.
BOOST_LOG(error) << buffer;
}
else if (level <= AV_LOG_WARNING) {
BOOST_LOG(warning) << buffer;
}
else if (level <= AV_LOG_INFO) {
BOOST_LOG(info) << buffer;
}
else if (level <= AV_LOG_VERBOSE) {
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
BOOST_LOG(debug) << buffer;
}
else {
BOOST_LOG(verbose) << buffer;
}
});
}
/**
* @brief Flush the log.
*
* EXAMPLES:
* ```cpp
* log_flush();
* ```
*/
void
log_flush() {
if (sink) {
sink->flush();
}
}
/**
* @brief Print help to stdout.
* @param name The name of the program.
*
* EXAMPLES:
* ```cpp
* print_help("sunshine");
* ```
*/
void
print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
} // namespace logging

View File

@@ -10,7 +10,6 @@
#include <boost/log/common.hpp>
#include <boost/log/sinks.hpp>
extern boost::shared_ptr<boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>> sink;
using text_sink = boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>;
extern boost::log::sources::severity_logger<int> verbose;
@@ -20,8 +19,20 @@ extern boost::log::sources::severity_logger<int> warning;
extern boost::log::sources::severity_logger<int> error;
extern boost::log::sources::severity_logger<int> fatal;
// functions
void
log_flush();
void
print_help(const char *name);
namespace logging {
class deinit_t {
public:
~deinit_t();
};
void
deinit();
[[nodiscard]] std::unique_ptr<deinit_t>
init(int min_log_level, const std::string &log_file);
void
setup_av_logging(int min_log_level);
void
log_flush();
void
print_help(const char *name);
} // namespace logging

View File

@@ -4,13 +4,11 @@
*/
// standard includes
#include <codecvt>
#include <csignal>
#include <fstream>
#include <iostream>
// lib includes
#include <boost/log/expressions.hpp>
// local includes
#include "confighttp.h"
#include "entry_handler.h"
@@ -26,19 +24,10 @@
#include "video.h"
extern "C" {
#include <libavutil/log.h>
#include <rs.h>
}
using namespace std::literals;
namespace bl = boost::log;
struct NoDelete {
void
operator()(void *) {}
};
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
std::map<int, std::function<void()>> signal_handlers;
void
@@ -118,84 +107,10 @@ main(int argc, char *argv[]) {
return 0;
}
if (config::sunshine.min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
auto log_deinit_guard = logging::init(config::sunshine.min_log_level, config::sunshine.log_file);
if (!log_deinit_guard) {
BOOST_LOG(error) << "Logging failed to initialize"sv;
}
else {
av_log_set_level(AV_LOG_DEBUG);
}
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
static int print_prefix = 1;
char buffer[1024];
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
if (level <= AV_LOG_ERROR) {
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
// are expected in some cases, such as lack of codec support or similar things.
BOOST_LOG(error) << buffer;
}
else if (level <= AV_LOG_WARNING) {
BOOST_LOG(warning) << buffer;
}
else if (level <= AV_LOG_INFO) {
BOOST_LOG(info) << buffer;
}
else if (level <= AV_LOG_VERBOSE) {
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
BOOST_LOG(debug) << buffer;
}
else {
BOOST_LOG(verbose) << buffer;
}
});
sink = boost::make_shared<text_sink>();
boost::shared_ptr<std::ostream> stream { &std::cout, NoDelete {} };
sink->locked_backend()->add_stream(stream);
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(config::sunshine.log_file));
sink->set_filter(severity >= config::sunshine.min_log_level);
sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
auto log_level = view.attribute_values()[severity].extract<int>().get();
std::string_view log_type;
switch (log_level) {
case 0:
log_type = "Verbose: "sv;
break;
case 1:
log_type = "Debug: "sv;
break;
case 2:
log_type = "Info: "sv;
break;
case 3:
log_type = "Warning: "sv;
break;
case 4:
log_type = "Error: "sv;
break;
case 5:
log_type = "Fatal: "sv;
break;
};
char _date[DATE_BUFFER_SIZE];
std::time_t t = std::time(nullptr);
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));
os << _date << log_type << view.attribute_values()[message].extract<std::string>();
});
// Flush after each log record to ensure log file contents on disk isn't stale.
// This is particularly important when running from a Windows service.
sink->locked_backend()->auto_flush(true);
bl::core::get()->add_sink(sink);
auto fg = util::fail_guard(log_flush);
// logging can begin at this point
// if anything is logged prior to this point, it will appear in stdout, but not in the log viewer in the UI
@@ -227,7 +142,7 @@ main(int argc, char *argv[]) {
nvprefs_instance.modify_application_profile();
// Modify global settings, undo file is produced in the process to restore after improper termination
nvprefs_instance.modify_global_profile();
// Unload dynamic library to survive driver reinstallation
// Unload dynamic library to survive driver re-installation
nvprefs_instance.unload();
}
@@ -320,7 +235,7 @@ main(int argc, char *argv[]) {
auto task = []() {
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
log_flush();
logging::log_flush();
lifetime::debug_trap();
};
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
@@ -333,7 +248,7 @@ main(int argc, char *argv[]) {
auto task = []() {
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
log_flush();
logging::log_flush();
lifetime::debug_trap();
};
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
@@ -346,8 +261,8 @@ main(int argc, char *argv[]) {
// If any of the following fail, we log an error and continue event though sunshine will not function correctly.
// This allows access to the UI to fix configuration problems or view the logs.
auto deinit_guard = platf::init();
if (!deinit_guard) {
auto platf_deinit_guard = platf::init();
if (!platf_deinit_guard) {
BOOST_LOG(error) << "Platform failed to initialize"sv;
}

View File

@@ -7,6 +7,7 @@
#include "logging.h"
#include "utility.h"
#include <algorithm>
#include <sstream>
using namespace std::literals;
@@ -169,7 +170,9 @@ namespace net {
addr_to_url_escaped_string(boost::asio::ip::address address) {
address = normalize_address(address);
if (address.is_v6()) {
return "["s + address.to_string() + ']';
std::stringstream ss;
ss << '[' << address.to_string() << ']';
return ss.str();
}
else {
return address.to_string();

View File

@@ -5,6 +5,7 @@
#pragma once
#include <tuple>
#include <utility>
#include <boost/asio.hpp>

View File

@@ -8,6 +8,7 @@
// standard includes
#include <filesystem>
#include <utility>
// lib includes
#include <Simple-Web-Server/server_http.hpp>
@@ -42,6 +43,8 @@ namespace nvhttp {
namespace fs = std::filesystem;
namespace pt = boost::property_tree;
crypto::cert_chain_t cert_chain;
class SunshineHttpsServer: public SimpleWeb::Server<SimpleWeb::HTTPS> {
public:
SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file):
@@ -1017,7 +1020,6 @@ namespace nvhttp {
conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str());
crypto::cert_chain_t cert_chain;
for (auto &[_, client] : map_id_client) {
for (auto &cert : client.certs) {
cert_chain.add(crypto::x509(cert));
@@ -1026,15 +1028,15 @@ namespace nvhttp {
auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>(30);
// /resume doesn't always get the parameter "localAudioPlayMode"
// /launch will store it in host_audio
// resume doesn't always get the parameter "localAudioPlayMode"
// launch will store it in host_audio
bool host_audio {};
https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey };
http_server_t http_server;
// Verify certificates after establishing connection
https_server.verify = [&cert_chain, add_cert](SSL *ssl) {
https_server.verify = [add_cert](SSL *ssl) {
crypto::x509_t x509 { SSL_get_peer_certificate(ssl) };
if (!x509) {
BOOST_LOG(info) << "unknown -- denied"sv;
@@ -1148,6 +1150,7 @@ namespace nvhttp {
void
erase_all_clients() {
map_id_client.clear();
cert_chain.clear();
save_state();
}
} // namespace nvhttp

View File

@@ -10,7 +10,9 @@
#include <mutex>
#include <string>
#include "src/config.h"
#include "src/logging.h"
#include "src/stat_trackers.h"
#include "src/thread_safe.h"
#include "src/utility.h"
#include "src/video_colorspace.h"
@@ -19,6 +21,8 @@ extern "C" {
#include <moonlight-common-c/src/Limelight.h>
}
using namespace std::literals;
struct sockaddr;
struct AVFrame;
struct AVBufferRef;
@@ -499,6 +503,22 @@ namespace platf {
int env_width, env_height;
int width, height;
protected:
// collect capture timing data (at loglevel debug)
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
void
log_sleep_overshoot(std::chrono::nanoseconds overshoot_ns) {
if (config::sunshine.min_log_level <= 1) {
// Print sleep overshoot stats to debug log every 20 seconds
auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
auto f = stat_trackers::one_digit_after_decimal();
BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
};
// std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - next_frame;
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
}
}
};
class mic_t {

View File

@@ -262,7 +262,7 @@ namespace cuda {
fs::path sysfs_dir { sysfs_path };
for (auto &entry : fs::directory_iterator { sysfs_dir }) {
auto file = entry.path().filename();
auto filestring = file.generic_u8string();
auto filestring = file.generic_string();
if (std::string_view { filestring }.substr(0, 4) != "card"sv) {
continue;
}
@@ -800,16 +800,21 @@ namespace cuda {
handle.reset();
});
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
std::this_thread::sleep_for(1ns);
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 150ms, *cursor);
@@ -1044,4 +1049,4 @@ namespace platf {
return display_names;
}
} // namespace platf
} // namespace platf

View File

@@ -22,7 +22,9 @@ extern "C" {
#define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) &0x00ffffffffffffffULL))
#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1))
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl"
#if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl"
#endif
using namespace std::literals;
namespace gl {
@@ -37,7 +39,7 @@ namespace gl {
}
tex_t::~tex_t() {
if (!size() == 0) {
if (size() != 0) {
ctx.DeleteTextures(size(), begin());
}
}

View File

@@ -587,8 +587,8 @@ namespace platf {
weak_strong += data.rumble(tp);
}
std::clamp<std::uint32_t>(weak_strong.first, 0, 0xFFFF);
std::clamp<std::uint32_t>(weak_strong.second, 0, 0xFFFF);
weak_strong.first = std::clamp<std::uint32_t>(weak_strong.first, 0, 0xFFFF);
weak_strong.second = std::clamp<std::uint32_t>(weak_strong.second, 0, 0xFFFF);
old_rumble = weak_strong * gain / 0xFFFF;
return old_rumble;
@@ -1510,7 +1510,7 @@ namespace platf {
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (const auto &ch : str) {
ss << ch;
ss << static_cast<uint_least32_t>(ch);
}
std::string hex_unicode(ss.str());

View File

@@ -614,7 +614,7 @@ namespace platf {
for (auto &entry : fs::directory_iterator { card_dir }) {
auto file = entry.path().filename();
auto filestring = file.generic_u8string();
auto filestring = file.generic_string();
if (filestring.size() < 4 || std::string_view { filestring }.substr(0, 4) != "card"sv) {
continue;
}
@@ -1069,7 +1069,7 @@ namespace platf {
}
inline capture_e
refresh(file_t *file, egl::surface_descriptor_t *sd) {
refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional<std::chrono::steady_clock::time_point> &frame_timestamp) {
// Check for a change in HDR metadata
if (connector_id) {
auto connector_props = card.connector_props(*connector_id);
@@ -1080,6 +1080,7 @@ namespace platf {
}
plane_t plane = drmModeGetPlane(card.fd.el, plane_id);
frame_timestamp = std::chrono::steady_clock::now();
auto fb = card.fb(plane.get());
if (!fb) {
@@ -1192,17 +1193,22 @@ namespace platf {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
std::this_thread::sleep_for(1ns);
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
@@ -1303,7 +1309,8 @@ namespace platf {
egl::surface_descriptor_t sd;
auto status = refresh(fb_fd, &sd);
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
auto status = refresh(fb_fd, &sd, frame_timestamp);
if (status != capture_e::ok) {
return status;
}
@@ -1330,6 +1337,8 @@ namespace platf {
gl::ctx.GetTextureSubImage(rgb->tex[0], 0, img_offset_x, img_offset_y, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data);
img_out->frame_timestamp = frame_timestamp;
if (cursor && captured_cursor.visible) {
blend_cursor(*img_out);
}
@@ -1408,17 +1417,22 @@ namespace platf {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
std::this_thread::sleep_for(1ns);
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
@@ -1456,7 +1470,7 @@ namespace platf {
auto img = (egl::img_descriptor_t *) img_out.get();
img->reset();
auto status = refresh(fb_fd, &img->sd);
auto status = refresh(fb_fd, &img->sd, img->frame_timestamp);
if (status != capture_e::ok) {
return status;
}
@@ -1627,7 +1641,7 @@ namespace platf {
for (auto &entry : fs::directory_iterator { card_dir }) {
auto file = entry.path().filename();
auto filestring = file.generic_u8string();
auto filestring = file.generic_string();
if (std::string_view { filestring }.substr(0, 4) != "card"sv) {
continue;
}
@@ -1671,7 +1685,7 @@ namespace platf {
if (!fb->handles[0]) {
BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv;
BOOST_LOG((window_system != window_system_e::X11 || config::video.capture == "kms") ? fatal : error)
<< "You must run [sudo setcap cap_sys_admin+p $(readlink -f sunshine)] for KMS display capture to work!"sv;
<< "You must run [sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))] for KMS display capture to work!"sv;
break;
}

View File

@@ -129,16 +129,22 @@ namespace wl {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
@@ -259,16 +265,22 @@ namespace wl {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);

View File

@@ -421,7 +421,7 @@ namespace platf {
}
if (streamedMonitor != -1) {
BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv;
BOOST_LOG(info) << "Configuring selected display ("sv << streamedMonitor << ") to stream"sv;
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
int output = screenr->noutput;
@@ -481,17 +481,22 @@ namespace platf {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
std::this_thread::sleep_for(1ns);
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
@@ -535,6 +540,7 @@ namespace platf {
auto img = (x11_img_t *) img_out.get();
XImage *x_img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) };
img->frame_timestamp = std::chrono::steady_clock::now();
img->width = x_img->width;
img->height = x_img->height;
@@ -621,17 +627,22 @@ namespace platf {
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
sleep_overshoot_tracker.reset();
while (true) {
auto now = std::chrono::steady_clock::now();
if (next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
std::this_thread::sleep_for(next_frame - now);
}
while (next_frame > now) {
std::this_thread::sleep_for(1ns);
now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
std::chrono::nanoseconds overshoot_ns = now - next_frame;
log_sleep_overshoot(overshoot_ns);
next_frame += delay;
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
next_frame = now + delay;
}
next_frame = now + delay;
std::shared_ptr<platf::img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
@@ -795,7 +806,7 @@ namespace platf {
return {};
}
BOOST_LOG(info) << "Detecting monitors"sv;
BOOST_LOG(info) << "Detecting displays"sv;
x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) };
if (!xdisplay) {
@@ -810,7 +821,7 @@ namespace platf {
for (int x = 0; x < output; ++x) {
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
if (out_info) {
BOOST_LOG(info) << "Detected monitor "sv << monitor << ": "sv << out_info->name << ", connected: "sv << (out_info->connection == RR_Connected);
BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected);
++monitor;
}
}

View File

@@ -5,6 +5,7 @@
#pragma once
#import <AVFoundation/AVFoundation.h>
#import <AppKit/AppKit.h>
struct CaptureSession {
AVCaptureVideoDataOutput *output;
@@ -29,6 +30,7 @@ typedef bool (^FrameCallbackBlock)(CMSampleBufferRef);
@property (nonatomic, assign) NSMapTable<AVCaptureConnection *, dispatch_semaphore_t> *captureSignals;
+ (NSArray<NSDictionary *> *)displayNames;
+ (NSString *)getDisplayName:(CGDirectDisplayID)displayID;
- (id)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate;

View File

@@ -23,13 +23,24 @@
for (uint32_t i = 0; i < count; i++) {
[result addObject:@{
@"id": [NSNumber numberWithUnsignedInt:displays[i]],
@"name": [NSString stringWithFormat:@"%d", displays[i]]
@"name": [NSString stringWithFormat:@"%d", displays[i]],
@"displayName": [self getDisplayName:displays[i]],
}];
}
return [NSArray arrayWithArray:result];
}
+ (NSString *)getDisplayName:(CGDirectDisplayID)displayID {
NSScreen *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
if (screen.deviceDescription[@"NSScreenNumber"] == [NSNumber numberWithUnsignedInt:displayID]) {
return screen.localizedName;
}
}
return nil;
}
- (id)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate {
self = [super init];

View File

@@ -142,18 +142,23 @@ namespace platf {
auto display = std::make_shared<av_display_t>();
// Default to main display
display->display_id = CGMainDisplayID();
if (!display_name.empty()) {
auto display_array = [AVVideo displayNames];
for (NSDictionary *item in display_array) {
NSString *name = item[@"name"];
if (name.UTF8String == display_name) {
NSNumber *display_id = item[@"id"];
display->display_id = [display_id unsignedIntValue];
}
// Print all displays available with it's name and id
auto display_array = [AVVideo displayNames];
BOOST_LOG(info) << "Detecting displays"sv;
for (NSDictionary *item in display_array) {
NSNumber *display_id = item[@"id"];
// We need show display's product name and corresponding display number given by user
NSString *name = item[@"displayName"];
// We are using CGGetActiveDisplayList that only returns active displays so hardcoded connected value in log to true
BOOST_LOG(info) << "Detected display: "sv << name.UTF8String << " (id: "sv << [NSString stringWithFormat:@"%@", display_id].UTF8String << ") connected: true"sv;
if (!display_name.empty() && std::atoi(display_name.c_str()) == [display_id unsignedIntValue]) {
display->display_id = [display_id unsignedIntValue];
}
}
BOOST_LOG(info) << "Configuring selected display ("sv << display->display_id << ") to stream"sv;
display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate];

View File

@@ -509,9 +509,28 @@ const KeyCodeMap kKeyCodesMap[] = {
auto macos_input = (macos_input_t *) result.get();
// If we don't use the main display in the future, this has to be adapted
// Default to main display
macos_input->display = CGMainDisplayID();
auto output_name = config::video.output_name;
// If output_name is set, try to find the display with that display id
if (!output_name.empty()) {
uint32_t max_display = 32;
uint32_t display_count;
CGDirectDisplayID displays[max_display];
if (CGGetActiveDisplayList(max_display, displays, &display_count) != kCGErrorSuccess) {
BOOST_LOG(error) << "Unable to get active display list , error: "sv << std::endl;
}
else {
for (int i = 0; i < display_count; i++) {
CGDirectDisplayID display_id = displays[i];
if (display_id == std::atoi(output_name.c_str())) {
macos_input->display = display_id;
}
}
}
}
// Input coordinates are based on the virtual resolution not the physical, so we need the scaling factor
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(macos_input->display);
macos_input->displayScaling = ((CGFloat) CGDisplayPixelsWide(macos_input->display)) / ((CGFloat) CGDisplayModeGetPixelWidth(mode));

View File

@@ -154,7 +154,7 @@ namespace platf::dxgi {
SetThreadExecutionState(ES_CONTINUOUS);
});
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
sleep_overshoot_tracker.reset();
while (true) {
// This will return false if the HDR state changes or for any number of other
@@ -184,16 +184,8 @@ namespace platf::dxgi {
}
else {
high_precision_sleep(sleep_period);
if (config::sunshine.min_log_level <= 1) {
// Print sleep overshoot stats to debug log every 20 seconds
auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
auto f = stat_trackers::one_digit_after_decimal();
BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
};
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
}
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
log_sleep_overshoot(overshoot_ns);
status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor);

View File

@@ -25,7 +25,9 @@ extern "C" {
#include <boost/algorithm/string/predicate.hpp>
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx"
#if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx"
#endif
namespace platf {
using namespace std::literals;
}

View File

@@ -1411,7 +1411,7 @@ namespace platf {
ds4_update_state(gamepad_context_t &gamepad, const gamepad_state_t &gamepad_state) {
auto &report = gamepad.report.ds4.Report;
report.wButtons = ds4_buttons(gamepad_state) | ds4_dpad(gamepad_state);
report.wButtons = static_cast<uint16_t>(ds4_buttons(gamepad_state)) | static_cast<uint16_t>(ds4_dpad(gamepad_state));
report.bSpecial = ds4_special_buttons(gamepad_state);
report.bTriggerL = gamepad_state.lt;

View File

@@ -1691,8 +1691,8 @@ namespace platf {
}
int64_t
qpc_counter() {
LARGE_INTEGER performace_counter;
if (QueryPerformanceCounter(&performace_counter)) return performace_counter.QuadPart;
LARGE_INTEGER performance_counter;
if (QueryPerformanceCounter(&performance_counter)) return performance_counter.QuadPart;
return 0;
}

View File

@@ -11,6 +11,7 @@ extern "C" {
#include <array>
#include <cctype>
#include <utility>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

View File

@@ -1814,7 +1814,7 @@ namespace stream {
// The alternative is that Sunshine can never start another session until it's manually restarted.
auto task = []() {
BOOST_LOG(fatal) << "Hang detected! Session failed to terminate in 10 seconds."sv;
log_flush();
logging::log_flush();
lifetime::debug_trap();
};
auto force_kill = task_pool.pushDelayed(task, 10s).task_id;

View File

@@ -3,6 +3,7 @@
* @brief todo
*/
#pragma once
#include <utility>
#include <boost/asio.hpp>

Some files were not shown because too many files have changed in this diff Show More