diff --git a/dat.h b/dat.h index 25816bf..0dc8e73 100644 --- a/dat.h +++ b/dat.h @@ -158,6 +158,7 @@ struct Popup int markx; int marky; int markw; + Str mark; int selx; int sely; int selw; diff --git a/fn.h b/fn.h index 641d6bb..fe52506 100644 --- a/fn.h +++ b/fn.h @@ -47,6 +47,4 @@ void* erealloc(void*, ulong); int textinit(void); void textclose(void); int textwidth(Str*); -void textdraw(u32int*, int, int, int, int, Str*); -void textdrawfit(u32int*, int, int, int, int, int, Str*); -void textdrawfitcolor(u32int*, int, int, int, int, int, u32int, Str*); +void textdraw(u32int*, int, int, int, int, int, u32int, Str*); diff --git a/font.c b/font.c index 2009ca6..6c1d0b4 100644 --- a/font.c +++ b/font.c @@ -116,8 +116,13 @@ textwidth(Str *s) 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) { PangoRectangle r; @@ -125,16 +130,12 @@ drawtext(u32int *buf, int w, int h, int x, int y, int fit, u32int color, cairo_t *cr; int b, g, red, stride; - if(layout == nil) + if(layout == nil || fit <= 0) return; resetlayout(); - if(fit != -1 && fit <= 0) - return; - if(fit > 0){ - pango_layout_set_width(layout, fit * PANGO_SCALE); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - } - if(buf == nil || w <= 0 || h <= 0 || !settext(s)) + pango_layout_set_width(layout, fit * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + if(w <= 0 || h <= 0 || !settext(s)) return; stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); 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){ pango_cairo_update_layout(cr, layout); textextents(&r); - /* A fallback face must not paint into another popup row. */ - if(fit > 0) - cairo_rectangle(cr, x, y, fit, Fontsz); - else - cairo_rectangle(cr, 0, y, w, Fontsz); + cairo_rectangle(cr, x, y, fit, Fontsz); cairo_clip(cr); cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y); 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_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); -} diff --git a/popup_layout.c b/popup_layout.c index 432e759..06b55d5 100644 --- a/popup_layout.c +++ b/popup_layout.c @@ -117,9 +117,7 @@ void popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) { char buf[32]; - Str mark; - vlong width; - int first, i, markrow, nall, nmark, npre, pad, total, y; + int first, i, markrow, nall, nmark, npre, pad, total, width, y; memset(p, 0, sizeof *p); p->prey = -1; @@ -128,7 +126,6 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) p->markx = -1; p->marky = -1; p->sely = -1; - p->row0 = 0; pad = PopupPad; nall = min(max(dc->nkouho, 0), Maxdisp); npre = dc->pre.n != 0; @@ -163,13 +160,14 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) width = PopupBasew; 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++) - width = max(width, (vlong)textwidth(&dc->kouho[p->row0+i]) + + width = max(width, textwidth(&dc->kouho[p->row0+i]) + PopupNumw + 2*PopupPad); - if(nmark != 0){ - sinit(&mark, buf, nmark); - width = max(width, (vlong)textwidth(&mark) + 2*PopupPad); + if(markrow){ + sinit(&p->mark, buf, nmark); + p->markw = textwidth(&p->mark); + width = max(width, p->markw + 2*PopupPad); } p->w = min(width, areaw); @@ -195,7 +193,7 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) p->textx = PopupPad + PopupNumw; p->textw = max(p->w - PopupPad - p->textx, 0); 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->selx = PopupPad; @@ -207,10 +205,9 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p) void popupdraw(u32int *img, Drawcmd *dc, Popup *p) { - char buf[32]; - Str mark, num; + Str num; u32int color; - int first, i, j, nmark, total, y; + int i, j, y; if(img == nil || p->n < 0 || p->n > Maxdisp || p->row0 < 0 || p->row0 + p->n > dc->nkouho || @@ -218,8 +215,8 @@ popupdraw(u32int *img, Drawcmd *dc, Popup *p) return; fill(img, p->w*p->h, Colbg); if(p->prey >= 0) - textdrawfit(img, p->w, p->h, PopupPad, p->prey, - max(p->w - 2*PopupPad, 0), &dc->pre); + textdraw(img, p->w, p->h, PopupPad, p->prey, + max(p->w - 2*PopupPad, 0), Colfg, &dc->pre); if(p->sepy >= 0) fillrect(img, p->w, p->h, PopupPad, p->sepy, p->w - 2*PopupPad, PopupSep, Colsep); @@ -231,20 +228,15 @@ popupdraw(u32int *img, Drawcmd *dc, Popup *p) color = j == dc->sel ? Colselfg : Colfg; sclear(&num); sputr(&num, '1' + j + Asciitofull); - textdrawfitcolor(img, p->w, p->h, p->numx, y, - min(PopupNumw, - max(p->w - PopupPad - p->numx, 0)), color, &num); - textdrawfitcolor(img, p->w, p->h, p->textx, y, - p->textw, color, &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); + textdraw(img, p->w, p->h, p->numx, y, + min(PopupNumw, max(p->w - PopupPad - p->numx, 0)), + color, &num); + textdraw(img, p->w, p->h, p->textx, y, p->textw, color, + &dc->kouho[j]); } + if(p->marky >= 0) + textdraw(img, p->w, p->h, p->markx, p->marky, p->markw, + Colfg, &p->mark); } void diff --git a/tests/font_test.c b/tests/font_test.c index 3548341..a7cfe3a 100644 --- a/tests/font_test.c +++ b/tests/font_test.c @@ -120,7 +120,7 @@ checktext(struct ct *t, u32int *buf, char *utf) CT_CHECK(t, w > 0); CT_CHECK(t, w < Testw - Fontsz); 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))) return; CT_CHECK(t, minx >= 0 && miny >= 0); @@ -140,7 +140,7 @@ checkcolor(struct ct *t, u32int *buf, char *utf) if(s.n > 1) CT_CHECK(t, w < s.n * Fontsz); 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, 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 < s.n * Fontsz); 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)); } @@ -167,7 +167,7 @@ checkclip(struct ct *t, u32int *mem, int x, int y, Str *s) buf = mem + Guard; 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)); checkoutside(t, buf, Colbg, y); checkguards(t, mem); @@ -203,7 +203,7 @@ font_render(struct ct *t) heart = mkstr("❤️"); 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_EQ_UINT(t, Colsel, buf[(Fontsz + Fontsz/2) * Testw + Testw - 1] & Rgbmask); @@ -220,8 +220,8 @@ font_render(struct ct *t) natural = textwidth(&longrow); CT_CHECK(t, natural > 2*Fontsz); fillpixels(buf, Testn, Colbg); - textdrawfit(buf, Testw, Testh, 2*Fontsz, Fontsz, - 2*Fontsz, &longrow); + textdraw(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colfg, + &longrow); CT_CHECK(t, hasink(buf, Testn, Colbg)); for(y = Fontsz; y < 2*Fontsz; y++){ for(x = 0; x < 2*Fontsz; x++) @@ -232,8 +232,8 @@ font_render(struct ct *t) CT_EQ_INT(t, natural, textwidth(&longrow)); fillpixels(buf, Testn, Colsel); - textdrawfitcolor(buf, Testw, Testh, 2*Fontsz, Fontsz, - 2*Fontsz, Colselfg, &longrow); + textdraw(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colselfg, + &longrow); CT_CHECK(t, (Colselfg & Rgbmask) != (Colsel & Rgbmask)); if(CT_CHECK(t, inkbounds(buf, Colsel, &minx, &miny, &maxx, &maxy))){ @@ -248,8 +248,8 @@ font_render(struct ct *t) missing = mkstr("\xF4\x8F\xBF\xBF"); for(i = 0; i < 32; i++){ - textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); - textdraw(buf, Testw, Testh, 0, 0, &missing); + textdraw(buf, Testw, Testh, Fontsz, Fontsz, Testw, Colfg, &heart); + textdraw(buf, Testw, Testh, 0, 0, Testw, Colfg, &missing); } checkguards(t, mem); free(mem); diff --git a/tests/popup_test.c b/tests/popup_test.c index c186bb9..5a7c662 100644 --- a/tests/popup_test.c +++ b/tests/popup_test.c @@ -74,10 +74,6 @@ popup_layout(struct ct *t) CT_EQ_INT(t, strlen(markers[i].want), n); 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. */ mon[0] = (Area){0, 0, 1920, 1080}; diff --git a/win.c b/win.c index 97816d1..ed7d6ec 100644 --- a/win.c +++ b/win.c @@ -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(); }