dmenu-vi_mode-20230416-0fe460d.diff - sites - public wiki contents of suckless.… | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
dmenu-vi_mode-20230416-0fe460d.diff (7192B) | |
--- | |
1 diff --git a/config.def.h b/config.def.h | |
2 index 1edb647..7bf5f4a 100644 | |
3 --- a/config.def.h | |
4 +++ b/config.def.h | |
5 @@ -12,6 +12,7 @@ static const char *colors[SchemeLast][2] = { | |
6 [SchemeNorm] = { "#bbbbbb", "#222222" }, | |
7 [SchemeSel] = { "#eeeeee", "#005577" }, | |
8 [SchemeOut] = { "#000000", "#00ffff" }, | |
9 + [SchemeCursor] = { "#222222", "#bbbbbb"}, | |
10 }; | |
11 /* -l option; if nonzero, dmenu uses vertical list with given number of… | |
12 static unsigned int lines = 0; | |
13 @@ -21,3 +22,15 @@ static unsigned int lines = 0; | |
14 * for example: " /?\"&[]" | |
15 */ | |
16 static const char worddelimiters[] = " "; | |
17 + | |
18 +/* | |
19 + * -vi option; if nonzero, vi mode is always enabled and can be | |
20 + * accessed with the global_esc keysym + mod mask | |
21 + */ | |
22 +static unsigned int vi_mode = 1; | |
23 +static unsigned int start_mode = 0; /* mode to u… | |
24 +static Key global_esc = { XK_n, Mod1Mask }; /* escape key when v… | |
25 +static Key quit_keys[] = { | |
26 + /* keysym modifier */ | |
27 + { XK_q, 0 } | |
28 +}; | |
29 diff --git a/dmenu.c b/dmenu.c | |
30 index 62f1089..8066271 100644 | |
31 --- a/dmenu.c | |
32 +++ b/dmenu.c | |
33 @@ -26,7 +26,7 @@ | |
34 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) | |
35 | |
36 /* enums */ | |
37 -enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes… | |
38 +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeCursor, SchemeLast }; /*… | |
39 | |
40 struct item { | |
41 char *text; | |
42 @@ -34,6 +34,11 @@ struct item { | |
43 int out; | |
44 }; | |
45 | |
46 +typedef struct { | |
47 + KeySym ksym; | |
48 + unsigned int state; | |
49 +} Key; | |
50 + | |
51 static char text[BUFSIZ] = ""; | |
52 static char *embed; | |
53 static int bh, mw, mh; | |
54 @@ -44,6 +49,7 @@ static struct item *items = NULL; | |
55 static struct item *matches, *matchend; | |
56 static struct item *prev, *curr, *next, *sel; | |
57 static int mon = -1, screen; | |
58 +static unsigned int using_vi_mode = 0; | |
59 | |
60 static Atom clip, utf8; | |
61 static Display *dpy; | |
62 @@ -163,7 +169,15 @@ drawmenu(void) | |
63 drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); | |
64 | |
65 curpos = TEXTW(text) - TEXTW(&text[cursor]); | |
66 - if ((curpos += lrpad / 2 - 1) < w) { | |
67 + curpos += lrpad / 2 - 1; | |
68 + if (using_vi_mode && text[0] != '\0') { | |
69 + drw_setscheme(drw, scheme[SchemeCursor]); | |
70 + char vi_char[] = {text[cursor], '\0'}; | |
71 + drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh… | |
72 + } else if (using_vi_mode) { | |
73 + drw_setscheme(drw, scheme[SchemeNorm]); | |
74 + drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0); | |
75 + } else if (curpos < w) { | |
76 drw_setscheme(drw, scheme[SchemeNorm]); | |
77 drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); | |
78 } | |
79 @@ -321,6 +335,181 @@ movewordedge(int dir) | |
80 } | |
81 } | |
82 | |
83 +static void | |
84 +vi_keypress(KeySym ksym, const XKeyEvent *ev) | |
85 +{ | |
86 + static const size_t quit_len = LENGTH(quit_keys); | |
87 + if (ev->state & ControlMask) { | |
88 + switch(ksym) { | |
89 + /* movement */ | |
90 + case XK_d: /* fallthrough */ | |
91 + if (next) { | |
92 + sel = curr = next; | |
93 + calcoffsets(); | |
94 + goto draw; | |
95 + } else | |
96 + ksym = XK_G; | |
97 + break; | |
98 + case XK_u: | |
99 + if (prev) { | |
100 + sel = curr = prev; | |
101 + calcoffsets(); | |
102 + goto draw; | |
103 + } else | |
104 + ksym = XK_g; | |
105 + break; | |
106 + case XK_p: /* fallthrough */ | |
107 + case XK_P: break; | |
108 + case XK_c: | |
109 + cleanup(); | |
110 + exit(1); | |
111 + case XK_Return: /* fallthrough */ | |
112 + case XK_KP_Enter: break; | |
113 + default: return; | |
114 + } | |
115 + } | |
116 + | |
117 + switch(ksym) { | |
118 + /* movement */ | |
119 + case XK_0: | |
120 + cursor = 0; | |
121 + break; | |
122 + case XK_dollar: | |
123 + if (text[cursor + 1] != '\0') { | |
124 + cursor = strlen(text) - 1; | |
125 + break; | |
126 + } | |
127 + break; | |
128 + case XK_b: | |
129 + movewordedge(-1); | |
130 + break; | |
131 + case XK_e: | |
132 + cursor = nextrune(+1); | |
133 + movewordedge(+1); | |
134 + if (text[cursor] == '\0') | |
135 + --cursor; | |
136 + else | |
137 + cursor = nextrune(-1); | |
138 + break; | |
139 + case XK_g: | |
140 + if (sel == matches) { | |
141 + break; | |
142 + } | |
143 + sel = curr = matches; | |
144 + calcoffsets(); | |
145 + break; | |
146 + case XK_G: | |
147 + if (next) { | |
148 + /* jump to end of list and position items in re… | |
149 + curr = matchend; | |
150 + calcoffsets(); | |
151 + curr = prev; | |
152 + calcoffsets(); | |
153 + while (next && (curr = curr->right)) | |
154 + calcoffsets(); | |
155 + } | |
156 + sel = matchend; | |
157 + break; | |
158 + case XK_h: | |
159 + if (cursor) | |
160 + cursor = nextrune(-1); | |
161 + break; | |
162 + case XK_j: | |
163 + if (sel && sel->right && (sel = sel->right) == next) { | |
164 + curr = next; | |
165 + calcoffsets(); | |
166 + } | |
167 + break; | |
168 + case XK_k: | |
169 + if (sel && sel->left && (sel = sel->left)->right == cur… | |
170 + curr = prev; | |
171 + calcoffsets(); | |
172 + } | |
173 + break; | |
174 + case XK_l: | |
175 + if (text[cursor] != '\0' && text[cursor + 1] != '\0') | |
176 + cursor = nextrune(+1); | |
177 + else if (text[cursor] == '\0' && cursor) | |
178 + --cursor; | |
179 + break; | |
180 + case XK_w: | |
181 + movewordedge(+1); | |
182 + if (text[cursor] != '\0' && text[cursor + 1] != '\0') | |
183 + cursor = nextrune(+1); | |
184 + else if (cursor) | |
185 + --cursor; | |
186 + break; | |
187 + /* insertion */ | |
188 + case XK_a: | |
189 + cursor = nextrune(+1); | |
190 + /* fallthrough */ | |
191 + case XK_i: | |
192 + using_vi_mode = 0; | |
193 + break; | |
194 + case XK_A: | |
195 + if (text[cursor] != '\0') | |
196 + cursor = strlen(text); | |
197 + using_vi_mode = 0; | |
198 + break; | |
199 + case XK_I: | |
200 + cursor = using_vi_mode = 0; | |
201 + break; | |
202 + case XK_p: | |
203 + if (text[cursor] != '\0') | |
204 + cursor = nextrune(+1); | |
205 + XConvertSelection(dpy, (ev->state & ControlMask) ? clip… | |
206 + utf8, utf8, win… | |
207 + return; | |
208 + case XK_P: | |
209 + XConvertSelection(dpy, (ev->state & ControlMask) ? clip… | |
210 + utf8, utf8, win… | |
211 + return; | |
212 + /* deletion */ | |
213 + case XK_D: | |
214 + text[cursor] = '\0'; | |
215 + if (cursor) | |
216 + cursor = nextrune(-1); | |
217 + match(); | |
218 + break; | |
219 + case XK_x: | |
220 + cursor = nextrune(+1); | |
221 + insert(NULL, nextrune(-1) - cursor); | |
222 + if (text[cursor] == '\0' && text[0] != '\0') | |
223 + --cursor; | |
224 + match(); | |
225 + break; | |
226 + /* misc. */ | |
227 + case XK_Return: | |
228 + case XK_KP_Enter: | |
229 + puts((sel && !(ev->state & ShiftMask)) ? sel->text : te… | |
230 + if (!(ev->state & ControlMask)) { | |
231 + cleanup(); | |
232 + exit(0); | |
233 + } | |
234 + if (sel) | |
235 + sel->out = 1; | |
236 + break; | |
237 + case XK_Tab: | |
238 + if (!sel) | |
239 + return; | |
240 + strncpy(text, sel->text, sizeof text - 1); | |
241 + text[sizeof text - 1] = '\0'; | |
242 + cursor = strlen(text) - 1; | |
243 + match(); | |
244 + break; | |
245 + default: | |
246 + for (size_t i = 0; i < quit_len; ++i) | |
247 + if (quit_keys[i].ksym == ksym && | |
248 + (quit_keys[i].state & ev->state) == qui… | |
249 + cleanup(); | |
250 + exit(1); | |
251 + } | |
252 + } | |
253 + | |
254 +draw: | |
255 + drawmenu(); | |
256 +} | |
257 + | |
258 static void | |
259 keypress(XKeyEvent *ev) | |
260 { | |
261 @@ -340,6 +529,18 @@ keypress(XKeyEvent *ev) | |
262 break; | |
263 } | |
264 | |
265 + if (using_vi_mode) { | |
266 + vi_keypress(ksym, ev); | |
267 + return; | |
268 + } else if (vi_mode && | |
269 + (ksym == global_esc.ksym && | |
270 + (ev->state & global_esc.state) == globa… | |
271 + using_vi_mode = 1; | |
272 + if (cursor) | |
273 + cursor = nextrune(-1); | |
274 + goto draw; | |
275 + } | |
276 + | |
277 if (ev->state & ControlMask) { | |
278 switch(ksym) { | |
279 case XK_a: ksym = XK_Home; break; | |
280 @@ -543,6 +744,8 @@ paste(void) | |
281 insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strl… | |
282 XFree(p); | |
283 } | |
284 + if (using_vi_mode && text[cursor] == '\0') | |
285 + --cursor; | |
286 drawmenu(); | |
287 } | |
288 | |
289 @@ -738,6 +941,11 @@ main(int argc, char *argv[]) | |
290 else if (!strcmp(argv[i], "-i")) { /* case-insensitive … | |
291 fstrncmp = strncasecmp; | |
292 fstrstr = cistrstr; | |
293 + } else if (!strcmp(argv[i], "-vi")) { | |
294 + vi_mode = 1; | |
295 + using_vi_mode = start_mode; | |
296 + global_esc.ksym = XK_Escape; | |
297 + global_esc.state = 0; | |
298 } else if (i + 1 == argc) | |
299 usage(); | |
300 /* these options take one argument */ |