Diagnostic logs from a busy machine show the candidate host repainting far
more often than the input changes. Three separate causes, all in the UI
thread's handling of the worker's posted messages:
- A HideCandidate for one keystroke of a burst (backspacing the composition
empty, then typing again) cloaks a visible window and un-cloaks it ~30ms
later. Defer the hide on a short grace timer and drop it when a show
arrives first; a real commit or focus loss still hides, 50ms later.
- Each keystroke posts two WM_SHOW_MAIN_WINDOW messages, since the candidate
rebuild and the English/cloud merge both request a show. Skip a repaint
whose preedit, page, selection and caret match the frame already on screen
within a short window.
- When the worker stalls it drains several keystrokes at once and posts a
show for each. The handler always renders the newest published state, so an
older queued show can only repaint what the newer one is about to; drop it
when a newer show is already queued.
The stall itself (PrepareCandidateList blocking the shared task thread, worst
case 688ms against a median of 11ms) is untouched here.