feat(kvm): layout-aware desktop key protocol #127
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/VA-126-layout-aware-desktop-keys"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Replaces the layout-fragile Windows-virtual-key desktop key frame with a layout-aware key contract so the number row, shifted digits, and punctuation reproduce from a macOS or other non-US client. This is the agent side of VA-126; VAPP-78 is the client (SPA) side.
The old frame (
[0,1,0,6, action, vk]) carried only a deprecatedevent.keyCodebyte, whose Windows OEM codes are layout-dependent, and no modifier state, so the agent had to reconstruct shifted characters from a separately-received Shift event. That could not work for the number row /-_ =+/ punctuation off a Mac.Wire contract (Vervain owns this number space; native extensions at 200+)
MNG_KVM_KEY_LAYOUT = 200, big-endian:[u16 cmd][u16 size][u8 action][u8 mods][u8 layout_id][u8 code_len][u8 key_len][code UTF-8][key UTF-8].action0=down/1=up;modsbit0 Shift / bit1 Ctrl / bit2 Alt / bit3 Meta;code=event.code;key=event.key;layout_idselects a fallback layout table whenkeyis empty.MNG_KVM_CLIENT_HELLO = 201negotiates the desktop protocol version (DESKTOP_PROTOCOL_VERSION = 2): the agent replies with its version and logs a mismatch loudly. Vervain is not in production, so no legacy path is kept: the retired VK frame (cmd 1) is rejected loudly rather than silently mis-injected.Resolution (
vervain-agent-kvm::keymap)Every backend consumes one canonical form, an X11 keysym. Resolution trusts
event.keyfor printable input (the client already applied its own layout), maps named / function keys directly, and falls back to a per-layout physical-code table otherwise. The US and Mac-US tables (Mac US ANSI matches US across the affected keys, so it is fully knowable without a physical Mac) are the deterministic authority the unit tests assert against. Modifier state comes from the bitmask, not separate Shift events: Shift is folded into a printable keysym and synthesized only for named-key chords (e.g. Shift+ArrowLeft); a standalone modifier frame resolves to nothing.Injection
A single serialized injector drains the keysym transitions in order, so modifier-then-key and down-then-up ordering hold regardless of backend round-trip timing (the old fire-and-forget-per-frame path could not guarantee this).
inject_keysymselects uinput (evdev scancode + shift level viakeysym_to_evdev), the RemoteDesktop portal (keysym direct), X11 XTEST, or the Windows SendInput path. The X11 backend now presses Shift for a shifted-level keysym (a keysym is an absolute symbol, so@must hold Shift, not tap the2keycode); this also corrects the VNC-native and MCP key paths.Completeness sweep
Invariant: every desktop key-injection backend reproduces the intended keystroke, including the Shift level of a shifted printable keysym.
uinput::key_keysymportal::keyboard_keysyminput.rs linux::key_eventwindows/input.rs key_eventkvm_input_keydispatch.rscmd 1handle_desktop_inputAcceptance criteria
event.code+event.key+ modifier frame.-/_,=/+, and other punctuation reproduce (unit-tested against the Mac-US layout, no physical Mac needed).Tests
12 new
keymapunit tests (Mac number row, punctuation, layout fallback, named/function keys, standalone-modifier suppression, printable-shift non-resynthesis, named-key shift synthesis, Ctrl shortcut hold, release ordering, keysym->evdev, keysym->name). Fulljust checkand the pre-commit clean Docker CI build are green.#VA-126