Compare commits

...

10 commits

Author SHA1 Message Date
bbc1f724e1 Phase 1
Some checks failed
ci-bundle.yml / Phase 1 (push) Failing after 0s
ci-copr.yml / Phase 1 (push) Failing after 0s
ci-homebrew.yml / Phase 1 (push) Failing after 0s
2026-02-10 06:34:17 -07:00
David Lane
97b6168ba6
ci(deps): use codecov-action for test results (#4689)
Some checks failed
ci-bundle.yml / ci(deps): use codecov-action for test results (#4689) (push) Failing after 0s
ci-copr.yml / ci(deps): use codecov-action for test results (#4689) (push) Failing after 0s
ci-homebrew.yml / ci(deps): use codecov-action for test results (#4689) (push) Failing after 0s
CodeQL / CodeQL (push) Has been cancelled
CI / GitHub Env Debug (push) Has been cancelled
CI / Release Setup (push) Has been cancelled
CI / Docker (push) Has been cancelled
CI / FreeBSD (push) Has been cancelled
CI / Homebrew (push) Has been cancelled
CI / Linux (push) Has been cancelled
CI / Archlinux (push) Has been cancelled
CI / Linux Copr (push) Has been cancelled
CI / Linux Flatpak (push) Has been cancelled
CI / Windows (push) Has been cancelled
CI / Bundle Analysis (push) Has been cancelled
CI / Coverage-Homebrew-macos-14 (push) Has been cancelled
CI / Coverage-Homebrew-macos-15 (push) Has been cancelled
CI / Coverage-Homebrew-macos-26 (push) Has been cancelled
CI / Coverage-Archlinux (push) Has been cancelled
CI / Coverage-FreeBSD-14.3-aarch64 (push) Has been cancelled
CI / Coverage-FreeBSD-14.3-amd64 (push) Has been cancelled
CI / Coverage-Homebrew-ubuntu-22.04 (push) Has been cancelled
CI / Coverage-Linux-AppImage (push) Has been cancelled
CI / Coverage-Windows-AMD64 (push) Has been cancelled
CI / Coverage-Windows-ARM64 (push) Has been cancelled
CI / Release (push) Has been cancelled
CI / Release Homebrew Beta (push) Has been cancelled
Build GH-Pages / prep (push) Has been cancelled
Build GH-Pages / call-jekyll-build (push) Has been cancelled
2026-02-08 22:41:51 -05:00
Coia Prant
8aed1a82c8
build(web-ui): fix rollup failing (#4687) 2026-02-08 15:30:40 -05:00
David Lane
5bd3a2b225
docs: miscellaneous updates (#4597) 2026-02-08 00:12:38 -05:00
Conn O'Griofa
2f61116432
revert: "fix(linux/xdgportal): flag stream as realtime" (#4686)
This reverts commit bf574afdfd.
2026-02-07 22:03:27 -05:00
Coia Prant
d591643706
build(windows): add arm64 support (#3905)
Signed-off-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
Signed-off-by: Coia Prant <coiaprant@gmail.com>
Co-authored-by: Ricky8955555 <rkmiao@duck.com>
Co-authored-by: Mike Fara <mjfara@gmail.com>
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
2026-02-07 21:03:05 -05:00
David Lane
cdc444314f
feat(installer/windows): add wix installer (#3916) 2026-02-07 15:21:25 -05:00
David Lane
e2652fa52b
refactor(packaging/windows): installer script execution (#4675) 2026-02-07 13:04:43 -05:00
Conn O'Griofa
bf574afdfd
fix(linux/xdgportal): flag stream as realtime (#4684) 2026-02-07 11:32:37 -05:00
Conn O'Griofa
b48a96f9f1
fix(linux/xdgportal): populate host latency statistics (#4685) 2026-02-07 09:40:06 -05:00
37 changed files with 1268 additions and 146 deletions

View file

@ -32,7 +32,9 @@ body:
id: description id: description
attributes: attributes:
label: Describe the Bug label: Describe the Bug
description: A clear and concise description of the bug, list the reproduction steps. description: |
A clear and concise description of the bug, list the reproduction steps.
:warning: Errors in log messages are NOT bugs. Read the message and fix what it's telling you. :warning:
validations: validations:
required: true required: true
- type: textarea - type: textarea
@ -52,6 +54,7 @@ body:
description: What version operating system are you running the software on? description: What version operating system are you running the software on?
options: options:
- Docker - Docker
- FreeBSD
- Linux - Linux
- macOS - macOS
- Windows - Windows
@ -75,15 +78,6 @@ body:
- other, n/a - other, n/a
validations: validations:
required: true 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 - type: dropdown
id: package_type id: package_type
attributes: attributes:
@ -103,10 +97,10 @@ body:
- Linux - solus (Third Party) - Linux - solus (Third Party)
- Linux - Unraid (Third Party) - Linux - Unraid (Third Party)
- macOS - Homebrew - macOS - Homebrew
- macOS - Portfile
- Windows - Chocolatey (Third Party) - Windows - Chocolatey (Third Party)
- Windows - installer (recommended) - Windows - exe installer
- Windows - portable (not recommended) - Windows - msi installer (recommended)
- Windows - portable (NOT recommended)
- Windows - Scoop (Third Party) - Windows - Scoop (Third Party)
- Windows - Winget - Windows - Winget
- other (not listed) - other (not listed)
@ -154,19 +148,11 @@ body:
- NvFBC (Linux) - NvFBC (Linux)
- wlroots (Linux) - wlroots (Linux)
- X11 (Linux) - X11 (Linux)
- XDG Portal Grab (Linux)
- Desktop Duplication API (Windows) - Desktop Duplication API (Windows)
- Windows.Graphics.Capture (Windows) - Windows.Graphics.Capture (Windows)
validations: validations:
required: false required: false
- type: textarea
id: config
attributes:
label: Config
description: |
Please copy and paste your config (`sunshine.conf`) file.
render: Shell
validations:
required: false
- type: textarea - type: textarea
id: apps id: apps
attributes: attributes:
@ -179,10 +165,20 @@ body:
- type: textarea - type: textarea
id: logs id: logs
attributes: attributes:
label: Relevant log output label: Log output
description: | description: |
Please copy and paste any relevant log output. This will be automatically formatted into code, Copy and paste logs from web-ui troubleshooting page.
so no need for backticks. This will be automatically formatted into code, so no need for backticks.
:warning: If full logs are not provided, the issue will be closed! :warning:
render: shell render: shell
validations: validations:
required: true required: false
- type: input
id: logs_link
attributes:
label: Online logs
description: |
If logs are too long to include in the field above,
create a [gist](https://gist.github.com/) of the logs and paste the link here.
validations:
required: false

View file

@ -23,6 +23,10 @@ jobs:
- name: Install npm dependencies - name: Install npm dependencies
run: npm install --ignore-scripts run: npm install --ignore-scripts
- name: Debug install
if: always()
run: cat "${HOME}/.npm/_logs/*-debug-0.log" || true
- name: Build - name: Build
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View file

@ -29,6 +29,11 @@ jobs:
arch: x86_64 arch: x86_64
msystem: ucrt64 msystem: ucrt64
toolchain: ucrt-x86_64 toolchain: ucrt-x86_64
- name: Windows-ARM64
os: windows-11-arm
arch: aarch64
msystem: clangarm64
toolchain: clang-aarch64
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@ -46,7 +51,9 @@ jobs:
- name: Update Windows dependencies - name: Update Windows dependencies
env: env:
MSYSTEM: ${{ matrix.msystem }} # MSYSTEM is a built-in environment variable of MSYS2.
# Do not use this environment variable name.
MATRIX_MSYSTEM: ${{ matrix.msystem }}
TOOLCHAIN: ${{ matrix.toolchain }} TOOLCHAIN: ${{ matrix.toolchain }}
shell: msys2 {0} shell: msys2 {0}
run: | run: |
@ -62,17 +69,22 @@ jobs:
"mingw-w64-${TOOLCHAIN}-curl-winssl" "mingw-w64-${TOOLCHAIN}-curl-winssl"
"mingw-w64-${TOOLCHAIN}-gcc" "mingw-w64-${TOOLCHAIN}-gcc"
"mingw-w64-${TOOLCHAIN}-graphviz" "mingw-w64-${TOOLCHAIN}-graphviz"
"mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-miniupnpc" "mingw-w64-${TOOLCHAIN}-miniupnpc"
"mingw-w64-${TOOLCHAIN}-nlohmann-json" "mingw-w64-${TOOLCHAIN}-nlohmann-json"
"mingw-w64-${TOOLCHAIN}-nodejs"
"mingw-w64-${TOOLCHAIN}-nsis"
"mingw-w64-${TOOLCHAIN}-onevpl" "mingw-w64-${TOOLCHAIN}-onevpl"
"mingw-w64-${TOOLCHAIN}-openssl" "mingw-w64-${TOOLCHAIN}-openssl"
"mingw-w64-${TOOLCHAIN}-opus" "mingw-w64-${TOOLCHAIN}-opus"
"mingw-w64-${TOOLCHAIN}-toolchain" "mingw-w64-${TOOLCHAIN}-toolchain"
) )
if [[ "${MATRIX_MSYSTEM}" == "ucrt64" ]]; then
dependencies+=(
"mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-nsis"
"mingw-w64-${TOOLCHAIN}-nodejs"
)
fi
# do not modify below this line # do not modify below this line
ignore_packages=() ignore_packages=()
@ -83,7 +95,7 @@ jobs:
tarball="${pkg}-${version}-any.pkg.tar.zst" tarball="${pkg}-${version}-any.pkg.tar.zst"
# download working version # download working version
wget "https://repo.msys2.org/mingw/${MSYSTEM}/${tarball}" wget "https://repo.msys2.org/mingw/${MATRIX_MSYSTEM}/${tarball}"
tarballs="${tarballs} ${tarball}" tarballs="${tarballs} ${tarball}"
done done
@ -129,6 +141,33 @@ jobs:
# Clean up # Clean up
Remove-Item -Path doxygen-setup.exe Remove-Item -Path doxygen-setup.exe
- name: Setup dotnet # needed for wix
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
with:
dotnet-version: '10.x'
- name: Setup NodeJS
# Clang compiled NodeJS has issues when running rollup webpack
if: matrix.msystem != 'ucrt64'
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
- name: NodeJS Path
if: matrix.msystem != 'ucrt64'
shell: pwsh
run: |
# get NodeJS PATH
$NODEJS_BINARY_PATH = (Get-Command node).Source
$NODEJS_PATH = Split-Path -Path "$NODEJS_BINARY_PATH" -Parent
# setup environment variables
echo "NODEJS_PATH=$NODEJS_PATH" >> $env:GITHUB_ENV
# step output
echo "nodejs-path=$NODEJS_PATH"
echo "nodejs-path=$NODEJS_PATH" >> $env:GITHUB_OUTPUT
- name: Setup python - name: Setup python
id: setup-python id: setup-python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@ -149,10 +188,19 @@ jobs:
- name: Build Windows - name: Build Windows
shell: msys2 {0} shell: msys2 {0}
env: env:
# MSYSTEM is a built-in environment variable of MSYS2.
# Do not use this environment variable name.
MATRIX_MSYSTEM: ${{ matrix.msystem }}
BRANCH: ${{ github.head_ref || github.ref_name }} BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ inputs.release_version }} BUILD_VERSION: ${{ inputs.release_version }}
COMMIT: ${{ inputs.release_commit }} COMMIT: ${{ inputs.release_commit }}
run: | run: |
# setup NodeJS PATH
if [[ "${MATRIX_MSYSTEM}" != "ucrt64" ]]; then
NODEJS_PATH=$(cygpath "$NODEJS_PATH")
export PATH="$PATH:$NODEJS_PATH"
fi
mkdir -p build mkdir -p build
cmake \ cmake \
-B build \ -B build \
@ -176,12 +224,24 @@ jobs:
# package # package
cpack -G NSIS cpack -G NSIS
cpack -G WIX
cpack -G ZIP cpack -G ZIP
# move # move
mv ./cpack_artifacts/Sunshine.exe ../artifacts/Sunshine-${{ matrix.name }}-installer.exe mv ./cpack_artifacts/Sunshine.exe ../artifacts/Sunshine-${{ matrix.name }}-installer.exe
mv ./cpack_artifacts/Sunshine.msi ../artifacts/Sunshine-${{ matrix.name }}-installer.msi
mv ./cpack_artifacts/Sunshine.zip ../artifacts/Sunshine-${{ matrix.name }}-portable.zip mv ./cpack_artifacts/Sunshine.zip ../artifacts/Sunshine-${{ matrix.name }}-portable.zip
- name: Debug nsis
if: always()
shell: msys2 {0}
run: cat ./build/cpack_artifacts/_CPack_Packages/win64/NSIS/NSISOutput.log || true
- name: Debug wix
if: always()
shell: msys2 {0}
run: cat ./build/cpack_artifacts/_CPack_Packages/win64/WIX/wix.log || true
- name: Run tests - name: Run tests
id: test id: test
shell: msys2 {0} shell: msys2 {0}

View file

@ -181,6 +181,9 @@ jobs:
- name: Windows-AMD64 - name: Windows-AMD64
coverage: true coverage: true
pr: true pr: true
- name: Windows-ARM64
coverage: true
pr: true
steps: steps:
- name: Should run - name: Should run
id: should_run id: should_run
@ -202,18 +205,7 @@ jobs:
name: coverage-${{ matrix.name }} name: coverage-${{ matrix.name }}
path: _coverage path: _coverage
- name: Upload test results - name: Upload test coverage
if: steps.should_run.outputs.SHOULD_RUN == 'true'
uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1.2.1
with:
disable_search: true
fail_ci_if_error: true
files: ./_coverage/tests/test_results.xml
flags: ${{ matrix.name }}
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
- name: Upload coverage
if: | if: |
steps.should_run.outputs.SHOULD_RUN == 'true' && steps.should_run.outputs.SHOULD_RUN == 'true' &&
matrix.coverage != false matrix.coverage != false
@ -222,6 +214,19 @@ jobs:
disable_search: true disable_search: true
fail_ci_if_error: true fail_ci_if_error: true
files: ./_coverage/coverage.xml files: ./_coverage/coverage.xml
report_type: coverage
flags: ${{ matrix.name }}
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
- name: Upload test results
if: steps.should_run.outputs.SHOULD_RUN == 'true'
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:
disable_search: true
fail_ci_if_error: true
files: ./_coverage/tests/test_results.xml
report_type: test_results
flags: ${{ matrix.name }} flags: ${{ matrix.name }}
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
verbose: true verbose: true

View file

@ -9,6 +9,13 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
# gcc complains about misleading indentation in some mingw includes # gcc complains about misleading indentation in some mingw includes
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-misleading-indentation) list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-misleading-indentation)
# Disable warnings for Windows ARM64
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-dll-attribute-on-redeclaration) # Boost
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-unknown-warning-option) # ViGEmClient
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wno-unused-variable) # Boost
endif()
# see gcc bug 98723 # see gcc bug 98723
add_definitions(-DUSE_BOOST_REGEX) add_definitions(-DUSE_BOOST_REGEX)

View file

@ -1,9 +1,35 @@
# windows specific dependencies # windows specific dependencies
# Make sure MinHook is installed # MinHook setup - use installed minhook for AMD64, otherwise download minhook-detours for ARM64
find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED) if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED) # Make sure MinHook is installed for x86/x64
find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED)
find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED)
add_library(minhook::minhook STATIC IMPORTED) add_library(minhook::minhook STATIC IMPORTED)
set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY}) set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY})
target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR}) target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR})
else()
# Download pre-built minhook-detours for ARM64
message(STATUS "Downloading minhook-detours pre-built binaries for ARM64")
include(FetchContent)
FetchContent_Declare(
minhook-detours
URL https://github.com/m417z/minhook-detours/releases/download/v1.0.6/minhook-detours-1.0.6.zip
URL_HASH SHA256=E719959D824511E27395A82AEDA994CAAD53A67EE5894BA5FC2F4BF1FA41E38E
)
FetchContent_MakeAvailable(minhook-detours)
# Create imported library for the pre-built DLL
set(_MINHOOK_DLL
"${minhook-detours_SOURCE_DIR}/Release/minhook-detours.ARM64.Release.dll"
CACHE INTERNAL "Path to minhook-detours DLL")
add_library(minhook::minhook SHARED IMPORTED GLOBAL)
set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION "${_MINHOOK_DLL}")
set_property(TARGET minhook::minhook PROPERTY IMPORTED_IMPLIB
"${minhook-detours_SOURCE_DIR}/Release/minhook-detours.ARM64.Release.lib")
set_target_properties(minhook::minhook PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${minhook-detours_SOURCE_DIR}/src"
)
endif()

