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:
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 */