Skip to content

Fix GlobalKeyMap matching for keyboards with layout-dependent charactersIgnoringModifiers#665

Open
TankTechnology wants to merge 2 commits into
gnachman:masterfrom
TankTechnology:fix/keystroke-layout-agnostic-fallback
Open

Fix GlobalKeyMap matching for keyboards with layout-dependent charactersIgnoringModifiers#665
TankTechnology wants to merge 2 commits into
gnachman:masterfrom
TankTechnology:fix/keystroke-layout-agnostic-fallback

Conversation

@TankTechnology
Copy link
Copy Markdown

Problem

On keyboards where the same physical key reports different charactersIgnoringModifiers values depending on input method state (e.g., Chinese/Pinyin layouts where the backtick key returns 0xb7 in one mode and 0x60 in another), GlobalKeyMap key bindings become unrecognized after text input.

Reproduction

  1. Configure a GlobalKeyMap binding for Cmd+` (recorded when the keyboard returns char=0xb7)
  2. Open iTerm2, create two split panes
  3. Type any character in one pane (changes input method state)
  4. Press Cmd+` — the binding is not recognized

The stored binding key is 0xb7-0x100000-0x32 but the keystroke from the event serializes as 0x60-0x100000-0x32 because charactersIgnoringModifiers now returns 0x60.

Root Cause

In iTermKeystroke.keyInBindingDictionary:, the normal-path fallbacks all depend on the character field (from charactersIgnoringModifiers):

  1. serialized0x<char>-0x<mods>-0x<keycode> ✗ (char differs)
  2. legacySerialized0x<char>-0x<mods> ✗ (char differs)
  3. modifiedSerialized:0x<modChar>:0x<mods> ✗ (char differs)
  4. Slow legacy iteration — same problem

None of these match when the character field varies.

Fix

Add a final fallback using portableSerialized (*-0x<mods>-0x<keycode>), which matches on virtual key code and modifiers only — the same strategy already used when LanguageAgnosticKeyBindings is enabled. This is a 15-line addition that only activates when all existing matching strategies have failed, so it's fully backward-compatible.

Commits

  1. Test reproduction — Python script that 1:1 mirrors the keyInBindingDictionary: matching logic, plus a Swift XCTest
  2. Fix — Add portableSerialized fallback in the normal matching path

Testing

Run tests/reproduce_keystroke_bug.py to verify the fix:

$ python3 tests/reproduce_keystroke_bug.py
BEFORE fix: NO MATCH — BUG REPRODUCED
AFTER  fix: MATCH

The Swift XCTest in ModernTests/iTermKeystrokeBindingTests.swift covers the same scenarios and can be run with Xcode.

🤖 Generated with Claude Code

TankTechnology and others added 2 commits May 13, 2026 17:37
Demonstrates that GlobalKeyMap key binding matching breaks when
charactersIgnoringModifiers differs from the character stored in
the plist binding. On Chinese/Pinyin keyboards the backtick key
(`) returns different charactersIgnoringModifiers values depending
on input method state (0xb7 vs 0x60), causing Cmd+` to become
unrecognized after text input.

The test mirrors iTermKeystroke.keyInBindingDictionary: logic and
shows a portableSerialized fallback resolves the mismatch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a keystroke's charactersIgnoringModifiers differs from
what was recorded in the binding (common on Chinese/Pinyin
keyboards where the backtick key returns different characters
in different input modes), existing fallbacks fail because they
all depend on the character field.

Add a portableSerialized (*-modifiers-keycode) fallback that
matches on virtual key code and modifiers only, ignoring the
layout-dependent character. This is the same matching strategy
used when LanguageAgnosticKeyBindings is enabled, but applied
as a last-resort fallback in the normal path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant