New functions.
[midnight-commander.git] / src / tty / key.c
blob74b901800e787d466ef945658d28c9bdccd4d7bf
1 /* Keyboard support routines.
3 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007 Free Software Foundation, Inc.
6 Written by: 1994, 1995 Miguel de Icaza.
7 1994, 1995 Janne Kukonlehto.
8 1995 Jakub Jelinek.
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /** \file key.c
26 * \brief Source: keyboard support routines
29 #include <config.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <unistd.h>
40 #include "../../src/global.h"
42 #include "../../src/tty/tty.h"
43 #include "../../src/tty/mouse.h"
44 #include "../../src/tty/key.h"
45 #include "../../src/tty/win.h" /* xterm_flag */
47 #include "../../src/main.h"
48 #include "../../src/layout.h" /* winch_flag */
49 #include "../../src/cons.saver.h"
50 #include "../../src/strutil.h" /* str_casecmp */
52 #ifdef USE_VFS
53 #include "../../vfs/gc.h"
54 #endif
56 #ifdef HAVE_TEXTMODE_X11_SUPPORT
57 # include "../src/x11conn.h"
58 #endif
60 #ifdef __linux__
61 # if defined(__GLIBC__) && (__GLIBC__ < 2)
62 # include <linux/termios.h> /* TIOCLINUX */
63 # elif defined HAVE_TERMIOS_H
64 # include <termios.h>
65 # endif
66 # include <sys/ioctl.h>
67 #endif /* __linux__ */
69 #ifdef __CYGWIN__
70 # include <termios.h>
71 # include <sys/ioctl.h>
72 #endif /* __CYGWIN__ */
74 #ifdef __QNXNTO__
75 # include <dlfcn.h>
76 # include <Ph.h>
77 # include <sys/dcmd_chr.h>
78 #endif
80 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
81 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
82 (t2.tv_usec-t1.tv_usec)/1000)
84 /* Linux console keyboard modifiers */
85 #define SHIFT_PRESSED (1 << 0)
86 #define ALTR_PRESSED (1 << 1)
87 #define CONTROL_PRESSED (1 << 2)
88 #define ALTL_PRESSED (1 << 3)
90 int mou_auto_repeat = 100;
91 int double_click_speed = 250;
92 int old_esc_mode = 0;
93 /* timeout for old_esc_mode in usec */
94 static int keyboard_key_timeout = 1000000; /* settable via env */
96 int use_8th_bit_as_meta = 0;
98 typedef struct key_def {
99 char ch; /* Holds the matching char code */
100 int code; /* The code returned, valid if child == NULL */
101 struct key_def *next;
102 struct key_def *child; /* sequence continuation */
103 int action; /* optional action to be done. Now used only
104 to mark that we are just after the first
105 Escape */
106 } key_def;
108 /* This holds all the key definitions */
109 static key_def *keys = NULL;
111 static int input_fd;
112 static int disabled_channels = 0; /* Disable channels checking */
113 static int xgetch_second (void);
114 static int get_modifier (void);
116 /* File descriptor monitoring add/remove routines */
117 typedef struct SelectList {
118 int fd;
119 select_fn callback;
120 void *info;
121 struct SelectList *next;
122 } SelectList;
124 #ifdef __QNXNTO__
125 typedef int (*ph_dv_f) (void *, void *);
126 typedef int (*ph_ov_f) (void *);
127 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
128 ph_dv_f ph_attach;
129 ph_ov_f ph_input_group;
130 ph_pqc_f ph_query_cursor;
131 #endif
133 static SelectList *select_list = NULL;
135 void add_select_channel (int fd, select_fn callback, void *info)
137 SelectList *new;
139 new = g_new (SelectList, 1);
140 new->fd = fd;
141 new->callback = callback;
142 new->info = info;
143 new->next = select_list;
144 select_list = new;
147 void delete_select_channel (int fd)
149 SelectList *p = select_list;
150 SelectList *p_prev = NULL;
151 SelectList *p_next;
153 while (p) {
154 if (p->fd == fd) {
155 p_next = p->next;
157 if (p_prev)
158 p_prev->next = p_next;
159 else
160 select_list = p_next;
162 g_free (p);
163 p = p_next;
164 continue;
167 p_prev = p;
168 p = p->next;
172 inline static int add_selects (fd_set *select_set)
174 SelectList *p;
175 int top_fd = 0;
177 if (disabled_channels)
178 return 0;
180 for (p = select_list; p; p = p->next) {
181 FD_SET (p->fd, select_set);
182 if (p->fd > top_fd)
183 top_fd = p->fd;
185 return top_fd;
188 static void check_selects (fd_set *select_set)
190 SelectList *p;
191 gboolean retry;
193 if (disabled_channels)
194 return;
196 do {
197 retry = FALSE;
198 for (p = select_list; p; p = p->next)
199 if (FD_ISSET (p->fd, select_set)) {
200 FD_CLR (p->fd, select_set);
201 (*p->callback)(p->fd, p->info);
202 retry = TRUE;
203 break;
205 } while (retry);
208 void channels_up (void)
210 if (!disabled_channels)
211 fputs ("Error: channels_up called with disabled_channels = 0\n",
212 stderr);
213 disabled_channels--;
216 void channels_down (void)
218 disabled_channels++;
221 gboolean
222 is_abort_char (int c)
224 return (c == XCTRL('c') || c == XCTRL('g') || c == ESC_CHAR ||
225 c == KEY_F(10));
229 * Common handler for standard movement keys in a text area. Provided
230 * functions are called with the "data" argument. backfn and forfn also
231 * get an argument indicating how many lines to scroll. Return MSG_HANDLED
232 * if the key was handled, MSG_NOT_HANDLED otherwise.
234 cb_ret_t
235 check_movement_keys (int key, int page_size, void *data, move_fn backfn,
236 move_fn forfn, move_fn topfn, move_fn bottomfn)
238 switch (key) {
239 case KEY_UP:
240 case XCTRL ('p'):
241 (*backfn) (data, 1);
242 break;
244 case KEY_DOWN:
245 case XCTRL ('n'):
246 (*forfn) (data, 1);
247 break;
249 case KEY_PPAGE:
250 case ALT ('v'):
251 (*backfn) (data, page_size - 1);
252 break;
254 case KEY_NPAGE:
255 case XCTRL ('v'):
256 (*forfn) (data, page_size - 1);
257 break;
259 case KEY_HOME:
260 case KEY_M_CTRL | KEY_HOME:
261 case KEY_M_CTRL | KEY_PPAGE:
262 case KEY_A1:
263 case ALT ('<'):
264 (*topfn) (data, 0);
265 break;
267 case KEY_END:
268 case KEY_M_CTRL | KEY_END:
269 case KEY_M_CTRL | KEY_NPAGE:
270 case KEY_C1:
271 case ALT ('>'):
272 (*bottomfn) (data, 0);
273 break;
275 case 'b':
276 case KEY_BACKSPACE:
277 (*backfn) (data, page_size - 1);
278 break;
280 case ' ':
281 (*forfn) (data, page_size - 1);
282 break;
284 case 'u':
285 (*backfn) (data, page_size / 2);
286 break;
288 case 'd':
289 (*forfn) (data, page_size / 2);
290 break;
292 case 'g':
293 (*topfn) (data, 0);
294 break;
296 case 'G':
297 (*bottomfn) (data, 0);
298 break;
300 default:
301 return MSG_NOT_HANDLED;
303 return MSG_HANDLED;
306 typedef const struct {
307 int code;
308 const char *seq;
309 int action;
310 } key_define_t;
312 /* Broken terminfo and termcap databases on xterminals */
313 static key_define_t xterm_key_defines [] = {
314 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
315 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
316 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
317 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
318 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
319 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
320 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
321 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
322 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
323 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
324 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
325 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
326 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
327 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
329 /* old xterm Shift-arrows */
330 { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
331 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
332 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
333 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
335 /* new xterm Shift-arrows */
336 { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
337 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
338 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
339 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
341 /* more xterm keys with modifiers */
342 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
343 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
344 { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
345 { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
346 { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
347 { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
348 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
349 { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
350 { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
351 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
352 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
353 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
354 { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
355 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
356 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
357 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
358 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
359 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
361 /* putty */
362 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION },
363 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION },
364 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION },
365 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION },
367 /* putty alt-arrow keys */
368 /* removed as source esc esc esc trouble */
370 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
371 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
372 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
373 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
374 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
375 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
376 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
377 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
379 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
380 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
381 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
382 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
384 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
385 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
386 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
387 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
389 /* xterm alt-arrow keys */
390 { KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION },
391 { KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION },
392 { KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION },
393 { KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION },
394 { KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION },
395 { KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION },
396 { KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION },
397 { KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION },
398 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION },
399 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION },
400 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION },
401 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION },
402 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION },
403 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION },
404 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION },
405 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION },
407 /* rxvt keys with modifiers */
408 { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
409 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
410 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
411 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
412 { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
413 { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
414 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
415 { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
416 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
417 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
418 { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
419 { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
420 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
421 { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
422 { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
423 { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
424 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
426 /* konsole keys with modifiers */
427 { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
428 { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
430 /* gnome-terminal */
431 { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
432 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
433 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
434 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
435 { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
436 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
437 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
438 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
439 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
440 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
441 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
442 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
444 /* gnome-terminal - application mode */
445 { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
446 { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
447 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
448 { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
449 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
450 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
451 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
452 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
454 /* iTerm */
455 { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION },
456 { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION },
458 /* putty */
459 { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION },
460 { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION },
462 /* keypad keys */
463 { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
464 { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
465 { '/', ESC_STR "Oo", MCKEY_NOACTION },
466 { '\n', ESC_STR "OM", MCKEY_NOACTION },
468 { 0, 0, MCKEY_NOACTION },
471 /* qansi-m terminals have a much more key combinatios,
472 which are undefined in termcap/terminfo */
473 static key_define_t qansi_key_defines[] =
475 /* qansi-m terminal */
476 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
477 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
478 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
479 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
480 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
481 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
482 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
483 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
484 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
485 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
486 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
487 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
488 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
489 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
490 {KEY_M_CTRL | KEY_F(1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
491 {KEY_M_CTRL | KEY_F(2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
492 {KEY_M_CTRL | KEY_F(3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
493 {KEY_M_CTRL | KEY_F(4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
494 {KEY_M_CTRL | KEY_F(5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
495 {KEY_M_CTRL | KEY_F(6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
496 {KEY_M_CTRL | KEY_F(7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
497 {KEY_M_CTRL | KEY_F(8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
498 {KEY_M_CTRL | KEY_F(9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
499 {KEY_M_CTRL | KEY_F(10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
500 {KEY_M_CTRL | KEY_F(11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
501 {KEY_M_CTRL | KEY_F(12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
502 {KEY_M_ALT | KEY_F(1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
503 {KEY_M_ALT | KEY_F(2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
504 {KEY_M_ALT | KEY_F(3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
505 {KEY_M_ALT | KEY_F(4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
506 {KEY_M_ALT | KEY_F(5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
507 {KEY_M_ALT | KEY_F(6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
508 {KEY_M_ALT | KEY_F(7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
509 {KEY_M_ALT | KEY_F(8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
510 {KEY_M_ALT | KEY_F(9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
511 {KEY_M_ALT | KEY_F(10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
512 {KEY_M_ALT | KEY_F(11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
513 {KEY_M_ALT | KEY_F(12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
514 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
515 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
516 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
517 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
518 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
519 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
520 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
521 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
522 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
523 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
524 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
525 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
526 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
527 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
528 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
529 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-r */
530 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
531 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
532 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
533 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
534 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
535 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
536 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
537 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
538 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
539 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
540 {0, NULL, MCKEY_NOACTION},
543 static key_define_t mc_default_keys [] = {
544 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
545 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
546 { 0, NULL, MCKEY_NOACTION },
550 /* This table is a mapping between names and the constants we use
551 * We use this to allow users to define alternate definitions for
552 * certain keys that may be missing from the terminal database
554 key_code_name_t key_name_conv_tab [] = {
555 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
556 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
557 { KEY_F(1), "f1", N_("Function key 1") },
558 { KEY_F(2), "f2", N_("Function key 2") },
559 { KEY_F(3), "f3", N_("Function key 3") },
560 { KEY_F(4), "f4", N_("Function key 4") },
561 { KEY_F(5), "f5", N_("Function key 5") },
562 { KEY_F(6), "f6", N_("Function key 6") },
563 { KEY_F(7), "f7", N_("Function key 7") },
564 { KEY_F(8), "f8", N_("Function key 8") },
565 { KEY_F(9), "f9", N_("Function key 9") },
566 { KEY_F(10), "f10", N_("Function key 10") },
567 { KEY_F(11), "f11", N_("Function key 11") },
568 { KEY_F(12), "f12", N_("Function key 12") },
569 { KEY_F(13), "f13", N_("Function key 13") },
570 { KEY_F(14), "f14", N_("Function key 14") },
571 { KEY_F(15), "f15", N_("Function key 15") },
572 { KEY_F(16), "f16", N_("Function key 16") },
573 { KEY_F(17), "f17", N_("Function key 17") },
574 { KEY_F(18), "f18", N_("Function key 18") },
575 { KEY_F(19), "f19", N_("Function key 19") },
576 { KEY_F(20), "f20", N_("Function key 20") },
577 { KEY_BACKSPACE, "bs", N_("Backspace key") },
578 { KEY_END, "end", N_("End key") },
579 { KEY_UP, "up", N_("Up arrow key") },
580 { KEY_DOWN, "down", N_("Down arrow key") },
581 { KEY_LEFT, "left", N_("Left arrow key") },
582 { KEY_RIGHT, "right", N_("Right arrow key") },
583 { KEY_HOME, "home", N_("Home key") },
584 { KEY_NPAGE, "pgdn", N_("Page Down key") },
585 { KEY_PPAGE, "pgup", N_("Page Up key") },
586 { KEY_IC, "ins", N_("Insert key") },
587 { KEY_DC, "delete", N_("Delete key") },
588 { ALT('\t'), "complete", N_("Completion/M-tab") },
589 { KEY_KP_ADD, "kpplus", N_("+ on keypad") },
590 { KEY_KP_SUBTRACT,"kpminus", N_("- on keypad") },
591 { KEY_KP_MULTIPLY,"kpasterix", N_("* on keypad") },
593 /* From here on, these won't be shown in Learn keys (no space) */
594 { KEY_LEFT, "kpleft", N_("Left arrow keypad") },
595 { KEY_RIGHT, "kpright", N_("Right arrow keypad") },
596 { KEY_UP, "kpup", N_("Up arrow keypad") },
597 { KEY_DOWN, "kpdown", N_("Down arrow keypad") },
598 { KEY_HOME, "kphome", N_("Home on keypad") },
599 { KEY_END, "kpend", N_("End on keypad") },
600 { KEY_NPAGE, "kpnpage", N_("Page Down keypad") },
601 { KEY_PPAGE, "kpppage", N_("Page Up keypad") },
602 { KEY_IC, "kpinsert", N_("Insert on keypad") },
603 { KEY_DC, "kpdelete", N_("Delete on keypad") },
604 { (int) '\n', "kpenter", N_("Enter on keypad") },
605 { (int) '\n', "enter", N_("Enter on keypad") },
606 { (int) '\t', "tab", N_("Tab on keypad") },
607 { (int) ' ', "space", N_("Space on keypad") },
608 { (int) '/', "kpslash", N_("Slash on keypad") },
609 { (int) '#', "kpnumlock", N_("NumLock on keypad") },
611 /* Alternative label */
612 { KEY_BACKSPACE, "backspace", N_("Backspace key") },
613 { KEY_IC, "insert", N_("Insert key") },
614 { KEY_KP_ADD, "plus", N_("+ on keypad") },
615 { KEY_KP_SUBTRACT,"minus", N_("- on keypad") },
616 { KEY_KP_MULTIPLY,"asterix", N_("* on keypad") },
618 /* meta keys */
619 { KEY_M_CTRL, "control", N_("Ctrl") },
620 { KEY_M_CTRL, "ctrl", N_("Ctrl") },
621 { KEY_M_ALT, "alt", N_("Alt") },
622 { KEY_M_ALT, "ralt", N_("Alt") },
623 { KEY_M_ALT, "meta", N_("Alt") },
624 { KEY_M_SHIFT, "shift", N_("Shift") },
626 { 0, 0, 0 }
630 lookup_keyname (char *keyname)
632 int i;
634 if (keyname[0] == '\0')
635 return 0;
636 if (keyname[1] == '\0')
637 return (int) keyname[0];
639 for (i = 0; key_name_conv_tab [i].code; i++)
640 if (str_casecmp (key_name_conv_tab [i].name, keyname) == 0)
641 return key_name_conv_tab [i].code;
643 return 0;
647 /* Return the code associated with the symbolic name keyname */
649 lookup_key (char *keyname)
651 int k = -1;
652 char **keys;
653 guint keys_count = -1;
654 int key = 0;
655 int i = 0;
657 if (keyname == NULL)
658 return 0;
660 keys = g_strsplit (keyname, " ", -1);
661 keys_count = g_strv_length (keys);
662 for (i = keys_count - 1; i >= 0; i--) {
663 if (keys[i] !=NULL && keys[i][0] != 0) {
664 key = lookup_keyname (keys[i]);
665 if (key & KEY_M_CTRL) {
666 if (k < 256)
667 k = XCTRL(k);
668 else
669 k |= key;
670 } else {
671 if (k == -1)
672 k = key;
673 else
674 k |= key;
678 if (k == -1)
679 return 0;
681 return k;
685 static void
686 define_sequences (key_define_t *kd)
688 int i;
690 for (i = 0; kd[i].code != 0; i++)
691 define_sequence(kd[i].code, kd[i].seq, kd[i].action);
694 #ifdef HAVE_TEXTMODE_X11_SUPPORT
696 static Display *x11_display;
697 static Window x11_window;
699 static void
700 init_key_x11 (void)
702 if (!getenv ("DISPLAY"))
703 return;
705 x11_display = mc_XOpenDisplay (0);
707 if (x11_display)
708 x11_window = DefaultRootWindow (x11_display);
710 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
713 /* This has to be called before init_slang or whatever routine
714 calls any define_sequence */
715 void
716 init_key (void)
718 const char *term = getenv ("TERM");
719 char *kt = getenv ("KEYBOARD_KEY_TIMEOUT_US");
720 if (kt != NULL)
721 keyboard_key_timeout = atoi (kt);
723 /* This has to be the first define_sequence */
724 /* So, we can assume that the first keys member has ESC */
725 define_sequences (mc_default_keys);
727 /* Terminfo on irix does not have some keys */
728 if (xterm_flag
729 || (term != NULL
730 && (strncmp (term, "iris-ansi", 9) == 0
731 || strncmp (term, "xterm", 5) == 0
732 || strncmp (term, "rxvt", 4) == 0
733 || strcmp (term, "screen") == 0)))
734 define_sequences (xterm_key_defines);
736 /* load some additional keys (e.g. direct Alt-? support) */
737 load_xtra_key_defines ();
739 #ifdef __QNX__
740 if (term && strncmp (term, "qnx", 3) == 0) {
741 /* Modify the default value of use_8th_bit_as_meta: we would
742 * like to provide a working mc for a newbie who knows nothing
743 * about [Options|Display bits|Full 8 bits input]...
745 * Don't use 'meta'-bit, when we are dealing with a
746 * 'qnx*'-type terminal: clear the default value!
747 * These terminal types use 0xFF as an escape character,
748 * so use_8th_bit_as_meta==1 must not be enabled!
750 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
751 * is not used now (doesn't even depend on use_8th_bit_as_meta
752 * as in mc-3.1.2)...GREAT!...no additional code is required!]
754 use_8th_bit_as_meta = 0;
756 #endif /* __QNX__ */
758 #ifdef HAVE_TEXTMODE_X11_SUPPORT
759 init_key_x11 ();
760 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
762 /* Load the qansi-m key definitions
763 if we are running under the qansi-m terminal */
764 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0)) {
765 define_sequences (qansi_key_defines);
769 /* This has to be called after SLang_init_tty/slint_init */
770 void init_key_input_fd (void)
772 #ifdef HAVE_SLANG
773 input_fd = SLang_TT_Read_FD;
774 #endif
778 static void
779 xmouse_get_event (Gpm_Event *ev)
781 int btn;
782 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
783 static struct timeval tv2;
784 static int clicks;
785 static int last_btn = 0;
787 /* Decode Xterm mouse information to a GPM style event */
789 /* Variable btn has following meaning: */
790 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
791 btn = getch () - 32;
793 /* There seems to be no way of knowing which button was released */
794 /* So we assume all the buttons were released */
796 if (btn == 3) {
797 if (last_btn) {
798 ev->type = GPM_UP | (GPM_SINGLE << clicks);
799 ev->buttons = 0;
800 last_btn = 0;
801 GET_TIME (tv1);
802 clicks = 0;
803 } else {
804 /* Bogus event, maybe mouse wheel */
805 ev->type = 0;
807 } else {
808 if (btn >= 32 && btn <= 34) {
809 btn -= 32;
810 ev->type = GPM_DRAG;
811 } else
812 ev->type = GPM_DOWN;
814 GET_TIME (tv2);
815 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)) {
816 clicks++;
817 clicks %= 3;
818 } else
819 clicks = 0;
821 switch (btn) {
822 case 0:
823 ev->buttons = GPM_B_LEFT;
824 break;
825 case 1:
826 ev->buttons = GPM_B_MIDDLE;
827 break;
828 case 2:
829 ev->buttons = GPM_B_RIGHT;
830 break;
831 case 64:
832 ev->buttons = GPM_B_UP;
833 clicks = 0;
834 break;
835 case 65:
836 ev->buttons = GPM_B_DOWN;
837 clicks = 0;
838 break;
839 default:
840 /* Nothing */
841 ev->type = 0;
842 ev->buttons = 0;
843 break;
845 last_btn = ev->buttons;
847 /* Coordinates are 33-based */
848 /* Transform them to 1-based */
849 ev->x = getch () - 32;
850 ev->y = getch () - 32;
853 static key_def *create_sequence (const char *seq, int code, int action)
855 key_def *base, *p, *attach;
857 for (base = attach = NULL; *seq; seq++) {
858 p = g_new (key_def, 1);
859 if (!base) base = p;
860 if (attach) attach->child = p;
862 p->ch = *seq;
863 p->code = code;
864 p->child = p->next = NULL;
865 if (!seq[1])
866 p->action = action;
867 else
868 p->action = MCKEY_NOACTION;
869 attach = p;
871 return base;
874 /* The maximum sequence length (32 + null terminator) */
875 #define SEQ_BUFFER_LEN 33
876 static int seq_buffer [SEQ_BUFFER_LEN];
877 static int *seq_append = 0;
879 static int push_char (int c)
881 if (!seq_append)
882 seq_append = seq_buffer;
884 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
885 return 0;
886 *(seq_append++) = c;
887 *seq_append = 0;
888 return 1;
892 * Return 1 on success, 0 on error.
893 * An error happens if SEQ is a beginning of an existing longer sequence.
895 int define_sequence (int code, const char *seq, int action)
897 key_def *base;
899 if (strlen (seq) > SEQ_BUFFER_LEN-1)
900 return 0;
902 for (base = keys; (base != 0) && *seq; ) {
903 if (*seq == base->ch) {
904 if (base->child == 0) {
905 if (*(seq+1)) {
906 base->child = create_sequence (seq+1, code, action);
907 return 1;
908 } else {
909 /* The sequence matches an existing one. */
910 base->code = code;
911 base->action = action;
912 return 1;
914 } else {
915 base = base->child;
916 seq++;
918 } else {
919 if (base->next)
920 base = base->next;
921 else {
922 base->next = create_sequence (seq, code, action);
923 return 1;
928 if (!*seq) {
929 /* Attempt to redefine a sequence with a shorter sequence. */
930 return 0;
933 keys = create_sequence (seq, code, action);
934 return 1;
937 static int *pending_keys;
939 /* Apply corrections for the keycode generated in get_key_code() */
940 static int
941 correct_key_code (int code)
943 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
944 unsigned int mod = code & KEY_M_MASK; /* modifier */
945 #ifdef __QNXNTO__
946 unsigned int qmod; /* bunch of the QNX console
947 modifiers needs unchanged */
948 #endif /* __QNXNTO__ */
951 * Add key modifiers directly from X11 or OS.
952 * Ordinary characters only get modifiers from sequences.
954 if (c < 32 || c >= 256) {
955 mod |= get_modifier ();
958 /* This is needed if the newline is reported as carriage return */
959 if (c == '\r')
960 c = '\n';
962 /* This is reported to be useful on AIX */
963 if (c == KEY_SCANCEL)
964 c = '\t';
966 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
967 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
968 c = KEY_BTAB;
969 mod = 0;
972 /* F0 is the same as F10 for out purposes */
973 if (c == KEY_F (0))
974 c = KEY_F (10);
977 * We are not interested if Ctrl was pressed when entering control
978 * characters, so assume that it was. When checking for such keys,
979 * XCTRL macro should be used. In some cases, we are interested,
980 * e.g. to distinguish Ctrl-Enter from Enter.
982 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
983 mod |= KEY_M_CTRL;
986 #ifdef __QNXNTO__
987 qmod=get_modifier();
989 if ((c == 127) && (mod==0)) /* Add Ctrl/Alt/Shift-BackSpace */
991 mod |= get_modifier();
992 c = KEY_BACKSPACE;
995 if ((c=='0') && (mod==0)) /* Add Shift-Insert on key pad */
997 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
999 mod = KEY_M_SHIFT;
1000 c = KEY_IC;
1004 if ((c=='.') && (mod==0)) /* Add Shift-Del on key pad */
1006 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1008 mod = KEY_M_SHIFT;
1009 c = KEY_DC;
1012 #endif /* __QNXNTO__ */
1014 /* Unrecognized 0177 is delete (preserve Ctrl) */
1015 if (c == 0177) {
1016 c = KEY_BACKSPACE;
1019 /* Unrecognized Ctrl-d is delete */
1020 if (c == (31 & 'd')) {
1021 c = KEY_DC;
1022 mod &= ~KEY_M_CTRL;
1025 /* Unrecognized Ctrl-h is backspace */
1026 if (c == (31 & 'h')) {
1027 c = KEY_BACKSPACE;
1028 mod &= ~KEY_M_CTRL;
1031 /* Shift+BackSpace is backspace */
1032 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
1033 mod &= ~KEY_M_SHIFT;
1036 /* Convert Shift+Fn to F(n+10) */
1037 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
1038 c += 10;
1041 /* Remove Shift information from function keys */
1042 if (c >= KEY_F (1) && c <= KEY_F (20)) {
1043 mod &= ~KEY_M_SHIFT;
1046 if (!alternate_plus_minus)
1047 switch (c) {
1048 case KEY_KP_ADD:
1049 c = '+';
1050 break;
1051 case KEY_KP_SUBTRACT:
1052 c = '-';
1053 break;
1054 case KEY_KP_MULTIPLY:
1055 c = '*';
1056 break;
1059 return (mod | c);
1062 int get_key_code (int no_delay)
1064 int c;
1065 static key_def *this = NULL, *parent;
1066 static struct timeval esctime = { -1, -1 };
1067 static int lastnodelay = -1;
1069 if (no_delay != lastnodelay) {
1070 this = NULL;
1071 lastnodelay = no_delay;
1074 pend_send:
1075 if (pending_keys) {
1076 int d = *pending_keys++;
1077 check_pend:
1078 if (!*pending_keys) {
1079 pending_keys = 0;
1080 seq_append = 0;
1082 if (d == ESC_CHAR && pending_keys) {
1083 d = ALT(*pending_keys++);
1084 goto check_pend;
1086 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
1087 d = ALT(d & 0x7f);
1088 this = NULL;
1089 return correct_key_code (d);
1092 nodelay_try_again:
1093 if (no_delay) {
1094 tty_nodelay (TRUE);
1096 c = getch ();
1097 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1098 if (c == KEY_RESIZE)
1099 goto nodelay_try_again;
1100 #endif
1101 if (no_delay) {
1102 tty_nodelay (FALSE);
1103 if (c == -1) {
1104 if (this != NULL && parent != NULL &&
1105 parent->action == MCKEY_ESCAPE && old_esc_mode) {
1106 struct timeval current, timeout;
1108 if (esctime.tv_sec == -1)
1109 return -1;
1110 GET_TIME (current);
1111 timeout.tv_sec = keyboard_key_timeout / 1000000 + esctime.tv_sec;
1112 timeout.tv_usec = keyboard_key_timeout % 1000000 + esctime.tv_usec;
1113 if (timeout.tv_usec > 1000000) {
1114 timeout.tv_usec -= 1000000;
1115 timeout.tv_sec++;
1117 if (current.tv_sec < timeout.tv_sec)
1118 return -1;
1119 if (current.tv_sec == timeout.tv_sec &&
1120 current.tv_usec < timeout.tv_usec)
1121 return -1;
1122 this = NULL;
1123 pending_keys = seq_append = NULL;
1124 return ESC_CHAR;
1126 return -1;
1128 } else if (c == -1) {
1129 /* Maybe we got an incomplete match.
1130 This we do only in delay mode, since otherwise
1131 getch can return -1 at any time. */
1132 if (seq_append) {
1133 pending_keys = seq_buffer;
1134 goto pend_send;
1136 this = NULL;
1137 return -1;
1140 /* Search the key on the root */
1141 if (!no_delay || this == NULL) {
1142 this = keys;
1143 parent = NULL;
1145 if ((c > 127 && c < 256) && use_8th_bit_as_meta) {
1146 c &= 0x7f;
1148 /* The first sequence defined starts with esc */
1149 parent = keys;
1150 this = keys->child;
1153 while (this) {
1154 if (c == this->ch) {
1155 if (this->child) {
1156 if (!push_char (c)) {
1157 pending_keys = seq_buffer;
1158 goto pend_send;
1160 parent = this;
1161 this = this->child;
1162 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
1163 if (no_delay) {
1164 GET_TIME (esctime);
1165 if (this == NULL) {
1166 /* Shouldn't happen */
1167 fputs ("Internal error\n", stderr);
1168 exit (1);
1170 goto nodelay_try_again;
1172 esctime.tv_sec = -1;
1173 c = xgetch_second ();
1174 if (c == -1) {
1175 pending_keys = seq_append = NULL;
1176 this = NULL;
1177 return ESC_CHAR;
1179 } else {
1180 if (no_delay)
1181 goto nodelay_try_again;
1182 c = getch ();
1184 } else {
1185 /* We got a complete match, return and reset search */
1186 int code;
1188 pending_keys = seq_append = NULL;
1189 code = this->code;
1190 this = NULL;
1191 return correct_key_code (code);
1193 } else {
1194 if (this->next)
1195 this = this->next;
1196 else {
1197 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
1199 /* Convert escape-digits to F-keys */
1200 if (g_ascii_isdigit(c))
1201 c = KEY_F (c - '0');
1202 else if (c == ' ')
1203 c = ESC_CHAR;
1204 else
1205 c = ALT(c);
1207 pending_keys = seq_append = NULL;
1208 this = NULL;
1209 return correct_key_code (c);
1211 /* Did not find a match or {c} was changed in the if above,
1212 so we have to return everything we had skipped
1214 push_char (c);
1215 pending_keys = seq_buffer;
1216 goto pend_send;
1220 this = NULL;
1221 return correct_key_code (c);
1224 /* Return the code associated with the symbolic name keyname */
1225 int lookup_key (char *keyname)
1227 int i;
1229 for (i = 0; key_name_conv_tab [i].code; i++){
1230 if (str_casecmp (key_name_conv_tab [i].name, keyname))
1231 continue;
1232 return key_name_conv_tab [i].code;
1234 return 0;
1236 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
1237 static void
1238 try_channels (int set_timeout)
1240 struct timeval timeout;
1241 static fd_set select_set;
1242 struct timeval *timeptr;
1243 int v;
1244 int maxfdp;
1246 while (1) {
1247 FD_ZERO (&select_set);
1248 FD_SET (input_fd, &select_set); /* Add stdin */
1249 maxfdp = max (add_selects (&select_set), input_fd);
1251 if (set_timeout) {
1252 timeout.tv_sec = 0;
1253 timeout.tv_usec = 100000;
1254 timeptr = &timeout;
1255 } else
1256 timeptr = 0;
1258 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
1259 if (v > 0) {
1260 check_selects (&select_set);
1261 if (FD_ISSET (input_fd, &select_set))
1262 return;
1267 /* Workaround for System V Curses vt100 bug */
1268 static int getch_with_delay (void)
1270 int c;
1272 /* This routine could be used on systems without mouse support,
1273 so we need to do the select check :-( */
1274 while (1) {
1275 if (!pending_keys)
1276 try_channels (0);
1278 /* Try to get a character */
1279 c = get_key_code (0);
1280 if (c != -1)
1281 break;
1282 /* Failed -> wait 0.1 secs and try again */
1283 try_channels (1);
1285 /* Success -> return the character */
1286 return c;
1289 /* Returns a character read from stdin with appropriate interpretation */
1290 /* Also takes care of generated mouse events */
1291 /* Returns EV_MOUSE if it is a mouse event */
1292 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1294 get_event (struct Gpm_Event *event, int redo_event, int block)
1296 int c;
1297 static int flag; /* Return value from select */
1298 #ifdef HAVE_LIBGPM
1299 static struct Gpm_Event ev; /* Mouse event */
1300 #endif
1301 struct timeval timeout;
1302 struct timeval *time_addr = NULL;
1303 static int dirty = 3;
1305 if ((dirty == 3) || is_idle ()) {
1306 tty_refresh ();
1307 doupdate ();
1308 dirty = 1;
1309 } else
1310 dirty++;
1312 vfs_timeout_handler ();
1314 /* Ok, we use (event->x < 0) to signal that the event does not contain
1315 a suitable position for the mouse, so we can't use show_mouse_pointer
1316 on it.
1318 if (event->x > 0) {
1319 show_mouse_pointer (event->x, event->y);
1320 if (!redo_event)
1321 event->x = -1;
1324 /* Repeat if using mouse */
1325 while (mouse_enabled && !pending_keys) {
1326 int maxfdp;
1327 fd_set select_set;
1329 FD_ZERO (&select_set);
1330 FD_SET (input_fd, &select_set);
1331 maxfdp = max (add_selects (&select_set), input_fd);
1333 #ifdef HAVE_LIBGPM
1334 if (use_mouse_p == MOUSE_GPM) {
1335 if (gpm_fd < 0) {
1336 /* Connection to gpm broken, possibly gpm has died */
1337 mouse_enabled = 0;
1338 use_mouse_p = MOUSE_NONE;
1339 break;
1340 } else {
1341 FD_SET (gpm_fd, &select_set);
1342 maxfdp = max (maxfdp, gpm_fd);
1345 #endif
1347 if (redo_event) {
1348 timeout.tv_usec = mou_auto_repeat * 1000;
1349 timeout.tv_sec = 0;
1351 time_addr = &timeout;
1352 } else {
1353 int seconds;
1355 if ((seconds = vfs_timeouts ())) {
1356 /* the timeout could be improved and actually be
1357 * the number of seconds until the next vfs entry
1358 * timeouts in the stamp list.
1361 timeout.tv_sec = seconds;
1362 timeout.tv_usec = 0;
1363 time_addr = &timeout;
1364 } else
1365 time_addr = NULL;
1368 if (!block || winch_flag) {
1369 time_addr = &timeout;
1370 timeout.tv_sec = 0;
1371 timeout.tv_usec = 0;
1373 tty_enable_interrupt_key ();
1374 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1375 tty_disable_interrupt_key ();
1377 /* select timed out: it could be for any of the following reasons:
1378 * redo_event -> it was because of the MOU_REPEAT handler
1379 * !block -> we did not block in the select call
1380 * else -> 10 second timeout to check the vfs status.
1382 if (flag == 0) {
1383 if (redo_event)
1384 return EV_MOUSE;
1385 if (!block || winch_flag)
1386 return EV_NONE;
1387 vfs_timeout_handler ();
1389 if (flag == -1 && errno == EINTR)
1390 return EV_NONE;
1392 check_selects (&select_set);
1394 if (FD_ISSET (input_fd, &select_set))
1395 break;
1396 #ifdef HAVE_LIBGPM
1397 if (use_mouse_p == MOUSE_GPM && gpm_fd > 0
1398 && FD_ISSET (gpm_fd, &select_set)) {
1399 Gpm_GetEvent (&ev);
1400 Gpm_FitEvent (&ev);
1401 *event = ev;
1402 return EV_MOUSE;
1404 #endif /* !HAVE_LIBGPM */
1406 #ifndef HAVE_SLANG
1407 flag = is_wintouched (stdscr);
1408 untouchwin (stdscr);
1409 #endif /* !HAVE_SLANG */
1410 c = block ? getch_with_delay () : get_key_code (1);
1412 #ifndef HAVE_SLANG
1413 if (flag)
1414 tty_touch_screen ();
1415 #endif /* !HAVE_SLANG */
1417 if (c == MCKEY_MOUSE
1418 #ifdef KEY_MOUSE
1419 || c == KEY_MOUSE
1420 #endif /* KEY_MOUSE */
1422 /* Mouse event */
1423 xmouse_get_event (event);
1424 if (event->type)
1425 return EV_MOUSE;
1426 else
1427 return EV_NONE;
1430 return c;
1433 /* Returns a key press, mouse events are discarded */
1434 int mi_getch ()
1436 Gpm_Event ev;
1437 int key;
1439 ev.x = -1;
1440 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
1442 return key;
1445 static int xgetch_second (void)
1447 fd_set Read_FD_Set;
1448 int c;
1449 struct timeval timeout;
1451 timeout.tv_sec = keyboard_key_timeout / 1000000;
1452 timeout.tv_usec = keyboard_key_timeout % 1000000;
1453 tty_nodelay (TRUE);
1454 FD_ZERO (&Read_FD_Set);
1455 FD_SET (input_fd, &Read_FD_Set);
1456 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1457 c = getch ();
1458 tty_nodelay (FALSE);
1459 return c;
1462 static void
1463 learn_store_key (char *buffer, char **p, int c)
1465 if (*p - buffer > 253)
1466 return;
1467 if (c == ESC_CHAR) {
1468 *(*p)++ = '\\';
1469 *(*p)++ = 'e';
1470 } else if (c < ' ') {
1471 *(*p)++ = '^';
1472 *(*p)++ = c + 'a' - 1;
1473 } else if (c == '^') {
1474 *(*p)++ = '^';
1475 *(*p)++ = '^';
1476 } else
1477 *(*p)++ = (char) c;
1480 char *learn_key (void)
1482 /* LEARN_TIMEOUT in usec */
1483 #define LEARN_TIMEOUT 200000
1485 fd_set Read_FD_Set;
1486 struct timeval endtime;
1487 struct timeval timeout;
1488 int c;
1489 char buffer [256];
1490 char *p = buffer;
1492 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
1493 c = getch ();
1494 while (c == -1)
1495 c = getch (); /* Sanity check, should be unnecessary */
1496 learn_store_key (buffer, &p, c);
1497 GET_TIME (endtime);
1498 endtime.tv_usec += LEARN_TIMEOUT;
1499 if (endtime.tv_usec > 1000000) {
1500 endtime.tv_usec -= 1000000;
1501 endtime.tv_sec++;
1503 tty_nodelay (TRUE);
1504 for (;;) {
1505 while ((c = getch ()) == -1) {
1506 GET_TIME (timeout);
1507 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1508 if (timeout.tv_usec < 0)
1509 timeout.tv_sec++;
1510 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1511 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1512 FD_ZERO (&Read_FD_Set);
1513 FD_SET (input_fd, &Read_FD_Set);
1514 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1515 } else
1516 break;
1518 if (c == -1)
1519 break;
1520 learn_store_key (buffer, &p, c);
1522 tty_keypad (TRUE);
1523 tty_nodelay (FALSE);
1524 *p = 0;
1525 return g_strdup (buffer);
1528 /* xterm and linux console only: set keypad to numeric or application
1529 mode. Only in application keypad mode it's possible to distinguish
1530 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1531 void
1532 numeric_keypad_mode (void)
1534 if (console_flag || xterm_flag) {
1535 fputs ("\033>", stdout);
1536 fflush (stdout);
1540 void
1541 application_keypad_mode (void)
1543 if (console_flag || xterm_flag) {
1544 fputs ("\033=", stdout);
1545 fflush (stdout);
1551 * Check if we are idle, i.e. there are no pending keyboard or mouse
1552 * events. Return 1 is idle, 0 is there are pending events.
1555 is_idle (void)
1557 int maxfdp;
1558 fd_set select_set;
1559 struct timeval timeout;
1561 FD_ZERO (&select_set);
1562 FD_SET (input_fd, &select_set);
1563 maxfdp = input_fd;
1564 #ifdef HAVE_LIBGPM
1565 if (use_mouse_p == MOUSE_GPM && mouse_enabled && gpm_fd > 0) {
1566 FD_SET (gpm_fd, &select_set);
1567 maxfdp = max (maxfdp, gpm_fd);
1569 #endif
1570 timeout.tv_sec = 0;
1571 timeout.tv_usec = 0;
1572 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1577 * Get modifier state (shift, alt, ctrl) for the last key pressed.
1578 * We are assuming that the state didn't change since the key press.
1579 * This is only correct if get_modifier() is called very fast after
1580 * the input was received, so that the user didn't release the
1581 * modifier keys yet.
1583 static int
1584 get_modifier (void)
1586 int result = 0;
1587 #ifdef __QNXNTO__
1588 int mod_status, shift_ext_status;
1589 static int in_photon = 0;
1590 static int ph_ig = 0;
1591 PhCursorInfo_t cursor_info;
1592 #endif /* __QNXNTO__ */
1594 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1595 if (x11_window) {
1596 Window root, child;
1597 int root_x, root_y;
1598 int win_x, win_y;
1599 unsigned int mask;
1601 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
1602 &root_y, &win_x, &win_y, &mask);
1604 if (mask & ShiftMask)
1605 result |= KEY_M_SHIFT;
1606 if (mask & ControlMask)
1607 result |= KEY_M_CTRL;
1608 return result;
1610 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1611 #ifdef __QNXNTO__
1613 if (in_photon == 0) {
1614 /* First time here, let's load Photon library and attach
1615 to Photon */
1616 in_photon = -1;
1617 if (getenv ("PHOTON2_PATH") != NULL) {
1618 /* QNX 6.x has no support for RTLD_LAZY */
1619 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
1620 if (ph_handle != NULL) {
1621 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
1622 ph_input_group =
1623 (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
1624 ph_query_cursor =
1625 (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
1626 if ((ph_attach != NULL) && (ph_input_group != NULL)
1627 && (ph_query_cursor != NULL)) {
1628 if ((*ph_attach) (0, 0)) { /* Attached */
1629 ph_ig = (*ph_input_group) (0);
1630 in_photon = 1;
1636 /* We do not have Photon running. Assume we are in text
1637 console or xterm */
1638 if (in_photon == -1) {
1639 if (devctl
1640 (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status,
1641 sizeof (int), NULL) == -1)
1642 return 0;
1643 shift_ext_status = mod_status & 0xffffff00UL;
1644 mod_status &= 0x7f;
1645 if (mod_status & _LINESTATUS_CON_ALT)
1646 result |= KEY_M_ALT;
1647 if (mod_status & _LINESTATUS_CON_CTRL)
1648 result |= KEY_M_CTRL;
1649 if ((mod_status & _LINESTATUS_CON_SHIFT)
1650 || (shift_ext_status & 0x00000800UL))
1651 result |= KEY_M_SHIFT;
1652 } else {
1653 (*ph_query_cursor) (ph_ig, &cursor_info);
1654 if (cursor_info.key_mods & 0x04)
1655 result |= KEY_M_ALT;
1656 if (cursor_info.key_mods & 0x02)
1657 result |= KEY_M_CTRL;
1658 if (cursor_info.key_mods & 0x01)
1659 result |= KEY_M_SHIFT;
1661 #endif /* __QNXNTO__ */
1663 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
1665 unsigned char modifiers = 6;
1667 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
1668 return 0;
1670 /* Translate Linux modifiers into mc modifiers */
1671 if (modifiers & SHIFT_PRESSED)
1672 result |= KEY_M_SHIFT;
1673 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
1674 result |= KEY_M_ALT;
1675 if (modifiers & CONTROL_PRESSED)
1676 result |= KEY_M_CTRL;
1678 #endif /* !__linux__ */
1679 return result;
1682 static void k_dispose (key_def *k)
1684 if (!k)
1685 return;
1686 k_dispose (k->child);
1687 k_dispose (k->next);
1688 g_free (k);
1691 static void s_dispose (SelectList *sel)
1693 if (!sel)
1694 return;
1696 s_dispose (sel->next);
1697 g_free (sel);
1700 void done_key ()
1702 k_dispose (keys);
1703 s_dispose (select_list);
1705 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1706 if (x11_display)
1707 mc_XCloseDisplay (x11_display);
1708 #endif