text-input: a done the compositor sent before our disable is applied as current (the serial is ignored) #188
Labels
No labels
claude:blocked
claude:done
claude:failed
claude:in-progress
claude:ready
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Catcrafts/Crafter.Graphics#188
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Device::text_input_doneapplies everydonetotextInputWindowas if it were current. The protocol's serial — the number ofcommitrequests the client has sent, which the compositor echoes indone— is never compared, and a disable (SetTextInput(false)) clearsshownand the pending flags but nottextInputWindow(only the leave does). So adonethe compositor relayed before it processed our disable + commit — already in the socket when the event that made us disable (a tap, a key) was dispatched — reaches the application as a live edit.How it bites a consumer (imsd-messages' pre-push review, 2026-10-31): the message field commits the composing text itself (
ComposeCommitPreedit), disables the text input, and moves the cursor to the tap. The stalecommit_string("wor ")+donethen lands throughonTextInputat the new cursor:hello worbecomeswor hello wor, the word twice and the space misplaced. It needs two thumbs (an on-screen-keyboard key and a tap on the field in one dispatch) or a click while typing; the damage is draft text, visible before sending. imsd-messages ships 1.0 with it waived (2026-11-01) and this issue as the follow-up.Where (at
6f471ca; unchanged by 44c913f):implementations/Crafter.Graphics-Device.cpp—text_input_done(:423–437) invokes:455–464) clearsonTextInput/onPreeditfortextInputWindowwithout looking atserial; the disable path (shownand the pending flags only; the leave (~:391–401) is the one placetextInputWindowis cleared.Protocol: text-input-unstable-v3 —
done'sserialis the count ofcommitrequests; adonewhose serial differs from the client's count "must proceed with evaluating and applying the changes as normal" (lib/text-input-unstable-v3-client-protocol.h~:543–546), its first step being "replace existing preedit string" — which the client can only do if it knows thedonepredates its own reset.Proposed cure: count the
commitrequests inDevice(one per enable/disable/commit we send) and remember the count at the latest disable; ondone, a serial below that count marks the batch stale. Either drop stale batches in the library (simplest; a client that already committed the preedit loses only what the input method added after it, a space), or surface them flagged — astaleflag on the event, or a separate event — so a consumer can drop a stale preedit and treat a stale commit as replacing the span it committed itself. A test can drivetext_input_commit_string+text_input_donewith a stale serial afterSetTextInput(false)on a default-constructedWindow, astests/MouseScrolldrives the pointer callbacks.