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