aboutsummaryrefslogtreecommitdiffstats
path: root/patches
diff options
context:
space:
mode:
Diffstat (limited to 'patches')
-rw-r--r--patches/README.md2
-rw-r--r--patches/dmenu-alpha-20251118-8b48986.diff276
-rw-r--r--patches/dmenu-border-20230512-0fe460d.diff36
-rw-r--r--patches/dmenu-center-20250407-b1e217b.diff135
-rw-r--r--patches/dmenu-grid-4.9.diff107
-rw-r--r--patches/dmenu-gridnav-5.2.diff69
-rw-r--r--patches/dmenu-initialtext-4.7.diff49
-rw-r--r--patches/dmenu-noinput-5.2.diff147
-rw-r--r--patches/dmenu-password-5.0.diff103
-rw-r--r--patches/dmenu-vi_mode-20230416-0fe460d.diff300
-rw-r--r--patches/dmenu-xresources-alt-5.0.diff182
11 files changed, 1406 insertions, 0 deletions
diff --git a/patches/README.md b/patches/README.md
new file mode 100644
index 0000000..f25581b
--- /dev/null
+++ b/patches/README.md
@@ -0,0 +1,2 @@
+# eigen
+- vi-mode integration für gridnav
diff --git a/patches/dmenu-alpha-20251118-8b48986.diff b/patches/dmenu-alpha-20251118-8b48986.diff
new file mode 100644
index 0000000..8b06058
--- /dev/null
+++ b/patches/dmenu-alpha-20251118-8b48986.diff
@@ -0,0 +1,276 @@
+diff --git a/config.def.h b/config.def.h
+index 1edb647..a2cb342 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 const unsigned int alpha = 0xee; /* Amount of opacity. 0xff is opaque */
+ /* -fn option overrides fonts[0]; default X11 font or font set */
+ static const char *fonts[] = {
+ "monospace:size=10"
+@@ -13,6 +14,12 @@ static const char *colors[SchemeLast][2] = {
+ [SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeOut] = { "#000000", "#00ffff" },
+ };
++
++static const unsigned int alphas[SchemeLast][2] = {
++ [SchemeNorm] = { OPAQUE, alpha },
++ [SchemeSel] = { OPAQUE, alpha },
++ [SchemeOut] = { OPAQUE, alpha },
++};
+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+ static unsigned int lines = 0;
+
+diff --git a/config.mk b/config.mk
+index dcc5bb3..2166055 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) -lXrender
+
+ # 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 9577b37..22e4a4b 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -10,10 +10,12 @@
+
+ #include <X11/Xlib.h>
+ #include <X11/Xatom.h>
++#include <X11/Xproto.h>
+ #include <X11/Xutil.h>
+ #ifdef XINERAMA
+ #include <X11/extensions/Xinerama.h>
+ #endif
++#include <X11/extensions/Xrender.h>
+ #include <X11/Xft/Xft.h>
+
+ #include "drw.h"
+@@ -24,6 +26,8 @@
+ * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
++#define OPAQUE 0xffu
++
+ /* enums */
+ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+
+@@ -52,10 +56,16 @@ static XIC xic;
+ static Drw *drw;
+ static Clr *scheme[SchemeLast];
+
++static int useargb = 0;
++static Visual *visual;
++static int depth;
++static Colormap cmap;
++
+ #include "config.h"
+
+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+ static char *(*fstrstr)(const char *, const char *) = strstr;
++static void xinitvisual(void);
+
+ static unsigned int
+ textw_clamp(const char *str, unsigned int n)
+@@ -627,7 +637,7 @@ setup(void)
+ #endif
+ /* init appearance */
+ for (j = 0; j < SchemeLast; j++)
+- scheme[j] = drw_scm_create(drw, colors[j], 2);
++ scheme[j] = drw_scm_create(drw, colors[j], 2, alphas[j]);
+
+ clip = XInternAtom(dpy, "CLIPBOARD", False);
+ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
+@@ -682,11 +692,12 @@ setup(void)
+
+ /* create menu window */
+ swa.override_redirect = True;
+- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ swa.border_pixel = 0;
++ swa.colormap = cmap;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
+- CopyFromParent, CopyFromParent, CopyFromParent,
+- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
++ depth, CopyFromParent, visual,
++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
+ XSetClassHint(dpy, win, &ch);
+
+ /* input methods */
+@@ -771,7 +782,8 @@ main(int argc, char *argv[])
+ if (!XGetWindowAttributes(dpy, parentwin, &wa))
+ die("could not get embedding window attributes: 0x%lx",
+ parentwin);
+- drw = drw_create(dpy, screen, root, wa.width, wa.height);
++ xinitvisual();
++ drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+@@ -793,3 +805,40 @@ main(int argc, char *argv[])
+
+ return 1; /* unreachable */
+ }
++
++void
++xinitvisual(void)
++{
++ XVisualInfo *infos;
++ XRenderPictFormat *fmt;
++ int nitems;
++ int i;
++
++ XVisualInfo tpl = {
++ .screen = screen,
++ .depth = 32,
++ .class = TrueColor
++ };
++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
++
++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
++ visual = NULL;
++ for(i = 0; i < nitems; i ++) {
++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
++ visual = infos[i].visual;
++ depth = infos[i].depth;
++ cmap = XCreateColormap(dpy, root, visual, AllocNone);
++ useargb = 1;
++ break;
++ }
++ }
++
++ XFree(infos);
++
++ if (!visual) {
++ visual = DefaultVisual(dpy, screen);
++ depth = DefaultDepth(dpy, screen);
++ cmap = DefaultColormap(dpy, screen);
++ }
++}
+diff --git a/drw.c b/drw.c
+index 9fdd1a4..a4a01e8 100644
+--- a/drw.c
++++ b/drw.c
+@@ -47,7 +47,7 @@ utf8decode(const char *s_in, long *u, int *err)
+ }
+
+ Drw *
+-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
+ {
+ Drw *drw = ecalloc(1, sizeof(Drw));
+
+@@ -56,8 +56,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
+ drw->root = root;
+ drw->w = w;
+ drw->h = h;
+- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+- drw->gc = XCreateGC(dpy, root, 0, NULL);
++ drw->visual = visual;
++ drw->depth = depth;
++ drw->cmap = cmap;
++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+ return drw;
+@@ -73,7 +76,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
+ drw->h = h;
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
+ }
+
+ void
+@@ -167,20 +170,21 @@ drw_fontset_free(Fnt *font)
+ }
+
+ void
+-drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
+ {
+ if (!drw || !dest || !clrname)
+ return;
+
+- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+- DefaultColormap(drw->dpy, drw->screen),
++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
++
++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
+ }
+
+ /* Create color schemes. */
+ Clr *
+-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
++drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount, const unsigned int alphas[])
+ {
+ size_t i;
+ Clr *ret;
+@@ -190,7 +194,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+ return NULL;
+
+ for (i = 0; i < clrcount; i++)
+- drw_clr_create(drw, &ret[i], clrnames[i]);
++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
+ return ret;
+ }
+
+@@ -271,11 +275,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
+ } else {
+ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
+ if (w < lpad)
+ return x + w;
+- d = XftDrawCreate(drw->dpy, drw->drawable,
+- DefaultVisual(drw->dpy, drw->screen),
+- DefaultColormap(drw->dpy, drw->screen));
+ x += lpad;
+ w -= lpad;
+ }
+diff --git a/drw.h b/drw.h
+index 2ed77be..5a0d7d7 100644
+--- a/drw.h
++++ b/drw.h
+@@ -20,6 +20,9 @@ typedef struct {
+ Display *dpy;
+ int screen;
+ Window root;
++ Visual *visual;
++ unsigned int depth;
++ Colormap cmap;
+ Drawable drawable;
+ GC gc;
+ Clr *scheme;
+@@ -27,7 +30,7 @@ typedef struct {
+ } Drw;
+
+ /* Drawable abstraction */
+-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap);
+ void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+ void drw_free(Drw *drw);
+
+@@ -39,9 +42,9 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
+ void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+
+ /* Colorscheme abstraction */
+-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
+ void drw_clr_free(Drw *drw, Clr *c);
+-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
++Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount, const unsigned int alphas[]);
+ void drw_scm_free(Drw *drw, Clr *scm, size_t clrcount);
+
+ /* Cursor abstraction */
diff --git a/patches/dmenu-border-20230512-0fe460d.diff b/patches/dmenu-border-20230512-0fe460d.diff
new file mode 100644
index 0000000..f7a5971
--- /dev/null
+++ b/patches/dmenu-border-20230512-0fe460d.diff
@@ -0,0 +1,36 @@
+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 27b7a30..7c130fc 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -684,9 +684,11 @@ setup(void)
+ swa.override_redirect = True;
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+- win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
++ win = XCreateWindow(dpy, root, 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);
+
+
+@@ -757,6 +759,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();
diff --git a/patches/dmenu-center-20250407-b1e217b.diff b/patches/dmenu-center-20250407-b1e217b.diff
new file mode 100644
index 0000000..a28efdb
--- /dev/null
+++ b/patches/dmenu-center-20250407-b1e217b.diff
@@ -0,0 +1,135 @@
+From 95a444534c230de79000348b0e12f8644aac8b15 Mon Sep 17 00:00:00 2001
+From: leliel <mail.leliel@proton.me>
+Date: Mon, 7 Apr 2025 01:00:01 +0000
+Subject: [PATCH] Increased speed for long files with emojis.
+
+---
+ config.def.h | 3 +++
+ dmenu.1 | 3 +++
+ dmenu.c | 40 ++++++++++++++++++++++++++++++++++------
+ 3 files changed, 40 insertions(+), 6 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 1edb647..832896f 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,9 @@
+ /* Default settings; can be overriden by command line. */
+
+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
++static int centered = 1; /* -c option; centers dmenu on screen */
++static int min_width = 500; /* minimum width when centered */
++static const float menu_height_ratio = 4.0f; /* This is the ratio used in the original calculation */
+ /* -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 fd49549..ceb52c7 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -29,6 +29,7 @@ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+
+ struct item {
+ char *text;
++ unsigned int width;
+ struct item *left, *right;
+ int out;
+ };
+@@ -95,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(item->width, len);
++ return len;
++}
++
+ static void
+ cleanup(void)
+ {
+@@ -563,6 +573,7 @@ readstdin(void)
+ line[len - 1] = '\0';
+ if (!(items[i].text = strdup(line)))
+ die("strdup:");
++ items[i].width = TEXTW(line);
+
+ items[i].out = 0;
+ }
+@@ -636,6 +647,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 +674,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) / menu_height_ratio);
++ } 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 +691,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 +759,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.49.0
+
diff --git a/patches/dmenu-grid-4.9.diff b/patches/dmenu-grid-4.9.diff
new file mode 100644
index 0000000..c27689b
--- /dev/null
+++ b/patches/dmenu-grid-4.9.diff
@@ -0,0 +1,107 @@
+From 39ab9676914bd0d8105d0f96bbd7611a53077438 Mon Sep 17 00:00:00 2001
+From: Miles Alan <m@milesalan.com>
+Date: Sat, 4 Jul 2020 11:19:04 -0500
+Subject: [PATCH] Add -g option to display entries in the given number of grid
+ columns
+
+This option can be used in conjunction with -l to format dmenu's options in
+arbitrary size grids. For example, to create a 4 column by 6 line grid, you
+could use: dmenu -g 4 -l 6
+---
+ config.def.h | 3 ++-
+ dmenu.1 | 7 ++++++-
+ dmenu.c | 22 ++++++++++++++++------
+ 3 files changed, 24 insertions(+), 8 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 1edb647..96cf3c9 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -13,8 +13,9 @@ static const char *colors[SchemeLast][2] = {
+ [SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeOut] = { "#000000", "#00ffff" },
+ };
+-/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
++/* -l and -g options; controls number of lines and columns in grid if > 0 */
+ static unsigned int lines = 0;
++static unsigned int columns = 0;
+
+ /*
+ * Characters not considered part of a word while deleting words
+diff --git a/dmenu.1 b/dmenu.1
+index 323f93c..d0a734a 100644
+--- a/dmenu.1
++++ b/dmenu.1
+@@ -4,6 +4,8 @@ dmenu \- dynamic menu
+ .SH SYNOPSIS
+ .B dmenu
+ .RB [ \-bfiv ]
++.RB [ \-g
++.IR columns ]
+ .RB [ \-l
+ .IR lines ]
+ .RB [ \-m
+@@ -47,8 +49,11 @@ is faster, but will lock up X until stdin reaches end\-of\-file.
+ .B \-i
+ dmenu matches menu items case insensitively.
+ .TP
++.BI \-g " columns"
++dmenu lists items in a grid with the given number of columns.
++.TP
+ .BI \-l " lines"
+-dmenu lists items vertically, with the given number of lines.
++dmenu lists items in a grid with the given number of lines.
+ .TP
+ .BI \-m " monitor"
+ dmenu is displayed on the monitor number supplied. Monitor numbers are starting
+diff --git a/dmenu.c b/dmenu.c
+index 6b8f51b..d79b6bb 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -77,7 +77,7 @@ calcoffsets(void)
+ int i, n;
+
+ if (lines > 0)
+- n = lines * bh;
++ n = lines * columns * bh;
+ else
+ n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
+ /* calculate which items will begin the next page and previous page */
+@@ -152,9 +152,15 @@ drawmenu(void)
+ }
+
+ if (lines > 0) {
+- /* draw vertical list */
+- for (item = curr; item != next; item = item->right)
+- drawitem(item, x, y += bh, mw - x);
++ /* draw grid */
++ int i = 0;
++ for (item = curr; item != next; item = item->right, i++)
++ drawitem(
++ item,
++ x + ((i / lines) * ((mw - x) / columns)),
++ y + (((i % lines) + 1) * bh),
++ (mw - x) / columns
++ );
+ } else if (matches) {
+ /* draw horizontal list */
+ x += inputw;
+@@ -708,9 +714,13 @@ main(int argc, char *argv[])
+ } else if (i + 1 == argc)
+ usage();
+ /* these options take one argument */
+- else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
++ else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */
++ columns = atoi(argv[++i]);
++ if (lines == 0) lines = 1;
++ } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */
+ lines = atoi(argv[++i]);
+- else if (!strcmp(argv[i], "-m"))
++ if (columns == 0) columns = 1;
++ } 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];
+--
+2.23.1
+
diff --git a/patches/dmenu-gridnav-5.2.diff b/patches/dmenu-gridnav-5.2.diff
new file mode 100644
index 0000000..7adc36f
--- /dev/null
+++ b/patches/dmenu-gridnav-5.2.diff
@@ -0,0 +1,69 @@
+diff --git a/dmenu.c b/dmenu.c
+index 027dddb..0b92f1f 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -334,6 +334,8 @@ keypress(XKeyEvent *ev)
+ int len;
+ KeySym ksym = NoSymbol;
+ Status status;
++ int i, offscreen = 0;
++ struct item *tmpsel;
+
+ len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
+ switch (status) {
+@@ -465,6 +467,27 @@ insert:
+ calcoffsets();
+ break;
+ case XK_Left:
++ if (columns > 1) {
++ if (!sel)
++ return;
++ tmpsel = sel;
++ for (i = 0; i < lines; i++) {
++ if (!tmpsel->left || tmpsel->left->right != tmpsel) {
++ if (offscreen)
++ break;
++ return;
++ }
++ if (tmpsel == curr)
++ offscreen = 1;
++ tmpsel = tmpsel->left;
++ }
++ sel = tmpsel;
++ if (offscreen) {
++ curr = prev;
++ calcoffsets();
++ }
++ break;
++ }
+ case XK_KP_Left:
+ if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
+ cursor = nextrune(-1);
+@@ -505,6 +528,27 @@ insert:
+ sel->out = 1;
+ break;
+ case XK_Right:
++ if (columns > 1) {
++ if (!sel)
++ return;
++ tmpsel = sel;
++ for (i = 0; i < lines; i++) {
++ if (!tmpsel->right || tmpsel->right->left != tmpsel) {
++ if (offscreen)
++ break;
++ return;
++ }
++ tmpsel = tmpsel->right;
++ if (tmpsel == next)
++ offscreen = 1;
++ }
++ sel = tmpsel;
++ if (offscreen) {
++ curr = next;
++ calcoffsets();
++ }
++ break;
++ }
+ case XK_KP_Right:
+ if (text[cursor] != '\0') {
+ cursor = nextrune(+1);
diff --git a/patches/dmenu-initialtext-4.7.diff b/patches/dmenu-initialtext-4.7.diff
new file mode 100644
index 0000000..3d84708
--- /dev/null
+++ b/patches/dmenu-initialtext-4.7.diff
@@ -0,0 +1,49 @@
+diff --git a/dmenu.1 b/dmenu.1
+index 9eab758..b4947f9 100644
+--- a/dmenu.1
++++ b/dmenu.1
+@@ -22,6 +22,8 @@ dmenu \- dynamic menu
+ .IR color ]
+ .RB [ \-w
+ .IR windowid ]
++.RB [ \-it
++.IR text ]
+ .P
+ .BR dmenu_run " ..."
+ .SH DESCRIPTION
+@@ -80,6 +82,9 @@ prints version information to stdout, then exits.
+ .TP
+ .BI \-w " windowid"
+ embed into windowid.
++.TP
++.BI \-it " text".
++start with initial text input.
+ .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 d605ab4..0564dfe 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -637,7 +637,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"
++ " [-it text]\n", stderr);
+ exit(1);
+ }
+
+@@ -680,7 +681,10 @@ main(int argc, char *argv[])
+ colors[SchemeSel][ColFg] = argv[++i];
+ else if (!strcmp(argv[i], "-w")) /* embedding window id */
+ embed = argv[++i];
+- else
++ else if (!strcmp(argv[i], "-it")) { /* embedding window id */
++ const char * text = argv[++i];
++ insert(text, strlen(text));
++ } else
+ usage();
+
+ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
diff --git a/patches/dmenu-noinput-5.2.diff b/patches/dmenu-noinput-5.2.diff
new file mode 100644
index 0000000..139ec3a
--- /dev/null
+++ b/patches/dmenu-noinput-5.2.diff
@@ -0,0 +1,147 @@
+diff -up dmenu/config.def.h dmenu-noinput/config.def.h
+--- dmenu/config.def.h 2024-03-03 12:59:00.545293913 -0600
++++ dmenu-noinput/config.def.h 2024-03-03 10:00:59.974900500 -0600
+@@ -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 draw_input = 1; /* -noi option; if 0, the input will not be drawn by default */
+ /* -fn option overrides fonts[0]; default X11 font or font set */
+ static const char *fonts[] = {
+ "monospace:size=10"
+diff -up dmenu/dmenu.c dmenu-noinput/dmenu.c
+--- dmenu/dmenu.c 2024-03-03 12:59:00.545293913 -0600
++++ dmenu-noinput/dmenu.c 2024-03-03 12:15:44.416277776 -0600
+@@ -147,7 +147,7 @@ drawmenu(void)
+ {
+ unsigned int curpos;
+ struct item *item;
+- int x = 0, y = 0, w;
++ int x = 0, y = 0, w = 0;
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, 0, 0, mw, mh, 1, 1);
+@@ -156,15 +156,17 @@ drawmenu(void)
+ drw_setscheme(drw, scheme[SchemeSel]);
+ x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
+ }
+- /* 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);
+
+- curpos = TEXTW(text) - TEXTW(&text[cursor]);
+- if ((curpos += lrpad / 2 - 1) < w) {
++ if (draw_input) {
++ w = (lines > 0 || !matches) ? mw - x : inputw;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 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, 2, bh - 4, 1, 0);
++ }
+ }
+
+ if (lines > 0) {
+@@ -178,8 +180,8 @@ drawmenu(void)
+ if (curr->left) {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
++ x += w;
+ }
+- x += w;
+ for (item = curr; item != next; item = item->right)
+ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
+ if (next) {
+@@ -358,16 +360,19 @@ keypress(XKeyEvent *ev)
+ case XK_p: ksym = XK_Up; break;
+
+ case XK_k: /* delete right */
+- text[cursor] = '\0';
+- match();
++ if (draw_input) {
++ text[cursor] = '\0';
++ match();
++ }
+ break;
+ case XK_u: /* delete left */
+- insert(NULL, 0 - cursor);
++ if (draw_input)
++ insert(NULL, 0 - cursor);
+ break;
+ case XK_w: /* delete word */
+- while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
++ while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]) && draw_input)
+ insert(NULL, nextrune(-1) - cursor);
+- while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
++ while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]) && draw_input)
+ insert(NULL, nextrune(-1) - cursor);
+ break;
+ case XK_y: /* paste selection */
+@@ -414,23 +419,23 @@ keypress(XKeyEvent *ev)
+ switch(ksym) {
+ default:
+ insert:
+- if (!iscntrl((unsigned char)*buf))
++ if (!iscntrl((unsigned char)*buf) && draw_input)
+ insert(buf, len);
+ break;
+ case XK_Delete:
+ case XK_KP_Delete:
+- if (text[cursor] == '\0')
++ if (text[cursor] == '\0' || !draw_input)
+ return;
+ cursor = nextrune(+1);
+ /* fallthrough */
+ case XK_BackSpace:
+- if (cursor == 0)
++ if (cursor == 0 || !draw_input)
+ return;
+ insert(NULL, nextrune(-1) - cursor);
+ break;
+ case XK_End:
+ case XK_KP_End:
+- if (text[cursor] != '\0') {
++ if (text[cursor] != '\0' && draw_input) {
+ cursor = strlen(text);
+ break;
+ }
+@@ -514,7 +519,7 @@ insert:
+ }
+ break;
+ case XK_Tab:
+- if (!sel)
++ if (!sel || !draw_input)
+ return;
+ cursor = strnlen(sel->text, sizeof text - 1);
+ memcpy(text, sel->text, cursor);
+@@ -677,7 +682,7 @@ setup(void)
+ mw = wa.width;
+ }
+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
+- inputw = mw / 3; /* input width: ~33% of monitor width */
++ inputw = !draw_input ? 0 : mw / 3; /* input width: ~33% of monitor width */
+ match();
+
+ /* create menu window */
+@@ -715,7 +720,7 @@ setup(void)
+ static void
+ usage(void)
+ {
+- die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
++ die("usage: dmenu [-bfiv] [-noi] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
+ }
+
+@@ -734,6 +739,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], "-noi")) /* no input field. intended to be used with a prompt */
++ draw_input = 0;
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
diff --git a/patches/dmenu-password-5.0.diff b/patches/dmenu-password-5.0.diff
new file mode 100644
index 0000000..7a187b9
--- /dev/null
+++ b/patches/dmenu-password-5.0.diff
@@ -0,0 +1,103 @@
+From c4de1032bd4c247bc20b6ab92a10a8d778966679 Mon Sep 17 00:00:00 2001
+From: Mehrad Mahmoudian <m.mahmoudian@gmail.com>
+Date: Tue, 4 May 2021 12:05:09 +0300
+Subject: [PATCH] patched with password patch
+
+---
+ dmenu.1 | 5 ++++-
+ dmenu.c | 21 +++++++++++++++++----
+ 2 files changed, 21 insertions(+), 5 deletions(-)
+
+diff --git a/dmenu.1 b/dmenu.1
+index 323f93c..762f707 100644
+--- a/dmenu.1
++++ b/dmenu.1
+@@ -3,7 +3,7 @@
+ dmenu \- dynamic menu
+ .SH SYNOPSIS
+ .B dmenu
+-.RB [ \-bfiv ]
++.RB [ \-bfivP ]
+ .RB [ \-l
+ .IR lines ]
+ .RB [ \-m
+@@ -47,6 +47,9 @@ is faster, but will lock up X until stdin reaches end\-of\-file.
+ .B \-i
+ dmenu matches menu items case insensitively.
+ .TP
++.B \-P
++dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored.
++.TP
+ .BI \-l " lines"
+ dmenu lists items vertically, with the given number of lines.
+ .TP
+diff --git a/dmenu.c b/dmenu.c
+index 65f25ce..ad8f63b 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -37,7 +37,7 @@ struct item {
+ static char text[BUFSIZ] = "";
+ static char *embed;
+ static int bh, mw, mh;
+-static int inputw = 0, promptw;
++static int inputw = 0, promptw, passwd = 0;
+ static int lrpad; /* sum of left and right padding */
+ static size_t cursor;
+ static struct item *items = NULL;
+@@ -132,6 +132,7 @@ drawmenu(void)
+ unsigned int curpos;
+ struct item *item;
+ int x = 0, y = 0, w;
++ char *censort;
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, 0, 0, mw, mh, 1, 1);
+@@ -143,7 +144,12 @@ drawmenu(void)
+ /* draw input field */
+ w = (lines > 0 || !matches) ? mw - x : inputw;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
++ if (passwd) {
++ censort = ecalloc(1, sizeof(text));
++ memset(censort, '.', strlen(text));
++ drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0);
++ free(censort);
++ } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+
+ curpos = TEXTW(text) - TEXTW(&text[cursor]);
+ if ((curpos += lrpad / 2 - 1) < w) {
+@@ -524,6 +530,11 @@ readstdin(void)
+ char buf[sizeof text], *p;
+ size_t i, imax = 0, size = 0;
+ unsigned int tmpmax = 0;
++ if(passwd){
++ inputw = lines = 0;
++ return;
++ }
++
+
+ /* read each line from stdin and add it to the item list */
+ for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
+@@ -689,7 +700,7 @@ setup(void)
+ static void
+ usage(void)
+ {
+- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
++ fputs("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
+ exit(1);
+ }
+@@ -712,7 +723,9 @@ main(int argc, char *argv[])
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
+- } else if (i + 1 == argc)
++ } else if (!strcmp(argv[i], "-P")) /* is the input a password */
++ passwd = 1;
++ else if (i + 1 == argc)
+ usage();
+ /* these options take one argument */
+ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
+--
+2.31.1
+
diff --git a/patches/dmenu-vi_mode-20230416-0fe460d.diff b/patches/dmenu-vi_mode-20230416-0fe460d.diff
new file mode 100644
index 0000000..514b684
--- /dev/null
+++ b/patches/dmenu-vi_mode-20230416-0fe460d.diff
@@ -0,0 +1,300 @@
+diff --git a/config.def.h b/config.def.h
+index 1edb647..7bf5f4a 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -12,6 +12,7 @@ static const char *colors[SchemeLast][2] = {
+ [SchemeNorm] = { "#bbbbbb", "#222222" },
+ [SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeOut] = { "#000000", "#00ffff" },
++ [SchemeCursor] = { "#222222", "#bbbbbb"},
+ };
+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+ static unsigned int lines = 0;
+@@ -21,3 +22,15 @@ static unsigned int lines = 0;
+ * for example: " /?\"&[]"
+ */
+ static const char worddelimiters[] = " ";
++
++/*
++ * -vi option; if nonzero, vi mode is always enabled and can be
++ * accessed with the global_esc keysym + mod mask
++ */
++static unsigned int vi_mode = 1;
++static unsigned int start_mode = 0; /* mode to use when -vi is passed. 0 = insert mode, 1 = normal mode */
++static Key global_esc = { XK_n, Mod1Mask }; /* escape key when vi mode is not enabled explicitly */
++static Key quit_keys[] = {
++ /* keysym modifier */
++ { XK_q, 0 }
++};
+diff --git a/dmenu.c b/dmenu.c
+index 62f1089..8066271 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -26,7 +26,7 @@
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+ /* enums */
+-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
++enum { SchemeNorm, SchemeSel, SchemeOut, SchemeCursor, SchemeLast }; /* color schemes */
+
+ struct item {
+ char *text;
+@@ -34,6 +34,11 @@ struct item {
+ int out;
+ };
+
++typedef struct {
++ KeySym ksym;
++ unsigned int state;
++} Key;
++
+ static char text[BUFSIZ] = "";
+ static char *embed;
+ static int bh, mw, mh;
+@@ -44,6 +49,7 @@ static struct item *items = NULL;
+ static struct item *matches, *matchend;
+ static struct item *prev, *curr, *next, *sel;
+ static int mon = -1, screen;
++static unsigned int using_vi_mode = 0;
+
+ static Atom clip, utf8;
+ static Display *dpy;
+@@ -163,7 +169,15 @@ drawmenu(void)
+ drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+
+ curpos = TEXTW(text) - TEXTW(&text[cursor]);
+- if ((curpos += lrpad / 2 - 1) < w) {
++ curpos += lrpad / 2 - 1;
++ if (using_vi_mode && text[0] != '\0') {
++ drw_setscheme(drw, scheme[SchemeCursor]);
++ char vi_char[] = {text[cursor], '\0'};
++ drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0);
++ } else if (using_vi_mode) {
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0);
++ } else if (curpos < w) {
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
+ }
+@@ -321,6 +335,181 @@ movewordedge(int dir)
+ }
+ }
+
++static void
++vi_keypress(KeySym ksym, const XKeyEvent *ev)
++{
++ static const size_t quit_len = LENGTH(quit_keys);
++ if (ev->state & ControlMask) {
++ switch(ksym) {
++ /* movement */
++ case XK_d: /* fallthrough */
++ if (next) {
++ sel = curr = next;
++ calcoffsets();
++ goto draw;
++ } else
++ ksym = XK_G;
++ break;
++ case XK_u:
++ if (prev) {
++ sel = curr = prev;
++ calcoffsets();
++ goto draw;
++ } else
++ ksym = XK_g;
++ break;
++ case XK_p: /* fallthrough */
++ case XK_P: break;
++ case XK_c:
++ cleanup();
++ exit(1);
++ case XK_Return: /* fallthrough */
++ case XK_KP_Enter: break;
++ default: return;
++ }
++ }
++
++ switch(ksym) {
++ /* movement */
++ case XK_0:
++ cursor = 0;
++ break;
++ case XK_dollar:
++ if (text[cursor + 1] != '\0') {
++ cursor = strlen(text) - 1;
++ break;
++ }
++ break;
++ case XK_b:
++ movewordedge(-1);
++ break;
++ case XK_e:
++ cursor = nextrune(+1);
++ movewordedge(+1);
++ if (text[cursor] == '\0')
++ --cursor;
++ else
++ cursor = nextrune(-1);
++ break;
++ case XK_g:
++ if (sel == matches) {
++ break;
++ }
++ sel = curr = matches;
++ calcoffsets();
++ break;
++ case XK_G:
++ if (next) {
++ /* jump to end of list and position items in reverse */
++ curr = matchend;
++ calcoffsets();
++ curr = prev;
++ calcoffsets();
++ while (next && (curr = curr->right))
++ calcoffsets();
++ }
++ sel = matchend;
++ break;
++ case XK_h:
++ if (cursor)
++ cursor = nextrune(-1);
++ break;
++ case XK_j:
++ if (sel && sel->right && (sel = sel->right) == next) {
++ curr = next;
++ calcoffsets();
++ }
++ break;
++ case XK_k:
++ if (sel && sel->left && (sel = sel->left)->right == curr) {
++ curr = prev;
++ calcoffsets();
++ }
++ break;
++ case XK_l:
++ if (text[cursor] != '\0' && text[cursor + 1] != '\0')
++ cursor = nextrune(+1);
++ else if (text[cursor] == '\0' && cursor)
++ --cursor;
++ break;
++ case XK_w:
++ movewordedge(+1);
++ if (text[cursor] != '\0' && text[cursor + 1] != '\0')
++ cursor = nextrune(+1);
++ else if (cursor)
++ --cursor;
++ break;
++ /* insertion */
++ case XK_a:
++ cursor = nextrune(+1);
++ /* fallthrough */
++ case XK_i:
++ using_vi_mode = 0;
++ break;
++ case XK_A:
++ if (text[cursor] != '\0')
++ cursor = strlen(text);
++ using_vi_mode = 0;
++ break;
++ case XK_I:
++ cursor = using_vi_mode = 0;
++ break;
++ case XK_p:
++ if (text[cursor] != '\0')
++ cursor = nextrune(+1);
++ XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
++ utf8, utf8, win, CurrentTime);
++ return;
++ case XK_P:
++ XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
++ utf8, utf8, win, CurrentTime);
++ return;
++ /* deletion */
++ case XK_D:
++ text[cursor] = '\0';
++ if (cursor)
++ cursor = nextrune(-1);
++ match();
++ break;
++ case XK_x:
++ cursor = nextrune(+1);
++ insert(NULL, nextrune(-1) - cursor);
++ if (text[cursor] == '\0' && text[0] != '\0')
++ --cursor;
++ match();
++ break;
++ /* misc. */
++ case XK_Return:
++ case XK_KP_Enter:
++ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
++ if (!(ev->state & ControlMask)) {
++ cleanup();
++ exit(0);
++ }
++ if (sel)
++ sel->out = 1;
++ break;
++ case XK_Tab:
++ if (!sel)
++ return;
++ strncpy(text, sel->text, sizeof text - 1);
++ text[sizeof text - 1] = '\0';
++ cursor = strlen(text) - 1;
++ match();
++ break;
++ default:
++ for (size_t i = 0; i < quit_len; ++i)
++ if (quit_keys[i].ksym == ksym &&
++ (quit_keys[i].state & ev->state) == quit_keys[i].state) {
++ cleanup();
++ exit(1);
++ }
++ }
++
++draw:
++ drawmenu();
++}
++
+ static void
+ keypress(XKeyEvent *ev)
+ {
+@@ -340,6 +529,18 @@ keypress(XKeyEvent *ev)
+ break;
+ }
+
++ if (using_vi_mode) {
++ vi_keypress(ksym, ev);
++ return;
++ } else if (vi_mode &&
++ (ksym == global_esc.ksym &&
++ (ev->state & global_esc.state) == global_esc.state)) {
++ using_vi_mode = 1;
++ if (cursor)
++ cursor = nextrune(-1);
++ goto draw;
++ }
++
+ if (ev->state & ControlMask) {
+ switch(ksym) {
+ case XK_a: ksym = XK_Home; break;
+@@ -543,6 +744,8 @@ paste(void)
+ insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
+ XFree(p);
+ }
++ if (using_vi_mode && text[cursor] == '\0')
++ --cursor;
+ drawmenu();
+ }
+
+@@ -738,6 +941,11 @@ main(int argc, char *argv[])
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
++ } else if (!strcmp(argv[i], "-vi")) {
++ vi_mode = 1;
++ using_vi_mode = start_mode;
++ global_esc.ksym = XK_Escape;
++ global_esc.state = 0;
+ } else if (i + 1 == argc)
+ usage();
+ /* these options take one argument */
diff --git a/patches/dmenu-xresources-alt-5.0.diff b/patches/dmenu-xresources-alt-5.0.diff
new file mode 100644
index 0000000..f7f052f
--- /dev/null
+++ b/patches/dmenu-xresources-alt-5.0.diff
@@ -0,0 +1,182 @@
+diff -rupN orig/config.def.h patched/config.def.h
+--- orig/config.def.h 2021-04-16 06:30:47.713924755 +0300
++++ patched/config.def.h 2021-04-16 06:34:14.956933252 +0300
+@@ -2,16 +2,25 @@
+ /* Default settings; can be overriden by command line. */
+
+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
++
+ /* -fn option overrides fonts[0]; default X11 font or font set */
++static char font[] = "monospace:size=10";
+ static const char *fonts[] = {
+- "monospace:size=10"
++ font,
++ "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 *prompt = NULL; /* -p option; prompt to the left of input field */
++
++static char normfgcolor[] = "#bbbbbb";
++static char normbgcolor[] = "#222222";
++static char selfgcolor[] = "#eeeeee";
++static char selbgcolor[] = "#005577";
++static char *colors[SchemeLast][2] = {
+ /* fg bg */
+- [SchemeNorm] = { "#bbbbbb", "#222222" },
+- [SchemeSel] = { "#eeeeee", "#005577" },
+- [SchemeOut] = { "#000000", "#00ffff" },
++ [SchemeNorm] = { normfgcolor, normbgcolor },
++ [SchemeSel] = { selfgcolor, selbgcolor },
++ [SchemeOut] = { "#000000", "#00ffff" },
+ };
+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+ static unsigned int lines = 0;
+@@ -21,3 +30,15 @@ static unsigned int lines = 0;
+ * for example: " /?\"&[]"
+ */
+ static const char worddelimiters[] = " ";
++
++/*
++ * Xresources preferences to load at startup
++ */
++ResourcePref resources[] = {
++ { "font", STRING, &font },
++ { "normfgcolor", STRING, &normfgcolor },
++ { "normbgcolor", STRING, &normbgcolor },
++ { "selfgcolor", STRING, &selfgcolor },
++ { "selbgcolor", STRING, &selbgcolor },
++ { "prompt", STRING, &prompt },
++};
+diff -rupN orig/dmenu.c patched/dmenu.c
+--- orig/dmenu.c 2021-04-16 06:30:47.715924755 +0300
++++ patched/dmenu.c 2021-04-16 06:30:59.668925245 +0300
+@@ -11,6 +11,7 @@
+ #include <X11/Xlib.h>
+ #include <X11/Xatom.h>
+ #include <X11/Xutil.h>
++#include <X11/Xresource.h>
+ #ifdef XINERAMA
+ #include <X11/extensions/Xinerama.h>
+ #endif
+@@ -53,6 +54,21 @@ static XIC xic;
+ static Drw *drw;
+ static Clr *scheme[SchemeLast];
+
++/* Xresources preferences */
++enum resource_type {
++ STRING = 0,
++ INTEGER = 1,
++ FLOAT = 2
++};
++typedef struct {
++ char *name;
++ enum resource_type type;
++ void *dst;
++} ResourcePref;
++
++static void load_xresources(void);
++static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst);
++
+ #include "config.h"
+
+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+@@ -395,7 +411,7 @@ keypress(XKeyEvent *ev)
+
+ switch(ksym) {
+ default:
+-insert:
++ insert:
+ if (!iscntrl(*buf))
+ insert(buf, len);
+ break;
+@@ -547,6 +563,54 @@ readstdin(void)
+ lines = MIN(lines, i);
+ }
+
++void
++resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst)
++{
++ char *sdst = NULL;
++ int *idst = NULL;
++ float *fdst = NULL;
++ sdst = dst;
++ idst = dst;
++ fdst = dst;
++ char fullname[256];
++ char *type;
++ XrmValue ret;
++ snprintf(fullname, sizeof(fullname), "%s.%s", "dmenu", name);
++ fullname[sizeof(fullname) - 1] = '\0';
++ XrmGetResource(db, fullname, "*", &type, &ret);
++ if (!(ret.addr == NULL || strncmp("String", type, 64)))
++ {
++ switch (rtype) {
++ case STRING:
++ strcpy(sdst, ret.addr);
++ break;
++ case INTEGER:
++ *idst = strtoul(ret.addr, NULL, 10);
++ break;
++ case FLOAT:
++ *fdst = strtof(ret.addr, NULL);
++ break;
++ }
++ }
++}
++
++void
++load_xresources(void)
++{
++ Display *display;
++ char *resm;
++ XrmDatabase db;
++ ResourcePref *p;
++ display = XOpenDisplay(NULL);
++ resm = XResourceManagerString(display);
++ if (!resm)
++ return;
++ db = XrmGetStringDatabase(resm);
++ for (p = resources; p < resources + LENGTH(resources); p++)
++ resource_load(db, p->name, p->type, p->dst);
++ XCloseDisplay(display);
++}
++
+ static void
+ run(void)
+ {
+@@ -700,6 +764,9 @@ main(int argc, char *argv[])
+ XWindowAttributes wa;
+ int i, fast = 0;
+
++ XrmInitialize();
++ load_xresources();
++
+ for (i = 1; i < argc; i++)
+ /* these options take no arguments */
+ if (!strcmp(argv[i], "-v")) { /* prints version information */
+diff -rupN orig/drw.c patched/drw.c
+--- orig/drw.c 2021-04-16 06:30:47.718924755 +0300
++++ patched/drw.c 2021-04-16 06:30:59.670925245 +0300
+@@ -208,7 +208,7 @@ drw_clr_create(Drw *drw, Clr *dest, cons
+ /* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+ Clr *
+-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
++drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount)
+ {
+ size_t i;
+ Clr *ret;
+diff -rupN orig/drw.h patched/drw.h
+--- orig/drw.h 2021-04-16 06:30:47.718924755 +0300
++++ patched/drw.h 2021-04-16 06:30:59.671925245 +0300
+@@ -39,7 +39,7 @@ void drw_font_getexts(Fnt *font, const c
+
+ /* Colorscheme abstraction */
+ void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
+-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
++Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount);
+
+ /* Cursor abstraction */
+ Cur *drw_cur_create(Drw *drw, int shape);