Merge commit 'origin/mc-4.6'
[midnight-commander.git] / src / key.c
blobd531541f55b2c0535b02201f20b24cc1e4e61ea7
1 /* Keyboard support routines.
3 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007 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 #include <config.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include <sys/types.h>
34 #include <unistd.h>
36 #include "global.h"
37 #include "tty.h"
38 #include "mouse.h"
39 #include "key.h"
40 #include "layout.h" /* winch_flag */
41 #include "main.h"
42 #include "win.h"
43 #include "cons.saver.h"
45 #ifdef USE_VFS
46 #include "../vfs/gc.h"
47 #endif
49 #ifdef HAVE_TEXTMODE_X11_SUPPORT
50 # include "x11conn.h"
51 #endif
53 #ifdef __linux__
54 # if defined(__GLIBC__) && (__GLIBC__ < 2)
55 # include <linux/termios.h> /* TIOCLINUX */
56 # else
57 # include <termios.h>
58 # endif
59 # include <sys/ioctl.h>
60 #endif /* __linux__ */
62 #ifdef __CYGWIN__
63 # include <termios.h>
64 # include <sys/ioctl.h>
65 #endif /* __CYGWIN__ */
67 #ifdef __QNXNTO__
68 # include <dlfcn.h>
69 # include <Ph.h>
70 # include <sys/dcmd_chr.h>
71 #endif
73 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
74 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
75 (t2.tv_usec-t1.tv_usec)/1000)
77 /* Linux console keyboard modifiers */
78 #define SHIFT_PRESSED (1 << 0)
79 #define ALTR_PRESSED (1 << 1)
80 #define CONTROL_PRESSED (1 << 2)
81 #define ALTL_PRESSED (1 << 3)
83 int mou_auto_repeat = 100;
84 int double_click_speed = 250;
85 int old_esc_mode = 0;
86 /* timeout for old_esc_mode in usec */
87 int keyboard_key_timeout = 1000000; /* settable via env */
89 int use_8th_bit_as_meta = 0;
91 typedef struct key_def {
92 char ch; /* Holds the matching char code */
93 int code; /* The code returned, valid if child == NULL */
94 struct key_def *next;
95 struct key_def *child; /* sequence continuation */
96 int action; /* optional action to be done. Now used only
97 to mark that we are just after the first
98 Escape */
99 } key_def;
101 /* This holds all the key definitions */
102 static key_def *keys = NULL;
104 static int input_fd;
105 static int disabled_channels = 0; /* Disable channels checking */
106 static int xgetch_second (void);
107 static int get_modifier (void);
109 /* File descriptor monitoring add/remove routines */
110 typedef struct SelectList {
111 int fd;
112 select_fn callback;
113 void *info;
114 struct SelectList *next;
115 } SelectList;
117 #ifdef __QNXNTO__
118 typedef int (*ph_dv_f) (void *, void *);
119 typedef int (*ph_ov_f) (void *);
120 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
121 ph_dv_f ph_attach;
122 ph_ov_f ph_input_group;
123 ph_pqc_f ph_query_cursor;
124 #endif
126 static SelectList *select_list = NULL;
128 void add_select_channel (int fd, select_fn callback, void *info)
130 SelectList *new;
132 new = g_new (SelectList, 1);
133 new->fd = fd;
134 new->callback = callback;
135 new->info = info;
136 new->next = select_list;
137 select_list = new;
140 void delete_select_channel (int fd)
142 SelectList *p = select_list;
143 SelectList *p_prev = NULL;
144 SelectList *p_next;
146 while (p) {
147 if (p->fd == fd) {
148 p_next = p->next;
150 if (p_prev)
151 p_prev->next = p_next;
152 else
153 select_list = p_next;
155 g_free (p);
156 p = p_next;
157 continue;
160 p_prev = p;
161 p = p->next;
165 inline static int add_selects (fd_set *select_set)
167 SelectList *p;
168 int top_fd = 0;
170 if (disabled_channels)
171 return 0;
173 for (p = select_list; p; p = p->next) {
174 FD_SET (p->fd, select_set);
175 if (p->fd > top_fd)
176 top_fd = p->fd;
178 return top_fd;
181 static void check_selects (fd_set *select_set)
183 SelectList *p;
184 gboolean retry;
186 if (disabled_channels)
187 return;
189 do {
190 retry = FALSE;
191 for (p = select_list; p; p = p->next)
192 if (FD_ISSET (p->fd, select_set)) {
193 FD_CLR (p->fd, select_set);
194 (*p->callback)(p->fd, p->info);
195 retry = TRUE;
196 break;
198 } while (retry);
201 void channels_down (void)
203 disabled_channels++;
206 void channels_up (void)
208 if (!disabled_channels)
209 fputs ("Error: channels_up called with disabled_channels = 0\n",
210 stderr);
211 disabled_channels--;
214 typedef const struct {
215 int code;
216 const char *seq;
217 int action;
218 } key_define_t;
220 /* Broken terminfo and termcap databases on xterminals */
221 static key_define_t xterm_key_defines [] = {
222 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
223 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
224 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
225 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
226 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
227 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
228 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
229 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
230 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
231 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
232 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
233 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
234 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
235 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
237 /* old xterm Shift-arrows */
238 { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
239 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
240 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
241 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
243 /* new xterm Shift-arrows */
244 { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
245 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
246 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
247 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
249 /* more xterm keys with modifiers */
250 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
251 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
252 { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
253 { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
254 { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
255 { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
256 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
257 { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
258 { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
259 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
260 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
261 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
262 { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
263 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
264 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
265 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
266 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
267 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
269 /* rxvt keys with modifiers */
270 { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
271 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
272 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
273 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
274 { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
275 { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
276 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
277 { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
278 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
279 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
280 { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
281 { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
282 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
283 { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
284 { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
285 { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
286 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
288 /* konsole keys with modifiers */
289 { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
290 { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
292 /* gnome-terminal */
293 { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
294 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
295 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
296 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
297 { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
298 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
299 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
300 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
301 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
302 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
303 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
304 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
306 /* gnome-terminal - application mode */
307 { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
308 { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
309 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
310 { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
311 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
312 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
313 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
314 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
316 /* iTerm */
317 { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION },
318 { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION },
320 /* keypad keys */
321 { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
322 { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
323 { '/', ESC_STR "Oo", MCKEY_NOACTION },
324 { '\n', ESC_STR "OM", MCKEY_NOACTION },
326 { 0, 0, MCKEY_NOACTION },
329 /* qansi-m terminals have a much more key combinatios,
330 which are undefined in termcap/terminfo */
331 static key_define_t qansi_key_defines[] =
333 /* qansi-m terminal */
334 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
335 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
336 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
337 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
338 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
339 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
340 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
341 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
342 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
343 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
344 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
345 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
346 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
347 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
348 {KEY_M_CTRL | KEY_F(1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
349 {KEY_M_CTRL | KEY_F(2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
350 {KEY_M_CTRL | KEY_F(3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
351 {KEY_M_CTRL | KEY_F(4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
352 {KEY_M_CTRL | KEY_F(5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
353 {KEY_M_CTRL | KEY_F(6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
354 {KEY_M_CTRL | KEY_F(7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
355 {KEY_M_CTRL | KEY_F(8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
356 {KEY_M_CTRL | KEY_F(9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
357 {KEY_M_CTRL | KEY_F(10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
358 {KEY_M_CTRL | KEY_F(11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
359 {KEY_M_CTRL | KEY_F(12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
360 {KEY_M_ALT | KEY_F(1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
361 {KEY_M_ALT | KEY_F(2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
362 {KEY_M_ALT | KEY_F(3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
363 {KEY_M_ALT | KEY_F(4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
364 {KEY_M_ALT | KEY_F(5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
365 {KEY_M_ALT | KEY_F(6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
366 {KEY_M_ALT | KEY_F(7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
367 {KEY_M_ALT | KEY_F(8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
368 {KEY_M_ALT | KEY_F(9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
369 {KEY_M_ALT | KEY_F(10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
370 {KEY_M_ALT | KEY_F(11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
371 {KEY_M_ALT | KEY_F(12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
372 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
373 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
374 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
375 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
376 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
377 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
378 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
379 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
380 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
381 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
382 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
383 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
384 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
385 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
386 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
387 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-r */
388 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
389 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
390 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
391 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
392 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
393 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
394 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
395 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
396 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
397 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
398 {0, NULL, MCKEY_NOACTION},
401 static key_define_t mc_default_keys [] = {
402 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
403 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
404 { 0, NULL, MCKEY_NOACTION },
407 static void
408 define_sequences (key_define_t *kd)
410 int i;
412 for (i = 0; kd[i].code != 0; i++)
413 define_sequence(kd[i].code, kd[i].seq, kd[i].action);
416 #ifdef HAVE_TEXTMODE_X11_SUPPORT
418 static Display *x11_display;
419 static Window x11_window;
421 static void
422 init_key_x11 (void)
424 if (!getenv ("DISPLAY"))
425 return;
427 x11_display = mc_XOpenDisplay (0);
429 if (x11_display)
430 x11_window = DefaultRootWindow (x11_display);
432 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
435 /* This has to be called before slang_init or whatever routine
436 calls any define_sequence */
437 void
438 init_key (void)
440 const char *term = getenv ("TERM");
441 char *kt = getenv ("KEYBOARD_KEY_TIMEOUT_US");
442 if (kt != NULL)
443 keyboard_key_timeout = atoi (kt);
445 /* This has to be the first define_sequence */
446 /* So, we can assume that the first keys member has ESC */
447 define_sequences (mc_default_keys);
449 /* Terminfo on irix does not have some keys */
450 if (xterm_flag
451 || (term != NULL
452 && (strncmp (term, "iris-ansi", 9) == 0
453 || strncmp (term, "xterm", 5) == 0
454 || strncmp (term, "rxvt", 4) == 0
455 || strcmp (term, "screen") == 0)))
456 define_sequences (xterm_key_defines);
458 /* load some additional keys (e.g. direct Alt-? support) */
459 load_xtra_key_defines ();
461 #ifdef __QNX__
462 if (term && strncmp (term, "qnx", 3) == 0) {
463 /* Modify the default value of use_8th_bit_as_meta: we would
464 * like to provide a working mc for a newbie who knows nothing
465 * about [Options|Display bits|Full 8 bits input]...
467 * Don't use 'meta'-bit, when we are dealing with a
468 * 'qnx*'-type terminal: clear the default value!
469 * These terminal types use 0xFF as an escape character,
470 * so use_8th_bit_as_meta==1 must not be enabled!
472 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
473 * is not used now (doesn't even depend on use_8th_bit_as_meta
474 * as in mc-3.1.2)...GREAT!...no additional code is required!]
476 use_8th_bit_as_meta = 0;
478 #endif /* __QNX__ */
480 #ifdef HAVE_TEXTMODE_X11_SUPPORT
481 init_key_x11 ();
482 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
484 /* Load the qansi-m key definitions
485 if we are running under the qansi-m terminal */
486 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0)) {
487 define_sequences (qansi_key_defines);
491 /* This has to be called after SLang_init_tty/slint_init */
492 void init_key_input_fd (void)
494 #ifdef HAVE_SLANG
495 input_fd = SLang_TT_Read_FD;
496 #endif
500 static void
501 xmouse_get_event (Gpm_Event *ev)
503 int btn;
504 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
505 static struct timeval tv2;
506 static int clicks;
507 static int last_btn = 0;
509 /* Decode Xterm mouse information to a GPM style event */
511 /* Variable btn has following meaning: */
512 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
513 btn = getch () - 32;
515 /* There seems to be no way of knowing which button was released */
516 /* So we assume all the buttons were released */
518 if (btn == 3) {
519 if (last_btn) {
520 ev->type = GPM_UP | (GPM_SINGLE << clicks);
521 ev->buttons = 0;
522 last_btn = 0;
523 GET_TIME (tv1);
524 clicks = 0;
525 } else {
526 /* Bogus event, maybe mouse wheel */
527 ev->type = 0;
529 } else {
530 if (btn >= 32 && btn <= 34) {
531 btn -= 32;
532 ev->type = GPM_DRAG;
533 } else
534 ev->type = GPM_DOWN;
536 GET_TIME (tv2);
537 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)) {
538 clicks++;
539 clicks %= 3;
540 } else
541 clicks = 0;
543 switch (btn) {
544 case 0:
545 ev->buttons = GPM_B_LEFT;
546 break;
547 case 1:
548 ev->buttons = GPM_B_MIDDLE;
549 break;
550 case 2:
551 ev->buttons = GPM_B_RIGHT;
552 break;
553 case 64:
554 ev->buttons = GPM_B_UP;
555 clicks = 0;
556 break;
557 case 65:
558 ev->buttons = GPM_B_DOWN;
559 clicks = 0;
560 break;
561 default:
562 /* Nothing */
563 ev->type = 0;
564 ev->buttons = 0;
565 break;
567 last_btn = ev->buttons;
569 /* Coordinates are 33-based */
570 /* Transform them to 1-based */
571 ev->x = getch () - 32;
572 ev->y = getch () - 32;
575 static key_def *create_sequence (const char *seq, int code, int action)
577 key_def *base, *p, *attach;
579 for (base = attach = NULL; *seq; seq++) {
580 p = g_new (key_def, 1);
581 if (!base) base = p;
582 if (attach) attach->child = p;
584 p->ch = *seq;
585 p->code = code;
586 p->child = p->next = NULL;
587 if (!seq[1])
588 p->action = action;
589 else
590 p->action = MCKEY_NOACTION;
591 attach = p;
593 return base;
596 /* The maximum sequence length (32 + null terminator) */
597 #define SEQ_BUFFER_LEN 33
598 static int seq_buffer [SEQ_BUFFER_LEN];
599 static int *seq_append = 0;
601 static int push_char (int c)
603 if (!seq_append)
604 seq_append = seq_buffer;
606 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
607 return 0;
608 *(seq_append++) = c;
609 *seq_append = 0;
610 return 1;
614 * Return 1 on success, 0 on error.
615 * An error happens if SEQ is a beginning of an existing longer sequence.
617 int define_sequence (int code, const char *seq, int action)
619 key_def *base;
621 if (strlen (seq) > SEQ_BUFFER_LEN-1)
622 return 0;
624 for (base = keys; (base != 0) && *seq; ) {
625 if (*seq == base->ch) {
626 if (base->child == 0) {
627 if (*(seq+1)) {
628 base->child = create_sequence (seq+1, code, action);
629 return 1;
630 } else {
631 /* The sequence matches an existing one. */
632 base->code = code;
633 base->action = action;
634 return 1;
636 } else {
637 base = base->child;
638 seq++;
640 } else {
641 if (base->next)
642 base = base->next;
643 else {
644 base->next = create_sequence (seq, code, action);
645 return 1;
650 if (!*seq) {
651 /* Attempt to redefine a sequence with a shorter sequence. */
652 return 0;
655 keys = create_sequence (seq, code, action);
656 return 1;
659 static int *pending_keys;
661 /* Apply corrections for the keycode generated in get_key_code() */
662 static int
663 correct_key_code (int code)
665 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
666 unsigned int mod = code & KEY_M_MASK; /* modifier */
667 #ifdef __QNXNTO__
668 unsigned int qmod; /* bunch of the QNX console
669 modifiers needs unchanged */
670 #endif /* __QNXNTO__ */
673 * Add key modifiers directly from X11 or OS.
674 * Ordinary characters only get modifiers from sequences.
676 if (c < 32 || c >= 256) {
677 mod |= get_modifier ();
680 /* This is needed if the newline is reported as carriage return */
681 if (c == '\r')
682 c = '\n';
684 /* This is reported to be useful on AIX */
685 if (c == KEY_SCANCEL)
686 c = '\t';
688 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
689 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
690 c = KEY_BTAB;
691 mod = 0;
694 /* F0 is the same as F10 for out purposes */
695 if (c == KEY_F (0))
696 c = KEY_F (10);
699 * We are not interested if Ctrl was pressed when entering control
700 * characters, so assume that it was. When checking for such keys,
701 * XCTRL macro should be used. In some cases, we are interested,
702 * e.g. to distinguish Ctrl-Enter from Enter.
704 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
705 mod |= KEY_M_CTRL;
708 #ifdef __QNXNTO__
709 qmod=get_modifier();
711 if ((c == 127) && (mod==0)) /* Add Ctrl/Alt/Shift-BackSpace */
713 mod |= get_modifier();
714 c = KEY_BACKSPACE;
717 if ((c=='0') && (mod==0)) /* Add Shift-Insert on key pad */
719 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
721 mod = KEY_M_SHIFT;
722 c = KEY_IC;
726 if ((c=='.') && (mod==0)) /* Add Shift-Del on key pad */
728 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
730 mod = KEY_M_SHIFT;
731 c = KEY_DC;
734 #endif /* __QNXNTO__ */
736 /* Unrecognized 0177 is delete (preserve Ctrl) */
737 if (c == 0177) {
738 c = KEY_BACKSPACE;
741 /* Unrecognized Ctrl-d is delete */
742 if (c == (31 & 'd')) {
743 c = KEY_DC;
744 mod &= ~KEY_M_CTRL;
747 /* Unrecognized Ctrl-h is backspace */
748 if (c == (31 & 'h')) {
749 c = KEY_BACKSPACE;
750 mod &= ~KEY_M_CTRL;
753 /* Shift+BackSpace is backspace */
754 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
755 mod &= ~KEY_M_SHIFT;
758 /* Convert Shift+Fn to F(n+10) */
759 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
760 c += 10;
763 /* Remove Shift information from function keys */
764 if (c >= KEY_F (1) && c <= KEY_F (20)) {
765 mod &= ~KEY_M_SHIFT;
768 if (!alternate_plus_minus)
769 switch (c) {
770 case KEY_KP_ADD:
771 c = '+';
772 break;
773 case KEY_KP_SUBTRACT:
774 c = '-';
775 break;
776 case KEY_KP_MULTIPLY:
777 c = '*';
778 break;
781 return (mod | c);
784 int get_key_code (int no_delay)
786 int c;
787 static key_def *this = NULL, *parent;
788 static struct timeval esctime = { -1, -1 };
789 static int lastnodelay = -1;
791 if (no_delay != lastnodelay) {
792 this = NULL;
793 lastnodelay = no_delay;
796 pend_send:
797 if (pending_keys) {
798 int d = *pending_keys++;
799 check_pend:
800 if (!*pending_keys) {
801 pending_keys = 0;
802 seq_append = 0;
804 if (d == ESC_CHAR && pending_keys) {
805 d = ALT(*pending_keys++);
806 goto check_pend;
808 if ((d > 127 && d < 256) && use_8th_bit_as_meta)
809 d = ALT(d & 0x7f);
810 this = NULL;
811 return correct_key_code (d);
814 nodelay_try_again:
815 if (no_delay) {
816 nodelay (stdscr, TRUE);
818 c = getch ();
819 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
820 if (c == KEY_RESIZE)
821 goto nodelay_try_again;
822 #endif
823 if (no_delay) {
824 nodelay (stdscr, FALSE);
825 if (c == -1) {
826 if (this != NULL && parent != NULL &&
827 parent->action == MCKEY_ESCAPE && old_esc_mode) {
828 struct timeval current, timeout;
830 if (esctime.tv_sec == -1)
831 return -1;
832 GET_TIME (current);
833 timeout.tv_sec = keyboard_key_timeout / 1000000 + esctime.tv_sec;
834 timeout.tv_usec = keyboard_key_timeout % 1000000 + esctime.tv_usec;
835 if (timeout.tv_usec > 1000000) {
836 timeout.tv_usec -= 1000000;
837 timeout.tv_sec++;
839 if (current.tv_sec < timeout.tv_sec)
840 return -1;
841 if (current.tv_sec == timeout.tv_sec &&
842 current.tv_usec < timeout.tv_usec)
843 return -1;
844 this = NULL;
845 pending_keys = seq_append = NULL;
846 return ESC_CHAR;
848 return -1;
850 } else if (c == -1) {
851 /* Maybe we got an incomplete match.
852 This we do only in delay mode, since otherwise
853 getch can return -1 at any time. */
854 if (seq_append) {
855 pending_keys = seq_buffer;
856 goto pend_send;
858 this = NULL;
859 return -1;
862 /* Search the key on the root */
863 if (!no_delay || this == NULL) {
864 this = keys;
865 parent = NULL;
867 if ((c > 127 && c < 256) && use_8th_bit_as_meta) {
868 c &= 0x7f;
870 /* The first sequence defined starts with esc */
871 parent = keys;
872 this = keys->child;
875 while (this) {
876 if (c == this->ch) {
877 if (this->child) {
878 if (!push_char (c)) {
879 pending_keys = seq_buffer;
880 goto pend_send;
882 parent = this;
883 this = this->child;
884 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
885 if (no_delay) {
886 GET_TIME (esctime);
887 if (this == NULL) {
888 /* Shouldn't happen */
889 fputs ("Internal error\n", stderr);
890 exit (1);
892 goto nodelay_try_again;
894 esctime.tv_sec = -1;
895 c = xgetch_second ();
896 if (c == -1) {
897 pending_keys = seq_append = NULL;
898 this = NULL;
899 return ESC_CHAR;
901 } else {
902 if (no_delay)
903 goto nodelay_try_again;
904 c = getch ();
906 } else {
907 /* We got a complete match, return and reset search */
908 int code;
910 pending_keys = seq_append = NULL;
911 code = this->code;
912 this = NULL;
913 return correct_key_code (code);
915 } else {
916 if (this->next)
917 this = this->next;
918 else {
919 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
921 /* Convert escape-digits to F-keys */
922 if (isdigit(c))
923 c = KEY_F (c - '0');
924 else if (c == ' ')
925 c = ESC_CHAR;
926 else
927 c = ALT(c);
929 pending_keys = seq_append = NULL;
930 this = NULL;
931 return correct_key_code (c);
933 /* Did not find a match or {c} was changed in the if above,
934 so we have to return everything we had skipped
936 push_char (c);
937 pending_keys = seq_buffer;
938 goto pend_send;
942 this = NULL;
943 return correct_key_code (c);
946 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
947 static void
948 try_channels (int set_timeout)
950 struct timeval timeout;
951 static fd_set select_set;
952 struct timeval *timeptr;
953 int v;
954 int maxfdp;
956 while (1) {
957 FD_ZERO (&select_set);
958 FD_SET (input_fd, &select_set); /* Add stdin */
959 maxfdp = max (add_selects (&select_set), input_fd);
961 if (set_timeout) {
962 timeout.tv_sec = 0;
963 timeout.tv_usec = 100000;
964 timeptr = &timeout;
965 } else
966 timeptr = 0;
968 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
969 if (v > 0) {
970 check_selects (&select_set);
971 if (FD_ISSET (input_fd, &select_set))
972 return;
977 /* Workaround for System V Curses vt100 bug */
978 static int getch_with_delay (void)
980 int c;
982 /* This routine could be used on systems without mouse support,
983 so we need to do the select check :-( */
984 while (1) {
985 if (!pending_keys)
986 try_channels (0);
988 /* Try to get a character */
989 c = get_key_code (0);
990 if (c != -1)
991 break;
992 /* Failed -> wait 0.1 secs and try again */
993 try_channels (1);
995 /* Success -> return the character */
996 return c;
999 /* Returns a character read from stdin with appropriate interpretation */
1000 /* Also takes care of generated mouse events */
1001 /* Returns EV_MOUSE if it is a mouse event */
1002 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1004 get_event (struct Gpm_Event *event, int redo_event, int block)
1006 int c;
1007 static int flag; /* Return value from select */
1008 #ifdef HAVE_LIBGPM
1009 static struct Gpm_Event ev; /* Mouse event */
1010 #endif
1011 struct timeval timeout;
1012 struct timeval *time_addr = NULL;
1013 static int dirty = 3;
1015 if ((dirty == 3) || is_idle ()) {
1016 mc_refresh ();
1017 doupdate ();
1018 dirty = 1;
1019 } else
1020 dirty++;
1022 vfs_timeout_handler ();
1024 /* Ok, we use (event->x < 0) to signal that the event does not contain
1025 a suitable position for the mouse, so we can't use show_mouse_pointer
1026 on it.
1028 if (event->x > 0) {
1029 show_mouse_pointer (event->x, event->y);
1030 if (!redo_event)
1031 event->x = -1;
1034 /* Repeat if using mouse */
1035 while (mouse_enabled && !pending_keys) {
1036 int maxfdp;
1037 fd_set select_set;
1039 FD_ZERO (&select_set);
1040 FD_SET (input_fd, &select_set);
1041 maxfdp = max (add_selects (&select_set), input_fd);
1043 #ifdef HAVE_LIBGPM
1044 if (use_mouse_p == MOUSE_GPM) {
1045 if (gpm_fd < 0) {
1046 /* Connection to gpm broken, possibly gpm has died */
1047 mouse_enabled = 0;
1048 use_mouse_p = MOUSE_NONE;
1049 break;
1050 } else {
1051 FD_SET (gpm_fd, &select_set);
1052 maxfdp = max (maxfdp, gpm_fd);
1055 #endif
1057 if (redo_event) {
1058 timeout.tv_usec = mou_auto_repeat * 1000;
1059 timeout.tv_sec = 0;
1061 time_addr = &timeout;
1062 } else {
1063 int seconds;
1065 if ((seconds = vfs_timeouts ())) {
1066 /* the timeout could be improved and actually be
1067 * the number of seconds until the next vfs entry
1068 * timeouts in the stamp list.
1071 timeout.tv_sec = seconds;
1072 timeout.tv_usec = 0;
1073 time_addr = &timeout;
1074 } else
1075 time_addr = NULL;
1078 if (!block || winch_flag) {
1079 time_addr = &timeout;
1080 timeout.tv_sec = 0;
1081 timeout.tv_usec = 0;
1083 enable_interrupt_key ();
1084 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1085 disable_interrupt_key ();
1087 /* select timed out: it could be for any of the following reasons:
1088 * redo_event -> it was because of the MOU_REPEAT handler
1089 * !block -> we did not block in the select call
1090 * else -> 10 second timeout to check the vfs status.
1092 if (flag == 0) {
1093 if (redo_event)
1094 return EV_MOUSE;
1095 if (!block || winch_flag)
1096 return EV_NONE;
1097 vfs_timeout_handler ();
1099 if (flag == -1 && errno == EINTR)
1100 return EV_NONE;
1102 check_selects (&select_set);
1104 if (FD_ISSET (input_fd, &select_set))
1105 break;
1106 #ifdef HAVE_LIBGPM
1107 if (use_mouse_p == MOUSE_GPM && gpm_fd > 0
1108 && FD_ISSET (gpm_fd, &select_set)) {
1109 Gpm_GetEvent (&ev);
1110 Gpm_FitEvent (&ev);
1111 *event = ev;
1112 return EV_MOUSE;
1114 #endif /* !HAVE_LIBGPM */
1116 #ifndef HAVE_SLANG
1117 flag = is_wintouched (stdscr);
1118 untouchwin (stdscr);
1119 #endif /* !HAVE_SLANG */
1120 c = block ? getch_with_delay () : get_key_code (1);
1122 #ifndef HAVE_SLANG
1123 if (flag)
1124 touchwin (stdscr);
1125 #endif /* !HAVE_SLANG */
1127 if (c == MCKEY_MOUSE
1128 #ifdef KEY_MOUSE
1129 || c == KEY_MOUSE
1130 #endif /* KEY_MOUSE */
1132 /* Mouse event */
1133 xmouse_get_event (event);
1134 if (event->type)
1135 return EV_MOUSE;
1136 else
1137 return EV_NONE;
1140 return c;
1143 /* Returns a key press, mouse events are discarded */
1144 int mi_getch ()
1146 Gpm_Event ev;
1147 int key;
1149 ev.x = -1;
1150 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
1152 return key;
1155 static int xgetch_second (void)
1157 fd_set Read_FD_Set;
1158 int c;
1159 struct timeval timeout;
1161 timeout.tv_sec = keyboard_key_timeout / 1000000;
1162 timeout.tv_usec = keyboard_key_timeout % 1000000;
1163 nodelay (stdscr, TRUE);
1164 FD_ZERO (&Read_FD_Set);
1165 FD_SET (input_fd, &Read_FD_Set);
1166 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1167 c = getch ();
1168 nodelay (stdscr, FALSE);
1169 return c;
1172 static void
1173 learn_store_key (char *buffer, char **p, int c)
1175 if (*p - buffer > 253)
1176 return;
1177 if (c == ESC_CHAR) {
1178 *(*p)++ = '\\';
1179 *(*p)++ = 'e';
1180 } else if (c < ' ') {
1181 *(*p)++ = '^';
1182 *(*p)++ = c + 'a' - 1;
1183 } else if (c == '^') {
1184 *(*p)++ = '^';
1185 *(*p)++ = '^';
1186 } else
1187 *(*p)++ = (char) c;
1190 char *learn_key (void)
1192 /* LEARN_TIMEOUT in usec */
1193 #define LEARN_TIMEOUT 200000
1195 fd_set Read_FD_Set;
1196 struct timeval endtime;
1197 struct timeval timeout;
1198 int c;
1199 char buffer [256];
1200 char *p = buffer;
1202 keypad(stdscr, FALSE); /* disable intepreting keys by ncurses */
1203 c = getch ();
1204 while (c == -1)
1205 c = getch (); /* Sanity check, should be unnecessary */
1206 learn_store_key (buffer, &p, c);
1207 GET_TIME (endtime);
1208 endtime.tv_usec += LEARN_TIMEOUT;
1209 if (endtime.tv_usec > 1000000) {
1210 endtime.tv_usec -= 1000000;
1211 endtime.tv_sec++;
1213 nodelay (stdscr, TRUE);
1214 for (;;) {
1215 while ((c = getch ()) == -1) {
1216 GET_TIME (timeout);
1217 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1218 if (timeout.tv_usec < 0)
1219 timeout.tv_sec++;
1220 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1221 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1222 FD_ZERO (&Read_FD_Set);
1223 FD_SET (input_fd, &Read_FD_Set);
1224 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1225 } else
1226 break;
1228 if (c == -1)
1229 break;
1230 learn_store_key (buffer, &p, c);
1232 keypad(stdscr, TRUE);
1233 nodelay (stdscr, FALSE);
1234 *p = 0;
1235 return g_strdup (buffer);
1238 /* xterm and linux console only: set keypad to numeric or application
1239 mode. Only in application keypad mode it's possible to distinguish
1240 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1241 void
1242 numeric_keypad_mode (void)
1244 if (console_flag || xterm_flag) {
1245 fputs ("\033>", stdout);
1246 fflush (stdout);
1250 void
1251 application_keypad_mode (void)
1253 if (console_flag || xterm_flag) {
1254 fputs ("\033=", stdout);
1255 fflush (stdout);
1261 * Check if we are idle, i.e. there are no pending keyboard or mouse
1262 * events. Return 1 is idle, 0 is there are pending events.
1265 is_idle (void)
1267 int maxfdp;
1268 fd_set select_set;
1269 struct timeval timeout;
1271 FD_ZERO (&select_set);
1272 FD_SET (input_fd, &select_set);
1273 maxfdp = input_fd;
1274 #ifdef HAVE_LIBGPM
1275 if (use_mouse_p == MOUSE_GPM && mouse_enabled && gpm_fd > 0) {
1276 FD_SET (gpm_fd, &select_set);
1277 maxfdp = max (maxfdp, gpm_fd);
1279 #endif
1280 timeout.tv_sec = 0;
1281 timeout.tv_usec = 0;
1282 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1287 * Get modifier state (shift, alt, ctrl) for the last key pressed.
1288 * We are assuming that the state didn't change since the key press.
1289 * This is only correct if get_modifier() is called very fast after
1290 * the input was received, so that the user didn't release the
1291 * modifier keys yet.
1293 static int
1294 get_modifier (void)
1296 int result = 0;
1297 #ifdef __QNXNTO__
1298 int mod_status, shift_ext_status;
1299 static int in_photon = 0;
1300 static int ph_ig = 0;
1301 PhCursorInfo_t cursor_info;
1302 #endif /* __QNXNTO__ */
1304 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1305 if (x11_window) {
1306 Window root, child;
1307 int root_x, root_y;
1308 int win_x, win_y;
1309 unsigned int mask;
1311 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
1312 &root_y, &win_x, &win_y, &mask);
1314 if (mask & ShiftMask)
1315 result |= KEY_M_SHIFT;
1316 if (mask & ControlMask)
1317 result |= KEY_M_CTRL;
1318 return result;
1320 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1321 #ifdef __QNXNTO__
1323 if (in_photon == 0) {
1324 /* First time here, let's load Photon library and attach
1325 to Photon */
1326 in_photon = -1;
1327 if (getenv ("PHOTON2_PATH") != NULL) {
1328 /* QNX 6.x has no support for RTLD_LAZY */
1329 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
1330 if (ph_handle != NULL) {
1331 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
1332 ph_input_group =
1333 (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
1334 ph_query_cursor =
1335 (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
1336 if ((ph_attach != NULL) && (ph_input_group != NULL)
1337 && (ph_query_cursor != NULL)) {
1338 if ((*ph_attach) (0, 0)) { /* Attached */
1339 ph_ig = (*ph_input_group) (0);
1340 in_photon = 1;
1346 /* We do not have Photon running. Assume we are in text
1347 console or xterm */
1348 if (in_photon == -1) {
1349 if (devctl
1350 (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status,
1351 sizeof (int), NULL) == -1)
1352 return 0;
1353 shift_ext_status = mod_status & 0xffffff00UL;
1354 mod_status &= 0x7f;
1355 if (mod_status & _LINESTATUS_CON_ALT)
1356 result |= KEY_M_ALT;
1357 if (mod_status & _LINESTATUS_CON_CTRL)
1358 result |= KEY_M_CTRL;
1359 if ((mod_status & _LINESTATUS_CON_SHIFT)
1360 || (shift_ext_status & 0x00000800UL))
1361 result |= KEY_M_SHIFT;
1362 } else {
1363 (*ph_query_cursor) (ph_ig, &cursor_info);
1364 if (cursor_info.key_mods & 0x04)
1365 result |= KEY_M_ALT;
1366 if (cursor_info.key_mods & 0x02)
1367 result |= KEY_M_CTRL;
1368 if (cursor_info.key_mods & 0x01)
1369 result |= KEY_M_SHIFT;
1371 #endif /* __QNXNTO__ */
1373 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
1375 unsigned char modifiers = 6;
1377 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
1378 return 0;
1380 /* Translate Linux modifiers into mc modifiers */
1381 if (modifiers & SHIFT_PRESSED)
1382 result |= KEY_M_SHIFT;
1383 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
1384 result |= KEY_M_ALT;
1385 if (modifiers & CONTROL_PRESSED)
1386 result |= KEY_M_CTRL;
1388 #endif /* !__linux__ */
1389 return result;
1392 static void k_dispose (key_def *k)
1394 if (!k)
1395 return;
1396 k_dispose (k->child);
1397 k_dispose (k->next);
1398 g_free (k);
1401 static void s_dispose (SelectList *sel)
1403 if (!sel)
1404 return;
1406 s_dispose (sel->next);
1407 g_free (sel);
1410 void done_key ()
1412 k_dispose (keys);
1413 s_dispose (select_list);
1415 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1416 if (x11_display)
1417 mc_XCloseDisplay (x11_display);
1418 #endif