remove unused files

This commit is contained in:
pigeatgarlic 2024-09-01 01:03:37 +07:00
parent f6659e6641
commit 31c237bda4
108 changed files with 0 additions and 14588 deletions

View File

@ -1,11 +0,0 @@
# install dependencies for C++ analysis
set -e
chmod +x ./scripts/linux_build.sh
./scripts/linux_build.sh --skip-package --ubuntu-test-repo
# Delete CUDA
rm -rf ./build/cuda
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@ -1,45 +0,0 @@
# install dependencies for C++ analysis
set -e
# update pacman
pacman --noconfirm -Suy
# install wget
pacman --noconfirm -S \
wget
# download working curl
wget https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-curl-8.8.0-1-any.pkg.tar.zst
# install dependencies
pacman -U --noconfirm mingw-w64-ucrt-x86_64-curl-8.8.0-1-any.pkg.tar.zst
pacman -Syu --noconfirm --ignore=mingw-w64-ucrt-x86_64-curl \
base-devel \
diffutils \
gcc \
git \
make \
mingw-w64-ucrt-x86_64-boost \
mingw-w64-ucrt-x86_64-cmake \
mingw-w64-ucrt-x86_64-cppwinrt \
mingw-w64-ucrt-x86_64-graphviz \
mingw-w64-ucrt-x86_64-miniupnpc \
mingw-w64-ucrt-x86_64-nlohmann-json \
mingw-w64-ucrt-x86_64-nodejs \
mingw-w64-ucrt-x86_64-nsis \
mingw-w64-ucrt-x86_64-onevpl \
mingw-w64-ucrt-x86_64-openssl \
mingw-w64-ucrt-x86_64-opus \
mingw-w64-ucrt-x86_64-rust \
mingw-w64-ucrt-x86_64-toolchain
# build
mkdir -p build
cd build || exit 1
cmake \
-DBUILD_DOCS=OFF \
-G "MinGW Makefiles" ..
mingw32-make -j"$(nproc)"
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@ -1,23 +0,0 @@
# 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 \
-DBOOST_USE_STATIC=OFF \
-DBUILD_DOCS=OFF \
-G "Unix Makefiles" ..
make -j"$(sysctl -n hw.logicalcpu)"
# skip autobuild
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"

View File

@ -1,188 +0,0 @@
---
name: Bug Report
description: Create a bug report to help us improve.
body:
- type: markdown
attributes:
value: >
**THIS IS NOT THE PLACE TO ASK FOR SUPPORT!**
Please use our [Support Center](https://app.lizardbyte.dev/support) for support issues.
Non actionable bug reports will be LOCKED and CLOSED!
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
- type: checkboxes
attributes:
label: Is your issue described in the documentation?
description: Please read our [documentation](https://docs.lizardbyte.dev/projects/sunshine)
options:
- label: I have read the documentation
- type: dropdown
attributes:
label: Is your issue present in the latest beta/pre-release?
description: Please test the latest [pre-release](https://github.com/LizardByte/Sunshine/releases).
options:
- "I didn't read the issue template"
- "I'm too lazy to test"
- "This issue is present in the latest pre-release"
- type: textarea
id: description
attributes:
label: Describe the Bug
description: A clear and concise description of the bug, list the reproduction steps.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
- type: textarea
id: additional
attributes:
label: Additional Context
description: Add any other context about the bug here.
- type: dropdown
id: os
attributes:
label: Host Operating System
description: What version operating system are you running the software on?
options:
- Docker
- Linux
- macOS
- Windows
- other, n/a
validations:
required: true
- type: input
id: os-version
attributes:
label: Operating System Version
description: Provide the version of the operating system. Additionally a build number would be helpful.
validations:
required: true
- type: dropdown
id: os-architecture
attributes:
label: Architecture
options:
- 32 bit
- 64 bit
- arm
- other, n/a
validations:
required: true
- type: input
id: version
attributes:
label: Sunshine commit or version
description: |
Use `sunshine --verison` to get the version, or get the version from web UI.
Please don't just copy the latest commit from our repo, if that's not the commit you're actually using.
validations:
required: true
- type: dropdown
id: package_type
attributes:
label: Package
description: The package you installed
options:
- Linux - AppImage
- Linux - AUR (Third Party)
- Linux - deb
- Linux - Docker
- Linux - flathub (Third Party)
- Linux - flatpak
- Linux - Homebrew
- Linux - nixpkgs (Third Party)
- Linux - pkg.tar.zst
- Linux - rpm
- Linux - solus (Third Party)
- macOS - Homebrew
- macOS - Portfile
- Windows - Chocolatey (Third Party)
- Windows - installer
- Windows - portable
- Windows - Scoop (Third Party)
- Windows - Winget
- other (not listed)
- other (self built)
- other (fork of this repo)
- n/a
validations:
required: true
- type: dropdown
id: graphics_type
attributes:
label: GPU Type
description: The type of the installed graphics card.
options:
- AMD
- Apple Silicon
- Intel
- Nvidia
- none (software encoding)
- n/a
validations:
required: true
- type: input
id: graphics_model
attributes:
label: GPU Model
description: The model of the installed graphics card.
validations:
required: true
- type: input
id: graphics_driver
attributes:
label: GPU Driver/Mesa Version
description: The driver/mesa version of the installed graphics card.
validations:
required: true
- type: dropdown
id: capture_method
attributes:
label: Capture Method
description: The capture method being used.
options:
- AVCaptureScreen (macOS)
- KMX (Linux)
- NvFBC (Linux)
- wlroots (Linux)
- X11 (Linux)
- Desktop Duplication API (Windows)
- Windows.Graphics.Capture (Windows)
validations:
required: false
- type: textarea
id: config
attributes:
label: Config
description: |
Please copy and paste your config (`sunshine.conf`) file.
render: Shell
validations:
required: true
- type: textarea
id: apps
attributes:
label: Apps
description: |
If this is an issue with launching a game or app, please copy and paste your `apps.json` file.
render: json
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output
description: |
Please copy and paste any relevant log output. This will be automatically formatted into code,
so no need for backticks.
render: shell
validations:
required: true

View File

@ -1,13 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
blank_issues_enabled: false
contact_links:
- name: Support Center
url: https://app.lizardbyte.dev/support
about: Official LizardByte support
- name: Feature request
url: https://ideas.moonlight-stream.org
about: Share your suggestions or ideas to help Moonlight and Sunshine improve

View File

@ -1,48 +0,0 @@
---
# This file is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
time: "08:00"
open-pull-requests-limit: 10
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
time: "08:30"
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
time: "09:00"
open-pull-requests-limit: 10
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
time: "09:30"
open-pull-requests-limit: 10
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
time: "10:00"
open-pull-requests-limit: 10
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "daily"
time: "10:30"
open-pull-requests-limit: 10

View File

@ -1,49 +0,0 @@
---
# This file is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Configuration for Label Actions - https://github.com/dessant/label-actions
added:
comment: >
This feature has been added and will be available in the next release.
fixed:
comment: >
This issue has been fixed and will be available in the next release.
invalid:duplicate:
comment: >
:wave: @{issue-author}, this appears to be a duplicate of a pre-existing issue.
close: true
lock: true
unlabel: 'status:awaiting-triage'
-invalid:duplicate:
reopen: true
unlock: true
invalid:support:
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively for bug reports.
However, this issue appears to be a support request. Please use our
[Support Center](https://app.lizardbyte.dev/support) for support issues. Thanks.
close: true
lock: true
lock-reason: 'off-topic'
unlabel: 'status:awaiting-triage'
-invalid:support:
reopen: true
unlock: true
invalid:template-incomplete:
issues:
comment: >
:wave: @{issue-author}, please edit your issue to complete the template with
all the required info. Your issue will be automatically closed in 5 days if
the template is not completed. Thanks.
prs:
comment: >
:wave: @{issue-author}, please edit your PR to complete the template with
all the required info. Your PR will be automatically closed in 5 days if
the template is not completed. Thanks.

14
.github/semantic.yml vendored
View File

@ -1,14 +0,0 @@
---
# This file is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# This is the configuration file for https://github.com/Ezard/semantic-prs
enabled: true
titleOnly: true # We only use the PR title as we squash and merge
commitsOnly: false
titleAndCommits: false
anyCommit: false
allowMergeCommits: false
allowRevertCommits: false

1199
.github/workflows/CI.yml vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,385 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# This workflow is intended to work with all our organization Docker projects. A readme named `DOCKER_README.md`
# will be used to update the description on Docker hub.
# custom comments in dockerfiles:
# `# platforms: `
# Comma separated list of platforms, i.e. `# platforms: linux/386,linux/amd64`. Docker platforms can alternatively
# be listed in a file named `.docker_platforms`.
# `# platforms_pr: `
# Comma separated list of platforms to run for PR events, i.e. `# platforms_pr: linux/amd64`. This will take
# precedence over the `# platforms: ` directive.
# `# artifacts: `
# `true` to build in two steps, stopping at `artifacts` build stage and extracting the image from there to the
# GitHub runner.
name: CI Docker
on:
pull_request:
branches: [master]
types: [opened, synchronize, reopened]
push:
branches: [master]
workflow_dispatch:
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
check_dockerfiles:
name: Check Dockerfiles
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Find dockerfiles
id: find
run: |
dockerfiles=$(find . -type f -iname "Dockerfile" -o -iname "*.dockerfile")
echo "found dockerfiles: ${dockerfiles}"
# do not quote to keep this as a single line
echo dockerfiles=${dockerfiles} >> $GITHUB_OUTPUT
MATRIX_COMBINATIONS=""
for FILE in ${dockerfiles}; do
# extract tag from file name
tag=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*\/(Dockerfile)/None/gm')
if [[ $tag == "None" ]]; then
MATRIX_COMBINATIONS="$MATRIX_COMBINATIONS {\"dockerfile\": \"$FILE\"},"
else
tag=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*\/(.+)(\.dockerfile)/-\2/gm')
MATRIX_COMBINATIONS="$MATRIX_COMBINATIONS {\"dockerfile\": \"$FILE\", \"tag\": \"$tag\"},"
fi
done
# removes the last character (i.e. comma)
MATRIX_COMBINATIONS=${MATRIX_COMBINATIONS::-1}
# setup matrix for later jobs
matrix=$((
echo "{ \"include\": [$MATRIX_COMBINATIONS] }"
) | jq -c .)
echo $matrix
echo $matrix | jq .
echo "matrix=$matrix" >> $GITHUB_OUTPUT
- name: Find dotnet solution file
id: find_dotnet
run: |
solution=$(find . -maxdepth 1 -type f -iname "*.sln")
echo "found solution: ${solution}"
# do not quote to keep this as a single line
echo solution=${solution} >> $GITHUB_OUTPUT
if [[ $solution != "" ]]; then
echo "dotnet=true" >> $GITHUB_OUTPUT
else
echo "dotnet=false" >> $GITHUB_OUTPUT
fi
outputs:
dockerfiles: ${{ steps.find.outputs.dockerfiles }}
matrix: ${{ steps.find.outputs.matrix }}
dotnet: ${{ steps.find_dotnet.outputs.dotnet }}
solution: ${{ steps.find_dotnet.outputs.solution }}
setup_release:
if: ${{ needs.check_dockerfiles.outputs.dockerfiles }}
name: Setup Release
needs:
- check_dockerfiles
outputs:
publish_release: ${{ steps.setup_release.outputs.publish_release }}
release_body: ${{ steps.setup_release.outputs.release_body }}
release_commit: ${{ steps.setup_release.outputs.release_commit }}
release_generate_release_notes: ${{ steps.setup_release.outputs.release_generate_release_notes }}
release_tag: ${{ steps.setup_release.outputs.release_tag }}
release_version: ${{ steps.setup_release.outputs.release_version }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Release
id: setup_release
uses: LizardByte/setup-release-action@v2024.801.192524
with:
dotnet: ${{ needs.check_dockerfiles.outputs.dotnet }}
github_token: ${{ secrets.GITHUB_TOKEN }}
lint_dockerfile:
needs: [check_dockerfiles]
if: ${{ needs.check_dockerfiles.outputs.dockerfiles }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.check_dockerfiles.outputs.matrix) }}
name: Lint Dockerfile${{ matrix.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Hadolint
id: hadolint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: ${{ matrix.dockerfile }}
ignore: DL3008,DL3013,DL3016,DL3018,DL3028,DL3059
output-file: ./hadolint.log
verbose: true
- name: Log
if: failure()
run: |
echo "Hadolint outcome: ${{ steps.hadolint.outcome }}" >> $GITHUB_STEP_SUMMARY
cat "./hadolint.log" >> $GITHUB_STEP_SUMMARY
docker:
needs: [check_dockerfiles, setup_release]
if: ${{ needs.check_dockerfiles.outputs.dockerfiles }}
runs-on: ubuntu-latest
permissions:
packages: write
contents: write
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.check_dockerfiles.outputs.matrix) }}
name: Docker${{ matrix.tag }}
steps:
- name: Maximize build space
uses: easimon/maximize-build-space@v10
with:
root-reserve-mb: 30720 # https://github.com/easimon/maximize-build-space#caveats
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-docker-images: 'true'
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Prepare
id: prepare
env:
NV: ${{ needs.setup_release.outputs.release_tag }}
run: |
# get branch name
BRANCH=${GITHUB_HEAD_REF}
RELEASE=${{ needs.setup_release.outputs.publish_release }}
COMMIT=${{ needs.setup_release.outputs.release_commit }}
if [ -z "$BRANCH" ]; then
echo "This is a PUSH event"
BRANCH=${{ github.ref_name }}
CLONE_URL=${{ github.event.repository.clone_url }}
else
echo "This is a PULL REQUEST event"
CLONE_URL=${{ github.event.pull_request.head.repo.clone_url }}
fi
# determine to push image to dockerhub and ghcr or not
if [[ $GITHUB_EVENT_NAME == "push" ]]; then
PUSH=true
else
PUSH=false
fi
# setup the tags
REPOSITORY=${{ github.repository }}
BASE_TAG=$(echo $REPOSITORY | tr '[:upper:]' '[:lower:]')
TAGS="${BASE_TAG}:${COMMIT:0:7}${{ matrix.tag }},ghcr.io/${BASE_TAG}:${COMMIT:0:7}${{ matrix.tag }}"
if [[ $GITHUB_REF == refs/heads/master ]]; then
TAGS="${TAGS},${BASE_TAG}:latest${{ matrix.tag }},ghcr.io/${BASE_TAG}:latest${{ matrix.tag }}"
TAGS="${TAGS},${BASE_TAG}:master${{ matrix.tag }},ghcr.io/${BASE_TAG}:master${{ matrix.tag }}"
else
TAGS="${TAGS},${BASE_TAG}:test${{ matrix.tag }},ghcr.io/${BASE_TAG}:test${{ matrix.tag }}"
fi
if [[ ${NV} != "" ]]; then
TAGS="${TAGS},${BASE_TAG}:${NV}${{ matrix.tag }},ghcr.io/${BASE_TAG}:${NV}${{ matrix.tag }}"
fi
# parse custom directives out of dockerfile
# try to get the platforms from the dockerfile custom directive, i.e. `# platforms: xxx,yyy`
# directives for PR event, i.e. not push event
if [[ ${RELEASE} == "false" ]]; then
while read -r line; do
if [[ $line == "# platforms_pr: "* && $PLATFORMS == "" ]]; then
# echo the line and use `sed` to remove the custom directive
PLATFORMS=$(echo -e "$line" | sed 's/# platforms_pr: //')
elif [[ $PLATFORMS != "" ]]; then
# break while loop once all custom "PR" event directives are found
break
fi
done <"${{ matrix.dockerfile }}"
fi
# directives for all events... above directives will not be parsed if they were already found
while read -r line; do
if [[ $line == "# platforms: "* && $PLATFORMS == "" ]]; then
# echo the line and use `sed` to remove the custom directive
PLATFORMS=$(echo -e "$line" | sed 's/# platforms: //')
elif [[ $line == "# artifacts: "* && $ARTIFACTS == "" ]]; then
# echo the line and use `sed` to remove the custom directive
ARTIFACTS=$(echo -e "$line" | sed 's/# artifacts: //')
elif [[ $line == "# no-cache-filters: "* && $NO_CACHE_FILTERS == "" ]]; then
# echo the line and use `sed` to remove the custom directive
NO_CACHE_FILTERS=$(echo -e "$line" | sed 's/# no-cache-filters: //')
elif [[ $PLATFORMS != "" && $ARTIFACTS != "" && $NO_CACHE_FILTERS != "" ]]; then
# break while loop once all custom directives are found
break
fi
done <"${{ matrix.dockerfile }}"
# if PLATFORMS is blank, fall back to the legacy method of reading from the `.docker_platforms` file
if [[ $PLATFORMS == "" ]]; then
# read the platforms from `.docker_platforms`
PLATFORMS=$(<.docker_platforms)
fi
# if PLATFORMS is still blank, fall back to `linux/amd64`
if [[ $PLATFORMS == "" ]]; then
PLATFORMS="linux/amd64"
fi
echo "branch=${BRANCH}" >> $GITHUB_OUTPUT
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
echo "clone_url=${CLONE_URL}" >> $GITHUB_OUTPUT
echo "artifacts=${ARTIFACTS}" >> $GITHUB_OUTPUT
echo "no_cache_filters=${NO_CACHE_FILTERS}" >> $GITHUB_OUTPUT
echo "platforms=${PLATFORMS}" >> $GITHUB_OUTPUT
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- name: Set Up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
- name: Cache Docker Layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: Docker-buildx${{ matrix.tag }}-${{ github.sha }}
restore-keys: |
Docker-buildx${{ matrix.tag }}-
- name: Log in to Docker Hub
if: ${{ needs.setup_release.outputs.publish_release == 'true' }} # PRs do not have access to secrets
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Log in to the Container registry
if: ${{ needs.setup_release.outputs.publish_release == 'true' }} # PRs do not have access to secrets
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.GH_BOT_NAME }}
password: ${{ secrets.GH_BOT_TOKEN }}
- name: Build artifacts
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
id: build_artifacts
uses: docker/build-push-action@v6
with:
context: ./
file: ${{ matrix.dockerfile }}
target: artifacts
outputs: type=local,dest=artifacts
push: false
platforms: ${{ steps.prepare.outputs.platforms }}
build-args: |
BRANCH=${{ steps.prepare.outputs.branch }}
BUILD_DATE=${{ steps.prepare.outputs.build_date }}
BUILD_VERSION=${{ needs.setup_release.outputs.release_tag }}
COMMIT=${{ needs.setup_release.outputs.release_commit }}
CLONE_URL=${{ steps.prepare.outputs.clone_url }}
RELEASE=${{ needs.setup_release.outputs.publish_release }}
tags: ${{ steps.prepare.outputs.tags }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
no-cache-filters: ${{ steps.prepare.outputs.no_cache_filters }}
- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
context: ./
file: ${{ matrix.dockerfile }}
push: ${{ needs.setup_release.outputs.publish_release }}
platforms: ${{ steps.prepare.outputs.platforms }}
build-args: |
BRANCH=${{ steps.prepare.outputs.branch }}
BUILD_DATE=${{ steps.prepare.outputs.build_date }}
BUILD_VERSION=${{ needs.setup_release.outputs.release_tag }}
COMMIT=${{ needs.setup_release.outputs.release_commit }}
CLONE_URL=${{ steps.prepare.outputs.clone_url }}
RELEASE=${{ needs.setup_release.outputs.publish_release }}
tags: ${{ steps.prepare.outputs.tags }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
no-cache-filters: ${{ steps.prepare.outputs.no_cache_filters }}
- name: Arrange Artifacts
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
working-directory: artifacts
run: |
# artifacts will be in sub directories named after the docker target platform, e.g. `linux_amd64`
# so move files to the artifacts directory
# https://unix.stackexchange.com/a/52816
find ./ -type f -exec mv -t ./ -n '{}' +
# remove provenance file
rm -f ./provenance.json
- name: Upload Artifacts
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
uses: actions/upload-artifact@v4
with:
name: Docker${{ matrix.tag }}
path: artifacts/
- name: Create/Update GitHub Release
if: ${{ needs.setup_release.outputs.publish_release == 'true' && steps.prepare.outputs.artifacts == 'true' }}
uses: LizardByte/create-release-action@v2024.614.221009
with:
allowUpdates: true
artifacts: "*artifacts/*"
body: ${{ needs.setup_release.outputs.release_body }}
discussionCategory: announcements
generateReleaseNotes: ${{ needs.setup_release.outputs.release_generate_release_notes }}
name: ${{ needs.setup_release.outputs.release_tag }}
prerelease: true
tag: ${{ needs.setup_release.outputs.release_tag }}
token: ${{ secrets.GH_BOT_TOKEN }}
- name: Update Docker Hub Description
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} # token is not currently supported
repository: ${{ env.BASE_TAG }}
short-description: ${{ github.event.repository.description }}
readme-filepath: ./DOCKER_README.md

View File

@ -1,214 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# This workflow will analyze all supported languages in the repository using CodeQL Analysis.
name: "CodeQL"
on:
push:
branches: ["master"]
pull_request:
branches: ["master"]
schedule:
- cron: '00 12 * * 0' # every Sunday at 12:00 UTC
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
languages:
name: Get language matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.lang.outputs.result }}
continue: ${{ steps.continue.outputs.result }}
steps:
- name: Get repo languages
uses: actions/github-script@v7
id: lang
with:
script: |
// CodeQL supports ['cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift']
// Use only 'java' to analyze code written in Java, Kotlin or both
// Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
// Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
const supported_languages = ['cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift']
const remap_languages = {
'c++': 'cpp',
'c#': 'csharp',
'kotlin': 'java',
'typescript': 'javascript',
}
const repo = context.repo
const response = await github.rest.repos.listLanguages(repo)
let matrix = {
"include": []
}
for (let [key, value] of Object.entries(response.data)) {
// remap language
if (remap_languages[key.toLowerCase()]) {
console.log(`Remapping language: ${key} to ${remap_languages[key.toLowerCase()]}`)
key = remap_languages[key.toLowerCase()]
}
if (supported_languages.includes(key.toLowerCase())) {
console.log(`Found supported language: ${key}`)
let osList = ['ubuntu-latest'];
if (key.toLowerCase() === 'swift') {
osList = ['macos-latest'];
} else if (key.toLowerCase() === 'cpp') {
// TODO: update macos to latest after the below issue is resolved
// https://github.com/github/codeql-action/issues/2266
osList = ['macos-13', '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})
}
}
}
// print languages
console.log(`matrix: ${JSON.stringify(matrix)}`)
return matrix
- name: Continue
uses: actions/github-script@v7
id: continue
with:
script: |
// if matrix['include'] is an empty list return false, otherwise true
const matrix = ${{ steps.lang.outputs.result }} // this is already json encoded
if (matrix['include'].length == 0) {
return false
} else {
return true
}
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.os || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.languages.outputs.matrix) }}
steps:
- name: Maximize build space
if: >-
runner.os == 'Linux' &&
matrix.language == 'cpp'
uses: easimon/maximize-build-space@v10
with:
root-reserve-mb: 30720
remove-dotnet: ${{ (matrix.language == 'csharp' && 'false') || 'true' }}
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'false'
remove-docker-images: 'true'
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup msys2
if: >-
runner.os == 'Windows' &&
matrix.language == 'cpp'
uses: msys2/setup-msys2@v2
with:
msystem: ucrt64
update: true
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# yamllint disable-line rule:line-length
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
config: |
paths-ignore:
- build
- node_modules
- third-party
# 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 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
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
output: sarif-results
upload: failure-only
- name: filter-sarif
uses: advanced-security/filter-sarif@v1
with:
input: sarif-results/${{ matrix.language }}.sarif
output: sarif-results/${{ matrix.language }}.sarif
patterns: |
-build/**
-node_modules/**
-third\-party/**
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sarif-results/${{ matrix.language }}.sarif
- name: Upload loc as a Build Artifact
uses: actions/upload-artifact@v4
with:
name: sarif-results-${{ matrix.language }}-${{ runner.os }}
path: sarif-results
retention-days: 1

View File

@ -1,120 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Lint c++ source files and cmake files.
name: C++ Lint
on:
pull_request:
branches: [master]
types: [opened, synchronize, reopened]
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
clang-format:
name: Clang Format Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Find cpp files
id: find_files
run: |
# find files
found_files=$(find . -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.m" -o -iname "*.mm")
ignore_files=$(find . -type f -iname ".clang-format-ignore")
# Loop through each C++ file
for file in $found_files; do
for ignore_file in $ignore_files; do
ignore_directory=$(dirname "$ignore_file")
# if directory of ignore_file is beginning of file
if [[ "$file" == "$ignore_directory"* ]]; then
echo "ignoring file: ${file}"
found_files="${found_files//${file}/}"
break 1
fi
done
done
# remove empty lines
found_files=$(echo "$found_files" | sed '/^\s*$/d')
echo "found cpp files: ${found_files}"
# do not quote to keep this as a single line
echo found_files=${found_files} >> $GITHUB_OUTPUT
- name: Clang format lint
if: ${{ steps.find_files.outputs.found_files }}
uses: DoozyX/clang-format-lint-action@v0.18
with:
source: ${{ steps.find_files.outputs.found_files }}
extensions: 'cpp,h,m,mm'
clangFormatVersion: 16
style: file
inplace: false
- name: Upload Artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: clang-format-fixes
path: ${{ steps.find_files.outputs.found_files }}
cmake-lint:
name: CMake Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools cmakelang
- name: Find cmake files
id: find_files
run: |
# find files
found_files=$(find . -type f -iname "CMakeLists.txt" -o -iname "*.cmake")
ignore_files=$(find . -type f -iname ".cmake-lint-ignore")
# Loop through each C++ file
for file in $found_files; do
for ignore_file in $ignore_files; do
ignore_directory=$(dirname "$ignore_file")
# if directory of ignore_file is beginning of file
if [[ "$file" == "$ignore_directory"* ]]; then
echo "ignoring file: ${file}"
found_files="${found_files//${file}/}"
break 1
fi
done
done
# remove empty lines
found_files=$(echo "$found_files" | sed '/^\s*$/d')
echo "found cmake files: ${found_files}"
# do not quote to keep this as a single line
echo found_files=${found_files} >> $GITHUB_OUTPUT
- name: Test with cmake-lint
run: |
cmake-lint --line-width 120 --tab-size 4 ${{ steps.find_files.outputs.found_files }}

View File

@ -1,25 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Label and un-label actions using `../label-actions.yml`.
name: Issues
on:
issues:
types: [labeled, unlabeled]
discussion:
types: [labeled, unlabeled]
jobs:
label:
name: Label Actions
if: startsWith(github.repository, 'LizardByte/')
runs-on: ubuntu-latest
steps:
- name: Label Actions
uses: dessant/label-actions@v4
with:
github-token: ${{ secrets.GH_BOT_TOKEN }}

View File

@ -1,98 +0,0 @@
---
name: localize
on:
push:
branches: [master]
paths: # prevents workflow from running unless these files change
- '.github/workflows/localize.yml'
- 'src/**'
- 'locale/sunshine.po'
workflow_dispatch:
env:
file: ./locale/sunshine.po
jobs:
localize:
name: Update Localization
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Python 3.9
uses: actions/setup-python@v5 # https://github.com/actions/setup-python
with:
python-version: '3.9'
- name: Set up Python 3.9 Dependencies
run: |
cd ./scripts
python -m pip install --upgrade pip setuptools
python -m pip install -r requirements.txt
- name: Set up xgettext
run: |
sudo apt-get update -y && \
sudo apt-get --reinstall install -y \
gettext
- name: Update Strings
run: |
# first, try to remove existing file as xgettext does not remove unused translations
if [ -f "${{ env.file }}" ];
then
rm ${{ env.file }}
echo "new_file=false" >> $GITHUB_ENV
else
echo "new_file=true" >> $GITHUB_ENV
fi
# extract the new strings
python ./scripts/_locale.py --extract
- name: git diff
if: ${{ env.new_file == 'false' }}
run: |
# disable the pager
git config --global pager.diff false
# print the git diff
git diff locale/sunshine.po
# 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
- name: git reset
# only run if a single line changed (date/time) and file already existed
if: ${{ env.git_diff == '1 1 locale/sunshine.po' && env.new_file == 'false' }}
run: |
git reset --hard
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Create/Update Pull Request
uses: peter-evans/create-pull-request@v6
with:
add-paths: |
locale/*.po
token: ${{ secrets.GH_BOT_TOKEN }} # must trigger PR tests
commit-message: New localization template
branch: localize/update
delete-branch: true
base: master
title: New Babel Updates
body: |
Update report
- Updated ${{ steps.date.outputs.date }}
- Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
labels: |
babel
l10n

View File

@ -1,62 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Lint python files with flake8.
name: flake8
on:
pull_request:
branches: [master]
types: [opened, synchronize, reopened]
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
flake8:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5 # https://github.com/actions/setup-python
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade \
pip \
setuptools \
wheel \
flake8 \
nb-clean \
nbqa[toolchain]
- name: Test with flake8
run: |
python -m flake8 \
--color=always \
--verbose
- name: Test with nbqa
run: |
python -m nbqa flake8 \
--color=always \
--verbose \
.
- name: Test with nb-clean
run: |
output=$(find . -name '*.ipynb' -exec nb-clean check {} \;)
# fail if there are any issues
if [ -n "$output" ]; then
echo "$output"
exit 1
fi

View File

@ -1,27 +0,0 @@
---
name: Release Notifications (Moonlight)
on:
release:
types:
- released # this triggers when a release is published, but does not include prereleases or drafts
jobs:
discord:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
runs-on: ubuntu-latest
steps:
- name: discord
uses: sarisia/actions-status-discord@v1 # https://github.com/sarisia/actions-status-discord
with:
webhook: ${{ secrets.DISCORD_RELEASE_WEBHOOK_MOONLIGHT }}
nodetail: true
nofail: false
username: ${{ secrets.DISCORD_USERNAME }}
avatar_url: ${{ secrets.ORG_LOGO_URL }}
title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released
description: ${{ github.event.release.body }}
color: 0xFF4500

View File

@ -1,127 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Send release notification to various platforms.
name: Release Notifications
on:
release:
types:
- released # this triggers when a release is published, but does not include pre-releases or drafts
jobs:
simplified_changelog:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
outputs:
SIMPLIFIED_BODY: ${{ steps.output.outputs.SIMPLIFIED_BODY }}
runs-on: ubuntu-latest
steps:
- name: remove contributors section
env:
RELEASE_BODY: ${{ github.event.release.body }}
id: output
run: |
echo "${RELEASE_BODY}" > ./release_body.md
modified_body=$(sed '/^---$/d; /^## Contributors$/,/<\/a>/d' ./release_body.md)
echo "modified_body: ${modified_body}"
# use a heredoc to ensure the output is multiline
echo "SIMPLIFIED_BODY<<EOF" >> $GITHUB_OUTPUT
echo "${modified_body}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
discord:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
needs: simplified_changelog
runs-on: ubuntu-latest
steps:
- name: discord
uses: sarisia/actions-status-discord@v1
with:
avatar_url: ${{ secrets.ORG_LOGO_URL }}
color: 0x00ff00
description: ${{ needs.simplified_changelog.outputs.SIMPLIFIED_BODY }}
nodetail: true
nofail: false
title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released
url: ${{ github.event.release.html_url }}
username: ${{ secrets.DISCORD_USERNAME }}
webhook: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
facebook_group:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
runs-on: ubuntu-latest
steps:
- name: facebook-post-action
uses: ReenigneArcher/facebook-post-action@v1
with:
page_id: ${{ secrets.FACEBOOK_GROUP_ID }}
access_token: ${{ secrets.FACEBOOK_ACCESS_TOKEN }}
message: |
${{ github.event.repository.name }} ${{ github.ref_name }} Released
url: ${{ github.event.release.html_url }}
facebook_page:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
runs-on: ubuntu-latest
steps:
- name: facebook-post-action
uses: ReenigneArcher/facebook-post-action@v1
with:
page_id: ${{ secrets.FACEBOOK_PAGE_ID }}
access_token: ${{ secrets.FACEBOOK_ACCESS_TOKEN }}
message: |
${{ github.event.repository.name }} ${{ github.ref_name }} Released
url: ${{ github.event.release.html_url }}
reddit:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
needs: simplified_changelog
runs-on: ubuntu-latest
steps:
- name: reddit
uses: bluwy/release-for-reddit-action@v2
with:
username: ${{ secrets.REDDIT_USERNAME }}
password: ${{ secrets.REDDIT_PASSWORD }}
app-id: ${{ secrets.REDDIT_CLIENT_ID }}
app-secret: ${{ secrets.REDDIT_CLIENT_SECRET }}
subreddit: ${{ secrets.REDDIT_SUBREDDIT }}
title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released
url: ${{ github.event.release.html_url }}
flair-id: ${{ secrets.REDDIT_FLAIR_ID }} # https://www.reddit.com/r/<subreddit>>/api/link_flair.json
comment: ${{ needs.simplified_changelog.outputs.SIMPLIFIED_BODY }}
x:
if: >-
startsWith(github.repository, 'LizardByte/') &&
!github.event.release.prerelease &&
!github.event.release.draft
runs-on: ubuntu-latest
steps:
- name: x
uses: nearform-actions/github-action-notify-twitter@v1
with:
message: ${{ github.event.release.html_url }}
twitter-app-key: ${{ secrets.X_APP_KEY }}
twitter-app-secret: ${{ secrets.X_APP_SECRET }}
twitter-access-token: ${{ secrets.X_ACCESS_TOKEN }}
twitter-access-token-secret: ${{ secrets.X_ACCESS_TOKEN_SECRET }}

View File

@ -1,31 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Update changelog on release events.
name: Update changelog
on:
release:
types: [created, edited, deleted]
workflow_dispatch:
concurrency:
group: "${{ github.workflow }}"
cancel-in-progress: true
jobs:
update-changelog:
if: >-
github.event_name == 'workflow_dispatch' ||
(!github.event.release.prerelease && !github.event.release.draft)
runs-on: ubuntu-latest
steps:
- name: Update Changelog
uses: LizardByte/update-changelog-action@v2024.609.4705
with:
changelogBranch: changelog
changelogFile: CHANGELOG.md
token: ${{ secrets.GH_BOT_TOKEN }}

View File

@ -1,89 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Use the `rtd` repository label to identify repositories that should trigger have this workflow.
# If the project slug is not the repository name, add a repository variable named `READTHEDOCS_SLUG` with the value of
# the ReadTheDocs project slug.
# Update readthedocs on release events.
name: Update docs
on:
release:
types: [created, edited, deleted]
concurrency:
group: "${{ github.workflow }}-${{ github.event.release.tag_name }}"
cancel-in-progress: true
jobs:
update-docs:
env:
RTD_SLUG: ${{ vars.READTHEDOCS_SLUG }}
RTD_TOKEN: ${{ secrets.READTHEDOCS_TOKEN }}
TAG: ${{ github.event.release.tag_name }}
if: >-
!github.event.release.draft
runs-on: ubuntu-latest
steps:
- name: Get RTD_SLUG
run: |
# if the RTD_SLUG is not set, use the repository name in lowercase
if [ -z "${RTD_SLUG}" ]; then
RTD_SLUG=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]')
fi
echo "RTD_SLUG=${RTD_SLUG}" >> $GITHUB_ENV
- name: Deactivate deleted release
if: >-
github.event_name == 'release' &&
github.event.action == 'deleted'
run: |
json_body=$(jq -n \
--arg active "false" \
--arg hidden "false" \
--arg privacy_level "public" \
'{active: $active, hidden: $hidden, privacy_level: $privacy_level}')
curl \
-X PATCH \
-H "Authorization: Token ${RTD_TOKEN}" \
https://readthedocs.org/api/v3/projects/${RTD_SLUG}/versions/${TAG}/ \
-H "Content-Type: application/json" \
-d "$json_body"
- name: Check if edited release is latest GitHub release
id: check
if: >-
github.event_name == 'release' &&
github.event.action == 'edited'
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo
});
core.setOutput('isLatestRelease', latestRelease.data.tag_name === context.payload.release.tag_name);
- name: Update RTD project
# changing the default branch in readthedocs makes "latest" point to that branch/tag
# we can also update other properties like description, etc.
if: >-
steps.check.outputs.isLatestRelease == 'true'
run: |
json_body=$(jq -n \
--arg default_branch "${TAG}" \
--arg description "${{ github.event.repository.description }}" \
'{default_branch: $default_branch}')
curl \
-X PATCH \
-H "Authorization: Token ${RTD_TOKEN}" \
https://readthedocs.org/api/v3/projects/${RTD_SLUG}/ \
-H "Content-Type: application/json" \
-d "$json_body"

View File

@ -1,71 +0,0 @@
---
# This action is a candidate to centrally manage in https://github.com/<organization>/.github/
# If more Homebrew applications are developed, consider moving this action to the organization's .github repository,
# using the `homebrew-pkg` repository label to identify repositories that should trigger this workflow.
# Update Homebrew on release events.
name: Update Homebrew release
on:
release:
types: [created, edited]
concurrency:
group: "${{ github.workflow }}-${{ github.event.release.tag_name }}"
cancel-in-progress: true
jobs:
update-homebrew-release:
if: >-
github.repository_owner == 'LizardByte' &&
!github.event.release.draft && !github.event.release.prerelease
runs-on: ubuntu-latest
steps:
- name: Check if Homebrew repo
env:
TOPIC: homebrew-pkg
id: check
uses: actions/github-script@v7
with:
script: |
const topic = process.env.TOPIC;
console.log(`Checking if repo has topic: ${topic}`);
const repoTopics = await github.rest.repos.getAllTopics({
owner: context.repo.owner,
repo: context.repo.repo
});
console.log(`Repo topics: ${repoTopics.data.names}`);
const hasTopic = repoTopics.data.names.includes(topic);
console.log(`Has topic: ${hasTopic}`);
core.setOutput('hasTopic', hasTopic);
- name: Download release asset
id: download
if: >-
steps.check.outputs.hasTopic == 'true'
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"
fileName: "*.rb"
tarBall: false
zipBall: false
out-file-path: "release_downloads"
extract: false
- name: Publish Homebrew Formula
if: >-
steps.check.outputs.hasTopic == 'true' &&
fromJson(steps.download.outputs.downloaded_files)[0]
uses: LizardByte/homebrew-release-action@v2024.809.31635
with:
formula_file: ${{ fromJson(steps.download.outputs.downloaded_files)[0] }}
git_email: ${{ secrets.GH_BOT_EMAIL }}
git_username: ${{ secrets.GH_BOT_NAME }}
publish: true
token: ${{ secrets.GH_BOT_TOKEN }}
validate: false

View File

@ -1,110 +0,0 @@
---
# This action is a candidate to centrally manage in https://github.com/<organization>/.github/
# If more pacman packages are developed, consider moving this action to the organization's .github repository,
# using the `pacman-pkg` repository label to identify repositories that should trigger have this workflow.
# Update pacman repo on release events.
name: Update pacman repo
on:
release:
types: [created, edited]
concurrency:
group: "${{ github.workflow }}-${{ github.event.release.tag_name }}"
cancel-in-progress: true
jobs:
update-homebrew-release:
if: >-
github.repository_owner == 'LizardByte' &&
!github.event.release.draft && !github.event.release.prerelease
runs-on: ubuntu-latest
steps:
- name: Check if pacman repo
env:
TOPIC: pacman-pkg
id: check
uses: actions/github-script@v7
with:
script: |
const topic = process.env.TOPIC;
console.log(`Checking if repo has topic: ${topic}`);
const repoTopics = await github.rest.repos.getAllTopics({
owner: context.repo.owner,
repo: context.repo.repo
});
console.log(`Repo topics: ${repoTopics.data.names}`);
const hasTopic = repoTopics.data.names.includes(topic);
console.log(`Has topic: ${hasTopic}`);
core.setOutput('hasTopic', hasTopic);
- name: Check if edited release is latest GitHub release
id: check
if: >-
github.event_name == 'release' &&
github.event.action == 'edited'
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo
});
core.setOutput('isLatestRelease', latestRelease.data.tag_name === context.payload.release.tag_name);
- name: Checkout pacman-repo
if: >-
steps.check.outputs.hasTopic == 'true' &&
steps.check.outputs.isLatestRelease == 'true'
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/pacman-repo
- name: Prep
id: prep
if: >-
steps.check.outputs.hasTopic == 'true' &&
steps.check.outputs.isLatestRelease == 'true'
run: |
echo "pkg_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
- name: Download release asset
id: download
if: >-
steps.check.outputs.hasTopic == 'true' &&
steps.check.outputs.isLatestRelease == 'true'
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"
fileName: "*.pkg.tar.gz"
tarBall: false
zipBall: false
out-file-path: "pkgbuilds/${{ steps.prep.outputs.pkg_name }}"
extract: true
- name: Create/Update Pull Request
if: >-
steps.check.outputs.hasTopic == 'true'&&
steps.check.outputs.isLatestRelease == 'true' &&
fromJson(steps.download.outputs.downloaded_files)[0]
uses: peter-evans/create-pull-request@v6
with:
add-paths: |
pkgbuilds/*
token: ${{ secrets.GH_BOT_TOKEN }}
commit-message: Update ${{ github.repository }} to ${{ github.event.release.tag_name }}
branch: bot/bump-${{ github.repository }}-${{ github.event.release.tag_name }}
delete-branch: true
base: master
title: Update ${{ github.repository }} to ${{ github.event.release.tag_name }}
body: ${{ github.event.release.body }}
labels: |
auto-approve
auto-merge

View File

@ -1,62 +0,0 @@
---
name: Build GH-Pages
on:
pull_request:
branches: [master]
types: [opened, synchronize, reopened]
push:
branches: [master]
workflow_dispatch:
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
update_pages:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Checkout gh-pages
uses: actions/checkout@v4
with:
ref: gh-pages
path: gh-pages
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of the personal token
fetch-depth: 0 # otherwise, will fail to push refs to dest repo
- name: Prepare gh-pages
run: |
# empty contents
rm -f -r ./gh-pages/*
# copy template back to pages
cp -f -r ./gh-pages-template/. ./gh-pages/
- name: Upload Artifacts
if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }}
uses: actions/upload-artifact@v4
with:
name: gh-pages
if-no-files-found: error # 'warn' or 'ignore' are also available, defaults to `warn`
path: |
${{ github.workspace }}/gh-pages
!**/*.git
- name: Deploy to gh-pages
if: >-
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_dispatch')
uses: actions-js/push@v1.5
with:
github_token: ${{ secrets.GH_BOT_TOKEN }}
author_email: ${{ secrets.GH_BOT_EMAIL }}
author_name: ${{ secrets.GH_BOT_NAME }}
directory: gh-pages
branch: gh-pages
force: false
message: sync gh-pages to ${{ github.sha }}

