Skip to content

Commit 248a8e6

Browse files
Add mouse controls to virtualhid UI
Extend the native control tool to create, inspect, reset, and remove virtual mice alongside gamepads. This adds keyboard-navigable mouse motion, button, and wheel actions, plus delayed browser-test scheduling for focus-sensitive mouse validation. Update the shared control model tests and refresh README and Windows/platform/store-review docs to reflect driver-backed mouse support and its licensing requirements.
1 parent 4c5c21f commit 248a8e6

8 files changed

Lines changed: 710 additions & 91 deletions

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<div align="center">
2020
<h2>🎮 Windows Virtual HID Driver License</h2>
2121
<p>
22-
<strong>A license is required to create virtual gamepads with the Windows driver.</strong><br>
22+
<strong>A license is required to create virtual gamepads or Raw Input-visible mice with the Windows driver.</strong><br>
2323
This requirement is Windows-only; non-Windows backends do not currently require a license.<br>
2424
Yearly and lifetime options are available.
2525
</p>
@@ -48,12 +48,13 @@ behind backend implementations.
4848
- Descriptor-driven PlayStation gamepads through Linux `uhid`; Generic, Xbox,
4949
and Switch Pro gamepads plus keyboard, mouse, touchscreen, trackpad, and pen
5050
tablet devices through `uinput`.
51-
- Windows gamepads through a user-mode UMDF2 control driver backed by Virtual
52-
HID Framework, with keyboard and mouse support through normal Win32 APIs.
51+
- Windows gamepads and Raw Input-visible mice through a user-mode UMDF2 control
52+
driver backed by Virtual HID Framework, with keyboard and fallback mouse
53+
support through normal Win32 APIs.
5354
- Output callbacks for profile-specific feedback such as rumble, LEDs,
5455
adaptive triggers, and raw HID output reports when available.
5556
- An optional `virtualhid_control` native UI tool for creating, removing,
56-
controlling, and inspecting test gamepads through the public C++ API.
57+
controlling, and inspecting test gamepads and mice through the public C++ API.
5758
- CMake consumption through installed packages, vendored source,
5859
`add_subdirectory`, or `FetchContent`.
5960

docs/maintainer/store-review-validation.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Paste this into the Partner Center certification notes field:
1515
```text
1616
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.
1717
18-
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.
18+
Every virtual gamepad or driver-backed Raw Input mouse 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.
1919
2020
Launch the validation tool below.
2121
@@ -31,17 +31,20 @@ Required validation:
3131
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
3232
Start-Process "$installRoot\tools\windows\virtualhid_control.exe"
3333
34-
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.
34+
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. Next, change Device type to Mouse and click Create. Use Tab or the arrow keys to highlight the mouse controls and Space or Enter to activate relative movement, momentary button, and wheel input without using the physical mouse.
3535
3636
Expected result:
37-
- The backend status reports windows-umdf with gamepad support available
37+
- The backend status reports windows-umdf with gamepad and mouse support available
3838
- The libvirtualhid_broker service is running
3939
- License validation succeeds and the license status reports Licensed
4040
- A virtual HID gamepad is created and appears in the device list
4141
- A virtual HID gamepad child device starts with the Xbox Series HID ID
4242
HID\VID_045E&PID_0B12&IG_00
4343
- Button, axis, and Share values in the UI can be pressed or moved without
4444
errors
45+
- A driver-backed virtual HID mouse is created and appears in the device list
46+
- Keyboard activation of the mouse controls moves the pointer, changes button
47+
state, and scrolls without errors
4548
4649
Optional browser validation:
4750
$installRoot = Join-Path $env:ProgramFiles "libvirtualhid"
@@ -55,6 +58,12 @@ Use the libvirtualhid control window to press buttons or move axes while the bro
5558
Expected result:
5659
- The browser Gamepad API sees an Xbox-compatible controller
5760
- Button and axis values change while controls are used in the validation UI
61+
62+
For a browser mouse-event tester, create a mouse in the validation UI and enable Delayed browser test. Leave the pointer over the browser test target, activate a movement, button, or wheel action with the keyboard, then switch to the browser before the displayed countdown expires.
63+
64+
Expected result:
65+
- The browser receives the queued mouse action while it owns focus
66+
- A queued button action produces one press followed by one release
5867
```
5968

6069
## Manual Review Steps
@@ -66,7 +75,8 @@ Expected result:
6675
4. Run the required validation tool from the submission notes.
6776
5. Activate the review key supplied through Partner Center.
6877
6. Create the default gamepad and exercise its controls.
69-
7. Optionally, run the browser validation steps.
78+
7. Create a mouse and exercise its controls with keyboard navigation.
79+
8. Optionally, run the browser validation steps.
7080

7181
If the default install location was changed during MSI installation, replace
7282
`$env:ProgramFiles\libvirtualhid` with the selected install directory.
@@ -84,5 +94,5 @@ HID-only and intentionally does not emulate the Xbox 360 XUSB stack.
8494

8595
The reviewer-visible success signal is the installed `ROOT\LIBVIRTUALHID`
8696
control device, the `\\.\LibVirtualHid` control path, the running
87-
`libvirtualhid_broker` service, and a started HID gamepad child device while
88-
`virtualhid_control.exe` has a gamepad created.
97+
`libvirtualhid_broker` service, and started HID child devices while
98+
`virtualhid_control.exe` has a gamepad and mouse created.

docs/platform-support.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ Use capability queries for behavior such as:
2525
## Windows
2626

2727
The Windows backend keeps the normal C++ library buildable with MSVC and
28-
MinGW/UCRT64. Keyboard and mouse input use Win32 APIs. Gamepad creation uses a
29-
user-mode UMDF2 control driver and Windows Virtual HID Framework.
28+
MinGW/UCRT64. Gamepad creation and Raw Input-visible relative mouse input use a
29+
user-mode UMDF2 control driver and Windows Virtual HID Framework. Keyboard,
30+
absolute mouse input, and the mouse fallback use Win32 APIs.
3031

3132
The C++ library communicates with the driver through fixed-size protocol
3233
structures and `DeviceIoControl`, not C++ STL types. This keeps the public API
@@ -199,6 +200,13 @@ libraries. Many distro toolchains intentionally omit some static archives, so
199200
release packaging should keep full static linking as a packaging-mode choice
200201
rather than an unconditional default.
201202

203+
The UI can create and exercise both gamepads and mice. Its mouse movement,
204+
button, and wheel controls participate in Dear ImGui keyboard navigation; use
205+
Tab or the arrow keys to highlight them and Space or Enter to activate them.
206+
Mouse buttons are momentary. A delayed browser-test mode queues an action long
207+
enough to switch focus to an external event tester, sending button actions as a
208+
single press-and-release click.
209+
202210
### Permissions
203211

204212
Linux deployment requires both device-node permissions and the kernel modules

docs/windows-driver.md

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ Virtual HID Driver installs the user-mode driver component used by compatible
2929
applications to create virtual HID gamepads and mice on Windows.
3030
3131
The package includes a local diagnostic UI for creating and testing virtual
32-
gamepads. Compatible applications can also request virtual HID gamepads or mice,
33-
and Windows applications that understand standard HID devices can discover them.
32+
gamepads and mice. Compatible applications can also request virtual HID gamepads
33+
or mice, and Windows applications that understand standard HID devices can
34+
discover them.
3435
```
3536

3637
## Architecture
@@ -182,7 +183,7 @@ The WiX installer also places validation files under the default install root,
182183
The source-tree validation scripts remain developer and CI helpers. They are not
183184
packaged as reviewer-facing MSI validation scripts because the native
184185
`virtualhid_control.exe` tool can create, exercise, and inspect virtual
185-
gamepads interactively.
186+
gamepads and mice interactively.
186187

187188
The install helper stages the INF with `pnputil`, updates an existing
188189
`ROOT\LIBVIRTUALHID` device when present, and creates that root-enumerated
@@ -228,18 +229,33 @@ For interactive local validation, run:
228229
tools\windows\virtualhid_control.exe
229230
```
230231

