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
attributes:
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:
required: true
- type: textarea
@ -52,6 +54,7 @@ body:
description: What version operating system are you running the software on?
options:
- Docker
- FreeBSD
- Linux
- macOS
- Windows
@ -75,15 +78,6 @@ body:
- 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:
@ -103,10 +97,10 @@ body:
- Linux - solus (Third Party)
- Linux - Unraid (Third Party)
- macOS - Homebrew
- macOS - Portfile
- Windows - Chocolatey (Third Party)
- Windows - installer (recommended)
- Windows - portable (not recommended)
- Windows - exe installer
- Windows - msi installer (recommended)
- Windows - portable (NOT recommended)
- Windows - Scoop (Third Party)
- Windows - Winget
- other (not listed)
@ -154,19 +148,11 @@ body:
- NvFBC (Linux)
- wlroots (Linux)
- X11 (Linux)
- XDG Portal Grab (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: false
- type: textarea
id: apps
attributes:
@ -179,10 +165,20 @@ body:
- type: textarea
id: logs
attributes:
label: Relevant log output
label: Log output
description: |
Please copy and paste any relevant log output. This will be automatically formatted into code,
so no need for backticks.
Copy and paste logs from web-ui troubleshooting page.
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
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
run: npm install --ignore-scripts
- name: Debug install
if: always()
run: cat "${HOME}/.npm/_logs/*-debug-0.log" || true
- name: Build
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View file

@ -29,6 +29,11 @@ jobs:
arch: x86_64
msystem: ucrt64
toolchain: ucrt-x86_64
- name: Windows-ARM64
os: windows-11-arm
arch: aarch64
msystem: clangarm64
toolchain: clang-aarch64
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@ -46,7 +51,9 @@ jobs:
- name: Update Windows dependencies
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 }}
shell: msys2 {0}
run: |
@ -62,17 +69,22 @@ jobs:
"mingw-w64-${TOOLCHAIN}-curl-winssl"
"mingw-w64-${TOOLCHAIN}-gcc"
"mingw-w64-${TOOLCHAIN}-graphviz"
"mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-miniupnpc"
"mingw-w64-${TOOLCHAIN}-nlohmann-json"
"mingw-w64-${TOOLCHAIN}-nodejs"
"mingw-w64-${TOOLCHAIN}-nsis"
"mingw-w64-${TOOLCHAIN}-onevpl"
"mingw-w64-${TOOLCHAIN}-openssl"
"mingw-w64-${TOOLCHAIN}-opus"
"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
ignore_packages=()
@ -83,7 +95,7 @@ jobs:
tarball="${pkg}-${version}-any.pkg.tar.zst"
# download working version
wget "https://repo.msys2.org/mingw/${MSYSTEM}/${tarball}"
wget "https://repo.msys2.org/mingw/${MATRIX_MSYSTEM}/${tarball}"
tarballs="${tarballs} ${tarball}"
done
@ -129,6 +141,33 @@ jobs:
# Clean up
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
id: setup-python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@ -149,10 +188,19 @@ jobs:
- name: Build Windows
shell: msys2 {0}
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 }}
BUILD_VERSION: ${{ inputs.release_version }}
COMMIT: ${{ inputs.release_commit }}
run: |
# setup NodeJS PATH
if [[ "${MATRIX_MSYSTEM}" != "ucrt64" ]]; then
NODEJS_PATH=$(cygpath "$NODEJS_PATH")
export PATH="$PATH:$NODEJS_PATH"
fi
mkdir -p build
cmake \
-B build \
@ -176,12 +224,24 @@ jobs:
# package
cpack -G NSIS
cpack -G WIX
cpack -G ZIP
# move
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
- 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
id: test
shell: msys2 {0}

View file

@ -181,6 +181,9 @@ jobs:
- name: Windows-AMD64
coverage: true
pr: true
- name: Windows-ARM64
coverage: true
pr: true
steps:
- name: Should run
id: should_run
@ -202,18 +205,7 @@ jobs:
name: coverage-${{ matrix.name }}
path: _coverage
- name: Upload test results
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
- name: Upload test coverage
if: |
steps.should_run.outputs.SHOULD_RUN == 'true' &&
matrix.coverage != false
@ -222,6 +214,19 @@ jobs:
disable_search: true
fail_ci_if_error: true
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 }}
token: ${{ secrets.CODECOV_TOKEN }}
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
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
add_definitions(-DUSE_BOOST_REGEX)

View file

@ -1,9 +1,35 @@
# windows specific dependencies
# Make sure MinHook is installed
find_library(MINHOOK_LIBRARY libMinHook.a REQUIRED)
find_path(MINHOOK_INCLUDE_DIR MinHook.h PATH_SUFFIXES include REQUIRED)
# MinHook setup - use installed minhook for AMD64, otherwise download minhook-detours for ARM64
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64")
# 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)
set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY})
target_include_directories(minhook::minhook INTERFACE ${MINHOOK_INCLUDE_DIR})
add_library(minhook::minhook STATIC IMPORTED)
set_property(TARGET minhook::minhook PROPERTY IMPORTED_LOCATION ${MINHOOK_LIBRARY})
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)
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
set(VIGEMBUS_INSTALLER "${CMAKE_BINARY_DIR}/scripts/vigembus_installer.exe")
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)
# 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/"
DESTINATION "scripts"
COMPONENT assets)

View file

@ -3,36 +3,42 @@
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
# Restores permissions on the install directory
# Migrates config files from the root into the new config folder
# Install service
# Runs the main setup script which handles all installation tasks
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
IfSilent +2 0
ExecShell 'open' 'https://docs.lizardbyte.dev/projects/sunshine'
nsExec::ExecToLog 'icacls \\\"$INSTDIR\\\" /reset'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" add'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\migrate-config.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\autostart-service.bat\\\"'
NoController:
${NSIS_LOGSET_COMMAND}
IfSilent +3 0
nsExec::ExecToLog \
'powershell -ExecutionPolicy Bypass \
-File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action install'
Goto +2
nsExec::ExecToLog \
'powershell -ExecutionPolicy Bypass \
-File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action install -Silent'
install_done:
")
# Extra uninstall commands
# Uninstall service
# Runs the main setup script which handles all uninstallation tasks
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" --restore-nvprefs-undo'
${NSIS_LOGSET_COMMAND}
nsExec::ExecToLog \
'powershell -ExecutionPolicy Bypass \
-File \\\"$INSTDIR\\\\scripts\\\\sunshine-setup.ps1\\\" -Action uninstall'
MessageBox MB_YESNO|MB_ICONQUESTION \
'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
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" remove'
NoDelete:
no_delete:
")
# Adding an option for the start menu

View file

@ -1,4 +1,97 @@
# WIX Packaging
# 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)
MESSAGE("Sunshine Branch: ${GIT_DESCRIBE_BRANCH}")
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}")
endif()
if(GIT_IS_DIRTY)
set(PROJECT_VERSION ${PROJECT_VERSION}.dirty)
set(PROJECT_VERSION ${PROJECT_VERSION}-dirty)
MESSAGE("Git tree is dirty!")
endif()
else()

View file

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

View file

@ -126,36 +126,60 @@ sudo port install "${dependencies[@]}"
```
#### 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
```bash
pacman -Syu
```
##### Set toolchain variable
For UCRT64:
```bash
export TOOLCHAIN="ucrt-x86_64"
```
For CLANGARM64:
```bash
export TOOLCHAIN="clang-aarch64"
```
##### Install dependencies
```bash
dependencies=(
"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-winssl"
"mingw-w64-ucrt-x86_64-doxygen" # Optional, for docs... better to install official Doxygen
"mingw-w64-ucrt-x86_64-graphviz" # Optional, for docs
"mingw-w64-ucrt-x86_64-MinHook"
"mingw-w64-ucrt-x86_64-miniupnpc"
"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"
"mingw-w64-${TOOLCHAIN}-boost" # Optional
"mingw-w64-${TOOLCHAIN}-cmake"
"mingw-w64-${TOOLCHAIN}-cppwinrt"
"mingw-w64-${TOOLCHAIN}-curl-winssl"
"mingw-w64-${TOOLCHAIN}-doxygen" # Optional, for docs... better to install official Doxygen
"mingw-w64-${TOOLCHAIN}-graphviz" # Optional, for docs
"mingw-w64-${TOOLCHAIN}-miniupnpc"
"mingw-w64-${TOOLCHAIN}-onevpl"
"mingw-w64-${TOOLCHAIN}-openssl"
"mingw-w64-${TOOLCHAIN}-opus"
"mingw-w64-${TOOLCHAIN}-toolchain"
)
if [[ "${MSYSTEM}" == "UCRT64" ]]; then
dependencies+=(
"mingw-w64-${TOOLCHAIN}-MinHook"
"mingw-w64-${TOOLCHAIN}-nodejs"
"mingw-w64-${TOOLCHAIN}-nsis"
)
fi
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
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{Installer | ```bash
@tab{NSIS Installer | ```bash
cpack -G NSIS --config ./build/CPackConfig.cmake
```}
@tab{WiX Installer | ```bash
cpack -G WIX --config ./build/CPackConfig.cmake
```}
@tab{Portable | ```bash
cpack -G ZIP --config ./build/CPackConfig.cmake
```}

View file

@ -100,22 +100,10 @@ CUDA is used for NVFBC capture.
> [!CAUTION]
> 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.
- ✖ 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
> [!NOTE]
> The AppImage is built on Ubuntu 22.04, which requires `glibc 2.35` or newer and `libstdc++ 3.4.11` or newer.
##### Install
1. Download [sunshine.AppImage](https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.AppImage)
@ -214,6 +202,12 @@ sudo dnf remove sunshine
```
##### 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.
```bash
sudo dnf copr enable lizardbyte/stable
@ -238,6 +232,7 @@ sudo dnf remove Sunshine
> [!CAUTION]
> 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.
@ -305,6 +300,9 @@ brew install sunshine
brew uninstall sunshine
```
> [!TIP]
> For beta you can replace `sunshine` with `sunshine-beta` in the above commands.
### macOS
> [!IMPORTANT]
@ -315,6 +313,8 @@ This package requires that you have [Homebrew](https://docs.brew.sh/Installation
##### Install
```bash
brew update
brew upgrade
brew tap LizardByte/homebrew
brew install sunshine
```
@ -329,10 +329,31 @@ brew uninstall sunshine
### 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)
1. Download and install
[Sunshine-Windows-AMD64-installer.exe](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-installer.exe)
> [!CAUTION]
> 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]
> 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
> recommended for most users. No support will be provided!
1. Download and extract
[Sunshine-Windows-AMD64-portable.zip](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-Windows-AMD64-portable.zip)
1. Download and extract based on your architecture:
| 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
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
recommended to restart your computer.
![ViGEmBus Installation](images/vigembus-installer.png)
## Usage
### Basic usage
@ -455,9 +483,8 @@ 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.
> [!TIP]
> 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.
> The configuration file specified will be created if it doesn't exist.
### Start Sunshine over SSH (Linux/X11)
@ -493,16 +520,27 @@ by default. You may replace *localhost* with your internal ip address.
> [!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:
1. Change the web-ui to your desired theme, using the dropdown menu in the navbar.
![Theme Selection](images/split-themes.png)
2. Add games and applications.
![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
- 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
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
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>
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) \
if (quality_type == #x##sv) \
return (int) T::x
@ -199,7 +199,7 @@ namespace config {
}
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) \
if (rc == #x##sv) \
return (int) T::x
@ -212,7 +212,7 @@ namespace config {
}
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) \
if (usage == #x##sv) \
return (int) T::x
@ -225,7 +225,7 @@ namespace config {
return original;
}
int coder_from_view(const std::string_view &coder) {
int coder_from_view(const ::std::string_view &coder) {
if (coder == "auto"sv) {
return _auto;
}
@ -556,8 +556,8 @@ namespace config {
true, // client gamepads with touchpads are emulated as DS4
true, // ds5_inputtino_randomize_mac
true, // keyboard enabled
true, // mouse enabled
false, // keyboard disabled (Parsec-style default)
false, // mouse disabled (Parsec-style default)
true, // controller enabled
true, // always send scancodes
true, // high resolution scrolling

View file

@ -383,7 +383,7 @@ int main(int argc, char *argv[]) {
BOOST_LOG(info) << "Starting system tray"sv;
#ifdef _WIN32
// 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.
// 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.

View file

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

View file

@ -1661,9 +1661,14 @@ namespace platf {
if (!fb->handles[0]) {
BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv;
BOOST_LOG((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
<< "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;
}

View file

@ -736,8 +736,9 @@ namespace portal {
struct spa_buffer *buf;
buf = stream_data.current_buffer->buffer;
if (buf->datas[0].chunk->size != 0) {
if (buf->datas[0].type == SPA_DATA_DmaBuf) {
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) {
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.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)
#define STEAM_DRIVER_SUBDIR L"x64"
#else
#warning No known Steam audio driver for this architecture
#endif
namespace {
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";
#endif
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")) {
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 {
BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view();
}

View file

@ -10,6 +10,7 @@
// standard includes
#include <cmath>
#include <thread>
#include <vector>
// lib includes
#include <ViGEm/Client.h>
@ -1134,9 +1135,9 @@ namespace platf {
void unicode(input_t &input, char *utf8, int size) {
// 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) {
return;
}

View file

@ -9,6 +9,7 @@
#include <iterator>
#include <set>
#include <sstream>
#include <vector>
// lib includes
#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);
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;
if (send_info.headers) {
// Interleave buffers for headers and payloads
@ -1409,7 +1410,7 @@ namespace platf {
}
}
msg.lpBuffers = bufs;
msg.lpBuffers = bufs.data();
msg.dwBufferCount = bufcount;
msg.dwFlags = 0;

View file

@ -41,4 +41,4 @@ BEGIN
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
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
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 {
@ -825,6 +826,63 @@ namespace video {
},
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
encoder_t software {
@ -1031,6 +1089,7 @@ namespace video {
#ifdef _WIN32
&quicksync,
&amdvce,
&mediafoundation,
#endif
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
&vaapi,
@ -1566,11 +1625,17 @@ namespace video {
ctx->max_b_frames = 0;
// Use an infinite GOP length since I-frames are generated on demand
// Exception: encoders with FIXED_GOP_SIZE flag don't support on-demand IDR
if (encoder.flags & FIXED_GOP_SIZE) {
// 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 = 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
if (config.numRefFrames) {

View file

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

View file

@ -203,12 +203,12 @@
"touchpad_as_ds4": "enabled",
"ds5_inputtino_randomize_mac": "enabled",
"back_button_timeout": -1,
"keyboard": "enabled",
"keyboard": "disabled",
"key_repeat_delay": 500,
"key_repeat_frequency": 24.9,
"always_send_scancodes": "enabled",
"key_rightalt_to_key_win": "disabled",
"mouse": "enabled",
"mouse": "disabled",
"high_resolution_scrolling": "enabled",
"native_pen_touch": "enabled",
"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"
locale-prefix="config"
v-model="config.keyboard"
default="true"
default="false"
></Checkbox>
<!-- Key Repeat Delay-->
@ -161,7 +161,7 @@ const config = ref(props.config)
id="mouse"
locale-prefix="config"
v-model="config.mouse"
default="true"
default="false"
></Checkbox>
<!-- 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
# this fixes libcurl linking errors when using non MSYS2 version of CMake
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 ()