Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,21 @@ jobs:
throw "Windows driver installer exited with code $($process.ExitCode)."
}

- name: Enable GitHub Actions evaluation window
if: runner.os == 'Windows'
shell: pwsh
run: |
$serviceName = "libvirtualhid_broker"
$serviceRegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName"
New-ItemProperty `
-LiteralPath $serviceRegistryPath `
-Name Environment `
-PropertyType MultiString `
-Value @("GITHUB_ACTIONS=true") `
-Force | Out-Null
Restart-Service -Name $serviceName -Force
(Get-Service -Name $serviceName).WaitForStatus("Running", [TimeSpan]::FromSeconds(15))

- name: Verify Windows test driver package
if: runner.os == 'Windows'
shell: pwsh
Expand All @@ -320,6 +335,9 @@ jobs:
-Verbose
}

- name: Run gamepad adapter example
run: cmake --build cmake-build-ci --config ${{ env.CMAKE_BUILD_CONFIG }} --target run_gamepad_adapter_example

- name: Prepare report directory
run: cmake -E make_directory cmake-build-ci/reports

Expand Down Expand Up @@ -384,9 +402,6 @@ jobs:

$coverage.Save($coveragePath)

- name: Run gamepad adapter example
run: cmake --build cmake-build-ci --config ${{ env.CMAKE_BUILD_CONFIG }} --target run_gamepad_adapter_example

- name: Generate gcov report
id: test_report
if: >-
Expand Down Expand Up @@ -506,7 +521,7 @@ jobs:
run: >-
cmake --build cmake-build-driver
--config ${{ env.DRIVER_BUILD_CONFIG }}
--target libvirtualhid_windows_catalog gamepad_adapter virtualhid_control
--target libvirtualhid_windows_catalog libvirtualhid_broker gamepad_adapter virtualhid_control
--parallel 2

- name: Validate Azure signing configuration
Expand Down
32 changes: 32 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ option(LIBVIRTUALHID_TOOLS_FULLY_STATIC "Attempt to link libvirtualhid tools as
option(LIBVIRTUALHID_TOOLS_STATIC_SDL3 "Prefer a static SDL3 diagnostic UI dependency when available" ON)
option(LIBVIRTUALHID_ENABLE_XTEST "Enable X11/XTest keyboard and mouse fallback on Linux" ON)
option(LIBVIRTUALHID_BUILD_WINDOWS_DRIVER "Build the Windows UMDF2 driver package with the WDK/MSVC toolchain" OFF)
option(LIBVIRTUALHID_BUILD_WINDOWS_BROKER
"Build the Windows broker service used by the monetized UMDF driver package"
${LIBVIRTUALHID_BUILD_WINDOWS_DRIVER})
option(LIBVIRTUALHID_INSTALL
"Install libvirtualhid targets, headers, and CMake package files" ${LIBVIRTUALHID_IS_TOP_LEVEL})
option(LIBVIRTUALHID_ENABLE_PACKAGING "Enable CPack package metadata" ${LIBVIRTUALHID_INSTALL})
Expand All @@ -67,6 +70,31 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTS AND NOT CMAKE_CXX_CO
set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage -ggdb -O0")
endif()

set(LIBVIRTUALHID_USES_LIZARDBYTE_COMMON OFF)
if(WIN32)
set(LIBVIRTUALHID_USES_LIZARDBYTE_COMMON ON)
endif()

if(LIBVIRTUALHID_USES_LIZARDBYTE_COMMON OR BUILD_TESTS)
set(LIZARDBYTE_COMMON_BUILD_TEST_SUPPORT
${BUILD_TESTS}
CACHE BOOL "Build lizardbyte-common GoogleTest support helpers" FORCE)
set(LIZARDBYTE_COMMON_INSTALL
${LIBVIRTUALHID_INSTALL}
CACHE BOOL "Install lizardbyte-common targets and package configuration" FORCE)
if(NOT TARGET lizardbyte::common)
add_subdirectory(third-party/lizardbyte-common)
endif()
if(MSVC AND LIBVIRTUALHID_BUILD_WINDOWS_DRIVER)
set_property(TARGET lizardbyte_common PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
if(TARGET lizardbyte_common_test_support)
set_property(TARGET lizardbyte_common_test_support PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()
endif()

# Copy MinGW runtime DLLs beside a target when using GNU toolchains on Windows.
function(libvirtualhid_copy_mingw_runtime target_name)
if(NOT WIN32 OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
Expand Down Expand Up @@ -103,6 +131,10 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_subdirectory(src/platform/windows/driver)
endif()

if(WIN32 AND LIBVIRTUALHID_BUILD_WINDOWS_BROKER)
add_subdirectory(src/platform/windows/broker)
endif()

if(BUILD_DOCS)
add_subdirectory(third-party/doxyconfig docs)
endif()
Expand Down
2 changes: 1 addition & 1 deletion LICENSES/LicenseRef-LizardByte-SAL-1.0.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
LIZARDBYTE SOURCE-AVAILABLE LICENSE
Version 1.0, May 2026

Copyright (C) 2026 David Lane. <https://app.lizardbyte.dev/>
Copyright (C) 2026 LIZARDBYTE LLC. <https://app.lizardbyte.dev/>
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.
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
<a href="https://sonarcloud.io/project/overview?id=LizardByte_libvirtualhid"><img src="https://img.shields.io/sonar/quality_gate/LizardByte_libvirtualhid.svg?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=sonarcloud" alt="SonarCloud"></a>
</div>

<div align="center">
<h2>🎮 Windows Virtual HID Driver License</h2>
<p>
<strong>A license is required to create virtual gamepads with the Windows driver.</strong><br>
This requirement is Windows-only; non-Windows backends do not currently require a license.<br>
Yearly and lifetime options are available.
</p>
<a href="https://buy.polar.sh/polar_cl_zj6Io5NVukXfZSl97ULtFvImfI5L1jbL2cSnc0Y72Pt"><img src="https://img.shields.io/badge/Buy_a_Windows_license-0078D4?logo=windows11&logoColor=white&style=for-the-badge" alt="Buy a Windows license"></a>
</div>

# Overview

## ℹ️ About
Expand Down
2 changes: 2 additions & 0 deletions cmake/libvirtualhid-config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ if(@LIBVIRTUALHID_USES_THREADS@)
pkg_check_modules(LIBEVDEV REQUIRED IMPORTED_TARGET libevdev)
endif()

find_dependency(lizardbyte-common)

include("${CMAKE_CURRENT_LIST_DIR}/libvirtualhid-targets.cmake")

check_required_components(libvirtualhid)
6 changes: 6 additions & 0 deletions cmake/packaging/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ if(NOT TARGET virtualhid_control)
"so the virtualhid_control UI tool can be packaged.")
endif()

if(NOT TARGET libvirtualhid_broker)
message(FATAL_ERROR
"The Windows driver installer requires LIBVIRTUALHID_BUILD_WINDOWS_BROKER=ON "
"so the broker service can be packaged.")
endif()

install(TARGETS gamepad_adapter
RUNTIME DESTINATION "tools/windows"
COMPONENT driver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
<CPackWiXFragment Id="#PRODUCT">
<CustomAction Id="CA_LibVirtualHidInstallDriver"
Directory="INSTALL_ROOT"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\windows\install-driver.ps1&quot; -InfPath &quot;[INSTALL_ROOT]drivers\windows\libvirtualhid.inf&quot; -CertificatePath &quot;[INSTALL_ROOT]certificates\libvirtualhid-ci-test.cer&quot; -LogPath &quot;[CommonAppDataFolder]libvirtualhid\install-driver.log&quot;"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\windows\install-driver.ps1&quot; -InfPath &quot;[INSTALL_ROOT]drivers\windows\libvirtualhid.inf&quot; -CertificatePath &quot;[INSTALL_ROOT]certificates\libvirtualhid-ci-test.cer&quot; -BrokerPath &quot;[INSTALL_ROOT]services\windows\libvirtualhid_broker.exe&quot; -LogPath &quot;[CommonAppDataFolder]libvirtualhid\install-driver.log&quot;"
Execute="deferred"
Return="check"
Impersonate="no" />
<CustomAction Id="CA_LibVirtualHidInstallDriverSilent"
Directory="INSTALL_ROOT"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\windows\install-driver.ps1&quot; -InfPath &quot;[INSTALL_ROOT]drivers\windows\libvirtualhid.inf&quot; -CertificatePath &quot;[INSTALL_ROOT]certificates\libvirtualhid-ci-test.cer&quot; -LogPath &quot;[CommonAppDataFolder]libvirtualhid\install-driver.log&quot;"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\windows\install-driver.ps1&quot; -InfPath &quot;[INSTALL_ROOT]drivers\windows\libvirtualhid.inf&quot; -CertificatePath &quot;[INSTALL_ROOT]certificates\libvirtualhid-ci-test.cer&quot; -BrokerPath &quot;[INSTALL_ROOT]services\windows\libvirtualhid_broker.exe&quot; -LogPath &quot;[CommonAppDataFolder]libvirtualhid\install-driver.log&quot;"
Execute="deferred"
Return="check"
Impersonate="no" />
Expand Down
18 changes: 13 additions & 5 deletions docs/store-review-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Driver Store. It is not a kernel-mode `.sys` driver.
Paste this into the Partner Center certification notes field:

```text
This package installs the libvirtualhid Windows user-mode UMDF/VHF virtual HID driver. Applications consume it through the libvirtualhid client API, and the MSI includes a native diagnostic UI for local validation.
This package installs the libvirtualhid Windows user-mode UMDF/VHF virtual HID driver and local broker service. Applications consume it through the libvirtualhid client API, and the MSI includes a native diagnostic UI for local validation.

