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.
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. */
33 #include <sys/types.h>
40 #include "layout.h" /* winch_flag */
43 #include "cons.saver.h"
46 #include "../vfs/gc.h"
49 #ifdef HAVE_TEXTMODE_X11_SUPPORT
54 # if defined(__GLIBC__) && (__GLIBC__ < 2)
55 # include <linux/termios.h> /* TIOCLINUX */
59 # include <sys/ioctl.h>
60 #endif /* __linux__ */
64 # include <sys/ioctl.h>
65 #endif /* __CYGWIN__ */
70 # include <sys/dcmd_chr.h>
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;
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 */
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
101 /* This holds all the key definitions */
102 static key_def
*keys
= NULL
;
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
{
114 struct SelectList
*next
;
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
*);
122 ph_ov_f ph_input_group
;
123 ph_pqc_f ph_query_cursor
;
126 static SelectList
*select_list
= NULL
;
128 void add_select_channel (int fd
, select_fn callback
, void *info
)
132 new = g_new (SelectList
, 1);
134 new->callback
= callback
;
136 new->next
= select_list
;
140 void delete_select_channel (int fd
)
142 SelectList
*p
= select_list
;
143 SelectList
*p_prev
= NULL
;
151 p_prev
->next
= p_next
;
153 select_list
= p_next
;
165 inline static int add_selects (fd_set
*select_set
)
170 if (disabled_channels
)
173 for (p
= select_list
; p
; p
= p
->next
) {
174 FD_SET (p
->fd
, select_set
);
181 static void check_selects (fd_set
*select_set
)
186 if (disabled_channels
)
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
);
201 void channels_down (void)
206 void channels_up (void)
208 if (!disabled_channels
)
209 fputs ("Error: channels_up called with disabled_channels = 0\n",
214 typedef const struct {
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
},
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
},
317 { KEY_M_SHIFT
| KEY_PPAGE
, ESC_STR
"[5;2~", MCKEY_NOACTION
},
318 { KEY_M_SHIFT
| KEY_NPAGE
, ESC_STR
"[6;2~", MCKEY_NOACTION
},
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
},
408 define_sequences (key_define_t
*kd
)
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
;
424 if (!getenv ("DISPLAY"))
427 x11_display
= mc_XOpenDisplay (0);
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 */
440 const char *term
= getenv ("TERM");
441 char *kt
= getenv ("KEYBOARD_KEY_TIMEOUT_US");
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 */
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 ();
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;
480 #ifdef HAVE_TEXTMODE_X11_SUPPORT
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)
495 input_fd
= SLang_TT_Read_FD
;
501 xmouse_get_event (Gpm_Event
*ev
)
504 static struct timeval tv1
= { 0, 0 }; /* Force first click as single */
505 static struct timeval tv2
;
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 */
515 /* There seems to be no way of knowing which button was released */
516 /* So we assume all the buttons were released */
520 ev
->type
= GPM_UP
| (GPM_SINGLE
<< clicks
);
526 /* Bogus event, maybe mouse wheel */
530 if (btn
>= 32 && btn
<= 34) {
537 if (tv1
.tv_sec
&& (DIF_TIME (tv1
,tv2
) < double_click_speed
)) {
545 ev
->buttons
= GPM_B_LEFT
;
548 ev
->buttons
= GPM_B_MIDDLE
;
551 ev
->buttons
= GPM_B_RIGHT
;
554 ev
->buttons
= GPM_B_UP
;
558 ev
->buttons
= GPM_B_DOWN
;
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);
582 if (attach
) attach
->child
= p
;
586 p
->child
= p
->next
= NULL
;
590 p
->action
= MCKEY_NOACTION
;
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
)
604 seq_append
= seq_buffer
;
606 if (seq_append
== &(seq_buffer
[SEQ_BUFFER_LEN
-2]))
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
)
621 if (strlen (seq
) > SEQ_BUFFER_LEN
-1)
624 for (base
= keys
; (base
!= 0) && *seq
; ) {
625 if (*seq
== base
->ch
) {
626 if (base
->child
== 0) {
628 base
->child
= create_sequence (seq
+1, code
, action
);
631 /* The sequence matches an existing one. */
633 base
->action
= action
;
644 base
->next
= create_sequence (seq
, code
, action
);
651 /* Attempt to redefine a sequence with a shorter sequence. */
655 keys
= create_sequence (seq
, code
, action
);
659 static int *pending_keys
;
661 /* Apply corrections for the keycode generated in get_key_code() */
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 */
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 */
684 /* This is reported to be useful on AIX */
685 if (c
== KEY_SCANCEL
)
688 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
689 if ((c
== '\t') && (mod
& (KEY_M_SHIFT
| KEY_M_CTRL
))) {
694 /* F0 is the same as F10 for out purposes */
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') {
711 if ((c
== 127) && (mod
==0)) /* Add Ctrl/Alt/Shift-BackSpace */
713 mod
|= get_modifier();
717 if ((c
=='0') && (mod
==0)) /* Add Shift-Insert on key pad */
719 if ((qmod
& KEY_M_SHIFT
) == KEY_M_SHIFT
)
726 if ((c
=='.') && (mod
==0)) /* Add Shift-Del on key pad */
728 if ((qmod
& KEY_M_SHIFT
) == KEY_M_SHIFT
)
734 #endif /* __QNXNTO__ */
736 /* Unrecognized 0177 is delete (preserve Ctrl) */
741 /* Unrecognized Ctrl-d is delete */
742 if (c
== (31 & 'd')) {
747 /* Unrecognized Ctrl-h is backspace */
748 if (c
== (31 & 'h')) {
753 /* Shift+BackSpace is backspace */
754 if (c
== KEY_BACKSPACE
&& (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
)) {
763 /* Remove Shift information from function keys */
764 if (c
>= KEY_F (1) && c
<= KEY_F (20)) {
768 if (!alternate_plus_minus
)
773 case KEY_KP_SUBTRACT
:
776 case KEY_KP_MULTIPLY
:
784 int get_key_code (int no_delay
)
787 static key_def
*this = NULL
, *parent
;
788 static struct timeval esctime
= { -1, -1 };
789 static int lastnodelay
= -1;
791 if (no_delay
!= lastnodelay
) {
793 lastnodelay
= no_delay
;
798 int d
= *pending_keys
++;
800 if (!*pending_keys
) {
804 if (d
== ESC_CHAR
&& pending_keys
) {
805 d
= ALT(*pending_keys
++);
808 if ((d
> 127 && d
< 256) && use_8th_bit_as_meta
)
811 return correct_key_code (d
);
816 nodelay (stdscr
, TRUE
);
819 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
821 goto nodelay_try_again
;
824 nodelay (stdscr
, FALSE
);
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)
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;
839 if (current
.tv_sec
< timeout
.tv_sec
)
841 if (current
.tv_sec
== timeout
.tv_sec
&&
842 current
.tv_usec
< timeout
.tv_usec
)
845 pending_keys
= seq_append
= NULL
;
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. */
855 pending_keys
= seq_buffer
;
862 /* Search the key on the root */
863 if (!no_delay
|| this == NULL
) {
867 if ((c
> 127 && c
< 256) && use_8th_bit_as_meta
) {
870 /* The first sequence defined starts with esc */
878 if (!push_char (c
)) {
879 pending_keys
= seq_buffer
;
884 if (parent
->action
== MCKEY_ESCAPE
&& old_esc_mode
) {
888 /* Shouldn't happen */
889 fputs ("Internal error\n", stderr
);
892 goto nodelay_try_again
;
895 c
= xgetch_second ();
897 pending_keys
= seq_append
= NULL
;
903 goto nodelay_try_again
;
907 /* We got a complete match, return and reset search */
910 pending_keys
= seq_append
= NULL
;
913 return correct_key_code (code
);
919 if (parent
!= NULL
&& parent
->action
== MCKEY_ESCAPE
) {
921 /* Convert escape-digits to F-keys */
929 pending_keys
= seq_append
= 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
937 pending_keys
= seq_buffer
;
943 return correct_key_code (c
);
946 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
948 try_channels (int set_timeout
)
950 struct timeval timeout
;
951 static fd_set select_set
;
952 struct timeval
*timeptr
;
957 FD_ZERO (&select_set
);
958 FD_SET (input_fd
, &select_set
); /* Add stdin */
959 maxfdp
= max (add_selects (&select_set
), input_fd
);
963 timeout
.tv_usec
= 100000;
968 v
= select (maxfdp
+ 1, &select_set
, NULL
, NULL
, timeptr
);
970 check_selects (&select_set
);
971 if (FD_ISSET (input_fd
, &select_set
))
977 /* Workaround for System V Curses vt100 bug */
978 static int getch_with_delay (void)
982 /* This routine could be used on systems without mouse support,
983 so we need to do the select check :-( */
988 /* Try to get a character */
989 c
= get_key_code (0);
992 /* Failed -> wait 0.1 secs and try again */
995 /* Success -> return the character */
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
)
1007 static int flag
; /* Return value from select */
1009 static struct Gpm_Event ev
; /* Mouse event */
1011 struct timeval timeout
;
1012 struct timeval
*time_addr
= NULL
;
1013 static int dirty
= 3;
1015 if ((dirty
== 3) || is_idle ()) {
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
1029 show_mouse_pointer (event
->x
, event
->y
);
1034 /* Repeat if using mouse */
1035 while (mouse_enabled
&& !pending_keys
) {
1039 FD_ZERO (&select_set
);
1040 FD_SET (input_fd
, &select_set
);
1041 maxfdp
= max (add_selects (&select_set
), input_fd
);
1044 if (use_mouse_p
== MOUSE_GPM
) {
1046 /* Connection to gpm broken, possibly gpm has died */
1048 use_mouse_p
= MOUSE_NONE
;
1051 FD_SET (gpm_fd
, &select_set
);
1052 maxfdp
= max (maxfdp
, gpm_fd
);
1058 timeout
.tv_usec
= mou_auto_repeat
* 1000;
1061 time_addr
= &timeout
;
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
;
1078 if (!block
|| winch_flag
) {
1079 time_addr
= &timeout
;
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.
1095 if (!block
|| winch_flag
)
1097 vfs_timeout_handler ();
1099 if (flag
== -1 && errno
== EINTR
)
1102 check_selects (&select_set
);
1104 if (FD_ISSET (input_fd
, &select_set
))
1107 if (use_mouse_p
== MOUSE_GPM
&& gpm_fd
> 0
1108 && FD_ISSET (gpm_fd
, &select_set
)) {
1114 #endif /* !HAVE_LIBGPM */
1117 flag
= is_wintouched (stdscr
);
1118 untouchwin (stdscr
);
1119 #endif /* !HAVE_SLANG */
1120 c
= block
? getch_with_delay () : get_key_code (1);
1125 #endif /* !HAVE_SLANG */
1127 if (c
== MCKEY_MOUSE
1130 #endif /* KEY_MOUSE */
1133 xmouse_get_event (event
);
1143 /* Returns a key press, mouse events are discarded */
1150 while ((key
= get_event (&ev
, 0, 1)) == EV_NONE
)
1155 static int xgetch_second (void)
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
);
1168 nodelay (stdscr
, FALSE
);
1173 learn_store_key (char *buffer
, char **p
, int c
)
1175 if (*p
- buffer
> 253)
1177 if (c
== ESC_CHAR
) {
1180 } else if (c
< ' ') {
1182 *(*p
)++ = c
+ 'a' - 1;
1183 } else if (c
== '^') {
1190 char *learn_key (void)
1192 /* LEARN_TIMEOUT in usec */
1193 #define LEARN_TIMEOUT 200000
1196 struct timeval endtime
;
1197 struct timeval timeout
;
1202 keypad(stdscr
, FALSE
); /* disable intepreting keys by ncurses */
1205 c
= getch (); /* Sanity check, should be unnecessary */
1206 learn_store_key (buffer
, &p
, c
);
1208 endtime
.tv_usec
+= LEARN_TIMEOUT
;
1209 if (endtime
.tv_usec
> 1000000) {
1210 endtime
.tv_usec
-= 1000000;
1213 nodelay (stdscr
, TRUE
);
1215 while ((c
= getch ()) == -1) {
1217 timeout
.tv_usec
= endtime
.tv_usec
- timeout
.tv_usec
;
1218 if (timeout
.tv_usec
< 0)
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
);
1230 learn_store_key (buffer
, &p
, c
);
1232 keypad(stdscr
, TRUE
);
1233 nodelay (stdscr
, FALSE
);
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)*/
1242 numeric_keypad_mode (void)
1244 if (console_flag
|| xterm_flag
) {
1245 fputs ("\033>", stdout
);
1251 application_keypad_mode (void)
1253 if (console_flag
|| xterm_flag
) {
1254 fputs ("\033=", 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.
1269 struct timeval timeout
;
1271 FD_ZERO (&select_set
);
1272 FD_SET (input_fd
, &select_set
);
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
);
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.
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
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
;
1320 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1323 if (in_photon
== 0) {
1324 /* First time here, let's load Photon library and attach
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");
1333 (ph_ov_f
) dlsym (ph_handle
, "PhInputGroup");
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);
1346 /* We do not have Photon running. Assume we are in text
1348 if (in_photon
== -1) {
1350 (fileno (stdin
), DCMD_CHR_LINESTATUS
, &mod_status
,
1351 sizeof (int), NULL
) == -1)
1353 shift_ext_status
= mod_status
& 0xffffff00UL
;
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
;
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)
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__ */
1392 static void k_dispose (key_def
*k
)
1396 k_dispose (k
->child
);
1397 k_dispose (k
->next
);
1401 static void s_dispose (SelectList
*sel
)
1406 s_dispose (sel
->next
);
1413 s_dispose (select_list
);
1415 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1417 mc_XCloseDisplay (x11_display
);