Ticket #2637: faster startup of mc.
[midnight-commander.git] / lib / tty / key.c
blob57c1caf919e7b8b6de2a97db7d02eb7a61f6f07e
1 /*
2 Keyboard support routines.
4 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
5 2005, 2006, 2007, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Norbert Warmuth, 1997
14 This file is part of the Midnight Commander.
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 /** \file key.c
31 * \brief Source: keyboard support routines
34 #include <config.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <unistd.h>
45 #include "lib/global.h"
47 #include "lib/vfs/vfs.h"
49 #include "tty.h"
50 #include "tty-internal.h" /* mouse_enabled */
51 #include "mouse.h"
52 #include "key.h"
54 #include "lib/widget.h" /* mc_refresh() */
56 #ifdef HAVE_TEXTMODE_X11_SUPPORT
57 #include "x11conn.h"
58 #endif
60 #ifdef __linux__
61 #if defined(__GLIBC__) && (__GLIBC__ < 2)
62 #include <linux/termios.h> /* TIOCLINUX */
63 #else
64 #include <termios.h>
65 #endif
66 #include <sys/ioctl.h>
67 #endif /* __linux__ */
69 #ifdef __CYGWIN__
70 #include <termios.h>
71 #include <sys/ioctl.h>
72 #endif /* __CYGWIN__ */
74 #ifdef __QNXNTO__
75 #include <dlfcn.h>
76 #include <Ph.h>
77 #include <sys/dcmd_chr.h>
78 #endif /* __QNXNTO__ */
80 /*** global variables ****************************************************************************/
82 int mou_auto_repeat = 100;
83 int double_click_speed = 250;
84 int old_esc_mode = 0;
85 int use_8th_bit_as_meta = 0;
87 /* This table is a mapping between names and the constants we use
88 * We use this to allow users to define alternate definitions for
89 * certain keys that may be missing from the terminal database
91 const key_code_name_t key_name_conv_tab[] = {
92 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
93 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
94 {KEY_F (1), "f1", N_("Function key 1"), "F1"},
95 {KEY_F (2), "f2", N_("Function key 2"), "F2"},
96 {KEY_F (3), "f3", N_("Function key 3"), "F3"},
97 {KEY_F (4), "f4", N_("Function key 4"), "F4"},
98 {KEY_F (5), "f5", N_("Function key 5"), "F5"},
99 {KEY_F (6), "f6", N_("Function key 6"), "F6"},
100 {KEY_F (7), "f7", N_("Function key 7"), "F7"},
101 {KEY_F (8), "f8", N_("Function key 8"), "F8"},
102 {KEY_F (9), "f9", N_("Function key 9"), "F9"},
103 {KEY_F (10), "f10", N_("Function key 10"), "F10"},
104 {KEY_F (11), "f11", N_("Function key 11"), "F11"},
105 {KEY_F (12), "f12", N_("Function key 12"), "F12"},
106 {KEY_F (13), "f13", N_("Function key 13"), "F13"},
107 {KEY_F (14), "f14", N_("Function key 14"), "F14"},
108 {KEY_F (15), "f15", N_("Function key 15"), "F15"},
109 {KEY_F (16), "f16", N_("Function key 16"), "F16"},
110 {KEY_F (17), "f17", N_("Function key 17"), "F17"},
111 {KEY_F (18), "f18", N_("Function key 18"), "F18"},
112 {KEY_F (19), "f19", N_("Function key 19"), "F19"},
113 {KEY_F (20), "f20", N_("Function key 20"), "F20"},
114 {KEY_BACKSPACE, "backspace", N_("Backspace key"), "Backspace"},
115 {KEY_END, "end", N_("End key"), "End"},
116 {KEY_UP, "up", N_("Up arrow key"), "Up"},
117 {KEY_DOWN, "down", N_("Down arrow key"), "Down"},
118 {KEY_LEFT, "left", N_("Left arrow key"), "Left"},
119 {KEY_RIGHT, "right", N_("Right arrow key"), "Right"},
120 {KEY_HOME, "home", N_("Home key"), "Home"},
121 {KEY_NPAGE, "pgdn", N_("Page Down key"), "PgDn"},
122 {KEY_PPAGE, "pgup", N_("Page Up key"), "PgUp"},
123 {KEY_IC, "insert", N_("Insert key"), "Ins"},
124 {KEY_DC, "delete", N_("Delete key"), "Del"},
125 {ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab"},
126 {KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+"},
127 {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-"},
128 {(int) '/', "kpslash", N_("Slash on keypad"), "/"},
129 {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*"},
131 /* From here on, these won't be shown in Learn keys (no space) */
132 {ESC_CHAR, "escape", N_("Escape key"), "Esc"},
133 {KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left"},
134 {KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right"},
135 {KEY_UP, "kpup", N_("Up arrow keypad"), "Up"},
136 {KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down"},
137 {KEY_HOME, "kphome", N_("Home on keypad"), "Home"},
138 {KEY_END, "kpend", N_("End on keypad"), "End"},
139 {KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn"},
140 {KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp"},
141 {KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins"},
142 {KEY_DC, "kpdelete", N_("Delete on keypad"), "Del"},
143 {(int) '\n', "kpenter", N_("Enter on keypad"), "Enter"},
144 {KEY_F (21), "f21", N_("Function key 21"), "F21"},
145 {KEY_F (22), "f22", N_("Function key 22"), "F22"},
146 {KEY_F (23), "f23", N_("Function key 23"), "F23"},
147 {KEY_F (24), "f24", N_("Function key 24"), "F24"},
148 {KEY_A1, "a1", N_("A1 key"), "A1"},
149 {KEY_C1, "c1", N_("C1 key"), "C1"},
151 /* Alternative label */
152 {ESC_CHAR, "esc", N_("Escape key"), "Esc"},
153 {KEY_BACKSPACE, "bs", N_("Backspace key"), "Bakspace"},
154 {KEY_IC, "ins", N_("Insert key"), "Ins"},
155 {KEY_DC, "del", N_("Delete key"), "Del"},
156 {(int) '+', "plus", N_("Plus"), "+"},
157 {(int) '-', "minus", N_("Minus"), "-"},
158 {(int) '*', "asterisk", N_("Asterisk"), "*"},
159 {(int) '.', "dot", N_("Dot"), "."},
160 {(int) '<', "lt", N_("Less than"), "<"},
161 {(int) '>', "gt", N_("Great than"), ">"},
162 {(int) '=', "equal", N_("Equal"), "="},
163 {(int) ',', "comma", N_("Comma"), ","},
164 {(int) '\'', "apostrophe", N_("Apostrophe"), "\'"},
165 {(int) ':', "colon", N_("Colon"), ":"},
166 {(int) '!', "exclamation", N_("Exclamation mark"), "!"},
167 {(int) '?', "question", N_("Question mark"), "?"},
168 {(int) '&', "ampersand", N_("Ampersand"), "&"},
169 {(int) '$', "dollar", N_("Dollar sign"), "$"},
170 {(int) '"', "quota", N_("Quotation mark"), "\""},
171 {(int) '%', "percent", N_("Percent sign"), "%"},
172 {(int) '^', "caret", N_("Caret"), "^"},
173 {(int) '~', "tilda", N_("Tilda"), "~"},
174 {(int) '`', "prime", N_("Prime"), "`"},
175 {(int) '_', "underline", N_("Underline"), "_"},
176 {(int) '_', "understrike", N_("Understrike"), "_"},
177 {(int) '|', "pipe", N_("Pipe"), "|"},
178 {(int) '(', "lparenthesis", N_("Left parenthesis"), "("},
179 {(int) ')', "rparenthesis", N_("Right parenthesis"), ")"},
180 {(int) '[', "lbracket", N_("Left bracket"), "["},
181 {(int) ']', "rbracket", N_("Right bracket"), "]"},
182 {(int) '{', "lbrace", N_("Left brace"), "{"},
183 {(int) '}', "rbrace", N_("Right brace"), "}"},
184 {(int) '\n', "enter", N_("Enter"), "Enter"},
185 {(int) '\t', "tab", N_("Tab key"), "Tab"},
186 {(int) ' ', "space", N_("Space key"), "Space"},
187 {(int) '/', "slash", N_("Slash key"), "/"},
188 {(int) '\\', "backslash", N_("Backslash key"), "\\"},
189 {(int) '#', "number", N_("Number sign #"), "#"},
190 {(int) '#', "hash", N_("Number sign #"), "#"},
191 /* TRANSLATORS: Please translate as in "at sign" (@). */
192 {(int) '@', "at", N_("At sign"), "@"},
194 /* meta keys */
195 {KEY_M_CTRL, "control", N_("Ctrl"), "C"},
196 {KEY_M_CTRL, "ctrl", N_("Ctrl"), "C"},
197 {KEY_M_ALT, "meta", N_("Alt"), "M"},
198 {KEY_M_ALT, "alt", N_("Alt"), "M"},
199 {KEY_M_ALT, "ralt", N_("Alt"), "M"},
200 {KEY_M_SHIFT, "shift", N_("Shift"), "S"},
202 {0, NULL, NULL, NULL}
205 /*** file scope macro definitions ****************************************************************/
207 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
208 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
210 /* The maximum sequence length (32 + null terminator) */
211 #define SEQ_BUFFER_LEN 33
213 /*** file scope type declarations ****************************************************************/
215 /* Linux console keyboard modifiers */
216 typedef enum
218 SHIFT_PRESSED = (1 << 0),
219 ALTR_PRESSED = (1 << 1),
220 CONTROL_PRESSED = (1 << 2),
221 ALTL_PRESSED = (1 << 3)
222 } mod_pressed_t;
224 typedef struct key_def
226 char ch; /* Holds the matching char code */
227 int code; /* The code returned, valid if child == NULL */
228 struct key_def *next;
229 struct key_def *child; /* sequence continuation */
230 int action; /* optional action to be done. Now used only
231 to mark that we are just after the first
232 Escape */
233 } key_def;
235 typedef struct
237 int code;
238 const char *seq;
239 int action;
240 } key_define_t;
242 /* File descriptor monitoring add/remove routines */
243 typedef struct SelectList
245 int fd;
246 select_fn callback;
247 void *info;
248 struct SelectList *next;
249 } SelectList;
251 typedef enum KeySortType
253 KEY_NOSORT = 0,
254 KEY_SORTBYNAME,
255 KEY_SORTBYCODE
256 } KeySortType;
258 #ifdef __QNXNTO__
259 typedef int (*ph_dv_f) (void *, void *);
260 typedef int (*ph_ov_f) (void *);
261 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
262 #endif
264 /*** file scope variables ************************************************************************/
266 static key_define_t mc_default_keys[] = {
267 {ESC_CHAR, ESC_STR, MCKEY_ESCAPE},
268 {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION},
269 {0, NULL, MCKEY_NOACTION},
272 /* Broken terminfo and termcap databases on xterminals */
273 static key_define_t xterm_key_defines[] = {
274 {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION},
275 {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION},
276 {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION},
277 {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION},
278 {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION},
279 {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION},
280 {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION},
281 {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION},
282 {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION},
283 {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION},
284 {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION},
285 {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION},
286 {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION},
287 {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION},
289 /* old xterm Shift-arrows */
290 {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION},
291 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION},
292 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION},
293 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION},
295 /* new xterm Shift-arrows */
296 {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION},
297 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION},
298 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION},
299 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION},
301 /* more xterm keys with modifiers */
302 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION},
303 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION},
304 {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION},
305 {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION},
306 {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION},
307 {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION},
308 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION},
309 {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION},
310 {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION},
311 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION},
312 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION},
313 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION},
314 {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION},
315 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION},
316 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION},
317 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION},
318 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION},
319 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION},
321 /* putty */
322 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION},
323 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION},
324 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION},
325 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION},
327 /* putty alt-arrow keys */
328 /* removed as source esc esc esc trouble */
330 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
331 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
332 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
333 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
334 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
335 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
336 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
337 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
339 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
340 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
341 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
342 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
344 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
345 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
346 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
347 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
349 /* xterm alt-arrow keys */
350 {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION},
351 {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION},
352 {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION},
353 {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION},
354 {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION},
355 {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION},
356 {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION},
357 {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION},
358 {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION},
359 {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION},
360 {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION},
361 {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION},
362 {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION},
363 {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION},
364 {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION},
365 {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION},
367 /* rxvt keys with modifiers */
368 {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
369 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
370 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
371 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
372 {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
373 {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
374 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
375 {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
376 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
377 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
378 {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
379 {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
380 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
381 {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
382 {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
383 {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
384 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
386 /* konsole keys with modifiers */
387 {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
388 {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
390 /* gnome-terminal */
391 {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
392 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
393 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
394 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
395 {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
396 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
397 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
398 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
399 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
400 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
401 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
402 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
404 /* gnome-terminal - application mode */
405 {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
406 {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
407 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
408 {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
409 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
410 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
411 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
412 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
414 /* iTerm */
415 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
416 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
418 /* putty */
419 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
420 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
422 /* keypad keys */
423 {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
424 {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
425 {'/', ESC_STR "Oo", MCKEY_NOACTION},
426 {'\n', ESC_STR "OM", MCKEY_NOACTION},
428 {0, NULL, MCKEY_NOACTION},
431 /* qansi-m terminals have a much more key combinatios,
432 which are undefined in termcap/terminfo */
433 static key_define_t qansi_key_defines[] = {
434 /* qansi-m terminal */
435 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
436 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
437 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
438 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
439 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
440 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
441 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
442 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
443 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
444 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
445 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
446 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
447 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
448 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
449 {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
450 {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
451 {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
452 {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
453 {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
454 {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
455 {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
456 {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
457 {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
458 {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
459 {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
460 {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
461 {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
462 {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
463 {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
464 {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
465 {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
466 {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
467 {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
468 {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
469 {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
470 {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
471 {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
472 {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
473 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
474 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
475 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
476 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
477 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
478 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
479 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
480 {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */
481 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
482 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
483 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
484 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
485 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
486 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
487 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
488 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
489 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */
490 {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */
491 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
492 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
493 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
494 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
495 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
496 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
497 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
498 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
499 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
500 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
501 {0, NULL, MCKEY_NOACTION},
504 /* timeout for old_esc_mode in usec */
505 int old_esc_mode_timeout = 1000000; /* settable via env */
507 /* This holds all the key definitions */
508 static key_def *keys = NULL;
510 static int input_fd;
511 static int disabled_channels = 0; /* Disable channels checking */
513 static SelectList *select_list = NULL;
515 static int seq_buffer[SEQ_BUFFER_LEN];
516 static int *seq_append = NULL;
518 static int *pending_keys = NULL;
520 #ifdef __QNXNTO__
521 ph_dv_f ph_attach;
522 ph_ov_f ph_input_group;
523 ph_pqc_f ph_query_cursor;
524 #endif
526 #ifdef HAVE_TEXTMODE_X11_SUPPORT
527 static Display *x11_display;
528 static Window x11_window;
529 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
531 static KeySortType has_been_sorted = KEY_NOSORT;
533 /* *INDENT-OFF* */
534 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
535 /* *INDENT-ON* */
537 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
539 /*** file scope functions ************************************************************************/
540 /* --------------------------------------------------------------------------------------------- */
542 static int
543 add_selects (fd_set * select_set)
545 int top_fd = 0;
547 if (disabled_channels == 0)
549 SelectList *p;
551 for (p = select_list; p != NULL; p = p->next)
553 FD_SET (p->fd, select_set);
554 if (p->fd > top_fd)
555 top_fd = p->fd;
559 return top_fd;
562 /* --------------------------------------------------------------------------------------------- */
564 static void
565 check_selects (fd_set * select_set)
567 if (disabled_channels == 0)
569 gboolean retry;
573 SelectList *p;
575 retry = FALSE;
576 for (p = select_list; p; p = p->next)
577 if (FD_ISSET (p->fd, select_set))
579 FD_CLR (p->fd, select_set);
580 (*p->callback) (p->fd, p->info);
581 retry = TRUE;
582 break;
585 while (retry);
589 /* --------------------------------------------------------------------------------------------- */
590 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
592 static void
593 try_channels (int set_timeout)
595 struct timeval time_out;
596 static fd_set select_set;
597 struct timeval *timeptr;
598 int v;
599 int maxfdp;
601 while (1)
603 FD_ZERO (&select_set);
604 FD_SET (input_fd, &select_set); /* Add stdin */
605 maxfdp = max (add_selects (&select_set), input_fd);
607 timeptr = NULL;
608 if (set_timeout)
610 time_out.tv_sec = 0;
611 time_out.tv_usec = 100000;
612 timeptr = &time_out;
615 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
616 if (v > 0)
618 check_selects (&select_set);
619 if (FD_ISSET (input_fd, &select_set))
620 break;
625 /* --------------------------------------------------------------------------------------------- */
627 static key_def *
628 create_sequence (const char *seq, int code, int action)
630 key_def *base, *p, *attach;
632 for (base = attach = NULL; *seq; seq++)
634 p = g_new (key_def, 1);
635 if (base == NULL)
636 base = p;
637 if (attach != NULL)
638 attach->child = p;
640 p->ch = *seq;
641 p->code = code;
642 p->child = p->next = NULL;
643 if (seq[1] == '\0')
644 p->action = action;
645 else
646 p->action = MCKEY_NOACTION;
647 attach = p;
649 return base;
652 /* --------------------------------------------------------------------------------------------- */
654 static void
655 define_sequences (const key_define_t * kd)
657 int i;
659 for (i = 0; kd[i].code != 0; i++)
660 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
663 /* --------------------------------------------------------------------------------------------- */
665 static void
666 init_key_x11 (void)
668 #ifdef HAVE_TEXTMODE_X11_SUPPORT
669 if (getenv ("DISPLAY") != NULL)
671 x11_display = mc_XOpenDisplay (0);
673 if (x11_display != NULL)
674 x11_window = DefaultRootWindow (x11_display);
676 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
679 /* --------------------------------------------------------------------------------------------- */
680 /* Workaround for System V Curses vt100 bug */
682 static int
683 getch_with_delay (void)
685 int c;
687 /* This routine could be used on systems without mouse support,
688 so we need to do the select check :-( */
689 while (1)
691 if (pending_keys == NULL)
692 try_channels (0);
694 /* Try to get a character */
695 c = get_key_code (0);
696 if (c != -1)
697 break;
698 /* Failed -> wait 0.1 secs and try again */
699 try_channels (1);
701 /* Success -> return the character */
702 return c;
705 /* --------------------------------------------------------------------------------------------- */
707 static void
708 xmouse_get_event (Gpm_Event * ev)
710 int btn;
711 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
712 static struct timeval tv2;
713 static int clicks = 0;
714 static int last_btn = 0;
716 /* Decode Xterm mouse information to a GPM style event */
718 /* Variable btn has following meaning: */
719 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
720 btn = tty_lowlevel_getch () - 32;
722 /* There seems to be no way of knowing which button was released */
723 /* So we assume all the buttons were released */
725 if (btn == 3)
727 if (last_btn != 0)
729 if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
731 /* FIXME: DIRTY HACK */
732 /* don't generate GPM_UP after mouse wheel */
733 /* need for menu event handling */
734 ev->type = 0;
735 tv1.tv_sec = 0;
736 tv1.tv_usec = 0;
738 else
740 ev->type = GPM_UP | (GPM_SINGLE << clicks);
741 GET_TIME (tv1);
743 ev->buttons = 0;
744 last_btn = 0;
745 clicks = 0;
747 else
749 /* Bogus event, maybe mouse wheel */
750 ev->type = 0;
753 else
755 if (btn >= 32 && btn <= 34)
757 btn -= 32;
758 ev->type = GPM_DRAG;
760 else
761 ev->type = GPM_DOWN;
763 GET_TIME (tv2);
764 if (tv1.tv_sec && (DIF_TIME (tv1, tv2) < double_click_speed))
766 clicks++;
767 clicks %= 3;
769 else
770 clicks = 0;
772 switch (btn)
774 case 0:
775 ev->buttons = GPM_B_LEFT;
776 break;
777 case 1:
778 ev->buttons = GPM_B_MIDDLE;
779 break;
780 case 2:
781 ev->buttons = GPM_B_RIGHT;
782 break;
783 case 64:
784 ev->buttons = GPM_B_UP;
785 clicks = 0;
786 break;
787 case 65:
788 ev->buttons = GPM_B_DOWN;
789 clicks = 0;
790 break;
791 default:
792 /* Nothing */
793 ev->type = 0;
794 ev->buttons = 0;
795 break;
797 last_btn = ev->buttons;
799 /* Coordinates are 33-based */
800 /* Transform them to 1-based */
801 ev->x = tty_lowlevel_getch () - 32;
802 ev->y = tty_lowlevel_getch () - 32;
805 /* --------------------------------------------------------------------------------------------- */
807 * Get modifier state (shift, alt, ctrl) for the last key pressed.
808 * We are assuming that the state didn't change since the key press.
809 * This is only correct if get_modifier() is called very fast after
810 * the input was received, so that the user didn't release the
811 * modifier keys yet.
814 static int
815 get_modifier (void)
817 int result = 0;
818 #ifdef __QNXNTO__
819 int mod_status, shift_ext_status;
820 static int in_photon = 0;
821 static int ph_ig = 0;
822 PhCursorInfo_t cursor_info;
823 #endif /* __QNXNTO__ */
825 #ifdef HAVE_TEXTMODE_X11_SUPPORT
826 if (x11_window != 0)
828 Window root, child;
829 int root_x, root_y;
830 int win_x, win_y;
831 unsigned int mask;
833 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
834 &root_y, &win_x, &win_y, &mask);
836 if (mask & ShiftMask)
837 result |= KEY_M_SHIFT;
838 if (mask & ControlMask)
839 result |= KEY_M_CTRL;
840 return result;
842 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
843 #ifdef __QNXNTO__
845 if (in_photon == 0)
847 /* First time here, let's load Photon library and attach
848 to Photon */
849 in_photon = -1;
850 if (getenv ("PHOTON2_PATH") != NULL)
852 /* QNX 6.x has no support for RTLD_LAZY */
853 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
854 if (ph_handle != NULL)
856 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
857 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
858 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
859 if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL))
861 if ((*ph_attach) (0, 0))
862 { /* Attached */
863 ph_ig = (*ph_input_group) (0);
864 in_photon = 1;
870 /* We do not have Photon running. Assume we are in text
871 console or xterm */
872 if (in_photon == -1)
874 if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (int), NULL) == -1)
875 return 0;
876 shift_ext_status = mod_status & 0xffffff00UL;
877 mod_status &= 0x7f;
878 if (mod_status & _LINESTATUS_CON_ALT)
879 result |= KEY_M_ALT;
880 if (mod_status & _LINESTATUS_CON_CTRL)
881 result |= KEY_M_CTRL;
882 if ((mod_status & _LINESTATUS_CON_SHIFT) || (shift_ext_status & 0x00000800UL))
883 result |= KEY_M_SHIFT;
885 else
887 (*ph_query_cursor) (ph_ig, &cursor_info);
888 if (cursor_info.key_mods & 0x04)
889 result |= KEY_M_ALT;
890 if (cursor_info.key_mods & 0x02)
891 result |= KEY_M_CTRL;
892 if (cursor_info.key_mods & 0x01)
893 result |= KEY_M_SHIFT;
895 #endif /* __QNXNTO__ */
897 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
899 unsigned char modifiers = 6;
901 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
902 return 0;
904 /* Translate Linux modifiers into mc modifiers */
905 if (modifiers & SHIFT_PRESSED)
906 result |= KEY_M_SHIFT;
907 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
908 result |= KEY_M_ALT;
909 if (modifiers & CONTROL_PRESSED)
910 result |= KEY_M_CTRL;
912 #endif /* !__linux__ */
913 return result;
916 /* --------------------------------------------------------------------------------------------- */
918 static gboolean
919 push_char (int c)
921 gboolean ret = FALSE;
923 if (seq_append == NULL)
924 seq_append = seq_buffer;
926 if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
928 *(seq_append++) = c;
929 *seq_append = 0;
930 ret = TRUE;
933 return ret;
936 /* --------------------------------------------------------------------------------------------- */
937 /* Apply corrections for the keycode generated in get_key_code() */
939 static int
940 correct_key_code (int code)
942 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
943 unsigned int mod = code & KEY_M_MASK; /* modifier */
944 #ifdef __QNXNTO__
945 unsigned int qmod; /* bunch of the QNX console
946 modifiers needs unchanged */
947 #endif /* __QNXNTO__ */
950 * Add key modifiers directly from X11 or OS.
951 * Ordinary characters only get modifiers from sequences.
953 if (c < 32 || c >= 256)
955 mod |= get_modifier ();
958 /* This is needed if the newline is reported as carriage return */
959 if (c == '\r')
960 c = '\n';
962 /* This is reported to be useful on AIX */
963 if (c == KEY_SCANCEL)
964 c = '\t';
966 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
967 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL)))
969 c = KEY_BTAB;
970 mod = 0;
973 /* F0 is the same as F10 for out purposes */
974 if (c == KEY_F (0))
975 c = KEY_F (10);
978 * We are not interested if Ctrl was pressed when entering control
979 * characters, so assume that it was. When checking for such keys,
980 * XCTRL macro should be used. In some cases, we are interested,
981 * e.g. to distinguish Ctrl-Enter from Enter.
983 if (c == '\b')
985 /* Special case for backspase ('\b' < 32) */
986 c = KEY_BACKSPACE;
987 mod &= ~KEY_M_CTRL;
989 else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
991 mod |= KEY_M_CTRL;
994 #ifdef __QNXNTO__
995 qmod = get_modifier ();
997 if ((c == 127) && (mod == 0))
998 { /* Add Ctrl/Alt/Shift-BackSpace */
999 mod |= get_modifier ();
1000 c = KEY_BACKSPACE;
1003 if ((c == '0') && (mod == 0))
1004 { /* Add Shift-Insert on key pad */
1005 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1007 mod = KEY_M_SHIFT;
1008 c = KEY_IC;
1012 if ((c == '.') && (mod == 0))
1013 { /* Add Shift-Del on key pad */
1014 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1016 mod = KEY_M_SHIFT;
1017 c = KEY_DC;
1020 #endif /* __QNXNTO__ */
1022 /* Unrecognized 0177 is delete (preserve Ctrl) */
1023 if (c == 0177)
1025 c = KEY_BACKSPACE;
1028 #if 0
1029 /* Unrecognized Ctrl-d is delete */
1030 if (c == (31 & 'd'))
1032 c = KEY_DC;
1033 mod &= ~KEY_M_CTRL;
1036 /* Unrecognized Ctrl-h is backspace */
1037 if (c == (31 & 'h'))
1039 c = KEY_BACKSPACE;
1040 mod &= ~KEY_M_CTRL;
1042 #endif
1044 /* Shift+BackSpace is backspace */
1045 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT))
1047 mod &= ~KEY_M_SHIFT;
1050 /* Convert Shift+Fn to F(n+10) */
1051 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT))
1053 c += 10;
1056 /* Remove Shift information from function keys */
1057 if (c >= KEY_F (1) && c <= KEY_F (20))
1059 mod &= ~KEY_M_SHIFT;
1062 if (!mc_global.tty.alternate_plus_minus)
1063 switch (c)
1065 case KEY_KP_ADD:
1066 c = '+';
1067 break;
1068 case KEY_KP_SUBTRACT:
1069 c = '-';
1070 break;
1071 case KEY_KP_MULTIPLY:
1072 c = '*';
1073 break;
1076 return (mod | c);
1079 /* --------------------------------------------------------------------------------------------- */
1081 static int
1082 xgetch_second (void)
1084 fd_set Read_FD_Set;
1085 int c;
1086 struct timeval time_out;
1088 time_out.tv_sec = old_esc_mode_timeout / 1000000;
1089 time_out.tv_usec = old_esc_mode_timeout % 1000000;
1090 tty_nodelay (TRUE);
1091 FD_ZERO (&Read_FD_Set);
1092 FD_SET (input_fd, &Read_FD_Set);
1093 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1094 c = tty_lowlevel_getch ();
1095 tty_nodelay (FALSE);
1096 return c;
1099 /* --------------------------------------------------------------------------------------------- */
1101 static void
1102 learn_store_key (char *buffer, char **p, int c)
1104 if (*p - buffer > 253)
1105 return;
1106 if (c == ESC_CHAR)
1108 *(*p)++ = '\\';
1109 *(*p)++ = 'e';
1111 else if (c < ' ')
1113 *(*p)++ = '^';
1114 *(*p)++ = c + 'a' - 1;
1116 else if (c == '^')
1118 *(*p)++ = '^';
1119 *(*p)++ = '^';
1121 else
1122 *(*p)++ = (char) c;
1125 /* --------------------------------------------------------------------------------------------- */
1127 static void
1128 k_dispose (key_def * k)
1130 if (k != NULL)
1132 k_dispose (k->child);
1133 k_dispose (k->next);
1134 g_free (k);
1138 /* --------------------------------------------------------------------------------------------- */
1140 static void
1141 s_dispose (SelectList * sel)
1143 if (sel != NULL)
1145 s_dispose (sel->next);
1146 g_free (sel);
1150 /* --------------------------------------------------------------------------------------------- */
1152 static int
1153 key_code_comparator_by_name (const void *p1, const void *p2)
1155 const key_code_name_t *n1 = *(const key_code_name_t **) p1;
1156 const key_code_name_t *n2 = *(const key_code_name_t **) p2;
1158 return g_ascii_strcasecmp (n1->name, n2->name);
1161 /* --------------------------------------------------------------------------------------------- */
1163 static int
1164 key_code_comparator_by_code (const void *p1, const void *p2)
1166 const key_code_name_t *n1 = *(const key_code_name_t **) p1;
1167 const key_code_name_t *n2 = *(const key_code_name_t **) p2;
1169 return n1->code - n2->code;
1172 /* --------------------------------------------------------------------------------------------- */
1174 static inline void
1175 sort_key_conv_tab (enum KeySortType type_sort)
1177 if (has_been_sorted != type_sort)
1179 size_t i;
1180 for (i = 0; i < key_conv_tab_size; i++)
1181 key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1183 if (type_sort == KEY_SORTBYNAME)
1185 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1186 &key_code_comparator_by_name);
1188 else if (type_sort == KEY_SORTBYCODE)
1190 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1191 &key_code_comparator_by_code);
1193 has_been_sorted = type_sort;
1197 /* --------------------------------------------------------------------------------------------- */
1199 static int
1200 lookup_keyname (const char *name, int *idx)
1202 if (name[0] != '\0')
1204 const key_code_name_t key = { 0, name, NULL, NULL };
1205 const key_code_name_t *keyp = &key;
1206 key_code_name_t **res;
1208 if (name[1] == '\0')
1210 *idx = -1;
1211 return (int) name[0];
1214 sort_key_conv_tab (KEY_SORTBYNAME);
1216 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1217 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1219 if (res != NULL)
1221 *idx = (int) (res - (key_code_name_t **) key_conv_tab_sorted);
1222 return (*res)->code;
1226 *idx = -1;
1227 return 0;
1230 /* --------------------------------------------------------------------------------------------- */
1232 static gboolean
1233 lookup_keycode (const long code, int *idx)
1235 if (code != 0)
1237 const key_code_name_t key = { code, NULL, NULL, NULL };
1238 const key_code_name_t *keyp = &key;
1239 key_code_name_t **res;
1241 sort_key_conv_tab (KEY_SORTBYCODE);
1243 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1244 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1246 if (res != NULL)
1248 *idx = (int) (res - (key_code_name_t **) key_conv_tab_sorted);
1249 return TRUE;
1253 *idx = -1;
1254 return FALSE;
1257 /* --------------------------------------------------------------------------------------------- */
1258 /*** public functions ****************************************************************************/
1259 /* --------------------------------------------------------------------------------------------- */
1260 /* This has to be called before init_slang or whatever routine
1261 calls any define_sequence */
1263 void
1264 init_key (void)
1266 const char *term = getenv ("TERM");
1268 /* This has to be the first define_sequence */
1269 /* So, we can assume that the first keys member has ESC */
1270 define_sequences (mc_default_keys);
1272 /* Terminfo on irix does not have some keys */
1273 if (mc_global.tty.xterm_flag
1274 || (term != NULL
1275 && (strncmp (term, "iris-ansi", 9) == 0
1276 || strncmp (term, "xterm", 5) == 0
1277 || strncmp (term, "rxvt", 4) == 0 || strcmp (term, "screen") == 0)))
1278 define_sequences (xterm_key_defines);
1280 /* load some additional keys (e.g. direct Alt-? support) */
1281 load_xtra_key_defines ();
1283 #ifdef __QNX__
1284 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1286 /* Modify the default value of use_8th_bit_as_meta: we would
1287 * like to provide a working mc for a newbie who knows nothing
1288 * about [Options|Display bits|Full 8 bits input]...
1290 * Don't use 'meta'-bit, when we are dealing with a
1291 * 'qnx*'-type terminal: clear the default value!
1292 * These terminal types use 0xFF as an escape character,
1293 * so use_8th_bit_as_meta==1 must not be enabled!
1295 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1296 * is not used now (doesn't even depend on use_8th_bit_as_meta
1297 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1299 use_8th_bit_as_meta = 0;
1301 #endif /* __QNX__ */
1303 init_key_x11 ();
1305 /* Load the qansi-m key definitions
1306 if we are running under the qansi-m terminal */
1307 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1308 define_sequences (qansi_key_defines);
1311 /* --------------------------------------------------------------------------------------------- */
1313 * This has to be called after SLang_init_tty/slint_init
1316 void
1317 init_key_input_fd (void)
1319 #ifdef HAVE_SLANG
1320 input_fd = SLang_TT_Read_FD;
1321 #endif
1324 /* --------------------------------------------------------------------------------------------- */
1326 void
1327 done_key (void)
1329 k_dispose (keys);
1330 s_dispose (select_list);
1332 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1333 if (x11_display)
1334 mc_XCloseDisplay (x11_display);
1335 #endif
1338 /* --------------------------------------------------------------------------------------------- */
1340 void
1341 add_select_channel (int fd, select_fn callback, void *info)
1343 SelectList *new;
1345 new = g_new (SelectList, 1);
1346 new->fd = fd;
1347 new->callback = callback;
1348 new->info = info;
1349 new->next = select_list;
1350 select_list = new;
1353 /* --------------------------------------------------------------------------------------------- */
1355 void
1356 delete_select_channel (int fd)
1358 SelectList *p = select_list;
1359 SelectList *p_prev = NULL;
1360 SelectList *p_next;
1362 while (p != NULL)
1363 if (p->fd == fd)
1365 p_next = p->next;
1367 if (p_prev != NULL)
1368 p_prev->next = p_next;
1369 else
1370 select_list = p_next;
1372 g_free (p);
1373 p = p_next;
1375 else
1377 p_prev = p;
1378 p = p->next;
1382 /* --------------------------------------------------------------------------------------------- */
1384 void
1385 channels_up (void)
1387 if (disabled_channels == 0)
1388 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1389 disabled_channels--;
1392 /* --------------------------------------------------------------------------------------------- */
1394 void
1395 channels_down (void)
1397 disabled_channels++;
1400 /* --------------------------------------------------------------------------------------------- */
1402 * Return the code associated with the symbolic name keyname
1405 long
1406 lookup_key (const char *name, char **label)
1408 char **lc_keys, **p;
1409 int k = -1;
1410 int key = 0;
1411 int lc_index = -1;
1413 int use_meta = -1;
1414 int use_ctrl = -1;
1415 int use_shift = -1;
1417 if (name == NULL)
1418 return 0;
1420 name = g_strstrip (g_strdup (name));
1421 p = lc_keys = g_strsplit_set (name, "-+ ", -1);
1422 g_free ((char *) name);
1424 while ((p != NULL) && (*p != NULL))
1426 if ((*p)[0] != '\0')
1428 int idx;
1430 key = lookup_keyname (g_strstrip (*p), &idx);
1432 if (key == KEY_M_ALT)
1433 use_meta = idx;
1434 else if (key == KEY_M_CTRL)
1435 use_ctrl = idx;
1436 else if (key == KEY_M_SHIFT)
1437 use_shift = idx;
1438 else
1440 k = key;
1441 lc_index = idx;
1442 break;
1446 p++;
1449 g_strfreev (lc_keys);
1451 /* output */
1452 if (k <= 0)
1453 return 0;
1456 if (label != NULL)
1458 GString *s;
1460 s = g_string_new ("");
1462 if (use_meta != -1)
1464 g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1465 g_string_append_c (s, '-');
1467 if (use_ctrl != -1)
1469 g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1470 g_string_append_c (s, '-');
1472 if (use_shift != -1)
1474 if (k < 127)
1475 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1476 else
1478 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1479 g_string_append_c (s, '-');
1480 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1483 else if (k < 128)
1485 if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1486 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1487 else
1488 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1490 else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1491 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1492 else
1493 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1495 *label = g_string_free (s, FALSE);
1498 if (use_shift != -1)
1500 if (k < 127 && k > 31)
1501 k = g_ascii_toupper ((gchar) k);
1502 else
1503 k |= KEY_M_SHIFT;
1506 if (use_ctrl != -1)
1508 if (k < 256)
1509 k = XCTRL (k);
1510 else
1511 k |= KEY_M_CTRL;
1514 if (use_meta != -1)
1515 k = ALT (k);
1517 return (long) k;
1520 /* --------------------------------------------------------------------------------------------- */
1522 char *
1523 lookup_key_by_code (const int keycode)
1525 /* code without modifier */
1526 unsigned int k = keycode & ~KEY_M_MASK;
1527 /* modifier */
1528 unsigned int mod = keycode & KEY_M_MASK;
1530 int use_meta = -1;
1531 int use_ctrl = -1;
1532 int use_shift = -1;
1533 int key_idx = -1;
1535 GString *s;
1536 int idx;
1538 s = g_string_sized_new (8);
1540 if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1542 if (mod & KEY_M_ALT)
1544 if (lookup_keycode (KEY_M_ALT, &idx))
1546 use_meta = idx;
1547 g_string_append (s, key_conv_tab_sorted[use_meta]->name);
1548 g_string_append_c (s, '-');
1551 if (mod & KEY_M_CTRL)
1553 /* non printeble chars like a CTRL-[A..Z] */
1554 if (k < 32)
1555 k += 64;
1557 if (lookup_keycode (KEY_M_CTRL, &idx))
1559 use_ctrl = idx;
1560 g_string_append (s, key_conv_tab_sorted[use_ctrl]->name);
1561 g_string_append_c (s, '-');
1564 if (mod & KEY_M_SHIFT)
1566 if (lookup_keycode (KEY_M_ALT, &idx))
1568 use_shift = idx;
1569 if (k < 127)
1570 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1571 else
1573 g_string_append (s, key_conv_tab_sorted[use_shift]->name);
1574 g_string_append_c (s, '-');
1575 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1579 else if (k < 128)
1581 if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1582 g_string_append_c (s, (gchar) k);
1583 else
1584 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1586 else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1587 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1588 else
1589 g_string_append_c (s, (gchar) keycode);
1592 return g_string_free (s, s->len == 0);
1595 /* --------------------------------------------------------------------------------------------- */
1597 * Return TRUE on success, FALSE on error.
1598 * An error happens if SEQ is a beginning of an existing longer sequence.
1601 gboolean
1602 define_sequence (int code, const char *seq, int action)
1604 key_def *base;
1606 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1607 return FALSE;
1609 for (base = keys; (base != NULL) && (*seq != '\0');)
1610 if (*seq == base->ch)
1612 if (base->child == 0)
1614 if (*(seq + 1) != '\0')
1615 base->child = create_sequence (seq + 1, code, action);
1616 else
1618 /* The sequence matches an existing one. */
1619 base->code = code;
1620 base->action = action;
1622 return TRUE;
1625 base = base->child;
1626 seq++;
1628 else
1630 if (base->next)
1631 base = base->next;
1632 else
1634 base->next = create_sequence (seq, code, action);
1635 return TRUE;
1639 if (*seq == '\0')
1641 /* Attempt to redefine a sequence with a shorter sequence. */
1642 return FALSE;
1645 keys = create_sequence (seq, code, action);
1646 return TRUE;
1649 /* --------------------------------------------------------------------------------------------- */
1651 * Check if we are idle, i.e. there are no pending keyboard or mouse
1652 * events. Return 1 is idle, 0 is there are pending events.
1654 gboolean
1655 is_idle (void)
1657 int maxfdp;
1658 fd_set select_set;
1659 struct timeval time_out;
1661 FD_ZERO (&select_set);
1662 FD_SET (input_fd, &select_set);
1663 maxfdp = input_fd;
1664 #ifdef HAVE_LIBGPM
1665 if (mouse_enabled && (use_mouse_p == MOUSE_GPM) && (gpm_fd > 0))
1667 FD_SET (gpm_fd, &select_set);
1668 maxfdp = max (maxfdp, gpm_fd);
1670 #endif
1671 time_out.tv_sec = 0;
1672 time_out.tv_usec = 0;
1673 return (select (maxfdp + 1, &select_set, 0, 0, &time_out) <= 0);
1676 /* --------------------------------------------------------------------------------------------- */
1679 get_key_code (int no_delay)
1681 int c;
1682 static key_def *this = NULL, *parent;
1683 static struct timeval esctime = { -1, -1 };
1684 static int lastnodelay = -1;
1686 if (no_delay != lastnodelay)
1688 this = NULL;
1689 lastnodelay = no_delay;
1692 pend_send:
1693 if (pending_keys != NULL)
1695 int d = *pending_keys++;
1696 check_pend:
1697 if (*pending_keys == 0)
1699 pending_keys = NULL;
1700 seq_append = NULL;
1702 if ((d == ESC_CHAR) && (pending_keys != NULL))
1704 d = ALT (*pending_keys++);
1705 goto check_pend;
1707 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
1708 d = ALT (d & 0x7f);
1709 this = NULL;
1710 return correct_key_code (d);
1713 nodelay_try_again:
1714 if (no_delay)
1715 tty_nodelay (TRUE);
1717 c = tty_lowlevel_getch ();
1718 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1719 if (c == KEY_RESIZE)
1720 goto nodelay_try_again;
1721 #endif
1722 if (no_delay)
1724 tty_nodelay (FALSE);
1725 if (c == -1)
1727 if (this != NULL && parent != NULL && parent->action == MCKEY_ESCAPE && old_esc_mode)
1729 struct timeval current, time_out;
1731 if (esctime.tv_sec == -1)
1732 return -1;
1733 GET_TIME (current);
1734 time_out.tv_sec = old_esc_mode_timeout / 1000000 + esctime.tv_sec;
1735 time_out.tv_usec = old_esc_mode_timeout % 1000000 + esctime.tv_usec;
1736 if (time_out.tv_usec > 1000000)
1738 time_out.tv_usec -= 1000000;
1739 time_out.tv_sec++;
1741 if (current.tv_sec < time_out.tv_sec)
1742 return -1;
1743 if (current.tv_sec == time_out.tv_sec && current.tv_usec < time_out.tv_usec)
1744 return -1;
1745 this = NULL;
1746 pending_keys = seq_append = NULL;
1747 return ESC_CHAR;
1749 return -1;
1752 else if (c == -1)
1754 /* Maybe we got an incomplete match.
1755 This we do only in delay mode, since otherwise
1756 tty_lowlevel_getch can return -1 at any time. */
1757 if (seq_append != NULL)
1759 pending_keys = seq_buffer;
1760 goto pend_send;
1762 this = NULL;
1763 return -1;
1766 /* Search the key on the root */
1767 if (!no_delay || this == NULL)
1769 this = keys;
1770 parent = NULL;
1772 if ((c > 127 && c < 256) && use_8th_bit_as_meta)
1774 c &= 0x7f;
1776 /* The first sequence defined starts with esc */
1777 parent = keys;
1778 this = keys->child;
1781 while (this != NULL)
1783 if (c == this->ch)
1785 if (this->child)
1787 if (!push_char (c))
1789 pending_keys = seq_buffer;
1790 goto pend_send;
1792 parent = this;
1793 this = this->child;
1794 if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1796 if (no_delay)
1798 GET_TIME (esctime);
1799 if (this == NULL)
1801 /* Shouldn't happen */
1802 fputs ("Internal error\n", stderr);
1803 exit (EXIT_FAILURE);
1805 goto nodelay_try_again;
1807 esctime.tv_sec = -1;
1808 c = xgetch_second ();
1809 if (c == -1)
1811 pending_keys = seq_append = NULL;
1812 this = NULL;
1813 return ESC_CHAR;
1816 else
1818 if (no_delay)
1819 goto nodelay_try_again;
1820 c = tty_lowlevel_getch ();
1823 else
1825 /* We got a complete match, return and reset search */
1826 int code;
1828 pending_keys = seq_append = NULL;
1829 code = this->code;
1830 this = NULL;
1831 return correct_key_code (code);
1834 else
1836 if (this->next != NULL)
1837 this = this->next;
1838 else
1840 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1842 /* Convert escape-digits to F-keys */
1843 if (g_ascii_isdigit (c))
1844 c = KEY_F (c - '0');
1845 else if (c == ' ')
1846 c = ESC_CHAR;
1847 else
1848 c = ALT (c);
1850 pending_keys = seq_append = NULL;
1851 this = NULL;
1852 return correct_key_code (c);
1854 /* Did not find a match or {c} was changed in the if above,
1855 so we have to return everything we had skipped
1857 push_char (c);
1858 pending_keys = seq_buffer;
1859 goto pend_send;
1863 this = NULL;
1864 return correct_key_code (c);
1867 /* --------------------------------------------------------------------------------------------- */
1868 /* Returns a character read from stdin with appropriate interpretation */
1869 /* Also takes care of generated mouse events */
1870 /* Returns EV_MOUSE if it is a mouse event */
1871 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1874 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1876 int c;
1877 static int flag = 0; /* Return value from select */
1878 #ifdef HAVE_LIBGPM
1879 static struct Gpm_Event ev; /* Mouse event */
1880 #endif
1881 struct timeval time_out;
1882 struct timeval *time_addr = NULL;
1883 static int dirty = 3;
1885 if ((dirty == 3) || is_idle ())
1887 mc_refresh ();
1888 dirty = 1;
1890 else
1891 dirty++;
1893 vfs_timeout_handler ();
1895 /* Ok, we use (event->x < 0) to signal that the event does not contain
1896 a suitable position for the mouse, so we can't use show_mouse_pointer
1897 on it.
1899 if (event->x > 0)
1901 show_mouse_pointer (event->x, event->y);
1902 if (!redo_event)
1903 event->x = -1;
1906 /* Repeat if using mouse */
1907 while (pending_keys == NULL)
1909 int maxfdp;
1910 fd_set select_set;
1912 FD_ZERO (&select_set);
1913 FD_SET (input_fd, &select_set);
1914 maxfdp = max (add_selects (&select_set), input_fd);
1916 #ifdef HAVE_LIBGPM
1917 if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1919 if (gpm_fd < 0)
1921 /* Connection to gpm broken, possibly gpm has died */
1922 mouse_enabled = FALSE;
1923 use_mouse_p = MOUSE_NONE;
1924 break;
1927 FD_SET (gpm_fd, &select_set);
1928 maxfdp = max (maxfdp, gpm_fd);
1930 #endif
1932 if (redo_event)
1934 time_out.tv_usec = mou_auto_repeat * 1000;
1935 time_out.tv_sec = 0;
1937 time_addr = &time_out;
1939 else
1941 int seconds;
1943 seconds = vfs_timeouts ();
1944 time_addr = NULL;
1946 if (seconds != 0)
1948 /* the timeout could be improved and actually be
1949 * the number of seconds until the next vfs entry
1950 * timeouts in the stamp list.
1953 time_out.tv_sec = seconds;
1954 time_out.tv_usec = 0;
1955 time_addr = &time_out;
1959 if (!block || mc_global.tty.winch_flag)
1961 time_addr = &time_out;
1962 time_out.tv_sec = 0;
1963 time_out.tv_usec = 0;
1966 tty_enable_interrupt_key ();
1967 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1968 tty_disable_interrupt_key ();
1970 /* select timed out: it could be for any of the following reasons:
1971 * redo_event -> it was because of the MOU_REPEAT handler
1972 * !block -> we did not block in the select call
1973 * else -> 10 second timeout to check the vfs status.
1975 if (flag == 0)
1977 if (redo_event)
1978 return EV_MOUSE;
1979 if (!block || mc_global.tty.winch_flag)
1980 return EV_NONE;
1981 vfs_timeout_handler ();
1983 if (flag == -1 && errno == EINTR)
1984 return EV_NONE;
1986 check_selects (&select_set);
1988 if (FD_ISSET (input_fd, &select_set))
1989 break;
1990 #ifdef HAVE_LIBGPM
1991 if (mouse_enabled && use_mouse_p == MOUSE_GPM
1992 && gpm_fd > 0 && FD_ISSET (gpm_fd, &select_set))
1994 Gpm_GetEvent (&ev);
1995 Gpm_FitEvent (&ev);
1996 *event = ev;
1997 return EV_MOUSE;
1999 #endif /* !HAVE_LIBGPM */
2002 #ifndef HAVE_SLANG
2003 flag = is_wintouched (stdscr);
2004 untouchwin (stdscr);
2005 #endif /* !HAVE_SLANG */
2006 c = block ? getch_with_delay () : get_key_code (1);
2008 #ifndef HAVE_SLANG
2009 if (flag > 0)
2010 tty_touch_screen ();
2011 #endif /* !HAVE_SLANG */
2013 if (mouse_enabled && (c == MCKEY_MOUSE
2014 #ifdef KEY_MOUSE
2015 || c == KEY_MOUSE
2016 #endif /* KEY_MOUSE */
2019 /* Mouse event */
2020 xmouse_get_event (event);
2021 return (event->type != 0) ? EV_MOUSE : EV_NONE;
2024 return c;
2027 /* --------------------------------------------------------------------------------------------- */
2028 /* Returns a key press, mouse events are discarded */
2031 tty_getch (void)
2033 Gpm_Event ev;
2034 int key;
2036 ev.x = -1;
2037 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE);
2038 return key;
2041 /* --------------------------------------------------------------------------------------------- */
2043 char *
2044 learn_key (void)
2046 /* LEARN_TIMEOUT in usec */
2047 #define LEARN_TIMEOUT 200000
2049 fd_set Read_FD_Set;
2050 struct timeval endtime;
2051 struct timeval time_out;
2052 int c;
2053 char buffer[256];
2054 char *p = buffer;
2056 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
2057 c = tty_lowlevel_getch ();
2058 while (c == -1)
2059 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
2060 learn_store_key (buffer, &p, c);
2061 GET_TIME (endtime);
2062 endtime.tv_usec += LEARN_TIMEOUT;
2063 if (endtime.tv_usec > 1000000)
2065 endtime.tv_usec -= 1000000;
2066 endtime.tv_sec++;
2068 tty_nodelay (TRUE);
2069 for (;;)
2071 while ((c = tty_lowlevel_getch ()) == -1)
2073 GET_TIME (time_out);
2074 time_out.tv_usec = endtime.tv_usec - time_out.tv_usec;
2075 if (time_out.tv_usec < 0)
2076 time_out.tv_sec++;
2077 time_out.tv_sec = endtime.tv_sec - time_out.tv_sec;
2078 if (time_out.tv_sec >= 0 && time_out.tv_usec > 0)
2080 FD_ZERO (&Read_FD_Set);
2081 FD_SET (input_fd, &Read_FD_Set);
2082 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
2084 else
2085 break;
2087 if (c == -1)
2088 break;
2089 learn_store_key (buffer, &p, c);
2091 tty_keypad (TRUE);
2092 tty_nodelay (FALSE);
2093 *p = '\0';
2094 return g_strdup (buffer);
2095 #undef LEARN_TIMEOUT
2098 /* --------------------------------------------------------------------------------------------- */
2099 /* xterm and linux console only: set keypad to numeric or application
2100 mode. Only in application keypad mode it's possible to distinguish
2101 the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2103 void
2104 numeric_keypad_mode (void)
2106 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2108 fputs (ESC_STR ">", stdout);
2109 fflush (stdout);
2113 /* --------------------------------------------------------------------------------------------- */
2115 void
2116 application_keypad_mode (void)
2118 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2120 fputs (ESC_STR "=", stdout);
2121 fflush (stdout);
2125 /* --------------------------------------------------------------------------------------------- */