Every virtual gamepad creation requires an active license. A currently granted review license key with an available device activation is supplied separately in the Partner Center certification credentials or notes. The key is not embedded in the package or this document.

Launch the validation tool below.

Expand All @@ -23,15 +25,18 @@ C:\Program Files\libvirtualhid
Installed validation files:
C:\Program Files\libvirtualhid\tools\windows\virtualhid_control.exe
C:\Program Files\libvirtualhid\tools\windows\gamepad_adapter.exe
C:\Program Files\libvirtualhid\services\windows\libvirtualhid_broker.exe

Required validation:
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
& "$installRoot\tools\windows\virtualhid_control.exe"

In the libvirtualhid control window, leave the default Xbox Series profile selected and click Create. Use the button and axis controls in the UI to submit input to the virtual controller.
In the libvirtualhid control window, paste the supplied review key into the License key field and click Activate license. Confirm the status changes to Licensed. Then leave the default Xbox Series profile selected and click Create. Use the button and axis controls in the UI to submit input to the virtual controller.

Expected result:
- The backend status reports windows-umdf with gamepad support available
- The libvirtualhid_broker service is running
- License validation succeeds and the license status reports Licensed
- A virtual HID gamepad is created and appears in the device list
- A virtual HID gamepad child device starts with the Xbox Series HID ID
HID\VID_045E&PID_0B12&IG_00
Expand Down Expand Up @@ -59,7 +64,9 @@ Expected result:
2. Reboot only if Windows reports that a reboot is required.
3. Open PowerShell.
4. Run the required validation tool from the submission notes.
5. Optionally run the browser validation steps.
5. Activate the review key supplied through Partner Center.
6. Create the default gamepad and exercise its controls.
7. Optionally run the browser validation steps.

