Skip to content

Commit f06302f

Browse files
feat(windows): add keyboard/mouse support and expose global UMDF control-device path (#34)
1 parent 0d704b0 commit f06302f

11 files changed

Lines changed: 629 additions & 34 deletions

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,16 @@ be compiler-neutral: prefer a stable C ABI, named pipe, device interface IOCTL,
9999
or similar control channel over passing C++ STL types across that boundary.
100100

101101
The current Windows backend selects a UMDF control-channel implementation for
102-
`BackendKind::platform_default`. It probes `\\.\LibVirtualHid`, reports
103-
`requires_installed_driver = true`, and only advertises gamepad/output-report
104-
support when the driver package is installed and the control device can be
105-
opened. The client library stays buildable with MSVC and MinGW/UCRT64 because
106-
the backend talks to the driver through fixed-size C protocol structures and
107-
Win32 `DeviceIoControl` calls. The default control device path can be overridden
108-
for diagnostics with `LIBVIRTUALHID_WINDOWS_CONTROL_DEVICE`.
102+
`BackendKind::platform_default`. It always exposes keyboard and mouse through
103+
Win32 `SendInput`, then probes `\\.\LibVirtualHid` for descriptor-driven virtual
104+
gamepads. It reports `requires_installed_driver = true`, and only advertises
105+
gamepad/output-report support when the driver package is installed and the
106+
control device can be opened. Touchscreen, trackpad, and pen tablet support are
107+
not implemented in the Windows backend yet. The client library stays buildable
108+
with MSVC and MinGW/UCRT64 because the gamepad path talks to the driver through
109+
fixed-size C protocol structures and Win32 `DeviceIoControl` calls. The default
110+
control device path can be overridden for diagnostics with
111+
`LIBVIRTUALHID_WINDOWS_CONTROL_DEVICE`.
109112

110113
The UMDF driver uses Windows Virtual HID Framework (VHF) for OS-visible gamepad
111114
devices. Create requests start a VHF child device from the requested descriptor,

src/CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@ target_sources(${PROJECT_NAME}
1212

1313
if(LIBVIRTUALHID_USES_THREADS)
1414
find_package(Threads REQUIRED)
15-
find_package(PkgConfig REQUIRED)
16-
pkg_check_modules(LIBEVDEV REQUIRED IMPORTED_TARGET libevdev)
15+
if(LIBEVDEV_CUSTOM_INCLUDE_DIR AND LIBEVDEV_CUSTOM_LIBRARY)
16+
add_library(PkgConfig::LIBEVDEV UNKNOWN IMPORTED)
17+
set_target_properties(PkgConfig::LIBEVDEV PROPERTIES
18+
IMPORTED_LOCATION "${LIBEVDEV_CUSTOM_LIBRARY}"
19+
INTERFACE_INCLUDE_DIRECTORIES "${LIBEVDEV_CUSTOM_INCLUDE_DIR}")
20+
else()
21+
find_package(PkgConfig REQUIRED)
22+
pkg_check_modules(LIBEVDEV REQUIRED IMPORTED_TARGET libevdev)
23+
endif()
1724
if(LIBVIRTUALHID_ENABLE_XTEST)
1825
find_package(X11 QUIET)
1926
endif()

src/platform/windows/control_protocol.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
namespace lvh::detail::windows {
2424

2525
inline constexpr std::string_view default_control_device_path {LVH_WINDOWS_CONTROL_DEVICE_PATH};
26+
inline constexpr std::string_view global_control_device_path {LVH_WINDOWS_GLOBAL_CONTROL_DEVICE_PATH};
2627

2728
inline std::uint32_t gamepad_flags(const GamepadProfileCapabilities &capabilities) {
2829
std::uint32_t flags = 0;

src/platform/windows/driver/libvirtualhid_umdf.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ EVT_VHF_ASYNC_OPERATION LvhEvtVhfWriteReport;
5353
namespace {
5454

5555
constexpr auto symbolic_link_name = L"\\DosDevices\\LibVirtualHid";
56+
constexpr auto global_symbolic_link_name = L"\\DosDevices\\Global\\LibVirtualHid";
5657

5758
struct DeviceRecord {
5859
std::mutex mutex;
@@ -472,12 +473,15 @@ NTSTATUS LvhEvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT device_init) {
472473
}
473474

474475
UNICODE_STRING symbolic_link;
475-
RtlInitUnicodeString(&symbolic_link, symbolic_link_name);
476+
RtlInitUnicodeString(&symbolic_link, global_symbolic_link_name);
476477
status = WdfDeviceCreateSymbolicLink(device, &symbolic_link);
477478
if (!NT_SUCCESS(status)) {
478479
return status;
479480
}
480481

482+
RtlInitUnicodeString(&symbolic_link, symbolic_link_name);
483+
static_cast<void>(WdfDeviceCreateSymbolicLink(device, &symbolic_link));
484+
481485
status = initialize_vhf_target(device);
482486
if (!NT_SUCCESS(status)) {
483487
return status;

src/platform/windows/shared/lvh_windows_protocol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
inline constexpr uint32_t LVH_WINDOWS_CONTROL_PROTOCOL_VERSION = 1u;
1212
inline constexpr char LVH_WINDOWS_CONTROL_DEVICE_PATH[] = R"(\\.\LibVirtualHid)";
13+
inline constexpr char LVH_WINDOWS_GLOBAL_CONTROL_DEVICE_PATH[] = R"(\\.\Global\LibVirtualHid)";
1314

1415
inline constexpr uint32_t LVH_WINDOWS_MAX_REPORT_DESCRIPTOR_SIZE = 1024u;
1516
inline constexpr uint32_t LVH_WINDOWS_MAX_INPUT_REPORT_SIZE = 256u;
@@ -161,6 +162,7 @@ enum {
161162
};
162163

163164
static const char LVH_WINDOWS_CONTROL_DEVICE_PATH[] = "\\\\.\\LibVirtualHid";
165+
static const char LVH_WINDOWS_GLOBAL_CONTROL_DEVICE_PATH[] = "\\\\.\\Global\\LibVirtualHid";
164166

165167
static const uint32_t LVH_WINDOWS_IOCTL_CREATE_GAMEPAD = 0x8000E000u;
166168
static const uint32_t LVH_WINDOWS_IOCTL_DESTROY_DEVICE = 0x8000E004u;

0 commit comments

Comments
 (0)