From d37bbc0c900aa6e42e9f2dac7500c468083bb69b Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:35:45 -0600 Subject: ignore unneeded files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10717b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +dmenu +stest -- cgit v1.2.3 From 3e780ab7e8f5a6358c89813e2d2e430b90fe8a66 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:38:42 -0600 Subject: reset to vanilla dmenu-5.2 --- README.md | 3 - config.def.h | 15 +- config.h | 34 - config.mk | 7 +- dmenu-5.1.diff | 3199 ---------------------- dmenu.1 | 51 +- dmenu.c | 570 +--- drw.c | 102 +- drw.h | 1 + patches/dmenu-borderoption-20200217-bf60a1e.diff | 46 - patches/dmenu-center-20200111-8cd37e1.diff | 120 - patches/dmenu-fuzzyhighlight-4.9.diff | 152 - patches/dmenu-fuzzymatch-4.9.diff | 163 -- patches/dmenu-lineheight-5.0.diff | 106 - patches/dmenu-mousesupport-5.1.diff | 144 - patches/dmenu-mousesupporthoverbgcol-5.0.diff | 184 -- patches/dmenu-password-5.0.diff | 103 - patches/dmenu-rejectnomatch-4.7.diff | 82 - patches/dmenu-separator-20210904-d78ff08.diff | 101 - patches/dmenu-xresources-4.9.diff | 126 - util.c | 23 +- 21 files changed, 133 insertions(+), 5199 deletions(-) delete mode 100644 README.md delete mode 100644 config.h delete mode 100644 dmenu-5.1.diff delete mode 100644 patches/dmenu-borderoption-20200217-bf60a1e.diff delete mode 100644 patches/dmenu-center-20200111-8cd37e1.diff delete mode 100644 patches/dmenu-fuzzyhighlight-4.9.diff delete mode 100644 patches/dmenu-fuzzymatch-4.9.diff delete mode 100644 patches/dmenu-lineheight-5.0.diff delete mode 100644 patches/dmenu-mousesupport-5.1.diff delete mode 100644 patches/dmenu-mousesupporthoverbgcol-5.0.diff delete mode 100644 patches/dmenu-password-5.0.diff delete mode 100644 patches/dmenu-rejectnomatch-4.7.diff delete mode 100644 patches/dmenu-separator-20210904-d78ff08.diff delete mode 100644 patches/dmenu-xresources-4.9.diff 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; diff --git a/config.mk b/config.mk index 56427e7..566348b 100644 --- a/config.mk +++ b/config.mk @@ -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 - #include -+#include - #include - #include - #include -@@ -15,6 +16,7 @@ - #include - #endif - #include -+#include - - #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 -+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 -+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 -+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 -+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 -+ #include -++#include -+ #include -+ #include -+ #include -+@@ -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 -+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 -+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 -+ #endif -+ #include -++#include -+ -+ #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 - #include -+#include - #include - #include - #include -@@ -15,6 +16,7 @@ - #include - #endif - #include -+#include - - #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/dmenu.1 b/dmenu.1 index 1602793..323f93c 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -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. diff --git a/dmenu.c b/dmenu.c index d304e3a..7cf253b 100644 --- a/dmenu.c +++ b/dmenu.c @@ -1,7 +1,6 @@ /* See LICENSE file for copyright and license details. */ #include #include -#include #include #include #include @@ -16,7 +15,6 @@ #include #endif #include -#include #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; } @@ -690,156 +528,6 @@ 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) { @@ -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__ diff --git a/drw.c b/drw.c index db9284c..a58a2b4 100644 --- a/drw.c +++ b/drw.c @@ -133,19 +133,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) die("no font specified."); } - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - font = ecalloc(1, sizeof(Fnt)); font->xfont = xfont; font->pattern = pattern; @@ -251,12 +238,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { - char buf[1024]; - int ty; - unsigned int ew=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) { diff --git a/drw.h b/drw.h index 4c67419..fd7631b 100644 --- a/drw.h +++ b/drw.h @@ -35,6 +35,7 @@ void drw_free(Drw *drw); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); void drw_fontset_free(Fnt* set); unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); /* Colorscheme abstraction */ diff --git a/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 -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 -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 -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 -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 - #include -+#include - #include - #include - #include -@@ -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 -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 -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 - #endif - #include -+#include - - #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/util.c b/util.c index fe044fc..96b82c9 100644 --- a/util.c +++ b/util.c @@ -6,18 +6,9 @@ #include "util.h" -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - die("calloc:"); - return p; -} - void -die(const char *fmt, ...) { +die(const char *fmt, ...) +{ va_list ap; va_start(ap, fmt); @@ -33,3 +24,13 @@ die(const char *fmt, ...) { exit(1); } + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} -- cgit v1.2.3 From b31a684c71db67dfe9661a86364539822d4d439d Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:45:02 -0600 Subject: ignore config.h --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 10717b6..d29fa65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o dmenu stest +config.h -- cgit v1.2.3 From 2e426cac046fdd057880eda259482db0e965aebd Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:45:16 -0600 Subject: add separator patch --- dmenu.1 | 12 ++++ dmenu.c | 31 +++++++-- patches/dmenu-separator-5.2.patch | 133 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 patches/dmenu-separator-5.2.patch 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 7cf253b..a8eb321 100644 --- a/dmenu.c +++ b/dmenu.c @@ -30,12 +30,15 @@ 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, separator_reverse; +static char * (*sepchr)(const char *, int); static int bh, mw, mh; static int inputw = 0, promptw; static int lrpad; /* sum of left and right padding */ @@ -105,7 +108,7 @@ cleanup(void) for (i = 0; i < SchemeLast; i++) free(scheme[i]); for (i = 0; items && items[i].text; ++i) - free(items[i].text); + free(separator_reverse ? items[i].text_output : items[i].text); free(items); drw_free(drw); XSync(dpy, False); @@ -490,7 +493,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); @@ -549,7 +552,7 @@ paste(void) static void readstdin(void) { - char *line = NULL; + char *p, *line = NULL; size_t i, junk, size = 0; ssize_t len; @@ -561,6 +564,19 @@ readstdin(void) if (line[len - 1] == '\n') line[len - 1] = '\0'; items[i].text = line; + + if (separator && (p = sepchr(items[i].text, separator)) != NULL) { + *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; } if (items) @@ -711,7 +727,8 @@ static void usage(void) { die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" + " [-d separator] [-D separator]"); } int @@ -753,6 +770,12 @@ 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 */ + !strcmp(argv[i], "-D")) { + sepchr = argv[i][1] == 'D' ? strrchr : strchr; + separator = argv[++i][0]; + separator_reverse = argv[i][1] == '|'; + } else usage(); diff --git a/patches/dmenu-separator-5.2.patch b/patches/dmenu-separator-5.2.patch new file mode 100644 index 0000000..09f41cb --- /dev/null +++ b/patches/dmenu-separator-5.2.patch @@ -0,0 +1,133 @@ +From db596234b244382e984228791e840190d82967ea Mon Sep 17 00:00:00 2001 +From: NRK +Date: Fri, 3 Sep 2021 11:11:14 +0600 +Subject: [PATCH] patch: seperator + +--- + dmenu.1 | 12 ++++++++++++ + dmenu.c | 31 +++++++++++++++++++++++++++---- + 2 files changed, 39 insertions(+), 4 deletions(-) + +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 7cf253b..a8eb321 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -30,12 +30,15 @@ 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, separator_reverse; ++static char * (*sepchr)(const char *, int); + static int bh, mw, mh; + static int inputw = 0, promptw; + static int lrpad; /* sum of left and right padding */ +@@ -105,7 +108,7 @@ cleanup(void) + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + for (i = 0; items && items[i].text; ++i) +- free(items[i].text); ++ free(separator_reverse ? items[i].text_output : items[i].text); + free(items); + drw_free(drw); + XSync(dpy, False); +@@ -490,7 +493,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); +@@ -549,7 +552,7 @@ paste(void) + static void + readstdin(void) + { +- char *line = NULL; ++ char *p, *line = NULL; + size_t i, junk, size = 0; + ssize_t len; + +@@ -561,6 +564,19 @@ readstdin(void) + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; ++ ++ if (separator && (p = sepchr(items[i].text, separator)) != NULL) { ++ *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; + } + if (items) +@@ -711,7 +727,8 @@ static void + usage(void) + { + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" +- " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); ++ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" ++ " [-d separator] [-D separator]"); + } + + int +@@ -753,6 +770,12 @@ 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 */ ++ !strcmp(argv[i], "-D")) { ++ sepchr = argv[i][1] == 'D' ? strrchr : strchr; ++ separator = argv[++i][0]; ++ separator_reverse = argv[i][1] == '|'; ++ } + else + usage(); + +-- +2.35.1 + -- cgit v1.2.3 From addad8d7671563820a8a10cb181e1034d1891678 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:47:13 -0600 Subject: add borderoption patch --- config.def.h | 3 ++ dmenu.c | 6 +++- patches/dmenu-borderoption-20200217-bf60a1e.diff | 46 ++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 patches/dmenu-borderoption-20200217-bf60a1e.diff 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 a8eb321..dcc09da 100644 --- a/dmenu.c +++ b/dmenu.c @@ -696,9 +696,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); @@ -770,6 +772,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 if (!strcmp(argv[i], "-d") || /* field separator */ !strcmp(argv[i], "-D")) { sepchr = argv[i][1] == 'D' ? strrchr : strchr; 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 +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 + -- cgit v1.2.3 From 59fbcc368693d01182e41ec8f397b9e47325cd8c Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:48:38 -0600 Subject: add center patch --- config.def.h | 2 + dmenu.1 | 3 ++ dmenu.c | 38 ++++++++++++--- patches/dmenu-center-5.2.diff | 104 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 patches/dmenu-center-5.2.diff diff --git a/config.def.h b/config.def.h index dd3eb31..cfff3e4 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 d511148..bd3dd40 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -44,6 +44,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 dcc09da..1153e6e 100644 --- a/dmenu.c +++ b/dmenu.c @@ -99,6 +99,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) { @@ -648,6 +657,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))) { @@ -674,9 +684,16 @@ setup(void) if (INTERSECT(x, y, 1, 1, info[i]) != 0) 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 @@ -684,9 +701,16 @@ 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 = mw / 3; /* input width: ~33% of monitor width */ @@ -748,6 +772,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; diff --git a/patches/dmenu-center-5.2.diff b/patches/dmenu-center-5.2.diff new file mode 100644 index 0000000..9401dc5 --- /dev/null +++ b/patches/dmenu-center-5.2.diff @@ -0,0 +1,104 @@ +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 27b7a30..427fb04 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -96,6 +96,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) + { +@@ -636,6 +645,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))) { +@@ -662,9 +672,16 @@ setup(void) + if (INTERSECT(x, y, 1, 1, info[i]) != 0) + 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 +@@ -672,9 +689,16 @@ 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 = mw / 3; /* input width: ~33% of monitor width */ +@@ -733,6 +757,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; -- cgit v1.2.3 From 17f838e4987aae8a81858d5f132de0a51669d6e5 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:51:47 -0600 Subject: add fuzzyhighlight-caseinsensitive patch --- config.def.h | 2 + dmenu.1 | 20 +++ dmenu.c | 55 ++++++- .../dmenu-fuzzyhighlight-caseinsensitive-4.9.diff | 164 +++++++++++++++++++++ 4 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 patches/dmenu-fuzzyhighlight-caseinsensitive-4.9.diff diff --git a/config.def.h b/config.def.h index cfff3e4..9723027 100644 --- a/config.def.h +++ b/config.def.h @@ -13,6 +13,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 --git a/dmenu.1 b/dmenu.1 index bd3dd40..82dca41 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -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 ] .RB [ \-d @@ -82,6 +90,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 --git a/dmenu.c b/dmenu.c index 1153e6e..03327eb 100644 --- a/dmenu.c +++ b/dmenu.c @@ -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; @@ -142,9 +144,47 @@ 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(&text[i], highlight, 1)) { + c = highlight[1]; + highlight[1] = '\0'; + + /* get indentation */ + indent = TEXTW(item->text); + + /* highlight character */ + drw_text( + drw, + x + indent - lrpad, + 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) @@ -152,7 +192,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 @@ -754,6 +796,7 @@ usage(void) { die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" + " [-nhb color] [-nhf color] [-shb color] [-shf color]\n" " [-d separator] [-D separator]"); } @@ -796,6 +839,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 if (!strcmp(argv[i], "-bw")) diff --git a/patches/dmenu-fuzzyhighlight-caseinsensitive-4.9.diff b/patches/dmenu-fuzzyhighlight-caseinsensitive-4.9.diff new file mode 100644 index 0000000..026b3d2 --- /dev/null +++ b/patches/dmenu-fuzzyhighlight-caseinsensitive-4.9.diff @@ -0,0 +1,164 @@ +From 2de2780bef245ae44d677abd20c589160f0d984b Mon Sep 17 00:00:00 2001 +From: Oleh Kopeykin +Date: Sun, 5 Feb 2023 01:15:35 +0300 +Subject: [PATCH] Adds the fuzzyhighlight patch + +--- + config.def.h | 2 ++ + dmenu.1 | 20 ++++++++++++++++++ + dmenu.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 76 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 51612b9..0103bda 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -12,6 +12,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 --git a/dmenu.1 b/dmenu.1 +index 323f93c..472b179 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -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 --git a/dmenu.c b/dmenu.c +index 96ddc98..442d707 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -27,7 +27,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; +@@ -115,9 +117,47 @@ 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 (!fstrncmp(&text[i], highlight, 1)) { ++ c = highlight[1]; ++ highlight[1] = '\0'; ++ ++ /* get indentation */ ++ indent = TEXTW(item->text); ++ ++ /* highlight character */ ++ drw_text( ++ drw, ++ x + indent - lrpad, ++ 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) +@@ -125,7 +165,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 +@@ -770,7 +812,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); + } + +@@ -813,6 +856,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 +-- +2.39.1 + -- cgit v1.2.3 From 04da33709854688ca0f6cb1cff4fd0c5a34fd3be Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:57:34 -0600 Subject: add (and tweak) fuzzymatch patch --- config.def.h | 1 + config.mk | 2 +- dmenu.c | 89 +++++++++++++++++++++ patches/dmenu-fuzzymatch-4.9.diff | 163 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 patches/dmenu-fuzzymatch-4.9.diff diff --git a/config.def.h b/config.def.h index 9723027..b7bff67 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 */ 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 */ diff --git a/config.mk b/config.mk index 566348b..fd6ff05 100644 --- a/config.mk +++ b/config.mk @@ -21,7 +21,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 03327eb..9c505e3 100644 --- a/dmenu.c +++ b/dmenu.c @@ -1,6 +1,7 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include #include #include #include @@ -35,6 +36,7 @@ struct item { char *text_output; struct item *left, *right; int out; + double distance; }; static char text[BUFSIZ] = ""; @@ -281,9 +283,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; @@ -815,6 +902,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")) /* toggles fuzzy matching */ + fuzzy = fuzzy ? 0 : 1; else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ centered = 1; else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ 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 +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 + #include ++#include + #include + #include + #include +@@ -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 + -- cgit v1.2.3 From 2b5883bff1a7c952d0685098bb813adc3e48ef88 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 13:59:57 -0600 Subject: add lineheight patch --- config.def.h | 3 ++ dmenu.1 | 5 ++ dmenu.c | 11 ++-- patches/dmenu-lineheight-5.2.diff | 106 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 patches/dmenu-lineheight-5.2.diff diff --git a/config.def.h b/config.def.h index b7bff67..91bda10 100644 --- a/config.def.h +++ b/config.def.h @@ -20,6 +20,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 82dca41..c44cb08 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 @@ -65,6 +67,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 9c505e3..9152698 100644 --- a/dmenu.c +++ b/dmenu.c @@ -204,7 +204,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); @@ -221,7 +221,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) { @@ -784,6 +784,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; promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; @@ -884,7 +885,7 @@ usage(void) die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" " [-nhb color] [-nhf color] [-shb color] [-shf color]\n" - " [-d separator] [-D separator]"); + " [-d separator] [-D separator] [-h height]"); } int @@ -914,6 +915,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 */ diff --git a/patches/dmenu-lineheight-5.2.diff b/patches/dmenu-lineheight-5.2.diff new file mode 100644 index 0000000..a5e8468 --- /dev/null +++ b/patches/dmenu-lineheight-5.2.diff @@ -0,0 +1,106 @@ +From ba103e38ea4ab07f9a3ee90627714b9bea17c329 Mon Sep 17 00:00:00 2001 +From: pskry +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 e7be8af..82b204b 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -148,7 +148,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); +@@ -165,7 +165,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) { +@@ -630,6 +630,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 +@@ -710,7 +711,7 @@ setup(void) + static void + usage(void) + { +- die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" ++ die("usage: dmenu [-bfiv] [-l lines] [-h height] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); + } + +@@ -737,6 +738,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.38.1 + -- cgit v1.2.3 From 47cc6ebdd6e9f7d4a653b0f2812e656fd9d3eb9a Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 14:01:46 -0600 Subject: add mousesupport patch --- dmenu.c | 119 ++++++++++++++++++++++++++++- patches/dmenu-mousesupport-5.2.diff | 144 ++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 patches/dmenu-mousesupport-5.2.diff diff --git a/dmenu.c b/dmenu.c index 9152698..652fab2 100644 --- a/dmenu.c +++ b/dmenu.c @@ -669,6 +669,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) { @@ -736,6 +849,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); @@ -849,7 +965,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, border_width, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); diff --git a/patches/dmenu-mousesupport-5.2.diff b/patches/dmenu-mousesupport-5.2.diff new file mode 100644 index 0000000..eaacea4 --- /dev/null +++ b/patches/dmenu-mousesupport-5.2.diff @@ -0,0 +1,144 @@ +diff --git a/dmenu.c b/dmenu.c +index 7cf253b..d276a94 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -528,6 +528,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) + { +@@ -582,6 +695,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); +@@ -679,7 +795,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); -- cgit v1.2.3 From efffd811301f69ee1e91183c2356ccc8f5115c02 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 14:09:41 -0600 Subject: add password patch --- dmenu.1 | 5 +- dmenu.c | 21 ++++++-- patches/dmenu-password-5.0.diff | 103 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 patches/dmenu-password-5.0.diff diff --git a/dmenu.1 b/dmenu.1 index c44cb08..3e72fee 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 [ \-h @@ -64,6 +64,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 652fab2..f698d44 100644 --- a/dmenu.c +++ b/dmenu.c @@ -44,7 +44,7 @@ static char *embed; static char separator, separator_reverse; static char * (*sepchr)(const char *, int); 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; @@ -205,6 +205,7 @@ drawmenu(void) unsigned int curpos; struct item *item; 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); @@ -216,7 +217,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) { @@ -807,6 +813,11 @@ readstdin(void) size_t i, junk, size = 0; ssize_t len; + if(passwd) { + inputw = lines = 0; + return; + } + /* read each line from stdin and add it to the item list */ for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { if (i + 1 >= size / sizeof *items) @@ -999,7 +1010,7 @@ setup(void) static void usage(void) { - die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + die("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" " [-nhb color] [-nhf color] [-shb color] [-shf color]\n" " [-d separator] [-D separator] [-h height]"); @@ -1027,7 +1038,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 */ 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 +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 + -- cgit v1.2.3 From f5e21c71896573cccd82fdb1a29cfb9a76818a87 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 14:12:54 -0600 Subject: add rejectnomatch patch --- dmenu.1 | 5 ++- dmenu.c | 21 ++++++++- patches/dmenu-rejectnomatch-4.7.diff | 82 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 patches/dmenu-rejectnomatch-4.7.diff diff --git a/dmenu.1 b/dmenu.1 index 3e72fee..ec120c8 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -3,7 +3,7 @@ dmenu \- dynamic menu .SH SYNOPSIS .B dmenu -.RB [ \-bfivP ] +.RB [ \-bfirvP ] .RB [ \-l .IR lines ] .RB [ \-h @@ -67,6 +67,9 @@ dmenu matches menu items case insensitively. .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 diff --git a/dmenu.c b/dmenu.c index f698d44..442ca3d 100644 --- a/dmenu.c +++ b/dmenu.c @@ -46,6 +46,7 @@ static char * (*sepchr)(const char *, int); static int bh, mw, mh; 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; @@ -433,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 @@ -1010,7 +1025,7 @@ setup(void) static void usage(void) { - die("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + die("usage: dmenu [-bfirvP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" " [-nhb color] [-nhf color] [-shb color] [-shf color]\n" " [-d separator] [-D separator] [-h height]"); @@ -1038,8 +1053,10 @@ main(int argc, char *argv[]) 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 */ + } 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 */ 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 */ -- cgit v1.2.3 From c6e51f44ae152c57a07ab9309b0ee8ae3cf7de74 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 14:25:58 -0600 Subject: add xresources patch (and fix with fuzzyhighlight) --- dmenu.c | 108 ++++++++++++++++++++++++++++---- patches/dmenu-xresources-4.9.diff | 126 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 patches/dmenu-xresources-4.9.diff diff --git a/dmenu.c b/dmenu.c index 442ca3d..33c07e5 100644 --- a/dmenu.c +++ b/dmenu.c @@ -16,6 +16,7 @@ #include #endif #include +#include #include "drw.h" #include "util.h" @@ -61,6 +62,10 @@ static XIC xic; static Drw *drw; static Clr *scheme[SchemeLast]; +/* Temporary arrays to allow overriding xresources values */ +static char *colortemp[8]; +static char *tempfonts; + #include "config.h" static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; @@ -918,8 +923,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); @@ -1031,6 +1041,57 @@ usage(void) " [-d separator] [-D separator] [-h height]"); } +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.hibackground", "*", &type, &xval)) + colors[SchemeNormHighlight][ColBg] = strdup(xval.addr); + else + colors[SchemeNormHighlight][ColBg] = strdup(colors[SchemeSel][ColFg]); + if (XrmGetResource(xdb, "dmenu.hiforeground", "*", &type, &xval)) + colors[SchemeNormHighlight][ColFg] = strdup(xval.addr); + else + colors[SchemeNormHighlight][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][ColFg]); + if (XrmGetResource(xdb, "dmenu.selhiforeground", "*", &type, &xval)) + colors[SchemeSelHighlight][ColFg] = strdup(xval.addr); + else + colors[SchemeSelHighlight][ColFg] = strdup(colors[SchemeSel][ColFg]); + + XrmDestroyDatabase(xdb); + } +} + int main(int argc, char *argv[]) { @@ -1071,23 +1132,23 @@ 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], "-nhb")) /* normal hi background color */ - colors[SchemeNormHighlight][ColBg] = argv[++i]; + colortemp[4] = argv[++i]; else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ - colors[SchemeNormHighlight][ColFg] = argv[++i]; + colortemp[5] = argv[++i]; else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ - colors[SchemeSelHighlight][ColBg] = argv[++i]; + colortemp[6] = argv[++i]; else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ - colors[SchemeSelHighlight][ColFg] = argv[++i]; + colortemp[7] = argv[++i]; else if (!strcmp(argv[i], "-w")) /* embedding window id */ embed = argv[++i]; else if (!strcmp(argv[i], "-bw")) @@ -1113,8 +1174,31 @@ 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 ( colortemp[4]) + colors[SchemeNormHighlight][ColBg] = strdup(colortemp[4]); + if ( colortemp[5]) + colors[SchemeNormHighlight][ColFg] = strdup(colortemp[5]); + if ( colortemp[6]) + colors[SchemeSelHighlight][ColBg] = strdup(colortemp[6]); + if ( colortemp[7]) + colors[SchemeSelHighlight][ColFg] = strdup(colortemp[7]); + + 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-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 + #endif + #include ++#include + + #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__ -- cgit v1.2.3 From 96e4a549ce8b3da7d5d339a94487bb2849520ae7 Mon Sep 17 00:00:00 2001 From: zachir Date: Tue, 21 Feb 2023 14:29:56 -0600 Subject: set my defaults --- config.def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 91bda10..47c1e3e 100644 --- a/config.def.h +++ b/config.def.h @@ -2,12 +2,12 @@ /* 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 */ +static int fuzzy = 0; /* -F option; if 0, dmenu doesn't use fuzzy matching */ 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" + "mononoki Nerd Font Mono:size=12" }; static const char *prompt = NULL; /* -p option; prompt to the left of input field */ static const char *colors[SchemeLast][2] = { @@ -21,7 +21,7 @@ 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 lineheight = 24; static unsigned int min_lineheight = 8; /* -- cgit v1.2.3