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

1
dat.h
View File

@@ -158,6 +158,7 @@ struct Popup
int markx; int markx;
int marky; int marky;
int markw; int markw;
Str mark;
int selx; int selx;
int sely; int sely;
int selw; int selw;

4
fn.h
View File

@@ -47,6 +47,4 @@ void* erealloc(void*, ulong);
int textinit(void); int textinit(void);
void textclose(void); void textclose(void);
int textwidth(Str*); int textwidth(Str*);
void textdraw(u32int*, int, int, int, int, Str*); void textdraw(u32int*, int, int, int, int, int, u32int, Str*);
void textdrawfit(u32int*, int, int, int, int, int, Str*);
void textdrawfitcolor(u32int*, int, int, int, int, int, u32int, Str*);

40
font.c
View File

@@ -116,8 +116,13 @@ textwidth(Str *s)
return r.width; return r.width;
} }
static void /*
drawtext(u32int *buf, int w, int h, int x, int y, int fit, u32int color, * Draws s at (x, y) into the w-by-h buffer, ellipsized to fit pixels and
* clipped to its own row so a tall fallback face cannot paint a
* neighbour.
*/
void
textdraw(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
Str *s) Str *s)
{ {
PangoRectangle r; PangoRectangle r;
@@ -125,16 +130,12 @@ drawtext(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
cairo_t *cr; cairo_t *cr;
int b, g, red, stride; int b, g, red, stride;
if(layout == nil) if(layout == nil || fit <= 0)
return; return;
resetlayout(); resetlayout();
if(fit != -1 && fit <= 0)
return;
if(fit > 0){
pango_layout_set_width(layout, fit * PANGO_SCALE); pango_layout_set_width(layout, fit * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
} if(w <= 0 || h <= 0 || !settext(s))
if(buf == nil || w <= 0 || h <= 0 || !settext(s))
return; return;
stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
if(stride < 0 || (vlong)stride != (vlong)w * sizeof buf[0]) if(stride < 0 || (vlong)stride != (vlong)w * sizeof buf[0])
@@ -150,11 +151,7 @@ drawtext(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
if(cairo_status(cr) == CAIRO_STATUS_SUCCESS){ if(cairo_status(cr) == CAIRO_STATUS_SUCCESS){
pango_cairo_update_layout(cr, layout); pango_cairo_update_layout(cr, layout);
textextents(&r); textextents(&r);
/* A fallback face must not paint into another popup row. */
if(fit > 0)
cairo_rectangle(cr, x, y, fit, Fontsz); cairo_rectangle(cr, x, y, fit, Fontsz);
else
cairo_rectangle(cr, 0, y, w, Fontsz);
cairo_clip(cr); cairo_clip(cr);
cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y); cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y);
red = color >> 16 & 0xff; red = color >> 16 & 0xff;
@@ -167,22 +164,3 @@ drawtext(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
cairo_surface_flush(surface); cairo_surface_flush(surface);
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
} }
void
textdraw(u32int *buf, int w, int h, int x, int y, Str *s)
{
drawtext(buf, w, h, x, y, -1, Colfg, s);
}
void
textdrawfit(u32int *buf, int w, int h, int x, int y, int fit, Str *s)
{
drawtext(buf, w, h, x, y, fit, Colfg, s);
}
void
textdrawfitcolor(u32int *buf, int w, int h, int x, int y, int fit,
u32int color, Str *s)
{
drawtext(buf, w, h, x, y, fit, color, s);
}

View File

@@ -117,9 +117,7 @@ void
popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
{ {
char buf[32]; char buf[32];
Str mark; int first, i, markrow, nall, nmark, npre, pad, total, width, y;
vlong width;
int first, i, markrow, nall, nmark, npre, pad, total, y;
memset(p, 0, sizeof *p); memset(p, 0, sizeof *p);
p->prey = -1; p->prey = -1;
@@ -128,7 +126,6 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
p->markx = -1; p->markx = -1;
p->marky = -1; p->marky = -1;
p->sely = -1; p->sely = -1;
p->row0 = 0;
pad = PopupPad; pad = PopupPad;
nall = min(max(dc->nkouho, 0), Maxdisp); nall = min(max(dc->nkouho, 0), Maxdisp);
npre = dc->pre.n != 0; npre = dc->pre.n != 0;
@@ -163,13 +160,14 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
width = PopupBasew; width = PopupBasew;
if(npre) if(npre)
width = max(width, (vlong)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++)
width = max(width, (vlong)textwidth(&dc->kouho[p->row0+i]) + width = max(width, textwidth(&dc->kouho[p->row0+i]) +
PopupNumw + 2*PopupPad); PopupNumw + 2*PopupPad);
if(nmark != 0){ if(markrow){
sinit(&mark, buf, nmark); sinit(&p->mark, buf, nmark);
width = max(width, (vlong)textwidth(&mark) + 2*PopupPad); p->markw = textwidth(&p->mark);
width = max(width, p->markw + 2*PopupPad);
} }
p->w = min(width, areaw); p->w = min(width, areaw);
@@ -195,7 +193,7 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
p->textx = PopupPad + PopupNumw; p->textx = PopupPad + PopupNumw;
p->textw = max(p->w - PopupPad - p->textx, 0); p->textw = max(p->w - PopupPad - p->textx, 0);
if(markrow){ if(markrow){
p->markw = min(textwidth(&mark), max(p->w - 2*PopupPad, 0)); p->markw = min(p->markw, max(p->w - 2*PopupPad, 0));
p->markx = p->w - PopupPad - p->markw; p->markx = p->w - PopupPad - p->markw;
} }
p->selx = PopupPad; p->selx = PopupPad;
@@ -207,10 +205,9 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
void void
popupdraw(u32int *img, Drawcmd *dc, Popup *p) popupdraw(u32int *img, Drawcmd *dc, Popup *p)
{ {
char buf[32]; Str num;
Str mark, num;
u32int color; u32int color;
int first, i, j, nmark, total, y; int i, j, y;
if(img == nil || p->n < 0 || p->n > Maxdisp || if(img == nil || p->n < 0 || p->n > Maxdisp ||
p->row0 < 0 || p->row0 + p->n > dc->nkouho || p->row0 < 0 || p->row0 + p->n > dc->nkouho ||
@@ -218,8 +215,8 @@ popupdraw(u32int *img, Drawcmd *dc, Popup *p)
return; return;
fill(img, p->w*p->h, Colbg); fill(img, p->w*p->h, Colbg);
if(p->prey >= 0) if(p->prey >= 0)
textdrawfit(img, p->w, p->h, PopupPad, p->prey, textdraw(img, p->w, p->h, PopupPad, p->prey,
max(p->w - 2*PopupPad, 0), &dc->pre); max(p->w - 2*PopupPad, 0), Colfg, &dc->pre);
if(p->sepy >= 0) if(p->sepy >= 0)
fillrect(img, p->w, p->h, PopupPad, p->sepy, fillrect(img, p->w, p->h, PopupPad, p->sepy,
p->w - 2*PopupPad, PopupSep, Colsep); p->w - 2*PopupPad, PopupSep, Colsep);
@@ -231,20 +228,15 @@ popupdraw(u32int *img, Drawcmd *dc, Popup *p)
color = j == dc->sel ? Colselfg : Colfg; color = j == dc->sel ? Colselfg : Colfg;
sclear(&num); sclear(&num);
sputr(&num, '1' + j + Asciitofull); sputr(&num, '1' + j + Asciitofull);
textdrawfitcolor(img, p->w, p->h, p->numx, y, textdraw(img, p->w, p->h, p->numx, y,
min(PopupNumw, min(PopupNumw, max(p->w - PopupPad - p->numx, 0)),
max(p->w - PopupPad - p->numx, 0)), color, &num); color, &num);
textdrawfitcolor(img, p->w, p->h, p->textx, y, textdraw(img, p->w, p->h, p->textx, y, p->textw, color,
p->textw, color, &dc->kouho[j]); &dc->kouho[j]);
}
first = dc->first + p->row0;
total = max(dc->total, dc->first + dc->nkouho);
nmark = pagemarker(buf, sizeof buf, first, p->n, total);
if(nmark != 0 && p->marky >= 0){
sinit(&mark, buf, nmark);
textdrawfit(img, p->w, p->h, p->markx, p->marky,
p->markw, &mark);
} }
if(p->marky >= 0)
textdraw(img, p->w, p->h, p->markx, p->marky, p->markw,
Colfg, &p->mark);
} }
void void

View File

@@ -120,7 +120,7 @@ checktext(struct ct *t, u32int *buf, char *utf)
CT_CHECK(t, w > 0); CT_CHECK(t, w > 0);
CT_CHECK(t, w < Testw - Fontsz); CT_CHECK(t, w < Testw - Fontsz);
fillpixels(buf, Testn, Colbg); fillpixels(buf, Testn, Colbg);
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &s);
if(!CT_CHECK(t, inkbounds(buf, Colbg, &minx, &miny, &maxx, &maxy))) if(!CT_CHECK(t, inkbounds(buf, Colbg, &minx, &miny, &maxx, &maxy)))
return; return;
CT_CHECK(t, minx >= 0 && miny >= 0); CT_CHECK(t, minx >= 0 && miny >= 0);
@@ -140,7 +140,7 @@ checkcolor(struct ct *t, u32int *buf, char *utf)
if(s.n > 1) if(s.n > 1)
CT_CHECK(t, w < s.n * Fontsz); CT_CHECK(t, w < s.n * Fontsz);
fillpixels(buf, Testn, Colbg); fillpixels(buf, Testn, Colbg);
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &s);
CT_CHECK(t, hasink(buf, Testn, Colbg)); CT_CHECK(t, hasink(buf, Testn, Colbg));
CT_CHECK(t, hascolors(buf, Testn, Colbg)); CT_CHECK(t, hascolors(buf, Testn, Colbg));
} }
@@ -156,7 +156,7 @@ checkshape(struct ct *t, u32int *buf, char *utf)
CT_CHECK(t, w > 0); CT_CHECK(t, w > 0);
CT_CHECK(t, w < s.n * Fontsz); CT_CHECK(t, w < s.n * Fontsz);
fillpixels(buf, Testn, Colbg); fillpixels(buf, Testn, Colbg);
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &s);
CT_CHECK(t, hasink(buf, Testn, Colbg)); CT_CHECK(t, hasink(buf, Testn, Colbg));
} }
@@ -167,7 +167,7 @@ checkclip(struct ct *t, u32int *mem, int x, int y, Str *s)
buf = mem + Guard; buf = mem + Guard;
fillpixels(buf, Testn, Colbg); fillpixels(buf, Testn, Colbg);
textdraw(buf, Testw, Testh, x, y, s); textdraw(buf, Testw, Testh, x, y, Testw, Colfg, s);
CT_CHECK(t, hasink(buf, Testn, Colbg)); CT_CHECK(t, hasink(buf, Testn, Colbg));
checkoutside(t, buf, Colbg, y); checkoutside(t, buf, Colbg, y);
checkguards(t, mem); checkguards(t, mem);
@@ -203,7 +203,7 @@ font_render(struct ct *t)
heart = mkstr("❤️"); heart = mkstr("❤️");
fillpixels(buf, Testn, Colsel); fillpixels(buf, Testn, Colsel);
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &heart);
CT_CHECK(t, hascolors(buf, Testn, Colsel)); CT_CHECK(t, hascolors(buf, Testn, Colsel));
CT_EQ_UINT(t, Colsel, CT_EQ_UINT(t, Colsel,
buf[(Fontsz + Fontsz/2) * Testw + Testw - 1] & Rgbmask); buf[(Fontsz + Fontsz/2) * Testw + Testw - 1] & Rgbmask);
@@ -220,8 +220,8 @@ font_render(struct ct *t)
natural = textwidth(&longrow); natural = textwidth(&longrow);
CT_CHECK(t, natural > 2*Fontsz); CT_CHECK(t, natural > 2*Fontsz);
fillpixels(buf, Testn, Colbg); fillpixels(buf, Testn, Colbg);
textdrawfit(buf, Testw, Testh, 2*Fontsz, Fontsz, textdraw(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colfg,
2*Fontsz, &longrow); &longrow);
CT_CHECK(t, hasink(buf, Testn, Colbg)); CT_CHECK(t, hasink(buf, Testn, Colbg));
for(y = Fontsz; y < 2*Fontsz; y++){ for(y = Fontsz; y < 2*Fontsz; y++){
for(x = 0; x < 2*Fontsz; x++) for(x = 0; x < 2*Fontsz; x++)
@@ -232,8 +232,8 @@ font_render(struct ct *t)
CT_EQ_INT(t, natural, textwidth(&longrow)); CT_EQ_INT(t, natural, textwidth(&longrow));
fillpixels(buf, Testn, Colsel); fillpixels(buf, Testn, Colsel);
textdrawfitcolor(buf, Testw, Testh, 2*Fontsz, Fontsz, textdraw(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colselfg,
2*Fontsz, Colselfg, &longrow); &longrow);
CT_CHECK(t, (Colselfg & Rgbmask) != (Colsel & Rgbmask)); CT_CHECK(t, (Colselfg & Rgbmask) != (Colsel & Rgbmask));
if(CT_CHECK(t, inkbounds(buf, Colsel, if(CT_CHECK(t, inkbounds(buf, Colsel,
&minx, &miny, &maxx, &maxy))){ &minx, &miny, &maxx, &maxy))){
@@ -248,8 +248,8 @@ font_render(struct ct *t)
missing = mkstr("\xF4\x8F\xBF\xBF"); missing = mkstr("\xF4\x8F\xBF\xBF");
for(i = 0; i < 32; i++){ for(i = 0; i < 32; i++){
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &heart);
textdraw(buf, Testw, Testh, 0, 0, &missing); textdraw(buf, Testw, Testh, 0, 0, Testw, Colfg, &missing);
} }
checkguards(t, mem); checkguards(t, mem);
free(mem); free(mem);

View File

@@ -74,10 +74,6 @@ popup_layout(struct ct *t)
CT_EQ_INT(t, strlen(markers[i].want), n); CT_EQ_INT(t, strlen(markers[i].want), n);
CT_CHECK(t, strcmp(markers[i].want, buf) == 0); CT_CHECK(t, strcmp(markers[i].want, buf) == 0);
} }
CT_EQ_INT(t, 12*Fontsz, PopupTextw);
CT_EQ_INT(t, 2*PopupPad + PopupNumw + PopupTextw, PopupBasew);
CT_EQ_INT(t, 2*PopupPad + (1 + Maxdisp + 1)*Fontsz + PopupSep,
Imgh);
/* Monitor selection retains origins and intersects the EWMH work area. */ /* Monitor selection retains origins and intersects the EWMH work area. */
mon[0] = (Area){0, 0, 1920, 1080}; mon[0] = (Area){0, 0, 1920, 1080};

125
win.c
View File

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