dmenu-vi_mode-20230416-0fe460d.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) 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 lines */
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 use when -vi is passed. 0 = insert mode, 1 = normal mode */
24 +static Key global_esc = { XK_n, Mod1Mask }; /* escape key when vi mode is not enabled explicitly */
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 }; /* color schemes */
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, 0, vi_char, 0);
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 reverse */
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 == curr) {
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 : XA_PRIMARY,
206 + utf8, utf8, win, CurrentTime);
207 + return;
208 + case XK_P:
209 + XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
210 + utf8, utf8, win, CurrentTime);
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 : text);
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) == quit_keys[i].state) {
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) == global_esc.state)) {
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)strlen(p));
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 item matching */
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 */