Updated italian translation
[midnight-commander.git] / src / key.c
blob76f0acac3b9cbbea4ff833384144de65cd4a5799
1 /* Keyboard support routines.
3 Copyright (C) 1994,1995 the Free Software Foundation.
5 Written by: 1994, 1995 Miguel de Icaza.
6 1994, 1995 Janne Kukonlehto.
7 1995 Jakub Jelinek.
8 1997 Norbert Warmuth
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <ctype.h>
32 #include <errno.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "mouse.h"
37 #include "key.h"
38 #include "main.h"
39 #include "win.h"
40 #include "cons.saver.h"
42 #ifdef USE_VFS
43 #include "../vfs/gc.h"
44 #endif
46 #ifdef HAVE_TEXTMODE_X11_SUPPORT
47 #ifdef HAVE_GMODULE
48 #include <gmodule.h>
49 #endif /* HAVE_GMODULE */
50 #include <X11/Xlib.h>
51 #endif
53 #ifdef __linux__
54 # if defined(__GLIBC__) && (__GLIBC__ < 2)
55 # include <linux/termios.h> /* TIOCLINUX */
56 # else
57 # include <termios.h>
58 # endif
59 # include <sys/ioctl.h>
60 #endif /* __linux__ */
62 #ifdef __QNXNTO__
63 # include <dlfcn.h>
64 # include <Ph.h>
65 # include <sys/dcmd_chr.h>
66 #endif
68 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
69 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
70 (t2.tv_usec-t1.tv_usec)/1000)
72 /* timeout for old_esc_mode in usec */
73 #define ESCMODE_TIMEOUT 1000000
75 /* Linux console keyboard modifiers */
76 #define SHIFT_PRESSED 1
77 #define ALTR_PRESSED 2
78 #define CONTROL_PRESSED 4
79 #define ALTL_PRESSED 8
82 int mou_auto_repeat = 100;
83 int double_click_speed = 250;
84 int old_esc_mode = 0;
86 int use_8th_bit_as_meta = 1;
88 typedef struct key_def {
89 char ch; /* Holds the matching char code */
90 int code; /* The code returned, valid if child == NULL */
91 struct key_def *next;
92 struct key_def *child; /* sequence continuation */
93 int action; /* optional action to be done. Now used only
94 to mark that we are just after the first
95 Escape */
96 } key_def;
98 /* This holds all the key definitions */
99 static key_def *keys = 0;
101 static int input_fd;
102 static int disabled_channels = 0; /* Disable channels checking */
103 static int xgetch_second (void);
104 static int get_modifier (void);
106 /* File descriptor monitoring add/remove routines */
107 typedef struct SelectList {
108 int fd;
109 select_fn callback;
110 void *info;
111 struct SelectList *next;
112 } SelectList;
114 #ifdef __QNXNTO__
115 typedef int (*ph_dv_f) (void *, void *);
116 typedef int (*ph_ov_f) (void *);
117 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
118 ph_dv_f ph_attach;
119 ph_ov_f ph_input_group;
120 ph_pqc_f ph_query_cursor;
121 #endif
123 static SelectList *select_list = 0;
125 void add_select_channel (int fd, select_fn callback, void *info)
127 SelectList *new;
129 new = g_new (SelectList, 1);
130 new->fd = fd;
131 new->callback = callback;
132 new->info = info;
133 new->next = select_list;
134 select_list = new;
137 void delete_select_channel (int fd)
139 SelectList *p = select_list;
140 SelectList *p_prev = 0;
141 SelectList *p_next;
143 while (p) {
144 if (p->fd == fd) {
145 p_next = p->next;
147 if (p_prev)
148 p_prev->next = p_next;
149 else
150 select_list = p_next;
152 g_free (p);
153 p = p_next;
154 continue;
157 p_prev = p;
158 p = p->next;
162 inline static int add_selects (fd_set *select_set)
164 SelectList *p;
165 int top_fd = 0;
167 if (disabled_channels)
168 return 0;
170 for (p = select_list; p; p = p->next){
171 FD_SET (p->fd, select_set);
172 if (p->fd > top_fd)
173 top_fd = p->fd;
175 return top_fd;
178 static void check_selects (fd_set *select_set)
180 SelectList *p;
182 if (disabled_channels)
183 return;
185 for (p = select_list; p; p = p->next)
186 if (FD_ISSET (p->fd, select_set))
187 (*p->callback)(p->fd, p->info);
190 void channels_down (void)
192 disabled_channels ++;
195 void channels_up (void)
197 if (!disabled_channels)
198 fputs ("Error: channels_up called with disabled_channels = 0\n",
199 stderr);
200 disabled_channels--;
203 typedef const struct {
204 int code;
205 char *seq;
206 int action;
207 } key_define_t;
209 /* Broken terminfo and termcap databases on xterminals */
210 static key_define_t xterm_key_defines [] = {
211 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
212 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
213 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
214 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
215 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
216 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
217 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
218 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
219 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
220 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
221 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
222 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
223 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
224 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
226 /* old xterm Shift-arrows */
227 { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
228 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
229 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
230 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
232 /* new xterm Shift-arrows */
233 { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
234 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
235 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
236 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
238 /* more xterm keys with modifiers */
239 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
240 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
241 { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
242 { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
243 { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
244 { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
245 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
246 { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
247 { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
248 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
249 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
250 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
251 { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
252 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
253 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
254 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
255 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
256 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
258 /* rxvt keys with modifiers */
259 { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
260 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
261 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
262 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
263 { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
264 { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
265 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
266 { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
267 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
268 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
269 { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
270 { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
271 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
272 { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
273 { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
274 { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
275 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
277 /* konsole keys with modifiers */
278 { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
279 { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
281 /* gnome-terminal */
282 { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
283 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
284 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
285 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
286 { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
287 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
288 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
289 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
290 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
291 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
292 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
293 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
295 /* gnome-terminal - application mode */
296 { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
297 { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
298 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
299 { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
300 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
301 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
302 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
303 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
305 /* keypad keys */
306 { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
307 { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
308 { '/', ESC_STR "Oo", MCKEY_NOACTION },
309 { '\n', ESC_STR "OM", MCKEY_NOACTION },
311 { 0, 0, MCKEY_NOACTION },
314 static key_define_t mc_default_keys [] = {
315 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
316 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
317 { 0, 0, MCKEY_NOACTION },
320 static void
321 define_sequences (key_define_t *kd)
323 int i;
325 for (i = 0; kd [i].code; i++)
326 define_sequence(kd [i].code, kd [i].seq, kd [i].action);
329 #ifdef HAVE_TEXTMODE_X11_SUPPORT
331 #ifdef HAVE_GMODULE
332 static int (*func_XCloseDisplay) (Display *);
333 static Bool (*func_XQueryPointer) (Display *, Window, Window *, Window *,
334 int *, int *, int *, int *,
335 unsigned int *);
337 static GModule *x11_module;
338 #endif /* HAVE_GMODULE */
340 static Display *x11_display;
341 static Window x11_window;
343 static void
344 init_key_x11 (void)
346 #ifdef HAVE_GMODULE
347 static Display *(*func_XOpenDisplay) (_Xconst char *);
348 gchar *x11_module_fname;
349 #endif /* HAVE_GMODULE */
351 if (!getenv ("DISPLAY"))
352 return;
354 #ifdef HAVE_GMODULE
355 x11_module_fname = g_module_build_path (NULL, "X11");
357 if (!x11_module_fname)
358 return;
360 x11_module = g_module_open (x11_module_fname, G_MODULE_BIND_LAZY);
361 g_free (x11_module_fname);
363 if (!x11_module)
364 return;
366 if (g_module_symbol
367 (x11_module, "XOpenDisplay", (void *) &func_XOpenDisplay)
368 && g_module_symbol (x11_module, "XCloseDisplay",
369 (void *) &func_XCloseDisplay)
370 && g_module_symbol (x11_module, "XQueryPointer",
371 (void *) &func_XQueryPointer)) {
372 x11_display = (*func_XOpenDisplay) (0);
374 #else
375 x11_display = XOpenDisplay (0);
376 #endif /* HAVE_GMODULE */
378 if (x11_display)
379 x11_window = DefaultRootWindow (x11_display);
381 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
384 /* This has to be called before slang_init or whatever routine
385 calls any define_sequence */
386 void init_key (void)
388 char *term = (char *) getenv ("TERM");
390 /* This has to be the first define_sequence */
391 /* So, we can assume that the first keys member has ESC */
392 define_sequences (mc_default_keys);
394 /* Terminfo on irix does not have some keys */
395 if (term &&
396 ((!strncmp (term, "iris-ansi", 9)) || (!strncmp (term, "xterm", 5))))
397 define_sequences (xterm_key_defines);
399 /* load some additional keys (e.g. direct Alt-? support) */
400 load_xtra_key_defines();
402 #ifdef __QNX__
403 if (term && strncmp (term, "qnx", 3) == 0) {
404 /* Modify the default value of use_8th_bit_as_meta: we would
405 * like to provide a working mc for a newbie who knows nothing
406 * about [Options|Display bits|Full 8 bits input]...
408 * Don't use 'meta'-bit, when we are dealing with a
409 * 'qnx*'-type terminal: clear the default value!
410 * These terminal types use 0xFF as an escape character,
411 * so use_8th_bit_as_meta==1 must not be enabled!
413 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
414 * is not used now (doesn't even depend on use_8th_bit_as_meta
415 * as in mc-3.1.2)...GREAT!...no additional code is required!]
417 use_8th_bit_as_meta = 0;
419 #endif /* __QNX__ */
421 #ifdef HAVE_TEXTMODE_X11_SUPPORT
422 init_key_x11 ();
423 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
426 /* This has to be called after SLang_init_tty/slint_init */
427 void init_key_input_fd (void)
429 #ifdef HAVE_SLANG
430 input_fd = SLang_TT_Read_FD;
431 #endif
435 static void
436 xmouse_get_event (Gpm_Event *ev)
438 int btn;
439 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
440 static struct timeval tv2;
441 static int clicks;
442 static int last_btn = 0;
444 /* Decode Xterm mouse information to a GPM style event */
446 /* Variable btn has following meaning: */
447 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
448 btn = getch () - 32;
450 /* There seems to be no way of knowing which button was released */
451 /* So we assume all the buttons were released */
453 if (btn == 3){
454 if (last_btn) {
455 ev->type = GPM_UP | (GPM_SINGLE << clicks);
456 ev->buttons = 0;
457 last_btn = 0;
458 GET_TIME (tv1);
459 clicks = 0;
460 } else {
461 /* Bogus event, maybe mouse wheel */
462 ev->type = 0;
464 } else {
465 ev->type = GPM_DOWN;
466 GET_TIME (tv2);
467 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
468 clicks++;
469 clicks %= 3;
470 } else
471 clicks = 0;
473 switch (btn) {
474 case 0:
475 ev->buttons = GPM_B_LEFT;
476 break;
477 case 1:
478 ev->buttons = GPM_B_MIDDLE;
479 break;
480 case 2:
481 ev->buttons = GPM_B_RIGHT;
482 break;
483 case 64:
484 ev->buttons = GPM_B_UP;
485 break;
486 case 65:
487 ev->buttons = GPM_B_DOWN;
488 break;
489 default:
490 /* Nothing */
491 ev->type = 0;
492 ev->buttons = 0;
493 break;
495 last_btn = ev->buttons;
497 /* Coordinates are 33-based */
498 /* Transform them to 1-based */
499 ev->x = getch () - 32;
500 ev->y = getch () - 32;
503 static key_def *create_sequence (char *seq, int code, int action)
505 key_def *base, *p, *attach;
507 for (base = attach = NULL; *seq; seq++){
508 p = g_new (key_def, 1);
509 if (!base) base = p;
510 if (attach) attach->child = p;
512 p->ch = *seq;
513 p->code = code;
514 p->child = p->next = NULL;
515 if (!seq[1])
516 p->action = action;
517 else
518 p->action = MCKEY_NOACTION;
519 attach = p;
521 return base;
524 /* The maximum sequence length (32 + null terminator) */
525 #define SEQ_BUFFER_LEN 33
526 static int seq_buffer [SEQ_BUFFER_LEN];
527 static int *seq_append = 0;
529 static int push_char (int c)
531 if (!seq_append)
532 seq_append = seq_buffer;
534 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
535 return 0;
536 *(seq_append++) = c;
537 *seq_append = 0;
538 return 1;
542 * Return 1 on success, 0 on error.
543 * An error happens if SEQ is a beginning of an existing longer sequence.
545 int define_sequence (int code, char *seq, int action)
547 key_def *base;
549 if (strlen (seq) > SEQ_BUFFER_LEN-1)
550 return 0;
552 for (base = keys; (base != 0) && *seq; ){
553 if (*seq == base->ch){
554 if (base->child == 0){
555 if (*(seq+1)){
556 base->child = create_sequence (seq+1, code, action);
557 return 1;
558 } else {
559 /* The sequence matches an existing one. */
560 base->code = code;
561 base->action = action;
562 return 1;
564 } else {
565 base = base->child;
566 seq++;
568 } else {
569 if (base->next)
570 base = base->next;
571 else {
572 base->next = create_sequence (seq, code, action);
573 return 1;
578 if (!*seq) {
579 /* Attempt to redefine a sequence with a shorter sequence. */
580 return 0;
583 keys = create_sequence (seq, code, action);
584 return 1;
587 static int *pending_keys;
589 /* Apply corrections for the keycode generated in get_key_code() */
590 static int
591 correct_key_code (int code)
593 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
594 unsigned int mod = code & KEY_M_MASK; /* modifier */
597 * Add key modifiers directly from X11 or OS.
598 * Ordinary characters only get modifiers from sequences.
600 if (c < 32 || c >= 256) {
601 mod |= get_modifier ();
604 /* This is needed if the newline is reported as carriage return */
605 if (c == '\r')
606 c = '\n';
608 /* This is reported to be useful on AIX */
609 if (c == KEY_SCANCEL)
610 c = '\t';
612 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
613 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
614 c = KEY_BTAB;
615 mod = 0;
618 /* F0 is the same as F10 for out purposes */
619 if (c == KEY_F (0))
620 c = KEY_F (10);
623 * We are not interested if Ctrl was pressed when entering control
624 * characters, so assume that it was. When checking for such keys,
625 * XCTRL macro should be used. In some cases, we are interested,
626 * e.g. to distinguish Ctrl-Enter from Enter.
628 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
629 mod |= KEY_M_CTRL;
632 /* Unrecognized 0177 is delete (preserve Ctrl) */
633 if (c == 0177) {
634 c = KEY_BACKSPACE;
637 /* Unrecognized Ctrl-d is delete */
638 if (c == (31 & 'd')) {
639 c = KEY_DC;
640 mod &= ~KEY_M_CTRL;
643 /* Unrecognized Ctrl-h is backspace */
644 if (c == (31 & 'h')) {
645 c = KEY_BACKSPACE;
646 mod &= ~KEY_M_CTRL;
649 /* Shift+BackSpace is backspace */
650 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
651 mod &= ~KEY_M_SHIFT;
654 /* Convert Shift+Fn to F(n+10) */
655 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
656 c += 10;
659 /* Remove Shift information from function keys */
660 if (c >= KEY_F (1) && c <= KEY_F (20)) {
661 mod &= ~KEY_M_SHIFT;
664 if (!alternate_plus_minus)
665 switch (c) {
666 case KEY_KP_ADD:
667 c = '+';
668 break;
669 case KEY_KP_SUBTRACT:
670 c = '-';
671 break;
672 case KEY_KP_MULTIPLY:
673 c = '*';
674 break;
677 return (mod | c);
680 int get_key_code (int no_delay)
682 int c;
683 static key_def *this = NULL, *parent;
684 static struct timeval esctime = { -1, -1 };
685 static int lastnodelay = -1;
687 if (no_delay != lastnodelay) {
688 this = NULL;
689 lastnodelay = no_delay;
692 pend_send:
693 if (pending_keys){
694 int d = *pending_keys++;
695 check_pend:
696 if (!*pending_keys){
697 pending_keys = 0;
698 seq_append = 0;
700 if (d == ESC_CHAR && pending_keys){
701 d = ALT(*pending_keys++);
702 goto check_pend;
704 if ((d & 0x80) && use_8th_bit_as_meta)
705 d = ALT(d & 0x7f);
706 this = NULL;
707 return correct_key_code (d);
710 nodelay_try_again:
711 if (no_delay) {
712 nodelay (stdscr, TRUE);
714 c = getch ();
715 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
716 if (c == KEY_RESIZE)
717 goto nodelay_try_again;
718 #endif
719 if (no_delay) {
720 nodelay (stdscr, FALSE);
721 if (c == -1) {
722 if (this != NULL && parent != NULL &&
723 parent->action == MCKEY_ESCAPE && old_esc_mode) {
724 struct timeval current, timeout;
726 if (esctime.tv_sec == -1)
727 return -1;
728 GET_TIME (current);
729 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
730 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
731 if (timeout.tv_usec > 1000000) {
732 timeout.tv_usec -= 1000000;
733 timeout.tv_sec++;
735 if (current.tv_sec < timeout.tv_sec)
736 return -1;
737 if (current.tv_sec == timeout.tv_sec &&
738 current.tv_usec < timeout.tv_usec)
739 return -1;
740 this = NULL;
741 pending_keys = seq_append = NULL;
742 return ESC_CHAR;
744 return -1;
746 } else if (c == -1){
747 /* Maybe we got an incomplete match.
748 This we do only in delay mode, since otherwise
749 getch can return -1 at any time. */
750 if (seq_append) {
751 pending_keys = seq_buffer;
752 goto pend_send;
754 this = NULL;
755 return -1;
758 /* Search the key on the root */
759 if (!no_delay || this == NULL) {
760 this = keys;
761 parent = NULL;
763 if ((c & 0x80) && use_8th_bit_as_meta) {
764 c &= 0x7f;
766 /* The first sequence defined starts with esc */
767 parent = keys;
768 this = keys->child;
771 while (this){
772 if (c == this->ch){
773 if (this->child){
774 if (!push_char (c)){
775 pending_keys = seq_buffer;
776 goto pend_send;
778 parent = this;
779 this = this->child;
780 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
781 if (no_delay) {
782 GET_TIME (esctime);
783 if (this == NULL) {
784 /* Shouldn't happen */
785 fputs ("Internal error\n", stderr);
786 exit (1);
788 goto nodelay_try_again;
790 esctime.tv_sec = -1;
791 c = xgetch_second ();
792 if (c == -1) {
793 pending_keys = seq_append = NULL;
794 this = NULL;
795 return ESC_CHAR;
797 } else {
798 if (no_delay)
799 goto nodelay_try_again;
800 c = getch ();
802 } else {
803 /* We got a complete match, return and reset search */
804 int code;
806 pending_keys = seq_append = NULL;
807 code = this->code;
808 this = NULL;
809 return correct_key_code (code);
811 } else {
812 if (this->next)
813 this = this->next;
814 else {
815 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
817 /* Convert escape-digits to F-keys */
818 if (isdigit(c))
819 c = KEY_F (c - '0');
820 else if (c == ' ')
821 c = ESC_CHAR;
822 else
823 c = ALT(c);
825 pending_keys = seq_append = NULL;
826 this = NULL;
827 return correct_key_code (c);
829 /* Did not find a match or {c} was changed in the if above,
830 so we have to return everything we had skipped
832 push_char (c);
833 pending_keys = seq_buffer;
834 goto pend_send;
838 this = NULL;
839 return correct_key_code (c);
842 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
843 static void
844 try_channels (int set_timeout)
846 struct timeval timeout;
847 static fd_set select_set;
848 struct timeval *timeptr;
849 int v;
850 int maxfdp;
852 while (1){
853 FD_ZERO (&select_set);
854 FD_SET (input_fd, &select_set); /* Add stdin */
855 maxfdp = max (add_selects (&select_set), input_fd);
857 if (set_timeout){
858 timeout.tv_sec = 0;
859 timeout.tv_usec = 100000;
860 timeptr = &timeout;
861 } else
862 timeptr = 0;
864 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
865 if (v > 0){
866 check_selects (&select_set);
867 if (FD_ISSET (input_fd, &select_set))
868 return;
873 /* Workaround for System V Curses vt100 bug */
874 static int getch_with_delay (void)
876 int c;
878 /* This routine could be used on systems without mouse support,
879 so we need to do the select check :-( */
880 while (1){
881 if (!pending_keys)
882 try_channels (0);
884 /* Try to get a character */
885 c = get_key_code (0);
886 if (c != -1)
887 break;
888 /* Failed -> wait 0.1 secs and try again */
889 try_channels (1);
891 /* Success -> return the character */
892 return c;
895 /* Returns a character read from stdin with appropriate interpretation */
896 /* Also takes care of generated mouse events */
897 /* Returns EV_MOUSE if it is a mouse event */
898 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
900 get_event (struct Gpm_Event *event, int redo_event, int block)
902 int c;
903 static int flag; /* Return value from select */
904 #ifdef HAVE_LIBGPM
905 static struct Gpm_Event ev; /* Mouse event */
906 #endif
907 struct timeval timeout;
908 struct timeval *time_addr = NULL;
909 static int dirty = 3;
911 if ((dirty == 3) || is_idle ()) {
912 mc_refresh ();
913 doupdate ();
914 dirty = 1;
915 } else
916 dirty++;
918 vfs_timeout_handler ();
920 /* Ok, we use (event->x < 0) to signal that the event does not contain
921 a suitable position for the mouse, so we can't use show_mouse_pointer
922 on it.
924 if (event->x > 0) {
925 show_mouse_pointer (event->x, event->y);
926 if (!redo_event)
927 event->x = -1;
930 /* Repeat if using mouse */
931 while (mouse_enabled && !pending_keys) {
932 int maxfdp;
933 fd_set select_set;
935 FD_ZERO (&select_set);
936 FD_SET (input_fd, &select_set);
937 maxfdp = max (add_selects (&select_set), input_fd);
939 #ifdef HAVE_LIBGPM
940 if (use_mouse_p == MOUSE_GPM) {
941 if (gpm_fd < 0) {
942 /* Connection to gpm broken, possibly gpm has died */
943 mouse_enabled = 0;
944 use_mouse_p = MOUSE_NONE;
945 break;
946 } else {
947 FD_SET (gpm_fd, &select_set);
948 maxfdp = max (maxfdp, gpm_fd);
951 #endif
953 if (redo_event) {
954 timeout.tv_usec = mou_auto_repeat * 1000;
955 timeout.tv_sec = 0;
957 time_addr = &timeout;
958 } else {
959 int seconds;
961 if ((seconds = vfs_timeouts ())) {
962 /* the timeout could be improved and actually be
963 * the number of seconds until the next vfs entry
964 * timeouts in the stamp list.
967 timeout.tv_sec = seconds;
968 timeout.tv_usec = 0;
969 time_addr = &timeout;
970 } else
971 time_addr = NULL;
974 if (!block) {
975 time_addr = &timeout;
976 timeout.tv_sec = 0;
977 timeout.tv_usec = 0;
979 enable_interrupt_key ();
980 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
981 disable_interrupt_key ();
983 /* select timed out: it could be for any of the following reasons:
984 * redo_event -> it was because of the MOU_REPEAT handler
985 * !block -> we did not block in the select call
986 * else -> 10 second timeout to check the vfs status.
988 if (flag == 0) {
989 if (redo_event)
990 return EV_MOUSE;
991 if (!block)
992 return EV_NONE;
993 vfs_timeout_handler ();
995 if (flag == -1 && errno == EINTR)
996 return EV_NONE;
998 check_selects (&select_set);
1000 if (FD_ISSET (input_fd, &select_set))
1001 break;
1002 #ifdef HAVE_LIBGPM
1003 if (use_mouse_p == MOUSE_GPM && gpm_fd > 0
1004 && FD_ISSET (gpm_fd, &select_set)) {
1005 Gpm_GetEvent (&ev);
1006 Gpm_FitEvent (&ev);
1007 *event = ev;
1008 return EV_MOUSE;
1010 #endif /* !HAVE_LIBGPM */
1012 #ifndef HAVE_SLANG
1013 flag = is_wintouched (stdscr);
1014 untouchwin (stdscr);
1015 #endif /* !HAVE_SLANG */
1016 c = block ? getch_with_delay () : get_key_code (1);
1018 #ifndef HAVE_SLANG
1019 if (flag)
1020 touchwin (stdscr);
1021 #endif /* !HAVE_SLANG */
1023 if (c == MCKEY_MOUSE
1024 #ifdef KEY_MOUSE
1025 || c == KEY_MOUSE
1026 #endif /* KEY_MOUSE */
1028 /* Mouse event */
1029 xmouse_get_event (event);
1030 if (event->type)
1031 return EV_MOUSE;
1032 else
1033 return EV_NONE;
1036 return c;
1039 /* Returns a key press, mouse events are discarded */
1040 int mi_getch ()
1042 Gpm_Event ev;
1043 int key;
1045 ev.x = -1;
1046 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
1048 return key;
1051 static int xgetch_second (void)
1053 fd_set Read_FD_Set;
1054 int c;
1055 struct timeval timeout;
1057 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000;
1058 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000;
1059 nodelay (stdscr, TRUE);
1060 FD_ZERO (&Read_FD_Set);
1061 FD_SET (input_fd, &Read_FD_Set);
1062 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1063 c = getch ();
1064 nodelay (stdscr, FALSE);
1065 return c;
1068 static void
1069 learn_store_key (char *buffer, char **p, int c)
1071 if (*p - buffer > 253)
1072 return;
1073 if (c == ESC_CHAR) {
1074 *(*p)++ = '\\';
1075 *(*p)++ = 'e';
1076 } else if (c < ' ') {
1077 *(*p)++ = '^';
1078 *(*p)++ = c + 'a' - 1;
1079 } else if (c == '^') {
1080 *(*p)++ = '^';
1081 *(*p)++ = '^';
1082 } else
1083 *(*p)++ = (char) c;
1086 char *learn_key (void)
1088 /* LEARN_TIMEOUT in usec */
1089 #define LEARN_TIMEOUT 200000
1091 fd_set Read_FD_Set;
1092 struct timeval endtime;
1093 struct timeval timeout;
1094 int c;
1095 char buffer [256];
1096 char *p = buffer;
1098 keypad(stdscr, FALSE); /* disable intepreting keys by ncurses */
1099 c = getch ();
1100 while (c == -1)
1101 c = getch (); /* Sanity check, should be unnecessary */
1102 learn_store_key (buffer, &p, c);
1103 GET_TIME (endtime);
1104 endtime.tv_usec += LEARN_TIMEOUT;
1105 if (endtime.tv_usec > 1000000) {
1106 endtime.tv_usec -= 1000000;
1107 endtime.tv_sec++;
1109 nodelay (stdscr, TRUE);
1110 for (;;) {
1111 while ((c = getch ()) == -1) {
1112 GET_TIME (timeout);
1113 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1114 if (timeout.tv_usec < 0)
1115 timeout.tv_sec++;
1116 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1117 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1118 FD_ZERO (&Read_FD_Set);
1119 FD_SET (input_fd, &Read_FD_Set);
1120 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1121 } else
1122 break;
1124 if (c == -1)
1125 break;
1126 learn_store_key (buffer, &p, c);
1128 keypad(stdscr, TRUE);
1129 nodelay (stdscr, FALSE);
1130 *p = 0;
1131 return g_strdup (buffer);
1134 /* xterm and linux console only: set keypad to numeric or application
1135 mode. Only in application keypad mode it's possible to distinguish
1136 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1137 void
1138 numeric_keypad_mode (void)
1140 if (console_flag || xterm_flag) {
1141 fputs ("\033>", stdout);
1142 fflush (stdout);
1146 void
1147 application_keypad_mode (void)
1149 if (console_flag || xterm_flag) {
1150 fputs ("\033=", stdout);
1151 fflush (stdout);
1157 * Check if we are idle, i.e. there are no pending keyboard or mouse
1158 * events. Return 1 is idle, 0 is there are pending events.
1161 is_idle (void)
1163 int maxfdp;
1164 fd_set select_set;
1165 struct timeval timeout;
1167 FD_ZERO (&select_set);
1168 FD_SET (input_fd, &select_set);
1169 maxfdp = input_fd;
1170 #ifdef HAVE_LIBGPM
1171 if (use_mouse_p == MOUSE_GPM && mouse_enabled && gpm_fd > 0) {
1172 FD_SET (gpm_fd, &select_set);
1173 maxfdp = max (maxfdp, gpm_fd);
1175 #endif
1176 timeout.tv_sec = 0;
1177 timeout.tv_usec = 0;
1178 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1183 * Get modifier state (shift, alt, ctrl) for the last key pressed.
1184 * We are assuming that the state didn't change since the key press.
1185 * This is only correct if get_modifier() is called very fast after
1186 * the input was received, so that the user didn't release the
1187 * modifier keys yet.
1189 static int
1190 get_modifier (void)
1192 int result = 0;
1193 #ifdef __QNXNTO__
1194 int mod_status, shift_ext_status;
1195 static int in_photon = 0;
1196 static int ph_ig = 0;
1197 char phlib_path[PATH_MAX];
1198 PhCursorInfo_t cursor_info;
1199 static void *ph_handle;
1200 char *ph_env;
1201 #endif /* __QNXNTO__ */
1203 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1204 if (x11_window) {
1205 Window root, child;
1206 int root_x, root_y;
1207 int win_x, win_y;
1208 unsigned int mask;
1210 #ifdef HAVE_GMODULE
1211 (*func_XQueryPointer) (x11_display, x11_window, &root, &child,
1212 &root_x, &root_y, &win_x, &win_y, &mask);
1213 #else
1214 XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
1215 &root_y, &win_x, &win_y, &mask);
1216 #endif /* HAVE_GMODULE */
1218 if (mask & ShiftMask)
1219 result |= KEY_M_SHIFT;
1220 if (mask & ControlMask)
1221 result |= KEY_M_CTRL;
1222 return result;
1224 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1225 #ifdef __QNXNTO__
1227 if (in_photon == 0) {
1228 /* First time here, let's load Photon library and attach
1229 to Photon */
1230 in_photon = -1;
1231 ph_env = getenv ("PHOTON2_PATH");
1232 if (ph_env != NULL) {
1233 g_snprintf (phlib_path, sizeof (phlib_path),
1234 "%s/lib/libph.so.1", ph_env);
1235 /* QNX 6.x has no support for RTLD_LAZY */
1236 ph_handle = dlopen (phlib_path, RTLD_NOW);
1237 if (ph_handle != NULL) {
1238 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
1239 ph_input_group =
1240 (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
1241 ph_query_cursor =
1242 (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
1243 if ((ph_attach != NULL) && (ph_input_group != NULL)
1244 && (ph_query_cursor != NULL)) {
1245 if ((*ph_attach) (0, 0)) { /* Attached */
1246 ph_ig = (*ph_input_group) (0);
1247 in_photon = 1;
1253 /* We do not have Photon running. Assume we are in text
1254 console or xterm */
1255 if (in_photon == -1) {
1256 if (devctl
1257 (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status,
1258 sizeof (int), NULL) == -1)
1259 return 0;
1260 shift_ext_status = mod_status & 0xffffff00UL;
1261 mod_status &= 0x7f;
1262 if (mod_status & _LINESTATUS_CON_ALT)
1263 result |= KEY_M_ALT;
1264 if (mod_status & _LINESTATUS_CON_CTRL)
1265 result |= KEY_M_CTRL;
1266 if ((mod_status & _LINESTATUS_CON_SHIFT)
1267 || (shift_ext_status & 0x00000800UL))
1268 result |= KEY_M_SHIFT;
1269 } else {
1270 (*ph_query_cursor) (ph_ig, &cursor_info);
1271 if (cursor_info.key_mods & 0x04)
1272 result |= KEY_M_ALT;
1273 if (cursor_info.key_mods & 0x02)
1274 result |= KEY_M_CTRL;
1275 if (cursor_info.key_mods & 0x01)
1276 result |= KEY_M_SHIFT;
1278 #endif /* __QNXNTO__ */
1279 #ifdef __linux__
1281 unsigned char modifiers = 6;
1283 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
1284 return 0;
1286 /* Translate Linux modifiers into mc modifiers */
1287 if (modifiers & SHIFT_PRESSED)
1288 result |= KEY_M_SHIFT;
1289 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
1290 result |= KEY_M_ALT;
1291 if (modifiers & CONTROL_PRESSED)
1292 result |= KEY_M_CTRL;
1294 return result;
1296 #else
1297 return result;
1298 #endif /* !__linux__ */
1301 static void k_dispose (key_def *k)
1303 if (!k)
1304 return;
1305 k_dispose (k->child);
1306 k_dispose (k->next);
1307 g_free (k);
1310 static void s_dispose (SelectList *sel)
1312 if (!sel)
1313 return;
1315 s_dispose (sel->next);
1316 g_free (sel);
1319 void done_key ()
1321 k_dispose (keys);
1322 s_dispose (select_list);
1324 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1325 #ifdef HAVE_GMODULE
1326 if (x11_display)
1327 (*func_XCloseDisplay) (x11_display);
1328 if (x11_module)
1329 g_module_close (x11_module);
1330 #else
1331 if (x11_display)
1332 XCloseDisplay (x11_display);
1333 #endif /* HAVE_GMODULE */
1334 #endif /* HAVE_TEXTMODE_X11_SUPPORT */