fix: main.c keys handler
[midnight-commander.git] / src / tty / key.c
blobedc033ed719239265efcdb1dc00a76efe75087fd
1 /* Keyboard support routines.
3 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2009 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/tty-internal.h" /* mouse_enabled */
44 #include "../../src/tty/mouse.h"
45 #include "../../src/tty/key.h"
46 #include "../../src/tty/win.h" /* xterm_flag */
48 #include "../../src/main.h"
49 #include "../../src/layout.h" /* winch_flag, mc_refresh() */
50 #include "../../src/cons.saver.h"
51 #include "../../src/strutil.h" /* str_casecmp */
53 #ifdef USE_VFS
54 #include "../../vfs/gc.h"
55 #endif
57 #ifdef HAVE_TEXTMODE_X11_SUPPORT
58 #include "../src/tty/x11conn.h"
59 #endif
61 #ifdef __linux__
62 #if defined(__GLIBC__) && (__GLIBC__ < 2)
63 # include <linux/termios.h> /* TIOCLINUX */
64 #elif defined HAVE_TERMIOS_H
65 # include <termios.h>
66 #endif
67 #include <sys/ioctl.h>
68 #endif /* __linux__ */
70 #ifdef __CYGWIN__
71 #include <termios.h>
72 #include <sys/ioctl.h>
73 #endif /* __CYGWIN__ */
75 #ifdef __QNXNTO__
76 #include <dlfcn.h>
77 #include <Ph.h>
78 #include <sys/dcmd_chr.h>
79 #endif /* __QNXNTO__ */
81 /*** global variables **************************************************/
83 /* If true, use + and \ keys normally and select/unselect do if M-+ / M-\.
84 and M-- and keypad + / - */
85 int alternate_plus_minus = 0;
87 int mou_auto_repeat = 100;
88 int double_click_speed = 250;
89 int old_esc_mode = 0;
90 int use_8th_bit_as_meta = 0;
92 /* This table is a mapping between names and the constants we use
93 * We use this to allow users to define alternate definitions for
94 * certain keys that may be missing from the terminal database
96 key_code_name_t key_name_conv_tab[] = {
97 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
98 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
99 {KEY_F (1), "f1", N_("Function key 1")},
100 {KEY_F (2), "f2", N_("Function key 2")},
101 {KEY_F (3), "f3", N_("Function key 3")},
102 {KEY_F (4), "f4", N_("Function key 4")},
103 {KEY_F (5), "f5", N_("Function key 5")},
104 {KEY_F (6), "f6", N_("Function key 6")},
105 {KEY_F (7), "f7", N_("Function key 7")},
106 {KEY_F (8), "f8", N_("Function key 8")},
107 {KEY_F (9), "f9", N_("Function key 9")},
108 {KEY_F (10), "f10", N_("Function key 10")},
109 {KEY_F (11), "f11", N_("Function key 11")},
110 {KEY_F (12), "f12", N_("Function key 12")},
111 {KEY_F (13), "f13", N_("Function key 13")},
112 {KEY_F (14), "f14", N_("Function key 14")},
113 {KEY_F (15), "f15", N_("Function key 15")},
114 {KEY_F (16), "f16", N_("Function key 16")},
115 {KEY_F (17), "f17", N_("Function key 17")},
116 {KEY_F (18), "f18", N_("Function key 18")},
117 {KEY_F (19), "f19", N_("Function key 19")},
118 {KEY_F (20), "f20", N_("Function key 20")},
119 {KEY_BACKSPACE, "bs", N_("Backspace key")},
120 {KEY_END, "end", N_("End key")},
121 {KEY_UP, "up", N_("Up arrow key")},
122 {KEY_DOWN, "down", N_("Down arrow key")},
123 {KEY_LEFT, "left", N_("Left arrow key")},
124 {KEY_RIGHT, "right", N_("Right arrow key")},
125 {KEY_HOME, "home", N_("Home key")},
126 {KEY_NPAGE, "pgdn", N_("Page Down key")},
127 {KEY_PPAGE, "pgup", N_("Page Up key")},
128 {KEY_IC, "ins", N_("Insert key")},
129 {KEY_DC, "delete", N_("Delete key")},
130 {ALT ('\t'), "complete", N_("Completion/M-tab")},
131 {KEY_KP_ADD, "kpplus", N_("+ on keypad")},
132 {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad")},
133 {(int) '/', "kpslash", N_("Slash on keypad")},
134 {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad")},
136 /* From here on, these won't be shown in Learn keys (no space) */
137 {KEY_LEFT, "kpleft", N_("Left arrow keypad")},
138 {KEY_RIGHT, "kpright", N_("Right arrow keypad")},
139 {KEY_UP, "kpup", N_("Up arrow keypad")},
140 {KEY_DOWN, "kpdown", N_("Down arrow keypad")},
141 {KEY_HOME, "kphome", N_("Home on keypad")},
142 {KEY_END, "kpend", N_("End on keypad")},
143 {KEY_NPAGE, "kpnpage", N_("Page Down keypad")},
144 {KEY_PPAGE, "kpppage", N_("Page Up keypad")},
145 {KEY_IC, "kpinsert", N_("Insert on keypad")},
146 {KEY_DC, "kpdelete", N_("Delete on keypad")},
147 {(int) '\n', "kpenter", N_("Enter on keypad")},
149 /* Alternative label */
150 {KEY_BACKSPACE, "backspace", N_("Backspace key")},
151 {KEY_IC, "insert", N_("Insert key")},
152 {(int) '+', "plus", N_("Plus")},
153 {(int) '-', "minus", N_("Minus")},
154 {(int) '*', "asterisk", N_("Asterisk")},
155 {(int) '.', "dot", N_("Dot")},
156 {(int) '<', "lt", N_("Less than")},
157 {(int) '>', "gt", N_("Great than")},
158 {(int) '=', "equal", N_("Equal")},
159 {(int) ',', "comma", N_("Comma")},
160 {(int) '\'', "apostrophe", N_("Apostrophe")},
161 {(int) ':', "colon", N_("Colon")},
162 {(int) '!', "exclamation", N_("Exclamation mark")},
163 {(int) '?', "question", N_("Question mark")},
164 {(int) '&', "ampersand", N_("Ampersand")},
165 {(int) '$', "dollar", N_("Dollar sign")},
166 {(int) '"', "quota", N_("Quotation mark")},
167 {(int) '^', "caret", N_("Caret")},
168 {(int) '~', "tilda", N_("Tilda")},
169 {(int) '`', "prime", N_("Prime")},
170 {(int) '_', "underline", N_("Underline")},
171 {(int) '_', "understrike", N_("Understrike")},
172 {(int) '|', "pipe", N_("Pipe")},
173 {(int) '\n', "enter", N_("Enter")},
174 {(int) '\t', "tab", N_("Tab key")},
175 {(int) ' ', "space", N_("Space key")},
176 {(int) '/', "slash", N_("Slash key")},
177 {(int) '\\', "backslash", N_("Backslash key")},
178 {(int) '#', "number", N_("Number sign #")},
179 {(int) '#', "hash", N_("Number sign #")},
181 /* meta keys */
182 {KEY_M_CTRL, "control", N_("Ctrl")},
183 {KEY_M_CTRL, "ctrl", N_("Ctrl")},
184 {KEY_M_ALT, "alt", N_("Alt")},
185 {KEY_M_ALT, "ralt", N_("Alt")},
186 {KEY_M_ALT, "meta", N_("Alt")},
187 {KEY_M_SHIFT, "shift", N_("Shift")},
189 {0, 0, 0}
193 /*** file scope macro definitions **************************************/
195 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
196 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
198 /* The maximum sequence length (32 + null terminator) */
199 #define SEQ_BUFFER_LEN 33
201 /*** file scope type declarations **************************************/
203 /* Linux console keyboard modifiers */
204 typedef enum {
205 SHIFT_PRESSED = (1 << 0),
206 ALTR_PRESSED = (1 << 1),
207 CONTROL_PRESSED = (1 << 2),
208 ALTL_PRESSED = (1 << 3)
209 } mod_pressed_t;
211 typedef struct key_def {
212 char ch; /* Holds the matching char code */
213 int code; /* The code returned, valid if child == NULL */
214 struct key_def *next;
215 struct key_def *child; /* sequence continuation */
216 int action; /* optional action to be done. Now used only
217 to mark that we are just after the first
218 Escape */
219 } key_def;
221 typedef struct {
222 int code;
223 const char *seq;
224 int action;
225 } key_define_t;
227 /* File descriptor monitoring add/remove routines */
228 typedef struct SelectList {
229 int fd;
230 select_fn callback;
231 void *info;
232 struct SelectList *next;
233 } SelectList;
235 #ifdef __QNXNTO__
236 typedef int (*ph_dv_f) (void *, void *);
237 typedef int (*ph_ov_f) (void *);
238 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
239 #endif
241 /*** file scope variables **********************************************/
243 static key_define_t mc_default_keys[] = {
244 {ESC_CHAR, ESC_STR, MCKEY_ESCAPE},
245 {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION},
246 {0, NULL, MCKEY_NOACTION},
249 /* Broken terminfo and termcap databases on xterminals */
250 static key_define_t xterm_key_defines[] = {
251 {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION},
252 {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION},
253 {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION},
254 {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION},
255 {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION},
256 {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION},
257 {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION},
258 {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION},
259 {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION},
260 {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION},
261 {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION},
262 {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION},
263 {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION},
264 {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION},
266 /* old xterm Shift-arrows */
267 {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION},
268 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION},
269 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION},
270 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION},
272 /* new xterm Shift-arrows */
273 {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION},
274 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION},
275 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION},
276 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION},
278 /* more xterm keys with modifiers */
279 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION},
280 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION},
281 {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION},
282 {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION},
283 {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION},
284 {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION},
285 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION},
286 {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION},
287 {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION},
288 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION},
289 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION},
290 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION},
291 {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION},
292 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION},
293 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION},
294 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION},
295 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION},
296 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION},
298 /* putty */
299 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION},
300 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION},
301 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION},
302 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION},
304 /* putty alt-arrow keys */
305 /* removed as source esc esc esc trouble */
307 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
308 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
309 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
310 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
311 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
312 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
313 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
314 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
316 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
317 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
318 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
319 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
321 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
322 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
323 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
324 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
326 /* xterm alt-arrow keys */
327 {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION},
328 {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION},
329 {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION},
330 {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION},
331 {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION},
332 {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION},
333 {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION},
334 {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION},
335 {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION},
336 {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION},
337 {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION},
338 {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION},
339 {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION},
340 {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION},
341 {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION},
342 {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION},
344 /* rxvt keys with modifiers */
345 {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
346 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
347 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
348 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
349 {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
350 {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
351 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
352 {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
353 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
354 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
355 {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
356 {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
357 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
358 {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
359 {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
360 {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
361 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
363 /* konsole keys with modifiers */
364 {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
365 {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
367 /* gnome-terminal */
368 {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
369 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
370 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
371 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
372 {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
373 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
374 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
375 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
376 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
377 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
378 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
379 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
381 /* gnome-terminal - application mode */
382 {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
383 {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
384 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
385 {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
386 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
387 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
388 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
389 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
391 /* iTerm */
392 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
393 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
395 /* putty */
396 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
397 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
399 /* keypad keys */
400 {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
401 {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
402 {'/', ESC_STR "Oo", MCKEY_NOACTION},
403 {'\n', ESC_STR "OM", MCKEY_NOACTION},
405 {0, NULL, MCKEY_NOACTION},
408 /* qansi-m terminals have a much more key combinatios,
409 which are undefined in termcap/terminfo */
410 static key_define_t qansi_key_defines[] = {
411 /* qansi-m terminal */
412 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
413 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
414 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
415 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
416 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
417 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
418 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
419 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
420 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
421 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
422 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
423 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
424 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
425 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
426 {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
427 {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
428 {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
429 {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
430 {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
431 {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
432 {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
433 {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
434 {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
435 {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
436 {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
437 {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
438 {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
439 {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
440 {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
441 {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
442 {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
443 {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
444 {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
445 {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
446 {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
447 {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
448 {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
449 {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
450 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
451 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
452 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
453 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
454 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
455 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
456 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
457 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
458 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
459 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
460 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
461 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
462 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
463 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
464 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
465 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-r */
466 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
467 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
468 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
469 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
470 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
471 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
472 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
473 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
474 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
475 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
476 {0, NULL, MCKEY_NOACTION},
479 /* timeout for old_esc_mode in usec */
480 static int keyboard_key_timeout = 1000000; /* settable via env */
482 /* This holds all the key definitions */
483 static key_def *keys = NULL;
485 static int input_fd;
486 static int disabled_channels = 0; /* Disable channels checking */
488 static SelectList *select_list = NULL;
490 static int seq_buffer[SEQ_BUFFER_LEN];
491 static int *seq_append = NULL;
493 static int *pending_keys = NULL;
495 #ifdef __QNXNTO__
496 ph_dv_f ph_attach;
497 ph_ov_f ph_input_group;
498 ph_pqc_f ph_query_cursor;
499 #endif
501 #ifdef HAVE_TEXTMODE_X11_SUPPORT
502 static Display *x11_display;
503 static Window x11_window;
504 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
506 /*** file scope functions **********************************************/
508 static int
509 add_selects (fd_set * select_set)
511 int top_fd = 0;
513 if (disabled_channels == 0) {
514 SelectList *p;
516 for (p = select_list; p != NULL; p = p->next) {
517 FD_SET (p->fd, select_set);
518 if (p->fd > top_fd)
519 top_fd = p->fd;
523 return top_fd;
526 static void
527 check_selects (fd_set * select_set)
529 if (disabled_channels == 0) {
530 gboolean retry;
532 do {
533 SelectList *p;
535 retry = FALSE;
536 for (p = select_list; p; p = p->next)
537 if (FD_ISSET (p->fd, select_set)) {
538 FD_CLR (p->fd, select_set);
539 (*p->callback) (p->fd, p->info);
540 retry = TRUE;
541 break;
543 } while (retry);
547 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
548 static void
549 try_channels (int set_timeout)
551 struct timeval timeout;
552 static fd_set select_set;
553 struct timeval *timeptr;
554 int v;
555 int maxfdp;
557 while (1) {
558 FD_ZERO (&select_set);
559 FD_SET (input_fd, &select_set); /* Add stdin */
560 maxfdp = max (add_selects (&select_set), input_fd);
562 if (set_timeout) {
563 timeout.tv_sec = 0;
564 timeout.tv_usec = 100000;
565 timeptr = &timeout;
566 } else
567 timeptr = 0;
569 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
570 if (v > 0) {
571 check_selects (&select_set);
572 if (FD_ISSET (input_fd, &select_set))
573 return;
578 static key_def *
579 create_sequence (const char *seq, int code, int action)
581 key_def *base, *p, *attach;
583 for (base = attach = NULL; *seq; seq++) {
584 p = g_new (key_def, 1);
585 if (base == NULL)
586 base = p;
587 if (attach != NULL)
588 attach->child = p;
590 p->ch = *seq;
591 p->code = code;
592 p->child = p->next = NULL;
593 if (seq[1] == '\0')
594 p->action = action;
595 else
596 p->action = MCKEY_NOACTION;
597 attach = p;
599 return base;
602 static void
603 define_sequences (const key_define_t * kd)
605 int i;
607 for (i = 0; kd[i].code != 0; i++)
608 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
611 static void
612 init_key_x11 (void)
614 #ifdef HAVE_TEXTMODE_X11_SUPPORT
615 if (getenv ("DISPLAY") != NULL) {
616 x11_display = mc_XOpenDisplay (0);
618 if (x11_display != NULL)
619 x11_window = DefaultRootWindow (x11_display);
621 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
624 /* Workaround for System V Curses vt100 bug */
625 static int
626 getch_with_delay (void)
628 int c;
630 /* This routine could be used on systems without mouse support,
631 so we need to do the select check :-( */
632 while (1) {
633 if (pending_keys == NULL)
634 try_channels (0);
636 /* Try to get a character */
637 c = get_key_code (0);
638 if (c != -1)
639 break;
640 /* Failed -> wait 0.1 secs and try again */
641 try_channels (1);
643 /* Success -> return the character */
644 return c;
647 static void
648 xmouse_get_event (Gpm_Event * ev)
650 int btn;
651 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
652 static struct timeval tv2;
653 static int clicks = 0;
654 static int last_btn = 0;
656 /* Decode Xterm mouse information to a GPM style event */
658 /* Variable btn has following meaning: */
659 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
660 btn = tty_lowlevel_getch () - 32;
662 /* There seems to be no way of knowing which button was released */
663 /* So we assume all the buttons were released */
665 if (btn == 3) {
666 if (last_btn != 0) {
667 if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0) {
668 /* FIXME: DIRTY HACK */
669 /* don't generate GPM_UP after mouse wheel */
670 /* need for menu event handling */
671 ev->type = 0;
672 tv1.tv_sec = 0;
673 tv1.tv_usec = 0;
674 } else {
675 ev->type = GPM_UP | (GPM_SINGLE << clicks);
676 GET_TIME (tv1);
678 ev->buttons = 0;
679 last_btn = 0;
680 clicks = 0;
681 } else {
682 /* Bogus event, maybe mouse wheel */
683 ev->type = 0;
685 } else {
686 if (btn >= 32 && btn <= 34) {
687 btn -= 32;
688 ev->type = GPM_DRAG;
689 } else
690 ev->type = GPM_DOWN;
692 GET_TIME (tv2);
693 if (tv1.tv_sec && (DIF_TIME (tv1, tv2) < double_click_speed)) {
694 clicks++;
695 clicks %= 3;
696 } else
697 clicks = 0;
699 switch (btn) {
700 case 0:
701 ev->buttons = GPM_B_LEFT;
702 break;
703 case 1:
704 ev->buttons = GPM_B_MIDDLE;
705 break;
706 case 2:
707 ev->buttons = GPM_B_RIGHT;
708 break;
709 case 64:
710 ev->buttons = GPM_B_UP;
711 clicks = 0;
712 break;
713 case 65:
714 ev->buttons = GPM_B_DOWN;
715 clicks = 0;
716 break;
717 default:
718 /* Nothing */
719 ev->type = 0;
720 ev->buttons = 0;
721 break;
723 last_btn = ev->buttons;
725 /* Coordinates are 33-based */
726 /* Transform them to 1-based */
727 ev->x = tty_lowlevel_getch () - 32;
728 ev->y = tty_lowlevel_getch () - 32;
732 * Get modifier state (shift, alt, ctrl) for the last key pressed.
733 * We are assuming that the state didn't change since the key press.
734 * This is only correct if get_modifier() is called very fast after
735 * the input was received, so that the user didn't release the
736 * modifier keys yet.
738 static int
739 get_modifier (void)
741 int result = 0;
742 #ifdef __QNXNTO__
743 int mod_status, shift_ext_status;
744 static int in_photon = 0;
745 static int ph_ig = 0;
746 PhCursorInfo_t cursor_info;
747 #endif /* __QNXNTO__ */
749 #ifdef HAVE_TEXTMODE_X11_SUPPORT
750 if (x11_window != 0) {
751 Window root, child;
752 int root_x, root_y;
753 int win_x, win_y;
754 unsigned int mask;
756 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
757 &root_y, &win_x, &win_y, &mask);
759 if (mask & ShiftMask)
760 result |= KEY_M_SHIFT;
761 if (mask & ControlMask)
762 result |= KEY_M_CTRL;
763 return result;
765 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
766 #ifdef __QNXNTO__
768 if (in_photon == 0) {
769 /* First time here, let's load Photon library and attach
770 to Photon */
771 in_photon = -1;
772 if (getenv ("PHOTON2_PATH") != NULL) {
773 /* QNX 6.x has no support for RTLD_LAZY */
774 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
775 if (ph_handle != NULL) {
776 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
777 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
778 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
779 if ((ph_attach != NULL) && (ph_input_group != NULL)
780 && (ph_query_cursor != NULL)) {
781 if ((*ph_attach) (0, 0)) { /* Attached */
782 ph_ig = (*ph_input_group) (0);
783 in_photon = 1;
789 /* We do not have Photon running. Assume we are in text
790 console or xterm */
791 if (in_photon == -1) {
792 if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (int), NULL) == -1)
793 return 0;
794 shift_ext_status = mod_status & 0xffffff00UL;
795 mod_status &= 0x7f;
796 if (mod_status & _LINESTATUS_CON_ALT)
797 result |= KEY_M_ALT;
798 if (mod_status & _LINESTATUS_CON_CTRL)
799 result |= KEY_M_CTRL;
800 if ((mod_status & _LINESTATUS_CON_SHIFT)
801 || (shift_ext_status & 0x00000800UL))
802 result |= KEY_M_SHIFT;
803 } else {
804 (*ph_query_cursor) (ph_ig, &cursor_info);
805 if (cursor_info.key_mods & 0x04)
806 result |= KEY_M_ALT;
807 if (cursor_info.key_mods & 0x02)
808 result |= KEY_M_CTRL;
809 if (cursor_info.key_mods & 0x01)
810 result |= KEY_M_SHIFT;
812 #endif /* __QNXNTO__ */
814 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
816 unsigned char modifiers = 6;
818 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
819 return 0;
821 /* Translate Linux modifiers into mc modifiers */
822 if (modifiers & SHIFT_PRESSED)
823 result |= KEY_M_SHIFT;
824 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
825 result |= KEY_M_ALT;
826 if (modifiers & CONTROL_PRESSED)
827 result |= KEY_M_CTRL;
829 #endif /* !__linux__ */
830 return result;
833 static gboolean
834 push_char (int c)
836 gboolean ret = FALSE;
838 if (seq_append == NULL)
839 seq_append = seq_buffer;
841 if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2])) {
842 *(seq_append++) = c;
843 *seq_append = 0;
844 ret = TRUE;
847 return ret;
850 /* Apply corrections for the keycode generated in get_key_code() */
851 static int
852 correct_key_code (int code)
854 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
855 unsigned int mod = code & KEY_M_MASK; /* modifier */
856 #ifdef __QNXNTO__
857 unsigned int qmod; /* bunch of the QNX console
858 modifiers needs unchanged */
859 #endif /* __QNXNTO__ */
862 * Add key modifiers directly from X11 or OS.
863 * Ordinary characters only get modifiers from sequences.
865 if (c < 32 || c >= 256) {
866 mod |= get_modifier ();
869 /* This is needed if the newline is reported as carriage return */
870 if (c == '\r')
871 c = '\n';
873 /* This is reported to be useful on AIX */
874 if (c == KEY_SCANCEL)
875 c = '\t';
877 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
878 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
879 c = KEY_BTAB;
880 mod = 0;
883 /* F0 is the same as F10 for out purposes */
884 if (c == KEY_F (0))
885 c = KEY_F (10);
888 * We are not interested if Ctrl was pressed when entering control
889 * characters, so assume that it was. When checking for such keys,
890 * XCTRL macro should be used. In some cases, we are interested,
891 * e.g. to distinguish Ctrl-Enter from Enter.
893 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
894 mod |= KEY_M_CTRL;
896 #ifdef __QNXNTO__
897 qmod = get_modifier ();
899 if ((c == 127) && (mod == 0)) { /* Add Ctrl/Alt/Shift-BackSpace */
900 mod |= get_modifier ();
901 c = KEY_BACKSPACE;
904 if ((c == '0') && (mod == 0)) { /* Add Shift-Insert on key pad */
905 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT) {
906 mod = KEY_M_SHIFT;
907 c = KEY_IC;
911 if ((c == '.') && (mod == 0)) { /* Add Shift-Del on key pad */
912 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT) {
913 mod = KEY_M_SHIFT;
914 c = KEY_DC;
917 #endif /* __QNXNTO__ */
919 /* Unrecognized 0177 is delete (preserve Ctrl) */
920 if (c == 0177) {
921 c = KEY_BACKSPACE;
924 /* Unrecognized Ctrl-d is delete */
925 if (c == (31 & 'd')) {
926 c = KEY_DC;
927 mod &= ~KEY_M_CTRL;
930 /* Unrecognized Ctrl-h is backspace */
931 if (c == (31 & 'h')) {
932 c = KEY_BACKSPACE;
933 mod &= ~KEY_M_CTRL;
936 /* Shift+BackSpace is backspace */
937 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
938 mod &= ~KEY_M_SHIFT;
941 /* Convert Shift+Fn to F(n+10) */
942 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
943 c += 10;
946 /* Remove Shift information from function keys */
947 if (c >= KEY_F (1) && c <= KEY_F (20)) {
948 mod &= ~KEY_M_SHIFT;
951 if (!alternate_plus_minus)
952 switch (c) {
953 case KEY_KP_ADD:
954 c = '+';
955 break;
956 case KEY_KP_SUBTRACT:
957 c = '-';
958 break;
959 case KEY_KP_MULTIPLY:
960 c = '*';
961 break;
964 return (mod | c);
967 static int
968 xgetch_second (void)
970 fd_set Read_FD_Set;
971 int c;
972 struct timeval timeout;
974 timeout.tv_sec = keyboard_key_timeout / 1000000;
975 timeout.tv_usec = keyboard_key_timeout % 1000000;
976 tty_nodelay (TRUE);
977 FD_ZERO (&Read_FD_Set);
978 FD_SET (input_fd, &Read_FD_Set);
979 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
980 c = tty_lowlevel_getch ();
981 tty_nodelay (FALSE);
982 return c;
985 static void
986 learn_store_key (char *buffer, char **p, int c)
988 if (*p - buffer > 253)
989 return;
990 if (c == ESC_CHAR) {
991 *(*p)++ = '\\';
992 *(*p)++ = 'e';
993 } else if (c < ' ') {
994 *(*p)++ = '^';
995 *(*p)++ = c + 'a' - 1;
996 } else if (c == '^') {
997 *(*p)++ = '^';
998 *(*p)++ = '^';
999 } else
1000 *(*p)++ = (char) c;
1003 static void
1004 k_dispose (key_def * k)
1006 if (k != NULL) {
1007 k_dispose (k->child);
1008 k_dispose (k->next);
1009 g_free (k);
1013 static void
1014 s_dispose (SelectList * sel)
1016 if (sel != NULL) {
1017 s_dispose (sel->next);
1018 g_free (sel);
1022 /*** public functions **************************************************/
1024 /* This has to be called before init_slang or whatever routine
1025 calls any define_sequence */
1026 void
1027 init_key (void)
1029 const char *term = getenv ("TERM");
1030 const char *kt = getenv ("KEYBOARD_KEY_TIMEOUT_US");
1031 if (kt != NULL)
1032 keyboard_key_timeout = atoi (kt);
1034 /* This has to be the first define_sequence */
1035 /* So, we can assume that the first keys member has ESC */
1036 define_sequences (mc_default_keys);
1038 /* Terminfo on irix does not have some keys */
1039 if (xterm_flag
1040 || (term != NULL
1041 && (strncmp (term, "iris-ansi", 9) == 0
1042 || strncmp (term, "xterm", 5) == 0
1043 || strncmp (term, "rxvt", 4) == 0 || strcmp (term, "screen") == 0)))
1044 define_sequences (xterm_key_defines);
1046 /* load some additional keys (e.g. direct Alt-? support) */
1047 load_xtra_key_defines ();
1049 #ifdef __QNX__
1050 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0)) {
1051 /* Modify the default value of use_8th_bit_as_meta: we would
1052 * like to provide a working mc for a newbie who knows nothing
1053 * about [Options|Display bits|Full 8 bits input]...
1055 * Don't use 'meta'-bit, when we are dealing with a
1056 * 'qnx*'-type terminal: clear the default value!
1057 * These terminal types use 0xFF as an escape character,
1058 * so use_8th_bit_as_meta==1 must not be enabled!
1060 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1061 * is not used now (doesn't even depend on use_8th_bit_as_meta
1062 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1064 use_8th_bit_as_meta = 0;
1066 #endif /* __QNX__ */
1068 init_key_x11 ();
1070 /* Load the qansi-m key definitions
1071 if we are running under the qansi-m terminal */
1072 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1073 define_sequences (qansi_key_defines);
1076 /* This has to be called after SLang_init_tty/slint_init */
1077 void
1078 init_key_input_fd (void)
1080 #ifdef HAVE_SLANG
1081 input_fd = SLang_TT_Read_FD;
1082 #endif
1085 void
1086 done_key (void)
1088 k_dispose (keys);
1089 s_dispose (select_list);
1091 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1092 if (x11_display)
1093 mc_XCloseDisplay (x11_display);
1094 #endif
1097 void
1098 add_select_channel (int fd, select_fn callback, void *info)
1100 SelectList *new;
1102 new = g_new (SelectList, 1);
1103 new->fd = fd;
1104 new->callback = callback;
1105 new->info = info;
1106 new->next = select_list;
1107 select_list = new;
1110 void
1111 delete_select_channel (int fd)
1113 SelectList *p = select_list;
1114 SelectList *p_prev = NULL;
1115 SelectList *p_next;
1117 while (p != NULL)
1118 if (p->fd == fd) {
1119 p_next = p->next;
1121 if (p_prev != NULL)
1122 p_prev->next = p_next;
1123 else
1124 select_list = p_next;
1126 g_free (p);
1127 p = p_next;
1128 } else {
1129 p_prev = p;
1130 p = p->next;
1134 void
1135 channels_up (void)
1137 if (disabled_channels == 0)
1138 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1139 disabled_channels--;
1142 void
1143 channels_down (void)
1145 disabled_channels++;
1149 * Common handler for standard movement keys in a text area. Provided
1150 * functions are called with the "data" argument. backfn and forfn also
1151 * get an argument indicating how many lines to scroll. Return MSG_HANDLED
1152 * if the key was handled, MSG_NOT_HANDLED otherwise.
1154 cb_ret_t
1155 check_movement_keys (int key, int page_size, void *data, move_fn backfn,
1156 move_fn forfn, move_fn topfn, move_fn bottomfn)
1158 switch (key) {
1159 case KEY_UP:
1160 case XCTRL ('p'):
1161 (*backfn) (data, 1);
1162 break;
1164 case KEY_DOWN:
1165 case XCTRL ('n'):
1166 (*forfn) (data, 1);
1167 break;
1169 case KEY_PPAGE:
1170 case ALT ('v'):
1171 (*backfn) (data, page_size - 1);
1172 break;
1174 case KEY_NPAGE:
1175 case XCTRL ('v'):
1176 (*forfn) (data, page_size - 1);
1177 break;
1179 case KEY_HOME:
1180 case KEY_M_CTRL | KEY_HOME:
1181 case KEY_M_CTRL | KEY_PPAGE:
1182 case KEY_A1:
1183 case ALT ('<'):
1184 (*topfn) (data, 0);
1185 break;
1187 case KEY_END:
1188 case KEY_M_CTRL | KEY_END:
1189 case KEY_M_CTRL | KEY_NPAGE:
1190 case KEY_C1:
1191 case ALT ('>'):
1192 (*bottomfn) (data, 0);
1193 break;
1195 case 'b':
1196 case KEY_BACKSPACE:
1197 (*backfn) (data, page_size - 1);
1198 break;
1200 case ' ':
1201 (*forfn) (data, page_size - 1);
1202 break;
1204 case 'u':
1205 (*backfn) (data, page_size / 2);
1206 break;
1208 case 'd':
1209 (*forfn) (data, page_size / 2);
1210 break;
1212 case 'g':
1213 (*topfn) (data, 0);
1214 break;
1216 case 'G':
1217 (*bottomfn) (data, 0);
1218 break;
1220 default:
1221 return MSG_NOT_HANDLED;
1223 return MSG_HANDLED;
1227 lookup_keyname (char *keyname)
1229 int i;
1231 if (keyname[0] == '\0')
1232 return 0;
1233 if (keyname[1] == '\0')
1234 return (int) keyname[0];
1236 for (i = 0; key_name_conv_tab[i].code; i++)
1237 if (str_casecmp (key_name_conv_tab[i].name, keyname) == 0)
1238 return key_name_conv_tab[i].code;
1240 return 0;
1243 /* Return the code associated with the symbolic name keyname */
1245 lookup_key (char *keyname)
1247 int k = -1;
1248 char **keys;
1249 guint keys_count = -1;
1250 int key = 0;
1251 int i = 0;
1253 if (keyname == NULL)
1254 return 0;
1256 keys = g_strsplit_set (keyname, "-+ ", -1);
1257 keys_count = g_strv_length (keys);
1258 for (i = keys_count - 1; i >= 0; i--) {
1259 if (keys[i] != NULL && keys[i][0] != 0) {
1260 g_strstrip(keys[i]);
1261 key = lookup_keyname (keys[i]);
1263 if (key & KEY_M_SHIFT) {
1264 if (k < 127) {
1265 k = (gchar) g_ascii_toupper ((gchar) k);
1266 continue;
1269 if (key & KEY_M_CTRL) {
1270 if (k < 256)
1271 k = XCTRL (k);
1272 else
1273 k |= key;
1274 } else {
1275 if (k == -1) {
1276 if (key < 127)
1277 key = (gchar) g_ascii_tolower ((gchar) key);
1278 k = key;
1279 } else
1280 k |= key;
1284 g_strfreev (keys);
1285 if (k == -1)
1286 return 0;
1288 return k;
1292 * Return TRUE on success, FALSE on error.
1293 * An error happens if SEQ is a beginning of an existing longer sequence.
1295 gboolean
1296 define_sequence (int code, const char *seq, int action)
1298 key_def *base;
1300 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1301 return FALSE;
1303 for (base = keys; (base != NULL) && (*seq != '\0');)
1304 if (*seq == base->ch) {
1305 if (base->child == 0) {
1306 if (*(seq + 1) != '\0')
1307 base->child = create_sequence (seq + 1, code, action);
1308 else {
1309 /* The sequence matches an existing one. */
1310 base->code = code;
1311 base->action = action;
1313 return TRUE;
1316 base = base->child;
1317 seq++;
1318 } else {
1319 if (base->next)
1320 base = base->next;
1321 else {
1322 base->next = create_sequence (seq, code, action);
1323 return TRUE;
1327 if (*seq == '\0') {
1328 /* Attempt to redefine a sequence with a shorter sequence. */
1329 return FALSE;
1332 keys = create_sequence (seq, code, action);
1333 return TRUE;
1337 * Check if we are idle, i.e. there are no pending keyboard or mouse
1338 * events. Return 1 is idle, 0 is there are pending events.
1340 gboolean
1341 is_idle (void)
1343 int maxfdp;
1344 fd_set select_set;
1345 struct timeval timeout;
1347 FD_ZERO (&select_set);
1348 FD_SET (input_fd, &select_set);
1349 maxfdp = input_fd;
1350 #ifdef HAVE_LIBGPM
1351 if (mouse_enabled && (use_mouse_p == MOUSE_GPM) && (gpm_fd > 0)) {
1352 FD_SET (gpm_fd, &select_set);
1353 maxfdp = max (maxfdp, gpm_fd);
1355 #endif
1356 timeout.tv_sec = 0;
1357 timeout.tv_usec = 0;
1358 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1362 get_key_code (int no_delay)
1364 int c;
1365 static key_def *this = NULL, *parent;
1366 static struct timeval esctime = { -1, -1 };
1367 static int lastnodelay = -1;
1369 if (no_delay != lastnodelay) {
1370 this = NULL;
1371 lastnodelay = no_delay;
1374 pend_send:
1375 if (pending_keys != NULL) {
1376 int d = *pending_keys++;
1377 check_pend:
1378 if (*pending_keys == 0) {
1379 pending_keys = NULL;
1380 seq_append = NULL;
1382 if ((d == ESC_CHAR) && (pending_keys != NULL)) {
1383 d = ALT (*pending_keys++);
1384 goto check_pend;
1386 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
1387 d = ALT (d & 0x7f);
1388 this = NULL;
1389 return correct_key_code (d);
1392 nodelay_try_again:
1393 if (no_delay)
1394 tty_nodelay (TRUE);
1396 c = tty_lowlevel_getch ();
1397 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1398 if (c == KEY_RESIZE)
1399 goto nodelay_try_again;
1400 #endif
1401 if (no_delay) {
1402 tty_nodelay (FALSE);
1403 if (c == -1) {
1404 if (this != NULL && parent != NULL && parent->action == MCKEY_ESCAPE && old_esc_mode) {
1405 struct timeval current, timeout;
1407 if (esctime.tv_sec == -1)
1408 return -1;
1409 GET_TIME (current);
1410 timeout.tv_sec = keyboard_key_timeout / 1000000 + esctime.tv_sec;
1411 timeout.tv_usec = keyboard_key_timeout % 1000000 + esctime.tv_usec;
1412 if (timeout.tv_usec > 1000000) {
1413 timeout.tv_usec -= 1000000;
1414 timeout.tv_sec++;
1416 if (current.tv_sec < timeout.tv_sec)
1417 return -1;
1418 if (current.tv_sec == timeout.tv_sec && current.tv_usec < timeout.tv_usec)
1419 return -1;
1420 this = NULL;
1421 pending_keys = seq_append = NULL;
1422 return ESC_CHAR;
1424 return -1;
1426 } else if (c == -1) {
1427 /* Maybe we got an incomplete match.
1428 This we do only in delay mode, since otherwise
1429 tty_lowlevel_getch can return -1 at any time. */
1430 if (seq_append != NULL) {
1431 pending_keys = seq_buffer;
1432 goto pend_send;
1434 this = NULL;
1435 return -1;
1438 /* Search the key on the root */
1439 if (!no_delay || this == NULL) {
1440 this = keys;
1441 parent = NULL;
1443 if ((c > 127 && c < 256) && use_8th_bit_as_meta) {
1444 c &= 0x7f;
1446 /* The first sequence defined starts with esc */
1447 parent = keys;
1448 this = keys->child;
1451 while (this != NULL) {
1452 if (c == this->ch) {
1453 if (this->child) {
1454 if (!push_char (c)) {
1455 pending_keys = seq_buffer;
1456 goto pend_send;
1458 parent = this;
1459 this = this->child;
1460 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
1461 if (no_delay) {
1462 GET_TIME (esctime);
1463 if (this == NULL) {
1464 /* Shouldn't happen */
1465 fputs ("Internal error\n", stderr);
1466 exit (1);
1468 goto nodelay_try_again;
1470 esctime.tv_sec = -1;
1471 c = xgetch_second ();
1472 if (c == -1) {
1473 pending_keys = seq_append = NULL;
1474 this = NULL;
1475 return ESC_CHAR;
1477 } else {
1478 if (no_delay)
1479 goto nodelay_try_again;
1480 c = tty_lowlevel_getch ();
1482 } else {
1483 /* We got a complete match, return and reset search */
1484 int code;
1486 pending_keys = seq_append = NULL;
1487 code = this->code;
1488 this = NULL;
1489 return correct_key_code (code);
1491 } else {
1492 if (this->next != NULL)
1493 this = this->next;
1494 else {
1495 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) {
1496 /* Convert escape-digits to F-keys */
1497 if (g_ascii_isdigit (c))
1498 c = KEY_F (c - '0');
1499 else if (c == ' ')
1500 c = ESC_CHAR;
1501 else
1502 c = ALT (c);
1504 pending_keys = seq_append = NULL;
1505 this = NULL;
1506 return correct_key_code (c);
1508 /* Did not find a match or {c} was changed in the if above,
1509 so we have to return everything we had skipped
1511 push_char (c);
1512 pending_keys = seq_buffer;
1513 goto pend_send;
1517 this = NULL;
1518 return correct_key_code (c);
1521 /* Returns a character read from stdin with appropriate interpretation */
1522 /* Also takes care of generated mouse events */
1523 /* Returns EV_MOUSE if it is a mouse event */
1524 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1526 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1528 int c;
1529 static int flag = 0; /* Return value from select */
1530 #ifdef HAVE_LIBGPM
1531 static struct Gpm_Event ev; /* Mouse event */
1532 #endif
1533 struct timeval timeout;
1534 struct timeval *time_addr = NULL;
1535 static int dirty = 3;
1537 if ((dirty == 3) || is_idle ()) {
1538 mc_refresh ();
1539 dirty = 1;
1540 } else
1541 dirty++;
1543 vfs_timeout_handler ();
1545 /* Ok, we use (event->x < 0) to signal that the event does not contain
1546 a suitable position for the mouse, so we can't use show_mouse_pointer
1547 on it.
1549 if (event->x > 0) {
1550 show_mouse_pointer (event->x, event->y);
1551 if (!redo_event)
1552 event->x = -1;
1555 /* Repeat if using mouse */
1556 while (pending_keys == NULL) {
1557 int maxfdp;
1558 fd_set select_set;
1560 FD_ZERO (&select_set);
1561 FD_SET (input_fd, &select_set);
1562 maxfdp = max (add_selects (&select_set), input_fd);
1564 #ifdef HAVE_LIBGPM
1565 if (mouse_enabled && (use_mouse_p == MOUSE_GPM)) {
1566 if (gpm_fd < 0) {
1567 /* Connection to gpm broken, possibly gpm has died */
1568 mouse_enabled = FALSE;
1569 use_mouse_p = MOUSE_NONE;
1570 break;
1573 FD_SET (gpm_fd, &select_set);
1574 maxfdp = max (maxfdp, gpm_fd);
1576 #endif
1578 if (redo_event) {
1579 timeout.tv_usec = mou_auto_repeat * 1000;
1580 timeout.tv_sec = 0;
1582 time_addr = &timeout;
1583 } else {
1584 int seconds;
1586 seconds = vfs_timeouts ();
1587 time_addr = NULL;
1589 if (seconds != 0) {
1590 /* the timeout could be improved and actually be
1591 * the number of seconds until the next vfs entry
1592 * timeouts in the stamp list.
1595 timeout.tv_sec = seconds;
1596 timeout.tv_usec = 0;
1597 time_addr = &timeout;
1601 if (!block || winch_flag) {
1602 time_addr = &timeout;
1603 timeout.tv_sec = 0;
1604 timeout.tv_usec = 0;
1607 tty_enable_interrupt_key ();
1608 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1609 tty_disable_interrupt_key ();
1611 /* select timed out: it could be for any of the following reasons:
1612 * redo_event -> it was because of the MOU_REPEAT handler
1613 * !block -> we did not block in the select call
1614 * else -> 10 second timeout to check the vfs status.
1616 if (flag == 0) {
1617 if (redo_event)
1618 return EV_MOUSE;
1619 if (!block || winch_flag)
1620 return EV_NONE;
1621 vfs_timeout_handler ();
1623 if (flag == -1 && errno == EINTR)
1624 return EV_NONE;
1626 check_selects (&select_set);
1628 if (FD_ISSET (input_fd, &select_set))
1629 break;
1630 #ifdef HAVE_LIBGPM
1631 if (mouse_enabled && use_mouse_p == MOUSE_GPM
1632 && gpm_fd > 0 && FD_ISSET (gpm_fd, &select_set)) {
1633 Gpm_GetEvent (&ev);
1634 Gpm_FitEvent (&ev);
1635 *event = ev;
1636 return EV_MOUSE;
1638 #endif /* !HAVE_LIBGPM */
1641 #ifndef HAVE_SLANG
1642 flag = is_wintouched (stdscr);
1643 untouchwin (stdscr);
1644 #endif /* !HAVE_SLANG */
1645 c = block ? getch_with_delay () : get_key_code (1);
1647 #ifndef HAVE_SLANG
1648 if (flag > 0)
1649 tty_touch_screen ();
1650 #endif /* !HAVE_SLANG */
1652 if (mouse_enabled && (c == MCKEY_MOUSE
1653 #ifdef KEY_MOUSE
1654 || c == KEY_MOUSE
1655 #endif /* KEY_MOUSE */
1656 )) {
1657 /* Mouse event */
1658 xmouse_get_event (event);
1659 return (event->type != 0) ? EV_MOUSE : EV_NONE;
1662 return c;
1665 /* Returns a key press, mouse events are discarded */
1667 tty_getch (void)
1669 Gpm_Event ev;
1670 int key;
1672 ev.x = -1;
1673 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE);
1674 return key;
1677 char *
1678 learn_key (void)
1680 /* LEARN_TIMEOUT in usec */
1681 #define LEARN_TIMEOUT 200000
1683 fd_set Read_FD_Set;
1684 struct timeval endtime;
1685 struct timeval timeout;
1686 int c;
1687 char buffer[256];
1688 char *p = buffer;
1690 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
1691 c = tty_lowlevel_getch ();
1692 while (c == -1)
1693 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
1694 learn_store_key (buffer, &p, c);
1695 GET_TIME (endtime);
1696 endtime.tv_usec += LEARN_TIMEOUT;
1697 if (endtime.tv_usec > 1000000) {
1698 endtime.tv_usec -= 1000000;
1699 endtime.tv_sec++;
1701 tty_nodelay (TRUE);
1702 for (;;) {
1703 while ((c = tty_lowlevel_getch ()) == -1) {
1704 GET_TIME (timeout);
1705 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1706 if (timeout.tv_usec < 0)
1707 timeout.tv_sec++;
1708 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1709 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1710 FD_ZERO (&Read_FD_Set);
1711 FD_SET (input_fd, &Read_FD_Set);
1712 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1713 } else
1714 break;
1716 if (c == -1)
1717 break;
1718 learn_store_key (buffer, &p, c);
1720 tty_keypad (TRUE);
1721 tty_nodelay (FALSE);
1722 *p = '\0';
1723 return g_strdup (buffer);
1724 #undef LEARN_TIMEOUT
1727 /* xterm and linux console only: set keypad to numeric or application
1728 mode. Only in application keypad mode it's possible to distinguish
1729 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1730 void
1731 numeric_keypad_mode (void)
1733 if (console_flag || xterm_flag) {
1734 fputs ("\033>", stdout);
1735 fflush (stdout);
1739 void
1740 application_keypad_mode (void)
1742 if (console_flag || xterm_flag) {
1743 fputs ("\033=", stdout);
1744 fflush (stdout);