popup: a border, a preedit as wide as its text, one place by the pointer, and no dying

The popup was a white box on the usually white text it covered, with
no edge to tell them apart; it has a border in the separator's colour.
A syllable being composed came in a bar twelve ems wide, wiping out the
line under it: only candidate rows share that steady width now, a
preedit alone hugs its text.  A popup placed by the pointer, when the
client sends no caret, was placed again on every key and so followed
the mouse; it stays where it came up.  Clicks on it no longer fall
through to the root window and its menu.  And one failed draw — a lost
pointer reply, a pixmap the server refused — ended the draw thread and
the popup for the rest of the session; only a dead connection does now.
This commit is contained in:
2026-08-17 01:41:54 +09:00
parent 87cb03fdf8
commit bab40c95a7
4 changed files with 45 additions and 28 deletions

1
dat.h
View File

@@ -41,6 +41,7 @@ extern int popupscale;
#define Fontsz (32*popupscale) #define Fontsz (32*popupscale)
#define PopupPad (4*popupscale) #define PopupPad (4*popupscale)
#define PopupSep popupscale #define PopupSep popupscale
#define PopupBorder popupscale
#define PopupNumw Fontsz #define PopupNumw Fontsz
#define PopupTextw (12*Fontsz) #define PopupTextw (12*Fontsz)
#define PopupBasew (2*PopupPad + PopupNumw + PopupTextw) #define PopupBasew (2*PopupPad + PopupNumw + PopupTextw)

View File

@@ -149,7 +149,8 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
if(nmark == 0) if(nmark == 0)
markrow = 0; markrow = 0;
width = PopupBasew; /* Rows share one steady width; a preedit alone hugs its text. */
width = p->n != 0 ? PopupBasew : 2*PopupPad;
if(npre) if(npre)
width = max(width, textwidth(&dc->pre) + 2*PopupPad); width = max(width, textwidth(&dc->pre) + 2*PopupPad);
for(i = 0; i < p->n; i++) for(i = 0; i < p->n; i++)

View File

@@ -144,10 +144,10 @@ popup_layout(struct ct *t)
CT_EQ_INT(t, -1, p.rowsy); CT_EQ_INT(t, -1, p.rowsy);
CT_EQ_INT(t, -1, p.marky); CT_EQ_INT(t, -1, p.marky);
/* Preedit only. */ /* Preedit only: as wide as its text. */
dc.pre = mkstr("preedit"); dc.pre = mkstr("preedit");
popuplayout(&dc, 2*PopupBasew, Imgh, &p); popuplayout(&dc, 2*PopupBasew, Imgh, &p);
CT_EQ_INT(t, PopupBasew, p.w); CT_EQ_INT(t, textwidth(&dc.pre) + 2*PopupPad, p.w);
CT_EQ_INT(t, PopupPad, p.prey); CT_EQ_INT(t, PopupPad, p.prey);
CT_EQ_INT(t, -1, p.sepy); CT_EQ_INT(t, -1, p.sepy);
CT_EQ_INT(t, -1, p.rowsy); CT_EQ_INT(t, -1, p.rowsy);

65
win.c
View File

