Manual pages: mcmcdiffmceditmcview

root/lib/tty/key.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. select_cmp_by_fd_set
  2. select_cmp_by_fd
  3. add_selects
  4. check_selects
  5. try_channels
  6. create_sequence
  7. define_sequences
  8. init_key_x11
  9. getch_with_delay
  10. xmouse_get_event
  11. get_modifier
  12. push_char
  13. correct_key_code
  14. getch_with_timeout
  15. learn_store_key
  16. k_dispose
  17. key_code_comparator_by_name
  18. key_code_comparator_by_code
  19. sort_key_conv_tab
  20. lookup_keyname
  21. lookup_keycode
  22. init_key
  23. init_key_input_fd
  24. done_key
  25. add_select_channel
  26. delete_select_channel
  27. channels_up
  28. channels_down
  29. tty_keyname_to_keycode
  30. tty_keycode_to_keyname
  31. define_sequence
  32. is_idle
  33. get_key_code
  34. tty_get_event
  35. tty_getch
  36. learn_key
  37. numeric_keypad_mode
  38. application_keypad_mode
  39. enable_bracketed_paste
  40. disable_bracketed_paste

   1 /*
   2    Keyboard support routines.
   3 
   4    Copyright (C) 1994-2026
   5    Free Software Foundation, Inc.
   6 
   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
  15 
  16    This file is part of the Midnight Commander.
  17 
  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.
  22 
  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.
  27 
  28    You should have received a copy of the GNU General Public License
  29    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  30  */
  31 
  32 /** \file key.c
  33  *  \brief Source: keyboard support routines
  34  */
  35 
  36 #include <config.h>
  37 
  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
  50 
  51 #include "lib/global.h"
  52 
  53 #include "lib/vfs/vfs.h"
  54 
  55 #include "tty.h"
  56 #include "tty-internal.h"  // mouse_enabled
  57 #include "mouse.h"
  58 #include "key.h"
  59 
  60 #include "lib/widget.h"  // mc_refresh()
  61 
  62 #ifdef HAVE_TEXTMODE_X11_SUPPORT
  63 #include "x11conn.h"
  64 #endif
  65 
  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
  76 
  77 #ifdef __CYGWIN__
  78 #include <termios.h>
  79 #ifdef HAVE_SYS_IOCTL_H
  80 #include <sys/ioctl.h>
  81 #endif
  82 #endif
  83 
  84 #ifdef __QNXNTO__
  85 #include <dlfcn.h>
  86 #include <Ph.h>
  87 #include <sys/dcmd_chr.h>
  88 #endif
  89 
  90 /*** global variables ****************************************************************************/
  91 
  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 
  98 gboolean bracketed_pasting_in_progress = FALSE;
  99 
 100 /* This table is a mapping between names and the constants we use
 101  * We use this to allow users to define alternate definitions for
 102  * certain keys that may be missing from the terminal database
 103  */
 104 const key_code_name_t key_name_conv_tab[] = {
 105     { ESC_CHAR, "escape", N_ ("Escape"), "Esc" },
 106     /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason
 107        to define f0 as well. Also, it makes Learn keys a bunch of problems :( */
 108     { KEY_F (1), "f1", N_ ("Function key 1"), "F1" },
 109     { KEY_F (2), "f2", N_ ("Function key 2"), "F2" },
 110     { KEY_F (3), "f3", N_ ("Function key 3"), "F3" },
 111     { KEY_F (4), "f4", N_ ("Function key 4"), "F4" },
 112     { KEY_F (5), "f5", N_ ("Function key 5"), "F5" },
 113     { KEY_F (6), "f6", N_ ("Function key 6"), "F6" },
 114     { KEY_F (7), "f7", N_ ("Function key 7"), "F7" },
 115     { KEY_F (8), "f8", N_ ("Function key 8"), "F8" },
 116     { KEY_F (9), "f9", N_ ("Function key 9"), "F9" },
 117     { KEY_F (10), "f10", N_ ("Function key 10"), "F10" },
 118     { KEY_F (11), "f11", N_ ("Function key 11"), "F11" },
 119     { KEY_F (12), "f12", N_ ("Function key 12"), "F12" },
 120     { KEY_F (13), "f13", N_ ("Function key 13"), "F13" },
 121     { KEY_F (14), "f14", N_ ("Function key 14"), "F14" },
 122     { KEY_F (15), "f15", N_ ("Function key 15"), "F15" },
 123     { KEY_F (16), "f16", N_ ("Function key 16"), "F16" },
 124     { KEY_F (17), "f17", N_ ("Function key 17"), "F17" },
 125     { KEY_F (18), "f18", N_ ("Function key 18"), "F18" },
 126     { KEY_F (19), "f19", N_ ("Function key 19"), "F19" },
 127     { KEY_F (20), "f20", N_ ("Function key 20"), "F20" },
 128     { ALT ('\t'), "complete", N_ ("Completion/M-tab"), "Meta-Tab" },
 129     { KEY_BTAB, "backtab", N_ ("BackTab/S-tab"), "Shift-Tab" },
 130     { KEY_BACKSPACE, "backspace", N_ ("Backspace"), "Backspace" },
 131     { KEY_UP, "up", N_ ("Up arrow"), "Up" },
 132     { KEY_DOWN, "down", N_ ("Down arrow"), "Down" },
 133     { KEY_LEFT, "left", N_ ("Left arrow"), "Left" },
 134     { KEY_RIGHT, "right", N_ ("Right arrow"), "Right" },
 135     { KEY_IC, "insert", N_ ("Insert"), "Ins" },
 136     { KEY_DC, "delete", N_ ("Delete"), "Del" },
 137     { KEY_HOME, "home", N_ ("Home"), "Home" },
 138     { KEY_END, "end", N_ ("End key"), "End" },
 139     { KEY_PPAGE, "pgup", N_ ("Page Up"), "PgUp" },
 140     { KEY_NPAGE, "pgdn", N_ ("Page Down"), "PgDn" },
 141     { (int) '/', "kpslash", N_ ("/ on keypad"), "/" },
 142     { KEY_KP_MULTIPLY, "kpasterisk", N_ ("* on keypad"), "*" },
 143     { KEY_KP_SUBTRACT, "kpminus", N_ ("- on keypad"), "-" },
 144     { KEY_KP_ADD, "kpplus", N_ ("+ on keypad"), "+" },
 145 
 146     // From here on, these won't be shown in Learn keys (no space)
 147     { KEY_LEFT, "kpleft", N_ ("Left arrow keypad"), "Left" },
 148     { KEY_RIGHT, "kpright", N_ ("Right arrow keypad"), "Right" },
 149     { KEY_UP, "kpup", N_ ("Up arrow keypad"), "Up" },
 150     { KEY_DOWN, "kpdown", N_ ("Down arrow keypad"), "Down" },
 151     { KEY_HOME, "kphome", N_ ("Home on keypad"), "Home" },
 152     { KEY_END, "kpend", N_ ("End on keypad"), "End" },
 153     { KEY_NPAGE, "kpnpage", N_ ("Page Down keypad"), "PgDn" },
 154     { KEY_PPAGE, "kpppage", N_ ("Page Up keypad"), "PgUp" },
 155     { KEY_IC, "kpinsert", N_ ("Insert on keypad"), "Ins" },
 156     { KEY_DC, "kpdelete", N_ ("Delete on keypad"), "Del" },
 157     { (int) '\n', "kpenter", N_ ("Enter on keypad"), "Enter" },
 158     { KEY_F (21), "f21", N_ ("Function key 21"), "F21" },
 159     { KEY_F (22), "f22", N_ ("Function key 22"), "F22" },
 160     { KEY_F (23), "f23", N_ ("Function key 23"), "F23" },
 161     { KEY_F (24), "f24", N_ ("Function key 24"), "F24" },
 162     { KEY_A1, "a1", N_ ("A1 key"), "A1" },
 163     { KEY_C1, "c1", N_ ("C1 key"), "C1" },
 164 
 165     // Alternative label
 166     { ESC_CHAR, "esc", N_ ("Escape"), "Esc" },
 167     { KEY_BACKSPACE, "bs", N_ ("Backspace"), "Bakspace" },
 168     { KEY_IC, "ins", N_ ("Insert"), "Ins" },
 169     { KEY_DC, "del", N_ ("Delete"), "Del" },
 170     { (int) '*', "asterisk", N_ ("Asterisk"), "*" },
 171     { (int) '-', "minus", N_ ("Minus"), "-" },
 172     { (int) '+', "plus", N_ ("Plus"), "+" },
 173     { (int) '.', "dot", N_ ("Dot"), "." },
 174     { (int) '<', "lt", N_ ("Less than"), "<" },
 175     { (int) '>', "gt", N_ ("Great than"), ">" },
 176     { (int) '=', "equal", N_ ("Equal"), "=" },
 177     { (int) ',', "comma", N_ ("Comma"), "," },
 178     { (int) '\'', "apostrophe", N_ ("Apostrophe"), "\'" },
 179     { (int) ':', "colon", N_ ("Colon"), ":" },
 180     { (int) ';', "semicolon", N_ ("Semicolon"), ";" },
 181     { (int) '!', "exclamation", N_ ("Exclamation mark"), "!" },
 182     { (int) '?', "question", N_ ("Question mark"), "?" },
 183     { (int) '&', "ampersand", N_ ("Ampersand"), "&" },
 184     { (int) '$', "dollar", N_ ("Dollar sign"), "$" },
 185     { (int) '"', "quota", N_ ("Quotation mark"), "\"" },
 186     { (int) '%', "percent", N_ ("Percent sign"), "%" },
 187     { (int) '^', "caret", N_ ("Caret"), "^" },
 188     { (int) '~', "tilda", N_ ("Tilda"), "~" },
 189     { (int) '`', "prime", N_ ("Prime"), "`" },
 190     { (int) '_', "underline", N_ ("Underline"), "_" },
 191     { (int) '_', "understrike", N_ ("Understrike"), "_" },
 192     { (int) '|', "pipe", N_ ("Pipe"), "|" },
 193     { (int) '(', "lparenthesis", N_ ("Left parenthesis"), "(" },
 194     { (int) ')', "rparenthesis", N_ ("Right parenthesis"), ")" },
 195     { (int) '[', "lbracket", N_ ("Left bracket"), "[" },
 196     { (int) ']', "rbracket", N_ ("Right bracket"), "]" },
 197     { (int) '{', "lbrace", N_ ("Left brace"), "{" },
 198     { (int) '}', "rbrace", N_ ("Right brace"), "}" },
 199     { (int) '\n', "enter", N_ ("Enter"), "Enter" },
 200     { (int) '\t', "tab", N_ ("Tab key"), "Tab" },
 201     { (int) ' ', "space", N_ ("Space key"), "Space" },
 202     { (int) '/', "slash", N_ ("Slash key"), "/" },
 203     { (int) '\\', "backslash", N_ ("Backslash key"), "\\" },
 204     { (int) '#', "number", N_ ("Number sign #"), "#" },
 205     { (int) '#', "hash", N_ ("Number sign #"), "#" },
 206     // TRANSLATORS: Please translate as in "at sign" (@).
 207     { (int) '@', "at", N_ ("At sign"), "@" },
 208 
 209     // meta keys
 210     { KEY_M_CTRL, "control", N_ ("Ctrl"), "C" },
 211     { KEY_M_CTRL, "ctrl", N_ ("Ctrl"), "C" },
 212     { KEY_M_ALT, "meta", N_ ("Alt"), "M" },
 213     { KEY_M_ALT, "alt", N_ ("Alt"), "M" },
 214     { KEY_M_ALT, "ralt", N_ ("Alt"), "M" },
 215     { KEY_M_SHIFT, "shift", N_ ("Shift"), "S" },
 216 
 217     { 0, NULL, NULL, NULL },
 218 };
 219 
 220 /*** file scope macro definitions ****************************************************************/
 221 
 222 #define MC_USEC_PER_MSEC 1000
 223 
 224 /* The maximum sequence length (32 + null terminator) */
 225 #define SEQ_BUFFER_LEN 33
 226 
 227 /*** file scope type declarations ****************************************************************/
 228 
 229 /* Linux console keyboard modifiers */
 230 typedef enum
 231 {
 232     SHIFT_PRESSED = (1 << 0),
 233     ALTR_PRESSED = (1 << 1),
 234     CONTROL_PRESSED = (1 << 2),
 235     ALTL_PRESSED = (1 << 3)
 236 } mod_pressed_t;
 237 
 238 typedef struct key_def
 239 {
 240     char ch;   // Holds the matching char code
 241     int code;  // The code returned, valid if child == NULL
 242     struct key_def *next;
 243     struct key_def *child;  // sequence continuation
 244     int action;             /* optional action to be done. Now used only
 245                                to mark that we are just after the first
 246                                Escape */
 247 } key_def;
 248 
 249 typedef struct
 250 {
 251     int code;
 252     const char *seq;
 253     int action;
 254 } key_define_t;
 255 
 256 /* File descriptor monitoring add/remove routines */
 257 typedef struct
 258 {
 259     int fd;
 260     select_fn callback;
 261     void *info;
 262 } select_t;
 263 
 264 typedef enum KeySortType
 265 {
 266     KEY_NOSORT = 0,
 267     KEY_SORTBYNAME,
 268     KEY_SORTBYCODE
 269 } KeySortType;
 270 
 271 #ifdef __QNXNTO__
 272 typedef int (*ph_dv_f) (void *, void *);
 273 typedef int (*ph_ov_f) (void *);
 274 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
 275 #endif
 276 
 277 /*** forward declarations (file scope functions) *************************************************/
 278 
 279 /*** file scope variables ************************************************************************/
 280 
 281 static key_define_t mc_default_keys[] = {
 282     { ESC_CHAR, ESC_STR, MCKEY_ESCAPE },
 283     { ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION },
 284     { MCKEY_BRACKETED_PASTING_START, ESC_STR "[200~", MCKEY_NOACTION },
 285     { MCKEY_BRACKETED_PASTING_END, ESC_STR "[201~", MCKEY_NOACTION },
 286     { 0, NULL, MCKEY_NOACTION },
 287 };
 288 
 289 /* Broken terminfo and termcap databases on xterminals */
 290 static key_define_t xterm_key_defines[] = {
 291     { KEY_F (1), ESC_STR "OP", MCKEY_NOACTION },
 292     { KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION },
 293     { KEY_F (3), ESC_STR "OR", MCKEY_NOACTION },
 294     { KEY_F (4), ESC_STR "OS", MCKEY_NOACTION },
 295     { KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION },
 296     { KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION },
 297     { KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION },
 298     { KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION },
 299     { KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION },
 300     { KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION },
 301     { KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION },
 302     { KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION },
 303     { KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION },
 304     { KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION },
 305 
 306     // old xterm Shift-arrows
 307     { KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION },
 308     { KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION },
 309     { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION },
 310     { KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION },
 311 
 312     // new xterm Shift-arrows
 313     { KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION },
 314     { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION },
 315     { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION },
 316     { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION },
 317 
 318     // more xterm keys with modifiers
 319     { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION },
 320     { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION },
 321     { KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION },
 322     { KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION },
 323     { KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION },
 324     { KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION },
 325     { KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION },
 326     { KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION },
 327     { KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION },
 328     { KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION },
 329     { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION },
 330     { KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION },
 331     { KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION },
 332     { KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION },
 333     { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION },
 334     { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION },
 335     { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION },
 336     { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION },
 337     { KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION },
 338 
 339     // putty
 340     { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION },
 341     { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION },
 342     { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION },
 343     { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION },
 344 
 345     // putty alt-arrow keys
 346     // removed as source esc esc esc trouble
 347     /*
 348        { KEY_M_ALT | KEY_UP,    ESC_STR ESC_STR "OA", MCKEY_NOACTION },
 349        { KEY_M_ALT | KEY_DOWN,  ESC_STR ESC_STR "OB", MCKEY_NOACTION },
 350        { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION },
 351        { KEY_M_ALT | KEY_LEFT,  ESC_STR ESC_STR "OD", MCKEY_NOACTION },
 352        { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION },
 353        { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION },
 354        { KEY_M_ALT | KEY_HOME,  ESC_STR ESC_STR "[1~", MCKEY_NOACTION },
 355        { KEY_M_ALT | KEY_END,   ESC_STR ESC_STR "[4~", MCKEY_NOACTION },
 356 
 357        { KEY_M_CTRL | KEY_M_ALT | KEY_UP,    ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION },
 358        { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN,  ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION },
 359        { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION },
 360        { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT,  ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION },
 361 
 362        { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION },
 363        { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION },
 364        { KEY_M_CTRL | KEY_M_ALT | KEY_HOME,  ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION },
 365        { KEY_M_CTRL | KEY_M_ALT | KEY_END,   ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION },
 366      */
 367     // xterm alt-arrow keys
 368     { KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION },
 369     { KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION },
 370     { KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION },
 371     { KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION },
 372     { KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION },
 373     { KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION },
 374     { KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION },
 375     { KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION },
 376     { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION },
 377     { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION },
 378     { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION },
 379     { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION },
 380     { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION },
 381     { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION },
 382     { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION },
 383     { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION },
 384 
 385     { KEY_M_SHIFT | KEY_M_ALT | KEY_UP, ESC_STR "[1;4A", MCKEY_NOACTION },
 386     { KEY_M_SHIFT | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;4B", MCKEY_NOACTION },
 387     { KEY_M_SHIFT | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;4C", MCKEY_NOACTION },
 388     { KEY_M_SHIFT | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;4D", MCKEY_NOACTION },
 389 
 390     // rxvt keys with modifiers
 391     { KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },
 392     { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },
 393     { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },
 394     { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },
 395     { KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION },
 396     { KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION },
 397     { KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION },
 398     { KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION },
 399     { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION },
 400     { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION },
 401     { KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION },
 402     { KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION },
 403     { KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION },
 404     { KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION },
 405     { KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION },
 406     { KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION },
 407     { KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION },
 408 
 409     // konsole keys with modifiers
 410     { KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION },
 411     { KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION },
 412 
 413     // gnome-terminal
 414     { KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION },
 415     { KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION },
 416     { KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION },
 417     { KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION },
 418     { KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION },
 419     { KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION },
 420     { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION },
 421     { KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION },
 422     { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION },
 423     { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION },
 424     { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION },
 425     { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION },
 426 
 427     // gnome-terminal - application mode
 428     { KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION },
 429     { KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION },
 430     { KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION },
 431     { KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION },
 432     { KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION },
 433     { KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION },
 434     { KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION },
 435     { KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION },
 436 
 437     // iTerm
 438     { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION },
 439     { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION },
 440 
 441     // putty
 442     { KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION },
 443     { KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION },
 444 
 445     // keypad keys
 446     { KEY_IC, ESC_STR "Op", MCKEY_NOACTION },
 447     { KEY_DC, ESC_STR "On", MCKEY_NOACTION },
 448     { '/', ESC_STR "Oo", MCKEY_NOACTION },
 449     { '\n', ESC_STR "OM", MCKEY_NOACTION },
 450 
 451     { 0, NULL, MCKEY_NOACTION },
 452 };
 453 
 454 /* qansi-m terminals have a much more key combinations,
 455    which are undefined in termcap/terminfo */
 456 static key_define_t qansi_key_defines[] = {
 457     // qansi-m terminal
 458     { KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION },        // Ctrl-PgDown
 459     { KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION },        // Ctrl-PgUp
 460     { KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION },         // Ctrl-Home
 461     { KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION },          // Ctrl-End
 462     { KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION },           // Ctrl-Insert
 463     { KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION },           // Ctrl-Delete
 464     { KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION },         // Ctrl-Left
 465     { KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION },        // Ctrl-Right
 466     { KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION },         // Ctrl-Down
 467     { KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION },           // Ctrl-Up
 468     { KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION },       // Ctrl-Gr-Plus
 469     { KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION },  // Ctrl-Gr-Minus
 470     { KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION },             // Ctrl-Tab
 471     { KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION },            // Shift-Tab
 472     { KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION },       // Ctrl-F1
 473     { KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION },       // Ctrl-F2
 474     { KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION },       // Ctrl-F3
 475     { KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION },       // Ctrl-F4
 476     { KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION },       // Ctrl-F5
 477     { KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION },       // Ctrl-F6
 478     { KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION },       // Ctrl-F7
 479     { KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION },       // Ctrl-F8
 480     { KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION },       // Ctrl-F9
 481     { KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION },     // Ctrl-F10
 482     { KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION },     // Ctrl-F11
 483     { KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION },     // Ctrl-F12
 484     { KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION },       // Alt-F1
 485     { KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION },       // Alt-F2
 486     { KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION },       // Alt-F3
 487     { KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION },       // Alt-F4
 488     { KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION },       // Alt-F5
 489     { KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION },       // Alt-F6
 490     { KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION },       // Alt-F7
 491     { KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION },       // Alt-F8
 492     { KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION },       // Alt-F9
 493     { KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION },      // Alt-F10
 494     { KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION },      // Alt-F11
 495     { KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION },      // Alt-F12
 496     { KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION },               // Alt-a
 497     { KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION },               // Alt-b
 498     { KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION },               // Alt-c
 499     { KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION },               // Alt-d
 500     { KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION },               // Alt-e
 501     { KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION },               // Alt-f
 502     { KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION },               // Alt-g
 503     { KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION },               // Alt-h
 504     { KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION },               // Alt-i
 505     { KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION },               // Alt-j
 506     { KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION },               // Alt-k
 507     { KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION },               // Alt-l
 508     { KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION },               // Alt-m
 509     { KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION },               // Alt-n
 510     { KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION },               // Alt-o
 511     { KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION },               // Alt-p
 512     { KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION },               // Alt-q
 513     { KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION },               // Alt-r
 514     { KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION },               // Alt-s
 515     { KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION },               // Alt-t
 516     { KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION },               // Alt-u
 517     { KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION },               // Alt-v
 518     { KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION },               // Alt-w
 519     { KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION },               // Alt-x
 520     { KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION },               // Alt-y
 521     { KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION },               // Alt-z
 522     { KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION },               // Gr-Minus
 523     { KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION },                    // Gr-Plus
 524     { 0, NULL, MCKEY_NOACTION },
 525 };
 526 
 527 /* This holds all the key definitions */
 528 static key_def *keys = NULL;
 529 
 530 static int input_fd;
 531 static int disabled_channels = 0;  // Disable channels checking
 532 
 533 static GSList *select_list = NULL;
 534 
 535 static int seq_buffer[SEQ_BUFFER_LEN];
 536 static int *seq_append = NULL;
 537 
 538 static int *pending_keys = NULL;
 539 
 540 #ifdef __QNXNTO__
 541 ph_dv_f ph_attach;
 542 ph_ov_f ph_input_group;
 543 ph_pqc_f ph_query_cursor;
 544 #endif
 545 
 546 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 547 static Display *x11_display;
 548 static Window x11_window;
 549 #endif
 550 
 551 static KeySortType has_been_sorted = KEY_NOSORT;
 552 
 553 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
 554 
 555 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
 556 
 557 /* --------------------------------------------------------------------------------------------- */
 558 /*** file scope functions ************************************************************************/
 559 /* --------------------------------------------------------------------------------------------- */
 560 
 561 static int
 562 select_cmp_by_fd_set (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 563 {
 564     const select_t *s = (const select_t *) a;
 565     const fd_set *f = (const fd_set *) b;
 566 
 567     return (FD_ISSET (s->fd, f) ? 0 : 1);
 568 }
 569 
 570 /* --------------------------------------------------------------------------------------------- */
 571 
 572 static int
 573 select_cmp_by_fd (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 574 {
 575     const select_t *s = (const select_t *) a;
 576     const int fd = GPOINTER_TO_INT (b);
 577 
 578     return (s->fd == fd ? 0 : 1);
 579 }
 580 
 581 /* --------------------------------------------------------------------------------------------- */
 582 
 583 static int
 584 add_selects (fd_set *select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 585 {
 586     int top_fd = 0;
 587 
 588     if (disabled_channels == 0)
 589     {
 590         GSList *s;
 591 
 592         for (s = select_list; s != NULL; s = g_slist_next (s))
 593         {
 594             select_t *p = (select_t *) s->data;
 595 
 596             FD_SET (p->fd, select_set);
 597             if (p->fd > top_fd)
 598                 top_fd = p->fd;
 599         }
 600     }
 601 
 602     return top_fd;
 603 }
 604 
 605 /* --------------------------------------------------------------------------------------------- */
 606 
 607 static void
 608 check_selects (fd_set *select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 609 {
 610     while (disabled_channels == 0)
 611     {
 612         GSList *s;
 613         select_t *p;
 614 
 615         s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set);
 616         if (s == NULL)
 617             break;
 618 
 619         p = (select_t *) s->data;
 620         FD_CLR (p->fd, select_set);
 621         p->callback (p->fd, p->info);
 622     }
 623 }
 624 
 625 /* --------------------------------------------------------------------------------------------- */
 626 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
 627 
 628 static void
 629 try_channels (gboolean set_timeout)
     /* [previous][next][first][last][top][bottom][index][help]  */
 630 {
 631     struct timeval time_out;
 632     static fd_set select_set;
 633 
 634     while (TRUE)
 635     {
 636         struct timeval *timeptr = NULL;
 637         int maxfdp, v;
 638 
 639         FD_ZERO (&select_set);
 640         FD_SET (input_fd, &select_set);  // Add stdin
 641         maxfdp = MAX (add_selects (&select_set), input_fd);
 642 
 643         if (set_timeout)
 644         {
 645             time_out.tv_sec = 0;
 646             time_out.tv_usec = 100 * MC_USEC_PER_MSEC;
 647             timeptr = &time_out;
 648         }
 649 
 650         v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
 651         if (v > 0)
 652         {
 653             check_selects (&select_set);
 654             if (FD_ISSET (input_fd, &select_set))
 655                 break;
 656         }
 657     }
 658 }
 659 
 660 /* --------------------------------------------------------------------------------------------- */
 661 
 662 static key_def *
 663 create_sequence (const char *seq, int code, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 664 {
 665     key_def *base, *attach;
 666 
 667     for (base = attach = NULL; *seq != '\0'; seq++)
 668     {
 669         key_def *p;
 670 
 671         p = g_new (key_def, 1);
 672         if (base == NULL)
 673             base = p;
 674         if (attach != NULL)
 675             attach->child = p;
 676 
 677         p->ch = *seq;
 678         p->code = code;
 679         p->child = NULL;
 680         p->next = NULL;
 681         p->action = seq[1] == '\0' ? action : MCKEY_NOACTION;
 682         attach = p;
 683     }
 684     return base;
 685 }
 686 
 687 /* --------------------------------------------------------------------------------------------- */
 688 
 689 static void
 690 define_sequences (const key_define_t *kd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 691 {
 692     int i;
 693 
 694     for (i = 0; kd[i].code != 0; i++)
 695         define_sequence (kd[i].code, kd[i].seq, kd[i].action);
 696 }
 697 
 698 /* --------------------------------------------------------------------------------------------- */
 699 
 700 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 701 static void
 702 init_key_x11 (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 703 {
 704     if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
 705     {
 706         x11_display = mc_XOpenDisplay (0);
 707 
 708         if (x11_display != NULL)
 709             x11_window = DefaultRootWindow (x11_display);
 710     }
 711 }
 712 #endif
 713 
 714 /* --------------------------------------------------------------------------------------------- */
 715 /* Workaround for System V Curses vt100 bug */
 716 
 717 static int
 718 getch_with_delay (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 719 {
 720     int c;
 721 
 722     /* This routine could be used on systems without mouse support,
 723        so we need to do the select check :-( */
 724     while (TRUE)
 725     {
 726         if (pending_keys == NULL)
 727             try_channels (FALSE);
 728 
 729         // Try to get a character
 730         c = get_key_code (0);
 731         if (c != -1)
 732             break;
 733 
 734         // Failed -> wait 0.1 secs and try again
 735         try_channels (TRUE);
 736     }
 737 
 738     // Success -> return the character
 739     return c;
 740 }
 741 
 742 /* --------------------------------------------------------------------------------------------- */
 743 
 744 static void
 745 xmouse_get_event (Gpm_Event *ev, gboolean extended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 746 {
 747     static gint64 tv1 = 0;  // Force first click as single
 748     static int clicks = 0;
 749     static int last_btn = 0;
 750     int btn;
 751 
 752     // Decode Xterm mouse information to a GPM style event
 753 
 754     if (!extended)
 755     {
 756         // Variable btn has following meaning:
 757         // 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up
 758         btn = tty_lowlevel_getch () - 32;
 759         // Coordinates are 33-based
 760         // Transform them to 1-based
 761         ev->x = tty_lowlevel_getch () - 32;
 762         ev->y = tty_lowlevel_getch () - 32;
 763     }
 764     else
 765     {
 766         /* SGR 1006 extension (e.g. "\e[<0;12;300M"):
 767            - Numbers are encoded in decimal to make it ASCII-safe
 768            and to overcome the limit of 223 columns/rows.
 769            - Mouse release is encoded by trailing 'm' rather than 'M'
 770            so that the released button can be reported.
 771            - Numbers are no longer offset by 32. */
 772         char c;
 773 
 774         btn = ev->x = ev->y = 0;
 775         ev->type = 0;  // In case we return on an invalid sequence
 776 
 777         while ((c = tty_lowlevel_getch ()) != ';')
 778         {
 779             if (c < '0' || c > '9')
 780                 return;
 781             btn = 10 * btn + (c - '0');
 782         }
 783         while ((c = tty_lowlevel_getch ()) != ';')
 784         {
 785             if (c < '0' || c > '9')
 786                 return;
 787             ev->x = 10 * ev->x + (c - '0');
 788         }
 789         while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
 790         {
 791             if (c < '0' || c > '9')
 792                 return;
 793             ev->y = 10 * ev->y + (c - '0');
 794         }
 795         /* Legacy mouse protocol doesn't tell which button was released,
 796            conveniently all of mc's widgets are written not to rely on this
 797            information. With the SGR extension the released button becomes
 798            known, but for the sake of simplicity we just ignore it. */
 799         if (c == 'm')
 800             btn = 3;
 801     }
 802 
 803     // There seems to be no way of knowing which button was released
 804     // So we assume all the buttons were released
 805 
 806     if (btn == 3)
 807     {
 808         if (last_btn != 0)
 809         {
 810             if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
 811             {
 812                 // FIXME: DIRTY HACK
 813                 // don't generate GPM_UP after mouse wheel
 814                 // need for menu event handling
 815                 ev->type = 0;
 816                 tv1 = 0;
 817             }
 818             else
 819             {
 820                 ev->type = GPM_UP | (GPM_SINGLE << clicks);
 821             }
 822             ev->buttons = 0;
 823             last_btn = 0;
 824         }
 825         else
 826         {
 827             // Bogus event, maybe mouse wheel
 828             ev->type = 0;
 829         }
 830     }
 831     else
 832     {
 833         gint64 tv2;
 834 
 835         tv2 = g_get_monotonic_time ();
 836         if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC)
 837         {
 838             clicks++;
 839             clicks %= 3;
 840         }
 841         else
 842             clicks = 0;
 843 
 844         if (btn >= 32 && btn <= 34)
 845         {
 846             btn -= 32;
 847             ev->type = GPM_DRAG;
 848         }
 849         else
 850         {
 851             ev->type = GPM_DOWN;
 852             tv1 = g_get_monotonic_time ();
 853         }
 854 
 855         switch (btn)
 856         {
 857         case 0:
 858             ev->buttons = GPM_B_LEFT;
 859             break;
 860         case 1:
 861             ev->buttons = GPM_B_MIDDLE;
 862             break;
 863         case 2:
 864             ev->buttons = GPM_B_RIGHT;
 865             break;
 866         case 64:
 867             ev->buttons = GPM_B_UP;
 868             clicks = 0;
 869             break;
 870         case 65:
 871             ev->buttons = GPM_B_DOWN;
 872             clicks = 0;
 873             break;
 874         default:
 875             // Nothing
 876             ev->type = 0;
 877             ev->buttons = 0;
 878             break;
 879         }
 880         ev->type |= (GPM_SINGLE << clicks);
 881         last_btn = ev->buttons;
 882     }
 883 }
 884 
 885 /* --------------------------------------------------------------------------------------------- */
 886 /**
 887  * Get modifier state (shift, alt, ctrl) for the last key pressed.
 888  * We are assuming that the state didn't change since the key press.
 889  * This is only correct if get_modifier() is called very fast after
 890  * the input was received, so that the user didn't release the
 891  * modifier keys yet.
 892  */
 893 
 894 static int
 895 get_modifier (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 896 {
 897     int result = 0;
 898 #ifdef __QNXNTO__
 899     static int in_photon = 0;
 900     static int ph_ig = 0;
 901 #endif
 902 
 903 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 904     if (x11_window != 0)
 905     {
 906         Window root, child;
 907         int root_x, root_y;
 908         int win_x, win_y;
 909         unsigned int mask;
 910 
 911         mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x, &root_y, &win_x, &win_y,
 912                           &mask);
 913 
 914         if ((mask & ShiftMask) != 0)
 915             result |= KEY_M_SHIFT;
 916         if ((mask & ControlMask) != 0)
 917             result |= KEY_M_CTRL;
 918         return result;
 919     }
 920 #endif
 921 
 922 #ifdef __QNXNTO__
 923     if (in_photon == 0)
 924     {
 925         // First time here, let's load Photon library and attach to Photon
 926         in_photon = -1;
 927 
 928         if (getenv ("PHOTON2_PATH") != NULL)
 929         {
 930             // QNX 6.x has no support for RTLD_LAZY
 931             void *ph_handle;
 932 
 933             ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
 934             if (ph_handle != NULL)
 935             {
 936                 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
 937                 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
 938                 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
 939                 if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL)
 940                     && (*ph_attach) (0, 0) != NULL)
 941                 {
 942                     // Attached
 943                     ph_ig = (*ph_input_group) (0);
 944                     in_photon = 1;
 945                 }
 946             }
 947         }
 948     }
 949     // We do not have Photon running. Assume we are in text console or xterm
 950     if (in_photon == -1)
 951     {
 952         int mod_status;
 953         int shift_ext_status;
 954 
 955         if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL)
 956             == -1)
 957             return 0;
 958 
 959         shift_ext_status = mod_status & 0xffffff00UL;
 960         mod_status &= 0x7f;
 961         if ((mod_status & _LINESTATUS_CON_ALT) != 0)
 962             result |= KEY_M_ALT;
 963         if ((mod_status & _LINESTATUS_CON_CTRL) != 0)
 964             result |= KEY_M_CTRL;
 965         if ((mod_status & _LINESTATUS_CON_SHIFT) != 0 || (shift_ext_status & 0x00000800UL) != 0)
 966             result |= KEY_M_SHIFT;
 967     }
 968     else
 969     {
 970         PhCursorInfo_t cursor_info;
 971 
 972         (*ph_query_cursor) (ph_ig, &cursor_info);
 973         if ((cursor_info.key_mods & 0x04) != 0)
 974             result |= KEY_M_ALT;
 975         if ((cursor_info.key_mods & 0x02) != 0)
 976             result |= KEY_M_CTRL;
 977         if ((cursor_info.key_mods & 0x01) != 0)
 978             result |= KEY_M_SHIFT;
 979     }
 980 #endif
 981 
 982 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
 983     {
 984         unsigned char modifiers = 6;
 985 
 986         if (ioctl (0, TIOCLINUX, &modifiers) < 0)
 987             return 0;
 988 
 989         // Translate Linux modifiers into mc modifiers
 990         if ((modifiers & SHIFT_PRESSED) != 0)
 991             result |= KEY_M_SHIFT;
 992         if ((modifiers & (ALTL_PRESSED | ALTR_PRESSED)) != 0)
 993             result |= KEY_M_ALT;
 994         if ((modifiers & CONTROL_PRESSED) != 0)
 995             result |= KEY_M_CTRL;
 996     }
 997 #endif
 998 
 999     return result;
1000 }
1001 
1002 /* --------------------------------------------------------------------------------------------- */
1003 
1004 static gboolean
1005 push_char (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1006 {
1007     gboolean ret = FALSE;
1008 
1009     if (seq_append == NULL)
1010         seq_append = seq_buffer;
1011 
1012     if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
1013     {
1014         *(seq_append++) = c;
1015         *seq_append = '\0';
1016         ret = TRUE;
1017     }
1018 
1019     return ret;
1020 }
1021 
1022 /* --------------------------------------------------------------------------------------------- */
1023 /* Apply corrections for the keycode generated in get_key_code() */
1024 
1025 static int
1026 correct_key_code (int code)
     /* [previous][next][first][last][top][bottom][index][help]  */
1027 {
1028     unsigned int c = code & ~KEY_M_MASK;   // code without modifier
1029     unsigned int mod = code & KEY_M_MASK;  // modifier
1030 #ifdef __QNXNTO__
1031     unsigned int qmod; /* bunch of the QNX console
1032                           modifiers needs unchanged */
1033 #endif
1034 
1035     /*
1036      * Add key modifiers directly from X11 or OS.
1037      * Ordinary characters only get modifiers from sequences.
1038      */
1039     if (c < 32 || c >= 256)
1040         mod |= get_modifier ();
1041 
1042     // This is needed if the newline is reported as carriage return
1043     if (c == '\r')
1044         c = '\n';
1045 
1046     // This is reported to be useful on AIX
1047     if (c == KEY_SCANCEL)
1048         c = '\t';
1049 
1050     // Convert Back Tab to Shift+Tab
1051     if (c == KEY_BTAB)
1052     {
1053         c = '\t';
1054         mod = KEY_M_SHIFT;
1055     }
1056 
1057     // F0 is the same as F10 for out purposes
1058     if (c == KEY_F (0))
1059         c = KEY_F (10);
1060 
1061     /*
1062      * We are not interested if Ctrl was pressed when entering control
1063      * characters, so assume that it was.  When checking for such keys,
1064      * XCTRL macro should be used.  In some cases, we are interested,
1065      * e.g. to distinguish Ctrl-Enter from Enter.
1066      */
1067     if (c == '\b')
1068     {
1069         // Special case for backspase ('\b' < 32)
1070         c = KEY_BACKSPACE;
1071         mod &= ~KEY_M_CTRL;
1072     }
1073     else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
1074         mod |= KEY_M_CTRL;
1075 
1076 #ifdef __QNXNTO__
1077     qmod = get_modifier ();
1078 
1079     if (c == 127 && mod == 0)
1080     {
1081         // Add Ctrl/Alt/Shift-BackSpace
1082         mod |= get_modifier ();
1083         c = KEY_BACKSPACE;
1084     }
1085 
1086     if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1087     {
1088         // Add Shift-Insert on key pad
1089         mod = KEY_M_SHIFT;
1090         c = KEY_IC;
1091     }
1092 
1093     if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1094     {
1095         // Add Shift-Del on key pad
1096         mod = KEY_M_SHIFT;
1097         c = KEY_DC;
1098     }
1099 #endif
1100 
1101     // Unrecognized 0177 is delete (preserve Ctrl)
1102     if (c == 0177)
1103         c = KEY_BACKSPACE;
1104 
1105 #if 0
1106     // Unrecognized Ctrl-d is delete 
1107     if (c == 'd' & 31)
1108     {
1109         c = KEY_DC;
1110         mod &= ~KEY_M_CTRL;
1111     }
1112 
1113     // Unrecognized Ctrl-h is backspace 
1114     if (c == 'h' & 31)
1115     {
1116         c = KEY_BACKSPACE;
1117         mod &= ~KEY_M_CTRL;
1118     }
1119 #endif
1120 
1121     // Shift+BackSpace is backspace
1122     if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0)
1123         mod &= ~KEY_M_SHIFT;
1124 
1125     // Convert Shift+Fn to F(n+10)
1126     if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT) != 0)
1127         c += 10;
1128 
1129     // Remove Shift information from function keys
1130     if (c >= KEY_F (1) && c <= KEY_F (20))
1131         mod &= ~KEY_M_SHIFT;
1132 
1133     if (!mc_global.tty.alternate_plus_minus)
1134         switch (c)
1135         {
1136         case KEY_KP_ADD:
1137             c = '+';
1138             break;
1139         case KEY_KP_SUBTRACT:
1140             c = '-';
1141             break;
1142         case KEY_KP_MULTIPLY:
1143             c = '*';
1144             break;
1145         default:
1146             break;
1147         }
1148 
1149     return (mod | c);
1150 }
1151 
1152 /* --------------------------------------------------------------------------------------------- */
1153 
1154 static int
1155 getch_with_timeout (unsigned int delay_us)
     /* [previous][next][first][last][top][bottom][index][help]  */
1156 {
1157     fd_set Read_FD_Set;
1158     int c;
1159     struct timeval time_out;
1160 
1161     time_out.tv_sec = delay_us / G_USEC_PER_SEC;
1162     time_out.tv_usec = delay_us % G_USEC_PER_SEC;
1163     tty_nodelay (TRUE);
1164     FD_ZERO (&Read_FD_Set);
1165     FD_SET (input_fd, &Read_FD_Set);
1166     select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1167     c = tty_lowlevel_getch ();
1168     tty_nodelay (FALSE);
1169     return c;
1170 }
1171 
1172 /* --------------------------------------------------------------------------------------------- */
1173 
1174 static void
1175 learn_store_key (GString *buffer, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1176 {
1177     if (c == ESC_CHAR)
1178         g_string_append (buffer, "\\e");
1179     else if (c < ' ')
1180     {
1181         g_string_append_c (buffer, '^');
1182         g_string_append_c (buffer, c + 'a' - 1);
1183     }
1184     else if (c == '^')
1185         g_string_append (buffer, "^^");
1186     else
1187         g_string_append_c (buffer, (char) c);
1188 }
1189 
1190 /* --------------------------------------------------------------------------------------------- */
1191 
1192 static void
1193 k_dispose (key_def *k)
     /* [previous][next][first][last][top][bottom][index][help]  */
1194 {
1195     if (k != NULL)
1196     {
1197         k_dispose (k->child);
1198         k_dispose (k->next);
1199         g_free (k);
1200     }
1201 }
1202 
1203 /* --------------------------------------------------------------------------------------------- */
1204 
1205 static int
1206 key_code_comparator_by_name (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1207 {
1208     const key_code_name_t *n1 = *(const key_code_name_t *const *) p1;
1209     const key_code_name_t *n2 = *(const key_code_name_t *const *) p2;
1210 
1211     return g_ascii_strcasecmp (n1->name, n2->name);
1212 }
1213 
1214 /* --------------------------------------------------------------------------------------------- */
1215 
1216 static int
1217 key_code_comparator_by_code (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1218 {
1219     const key_code_name_t *n1 = *(const key_code_name_t *const *) p1;
1220     const key_code_name_t *n2 = *(const key_code_name_t *const *) p2;
1221 
1222     return n1->code - n2->code;
1223 }
1224 
1225 /* --------------------------------------------------------------------------------------------- */
1226 
1227 static inline void
1228 sort_key_conv_tab (enum KeySortType type_sort)
     /* [previous][next][first][last][top][bottom][index][help]  */
1229 {
1230     if (has_been_sorted != type_sort)
1231     {
1232         size_t i;
1233 
1234         for (i = 0; i < key_conv_tab_size; i++)
1235             key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1236 
1237         if (type_sort == KEY_SORTBYNAME)
1238             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1239                    &key_code_comparator_by_name);
1240         else if (type_sort == KEY_SORTBYCODE)
1241             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1242                    &key_code_comparator_by_code);
1243 
1244         has_been_sorted = type_sort;
1245     }
1246 }
1247 
1248 /* --------------------------------------------------------------------------------------------- */
1249 
1250 static int
1251 lookup_keyname (const char *name, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1252 {
1253     if (name[0] != '\0')
1254     {
1255         const key_code_name_t key = { 0, name, NULL, NULL };
1256         const key_code_name_t *keyp = &key;
1257         const key_code_name_t **res;
1258 
1259         if (name[1] == '\0')
1260         {
1261             *idx = -1;
1262             return (int) name[0];
1263         }
1264 
1265         sort_key_conv_tab (KEY_SORTBYNAME);
1266 
1267         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1268                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1269 
1270         if (res != NULL)
1271         {
1272             *idx = (int) (res - key_conv_tab_sorted);
1273             return (*res)->code;
1274         }
1275     }
1276 
1277     *idx = -1;
1278     return 0;
1279 }
1280 
1281 /* --------------------------------------------------------------------------------------------- */
1282 
1283 static gboolean
1284 lookup_keycode (const int code, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1285 {
1286     if (code != 0)
1287     {
1288         const key_code_name_t key = { code, NULL, NULL, NULL };
1289         const key_code_name_t *keyp = &key;
1290         const key_code_name_t **res;
1291 
1292         sort_key_conv_tab (KEY_SORTBYCODE);
1293 
1294         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1295                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1296 
1297         if (res != NULL)
1298         {
1299             *idx = (int) (res - key_conv_tab_sorted);
1300             return TRUE;
1301         }
1302     }
1303 
1304     *idx = -1;
1305     return FALSE;
1306 }
1307 
1308 /* --------------------------------------------------------------------------------------------- */
1309 /*** public functions ****************************************************************************/
1310 /* --------------------------------------------------------------------------------------------- */
1311 /* This has to be called before init_slang or whatever routine
1312    calls any define_sequence */
1313 
1314 void
1315 init_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1316 {
1317     const char *term;
1318 
1319     term = getenv ("TERM");
1320 
1321     // This has to be the first define_sequence
1322     // So, we can assume that the first keys member has ESC
1323     define_sequences (mc_default_keys);
1324 
1325     // Terminfo on irix does not have some keys
1326     if (mc_global.tty.xterm_flag
1327         || (term != NULL
1328             && (strncmp (term, "iris-ansi", 9) == 0 || strncmp (term, "xterm", 5) == 0
1329                 || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0)))
1330         define_sequences (xterm_key_defines);
1331 
1332     // load some additional keys (e.g. direct Alt-? support)
1333     load_xtra_key_defines ();
1334 
1335 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1336     init_key_x11 ();
1337 #endif
1338 
1339     /* Load the qansi-m key definitions
1340        if we are running under the qansi-m terminal */
1341     if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1342         define_sequences (qansi_key_defines);
1343 }
1344 
1345 /* --------------------------------------------------------------------------------------------- */
1346 /**
1347  * This has to be called after SLang_init_tty/slint_init
1348  */
1349 
1350 void
1351 init_key_input_fd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1352 {
1353 #ifdef HAVE_SLANG
1354     input_fd = SLang_TT_Read_FD;
1355 #endif
1356 }
1357 
1358 /* --------------------------------------------------------------------------------------------- */
1359 
1360 void
1361 done_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1362 {
1363     k_dispose (keys);
1364     g_slist_free_full (select_list, g_free);
1365 
1366 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1367     if (x11_display)
1368         mc_XCloseDisplay (x11_display);
1369 #endif
1370 }
1371 
1372 /* --------------------------------------------------------------------------------------------- */
1373 
1374 void
1375 add_select_channel (int fd, select_fn callback, void *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
1376 {
1377     select_t *new;
1378 
1379     new = g_new (select_t, 1);
1380     new->fd = fd;
1381     new->callback = callback;
1382     new->info = info;
1383 
1384     select_list = g_slist_prepend (select_list, new);
1385 }
1386 
1387 /* --------------------------------------------------------------------------------------------- */
1388 
1389 void
1390 delete_select_channel (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
1391 {
1392     GSList *p;
1393 
1394     p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd);
1395     if (p != NULL)
1396         select_list = g_slist_delete_link (select_list, p);
1397 }
1398 
1399 /* --------------------------------------------------------------------------------------------- */
1400 
1401 void
1402 channels_up (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1403 {
1404     if (disabled_channels == 0)
1405         fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1406     disabled_channels--;
1407 }
1408 
1409 /* --------------------------------------------------------------------------------------------- */
1410 
1411 void
1412 channels_down (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1413 {
1414     disabled_channels++;
1415 }
1416 
1417 /* --------------------------------------------------------------------------------------------- */
1418 /**
1419  * Return the code associated with the symbolic name keyname
1420  */
1421 
1422 int
1423 tty_keyname_to_keycode (const char *name, char **label)
     /* [previous][next][first][last][top][bottom][index][help]  */
1424 {
1425     char **lc_keys, **p;
1426     char *cname;
1427     int k = -1;
1428     int key = 0;
1429     int lc_index = -1;
1430 
1431     int use_meta = -1;
1432     int use_ctrl = -1;
1433     int use_shift = -1;
1434 
1435     if (name == NULL)
1436         return 0;
1437 
1438     cname = g_strstrip (g_strdup (name));
1439     lc_keys = g_strsplit_set (cname, "-+ ", -1);
1440     g_free (cname);
1441 
1442     for (p = lc_keys; p != NULL && *p != NULL; p++)
1443     {
1444         if ((*p)[0] != '\0')
1445         {
1446             int idx;
1447 
1448             key = lookup_keyname (g_strstrip (*p), &idx);
1449 
1450             if (key == KEY_M_ALT)
1451                 use_meta = idx;
1452             else if (key == KEY_M_CTRL)
1453                 use_ctrl = idx;
1454             else if (key == KEY_M_SHIFT)
1455                 use_shift = idx;
1456             else
1457             {
1458                 k = key;
1459                 lc_index = idx;
1460                 break;
1461             }
1462         }
1463     }
1464 
1465     g_strfreev (lc_keys);
1466 
1467     // output
1468     if (k <= 0)
1469         return 0;
1470 
1471     if (label != NULL)
1472     {
1473         GString *s;
1474 
1475         s = g_string_new ("");
1476 
1477         if (use_meta != -1)
1478         {
1479             g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1480             g_string_append_c (s, '-');
1481         }
1482         if (use_ctrl != -1)
1483         {
1484             g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1485             g_string_append_c (s, '-');
1486         }
1487         if (use_shift != -1)
1488         {
1489             if (k < 127)
1490                 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1491             else
1492             {
1493                 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1494                 g_string_append_c (s, '-');
1495                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1496             }
1497         }
1498         else if (k < 128)
1499         {
1500             if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1501                 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1502             else
1503                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1504         }
1505         else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1506             g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1507         else
1508             g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1509 
1510         *label = g_string_free (s, FALSE);
1511     }
1512 
1513     if (use_shift != -1)
1514     {
1515         if (k < 127 && k > 31)
1516             k = g_ascii_toupper ((gchar) k);
1517         else
1518             k |= KEY_M_SHIFT;
1519     }
1520 
1521     if (use_ctrl != -1)
1522     {
1523         if (k < 256)
1524             k = XCTRL (k);
1525         else
1526             k |= KEY_M_CTRL;
1527     }
1528 
1529     if (use_meta != -1)
1530         k = ALT (k);
1531 
1532     return k;
1533 }
1534 
1535 /* --------------------------------------------------------------------------------------------- */
1536 
1537 char *
1538 tty_keycode_to_keyname (const int keycode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1539 {
1540     // code without modifier
1541     unsigned int k = keycode & ~KEY_M_MASK;
1542     // modifier
1543     unsigned int mod = keycode & KEY_M_MASK;
1544 
1545     int key_idx = -1;
1546 
1547     GString *s;
1548     int idx;
1549 
1550     s = g_string_sized_new (8);
1551 
1552     if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1553     {
1554         if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx))
1555         {
1556             g_string_append (s, key_conv_tab_sorted[idx]->name);
1557             g_string_append_c (s, '-');
1558         }
1559 
1560         if ((mod & KEY_M_CTRL) != 0)
1561         {
1562             // non printeble chars like a CTRL-[A..Z]
1563             if (k < 32)
1564                 k += 64;
1565 
1566             if (lookup_keycode (KEY_M_CTRL, &idx))
1567             {
1568                 g_string_append (s, key_conv_tab_sorted[idx]->name);
1569                 g_string_append_c (s, '-');
1570             }
1571         }
1572 
1573         if ((mod & KEY_M_SHIFT) != 0)
1574         {
1575             if (lookup_keycode (KEY_M_ALT, &idx))
1576             {
1577                 if (k < 127)
1578                     g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1579                 else
1580                 {
1581                     g_string_append (s, key_conv_tab_sorted[idx]->name);
1582                     g_string_append_c (s, '-');
1583                     g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1584                 }
1585             }
1586         }
1587         else if (k < 128)
1588         {
1589             if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1590                 g_string_append_c (s, (gchar) k);
1591             else
1592                 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1593         }
1594         else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1595             g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1596         else
1597             g_string_append_c (s, (gchar) keycode);
1598     }
1599 
1600     return g_string_free (s, s->len == 0);
1601 }
1602 
1603 /* --------------------------------------------------------------------------------------------- */
1604 /**
1605  * Return TRUE on success, FALSE on error.
1606  * An error happens if SEQ is a beginning of an existing longer sequence.
1607  */
1608 
1609 gboolean
1610 define_sequence (int code, const char *seq, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1611 {
1612     key_def *base;
1613 
1614     if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1615         return FALSE;
1616 
1617     for (base = keys; (base != NULL) && (*seq != '\0');)
1618         if (*seq == base->ch)
1619         {
1620             if (base->child == NULL)
1621             {
1622                 if (*(seq + 1) != '\0')
1623                     base->child = create_sequence (seq + 1, code, action);
1624                 else
1625                 {
1626                     // The sequence matches an existing one.
1627                     base->code = code;
1628                     base->action = action;
1629                 }
1630                 return TRUE;
1631             }
1632 
1633             base = base->child;
1634             seq++;
1635         }
1636         else
1637         {
1638             if (base->next != NULL)
1639                 base = base->next;
1640             else
1641             {
1642                 base->next = create_sequence (seq, code, action);
1643                 return TRUE;
1644             }
1645         }
1646 
1647     if (*seq == '\0')
1648     {
1649         // Attempt to redefine a sequence with a shorter sequence.
1650         return FALSE;
1651     }
1652 
1653     keys = create_sequence (seq, code, action);
1654     return TRUE;
1655 }
1656 
1657 /* --------------------------------------------------------------------------------------------- */
1658 /**
1659  * Check if we are idle, i.e. there are no pending keyboard or mouse
1660  * events.  Return 1 is idle, 0 is there are pending events.
1661  */
1662 gboolean
1663 is_idle (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1664 {
1665     int nfd;
1666     fd_set select_set;
1667     struct timeval time_out;
1668 
1669     FD_ZERO (&select_set);
1670     FD_SET (input_fd, &select_set);
1671     nfd = MAX (0, input_fd) + 1;
1672     time_out.tv_sec = 0;
1673     time_out.tv_usec = 0;
1674 #ifdef HAVE_LIBGPM
1675     if (mouse_enabled && use_mouse_p == MOUSE_GPM)
1676     {
1677         if (gpm_fd >= 0)
1678         {
1679             FD_SET (gpm_fd, &select_set);
1680             nfd = MAX (nfd, gpm_fd + 1);
1681         }
1682         else
1683         {
1684             if (mouse_fd >= 0)  // error indicative
1685             {
1686                 if (FD_ISSET (mouse_fd, &select_set))
1687                     FD_CLR (mouse_fd, &select_set);
1688                 mouse_fd = gpm_fd;
1689             }
1690             // gpm_fd == -2 means under some X terminal
1691             if (gpm_fd == -1)
1692             {
1693                 mouse_enabled = FALSE;
1694                 use_mouse_p = MOUSE_NONE;
1695             }
1696         }
1697     }
1698 #endif
1699     return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1700 }
1701 
1702 /* --------------------------------------------------------------------------------------------- */
1703 
1704 int
1705 get_key_code (int no_delay)
     /* [previous][next][first][last][top][bottom][index][help]  */
1706 {
1707     int c;
1708     static key_def *this = NULL, *parent;
1709     static gint64 esc_time = -1;
1710     static int lastnodelay = -1;
1711 
1712     if (no_delay != lastnodelay)
1713     {
1714         this = NULL;
1715         lastnodelay = no_delay;
1716     }
1717 
1718 pend_send:
1719     if (pending_keys != NULL)
1720     {
1721         gboolean bad_seq;
1722 
1723         c = *pending_keys++;
1724         while (c == ESC_CHAR)
1725             c = ALT (*pending_keys++);
1726 
1727         bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0');
1728         if (*pending_keys == '\0' || bad_seq)
1729             pending_keys = seq_append = NULL;
1730 
1731         if (bad_seq)
1732         {
1733             /* This is an unknown ESC sequence.
1734              * To prevent interpreting its tail as a random garbage,
1735              * eat and discard all buffered and quickly following chars.
1736              * Small, but non-zero timeout is needed to reconnect
1737              * escape sequence split up by e.g. a serial line.
1738              */
1739             int paranoia = 20;
1740 
1741             while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1742                 ;
1743         }
1744         else
1745             goto done;
1746     }
1747 
1748 nodelay_try_again:
1749     if (no_delay != 0)
1750         tty_nodelay (TRUE);
1751 
1752     c = tty_lowlevel_getch ();
1753 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1754     if (c == KEY_RESIZE)
1755         goto nodelay_try_again;
1756 #endif
1757 
1758     if (no_delay != 0)
1759     {
1760         tty_nodelay (FALSE);
1761         if (c == -1)
1762         {
1763             if (this == NULL || parent == NULL || parent->action != MCKEY_ESCAPE || !old_esc_mode
1764                 || esc_time == -1 || g_get_monotonic_time () < esc_time + old_esc_mode_timeout)
1765                 return -1;
1766 
1767             this = NULL;
1768             pending_keys = seq_append = NULL;
1769             return ESC_CHAR;
1770         }
1771     }
1772     else if (c == -1)
1773     {
1774         /* Maybe we got an incomplete match.
1775            This we do only in delay mode, since otherwise
1776            tty_lowlevel_getch can return -1 at any time. */
1777         if (seq_append == NULL)
1778         {
1779             this = NULL;
1780             return -1;
1781         }
1782 
1783         pending_keys = seq_buffer;
1784         goto pend_send;
1785     }
1786 
1787     // Search the key on the root
1788     if (no_delay == 0 || this == NULL)
1789     {
1790         this = keys;
1791         parent = NULL;
1792     }
1793 
1794     while (this != NULL)
1795     {
1796         if (c == this->ch)
1797         {
1798             if (this->child == NULL)
1799             {
1800                 // We got a complete match, return and reset search
1801                 pending_keys = seq_append = NULL;
1802                 c = this->code;
1803                 goto done;
1804             }
1805 
1806             // No match yet, but it may be a prefix for a valid seq
1807             if (!push_char (c))
1808             {
1809                 pending_keys = seq_buffer;
1810                 goto pend_send;
1811             }
1812 
1813             parent = this;
1814             this = this->child;
1815             if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1816             {
1817                 if (no_delay != 0)
1818                 {
1819                     esc_time = g_get_monotonic_time ();
1820                     goto nodelay_try_again;
1821                 }
1822 
1823                 esc_time = -1;
1824                 c = getch_with_timeout (old_esc_mode_timeout);
1825                 if (c != -1)
1826                     continue;
1827 
1828                 pending_keys = seq_append = NULL;
1829                 this = NULL;
1830                 return ESC_CHAR;
1831             }
1832 
1833             if (no_delay != 0)
1834                 goto nodelay_try_again;
1835             c = tty_lowlevel_getch ();
1836             continue;
1837         }
1838 
1839         // c != this->ch. Try other keys with this prefix
1840         if (this->next != NULL)
1841         {
1842             this = this->next;
1843             continue;
1844         }
1845 
1846         // No match found. Is it one of our ESC <key> specials?
1847         if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1848         {
1849             // Convert escape-digits to F-keys
1850             if (g_ascii_isdigit (c))
1851                 c = KEY_F (c - '0');
1852             else if (c == ' ')
1853                 c = ESC_CHAR;
1854             else
1855                 c = ALT (c);
1856 
1857             pending_keys = seq_append = NULL;
1858             goto done;
1859         }
1860 
1861         // Unknown sequence. Maybe a prefix of a longer one. Save it.
1862         push_char (c);
1863         pending_keys = seq_buffer;
1864         goto pend_send;
1865     }  // while (this != NULL)
1866 
1867 done:
1868     this = NULL;
1869     return correct_key_code (c);
1870 }
1871 
1872 /* --------------------------------------------------------------------------------------------- */
1873 /* Returns a character read from stdin with appropriate interpretation */
1874 /* Also takes care of generated mouse events */
1875 /* Returns EV_MOUSE if it is a mouse event */
1876 /* Returns EV_NONE  if non-blocking or interrupt set and nothing was done */
1877 
1878 int
1879 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
     /* [previous][next][first][last][top][bottom][index][help]  */
1880 {
1881     int c;
1882     int flag = 0;  // Return value from select
1883 #ifdef HAVE_LIBGPM
1884     static struct Gpm_Event ev;  // Mouse event
1885 #endif
1886     struct timeval time_out;
1887     struct timeval *time_addr = NULL;
1888     static int dirty = 3;
1889 
1890     if ((dirty == 3) || is_idle ())
1891     {
1892         mc_refresh ();
1893         dirty = 1;
1894     }
1895     else
1896         dirty++;
1897 
1898     vfs_timeout_handler ();
1899 
1900     /* Ok, we use (event->x < 0) to signal that the event does not contain
1901        a suitable position for the mouse, so we can't use show_mouse_pointer
1902        on it.
1903      */
1904     if (event->x > 0)
1905     {
1906         show_mouse_pointer (event->x, event->y);
1907         if (!redo_event)
1908             event->x = -1;
1909     }
1910 
1911     // Repeat if using mouse
1912     while (pending_keys == NULL)
1913     {
1914         int nfd;
1915         fd_set select_set;
1916 
1917         FD_ZERO (&select_set);
1918         FD_SET (input_fd, &select_set);
1919         nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1920 
1921 #ifdef HAVE_LIBGPM
1922         if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1923         {
1924             if (gpm_fd >= 0)
1925             {
1926                 FD_SET (gpm_fd, &select_set);
1927                 nfd = MAX (nfd, gpm_fd + 1);
1928             }
1929             else
1930             {
1931                 if (mouse_fd >= 0)  // error indicative
1932                 {
1933                     if (FD_ISSET (mouse_fd, &select_set))
1934                         FD_CLR (mouse_fd, &select_set);
1935                     mouse_fd = gpm_fd;
1936                 }
1937                 // gpm_fd == -2 means under some X terminal
1938                 if (gpm_fd == -1)
1939                 {
1940                     mouse_enabled = FALSE;
1941                     use_mouse_p = MOUSE_NONE;
1942                 }
1943                 break;
1944             }
1945         }
1946 #endif
1947 
1948         if (redo_event)
1949         {
1950             time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC;
1951             time_out.tv_sec = 0;
1952 
1953             time_addr = &time_out;
1954         }
1955         else
1956         {
1957             int seconds;
1958 
1959             seconds = vfs_timeouts ();
1960             time_addr = NULL;
1961 
1962             if (seconds != 0)
1963             {
1964                 /* the timeout could be improved and actually be
1965                  * the number of seconds until the next vfs entry
1966                  * timeouts in the stamp list.
1967                  */
1968 
1969                 time_out.tv_sec = seconds;
1970                 time_out.tv_usec = 0;
1971                 time_addr = &time_out;
1972             }
1973         }
1974 
1975         if (!block || tty_got_winch ())
1976         {
1977             time_addr = &time_out;
1978             time_out.tv_sec = 0;
1979             time_out.tv_usec = 0;
1980         }
1981 
1982         tty_enable_interrupt_key ();
1983         flag = select (nfd, &select_set, NULL, NULL, time_addr);
1984         tty_disable_interrupt_key ();
1985 
1986         /* select timed out: it could be for any of the following reasons:
1987          * redo_event -> it was because of the MOU_REPEAT handler
1988          * !block     -> we did not block in the select call
1989          * else       -> 10 second timeout to check the vfs status.
1990          */
1991         if (flag == 0)
1992         {
1993             if (redo_event)
1994                 return EV_MOUSE;
1995             if (!block || tty_got_winch ())
1996                 return EV_NONE;
1997             vfs_timeout_handler ();
1998         }
1999         if (flag == -1 && errno == EINTR)
2000             return EV_NONE;
2001 
2002         check_selects (&select_set);
2003 
2004         if (FD_ISSET (input_fd, &select_set))
2005             break;
2006 
2007 #ifdef HAVE_LIBGPM
2008         if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2009         {
2010             if (gpm_fd >= 0)
2011             {
2012                 if (FD_ISSET (gpm_fd, &select_set))
2013                 {
2014                     int status;
2015 
2016                     status = Gpm_GetEvent (&ev);
2017                     if (status == 1)  // success
2018                     {
2019                         Gpm_FitEvent (&ev);
2020                         *event = ev;
2021                         return EV_MOUSE;
2022                     }
2023                     if (status <= 0)  // connection closed; -1 == error
2024                     {
2025                         if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2026                             FD_CLR (mouse_fd, &select_set);
2027 
2028                         disable_mouse ();
2029                         return EV_NONE;
2030                     }
2031                 }
2032             }
2033             else
2034             {
2035                 if (mouse_fd >= 0)  // error indicative
2036                 {
2037                     if (FD_ISSET (mouse_fd, &select_set))
2038                         FD_CLR (mouse_fd, &select_set);
2039                     mouse_fd = gpm_fd;
2040                 }
2041                 // gpm_fd == -2 means under some X terminal
2042                 if (gpm_fd == -1)
2043                 {
2044                     mouse_enabled = FALSE;
2045                     use_mouse_p = MOUSE_NONE;
2046                 }
2047                 break;
2048             }
2049         }
2050 #endif
2051     }
2052 
2053 #ifndef HAVE_SLANG
2054     flag = is_wintouched (stdscr);
2055     untouchwin (stdscr);
2056 #endif
2057     c = block ? getch_with_delay () : get_key_code (1);
2058 
2059 #ifndef HAVE_SLANG
2060     if (flag > 0)
2061         tty_touch_screen ();
2062 #endif
2063 
2064     if (mouse_enabled
2065         && (c == MCKEY_MOUSE
2066 #ifdef KEY_MOUSE
2067             || c == KEY_MOUSE
2068 #endif
2069             || c == MCKEY_EXTENDED_MOUSE))
2070     {
2071         // Mouse event. See tickets 2956, 3954 and 4144 for extended mode detection.
2072         gboolean extended = c == MCKEY_EXTENDED_MOUSE;
2073 
2074 #ifdef KEY_MOUSE
2075         extended = extended || (c == KEY_MOUSE && ncurses_key_mouse_means_extended);
2076 #endif
2077 
2078         xmouse_get_event (event, extended);
2079         c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2080     }
2081     else if (c == MCKEY_BRACKETED_PASTING_START)
2082     {
2083         bracketed_pasting_in_progress = TRUE;
2084         c = EV_NONE;
2085     }
2086     else if (c == MCKEY_BRACKETED_PASTING_END)
2087     {
2088         bracketed_pasting_in_progress = FALSE;
2089         c = EV_NONE;
2090     }
2091 
2092     return c;
2093 }
2094 
2095 /* --------------------------------------------------------------------------------------------- */
2096 /* Returns a key press, mouse events are discarded */
2097 
2098 int
2099 tty_getch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2100 {
2101     Gpm_Event ev;
2102     int key;
2103 
2104     ev.x = -1;
2105     while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
2106         ;
2107     return key;
2108 }
2109 
2110 /* --------------------------------------------------------------------------------------------- */
2111 
2112 char *
2113 learn_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2114 {
2115     // LEARN_TIMEOUT in ms
2116 #define LEARN_TIMEOUT 200
2117 
2118     fd_set Read_FD_Set;
2119     gint64 end_time;
2120     int c;
2121     GString *buffer;
2122 
2123     buffer = g_string_sized_new (16);
2124 
2125     tty_keypad (FALSE);  // disable interpreting keys by ncurses
2126     c = tty_lowlevel_getch ();
2127     while (c == -1)
2128         c = tty_lowlevel_getch ();  // Sanity check, should be unnecessary
2129     learn_store_key (buffer, c);
2130 
2131     end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC;
2132 
2133     tty_nodelay (TRUE);
2134     while (TRUE)
2135     {
2136         while ((c = tty_lowlevel_getch ()) == -1)
2137         {
2138             gint64 time_out;
2139             struct timeval tv;
2140 
2141             time_out = end_time - g_get_monotonic_time ();
2142             if (time_out <= 0)
2143                 break;
2144 
2145             tv.tv_sec = time_out / G_USEC_PER_SEC;
2146             tv.tv_usec = time_out % G_USEC_PER_SEC;
2147             FD_ZERO (&Read_FD_Set);
2148             FD_SET (input_fd, &Read_FD_Set);
2149             select (input_fd + 1, &Read_FD_Set, NULL, NULL, &tv);
2150         }
2151         if (c == -1)
2152             break;
2153         learn_store_key (buffer, c);
2154     }
2155     tty_keypad (TRUE);
2156     tty_nodelay (FALSE);
2157 
2158     return g_string_free (buffer, buffer->len == 0);
2159 #undef LEARN_TIMEOUT
2160 }
2161 
2162 /* --------------------------------------------------------------------------------------------- */
2163 /* xterm and linux console only: set keypad to numeric or application
2164    mode. Only in application keypad mode it's possible to distinguish
2165    the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2166 
2167 void
2168 numeric_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2169 {
2170     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2171     {
2172         fputs (ESC_STR ">", stdout);
2173         fflush (stdout);
2174     }
2175 }
2176 
2177 /* --------------------------------------------------------------------------------------------- */
2178 
2179 void
2180 application_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2181 {
2182     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2183     {
2184         fputs (ESC_STR "=", stdout);
2185         fflush (stdout);
2186     }
2187 }
2188 
2189 /* --------------------------------------------------------------------------------------------- */
2190 
2191 void
2192 enable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2193 {
2194     printf (ESC_STR "[?2004h");
2195     fflush (stdout);
2196 }
2197 
2198 /* --------------------------------------------------------------------------------------------- */
2199 
2200 void
2201 disable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2202 {
2203     printf (ESC_STR "[?2004l");
2204     fflush (stdout);
2205     bracketed_pasting_in_progress = FALSE;
2206 }
2207 
2208 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */