Skip to content

fix(input): debounce selectMode to eliminate keystroke dispatch stall#5

Merged
hiking90 merged 2 commits into
fix/focus-steal-correctorfrom
fix/select-mode-stall
May 15, 2026
Merged

fix(input): debounce selectMode to eliminate keystroke dispatch stall#5
hiking90 merged 2 commits into
fix/focus-steal-correctorfrom
fix/select-mode-stall

Conversation

@hiking90
Copy link
Copy Markdown
Owner

@hiking90 hiking90 commented May 14, 2026

Summary

한↔영 토글 직후 첫 1–4 keystroke가 100–350ms 지연됐다가 한꺼번에 dispatch되는 문제 수정. 영문/한글이 별도 input source로 등록되어 토글마다 client.selectMode()가 호출되는데, selectMode 직후 macOS IMK가 keystroke dispatch를 일시 stall한다. selectMode 호출을 keystroke idle 0.6s 디바운스하여 우회.

System Settings 텍스트 대치 입력창처럼 IME activate/메뉴 갱신이 반복되는 화면에서는 매 activate마다 selectMode → 연속 stall로 단축키 한/영 전환이 아예 안 먹는 것처럼 보였다 (이슈 #7).

Cause

토글 직후 CGEventTap→IMK dispatch gap 측정: 모드 안정 시 ~5–10ms, 토글 직후 첫 키 ~313–354ms (이후 키 순차 감소, 4–5번째부터 정상화). 한→영/영→한 대칭. focus-steal correction과 무관한 별개 원인.

Solution

selectMode를 마지막 keyDown으로부터 selectModeIdleInterval(0.6s) 동안 추가 입력이 없을 때만 호출하도록 디바운스.

메서드 호출 시점
scheduleSelectMode(_:) applyEffect(modeChanged) · performEnglishLockToggleFromTap(English Lock) — 보류 시작
bumpSelectModeIdle() handle keyDown 진입마다 타이머 리셋
fireSelectMode() idle 만료 시 실제 client.selectMode() 실행
flushSelectMode() deactivateServer — 입력 끝, 즉시 호출
cancelSelectMode() activateServer · setValue · focus-steal 아이콘 동기화 — stale 폐기

타이핑 중에는 selectMode 미발동(stall 없음), 입력이 멈춘 뒤에야 메뉴바 아이콘 동기화.

Trade-off: 메뉴바 한/영 아이콘 동기화가 타이핑 멈춘 후 최대 0.6s 지연. (0.3s는 자연스러운 타이핑 멈춤에 만료되어 stall 재노출 → 0.6s 상향, 1.0s는 과해 절충.)

Test plan

  • 한↔영 토글 직후 첫 키가 stall 없이 즉시 표시 (양방향)
  • 입력 멈춤 0.6s 후 메뉴바 아이콘 정상 동기화
  • 앱 전환 시 보류된 selectMode flush, focus-steal 시 stale 폐기
  • English Lock 토글에도 디바운스 적용
  • FocusStealCorrector regression suite 통과

Notes

🤖 Generated with Claude Code

hiking90 and others added 2 commits May 15, 2026 00:01
영문/한글이 별도 input source로 등록되어 있어 한↔영 토글 시마다
client.selectMode()가 호출된다. 측정 결과 selectMode 호출 직후 macOS
IMK가 keystroke dispatch를 100–350ms 동안 stall하여, 그 사이 누른 키
1–4개가 큐잉됐다 한꺼번에 controller에 도달한다. 사용자에게는 "첫 키들이
안 보이다가 한꺼번에 나타나는" 시각적 지연으로 인지된다.

CGEventTap recorded → IMK handle gap 측정:
  모드 안정 (typing 중)        : ~5–10ms
  토글 직후 첫 키              : ~313–354ms
  토글 직후 두번째 키          : ~239–251ms
  토글 직후 4–5번째 키 이후    : ~5–10ms (정상화)

selectMode 호출을 마지막 keyDown으로부터 selectModeIdleInterval(300ms)
동안 추가 입력이 없을 때 호출하도록 디바운스한다.

- scheduleSelectMode(_:) — 토글 시 보류 시작
- bumpSelectModeIdle()   — keyDown마다 타이머 리셋
- flushSelectMode()      — deactivate 시 즉시 호출
- cancelSelectMode()     — activate/setValue/focus-steal 시 stale 폐기

사용자가 타이핑 중인 동안에는 selectMode가 호출되지 않아 stall이 발생하지
않고, 입력이 멈춘 후에야 메뉴바 아이콘이 동기화된다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0.3s expires during natural typing pauses (breath between words,
thinking gaps), letting selectMode fire mid-input and re-exposing
the dispatch stall. 0.6s keeps selectMode deferred through
continuous typing while keeping menu bar icon sync reasonably
prompt.

Trade-off: menu bar Hangul/English icon sync is delayed up to
0.6s after typing stops.
@hiking90 hiking90 force-pushed the fix/select-mode-stall branch from 3ee20eb to 9f113fc Compare May 15, 2026 13:37
@hiking90 hiking90 merged commit edb04f5 into fix/focus-steal-corrector May 15, 2026
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