diff options
author | zachir <zachir@librem.one> | 2023-02-21 14:30:34 -0600 |
---|---|---|
committer | zachir <zachir@librem.one> | 2023-02-21 14:30:34 -0600 |
commit | ee4eee0e05b567fe72f94eabcec9656a096625bc (patch) | |
tree | af857dbbee2fae2f7ff2674750c0871b24c753b5 /dmenu-5.1.diff | |
parent | 0e8723fdc24c099954f6356f22d1a1df47eddb20 (diff) | |
parent | 96e4a549ce8b3da7d5d339a94487bb2849520ae7 (diff) |
merge master to version 5.25.2
Diffstat (limited to 'dmenu-5.1.diff')
-rw-r--r-- | dmenu-5.1.diff | 3199 |
1 files changed, 0 insertions, 3199 deletions
diff --git a/dmenu-5.1.diff b/dmenu-5.1.diff deleted file mode 100644 index f062280..0000000 --- a/dmenu-5.1.diff +++ /dev/null @@ -1,3199 +0,0 @@ -diff --git a/config.def.h b/config.def.h -index 1edb647..3fb3def 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -2,22 +2,33 @@ - /* Default settings; can be overriden by command line. */ - - static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -+static int centered = 0; /* -c option; centers dmenu on screen */ -+static int min_width = 500; /* minimum width when centered */ -+static int fuzzy = 0; /* -F option; if 0, dmenu doesn't use fuzzy matching */ - /* -fn option overrides fonts[0]; default X11 font or font set */ --static const char *fonts[] = { -+static char *fonts[] = { - "monospace:size=10" - }; - static const char *prompt = NULL; /* -p option; prompt to the left of input field */ --static const char *colors[SchemeLast][2] = { -+static char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, -+ [SchemeSelHighlight] = { "#ffc978", "#005577" }, -+ [SchemeNormHighlight] = { "#ffc978", "#222222" }, - [SchemeOut] = { "#000000", "#00ffff" }, - }; - /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ - static unsigned int lines = 0; -+/* -h option; minimum height of a menu line */ -+static unsigned int lineheight = 0; -+static unsigned int min_lineheight = 8; - - /* - * Characters not considered part of a word while deleting words - * for example: " /?\"&[]" - */ - static const char worddelimiters[] = " "; -+ -+/* Size of the window border */ -+static unsigned int border_width = 0; -diff --git a/config.mk b/config.mk -index 0df3fc8..3423b6a 100644 ---- a/config.mk -+++ b/config.mk -@@ -20,7 +20,7 @@ FREETYPEINC = /usr/include/freetype2 - - # includes and libs - INCS = -I$(X11INC) -I$(FREETYPEINC) --LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm - - # flags - CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) -diff --git a/dmenu.1 b/dmenu.1 -index 323f93c..1602793 100644 ---- a/dmenu.1 -+++ b/dmenu.1 -@@ -3,9 +3,11 @@ - dmenu \- dynamic menu - .SH SYNOPSIS - .B dmenu --.RB [ \-bfiv ] -+.RB [ \-bFfirvP ] - .RB [ \-l - .IR lines ] -+.RB [ \-h -+.IR height ] - .RB [ \-m - .IR monitor ] - .RB [ \-p -@@ -20,8 +22,20 @@ dmenu \- dynamic menu - .IR color ] - .RB [ \-sf - .IR color ] -+.RB [ \-nhb -+.IR color ] -+.RB [ \-nhf -+.IR color ] -+.RB [ \-shb -+.IR color ] -+.RB [ \-shf -+.IR color ] - .RB [ \-w - .IR windowid ] -+.RB [ \-d -+.IR separator ] -+.RB [ \-D -+.IR separator ] - .P - .BR dmenu_run " ..." - .SH DESCRIPTION -@@ -40,6 +54,12 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. - .B \-b - dmenu appears at the bottom of the screen. - .TP -+.B \-c -+dmenu appears centered on the screen. -+.TP -+.B \-F -+dmenu uses fuzzy matching -+.TP - .B \-f - dmenu grabs the keyboard before reading stdin if not reading from a tty. This - is faster, but will lock up X until stdin reaches end\-of\-file. -@@ -47,9 +67,18 @@ is faster, but will lock up X until stdin reaches end\-of\-file. - .B \-i - dmenu matches menu items case insensitively. - .TP -+.B \-P -+dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. -+.TP -+.B \-r -+dmenu will reject any input which would result in no matching option left. -+.TP - .BI \-l " lines" - dmenu lists items vertically, with the given number of lines. - .TP -+.BI \-h " height" -+dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. -+.TP - .BI \-m " monitor" - dmenu is displayed on the monitor number supplied. Monitor numbers are starting - from 0. -@@ -75,11 +104,31 @@ defines the selected background color. - .BI \-sf " color" - defines the selected foreground color. - .TP -+.BI \-nhb " color" -+defines the normal highlight background color. -+.TP -+.BI \-nhf " color" -+defines the normal highlight foreground color. -+.TP -+.BI \-shb " color" -+defines the selected highlight background color. -+.TP -+.BI \-shf " color" -+defines the selected highlight foreground color. -+.TP - .B \-v - prints version information to stdout, then exits. - .TP - .BI \-w " windowid" - embed into windowid. -+.TP -+.BI \-d " separator" -+separate the input into two halves on the first occurrence of the given charcter. -+Display only the first half in dmenu and print the second half to stdout upon selection. -+Appending '|' to the separator reverses the display/printing order. -+.TP -+.BI \-D " separator" -+same as \-d but separate based on the last occurrence. - .SH USAGE - dmenu is completely controlled by the keyboard. Items are selected using the - arrow keys, page up, page down, home, and end. -diff --git a/dmenu.c b/dmenu.c -index d95e6c6..d304e3a 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -1,6 +1,7 @@ - /* See LICENSE file for copyright and license details. */ - #include <ctype.h> - #include <locale.h> -+#include <math.h> - #include <stdio.h> - #include <stdlib.h> - #include <string.h> -@@ -15,6 +16,7 @@ - #include <X11/extensions/Xinerama.h> - #endif - #include <X11/Xft/Xft.h> -+#include <X11/Xresource.h> - - #include "drw.h" - #include "util.h" -@@ -22,23 +24,33 @@ - /* 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 BOOL_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 */ --enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ -+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, -+ SchemeOut, SchemeLast }; /* color schemes */ -+ - - struct item { - char *text; -+ char *text_output; - struct item *left, *right; - int out; -+ double distance; - }; - - static char text[BUFSIZ] = ""; - static char *embed; -+static char separator; -+static int separator_greedy; -+static int separator_reverse; - static int bh, mw, mh; --static int inputw = 0, promptw; -+static int inputw = 0, promptw, passwd = 0; - static int lrpad; /* sum of left and right padding */ -+static int reject_no_match = 0; - static size_t cursor; - static struct item *items = NULL; - static struct item *matches, *matchend; -@@ -53,6 +65,10 @@ static XIC xic; - static Drw *drw; - static Clr *scheme[SchemeLast]; - -+/* Temporary arrays to allow overriding xresources values */ -+static char *colortemp[4]; -+static char *tempfonts; -+ - #include "config.h" - - static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -@@ -89,6 +105,15 @@ calcoffsets(void) - break; - } - -+static int -+max_textw(void) -+{ -+ int len = 0; -+ for (struct item *item = items; item && item->text; item++) -+ len = MAX(TEXTW(item->text), len); -+ return len; -+} -+ - static void - cleanup(void) - { -@@ -120,9 +145,49 @@ cistrstr(const char *h, const char *n) - return NULL; - } - -+static void -+drawhighlights(struct item *item, int x, int y, int maxw) -+{ -+ int i, indent; -+ char *highlight; -+ char c; -+ -+ if (!(strlen(item->text) && strlen(text))) -+ return; -+ -+ drw_setscheme(drw, scheme[item == sel -+ ? SchemeSelHighlight -+ : SchemeNormHighlight]); -+ for (i = 0, highlight = item->text; *highlight && text[i];) { -+ if (!fstrncmp(&(*highlight), &text[i], 1)) { -+ /* get indentation */ -+ c = *highlight; -+ *highlight = '\0'; -+ indent = TEXTW(item->text); -+ *highlight = c; -+ -+ /* highlight character */ -+ c = highlight[1]; -+ highlight[1] = '\0'; -+ drw_text( -+ drw, -+ x + indent - (lrpad / 2), -+ y, -+ MIN(maxw - indent, TEXTW(highlight) - lrpad), -+ bh, 0, highlight, 0 -+ ); -+ highlight[1] = c; -+ i++; -+ } -+ highlight++; -+ } -+} -+ -+ - static int - drawitem(struct item *item, int x, int y, int w) - { -+ int r; - if (item == sel) - drw_setscheme(drw, scheme[SchemeSel]); - else if (item->out) -@@ -130,7 +195,9 @@ drawitem(struct item *item, int x, int y, int w) - else - drw_setscheme(drw, scheme[SchemeNorm]); - -- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -+ drawhighlights(item, x, y, w); -+ return r; - } - - static void -@@ -138,7 +205,8 @@ drawmenu(void) - { - unsigned int curpos; - struct item *item; -- int x = 0, y = 0, w; -+ int x = 0, y = 0, fh = drw->fonts->h, w; -+ char *censort; - - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1); -@@ -150,12 +218,17 @@ drawmenu(void) - /* draw input field */ - w = (lines > 0 || !matches) ? mw - x : inputw; - drw_setscheme(drw, scheme[SchemeNorm]); -- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); -+ if (passwd) { -+ censort = ecalloc(1, sizeof(text)); -+ memset(censort, '.', strlen(text)); -+ drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); -+ free(censort); -+ } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); - - curpos = TEXTW(text) - TEXTW(&text[cursor]); - if ((curpos += lrpad / 2 - 1) < w) { - drw_setscheme(drw, scheme[SchemeNorm]); -- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); -+ drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0); - } - - if (lines > 0) { -@@ -217,9 +290,94 @@ grabkeyboard(void) - die("cannot grab keyboard"); - } - -+int -+compare_distance(const void *a, const void *b) -+{ -+ struct item *da = *(struct item **) a; -+ struct item *db = *(struct item **) b; -+ -+ if (!db) -+ return 1; -+ if (!da) -+ return -1; -+ -+ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; -+} -+ -+void -+fuzzymatch(void) -+{ -+ /* bang - we have so much memory */ -+ struct item *it; -+ struct item **fuzzymatches = NULL; -+ char c; -+ int number_of_matches = 0, i, pidx, sidx, eidx; -+ int text_len = strlen(text), itext_len; -+ -+ matches = matchend = NULL; -+ -+ /* walk through all items */ -+ for (it = items; it && it->text; it++) { -+ if (text_len) { -+ itext_len = strlen(it->text); -+ pidx = 0; /* pointer */ -+ sidx = eidx = -1; /* start of match, end of match */ -+ /* walk through item text */ -+ for (i = 0; i < itext_len && (c = it->text[i]); i++) { -+ /* fuzzy match pattern */ -+ if (!fstrncmp(&text[pidx], &c, 1)) { -+ if(sidx == -1) -+ sidx = i; -+ pidx++; -+ if (pidx == text_len) { -+ eidx = i; -+ break; -+ } -+ } -+ } -+ /* build list of matches */ -+ if (eidx != -1) { -+ /* compute distance */ -+ /* add penalty if match starts late (log(sidx+2)) -+ * add penalty for long a match without many matching characters */ -+ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); -+ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ -+ appenditem(it, &matches, &matchend); -+ number_of_matches++; -+ } -+ } else { -+ appenditem(it, &matches, &matchend); -+ } -+ } -+ -+ if (number_of_matches) { -+ /* initialize array with matches */ -+ if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) -+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); -+ for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { -+ fuzzymatches[i] = it; -+ } -+ /* sort matches according to distance */ -+ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); -+ /* rebuild list of matches */ -+ matches = matchend = NULL; -+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ -+ it->text; i++, it = fuzzymatches[i]) { -+ appenditem(it, &matches, &matchend); -+ } -+ free(fuzzymatches); -+ } -+ curr = sel = matches; -+ calcoffsets(); -+} -+ - static void - match(void) - { -+ if (fuzzy) { -+ fuzzymatch(); -+ return; -+ } - static char **tokv = NULL; - static int tokn = 0; - -@@ -276,12 +434,26 @@ insert(const char *str, ssize_t n) - { - if (strlen(text) + n > sizeof text - 1) - return; -+ -+ static char last[BUFSIZ] = ""; -+ if(reject_no_match) { -+ /* store last text value in case we need to revert it */ -+ memcpy(last, text, BUFSIZ); -+ } -+ - /* move existing text out of the way, insert new text, and update cursor */ - memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - if (n > 0) - memcpy(&text[cursor], str, n); - cursor += n; - match(); -+ -+ if(!matches && reject_no_match) { -+ /* revert to last text value if theres no match */ -+ memcpy(text, last, BUFSIZ); -+ cursor -= n; -+ match(); -+ } - } - - static size_t -@@ -480,7 +652,7 @@ insert: - break; - case XK_Return: - case XK_KP_Enter: -- puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); -+ puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); - if (!(ev->state & ControlMask)) { - cleanup(); - exit(0); -@@ -518,6 +690,156 @@ draw: - drawmenu(); - } - -+static void -+buttonpress(XEvent *e) -+{ -+ struct item *item; -+ XButtonPressedEvent *ev = &e->xbutton; -+ int x = 0, y = 0, h = bh, w; -+ -+ if (ev->window != win) -+ return; -+ -+ /* right-click: exit */ -+ if (ev->button == Button3) -+ exit(1); -+ -+ if (prompt && *prompt) -+ x += promptw; -+ -+ /* input field */ -+ w = (lines > 0 || !matches) ? mw - x : inputw; -+ -+ /* left-click on input: clear input, -+ * NOTE: if there is no left-arrow the space for < is reserved so -+ * add that to the input width */ -+ if (ev->button == Button1 && -+ ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + -+ ((!prev || !curr->left) ? TEXTW("<") : 0)) || -+ (lines > 0 && ev->y >= y && ev->y <= y + h))) { -+ insert(NULL, -cursor); -+ drawmenu(); -+ return; -+ } -+ /* middle-mouse click: paste selection */ -+ if (ev->button == Button2) { -+ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, -+ utf8, utf8, win, CurrentTime); -+ drawmenu(); -+ return; -+ } -+ /* scroll up */ -+ if (ev->button == Button4 && prev) { -+ sel = curr = prev; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ /* scroll down */ -+ if (ev->button == Button5 && next) { -+ sel = curr = next; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ if (ev->button != Button1) -+ return; -+ /* disabled below, needs to be fixed */ -+ /* -+ if (ev->state & ~ControlMask) -+ return; -+ */ -+ if (lines > 0) { -+ /* vertical list: (ctrl)left-click on item */ -+ w = mw - x; -+ for (item = curr; item != next; item = item->right) { -+ y += h; -+ if (ev->y >= y && ev->y <= (y + h)) { -+ puts(item->text); -+ if (!(ev->state & ControlMask)) -+ exit(0); -+ sel = item; -+ if (sel) { -+ sel->out = 1; -+ drawmenu(); -+ } -+ return; -+ } -+ } -+ } else if (matches) { -+ /* left-click on left arrow */ -+ x += inputw; -+ w = TEXTW("<"); -+ if (prev && curr->left) { -+ if (ev->x >= x && ev->x <= x + w) { -+ sel = curr = prev; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ /* horizontal list: (ctrl)left-click on item */ -+ for (item = curr; item != next; item = item->right) { -+ x += w; -+ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -+ if (ev->x >= x && ev->x <= x + w) { -+ puts(item->text); -+ if (!(ev->state & ControlMask)) -+ exit(0); -+ sel = item; -+ if (sel) { -+ sel->out = 1; -+ drawmenu(); -+ } -+ return; -+ } -+ } -+ /* left-click on right arrow */ -+ w = TEXTW(">"); -+ x = mw - w; -+ if (next && ev->x >= x && ev->x <= x + w) { -+ sel = curr = next; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+} -+ -+static void -+mousemove(XEvent *e) -+{ -+ struct item *item; -+ XPointerMovedEvent *ev = &e->xmotion; -+ int x = 0, y = 0, h = bh, w; -+ -+ if (lines > 0) { -+ w = mw - x; -+ for (item = curr; item != next; item = item->right) { -+ y += h; -+ if (ev->y >= y && ev->y <= (y + h)) { -+ sel = item; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ } else if (matches) { -+ x += inputw; -+ w = TEXTW("<"); -+ for (item = curr; item != next; item = item->right) { -+ x += w; -+ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -+ if (ev->x >= x && ev->x <= x + w) { -+ sel = item; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ } -+} -+ - static void - paste(void) - { -@@ -542,6 +864,11 @@ readstdin(void) - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; -+ if(passwd){ -+ inputw = lines = 0; -+ return; -+ } -+ - - /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { -@@ -552,6 +879,18 @@ readstdin(void) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf) + 1); -+ if (separator && (p = separator_greedy ? -+ strrchr(items[i].text, separator) : strchr(items[i].text, separator))) { -+ *p = '\0'; -+ items[i].text_output = ++p; -+ } else { -+ items[i].text_output = items[i].text; -+ } -+ if (separator_reverse) { -+ p = items[i].text; -+ items[i].text = items[i].text_output; -+ items[i].text_output = p; -+ } - items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { -@@ -579,6 +918,12 @@ run(void) - break; - cleanup(); - exit(1); -+ case ButtonPress: -+ buttonpress(&ev); -+ break; -+ case MotionNotify: -+ mousemove(&ev); -+ break; - case Expose: - if (ev.xexpose.count == 0) - drw_map(drw, win, 0, 0, mw, mh); -@@ -619,16 +964,23 @@ setup(void) - int a, di, n, area = 0; - #endif - /* init appearance */ -- for (j = 0; j < SchemeLast; j++) -- scheme[j] = drw_scm_create(drw, colors[j], 2); -+ for (j = 0; j < SchemeLast; j++) { -+ scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); -+ } -+ for (j = 0; j < SchemeOut; ++j) { -+ for (i = 0; i < 2; ++i) -+ free(colors[j][i]); -+ } - - clip = XInternAtom(dpy, "CLIPBOARD", False); - utf8 = XInternAtom(dpy, "UTF8_STRING", False); - - /* calculate menu geometry */ - bh = drw->fonts->h + 2; -+ bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ - lines = MAX(lines, 0); - mh = (lines + 1) * bh; -+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - #ifdef XINERAMA - i = 0; - if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { -@@ -652,12 +1004,19 @@ 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 (BOOL_INTERSECT(x, y, 1, 1, info[i])) - break; - -- x = info[i].x_org; -- y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -- mw = info[i].width; -+ if (centered) { -+ mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); -+ x = info[i].x_org + ((info[i].width - mw) / 2); -+ y = info[i].y_org + ((info[i].height - mh) / 2); -+ } else { -+ x = info[i].x_org; -+ y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -+ mw = info[i].width; -+ } -+ - XFree(info); - } else - #endif -@@ -665,21 +1024,30 @@ setup(void) - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - die("could not get embedding window attributes: 0x%lx", - parentwin); -- x = 0; -- y = topbar ? 0 : wa.height - mh; -- mw = wa.width; -+ -+ if (centered) { -+ mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); -+ x = (wa.width - mw) / 2; -+ y = (wa.height - mh) / 2; -+ } else { -+ x = 0; -+ y = topbar ? 0 : wa.height - mh; -+ mw = wa.width; -+ } - } -- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - inputw = MIN(inputw, mw/3); - 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, -+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | -+ ButtonPressMask | PointerMotionMask; -+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, - CopyFromParent, CopyFromParent, CopyFromParent, - CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); -+ if (border_width) -+ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); - XSetClassHint(dpy, win, &ch); - - -@@ -707,11 +1075,64 @@ 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); -+ fputs("usage: dmenu [-bfirvP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -+ " [-nb color] [-nf color] [-sb color] [-sf color] [-bw width]\n" -+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n" -+ " [-d separator] [-D separator]\n", stderr); - exit(1); - } - -+void -+readxresources(void) { -+ XrmInitialize(); -+ -+ char* xrm; -+ if ((xrm = XResourceManagerString(drw->dpy))) { -+ char *type; -+ XrmDatabase xdb = XrmGetStringDatabase(xrm); -+ XrmValue xval; -+ -+ if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval)) -+ fonts[0] = strdup(xval.addr); -+ else -+ fonts[0] = strdup(fonts[0]); -+ if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval)) -+ colors[SchemeNorm][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeNorm][ColBg] = strdup(colors[SchemeNorm][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval)) -+ colors[SchemeNorm][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeNorm][ColFg] = strdup(colors[SchemeNorm][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval)) -+ colors[SchemeSel][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeSel][ColBg] = strdup(colors[SchemeSel][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval)) -+ colors[SchemeSel][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeSel][ColFg] = strdup(colors[SchemeSel][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.selhibackground", "*", &type, &xval)) -+ colors[SchemeSelHighlight][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeSelHighlight][ColBg] = strdup(colors[SchemeSel][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.selhiforeground", "*", &type, &xval)) -+ colors[SchemeSelHighlight][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeSelHighlight][ColFg] = strdup(colors[SchemeSel][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.hibackground", "*", &type, &xval)) -+ colors[SchemeNormHighlight][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeNormHighlight][ColBg] = strdup(colors[SchemeNorm][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.hiforeground", "*", &type, &xval)) -+ colors[SchemeNormHighlight][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeNormHighlight][ColFg] = strdup(colors[SchemeNorm][ColFg]); -+ -+ XrmDestroyDatabase(xdb); -+ } -+} -+ - int - main(int argc, char *argv[]) - { -@@ -727,30 +1148,56 @@ main(int argc, char *argv[]) - topbar = 0; - else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ - fast = 1; -+ else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ -+ centered = 1; -+ else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */ -+ fuzzy = 1; - else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ - fstrncmp = strncasecmp; - fstrstr = cistrstr; -- } else if (i + 1 == argc) -+ } else if (!strcmp(argv[i], "-P")) /* is the input a password */ -+ passwd = 1; -+ else if (!strcmp(argv[i], "-r")) /* reject input which results in no match */ -+ reject_no_match = 1; -+ else if (i + 1 == argc) - usage(); - /* these options take one argument */ - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ - lines = atoi(argv[++i]); -+ else if (!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ -+ lineheight = atoi(argv[++i]); -+ lineheight = MAX(lineheight, min_lineheight); -+ } - else if (!strcmp(argv[i], "-m")) - mon = atoi(argv[++i]); - else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ - prompt = argv[++i]; - else if (!strcmp(argv[i], "-fn")) /* font or font set */ -- fonts[0] = argv[++i]; -+ tempfonts = argv[++i]; - else if (!strcmp(argv[i], "-nb")) /* normal background color */ -- colors[SchemeNorm][ColBg] = argv[++i]; -+ colortemp[0] = argv[++i]; - else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ -- colors[SchemeNorm][ColFg] = argv[++i]; -+ colortemp[1] = argv[++i]; - else if (!strcmp(argv[i], "-sb")) /* selected background color */ -- colors[SchemeSel][ColBg] = argv[++i]; -+ colortemp[2] = argv[++i]; - else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ -- colors[SchemeSel][ColFg] = argv[++i]; -+ colortemp[3] = argv[++i]; -+ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ -+ colors[SchemeNormHighlight][ColBg] = argv[++i]; -+ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ -+ colors[SchemeNormHighlight][ColFg] = argv[++i]; -+ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ -+ colors[SchemeSelHighlight][ColBg] = argv[++i]; -+ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ -+ colors[SchemeSelHighlight][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-w")) /* embedding window id */ - embed = argv[++i]; -+ else if (!strcmp(argv[i], "-d") || /* field separator */ -+ (separator_greedy = !strcmp(argv[i], "-D"))) { -+ separator = argv[++i][0]; -+ separator_reverse = argv[i][1] == '|'; -+ } else if (!strcmp(argv[i], "-bw")) -+ border_width = atoi(argv[++i]); /* border width */ - else - usage(); - -@@ -766,8 +1213,23 @@ main(int argc, char *argv[]) - die("could not get embedding window attributes: 0x%lx", - parentwin); - drw = drw_create(dpy, screen, root, wa.width, wa.height); -- if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) -+ readxresources(); -+ /* Now we check whether to override xresources with commandline parameters */ -+ if ( tempfonts ) -+ fonts[0] = strdup(tempfonts); -+ if ( colortemp[0]) -+ colors[SchemeNorm][ColBg] = strdup(colortemp[0]); -+ if ( colortemp[1]) -+ colors[SchemeNorm][ColFg] = strdup(colortemp[1]); -+ if ( colortemp[2]) -+ colors[SchemeSel][ColBg] = strdup(colortemp[2]); -+ if ( colortemp[3]) -+ colors[SchemeSel][ColFg] = strdup(colortemp[3]); -+ -+ if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) - die("no fonts could be loaded."); -+ -+ free(fonts[0]); - lrpad = drw->fonts->h; - - #ifdef __OpenBSD__ -diff --git a/patches/dmenu-borderoption-20200217-bf60a1e.diff b/patches/dmenu-borderoption-20200217-bf60a1e.diff -new file mode 100644 -index 0000000..b9b5726 ---- /dev/null -+++ b/patches/dmenu-borderoption-20200217-bf60a1e.diff -@@ -0,0 +1,46 @@ -+From bf60a1eaf98c7aebae51021914e35bc73dd8c23e Mon Sep 17 00:00:00 2001 -+From: 0x1bi <ben@0x1bi.net> -+Date: Mon, 17 Feb 2020 11:02:35 -0500 -+Subject: [PATCH] added border with option -+ -+ -+diff --git a/config.def.h b/config.def.h -+index 1edb647..dd3eb31 100644 -+--- a/config.def.h -++++ b/config.def.h -+@@ -21,3 +21,6 @@ static unsigned int lines = 0; -+ * for example: " /?\"&[]" -+ */ -+ static const char worddelimiters[] = " "; -++ -++/* Size of the window border */ -++static unsigned int border_width = 0; -+diff --git a/dmenu.c b/dmenu.c -+index 65f25ce..f0c3c6f 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -659,9 +659,11 @@ setup(void) -+ 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, parentwin, x, y, mw, mh, border_width, -+ CopyFromParent, CopyFromParent, CopyFromParent, -+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); -++ if (border_width) -++ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); -+ XSetClassHint(dpy, win, &ch); -+ -+ -+@@ -733,6 +735,8 @@ main(int argc, char *argv[]) -+ colors[SchemeSel][ColFg] = argv[++i]; -+ else if (!strcmp(argv[i], "-w")) /* embedding window id */ -+ embed = argv[++i]; -++ else if (!strcmp(argv[i], "-bw")) -++ border_width = atoi(argv[++i]); /* border width */ -+ else -+ usage(); -+ -+-- -+2.24.1 -+ -diff --git a/patches/dmenu-center-20200111-8cd37e1.diff b/patches/dmenu-center-20200111-8cd37e1.diff -new file mode 100644 -index 0000000..af249a6 ---- /dev/null -+++ b/patches/dmenu-center-20200111-8cd37e1.diff -@@ -0,0 +1,120 @@ -+From 8cd37e1ab9e7cb025224aeb3543f1a5be8bceb93 Mon Sep 17 00:00:00 2001 -+From: Nihal Jere <nihal@nihaljere.xyz> -+Date: Sat, 11 Jan 2020 21:16:08 -0600 -+Subject: [PATCH] center patch now has adjustable minimum width -+ -+--- -+ config.def.h | 2 ++ -+ dmenu.1 | 3 +++ -+ dmenu.c | 39 ++++++++++++++++++++++++++++++++------- -+ 3 files changed, 37 insertions(+), 7 deletions(-) -+ -+diff --git a/config.def.h b/config.def.h -+index 1edb647..88ef264 100644 -+--- a/config.def.h -++++ b/config.def.h -+@@ -2,6 +2,8 @@ -+ /* Default settings; can be overriden by command line. */ -+ -+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -++static int centered = 0; /* -c option; centers dmenu on screen */ -++static int min_width = 500; /* minimum width when centered */ -+ /* -fn option overrides fonts[0]; default X11 font or font set */ -+ static const char *fonts[] = { -+ "monospace:size=10" -+diff --git a/dmenu.1 b/dmenu.1 -+index 323f93c..c036baa 100644 -+--- a/dmenu.1 -++++ b/dmenu.1 -+@@ -40,6 +40,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. -+ .B \-b -+ dmenu appears at the bottom of the screen. -+ .TP -++.B \-c -++dmenu appears centered on the screen. -++.TP -+ .B \-f -+ dmenu grabs the keyboard before reading stdin if not reading from a tty. This -+ is faster, but will lock up X until stdin reaches end\-of\-file. -+diff --git a/dmenu.c b/dmenu.c -+index 65f25ce..041c7f8 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -89,6 +89,15 @@ calcoffsets(void) -+ break; -+ } -+ -++static int -++max_textw(void) -++{ -++ int len = 0; -++ for (struct item *item = items; item && item->text; item++) -++ len = MAX(TEXTW(item->text), len); -++ return len; -++} -++ -+ static void -+ cleanup(void) -+ { -+@@ -611,6 +620,7 @@ setup(void) -+ bh = drw->fonts->h + 2; -+ lines = MAX(lines, 0); -+ mh = (lines + 1) * bh; -++ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; -+ #ifdef XINERAMA -+ i = 0; -+ if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { -+@@ -637,9 +647,16 @@ setup(void) -+ if (INTERSECT(x, y, 1, 1, info[i])) -+ break; -+ -+- x = info[i].x_org; -+- y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -+- mw = info[i].width; -++ if (centered) { -++ mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); -++ x = info[i].x_org + ((info[i].width - mw) / 2); -++ y = info[i].y_org + ((info[i].height - mh) / 2); -++ } else { -++ x = info[i].x_org; -++ y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -++ mw = info[i].width; -++ } -++ -+ XFree(info); -+ } else -+ #endif -+@@ -647,11 +664,17 @@ setup(void) -+ if (!XGetWindowAttributes(dpy, parentwin, &wa)) -+ die("could not get embedding window attributes: 0x%lx", -+ parentwin); -+- x = 0; -+- y = topbar ? 0 : wa.height - mh; -+- mw = wa.width; -++ -++ if (centered) { -++ mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); -++ x = (wa.width - mw) / 2; -++ y = (wa.height - mh) / 2; -++ } else { -++ x = 0; -++ y = topbar ? 0 : wa.height - mh; -++ mw = wa.width; -++ } -+ } -+- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; -+ inputw = MIN(inputw, mw/3); -+ match(); -+ -+@@ -709,6 +732,8 @@ main(int argc, char *argv[]) -+ topbar = 0; -+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ -+ fast = 1; -++ else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ -++ centered = 1; -+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ -+ fstrncmp = strncasecmp; -+ fstrstr = cistrstr; -+-- -+2.24.1 -+ -diff --git a/patches/dmenu-fuzzyhighlight-4.9.diff b/patches/dmenu-fuzzyhighlight-4.9.diff -new file mode 100644 -index 0000000..58d5c6f ---- /dev/null -+++ b/patches/dmenu-fuzzyhighlight-4.9.diff -@@ -0,0 +1,152 @@ -+Author: Chris Noxz <chris@noxz.tech> -+note: This patch is meant to be used together with fuzzymatch -+ -+diff -upN dmenu-4.9/config.def.h dmenu-4.9-fuzzyhighlight/config.def.h -+--- dmenu-4.9/config.def.h 2019-02-02 13:55:02.000000000 +0100 -++++ dmenu-4.9-fuzzyhighlight/config.def.h 2020-04-04 10:26:36.990890854 +0200 -+@@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] -+ /* fg bg */ -+ [SchemeNorm] = { "#bbbbbb", "#222222" }, -+ [SchemeSel] = { "#eeeeee", "#005577" }, -++ [SchemeSelHighlight] = { "#ffc978", "#005577" }, -++ [SchemeNormHighlight] = { "#ffc978", "#222222" }, -+ [SchemeOut] = { "#000000", "#00ffff" }, -+ }; -+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -+diff -upN dmenu-4.9/dmenu.1 dmenu-4.9-fuzzyhighlight/dmenu.1 -+--- dmenu-4.9/dmenu.1 2019-02-02 13:55:02.000000000 +0100 -++++ dmenu-4.9-fuzzyhighlight/dmenu.1 2020-04-04 10:30:16.430054933 +0200 -+@@ -20,6 +20,14 @@ dmenu \- dynamic menu -+ .IR color ] -+ .RB [ \-sf -+ .IR color ] -++.RB [ \-nhb -++.IR color ] -++.RB [ \-nhf -++.IR color ] -++.RB [ \-shb -++.IR color ] -++.RB [ \-shf -++.IR color ] -+ .RB [ \-w -+ .IR windowid ] -+ .P -+@@ -75,6 +83,18 @@ defines the selected background color. -+ .BI \-sf " color" -+ defines the selected foreground color. -+ .TP -++.BI \-nhb " color" -++defines the normal highlight background color. -++.TP -++.BI \-nhf " color" -++defines the normal highlight foreground color. -++.TP -++.BI \-shb " color" -++defines the selected highlight background color. -++.TP -++.BI \-shf " color" -++defines the selected highlight foreground color. -++.TP -+ .B \-v -+ prints version information to stdout, then exits. -+ .TP -+diff -upN dmenu-4.9/dmenu.c dmenu-4.9-fuzzyhighlight/dmenu.c -+--- dmenu-4.9/dmenu.c 2019-02-02 13:55:02.000000000 +0100 -++++ dmenu-4.9-fuzzyhighlight/dmenu.c 2020-04-04 10:27:43.888026309 +0200 -+@@ -26,7 +26,9 @@ -+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) -+ -+ /* enums */ -+-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ -++enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, -++ SchemeOut, SchemeLast }; /* color schemes */ -++ -+ -+ struct item { -+ char *text; -+@@ -113,9 +115,49 @@ cistrstr(const char *s, const char *sub) -+ return NULL; -+ } -+ -++static void -++drawhighlights(struct item *item, int x, int y, int maxw) -++{ -++ int i, indent; -++ char *highlight; -++ char c; -++ -++ if (!(strlen(item->text) && strlen(text))) -++ return; -++ -++ drw_setscheme(drw, scheme[item == sel -++ ? SchemeSelHighlight -++ : SchemeNormHighlight]); -++ for (i = 0, highlight = item->text; *highlight && text[i];) { -++ if (*highlight == text[i]) { -++ /* get indentation */ -++ c = *highlight; -++ *highlight = '\0'; -++ indent = TEXTW(item->text); -++ *highlight = c; -++ -++ /* highlight character */ -++ c = highlight[1]; -++ highlight[1] = '\0'; -++ drw_text( -++ drw, -++ x + indent - (lrpad / 2), -++ y, -++ MIN(maxw - indent, TEXTW(highlight) - lrpad), -++ bh, 0, highlight, 0 -++ ); -++ highlight[1] = c; -++ i++; -++ } -++ highlight++; -++ } -++} -++ -++ -+ static int -+ drawitem(struct item *item, int x, int y, int w) -+ { -++ int r; -+ if (item == sel) -+ drw_setscheme(drw, scheme[SchemeSel]); -+ else if (item->out) -+@@ -123,7 +165,9 @@ drawitem(struct item *item, int x, int y -+ else -+ drw_setscheme(drw, scheme[SchemeNorm]); -+ -+- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -++ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -++ drawhighlights(item, x, y, w); -++ return r; -+ } -+ -+ static void -+@@ -683,7 +727,8 @@ 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); -++ " [-nb color] [-nf color] [-sb color] [-sf color]\n" -++ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr); -+ exit(1); -+ } -+ -+@@ -724,6 +769,14 @@ main(int argc, char *argv[]) -+ colors[SchemeSel][ColBg] = argv[++i]; -+ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ -+ colors[SchemeSel][ColFg] = argv[++i]; -++ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ -++ colors[SchemeNormHighlight][ColBg] = argv[++i]; -++ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ -++ colors[SchemeNormHighlight][ColFg] = argv[++i]; -++ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ -++ colors[SchemeSelHighlight][ColBg] = argv[++i]; -++ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ -++ colors[SchemeSelHighlight][ColFg] = argv[++i]; -+ else if (!strcmp(argv[i], "-w")) /* embedding window id */ -+ embed = argv[++i]; -+ else -diff --git a/patches/dmenu-fuzzymatch-4.9.diff b/patches/dmenu-fuzzymatch-4.9.diff -new file mode 100644 -index 0000000..9fd206d ---- /dev/null -+++ b/patches/dmenu-fuzzymatch-4.9.diff -@@ -0,0 +1,163 @@ -+From 94353eb52055927d9079f3d9e33da1c954abf386 Mon Sep 17 00:00:00 2001 -+From: aleks <aleks.stier@icloud.com> -+Date: Wed, 26 Jun 2019 13:25:10 +0200 -+Subject: [PATCH] Add support for fuzzy-matching -+ -+--- -+ config.def.h | 1 + -+ config.mk | 2 +- -+ dmenu.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ -+ 3 files changed, 91 insertions(+), 1 deletion(-) -+ -+diff --git a/config.def.h b/config.def.h -+index 1edb647..51612b9 100644 -+--- a/config.def.h -++++ b/config.def.h -+@@ -2,6 +2,7 @@ -+ /* Default settings; can be overriden by command line. */ -+ -+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -++static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */ -+ /* -fn option overrides fonts[0]; default X11 font or font set */ -+ static const char *fonts[] = { -+ "monospace:size=10" -+diff --git a/config.mk b/config.mk -+index 0929b4a..d14309a 100644 -+--- a/config.mk -++++ b/config.mk -+@@ -20,7 +20,7 @@ FREETYPEINC = /usr/include/freetype2 -+ -+ # includes and libs -+ INCS = -I$(X11INC) -I$(FREETYPEINC) -+-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -++LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm -+ -+ # flags -+ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) -+diff --git a/dmenu.c b/dmenu.c -+index 6b8f51b..96ddc98 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -1,6 +1,7 @@ -+ /* See LICENSE file for copyright and license details. */ -+ #include <ctype.h> -+ #include <locale.h> -++#include <math.h> -+ #include <stdio.h> -+ #include <stdlib.h> -+ #include <string.h> -+@@ -32,6 +33,7 @@ struct item { -+ char *text; -+ struct item *left, *right; -+ int out; -++ double distance; -+ }; -+ -+ static char text[BUFSIZ] = ""; -+@@ -210,9 +212,94 @@ grabkeyboard(void) -+ die("cannot grab keyboard"); -+ } -+ -++int -++compare_distance(const void *a, const void *b) -++{ -++ struct item *da = *(struct item **) a; -++ struct item *db = *(struct item **) b; -++ -++ if (!db) -++ return 1; -++ if (!da) -++ return -1; -++ -++ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; -++} -++ -++void -++fuzzymatch(void) -++{ -++ /* bang - we have so much memory */ -++ struct item *it; -++ struct item **fuzzymatches = NULL; -++ char c; -++ int number_of_matches = 0, i, pidx, sidx, eidx; -++ int text_len = strlen(text), itext_len; -++ -++ matches = matchend = NULL; -++ -++ /* walk through all items */ -++ for (it = items; it && it->text; it++) { -++ if (text_len) { -++ itext_len = strlen(it->text); -++ pidx = 0; /* pointer */ -++ sidx = eidx = -1; /* start of match, end of match */ -++ /* walk through item text */ -++ for (i = 0; i < itext_len && (c = it->text[i]); i++) { -++ /* fuzzy match pattern */ -++ if (!fstrncmp(&text[pidx], &c, 1)) { -++ if(sidx == -1) -++ sidx = i; -++ pidx++; -++ if (pidx == text_len) { -++ eidx = i; -++ break; -++ } -++ } -++ } -++ /* build list of matches */ -++ if (eidx != -1) { -++ /* compute distance */ -++ /* add penalty if match starts late (log(sidx+2)) -++ * add penalty for long a match without many matching characters */ -++ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); -++ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ -++ appenditem(it, &matches, &matchend); -++ number_of_matches++; -++ } -++ } else { -++ appenditem(it, &matches, &matchend); -++ } -++ } -++ -++ if (number_of_matches) { -++ /* initialize array with matches */ -++ if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) -++ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); -++ for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { -++ fuzzymatches[i] = it; -++ } -++ /* sort matches according to distance */ -++ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); -++ /* rebuild list of matches */ -++ matches = matchend = NULL; -++ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ -++ it->text; i++, it = fuzzymatches[i]) { -++ appenditem(it, &matches, &matchend); -++ } -++ free(fuzzymatches); -++ } -++ curr = sel = matches; -++ calcoffsets(); -++} -++ -+ static void -+ match(void) -+ { -++ if (fuzzy) { -++ fuzzymatch(); -++ return; -++ } -+ static char **tokv = NULL; -+ static int tokn = 0; -+ -+@@ -702,6 +789,8 @@ main(int argc, char *argv[]) -+ topbar = 0; -+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ -+ fast = 1; -++ else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */ -++ fuzzy = 0; -+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ -+ fstrncmp = strncasecmp; -+ fstrstr = cistrstr; -+-- -+2.22.0 -+ -diff --git a/patches/dmenu-lineheight-5.0.diff b/patches/dmenu-lineheight-5.0.diff -new file mode 100644 -index 0000000..3b0df3d ---- /dev/null -+++ b/patches/dmenu-lineheight-5.0.diff -@@ -0,0 +1,106 @@ -+From ba103e38ea4ab07f9a3ee90627714b9bea17c329 Mon Sep 17 00:00:00 2001 -+From: pskry <peter@skrypalle.dk> -+Date: Sun, 8 Nov 2020 22:04:22 +0100 -+Subject: [PATCH] Add an option which defines the lineheight -+ -+Despite both the panel and dmenu using the same font (a Terminus 12), -+dmenu is shorter and the panel is visible from under the dmenu bar. -+The appearance can be even more distracting when using similar colors -+for background and selections. With the option added by this patch, -+dmenu can be launched with a '-h 24', thus completely covering the panel. -+--- -+ config.def.h | 3 +++ -+ dmenu.1 | 5 +++++ -+ dmenu.c | 11 ++++++++--- -+ 3 files changed, 16 insertions(+), 3 deletions(-) -+ -+diff --git a/config.def.h b/config.def.h -+index 1edb647..4394dec 100644 -+--- a/config.def.h -++++ b/config.def.h -+@@ -15,6 +15,9 @@ static const char *colors[SchemeLast][2] = { -+ }; -+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -+ static unsigned int lines = 0; -++/* -h option; minimum height of a menu line */ -++static unsigned int lineheight = 0; -++static unsigned int min_lineheight = 8; -+ -+ /* -+ * Characters not considered part of a word while deleting words -+diff --git a/dmenu.1 b/dmenu.1 -+index 323f93c..f2a82b4 100644 -+--- a/dmenu.1 -++++ b/dmenu.1 -+@@ -6,6 +6,8 @@ dmenu \- dynamic menu -+ .RB [ \-bfiv ] -+ .RB [ \-l -+ .IR lines ] -++.RB [ \-h -++.IR height ] -+ .RB [ \-m -+ .IR monitor ] -+ .RB [ \-p -+@@ -50,6 +52,9 @@ dmenu matches menu items case insensitively. -+ .BI \-l " lines" -+ dmenu lists items vertically, with the given number of lines. -+ .TP -++.BI \-h " height" -++dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. -++.TP -+ .BI \-m " monitor" -+ dmenu is displayed on the monitor number supplied. Monitor numbers are starting -+ from 0. -+diff --git a/dmenu.c b/dmenu.c -+index 65f25ce..f2a4047 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -131,7 +131,7 @@ drawmenu(void) -+ { -+ unsigned int curpos; -+ struct item *item; -+- int x = 0, y = 0, w; -++ int x = 0, y = 0, fh = drw->fonts->h, w; -+ -+ drw_setscheme(drw, scheme[SchemeNorm]); -+ drw_rect(drw, 0, 0, mw, mh, 1, 1); -+@@ -148,7 +148,7 @@ drawmenu(void) -+ curpos = TEXTW(text) - TEXTW(&text[cursor]); -+ if ((curpos += lrpad / 2 - 1) < w) { -+ drw_setscheme(drw, scheme[SchemeNorm]); -+- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); -++ drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0); -+ } -+ -+ if (lines > 0) { -+@@ -609,6 +609,7 @@ setup(void) -+ -+ /* calculate menu geometry */ -+ bh = drw->fonts->h + 2; -++ bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ -+ lines = MAX(lines, 0); -+ mh = (lines + 1) * bh; -+ #ifdef XINERAMA -+@@ -689,7 +690,7 @@ setup(void) -+ static void -+ usage(void) -+ { -+- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -++ fputs("usage: dmenu [-bfiv] [-l lines] [-h height] [-p prompt] [-fn font] [-m monitor]\n" -+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); -+ exit(1); -+ } -+@@ -717,6 +718,10 @@ main(int argc, char *argv[]) -+ /* these options take one argument */ -+ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ -+ lines = atoi(argv[++i]); -++ else if (!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ -++ lineheight = atoi(argv[++i]); -++ lineheight = MAX(lineheight, min_lineheight); -++ } -+ else if (!strcmp(argv[i], "-m")) -+ mon = atoi(argv[++i]); -+ else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ -+-- -+2.29.2 -+ -diff --git a/patches/dmenu-mousesupport-5.1.diff b/patches/dmenu-mousesupport-5.1.diff -new file mode 100644 -index 0000000..49824ba ---- /dev/null -+++ b/patches/dmenu-mousesupport-5.1.diff -@@ -0,0 +1,144 @@ -+diff --git a/dmenu.c b/dmenu.c -+index d95e6c6..75a79d0 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -518,6 +518,119 @@ draw: -+ drawmenu(); -+ } -+ -++static void -++buttonpress(XEvent *e) -++{ -++ struct item *item; -++ XButtonPressedEvent *ev = &e->xbutton; -++ int x = 0, y = 0, h = bh, w; -++ -++ if (ev->window != win) -++ return; -++ -++ /* right-click: exit */ -++ if (ev->button == Button3) -++ exit(1); -++ -++ if (prompt && *prompt) -++ x += promptw; -++ -++ /* input field */ -++ w = (lines > 0 || !matches) ? mw - x : inputw; -++ -++ /* left-click on input: clear input, -++ * NOTE: if there is no left-arrow the space for < is reserved so -++ * add that to the input width */ -++ if (ev->button == Button1 && -++ ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + -++ ((!prev || !curr->left) ? TEXTW("<") : 0)) || -++ (lines > 0 && ev->y >= y && ev->y <= y + h))) { -++ insert(NULL, -cursor); -++ drawmenu(); -++ return; -++ } -++ /* middle-mouse click: paste selection */ -++ if (ev->button == Button2) { -++ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, -++ utf8, utf8, win, CurrentTime); -++ drawmenu(); -++ return; -++ } -++ /* scroll up */ -++ if (ev->button == Button4 && prev) { -++ sel = curr = prev; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ /* scroll down */ -++ if (ev->button == Button5 && next) { -++ sel = curr = next; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ if (ev->button != Button1) -++ return; -++ if (ev->state & ~ControlMask) -++ return; -++ if (lines > 0) { -++ /* vertical list: (ctrl)left-click on item */ -++ w = mw - x; -++ for (item = curr; item != next; item = item->right) { -++ y += h; -++ if (ev->y >= y && ev->y <= (y + h)) { -++ puts(item->text); -++ if (!(ev->state & ControlMask)) -++ exit(0); -++ sel = item; -++ if (sel) { -++ sel->out = 1; -++ drawmenu(); -++ } -++ return; -++ } -++ } -++ } else if (matches) { -++ /* left-click on left arrow */ -++ x += inputw; -++ w = TEXTW("<"); -++ if (prev && curr->left) { -++ if (ev->x >= x && ev->x <= x + w) { -++ sel = curr = prev; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++ /* horizontal list: (ctrl)left-click on item */ -++ for (item = curr; item != next; item = item->right) { -++ x += w; -++ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -++ if (ev->x >= x && ev->x <= x + w) { -++ puts(item->text); -++ if (!(ev->state & ControlMask)) -++ exit(0); -++ sel = item; -++ if (sel) { -++ sel->out = 1; -++ drawmenu(); -++ } -++ return; -++ } -++ } -++ /* left-click on right arrow */ -++ w = TEXTW(">"); -++ x = mw - w; -++ if (next && ev->x >= x && ev->x <= x + w) { -++ sel = curr = next; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++} -++ -+ static void -+ paste(void) -+ { -+@@ -579,6 +692,9 @@ run(void) -+ break; -+ cleanup(); -+ exit(1); -++ case ButtonPress: -++ buttonpress(&ev); -++ break; -+ case Expose: -+ if (ev.xexpose.count == 0) -+ drw_map(drw, win, 0, 0, mw, mh); -+@@ -676,7 +792,8 @@ setup(void) -+ /* create menu window */ -+ swa.override_redirect = True; -+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; -+- swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; -++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | -++ ButtonPressMask; -+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, -+ CopyFromParent, CopyFromParent, CopyFromParent, -+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); -diff --git a/patches/dmenu-mousesupporthoverbgcol-5.0.diff b/patches/dmenu-mousesupporthoverbgcol-5.0.diff -new file mode 100644 -index 0000000..1fb7d41 ---- /dev/null -+++ b/patches/dmenu-mousesupporthoverbgcol-5.0.diff -@@ -0,0 +1,184 @@ -+Only in .: config.h -+diff -up ../dmenu-5.0/dmenu.c ./dmenu.c -+--- ../dmenu-5.0/dmenu.c Wed Sep 2 18:37:07 2020 -++++ ./dmenu.c Wed Nov 4 15:25:27 2020 -+@@ -501,6 +501,156 @@ draw: -+ } -+ -+ static void -++buttonpress(XEvent *e) -++{ -++ struct item *item; -++ XButtonPressedEvent *ev = &e->xbutton; -++ int x = 0, y = 0, h = bh, w; -++ -++ if (ev->window != win) -++ return; -++ -++ /* right-click: exit */ -++ if (ev->button == Button3) -++ exit(1); -++ -++ if (prompt && *prompt) -++ x += promptw; -++ -++ /* input field */ -++ w = (lines > 0 || !matches) ? mw - x : inputw; -++ -++ /* left-click on input: clear input, -++ * NOTE: if there is no left-arrow the space for < is reserved so -++ * add that to the input width */ -++ if (ev->button == Button1 && -++ ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + -++ ((!prev || !curr->left) ? TEXTW("<") : 0)) || -++ (lines > 0 && ev->y >= y && ev->y <= y + h))) { -++ insert(NULL, -cursor); -++ drawmenu(); -++ return; -++ } -++ /* middle-mouse click: paste selection */ -++ if (ev->button == Button2) { -++ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, -++ utf8, utf8, win, CurrentTime); -++ drawmenu(); -++ return; -++ } -++ /* scroll up */ -++ if (ev->button == Button4 && prev) { -++ sel = curr = prev; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ /* scroll down */ -++ if (ev->button == Button5 && next) { -++ sel = curr = next; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ if (ev->button != Button1) -++ return; -++ /* disabled below, needs to be fixed */ -++ /* -++ if (ev->state & ~ControlMask) -++ return; -++ */ -++ if (lines > 0) { -++ /* vertical list: (ctrl)left-click on item */ -++ w = mw - x; -++ for (item = curr; item != next; item = item->right) { -++ y += h; -++ if (ev->y >= y && ev->y <= (y + h)) { -++ puts(item->text); -++ if (!(ev->state & ControlMask)) -++ exit(0); -++ sel = item; -++ if (sel) { -++ sel->out = 1; -++ drawmenu(); -++ } -++ return; -++ } -++ } -++ } else if (matches) { -++ /* left-click on left arrow */ -++ x += inputw; -++ w = TEXTW("<"); -++ if (prev && curr->left) { -++ if (ev->x >= x && ev->x <= x + w) { -++ sel = curr = prev; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++ /* horizontal list: (ctrl)left-click on item */ -++ for (item = curr; item != next; item = item->right) { -++ x += w; -++ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -++ if (ev->x >= x && ev->x <= x + w) { -++ puts(item->text); -++ if (!(ev->state & ControlMask)) -++ exit(0); -++ sel = item; -++ if (sel) { -++ sel->out = 1; -++ drawmenu(); -++ } -++ return; -++ } -++ } -++ /* left-click on right arrow */ -++ w = TEXTW(">"); -++ x = mw - w; -++ if (next && ev->x >= x && ev->x <= x + w) { -++ sel = curr = next; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++} -++ -++static void -++mousemove(XEvent *e) -++{ -++ struct item *item; -++ XPointerMovedEvent *ev = &e->xmotion; -++ int x = 0, y = 0, h = bh, w; -++ -++ if (lines > 0) { -++ w = mw - x; -++ for (item = curr; item != next; item = item->right) { -++ y += h; -++ if (ev->y >= y && ev->y <= (y + h)) { -++ sel = item; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++ } else if (matches) { -++ x += inputw; -++ w = TEXTW("<"); -++ for (item = curr; item != next; item = item->right) { -++ x += w; -++ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -++ if (ev->x >= x && ev->x <= x + w) { -++ sel = item; -++ calcoffsets(); -++ drawmenu(); -++ return; -++ } -++ } -++ } -++} -++ -++static void -+ paste(void) -+ { -+ char *p, *q; -+@@ -561,6 +711,12 @@ run(void) -+ break; -+ cleanup(); -+ exit(1); -++ case ButtonPress: -++ buttonpress(&ev); -++ break; -++ case MotionNotify: -++ mousemove(&ev); -++ break; -+ case Expose: -+ if (ev.xexpose.count == 0) -+ drw_map(drw, win, 0, 0, mw, mh); -+@@ -658,7 +814,8 @@ setup(void) -+ /* create menu window */ -+ swa.override_redirect = True; -+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; -+- swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; -++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | -++ ButtonPressMask | PointerMotionMask; -+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, -+ CopyFromParent, CopyFromParent, CopyFromParent, -+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); -diff --git a/patches/dmenu-password-5.0.diff b/patches/dmenu-password-5.0.diff -new file mode 100644 -index 0000000..7a187b9 ---- /dev/null -+++ b/patches/dmenu-password-5.0.diff -@@ -0,0 +1,103 @@ -+From c4de1032bd4c247bc20b6ab92a10a8d778966679 Mon Sep 17 00:00:00 2001 -+From: Mehrad Mahmoudian <m.mahmoudian@gmail.com> -+Date: Tue, 4 May 2021 12:05:09 +0300 -+Subject: [PATCH] patched with password patch -+ -+--- -+ dmenu.1 | 5 ++++- -+ dmenu.c | 21 +++++++++++++++++---- -+ 2 files changed, 21 insertions(+), 5 deletions(-) -+ -+diff --git a/dmenu.1 b/dmenu.1 -+index 323f93c..762f707 100644 -+--- a/dmenu.1 -++++ b/dmenu.1 -+@@ -3,7 +3,7 @@ -+ dmenu \- dynamic menu -+ .SH SYNOPSIS -+ .B dmenu -+-.RB [ \-bfiv ] -++.RB [ \-bfivP ] -+ .RB [ \-l -+ .IR lines ] -+ .RB [ \-m -+@@ -47,6 +47,9 @@ is faster, but will lock up X until stdin reaches end\-of\-file. -+ .B \-i -+ dmenu matches menu items case insensitively. -+ .TP -++.B \-P -++dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. -++.TP -+ .BI \-l " lines" -+ dmenu lists items vertically, with the given number of lines. -+ .TP -+diff --git a/dmenu.c b/dmenu.c -+index 65f25ce..ad8f63b 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -37,7 +37,7 @@ struct item { -+ static char text[BUFSIZ] = ""; -+ static char *embed; -+ static int bh, mw, mh; -+-static int inputw = 0, promptw; -++static int inputw = 0, promptw, passwd = 0; -+ static int lrpad; /* sum of left and right padding */ -+ static size_t cursor; -+ static struct item *items = NULL; -+@@ -132,6 +132,7 @@ drawmenu(void) -+ unsigned int curpos; -+ struct item *item; -+ int x = 0, y = 0, w; -++ char *censort; -+ -+ drw_setscheme(drw, scheme[SchemeNorm]); -+ drw_rect(drw, 0, 0, mw, mh, 1, 1); -+@@ -143,7 +144,12 @@ drawmenu(void) -+ /* draw input field */ -+ w = (lines > 0 || !matches) ? mw - x : inputw; -+ drw_setscheme(drw, scheme[SchemeNorm]); -+- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); -++ if (passwd) { -++ censort = ecalloc(1, sizeof(text)); -++ memset(censort, '.', strlen(text)); -++ drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); -++ free(censort); -++ } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); -+ -+ curpos = TEXTW(text) - TEXTW(&text[cursor]); -+ if ((curpos += lrpad / 2 - 1) < w) { -+@@ -524,6 +530,11 @@ readstdin(void) -+ char buf[sizeof text], *p; -+ size_t i, imax = 0, size = 0; -+ unsigned int tmpmax = 0; -++ if(passwd){ -++ inputw = lines = 0; -++ return; -++ } -++ -+ -+ /* read each line from stdin and add it to the item list */ -+ for (i = 0; fgets(buf, sizeof buf, stdin); i++) { -+@@ -689,7 +700,7 @@ setup(void) -+ static void -+ usage(void) -+ { -+- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -++ fputs("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); -+ exit(1); -+ } -+@@ -712,7 +723,9 @@ main(int argc, char *argv[]) -+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ -+ fstrncmp = strncasecmp; -+ fstrstr = cistrstr; -+- } else if (i + 1 == argc) -++ } else if (!strcmp(argv[i], "-P")) /* is the input a password */ -++ passwd = 1; -++ else if (i + 1 == argc) -+ usage(); -+ /* these options take one argument */ -+ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ -+-- -+2.31.1 -+ -diff --git a/patches/dmenu-rejectnomatch-4.7.diff b/patches/dmenu-rejectnomatch-4.7.diff -new file mode 100644 -index 0000000..329ab1d ---- /dev/null -+++ b/patches/dmenu-rejectnomatch-4.7.diff -@@ -0,0 +1,82 @@ -+diff --git a/dmenu.1 b/dmenu.1 -+index 9eab758..61084ab 100644 -+--- a/dmenu.1 -++++ b/dmenu.1 -+@@ -3,7 +3,7 @@ -+ dmenu \- dynamic menu -+ .SH SYNOPSIS -+ .B dmenu -+-.RB [ \-bfiv ] -++.RB [ \-bfirv ] -+ .RB [ \-l -+ .IR lines ] -+ .RB [ \-m -+@@ -47,6 +47,9 @@ X until stdin reaches end\-of\-file. -+ .B \-i -+ dmenu matches menu items case insensitively. -+ .TP -++.B \-r -++dmenu will reject any input which would result in no matching option left. -++.TP -+ .BI \-l " lines" -+ dmenu lists items vertically, with the given number of lines. -+ .TP -+diff --git a/dmenu.c b/dmenu.c -+index d605ab4..7505278 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -38,6 +38,7 @@ static char *embed; -+ static int bh, mw, mh; -+ static int inputw = 0, promptw; -+ static int lrpad; /* sum of left and right padding */ -++static int reject_no_match = 0; -+ static size_t cursor; -+ static struct item *items = NULL; -+ static struct item *matches, *matchend; -+@@ -268,12 +269,26 @@ insert(const char *str, ssize_t n) -+ { -+ if (strlen(text) + n > sizeof text - 1) -+ return; -++ -++ static char last[BUFSIZ] = ""; -++ if(reject_no_match) { -++ /* store last text value in case we need to revert it */ -++ memcpy(last, text, BUFSIZ); -++ } -++ -+ /* move existing text out of the way, insert new text, and update cursor */ -+ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); -+ if (n > 0) -+ memcpy(&text[cursor], str, n); -+ cursor += n; -+ match(); -++ -++ if(!matches && reject_no_match) { -++ /* revert to last text value if theres no match */ -++ memcpy(text, last, BUFSIZ); -++ cursor -= n; -++ match(); -++ } -+ } -+ -+ static size_t -+@@ -636,7 +651,7 @@ setup(void) -+ static void -+ usage(void) -+ { -+- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -++ fputs("usage: dmenu [-bfirv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); -+ exit(1); -+ } -+@@ -659,7 +674,9 @@ main(int argc, char *argv[]) -+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ -+ fstrncmp = strncasecmp; -+ fstrstr = cistrstr; -+- } else if (i + 1 == argc) -++ } else if (!strcmp(argv[i], "-r")) /* reject input which results in no match */ -++ reject_no_match = 1; -++ else if (i + 1 == argc) -+ usage(); -+ /* these options take one argument */ -+ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ -diff --git a/patches/dmenu-separator-20210904-d78ff08.diff b/patches/dmenu-separator-20210904-d78ff08.diff -new file mode 100644 -index 0000000..be30420 ---- /dev/null -+++ b/patches/dmenu-separator-20210904-d78ff08.diff -@@ -0,0 +1,101 @@ -+diff --git a/dmenu.1 b/dmenu.1 -+index 323f93c..d511148 100644 -+--- a/dmenu.1 -++++ b/dmenu.1 -+@@ -22,6 +22,10 @@ dmenu \- dynamic menu -+ .IR color ] -+ .RB [ \-w -+ .IR windowid ] -++.RB [ \-d -++.IR separator ] -++.RB [ \-D -++.IR separator ] -+ .P -+ .BR dmenu_run " ..." -+ .SH DESCRIPTION -+@@ -80,6 +84,14 @@ prints version information to stdout, then exits. -+ .TP -+ .BI \-w " windowid" -+ embed into windowid. -++.TP -++.BI \-d " separator" -++separate the input into two halves on the first occurrence of the given charcter. -++Display only the first half in dmenu and print the second half to stdout upon selection. -++Appending '|' to the separator reverses the display/printing order. -++.TP -++.BI \-D " separator" -++same as \-d but separate based on the last occurrence. -+ .SH USAGE -+ dmenu is completely controlled by the keyboard. Items are selected using the -+ arrow keys, page up, page down, home, and end. -+diff --git a/dmenu.c b/dmenu.c -+index 98507d9..82227c8 100644 -+--- a/dmenu.c -++++ b/dmenu.c -+@@ -30,12 +30,16 @@ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ -+ -+ struct item { -+ char *text; -++ char *text_output; -+ struct item *left, *right; -+ int out; -+ }; -+ -+ static char text[BUFSIZ] = ""; -+ static char *embed; -++static char separator; -++static int separator_greedy; -++static int separator_reverse; -+ static int bh, mw, mh; -+ static int inputw = 0, promptw; -+ static int lrpad; /* sum of left and right padding */ -+@@ -473,7 +477,7 @@ insert: -+ break; -+ case XK_Return: -+ case XK_KP_Enter: -+- puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); -++ puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); -+ if (!(ev->state & ControlMask)) { -+ cleanup(); -+ exit(0); -+@@ -545,6 +549,18 @@ readstdin(void) -+ *p = '\0'; -+ if (!(items[i].text = strdup(buf))) -+ die("cannot strdup %u bytes:", strlen(buf) + 1); -++ if (separator && (p = separator_greedy ? -++ strrchr(items[i].text, separator) : strchr(items[i].text, separator))) { -++ *p = '\0'; -++ items[i].text_output = ++p; -++ } else { -++ items[i].text_output = items[i].text; -++ } -++ if (separator_reverse) { -++ p = items[i].text; -++ items[i].text = items[i].text_output; -++ items[i].text_output = p; -++ } -+ items[i].out = 0; -+ drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); -+ if (tmpmax > inputw) { -+@@ -701,7 +717,8 @@ 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); -++ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" -++ " [-d separator] [-D separator]\n", stderr); -+ exit(1); -+ } -+ -+@@ -744,6 +761,11 @@ main(int argc, char *argv[]) -+ colors[SchemeSel][ColFg] = argv[++i]; -+ else if (!strcmp(argv[i], "-w")) /* embedding window id */ -+ embed = argv[++i]; -++ else if (!strcmp(argv[i], "-d") || /* field separator */ -++ (separator_greedy = !strcmp(argv[i], "-D"))) { -++ separator = argv[++i][0]; -++ separator_reverse = argv[i][1] == '|'; -++ } -+ else -+ usage(); -+ -diff --git a/patches/dmenu-xresources-4.9.diff b/patches/dmenu-xresources-4.9.diff -new file mode 100644 -index 0000000..267fb0a ---- /dev/null -+++ b/patches/dmenu-xresources-4.9.diff -@@ -0,0 +1,126 @@ -+diff '--color=auto' -up ../dmenu-4.9/dmenu.c ./dmenu.c -+--- ../dmenu-4.9/dmenu.c 2019-02-02 13:55:02.000000000 +0100 -++++ ./dmenu.c 2020-05-24 00:27:58.038586112 +0200 -+@@ -15,6 +15,7 @@ -+ #include <X11/extensions/Xinerama.h> -+ #endif -+ #include <X11/Xft/Xft.h> -++#include <X11/Xresource.h> -+ -+ #include "drw.h" -+ #include "util.h" -+@@ -53,6 +54,10 @@ static XIC xic; -+ static Drw *drw; -+ static Clr *scheme[SchemeLast]; -+ -++/* Temporary arrays to allow overriding xresources values */ -++static char *colortemp[4]; -++static char *tempfonts; -++ -+ #include "config.h" -+ -+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -+@@ -596,8 +601,13 @@ setup(void) -+ int a, di, n, area = 0; -+ #endif -+ /* init appearance */ -+- for (j = 0; j < SchemeLast; j++) -+- scheme[j] = drw_scm_create(drw, colors[j], 2); -++ for (j = 0; j < SchemeLast; j++) { -++ scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); -++ } -++ for (j = 0; j < SchemeOut; ++j) { -++ for (i = 0; i < 2; ++i) -++ free(colors[j][i]); -++ } -+ -+ clip = XInternAtom(dpy, "CLIPBOARD", False); -+ utf8 = XInternAtom(dpy, "UTF8_STRING", False); -+@@ -687,6 +697,41 @@ usage(void) -+ exit(1); -+ } -+ -++void -++readxresources(void) { -++ XrmInitialize(); -++ -++ char* xrm; -++ if ((xrm = XResourceManagerString(drw->dpy))) { -++ char *type; -++ XrmDatabase xdb = XrmGetStringDatabase(xrm); -++ XrmValue xval; -++ -++ if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval)) -++ fonts[0] = strdup(xval.addr); -++ else -++ fonts[0] = strdup(fonts[0]); -++ if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval)) -++ colors[SchemeNorm][ColBg] = strdup(xval.addr); -++ else -++ colors[SchemeNorm][ColBg] = strdup(colors[SchemeNorm][ColBg]); -++ if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval)) -++ colors[SchemeNorm][ColFg] = strdup(xval.addr); -++ else -++ colors[SchemeNorm][ColFg] = strdup(colors[SchemeNorm][ColFg]); -++ if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval)) -++ colors[SchemeSel][ColBg] = strdup(xval.addr); -++ else -++ colors[SchemeSel][ColBg] = strdup(colors[SchemeSel][ColBg]); -++ if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval)) -++ colors[SchemeSel][ColFg] = strdup(xval.addr); -++ else -++ colors[SchemeSel][ColFg] = strdup(colors[SchemeSel][ColFg]); -++ -++ XrmDestroyDatabase(xdb); -++ } -++} -++ -+ int -+ main(int argc, char *argv[]) -+ { -+@@ -715,15 +760,15 @@ main(int argc, char *argv[]) -+ else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ -+ prompt = argv[++i]; -+ else if (!strcmp(argv[i], "-fn")) /* font or font set */ -+- fonts[0] = argv[++i]; -++ tempfonts = argv[++i]; -+ else if (!strcmp(argv[i], "-nb")) /* normal background color */ -+- colors[SchemeNorm][ColBg] = argv[++i]; -++ colortemp[0] = argv[++i]; -+ else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ -+- colors[SchemeNorm][ColFg] = argv[++i]; -++ colortemp[1] = argv[++i]; -+ else if (!strcmp(argv[i], "-sb")) /* selected background color */ -+- colors[SchemeSel][ColBg] = argv[++i]; -++ colortemp[2] = argv[++i]; -+ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ -+- colors[SchemeSel][ColFg] = argv[++i]; -++ colortemp[3] = argv[++i]; -+ else if (!strcmp(argv[i], "-w")) /* embedding window id */ -+ embed = argv[++i]; -+ else -+@@ -743,8 +788,23 @@ main(int argc, char *argv[]) -+ die("could not get embedding window attributes: 0x%lx", -+ parentwin); -+ drw = drw_create(dpy, screen, root, wa.width, wa.height); -+- if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) -++ readxresources(); -++ /* Now we check whether to override xresources with commandline parameters */ -++ if ( tempfonts ) -++ fonts[0] = strdup(tempfonts); -++ if ( colortemp[0]) -++ colors[SchemeNorm][ColBg] = strdup(colortemp[0]); -++ if ( colortemp[1]) -++ colors[SchemeNorm][ColFg] = strdup(colortemp[1]); -++ if ( colortemp[2]) -++ colors[SchemeSel][ColBg] = strdup(colortemp[2]); -++ if ( colortemp[3]) -++ colors[SchemeSel][ColFg] = strdup(colortemp[3]); -++ -++ if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) -+ die("no fonts could be loaded."); -++ -++ free(fonts[0]); -+ lrpad = drw->fonts->h; -+ -+ #ifdef __OpenBSD__ -diff --git a/config.def.h b/config.def.h -index 1edb647..3fb3def 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -2,22 +2,33 @@ - /* Default settings; can be overriden by command line. */ - - static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -+static int centered = 0; /* -c option; centers dmenu on screen */ -+static int min_width = 500; /* minimum width when centered */ -+static int fuzzy = 0; /* -F option; if 0, dmenu doesn't use fuzzy matching */ - /* -fn option overrides fonts[0]; default X11 font or font set */ --static const char *fonts[] = { -+static char *fonts[] = { - "monospace:size=10" - }; - static const char *prompt = NULL; /* -p option; prompt to the left of input field */ --static const char *colors[SchemeLast][2] = { -+static char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, -+ [SchemeSelHighlight] = { "#ffc978", "#005577" }, -+ [SchemeNormHighlight] = { "#ffc978", "#222222" }, - [SchemeOut] = { "#000000", "#00ffff" }, - }; - /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ - static unsigned int lines = 0; -+/* -h option; minimum height of a menu line */ -+static unsigned int lineheight = 0; -+static unsigned int min_lineheight = 8; - - /* - * Characters not considered part of a word while deleting words - * for example: " /?\"&[]" - */ - static const char worddelimiters[] = " "; -+ -+/* Size of the window border */ -+static unsigned int border_width = 0; -diff --git a/config.mk b/config.mk -index 0df3fc8..3423b6a 100644 ---- a/config.mk -+++ b/config.mk -@@ -20,7 +20,7 @@ FREETYPEINC = /usr/include/freetype2 - - # includes and libs - INCS = -I$(X11INC) -I$(FREETYPEINC) --LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm - - # flags - CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) -diff --git a/dmenu.1 b/dmenu.1 -index 323f93c..1602793 100644 ---- a/dmenu.1 -+++ b/dmenu.1 -@@ -3,9 +3,11 @@ - dmenu \- dynamic menu - .SH SYNOPSIS - .B dmenu --.RB [ \-bfiv ] -+.RB [ \-bFfirvP ] - .RB [ \-l - .IR lines ] -+.RB [ \-h -+.IR height ] - .RB [ \-m - .IR monitor ] - .RB [ \-p -@@ -20,8 +22,20 @@ dmenu \- dynamic menu - .IR color ] - .RB [ \-sf - .IR color ] -+.RB [ \-nhb -+.IR color ] -+.RB [ \-nhf -+.IR color ] -+.RB [ \-shb -+.IR color ] -+.RB [ \-shf -+.IR color ] - .RB [ \-w - .IR windowid ] -+.RB [ \-d -+.IR separator ] -+.RB [ \-D -+.IR separator ] - .P - .BR dmenu_run " ..." - .SH DESCRIPTION -@@ -40,6 +54,12 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. - .B \-b - dmenu appears at the bottom of the screen. - .TP -+.B \-c -+dmenu appears centered on the screen. -+.TP -+.B \-F -+dmenu uses fuzzy matching -+.TP - .B \-f - dmenu grabs the keyboard before reading stdin if not reading from a tty. This - is faster, but will lock up X until stdin reaches end\-of\-file. -@@ -47,9 +67,18 @@ is faster, but will lock up X until stdin reaches end\-of\-file. - .B \-i - dmenu matches menu items case insensitively. - .TP -+.B \-P -+dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. -+.TP -+.B \-r -+dmenu will reject any input which would result in no matching option left. -+.TP - .BI \-l " lines" - dmenu lists items vertically, with the given number of lines. - .TP -+.BI \-h " height" -+dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. -+.TP - .BI \-m " monitor" - dmenu is displayed on the monitor number supplied. Monitor numbers are starting - from 0. -@@ -75,11 +104,31 @@ defines the selected background color. - .BI \-sf " color" - defines the selected foreground color. - .TP -+.BI \-nhb " color" -+defines the normal highlight background color. -+.TP -+.BI \-nhf " color" -+defines the normal highlight foreground color. -+.TP -+.BI \-shb " color" -+defines the selected highlight background color. -+.TP -+.BI \-shf " color" -+defines the selected highlight foreground color. -+.TP - .B \-v - prints version information to stdout, then exits. - .TP - .BI \-w " windowid" - embed into windowid. -+.TP -+.BI \-d " separator" -+separate the input into two halves on the first occurrence of the given charcter. -+Display only the first half in dmenu and print the second half to stdout upon selection. -+Appending '|' to the separator reverses the display/printing order. -+.TP -+.BI \-D " separator" -+same as \-d but separate based on the last occurrence. - .SH USAGE - dmenu is completely controlled by the keyboard. Items are selected using the - arrow keys, page up, page down, home, and end. -diff --git a/dmenu.c b/dmenu.c -index d95e6c6..d304e3a 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -1,6 +1,7 @@ - /* See LICENSE file for copyright and license details. */ - #include <ctype.h> - #include <locale.h> -+#include <math.h> - #include <stdio.h> - #include <stdlib.h> - #include <string.h> -@@ -15,6 +16,7 @@ - #include <X11/extensions/Xinerama.h> - #endif - #include <X11/Xft/Xft.h> -+#include <X11/Xresource.h> - - #include "drw.h" - #include "util.h" -@@ -22,23 +24,33 @@ - /* 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 BOOL_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 */ --enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ -+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, -+ SchemeOut, SchemeLast }; /* color schemes */ -+ - - struct item { - char *text; -+ char *text_output; - struct item *left, *right; - int out; -+ double distance; - }; - - static char text[BUFSIZ] = ""; - static char *embed; -+static char separator; -+static int separator_greedy; -+static int separator_reverse; - static int bh, mw, mh; --static int inputw = 0, promptw; -+static int inputw = 0, promptw, passwd = 0; - static int lrpad; /* sum of left and right padding */ -+static int reject_no_match = 0; - static size_t cursor; - static struct item *items = NULL; - static struct item *matches, *matchend; -@@ -53,6 +65,10 @@ static XIC xic; - static Drw *drw; - static Clr *scheme[SchemeLast]; - -+/* Temporary arrays to allow overriding xresources values */ -+static char *colortemp[4]; -+static char *tempfonts; -+ - #include "config.h" - - static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -@@ -89,6 +105,15 @@ calcoffsets(void) - break; - } - -+static int -+max_textw(void) -+{ -+ int len = 0; -+ for (struct item *item = items; item && item->text; item++) -+ len = MAX(TEXTW(item->text), len); -+ return len; -+} -+ - static void - cleanup(void) - { -@@ -120,9 +145,49 @@ cistrstr(const char *h, const char *n) - return NULL; - } - -+static void -+drawhighlights(struct item *item, int x, int y, int maxw) -+{ -+ int i, indent; -+ char *highlight; -+ char c; -+ -+ if (!(strlen(item->text) && strlen(text))) -+ return; -+ -+ drw_setscheme(drw, scheme[item == sel -+ ? SchemeSelHighlight -+ : SchemeNormHighlight]); -+ for (i = 0, highlight = item->text; *highlight && text[i];) { -+ if (!fstrncmp(&(*highlight), &text[i], 1)) { -+ /* get indentation */ -+ c = *highlight; -+ *highlight = '\0'; -+ indent = TEXTW(item->text); -+ *highlight = c; -+ -+ /* highlight character */ -+ c = highlight[1]; -+ highlight[1] = '\0'; -+ drw_text( -+ drw, -+ x + indent - (lrpad / 2), -+ y, -+ MIN(maxw - indent, TEXTW(highlight) - lrpad), -+ bh, 0, highlight, 0 -+ ); -+ highlight[1] = c; -+ i++; -+ } -+ highlight++; -+ } -+} -+ -+ - static int - drawitem(struct item *item, int x, int y, int w) - { -+ int r; - if (item == sel) - drw_setscheme(drw, scheme[SchemeSel]); - else if (item->out) -@@ -130,7 +195,9 @@ drawitem(struct item *item, int x, int y, int w) - else - drw_setscheme(drw, scheme[SchemeNorm]); - -- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -+ drawhighlights(item, x, y, w); -+ return r; - } - - static void -@@ -138,7 +205,8 @@ drawmenu(void) - { - unsigned int curpos; - struct item *item; -- int x = 0, y = 0, w; -+ int x = 0, y = 0, fh = drw->fonts->h, w; -+ char *censort; - - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1); -@@ -150,12 +218,17 @@ drawmenu(void) - /* draw input field */ - w = (lines > 0 || !matches) ? mw - x : inputw; - drw_setscheme(drw, scheme[SchemeNorm]); -- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); -+ if (passwd) { -+ censort = ecalloc(1, sizeof(text)); -+ memset(censort, '.', strlen(text)); -+ drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); -+ free(censort); -+ } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); - - curpos = TEXTW(text) - TEXTW(&text[cursor]); - if ((curpos += lrpad / 2 - 1) < w) { - drw_setscheme(drw, scheme[SchemeNorm]); -- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); -+ drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0); - } - - if (lines > 0) { -@@ -217,9 +290,94 @@ grabkeyboard(void) - die("cannot grab keyboard"); - } - -+int -+compare_distance(const void *a, const void *b) -+{ -+ struct item *da = *(struct item **) a; -+ struct item *db = *(struct item **) b; -+ -+ if (!db) -+ return 1; -+ if (!da) -+ return -1; -+ -+ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; -+} -+ -+void -+fuzzymatch(void) -+{ -+ /* bang - we have so much memory */ -+ struct item *it; -+ struct item **fuzzymatches = NULL; -+ char c; -+ int number_of_matches = 0, i, pidx, sidx, eidx; -+ int text_len = strlen(text), itext_len; -+ -+ matches = matchend = NULL; -+ -+ /* walk through all items */ -+ for (it = items; it && it->text; it++) { -+ if (text_len) { -+ itext_len = strlen(it->text); -+ pidx = 0; /* pointer */ -+ sidx = eidx = -1; /* start of match, end of match */ -+ /* walk through item text */ -+ for (i = 0; i < itext_len && (c = it->text[i]); i++) { -+ /* fuzzy match pattern */ -+ if (!fstrncmp(&text[pidx], &c, 1)) { -+ if(sidx == -1) -+ sidx = i; -+ pidx++; -+ if (pidx == text_len) { -+ eidx = i; -+ break; -+ } -+ } -+ } -+ /* build list of matches */ -+ if (eidx != -1) { -+ /* compute distance */ -+ /* add penalty if match starts late (log(sidx+2)) -+ * add penalty for long a match without many matching characters */ -+ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); -+ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ -+ appenditem(it, &matches, &matchend); -+ number_of_matches++; -+ } -+ } else { -+ appenditem(it, &matches, &matchend); -+ } -+ } -+ -+ if (number_of_matches) { -+ /* initialize array with matches */ -+ if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) -+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); -+ for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { -+ fuzzymatches[i] = it; -+ } -+ /* sort matches according to distance */ -+ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); -+ /* rebuild list of matches */ -+ matches = matchend = NULL; -+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ -+ it->text; i++, it = fuzzymatches[i]) { -+ appenditem(it, &matches, &matchend); -+ } -+ free(fuzzymatches); -+ } -+ curr = sel = matches; -+ calcoffsets(); -+} -+ - static void - match(void) - { -+ if (fuzzy) { -+ fuzzymatch(); -+ return; -+ } - static char **tokv = NULL; - static int tokn = 0; - -@@ -276,12 +434,26 @@ insert(const char *str, ssize_t n) - { - if (strlen(text) + n > sizeof text - 1) - return; -+ -+ static char last[BUFSIZ] = ""; -+ if(reject_no_match) { -+ /* store last text value in case we need to revert it */ -+ memcpy(last, text, BUFSIZ); -+ } -+ - /* move existing text out of the way, insert new text, and update cursor */ - memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - if (n > 0) - memcpy(&text[cursor], str, n); - cursor += n; - match(); -+ -+ if(!matches && reject_no_match) { -+ /* revert to last text value if theres no match */ -+ memcpy(text, last, BUFSIZ); -+ cursor -= n; -+ match(); -+ } - } - - static size_t -@@ -480,7 +652,7 @@ insert: - break; - case XK_Return: - case XK_KP_Enter: -- puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); -+ puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); - if (!(ev->state & ControlMask)) { - cleanup(); - exit(0); -@@ -518,6 +690,156 @@ draw: - drawmenu(); - } - -+static void -+buttonpress(XEvent *e) -+{ -+ struct item *item; -+ XButtonPressedEvent *ev = &e->xbutton; -+ int x = 0, y = 0, h = bh, w; -+ -+ if (ev->window != win) -+ return; -+ -+ /* right-click: exit */ -+ if (ev->button == Button3) -+ exit(1); -+ -+ if (prompt && *prompt) -+ x += promptw; -+ -+ /* input field */ -+ w = (lines > 0 || !matches) ? mw - x : inputw; -+ -+ /* left-click on input: clear input, -+ * NOTE: if there is no left-arrow the space for < is reserved so -+ * add that to the input width */ -+ if (ev->button == Button1 && -+ ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + -+ ((!prev || !curr->left) ? TEXTW("<") : 0)) || -+ (lines > 0 && ev->y >= y && ev->y <= y + h))) { -+ insert(NULL, -cursor); -+ drawmenu(); -+ return; -+ } -+ /* middle-mouse click: paste selection */ -+ if (ev->button == Button2) { -+ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, -+ utf8, utf8, win, CurrentTime); -+ drawmenu(); -+ return; -+ } -+ /* scroll up */ -+ if (ev->button == Button4 && prev) { -+ sel = curr = prev; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ /* scroll down */ -+ if (ev->button == Button5 && next) { -+ sel = curr = next; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ if (ev->button != Button1) -+ return; -+ /* disabled below, needs to be fixed */ -+ /* -+ if (ev->state & ~ControlMask) -+ return; -+ */ -+ if (lines > 0) { -+ /* vertical list: (ctrl)left-click on item */ -+ w = mw - x; -+ for (item = curr; item != next; item = item->right) { -+ y += h; -+ if (ev->y >= y && ev->y <= (y + h)) { -+ puts(item->text); -+ if (!(ev->state & ControlMask)) -+ exit(0); -+ sel = item; -+ if (sel) { -+ sel->out = 1; -+ drawmenu(); -+ } -+ return; -+ } -+ } -+ } else if (matches) { -+ /* left-click on left arrow */ -+ x += inputw; -+ w = TEXTW("<"); -+ if (prev && curr->left) { -+ if (ev->x >= x && ev->x <= x + w) { -+ sel = curr = prev; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ /* horizontal list: (ctrl)left-click on item */ -+ for (item = curr; item != next; item = item->right) { -+ x += w; -+ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -+ if (ev->x >= x && ev->x <= x + w) { -+ puts(item->text); -+ if (!(ev->state & ControlMask)) -+ exit(0); -+ sel = item; -+ if (sel) { -+ sel->out = 1; -+ drawmenu(); -+ } -+ return; -+ } -+ } -+ /* left-click on right arrow */ -+ w = TEXTW(">"); -+ x = mw - w; -+ if (next && ev->x >= x && ev->x <= x + w) { -+ sel = curr = next; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+} -+ -+static void -+mousemove(XEvent *e) -+{ -+ struct item *item; -+ XPointerMovedEvent *ev = &e->xmotion; -+ int x = 0, y = 0, h = bh, w; -+ -+ if (lines > 0) { -+ w = mw - x; -+ for (item = curr; item != next; item = item->right) { -+ y += h; -+ if (ev->y >= y && ev->y <= (y + h)) { -+ sel = item; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ } else if (matches) { -+ x += inputw; -+ w = TEXTW("<"); -+ for (item = curr; item != next; item = item->right) { -+ x += w; -+ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); -+ if (ev->x >= x && ev->x <= x + w) { -+ sel = item; -+ calcoffsets(); -+ drawmenu(); -+ return; -+ } -+ } -+ } -+} -+ - static void - paste(void) - { -@@ -542,6 +864,11 @@ readstdin(void) - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; -+ if(passwd){ -+ inputw = lines = 0; -+ return; -+ } -+ - - /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { -@@ -552,6 +879,18 @@ readstdin(void) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf) + 1); -+ if (separator && (p = separator_greedy ? -+ strrchr(items[i].text, separator) : strchr(items[i].text, separator))) { -+ *p = '\0'; -+ items[i].text_output = ++p; -+ } else { -+ items[i].text_output = items[i].text; -+ } -+ if (separator_reverse) { -+ p = items[i].text; -+ items[i].text = items[i].text_output; -+ items[i].text_output = p; -+ } - items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { -@@ -579,6 +918,12 @@ run(void) - break; - cleanup(); - exit(1); -+ case ButtonPress: -+ buttonpress(&ev); -+ break; -+ case MotionNotify: -+ mousemove(&ev); -+ break; - case Expose: - if (ev.xexpose.count == 0) - drw_map(drw, win, 0, 0, mw, mh); -@@ -619,16 +964,23 @@ setup(void) - int a, di, n, area = 0; - #endif - /* init appearance */ -- for (j = 0; j < SchemeLast; j++) -- scheme[j] = drw_scm_create(drw, colors[j], 2); -+ for (j = 0; j < SchemeLast; j++) { -+ scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); -+ } -+ for (j = 0; j < SchemeOut; ++j) { -+ for (i = 0; i < 2; ++i) -+ free(colors[j][i]); -+ } - - clip = XInternAtom(dpy, "CLIPBOARD", False); - utf8 = XInternAtom(dpy, "UTF8_STRING", False); - - /* calculate menu geometry */ - bh = drw->fonts->h + 2; -+ bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ - lines = MAX(lines, 0); - mh = (lines + 1) * bh; -+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - #ifdef XINERAMA - i = 0; - if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { -@@ -652,12 +1004,19 @@ 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 (BOOL_INTERSECT(x, y, 1, 1, info[i])) - break; - -- x = info[i].x_org; -- y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -- mw = info[i].width; -+ if (centered) { -+ mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); -+ x = info[i].x_org + ((info[i].width - mw) / 2); -+ y = info[i].y_org + ((info[i].height - mh) / 2); -+ } else { -+ x = info[i].x_org; -+ y = info[i].y_org + (topbar ? 0 : info[i].height - mh); -+ mw = info[i].width; -+ } -+ - XFree(info); - } else - #endif -@@ -665,21 +1024,30 @@ setup(void) - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - die("could not get embedding window attributes: 0x%lx", - parentwin); -- x = 0; -- y = topbar ? 0 : wa.height - mh; -- mw = wa.width; -+ -+ if (centered) { -+ mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); -+ x = (wa.width - mw) / 2; -+ y = (wa.height - mh) / 2; -+ } else { -+ x = 0; -+ y = topbar ? 0 : wa.height - mh; -+ mw = wa.width; -+ } - } -- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - inputw = MIN(inputw, mw/3); - 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, -+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | -+ ButtonPressMask | PointerMotionMask; -+ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, - CopyFromParent, CopyFromParent, CopyFromParent, - CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); -+ if (border_width) -+ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); - XSetClassHint(dpy, win, &ch); - - -@@ -707,11 +1075,64 @@ 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); -+ fputs("usage: dmenu [-bfirvP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -+ " [-nb color] [-nf color] [-sb color] [-sf color] [-bw width]\n" -+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n" -+ " [-d separator] [-D separator]\n", stderr); - exit(1); - } - -+void -+readxresources(void) { -+ XrmInitialize(); -+ -+ char* xrm; -+ if ((xrm = XResourceManagerString(drw->dpy))) { -+ char *type; -+ XrmDatabase xdb = XrmGetStringDatabase(xrm); -+ XrmValue xval; -+ -+ if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval)) -+ fonts[0] = strdup(xval.addr); -+ else -+ fonts[0] = strdup(fonts[0]); -+ if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval)) -+ colors[SchemeNorm][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeNorm][ColBg] = strdup(colors[SchemeNorm][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval)) -+ colors[SchemeNorm][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeNorm][ColFg] = strdup(colors[SchemeNorm][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval)) -+ colors[SchemeSel][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeSel][ColBg] = strdup(colors[SchemeSel][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval)) -+ colors[SchemeSel][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeSel][ColFg] = strdup(colors[SchemeSel][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.selhibackground", "*", &type, &xval)) -+ colors[SchemeSelHighlight][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeSelHighlight][ColBg] = strdup(colors[SchemeSel][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.selhiforeground", "*", &type, &xval)) -+ colors[SchemeSelHighlight][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeSelHighlight][ColFg] = strdup(colors[SchemeSel][ColFg]); -+ if (XrmGetResource(xdb, "dmenu.hibackground", "*", &type, &xval)) -+ colors[SchemeNormHighlight][ColBg] = strdup(xval.addr); -+ else -+ colors[SchemeNormHighlight][ColBg] = strdup(colors[SchemeNorm][ColBg]); -+ if (XrmGetResource(xdb, "dmenu.hiforeground", "*", &type, &xval)) -+ colors[SchemeNormHighlight][ColFg] = strdup(xval.addr); -+ else -+ colors[SchemeNormHighlight][ColFg] = strdup(colors[SchemeNorm][ColFg]); -+ -+ XrmDestroyDatabase(xdb); -+ } -+} -+ - int - main(int argc, char *argv[]) - { -@@ -727,30 +1148,56 @@ main(int argc, char *argv[]) - topbar = 0; - else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ - fast = 1; -+ else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ -+ centered = 1; -+ else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */ -+ fuzzy = 1; - else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ - fstrncmp = strncasecmp; - fstrstr = cistrstr; -- } else if (i + 1 == argc) -+ } else if (!strcmp(argv[i], "-P")) /* is the input a password */ -+ passwd = 1; -+ else if (!strcmp(argv[i], "-r")) /* reject input which results in no match */ -+ reject_no_match = 1; -+ else if (i + 1 == argc) - usage(); - /* these options take one argument */ - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ - lines = atoi(argv[++i]); -+ else if (!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ -+ lineheight = atoi(argv[++i]); -+ lineheight = MAX(lineheight, min_lineheight); -+ } - else if (!strcmp(argv[i], "-m")) - mon = atoi(argv[++i]); - else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ - prompt = argv[++i]; - else if (!strcmp(argv[i], "-fn")) /* font or font set */ -- fonts[0] = argv[++i]; -+ tempfonts = argv[++i]; - else if (!strcmp(argv[i], "-nb")) /* normal background color */ -- colors[SchemeNorm][ColBg] = argv[++i]; -+ colortemp[0] = argv[++i]; - else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ -- colors[SchemeNorm][ColFg] = argv[++i]; -+ colortemp[1] = argv[++i]; - else if (!strcmp(argv[i], "-sb")) /* selected background color */ -- colors[SchemeSel][ColBg] = argv[++i]; -+ colortemp[2] = argv[++i]; - else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ -- colors[SchemeSel][ColFg] = argv[++i]; -+ colortemp[3] = argv[++i]; -+ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ -+ colors[SchemeNormHighlight][ColBg] = argv[++i]; -+ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ -+ colors[SchemeNormHighlight][ColFg] = argv[++i]; -+ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ -+ colors[SchemeSelHighlight][ColBg] = argv[++i]; -+ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ -+ colors[SchemeSelHighlight][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-w")) /* embedding window id */ - embed = argv[++i]; -+ else if (!strcmp(argv[i], "-d") || /* field separator */ -+ (separator_greedy = !strcmp(argv[i], "-D"))) { -+ separator = argv[++i][0]; -+ separator_reverse = argv[i][1] == '|'; -+ } else if (!strcmp(argv[i], "-bw")) -+ border_width = atoi(argv[++i]); /* border width */ - else - usage(); - -@@ -766,8 +1213,23 @@ main(int argc, char *argv[]) - die("could not get embedding window attributes: 0x%lx", - parentwin); - drw = drw_create(dpy, screen, root, wa.width, wa.height); -- if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) -+ readxresources(); -+ /* Now we check whether to override xresources with commandline parameters */ -+ if ( tempfonts ) -+ fonts[0] = strdup(tempfonts); -+ if ( colortemp[0]) -+ colors[SchemeNorm][ColBg] = strdup(colortemp[0]); -+ if ( colortemp[1]) -+ colors[SchemeNorm][ColFg] = strdup(colortemp[1]); -+ if ( colortemp[2]) -+ colors[SchemeSel][ColBg] = strdup(colortemp[2]); -+ if ( colortemp[3]) -+ colors[SchemeSel][ColFg] = strdup(colortemp[3]); -+ -+ if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) - die("no fonts could be loaded."); -+ -+ free(fonts[0]); - lrpad = drw->fonts->h; - - #ifdef __OpenBSD__ |