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-2024
   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 <http://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 /* __linux__ */
  76 
  77 #ifdef __CYGWIN__
  78 #include <termios.h>
  79 #ifdef HAVE_SYS_IOCTL_H
  80 #include <sys/ioctl.h>
  81 #endif
  82 #endif /* __CYGWIN__ */
  83 
  84 #ifdef __QNXNTO__
  85 #include <dlfcn.h>
  86 #include <Ph.h>
  87 #include <sys/dcmd_chr.h>
  88 #endif /* __QNXNTO__ */
  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 gboolean use_8th_bit_as_meta = FALSE;
  98 
  99 gboolean bracketed_pasting_in_progress = FALSE;
 100 
 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
 104  */
 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"), "+"},
 146 
 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"},
 165 
 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"), "@"},
 209 
 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"},
 217 
 218     {0, NULL, NULL, NULL}
 219 };
 220 
 221 /*** file scope macro definitions ****************************************************************/
 222 
 223 #define MC_USEC_PER_MSEC 1000
 224 
 225 /* The maximum sequence length (32 + null terminator) */
 226 #define SEQ_BUFFER_LEN 33
 227 
 228 /*** file scope type declarations ****************************************************************/
 229 
 230 /* Linux console keyboard modifiers */
 231 typedef enum
 232 {
 233     SHIFT_PRESSED = (1 << 0),
 234     ALTR_PRESSED = (1 << 1),
 235     CONTROL_PRESSED = (1 << 2),
 236     ALTL_PRESSED = (1 << 3)
 237 } mod_pressed_t;
 238 
 239 typedef struct key_def
 240 {
 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;
 249 
 250 typedef struct
 251 {
 252     int code;
 253     const char *seq;
 254     int action;
 255 } key_define_t;
 256 
 257 /* File descriptor monitoring add/remove routines */
 258 typedef struct
 259 {
 260     int fd;
 261     select_fn callback;
 262     void *info;
 263 } select_t;
 264 
 265 typedef enum KeySortType
 266 {
 267     KEY_NOSORT = 0,
 268     KEY_SORTBYNAME,
 269     KEY_SORTBYCODE
 270 } KeySortType;
 271 
 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
 277 
 278 /*** forward declarations (file scope functions) *************************************************/
 279 
 280 /*** file scope variables ************************************************************************/
 281 
 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},
 288 };
 289 
 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},
 306 
 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},
 312 
 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},
 318 
 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},
 339 
 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},
 345 
 346     /* putty alt-arrow keys */
 347     /* removed as source esc esc esc trouble */
 348     /*
 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 },
 357 
 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 },
 362 
 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 },
 367      */
 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},
 385 
 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},
 390 
 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},
 409 
 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},
 413 
 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},
 427 
 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},
 437 
 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},
 441 
 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},
 445 
 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},
 451 
 452     {0, NULL, MCKEY_NOACTION},
 453 };
 454 
 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},
 526 };
 527 
 528 /* This holds all the key definitions */
 529 static key_def *keys = NULL;
 530 
 531 static int input_fd;
 532 static int disabled_channels = 0;       /* Disable channels checking */
 533 
 534 static GSList *select_list = NULL;
 535 
 536 static int seq_buffer[SEQ_BUFFER_LEN];
 537 static int *seq_append = NULL;
 538 
 539 static int *pending_keys = NULL;
 540 
 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
 546 
 547 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 548 static Display *x11_display;
 549 static Window x11_window;
 550 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 551 
 552 static KeySortType has_been_sorted = KEY_NOSORT;
 553 
 554 /* *INDENT-OFF* */
 555 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
 556 /* *INDENT-ON* */
 557 
 558 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
 559 
 560 /* --------------------------------------------------------------------------------------------- */
 561 /*** file scope functions ************************************************************************/
 562 /* --------------------------------------------------------------------------------------------- */
 563 
 564 static int
 565 select_cmp_by_fd_set (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 566 {
 567     const select_t *s = (const select_t *) a;
 568     const fd_set *f = (const fd_set *) b;
 569 
 570     return (FD_ISSET (s->fd, f) ? 0 : 1);
 571 }
 572 
 573 /* --------------------------------------------------------------------------------------------- */
 574 
 575 static int
 576 select_cmp_by_fd (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 577 {
 578     const select_t *s = (const select_t *) a;
 579     const int fd = GPOINTER_TO_INT (b);
 580 
 581     return (s->fd == fd ? 0 : 1);
 582 }
 583 
 584 /* --------------------------------------------------------------------------------------------- */
 585 
 586 static int
 587 add_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 588 {
 589     int top_fd = 0;
 590 
 591     if (disabled_channels == 0)
 592     {
 593         GSList *s;
 594 
 595         for (s = select_list; s != NULL; s = g_slist_next (s))
 596         {
 597             select_t *p = (select_t *) s->data;
 598 
 599             FD_SET (p->fd, select_set);
 600             if (p->fd > top_fd)
 601                 top_fd = p->fd;
 602         }
 603     }
 604 
 605     return top_fd;
 606 }
 607 
 608 /* --------------------------------------------------------------------------------------------- */
 609 
 610 static void
 611 check_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 612 {
 613     while (disabled_channels == 0)
 614     {
 615         GSList *s;
 616         select_t *p;
 617 
 618         s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set);
 619         if (s == NULL)
 620             break;
 621 
 622         p = (select_t *) s->data;
 623         FD_CLR (p->fd, select_set);
 624         p->callback (p->fd, p->info);
 625     }
 626 }
 627 
 628 /* --------------------------------------------------------------------------------------------- */
 629 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
 630 
 631 static void
 632 try_channels (gboolean set_timeout)
     /* [previous][next][first][last][top][bottom][index][help]  */
 633 {
 634     struct timeval time_out;
 635     static fd_set select_set;
 636 
 637     while (TRUE)
 638     {
 639         struct timeval *timeptr = NULL;
 640         int maxfdp, v;
 641 
 642         FD_ZERO (&select_set);
 643         FD_SET (input_fd, &select_set); /* Add stdin */
 644         maxfdp = MAX (add_selects (&select_set), input_fd);
 645 
 646         if (set_timeout)
 647         {
 648             time_out.tv_sec = 0;
 649             time_out.tv_usec = 100 * MC_USEC_PER_MSEC;
 650             timeptr = &time_out;
 651         }
 652 
 653         v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
 654         if (v > 0)
 655         {
 656             check_selects (&select_set);
 657             if (FD_ISSET (input_fd, &select_set))
 658                 break;
 659         }
 660     }
 661 }
 662 
 663 /* --------------------------------------------------------------------------------------------- */
 664 
 665 static key_def *
 666 create_sequence (const char *seq, int code, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 667 {
 668     key_def *base, *p, *attach;
 669 
 670     for (base = attach = NULL; *seq != '\0'; seq++)
 671     {
 672         p = g_new (key_def, 1);
 673         if (base == NULL)
 674             base = p;
 675         if (attach != NULL)
 676             attach->child = p;
 677 
 678         p->ch = *seq;
 679         p->code = code;
 680         p->child = p->next = NULL;
 681         if (seq[1] == '\0')
 682             p->action = action;
 683         else
 684             p->action = MCKEY_NOACTION;
 685         attach = p;
 686     }
 687     return base;
 688 }
 689 
 690 /* --------------------------------------------------------------------------------------------- */
 691 
 692 static void
 693 define_sequences (const key_define_t * kd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 694 {
 695     int i;
 696 
 697     for (i = 0; kd[i].code != 0; i++)
 698         define_sequence (kd[i].code, kd[i].seq, kd[i].action);
 699 }
 700 
 701 /* --------------------------------------------------------------------------------------------- */
 702 
 703 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 704 static void
 705 init_key_x11 (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 706 {
 707     if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
 708     {
 709         x11_display = mc_XOpenDisplay (0);
 710 
 711         if (x11_display != NULL)
 712             x11_window = DefaultRootWindow (x11_display);
 713     }
 714 }
 715 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 716 
 717 /* --------------------------------------------------------------------------------------------- */
 718 /* Workaround for System V Curses vt100 bug */
 719 
 720 static int
 721 getch_with_delay (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 722 {
 723     int c;
 724 
 725     /* This routine could be used on systems without mouse support,
 726        so we need to do the select check :-( */
 727     while (TRUE)
 728     {
 729         if (pending_keys == NULL)
 730             try_channels (FALSE);
 731 
 732         /* Try to get a character */
 733         c = get_key_code (0);
 734         if (c != -1)
 735             break;
 736 
 737         /* Failed -> wait 0.1 secs and try again */
 738         try_channels (TRUE);
 739     }
 740 
 741     /* Success -> return the character */
 742     return c;
 743 }
 744 
 745 /* --------------------------------------------------------------------------------------------- */
 746 
 747 static void
 748 xmouse_get_event (Gpm_Event * ev, gboolean extended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 749 {
 750     static gint64 tv1 = 0;      /* Force first click as single */
 751     static int clicks = 0;
 752     static int last_btn = 0;
 753     int btn;
 754 
 755     /* Decode Xterm mouse information to a GPM style event */
 756 
 757     if (!extended)
 758     {
 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;
 766     }
 767     else
 768     {
 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;
 776 
 777         btn = ev->x = ev->y = 0;
 778         ev->type = 0;           /* In case we return on an invalid sequence */
 779 
 780         while ((c = tty_lowlevel_getch ()) != ';')
 781         {
 782             if (c < '0' || c > '9')
 783                 return;
 784             btn = 10 * btn + (c - '0');
 785         }
 786         while ((c = tty_lowlevel_getch ()) != ';')
 787         {
 788             if (c < '0' || c > '9')
 789                 return;
 790             ev->x = 10 * ev->x + (c - '0');
 791         }
 792         while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
 793         {
 794             if (c < '0' || c > '9')
 795                 return;
 796             ev->y = 10 * ev->y + (c - '0');
 797         }
 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;
 804     }
 805 
 806     /* There seems to be no way of knowing which button was released */
 807     /* So we assume all the buttons were released */
 808 
 809     if (btn == 3)
 810     {
 811         if (last_btn != 0)
 812         {
 813             if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
 814             {
 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;
 820             }
 821             else
 822             {
 823                 ev->type = GPM_UP | (GPM_SINGLE << clicks);
 824                 tv1 = g_get_monotonic_time ();
 825             }
 826             ev->buttons = 0;
 827             last_btn = 0;
 828             clicks = 0;
 829         }
 830         else
 831         {
 832             /* Bogus event, maybe mouse wheel */
 833             ev->type = 0;
 834         }
 835     }
 836     else
 837     {
 838         gint64 tv2;
 839 
 840         if (btn >= 32 && btn <= 34)
 841         {
 842             btn -= 32;
 843             ev->type = GPM_DRAG;
 844         }
 845         else
 846             ev->type = GPM_DOWN;
 847 
 848         tv2 = g_get_monotonic_time ();
 849         if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC)
 850         {
 851             clicks++;
 852             clicks %= 3;
 853         }
 854         else
 855             clicks = 0;
 856 
 857         switch (btn)
 858         {
 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;
 881         }
 882         last_btn = ev->buttons;
 883     }
 884 }
 885 
 886 /* --------------------------------------------------------------------------------------------- */
 887 /**
 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.
 893  */
 894 
 895 static int
 896 get_modifier (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 897 {
 898     int result = 0;
 899 #ifdef __QNXNTO__
 900     static int in_photon = 0;
 901     static int ph_ig = 0;
 902 #endif /* __QNXNTO__ */
 903 
 904 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 905     if (x11_window != 0)
 906     {
 907         Window root, child;
 908         int root_x, root_y;
 909         int win_x, win_y;
 910         unsigned int mask;
 911 
 912         mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
 913                           &root_y, &win_x, &win_y, &mask);
 914 
 915         if ((mask & ShiftMask) != 0)
 916             result |= KEY_M_SHIFT;
 917         if ((mask & ControlMask) != 0)
 918             result |= KEY_M_CTRL;
 919         return result;
 920     }
 921 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 922 
 923 #ifdef __QNXNTO__
 924     if (in_photon == 0)
 925     {
 926         /* First time here, let's load Photon library and attach to Photon */
 927         in_photon = -1;
 928 
 929         if (getenv ("PHOTON2_PATH") != NULL)
 930         {
 931             /* QNX 6.x has no support for RTLD_LAZY */
 932             void *ph_handle;
 933 
 934             ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
 935             if (ph_handle != NULL)
 936             {
 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)
 942                 {
 943                     /* Attached */
 944                     ph_ig = (*ph_input_group) (0);
 945                     in_photon = 1;
 946                 }
 947             }
 948         }
 949     }
 950     /* We do not have Photon running. Assume we are in text console or xterm */
 951     if (in_photon == -1)
 952     {
 953         int mod_status;
 954         int shift_ext_status;
 955 
 956         if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) ==
 957             -1)
 958             return 0;
 959 
 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;
 968     }
 969     else
 970     {
 971         PhCursorInfo_t cursor_info;
 972 
 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;
 980     }
 981 #endif /* __QNXNTO__ */
 982 
 983 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
 984     {
 985         unsigned char modifiers = 6;
 986 
 987         if (ioctl (0, TIOCLINUX, &modifiers) < 0)
 988             return 0;
 989 
 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;
 997     }
 998 #endif /* !__linux__ */
 999 
1000     return result;
1001 }
1002 
1003 /* --------------------------------------------------------------------------------------------- */
1004 
1005 static gboolean
1006 push_char (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1007 {
1008     gboolean ret = FALSE;
1009 
1010     if (seq_append == NULL)
1011         seq_append = seq_buffer;
1012 
1013     if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
1014     {
1015         *(seq_append++) = c;
1016         *seq_append = '\0';
1017         ret = TRUE;
1018     }
1019 
1020     return ret;
1021 }
1022 
1023 /* --------------------------------------------------------------------------------------------- */
1024 /* Apply corrections for the keycode generated in get_key_code() */
1025 
1026 static int
1027 correct_key_code (int code)
     /* [previous][next][first][last][top][bottom][index][help]  */
1028 {
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__ */
1035 
1036     /*
1037      * Add key modifiers directly from X11 or OS.
1038      * Ordinary characters only get modifiers from sequences.
1039      */
1040     if (c < 32 || c >= 256)
1041         mod |= get_modifier ();
1042 
1043     /* This is needed if the newline is reported as carriage return */
1044     if (c == '\r')
1045         c = '\n';
1046 
1047     /* This is reported to be useful on AIX */
1048     if (c == KEY_SCANCEL)
1049         c = '\t';
1050 
1051     /* Convert Back Tab to Shift+Tab */
1052     if (c == KEY_BTAB)
1053     {
1054         c = '\t';
1055         mod = KEY_M_SHIFT;
1056     }
1057 
1058     /* F0 is the same as F10 for out purposes */
1059     if (c == KEY_F (0))
1060         c = KEY_F (10);
1061 
1062     /*
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.
1067      */
1068     if (c == '\b')
1069     {
1070         /* Special case for backspase ('\b' < 32) */
1071         c = KEY_BACKSPACE;
1072         mod &= ~KEY_M_CTRL;
1073     }
1074     else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
1075         mod |= KEY_M_CTRL;
1076 
1077 #ifdef __QNXNTO__
1078     qmod = get_modifier ();
1079 
1080     if (c == 127 && mod == 0)
1081     {
1082         /* Add Ctrl/Alt/Shift-BackSpace */
1083         mod |= get_modifier ();
1084         c = KEY_BACKSPACE;
1085     }
1086 
1087     if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1088     {
1089         /* Add Shift-Insert on key pad */
1090         mod = KEY_M_SHIFT;
1091         c = KEY_IC;
1092     }
1093 
1094     if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1095     {
1096         /* Add Shift-Del on key pad */
1097         mod = KEY_M_SHIFT;
1098         c = KEY_DC;
1099     }
1100 #endif /* __QNXNTO__ */
1101 
1102     /* Unrecognized 0177 is delete (preserve Ctrl) */
1103     if (c == 0177)
1104         c = KEY_BACKSPACE;
1105 
1106 #if 0
1107     /* Unrecognized Ctrl-d is delete */
1108     if (c == 'd' & 31)
1109     {
1110         c = KEY_DC;
1111         mod &= ~KEY_M_CTRL;
1112     }
1113 
1114     /* Unrecognized Ctrl-h is backspace */
1115     if (c == 'h' & 31)
1116     {
1117         c = KEY_BACKSPACE;
1118         mod &= ~KEY_M_CTRL;
1119     }
1120 #endif
1121 
1122     /* Shift+BackSpace is backspace */
1123     if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0)
1124         mod &= ~KEY_M_SHIFT;
1125 
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;
1129 
1130     /* Remove Shift information from function keys */
1131     if (c >= KEY_F (1) && c <= KEY_F (20))
1132         mod &= ~KEY_M_SHIFT;
1133 
1134     if (!mc_global.tty.alternate_plus_minus)
1135         switch (c)
1136         {
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;
1148         }
1149 
1150     return (mod | c);
1151 }
1152 
1153 /* --------------------------------------------------------------------------------------------- */
1154 
1155 static int
1156 getch_with_timeout (unsigned int delay_us)
     /* [previous][next][first][last][top][bottom][index][help]  */
1157 {
1158     fd_set Read_FD_Set;
1159     int c;
1160     struct timeval time_out;
1161 
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;
1171 }
1172 
1173 /* --------------------------------------------------------------------------------------------- */
1174 
1175 static void
1176 learn_store_key (GString * buffer, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1177 {
1178     if (c == ESC_CHAR)
1179         g_string_append (buffer, "\\e");
1180     else if (c < ' ')
1181     {
1182         g_string_append_c (buffer, '^');
1183         g_string_append_c (buffer, c + 'a' - 1);
1184     }
1185     else if (c == '^')
1186         g_string_append (buffer, "^^");
1187     else
1188         g_string_append_c (buffer, (char) c);
1189 }
1190 
1191 /* --------------------------------------------------------------------------------------------- */
1192 
1193 static void
1194 k_dispose (key_def * k)
     /* [previous][next][first][last][top][bottom][index][help]  */
1195 {
1196     if (k != NULL)
1197     {
1198         k_dispose (k->child);
1199         k_dispose (k->next);
1200         g_free (k);
1201     }
1202 }
1203 
1204 /* --------------------------------------------------------------------------------------------- */
1205 
1206 static int
1207 key_code_comparator_by_name (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1208 {
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;
1211 
1212     return g_ascii_strcasecmp (n1->name, n2->name);
1213 }
1214 
1215 /* --------------------------------------------------------------------------------------------- */
1216 
1217 static int
1218 key_code_comparator_by_code (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1219 {
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;
1222 
1223     return n1->code - n2->code;
1224 }
1225 
1226 /* --------------------------------------------------------------------------------------------- */
1227 
1228 static inline void
1229 sort_key_conv_tab (enum KeySortType type_sort)
     /* [previous][next][first][last][top][bottom][index][help]  */
1230 {
1231     if (has_been_sorted != type_sort)
1232     {
1233         size_t i;
1234 
1235         for (i = 0; i < key_conv_tab_size; i++)
1236             key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1237 
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);
1244 
1245         has_been_sorted = type_sort;
1246     }
1247 }
1248 
1249 /* --------------------------------------------------------------------------------------------- */
1250 
1251 static int
1252 lookup_keyname (const char *name, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1253 {
1254     if (name[0] != '\0')
1255     {
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;
1259 
1260         if (name[1] == '\0')
1261         {
1262             *idx = -1;
1263             return (int) name[0];
1264         }
1265 
1266         sort_key_conv_tab (KEY_SORTBYNAME);
1267 
1268         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1269                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1270 
1271         if (res != NULL)
1272         {
1273             *idx = (int) (res - key_conv_tab_sorted);
1274             return (*res)->code;
1275         }
1276     }
1277 
1278     *idx = -1;
1279     return 0;
1280 }
1281 
1282 /* --------------------------------------------------------------------------------------------- */
1283 
1284 static gboolean
1285 lookup_keycode (const long code, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1286 {
1287     if (code != 0)
1288     {
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;
1292 
1293         sort_key_conv_tab (KEY_SORTBYCODE);
1294 
1295         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1296                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1297 
1298         if (res != NULL)
1299         {
1300             *idx = (int) (res - key_conv_tab_sorted);
1301             return TRUE;
1302         }
1303     }
1304 
1305     *idx = -1;
1306     return FALSE;
1307 }
1308 
1309 /* --------------------------------------------------------------------------------------------- */
1310 /*** public functions ****************************************************************************/
1311 /* --------------------------------------------------------------------------------------------- */
1312 /* This has to be called before init_slang or whatever routine
1313    calls any define_sequence */
1314 
1315 void
1316 init_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1317 {
1318     const char *term;
1319 
1320     term = getenv ("TERM");
1321 
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);
1325 
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);
1333 
1334     /* load some additional keys (e.g. direct Alt-? support) */
1335     load_xtra_key_defines ();
1336 
1337 #ifdef __QNX__
1338     if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1339     {
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]...
1343          *
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!
1348          *
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!]
1352          */
1353         use_8th_bit_as_meta = FALSE;
1354     }
1355 #endif /* __QNX__ */
1356 
1357 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1358     init_key_x11 ();
1359 #endif
1360 
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);
1365 }
1366 
1367 /* --------------------------------------------------------------------------------------------- */
1368 /**
1369  * This has to be called after SLang_init_tty/slint_init
1370  */
1371 
1372 void
1373 init_key_input_fd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1374 {
1375 #ifdef HAVE_SLANG
1376     input_fd = SLang_TT_Read_FD;
1377 #endif
1378 }
1379 
1380 /* --------------------------------------------------------------------------------------------- */
1381 
1382 void
1383 done_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1384 {
1385     k_dispose (keys);
1386     g_slist_free_full (select_list, g_free);
1387 
1388 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1389     if (x11_display)
1390         mc_XCloseDisplay (x11_display);
1391 #endif
1392 }
1393 
1394 /* --------------------------------------------------------------------------------------------- */
1395 
1396 void
1397 add_select_channel (int fd, select_fn callback, void *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
1398 {
1399     select_t *new;
1400 
1401     new = g_new (select_t, 1);
1402     new->fd = fd;
1403     new->callback = callback;
1404     new->info = info;
1405 
1406     select_list = g_slist_prepend (select_list, new);
1407 }
1408 
1409 /* --------------------------------------------------------------------------------------------- */
1410 
1411 void
1412 delete_select_channel (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
1413 {
1414     GSList *p;
1415 
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);
1419 }
1420 
1421 /* --------------------------------------------------------------------------------------------- */
1422 
1423 void
1424 channels_up (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1425 {
1426     if (disabled_channels == 0)
1427         fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1428     disabled_channels--;
1429 }
1430 
1431 /* --------------------------------------------------------------------------------------------- */
1432 
1433 void
1434 channels_down (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1435 {
1436     disabled_channels++;
1437 }
1438 
1439 /* --------------------------------------------------------------------------------------------- */
1440 /**
1441  * Return the code associated with the symbolic name keyname
1442  */
1443 
1444 long
1445 tty_keyname_to_keycode (const char *name, char **label)
     /* [previous][next][first][last][top][bottom][index][help]  */
1446 {
1447     char **lc_keys, **p;
1448     char *cname;
1449     int k = -1;
1450     int key = 0;
1451     int lc_index = -1;
1452 
1453     int use_meta = -1;
1454     int use_ctrl = -1;
1455     int use_shift = -1;
1456 
1457     if (name == NULL)
1458         return 0;
1459 
1460     cname = g_strstrip (g_strdup (name));
1461     lc_keys = g_strsplit_set (cname, "-+ ", -1);
1462     g_free (cname);
1463 
1464     for (p = lc_keys; p != NULL && *p != NULL; p++)
1465     {
1466         if ((*p)[0] != '\0')
1467         {
1468             int idx;
1469 
1470             key = lookup_keyname (g_strstrip (*p), &idx);
1471 
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
1479             {
1480                 k = key;
1481                 lc_index = idx;
1482                 break;
1483             }
1484         }
1485     }
1486 
1487     g_strfreev (lc_keys);
1488 
1489     /* output */
1490     if (k <= 0)
1491         return 0;
1492 
1493     if (label != NULL)
1494     {
1495         GString *s;
1496 
1497         s = g_string_new ("");
1498 
1499         if (use_meta != -1)
1500         {
1501             g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1502             g_string_append_c (s, '-');
1503         }
1504         if (use_ctrl != -1)
1505         {
1506             g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1507             g_string_append_c (s, '-');
1508         }
1509         if (use_shift != -1)
1510         {
1511             if (k < 127)
1512                 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1513             else
1514             {
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);
1518             }
1519         }
1520         else if (k < 128)
1521         {
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);
1526         }
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));
1531 
1532         *label = g_string_free (s, FALSE);
1533     }
1534 
1535     if (use_shift != -1)
1536     {
1537         if (k < 127 && k > 31)
1538             k = g_ascii_toupper ((gchar) k);
1539         else
1540             k |= KEY_M_SHIFT;
1541     }
1542 
1543     if (use_ctrl != -1)
1544     {
1545         if (k < 256)
1546             k = XCTRL (k);
1547         else
1548             k |= KEY_M_CTRL;
1549     }
1550 
1551     if (use_meta != -1)
1552         k = ALT (k);
1553 
1554     return (long) k;
1555 }
1556 
1557 /* --------------------------------------------------------------------------------------------- */
1558 
1559 char *
1560 tty_keycode_to_keyname (const int keycode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1561 {
1562     /* code without modifier */
1563     unsigned int k = keycode & ~KEY_M_MASK;
1564     /* modifier */
1565     unsigned int mod = keycode & KEY_M_MASK;
1566 
1567     int key_idx = -1;
1568 
1569     GString *s;
1570     int idx;
1571 
1572     s = g_string_sized_new (8);
1573 
1574     if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1575     {
1576         if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx))
1577         {
1578             g_string_append (s, key_conv_tab_sorted[idx]->name);
1579             g_string_append_c (s, '-');
1580         }
1581 
1582         if ((mod & KEY_M_CTRL) != 0)
1583         {
1584             /* non printeble chars like a CTRL-[A..Z] */
1585             if (k < 32)
1586                 k += 64;
1587 
1588             if (lookup_keycode (KEY_M_CTRL, &idx))
1589             {
1590                 g_string_append (s, key_conv_tab_sorted[idx]->name);
1591                 g_string_append_c (s, '-');
1592             }
1593         }
1594 
1595         if ((mod & KEY_M_SHIFT) != 0)
1596         {
1597             if (lookup_keycode (KEY_M_ALT, &idx))
1598             {
1599                 if (k < 127)
1600                     g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1601                 else
1602                 {
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);
1606                 }
1607             }
1608         }
1609         else if (k < 128)
1610         {
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);
1615         }
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);
1620     }
1621 
1622     return g_string_free (s, s->len == 0);
1623 }
1624 
1625 /* --------------------------------------------------------------------------------------------- */
1626 /**
1627  * Return TRUE on success, FALSE on error.
1628  * An error happens if SEQ is a beginning of an existing longer sequence.
1629  */
1630 
1631 gboolean
1632 define_sequence (int code, const char *seq, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1633 {
1634     key_def *base;
1635 
1636     if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1637         return FALSE;
1638 
1639     for (base = keys; (base != NULL) && (*seq != '\0');)
1640         if (*seq == base->ch)
1641         {
1642             if (base->child == NULL)
1643             {
1644                 if (*(seq + 1) != '\0')
1645                     base->child = create_sequence (seq + 1, code, action);
1646                 else
1647                 {
1648                     /* The sequence matches an existing one.  */
1649                     base->code = code;
1650                     base->action = action;
1651                 }
1652                 return TRUE;
1653             }
1654 
1655             base = base->child;
1656             seq++;
1657         }
1658         else
1659         {
1660             if (base->next != NULL)
1661                 base = base->next;
1662             else
1663             {
1664                 base->next = create_sequence (seq, code, action);
1665                 return TRUE;
1666             }
1667         }
1668 
1669     if (*seq == '\0')
1670     {
1671         /* Attempt to redefine a sequence with a shorter sequence.  */
1672         return FALSE;
1673     }
1674 
1675     keys = create_sequence (seq, code, action);
1676     return TRUE;
1677 }
1678 
1679 /* --------------------------------------------------------------------------------------------- */
1680 /**
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.
1683  */
1684 gboolean
1685 is_idle (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1686 {
1687     int nfd;
1688     fd_set select_set;
1689     struct timeval time_out;
1690 
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)
1698     {
1699         if (gpm_fd >= 0)
1700         {
1701             FD_SET (gpm_fd, &select_set);
1702             nfd = MAX (nfd, gpm_fd + 1);
1703         }
1704         else
1705         {
1706             if (mouse_fd >= 0)  /* error indicative */
1707             {
1708                 if (FD_ISSET (mouse_fd, &select_set))
1709                     FD_CLR (mouse_fd, &select_set);
1710                 mouse_fd = gpm_fd;
1711             }
1712             /* gpm_fd == -2 means under some X terminal */
1713             if (gpm_fd == -1)
1714             {
1715                 mouse_enabled = FALSE;
1716                 use_mouse_p = MOUSE_NONE;
1717             }
1718         }
1719     }
1720 #endif
1721     return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1722 }
1723 
1724 /* --------------------------------------------------------------------------------------------- */
1725 
1726 int
1727 get_key_code (int no_delay)
     /* [previous][next][first][last][top][bottom][index][help]  */
1728 {
1729     int c;
1730     static key_def *this = NULL, *parent;
1731     static gint64 esc_time = -1;
1732     static int lastnodelay = -1;
1733 
1734     if (no_delay != lastnodelay)
1735     {
1736         this = NULL;
1737         lastnodelay = no_delay;
1738     }
1739 
1740   pend_send:
1741     if (pending_keys != NULL)
1742     {
1743         gboolean bad_seq;
1744 
1745         c = *pending_keys++;
1746         while (c == ESC_CHAR)
1747             c = ALT (*pending_keys++);
1748 
1749         bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0');
1750         if (*pending_keys == '\0' || bad_seq)
1751             pending_keys = seq_append = NULL;
1752 
1753         if (bad_seq)
1754         {
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.
1760              */
1761             int paranoia = 20;
1762 
1763             while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1764                 ;
1765         }
1766         else
1767         {
1768             if (c > 127 && c < 256 && use_8th_bit_as_meta)
1769                 c = ALT (c & 0x7f);
1770 
1771             goto done;
1772         }
1773     }
1774 
1775   nodelay_try_again:
1776     if (no_delay != 0)
1777         tty_nodelay (TRUE);
1778 
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
1784 
1785     if (no_delay != 0)
1786     {
1787         tty_nodelay (FALSE);
1788         if (c == -1)
1789         {
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;
1793 
1794             this = NULL;
1795             pending_keys = seq_append = NULL;
1796             return ESC_CHAR;
1797         }
1798     }
1799     else if (c == -1)
1800     {
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)
1805         {
1806             this = NULL;
1807             return -1;
1808         }
1809 
1810         pending_keys = seq_buffer;
1811         goto pend_send;
1812     }
1813 
1814     /* Search the key on the root */
1815     if (no_delay == 0 || this == NULL)
1816     {
1817         this = keys;
1818         parent = NULL;
1819 
1820         if (c > 127 && c < 256 && use_8th_bit_as_meta)
1821         {
1822             c &= 0x7f;
1823 
1824             /* The first sequence defined starts with esc */
1825             parent = keys;
1826             this = keys->child;
1827         }
1828     }
1829 
1830     while (this != NULL)
1831     {
1832         if (c == this->ch)
1833         {
1834             if (this->child == NULL)
1835             {
1836                 /* We got a complete match, return and reset search */
1837                 pending_keys = seq_append = NULL;
1838                 c = this->code;
1839                 goto done;
1840             }
1841 
1842             /* No match yet, but it may be a prefix for a valid seq */
1843             if (!push_char (c))
1844             {
1845                 pending_keys = seq_buffer;
1846                 goto pend_send;
1847             }
1848 
1849             parent = this;
1850             this = this->child;
1851             if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1852             {
1853                 if (no_delay != 0)
1854                 {
1855                     esc_time = g_get_monotonic_time ();
1856                     goto nodelay_try_again;
1857                 }
1858 
1859                 esc_time = -1;
1860                 c = getch_with_timeout (old_esc_mode_timeout);
1861                 if (c != -1)
1862                     continue;
1863 
1864                 pending_keys = seq_append = NULL;
1865                 this = NULL;
1866                 return ESC_CHAR;
1867             }
1868 
1869             if (no_delay != 0)
1870                 goto nodelay_try_again;
1871             c = tty_lowlevel_getch ();
1872             continue;
1873         }
1874 
1875         /* c != this->ch. Try other keys with this prefix */
1876         if (this->next != NULL)
1877         {
1878             this = this->next;
1879             continue;
1880         }
1881 
1882         /* No match found. Is it one of our ESC <key> specials? */
1883         if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1884         {
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);
1892 
1893             pending_keys = seq_append = NULL;
1894             goto done;
1895         }
1896 
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) */
1902 
1903   done:
1904     this = NULL;
1905     return correct_key_code (c);
1906 }
1907 
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 */
1913 
1914 int
1915 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
     /* [previous][next][first][last][top][bottom][index][help]  */
