De-inline a few functions which are large
[midnight-commander.git] / src / tty / key.c
blobc7e52815b4091e89edf5c65347b5d399592ab545
1 /* Keyboard support routines.
3 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Written by: 1994, 1995 Miguel de Icaza.
7 1994, 1995 Janne Kukonlehto.
8 1995 Jakub Jelinek.
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
25 /** \file key.c
26 * \brief Source: keyboard support routines
29 #include <config.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <unistd.h>
40 #include "../../src/global.h"
42 #include "../../src/tty/tty.h"
43 #include "../../src/tty/tty-internal.h" /* mouse_enabled */
44 #include "../../src/tty/mouse.h"
45 #include "../../src/tty/key.h"
46 #include "../../src/tty/win.h" /* xterm_flag */
48 #include "../../src/main.h"
49 #include "../../src/layout.h" /* winch_flag, mc_refresh() */
50 #include "../../src/cons.saver.h"
51 #include "../../src/strutil.h" /* str_casecmp */
53 #ifdef USE_VFS
54 #include "../../vfs/gc.h"
55 #endif
57 #ifdef HAVE_TEXTMODE_X11_SUPPORT
58 #include "../src/tty/x11conn.h"
59 #endif
61 #ifdef __linux__
62 #if defined(__GLIBC__) && (__GLIBC__ < 2)
63 # include <linux/termios.h> /* TIOCLINUX */
64 #elif defined HAVE_TERMIOS_H
65 # include <termios.h>
66 #endif
67 #include <sys/ioctl.h>
68 #endif /* __linux__ */
70 #ifdef __CYGWIN__
71 #include <termios.h>
72 #include <sys/ioctl.h>
73 #endif /* __CYGWIN__ */
75 #ifdef __QNXNTO__
76 #include <dlfcn.h>
77 #include <Ph.h>
78 #include <sys/dcmd_chr.h>
79 #endif /* __QNXNTO__ */
81 /*** global variables **************************************************/
83 /* If true, use + and \ keys normally and select/unselect do if M-+ / M-\.
84 and M-- and keypad + / - */
85 int alternate_plus_minus = 0;
87 int mou_auto_repeat = 100;
88 int double_click_speed = 250;
89 int old_esc_mode = 0;
90 int use_8th_bit_as_meta = 0;
92 /* This table is a mapping between names and the constants we use
93 * We use this to allow users to define alternate definitions for
94 * certain keys that may be missing from the terminal database
96 key_code_name_t key_name_conv_tab [] = {
97 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
98 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
99 { KEY_F(1), "f1", N_("Function key 1") },
100 { KEY_F(2), "f2", N_("Function key 2") },
101 { KEY_F(3), "f3", N_("Function key 3") },
102 { KEY_F(4), "f4", N_("Function key 4") },
103 { KEY_F(5), "f5", N_("Function key 5") },
104 { KEY_F(6), "f6", N_("Function key 6") },
105 { KEY_F(7), "f7", N_("Function key 7") },
106 { KEY_F(8), "f8", N_("Function key 8") },
107 { KEY_F(9), "f9", N_("Function key 9") },
108 { KEY_F(10), "f10", N_("Function key 10") },
109 { KEY_F(11), "f11", N_("Function key 11") },
110 { KEY_F(12), "f12", N_("Function key 12") },
111 { KEY_F(13), "f13", N_("Function key 13") },
112 { KEY_F(14), "f14", N_("Function key 14") },
113 { KEY_F(15), "f15", N_("Function key 15") },
114 { KEY_F(16), "f16", N_("Function key 16") },
115 { KEY_F(17), "f17", N_("Function key 17") },
116 { KEY_F(18), "f18", N_("Function key 18") },
117 { KEY_F(19), "f19", N_("Function key 19") },
118 { KEY_F(20), "f20", N_("Function key 20") },
119 { KEY_BACKSPACE, "bs", N_("Backspace key") },
120 { KEY_END, "end", N_("End key") },
121 { KEY_UP, "up", N_("Up arrow key") },
122 { KEY_DOWN, "down", N_("Down arrow key") },
123 { KEY_LEFT, "left", N_("Left arrow key") },
124 { KEY_RIGHT, "right", N_("Right arrow key") },
125 { KEY_HOME, "home", N_("Home key") },
126 { KEY_NPAGE, "pgdn", N_("Page Down key") },
127 { KEY_PPAGE, "pgup", N_("Page Up key") },
128 { KEY_IC, "ins", N_("Insert key") },
129 { KEY_DC, "delete", N_("Delete key") },
130 { ALT('\t'), "complete", N_("Completion/M-tab") },
131 { KEY_KP_ADD, "kpplus", N_("+ on keypad") },
132 { KEY_KP_SUBTRACT,"kpminus", N_("- on keypad") },
133 { KEY_KP_MULTIPLY,"kpasterix", N_("* on keypad") },
135 /* From here on, these won't be shown in Learn keys (no space) */
136 { KEY_LEFT, "kpleft", N_("Left arrow keypad") },
137 { KEY_RIGHT, "kpright", N_("Right arrow keypad") },
138 { KEY_UP, "kpup", N_("Up arrow keypad") },
139 { KEY_DOWN, "kpdown", N_("Down arrow keypad") },
140 { KEY_HOME, "kphome", N_("Home on keypad") },
141 { KEY_END, "kpend", N_("End on keypad") },
142 { KEY_NPAGE, "kpnpage", N_("Page Down keypad") },
143 { KEY_PPAGE, "kpppage", N_("Page Up keypad") },
144 { KEY_IC, "kpinsert", N_("Insert on keypad") },
145 { KEY_DC, "kpdelete", N_("Delete on keypad") },
146 { (int) '\n', "kpenter", N_("Enter on keypad") },
147 { (int) '\n', "enter", N_("Enter on keypad") },
148 { (int) '\t', "tab", N_("Tab on keypad") },
149 { (int) ' ', "space", N_("Space on keypad") },
150 { (int) '/', "kpslash", N_("Slash on keypad") },
151 { (int) '#', "kpnumlock", N_("NumLock on keypad") },
153 /* Alternative label */
154 { KEY_BACKSPACE, "backspace", N_("Backspace key") },
155 { KEY_IC, "insert", N_("Insert key") },
156 { KEY_KP_ADD, "plus", N_("+ on keypad") },
157 { KEY_KP_SUBTRACT,"minus", N_("- on keypad") },
158 { KEY_KP_MULTIPLY,"asterix", N_("* on keypad") },
160 /* meta keys */
161 { KEY_M_CTRL, "control", N_("Ctrl") },
162 { KEY_M_CTRL, "ctrl", N_("Ctrl") },
163 { KEY_M_ALT, "alt", N_("Alt") },
164 { KEY_M_ALT, "ralt", N_("Alt") },
165 { KEY_M_ALT, "meta", N_("Alt") },
166 { KEY_M_SHIFT, "shift", N_("Shift") },
168 { 0, 0, 0 }
172 /*** file scope macro definitions **************************************/
174 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *) NULL))
175 #define DIF_TIME(t1, t2) ((t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/1000)
177 /* The maximum sequence length (32 + null terminator) */
178 #define SEQ_BUFFER_LEN 33
180 /*** file scope type declarations **************************************/
182 /* Linux console keyboard modifiers */
183 typedef enum {
184 SHIFT_PRESSED = (1 << 0),
185 ALTR_PRESSED = (1 << 1),
186 CONTROL_PRESSED = (1 << 2),
187 ALTL_PRESSED = (1 << 3)
188 } mod_pressed_t;
190 typedef struct key_def {
191 char ch; /* Holds the matching char code */
192 int code; /* The code returned, valid if child == NULL */
193 struct key_def *next;
194 struct key_def *child; /* sequence continuation */
195 int action; /* optional action to be done. Now used only
196 to mark that we are just after the first
197 Escape */
198 } key_def;
200 typedef struct {
201 int code;
202 const char *seq;
203 int action;
204 } key_define_t;
206 /* File descriptor monitoring add/remove routines */
207 typedef struct SelectList {
208 int fd;
209 select_fn callback;
210 void *info;
211 struct SelectList *next;
212 } SelectList;
214 #ifdef __QNXNTO__
215 typedef int (*ph_dv_f) (void *, void *);
216 typedef int (*ph_ov_f) (void *);
217 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
218 #endif
220 /*** file scope variables **********************************************/
222 static key_define_t mc_default_keys [] = {
223 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
224 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
225 { 0, NULL, MCKEY_NOACTION },
228 /* Broken terminfo and termcap databases on xterminals */
229 static key_define_t xterm_key_defines [] = {
230 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
231 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
232 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
233 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
234 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
235 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
236 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
237 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
238 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
239 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
240 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
241 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
242 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
243 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
245 /* old xterm Shift-arrows */
246 { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
247 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
248 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
249 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
251 /* new xterm Shift-arrows */
252 { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
253 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
254 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
255 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
257 /* more xterm keys with modifiers */
258 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
259 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
260 { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
261 { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
262 { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
263 { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
264 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
265 { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
266 { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
267 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
268 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
269 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
270 { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
271 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
272 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
273 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
274 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
275 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
277 /* putty */
278 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION },
279 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION },
280 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION },
281 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION },
283 /* putty alt-arrow keys */
284 /* removed as source esc esc esc trouble */
286 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
287 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
288 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
289 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
290 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
291 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
292 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
293 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
295 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
296 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
297 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
298 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
300 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
301 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
302 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
303 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
305 /* xterm alt-arrow keys */
306 { KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION },
307 { KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION },
308 { KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION },
309 { KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION },
310 { KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION },
311 { KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION },
312 { KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION },
313 { KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION },
314 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION },
315 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION },
316 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION },
317 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION },
318 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION },
319 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION },
320 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION },
321 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION },
323 /* rxvt keys with modifiers */
324 { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
325 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
326 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
327 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
328 { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
329 { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
330 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
331 { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
332 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
333 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
334 { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
335 { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
336 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
337 { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
338 { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
339 { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
340 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
342 /* konsole keys with modifiers */
343 { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
344 { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
346 /* gnome-terminal */
347 { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
348 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
349 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
350 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
351 { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
352 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
353 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
354 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
355 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
356 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
357 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
358 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
360 /* gnome-terminal - application mode */
361 { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
362 { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
363 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
364 { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
365 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
366 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
367 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
368 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
370 /* iTerm */
371 { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION },
372 { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION },
374 /* putty */
375 { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION },
376 { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION },
378 /* keypad keys */
379 { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
380 { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
381 { '/', ESC_STR "Oo", MCKEY_NOACTION },
382 { '\n', ESC_STR "OM", MCKEY_NOACTION },
384 { 0, NULL, MCKEY_NOACTION },
387 /* qansi-m terminals have a much more key combinatios,
388 which are undefined in termcap/terminfo */
389 static key_define_t qansi_key_defines[] =
391 /* qansi-m terminal */
392 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
393 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
394 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
395 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
396 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
397 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
398 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
399 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
400 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
401 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
402 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
403 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
404 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
405 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
406 {KEY_M_CTRL | KEY_F(1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
407 {KEY_M_CTRL | KEY_F(2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
408 {KEY_M_CTRL | KEY_F(3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
409 {KEY_M_CTRL | KEY_F(4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
410 {KEY_M_CTRL | KEY_F(5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
411 {KEY_M_CTRL | KEY_F(6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
412 {KEY_M_CTRL | KEY_F(7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
413 {KEY_M_CTRL | KEY_F(8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
414 {KEY_M_CTRL | KEY_F(9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
415 {KEY_M_CTRL | KEY_F(10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
416 {KEY_M_CTRL | KEY_F(11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
417 {KEY_M_CTRL | KEY_F(12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
418 {KEY_M_ALT | KEY_F(1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
419 {KEY_M_ALT | KEY_F(2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
420 {KEY_M_ALT | KEY_F(3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
421 {KEY_M_ALT | KEY_F(4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
422 {KEY_M_ALT | KEY_F(5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
423 {KEY_M_ALT | KEY_F(6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
424 {KEY_M_ALT | KEY_F(7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
425 {KEY_M_ALT | KEY_F(8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
426 {KEY_M_ALT | KEY_F(9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
427 {KEY_M_ALT | KEY_F(10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
428 {KEY_M_ALT | KEY_F(11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
429 {KEY_M_ALT | KEY_F(12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
430 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
431 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
432 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
433 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
434 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
435 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
436 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
437 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
438 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
439 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
440 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
441 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
442 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
443 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
444 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
445 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-r */
446 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
447 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
448 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
449 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
450 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
451 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
452 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
453 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
454 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
455 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
456 {0, NULL, MCKEY_NOACTION},
459 /* timeout for old_esc_mode in usec */
460 static int keyboard_key_timeout = 1000000; /* settable via env */
462 /* This holds all the key definitions */
463 static key_def *keys = NULL;
465 static int input_fd;
466 static int disabled_channels = 0; /* Disable channels checking */
468 static SelectList *select_list = NULL;
470 static int seq_buffer [SEQ_BUFFER_LEN];
471 static int *seq_append = NULL;
473 static int *pending_keys = NULL;
475 #ifdef __QNXNTO__
476 ph_dv_f ph_attach;
477 ph_ov_f ph_input_group;
478 ph_pqc_f ph_query_cursor;
479 #endif
481 #ifdef HAVE_TEXTMODE_X11_SUPPORT
482 static Display *x11_display;
483 static Window x11_window;
484 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
486 /*** file scope functions **********************************************/
488 static int
489 add_selects (fd_set *select_set)
491 int top_fd = 0;
493 if (disabled_channels == 0) {
494 SelectList *p;
496 for (p = select_list; p != NULL; p = p->next) {
497 FD_SET (p->fd, select_set);
498 if (p->fd > top_fd)
499 top_fd = p->fd;
503 return top_fd;
506 static void
507 check_selects (fd_set *select_set)
509 if (disabled_channels == 0) {
510 gboolean retry;
512 do {
513 SelectList *p;
515 retry = FALSE;
516 for (p = select_list; p; p = p->next)
517 if (FD_ISSET (p->fd, select_set)) {
518 FD_CLR (p->fd, select_set);
519 (*p->callback)(p->fd, p->info);
520 retry = TRUE;
521 break;
523 } while (retry);
527 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
528 static void
529 try_channels (int set_timeout)
531 struct timeval timeout;
532 static fd_set select_set;
533 struct timeval *timeptr;
534 int v;
535 int maxfdp;
537 while (1) {
538 FD_ZERO (&select_set);
539 FD_SET (input_fd, &select_set); /* Add stdin */
540 maxfdp = max (add_selects (&select_set), input_fd);
542 if (set_timeout) {
543 timeout.tv_sec = 0;
544 timeout.tv_usec = 100000;
545 timeptr = &timeout;
546 } else
547 timeptr = 0;
549 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
550 if (v > 0) {
551 check_selects (&select_set);
552 if (FD_ISSET (input_fd, &select_set))
553 return;
558 static key_def *
559 create_sequence (const char *seq, int code, int action)
561 key_def *base, *p, *attach;
563 for (base = attach = NULL; *seq; seq++) {
564 p = g_new (key_def, 1);
565 if (base == NULL)
566 base = p;
567 if (attach != NULL)
568 attach->child = p;
570 p->ch = *seq;
571 p->code = code;
572 p->child = p->next = NULL;
573 if (seq[1] == '\0')
574 p->action = action;
575 else
576 p->action = MCKEY_NOACTION;
577 attach = p;
579 return base;
582 static void
583 define_sequences (const key_define_t *kd)
585 int i;
587 for (i = 0; kd[i].code != 0; i++)
588 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
591 static void
592 init_key_x11 (void)
594 #ifdef HAVE_TEXTMODE_X11_SUPPORT
595 if (getenv ("DISPLAY") != NULL) {
596 x11_display = mc_XOpenDisplay (0);
598 if (x11_display != NULL)
599 x11_window = DefaultRootWindow (x11_display);
601 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
604 /* Workaround for System V Curses vt100 bug */
605 static int
606 getch_with_delay (void)
608 int c;
610 /* This routine could be used on systems without mouse support,
611 so we need to do the select check :-( */
612 while (1) {
613 if (pending_keys == NULL)
614 try_channels (0);
616 /* Try to get a character */
617 c = get_key_code (0);
618 if (c != -1)
619 break;
620 /* Failed -> wait 0.1 secs and try again */
621 try_channels (1);
623 /* Success -> return the character */
624 return c;
627 static void
628 xmouse_get_event (Gpm_Event *ev)
630 int btn;
631 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
632 static struct timeval tv2;
633 static int clicks = 0;
634 static int last_btn = 0;
636 /* Decode Xterm mouse information to a GPM style event */
638 /* Variable btn has following meaning: */
639 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
640 btn = tty_lowlevel_getch () - 32;
642 /* There seems to be no way of knowing which button was released */
643 /* So we assume all the buttons were released */
645 if (btn == 3) {
646 if (last_btn) {
647 ev->type = GPM_UP | (GPM_SINGLE << clicks);
648 ev->buttons = 0;
649 last_btn = 0;
650 GET_TIME (tv1);
651 clicks = 0;
652 } else {
653 /* Bogus event, maybe mouse wheel */
654 ev->type = 0;
656 } else {
657 if (btn >= 32 && btn <= 34) {
658 btn -= 32;
659 ev->type = GPM_DRAG;
660 } else
661 ev->type = GPM_DOWN;
663 GET_TIME (tv2);
664 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)) {
665 clicks++;
666 clicks %= 3;
667 } else
668 clicks = 0;
670 switch (btn) {
671 case 0:
672 ev->buttons = GPM_B_LEFT;
673 break;
674 case 1:
675 ev->buttons = GPM_B_MIDDLE;
676 break;
677 case 2:
678 ev->buttons = GPM_B_RIGHT;
679 break;
680 case 64:
681 ev->buttons = GPM_B_UP;
682 clicks = 0;
683 break;
684 case 65:
685 ev->buttons = GPM_B_DOWN;
686 clicks = 0;
687 break;
688 default:
689 /* Nothing */
690 ev->type = 0;
691 ev->buttons = 0;
692 break;
694 last_btn = ev->buttons;
696 /* Coordinates are 33-based */
697 /* Transform them to 1-based */
698 ev->x = tty_lowlevel_getch () - 32;
699 ev->y = tty_lowlevel_getch () - 32;
703 * Get modifier state (shift, alt, ctrl) for the last key pressed.
704 * We are assuming that the state didn't change since the key press.
705 * This is only correct if get_modifier() is called very fast after
706 * the input was received, so that the user didn't release the
707 * modifier keys yet.
709 static int
710 get_modifier (void)
712 int result = 0;
713 #ifdef __QNXNTO__
714 int mod_status, shift_ext_status;
715 static int in_photon = 0;
716 static int ph_ig = 0;
717 PhCursorInfo_t cursor_info;
718 #endif /* __QNXNTO__ */
720 #ifdef HAVE_TEXTMODE_X11_SUPPORT
721 if (x11_window != 0) {
722 Window root, child;
723 int root_x, root_y;
724 int win_x, win_y;
725 unsigned int mask;
727 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
728 &root_y, &win_x, &win_y, &mask);
730 if (mask & ShiftMask)
731 result |= KEY_M_SHIFT;
732 if (mask & ControlMask)
733 result |= KEY_M_CTRL;
734 return result;
736 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
737 #ifdef __QNXNTO__
739 if (in_photon == 0) {
740 /* First time here, let's load Photon library and attach
741 to Photon */
742 in_photon = -1;
743 if (getenv ("PHOTON2_PATH") != NULL) {
744 /* QNX 6.x has no support for RTLD_LAZY */
745 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
746 if (ph_handle != NULL) {
747 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
748 ph_input_group =
749 (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
750 ph_query_cursor =
751 (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
752 if ((ph_attach != NULL) && (ph_input_group != NULL)
753 && (ph_query_cursor != NULL)) {
754 if ((*ph_attach) (0, 0)) { /* Attached */
755 ph_ig = (*ph_input_group) (0);
756 in_photon = 1;
762 /* We do not have Photon running. Assume we are in text
763 console or xterm */
764 if (in_photon == -1) {
765 if (devctl
766 (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status,
767 sizeof (int), NULL) == -1)
768 return 0;
769 shift_ext_status = mod_status & 0xffffff00UL;
770 mod_status &= 0x7f;
771 if (mod_status & _LINESTATUS_CON_ALT)
772 result |= KEY_M_ALT;
773 if (mod_status & _LINESTATUS_CON_CTRL)
774 result |= KEY_M_CTRL;
775 if ((mod_status & _LINESTATUS_CON_SHIFT)
776 || (shift_ext_status & 0x00000800UL))
777 result |= KEY_M_SHIFT;
778 } else {
779 (*ph_query_cursor) (ph_ig, &cursor_info);
780 if (cursor_info.key_mods & 0x04)
781 result |= KEY_M_ALT;
782 if (cursor_info.key_mods & 0x02)
783 result |= KEY_M_CTRL;
784 if (cursor_info.key_mods & 0x01)
785 result |= KEY_M_SHIFT;
787 #endif /* __QNXNTO__ */
789 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
791 unsigned char modifiers = 6;
793 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
794 return 0;
796 /* Translate Linux modifiers into mc modifiers */
797 if (modifiers & SHIFT_PRESSED)
798 result |= KEY_M_SHIFT;
799 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
800 result |= KEY_M_ALT;
801 if (modifiers & CONTROL_PRESSED)
802 result |= KEY_M_CTRL;
804 #endif /* !__linux__ */
805 return result;
808 static gboolean
809 push_char (int c)
811 gboolean ret = FALSE;
813 if (seq_append == NULL)
814 seq_append = seq_buffer;
816 if (seq_append != &(seq_buffer [SEQ_BUFFER_LEN - 2])) {
817 *(seq_append++) = c;
818 *seq_append = 0;
819 ret = TRUE;
822 return ret;
825 /* Apply corrections for the keycode generated in get_key_code() */
826 static int
827 correct_key_code (int code)
829 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
830 unsigned int mod = code & KEY_M_MASK; /* modifier */
831 #ifdef __QNXNTO__
832 unsigned int qmod; /* bunch of the QNX console
833 modifiers needs unchanged */
834 #endif /* __QNXNTO__ */
837 * Add key modifiers directly from X11 or OS.
838 * Ordinary characters only get modifiers from sequences.
840 if (c < 32 || c >= 256) {
841 mod |= get_modifier ();
844 /* This is needed if the newline is reported as carriage return */
845 if (c == '\r')
846 c = '\n';
848 /* This is reported to be useful on AIX */
849 if (c == KEY_SCANCEL)
850 c = '\t';
852 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
853 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
854 c = KEY_BTAB;
855 mod = 0;
858 /* F0 is the same as F10 for out purposes */
859 if (c == KEY_F (0))
860 c = KEY_F (10);
863 * We are not interested if Ctrl was pressed when entering control
864 * characters, so assume that it was. When checking for such keys,
865 * XCTRL macro should be used. In some cases, we are interested,
866 * e.g. to distinguish Ctrl-Enter from Enter.
868 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
869 mod |= KEY_M_CTRL;
872 #ifdef __QNXNTO__
873 qmod = get_modifier ();
875 if ((c == 127) && (mod == 0)) /* Add Ctrl/Alt/Shift-BackSpace */
877 mod |= get_modifier();
878 c = KEY_BACKSPACE;
881 if ((c == '0') && (mod == 0)) /* Add Shift-Insert on key pad */
883 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
885 mod = KEY_M_SHIFT;
886 c = KEY_IC;
890 if ((c == '.') && (mod == 0)) /* Add Shift-Del on key pad */
892 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
894 mod = KEY_M_SHIFT;
895 c = KEY_DC;
898 #endif /* __QNXNTO__ */
900 /* Unrecognized 0177 is delete (preserve Ctrl) */
901 if (c == 0177) {
902 c = KEY_BACKSPACE;
905 /* Unrecognized Ctrl-d is delete */
906 if (c == (31 & 'd')) {
907 c = KEY_DC;
908 mod &= ~KEY_M_CTRL;
911 /* Unrecognized Ctrl-h is backspace */
912 if (c == (31 & 'h')) {
913 c = KEY_BACKSPACE;
914 mod &= ~KEY_M_CTRL;
917 /* Shift+BackSpace is backspace */
918 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
919 mod &= ~KEY_M_SHIFT;
922 /* Convert Shift+Fn to F(n+10) */
923 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
924 c += 10;
927 /* Remove Shift information from function keys */
928 if (c >= KEY_F (1) && c <= KEY_F (20)) {
929 mod &= ~KEY_M_SHIFT;
932 if (!alternate_plus_minus)
933 switch (c) {
934 case KEY_KP_ADD:
935 c = '+';
936 break;
937 case KEY_KP_SUBTRACT:
938 c = '-';
939 break;
940 case KEY_KP_MULTIPLY:
941 c = '*';
942 break;
945 return (mod | c);
948 static int
949 xgetch_second (void)
951 fd_set Read_FD_Set;
952 int c;
953 struct timeval timeout;
955 timeout.tv_sec = keyboard_key_timeout / 1000000;
956 timeout.tv_usec = keyboard_key_timeout % 1000000;
957 tty_nodelay (TRUE);
958 FD_ZERO (&Read_FD_Set);
959 FD_SET (input_fd, &Read_FD_Set);
960 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
961 c = tty_lowlevel_getch ();
962 tty_nodelay (FALSE);
963 return c;
966 static void
967 learn_store_key (char *buffer, char **p, int c)
969 if (*p - buffer > 253)
970 return;
971 if (c == ESC_CHAR) {
972 *(*p)++ = '\\';
973 *(*p)++ = 'e';
974 } else if (c < ' ') {
975 *(*p)++ = '^';
976 *(*p)++ = c + 'a' - 1;
977 } else if (c == '^') {
978 *(*p)++ = '^';
979 *(*p)++ = '^';
980 } else
981 *(*p)++ = (char) c;
984 static void
985 k_dispose (key_def *k)
987 if (k != NULL) {
988 k_dispose (k->child);
989 k_dispose (k->next);
990 g_free (k);
994 static void
995 s_dispose (SelectList *sel)
997 if (sel != NULL) {
998 s_dispose (sel->next);
999 g_free (sel);
1003 /*** public functions **************************************************/
1005 /* This has to be called before init_slang or whatever routine
1006 calls any define_sequence */
1007 void
1008 init_key (void)
1010 const char *term = getenv ("TERM");
1011 const char *kt = getenv ("KEYBOARD_KEY_TIMEOUT_US");
1012 if (kt != NULL)
1013 keyboard_key_timeout = atoi (kt);
1015 /* This has to be the first define_sequence */
1016 /* So, we can assume that the first keys member has ESC */
1017 define_sequences (mc_default_keys);
1019 /* Terminfo on irix does not have some keys */
1020 if (xterm_flag
1021 || (term != NULL
1022 && (strncmp (term, "iris-ansi", 9) == 0
1023 || strncmp (term, "xterm", 5) == 0
1024 || strncmp (term, "rxvt", 4) == 0
1025 || strcmp (term, "screen") == 0)))
1026 define_sequences (xterm_key_defines);
1028 /* load some additional keys (e.g. direct Alt-? support) */
1029 load_xtra_key_defines ();
1031 #ifdef __QNX__
1032 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0)) {
1033 /* Modify the default value of use_8th_bit_as_meta: we would
1034 * like to provide a working mc for a newbie who knows nothing
1035 * about [Options|Display bits|Full 8 bits input]...
1037 * Don't use 'meta'-bit, when we are dealing with a
1038 * 'qnx*'-type terminal: clear the default value!
1039 * These terminal types use 0xFF as an escape character,
1040 * so use_8th_bit_as_meta==1 must not be enabled!
1042 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1043 * is not used now (doesn't even depend on use_8th_bit_as_meta
1044 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1046 use_8th_bit_as_meta = 0;
1048 #endif /* __QNX__ */
1050 init_key_x11 ();
1052 /* Load the qansi-m key definitions
1053 if we are running under the qansi-m terminal */
1054 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1055 define_sequences (qansi_key_defines);
1058 /* This has to be called after SLang_init_tty/slint_init */
1059 void
1060 init_key_input_fd (void)
1062 #ifdef HAVE_SLANG
1063 input_fd = SLang_TT_Read_FD;
1064 #endif
1067 void
1068 done_key (void)
1070 k_dispose (keys);
1071 s_dispose (select_list);
1073 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1074 if (x11_display)
1075 mc_XCloseDisplay (x11_display);
1076 #endif
1079 void
1080 add_select_channel (int fd, select_fn callback, void *info)
1082 SelectList *new;
1084 new = g_new (SelectList, 1);
1085 new->fd = fd;
1086 new->callback = callback;
1087 new->info = info;
1088 new->next = select_list;
1089 select_list = new;
1092 void
1093 delete_select_channel (int fd)
1095 SelectList *p = select_list;
1096 SelectList *p_prev = NULL;
1097 SelectList *p_next;
1099 while (p != NULL)
1100 if (p->fd == fd) {
1101 p_next = p->next;
1103 if (p_prev != NULL)
1104 p_prev->next = p_next;
1105 else
1106 select_list = p_next;
1108 g_free (p);
1109 p = p_next;
1110 } else {
1111 p_prev = p;
1112 p = p->next;
1116 void
1117 channels_up (void)
1119 if (disabled_channels == 0)
1120 fputs ("Error: channels_up called with disabled_channels = 0\n",
1121 stderr);
1122 disabled_channels--;
1125 void
1126 channels_down (void)
1128 disabled_channels++;
1132 * Common handler for standard movement keys in a text area. Provided
1133 * functions are called with the "data" argument. backfn and forfn also
1134 * get an argument indicating how many lines to scroll. Return MSG_HANDLED
1135 * if the key was handled, MSG_NOT_HANDLED otherwise.
1137 cb_ret_t
1138 check_movement_keys (int key, int page_size, void *data, move_fn backfn,
1139 move_fn forfn, move_fn topfn, move_fn bottomfn)
1141 switch (key) {
1142 case KEY_UP:
1143 case XCTRL ('p'):
1144 (*backfn) (data, 1);
1145 break;
1147 case KEY_DOWN:
1148 case XCTRL ('n'):
1149 (*forfn) (data, 1);
1150 break;
1152 case KEY_PPAGE:
1153 case ALT ('v'):
1154 (*backfn) (data, page_size - 1);
1155 break;
1157 case KEY_NPAGE:
1158 case XCTRL ('v'):
1159 (*forfn) (data, page_size - 1);
1160 break;
1162 case KEY_HOME:
1163 case KEY_M_CTRL | KEY_HOME:
1164 case KEY_M_CTRL | KEY_PPAGE:
1165 case KEY_A1:
1166 case ALT ('<'):
1167 (*topfn) (data, 0);
1168 break;
1170 case KEY_END:
1171 case KEY_M_CTRL | KEY_END:
1172 case KEY_M_CTRL | KEY_NPAGE:
1173 case KEY_C1:
1174 case ALT ('>'):
1175 (*bottomfn) (data, 0);
1176 break;
1178 case 'b':
1179 case KEY_BACKSPACE:
1180 (*backfn) (data, page_size - 1);
1181 break;
1183 case ' ':
1184 (*forfn) (data, page_size - 1);
1185 break;
1187 case 'u':
1188 (*backfn) (data, page_size / 2);
1189 break;
1191 case 'd':
1192 (*forfn) (data, page_size / 2);
1193 break;
1195 case 'g':
1196 (*topfn) (data, 0);
1197 break;
1199 case 'G':
1200 (*bottomfn) (data, 0);
1201 break;
1203 default:
1204 return MSG_NOT_HANDLED;
1206 return MSG_HANDLED;
1210 lookup_keyname (char *keyname)
1212 int i;
1214 if (keyname[0] == '\0')
1215 return 0;
1216 if (keyname[1] == '\0')
1217 return (int) keyname[0];
1219 for (i = 0; key_name_conv_tab [i].code; i++)
1220 if (str_casecmp (key_name_conv_tab [i].name, keyname) == 0)
1221 return key_name_conv_tab [i].code;
1223 return 0;
1226 /* Return the code associated with the symbolic name keyname */
1228 lookup_key (char *keyname)
1230 int k = -1;
1231 char **keys;
1232 guint keys_count = -1;
1233 int key = 0;
1234 int i = 0;
1236 if (keyname == NULL)
1237 return 0;
1239 keys = g_strsplit (keyname, " ", -1);
1240 keys_count = g_strv_length (keys);
1241 for (i = keys_count - 1; i >= 0; i--) {
1242 if (keys[i] !=NULL && keys[i][0] != 0) {
1243 key = lookup_keyname (keys[i]);
1244 if (key & KEY_M_CTRL) {
1245 if (k < 256)
1246 k = XCTRL(k);
1247 else
1248 k |= key;
1249 } else {
1250 if (k == -1)
1251 k = key;
1252 else
1253 k |= key;
1257 if (k == -1)
1258 return 0;
1260 return k;
1264 * Return TRUE on success, FALSE on error.
1265 * An error happens if SEQ is a beginning of an existing longer sequence.
1267 gboolean
1268 define_sequence (int code, const char *seq, int action)
1270 key_def *base;
1272 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1273 return FALSE;
1275 for (base = keys; (base != NULL) && (*seq != '\0'); )
1276 if (*seq == base->ch) {
1277 if (base->child == 0) {
1278 if (*(seq + 1) != '\0')
1279 base->child = create_sequence (seq + 1, code, action);
1280 else {
1281 /* The sequence matches an existing one. */
1282 base->code = code;
1283 base->action = action;
1285 return TRUE;
1288 base = base->child;
1289 seq++;
1290 } else {
1291 if (base->next)
1292 base = base->next;
1293 else {
1294 base->next = create_sequence (seq, code, action);
1295 return TRUE;
1299 if (*seq == '\0') {
1300 /* Attempt to redefine a sequence with a shorter sequence. */
1301 return FALSE;
1304 keys = create_sequence (seq, code, action);
1305 return TRUE;
1309 * Check if we are idle, i.e. there are no pending keyboard or mouse
1310 * events. Return 1 is idle, 0 is there are pending events.
1312 gboolean
1313 is_idle (void)
1315 int maxfdp;
1316 fd_set select_set;
1317 struct timeval timeout;
1319 FD_ZERO (&select_set);
1320 FD_SET (input_fd, &select_set);
1321 maxfdp = input_fd;
1322 #ifdef HAVE_LIBGPM
1323 if (mouse_enabled && (use_mouse_p == MOUSE_GPM) && (gpm_fd > 0)) {
1324 FD_SET (gpm_fd, &select_set);
1325 maxfdp = max (maxfdp, gpm_fd);
1327 #endif
1328 timeout.tv_sec = 0;
1329 timeout.tv_usec = 0;
1330 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1334 get_key_code (int no_delay)
1336 int c;
1337 static key_def *this = NULL, *parent;
1338 static struct timeval esctime = { -1, -1 };
1339 static int lastnodelay = -1;
1341 if (no_delay != lastnodelay) {
1342 this = NULL;
1343 lastnodelay = no_delay;
1346 pend_send:
1347 if (pending_keys != NULL) {
1348 int d = *pending_keys++;
1349 check_pend:
1350 if (*pending_keys == 0) {
1351 pending_keys = NULL;
1352 seq_append = NULL;
1354 if ((d == ESC_CHAR) && (pending_keys != NULL)) {
1355 d = ALT (*pending_keys++);
1356 goto check_pend;
1358 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
1359 d = ALT(d & 0x7f);
1360 this = NULL;
1361 return correct_key_code (d);
1364 nodelay_try_again:
1365 if (no_delay)
1366 tty_nodelay (TRUE);
1368 c = tty_lowlevel_getch ();
1369 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1370 if (c == KEY_RESIZE)
1371 goto nodelay_try_again;
1372 #endif
1373 if (no_delay) {
1374 tty_nodelay (FALSE);
1375 if (c == -1) {
1376 if (this != NULL && parent != NULL &&
1377 parent->action == MCKEY_ESCAPE && old_esc_mode) {
1378 struct timeval current, timeout;
1380 if (esctime.tv_sec == -1)
1381 return -1;
1382 GET_TIME (current);
1383 timeout.tv_sec = keyboard_key_timeout / 1000000 + esctime.tv_sec;
1384 timeout.tv_usec = keyboard_key_timeout % 1000000 + esctime.tv_usec;
1385 if (timeout.tv_usec > 1000000) {
1386 timeout.tv_usec -= 1000000;
1387 timeout.tv_sec++;
1389 if (current.tv_sec < timeout.tv_sec)
1390 return -1;
1391 if (current.tv_sec == timeout.tv_sec &&
1392 current.tv_usec < timeout.tv_usec)
1393 return -1;
1394 this = NULL;
1395 pending_keys = seq_append = NULL;
1396 return ESC_CHAR;
1398 return -1;
1400 } else if (c == -1) {
1401 /* Maybe we got an incomplete match.
1402 This we do only in delay mode, since otherwise
1403 tty_lowlevel_getch can return -1 at any time. */
1404 if (seq_append != NULL) {
1405 pending_keys = seq_buffer;
1406 goto pend_send;
1408 this = NULL;
1409 return -1;
1412 /* Search the key on the root */
1413 if (!no_delay || this == NULL) {
1414 this = keys;
1415 parent = NULL;
1417 if ((c > 127 && c < 256) && use_8th_bit_as_meta) {
1418 c &= 0x7f;
1420 /* The first sequence defined starts with esc */
1421 parent = keys;
1422 this = keys->child;
1425 while (this != NULL) {
1426 if (c == this->ch) {
1427 if (this->child) {
1428 if (!push_char (c)) {
1429 pending_keys = seq_buffer;
1430 goto pend_send;
1432 parent = this;
1433 this = this->child;
1434 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
1435 if (no_delay) {
1436 GET_TIME (esctime);
1437 if (this == NULL) {
1438 /* Shouldn't happen */
1439 fputs ("Internal error\n", stderr);
1440 exit (1);
1442 goto nodelay_try_again;
1444 esctime.tv_sec = -1;
1445 c = xgetch_second ();
1446 if (c == -1) {
1447 pending_keys = seq_append = NULL;
1448 this = NULL;
1449 return ESC_CHAR;
1451 } else {
1452 if (no_delay)
1453 goto nodelay_try_again;
1454 c = tty_lowlevel_getch ();
1456 } else {
1457 /* We got a complete match, return and reset search */
1458 int code;
1460 pending_keys = seq_append = NULL;
1461 code = this->code;
1462 this = NULL;
1463 return correct_key_code (code);
1465 } else {
1466 if (this->next != NULL)
1467 this = this->next;
1468 else {
1469 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) {
1470 /* Convert escape-digits to F-keys */
1471 if (g_ascii_isdigit(c))
1472 c = KEY_F (c - '0');
1473 else if (c == ' ')
1474 c = ESC_CHAR;
1475 else
1476 c = ALT (c);
1478 pending_keys = seq_append = NULL;
1479 this = NULL;
1480 return correct_key_code (c);
1482 /* Did not find a match or {c} was changed in the if above,
1483 so we have to return everything we had skipped
1485 push_char (c);
1486 pending_keys = seq_buffer;
1487 goto pend_send;
1491 this = NULL;
1492 return correct_key_code (c);
1495 /* Returns a character read from stdin with appropriate interpretation */
1496 /* Also takes care of generated mouse events */
1497 /* Returns EV_MOUSE if it is a mouse event */
1498 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1500 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1502 int c;
1503 static int flag = 0; /* Return value from select */
1504 #ifdef HAVE_LIBGPM
1505 static struct Gpm_Event ev; /* Mouse event */
1506 #endif
1507 struct timeval timeout;
1508 struct timeval *time_addr = NULL;
1509 static int dirty = 3;
1511 if ((dirty == 3) || is_idle ()) {
1512 mc_refresh ();
1513 dirty = 1;
1514 } else
1515 dirty++;
1517 vfs_timeout_handler ();
1519 /* Ok, we use (event->x < 0) to signal that the event does not contain
1520 a suitable position for the mouse, so we can't use show_mouse_pointer
1521 on it.
1523 if (event->x > 0) {
1524 show_mouse_pointer (event->x, event->y);
1525 if (!redo_event)
1526 event->x = -1;
1529 /* Repeat if using mouse */
1530 while (pending_keys == NULL) {
1531 int maxfdp;
1532 fd_set select_set;
1534 FD_ZERO (&select_set);
1535 FD_SET (input_fd, &select_set);
1536 maxfdp = max (add_selects (&select_set), input_fd);
1538 #ifdef HAVE_LIBGPM
1539 if (mouse_enabled && (use_mouse_p == MOUSE_GPM)) {
1540 if (gpm_fd < 0) {
1541 /* Connection to gpm broken, possibly gpm has died */
1542 mouse_enabled = FALSE;
1543 use_mouse_p = MOUSE_NONE;
1544 break;
1547 FD_SET (gpm_fd, &select_set);
1548 maxfdp = max (maxfdp, gpm_fd);
1550 #endif
1552 if (redo_event) {
1553 timeout.tv_usec = mou_auto_repeat * 1000;
1554 timeout.tv_sec = 0;
1556 time_addr = &timeout;
1557 } else {
1558 int seconds;
1560 seconds = vfs_timeouts ();
1561 time_addr = NULL;
1563 if (seconds != 0) {
1564 /* the timeout could be improved and actually be
1565 * the number of seconds until the next vfs entry
1566 * timeouts in the stamp list.
1569 timeout.tv_sec = seconds;
1570 timeout.tv_usec = 0;
1571 time_addr = &timeout;
1575 if (!block || winch_flag) {
1576 time_addr = &timeout;
1577 timeout.tv_sec = 0;
1578 timeout.tv_usec = 0;
1581 tty_enable_interrupt_key ();
1582 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1583 tty_disable_interrupt_key ();
1585 /* select timed out: it could be for any of the following reasons:
1586 * redo_event -> it was because of the MOU_REPEAT handler
1587 * !block -> we did not block in the select call
1588 * else -> 10 second timeout to check the vfs status.
1590 if (flag == 0) {
1591 if (redo_event)
1592 return EV_MOUSE;
1593 if (!block || winch_flag)
1594 return EV_NONE;
1595 vfs_timeout_handler ();
1597 if (flag == -1 && errno == EINTR)
1598 return EV_NONE;
1600 check_selects (&select_set);
1602 if (FD_ISSET (input_fd, &select_set))
1603 break;
1604 #ifdef HAVE_LIBGPM
1605 if (mouse_enabled && use_mouse_p == MOUSE_GPM
1606 && gpm_fd > 0 && FD_ISSET (gpm_fd, &select_set)) {
1607 Gpm_GetEvent (&ev);
1608 Gpm_FitEvent (&ev);
1609 *event = ev;
1610 return EV_MOUSE;
1612 #endif /* !HAVE_LIBGPM */
1615 #ifndef HAVE_SLANG
1616 flag = is_wintouched (stdscr);
1617 untouchwin (stdscr);
1618 #endif /* !HAVE_SLANG */
1619 c = block ? getch_with_delay () : get_key_code (1);
1621 #ifndef HAVE_SLANG
1622 if (flag > 0)
1623 tty_touch_screen ();
1624 #endif /* !HAVE_SLANG */
1626 if (mouse_enabled && (c == MCKEY_MOUSE
1627 #ifdef KEY_MOUSE
1628 || c == KEY_MOUSE
1629 #endif /* KEY_MOUSE */
1630 )) {
1631 /* Mouse event */
1632 xmouse_get_event (event);
1633 return (event->type != 0) ? EV_MOUSE : EV_NONE;
1636 return c;
1639 /* Returns a key press, mouse events are discarded */
1641 tty_getch (void)
1643 Gpm_Event ev;
1644 int key;
1646 ev.x = -1;
1647 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
1649 return key;
1652 char *
1653 learn_key (void)
1655 /* LEARN_TIMEOUT in usec */
1656 #define LEARN_TIMEOUT 200000
1658 fd_set Read_FD_Set;
1659 struct timeval endtime;
1660 struct timeval timeout;
1661 int c;
1662 char buffer [256];
1663 char *p = buffer;
1665 tty_keypad (FALSE); /* disable intepreting keys by ncurses */
1666 c = tty_lowlevel_getch ();
1667 while (c == -1)
1668 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
1669 learn_store_key (buffer, &p, c);
1670 GET_TIME (endtime);
1671 endtime.tv_usec += LEARN_TIMEOUT;
1672 if (endtime.tv_usec > 1000000) {
1673 endtime.tv_usec -= 1000000;
1674 endtime.tv_sec++;
1676 tty_nodelay (TRUE);
1677 for (;;) {
1678 while ((c = tty_lowlevel_getch ()) == -1) {
1679 GET_TIME (timeout);
1680 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1681 if (timeout.tv_usec < 0)
1682 timeout.tv_sec++;
1683 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1684 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1685 FD_ZERO (&Read_FD_Set);
1686 FD_SET (input_fd, &Read_FD_Set);
1687 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1688 } else
1689 break;
1691 if (c == -1)
1692 break;
1693 learn_store_key (buffer, &p, c);
1695 tty_keypad (TRUE);
1696 tty_nodelay (FALSE);
1697 *p = '\0';
1698 return g_strdup (buffer);
1699 #undef LEARN_TIMEOUT
1702 /* xterm and linux console only: set keypad to numeric or application
1703 mode. Only in application keypad mode it's possible to distinguish
1704 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1705 void
1706 numeric_keypad_mode (void)
1708 if (console_flag || xterm_flag) {
1709 fputs ("\033>", stdout);
1710 fflush (stdout);
1714 void
1715 application_keypad_mode (void)
1717 if (console_flag || xterm_flag) {
1718 fputs ("\033=", stdout);
1719 fflush (stdout);