diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | config.def.h | 15 | ||||
-rw-r--r-- | config.h | 34 | ||||
-rw-r--r-- | config.mk | 7 | ||||
-rw-r--r-- | dmenu-5.1.diff | 3199 | ||||
-rw-r--r-- | dmenu.1 | 51 | ||||
-rw-r--r-- | dmenu.c | 570 | ||||
-rw-r--r-- | drw.c | 102 | ||||
-rw-r--r-- | drw.h | 1 | ||||
-rw-r--r-- | patches/dmenu-borderoption-20200217-bf60a1e.diff | 46 | ||||
-rw-r--r-- | patches/dmenu-center-20200111-8cd37e1.diff | 120 | ||||
-rw-r--r-- | patches/dmenu-fuzzyhighlight-4.9.diff | 152 | ||||
-rw-r--r-- | patches/dmenu-fuzzymatch-4.9.diff | 163 | ||||
-rw-r--r-- | patches/dmenu-lineheight-5.0.diff | 106 | ||||
-rw-r--r-- | patches/dmenu-mousesupport-5.1.diff | 144 | ||||
-rw-r--r-- | patches/dmenu-mousesupporthoverbgcol-5.0.diff | 184 | ||||
-rw-r--r-- | patches/dmenu-password-5.0.diff | 103 | ||||
-rw-r--r-- | patches/dmenu-rejectnomatch-4.7.diff | 82 | ||||
-rw-r--r-- | patches/dmenu-separator-20210904-d78ff08.diff | 101 | ||||
-rw-r--r-- | patches/dmenu-xresources-4.9.diff | 126 | ||||
-rw-r--r-- | util.c | 23 |
21 files changed, 133 insertions, 5199 deletions
diff --git a/README.md b/README.md deleted file mode 100644 index 743aa3f..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# dmenu-zir - -My personal fork of dmenu
\ No newline at end of file diff --git a/config.def.h b/config.def.h index 3fb3def..1edb647 100644 --- a/config.def.h +++ b/config.def.h @@ -2,33 +2,22 @@ /* 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 char *fonts[] = { +static const char *fonts[] = { "monospace:size=10" }; static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -static char *colors[SchemeLast][2] = { +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 */ 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.h b/config.h deleted file mode 100644 index 19c566f..0000000 --- a/config.h +++ /dev/null @@ -1,34 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -/* 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 char *fonts[] = { - "monospace:size=10" -}; -static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -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 = 24; -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; @@ -1,5 +1,5 @@ # dmenu version -VERSION = 5.1 +VERSION = 5.2 # paths PREFIX = /usr/local @@ -17,14 +17,15 @@ FREETYPELIBS = -lfontconfig -lXft FREETYPEINC = /usr/include/freetype2 # OpenBSD (uncomment) #FREETYPEINC = $(X11INC)/freetype2 +#MANPREFIX = ${PREFIX}/man # includes and libs INCS = -I$(X11INC) -I$(FREETYPEINC) -LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) -CFLAGS = -std=c99 -pedantic -Wall -O3 -march=native $(INCS) $(CPPFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) LDFLAGS = $(LIBS) # compiler and linker 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__ @@ -3,11 +3,9 @@ dmenu \- dynamic menu .SH SYNOPSIS .B dmenu -.RB [ \-bFfirvP ] +.RB [ \-bfiv ] .RB [ \-l .IR lines ] -.RB [ \-h -.IR height ] .RB [ \-m .IR monitor ] .RB [ \-p @@ -22,20 +20,8 @@ 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 @@ -54,12 +40,6 @@ 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. @@ -67,18 +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 -.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. @@ -104,31 +75,11 @@ 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. @@ -1,7 +1,6 @@ /* 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> @@ -16,7 +15,6 @@ #include <X11/extensions/Xinerama.h> #endif #include <X11/Xft/Xft.h> -#include <X11/Xresource.h> #include "drw.h" #include "util.h" @@ -24,33 +22,23 @@ /* 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, SchemeNormHighlight, SchemeSelHighlight, - SchemeOut, SchemeLast }; /* color schemes */ - +enum { SchemeNorm, SchemeSel, 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, passwd = 0; +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; @@ -65,15 +53,18 @@ 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; static char *(*fstrstr)(const char *, const char *) = strstr; +static unsigned int +textw_clamp(const char *str, unsigned int n) +{ + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; + return MIN(w, n); +} + static void appenditem(struct item *item, struct item **list, struct item **last) { @@ -98,22 +89,13 @@ calcoffsets(void) n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); /* calculate which items will begin the next page and previous page */ for (i = 0, next = curr; next; next = next->right) - if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) break; for (i = 0, prev = curr; prev && prev->left; prev = prev->left) - if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) break; } -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) { @@ -122,6 +104,9 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); for (i = 0; i < SchemeLast; i++) free(scheme[i]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); + free(items); drw_free(drw); XSync(dpy, False); XCloseDisplay(dpy); @@ -145,49 +130,9 @@ 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) @@ -195,9 +140,7 @@ drawitem(struct item *item, int x, int y, int w) else drw_setscheme(drw, scheme[SchemeNorm]); - r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); - drawhighlights(item, x, y, w); - return r; + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); } static void @@ -205,8 +148,7 @@ drawmenu(void) { unsigned int curpos; struct item *item; - int x = 0, y = 0, fh = drw->fonts->h, w; - char *censort; + int x = 0, y = 0, w; drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, 0, 0, mw, mh, 1, 1); @@ -218,17 +160,12 @@ drawmenu(void) /* draw input field */ w = (lines > 0 || !matches) ? mw - x : inputw; drw_setscheme(drw, scheme[SchemeNorm]); - 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); + 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 + (bh - fh) / 2, 2, fh - 4, 1, 0); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); } if (lines > 0) { @@ -245,7 +182,7 @@ drawmenu(void) } x += w; for (item = curr; item != next; item = item->right) - x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); if (next) { w = TEXTW(">"); drw_setscheme(drw, scheme[SchemeNorm]); @@ -290,94 +227,9 @@ 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; @@ -390,7 +242,7 @@ match(void) /* separate input text into tokens to be matched individually */ for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) - die("cannot realloc %u bytes:", tokn * sizeof *tokv); + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); len = tokc ? strlen(tokv[0]) : 0; matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; @@ -434,26 +286,12 @@ 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 @@ -577,7 +415,7 @@ keypress(XKeyEvent *ev) switch(ksym) { default: insert: - if (!iscntrl(*buf)) + if (!iscntrl((unsigned char)*buf)) insert(buf, len); break; case XK_Delete: @@ -652,7 +490,7 @@ insert: break; case XK_Return: case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); if (!(ev->state & ControlMask)) { cleanup(); exit(0); @@ -679,9 +517,9 @@ insert: case XK_Tab: if (!sel) return; - strncpy(text, sel->text, sizeof text - 1); - text[sizeof text - 1] = '\0'; - cursor = strlen(text); + cursor = strnlen(sel->text, sizeof text - 1); + memcpy(text, sel->text, cursor); + text[cursor] = '\0'; match(); break; } @@ -691,156 +529,6 @@ 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; @@ -861,46 +549,22 @@ paste(void) static void readstdin(void) { - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - unsigned int tmpmax = 0; - if(passwd){ - inputw = lines = 0; - return; - } - + char *line = NULL; + size_t i, junk, size = 0; + ssize_t len; /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { if (i + 1 >= size / sizeof *items) if (!(items = realloc(items, (size += BUFSIZ)))) - die("cannot realloc %u bytes:", size); - if ((p = strchr(buf, '\n'))) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf) + 1); - 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; - } + die("cannot realloc %zu bytes:", size); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); - if (tmpmax > inputw) { - inputw = tmpmax; - imax = i; - } } if (items) items[i].text = NULL; - inputw = items ? TEXTW(items[imax].text) : 0; lines = MIN(lines, i); } @@ -918,12 +582,6 @@ 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); @@ -964,23 +622,16 @@ setup(void) int a, di, n, area = 0; #endif /* init appearance */ - 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]); - } + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); 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))) { @@ -1004,19 +655,12 @@ 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 (BOOL_INTERSECT(x, y, 1, 1, info[i])) + if (INTERSECT(x, y, 1, 1, info[i]) != 0) break; - 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; - } - + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; XFree(info); } else #endif @@ -1024,30 +668,21 @@ setup(void) if (!XGetWindowAttributes(dpy, parentwin, &wa)) die("could not get embedding window attributes: 0x%lx", parentwin); - - 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; - } + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; } - inputw = MIN(inputw, mw/3); + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = mw / 3; /* input width: ~33% of monitor width */ match(); /* create menu window */ swa.override_redirect = True; swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; - swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | - ButtonPressMask | PointerMotionMask; - win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); - if (border_width) - XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); XSetClassHint(dpy, win, &ch); @@ -1075,62 +710,8 @@ setup(void) static void usage(void) { - 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); - } + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); } int @@ -1148,56 +729,30 @@ 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 (!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) + } 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 */ - tempfonts = argv[++i]; + fonts[0] = argv[++i]; else if (!strcmp(argv[i], "-nb")) /* normal background color */ - colortemp[0] = argv[++i]; + colors[SchemeNorm][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ - colortemp[1] = argv[++i]; + colors[SchemeNorm][ColFg] = argv[++i]; else if (!strcmp(argv[i], "-sb")) /* selected background color */ - colortemp[2] = argv[++i]; + colors[SchemeSel][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ - 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]; + 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 if (!strcmp(argv[i], "-bw")) - border_width = atoi(argv[++i]); /* border width */ else usage(); @@ -1213,23 +768,8 @@ 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); - 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))) + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) die("no fonts could be loaded."); - - free(fonts[0]); lrpad = drw->fonts->h; #ifdef __OpenBSD__ @@ -133,19 +133,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) die("no font specified."); } - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - font = ecalloc(1, sizeof(Fnt)); font->xfont = xfont; font->pattern = pattern; @@ -251,12 +238,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { - char buf[1024]; - int ty; - unsigned int ew=0; + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; XftDraw *d = NULL; Fnt *usedfont, *curfont, *nextfont; - size_t i, len; int utf8strlen, utf8charlen, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; @@ -264,13 +249,17 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp FcPattern *fcpattern; FcPattern *match; XftResult result; - int charexists = 0; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; - if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) return 0; if (!render) { - w = ~w; + w = invert ? invert : ~invert; } else { XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); @@ -282,8 +271,10 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); while (1) { - utf8strlen = 0; + ew = ellipsis_len = utf8strlen = 0; utf8str = text; nextfont = NULL; while (*text) { @@ -291,9 +282,27 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp for (curfont = drw->fonts; curfont; curfont = curfont->next) { charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { - if (curfont == usedfont) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { utf8strlen += utf8charlen; text += utf8charlen; + ew += tmpw; } else { nextfont = curfont; } @@ -301,36 +310,25 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } } - if (!charexists || nextfont) + if (overflow || !charexists || nextfont) break; else charexists = 0; } if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) - drw_font_getexts(usedfont, utf8str, len, &ew, NULL); - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); } + x += ew; + w -= ew; } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); - if (!*text) { + if (!*text || overflow) { break; } else if (nextfont) { charexists = 0; @@ -340,6 +338,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp * character must be drawn. */ charexists = 1; + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); @@ -351,7 +355,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp fcpattern = FcPatternDuplicate(drw->fonts->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); @@ -368,6 +371,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp curfont->next = usedfont; } else { xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: usedfont = drw->fonts; } } @@ -397,6 +402,15 @@ drw_fontset_getwidth(Drw *drw, const char *text) return drw_text(drw, 0, 0, 0, 0, 0, text, 0); } +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) { @@ -35,6 +35,7 @@ void drw_free(Drw *drw); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); void drw_fontset_free(Fnt* set); unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); /* Colorscheme abstraction */ diff --git a/patches/dmenu-borderoption-20200217-bf60a1e.diff b/patches/dmenu-borderoption-20200217-bf60a1e.diff deleted file mode 100644 index b9b5726..0000000 --- a/patches/dmenu-borderoption-20200217-bf60a1e.diff +++ /dev/null @@ -1,46 +0,0 @@ -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 deleted file mode 100644 index af249a6..0000000 --- a/patches/dmenu-center-20200111-8cd37e1.diff +++ /dev/null @@ -1,120 +0,0 @@ -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 deleted file mode 100644 index 58d5c6f..0000000 --- a/patches/dmenu-fuzzyhighlight-4.9.diff +++ /dev/null @@ -1,152 +0,0 @@ -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 deleted file mode 100644 index 9fd206d..0000000 --- a/patches/dmenu-fuzzymatch-4.9.diff +++ /dev/null @@ -1,163 +0,0 @@ -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 deleted file mode 100644 index 3b0df3d..0000000 --- a/patches/dmenu-lineheight-5.0.diff +++ /dev/null @@ -1,106 +0,0 @@ -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 deleted file mode 100644 index 49824ba..0000000 --- a/patches/dmenu-mousesupport-5.1.diff +++ /dev/null @@ -1,144 +0,0 @@ -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 deleted file mode 100644 index 1fb7d41..0000000 --- a/patches/dmenu-mousesupporthoverbgcol-5.0.diff +++ /dev/null @@ -1,184 +0,0 @@ -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 deleted file mode 100644 index 7a187b9..0000000 --- a/patches/dmenu-password-5.0.diff +++ /dev/null @@ -1,103 +0,0 @@ -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 deleted file mode 100644 index 329ab1d..0000000 --- a/patches/dmenu-rejectnomatch-4.7.diff +++ /dev/null @@ -1,82 +0,0 @@ -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 deleted file mode 100644 index be30420..0000000 --- a/patches/dmenu-separator-20210904-d78ff08.diff +++ /dev/null @@ -1,101 +0,0 @@ -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 deleted file mode 100644 index 267fb0a..0000000 --- a/patches/dmenu-xresources-4.9.diff +++ /dev/null @@ -1,126 +0,0 @@ -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__ @@ -6,18 +6,9 @@ #include "util.h" -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - die("calloc:"); - return p; -} - void -die(const char *fmt, ...) { +die(const char *fmt, ...) +{ va_list ap; va_start(ap, fmt); @@ -33,3 +24,13 @@ die(const char *fmt, ...) { exit(1); } + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} |