Update base version to 4.6.1.
[midnight-commander.git] / src / key.c
blob9a138658b41f07f117cfb38c5347b8767572552f
1 /* Keyboard support routines.
3 Copyright (C) 1994,1995 the Free Software Foundation.
5 Written by: 1994, 1995 Miguel de Icaza.
6 1994, 1995 Janne Kukonlehto.
7 1995 Jakub Jelinek.
8 1997 Norbert Warmuth
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include <config.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include <sys/types.h>
33 #include <unistd.h>
35 #include "global.h"
36 #include "tty.h"
37 #include "mouse.h"
38 #include "key.h"
39 #include "main.h"
40 #include "win.h"
41 #include "cons.saver.h"
43 #ifdef USE_VFS
44 #include "../vfs/gc.h"
45 #endif
47 #ifdef HAVE_TEXTMODE_X11_SUPPORT
48 # include "x11conn.h"
49 #endif
51 #ifdef __linux__
52 # if defined(__GLIBC__) && (__GLIBC__ < 2)
53 # include <linux/termios.h> /* TIOCLINUX */
54 # else
55 # include <termios.h>
56 # endif
57 # include <sys/ioctl.h>
58 #endif /* __linux__ */
60 #ifdef __CYGWIN__
61 # include <termios.h>
62 # include <sys/ioctl.h>
63 #endif /* __CYGWIN__ */
65 #ifdef __QNXNTO__
66 # include <dlfcn.h>
67 # include <Ph.h>
68 # include <sys/dcmd_chr.h>
69 #endif
71 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
72 #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \
73 (t2.tv_usec-t1.tv_usec)/1000)
75 /* timeout for old_esc_mode in usec */
76 #define ESCMODE_TIMEOUT 1000000
78 /* Linux console keyboard modifiers */
79 #define SHIFT_PRESSED 1
80 #define ALTR_PRESSED 2
81 #define CONTROL_PRESSED 4
82 #define ALTL_PRESSED 8
85 int mou_auto_repeat = 100;
86 int double_click_speed = 250;
87 int old_esc_mode = 0;
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 = 0;
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 = 0;
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 = 0;
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;
185 if (disabled_channels)
186 return;
188 for (p = select_list; p; p = p->next)
189 if (FD_ISSET (p->fd, select_set))
190 (*p->callback)(p->fd, p->info);
193 void channels_down (void)
195 disabled_channels ++;
198 void channels_up (void)
200 if (!disabled_channels)
201 fputs ("Error: channels_up called with disabled_channels = 0\n",
202 stderr);
203 disabled_channels--;
206 typedef const struct {
207 int code;
208 const char *seq;
209 int action;
210 } key_define_t;
212 /* Broken terminfo and termcap databases on xterminals */
213 static key_define_t xterm_key_defines [] = {
214 { KEY_F(1), ESC_STR "OP", MCKEY_NOACTION },
215 { KEY_F(2), ESC_STR "OQ", MCKEY_NOACTION },
216 { KEY_F(3), ESC_STR "OR", MCKEY_NOACTION },
217 { KEY_F(4), ESC_STR "OS", MCKEY_NOACTION },
218 { KEY_F(1), ESC_STR "[11~", MCKEY_NOACTION },
219 { KEY_F(2), ESC_STR "[12~", MCKEY_NOACTION },
220 { KEY_F(3), ESC_STR "[13~", MCKEY_NOACTION },
221 { KEY_F(4), ESC_STR "[14~", MCKEY_NOACTION },
222 { KEY_F(5), ESC_STR "[15~", MCKEY_NOACTION },
223 { KEY_F(6), ESC_STR "[17~", MCKEY_NOACTION },
224 { KEY_F(7), ESC_STR "[18~", MCKEY_NOACTION },
225 { KEY_F(8), ESC_STR "[19~", MCKEY_NOACTION },
226 { KEY_F(9), ESC_STR "[20~", MCKEY_NOACTION },
227 { KEY_F(10), ESC_STR "[21~", MCKEY_NOACTION },
229 /* old xterm Shift-arrows */
230 { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
231 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
232 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
233 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
235 /* new xterm Shift-arrows */
236 { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
237 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
238 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
239 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
241 /* more xterm keys with modifiers */
242 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
243 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
244 { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
245 { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
246 { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
247 { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
248 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
249 { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
250 { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
251 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
252 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
253 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
254 { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
255 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
256 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
257 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
258 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
259 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
261 /* rxvt keys with modifiers */
262 { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
263 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
264 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
265 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
266 { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
267 { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
268 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
269 { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
270 { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
271 { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
272 { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
273 { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
274 { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
275 { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
276 { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
277 { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
278 { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
280 /* konsole keys with modifiers */
281 { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
282 { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
284 /* gnome-terminal */
285 { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
286 { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
287 { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
288 { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
289 { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
290 { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
291 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
292 { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
293 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
294 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
295 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
296 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
298 /* gnome-terminal - application mode */
299 { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
300 { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
301 { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
302 { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
303 { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
304 { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
305 { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
306 { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
308 /* keypad keys */
309 { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
310 { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
311 { '/', ESC_STR "Oo", MCKEY_NOACTION },
312 { '\n', ESC_STR "OM", MCKEY_NOACTION },
314 { 0, 0, MCKEY_NOACTION },
317 /* qansi-m terminals have a much more key combinatios,
318 which are undefined in termcap/terminfo */
319 static key_define_t qansi_key_defines[] =
321 /* qansi-m terminal */
322 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
323 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
324 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
325 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
326 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
327 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
328 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
329 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
330 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
331 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
332 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
333 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
334 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
335 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
336 {KEY_M_CTRL | KEY_F(1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
337 {KEY_M_CTRL | KEY_F(2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
338 {KEY_M_CTRL | KEY_F(3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
339 {KEY_M_CTRL | KEY_F(4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
340 {KEY_M_CTRL | KEY_F(5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
341 {KEY_M_CTRL | KEY_F(6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
342 {KEY_M_CTRL | KEY_F(7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
343 {KEY_M_CTRL | KEY_F(8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
344 {KEY_M_CTRL | KEY_F(9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
345 {KEY_M_CTRL | KEY_F(10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
346 {KEY_M_CTRL | KEY_F(11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
347 {KEY_M_CTRL | KEY_F(12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
348 {KEY_M_ALT | KEY_F(1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
349 {KEY_M_ALT | KEY_F(2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
350 {KEY_M_ALT | KEY_F(3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
351 {KEY_M_ALT | KEY_F(4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
352 {KEY_M_ALT | KEY_F(5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
353 {KEY_M_ALT | KEY_F(6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
354 {KEY_M_ALT | KEY_F(7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
355 {KEY_M_ALT | KEY_F(8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
356 {KEY_M_ALT | KEY_F(9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
357 {KEY_M_ALT | KEY_F(10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
358 {KEY_M_ALT | KEY_F(11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
359 {KEY_M_ALT | KEY_F(12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
360 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
361 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
362 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
363 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
364 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
365 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
366 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
367 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
368 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
369 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
370 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
371 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
372 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
373 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
374 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
375 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-r */
376 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
377 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
378 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
379 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
380 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
381 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
382 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
383 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
384 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
385 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
386 {0, 0, MCKEY_NOACTION},
389 static key_define_t mc_default_keys [] = {
390 { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
391 { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
392 { 0, 0, MCKEY_NOACTION },
395 static void
396 define_sequences (key_define_t *kd)
398 int i;
400 for (i = 0; kd [i].code; i++)
401 define_sequence(kd [i].code, kd [i].seq, kd [i].action);
404 #ifdef HAVE_TEXTMODE_X11_SUPPORT
406 static Display *x11_display;
407 static Window x11_window;
409 static void
410 init_key_x11 (void)
412 if (!getenv ("DISPLAY"))
413 return;
415 x11_display = mc_XOpenDisplay (0);
417 if (x11_display)
418 x11_window = DefaultRootWindow (x11_display);
420 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
423 /* This has to be called before slang_init or whatever routine
424 calls any define_sequence */
425 void
426 init_key (void)
428 const char *term = getenv ("TERM");
430 /* This has to be the first define_sequence */
431 /* So, we can assume that the first keys member has ESC */
432 define_sequences (mc_default_keys);
434 /* Terminfo on irix does not have some keys */
435 if (xterm_flag
436 || (term != NULL
437 && (strncmp (term, "iris-ansi", 9) == 0
438 || strncmp (term, "xterm", 5) == 0
439 || strncmp (term, "rxvt", 4) == 0
440 || strcmp (term, "screen") == 0)))
441 define_sequences (xterm_key_defines);
443 /* load some additional keys (e.g. direct Alt-? support) */
444 load_xtra_key_defines ();
446 #ifdef __QNX__
447 if (term && strncmp (term, "qnx", 3) == 0) {
448 /* Modify the default value of use_8th_bit_as_meta: we would
449 * like to provide a working mc for a newbie who knows nothing
450 * about [Options|Display bits|Full 8 bits input]...
452 * Don't use 'meta'-bit, when we are dealing with a
453 * 'qnx*'-type terminal: clear the default value!
454 * These terminal types use 0xFF as an escape character,
455 * so use_8th_bit_as_meta==1 must not be enabled!
457 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
458 * is not used now (doesn't even depend on use_8th_bit_as_meta
459 * as in mc-3.1.2)...GREAT!...no additional code is required!]
461 use_8th_bit_as_meta = 0;
463 #endif /* __QNX__ */
465 #ifdef HAVE_TEXTMODE_X11_SUPPORT
466 init_key_x11 ();
467 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
469 /* Load the qansi-m key definitions
470 if we are running under the qansi-m terminal */
471 if ((term) && (strncmp (term, "qansi-m", 7) == 0)) {
472 define_sequences (qansi_key_defines);
476 /* This has to be called after SLang_init_tty/slint_init */
477 void init_key_input_fd (void)
479 #ifdef HAVE_SLANG
480 input_fd = SLang_TT_Read_FD;
481 #endif
485 static void
486 xmouse_get_event (Gpm_Event *ev)
488 int btn;
489 static struct timeval tv1 = { 0, 0 }; /* Force first click as single */
490 static struct timeval tv2;
491 static int clicks;
492 static int last_btn = 0;
494 /* Decode Xterm mouse information to a GPM style event */
496 /* Variable btn has following meaning: */
497 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
498 btn = getch () - 32;
500 /* There seems to be no way of knowing which button was released */
501 /* So we assume all the buttons were released */
503 if (btn == 3){
504 if (last_btn) {
505 ev->type = GPM_UP | (GPM_SINGLE << clicks);
506 ev->buttons = 0;
507 last_btn = 0;
508 GET_TIME (tv1);
509 clicks = 0;
510 } else {
511 /* Bogus event, maybe mouse wheel */
512 ev->type = 0;
514 } else {
515 ev->type = GPM_DOWN;
516 GET_TIME (tv2);
517 if (tv1.tv_sec && (DIF_TIME (tv1,tv2) < double_click_speed)){
518 clicks++;
519 clicks %= 3;
520 } else
521 clicks = 0;
523 switch (btn) {
524 case 0:
525 ev->buttons = GPM_B_LEFT;
526 break;
527 case 1:
528 ev->buttons = GPM_B_MIDDLE;
529 break;
530 case 2:
531 ev->buttons = GPM_B_RIGHT;
532 break;
533 case 64:
534 ev->buttons = GPM_B_UP;
535 break;
536 case 65:
537 ev->buttons = GPM_B_DOWN;
538 break;
539 default:
540 /* Nothing */
541 ev->type = 0;
542 ev->buttons = 0;
543 break;
545 last_btn = ev->buttons;
547 /* Coordinates are 33-based */
548 /* Transform them to 1-based */
549 ev->x = getch () - 32;
550 ev->y = getch () - 32;
553 static key_def *create_sequence (const char *seq, int code, int action)
555 key_def *base, *p, *attach;
557 for (base = attach = NULL; *seq; seq++){
558 p = g_new (key_def, 1);
559 if (!base) base = p;
560 if (attach) attach->child = p;
562 p->ch = *seq;
563 p->code = code;
564 p->child = p->next = NULL;
565 if (!seq[1])
566 p->action = action;
567 else
568 p->action = MCKEY_NOACTION;
569 attach = p;
571 return base;
574 /* The maximum sequence length (32 + null terminator) */
575 #define SEQ_BUFFER_LEN 33
576 static int seq_buffer [SEQ_BUFFER_LEN];
577 static int *seq_append = 0;
579 static int push_char (int c)
581 if (!seq_append)
582 seq_append = seq_buffer;
584 if (seq_append == &(seq_buffer [SEQ_BUFFER_LEN-2]))
585 return 0;
586 *(seq_append++) = c;
587 *seq_append = 0;
588 return 1;
592 * Return 1 on success, 0 on error.
593 * An error happens if SEQ is a beginning of an existing longer sequence.
595 int define_sequence (int code, const char *seq, int action)
597 key_def *base;
599 if (strlen (seq) > SEQ_BUFFER_LEN-1)
600 return 0;
602 for (base = keys; (base != 0) && *seq; ){
603 if (*seq == base->ch){
604 if (base->child == 0){
605 if (*(seq+1)){
606 base->child = create_sequence (seq+1, code, action);
607 return 1;
608 } else {
609 /* The sequence matches an existing one. */
610 base->code = code;
611 base->action = action;
612 return 1;
614 } else {
615 base = base->child;
616 seq++;
618 } else {
619 if (base->next)
620 base = base->next;
621 else {
622 base->next = create_sequence (seq, code, action);
623 return 1;
628 if (!*seq) {
629 /* Attempt to redefine a sequence with a shorter sequence. */
630 return 0;
633 keys = create_sequence (seq, code, action);
634 return 1;
637 static int *pending_keys;
639 /* Apply corrections for the keycode generated in get_key_code() */
640 static int
641 correct_key_code (int code)
643 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
644 unsigned int mod = code & KEY_M_MASK; /* modifier */
645 #ifdef __QNXNTO__
646 unsigned int qmod; /* bunch of the QNX console
647 modifiers needs unchanged */
648 #endif /* __QNXNTO__ */
651 * Add key modifiers directly from X11 or OS.
652 * Ordinary characters only get modifiers from sequences.
654 if (c < 32 || c >= 256) {
655 mod |= get_modifier ();
658 /* This is needed if the newline is reported as carriage return */
659 if (c == '\r')
660 c = '\n';
662 /* This is reported to be useful on AIX */
663 if (c == KEY_SCANCEL)
664 c = '\t';
666 /* Convert Shift+Tab and Ctrl+Tab to Back Tab */
667 if ((c == '\t') && (mod & (KEY_M_SHIFT | KEY_M_CTRL))) {
668 c = KEY_BTAB;
669 mod = 0;
672 /* F0 is the same as F10 for out purposes */
673 if (c == KEY_F (0))
674 c = KEY_F (10);
677 * We are not interested if Ctrl was pressed when entering control
678 * characters, so assume that it was. When checking for such keys,
679 * XCTRL macro should be used. In some cases, we are interested,
680 * e.g. to distinguish Ctrl-Enter from Enter.
682 if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') {
683 mod |= KEY_M_CTRL;
686 #ifdef __QNXNTO__
687 qmod=get_modifier();
689 if ((c == 127) && (mod==0)) /* Add Ctrl/Alt/Shift-BackSpace */
691 mod |= get_modifier();
692 c = KEY_BACKSPACE;
695 if ((c=='0') && (mod==0)) /* Add Shift-Insert on key pad */
697 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
699 mod = KEY_M_SHIFT;
700 c = KEY_IC;
704 if ((c=='.') && (mod==0)) /* Add Shift-Del on key pad */
706 if ((qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
708 mod = KEY_M_SHIFT;
709 c = KEY_DC;
712 #endif /* __QNXNTO__ */
714 /* Unrecognized 0177 is delete (preserve Ctrl) */
715 if (c == 0177) {
716 c = KEY_BACKSPACE;
719 /* Unrecognized Ctrl-d is delete */
720 if (c == (31 & 'd')) {
721 c = KEY_DC;
722 mod &= ~KEY_M_CTRL;
725 /* Unrecognized Ctrl-h is backspace */
726 if (c == (31 & 'h')) {
727 c = KEY_BACKSPACE;
728 mod &= ~KEY_M_CTRL;
731 /* Shift+BackSpace is backspace */
732 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT)) {
733 mod &= ~KEY_M_SHIFT;
736 /* Convert Shift+Fn to F(n+10) */
737 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT)) {
738 c += 10;
741 /* Remove Shift information from function keys */
742 if (c >= KEY_F (1) && c <= KEY_F (20)) {
743 mod &= ~KEY_M_SHIFT;
746 if (!alternate_plus_minus)
747 switch (c) {
748 case KEY_KP_ADD:
749 c = '+';
750 break;
751 case KEY_KP_SUBTRACT:
752 c = '-';
753 break;
754 case KEY_KP_MULTIPLY:
755 c = '*';
756 break;
759 return (mod | c);
762 int get_key_code (int no_delay)
764 int c;
765 static key_def *this = NULL, *parent;
766 static struct timeval esctime = { -1, -1 };
767 static int lastnodelay = -1;
769 if (no_delay != lastnodelay) {
770 this = NULL;
771 lastnodelay = no_delay;
774 pend_send:
775 if (pending_keys){
776 int d = *pending_keys++;
777 check_pend:
778 if (!*pending_keys){
779 pending_keys = 0;
780 seq_append = 0;
782 if (d == ESC_CHAR && pending_keys){
783 d = ALT(*pending_keys++);
784 goto check_pend;
786 if ((d & 0x80) && use_8th_bit_as_meta)
787 d = ALT(d & 0x7f);
788 this = NULL;
789 return correct_key_code (d);
792 nodelay_try_again:
793 if (no_delay) {
794 nodelay (stdscr, TRUE);
796 c = getch ();
797 #if defined(USE_NCURSES) && defined(KEY_RESIZE)
798 if (c == KEY_RESIZE)
799 goto nodelay_try_again;
800 #endif
801 if (no_delay) {
802 nodelay (stdscr, FALSE);
803 if (c == -1) {
804 if (this != NULL && parent != NULL &&
805 parent->action == MCKEY_ESCAPE && old_esc_mode) {
806 struct timeval current, timeout;
808 if (esctime.tv_sec == -1)
809 return -1;
810 GET_TIME (current);
811 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000 + esctime.tv_sec;
812 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000 + esctime.tv_usec;
813 if (timeout.tv_usec > 1000000) {
814 timeout.tv_usec -= 1000000;
815 timeout.tv_sec++;
817 if (current.tv_sec < timeout.tv_sec)
818 return -1;
819 if (current.tv_sec == timeout.tv_sec &&
820 current.tv_usec < timeout.tv_usec)
821 return -1;
822 this = NULL;
823 pending_keys = seq_append = NULL;
824 return ESC_CHAR;
826 return -1;
828 } else if (c == -1){
829 /* Maybe we got an incomplete match.
830 This we do only in delay mode, since otherwise
831 getch can return -1 at any time. */
832 if (seq_append) {
833 pending_keys = seq_buffer;
834 goto pend_send;
836 this = NULL;
837 return -1;
840 /* Search the key on the root */
841 if (!no_delay || this == NULL) {
842 this = keys;
843 parent = NULL;
845 if ((c & 0x80) && use_8th_bit_as_meta) {
846 c &= 0x7f;
848 /* The first sequence defined starts with esc */
849 parent = keys;
850 this = keys->child;
853 while (this){
854 if (c == this->ch){
855 if (this->child){
856 if (!push_char (c)){
857 pending_keys = seq_buffer;
858 goto pend_send;
860 parent = this;
861 this = this->child;
862 if (parent->action == MCKEY_ESCAPE && old_esc_mode) {
863 if (no_delay) {
864 GET_TIME (esctime);
865 if (this == NULL) {
866 /* Shouldn't happen */
867 fputs ("Internal error\n", stderr);
868 exit (1);
870 goto nodelay_try_again;
872 esctime.tv_sec = -1;
873 c = xgetch_second ();
874 if (c == -1) {
875 pending_keys = seq_append = NULL;
876 this = NULL;
877 return ESC_CHAR;
879 } else {
880 if (no_delay)
881 goto nodelay_try_again;
882 c = getch ();
884 } else {
885 /* We got a complete match, return and reset search */
886 int code;
888 pending_keys = seq_append = NULL;
889 code = this->code;
890 this = NULL;
891 return correct_key_code (code);
893 } else {
894 if (this->next)
895 this = this->next;
896 else {
897 if (parent != NULL && parent->action == MCKEY_ESCAPE) {
899 /* Convert escape-digits to F-keys */
900 if (isdigit(c))
901 c = KEY_F (c - '0');
902 else if (c == ' ')
903 c = ESC_CHAR;
904 else
905 c = ALT(c);
907 pending_keys = seq_append = NULL;
908 this = NULL;
909 return correct_key_code (c);
911 /* Did not find a match or {c} was changed in the if above,
912 so we have to return everything we had skipped
914 push_char (c);
915 pending_keys = seq_buffer;
916 goto pend_send;
920 this = NULL;
921 return correct_key_code (c);
924 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
925 static void
926 try_channels (int set_timeout)
928 struct timeval timeout;
929 static fd_set select_set;
930 struct timeval *timeptr;
931 int v;
932 int maxfdp;
934 while (1){
935 FD_ZERO (&select_set);
936 FD_SET (input_fd, &select_set); /* Add stdin */
937 maxfdp = max (add_selects (&select_set), input_fd);
939 if (set_timeout){
940 timeout.tv_sec = 0;
941 timeout.tv_usec = 100000;
942 timeptr = &timeout;
943 } else
944 timeptr = 0;
946 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
947 if (v > 0){
948 check_selects (&select_set);
949 if (FD_ISSET (input_fd, &select_set))
950 return;
955 /* Workaround for System V Curses vt100 bug */
956 static int getch_with_delay (void)
958 int c;
960 /* This routine could be used on systems without mouse support,
961 so we need to do the select check :-( */
962 while (1){
963 if (!pending_keys)
964 try_channels (0);
966 /* Try to get a character */
967 c = get_key_code (0);
968 if (c != -1)
969 break;
970 /* Failed -> wait 0.1 secs and try again */
971 try_channels (1);
973 /* Success -> return the character */
974 return c;
977 /* Returns a character read from stdin with appropriate interpretation */
978 /* Also takes care of generated mouse events */
979 /* Returns EV_MOUSE if it is a mouse event */
980 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
982 get_event (struct Gpm_Event *event, int redo_event, int block)
984 int c;
985 static int flag; /* Return value from select */
986 #ifdef HAVE_LIBGPM
987 static struct Gpm_Event ev; /* Mouse event */
988 #endif
989 struct timeval timeout;
990 struct timeval *time_addr = NULL;
991 static int dirty = 3;
993 if ((dirty == 3) || is_idle ()) {
994 mc_refresh ();
995 doupdate ();
996 dirty = 1;
997 } else
998 dirty++;
1000 vfs_timeout_handler ();
1002 /* Ok, we use (event->x < 0) to signal that the event does not contain
1003 a suitable position for the mouse, so we can't use show_mouse_pointer
1004 on it.
1006 if (event->x > 0) {
1007 show_mouse_pointer (event->x, event->y);
1008 if (!redo_event)
1009 event->x = -1;
1012 /* Repeat if using mouse */
1013 while (mouse_enabled && !pending_keys) {
1014 int maxfdp;
1015 fd_set select_set;
1017 FD_ZERO (&select_set);
1018 FD_SET (input_fd, &select_set);
1019 maxfdp = max (add_selects (&select_set), input_fd);
1021 #ifdef HAVE_LIBGPM
1022 if (use_mouse_p == MOUSE_GPM) {
1023 if (gpm_fd < 0) {
1024 /* Connection to gpm broken, possibly gpm has died */
1025 mouse_enabled = 0;
1026 use_mouse_p = MOUSE_NONE;
1027 break;
1028 } else {
1029 FD_SET (gpm_fd, &select_set);
1030 maxfdp = max (maxfdp, gpm_fd);
1033 #endif
1035 if (redo_event) {
1036 timeout.tv_usec = mou_auto_repeat * 1000;
1037 timeout.tv_sec = 0;
1039 time_addr = &timeout;
1040 } else {
1041 int seconds;
1043 if ((seconds = vfs_timeouts ())) {
1044 /* the timeout could be improved and actually be
1045 * the number of seconds until the next vfs entry
1046 * timeouts in the stamp list.
1049 timeout.tv_sec = seconds;
1050 timeout.tv_usec = 0;
1051 time_addr = &timeout;
1052 } else
1053 time_addr = NULL;
1056 if (!block) {
1057 time_addr = &timeout;
1058 timeout.tv_sec = 0;
1059 timeout.tv_usec = 0;
1061 enable_interrupt_key ();
1062 flag = select (maxfdp + 1, &select_set, NULL, NULL, time_addr);
1063 disable_interrupt_key ();
1065 /* select timed out: it could be for any of the following reasons:
1066 * redo_event -> it was because of the MOU_REPEAT handler
1067 * !block -> we did not block in the select call
1068 * else -> 10 second timeout to check the vfs status.
1070 if (flag == 0) {
1071 if (redo_event)
1072 return EV_MOUSE;
1073 if (!block)
1074 return EV_NONE;
1075 vfs_timeout_handler ();
1077 if (flag == -1 && errno == EINTR)
1078 return EV_NONE;
1080 check_selects (&select_set);
1082 if (FD_ISSET (input_fd, &select_set))
1083 break;
1084 #ifdef HAVE_LIBGPM
1085 if (use_mouse_p == MOUSE_GPM && gpm_fd > 0
1086 && FD_ISSET (gpm_fd, &select_set)) {
1087 Gpm_GetEvent (&ev);
1088 Gpm_FitEvent (&ev);
1089 *event = ev;
1090 return EV_MOUSE;
1092 #endif /* !HAVE_LIBGPM */
1094 #ifndef HAVE_SLANG
1095 flag = is_wintouched (stdscr);
1096 untouchwin (stdscr);
1097 #endif /* !HAVE_SLANG */
1098 c = block ? getch_with_delay () : get_key_code (1);
1100 #ifndef HAVE_SLANG
1101 if (flag)
1102 touchwin (stdscr);
1103 #endif /* !HAVE_SLANG */
1105 if (c == MCKEY_MOUSE
1106 #ifdef KEY_MOUSE
1107 || c == KEY_MOUSE
1108 #endif /* KEY_MOUSE */
1110 /* Mouse event */
1111 xmouse_get_event (event);
1112 if (event->type)
1113 return EV_MOUSE;
1114 else
1115 return EV_NONE;
1118 return c;
1121 /* Returns a key press, mouse events are discarded */
1122 int mi_getch ()
1124 Gpm_Event ev;
1125 int key;
1127 ev.x = -1;
1128 while ((key = get_event (&ev, 0, 1)) == EV_NONE)
1130 return key;
1133 static int xgetch_second (void)
1135 fd_set Read_FD_Set;
1136 int c;
1137 struct timeval timeout;
1139 timeout.tv_sec = ESCMODE_TIMEOUT / 1000000;
1140 timeout.tv_usec = ESCMODE_TIMEOUT % 1000000;
1141 nodelay (stdscr, TRUE);
1142 FD_ZERO (&Read_FD_Set);
1143 FD_SET (input_fd, &Read_FD_Set);
1144 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1145 c = getch ();
1146 nodelay (stdscr, FALSE);
1147 return c;
1150 static void
1151 learn_store_key (char *buffer, char **p, int c)
1153 if (*p - buffer > 253)
1154 return;
1155 if (c == ESC_CHAR) {
1156 *(*p)++ = '\\';
1157 *(*p)++ = 'e';
1158 } else if (c < ' ') {
1159 *(*p)++ = '^';
1160 *(*p)++ = c + 'a' - 1;
1161 } else if (c == '^') {
1162 *(*p)++ = '^';
1163 *(*p)++ = '^';
1164 } else
1165 *(*p)++ = (char) c;
1168 char *learn_key (void)
1170 /* LEARN_TIMEOUT in usec */
1171 #define LEARN_TIMEOUT 200000
1173 fd_set Read_FD_Set;
1174 struct timeval endtime;
1175 struct timeval timeout;
1176 int c;
1177 char buffer [256];
1178 char *p = buffer;
1180 keypad(stdscr, FALSE); /* disable intepreting keys by ncurses */
1181 c = getch ();
1182 while (c == -1)
1183 c = getch (); /* Sanity check, should be unnecessary */
1184 learn_store_key (buffer, &p, c);
1185 GET_TIME (endtime);
1186 endtime.tv_usec += LEARN_TIMEOUT;
1187 if (endtime.tv_usec > 1000000) {
1188 endtime.tv_usec -= 1000000;
1189 endtime.tv_sec++;
1191 nodelay (stdscr, TRUE);
1192 for (;;) {
1193 while ((c = getch ()) == -1) {
1194 GET_TIME (timeout);
1195 timeout.tv_usec = endtime.tv_usec - timeout.tv_usec;
1196 if (timeout.tv_usec < 0)
1197 timeout.tv_sec++;
1198 timeout.tv_sec = endtime.tv_sec - timeout.tv_sec;
1199 if (timeout.tv_sec >= 0 && timeout.tv_usec > 0) {
1200 FD_ZERO (&Read_FD_Set);
1201 FD_SET (input_fd, &Read_FD_Set);
1202 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &timeout);
1203 } else
1204 break;
1206 if (c == -1)
1207 break;
1208 learn_store_key (buffer, &p, c);
1210 keypad(stdscr, TRUE);
1211 nodelay (stdscr, FALSE);
1212 *p = 0;
1213 return g_strdup (buffer);
1216 /* xterm and linux console only: set keypad to numeric or application
1217 mode. Only in application keypad mode it's possible to distinguish
1218 the '+' key and the '+' on the keypad ('*' and '-' ditto)*/
1219 void
1220 numeric_keypad_mode (void)
1222 if (console_flag || xterm_flag) {
1223 fputs ("\033>", stdout);
1224 fflush (stdout);
1228 void
1229 application_keypad_mode (void)
1231 if (console_flag || xterm_flag) {
1232 fputs ("\033=", stdout);
1233 fflush (stdout);
1239 * Check if we are idle, i.e. there are no pending keyboard or mouse
1240 * events. Return 1 is idle, 0 is there are pending events.
1243 is_idle (void)
1245 int maxfdp;
1246 fd_set select_set;
1247 struct timeval timeout;
1249 FD_ZERO (&select_set);
1250 FD_SET (input_fd, &select_set);
1251 maxfdp = input_fd;
1252 #ifdef HAVE_LIBGPM
1253 if (use_mouse_p == MOUSE_GPM && mouse_enabled && gpm_fd > 0) {
1254 FD_SET (gpm_fd, &select_set);
1255 maxfdp = max (maxfdp, gpm_fd);
1257 #endif
1258 timeout.tv_sec = 0;
1259 timeout.tv_usec = 0;
1260 return (select (maxfdp + 1, &select_set, 0, 0, &timeout) <= 0);
1265 * Get modifier state (shift, alt, ctrl) for the last key pressed.
1266 * We are assuming that the state didn't change since the key press.
1267 * This is only correct if get_modifier() is called very fast after
1268 * the input was received, so that the user didn't release the
1269 * modifier keys yet.
1271 static int
1272 get_modifier (void)
1274 int result = 0;
1275 #ifdef __QNXNTO__
1276 int mod_status, shift_ext_status;
1277 static int in_photon = 0;
1278 static int ph_ig = 0;
1279 PhCursorInfo_t cursor_info;
1280 #endif /* __QNXNTO__ */
1282 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1283 if (x11_window) {
1284 Window root, child;
1285 int root_x, root_y;
1286 int win_x, win_y;
1287 unsigned int mask;
1289 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
1290 &root_y, &win_x, &win_y, &mask);
1292 if (mask & ShiftMask)
1293 result |= KEY_M_SHIFT;
1294 if (mask & ControlMask)
1295 result |= KEY_M_CTRL;
1296 return result;
1298 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
1299 #ifdef __QNXNTO__
1301 if (in_photon == 0) {
1302 /* First time here, let's load Photon library and attach
1303 to Photon */
1304 in_photon = -1;
1305 if (getenv ("PHOTON2_PATH") != NULL) {
1306 /* QNX 6.x has no support for RTLD_LAZY */
1307 void *ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
1308 if (ph_handle != NULL) {
1309 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
1310 ph_input_group =
1311 (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
1312 ph_query_cursor =
1313 (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
1314 if ((ph_attach != NULL) && (ph_input_group != NULL)
1315 && (ph_query_cursor != NULL)) {
1316 if ((*ph_attach) (0, 0)) { /* Attached */
1317 ph_ig = (*ph_input_group) (0);
1318 in_photon = 1;
1324 /* We do not have Photon running. Assume we are in text
1325 console or xterm */
1326 if (in_photon == -1) {
1327 if (devctl
1328 (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status,
1329 sizeof (int), NULL) == -1)
1330 return 0;
1331 shift_ext_status = mod_status & 0xffffff00UL;
1332 mod_status &= 0x7f;
1333 if (mod_status & _LINESTATUS_CON_ALT)
1334 result |= KEY_M_ALT;
1335 if (mod_status & _LINESTATUS_CON_CTRL)
1336 result |= KEY_M_CTRL;
1337 if ((mod_status & _LINESTATUS_CON_SHIFT)
1338 || (shift_ext_status & 0x00000800UL))
1339 result |= KEY_M_SHIFT;
1340 } else {
1341 (*ph_query_cursor) (ph_ig, &cursor_info);
1342 if (cursor_info.key_mods & 0x04)
1343 result |= KEY_M_ALT;
1344 if (cursor_info.key_mods & 0x02)
1345 result |= KEY_M_CTRL;
1346 if (cursor_info.key_mods & 0x01)
1347 result |= KEY_M_SHIFT;
1349 #endif /* __QNXNTO__ */
1351 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
1353 unsigned char modifiers = 6;
1355 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
1356 return 0;
1358 /* Translate Linux modifiers into mc modifiers */
1359 if (modifiers & SHIFT_PRESSED)
1360 result |= KEY_M_SHIFT;
1361 if (modifiers & (ALTL_PRESSED | ALTR_PRESSED))
1362 result |= KEY_M_ALT;
1363 if (modifiers & CONTROL_PRESSED)
1364 result |= KEY_M_CTRL;
1366 #endif /* !__linux__ */
1367 return result;
1370 static void k_dispose (key_def *k)
1372 if (!k)
1373 return;
1374 k_dispose (k->child);
1375 k_dispose (k->next);
1376 g_free (k);
1379 static void s_dispose (SelectList *sel)
1381 if (!sel)
1382 return;
1384 s_dispose (sel->next);
1385 g_free (sel);
1388 void done_key ()
1390 k_dispose (keys);
1391 s_dispose (select_list);
1393 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1394 if (x11_display)
1395 mc_XCloseDisplay (x11_display);
1396 #endif