Added code names for parentheses, brackets and braces.
[midnight-commander.git] / lib / tty / key.c
blob188fd1af8824a3eac978c9efe9e7f03f5f7656a4
1 /* Keyboard support routines.
3 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Written by: 1994, 1995 Miguel de Icaza.
7 1994, 1995 Janne Kukonlehto.
8 1995 Jakub Jelinek.
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /** \file key.c
26 * \brief Source: keyboard support routines
29 #include <config.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <unistd.h>
40 #include "lib/global.h"
41 #include "lib/strutil.h" /* str_casecmp */
43 #include "lib/vfs/mc-vfs/vfs.h"
45 #ifdef ENABLE_VFS
46 #include "lib/vfs/mc-vfs/gc.h"
47 #endif /* ENABLE_VFS */
49 #include "tty.h"
50 #include "tty-internal.h" /* mouse_enabled */
51 #include "mouse.h"
52 #include "key.h"
53 #include "win.h" /* xterm_flag */
55 #include "src/main.h"
56 #include "src/layout.h" /* winch_flag, mc_refresh() */
57 #include "src/consaver/cons.saver.h"
60 #ifdef HAVE_TEXTMODE_X11_SUPPORT
61 #include "x11conn.h"
62 #endif
64 #ifdef __linux__
65 #if defined(__GLIBC__) && (__GLIBC__ < 2)
66 # include <linux/termios.h> /* TIOCLINUX */
67 #else
68 # include <termios.h>
69 #endif
70 #include <sys/ioctl.h>
71 #endif /* __linux__ */
73 #ifdef __CYGWIN__
74 #include <termios.h>
75 #include <sys/ioctl.h>
76 #endif /* __CYGWIN__ */
78 #ifdef __QNXNTO__
79 #include <dlfcn.h>
80 #include <Ph.h>
81 #include <sys/dcmd_chr.h>
82 #endif /* __QNXNTO__ */
84 /*** global variables **************************************************/
86 /* If true, use + and \ keys normally and select/unselect do if M-+ / M-\.
87 and M-- and keypad + / - */
88 int alternate_plus_minus = 0;
90 int mou_auto_repeat = 100;
91 int double_click_speed = 250;
92 int old_esc_mode = 0;
93 int use_8th_bit_as_meta = 0;
95 /* This table is a mapping between names and the constants we use
96 * We use this to allow users to define alternate definitions for
97 * certain keys that may be missing from the terminal database
99 const key_code_name_t key_name_conv_tab[] = {
100 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
101 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
102 { KEY_F (1), "f1", N_("Function key 1"), "F1" },
103 { KEY_F (2), "f2", N_("Function key 2"), "F2" },
104 { KEY_F (3), "f3", N_("Function key 3"), "F3" },
105 { KEY_F (4), "f4", N_("Function key 4"), "F4" },
106 { KEY_F (5), "f5", N_("Function key 5"), "F5" },
107 { KEY_F (6), "f6", N_("Function key 6"), "F6" },
108 { KEY_F (7), "f7", N_("Function key 7"), "F7" },
109 { KEY_F (8), "f8", N_("Function key 8"), "F8" },
110 { KEY_F (9), "f9", N_("Function key 9"), "F9" },
111 { KEY_F (10), "f10", N_("Function key 10"), "F10" },
112 { KEY_F (11), "f11", N_("Function key 11"), "F11" },
113 { KEY_F (12), "f12", N_("Function key 12"), "F12" },
114 { KEY_F (13), "f13", N_("Function key 13"), "F13" },
115 { KEY_F (14), "f14", N_("Function key 14"), "F14" },
116 { KEY_F (15), "f15", N_("Function key 15"), "F15" },
117 { KEY_F (16), "f16", N_("Function key 16"), "F16" },
118 { KEY_F (17), "f17", N_("Function key 17"), "F17" },
119 { KEY_F (18), "f18", N_("Function key 18"), "F18" },
120 { KEY_F (19), "f19", N_("Function key 19"), "F19" },
121 { KEY_F (20), "f20", N_("Function key 20"), "F20" },
122 { KEY_BACKSPACE, "backspace", N_("Backspace key"), "Backspace" },
123 { KEY_END, "end", N_("End key"), "End" },
124 { KEY_UP, "up", N_("Up arrow key"), "Up" },
125 { KEY_DOWN, "down", N_("Down arrow key"), "Down" },
126 { KEY_LEFT, "left", N_("Left arrow key"), "Left" },
127 { KEY_RIGHT, "right", N_("Right arrow key"), "Right" },
128 { KEY_HOME, "home", N_("Home key"), "Home" },
129 { KEY_NPAGE, "pgdn", N_("Page Down key"), "PgDn" },
130 { KEY_PPAGE, "pgup", N_("Page Up key"), "PgUp" },
131 { KEY_IC, "insert", N_("Insert key"), "Ins" },
132 { KEY_DC, "delete", N_("Delete key"), "Del" },
133 { ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab" },
134 { KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+" },
135 { KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-" },
136 { (int) '/', "kpslash", N_("Slash on keypad"), "/" },
137 { KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*" },
139 /* From here on, these won't be shown in Learn keys (no space) */
140 { ESC_CHAR, "escape", N_("Escape key"), "Esc" },
141 { KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left" },
142 { KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right" },
143 { KEY_UP, "kpup", N_("Up arrow keypad"), "Up" },
144 { KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down" },
145 { KEY_HOME, "kphome", N_("Home on keypad"), "Home" },
146 { KEY_END, "kpend", N_("End on keypad"), "End" },
147 { KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn" },
148 { KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp" },
149 { KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins" },
150 { KEY_DC, "kpdelete", N_("Delete on keypad"), "Del" },
151 { (int) '\n', "kpenter", N_("Enter on keypad"), "Enter" },
152 { KEY_F (21), "f21", N_("Function key 21"), "F21" },
153 { KEY_F (22), "f22", N_("Function key 22"), "F22" },
154 { KEY_F (23), "f23", N_("Function key 23"), "F23" },
155 { KEY_F (24), "f24", N_("Function key 24"), "F24" },
157 /* Alternative label */
158 { ESC_CHAR, "esc", N_("Escape key"), "Esc" },
159 { KEY_BACKSPACE, "bs", N_("Backspace key"), "Bakspace" },
160 { KEY_IC, "ins", N_("Insert key"), "Ins" },
161 { KEY_DC, "del", N_("Delete key"), "Del" },
162 { (int) '+', "plus", N_("Plus"), "+" },
163 { (int) '-', "minus", N_("Minus"), "-" },
164 { (int) '*', "asterisk", N_("Asterisk"), "*" },
165 { (int) '.', "dot", N_("Dot"), "." },
166 { (int) '<', "lt", N_("Less than"), "<" },
167 { (int) '>', "gt", N_("Great than"), ">" },
168 { (int) '=', "equal", N_("Equal"), "=" },
169 { (int) ',', "comma", N_("Comma"), "," },
170 { (int) '\'', "apostrophe", N_("Apostrophe"), "\'" },
171 { (int) ':', "colon", N_("Colon"), ":" },
172 { (int) '!', "exclamation", N_("Exclamation mark"), "!" },
173 { (int) '?', "question", N_("Question mark"), "?" },
174 { (int) '&', "ampersand", N_("Ampersand"), "&" },
175 { (int) '$', "dollar", N_("Dollar sign"), "$" },
176 { (int) '"', "quota", N_("Quotation mark"), "\"" },
177 { (int) '^', "caret", N_("Caret"), "^" },
178 { (int) '~', "tilda", N_("Tilda"), "~" },
179 { (int) '`', "prime", N_("Prime"), "`" },
180 { (int) '_', "underline", N_("Underline"), "_" },
181 { (int) '_', "understrike", N_("Understrike"), "_" },
182 { (int) '|', "pipe", N_("Pipe"), "|" },
183 { (int) '(', "lparenthese", N_("Left parenthese"), "(" },
184 { (int) ')', "rparenthese", N_("Right parenthese"), ")" },
185 { (int) '[', "lbracket", N_("Left bracket"), "[" },
186 { (int) ']', "rbracket", N_("Right bracket"), "]" },
187 { (int) '{', "lbrace", N_("Left brace"), "{" },
188 { (int) '}', "rbrace", N_("Right brace"), "}" },
189 { (int) '\n', "enter", N_("Enter"), "Enter" },
190 { (int) '\t', "tab", N_("Tab key"), "Tab" },
191 { (int) ' ', "space", N_("Space key"), "Space" },
192 { (int) '/', "slash", N_("Slash key"), "/" },
193 { (int) '\\', "backslash", N_("Backslash key"), "\\" },
194 { (int) '#', "number", N_("Number sign #"), "#" },
195 { (int) '#', "hash", N_("Number sign #"), "#" },
197 /* meta keys */
198 { KEY_M_CTRL, "control", N_("Ctrl"), "C" },
199 { KEY_M_CTRL, "ctrl", N_("Ctrl"), "C" },
200 { KEY_M_ALT, "meta", N_("Alt"), "M" },
201 { KEY_M_ALT, "alt", N_("Alt"), "M" },
202 { KEY_M_ALT, "ralt", N_("Alt"), "M" },
203 { KEY_M_SHIFT, "shift", N_("Shift"), "S" },
205 { 0, NULL, NULL, NULL }
209 /*** file scope macro definitions **************************************/
211 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
212 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
214 /* The maximum sequence length (32 + null terminator) */
215 #define SEQ_BUFFER_LEN 33
217 /*** file scope type declarations **************************************/
219 /* Linux console keyboard modifiers */
220 typedef enum {
221 SHIFT_PRESSED = (1 << 0),
222 ALTR_PRESSED = (1 << 1),
223 CONTROL_PRESSED = (1 << 2),
224 ALTL_PRESSED = (1 << 3)
225 } mod_pressed_t;
227 typedef struct key_def {
228 char ch; /* Holds the matching char code */
229 int code; /* The code returned, valid if child == NULL */
230 struct key_def *next;
231 struct key_def *child; /* sequence continuation */
232 int action; /* optional action to be done. Now used only
233 to mark that we are just after the first
234 Escape */
235 } key_def;
237 typedef struct {
238 int code;
239 const char *seq;
240 int action;
241 } key_define_t;
243 /* File descriptor monitoring add/remove routines */
244 typedef struct SelectList {
245 int fd;
246 select_fn callback;
247 void *info;
248 struct SelectList *next;
249 } SelectList;
251 #ifdef __QNXNTO__
252 typedef int (*ph_dv_f) (void *, void *);
253 typedef int (*ph_ov_f) (void *);
254 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
255 #endif
257 /*** file scope variables **********************************************/
259 static key_define_t mc_default_keys[] = {
260 {ESC_CHAR, ESC_STR, MCKEY_ESCAPE},
261 {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION},
262 {0, NULL, MCKEY_NOACTION},
265 /* Broken terminfo and termcap databases on xterminals */
266 static key_define_t xterm_key_defines[] = {
267 {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION},
268 {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION},
269 {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION},
270 {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION},
271 {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION},
272 {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION},
273 {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION},
274 {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION},
275 {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION},
276 {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION},
277 {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION},
278 {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION},
279 {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION},
280 {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION},
282 /* old xterm Shift-arrows */
283 {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION},
284 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION},
285 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION},
286 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION},
288 /* new xterm Shift-arrows */
289 {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION},
290 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION},
291 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION},
292 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION},
294 /* more xterm keys with modifiers */
295 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION},
296 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION},
297 {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION},
298 {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION},
299 {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION},
300 {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION},
301 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION},
302 {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION},
303 {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION},
304 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION},
305 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION},
306 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION},
307 {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION},
308 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION},
309 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION},
310 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION},
311 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION},
312 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION},
314 /* putty */
315 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION},
316 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION},
317 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION},
318 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION},
320 /* putty alt-arrow keys */
321 /* removed as source esc esc esc trouble */
323 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
324 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
325 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
326 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
327 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
328 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
329 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
330 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
332 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
333 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
334 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
335 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
337 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
338 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
339 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
340 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
342 /* xterm alt-arrow keys */
343 {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION},
344 {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION},
345 {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION},
346 {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION},
347 {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION},
348 {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION},
349 {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION},
350 {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION},
351 {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION},
352 {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION},
353 {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION},
354 {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION},
355 {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION},
356 {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION},
357 {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION},
358 {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION},
360 /* rxvt keys with modifiers */
361 {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
362 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
363 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
364 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
365 {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
366 {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
367 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
368 {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
369 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
370 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
371 {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
372 {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
373 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
374 {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
375 {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
376 {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
377 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
379 /* konsole keys with modifiers */
380 {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
381 {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
383 /* gnome-terminal */
384 {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
385 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
386 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
387 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
388 {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
389 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
390 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
391 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
392 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
393 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
394 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
395 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
397 /* gnome-terminal - application mode */
398 {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
399 {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
400 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
401 {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
402 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
403 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
404 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
405 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
407 /* iTerm */
408 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
409 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
411 /* putty */
412 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
413 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
415 /* keypad keys */
416 {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
417 {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
418 {'/', ESC_STR "Oo", MCKEY_NOACTION},
419 {'\n', ESC_STR "OM", MCKEY_NOACTION},
421 {0, NULL, MCKEY_NOACTION},
424 /* qansi-m terminals have a much more key combinatios,
425 which are undefined in termcap/terminfo */
426 static key_define_t qansi_key_defines[] = {
427 /* qansi-m terminal */
428 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
429 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
430 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
431 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
432 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
433 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
434 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
435 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
436 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
437 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
438 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
439 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
440 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
441 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
442 {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
443 {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
444 {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
445 {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
446 {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
447 {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
448 {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
449 {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
450 {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
451 {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
452 {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
453 {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
454 {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
455 {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
456 {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
457 {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
458 {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
459 {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
460 {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
461 {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
462 {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
463 {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
464 {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
465 {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
466 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
467 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
468 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
469 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
470 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
471 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
472 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
473 {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */
474 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
475 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
476 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
477 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
478 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
479 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
480 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
481 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
482 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */
483 {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */
484 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
485 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
486 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
487 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
488 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
489 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
490 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
491 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
492 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
493 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
494 {0, NULL, MCKEY_NOACTION},
497 /* timeout for old_esc_mode in usec */
498 int old_esc_mode_timeout = 1000000; /* settable via env */
500 /* This holds all the key definitions */
501 static key_def *keys = NULL;
503 static int input_fd;
504 static int disabled_channels = 0; /* Disable channels checking */
506 static SelectList *select_list = NULL;
508 static int seq_buffer[SEQ_BUFFER_LEN];
509 static int *seq_append = NULL;
511 static int *pending_keys = NULL;
513 #ifdef __QNXNTO__
514 ph_dv_f ph_attach;
515 ph_ov_f ph_input_group;
516 ph_pqc_f ph_query_cursor;
517 #endif
519 #ifdef HAVE_TEXTMODE_X11_SUPPORT
520 static Display *x11_display;
521 static Window x11_window;
522 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
524 /*** file scope functions **********************************************/
526 static int
527 add_selects (fd_set * select_set)
529 int top_fd = 0;
531 if (disabled_channels == 0) {
532 SelectList *p;
534 for (p = select_list; p != NULL; p = p->next) {
535 FD_SET (p->fd, select_set);
536 if (p->fd > top_fd)
537 top_fd = p->fd;
541 return top_fd;
544 static void
545 check_selects (fd_set * select_set)
547 if (disabled_channels == 0) {
548 gboolean retry;
550 do {
551 SelectList *p;
553 retry = FALSE;
554 for (p = select_list; p; p = p->next)
555 if (FD_ISSET (p->fd, select_set)) {
556 FD_CLR (p->fd, select_set);
557 (*p->callback) (p->fd, p->info);
558 retry = TRUE;
559 break;
561 } while (retry);
565 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
566 static void
567 try_channels (int set_timeout)
569 struct timeval time_out;
570 static fd_set select_set;
571 struct timeval *timeptr;
572 int v;
573 int maxfdp;
575 while (1) {
576 FD_ZERO (&select_set);
577 FD_SET (input_fd, &select_set); /* Add stdin */
578 maxfdp = max (add_selects (&select_set), input_fd);
580 timeptr = NULL;
581 if (set_timeout) {
582 time_out.tv_sec = 0;
583 time_out.tv_usec = 100000;
584 timeptr = &time_out;
587 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
588 if (v > 0) {
589 check_selects (&select_set);
590 if (FD_ISSET (input_fd, &select_set))
591 break;
596 static key_def *
597 create_sequence (const char *seq, int code, int action)
599 key_def *base, *p, *attach;
601 for (base = attach = NULL; *seq; seq++) {
602 p = g_new (key_def, 1);
603 if (base == NULL)
604 base = p;
605 if (attach != NULL)
606 attach->child = p;
608 p->ch = *seq;
609 p->code = code;
610 p->child = p->next = NULL;
611 if (seq[1] == '\0')
612 p->action = action;
613 else
614 p->action = MCKEY_NOACTION;
615 attach = p;
617 return base;
620 static void
621 define_sequences (const key_define_t * kd)
623 int i;
625 for (i = 0; kd[i].code != 0; i++)
626 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
629 static void
630 init_key_x11 (void)
632 #ifdef HAVE_TEXTMODE_X11_SUPPORT
633 if (getenv ("DISPLAY") != NULL) {
634 x11_display = mc_XOpenDisplay (0);
636 if (x11_display != NULL)
637 x11_window = DefaultRootWindow (x11_display);
639 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
642 /* Workaround for System V Curses vt100 bug */
643 static int
644 getch_with_delay (void)
646 int c;
648 /* This routine could be used on systems without mouse support,
649 so we need to do the select check :-( */
650 while (1) {
651 if (pending_keys == NULL)
652 try_channels (0);
654 /* Try to get a character */
655 c = get_key_code (0);
656 if (c != -1)
657 break;
658 /* Failed -> wait 0.1 secs and try again */
659 try_channels (1);
661 /* Success -> return the character */
662 return c;
665 static void
666 xmouse_get_event (Gpm_Event * ev)
668 int btn;
669 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
670 static struct timeval tv2;
671 static int clicks = 0;
672 static int last_btn = 0;
674 /* Decode Xterm mouse information to a GPM style event */
676 /* Variable btn has following meaning: */
677 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
678 btn = tty_lowlevel_getch () - 32;
680 /* There seems to be no way of knowing which button was released */
681 /* So we assume all the buttons were released */
683 if (btn == 3) {
684 if (last_btn != 0) {
685 if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0) {
686 /* FIXME: DIRTY HACK */
687 /* don't generate GPM_UP after mouse wheel */
688 /* need for menu event handling */
689 ev->type = 0;
690 tv1.tv_sec = 0;
691 tv1.tv_usec = 0;
692 } else {
693 ev->type = GPM_UP | (GPM_SINGLE << clicks);
694 GET_TIME (tv1);
696 ev->buttons = 0;
697 last_btn = 0;
698 clicks = 0;
699 } else {
700 /* Bogus event, maybe mouse wheel */
701 ev->type = 0;
703 } else {
704 if (btn >= 32 && btn <= 34) {
705 btn -= 32;
706 ev->type = GPM_DRAG;
707 } else
708 ev->type = GPM_DOWN;
710 GET_TIME (tv2);
711 if (tv1.tv_sec && (DIF_TIME (tv1, tv2) < double_click_speed)) {
712 clicks++;
713 clicks %= 3;
714 } else
715 clicks = 0;
717 switch (btn) {
718 case 0:
719 ev->buttons = GPM_B_LEFT;
720 break;
721 case 1:
722 ev->buttons = GPM_B_MIDDLE;
723 break;
724 case 2:
725 ev->buttons = GPM_B_RIGHT;
726 break;
727 case 64:
728 ev->buttons = GPM_B_UP;
729 clicks = 0;
730 break;
731 case 65:
732 ev->buttons = GPM_B_DOWN;
733 clicks = 0;
734 break;
735 default:
736 /* Nothing */
737 ev->type = 0;
738 ev->buttons = 0;
739 break;
741 last_btn = ev->buttons;
743 /* Coordinates are 33-based */
744 /* Transform them to 1-based */
745 ev->x = tty_lowlevel_getch () - 32;
746 ev->y = tty_lowlevel_getch () - 32;
750 * Get modifier state (shift, alt, ctrl) for the last key pressed.
751 * We are assuming that the state didn't change since the key press.
752 * This is only correct if get_modifier() is called very fast after
753 * the input was received, so that the user didn't release the
754 * modifier keys yet.
756 static int
757 get_modifier (void)
759 int result = 0;
760 #ifdef __QNXNTO__
761 int mod_status, shift_ext_status;
762 static int in_photon = 0;
763 static int ph_ig = 0;
764 PhCursorInfo_t cursor_info;
765 #endif /* __QNXNTO__ */
767 #ifdef HAVE_TEXTMODE_X11_SUPPORT
768 if (x11_window != 0) {
769 Window root, child;
770 int root_x, root_y;
771 int win_x, win_y;
772 unsigned int mask;
774 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
775 &root_y, &win_x, &win_y, &mask);
777 if (mask & ShiftMask)
778 result |= KEY_M_SHIFT;
779 if (mask & ControlMask)
780 result |= KEY_M_CTRL;
781 return result;
783 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
784 #ifdef __QNXNTO__
786 if (in_photon == 0) {
787 /* First time here, let's load Photon library and attach
788 to Photon */
789 in_photon = -1;
790 if (getenv ("PHOTON2_PATH") != NULL) {
791 /* QNX 6.x has no support for RTLD_LAZY */
792 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
793 if (ph_handle != NULL) {
794 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
795 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
796 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
797 if ((ph_attach != NULL) && (ph_input_group != NULL)
798 && (ph_query_cursor != NULL)) {
799 if ((*ph_attach) (0, 0)) { /* Attached */
800 ph_ig = (*ph_input_group) (0);
801 in_photon = 1;
807 /* We do not have Photon running. Assume we are in text
808 console or xterm */
809 if (in_photon == -1) {
810 if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (int), NULL) == -1)
811 return 0;
812 shift_ext_status = mod_status & 0xffffff00UL;
813 mod_status &= 0x7f;
814 if (mod_status & _LINESTATUS_CON_ALT)
815 result |= KEY_M_ALT;
816 if (mod_status & _LINESTATUS_CON_CTRL)
817 result |= KEY_M_CTRL;
818 if ((mod_status & _LINESTATUS_CON_SHIFT)
819 || (shift_ext_status & 0x00000800UL))
820 result |= KEY_M_SHIFT;
821 } else {
822 (*ph_query_cursor) (ph_ig, &cursor_info);
823 if (cursor_info.key_mods & 0x04)
824 result |= KEY_M_ALT;
825 if (cursor_info.key_mods & 0x02)
826 result |= KEY_M_CTRL;
827 if (cursor_info.key_mods & 0x01)
828 result |= KEY_M_SHIFT;
830 #endif /* __QNXNTO__ */
832 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
834 unsigned char modifiers = 6;
836 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
837 return 0;
839 /* Translate Linux modifiers into mc modifiers */
840 if (modifiers & SHIFT_PRESSED)
841 result |= KEY_M_SHIFT;
842 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
843 result |= KEY_M_ALT;
844 if (modifiers & CONTROL_PRESSED)
845 result |= KEY_M_CTRL;
847 #endif /* !__linux__ */
848 return result;
851 static gboolean
852 push_char (int c)
854 gboolean ret = FALSE;
856 if (seq_append == NULL)
857 seq_append = seq_buffer;
859 if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2])) {
860 *(seq_append++) = c;
861 *seq_append = 0;
862 ret = TRUE;
865 return ret;
868 /* Apply corrections for the keycode generated in get_key_code() */
869 static int
870 correct_key_code (int code)
872 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
873 unsigned int mod = code & KEY_M_MASK; /* modifier */
874 #ifdef __QNXNTO__
875 unsigned int qmod; /* bunch of the QNX console
876 modifiers needs unchanged */
877 #endif /* __QNXNTO__ */
880 * Add key modifiers directly from X11 or OS.
881 * Ordinary characters only get modifiers from sequences.
883 if (c < 32 || c >= 256) {
884 mod |= get_modifier ();
887 /* This is needed if the newline is reported as carriage return */
888 if (c == '\r')
889 c = '\n';
891 /* This is reported to be useful on AIX */
892 if (c == KEY_SCANCEL)
893 c = '\t';
895 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
896 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
897 c = KEY_BTAB;
898 mod = 0;
901 /* F0 is the same as F10 for out purposes */
902 if (c == KEY_F (0))
903 c = KEY_F (10);
906 * We are not interested if Ctrl was pressed when entering control
907 * characters, so assume that it was. When checking for such keys,
908 * XCTRL macro should be used. In some cases, we are interested,
909 * e.g. to distinguish Ctrl-Enter from Enter.
911 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
912 mod |= KEY_M_CTRL;
914 #ifdef __QNXNTO__
915 qmod = get_modifier ();
917 if ((c == 127) && (mod == 0)) { /* Add Ctrl/Alt/Shift-BackSpace */
918 mod |= get_modifier ();
919 c = KEY_BACKSPACE;
922 if ((c == '0') && (mod == 0)) { /* Add Shift-Insert on key pad */
923 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT) {
924 mod = KEY_M_SHIFT;
925 c = KEY_IC;
929 if ((c == '.') && (mod == 0)) { /* Add Shift-Del on key pad */
930 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT) {
931 mod = KEY_M_SHIFT;
932 c = KEY_DC;
935 #endif /* __QNXNTO__ */
937 /* Unrecognized 0177 is delete (preserve Ctrl) */
938 if (c == 0177) {
939 c = KEY_BACKSPACE;
942 /* Unrecognized Ctrl-d is delete */
943 if (c == (31 & 'd')) {
944 c = KEY_DC;
945 mod &= ~KEY_M_CTRL;
948 /* Unrecognized Ctrl-h is backspace */
949 if (c == (31 & 'h')) {
950 c = KEY_BACKSPACE;
951 mod &= ~KEY_M_CTRL;
954 /* Shift+BackSpace is backspace */
955 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
956 mod &= ~KEY_M_SHIFT;
959 /* Convert Shift+Fn to F(n+10) */
960 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
961 c += 10;
964 /* Remove Shift information from function keys */
965 if (c >= KEY_F (1) && c <= KEY_F (20)) {
966 mod &= ~KEY_M_SHIFT;
969 if (!alternate_plus_minus)
970 switch (c) {
971 case KEY_KP_ADD:
972 c = '+';
973 break;
974 case KEY_KP_SUBTRACT:
975 c = '-';
976 break;
977 case KEY_KP_MULTIPLY:
978 c = '*';
979 break;
982 return (mod | c);
985 static int
986 xgetch_second (void)
988 fd_set Read_FD_Set;
989 int c;
990 struct timeval time_out;
992 time_out.tv_sec = old_esc_mode_timeout / 1000000;
993 time_out.tv_usec = old_esc_mode_timeout % 1000000;
994 tty_nodelay (TRUE);
995 FD_ZERO (&Read_FD_Set);
996 FD_SET (input_fd, &Read_FD_Set);
997 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
998 c = tty_lowlevel_getch ();
999 tty_nodelay (FALSE);
1000 return c;
1003 static void
1004 learn_store_key (char *buffer, char **p, int c)
1006 if (*p - buffer > 253)
1007 return;
1008 if (c == ESC_CHAR) {
1009 *(*p)++ = '\\';
1010 *(*p)++ = 'e';
1011 } else if (c < ' ') {
1012 *(*p)++ = '^';
1013 *(*p)++ = c + 'a' - 1;
1014 } else if (c == '^') {
1015 *(*p)++ = '^';
1016 *(*p)++ = '^';
1017 } else
1018 *(*p)++ = (char) c;
1021 static void
1022 k_dispose (key_def * k)
1024 if (k != NULL) {
1025 k_dispose (k->child);
1026 k_dispose (k->next);
1027 g_free (k);
1031 static void
1032 s_dispose (SelectList * sel)
1034 if (sel != NULL) {
1035 s_dispose (sel->next);
1036 g_free (sel);
1040 /*** public functions **************************************************/
1042 /* This has to be called before init_slang or whatever routine
1043 calls any define_sequence */
1044 void
1045 init_key (void)
1047 const char *term = getenv ("TERM");
1049 /* This has to be the first define_sequence */
1050 /* So, we can assume that the first keys member has ESC */
1051 define_sequences (mc_default_keys);
1053 /* Terminfo on irix does not have some keys */
1054 if (xterm_flag
1055 || (term != NULL
1056 && (strncmp (term, "iris-ansi", 9) == 0
1057 || strncmp (term, "xterm", 5) == 0
1058 || strncmp (term, "rxvt", 4) == 0 || strcmp (term, "screen") == 0)))
1059 define_sequences (xterm_key_defines);
1061 /* load some additional keys (e.g. direct Alt-? support) */
1062 load_xtra_key_defines ();
1064 #ifdef __QNX__
1065 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0)) {
1066 /* Modify the default value of use_8th_bit_as_meta: we would
1067 * like to provide a working mc for a newbie who knows nothing
1068 * about [Options|Display bits|Full 8 bits input]...
1070 * Don't use 'meta'-bit, when we are dealing with a
1071 * 'qnx*'-type terminal: clear the default value!
1072 * These terminal types use 0xFF as an escape character,
1073 * so use_8th_bit_as_meta==1 must not be enabled!
1075 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1076 * is not used now (doesn't even depend on use_8th_bit_as_meta
1077 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1079 use_8th_bit_as_meta = 0;
1081 #endif /* __QNX__ */
1083 init_key_x11 ();
1085 /* Load the qansi-m key definitions
1086 if we are running under the qansi-m terminal */
1087 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1088 define_sequences (qansi_key_defines);
1091 /* This has to be called after SLang_init_tty/slint_init */
1092 void
1093 init_key_input_fd (void)
1095 #ifdef HAVE_SLANG
1096 input_fd = SLang_TT_Read_FD;
1097 #endif
1100 void
1101 done_key (void)
1103 k_dispose (keys);
1104 s_dispose (select_list);
1106 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1107 if (x11_display)
1108 mc_XCloseDisplay (x11_display);
1109 #endif
1112 void
1113 add_select_channel (int fd, select_fn callback, void *info)
1115 SelectList *new;
1117 new = g_new (SelectList, 1);
1118 new->fd = fd;
1119 new->callback = callback;
1120 new->info = info;
1121 new->next = select_list;
1122 select_list = new;
1125 void
1126 delete_select_channel (int fd)
1128 SelectList *p = select_list;
1129 SelectList *p_prev = NULL;
1130 SelectList *p_next;
1132 while (p != NULL)
1133 if (p->fd == fd) {
1134 p_next = p->next;
1136 if (p_prev != NULL)
1137 p_prev->next = p_next;
1138 else
1139 select_list = p_next;
1141 g_free (p);
1142 p = p_next;
1143 } else {
1144 p_prev = p;
1145 p = p->next;
1149 void
1150 channels_up (void)
1152 if (disabled_channels == 0)
1153 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1154 disabled_channels--;
1157 void
1158 channels_down (void)
1160 disabled_channels++;
1164 * Common handler for standard movement keys in a text area. Provided
1165 * functions are called with the "data" argument. backfn and forfn also
1166 * get an argument indicating how many lines to scroll. Return MSG_HANDLED
1167 * if the key was handled, MSG_NOT_HANDLED otherwise.
1169 cb_ret_t
1170 check_movement_keys (int key, int page_size, void *data, move_fn backfn,
1171 move_fn forfn, move_fn topfn, move_fn bottomfn)
1173 switch (key) {
1174 case KEY_UP:
1175 case XCTRL ('p'):
1176 (*backfn) (data, 1);
1177 break;
1179 case KEY_DOWN:
1180 case XCTRL ('n'):
1181 (*forfn) (data, 1);
1182 break;
1184 case KEY_PPAGE:
1185 case ALT ('v'):
1186 (*backfn) (data, page_size - 1);
1187 break;
1189 case KEY_NPAGE:
1190 case XCTRL ('v'):
1191 (*forfn) (data, page_size - 1);
1192 break;
1194 case KEY_HOME:
1195 case KEY_M_CTRL | KEY_HOME:
1196 case KEY_M_CTRL | KEY_PPAGE:
1197 case KEY_A1:
1198 case ALT ('<'):
1199 (*topfn) (data, 0);
1200 break;
1202 case KEY_END:
1203 case KEY_M_CTRL | KEY_END:
1204 case KEY_M_CTRL | KEY_NPAGE:
1205 case KEY_C1:
1206 case ALT ('>'):
1207 (*bottomfn) (data, 0);
1208 break;
1210 case 'b':
1211 case KEY_BACKSPACE:
1212 (*backfn) (data, page_size - 1);
1213 break;
1215 case ' ':
1216 (*forfn) (data, page_size - 1);
1217 break;
1219 case 'u':
1220 (*backfn) (data, page_size / 2);
1221 break;
1223 case 'd':
1224 (*forfn) (data, page_size / 2);
1225 break;
1227 case 'g':
1228 (*topfn) (data, 0);
1229 break;
1231 case 'G':
1232 (*bottomfn) (data, 0);
1233 break;
1235 default:
1236 return MSG_NOT_HANDLED;
1238 return MSG_HANDLED;
1242 static const size_t key_name_conv_tab_size = sizeof (key_name_conv_tab) /
1243 sizeof (key_name_conv_tab[0]) - 1;
1244 static const key_code_name_t *key_name_conv_tab_sorted[sizeof (key_name_conv_tab) /
1245 sizeof (key_name_conv_tab[0]) - 1];
1247 static int
1248 key_code_name_comparator (const void *p1, const void *p2)
1250 const key_code_name_t *n1 = *(const key_code_name_t **) p1;
1251 const key_code_name_t *n2 = *(const key_code_name_t **) p2;
1253 return str_casecmp (n1->name, n2->name);
1256 static inline void
1257 sort_key_name_conv_tab (void)
1259 static gboolean has_been_sorted = FALSE;
1261 if (!has_been_sorted) {
1262 size_t i;
1263 for (i = 0; i < key_name_conv_tab_size; i++)
1264 key_name_conv_tab_sorted[i] = &key_name_conv_tab[i];
1266 qsort (key_name_conv_tab_sorted,
1267 key_name_conv_tab_size, sizeof (key_name_conv_tab_sorted[0]),
1268 &key_code_name_comparator);
1269 has_been_sorted = TRUE;
1273 static int
1274 lookup_keyname (const char *name, int *idx)
1276 if (name[0] != '\0') {
1277 const key_code_name_t key = { 0, name, NULL, NULL };
1278 const key_code_name_t *keyp = &key;
1279 key_code_name_t **res;
1281 if (name[1] == '\0') {
1282 *idx = -1;
1283 return (int) name[0];
1286 sort_key_name_conv_tab ();
1288 res = bsearch (&keyp, key_name_conv_tab_sorted,
1289 key_name_conv_tab_size,
1290 sizeof (key_name_conv_tab_sorted[0]),
1291 key_code_name_comparator);
1293 if (res != NULL) {
1294 *idx = (int) (res - (key_code_name_t **) key_name_conv_tab_sorted);
1295 return (*res)->code;
1299 *idx = -1;
1300 return 0;
1303 /* Return the code associated with the symbolic name keyname */
1304 long
1305 lookup_key (const char *name, char **label)
1307 char **lc_keys, **p;
1308 int k = -1;
1309 int key = 0;
1310 int lc_index = -1;
1312 int use_meta = -1;
1313 int use_ctrl = -1;
1314 int use_shift = -1;
1316 if (name == NULL)
1317 return 0;
1319 name = g_strstrip (g_strdup (name));
1320 p = lc_keys = g_strsplit_set (name, "-+ ", -1);
1321 g_free ((char *) name);
1323 while ((p != NULL) && (*p != NULL)) {
1324 if ((*p)[0] != '\0') {
1325 int idx;
1327 key = lookup_keyname (g_strstrip (*p), &idx);
1329 if (key == KEY_M_ALT)
1330 use_meta = idx;
1331 else if (key == KEY_M_CTRL)
1332 use_ctrl = idx;
1333 else if (key == KEY_M_SHIFT)
1334 use_shift = idx;
1335 else {
1336 k = key;
1337 lc_index = idx;
1338 break;
1342 p++;
1345 g_strfreev (lc_keys);
1347 /* output */
1348 if (k <= 0)
1349 return 0;
1352 if (label != NULL) {
1353 GString *s;
1355 s = g_string_new ("");
1357 if (use_meta != -1) {
1358 g_string_append (s, key_name_conv_tab_sorted[use_meta]->shortcut);
1359 g_string_append_c (s, '-');
1361 if (use_ctrl != -1) {
1362 g_string_append (s, key_name_conv_tab_sorted[use_ctrl]->shortcut);
1363 g_string_append_c (s, '-');
1365 if (use_shift != -1) {
1366 if (k < 127)
1367 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1368 else {
1369 g_string_append (s, key_name_conv_tab_sorted[use_shift]->shortcut);
1370 g_string_append_c (s, '-');
1371 g_string_append (s, key_name_conv_tab_sorted[lc_index]->shortcut);
1373 } else if (k < 128) {
1374 if ((k >= 'A') || (lc_index < 0)
1375 || (key_name_conv_tab_sorted[lc_index]->shortcut == NULL))
1376 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1377 else
1378 g_string_append (s, key_name_conv_tab_sorted[lc_index]->shortcut);
1379 } else if ((lc_index != -1) && (key_name_conv_tab_sorted[lc_index]->shortcut != NULL))
1380 g_string_append (s, key_name_conv_tab_sorted[lc_index]->shortcut);
1381 else
1382 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1384 *label = g_string_free (s, FALSE);
1387 if (use_shift != -1) {
1388 if (k < 127 && k > 31 )
1389 k = g_ascii_toupper ((gchar) k);
1390 else
1391 k |= KEY_M_SHIFT;
1394 if (use_ctrl != -1) {
1395 if (k < 256)
1396 k = XCTRL (k);
1397 else
1398 k |= KEY_M_CTRL;
1401 if (use_meta != -1)
1402 k = ALT (k);
1404 return (long) k;
1408 * Return TRUE on success, FALSE on error.
1409 * An error happens if SEQ is a beginning of an existing longer sequence.
1411 gboolean
1412 define_sequence (int code, const char *seq, int action)
1414 key_def *base;
1416 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1417 return FALSE;
1419 for (base = keys; (base != NULL) && (*seq != '\0');)
1420 if (*seq == base->ch) {
1421 if (base->child == 0) {
1422 if (*(seq + 1) != '\0')
1423 base->child = create_sequence (seq + 1, code, action);
1424 else {
1425 /* The sequence matches an existing one. */
1426 base->code = code;
1427 base->action = action;
1429 return TRUE;
1432 base = base->child;
1433 seq++;
1434 } else {
1435 if (base->next)
1436 base = base->next;
1437 else {
1438 base->next = create_sequence (seq, code, action);
1439 return TRUE;
1443 if (*seq == '\0') {
1444 /* Attempt to redefine a sequence with a shorter sequence. */
1445 return FALSE;
1448 keys = create_sequence (seq, code, action);
1449 return TRUE;
1453 * Check if we are idle, i.e. there are no pending keyboard or mouse
1454 * events. Return 1 is idle, 0 is there are pending events.
1456 gboolean
1457 is_idle (void)
1459 int maxfdp;
1460 fd_set select_set;
1461 struct timeval time_out;
1463 FD_ZERO (&select_set);
1464 FD_SET (input_fd, &select_set);
1465 maxfdp = input_fd;
1466 #ifdef HAVE_LIBGPM
1467 if (mouse_enabled && (use_mouse_p == MOUSE_GPM) && (gpm_fd > 0)) {
1468 FD_SET (gpm_fd, &select_set);
1469 maxfdp = max (maxfdp, gpm_fd);
1471 #endif
1472 time_out.tv_sec = 0;
1473 time_out.tv_usec = 0;
1474 return (select (maxfdp + 1, &select_set, 0, 0, &time_out) <= 0);
1478 get_key_code (int no_delay)
1480 int c;
1481 static key_def *this = NULL, *parent;
1482 static struct timeval esctime = { -1, -1 };
1483 static int lastnodelay = -1;
1485 if (no_delay != lastnodelay) {
1486 this = NULL;
1487 lastnodelay = no_delay;
1490 pend_send:
1491 if (pending_keys != NULL) {
1492 int d = *pending_keys++;
1493 check_pend:
1494 if (*pending_keys == 0) {
1495 pending_keys = NULL;
1496 seq_append = NULL;
1498 if ((d == ESC_CHAR) && (pending_keys != NULL)) {
1499 d = ALT (*pending_keys++);
1500 goto check_pend;
1502 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
1503 d = ALT (d & 0x7f);
1504 this = NULL;
1505 return correct_key_code (d);
1508 nodelay_try_again:
1509 if (no_delay)
1510 tty_nodelay (TRUE);
1512 c = tty_lowlevel_getch ();
1513 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1514 if (c == KEY_RESIZE)
1515 goto nodelay_try_again;
1516 #endif
1517 if (no_delay) {
1518 tty_nodelay (FALSE);
1519 if (c == -1) {
1520 if (this != NULL && parent != NULL && parent->action == MCKEY_ESCAPE && old_esc_mode) {
1521 struct timeval current, time_out;
1523 if (esctime.tv_sec == -1)
1524 return -1;
1525 GET_TIME (current);
1526 time_out.tv_sec = old_esc_mode_timeout / 1000000 + esctime.tv_sec;
1527 time_out.tv_usec = old_esc_mode_timeout % 1000000 + esctime.tv_usec;
1528 if (time_out.tv_usec > 1000000) {
1529 time_out.tv_usec -= 1000000;
1530 time_out.tv_sec++;
1532 if (current.tv_sec < time_out.tv_sec)
1533 return -1;
1534 if (current.tv_sec == time_out.tv_sec && current.tv_usec < time_out.tv_usec)
1535 return -1;
1536 this = NULL;
1537 pending_keys = seq_append = NULL;
1538 return ESC_CHAR;
1540 return -1;
1542 } else if (c == -1) {
1543 /* Maybe we got an incomplete match.
1544 This we do only in delay mode, since otherwise
1545 tty_lowlevel_getch can return -1 at any time. */
1546 if (seq_append != NULL) {
1547 pending_keys = seq_buffer;
1548 goto pend_send;
1550 this = NULL;
1551 return -1;
1554 /* Search the key on the root */
1555 if (!no_delay || this == NULL) {
1556 this = keys;
1557 parent = NULL;
1559 if ((c > 127 && c < 256) && use_8th_bit_as_meta) {
1560 c &= 0x7f;
1562 /* The first sequence defined starts with esc */
1563 parent = keys;
1564 this = keys->child;
1567 while (this != NULL) {
1568 if (c == this->ch) {
1569 if (this->child) {
1570 if (!push_char (c)) {
1571 pending_keys = seq_buffer;
1572 goto pend_send;
1574 parent = this;
1575 this = this->child;
1576 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
1577 if (no_delay) {
1578 GET_TIME (esctime);
1579 if (this == NULL) {
1580 /* Shouldn't happen */
1581 fputs ("Internal error\n", stderr);
1582 exit (EXIT_FAILURE);
1584 goto nodelay_try_again;
1586 esctime.tv_sec = -1;
1587 c = xgetch_second ();
1588 if (c == -1) {
1589 pending_keys = seq_append = NULL;
1590 this = NULL;
1591 return ESC_CHAR;
1593 } else {
1594 if (no_delay)
1595 goto nodelay_try_again;
1596 c = tty_lowlevel_getch ();
1598 } else {
1599 /* We got a complete match, return and reset search */
1600 int code;
1602 pending_keys = seq_append = NULL;
1603 code = this->code;
1604 this = NULL;
1605 return correct_key_code (code);
1607 } else {
1608 if (this->next != NULL)
1609 this = this->next;
1610 else {
1611 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) {
1612 /* Convert escape-digits to F-keys */
1613 if (g_ascii_isdigit (c))
1614 c = KEY_F (c - '0');
1615 else if (c == ' ')
1616 c = ESC_CHAR;
1617 else
1618 c = ALT (c);
1620 pending_keys = seq_append = NULL;
1621 this = NULL;
1622 return correct_key_code (c);
1624 /* Did not find a match or {c} was changed in the if above,
1625 so we have to return everything we had skipped
1627 push_char (c);
1628 pending_keys = seq_buffer;
1629 goto pend_send;
1633 this = NULL;
1634 return correct_key_code (c);
1637 /* Returns a character read from stdin with appropriate interpretation */
1638 /* Also takes care of generated mouse events */
1639 /* Returns EV_MOUSE if it is a mouse event */
1640 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1642 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1644 int c;
1645 static int flag = 0; /* Return value from select */
1646 #ifdef HAVE_LIBGPM
1647 static struct Gpm_Event ev; /* Mouse event */
1648 #endif
1649 struct timeval time_out;
1650 struct timeval *time_addr = NULL;
1651 static int dirty = 3;
1653 if ((dirty == 3) || is_idle ()) {
1654 mc_refresh ();
1655 dirty = 1;
1656 } else
1657 dirty++;
1659 vfs_timeout_handler ();
1661 /* Ok, we use (event->x < 0) to signal that the event does not contain
1662 a suitable position for the mouse, so we can't use show_mouse_pointer
1663 on it.
1665 if (event->x > 0) {
1666 show_mouse_pointer (event->x, event->y);
1667 if (!redo_event)
1668 event->x = -1;
1671 /* Repeat if using mouse */
1672 while (pending_keys == NULL) {
1673 int maxfdp;
1674 fd_set select_set;
1676 FD_ZERO (&select_set);
1677 FD_SET (input_fd, &select_set);
1678 maxfdp = max (add_selects (&select_set), input_fd);
1680 #ifdef HAVE_LIBGPM
1681 if (mouse_enabled && (use_mouse_p == MOUSE_GPM)) {
1682 if (gpm_fd < 0) {
1683 /* Connection to gpm broken, possibly gpm has died */
1684 mouse_enabled = FALSE;
1685 use_mouse_p = MOUSE_NONE;
1686 break;
1689 FD_SET (gpm_fd, &select_set);
1690 maxfdp = max (maxfdp, gpm_fd);
1692 #endif
1694 if (redo_event) {
1695 time_out.tv_usec = mou_auto_repeat * 1000;
1696 time_out.tv_sec = 0;
1698 time_addr = &time_out;
1699 } else {
1700 int seconds;
1702 seconds = vfs_timeouts ();
1703 time_addr = NULL;
1705 if (seconds != 0) {
1706 /* the timeout could be improved and actually be
1707 * the number of seconds until the next vfs entry
1708 * timeouts in the stamp list.
1711 time_out.tv_sec = seconds;
1712 time_out.tv_usec = 0;
1713 time_addr = &time_out;
1717 if (!block || winch_flag) {
1718 time_addr = &time_out;
1719 time_out.tv_sec = 0;
1720 time_out.tv_usec = 0;
1723 tty_enable_interrupt_key ();
1724 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1725 tty_disable_interrupt_key ();
1727 /* select timed out: it could be for any of the following reasons:
1728 * redo_event -> it was because of the MOU_REPEAT handler
1729 * !block -> we did not block in the select call
1730 * else -> 10 second timeout to check the vfs status.
1732 if (flag == 0) {
1733 if (redo_event)
1734 return EV_MOUSE;
1735 if (!block || winch_flag)
1736 return EV_NONE;
1737 vfs_timeout_handler ();
1739 if (flag == -1 && errno == EINTR)
1740 return EV_NONE;
1742 check_selects (&select_set);
1744 if (FD_ISSET (input_fd, &select_set))
1745 break;
1746 #ifdef HAVE_LIBGPM
1747 if (mouse_enabled && use_mouse_p == MOUSE_GPM
1748 && gpm_fd > 0 && FD_ISSET (gpm_fd, &select_set)) {
1749 Gpm_GetEvent (&ev);
1750 Gpm_FitEvent (&ev);
1751 *event = ev;
1752 return EV_MOUSE;
1754 #endif /* !HAVE_LIBGPM */
1757 #ifndef HAVE_SLANG
1758 flag = is_wintouched (stdscr);
1759 untouchwin (stdscr);
1760 #endif /* !HAVE_SLANG */
1761 c = block ? getch_with_delay () : get_key_code (1);
1763 #ifndef HAVE_SLANG
1764 if (flag > 0)
1765 tty_touch_screen ();
1766 #endif /* !HAVE_SLANG */
1768 if (mouse_enabled && (c == MCKEY_MOUSE
1769 #ifdef KEY_MOUSE
1770 || c == KEY_MOUSE
1771 #endif /* KEY_MOUSE */
1772 )) {
1773 /* Mouse event */
1774 xmouse_get_event (event);
1775 return (event->type != 0) ? EV_MOUSE : EV_NONE;
1778 return c;
1781 /* Returns a key press, mouse events are discarded */
1783 tty_getch (void)
1785 Gpm_Event ev;
1786 int key;
1788 ev.x = -1;
1789 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE);
1790 return key;
1793 char *
1794 learn_key (void)
1796 /* LEARN_TIMEOUT in usec */
1797 #define LEARN_TIMEOUT 200000
1799 fd_set Read_FD_Set;
1800 struct timeval endtime;
1801 struct timeval time_out;
1802 int c;
1803 char buffer[256];
1804 char *p = buffer;
1806 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
1807 c = tty_lowlevel_getch ();
1808 while (c == -1)
1809 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
1810 learn_store_key (buffer, &p, c);
1811 GET_TIME (endtime);
1812 endtime.tv_usec += LEARN_TIMEOUT;
1813 if (endtime.tv_usec > 1000000) {
1814 endtime.tv_usec -= 1000000;
1815 endtime.tv_sec++;
1817 tty_nodelay (TRUE);
1818 for (;;) {
1819 while ((c = tty_lowlevel_getch ()) == -1) {
1820 GET_TIME (time_out);
1821 time_out.tv_usec = endtime.tv_usec - time_out.tv_usec;
1822 if (time_out.tv_usec < 0)
1823 time_out.tv_sec++;
1824 time_out.tv_sec = endtime.tv_sec - time_out.tv_sec;
1825 if (time_out.tv_sec >= 0 && time_out.tv_usec > 0) {
1826 FD_ZERO (&Read_FD_Set);
1827 FD_SET (input_fd, &Read_FD_Set);
1828 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1829 } else
1830 break;
1832 if (c == -1)
1833 break;
1834 learn_store_key (buffer, &p, c);
1836 tty_keypad (TRUE);
1837 tty_nodelay (FALSE);
1838 *p = '\0';
1839 return g_strdup (buffer);
1840 #undef LEARN_TIMEOUT
1843 /* xterm and linux console only: set keypad to numeric or application
1844 mode. Only in application keypad mode it's possible to distinguish
1845 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1846 void
1847 numeric_keypad_mode (void)
1849 if (console_flag || xterm_flag) {
1850 fputs ("\033>", stdout);
1851 fflush (stdout);
1855 void
1856 application_keypad_mode (void)
1858 if (console_flag || xterm_flag) {
1859 fputs ("\033=", stdout);
1860 fflush (stdout);