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.
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. */
26 * \brief Source: keyboard support routines
37 #include <sys/types.h>
40 #include "lib/global.h"
41 #include "lib/strutil.h" /* str_casecmp */
43 #include "lib/vfs/mc-vfs/vfs.h"
46 #include "lib/vfs/mc-vfs/gc.h"
47 #endif /* ENABLE_VFS */
50 #include "tty-internal.h" /* mouse_enabled */
53 #include "win.h" /* xterm_flag */
56 #include "src/layout.h" /* winch_flag, mc_refresh() */
57 #include "src/consaver/cons.saver.h"
60 #ifdef HAVE_TEXTMODE_X11_SUPPORT
65 #if defined(__GLIBC__) && (__GLIBC__ < 2)
66 # include <linux/termios.h> /* TIOCLINUX */
70 #include <sys/ioctl.h>
71 #endif /* __linux__ */
75 #include <sys/ioctl.h>
76 #endif /* __CYGWIN__ */
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;
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) '\n', "enter", N_("Enter"), "Enter" },
184 { (int) '\t', "tab", N_("Tab key"), "Tab" },
185 { (int) ' ', "space", N_("Space key"), "Space" },
186 { (int) '/', "slash", N_("Slash key"), "/" },
187 { (int) '\\', "backslash", N_("Backslash key"), "\\" },
188 { (int) '#', "number", N_("Number sign #"), "#" },
189 { (int) '#', "hash", N_("Number sign #"), "#" },
192 { KEY_M_CTRL
, "control", N_("Ctrl"), "C" },
193 { KEY_M_CTRL
, "ctrl", N_("Ctrl"), "C" },
194 { KEY_M_ALT
, "meta", N_("Alt"), "M" },
195 { KEY_M_ALT
, "alt", N_("Alt"), "M" },
196 { KEY_M_ALT
, "ralt", N_("Alt"), "M" },
197 { KEY_M_SHIFT
, "shift", N_("Shift"), "S" },
199 { 0, NULL
, NULL
, NULL
}
203 /*** file scope macro definitions **************************************/
205 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
206 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
208 /* The maximum sequence length (32 + null terminator) */
209 #define SEQ_BUFFER_LEN 33
211 /*** file scope type declarations **************************************/
213 /* Linux console keyboard modifiers */
215 SHIFT_PRESSED
= (1 << 0),
216 ALTR_PRESSED
= (1 << 1),
217 CONTROL_PRESSED
= (1 << 2),
218 ALTL_PRESSED
= (1 << 3)
221 typedef struct key_def
{
222 char ch
; /* Holds the matching char code */
223 int code
; /* The code returned, valid if child == NULL */
224 struct key_def
*next
;
225 struct key_def
*child
; /* sequence continuation */
226 int action
; /* optional action to be done. Now used only
227 to mark that we are just after the first
237 /* File descriptor monitoring add/remove routines */
238 typedef struct SelectList
{
242 struct SelectList
*next
;
246 typedef int (*ph_dv_f
) (void *, void *);
247 typedef int (*ph_ov_f
) (void *);
248 typedef int (*ph_pqc_f
) (unsigned short, PhCursorInfo_t
*);
251 /*** file scope variables **********************************************/
253 static key_define_t mc_default_keys
[] = {
254 {ESC_CHAR
, ESC_STR
, MCKEY_ESCAPE
},
255 {ESC_CHAR
, ESC_STR ESC_STR
, MCKEY_NOACTION
},
256 {0, NULL
, MCKEY_NOACTION
},
259 /* Broken terminfo and termcap databases on xterminals */
260 static key_define_t xterm_key_defines
[] = {
261 {KEY_F (1), ESC_STR
"OP", MCKEY_NOACTION
},
262 {KEY_F (2), ESC_STR
"OQ", MCKEY_NOACTION
},
263 {KEY_F (3), ESC_STR
"OR", MCKEY_NOACTION
},
264 {KEY_F (4), ESC_STR
"OS", MCKEY_NOACTION
},
265 {KEY_F (1), ESC_STR
"[11~", MCKEY_NOACTION
},
266 {KEY_F (2), ESC_STR
"[12~", MCKEY_NOACTION
},
267 {KEY_F (3), ESC_STR
"[13~", MCKEY_NOACTION
},
268 {KEY_F (4), ESC_STR
"[14~", MCKEY_NOACTION
},
269 {KEY_F (5), ESC_STR
"[15~", MCKEY_NOACTION
},
270 {KEY_F (6), ESC_STR
"[17~", MCKEY_NOACTION
},
271 {KEY_F (7), ESC_STR
"[18~", MCKEY_NOACTION
},
272 {KEY_F (8), ESC_STR
"[19~", MCKEY_NOACTION
},
273 {KEY_F (9), ESC_STR
"[20~", MCKEY_NOACTION
},
274 {KEY_F (10), ESC_STR
"[21~", MCKEY_NOACTION
},
276 /* old xterm Shift-arrows */
277 {KEY_M_SHIFT
| KEY_UP
, ESC_STR
"O2A", MCKEY_NOACTION
},
278 {KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"O2B", MCKEY_NOACTION
},
279 {KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"O2C", MCKEY_NOACTION
},
280 {KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"O2D", MCKEY_NOACTION
},
282 /* new xterm Shift-arrows */
283 {KEY_M_SHIFT
| KEY_UP
, ESC_STR
"[1;2A", MCKEY_NOACTION
},
284 {KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"[1;2B", MCKEY_NOACTION
},
285 {KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"[1;2C", MCKEY_NOACTION
},
286 {KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"[1;2D", MCKEY_NOACTION
},
288 /* more xterm keys with modifiers */
289 {KEY_M_CTRL
| KEY_PPAGE
, ESC_STR
"[5;5~", MCKEY_NOACTION
},
290 {KEY_M_CTRL
| KEY_NPAGE
, ESC_STR
"[6;5~", MCKEY_NOACTION
},
291 {KEY_M_CTRL
| KEY_IC
, ESC_STR
"[2;5~", MCKEY_NOACTION
},
292 {KEY_M_CTRL
| KEY_DC
, ESC_STR
"[3;5~", MCKEY_NOACTION
},
293 {KEY_M_CTRL
| KEY_HOME
, ESC_STR
"[1;5H", MCKEY_NOACTION
},
294 {KEY_M_CTRL
| KEY_END
, ESC_STR
"[1;5F", MCKEY_NOACTION
},
295 {KEY_M_SHIFT
| KEY_HOME
, ESC_STR
"[1;2H", MCKEY_NOACTION
},
296 {KEY_M_SHIFT
| KEY_END
, ESC_STR
"[1;2F", MCKEY_NOACTION
},
297 {KEY_M_CTRL
| KEY_UP
, ESC_STR
"[1;5A", MCKEY_NOACTION
},
298 {KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"[1;5B", MCKEY_NOACTION
},
299 {KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"[1;5C", MCKEY_NOACTION
},
300 {KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"[1;5D", MCKEY_NOACTION
},
301 {KEY_M_SHIFT
| KEY_IC
, ESC_STR
"[2;2~", MCKEY_NOACTION
},
302 {KEY_M_SHIFT
| KEY_DC
, ESC_STR
"[3;2~", MCKEY_NOACTION
},
303 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_UP
, ESC_STR
"[1;6A", MCKEY_NOACTION
},
304 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"[1;6B", MCKEY_NOACTION
},
305 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"[1;6C", MCKEY_NOACTION
},
306 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"[1;6D", 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 alt-arrow keys */
315 /* removed as source esc esc esc trouble */
317 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
318 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
319 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
320 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
321 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
322 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
323 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
324 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
326 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
327 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
328 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
329 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
331 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
332 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
333 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
334 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
336 /* xterm alt-arrow keys */
337 {KEY_M_ALT
| KEY_UP
, ESC_STR
"[1;3A", MCKEY_NOACTION
},
338 {KEY_M_ALT
| KEY_DOWN
, ESC_STR
"[1;3B", MCKEY_NOACTION
},
339 {KEY_M_ALT
| KEY_RIGHT
, ESC_STR
"[1;3C", MCKEY_NOACTION
},
340 {KEY_M_ALT
| KEY_LEFT
, ESC_STR
"[1;3D", MCKEY_NOACTION
},
341 {KEY_M_ALT
| KEY_PPAGE
, ESC_STR
"[5;3~", MCKEY_NOACTION
},
342 {KEY_M_ALT
| KEY_NPAGE
, ESC_STR
"[6;3~", MCKEY_NOACTION
},
343 {KEY_M_ALT
| KEY_HOME
, ESC_STR
"[1~", MCKEY_NOACTION
},
344 {KEY_M_ALT
| KEY_END
, ESC_STR
"[4~", MCKEY_NOACTION
},
345 {KEY_M_CTRL
| KEY_M_ALT
| KEY_UP
, ESC_STR
"[1;7A", MCKEY_NOACTION
},
346 {KEY_M_CTRL
| KEY_M_ALT
| KEY_DOWN
, ESC_STR
"[1;7B", MCKEY_NOACTION
},
347 {KEY_M_CTRL
| KEY_M_ALT
| KEY_RIGHT
, ESC_STR
"[1;7C", MCKEY_NOACTION
},
348 {KEY_M_CTRL
| KEY_M_ALT
| KEY_LEFT
, ESC_STR
"[1;7D", MCKEY_NOACTION
},
349 {KEY_M_CTRL
| KEY_M_ALT
| KEY_PPAGE
, ESC_STR
"[5;7~", MCKEY_NOACTION
},
350 {KEY_M_CTRL
| KEY_M_ALT
| KEY_NPAGE
, ESC_STR
"[6;7~", MCKEY_NOACTION
},
351 {KEY_M_CTRL
| KEY_M_ALT
| KEY_HOME
, ESC_STR
"OH", MCKEY_NOACTION
},
352 {KEY_M_CTRL
| KEY_M_ALT
| KEY_END
, ESC_STR
"OF", MCKEY_NOACTION
},
354 /* rxvt keys with modifiers */
355 {KEY_M_SHIFT
| KEY_UP
, ESC_STR
"[a", MCKEY_NOACTION
},
356 {KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"[b", MCKEY_NOACTION
},
357 {KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"[c", MCKEY_NOACTION
},
358 {KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"[d", MCKEY_NOACTION
},
359 {KEY_M_CTRL
| KEY_UP
, ESC_STR
"Oa", MCKEY_NOACTION
},
360 {KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"Ob", MCKEY_NOACTION
},
361 {KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"Oc", MCKEY_NOACTION
},
362 {KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"Od", MCKEY_NOACTION
},
363 {KEY_M_CTRL
| KEY_PPAGE
, ESC_STR
"[5^", MCKEY_NOACTION
},
364 {KEY_M_CTRL
| KEY_NPAGE
, ESC_STR
"[6^", MCKEY_NOACTION
},
365 {KEY_M_CTRL
| KEY_HOME
, ESC_STR
"[7^", MCKEY_NOACTION
},
366 {KEY_M_CTRL
| KEY_END
, ESC_STR
"[8^", MCKEY_NOACTION
},
367 {KEY_M_SHIFT
| KEY_HOME
, ESC_STR
"[7$", MCKEY_NOACTION
},
368 {KEY_M_SHIFT
| KEY_END
, ESC_STR
"[8$", MCKEY_NOACTION
},
369 {KEY_M_CTRL
| KEY_IC
, ESC_STR
"[2^", MCKEY_NOACTION
},
370 {KEY_M_CTRL
| KEY_DC
, ESC_STR
"[3^", MCKEY_NOACTION
},
371 {KEY_M_SHIFT
| KEY_DC
, ESC_STR
"[3$", MCKEY_NOACTION
},
373 /* konsole keys with modifiers */
374 {KEY_M_SHIFT
| KEY_HOME
, ESC_STR
"O2H", MCKEY_NOACTION
},
375 {KEY_M_SHIFT
| KEY_END
, ESC_STR
"O2F", MCKEY_NOACTION
},
378 {KEY_M_SHIFT
| KEY_UP
, ESC_STR
"[2A", MCKEY_NOACTION
},
379 {KEY_M_SHIFT
| KEY_DOWN
, ESC_STR
"[2B", MCKEY_NOACTION
},
380 {KEY_M_SHIFT
| KEY_RIGHT
, ESC_STR
"[2C", MCKEY_NOACTION
},
381 {KEY_M_SHIFT
| KEY_LEFT
, ESC_STR
"[2D", MCKEY_NOACTION
},
382 {KEY_M_CTRL
| KEY_UP
, ESC_STR
"[5A", MCKEY_NOACTION
},
383 {KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"[5B", MCKEY_NOACTION
},
384 {KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"[5C", MCKEY_NOACTION
},
385 {KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"[5D", MCKEY_NOACTION
},
386 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_UP
, ESC_STR
"[6A", MCKEY_NOACTION
},
387 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"[6B", MCKEY_NOACTION
},
388 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"[6C", MCKEY_NOACTION
},
389 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"[6D", MCKEY_NOACTION
},
391 /* gnome-terminal - application mode */
392 {KEY_M_CTRL
| KEY_UP
, ESC_STR
"O5A", MCKEY_NOACTION
},
393 {KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"O5B", MCKEY_NOACTION
},
394 {KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"O5C", MCKEY_NOACTION
},
395 {KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"O5D", MCKEY_NOACTION
},
396 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_UP
, ESC_STR
"O6A", MCKEY_NOACTION
},
397 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"O6B", MCKEY_NOACTION
},
398 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"O6C", MCKEY_NOACTION
},
399 {KEY_M_SHIFT
| KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"O6D", MCKEY_NOACTION
},
402 {KEY_M_SHIFT
| KEY_PPAGE
, ESC_STR
"[5;2~", MCKEY_NOACTION
},
403 {KEY_M_SHIFT
| KEY_NPAGE
, ESC_STR
"[6;2~", MCKEY_NOACTION
},
406 {KEY_M_SHIFT
| KEY_PPAGE
, ESC_STR
"[[5;53~", MCKEY_NOACTION
},
407 {KEY_M_SHIFT
| KEY_NPAGE
, ESC_STR
"[[6;53~", MCKEY_NOACTION
},
410 {KEY_IC
, ESC_STR
"Op", MCKEY_NOACTION
},
411 {KEY_DC
, ESC_STR
"On", MCKEY_NOACTION
},
412 {'/', ESC_STR
"Oo", MCKEY_NOACTION
},
413 {'\n', ESC_STR
"OM", MCKEY_NOACTION
},
415 {0, NULL
, MCKEY_NOACTION
},
418 /* qansi-m terminals have a much more key combinatios,
419 which are undefined in termcap/terminfo */
420 static key_define_t qansi_key_defines
[] = {
421 /* qansi-m terminal */
422 {KEY_M_CTRL
| KEY_NPAGE
, ESC_STR
"[u", MCKEY_NOACTION
}, /* Ctrl-PgDown */
423 {KEY_M_CTRL
| KEY_PPAGE
, ESC_STR
"[v", MCKEY_NOACTION
}, /* Ctrl-PgUp */
424 {KEY_M_CTRL
| KEY_HOME
, ESC_STR
"[h", MCKEY_NOACTION
}, /* Ctrl-Home */
425 {KEY_M_CTRL
| KEY_END
, ESC_STR
"[y", MCKEY_NOACTION
}, /* Ctrl-End */
426 {KEY_M_CTRL
| KEY_IC
, ESC_STR
"[`", MCKEY_NOACTION
}, /* Ctrl-Insert */
427 {KEY_M_CTRL
| KEY_DC
, ESC_STR
"[p", MCKEY_NOACTION
}, /* Ctrl-Delete */
428 {KEY_M_CTRL
| KEY_LEFT
, ESC_STR
"[d", MCKEY_NOACTION
}, /* Ctrl-Left */
429 {KEY_M_CTRL
| KEY_RIGHT
, ESC_STR
"[c", MCKEY_NOACTION
}, /* Ctrl-Right */
430 {KEY_M_CTRL
| KEY_DOWN
, ESC_STR
"[b", MCKEY_NOACTION
}, /* Ctrl-Down */
431 {KEY_M_CTRL
| KEY_UP
, ESC_STR
"[a", MCKEY_NOACTION
}, /* Ctrl-Up */
432 {KEY_M_CTRL
| KEY_KP_ADD
, ESC_STR
"[s", MCKEY_NOACTION
}, /* Ctrl-Gr-Plus */
433 {KEY_M_CTRL
| KEY_KP_SUBTRACT
, ESC_STR
"[t", MCKEY_NOACTION
}, /* Ctrl-Gr-Minus */
434 {KEY_M_CTRL
| '\t', ESC_STR
"[z", MCKEY_NOACTION
}, /* Ctrl-Tab */
435 {KEY_M_SHIFT
| '\t', ESC_STR
"[Z", MCKEY_NOACTION
}, /* Shift-Tab */
436 {KEY_M_CTRL
| KEY_F (1), ESC_STR
"[1~", MCKEY_NOACTION
}, /* Ctrl-F1 */
437 {KEY_M_CTRL
| KEY_F (2), ESC_STR
"[2~", MCKEY_NOACTION
}, /* Ctrl-F2 */
438 {KEY_M_CTRL
| KEY_F (3), ESC_STR
"[3~", MCKEY_NOACTION
}, /* Ctrl-F3 */
439 {KEY_M_CTRL
| KEY_F (4), ESC_STR
"[4~", MCKEY_NOACTION
}, /* Ctrl-F4 */
440 {KEY_M_CTRL
| KEY_F (5), ESC_STR
"[5~", MCKEY_NOACTION
}, /* Ctrl-F5 */
441 {KEY_M_CTRL
| KEY_F (6), ESC_STR
"[6~", MCKEY_NOACTION
}, /* Ctrl-F6 */
442 {KEY_M_CTRL
| KEY_F (7), ESC_STR
"[7~", MCKEY_NOACTION
}, /* Ctrl-F7 */
443 {KEY_M_CTRL
| KEY_F (8), ESC_STR
"[8~", MCKEY_NOACTION
}, /* Ctrl-F8 */
444 {KEY_M_CTRL
| KEY_F (9), ESC_STR
"[9~", MCKEY_NOACTION
}, /* Ctrl-F9 */
445 {KEY_M_CTRL
| KEY_F (10), ESC_STR
"[10~", MCKEY_NOACTION
}, /* Ctrl-F10 */
446 {KEY_M_CTRL
| KEY_F (11), ESC_STR
"[11~", MCKEY_NOACTION
}, /* Ctrl-F11 */
447 {KEY_M_CTRL
| KEY_F (12), ESC_STR
"[12~", MCKEY_NOACTION
}, /* Ctrl-F12 */
448 {KEY_M_ALT
| KEY_F (1), ESC_STR
"[17~", MCKEY_NOACTION
}, /* Alt-F1 */
449 {KEY_M_ALT
| KEY_F (2), ESC_STR
"[18~", MCKEY_NOACTION
}, /* Alt-F2 */
450 {KEY_M_ALT
| KEY_F (3), ESC_STR
"[19~", MCKEY_NOACTION
}, /* Alt-F3 */
451 {KEY_M_ALT
| KEY_F (4), ESC_STR
"[20~", MCKEY_NOACTION
}, /* Alt-F4 */
452 {KEY_M_ALT
| KEY_F (5), ESC_STR
"[21~", MCKEY_NOACTION
}, /* Alt-F5 */
453 {KEY_M_ALT
| KEY_F (6), ESC_STR
"[22~", MCKEY_NOACTION
}, /* Alt-F6 */
454 {KEY_M_ALT
| KEY_F (7), ESC_STR
"[23~", MCKEY_NOACTION
}, /* Alt-F7 */
455 {KEY_M_ALT
| KEY_F (8), ESC_STR
"[24~", MCKEY_NOACTION
}, /* Alt-F8 */
456 {KEY_M_ALT
| KEY_F (9), ESC_STR
"[25~", MCKEY_NOACTION
}, /* Alt-F9 */
457 {KEY_M_ALT
| KEY_F (10), ESC_STR
"[26~", MCKEY_NOACTION
}, /* Alt-F10 */
458 {KEY_M_ALT
| KEY_F (11), ESC_STR
"[27~", MCKEY_NOACTION
}, /* Alt-F11 */
459 {KEY_M_ALT
| KEY_F (12), ESC_STR
"[28~", MCKEY_NOACTION
}, /* Alt-F12 */
460 {KEY_M_ALT
| 'a', ESC_STR
"Na", MCKEY_NOACTION
}, /* Alt-a */
461 {KEY_M_ALT
| 'b', ESC_STR
"Nb", MCKEY_NOACTION
}, /* Alt-b */
462 {KEY_M_ALT
| 'c', ESC_STR
"Nc", MCKEY_NOACTION
}, /* Alt-c */
463 {KEY_M_ALT
| 'd', ESC_STR
"Nd", MCKEY_NOACTION
}, /* Alt-d */
464 {KEY_M_ALT
| 'e', ESC_STR
"Ne", MCKEY_NOACTION
}, /* Alt-e */
465 {KEY_M_ALT
| 'f', ESC_STR
"Nf", MCKEY_NOACTION
}, /* Alt-f */
466 {KEY_M_ALT
| 'g', ESC_STR
"Ng", MCKEY_NOACTION
}, /* Alt-g */
467 {KEY_M_ALT
| 'h', ESC_STR
"Nh", MCKEY_NOACTION
}, /* Alt-h */
468 {KEY_M_ALT
| 'i', ESC_STR
"Ni", MCKEY_NOACTION
}, /* Alt-i */
469 {KEY_M_ALT
| 'j', ESC_STR
"Nj", MCKEY_NOACTION
}, /* Alt-j */
470 {KEY_M_ALT
| 'k', ESC_STR
"Nk", MCKEY_NOACTION
}, /* Alt-k */
471 {KEY_M_ALT
| 'l', ESC_STR
"Nl", MCKEY_NOACTION
}, /* Alt-l */
472 {KEY_M_ALT
| 'm', ESC_STR
"Nm", MCKEY_NOACTION
}, /* Alt-m */
473 {KEY_M_ALT
| 'n', ESC_STR
"Nn", MCKEY_NOACTION
}, /* Alt-n */
474 {KEY_M_ALT
| 'o', ESC_STR
"No", MCKEY_NOACTION
}, /* Alt-o */
475 {KEY_M_ALT
| 'p', ESC_STR
"Np", MCKEY_NOACTION
}, /* Alt-p */
476 {KEY_M_ALT
| 'q', ESC_STR
"Nq", MCKEY_NOACTION
}, /* Alt-q */
477 {KEY_M_ALT
| 'r', ESC_STR
"Nr", MCKEY_NOACTION
}, /* Alt-r */
478 {KEY_M_ALT
| 's', ESC_STR
"Ns", MCKEY_NOACTION
}, /* Alt-s */
479 {KEY_M_ALT
| 't', ESC_STR
"Nt", MCKEY_NOACTION
}, /* Alt-t */
480 {KEY_M_ALT
| 'u', ESC_STR
"Nu", MCKEY_NOACTION
}, /* Alt-u */
481 {KEY_M_ALT
| 'v', ESC_STR
"Nv", MCKEY_NOACTION
}, /* Alt-v */
482 {KEY_M_ALT
| 'w', ESC_STR
"Nw", MCKEY_NOACTION
}, /* Alt-w */
483 {KEY_M_ALT
| 'x', ESC_STR
"Nx", MCKEY_NOACTION
}, /* Alt-x */
484 {KEY_M_ALT
| 'y', ESC_STR
"Ny", MCKEY_NOACTION
}, /* Alt-y */
485 {KEY_M_ALT
| 'z', ESC_STR
"Nz", MCKEY_NOACTION
}, /* Alt-z */
486 {KEY_KP_SUBTRACT
, ESC_STR
"[S", MCKEY_NOACTION
}, /* Gr-Minus */
487 {KEY_KP_ADD
, ESC_STR
"[T", MCKEY_NOACTION
}, /* Gr-Plus */
488 {0, NULL
, MCKEY_NOACTION
},
491 /* timeout for old_esc_mode in usec */
492 static int keyboard_key_timeout
= 1000000; /* settable via env */
494 /* This holds all the key definitions */
495 static key_def
*keys
= NULL
;
498 static int disabled_channels
= 0; /* Disable channels checking */
500 static SelectList
*select_list
= NULL
;
502 static int seq_buffer
[SEQ_BUFFER_LEN
];
503 static int *seq_append
= NULL
;
505 static int *pending_keys
= NULL
;
509 ph_ov_f ph_input_group
;
510 ph_pqc_f ph_query_cursor
;
513 #ifdef HAVE_TEXTMODE_X11_SUPPORT
514 static Display
*x11_display
;
515 static Window x11_window
;
516 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
518 /*** file scope functions **********************************************/
521 add_selects (fd_set
* select_set
)
525 if (disabled_channels
== 0) {
528 for (p
= select_list
; p
!= NULL
; p
= p
->next
) {
529 FD_SET (p
->fd
, select_set
);
539 check_selects (fd_set
* select_set
)
541 if (disabled_channels
== 0) {
548 for (p
= select_list
; p
; p
= p
->next
)
549 if (FD_ISSET (p
->fd
, select_set
)) {
550 FD_CLR (p
->fd
, select_set
);
551 (*p
->callback
) (p
->fd
, p
->info
);
559 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
561 try_channels (int set_timeout
)
563 struct timeval time_out
;
564 static fd_set select_set
;
565 struct timeval
*timeptr
;
570 FD_ZERO (&select_set
);
571 FD_SET (input_fd
, &select_set
); /* Add stdin */
572 maxfdp
= max (add_selects (&select_set
), input_fd
);
577 time_out
.tv_usec
= 100000;
581 v
= select (maxfdp
+ 1, &select_set
, NULL
, NULL
, timeptr
);
583 check_selects (&select_set
);
584 if (FD_ISSET (input_fd
, &select_set
))
591 create_sequence (const char *seq
, int code
, int action
)
593 key_def
*base
, *p
, *attach
;
595 for (base
= attach
= NULL
; *seq
; seq
++) {
596 p
= g_new (key_def
, 1);
604 p
->child
= p
->next
= NULL
;
608 p
->action
= MCKEY_NOACTION
;
615 define_sequences (const key_define_t
* kd
)
619 for (i
= 0; kd
[i
].code
!= 0; i
++)
620 define_sequence (kd
[i
].code
, kd
[i
].seq
, kd
[i
].action
);
626 #ifdef HAVE_TEXTMODE_X11_SUPPORT
627 if (getenv ("DISPLAY") != NULL
) {
628 x11_display
= mc_XOpenDisplay (0);
630 if (x11_display
!= NULL
)
631 x11_window
= DefaultRootWindow (x11_display
);
633 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
636 /* Workaround for System V Curses vt100 bug */
638 getch_with_delay (void)
642 /* This routine could be used on systems without mouse support,
643 so we need to do the select check :-( */
645 if (pending_keys
== NULL
)
648 /* Try to get a character */
649 c
= get_key_code (0);
652 /* Failed -> wait 0.1 secs and try again */
655 /* Success -> return the character */
660 xmouse_get_event (Gpm_Event
* ev
)
663 static struct timeval tv1
= { 0, 0 }; /* Force first click as single */
664 static struct timeval tv2
;
665 static int clicks
= 0;
666 static int last_btn
= 0;
668 /* Decode Xterm mouse information to a GPM style event */
670 /* Variable btn has following meaning: */
671 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
672 btn
= tty_lowlevel_getch () - 32;
674 /* There seems to be no way of knowing which button was released */
675 /* So we assume all the buttons were released */
679 if ((last_btn
& (GPM_B_UP
| GPM_B_DOWN
)) != 0) {
680 /* FIXME: DIRTY HACK */
681 /* don't generate GPM_UP after mouse wheel */
682 /* need for menu event handling */
687 ev
->type
= GPM_UP
| (GPM_SINGLE
<< clicks
);
694 /* Bogus event, maybe mouse wheel */
698 if (btn
>= 32 && btn
<= 34) {
705 if (tv1
.tv_sec
&& (DIF_TIME (tv1
, tv2
) < double_click_speed
)) {
713 ev
->buttons
= GPM_B_LEFT
;
716 ev
->buttons
= GPM_B_MIDDLE
;
719 ev
->buttons
= GPM_B_RIGHT
;
722 ev
->buttons
= GPM_B_UP
;
726 ev
->buttons
= GPM_B_DOWN
;
735 last_btn
= ev
->buttons
;
737 /* Coordinates are 33-based */
738 /* Transform them to 1-based */
739 ev
->x
= tty_lowlevel_getch () - 32;
740 ev
->y
= tty_lowlevel_getch () - 32;
744 * Get modifier state (shift, alt, ctrl) for the last key pressed.
745 * We are assuming that the state didn't change since the key press.
746 * This is only correct if get_modifier() is called very fast after
747 * the input was received, so that the user didn't release the
755 int mod_status
, shift_ext_status
;
756 static int in_photon
= 0;
757 static int ph_ig
= 0;
758 PhCursorInfo_t cursor_info
;
759 #endif /* __QNXNTO__ */
761 #ifdef HAVE_TEXTMODE_X11_SUPPORT
762 if (x11_window
!= 0) {
768 mc_XQueryPointer (x11_display
, x11_window
, &root
, &child
, &root_x
,
769 &root_y
, &win_x
, &win_y
, &mask
);
771 if (mask
& ShiftMask
)
772 result
|= KEY_M_SHIFT
;
773 if (mask
& ControlMask
)
774 result
|= KEY_M_CTRL
;
777 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
780 if (in_photon
== 0) {
781 /* First time here, let's load Photon library and attach
784 if (getenv ("PHOTON2_PATH") != NULL
) {
785 /* QNX 6.x has no support for RTLD_LAZY */
786 void *ph_handle
= dlopen ("/usr/lib/libph.so", RTLD_NOW
);
787 if (ph_handle
!= NULL
) {
788 ph_attach
= (ph_dv_f
) dlsym (ph_handle
, "PhAttach");
789 ph_input_group
= (ph_ov_f
) dlsym (ph_handle
, "PhInputGroup");
790 ph_query_cursor
= (ph_pqc_f
) dlsym (ph_handle
, "PhQueryCursor");
791 if ((ph_attach
!= NULL
) && (ph_input_group
!= NULL
)
792 && (ph_query_cursor
!= NULL
)) {
793 if ((*ph_attach
) (0, 0)) { /* Attached */
794 ph_ig
= (*ph_input_group
) (0);
801 /* We do not have Photon running. Assume we are in text
803 if (in_photon
== -1) {
804 if (devctl (fileno (stdin
), DCMD_CHR_LINESTATUS
, &mod_status
, sizeof (int), NULL
) == -1)
806 shift_ext_status
= mod_status
& 0xffffff00UL
;
808 if (mod_status
& _LINESTATUS_CON_ALT
)
810 if (mod_status
& _LINESTATUS_CON_CTRL
)
811 result
|= KEY_M_CTRL
;
812 if ((mod_status
& _LINESTATUS_CON_SHIFT
)
813 || (shift_ext_status
& 0x00000800UL
))
814 result
|= KEY_M_SHIFT
;
816 (*ph_query_cursor
) (ph_ig
, &cursor_info
);
817 if (cursor_info
.key_mods
& 0x04)
819 if (cursor_info
.key_mods
& 0x02)
820 result
|= KEY_M_CTRL
;
821 if (cursor_info
.key_mods
& 0x01)
822 result
|= KEY_M_SHIFT
;
824 #endif /* __QNXNTO__ */
826 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
828 unsigned char modifiers
= 6;
830 if (ioctl (0, TIOCLINUX
, &modifiers
) < 0)
833 /* Translate Linux modifiers into mc modifiers */
834 if (modifiers
& SHIFT_PRESSED
)
835 result
|= KEY_M_SHIFT
;
836 if (modifiers
& (ALTL_PRESSED
| ALTR_PRESSED
))
838 if (modifiers
& CONTROL_PRESSED
)
839 result
|= KEY_M_CTRL
;
841 #endif /* !__linux__ */
848 gboolean ret
= FALSE
;
850 if (seq_append
== NULL
)
851 seq_append
= seq_buffer
;
853 if (seq_append
!= &(seq_buffer
[SEQ_BUFFER_LEN
- 2])) {
862 /* Apply corrections for the keycode generated in get_key_code() */
864 correct_key_code (int code
)
866 unsigned int c
= code
& ~KEY_M_MASK
; /* code without modifier */
867 unsigned int mod
= code
& KEY_M_MASK
; /* modifier */
869 unsigned int qmod
; /* bunch of the QNX console
870 modifiers needs unchanged */
871 #endif /* __QNXNTO__ */
874 * Add key modifiers directly from X11 or OS.
875 * Ordinary characters only get modifiers from sequences.
877 if (c
< 32 || c
>= 256) {
878 mod
|= get_modifier ();
881 /* This is needed if the newline is reported as carriage return */
885 /* This is reported to be useful on AIX */
886 if (c
== KEY_SCANCEL
)
889 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
890 if ((c
== '\t') && (mod
& (KEY_M_SHIFT
| KEY_M_CTRL
))) {
895 /* F0 is the same as F10 for out purposes */
900 * We are not interested if Ctrl was pressed when entering control
901 * characters, so assume that it was. When checking for such keys,
902 * XCTRL macro should be used. In some cases, we are interested,
903 * e.g. to distinguish Ctrl-Enter from Enter.
905 if (c
< 32 && c
!= ESC_CHAR
&& c
!= '\t' && c
!= '\n') {
909 qmod
= get_modifier ();
911 if ((c
== 127) && (mod
== 0)) { /* Add Ctrl/Alt/Shift-BackSpace */
912 mod
|= get_modifier ();
916 if ((c
== '0') && (mod
== 0)) { /* Add Shift-Insert on key pad */
917 if ((qmod
& KEY_M_SHIFT
) == KEY_M_SHIFT
) {
923 if ((c
== '.') && (mod
== 0)) { /* Add Shift-Del on key pad */
924 if ((qmod
& KEY_M_SHIFT
) == KEY_M_SHIFT
) {
929 #endif /* __QNXNTO__ */
931 /* Unrecognized 0177 is delete (preserve Ctrl) */
936 /* Unrecognized Ctrl-d is delete */
937 if (c
== (31 & 'd')) {
942 /* Unrecognized Ctrl-h is backspace */
943 if (c
== (31 & 'h')) {
948 /* Shift+BackSpace is backspace */
949 if (c
== KEY_BACKSPACE
&& (mod
& KEY_M_SHIFT
)) {
953 /* Convert Shift+Fn to F(n+10) */
954 if (c
>= KEY_F (1) && c
<= KEY_F (10) && (mod
& KEY_M_SHIFT
)) {
958 /* Remove Shift information from function keys */
959 if (c
>= KEY_F (1) && c
<= KEY_F (20)) {
963 if (!alternate_plus_minus
)
968 case KEY_KP_SUBTRACT
:
971 case KEY_KP_MULTIPLY
:
984 struct timeval time_out
;
986 time_out
.tv_sec
= keyboard_key_timeout
/ 1000000;
987 time_out
.tv_usec
= keyboard_key_timeout
% 1000000;
989 FD_ZERO (&Read_FD_Set
);
990 FD_SET (input_fd
, &Read_FD_Set
);
991 select (input_fd
+ 1, &Read_FD_Set
, NULL
, NULL
, &time_out
);
992 c
= tty_lowlevel_getch ();
998 learn_store_key (char *buffer
, char **p
, int c
)
1000 if (*p
- buffer
> 253)
1002 if (c
== ESC_CHAR
) {
1005 } else if (c
< ' ') {
1007 *(*p
)++ = c
+ 'a' - 1;
1008 } else if (c
== '^') {
1016 k_dispose (key_def
* k
)
1019 k_dispose (k
->child
);
1020 k_dispose (k
->next
);
1026 s_dispose (SelectList
* sel
)
1029 s_dispose (sel
->next
);
1034 /*** public functions **************************************************/
1036 /* This has to be called before init_slang or whatever routine
1037 calls any define_sequence */
1041 const char *term
= getenv ("TERM");
1042 const char *kt
= getenv ("KEYBOARD_KEY_TIMEOUT_US");
1044 keyboard_key_timeout
= atoi (kt
);
1046 /* This has to be the first define_sequence */
1047 /* So, we can assume that the first keys member has ESC */
1048 define_sequences (mc_default_keys
);
1050 /* Terminfo on irix does not have some keys */
1053 && (strncmp (term
, "iris-ansi", 9) == 0
1054 || strncmp (term
, "xterm", 5) == 0
1055 || strncmp (term
, "rxvt", 4) == 0 || strcmp (term
, "screen") == 0)))
1056 define_sequences (xterm_key_defines
);
1058 /* load some additional keys (e.g. direct Alt-? support) */
1059 load_xtra_key_defines ();
1062 if ((term
!= NULL
) && (strncmp (term
, "qnx", 3) == 0)) {
1063 /* Modify the default value of use_8th_bit_as_meta: we would
1064 * like to provide a working mc for a newbie who knows nothing
1065 * about [Options|Display bits|Full 8 bits input]...
1067 * Don't use 'meta'-bit, when we are dealing with a
1068 * 'qnx*'-type terminal: clear the default value!
1069 * These terminal types use 0xFF as an escape character,
1070 * so use_8th_bit_as_meta==1 must not be enabled!
1072 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1073 * is not used now (doesn't even depend on use_8th_bit_as_meta
1074 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1076 use_8th_bit_as_meta
= 0;
1078 #endif /* __QNX__ */
1082 /* Load the qansi-m key definitions
1083 if we are running under the qansi-m terminal */
1084 if (term
!= NULL
&& (strncmp (term
, "qansi-m", 7) == 0))
1085 define_sequences (qansi_key_defines
);
1088 /* This has to be called after SLang_init_tty/slint_init */
1090 init_key_input_fd (void)
1093 input_fd
= SLang_TT_Read_FD
;
1101 s_dispose (select_list
);
1103 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1105 mc_XCloseDisplay (x11_display
);
1110 add_select_channel (int fd
, select_fn callback
, void *info
)
1114 new = g_new (SelectList
, 1);
1116 new->callback
= callback
;
1118 new->next
= select_list
;
1123 delete_select_channel (int fd
)
1125 SelectList
*p
= select_list
;
1126 SelectList
*p_prev
= NULL
;
1134 p_prev
->next
= p_next
;
1136 select_list
= p_next
;
1149 if (disabled_channels
== 0)
1150 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr
);
1151 disabled_channels
--;
1155 channels_down (void)
1157 disabled_channels
++;
1161 * Common handler for standard movement keys in a text area. Provided
1162 * functions are called with the "data" argument. backfn and forfn also
1163 * get an argument indicating how many lines to scroll. Return MSG_HANDLED
1164 * if the key was handled, MSG_NOT_HANDLED otherwise.
1167 check_movement_keys (int key
, int page_size
, void *data
, move_fn backfn
,
1168 move_fn forfn
, move_fn topfn
, move_fn bottomfn
)
1173 (*backfn
) (data
, 1);
1183 (*backfn
) (data
, page_size
- 1);
1188 (*forfn
) (data
, page_size
- 1);
1192 case KEY_M_CTRL
| KEY_HOME
:
1193 case KEY_M_CTRL
| KEY_PPAGE
:
1200 case KEY_M_CTRL
| KEY_END
:
1201 case KEY_M_CTRL
| KEY_NPAGE
:
1204 (*bottomfn
) (data
, 0);
1209 (*backfn
) (data
, page_size
- 1);
1213 (*forfn
) (data
, page_size
- 1);
1217 (*backfn
) (data
, page_size
/ 2);
1221 (*forfn
) (data
, page_size
/ 2);
1229 (*bottomfn
) (data
, 0);
1233 return MSG_NOT_HANDLED
;
1239 static const size_t key_name_conv_tab_size
= sizeof (key_name_conv_tab
) /
1240 sizeof (key_name_conv_tab
[0]) - 1;
1241 static const key_code_name_t
*key_name_conv_tab_sorted
[sizeof (key_name_conv_tab
) /
1242 sizeof (key_name_conv_tab
[0]) - 1];
1245 key_code_name_comparator (const void *p1
, const void *p2
)
1247 const key_code_name_t
*n1
= *(const key_code_name_t
**) p1
;
1248 const key_code_name_t
*n2
= *(const key_code_name_t
**) p2
;
1250 return str_casecmp (n1
->name
, n2
->name
);
1254 sort_key_name_conv_tab (void)
1256 static gboolean has_been_sorted
= FALSE
;
1258 if (!has_been_sorted
) {
1260 for (i
= 0; i
< key_name_conv_tab_size
; i
++)
1261 key_name_conv_tab_sorted
[i
] = &key_name_conv_tab
[i
];
1263 qsort (key_name_conv_tab_sorted
,
1264 key_name_conv_tab_size
, sizeof (key_name_conv_tab_sorted
[0]),
1265 &key_code_name_comparator
);
1266 has_been_sorted
= TRUE
;
1271 lookup_keyname (const char *name
, int *idx
)
1273 if (name
[0] != '\0') {
1274 const key_code_name_t key
= { 0, name
, NULL
, NULL
};
1275 const key_code_name_t
*keyp
= &key
;
1276 key_code_name_t
**res
;
1278 if (name
[1] == '\0') {
1280 return (int) name
[0];
1283 sort_key_name_conv_tab ();
1285 res
= bsearch (&keyp
, key_name_conv_tab_sorted
,
1286 key_name_conv_tab_size
,
1287 sizeof (key_name_conv_tab_sorted
[0]),
1288 key_code_name_comparator
);
1291 *idx
= (int) (res
- (key_code_name_t
**) key_name_conv_tab_sorted
);
1292 return (*res
)->code
;
1300 /* Return the code associated with the symbolic name keyname */
1302 lookup_key (const char *name
, char **label
)
1304 char **lc_keys
, **p
;
1316 name
= g_strstrip (g_strdup (name
));
1317 p
= lc_keys
= g_strsplit_set (name
, "-+ ", -1);
1318 g_free ((char *) name
);
1320 while ((p
!= NULL
) && (*p
!= NULL
)) {
1321 if ((*p
)[0] != '\0') {
1324 key
= lookup_keyname (g_strstrip (*p
), &idx
);
1326 if (key
== KEY_M_ALT
)
1328 else if (key
== KEY_M_CTRL
)
1330 else if (key
== KEY_M_SHIFT
)
1342 g_strfreev (lc_keys
);
1349 if (label
!= NULL
) {
1352 s
= g_string_new ("");
1354 if (use_meta
!= -1) {
1355 g_string_append (s
, key_name_conv_tab_sorted
[use_meta
]->shortcut
);
1356 g_string_append_c (s
, '-');
1358 if (use_ctrl
!= -1) {
1359 g_string_append (s
, key_name_conv_tab_sorted
[use_ctrl
]->shortcut
);
1360 g_string_append_c (s
, '-');
1362 if (use_shift
!= -1) {
1364 g_string_append_c (s
, (gchar
) g_ascii_toupper ((gchar
) k
));
1366 g_string_append (s
, key_name_conv_tab_sorted
[use_shift
]->shortcut
);
1367 g_string_append_c (s
, '-');
1368 g_string_append (s
, key_name_conv_tab_sorted
[lc_index
]->shortcut
);
1370 } else if (k
< 128) {
1371 if ((k
>= 'A') || (lc_index
< 0)
1372 || (key_name_conv_tab_sorted
[lc_index
]->shortcut
== NULL
))
1373 g_string_append_c (s
, (gchar
) g_ascii_tolower ((gchar
) k
));
1375 g_string_append (s
, key_name_conv_tab_sorted
[lc_index
]->shortcut
);
1376 } else if ((lc_index
!= -1) && (key_name_conv_tab_sorted
[lc_index
]->shortcut
!= NULL
))
1377 g_string_append (s
, key_name_conv_tab_sorted
[lc_index
]->shortcut
);
1379 g_string_append_c (s
, (gchar
) g_ascii_tolower ((gchar
) key
));
1381 *label
= g_string_free (s
, FALSE
);
1384 if (use_shift
!= -1) {
1385 if (k
< 127 && k
> 31 )
1386 k
= g_ascii_toupper ((gchar
) k
);
1391 if (use_ctrl
!= -1) {
1405 * Return TRUE on success, FALSE on error.
1406 * An error happens if SEQ is a beginning of an existing longer sequence.
1409 define_sequence (int code
, const char *seq
, int action
)
1413 if (strlen (seq
) > SEQ_BUFFER_LEN
- 1)
1416 for (base
= keys
; (base
!= NULL
) && (*seq
!= '\0');)
1417 if (*seq
== base
->ch
) {
1418 if (base
->child
== 0) {
1419 if (*(seq
+ 1) != '\0')
1420 base
->child
= create_sequence (seq
+ 1, code
, action
);
1422 /* The sequence matches an existing one. */
1424 base
->action
= action
;
1435 base
->next
= create_sequence (seq
, code
, action
);
1441 /* Attempt to redefine a sequence with a shorter sequence. */
1445 keys
= create_sequence (seq
, code
, action
);
1450 * Check if we are idle, i.e. there are no pending keyboard or mouse
1451 * events. Return 1 is idle, 0 is there are pending events.
1458 struct timeval time_out
;
1460 FD_ZERO (&select_set
);
1461 FD_SET (input_fd
, &select_set
);
1464 if (mouse_enabled
&& (use_mouse_p
== MOUSE_GPM
) && (gpm_fd
> 0)) {
1465 FD_SET (gpm_fd
, &select_set
);
1466 maxfdp
= max (maxfdp
, gpm_fd
);
1469 time_out
.tv_sec
= 0;
1470 time_out
.tv_usec
= 0;
1471 return (select (maxfdp
+ 1, &select_set
, 0, 0, &time_out
) <= 0);
1475 get_key_code (int no_delay
)
1478 static key_def
*this = NULL
, *parent
;
1479 static struct timeval esctime
= { -1, -1 };
1480 static int lastnodelay
= -1;
1482 if (no_delay
!= lastnodelay
) {
1484 lastnodelay
= no_delay
;
1488 if (pending_keys
!= NULL
) {
1489 int d
= *pending_keys
++;
1491 if (*pending_keys
== 0) {
1492 pending_keys
= NULL
;
1495 if ((d
== ESC_CHAR
) && (pending_keys
!= NULL
)) {
1496 d
= ALT (*pending_keys
++);
1499 if ((d
> 127 && d
< 256) && use_8th_bit_as_meta
)
1502 return correct_key_code (d
);
1509 c
= tty_lowlevel_getch ();
1510 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1511 if (c
== KEY_RESIZE
)
1512 goto nodelay_try_again
;
1515 tty_nodelay (FALSE
);
1517 if (this != NULL
&& parent
!= NULL
&& parent
->action
== MCKEY_ESCAPE
&& old_esc_mode
) {
1518 struct timeval current
, time_out
;
1520 if (esctime
.tv_sec
== -1)
1523 time_out
.tv_sec
= keyboard_key_timeout
/ 1000000 + esctime
.tv_sec
;
1524 time_out
.tv_usec
= keyboard_key_timeout
% 1000000 + esctime
.tv_usec
;
1525 if (time_out
.tv_usec
> 1000000) {
1526 time_out
.tv_usec
-= 1000000;
1529 if (current
.tv_sec
< time_out
.tv_sec
)
1531 if (current
.tv_sec
== time_out
.tv_sec
&& current
.tv_usec
< time_out
.tv_usec
)
1534 pending_keys
= seq_append
= NULL
;
1539 } else if (c
== -1) {
1540 /* Maybe we got an incomplete match.
1541 This we do only in delay mode, since otherwise
1542 tty_lowlevel_getch can return -1 at any time. */
1543 if (seq_append
!= NULL
) {
1544 pending_keys
= seq_buffer
;
1551 /* Search the key on the root */
1552 if (!no_delay
|| this == NULL
) {
1556 if ((c
> 127 && c
< 256) && use_8th_bit_as_meta
) {
1559 /* The first sequence defined starts with esc */
1564 while (this != NULL
) {
1565 if (c
== this->ch
) {
1567 if (!push_char (c
)) {
1568 pending_keys
= seq_buffer
;
1573 if (parent
->action
== MCKEY_ESCAPE
&& old_esc_mode
) {
1577 /* Shouldn't happen */
1578 fputs ("Internal error\n", stderr
);
1581 goto nodelay_try_again
;
1583 esctime
.tv_sec
= -1;
1584 c
= xgetch_second ();
1586 pending_keys
= seq_append
= NULL
;
1592 goto nodelay_try_again
;
1593 c
= tty_lowlevel_getch ();
1596 /* We got a complete match, return and reset search */
1599 pending_keys
= seq_append
= NULL
;
1602 return correct_key_code (code
);
1605 if (this->next
!= NULL
)
1608 if ((parent
!= NULL
) && (parent
->action
== MCKEY_ESCAPE
)) {
1609 /* Convert escape-digits to F-keys */
1610 if (g_ascii_isdigit (c
))
1611 c
= KEY_F (c
- '0');
1617 pending_keys
= seq_append
= NULL
;
1619 return correct_key_code (c
);
1621 /* Did not find a match or {c} was changed in the if above,
1622 so we have to return everything we had skipped
1625 pending_keys
= seq_buffer
;
1631 return correct_key_code (c
);
1634 /* Returns a character read from stdin with appropriate interpretation */
1635 /* Also takes care of generated mouse events */
1636 /* Returns EV_MOUSE if it is a mouse event */
1637 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1639 tty_get_event (struct Gpm_Event
*event
, gboolean redo_event
, gboolean block
)
1642 static int flag
= 0; /* Return value from select */
1644 static struct Gpm_Event ev
; /* Mouse event */
1646 struct timeval time_out
;
1647 struct timeval
*time_addr
= NULL
;
1648 static int dirty
= 3;
1650 if ((dirty
== 3) || is_idle ()) {
1656 vfs_timeout_handler ();
1658 /* Ok, we use (event->x < 0) to signal that the event does not contain
1659 a suitable position for the mouse, so we can't use show_mouse_pointer
1663 show_mouse_pointer (event
->x
, event
->y
);
1668 /* Repeat if using mouse */
1669 while (pending_keys
== NULL
) {
1673 FD_ZERO (&select_set
);
1674 FD_SET (input_fd
, &select_set
);
1675 maxfdp
= max (add_selects (&select_set
), input_fd
);
1678 if (mouse_enabled
&& (use_mouse_p
== MOUSE_GPM
)) {
1680 /* Connection to gpm broken, possibly gpm has died */
1681 mouse_enabled
= FALSE
;
1682 use_mouse_p
= MOUSE_NONE
;
1686 FD_SET (gpm_fd
, &select_set
);
1687 maxfdp
= max (maxfdp
, gpm_fd
);
1692 time_out
.tv_usec
= mou_auto_repeat
* 1000;
1693 time_out
.tv_sec
= 0;
1695 time_addr
= &time_out
;
1699 seconds
= vfs_timeouts ();
1703 /* the timeout could be improved and actually be
1704 * the number of seconds until the next vfs entry
1705 * timeouts in the stamp list.
1708 time_out
.tv_sec
= seconds
;
1709 time_out
.tv_usec
= 0;
1710 time_addr
= &time_out
;
1714 if (!block
|| winch_flag
) {
1715 time_addr
= &time_out
;
1716 time_out
.tv_sec
= 0;
1717 time_out
.tv_usec
= 0;
1720 tty_enable_interrupt_key ();
1721 flag
= select (maxfdp
+ 1, &select_set
, NULL
, NULL
, time_addr
);
1722 tty_disable_interrupt_key ();
1724 /* select timed out: it could be for any of the following reasons:
1725 * redo_event -> it was because of the MOU_REPEAT handler
1726 * !block -> we did not block in the select call
1727 * else -> 10 second timeout to check the vfs status.
1732 if (!block
|| winch_flag
)
1734 vfs_timeout_handler ();
1736 if (flag
== -1 && errno
== EINTR
)
1739 check_selects (&select_set
);
1741 if (FD_ISSET (input_fd
, &select_set
))
1744 if (mouse_enabled
&& use_mouse_p
== MOUSE_GPM
1745 && gpm_fd
> 0 && FD_ISSET (gpm_fd
, &select_set
)) {
1751 #endif /* !HAVE_LIBGPM */
1755 flag
= is_wintouched (stdscr
);
1756 untouchwin (stdscr
);
1757 #endif /* !HAVE_SLANG */
1758 c
= block
? getch_with_delay () : get_key_code (1);
1762 tty_touch_screen ();
1763 #endif /* !HAVE_SLANG */
1765 if (mouse_enabled
&& (c
== MCKEY_MOUSE
1768 #endif /* KEY_MOUSE */
1771 xmouse_get_event (event
);
1772 return (event
->type
!= 0) ? EV_MOUSE
: EV_NONE
;
1778 /* Returns a key press, mouse events are discarded */
1786 while ((key
= tty_get_event (&ev
, FALSE
, TRUE
)) == EV_NONE
);
1793 /* LEARN_TIMEOUT in usec */
1794 #define LEARN_TIMEOUT 200000
1797 struct timeval endtime
;
1798 struct timeval time_out
;
1803 tty_keypad (FALSE
); /* disable intepreting keys by ncurses */
1804 c
= tty_lowlevel_getch ();
1806 c
= tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
1807 learn_store_key (buffer
, &p
, c
);
1809 endtime
.tv_usec
+= LEARN_TIMEOUT
;
1810 if (endtime
.tv_usec
> 1000000) {
1811 endtime
.tv_usec
-= 1000000;
1816 while ((c
= tty_lowlevel_getch ()) == -1) {
1817 GET_TIME (time_out
);
1818 time_out
.tv_usec
= endtime
.tv_usec
- time_out
.tv_usec
;
1819 if (time_out
.tv_usec
< 0)
1821 time_out
.tv_sec
= endtime
.tv_sec
- time_out
.tv_sec
;
1822 if (time_out
.tv_sec
>= 0 && time_out
.tv_usec
> 0) {
1823 FD_ZERO (&Read_FD_Set
);
1824 FD_SET (input_fd
, &Read_FD_Set
);
1825 select (input_fd
+ 1, &Read_FD_Set
, NULL
, NULL
, &time_out
);
1831 learn_store_key (buffer
, &p
, c
);
1834 tty_nodelay (FALSE
);
1836 return g_strdup (buffer
);
1837 #undef LEARN_TIMEOUT
1840 /* xterm and linux console only: set keypad to numeric or application
1841 mode. Only in application keypad mode it's possible to distinguish
1842 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1844 numeric_keypad_mode (void)
1846 if (console_flag
|| xterm_flag
) {
1847 fputs ("\033>", stdout
);
1853 application_keypad_mode (void)
1855 if (console_flag
|| xterm_flag
) {
1856 fputs ("\033=", stdout
);