popup: no fallbacks for work areas no desktop has; text setup cannot fail

popuplayout had two refit stages for a work area shorter than one
padded row — 40 pixels — and popupdraw re-checked the layout it had
just been handed, against an Imgh that bounded no buffer any more.
Pango and Cairo abort rather than return nil, so textinit is void and
the layout is never nil; the stride Cairo computes for RGB24 is w*4 by
definition; and textdraw set the layout's width and ellipsis twice.
This commit is contained in:
2026-08-17 01:36:16 +09:00
parent cd637be4ca
commit 87cb03fdf8
7 changed files with 20 additions and 89 deletions

1
dat.h
View File

@@ -44,7 +44,6 @@ extern int popupscale;
#define PopupNumw Fontsz
#define PopupTextw (12*Fontsz)
#define PopupBasew (2*PopupPad + PopupNumw + PopupTextw)
#define Imgh (2*PopupPad + (Maxdisp + 2)*Fontsz + PopupSep)
typedef struct Str Str;
struct Str

2
fn.h
View File

@@ -48,7 +48,7 @@ int keymeaningful(u32int);
void* emalloc(ulong);
void* erealloc(void*, ulong);
int textinit(void);
void textinit(void);
void textclose(void);
int textwidth(Str*);
void textdraw(u32int*, int, int, int, int, int, u32int, Str*);

40
font.c
View File

