diff --git a/PKGBUILD b/PKGBUILD index 44a319f..5476feb 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,9 +1,11 @@ # Maintainer: Jef Roosens -pkgname="jjr-dmenu" -pkgver="5.2" +pkgname=jjr-dmenu +pkgver=5.3 pkgrel=1 pkgdesc="Chewing_Bever's custom build of Suckless's dmenu." +url="https://git.rustybever.be/Chewing_Bever/dmenu" + arch=( "x86_64" "x86_64_tigerlake" @@ -11,11 +13,10 @@ arch=( "aarch64" ) -url="https://git.rustybever.be/Chewing_Bever/dmenu" license=("MIT") -depends=("libxinerama" "libxft" "fontconfig" "freetype2") -makedepends=("glibc" "gcc") +depends=("glibc" "libxinerama" "libxft" "fontconfig" "freetype2") +makedepends=("gcc") provides=("dmenu") conflicts=("dmenu-git") @@ -24,19 +25,19 @@ source=("arg.h" "config.h" "config.mk" "dmenu.1" "dmenu.c" "dmenu_path.sh" "stest.c" "util.c" "util.h") sha256sums=('9ed2b2e3396fdebc8ecab2cdbb09db115cca015f63ec0443b8ccc56340b6b03c' '9ae33f74aed02823abb8381e553d38455541ce3b6a0d642282b693992e71caef' - 'f6de4c72dab3b9bef214bd52e6a7dff22e87a3118abaffd26ec1ae589cf68b3f' + '5a6066f1cf97aaee4792708893faa1f16fad39d4dce22c79ccfb7590349fe0cc' '9f749419697381a1db77e9b23cb41629808fd7e451d475c9b5ee0e88bee803f6' - '5b2a53db7ba608d392efaa96be5530665b46107dc4e0ee1a864745a6e8a8587d' + 'dc93fd213531f01c968ec073382d4c91c14a3fb2464e1a62e23e94653a74fb81' 'a3f19e648e20eb80532dfe87e9bd9feeaa92df838e6ecc061f274c9da579d617' '564035fd8a8ade6504521d98fa10e8732e135403d546d04ca77316fe4a67b21c' - 'a7c7358ce13c8571044b79d5d91d01314d9174793474a27faa37d23c75ab603f' - 'c35796210ea8410ae096025cc0d7996dccb3d9936306dff7531a0bb253e079f5' + '8c9f81b98f447440cf59dd3eefda99eec4e7733266ad0c072612163ccff4e1df' + '7ed1cf72bfcb0f5444f338293aef74c455ce4ed5463f171a48358f64d7835d49' '9f8e922fcfc18ffcc4975dd0b1bee38302bd4e0c538251dfef6d77cd7ed59c89' 'c32b5173eddf376296af085725cb878e4ca2603d4fda8bba3faac6a2bb1b4390' 'ec98e8759049af796598f0790752a21a69eaea1d438b09c39922d4a9f4a7cfe2' '00d3389ef8858a565cf3fb3445620b90eb67d1a9b31434f4d54b05ed6c5abefb' 'ba76404e61abbd734e315999ffe4e883419c4c69ff17a5d4d88a5cd11dd4b055' - 'dec870d0b5834c9008ff62f495710daf524cffdf12d0cf8ba4fadf5643623987' + '1c093b9969daaf3e1a001b924c6c2da3770993916b444dfe65fe39aa0090aa1a' '1196a7b6efbf4cb3f5c435fffd72e7647f977483845d5c78c1c48d9ab8b96819') build() { @@ -56,3 +57,5 @@ package() { # install $installopts "$docdir" README.terminfo.rst # install $installopts "$shrdir/$pkgname" "$_sourcedir/st.info" } + +# vim: ft=bash diff --git a/config.mk b/config.mk index ddb33fa..0ea1efd 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # dmenu version -VERSION = 5.2 +VERSION = 5.3 # paths PREFIX = /usr/local @@ -17,6 +17,7 @@ FREETYPELIBS = -lfontconfig -lXft FREETYPEINC = /usr/include/freetype2 # OpenBSD (uncomment) #FREETYPEINC = $(X11INC)/freetype2 +#MANPREFIX = ${PREFIX}/man # includes and libs INCS = -I$(X11INC) -I$(FREETYPEINC) diff --git a/dmenu.c b/dmenu.c index b264d53..0363aa0 100644 --- a/dmenu.c +++ b/dmenu.c @@ -61,6 +61,13 @@ static char * cistrstr(const char *s, const char *sub); static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp; static char *(*fstrstr)(const char *, const char *) = cistrstr; +static unsigned int +textw_clamp(const char *str, unsigned int n) +{ + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; + return MIN(w, n); +} + static void appenditem(struct item *item, struct item **list, struct item **last) { @@ -85,10 +92,10 @@ calcoffsets(void) n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); /* calculate which items will begin the next page and previous page */ for (i = 0, next = curr; next; next = next->right) - if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) break; for (i = 0, prev = curr; prev && prev->left; prev = prev->left) - if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) break; } @@ -100,6 +107,9 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); for (i = 0; i < SchemeLast; i++) free(scheme[i]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); + free(items); drw_free(drw); XSync(dpy, False); XCloseDisplay(dpy); @@ -175,7 +185,7 @@ drawmenu(void) } x += w; for (item = curr; item != next; item = item->right) - x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); if (next) { w = TEXTW(">"); drw_setscheme(drw, scheme[SchemeNorm]); @@ -320,7 +330,7 @@ match(void) /* separate input text into tokens to be matched individually */ for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) - die("cannot realloc %u bytes:", tokn * sizeof *tokv); + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); len = tokc ? strlen(tokv[0]) : 0; matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; @@ -493,7 +503,7 @@ keypress(XKeyEvent *ev) switch(ksym) { default: insert: - if (!iscntrl(*buf)) + if (!iscntrl((unsigned char)*buf)) insert(buf, len); break; case XK_Delete: @@ -595,9 +605,9 @@ insert: case XK_Tab: if (!sel) return; - strncpy(text, sel->text, sizeof text - 1); - text[sizeof text - 1] = '\0'; - cursor = strlen(text); + cursor = strnlen(sel->text, sizeof text - 1); + memcpy(text, sel->text, cursor); + text[cursor] = '\0'; match(); break; } @@ -627,29 +637,22 @@ paste(void) static void readstdin(void) { - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; + char *line = NULL; + size_t i, junk, size = 0; + ssize_t len; /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { if (i + 1 >= size / sizeof *items) if (!(items = realloc(items, (size += BUFSIZ)))) - die("cannot realloc %u bytes:", size); - if ((p = strchr(buf, '\n'))) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf) + 1); + die("cannot realloc %zu bytes:", size); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { - inputw = tmpmax; - imax = i; - } } if (items) items[i].text = NULL; - inputw = items ? TEXTW(items[imax].text) : 0; lines = MIN(lines, i); } @@ -758,7 +761,7 @@ setup(void) mw = wa.width; } promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - inputw = MIN(inputw, mw/3); + inputw = mw / 3; /* input width: ~33% of monitor width */ match(); /* create menu window */ @@ -795,9 +798,8 @@ setup(void) static void usage(void) { - fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); - exit(1); + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); } int diff --git a/drw.c b/drw.c index 4cdbcbe..a58a2b4 100644 --- a/drw.c +++ b/drw.c @@ -133,19 +133,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) die("no font specified."); } - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - font = ecalloc(1, sizeof(Fnt)); font->xfont = xfont; font->pattern = pattern; @@ -251,12 +238,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { - char buf[1024]; - int ty; - unsigned int ew; + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; XftDraw *d = NULL; Fnt *usedfont, *curfont, *nextfont; - size_t i, len; int utf8strlen, utf8charlen, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; @@ -264,13 +249,17 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp FcPattern *fcpattern; FcPattern *match; XftResult result; - int charexists = 0; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; - if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) return 0; if (!render) { - w = ~w; + w = invert ? invert : ~invert; } else { XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); @@ -282,8 +271,10 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); while (1) { - utf8strlen = 0; + ew = ellipsis_len = utf8strlen = 0; utf8str = text; nextfont = NULL; while (*text) { @@ -291,9 +282,27 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp for (curfont = drw->fonts; curfont; curfont = curfont->next) { charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { - if (curfont == usedfont) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { utf8strlen += utf8charlen; text += utf8charlen; + ew += tmpw; } else { nextfont = curfont; } @@ -301,36 +310,25 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } } - if (!charexists || nextfont) + if (overflow || !charexists || nextfont) break; else charexists = 0; } if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) - drw_font_getexts(usedfont, utf8str, len, &ew, NULL); - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); } + x += ew; + w -= ew; } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); - if (!*text) { + if (!*text || overflow) { break; } else if (nextfont) { charexists = 0; @@ -340,6 +338,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp * character must be drawn. */ charexists = 1; + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); @@ -351,7 +355,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp fcpattern = FcPatternDuplicate(drw->fonts->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); @@ -368,6 +371,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp curfont->next = usedfont; } else { xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: usedfont = drw->fonts; } } @@ -397,6 +402,15 @@ drw_fontset_getwidth(Drw *drw, const char *text) return drw_text(drw, 0, 0, 0, 0, 0, text, 0); } +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) { diff --git a/drw.h b/drw.h index 4c67419..fd7631b 100644 --- a/drw.h +++ b/drw.h @@ -35,6 +35,7 @@ void drw_free(Drw *drw); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); void drw_fontset_free(Fnt* set); unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); /* Colorscheme abstraction */ diff --git a/util.c b/util.c index fe044fc..96b82c9 100644 --- a/util.c +++ b/util.c @@ -6,18 +6,9 @@ #include "util.h" -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - die("calloc:"); - return p; -} - void -die(const char *fmt, ...) { +die(const char *fmt, ...) +{ va_list ap; va_start(ap, fmt); @@ -33,3 +24,13 @@ die(const char *fmt, ...) { exit(1); } + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +}