diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e3d52b13a2..61c8a635b74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,18 @@ project(Sunshine VERSION 0.0.0 DESCRIPTION "Self-hosted game stream host for Moonlight" HOMEPAGE_URL "https://app.lizardbyte.dev/Sunshine") -# Minimum separately installed Virtual HID Driver version supported by this build. +# Minimum separately installed Virtual HID Broker version supported by this build. set(LIBVIRTUALHID_MINIMUM_VERSION "2026.914.1218.10") list(APPEND SUNSHINE_DEFINITIONS LIBVIRTUALHID_MINIMUM_VERSION="${LIBVIRTUALHID_MINIMUM_VERSION}") +if(APPLE) + # macOS bundle versions have three components; the Windows driver uses four. + if(LIBVIRTUALHID_MINIMUM_VERSION MATCHES "^([0-9]+\\.[0-9]+\\.[0-9]+)\\.[0-9]+$") + set(LIBVIRTUALHID_MACOS_MINIMUM_VERSION "${CMAKE_MATCH_1}") + else() + set(LIBVIRTUALHID_MACOS_MINIMUM_VERSION "${LIBVIRTUALHID_MINIMUM_VERSION}") + endif() + list(APPEND SUNSHINE_DEFINITIONS LIBVIRTUALHID_MACOS_MINIMUM_VERSION="${LIBVIRTUALHID_MACOS_MINIMUM_VERSION}") +endif() set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/README.md b/README.md index bc9a6f46455..2ffd99b7aa3 100644 --- a/README.md +++ b/README.md @@ -56,55 +56,57 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz Generic 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 DualShock / DS4 (PlayStation 4) 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 DualSense / DS5 (PlayStation 5) 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 Nintendo Switch Pro 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 Xbox 360 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 Xbox One 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 Xbox Series 🟑1 βœ… - ❌ - βœ… + βœ…2 + βœ…3 > [!NOTE] > 1 Missing motion, touchpad input, battery state, RGB LEDs, adaptive triggers, and raw HID output reports. +> 2 Requires the separately installed and licensed Virtual HID Broker. +> 3 All profiles are available through the separately installed and licensed Virtual HID Broker. Xbox 360 and DualShock 4 can also use ViGEmBus. diff --git a/cmake/packaging/common.cmake b/cmake/packaging/common.cmake index a094f0034a2..da338a2746f 100644 --- a/cmake/packaging/common.cmake +++ b/cmake/packaging/common.cmake @@ -16,9 +16,17 @@ set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/sunshine.png) set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}") set(CPACK_STRIP_FILES YES) +# Keep macOS assets in the Runtime component so app assets are installed +# before macos.cmake signs the finished bundle. +set(_sunshine_common_asset_component) +if(APPLE) + set(_sunshine_common_asset_component COMPONENT Runtime) +endif() + # install common assets install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}" + ${_sunshine_common_asset_component} PATTERN "web" EXCLUDE) # copy assets to build directory, for running without install file(GLOB_RECURSE ALL_ASSETS @@ -31,7 +39,8 @@ endforeach() # install built vite assets install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/assets/web" - DESTINATION "${SUNSHINE_ASSETS_DIR}") + DESTINATION "${SUNSHINE_ASSETS_DIR}" + ${_sunshine_common_asset_component}) # platform specific packaging if(WIN32) diff --git a/docs/configuration.md b/docs/configuration.md index d1b12b3c17a..6d51304d62b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -319,12 +319,12 @@ supported on the current platform. @@ -340,17 +340,21 @@ supported on the current platform. @endcode - + - + - + - + + + + +
Encoding API
Description - Controls which virtual gamepad drivers Sunshine may use. The Web UI and startup notification continue to - request a choice while this option is not set. If Sunshine detects an active Virtual HID Driver license, - it automatically sets this option to `all` when it is missing. + Controls which virtual gamepad backend Sunshine may use on Windows and macOS. On Windows, the Web UI and + startup notification request a choice while this option is not set. If Sunshine detects an active Virtual + HID Broker license on Windows, it automatically sets this option to `all` when it is missing. @warning{ViGEmBus has limited gamepad features, supports only Xbox 360 and DualShock 4 emulation, and has - reached end of life. Selecting `vigembus` also suppresses Virtual HID Driver startup notifications.} - @note{This option applies only to Windows.} + reached end of life. Selecting `vigembus` also suppresses Virtual HID Broker startup notifications.} + @note{`all` and `vigembus` apply only to Windows. On macOS, an unset value uses Virtual HID Broker.}
ChoicesChoices allPrefer Virtual HID Driver when it is available and licensed, with ViGEmBus as a limited fallback.Windows only: prefer Virtual HID Broker when it is available and licensed, with ViGEmBus as a limited fallback.
virtualhidUse only Virtual HID Driver. An active paid license is required; ViGEmBus fallback is disabled.Use Virtual HID Broker for gamepads. An active paid license is required; Windows ViGEmBus fallback is disabled.
vigembusUse only ViGEmBus for gamepads and hide Virtual HID Driver status and licensing details.Windows only: use ViGEmBus for gamepads and hide Virtual HID Broker status and licensing details.
noneDisable virtual gamepads on Windows and macOS without changing keyboard or mouse input. Suppress broker and driver choice notices.
@@ -360,8 +364,9 @@ supported on the current platform. Description - The type of gamepad to emulate on the host. - @note{This option applies to FreeBSD, Linux, and Windows.} + The type of gamepad to emulate on the host. Automatic selection uses the controller type + reported by the client. If the type is unknown, Sunshine can select a PlayStation-style + controller from reported motion or touchpad support; otherwise it uses an Xbox-style controller. @note{When gamepad_driver is `vigembus` on Windows, only auto, x360, and ds4 are available.} diff --git a/docs/getting_started.md b/docs/getting_started.md index 2a3eb12147e..4f583676956 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -400,7 +400,19 @@ brew uninstall sunshine ### macOS > [!IMPORTANT] -> Sunshine on macOS is experimental. Gamepads do not work. +> Virtual gamepads require the separately installed, licensed +> [Virtual HID Broker](https://github.com/LizardByte/libvirtualhid/releases/latest). + +To use gamepads, download the macOS universal libvirtualhid DMG from the link above. Open it and run +**Install libvirtualhid.command** to install Virtual HID Broker. In **System Settings > Privacy & Security > +Device Control and Data Access**, add `/Applications/VirtualHIDBroker.app` and enable it. Restart the broker if +you changed this permission with `sudo launchctl kickstart -k system/dev.lizardbyte.app.libvirtualhid`. +Activate the machine license in Sunshine’s +**Troubleshooting > Virtual HID Broker License** section. In **Configuration > Input**, turn on **Enable Gamepad Input** +and choose the emulated gamepad. The Gamepad Backend setting can be set to **None** to disable gamepads without affecting +keyboard or mouse input. Sunshine’s menu bar **Virtual HID Broker** submenu shows the license status and links +for license management and downloads. Sunshine’s DMG does not contain the broker. Keyboard and mouse input use the +standard macOS synthetic-input permission path. #### DMG @@ -569,15 +581,15 @@ and enter its device name in the [audio_sink](configuration.md#audio_sink) field ### Windows Sunshine supports two virtual gamepad backends on Windows. You can install the -[Virtual HID Driver](https://github.com/LizardByte/libvirtualhid/releases/latest) separately as an optional paid upgrade -for a driver-backed Raw Input keyboard and mouse plus full virtual gamepad support. ViGEmBus remains available as a -limited alternative for Xbox 360 and DualShock 4 gamepads, but it has reached end of life. +[Virtual HID Broker](https://github.com/LizardByte/libvirtualhid/releases/latest) separately as an optional paid upgrade. +Its Windows package includes a broker service and user-mode driver for a Raw Input keyboard and mouse plus full virtual +gamepad support. ViGEmBus remains available as a limited alternative for Xbox 360 and DualShock 4 gamepads, but it has reached end of life. -When Virtual HID Driver is used, Sunshine requires version `2026.914.1218.10` or newer. +When Virtual HID Broker is used, Sunshine requires libvirtualhid version `2026.914.1218.10` or newer. -Compared with the ViGEmBus fallback, Virtual HID Driver can create Xbox One, Xbox Series, DualSense, Nintendo Switch +Compared with the ViGEmBus fallback, Virtual HID Broker can create Xbox One, Xbox Series, DualSense, Nintendo Switch Pro, and Generic gamepads in addition to Xbox 360 and DualShock 4. It can also expose controller-specific features such -as motion, touchpads, LEDs, and adaptive triggers when supported. Virtual HID Driver is actively developed and +as motion, touchpads, LEDs, and adaptive triggers when supported. Virtual HID Broker is actively developed and supported by the LizardByte team. With a compatible driver and active license, normal key transitions are exposed through a real HID keyboard so @@ -589,15 +601,16 @@ Relative mouse movement, buttons, and scrolling are exposed as a real HID mouse so applications using Raw Input can receive them. Absolute mouse positioning continues to use Windows input injection. When the driver-backed mouse cannot be created, libvirtualhid retains its legacy SendInput fallback. -The Virtual HID Driver requires an active paid machine license for driver-backed devices, including gamepads and the Raw +The Virtual HID Broker requires an active paid machine license for driver-backed devices, including gamepads and the Raw Input keyboard and mouse. Sunshine shows the current license status and actions on the Web UI Troubleshooting page and -in the **Virtual HID Driver** system tray submenu. In **Configuration > Input**, choose whether Sunshine may use **All -Available Drivers**, only **Virtual HID Driver**, or only **ViGEmBus**. Sunshine continues to show the selection prompt -until this setting is saved. If an active Virtual HID Driver license is already present, Sunshine selects **All Available -Drivers** automatically. That policy prefers Virtual HID Driver. When its license is not valid, Sunshine falls back to +in the **Virtual HID Broker** system tray submenu. In **Configuration > Input**, choose whether Sunshine may use **All +Available Drivers**, only **Virtual HID Broker**, or only **ViGEmBus**. Sunshine continues to show the selection prompt +until this setting is saved. If an active Virtual HID Broker license is already present, Sunshine selects **All Available +Drivers** automatically. That policy prefers Virtual HID Broker. When its license is not valid, Sunshine falls back to ViGEmBus for Xbox 360 and DualShock 4 gamepads and to SendInput for keyboard and mouse. Selecting only **ViGEmBus** -suppresses Virtual HID Driver startup notifications and limits the available emulated gamepads to Xbox 360 and -DualShock 4. Sunshine recreates the shared keyboard and mouse after a successful license action, so switching between +suppresses Virtual HID Broker startup notifications and limits the available emulated gamepads to Xbox 360 and +DualShock 4. **None** disables all virtual gamepads and their startup notices without affecting keyboard or mouse +input. Sunshine recreates the shared keyboard and mouse after a successful license action, so switching between the HID and SendInput paths does not require restarting Sunshine. After installing or updating virtual input drivers, it is recommended to restart your computer. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index fb3ae3ff7dc..889bea61639 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -306,6 +306,18 @@ Some users have reported stuttering issues when streaming games running within G ## macOS +### No gamepad detected +Confirm **Enable Gamepad Input** is on under **Configuration > Input**. Install +[Virtual HID Broker](https://github.com/LizardByte/libvirtualhid/releases/latest) separately from Sunshine, +using the macOS installer in its universal DMG. Confirm that `/Applications/VirtualHIDBroker.app` is allowed in +**System Settings > Privacy & Security > Device Control and Data Access**, and activate a machine license under +**Troubleshooting > Virtual HID Broker License** in Sunshine. The menu bar **Virtual HID Broker** submenu also +shows license status and links for license management and downloads. If the broker or license is unavailable, the +Troubleshooting page shows the broker response. Sunshine can show a startup notice when gamepad input is enabled +and the broker or license is unavailable. Choose **None** under **Configuration > Input > Gamepad Backend** to disable +virtual gamepads and that notice without affecting keyboard or mouse input. Reconnect the Moonlight session after choosing +a different emulated gamepad profile. + ### Dynamic session lookup failed If you get this error: @@ -321,46 +333,47 @@ launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist ### No gamepad detected Sunshine supports two virtual gamepad backends on Windows. You can install the -[Virtual HID Driver](https://github.com/LizardByte/libvirtualhid/releases/latest) separately as an optional paid upgrade -for a driver-backed Raw Input keyboard and mouse plus full virtual gamepad support. ViGEmBus is a limited alternative -for Xbox 360 and DualShock 4 support that has reached end of life. If you use the +[Virtual HID Broker](https://github.com/LizardByte/libvirtualhid/releases/latest) separately as an optional paid upgrade. +Its Windows package includes a broker service and user-mode driver for a Raw Input keyboard and mouse plus full virtual +gamepad support. ViGEmBus is a limited alternative for Xbox 360 and DualShock 4 support that has reached end of life. If you use the [ViGEmBus fallback](https://github.com/nefarius/ViGEmBus/releases/latest), you must use version 1.17 or newer. -When Virtual HID Driver is used, Sunshine requires version `2026.914.1218.10` or newer. +When Virtual HID Broker is used, Sunshine requires libvirtualhid version `2026.914.1218.10` or newer. -Virtual HID Driver adds Xbox One, Xbox Series, DualSense, Nintendo Switch Pro, and Generic gamepads, plus advanced +Virtual HID Broker adds Xbox One, Xbox Series, DualSense, Nintendo Switch Pro, and Generic gamepads, plus advanced controller features such as motion, touchpads, LEDs, and adaptive triggers when supported. Unlike the discontinued -ViGEmBus project, Virtual HID Driver is actively developed and supported by the LizardByte team. +ViGEmBus project, Virtual HID Broker is actively developed and supported by the LizardByte team. -An active paid Virtual HID Driver machine license is required before Sunshine can create driver-backed libvirtualhid +An active paid Virtual HID Broker machine license is required before Sunshine can create driver-backed libvirtualhid devices, including gamepads and the Raw Input keyboard and mouse. Use the message on the Web UI home page, the startup -tray notification, or **Get/Manage License** in the **Virtual HID Driver** tray submenu to open the license section on -the Troubleshooting page. In **Configuration > Input**, select **All Available Drivers**, only **Virtual HID Driver**, or +tray notification, or **Get/Manage License** in the **Virtual HID Broker** tray submenu to open the license section on +the Troubleshooting page. In **Configuration > Input**, select **All Available Drivers**, only **Virtual HID Broker**, or only **ViGEmBus**. Sunshine keeps prompting until this setting is saved, but automatically selects **All Available -Drivers** when it detects an existing active Virtual HID Driver license. Whenever the Virtual HID Driver license is not +Drivers** when it detects an existing active Virtual HID Broker license. Whenever the Virtual HID Broker license is not valid, **All Available Drivers** falls back to a compatible ViGEmBus installation for Xbox 360 and DualShock 4 gamepads -and to SendInput for keyboard and mouse. Selecting only **ViGEmBus** suppresses Virtual HID Driver startup notifications -and hides its status and license details from the Troubleshooting page. +and to SendInput for keyboard and mouse. Selecting only **ViGEmBus** suppresses Virtual HID Broker startup notifications +and hides its status and license details from the Troubleshooting page. Selecting **None** disables all virtual +gamepads and suppresses broker and driver choice notices without affecting keyboard or mouse input. After installation, it is recommended to restart your computer. ### Games do not detect keyboard input -With a compatible Virtual HID Driver and active license, Sunshine sends normal key transitions through a real HID +With a compatible Virtual HID Broker and active license, Sunshine sends normal key transitions through a real HID keyboard so games using Raw Input can receive them. Unicode text input and keys outside the supported HID keyboard page continue to use Windows input injection. When the driver-backed keyboard cannot be created because the driver, broker, or license is unavailable, libvirtualhid falls back to SendInput. -Check the Virtual HID Driver version and license sections on the Web UI Troubleshooting page. Sunshine recreates the +Check the libvirtualhid driver version and Virtual HID Broker license on the Web UI Troubleshooting page. Sunshine recreates the shared keyboard and mouse after a successful license activation, validation, or deactivation, so you do not need to restart Sunshine merely to switch between the HID and SendInput paths. ### Games do not detect mouse input -With a compatible Virtual HID Driver and active license, Sunshine sends relative mouse movement, buttons, and scrolling +With a compatible Virtual HID Broker and active license, Sunshine sends relative mouse movement, buttons, and scrolling through a real HID device so games using Raw Input can receive them. Absolute positioning still uses Windows input injection. When the driver-backed mouse cannot be created, libvirtualhid falls back to SendInput; the Windows cursor may still move even though a game that listens only for Raw Input receives nothing. -Check the Virtual HID Driver version and license sections on the Web UI Troubleshooting page even when controller input +Check the libvirtualhid driver version and Virtual HID Broker license on the Web UI Troubleshooting page even when controller input is disabled. The same live refresh used by the keyboard path also switches the mouse between HID and SendInput without requiring a Sunshine restart. diff --git a/gh-pages-template/_data/features.yml b/gh-pages-template/_data/features.yml index ac66b9eabd8..067bdcd7a2f 100644 --- a/gh-pages-template/_data/features.yml +++ b/gh-pages-template/_data/features.yml @@ -27,11 +27,8 @@ - title: "Control" icon_fa: "fas fa-gamepad" description: > - Sunshine emulates an Xbox, PlayStation, or Nintendo Switch controller. + Sunshine emulates Xbox 360|One|Series, PlayStation DualShock 4|DualSense, or Nintendo Switch Pro controllers. Use nearly any controller on your Moonlight client! -
- title: "Configurable" icon_fa: "fas fa-gear" diff --git a/src/config.cpp b/src/config.cpp index e0e864735da..7bdd9f7c5ac 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -850,11 +850,8 @@ namespace config { 500ms, // key_repeat_delay std::chrono::duration {1 / 24.9}, // key_repeat_period - { - platf::supported_gamepads(nullptr).front().name.data(), - platf::supported_gamepads(nullptr).front().name.size(), - }, // Default gamepad - {}, // gamepad_driver remains unset until the user chooses a Windows driver policy + "auto", // Default gamepad profile. + {}, // Windows requests a backend choice; macOS defaults to Virtual HID Broker. true, // back as touchpad click enabled for PlayStation-style gamepads true, // client gamepads with motion events use PlayStation-style emulation true, // client gamepads with touchpads use PlayStation-style emulation @@ -1571,14 +1568,17 @@ namespace config { * * @return Platform-supported gamepad backend names accepted by configuration. */ - std::vector &get_supported_gamepad_options() { - const auto options = platf::supported_gamepads(nullptr); - static std::vector opts {}; - opts.reserve(options.size()); - for (auto &opt : options) { - opts.emplace_back(opt.name); - } - return opts; + const std::vector &get_supported_gamepad_options() { + static const auto gamepads = platf::supported_gamepads(nullptr); + static const auto options = []() { + std::vector names; + names.reserve(gamepads.size()); + for (const auto &gamepad : gamepads) { + names.emplace_back(gamepad.name); + } + return names; + }(); + return options; } /** @@ -1828,6 +1828,7 @@ namespace config { GAMEPAD_DRIVER_ALL, GAMEPAD_DRIVER_VIRTUALHID, GAMEPAD_DRIVER_VIGEMBUS, + GAMEPAD_DRIVER_NONE, }); string_restricted_f(vars, "gamepad"s, input.gamepad, get_supported_gamepad_options()); #ifdef _WIN32 diff --git a/src/config.h b/src/config.h index 78615387365..8bd3aa4d533 100644 --- a/src/config.h +++ b/src/config.h @@ -34,8 +34,9 @@ namespace config { constexpr int PACKETSIZE_LARGE = 1456; ///< Default large packet size that avoids common MTU fragmentation. inline constexpr std::string_view GAMEPAD_DRIVER_ALL = "all"; ///< Allow every available Windows virtual gamepad driver. - inline constexpr std::string_view GAMEPAD_DRIVER_VIRTUALHID = "virtualhid"; ///< Allow only Virtual HID Driver for Windows gamepads. + inline constexpr std::string_view GAMEPAD_DRIVER_VIRTUALHID = "virtualhid"; ///< Use Virtual HID Broker for gamepads on Windows or macOS. inline constexpr std::string_view GAMEPAD_DRIVER_VIGEMBUS = "vigembus"; ///< Allow only ViGEmBus for Windows gamepads. + inline constexpr std::string_view GAMEPAD_DRIVER_NONE = "none"; ///< Disable virtual gamepads on Windows and macOS. // track modified config options inline std::unordered_map modified_config_settings; ///< Configuration keys changed during the current parse or UI update. @@ -291,7 +292,7 @@ namespace config { std::chrono::duration key_repeat_period; ///< Interval between repeated keyboard key events. std::string gamepad; ///< Virtual controller profile selected by configuration. - std::string gamepad_driver; ///< Windows virtual gamepad driver policy, or empty until the user chooses one. + std::string gamepad_driver; ///< Virtual gamepad backend policy on Windows and macOS. bool ds4_back_as_touchpad_click; ///< Map Back/Select to touchpad click for PlayStation-style gamepads. bool motion_as_ds4; ///< Prefer PlayStation-style emulation for client gamepads with motion controls. bool touchpad_as_ds4; ///< Prefer PlayStation-style emulation for client gamepads with touchpad input. diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 5bfcf1f2092..b0241854a8d 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -8,6 +8,7 @@ // standard includes #include +#include #include #include #include @@ -27,12 +28,17 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) #include "platform/virtualhid_input.h" +#endif + +#ifdef _WIN32 #include "platform/windows/misc.h" #include "platform/windows/utf_utils.h" #include +#elif defined(__APPLE__) + #include #endif // local includes @@ -208,7 +214,12 @@ namespace confighttp { */ constexpr auto CSRF_TOKEN_LIFETIME = std::chrono::hours(1); // Tokens valid for 1 hour - constexpr std::string_view libvirtualhid_minimum_version = LIBVIRTUALHID_MINIMUM_VERSION; ///< Minimum supported libvirtualhid driver version. +#ifndef __APPLE__ + constexpr std::string_view libvirtualhid_minimum_version = LIBVIRTUALHID_MINIMUM_VERSION; ///< Minimum supported Windows broker version. +#endif +#ifdef __APPLE__ + constexpr std::string_view libvirtualhid_macos_minimum_version = LIBVIRTUALHID_MACOS_MINIMUM_VERSION; ///< Minimum supported macOS broker bundle version. +#endif constexpr auto VIGEMBUS_MINIMUM_VERSION = "1.17.0.0"sv; ///< Minimum supported ViGEmBus fallback driver version. // NOSONAR(cpp:S1313): not an IP address /** @@ -509,6 +520,42 @@ namespace confighttp { #endif +#ifdef __APPLE__ + /** + * @brief Read the installed macOS Virtual HID Broker app version. + * + * @return Three-part bundle version, or empty when the app is unavailable. + */ + std::string read_libvirtualhid_broker_version() { + const auto bundle_url = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, + CFSTR("/Applications/VirtualHIDBroker.app"), + kCFURLPOSIXPathStyle, + true + ); + if (!bundle_url) { + return {}; + } + + const auto bundle = CFBundleCreate(kCFAllocatorDefault, bundle_url); + CFRelease(bundle_url); + if (!bundle) { + return {}; + } + + std::string version; + const auto value = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); + if (value && CFGetTypeID(value) == CFStringGetTypeID()) { + std::array buffer {}; + if (CFStringGetCString(static_cast(value), buffer.data(), buffer.size(), kCFStringEncodingUTF8)) { + version = buffer.data(); + } + } + CFRelease(bundle); + return version; + } +#endif + /** * @brief Log the request details. * @param request The HTTP request object. @@ -1959,15 +2006,21 @@ namespace confighttp { } /** - * @brief Build libvirtualhid driver version and installation status. + * @brief Build Virtual HID Broker version and installation status. * - * @return libvirtualhid driver status JSON. + * @return Virtual HID Broker status JSON. */ nlohmann::json get_virtualhid_driver_status() { -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) + #ifdef _WIN32 const auto version_str = read_libvirtualhid_driver_version(); + const auto minimum_version = libvirtualhid_minimum_version; + #else + const auto version_str = read_libvirtualhid_broker_version(); + const auto minimum_version = libvirtualhid_macos_minimum_version; + #endif const auto driver_detected = !version_str.empty(); - auto output_tree = build_driver_status(driver_detected, version_str, libvirtualhid_minimum_version); + auto output_tree = build_driver_status(driver_detected, version_str, minimum_version); bool requires_installed_driver = true; std::string backend_name; std::string runtime_error_message; @@ -1978,7 +2031,7 @@ namespace confighttp { const auto &capabilities = runtime->capabilities(); backend_name = capabilities.backend_name; requires_installed_driver = capabilities.requires_installed_driver; - output_tree = build_driver_status(driver_detected || capabilities.supports_gamepad, version_str, libvirtualhid_minimum_version); + output_tree = build_driver_status(driver_detected || capabilities.supports_gamepad, version_str, minimum_version); } } catch (const std::bad_alloc &exception) { runtime_error_message = exception.what(); @@ -1991,7 +2044,7 @@ namespace confighttp { } #else auto output_tree = build_driver_status(false, "", libvirtualhid_minimum_version); - output_tree["error"] = "libvirtualhid driver status is only available on Windows"; + output_tree["error"] = "Virtual HID Broker status is only available on Windows and macOS"; output_tree["backend_name"] = ""; output_tree["requires_installed_driver"] = false; #endif @@ -2112,7 +2165,7 @@ namespace confighttp { #ifdef _WIN32 config::select_all_gamepad_drivers_if_licensed(result.license.licensed()); #endif -#if defined(_WIN32) && defined(SUNSHINE_TRAY) && SUNSHINE_TRAY >= 1 +#if (defined(_WIN32) || defined(__APPLE__)) && defined(SUNSHINE_TRAY) && SUNSHINE_TRAY >= 1 system_tray::update_tray_virtualhid_license(result.license, false); #endif #ifdef _WIN32 diff --git a/src/confighttp.h b/src/confighttp.h index 7e083760517..1e6d88ebda0 100644 --- a/src/confighttp.h +++ b/src/confighttp.h @@ -160,9 +160,9 @@ namespace confighttp { nlohmann::json build_virtualhid_license_status(const lvh::LicenseResult &result); /** - * @brief Build libvirtualhid driver version and installation status. + * @brief Build Virtual HID Broker version and installation status. * - * @return libvirtualhid driver status JSON. + * @return Virtual HID Broker status JSON. */ nlohmann::json get_virtualhid_driver_status(); diff --git a/src/input.cpp b/src/input.cpp index 4c1660835fb..b30352205b8 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1333,7 +1333,7 @@ namespace input { * @param packet The controller arrival packet. */ void passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) { - if (!config::input.controller) { + if (!config::input.controller || config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return; } @@ -1483,7 +1483,7 @@ namespace input { * @param packet The controller touch packet. */ void passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet) { - if (!config::input.controller) { + if (!config::input.controller || config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return; } @@ -1516,7 +1516,7 @@ namespace input { * @param packet The controller motion packet. */ void passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet) { - if (!config::input.controller) { + if (!config::input.controller || config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return; } @@ -1548,7 +1548,7 @@ namespace input { * @param packet The controller battery packet. */ void passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet) { - if (!config::input.controller) { + if (!config::input.controller || config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return; } @@ -1579,7 +1579,7 @@ namespace input { * @param packet Protocol packet being processed. */ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { - if (!config::input.controller) { + if (!config::input.controller || config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return; } diff --git a/src/main.cpp b/src/main.cpp index 21fb8ef0ec2..de45d2cbf7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -501,9 +501,11 @@ int main(int argc, char *argv[]) { if (tray_is_enabled && config::sunshine.system_tray) { BOOST_LOG(info) << "Starting system tray"sv; -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) system_tray::prepare_tray_virtualhid_license(); system_tray::prepare_tray_virtualhid_driver(); +#endif +#ifdef _WIN32 // TODO: Windows has a weird bug where when running as a service and on the first Windows boot, // the tray icon would not appear even though Sunshine is running correctly otherwise. // Restarting the service would allow the icon to appear normally. diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index 8b257a40053..dbc19cb27c8 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -12,6 +12,9 @@ #include #include +// lib includes +#include + // local includes #include "src/config.h" #include "src/platform/virtualhid_input.h" @@ -37,7 +40,7 @@ namespace platf { } const auto &capabilities = runtime->capabilities(); - if (capabilities.supports_gamepad && virtualhid::configured_gamepad_supports_controller_extensions()) { + if (config::input.gamepad_driver != config::GAMEPAD_DRIVER_NONE && capabilities.supports_gamepad && lvh::get_license_status().license.licensed() && virtualhid::configured_gamepad_supports_controller_extensions()) { caps |= platform_caps::controller_touch; } if (config::input.native_pen_touch && (capabilities.supports_touchscreen || capabilities.supports_pen_tablet)) { @@ -54,7 +57,13 @@ namespace platf { return gamepads; } - gamepads = virtualhid::supported_gamepads(virtualhid::get_input_context(*input).runtime.get()); + if (config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { + gamepads.clear(); + return gamepads; + } + + const auto licensed = lvh::get_license_status().license.licensed(); + gamepads = virtualhid::supported_gamepads(virtualhid::get_input_context(*input).runtime.get(), false, licensed, true); return gamepads; } diff --git a/src/platform/virtualhid_input.cpp b/src/platform/virtualhid_input.cpp index 362f50af5e0..db710dae6d4 100644 --- a/src/platform/virtualhid_input.cpp +++ b/src/platform/virtualhid_input.cpp @@ -542,14 +542,15 @@ namespace platf::virtualhid { std::vector supported_gamepads( lvh::Runtime *runtime, const bool fallback_vigem_available, - const bool virtualhid_licensed + const bool virtualhid_licensed, + const bool require_license ) { if (!runtime) { return static_supported_gamepads(); } const auto &capabilities = runtime->capabilities(); - const auto license_valid = !capabilities.requires_installed_driver || virtualhid_licensed; + const auto license_valid = (!capabilities.requires_installed_driver && !require_license) || virtualhid_licensed; const auto libvirtualhid_available = capabilities.supports_gamepad && license_valid; std::string reason; if (!capabilities.supports_gamepad) { @@ -582,7 +583,7 @@ namespace platf::virtualhid { const std::string_view gamepad_driver, const bool virtualhid_licensed ) { - return gamepad_driver != config::GAMEPAD_DRIVER_VIGEMBUS && capabilities.supports_gamepad && + return gamepad_driver != config::GAMEPAD_DRIVER_VIGEMBUS && gamepad_driver != config::GAMEPAD_DRIVER_NONE && capabilities.supports_gamepad && (!capabilities.requires_installed_driver || virtualhid_licensed); } @@ -591,7 +592,7 @@ namespace platf::virtualhid { const bool virtualhid_selected, const std::string_view gamepad_driver ) { - if (gamepad_driver == config::GAMEPAD_DRIVER_VIRTUALHID) { + if (gamepad_driver == config::GAMEPAD_DRIVER_VIRTUALHID || gamepad_driver == config::GAMEPAD_DRIVER_NONE) { return false; } if (gamepad_driver == config::GAMEPAD_DRIVER_VIGEMBUS || !virtualhid_selected) { diff --git a/src/platform/virtualhid_input.h b/src/platform/virtualhid_input.h index d6302b25529..967b3bbb246 100644 --- a/src/platform/virtualhid_input.h +++ b/src/platform/virtualhid_input.h @@ -106,13 +106,15 @@ namespace platf::virtualhid { * * @param runtime Runtime to probe. * @param fallback_vigem_available Whether Windows ViGEm fallback can create gamepads. - * @param virtualhid_licensed Whether an installed-driver runtime has a valid license. + * @param virtualhid_licensed Whether the broker has a valid license. + * @param require_license Whether this platform requires a broker license for gamepads. * @return Supported gamepad choices. */ std::vector supported_gamepads( lvh::Runtime *runtime, bool fallback_vigem_available = false, - bool virtualhid_licensed = true + bool virtualhid_licensed = true, + bool require_license = false ); /** diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index 5eac499ed85..54f09f5cc19 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -1274,6 +1274,11 @@ namespace platf { } const auto raw = (input_raw_t *) input->get(); + if (config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE) { + gps.clear(); + return gps; + } + if (config::input.gamepad_driver == config::GAMEPAD_DRIVER_VIGEMBUS) { gps = vigembus_supported_gamepads(raw->vigem != nullptr); return gps; @@ -1310,7 +1315,7 @@ namespace platf { platform_caps::caps_t get_capabilities() { platform_caps::caps_t caps = 0; - if (virtualhid::configured_gamepad_supports_controller_extensions()) { + if (config::input.gamepad_driver != config::GAMEPAD_DRIVER_NONE && virtualhid::configured_gamepad_supports_controller_extensions()) { caps |= platform_caps::controller_touch; } diff --git a/src/system_tray.cpp b/src/system_tray.cpp index 0a2ae33aa35..53fc797d391 100644 --- a/src/system_tray.cpp +++ b/src/system_tray.cpp @@ -27,7 +27,7 @@ #define TRAY_ICON_LOCKED WEB_DIR "images/sunshine-locked.svg" /** * @def TRAY_ICON_VIRTUALHID - * @brief Path to the Virtual HID Driver notification icon. + * @brief Path to the Virtual HID Broker notification icon. */ #define TRAY_ICON_VIRTUALHID WEB_DIR "images/logo-libvirtualhid.svg" @@ -65,7 +65,7 @@ // lib includes #include #include - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) #include #endif @@ -134,9 +134,9 @@ namespace system_tray { } }; - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) /** - * @brief Access storage for dynamic Virtual HID Driver license menu labels. + * @brief Access storage for dynamic Virtual HID Broker license menu labels. * * @return Persistent string storage backing the tray menu label pointers. */ @@ -146,7 +146,7 @@ namespace system_tray { } /** - * @brief Access storage for the Virtual HID Driver compatibility notification. + * @brief Access storage for the Virtual HID Broker compatibility notification. * * @return Persistent string storage backing the tray notification pointer. */ @@ -155,13 +155,14 @@ namespace system_tray { return notification_text; } + #ifdef _WIN32 /** - * @brief Access persistent storage for the Virtual HID Driver benefits menu. + * @brief Access persistent storage for the Virtual HID Broker benefits menu. * * The tray C API requires a mutable submenu pointer even though Sunshine * treats these entries as immutable. * - * @return Persistent Virtual HID Driver benefits menu storage. + * @return Persistent Virtual HID Broker benefits menu storage. */ std::array &virtualhid_benefits_menu_storage() { static std::array benefits_menu {{ @@ -173,11 +174,12 @@ namespace system_tray { }}; return benefits_menu; } + #endif #endif } // namespace - #ifdef _WIN32 - constexpr auto LIBVIRTUALHID_RELEASES_URL = "https://github.com/LizardByte/libvirtualhid/releases/latest"sv; ///< Latest Virtual HID Driver release. + #if defined(_WIN32) || defined(__APPLE__) + constexpr auto LIBVIRTUALHID_RELEASES_URL = "https://github.com/LizardByte/libvirtualhid/releases/latest"sv; ///< Latest Virtual HID Broker release. #endif void tray_open_ui_cb([[maybe_unused]] struct tray_menu *item) { @@ -197,14 +199,18 @@ namespace system_tray { platf::open_url("https://www.paypal.com/paypalme/ReenigneArcher"); } - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) void tray_virtualhid_license_cb([[maybe_unused]] struct tray_menu *item) { - BOOST_LOG(info) << "Opening Virtual HID Driver license settings from system tray"sv; + BOOST_LOG(info) << "Opening Virtual HID Broker license settings from system tray"sv; + #ifdef _WIN32 launch_ui(config::input.gamepad_driver == config::GAMEPAD_DRIVER_VIGEMBUS ? "/config#gamepad_driver" : "/troubleshooting#virtualhid-license"); + #else + launch_ui("/troubleshooting#virtualhid-license"); + #endif } void tray_virtualhid_download_cb([[maybe_unused]] struct tray_menu *item) { - BOOST_LOG(info) << "Opening Virtual HID Driver download from system tray"sv; + BOOST_LOG(info) << "Opening Virtual HID Broker download from system tray"sv; platf::open_url(std::string {LIBVIRTUALHID_RELEASES_URL}); } #endif @@ -261,9 +267,9 @@ namespace system_tray { lifetime::exit_sunshine(0, true); } - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) /** - * @brief Create the initial Virtual HID Driver license submenu. + * @brief Create the initial Virtual HID Broker license submenu. * * @return Menu storage with a checking state, benefits, license settings, and driver download. */ @@ -272,12 +278,16 @@ namespace system_tray { menu[0] = {.text = "Status: Checking", .disabled = 1}; menu[1] = {.text = "-"}; menu[2] = {.text = "Get/Manage License", .cb = tray_virtualhid_license_cb}; - menu[3] = {.text = "Virtual HID Driver Benefits", .submenu = virtualhid_benefits_menu_storage().data()}; - menu[4] = {.text = "Download Virtual HID Driver", .cb = tray_virtualhid_download_cb}; + #ifdef _WIN32 + menu[3] = {.text = "Virtual HID Broker Benefits", .submenu = virtualhid_benefits_menu_storage().data()}; + menu[4] = {.text = "Download Virtual HID Broker", .cb = tray_virtualhid_download_cb}; + #else + menu[3] = {.text = "Download Virtual HID Broker", .cb = tray_virtualhid_download_cb}; + #endif return menu; } - static auto virtualhid_license_menu = initial_virtualhid_license_menu(); ///< Virtual HID Driver license submenu. + static auto virtualhid_license_menu = initial_virtualhid_license_menu(); ///< Virtual HID Broker license submenu. #endif // Tray menu @@ -289,8 +299,8 @@ namespace system_tray { // Tray menu labels currently use the project's English source strings. {.text = "Open Sunshine", .cb = tray_open_ui_cb}, {.text = "-"}, - #ifdef _WIN32 - {.text = "Virtual HID Driver", .submenu = virtualhid_license_menu.data()}, + #if defined(_WIN32) || defined(__APPLE__) + {.text = "Virtual HID Broker", .submenu = virtualhid_license_menu.data()}, {.text = "-"}, #endif {.text = "Donate", @@ -310,7 +320,7 @@ namespace system_tray { {.text = "Quit", .cb = tray_quit_cb}, {.text = nullptr} }, - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) .iconPathCount = 5, .allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING, TRAY_ICON_VIRTUALHID}, #else @@ -336,7 +346,7 @@ namespace system_tray { tray.notification_text = nullptr; tray.notification_title = nullptr; tray.notification_cb = nullptr; - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) virtualhid_license_menu_text_storage() = {}; virtualhid_driver_notification_text_storage().clear(); virtualhid_license_menu = initial_virtualhid_license_menu(); @@ -344,9 +354,9 @@ namespace system_tray { } #endif - #ifdef _WIN32 + #if defined(_WIN32) || defined(__APPLE__) /** - * @brief Return the user-visible label for a Virtual HID Driver license state. + * @brief Return the user-visible label for a Virtual HID Broker license state. * * @param state License state reported by libvirtualhid. * @return Short state label suitable for a tray menu. @@ -398,7 +408,7 @@ namespace system_tray { } /** - * @brief Assign text and behavior to one Virtual HID Driver submenu item. + * @brief Assign text and behavior to one Virtual HID Broker submenu item. * * @tparam Callback Callback type accepted by the tray library. * @param index Submenu index to populate. @@ -423,7 +433,7 @@ namespace system_tray { } /** - * @brief Rebuild the Virtual HID Driver submenu for the latest license state. + * @brief Rebuild the Virtual HID Broker submenu for the latest license state. * * @param license Latest machine license details. */ @@ -446,13 +456,17 @@ namespace system_tray { set_virtualhid_license_menu_item( 3, license.activation_limit == 0 ? - "Machine activations: Not reported" : - std::format("Machine activations: {} / {}", license.activation_usage, license.activation_limit), + "Machine activation limit: Not reported" : + std::format("Machine activation limit: {}", license.activation_limit), true ); } else { set_virtualhid_license_menu_item(1, std::string {virtualhid_license_state_detail(license.state)}, true); + #ifdef _WIN32 set_virtualhid_license_menu_item(2, "Driver-backed keyboard, mouse, and gamepads are locked", true); + #else + set_virtualhid_license_menu_item(2, "Virtual gamepads are locked", true); + #endif set_virtualhid_license_menu_item( 3, license.service_available ? "License service: Available" : "License service: Unavailable", @@ -461,9 +475,13 @@ namespace system_tray { } virtualhid_license_menu[4] = {.text = "-"}; set_virtualhid_license_menu_item(5, "Get/Manage License", false, tray_virtualhid_license_cb); - set_virtualhid_license_menu_item(6, "Virtual HID Driver Benefits", false); + #ifdef _WIN32 + set_virtualhid_license_menu_item(6, "Virtual HID Broker Benefits", false); virtualhid_license_menu[6].submenu = virtualhid_benefits_menu_storage().data(); - set_virtualhid_license_menu_item(7, "Download Virtual HID Driver", false, tray_virtualhid_download_cb); + set_virtualhid_license_menu_item(7, "Download Virtual HID Broker", false, tray_virtualhid_download_cb); + #else + set_virtualhid_license_menu_item(6, "Download Virtual HID Broker", false, tray_virtualhid_download_cb); + #endif } /** @@ -481,16 +499,17 @@ namespace system_tray { clear_tray_notification(); rebuild_virtualhid_license_menu(license); - if (config::input.gamepad_driver.empty()) { - tray.notification_title = "Choose a Gamepad Driver"; + #ifdef _WIN32 + if (config::input.controller && config::input.gamepad_driver.empty()) { + tray.notification_title = "Choose a Gamepad Backend"; tray.notification_text = - "Choose a driver in Input settings. Virtual HID Driver is a paid upgrade; ViGEmBus is limited and has reached end of life."; + "Choose a gamepad backend in Input settings. Virtual HID Broker is a paid upgrade; ViGEmBus is limited and has reached end of life."; tray.notification_icon = tray.allIconPaths[4]; tray.notification_cb = []() { launch_ui("/config#gamepad_driver"); }; - } else if (config::input.gamepad_driver != config::GAMEPAD_DRIVER_VIGEMBUS && notify_if_unlicensed && !license.licensed()) { - tray.notification_title = "Virtual HID Driver License"; + } else if (config::input.controller && config::input.gamepad_driver != config::GAMEPAD_DRIVER_NONE && config::input.gamepad_driver != config::GAMEPAD_DRIVER_VIGEMBUS && notify_if_unlicensed && !license.licensed()) { + tray.notification_title = "Virtual HID Broker License"; tray.notification_text = "Get or manage a license, or use the limited, end-of-life ViGEmBus driver."; tray.notification_icon = tray.allIconPaths[4]; @@ -498,6 +517,24 @@ namespace system_tray { launch_ui("/troubleshooting#virtualhid-license"); }; } + #else + if (config::input.controller && config::input.gamepad_driver != config::GAMEPAD_DRIVER_NONE && notify_if_unlicensed && !license.licensed()) { + if (license.service_available) { + tray.notification_title = "Virtual HID Broker License"; + tray.notification_text = "Activate a machine license to use virtual gamepads. Click to manage the license."; + tray.notification_cb = []() { + launch_ui("/troubleshooting#virtualhid-license"); + }; + } else { + tray.notification_title = "Virtual HID Broker Is Unavailable"; + tray.notification_text = "Install and start Virtual HID Broker to use virtual gamepads. Click to download."; + tray.notification_cb = []() { + tray_virtualhid_download_cb(nullptr); + }; + } + tray.notification_icon = tray.allIconPaths[4]; + } + #endif if (tray_initialized_state().load()) { tray_update(&tray); @@ -515,9 +552,14 @@ namespace system_tray { const bool version_compatible, const std::string_view supported_versions ) { - if (config::input.gamepad_driver.empty() || config::input.gamepad_driver == config::GAMEPAD_DRIVER_VIGEMBUS || !installed || version_compatible) { + if (config::input.gamepad_driver == config::GAMEPAD_DRIVER_NONE || !installed || version_compatible) { return; } + #ifdef _WIN32 + if (config::input.gamepad_driver.empty() || config::input.gamepad_driver == config::GAMEPAD_DRIVER_VIGEMBUS) { + return; + } + #endif const std::scoped_lock lock(tray_state_mutex()); clear_tray_notification(); @@ -525,11 +567,11 @@ namespace system_tray { const auto displayed_version = version.empty() ? "unknown" : std::format("v{}", version); auto ¬ification_text = virtualhid_driver_notification_text_storage(); notification_text = std::format( - "Installed Virtual HID Driver {} is not supported by this version of Sunshine. Supported versions: {}. Restart Sunshine after updating. Click for instructions.", + "Installed Virtual HID Broker {} is not supported by this version of Sunshine. Supported versions: {}. Restart Sunshine after updating. Click for instructions.", displayed_version, supported_versions ); - tray.notification_title = "Update Virtual HID Driver"; + tray.notification_title = "Update Virtual HID Broker"; tray.notification_text = notification_text.c_str(); tray.notification_icon = tray.allIconPaths[4]; tray.notification_cb = []() { diff --git a/src/system_tray.h b/src/system_tray.h index f0fa019b1fb..c1383121d80 100644 --- a/src/system_tray.h +++ b/src/system_tray.h @@ -8,7 +8,7 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) namespace lvh { struct LicenseStatus; } @@ -42,15 +42,15 @@ namespace system_tray { */ void tray_donate_paypal_cb([[maybe_unused]] struct tray_menu *item); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) /** - * @brief Callback for opening Virtual HID Driver license settings in the Web UI. + * @brief Callback for opening Virtual HID Broker license settings in the Web UI. * @param item The tray menu item. */ void tray_virtualhid_license_cb([[maybe_unused]] struct tray_menu *item); /** - * @brief Callback for opening the latest Virtual HID Driver release. + * @brief Callback for opening the latest Virtual HID Broker release. * @param item The tray menu item. */ void tray_virtualhid_download_cb([[maybe_unused]] struct tray_menu *item); @@ -115,28 +115,28 @@ namespace system_tray { */ void update_tray_require_pin(); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) /** - * @brief Update the Virtual HID Driver license submenu and optional notification. + * @brief Update the Virtual HID Broker license submenu and optional notification. * * @param license Latest machine license details. - * @param notify_if_unlicensed Whether to notify the user when the machine is not activated and ViGEmBus is not exclusively selected. + * @param notify_if_unlicensed Whether to notify the user when the broker needs a license. */ void update_tray_virtualhid_license(const lvh::LicenseStatus &license, bool notify_if_unlicensed); /** - * @brief Query the Virtual HID Driver license and prepare the startup tray state. + * @brief Query the Virtual HID Broker license and prepare the startup tray state. */ void prepare_tray_virtualhid_license(); /** - * @brief Show an update notification for an unsupported Virtual HID Driver. + * @brief Show an update notification for an unsupported Virtual HID Broker. * - * Existing notifications are preserved while the driver choice is unset, when ViGEmBus is exclusively selected, - * or when the driver is absent or supported. + * Existing notifications are preserved when gamepad input is disabled, the broker is absent or supported, + * or the Windows backend choice does not use the broker. * - * @param installed Whether the driver is installed. - * @param version Installed driver version. + * @param installed Whether the broker is installed. + * @param version Installed broker version. * @param version_compatible Whether Sunshine supports the installed version. * @param supported_versions User-visible supported version range. */ @@ -148,7 +148,7 @@ namespace system_tray { ); /** - * @brief Query the Virtual HID Driver version and prepare its startup notification. + * @brief Query the Virtual HID Broker version and prepare its startup notification. */ void prepare_tray_virtualhid_driver(); #endif diff --git a/src_assets/common/assets/web/Home.vue b/src_assets/common/assets/web/Home.vue index 125738664ef..8d6ac5217c3 100644 --- a/src_assets/common/assets/web/Home.vue +++ b/src_assets/common/assets/web/Home.vue @@ -21,7 +21,7 @@ - +
@@ -188,19 +188,23 @@ this.preReleaseVersion = new SunshineVersion((await fetch("https://api.github.com/repos/LizardByte/Sunshine/releases").then((r) => r.json())).find(release => release.prerelease), null); console.log("Pre-Release Version: ", this.preReleaseVersion.version) - // The Virtual HID Driver also backs relative mouse input when controllers are disabled. - if (this.platform === 'windows') { + // Read the broker version on both platforms to identify development builds. + if (this.platform === 'windows' || this.platform === 'macos') { try { const virtualInputStatus = await fetch("./api/virtual-input/status").then((r) => r.json()); this.virtualhid = virtualInputStatus.virtualhid; - this.vigembus = virtualInputStatus.vigembus; + if (this.platform === 'windows') { + this.vigembus = virtualInputStatus.vigembus; + } } catch (e) { console.error("Failed to fetch virtual input driver status:", e); } + } + if (this.platform === 'windows' || this.platform === 'macos') { try { this.virtualhidLicense = await fetch("./api/virtual-input/license").then((r) => r.json()); } catch (e) { - console.error("Failed to fetch Virtual HID Driver license status:", e); + console.error("Failed to fetch Virtual HID Broker license status:", e); } } } catch (e) { @@ -215,11 +219,18 @@ }, computed: { /** - * Build the single Windows virtual-input message shown on the home page. - * Warnings are reserved for an unusable selected backend, an unsupported - * installed driver, or an installed driver with an invalid license. + * Build the virtual-input message shown on the home page. + * Warn about broker or gamepad driver issues when virtual gamepads are enabled. */ virtualInputNotice() { + if (!this.controllerEnabled || this.gamepadDriver === 'none') { + return null; + } + + if (this.platform === 'macos') { + return this.buildMacosVirtualInputNotice(); + } + if (this.platform !== 'windows' || !this.virtualhid || !this.vigembus) { return null; } @@ -245,7 +256,7 @@ } if (this.gamepadDriver === 'virtualhid') { - return this.buildVirtualInputNotice(true, 'index.virtualhid_required_title', [{ key: 'index.virtualhid_required_desc' }]); + return this.buildVirtualInputNotice(true, 'index.virtualhid_broker_unavailable_title', [{ key: 'index.virtualhid_required_desc' }]); } if (this.controllerEnabled && !vigembusUsable) { @@ -294,7 +305,22 @@ }, methods: { /** - * Build a home-page notice for the current Windows virtual-input state. + * Build the macOS broker notice, prioritizing license and service warnings. + * + * @returns {object|null} Warning or development-build notice, when applicable. + */ + buildMacosVirtualInputNotice() { + if (this.virtualhidLicense && !this.virtualhidLicense.licensed) { + return this.virtualhidLicense.service_available + ? this.buildVirtualInputNotice(true, 'index.virtualhid_macos_license_title', [{ key: 'index.virtualhid_macos_license_desc' }]) + : this.buildVirtualInputNotice(true, 'index.virtualhid_broker_unavailable_title', [{ key: 'index.virtualhid_macos_broker_desc' }]); + } + return this.virtualhid?.development_version + ? this.buildVirtualInputNotice(false, 'index.virtualhid_development_title', [{ key: 'index.virtualhid_development_desc' }]) + : null; + }, + /** + * Build a home-page notice for the current virtual-input state. * * @param {boolean} warning Whether the notice represents an actionable warning. * @param {string} title Localization key for the notice title. diff --git a/src_assets/common/assets/web/Troubleshooting.vue b/src_assets/common/assets/web/Troubleshooting.vue index 1cd1001b882..de679d63e4a 100644 --- a/src_assets/common/assets/web/Troubleshooting.vue +++ b/src_assets/common/assets/web/Troubleshooting.vue @@ -2,8 +2,8 @@

{{ $t('troubleshooting.troubleshooting') }}

- -
+ +