View file

@ -4,6 +4,11 @@ install(TARGETS sunshine RUNTIME DESTINATION "." COMPONENT application)
# Hardening: include zlib1.dll (loaded via LoadLibrary() in openssl's libcrypto.a) # Hardening: include zlib1.dll (loaded via LoadLibrary() in openssl's libcrypto.a)
install(FILES "${ZLIB}" DESTINATION "." COMPONENT application) install(FILES "${ZLIB}" DESTINATION "." COMPONENT application)
# ARM64: include minhook-detours DLL (shared library for ARM64)
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND DEFINED _MINHOOK_DLL)
install(FILES "${_MINHOOK_DLL}" DESTINATION "." COMPONENT application)
endif()
# ViGEmBus installer # ViGEmBus installer
set(VIGEMBUS_INSTALLER "${CMAKE_BINARY_DIR}/scripts/vigembus_installer.exe") set(VIGEMBUS_INSTALLER "${CMAKE_BINARY_DIR}/scripts/vigembus_installer.exe")
set(VIGEMBUS_DOWNLOAD_URL_1 "https://github.com/nefarius/ViGEmBus/releases/download") set(VIGEMBUS_DOWNLOAD_URL_1 "https://github.com/nefarius/ViGEmBus/releases/download")
@ -28,6 +33,9 @@ install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio)
install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT application) install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT application)
# Mandatory scripts # Mandatory scripts
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/sunshine-setup.ps1"
DESTINATION "scripts"
COMPONENT assets)
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/" install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/"
DESTINATION "scripts" DESTINATION "scripts"
COMPONENT assets) COMPONENT assets)

View file

