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
32 changes: 14 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ jobs:
-DBUILD_EXAMPLES=ON \
-DBUILD_TESTS=ON \
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_CONFIG} \
-DLIBVIRTUALHID_BUILD_TOOLS=ON \
-B cmake-build-ci \
-G Ninja \
-S .
Expand All @@ -226,6 +227,7 @@ jobs:
-DBUILD_DOCS=OFF `
-DBUILD_EXAMPLES=ON `
-DBUILD_TESTS=ON `
-DLIBVIRTUALHID_BUILD_TOOLS=ON `
-A x64 `
-B cmake-build-ci `
-G "Visual Studio 17 2022" `
Expand Down Expand Up @@ -339,7 +341,9 @@ jobs:
}

& $openCppCoverage `
--sources "$env:GITHUB_WORKSPACE\examples" `
--sources "$env:GITHUB_WORKSPACE\src" `
--sources "$env:GITHUB_WORKSPACE\tools" `
"--export_type=cobertura:$env:GITHUB_WORKSPACE\cmake-build-ci\reports\coverage.xml" `
--working_dir "$env:GITHUB_WORKSPACE\cmake-build-ci\tests" `
-- `
Expand Down Expand Up @@ -375,6 +379,9 @@ 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 All @@ -386,31 +393,20 @@ jobs:
GCOV_EXECUTABLE: ${{ matrix.gcov_executable }}
MSYS2_PATH_TYPE: inherit
run: |
uv run --project ../third-party/lizardbyte-common --locked --no-sync gcovr . -r ../src \
uv run --project ../third-party/lizardbyte-common --locked --no-sync gcovr . -r .. \
--filter ../examples/ \
--filter ../src/ \
--filter ../tools/ \
--gcov-executable "${GCOV_EXECUTABLE}" \
--exclude ../tests/ \
--exclude ../third-party/ \
--exclude-noncode-lines \
--exclude-throw-branches \
--exclude-unreachable-branches \
--verbose \
--xml-pretty \
-o reports/coverage.xml

- name: Run gamepad adapter example
if: runner.os == 'Linux'
run: |
./cmake-build-ci/examples/gamepad_adapter

- name: Run gamepad adapter example Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
if ("${{ matrix.kind }}" -eq "msys2") {
$env:PATH = "C:\msys64\${{ matrix.msystem }}\bin;C:\msys64\usr\bin;$env:PATH"
& .\cmake-build-ci\examples\gamepad_adapter.exe
} else {
& ".\cmake-build-ci\examples\$env:CMAKE_BUILD_CONFIG\gamepad_adapter.exe"
}

- name: Uninstall Windows driver installer
if: >-
always() &&
Expand Down Expand Up @@ -505,7 +501,7 @@ jobs:
run: >-
cmake --build cmake-build-driver
--config ${{ env.DRIVER_BUILD_CONFIG }}
--target libvirtualhid_windows_catalog gamepad_adapter
--target libvirtualhid_windows_catalog gamepad_adapter virtualhid_control
--parallel 2

- name: Validate Azure signing configuration
Expand Down
2 changes: 1 addition & 1 deletion .run/docs.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="docs" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="file:///$PROJECT_DIR$/../tray/cmake-build-release/docs/build/html/index.html" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="libvirtualhid" TARGET_NAME="docs" CONFIG_NAME="Debug-MinGW (ucrt64-ninja)" RUN_PATH="$PROJECT_DIR$/../../../../../Windows/explorer.exe">
<configuration default="false" name="docs" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="file:///$PROJECT_DIR$/../tray/cmake-build-release/docs/build/html/index.html" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="libvirtualhid" TARGET_NAME="docs" CONFIG_NAME="Release-MinGW (ucrt64-ninja)" RUN_PATH="$PROJECT_DIR$/../../../../../Windows/explorer.exe">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
Expand Down
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_version.cmake")
option(BUILD_DOCS "Build documentation" ${LIBVIRTUALHID_IS_TOP_LEVEL})
option(BUILD_TESTS "Build tests" ${LIBVIRTUALHID_IS_TOP_LEVEL})
option(BUILD_EXAMPLES "Build examples" ${LIBVIRTUALHID_IS_TOP_LEVEL})
option(LIBVIRTUALHID_BUILD_TOOLS "Build libvirtualhid diagnostic tools" ${LIBVIRTUALHID_IS_TOP_LEVEL})
option(LIBVIRTUALHID_TOOLS_STATIC_RUNTIME "Link libvirtualhid tools to static compiler runtimes where supported" ON)
option(LIBVIRTUALHID_TOOLS_FULLY_STATIC "Attempt to link libvirtualhid tools as fully static binaries" OFF)
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_INSTALL
Expand Down Expand Up @@ -107,6 +110,10 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_subdirectory(examples)
endif()

