Win32: VK_PACKET and VK_PROCESSKEY keydowns fire raw-key events for keys nobody pressed #182

Open
opened 2026-10-31 17:22:46 +00:00 by jorijnvdgraaf · 0 comments

Summary

The Win32 key handler (implementations/Crafter.Graphics-Window.cpp:181-201 at 852ae8a) builds the KeyCode from lParam's scan code and extended-key bit (KeyCodeFromLParam, :122) and never looks at wParam. Two kinds of WM_KEYDOWN carry no physical key, and both fire onRawKeyDown, onRawKeyHold and onRawKeyUp for whatever key their lParam happens to spell:

  • VK_PACKET: input sent with SendInput and KEYEVENTF_UNICODE, which auto-type tools use for characters the layout doesn't have. Its lParam high word is the UTF-16 unit, not a scan code. For 'œ' (U+0153), KeyCodeFromLParam gives 0x153, which is Key(CrafterKeys::Delete), so InputField_OnKey deletes the character after the caret before the WM_CHAR inserts 'œ'. '9' (U+0039) reads as Space, and ';' to 'D' as F1 to F10.
  • VK_PROCESSKEY: a key an input method is consuming. While the Japanese IME composes, Backspace still fires onRawKeyDown(Backspace) and erases a committed character from the field, and the Enter that confirms a conversion fires the application's Enter binding.

With the Unicode window class in claude/win32-title, the text that goes with these keys now arrives intact, which makes this the next problem for IME and injected input.

Found while reviewing claude/win32-title.

Suggested fix

Skip onRawKey* and heldKeys for wParam == VK_PACKET and wParam == VK_PROCESSKEY. The text still comes through WM_CHAR.

## Summary The Win32 key handler (`implementations/Crafter.Graphics-Window.cpp:181-201` at `852ae8a`) builds the `KeyCode` from `lParam`'s scan code and extended-key bit (`KeyCodeFromLParam`, `:122`) and never looks at `wParam`. Two kinds of `WM_KEYDOWN` carry no physical key, and both fire `onRawKeyDown`, `onRawKeyHold` and `onRawKeyUp` for whatever key their `lParam` happens to spell: - **`VK_PACKET`**: input sent with `SendInput` and `KEYEVENTF_UNICODE`, which auto-type tools use for characters the layout doesn't have. Its `lParam` high word is the UTF-16 unit, not a scan code. For 'œ' (U+0153), `KeyCodeFromLParam` gives `0x153`, which is `Key(CrafterKeys::Delete)`, so `InputField_OnKey` deletes the character after the caret before the `WM_CHAR` inserts 'œ'. '9' (U+0039) reads as Space, and ';' to 'D' as F1 to F10. - **`VK_PROCESSKEY`**: a key an input method is consuming. While the Japanese IME composes, Backspace still fires `onRawKeyDown(Backspace)` and erases a committed character from the field, and the Enter that confirms a conversion fires the application's Enter binding. With the Unicode window class in `claude/win32-title`, the text that goes with these keys now arrives intact, which makes this the next problem for IME and injected input. Found while reviewing `claude/win32-title`. ## Suggested fix Skip `onRawKey*` and `heldKeys` for `wParam == VK_PACKET` and `wParam == VK_PROCESSKEY`. The text still comes through `WM_CHAR`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Catcrafts/Crafter.Graphics#182
No description provided.