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
63 changes: 61 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,12 @@ jobs:
throw "OpenCppCoverage.exe was not found."
}

# The broker test hook compiles a private copy only for failure injection.
& $openCppCoverage `
--sources "$env:GITHUB_WORKSPACE\examples" `
--sources "$env:GITHUB_WORKSPACE\src" `
--sources "$env:GITHUB_WORKSPACE\tools" `
--excluded_sources "$env:GITHUB_WORKSPACE\src\platform\windows\broker" `
"--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 @@ -413,11 +415,13 @@ jobs:
GCOV_EXECUTABLE: ${{ matrix.gcov_executable }}
MSYS2_PATH_TYPE: inherit
run: |
# The broker test hook compiles a private copy only for failure injection.
uv run --project ../third-party/lizardbyte-common --locked --no-sync gcovr . -r .. \
--filter ../examples/ \
--filter ../src/ \
--filter ../tools/ \
--gcov-executable "${GCOV_EXECUTABLE}" \
--exclude ../src/platform/windows/broker/ \
--exclude ../tests/ \
--exclude ../third-party/ \
--exclude-noncode-lines \
Expand Down Expand Up @@ -590,7 +594,8 @@ jobs:
New-Item -ItemType Directory -Force -Path artifacts | Out-Null
Copy-Item `
-LiteralPath .\cmake-build-driver\cpack_artifacts\libvirtualhid.msi `
-Destination .\artifacts\libvirtualhid-Windows-Driver-installer.msi
-Destination `
".\artifacts\libvirtualhid-Windows-AMD64-driver-installer.msi"

- name: Export Azure driver signing certificate
if: >-
Expand Down Expand Up @@ -635,6 +640,39 @@ jobs:
files-folder-recurse: false
signing-account-name: ${{ vars.AZURE_SIGNING_ACCOUNT }}

- name: Validate release signing identities
if: >-
github.event_name == 'push' &&
needs.setup_release.outputs.publish_release == 'true' &&
vars.AZURE_SIGNING_ACCOUNT != ''
shell: pwsh
run: |
$catalogPath = Join-Path `
$env:GITHUB_WORKSPACE `
"cmake-build-driver\src\platform\windows\driver\package\$env:DRIVER_BUILD_CONFIG\libvirtualhid.cat"
$installerPath = Get-ChildItem -LiteralPath .\artifacts -Filter *.msi |
Select-Object -ExpandProperty FullName -First 1
if (!$installerPath) {
throw "The signed Windows driver installer was not found."
}