if(LIBVIRTUALHID_BUILD_TOOLS)
add_subdirectory(tools)
endif()

if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ behind backend implementations.
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.
- An optional `virtualhid_control` native UI tool for creating, removing,
controlling, and inspecting test gamepads through the public C++ API.
- CMake consumption through installed packages, vendored source,
`add_subdirectory`, or `FetchContent`.

Expand Down
12 changes: 10 additions & 2 deletions cmake/packaging/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ set(LIBVIRTUALHID_DRIVER_LICENSE_FILE
install(FILES
"${PROJECT_SOURCE_DIR}/scripts/windows/libvirtualhid-driver-common.ps1"
"${PROJECT_SOURCE_DIR}/scripts/windows/install-driver.ps1"
"${PROJECT_SOURCE_DIR}/scripts/windows/test-browser-gamepad.ps1"
"${PROJECT_SOURCE_DIR}/scripts/windows/test-installed-driver.ps1"
"${PROJECT_SOURCE_DIR}/scripts/windows/uninstall-driver.ps1"
DESTINATION "scripts/windows"
COMPONENT driver)
Expand All @@ -30,10 +28,20 @@ if(NOT TARGET gamepad_adapter)
"gamepad_adapter validation tool can be packaged.")
endif()

if(NOT TARGET virtualhid_control)
message(FATAL_ERROR
"The Windows driver installer requires LIBVIRTUALHID_BUILD_TOOLS=ON "
"so the virtualhid_control UI tool can be packaged.")
endif()

install(TARGETS gamepad_adapter
RUNTIME DESTINATION "tools/windows"
COMPONENT driver)

install(TARGETS virtualhid_control
RUNTIME DESTINATION "tools/windows"
COMPONENT driver)

