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