40fd4ab was half right and the sway run said which half. The compositor
does relay the commit when a client merely disabled its text input while
holding the focus -- that much was read correctly out of sway -- but the
client has stopped listening by then. On the wire, in that order:
-> zwp_text_input_v3.disable()
-> zwp_text_input_v3.commit()
<- zwp_text_input_v3.preedit_string("")
<- zwp_text_input_v3.commit_string("가")
<- zwp_text_input_v3.done()
and the entry unchanged. GTK3 drops global->current at its own
focus-out, and any client that follows text-input-v3 does the same, since
events between disable and the next enable are to be ignored. So the
composition is unrecoverable there, by no fault of ours, and the commit
was a request nobody could take.
The code goes back to what it was; the comment does not. The old one
said the compositor drops the commit, which is true only when the focus
moved, and that half-truth is what invited the change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both popups read it, each just before its own textinit, and the second
copy needed a comment to say why it was there at all. One frontend runs
per session and popupscale is one global, so threadmain reads it before it
starts either -- which is before any setfont, the only ordering the two
copies were keeping.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two frontends read the same two numbers, and wl.c carried four lines of
comment to say why it kept its own copy of them. IBus and text-input-v3
number the purposes alike because both took them from GTK, so the header
is where they belong and the explanation goes away with the copy. hidden
stays in both files: one asks about a context, the other about the seat.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
grabkey returns before it asks for the mask when there is no keyboard
state, so the guard inside modmask answered a question nobody put to it --
except the test, which was the only caller that could reach it. A check
kept alive by the test that reaches it is one line of code and one line of
test to delete.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An activate and a deactivate in one batch left the grab taken with nothing
active, which is the one state the grab must never be in: sway hands keys
to the grab holder without asking whether the input method is active.
sway cannot produce that batch -- relay_send_im_state sends a done after
each -- so this is the table being total, not a bug that bit. The
deactivate arm was already right; only the activate arm forgot to look at
where the batch ended.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bitmap held 256, and forward dropped anything above it rather than
passing it on, so a key the engine does not want vanished instead of
reaching the client. Keys do live up there: KEY_FN is 0x1d0,
KEY_VOICECOMMAND 0x246, KEY_MACRO1 0x290, and xkeyboard-config maps
keycodes to <I709>, so they carry a keysym and are nothing special to us.
sway matches its bindings before it hands a key to the grab, so an unbound
one of those was lost for as long as any text field had the focus.
The whole evdev range costs 96 bytes of bitmap instead of 32. The test
already writes the bound rather than the number, so it still says what it
said.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pendingpurpose was never cleared, and sway sends content_type only when
the text input asked for one: active_features is frozen at the enable
commit (wlr_text_input_v3.c) and relay_send_im_state tests it before
sending. So a client that sets no content type leaves the last client's
purpose standing -- and after a GTK password entry that purpose is
password, so every key in the next window was forwarded raw and strans
looked dead there until a client that does declare one took the focus.
A purpose is per activation, as it is per context in ibus.c. Clearing it
on activate is enough: the content type, if any, arrives after the
activate and before the done that applies both.
The sway runs could not have found this; the password client was GTK,
which always declares.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things arrive as a deactivate. When the focus moved, the compositor
sent the text input its leave first, and sway's handle_im_commit finds no
focused text input and drops the whole commit -- which is what the old
comment described. But a client that merely disabled its text input
still holds the focus: sway relays that deactivate only in that case
(handle_text_input_disable returns early once the surface is unfocused),
so the commit would have been delivered, and half a syllable was thrown
away instead. A GtkEntry losing the focus to a button in its own window
is that case.
XIM and IBus both hand the text back there and drop it only when the
client itself is gone -- ibus.c keeps the two apart as flushcontext and
releasecontext. This gives the Wayland frontend the same halves. The
commit that goes nowhere costs one empty request.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One wl_surface and one zwp_input_popup_surface_v2, made once; the
compositor makes them visible on activate, puts them at the text cursor
and takes them down again, so popuparea, popupposition and Caret stay
X11-only and popuplayout gets a constant area. Two shm buffers are used
in turn, each busy from attach until the compositor releases it, because
win.c's single grow-only image would be redrawn while the compositor was
still reading it; a picture that arrives while both are busy waits for
the next release, and the engine will not send it twice. drawthread no
longer runs, so wl.c reads GDK_SCALE itself and calls textinit after it,
since setfont reads Fontsz. The buffer is rounded up to a multiple of
the scale: one that is not is an invalid_size error at attach, and a
preedit alone really does lay out to an odd width at GDK_SCALE=2.
drawc is taken where the engine's picture and our surface can part:
after every key, after the Keyrelease a deactivate sends, and after the
owner poll, hiding for the last two. The poll is not an optimisation --
once another frontend takes the engine, its pictures go unconsumed and
no later send will ever say hide.
main.c now starts either the Wayland frontend or the X11 popup and XIM,
which is where the two popups would otherwise collide.
Checked by hand under headless sway 1.12, reading grim screenshots: the
hanja list for 가 with its selection and 1-9/125 marker, the emoji
search, the popup gone after Escape, and the same at GDK_SCALE=2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zwp_input_method_v2 over one connection, in one process: activate,
deactivate and content type applied at done, whose count is the serial a
commit must carry. The keyboard is grabbed at every activate and
released at every deactivate, because sway hands keys to the grab holder
without asking whether the input method is active, and a grab held while
inactive would take every key in the session. Keys come as evdev codes,
go through xkb and the Compose table the other frontends already share,
and reach the engine as an ipckeysym and a modifier mask; the grab's
modifiers go on to the virtual keyboard as well, since wlroots derives no
state from a virtual keyboard's own keys. A key the engine does not eat
goes back as a key, not as text, and whatever is still down is released
when the context goes. A password or a PIN purpose never reaches the
engine at all.
No popup yet: the X11 one still owns drawc, and with no Keycaret from us
it follows the pointer.
Checked by hand under headless sway 1.12 with a GTK3 entry driven by
wtype: ascii passes through, Ctrl+s selects Korean, rk shows the preedit
and Enter commits 가 and lets the Return on, Ctrl+c arrives with its
modifier, focus moving between two clients re-grabs and keeps typing, a
password entry types literally, a second strans says so and stops, and
WAYLAND_DISPLAY without a compositor falls back to XIM.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>