@ -3,36 +3,42 @@
set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}") set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}")
# Enable detailed logging only on AMD64
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
set(NSIS_LOGSET_COMMAND "LogSet on")
else()
set(NSIS_LOGSET_COMMAND "")
endif()
# Extra install commands # Extra install commands
# Restores permissions on the install directory # Runs the main setup script which handles all installation tasks
# Migrates config files from the root into the new config folder
# Install service
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
IfSilent +2 0 ${NSIS_LOGSET_COMMAND}
ExecShell 'open' 'https://docs.lizardbyte.dev/projects/sunshine' IfSilent +3 0
nsExec::ExecToLog 'icacls \\\"$INSTDIR\\\" /reset' nsExec::ExecToLog \
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" add' 'powershell -ExecutionPolicy Bypass \
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\migrate-config.bat\\\"' -File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action install'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"' Goto +2
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"' nsExec::ExecToLog \
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\autostart-service.bat\\\"' 'powershell -ExecutionPolicy Bypass \
NoController: -File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action install -Silent'
install_done:
") ")
# Extra uninstall commands # Extra uninstall commands
# Uninstall service # Runs the main setup script which handles all uninstallation tasks
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"' ${NSIS_LOGSET_COMMAND}
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"' nsExec::ExecToLog \
nsExec::ExecToLog '\\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" --restore-nvprefs-undo' 'powershell -ExecutionPolicy Bypass \
-File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action uninstall'
MessageBox MB_YESNO|MB_ICONQUESTION \ MessageBox MB_YESNO|MB_ICONQUESTION \
'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \ 'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \
/SD IDNO IDNO NoDelete /SD IDNO IDNO no_delete
RMDir /r \\\"$INSTDIR\\\"; skipped if no RMDir /r \\\"$INSTDIR\\\"; skipped if no
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" remove' no_delete:
NoDelete:
") ")
# Adding an option for the start menu # Adding an option for the start menu

View file

@ -1,4 +1,97 @@
# WIX Packaging # WIX Packaging
# see options at: https://cmake.org/cmake/help/latest/cpack_gen/wix.html # see options at: https://cmake.org/cmake/help/latest/cpack_gen/wix.html
# TODO: Replace nsis with wix # find dotnet
find_program(DOTNET_EXECUTABLE dotnet HINTS "C:/Program Files/dotnet")
if(NOT DOTNET_EXECUTABLE)
message(WARNING "Dotnet executable not found, skipping WiX packaging.")
return()
endif()
set(CPACK_WIX_VERSION 4)
set(WIX_VERSION 4.0.4)
set(WIX_UI_VERSION 4.0.4) # extension versioning is independent of the WiX version
set(WIX_BUILD_PARENT_DIRECTORY "${CMAKE_BINARY_DIR}/wix_packaging")
set(WIX_BUILD_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/win64/WIX")
# Download and install WiX tools locally in the build directory
set(WIX_TOOL_PATH "${CMAKE_BINARY_DIR}/.wix")
file(MAKE_DIRECTORY ${WIX_TOOL_PATH})
# Install WiX locally using dotnet
execute_process(
COMMAND ${DOTNET_EXECUTABLE} tool install --tool-path ${WIX_TOOL_PATH} wix --version ${WIX_VERSION}
ERROR_VARIABLE WIX_INSTALL_OUTPUT
RESULT_VARIABLE WIX_INSTALL_RESULT
)
if(NOT WIX_INSTALL_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to install WiX tools locally.
WiX packaging may not work correctly, error: ${WIX_INSTALL_OUTPUT}")
endif()
# Install WiX UI Extension
execute_process(
COMMAND "${WIX_TOOL_PATH}/wix" extension add WixToolset.UI.wixext/${WIX_UI_VERSION}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
ERROR_VARIABLE WIX_UI_INSTALL_OUTPUT
RESULT_VARIABLE WIX_UI_INSTALL_RESULT
)
if(NOT WIX_UI_INSTALL_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to install WiX UI extension, error: ${WIX_UI_INSTALL_OUTPUT}")
endif()
# Install WiX Util Extension
execute_process(
COMMAND "${WIX_TOOL_PATH}/wix" extension add WixToolset.Util.wixext/${WIX_UI_VERSION}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
ERROR_VARIABLE WIX_UTIL_INSTALL_OUTPUT
RESULT_VARIABLE WIX_UTIL_INSTALL_RESULT
)
if(NOT WIX_UTIL_INSTALL_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to install WiX Util extension, error: ${WIX_UTIL_INSTALL_OUTPUT}")
endif()
# Set WiX-specific variables
set(CPACK_WIX_ROOT "${WIX_TOOL_PATH}")
set(CPACK_WIX_UPGRADE_GUID "512A3D1B-BE16-401B-A0D1-59BBA3942FB8")
# Installer metadata
set(CPACK_WIX_HELP_LINK "https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html")
set(CPACK_WIX_PRODUCT_ICON "${SUNSHINE_ICON_PATH}")
set(CPACK_WIX_PRODUCT_URL "${CMAKE_PROJECT_HOMEPAGE_URL}")
set(CPACK_WIX_PROGRAM_MENU_FOLDER "LizardByte")
set(CPACK_WIX_EXTENSIONS
"WixToolset.UI.wixext"
"WixToolset.Util.wixext"
)
message(STATUS "cpack package directory: ${CPACK_PACKAGE_DIRECTORY}")
# copy custom wxs files to the build directory
file(COPY "${CMAKE_CURRENT_LIST_DIR}/wix_resources/"
DESTINATION "${WIX_BUILD_PARENT_DIRECTORY}/")
set(CPACK_WIX_EXTRA_SOURCES
"${WIX_BUILD_PARENT_DIRECTORY}/sunshine-installer.wxs"
)
set(CPACK_WIX_PATCH_FILE
"${WIX_BUILD_PARENT_DIRECTORY}/patch.xml"
)
# Copy root LICENSE and rename to have .txt extension
file(COPY "${CMAKE_SOURCE_DIR}/LICENSE"
DESTINATION "${CMAKE_BINARY_DIR}")
file(RENAME "${CMAKE_BINARY_DIR}/LICENSE" "${CMAKE_BINARY_DIR}/LICENSE.txt")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_BINARY_DIR}/LICENSE.txt") # cpack will covert this to an RTF if it is txt
# https://cmake.org/cmake/help/latest/cpack_gen/wix.html#variable:CPACK_WIX_ARCHITECTURE
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
set(CPACK_WIX_ARCHITECTURE "arm64")
else()
set(CPACK_WIX_ARCHITECTURE "x64")
endif()

View file

@ -0,0 +1,5 @@
<CPackWiXPatch>
<CPackWiXFragment Id="CM_G_Core">
<FeatureRef Id="RunSunshineInstallScripts"/>
</CPackWiXFragment>
</CPackWiXPatch>

View file

@ -0,0 +1,83 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<Fragment>
<StandardDirectory Id="ProgramMenuFolder">
<Component Id="ApplicationShortcutRoot" Guid="*">
<Shortcut Id="ApplicationStartMenuShortcutRoot"
Name="Sunshine"
Description="Sunshine Game Stream Host"
Target="[INSTALL_ROOT]sunshine.exe"
Arguments="--shortcut"
WorkingDirectory="INSTALL_ROOT"/>
<RegistryValue Root="HKCU" Key="Software\LizardByte\Sunshine" Name="installed_root" Type="integer" Value="1" KeyPath="yes"/>
</Component>
<Directory Id="ProgramMenuSubfolder" Name="LizardByte">
<Directory Id="ProgramMenuSunshineFolder" Name="Sunshine">
<Component Id="ApplicationShortcut" Guid="*">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="Sunshine"
Description="Sunshine Game Stream Host"
Target="[INSTALL_ROOT]sunshine.exe"
Arguments="--shortcut"
WorkingDirectory="INSTALL_ROOT"/>
<RemoveFolder Id="CleanUpShortCut" Directory="ProgramMenuSunshineFolder" On="uninstall"/>
<RemoveFolder Id="CleanUpShortCutParent" Directory="ProgramMenuSubfolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\LizardByte\Sunshine" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
<Component Id="DocumentationShortcut" Guid="*">
<util:InternetShortcut Id="DocumentationLink"
Name="Sunshine Documentation"
Target="https://docs.lizardbyte.dev/projects/sunshine"
Type="url"/>
<RemoveFolder Id="CleanUpDocsShortCut" Directory="ProgramMenuSunshineFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\LizardByte\Sunshine" Name="docs_shortcut" Type="integer" Value="1" KeyPath="yes"/>
</Component>
<Component Id="WebsiteShortcut" Guid="*">
<util:InternetShortcut Id="WebsiteLink"
Name="LizardByte Web Site"
Target="https://app.lizardbyte.dev"
Type="url"/>
<RemoveFolder Id="CleanUpWebsiteShortCut" Directory="ProgramMenuSunshineFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\LizardByte\Sunshine" Name="website_shortcut" Type="integer" Value="1" KeyPath="yes"/>
</Component>
<Component Id="SupportShortcut" Guid="*">
<util:InternetShortcut Id="SupportLink"
Name="LizardByte Support"
Target="https://app.lizardbyte.dev/support"
Type="url"/>
<RemoveFolder Id="CleanUpSupportShortCut" Directory="ProgramMenuSunshineFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\LizardByte\Sunshine" Name="support_shortcut" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
</StandardDirectory>
<!-- Install: Run sunshine-setup.ps1 with -Action install, add -Silent if UILevel <= 3 (silent/basic UI) -->
<CustomAction Id="CA_SunshineInstall" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\sunshine-setup.ps1&quot; -Action install" Execute="deferred" Return="ignore" Impersonate="no" />
<CustomAction Id="CA_SunshineInstallSilent" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\sunshine-setup.ps1&quot; -Action install -Silent" Execute="deferred" Return="ignore" Impersonate="no" />
<!-- Uninstall: Run sunshine-setup.ps1 with -Action uninstall, add -Silent if UILevel <= 3 (silent/basic UI) -->
<CustomAction Id="CA_SunshineUninstall" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\sunshine-setup.ps1&quot; -Action uninstall" Execute="deferred" Return="ignore" Impersonate="no" />
<CustomAction Id="CA_SunshineUninstallSilent" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\sunshine-setup.ps1&quot; -Action uninstall -Silent" Execute="deferred" Return="ignore" Impersonate="no" />
<InstallExecuteSequence>
<!-- Run installation script after files are installed -->
<!-- UILevel > 3 means Full UI (interactive), UILevel <= 3 means Silent/Basic UI -->
<Custom Action="CA_SunshineInstall" After="InstallFiles" Condition="NOT Installed AND UILevel &gt; 3" />
<Custom Action="CA_SunshineInstallSilent" After="InstallFiles" Condition="NOT Installed AND UILevel &lt;= 3" />
<!-- Run uninstallation script before files are removed -->
<Custom Action="CA_SunshineUninstall" Before="RemoveFiles" Condition="REMOVE=&quot;ALL&quot; AND UILevel &gt; 3" />
<Custom Action="CA_SunshineUninstallSilent" Before="RemoveFiles" Condition="REMOVE=&quot;ALL&quot; AND UILevel &lt;= 3" />
</InstallExecuteSequence>
<!-- We need this in order to actually run our custom actions, but let's hide it -->
<Feature Id="RunSunshineInstallScripts" Title="Run Sunshine Installation Scripts" Level="1" Display="hidden">
<ComponentRef Id="ApplicationShortcutRoot" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="DocumentationShortcut" />
<ComponentRef Id="WebsiteShortcut" />
<ComponentRef Id="SupportShortcut" />
</Feature>
</Fragment>
</Wix>

View file

@ -56,11 +56,11 @@ else()
if(NOT GIT_DESCRIBE_ERROR_CODE) if(NOT GIT_DESCRIBE_ERROR_CODE)
MESSAGE("Sunshine Branch: ${GIT_DESCRIBE_BRANCH}") MESSAGE("Sunshine Branch: ${GIT_DESCRIBE_BRANCH}")
if(NOT GIT_DESCRIBE_BRANCH STREQUAL "master") if(NOT GIT_DESCRIBE_BRANCH STREQUAL "master")
set(PROJECT_VERSION ${PROJECT_VERSION}.${GIT_DESCRIBE_VERSION}) set(PROJECT_VERSION ${PROJECT_VERSION}-${GIT_DESCRIBE_VERSION})
MESSAGE("Sunshine Version: ${GIT_DESCRIBE_VERSION}") MESSAGE("Sunshine Version: ${GIT_DESCRIBE_VERSION}")
endif() endif()
if(GIT_IS_DIRTY) if(GIT_IS_DIRTY)
set(PROJECT_VERSION ${PROJECT_VERSION}.dirty) set(PROJECT_VERSION ${PROJECT_VERSION}-dirty)
MESSAGE("Git tree is dirty!") MESSAGE("Git tree is dirty!")
endif() endif()
else() else()

View file

@ -31,7 +31,7 @@ PROJECT_NAME = Sunshine
# project specific settings # project specific settings
DOT_GRAPH_MAX_NODES = 60 DOT_GRAPH_MAX_NODES = 60
# IMAGE_PATH = ../docs/images IMAGE_PATH = ../docs/images
PREDEFINED += SUNSHINE_BUILD_WAYLAND PREDEFINED += SUNSHINE_BUILD_WAYLAND
PREDEFINED += SUNSHINE_TRAY=1 PREDEFINED += SUNSHINE_TRAY=1

View file

@ -126,36 +126,60 @@ sudo port install "${dependencies[@]}"
``` ```
#### Windows #### Windows
First you need to install [MSYS2](https://www.msys2.org), then startup "MSYS2 UCRT64" and execute the following
commands. > [!WARNING]
> Cross-compilation is not supported on Windows. You must build on the target architecture.
First, you need to install [MSYS2](https://www.msys2.org).
For AMD64 startup "MSYS2 UCRT64" (or for ARM64 startup "MSYS2 CLANGARM64") then execute the following commands.
##### Update all packages ##### Update all packages
```bash ```bash
pacman -Syu pacman -Syu
``` ```
##### Set toolchain variable
For UCRT64:
```bash
export TOOLCHAIN="ucrt-x86_64"
```
For CLANGARM64:
```bash
export TOOLCHAIN="clang-aarch64"
```
##### Install dependencies ##### Install dependencies
```bash ```bash
dependencies=( dependencies=(
"git" "git"
"mingw-w64-ucrt-x86_64-boost" # Optional "mingw-w64-${TOOLCHAIN}-boost" # Optional
"mingw-w64-ucrt-x86_64-cmake" "mingw-w64-${TOOLCHAIN}-cmake"
"mingw-w64-ucrt-x86_64-cppwinrt" "mingw-w64-${TOOLCHAIN}-cppwinrt"
"mingw-w64-ucrt-x86_64-curl-winssl" "mingw-w64-${TOOLCHAIN}-curl-winssl"
"mingw-w64-ucrt-x86_64-doxygen" # Optional, for docs... better to install official Doxygen "mingw-w64-${TOOLCHAIN}-doxygen" # Optional, for docs... better to install official Doxygen
"mingw-w64-ucrt-x86_64-graphviz" # Optional, for docs "mingw-w64-${TOOLCHAIN}-graphviz" # Optional, for docs
"mingw-w64-ucrt-x86_64-MinHook" "mingw-w64-${TOOLCHAIN}-miniupnpc"
"mingw-w64-ucrt-x86_64-miniupnpc" "mingw-w64-${TOOLCHAIN}-onevpl"
"mingw-w64-ucrt-x86_64-nodejs" "mingw-w64-${TOOLCHAIN}-openssl"
"mingw-w64-ucrt-x86_64-nsis" "mingw-w64-${TOOLCHAIN}-opus"
"mingw-w64-ucrt-x86_64-onevpl" "mingw-w64-${TOOLCHAIN}-toolchain"
"mingw-w64-ucrt-x86_64-openssl"
"mingw-w64-ucrt-x86_64-opus"
"mingw-w64-ucrt-x86_64-toolchain"
) )
if [[ "${MSYSTEM}" == "UCRT64" ]]; then
dependencies+=(
"mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-nodejs"
"mingw-w64-${TOOLCHAIN}-nsis"
)
fi
pacman -S "${dependencies[@]}" pacman -S "${dependencies[@]}"
``` ```
To create a WiX installer, you also need to install [.NET](https://dotnet.microsoft.com/download).
For ARM64: To build frontend, you also need to install [Node.JS](https://nodejs.org/en/download)
### Clone ### Clone
Ensure [git](https://git-scm.com) is installed on your system, then clone the repository using the following command: Ensure [git](https://git-scm.com) is installed on your system, then clone the repository using the following command:
@ -198,9 +222,12 @@ ninja -C build
```} ```}
}} }}
@tab{Windows | @tabs{ @tab{Windows | @tabs{
@tab{Installer | ```bash @tab{NSIS Installer | ```bash
cpack -G NSIS --config ./build/CPackConfig.cmake cpack -G NSIS --config ./build/CPackConfig.cmake
```} ```}
@tab{WiX Installer | ```bash
cpack -G WIX --config ./build/CPackConfig.cmake
```}
@tab{Portable | ```bash @tab{Portable | ```bash
cpack -G ZIP --config ./build/CPackConfig.cmake cpack -G ZIP --config ./build/CPackConfig.cmake
```} ```}

View file

@ -100,22 +100,10 @@ CUDA is used for NVFBC capture.
> [!CAUTION] > [!CAUTION]
> Use distro-specific packages instead of the AppImage if they are available. > Use distro-specific packages instead of the AppImage if they are available.
> AppImage does not support KMS capture.
According to AppImageLint the supported distro matrix of the AppImage is below. > [!NOTE]
> The AppImage is built on Ubuntu 22.04, which requires `glibc 2.35` or newer and `libstdc++ 3.4.11` or newer.
- ✖ Debian bullseye
- ✔ Debian bookworm
- ✔ Debian trixie
- ✔ Debian sid
- ✔ Ubuntu plucky
- ✔ Ubuntu noble
- ✔ Ubuntu jammy
- ✖ Ubuntu focal
- ✖ Ubuntu bionic
- ✖ Ubuntu xenial
- ✖ Ubuntu trusty
- ✖ Rocky Linux 8
- ✖ Rocky Linux 9
##### Install ##### Install
1. Download [sunshine.AppImage](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage) 1. Download [sunshine.AppImage](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage)
@ -214,6 +202,12 @@ sudo dnf remove sunshine
``` ```
##### Install (Copr) ##### Install (Copr)
> [!IMPORTANT]
> Stable builds are only available if the Sunshine release was made after the Fedora version release.
> Because of this, it is often recommended to use the beta copr; however, you do not need to regularly update.
> This could lead to annoyances in rare cases where there may be a breaking change.
1. Enable copr repository. 1. Enable copr repository.
```bash ```bash
sudo dnf copr enable lizardbyte/stable sudo dnf copr enable lizardbyte/stable
@ -238,6 +232,7 @@ sudo dnf remove Sunshine
> [!CAUTION] > [!CAUTION]
> Use distro-specific packages instead of the Flatpak if they are available. > Use distro-specific packages instead of the Flatpak if they are available.
> Flatpak does not support KMS capture.
Using this package requires that you have [Flatpak](https://flatpak.org/setup) installed. Using this package requires that you have [Flatpak](https://flatpak.org/setup) installed.
@ -305,6 +300,9 @@ brew install sunshine
brew uninstall sunshine brew uninstall sunshine
``` ```
> [!TIP]
> For beta you can replace `sunshine` with `sunshine-beta` in the above commands.
### macOS ### macOS
> [!IMPORTANT] > [!IMPORTANT]
@ -315,6 +313,8 @@ This package requires that you have [Homebrew](https://docs.brew.sh/Installation
##### Install ##### Install
```bash ```bash
brew update
brew upgrade
brew tap LizardByte/homebrew brew tap LizardByte/homebrew
brew install sunshine brew install sunshine
``` ```
@ -329,10 +329,31 @@ brew uninstall sunshine
### Windows ### Windows
> [!NOTE]
> Sunshine supports ARM64 on Windows; however, this should be considered experimental. This version does not properly
> support GPU scheduling and any hardware acceleration.
#### Installer (recommended) #### Installer (recommended)
1. Download and install > [!CAUTION]
[Sunshine-Windows-AMD64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.exe) > The msi installer is preferred moving forward. Before using a different type of installer, you should manually
> uninstall the previous installation.
1. Download and install based on your architecture:
| Architecture | Installer |
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-installer.msi](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.msi) |
| AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.exe) |
| ARM64 | [Sunshine-Windows-ARM64-installer.msi](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-installer.msi) |
| ARM64 | [Sunshine-Windows-ARM64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-installer.exe) |
> [!TIP]
> Installer logs can be found in the following locations.<br>
> | File | log paths |
> | ---- | --------- |
> | .exe | `%%PROGRAMFILES%/Sunshine/install.log` (AMD64 only)<br>`%%TEMP%/Sunshine/logs/install/` |
> | .msi | `%%TEMP%/Sunshine/logs/install/` |
> [!CAUTION] > [!CAUTION]
> You should carefully select or unselect the options you want to install. Do not blindly install or > You should carefully select or unselect the options you want to install. Do not blindly install or
@ -347,8 +368,13 @@ overflow menu. Different versions of Windows may provide slightly different step
> By using this package instead of the installer, performance will be reduced. This package is not > 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! > recommended for most users. No support will be provided!
1. Download and extract 1. Download and extract based on your architecture:
[Sunshine-Windows-AMD64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-portable.zip)
| Architecture | Installer |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
| AMD64/x64 (Intel/AMD) | [Sunshine-Windows-AMD64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-portable.zip) |
| ARM64 | [Sunshine-Windows-ARM64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-ARM64-portable.zip) |
2. Open command prompt as administrator 2. Open command prompt as administrator
3. Firewall rules 3. Firewall rules
@ -435,6 +461,8 @@ In order for virtual gamepads to work, you must install ViGEmBus. You can do thi
in the web UI, as long as you are running Sunshine as a service or as an administrator. After installation, it is in the web UI, as long as you are running Sunshine as a service or as an administrator. After installation, it is
recommended to restart your computer. recommended to restart your computer.
![ViGEmBus Installation](images/vigembus-installer.png)
## Usage ## Usage
### Basic usage ### Basic usage
@ -455,9 +483,8 @@ sunshine <directory of conf file>/sunshine.conf
``` ```
> [!NOTE] > [!NOTE]
> You do not need to specify a config file. If no config file is entered, the default location will be used. > This step is optional, you do not need to specify a config file.
> If no config file is entered, the default location will be used.
> [!TIP]
> The configuration file specified will be created if it doesn't exist. > The configuration file specified will be created if it doesn't exist.
### Start Sunshine over SSH (Linux/X11) ### Start Sunshine over SSH (Linux/X11)
@ -493,16 +520,27 @@ by default. You may replace *localhost* with your internal ip address.
> [!CAUTION] > [!CAUTION]
> If running for the first time, make sure to note the username and password that you created. > If running for the first time, make sure to note the username and password that you created.
1. Add games and applications. 1. Change the web-ui to your desired theme, using the dropdown menu in the navbar.
2. Adjust any configuration settings as needed. ![Theme Selection](images/split-themes.png)
3. In Moonlight, you may need to add the PC manually. 2. Add games and applications.
4. When Moonlight requests for you insert the pin: ![Applications](images/applications.png)
3. Adjust any configuration settings as needed. You can search for options in the search bar.
![Configuration](images/configuration-search.png)
4. Find Moonlight clients and other tools for Sunshine in the `Featured Apps` tab.
![Featured Apps](images/featured-apps.png)
5. In Moonlight, you may need to add the PC manually.
6. When Moonlight requests for you insert the pin:
- Login to the web ui - Login to the web-ui
- Go to "PIN" in the Navbar - Go to "PIN" in the Navbar
- Type in your PIN and press Enter, you should get a Success Message - Type in your PIN and press `Enter`, and enter a name of your choosing for the device.
You should get a Success Message!
- In Moonlight, select one of the Applications listed - In Moonlight, select one of the Applications listed
7. If you run into issues, logs are available in the `Troubleshooting` tab.
You can navigate through each warning/error message for clues to the issue.
![Logs](images/troubleshooting-logs.png)
### Arguments ### Arguments
To get a list of available arguments, run the following command. To get a list of available arguments, run the following command.

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -187,7 +187,7 @@ namespace config {
}; };
template<class T> template<class T>
std::optional<int> quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) { ::std::optional<int> quality_from_view(const ::std::string_view &quality_type, const ::std::optional<int>(&original)) {
#define _CONVERT_(x) \ #define _CONVERT_(x) \
if (quality_type == #x##sv) \ if (quality_type == #x##sv) \
return (int) T::x return (int) T::x
@ -199,7 +199,7 @@ namespace config {
} }
template<class T> template<class T>
std::optional<int> rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) { ::std::optional<int> rc_from_view(const ::std::string_view &rc, const ::std::optional<int>(&original)) {
#define _CONVERT_(x) \ #define _CONVERT_(x) \
if (rc == #x##sv) \ if (rc == #x##sv) \
return (int) T::x return (int) T::x
@ -212,7 +212,7 @@ namespace config {
} }
template<class T> template<class T>
std::optional<int> usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) { ::std::optional<int> usage_from_view(const ::std::string_view &usage, const ::std::optional<int>(&original)) {
#define _CONVERT_(x) \ #define _CONVERT_(x) \
if (usage == #x##sv) \ if (usage == #x##sv) \
return (int) T::x return (int) T::x
@ -225,7 +225,7 @@ namespace config {
return original; return original;
} }
int coder_from_view(const std::string_view &coder) { int coder_from_view(const ::std::string_view &coder) {
if (coder == "auto"sv) { if (coder == "auto"sv) {
return _auto; return _auto;
} }
@ -556,8 +556,8 @@ namespace config {
true, // client gamepads with touchpads are emulated as DS4 true, // client gamepads with touchpads are emulated as DS4
true, // ds5_inputtino_randomize_mac true, // ds5_inputtino_randomize_mac
true, // keyboard enabled false, // keyboard disabled (Parsec-style default)
true, // mouse enabled false, // mouse disabled (Parsec-style default)
true, // controller enabled true, // controller enabled
true, // always send scancodes true, // always send scancodes
true, // high resolution scrolling true, // high resolution scrolling

View file

@ -383,7 +383,7 @@ int main(int argc, char *argv[]) {
BOOST_LOG(info) << "Starting system tray"sv; BOOST_LOG(info) << "Starting system tray"sv;
#ifdef _WIN32 #ifdef _WIN32
// TODO: Windows has a weird bug where when running as a service and on the first Windows boot, // TODO: Windows has a weird bug where when running as a service and on the first Windows boot,
// he tray icon would not appear even though Sunshine is running correctly otherwise. // the tray icon would not appear even though Sunshine is running correctly otherwise.
// Restarting the service would allow the icon to appear normally. // Restarting the service would allow the icon to appear normally.
// For now we will keep the Windows tray icon on a separate thread. // For now we will keep the Windows tray icon on a separate thread.
// Ideally, we would run the system tray on the main thread for all platforms. // Ideally, we would run the system tray on the main thread for all platforms.

View file

@ -56,7 +56,6 @@ namespace nvenc {
autopop_context push_context(); autopop_context push_context();
HMODULE dll = nullptr;
const ID3D11DevicePtr d3d_device; const ID3D11DevicePtr d3d_device;
ID3D11Texture2DPtr d3d_input_texture; ID3D11Texture2DPtr d3d_input_texture;

View file

@ -1661,9 +1661,14 @@ namespace platf {
if (!fb->handles[0]) { if (!fb->handles[0]) {
BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv; BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv;
BOOST_LOG((config::video.capture == "kms") ? fatal : error) BOOST_LOG((config::video.capture == "kms") ? fatal : error)
<< "If you installed from AppImage or Flatpak, KMS capture is not supported.\n"sv #if defined(SUNSHINE_BUILD_FLATPAK) || defined(SUNSHINE_BUILD_APPIMAGE)
<< "AppImage and Flatpak do not support KMS capture. Use another capture method."sv;
#else
<< "You must use the 'sunshine-kms' service instead of the 'sunshine' service for KMS capture.\n"sv
<< "Please refer to the official documentation:\n"sv << "Please refer to the official documentation:\n"sv
<< "https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html#linux"sv; << " stable: https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html#linux-1"sv
<< " beta: https://docs.lizardbyte.dev/projects/sunshine/master/md_docs_2getting__started.html#linux-1"sv;
#endif
break; break;
} }

View file

@ -736,8 +736,9 @@ namespace portal {
struct spa_buffer *buf; struct spa_buffer *buf;
buf = stream_data.current_buffer->buffer; buf = stream_data.current_buffer->buffer;
if (buf->datas[0].chunk->size != 0) { if (buf->datas[0].chunk->size != 0) {
const auto img_descriptor = static_cast<egl::img_descriptor_t *>(img);
img_descriptor->frame_timestamp = std::chrono::steady_clock::now();
if (buf->datas[0].type == SPA_DATA_DmaBuf) { if (buf->datas[0].type == SPA_DATA_DmaBuf) {
const auto img_descriptor = static_cast<egl::img_descriptor_t *>(img);
img_descriptor->sd.width = stream_data.format.info.raw.size.width; img_descriptor->sd.width = stream_data.format.info.raw.size.width;
img_descriptor->sd.height = stream_data.format.info.raw.size.height; img_descriptor->sd.height = stream_data.format.info.raw.size.height;
img_descriptor->sd.modifier = stream_data.format.info.raw.modifier; img_descriptor->sd.modifier = stream_data.format.info.raw.modifier;

View file

@ -32,14 +32,14 @@ DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64)
#define STEAM_DRIVER_SUBDIR L"x64" #define STEAM_DRIVER_SUBDIR L"x64"
#else
#warning No known Steam audio driver for this architecture
#endif #endif
namespace { namespace {
constexpr auto SAMPLE_RATE = 48000; constexpr auto SAMPLE_RATE = 48000;
#ifdef STEAM_DRIVER_SUBDIR
constexpr auto STEAM_AUDIO_DRIVER_PATH = L"%CommonProgramFiles(x86)%\\Steam\\drivers\\Windows10\\" STEAM_DRIVER_SUBDIR L"\\SteamStreamingSpeakers.inf"; constexpr auto STEAM_AUDIO_DRIVER_PATH = L"%CommonProgramFiles(x86)%\\Steam\\drivers\\Windows10\\" STEAM_DRIVER_SUBDIR L"\\SteamStreamingSpeakers.inf";
#endif
constexpr auto waveformat_mask_stereo = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; constexpr auto waveformat_mask_stereo = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;

View file

@ -1907,6 +1907,12 @@ namespace platf::dxgi {
if (!boost::algorithm::ends_with(name, "_nvenc")) { if (!boost::algorithm::ends_with(name, "_nvenc")) {
return false; return false;
} }
} else if (adapter_desc.VendorId == 0x4D4F4351 || // Qualcomm (QCOM as MOQC reversed)
adapter_desc.VendorId == 0x5143) { // Qualcomm alternate ID
// If it's not a MediaFoundation encoder, it's not compatible with a Qualcomm GPU
if (!boost::algorithm::ends_with(name, "_mf")) {
return false;
}
} else { } else {
BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view(); BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view();
} }

View file

@ -10,6 +10,7 @@
// standard includes // standard includes
#include <cmath> #include <cmath>
#include <thread> #include <thread>
#include <vector>
// lib includes // lib includes
#include <ViGEm/Client.h> #include <ViGEm/Client.h>
@ -1134,9 +1135,9 @@ namespace platf {
void unicode(input_t &input, char *utf8, int size) { void unicode(input_t &input, char *utf8, int size) {
// We can do no worse than one UTF-16 character per byte of UTF-8 // We can do no worse than one UTF-16 character per byte of UTF-8
WCHAR wide[size]; std::vector<WCHAR> wide(size);
int chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size, wide, size); int chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size, wide.data(), size);
if (chars <= 0) { if (chars <= 0) {
return; return;
} }

View file

@ -9,6 +9,7 @@
#include <iterator> #include <iterator>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <vector>
// lib includes // lib includes
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -1383,7 +1384,7 @@ namespace platf {
auto const max_bufs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0); auto const max_bufs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0);
WSABUF bufs[(send_info.headers ? send_info.block_count : 1) * max_bufs_per_msg]; std::vector<WSABUF> bufs((send_info.headers ? send_info.block_count : 1) * max_bufs_per_msg);
DWORD bufcount = 0; DWORD bufcount = 0;
if (send_info.headers) { if (send_info.headers) {
// Interleave buffers for headers and payloads // Interleave buffers for headers and payloads
@ -1409,7 +1410,7 @@ namespace platf {
} }
} }
msg.lpBuffers = bufs; msg.lpBuffers = bufs.data();
msg.dwBufferCount = bufcount; msg.dwBufferCount = bufcount;
msg.dwFlags = 0; msg.dwFlags = 0;

View file

@ -41,4 +41,4 @@ BEGIN
END END
END END
SuperDuperAmazing ICON DISCARDABLE PROJECT_ICON_PATH SuperDuperAmazing ICON DISCARDABLE TOSTRING(PROJECT_ICON_PATH)

View file

@ -300,6 +300,7 @@ namespace video {
ALWAYS_REPROBE = 1 << 9, ///< This is an encoder of last resort and we want to aggressively probe for a better one ALWAYS_REPROBE = 1 << 9, ///< This is an encoder of last resort and we want to aggressively probe for a better one
YUV444_SUPPORT = 1 << 10, ///< Encoder may support 4:4:4 chroma sampling depending on hardware YUV444_SUPPORT = 1 << 10, ///< Encoder may support 4:4:4 chroma sampling depending on hardware
ASYNC_TEARDOWN = 1 << 11, ///< Encoder supports async teardown on a different thread ASYNC_TEARDOWN = 1 << 11, ///< Encoder supports async teardown on a different thread
FIXED_GOP_SIZE = 1 << 12, ///< Use fixed small GOP size (encoder doesn't support on-demand IDR frames)
}; };
class avcodec_encode_session_t: public encode_session_t { class avcodec_encode_session_t: public encode_session_t {
@ -825,6 +826,63 @@ namespace video {
}, },
PARALLEL_ENCODING PARALLEL_ENCODING
}; };
encoder_t mediafoundation {
"mediafoundation"sv,
std::make_unique<encoder_platform_formats_avcodec>(
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_NONE,
AV_PIX_FMT_D3D11,
AV_PIX_FMT_NV12, // SDR 4:2:0 8-bit (only format Qualcomm supports)
AV_PIX_FMT_NONE, // No HDR - Qualcomm MF only supports 8-bit
AV_PIX_FMT_NONE, // No YUV444 SDR
AV_PIX_FMT_NONE, // No YUV444 HDR
dxgi_init_avcodec_hardware_input_buffer
),
{
// Common options for AV1 - Qualcomm MF encoder
{
{"hw_encoding"s, 1},
{"rate_control"s, "cbr"s},
{"scenario"s, "display_remoting"s},
},
{}, // SDR-specific options
{}, // HDR-specific options
{}, // YUV444 SDR-specific options
{}, // YUV444 HDR-specific options
{}, // Fallback options
"av1_mf"s,
},
{
// Common options for HEVC - Qualcomm MF encoder
{
{"hw_encoding"s, 1},
{"rate_control"s, "cbr"s},
{"scenario"s, "display_remoting"s},
},
{}, // SDR-specific options
{}, // HDR-specific options
{}, // YUV444 SDR-specific options
{}, // YUV444 HDR-specific options
{}, // Fallback options
"hevc_mf"s,
},
{
// Common options for H.264 - Qualcomm MF encoder
{
{"hw_encoding"s, 1},
{"rate_control"s, "cbr"s},
{"scenario"s, "display_remoting"s},
},
{}, // SDR-specific options
{}, // HDR-specific options
{}, // YUV444 SDR-specific options
{}, // YUV444 HDR-specific options
{}, // Fallback options
"h264_mf"s,
},
PARALLEL_ENCODING | FIXED_GOP_SIZE // MF encoder doesn't support on-demand IDR frames
};
#endif #endif
encoder_t software { encoder_t software {
@ -1031,6 +1089,7 @@ namespace video {
#ifdef _WIN32 #ifdef _WIN32
&quicksync, &quicksync,
&amdvce, &amdvce,
&mediafoundation,
#endif #endif
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__) #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
&vaapi, &vaapi,
@ -1566,11 +1625,17 @@ namespace video {
ctx->max_b_frames = 0; ctx->max_b_frames = 0;
// Use an infinite GOP length since I-frames are generated on demand // Use an infinite GOP length since I-frames are generated on demand
ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ? // Exception: encoders with FIXED_GOP_SIZE flag don't support on-demand IDR
std::numeric_limits<std::int16_t>::max() : if (encoder.flags & FIXED_GOP_SIZE) {
std::numeric_limits<int>::max(); // Fixed GOP for encoders that don't support on-demand IDR (e.g. Media Foundation)
ctx->gop_size = 120; // ~2 seconds at 60 FPS - larger to reduce oversized IDR frame frequency
ctx->keyint_min = std::numeric_limits<int>::max(); ctx->keyint_min = 120;
} else {
ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ?
std::numeric_limits<std::int16_t>::max() :
std::numeric_limits<int>::max();
ctx->keyint_min = std::numeric_limits<int>::max();
}
// Some client decoders have limits on the number of reference frames // Some client decoders have limits on the number of reference frames
if (config.numRefFrames) { if (config.numRefFrames) {

View file

@ -220,6 +220,7 @@ namespace video {
#ifdef _WIN32 #ifdef _WIN32
extern encoder_t amdvce; extern encoder_t amdvce;
extern encoder_t quicksync; extern encoder_t quicksync;
extern encoder_t mediafoundation;
#endif #endif
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__) #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)

View file

@ -203,12 +203,12 @@
"touchpad_as_ds4": "enabled", "touchpad_as_ds4": "enabled",
"ds5_inputtino_randomize_mac": "enabled", "ds5_inputtino_randomize_mac": "enabled",
"back_button_timeout": -1, "back_button_timeout": -1,
"keyboard": "enabled", "keyboard": "disabled",
"key_repeat_delay": 500, "key_repeat_delay": 500,
"key_repeat_frequency": 24.9, "key_repeat_frequency": 24.9,
"always_send_scancodes": "enabled", "always_send_scancodes": "enabled",
"key_rightalt_to_key_win": "disabled", "key_rightalt_to_key_win": "disabled",
"mouse": "enabled", "mouse": "disabled",
"high_resolution_scrolling": "enabled", "high_resolution_scrolling": "enabled",
"native_pen_touch": "enabled", "native_pen_touch": "enabled",
"keybindings": "[0x10,0xA0,0x11,0xA2,0x12,0xA4]", // todo: add this to UI "keybindings": "[0x10,0xA0,0x11,0xA2,0x12,0xA4]", // todo: add this to UI

View file

@ -118,7 +118,7 @@ const config = ref(props.config)
id="keyboard" id="keyboard"
locale-prefix="config" locale-prefix="config"
v-model="config.keyboard" v-model="config.keyboard"
default="true" default="false"
></Checkbox> ></Checkbox>
<!-- Key Repeat Delay--> <!-- Key Repeat Delay-->
@ -161,7 +161,7 @@ const config = ref(props.config)
id="mouse" id="mouse"
locale-prefix="config" locale-prefix="config"
v-model="config.mouse" v-model="config.mouse"
default="true" default="false"
></Checkbox> ></Checkbox>
<!-- High resolution scrolling support --> <!-- High resolution scrolling support -->

View file

@ -0,0 +1,674 @@
# Sunshine Setup Script
# This script orchestrates the installation and uninstallation of Sunshine
# Usage: sunshine-setup.ps1 -Action [install|uninstall] [-Silent]
param(
[Parameter(Mandatory=$false)]
[ValidateSet(
"install",
"uninstall"
)]
[string]$Action,
[Parameter(Mandatory=$false)]
[switch]$Silent
)
# Constants
$DocsUrl = "https://docs.lizardbyte.dev/projects/sunshine"
# Set preference variables for output streams
$InformationPreference = 'Continue'
# Function to write output to both console (with color/stream) and log file (without color)
function Write-LogMessage {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '',
Justification='Write-Host is required for colored output')]
param(
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
[string]$Message,
[Parameter(Mandatory=$false)]
[ValidateSet(
'Debug',
'Error',
'Information',
'Step',
'Success',
'Verbose',
'Warning'
)]
[string]$Level = 'Information',
[Parameter(Mandatory=$false)]
[ValidateSet(
'Black',
'Blue',
'Cyan',
'DarkGray',
'Gray',
'Green',
'Magenta',
'Red',
'White',
'Yellow'
)]
[string]$Color = $null,
[Parameter(Mandatory=$false)]
[switch]$NoTimestamp,
[Parameter(Mandatory=$false)]
[switch]$NoLogFile
)
# Map levels to colors and output streams
$levelConfig = @{
'Debug' = @{ DefaultColor = 'DarkGray'; Stream = 'Debug'; Emoji = ''; LogLevel = 'DEBUG' }
'Error' = @{ DefaultColor = 'Red'; Stream = 'Error'; Emoji = '✗'; LogLevel = 'ERROR' }
'Information' = @{ DefaultColor = $null; Stream = 'Host'; Emoji = ''; LogLevel = 'INFO' }
'Step' = @{ DefaultColor = 'Cyan'; Stream = 'Host'; Emoji = '==>'; LogLevel = 'INFO' }
'Success' = @{ DefaultColor = 'Green'; Stream = 'Host'; Emoji = '✓'; LogLevel = 'INFO' }
'Verbose' = @{ DefaultColor = 'DarkGray'; Stream = 'Verbose'; Emoji = ''; LogLevel = 'VERBOSE' }
'Warning' = @{ DefaultColor = 'Yellow'; Stream = 'Warning'; Emoji = '⚠'; LogLevel = 'WARN' }
}
$config = $levelConfig[$Level]
# Use custom color if specified, otherwise use default color for the level
$displayColor = if ($Color) { $Color } else { $config.DefaultColor }
# Write to appropriate output stream with color
switch ($config.Stream) {
'Debug' {
Write-Debug $Message
}
'Error' {
Write-Error $Message
}
'Host' {
if ($null -ne $displayColor) {
Write-Host "$($config.Emoji) $Message" -ForegroundColor $displayColor
} else {
Write-Host "$($config.Emoji) $Message"
}
}
'Information' {
Write-Information $Message
}
'Verbose' {
Write-Verbose $Message
}
'Warning' {
Write-Warning $Message
}
default {
Write-Information $Message
}
}
# Write to log file without color codes (only if LogPath exists and not disabled)
if ($script:LogPath -and -not $NoLogFile) {
try {
# Format log entry with timestamp and level
if ($NoTimestamp) {
$logEntry = $Message
} else {
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$logEntry = "[$timestamp] [$($config.LogLevel)] $Message"
}
$logEntry | Out-File `
-FilePath $script:LogPath `
-Append `
-Encoding UTF8
} catch {
# Avoid infinite recursion - use Write-Verbose directly
Write-Verbose "Could not write to log file: $($_.Exception.Message)"
}
}
}
# Function to print a separator bar
function Write-Bar {
param(
[string]$Level = 'Information',
[int]$Length = 63,
[string]$Color = $null,
[switch]$NoTimestamp
)
$bar = "=" * $Length
if ($Color) {
Write-LogMessage -Message $bar -Level $Level -Color $Color -NoTimestamp:$NoTimestamp
} else {
Write-LogMessage -Message $bar -Level $Level -NoTimestamp:$NoTimestamp
}
}
# Function to print text framed by bars
function Write-FramedText {
param(
[string]$Message,
[string]$Level = 'Information',
[int]$BarLength = 63,
[string]$Color = $null,
[switch]$NoTimestamp,
[switch]$NoCenter
)
# Center the message if NoCenter is not specified
$displayMessage = $Message
if (-not $NoCenter) {
$messageLength = $Message.Trim().Length
if ($messageLength -lt $BarLength) {
$totalPadding = $BarLength - $messageLength
$leftPadding = [Math]::Floor($totalPadding / 2)
$displayMessage = (' ' * $leftPadding) + $Message.Trim()
} else {
$displayMessage = $Message.Trim()
}
}
if ($Color) {
Write-Bar -Level $Level -Length $BarLength -Color $Color -NoTimestamp:$NoTimestamp
Write-LogMessage -Message $displayMessage -Level $Level -Color $Color -NoTimestamp:$NoTimestamp
Write-Bar -Level $Level -Length $BarLength -Color $Color -NoTimestamp:$NoTimestamp
} else {
Write-Bar -Level $Level -Length $BarLength -NoTimestamp:$NoTimestamp
Write-LogMessage -Message $displayMessage -Level $Level -NoTimestamp:$NoTimestamp
Write-Bar -Level $Level -Length $BarLength -NoTimestamp:$NoTimestamp
}
}
# Function to write to log file (helper function)
function Write-LogFile {
param(
[string[]]$Lines
)
if ($script:LogPath) {
try {
foreach ($line in $Lines) {
$line | Out-File `
-FilePath $script:LogPath `
-Append `
-Encoding UTF8
}
} catch {
Write-Warning "Failed to write to log file: $($_.Exception.Message)"
}
}
}
# If Action is not provided, prompt the user
if (-not $Action) {
Write-Information ""
Write-FramedText -Message "🔅 Sunshine Setup Script" -Level "Information" -Color "Cyan"
Write-Information ""
Write-LogMessage -Message "Please select an action:" -Level "Information" -Color "Yellow"
Write-LogMessage -Message " 1. Install Sunshine" -Level "Information" -Color "Green"
Write-LogMessage -Message " 2. Uninstall Sunshine" -Level "Information" -Color "Red"
Write-Information ""
$validChoice = $false
while (-not $validChoice) {
$choice = Read-Host "Enter your choice (1 or 2)"
switch ($choice) {
"1" {
$Action = "install"
$validChoice = $true
}
"2" {
$Action = "uninstall"
$validChoice = $true
}
default {
Write-Warning "Invalid choice. Please select 1 or 2."
Write-Information ""
}
}
}
Write-Information ""
}
# Check if running as administrator, if not, relaunch with elevation
$currentPrincipal = New-Object `
Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Warning "This script requires administrator privileges. Relaunching with elevation..."
# Build the argument list for the elevated process
$arguments = "-ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`" -Action $Action"
if ($Silent) {
$arguments += " -Silent"
}
try {
# Relaunch the script with elevation
Start-Process powershell.exe -Verb RunAs -ArgumentList $arguments -Wait
exit $LASTEXITCODE
} catch {
Write-Error "Failed to elevate privileges: $($_.Exception.Message)"
exit 1
}
}
# Get the script directory and root directory
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$RootDir = Split-Path -Parent $ScriptDir
# Set up transcript logging
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logDir = Join-Path $env:TEMP "Sunshine\logs\$Action"
$LogPath = Join-Path $logDir "${timestamp}.log"
# Ensure the log directory exists
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
# Store LogPath in script scope for logging functions
$script:LogPath = $LogPath
# Function to execute a batch script if it exists
function Invoke-ScriptIfExist {
param(
[string]$ScriptPath,
[string]$Arguments = "",
[string]$Description = "",
[string]$Emoji = "🔧"
)
if ($Description) {
Write-LogMessage -Message "$Emoji $Description" -Level "Step"
}
if (Test-Path $ScriptPath) {
Write-LogMessage -Message "Executing: $ScriptPath $Arguments" -Level "Information"
# Capture output to suppress it from console but log it
$stdoutFile = [System.IO.Path]::GetTempFileName()
$stderrFile = [System.IO.Path]::GetTempFileName()
try {
if ($Arguments -ne "") {
$process = Start-Process `
-FilePath $ScriptPath `
-ArgumentList $Arguments `
-Wait `
-PassThru `
-NoNewWindow `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile
} else {
$process = Start-Process `
-FilePath $ScriptPath `
-Wait `
-PassThru `
-NoNewWindow `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile
}
# Log and display the output
if (Test-Path $stdoutFile) {
$output = Get-Content $stdoutFile -Raw -ErrorAction SilentlyContinue
if ($output) {
# Display output with indentation
$output -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Information" -Color "DarkGray"
}
}
}
}
if (Test-Path $stderrFile) {
$errors = Get-Content $stderrFile -Raw -ErrorAction SilentlyContinue
if ($errors) {
# Display errors with indentation
$errors -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Warning"
}
}
}
}
if ($process.ExitCode -ne 0) {
Write-LogMessage -Message " ⚠ Script exited with code $($process.ExitCode): $ScriptPath" -Level "Warning"
return $process.ExitCode
} else {
Write-LogMessage -Message " ✓ Done" -Level "Success"
return 0
}
} finally {
# Clean up temp files
if (Test-Path $stdoutFile) {
Remove-Item $stdoutFile -Force -ErrorAction SilentlyContinue
}
if (Test-Path $stderrFile) {
Remove-Item $stderrFile -Force -ErrorAction SilentlyContinue
}
}
} else {
Write-LogMessage -Message " ⓘ Skipped (script not found)" -Level "Information" -Color "DarkGray"
return 0
}
}
# Function to execute sunshine.exe with arguments if it exists
function Invoke-SunshineIfExist {
param(
[string]$Arguments,
[string]$Description = "",
[string]$Emoji = "🔧"
)
if ($Description) {
Write-LogMessage -Message "$Emoji $Description" -Level "Step"
}
$SunshinePath = Join-Path $RootDir "sunshine.exe"
if (Test-Path $SunshinePath) {
Write-LogMessage -Message "Executing: $SunshinePath $Arguments" -Level "Information"
# Capture output to suppress it from console but log it
$stdoutFile = [System.IO.Path]::GetTempFileName()
$stderrFile = [System.IO.Path]::GetTempFileName()
try {
$process = Start-Process `
-FilePath $SunshinePath `
-ArgumentList $Arguments `
-Wait `
-PassThru `
-NoNewWindow `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile
# Log and display the output
if (Test-Path $stdoutFile) {
$output = Get-Content $stdoutFile -Raw -ErrorAction SilentlyContinue
if ($output) {
# Display output with indentation
$output -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Information" -Color "DarkGray"
}
}
}
}
if (Test-Path $stderrFile) {
$errors = Get-Content $stderrFile -Raw -ErrorAction SilentlyContinue
if ($errors) {
# Display errors with indentation
$errors -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Warning"
}
}
}
}
if ($process.ExitCode -ne 0) {
Write-LogMessage -Message " ⚠ Sunshine exited with code $($process.ExitCode)" -Level "Warning"
return $process.ExitCode
} else {
Write-LogMessage -Message " ✓ Done" -Level "Success"
return 0
}
} finally {
# Clean up temp files
if (Test-Path $stdoutFile) {
Remove-Item $stdoutFile -Force -ErrorAction SilentlyContinue
}
if (Test-Path $stderrFile) {
Remove-Item $stderrFile -Force -ErrorAction SilentlyContinue
}
}
} else {
Write-LogMessage -Message " ⓘ Skipped (executable not found)" -Level "Information" -Color "DarkGray"
return 0
}
}
# Main script logic
Write-Information ""
if ($Action -eq "install") {
Write-FramedText `
-Message "🔅 Sunshine Installation Script" `
-Level "Information" `
-Color "Yellow"
Write-Information ""
$totalSteps = 6
$currentStep = 0
# Reset permissions on the install directory
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Resetting permissions on installation directory" `
-PercentComplete (($currentStep / $totalSteps) * 100)
Write-LogMessage -Message "🔐 Resetting permissions on installation directory" -Level "Step"
try {
Write-LogMessage -Message "Executing: icacls.exe `"$RootDir`" /reset" -Level "Information"
# Capture output to suppress it from console but log it
$stdoutFile = [System.IO.Path]::GetTempFileName()
$stderrFile = [System.IO.Path]::GetTempFileName()
try {
$icaclsProcess = Start-Process `
-FilePath "icacls.exe" `
-ArgumentList "`"$RootDir`" /reset" `
-Wait `
-PassThru `
-NoNewWindow `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile
# Log and display the output
if (Test-Path $stdoutFile) {
$output = Get-Content $stdoutFile -Raw -ErrorAction SilentlyContinue
if ($output) {
# Display output with indentation
$output -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Information" -Color "DarkGray"
}
}
}
}
if (Test-Path $stderrFile) {
$errors = Get-Content $stderrFile -Raw -ErrorAction SilentlyContinue
if ($errors) {
# Display errors with indentation
$errors -split "`r?`n" | ForEach-Object {
if ($_.Trim()) {
Write-LogMessage -Message " $_" -Level "Warning"
}
}
}
}
if ($icaclsProcess.ExitCode -eq 0) {
Write-LogMessage -Message " ✓ Done" -Level "Success"
} else {
Write-LogMessage -Message " ⚠ Exit code $($icaclsProcess.ExitCode)" -Level "Warning"
}
} finally {
# Clean up temp files
if (Test-Path $stdoutFile) {
Remove-Item $stdoutFile -Force -ErrorAction SilentlyContinue
}
if (Test-Path $stderrFile) {
Remove-Item $stderrFile -Force -ErrorAction SilentlyContinue
}
}
} catch {
Write-LogMessage -Message " ⚠ Failed to reset permissions: $($_.Exception.Message)" -Level "Warning"
}
Write-Information ""
# 1. Update PATH (add)
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Updating system PATH" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$updatePathScript = Join-Path $RootDir "scripts\update-path.bat"
Invoke-ScriptIfExist `
-ScriptPath $updatePathScript `
-Arguments "add" `
-Description "Adding Sunshine directories to PATH" `
-Emoji "📁"
Write-Information ""
# 2. Migrate configuration
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Migrating configuration" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$migrateConfigScript = Join-Path $RootDir "scripts\migrate-config.bat"
Invoke-ScriptIfExist `
-ScriptPath $migrateConfigScript `
-Description "Migrating configuration files" `
-Emoji "⚙️"
Write-Information ""
# 3. Add firewall rules
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Configuring firewall" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$addFirewallScript = Join-Path $RootDir "scripts\add-firewall-rule.bat"
Invoke-ScriptIfExist `
-ScriptPath $addFirewallScript `
-Description "Adding firewall rules" `
-Emoji "🛡️"
Write-Information ""
# 4. Install service
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Installing service" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$installServiceScript = Join-Path $RootDir "scripts\install-service.bat"
Invoke-ScriptIfExist `
-ScriptPath $installServiceScript `
-Description "Installing Windows Service" `
-Emoji ""
Write-Information ""
# 5. Configure autostart
$currentStep++
Write-Progress `
-Activity "Installing Sunshine" `
-Status "Configuring autostart" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$autostartScript = Join-Path $RootDir "scripts\autostart-service.bat"
Invoke-ScriptIfExist `
-ScriptPath $autostartScript `
-Description "Configuring autostart" `
-Emoji "🚀"
Write-Information ""
Write-Progress -Activity "Installing Sunshine" -Completed
Write-FramedText -Message "✓ Sunshine installation completed successfully!" -Level "Success"
# Open documentation in browser (only if not running silently)
if (-not $Silent) {
Write-Information ""
Write-LogMessage `
-Message "📖 Opening documentation in your browser: $DocsUrl" `
-Level "Step"
try {
Start-Process $DocsUrl
Write-LogMessage -Message " ✓ Done" -Level "Success"
} catch {
Write-LogMessage `
-Message " ⓘ Could not open browser automatically: $($_.Exception.Message)" `
-Level "Warning"
}
}
} elseif ($Action -eq "uninstall") {
Write-FramedText `
-Message "🗑️ Sunshine Uninstallation Script" `
-Level "Information" `
-Color "Yellow"
Write-Information ""
$totalSteps = 4
$currentStep = 0
# 1. Delete firewall rules
$currentStep++
Write-Progress `
-Activity "Uninstalling Sunshine" `
-Status "Removing firewall rules" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$deleteFirewallScript = Join-Path $RootDir "scripts\delete-firewall-rule.bat"
Invoke-ScriptIfExist `
-ScriptPath $deleteFirewallScript `
-Description "Removing firewall rules" `
-Emoji "🛡️"
Write-Information ""
# 2. Uninstall service
$currentStep++
Write-Progress `
-Activity "Uninstalling Sunshine" `
-Status "Uninstalling service" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$uninstallServiceScript = Join-Path $RootDir "scripts\uninstall-service.bat"
Invoke-ScriptIfExist `
-ScriptPath $uninstallServiceScript `
-Description "Removing Windows Service" `
-Emoji ""
Write-Information ""
# 3. Restore NVIDIA preferences
$currentStep++
Write-Progress `
-Activity "Uninstalling Sunshine" `
-Status "Restoring NVIDIA settings" `
-PercentComplete (($currentStep / $totalSteps) * 100)
Invoke-SunshineIfExist `
-Arguments "--restore-nvprefs-undo" `
-Description "Restoring NVIDIA preferences" `
-Emoji "🎮"
Write-Information ""
# 4. Update PATH (remove)
$currentStep++
Write-Progress `
-Activity "Uninstalling Sunshine" `
-Status "Cleaning up system PATH" `
-PercentComplete (($currentStep / $totalSteps) * 100)
$updatePathScript = Join-Path $RootDir "scripts\update-path.bat"
Invoke-ScriptIfExist `
-ScriptPath $updatePathScript `
-Arguments "remove" `
-Description "Removing from PATH" `
-Emoji "📁"
Write-Information ""
Write-Progress -Activity "Uninstalling Sunshine" -Completed
Write-FramedText `
-Message "✓ Sunshine uninstallation completed successfully!" `
-Level "Success"
}
Write-Information ""
exit 0

View file

@ -163,4 +163,15 @@ if (WIN32)
# prefer static libraries since we're linking statically # prefer static libraries since we're linking statically
# this fixes libcurl linking errors when using non MSYS2 version of CMake # this fixes libcurl linking errors when using non MSYS2 version of CMake
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1) set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
# Copy minhook-detours DLL to test binary directory for ARM64
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" AND DEFINED _MINHOOK_DLL)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_MINHOOK_DLL}"
"${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Copying minhook-detours DLL to test binary directory"
VERBATIM
)
endif()
endif () endif ()