From 0c664be40b6a81727bbefaceff053598766cfa2b Mon Sep 17 00:00:00 2001 From: justsaumit Date: Wed, 2 Mar 2022 09:21:44 +0530 Subject: removed numbers patch --- patches/4.dmenu-instant-4.7.diff | 73 +++++++ patches/4.dmenu-numbers-4.9.diff | 81 ------- patches/5.dmenu-instant-4.7.diff | 73 ------- patches/5.dmenu-mousesupport-5.1.diff | 144 +++++++++++++ patches/6.dmenu-mousesupport-5.1.diff | 144 ------------- patches/6.dmenu-textscroll-20180607-a314412.diff | 245 ++++++++++++++++++++++ patches/7.dmenu-navhistory-5.0.diff | 256 +++++++++++++++++++++++ patches/7.dmenu-textscroll-20180607-a314412.diff | 245 ---------------------- patches/8.dmenu-navhistory-5.0.diff | 256 ----------------------- 9 files changed, 718 insertions(+), 799 deletions(-) create mode 100644 patches/4.dmenu-instant-4.7.diff delete mode 100644 patches/4.dmenu-numbers-4.9.diff delete mode 100644 patches/5.dmenu-instant-4.7.diff create mode 100644 patches/5.dmenu-mousesupport-5.1.diff delete mode 100644 patches/6.dmenu-mousesupport-5.1.diff create mode 100644 patches/6.dmenu-textscroll-20180607-a314412.diff create mode 100644 patches/7.dmenu-navhistory-5.0.diff delete mode 100644 patches/7.dmenu-textscroll-20180607-a314412.diff delete mode 100644 patches/8.dmenu-navhistory-5.0.diff (limited to 'patches') diff --git a/patches/4.dmenu-instant-4.7.diff b/patches/4.dmenu-instant-4.7.diff new file mode 100644 index 0000000..a78276f --- /dev/null +++ b/patches/4.dmenu-instant-4.7.diff @@ -0,0 +1,73 @@ +diff --git a/config.def.h b/config.def.h +index 1edb647..37f43d5 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -1,6 +1,7 @@ + /* See LICENSE file for copyright and license details. */ + /* Default settings; can be overriden by command line. */ + ++static int instant = 0; + static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ + /* -fn option overrides fonts[0]; default X11 font or font set */ + static const char *fonts[] = { +diff --git a/dmenu.1 b/dmenu.1 +index 9eab758..98d3725 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -3,7 +3,7 @@ + dmenu \- dynamic menu + .SH SYNOPSIS + .B dmenu +-.RB [ \-bfiv ] ++.RB [ \-bfinv ] + .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 \-n ++dmenu instantly selects if only one match. ++.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..2c2f03e 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -260,6 +260,13 @@ match(void) + matchend = substrend; + } + curr = sel = matches; ++ ++ if(instant && matches && matches==matchend && !lsubstr) { ++ puts(matches->text); ++ cleanup(); ++ exit(0); ++ } ++ + calcoffsets(); + } + +@@ -636,7 +643,7 @@ setup(void) + static void + usage(void) + { +- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" ++ fputs("usage: dmenu [-bfinv] [-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 +666,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], "-n")) /* instant select only match */ ++ instant = 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/4.dmenu-numbers-4.9.diff b/patches/4.dmenu-numbers-4.9.diff deleted file mode 100644 index 113be80..0000000 --- a/patches/4.dmenu-numbers-4.9.diff +++ /dev/null @@ -1,81 +0,0 @@ -From 61abc60dbfaa8ec63fcd176307308aee88a19e32 Mon Sep 17 00:00:00 2001 -From: Miles Alan -Date: Sat, 10 Aug 2019 17:20:08 -0500 -Subject: [PATCH] Display number of matched and total items in top right corner - ---- - dmenu.c | 25 +++++++++++++++++++++++-- - 1 file changed, 23 insertions(+), 2 deletions(-) - -diff --git a/dmenu.c b/dmenu.c -index 6b8f51b..98c5810 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -24,6 +24,8 @@ - * 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) -+#define NUMBERSMAXDIGITS 100 -+#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1 - - /* enums */ - enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ -@@ -34,6 +36,7 @@ struct item { - int out; - }; - -+static char numbers[NUMBERSBUFSIZE] = ""; - static char text[BUFSIZ] = ""; - static char *embed; - static int bh, mw, mh; -@@ -126,6 +129,21 @@ drawitem(struct item *item, int x, int y, int w) - return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); - } - -+static void -+recalculatenumbers() -+{ -+ unsigned int numer = 0, denom = 0; -+ struct item *item; -+ if (matchend) { -+ numer++; -+ for (item = matchend; item && item->left; item = item->left) -+ numer++; -+ } -+ for (item = items; item && item->text; item++) -+ denom++; -+ snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom); -+} -+ - static void - drawmenu(void) - { -@@ -151,6 +169,7 @@ drawmenu(void) - drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); - } - -+ recalculatenumbers(); - if (lines > 0) { - /* draw vertical list */ - for (item = curr; item != next; item = item->right) -@@ -165,13 +184,15 @@ 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, MIN(TEXTW(item->text), mw - x - TEXTW(">") - TEXTW(numbers))); - if (next) { - w = TEXTW(">"); - drw_setscheme(drw, scheme[SchemeNorm]); -- drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); -+ drw_text(drw, mw - w - TEXTW(numbers), 0, w, bh, lrpad / 2, ">", 0); - } - } -+ drw_setscheme(drw, scheme[SchemeNorm]); -+ drw_text(drw, mw - TEXTW(numbers), 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0); - drw_map(drw, win, 0, 0, mw, mh); - } - --- -2.19.2 - diff --git a/patches/5.dmenu-instant-4.7.diff b/patches/5.dmenu-instant-4.7.diff deleted file mode 100644 index a78276f..0000000 --- a/patches/5.dmenu-instant-4.7.diff +++ /dev/null @@ -1,73 +0,0 @@ -diff --git a/config.def.h b/config.def.h -index 1edb647..37f43d5 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -1,6 +1,7 @@ - /* See LICENSE file for copyright and license details. */ - /* Default settings; can be overriden by command line. */ - -+static int instant = 0; - static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ - /* -fn option overrides fonts[0]; default X11 font or font set */ - static const char *fonts[] = { -diff --git a/dmenu.1 b/dmenu.1 -index 9eab758..98d3725 100644 ---- a/dmenu.1 -+++ b/dmenu.1 -@@ -3,7 +3,7 @@ - dmenu \- dynamic menu - .SH SYNOPSIS - .B dmenu --.RB [ \-bfiv ] -+.RB [ \-bfinv ] - .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 \-n -+dmenu instantly selects if only one match. -+.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..2c2f03e 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -260,6 +260,13 @@ match(void) - matchend = substrend; - } - curr = sel = matches; -+ -+ if(instant && matches && matches==matchend && !lsubstr) { -+ puts(matches->text); -+ cleanup(); -+ exit(0); -+ } -+ - calcoffsets(); - } - -@@ -636,7 +643,7 @@ setup(void) - static void - usage(void) - { -- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" -+ fputs("usage: dmenu [-bfinv] [-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 +666,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], "-n")) /* instant select only match */ -+ instant = 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/5.dmenu-mousesupport-5.1.diff b/patches/5.dmenu-mousesupport-5.1.diff new file mode 100644 index 0000000..49824ba --- /dev/null +++ b/patches/5.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/6.dmenu-mousesupport-5.1.diff b/patches/6.dmenu-mousesupport-5.1.diff deleted file mode 100644 index 49824ba..0000000 --- a/patches/6.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/6.dmenu-textscroll-20180607-a314412.diff b/patches/6.dmenu-textscroll-20180607-a314412.diff new file mode 100644 index 0000000..7a7386a --- /dev/null +++ b/patches/6.dmenu-textscroll-20180607-a314412.diff @@ -0,0 +1,245 @@ +diff --git a/dmenu.c b/dmenu.c +index 5c835dd..71efe52 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -131,9 +131,10 @@ drawitem(struct item *item, int x, int y, int w) + static void + drawmenu(void) + { +- unsigned int curpos; ++ static int curpos, oldcurlen; + struct item *item; + int x = 0, y = 0, w; ++ int curlen, rcurlen; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); +@@ -144,14 +145,21 @@ 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); ++ w -= lrpad / 2; ++ x += lrpad / 2; + +- 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); +- } ++ rcurlen = drw_fontset_getwidth(drw, text + cursor); ++ curlen = drw_fontset_getwidth(drw, text) - rcurlen; ++ curpos += curlen - oldcurlen; ++ curpos = MIN(w, MAX(0, curpos)); ++ curpos = MAX(curpos, w - rcurlen); ++ curpos = MIN(curpos, curlen); ++ oldcurlen = curlen; ++ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); ++ drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); ++ drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0); + + if (lines > 0) { + /* draw vertical list */ +diff --git a/drw.c b/drw.c +index c638323..bfffbc1 100644 +--- a/drw.c ++++ b/drw.c +@@ -364,6 +364,175 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp + return x + (render ? w : 0); + } + ++int ++utf8nextchar(const char *str, int len, int i, int inc) ++{ ++ int n; ++ ++ for (n = i + inc; n + inc >= 0 && n + inc <= len ++ && (str[n] & 0xc0) == 0x80; n += inc) ++ ; ++ return n; ++} ++ ++int ++drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align) ++{ ++ int ty; ++ unsigned int ew; ++ XftDraw *d = NULL; ++ Fnt *usedfont, *curfont, *nextfont; ++ size_t len; ++ int utf8strlen, utf8charlen, render = x || y || w || h; ++ long utf8codepoint = 0; ++ const char *utf8str; ++ FcCharSet *fccharset; ++ FcPattern *fcpattern; ++ FcPattern *match; ++ XftResult result; ++ int charexists = 0; ++ int i, n; ++ ++ if (!drw || (render && !drw->scheme) || !text || !drw->fonts || textlen <= 0 ++ || (align != AlignL && align != AlignR)) ++ return 0; ++ ++ if (!render) { ++ w = ~w; ++ } else { ++ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); ++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); ++ d = XftDrawCreate(drw->dpy, drw->drawable, ++ DefaultVisual(drw->dpy, drw->screen), ++ DefaultColormap(drw->dpy, drw->screen)); ++ } ++ ++ usedfont = drw->fonts; ++ i = align == AlignL ? 0 : textlen; ++ x = align == AlignL ? x : x + w; ++ while (1) { ++ utf8strlen = 0; ++ nextfont = NULL; ++ /* if (align == AlignL) */ ++ utf8str = text + i; ++ ++ while ((align == AlignL && i < textlen) || (align == AlignR && i > 0)) { ++ if (align == AlignL) { ++ utf8charlen = utf8decode(text + i, &utf8codepoint, MIN(textlen - i, UTF_SIZ)); ++ if (!utf8charlen) { ++ textlen = i; ++ break; ++ } ++ } else { ++ n = utf8nextchar(text, textlen, i, -1); ++ utf8charlen = utf8decode(text + n, &utf8codepoint, MIN(textlen - n, UTF_SIZ)); ++ if (!utf8charlen) { ++ textlen -= i; ++ text += i; ++ i = 0; ++ break; ++ } ++ } ++ for (curfont = drw->fonts; curfont; curfont = curfont->next) { ++ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); ++ if (charexists) { ++ if (curfont == usedfont) { ++ utf8strlen += utf8charlen; ++ i += align == AlignL ? utf8charlen : -utf8charlen; ++ } else { ++ nextfont = curfont; ++ } ++ break; ++ } ++ } ++ ++ if (!charexists || nextfont) ++ break; ++ else ++ charexists = 0; ++ } ++ ++ if (align == AlignR) ++ utf8str = text + i; ++ ++ if (utf8strlen) { ++ drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); ++ /* shorten text if necessary */ ++ if (align == AlignL) { ++ for (len = utf8strlen; len && ew > w; ) { ++ len = utf8nextchar(utf8str, len, len, -1); ++ drw_font_getexts(usedfont, utf8str, len, &ew, NULL); ++ } ++ } else { ++ for (len = utf8strlen; len && ew > w; ) { ++ n = utf8nextchar(utf8str, len, 0, +1); ++ utf8str += n; ++ len -= n; ++ drw_font_getexts(usedfont, utf8str, len, &ew, NULL); ++ } ++ } ++ ++ if (len) { ++ if (render) { ++ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; ++ XftDrawStringUtf8(d, &drw->scheme[ColFg], ++ usedfont->xfont, align == AlignL ? x : x - ew, ty, (XftChar8 *)utf8str, len); ++ } ++ x += align == AlignL ? ew : -ew; ++ w -= ew; ++ } ++ if (len < utf8strlen) ++ break; ++ } ++ ++ if ((align == AlignR && i <= 0) || (align == AlignL && i >= textlen)) { ++ break; ++ } else if (nextfont) { ++ charexists = 0; ++ usedfont = nextfont; ++ } else { ++ /* Regardless of whether or not a fallback font is found, the ++ * character must be drawn. */ ++ charexists = 1; ++ ++ fccharset = FcCharSetCreate(); ++ FcCharSetAddChar(fccharset, utf8codepoint); ++ ++ if (!drw->fonts->pattern) { ++ /* Refer to the comment in xfont_create for more information. */ ++ die("the first font in the cache must be loaded from a font string."); ++ } ++ ++ fcpattern = FcPatternDuplicate(drw->fonts->pattern); ++ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); ++ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); ++ ++ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); ++ FcDefaultSubstitute(fcpattern); ++ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); ++ ++ FcCharSetDestroy(fccharset); ++ FcPatternDestroy(fcpattern); ++ ++ if (match) { ++ usedfont = xfont_create(drw, NULL, match); ++ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { ++ for (curfont = drw->fonts; curfont->next; curfont = curfont->next) ++ ; /* NOP */ ++ curfont->next = usedfont; ++ } else { ++ xfont_free(usedfont); ++ usedfont = drw->fonts; ++ } ++ } ++ } ++ } ++ if (d) ++ XftDrawDestroy(d); ++ ++ return x; ++} ++ + void + drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + { +diff --git a/drw.h b/drw.h +index 4c67419..b66a83e 100644 +--- a/drw.h ++++ b/drw.h +@@ -13,6 +13,7 @@ typedef struct Fnt { + } Fnt; + + enum { ColFg, ColBg }; /* Clr scheme index */ ++enum { AlignL, AlignR }; + typedef XftColor Clr; + + typedef struct { +@@ -52,6 +53,7 @@ void drw_setscheme(Drw *drw, Clr *scm); + /* Drawing functions */ + void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); + int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); ++int drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align); + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/patches/7.dmenu-navhistory-5.0.diff b/patches/7.dmenu-navhistory-5.0.diff new file mode 100644 index 0000000..1f7cf6c --- /dev/null +++ b/patches/7.dmenu-navhistory-5.0.diff @@ -0,0 +1,256 @@ +From a4a08baf35edb6b50ed14f76e99d0c6fe790759d Mon Sep 17 00:00:00 2001 +From: Max Schillinger +Date: Fri, 9 Jul 2021 17:17:36 +0200 +Subject: [PATCH] Bug fix: Writing first entry to history file was skipped + +--- + config.def.h | 2 + + dmenu.1 | 5 ++ + dmenu.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++- + dmenu_run | 2 +- + 4 files changed, 151 insertions(+), 2 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..e3e1b53 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -15,6 +15,8 @@ static const char *colors[SchemeLast][2] = { + }; + /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ + static unsigned int lines = 0; ++static unsigned int maxhist = 64; ++static int histnodup = 1; /* if 0, record repeated histories */ + + /* + * Characters not considered part of a word while deleting words +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..ff496dd 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -22,6 +22,8 @@ dmenu \- dynamic menu + .IR color ] + .RB [ \-w + .IR windowid ] ++.RB [ \-H ++.IR histfile ] + .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 \-H " histfile" ++save input in histfile and use it for history navigation. + .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 65f25ce..5023257 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -53,6 +53,10 @@ static XIC xic; + static Drw *drw; + static Clr *scheme[SchemeLast]; + ++static char *histfile; ++static char **history; ++static size_t histsz, histpos; ++ + #include "config.h" + + static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +@@ -304,6 +308,129 @@ movewordedge(int dir) + } + } + ++static void ++loadhistory(void) ++{ ++ FILE *fp = NULL; ++ static size_t cap = 0; ++ size_t llen; ++ char *line; ++ ++ if (!histfile) { ++ return; ++ } ++ ++ fp = fopen(histfile, "r"); ++ if (!fp) { ++ return; ++ } ++ ++ for (;;) { ++ line = NULL; ++ llen = 0; ++ if (-1 == getline(&line, &llen, fp)) { ++ if (ferror(fp)) { ++ die("failed to read history"); ++ } ++ free(line); ++ break; ++ } ++ ++ if (cap == histsz) { ++ cap += 64 * sizeof(char*); ++ history = realloc(history, cap); ++ if (!history) { ++ die("failed to realloc memory"); ++ } ++ } ++ strtok(line, "\n"); ++ history[histsz] = line; ++ histsz++; ++ } ++ histpos = histsz; ++ ++ if (fclose(fp)) { ++ die("failed to close file %s", histfile); ++ } ++} ++ ++static void ++navhistory(int dir) ++{ ++ static char def[BUFSIZ]; ++ char *p = NULL; ++ size_t len = 0; ++ ++ if (!history || histpos + 1 == 0) ++ return; ++ ++ if (histsz == histpos) { ++ strncpy(def, text, sizeof(def)); ++ } ++ ++ switch(dir) { ++ case 1: ++ if (histpos < histsz - 1) { ++ p = history[++histpos]; ++ } else if (histpos == histsz - 1) { ++ p = def; ++ histpos++; ++ } ++ break; ++ case -1: ++ if (histpos > 0) { ++ p = history[--histpos]; ++ } ++ break; ++ } ++ if (p == NULL) { ++ return; ++ } ++ ++ len = MIN(strlen(p), BUFSIZ - 1); ++ strncpy(text, p, len); ++ text[len] = '\0'; ++ cursor = len; ++ match(); ++} ++ ++static void ++savehistory(char *input) ++{ ++ unsigned int i; ++ FILE *fp; ++ ++ if (!histfile || ++ 0 == maxhist || ++ 0 == strlen(input)) { ++ goto out; ++ } ++ ++ fp = fopen(histfile, "w"); ++ if (!fp) { ++ die("failed to open %s", histfile); ++ } ++ for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) { ++ if (0 >= fprintf(fp, "%s\n", history[i])) { ++ die("failed to write to %s", histfile); ++ } ++ } ++ if (histsz == 0 || !histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) { /* TODO */ ++ if (0 >= fputs(input, fp)) { ++ die("failed to write to %s", histfile); ++ } ++ } ++ if (fclose(fp)) { ++ die("failed to close file %s", histfile); ++ } ++ ++out: ++ for (i = 0; i < histsz; i++) { ++ free(history[i]); ++ } ++ free(history); ++} ++ + static void + keypress(XKeyEvent *ev) + { +@@ -388,6 +515,14 @@ keypress(XKeyEvent *ev) + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; ++ case XK_p: ++ navhistory(-1); ++ buf[0]=0; ++ break; ++ case XK_n: ++ navhistory(1); ++ buf[0]=0; ++ break; + default: + return; + } +@@ -466,6 +601,8 @@ insert: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { ++ savehistory((sel && !(ev->state & ShiftMask)) ++ ? sel->text : text); + cleanup(); + exit(0); + } +@@ -690,7 +827,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" ++ " [-H histfile]", stderr); + exit(1); + } + +@@ -715,6 +853,8 @@ main(int argc, char *argv[]) + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ ++ else if (!strcmp(argv[i], "-H")) ++ histfile = argv[++i]; + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) +@@ -757,6 +897,8 @@ main(int argc, char *argv[]) + die("pledge"); + #endif + ++ loadhistory(); ++ + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); +diff --git a/dmenu_run b/dmenu_run +index 834ede5..59ec622 100755 +--- a/dmenu_run ++++ b/dmenu_run +@@ -1,2 +1,2 @@ + #!/bin/sh +-dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & ++dmenu_path | dmenu -H "${XDG_CACHE_HOME:-$HOME/.cache/}/dmenu_run.hist" "$@" | ${SHELL:-"/bin/sh"} & +-- +2.25.1 + diff --git a/patches/7.dmenu-textscroll-20180607-a314412.diff b/patches/7.dmenu-textscroll-20180607-a314412.diff deleted file mode 100644 index 7a7386a..0000000 --- a/patches/7.dmenu-textscroll-20180607-a314412.diff +++ /dev/null @@ -1,245 +0,0 @@ -diff --git a/dmenu.c b/dmenu.c -index 5c835dd..71efe52 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -131,9 +131,10 @@ drawitem(struct item *item, int x, int y, int w) - static void - drawmenu(void) - { -- unsigned int curpos; -+ static int curpos, oldcurlen; - struct item *item; - int x = 0, y = 0, w; -+ int curlen, rcurlen; - - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1); -@@ -144,14 +145,21 @@ 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); -+ w -= lrpad / 2; -+ x += lrpad / 2; - -- 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); -- } -+ rcurlen = drw_fontset_getwidth(drw, text + cursor); -+ curlen = drw_fontset_getwidth(drw, text) - rcurlen; -+ curpos += curlen - oldcurlen; -+ curpos = MIN(w, MAX(0, curpos)); -+ curpos = MAX(curpos, w - rcurlen); -+ curpos = MIN(curpos, curlen); -+ oldcurlen = curlen; -+ -+ drw_setscheme(drw, scheme[SchemeNorm]); -+ drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); -+ drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); -+ drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0); - - if (lines > 0) { - /* draw vertical list */ -diff --git a/drw.c b/drw.c -index c638323..bfffbc1 100644 ---- a/drw.c -+++ b/drw.c -@@ -364,6 +364,175 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp - return x + (render ? w : 0); - } - -+int -+utf8nextchar(const char *str, int len, int i, int inc) -+{ -+ int n; -+ -+ for (n = i + inc; n + inc >= 0 && n + inc <= len -+ && (str[n] & 0xc0) == 0x80; n += inc) -+ ; -+ return n; -+} -+ -+int -+drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align) -+{ -+ int ty; -+ unsigned int ew; -+ XftDraw *d = NULL; -+ Fnt *usedfont, *curfont, *nextfont; -+ size_t len; -+ int utf8strlen, utf8charlen, render = x || y || w || h; -+ long utf8codepoint = 0; -+ const char *utf8str; -+ FcCharSet *fccharset; -+ FcPattern *fcpattern; -+ FcPattern *match; -+ XftResult result; -+ int charexists = 0; -+ int i, n; -+ -+ if (!drw || (render && !drw->scheme) || !text || !drw->fonts || textlen <= 0 -+ || (align != AlignL && align != AlignR)) -+ return 0; -+ -+ if (!render) { -+ w = ~w; -+ } else { -+ XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); -+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); -+ d = XftDrawCreate(drw->dpy, drw->drawable, -+ DefaultVisual(drw->dpy, drw->screen), -+ DefaultColormap(drw->dpy, drw->screen)); -+ } -+ -+ usedfont = drw->fonts; -+ i = align == AlignL ? 0 : textlen; -+ x = align == AlignL ? x : x + w; -+ while (1) { -+ utf8strlen = 0; -+ nextfont = NULL; -+ /* if (align == AlignL) */ -+ utf8str = text + i; -+ -+ while ((align == AlignL && i < textlen) || (align == AlignR && i > 0)) { -+ if (align == AlignL) { -+ utf8charlen = utf8decode(text + i, &utf8codepoint, MIN(textlen - i, UTF_SIZ)); -+ if (!utf8charlen) { -+ textlen = i; -+ break; -+ } -+ } else { -+ n = utf8nextchar(text, textlen, i, -1); -+ utf8charlen = utf8decode(text + n, &utf8codepoint, MIN(textlen - n, UTF_SIZ)); -+ if (!utf8charlen) { -+ textlen -= i; -+ text += i; -+ i = 0; -+ break; -+ } -+ } -+ for (curfont = drw->fonts; curfont; curfont = curfont->next) { -+ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); -+ if (charexists) { -+ if (curfont == usedfont) { -+ utf8strlen += utf8charlen; -+ i += align == AlignL ? utf8charlen : -utf8charlen; -+ } else { -+ nextfont = curfont; -+ } -+ break; -+ } -+ } -+ -+ if (!charexists || nextfont) -+ break; -+ else -+ charexists = 0; -+ } -+ -+ if (align == AlignR) -+ utf8str = text + i; -+ -+ if (utf8strlen) { -+ drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); -+ /* shorten text if necessary */ -+ if (align == AlignL) { -+ for (len = utf8strlen; len && ew > w; ) { -+ len = utf8nextchar(utf8str, len, len, -1); -+ drw_font_getexts(usedfont, utf8str, len, &ew, NULL); -+ } -+ } else { -+ for (len = utf8strlen; len && ew > w; ) { -+ n = utf8nextchar(utf8str, len, 0, +1); -+ utf8str += n; -+ len -= n; -+ drw_font_getexts(usedfont, utf8str, len, &ew, NULL); -+ } -+ } -+ -+ if (len) { -+ if (render) { -+ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; -+ XftDrawStringUtf8(d, &drw->scheme[ColFg], -+ usedfont->xfont, align == AlignL ? x : x - ew, ty, (XftChar8 *)utf8str, len); -+ } -+ x += align == AlignL ? ew : -ew; -+ w -= ew; -+ } -+ if (len < utf8strlen) -+ break; -+ } -+ -+ if ((align == AlignR && i <= 0) || (align == AlignL && i >= textlen)) { -+ break; -+ } else if (nextfont) { -+ charexists = 0; -+ usedfont = nextfont; -+ } else { -+ /* Regardless of whether or not a fallback font is found, the -+ * character must be drawn. */ -+ charexists = 1; -+ -+ fccharset = FcCharSetCreate(); -+ FcCharSetAddChar(fccharset, utf8codepoint); -+ -+ if (!drw->fonts->pattern) { -+ /* Refer to the comment in xfont_create for more information. */ -+ die("the first font in the cache must be loaded from a font string."); -+ } -+ -+ fcpattern = FcPatternDuplicate(drw->fonts->pattern); -+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); -+ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); -+ -+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); -+ FcDefaultSubstitute(fcpattern); -+ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); -+ -+ FcCharSetDestroy(fccharset); -+ FcPatternDestroy(fcpattern); -+ -+ if (match) { -+ usedfont = xfont_create(drw, NULL, match); -+ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { -+ for (curfont = drw->fonts; curfont->next; curfont = curfont->next) -+ ; /* NOP */ -+ curfont->next = usedfont; -+ } else { -+ xfont_free(usedfont); -+ usedfont = drw->fonts; -+ } -+ } -+ } -+ } -+ if (d) -+ XftDrawDestroy(d); -+ -+ return x; -+} -+ - void - drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) - { -diff --git a/drw.h b/drw.h -index 4c67419..b66a83e 100644 ---- a/drw.h -+++ b/drw.h -@@ -13,6 +13,7 @@ typedef struct Fnt { - } Fnt; - - enum { ColFg, ColBg }; /* Clr scheme index */ -+enum { AlignL, AlignR }; - typedef XftColor Clr; - - typedef struct { -@@ -52,6 +53,7 @@ void drw_setscheme(Drw *drw, Clr *scm); - /* Drawing functions */ - void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); - int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); -+int drw_text_align(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int textlen, int align); - - /* Map functions */ - void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/patches/8.dmenu-navhistory-5.0.diff b/patches/8.dmenu-navhistory-5.0.diff deleted file mode 100644 index 1f7cf6c..0000000 --- a/patches/8.dmenu-navhistory-5.0.diff +++ /dev/null @@ -1,256 +0,0 @@ -From a4a08baf35edb6b50ed14f76e99d0c6fe790759d Mon Sep 17 00:00:00 2001 -From: Max Schillinger -Date: Fri, 9 Jul 2021 17:17:36 +0200 -Subject: [PATCH] Bug fix: Writing first entry to history file was skipped - ---- - config.def.h | 2 + - dmenu.1 | 5 ++ - dmenu.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++- - dmenu_run | 2 +- - 4 files changed, 151 insertions(+), 2 deletions(-) - -diff --git a/config.def.h b/config.def.h -index 1edb647..e3e1b53 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -15,6 +15,8 @@ static const char *colors[SchemeLast][2] = { - }; - /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ - static unsigned int lines = 0; -+static unsigned int maxhist = 64; -+static int histnodup = 1; /* if 0, record repeated histories */ - - /* - * Characters not considered part of a word while deleting words -diff --git a/dmenu.1 b/dmenu.1 -index 323f93c..ff496dd 100644 ---- a/dmenu.1 -+++ b/dmenu.1 -@@ -22,6 +22,8 @@ dmenu \- dynamic menu - .IR color ] - .RB [ \-w - .IR windowid ] -+.RB [ \-H -+.IR histfile ] - .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 \-H " histfile" -+save input in histfile and use it for history navigation. - .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 65f25ce..5023257 100644 ---- a/dmenu.c -+++ b/dmenu.c -@@ -53,6 +53,10 @@ static XIC xic; - static Drw *drw; - static Clr *scheme[SchemeLast]; - -+static char *histfile; -+static char **history; -+static size_t histsz, histpos; -+ - #include "config.h" - - static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -@@ -304,6 +308,129 @@ movewordedge(int dir) - } - } - -+static void -+loadhistory(void) -+{ -+ FILE *fp = NULL; -+ static size_t cap = 0; -+ size_t llen; -+ char *line; -+ -+ if (!histfile) { -+ return; -+ } -+ -+ fp = fopen(histfile, "r"); -+ if (!fp) { -+ return; -+ } -+ -+ for (;;) { -+ line = NULL; -+ llen = 0; -+ if (-1 == getline(&line, &llen, fp)) { -+ if (ferror(fp)) { -+ die("failed to read history"); -+ } -+ free(line); -+ break; -+ } -+ -+ if (cap == histsz) { -+ cap += 64 * sizeof(char*); -+ history = realloc(history, cap); -+ if (!history) { -+ die("failed to realloc memory"); -+ } -+ } -+ strtok(line, "\n"); -+ history[histsz] = line; -+ histsz++; -+ } -+ histpos = histsz; -+ -+ if (fclose(fp)) { -+ die("failed to close file %s", histfile); -+ } -+} -+ -+static void -+navhistory(int dir) -+{ -+ static char def[BUFSIZ]; -+ char *p = NULL; -+ size_t len = 0; -+ -+ if (!history || histpos + 1 == 0) -+ return; -+ -+ if (histsz == histpos) { -+ strncpy(def, text, sizeof(def)); -+ } -+ -+ switch(dir) { -+ case 1: -+ if (histpos < histsz - 1) { -+ p = history[++histpos]; -+ } else if (histpos == histsz - 1) { -+ p = def; -+ histpos++; -+ } -+ break; -+ case -1: -+ if (histpos > 0) { -+ p = history[--histpos]; -+ } -+ break; -+ } -+ if (p == NULL) { -+ return; -+ } -+ -+ len = MIN(strlen(p), BUFSIZ - 1); -+ strncpy(text, p, len); -+ text[len] = '\0'; -+ cursor = len; -+ match(); -+} -+ -+static void -+savehistory(char *input) -+{ -+ unsigned int i; -+ FILE *fp; -+ -+ if (!histfile || -+ 0 == maxhist || -+ 0 == strlen(input)) { -+ goto out; -+ } -+ -+ fp = fopen(histfile, "w"); -+ if (!fp) { -+ die("failed to open %s", histfile); -+ } -+ for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) { -+ if (0 >= fprintf(fp, "%s\n", history[i])) { -+ die("failed to write to %s", histfile); -+ } -+ } -+ if (histsz == 0 || !histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) { /* TODO */ -+ if (0 >= fputs(input, fp)) { -+ die("failed to write to %s", histfile); -+ } -+ } -+ if (fclose(fp)) { -+ die("failed to close file %s", histfile); -+ } -+ -+out: -+ for (i = 0; i < histsz; i++) { -+ free(history[i]); -+ } -+ free(history); -+} -+ - static void - keypress(XKeyEvent *ev) - { -@@ -388,6 +515,14 @@ keypress(XKeyEvent *ev) - case XK_j: ksym = XK_Next; break; - case XK_k: ksym = XK_Prior; break; - case XK_l: ksym = XK_Down; break; -+ case XK_p: -+ navhistory(-1); -+ buf[0]=0; -+ break; -+ case XK_n: -+ navhistory(1); -+ buf[0]=0; -+ break; - default: - return; - } -@@ -466,6 +601,8 @@ insert: - case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); - if (!(ev->state & ControlMask)) { -+ savehistory((sel && !(ev->state & ShiftMask)) -+ ? sel->text : text); - cleanup(); - exit(0); - } -@@ -690,7 +827,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" -+ " [-H histfile]", stderr); - exit(1); - } - -@@ -715,6 +853,8 @@ main(int argc, char *argv[]) - } else if (i + 1 == argc) - usage(); - /* these options take one argument */ -+ else if (!strcmp(argv[i], "-H")) -+ histfile = argv[++i]; - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ - lines = atoi(argv[++i]); - else if (!strcmp(argv[i], "-m")) -@@ -757,6 +897,8 @@ main(int argc, char *argv[]) - die("pledge"); - #endif - -+ loadhistory(); -+ - if (fast && !isatty(0)) { - grabkeyboard(); - readstdin(); -diff --git a/dmenu_run b/dmenu_run -index 834ede5..59ec622 100755 ---- a/dmenu_run -+++ b/dmenu_run -@@ -1,2 +1,2 @@ - #!/bin/sh --dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & -+dmenu_path | dmenu -H "${XDG_CACHE_HOME:-$HOME/.cache/}/dmenu_run.hist" "$@" | ${SHELL:-"/bin/sh"} & --- -2.25.1 - -- cgit v1.2.3