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>
58 KiB
58 KiB