popup: one text drawing call; drop what the draw thread never reaches

font.c exported three wrappers for one function, and the fit == -1 mode
existed only for the tests; textdraw() takes fit and colour. win.c
tested the empty picture in the thread and again in winshow, interned
two atoms by hand next to getatom(), kept a consumer-side drain that the
producer's already guarantees never finds anything, zeroed ten globals
before returning from the only thread that read them, and re-checked
sizes that resizebacking had just established. The page marker is laid
out once and drawn from the layout instead of being recomputed.
This commit is contained in:
2026-08-16 16:22:37 +09:00
parent eaf31da0d1
commit 849bf1278b
7 changed files with 91 additions and 169 deletions

135
win.c
View File

@@ -11,7 +11,7 @@ static xcb_pixmap_t pix;
static u32int *img;
static xcb_atom_t currentdesktop;
static xcb_atom_t workarea;
static int depth, hasrandr, imgh, imgw;
static int hasrandr, imgh, imgw;
static xcb_screen_t*
getscr(xcb_connection_t *c, int n)
@@ -42,6 +42,7 @@ getvisual(xcb_screen_t *s)
return nil;
}
/* putimage writes native-endian x8r8g8b8; the root must take that. */
static int
validformat(xcb_connection_t *c, xcb_screen_t *s)
{
@@ -72,13 +73,13 @@ validformat(xcb_connection_t *c, xcb_screen_t *s)
}
static xcb_atom_t
getatom(char *name, int exists)
getatom(char *name)
{
xcb_intern_atom_cookie_t cookie;
xcb_intern_atom_reply_t *reply;
xcb_atom_t atom;
cookie = xcb_intern_atom(conn, exists, strlen(name), name);
cookie = xcb_intern_atom(conn, 0, strlen(name), name);
reply = xcb_intern_atom_reply(conn, cookie, nil);
atom = reply == nil ? XCB_ATOM_NONE : reply->atom;
free(reply);
@@ -144,58 +145,48 @@ getrootarea(Area *a)
free(reply);
}
/* The area the popup may use: the RandR monitor under (x, y), or the
* root, cut down to the EWMH work area. */
static void
popupwork(int x, int y, Area *out)
{
xcb_generic_error_t *err;
xcb_randr_get_monitors_cookie_t cookie;
xcb_randr_get_monitors_reply_t *reply;
xcb_randr_monitor_info_iterator_t it;
Area root, net, *mon;
int i, j, n;
int n;
mon = nil;
n = 0;
err = nil;
reply = nil;
if(hasrandr){
cookie = xcb_randr_get_monitors(conn, scr->root, 1);
reply = xcb_randr_get_monitors_reply(conn, cookie, &err);
if(reply != nil)
n = xcb_randr_get_monitors_monitors_length(reply);
if(n > 0 && (uvlong)n <= (uvlong)(~(ulong)0)/sizeof mon[0])
mon = malloc(n*sizeof mon[0]);
if(mon != nil){
it = xcb_randr_get_monitors_monitors_iterator(reply);
for(i = j = 0; i < n && it.rem; i++, xcb_randr_monitor_info_next(&it)){
if(it.data->width == 0 || it.data->height == 0)
continue;
mon[j].x = it.data->x;
mon[j].y = it.data->y;
mon[j].w = it.data->width;
mon[j].h = it.data->height;
j++;
}
n = j;
if(n == 0){
free(mon);
mon = nil;
}
reply = xcb_randr_get_monitors_reply(conn, cookie, nil);
}
if(reply != nil && xcb_randr_get_monitors_monitors_length(reply) > 0){
mon = emalloc(xcb_randr_get_monitors_monitors_length(reply) *
sizeof mon[0]);
it = xcb_randr_get_monitors_monitors_iterator(reply);
for(; it.rem; xcb_randr_monitor_info_next(&it)){
if(it.data->width == 0 || it.data->height == 0)
continue;
mon[n].x = it.data->x;
mon[n].y = it.data->y;
mon[n].w = it.data->width;
mon[n].h = it.data->height;
n++;
}
}
if(mon == nil){
if(n == 0){
free(mon);
getrootarea(&root);
mon = &root;
n = 1;
}
if(getworkarea(&net))
popuparea(mon, n, &net, x, y, out);
else
popuparea(mon, n, nil, x, y, out);
popuparea(mon, n, getworkarea(&net) ? &net : nil, x, y, out);
if(mon != &root)
free(mon);
free(reply);
free(err);
}
static void
@@ -203,20 +194,7 @@ wincleanup(void)
{
textclose();
free(img);
img = nil;
if(conn != nil){
xcb_disconnect(conn);
conn = nil;
}
scr = nil;
win = 0;
gc = 0;
pix = 0;
currentdesktop = XCB_ATOM_NONE;
workarea = XCB_ATOM_NONE;
hasrandr = 0;
imgh = 0;
imgw = 0;
xcb_disconnect(conn);
}
static int
@@ -224,14 +202,13 @@ wininit(void)
{
int n;
u32int mask, vals[4];
xcb_intern_atom_cookie_t c1, c2;
xcb_intern_atom_reply_t *r1, *r2;
xcb_atom_t type, tooltip;
xcb_randr_query_version_cookie_t rc;
xcb_randr_query_version_reply_t *rr;
const xcb_query_extension_reply_t *rext;
conn = xcb_connect(nil, &n);
if(conn == nil || xcb_connection_has_error(conn)){
if(xcb_connection_has_error(conn)){
fprint(2, "strans: popup disabled: cannot connect to X display\n");
wincleanup();
return 0;
@@ -242,7 +219,6 @@ wininit(void)
wincleanup();
return 0;
}
depth = scr->root_depth;
rext = xcb_get_extension_data(conn, &xcb_randr_id);
if(rext != nil && rext->present){
rc = xcb_randr_query_version(conn, 1, 5);
@@ -251,8 +227,8 @@ wininit(void)
(rr->major_version == 1 && rr->minor_version >= 5));
free(rr);
}
currentdesktop = getatom("_NET_CURRENT_DESKTOP", 0);
workarea = getatom("_NET_WORKAREA", 0);
currentdesktop = getatom("_NET_CURRENT_DESKTOP");
workarea = getatom("_NET_WORKAREA");
win = xcb_generate_id(conn);
mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER;
@@ -263,18 +239,11 @@ wininit(void)
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
scr->root_visual, mask, vals);
c1 = xcb_intern_atom(conn, 0,
strlen("_NET_WM_WINDOW_TYPE"), "_NET_WM_WINDOW_TYPE");
c2 = xcb_intern_atom(conn, 0,
strlen("_NET_WM_WINDOW_TYPE_TOOLTIP"),
"_NET_WM_WINDOW_TYPE_TOOLTIP");
r1 = xcb_intern_atom_reply(conn, c1, nil);
r2 = xcb_intern_atom_reply(conn, c2, nil);
if(r1 != nil && r2 != nil)
type = getatom("_NET_WM_WINDOW_TYPE");
tooltip = getatom("_NET_WM_WINDOW_TYPE_TOOLTIP");
if(type != XCB_ATOM_NONE && tooltip != XCB_ATOM_NONE)
xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
win, r1->atom, XCB_ATOM_ATOM, 32, 1, &r2->atom);
free(r1);
free(r2);
win, type, XCB_ATOM_ATOM, 32, 1, &tooltip);
gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, win, 0, nil);
if(!textinit()){
@@ -285,27 +254,23 @@ wininit(void)
return 1;
}
/* The image and its retained pixmap only ever grow. */
static int
resizebacking(int w, int h)
{
xcb_generic_error_t *err;
xcb_pixmap_t old, new;
xcb_void_cookie_t cookie;
uvlong pixels;
int nh, nw;
if(w <= 0 || h <= 0)
return 0;
if(w <= imgw && h <= imgh)
return 1;
nw = max(w, imgw);
nh = max(h, imgh);
pixels = (uvlong)nw * nh;
if(pixels > (uvlong)(~(ulong)0)/sizeof img[0])
return 0;
img = erealloc(img, pixels*sizeof img[0]);
img = erealloc(img, (ulong)nw * nh * sizeof img[0]);
new = xcb_generate_id(conn);
cookie = xcb_create_pixmap_checked(conn, depth, new, win, nw, nh);
cookie = xcb_create_pixmap_checked(conn, scr->root_depth, new, win,
nw, nh);
err = xcb_request_check(conn, cookie);
if(err != nil){
free(err);
@@ -331,10 +296,8 @@ resizebacking(int w, int h)
static void
putimage(int w, int h)
{
if(w <= 0 || w > imgw || h <= 0 || h > imgh)
return;
xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img);
w, h, 0, 0, 0, scr->root_depth, w * h * 4, (u8int*)img);
/* The retained background pixmap lets the server repaint exposures. */
xcb_clear_area(conn, 0, win, 0, 0, w, h);
}
@@ -346,19 +309,19 @@ winhide(void)
return xcb_flush(conn) > 0;
}
/* Draws dc at the caret, or by the pointer when the caret is unknown. */
static int
winshow(Drawcmd *dc)
{
Area area;
Popup p;
int ax, ay, px, py;
int ax, ay, px, py, x, y;
u32int vals[4];
xcb_query_pointer_reply_t *ptr;
xcb_query_pointer_cookie_t cookie;
if(dc->nkouho <= 0 && dc->pre.n == 0){
if(dc->nkouho == 0 && dc->pre.n == 0)
return winhide();
}
px = py = 0;
if(!dc->caret.valid){
cookie = xcb_query_pointer(conn, scr->root);
@@ -375,11 +338,11 @@ winshow(Drawcmd *dc)
popuplayout(dc, area.w, area.h, &p);
if(p.w <= 0 || p.h <= 0 || !resizebacking(p.w, p.h))
return 0;
popupposition(&dc->caret, px, py, &area, p.w, p.h, &x, &y);
vals[0] = x;
vals[1] = y;
vals[2] = p.w;
vals[3] = p.h;
popupposition(&dc->caret, px, py, &area, p.w, p.h, &px, &py);
vals[1] = py;
vals[0] = px;
xcb_configure_window(conn, win,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
@@ -398,14 +361,8 @@ drawthread(void*)
threadsetname("draw");
if(!wininit())
return;
while(chanrecv(drawc, &dc) > 0){
while(channbrecv(drawc, &dc) > 0)
;
if(dc.nkouho == 0 && dc.pre.n == 0){
if(!winhide())
break;
}else if(!winshow(&dc))
while(chanrecv(drawc, &dc) > 0)
if(!winshow(&dc))
break;
}
wincleanup();
}