feat(hid): add optional vendor-defined selector key (vkey)#3390
Open
akira-toriyama wants to merge 1 commit into
Open
feat(hid): add optional vendor-defined selector key (vkey)#3390akira-toriyama wants to merge 1 commit into
akira-toriyama wants to merge 1 commit into
Conversation
Add CONFIG_ZMK_HID_VKEY (default n). When enabled, ZMK gains an independent vendor-defined HID top-level collection that reports a single 1-byte selector (the "vkey" id, 1-255; 0 = released) on a configurable vendor usage page (CONFIG_ZMK_HID_VKEY_USAGE_PAGE, default 0xFF31) and report ID (CONFIG_ZMK_HID_VKEY_REPORT_ID, default 0x20), plus a &vkey <id> behavior that emits it on press and clears it on release. When disabled (the default) the report descriptor, report state, send path and behavior are all compiled out, so the firmware is byte-identical to a build without the option. Why --- Some setups want to send "original" key codes that are guaranteed never to collide with any standard keyboard/consumer/mouse usage, and have a host-side agent map them to actions (launch apps, window management, IME toggles, and so on). A vendor-defined selector report is the HID-correct way to carry such codes: the host sees a distinct vendor collection it can match exclusively, and normal typing is untouched because the codes live on a vendor page no application reads. How --- - hid.h: under CONFIG_ZMK_HID_VKEY, append an independent Application collection (one 8-bit Data/Var/Abs field, logical 0-255) after the standard collections. The 16-bit vendor usage page is written as a raw long item (0x06, lo, hi) split from the Kconfig value at compile time, since HID_USAGE_PAGE() emits a 1-byte item. - hid.c: a struct zmk_hid_vkey_report plus zmk_hid_vkey_set/clear/get. - usb_hid.c / endpoints.c: zmk_usb_hid_send_vkey_report and zmk_endpoint_send_vkey_report mirroring the consumer report path, plus a GET_REPORT case and a clear+resend on endpoint switch so a held vkey cannot get stranded on the host. - behavior_vkey.c: a one-parameter behavior that runs on the central and sets/sends the selector directly. BLE HOG send is out of scope for this PR (USB transport only); the vendor collection still appears in the HOG report map because the descriptor is shared, so adding a HOG send path later is additive.
This was referenced Jun 18, 2026
akira-toriyama
added a commit
to akira-toriyama/canon
that referenced
this pull request
Jun 18, 2026
vkey の汎用化 upstream PR を zmkfirmware/zmk#3390 として提出(案A=USB-only、 default-off CONFIG_ZMK_HID_VKEY、build-verify 済み・ZMK CI 同一 clang-format 整形)。 canon 側 docs を実態へ更新: - docs/vkey-roadmap.md: 「Upstream 収束」表と残タスク #2 を「#3390 提出済み」へ。 - patches/zmk/README.md: vkey の upstream PR 欄を「未提出」→ #3390。 - docs/vkey-upstream-pr-draft.md(新規): 汎用化設計 + PR 本文 + ビルド検証結果 + 移行手順。 - docs/vkey-upstream-pr-draft.patch(新規): #3390 として提出した一般化 diff(バニラ ZMK 適用可)。 方針: マージは難航しうるため patches/zmk/vkey-report.patch は canon に維持(canon は upstream 非依存)。#3390 が実際に merge された時のみ patch 撤去 → 案1 巻き戻しを実行。 Closes #61 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Add
CONFIG_ZMK_HID_VKEY(defaultn). When enabled, ZMK gains:1-byte selector — the "vkey id" (
1–255;0= released) — on aconfigurable vendor usage page (
CONFIG_ZMK_HID_VKEY_USAGE_PAGE, default0xFF31) and report ID (CONFIG_ZMK_HID_VKEY_REPORT_ID, default0x20);&vkey <id>behavior that sets the selector to<id>and sends the reporton press, and sets it to
0and sends on release.When disabled (the default) the report descriptor, report state, send path and
behavior are all compiled out — the firmware is byte-identical to a build
without the option.
Why
Some setups want to send "original" key codes that are guaranteed never to
collide with any standard keyboard/consumer/mouse usage, and have a host-side
agent map them to actions (launch apps, window management, IME toggles, …).
Today that needs either reusing function-row/consumer codes (which collide with
real shortcuts) or an out-of-tree patch. A vendor-defined selector report is the
HID-correct way to carry such codes: the host sees a distinct vendor collection
it can match exclusively, and normal typing is untouched because the codes live
on a vendor page no application reads.
How
hid.h): under#if IS_ENABLED(CONFIG_ZMK_HID_VKEY), appendan independent Application collection (Report ID =
CONFIG_ZMK_HID_VKEY_REPORT_ID,one 8-bit Data/Var/Abs field, logical 0–255) after the standard collections.
The 16-bit vendor usage page is written as a raw long item (
0x06, lo, hi)because
HID_USAGE_PAGE()emits a 1-byte item;lo/hiare split from theKconfig value at compile time.
hid.c):struct zmk_hid_vkey_reportpluszmk_hid_vkey_set/clear/get, all gated.usb_hid.c,endpoints.c):zmk_usb_hid_send_vkey_reportandzmk_endpoint_send_vkey_report, mirroring the consumer report path, plus aGET_REPORTcase for the new report ID.zmk_endpoint_clear_reportsalsoclears + resends the selector on endpoint switch, so a held vkey can't get
stranded on the host.
behavior_vkey.c+ DT binding): a one-parameter behavior thatruns on the central (where HID state and the USB endpoint live) and directly
sets/sends the selector — it does not raise a keycode event, since the vendor
selector isn't a KEY/CONSUMER usage.
BLE HOG send is out of scope for this PR (USB transport only). The vendor
collection still appears in the HOG report map because the descriptor is shared,
so adding a HOG send path later is purely additive.
Testing
central) with
CONFIG_ZMK_HID_VKEY=y; clang-format (pre-commit) and the buildare clean.
expected bytes (Report ID
0x20, usage page0xFF31, one 8-bit selector) inthe dongle
.elf.&vkey <id>press/release emits a 2-byte report
[report_id, id]/[report_id, 0]overUSB, read on macOS via
IOHIDManagerand mapped to actions; normal typing isunaffected.
CONFIG_ZMK_HID_VKEY=n(the default) every vkey addition sits behind#if IS_ENABLED(CONFIG_ZMK_HID_VKEY), so the descriptor and symbols areunchanged.
Open questions / feedback welcome
vkey/&vkey/CONFIG_ZMK_HID_VKEY— open to a different name(e.g. vendor-key / raw-hid) if preferred.
with a follow-up acceptable?
simultaneous ids could be a later, backward-compatible extension.