231-
The native UI can create, remove, control, and monitor gamepads that it owns.
232-
Buttons are momentary by default, with an explicit lock mode for held inputs.
233-
The UI also shows supported profile features, battery input state, device nodes,
234-
and normalized feedback reports such as rumble, RGB LED, adaptive trigger, and
235-
raw output events. Devices created by another process are not listed yet; that
236-
requires a future Windows control-protocol extension for cross-process
237-
diagnostics.
232+
The native UI can create, remove, control, and monitor gamepads and mice that it
233+
owns. Gamepad buttons are momentary by default, with an explicit lock mode for
234+
held inputs. Mouse controls provide relative movement, five momentary buttons,
235+
vertical scrolling, and horizontal panning. Use Tab or the arrow keys to
236+
highlight a mouse control and Space or Enter to activate it, avoiding use of the
237+
physical mouse while testing the virtual device. A mouse button remains pressed
238+
only while its activation key is held.
239+
240+
For an external mouse-event tester, enable **Delayed browser test**, choose a
241+
delay, and activate the desired action. Switch to the browser before the
242+
countdown expires while leaving the pointer over its test target. Movement and
243+
wheel actions are submitted once the browser owns focus; a delayed button action
244+
submits one press-and-release click. The scheduler continues while the control
245+
window is unfocused or minimized.
246+
247+
The UI identifies a driver-backed HID mouse separately from the `SendInput`
248+
fallback and also shows supported profile features, battery input state, device
249+
nodes, and normalized gamepad feedback reports such as rumble, RGB LED,
250+
adaptive trigger, and raw output events. Devices created by another process are
251+
not listed yet; that requires a future Windows control-protocol extension for
252+
cross-process diagnostics.
238253

239254
On Windows, the UI also shows broker license status. It can activate a license
240255
key, refresh validation, deactivate the current machine, and open
241-
compiled purchase or account-management URLs. License management and normal
242-
virtual HID device use do not require elevation.
256+
compiled purchase or account-management URLs. The Create button is enabled for
257+
both gamepads and mice only while the broker reports a current machine license.
258+
License management and normal virtual HID device use do not require elevation.
243259

244260
## Installation Notes
245261

tests/unit/test_virtualhid_control_model.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,58 @@ TEST(VirtualHidControlModelTest, MapsProfileChoicesToProfiles) {
100100
EXPECT_FALSE(control::profile_for_choice(invalid).has_value());
101101
}
102102