@@ -12,6 +12,7 @@ static u32int *img;
static xcb_atom_t currentdesktop; static xcb_atom_t currentdesktop;
static xcb_atom_t workarea; static xcb_atom_t workarea;
static int hasrandr, imgh, imgw; static int hasrandr, imgh, imgw;
static int shown, ptrx, ptry; /* the popup, and where the pointer was as it came up */
static xcb_screen_t* static xcb_screen_t*
getscr(xcb_connection_t *c, int n) getscr(xcb_connection_t *c, int n)
@@ -201,7 +202,7 @@ static int
wininit(void) wininit(void)
{ {
int n; int n;
u32int mask, vals[4]; u32int mask, vals[5];
xcb_atom_t type, tooltip; xcb_atom_t type, tooltip;
xcb_randr_query_version_cookie_t rc; xcb_randr_query_version_cookie_t rc;
xcb_randr_query_version_reply_t *rr; xcb_randr_query_version_reply_t *rr;
@@ -234,14 +235,17 @@ wininit(void)
currentdesktop = getatom("_NET_CURRENT_DESKTOP"); currentdesktop = getatom("_NET_CURRENT_DESKTOP");
workarea = getatom("_NET_WORKAREA"); workarea = getatom("_NET_WORKAREA");
win = xcb_generate_id(conn); win = xcb_generate_id(conn);
/* Clicks on the popup go nowhere, not to the root window under it. */
mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER; XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER |
XCB_CW_DONT_PROPAGATE;
vals[0] = Colbg; vals[0] = Colbg;
vals[1] = 0; vals[1] = Colsep;
vals[2] = 1; vals[2] = 1;
vals[3] = 1; vals[3] = 1;
vals[4] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE;
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root, xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, 0, 1, 1, PopupBorder, XCB_WINDOW_CLASS_INPUT_OUTPUT,
scr->root_visual, mask, vals); scr->root_visual, mask, vals);
type = getatom("_NET_WM_WINDOW_TYPE"); type = getatom("_NET_WM_WINDOW_TYPE");
tooltip = getatom("_NET_WM_WINDOW_TYPE_TOOLTIP"); tooltip = getatom("_NET_WM_WINDOW_TYPE_TOOLTIP");
@@ -302,43 +306,54 @@ putimage(int w, int h)
xcb_clear_area(conn, 0, win, 0, 0, w, h); xcb_clear_area(conn, 0, win, 0, 0, w, h);
} }
static int static void
winhide(void) winhide(void)
{ {
xcb_unmap_window(conn, win); xcb_unmap_window(conn, win);
return xcb_flush(conn) > 0; xcb_flush(conn);
shown = 0;
} }
/* Draws dc at the caret, or by the pointer when the caret is unknown. */ /*
static int * Draws dc at the caret, or by the pointer when the caret is unknown:
* where the pointer was as the popup came up, so that it does not
* follow the mouse from key to key.
*/
static void
winshow(Drawcmd *dc) winshow(Drawcmd *dc)
{ {
Area area; Area area;
Popup p; Popup p;
int ax, ay, px, py, x, y; int ax, ay, x, y;
u32int vals[5]; u32int vals[5];
xcb_query_pointer_reply_t *ptr; xcb_query_pointer_reply_t *ptr;
xcb_query_pointer_cookie_t cookie; xcb_query_pointer_cookie_t cookie;
if(dc->nkouho == 0 && dc->pre.n == 0) if(dc->nkouho == 0 && dc->pre.n == 0){
return winhide(); winhide();
px = py = 0; return;
if(!dc->caret.valid){ }
if(!dc->caret.valid && !shown){
cookie = xcb_query_pointer(conn, scr->root); cookie = xcb_query_pointer(conn, scr->root);
ptr = xcb_query_pointer_reply(conn, cookie, nil); ptr = xcb_query_pointer_reply(conn, cookie, nil);
if(ptr == nil) if(ptr == nil)
return 0; return;
px = ptr->root_x; ptrx = ptr->root_x;
py = ptr->root_y; ptry = ptr->root_y;
free(ptr); free(ptr);
} }
ax = dc->caret.valid ? dc->caret.x : px; ax = dc->caret.valid ? dc->caret.x : ptrx;
ay = dc->caret.valid ? dc->caret.y : py; ay = dc->caret.valid ? dc->caret.y : ptry;
popupwork(ax, ay, &area); popupwork(ax, ay, &area);
popuplayout(dc, area.w, area.h, &p); popuplayout(dc, area.w, area.h, &p);
if(p.w <= 0 || p.h <= 0 || !resizebacking(p.w, p.h)) if(p.w <= 0 || p.h <= 0){
return 0; winhide();
popupposition(&dc->caret, px, py, &area, p.w, p.h, &x, &y); return;
}
if(!resizebacking(p.w, p.h))
return;
popupposition(&dc->caret, ptrx, ptry, &area, p.w + 2*PopupBorder,
p.h + 2*PopupBorder, &x, &y);
/* Mapping does not restack: raise above windows opened since. */ /* Mapping does not restack: raise above windows opened since. */
vals[0] = x; vals[0] = x;
vals[1] = y; vals[1] = y;
@@ -352,7 +367,8 @@ winshow(Drawcmd *dc)
popupdraw(img, dc, &p); popupdraw(img, dc, &p);
putimage(p.w, p.h); putimage(p.w, p.h);
xcb_map_window(conn, win); xcb_map_window(conn, win);
return xcb_flush(conn) > 0; xcb_flush(conn);
shown = 1;
} }
void void
@@ -363,8 +379,7 @@ drawthread(void*)
threadsetname("draw"); threadsetname("draw");
if(!wininit()) if(!wininit())
return; return;
while(chanrecv(drawc, &dc) > 0) while(chanrecv(drawc, &dc) > 0 && !xcb_connection_has_error(conn))
if(!winshow(&dc)) winshow(&dc);
break;
wincleanup(); wincleanup();
} }