Bug: Global hotkey recorder captures the wrong key on non-US keyboard layouts (e.g. French AZERTY)

Environment

  • Vowen version: 0.5.1

  • OS: Windows 11

  • Keyboard layout: French (France) AZERTY

Description

When recording a shortcut in the "Enregistrer le raccourci" (Change Shortcut) dialog and pressing the physical key that produces ² on an AZERTY keyboard (top-left key, next to 1), the dialog does not display or capture ². Instead it shows/records something resembling a quote/accent character.

Steps to reproduce

  1. Switch Windows keyboard layout to French (France).

  2. Open Vowen → Settings → any hotkey (e.g. Hands Free) → "Change Shortcut".

  3. Press the physical key that types ² (top-left, next to 1).

Expected behavior

The recorder captures/displays ², and the resulting shortcut reliably triggers when that physical key is pressed later.

Actual behavior

The recorder shows a different glyph (looks like an isolated accent/quote mark), not ².

Root cause (confirmed at the Windows API level)

I traced this down to the OS keyboard layout mapping, not app logic at first, so this reproduces on any layout, not just AZERTY.

On the French layout, scan code 0x29 (the physical key that types ²) maps to Virtual Key 0xDE, verified via MapVirtualKeyEx(0x29, MAPVK_VSC_TO_VK_EX, hkl) on the active French HKL. 0xDE is VK_OEM_7, which is hardcoded in most keyboard libraries (including the bundled keyspy dependency — see WinGlobalKeyLookup[0xDE]) with the US-centric generic name "QUOTE", because on a US QWERTY layout VK_OEM_7 is the apostrophe key.

So the recorder is displaying the generic US name for the virtual-key slot, not the character actually produced by the physical key on the active layout. GetKeyNameTextW for the same scan code correctly resolves to ², confirming the OS itself knows the right label, it's the app's key-naming table that doesn't.

Suspected secondary issue (needs dev confirmation, not fully verified on my end)

The app appears to use two different keyboard libraries:

  • keyspy (VK-code based, layout-aware) for the shortcut recorder UI.

  • uiohook-napi (scan-code/physical-position based, layout-independent) for the actual background global-hotkey listener, based on strings like "Backquote" found in its keycode table for this same physical key.

If the recorder stores the keyspy-derived name (e.g. "OEM_7") but the runtime listener matches against uiohook-napi-derived names (e.g. "Backquote"), the two would never agree for this class of key on non-US layouts, meaning a shortcut recorded this way might silently never fire even after being "saved" successfully. I wasn't able to fully confirm which naming scheme the live matcher uses at trigger time, but it's worth checking given the mismatch is real between the two bundled libraries' naming tables for OEM/punctuation-row keys.

Suggested fix

  • Resolve the key's display name and the stored identifier from the same source used by the background listener, so recorder output always matches what the listener will actually see.

  • When storing an OEM/punctuation-row key, prefer the OS-resolved character (², `, etc., via GetKeyNameText or equivalent) over the hardcoded US-centric VK_OEM_* label, since that label is meaningless to a non-US user.

  • This isn't AZERTY-specific: any layout where OEM-row keys don't match the US layout (German QWERTZ, Spanish, etc.) likely hits the same mismatch, since the underlying cause is the fixed US-anchored VK_OEM_* naming table, not anything AZERTY-specific.

Please authenticate to join the conversation.

Upvoters
Status

In Review

Board
💡

Feature Request

Date

18 days ago

Author

Tqt

Subscribe to post

Get notified by email when there are changes.