Files
orca/src
Neil d4460d34f3 fix(terminal): stop a Hangul-terminating digit being eaten as a candidate pick (#15429)
* fix(terminal): stop a Hangul-terminating digit being eaten as a candidate pick

A digit typed immediately after a Hangul syllable is dropped in the
terminal on Wayland. Typing 아1 produces 아. The syllable composes and
commits correctly; only the keystroke that ends it is lost.

Reproduced on Ubuntu 24.04, GNOME Shell 46, ibus-hangul 1.5.5, in a
nested Wayland session with real key injection. The pty receives 아 on
Wayland and 아1 under X11 on the same machine with the same engine, and
a GTK client in the same Wayland session receives 아1 - so the
compositor, the input method and the digit are all behaving.

The difference is where the digit is delivered. Under X11 ibus-hangul
swallows it into the preedit and commits 아1 as composition text. Under
Wayland it commits 아 and lets the digit through as an ordinary key -
and the jamo before it arrive with no keydown at all, only keyups.

That orphaned keyup is what breaks it. A plain letter keyup with no
matching keydown arms a 1500ms window in which a bare digit is treated
as a candidate selection, because a Pinyin engine indexes its candidate
list by digit. The digit lands microseconds later, inside the window,
and is suppressed. The window disarms after exactly one digit, which is
why the syllable survives and only the terminating key vanishes.

A Hangul engine has no numbered candidates over its preedit - a digit
ends the syllable and is literal text - so the guard was spending its
one suppression on a keystroke meant for the shell. The tracker now
records whether the current preedit is Hangul and the guard declines.

Read from compositionupdate rather than compositionend: a Pinyin preedit
is the Latin spelling being narrowed while its commit is the Han text,
so reading the commit would misclassify Pinyin and reopen the bugs the
guard was added for. Space is untouched; only digits are reclassified.

Verified A/B/A on that machine: unfixed drops the digit, this change
preserves it, reverting drops it again, three runs each. The X11 case
and both existing ibus-hangul specs still pass.

Closes #15299

* fix(terminal): expire the Hangul preedit flag and narrow its digit exemption

Review of the #15299 fix found the Hangul classification could not be
retired and that it exempted more than the reported bug.

The flag was written only on a non-empty compositionupdate and cleared
only on blur, so it latched for the whole focus session. Switching input
engine (Hangul -> Pinyin) under fcitx/ibus moves no DOM focus, and the
#8241 orphan-digit path emits no composition and no input events, so
nothing could refresh or clear it. The stale flag then turned the
#8241/#7543 Pinyin candidate-digit guard off silently and permanently -
the regression those guards exist to prevent. It now expires against
lastCompositionEventAt on the same staleness window as the neighbouring
guards, and compositionstart clears it because the following
compositionupdate re-reads the preedit script.

It is deliberately not cleared on compositionend or on input: the bug is
a digit arriving after the commit, and both recordings deliver the
commit's own insertText before it. Clearing on either reverts the fix -
the orphan-window test fails when input clears it.

The exemption also gated suppressCandidateKey as a whole, so it fired
during a live composition as well. That is broader than the bug, which
is exclusively a digit after compositionend, and it is unsafe: the
earlier claim that no Hangul engine indexes candidates by digit over a
preedit is wrong. ibus-hangul's Hanja conversion (Hanja key / F9) puts a
numbered lookup table over a live Hangul preedit, and its symbol table
behaves the same. Only the orphan-keyup guard - which arms off a bare
keyup and cannot see which engine produced it - now declines.

The tests drove a shape neither recording produces: an empty
compositionupdate immediately before compositionend, which armed the
250ms post-composition window. The Wayland reproduction has no empty
update, and the X11 fixture follows its empty update with
deleteContentBackward + insertText, which disarms that window. The suite
now follows the recorded trace, asserts the 250ms window never arms, and
covers both the expiry and the live-preedit Hanja case.

Refs #15299
2026-08-18 23:15:12 -07:00
..