Compare commits

...

14 Commits

Author SHA1 Message Date
ReenigneArcher bd0f9a4508 ci(docker): adjust root reserve size 2025-08-08 12:29:45 -04:00
ReenigneArcher fc7b9e30c8 style(sonar): fix cpp:S4962 (#4140)
CodeQL / CodeQL (push) Has been cancelled
CI / GitHub Env Debug (push) Has been cancelled
CI / Release Setup (push) Has been cancelled
localize / Update Localization (push) Has been cancelled
Build GH-Pages / prep (push) Has been cancelled
CI / Docker (push) Has been cancelled
CI / Homebrew (push) Has been cancelled
CI / Linux (push) Has been cancelled
CI / Linux Copr (push) Has been cancelled
CI / Linux Flatpak (push) Has been cancelled
CI / Windows (push) Has been cancelled
CI / Coverage-Homebrew-macos-14 (push) Has been cancelled
CI / Coverage-Homebrew-macos-15 (push) Has been cancelled
CI / Coverage-Homebrew-ubuntu-latest (push) Has been cancelled
CI / Coverage-Linux-AppImage (push) Has been cancelled
CI / Coverage-Windows-AMD64 (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Release Homebrew Beta (push) Has been cancelled
Build GH-Pages / call-jekyll-build (push) Has been cancelled
2025-08-08 00:45:53 -04:00
ReenigneArcher dbe80d0f92 style(sonar): fix cpp:S6185 (#4133) 2025-08-07 23:17:13 -04:00
ReenigneArcher a28c20df18 build(linux): compile with gcc13+ and bump cuda (#4136) 2025-08-07 20:13:25 -04:00
dependabot[bot] 4503fea7fb build(deps): bump actions/download-artifact from 4 to 5 in the github-actions group (#4134)
CodeQL / CodeQL (push) Has been cancelled
CI / GitHub Env Debug (push) Has been cancelled
CI / Release Setup (push) Has been cancelled
Build GH-Pages / prep (push) Has been cancelled
CI / Docker (push) Has been cancelled
CI / Homebrew (push) Has been cancelled
CI / Linux (push) Has been cancelled
CI / Linux Copr (push) Has been cancelled
CI / Linux Flatpak (push) Has been cancelled
CI / Windows (push) Has been cancelled
CI / Coverage-Homebrew-macos-13 (push) Has been cancelled
CI / Coverage-Homebrew-macos-14 (push) Has been cancelled
CI / Coverage-Homebrew-macos-15 (push) Has been cancelled
CI / Coverage-Homebrew-ubuntu-latest (push) Has been cancelled
CI / Coverage-Linux-AppImage (push) Has been cancelled
CI / Coverage-Windows-AMD64 (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Release Homebrew Beta (push) Has been cancelled
Build GH-Pages / call-jekyll-build (push) Has been cancelled
build(deps): bump actions/download-artifact in the github-actions group

Bumps the github-actions group with 1 update: [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/download-artifact` from 4 to 5
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 13:56:25 -04:00
dependabot[bot] 03bb53d591 build(deps): bump packaging/linux/flatpak/deps/flatpak-builder-tools from 7090720 to d2fa57a (#4117)
CodeQL / CodeQL (push) Has been cancelled
CI / GitHub Env Debug (push) Has been cancelled
CI / Release Setup (push) Has been cancelled
CI / Docker (push) Has been cancelled
CI / Homebrew (push) Has been cancelled
CI / Linux (push) Has been cancelled
CI / Linux Copr (push) Has been cancelled
CI / Linux Flatpak (push) Has been cancelled
CI / Windows (push) Has been cancelled
CI / Coverage-Homebrew-macos-13 (push) Has been cancelled
CI / Coverage-Homebrew-macos-14 (push) Has been cancelled
CI / Coverage-Homebrew-macos-15 (push) Has been cancelled
CI / Coverage-Homebrew-ubuntu-latest (push) Has been cancelled
CI / Coverage-Linux-AppImage (push) Has been cancelled
CI / Coverage-Windows-AMD64 (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Release Homebrew Beta (push) Has been cancelled
Build GH-Pages / prep (push) Has been cancelled
Build GH-Pages / call-jekyll-build (push) Has been cancelled
localize / Update Localization (push) Has been cancelled
build(deps): bump packaging/linux/flatpak/deps/flatpak-builder-tools

Bumps [packaging/linux/flatpak/deps/flatpak-builder-tools](https://github.com/flatpak/flatpak-builder-tools) from `7090720` to `d2fa57a`.
- [Commits](https://github.com/flatpak/flatpak-builder-tools/compare/7090720d43404c60b88445a3149835ec3c585717...d2fa57ab89811efb8d83da6cfa93fc184e94c640)

---
updated-dependencies:
- dependency-name: packaging/linux/flatpak/deps/flatpak-builder-tools
  dependency-version: d2fa57ab89811efb8d83da6cfa93fc184e94c640
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 11:07:26 -04:00
LizardByte-bot bb6e3bc8e1 chore: update global workflows (#4125) 2025-08-05 10:02:25 -04:00
ReenigneArcher b2d3ded6f4 style(sonar): fix cpp:S1110 (#4130) 2025-08-05 08:30:24 -04:00
ReenigneArcher 9f101fb546 style(sonar): fix cpp:S3806 (#4129) 2025-08-04 23:30:48 -04:00
ReenigneArcher 48c2c52498 fix(packaging/linux): depend on libgbm (#4113) 2025-07-31 21:50:07 -04:00
dependabot[bot] d6787c4ed4 build(deps): bump third-party/build-deps from a9a7f86 to 94369e6 (#4121)
Bumps [third-party/build-deps](https://github.com/LizardByte/build-deps) from `a9a7f86` to `94369e6`.
- [Commits](https://github.com/LizardByte/build-deps/compare/a9a7f86328963837e30e21c8901faba00fe81542...94369e63776e3a018df0bdb82992d1a7ba98adf7)

---
updated-dependencies:
- dependency-name: third-party/build-deps
  dependency-version: 94369e63776e3a018df0bdb82992d1a7ba98adf7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 08:50:37 -04:00
Erik Groh eff88de993 docs: Docs buffer overrun improvement (#3986) 2025-07-30 17:47:36 -04:00
Andy Grundman b3ee60d422 fix(video): restore the ability to set a minimum fps target (#4114) 2025-07-29 23:12:16 -04:00
ReenigneArcher 99cf9ac960 ci: use actionlint and problem matchers (#4116) 2025-07-29 22:08:30 -04:00
78 changed files with 643 additions and 405 deletions
+2 -3
View File
@@ -1,6 +1,5 @@
# ensure dockerfiles are checked out with LF line endings
# ensure Linux specific files are checked out with LF line endings
Dockerfile text eol=lf
*.dockerfile text eol=lf
# ensure flatpak lint json files are checked out with LF line endings
*flatpak-lint-*.json text eol=lf
*.sh text eol=lf
+17
View File
@@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "copr-ci-gcc",
"pattern": [
{
"regexp": "^/?(?:[^/]+/){5}([^:]+):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "docker-gcc",
"pattern": [
{
"regexp": "^(?:#\\d+\\s+\\d+\\.\\d+\\s+)?/?(?:[^/]+/){2}([^:]+):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "gcc-strip3",
"pattern": [
{
"regexp": "^/?(?:[^/]+/){3}([^:]+):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}
+29
View File
@@ -0,0 +1,29 @@
{
"problemMatcher": [
{
"owner": "gcc",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
},
{
"owner": "doxygen",
"pattern": [
{
"regexp": "^.*?([A-Za-z]:[\\\\/][^:]+|[\\\\/][^:]+):(\\d+): ([a-zA-Z]+): (.+)$",
"file": 1,
"line": 2,
"severity": 3,
"message": 4
}
]
}
]
}
+2
View File
@@ -20,4 +20,6 @@ jobs:
uses: LizardByte/.github/.github/workflows/__call-release-notifier.yml@master
if: github.repository_owner == 'LizardByte'
secrets:
GH_EMAIL: ${{ secrets.GH_BOT_EMAIL }}
GH_NAME: ${{ secrets.GH_BOT_NAME }}
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
@@ -26,4 +26,6 @@ jobs:
uses: LizardByte/.github/.github/workflows/__call-update-flathub-repo.yml@master
if: github.repository_owner == 'LizardByte'
secrets:
GH_EMAIL: ${{ secrets.GH_BOT_EMAIL }}
GH_NAME: ${{ secrets.GH_BOT_NAME }}
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
@@ -26,4 +26,6 @@ jobs:
uses: LizardByte/.github/.github/workflows/__call-update-pacman-repo.yml@master
if: github.repository_owner == 'LizardByte'
secrets:
GH_EMAIL: ${{ secrets.GH_BOT_EMAIL }}
GH_NAME: ${{ secrets.GH_BOT_NAME }}
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
+5 -1
View File
@@ -16,6 +16,10 @@ on:
required: false
COPR_CLI_CONFIG:
required: false
GH_BOT_TOKEN:
required: false
VIRUSTOTAL_API_KEY:
required: false
concurrency:
group: "_${{ github.workflow }}-${{ github.ref }}"
@@ -45,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: artifacts
pattern: build-*
+23 -18
View File
@@ -73,15 +73,16 @@ jobs:
cmake \
flatpak
sudo su $(whoami) -c "flatpak --user remote-add --if-not-exists flathub \
https://flathub.org/repo/flathub.flatpakrepo"
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 \
sudo su "$(whoami)" -c "flatpak --user install -y flathub \
org.flatpak.Builder \
org.freedesktop.Platform/${{ matrix.arch }}/${PLATFORM_VERSION} \
org.freedesktop.Sdk/${{ matrix.arch }}/${PLATFORM_VERSION} \
org.freedesktop.Sdk.Extension.node${NODE_VERSION}/${{ matrix.arch }}/${PLATFORM_VERSION} \
"
"
flatpak run org.flatpak.Builder --version
@@ -101,9 +102,11 @@ jobs:
flatpak-${{ matrix.arch }}-
- name: Configure Flatpak Manifest
env:
BRANCH: ${{ github.head_ref }}
run: |
# variables for manifest
branch="${{ github.head_ref }}"
branch="${{ env.BRANCH }}"
build_version=${{ inputs.release_version }}
commit=${{ inputs.release_commit }}
@@ -137,19 +140,20 @@ jobs:
- name: Debug Manifest
working-directory: build
run: cat ${APP_ID}.yml
run: cat "${APP_ID}.yml"
- name: Build Linux Flatpak
working-directory: build
run: |
sudo su $(whoami) -c "flatpak run org.flatpak.Builder \
echo "::add-matcher::.github/matchers/gcc-strip3.json"
sudo su "$(whoami)" -c "flatpak run org.flatpak.Builder \
--arch=${{ matrix.arch }} \
--force-clean \
--repo=repo \
--sandbox \
--stop-at=cuda build-sunshine ${APP_ID}.yml"
cp -r .flatpak-builder copy-of-flatpak-builder
sudo su $(whoami) -c "flatpak run org.flatpak.Builder \
sudo su "$(whoami)" -c "flatpak run org.flatpak.Builder \
--arch=${{ matrix.arch }} \
--force-clean \
--repo=repo \
@@ -157,15 +161,16 @@ jobs:
build-sunshine ${APP_ID}.yml"
rm -rf .flatpak-builder
mv copy-of-flatpak-builder .flatpak-builder
sudo su $(whoami) -c "flatpak build-bundle \
sudo su "$(whoami)" -c "flatpak build-bundle \
--arch=${{ matrix.arch }} \
./repo \
../artifacts/sunshine_${{ matrix.arch }}.flatpak ${APP_ID}"
sudo su $(whoami) -c "flatpak build-bundle \
sudo su "$(whoami)" -c "flatpak build-bundle \
--runtime \
--arch=${{ matrix.arch }} \
./repo \
../artifacts/sunshine_debug_${{ matrix.arch }}.flatpak ${APP_ID}.Debug"
echo "::remove-matcher owner=gcc-strip3::"
- name: Lint Flatpak
working-directory: build
@@ -177,7 +182,7 @@ jobs:
--exceptions \
--user-exceptions "${exceptions_file}" \
manifest \
${APP_ID}.yml
"${APP_ID}.yml"
echo "Linting flatpak repo"
# TODO: add arg
@@ -193,13 +198,13 @@ jobs:
if: matrix.arch == 'x86_64'
run: |
mkdir -p flathub/modules
cp ./build/generated-sources.json ./flathub/
cp ./build/package-lock.json ./flathub/
cp ./build/${APP_ID}.yml ./flathub/
cp ./build/${APP_ID}.metainfo.xml ./flathub/
cp ./packaging/linux/flatpak/README.md ./flathub/
cp ./packaging/linux/flatpak/flathub.json ./flathub/
cp -r ./packaging/linux/flatpak/modules/. ./flathub/modules/
cp "./build/generated-sources.json" "./flathub/"
cp "./build/package-lock.json" "./flathub/"
cp "./build/${APP_ID}.yml" "./flathub/"
cp "./build/${APP_ID}.metainfo.xml" "./flathub/"
cp "./packaging/linux/flatpak/README.md" "./flathub/"
cp "./packaging/linux/flatpak/flathub.json" "./flathub/"
cp -r "./packaging/linux/flatpak/modules/." "./flathub/modules/"
# submodules will need to be handled in the workflow that creates the PR
# create the archive
+11 -7
View File
@@ -36,8 +36,6 @@ jobs:
include:
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
# while GitHub has larger macOS runners, they are not available for our repos :(
- os_version: "13"
os_name: "macos"
- os_version: "14"
os_name: "macos"
- os_version: "15"
@@ -73,9 +71,13 @@ jobs:
brew install python3
- name: Configure formula
env:
HEAD_REF: ${{ github.head_ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_DEFAULT_BRANCH: ${{ github.event.pull_request.head.repo.default_branch }}
run: |
# variables for formula
branch="${{ github.head_ref }}"
branch="${{ env.HEAD_REF }}"
build_version=${{ inputs.release_version }}
commit=${{ inputs.release_commit }}
@@ -96,9 +98,9 @@ jobs:
else
echo "This is a PR event"
clone_url=${{ github.event.pull_request.head.repo.clone_url }}
branch="${{ github.event.pull_request.head.ref }}"
default_branch="${{ github.event.pull_request.head.repo.default_branch }}"
tag="${{ github.event.pull_request.head.ref }}"
branch="${{ env.PR_HEAD_REF }}"
default_branch="${{ env.PR_DEFAULT_BRANCH }}"
tag="${{ env.PR_HEAD_REF }}"
fi
echo "Branch: ${branch}"
echo "Clone URL: ${clone_url}"
@@ -143,8 +145,9 @@ jobs:
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
echo "DISPLAY=${DISPLAY}" >> $GITHUB_ENV
echo "DISPLAY=${DISPLAY}" >> "${GITHUB_ENV}"
- run: echo "::add-matcher::.github/matchers/gcc-strip3.json"
- name: Validate Homebrew Formula
id: test
if: matrix.release != true
@@ -156,6 +159,7 @@ jobs:
publish: false
token: ${{ secrets.GH_TOKEN }}
validate: true
- run: echo "::remove-matcher owner=gcc-strip3::"
- name: Setup python
id: python
+5 -3
View File
@@ -79,8 +79,8 @@ jobs:
--enable-x11 \
--enable-glx \
--enable-wayland \
--without-legacy # emgd, nvctrl, fglrx
make -j $(nproc)
--without-legacy
make -j "$(nproc)"
sudo make install
cd .. && rm -rf libva-*
@@ -91,6 +91,7 @@ jobs:
COMMIT: ${{ inputs.release_commit }}
run: |
chmod +x ./scripts/linux_build.sh
echo "::add-matcher::.github/matchers/gcc.json"
./scripts/linux_build.sh \
--publisher-name='${{ github.repository_owner }}' \
--publisher-website='https://app.lizardbyte.dev' \
@@ -98,12 +99,13 @@ jobs:
--skip-cleanup \
--skip-package \
--ubuntu-test-repo ${{ matrix.EXTRA_ARGS }}
echo "::remove-matcher owner=gcc::"
- name: Set AppImage Version
if: matrix.name == 'AppImage'
run: |
version=${{ inputs.release_version }}
echo "VERSION=${version}" >> $GITHUB_ENV
echo "VERSION=${version}" >> "${GITHUB_ENV}"
- name: Package Linux - AppImage
if: matrix.name == 'AppImage'
+6 -4
View File
@@ -199,12 +199,12 @@ jobs:
ignore_list=$(IFS=,; echo "${ignore_packages[*]}")
# install pinned dependencies
if [ -n "$tarballs" ]; then
pacman -U --noconfirm ${tarballs}
if [ -n "${tarballs}" ]; then
pacman -U --noconfirm "${tarballs}"
fi
# Only add --ignore if we have packages to ignore
if [ -n "$ignore_list" ]; then
if [ -n "${ignore_list}" ]; then
pacman -Syu --noconfirm --ignore="${ignore_list}" "${dependencies[@]}"
else
pacman -Syu --noconfirm "${dependencies[@]}"
@@ -251,7 +251,7 @@ jobs:
# step output
echo "python-path=${python_path}"
echo "python-path=${python_path}" >> $GITHUB_OUTPUT
echo "python-path=${python_path}" >> "${GITHUB_OUTPUT}"
- name: Build Windows
shell: msys2 {0}
@@ -272,7 +272,9 @@ jobs:
-DSUNSHINE_PUBLISHER_NAME='${{ github.repository_owner }}' \
-DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \
-DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
echo "::add-matcher::.github/matchers/gcc.json"
ninja -C build
echo "::remove-matcher owner=gcc::"
- name: Package Windows
shell: msys2 {0}
+4 -5
View File
@@ -56,6 +56,7 @@ jobs:
uses: LizardByte/.github/.github/workflows/__call-docker.yml@master
with:
maximize_build_space: true
maximize_build_space_root_reserve_size: 28672
publish_release: ${{ needs.release-setup.outputs.publish_release }}
release_commit: ${{ needs.release-setup.outputs.release_commit }}
release_tag: ${{ needs.release-setup.outputs.release_tag }}
@@ -136,8 +137,6 @@ jobs:
include:
- name: Linux-AppImage
coverage: true
- name: Homebrew-macos-13
coverage: false
- name: Homebrew-macos-14
coverage: false
- name: Homebrew-macos-15
@@ -151,7 +150,7 @@ jobs:
uses: actions/checkout@v4
- name: Download coverage artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: coverage-${{ matrix.name }}
path: _coverage
@@ -192,7 +191,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: artifacts
pattern: build-*
@@ -225,7 +224,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download homebrew artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: beta-Homebrew
path: homebrew
+4 -4
View File
@@ -48,9 +48,9 @@ jobs:
if [ -f "${{ env.file }}" ];
then
rm ${{ env.file }}
echo "new_file=false" >> $GITHUB_ENV
echo "new_file=false" >> "${GITHUB_ENV}"
else
echo "new_file=true" >> $GITHUB_ENV
echo "new_file=true" >> "${GITHUB_ENV}"
fi
# extract the new strings
@@ -67,7 +67,7 @@ jobs:
# set the variable with minimal output, replacing `\t` with ` `
OUTPUT=$(git diff --numstat locale/sunshine.po | sed -e "s#\t# #g")
echo "git_diff=${OUTPUT}" >> $GITHUB_ENV
echo "git_diff=${OUTPUT}" >> "${GITHUB_ENV}"
- name: git reset
# only run if a single line changed (date/time) and file already existed
@@ -79,7 +79,7 @@ jobs:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
run: echo "date=$(date +'%Y-%m-%d')" >> "${GITHUB_OUTPUT}"
- name: Create/Update Pull Request
uses: peter-evans/create-pull-request@v7
+3 -3
View File
@@ -72,13 +72,13 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
<td>Windows: 10+ (Windows Server does not support virtual gamepads)</td>
</tr>
<tr>
<td>macOS: 13+</td>
<td>macOS: 14+</td>
</tr>
<tr>
<td>Linux/Debian: 12+ (bookworm)</td>
<td>Linux/Debian: 13+ (trixie)</td>
</tr>
<tr>
<td>Linux/Fedora: 40+</td>
<td>Linux/Fedora: 41+</td>
</tr>
<tr>
<td>Linux/Ubuntu: 22.04+ (jammy)</td>
+2
View File
@@ -45,6 +45,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "\
libcap2, \
libcurl4, \
libdrm2, \
libgbm1, \
libevdev2, \
libnuma1, \
libopus0, \
@@ -65,6 +66,7 @@ set(CPACK_RPM_PACKAGE_REQUIRES "\
libva >= 2.14.0, \
libwayland-client >= 1.20.0, \
libX11 >= 1.7.3.1, \
mesa-libgbm >= 25.0.7, \
miniupnpc >= 2.2.4, \
numactl-libs >= 2.0.14, \
openssl >= 3.0.2, \
+4 -4
View File
@@ -1,18 +1,18 @@
# Set build variables if env variables are defined
# These are used in configured files such as manifests for different packages
if(DEFINED ENV{BRANCH}) # cmake-lint: disable=W0106
if(DEFINED ENV{BRANCH})
set(GITHUB_BRANCH $ENV{BRANCH})
endif()
if(DEFINED ENV{BUILD_VERSION}) # cmake-lint: disable=W0106
set(BUILD_VERSION $ENV{BUILD_VERSION})
endif()
if(DEFINED ENV{CLONE_URL}) # cmake-lint: disable=W0106
if(DEFINED ENV{CLONE_URL})
set(GITHUB_CLONE_URL $ENV{CLONE_URL})
endif()
if(DEFINED ENV{COMMIT}) # cmake-lint: disable=W0106
if(DEFINED ENV{COMMIT})
set(GITHUB_COMMIT $ENV{COMMIT})
endif()
if(DEFINED ENV{TAG}) # cmake-lint: disable=W0106
if(DEFINED ENV{TAG})
set(GITHUB_TAG $ENV{TAG})
endif()
+1 -1
View File
@@ -32,7 +32,6 @@ ENV CLONE_URL=${CLONE_URL}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# hadolint ignore=SC2016
RUN <<_SETUP
#!/bin/bash
set -e
@@ -42,6 +41,7 @@ useradd -m builder
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
# patch the build flags
# shellcheck disable=SC2016
sed -i 's,#MAKEFLAGS="-j2",MAKEFLAGS="-j$(nproc)",g' /etc/makepkg.conf
# install dependencies
+28 -24
View File
@@ -3,8 +3,8 @@
# platforms: linux/amd64
# platforms_pr: linux/amd64
# no-cache-filters: toolchain-base,toolchain
ARG BASE=ubuntu
ARG TAG=22.04
ARG BASE=debian
ARG TAG=trixie-slim
FROM ${BASE}:${TAG} AS toolchain-base
ENV DEBIAN_FRONTEND=noninteractive
@@ -19,18 +19,17 @@ ENV DISPLAY=:0
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install dependencies
# hadolint ignore=SC1091
RUN <<_DEPS
#!/bin/bash
set -e
apt-get update -y
apt-get install -y --no-install-recommends \
build-essential \
cmake=3.22.* \
cmake=3.31.* \
ca-certificates \
doxygen \
gcc=4:11.2.* \
g++=4:11.2.* \
gcc=4:14.2.* \
g++=4:14.2.* \
gdb \
git \
graphviz \
@@ -60,21 +59,13 @@ apt-get install -y --no-install-recommends \
xvfb
apt-get clean
rm -rf /var/lib/apt/lists/*
# Install Node
wget --max-redirect=0 -qO- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source "$HOME/.nvm/nvm.sh"
nvm install node
nvm use node
nvm alias default node
_DEPS
# install cuda
WORKDIR /build/cuda
# versions: https://developer.nvidia.com/cuda-toolkit-archive
ENV CUDA_VERSION="11.8.0"
ENV CUDA_BUILD="520.61.05"
# hadolint ignore=SC3010
ENV CUDA_VERSION="12.9.1"
ENV CUDA_BUILD="575.57.08"
RUN <<_INSTALL_CUDA
#!/bin/bash
set -e
@@ -85,18 +76,31 @@ if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
fi
url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"
echo "cuda url: ${url}"
wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run
chmod a+x ./cuda.run
./cuda.run --silent --toolkit --toolkitpath=/usr/local --no-opengl-libs --no-man-page --no-drm
rm ./cuda.run
tmpfile="/tmp/cuda.run"
wget "$url" --progress=bar:force:noscroll --show-progress -O "$tmpfile"
chmod a+x "${tmpfile}"
"${tmpfile}" --silent --toolkit --toolkitpath=/usr/local --no-opengl-libs --no-man-page --no-drm
rm -f "${tmpfile}"
_INSTALL_CUDA
WORKDIR /
# Write a shell script that starts Xvfb and then runs a shell
# install node
RUN <<_INSTALL_NODE
#!/bin/bash
set -e
wget --max-redirect=0 -qO- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source "$HOME/.nvm/nvm.sh"
nvm install node
nvm use node
nvm alias default node
_INSTALL_NODE
WORKDIR /toolchain
# Create a shell script that starts Xvfb and then runs a shell
RUN <<_ENTRYPOINT
#!/bin/bash
set -e
cat <<EOF > /entrypoint.sh
cat <<EOF > entrypoint.sh
#!/bin/bash
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
if [ "\$#" -eq 0 ]; then
@@ -107,11 +111,11 @@ fi
EOF
# Make the script executable
chmod +x /entrypoint.sh
chmod +x entrypoint.sh
# Note about CLion
echo "ATTENTION: CLion will override the entrypoint, you can disable this in the toolchain settings"
_ENTRYPOINT
# Use the shell script as the entrypoint
ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["/toolchain/entrypoint.sh"]
@@ -4,7 +4,7 @@
# platforms_pr: linux/amd64
# no-cache-filters: sunshine-base,artifacts,sunshine
ARG BASE=debian
ARG TAG=bookworm
ARG TAG=trixie
FROM ${BASE}:${TAG} AS sunshine-base
ENV DEBIAN_FRONTEND=noninteractive
@@ -32,6 +32,7 @@ RUN <<_BUILD
set -e
chmod +x ./scripts/linux_build.sh
./scripts/linux_build.sh \
--cuda-patches \
--publisher-name='LizardByte' \
--publisher-website='https://app.lizardbyte.dev' \
--publisher-issue-url='https://app.lizardbyte.dev/support' \
@@ -42,7 +43,6 @@ _BUILD
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
+2 -2
View File
@@ -35,14 +35,14 @@ chmod +x ./scripts/linux_build.sh
--publisher-name='LizardByte' \
--publisher-website='https://app.lizardbyte.dev' \
--publisher-issue-url='https://app.lizardbyte.dev/support' \
--sudo-off
--sudo-off \
--ubuntu-test-repo
apt-get clean
rm -rf /var/lib/apt/lists/*
_BUILD
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
-1
View File
@@ -42,7 +42,6 @@ _BUILD
# run tests
WORKDIR /build/sunshine/build/tests
# hadolint ignore=SC1091
RUN <<_TEST
#!/bin/bash
set -e
+10 -1
View File
@@ -3,6 +3,15 @@ Sunshine binaries are built using [CMake](https://cmake.org) and requires `cmake
## Building Locally
### Compiler
It is recommended to use one of the following compilers:
| Compiler | Version |
|:------------|:--------|
| GCC | 13+ |
| Clang | 17+ |
| Apple Clang | 15+ |
### Dependencies
#### Linux
@@ -16,7 +25,7 @@ Sunshine requires CUDA Toolkit for NVFBC capture. There are two caveats to CUDA:
1. The version installed depends on the version of GCC.
2. 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.
At the time of writing, the recommended version to use is CUDA ~12.9.
See [CUDA compatibility](https://docs.nvidia.com/deploy/cuda-compatibility/index.html) for more info.
@tip{To install older versions, select the appropriate run file based on your desired CUDA version and architecture
+31 -5
View File
@@ -1270,7 +1270,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<td colspan="2">
Remap the requested resolution and FPS to another display mode.<br>
Depending on the [dd_resolution_option](#dd_resolution_option) and
[dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping
[dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping
groups are available:
<ul>
<li>`mixed` - both options are set to `auto`.</li>
@@ -1281,7 +1281,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
`refresh_rate_only` - only [dd_refresh_rate_option](#dd_refresh_rate_option) is set to `auto`.
</li>
</ul>
For each of those groups, a list of fields can be configured to perform remapping:
For each of those groups, a list of fields can be configured to perform remapping:
<ul>
<li>
`requested_resolution` - resolution that needs to be matched in order to use this remapping entry.
@@ -1291,10 +1291,10 @@ editing the `conf` file in a text editor. Use the examples as reference.
<li>`final_refresh_rate` - refresh rate value to be used if the entry was matched.</li>
</ul>
If `requested_*` field is left empty, it will match <b>everything</b>.<br>
If `final_*` field is left empty, the original value will not be remapped and either a requested, manual
or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered
If `final_*` field is left empty, the original value will not be remapped and either a requested, manual
or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered
invalid.<br>
@note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution`
@note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution`
field to be considered.}
@note{First entry to be matched in the list is the one that will be used.}
@tip{`requested_resolution` and `final_resolution` can be omitted for `refresh_rate_only` group.}
@@ -1371,6 +1371,32 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>
### minimum_fps_target
<table>
<tr>
<td>Description</td>
<td colspan="2">
Sunshine tries to save bandwidth when content on screen is static or a low framerate. Because many clients expect a constant stream of video frames, a certain amount of duplicate frames are sent when this happens. This setting controls the lowest effective framerate a stream can reach.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
0
@endcode</td>
</tr>
<tr>
<td rowspan="3">Choices</td>
<td>0</td>
<td>Use half the stream's FPS as the minimum target.</td>
</tr>
<tr>
<td>1-1000</td>
<td>Specify your own value. The real minimum may differ from this value.</td>
</tr>
</table>
## Network
### upnp
+8 -15
View File
@@ -31,7 +31,7 @@ See [Docker](../DOCKER_README.md) for more information.
CUDA is used for NVFBC capture.
@tip{See [CUDA GPUS](https://developer.nvidia.com/cuda-gpus) to cross-reference Compute Capability to your GPU.
The table below applies to packages provided by LizardByte. If you use an official LizardByte package then you do not
The table below applies to packages provided by LizardByte. If you use an official LizardByte package, then you do not
need to install CUDA.}
<table>
@@ -43,9 +43,9 @@ need to install CUDA.}
<th>Package</th>
</tr>
<tr>
<td rowspan="3">11.8.0</td>
<td rowspan="3">450.80.02</td>
<td rowspan="3">35;50;52;60;61;62;70;72;75;80;86;87;89;90</td>
<td rowspan="8">12.9.1</td>
<td rowspan="8">575.57.08</td>
<td rowspan="8">50;52;60;61;62;70;72;75;80;86;87;89;90</td>
<td>sunshine.AppImage</td>
</tr>
<tr>
@@ -55,27 +55,18 @@ need to install CUDA.}
<td>sunshine-ubuntu-24.04-{arch}.deb</td>
</tr>
<tr>
<td rowspan="1">12.0.0</td>
<td rowspan="1">525.60.13</td>
<td rowspan="5">50;52;60;61;62;70;72;75;80;86;87;89;90</td>
<td>sunshine-debian-bookworm-{arch}.deb</td>
<td>sunshine-debian-trixie-{arch}.deb</td>
</tr>
<tr>
<td rowspan="2">12.6.2</td>
<td rowspan="2">560.35.03</td>
<td>sunshine_{arch}.flatpak</td>
</tr>
<tr>
<td>Sunshine (copr - Fedora 41)</td>
</tr>
<tr>
<td rowspan="1">12.8.1</td>
<td rowspan="1">570.124.06</td>
<td>Sunshine (copr - Fedora 42)</td>
</tr>
<tr>
<td rowspan="1">12.9.1</td>
<td rowspan="1">575.57.08</td>
<td>sunshine.pkg.tar.zst</td>
</tr>
</table>
@@ -89,13 +80,15 @@ According to AppImageLint the supported distro matrix of the AppImage is below.
- ✔ Debian bookworm
- ✔ Debian trixie
- ✔ Debian sid
- ✔ Ubuntu plucky
- ✔ Ubuntu noble
- ✔ Ubuntu jammy
- ✖ Ubuntu focal
- ✖ Ubuntu bionic
- ✖ Ubuntu xenial
- ✖ Ubuntu trusty
- ✖ CentOS 7
- ✖ Rocky Linux 8
- ✖ Rocky Linux 9
##### Install
1. Download [sunshine.AppImage](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage)
+28 -2
View File
@@ -84,9 +84,35 @@ client only 1 Gbit/s or Wi-Fi. Similarly, a 1 Gbps host may be too fast for a
client having only a 100 Mbps interface.
As a workaround the transmission speed of the host NIC can be reduced: 1 Gbps
instead of 2.5 or 100 Mbps instead of 1 Gbps. (A technically more advanced
instead of 2.5 or 100 Mbps instead of 1 Gbps. A technically more advanced
solution would be to configure traffic shaping rules at the OS-level, so that
only Sunshine's traffic is slowed down.)
only Sunshine's traffic is slowed down.
Such a solution on Linux could look like that:
```bash
# 1) Remove existing qdisc (pfifo_fast)
sudo tc qdisc del dev <NIC> root
# 2) Add HTB root qdisc with default class 1:1
sudo tc qdisc add dev <NIC> root handle 1: htb default 1
# 3) Create class 1:1 for full 10 Gbit/s (all other traffic)
sudo tc class add dev <NIC> parent 1: classid 1:1 htb \
rate 10000mbit ceil 10000mbit burst 32k
# 4) Create class 1:10 for Sunshine game stream at 1 Gbit/s
sudo tc class add dev <NIC> parent 1: classid 1:10 htb \
rate 1000mbit ceil 1000mbit burst 32k
# 5) Filter UDP source port 47998 into class 1:10
sudo tc filter add dev <NIC> protocol ip parent 1: prio 1 \
u32 match ip protocol 17 0xff \
match ip sport 47998 0xffff flowid 1:10
```
In that way only the Sunshine traffic is limited by 1 Gbit. This is not persistent on reboots.
If you use a different port for the game stream you need to adjust the last command.
Sunshine versions > 0.23.1 include improved networking code that should
alleviate or even solve this issue (without reducing the NIC speed).
+6 -9
View File
@@ -56,18 +56,18 @@ BuildRequires: which
BuildRequires: xorg-x11-server-Xvfb
# Conditional BuildRequires for cuda-gcc based on Fedora version
%if 0%{?fedora} >= 40 && 0%{?fedora} <= 41
%if 0%{?fedora} <= 41
BuildRequires: gcc13
BuildRequires: gcc13-c++
%global gcc_version 13
%global cuda_version 12.6.3
%global cuda_build 560.35.05
%global cuda_version 12.9.1
%global cuda_build 575.57.08
%elif %{?fedora} >= 42
BuildRequires: gcc14
BuildRequires: gcc14-c++
%global gcc_version 14
%global cuda_version 12.8.1
%global cuda_build 570.124.06
%global cuda_version 12.9.1
%global cuda_build 575.57.08
%endif
%global cuda_dir %{_builddir}/cuda
@@ -97,9 +97,6 @@ tar -xzf %{SOURCE0} -C %{_builddir}/Sunshine
# list directory
ls -a %{_builddir}/Sunshine
# patches
%autopatch -p1
%build
# exit on error
set -e
@@ -174,7 +171,7 @@ function install_cuda() {
--backup \
--directory="%{cuda_dir}" \
--verbose \
< "%{_builddir}/Sunshine/packaging/linux/fedora/patches/f42/${architecture}/01-math_functions.patch"
< "%{_builddir}/Sunshine/packaging/linux/patches/${architecture}/01-math_functions.patch"
fi
}
+4 -4
View File
@@ -19,8 +19,8 @@
"only-arches": [
"x86_64"
],
"url": "https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.35.03_linux.run",
"sha256": "3729a89cb58f7ca6a46719cff110d6292aec7577585a8d71340f0dbac54fb237",
"url": "https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda_12.9.1_575.57.08_linux.run",
"sha256": "0f6d806ddd87230d2adbe8a6006a9d20144fdbda9de2d6acc677daa5d036417a",
"dest-filename": "cuda.run"
},
{
@@ -28,8 +28,8 @@
"only-arches": [
"aarch64"
],
"url": "https://developer.download.nvidia.com/compute/cuda/12.6.2/local_installers/cuda_12.6.2_560.35.03_linux_sbsa.run",
"sha256": "2249408848b705c18b9eadfb5161b52e4e36fcc5753647329cce93db141e5466",
"url": "https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda_12.9.1_575.57.08_linux_sbsa.run",
"sha256": "64f47ab791a76b6889702425e0755385f5fa216c5a9f061875c7deed5f08cdb6",
"dest-filename": "cuda.run"
}
]
@@ -2,12 +2,12 @@
# User Service
mkdir -p ~/.config/systemd/user
cp /app/share/sunshine/systemd/user/sunshine.service $HOME/.config/systemd/user/sunshine.service
echo Sunshine User Service has been installed.
echo Use [systemctl --user enable sunshine] once to autostart Sunshine on login.
cp "/app/share/sunshine/systemd/user/sunshine.service" "$HOME/.config/systemd/user/sunshine.service"
echo "Sunshine User Service has been installed."
echo "Use [systemctl --user enable sunshine] once to autostart Sunshine on login."
# Udev rule
UDEV=$(cat /app/share/sunshine/udev/rules.d/60-sunshine.rules)
echo Configuring mouse permission.
echo "Configuring mouse permission."
flatpak-spawn --host pkexec sh -c "echo '$UDEV' > /etc/udev/rules.d/60-sunshine.rules"
echo Restart computer for mouse permission to take effect.
echo "Restart computer for mouse permission to take effect."
@@ -2,10 +2,10 @@
# User Service
systemctl --user stop sunshine
rm $HOME/.config/systemd/user/sunshine.service
rm "$HOME/.config/systemd/user/sunshine.service"
systemctl --user daemon-reload
echo Sunshine User Service has been removed.
echo "Sunshine User Service has been removed."
# Udev rule
flatpak-spawn --host pkexec sh -c "rm /etc/udev/rules.d/60-sunshine.rules"
echo Input rules removed. Restart computer to take effect.
echo "Input rules removed. Restart computer to take effect."
+14
View File
@@ -37,6 +37,10 @@ class @PROJECT_NAME@ < Formula
depends_on "opus"
depends_on "icu4c" => :recommended
on_macos do
depends_on xcode: ["15.3", :build]
end
on_linux do
depends_on "avahi"
depends_on "libayatana-appindicator"
@@ -59,6 +63,16 @@ class @PROJECT_NAME@ < Formula
depends_on "wayland"
end
fails_with :clang do
build 1400
cause "Requires C++23 support"
end
fails_with :gcc do
version "12" # fails with GCC 12.x and earlier
cause "Requires C++23 support"
end
def install
ENV["BRANCH"] = "@GITHUB_BRANCH@"
ENV["BUILD_VERSION"] = "@BUILD_VERSION@"
+63 -61
View File
@@ -1,61 +1,63 @@
#!/bin/bash
if ! [ -x "$(command -v ./go-png2ico)" ]; then
echo "./go-png2ico not found"
echo "download the executable from https://github.com/J-Siu/go-png2ico"
echo "and drop it in this folder"
exit 1
fi
if ! [ -x "$(command -v ./oxipng)" ]; then
echo "./oxipng executable not found"
echo "download the executable from https://github.com/shssoichiro/oxipng"
echo "and drop it in this folder"
exit 1
fi
if ! [ -x "$(command -v inkscape)" ]; then
echo "inkscape executable not found"
exit 1
fi
icon_base_sizes=(16 64)
icon_sizes_keys=() # associative array to prevent duplicates
icon_sizes_keys[256]=1
for icon_base_size in ${icon_base_sizes[@]}; do
# increment in 25% till 400%
icon_size_increment=$((icon_base_size / 4))
for ((i = 0; i <= 12; i++)); do
icon_sizes_keys[$((icon_base_size + i * icon_size_increment))]=1
done
done
# convert to normal array
icon_sizes=${!icon_sizes_keys[@]}
echo "using icon sizes:"
echo ${icon_sizes[@]}
src_vectors=("../../src_assets/common/assets/web/public/images/sunshine-locked.svg"
"../../src_assets/common/assets/web/public/images/sunshine-pausing.svg"
"../../src_assets/common/assets/web/public/images/sunshine-playing.svg"
"../../sunshine.svg")
echo "using sources vectors:"
echo ${src_vectors[@]}
for src_vector in ${src_vectors[@]}; do
file_name=`basename "$src_vector" .svg`
png_files=()
for icon_size in ${icon_sizes[@]}; do
png_file="${file_name}${icon_size}.png"
echo "converting ${png_file}"
inkscape -w $icon_size -h $icon_size "$src_vector" --export-filename "${png_file}" &&
./oxipng -o max --strip safe --alpha "${png_file}" &&
png_files+=("${png_file}")
done
echo "packing ${file_name}.ico"
./go-png2ico "${png_files[@]}" "${file_name}.ico"
done
#!/bin/bash
if ! [ -x "$(command -v ./go-png2ico)" ]; then
echo "./go-png2ico not found"
echo "download the executable from https://github.com/J-Siu/go-png2ico"
echo "and drop it in this folder"
exit 1
fi
if ! [ -x "$(command -v ./oxipng)" ]; then
echo "./oxipng executable not found"
echo "download the executable from https://github.com/shssoichiro/oxipng"
echo "and drop it in this folder"
exit 1
fi
if ! [ -x "$(command -v inkscape)" ]; then
echo "inkscape executable not found"
exit 1
fi
icon_base_sizes=(16 64)
icon_sizes_keys=() # associative array to prevent duplicates
icon_sizes_keys[256]=1
for icon_base_size in "${icon_base_sizes[@]}"; do
# increment in 25% till 400%
icon_size_increment=$((icon_base_size / 4))
for ((i = 0; i <= 12; i++)); do
icon_sizes_keys[icon_base_size + i * icon_size_increment]=1
done
done
# convert to normal array
icon_sizes=("${!icon_sizes_keys[@]}")
echo "using icon sizes:"
# shellcheck disable=SC2068 # intentionally word split
echo ${icon_sizes[@]}
src_vectors=("../../src_assets/common/assets/web/public/images/sunshine-locked.svg"
"../../src_assets/common/assets/web/public/images/sunshine-pausing.svg"
"../../src_assets/common/assets/web/public/images/sunshine-playing.svg"
"../../sunshine.svg")
echo "using sources vectors:"
# shellcheck disable=SC2068 # intentionally word split
echo ${src_vectors[@]}
for src_vector in "${src_vectors[@]}"; do
file_name=$(basename "${src_vector}" .svg)
png_files=()
for icon_size in "${icon_sizes[@]}"; do
png_file="${file_name}${icon_size}.png"
echo "converting ${png_file}"
inkscape -w "${icon_size}" -h "${icon_size}" "${src_vector}" --export-filename "${png_file}" &&
./oxipng -o max --strip safe --alpha "${png_file}" &&
png_files+=("${png_file}")
done
echo "packing ${file_name}.ico"
./go-png2ico "${png_files[@]}" "${file_name}.ico"
done
+58 -30
View File
@@ -3,6 +3,7 @@ set -e
# Default value for arguments
appimage_build=0
cuda_patches=0
num_processors=$(nproc)
publisher_name="Third Party Publisher"
publisher_website=""
@@ -28,6 +29,7 @@ Options:
-h, --help Display this help message.
-s, --sudo-off Disable sudo command.
--appimage-build Compile for AppImage, this will not create the AppImage, just the executable.
--cuda-patches Apply cuda patches.
--num-processors The number of processors to use for compilation. Default is the value of 'nproc'.
--publisher-name The name of the publisher (not developer) of the application.
--publisher-website The URL of the publisher's website.
@@ -55,6 +57,9 @@ while getopts ":hs-:" opt; do
appimage_build=1
skip_libva=1
;;
cuda-patches)
cuda_patches=1
;;
num-processors=*)
num_processors="${OPTARG#*=}"
;;
@@ -185,7 +190,15 @@ function add_debian_based_deps() {
fi
}
function add_test_ppa() {
if [ "$ubuntu_test_repo" == 1 ]; then
$package_install_command "software-properties-common"
${sudo_cmd} add-apt-repository ppa:ubuntu-toolchain-r/test -y
fi
}
function add_debian_deps() {
add_test_ppa
add_debian_based_deps
dependencies+=(
"libayatana-appindicator3-dev"
@@ -193,11 +206,7 @@ function add_debian_deps() {
}
function add_ubuntu_deps() {
if [ "$ubuntu_test_repo" == 1 ]; then
# allow newer gcc
${sudo_cmd} add-apt-repository ppa:ubuntu-toolchain-r/test -y
fi
add_test_ppa
add_debian_based_deps
dependencies+=(
"libappindicator3-dev"
@@ -298,6 +307,24 @@ function install_cuda() {
"${build_dir}/cuda.run" --silent --toolkit --toolkitpath="${build_dir}/cuda" --no-opengl-libs --no-man-page --no-drm
rm "${build_dir}/cuda.run"
nvcc_path="${build_dir}/cuda/bin/nvcc"
# run cuda patches
if [ "$cuda_patches" == 1 ]; then
echo "Applying CUDA patches"
local patch_dir="${script_dir}/../packaging/linux/patches/${architecture}"
if [ -d "$patch_dir" ]; then
for patch in "$patch_dir"/*.patch; do
echo "Applying patch: $patch"
patch -p2 \
--backup \
--directory="${build_dir}/cuda" \
--verbose \
< "$patch"
done
else
echo "No patches found for architecture: $architecture"
fi
fi
}
function check_version() {
@@ -460,6 +487,8 @@ function run_install() {
nvm_url="https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh"
echo "nvm url: ${nvm_url}"
wget -qO- ${nvm_url} | bash
# shellcheck source=/dev/null # we don't care that shellcheck cannot find nvm.sh
source "$HOME/.nvm/nvm.sh"
nvm install node
nvm use node
@@ -534,27 +563,26 @@ elif grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then
version="12"
package_update_command="${sudo_cmd} apt-get update"
package_install_command="${sudo_cmd} apt-get install -y"
cuda_version="12.0.0"
cuda_build="525.60.13"
gcc_version="12"
nvm_node=0
elif grep -q "PLATFORM_ID=\"platform:f40\"" /etc/os-release; then
distro="fedora"
version="40"
package_update_command="${sudo_cmd} dnf update -y"
package_install_command="${sudo_cmd} dnf install -y"
cuda_version=12.6.3
cuda_build=560.35.05
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="13"
nvm_node=0
dev_tools_group="Development Tools"
elif grep -q "Debian GNU/Linux 13 (trixie)" /etc/os-release; then
distro="debian"
version="13"
package_update_command="${sudo_cmd} apt-get update"
package_install_command="${sudo_cmd} apt-get install -y"
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="14"
nvm_node=0
elif grep -q "PLATFORM_ID=\"platform:f41\"" /etc/os-release; then
distro="fedora"
version="41"
package_update_command="${sudo_cmd} dnf update -y"
package_install_command="${sudo_cmd} dnf install -y"
cuda_version=12.6.3
cuda_build=560.35.05
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="13"
nvm_node=0
dev_tools_group="development-tools"
@@ -563,8 +591,8 @@ elif grep -q "PLATFORM_ID=\"platform:f42\"" /etc/os-release; then
version="42"
package_update_command="${sudo_cmd} dnf update -y"
package_install_command="${sudo_cmd} dnf install -y"
cuda_version=12.8.1
cuda_build=570.124.06
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="14"
nvm_node=0
dev_tools_group="development-tools"
@@ -573,27 +601,27 @@ elif grep -q "Ubuntu 22.04" /etc/os-release; then
version="22.04"
package_update_command="${sudo_cmd} apt-get update"
package_install_command="${sudo_cmd} apt-get install -y"
cuda_version="11.8.0"
cuda_build="520.61.05"
gcc_version="11"
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="13"
nvm_node=1
elif grep -q "Ubuntu 24.04" /etc/os-release; then
distro="ubuntu"
version="24.04"
package_update_command="${sudo_cmd} apt-get update"
package_install_command="${sudo_cmd} apt-get install -y"
cuda_version="11.8.0"
cuda_build="520.61.05"
gcc_version="11"
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="14"
nvm_node=0
elif grep -q "Ubuntu 25.04" /etc/os-release; then
distro="ubuntu"
version="25.04"
package_update_command="${sudo_cmd} apt-get update"
package_install_command="${sudo_cmd} apt-get install -y"
cuda_version="11.8.0"
cuda_build="520.61.05"
gcc_version="11"
cuda_version="12.9.1"
cuda_build="575.57.08"
gcc_version="14"
nvm_node=0
else
echo "Unsupported Distro or Version"
+4 -2
View File
@@ -504,7 +504,8 @@ namespace config {
{} // wa
}, // display_device
0 // max_bitrate
0, // max_bitrate
0 // minimum_fps_target (0 = framerate)
};
audio_t audio {
@@ -1146,6 +1147,7 @@ namespace config {
}
int_f(vars, "max_bitrate", video.max_bitrate);
double_between_f(vars, "minimum_fps_target", video.minimum_fps_target, {0.0, 1000.0});
path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
@@ -1419,7 +1421,7 @@ namespace config {
if (!service_ctrl::is_service_running()) {
// If the service isn't running, relaunch ourselves as admin to start it
WCHAR executable[MAX_PATH];
GetModuleFileNameW(NULL, executable, ARRAYSIZE(executable));
GetModuleFileNameW(nullptr, executable, ARRAYSIZE(executable));
SHELLEXECUTEINFOW shell_exec_info {};
shell_exec_info.cbSize = sizeof(shell_exec_info);
+1
View File
@@ -141,6 +141,7 @@ namespace config {
} dd;
int max_bitrate; // Maximum bitrate, sets ceiling in kbps for bitrate requested from client
double minimum_fps_target; ///< Lowest framerate that will be used when streaming. Range 0-1000, 0 = half of client's requested framerate.
};
struct audio_t {
+3 -2
View File
@@ -8,6 +8,7 @@
// standard includes
#include <filesystem>
#include <format>
#include <fstream>
#include <set>
@@ -713,7 +714,7 @@ namespace confighttp {
if (const int max_index = static_cast<int>(apps_node.size()) - 1; max_index < 0) {
error = "No applications to delete";
} else {
error = "'index' out of range, max index is "s + std::to_string(max_index);
error = std::format("'index' {} out of range, max index is {}", index, max_index);
}
bad_request(response, request, error);
return;
@@ -730,7 +731,7 @@ namespace confighttp {
proc::refresh(config::stream.file_apps);
output_tree["status"] = true;
output_tree["result"] = "application " + std::to_string(index) + " deleted";
output_tree["result"] = std::format("application {} deleted", index);
send_response(response, output_tree);
} catch (std::exception &e) {
BOOST_LOG(warning) << "DeleteApp: "sv << e.what();
+8 -9
View File
@@ -4,6 +4,7 @@
*/
// standard includes
#include <csignal>
#include <format>
#include <iostream>
#include <thread>
@@ -25,13 +26,11 @@ extern "C" {
using namespace std::literals;
void launch_ui() {
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS));
platf::open_url(url);
}
void launch_ui_with_path(std::string path) {
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)) + path;
void launch_ui(const std::optional<std::string> &path) {
std::string url = std::format("https://localhost:{}", static_cast<int>(net::map_port(confighttp::PORT_HTTPS)));
if (path) {
url += *path;
}
platf::open_url(url);
}
@@ -192,8 +191,8 @@ namespace service_ctrl {
}
private:
SC_HANDLE scm_handle = NULL;
SC_HANDLE service_handle = NULL;
SC_HANDLE scm_handle = nullptr;
SC_HANDLE service_handle = nullptr;
};
bool is_service_running() {
+3 -9
View File
@@ -14,19 +14,13 @@
/**
* @brief Launch the Web UI.
* @param path Optional path to append to the base URL.
* @examples
* launch_ui();
* launch_ui("/pin");
* @examples_end
*/
void launch_ui();
/**
* @brief Launch the Web UI at a specific endpoint.
* @examples
* launch_ui_with_path("/pin");
* @examples_end
*/
void launch_ui_with_path(std::string path);
void launch_ui(const std::optional<std::string> &path = std::nullopt);
/**
* @brief Functions for handling command line arguments.
+1 -1
View File
@@ -186,7 +186,7 @@ int main(int argc, char *argv[]) {
wnd_class.lpszClassName = "SunshineSessionMonitorClass";
wnd_class.lpfnWndProc = SessionMonitorWindowProc;
if (!RegisterClassA(&wnd_class)) {
session_monitor_hwnd_promise.set_value(NULL);
session_monitor_hwnd_promise.set_value(nullptr);
BOOST_LOG(error) << "Failed to register session monitor window class"sv << std::endl;
return;
}
+5 -2
View File
@@ -5,6 +5,9 @@
// this include
#include "nvenc_base.h"
// standard includes
#include <format>
// local includes
#include "src/config.h"
#include "src/logging.h"
@@ -427,7 +430,7 @@ namespace nvenc {
extra += " two-pass";
}
if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) {
extra += " vbv+" + std::to_string(config.vbv_percentage_increase);
extra += std::format(" vbv+{}", config.vbv_percentage_increase);
}
if (encoder_params.rfi) {
extra += " rfi";
@@ -439,7 +442,7 @@ namespace nvenc {
extra += " spatial-aq";
}
if (enc_config.rcParams.enableMinQP) {
extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP);
extra += std::format(" qpmin={}", enc_config.rcParams.minQP.qpInterP);
}
if (config.insert_filler_data) {
extra += " filler-data";
+4 -4
View File
@@ -12,13 +12,13 @@ namespace nvenc {
nvenc_d3d11::nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type):
nvenc_base(device_type) {
async_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
async_event_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
}
nvenc_d3d11::~nvenc_d3d11() {
if (dll) {
FreeLibrary(dll);
dll = NULL;
dll = nullptr;
}
if (async_event_handle) {
CloseHandle(async_event_handle);
@@ -36,7 +36,7 @@ namespace nvenc {
constexpr auto dll_name = "nvEncodeAPI.dll";
#endif
if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
if ((dll = LoadLibraryEx(dll_name, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) {
auto new_nvenc = std::make_unique<NV_ENCODE_API_FUNCTION_LIST>();
new_nvenc->version = min_struct_version(NV_ENCODE_API_FUNCTION_LIST_VER);
@@ -55,7 +55,7 @@ namespace nvenc {
if (dll) {
FreeLibrary(dll);
dll = NULL;
dll = nullptr;
}
return false;
+1 -1
View File
@@ -39,7 +39,7 @@ namespace nvenc {
bool wait_for_async_event(uint32_t timeout_ms) override;
private:
HMODULE dll = NULL;
HMODULE dll = nullptr;
};
} // namespace nvenc
+1 -1
View File
@@ -63,7 +63,7 @@ namespace nvenc {
constexpr auto dll_name = "nvcuda.dll";
if ((cuda_functions.dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
if ((cuda_functions.dll = LoadLibraryEx(dll_name, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
auto load_function = [&]<typename T>(T &location, auto symbol) -> bool {
location = (T) GetProcAddress(cuda_functions.dll, symbol);
return location != nullptr;
+1 -1
View File
@@ -56,7 +56,7 @@ namespace nvenc {
autopop_context push_context();
HMODULE dll = NULL;
HMODULE dll = nullptr;
const ID3D11DevicePtr d3d_device;
ID3D11Texture2DPtr d3d_input_texture;
+21 -4
View File
@@ -7,6 +7,7 @@
// standard includes
#include <filesystem>
#include <format>
#include <string>
#include <utility>
@@ -154,7 +155,7 @@ namespace nvhttp {
std::string get_arg(const args_t &args, const char *name, const char *default_value = nullptr) {
auto it = args.find(name);
if (it == std::end(args)) {
if (default_value != NULL) {
if (default_value != nullptr) {
return std::string(default_value);
}
@@ -636,7 +637,7 @@ namespace nvhttp {
tree.put("root.<xmlattr>.status_code", 400);
tree.put(
"root.<xmlattr>.status_message",
"Pin must be 4 digits, " + std::to_string(pin.size()) + " provided"
std::format("Pin must be 4 digits, {} provided", pin.size())
);
return false;
}
@@ -896,7 +897,15 @@ namespace nvhttp {
}
tree.put("root.<xmlattr>.status_code", 200);
tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT)));
tree.put(
"root.sessionUrl0",
std::format(
"{}{}:{}",
launch_session->rtsp_url_scheme,
net::addr_to_url_escaped_string(request->local_endpoint().address()),
static_cast<int>(net::map_port(rtsp_stream::RTSP_SETUP_PORT))
)
);
tree.put("root.gamesession", 1);
rtsp_stream::launch_session_raise(launch_session);
@@ -978,7 +987,15 @@ namespace nvhttp {
}
tree.put("root.<xmlattr>.status_code", 200);
tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT)));
tree.put(
"root.sessionUrl0",
std::format(
"{}{}:{}",
launch_session->rtsp_url_scheme,
net::addr_to_url_escaped_string(request->local_endpoint().address()),
static_cast<int>(net::map_port(rtsp_stream::RTSP_SETUP_PORT))
)
);
tree.put("root.resume", 1);
rtsp_stream::launch_session_raise(launch_session);
+2 -1
View File
@@ -4,6 +4,7 @@
*/
// standard includes
#include <fcntl.h>
#include <format>
#include <sstream>
#include <string>
@@ -574,7 +575,7 @@ namespace va {
if (!display) {
char string[1024];
auto bytes = readlink(("/proc/self/fd/" + std::to_string(fd)).c_str(), string, sizeof(string));
auto bytes = readlink(std::format("/proc/self/fd/{}", fd).c_str(), string, sizeof(string));
std::string_view render_device {string, (std::size_t) bytes};
+12 -10
View File
@@ -4,8 +4,11 @@
*/
#define INITGUID
// standard includes
#include <format>
// platform includes
#include <audioclient.h>
#include <Audioclient.h>
#include <avrt.h>
#include <mmdeviceapi.h>
#include <newdev.h>
@@ -168,28 +171,27 @@ namespace {
waveformat.SubFormat == KSDATAFORMAT_SUBTYPE_PCM ? "S" :
"UNKNOWN";
result += std::to_string(waveformat.Samples.wValidBitsPerSample) + " " +
std::to_string(waveformat.Format.nSamplesPerSec) + " ";
result += std::format("{} {} ", static_cast<int>(waveformat.Samples.wValidBitsPerSample), static_cast<int>(waveformat.Format.nSamplesPerSec));
switch (waveformat.dwChannelMask) {
case (waveformat_mask_stereo):
case waveformat_mask_stereo:
result += "2.0";
break;
case (waveformat_mask_surround51_with_backspeakers):
case waveformat_mask_surround51_with_backspeakers:
result += "5.1";
break;
case (waveformat_mask_surround51_with_sidespeakers):
case waveformat_mask_surround51_with_sidespeakers:
result += "5.1 (sidespeakers)";
break;
case (waveformat_mask_surround71):
case waveformat_mask_surround71:
result += "7.1";
break;
default:
result += std::to_string(waveformat.Format.nChannels) + " channels (unrecognized)";
result += std::format("{} channels (unrecognized)", static_cast<int>(waveformat.Format.nChannels));
break;
}
@@ -375,7 +377,7 @@ namespace platf::audio {
*ppvInterface = (IMMNotificationClient *) this;
return S_OK;
} else {
*ppvInterface = NULL;
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
}
@@ -677,7 +679,7 @@ namespace platf::audio {
float *sample_buf_pos;
int channels;
HANDLE mmcss_task_handle = NULL;
HANDLE mmcss_task_handle = nullptr;
};
class audio_control_t: public ::platf::audio_control_t {
+1 -1
View File
@@ -12,7 +12,7 @@
#include <dxgi.h>
#include <dxgi1_6.h>
#include <Unknwn.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <winrt/windows.graphics.capture.h>
// local includes
#include "src/platform/common.h"
+16 -16
View File
@@ -601,12 +601,12 @@ namespace platf::dxgi {
LUID val;
if (OpenProcessToken(GetCurrentProcess(), flags, &token) &&
!!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) {
!!LookupPrivilegeValue(nullptr, SE_INC_BASE_PRIORITY_NAME, &val)) {
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = val;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), nullptr, nullptr)) {
BOOST_LOG(warning) << "Could not set privilege to increase GPU priority";
}
}
@@ -918,20 +918,20 @@ namespace platf::dxgi {
"DXGI_FORMAT_A8P8",
"DXGI_FORMAT_B4G4R4A4_UNORM",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"DXGI_FORMAT_P208",
"DXGI_FORMAT_V208",
+2 -2
View File
@@ -7,7 +7,7 @@
// platform includes
#include <d3dcompiler.h>
#include <directxmath.h>
#include <DirectXMath.h>
extern "C" {
#include <libavcodec/avcodec.h>
@@ -1739,7 +1739,7 @@ namespace platf::dxgi {
img->data = nullptr;
if (img->encoder_texture_handle) {
CloseHandle(img->encoder_texture_handle);
img->encoder_texture_handle = NULL;
img->encoder_texture_handle = nullptr;
}
// Initialize format-dependent fields
+1 -1
View File
@@ -13,7 +13,7 @@
// Gross hack to work around MINGW-packages#22160
#define ____FIReference_1_boolean_INTERFACE_DEFINED__
#include <windows.graphics.capture.interop.h>
#include <Windows.Graphics.Capture.Interop.h>
#include <winrt/windows.foundation.h>
#include <winrt/windows.foundation.metadata.h>
#include <winrt/windows.graphics.directx.direct3d11.h>
+5 -5
View File
@@ -5,7 +5,7 @@
#define WINVER 0x0A00
// platform includes
#include <windows.h>
#include <Windows.h>
// standard includes
#include <cmath>
@@ -293,7 +293,7 @@ namespace platf {
if (gamepad.repeat_task) {
task_pool.cancel(gamepad.repeat_task);
gamepad.repeat_task = 0;
gamepad.repeat_task = nullptr;
}
if (gamepad.gp && vigem_target_is_attached(gamepad.gp.get())) {
@@ -1452,7 +1452,7 @@ namespace platf {
// Cancel any pending updates. We will requeue one here when we're finished.
if (gamepad.repeat_task) {
task_pool.cancel(gamepad.repeat_task);
gamepad.repeat_task = 0;
gamepad.repeat_task = nullptr;
}
if (gamepad.gp && vigem_target_is_attached(gamepad.gp.get())) {
@@ -1598,8 +1598,8 @@ namespace platf {
uint16_t y = touch.y * 943;
uint8_t touchData[] = {
(uint8_t) (x & 0xFF), // Low 8 bits of X
(uint8_t) (((x >> 8) & 0x0F) | ((y & 0x0F) << 4)), // High 4 bits of X and low 4 bits of Y
(uint8_t) (((y >> 4) & 0xFF)) // High 8 bits of Y
(uint8_t) ((x >> 8 & 0x0F) | (y & 0x0F) << 4), // High 4 bits of X and low 4 bits of Y
(uint8_t) (y >> 4 & 0xFF) // High 8 bits of Y
};
report.sCurrentTouch.bPacketCounter++;
+33 -33
View File
@@ -22,13 +22,13 @@
#include <iphlpapi.h>
#include <iterator>
#include <timeapi.h>
#include <userenv.h>
#include <winsock2.h>
#include <windows.h>
#include <winuser.h>
#include <UserEnv.h>
#include <WinSock2.h>
#include <Windows.h>
#include <WinUser.h>
#include <wlanapi.h>
#include <ws2tcpip.h>
#include <wtsapi32.h>
#include <WS2tcpip.h>
#include <WtsApi32.h>
#include <sddl.h>
// clang-format on
@@ -117,7 +117,7 @@ namespace platf {
std::filesystem::path appdata() {
WCHAR sunshine_path[MAX_PATH];
GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path));
GetModuleFileNameW(nullptr, sunshine_path, _countof(sunshine_path));
return std::filesystem::path {sunshine_path}.remove_filename() / L"config"sv;
}
@@ -410,16 +410,16 @@ namespace platf {
LPPROC_THREAD_ATTRIBUTE_LIST allocate_proc_thread_attr_list(DWORD attribute_count) {
SIZE_T size;
InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size);
InitializeProcThreadAttributeList(nullptr, attribute_count, 0, &size);
auto list = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, size);
if (list == NULL) {
return NULL;
if (list == nullptr) {
return nullptr;
}
if (!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) {
HeapFree(GetProcessHeap(), 0, list);
return NULL;
return nullptr;
}
return list;
@@ -518,7 +518,7 @@ namespace platf {
// Allocate a process attribute list with space for 2 elements
startup_info.lpAttributeList = allocate_proc_thread_attr_list(2);
if (startup_info.lpAttributeList == NULL) {
if (startup_info.lpAttributeList == nullptr) {
// If the allocation failed, set ec to an appropriate error code and return the structure
ec = std::make_error_code(std::errc::not_enough_memory);
return startup_info;
@@ -530,7 +530,7 @@ namespace platf {
// Populate std handles if the caller gave us a log file to use
startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
startup_info.StartupInfo.hStdInput = NULL;
startup_info.StartupInfo.hStdInput = nullptr;
startup_info.StartupInfo.hStdOutput = log_file_handle;
startup_info.StartupInfo.hStdError = log_file_handle;
@@ -539,7 +539,7 @@ namespace platf {
//
// Note: The value we point to here must be valid for the lifetime of the attribute list,
// so we need to point into the STARTUPINFO instead of our log_file_variable on the stack.
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &startup_info.StartupInfo.hStdOutput, sizeof(startup_info.StartupInfo.hStdOutput), NULL, NULL);
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &startup_info.StartupInfo.hStdOutput, sizeof(startup_info.StartupInfo.hStdOutput), nullptr, nullptr);
}
if (job) {
@@ -547,7 +547,7 @@ namespace platf {
//
// Note: The value we point to here must be valid for the lifetime of the attribute list,
// so we take a HANDLE* instead of just a HANDLE to use the caller's stack storage.
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, job, sizeof(*job), NULL, NULL);
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, job, sizeof(*job), nullptr, nullptr);
}
return startup_info;
@@ -555,11 +555,11 @@ namespace platf {
/**
* @brief This function overrides HKEY_CURRENT_USER and HKEY_CLASSES_ROOT using the provided token.
* @param token The primary token identifying the user to use, or `NULL` to restore original keys.
* @param token The primary token identifying the user to use, or `nullptr` to restore original keys.
* @return `true` if the override or restore operation was successful.
*/
bool override_per_user_predefined_keys(HANDLE token) {
HKEY user_classes_root = NULL;
HKEY user_classes_root = nullptr;
if (token) {
auto err = RegOpenUserClassesRoot(token, 0, GENERIC_ALL, &user_classes_root);
if (err != ERROR_SUCCESS) {
@@ -573,14 +573,14 @@ namespace platf {
}
});
HKEY user_key = NULL;
HKEY user_key = nullptr;
if (token) {
impersonate_current_user(token, [&]() {
// RegOpenCurrentUser() doesn't take a token. It assumes we're impersonating the desired user.
auto err = RegOpenCurrentUser(GENERIC_ALL, &user_key);
if (err != ERROR_SUCCESS) {
BOOST_LOG(error) << "Failed to open user key for target user: "sv << err;
user_key = NULL;
user_key = nullptr;
}
});
if (!user_key) {
@@ -602,7 +602,7 @@ namespace platf {
err = RegOverridePredefKey(HKEY_CURRENT_USER, user_key);
if (err != ERROR_SUCCESS) {
BOOST_LOG(error) << "Failed to override HKEY_CURRENT_USER: "sv << err;
RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL);
RegOverridePredefKey(HKEY_CLASSES_ROOT, nullptr);
return false;
}
@@ -671,7 +671,7 @@ namespace platf {
* @details This converts URLs and non-executable file paths into a runnable command like ShellExecute().
* @param raw_cmd The raw command provided by the user.
* @param working_dir The working directory for the new process.
* @param token The user token currently being impersonated or `NULL` if running as ourselves.
* @param token The user token currently being impersonated or `nullptr` if running as ourselves.
* @param creation_flags The creation flags for CreateProcess(), which may be modified by this function.
* @return A command string suitable for use by CreateProcess().
*/
@@ -757,7 +757,7 @@ namespace platf {
}
// Reset per-user keys back to the original value
override_per_user_predefined_keys(NULL);
override_per_user_predefined_keys(nullptr);
}
if (res != S_OK) {
@@ -972,7 +972,7 @@ namespace platf {
ec = impersonate_current_user(user_token, [&]() {
std::wstring env_block = create_environment_block(cloned_env);
std::wstring wcmd = resolve_command_string(cmd, start_dir, user_token, creation_flags);
ret = CreateProcessAsUserW(user_token, NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
ret = CreateProcessAsUserW(user_token, nullptr, (LPWSTR) wcmd.c_str(), nullptr, nullptr, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? nullptr : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
});
}
// Otherwise, launch the process using CreateProcessW()
@@ -995,8 +995,8 @@ namespace platf {
}
std::wstring env_block = create_environment_block(cloned_env);
std::wstring wcmd = resolve_command_string(cmd, start_dir, NULL, creation_flags);
ret = CreateProcessW(NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
std::wstring wcmd = resolve_command_string(cmd, start_dir, nullptr, creation_flags);
ret = CreateProcessW(nullptr, (LPWSTR) wcmd.c_str(), nullptr, nullptr, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? nullptr : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
}
// Use the results of the launch to create a bp::child object
@@ -1052,7 +1052,7 @@ namespace platf {
static std::once_flag load_wlanapi_once_flag;
std::call_once(load_wlanapi_once_flag, []() {
// wlanapi.dll is not installed by default on Windows Server, so we load it dynamically
HMODULE wlanapi = LoadLibraryExA("wlanapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
HMODULE wlanapi = LoadLibraryExA("wlanapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!wlanapi) {
BOOST_LOG(debug) << "wlanapi.dll is not available on this OS"sv;
return;
@@ -1129,7 +1129,7 @@ namespace platf {
fn_WlanFreeMemory(wlan_interface_list);
} else {
fn_WlanCloseHandle(wlan_handle, nullptr);
wlan_handle = NULL;
wlan_handle = nullptr;
}
}
}
@@ -1200,7 +1200,7 @@ namespace platf {
startup_info.StartupInfo.cb = sizeof(startup_info);
WCHAR executable[MAX_PATH];
if (GetModuleFileNameW(NULL, executable, ARRAYSIZE(executable)) == 0) {
if (GetModuleFileNameW(nullptr, executable, ARRAYSIZE(executable)) == 0) {
auto winerr = GetLastError();
BOOST_LOG(fatal) << "Failed to get Sunshine path: "sv << winerr;
return;
@@ -1220,7 +1220,7 @@ namespace platf {
void restart() {
// If we're running standalone, we have to respawn ourselves via CreateProcess().
// If we're running from the service, we should just exit and let it respawn us.
if (GetConsoleWindow() != NULL) {
if (GetConsoleWindow() != nullptr) {
// Avoid racing with the new process by waiting until we're exiting to start it.
atexit(restart_on_exit);
}
@@ -1538,7 +1538,7 @@ namespace platf {
}
virtual ~qos_t() {
if (!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET) NULL, flow_id, 0)) {
if (!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET) nullptr, flow_id, 0)) {
auto winerr = GetLastError();
BOOST_LOG(warning) << "QOSRemoveSocketFromFlow() failed: "sv << winerr;
}
@@ -1570,7 +1570,7 @@ namespace platf {
static std::once_flag load_qwave_once_flag;
std::call_once(load_qwave_once_flag, []() {
// qWAVE is not installed by default on Windows Server, so we load it dynamically
HMODULE qwave = LoadLibraryExA("qwave.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
HMODULE qwave = LoadLibraryExA("qwave.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!qwave) {
BOOST_LOG(debug) << "qwave.dll is not available on this OS"sv;
return;
@@ -1788,11 +1788,11 @@ namespace platf {
}
operator bool() override {
return timer != NULL;
return timer != nullptr;
}
private:
HANDLE timer = NULL;
HANDLE timer = nullptr;
};
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string_view>
// platform includes
#include <windows.h>
#include <Windows.h>
#include <winnt.h>
namespace platf {
@@ -60,7 +60,7 @@ namespace nvprefs {
void driver_settings_t::destroy() {
if (session_handle) {
NvAPI_DRS_DestroySession(session_handle);
session_handle = 0;
session_handle = nullptr;
}
NvAPI_Unload();
}
@@ -105,7 +105,7 @@ namespace nvprefs {
if (swapchain_data) {
NvAPI_Status status;
NvDRSProfileHandle profile_handle = 0;
NvDRSProfileHandle profile_handle = nullptr;
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
if (status != NVAPI_OK) {
nvapi_error_message(status);
@@ -168,7 +168,7 @@ namespace nvprefs {
return true;
}
NvDRSProfileHandle profile_handle = 0;
NvDRSProfileHandle profile_handle = nullptr;
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
if (status != NVAPI_OK) {
nvapi_error_message(status);
@@ -224,7 +224,7 @@ namespace nvprefs {
NvAPI_UnicodeString profile_name = {};
fill_nvapi_string(profile_name, sunshine_application_profile_name);
NvDRSProfileHandle profile_handle = 0;
NvDRSProfileHandle profile_handle = nullptr;
status = NvAPI_DRS_FindProfileByName(session_handle, profile_name, &profile_handle);
if (status != NVAPI_OK) {
@@ -36,7 +36,7 @@ namespace nvprefs {
bool check_and_modify_application_profile(bool &modified);
private:
NvDRSSessionHandle session_handle = 0;
NvDRSSessionHandle session_handle = nullptr;
};
} // namespace nvprefs
@@ -15,7 +15,7 @@
namespace {
std::map<const char *, void *> interfaces;
HMODULE dll = NULL;
HMODULE dll = nullptr;
template<typename Func, typename... Args>
NvAPI_Status call_interface(const char *name, Args... args) {
@@ -47,7 +47,7 @@ NvAPI_Initialize() {
auto dll_name = "nvapi.dll";
#endif
if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
if ((dll = LoadLibraryEx(dll_name, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
if (auto query_interface = (decltype(nvapi_QueryInterface) *) GetProcAddress(dll, "nvapi_QueryInterface")) {
for (const auto &item : nvapi_interface_table) {
interfaces[item.func] = query_interface(item.id);
@@ -64,7 +64,7 @@ NVAPI_INTERFACE NvAPI_Unload() {
if (dll) {
interfaces.clear();
FreeLibrary(dll);
dll = NULL;
dll = nullptr;
}
return NVAPI_OK;
}
@@ -7,8 +7,8 @@
// platform includes
// disable clang-format header reordering
// clang-format off
#include <windows.h>
#include <aclapi.h>
#include <Windows.h>
#include <AclAPI.h>
// clang-format on
// local includes
@@ -21,7 +21,7 @@ namespace nvprefs {
explicit operator bool() const {
auto handle = get();
return handle != NULL && handle != INVALID_HANDLE_VALUE;
return handle != nullptr && handle != INVALID_HANDLE_VALUE;
}
};
+2 -2
View File
@@ -51,7 +51,7 @@ namespace nvprefs {
std::optional<undo_file_t> undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) {
undo_file_t file;
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (file.file_handle) {
access_denied = false;
return file;
@@ -64,7 +64,7 @@ namespace nvprefs {
std::optional<undo_file_t> undo_file_t::create_new_file(std::filesystem::path file_path) {
undo_file_t file;
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr));
if (file.file_handle) {
// give GENERIC_READ, GENERIC_WRITE and DELETE permissions to Users group
+4 -4
View File
@@ -3,12 +3,12 @@
* @brief Definitions for Windows mDNS service registration.
*/
// platform includes
// winsock2.h must be included before windows.h
// WinSock2.h must be included before Windows.h
// clang-format off
#include <winsock2.h>
#include <windows.h>
#include <WinSock2.h>
#include <Windows.h>
// clang-format on
#include <windns.h>
#include <WinDNS.h>
#include <winerror.h>
// local includes
+1 -1
View File
@@ -158,7 +158,7 @@ namespace proc {
_env["SUNSHINE_CLIENT_GCMAP"] = std::to_string(launch_session->gcmap);
_env["SUNSHINE_CLIENT_HOST_AUDIO"] = launch_session->host_audio ? "true" : "false";
_env["SUNSHINE_CLIENT_ENABLE_SOPS"] = launch_session->enable_sops ? "true" : "false";
int channelCount = launch_session->surround_info & (65535);
int channelCount = launch_session->surround_info & 65535;
switch (channelCount) {
case 2:
_env["SUNSHINE_CLIENT_AUDIO_CONFIGURATION"] = "2.0";
+2 -1
View File
@@ -12,6 +12,7 @@ extern "C" {
// standard includes
#include <array>
#include <cctype>
#include <format>
#include <set>
#include <unordered_map>
#include <utility>
@@ -864,7 +865,7 @@ namespace rtsp_stream {
session_option.next = &port_option;
// Moonlight merely requires 'server_port=<port>'
auto port_value = "server_port=" + std::to_string(port);
auto port_value = std::format("server_port={}", static_cast<int>(port));
port_option.option = const_cast<char *>("Transport");
port_option.content = port_value.data();
+18 -18
View File
@@ -84,7 +84,7 @@ namespace system_tray {
#ifdef _WIN32
// If we're running in a service, return a special status to
// tell it to terminate too, otherwise it will just respawn us.
if (GetConsoleWindow() == NULL) {
if (GetConsoleWindow() == nullptr) {
lifetime::exit_sunshine(ERROR_SHUTDOWN_IN_PROGRESS, true);
return;
}
@@ -230,10 +230,10 @@ namespace system_tray {
return;
}
tray.notification_title = NULL;
tray.notification_text = NULL;
tray.notification_cb = NULL;
tray.notification_icon = NULL;
tray.notification_title = nullptr;
tray.notification_text = nullptr;
tray.notification_cb = nullptr;
tray.notification_icon = nullptr;
tray.icon = TRAY_ICON_PLAYING;
tray_update(&tray);
tray.icon = TRAY_ICON_PLAYING;
@@ -251,10 +251,10 @@ namespace system_tray {
return;
}
tray.notification_title = NULL;
tray.notification_text = NULL;
tray.notification_cb = NULL;
tray.notification_icon = NULL;
tray.notification_title = nullptr;
tray.notification_text = nullptr;
tray.notification_cb = nullptr;
tray.notification_icon = nullptr;
tray.icon = TRAY_ICON_PAUSING;
tray_update(&tray);
char msg[256];
@@ -272,10 +272,10 @@ namespace system_tray {
return;
}
tray.notification_title = NULL;
tray.notification_text = NULL;
tray.notification_cb = NULL;
tray.notification_icon = NULL;
tray.notification_title = nullptr;
tray.notification_text = nullptr;
tray.notification_cb = nullptr;
tray.notification_icon = nullptr;
tray.icon = TRAY_ICON;
tray_update(&tray);
char msg[256];
@@ -293,10 +293,10 @@ namespace system_tray {
return;
}
tray.notification_title = NULL;
tray.notification_text = NULL;
tray.notification_cb = NULL;
tray.notification_icon = NULL;
tray.notification_title = nullptr;
tray.notification_text = nullptr;
tray.notification_cb = nullptr;
tray.notification_icon = nullptr;
tray.icon = TRAY_ICON;
tray_update(&tray);
tray.icon = TRAY_ICON;
@@ -305,7 +305,7 @@ namespace system_tray {
tray.notification_icon = TRAY_ICON_LOCKED;
tray.tooltip = PROJECT_NAME;
tray.notification_cb = []() {
launch_ui_with_path("/pin");
launch_ui("/pin");
};
tray_update(&tray);
}
+5 -4
View File
@@ -1890,9 +1890,10 @@ namespace video {
}
});
// set minimum frame time based on client-requested target framerate
std::chrono::duration<double, std::milli> minimum_frame_time {1000.0 / config.framerate};
BOOST_LOG(info) << "Minimum frame time set to "sv << minimum_frame_time.count() << "ms, based on client-requested target framerate "sv << config.framerate << "."sv;
// set max frame time based on client-requested target framerate.
double minimum_fps_target = (config::video.minimum_fps_target > 0.0) ? config::video.minimum_fps_target : config.framerate;
std::chrono::duration<double, std::milli> max_frametime {1000.0 / minimum_fps_target};
BOOST_LOG(info) << "Minimum FPS target set to ~"sv << (minimum_fps_target / 2) << "fps ("sv << max_frametime.count() * 2 << "ms)"sv;
auto shutdown_event = mail->event<bool>(mail::shutdown);
auto packets = mail::man->queue<packet_t>(mail::video_packets);
@@ -1943,7 +1944,7 @@ namespace video {
// Encode at a minimum FPS to avoid image quality issues with static content
if (!requested_idr_frame || images->peek()) {
if (auto img = images->pop(minimum_frame_time)) {
if (auto img = images->pop(max_frametime)) {
frame_timestamp = img->frame_timestamp;
if (session->convert(*img)) {
BOOST_LOG(error) << "Could not convert image"sv;
+1
View File
@@ -179,6 +179,7 @@
"dd_mode_remapping": {"mixed": [], "resolution_only": [], "refresh_rate_only": []},
"dd_wa_hdr_toggle_delay": 0,
"max_bitrate": 0,
"minimum_fps_target": 0
},
},
{
@@ -17,6 +17,13 @@ const config = ref(props.config)
<input type="number" class="form-control" id="max_bitrate" placeholder="0" v-model="config.max_bitrate" />
<div class="form-text">{{ $t("config.max_bitrate_desc") }}</div>
</div>
<!--minimum_fps_target-->
<div class="mb-3">
<label for="minimum_fps_target" class="form-label">{{ $t("config.minimum_fps_target") }}</label>
<input type="number" min="0" max="1000" class="form-control" id="minimum_fps_target" placeholder="0" v-model="config.minimum_fps_target" />
<div class="form-text">{{ $t("config.minimum_fps_target_desc") }}</div>
</div>
</template>
<style scoped>
@@ -256,6 +256,8 @@
"log_path_desc": "The file where the current logs of Sunshine are stored.",
"max_bitrate": "Maximum Bitrate",
"max_bitrate_desc": "The maximum bitrate (in Kbps) that Sunshine will encode the stream at. If set to 0, it will always use the bitrate requested by Moonlight.",
"minimum_fps_target": "Minimum FPS Target",
"minimum_fps_target_desc": "The lowest effective FPS a stream can reach. A value of 0 is treated as roughly half of the stream's FPS. A setting of 20 is recommended if you stream 24 or 30fps content.",
"min_threads": "Minimum CPU Thread Count",
"min_threads_desc": "Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest value that can reliably encode at your desired streaming settings on your hardware.",
"misc": "Miscellaneous options",
+2 -1
View File
@@ -27,7 +27,8 @@ for dir in ${DIRECTORIES}; do
# remove the directory if it is empty
if [[ $empty_dir != "" ]]; then # prevent the loop from running and failing if no directories found
for i in "${empty_dir}"; do # don't split words as we already know this will be a single directory
# shellcheck disable=SC2066 # don't split words as we already know this will be a single directory
for i in "${empty_dir}"; do
echo "Removing empty directory: ${i}"
rmdir "${i}"
done
+2 -1
View File
@@ -4,6 +4,7 @@
*/
#include "../tests_common.h"
#include <format>
#include <src/config.h>
#include <src/display_device.h>
#include <src/rtsp.h>
@@ -473,7 +474,7 @@ namespace {
} else {
const auto [manual_res] = std::get<manual_value_t<res_t>>(input_res);
video_config.dd.resolution_option = manual;
video_config.dd.manual_resolution = std::to_string(manual_res.m_width) + "x"s + std::to_string(manual_res.m_height);
video_config.dd.manual_resolution = std::format("{}x{}", static_cast<int>(manual_res.m_width), static_cast<int>(manual_res.m_height));
}
}
+3 -2
View File
@@ -4,6 +4,7 @@
*/
#include "../tests_common.h"
#include <format>
#include <src/file_handler.h>
struct FileHandlerParentDirectoryTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
@@ -79,13 +80,13 @@ Hey, hey, hey!
TEST_P(FileHandlerTests, WriteFileTest) {
auto [fileNum, content] = GetParam();
std::string fileName = "write_file_test_" + std::to_string(fileNum) + ".txt";
const std::string fileName = std::format("write_file_test_{}.txt", fileNum);
EXPECT_EQ(file_handler::write_file(fileName.c_str(), content), 0);
}
TEST_P(FileHandlerTests, ReadFileTest) {
auto [fileNum, content] = GetParam();
std::string fileName = "write_file_test_" + std::to_string(fileNum) + ".txt";
const std::string fileName = std::format("write_file_test_{}.txt", fileNum);
EXPECT_EQ(file_handler::read_file(fileName.c_str()), content);
}
+2 -1
View File
@@ -5,6 +5,7 @@
#include "../tests_common.h"
#include "../tests_log_checker.h"
#include <format>
#include <random>
#include <src/logging.h>
@@ -39,7 +40,7 @@ TEST_P(LogLevelsTest, PutMessage) {
std::random_device rand_dev;
std::mt19937_64 rand_gen(rand_dev());
auto test_message = std::to_string(rand_gen()) + std::to_string(rand_gen());
auto test_message = std::format("{}{}", rand_gen(), rand_gen());
BOOST_LOG(logger) << test_message;
ASSERT_TRUE(log_checker::line_contains(log_file, test_message));
+1 -1
View File
@@ -5,7 +5,7 @@
#define INITGUID
// platform includes
#include <audioclient.h>
#include <Audioclient.h>
#include <codecvt>
#include <iostream>
#include <locale>
+34 -33
View File
@@ -3,9 +3,10 @@
* @brief Handles launching Sunshine.exe into user sessions as SYSTEM
*/
#define WIN32_LEAN_AND_MEAN
#include <format>
#include <string>
#include <Windows.h>
#include <wtsapi32.h>
#include <WtsApi32.h>
// PROC_THREAD_ATTRIBUTE_JOB_LIST is currently missing from MinGW headers
#ifndef PROC_THREAD_ATTRIBUTE_JOB_LIST
@@ -51,9 +52,9 @@ DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, L
}
HANDLE CreateJobObjectForChildProcess() {
HANDLE job_handle = CreateJobObjectW(NULL, NULL);
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
if (!job_handle) {
return NULL;
return nullptr;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limit_info = {};
@@ -68,7 +69,7 @@ HANDLE CreateJobObjectForChildProcess() {
if (!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &job_limit_info, sizeof(job_limit_info))) {
CloseHandle(job_handle);
return NULL;
return nullptr;
}
return job_handle;
@@ -76,16 +77,16 @@ HANDLE CreateJobObjectForChildProcess() {
LPPROC_THREAD_ATTRIBUTE_LIST AllocateProcThreadAttributeList(DWORD attribute_count) {
SIZE_T size;
InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size);
InitializeProcThreadAttributeList(nullptr, attribute_count, 0, &size);
auto list = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, size);
if (list == NULL) {
return NULL;
if (list == nullptr) {
return nullptr;
}
if (!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) {
HeapFree(GetProcessHeap(), 0, list);
return NULL;
return nullptr;
}
return list;
@@ -94,14 +95,14 @@ LPPROC_THREAD_ATTRIBUTE_LIST AllocateProcThreadAttributeList(DWORD attribute_cou
HANDLE DuplicateTokenForSession(DWORD console_session_id) {
HANDLE current_token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &current_token)) {
return NULL;
return nullptr;
}
// Duplicate our own LocalSystem token
HANDLE new_token;
if (!DuplicateTokenEx(current_token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &new_token)) {
if (!DuplicateTokenEx(current_token, TOKEN_ALL_ACCESS, nullptr, SecurityImpersonation, TokenPrimary, &new_token)) {
CloseHandle(current_token);
return NULL;
return nullptr;
}
CloseHandle(current_token);
@@ -109,7 +110,7 @@ HANDLE DuplicateTokenForSession(DWORD console_session_id) {
// Change the duplicated token to the console session ID
if (!SetTokenInformation(new_token, TokenSessionId, &console_session_id, sizeof(console_session_id))) {
CloseHandle(new_token);
return NULL;
return nullptr;
}
return new_token;
@@ -123,21 +124,21 @@ HANDLE OpenLogFileHandle() {
wcscat_s(log_file_name, L"sunshine.log");
// The file handle must be inheritable for our child process to use it
SECURITY_ATTRIBUTES security_attributes = {sizeof(security_attributes), NULL, TRUE};
SECURITY_ATTRIBUTES security_attributes = {sizeof(security_attributes), nullptr, TRUE};
// Overwrite the old sunshine.log
return CreateFileW(log_file_name, GENERIC_WRITE, FILE_SHARE_READ, &security_attributes, CREATE_ALWAYS, 0, NULL);
return CreateFileW(log_file_name, GENERIC_WRITE, FILE_SHARE_READ, &security_attributes, CREATE_ALWAYS, 0, nullptr);
}
bool RunTerminationHelper(HANDLE console_token, DWORD pid) {
WCHAR module_path[MAX_PATH];
GetModuleFileNameW(NULL, module_path, _countof(module_path));
GetModuleFileNameW(nullptr, module_path, _countof(module_path));
std::wstring command;
command += L'"';
command += module_path;
command += L'"';
command += L" --terminate " + std::to_wstring(pid);
command += std::format(L" --terminate {}", pid);
STARTUPINFOW startup_info = {};
startup_info.cb = sizeof(startup_info);
@@ -146,7 +147,7 @@ bool RunTerminationHelper(HANDLE console_token, DWORD pid) {
// Execute ourselves as a detached process in the user session with the --terminate argument.
// This will allow us to attach to Sunshine's console and send it a Ctrl-C event.
PROCESS_INFORMATION process_info;
if (!CreateProcessAsUserW(console_token, module_path, (LPWSTR) command.c_str(), NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS, NULL, NULL, &startup_info, &process_info)) {
if (!CreateProcessAsUserW(console_token, module_path, (LPWSTR) command.c_str(), nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS, nullptr, nullptr, &startup_info, &process_info)) {
return false;
}
@@ -166,8 +167,8 @@ bool RunTerminationHelper(HANDLE console_token, DWORD pid) {
}
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
service_status_handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, HandlerEx, NULL);
if (service_status_handle == NULL) {
service_status_handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, HandlerEx, nullptr);
if (service_status_handle == nullptr) {
// Nothing we can really do here but terminate ourselves
ExitProcess(GetLastError());
return;
@@ -184,8 +185,8 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
SetServiceStatus(service_status_handle, &service_status);
// Create a manual-reset stop event
stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
if (stop_event == NULL) {
stop_event = CreateEventA(nullptr, TRUE, FALSE, nullptr);
if (stop_event == nullptr) {
// Tell SCM we failed to start
service_status.dwWin32ExitCode = GetLastError();
service_status.dwCurrentState = SERVICE_STOPPED;
@@ -194,8 +195,8 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
}
// Create an auto-reset session change event
session_change_event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (session_change_event == NULL) {
session_change_event = CreateEventA(nullptr, FALSE, FALSE, nullptr);
if (session_change_event == nullptr) {
// Tell SCM we failed to start
service_status.dwWin32ExitCode = GetLastError();
service_status.dwCurrentState = SERVICE_STOPPED;
@@ -217,13 +218,13 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
startup_info.StartupInfo.cb = sizeof(startup_info);
startup_info.StartupInfo.lpDesktop = (LPWSTR) L"winsta0\\default";
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
startup_info.StartupInfo.hStdInput = NULL;
startup_info.StartupInfo.hStdInput = nullptr;
startup_info.StartupInfo.hStdOutput = log_file_handle;
startup_info.StartupInfo.hStdError = log_file_handle;
// Allocate an attribute list with space for 2 entries
startup_info.lpAttributeList = AllocateProcThreadAttributeList(2);
if (startup_info.lpAttributeList == NULL) {
if (startup_info.lpAttributeList == nullptr) {
// Tell SCM we failed to start
service_status.dwWin32ExitCode = GetLastError();
service_status.dwCurrentState = SERVICE_STOPPED;
@@ -232,7 +233,7 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
}
// Only allow Sunshine.exe to inherit the log file handle, not all inheritable handles
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &log_file_handle, sizeof(log_file_handle), NULL, NULL);
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &log_file_handle, sizeof(log_file_handle), nullptr, nullptr);
// Tell SCM we're running (and stoppable now)
service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE;
@@ -248,22 +249,22 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
}
auto console_token = DuplicateTokenForSession(console_session_id);
if (console_token == NULL) {
if (console_token == nullptr) {
continue;
}
// Job objects cannot span sessions, so we must create one for each process
auto job_handle = CreateJobObjectForChildProcess();
if (job_handle == NULL) {
if (job_handle == nullptr) {
CloseHandle(console_token);
continue;
}
// Start Sunshine.exe inside our job object
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &job_handle, sizeof(job_handle), NULL, NULL);
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &job_handle, sizeof(job_handle), nullptr, nullptr);
PROCESS_INFORMATION process_info;
if (!CreateProcessAsUserW(console_token, L"Sunshine.exe", NULL, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOW) &startup_info, &process_info)) {
if (!CreateProcessAsUserW(console_token, L"Sunshine.exe", nullptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, (LPSTARTUPINFOW) &startup_info, &process_info)) {
CloseHandle(console_token);
CloseHandle(job_handle);
continue;
@@ -326,7 +327,7 @@ int DoGracefulTermination(DWORD pid) {
}
// Disable our own Ctrl-C handling
SetConsoleCtrlHandler(NULL, TRUE);
SetConsoleCtrlHandler(nullptr, TRUE);
// Send a Ctrl-C event to Sunshine
if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
@@ -339,7 +340,7 @@ int DoGracefulTermination(DWORD pid) {
int main(int argc, char *argv[]) {
static const SERVICE_TABLE_ENTRY service_table[] = {
{(LPSTR) SERVICE_NAME, ServiceMain},
{NULL, NULL}
{nullptr, nullptr}
};
// Check if this is a reinvocation of ourselves to send Ctrl-C to Sunshine.exe
@@ -351,7 +352,7 @@ int main(int argc, char *argv[]) {
// We want to use the directory where Sunshine.exe is located instead of system32.
// This requires stripping off 2 path components: the file name and the last folder
WCHAR module_path[MAX_PATH];
GetModuleFileNameW(NULL, module_path, _countof(module_path));
GetModuleFileNameW(nullptr, module_path, _countof(module_path));
for (auto i = 0; i < 2; i++) {
auto last_sep = wcsrchr(module_path, '\\');
if (last_sep) {