feat(input/windows): add WinUHid backend for native DualSense emulation#4948
feat(input/windows): add WinUHid backend for native DualSense emulation#4948hkirste wants to merge 5 commits into
Conversation
Add cgutman/WinUHid as a submodule. WinUHid is a UMDF driver using Microsoft's VHF (Virtual HID Framework) that enables user-mode applications to create virtual HID devices. It provides ready-made DualSense (PS5), DualShock 4, and Xbox One controller emulation with full feature support including adaptive triggers, motion sensors, touchpad, and lightbar control.
- Add SUNSHINE_ENABLE_WINUHID CMake option (ON by default) - Compile WinUHid and WinUHidPS5 sources as static (WINUHID_STATIC) - Add MinGW-compatible WRL shim for Wrappers::Event/FileHandle (WinUHid uses MSVC WRL for RAII handle wrappers; this shim provides equivalent types for MinGW builds) - Rename DllMain per-file via -D to avoid duplicate symbol errors - Define SUNSHINE_WINUHID preprocessor guard for conditional code
Add a WinUHid-based gamepad backend alongside the existing ViGEmBus backend. When the WinUHid driver is installed, Sunshine can now create virtual DualSense (PS5) controllers that games recognize natively, enabling features that were impossible with DS4 emulation: - Adaptive trigger effects flow from games back to the Moonlight client's physical DualSense controller - RGB lightbar color feedback is now forwarded (was discarded) - Motion sensor data passes through without inverse-calibration hacks (WinUHid provides proper calibration in feature reports) - Touchpad uses native 1920x1080 DualSense resolution (was 1920x943) - Games and Steam detect the controller as a real DualSense Architecture: - winuhid_t manager class parallel to vigem_t - winuhid_gamepad_context_t holds per-gamepad state and PS5 report - Four WinUHid callbacks (rumble, lightbar, player LED, adaptive triggers) push feedback onto the existing feedback_queue - No changes needed to stream.cpp — the adaptive trigger and LED wire protocol support already existed from the Linux DS5 work - Backend selection: WinUHid preferred for PS-type controllers, ViGEm fallback for Xbox 360 or when WinUHid is not installed - All WinUHid code guarded by #ifdef SUNSHINE_WINUHID Adds "ds5" to supported_gamepads when WinUHid is available.
- Make WINUHID_STATIC a global compile definition instead of per-file (fixes dllimport errors when GCC processes WinUHid header includes) - Remove redundant #define WINUHID_STATIC from input.cpp - Rename local variable 'info' to 'gp_info' to avoid collision with Boost.Log's BOOST_LOG(info) macro
- Add noexcept move constructor/assignment to winuhid_gamepad_context_t - Replace C-style void* casts with static_cast - Replace raw new/delete with std::unique_ptr for winuhid_t and vigem_t - Extract should_use_ds5() helper to reduce alloc_gamepad complexity - Extract winuhid_handle_touch() to reduce gamepad_touch complexity - Replace redundant type declarations with auto - Split multi-variable declarations into separate statements - Remove comment that SonarCloud flagged as commented-out code - Use named parameters instead of (void) casts for unused args
|
|
WinUHID is not ready to be used yet. There are no drivers built/released that can be installed easily by Sunshine users. Due to this the build needs to be incorporated into Sunshine's build process. I have a PR open in WinUHID that will make this slightly easier. Until that is merged there is not much point in this PR. And since I'm working on the same exact feature, although I'm completely replacing vigembus... I will close this. |
|
Thanks for the context. Couple of things worth noting: This implementation is fully tested end-to-end. Input passthrough over Bluetooth, adaptive trigger effects on real hardware, rumble, LED, clean ViGEmBus fallback. The code side is solid. On driver distribution, totally fair point. If you're planning to build the UMDF driver as part of Sunshine's build, heads up: it needs the full WDK toolchain (stampinf for INF macros, WPP preprocessing, UMDF version stamping). Building it outside a Happy to have this branch used as reference for the input mapping, feedback callbacks, and the MinGW WRL compatibility shim. Let me know if there's a good way to plug into what you're doing. |
|
Appologies if my first comment was short and didn't explain things in a lot of detail. We definitely need to migrate away from vigembus, so it's not that this is closed because I'm not planning to have this at all. For reference: cgutman/WinUHid#2
It's not that I want to build the driver as part of Sunshine's build process. It would be ideal if it was pre-built and we could just consume it like we do for vigembus. Currently it's not available though, and for sure 99% of windows users are never going to know how to build a driver on their own, lol. My other changes in Sunshine I have kept local for now.
Might be good to submit a PR documenting the process to WinUHid. Does the driver need to be signed? I am assuming it does, and if so what kind of signing is required? OV or EV? I think just OV for user mode, but I am not 100% sure.
Once there is a distributable driver, we can definitely resume this. Maybe we need a mini repo to build and distribute the driver. If OV signing is okay, I'd be happy to setup a build directory if you'd be okay will helping out with the initial workflow? Or should it just be a fork? cgutman is a top contributor here, and obviously the primary Moonlight maintainer, so I definitely don't want to step on their toes. We can discuss further on discord if you'd like. |
|
No worries, makes sense now. On signing: OV is enough for UMDF. During testing I got it working with just a self-signed cert in the TrustedPublisher store, Secure Boot on, no test signing needed. So an OV cert would be more than sufficient for distribution. No EV required, that's only for kernel-mode drivers. A mini repo for the driver build+sign+package makes total sense. I went through the entire build process manually and can document the exact steps. The main pain points were: stampinf for INF macro expansion ( Happy to help set up that workflow. I'll ping you on Discord, username is hkirste. |
Yep, my goal is a prebuilt MSI like Vigembus was. However, we currently have a signing problem that is blocking this.
It does. Unfortunately for full compatibility with all architectures, it needs to be signed through the WHQL process since it's a driver. That means having an EV certificate (and an official legal entity). Since WinUHid is a UMDF driver rather than a driver that loads into kernel mode, you can technically get away with OV for x86 and x64, since they are grandfathered into to lower code integrity requirements for UMDF. However, this strategy doesn't work for ARM64. |
|
@cgutman thanks for the info! Do you have OV signing? I think it would be nice to continue with x86_64 even if we have to continue falling back to vigembus on ARM64 for now. |
|
I do, though I've got to see if I remember the PIN for my HSM 😬 In any case, I will take a look at WinUHid again in a week or so. I need to remember where I left it last year. |
|
For now I created (to be honest, vibed) a fork: https://github.com/lurebat/WinUHid It:
So it will be easier to use and play around with, to hopefully make this happen. I can also open PRs to the original, I just didn't want to by default since it's vibe coded. |




