Skip to content

Update VirtualHIDDevice client to v8 - #20

Merged
psych3r merged 1 commit into
psych3r:mainfrom
malpern:codex/virtualhid-v8
Jul 12, 2026
Merged

Update VirtualHIDDevice client to v8#20
psych3r merged 1 commit into
psych3r:mainfrom
malpern:codex/virtualhid-v8

Conversation

@malpern

@malpern malpern commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • update the bundled VirtualHIDDevice client from the v6.2-era implementation to the upstream v8.0.0 tag
  • compile the wrapper as C++23, as required by v8's Unix-domain stream transport
  • explicitly include the pqrs IOKit type used by driver_activated()
  • add a compile-time guard for client protocol 7
  • document the intentional protocol-5 compatibility break
  • exclude historical VirtualHID installer packages from the published crate

This prepares the crate for a separate Kanata dependency update. The migration was developed and runtime-tested against current upstream Kanata, without relying on an application-specific fork.

Why

Karabiner-Elements 16.1 installs VirtualHIDDevice v8, while the current crate is built for the v6.2/protocol-5 client. Current Kanata builds using this crate therefore cannot communicate with the v8 daemon and repeatedly report connect_failed asio.system:2.

VirtualHIDDevice v7 changed IPC from local datagrams to Unix-domain streams, and v8 moved the client protocol to 7. Supporting both generations at runtime would require retaining two transports and client implementations. This PR intentionally targets v8 rather than adding that complexity.

Implementation notes

The v8 update required two wrapper changes beyond the submodule bump:

  1. Its Unix-domain stream implementation uses C++23 APIs such as std::to_underlying.
  2. virtual_hid_device_service.hpp no longer transitively includes pqrs::osx::iokit_mach_port.

The Rust-facing readiness, input release/re-grab, keyboard report, and pointing report APIs remain unchanged because v8 retains the client signals used by the wrapper.

Build and package validation

Using upstream psych3r/driverkit main at ff62645 as the base:

  • cargo build
  • cargo test
  • stable Xcode 26.6: cargo build --all-targets
  • stable Xcode 26.6: cargo test
  • stable Xcode 26.6: release build of all examples
  • cargo package --allow-dirty, including rebuilding from the packaged crate
  • confirmed the packaged crate contains the protocol-7 headers and Unix-domain stream vendor source
  • excluded the 89 MB dist/ installer archive; the resulting crate is approximately 6.7 MB compressed

The repository currently has no automated runtime tests, so cargo test contains zero unit tests.

Runtime validation

Validated in a disposable macOS 15.7.7 ARM VM with:

  • VirtualHIDDevice v8.0.0 installed and activated enabled
  • upstream Kanata main at c7978d4
  • Kanata built directly against this branch

The following scenarios passed:

  • startup with the daemon already running
  • startup before the daemon becomes available
  • keyboard and pointing-device readiness
  • keyboard press and release reports (send_key returned success)
  • pointing motion and button reports
  • daemon termination while Kanata had seized input
  • input release while virtual output was unavailable
  • daemon restart and in-process recovery
  • three consecutive disconnect/reconnect cycles
  • clean shutdown
  • a new client connecting, seizing input, and sending reports after the previous client exited

During daemon loss, Kanata remained running, marked keyboard and pointing output unavailable, retried the connection, and restored both outputs after the daemon restarted.

Multiple-client validation

I repeated the same two-client scenario in separate disposable macOS 15.7.7 VMs using current upstream Kanata:

  • VirtualHIDDevice v6.2 with the published driverkit crate
  • VirtualHIDDevice v8 with this branch

In both environments:

  • two Kanata processes connected to the daemon
  • both virtual keyboard and pointing clients reached readiness
  • both processes remained alive when the daemon stopped
  • both recovered in-process after the daemon restarted

Both processes selected the VM's single available keyboard, so one received the expected macOS exclusive access and device already open error. Which process received it depended on startup order. This behavior was identical with v6 and v8 and is separate from VirtualHID output-client multiplexing.

This comparison found no multi-client regression from the v6 datagram transport to the v8 Unix-domain stream transport.

Compatibility

This is an intentional compatibility cut:

  • supported daemon: VirtualHIDDevice v8 / client protocol 7
  • no compatibility with the older v6 / protocol-5 daemon
  • no dual-protocol transport layer added

Maintainer decisions

Based on maintainer feedback:

  • the next release will intentionally target protocol 7 / VirtualHIDDevice v8
  • 0.4.0 is the appropriate version for the compatibility break
  • the submodule bump, C++23/include changes, and package exclusion should remain together in one commit

The implementation and isolated runtime matrix are complete.

@psych3r

psych3r commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Hi @malpern, Thanks again for contributing to driverkit!

  1. Yes, that's reasonable to me.
  2. Also yes, 0.4.0 is appropriate.
  3. Yes, lets keep them in a single commit.