@@ -7,13 +7,6 @@ static PangoFontMap *fontmap;
static PangoContext *context;
static PangoLayout *layout;
static void
resetlayout(void)
{
pango_layout_set_width(layout, -1);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
}
void
textclose(void)
{
@@ -50,28 +43,16 @@ setfont(void)
pango_font_description_free(font);
}
int
void
textinit(void)
{
textclose();
FcInit();
fontmap = pango_cairo_font_map_new();
if(fontmap == nil){
fprint(2, "strans: popup: can't initialize PangoCairo\n");
return 0;
}
pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap), 96);
context = pango_font_map_create_context(fontmap);
if(context != nil)
layout = pango_layout_new(context);
if(context == nil || layout == nil){
fprint(2, "strans: popup: can't create text layout\n");
textclose();
return 0;
}
layout = pango_layout_new(context);
pango_layout_set_single_paragraph_mode(layout, TRUE);
setfont();
return 1;
}
static int
@@ -80,8 +61,6 @@ settext(Str *s)
char utf[Maxutf];
int n;
if(layout == nil || s == nil || s->n <= 0)
return 0;
n = stoutf(s, utf, sizeof utf);
pango_layout_set_text(layout, utf, n);
return n > 0;
@@ -109,9 +88,8 @@ textwidth(Str *s)
{
PangoRectangle r;
if(layout == nil)
return 0;
resetlayout();
pango_layout_set_width(layout, -1);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
if(!settext(s))
return 0;
textextents(&r);
@@ -130,20 +108,16 @@ textdraw(u32int *buf, int w, int h, int x, int y, int fit, u32int color,
PangoRectangle r;
cairo_surface_t *surface;
cairo_t *cr;
int b, g, red, stride;
int b, g, red;
if(layout == nil || fit <= 0)
if(fit <= 0)
return;
resetlayout();
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])
return;
surface = cairo_image_surface_create_for_data((uchar*)buf,
CAIRO_FORMAT_RGB24, w, h, stride);
CAIRO_FORMAT_RGB24, w, h, w * sizeof buf[0]);
if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS){
cairo_surface_destroy(surface);
return;

View File

@@ -119,7 +119,7 @@ void
popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
{
char buf[32];
int first, i, markrow, nall, nmark, npre, pad, total, width, y;
int first, i, markrow, nall, nmark, npre, total, width, y;
memset(p, 0, sizeof *p);
p->prey = -1;
@@ -127,14 +127,12 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
p->rowsy = -1;
p->marky = -1;
p->sely = -1;
pad = PopupPad;
nall = min(max(dc->nkouho, 0), Maxdisp);
npre = dc->pre.n != 0;
if((nall == 0 && !npre) || areaw <= 0 || areah <= 0)
return;
/* Rows first; a page marker when they do not all fit; and in a
* sliver of a work area the selected row alone, unpadded. */
/* As many rows as fit, and a page marker when they do not all. */
total = max(dc->total, dc->first + nall);
markrow = dc->first > 0 || nall < total;
p->n = fitrows(areah, npre, markrow, nall);
@@ -142,15 +140,8 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
markrow = 1;
p->n = fitrows(areah, npre, markrow, nall);
}
if(p->n == 0 && markrow){
markrow = 0;
p->n = fitrows(areah, npre, markrow, nall);
}
if(p->n == 0 && nall != 0){
npre = 0;
pad = 0;
p->n = max(min(nall, areah / Fontsz), 1);
}
if(p->n == 0 && !npre)
return;
if(p->n != 0 && dc->sel >= p->n)
p->row0 = min(dc->sel - p->n + 1, nall - p->n);
first = dc->first + p->row0;
@@ -171,7 +162,7 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
}
p->w = min(width, areaw);
y = pad;
y = PopupPad;
if(npre){
p->prey = y;
y += Fontsz;
@@ -188,7 +179,7 @@ popuplayout(Drawcmd *dc, int areaw, int areah, Popup *p)
p->marky = y;
y += Fontsz;
}
p->h = min(y + pad, areah);
p->h = min(y + PopupPad, areah);
p->textw = max(p->w - 2*PopupPad - PopupNumw, 0);
if(markrow){
p->markw = min(p->markw, max(p->w - 2*PopupPad, 0));
@@ -206,10 +197,6 @@ popupdraw(u32int *img, Drawcmd *dc, Popup *p)
u32int color;
int i, j, y;
if(img == nil || p->n < 0 || p->n > Maxdisp ||
p->row0 < 0 || p->row0 + p->n > dc->nkouho ||
p->w <= 0 || p->h <= 0 || p->h > Imgh)
return;
fill(img, p->w*p->h, Colbg);
if(p->prey >= 0)
textdraw(img, p->w, p->h, PopupPad, p->prey,

View File

@@ -189,10 +189,7 @@ font_render(struct ct *t)
buf = mem + Guard;
fillpixels(buf, Testn, Colbg);
a = mkstr("A");
if(!CT_CHECK(t, textinit())){
free(mem);
return;
}
textinit();
for(i = 0; i < nelem(plain); i++)
checktext(t, buf, plain[i]);
@@ -257,6 +254,4 @@ font_render(struct ct *t)
checkguards(t, mem);
free(mem);
textclose();
textclose();
CT_EQ_INT(t, 0, textwidth(&a));
}

View File

@@ -2,6 +2,9 @@
#include "fn.h"
#include "test.h"
/* A popup with every section, tall enough for the tests. */
#define Imgh (2*PopupPad + (Maxdisp + 2)*Fontsz + PopupSep)
enum {
Rgbmask = 0xffffff,
};
@@ -40,9 +43,6 @@ checkpadding(struct ct *t, u32int *img, Popup *p)
void
popup_layout(struct ct *t)
{
int tinyheight[] = {
1, PopupPad, 2*PopupPad + Fontsz - 1,
};
static const struct {
int first;
int shown;
@@ -132,8 +132,7 @@ popup_layout(struct ct *t)
CT_EQ_INT(t, 100, x);
CT_EQ_INT(t, 200, y);
if(!CT_CHECK(t, textinit()))
return;
textinit();
/* Empty sections reserve nothing. */
memset(&dc, 0, sizeof dc);
@@ -320,24 +319,5 @@ popup_layout(struct ct *t)
CT_CHECK(t, numink > 0); /* full-width */
CT_CHECK(t, markink > 0); /* 9-9/9 */
/* The selected row wins over preedit and footer in one-row height. */
dc.pre = mkstr("preedit");
popuplayout(&dc, PopupBasew, 2*PopupPad + Fontsz, &p);
CT_EQ_INT(t, -1, p.prey);
CT_EQ_INT(t, 1, p.n);
CT_EQ_INT(t, Maxdisp - 1, p.row0);
CT_EQ_INT(t, -1, p.marky);
/* With less than one padded row, selected content starts at the top. */
for(i = 0; i < nelem(tinyheight); i++){
popuplayout(&dc, PopupBasew, tinyheight[i], &p);
CT_EQ_INT(t, min(tinyheight[i], Fontsz), p.h);
CT_EQ_INT(t, 0, p.rowsy);
CT_EQ_INT(t, 0, p.sely);
CT_EQ_INT(t, 1, p.n);
CT_EQ_INT(t, Maxdisp - 1, p.row0);
popupdraw(img, &dc, &p);
CT_EQ_UINT(t, Colsel, pixel(img, &p, p.w - PopupPad - 1, 0));
}
free(img);
}

6
win.c
View File

@@ -250,11 +250,7 @@ wininit(void)
win, type, XCB_ATOM_ATOM, 32, 1, &tooltip);
gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, win, 0, nil);
if(!textinit()){
fprint(2, "strans: popup disabled: no usable fonts\n");
wincleanup();
return 0;
}
textinit();
return 1;
}