View File

@ -1,69 +0,0 @@
---
# This action is a candidate to centrally manage in https://github.com/<organization>/.github/
# If more Winget applications are developed, consider moving this action to the organization's .github repository,
# using the `winget-pkg` repository label to identify repositories that should trigger this workflow.
# Update Winget on release events.
name: Update Winget release
on:
release:
types: [created, edited]
concurrency:
group: "${{ github.workflow }}-${{ github.event.release.tag_name }}"
cancel-in-progress: true
jobs:
update-winget-release:
if: >-
github.repository_owner == 'LizardByte' &&
!github.event.release.draft && !github.event.release.prerelease
runs-on: ubuntu-latest
steps:
- name: Check if Winget repo
env:
TOPIC: winget-pkg
id: check
uses: actions/github-script@v7
with:
script: |
const topic = process.env.TOPIC;
console.log(`Checking if repo has topic: ${topic}`);
const repoTopics = await github.rest.repos.getAllTopics({
owner: context.repo.owner,
repo: context.repo.repo
});
console.log(`Repo topics: ${repoTopics.data.names}`);
const hasTopic = repoTopics.data.names.includes(topic);
console.log(`Has topic: ${hasTopic}`);
core.setOutput('hasTopic', hasTopic);
- name: Download release asset
id: download
if: >-
steps.check.outputs.hasTopic == 'true'
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"
fileName: "*.exe"
tarBall: false
zipBall: false
out-file-path: "release_downloads"
extract: false
- name: Release to WinGet
if: >-
steps.check.outputs.hasTopic == 'true' &&
fromJson(steps.download.outputs.downloaded_files)[0]
uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: "${{ github.repository_owner }}.${{ github.event.repository.name }}"
release-tag: ${{ github.event.release.tag_name }}
installers-regex: '\.exe$'
token: ${{ secrets.GH_BOT_TOKEN }}

View File

@ -1,66 +0,0 @@
---
# This action is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.
# Lint yaml files.
name: yaml lint
on:
pull_request:
branches: [master]
types: [opened, synchronize, reopened]
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
yaml-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Find additional files
id: find-files
run: |
# space separated list of files
FILES=.clang-format
# empty placeholder
FOUND=""
for FILE in ${FILES}; do
if [ -f "$FILE" ]
then
FOUND="$FOUND $FILE"
fi
done
echo "found=${FOUND}" >> $GITHUB_OUTPUT
- name: yaml lint
id: yaml-lint
uses: ibiqlik/action-yamllint@v3
with:
# https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration
config_data: |
extends: default
rules:
comments:
level: error
line-length:
max: 120
truthy:
# GitHub uses "on" for workflow event triggers
# .clang-format file has options of "Yes" "No" that will be caught by this, so changed to "warning"
allowed-values: ['true', 'false', 'on']
check-keys: true
level: warning
file_or_dir: . ${{ steps.find-files.outputs.found }}
- name: Log
run: |
cat "${{ steps.yaml-lint.outputs.logfile }}" >> $GITHUB_STEP_SUMMARY

32
.gitmodules vendored
View File

@ -1,31 +1,11 @@
[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/doxyconfig"]
path = third-party/doxyconfig
url = https://github.com/LizardByte/doxyconfig.git
branch = master
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest/
branch = v1.14.x
[submodule "third-party/inputtino"]
path = third-party/inputtino
url = https://github.com/games-on-whales/inputtino.git
branch = stable
[submodule "third-party/moonlight-common-c"]
path = third-party/moonlight-common-c
url = https://github.com/moonlight-stream/moonlight-common-c.git
branch = master
[submodule "third-party/nanors"]
path = third-party/nanors
url = https://github.com/sleepybishop/nanors.git
branch = master
[submodule "third-party/nv-codec-headers"]
path = third-party/nv-codec-headers
url = https://github.com/FFmpeg/nv-codec-headers
@ -34,22 +14,10 @@
path = third-party/nvapi-open-source-sdk
url = https://github.com/LizardByte/nvapi-open-source-sdk
branch = sdk
[submodule "third-party/Simple-Web-Server"]
path = third-party/Simple-Web-Server
url = https://gitlab.com/eidheim/Simple-Web-Server.git
branch = master
[submodule "third-party/TPCircularBuffer"]
path = third-party/TPCircularBuffer
url = https://github.com/michaeltyson/TPCircularBuffer
branch = master
[submodule "third-party/tray"]
path = third-party/tray
url = https://github.com/LizardByte/tray
branch = master
[submodule "third-party/ViGEmClient"]
path = third-party/ViGEmClient
url = https://github.com/LizardByte/Virtual-Gamepad-Emulation-Client.git
branch = master
[submodule "third-party/wayland-protocols"]
path = third-party/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git

View File

@ -1,59 +0,0 @@
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
# project metadata
DOCSET_BUNDLE_ID = dev.lizardbyte.Sunshine
DOCSET_PUBLISHER_ID = dev.lizardbyte.Sunshine.documentation
PROJECT_BRIEF = "Self-hosted game stream host for Moonlight."
PROJECT_ICON = ../sunshine.ico
PROJECT_LOGO = ../sunshine.png
PROJECT_NAME = Sunshine
# project specific settings
DOT_GRAPH_MAX_NODES = 60
IMAGE_PATH = ../docs/images
INCLUDE_PATH = ../third-party/build-deps/ffmpeg/Linux-x86_64/include/
PREDEFINED += SUNSHINE_BUILD_WAYLAND
PREDEFINED += SUNSHINE_TRAY=1
# TODO: Enable this when we have complete documentation
WARN_IF_UNDOCUMENTED = NO
# files and directories to process
USE_MDFILE_AS_MAINPAGE = ../README.md
INPUT = ../README.md \
getting_started.md \
changelog.md \
../DOCKER_README.md \
third_party_packages.md \
gamestream_migration.md \
legal.md \
configuration.md \
app_examples.md \
guides.md \
performance_tuning.md \
troubleshooting.md \
building.md \
contributing.md \
../third-party/doxyconfig/docs/source_code.md \
../src

File diff suppressed because it is too large Load Diff

View File

@ -1,330 +0,0 @@
# App Examples
Since not all applications behave the same, we decided to create some examples to help you get started adding games
and applications to Sunshine.
@attention{Throughout these examples, any fields not shown are left blank. You can enhance your experience by
adding an image or a log file (via the `Output` field).}
@note{When a working directory is not specified, it defaults to the folder where the target application resides.}
## Common Examples
### Desktop
| Field | Value |
|------------------|----------------------------|
| Application Name | @code{}Desktop@endcode |
| Image | @code{}desktop.png@endcode |
### Steam Big Picture
@note{Steam is launched as a detached command because Steam starts with a process that self updates itself and the original
process is killed.}
@tabs{
@tab{Linux | <!-- -->
\| Field \| Value \|
\|-------------------\|-----------------------------------------------------\|
\| Application Name \| @code{}Steam Big Picture@endcode \|
\| Detached Commands \| @code{}setsid steam steam://open/bigpicture@endcode \|
\| Image \| @code{}steam.png@endcode \|
}
@tab{macOS | <!-- -->
\| Field \| Value \|
\|-------------------\|---------------------------------------------------\|
\| Application Name \| @code{}Steam Big Picture@endcode \|
\| Detached Commands \| @code{}open steam steam://open/bigpicture@endcode \|
\| Image \| @code{}steam.png@endcode \|
}
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|----------------------------------------\|
\| Application Name \| @code{}Steam Big Picture@endcode \|
\| Detached Commands \| @code{}steam://open/bigpicture@endcode \|
\| Image \| @code{}steam.png@endcode \|
}
}
### Epic Game Store game
@note{Using URI method will be the most consistent between various games.}
#### URI
@tabs{
@tab{Windows | <!-- -->
\| Field \| Value \|
\|------------------\|-------------------------------------------------------------------------------------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Commands \| @code{}com.epicgames.launcher://apps/d759128018124dcabb1fbee9bb28e178%3A20729b9176c241f0b617c5723e70ec2d%3AOvenbird?action=launch&silent=true@endcode \|
}
}
#### Binary (w/ working directory
@tabs{
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}MarsEpic.exe@endcode \|
\| Working Directory \| @code{}"C:\Program Files\Epic Games\SurvivingMars"@endcode \|
}
}
#### Binary (w/o working directory)
@tabs{
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|-------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}"C:\Program Files\Epic Games\SurvivingMars\MarsEpic.exe"@endcode \|
}
}
### Steam game
@note{Using URI method will be the most consistent between various games.}
#### URI
@tabs{
@tab{Linux | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Detached Commands \| @code{}setsid steam steam://rungameid/464920@endcode \|
}
@tab{macOS | <!-- -->
\| Field \| Value \|
\|-------------------\|----------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Detached Commands \| @code{}open steam://rungameid/464920@endcode \|
}
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|-----------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Detached Commands \| @code{}steam://rungameid/464920@endcode \|
}
}
#### Binary (w/ working directory
@tabs{
@tab{Linux | <!-- -->
\| Field \| Value \|
\|-------------------\|--------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}MarsSteam@endcode \|
\| Working Directory \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars@endcode \|
}
@tab{macOS | <!-- -->
\| Field \| Value \|
\|-------------------\|--------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}MarsSteam@endcode \|
\| Working Directory \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars@endcode \|
}
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|-------------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}MarsSteam.exe@endcode \|
\| Working Directory \| @code{}"C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars"@endcode \|
}
}
#### Binary (w/o working directory)
@tabs{
@tab{Linux | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars/MarsSteam@endcode \|
}
@tab{macOS | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars/MarsSteam@endcode \|
}
@tab{Windows | <!-- -->
\| Field \| Value \|
\|-------------------\|---------------------------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}"C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars\MarsSteam.exe"@endcode \|
}
}
### Prep Commands
#### Changing Resolution and Refresh Rate
##### Linux
###### X11
| Prep Step | Command |
|-----------|---------------------------------------------------------------------------------------------------------------------------------------|
| Do | @code{}sh -c "xrandr --output HDMI-1 --mode ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --rate ${SUNSHINE_CLIENT_FPS}"@endcode |
| Undo | @code{}xrandr --output HDMI-1 --mode 3840x2160 --rate 120@endcode |
@hint{The above only works if the xrandr mode already exists. You will need to create new modes to stream to macOS
and iOS devices, since they use non standard resolutions.
You can update the ``Do`` command to this:
```bash
bash -c "${HOME}/scripts/set-custom-res.sh \"${SUNSHINE_CLIENT_WIDTH}\" \"${SUNSHINE_CLIENT_HEIGHT}\" \"${SUNSHINE_CLIENT_FPS}\""
```
The `set-custom-res.sh` will have this content:
```bash
#!/bin/bash
set -e
# Get params and set any defaults
width=${1:-1920}
height=${2:-1080}
refresh_rate=${3:-60}
# You may need to adjust the scaling differently so the UI/text isn't too small / big
scale=${4:-0.55}
# Get the name of the active display
display_output=$(xrandr | grep " connected" | awk '{ print $1 }')
# Get the modeline info from the 2nd row in the cvt output
modeline=$(cvt ${width} ${height} ${refresh_rate} | awk 'FNR == 2')
xrandr_mode_str=${modeline//Modeline \"*\" /}
mode_alias="${width}x${height}"
echo "xrandr setting new mode ${mode_alias} ${xrandr_mode_str}"
xrandr --newmode ${mode_alias} ${xrandr_mode_str}
xrandr --addmode ${display_output} ${mode_alias}
# Reset scaling
xrandr --output ${display_output} --scale 1
# Apply new xrandr mode
xrandr --output ${display_output} --primary --mode ${mode_alias} --pos 0x0 --rotate normal --scale ${scale}
# Optional reset your wallpaper to fit to new resolution
# xwallpaper --zoom /path/to/wallpaper.png
```
}
###### Wayland
| Prep Step | Command |
|-----------|------------------------------------------------------------------------------------------------------------------------------------------|
| Do | @code{}sh -c "wlr-xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}@${SUNSHINE_CLIENT_FPS}Hz\""@endcode |
| Undo | @code{}wlr-xrandr --output HDMI-1 --mode 3840x2160@120Hz@endcode |
@hint{`wlr-xrandr` only works with wlroots-based compositors.}
###### Gnome (Wayland, X11)
| Prep Step | Command |
|-----------|---------------------------------------------------------------------------------------------------------------------------------------|
| Do | @code{}sh -c "xrandr --output HDMI-1 --mode ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --rate ${SUNSHINE_CLIENT_FPS}"@endcode |
| Undo | @code{}xrandr --output HDMI-1 --mode 3840x2160 --rate 120@endcode |
The commands above are valid for an X11 session but won't work for
Wayland. In that case `xrandr` must be replaced by [gnome-randr.py](https://gitlab.com/Oschowa/gnome-randr).
This script is intended as a drop-in replacement with the same syntax. (It can be saved in
`/usr/local/bin` and needs to be made executable.)
###### KDE Plasma (Wayland, X11)
| Prep Step | Command |
|-----------|--------------------------------------------------------------------------------------------------------------------------------------|
| Do | @code{}sh -c "kscreen-doctor output.HDMI-A-1.mode.${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}@${SUNSHINE_CLIENT_FPS}"@endcode |
| Undo | @code{}kscreen-doctor output.HDMI-A-1.mode.3840x2160@120@endcode |
###### NVIDIA
| Prep Step | Command |
|-----------|-------------------------------------------------------------------------------------------------------------|
| Do | @code{}sh -c "${HOME}/scripts/set-custom-res.sh ${SUNSHINE_CLIENT_WIDTH} ${SUNSHINE_CLIENT_HEIGHT}"@endcode |
| Undo | @code{}sh -c "${HOME}/scripts/set-custom-res.sh 3840 2160"@endcode |
The ``set-custom-res.sh`` will have this content:
```bash
#!/bin/bash
set -e
# Get params and set any defaults
width=${1:-1920}
height=${2:-1080}
output=${3:-HDMI-1}
nvidia-settings -a CurrentMetaMode="${output}: nvidia-auto-select { ViewPortIn=${width}x${height}, ViewPortOut=${width}x${height}+0+0 }"
```
##### macOS
###### displayplacer
@note{This example uses the `displayplacer` tool to change the resolution.
This tool can be installed following instructions in their
[GitHub repository](https://github.com/jakehilborn/displayplacer)}.
| Prep Step | Command |
|-----------|----------------------------------------------------------------------------------------------------|
| Do | @code{}displayplacer "id:<screenId> res:1920x1080 hz:60 scaling:on origin:(0,0) degree:0"@endcode |
| Undo | @code{}displayplacer "id:<screenId> res:3840x2160 hz:120 scaling:on origin:(0,0) degree:0"@endcode |
##### Windows
###### QRes
@note{This example uses the *QRes* tool to change the resolution and refresh rate.
This tool can be downloaded from their [SourceForge repository](https://sourceforge.net/projects/qres).}.
| Prep Step | Command |
|-----------|-------------------------------------------------------------------------------------------------------------------------|
| Do | @code{}cmd /C FullPath\qres.exe /x:%SUNSHINE_CLIENT_WIDTH% /y:%SUNSHINE_CLIENT_HEIGHT% /r:%SUNSHINE_CLIENT_FPS%@endcode |
| Undo | @code{}cmd /C FullPath\qres.exe /x:3840 /y:2160 /r:120@endcode |
### Additional Considerations
#### Linux (Flatpak)
@attention{Because Flatpak packages run in a sandboxed environment and do not normally have access to the
host, the Flatpak of Sunshine requires commands to be prefixed with `flatpak-spawn --host`.}
#### Windows
**Elevating Commands (Windows)**
If you've installed Sunshine as a service (default), you can specify if a command should be elevated with
administrative privileges. Simply enable the elevated option in the WEB UI, or add it to the JSON configuration.
This is an option for both prep-cmd and regular commands and will launch the process with the current user without a
UAC prompt.
@note{It is important to write the values "true" and "false" as string values, not as the typical true/false
values in most JSON.}
**Example**
```json
{
"name": "Game With AntiCheat that Requires Admin",
"output": "",
"cmd": "ping 127.0.0.1",
"exclude-global-prep-cmd": "false",
"elevated": "true",
"prep-cmd": [
{
"do": "powershell.exe -command \"Start-Streaming\"",
"undo": "powershell.exe -command \"Stop-Streaming\"",
"elevated": "false"
}
],
"image-path": ""
}
```
<div class="section_buttons">
| Previous | Next |
|:----------------------------------|--------------------:|
| [Configuration](configuration.md) | [Guides](guides.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,167 +0,0 @@
# Building
Sunshine binaries are built using [CMake](https://cmake.org) and requires `cmake` > 3.25.
## Building Locally
### Dependencies
#### Linux
Dependencies vary depending on the distribution. You can reference our
[linux_build.sh](https://github.com/LizardByte/Sunshine/blob/master/scripts/linux_build.sh) script for a list of
dependencies we use in Debian-based and Fedora-based distributions. Please submit a PR if you would like to extend the
script to support other distributions.
##### CUDA Toolkit
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.
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
according to [CUDA Toolkit Archive](https://developer.nvidia.com/cuda-toolkit-archive)}
#### macOS
You can either use [Homebrew](https://brew.sh) or [MacPorts](https://www.macports.org) to install dependencies.
##### Homebrew
```bash
dependencies=(
"boost" # Optional
"cmake"
"doxygen" # Optional, for docs
"graphviz" # Optional, for docs
"icu4c" # Optional, if boost is not installed
"miniupnpc"
"node"
"openssl@3"
"opus"
"pkg-config"
)
brew install ${dependencies[@]}
```
If there are issues with an SSL header that is not found:
@tabs{
@tab{ Intel | ```bash
ln -s /usr/local/opt/openssl/include/openssl /usr/local/include/openssl
```}
@tab{ Apple Silicon | ```bash
ln -s /opt/homebrew/opt/openssl/include/openssl /opt/homebrew/include/openssl
```
}
}
##### MacPorts
```bash
dependencies=(
"cmake"
"curl"
"doxygen" # Optional, for docs
"graphviz" # Optional, for docs
"libopus"
"miniupnpc"
"npm9"
"pkgconfig"
)
sudo port install ${dependencies[@]}
```
#### Windows
First you need to install [MSYS2](https://www.msys2.org), then startup "MSYS2 UCRT64" and execute the following
commands.
##### Update all packages
```bash
pacman -Syu
```
##### Install dependencies
```bash
dependencies=(
"doxygen" # Optional, for docs
"git"
"mingw-w64-ucrt-x86_64-boost" # Optional
"mingw-w64-ucrt-x86_64-cmake"
"mingw-w64-ucrt-x86_64-cppwinrt"
"mingw-w64-ucrt-x86_64-curl"
"mingw-w64-ucrt-x86_64-graphviz" # Optional, for docs
"mingw-w64-ucrt-x86_64-miniupnpc"
"mingw-w64-ucrt-x86_64-nlohmann-json"
"mingw-w64-ucrt-x86_64-nodejs"
"mingw-w64-ucrt-x86_64-nsis"
"mingw-w64-ucrt-x86_64-onevpl"
"mingw-w64-ucrt-x86_64-openssl"
"mingw-w64-ucrt-x86_64-opus"
"mingw-w64-ucrt-x86_64-toolchain"
)
pacman -S ${dependencies[@]}
```
### Clone
Ensure [git](https://git-scm.com) is installed on your system, then clone the repository using the following command:
```bash
git clone https://github.com/lizardbyte/sunshine.git --recurse-submodules
cd sunshine
mkdir build
```
### Build
```bash
cmake -B build -G Ninja -S .
ninja -C build
```
@tip{Available build options can be found in
[options.cmake](https://github.com/LizardByte/Sunshine/blob/master/cmake/prep/options.cmake).}
### Package
@tabs{
@tab{Linux | @tabs{
@tab{deb | ```bash
cpack -G DEB --config ./build/CPackConfig.cmake
```}
@tab{rpm | ```bash
cpack -G RPM --config ./build/CPackConfig.cmake
```}
}}
@tab{macOS | @tabs{
@tab{DragNDrop | ```bash
cpack -G DragNDrop --config ./build/CPackConfig.cmake
```}
}}
@tab{Windows | @tabs{
@tab{Installer | ```bash
cpack -G NSIS --config ./build/CPackConfig.cmake
```}
@tab{Portable | ```bash
cpack -G ZIP --config ./build/CPackConfig.cmake
```}
}}
}
### Remote Build
It may be beneficial to build remotely in some cases. This will enable easier building on different operating systems.
1. Fork the project
2. Activate workflows
3. Trigger the *CI* workflow manually
4. Download the artifacts/binaries from the workflow run summary
<div class="section_buttons">
| Previous | Next |
|:--------------------------------------|--------------------------------:|
| [Troubleshooting](troubleshooting.md) | [Contributing](contributing.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,22 +0,0 @@
# Changelog
@htmlonly
<script type="module" src="https://md-block.verou.me/md-block.js"></script>
<md-block
hmin="2"
src="https://raw.githubusercontent.com/LizardByte/Sunshine/changelog/CHANGELOG.md">
</md-block>
@endhtmlonly
<div class="section_buttons">
| Previous | Next |
|:--------------------------------------|------------------------------:|
| [Getting Started](getting_started.md) | [Docker](../DOCKER_README.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

File diff suppressed because it is too large Load Diff

View File

@ -1,200 +0,0 @@
# Contributing
Read our contribution guide in our organization level
[docs](https://lizardbyte.readthedocs.io/en/latest/developers/contributing.html).
## Project Patterns
### Web UI
* The Web UI uses [Vite](https://vitejs.dev) as its build system.
* The HTML pages used by the Web UI are found in `./src_assets/common/assets/web`.
* [EJS](https://www.npmjs.com/package/vite-plugin-ejs) is used as a templating system for the pages
(check `template_header.html` and `template_header_main.html`).
* The Style System is provided by [Bootstrap](https://getbootstrap.com).
* The JS framework used by the more interactive pages is [Vus.js](https://vuejs.org).
#### Building
@tabs{
@tab{CMake | ```bash
cmake -B build -G Ninja -S . --target web-ui
ninja -C build web-ui
```}
@tab{Manual | ```bash
npm run dev
```}
}
### Localization
Sunshine and related LizardByte projects are being localized into various languages.
The default language is `en` (English).
![](https://app.lizardbyte.dev/uno/crowdin/LizardByte_graph.svg)
@admonition{Community | We are looking for language coordinators to help approve translations.
The goal is to have the bars above filled with green!
If you are interesting, please reach out to us on our Discord server.}
#### CrowdIn
The translations occur on [CrowdIn][crowdin-url].
Anyone is free to contribute to the localization there.
##### Translation Basics
* The brand names *LizardByte* and *Sunshine* should never be translated.
* Other brand names should never be translated. Examples include *AMD*, *Intel*, and *NVIDIA*.
##### CrowdIn Integration
How does it work?
When a change is made to Sunshine source code, a workflow generates new translation templates
that get pushed to CrowdIn automatically.
When translations are updated on CrowdIn, a push gets made to the *l10n_master* branch and a PR is made against the
*master* branch. Once the PR is merged, all updated translations are part of the project and will be included in the
next release.
#### Extraction
##### Web UI
Sunshine uses [Vue I18n](https://vue-i18n.intlify.dev) for localizing the UI.
The following is a simple example of how to use it.
* Add the string to the `./src_assets/common/assets/web/public/assets/locale/en.json` file, in English.
```json
{
"index": {
"welcome": "Hello, Sunshine!"
}
}
```
@note{The json keys should be sorted alphabetically. You can use [jsonabc](https://novicelab.org/jsonabc)
to sort the keys.}
@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][crowdin-url]. Once the translations are complete, a PR will be made
to merge the translations into Sunshine.}
* Use the string in the Vue component.
```html
<template>
<div>
<p>{{ $t('index.welcome') }}</p>
</div>
</template>
```
@tip{More formatting examples can be found in the
[Vue I18n guide](https://kazupon.github.io/vue-i18n/guide/formatting.html).}
##### C++
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.
* Wrap the string to be extracted in a function as shown.
```cpp
#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{The below 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 `master` branch. Jobs are only run if
any of the following paths are modified.
```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
```bash
python ./scripts/_locale.py --extract --init --update
```
* Compile
```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][crowdin-url]. Once the translations are
complete, a PR will be made to merge the translations into Sunshine.}
### Testing
#### Clang Format
Source code is tested against the `.clang-format` file for linting errors. The workflow file responsible for clang
format testing is `.github/workflows/cpp-clang-format-lint.yml`.
Option 1:
```bash
find ./ -iname *.cpp -o -iname *.h -iname *.m -iname *.mm | xargs clang-format -i
```
Option 2 (will modify files):
```bash
python ./scripts/update_clang_format.py
```
#### Unit Testing
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.
```bash
./build/tests/test_sunshine
```
To see all available options, run the tests with the `--help` flag.
```bash
./build/tests/test_sunshine --help
```
@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.
[crowdin-url]: https://translate.lizardbyte.dev
<div class="section_buttons">
| Previous | Next |
|:------------------------|-------------------------------------------------------------:|
| [Building](building.md) | [Source Code](../third-party/doxyconfig/docs/source_code.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,36 +0,0 @@
# GameStream Migration
Nvidia announced that their GameStream service for Nvidia Games clients will be discontinued in February 2023.
Luckily, Sunshine performance is now equal to or better than Nvidia GameStream.
## Migration
We have developed a simple migration tool to help you migrate your GameStream games and apps to Sunshine automatically.
Please check out our [GSMS](https://github.com/LizardByte/GSMS) project if you're interested in an automated
migration option. GSMS offers the ability to migrate your custom and auto-detected games and apps. The
working directory, command, and image are all set in Sunshine's `apps.json` file. The box-art image is also copied
to a specified directory.
## Internet Streaming
If you are using the Moonlight Internet Hosting Tool, you can remove it from your system when you migrate to Sunshine.
To stream over the Internet with Sunshine and a UPnP-capable router, enable the UPnP option in the Sunshine Web UI.
@note{Running Sunshine together with versions of the Moonlight Internet Hosting Tool prior to v5.6 will cause UPnP
port forwarding to become unreliable. Either uninstall the tool entirely or update it to v5.6 or later.}
## Limitations
Sunshine does have some limitations, as compared to Nvidia GameStream.
* Automatic game/application list.
* Changing game settings automatically, to optimize streaming.
<div class="section_buttons">
| Previous | Next |
|:------------------------------------------------|------------------:|
| [Third-party Packages](third_party_packages.md) | [Legal](legal.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,578 +0,0 @@
# Getting Started
The recommended method for running Sunshine is to use the [binaries](#binaries) included in the
[latest release][latest-release], unless otherwise specified.
[Pre-releases](https://github.com/LizardByte/Sunshine/releases) are also available. These should be considered beta,
and release artifacts may be missing when merging changes on a faster cadence.
## Binaries
Binaries of Sunshine are created for each release. They are available for Linux, macOS, and Windows.
Binaries can be found in the [latest release][latest-release].
@tip{Some third party packages also exist.
See [Third Party Packages](third_party_packages.md) for more information.
No support will be provided for third party packages!}
## Install
### Docker
@warning{The Docker images are not recommended for most users.}
Docker images are available on [Dockerhub.io](https://hub.docker.com/repository/docker/lizardbyte/sunshin)
and [ghcr.io](https://github.com/orgs/LizardByte/packages?repo_name=sunshine).
See [Docker](../DOCKER_README.md) for more information.
### Linux
**CUDA Compatibility**
CUDA is used for NVFBC capture.
@tip{See [CUDA GPUS](https://developer.nvidia.com/cuda-gpus) to cross-reference Compute Capability to your GPU.}
<table>
<caption>CUDA Compatibility</caption>
<tr>
<th>CUDA Version</th>
<th>Min Driver</th>
<th>CUDA Compute Capabilities</th>
<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>sunshine.AppImage</td>
</tr>
<tr>
<td>sunshine-ubuntu-22.04-{arch}.deb</td>
</tr>
<tr>
<td>sunshine-ubuntu-24.04-{arch}.deb</td>
</tr>
<tr>
<td rowspan="2">12.0.0</td>
<td rowspan="4">525.60.13</td>
<td rowspan="4">50;52;60;61;62;70;72;75;80;86;87;89;90</td>
<td>sunshine_{arch}.flatpak</td>
</tr>
<tr>
<td>sunshine-debian-bookworm-{arch}.deb</td>
</tr>
<tr>
<td>12.4.0</td>
<td>sunshine-fedora-39-{arch}.rpm</td>
</tr>
<tr>
<td>12.5.1</td>
<td>sunshine.pkg.tar.zst</td>
</tr>
<tr>
<td>n/a</td>
<td>n/a</td>
<td>n/a</td>
<td>sunshine-fedora-40-{arch}.rpm</td>
</tr>
</table>
#### AppImage
@caution{Use distro-specific packages instead of the AppImage if they are available.}
According to AppImageLint the supported distro matrix of the AppImage is below.
- ✖ Debian bullseye
- ✔ Debian bookworm
- ✔ Debian trixie
- ✔ Debian sid
- ✔ Ubuntu noble
- ✔ Ubuntu jammy
- ✖ Ubuntu focal
- ✖ Ubuntu bionic
- ✖ Ubuntu xenial
- ✖ Ubuntu trusty
- ✖ CentOS 7
##### Install
1. Download [sunshine.AppImage](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage)
into your home directory.
```bash
cd ~
wget https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage
```
2. Open terminal and run the following command.
```bash
./sunshine.AppImage --install
```
##### Run
```bash
./sunshine.AppImage --install && ./sunshine.AppImage
```
##### Uninstall
```bash
./sunshine.AppImage --remove
```
#### ArchLinux
@warning{We do not provide support for any AUR packages.}
##### Install Prebuilt Packages
Follow the instructions at LizardByte's [pacman-repo](https://github.com/LizardByte/pacman-repo) to add
the repository. Then run the following command.
```bash
pacman -S sunshine
```
##### Install PKGBUILD Archive
Open terminal and run the following command.
```bash
wget https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.pkg.tar.gz
tar -xvf sunshine.pkg.tar.gz
cd sunshine
# install optional dependencies
pacman -S cuda # Nvidia GPU encoding support
pacman -S libva-mesa-driver # AMD GPU encoding support
makepkg -si
```
##### Uninstall
```bash
pacman -R sunshine
```
#### Debian/Ubuntu
##### Install
Download `sunshine-{distro}-{distro-version}-{arch}.deb` and run the following command.
```bash
sudo dpkg -i ./sunshine-{distro}-{distro-version}-{arch}.deb
```
@note{The `{distro-version}` is the version of the distro we built the package on. The `{arch}` is the
architecture of your operating system.}
@tip{You can double-click the deb file to see details about the package and begin installation.}
##### Uninstall
```bash
sudo apt remove sunshine
```
#### Fedora
##### Install
1. Add `rpmfusion` repositories.
```bash
sudo dnf install \
https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
```
2. Download `sunshine-{distro}-{distro-version}-{arch}.rpm` and run the following command.
```bash
sudo dnf install ./sunshine-{distro}-{distro-version}-{arch}.rpm
```
@note{The `{distro-version}` is the version of the distro we built the package on. The `{arch}` is the
architecture of your operating system.}
@tip{You can double-click the rpm file to see details about the package and begin installation.}
##### Uninstall
```bash
sudo dnf remove sunshine
```
#### Flatpak
@caution{Use distro-specific packages instead of the Flatpak if they are available.}
@important{The instructions provided here are for the version supplied in the [latest release][latest-release],
which does not necessarily match the version in the Flathub repository!}
Using this package requires that you have [Flatpak](https://flatpak.org/setup) installed.
##### Download
1. Download `sunshine_{arch}.flatpak` and run the following command.
@note{Replace `{arch}` with your system architecture.}
##### Install (system level)
```bash
flatpak install --system ./sunshine_{arch}.flatpak
```
##### Install (user level)
```bash
flatpak install --user ./sunshine_{arch}.flatpak
```
##### Additional installation (required)
```bash
flatpak run --command=additional-install.sh dev.lizardbyte.app.Sunshine
```
##### Run with NVFBC capture (X11 Only)
```bash
flatpak run dev.lizardbyte.app.Sunshine
```
##### Run with KMS capture (Wayland & X11)
```bash
sudo -i PULSE_SERVER=unix:$(pactl info | awk '/Server String/{print$3}') flatpak run dev.lizardbyte.app.Sunshine
```
##### Uninstall
```bash
flatpak run --command=remove-additional-install.sh dev.lizardbyte.app.Sunshine
flatpak uninstall --delete-data dev.lizardbyte.app.Sunshine
```
#### Homebrew
@important{The Homebrew package is experimental on Linux.}
This package requires that you have [Homebrew](https://docs.brew.sh/Installation) installed.
##### Install
```bash
brew tap LizardByte/homebrew
brew install sunshine
```
##### Uninstall
```bash
brew uninstall sunshine
```
### macOS
@important{Sunshine on macOS is experimental. Gamepads do not work.}
#### Homebrew
This package requires that you have [Homebrew](https://docs.brew.sh/Installation) installed.
##### Install
```bash
brew tap LizardByte/homebrew
brew install sunshine
```
##### Uninstall
```bash
brew uninstall sunshine
```
@tip{For beta you can replace `sunshine` with `sunshine-beta` in the above commands.}
#### Portfile
This package requires that you have [MacPorts](https://www.macports.org/install.php) installed.
##### Install
1. Update the Macports sources.
```bash
sudo nano /opt/local/etc/macports/sources.conf
```
Add this line, replacing your username, below the line that starts with `rsync`.
```bash
file:///Users/<username>/ports
```
`Ctrl+x`, then `Y` to exit and save changes.
2. Download and install by running the following commands.
```bash
mkdir -p ~/ports/multimedia/sunshine
cd ~/ports/multimedia/sunshine
curl -OL https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile
cd ~/ports
portindex
sudo port install sunshine
```
##### Install service (optional)
```bash
sudo port load sunshine
```
##### Uninstall
```bash
sudo port uninstall sunshine
```
### Windows
#### Installer (recommended)
1. Download and install
[sunshine-windows-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine-windows-installer.exe)
@attention{You should carefully select or unselect the options you want to install. Do not blindly install or
enable features.}
To uninstall, find Sunshine in the list <a href="ms-settings:installed-apps">here</a> and select "Uninstall" from the
overflow menu. Different versions of Windows may provide slightly different steps for uninstall.
#### Standalone (lite version)
@warning{By using this package instead of the installer, performance will be reduced. This package is not
recommended for most users. No support will be provided!}
1. Download and extract
[sunshine-windows-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine-windows-portable.zip)
2. Open command prompt as administrator
3. Firewall rules
Install:
```bash
cd /d {path to extracted directory}
scripts/add-firewall-rule.bat
```
Uninstall:
```bash
cd /d {path to extracted directory}
scripts/delete-firewall-rule.bat
```
4. Virtual Gamepad Support
Install:
```bash
cd /d {path to extracted directory}
scripts/install-gamepad.bat
```
Uninstall:
```bash
cd /d {path to extracted directory}
scripts/uninstall-gamepad.bat
```
5. Windows service
Install:
```bash
cd /d {path to extracted directory}
scripts/install-service.bat
scripts/autostart-service.bat
```
Uninstall:
```bash
cd /d {path to extracted directory}
scripts/uninstall-service.bat
```
To uninstall, delete the extracted directory which contains the `sunshine.exe` file.
## Initial Setup
After installation, some initial setup is required.
### Linux
#### KMS Capture
@warning{Capture of most Wayland-based desktop environments will fail unless this step is performed.}
@note{`cap_sys_admin` may as well be root, except you don't need to be root to run the program. This is necessary to
allow Sunshine to use KMS capture.}
##### Enable
```bash
sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))
```
#### X11 Capture
For X11 capture to work, you may need to disable the capabilities that were set for KMS capture.
```bash
sudo setcap -r $(readlink -f $(which sunshine))
```
#### Service
**Start once**
```bash
systemctl --user start sunshine
```
**Start on boot**
```bash
systemctl --user enable sunshine
```
### macOS
The first time you start Sunshine, you will be asked to grant access to screen recording and your microphone.
Sunshine can only access microphones on macOS due to system limitations. To stream system audio use
[Soundflower](https://github.com/mattingalls/Soundflower) or
[BlackHole](https://github.com/ExistentialAudio/BlackHole).
@note{Command Keys are not forwarded by Moonlight. Right Option-Key is mapped to CMD-Key.}
@caution{Gamepads are not currently supported.}
## Usage
### Basic usage
If Sunshine is not installed/running as a service, then start Sunshine with the following command, unless a start
command is listed in the specified package [install](#install) instructions above.
@note{A service is a process that runs in the background. This is the default when installing Sunshine from the
Windows installer. Running multiple instances of Sunshine is not advised.}
```bash
sunshine
```
### Specify config file
```bash
sunshine <directory of conf file>/sunshine.conf
```
@note{You do not need to specify a config file. If no config file is entered the default location will be used.}
@attention{The configuration file specified will be created if it doesn't exist.}
### Start Sunshine over SSH (Linux/X11)
Assuming you are already logged into the host, you can use this command
```bash
ssh <user>@<ip_address> 'export DISPLAY=:0; sunshine'
```
If you are logged into the host with only a tty (teletypewriter), you can use `startx` to start the X server prior to
executing Sunshine. You nay need to add `sleep` between `startx` and `sunshine` to allow more time for the display to
be ready.
```bash
ssh <user>@<ip_address> 'startx &; export DISPLAY=:0; sunshine'
```
@tip{You could also utilize the `~/.bash_profile` or `~/.bashrc` files to set up the `DISPLAY` variable.}
@seealso{ See [Remote SSH Headless Setup](md_docs_2guides.html#remote-ssh-headless-setup)
on how to set up a headless streaming server without autologin and dummy plugs (X11 + NVidia GPUs)}
### Configuration
Sunshine is configured via the web ui, which is available on [https://localhost:47990](https://localhost:47990)
by default. You may replace *localhost* with your internal ip address.
@attention{Ignore any warning given by your browser about "insecure website". This is due to the SSL certificate
being self-signed.}
@caution{If running for the first time, make sure to note the username and password that you created.}
1. Add games and applications.
2. Adjust any configuration settings as needed.
3. In Moonlight, you may need to add the PC manually.
4. When Moonlight requests for you insert the pin:
- Login to the web ui
- Go to "PIN" in the Navbar
- Type in your PIN and press Enter, you should get a Success Message
- In Moonlight, select one of the Applications listed
### Arguments
To get a list of available arguments, run the following command.
@tabs{
@tab{ General | @code{.bash}
sunshine --help
@endcode }
@tab{ AppImage | @code{.bash}
./sunshine.AppImage --help
@endcode }
@tab{ Flatpak | @code{.bash}
flatpak run --command=sunshine dev.lizardbyte.app.Sunshine --help
@endcode }
}
### Shortcuts
All shortcuts start with `Ctrl+Alt+Shift`, just like Moonlight.
* `Ctrl+Alt+Shift+N`: Hide/Unhide the cursor (This may be useful for Remote Desktop Mode for Moonlight)
* `Ctrl+Alt+Shift+F1/F12`: Switch to different monitor for Streaming
### Application List
* Applications should be configured via the web UI
* A basic understanding of working directories and commands is required
* You can use Environment variables in place of values
* `$(HOME)` will be replaced by the value of `$HOME`
* `$$` will be replaced by `$`, e.g. `$$(HOME)` will be become `$(HOME)`
* `env` - Adds or overwrites Environment variables for the commands/applications run by Sunshine.
This can only be changed by modifying the `apps.json` file directly.
### Considerations
* On Windows, Sunshine uses the Desktop Duplication API which only supports capturing from the GPU used for display.
If you want to capture and encode on the eGPU, connect a display or HDMI dummy display dongle to it and run the games
on that display.
* When an application is started, if there is an application already running, it will be terminated.
* If any of the prep-commands fail, starting the application is aborted.
* When the application has been shutdown, the stream shuts down as well.
* For example, if you attempt to run `steam` as a `cmd` instead of `detached` the stream will immediately fail.
This is due to the method in which the steam process is executed. Other applications may behave similarly.
* This does not apply to `detached` applications.
* The "Desktop" app works the same as any other application except it has no commands. It does not start an application,
instead it simply starts a stream. If you removed it and would like to get it back, just add a new application with
the name "Desktop" and "desktop.png" as the image path.
* For the Linux flatpak you must prepend commands with `flatpak-spawn --host`.
### HDR Support
Streaming HDR content is officially supported on Windows hosts and experimentally supported for Linux hosts.
* General HDR support information and requirements:
* HDR must be activated in the host OS, which may require an HDR-capable display or EDID emulator dongle
connected to your host PC.
* You must also enable the HDR option in your Moonlight client settings, otherwise the stream will be SDR
(and probably overexposed if your host is HDR).
* A good HDR experience relies on proper HDR display calibration both in the OS and in game. HDR calibration can
differ significantly between client and host displays.
* You may also need to tune the brightness slider or HDR calibration options in game to the different HDR brightness
capabilities of your client's display.
* Some GPUs video encoders can produce lower image quality or encoding performance when streaming in HDR compared
to SDR.
* Additional information:
@tabs{
@tab{ Windows |
- HDR streaming is supported for Intel, AMD, and NVIDIA GPUs that support encoding HEVC Main 10 or AV1 10-bit profiles.
- We recommend calibrating the display by streaming the Windows HDR Calibration app to your client device and saving an HDR calibration profile to use while streaming.
- Older games that use NVIDIA-specific NVAPI HDR rather than native Windows HDR support may not display properly in HDR.
}
@tab{ Linux |
- HDR streaming is supported for Intel and AMD GPUs that support encoding HEVC Main 10 or AV1 10-bit profiles using VAAPI.
- The KMS capture backend is required for HDR capture. Other capture methods, like NvFBC or X11, do not support HDR.
- You will need a desktop environment with a compositor that supports HDR rendering, such as Gamescope or KDE Plasma 6.
@seealso{[Arch wiki on HDR Support for Linux](https://wiki.archlinux.org/title/HDR_monitor_support) and
[Reddit Guide for HDR Support for AMD GPUs](https://www.reddit.com/r/linux_gaming/comments/10m2gyx/guide_alpha_test_hdr_on_linux)}
}
}
### Tutorials and Guides
Tutorial videos are available [here](https://www.youtube.com/playlist?list=PLMYr5_xSeuXAbhxYHz86hA1eCDugoxXY0).
Guides are available [here](guides.md).
@admonition{Community! |
Tutorials and Guides are community generated. Want to contribute? Reach out to us on our discord server.}
<div class="section_buttons">
| Previous | Next |
|:-------------------------|--------------------------:|
| [Overview](../README.md) | [Changelog](changelog.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>
[latest-release]: https://github.com/LizardByte/Sunshine/releases/latest

View File

@ -1,539 +0,0 @@
# Guides
@admonition{Community | This collection of guides is written by the community!
Feel free to contribute your own tips and trips by making a PR.}
## Linux
### Discord call cancellation
| Author | [RickAndTired](https://github.com/RickAndTired) |
|------------|-------------------------------------------------|
| Difficulty | Easy |
* Set your normal *Sound Output* volume to 100%
![](images/discord_calls_01.png)
* Start Sunshine
* Set *Sound Output* to *sink-sunshine-stereo* (if it isn't automatic)
![](images/discord_calls_02.png)
* In Discord, right click *Deafen* and select your normal *Output Device*.
This is also where you will need to adjust output volume for Discord calls
![](images/discord_calls_03.png)
* Open *qpwgraph*
![](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`
![](images/discord_calls_05.png)
### Remote SSH Headless Setup
| Author | [Eric Dong](https://github.com/e-dong) |
|------------|----------------------------------------|
| Difficulty | Intermediate |
This is a guide to setup remote SSH into host to startup X server and Sunshine without physical login and dummy plug.
The virtual display is accelerated by the NVidia GPU using the TwinView configuration.
@attention{This guide is specific for Xorg and NVidia GPUs. I start the X server using the `startx` command.
I also only tested this on an Artix runit init system on LAN.
I didn't have to do anything special with pulseaudio (pipewire untested).
Keep your monitors plugged in until the [Checkpoint](#checkpoint) step.}
@tip{Prior to editing any system configurations, you should make a copy of the original file.
This will allow you to use it for reference or revert your changes easily.}
#### The Big Picture
Once you are done, you will need to perform these 3 steps:
1. Turn on the host machine
2. Start Sunshine on remote host with a script that:
* Edits permissions of `/dev/uinput` (added sudo config to execute script with no password prompt)
* Starts X server with `startx` on virtual display
* Starts Sunshine
3. Startup Moonlight on the client of interest and connect to host
@hint{As an alternative to SSH...
**Step 2** can be replaced with autologin and starting Sunshine as a service or putting
`sunshine &` in your `.xinitrc` file if you start your X server with `startx`.
In this case, the workaround for `/dev/uinput` permissions is not needed because the udev rule would be triggered
for "physical" login. See [Linux Setup](md_docs_2getting__started.html#linux). I personally think autologin compromises
the security of the PC, so I went with the remote SSH route. I use the PC more than for gaming, so I don't need a
virtual display everytime I turn on the PC (E.g running updates, config changes, file/media server).}
First we will [setup the host](#host-setup) and then the [SSH Client](#ssh-client-setup)
(Which may not be the same as the machine running the moonlight client).
#### Host Setup
We will be setting up:
1. [Static IP Setup](#static-ip-setup)
2. [SSH Server Setup](#ssh-server-setup)
3. [Virtual Display Setup](#virtual-display-setup)
4. [Uinput Permissions Workaround](#uinput-permissions-workaround)
5. [Stream Launcher Script](#stream-launcher-script)
#### Static IP Setup
Setup static IP Address for host. For LAN connections you can use DHCP reservation within your assigned range.
e.g. 192.168.x.x. This will allow you to ssh to the host consistently, so the assigned IP address does
not change. It is preferred to set this through your router config.
#### SSH Server Setup
@note{Most distros have OpenSSH already installed. If it is not present, install OpenSSH using your package manager.}
@tabs{
@tab{Debian based | ```bash
sudo apt update
sudo apt install openssh-server
```}
@tab{Arch based | ```bash
sudo pacman -S openssh
# Install openssh-<other_init> if you are not using SystemD
# e.g. sudo pacman -S openssh-runit
```}
@tab{Alpine based | ```bash
sudo apk update
sudo apk add openssh
```}
@tab{Fedora based (dnf) | ```bash
sudo dnf install openssh-server
```}
@tab{Fedora based (yum) | ```bash
sudo yum install openssh-server
```}
}
Next make sure the OpenSSH daemon is enabled to run when the system starts.
@tabs{
@tab{SystemD | ```bash
sudo systemctl enable sshd.service
sudo systemctl start sshd.service # Starts the service now
sudo systemctl status sshd.service # See if the service is running
```}
@tab{Runit | ```bash
sudo ln -s /etc/runit/sv/sshd /run/runit/service # Enables the OpenSSH daemon to run when system starts
sudo sv start sshd # Starts the service now
sudo sv status sshd # See if the service is running
```}
@tab{OpenRC | ```bash
rc-update add sshd # Enables service
rc-status # List services to verify sshd is enabled
rc-service sshd start # Starts the service now
```}
}
**Disabling PAM in sshd**
I noticed when the ssh session is disconnected for any reason, `pulseaudio` would disconnect.
This is due to PAM handling sessions. When running `dmesg`, I noticed `elogind` would say removed user session.
In this [Gentoo Forums post](https://forums.gentoo.org/viewtopic-t-1090186-start-0.html),
someone had a similar issue. Starting the X server in the background and exiting out of the console would cause your
session to be removed.
@caution{According to this [article](https://devicetests.com/ssh-usepam-security-session-status)
disabling PAM increases security, but reduces certain functionality in terms of session handling.
*Do so at your own risk!*}
Edit the ``sshd_config`` file with the following to disable PAM.
```txt
usePAM no
```
After making changes to the `sshd_config`, restart the sshd service for changes to take effect.
@tip{Run the command to check the ssh configuration prior to restarting the sshd service.
```bash
sudo sshd -t -f /etc/ssh/sshd_config
```
An incorrect configuration will prevent the sshd service from starting, which might mean
losing SSH access to the server.}
@tabs{
@tab{SystemD | ```bash
sudo systemctl restart sshd.service
```}
@tab{Runit | ```bash
sudo sv restart sshd
```}
@tab{OpenRC | ```bash
sudo rc-service sshd restart
```}
}
#### Virtual Display Setup
As an alternative to a dummy dongle, you can use this config to create a virtual display.
@important{This is only available for NVidia GPUs using Xorg.}
@hint{Use ``xrandr`` to see name of your active display output. Usually it starts with ``DP`` or ``HDMI``. For me, it is ``DP-0``.
Put this name for the ``ConnectedMonitor`` option under the ``Device`` section.
```bash
xrandr | grep " connected" | awk '{ print $1 }'
```
}
```xorg
Section "ServerLayout"
Identifier "TwinLayout"
Screen 0 "metaScreen" 0 0
EndSection
Section "Monitor"
Identifier "Monitor0"
Option "Enable" "true"
EndSection
Section "Device"
Identifier "Card0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
Option "MetaModes" "1920x1080"
Option "ConnectedMonitor" "DP-0"
Option "ModeValidation" "NoDFPNativeResolutionCheck,NoVirtualSizeCheck,NoMaxPClkCheck,NoHorizSyncCheck,NoVertRefreshCheck,NoWidthAlignmentCheck"
EndSection
Section "Screen"
Identifier "metaScreen"
Device "Card0"
Monitor "Monitor0"
DefaultDepth 24
Option "TwinView" "True"
SubSection "Display"
Modes "1920x1080"
EndSubSection
EndSection
```
@note{The `ConnectedMonitor` tricks the GPU into thinking a monitor is connected,
even if there is none actually connected! This allows a virtual display to be created that is accelerated with
your GPU! The `ModeValidation` option disables valid resolution checks, so you can choose any
resolution on the host!
**References**
* [issue comment on virtual-display-linux](https://github.com/dianariyanto/virtual-display-linux/issues/9#issuecomment-786389065)
* [Nvidia Documentation on Configuring TwinView](https://download.nvidia.com/XFree86/Linux-x86/270.29/README/configtwinview.html)
* [Arch Wiki Nvidia#TwinView](https://wiki.archlinux.org/title/NVIDIA#TwinView)
* [Unix Stack Exchange - How to add virtual display monitor with Nvidia proprietary driver](https://unix.stackexchange.com/questions/559918/how-to-add-virtual-monitor-with-nvidia-proprietary-driver)
}
#### Uinput Permissions Workaround
##### Steps
We can use `chown` to change the permissions from a script. Since this requires `sudo`,
we will need to update the sudo configuration to execute this without being prompted for a password.
1. Create a `sunshine-setup.sh` script to update permissions on `/dev/uinput`. Since we aren't logged into the host,
the udev rule doesn't apply.
2. Update user sudo configuration `/etc/sudoers.d/<user>` to allow the `sunshine-setup.sh`
script to be executed with `sudo`.
@note{After I setup the :ref:`udev rule <about/setup:install>` to get access to `/dev/uinput`, I noticed when I sshed
into the host without physical login, the ACL permissions on `/dev/uinput` were not changed. So I asked
[reddit](https://www.reddit.com/r/linux_gaming/comments/14htuzv/does_sshing_into_host_trigger_udev_rule_on_the).
I discovered that SSH sessions are not the same as a physical login.
I suppose it's not possible for SSH to trigger a udev rule or create a physical login session.}
##### Setup Script
This script will take care of any preconditions prior to starting up Sunshine.
Run the following to create a script named something like `sunshine-setup.sh`:
```bash
echo "chown $(id -un):$(id -gn) /dev/uinput" > sunshine-setup.sh && \
chmod +x sunshine-setup.sh
```
(**Optional**) To Ensure ethernet is being used for streaming, you can block Wi-Fi with `rfkill`.
Run this command to append the rfkill block command to the script:
```bash
echo "rfkill block $(rfkill list | grep "Wireless LAN" \
| sed 's/^\([[:digit:]]\).*/\1/')" >> sunshine-setup.sh
```
##### Sudo Configuration
We will manually change the permissions of `/dev/uinput` using `chown`.
You need to use `sudo` to make this change, so add/update the entry in `/etc/sudoers.d/${USER}`.
@danger{Do so at your own risk! It is more secure to give sudo and no password prompt to a single script,
than a generic executable like chown.}
@warning{Be very careful of messing this config up. If you make a typo, *YOU LOSE THE ABILITY TO USE SUDO*.
Fortunately, your system is not borked, you will need to login as root to fix the config.
You may want to setup a backup user / SSH into the host as root to fix the config if this happens.
Otherwise, you will need to plug your machine back into a monitor and login as root to fix this.
To enable root login over SSH edit your SSHD config, and add `PermitRootLogin yes`, and restart the SSH server.}
1. First make a backup of your `/etc/sudoers.d/${USER}` file.
```bash
sudo cp /etc/sudoers.d/${USER} /etc/sudoers.d/${USER}.backup
```
2. `cd` to the parent dir of the `sunshine-setup.sh` script.
3. Execute the following to update your sudoer config file.
```bash
echo "${USER} ALL=(ALL:ALL) ALL, NOPASSWD: $(pwd)/sunshine-setup.sh" \
| sudo tee /etc/sudoers.d/${USER}
```
These changes allow the script to use sudo without being prompted with a password.
e.g. `sudo $(pwd)/sunshine-setup.sh`
#### Stream Launcher Script
This is the main entrypoint script that will run the `sunshine-setup.sh` script, start up X server, and Sunshine.
The client will call this script that runs on the host via ssh.
##### Sunshine Startup Script
This guide will refer to this script as `~/scripts/sunshine.sh`.
The setup script will be referred as `~/scripts/sunshine-setup.sh`.
```bash
#!/bin/bash
set -e
export DISPLAY=:0
# Check existing X server
ps -e | grep X >/dev/null
[[ ${?} -ne 0 ]] && {
echo "Starting X server"
startx &>/dev/null &
[[ ${?} -eq 0 ]] && {
echo "X server started successfully"
} || echo "X server failed to start"
} || echo "X server already running"
# Check if sunshine is already running
ps -e | grep -e .*sunshine$ >/dev/null
[[ ${?} -ne 0 ]] && {
sudo ~/scripts/sunshine-setup.sh
echo "Starting Sunshine!"
sunshine > /dev/null &
[[ ${?} -eq 0 ]] && {
echo "Sunshine started successfully"
} || echo "Sunshine failed to start"
} || echo "Sunshine is already running"
# Add any other Programs that you want to startup automatically
# e.g.
# steam &> /dev/null &
# firefox &> /dev/null &
# kdeconnect-app &> /dev/null &
```
#### SSH Client Setup
We will be setting up:
1. [SSH Key Authentication Setup](#ssh-key-authentication-setup)
2. [SSH Client Script (Optional)](#ssh-client-script-optional)
##### SSH Key Authentication Setup
1. Setup your SSH keys with `ssh-keygen` and use `ssh-copy-id` to authorize remote login to your host.
Run `ssh <user>@<ip_address>` to login to your host.
SSH keys automate login so you don't need to input your password!
2. Optionally setup a `~/.ssh/config` file to simplify the `ssh` command
```txt
Host <some_alias>
Hostname <ip_address>
User <username>
IdentityFile ~/.ssh/<your_private_key>
```
Now you can use `ssh <some_alias>`.
`ssh <some_alias> <commands/script>` will execute the command or script on the remote host.
##### Checkpoint
As a sanity check, let's make sure your setup is working so far!
###### Test Steps
With your monitor still plugged into your Sunshine host PC:
1. `ssh <alias>`
2. `~/scripts/sunshine.sh`
3. `nvidia-smi`
You should see the Sunshine and Xorg processing running:
```bash
nvidia-smi
```
*Output:*
```txt
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 NVIDIA GeForce RTX 3070 Off | 00000000:01:00.0 On | N/A |
| 30% 46C P2 45W / 220W | 549MiB / 8192MiB | 2% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| 0 N/A N/A 1393 G /usr/lib/Xorg 86MiB |
| 0 N/A N/A 1440 C+G sunshine 293MiB |
+---------------------------------------------------------------------------------------+
```
4. Check `/dev/uinput` permissions
```bash
ls -l /dev/uinput
```
*Output:*
```console
crw------- 1 <user> <primary_group> 10, 223 Aug 29 17:31 /dev/uinput
```
5. Connect to Sunshine host from a moonlight client
Now kill X and Sunshine by running `pkill X` on the host, unplug your monitors from your GPU, and repeat steps 1 - 5.
You should get the same result.
With this setup you don't need to modify the Xorg config regardless if monitors are plugged in or not.
```bash
pkill X
```
##### SSH Client Script (Optional)
At this point you have a working setup! For convenience, I created this bash script to automate the
startup of the X server and Sunshine on the host.
This can be run on Unix systems, or on Windows using the `git-bash` or any bash shell.
For Android/iOS you can install Linux emulators, e.g. `Userland` for Android and `ISH` for iOS.
The neat part is that you can execute one script to launch Sunshine from your phone or tablet!
```bash
#!/bin/bash
set -e
ssh_args="<user>@192.168.X.X" # Or use alias set in ~/.ssh/config
check_ssh(){
result=1
# Note this checks infinitely, you could update this to have a max # of retries
while [[ $result -ne 0 ]]
do
echo "checking host..."
ssh $ssh_args "exit 0" 2>/dev/null
result=$?
[[ $result -ne 0 ]] && {
echo "Failed to ssh to $ssh_args, with exit code $result"
}
sleep 3
done
echo "Host is ready for streaming!"
}
start_stream(){
echo "Starting sunshine server on host..."
echo "Start moonlight on your client of choice"
# -f runs ssh in the background
ssh -f $ssh_args "~/scripts/sunshine.sh &"
}
check_ssh
start_stream
exit_code=${?}
sleep 3
exit ${exit_code}
```
#### Next Steps
Congratulations, you can now stream your desktop headless! When trying this the first time,
keep your monitors close by incase something isn't working right.
@seealso{Now that you have a virtual display, you may want to automate changing the resolution
and refresh rate prior to connecting to an app. See
[Changing Resolution and Refresh Rate](md_docs_2app__examples#changing-resolution-and-refresh-rate)
for more information.}
## macOS
@todo{It's looking lonely here.}
## Windows
| Author | [BeeLeDev](https://github.com/BeeLeDev) |
|------------|-----------------------------------------|
| Difficulty | Intermediate |
### Discord call cancellation
Cancel Discord call audio with Voicemeeter (Standard)
#### Voicemeeter Configuration
1. Click "Hardware Out"
2. Set the physical device you receive audio to as your Hardware Out with MME
3. Turn on BUS A for the Virtual Input
#### Windows Configuration
1. Open the sound settings
2. Set your default Playback as Voicemeeter Input
@tip{Run audio in the background to find the device that your Virtual Input is using
(Voicemeeter In #), you will see the bar to the right of the device have green bars
going up and down. This device will be referred to as Voicemeeter Input.}
#### Discord Configuration
1. Open the settings
2. Go to Voice & Video
3. Set your Output Device as the physical device you receive audio to
@tip{It is usually the same device you set for Hardware Out in Voicemeeter.}
#### Sunshine Configuration
1. Go to Configuration
2. Go to the Audio/Video tab
3. Set Virtual Sink as Voicemeeter Input
@note{This should be the device you set as default previously in Playback.}
<div class="section_buttons">
| Previous | Next |
|:--------------------------------|--------------------------------------------:|
| [App Examples](app_examples.md) | [Performance Tuning](performance_tuning.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

View File

@ -1,32 +0,0 @@
# Legal
@attention{This documentation is for informational purposes only and is not intended as legal advice. If you have
any legal questions or concerns about using Sunshine, we recommend consulting with a lawyer.}
Sunshine is licensed under the GPL-3.0 license, which allows for free use and modification of the software.
The full text of the license can be reviewed [here](https://github.com/LizardByte/Sunshine/blob/master/LICENSE).
## Commercial Use
Sunshine can be used in commercial applications without any limitations. This means that businesses and organizations
can use Sunshine to create and sell products or services without needing to seek permission or pay a fee.
However, it is important to note that the GPL-3.0 license does not grant any rights to distribute or sell the encoders
contained within Sunshine. If you plan to sell access to Sunshine as part of their distribution, you are responsible
for obtaining the necessary licenses to do so. This may include obtaining a license from the
Motion Picture Experts Group (MPEG-LA) and/or any other necessary licensing requirements.
In summary, while Sunshine is free to use, it is the user's responsibility to ensure compliance with all applicable
licensing requirements when redistributing the software as part of a commercial offering. If you have any questions or
concerns about using Sunshine in a commercial setting, we recommend consulting with a lawyer.
<div class="section_buttons">
| Previous | Next |
|:------------------------------------------------|----------------------------------:|
| [Gamestream Migration](gamestream_migration.md) | [Configuration](configuration.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,25 +0,0 @@
# Performance Tuning
In addition to the options available in the [Configuration](configuration.md) section, there are a few additional
system options that can be used to help improve the performance of Sunshine.
## AMD
In Windows, enabling *Enhanced Sync* in AMD's settings may help reduce the latency by an additional frame. This
applies to `amfenc` and `libx264`.
## NVIDIA
Enabling *Fast Sync* in Nvidia settings may help reduce latency.
<div class="section_buttons">
| Previous | Next |
|:--------------------|--------------------------------------:|
| [Guides](guides.md) | [Troubleshooting](troubleshooting.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,31 +0,0 @@
# Third-Party Packages
@danger{These packages are not maintained by LizardByte. Use at your own risk.}
## Chocolatey
[![Chocolatey](https://img.shields.io/badge/dynamic/xml.svg?color=orange&label=chocolatey&style=for-the-badge&prefix=v&query=%2F%2Ftr%5B%40id%3D%27chocolatey%27%5D%2Ftd%5B3%5D%2Fspan%2Fa&url=https%3A%2F%2Frepology.org%2Fproject%2Fsunshine%2Fversions&logo=chocolatey)](https://community.chocolatey.org/packages/sunshine)
## Flathub
[![Flathub](https://img.shields.io/flathub/v/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=Flathub)](https://flathub.org/apps/dev.lizardbyte.app.Sunshine)
## nixpkgs
[![nixpkgs](https://img.shields.io/badge/dynamic/xml.svg?color=orange&label=nixpkgs&style=for-the-badge&prefix=v&query=%2F%2Ftr%5B%40id%3D%27nix_unstable%27%5D%2Ftd%5B3%5D%2Fspan%2Fa&url=https%3A%2F%2Frepology.org%2Fproject%2Fsunshine%2Fversions&logo=nixos)](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/servers/sunshine/default.nix)
## Scoop
[![Scoop (extras bucket)](https://img.shields.io/scoop/v/sunshine.svg?bucket=extras&style=for-the-badge&logo=data:image/vnd.microsoft.icon;base64,AAABAAEAEBAAAAEAGAAhAwAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAuhJREFUOE9tk1tIFFEYx7+ZXdfbrhdMElJLFCykCxL20MUW9UkkqeiOFGSWYW75EvjgVlJmlpkaJV5SMtQlMYjEROqpQoiMMEpRW2/p6q67bTuXM2dmOjPu2moNDHPm4/v/Zs7//D9KlmUNAMjkBoqiJOVJapTyqqzXXn49tCohzbRSVERPSi7tokFOSkne2rmzoED4H6C0pHwjT2G2qspsU7U+wBuzWTs8M9mlpen0YEOoMS/73DjrnMuhXFyiLEmjwZH6vmufR5DDNtHBI7b9cWNNpw9AgcVCtw6+P8R43KdkjHMM+vDqI/tywyiN5oy46KQpLEogiG0149+7rG5HGRK5o01N9VYVoPxm/ZXCOMrD95NloihiOj4qhs1K3R8IbqQFogVJAuRifrXNT3wactkGmpvrbni9UregQu7nn87X0XB3w+ZYfcruHRAVJgNtE0EclmCGM8CYC2DE5UK8TJXtzT1ZZTRSeJUHiqOvW29Vb89KKw4kYgEvgIQFGHurg3l7AlitS8CzAohYZgQB5ZU9Ovx8FcBkMkdcKEx5GL1ee1yWGcKjgWMQfHgVDVOjNPD88qHwHAYOe57GbHOcLSoqQiunYC4tT4tL0NYmbwkOx1hO1ukABITg40AkOO0BJCgiYFEAl9sBjGj/pl+nyairq5xdAdy50xbKuH+eFyUMkijdJtHQCAIGxiOQYC0nguMYmJqeVJJW29vfU7wqSErDzeuV6aQ5lUPoIjn7RI5FRIRUMQkbLC05YN42txgaEpTd89IyuNZEaGlpCZqdXsjHAj5Avp7h+c2CIIiqGGMMMzNTgDD/oLev57I3vX+T6IttRUVNvNvpusey3EGeE5QtAkI82B12YFjmXagh5ER39zOrfw7UWfDPvcl0ddP0j+lGjucylDoiZhIbvkboDccsL9q/+Hr/2YI/JDMzZ4/IIyMhRyh1XYBmKCEptqOhoWFlyHwAZZxX/YHXNK/3/tiVUfcV6T8hxMYSf1PeGAAAAABJRU5ErkJggg==)](https://scoop.sh/#/apps?s=0&d=1&o=true&q=sunshine)
## Solus
[![Solus](https://img.shields.io/badge/dynamic/xml.svg?color=orange&label=Solus&style=for-the-badge&prefix=v&query=%2F%2Ftr%5B%40id%3D%27solus%27%5D%2Ftd%5B3%5D%2Fspan%2Fa&url=https%3A%2F%2Frepology.org%2Fproject%2Fsunshine%2Fversions&logo=solus)](https://dev.getsol.us/source/sunshine)
<div class="section_buttons">
| Previous | Next |
|:------------------------------|------------------------------------------------:|
| [Docker](../DOCKER_README.md) | [Gamestream Migration](gamestream_migration.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

View File

@ -1,205 +0,0 @@
# Troubleshooting
## General
### Forgotten Credentials
If you forgot your credentials to the web UI, try this.
@tabs{
@tab{General | ```bash
sunshine --creds {new-username} {new-password}
```
}
@tab{AppImage | ```bash
./sunshine.AppImage --creds {new-username} {new-password}
```
}
@tab{Flatpak | ```bash
flatpak run --command=sunshine dev.lizardbyte.app.Sunshine --creds {new-username} {new-password}
```
}
}
@tip{Don't forget to replace `{new-username}` and `{new-password}` with your new credentials.
Do not include the curly braces.}
### Web UI Access
Can't access the web UI?
1. Check firewall rules.
### Controller works on Steam but not in games
One trick might be to change Steam settings and check or uncheck the configuration to support Xbox/Playstation
controllers and leave only support for Generic controllers.
Also, if you have many controllers already directly connected to the host, it might help to disable them so that the
Sunshine provided controller (connected to the guest) is the "first" one. In Linux this can be accomplished on USB
devices by finding the device in `/sys/bus/usb/devices/` and writing `0` to the `authorized` file.
### Network performance test
For real-time game streaming the most important characteristic of the network
path between server and client is not pure bandwidth but rather stability and
consistency (low latency with low variance, minimal or no packet loss).
The network can be tested using the multi-platform tool [iPerf3](https://iperf.fr).
On the Sunshine host `iperf3` is started in server mode:
```bash
iperf3 -s
```
On the client device iperf3 is asked to perform a 60-second UDP test in reverse
direction (from server to client) at a given bitrate (e.g. 50 Mbps):
```bash
iperf3 -c {HostIpAddress} -t 60 -u -R -b 50M
```
Watch the output on the client for packet loss and jitter values. Both should be
(very) low. Ideally packet loss remains less than 5% and jitter below 1ms.
For Android clients use
[PingMaster](https://play.google.com/store/apps/details?id=com.appplanex.pingmasternetworktools).
For iOS clients use [HE.NET Network Tools](https://apps.apple.com/us/app/he-net-network-tools/id858241710).
If you are testing a remote connection (over the internet) you will need to
forward the port 5201 (TCP and UDP) from your host.
### Packet loss (Buffer overrun)
If the host PC (running Sunshine) has a much faster connection to the network
than the slowest segment of the network path to the client device (running
Moonlight), massive packet loss can occur: Sunshine emits its stream in bursts
every 16ms (for 60fps) but those bursts can't be passed on fast enough to the
client and must be buffered by one of the network devices inbetween. If the
bitrate is high enough, these buffers will overflow and data will be discarded.
This can easily happen if e.g. the host has a 2.5 Gbit/s connection and the
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
solution would be to configure traffic shaping rules at the OS-level, so that
only Sunshine's traffic is slowed down.)
Sunshine versions > 0.23.1 include improved networking code that should
alleviate or even solve this issue (without reducing the NIC speed).
### Packet loss (MTU)
Although unlikely, some guests might work better with a lower
[MTU](https://en.wikipedia.org/wiki/Maximum_transmission_unit) from the host.
For example, a LG TV was found to have 30-60% packet loss when the host had MTU
set to 1500 and 1472, but 0% packet loss with a MTU of 1428 set in the network card
serving the stream (a Linux PC). It's unclear how that helped precisely, so it's a last
resort suggestion.
## Linux
### Hardware Encoding fails
Due to legal concerns, Mesa has disabled hardware decoding and encoding by default.
```txt
Error: Could not open codec [h264_vaapi]: Function not implemented
```
If you see the above error in the Sunshine logs, compiling *Mesa* manually, may be required. See the official Mesa3D
[Compiling and Installing](https://docs.mesa3d.org/install.html) documentation for instructions.
@important{You must re-enable the disabled encoders. You can do so, by passing the following argument to the build
system. You may also want to enable decoders, however that is not required for Sunshine and is not covered here.
```bash
-Dvideo-codecs=h264enc,h265enc
```
}
@note{Other build options are listed in the
[meson options](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/meson_options.txt) file.}
### KMS Streaming fails
If screencasting fails with KMS, you may need to run the following to force unprivileged screencasting.
```bash
sudo setcap -r $(readlink -f $(which sunshine))
```
@note{The above command will not work with the AppImage or Flatpak packages. Please refer to the
[AppImage setup](md_docs_2getting__started.html#appimage) or
[Flatpak setup](md_docs_2getting__started.html#flatpak) for more specific instructions.}
### KMS streaming fails on Nvidia GPUs
If KMS screen capture results in a black screen being streamed, you may need to
set the parameter `modeset=1` for Nvidia's kernel module. This can be done by
adding the following directive to the kernel command line:
```bash
nvidia_drm.modeset=1
```
Consult your distribution's documentation for details on how to do this. (Most
often grub is used to load the kernel and set its command line.)
### AMD encoding latency issues
If you notice unexpectedly high encoding latencies (e.g. in Moonlight's
performance overlay) or strong fluctuations thereof, this is due to
[missing support](https://gitlab.freedesktop.org/drm/amd/-/issues/3336)
in Mesa/libva for AMD's low latency encoder mode. This is particularly
problematic at higher resolutions (4K).
Only the most recent development versions of mesa include support for this
low-latency mode. It will be included in Mesa-24.2.
In order to enable it, Sunshine has to be started with a special environment
variable:
```bash
AMD_DEBUG=lowlatencyenc sunshine
```
To check whether low-latency mode is being used, one can watch the `VCLK` and
`DCLK` frequencies in `amdgpu_top`. Without this encoder tuning both clock
frequencies will fluctuate strongly, whereas with active low-latency encoding
they will stay high as long as the encoder is used.
### Gamescope compatibility
Some users have reported stuttering issues when streaming games running within Gamescope.
## macOS
### Dynamic session lookup failed
If you get this error:
> Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that
> org.freedesktop.dbus-session.plist is loaded!
Try this.
```bash
launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist
```
## Windows
### No gamepad detected
Verify that you've installed [Nefarius Virtual Gamepad](https://github.com/nefarius/ViGEmBus/releases/latest).
### Permission denied
Since Sunshine runs as a service on Windows, it may not have the same level of access that your regular user account
has. You may get permission denied errors when attempting to launch a game or application from a non system drive.
You will need to modify the security permissions on your disk. Ensure that user/principal SYSTEM has full
permissions on the disk.
<div class="section_buttons">
| Previous | Next |
|:--------------------------------------------|------------------------:|
| [Performance Tuning](performance_tuning.md) | [Building](building.md) |
</div>
<details style="display: none;">
<summary></summary>
[TOC]
</details>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1,814 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>LizardByte - Sunshine</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="Self-hosted game stream host for Moonlight." />
<meta name="author" content="" />
<!-- Open Graph/Twitter metadata -->
<meta property="og:site_name" content="LizardByte" />
<meta property="og:title" content="LizardByte | Sunshine" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://app.lizardbyte.dev/uno/github/openGraphImages/Sunshine_624x312.png" />
<meta property="og:url" content="https://app.lizardbyte.dev/Sunshine" />
<meta property="og:description" content="Self-hosted game stream host for Moonlight." />
<meta property="og:locale" content="en-US" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="og:twitter_site" content="@lizardbytedev" />
<meta property="og:twitter_site:id" content="@lizardbytedev" />
<meta property="og:twitter_creator" content="@lizardbytedev" />
<meta property="og:twitter_creator:id" content="@lizardbytedev" />
<meta property="twitter:image" content="https://app.lizardbyte.dev/uno/github/openGraphImages/Sunshine_624x312.png" />
<!-- Favicon-->
<link rel="icon" type="image/x-icon" href="https://app.lizardbyte.dev/assets/images/favicon.ico" />
<!-- FontAwesome-->
<link href="https://app.lizardbyte.dev/node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" />
<!-- Bootstrap theme-->
<link href="https://app.lizardbyte.dev/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<!-- Custom css-->
<link href="https://app.lizardbyte.dev/css/custom.css" rel="stylesheet" />
<script src="https://app.lizardbyte.dev/node_modules/jquery/dist/jquery.min.js"></script>
<script src="https://app.lizardbyte.dev/node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
<!-- Crowdin widget -->
<script src="https://app.lizardbyte.dev/js/crowdin.js"></script>
<script src="https://app.lizardbyte.dev/js/crowdin_web_widget.js"></script>
</head>
<body class="d-flex flex-column h-100 bg-dark-gray">
<main class="flex-shrink-0 overflow-hidden">
<!-- Navigation-->
<nav id="nav-container"></nav>
<!-- Header-->
<header class="bg-dark py-0">
<section class="offset-anchor" id="Top">
<div id="carousel1" class="carousel slide carousel-fade" data-bs-ride="carousel"
style="height:50vh">
<!--Indicators-->
<div class="carousel-indicators">
<button type="button" data-bs-target="#carousel1" data-bs-slide-to="0" class="active"
aria-current="true" aria-label="Slide 1"></button>
<button type="button" data-bs-target="#carousel1" data-bs-slide-to="1"
aria-label="Slide 2"></button>
<button type="button" data-bs-target="#carousel1" data-bs-slide-to="2"
aria-label="Slide 3"></button>
</div>
<!--/.Indicators-->
<!--Slides-->
<div class="carousel-inner" role="listbox">
<div class="carousel-item active">
<div class="view">
<img src="assets/images/AdobeStock_305732536_1920x1280.jpg" alt="">
<div class="mask rgba-black-light"></div>
</div>
<!-- <div class="carousel-caption">-->
<!-- <h3 class="h3-responsive">1</h3>-->
<!-- <p>First text</p>-->
<!-- </div>-->
</div>
<div class="carousel-item">
<!--Mask color-->
<div class="view">
<img src="assets/images/AdobeStock_231616343_1920x1280.jpg" alt="">
<div class="mask rgba-black-strong"></div>
</div>
<!-- <div class="carousel-caption">-->
<!-- <h3 class="h3-responsive">2</h3>-->
<!-- <p>Secondary text</p>-->
<!-- </div>-->
</div>
<div class="carousel-item">
<!--Mask color-->
<div class="view">
<img src="assets/images/AdobeStock_303330124_1920x1280.jpg" alt="">
<div class="mask rgba-black-slight"></div>
</div>
<!-- <div class="carousel-caption">-->
<!-- <h3 class="h3-responsive">3</h3>-->
<!-- <p>Third text</p>-->
<!-- </div>-->
</div>
</div>
<!--/.Slides-->
<div>
<h1 class="carousel-overlay-title display-5 fw-bolder text-white mb-2">Sunshine</h1>
<p class="carousel-overlay-subtitle lead fw-bolder text-white-50 mb-4">
A LizardByte project</p>
</div>
<!--Controls-->
<button class="carousel-control-prev" type="button" data-bs-target="#carousel1"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carousel1"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
<!--/.Controls-->
</div>
</section>
</header>
<!-- About section-->
<section class="offset-anchor py-5" id="About">
<div class="container px-auto">
<p class="lead text-center text-white mx-auto mt-0 mb-5">
Sunshine is a self-hosted game stream host for Moonlight. Offering low latency, cloud gaming
server capabilities with support for AMD, Intel, and Nvidia GPUs for hardware encoding. Software
encoding is also available. You can connect to Sunshine from any Moonlight client on a variety
of devices. A web UI is provided to allow configuration, and client pairing, from your favorite
web browser. Pair from the local server or any mobile device.
</p>
</div>
</section>
<!-- Features section-->
<section class="offset-anchor bg-dark py-5" id="Features">
<div class="container px-auto">
<h2 class="text-center text-white fw-bolder my-5">Features</h2>
<!-- Create a card for each feature -->
<div class="row gx-5">
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-server"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Self-hosted</h5>
<p class="mb-0">
Run Sunshine on your own hardware. No need to pay monthly fees to a
cloud gaming provider.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<img height="40" src="https://moonlight-stream.org/images/moonlight.svg">
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Moonlight Support</h5>
<p class="mb-0">
Connect to Sunshine from any Moonlight client. Moonlight is available
for Windows, macOS, Linux, Android, iOS, Xbox, and more. See
<a class="text-white" href="#Clients">clients</a> for more information.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-microchip"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Hardware Encoding</h5>
<p class="mb-0">
Sunshine supports AMD, Intel, and Nvidia GPUs for hardware encoding.
Software encoding is also available.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 mb-5 mb-lg-0">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-globe"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Low Latency</h5>
<p class="mb-0">
Sunshine is designed to provide the lowest latency possible to achieve optimal gaming performance.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 mb-5 mb-lg-0">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-gamepad"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Control</h5>
<p class="mb-0">
Sunshine emulates an Xbox, PlayStation, or Nintendo Switch controller.
Use nearly any controller on your Moonlight client!<br>
<small>
<ul>
<li>Nintendo Switch emulation is only available on Linux.</li>
<li>Gamepad emulation is not currently supported on macOS.</li>
</ul>
</small>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 mb-5 mb-lg-0">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-gear"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">Configurable</h5>
<p class="mb-0">
Sunshine offers many configuration options to customize your experience.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Clients section-->
<section class="offset-anchor bg-dark py-5" id="Clients">
<div class="container px-auto">
<h2 class="text-center text-white fw-bolder my-5">Clients</h2>
<!-- Create a card for each client -->
<div class="row gx-5">
<!-- Android -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-android"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/moonlight-stream/moonlight-android" target="_blank" class="text-white text-decoration-none">
Android
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-info rounded-pill">Official</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div>
<a href="https://play.google.com/store/apps/details?id=com.limelight" target="_blank">
<img alt="Get it on Google Play"
src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"
height="60">
</a>
</div>
<div>
<a href="https://www.amazon.com/gp/product/B00JK4MFN2" target="_blank">
<img alt="Available at Amazon Appstore"
src="https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-black.png"
height="60"
style="padding: 10px;">
</a>
</div>
<div>
<a href="https://f-droid.org/packages/com.limelight" target="_blank">
<img alt="Get it on F-Droid"
src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
height="60">
</a>
</div>
</div>
</div>
</div>
<!-- ChromeOS -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-chrome"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/moonlight-stream/moonlight-chrome" target="_blank" class="text-white text-decoration-none">
ChromeOS
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-info rounded-pill">Official</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://chrome.google.com/webstore/detail/moonlight-game-streaming/gemamigbbenahjlfnmlfdjhdnkpbkfjj" target="_blank" class="btn btn-outline-light">
<img alt="Available in the Chrome Web Store"
src="https://developer.chrome.com/static/docs/webstore/branding/image/206x58-chrome-web-043497a3d766e.png"
height="30">
</a>
</div>
</div>
</div>
</div>
<!-- iOS -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-apple"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/moonlight-stream/moonlight-ios" target="_blank" class="text-white text-decoration-none">
iOS
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-info rounded-pill">Official</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://apps.apple.com/us/app/moonlight-game-streaming/id1000551566" target="_blank">
<img alt="Download on the App Store"
src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg"
height="40">
</a>
</div>
<div class="pb-3">
<a href="https://apps.apple.com/us/app/moonlight-game-streaming/id1000551566" target="_blank">
<img alt="Download on Apple TV"
src="https://developer.apple.com/app-store/marketing/guidelines/images/badge-download-on-apple-tv.svg"
height="40">
</a>
</div>
</div>
</div>
</div>
<!-- Moonlight-QT -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-linux"></i>
<i class="fa-fw fa-2x fab fa-apple"></i>
<i class="fa-fw fa-2x fab fa-windows"></i>
<i class="fa-fw fa-2x fab fa-steam"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/moonlight-stream/moonlight-qt" target="_blank" class="text-white text-decoration-none">
QT
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-info rounded-pill">Official</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/moonlight-stream/moonlight-qt/releases" target="_blank" class="btn btn-info">
<i class="fab fa-github"></i> Download on GitHub
</a>
</div>
</div>
</div>
</div>
<!-- Embedded -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-microchip"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/moonlight-stream/moonlight-embedded" target="_blank" class="text-white text-decoration-none">
Embedded
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-info rounded-pill">Official</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/irtimmer/moonlight-embedded/wiki/Packages" target="_blank" class="btn btn-info">
<i class="fas fa-download"></i> Download
</a>
</div>
</div>
</div>
</div>
<!-- Xbox One/Series -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-xbox"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/TheElixZammuto/moonlight-xbox" target="_blank" class="text-white text-decoration-none">
Xbox One/Series
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-warning rounded-pill">Community</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://apps.microsoft.com/store/detail/moonlight-uwp/9MW1BS08ZBTH" target="_blank">
<img alt="Get it from Microsoft"
src="https://get.microsoft.com/images/en-us%20dark.svg"
height="40">
</a>
</div>
</div>
</div>
</div>
<!-- PS Vita -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fab fa-playstation"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/xyzz/vita-moonlight" target="_blank" class="text-white text-decoration-none">
PS Vita
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-warning rounded-pill">Community</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/xyzz/vita-moonlight/releases" target="_blank" class="btn btn-info">
<i class="fab fa-github"></i> Download on GitHub
</a>
</div>
</div>
</div>
</div>
<!-- Nintendo Switch -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-code"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/XITRIX/Moonlight-Switch" target="_blank" class="text-white text-decoration-none">
Nintendo Switch
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-warning rounded-pill">Community</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/XITRIX/Moonlight-Switch/releases" target="_blank" class="btn btn-info">
<i class="fab fa-github"></i> Download on GitHub
</a>
</div>
</div>
</div>
</div>
<!-- Nintendo Wii U -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-code"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/GaryOderNichts/moonlight-wiiu" target="_blank" class="text-white text-decoration-none">
Nintendo Wii U
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-warning rounded-pill">Community</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/GaryOderNichts/moonlight-wiiu#quick-start" target="_blank" class="btn btn-info">
<i class="fas fa-download"></i> Download
</a>
</div>
</div>
</div>
</div>
<!-- LG webOS TV -->
<div class="col-md-6 col-lg-4 mb-5">
<div class="card bg-dark-gray text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<div class="icon text-white">
<i class="fa-fw fa-2x fas fa-code"></i>
</div>
<div class="ms-3">
<h5 class="fw-bolder mb-0">
<a href="https://github.com/mariotaku/moonlight-tv" target="_blank" class="text-white text-decoration-none">
LG webOS TV
</a>
</h5>
</div>
<div class="ms-auto">
<span class="badge text-bg-warning rounded-pill">Community</span>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<div class="pb-3">
<a href="https://github.com/mariotaku/moonlight-tv#download" target="_blank" class="btn btn-info">
<i class="fas fa-download"></i> Download
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- More cards -->
<div class="container py-5 px-auto">
<div class="container col-md-10">
<!-- Docs section -->
<section class="offset-anchor py-4" id="Docs">
<div class="card bg-dark text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<i class="fa-fw fa-2x fas fa-book"></i>
<div class="ms-3">
<h2 class="fw-bolder mb-0">Documentation</h2>
<p class="mb-0">
Read the documentation to learn how to install, use, and configure Sunshine.
</p>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<a class="btn btn-outline-light me-3 mb-3" href="https://docs.lizardbyte.dev/projects/sunshine" target="_blank">
<i class="fa-fw fas fa-book"></i>
Read the Docs
</a>
</div>
</div>
</section>
<!-- Download section -->
<section class="offset-anchor py-4" id="Download">
<div class="card bg-dark text-white rounded-0">
<div class="card-body p-4">
<div class="d-flex align-items-center">
<i class="fa-fw fa-2x fas fa-download"></i>
<div class="ms-3">
<h2 class="fw-bolder mb-0">Download</h2>
<p class="mb-0">
Download Sunshine for your platform.
</p>
</div>
</div>
</div>
<div class="card-footer p-3 ms-3">
<a class="latest-button btn btn-outline-light me-3 mb-3 d-none" href="https://github.com/LizardByte/Sunshine/releases/latest" target="_blank">
<i class="fa-fw fab fa-github"></i>
Latest: <span id="latest-version"></span>
</a>
<a class="beta-button btn btn-outline-light me-3 mb-3 d-none" href="#" target="_blank">
<i class="fa-fw fas fa-flask"></i>
Beta: <span id="beta-version"></span>
</a>
<a class="btn btn-outline-light me-3 mb-3" href="https://github.com/LizardByte/pacman-repo" target="_blank">
<i class="fa-fw fab fa-linux"></i>
ArchLinux
</a>
<a class="btn btn-outline-light me-3 mb-3" href="https://hub.docker.com/r/lizardbyte/sunshine" target="_blank">
<i class="fa-fw fab fa-docker"></i>
Docker
</a>
<a class="btn btn-outline-light me-3 mb-3" href="https://github.com/LizardByte/homebrew-homebrew" target="_blank">
<i class="fa-fw fas fa-beer-mug-empty"></i>
Homebrew
</a>
</div>
</div>
</section>
</div>
</div>
<!-- Donate section -->
<section class="offset-anchor bg-dark p-5" id="Donate">
<div class="container mb-5 shadow border-0 bg-dark-gray rounded-0 col-md-7">
<div class="d-table-row g-0 text-white">
<div class="d-table-cell px-3 align-middle text-center">
<h1>
<i class="fa-fw fas fa-coins"></i>
</h1>
</div>
<div class="col-sm-auto border-white my-3 px-3 py-2 border-start">
<div class="container">
<h4 class="card-title mb-3 fw-bolder">Donate</h4>
</div>
<a
class="text-decoration-none"
href="https://github.com/sponsors/LizardByte"
target="_blank">
<img class="m-3"
alt="GitHub Sponsors"
src="https://img.shields.io/github/sponsors/lizardbyte?label=Github%20Sponsors&style=for-the-badge&color=green&logo=githubsponsors"
>
</a>
<a
class="text-decoration-none"
href="https://mee6.xyz/m/804382334370578482"
target="_blank">
<img class="m-3"
alt="MEE6"
src="https://img.shields.io/static/v1?style=for-the-badge&label=MEE6&message=Donate&color=green&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAklEQVR4AewaftIAAAG6SURBVJXBPWgTARiA4fe7H/WKCGoo0qAcIhUJHCSnoIgZYiq4ugk6BEuEimRxk04SwcEhU0pMDY5xcnSK0N22XCexYHSxkpBCB69gc58ntHCcEerzCCmVIMyjVEFLCi4xgT5ID6HV8Zw1EoR9D4LQMQwav8Y6DwiTqW1KO4qoLXtOSMwkNr8ROmON3kfKHUD4N4kUX9Eblx8tdleb9T2D2Fi1AVLk0KQ4Vm0Qk0oQ5lX1IyD8HxUR31DVKiDsU43YHQ1I290eolFEgqhq1RAokbDx+iXd8nl2vm1y4OfgO2/nLrC+9JwkgZKl4JIwc/Um4WCLqeksB46dzJC7/5izxdskKbgWKZlcgUyuQJJh2fi1Z0xiCfQVZkkJhz/Y+fqZP+zjJzh10SNNoG8p9IBZUkafAjaf3OW0bTO6doviizekKfQsEWmp6kNASMhen6O89I6Z7QEfrpSJ+IuKSMtcb9a38gtPs4BPyvDMOb64l9AjR0kTod3xpl4ZxEyRGugKh6YrpkiNmElstVnf8xcWu5Yp05FSAITJ1DalLci9Zc8JiQkplSDMo1RBSwouMYE+SA+h1fGcNRJ+A8xgoQAar6IXAAAAAElFTkSuQmCC"
>
</a>
<a
class="text-decoration-none"
href="https://www.patreon.com/LizardByte"
target="_blank">
<img class="m-3"
alt="Patreon"
src="https://img.shields.io/badge/dynamic/json?color=green&label=Patreon&style=for-the-badge&query=patron_count&url=https%3A%2F%2Fapp.lizardbyte.dev%2Funo%2Fpatreon%2FLizardByte.json&logo=patreon"
>
</a>
<a
class="text-decoration-none"
href="https://www.paypal.com/paypalme/ReenigneArcher"
target="_blank">
<img class="m-3"
alt="PayPal"
src="https://img.shields.io/static/v1?style=for-the-badge&label=PayPal&message=Donate&color=green&logo=paypal"
>
</a>
</div>
</div>
</div>
</section>
<!-- Support -->
<section class="offset-anchor bg-dark p-5" id="Support">
<div class="col-md-7 mx-auto mb-5">
<div class="container shadow border-0 bg-primary rounded-0">
<div class="d-table-row g-0 text-white">
<div class="d-table-cell px-4 align-middle text-center">
<div class="fs-3 fw-bold text-white">Support Center</div>
<div class="text-white">Find answers and ask questions.</div>
</div>
<div class="col-sm-auto border-white my-3 px-4 py-2 border-start">
<p class="card-text">
<em>The one who knows all the answers has not been asked all the questions.</em>
Confucius.
</p>
<div class="input-group mb-2">
<a href="support">
<button class="btn btn-outline-light rounded-0"
id="button-support"
type="button"
>Support</button>
</a>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- Footer-->
<footer class="bg-dark py-4 mt-auto" id="footer-container"></footer>
<!-- Audio player bottom navbar -->
<nav id="player-navbar"></nav>
<!-- Bootstrap core JS-->
<script src="https://app.lizardbyte.dev/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<!-- Get navbar -->
<script src="https://app.lizardbyte.dev/js/navbar.js"></script>
<!-- Get footer -->
<script src="https://app.lizardbyte.dev/js/footer.js"></script>
<!-- Discord WidgetBot -->
<script src="https://app.lizardbyte.dev/js/discord.js"></script>
<!-- TODO: Move this to website repo, and make it accept arguments for the repo name -->
<script>
// Fetch the releases from the GitHub API
fetch('https://api.github.com/repos/LizardByte/Sunshine/releases')
.then(response => response.json())
.then(data => {
// Filter the releases to get only the pre-releases
const preReleases = data.filter(release => release.prerelease);
// Filter the releases to get only the stable releases
const stableReleases = data.filter(release => !release.prerelease);
// If there are no stable releases, hide the latest download button
if (stableReleases.length === 0) {
document.querySelector('.latest-button').classList.add('d-none');
} else {
// Show the latest download button
document.querySelector('.latest-button').classList.remove('d-none');
// Get the latest stable release
const latestStableRelease = stableReleases[0];
document.querySelector('#latest-version').textContent = latestStableRelease.tag_name;
// If there is a pre-release, update the href attribute of the anchor tag
if (preReleases.length > 0) {
const latestPreRelease = preReleases[0];
// Compare the date of the latest pre-release with the date of the latest stable release
const preReleaseDate = new Date(latestPreRelease.published_at);
const stableReleaseDate = new Date(latestStableRelease.published_at);
// If the pre-release is newer, update the href attribute of the anchor tag
if (preReleaseDate > stableReleaseDate) {
document.querySelector('.beta-button').href = latestPreRelease.html_url;
document.querySelector('#beta-version').textContent = latestPreRelease.tag_name;
document.querySelector('.beta-button').classList.remove('d-none');
} else {
// If the pre-release is older, hide the button
document.querySelector('.beta-button').classList.add('d-none');
}
} else {
// If there is no pre-release, hide the button
document.querySelector('.beta-button').classList.add('d-none');
}
}
});
</script>
</body>
</html>

View File

@ -1,93 +0,0 @@
#!/bin/bash
# custom AppRun for Sunshine AppImage
# path of the extracted AppRun
HERE="$(dirname "$(readlink -f "${0}")")"
SUNSHINE_PATH=/usr/bin/sunshine
SUNSHINE_BIN_HERE=$HERE/usr/bin/sunshine
SUNSHINE_SHARE_HERE=$HERE/usr/share/sunshine
# Set APPDIR when running directly from the AppDir:
if [ -z "$APPDIR" ]; then
ARGV0="AppRun"
fi
cd "$HERE" || exit 1
function help() {
echo "
------------------------------
Sunshine AppImage package.
------------------------------
sunshine.AppImage options
------------------------
Usage: $ARGV0 --help, -h
------ # This message
$ARGV0 --install, -i
# Install input rules sunshine.service files. Restart required.
$ARGV0 --remove, -r
# Remove input rules sunshine.service files.
$ARGV0 --appimage-help
# Show available AppImage options
sunshine options
----------------
"
# print sunshine binary help, replacing the sunshine command in usage statement
"$SUNSHINE_BIN_HERE" --help | sed -e "s#$SUNSHINE_BIN_HERE#$ARGV0#g"
}
function install() {
# user input rules
# shellcheck disable=SC2002
cat "$SUNSHINE_SHARE_HERE/udev/rules.d/60-sunshine.rules" | sudo tee /etc/udev/rules.d/60-sunshine.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --property-match=DEVNAME=/dev/uinput
sudo udevadm trigger --property-match=DEVNAME=/dev/uhid
# sunshine service
mkdir -p ~/.config/systemd/user
cp -r "$SUNSHINE_SHARE_HERE/systemd/user/" ~/.config/systemd/
# patch service executable path
sed -i -e "s#$SUNSHINE_PATH#$(readlink -f $ARGV0)#g" ~/.config/systemd/user/sunshine.service
# setcap
sudo setcap cap_sys_admin+p "$(readlink -f "$SUNSHINE_BIN_HERE")"
}
function remove() {
# remove input rules
sudo rm -f /etc/udev/rules.d/60-sunshine.rules
# remove service
sudo rm -f ~/.config/systemd/user/sunshine.service
}
# process arguments
if [ "x$1" == "xhelp" ] || [ "x$1" == "x--help" ] || [ "x$1" == "x-h" ] ; then
help
exit $?
fi
if [ "x$1" == "xinstall" ] || [ "x$1" == "x--install" ] || [ "x$1" == "x-i" ] ; then
install
exit $?
fi
if [ "x$1" == "xremove" ] || [ "x$1" == "x--remove" ] || [ "x$1" == "x-r" ] ; then
remove
exit $?
fi
# create config directory if it doesn't exist
# https://github.com/LizardByte/Sunshine/issues/324
mkdir -p ~/.config/sunshine
# run sunshine
"$SUNSHINE_BIN_HERE" $@

View File

@ -1,13 +0,0 @@
[Desktop Entry]
Type=Application
Name=@PROJECT_NAME@
Exec=sunshine
Version=1.0
Comment=@PROJECT_DESCRIPTION@
Icon=sunshine
Keywords=gamestream;stream;moonlight;remote play;
Categories=AudioVideo;Network;RemoteAccess;
Terminal=true
X-AppImage-Name=sunshine
X-AppImage-Version=@PROJECT_VERSION@
X-AppImage-Arch=x86_64

View File

@ -1,104 +0,0 @@
# Edit on github: https://github.com/LizardByte/Sunshine/blob/master/packaging/linux/Arch/PKGBUILD
# Reference: https://wiki.archlinux.org/title/PKGBUILD
pkgname='sunshine'
pkgver=@PROJECT_VERSION@@SUNSHINE_SUB_VERSION@
pkgrel=1
pkgdesc="@PROJECT_DESCRIPTION@"
arch=('x86_64' 'aarch64')
url=@PROJECT_HOMEPAGE_URL@
license=('GPL-3.0-only')
install=sunshine.install
_gcc_version=13
depends=(
'avahi'
'curl'
'libayatana-appindicator'
'libcap'
'libdrm'
'libevdev'
'libmfx'
'libnotify'
'libpulse'
'libva'
'libvdpau'
'libx11'
'libxcb'
'libxfixes'
'libxrandr'
'libxtst'
'miniupnpc'
'numactl'
'openssl'
'opus'
'udev'
)
makedepends=(
'cmake'
'cuda'
'doxygen'
"gcc${_gcc_version}"
'graphviz'
'git'
'make'
'nodejs'
'npm'
)
optdepends=(
'cuda: Nvidia GPU encoding support'
'libva-mesa-driver: AMD GPU encoding support'
'xorg-server-xvfb: Virtual X server for headless testing'
)
provides=()
conflicts=()
source=("$pkgname::git+@GITHUB_CLONE_URL@#commit=@GITHUB_COMMIT@")
sha256sums=('SKIP')
prepare() {
cd "$pkgname"
git submodule update --recursive --init
}
build() {
export BRANCH="@GITHUB_BRANCH@"
export BUILD_VERSION="@GITHUB_BUILD_VERSION@"
export COMMIT="@GITHUB_COMMIT@"
export CC="gcc-${_gcc_version}"
export CXX="g++-${_gcc_version}"
export CFLAGS="${CFLAGS/-Werror=format-security/}"
export CXXFLAGS="${CXXFLAGS/-Werror=format-security/}"
cmake \
-S "$pkgname" \
-B build \
-Wno-dev \
-D BUILD_WERROR=ON \
-D CMAKE_INSTALL_PREFIX=/usr \
-D SUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-D SUNSHINE_ASSETS_DIR="share/sunshine" \
-D SUNSHINE_PUBLSIHER_NAME='LizardByte' \
-D SUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \
-D SUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
make -C build
}
check() {
export CC="gcc-${_gcc_version}"
export CXX="g++-${_gcc_version}"
cd "${srcdir}/build/tests"
./test_sunshine --gtest_color=yes
}
package() {
make -C build install DESTDIR="$pkgdir"
}

View File

@ -1,22 +0,0 @@
do_setcap() {
setcap cap_sys_admin+p $(readlink -f $(which sunshine))
}
do_udev_reload() {
udevadm control --reload-rules
udevadm trigger --property-match=DEVNAME=/dev/uinput
udevadm trigger --property-match=DEVNAME=/dev/uhid
modprobe uinput || true
modprobe uhid || true
}
post_install() {
do_setcap
do_udev_reload
}
post_upgrade() {
do_setcap
do_udev_reload
}

View File

@ -1,11 +0,0 @@
{
"env": {
"PATH": "$(PATH):$(HOME)/.local/bin"
},
"apps": [
{
"name": "Desktop",
"image-path": "desktop.png"
}
]
}

@ -1 +0,0 @@
Subproject commit d88a9156b91eef64ecf1a313c868f1401c4bb39b

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>@PROJECT_FQDN@</id>
<name>@CMAKE_PROJECT_NAME@</name>
<summary>@PROJECT_BRIEF_DESCRIPTION@</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>@PROJECT_LICENSE@</project_license>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
<control>gamepad</control>
</supports>
<url type="bugtracker">https://github.com/LizardByte/Sunshine/issues</url>
<url type="homepage">@PROJECT_HOMEPAGE_URL@</url>
<url type="donation">https://app.lizardbyte.dev/#Donate</url>
<url type="contact">https://app.lizardbyte.dev/support</url>
<url type="translate">https://translate.lizardbyte.dev</url>
<url type="contribute">https://docs.lizardbyte.dev</url>
<url type="vcs-browser">https://github.com/LizardByte/Sunshine</url>
<description>
<p>
@PROJECT_LONG_DESCRIPTION@
</p>
<p>NOTE: Allow Sunshine Virtual Input (Required)</p>
<p>sudo chown $USER /dev/uinput &amp;&amp; echo 'KERNEL=="uinput", SUBSYSTEM=="misc",
OPTIONS+="static_node=uinput", TAG+="uaccess"' | sudo tee
/etc/udev/rules.d/60-sunshine-input.rules</p>
<p>NOTE: Sunshine uses a self-signed certificate. The web browser will report it as not secure,
but it is safe.</p>
<p>NOTE: KMS Grab (Optional)</p>
<p>sudo -i PULSE_SERVER=unix:$(pactl info | awk '/Server String/{print$3}')
flatpak run @PROJECT_FQDN@</p>
</description>
<releases>
<release version="0.23.1" date="2024-04-21"/>
<release version="0.23.0" date="2024-04-06"/>
<release version="0.22.2" date="2024-03-15"/>
<release version="0.22.1" date="2024-03-13"/>
<release version="0.22.0" date="2024-03-04"/>
<release version="0.21.0" date="2023-10-15"/>
<release version="0.20.0" date="2023-05-29"/>
<release version="0.19.1" date="2023-03-30"/>
</releases>
<developer_name>LizardByte</developer_name>
<screenshots>
<screenshot type="default">
<image>https://app.lizardbyte.dev/Sunshine/assets/images/AdobeStock_305732536_1920x1280.jpg</image>
<caption>Sunshine</caption>
</screenshot>
</screenshots>
<content_rating type="oars-1.0">
<content_attribute id="language-profanity">moderate</content_attribute>
<content_attribute id="language-humor">mild</content_attribute>
<content_attribute id="money-purchasing">mild</content_attribute>
</content_rating>
<launchable type="desktop-id">@PROJECT_FQDN@.desktop</launchable>
</component>

View File

@ -1,93 +0,0 @@
---
app-id: "@PROJECT_FQDN@"
runtime: org.freedesktop.Platform
runtime-version: "22.08"
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.node18
- org.freedesktop.Sdk.Extension.vala
command: sunshine
separate-locales: false
finish-args:
- --device=all # access all devices
- --env=PULSE_PROP_media.category=Manager # allow sunshine to manage audio sinks
- --env=SUNSHINE_MIGRATE_CONFIG=1 # migrate config files to the new location
- --filesystem=home # need to save files in user's home directory
- --share=ipc # required for X11 shared memory extension
- --share=network # access network
- --socket=pulseaudio # play sounds using pulseaudio
- --socket=wayland # show windows using Wayland
- --socket=fallback-x11 # show windows using X11
- --system-talk-name=org.freedesktop.Avahi # talk to avahi on the system bus
- --talk-name=org.freedesktop.Flatpak # talk to flatpak on the session bus
cleanup:
- /include
- /lib/cmake
- /lib/pkgconfig
- /lib/*.la
- /lib/*.a
- /share/man
build-options:
append-path: /usr/lib/sdk/vala/bin
prepend-ld-library-path: /usr/lib/sdk/vala/lib
modules:
# Test dependencies
- "modules/xvfb/xvfb.json"
# Runtime dependencies
- shared-modules/libappindicator/libappindicator-gtk3-12.10.json
- "modules/avahi.json"
- "modules/libevdev.json"
- "modules/libnotify.json"
- "modules/miniupnpc.json"
- "modules/numactl.json" # TODO: is this still needed?
# Caching is configured until here, not including CUDA, since it is too large for GitHub cache
- "modules/cuda.json"
- name: sunshine
disabled: false
buildsystem: cmake-ninja
no-make-install: false
builddir: true
build-options:
append-path: /usr/lib/sdk/node18/bin
build-args:
- --share=network
test-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:
- -DBUILD_DOCS=OFF
- -DBUILD_WERROR=ON
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_CUDA_COMPILER=/app/cuda/bin/nvcc
- -DSUNSHINE_ASSETS_DIR=share/sunshine
- -DSUNSHINE_BUILD_FLATPAK=ON
- -DSUNSHINE_EXECUTABLE_PATH=/app/bin/sunshine
- -DSUNSHINE_ENABLE_WAYLAND=ON
- -DSUNSHINE_ENABLE_X11=ON
- -DSUNSHINE_ENABLE_DRM=ON
- -DSUNSHINE_ENABLE_CUDA=ON
- -DSUNSHINE_PUBLSIHER_NAME='LizardByte'
-DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev'
-DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
sources:
- type: git
url: "@GITHUB_CLONE_URL@"
commit: "@GITHUB_COMMIT@"
post-install:
- install -D $FLATPAK_BUILDER_BUILDDIR/packaging/linux/flatpak/scripts/* /app/bin
- install -D $FLATPAK_BUILDER_BUILDDIR/packaging/linux/flatpak/apps.json /app/share/sunshine/apps.json
run-tests: true
test-rule: "" # empty to disable
test-commands:
- xvfb-run tests/test_sunshine --gtest_color=yes

View File

@ -1,9 +0,0 @@
{
"errors": [
"finish-args-flatpak-spawn-access"
],
"info": [
"finish-args-flatpak-spawn-access: finish-args has a talk-name access for org.freedesktop.Flatpak"
],
"message": "Please consult the documentation at https://docs.flathub.org/docs/for-app-authors/linter"
}

View File

@ -1,11 +0,0 @@
{
"errors": [
"appstream-missing-screenshots",
"finish-args-flatpak-spawn-access"
],
"info": [
"appstream-missing-screenshots: Catalogue file has no screenshots. Please check if screenshot URLs are reachable",
"finish-args-flatpak-spawn-access: finish-args has a talk-name access for org.freedesktop.Flatpak"
],
"message": "Please consult the documentation at https://docs.flathub.org/docs/for-app-authors/linter"
}

View File

@ -1,53 +0,0 @@
{
"name": "avahi",
"cleanup": [
"/bin",
"/lib/avahi",
"/share"
],
"config-opts": [
"--with-distro=none",
"--disable-gobject",
"--disable-introspection",
"--disable-qt3",
"--disable-qt4",
"--disable-qt5",
"--disable-gtk",
"--disable-core-docs",
"--disable-manpages",
"--disable-libdaemon",
"--disable-python",
"--disable-pygobject",
"--disable-mono",
"--disable-monodoc",
"--disable-autoipd",
"--disable-doxygen-doc",
"--disable-doxygen-dot",
"--disable-doxygen-xml",
"--disable-doxygen-html",
"--disable-manpages",
"--disable-xmltoman",
"--disable-libevent"
],
"sources": [
{
"type": "git",
"url": "https://salsa.debian.org/utopia-team/avahi.git",
"commit": "1412c015d348166d58ea9c192239b00769eae24e",
"tag": "debian/0.8-13",
"x-checker-data": {
"type": "git",
"tag-pattern": "^debian\\/(\\d.+)$",
"versions": {
"<": "0.9"
}
}
},
{
"type": "shell",
"commands": [
"autoreconf -ivf"
]
}
]
}

View File

@ -1,36 +0,0 @@
{
"name": "cuda",
"build-options": {
"no_debuginfo": true
},
"buildsystem": "simple",
"cleanup": [
"*"
],
"build-commands": [
"chmod u+x ./cuda.run",
"./cuda.run --silent --toolkit --toolkitpath=$FLATPAK_DEST/cuda --no-opengl-libs --no-man-page --no-drm --tmpdir=$FLATPAK_BUILDER_BUILDDIR",
"rm -r $FLATPAK_DEST/cuda/nsight-systems-*",
"rm ./cuda.run"
],
"sources": [
{
"type": "file",
"only-arches": [
"x86_64"
],
"url": "https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda_12.0.0_525.60.13_linux.run",
"sha256": "905e9b9516900839fb76064719db752439f38b8cb730b49335d8bd53ddfad392",
"dest-filename": "cuda.run"
},
{
"type": "file",
"only-arches": [
"aarch64"
],
"url": "https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda_12.0.0_525.60.13_linux_sbsa.run",
"sha256": "cd13e9c65d4c8f895a968706f46064d536be09f9706bce081cc864b7e4fa4544",
"dest-filename": "cuda.run"
}
]
}

View File

@ -1,26 +0,0 @@
{
"name": "libevdev",
"buildsystem": "meson",
"config-opts": [
"-Ddocumentation=disabled",
"-Dtests=disabled"
],
"cleanup": [
"/bin"
],
"sources": [
{
"type": "git",
"url": "https://salsa.debian.org/debian/libevdev.git",
"commit": "1aa7baa233d6df4cee6a66fbc61bb5ffc8b6e88d",
"tag": "debian/1.13.0+dfsg-1",
"x-checker-data": {
"type": "git",
"tag-pattern": "^debian\\/(\\d.\\d+\\.\\d+)",
"versions": {
"<": "1.13.1"
}
}
}
]
}

View File

@ -1,26 +0,0 @@
{
"name": "libnotify",
"buildsystem": "meson",
"config-opts": [
"-Dtests=false",
"-Dintrospection=disabled",
"-Dman=false",
"-Dgtk_doc=false",
"-Ddocbook_docs=disabled"
],
"sources": [
{
"type": "git",
"url": "https://salsa.debian.org/gnome-team/libnotify.git",
"commit": "ccf2f62ef0a4b264dd4eff32cab70a3e213ceb1a",
"tag": "debian/0.8.1-1",
"x-checker-data": {
"type": "git",
"tag-pattern": "^debian\\/(\\d.+)$",
"versions": {
"<": "0.8.2"
}
}
}
]
}

View File

@ -1,25 +0,0 @@
{
"name": "miniupnpc",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DUPNPC_BUILD_SAMPLE=OFF",
"-DUPNPC_BUILD_SHARED=ON",
"-DUPNPC_BUILD_TESTS=OFF"
],
"sources": [
{
"type": "git",
"url": "https://salsa.debian.org/miniupnp-team/miniupnpc.git",
"commit": "c5fe3aa794e92a503cecec6a4071eb6d310b4e42",
"tag": "debian/2.2.4-1",
"x-checker-data": {
"type": "git",
"tag-pattern": "^debian\\/(\\d.+)$",
"versions": {
"<": "2.2.5"
}
}
}
]
}

View File

@ -1,22 +0,0 @@
{
"name": "numactl",
"buildsystem": "autotools",
"cleanup": [
"/bin"
],
"sources": [
{
"type": "git",
"url": "https://salsa.debian.org/debian/numactl.git",
"commit": "640bb34497702f9aaeb8af1b491f32b91d03ec80",
"tag": "debian/2.0.16-1",
"x-checker-data": {
"type": "git",
"tag-pattern": "^debian\\/(\\d.+)$",
"versions": {
"<": "2.0.17"
}
}
}
]
}

View File

@ -1,191 +0,0 @@
#!/bin/sh
# This script starts an instance of Xvfb, the "fake" X server, runs a command
# with that server available, and kills the X server when done. The return
# value of the command becomes the return value of this script.
#
# If anyone is using this to build a Debian package, make sure the package
# Build-Depends on xvfb and xauth.
set -e
PROGNAME=xvfb-run
SERVERNUM=99
AUTHFILE=
ERRORFILE=/dev/null
XVFBARGS="-screen 0 1280x1024x24"
LISTENTCP="-nolisten tcp"
XAUTHPROTO=.
# Query the terminal to establish a default number of columns to use for
# displaying messages to the user. This is used only as a fallback in the event
# the COLUMNS variable is not set. ($COLUMNS can react to SIGWINCH while the
# script is running, and this cannot, only being calculated once.)
DEFCOLUMNS=$(stty size 2>/dev/null | awk '{print $2}') || true
case "$DEFCOLUMNS" in
*[!0-9]*|'') DEFCOLUMNS=80 ;;
esac
# Display a message, wrapping lines at the terminal width.
message () {
echo "$PROGNAME: $*" | fmt -t -w ${COLUMNS:-$DEFCOLUMNS}
}
# Display an error message.
error () {
message "error: $*" >&2
}
# Display a usage message.
usage () {
if [ -n "$*" ]; then
message "usage error: $*"
fi
cat <<EOF
Usage: $PROGNAME [OPTION ...] COMMAND
Run COMMAND (usually an X client) in a virtual X server environment.
Options:
-a --auto-servernum try to get a free server number, starting at
--server-num
-e FILE --error-file=FILE file used to store xauth errors and Xvfb
output (default: $ERRORFILE)
-f FILE --auth-file=FILE file used to store auth cookie
(default: ./.Xauthority)
-h --help display this usage message and exit
-n NUM --server-num=NUM server number to use (default: $SERVERNUM)
-l --listen-tcp enable TCP port listening in the X server
-p PROTO --xauth-protocol=PROTO X authority protocol name to use
(default: xauth command's default)
-s ARGS --server-args=ARGS arguments (other than server number and
"-nolisten tcp") to pass to the Xvfb server
(default: "$XVFBARGS")
EOF
}
# Find a free server number by looking at .X*-lock files in /tmp.
find_free_servernum() {
# Sadly, the "local" keyword is not POSIX. Leave the next line commented in
# the hope Debian Policy eventually changes to allow it in /bin/sh scripts
# anyway.
#local i
i=$SERVERNUM
while [ -f /tmp/.X$i-lock ]; do
i=$(($i + 1))
done
echo $i
}
# Clean up files
clean_up() {
if [ -e "$AUTHFILE" ]; then
XAUTHORITY=$AUTHFILE xauth remove ":$SERVERNUM" >>"$ERRORFILE" 2>&1
fi
if [ -n "$XVFB_RUN_TMPDIR" ]; then
if ! rm -r "$XVFB_RUN_TMPDIR"; then
error "problem while cleaning up temporary directory"
exit 5
fi
fi
if [ -n "$XVFBPID" ]; then
kill "$XVFBPID" >>"$ERRORFILE" 2>&1
fi
}
# Parse the command line.
ARGS=$(getopt --options +ae:f:hn:lp:s:w: \
--long auto-servernum,error-file:,auth-file:,help,server-num:,listen-tcp,xauth-protocol:,server-args:,wait: \
--name "$PROGNAME" -- "$@")
GETOPT_STATUS=$?
if [ $GETOPT_STATUS -ne 0 ]; then
error "internal error; getopt exited with status $GETOPT_STATUS"
exit 6
fi
eval set -- "$ARGS"
while :; do
case "$1" in
-a|--auto-servernum) SERVERNUM=$(find_free_servernum); AUTONUM="yes" ;;
-e|--error-file) ERRORFILE="$2"; shift ;;
-f|--auth-file) AUTHFILE="$2"; shift ;;
-h|--help) SHOWHELP="yes" ;;
-n|--server-num) SERVERNUM="$2"; shift ;;
-l|--listen-tcp) LISTENTCP="" ;;
-p|--xauth-protocol) XAUTHPROTO="$2"; shift ;;
-s|--server-args) XVFBARGS="$2"; shift ;;
-w|--wait) shift ;;
--) shift; break ;;
*) error "internal error; getopt permitted \"$1\" unexpectedly"
exit 6
;;
esac
shift
done
if [ "$SHOWHELP" ]; then
usage
exit 0
fi
if [ -z "$*" ]; then
usage "need a command to run" >&2
exit 2
fi
if ! command -v xauth >/dev/null; then
error "xauth command not found"
exit 3
fi
# tidy up after ourselves
trap clean_up EXIT
# If the user did not specify an X authorization file to use, set up a temporary
# directory to house one.
if [ -z "$AUTHFILE" ]; then
XVFB_RUN_TMPDIR="$(mktemp -d -t $PROGNAME.XXXXXX)"
AUTHFILE="$XVFB_RUN_TMPDIR/Xauthority"
# Create empty file to avoid xauth warning
touch "$AUTHFILE"
fi
# Start Xvfb.
MCOOKIE=$(mcookie)
tries=10
while [ $tries -gt 0 ]; do
tries=$(( $tries - 1 ))
XAUTHORITY=$AUTHFILE xauth source - << EOF >>"$ERRORFILE" 2>&1
add :$SERVERNUM $XAUTHPROTO $MCOOKIE
EOF
# handle SIGUSR1 so Xvfb knows to send a signal when it's ready to accept
# connections
trap : USR1
(trap '' USR1; exec Xvfb ":$SERVERNUM" $XVFBARGS $LISTENTCP -auth $AUTHFILE >>"$ERRORFILE" 2>&1) &
XVFBPID=$!
wait || :
if kill -0 $XVFBPID 2>/dev/null; then
break
elif [ -n "$AUTONUM" ]; then
# The display is in use so try another one (if '-a' was specified).
SERVERNUM=$((SERVERNUM + 1))
SERVERNUM=$(find_free_servernum)
continue
fi
error "Xvfb failed to start" >&2
XVFBPID=
exit 1
done
# Start the command and save its exit status.
set +e
DISPLAY=:$SERVERNUM XAUTHORITY=$AUTHFILE "$@"
RETVAL=$?
set -e
# Return the executed command's exit status.
exit $RETVAL
# vim:set ai et sts=4 sw=4 tw=80:

View File

@ -1,147 +0,0 @@
{
"name": "xvfb",
"buildsystem": "meson",
"config-opts": [
"-Dxorg=true",
"-Dxvfb=true",
"-Dhal=false"
],
"build-commands": [
"install -Dm755 ../xvfb-run /app/bin/xvfb-run"
],
"sources": [
{
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/xserver/-/archive/xorg-server-21.1.13/xserver-xorg-server-21.1.13.tar.bz2",
"sha256": "ee2bf6d65f4b111ce86ca817c3327dc1e70d9c958aa16876f2820caf7bf7cffa",
"x-checker-data": {
"type": "anitya",
"project-id": 5250,
"stable-only": true,
"url-template": "https://gitlab.freedesktop.org/xorg/xserver/-/archive/xorg-server-$version/xserver-xorg-server-$version.tar.bz2"
}
},
{
"type": "file",
"path": "xvfb-run"
}
],
"modules": [
{
"name": "libxcvt",
"buildsystem": "meson",
"sources": [
{
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/lib/libxcvt/-/archive/libxcvt-0.1.2/libxcvt-libxcvt-0.1.2.tar.bz2",
"sha256": "590e5a6da87ace7aa7857026b207a2c4d378620035441e20ea97efedd15d6d4a",
"x-checker-data": {
"type": "anitya",
"project-id": 235147,
"stable-only": true,
"url-template": "https://gitlab.freedesktop.org/xorg/lib/libxcvt/-/archive/libxcvt-$version/libxcvt-libxcvt-$version.tar.bz2"
}
}
]
},
{
"name": "libXmu",
"sources": [
{
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libXmu-1.2.1.tar.gz",
"sha256": "bf0902583dd1123856c11e0a5085bd3c6e9886fbbd44954464975fd7d52eb599",
"x-checker-data": {
"type": "anitya",
"project-id": 1785,
"stable-only": true,
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libXmu-$version.tar.gz"
}
}
]
},
{
"name": "libfontenc",
"sources": [
{
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libfontenc-1.1.8.tar.xz",
"sha256": "7b02c3d405236e0d86806b1de9d6868fe60c313628b38350b032914aa4fd14c6",
"x-checker-data": {
"type": "anitya",
"project-id": 1613,
"stable-only": true,
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libfontenc-$version.tar.xz"
}
}
]
},
{
"name": "libtirpc",
"config-opts": [
"--disable-gssapi"
],
"sources": [
{
"type": "archive",
"url": "https://downloads.sourceforge.net/sourceforge/libtirpc/libtirpc-1.3.4.tar.bz2",
"sha256": "1e0b0c7231c5fa122e06c0609a76723664d068b0dba3b8219b63e6340b347860",
"x-checker-data": {
"type": "anitya",
"project-id": 1740,
"stable-only": true,
"url-template": "https://downloads.sourceforge.net/sourceforge/libtirpc/libtirpc-$version.tar.bz2"
}
}
]
},
{
"name": "font-util",
"sources": [
{
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/font/font-util-1.4.1.tar.gz",
"sha256": "f029ae80cdd75d89bee7f7af61c21e07982adfb9f72344a158b99f91f77ef5ed",
"x-checker-data": {
"type": "anitya",
"project-id": 15055,
"stable-only": true,
"url-template": "https://xorg.freedesktop.org/archive/individual/font/font-util-$version.tar.gz"
}
}
]
},
{
"name": "xvfb-libXfont2",
"sources": [
{
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libXfont2-2.0.6.tar.gz",
"sha256": "a944df7b6837c8fa2067f6a5fc25d89b0acc4011cd0bc085106a03557fb502fc",
"x-checker-data": {
"type": "anitya",
"project-id": 17165,
"stable-only": true,
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libXfont2-$version.tar.gz"
}
}
]
},
{
"name": "xvfb-xauth",
"sources": [
{
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/app/xauth/-/archive/xauth-1.1.1/xauth-xauth-1.1.3.tar.bz2",
"sha256": "3cee16ebe9de0e85c62513f6d6353710407c8ebb1f855b18d03807c27d38a215",
"x-checker-data": {
"type": "anitya",
"project-id": 5253,
"stable-only": true,
"url-template": "https://gitlab.freedesktop.org/xorg/app/xauth/-/archive/xauth-1.1.1/xauth-xauth-$version.tar.bz2"
}
}
]
}
]
}

View File

@ -1,13 +0,0 @@
#!/bin/sh
# 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.
# Udev rule
UDEV=$(cat /app/share/sunshine/udev/rules.d/60-sunshine.rules)
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.

View File

@ -1,11 +0,0 @@
#!/bin/sh
# User Service
systemctl --user stop sunshine
rm $HOME/.config/systemd/user/sunshine.service
systemctl --user daemon-reload
echo Sunshine User Service has been removed.
# Udev rule
flatpak-spawn --host pkexec sh -c "rm /etc/udev/rules.d/60-sunshine.rules"
echo Mouse permission removed. Restart computer to take effect.

View File

@ -1,20 +0,0 @@
[Desktop Entry]
Type=Application
Name=@PROJECT_NAME@
Exec=@PROJECT_FQDN@
Version=1.0
Comment=@PROJECT_DESCRIPTION@
Icon=@SUNSHINE_DESKTOP_ICON@
Keywords=gamestream;stream;moonlight;remote play;
Categories=AudioVideo;Network;RemoteAccess;
Actions=RunInTerminal;KMS;
[Desktop Action RunInTerminal]
Name=Run in Terminal
Icon=application-x-executable
Exec=gio launch @CMAKE_INSTALL_FULL_DATAROOTDIR@/applications/@PROJECT_FQDN@_terminal.desktop
[Desktop Action KMS]
Name=Run in Terminal (KMS)
Icon=application-x-executable
Exec=gio launch @CMAKE_INSTALL_FULL_DATAROOTDIR@/applications/@PROJECT_FQDN@_kms.desktop

View File

@ -1,6 +0,0 @@
[Desktop Entry]
Name=@PROJECT_NAME@ (KMS)
Exec=sudo -i PULSE_SERVER=unix:$(pactl info | awk '/Server String/{print$3}') flatpak run @PROJECT_FQDN@
Terminal=true
Type=Application
NoDisplay=true

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>@PROJECT_NAME@.desktop</id>
<metadata_license>@PROJECT_LICENSE@</metadata_license>
<project_license>@PROJECT_LICENSE@</project_license>
<name>@PROJECT_NAME@</name>
<url type="homepage">@CMAKE_PROJECT_HOMEPAGE_URL@</url>
<summary>@PROJECT_BRIEF_DESCRIPTION@</summary>
<description>
<p>
@PROJECT_LONG_DESCRIPTION@
</p>
</description>
<screenshots>
<screenshot type="default">
<image>https://app.lizardbyte.dev/Sunshine/assets/images/AdobeStock_305732536_1920x1280.jpg</image>
<caption>Sunshine</caption>
</screenshot>
</screenshots>
</component>

View File

@ -1,15 +0,0 @@
[Desktop Entry]
Type=Application
Name=@PROJECT_NAME@
Exec=/usr/bin/env systemctl start --u sunshine
Version=1.0
Comment=@PROJECT_DESCRIPTION@
Icon=@SUNSHINE_DESKTOP_ICON@
Keywords=gamestream;stream;moonlight;remote play;
Categories=AudioVideo;Network;RemoteAccess;
Actions=RunInTerminal;
[Desktop Action RunInTerminal]
Name=Run in Terminal
Icon=application-x-executable
Exec=gio launch @CMAKE_INSTALL_FULL_DATAROOTDIR@/applications/sunshine_terminal.desktop

View File

@ -1,15 +0,0 @@
[Unit]
Description=@PROJECT_DESCRIPTION@
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
# Avoid starting Sunshine before the desktop is fully initialized.
ExecStartPre=/bin/sleep 5
@SUNSHINE_SERVICE_START_COMMAND@
@SUNSHINE_SERVICE_STOP_COMMAND@
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=xdg-desktop-autostart.target

View File

@ -1,6 +0,0 @@
[Desktop Entry]
Name=@PROJECT_NAME@
Exec=sunshine
Terminal=true
Type=Application
NoDisplay=true

View File

@ -1,77 +0,0 @@
# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
# initial PR into macports: https://github.com/macports/macports-ports/pull/15143
PortSystem 1.0
PortGroup cmake 1.1
PortGroup github 1.0
name @PROJECT_NAME@
version @PROJECT_VERSION@
revision 0
categories multimedia emulators games
platforms darwin
license GPL-3
maintainers @LizardByte
description @PROJECT_DESCRIPTION@
# long_description will not be split into multiple lines as it's configured by CMakeLists
long_description @PROJECT_LONG_DESCRIPTION@
homepage @PROJECT_HOMEPAGE_URL@
master_sites https://github.com/lizardbyte/sunshine/releases
compiler.cxx_standard 2017
fetch.type git
git.url @GITHUB_CLONE_URL@
git.branch @GITHUB_COMMIT@
post-fetch {
system -W ${worksrcpath} "${git.cmd} submodule update --init --recursive"
}
# https://guide.macports.org/chunked/reference.dependencies.html
depends_build-append port:doxygen \
port:graphviz \
port:npm9 \
port:pkgconfig
depends_lib port:curl \
port:libopus \
port:miniupnpc
configure.args -DBOOST_USE_STATIC=ON \
-DBUILD_WERROR=ON \
-DCMAKE_INSTALL_PREFIX=${prefix} \
-DSUNSHINE_ASSETS_DIR=etc/sunshine/assets \
-DSUNSHINE_PUBLSIHER_NAME='LizardByte' \
-DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \
-DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
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
startupitem.name ${name}
startupitem.netchange yes
platform darwin {
if { ${os.major} < 20 } {
# See: https://github.com/LizardByte/Sunshine/discussions/117#discussioncomment-2513494
notes-append "Port is limited to software encoding, when used with macOS releases prior to Big Sur."
}
}
notes-append "Run @PROJECT_NAME@ by executing 'sunshine <path to user config>', e.g. 'sunshine ~/sunshine.conf' "
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 --gtest_filter=-*HIDTest.*:-*DeathTest.*

View File

@ -1,157 +0,0 @@
require "language/node"
class @PROJECT_NAME@ < Formula
# conflicts_with "sunshine", because: "sunshine and sunshine-beta cannot be installed at the same time"
desc "@PROJECT_DESCRIPTION@"
homepage "@PROJECT_HOMEPAGE_URL@"
url "@GITHUB_CLONE_URL@",
tag: "@GITHUB_TAG@"
version "@FORMULA_VERSION@"
license all_of: ["GPL-3.0-only"]
head "@GITHUB_CLONE_URL@", branch: "@GITHUB_DEFAULT_BRANCH@"
# https://docs.brew.sh/Brew-Livecheck#githublatest-strategy-block
livecheck do
url :stable
regex(/^v?(\d+\.\d+\.\d+)$/i)
strategy :github_latest do |json, regex|
match = json["tag_name"]&.match(regex)
next if match.blank?
match[1]
end
end
option "with-docs-off", "Disable docs"
option "with-dynamic-boost", "Dynamically link Boost libraries"
option "without-dynamic-boost", "Statically link Boost libraries" # default option
depends_on "cmake" => :build
depends_on "doxygen" => :build
depends_on "graphviz" => :build
depends_on "node" => :build
depends_on "pkg-config" => :build
depends_on "curl"
depends_on "miniupnpc"
depends_on "openssl"
depends_on "opus"
depends_on "icu4c" => :recommended
on_linux do
depends_on "avahi"
depends_on "libcap"
depends_on "libdrm"
depends_on "libnotify"
depends_on "libva"
depends_on "libvdpau"
depends_on "libx11"
depends_on "libxcb"
depends_on "libxcursor"
depends_on "libxfixes"
depends_on "libxi"
depends_on "libxinerama"
depends_on "libxrandr"
depends_on "libxtst"
depends_on "numactl"
depends_on "pulseaudio"
depends_on "systemd"
depends_on "wayland"
end
def install
ENV["BRANCH"] = "@GITHUB_BRANCH@"
ENV["BUILD_VERSION"] = "@BUILD_VERSION@"
ENV["COMMIT"] = "@GITHUB_COMMIT@"
args = %W[
-DBUILD_WERROR=ON
-DCMAKE_INSTALL_PREFIX=#{prefix}
-DHOMEBREW_ALLOW_FETCHCONTENT=ON
-DOPENSSL_ROOT_DIR=#{Formula["openssl"].opt_prefix}
-DSUNSHINE_ASSETS_DIR=sunshine/assets
-DSUNSHINE_BUILD_HOMEBREW=ON
-DSUNSHINE_ENABLE_TRAY=OFF
-DSUNSHINE_PUBLSIHER_NAME='LizardByte'
-DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev'
-DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
]
if build.with? "docs-off"
ohai "Building docs: disabled"
args << "-DBUILD_DOCS=OFF"
else
ohai "Building docs: enabled"
args << "-DBUILD_DOCS=ON"
end
if build.without? "dynamic-boost"
args << "-DBOOST_USE_STATIC=ON"
ohai "Statically linking Boost libraries"
unless Formula["icu4c"].any_version_installed?
odie <<~EOS
icu4c must be installed to link against static Boost libraries,
either install icu4c or use brew install sunshine --with-dynamic-boost instead
EOS
end
ENV.append "CXXFLAGS", "-I#{Formula["icu4c"].opt_include}"
icu4c_lib_path = Formula["icu4c"].opt_lib.to_s
ENV.append "LDFLAGS", "-L#{icu4c_lib_path}"
ENV["LIBRARY_PATH"] = icu4c_lib_path
ohai "Linking against ICU libraries at: #{icu4c_lib_path}"
else
args << "-DBOOST_USE_STATIC=OFF"
ohai "Dynamically linking Boost libraries"
end
system "cmake", "-S", ".", "-B", "build", *std_cmake_args, *args
cd "build" do
system "make"
system "make", "install"
bin.install "tests/test_sunshine"
end
bin.install "src_assets/linux/misc/postinst" if OS.linux?
end
service do
run [opt_bin/"sunshine", "~/.config/sunshine/sunshine.conf"]
end
def caveats
caveats_message = <<~EOS
Thanks for installing @PROJECT_NAME@!
To get started, review the documentation at:
https://docs.lizardbyte.dev/projects/sunshine/en/latest/
EOS
if OS.linux?
caveats_message += <<~EOS
ATTENTION: To complete installation, you must run the following command:
`sudo #{bin}/postinst`
EOS
end
if OS.mac?
caveats_message += <<~EOS
Sunshine can only access microphones on macOS due to system limitations.
To stream system audio use "Soundflower" or "BlackHole".
Gamepads are not currently supported on macOS.
EOS
end
caveats_message
end
test do
# test that the binary runs at all
system bin/"sunshine", "--version"
# run the test suite
system bin/"test_sunshine", "--gtest_color=yes"
end
end

View File

@ -1,174 +0,0 @@
"""
..
_locale.py
Functions related to building, initializing, updating, and compiling localization translations.
Borrowed from RetroArcher.
"""
# standard imports
import argparse
import datetime
import os
import subprocess
project_name = 'Sunshine'
project_owner = 'LizardByte'
script_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(script_dir)
locale_dir = os.path.join(root_dir, 'locale')
project_dir = os.path.join(root_dir, 'src')
year = datetime.datetime.now().year
# target locales
target_locales = [
'de', # German
'en', # English
'en_GB', # English (United Kingdom)
'en_US', # English (United States)
'es', # Spanish
'fr', # French
'it', # Italian
'ja', # Japanese
'pt', # Portuguese
'ru', # Russian
'sv', # Swedish
'zh', # Chinese
]
def x_extract():
"""Executes `xgettext extraction` in subprocess."""
pot_filepath = os.path.join(locale_dir, f'{project_name.lower()}.po')
commands = [
'xgettext',
'--keyword=translate:1,1t',
'--keyword=translate:1c,2,2t',
'--keyword=translate:1,2,3t',
'--keyword=translate:1c,2,3,4t',
'--keyword=gettext:1',
'--keyword=pgettext:1c,2',
'--keyword=ngettext:1,2',
'--keyword=npgettext:1c,2,3',
f'--default-domain={project_name.lower()}',
f'--output={pot_filepath}',
'--language=C++',
'--boost',
'--from-code=utf-8',
'-F',
f'--msgid-bugs-address=github.com/{project_owner.lower()}/{project_name.lower()}',
f'--copyright-holder={project_owner}',
f'--package-name={project_name}',
'--package-version=v0'
]
extensions = ['cpp', 'h', 'm', 'mm']
# find input files
for root, dirs, files in os.walk(project_dir, topdown=True):
for name in files:
filename = os.path.join(root, name)
extension = filename.rsplit('.', 1)[-1]
if extension in extensions: # append input files
commands.append(filename)
print(commands)
subprocess.check_output(args=commands, cwd=root_dir)
try:
# fix header
body = ""
with open(file=pot_filepath, mode='r') as file:
for line in file.readlines():
if line != '"Language: \\n"\n': # do not include this line
if line == '# SOME DESCRIPTIVE TITLE.\n':
body += f'# Translations template for {project_name}.\n'
elif line.startswith('#') and 'YEAR' in line:
body += line.replace('YEAR', str(year))
elif line.startswith('#') and 'PACKAGE' in line:
body += line.replace('PACKAGE', project_name)
else:
body += line
# rewrite pot file with updated header
with open(file=pot_filepath, mode='w+') as file:
file.write(body)
except FileNotFoundError:
pass
def babel_init(locale_code: str):
"""Executes `pybabel init` in subprocess.
:param locale_code: str - locale code
"""
commands = [
'pybabel',
'init',
'-i', os.path.join(locale_dir, f'{project_name.lower()}.po'),
'-d', locale_dir,
'-D', project_name.lower(),
'-l', locale_code
]
print(commands)
subprocess.check_output(args=commands, cwd=root_dir)
def babel_update():
"""Executes `pybabel update` in subprocess."""
commands = [
'pybabel',
'update',
'-i', os.path.join(locale_dir, f'{project_name.lower()}.po'),
'-d', locale_dir,
'-D', project_name.lower(),
'--update-header-comment'
]
print(commands)
subprocess.check_output(args=commands, cwd=root_dir)
def babel_compile():
"""Executes `pybabel compile` in subprocess."""
commands = [
'pybabel',
'compile',
'-d', locale_dir,
'-D', project_name.lower()
]
print(commands)
subprocess.check_output(args=commands, cwd=root_dir)
if __name__ == '__main__':
# Set up and gather command line arguments
parser = argparse.ArgumentParser(
description='Script helps update locale translations. Translations must be done manually.')
parser.add_argument('--extract', action='store_true', help='Extract messages from c++ files.')
parser.add_argument('--init', action='store_true', help='Initialize any new locales specified in target locales.')
parser.add_argument('--update', action='store_true', help='Update existing locales.')
parser.add_argument('--compile', action='store_true', help='Compile translated locales.')
args = parser.parse_args()
if args.extract:
x_extract()
if args.init:
for locale_id in target_locales:
if not os.path.isdir(os.path.join(locale_dir, locale_id)):
babel_init(locale_code=locale_id)
if args.update:
babel_update()
if args.compile:
babel_compile()

View File

@ -1,61 +0,0 @@
#!/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

View File

@ -1,502 +0,0 @@
#!/bin/bash
set -e
# Default value for arguments
appimage_build=0
publisher_name="Third Party Publisher"
publisher_website=""
publisher_issue_url="https://app.lizardbyte.dev/support"
skip_cleanup=0
skip_cuda=0
skip_libva=0
skip_package=0
sudo_cmd="sudo"
ubuntu_test_repo=0
function _usage() {
local exit_code=$1
cat <<EOF
This script installs the dependencies and builds the project.
The script is intended to be run on a Debian-based or Fedora-based system.
Usage:
$0 [options]
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.
--publisher-name The name of the publisher (not developer) of the application.
--publisher-website The URL of the publisher's website.
--publisher-issue-url The URL of the publisher's support site or issue tracker.
If you provide a modified version of Sunshine, we kindly request that you use your own url.
--skip-cleanup Do not restore the original gcc alternatives, or the math-vector.h file.
--skip-cuda Skip CUDA installation.
--skip-libva Skip libva installation. This will automatically be enabled if passing --appimage-build.
--skip-package Skip creating DEB, or RPM package.
--ubuntu-test-repo Install ppa:ubuntu-toolchain-r/test repo on Ubuntu.
EOF
exit "$exit_code"
}
# Parse named arguments
while getopts ":hs-:" opt; do
case ${opt} in
h ) _usage 0 ;;
s ) sudo_cmd="" ;;
- )
case "${OPTARG}" in
help) _usage 0 ;;
appimage-build)
appimage_build=1
skip_libva=1
;;
publisher-name=*)
publisher_name="${OPTARG#*=}"
;;
publisher-website=*)
publisher_website="${OPTARG#*=}"
;;
publisher-issue-url=*)
publisher_issue_url="${OPTARG#*=}"
;;
skip-cleanup) skip_cleanup=1 ;;
skip-cuda) skip_cuda=1 ;;
skip-libva) skip_libva=1 ;;
skip-package) skip_package=1 ;;
sudo-off) sudo_cmd="" ;;
ubuntu-test-repo) ubuntu_test_repo=1 ;;
*)
echo "Invalid option: --${OPTARG}" 1>&2
_usage 1
;;
esac
;;
\? )
echo "Invalid option: -${OPTARG}" 1>&2
_usage 1
;;
esac
done
shift $((OPTIND -1))
# dependencies array to build out
dependencies=()
function add_debain_based_deps() {
dependencies+=(
"bison" # required if we need to compile doxygen
"build-essential"
"cmake"
"doxygen"
"flex" # required if we need to compile doxygen
"gcc-${gcc_version}"
"g++-${gcc_version}"
"git"
"graphviz"
"libcap-dev" # KMS
"libcurl4-openssl-dev"
"libdrm-dev" # KMS
"libevdev-dev"
"libminiupnpc-dev"
"libnotify-dev"
"libnuma-dev"
"libopus-dev"
"libpulse-dev"
"libssl-dev"
"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
"ninja-build"
"npm" # web-ui
"udev"
"wget" # necessary for cuda install with `run` file
"xvfb" # necessary for headless unit testing
)
if [ "$skip_libva" == 0 ]; then
dependencies+=(
"libva-dev" # VA-API
)
fi
}
function add_debain_deps() {
add_debain_based_deps
dependencies+=(
"libayatana-appindicator3-dev"
)
}
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_debain_based_deps
dependencies+=(
"libappindicator3-dev"
)
}
function add_fedora_deps() {
dependencies+=(
"cmake"
"doxygen"
"gcc"
"g++"
"git"
"graphviz"
"libappindicator-gtk3-devel"
"libcap-devel"
"libcurl-devel"
"libdrm-devel"
"libevdev-devel"
"libnotify-devel"
"libvdpau-devel"
"libX11-devel" # X11
"libxcb-devel" # X11
"libXcursor-devel" # X11
"libXfixes-devel" # X11
"libXi-devel" # X11
"libXinerama-devel" # X11
"libXrandr-devel" # X11
"libXtst-devel" # X11
"mesa-libGL-devel"
"miniupnpc-devel"
"ninja-build"
"npm"
"numactl-devel"
"openssl-devel"
"opus-devel"
"pulseaudio-libs-devel"
"rpm-build" # if you want to build an RPM binary package
"wget" # necessary for cuda install with `run` file
"which" # necessary for cuda install with `run` file
"xorg-x11-server-Xvfb" # necessary for headless unit testing
)
if [ "$skip_libva" == 0 ]; then
dependencies+=(
"libva-devel" # VA-API
)
fi
}
function install_cuda() {
# check if we need to install cuda
if [ -f "${build_dir}/cuda/bin/nvcc" ]; then
echo "cuda already installed"
return
fi
local cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
local cuda_suffix=""
if [ "$architecture" == "aarch64" ]; then
local cuda_suffix="_sbsa"
fi
if [ "$architecture" == "aarch64" ]; then
# we need to patch the math-vector.h file for aarch64 fedora
# back up /usr/include/bits/math-vector.h
math_vector_file=""
if [ "$distro" == "ubuntu" ] || [ "$version" == "24.04" ]; then
math_vector_file="/usr/include/aarch64-linux-gnu/bits/math-vector.h"
elif [ "$distro" == "fedora" ]; then
math_vector_file="/usr/include/bits/math-vector.h"
fi
if [ -n "$math_vector_file" ]; then
# patch headers https://bugs.launchpad.net/ubuntu/+source/mumax3/+bug/2032624
${sudo_cmd} cp "$math_vector_file" "$math_vector_file.bak"
${sudo_cmd} sed -i 's/__Float32x4_t/int/g' "$math_vector_file"
${sudo_cmd} sed -i 's/__Float64x2_t/int/g' "$math_vector_file"
${sudo_cmd} sed -i 's/__SVFloat32_t/float/g' "$math_vector_file"
${sudo_cmd} sed -i 's/__SVFloat64_t/float/g' "$math_vector_file"
${sudo_cmd} sed -i 's/__SVBool_t/int/g' "$math_vector_file"
fi
fi
local 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 "${build_dir}/cuda.run"
chmod a+x "${build_dir}/cuda.run"
"${build_dir}/cuda.run" --silent --toolkit --toolkitpath="${build_dir}/cuda" --no-opengl-libs --no-man-page --no-drm
rm "${build_dir}/cuda.run"
}
function check_version() {
local package_name=$1
local min_version=$2
local installed_version
echo "Checking if $package_name is installed and at least version $min_version"
if [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then
installed_version=$(dpkg -s "$package_name" 2>/dev/null | grep '^Version:' | awk '{print $2}')
elif [ "$distro" == "fedora" ]; then
installed_version=$(rpm -q --queryformat '%{VERSION}' "$package_name" 2>/dev/null)
else
echo "Unsupported Distro"
return 1
fi
if [ -z "$installed_version" ]; then
echo "Package not installed"
return 1
fi
if [ "$(printf '%s\n' "$installed_version" "$min_version" | sort -V | head -n1)" = "$min_version" ]; then
echo "$package_name version $installed_version is at least $min_version"
return 0
else
echo "$package_name version $installed_version is less than $min_version"
return 1
fi
}
function run_install() {
# prepare CMAKE args
cmake_args=(
"-B=build"
"-G=Ninja"
"-S=."
"-DBUILD_WERROR=ON"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_INSTALL_PREFIX=/usr"
"-DSUNSHINE_ASSETS_DIR=share/sunshine"
"-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine"
"-DSUNSHINE_ENABLE_WAYLAND=ON"
"-DSUNSHINE_ENABLE_X11=ON"
"-DSUNSHINE_ENABLE_DRM=ON"
)
if [ "$appimage_build" == 1 ]; then
cmake_args+=("-DSUNSHINE_BUILD_APPIMAGE=ON")
fi
# Publisher metadata
if [ -n "$publisher_name" ]; then
cmake_args+=("-DSUNSHINE_PUBLISHER_NAME='${publisher_name}'")
fi
if [ -n "$publisher_website" ]; then
cmake_args+=("-DSUNSHINE_PUBLISHER_WEBSITE='${publisher_website}'")
fi
if [ -n "$publisher_issue_url" ]; then
cmake_args+=("-DSUNSHINE_PUBLISHER_ISSUE_URL='${publisher_issue_url}'")
fi
# Update the package list
$package_update_command
if [ "$distro" == "debian" ]; then
add_debain_deps
elif [ "$distro" == "ubuntu" ]; then
add_ubuntu_deps
elif [ "$distro" == "fedora" ]; then
add_fedora_deps
dnf group install "Development Tools" -y
fi
# Install the dependencies
$package_install_command "${dependencies[@]}"
# reload the environment
# shellcheck source=/dev/null
source ~/.bashrc
gcc_alternative_files=(
"gcc"
"g++"
"gcov"
"gcc-ar"
"gcc-ranlib"
)
# update alternatives for gcc and g++ if a debian based distro
if [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then
for file in "${gcc_alternative_files[@]}"; do
file_path="/etc/alternatives/$file"
if [ -e "$file_path" ]; then
mv "$file_path" "$file_path.bak"
fi
done
${sudo_cmd} update-alternatives --install \
/usr/bin/gcc gcc /usr/bin/gcc-${gcc_version} 100 \
--slave /usr/bin/g++ g++ /usr/bin/g++-${gcc_version} \
--slave /usr/bin/gcov gcov /usr/bin/gcov-${gcc_version} \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${gcc_version} \
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${gcc_version}
fi
# compile cmake if the version is too low
cmake_min="3.25.0"
target_cmake_version="3.30.1"
if ! check_version "cmake" "$cmake_min"; then
cmake_prefix="https://github.com/Kitware/CMake/releases/download/v"
if [ "$architecture" == "x86_64" ]; then
cmake_arch="x86_64"
elif [ "$architecture" == "aarch64" ]; then
cmake_arch="aarch64"
fi
url="${cmake_prefix}${target_cmake_version}/cmake-${target_cmake_version}-linux-${cmake_arch}.sh"
echo "cmake url: ${url}"
wget "$url" --progress=bar:force:noscroll -q --show-progress -O "${build_dir}/cmake.sh"
${sudo_cmd} sh "${build_dir}/cmake.sh" --skip-license --prefix=/usr/local
echo "cmake installed, version:"
cmake --version
fi
# compile doxygen if version is too low
doxygen_min="1.10.0"
_doxygen_min="1_10_0"
if ! check_version "doxygen" "$doxygen_min"; then
if [ "${SUNSHINE_COMPILE_DOXYGEN}" == "true" ]; then
echo "Compiling doxygen"
doxygen_url="https://github.com/doxygen/doxygen/releases/download/Release_${_doxygen_min}/doxygen-${doxygen_min}.src.tar.gz"
echo "doxygen url: ${doxygen_url}"
wget "$doxygen_url" --progress=bar:force:noscroll -q --show-progress -O "${build_dir}/doxygen.tar.gz"
tar -xzf "${build_dir}/doxygen.tar.gz"
cd "doxygen-${doxygen_min}"
cmake -DCMAKE_BUILD_TYPE=Release -G="Ninja" -B="build" -S="."
ninja -C "build"
ninja -C "build" install
else
echo "Doxygen version too low, skipping docs"
cmake_args+=("-DBUILD_DOCS=OFF")
fi
fi
# install node from nvm
if [ "$nvm_node" == 1 ]; then
nvm_url="https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh"
echo "nvm url: ${nvm_url}"
wget -qO- ${nvm_url} | bash
source "$HOME/.nvm/nvm.sh"
nvm install node
nvm use node
fi
# run the cuda install
if [ -n "$cuda_version" ] && [ "$skip_cuda" == 0 ]; then
install_cuda
cmake_args+=("-DSUNSHINE_ENABLE_CUDA=ON")
cmake_args+=("-DCMAKE_CUDA_COMPILER:PATH=${build_dir}/cuda/bin/nvcc")
fi
# Cmake stuff here
mkdir -p "build"
echo "cmake args:"
echo "${cmake_args[@]}"
cmake "${cmake_args[@]}"
ninja -C "build"
# Create the package
if [ "$skip_package" == 0 ]; then
if [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then
cpack -G DEB --config ./build/CPackConfig.cmake
elif [ "$distro" == "fedora" ]; then
cpack -G RPM --config ./build/CPackConfig.cmake
fi
fi
if [ "$skip_cleanup" == 0 ]; then
# Restore the original gcc alternatives
if [ "$distro" == "debian" ] || [ "$distro" == "ubuntu" ]; then
for file in "${gcc_alternative_files[@]}"; do
if [ -e "/etc/alternatives/$file.bak" ]; then
${sudo_cmd} mv "/etc/alternatives/$file.bak" "/etc/alternatives/$file"
else
${sudo_cmd} rm "/etc/alternatives/$file"
fi
done
fi
# restore the math-vector.h file
if [ "$architecture" == "aarch64" ] && [ -n "$math_vector_file" ]; then
${sudo_cmd} mv -f "$math_vector_file.bak" "$math_vector_file"
fi
fi
}
# Determine the OS and call the appropriate function
cat /etc/os-release
if grep -q "Debian GNU/Linux 12 (bookworm)" /etc/os-release; then
distro="debian"
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:f39\"" /etc/os-release; then
distro="fedora"
version="39"
package_update_command="${sudo_cmd} dnf update -y"
package_install_command="${sudo_cmd} dnf install -y"
cuda_version="12.4.0"
cuda_build="550.54.14"
gcc_version="13"
nvm_node=0
elif grep -q "PLATFORM_ID=\"platform:f40\"" /etc/os-release; then
distro="fedora"
version="40"
package_update_command="${sudo_cmd} dnf update -y"
package_install_command="${sudo_cmd} dnf install -y"
cuda_version=
cuda_build=
gcc_version="13"
nvm_node=0
elif grep -q "Ubuntu 22.04" /etc/os-release; then
distro="ubuntu"
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"
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"
nvm_node=0
else
echo "Unsupported Distro or Version"
exit 1
fi
architecture=$(uname -m)
echo "Detected Distro: $distro"
echo "Detected Version: $version"
echo "Detected Architecture: $architecture"
if [ "$architecture" != "x86_64" ] && [ "$architecture" != "aarch64" ]; then
echo "Unsupported Architecture"
exit 1
fi
# get directory of this script
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
build_dir="$script_dir/../build"
echo "Script Directory: $script_dir"
echo "Build Directory: $build_dir"
mkdir -p "$build_dir"
run_install

View File

@ -1,2 +0,0 @@
Babel==2.16.0
clang-format

View File

@ -1,40 +0,0 @@
# standard imports
import os
import subprocess
# variables
directories = [
'src',
'tests',
'tools',
os.path.join('third-party', 'glad'),
os.path.join('third-party', 'nvfbc'),
]
file_types = [
'cpp',
'h',
'm',
'mm'
]
def clang_format(file: str):
print(f'Formatting {file} ...')
subprocess.run(['clang-format', '-i', file])
def main():
"""
Main entry point.
"""
# walk the directories
for directory in directories:
for root, dirs, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
if os.path.isfile(file_path) and file.rsplit('.')[-1] in file_types:
clang_format(file=file_path)
if __name__ == '__main__':
main()

View File

@ -1,79 +0,0 @@
cmake_minimum_required(VERSION 3.13)
# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md#foundational-c-support
project(test_sunshine)
include_directories("${CMAKE_SOURCE_DIR}")
enable_testing()
# Add GoogleTest directory to the project
set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest")
set(INSTALL_GTEST OFF)
set(INSTALL_GMOCK OFF)
add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest")
include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}")
# coverage
# https://gcovr.com/en/stable/guide/compiling.html#compiler-options
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0")
set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0")
# if windows
if (WIN32)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # cmake-lint: disable=C0103
endif ()
# modify SUNSHINE_DEFINITIONS
if (WIN32)
list(APPEND
SUNSHINE_DEFINITIONS SUNSHINE_SHADERS_DIR="${CMAKE_SOURCE_DIR}/src_assets/windows/assets/shaders/directx")
elseif (NOT APPLE)
list(APPEND SUNSHINE_DEFINITIONS SUNSHINE_SHADERS_DIR="${CMAKE_SOURCE_DIR}/src_assets/linux/assets/shaders/opengl")
endif ()
set(TEST_DEFINITIONS) # list will be appended as needed
# make sure TESTS_SOFTWARE_ENCODER_UNAVAILABLE is set to "fail" or "skip"
if (NOT (TESTS_SOFTWARE_ENCODER_UNAVAILABLE STREQUAL "fail" OR TESTS_SOFTWARE_ENCODER_UNAVAILABLE STREQUAL "skip"))
set(TESTS_SOFTWARE_ENCODER_UNAVAILABLE "fail")
endif ()
list(APPEND TEST_DEFINITIONS TESTS_SOFTWARE_ENCODER_UNAVAILABLE="${TESTS_SOFTWARE_ENCODER_UNAVAILABLE}") # fail/skip
# this indicates we're building tests in case sunshine needs to adjust some code or add private tests
list(APPEND TEST_DEFINITIONS SUNSHINE_TESTS)
file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS
${CMAKE_SOURCE_DIR}/tests/*.h
${CMAKE_SOURCE_DIR}/tests/*.cpp)
set(SUNSHINE_SOURCES
${SUNSHINE_TARGET_FILES})
# remove main.cpp from the list of sources
list(REMOVE_ITEM SUNSHINE_SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp)
add_executable(${PROJECT_NAME}
${TEST_SOURCES}
${SUNSHINE_SOURCES})
foreach(dep ${SUNSHINE_TARGET_DEPENDENCIES})
add_dependencies(${PROJECT_NAME} ${dep}) # compile these before sunshine
endforeach()
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20)
target_link_libraries(${PROJECT_NAME}
${SUNSHINE_EXTERNAL_LIBRARIES}
gtest
${PLATFORM_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${SUNSHINE_DEFINITIONS} ${TEST_DEFINITIONS})
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301
target_link_options(${PROJECT_NAME} PRIVATE)
if (WIN32)
# prefer static libraries since we're linking statically
# this fixes libcurl linking errors when using non MSYS2 version of CMake
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
endif ()

View File

@ -1,30 +0,0 @@
/**
* @file tests/tests_common.h
* @brief Common declarations.
*/
#pragma once
#include <gtest/gtest.h>
#include <src/globals.h>
#include <src/logging.h>
#include <src/platform/common.h>
struct PlatformTestSuite: testing::Test {
static void
SetUpTestSuite() {
ASSERT_FALSE(platf_deinit);
BOOST_LOG(tests) << "Setting up platform test suite";
platf_deinit = platf::init();
ASSERT_TRUE(platf_deinit);
}
static void
TearDownTestSuite() {
ASSERT_TRUE(platf_deinit);
platf_deinit = {};
BOOST_LOG(tests) << "Tore down platform test suite";
}
private:
inline static std::unique_ptr<platf::deinit_t> platf_deinit;
};

View File

@ -1,22 +0,0 @@
/**
* @file tests/tests_environment.h
* @brief Declarations for SunshineEnvironment.
*/
#pragma once
#include "tests_common.h"
struct SunshineEnvironment: testing::Environment {
void
SetUp() override {
mail::man = std::make_shared<safe::mail_raw_t>();
deinit_log = logging::init(0, "test_sunshine.log");
}
void
TearDown() override {
deinit_log = {};
mail::man = {};
}
std::unique_ptr<logging::deinit_t> deinit_log;
};

View File

@ -1,70 +0,0 @@
/**
* @file tests/tests_events.h
* @brief Declarations for SunshineEventListener.
*/
#pragma once
#include "tests_common.h"
struct SunshineEventListener: testing::EmptyTestEventListener {
SunshineEventListener() {
sink = boost::make_shared<sink_t>();
sink_buffer = boost::make_shared<std::stringstream>();
sink->locked_backend()->add_stream(sink_buffer);
sink->set_formatter(&logging::formatter);
}
void
OnTestProgramStart(const testing::UnitTest &unit_test) override {
boost::log::core::get()->add_sink(sink);
}
void
OnTestProgramEnd(const testing::UnitTest &unit_test) override {
boost::log::core::get()->remove_sink(sink);
}
void
OnTestStart(const testing::TestInfo &test_info) override {
BOOST_LOG(tests) << "From " << test_info.file() << ":" << test_info.line();
BOOST_LOG(tests) << " " << test_info.test_suite_name() << "/" << test_info.name() << " started";
}
void
OnTestPartResult(const testing::TestPartResult &test_part_result) override {
std::string file = test_part_result.file_name();
BOOST_LOG(tests) << "At " << file << ":" << test_part_result.line_number();
auto result_text = test_part_result.passed() ? "Success" :
test_part_result.nonfatally_failed() ? "Non-fatal failure" :
test_part_result.fatally_failed() ? "Failure" :
"Skip";
std::string summary = test_part_result.summary();
std::string message = test_part_result.message();
BOOST_LOG(tests) << " " << result_text << ": " << summary;
if (message != summary) {
BOOST_LOG(tests) << " " << message;
}
}
void
OnTestEnd(const testing::TestInfo &test_info) override {
auto &result = *test_info.result();
auto result_text = result.Passed() ? "passed" :
result.Skipped() ? "skipped" :
"failed";
BOOST_LOG(tests) << test_info.test_suite_name() << "/" << test_info.name() << " " << result_text;
if (result.Failed()) {
std::cout << sink_buffer->str();
}
sink_buffer->str("");
sink_buffer->clear();
}
using sink_t = boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend>;
boost::shared_ptr<sink_t> sink;
boost::shared_ptr<std::stringstream> sink_buffer;
};

View File

@ -1,135 +0,0 @@
/**
* @file tests/tests_log_checker.h
* @brief Utility functions to check log file contents.
*/
#pragma once
#include <algorithm>
#include <fstream>
#include <regex>
#include <string>
#include <src/logging.h>
namespace log_checker {
/**
* @brief Remove the timestamp prefix from a log line.
* @param line The log line.
* @return The log line without the timestamp prefix.
*/
inline std::string
remove_timestamp_prefix(const std::string &line) {
static const std::regex timestamp_regex(R"(\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]: )");
return std::regex_replace(line, timestamp_regex, "");
}
/**
* @brief Check if a log file contains a line that starts with the given string.
* @param log_file Path to the log file.
* @param start_str The string that the line should start with.
* @return True if such a line is found, false otherwise.
*/
inline bool
line_starts_with(const std::string &log_file, const std::string_view &start_str) {
logging::log_flush();
std::ifstream input(log_file);
if (!input.is_open()) {
return false;
}
for (std::string line; std::getline(input, line);) {
line = remove_timestamp_prefix(line);
if (line.rfind(start_str, 0) == 0) {
return true;
}
}
return false;
}
/**
* @brief Check if a log file contains a line that ends with the given string.
* @param log_file Path to the log file.
* @param end_str The string that the line should end with.
* @return True if such a line is found, false otherwise.
*/
inline bool
line_ends_with(const std::string &log_file, const std::string_view &end_str) {
logging::log_flush();
std::ifstream input(log_file);
if (!input.is_open()) {
return false;
}
for (std::string line; std::getline(input, line);) {
line = remove_timestamp_prefix(line);
if (line.size() >= end_str.size() &&
line.compare(line.size() - end_str.size(), end_str.size(), end_str) == 0) {
return true;
}
}
return false;
}
/**
* @brief Check if a log file contains a line that equals the given string.
* @param log_file Path to the log file.
* @param str The string that the line should equal.
* @return True if such a line is found, false otherwise.
*/
inline bool
line_equals(const std::string &log_file, const std::string_view &str) {
logging::log_flush();
std::ifstream input(log_file);
if (!input.is_open()) {
return false;
}
for (std::string line; std::getline(input, line);) {
line = remove_timestamp_prefix(line);
if (line == str) {
return true;
}
}
return false;
}
/**
* @brief Check if a log file contains a line that contains the given substring.
* @param log_file Path to the log file.
* @param substr The substring to search for.
* @param case_insensitive Whether the search should be case-insensitive.
* @return True if such a line is found, false otherwise.
*/
inline bool
line_contains(const std::string &log_file, const std::string_view &substr, bool case_insensitive = false) {
logging::log_flush();
std::ifstream input(log_file);
if (!input.is_open()) {
return false;
}
std::string search_str(substr);
if (case_insensitive) {
// sonarcloud complains about this, but the solution doesn't work for macOS-12
std::transform(search_str.begin(), search_str.end(), search_str.begin(), ::tolower);
}
for (std::string line; std::getline(input, line);) {
line = remove_timestamp_prefix(line);
if (case_insensitive) {
// sonarcloud complains about this, but the solution doesn't work for macOS-12
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
}
if (line.find(search_str) != std::string::npos) {
return true;
}
}
return false;
}
} // namespace log_checker

View File

@ -1,15 +0,0 @@
/**
* @file tests/tests_main.cpp
* @brief Entry point definition.
*/
#include "tests_common.h"
#include "tests_environment.h"
#include "tests_events.h"
int
main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
testing::AddGlobalTestEnvironment(new SunshineEnvironment);
testing::UnitTest::GetInstance()->listeners().Append(new SunshineEventListener);
return RUN_ALL_TESTS();
}

View File

@ -1,49 +0,0 @@
/**
* @file tests/unit/platform/test_common.cpp
* @brief Test src/platform/common.*.
*/
#include <src/platform/common.h>
#include "../../tests_common.h"
struct SetEnvTest: ::testing::TestWithParam<std::tuple<std::string, std::string, int>> {
protected:
void
TearDown() override {
// Clean up environment variable after each test
const auto &[name, value, expected] = GetParam();
platf::unset_env(name);
}
};
TEST_P(SetEnvTest, SetEnvironmentVariableTests) {
const auto &[name, value, expected] = GetParam();
platf::set_env(name, value);
const char *env_value = std::getenv(name.c_str());
if (expected == 0 && !value.empty()) {
ASSERT_NE(env_value, nullptr);
ASSERT_EQ(std::string(env_value), value);
}
else {
ASSERT_EQ(env_value, nullptr);
}
}
TEST_P(SetEnvTest, UnsetEnvironmentVariableTests) {
const auto &[name, value, expected] = GetParam();
platf::unset_env(name);
const char *env_value = std::getenv(name.c_str());
if (expected == 0) {
ASSERT_EQ(env_value, nullptr);
}
}
INSTANTIATE_TEST_SUITE_P(
SetEnvTests,
SetEnvTest,
::testing::Values(
std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_0", 0),
std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_1", 0),
std::make_tuple("", "test_value", -1)));

View File

@ -1,67 +0,0 @@
/**
* @file tests/unit/test_audio.cpp
* @brief Test src/audio.*.
*/
#include <src/audio.h>
#include "../tests_common.h"
using namespace audio;
struct AudioTest: PlatformTestSuite, testing::WithParamInterface<std::tuple<std::basic_string_view<char>, config_t>> {
void
SetUp() override {
m_config = std::get<1>(GetParam());
m_mail = std::make_shared<safe::mail_raw_t>();
}
config_t m_config;
safe::mail_t m_mail;
};
constexpr std::bitset<config_t::MAX_FLAGS>
config_flags(int flag = -1) {
std::bitset<3> result = std::bitset<config_t::MAX_FLAGS>();
if (flag >= 0) {
result.set(flag);
}
return result;
}
INSTANTIATE_TEST_SUITE_P(
Configurations,
AudioTest,
testing::Values(
std::make_tuple("HIGH_STEREO", config_t { 5, 2, 0x3, { 0 }, config_flags(config_t::HIGH_QUALITY) }),
std::make_tuple("SURROUND51", config_t { 5, 6, 0x3F, { 0 }, config_flags() }),
std::make_tuple("SURROUND71", config_t { 5, 8, 0x63F, { 0 }, config_flags() }),
std::make_tuple("SURROUND51_CUSTOM", config_t { 5, 6, 0x3F, { 6, 4, 2, { 0, 1, 4, 5, 2, 3 } }, config_flags(config_t::CUSTOM_SURROUND_PARAMS) })),
[](const auto &info) { return std::string(std::get<0>(info.param)); });
TEST_P(AudioTest, TestEncode) {
std::thread timer([&] {
// Terminate the audio capture after 5 seconds.
std::this_thread::sleep_for(5s);
auto shutdown_event = m_mail->event<bool>(mail::shutdown);
auto audio_packets = m_mail->queue<packet_t>(mail::audio_packets);
shutdown_event->raise(true);
audio_packets->stop();
});
std::thread capture([&] {
auto packets = m_mail->queue<packet_t>(mail::audio_packets);
auto shutdown_event = m_mail->event<bool>(mail::shutdown);
while (auto packet = packets->pop()) {
if (shutdown_event->peek()) {
break;
}
auto packet_data = packet->second;
if (packet_data.size() == 0) {
FAIL() << "Empty packet data";
}
}
});
audio::capture(m_mail, m_config, nullptr);
timer.join();
capture.join();
}

View File

@ -1,18 +0,0 @@
/**
* @file tests/unit/test_entry_handler.cpp
* @brief Test src/entry_handler.*.
*/
#include <src/entry_handler.h>
#include "../tests_common.h"
#include "../tests_log_checker.h"
TEST(EntryHandlerTests, LogPublisherDataTest) {
// call log_publisher_data
log_publisher_data();
// check if specific log messages exist
ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Package Publisher: "));
ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Publisher Website: "));
ASSERT_TRUE(log_checker::line_starts_with("test_sunshine.log", "Info: Get support: "));
}

View File

@ -1,90 +0,0 @@
/**
* @file tests/unit/test_file_handler.cpp
* @brief Test src/file_handler.*.
*/
#include <src/file_handler.h>
#include "../tests_common.h"
struct FileHandlerParentDirectoryTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
TEST_P(FileHandlerParentDirectoryTest, Run) {
auto [input, expected] = GetParam();
EXPECT_EQ(file_handler::get_parent_directory(input), expected);
}
INSTANTIATE_TEST_SUITE_P(
FileHandlerTests,
FileHandlerParentDirectoryTest,
testing::Values(
std::make_tuple("/path/to/file.txt", "/path/to"),
std::make_tuple("/path/to/directory", "/path/to"),
std::make_tuple("/path/to/directory/", "/path/to")));
struct FileHandlerMakeDirectoryTest: testing::TestWithParam<std::tuple<std::string, bool, bool>> {};
TEST_P(FileHandlerMakeDirectoryTest, Run) {
auto [input, expected, remove] = GetParam();
const std::string test_dir = platf::appdata().string() + "/tests/path/";
input = test_dir + input;
EXPECT_EQ(file_handler::make_directory(input), expected);
EXPECT_TRUE(std::filesystem::exists(input));
// remove test directory
if (remove) {
std::filesystem::remove_all(test_dir);
EXPECT_FALSE(std::filesystem::exists(test_dir));
}
}
INSTANTIATE_TEST_SUITE_P(
FileHandlerTests,
FileHandlerMakeDirectoryTest,
testing::Values(
std::make_tuple("dir_123", true, false),
std::make_tuple("dir_123", true, true),
std::make_tuple("dir_123/abc", true, false),
std::make_tuple("dir_123/abc", true, true)));
struct FileHandlerTests: testing::TestWithParam<std::tuple<int, std::string>> {};
INSTANTIATE_TEST_SUITE_P(
TestFiles,
FileHandlerTests,
testing::Values(
std::make_tuple(0, ""), // empty file
std::make_tuple(1, "a"), // single character
std::make_tuple(2, "Mr. Blue Sky - Electric Light Orchestra"), // single line
std::make_tuple(3, R"(
Morning! Today's forecast calls for blue skies
The sun is shining in the sky
There ain't a cloud in sight
It's stopped raining
Everybody's in the play
And don't you know, it's a beautiful new day
Hey, hey, hey!
Running down the avenue
See how the sun shines brightly in the city
All the streets where once was pity
Mr. Blue Sky is living here today!
Hey, hey, hey!
)") // multi-line
));
TEST_P(FileHandlerTests, WriteFileTest) {
auto [fileNum, content] = GetParam();
std::string fileName = "write_file_test_" + std::to_string(fileNum) + ".txt";
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";
EXPECT_EQ(file_handler::read_file(fileName.c_str()), content);
}
TEST(FileHandlerTests, ReadMissingFileTest) {
// read missing file
EXPECT_EQ(file_handler::read_file("non-existing-file.txt"), "");
}

View File

@ -1,53 +0,0 @@
/**
* @file tests/unit/test_httpcommon.cpp
* @brief Test src/httpcommon.*.
*/
#include <src/httpcommon.h>
#include "../tests_common.h"
struct UrlEscapeTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
TEST_P(UrlEscapeTest, Run) {
auto [input, expected] = GetParam();
ASSERT_EQ(http::url_escape(input), expected);
}
INSTANTIATE_TEST_SUITE_P(
UrlEscapeTests,
UrlEscapeTest,
testing::Values(
std::make_tuple("igdb_0123456789", "igdb_0123456789"),
std::make_tuple("../../../", "..%2F..%2F..%2F"),
std::make_tuple("..*\\", "..%2A%5C")));
struct UrlGetHostTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
TEST_P(UrlGetHostTest, Run) {
auto [input, expected] = GetParam();
ASSERT_EQ(http::url_get_host(input), expected);
}
INSTANTIATE_TEST_SUITE_P(
UrlGetHostTests,
UrlGetHostTest,
testing::Values(
std::make_tuple("https://images.igdb.com/example.txt", "images.igdb.com"),
std::make_tuple("http://localhost:8080", "localhost"),
std::make_tuple("nonsense!!}{::", "")));
struct DownloadFileTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
TEST_P(DownloadFileTest, Run) {
auto [url, filename] = GetParam();
const std::string test_dir = platf::appdata().string() + "/tests/";
std::basic_string path = test_dir + filename;
ASSERT_TRUE(http::download_file(url, path));
}
INSTANTIATE_TEST_SUITE_P(
DownloadFileTests,
DownloadFileTest,
testing::Values(
std::make_tuple("https://httpbin.org/base64/aGVsbG8h", "hello.txt"),
std::make_tuple("https://httpbin.org/redirect-to?url=/base64/aGVsbG8h", "hello-redirect.txt")));

View File

@ -1,44 +0,0 @@
/**
* @file tests/unit/test_logging.cpp
* @brief Test src/logging.*.
*/
#include <src/logging.h>
#include "../tests_common.h"
#include "../tests_log_checker.h"
#include <random>
namespace {
std::array log_levels = {
std::tuple("verbose", &verbose),
std::tuple("debug", &debug),
std::tuple("info", &info),
std::tuple("warning", &warning),
std::tuple("error", &error),
std::tuple("fatal", &fatal),
};
constexpr auto log_file = "test_sunshine.log";
} // namespace
struct LogLevelsTest: testing::TestWithParam<decltype(log_levels)::value_type> {};
INSTANTIATE_TEST_SUITE_P(
Logging,
LogLevelsTest,
testing::ValuesIn(log_levels),
[](const auto &info) { return std::string(std::get<0>(info.param)); });
TEST_P(LogLevelsTest, PutMessage) {
auto [label, plogger] = GetParam();
ASSERT_TRUE(plogger);
auto &logger = *plogger;
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());
BOOST_LOG(logger) << test_message;
ASSERT_TRUE(log_checker::line_contains(log_file, test_message));
}

View File

@ -1,120 +0,0 @@
/**
* @file tests/unit/test_mouse.cpp
* @brief Test src/input.*.
*/
#include <src/input.h>
#include "../tests_common.h"
struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface<util::point_t> {
void
SetUp() override {
#ifdef _WIN32
// TODO: Windows tests are failing, `get_mouse_loc` seems broken and `platf::abs_mouse` too
// the alternative `platf::abs_mouse` method seem to work better during tests,
// but I'm not sure about real work
GTEST_SKIP() << "TODO Windows";
#elif __linux__
// TODO: Inputtino waiting https://github.com/games-on-whales/inputtino/issues/6 is resolved.
GTEST_SKIP() << "TODO Inputtino";
#endif
}
void
TearDown() override {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
};
INSTANTIATE_TEST_SUITE_P(
MouseInputs,
MouseHIDTest,
testing::Values(
util::point_t { 40, 40 },
util::point_t { 70, 150 }));
// todo: add tests for hitting screen edges
TEST_P(MouseHIDTest, MoveInputTest) {
util::point_t mouse_delta = GetParam();
BOOST_LOG(tests) << "MoveInputTest:: got param: " << mouse_delta;
platf::input_t input = platf::input();
BOOST_LOG(tests) << "MoveInputTest:: init input";
BOOST_LOG(tests) << "MoveInputTest:: get current mouse loc";
auto old_loc = platf::get_mouse_loc(input);
BOOST_LOG(tests) << "MoveInputTest:: got current mouse loc: " << old_loc;
BOOST_LOG(tests) << "MoveInputTest:: move: " << mouse_delta;
platf::move_mouse(input, mouse_delta.x, mouse_delta.y);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
BOOST_LOG(tests) << "MoveInputTest:: moved: " << mouse_delta;
BOOST_LOG(tests) << "MoveInputTest:: get updated mouse loc";
auto new_loc = platf::get_mouse_loc(input);
BOOST_LOG(tests) << "MoveInputTest:: got updated mouse loc: " << new_loc;
bool has_input_moved = old_loc.x != new_loc.x && old_loc.y != new_loc.y;
if (!has_input_moved) {
BOOST_LOG(tests) << "MoveInputTest:: haven't moved";
}
else {
BOOST_LOG(tests) << "MoveInputTest:: moved";
}
EXPECT_TRUE(has_input_moved);
// Verify we moved as much as we requested
EXPECT_EQ(new_loc.x - old_loc.x, mouse_delta.x);
EXPECT_EQ(new_loc.y - old_loc.y, mouse_delta.y);
}
TEST_P(MouseHIDTest, AbsMoveInputTest) {
util::point_t mouse_pos = GetParam();
BOOST_LOG(tests) << "AbsMoveInputTest:: got param: " << mouse_pos;
platf::input_t input = platf::input();
BOOST_LOG(tests) << "AbsMoveInputTest:: init input";
BOOST_LOG(tests) << "AbsMoveInputTest:: get current mouse loc";
auto old_loc = platf::get_mouse_loc(input);
BOOST_LOG(tests) << "AbsMoveInputTest:: got current mouse loc: " << old_loc;
#ifdef _WIN32
platf::touch_port_t abs_port {
0, 0,
65535, 65535
};
#elif __linux__
platf::touch_port_t abs_port {
0, 0,
19200, 12000
};
#else
platf::touch_port_t abs_port {};
#endif
BOOST_LOG(tests) << "AbsMoveInputTest:: move: " << mouse_pos;
platf::abs_mouse(input, abs_port, mouse_pos.x, mouse_pos.y);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
BOOST_LOG(tests) << "AbsMoveInputTest:: moved: " << mouse_pos;
BOOST_LOG(tests) << "AbsMoveInputTest:: get updated mouse loc";
auto new_loc = platf::get_mouse_loc(input);
BOOST_LOG(tests) << "AbsMoveInputTest:: got updated mouse loc: " << new_loc;
bool has_input_moved = old_loc.x != new_loc.x || old_loc.y != new_loc.y;
if (!has_input_moved) {
BOOST_LOG(tests) << "AbsMoveInputTest:: haven't moved";
}
else {
BOOST_LOG(tests) << "AbsMoveInputTest:: moved";
}
EXPECT_TRUE(has_input_moved);
// Verify we moved to the absolute coordinate
EXPECT_EQ(new_loc.x, mouse_pos.x);
EXPECT_EQ(new_loc.y, mouse_pos.y);
}

View File

@ -1,26 +0,0 @@
/**
* @file tests/unit/test_network.cpp
* @brief Test src/network.*
*/
#include <src/network.h>
#include "../tests_common.h"
struct MdnsInstanceNameTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
TEST_P(MdnsInstanceNameTest, Run) {
auto [input, expected] = GetParam();
ASSERT_EQ(net::mdns_instance_name(input), expected);
}
INSTANTIATE_TEST_SUITE_P(
MdnsInstanceNameTests,
MdnsInstanceNameTest,
testing::Values(
std::make_tuple("shortname-123", "shortname-123"),
std::make_tuple("space 123", "space-123"),
std::make_tuple("hostname.domain.test", "hostname"),
std::make_tuple("&", "Sunshine"),
std::make_tuple("", "Sunshine"),
std::make_tuple("😁", "Sunshine"),
std::make_tuple(std::string(128, 'a'), std::string(63, 'a'))));

View File

@ -1,36 +0,0 @@
/**
* @file tests/unit/test_rswrapper.cpp
* @brief Test src/rswrapper.*
*/
extern "C" {
#include <src/rswrapper.h>
}
#include "../tests_common.h"
TEST(ReedSolomonWrapperTests, InitTest) {
reed_solomon_init();
// Ensure all function pointers were populated
ASSERT_NE(reed_solomon_new, nullptr);
ASSERT_NE(reed_solomon_release, nullptr);
ASSERT_NE(reed_solomon_encode, nullptr);
ASSERT_NE(reed_solomon_decode, nullptr);
}
TEST(ReedSolomonWrapperTests, EncodeTest) {
reed_solomon_init();
auto rs = reed_solomon_new(1, 1);
ASSERT_NE(rs, nullptr);
uint8_t dataShard[16] = {};
uint8_t fecShard[16] = {};
// If we picked the incorrect ISA in our wrapper, we should crash here
uint8_t *shardPtrs[2] = { dataShard, fecShard };
auto ret = reed_solomon_encode(rs, shardPtrs, 2, sizeof(dataShard));
ASSERT_EQ(ret, 0);
reed_solomon_release(rs);
}

View File

@ -1,40 +0,0 @@
/**
* @file tests/unit/test_stream.cpp
* @brief Test src/stream.*
*/
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
namespace stream {
std::vector<uint8_t>
concat_and_insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data1, const std::string_view &data2);
}
#include "../tests_common.h"
TEST(ConcatAndInsertTests, ConcatNoInsertionTest) {
char b1[] = { 'a', 'b' };
char b2[] = { 'c', 'd', 'e' };
auto res = stream::concat_and_insert(0, 2, std::string_view { b1, sizeof(b1) }, std::string_view { b2, sizeof(b2) });
auto expected = std::vector<uint8_t> { 'a', 'b', 'c', 'd', 'e' };
ASSERT_EQ(res, expected);
}
TEST(ConcatAndInsertTests, ConcatLargeStrideTest) {
char b1[] = { 'a', 'b' };
char b2[] = { 'c', 'd', 'e' };
auto res = stream::concat_and_insert(1, sizeof(b1) + sizeof(b2) + 1, std::string_view { b1, sizeof(b1) }, std::string_view { b2, sizeof(b2) });
auto expected = std::vector<uint8_t> { 0, 'a', 'b', 'c', 'd', 'e' };
ASSERT_EQ(res, expected);
}
TEST(ConcatAndInsertTests, ConcatSmallStrideTest) {
char b1[] = { 'a', 'b' };
char b2[] = { 'c', 'd', 'e' };
auto res = stream::concat_and_insert(1, 1, std::string_view { b1, sizeof(b1) }, std::string_view { b2, sizeof(b2) });
auto expected = std::vector<uint8_t> { 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e' };
ASSERT_EQ(res, expected);
}

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