commit: b149460b04b6f7e3fcf9eb8d4651ca369a649783
parent: 099ca0590dceac9698d3e959f2aceff24d353af3
author: Chris Noxz <chris@noxz.tech>
date: Wed, 13 Dec 2023 20:20:12 +0100
Sync with suckless.org up to 230922
7 files changed, 122 insertions(+), 88 deletions(-)
diff --git a/Makefile b/Makefile
@@ -6,13 +6,7 @@ include config.mk
SRC = drw.c dmenu.c stest.c util.c
OBJ = $(SRC:.c=.o)
-all: options dmenu stest
-
-options:
- @echo dmenu build options:
- @echo "CFLAGS = $(CFLAGS)"
- @echo "LDFLAGS = $(LDFLAGS)"
- @echo "CC = $(CC)"
+all: dmenu stest
.c.o:
$(CC) -c $(CFLAGS) $<
@@ -61,4 +55,4 @@ uninstall:
$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
$(DESTDIR)$(MANPREFIX)/man1/stest.1
-.PHONY: all options clean dist install uninstall
+.PHONY: all clean dist install uninstall
diff --git a/config.mk b/config.mk
@@ -1,5 +1,5 @@
# dmenu version
-VERSION = 5.1
+VERSION = 5.2
# 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
@@ -23,7 +23,6 @@
/* macros */
#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
* MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
-#define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
@@ -64,6 +63,13 @@ static Clr *scheme[SchemeLast];
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
+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)
{
@@ -88,10 +94,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;
}
@@ -103,6 +109,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);
@@ -239,8 +248,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(">") - TEXTW(matched)));
+ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">") - TEXTW(matched)));
if (next) {
w = TEXTW(">");
drw_setscheme(drw, scheme[SchemeNorm]);
@@ -387,7 +395,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;
@@ -469,19 +477,19 @@ movewordedge(int dir)
static void
keypress(XKeyEvent *ev)
{
- char buf[32];
+ char buf[64];
int len;
- KeySym ksym;
+ KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
- case XLookupChars:
+ case XLookupChars: /* composed string from input method */
goto insert;
case XLookupKeySym:
- case XLookupBoth:
+ case XLookupBoth: /* a KeySym and a string are returned: use keysym */
break;
}
@@ -560,7 +568,7 @@ keypress(XKeyEvent *ev)
switch(ksym) {
default:
insert:
- if (!iscntrl(*buf))
+ if (!iscntrl((unsigned char)*buf))
insert(buf, len);
break;
case XK_Delete:
@@ -665,9 +673,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;
}
@@ -697,9 +705,9 @@ 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, itemsiz = 0, linesiz = 0;
+ ssize_t len;
if (passwd) {
inputw = lines = 0;
@@ -707,25 +715,22 @@ readstdin(void)
}
/* read each line from stdin and add it to the item list */
- for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
- 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);
- items[i].out = 0;
- items[i].idx = i;
- drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
- if (tmpmax > inputw) {
- inputw = tmpmax;
- imax = i;
+ for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
+ if (i + 1 >= itemsiz) {
+ itemsiz += 256;
+ if (!(items = realloc(items, itemsiz * sizeof(*items))))
+ die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
}
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (!(items[i].text = strdup(line)))
+ die("strdup:");
+
+ items[i].out = 0;
}
+ free(line);
if (items)
items[i].text = NULL;
- inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i);
}
@@ -817,7 +822,7 @@ setup(void)
/* no focused window is on screen, so use pointer location instead */
if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for (i = 0; i < n; i++)
- if (INTERSECT(x, y, 1, 1, info[i]))
+ if (INTERSECT(x, y, 1, 1, info[i]) != 0)
break;
x = info[i].x_org;
@@ -835,14 +840,14 @@ 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 */
swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
- win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
@@ -857,6 +862,7 @@ setup(void)
XMapRaised(dpy, win);
if (embed) {
+ XReparentWindow(dpy, win, parentwin, x, y);
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i)
@@ -872,10 +878,10 @@ setup(void)
static void
usage(void)
{
- fputs("usage: dmenu [-bfiPv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
- " [-h height]\n"
- " [-nb color] [-nf color] [-sb color] [-sf color]\n"
- " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr);
+ die("usage: dmenu [-bfiPv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-h height]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"
+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr);
exit(1);
}
diff --git a/drw.c b/drw.c
@@ -238,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 ty, ellipsis_x = 0;
+ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
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;
@@ -251,13 +249,15 @@ 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. */
+ static unsigned int nomatches[128], ellipsis_width;
- 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);
@@ -269,8 +269,11 @@ 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, "\u2026");
while (1) {
utf8strlen = 0;
+ ew = ellipsis_len = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
@@ -278,9 +281,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;
}
@@ -288,36 +309,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, "\u2026", invert);
- if (!*text) {
+ if (!*text || overflow) {
break;
} else if (nextfont) {
charexists = 0;
@@ -327,6 +337,15 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
+ hash = (unsigned int)utf8codepoint;
+ hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
+ hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
+ h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
+ h1 = (hash >> 17) % LENGTH(nomatches);
+ /* avoid expensive XftFontMatch call when we know we won't find a match */
+ if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
+ goto no_match;
+
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
@@ -354,6 +373,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[nomatches[h0] ? h1 : h0] = utf8codepoint;
+no_match:
usedfont = drw->fonts;
}
}
@@ -383,6 +404,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
@@ -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
@@ -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;
+}
diff --git a/util.h b/util.h
@@ -3,6 +3,7 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);