Compare commits
10 commits
476b984f7f
...
bbc1f724e1
| Author | SHA1 | Date | |
|---|---|---|---|
| bbc1f724e1 | |||
|
|
97b6168ba6 |
||
|
|
8aed1a82c8 |
||
|
|
5bd3a2b225 |
||
|
|
2f61116432 |
||
|
|
d591643706 |
||
|
|
cdc444314f |
||
|
|
e2652fa52b |
||
|
|
bf574afdfd |
||
|
|
b48a96f9f1 |
37 changed files with 1268 additions and 146 deletions
48
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
48
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
4
.github/workflows/ci-bundle.yml
vendored
4
.github/workflows/ci-bundle.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
70
.github/workflows/ci-windows.yml
vendored
70
.github/workflows/ci-windows.yml
vendored
|
|
@ -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}
|
||||
|
|
|
|||
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
5
cmake/packaging/wix_resources/patch.xml
Normal file
5
cmake/packaging/wix_resources/patch.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<CPackWiXPatch>
|
||||
<CPackWiXFragment Id="CM_G_Core">
|
||||
<FeatureRef Id="RunSunshineInstallScripts"/>
|
||||
</CPackWiXFragment>
|
||||
</CPackWiXPatch>
|
||||
83
cmake/packaging/wix_resources/sunshine-installer.wxs
Normal file
83
cmake/packaging/wix_resources/sunshine-installer.wxs
Normal 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 "[INSTALL_ROOT]scripts\sunshine-setup.ps1" -Action install" Execute="deferred" Return="ignore" Impersonate="no" />
|
||||
<CustomAction Id="CA_SunshineInstallSilent" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File "[INSTALL_ROOT]scripts\sunshine-setup.ps1" -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 "[INSTALL_ROOT]scripts\sunshine-setup.ps1" -Action uninstall" Execute="deferred" Return="ignore" Impersonate="no" />
|
||||
<CustomAction Id="CA_SunshineUninstallSilent" Directory="INSTALL_ROOT" ExeCommand="powershell.exe -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File "[INSTALL_ROOT]scripts\sunshine-setup.ps1" -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 > 3" />
|
||||
<Custom Action="CA_SunshineInstallSilent" After="InstallFiles" Condition="NOT Installed AND UILevel <= 3" />
|
||||
|
||||
<!-- Run uninstallation script before files are removed -->
|
||||
<Custom Action="CA_SunshineUninstall" Before="RemoveFiles" Condition="REMOVE="ALL" AND UILevel > 3" />
|
||||
<Custom Action="CA_SunshineUninstallSilent" Before="RemoveFiles" Condition="REMOVE="ALL" AND UILevel <= 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>
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
2. Add games and applications.
|
||||

|
||||
3. Adjust any configuration settings as needed. You can search for options in the search bar.
|
||||

|
||||
4. Find Moonlight clients and other tools for Sunshine in the `Featured Apps` tab.
|
||||

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

|
||||
|
||||
### Arguments
|
||||
To get a list of available arguments, run the following command.
|
||||
|
||||
|
|
|
|||
BIN
docs/images/applications.png
Normal file
BIN
docs/images/applications.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 386 KiB |
BIN
docs/images/configuration-search.png
Normal file
BIN
docs/images/configuration-search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/images/featured-apps.png
Normal file
BIN
docs/images/featured-apps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 KiB |
BIN
docs/images/split-themes.png
Normal file
BIN
docs/images/split-themes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 158 KiB |
BIN
docs/images/troubleshooting-logs.png
Normal file
BIN
docs/images/troubleshooting-logs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
docs/images/vigembus-installer.png
Normal file
BIN
docs/images/vigembus-installer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ namespace nvenc {
|
|||
|
||||
autopop_context push_context();
|
||||
|
||||
HMODULE dll = nullptr;
|
||||
const ID3D11DevicePtr d3d_device;
|
||||
ID3D11Texture2DPtr d3d_input_texture;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,4 +41,4 @@ BEGIN
|
|||
|
||||
END
|
||||
END
|
||||
SuperDuperAmazing ICON DISCARDABLE PROJECT_ICON_PATH
|
||||
SuperDuperAmazing ICON DISCARDABLE TOSTRING(PROJECT_ICON_PATH)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
674
src_assets/windows/misc/sunshine-setup.ps1
Normal file
674
src_assets/windows/misc/sunshine-setup.ps1
Normal 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
|
||||
|
|
@ -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 ()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue