diff --git a/LICENSES/LicenseRef-LizardByte-SAL-1.0.md b/LICENSES/LicenseRef-LizardByte-SAL-1.0.md new file mode 100644 index 0000000..5b82033 --- /dev/null +++ b/LICENSES/LicenseRef-LizardByte-SAL-1.0.md @@ -0,0 +1,217 @@ + LIZARDBYTE SOURCE-AVAILABLE LICENSE + Version 1.0, May 2026 + +Copyright (C) 2026 David Lane. +The Licensor may modify this license document at any time and for any reason. +Everyone else is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + PREAMBLE + +The LizardByte Source-Available License ("LB-SAL") is designed to make +source code available for review and upstream contribution while reserving +all rights not expressly granted by this License. This License is not an open +source license. + + TERMS AND CONDITIONS + +1. Definitions + + "License" refers to this document. + + "Licensor" refers to the copyright owner identified in the copyright notice + above and any successor or assignee who owns the rights licensed under this + License. + + "LizardByte" refers to the Licensor's projects, products, services, + repositories, websites, and official contribution channels operated under the + LizardByte name or under any successor name designated by the Licensor. + + "Software" refers to the copyrightable work that includes a notice stating + that it is licensed under this License. + + "Official Project" means the Software project controlled by the Licensor to + which the Software belongs, including the Licensor's official repository, + issue tracker, pull request system, mailing list, or other contribution + channel designated by the Licensor. + + "Contribution" means any modification, addition, patch, proposal, or other + work of authorship intentionally submitted to the Licensor through an Official + Project for possible inclusion in the Software. + + "Derivative Work" means any work based upon, derived from, adapted from, + translated from, modified from, or incorporating the Software or any portion + of the Software, except for a Contribution made solely as permitted by this + License. + + "Distribute" means to sell, rent, lease, lend, publish, sublicense, transfer, + provide, transmit, host, display, perform, make available, or otherwise give + access to the Software or any Derivative Work to any third party, whether by + traditional distribution channels, digital transmission, Software-as-a- + Service, managed service, hosted service, cloud service, streaming, network + access, remote execution, containerization, virtualization, API access, or any + other method now known or later developed. + + "Commercial Purpose" means any purpose intended for or directed toward + commercial advantage, monetary compensation, revenue generation, paid + services, business operations, or other exchange of value, whether direct or + indirect. + + "Competitive Purpose" means any purpose that develops, tests, supports, + markets, offers, operates, or makes available any product, service, feature, + repository, or project that substitutes for, competes with, or is intended to + reduce demand for any LizardByte project, product, or service. + + "Circumvention" means any act of bypassing, removing, deactivating, + defeating, impairing, or otherwise circumventing technological measures, + licensing logic, payment logic, access controls, feature restrictions, or + business logic that controls access to or enforces limitations on use of the + Software, including any paid or premium features. + +2. Grant of Rights + + Subject to the terms and conditions of this License, the Licensor grants you + a non-exclusive, worldwide, royalty-free, non-sublicensable, non-transferable + license to: + + a. View and inspect the Software for personal, non-commercial purposes. + + b. Run unmodified copies of the Software for personal, non-commercial + evaluation purposes only. + + c. Download, copy, or fork the Software only to the extent necessary to + exercise the rights granted in Sections 2(a), 2(b), and 2(d). + + d. Modify the Software only to the extent necessary to prepare and submit + Contributions to the Official Project, provided that any modification, patch, + branch, fork, or copy is created and used solely for that contribution + purpose. + + e. Submit Contributions to the Licensor through the Official Project. + +3. Restrictions + + Any rights not expressly granted in Section 2 are reserved by the Licensor. + Without limiting that reservation of rights, you may not: + + a. Modify the Software except solely as necessary to prepare a Contribution + that you submit, or in good faith intend to submit, to the Official Project. + You may not create or maintain a private, public, internal, or external fork, + branch, patch set, or Derivative Work for any other purpose. If a + Contribution is rejected, withdrawn, abandoned, or no longer intended for + submission to the Official Project, you must promptly delete, destroy, or + revert all related modifications and Derivative Works. + + b. Distribute the Software or any Derivative Work to any third party by any + means, regardless of whether the Distribution is commercial, non-commercial, + educational, individual, charitable, internal, public, private, or otherwise. + + c. Provide or make available the Software or any Derivative Work as a service, + including but not limited to Software-as-a-Service (SaaS), managed service, + hosted service, cloud service, streaming service, subscription service, + remote access, API access, or any other service model now known or later + developed. + + d. Use the Software or any Derivative Work for any Commercial Purpose without + the Licensor's prior written permission. + + e. Sell, rent, lease, lend, sublicense, or otherwise exchange the Software or + any Derivative Work for value, whether monetary or otherwise. + + f. Use the Software or any Derivative Work for any Competitive Purpose. + + g. Use the Software or any Derivative Work to train, fine tune, benchmark, or + evaluate any machine learning model, artificial intelligence system, code + generation system, or automated software development service without the + Licensor's prior written permission. + + h. Engage in Circumvention. + + i. Remove, obscure, or alter any copyright, license, attribution, trademark, + patent, or other proprietary notice in the Software. + + j. Use any trade name, trademark, service mark, logo, domain name, product + name, or branding of the Licensor or LizardByte except as required for + reasonable and customary attribution. + + k. Grant any sublicense or other downstream license to the Software or any + Derivative Work. + +4. Contributions + + Contributions are accepted only under the Contributor License Agreement, + assignment agreement, or other written contribution terms required by the + Licensor for the Official Project. If no separate written contribution terms + apply, then by submitting a Contribution you grant the Licensor a perpetual, + worldwide, irrevocable, royalty-free, fully paid-up, transferable, + sublicensable license to reproduce, modify, prepare derivative works of, + display, perform, distribute, commercialize, and otherwise use the + Contribution for any purpose and under any license or terms. + +5. Attribution + + All copies or substantial portions of the Software that you are permitted to + possess under this License must include a copy of this License and the + following attribution notice: + + "Contains software developed by LizardByte (https://app.lizardbyte.dev)." + +6. Third-Party Rights + + This License does not grant permission to use third-party software, content, + trademarks, patents, or other rights that may be included in or used by the + Software. You are responsible for complying with all applicable third-party + terms. + +7. Disclaimer of Warranty + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NONINFRINGEMENT. IN NO EVENT + SHALL THE LICENSOR, AUTHORS, CONTRIBUTORS, OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE, + THE USE OF THE SOFTWARE, OR OTHER DEALINGS IN THE SOFTWARE. + +8. Termination + + This License and all rights granted under it terminate automatically upon any + breach by you of this License. Upon termination, you must immediately cease + all use of the Software and destroy all copies of the Software and Derivative + Works in your possession, custody, or control. Sections 3, 4, 6, 7, 8, 9, and + 10 survive termination. + +9. Legal Enforcement + + The Licensor reserves the right to enforce this License through legal action, + including but not limited to seeking injunctive relief, damages, costs, and + attorney's fees for any violation of these terms, to the maximum extent + permitted by law. + +10. Miscellaneous + + a. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable, and the remaining provisions shall remain in effect. + + b. This License represents the complete agreement concerning the subject + matter of this License and supersedes all prior or contemporaneous oral or + written understandings regarding that subject matter. + + c. This License shall be governed by the laws of the United States and, where + state law applies, the laws of the state in which the Licensor is domiciled, + excluding conflict of law rules. + + d. No waiver of any provision of this License shall be deemed a further or + continuing waiver of such term or any other term. Any waiver must be in + writing and signed by the Licensor. + + e. The Licensor may revise, replace, or modify this License at any time and + for any reason. The Licensor may apply any revised, replaced, or modified + version of this License to any future release, copy, or distribution of the + Software, or may license the Software under any other terms. No revised, + replaced, or modified version changes the terms applicable to a copy of the + Software already received under this version unless the Licensor expressly + states that the revised terms apply and you accept or are otherwise legally + bound by them. This provision does not permit anyone other than the Licensor + to modify this License. diff --git a/LICENSES/README.md b/LICENSES/README.md new file mode 100644 index 0000000..282b050 --- /dev/null +++ b/LICENSES/README.md @@ -0,0 +1,16 @@ +# License Map + +`libvirtualhid` uses separate licenses for the cross-platform library and the +Windows driver package. + +- LB-SAL 1.0 SPDX custom identifier: `LicenseRef-LizardByte-SAL-1.0`. +- Cross-platform library source, public headers, non-driver backends, examples, + tests, build scripts, and documentation unless listed below: + [MIT](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSE). +- Windows UMDF driver source under `src/platform/windows/driver/`: + [LizardByte Source-Available License 1.0](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSES/LicenseRef-LizardByte-SAL-1.0.md). +- Generated Windows driver package artifacts, including the driver MSI: + [LizardByte Source-Available License 1.0](https://github.com/LizardByte/libvirtualhid/blob/master/LICENSES/LicenseRef-LizardByte-SAL-1.0.md). + +The Windows driver MSI may include MIT-licensed helper components from this +repository; packaged installs include both license texts for that reason. diff --git a/README.md b/README.md index 3458b9a..30081ea 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
LizardByte icon

libvirtualhid

