Skip to content

fix: switch input source via TIS instead of posting Eisu/Kana key events#115

Merged
kazukinakai merged 1 commit into
mainfrom
fix/tis-input-source-switch
Jul 3, 2026
Merged

fix: switch input source via TIS instead of posting Eisu/Kana key events#115
kazukinakai merged 1 commit into
mainfrom
fix/tis-input-source-switch

Conversation

@kazukinakai

Copy link
Copy Markdown
Member

Problem

In apps with a custom text engine (e.g. Affinity Designer/Photo text editing), pressing a Command key inserted a half-width space instead of switching the IME.

Root cause: lone-Cmd switching posts synthesized Eisu (102) / Kana (104) key events via CGEvent.post. Apps that don't route key events through the standard macOS input context never trigger the IME switch and instead interpret the injected key as a literal character (space).

Fix

When a remap output is Eisu (102) or Kana (104), switch the input source directly via the TIS API (TISSelectInputSource) and swallow the event, instead of posting a synthesized key event:

  • Eisu → TISCopyCurrentASCIICapableKeyboardInputSource()
  • Kana → enabled input source with language "ja", preferring Hiragana (com.apple.inputmethod.Japanese); no-op with NSLog if none found

Applied uniformly across all remap paths (modifierKeyUp, keyDown/keyUp, media keys). All other mappings unchanged.

Verification

  • swift build — clean
  • swift test — 33/33 pass (new: swallow-on-102/104 tests, shouldSwitchViaTIS unit tests)
  • swiftlint — 0 errors (8 pre-existing warnings unchanged in category)

Version bumped to 2.4.7 (triggers release on merge).

Apps with a custom text engine that bypasses the standard macOS input
context (e.g. Affinity) don't treat a synthesized Eisu (102) / Kana
(104) CGEvent as an IME switch — it lands as a literal half-width
space instead. Switch the input source directly through the TIS API
for these two outputs, for every app, instead of posting a synthesized
key event.

- Add InputSourceSwitcher: shouldSwitchViaTIS() is a pure, testable
  decision function; switchInputSource() does the actual
  TISCopyCurrentASCIICapableKeyboardInputSource / TISCreateInputSourceList
  + TISSelectInputSource calls (Japanese source preferring the
  standard Hiragana mode, silent no-op + NSLog if none is found).
- KeyEvent.modifierKeyUp, keyDown/keyUp remap path, and mediaKeyDown
  remap path all route Eisu/Kana outputs through the new switcher and
  swallow the event instead of posting it; keyUp swallows silently to
  avoid switching twice.
- Add tests for the swallow behavior and the decision function.
@kazukinakai kazukinakai enabled auto-merge July 3, 2026 12:22
@kazukinakai kazukinakai merged commit 67d6bbc into main Jul 3, 2026
2 checks passed
@kazukinakai kazukinakai deleted the fix/tis-input-source-switch branch July 3, 2026 12:24
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