1916 {
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;
1925 
1926     if ((dirty == 3) || is_idle ())
1927     {
1928         mc_refresh ();
1929         dirty = 1;
1930     }
1931     else
1932         dirty++;
1933 
1934     vfs_timeout_handler ();
1935 
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.
1939      */
1940     if (event->x > 0)
1941     {
1942         show_mouse_pointer (event->x, event->y);
1943         if (!redo_event)
1944             event->x = -1;
1945     }
1946 
1947     /* Repeat if using mouse */
1948     while (pending_keys == NULL)
1949     {
1950         int nfd;
1951         fd_set select_set;
1952 
1953         FD_ZERO (&select_set);
1954         FD_SET (input_fd, &select_set);
1955         nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1956 
1957 #ifdef HAVE_LIBGPM
1958         if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1959         {
1960             if (gpm_fd >= 0)
1961             {
1962                 FD_SET (gpm_fd, &select_set);
1963                 nfd = MAX (nfd, gpm_fd + 1);
1964             }
1965             else
1966             {
1967                 if (mouse_fd >= 0)      /* error indicative */
1968                 {
1969                     if (FD_ISSET (mouse_fd, &select_set))
1970                         FD_CLR (mouse_fd, &select_set);
1971                     mouse_fd = gpm_fd;
1972                 }
1973                 /* gpm_fd == -2 means under some X terminal */
1974                 if (gpm_fd == -1)
1975                 {
1976                     mouse_enabled = FALSE;
1977                     use_mouse_p = MOUSE_NONE;
1978                 }
1979                 break;
1980             }
1981         }
1982 #endif
1983 
1984         if (redo_event)
1985         {
1986             time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC;
1987             time_out.tv_sec = 0;
1988 
1989             time_addr = &time_out;
1990         }
1991         else
1992         {
1993             int seconds;
1994 
1995             seconds = vfs_timeouts ();
1996             time_addr = NULL;
1997 
1998             if (seconds != 0)
1999             {
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.
2003                  */
2004 
2005                 time_out.tv_sec = seconds;
2006                 time_out.tv_usec = 0;
2007                 time_addr = &time_out;
2008             }
2009         }
2010 
2011         if (!block || tty_got_winch ())
2012         {
2013             time_addr = &time_out;
2014             time_out.tv_sec = 0;
2015             time_out.tv_usec = 0;
2016         }
2017 
2018         tty_enable_interrupt_key ();
2019         flag = select (nfd, &select_set, NULL, NULL, time_addr);
2020         tty_disable_interrupt_key ();
2021 
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.
2026          */
2027         if (flag == 0)
2028         {
2029             if (redo_event)
2030                 return EV_MOUSE;
2031             if (!block || tty_got_winch ())
2032                 return EV_NONE;
2033             vfs_timeout_handler ();
2034         }
2035         if (flag == -1 && errno == EINTR)
2036             return EV_NONE;
2037 
2038         check_selects (&select_set);
2039 
2040         if (FD_ISSET (input_fd, &select_set))
2041             break;
2042 
2043 #ifdef HAVE_LIBGPM
2044         if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2045         {
2046             if (gpm_fd >= 0)
2047             {
2048                 if (FD_ISSET (gpm_fd, &select_set))
2049                 {
2050                     int status;
2051 
2052                     status = Gpm_GetEvent (&ev);
2053                     if (status == 1)    /* success */
2054                     {
2055                         Gpm_FitEvent (&ev);
2056                         *event = ev;
2057                         return EV_MOUSE;
2058                     }
2059                     if (status <= 0)    /* connection closed; -1 == error */
2060                     {
2061                         if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2062                             FD_CLR (mouse_fd, &select_set);
2063 
2064                         disable_mouse ();
2065                         return EV_NONE;
2066                     }
2067                 }
2068             }
2069             else
2070             {
2071                 if (mouse_fd >= 0)      /* error indicative */
2072                 {
2073                     if (FD_ISSET (mouse_fd, &select_set))
2074                         FD_CLR (mouse_fd, &select_set);
2075                     mouse_fd = gpm_fd;
2076                 }
2077                 /* gpm_fd == -2 means under some X terminal */
2078                 if (gpm_fd == -1)
2079                 {
2080                     mouse_enabled = FALSE;
2081                     use_mouse_p = MOUSE_NONE;
2082                 }
2083                 break;
2084             }
2085         }
2086 #endif /* !HAVE_LIBGPM */
2087     }
2088 
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);
2094 
2095 #ifndef HAVE_SLANG
2096     if (flag > 0)
2097         tty_touch_screen ();
2098 #endif /* !HAVE_SLANG */
2099 
2100     if (mouse_enabled && (c == MCKEY_MOUSE
2101 #ifdef KEY_MOUSE
2102                           || c == KEY_MOUSE
2103 #endif /* KEY_MOUSE */
2104                           || c == MCKEY_EXTENDED_MOUSE))
2105     {
2106         /* Mouse event. See tickets 2956 and 3954 for extended mode detection. */
2107         gboolean extended = c == MCKEY_EXTENDED_MOUSE;
2108 
2109 #ifdef KEY_MOUSE
2110         extended = extended || (c == KEY_MOUSE && xmouse_seq == NULL
2111                                 && xmouse_extended_seq != NULL);
2112 #endif /* KEY_MOUSE */
2113 
2114         xmouse_get_event (event, extended);
2115         c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2116     }
2117     else if (c == MCKEY_BRACKETED_PASTING_START)
2118     {
2119         bracketed_pasting_in_progress = TRUE;
2120         c = EV_NONE;
2121     }
2122     else if (c == MCKEY_BRACKETED_PASTING_END)
2123     {
2124         bracketed_pasting_in_progress = FALSE;
2125         c = EV_NONE;
2126     }
2127 
2128     return c;
2129 }
2130 
2131 /* --------------------------------------------------------------------------------------------- */
2132 /* Returns a key press, mouse events are discarded */
2133 
2134 int
2135 tty_getch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2136 {
2137     Gpm_Event ev;
2138     int key;
2139 
2140     ev.x = -1;
2141     while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
2142         ;
2143     return key;
2144 }
2145 
2146 /* --------------------------------------------------------------------------------------------- */
2147 
2148 char *
2149 learn_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2150 {
2151     /* LEARN_TIMEOUT in ms */
2152 #define LEARN_TIMEOUT 200
2153 
2154     fd_set Read_FD_Set;
2155     gint64 end_time;
2156     int c;
2157     GString *buffer;
2158 
2159     buffer = g_string_sized_new (16);
2160 
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);
2166 
2167     end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC;
2168 
2169     tty_nodelay (TRUE);
2170     while (TRUE)
2171     {
2172         while ((c = tty_lowlevel_getch ()) == -1)
2173         {
2174             gint64 time_out;
2175             struct timeval tv;
2176 
2177             time_out = end_time - g_get_monotonic_time ();
2178             if (time_out <= 0)
2179                 break;
2180 
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);
2186         }
2187         if (c == -1)
2188             break;
2189         learn_store_key (buffer, c);
2190     }
2191     tty_keypad (TRUE);
2192     tty_nodelay (FALSE);
2193 
2194     return g_string_free (buffer, buffer->len == 0);
2195 #undef LEARN_TIMEOUT
2196 }
2197 
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) */
2202 
2203 void
2204 numeric_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2205 {
2206     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2207     {
2208         fputs (ESC_STR ">", stdout);
2209         fflush (stdout);
2210     }
2211 }
2212 
2213 /* --------------------------------------------------------------------------------------------- */
2214 
2215 void
2216 application_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2217 {
2218     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2219     {
2220         fputs (ESC_STR "=", stdout);
2221         fflush (stdout);
2222     }
2223 }
2224 
2225 /* --------------------------------------------------------------------------------------------- */
2226 
2227 void
2228 enable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2229 {
2230     printf (ESC_STR "[?2004h");
2231     fflush (stdout);
2232 }
2233 
2234 /* --------------------------------------------------------------------------------------------- */
2235 
2236 void
2237 disable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2238 {
2239     printf (ESC_STR "[?2004l");
2240     fflush (stdout);
2241     bracketed_pasting_in_progress = FALSE;
2242 }
2243 
2244 /* --------------------------------------------------------------------------------------------- */

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