takelost's own comment says the context the engine was taken from "gets
that text back when it is next heard from", but it was wired to the
Keyrelease and Keyreset arms only. The Keypress arm opens with
sclear(&lost), so the one way a context is most obviously heard from --
somebody typing into it -- is the way that threw its text away.
Compose in A, type in B before A's focus-out arrives, then go back to A
and type: A's reading is gone rather than handed back. That interleave is
not exotic here, it is the premise takelost exists for. Focus-out can
arrive after the next context's keys, which is why the engine changes
hands with text still pending; and a context whose focus-out is still in
flight can be typed into again just as easily as it can be reset.
engine_test.c: the taken owner types and gets its reading back:
want "か", got ""
The call has to go above the owner switch, because that switch's own
sclear(&lost) is what destroys the text -- putting it after the switch
instead fails the same line. Above it, the order also comes out right:
the recovered reading is appended to the commit first and whatever the key
itself commits follows it.
What this does not cover: there is one lost slot. A third context taking
over between the two still destroys the first one's text for good, and
widening that means an array where a single Str is now -- a new structure
for an interleave that needs three contexts and no focus-out from any of
them. Left as it is.
ibus_client_smoke was pinning the defect rather than merely missing it: it
required exactly three commits on context a, and its handler rejected any
commit text but か, so the recovered reading tripped both. Bumping the
count would have been a constant with nothing behind it, so the take-back
got a stage of its own and the text is asserted -- the official libibus
client now watches the whole thing, and without the fix reports
retook=0
One assertion was written and then deleted. takelost now runs on every
key rather than only on the two lifecycle ops, so re-committing the same
text on every keystroke looked like the hazard worth pinning, and "the
reading comes back once" went in beside the others. Making takelost skip
its lostowner = nil left all 90 passing: the owner switch reassigns
lostowner to the context it just displaced, so lostowner is never the
owner of the next key, and the hazard is unreachable. An assertion that
cannot fail is decoration, so it is not in the tree.
90 unit, check-live, check-stress and valgrind clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Clicking elsewhere, changing focus, or any client reset dropped the
composition in the GTK module and IBus (only XIM ResetIC handed it
back), so typing 안녕 and clicking Send lost 녕. The engine now returns
the pending text — a moved-to candidate first, as Enter would — on
Keyreset and Keyrelease; the GTK module asks for it before closing on
focus-out and commits it, IBus commits it on FocusOut, Reset and a
switch to a password field, and XIM commits it on focus loss and hands
it back on ResetIC without a separate capability probe.