remove unused files
@ -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"
|
||||
@ -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"
|
||||
@ -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"
|
||||
188
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -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
|
||||
13
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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
|
||||
48
.github/dependabot.yml
vendored
@ -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
|
||||
49
.github/label-actions.yml
vendored
@ -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
@ -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
385
.github/workflows/ci-docker.yml
vendored
@ -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
|
||||
214
.github/workflows/codeql.yml
vendored
@ -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
|
||||
120
.github/workflows/cpp-lint.yml
vendored
@ -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 }}
|
||||
25
.github/workflows/issues.yml
vendored
@ -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 }}
|
||||
98
.github/workflows/localize.yml
vendored
@ -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
|
||||
62
.github/workflows/python-flake8.yml
vendored
@ -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
|
||||
27
.github/workflows/release-notifier-moonlight.yml
vendored
@ -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
|
||||
127
.github/workflows/release-notifier.yml
vendored
@ -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 }}
|
||||
31
.github/workflows/update-changelog.yml
vendored
@ -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 }}
|
||||
89
.github/workflows/update-docs.yml
vendored
@ -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"
|
||||
71
.github/workflows/update-homebrew-release.yml
vendored
@ -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
|
||||
110
.github/workflows/update-pacman-repo.yml
vendored
@ -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
|
||||
62
.github/workflows/update-pages.yml
vendored
@ -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 }}
|
||||
69
.github/workflows/update-winget-release.yml
vendored
@ -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 }}
|
||||
66
.github/workflows/yaml-lint.yml
vendored
@ -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
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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>
|
||||
167
docs/building.md
@ -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>
|
||||
@ -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>
|
||||
@ -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).
|
||||
|
||||

|
||||
|
||||
@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>
|
||||
@ -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>
|
||||
@ -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
|
||||
539
docs/guides.md
@ -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%
|
||||
|
||||

|
||||
|
||||
* Start Sunshine
|
||||
|
||||
* Set *Sound Output* to *sink-sunshine-stereo* (if it isn't automatic)
|
||||
|
||||

|
||||
|
||||
* 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
|
||||
|
||||

|
||||
|
||||
* Open *qpwgraph*
|
||||
|
||||

|
||||
|
||||
* Connect `sunshine [sunshine-record]` to your normal *Output Device*
|
||||
* Drag `monitor_FL` to `playback_FL`
|
||||
* Drag `monitor_FR` to `playback_FR`
|
||||
|
||||

|
||||
|
||||
### 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>
|
||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 95 KiB |
@ -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>
|
||||
@ -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>
|
||||
@ -1,31 +0,0 @@
|
||||
# Third-Party Packages
|
||||
|
||||
@danger{These packages are not maintained by LizardByte. Use at your own risk.}
|
||||
|
||||
## Chocolatey
|
||||
[](https://community.chocolatey.org/packages/sunshine)
|
||||
|
||||
## Flathub
|
||||
[](https://flathub.org/apps/dev.lizardbyte.app.Sunshine)
|
||||
|
||||
## nixpkgs
|
||||
[](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/servers/sunshine/default.nix)
|
||||
|
||||
## Scoop
|
||||
[](https://scoop.sh/#/apps?s=0&d=1&o=true&q=sunshine)
|
||||
|
||||
## 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>
|
||||
@ -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>
|
||||
|
Before Width: | Height: | Size: 6.2 MiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 5.9 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
@ -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>
|
||||
@ -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" $@
|
||||
@ -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
|
||||
@ -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"
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"PATH": "$(PATH):$(HOME)/.local/bin"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"name": "Desktop",
|
||||
"image-path": "desktop.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit d88a9156b91eef64ecf1a313c868f1401c4bb39b
|
||||
@ -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 && 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>
|
||||
@ -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
|
||||
@ -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"
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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:
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,6 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=@PROJECT_NAME@
|
||||
Exec=sunshine
|
||||
Terminal=true
|
||||
Type=Application
|
||||
NoDisplay=true
|
||||
@ -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.*
|
||||
@ -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
|
||||
@ -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()
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,2 +0,0 @@
|
||||
Babel==2.16.0
|
||||
clang-format
|
||||
@ -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()
|
||||
@ -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 ()
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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
|
||||
@ -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();
|
||||
}
|
||||
@ -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)));
|
||||
@ -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();
|
||||
}
|
||||
@ -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: "));
|
||||
}
|
||||
@ -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"), "");
|
||||
}
|
||||
@ -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")));
|
||||
@ -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));
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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'))));
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||