@@ -19,746 +19,146 @@ ## โ„น๏ธ About -`libvirtualhid` is a planned cross-platform C++ library for creating virtual HID -input devices for remote streaming hosts and similar low-latency input -applications. - -The primary target is gamepad input. Keyboard and mouse support are secondary -goals once the gamepad model, descriptor handling, and output report plumbing -are stable. - -## ๐ŸŽฏ Goals - -- Provide the same public C++ API on Windows, Linux, and eventually macOS. -- Hide platform-specific virtual HID details behind backend implementations. -- Prefer user-mode platform facilities and avoid custom kernel-mode drivers. -- Build with CMake and support direct consumption through `add_subdirectory`, - `FetchContent`, installed CMake packages, or vendored source. -- Keep Sunshine as the first target consumer and validate the library against - Sunshine's current input lifecycle, controller profiles, and packaging needs. -- Keep network transport out of scope. Consumers such as streaming hosts own - network input collection and feed local reports into this library. -- Use the MIT license. - -## โŒ Non-goals - -- No anti-cheat bypass or stealth device hiding. -- No replication of controller authentication chips or private vendor secrets. -- No Windows kernel-mode driver. -- No built-in network protocol. - -## ๐Ÿ“š Reference Projects - -The initial design is informed by these projects: - -- [cgutman/WinUHid](https://github.com/cgutman/WinUHid): Windows virtual HID - device emulation with a UMDF-oriented driver/package shape. -- [hifihedgehog/HIDMaestro](https://github.com/hifihedgehog/HIDMaestro): - Windows user-mode UMDF2 game controller emulation, profile-driven controller - identity, output callbacks, hot-plug behavior, and no custom kernel driver. -- [games-on-whales/inputtino](https://github.com/games-on-whales/inputtino): - Linux C++ virtual input library built around `uinput`, `evdev`, and `uhid`, - including gamepad, keyboard, mouse, and output-event handling. -- LizardByte C++ project structure references: - [tray](https://github.com/LizardByte/tray) and - [libdisplaydevice](https://github.com/LizardByte/libdisplaydevice), especially - their CMake option shape, top-level-only test/doc setup, `third-party` - submodule layout, and GoogleTest wiring. - -## ๐Ÿ–ฅ๏ธ Platform Strategy - -### Windows - -Windows should use a UMDF2 HID minidriver and a C++ client library/backend. The -driver remains user-mode, but it is still a Windows driver package and must be -installed and trusted on the host machine. - -That means a consuming application can compile the C++ library as part of its -own build, but compiling the library alone is not enough to create virtual HID -devices on Windows. The project should provide: - -- [x] A CMake-built C++ client library for consumers. -- [x] A Windows driver package containing the INF, signed catalog, UMDF driver DLL, - and any helper/control component needed by the backend. -- [x] Install/uninstall helpers suitable for developer machines and application - installers. -- [x] A path for projects to either build the driver package themselves with the - Windows SDK/WDK or redistribute an official prebuilt, signed package. - -The public API should not expose these details. Consumers should create a -runtime, create devices, submit input state, and receive output reports the same -way they do on Linux. - -The Windows C++ client library should support both MSVC and MinGW/UCRT64 where -the code only depends on normal Win32 or C++ APIs. MinGW support matters for -consumers that already build their application with that toolchain. The UMDF2 -driver package is different: it should be treated as a Windows SDK/WDK build -artifact and built with the Microsoft driver toolchain, such as Visual Studio, -MSBuild, or EWDK. The boundary between the library and driver should therefore -be compiler-neutral: prefer a stable C ABI, named pipe, device interface IOCTL, -or similar control channel over passing C++ STL types across that boundary. - -The current Windows backend selects a UMDF control-channel implementation for -`BackendKind::platform_default`. It always exposes keyboard and mouse through -Win32 `SendInput`, then probes the libvirtualhid control device interface for -descriptor-driven virtual gamepads. It falls back to the legacy fixed -`\\.\LibVirtualHid` and `\\.\Global\LibVirtualHid` links for diagnostics and -older driver builds. It reports `requires_installed_driver = true`, and only -advertises gamepad/output-report support when the driver package is installed -and the control device can be opened. Touchscreen, trackpad, and pen tablet -support are not implemented in the Windows backend yet. The client library -stays buildable with MSVC and MinGW/UCRT64 because the gamepad path talks to the -driver through fixed-size C protocol structures and Win32 `DeviceIoControl` -calls. The default control device path can be overridden for diagnostics with -`LIBVIRTUALHID_WINDOWS_CONTROL_DEVICE`. - -The UMDF driver uses Windows Virtual HID Framework (VHF) for OS-visible gamepad -devices. Create requests start a VHF child device from the requested descriptor, -VID/PID, and version; input reports are submitted with `VhfReadReportSubmit`; -and HID output writes are forwarded back through the existing output-report -callback path. DirectInput, SDL/HIDAPI, Windows.Gaming.Input/GameInput, and the -browser Gamepad API should therefore see standard HID gamepads after the driver -is installed. XInput is not a direct target for this HID-only backend because it -does not emulate the Xbox proprietary bus/API. -The client protocol uses complete HID reports; numbered reports carry the report -ID at byte 0 and unnumbered reports omit it. The UMDF driver passes that -complete report buffer to VHF and also sets `HID_XFER_PACKET.reportId` for -numbered reports. Output reports forwarded by VHF are normalized back to the -same complete-report shape before delivery to the C++ backend. VHF exposes -VID/PID/version, explicit -`HID\VID_....&PID_....` hardware IDs, Xbox -`HID\VID_....&PID_....&IG_00` hardware IDs where applicable, and the report -descriptor for the child HID device so Windows and browser consumers can -match the selected profile by HID attributes and report shape. VHF does not -provide a product/manufacturer string callback, so consumers that display the -raw HID product string may still show the Windows VHF product label even when -the VID/PID and descriptor match the selected controller. -The built-in Xbox One and Xbox Series profiles use an XboxGIP-shaped descriptor -and unnumbered 17-byte input reports derived from HIDMaestro's USB Xbox -profiles. The Xbox One profile uses `VID_045E&PID_02EA`, and the Xbox Series -profile uses the physical USB identity `VID_045E&PID_0B12`. Bluetooth Xbox -identities are intentionally not used for the built-in profiles. Windows' -`xinputhid.inf` does not bind `HID\VID_045E&PID_0B12&IG_00` VHF children to -XInput, so the UMDF driver also publishes HIDMaestro's GIP HID `driverPid` -identity `HID\VID_045E&PID_02FF&IG_00` as a driver-matching hardware ID while -keeping the public profile PID at `0B12`. The built-in generic profile uses a -browser-standard generic gamepad -descriptor: 16 one-bit digital buttons including the d-pad, followed by 8-bit -`X`, `Y`, `Rx`, `Ry`, `Z`, and `Rz` values so the sticks occupy the first four -axis slots and the analog triggers follow them. The Switch profile uses -HIDMaestro's Nintendo Switch Pro Controller identity (`VID_057E&PID_2009`, -product name `Pro Controller`) with Report ID `0x30`, a 64-byte input report, a -hat d-pad, four 16-bit stick axes, and digital ZL/ZR trigger-click bits rather -than analog trigger axes. The DualShock 4 profiles use the first-generation -controller identity (`VID_054C&PID_05C4`, version `0100`, product name -`Wireless Controller`, manufacturer `Sony Computer Entertainment`) to match the -ViGEmBus DS4 target and HIDMaestro's DS4 v1 reference. The DualSense profiles -use the standard `Wireless Controller` product name in the public profile and -control protocol. -The Xbox 360 HID profile -keeps the legacy common descriptor with 12 one-bit digital buttons, a hat switch -for the d-pad, and 8-bit `X`, `Y`, `Z`, `Rx`, `Ry`, and `Rz` values. -On Windows, the UMDF/VHF backend rejects the Xbox 360 profile because a real -Xbox 360 controller is an XUSB device rather than a VHF HID gamepad; consumers -that still expose an Xbox 360 option should use their XUSB fallback for that -profile. -The UMDF driver opens a separate VHF source target for each virtual gamepad and -parents that target to the control-file handle that created it, so process exits -or crashes clean up any virtual gamepads that were not explicitly destroyed. -During rapid development reinstalls, the fixed global control symbolic link can -outlive the previous root device briefly; the driver treats that collision as -non-fatal so stale object-manager state does not leave the control device in -Code 31. Normal clients discover the PnP control device interface first, so a -stale fixed link does not block the backend from reaching the current device. - -Build the UMDF package separately with the Microsoft driver toolchain: - -```powershell -cmake -S . -B cmake-build-windows-driver -G "Visual Studio 17 2022" -A x64 ` - -DLIBVIRTUALHID_BUILD_WINDOWS_DRIVER=ON -DLIBVIRTUALHID_ENABLE_PACKAGING=ON ` - -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=ON -cmake --build cmake-build-windows-driver --config Release ` - --target libvirtualhid_windows_catalog gamepad_adapter -cpack -G WIX -C Release --config .\cmake-build-windows-driver\CPackConfig.cmake -``` - -Developer install/uninstall helpers live under `scripts/windows`: - -```powershell -powershell -ExecutionPolicy Bypass -File .\scripts\windows\install-driver.ps1 ` - -InfPath .\cmake-build-windows-driver\src\platform\windows\driver\package\Release\libvirtualhid.inf ` - -LogPath .\cmake-build-windows-driver\install-driver.log -powershell -ExecutionPolicy Bypass -File .\scripts\windows\test-installed-driver.ps1 ` - -GamepadAdapterPath .\cmake-build-windows-driver\examples\Release\gamepad_adapter.exe ` - -GamepadProfile xseries -powershell -ExecutionPolicy Bypass -File .\scripts\windows\test-browser-gamepad.ps1 ` - -GamepadAdapterPath .\cmake-build-windows-driver\examples\Release\gamepad_adapter.exe ` - -GamepadProfile xseries -powershell -ExecutionPolicy Bypass -File .\scripts\windows\uninstall-driver.ps1 ` - -Force -RemoveCertificateSubject "CN=libvirtualhid CI Test Driver Signing" -``` - -The WiX driver installer also installs validation files under the default -install root, `C:\Program Files\libvirtualhid`: - -- `scripts\windows\test-installed-driver.ps1` -- `scripts\windows\test-browser-gamepad.ps1` -- `tools\windows\gamepad_adapter.exe` - -The helper stages the INF with `pnputil`, updates an existing -`ROOT\LIBVIRTUALHID` device when present, and creates that root-enumerated -device when it is missing. It uses SetupAPI/NewDev directly so MSI installs do -not require the WDK tools on the target machine. Existing devices are detected -by matching the `ROOT\LIBVIRTUALHID` hardware ID. The SetupAPI path creates a -root-enumerated instance such as `ROOT\LIBVIRTUALHID\####`. -The install and uninstall helpers also clean up malformed development devices -left by earlier installer revisions, including root instances left in the -failed `HIDClass` package shape. The WiX installer writes the helper transcript -to `C:\ProgramData\libvirtualhid\install-driver.log`. -The test helper fails if the root device is not reported as `Status: Started`, -if `\\.\LibVirtualHid` cannot be opened, or if a held gamepad adapter instance -does not produce a started HID child device such as -`HID\VID_045E&PID_0B12&IG_00` or an Xbox Series-compatible HID child such as -`HID\VID_045E&PID_02FF&IG_00`. That check is also run by the Windows CI legs -for every Windows UMDF/VHF-supported `gamepad_adapter` profile after installing -the Windows Driver Installer artifact. The browser helper is for manual -diagnostics: it launches a normal desktop Edge or Chrome instance at -`https://hardwaretester.com/gamepad`, holds a virtual gamepad, and fails if the -browser Gamepad API does not report a controller matching the selected profile -or does not observe changing button and axis input. For manual browser -validation, run the browser helper with `-KeepBrowserOpen`, or run -`tools\windows\gamepad_adapter.exe xseries --hold-seconds 60`, then open -`https://hardwaretester.com/gamepad` in a normal desktop browser and press one -of the held virtual buttons if the browser needs a gamepad activation event. - -The driver binary is a user-mode UMDF DLL installed through the Windows Driver -Store, not a libvirtualhid `.sys` copied into `C:\Windows\System32\drivers`. -Windows still uses its built-in `WUDFRd.sys` and VHF components under -`System32\drivers`; the libvirtualhid-specific sign that installation completed -is the `ROOT\LIBVIRTUALHID` device and the `\\.\LibVirtualHid` control device. The INF -includes the built-in `WUDFRd` install sections for the root `System` control -device, appends the VHF lower filter, sets `VhfMode=1` for the UMDF VHF source -stack, grants non-admin user-mode clients read/write access to the control -device, and disables UMDF host-process sharing so driver updates do not keep -using an older in-process UMDF module during development. The installer also -writes `VhfMode=1` onto the -root device before starting the driver so root-enumerated development installs -get the same VHF source mode as the INF hardware section. The UMDF control -device is restarted after install or update so same-version development builds -load the current UMDF module; if Windows cannot unload the old host, the -installer reports the reboot requirement. The UMDF control device starts -without opening VHF; each gamepad creation opens its own VHF target from the -creating file handle so target-open failures are reported through the -create-device response instead of making `\\.\LibVirtualHid` unavailable. The -generated INF uses the same UMDF -library version as the WDF headers and stub library selected by CMake. The -package defaults to UMDF 2.15, matching the inbox VHF UMDF source driver while -still exposing the framework APIs used by libvirtualhid. The driver target links -the MSVC runtime statically to avoid requiring VC runtime DLLs in the UMDF host -process. Development driver builds write a lightweight UMDF trace to -`C:\Windows\Temp\libvirtualhid-umdf-driver.log`. - -Windows driver packages require a signed catalog for normal installation. Pull -request builds generate a short-lived self-signed test certificate, sign -`libvirtualhid.cat`, bundle the public `.cer` into the WiX installer, and import -that certificate into the local machine root and trusted-publisher stores during -install. The uninstall helper removes certificates matching -`CN=libvirtualhid CI Test Driver Signing`. Push/release builds must use Azure -Trusted Signing for the catalog and generated MSI, matching Sunshine's Windows -signing model, and must not ship the local PR test certificate. - -### Linux - -Linux should compile directly into the consuming project and use standard kernel -user-space interfaces: - -- `uhid` for descriptor-driven HID gamepads where the raw HID identity and - output reports matter. -- `uinput` for keyboard, mouse, and simpler evdev-style devices. -- `libevdev` for uinput device construction where it reduces direct ioctl - handling and preserves the same public API. -- X11/XTest as a last-resort keyboard and mouse fallback when `uinput` cannot - be opened and an X11 session is available. - -Linux deployment should be documentation and permissions focused: users need -access to `/dev/uinput` and/or `/dev/uhid`, usually through udev rules or group -membership. No out-of-tree kernel module should be required. - -The current Linux MVP uses `uhid` and `uinput` for -`BackendKind::platform_default`. When `/dev/uhid` is readable and writable, the -backend reports gamepad and output-report support. When `/dev/uinput` is -readable and writable, it reports keyboard and mouse support. When a required -node is missing or permission is denied, the same backend remains selectable -but reports the affected capability as unavailable and returns -`backend_unavailable` from that device creation path. - -The Linux backend uses `libevdev` internally to construct uinput keyboard, -mouse, touchscreen, trackpad, and pen tablet devices. Consumers still use the -same platform-neutral C++ API; `libevdev` is a Linux build dependency, not a -public API dependency. UTF-8 keyboard text submission is supported through the -same Unicode compose sequence for both uinput keyboards and the XTest fallback. - -The Linux packaging model needs `/dev/uinput` and `/dev/uhid` access. Install a udev rules file such -as `/etc/udev/rules.d/60-libvirtualhid.rules` with: - -```udev -# Allows libvirtualhid consumers to access /dev/uinput -KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", GROUP="input", MODE="0660", TAG+="uaccess" - -# Allows libvirtualhid consumers to access /dev/uhid -KERNEL=="uhid", GROUP="input", MODE="0660", TAG+="uaccess" -``` - -Consuming applications may also install name-matched rules for their stable -virtual device names when generated `hidraw` or `input` nodes must be -accessible to the session user: - -```udev -KERNEL=="hidraw*", ATTRS{name}=="Your App Controller*", GROUP="input", MODE="0660", TAG+="uaccess" -SUBSYSTEMS=="input", ATTRS{name}=="Your App Controller*", GROUP="input", MODE="0660", TAG+="uaccess" -``` - -For `uhid` gamepad support, install a modules-load entry such as -`/etc/modules-load.d/60-libvirtualhid.conf` containing: - -```text -uhid -``` - -After installing the rules, load `uhid`, reload udev, and trigger the device -nodes: - -```bash -sudo modprobe uhid -sudo udevadm control --reload-rules -sudo udevadm trigger --property-match=DEVNAME=/dev/uinput -sudo udevadm trigger --property-match=DEVNAME=/dev/uhid -``` - -If input still does not work, add the user running the consuming application to -the `input` group, then log out and back in: - -```bash -sudo usermod -aG input $USER -``` - -The Linux UHID smoke test creates a real virtual gamepad and fails when the -current user cannot open `/dev/uhid`. - -The Linux uinput smoke test creates real keyboard and mouse devices and fails -when the current user cannot open `/dev/uinput`. - -The Linux consumer integration tests create real virtual devices and validate -them through in-process consumer libraries. SDL2 must see the UHID gamepad and -observe button/axis input. libinput must see the uinput keyboard and mouse and -observe key, pointer motion, and button events. These tests fail when the Linux -device nodes or consumer development libraries are unavailable. - -The XTest fallback should not be treated as a gamepad backend. It can cover -keyboard and mouse injection on X11, but it does not create virtual HID devices, -does not help on Wayland, and should not replace `uhid`/`uinput` for gamepads. -It is enabled automatically when `LIBVIRTUALHID_ENABLE_XTEST` is `ON` and CMake -finds X11/XTest development files. -commit `8227e8f8` added the XTest input fallback, and commit `f57aee90` removed -`src/platform/linux/input/legacy_input.cpp` when Sunshine moved fully to -inputtino. +`libvirtualhid` provides a platform-neutral C++ API for creating virtual input +devices. The primary target is gamepad input for remote streaming hosts and +other low-latency applications, with keyboard, mouse, touchscreen, trackpad, and +pen tablet support available where the platform backend supports those device +types. -### macOS +Consumers work with portable concepts such as runtimes, device profiles, +normalized gamepad state, output callbacks, and device nodes. Platform-specific +details such as Linux `uhid`/`uinput` or the Windows UMDF/VHF driver package stay +behind backend implementations. -macOS is a later target. The first planning milestone is to validate whether the -backend should use `IOHIDUserDevice`, DriverKit/HIDDriverKit, or a combination -of both, then document the entitlement, signing, and distribution requirements. -The public API should already be shaped so the macOS backend can plug in without -breaking Windows or Linux consumers. +## ๐ŸŽฎ Capabilities -## ๐Ÿ› ๏ธ CMake Consumption +- Gamepad profiles for generic HID, Xbox 360, Xbox One, Xbox Series, + DualShock 4, DualSense, and Nintendo Switch Pro-style controllers. +- Descriptor-driven Linux gamepads through `uhid`, plus keyboard, mouse, + touchscreen, trackpad, and pen tablet devices through `uinput`. +- Windows gamepads through a user-mode UMDF2 control driver backed by Virtual + HID Framework, with keyboard and mouse support through normal Win32 APIs. +- Output callbacks for profile-specific feedback such as rumble, LEDs, + adaptive triggers, and raw HID output reports when available. +- CMake consumption through installed packages, vendored source, + `add_subdirectory`, or `FetchContent`. -All consumption modes expose the same CMake target: -`libvirtualhid::libvirtualhid`. - -For an installed package, install the project into a prefix and point consumer -configures at that prefix: - -```bash -cmake --install cmake-build-release --prefix /opt/libvirtualhid -cmake -S your-app -B cmake-build-your-app -DCMAKE_PREFIX_PATH=/opt/libvirtualhid -``` - -Then link the exported config package from the consuming project: - -```cmake -find_package(libvirtualhid CONFIG REQUIRED) -target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) -``` - -For a vendored checkout, add the project directly and link the same target: - -```cmake -add_subdirectory(third-party/libvirtualhid) -target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) -``` - -For `FetchContent`, pin a tag or commit and make the project available: - -```cmake -include(FetchContent) - -FetchContent_Declare( - libvirtualhid - GIT_REPOSITORY https://github.com/LizardByte/libvirtualhid.git - GIT_TAG -) -FetchContent_MakeAvailable(libvirtualhid) - -target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) -``` - -Tests, examples, docs, and the Windows driver package are top-level or opt-in -builds, so normal vendored and `FetchContent` consumers only get the library -target unless they explicitly enable additional options. Linux consumers still -need the development packages used by the backend, such as `libevdev` and -`pkg-config`. - -Install rules follow the same top-level default. `LIBVIRTUALHID_INSTALL` -defaults to `ON` when configuring libvirtualhid directly and `OFF` when it is -added by another project. Consumers that want vendored install rules can opt in -with `-DLIBVIRTUALHID_INSTALL=ON`; direct builds can skip install/export rules -with `-DLIBVIRTUALHID_INSTALL=OFF`. - -## ๐Ÿ“– Proposed Public API Shape - -The exact names may change during implementation, but the API should center on -portable concepts instead of platform concepts: +## ๐Ÿš€ Quick Start ```cpp #include auto runtime = lvh::Runtime::create(); - -auto created = runtime->create_gamepad(lvh::profiles::xbox_360()); +auto created = runtime->create_gamepad(lvh::profiles::dualsense()); if (!created) { return; } auto &gamepad = *created.gamepad; gamepad.set_output_callback([](const lvh::GamepadOutput &output) { - // Route rumble, LED, or trigger feedback back to the physical controller. + // Forward rumble, LEDs, adaptive triggers, or raw reports to the client. }); lvh::GamepadState state; state.buttons.set(lvh::GamepadButton::a, true); -state.left_stick = {0.25f, -0.5f}; -state.right_trigger = 1.0f; +state.left_stick = {0.25F, -0.5F}; +state.right_trigger = 1.0F; gamepad.submit(state); ``` -Expected core types: - -- `Runtime`: owns platform backend discovery, initialization, and shutdown. -- `VirtualDevice`: common lifecycle for create, destroy, and hot-plug. -- `Gamepad`: gamepad-specific state submission and output callbacks. -- `Keyboard`: key press/release and UTF-8 text submission. -- `Mouse`: relative motion, absolute motion, button, vertical scroll, and - horizontal scroll submission. -- `Touchscreen`: direct multi-touch contacts for touch displays. -- `Trackpad`: indirect multi-touch contacts and click state for touchpads. -- `PenTablet`: tablet tool, pressure, distance, tilt, and pen button state. -- `DeviceProfile`: VID/PID, product strings, bus type, HID descriptor, report - layout, and platform capability metadata. -- `DeviceNode`: platform-reported device nodes and sysfs paths for consumers - that must hand created devices to SDL, libinput, HIDAPI, or diagnostics. -- `GamepadState`: normalized buttons, axes, triggers, hats, motion sensors, and - optional touchpad data. -- `GamepadOutput`: normalized rumble, haptics, LEDs, adaptive triggers, and raw - output reports when a profile needs them. -- `BackendCapabilities`: runtime capability query for platform/backend limits, - such as `supports_virtual_hid`, `supports_output_reports`, - `supports_keyboard`, `supports_mouse`, `supports_xtest_fallback`, and - `requires_installed_driver`. - -## ๐Ÿ“ก Streaming Host Integration Requirements - -Streaming hosts are the first consumer class to design against. The initial -implementation should cover the behavior Sunshine needs first, while keeping -the requirements expressed in terms that apply to other consumers: - -- [x] CMake consumption must work as a vendored dependency under a consuming - project's `third-party` tree. -- [x] The API must support multiple client-relative and global gamepad indexes so - streaming hosts can preserve stable controller lifecycles across arrival, - update, feedback, and removal events. -- [x] Built-in profiles should cover common streaming controller choices: - automatic selection, Xbox One-style, DualShock 4-style, DualSense-style, and - Switch Pro-style devices. Xbox 360 can remain useful as a compatibility - profile and test target. -- [x] Controller metadata must be rich enough for streaming-host selection rules: - client controller type, motion sensor capability, touchpad capability, RGB LED - support, battery state, and per-controller identity data. -- [x] Output callbacks must carry rumble first, then RGB LED, adaptive trigger, - and raw output report data where the selected profile supports it. -- [x] Keyboard and mouse APIs should map cleanly to common relative mouse, - absolute mouse, buttons, scroll, horizontal scroll, keyboard scancode, and - Unicode paths. -- [x] Linux keyboard support must include configurable auto-repeat for held keys - so streaming hosts can preserve input behavior previously covered by - inputtino. -- [x] Linux devices must expose created device nodes and relevant sysfs paths - for consumers and diagnostics that need to inspect or pass those paths onward. -- [x] Linux fallback behavior should match streaming-host operational - expectations: - prefer real virtual devices through `uhid`/`uinput`; only use XTest for - keyboard/mouse when virtual device creation fails and X11 is available. -- [x] Linux gamepad support must reach inputtino parity before replacement: - real DualSense UHID descriptors, GET_REPORT replies, periodic input reports, - touchpad, motion, battery, RGB LED, adaptive trigger callbacks, CRC handling, - and equivalent output-report feedback behavior for the UHID gamepad path. -- [x] Linux pointer support must cover touchscreen, trackpad, and pen tablet - virtual devices with libinput-observable behavior. -- [x] The library must not own a consumer's network protocol, client packet - parsing, configuration system, or feedback queue. It should expose the device - primitives consumers need to keep that ownership in their applications. - -## ๐Ÿ› ๏ธ Tooling and Dependency Plan - -- [x] Use CMake as the only build system for the core library. -- [x] Follow the LizardByte `tray` and `libdisplaydevice` pattern: top-level-only - `BUILD_TESTS` and `BUILD_DOCS` options, reusable library targets, and tests - that do not force themselves on parent projects. -- [x] Put all submodules under `third-party`. -- [x] Add GoogleTest as a submodule at `third-party/googletest`; do not download it - during configure. -- [x] Add the LizardByte Doxygen configuration as a submodule at - `third-party/doxyconfig` and use it for local docs and Read the Docs builds. -- [x] Expose `libvirtualhid::libvirtualhid` as the main CMake target. -- [x] Keep the public headers under `src/include/libvirtualhid` and the implementation - split into shared core code plus platform-specific backends. -- [x] Add Windows CI coverage for the client library with MSVC and MinGW/UCRT64. -- [x] Add Linux CI coverage for GCC and Clang, with integration tests requiring - `/dev/uinput`, `/dev/uhid`, SDL2, libinput, and X11/XTest where applicable. -- [ ] Add separate WDK/MSVC validation for the driver package once driver sources - exist. - -## ๐Ÿ“’ Repository Plan - -The intended project layout is: - -```text -src/include/libvirtualhid/ Public C++ headers -src/core/ Shared profile, descriptor, and report logic -src/platform/windows/ Windows client backend and UMDF control channel -src/platform/windows/driver/ Windows UMDF2 driver package sources -src/platform/linux/ Linux uhid/uinput backend -src/platform/macos/ Future macOS backend -profiles/ Built-in gamepad profiles -examples/ Minimal consumers and platform smoke tests -tests/ Unit and integration tests -cmake/ Package config and helper modules -docs/ Project Doxygen configuration -third-party/doxyconfig/ LizardByte Doxygen configuration submodule -third-party/googletest/ GoogleTest submodule -``` - -## ๐Ÿ“‹ Implementation Plan - -### Phase 1: Project Foundation - -- [x] Add CMake project scaffolding and exported target - `libvirtualhid::libvirtualhid`. -- [x] Define the public C++ API, error model, device lifecycle, and ownership rules. -- [x] Add a fake in-memory backend so API tests can run on every platform. -- [x] Add GoogleTest as a submodule under `third-party/googletest` and wire tests - using the same top-level-only pattern as `tray` and `libdisplaydevice`. -- [x] Add Doxygen documentation wiring with `third-party/doxyconfig`, a project - `docs/Doxyfile`, and Read the Docs configuration. -- [x] Add CI using the `libdisplaydevice` workflow pattern for Linux GCC, Linux - Clang, macOS, Windows MinGW/UCRT64, and Windows MSVC configure/build/test - coverage. -- [x] Add descriptor/profile models for at least Xbox 360, Xbox Series, - DualShock 4, DualSense, and a generic HID gamepad. -- [x] Add unit tests for state normalization and HID report packing. -- [x] Add a streaming-host-oriented example or adapter test that exercises - controller arrival, state updates, output feedback, and removal without - depending on consumer internals. - -### Phase 2: Linux MVP - -- [x] Implement gamepad creation over `uhid` for descriptor-driven controllers. -- [x] Add `uinput` support for keyboard and mouse once the gamepad path is stable. -- [x] Support output report callbacks for rumble and profile-specific feedback. -- [x] Add X11/XTest fallback support for keyboard and mouse only. -- [x] Add examples and integration tests that validate virtual device visibility - through SDL2 for generic gamepad input, DualShock 4 USB input, DualShock 4 - Bluetooth controller discovery, DualSense USB input, and DualSense Bluetooth - controller discovery, plus libinput for keyboard/mouse. -- [x] Document required Linux permissions and sample udev rules. - -### Phase 2B: Linux inputtino Parity - -- [x] Replace the generic DualSense USB profile behavior with a descriptor-driven - DualSense report descriptor and 64-byte input report packing. -- [x] Add Bluetooth DualSense descriptor parity, CRC handling, and Bluetooth input - report framing. -- [x] Add DualSense UHID GET_REPORT replies for calibration, pairing, and firmware - reports, including MAC/uniq identity handling. -- [x] Add periodic DualSense input reports for consumers that expect steady sensor - and touchpad updates. -- [x] Add DualSense input state for motion sensors, touchpad contacts, battery - state, and profile-specific buttons without leaking Linux-specific details - into consumers. -- [x] Parse DualSense output reports into rumble, RGB LED, adaptive trigger, and - raw-report callbacks. -- [x] Add DualShock 4 USB and Bluetooth profiles with descriptor-driven input - reports, touchpad click, touch contacts, motion sensors, battery state, - lightbar feedback, rumble callbacks, Bluetooth CRC handling, GET_REPORT - replies, stable MAC identity, periodic reports, and generated sensor - timestamps. -- [x] Expose created device nodes and sysfs paths through the platform-neutral - public API. -- [x] Add configurable keyboard auto-repeat for held keys. -- [x] Add touchscreen, trackpad, and pen tablet public device types and Linux - direct-uinput backend implementations. -- [x] Keep gamepad feedback on UHID output reports. There is no uinput-backed - gamepad path in this library; if one is added later, it must implement Linux - force-feedback upload, erase, playback, and gain handling. -- [x] Expand Linux consumer tests so SDL2 validates generic joystick input, - DualShock 4 USB controller input, DualShock 4 Bluetooth controller discovery, - DualSense USB controller input, and DualSense Bluetooth controller discovery, - and libinput validates keyboard, mouse, touchscreen, trackpad, and pen tablet - events. - -### Phase 2C: Linux uinput Hardening - -- [x] Prefer libevdev for uinput device construction where it removes fragile - direct ioctl setup, while keeping the public API unchanged. - -### Phase 3: Windows MVP - -- [x] Add CMake/WDK integration for the UMDF2 driver package. -- [x] Implement the Windows backend and control channel between the C++ library and - the UMDF driver. -- [x] Keep the client library buildable with MSVC and MinGW/UCRT64. Keep the driver - package on the Microsoft WDK toolchain. -- [x] Add install/uninstall tooling for developer workflows. -- [x] Support backend hot-plug, multi-controller instances, and output report callbacks - through the Windows control protocol. -- [x] Publish Windows gamepads through VHF so DirectInput, SDL/HIDAPI, - Windows.Gaming.Input/GameInput, and browser Gamepad API can enumerate standard - HID gamepads. XInput is not applicable to the HID-only backend without a - consumer-side mapping layer. - -### Phase 4: API Parity and Packaging - -- [x] Keep one API surface across Windows and Linux, with capability queries for - platform limitations instead of platform-specific methods. -- [x] Add installed CMake package support and `FetchContent` documentation. -- [x] Add CI for formatting, static analysis, CMake configure/build, unit tests, and - platform smoke tests. -- [x] Defer C, Python, and Rust bindings until after the platform API is stable, - likely after macOS support lands. -- [x] Decide whether official Windows releases should ship signed driver packages - in addition to source. - -### Phase 5: Sunshine Replacement Readiness - -The Windows driver package is part of the intended design, similar in role to -ViGEmBus as an installable user-mode virtual device component. The remaining -replacement blockers are compatibility, packaging integration, and feature -parity with Sunshine's current ViGEmBus and inputtino behavior, while keeping -one public API across supported operating systems. The API may expose a richer -cross-platform model than any one backend can implement, but backends must report -unsupported features through capabilities instead of forcing consumers onto -platform-specific calls. - -#### Phase 5A: Shared Sunshine Adapter - -- [x] Add a Sunshine-oriented adapter example or test that maps controller - arrival, state updates, touchpad contacts, motion samples, battery reports, - feedback callbacks, and removal through the existing platform-neutral - `Runtime` and `Gamepad` APIs. -- [x] Preserve Sunshine's asynchronous event shape by caching per-controller - `GamepadState` and resubmitting after separate button, axis, trigger, touch, - motion, and battery updates. Adapter creation submits one neutral input report - immediately so operating-system consumers can enumerate the virtual controller - before the first client input event arrives. -- [x] Expand or formally map the public button model so Sunshine's full - controller flag set is preserved, including guide/home, profile-specific - misc/share, and rear paddles where the emulated profile can expose them. -- [x] Add profile capability checks for rumble, trigger rumble, RGB LED, - adaptive triggers, motion, touchpad, battery, and profile-specific buttons so - Sunshine can keep one code path while warning only when a selected profile - cannot expose a client-reported feature. - -#### Phase 5B: Windows ViGEmBus Parity - -- [ ] Validate the UMDF/VHF backend as Sunshine's Windows gamepad replacement - against the consumers that currently work through ViGEmBus, including SDL, - HIDAPI, browser Gamepad API, DirectInput, GameInput, and games or clients that - rely on the current Xbox controller behavior. -- [ ] Decide and document the Windows Xbox compatibility story before replacing - ViGEmBus. If the HID backend is not accepted by XInput-only consumers, keep a - compatibility layer, consumer-side mapping, or a retained ViGEmBus path for - that class of application. -- [x] Add a DualShock 4 compatible profile for Sunshine's current DS4 mode, - including touchpad click, touch contacts, motion sensors, battery state, - lightbar feedback, rumble feedback, Bluetooth CRC handling, and timestamp - behavior. -- [ ] Validate the DualShock 4 profile through Sunshine's Windows path against - the same applications currently covered by ViGEmBus DS4 mode. -- [ ] Replace Sunshine's ViGEmBus installer, status API, and diagnostics with - equivalent libvirtualhid driver package checks, install/uninstall flows, and - signed release packaging. -- [ ] Run Windows lifecycle and multi-controller validation through Sunshine with - the installed driver package, including hot-plug, output-report callbacks, - process shutdown cleanup, and simultaneous controllers. - -#### Phase 5C: Linux and FreeBSD inputtino Parity - -- [ ] Implement a Sunshine Linux adapter that preserves the current `xone`, - `ds5`, `switch`, and `auto` selection behavior while using the same - platform-neutral libvirtualhid API as Windows. -- [ ] Validate Linux DualSense parity against inputtino's UHID path: USB and - Bluetooth reports, calibration/pairing/firmware feature replies, periodic - input reports, touchpad, motion, battery, rumble, RGB LED, and adaptive trigger - feedback. -- [ ] Validate Linux DualShock 4 parity against Sunshine's former Windows DS4 - behavior and Linux consumers: USB and Bluetooth reports, - calibration/pairing/firmware feature replies, periodic input reports, sensor - timestamps, touchpad, motion, battery, rumble, and RGB LED feedback. -- [ ] Validate Xbox One and Switch Pro behavior through SDL and evdev consumers, - including d-pad, sticks, triggers, guide/misc buttons, rumble, device names, - VID/PID/version identity, and stable device-node discovery. -- [ ] Add a FreeBSD backend plan instead of assuming the Linux backend applies - unchanged. Sunshine disables inputtino `USE_UHID` on FreeBSD, so the current - FreeBSD path uses the uinput/libevdev-style fallback and does not get the - UHID-only DualSense features. -- [ ] Keep the FreeBSD API surface identical to Linux and Windows, but report - FreeBSD's real backend capabilities separately. At minimum, validate basic - Xbox One, Switch Pro, and uinput-backed PS5-style gamepad creation; only mark - DualSense touch, motion, battery, RGB LED, adaptive triggers, and Bluetooth - identity as supported if a FreeBSD backend can actually deliver them. -- [ ] Add FreeBSD configure/build coverage and a smoke-test strategy for the - supported subset, with explicit documentation for required device nodes, - permissions, and any FreeBSD-specific package dependencies. - -### Phase 6: macOS Research and Backend - -- [ ] Prototype macOS virtual HID creation and report submission. -- [ ] Document signing, entitlement, and installer constraints. -- [ ] Add macOS backend behind the existing public API. -- [ ] Add macOS discovery and smoke-test coverage. - -## ๐Ÿงช Testing Plan - -- [ ] Unit test descriptor generation, report packing, axis scaling, button mapping, - and output report parsing. -- [ ] Run lifecycle tests for create, submit, output callback, destroy, repeated - hot-plug, and process shutdown cleanup. -- [ ] Validate multi-controller behavior and stable ordering. -- [ ] Test against real consumers where practical: Sunshine, SDL, HIDAPI, browser - Gamepad API, DirectInput/XInput/GameInput on Windows, and evdev/libinput - libraries on Linux. +More complete examples live in `examples/`, including the streaming-host-oriented +`examples/gamepad_adapter.cpp`. + +## ๐Ÿ“š Documentation + +- [Usage and API](docs/usage.md): CMake consumption, build options, public API + overview, profiles, and examples. +- [Platform support](docs/platform-support.md): backend capability model, + Windows, Linux, macOS, and Linux permission setup. +- [Windows driver package](docs/windows-driver.md): UMDF/VHF package build, + installation, validation, diagnostics, and signing notes. +- [Streaming-host integration](docs/streaming-host-integration.md): integration + contract and remaining replacement-readiness work for streaming hosts. +- [Development](docs/development.md): local build/test commands, repository + layout, docs generation, and roadmap. +- [Microsoft Store validation](docs/store-review-validation.md): review notes + and manual validation for Store submissions. + +## ๐ŸŽฏ Scope + +`libvirtualhid` owns local virtual device creation and input/output report +translation. It does not provide a network protocol, parse client packets, own a +consumer application's configuration system, bypass anti-cheat systems, hide +devices from the OS, or ship a Windows kernel-mode driver. + +## ๐Ÿ“Œ Status + +Linux and Windows are the active backends. Linux uses standard user-space kernel +interfaces. Windows remains user-mode: the C++ library talks to a UMDF2 control +driver, and the driver publishes HID gamepads through VHF. macOS support is not +implemented yet. + +The library is designed around gamepad use first because remote streaming hosts +are the first consumer class. Non-gamepad device types are available through the +same API where the backend exposes them. + +## ๐Ÿ” Alternatives + +Alternatives exist if `libvirtualhid` does not meet your needs. + +| Feature | `libvirtualhid` | [ViGEmBus](https://github.com/nefarius/ViGEmBus) | [HIDMaestro](https://github.com/hifihedgehog/HIDMaestro) | [inputtino](https://github.com/games-on-whales/inputtino) | [WinUHid](https://github.com/cgutman/WinUHid) | +|-----------------------------------|--------------------------------------------------|--------------------------------------------------|----------------------------------------------------------|-----------------------------------------------------------|--------------------------------------------------| +| Windows support | โœ… | โœ… | โœ… | โŒ | โœ… | +| Linux support | โœ… | โŒ | โŒ | โœ… | โŒ | +| Windows AMD64 support | โœ… | โœ… | โœ… | - | โœ… | +| Windows ARM64 support | โŒ5 | โœ… | โŒ6 | - | โœ… | +| Windows user-mode driver | โœ… | โŒ | โœ… | - | โœ… | +| No Windows kernel-mode driver | โœ… | โŒ | โœ… | - | โœ… | +| Descriptor-defined HID devices | โœ… | โŒ | โœ… | โœ…1 | โœ… | +| Platform-neutral C++ API | โœ… | โŒ2 | โŒ2 | โŒ3 | โŒ2 | +| Keyboard | โœ… | โŒ | โŒ | โœ… | โœ…4 | +| Mouse | โœ… | โŒ | โŒ | โœ… | โœ…4 | +| Touchscreen, trackpad, or pen | โœ… | โŒ | โŒ | โœ… | โœ…4 | +| Generic HID gamepad | โœ… | โŒ | โœ… | โŒ | โœ…4 | +| Xbox 360 gamepad | โœ… | โœ… | โœ… | โŒ | โœ…4 | +| Xbox One gamepad | โœ… | โŒ | โœ… | โœ… | โœ…4 | +| Xbox Series gamepad | โœ… | โŒ | โœ… | โŒ | โœ…4 | +| DualShock 4 gamepad | โœ… | โœ… | โœ… | โŒ | โœ…4 | +| DualSense gamepad | โœ… | โŒ | โœ… | โœ… | โœ…4 | +| Nintendo Switch Pro-style gamepad | โœ… | โŒ | โœ… | โœ… | โœ…4 | +| Rumble or output callbacks | โœ… | โŒ | โœ… | โœ… | โœ…4 | +| Data-driven profiles | โŒ | โŒ | โœ… | โŒ | โŒ | +| Actively developed | โœ… | โŒ | โœ… | โœ… | โœ… | + +1 inputtino uses `uhid` for +virtual joypads; its other listed device types use Linux input interfaces rather +than generic HID descriptors. + +2 Windows-only project, so it does +not provide a platform-neutral API surface. + +3 Linux-only project, so it does not +provide a platform-neutral API surface. + +4 WinUHid is a framework-level +virtual HID target; support requires a custom HID descriptor and matching report +handling rather than a ready-made device profile. + +5 libvirtualhid has +architecture-aware WiX packaging, but the built Windows +driver package target is AMD64 today. Windows ARM64 release driver packages +require a Microsoft dashboard signing path, such as WHQL/Hardware Dev Center +signing; the current Azure Trusted Signing catalog/MSI flow is not sufficient +for ARM64 release driver package installation. + +6 HIDMaestro documents a `win-x64` +test app path and does not currently advertise an ARM64 build. ## ๐Ÿ“„ License -`libvirtualhid` is licensed under the MIT License. See [LICENSE](LICENSE). +The cross-platform `libvirtualhid` library is licensed under the MIT License. +The Windows UMDF driver source and generated Windows +driver package artifacts, including the driver MSI, are licensed under the +LizardByte Source-Available License 1.0 (LB-SAL 1.0). See the +[license map](LICENSES/README.md) for the full repository split. diff --git a/cmake/packaging/windows.cmake b/cmake/packaging/windows.cmake index d828f31..5e539dd 100644 --- a/cmake/packaging/windows.cmake +++ b/cmake/packaging/windows.cmake @@ -12,6 +12,8 @@ set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};${CMAKE_PROJECT_NAME};driv set(LIBVIRTUALHID_DRIVER_TEST_CERTIFICATE "" CACHE FILEPATH "Optional public test certificate to include in the Windows driver installer.") +set(LIBVIRTUALHID_DRIVER_LICENSE_FILE + "${PROJECT_SOURCE_DIR}/LICENSES/LicenseRef-LizardByte-SAL-1.0.md") install(FILES "${PROJECT_SOURCE_DIR}/scripts/windows/libvirtualhid-driver-common.ps1" @@ -40,6 +42,12 @@ if(LIBVIRTUALHID_DRIVER_TEST_CERTIFICATE) OPTIONAL) endif() +install(FILES + "${PROJECT_SOURCE_DIR}/LICENSE" + "${LIBVIRTUALHID_DRIVER_LICENSE_FILE}" + DESTINATION "licenses" + COMPONENT driver) + set(CPACK_COMPONENT_DRIVER_DISPLAY_NAME "Windows UMDF Driver") set(CPACK_COMPONENT_DRIVER_DESCRIPTION "libvirtualhid Windows UMDF virtual HID driver package.") set(CPACK_COMPONENT_DRIVER_REQUIRED true) diff --git a/cmake/packaging/windows_wix.cmake b/cmake/packaging/windows_wix.cmake index c604ef5..ef0c7c8 100644 --- a/cmake/packaging/windows_wix.cmake +++ b/cmake/packaging/windows_wix.cmake @@ -67,10 +67,13 @@ set(CPACK_WIX_EXTENSIONS set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/wix_resources/libvirtualhid-driver-installer-patch.xml") -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") +set(LIBVIRTUALHID_DRIVER_CPACK_LICENSE_FILE + "${CMAKE_BINARY_DIR}/LicenseRef-LizardByte-SAL-1.0.txt") +configure_file( + "${LIBVIRTUALHID_DRIVER_LICENSE_FILE}" + "${LIBVIRTUALHID_DRIVER_CPACK_LICENSE_FILE}" + COPYONLY) +set(CPACK_RESOURCE_FILE_LICENSE "${LIBVIRTUALHID_DRIVER_CPACK_LICENSE_FILE}") if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") set(CPACK_WIX_ARCHITECTURE "arm64") diff --git a/docs/Doxyfile b/docs/Doxyfile index 09e4acb..e882cef 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -25,6 +25,8 @@ DOCSET_BUNDLE_ID = dev.lizardbyte.libvirtualhid DOCSET_PUBLISHER_ID = dev.lizardbyte.libvirtualhid.documentation PROJECT_BRIEF = "Cross-platform C++ library for virtual HID devices." +PROJECT_ICON = ../libvirtualhid.svg +PROJECT_LOGO = ../libvirtualhid.svg PROJECT_NAME = libvirtualhid # project specific settings @@ -35,5 +37,13 @@ WARN_IF_UNDOCUMENTED = YES # files and directories to process USE_MDFILE_AS_MAINPAGE = ../README.md INPUT = ../README.md \ + usage.md \ + platform-support.md \ + windows-driver.md \ + streaming-host-integration.md \ + development.md \ + store-review-validation.md \ + ../LICENSES/README.md \ + ../examples \ ../third-party/doxyconfig/docs/source_code.md \ ../src/include diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..4fb3a65 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,83 @@ +# Development + +This page covers project layout, local build commands, documentation generation, +and the remaining roadmap. + +## Repository Layout + +```text +src/include/libvirtualhid/ Public C++ headers +src/core/ Shared profile, descriptor, and report logic +src/platform/windows/ Windows client backend and UMDF control channel +src/platform/windows/driver/ Windows UMDF2 driver package sources +src/platform/linux/ Linux uhid/uinput backend +src/platform/macos/ Future macOS backend +examples/ Minimal consumers and platform smoke tests +tests/ Unit and integration tests +cmake/ Package config and helper modules +docs/ Project documentation and Doxygen settings +third-party/doxyconfig/ LizardByte Doxygen configuration submodule +third-party/googletest/ GoogleTest submodule +``` + +## Windows Library Build + +On Windows, the normal library and tests are built with MSYS2/UCRT64 for MinGW +coverage. Build directories should use the `cmake-build-` prefix. + +```bash +cmake -S . -B cmake-build-debug-mingw-ucrt64-ninja -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug +cmake --build cmake-build-debug-mingw-ucrt64-ninja +cmake-build-debug-mingw-ucrt64-ninja/tests/test_libvirtualhid.exe +``` + +The Windows UMDF driver package is a separate MSVC/WDK build. See +[Windows driver package](windows-driver.md). + +## Linux Build + +Linux builds use the same CMake target shape. Integration tests that create real +devices require access to `/dev/uhid` and `/dev/uinput`; see +[Platform support](platform-support.md). + +```bash +cmake -S . -B cmake-build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug +cmake --build cmake-build-debug +cmake-build-debug/tests/test_libvirtualhid +``` + +## Documentation + +Documentation is generated by the LizardByte `doxyconfig` submodule. The project +Doxygen input is defined in `docs/Doxyfile`. The docs target combines that +file with the shared `doxyconfig` settings at build time. + +```bash +cmake --build cmake-build-debug-mingw-ucrt64-ninja --target docs +``` + +Keep Markdown pages focused on decisions or workflows that consumers and +maintainers need. Avoid preserving old implementation-plan checklists once the +code and tests provide a better source of truth. + +## Validation Targets + +- `test_libvirtualhid`: shared GoogleTest binary under the build directory's + `tests` directory. +- `gamepad_adapter`: example executable for profile and lifecycle diagnostics. +- Windows driver helper scripts under `scripts/windows`. +- Linux consumer tests through SDL2, libinput, `uhid`, and `uinput` where the + host environment supports real virtual devices. +- Doxygen docs target for README, Markdown pages, and public header rendering. + +## Roadmap + +- Finish Windows streaming-host replacement validation, especially application + compatibility for the UMDF/VHF HID backend and any XInput-only fallback story. +- Replace consumer-specific ViGEmBus installer/status/diagnostic flows with + libvirtualhid driver-package checks. +- Validate Linux host-adapter behavior across the selected controller profiles. +- Define and implement the FreeBSD-supported backend subset. +- Prototype and document macOS virtual HID support, including signing, + entitlement, and installer constraints. diff --git a/docs/images/screenshots/windows-driver-game-controllers-and-properties.png b/docs/images/screenshots/windows-driver-game-controllers-and-properties.png new file mode 100644 index 0000000..1b24313 Binary files /dev/null and b/docs/images/screenshots/windows-driver-game-controllers-and-properties.png differ diff --git a/docs/images/screenshots/windows-driver-game-controllers.png b/docs/images/screenshots/windows-driver-game-controllers.png new file mode 100644 index 0000000..5cc45f6 Binary files /dev/null and b/docs/images/screenshots/windows-driver-game-controllers.png differ diff --git a/docs/images/screenshots/windows-driver-properties.png b/docs/images/screenshots/windows-driver-properties.png new file mode 100644 index 0000000..665b72b Binary files /dev/null and b/docs/images/screenshots/windows-driver-properties.png differ diff --git a/docs/platform-support.md b/docs/platform-support.md new file mode 100644 index 0000000..ca8e7d4 --- /dev/null +++ b/docs/platform-support.md @@ -0,0 +1,110 @@ +# Platform Support + +`libvirtualhid` keeps the public C++ API platform-neutral. Consumers ask the +runtime for capabilities, create devices from profiles, submit normalized state, +and receive output callbacks. Backend-specific virtual HID details stay inside +the platform implementation. + +## Capability Model + +Backends report what is available at runtime. A backend can be selectable while +still reporting that a specific device type is unavailable because permissions, +kernel modules, driver installation, or platform features are missing. Device +creation then returns an operation status instead of forcing consumers onto +platform-specific probing code. + +Use capability queries for behavior such as: + +- Whether the backend can create virtual HID devices. +- Whether gamepad output reports are supported. +- Whether keyboard, mouse, touchscreen, trackpad, or pen tablet creation is + available. +- Whether the Linux XTest fallback is active. +- Whether a Windows driver package must be installed. + +## Windows + +The Windows backend keeps the normal C++ library buildable with MSVC and +MinGW/UCRT64. Keyboard and mouse input use Win32 APIs. Gamepad creation uses a +user-mode UMDF2 control driver and Windows Virtual HID Framework. + +The C++ library communicates with the driver through fixed-size protocol +structures and `DeviceIoControl`, not C++ STL types. This keeps the public API +compiler-neutral and preserves the boundary between the MinGW/MSVC client +library and the WDK/MSVC driver package. + +When the driver is installed, the backend publishes HID gamepads that standard +HID consumers can enumerate, including SDL/HIDAPI, DirectInput, +Windows.Gaming.Input/GameInput, and browser Gamepad API clients. XInput is not a +direct target of the HID backend. + +See [Windows driver package](windows-driver.md) for build, install, validation, +and signing details. + +## Linux + +The Linux backend uses standard user-space kernel interfaces: + +- `uhid` for descriptor-driven HID gamepads. +- `uinput` for keyboard, mouse, touchscreen, trackpad, and pen tablet devices. +- `libevdev` internally for uinput device construction. +- X11/XTest only as a keyboard and mouse fallback when `uinput` cannot be used + and an X11 session is available. + +Gamepad support prefers `uhid` because descriptors, raw HID identity, feature +reports, and output reports matter for controller compatibility. Keyboard and +pointer devices prefer `uinput` because those devices map naturally to Linux +input devices. + +### Permissions + +Linux deployment is usually a permissions problem, not a kernel-module problem. +Install udev rules such as `/etc/udev/rules.d/60-libvirtualhid.rules`: + +```udev +# Allows libvirtualhid consumers to access /dev/uinput +KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", GROUP="input", MODE="0660", TAG+="uaccess" + +# Allows libvirtualhid consumers to access /dev/uhid +KERNEL=="uhid", GROUP="input", MODE="0660", TAG+="uaccess" +``` + +Consuming applications may also install name-matched rules for stable virtual +device names when generated `hidraw` or `input` nodes must be accessible to the +session user: + +```udev +KERNEL=="hidraw*", ATTRS{name}=="Your App Controller*", GROUP="input", MODE="0660", TAG+="uaccess" +SUBSYSTEMS=="input", ATTRS{name}=="Your App Controller*", GROUP="input", MODE="0660", TAG+="uaccess" +``` + +For `uhid` gamepad support, install a modules-load entry such as +`/etc/modules-load.d/60-libvirtualhid.conf`: + +```text +uhid +``` + +After installing the rules, load `uhid`, reload udev, and trigger the device +nodes: + +```bash +sudo modprobe uhid +sudo udevadm control --reload-rules +sudo udevadm trigger --property-match=DEVNAME=/dev/uinput +sudo udevadm trigger --property-match=DEVNAME=/dev/uhid +``` + +If input still does not work, add the user running the consuming application to +the `input` group, then log out and back in: + +```bash +sudo usermod -aG input $USER +``` + +## macOS + +macOS support is not implemented yet. The expected backend direction is +`IOHIDUserDevice`, DriverKit/HIDDriverKit, or a combination that preserves the +same public API while documenting any signing, entitlement, and installer +requirements. diff --git a/docs/streaming-host-integration.md b/docs/streaming-host-integration.md new file mode 100644 index 0000000..6359c4a --- /dev/null +++ b/docs/streaming-host-integration.md @@ -0,0 +1,72 @@ +# Streaming-Host Integration + +Remote streaming hosts are the first consumer class for `libvirtualhid`. +Integration should preserve a host application's existing network protocol, +client input parsing, configuration, feedback queue, and device lifecycle while +moving local virtual device creation behind the `libvirtualhid` API. + +## Integration Contract + +A streaming host should be able to: + +- Create stable per-client gamepad handles with both client-relative and global + indexes. +- Submit incremental button, axis, trigger, touchpad, motion, and battery + updates without recreating a device. +- Receive output callbacks for rumble, LEDs, adaptive triggers, trigger rumble, + and raw output reports where the selected profile supports them. +- Query profile and backend capabilities before warning users about unsupported + client features. +- Read device nodes and platform paths when a downstream consumer or diagnostic + needs to inspect SDL, HIDAPI, libinput, `hidraw`, or system device state. +- Use keyboard and mouse APIs for relative mouse, absolute mouse, buttons, + wheel, horizontal wheel, key events, and Unicode text input. + +`libvirtualhid` should not own the host application's network transport, packet +schema, configuration model, controller assignment policy, or status API. + +## Adapter Pattern + +The `examples/gamepad_adapter.cpp` example demonstrates the +intended shape: + +- Choose a built-in `DeviceProfile` from a host-facing profile name. +- Fill `CreateGamepadOptions` with stable controller metadata. +- Create a `GamepadStateAdapter` from a `Runtime`. +- Cache state inside the adapter as separate input events arrive. +- Submit an initial neutral report so operating-system consumers can enumerate + the virtual controller before the first client input packet. +- Forward output callbacks back to the physical client controller or feedback + queue. + +This keeps one public code path for Linux, Windows, and future platforms while +still letting each backend report real capability limits. + +## Current Readiness + +The core API and adapter shape cover the major streaming-host requirements: + +- Multiple controller lifecycles. +- Built-in profiles for common controller classes. +- Rich controller metadata. +- Gamepad output callbacks. +- Keyboard and mouse input paths. +- Linux `uhid` gamepads and `uinput` keyboard/pointer devices. +- Linux DualSense and DualShock 4 USB/Bluetooth report handling. +- Linux touchscreen, trackpad, and pen tablet device types. +- Windows UMDF/VHF gamepad creation through an installed driver package. + +Remaining replacement work is validation and packaging, not broad API shape: + +- Validate Windows UMDF/VHF gamepads against the same application classes that + previously relied on ViGEmBus, including XInput-only compatibility decisions. +- Validate DualShock 4 and Xbox behavior through the intended Windows streaming + host path. +- Replace any consumer-specific ViGEmBus installer, status, and diagnostics with + libvirtualhid driver-package checks. +- Add and validate the Linux host adapter for the selected controller profile + names used by the consuming application. +- Define the FreeBSD backend subset explicitly instead of assuming Linux + `uhid` behavior applies. +- Implement and validate a macOS backend before advertising macOS runtime + support. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..b0b5635 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,140 @@ +# Usage and API + +This page covers how consumers bring `libvirtualhid` into a CMake project and +which public API concepts they should build around. + +## CMake Consumption + +The library exports `libvirtualhid::libvirtualhid`. + +For an installed package, install this project into a prefix and point the +consumer configure at that prefix: + +```bash +cmake --install cmake-build-release --prefix /opt/libvirtualhid +cmake -S your-app -B cmake-build-your-app -DCMAKE_PREFIX_PATH=/opt/libvirtualhid +``` + +Then link the exported package from the consuming project: + +```cmake +find_package(libvirtualhid CONFIG REQUIRED) +target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) +``` + +For a vendored checkout, add the project directly and link the same target: + +```cmake +add_subdirectory(third-party/libvirtualhid) +target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) +``` + +For `FetchContent`, pin a tag or commit and make the project available: + +```cmake +include(FetchContent) + +FetchContent_Declare( + libvirtualhid + GIT_REPOSITORY https://github.com/LizardByte/libvirtualhid.git + GIT_TAG +) +FetchContent_MakeAvailable(libvirtualhid) + +target_link_libraries(your_app PRIVATE libvirtualhid::libvirtualhid) +``` + +Examples, tests, docs, and the Windows driver package are top-level or opt-in +builds. Normal vendored and `FetchContent` consumers only get the library target +unless they explicitly enable additional options. + +## Build Options + +- `BUILD_EXAMPLES`: build example executables when this repository is the top + level project. +- `BUILD_TESTS`: build the GoogleTest suite when this repository is the top + level project. +- `BUILD_DOCS`: build Doxygen documentation when this repository is the top + level project. +- `LIBVIRTUALHID_INSTALL`: install targets, headers, and CMake package files. + This defaults to on for direct builds and off when consumed by another CMake + project. +- `LIBVIRTUALHID_ENABLE_XTEST`: enable the Linux X11/XTest keyboard and mouse + fallback. +- `LIBVIRTUALHID_BUILD_WINDOWS_DRIVER`: build the Windows UMDF2 driver package + with the Microsoft WDK/MSVC toolchain. +- `LIBVIRTUALHID_ENABLE_PACKAGING`: enable CPack package metadata. +- `LIBVIRTUALHID_WARNINGS_AS_ERRORS`: treat project warnings as errors. + +Linux consumers need the backend development packages used by the build, such as +`libevdev` and `pkg-config`. Windows consumers can build the normal C++ library +with MSVC or MinGW/UCRT64; the UMDF driver package is a separate WDK/MSVC build +artifact. + +## Public API Shape + +The API centers on portable device concepts: + +- `Runtime`: owns backend discovery, initialization, device creation, and + shutdown. +- `VirtualDevice`: common lifecycle for created devices. +- `Gamepad`: submits normalized gamepad state and receives output callbacks. +- `Keyboard`: submits key press/release and UTF-8 text input. +- `Mouse`: submits relative motion, absolute motion, buttons, vertical scroll, + and horizontal scroll. +- `Touchscreen`, `Trackpad`, and `PenTablet`: expose touch and tablet device + primitives where the backend supports them. +- `DeviceProfile`: describes device identity, HID descriptors, report layout, + and profile capabilities. +- `DeviceNode`: reports backend-visible device nodes and paths for diagnostics + or handoff to SDL, libinput, HIDAPI, and similar consumers. +- `BackendCapabilities`: reports runtime/backend limits such as virtual HID, + output report, keyboard, mouse, XTest fallback, and installed-driver support. + +## Gamepad Example + +```cpp +#include + +auto runtime = lvh::Runtime::create(); +auto created = runtime->create_gamepad(lvh::profiles::xbox_series()); +if (!created) { + return; +} + +auto &gamepad = *created.gamepad; +gamepad.set_output_callback([](const lvh::GamepadOutput &output) { + if (output.kind == lvh::GamepadOutputKind::rumble) { + // Route rumble back to the physical client controller. + } +}); + +lvh::GamepadState state; +state.buttons.set(lvh::GamepadButton::a, true); +state.left_stick = {0.25F, -0.5F}; +state.right_trigger = 1.0F; + +gamepad.submit(state); +``` + +The `examples/gamepad_adapter.cpp` example shows the +streaming-host-oriented adapter path. It maps incremental button, axis, trigger, +touch, motion, battery, feedback, and lifecycle updates onto the platform-neutral +`Runtime` and `Gamepad` APIs. + +## Built-In Profiles + +Built-in gamepad profiles include: + +- Generic HID gamepad. +- Xbox 360. +- Xbox One. +- Xbox Series. +- DualShock 4 USB and Bluetooth. +- DualSense USB and Bluetooth. +- Nintendo Switch Pro. + +Profiles advertise support for features such as rumble, trigger rumble, RGB +LEDs, adaptive triggers, motion sensors, touchpads, battery state, profile +specific buttons, and raw output reports. Consumers should query profile and +backend capabilities before warning users about unsupported client features. diff --git a/docs/windows-driver.md b/docs/windows-driver.md new file mode 100644 index 0000000..3836445 --- /dev/null +++ b/docs/windows-driver.md @@ -0,0 +1,166 @@ +# Windows Driver Package + +Windows gamepad support uses a user-mode UMDF2 control driver backed by Virtual +HID Framework. The driver package is separate from the normal C++ library build: +the library remains consumable from MSVC and MinGW/UCRT64, while the driver +package is built with the Microsoft SDK/WDK toolchain. + +## Microsoft Store Listing Text + +The Windows driver package is not the same product surface as the C++ library, +so Store listing copy should describe the installed driver component. + +### Short Description + +```text +User-mode virtual HID driver package that enables compatible apps to create virtual gamepads on Windows. +``` + +### Description + +```text +Virtual HID Driver installs the user-mode driver component used by compatible +applications to create virtual HID gamepads on Windows. + +The package has no standalone user interface. After installation, compatible +applications can request virtual HID gamepads, and Windows applications that +understand standard HID gamepads can discover those devices. +``` + +## Architecture + +The backend opens the libvirtualhid control device and sends fixed-size C +protocol structures with `DeviceIoControl`. Create requests start a VHF child +device from the requested descriptor, VID/PID, version, and report layout. Input +reports are submitted through VHF, and HID output writes are normalized back to +the C++ output callback path. + +The driver opens a separate VHF source target for each virtual gamepad and +parents that target to the control-file handle that created it. If the creating +process exits or crashes, Windows cleans up gamepads that were not explicitly +destroyed. + +The backend reports `requires_installed_driver = true` and only advertises +gamepad/output-report support when the control device can be opened. Keyboard +and mouse support do not require the driver package. + +## Build + +Build the UMDF package with a Visual Studio generator and the WDK installed: + +```powershell +cmake -S . -B cmake-build-windows-driver -G "Visual Studio 17 2022" -A x64 ` + -DLIBVIRTUALHID_BUILD_WINDOWS_DRIVER=ON -DLIBVIRTUALHID_ENABLE_PACKAGING=ON ` + -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=ON +cmake --build cmake-build-windows-driver --config Release ` + --target libvirtualhid_windows_catalog gamepad_adapter +cpack -G WIX -C Release --config .\cmake-build-windows-driver\CPackConfig.cmake +``` + +The package defaults to UMDF 2.15, matching the inbox VHF UMDF source driver +while still exposing the framework APIs used by libvirtualhid. The driver links +the MSVC runtime statically so the UMDF host process does not need VC runtime +DLLs beside the driver. + +## Developer Install and Validation + +Developer helpers live under `scripts/windows`: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\windows\install-driver.ps1 ` + -InfPath .\cmake-build-windows-driver\src\platform\windows\driver\package\Release\libvirtualhid.inf ` + -LogPath .\cmake-build-windows-driver\install-driver.log +powershell -ExecutionPolicy Bypass -File .\scripts\windows\test-installed-driver.ps1 ` + -GamepadAdapterPath .\cmake-build-windows-driver\examples\Release\gamepad_adapter.exe ` + -GamepadProfile xseries +powershell -ExecutionPolicy Bypass -File .\scripts\windows\test-browser-gamepad.ps1 ` + -GamepadAdapterPath .\cmake-build-windows-driver\examples\Release\gamepad_adapter.exe ` + -GamepadProfile xseries +powershell -ExecutionPolicy Bypass -File .\scripts\windows\uninstall-driver.ps1 ` + -Force -RemoveCertificateSubject "CN=libvirtualhid CI Test Driver Signing" +``` + +The WiX installer also places validation files under the default install root, +`C:\Program Files\libvirtualhid`: + +- `scripts\windows\test-installed-driver.ps1` +- `scripts\windows\test-browser-gamepad.ps1` +- `tools\windows\gamepad_adapter.exe` + +The install helper stages the INF with `pnputil`, updates an existing +`ROOT\LIBVIRTUALHID` device when present, and creates that root-enumerated +device when it is missing. It uses SetupAPI/NewDev directly, so MSI installs do +not require WDK tools on the target machine. + +The installed-driver test fails if the root device is not started, if +`\\.\LibVirtualHid` cannot be opened, or if a held `gamepad_adapter` instance +does not produce a started HID child device. The browser helper launches a +desktop browser at `https://hardwaretester.com/gamepad` and validates that the +browser Gamepad API observes the held virtual controller. + +For manual browser validation, run the browser helper with `-KeepBrowserOpen`, +or run: + +```powershell +tools\windows\gamepad_adapter.exe xseries --hold-seconds 60 +``` + +Then open `https://hardwaretester.com/gamepad` in a normal desktop browser and +press one of the held virtual buttons if the browser requires a gamepad +activation event. + +## Installation Notes + +The driver binary is a user-mode UMDF DLL installed through the Windows Driver +Store, not a libvirtualhid `.sys` copied into `C:\Windows\System32\drivers`. +Windows still uses its built-in `WUDFRd.sys` and VHF components under +`System32\drivers`. + +The libvirtualhid-specific sign that installation completed is the +`ROOT\LIBVIRTUALHID` root device and the `\\.\LibVirtualHid` control device. +Development driver builds write a lightweight UMDF trace to: + +```text +C:\Windows\Temp\libvirtualhid-umdf-driver.log +``` + +During rapid development reinstalls, the fixed global control symbolic link can +briefly outlive the previous root device. The driver treats that collision as +non-fatal, and normal clients discover the PnP control device interface first. + +## Profile Compatibility + +The Windows backend publishes HID gamepads through VHF. DirectInput, SDL/HIDAPI, +Windows.Gaming.Input/GameInput, and browser Gamepad API clients should see +standard HID devices after the driver is installed. + +The built-in Xbox One and Xbox Series profiles use XboxGIP-shaped HID +descriptors. The Xbox Series profile keeps the public physical USB identity +`VID_045E&PID_0B12`; the driver also publishes a compatible driver-matching +hardware ID for Windows binding. The Xbox 360 profile is rejected by the +UMDF/VHF backend because a real Xbox 360 controller is an XUSB device rather +than a VHF HID gamepad. + +DualShock 4, DualSense, Switch Pro, and generic HID profiles are descriptor +driven. Consumers that display raw HID strings may still show the Windows VHF +product label because VHF does not provide a product/manufacturer string +callback. + +## Signing + +Windows driver packages require a signed catalog for normal installation. +Pull-request builds generate a short-lived self-signed test certificate, sign +`libvirtualhid.cat`, bundle the public certificate into the WiX installer, and +import it into local machine trust stores during install. + +Release builds must use Azure Trusted Signing for the catalog and generated MSI +and must not ship the local pull-request test certificate. + +## License + +The Windows UMDF driver source and generated Windows driver package artifacts, +including the driver MSI, are licensed under the LizardByte Source-Available +License 1.0 (LB-SAL 1.0). See the [license map](../LICENSES/README.md) for the +full repository license split. The MSI may also include MIT-licensed helper +components from this repository, so packaged installs include both license +texts. diff --git a/examples/gamepad_adapter.cpp b/examples/gamepad_adapter.cpp index 3dedf70..d34f694 100644 --- a/examples/gamepad_adapter.cpp +++ b/examples/gamepad_adapter.cpp @@ -65,6 +65,13 @@ namespace { } } // namespace +/** + * @brief Run the minimal virtual gamepad adapter example. + * @param argc Number of command-line arguments. + * @param argv Command-line arguments; the first positional argument selects + * the gamepad profile, and `--hold` keeps the device active for validation. + * @return Zero on success; nonzero when setup or input submission fails. + */ int main(int argc, char *argv[]) { auto profile_name = std::string_view {"ds5"}; auto hold = false; diff --git a/examples/keyboard_mouse_adapter.cpp b/examples/keyboard_mouse_adapter.cpp index c52780c..da245e9 100644 --- a/examples/keyboard_mouse_adapter.cpp +++ b/examples/keyboard_mouse_adapter.cpp @@ -9,6 +9,11 @@ // local includes #include +/** + * @brief Run the minimal keyboard and mouse input example. + * @return Zero on success; nonzero when device creation or input submission + * fails. + */ int main() { auto runtime = lvh::Runtime::create(); diff --git a/libvirtualhid-256.png b/libvirtualhid-256.png new file mode 100644 index 0000000..139979d Binary files /dev/null and b/libvirtualhid-256.png differ diff --git a/libvirtualhid-poster-1440x2160.png b/libvirtualhid-poster-1440x2160.png new file mode 100644 index 0000000..32ad710 Binary files /dev/null and b/libvirtualhid-poster-1440x2160.png differ diff --git a/libvirtualhid-store-2160.png b/libvirtualhid-store-2160.png new file mode 100644 index 0000000..1602fa6 Binary files /dev/null and b/libvirtualhid-store-2160.png differ diff --git a/libvirtualhid.svg b/libvirtualhid.svg new file mode 100644 index 0000000..998eba1 --- /dev/null +++ b/libvirtualhid.svg @@ -0,0 +1,31 @@ + + libvirtualhid dashed D-pad icon + A transparent virtual gamepad icon with a solid left half, dashed right half, and dashed D-pad in a green gradient. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/platform/windows/driver/CMakeLists.txt b/src/platform/windows/driver/CMakeLists.txt index 8535505..e1138e8 100644 --- a/src/platform/windows/driver/CMakeLists.txt +++ b/src/platform/windows/driver/CMakeLists.txt @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2026 David Lane +# SPDX-License-Identifier: LicenseRef-LizardByte-SAL-1.0 + if(NOT WIN32) message(FATAL_ERROR "The libvirtualhid Windows driver package can only be built on Windows.") endif() diff --git a/src/platform/windows/driver/libvirtualhid.inf.in b/src/platform/windows/driver/libvirtualhid.inf.in index e3bfe93..2cbe8af 100644 --- a/src/platform/windows/driver/libvirtualhid.inf.in +++ b/src/platform/windows/driver/libvirtualhid.inf.in @@ -1,4 +1,8 @@ ; +; +; SPDX-FileCopyrightText: 2026 David Lane +; SPDX-License-Identifier: LicenseRef-LizardByte-SAL-1.0 +; ; libvirtualhid UMDF2 control driver package. ; This package includes UMDF startup file tracing for local driver bring-up. ; diff --git a/src/platform/windows/driver/libvirtualhid_umdf.cpp b/src/platform/windows/driver/libvirtualhid_umdf.cpp index 949e546..fcd366a 100644 --- a/src/platform/windows/driver/libvirtualhid_umdf.cpp +++ b/src/platform/windows/driver/libvirtualhid_umdf.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2026 David Lane +// SPDX-License-Identifier: LicenseRef-LizardByte-SAL-1.0 + /** * @file src/platform/windows/driver/libvirtualhid_umdf.cpp * @brief UMDF2 control driver entry points for the Windows libvirtualhid backend.