If the default install location was changed during MSI installation, replace
`$env:ProgramFiles\libvirtualhid` with the selected install directory.
Expand All @@ -76,5 +83,6 @@ The `x360` profile is not used for Store review. The Windows UMDF/VHF backend is
HID-only and intentionally does not emulate the Xbox 360 XUSB stack.

The reviewer-visible success signal is the installed `ROOT\LIBVIRTUALHID`
control device, the `\\.\LibVirtualHid` control path, and a started HID gamepad
child device while `virtualhid_control.exe` has a gamepad created.
control device, the `\\.\LibVirtualHid` control path, the running
`libvirtualhid_broker` service, and a started HID gamepad child device while
`virtualhid_control.exe` has a gamepad created.
15 changes: 15 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ unless they explicitly enable additional options.
fallback.
- `LIBVIRTUALHID_BUILD_WINDOWS_DRIVER`: build the Windows UMDF2 driver package
with the Microsoft WDK/MSVC toolchain.
- `LIBVIRTUALHID_BUILD_WINDOWS_BROKER`: build the Windows broker service used by
the driver package for gamepad creation, active-device limits, and license
state.
- `LIBVIRTUALHID_ENABLE_PACKAGING`: enable CPack package metadata.
- `LIBVIRTUALHID_WARNINGS_AS_ERRORS`: treat project warnings as errors.

Expand All @@ -102,6 +105,14 @@ capabilities, list device nodes reported for UI-created devices, and display
normalized gamepad output such as rumble, RGB LED, adaptive trigger, trigger
rumble, and raw report events delivered through the normal callback path. Button
controls are momentary by default so they behave like physical gamepad buttons;
on Windows, the UI also displays broker license status and can activate,
refresh, or deactivate a machine license. Outside the explicitly marked GitHub
Actions test environment, every Windows UMDF gamepad creation requires a
current successful license validation response and there is no offline grace
period. The CI-only exception is a single five-minute window that begins with
the first gamepad creation attempt. Purchase and account-management buttons use
the compiled URLs in
`src/platform/windows/shared/lvh_windows_broker_config.hpp`.
enable `Lock buttons` to keep the old click-to-toggle behavior for held inputs.
The resizable window supports a compact width. Its device and control panels
stack, and the button grid reflows, to keep controls usable when it is narrowed.
Expand All @@ -119,6 +130,10 @@ The API centers on portable device concepts:

- `Runtime`: owns backend discovery, initialization, device creation, and
shutdown.
- `get_license_status`, `activate_license`, `validate_license`, and
`deactivate_license`: provider-neutral machine license operations for host
applications. On Windows these call the installed local broker; license keys
are not retained by the client library or returned to the application.
- `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.
Expand Down
Loading
Loading