103+
TEST(VirtualHidControlModelTest, ExposesGamepadAndMouseDeviceChoices) {
104+
ASSERT_EQ(control::device_type_choices.size(), 2U);
105+
EXPECT_EQ(control::device_type_choices[0].type, lvh::DeviceType::gamepad);
106+
EXPECT_EQ(control::device_type_choices[1].type, lvh::DeviceType::mouse);
107+
108+
ASSERT_EQ(control::mouse_button_choices.size(), 5U);
109+
EXPECT_EQ(control::mouse_button_choices[0].button, lvh::MouseButton::left);
110+
EXPECT_EQ(control::mouse_button_choices[1].button, lvh::MouseButton::middle);
111+
EXPECT_EQ(control::mouse_button_choices[2].button, lvh::MouseButton::right);
112+
EXPECT_EQ(control::mouse_button_choices[3].button, lvh::MouseButton::side);
113+
EXPECT_EQ(control::mouse_button_choices[4].button, lvh::MouseButton::extra);
114+
}
115+
116+
TEST(VirtualHidControlModelTest, MapsKeyboardMouseActionsToEvents) {
117+
using enum control::MouseControlAction;
118+
119+
const auto left = control::mouse_event_for_action(move_left, 25, 120);
120+
EXPECT_EQ(left.kind, lvh::MouseEventKind::relative_motion);
121+
EXPECT_EQ(left.x, -25);
122+
EXPECT_EQ(left.y, 0);
123+
124+
const auto right = control::mouse_event_for_action(move_right, 25, 120);
125+
EXPECT_EQ(right.x, 25);
126+
const auto up = control::mouse_event_for_action(move_up, 25, 120);
127+
EXPECT_EQ(up.y, -25);
128+
const auto down = control::mouse_event_for_action(move_down, 25, 120);
129+
EXPECT_EQ(down.y, 25);
130+
131+
const auto wheel_up_event = control::mouse_event_for_action(wheel_up, 25, 120);
132+
EXPECT_EQ(wheel_up_event.kind, lvh::MouseEventKind::vertical_scroll);
133+
EXPECT_EQ(wheel_up_event.high_resolution_scroll, 120);
134+
const auto wheel_down_event = control::mouse_event_for_action(wheel_down, 25, 120);
135+
EXPECT_EQ(wheel_down_event.high_resolution_scroll, -120);
136+
const auto pan_left_event = control::mouse_event_for_action(pan_left, 25, 120);
137+
EXPECT_EQ(pan_left_event.kind, lvh::MouseEventKind::horizontal_scroll);
138+
EXPECT_EQ(pan_left_event.high_resolution_scroll, -120);
139+
const auto pan_right_event = control::mouse_event_for_action(pan_right, 25, 120);
140+
EXPECT_EQ(pan_right_event.high_resolution_scroll, 120);
141+
}
142+
143+
TEST(VirtualHidControlModelTest, BuildsMomentaryMouseButtonEvents) {
144+
const auto press = control::mouse_button_event(lvh::MouseButton::side, true);
145+
EXPECT_EQ(press.kind, lvh::MouseEventKind::button);
146+
EXPECT_EQ(press.button, lvh::MouseButton::side);
147+
EXPECT_TRUE(press.pressed);
148+
149+
const auto release = control::mouse_button_event(lvh::MouseButton::side, false);
150+
EXPECT_EQ(release.kind, lvh::MouseEventKind::button);
151+
EXPECT_EQ(release.button, lvh::MouseButton::side);
152+
EXPECT_FALSE(release.pressed);
153+
}
154+
103155
TEST(VirtualHidControlModelTest, ConvertsSliderValues) {
104156
EXPECT_EQ(control::axis_to_slider(-2.0F), -control::slider_scale);
105157
EXPECT_EQ(control::axis_to_slider(-0.5F), -50);
@@ -132,6 +184,7 @@ TEST(VirtualHidControlModelTest, FormatsRawHex) {
132184
TEST(VirtualHidControlModelTest, SummarizesProfileFeatures) {
133185
const auto generic = lvh::profiles::generic_gamepad();
134186
const auto dualsense = lvh::profiles::dualsense();
187+
const auto mouse = lvh::profiles::mouse();
135188

136189
EXPECT_TRUE(control::supports_normalized_feedback(generic));
137190
EXPECT_TRUE(control::supports_normalized_feedback(dualsense));
@@ -144,6 +197,11 @@ TEST(VirtualHidControlModelTest, SummarizesProfileFeatures) {
144197
control::profile_feature_summary(dualsense),
145198
L"Features: battery yes | rumble yes | trigger rumble no | RGB LED yes | adaptive triggers yes | raw output yes"
146199
);
200+
EXPECT_EQ(
201+
control::device_feature_summary(mouse),
202+
L"Features: relative motion | five buttons | vertical wheel | horizontal wheel"
203+
);
204+
EXPECT_EQ(control::device_feature_summary(generic), control::profile_feature_summary(generic));
147205
}
148206

149207
TEST(VirtualHidControlModelTest, UpdatesVisibleControlsForProfiles) {

0 commit comments

Comments
 (0)