sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit ee6b95c8c4694d7e1766919cfee98002fe8101b5
parent b92c43124f5363ff94efdc01ff12dfdecf2eb26b
Author: zerg <zergrusherncrusher@disroot.org>
Date:   Sun, 16 Apr 2023 15:06:58 -0700

[dmenu][patch][vi_mode] Add Ctrl + Enter behavior

Diffstat:
Dtools.suckless.org/dmenu/patches/vi-mode/dmenu-vi_mode-20230330-dfbbf7f.diff | 298-------------------------------------------------------------------------------
Atools.suckless.org/dmenu/patches/vi-mode/dmenu-vi_mode-20230416-0fe460d.diff | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 300 insertions(+), 298 deletions(-)

diff --git a/tools.suckless.org/dmenu/patches/vi-mode/dmenu-vi_mode-20230330-dfbbf7f.diff b/tools.suckless.org/dmenu/patches/vi-mode/dmenu-vi_mode-20230330-dfbbf7f.diff @@ -1,298 +0,0 @@ -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 4e7df12..14fbde3 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,179 @@ 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); -+ 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 +527,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 +742,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(); - } - -@@ -737,6 +938,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/tools.suckless.org/dmenu/patches/vi-mode/dmenu-vi_mode-20230416-0fe460d.diff b/tools.suckless.org/dmenu/patches/vi-mode/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 */