diff options
| author | ProsperousPotato <ProsperousPotato@users.noreply.github.com> | 2025-12-12 23:27:14 +0000 |
|---|---|---|
| committer | ProsperousPotato <ProsperousPotato@users.noreply.github.com> | 2025-12-12 23:27:14 +0000 |
| commit | 188dde6ca27ef29460daffaf754bd9352545ce7a (patch) | |
| tree | 2ff3321554dc5be005e26972ececfae1a2d84f0b | |
| parent | dd87b52b68127d36dc51876c756e7cbfba715a4e (diff) | |
add vi modemain
| -rw-r--r--[-rwxr-xr-x] | LICENSE | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | Makefile | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | README | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | arg.h | 0 | ||||
| -rw-r--r-- | config.def.h | 24 | ||||
| -rw-r--r--[-rwxr-xr-x] | config.h | 13 | ||||
| -rw-r--r--[-rwxr-xr-x] | config.mk | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | dmenu.1 | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | dmenu.c | 212 | ||||
| -rw-r--r--[-rwxr-xr-x] | dmenu_path | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | dmenu_run | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | drw.c | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | drw.h | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | stest.1 | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | stest.c | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | util.c | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | util.h | 0 |
17 files changed, 223 insertions, 26 deletions
diff --git a/config.def.h b/config.def.h deleted file mode 100644 index c4c8b72..0000000 --- a/config.def.h +++ /dev/null @@ -1,24 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -/* Default settings; can be overriden by command line. */ - -static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -/* -fn option overrides fonts[0]; default X11 font or font set */ -static const char *fonts[] = { - "monospace:size=10" -}; -static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -static const char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, - [SchemeOut] = { "#000000", "#00ffff" }, - [SchemePrompt] = { "#444444", "#222222" }, -}; -/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -static unsigned int lines = 0; - -/* - * Characters not considered part of a word while deleting words - * for example: " /?\"&[]" - */ -static const char worddelimiters[] = " "; @@ -13,6 +13,7 @@ static const char *colors[SchemeLast][2] = { [SchemeSel] = { "#000000", "#bbbbbb" }, [SchemeOut] = { "#000000", "#00ffff" }, [SchemePrompt] = { "#444444", "#000000" }, + [SchemeCursor] = { "#222222", "#bbbbbb"}, }; /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ static unsigned int lines = 0; @@ -22,3 +23,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 = 1; /* 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/config.mk b/config.mk index dcc5bb3..dcc5bb3 100755..100644 --- a/config.mk +++ b/config.mk @@ -25,7 +25,7 @@ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) /* enums */ -enum { SchemeNorm, SchemeSel, SchemeOut, SchemePrompt, SchemeLast }; /* color schemes */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemePrompt, SchemeCursor, SchemeLast }; /* color schemes */ struct item { char *text; @@ -33,6 +33,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; @@ -43,6 +48,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; @@ -165,7 +171,15 @@ drawmenu(void) if (text[0] != '\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, 1, 2, bh - 4, 1, 0); } @@ -324,6 +338,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) { char buf[64]; @@ -342,6 +531,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; @@ -545,6 +746,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(); } @@ -739,6 +942,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/dmenu_path b/dmenu_path index 3a7cda7..3a7cda7 100755..100644 --- a/dmenu_path +++ b/dmenu_path |
