wl: the candidate popup

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>
This commit is contained in:
2026-08-17 19:35:19 +09:00
parent 4ac0625c6b
commit 5664a008de
2 changed files with 230 additions and 12 deletions

12
main.c
View File

@@ -57,13 +57,17 @@ threadmain(int argc, char **argv)
langinit(argv[1]);
composeinit();
srvinit();
proccreate(drawthread, nil, 16384);
proccreate(srvthread, nil, 16384);
proccreate(ibusthread, nil, 32768);
/* One popup per session: the Wayland frontend draws its own, so the
* X11 one and the XIM it serves stand down. */
if(getenv("WAYLAND_DISPLAY") != nil && wlinit())
proccreate(wlthread, nil, 32768);
display = getenv("DISPLAY");
if(display != nil && display[0] != '\0')
proccreate(ximthread, nil, 32768);
else{
proccreate(drawthread, nil, 16384);
display = getenv("DISPLAY");
if(display != nil && display[0] != '\0')
proccreate(ximthread, nil, 32768);
}
imthread(nil);
}