@psych3r

psych3r commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Please also update the compilation command commented above the main function in driverkit.cpp to use c++23
// g++ c_src/driverkit.cpp -DBUILD_AS_BINARY -Ic_src/Karabiner-DriverKit-VirtualHIDDevice/include/pqrs/karabiner/driverkit -Ic_src/Karabiner-DriverKit-VirtualHIDDevice/src/Client/vendor/include -Ic_src/Karabiner-DriverKit-VirtualHIDDevice/vendor/vendor/include -std=c++2b -framework IOKit -framework CoreFoundation -o driverkit -g -O0

@psych3r

psych3r commented Jul 12, 2026

Copy link
Copy Markdown
Owner

And btw, I installed v8 and did minimal testing.
My kanata config works with these changes.
I didn't cover all the scenarios in your test plan though...

Co-Authored-By: Codex <noreply@openai.com>
@malpern
malpern force-pushed the codex/virtualhid-v8 branch from 6d00e59 to 4b2bded Compare July 12, 2026 16:40
@malpern

malpern commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — I’ve incorporated the requested -std=c++2b update into the existing single commit.

I also completed the broader runtime matrix in a disposable macOS 15.7.7 ARM VM against current upstream Kanata. Startup before and after daemon availability, keyboard and pointing readiness/report output, daemon loss while input was seized, input release while output was unavailable, in-process recovery, three repeated reconnects, and clean shutdown all passed.

I updated the PR description to record the agreed protocol-7/v8 compatibility cut, 0.4.0 release direction, single-commit scope, and runtime results. Local cargo build --all-targets and cargo test pass on the amended commit.

@malpern

malpern commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Correction to the multiple-client note: the VM run did not demonstrate a v8 single-client limitation.

The second Kanata process connected to the v8 daemon and reached keyboard and pointing output readiness. Its IOHIDDeviceOpen ... exclusive access and device already open message came from both processes using the same catch-all input configuration and attempting to seize the same physical device. That is expected macOS HID ownership behavior, not a failure of daemon multiplexing.

Both the v6 and v8 daemon sources maintain per-client entries. I have corrected the PR description and will keep disjoint-device multi-instance behavior as a focused regression scenario rather than describing simultaneous clients as unsupported.

@malpern

malpern commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Completed the controlled v6/v8 comparison described above. Both versions supported two simultaneous Kanata output clients and recovered both clients after daemon restart. The same-device input-ownership warning was identical across versions, so it is not introduced by this migration. I’ve updated the PR description with the result.

@malpern
malpern marked this pull request as ready for review July 12, 2026 19:02
@psych3r
psych3r merged commit af8159c into psych3r:main Jul 12, 2026
@psych3r

psych3r commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Thanks a lot for your effort, it's really appreciated :)

@malpern
malpern deleted the codex/virtualhid-v8 branch July 13, 2026 02:47
jtroo pushed a commit to jtroo/kanata that referenced this pull request Jul 28, 2026
## Summary

- update `karabiner-driverkit` from 0.3.x to 0.4.0
- update the lockfile to the published 0.4.0 crate
- document VirtualHIDDevice v8.0.0 as the supported macOS driver
- document the protocol-7 compatibility cut from protocol-5/v6.2.0

## Why

Karabiner-Elements 16.1 uses VirtualHIDDevice v8. Kanata currently
depends on the protocol-5 driverkit client and documents
VirtualHIDDevice v6.2.0, so it cannot communicate with the current v8
daemon.

`karabiner-driverkit` 0.4.0 adds the protocol-7/v8 client support. This
intentionally updates Kanata to v8 rather than maintaining two IPC
implementations. Users of the standalone v6.2.0 driver must upgrade the
driver and Kanata together.

Closes #2105.

## Runtime validation

The driverkit migration was validated in a disposable macOS 15.7.7 ARM
VM during psych3r/driverkit#20. The validated scenarios included:

- startup before and after daemon availability
- keyboard and pointing output readiness
- daemon loss and in-process recovery
- repeated reconnects
- clean shutdown
- two simultaneous output clients and recovery after daemon restart

The controlled v6/v8 comparison found the same same-device
exclusive-input behavior in both versions; it was not introduced by the
v8 migration.

## Local checks

- `cargo fmt --all --check`
- `RUSTFLAGS="-Dwarnings" cargo build --all-targets`
- `RUSTFLAGS="-Dwarnings" cargo test --all`
- `RUSTFLAGS="-Dwarnings" cargo clippy --all -- -D warnings`
- `RUSTFLAGS="-Dwarnings" cargo test --all --features=cmd`
- `RUSTFLAGS="-Dwarnings" cargo clippy --all --features=cmd -- -D
warnings`
- `RUSTFLAGS="-Dwarnings" cargo build --release`
- `RUSTFLAGS="-Dwarnings" cargo build --release --features=cmd`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants