Remove min() and max() macros. Use MIN() and MAX() macros from GLib.
[midnight-commander.git] / lib / tty / key.c
blob3a1fb263e77e078adf9a2d9c6f68f640ace90b2c
1 /*
2 Keyboard support routines.
4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995
9 Janne Kukonlehto, 1994, 1995
10 Jakub Jelinek, 1995
11 Norbert Warmuth, 1997
12 Denys Vlasenko <vda.linux@googlemail.com>, 2013
13 Slava Zanko <slavazanko@gmail.com>, 2013
14 Egmont Koblinger <egmont@gmail.com>, 2013
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 /** \file key.c
33 * \brief Source: keyboard support routines
36 #include <config.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <unistd.h>
47 #include "lib/global.h"
49 #include "lib/vfs/vfs.h"
51 #include "tty.h"
52 #include "tty-internal.h" /* mouse_enabled */
53 #include "mouse.h"
54 #include "key.h"
56 #include "lib/widget.h" /* mc_refresh() */
58 #ifdef HAVE_TEXTMODE_X11_SUPPORT
59 #include "x11conn.h"
60 #endif
62 #ifdef __linux__
63 #if defined(__GLIBC__) && (__GLIBC__ < 2)
64 #include <linux/termios.h> /* TIOCLINUX */
65 #else
66 #include <termios.h>
67 #endif
68 #ifdef HAVE_SYS_IOCTL_H
69 #include <sys/ioctl.h>
70 #endif
71 #endif /* __linux__ */
73 #ifdef __CYGWIN__
74 #include <termios.h>
75 #ifdef HAVE_SYS_IOCTL_H
76 #include <sys/ioctl.h>
77 #endif
78 #endif /* __CYGWIN__ */
80 #ifdef __QNXNTO__
81 #include <dlfcn.h>
82 #include <Ph.h>
83 #include <sys/dcmd_chr.h>
84 #endif /* __QNXNTO__ */
86 /*** global variables ****************************************************************************/
88 int mou_auto_repeat = 100;
89 int double_click_speed = 250;
90 int old_esc_mode = 0;
91 /* timeout for old_esc_mode in usec */
92 int old_esc_mode_timeout = 1000000; /* settable via env */
93 int use_8th_bit_as_meta = 0;
95 gboolean bracketed_pasting_in_progress = FALSE;
97 /* This table is a mapping between names and the constants we use
98 * We use this to allow users to define alternate definitions for
99 * certain keys that may be missing from the terminal database
101 const key_code_name_t key_name_conv_tab[] = {
102 {ESC_CHAR, "escape", N_("Escape"), "Esc"},
103 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
104 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
105 {KEY_F (1), "f1", N_("Function key 1"), "F1"},
106 {KEY_F (2), "f2", N_("Function key 2"), "F2"},
107 {KEY_F (3), "f3", N_("Function key 3"), "F3"},
108 {KEY_F (4), "f4", N_("Function key 4"), "F4"},
109 {KEY_F (5), "f5", N_("Function key 5"), "F5"},
110 {KEY_F (6), "f6", N_("Function key 6"), "F6"},
111 {KEY_F (7), "f7", N_("Function key 7"), "F7"},
112 {KEY_F (8), "f8", N_("Function key 8"), "F8"},
113 {KEY_F (9), "f9", N_("Function key 9"), "F9"},
114 {KEY_F (10), "f10", N_("Function key 10"), "F10"},
115 {KEY_F (11), "f11", N_("Function key 11"), "F11"},
116 {KEY_F (12), "f12", N_("Function key 12"), "F12"},
117 {KEY_F (13), "f13", N_("Function key 13"), "F13"},
118 {KEY_F (14), "f14", N_("Function key 14"), "F14"},
119 {KEY_F (15), "f15", N_("Function key 15"), "F15"},
120 {KEY_F (16), "f16", N_("Function key 16"), "F16"},
121 {KEY_F (17), "f17", N_("Function key 17"), "F17"},
122 {KEY_F (18), "f18", N_("Function key 18"), "F18"},
123 {KEY_F (19), "f19", N_("Function key 19"), "F19"},
124 {KEY_F (20), "f20", N_("Function key 20"), "F20"},
125 {ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab"},
126 {KEY_BTAB, "backtab", N_("BackTab/S-tab"), "Shift-Tab"},
127 {KEY_BACKSPACE, "backspace", N_("Backspace"), "Backspace"},
128 {KEY_UP, "up", N_("Up arrow"), "Up"},
129 {KEY_DOWN, "down", N_("Down arrow"), "Down"},
130 {KEY_LEFT, "left", N_("Left arrow"), "Left"},
131 {KEY_RIGHT, "right", N_("Right arrow"), "Right"},
132 {KEY_IC, "insert", N_("Insert"), "Ins"},
133 {KEY_DC, "delete", N_("Delete"), "Del"},
134 {KEY_HOME, "home", N_("Home"), "Home"},
135 {KEY_END, "end", N_("End key"), "End"},
136 {KEY_PPAGE, "pgup", N_("Page Up"), "PgUp"},
137 {KEY_NPAGE, "pgdn", N_("Page Down"), "PgDn"},
138 {(int) '/', "kpslash", N_("/ on keypad"), "/"},
139 {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*"},
140 {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-"},
141 {KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+"},
143 /* From here on, these won't be shown in Learn keys (no space) */
144 {KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left"},
145 {KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right"},
146 {KEY_UP, "kpup", N_("Up arrow keypad"), "Up"},
147 {KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down"},
148 {KEY_HOME, "kphome", N_("Home on keypad"), "Home"},
149 {KEY_END, "kpend", N_("End on keypad"), "End"},
150 {KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn"},
151 {KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp"},
152 {KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins"},
153 {KEY_DC, "kpdelete", N_("Delete on keypad"), "Del"},
154 {(int) '\n', "kpenter", N_("Enter on keypad"), "Enter"},
155 {KEY_F (21), "f21", N_("Function key 21"), "F21"},
156 {KEY_F (22), "f22", N_("Function key 22"), "F22"},
157 {KEY_F (23), "f23", N_("Function key 23"), "F23"},
158 {KEY_F (24), "f24", N_("Function key 24"), "F24"},
159 {KEY_A1, "a1", N_("A1 key"), "A1"},
160 {KEY_C1, "c1", N_("C1 key"), "C1"},
162 /* Alternative label */
163 {ESC_CHAR, "esc", N_("Escape"), "Esc"},
164 {KEY_BACKSPACE, "bs", N_("Backspace"), "Bakspace"},
165 {KEY_IC, "ins", N_("Insert"), "Ins"},
166 {KEY_DC, "del", N_("Delete"), "Del"},
167 {(int) '*', "asterisk", N_("Asterisk"), "*"},
168 {(int) '-', "minus", N_("Minus"), "-"},
169 {(int) '+', "plus", N_("Plus"), "+"},
170 {(int) '.', "dot", N_("Dot"), "."},
171 {(int) '<', "lt", N_("Less than"), "<"},
172 {(int) '>', "gt", N_("Great than"), ">"},
173 {(int) '=', "equal", N_("Equal"), "="},
174 {(int) ',', "comma", N_("Comma"), ","},
175 {(int) '\'', "apostrophe", N_("Apostrophe"), "\'"},
176 {(int) ':', "colon", N_("Colon"), ":"},
177 {(int) ';', "semicolon", N_("Semicolon"), ";"},
178 {(int) '!', "exclamation", N_("Exclamation mark"), "!"},
179 {(int) '?', "question", N_("Question mark"), "?"},
180 {(int) '&', "ampersand", N_("Ampersand"), "&"},
181 {(int) '$', "dollar", N_("Dollar sign"), "$"},
182 {(int) '"', "quota", N_("Quotation mark"), "\""},
183 {(int) '%', "percent", N_("Percent sign"), "%"},
184 {(int) '^', "caret", N_("Caret"), "^"},
185 {(int) '~', "tilda", N_("Tilda"), "~"},
186 {(int) '`', "prime", N_("Prime"), "`"},
187 {(int) '_', "underline", N_("Underline"), "_"},
188 {(int) '_', "understrike", N_("Understrike"), "_"},
189 {(int) '|', "pipe", N_("Pipe"), "|"},
190 {(int) '(', "lparenthesis", N_("Left parenthesis"), "("},
191 {(int) ')', "rparenthesis", N_("Right parenthesis"), ")"},
192 {(int) '[', "lbracket", N_("Left bracket"), "["},
193 {(int) ']', "rbracket", N_("Right bracket"), "]"},
194 {(int) '{', "lbrace", N_("Left brace"), "{"},
195 {(int) '}', "rbrace", N_("Right brace"), "}"},
196 {(int) '\n', "enter", N_("Enter"), "Enter"},
197 {(int) '\t', "tab", N_("Tab key"), "Tab"},
198 {(int) ' ', "space", N_("Space key"), "Space"},
199 {(int) '/', "slash", N_("Slash key"), "/"},
200 {(int) '\\', "backslash", N_("Backslash key"), "\\"},
201 {(int) '#', "number", N_("Number sign #"), "#"},
202 {(int) '#', "hash", N_("Number sign #"), "#"},
203 /* TRANSLATORS: Please translate as in "at sign" (@). */
204 {(int) '@', "at", N_("At sign"), "@"},
206 /* meta keys */
207 {KEY_M_CTRL, "control", N_("Ctrl"), "C"},
208 {KEY_M_CTRL, "ctrl", N_("Ctrl"), "C"},
209 {KEY_M_ALT, "meta", N_("Alt"), "M"},
210 {KEY_M_ALT, "alt", N_("Alt"), "M"},
211 {KEY_M_ALT, "ralt", N_("Alt"), "M"},
212 {KEY_M_SHIFT, "shift", N_("Shift"), "S"},
214 {0, NULL, NULL, NULL}
217 /*** file scope macro definitions ****************************************************************/
219 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
220 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
222 /* The maximum sequence length (32 + null terminator) */
223 #define SEQ_BUFFER_LEN 33
225 /*** file scope type declarations ****************************************************************/
227 /* Linux console keyboard modifiers */
228 typedef enum
230 SHIFT_PRESSED = (1 << 0),
231 ALTR_PRESSED = (1 << 1),
232 CONTROL_PRESSED = (1 << 2),
233 ALTL_PRESSED = (1 << 3)
234 } mod_pressed_t;
236 typedef struct key_def
238 char ch; /* Holds the matching char code */
239 int code; /* The code returned, valid if child == NULL */
240 struct key_def *next;
241 struct key_def *child; /* sequence continuation */
242 int action; /* optional action to be done. Now used only
243 to mark that we are just after the first
244 Escape */
245 } key_def;
247 typedef struct
249 int code;
250 const char *seq;
251 int action;
252 } key_define_t;
254 /* File descriptor monitoring add/remove routines */
255 typedef struct SelectList
257 int fd;
258 select_fn callback;
259 void *info;
260 struct SelectList *next;
261 } SelectList;
263 typedef enum KeySortType
265 KEY_NOSORT = 0,
266 KEY_SORTBYNAME,
267 KEY_SORTBYCODE
268 } KeySortType;
270 #ifdef __QNXNTO__
271 typedef int (*ph_dv_f) (void *, void *);
272 typedef int (*ph_ov_f) (void *);
273 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
274 #endif
276 /*** file scope variables ************************************************************************/
278 static key_define_t mc_default_keys[] = {
279 {ESC_CHAR, ESC_STR, MCKEY_ESCAPE},
280 {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION},
281 {MCKEY_BRACKETED_PASTING_START, ESC_STR "[200~", MCKEY_NOACTION},
282 {MCKEY_BRACKETED_PASTING_END, ESC_STR "[201~", MCKEY_NOACTION},
283 {0, NULL, MCKEY_NOACTION},
286 /* Broken terminfo and termcap databases on xterminals */
287 static key_define_t xterm_key_defines[] = {
288 {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION},
289 {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION},
290 {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION},
291 {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION},
292 {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION},
293 {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION},
294 {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION},
295 {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION},
296 {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION},
297 {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION},
298 {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION},
299 {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION},
300 {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION},
301 {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION},
303 /* old xterm Shift-arrows */
304 {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION},
305 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION},
306 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION},
307 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION},
309 /* new xterm Shift-arrows */
310 {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION},
311 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION},
312 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION},
313 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION},
315 /* more xterm keys with modifiers */
316 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION},
317 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION},
318 {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION},
319 {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION},
320 {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION},
321 {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION},
322 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION},
323 {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION},
324 {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION},
325 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION},
326 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION},
327 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION},
328 {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION},
329 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION},
330 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION},
331 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION},
332 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION},
333 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION},
334 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION},
336 /* putty */
337 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION},
338 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION},
339 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION},
340 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION},
342 /* putty alt-arrow keys */
343 /* removed as source esc esc esc trouble */
345 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
346 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
347 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
348 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
349 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
350 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
351 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
352 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
354 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
355 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
356 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
357 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
359 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
360 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
361 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
362 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
364 /* xterm alt-arrow keys */
365 {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION},
366 {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION},
367 {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION},
368 {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION},
369 {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION},
370 {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION},
371 {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION},
372 {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION},
373 {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION},
374 {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION},
375 {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION},
376 {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION},
377 {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION},
378 {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION},
379 {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION},
380 {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION},
382 /* rxvt keys with modifiers */
383 {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
384 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
385 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
386 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
387 {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
388 {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
389 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
390 {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
391 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
392 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
393 {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
394 {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
395 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
396 {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
397 {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
398 {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
399 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
401 /* konsole keys with modifiers */
402 {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
403 {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
405 /* gnome-terminal */
406 {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
407 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
408 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
409 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
410 {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
411 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
412 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
413 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
414 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
415 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
416 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
417 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
419 /* gnome-terminal - application mode */
420 {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
421 {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
422 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
423 {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
424 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
425 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
426 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
427 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
429 /* iTerm */
430 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
431 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
433 /* putty */
434 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
435 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
437 /* keypad keys */
438 {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
439 {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
440 {'/', ESC_STR "Oo", MCKEY_NOACTION},
441 {'\n', ESC_STR "OM", MCKEY_NOACTION},
443 {0, NULL, MCKEY_NOACTION},
446 /* qansi-m terminals have a much more key combinatios,
447 which are undefined in termcap/terminfo */
448 static key_define_t qansi_key_defines[] = {
449 /* qansi-m terminal */
450 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
451 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
452 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
453 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
454 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
455 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
456 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
457 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
458 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
459 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
460 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
461 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
462 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
463 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
464 {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
465 {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
466 {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
467 {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
468 {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
469 {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
470 {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
471 {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
472 {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
473 {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
474 {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
475 {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
476 {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
477 {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
478 {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
479 {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
480 {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
481 {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
482 {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
483 {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
484 {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
485 {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
486 {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
487 {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
488 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
489 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
490 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
491 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
492 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
493 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
494 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
495 {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */
496 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
497 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
498 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
499 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
500 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
501 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
502 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
503 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
504 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */
505 {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */
506 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
507 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
508 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
509 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
510 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
511 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
512 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
513 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
514 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
515 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
516 {0, NULL, MCKEY_NOACTION},
519 /* This holds all the key definitions */
520 static key_def *keys = NULL;
522 static int input_fd;
523 static int disabled_channels = 0; /* Disable channels checking */
525 static SelectList *select_list = NULL;
527 static int seq_buffer[SEQ_BUFFER_LEN];
528 static int *seq_append = NULL;
530 static int *pending_keys = NULL;
532 #ifdef __QNXNTO__
533 ph_dv_f ph_attach;
534 ph_ov_f ph_input_group;
535 ph_pqc_f ph_query_cursor;
536 #endif
538 #ifdef HAVE_TEXTMODE_X11_SUPPORT
539 static Display *x11_display;
540 static Window x11_window;
541 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
543 static KeySortType has_been_sorted = KEY_NOSORT;
545 /* *INDENT-OFF* */
546 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
547 /* *INDENT-ON* */
549 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
551 /*** file scope functions ************************************************************************/
552 /* --------------------------------------------------------------------------------------------- */
554 static int
555 add_selects (fd_set * select_set)
557 int top_fd = 0;
559 if (disabled_channels == 0)
561 SelectList *p;
563 for (p = select_list; p != NULL; p = p->next)
565 FD_SET (p->fd, select_set);
566 if (p->fd > top_fd)
567 top_fd = p->fd;
571 return top_fd;
574 /* --------------------------------------------------------------------------------------------- */
576 static void
577 check_selects (fd_set * select_set)
579 if (disabled_channels == 0)
581 gboolean retry;
585 SelectList *p;
587 retry = FALSE;
588 for (p = select_list; p; p = p->next)
589 if (FD_ISSET (p->fd, select_set))
591 FD_CLR (p->fd, select_set);
592 (*p->callback) (p->fd, p->info);
593 retry = TRUE;
594 break;
597 while (retry);
601 /* --------------------------------------------------------------------------------------------- */
602 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
604 static void
605 try_channels (int set_timeout)
607 struct timeval time_out;
608 static fd_set select_set;
610 while (1)
612 struct timeval *timeptr = NULL;
613 int maxfdp, v;
615 FD_ZERO (&select_set);
616 FD_SET (input_fd, &select_set); /* Add stdin */
617 maxfdp = MAX (add_selects (&select_set), input_fd);
619 if (set_timeout)
621 time_out.tv_sec = 0;
622 time_out.tv_usec = 100000;
623 timeptr = &time_out;
626 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
627 if (v > 0)
629 check_selects (&select_set);
630 if (FD_ISSET (input_fd, &select_set))
631 break;
636 /* --------------------------------------------------------------------------------------------- */
638 static key_def *
639 create_sequence (const char *seq, int code, int action)
641 key_def *base, *p, *attach;
643 for (base = attach = NULL; *seq; seq++)
645 p = g_new (key_def, 1);
646 if (base == NULL)
647 base = p;
648 if (attach != NULL)
649 attach->child = p;
651 p->ch = *seq;
652 p->code = code;
653 p->child = p->next = NULL;
654 if (seq[1] == '\0')
655 p->action = action;
656 else
657 p->action = MCKEY_NOACTION;
658 attach = p;
660 return base;
663 /* --------------------------------------------------------------------------------------------- */
665 static void
666 define_sequences (const key_define_t * kd)
668 int i;
670 for (i = 0; kd[i].code != 0; i++)
671 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
674 /* --------------------------------------------------------------------------------------------- */
676 #ifdef HAVE_TEXTMODE_X11_SUPPORT
677 static void
678 init_key_x11 (void)
680 if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
682 x11_display = mc_XOpenDisplay (0);
684 if (x11_display != NULL)
685 x11_window = DefaultRootWindow (x11_display);
688 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
690 /* --------------------------------------------------------------------------------------------- */
691 /* Workaround for System V Curses vt100 bug */
693 static int
694 getch_with_delay (void)
696 int c;
698 /* This routine could be used on systems without mouse support,
699 so we need to do the select check :-( */
700 while (1)
702 if (pending_keys == NULL)
703 try_channels (0);
705 /* Try to get a character */
706 c = get_key_code (0);
707 if (c != -1)
708 break;
709 /* Failed -> wait 0.1 secs and try again */
710 try_channels (1);
712 /* Success -> return the character */
713 return c;
716 /* --------------------------------------------------------------------------------------------- */
718 static void
719 xmouse_get_event (Gpm_Event * ev, gboolean extended)
721 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
722 static struct timeval tv2;
723 static int clicks = 0;
724 static int last_btn = 0;
725 int btn;
727 /* Decode Xterm mouse information to a GPM style event */
729 if (!extended)
731 /* Variable btn has following meaning: */
732 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
733 btn = tty_lowlevel_getch () - 32;
734 /* Coordinates are 33-based */
735 /* Transform them to 1-based */
736 ev->x = tty_lowlevel_getch () - 32;
737 ev->y = tty_lowlevel_getch () - 32;
739 else
741 /* SGR 1006 extension (e.g. "\e[<0;12;300M"):
742 - Numbers are encoded in decimal to make it ASCII-safe
743 and to overcome the limit of 223 columns/rows.
744 - Mouse release is encoded by trailing 'm' rather than 'M'
745 so that the released button can be reported.
746 - Numbers are no longer offset by 32. */
747 char c;
748 btn = ev->x = ev->y = 0;
749 ev->type = 0; /* In case we return on an invalid sequence */
750 while ((c = tty_lowlevel_getch ()) != ';')
752 if (c < '0' || c > '9')
753 return;
754 btn = 10 * btn + (c - '0');
756 while ((c = tty_lowlevel_getch ()) != ';')
758 if (c < '0' || c > '9')
759 return;
760 ev->x = 10 * ev->x + (c - '0');
762 while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
764 if (c < '0' || c > '9')
765 return;
766 ev->y = 10 * ev->y + (c - '0');
768 /* Legacy mouse protocol doesn't tell which button was released,
769 conveniently all of mc's widgets are written not to rely on this
770 information. With the SGR extension the released button becomes
771 known, but for the sake of simplicity we just ignore it. */
772 if (c == 'm')
773 btn = 3;
776 /* There seems to be no way of knowing which button was released */
777 /* So we assume all the buttons were released */
779 if (btn == 3)
781 if (last_btn != 0)
783 if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
785 /* FIXME: DIRTY HACK */
786 /* don't generate GPM_UP after mouse wheel */
787 /* need for menu event handling */
788 ev->type = 0;
789 tv1.tv_sec = 0;
790 tv1.tv_usec = 0;
792 else
794 ev->type = GPM_UP | (GPM_SINGLE << clicks);
795 GET_TIME (tv1);
797 ev->buttons = 0;
798 last_btn = 0;
799 clicks = 0;
801 else
803 /* Bogus event, maybe mouse wheel */
804 ev->type = 0;
807 else
809 if (btn >= 32 && btn <= 34)
811 btn -= 32;
812 ev->type = GPM_DRAG;
814 else
815 ev->type = GPM_DOWN;
817 GET_TIME (tv2);
818 if (tv1.tv_sec && (DIF_TIME (tv1, tv2) < double_click_speed))
820 clicks++;
821 clicks %= 3;
823 else
824 clicks = 0;
826 switch (btn)
828 case 0:
829 ev->buttons = GPM_B_LEFT;
830 break;
831 case 1:
832 ev->buttons = GPM_B_MIDDLE;
833 break;
834 case 2:
835 ev->buttons = GPM_B_RIGHT;
836 break;
837 case 64:
838 ev->buttons = GPM_B_UP;
839 clicks = 0;
840 break;
841 case 65:
842 ev->buttons = GPM_B_DOWN;
843 clicks = 0;
844 break;
845 default:
846 /* Nothing */
847 ev->type = 0;
848 ev->buttons = 0;
849 break;
851 last_btn = ev->buttons;
855 /* --------------------------------------------------------------------------------------------- */
857 * Get modifier state (shift, alt, ctrl) for the last key pressed.
858 * We are assuming that the state didn't change since the key press.
859 * This is only correct if get_modifier() is called very fast after
860 * the input was received, so that the user didn't release the
861 * modifier keys yet.
864 static int
865 get_modifier (void)
867 int result = 0;
868 #ifdef __QNXNTO__
869 int mod_status;
870 static int in_photon = 0;
871 static int ph_ig = 0;
872 PhCursorInfo_t cursor_info;
873 #endif /* __QNXNTO__ */
875 #ifdef HAVE_TEXTMODE_X11_SUPPORT
876 if (x11_window != 0)
878 Window root, child;
879 int root_x, root_y;
880 int win_x, win_y;
881 unsigned int mask;
883 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
884 &root_y, &win_x, &win_y, &mask);
886 if (mask & ShiftMask)
887 result |= KEY_M_SHIFT;
888 if (mask & ControlMask)
889 result |= KEY_M_CTRL;
890 return result;
892 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
893 #ifdef __QNXNTO__
895 if (in_photon == 0)
897 /* First time here, let's load Photon library and attach
898 to Photon */
899 in_photon = -1;
900 if (getenv ("PHOTON2_PATH") != NULL)
902 /* QNX 6.x has no support for RTLD_LAZY */
903 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
904 if (ph_handle != NULL)
906 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
907 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
908 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
909 if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL))
911 if ((*ph_attach) (0, 0))
912 { /* Attached */
913 ph_ig = (*ph_input_group) (0);
914 in_photon = 1;
920 /* We do not have Photon running. Assume we are in text
921 console or xterm */
922 if (in_photon == -1)
924 int shift_ext_status;
926 if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) ==
928 return 0;
930 shift_ext_status = mod_status & 0xffffff00UL;
931 mod_status &= 0x7f;
932 if (mod_status & _LINESTATUS_CON_ALT)
933 result |= KEY_M_ALT;
934 if (mod_status & _LINESTATUS_CON_CTRL)
935 result |= KEY_M_CTRL;
936 if ((mod_status & _LINESTATUS_CON_SHIFT) || (shift_ext_status & 0x00000800UL))
937 result |= KEY_M_SHIFT;
939 else
941 (*ph_query_cursor) (ph_ig, &cursor_info);
942 if (cursor_info.key_mods & 0x04)
943 result |= KEY_M_ALT;
944 if (cursor_info.key_mods & 0x02)
945 result |= KEY_M_CTRL;
946 if (cursor_info.key_mods & 0x01)
947 result |= KEY_M_SHIFT;
949 #endif /* __QNXNTO__ */
951 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
953 unsigned char modifiers = 6;
955 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
956 return 0;
958 /* Translate Linux modifiers into mc modifiers */
959 if (modifiers & SHIFT_PRESSED)
960 result |= KEY_M_SHIFT;
961 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
962 result |= KEY_M_ALT;
963 if (modifiers & CONTROL_PRESSED)
964 result |= KEY_M_CTRL;
966 #endif /* !__linux__ */
967 return result;
970 /* --------------------------------------------------------------------------------------------- */
972 static gboolean
973 push_char (int c)
975 gboolean ret = FALSE;
977 if (seq_append == NULL)
978 seq_append = seq_buffer;
980 if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
982 *(seq_append++) = c;
983 *seq_append = 0;
984 ret = TRUE;
987 return ret;
990 /* --------------------------------------------------------------------------------------------- */
991 /* Apply corrections for the keycode generated in get_key_code() */
993 static int
994 correct_key_code (int code)
996 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
997 unsigned int mod = code & KEY_M_MASK; /* modifier */
998 #ifdef __QNXNTO__
999 unsigned int qmod; /* bunch of the QNX console
1000 modifiers needs unchanged */
1001 #endif /* __QNXNTO__ */
1004 * Add key modifiers directly from X11 or OS.
1005 * Ordinary characters only get modifiers from sequences.
1007 if (c < 32 || c >= 256)
1009 mod |= get_modifier ();
1012 /* This is needed if the newline is reported as carriage return */
1013 if (c == '\r')
1014 c = '\n';
1016 /* This is reported to be useful on AIX */
1017 if (c == KEY_SCANCEL)
1018 c = '\t';
1020 /* Convert Back Tab to Shift+Tab */
1021 if (c == KEY_BTAB)
1023 c = '\t';
1024 mod = KEY_M_SHIFT;
1027 /* F0 is the same as F10 for out purposes */
1028 if (c == KEY_F (0))
1029 c = KEY_F (10);
1032 * We are not interested if Ctrl was pressed when entering control
1033 * characters, so assume that it was. When checking for such keys,
1034 * XCTRL macro should be used. In some cases, we are interested,
1035 * e.g. to distinguish Ctrl-Enter from Enter.
1037 if (c == '\b')
1039 /* Special case for backspase ('\b' < 32) */
1040 c = KEY_BACKSPACE;
1041 mod &= ~KEY_M_CTRL;
1043 else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
1045 mod |= KEY_M_CTRL;
1048 #ifdef __QNXNTO__
1049 qmod = get_modifier ();
1051 if ((c == 127) && (mod == 0))
1052 { /* Add Ctrl/Alt/Shift-BackSpace */
1053 mod |= get_modifier ();
1054 c = KEY_BACKSPACE;
1057 if ((c == '0') && (mod == 0))
1058 { /* Add Shift-Insert on key pad */
1059 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1061 mod = KEY_M_SHIFT;
1062 c = KEY_IC;
1066 if ((c == '.') && (mod == 0))
1067 { /* Add Shift-Del on key pad */
1068 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1070 mod = KEY_M_SHIFT;
1071 c = KEY_DC;
1074 #endif /* __QNXNTO__ */
1076 /* Unrecognized 0177 is delete (preserve Ctrl) */
1077 if (c == 0177)
1079 c = KEY_BACKSPACE;
1082 #if 0
1083 /* Unrecognized Ctrl-d is delete */
1084 if (c == (31 & 'd'))
1086 c = KEY_DC;
1087 mod &= ~KEY_M_CTRL;
1090 /* Unrecognized Ctrl-h is backspace */
1091 if (c == (31 & 'h'))
1093 c = KEY_BACKSPACE;
1094 mod &= ~KEY_M_CTRL;
1096 #endif
1098 /* Shift+BackSpace is backspace */
1099 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT))
1101 mod &= ~KEY_M_SHIFT;
1104 /* Convert Shift+Fn to F(n+10) */
1105 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT))
1107 c += 10;
1110 /* Remove Shift information from function keys */
1111 if (c >= KEY_F (1) && c <= KEY_F (20))
1113 mod &= ~KEY_M_SHIFT;
1116 if (!mc_global.tty.alternate_plus_minus)
1117 switch (c)
1119 case KEY_KP_ADD:
1120 c = '+';
1121 break;
1122 case KEY_KP_SUBTRACT:
1123 c = '-';
1124 break;
1125 case KEY_KP_MULTIPLY:
1126 c = '*';
1127 break;
1128 default:
1129 break;
1132 return (mod | c);
1135 /* --------------------------------------------------------------------------------------------- */
1137 static int
1138 getch_with_timeout (unsigned int delay_us)
1140 fd_set Read_FD_Set;
1141 int c;
1142 struct timeval time_out;
1144 time_out.tv_sec = delay_us / 1000000u;
1145 time_out.tv_usec = delay_us % 1000000u;
1146 tty_nodelay (TRUE);
1147 FD_ZERO (&Read_FD_Set);
1148 FD_SET (input_fd, &Read_FD_Set);
1149 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1150 c = tty_lowlevel_getch ();
1151 tty_nodelay (FALSE);
1152 return c;
1155 /* --------------------------------------------------------------------------------------------- */
1157 static void
1158 learn_store_key (char *buffer, char **p, int c)
1160 if (*p - buffer > 253)
1161 return;
1162 if (c == ESC_CHAR)
1164 *(*p)++ = '\\';
1165 *(*p)++ = 'e';
1167 else if (c < ' ')
1169 *(*p)++ = '^';
1170 *(*p)++ = c + 'a' - 1;
1172 else if (c == '^')
1174 *(*p)++ = '^';
1175 *(*p)++ = '^';
1177 else
1178 *(*p)++ = (char) c;
1181 /* --------------------------------------------------------------------------------------------- */
1183 static void
1184 k_dispose (key_def * k)
1186 if (k != NULL)
1188 k_dispose (k->child);
1189 k_dispose (k->next);
1190 g_free (k);
1194 /* --------------------------------------------------------------------------------------------- */
1196 static void
1197 s_dispose (SelectList * sel)
1199 if (sel != NULL)
1201 s_dispose (sel->next);
1202 g_free (sel);
1206 /* --------------------------------------------------------------------------------------------- */
1208 static int
1209 key_code_comparator_by_name (const void *p1, const void *p2)
1211 const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1212 const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1214 return g_ascii_strcasecmp (n1->name, n2->name);
1217 /* --------------------------------------------------------------------------------------------- */
1219 static int
1220 key_code_comparator_by_code (const void *p1, const void *p2)
1222 const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1223 const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1225 return n1->code - n2->code;
1228 /* --------------------------------------------------------------------------------------------- */
1230 static inline void
1231 sort_key_conv_tab (enum KeySortType type_sort)
1233 if (has_been_sorted != type_sort)
1235 size_t i;
1236 for (i = 0; i < key_conv_tab_size; i++)
1237 key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1239 if (type_sort == KEY_SORTBYNAME)
1241 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1242 &key_code_comparator_by_name);
1244 else if (type_sort == KEY_SORTBYCODE)
1246 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1247 &key_code_comparator_by_code);
1249 has_been_sorted = type_sort;
1253 /* --------------------------------------------------------------------------------------------- */
1255 static int
1256 lookup_keyname (const char *name, int *idx)
1258 if (name[0] != '\0')
1260 const key_code_name_t key = { 0, name, NULL, NULL };
1261 const key_code_name_t *keyp = &key;
1262 const key_code_name_t **res;
1264 if (name[1] == '\0')
1266 *idx = -1;
1267 return (int) name[0];
1270 sort_key_conv_tab (KEY_SORTBYNAME);
1272 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1273 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1275 if (res != NULL)
1277 *idx = (int) (res - key_conv_tab_sorted);
1278 return (*res)->code;
1282 *idx = -1;
1283 return 0;
1286 /* --------------------------------------------------------------------------------------------- */
1288 static gboolean
1289 lookup_keycode (const long code, int *idx)
1291 if (code != 0)
1293 const key_code_name_t key = { code, NULL, NULL, NULL };
1294 const key_code_name_t *keyp = &key;
1295 const key_code_name_t **res;
1297 sort_key_conv_tab (KEY_SORTBYCODE);
1299 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1300 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1302 if (res != NULL)
1304 *idx = (int) (res - key_conv_tab_sorted);
1305 return TRUE;
1309 *idx = -1;
1310 return FALSE;
1313 /* --------------------------------------------------------------------------------------------- */
1314 /*** public functions ****************************************************************************/
1315 /* --------------------------------------------------------------------------------------------- */
1316 /* This has to be called before init_slang or whatever routine
1317 calls any define_sequence */
1319 void
1320 init_key (void)
1322 const char *term = getenv ("TERM");
1324 /* This has to be the first define_sequence */
1325 /* So, we can assume that the first keys member has ESC */
1326 define_sequences (mc_default_keys);
1328 /* Terminfo on irix does not have some keys */
1329 if (mc_global.tty.xterm_flag
1330 || (term != NULL
1331 && (strncmp (term, "iris-ansi", 9) == 0
1332 || strncmp (term, "xterm", 5) == 0
1333 || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0)))
1334 define_sequences (xterm_key_defines);
1336 /* load some additional keys (e.g. direct Alt-? support) */
1337 load_xtra_key_defines ();
1339 #ifdef __QNX__
1340 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1342 /* Modify the default value of use_8th_bit_as_meta: we would
1343 * like to provide a working mc for a newbie who knows nothing
1344 * about [Options|Display bits|Full 8 bits input]...
1346 * Don't use 'meta'-bit, when we are dealing with a
1347 * 'qnx*'-type terminal: clear the default value!
1348 * These terminal types use 0xFF as an escape character,
1349 * so use_8th_bit_as_meta==1 must not be enabled!
1351 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1352 * is not used now (doesn't even depend on use_8th_bit_as_meta
1353 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1355 use_8th_bit_as_meta = 0;
1357 #endif /* __QNX__ */
1359 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1360 init_key_x11 ();
1361 #endif
1363 /* Load the qansi-m key definitions
1364 if we are running under the qansi-m terminal */
1365 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1366 define_sequences (qansi_key_defines);
1369 /* --------------------------------------------------------------------------------------------- */
1371 * This has to be called after SLang_init_tty/slint_init
1374 void
1375 init_key_input_fd (void)
1377 #ifdef HAVE_SLANG
1378 input_fd = SLang_TT_Read_FD;
1379 #endif
1382 /* --------------------------------------------------------------------------------------------- */
1384 void
1385 done_key (void)
1387 k_dispose (keys);
1388 s_dispose (select_list);
1390 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1391 if (x11_display)
1392 mc_XCloseDisplay (x11_display);
1393 #endif
1396 /* --------------------------------------------------------------------------------------------- */
1398 void
1399 add_select_channel (int fd, select_fn callback, void *info)
1401 SelectList *new;
1403 new = g_new (SelectList, 1);
1404 new->fd = fd;
1405 new->callback = callback;
1406 new->info = info;
1407 new->next = select_list;
1408 select_list = new;
1411 /* --------------------------------------------------------------------------------------------- */
1413 void
1414 delete_select_channel (int fd)
1416 SelectList *p = select_list;
1417 SelectList *p_prev = NULL;
1418 SelectList *p_next;
1420 while (p != NULL)
1421 if (p->fd == fd)
1423 p_next = p->next;
1425 if (p_prev != NULL)
1426 p_prev->next = p_next;
1427 else
1428 select_list = p_next;
1430 g_free (p);
1431 p = p_next;
1433 else
1435 p_prev = p;
1436 p = p->next;
1440 /* --------------------------------------------------------------------------------------------- */
1442 void
1443 channels_up (void)
1445 if (disabled_channels == 0)
1446 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1447 disabled_channels--;
1450 /* --------------------------------------------------------------------------------------------- */
1452 void
1453 channels_down (void)
1455 disabled_channels++;
1458 /* --------------------------------------------------------------------------------------------- */
1460 * Return the code associated with the symbolic name keyname
1463 long
1464 lookup_key (const char *name, char **label)
1466 char **lc_keys, **p;
1467 char *cname;
1468 int k = -1;
1469 int key = 0;
1470 int lc_index = -1;
1472 int use_meta = -1;
1473 int use_ctrl = -1;
1474 int use_shift = -1;
1476 if (name == NULL)
1477 return 0;
1479 cname = g_strstrip (g_strdup (name));
1480 lc_keys = g_strsplit_set (cname, "-+ ", -1);
1481 g_free (cname);
1483 for (p = lc_keys; p != NULL && *p != NULL; p++)
1485 if ((*p)[0] != '\0')
1487 int idx;
1489 key = lookup_keyname (g_strstrip (*p), &idx);
1491 if (key == KEY_M_ALT)
1492 use_meta = idx;
1493 else if (key == KEY_M_CTRL)
1494 use_ctrl = idx;
1495 else if (key == KEY_M_SHIFT)
1496 use_shift = idx;
1497 else
1499 k = key;
1500 lc_index = idx;
1501 break;
1506 g_strfreev (lc_keys);
1508 /* output */
1509 if (k <= 0)
1510 return 0;
1512 if (label != NULL)
1514 GString *s;
1516 s = g_string_new ("");
1518 if (use_meta != -1)
1520 g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1521 g_string_append_c (s, '-');
1523 if (use_ctrl != -1)
1525 g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1526 g_string_append_c (s, '-');
1528 if (use_shift != -1)
1530 if (k < 127)
1531 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1532 else
1534 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1535 g_string_append_c (s, '-');
1536 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1539 else if (k < 128)
1541 if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1542 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1543 else
1544 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1546 else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1547 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1548 else
1549 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1551 *label = g_string_free (s, FALSE);
1554 if (use_shift != -1)
1556 if (k < 127 && k > 31)
1557 k = g_ascii_toupper ((gchar) k);
1558 else
1559 k |= KEY_M_SHIFT;
1562 if (use_ctrl != -1)
1564 if (k < 256)
1565 k = XCTRL (k);
1566 else
1567 k |= KEY_M_CTRL;
1570 if (use_meta != -1)
1571 k = ALT (k);
1573 return (long) k;
1576 /* --------------------------------------------------------------------------------------------- */
1578 char *
1579 lookup_key_by_code (const int keycode)
1581 /* code without modifier */
1582 unsigned int k = keycode & ~KEY_M_MASK;
1583 /* modifier */
1584 unsigned int mod = keycode & KEY_M_MASK;
1586 int key_idx = -1;
1588 GString *s;
1589 int idx;
1591 s = g_string_sized_new (8);
1593 if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1595 if (mod & KEY_M_ALT)
1597 if (lookup_keycode (KEY_M_ALT, &idx))
1599 g_string_append (s, key_conv_tab_sorted[idx]->name);
1600 g_string_append_c (s, '-');
1603 if (mod & KEY_M_CTRL)
1605 /* non printeble chars like a CTRL-[A..Z] */
1606 if (k < 32)
1607 k += 64;
1609 if (lookup_keycode (KEY_M_CTRL, &idx))
1611 g_string_append (s, key_conv_tab_sorted[idx]->name);
1612 g_string_append_c (s, '-');
1615 if (mod & KEY_M_SHIFT)
1617 if (lookup_keycode (KEY_M_ALT, &idx))
1619 if (k < 127)
1620 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1621 else
1623 g_string_append (s, key_conv_tab_sorted[idx]->name);
1624 g_string_append_c (s, '-');
1625 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1629 else if (k < 128)
1631 if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1632 g_string_append_c (s, (gchar) k);
1633 else
1634 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1636 else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1637 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1638 else
1639 g_string_append_c (s, (gchar) keycode);
1642 return g_string_free (s, s->len == 0);
1645 /* --------------------------------------------------------------------------------------------- */
1647 * Return TRUE on success, FALSE on error.
1648 * An error happens if SEQ is a beginning of an existing longer sequence.
1651 gboolean
1652 define_sequence (int code, const char *seq, int action)
1654 key_def *base;
1656 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1657 return FALSE;
1659 for (base = keys; (base != NULL) && (*seq != '\0');)
1660 if (*seq == base->ch)
1662 if (base->child == 0)
1664 if (*(seq + 1) != '\0')
1665 base->child = create_sequence (seq + 1, code, action);
1666 else
1668 /* The sequence matches an existing one. */
1669 base->code = code;
1670 base->action = action;
1672 return TRUE;
1675 base = base->child;
1676 seq++;
1678 else
1680 if (base->next)
1681 base = base->next;
1682 else
1684 base->next = create_sequence (seq, code, action);
1685 return TRUE;
1689 if (*seq == '\0')
1691 /* Attempt to redefine a sequence with a shorter sequence. */
1692 return FALSE;
1695 keys = create_sequence (seq, code, action);
1696 return TRUE;
1699 /* --------------------------------------------------------------------------------------------- */
1701 * Check if we are idle, i.e. there are no pending keyboard or mouse
1702 * events. Return 1 is idle, 0 is there are pending events.
1704 gboolean
1705 is_idle (void)
1707 int nfd;
1708 fd_set select_set;
1709 struct timeval time_out;
1711 FD_ZERO (&select_set);
1712 FD_SET (input_fd, &select_set);
1713 nfd = MAX (0, input_fd) + 1;
1714 time_out.tv_sec = 0;
1715 time_out.tv_usec = 0;
1716 #ifdef HAVE_LIBGPM
1717 if (mouse_enabled && use_mouse_p == MOUSE_GPM)
1719 if (gpm_fd >= 0)
1721 FD_SET (gpm_fd, &select_set);
1722 nfd = MAX (nfd, gpm_fd + 1);
1724 else
1726 if (mouse_fd >= 0) /* error indicative */
1728 if (FD_ISSET (mouse_fd, &select_set))
1729 FD_CLR (mouse_fd, &select_set);
1730 mouse_fd = gpm_fd;
1732 /* gpm_fd == -2 means under some X terminal */
1733 if (gpm_fd == -1)
1735 mouse_enabled = FALSE;
1736 use_mouse_p = MOUSE_NONE;
1740 #endif
1741 return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1744 /* --------------------------------------------------------------------------------------------- */
1747 get_key_code (int no_delay)
1749 int c;
1750 static key_def *this = NULL, *parent;
1751 static struct timeval esctime = { -1, -1 };
1752 static int lastnodelay = -1;
1754 if (no_delay != lastnodelay)
1756 this = NULL;
1757 lastnodelay = no_delay;
1760 pend_send:
1761 if (pending_keys != NULL)
1763 int d;
1764 gboolean bad_seq;
1766 d = *pending_keys++;
1767 while (d == ESC_CHAR)
1768 d = ALT (*pending_keys++);
1770 bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != 0);
1771 if (*pending_keys == '\0' || bad_seq)
1772 pending_keys = seq_append = NULL;
1774 if (bad_seq)
1776 /* This is an unknown ESC sequence.
1777 * To prevent interpreting its tail as a random garbage,
1778 * eat and discard all buffered and quickly following chars.
1779 * Small, but non-zero timeout is needed to reconnect
1780 * escape sequence split up by e.g. a serial line.
1782 int paranoia = 20;
1784 while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1786 goto nodelay_try_again;
1789 if (d > 127 && d < 256 && use_8th_bit_as_meta)
1790 d = ALT (d & 0x7f);
1792 this = NULL;
1793 return correct_key_code (d);
1796 nodelay_try_again:
1797 if (no_delay)
1798 tty_nodelay (TRUE);
1800 c = tty_lowlevel_getch ();
1801 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1802 if (c == KEY_RESIZE)
1803 goto nodelay_try_again;
1804 #endif
1805 if (no_delay)
1807 tty_nodelay (FALSE);
1808 if (c == -1)
1810 if (this != NULL && parent != NULL && parent->action == MCKEY_ESCAPE && old_esc_mode)
1812 struct timeval current, time_out;
1814 if (esctime.tv_sec == -1)
1815 return -1;
1816 GET_TIME (current);
1817 time_out.tv_sec = old_esc_mode_timeout / 1000000 + esctime.tv_sec;
1818 time_out.tv_usec = old_esc_mode_timeout % 1000000 + esctime.tv_usec;
1819 if (time_out.tv_usec > 1000000)
1821 time_out.tv_usec -= 1000000;
1822 time_out.tv_sec++;
1824 if (current.tv_sec < time_out.tv_sec)
1825 return -1;
1826 if (current.tv_sec == time_out.tv_sec && current.tv_usec < time_out.tv_usec)
1827 return -1;
1828 this = NULL;
1829 pending_keys = seq_append = NULL;
1830 return ESC_CHAR;
1832 return -1;
1835 else if (c == -1)
1837 /* Maybe we got an incomplete match.
1838 This we do only in delay mode, since otherwise
1839 tty_lowlevel_getch can return -1 at any time. */
1840 if (seq_append != NULL)
1842 pending_keys = seq_buffer;
1843 goto pend_send;
1845 this = NULL;
1846 return -1;
1849 /* Search the key on the root */
1850 if (!no_delay || this == NULL)
1852 this = keys;
1853 parent = NULL;
1855 if ((c > 127 && c < 256) && use_8th_bit_as_meta)
1857 c &= 0x7f;
1859 /* The first sequence defined starts with esc */
1860 parent = keys;
1861 this = keys->child;
1865 while (this != NULL)
1867 if (c == this->ch)
1869 if (this->child == NULL)
1871 /* We got a complete match, return and reset search */
1872 int code;
1874 pending_keys = seq_append = NULL;
1875 code = this->code;
1876 this = NULL;
1877 return correct_key_code (code);
1879 /* No match yet, but it may be a prefix for a valid seq */
1880 if (!push_char (c))
1882 pending_keys = seq_buffer;
1883 goto pend_send;
1885 parent = this;
1886 this = this->child;
1887 if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1889 if (no_delay)
1891 GET_TIME (esctime);
1892 goto nodelay_try_again;
1894 esctime.tv_sec = -1;
1895 c = getch_with_timeout (old_esc_mode_timeout);
1896 if (c == -1)
1898 pending_keys = seq_append = NULL;
1899 this = NULL;
1900 return ESC_CHAR;
1902 continue;
1904 if (no_delay)
1905 goto nodelay_try_again;
1906 c = tty_lowlevel_getch ();
1907 continue;
1910 /* c != this->ch. Try other keys with this prefix */
1911 if (this->next != NULL)
1913 this = this->next;
1914 continue;
1917 /* No match found. Is it one of our ESC <key> specials? */
1918 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1920 /* Convert escape-digits to F-keys */
1921 if (g_ascii_isdigit (c))
1922 c = KEY_F (c - '0');
1923 else if (c == ' ')
1924 c = ESC_CHAR;
1925 else
1926 c = ALT (c);
1928 pending_keys = seq_append = NULL;
1929 this = NULL;
1930 return correct_key_code (c);
1933 /* Unknown sequence. Maybe a prefix of a longer one. Save it. */
1934 push_char (c);
1935 pending_keys = seq_buffer;
1936 goto pend_send;
1938 } /* while (this != NULL) */
1940 this = NULL;
1941 return correct_key_code (c);
1944 /* --------------------------------------------------------------------------------------------- */
1945 /* Returns a character read from stdin with appropriate interpretation */
1946 /* Also takes care of generated mouse events */
1947 /* Returns EV_MOUSE if it is a mouse event */
1948 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1951 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1953 int c;
1954 int flag = 0; /* Return value from select */
1955 #ifdef HAVE_LIBGPM
1956 static struct Gpm_Event ev; /* Mouse event */
1957 #endif
1958 struct timeval time_out;
1959 struct timeval *time_addr = NULL;
1960 static int dirty = 3;
1962 if ((dirty == 3) || is_idle ())
1964 mc_refresh ();
1965 dirty = 1;
1967 else
1968 dirty++;
1970 vfs_timeout_handler ();
1972 /* Ok, we use (event->x < 0) to signal that the event does not contain
1973 a suitable position for the mouse, so we can't use show_mouse_pointer
1974 on it.
1976 if (event->x > 0)
1978 show_mouse_pointer (event->x, event->y);
1979 if (!redo_event)
1980 event->x = -1;
1983 /* Repeat if using mouse */
1984 while (pending_keys == NULL)
1986 int nfd;
1987 fd_set select_set;
1989 FD_ZERO (&select_set);
1990 FD_SET (input_fd, &select_set);
1991 nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1993 #ifdef HAVE_LIBGPM
1994 if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1996 if (gpm_fd >= 0)
1998 FD_SET (gpm_fd, &select_set);
1999 nfd = MAX (nfd, gpm_fd + 1);
2001 else
2003 if (mouse_fd >= 0) /* error indicative */
2005 if (FD_ISSET (mouse_fd, &select_set))
2006 FD_CLR (mouse_fd, &select_set);
2007 mouse_fd = gpm_fd;
2009 /* gpm_fd == -2 means under some X terminal */
2010 if (gpm_fd == -1)
2012 mouse_enabled = FALSE;
2013 use_mouse_p = MOUSE_NONE;
2015 break;
2018 #endif
2020 if (redo_event)
2022 time_out.tv_usec = mou_auto_repeat * 1000;
2023 time_out.tv_sec = 0;
2025 time_addr = &time_out;
2027 else
2029 int seconds;
2031 seconds = vfs_timeouts ();
2032 time_addr = NULL;
2034 if (seconds != 0)
2036 /* the timeout could be improved and actually be
2037 * the number of seconds until the next vfs entry
2038 * timeouts in the stamp list.
2041 time_out.tv_sec = seconds;
2042 time_out.tv_usec = 0;
2043 time_addr = &time_out;
2047 if (!block || mc_global.tty.winch_flag != 0)
2049 time_addr = &time_out;
2050 time_out.tv_sec = 0;
2051 time_out.tv_usec = 0;
2054 tty_enable_interrupt_key ();
2055 flag = select (nfd, &select_set, NULL, NULL, time_addr);
2056 tty_disable_interrupt_key ();
2058 /* select timed out: it could be for any of the following reasons:
2059 * redo_event -> it was because of the MOU_REPEAT handler
2060 * !block -> we did not block in the select call
2061 * else -> 10 second timeout to check the vfs status.
2063 if (flag == 0)
2065 if (redo_event)
2066 return EV_MOUSE;
2067 if (!block || mc_global.tty.winch_flag != 0)
2068 return EV_NONE;
2069 vfs_timeout_handler ();
2071 if (flag == -1 && errno == EINTR)
2072 return EV_NONE;
2074 check_selects (&select_set);
2076 if (FD_ISSET (input_fd, &select_set))
2077 break;
2078 #ifdef HAVE_LIBGPM
2079 if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2081 if (gpm_fd >= 0)
2083 if (FD_ISSET (gpm_fd, &select_set))
2085 int status;
2087 status = Gpm_GetEvent (&ev);
2088 if (status == 1) /* success */
2090 Gpm_FitEvent (&ev);
2091 *event = ev;
2092 return EV_MOUSE;
2094 else if (status == 0) /* connection closed; -1 == error */
2096 if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2097 FD_CLR (mouse_fd, &select_set);
2099 /* Try to reopen gpm_mouse connection */
2100 disable_mouse ();
2101 enable_mouse ();
2105 else
2107 if (mouse_fd >= 0) /* error indicative */
2109 if (FD_ISSET (mouse_fd, &select_set))
2110 FD_CLR (mouse_fd, &select_set);
2111 mouse_fd = gpm_fd;
2113 /* gpm_fd == -2 means under some X terminal */
2114 if (gpm_fd == -1)
2116 mouse_enabled = FALSE;
2117 use_mouse_p = MOUSE_NONE;
2119 break;
2122 #endif /* !HAVE_LIBGPM */
2125 #ifndef HAVE_SLANG
2126 flag = is_wintouched (stdscr);
2127 untouchwin (stdscr);
2128 #endif /* !HAVE_SLANG */
2129 c = block ? getch_with_delay () : get_key_code (1);
2131 #ifndef HAVE_SLANG
2132 if (flag > 0)
2133 tty_touch_screen ();
2134 #endif /* !HAVE_SLANG */
2136 if (mouse_enabled && (c == MCKEY_MOUSE
2137 #ifdef KEY_MOUSE
2138 || c == KEY_MOUSE
2139 #endif /* KEY_MOUSE */
2140 || c == MCKEY_EXTENDED_MOUSE))
2142 /* Mouse event */
2143 xmouse_get_event (event, c == MCKEY_EXTENDED_MOUSE);
2144 c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2146 else if (c == MCKEY_BRACKETED_PASTING_START)
2148 bracketed_pasting_in_progress = TRUE;
2149 c = EV_NONE;
2151 else if (c == MCKEY_BRACKETED_PASTING_END)
2153 bracketed_pasting_in_progress = FALSE;
2154 c = EV_NONE;
2157 return c;
2160 /* --------------------------------------------------------------------------------------------- */
2161 /* Returns a key press, mouse events are discarded */
2164 tty_getch (void)
2166 Gpm_Event ev;
2167 int key;
2169 ev.x = -1;
2170 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE);
2171 return key;
2174 /* --------------------------------------------------------------------------------------------- */
2176 char *
2177 learn_key (void)
2179 /* LEARN_TIMEOUT in usec */
2180 #define LEARN_TIMEOUT 200000
2182 fd_set Read_FD_Set;
2183 struct timeval endtime;
2184 struct timeval time_out;
2185 int c;
2186 char buffer[256];
2187 char *p = buffer;
2189 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
2190 c = tty_lowlevel_getch ();
2191 while (c == -1)
2192 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
2193 learn_store_key (buffer, &p, c);
2194 GET_TIME (endtime);
2195 endtime.tv_usec += LEARN_TIMEOUT;
2196 if (endtime.tv_usec > 1000000)
2198 endtime.tv_usec -= 1000000;
2199 endtime.tv_sec++;
2201 tty_nodelay (TRUE);
2202 while (TRUE)
2204 while ((c = tty_lowlevel_getch ()) == -1)
2206 GET_TIME (time_out);
2207 time_out.tv_usec = endtime.tv_usec - time_out.tv_usec;
2208 if (time_out.tv_usec < 0)
2209 time_out.tv_sec++;
2210 time_out.tv_sec = endtime.tv_sec - time_out.tv_sec;
2211 if (time_out.tv_sec >= 0 && time_out.tv_usec > 0)
2213 FD_ZERO (&Read_FD_Set);
2214 FD_SET (input_fd, &Read_FD_Set);
2215 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
2217 else
2218 break;
2220 if (c == -1)
2221 break;
2222 learn_store_key (buffer, &p, c);
2224 tty_keypad (TRUE);
2225 tty_nodelay (FALSE);
2226 *p = '\0';
2227 return g_strdup (buffer);
2228 #undef LEARN_TIMEOUT
2231 /* --------------------------------------------------------------------------------------------- */
2232 /* xterm and linux console only: set keypad to numeric or application
2233 mode. Only in application keypad mode it's possible to distinguish
2234 the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2236 void
2237 numeric_keypad_mode (void)
2239 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2241 fputs (ESC_STR ">", stdout);
2242 fflush (stdout);
2246 /* --------------------------------------------------------------------------------------------- */
2248 void
2249 application_keypad_mode (void)
2251 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2253 fputs (ESC_STR "=", stdout);
2254 fflush (stdout);
2258 /* --------------------------------------------------------------------------------------------- */
2260 void
2261 enable_bracketed_paste (void)
2263 printf (ESC_STR "[?2004h");
2264 fflush (stdout);
2267 /* --------------------------------------------------------------------------------------------- */
2269 void
2270 disable_bracketed_paste (void)
2272 printf (ESC_STR "[?2004l");
2273 fflush (stdout);
2274 bracketed_pasting_in_progress = FALSE;
2277 /* --------------------------------------------------------------------------------------------- */