$catalogSignature = Get-AuthenticodeSignature -FilePath $catalogPath
$installerSignature = Get-AuthenticodeSignature -FilePath $installerPath
foreach ($signature in @($catalogSignature, $installerSignature)) {
if ($signature.Status -ne "Valid" -or !$signature.SignerCertificate) {
throw "A release signature is invalid: $($signature.StatusMessage)"
}
}
if ($catalogSignature.SignerCertificate.Subject -cne `
$installerSignature.SignerCertificate.Subject) {
throw "The catalog and MSI were signed with different identities."
}
Write-Host (
"Validated release signer " +
"$($installerSignature.SignerCertificate.Subject) " +
"[$($installerSignature.SignerCertificate.Thumbprint)]."
)

- name: Debug wix
if: always()
shell: pwsh
Expand Down Expand Up @@ -771,12 +809,33 @@ jobs:
run: |
mkdir -p artifacts
for name in Linux-GCC Linux-Clang macOS Windows-MinGW-UCRT64 Windows-MSVC; do
release_name="${name}"
case "${name}" in
Windows-MinGW-UCRT64) release_name="Windows-AMD64-MinGW-UCRT64" ;;
Windows-MSVC) release_name="Windows-AMD64-MSVC" ;;
esac
zip -r \
"artifacts/libvirtualhid-${{ needs.setup_release.outputs.release_tag }}-${name}.zip" \
"artifacts/libvirtualhid-${release_name}.zip" \
"install-${name}"
done
cp windows-driver-installer/*.msi artifacts/

- name: Validate release metadata
env:
RELEASE_COMMIT: ${{ needs.setup_release.outputs.release_commit }}
RELEASE_TAG: ${{ needs.setup_release.outputs.release_tag }}
RELEASE_VERSION: ${{ needs.setup_release.outputs.release_version }}
run: |
test -n "${RELEASE_TAG}"
test -n "${RELEASE_VERSION}"
test "${RELEASE_COMMIT}" = "${GITHUB_SHA}"
test -s "artifacts/libvirtualhid-Linux-GCC.zip"
test -s "artifacts/libvirtualhid-Linux-Clang.zip"
test -s "artifacts/libvirtualhid-macOS.zip"
test -s "artifacts/libvirtualhid-Windows-AMD64-driver-installer.msi"
test -s "artifacts/libvirtualhid-Windows-AMD64-MinGW-UCRT64.zip"
test -s "artifacts/libvirtualhid-Windows-AMD64-MSVC.zip"

- name: Create/Update GitHub Release
if: needs.setup_release.outputs.publish_release == 'true'
uses: LizardByte/actions/actions/release_create@d0ae7f82215a479fe2b74f4088c53ee6460513dd # v2026.728.214955
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ 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.

## ⚠️ Known Windows Limitations

- Steam does not expose the Xbox Series Share button from the VHF child through
the same Xbox HIDAPI path used by physical controllers. That path requires a
non-VHF Xbox HIDAPI/GIP transport.
- PlayStation and Nintendo rumble parsing is covered by protocol and installed
driver tests but has not yet completed broad validation with real client
applications.
- The published Windows driver installer is AMD64-only. Windows ARM64 release
packages require a different Microsoft driver-signing path.

## 🔁 Alternatives

Alternatives exist if `libvirtualhid` does not meet your needs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
Directory="INSTALL_ROOT"
ExeCommand=""[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe" -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File "[INSTALL_ROOT]scripts\windows\uninstall-driver.ps1" -Force -RemoveCertificateSubject "CN=libvirtualhid CI Test Driver Signing""
Execute="deferred"
Return="ignore"
Return="check"
Impersonate="no" />
<CustomAction Id="CA_LibVirtualHidUninstallDriverSilent"
Directory="INSTALL_ROOT"
ExeCommand="&quot;[WindowsFolder]System32\WindowsPowerShell\v1.0\powershell.exe&quot; -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File &quot;[INSTALL_ROOT]scripts\windows\uninstall-driver.ps1&quot; -Force -RemoveCertificateSubject &quot;CN=libvirtualhid CI Test Driver Signing&quot;"
Execute="deferred"
Return="ignore"
Return="check"
Impersonate="no" />

<InstallExecuteSequence>
Expand Down
2 changes: 1 addition & 1 deletion docs/store-review-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Expected result:
## Manual Review Steps

1. Install the released, production-signed
`libvirtualhid-Windows-Driver-installer.msi`.
`libvirtualhid-Windows-AMD64-driver-installer.msi`.
2. Reboot only if Windows reports that a reboot is required.
3. Open PowerShell.
4. Run the required validation tool from the submission notes.
Expand Down
23 changes: 21 additions & 2 deletions docs/windows-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ with a service SID. The service `ImagePath` is stored as a literal quoted path,
and installation fails if the registry value is not safely quoted. This avoids
CWE-428 unquoted-service-path escalation when the install root contains spaces.
The install helper also clears any legacy broker service `Environment` value so
licensing configuration cannot be overridden on the user's machine. The uninstall
helper stops and deletes that service before removing the driver package.
licensing configuration cannot be overridden on the user's machine. The
uninstall helper stops and deletes that service before removing the driver
package. It discovers staged OEM INF names through language-neutral DISM and
CIM objects instead of parsing localized `pnputil` labels. Uninstall fails if a
command fails or if the broker service, root device, or staged driver package
is still present after cleanup, so the MSI cannot silently report a complete
removal while driver state remains.

The installed-driver test fails if the root device is not started, if
`\\.\LibVirtualHid` cannot be opened, or if a held `gamepad_adapter` instance
Expand Down Expand Up @@ -322,6 +327,20 @@ do not alter the public platform-neutral profile API.
Consumers that display raw HID strings may still show the Windows VHF product
label because VHF does not provide a product/manufacturer string callback.

### Current Release Limits

- Steam does not expose the Xbox Series Share button from the VHF child through
the same Xbox HIDAPI path used by physical controllers. Supporting that path
requires a non-VHF Xbox HIDAPI/GIP transport.
- PlayStation and Nintendo rumble parsing is covered by protocol and installed
driver tests, but has not yet completed broad validation with real client
applications.
- The published Windows driver installer is AMD64-only. Windows ARM64 release
packages require a Microsoft dashboard signing path that is not part of the
current Azure Trusted Signing workflow.
- Every production gamepad creation requires a successful online license
validation response. There is no offline grace period.

## Signing

Windows driver packages require a signed catalog for normal installation.
Expand Down
Loading
Loading