if(LIBVIRTUALHID_DRIVER_TEST_CERTIFICATE)
install(FILES "${LIBVIRTUALHID_DRIVER_TEST_CERTIFICATE}"
DESTINATION "certificates"
Expand Down
2 changes: 2 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ code and tests provide a better source of truth.
- `test_libvirtualhid`: shared GoogleTest binary under the build directory's
`tests` directory.
- `gamepad_adapter`: example executable for profile and lifecycle diagnostics.
- `run_gamepad_adapter_example`: CMake target that runs `gamepad_adapter` from
its generator-specific output path when a platform backend is available.
- Windows driver helper scripts under `scripts/windows`.
- Linux consumer tests through SDL2, libinput, `uhid`, and `uinput` where the
host environment supports real virtual devices.
Expand Down
9 changes: 9 additions & 0 deletions docs/platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ reports, and output reports matter for controller compatibility. Keyboard and
pointer devices prefer `uinput` because those devices map naturally to Linux
input devices.

The optional `virtualhid_control` diagnostic UI is currently implemented with
native Windows controls. A future Linux UI should use a native toolkit that can
be packaged as a release asset. Static Linux linking is possible only when the
target distribution provides static archives for all selected backend and UI
dependencies, including `libevdev` and any enabled X11/XTest libraries. Many
distro toolchains intentionally omit some static archives, so release packaging
should keep full static linking as a packaging-mode choice rather than an
unconditional default.

### Permissions

Linux deployment is usually a permissions problem, not a kernel-module problem.
Expand Down
57 changes: 31 additions & 26 deletions docs/store-review-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,55 @@ Paste this into the Partner Center certification notes field:

```text
This package installs the libvirtualhid Windows user-mode UMDF/VHF virtual HID
driver. It has no standalone end-user UI; applications consume it through the
libvirtualhid client API.
driver. Applications consume it through the libvirtualhid client API, and the
MSI includes a native diagnostic UI for local validation.

A prebuilt validation tool and PowerShell validation scripts are installed by
the MSI so no programming, SDK setup, or driver build environment is required.
Please install the released, production-signed MSI, then run the validation
commands below from PowerShell.
A prebuilt native validation tool is installed by the MSI so no programming,
SDK setup, or driver build environment is required. Please install the released,
production-signed MSI, then launch the validation tool below.

Default install root:
C:\Program Files\libvirtualhid

Installed validation files:
C:\Program Files\libvirtualhid\scripts\windows\test-installed-driver.ps1
C:\Program Files\libvirtualhid\scripts\windows\test-browser-gamepad.ps1
C:\Program Files\libvirtualhid\tools\windows\virtualhid_control.exe
C:\Program Files\libvirtualhid\tools\windows\gamepad_adapter.exe

Required validation:
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
powershell -ExecutionPolicy Bypass -File "$installRoot\scripts\windows\test-installed-driver.ps1" `
-GamepadAdapterPath "$installRoot\tools\windows\gamepad_adapter.exe" `
-GamepadProfile xseries `
-Verbose
& "$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.

Expected result:
- The command exits successfully.
- The ROOT\LIBVIRTUALHID control device reports Status: Started.
- The \\.\LibVirtualHid control device opens successfully.
- The UI launches without requiring SDK or WDK tools.
- The backend status reports windows-umdf with gamepad support available.
- A virtual HID gamepad is created and appears in the device list.
- A virtual HID gamepad child device starts, matching either
HID\VID_045E&PID_0B12&IG_00 or HID\VID_045E&PID_02FF&IG_00.
- Button and axis values in the UI can be pressed or moved without errors.

Optional browser validation:
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
powershell -ExecutionPolicy Bypass -File "$installRoot\scripts\windows\test-browser-gamepad.ps1" `
-GamepadAdapterPath "$installRoot\tools\windows\gamepad_adapter.exe" `
-GamepadProfile xseries `
-KeepBrowserOpen
& "$installRoot\tools\windows\virtualhid_control.exe"

Create the default Xbox Series gamepad, then open:
https://hardwaretester.com/gamepad

Use the libvirtualhid control window to press buttons or move axes while the
browser page is open.

Expected result:
- Microsoft Edge or Google Chrome opens a gamepad test page.
- Microsoft Edge, Google Chrome, or another desktop browser opens a gamepad
test page.
- The browser Gamepad API sees an Xbox-compatible controller.
- Button and axis values change while the validation adapter is running.
- Button and axis values change while controls are used in the validation UI.

The installed gamepad_adapter.exe is built with the static MSVC runtime, so it
does not require the Visual C++ Redistributable to be installed separately.
The installed virtualhid_control.exe and gamepad_adapter.exe binaries are built
with the static MSVC runtime, so they do not require the Visual C++
Redistributable to be installed separately.
```

## Manual Review Steps
Expand All @@ -66,8 +71,8 @@ does not require the Visual C++ Redistributable to be installed separately.
`libvirtualhid-Windows-Driver-installer.msi`.
2. Reboot only if Windows reports that a reboot is required.
3. Open PowerShell.
4. Run the required validation command from the submission notes.
5. Optionally run the browser validation command.
4. Run the required validation tool from the submission notes.
5. 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 @@ -85,4 +90,4 @@ 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 `gamepad_adapter.exe` is running.
child device while `virtualhid_control.exe` has a gamepad created.
33 changes: 33 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ unless they explicitly enable additional options.
level project.
- `BUILD_DOCS`: build Doxygen documentation when this repository is the top
level project.
- `LIBVIRTUALHID_BUILD_TOOLS`: build diagnostic tool binaries, including
`virtualhid_control`, when this repository is the top level project.
- `LIBVIRTUALHID_TOOLS_STATIC_RUNTIME`: link diagnostic tools against static
compiler runtimes where supported. This is enabled by default so the Windows
MinGW/UCRT64 `virtualhid_control.exe` does not need adjacent MinGW runtime
DLLs. MSVC uses the static runtime for tools in the Windows driver package
build, where the packaged library, examples, and tools are all built with the
same runtime setting.
- `LIBVIRTUALHID_TOOLS_FULLY_STATIC`: pass full static link flags for
diagnostic tools. On Linux this also requires static archives for backend
dependencies such as `libevdev`, and may not be supported by every distro.
- `LIBVIRTUALHID_INSTALL`: install targets, headers, and CMake package files.
This defaults to on for direct builds and off when consumed by another CMake
project.
Expand All @@ -71,6 +82,28 @@ Linux consumers need the backend development packages used by the build, such as
with MSVC or MinGW/UCRT64; the UMDF driver package is a separate WDK/MSVC build
artifact.

## Diagnostic UI

`virtualhid_control` is an optional native diagnostic UI binary:

```bash
virtualhid_control
```

The current Windows UI can create and remove gamepads from the built-in
profiles, submit buttons, sticks, triggers, and battery state, show backend and
profile 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; enable `Lock buttons` to keep the old click-to-toggle behavior
for held inputs.

External devices created by another process, such as Sunshine, are not
enumerated yet. That requires backend protocol support so the Windows driver or
Linux backend can expose cross-process device snapshots without letting two
processes race to control the same virtual device.

## Public API Shape

The API centers on portable device concepts:
Expand Down
35 changes: 27 additions & 8 deletions docs/windows-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ User-mode virtual HID driver package that enables compatible apps to create virt
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.
The package includes a local diagnostic UI for creating and testing virtual
gamepads. Compatible applications can also request virtual HID gamepads, and
Windows applications that understand standard HID gamepads can discover those
devices.
```

## Architecture
Expand All @@ -51,9 +52,9 @@ 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
-DBUILD_TESTS=OFF -DBUILD_EXAMPLES=ON -DLIBVIRTUALHID_BUILD_TOOLS=ON
cmake --build cmake-build-windows-driver --config Release `
--target libvirtualhid_windows_catalog gamepad_adapter
--target libvirtualhid_windows_catalog gamepad_adapter virtualhid_control
cpack -G WIX -C Release --config .\cmake-build-windows-driver\CPackConfig.cmake
```

Expand Down Expand Up @@ -83,9 +84,13 @@ powershell -ExecutionPolicy Bypass -File .\scripts\windows\uninstall-driver.ps1
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`
- `tools\windows\virtualhid_control.exe`

The source-tree validation scripts remain developer and CI helpers. They are not
packaged as reviewer-facing MSI validation scripts because the native
`virtualhid_control.exe` tool can create, exercise, and inspect virtual
gamepads interactively.

The install helper stages the INF with `pnputil`, updates an existing
`ROOT\LIBVIRTUALHID` device when present, and creates that root-enumerated
Expand All @@ -99,7 +104,7 @@ 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:
run the interactive UI, or run:

```powershell
tools\windows\gamepad_adapter.exe xseries --hold-seconds 60
Expand All @@ -109,6 +114,20 @@ 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.

For interactive local validation, run:

```powershell
tools\windows\virtualhid_control.exe
```

The native UI can create, remove, control, and monitor gamepads that it owns.
Buttons are momentary by default, with an explicit lock mode for held inputs.
The UI also shows supported profile features, battery input state, device nodes,
and normalized feedback reports such as rumble, RGB LED, adaptive trigger, and
raw output events. Devices created by another process are not listed yet; that
requires a future Windows control-protocol extension for cross-process
diagnostics.

## Installation Notes

The driver binary is a user-mode UMDF DLL installed through the Windows Driver
Expand Down
Loading
Loading