(create_sequence): reduce variable scope, trivial refactoring.
[midnight-commander.git] / lib / tty / key.c
blob5a8416ed01336e7fe57216d8c696ec389e6462fa
1 /*
2 Keyboard support routines.
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995
9 Janne Kukonlehto, 1994, 1995
10 Jakub Jelinek, 1995
11 Norbert Warmuth, 1997
12 Denys Vlasenko <vda.linux@googlemail.com>, 2013
13 Slava Zanko <slavazanko@gmail.com>, 2013
14 Egmont Koblinger <egmont@gmail.com>, 2013
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 /** \file key.c
33 * \brief Source: keyboard support routines
36 #include <config.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #ifdef HAVE_SYS_SELECT_H
44 #include <sys/select.h>
45 #else
46 #include <sys/time.h>
47 #include <sys/types.h>
48 #include <unistd.h>
49 #endif
51 #include "lib/global.h"
53 #include "lib/vfs/vfs.h"
55 #include "tty.h"
56 #include "tty-internal.h" /* mouse_enabled */
57 #include "mouse.h"
58 #include "key.h"
60 #include "lib/widget.h" /* mc_refresh() */
62 #ifdef HAVE_TEXTMODE_X11_SUPPORT
63 #include "x11conn.h"
64 #endif
66 #ifdef __linux__
67 #if defined(__GLIBC__) && (__GLIBC__ < 2)
68 #include <linux/termios.h> /* TIOCLINUX */
69 #else
70 #include <termios.h>
71 #endif
72 #ifdef HAVE_SYS_IOCTL_H
73 #include <sys/ioctl.h>
74 #endif
75 #endif /* __linux__ */
77 #ifdef __CYGWIN__
78 #include <termios.h>
79 #ifdef HAVE_SYS_IOCTL_H
80 #include <sys/ioctl.h>
81 #endif
82 #endif /* __CYGWIN__ */
84 #ifdef __QNXNTO__
85 #include <dlfcn.h>
86 #include <Ph.h>
87 #include <sys/dcmd_chr.h>
88 #endif /* __QNXNTO__ */
90 /*** global variables ****************************************************************************/
92 int mou_auto_repeat = 100; /* ms */
93 int double_click_speed = 250; /* ms */
94 gboolean old_esc_mode = TRUE;
95 /* timeout for old_esc_mode in usec */
96 int old_esc_mode_timeout = G_USEC_PER_SEC; /* us, settable via env */
97 gboolean use_8th_bit_as_meta = FALSE;
99 gboolean bracketed_pasting_in_progress = FALSE;
101 /* This table is a mapping between names and the constants we use
102 * We use this to allow users to define alternate definitions for
103 * certain keys that may be missing from the terminal database
105 const key_code_name_t key_name_conv_tab[] = {
106 {ESC_CHAR, "escape", N_("Escape"), "Esc"},
107 /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
108 to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
109 {KEY_F (1), "f1", N_("Function key 1"), "F1"},
110 {KEY_F (2), "f2", N_("Function key 2"), "F2"},
111 {KEY_F (3), "f3", N_("Function key 3"), "F3"},
112 {KEY_F (4), "f4", N_("Function key 4"), "F4"},
113 {KEY_F (5), "f5", N_("Function key 5"), "F5"},
114 {KEY_F (6), "f6", N_("Function key 6"), "F6"},
115 {KEY_F (7), "f7", N_("Function key 7"), "F7"},
116 {KEY_F (8), "f8", N_("Function key 8"), "F8"},
117 {KEY_F (9), "f9", N_("Function key 9"), "F9"},
118 {KEY_F (10), "f10", N_("Function key 10"), "F10"},
119 {KEY_F (11), "f11", N_("Function key 11"), "F11"},
120 {KEY_F (12), "f12", N_("Function key 12"), "F12"},
121 {KEY_F (13), "f13", N_("Function key 13"), "F13"},
122 {KEY_F (14), "f14", N_("Function key 14"), "F14"},
123 {KEY_F (15), "f15", N_("Function key 15"), "F15"},
124 {KEY_F (16), "f16", N_("Function key 16"), "F16"},
125 {KEY_F (17), "f17", N_("Function key 17"), "F17"},
126 {KEY_F (18), "f18", N_("Function key 18"), "F18"},
127 {KEY_F (19), "f19", N_("Function key 19"), "F19"},
128 {KEY_F (20), "f20", N_("Function key 20"), "F20"},
129 {ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab"},
130 {KEY_BTAB, "backtab", N_("BackTab/S-tab"), "Shift-Tab"},
131 {KEY_BACKSPACE, "backspace", N_("Backspace"), "Backspace"},
132 {KEY_UP, "up", N_("Up arrow"), "Up"},
133 {KEY_DOWN, "down", N_("Down arrow"), "Down"},
134 {KEY_LEFT, "left", N_("Left arrow"), "Left"},
135 {KEY_RIGHT, "right", N_("Right arrow"), "Right"},
136 {KEY_IC, "insert", N_("Insert"), "Ins"},
137 {KEY_DC, "delete", N_("Delete"), "Del"},
138 {KEY_HOME, "home", N_("Home"), "Home"},
139 {KEY_END, "end", N_("End key"), "End"},
140 {KEY_PPAGE, "pgup", N_("Page Up"), "PgUp"},
141 {KEY_NPAGE, "pgdn", N_("Page Down"), "PgDn"},
142 {(int) '/', "kpslash", N_("/ on keypad"), "/"},
143 {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*"},
144 {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-"},
145 {KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+"},
147 /* From here on, these won't be shown in Learn keys (no space) */
148 {KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left"},
149 {KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right"},
150 {KEY_UP, "kpup", N_("Up arrow keypad"), "Up"},
151 {KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down"},
152 {KEY_HOME, "kphome", N_("Home on keypad"), "Home"},
153 {KEY_END, "kpend", N_("End on keypad"), "End"},
154 {KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn"},
155 {KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp"},
156 {KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins"},
157 {KEY_DC, "kpdelete", N_("Delete on keypad"), "Del"},
158 {(int) '\n', "kpenter", N_("Enter on keypad"), "Enter"},
159 {KEY_F (21), "f21", N_("Function key 21"), "F21"},
160 {KEY_F (22), "f22", N_("Function key 22"), "F22"},
161 {KEY_F (23), "f23", N_("Function key 23"), "F23"},
162 {KEY_F (24), "f24", N_("Function key 24"), "F24"},
163 {KEY_A1, "a1", N_("A1 key"), "A1"},
164 {KEY_C1, "c1", N_("C1 key"), "C1"},
166 /* Alternative label */
167 {ESC_CHAR, "esc", N_("Escape"), "Esc"},
168 {KEY_BACKSPACE, "bs", N_("Backspace"), "Bakspace"},
169 {KEY_IC, "ins", N_("Insert"), "Ins"},
170 {KEY_DC, "del", N_("Delete"), "Del"},
171 {(int) '*', "asterisk", N_("Asterisk"), "*"},
172 {(int) '-', "minus", N_("Minus"), "-"},
173 {(int) '+', "plus", N_("Plus"), "+"},
174 {(int) '.', "dot", N_("Dot"), "."},
175 {(int) '<', "lt", N_("Less than"), "<"},
176 {(int) '>', "gt", N_("Great than"), ">"},
177 {(int) '=', "equal", N_("Equal"), "="},
178 {(int) ',', "comma", N_("Comma"), ","},
179 {(int) '\'', "apostrophe", N_("Apostrophe"), "\'"},
180 {(int) ':', "colon", N_("Colon"), ":"},
181 {(int) ';', "semicolon", N_("Semicolon"), ";"},
182 {(int) '!', "exclamation", N_("Exclamation mark"), "!"},
183 {(int) '?', "question", N_("Question mark"), "?"},
184 {(int) '&', "ampersand", N_("Ampersand"), "&"},
185 {(int) '$', "dollar", N_("Dollar sign"), "$"},
186 {(int) '"', "quota", N_("Quotation mark"), "\""},
187 {(int) '%', "percent", N_("Percent sign"), "%"},
188 {(int) '^', "caret", N_("Caret"), "^"},
189 {(int) '~', "tilda", N_("Tilda"), "~"},
190 {(int) '`', "prime", N_("Prime"), "`"},
191 {(int) '_', "underline", N_("Underline"), "_"},
192 {(int) '_', "understrike", N_("Understrike"), "_"},
193 {(int) '|', "pipe", N_("Pipe"), "|"},
194 {(int) '(', "lparenthesis", N_("Left parenthesis"), "("},
195 {(int) ')', "rparenthesis", N_("Right parenthesis"), ")"},
196 {(int) '[', "lbracket", N_("Left bracket"), "["},
197 {(int) ']', "rbracket", N_("Right bracket"), "]"},
198 {(int) '{', "lbrace", N_("Left brace"), "{"},
199 {(int) '}', "rbrace", N_("Right brace"), "}"},
200 {(int) '\n', "enter", N_("Enter"), "Enter"},
201 {(int) '\t', "tab", N_("Tab key"), "Tab"},
202 {(int) ' ', "space", N_("Space key"), "Space"},
203 {(int) '/', "slash", N_("Slash key"), "/"},
204 {(int) '\\', "backslash", N_("Backslash key"), "\\"},
205 {(int) '#', "number", N_("Number sign #"), "#"},
206 {(int) '#', "hash", N_("Number sign #"), "#"},
207 /* TRANSLATORS: Please translate as in "at sign" (@). */
208 {(int) '@', "at", N_("At sign"), "@"},
210 /* meta keys */
211 {KEY_M_CTRL, "control", N_("Ctrl"), "C"},
212 {KEY_M_CTRL, "ctrl", N_("Ctrl"), "C"},
213 {KEY_M_ALT, "meta", N_("Alt"), "M"},
214 {KEY_M_ALT, "alt", N_("Alt"), "M"},
215 {KEY_M_ALT, "ralt", N_("Alt"), "M"},
216 {KEY_M_SHIFT, "shift", N_("Shift"), "S"},
218 {0, NULL, NULL, NULL}
221 /*** file scope macro definitions ****************************************************************/
223 #define MC_USEC_PER_MSEC 1000
225 /* The maximum sequence length (32 + null terminator) */
226 #define SEQ_BUFFER_LEN 33
228 /*** file scope type declarations ****************************************************************/
230 /* Linux console keyboard modifiers */
231 typedef enum
233 SHIFT_PRESSED = (1 << 0),
234 ALTR_PRESSED = (1 << 1),
235 CONTROL_PRESSED = (1 << 2),
236 ALTL_PRESSED = (1 << 3)
237 } mod_pressed_t;
239 typedef struct key_def
241 char ch; /* Holds the matching char code */
242 int code; /* The code returned, valid if child == NULL */
243 struct key_def *next;
244 struct key_def *child; /* sequence continuation */
245 int action; /* optional action to be done. Now used only
246 to mark that we are just after the first
247 Escape */
248 } key_def;
250 typedef struct
252 int code;
253 const char *seq;
254 int action;
255 } key_define_t;
257 /* File descriptor monitoring add/remove routines */
258 typedef struct
260 int fd;
261 select_fn callback;
262 void *info;
263 } select_t;
265 typedef enum KeySortType
267 KEY_NOSORT = 0,
268 KEY_SORTBYNAME,
269 KEY_SORTBYCODE
270 } KeySortType;
272 #ifdef __QNXNTO__
273 typedef int (*ph_dv_f) (void *, void *);
274 typedef int (*ph_ov_f) (void *);
275 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
276 #endif
278 /*** forward declarations (file scope functions) *************************************************/
280 /*** file scope variables ************************************************************************/
282 static key_define_t mc_default_keys[] = {
283 {ESC_CHAR, ESC_STR, MCKEY_ESCAPE},
284 {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION},
285 {MCKEY_BRACKETED_PASTING_START, ESC_STR "[200~", MCKEY_NOACTION},
286 {MCKEY_BRACKETED_PASTING_END, ESC_STR "[201~", MCKEY_NOACTION},
287 {0, NULL, MCKEY_NOACTION},
290 /* Broken terminfo and termcap databases on xterminals */
291 static key_define_t xterm_key_defines[] = {
292 {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION},
293 {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION},
294 {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION},
295 {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION},
296 {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION},
297 {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION},
298 {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION},
299 {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION},
300 {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION},
301 {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION},
302 {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION},
303 {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION},
304 {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION},
305 {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION},
307 /* old xterm Shift-arrows */
308 {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION},
309 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION},
310 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION},
311 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION},
313 /* new xterm Shift-arrows */
314 {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION},
315 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION},
316 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION},
317 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION},
319 /* more xterm keys with modifiers */
320 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION},
321 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION},
322 {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION},
323 {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION},
324 {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION},
325 {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION},
326 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION},
327 {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION},
328 {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION},
329 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION},
330 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION},
331 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION},
332 {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION},
333 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION},
334 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION},
335 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION},
336 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION},
337 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION},
338 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION},
340 /* putty */
341 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION},
342 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION},
343 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION},
344 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION},
346 /* putty alt-arrow keys */
347 /* removed as source esc esc esc trouble */
349 { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION },
350 { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION },
351 { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
352 { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION },
353 { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
354 { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
355 { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
356 { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
358 { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
359 { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
360 { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
361 { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
363 { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
364 { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
365 { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
366 { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
368 /* xterm alt-arrow keys */
369 {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION},
370 {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION},
371 {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION},
372 {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION},
373 {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION},
374 {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION},
375 {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION},
376 {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION},
377 {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION},
378 {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION},
379 {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION},
380 {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION},
381 {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION},
382 {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION},
383 {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION},
384 {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION},
386 {KEY_M_SHIFT | KEY_M_ALT | KEY_UP, ESC_STR "[1;4A", MCKEY_NOACTION},
387 {KEY_M_SHIFT | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;4B", MCKEY_NOACTION},
388 {KEY_M_SHIFT | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;4C", MCKEY_NOACTION},
389 {KEY_M_SHIFT | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;4D", MCKEY_NOACTION},
391 /* rxvt keys with modifiers */
392 {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
393 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
394 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
395 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
396 {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
397 {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
398 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
399 {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
400 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
401 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
402 {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
403 {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
404 {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
405 {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
406 {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
407 {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
408 {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
410 /* konsole keys with modifiers */
411 {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
412 {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
414 /* gnome-terminal */
415 {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
416 {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
417 {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
418 {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
419 {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
420 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
421 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
422 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
423 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
424 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
425 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
426 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
428 /* gnome-terminal - application mode */
429 {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
430 {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
431 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
432 {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
433 {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
434 {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
435 {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
436 {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
438 /* iTerm */
439 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
440 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
442 /* putty */
443 {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
444 {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
446 /* keypad keys */
447 {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
448 {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
449 {'/', ESC_STR "Oo", MCKEY_NOACTION},
450 {'\n', ESC_STR "OM", MCKEY_NOACTION},
452 {0, NULL, MCKEY_NOACTION},
455 /* qansi-m terminals have a much more key combinations,
456 which are undefined in termcap/terminfo */
457 static key_define_t qansi_key_defines[] = {
458 /* qansi-m terminal */
459 {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */
460 {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */
461 {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */
462 {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */
463 {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */
464 {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */
465 {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */
466 {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */
467 {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */
468 {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */
469 {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */
470 {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */
471 {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */
472 {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */
473 {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */
474 {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */
475 {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */
476 {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */
477 {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */
478 {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */
479 {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */
480 {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */
481 {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */
482 {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */
483 {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */
484 {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */
485 {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */
486 {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */
487 {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */
488 {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */
489 {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */
490 {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */
491 {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */
492 {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */
493 {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */
494 {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */
495 {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */
496 {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */
497 {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */
498 {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */
499 {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */
500 {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */
501 {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */
502 {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */
503 {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */
504 {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */
505 {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */
506 {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */
507 {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */
508 {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */
509 {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */
510 {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */
511 {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */
512 {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */
513 {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */
514 {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */
515 {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */
516 {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */
517 {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */
518 {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */
519 {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */
520 {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */
521 {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */
522 {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */
523 {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */
524 {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */
525 {0, NULL, MCKEY_NOACTION},
528 /* This holds all the key definitions */
529 static key_def *keys = NULL;
531 static int input_fd;
532 static int disabled_channels = 0; /* Disable channels checking */
534 static GSList *select_list = NULL;
536 static int seq_buffer[SEQ_BUFFER_LEN];
537 static int *seq_append = NULL;
539 static int *pending_keys = NULL;
541 #ifdef __QNXNTO__
542 ph_dv_f ph_attach;
543 ph_ov_f ph_input_group;
544 ph_pqc_f ph_query_cursor;
545 #endif
547 #ifdef HAVE_TEXTMODE_X11_SUPPORT
548 static Display *x11_display;
549 static Window x11_window;
550 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
552 static KeySortType has_been_sorted = KEY_NOSORT;
554 /* *INDENT-OFF* */
555 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
556 /* *INDENT-ON* */
558 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
560 /* --------------------------------------------------------------------------------------------- */
561 /*** file scope functions ************************************************************************/
562 /* --------------------------------------------------------------------------------------------- */
564 static int
565 select_cmp_by_fd_set (gconstpointer a, gconstpointer b)
567 const select_t *s = (const select_t *) a;
568 const fd_set *f = (const fd_set *) b;
570 return (FD_ISSET (s->fd, f) ? 0 : 1);
573 /* --------------------------------------------------------------------------------------------- */
575 static int
576 select_cmp_by_fd (gconstpointer a, gconstpointer b)
578 const select_t *s = (const select_t *) a;
579 const int fd = GPOINTER_TO_INT (b);
581 return (s->fd == fd ? 0 : 1);
584 /* --------------------------------------------------------------------------------------------- */
586 static int
587 add_selects (fd_set *select_set)
589 int top_fd = 0;
591 if (disabled_channels == 0)
593 GSList *s;
595 for (s = select_list; s != NULL; s = g_slist_next (s))
597 select_t *p = (select_t *) s->data;
599 FD_SET (p->fd, select_set);
600 if (p->fd > top_fd)
601 top_fd = p->fd;
605 return top_fd;
608 /* --------------------------------------------------------------------------------------------- */
610 static void
611 check_selects (fd_set *select_set)
613 while (disabled_channels == 0)
615 GSList *s;
616 select_t *p;
618 s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set);
619 if (s == NULL)
620 break;
622 p = (select_t *) s->data;
623 FD_CLR (p->fd, select_set);
624 p->callback (p->fd, p->info);
628 /* --------------------------------------------------------------------------------------------- */
629 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
631 static void
632 try_channels (gboolean set_timeout)
634 struct timeval time_out;
635 static fd_set select_set;
637 while (TRUE)
639 struct timeval *timeptr = NULL;
640 int maxfdp, v;
642 FD_ZERO (&select_set);
643 FD_SET (input_fd, &select_set); /* Add stdin */
644 maxfdp = MAX (add_selects (&select_set), input_fd);
646 if (set_timeout)
648 time_out.tv_sec = 0;
649 time_out.tv_usec = 100 * MC_USEC_PER_MSEC;
650 timeptr = &time_out;
653 v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
654 if (v > 0)
656 check_selects (&select_set);
657 if (FD_ISSET (input_fd, &select_set))
658 break;
663 /* --------------------------------------------------------------------------------------------- */
665 static key_def *
666 create_sequence (const char *seq, int code, int action)
668 key_def *base, *attach;
670 for (base = attach = NULL; *seq != '\0'; seq++)
672 key_def *p;
674 p = g_new (key_def, 1);
675 if (base == NULL)
676 base = p;
677 if (attach != NULL)
678 attach->child = p;
680 p->ch = *seq;
681 p->code = code;
682 p->child = NULL;
683 p->next = NULL;
684 p->action = seq[1] == '\0' ? action : MCKEY_NOACTION;
685 attach = p;
687 return base;
690 /* --------------------------------------------------------------------------------------------- */
692 static void
693 define_sequences (const key_define_t *kd)
695 int i;
697 for (i = 0; kd[i].code != 0; i++)
698 define_sequence (kd[i].code, kd[i].seq, kd[i].action);
701 /* --------------------------------------------------------------------------------------------- */
703 #ifdef HAVE_TEXTMODE_X11_SUPPORT
704 static void
705 init_key_x11 (void)
707 if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
709 x11_display = mc_XOpenDisplay (0);
711 if (x11_display != NULL)
712 x11_window = DefaultRootWindow (x11_display);
715 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
717 /* --------------------------------------------------------------------------------------------- */
718 /* Workaround for System V Curses vt100 bug */
720 static int
721 getch_with_delay (void)
723 int c;
725 /* This routine could be used on systems without mouse support,
726 so we need to do the select check :-( */
727 while (TRUE)
729 if (pending_keys == NULL)
730 try_channels (FALSE);
732 /* Try to get a character */
733 c = get_key_code (0);
734 if (c != -1)
735 break;
737 /* Failed -> wait 0.1 secs and try again */
738 try_channels (TRUE);
741 /* Success -> return the character */
742 return c;
745 /* --------------------------------------------------------------------------------------------- */
747 static void
748 xmouse_get_event (Gpm_Event *ev, gboolean extended)
750 static gint64 tv1 = 0; /* Force first click as single */
751 static int clicks = 0;
752 static int last_btn = 0;
753 int btn;
755 /* Decode Xterm mouse information to a GPM style event */
757 if (!extended)
759 /* Variable btn has following meaning: */
760 /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
761 btn = tty_lowlevel_getch () - 32;
762 /* Coordinates are 33-based */
763 /* Transform them to 1-based */
764 ev->x = tty_lowlevel_getch () - 32;
765 ev->y = tty_lowlevel_getch () - 32;
767 else
769 /* SGR 1006 extension (e.g. "\e[<0;12;300M"):
770 - Numbers are encoded in decimal to make it ASCII-safe
771 and to overcome the limit of 223 columns/rows.
772 - Mouse release is encoded by trailing 'm' rather than 'M'
773 so that the released button can be reported.
774 - Numbers are no longer offset by 32. */
775 char c;
777 btn = ev->x = ev->y = 0;
778 ev->type = 0; /* In case we return on an invalid sequence */
780 while ((c = tty_lowlevel_getch ()) != ';')
782 if (c < '0' || c > '9')
783 return;
784 btn = 10 * btn + (c - '0');
786 while ((c = tty_lowlevel_getch ()) != ';')
788 if (c < '0' || c > '9')
789 return;
790 ev->x = 10 * ev->x + (c - '0');
792 while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
794 if (c < '0' || c > '9')
795 return;
796 ev->y = 10 * ev->y + (c - '0');
798 /* Legacy mouse protocol doesn't tell which button was released,
799 conveniently all of mc's widgets are written not to rely on this
800 information. With the SGR extension the released button becomes
801 known, but for the sake of simplicity we just ignore it. */
802 if (c == 'm')
803 btn = 3;
806 /* There seems to be no way of knowing which button was released */
807 /* So we assume all the buttons were released */
809 if (btn == 3)
811 if (last_btn != 0)
813 if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
815 /* FIXME: DIRTY HACK */
816 /* don't generate GPM_UP after mouse wheel */
817 /* need for menu event handling */
818 ev->type = 0;
819 tv1 = 0;
821 else
823 ev->type = GPM_UP | (GPM_SINGLE << clicks);
824 tv1 = g_get_monotonic_time ();
826 ev->buttons = 0;
827 last_btn = 0;
828 clicks = 0;
830 else
832 /* Bogus event, maybe mouse wheel */
833 ev->type = 0;
836 else
838 gint64 tv2;
840 if (btn >= 32 && btn <= 34)
842 btn -= 32;
843 ev->type = GPM_DRAG;
845 else
846 ev->type = GPM_DOWN;
848 tv2 = g_get_monotonic_time ();
849 if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC)
851 clicks++;
852 clicks %= 3;
854 else
855 clicks = 0;
857 switch (btn)
859 case 0:
860 ev->buttons = GPM_B_LEFT;
861 break;
862 case 1:
863 ev->buttons = GPM_B_MIDDLE;
864 break;
865 case 2:
866 ev->buttons = GPM_B_RIGHT;
867 break;
868 case 64:
869 ev->buttons = GPM_B_UP;
870 clicks = 0;
871 break;
872 case 65:
873 ev->buttons = GPM_B_DOWN;
874 clicks = 0;
875 break;
876 default:
877 /* Nothing */
878 ev->type = 0;
879 ev->buttons = 0;
880 break;
882 last_btn = ev->buttons;
886 /* --------------------------------------------------------------------------------------------- */
888 * Get modifier state (shift, alt, ctrl) for the last key pressed.
889 * We are assuming that the state didn't change since the key press.
890 * This is only correct if get_modifier() is called very fast after
891 * the input was received, so that the user didn't release the
892 * modifier keys yet.
895 static int
896 get_modifier (void)
898 int result = 0;
899 #ifdef __QNXNTO__
900 static int in_photon = 0;
901 static int ph_ig = 0;
902 #endif /* __QNXNTO__ */
904 #ifdef HAVE_TEXTMODE_X11_SUPPORT
905 if (x11_window != 0)
907 Window root, child;
908 int root_x, root_y;
909 int win_x, win_y;
910 unsigned int mask;
912 mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
913 &root_y, &win_x, &win_y, &mask);
915 if ((mask & ShiftMask) != 0)
916 result |= KEY_M_SHIFT;
917 if ((mask & ControlMask) != 0)
918 result |= KEY_M_CTRL;
919 return result;
921 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
923 #ifdef __QNXNTO__
924 if (in_photon == 0)
926 /* First time here, let's load Photon library and attach to Photon */
927 in_photon = -1;
929 if (getenv ("PHOTON2_PATH") != NULL)
931 /* QNX 6.x has no support for RTLD_LAZY */
932 void *ph_handle;
934 ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
935 if (ph_handle != NULL)
937 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
938 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
939 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
940 if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL)
941 && (*ph_attach) (0, 0) != NULL)
943 /* Attached */
944 ph_ig = (*ph_input_group) (0);
945 in_photon = 1;
950 /* We do not have Photon running. Assume we are in text console or xterm */
951 if (in_photon == -1)
953 int mod_status;
954 int shift_ext_status;
956 if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) ==
958 return 0;
960 shift_ext_status = mod_status & 0xffffff00UL;
961 mod_status &= 0x7f;
962 if ((mod_status & _LINESTATUS_CON_ALT) != 0)
963 result |= KEY_M_ALT;
964 if ((mod_status & _LINESTATUS_CON_CTRL) != 0)
965 result |= KEY_M_CTRL;
966 if ((mod_status & _LINESTATUS_CON_SHIFT) != 0 || (shift_ext_status & 0x00000800UL) != 0)
967 result |= KEY_M_SHIFT;
969 else
971 PhCursorInfo_t cursor_info;
973 (*ph_query_cursor) (ph_ig, &cursor_info);
974 if ((cursor_info.key_mods & 0x04) != 0)
975 result |= KEY_M_ALT;
976 if ((cursor_info.key_mods & 0x02) != 0)
977 result |= KEY_M_CTRL;
978 if ((cursor_info.key_mods & 0x01) != 0)
979 result |= KEY_M_SHIFT;
981 #endif /* __QNXNTO__ */
983 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
985 unsigned char modifiers = 6;
987 if (ioctl (0, TIOCLINUX, &modifiers) < 0)
988 return 0;
990 /* Translate Linux modifiers into mc modifiers */
991 if ((modifiers & SHIFT_PRESSED) != 0)
992 result |= KEY_M_SHIFT;
993 if ((modifiers & (ALTL_PRESSED | ALTR_PRESSED)) != 0)
994 result |= KEY_M_ALT;
995 if ((modifiers & CONTROL_PRESSED) != 0)
996 result |= KEY_M_CTRL;
998 #endif /* !__linux__ */
1000 return result;
1003 /* --------------------------------------------------------------------------------------------- */
1005 static gboolean
1006 push_char (int c)
1008 gboolean ret = FALSE;
1010 if (seq_append == NULL)
1011 seq_append = seq_buffer;
1013 if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
1015 *(seq_append++) = c;
1016 *seq_append = '\0';
1017 ret = TRUE;
1020 return ret;
1023 /* --------------------------------------------------------------------------------------------- */
1024 /* Apply corrections for the keycode generated in get_key_code() */
1026 static int
1027 correct_key_code (int code)
1029 unsigned int c = code & ~KEY_M_MASK; /* code without modifier */
1030 unsigned int mod = code & KEY_M_MASK; /* modifier */
1031 #ifdef __QNXNTO__
1032 unsigned int qmod; /* bunch of the QNX console
1033 modifiers needs unchanged */
1034 #endif /* __QNXNTO__ */
1037 * Add key modifiers directly from X11 or OS.
1038 * Ordinary characters only get modifiers from sequences.
1040 if (c < 32 || c >= 256)
1041 mod |= get_modifier ();
1043 /* This is needed if the newline is reported as carriage return */
1044 if (c == '\r')
1045 c = '\n';
1047 /* This is reported to be useful on AIX */
1048 if (c == KEY_SCANCEL)
1049 c = '\t';
1051 /* Convert Back Tab to Shift+Tab */
1052 if (c == KEY_BTAB)
1054 c = '\t';
1055 mod = KEY_M_SHIFT;
1058 /* F0 is the same as F10 for out purposes */
1059 if (c == KEY_F (0))
1060 c = KEY_F (10);
1063 * We are not interested if Ctrl was pressed when entering control
1064 * characters, so assume that it was. When checking for such keys,
1065 * XCTRL macro should be used. In some cases, we are interested,
1066 * e.g. to distinguish Ctrl-Enter from Enter.
1068 if (c == '\b')
1070 /* Special case for backspase ('\b' < 32) */
1071 c = KEY_BACKSPACE;
1072 mod &= ~KEY_M_CTRL;
1074 else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
1075 mod |= KEY_M_CTRL;
1077 #ifdef __QNXNTO__
1078 qmod = get_modifier ();
1080 if (c == 127 && mod == 0)
1082 /* Add Ctrl/Alt/Shift-BackSpace */
1083 mod |= get_modifier ();
1084 c = KEY_BACKSPACE;
1087 if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1089 /* Add Shift-Insert on key pad */
1090 mod = KEY_M_SHIFT;
1091 c = KEY_IC;
1094 if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1096 /* Add Shift-Del on key pad */
1097 mod = KEY_M_SHIFT;
1098 c = KEY_DC;
1100 #endif /* __QNXNTO__ */
1102 /* Unrecognized 0177 is delete (preserve Ctrl) */
1103 if (c == 0177)
1104 c = KEY_BACKSPACE;
1106 #if 0
1107 /* Unrecognized Ctrl-d is delete */
1108 if (c == 'd' & 31)
1110 c = KEY_DC;
1111 mod &= ~KEY_M_CTRL;
1114 /* Unrecognized Ctrl-h is backspace */
1115 if (c == 'h' & 31)
1117 c = KEY_BACKSPACE;
1118 mod &= ~KEY_M_CTRL;
1120 #endif
1122 /* Shift+BackSpace is backspace */
1123 if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0)
1124 mod &= ~KEY_M_SHIFT;
1126 /* Convert Shift+Fn to F(n+10) */
1127 if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT) != 0)
1128 c += 10;
1130 /* Remove Shift information from function keys */
1131 if (c >= KEY_F (1) && c <= KEY_F (20))
1132 mod &= ~KEY_M_SHIFT;
1134 if (!mc_global.tty.alternate_plus_minus)
1135 switch (c)
1137 case KEY_KP_ADD:
1138 c = '+';
1139 break;
1140 case KEY_KP_SUBTRACT:
1141 c = '-';
1142 break;
1143 case KEY_KP_MULTIPLY:
1144 c = '*';
1145 break;
1146 default:
1147 break;
1150 return (mod | c);
1153 /* --------------------------------------------------------------------------------------------- */
1155 static int
1156 getch_with_timeout (unsigned int delay_us)
1158 fd_set Read_FD_Set;
1159 int c;
1160 struct timeval time_out;
1162 time_out.tv_sec = delay_us / G_USEC_PER_SEC;
1163 time_out.tv_usec = delay_us % G_USEC_PER_SEC;
1164 tty_nodelay (TRUE);
1165 FD_ZERO (&Read_FD_Set);
1166 FD_SET (input_fd, &Read_FD_Set);
1167 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1168 c = tty_lowlevel_getch ();
1169 tty_nodelay (FALSE);
1170 return c;
1173 /* --------------------------------------------------------------------------------------------- */
1175 static void
1176 learn_store_key (GString *buffer, int c)
1178 if (c == ESC_CHAR)
1179 g_string_append (buffer, "\\e");
1180 else if (c < ' ')
1182 g_string_append_c (buffer, '^');
1183 g_string_append_c (buffer, c + 'a' - 1);
1185 else if (c == '^')
1186 g_string_append (buffer, "^^");
1187 else
1188 g_string_append_c (buffer, (char) c);
1191 /* --------------------------------------------------------------------------------------------- */
1193 static void
1194 k_dispose (key_def *k)
1196 if (k != NULL)
1198 k_dispose (k->child);
1199 k_dispose (k->next);
1200 g_free (k);
1204 /* --------------------------------------------------------------------------------------------- */
1206 static int
1207 key_code_comparator_by_name (const void *p1, const void *p2)
1209 const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1210 const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1212 return g_ascii_strcasecmp (n1->name, n2->name);
1215 /* --------------------------------------------------------------------------------------------- */
1217 static int
1218 key_code_comparator_by_code (const void *p1, const void *p2)
1220 const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1221 const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1223 return n1->code - n2->code;
1226 /* --------------------------------------------------------------------------------------------- */
1228 static inline void
1229 sort_key_conv_tab (enum KeySortType type_sort)
1231 if (has_been_sorted != type_sort)
1233 size_t i;
1235 for (i = 0; i < key_conv_tab_size; i++)
1236 key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1238 if (type_sort == KEY_SORTBYNAME)
1239 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1240 &key_code_comparator_by_name);
1241 else if (type_sort == KEY_SORTBYCODE)
1242 qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1243 &key_code_comparator_by_code);
1245 has_been_sorted = type_sort;
1249 /* --------------------------------------------------------------------------------------------- */
1251 static int
1252 lookup_keyname (const char *name, int *idx)
1254 if (name[0] != '\0')
1256 const key_code_name_t key = { 0, name, NULL, NULL };
1257 const key_code_name_t *keyp = &key;
1258 const key_code_name_t **res;
1260 if (name[1] == '\0')
1262 *idx = -1;
1263 return (int) name[0];
1266 sort_key_conv_tab (KEY_SORTBYNAME);
1268 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1269 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1271 if (res != NULL)
1273 *idx = (int) (res - key_conv_tab_sorted);
1274 return (*res)->code;
1278 *idx = -1;
1279 return 0;
1282 /* --------------------------------------------------------------------------------------------- */
1284 static gboolean
1285 lookup_keycode (const long code, int *idx)
1287 if (code != 0)
1289 const key_code_name_t key = { code, NULL, NULL, NULL };
1290 const key_code_name_t *keyp = &key;
1291 const key_code_name_t **res;
1293 sort_key_conv_tab (KEY_SORTBYCODE);
1295 res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1296 sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1298 if (res != NULL)
1300 *idx = (int) (res - key_conv_tab_sorted);
1301 return TRUE;
1305 *idx = -1;
1306 return FALSE;
1309 /* --------------------------------------------------------------------------------------------- */
1310 /*** public functions ****************************************************************************/
1311 /* --------------------------------------------------------------------------------------------- */
1312 /* This has to be called before init_slang or whatever routine
1313 calls any define_sequence */
1315 void
1316 init_key (void)
1318 const char *term;
1320 term = getenv ("TERM");
1322 /* This has to be the first define_sequence */
1323 /* So, we can assume that the first keys member has ESC */
1324 define_sequences (mc_default_keys);
1326 /* Terminfo on irix does not have some keys */
1327 if (mc_global.tty.xterm_flag
1328 || (term != NULL
1329 && (strncmp (term, "iris-ansi", 9) == 0
1330 || strncmp (term, "xterm", 5) == 0
1331 || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0)))
1332 define_sequences (xterm_key_defines);
1334 /* load some additional keys (e.g. direct Alt-? support) */
1335 load_xtra_key_defines ();
1337 #ifdef __QNX__
1338 if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1340 /* Modify the default value of use_8th_bit_as_meta: we would
1341 * like to provide a working mc for a newbie who knows nothing
1342 * about [Options|Display bits|Full 8 bits input]...
1344 * Don't use 'meta'-bit, when we are dealing with a
1345 * 'qnx*'-type terminal: clear the default value!
1346 * These terminal types use 0xFF as an escape character,
1347 * so use_8th_bit_as_meta==1 must not be enabled!
1349 * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1350 * is not used now (doesn't even depend on use_8th_bit_as_meta
1351 * as in mc-3.1.2)...GREAT!...no additional code is required!]
1353 use_8th_bit_as_meta = FALSE;
1355 #endif /* __QNX__ */
1357 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1358 init_key_x11 ();
1359 #endif
1361 /* Load the qansi-m key definitions
1362 if we are running under the qansi-m terminal */
1363 if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1364 define_sequences (qansi_key_defines);
1367 /* --------------------------------------------------------------------------------------------- */
1369 * This has to be called after SLang_init_tty/slint_init
1372 void
1373 init_key_input_fd (void)
1375 #ifdef HAVE_SLANG
1376 input_fd = SLang_TT_Read_FD;
1377 #endif
1380 /* --------------------------------------------------------------------------------------------- */
1382 void
1383 done_key (void)
1385 k_dispose (keys);
1386 g_slist_free_full (select_list, g_free);
1388 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1389 if (x11_display)
1390 mc_XCloseDisplay (x11_display);
1391 #endif
1394 /* --------------------------------------------------------------------------------------------- */
1396 void
1397 add_select_channel (int fd, select_fn callback, void *info)
1399 select_t *new;
1401 new = g_new (select_t, 1);
1402 new->fd = fd;
1403 new->callback = callback;
1404 new->info = info;
1406 select_list = g_slist_prepend (select_list, new);
1409 /* --------------------------------------------------------------------------------------------- */
1411 void
1412 delete_select_channel (int fd)
1414 GSList *p;
1416 p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd);
1417 if (p != NULL)
1418 select_list = g_slist_delete_link (select_list, p);
1421 /* --------------------------------------------------------------------------------------------- */
1423 void
1424 channels_up (void)
1426 if (disabled_channels == 0)
1427 fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1428 disabled_channels--;
1431 /* --------------------------------------------------------------------------------------------- */
1433 void
1434 channels_down (void)
1436 disabled_channels++;
1439 /* --------------------------------------------------------------------------------------------- */
1441 * Return the code associated with the symbolic name keyname
1444 long
1445 tty_keyname_to_keycode (const char *name, char **label)
1447 char **lc_keys, **p;
1448 char *cname;
1449 int k = -1;
1450 int key = 0;
1451 int lc_index = -1;
1453 int use_meta = -1;
1454 int use_ctrl = -1;
1455 int use_shift = -1;
1457 if (name == NULL)
1458 return 0;
1460 cname = g_strstrip (g_strdup (name));
1461 lc_keys = g_strsplit_set (cname, "-+ ", -1);
1462 g_free (cname);
1464 for (p = lc_keys; p != NULL && *p != NULL; p++)
1466 if ((*p)[0] != '\0')
1468 int idx;
1470 key = lookup_keyname (g_strstrip (*p), &idx);
1472 if (key == KEY_M_ALT)
1473 use_meta = idx;
1474 else if (key == KEY_M_CTRL)
1475 use_ctrl = idx;
1476 else if (key == KEY_M_SHIFT)
1477 use_shift = idx;
1478 else
1480 k = key;
1481 lc_index = idx;
1482 break;
1487 g_strfreev (lc_keys);
1489 /* output */
1490 if (k <= 0)
1491 return 0;
1493 if (label != NULL)
1495 GString *s;
1497 s = g_string_new ("");
1499 if (use_meta != -1)
1501 g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1502 g_string_append_c (s, '-');
1504 if (use_ctrl != -1)
1506 g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1507 g_string_append_c (s, '-');
1509 if (use_shift != -1)
1511 if (k < 127)
1512 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1513 else
1515 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1516 g_string_append_c (s, '-');
1517 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1520 else if (k < 128)
1522 if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1523 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1524 else
1525 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1527 else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1528 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1529 else
1530 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1532 *label = g_string_free (s, FALSE);
1535 if (use_shift != -1)
1537 if (k < 127 && k > 31)
1538 k = g_ascii_toupper ((gchar) k);
1539 else
1540 k |= KEY_M_SHIFT;
1543 if (use_ctrl != -1)
1545 if (k < 256)
1546 k = XCTRL (k);
1547 else
1548 k |= KEY_M_CTRL;
1551 if (use_meta != -1)
1552 k = ALT (k);
1554 return (long) k;
1557 /* --------------------------------------------------------------------------------------------- */
1559 char *
1560 tty_keycode_to_keyname (const int keycode)
1562 /* code without modifier */
1563 unsigned int k = keycode & ~KEY_M_MASK;
1564 /* modifier */
1565 unsigned int mod = keycode & KEY_M_MASK;
1567 int key_idx = -1;
1569 GString *s;
1570 int idx;
1572 s = g_string_sized_new (8);
1574 if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1576 if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx))
1578 g_string_append (s, key_conv_tab_sorted[idx]->name);
1579 g_string_append_c (s, '-');
1582 if ((mod & KEY_M_CTRL) != 0)
1584 /* non printeble chars like a CTRL-[A..Z] */
1585 if (k < 32)
1586 k += 64;
1588 if (lookup_keycode (KEY_M_CTRL, &idx))
1590 g_string_append (s, key_conv_tab_sorted[idx]->name);
1591 g_string_append_c (s, '-');
1595 if ((mod & KEY_M_SHIFT) != 0)
1597 if (lookup_keycode (KEY_M_ALT, &idx))
1599 if (k < 127)
1600 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1601 else
1603 g_string_append (s, key_conv_tab_sorted[idx]->name);
1604 g_string_append_c (s, '-');
1605 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1609 else if (k < 128)
1611 if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1612 g_string_append_c (s, (gchar) k);
1613 else
1614 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1616 else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1617 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1618 else
1619 g_string_append_c (s, (gchar) keycode);
1622 return g_string_free (s, s->len == 0);
1625 /* --------------------------------------------------------------------------------------------- */
1627 * Return TRUE on success, FALSE on error.
1628 * An error happens if SEQ is a beginning of an existing longer sequence.
1631 gboolean
1632 define_sequence (int code, const char *seq, int action)
1634 key_def *base;
1636 if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1637 return FALSE;
1639 for (base = keys; (base != NULL) && (*seq != '\0');)
1640 if (*seq == base->ch)
1642 if (base->child == NULL)
1644 if (*(seq + 1) != '\0')
1645 base->child = create_sequence (seq + 1, code, action);
1646 else
1648 /* The sequence matches an existing one. */
1649 base->code = code;
1650 base->action = action;
1652 return TRUE;
1655 base = base->child;
1656 seq++;
1658 else
1660 if (base->next != NULL)
1661 base = base->next;
1662 else
1664 base->next = create_sequence (seq, code, action);
1665 return TRUE;
1669 if (*seq == '\0')
1671 /* Attempt to redefine a sequence with a shorter sequence. */
1672 return FALSE;
1675 keys = create_sequence (seq, code, action);
1676 return TRUE;
1679 /* --------------------------------------------------------------------------------------------- */
1681 * Check if we are idle, i.e. there are no pending keyboard or mouse
1682 * events. Return 1 is idle, 0 is there are pending events.
1684 gboolean
1685 is_idle (void)
1687 int nfd;
1688 fd_set select_set;
1689 struct timeval time_out;
1691 FD_ZERO (&select_set);
1692 FD_SET (input_fd, &select_set);
1693 nfd = MAX (0, input_fd) + 1;
1694 time_out.tv_sec = 0;
1695 time_out.tv_usec = 0;
1696 #ifdef HAVE_LIBGPM
1697 if (mouse_enabled && use_mouse_p == MOUSE_GPM)
1699 if (gpm_fd >= 0)
1701 FD_SET (gpm_fd, &select_set);
1702 nfd = MAX (nfd, gpm_fd + 1);
1704 else
1706 if (mouse_fd >= 0) /* error indicative */
1708 if (FD_ISSET (mouse_fd, &select_set))
1709 FD_CLR (mouse_fd, &select_set);
1710 mouse_fd = gpm_fd;
1712 /* gpm_fd == -2 means under some X terminal */
1713 if (gpm_fd == -1)
1715 mouse_enabled = FALSE;
1716 use_mouse_p = MOUSE_NONE;
1720 #endif
1721 return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1724 /* --------------------------------------------------------------------------------------------- */
1727 get_key_code (int no_delay)
1729 int c;
1730 static key_def *this = NULL, *parent;
1731 static gint64 esc_time = -1;
1732 static int lastnodelay = -1;
1734 if (no_delay != lastnodelay)
1736 this = NULL;
1737 lastnodelay = no_delay;
1740 pend_send:
1741 if (pending_keys != NULL)
1743 gboolean bad_seq;
1745 c = *pending_keys++;
1746 while (c == ESC_CHAR)
1747 c = ALT (*pending_keys++);
1749 bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0');
1750 if (*pending_keys == '\0' || bad_seq)
1751 pending_keys = seq_append = NULL;
1753 if (bad_seq)
1755 /* This is an unknown ESC sequence.
1756 * To prevent interpreting its tail as a random garbage,
1757 * eat and discard all buffered and quickly following chars.
1758 * Small, but non-zero timeout is needed to reconnect
1759 * escape sequence split up by e.g. a serial line.
1761 int paranoia = 20;
1763 while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1766 else
1768 if (c > 127 && c < 256 && use_8th_bit_as_meta)
1769 c = ALT (c & 0x7f);
1771 goto done;
1775 nodelay_try_again:
1776 if (no_delay != 0)
1777 tty_nodelay (TRUE);
1779 c = tty_lowlevel_getch ();
1780 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1781 if (c == KEY_RESIZE)
1782 goto nodelay_try_again;
1783 #endif
1785 if (no_delay != 0)
1787 tty_nodelay (FALSE);
1788 if (c == -1)
1790 if (this == NULL || parent == NULL || parent->action != MCKEY_ESCAPE || !old_esc_mode ||
1791 esc_time == -1 || g_get_monotonic_time () < esc_time + old_esc_mode_timeout)
1792 return -1;
1794 this = NULL;
1795 pending_keys = seq_append = NULL;
1796 return ESC_CHAR;
1799 else if (c == -1)
1801 /* Maybe we got an incomplete match.
1802 This we do only in delay mode, since otherwise
1803 tty_lowlevel_getch can return -1 at any time. */
1804 if (seq_append == NULL)
1806 this = NULL;
1807 return -1;
1810 pending_keys = seq_buffer;
1811 goto pend_send;
1814 /* Search the key on the root */
1815 if (no_delay == 0 || this == NULL)
1817 this = keys;
1818 parent = NULL;
1820 if (c > 127 && c < 256 && use_8th_bit_as_meta)
1822 c &= 0x7f;
1824 /* The first sequence defined starts with esc */
1825 parent = keys;
1826 this = keys->child;
1830 while (this != NULL)
1832 if (c == this->ch)
1834 if (this->child == NULL)
1836 /* We got a complete match, return and reset search */
1837 pending_keys = seq_append = NULL;
1838 c = this->code;
1839 goto done;
1842 /* No match yet, but it may be a prefix for a valid seq */
1843 if (!push_char (c))
1845 pending_keys = seq_buffer;
1846 goto pend_send;
1849 parent = this;
1850 this = this->child;
1851 if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1853 if (no_delay != 0)
1855 esc_time = g_get_monotonic_time ();
1856 goto nodelay_try_again;
1859 esc_time = -1;
1860 c = getch_with_timeout (old_esc_mode_timeout);
1861 if (c != -1)
1862 continue;
1864 pending_keys = seq_append = NULL;
1865 this = NULL;
1866 return ESC_CHAR;
1869 if (no_delay != 0)
1870 goto nodelay_try_again;
1871 c = tty_lowlevel_getch ();
1872 continue;
1875 /* c != this->ch. Try other keys with this prefix */
1876 if (this->next != NULL)
1878 this = this->next;
1879 continue;
1882 /* No match found. Is it one of our ESC <key> specials? */
1883 if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1885 /* Convert escape-digits to F-keys */
1886 if (g_ascii_isdigit (c))
1887 c = KEY_F (c - '0');
1888 else if (c == ' ')
1889 c = ESC_CHAR;
1890 else
1891 c = ALT (c);
1893 pending_keys = seq_append = NULL;
1894 goto done;
1897 /* Unknown sequence. Maybe a prefix of a longer one. Save it. */
1898 push_char (c);
1899 pending_keys = seq_buffer;
1900 goto pend_send;
1901 } /* while (this != NULL) */
1903 done:
1904 this = NULL;
1905 return correct_key_code (c);
1908 /* --------------------------------------------------------------------------------------------- */
1909 /* Returns a character read from stdin with appropriate interpretation */
1910 /* Also takes care of generated mouse events */
1911 /* Returns EV_MOUSE if it is a mouse event */
1912 /* Returns EV_NONE if non-blocking or interrupt set and nothing was done */
1915 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
1917 int c;
1918 int flag = 0; /* Return value from select */
1919 #ifdef HAVE_LIBGPM
1920 static struct Gpm_Event ev; /* Mouse event */
1921 #endif
1922 struct timeval time_out;
1923 struct timeval *time_addr = NULL;
1924 static int dirty = 3;
1926 if ((dirty == 3) || is_idle ())
1928 mc_refresh ();
1929 dirty = 1;
1931 else
1932 dirty++;
1934 vfs_timeout_handler ();
1936 /* Ok, we use (event->x < 0) to signal that the event does not contain
1937 a suitable position for the mouse, so we can't use show_mouse_pointer
1938 on it.
1940 if (event->x > 0)
1942 show_mouse_pointer (event->x, event->y);
1943 if (!redo_event)
1944 event->x = -1;
1947 /* Repeat if using mouse */
1948 while (pending_keys == NULL)
1950 int nfd;
1951 fd_set select_set;
1953 FD_ZERO (&select_set);
1954 FD_SET (input_fd, &select_set);
1955 nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1957 #ifdef HAVE_LIBGPM
1958 if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1960 if (gpm_fd >= 0)
1962 FD_SET (gpm_fd, &select_set);
1963 nfd = MAX (nfd, gpm_fd + 1);
1965 else
1967 if (mouse_fd >= 0) /* error indicative */
1969 if (FD_ISSET (mouse_fd, &select_set))
1970 FD_CLR (mouse_fd, &select_set);
1971 mouse_fd = gpm_fd;
1973 /* gpm_fd == -2 means under some X terminal */
1974 if (gpm_fd == -1)
1976 mouse_enabled = FALSE;
1977 use_mouse_p = MOUSE_NONE;
1979 break;
1982 #endif
1984 if (redo_event)
1986 time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC;
1987 time_out.tv_sec = 0;
1989 time_addr = &time_out;
1991 else
1993 int seconds;
1995 seconds = vfs_timeouts ();
1996 time_addr = NULL;
1998 if (seconds != 0)
2000 /* the timeout could be improved and actually be
2001 * the number of seconds until the next vfs entry
2002 * timeouts in the stamp list.
2005 time_out.tv_sec = seconds;
2006 time_out.tv_usec = 0;
2007 time_addr = &time_out;
2011 if (!block || tty_got_winch ())
2013 time_addr = &time_out;
2014 time_out.tv_sec = 0;
2015 time_out.tv_usec = 0;
2018 tty_enable_interrupt_key ();
2019 flag = select (nfd, &select_set, NULL, NULL, time_addr);
2020 tty_disable_interrupt_key ();
2022 /* select timed out: it could be for any of the following reasons:
2023 * redo_event -> it was because of the MOU_REPEAT handler
2024 * !block -> we did not block in the select call
2025 * else -> 10 second timeout to check the vfs status.
2027 if (flag == 0)
2029 if (redo_event)
2030 return EV_MOUSE;
2031 if (!block || tty_got_winch ())
2032 return EV_NONE;
2033 vfs_timeout_handler ();
2035 if (flag == -1 && errno == EINTR)
2036 return EV_NONE;
2038 check_selects (&select_set);
2040 if (FD_ISSET (input_fd, &select_set))
2041 break;
2043 #ifdef HAVE_LIBGPM
2044 if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2046 if (gpm_fd >= 0)
2048 if (FD_ISSET (gpm_fd, &select_set))
2050 int status;
2052 status = Gpm_GetEvent (&ev);
2053 if (status == 1) /* success */
2055 Gpm_FitEvent (&ev);
2056 *event = ev;
2057 return EV_MOUSE;
2059 if (status <= 0) /* connection closed; -1 == error */
2061 if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2062 FD_CLR (mouse_fd, &select_set);
2064 disable_mouse ();
2065 return EV_NONE;
2069 else
2071 if (mouse_fd >= 0) /* error indicative */
2073 if (FD_ISSET (mouse_fd, &select_set))
2074 FD_CLR (mouse_fd, &select_set);
2075 mouse_fd = gpm_fd;
2077 /* gpm_fd == -2 means under some X terminal */
2078 if (gpm_fd == -1)
2080 mouse_enabled = FALSE;
2081 use_mouse_p = MOUSE_NONE;
2083 break;
2086 #endif /* !HAVE_LIBGPM */
2089 #ifndef HAVE_SLANG
2090 flag = is_wintouched (stdscr);
2091 untouchwin (stdscr);
2092 #endif /* !HAVE_SLANG */
2093 c = block ? getch_with_delay () : get_key_code (1);
2095 #ifndef HAVE_SLANG
2096 if (flag > 0)
2097 tty_touch_screen ();
2098 #endif /* !HAVE_SLANG */
2100 if (mouse_enabled && (c == MCKEY_MOUSE
2101 #ifdef KEY_MOUSE
2102 || c == KEY_MOUSE
2103 #endif /* KEY_MOUSE */
2104 || c == MCKEY_EXTENDED_MOUSE))
2106 /* Mouse event. See tickets 2956 and 3954 for extended mode detection. */
2107 gboolean extended = c == MCKEY_EXTENDED_MOUSE;
2109 #ifdef KEY_MOUSE
2110 extended = extended || (c == KEY_MOUSE && xmouse_seq == NULL
2111 && xmouse_extended_seq != NULL);
2112 #endif /* KEY_MOUSE */
2114 xmouse_get_event (event, extended);
2115 c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2117 else if (c == MCKEY_BRACKETED_PASTING_START)
2119 bracketed_pasting_in_progress = TRUE;
2120 c = EV_NONE;
2122 else if (c == MCKEY_BRACKETED_PASTING_END)
2124 bracketed_pasting_in_progress = FALSE;
2125 c = EV_NONE;
2128 return c;
2131 /* --------------------------------------------------------------------------------------------- */
2132 /* Returns a key press, mouse events are discarded */
2135 tty_getch (void)
2137 Gpm_Event ev;
2138 int key;
2140 ev.x = -1;
2141 while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
2143 return key;
2146 /* --------------------------------------------------------------------------------------------- */
2148 char *
2149 learn_key (void)
2151 /* LEARN_TIMEOUT in ms */
2152 #define LEARN_TIMEOUT 200
2154 fd_set Read_FD_Set;
2155 gint64 end_time;
2156 int c;
2157 GString *buffer;
2159 buffer = g_string_sized_new (16);
2161 tty_keypad (FALSE); /* disable interpreting keys by ncurses */
2162 c = tty_lowlevel_getch ();
2163 while (c == -1)
2164 c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */
2165 learn_store_key (buffer, c);
2167 end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC;
2169 tty_nodelay (TRUE);
2170 while (TRUE)
2172 while ((c = tty_lowlevel_getch ()) == -1)
2174 gint64 time_out;
2175 struct timeval tv;
2177 time_out = end_time - g_get_monotonic_time ();
2178 if (time_out <= 0)
2179 break;
2181 tv.tv_sec = time_out / G_USEC_PER_SEC;
2182 tv.tv_usec = time_out % G_USEC_PER_SEC;
2183 FD_ZERO (&Read_FD_Set);
2184 FD_SET (input_fd, &Read_FD_Set);
2185 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &tv);
2187 if (c == -1)
2188 break;
2189 learn_store_key (buffer, c);
2191 tty_keypad (TRUE);
2192 tty_nodelay (FALSE);
2194 return g_string_free (buffer, buffer->len == 0);
2195 #undef LEARN_TIMEOUT
2198 /* --------------------------------------------------------------------------------------------- */
2199 /* xterm and linux console only: set keypad to numeric or application
2200 mode. Only in application keypad mode it's possible to distinguish
2201 the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2203 void
2204 numeric_keypad_mode (void)
2206 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2208 fputs (ESC_STR ">", stdout);
2209 fflush (stdout);
2213 /* --------------------------------------------------------------------------------------------- */
2215 void
2216 application_keypad_mode (void)
2218 if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2220 fputs (ESC_STR "=", stdout);
2221 fflush (stdout);
2225 /* --------------------------------------------------------------------------------------------- */
2227 void
2228 enable_bracketed_paste (void)
2230 printf (ESC_STR "[?2004h");
2231 fflush (stdout);
2234 /* --------------------------------------------------------------------------------------------- */
2236 void
2237 disable_bracketed_paste (void)
2239 printf (ESC_STR "[?2004l");
2240 fflush (stdout);
2241 bracketed_pasting_in_progress = FALSE;
2244 /* --------------------------------------------------------------------------------------------- */