1 /*-
2 * Copyright (c) 2002 Jordan DeLong
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of contributors may be
14 * used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 #include "editor.h"
30
31 /* flag set to cause input_loop to exit */
32 int input_exitloop = 0;
33
34 /* last keysym the user sent */
35 int input_lastkeysym = KEYSYM_NOP;
36
37 /*
38 * draws can be protected by incrementing this variable. to allow
39 * different modules or subsystems (like macro) to concurrently
40 * disable drawing, this variable should only be incremented and
41 * decremented; not explicitly set to 1 or 0.
42 */
43 int input_nodraw = 0;
44
45 /* this should eventually be moved to screen.c */
46 static struct termios save_termios;
47
48 /*
49 * initalize all default keysym definitions
50 */
input_init()51 void input_init() {
52 if (tcgetattr(STDIN_FILENO, &save_termios) == -1)
53 err(1, "tcgetattr");
54
55 /* basic keysyms */
56 keybind_define(KEYSYM_UP, "<up>");
57 keybind_define(KEYSYM_DOWN, "<down>");
58 keybind_define(KEYSYM_LEFT, "<left>");
59 keybind_define(KEYSYM_RIGHT, "<right>");
60 keybind_define(KEYSYM_PGUP, "<ppage>");
61 keybind_define(KEYSYM_PGDN, "<npage>");
62 keybind_define(KEYSYM_PGUP, "<previous>");
63 keybind_define(KEYSYM_PGDN, "<next>");
64 keybind_define(KEYSYM_HOME, "<home>");
65 keybind_define(KEYSYM_END, "<end>");
66 keybind_define(KEYSYM_BACKSPACE, "<backspace>");
67 keybind_define(KEYSYM_DELETE, "<dc>");
68 keybind_define(KEYSYM_ENTER, "<enter>");
69
70 /* main keys */
71 keybind_define(KEYSYM_QUIT, "\\^X\\^C");
72 keybind_define(KEYSYM_SUSPEND, "\\^Z");
73 keybind_define(KEYSYM_CANCEL, "\\^C");
74 keybind_define(KEYSYM_CANCEL, "\\^G");
75 keybind_define(KEYSYM_EXCMD, "\\33x");
76 keybind_define(KEYSYM_SWITCHBUFF, "\\^Xb");
77 keybind_define(KEYSYM_SETOPT, "\\33oo");
78 keybind_define(KEYSYM_OPENFILE, "\\^X\\^F");
79 keybind_define(KEYSYM_SAVE, "\\^X\\^S");
80 keybind_define(KEYSYM_SAVEAS, "\\^X\\^W");
81 keybind_define(KEYSYM_REDRAW, "\\33l");
82 keybind_define(KEYSYM_QUOTENEXT, "\\33\\\\");
83 keybind_define(KEYSYM_AGAIN, "\\^\\");
84 keybind_define(KEYSYM_VERTSPLIT, "\\^_");
85 keybind_define(KEYSYM_HORIZSPLIT, "\\33-");
86 keybind_define(KEYSYM_VERTSPLIT, "\\^X2");
87 keybind_define(KEYSYM_HORIZSPLIT, "\\^X3");
88 keybind_define(KEYSYM_RMVIEW, "\\^X0");
89 keybind_define(KEYSYM_RMOTHERVIEWS, "\\^X1");
90 keybind_define(KEYSYM_VIEWCYCLE, "\\33v");
91 keybind_define(KEYSYM_VIEWCYCLE, "\\^Xo");
92 keybind_define(KEYSYM_PUSHVIEW, "\\0330");
93 keybind_define(KEYSYM_POPVIEW, "\\339");
94 keybind_define(KEYSYM_VERTGROW, "\\33.");
95 keybind_define(KEYSYM_VERTSHRINK, "\\33,");
96 keybind_define(KEYSYM_HORIZGROW, "\\33>");
97 keybind_define(KEYSYM_HORIZSHRINK, "\\33<");
98 keybind_define(KEYSYM_FRAMECYCLE, "\\33=");
99 keybind_define(KEYSYM_NEWFRAME, "\\^X52");
100 keybind_define(KEYSYM_NEWFRAME, "\\33+");
101 keybind_define(KEYSYM_RMFRAME, "\\^X=");
102 keybind_define(KEYSYM_RMFRAME, "\\^X50");
103
104 /* keyboard macro keys */
105 keybind_define(KEYSYM_RECORD, "\\33r");
106 keybind_define(KEYSYM_PLAYBACK, "\\33p");
107 keybind_define(KEYSYM_REPEAT, "\\33g");
108
109 /* vdefault keys */
110 keybind_define(KEYSYM_HOME, "\\^A");
111 keybind_define(KEYSYM_END, "\\^E");
112 keybind_define(KEYSYM_PGUP, "\\^F");
113 keybind_define(KEYSYM_PGDN, "\\^V");
114 keybind_define(KEYSYM_OPENLINE, "\\^O");
115 keybind_define(KEYSYM_TOGNLSTYLE, "\\33on");
116 keybind_define(KEYSYM_UNDO, "\\^U");
117 keybind_define(KEYSYM_REDO, "\\33u");
118 keybind_define(KEYSYM_NEXTWORD, "\\33f");
119 keybind_define(KEYSYM_PREVWORD, "\\33b");
120 keybind_define(KEYSYM_NEXTPARA, "\\33}");
121 keybind_define(KEYSYM_PREVPARA, "\\33{");
122 keybind_define(KEYSYM_VIEWTOP, "\\33\\^F");
123 keybind_define(KEYSYM_VIEWBOTTOM, "\\33\\^V");
124 keybind_define(KEYSYM_LINEUP, "\\^Q");
125 keybind_define(KEYSYM_LINEDN, "\\^D");
126 keybind_define(KEYSYM_FIRSTLINE, "\\33\\^A");
127 keybind_define(KEYSYM_LASTLINE, "\\33\\^E");
128 keybind_define(KEYSYM_HALFPGUP, "\\33(");
129 keybind_define(KEYSYM_HALFPGDN, "\\33)");
130 keybind_define(KEYSYM_CENTER, "\\^L");
131 keybind_define(KEYSYM_TRANCHARS, "\\^T");
132 keybind_define(KEYSYM_TRANWORDS, "\\33t");
133 keybind_define(KEYSYM_TRANLINES, "\\^X\\^T");
134 keybind_define(KEYSYM_SETMARK, "\\000");
135 keybind_define(KEYSYM_POPMARK, "\\33 ");
136 keybind_define(KEYSYM_PROMOTEMARK, "\\^X ");
137 keybind_define(KEYSYM_DEMOTEMARK, "\\33@");
138 keybind_define(KEYSYM_XCHGMARK, "\\^X\\^X");
139 keybind_define(KEYSYM_KILLLINE, "\\^K");
140 keybind_define(KEYSYM_KILLEOL, "\\33k");
141 keybind_define(KEYSYM_KILLWORD, "\\33\b");
142 keybind_define(KEYSYM_KILLWORD, "\\^W");
143 keybind_define(KEYSYM_FWDKILLWORD, "\\33d");
144 keybind_define(KEYSYM_APPENDKILL, "\\33\\^W");
145 keybind_define(KEYSYM_YANK, "\\^Y");
146 keybind_define(KEYSYM_YANKPOP, "\\33y");
147 keybind_define(KEYSYM_FWDYANKPOP, "\\33\\^Y");
148 keybind_define(KEYSYM_ISEARCH, "\\^S");
149 keybind_define(KEYSYM_RISEARCH, "\\^R");
150 keybind_define(KEYSYM_REPLACE, "\\33%");
151 keybind_define(KEYSYM_GOTOLN, "\\^]");
152 keybind_define(KEYSYM_FILTER, "\\33|");
153 keybind_define(KEYSYM_INSERTFILE, "\\^Xi");
154 }
155
156 /*
157 * allocate a range of keysym identifiers; used by views so that
158 * potential keysym conflicts are prevented.
159 */
input_allocsyms(int count)160 int input_allocsyms(int count) {
161 static int nextblk = KEYSYM_FIRSTVIEWSYM;
162 int ret;
163
164 ret = nextblk;
165 nextblk += count;
166
167 return ret;
168 }
169
170 /* get a key and handle resizes and macro playback */
input_getkey()171 int input_getkey() {
172 int was_repeating;
173 int c;
174
175 was_repeating = macro_repeating;
176 for (;;) {
177 /*
178 * get a key, either from the macro recorder/repeater,
179 * or from the tty
180 */
181 if (macro_playing || macro_repeating)
182 c = macro_getkey();
183 else if (macro_repeatplay) {
184 c = KEYSYM_PLAYBACK;
185 if (--macro_repeatplay == 0)
186 input_nodraw--;
187 } else
188 c = keybind_getkey();
189
190 /*
191 * handle window resizing; return a KEYSYM_NOP so
192 * the size changes get redrawn.
193 */
194 if (c == KEYSYM_RESIZE) {
195 frame_resize(screen_width, screen_height);
196 minibuff_resize(screen_width, screen_height);
197 return KEYSYM_NOP;
198 }
199
200 /*
201 * handle certain keys from here, so that they work
202 * anywhere.
203 */
204 switch (c) {
205 case KEYSYM_QUOTENEXT:
206 input_lastkeysym = KEYSYM_QUOTENEXT;
207 screen_nokeysyms(1);
208 c = input_getkey();
209 screen_nokeysyms(0);
210 break;
211 case KEYSYM_REDRAW:
212 screen_redraw();
213 continue;
214 }
215
216 /*
217 * record the key if the macro recorder is
218 * recording, repeated keys aren't recorded.
219 */
220 if (macro_recording && !was_repeating)
221 return macro_recordkey(c);
222 else
223 return c;
224 }
225 }
226
227 /*
228 * check for pending input and take into account macro playback and
229 * the key repeater. block for up to m millis.
230 */
input_key_pending(int m)231 int input_key_pending(int m) {
232 if (macro_playing || macro_repeating)
233 return 1;
234 return screen_key_pending(m);
235 }
236
237 /*
238 * determine if a key is a control (non printable) character
239 * or not.
240 */
input_isctrl(int keysym)241 int input_isctrl(int keysym) {
242 if (keysym >= SCREEN_FIRSTSYM)
243 return 1;
244 if ((u_char) keysym == '\t')
245 return 0;
246 if (!isprint((u_char) keysym))
247 return 1;
248 return 0;
249 }
250
251 /*
252 * default keysym handling; vdefault drops keysyms to this
253 * handler when they are app-level keysyms (save, quit, and
254 * so forth).
255 */
input_default_handler(viewhdr_t * view,int keysym)256 void input_default_handler(viewhdr_t *view, int keysym) {
257 command_t *command;
258
259 switch (keysym) {
260 case KEYSYM_QUIT:
261 input_exitloop = 1;
262 break;
263 case KEYSYM_SUSPEND:
264 screen_suspend();
265 kill(getpid(), SIGTSTP);
266 screen_reinit();
267 break;
268 case KEYSYM_HASHSTATS: command_hashstats(); break;
269 case KEYSYM_SAVEBUFFERS: command_savebuffers(); break;
270 case KEYSYM_SWITCHBUFF: command_switchbuff(); break;
271 case KEYSYM_SETOPT: command_setopt(); break;
272 case KEYSYM_OPENFILE: command_openfile(); break;
273 case KEYSYM_SAVE: command_save(); break;
274 case KEYSYM_SAVEAS: command_saveas(); break;
275 case KEYSYM_VIEWCYCLE: command_viewcycle(); break;
276 case KEYSYM_PUSHVIEW: command_pushview(); break;
277 case KEYSYM_POPVIEW: command_popview(); break;
278 case KEYSYM_VERTSPLIT: command_vertsplit(); break;
279 case KEYSYM_HORIZSPLIT: command_horizsplit(); break;
280 case KEYSYM_RMVIEW: command_rmview(); break;
281 case KEYSYM_RMOTHERVIEWS: command_rmotherviews(); break;
282 case KEYSYM_VERTGROW: command_vertgrow(); break;
283 case KEYSYM_VERTSHRINK: command_vertshrink(); break;
284 case KEYSYM_HORIZGROW: command_horizgrow(); break;
285 case KEYSYM_HORIZSHRINK: command_horizshrink(); break;
286 case KEYSYM_FRAMECYCLE: command_framecycle(); break;
287 case KEYSYM_NEWFRAME: command_newframe(); break;
288 case KEYSYM_RMFRAME: command_rmframe(); break;
289
290 case KEYSYM_RECORD: macro_record(); break;
291 case KEYSYM_PLAYBACK: macro_playback(); break;
292 case KEYSYM_REPEAT: macro_repeat(); break;
293
294 default:
295 /*
296 * try a function-pointer-based command (these are usually
297 * from a module).
298 */
299 command = command_getcmd(keysym);
300 if (command && command->type == COMMAND_FUNC) {
301 command->handler(command);
302 break;
303 }
304
305 /*
306 * express our displeasure
307 */
308 screen_bell();
309 }
310 }
311
312
313 /*
314 * give the active view control over input; if we're
315 * editing in the minibuffer, send it to the minibuffer
316 * handler instead.
317 */
input_dispatch(int keysym)318 void input_dispatch(int keysym) {
319 if (minibuff_editing)
320 minibuff_input(keysym);
321 else
322 VIEWCALL(view_current, input, keysym);
323 input_lastkeysym = keysym;
324 }
325
326 /* main input reception loop */
input_loop()327 void input_loop() {
328 int c;
329
330 /*
331 * loop getting keys from the tty and handing them off to
332 * the view's input handler. drawing is done unless we
333 * are executing a macro or repeating a key.
334 */
335 input_exitloop = 0;
336 while (!input_exitloop) {
337 if (!input_nodraw) {
338 draw_screen();
339 screen_refresh();
340
341 /* remove buffer dirty flags */
342 buffer_cleanse();
343 }
344
345 /* get a key to send to the view's input handler */
346 c = input_getkey();
347 while (c == KEYSYM_EXCMD)
348 c = command_excmd();
349 switch (c) {
350 case KEYSYM_AGAIN:
351 c = input_lastkeysym;
352 break;
353 case KEYSYM_NOP:
354 continue;
355 }
356
357 input_dispatch(c);
358 }
359 }
360
361 /*
362 * to make cancel handling allow cancels when inside a OS call that
363 * blocks, we use the tty's support for a key that will generate
364 * SIGINT.
365 */
366 static volatile int gotsigint = 0;
inthdlr(int sig)367 static void inthdlr(int sig) {
368 gotsigint = 1;
369 }
370
371 /* cancellation variables */
372 static int *cancel_typeahead;
373 static int cancel_len;
374 static int cancel_idx;
375 static int canceled;
376
377 /* begin a cancelable operation */
input_begincancel()378 void input_begincancel() {
379 struct termios tios;
380
381 canceled = cancel_idx = 0;
382 cancel_len = 10;
383 cancel_typeahead = ckmalloc(cancel_len * sizeof(int));
384
385 /*
386 * enable and set the tty's interrupt key to the character specified
387 * by the user's options.
388 */
389 signal(SIGINT, inthdlr);
390 memcpy(&tios, &save_termios, sizeof(struct termios));
391 tios.c_cc[VINTR] = options.intrchar;
392 tios.c_cc[VQUIT] = _POSIX_VDISABLE;
393 tios.c_cc[VSUSP] = _POSIX_VDISABLE;
394 tios.c_lflag |= ISIG;
395 if (tcsetattr(STDIN_FILENO, TCSANOW, &tios) == -1)
396 err(1, "tcsetattr");
397 }
398
399 /* check to see if the operation was canceled */
input_checkcancel()400 int input_checkcancel() {
401 int c;
402
403 if (canceled)
404 return 1;
405 if (gotsigint)
406 goto cancel;
407 while (screen_key_pending(0)) {
408 c = keybind_getkey();
409
410 if (gotsigint)
411 goto cancel;
412 if (c == KEYSYM_CANCEL)
413 goto cancel;
414 cancel_typeahead[cancel_idx++] = c;
415 if (cancel_idx >= cancel_len) {
416 cancel_len += 10;
417 cancel_typeahead = ckrealloc(cancel_typeahead,
418 cancel_len);
419 }
420 }
421
422 return 0;
423
424 cancel:
425 /* trash keys in the tty */
426 while (screen_key_pending(0))
427 screen_getkey();
428 canceled = 1;
429 return 1;
430 }
431
432 /* this should be called at the end of a cancelable operation */
input_endcancel()433 void input_endcancel() {
434 int i;
435
436 if (!canceled) {
437 for (i = cancel_idx - 1; i >= 0; i--)
438 keybind_ungetkey(cancel_typeahead[i]);
439 }
440 signal(SIGINT, SIG_IGN);
441 if (tcsetattr(STDIN_FILENO, TCSANOW, &save_termios) == -1)
442 err(1, "tcsetattr");
443 gotsigint = 0;
444 free(cancel_typeahead);
445 }
446