Description
Add a WinUHid-based gamepad backend for native DualSense (PS5) controller emulation on Windows, alongside the existing ViGEmBus backend.
When the WinUHid driver is installed, Sunshine creates virtual DualSense controllers instead of DS4 via ViGEmBus, enabling:
ViGEmBus remains as automatic fallback when WinUHid is not installed. All WinUHid code is guarded by
#ifdef SUNSHINE_WINUHID— zero risk to existing functionality.Architecture:
winuhid_tmanager class parallel to existingvigem_twinuhid_gamepad_context_tholds per-gamepad state and PS5 input reportfeedback_queuestream.cppprotocol handling — uses the same Sunshine extension mechanism (0x55xx) as existing featuresDependencies:
third-party/WinUHid-compat/for cross-compiler supportClient requirements:
d9c7a24, merged 2025-04-02). Stable v6.1.0 does NOT include this — a nightly build or v6.2+ is needed.Cross-project changes (PRs pending):
cgutman/WinUHid— firmware passthrough support, mic LED callback, feature report stubsmoonlight-stream/moonlight-common-c— TLV metadata on controller arrival, player/mic LED protocol extensionsmoonlight-stream/moonlight-qt— firmware read from physical DualSense, player/mic LED forwarding via SDLTesting done:
SUNSHINE_ENABLE_WINUHID=ONandOFF(MinGW g++ 15.2)"WinUHid driver detected (interface version 1)")Screenshot
Issues Fixed or Closed
Roadmap Issues
Type of Change
Checklist
AI Usage