diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 6356add1..9cf527ef 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -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 diff --git a/.github/workflows/ci-bundle.yml b/.github/workflows/ci-bundle.yml index 60a57c60..a6642de6 100644 --- a/.github/workflows/ci-bundle.yml +++ b/.github/workflows/ci-bundle.yml @@ -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 }} diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index e869692a..a6a21ae8 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -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} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3094d7f7..071461a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/cmake/compile_definitions/windows.cmake b/cmake/compile_definitions/windows.cmake index c1a2c95f..57af5e2a 100644 --- a/cmake/compile_definitions/windows.cmake +++ b/cmake/compile_definitions/windows.cmake @@ -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) diff --git a/cmake/dependencies/windows.cmake b/cmake/dependencies/windows.cmake index 3faad7df..a9e5630d 100644 --- a/cmake/dependencies/windows.cmake +++ b/cmake/dependencies/windows.cmake @@ -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() diff --git a/cmake/packaging/windows.cmake b/cmake/packaging/windows.cmake index 069fd851..9d75f7a6 100644 --- a/cmake/packaging/windows.cmake +++ b/cmake/packaging/windows.cmake @@ -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) diff --git a/cmake/packaging/windows_nsis.cmake b/cmake/packaging/windows_nsis.cmake index 6644a17d..7c413b9f 100644 --- a/cmake/packaging/windows_nsis.cmake +++ b/cmake/packaging/windows_nsis.cmake @@ -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 - RMDir /r \\\"$INSTDIR\\\"; skipped if no - nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" remove' - NoDelete: + 'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \ + /SD IDNO IDNO no_delete + RMDir /r \\\"$INSTDIR\\\"; skipped if no + no_delete: ") # Adding an option for the start menu diff --git a/cmake/packaging/windows_wix.cmake b/cmake/packaging/windows_wix.cmake index bc956182..4a2f4d03 100644 --- a/cmake/packaging/windows_wix.cmake +++ b/cmake/packaging/windows_wix.cmake @@ -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() diff --git a/cmake/packaging/wix_resources/patch.xml b/cmake/packaging/wix_resources/patch.xml new file mode 100644 index 00000000..e85c3581 --- /dev/null +++ b/cmake/packaging/wix_resources/patch.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmake/packaging/wix_resources/sunshine-installer.wxs b/cmake/packaging/wix_resources/sunshine-installer.wxs new file mode 100644 index 00000000..36f92969 --- /dev/null +++ b/cmake/packaging/wix_resources/sunshine-installer.wxs @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/prep/build_version.cmake b/cmake/prep/build_version.cmake index 7c10dde2..91209922 100644 --- a/cmake/prep/build_version.cmake +++ b/cmake/prep/build_version.cmake @@ -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() diff --git a/docs/Doxyfile b/docs/Doxyfile index 8549dbba..36196192 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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 diff --git a/docs/building.md b/docs/building.md index 67b2066c..4a3ee6c2 100644 --- a/docs/building.md +++ b/docs/building.md @@ -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 ```} diff --git a/docs/getting_started.md b/docs/getting_started.md index d2afb2b9..5daccc04 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -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.
+> | File | log paths | +> | ---- | --------- | +> | .exe | `%%PROGRAMFILES%/Sunshine/install.log` (AMD64 only)
`%%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 /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. diff --git a/docs/images/applications.png b/docs/images/applications.png new file mode 100644 index 00000000..42f7605f Binary files /dev/null and b/docs/images/applications.png differ diff --git a/docs/images/configuration-search.png b/docs/images/configuration-search.png new file mode 100644 index 00000000..c9c573ec Binary files /dev/null and b/docs/images/configuration-search.png differ diff --git a/docs/images/featured-apps.png b/docs/images/featured-apps.png new file mode 100644 index 00000000..489ad710 Binary files /dev/null and b/docs/images/featured-apps.png differ diff --git a/docs/images/split-themes.png b/docs/images/split-themes.png new file mode 100644 index 00000000..22b42e02 Binary files /dev/null and b/docs/images/split-themes.png differ diff --git a/docs/images/troubleshooting-logs.png b/docs/images/troubleshooting-logs.png new file mode 100644 index 00000000..ea57c7bc Binary files /dev/null and b/docs/images/troubleshooting-logs.png differ diff --git a/docs/images/vigembus-installer.png b/docs/images/vigembus-installer.png new file mode 100644 index 00000000..fb68d5e0 Binary files /dev/null and b/docs/images/vigembus-installer.png differ diff --git a/src/config.cpp b/src/config.cpp index e5d58f3e..cc7b704b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -187,7 +187,7 @@ namespace config { }; template - std::optional quality_from_view(const std::string_view &quality_type, const std::optional(&original)) { + ::std::optional quality_from_view(const ::std::string_view &quality_type, const ::std::optional(&original)) { #define _CONVERT_(x) \ if (quality_type == #x##sv) \ return (int) T::x @@ -199,7 +199,7 @@ namespace config { } template - std::optional rc_from_view(const std::string_view &rc, const std::optional(&original)) { + ::std::optional rc_from_view(const ::std::string_view &rc, const ::std::optional(&original)) { #define _CONVERT_(x) \ if (rc == #x##sv) \ return (int) T::x @@ -212,7 +212,7 @@ namespace config { } template - std::optional usage_from_view(const std::string_view &usage, const std::optional(&original)) { + ::std::optional usage_from_view(const ::std::string_view &usage, const ::std::optional(&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 diff --git a/src/main.cpp b/src/main.cpp index 20a733d8..b4117450 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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. diff --git a/src/nvenc/nvenc_d3d11_on_cuda.h b/src/nvenc/nvenc_d3d11_on_cuda.h index 102e1809..80aeb9ed 100644 --- a/src/nvenc/nvenc_d3d11_on_cuda.h +++ b/src/nvenc/nvenc_d3d11_on_cuda.h @@ -56,7 +56,6 @@ namespace nvenc { autopop_context push_context(); - HMODULE dll = nullptr; const ID3D11DevicePtr d3d_device; ID3D11Texture2DPtr d3d_input_texture; diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index e8bcc37f..c0012dd6 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -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; } diff --git a/src/platform/linux/portalgrab.cpp b/src/platform/linux/portalgrab.cpp index 4c27357d..c2c604f5 100644 --- a/src/platform/linux/portalgrab.cpp +++ b/src/platform/linux/portalgrab.cpp @@ -736,8 +736,9 @@ namespace portal { struct spa_buffer *buf; buf = stream_data.current_buffer->buffer; if (buf->datas[0].chunk->size != 0) { + const auto img_descriptor = static_cast(img); + img_descriptor->frame_timestamp = std::chrono::steady_clock::now(); if (buf->datas[0].type == SPA_DATA_DmaBuf) { - const auto img_descriptor = static_cast(img); 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; diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp index d7d4680e..b26f91a8 100644 --- a/src/platform/windows/audio.cpp +++ b/src/platform/windows/audio.cpp @@ -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; diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index c6b694f0..52a87c78 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -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(); } diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index 85273609..533e3790 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -10,6 +10,7 @@ // standard includes #include #include +#include // lib includes #include @@ -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 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; } diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 5a4ea26e..4736a4c7 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // lib includes #include @@ -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 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; diff --git a/src/platform/windows/windows.rc b/src/platform/windows/windows.rc index 8f1c2bf7..417fcf0d 100644 --- a/src/platform/windows/windows.rc +++ b/src/platform/windows/windows.rc @@ -41,4 +41,4 @@ BEGIN END END -SuperDuperAmazing ICON DISCARDABLE PROJECT_ICON_PATH +SuperDuperAmazing ICON DISCARDABLE TOSTRING(PROJECT_ICON_PATH) diff --git a/src/video.cpp b/src/video.cpp index 0290fd95..7487e127 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -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( + 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 - ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ? - std::numeric_limits::max() : - std::numeric_limits::max(); - - ctx->keyint_min = std::numeric_limits::max(); + // 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::max() : + std::numeric_limits::max(); + ctx->keyint_min = std::numeric_limits::max(); + } // Some client decoders have limits on the number of reference frames if (config.numRefFrames) { diff --git a/src/video.h b/src/video.h index 8dbf76e2..eb8bb46f 100644 --- a/src/video.h +++ b/src/video.h @@ -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__) diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 222fba0e..1bc7cdb6 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -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 diff --git a/src_assets/common/assets/web/configs/tabs/Inputs.vue b/src_assets/common/assets/web/configs/tabs/Inputs.vue index 7fa76a20..64792b07 100644 --- a/src_assets/common/assets/web/configs/tabs/Inputs.vue +++ b/src_assets/common/assets/web/configs/tabs/Inputs.vue @@ -118,7 +118,7 @@ const config = ref(props.config) id="keyboard" locale-prefix="config" v-model="config.keyboard" - default="true" + default="false" > @@ -161,7 +161,7 @@ const config = ref(props.config) id="mouse" locale-prefix="config" v-model="config.mouse" - default="true" + default="false" > diff --git a/src_assets/windows/misc/sunshine-setup.ps1 b/src_assets/windows/misc/sunshine-setup.ps1 new file mode 100644 index 00000000..f0015f0e --- /dev/null +++ b/src_assets/windows/misc/sunshine-setup.ps1 @@ -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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a570fd3..1d9bdad4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 ()