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. lookup_key
  30. lookup_key_by_code
  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-2021
   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_MSEC_PER_SEC  1000
 224 #define MC_USEC_PER_MSEC 1000
 225 
 226 /* The maximum sequence length (32 + null terminator) */
 227 #define SEQ_BUFFER_LEN 33
 228 
 229 /*** file scope type declarations ****************************************************************/
 230 
 231 /* Linux console keyboard modifiers */
 232 typedef enum
 233 {
 234     SHIFT_PRESSED = (1 << 0),
 235     ALTR_PRESSED = (1 << 1),
 236     CONTROL_PRESSED = (1 << 2),
 237     ALTL_PRESSED = (1 << 3)
 238 } mod_pressed_t;
 239 
 240 typedef struct key_def
 241 {
 242     char ch;                    /* Holds the matching char code */
 243     int code;                   /* The code returned, valid if child == NULL */
 244     struct key_def *next;
 245     struct key_def *child;      /* sequence continuation */
 246     int action;                 /* optional action to be done. Now used only
 247                                    to mark that we are just after the first
 248                                    Escape */
 249 } key_def;
 250 
 251 typedef struct
 252 {
 253     int code;
 254     const char *seq;
 255     int action;
 256 } key_define_t;
 257 
 258 /* File descriptor monitoring add/remove routines */
 259 typedef struct
 260 {
 261     int fd;
 262     select_fn callback;
 263     void *info;
 264 } select_t;
 265 
 266 typedef enum KeySortType
 267 {
 268     KEY_NOSORT = 0,
 269     KEY_SORTBYNAME,
 270     KEY_SORTBYCODE
 271 } KeySortType;
 272 
 273 #ifdef __QNXNTO__
 274 typedef int (*ph_dv_f) (void *, void *);
 275 typedef int (*ph_ov_f) (void *);
 276 typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *);
 277 #endif
 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 combinatios,
 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 /* HAVE_TEXTMODE_X11_SUPPORT */
 550 
 551 static KeySortType has_been_sorted = KEY_NOSORT;
 552 
 553 /* *INDENT-OFF* */
 554 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
 555 /* *INDENT-ON* */
 556 
 557 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
 558 
 559 /* --------------------------------------------------------------------------------------------- */
 560 /*** file scope functions ************************************************************************/
 561 /* --------------------------------------------------------------------------------------------- */
 562 
 563 static int
 564 select_cmp_by_fd_set (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 565 {
 566     const select_t *s = (const select_t *) a;
 567     const fd_set *f = (const fd_set *) b;
 568 
 569     return (FD_ISSET (s->fd, f) ? 0 : 1);
 570 }
 571 
 572 /* --------------------------------------------------------------------------------------------- */
 573 
 574 static int
 575 select_cmp_by_fd (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 576 {
 577     const select_t *s = (const select_t *) a;
 578     const int fd = GPOINTER_TO_INT (b);
 579 
 580     return (s->fd == fd ? 0 : 1);
 581 }
 582 
 583 /* --------------------------------------------------------------------------------------------- */
 584 
 585 static int
 586 add_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     int top_fd = 0;
 589 
 590     if (disabled_channels == 0)
 591     {
 592         GSList *s;
 593 
 594         for (s = select_list; s != NULL; s = g_slist_next (s))
 595         {
 596             select_t *p = (select_t *) s->data;
 597 
 598             FD_SET (p->fd, select_set);
 599             if (p->fd > top_fd)
 600                 top_fd = p->fd;
 601         }
 602     }
 603 
 604     return top_fd;
 605 }
 606 
 607 /* --------------------------------------------------------------------------------------------- */
 608 
 609 static void
 610 check_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 611 {
 612     while (disabled_channels == 0)
 613     {
 614         GSList *s;
 615         select_t *p;
 616 
 617         s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set);
 618         if (s == NULL)
 619             break;
 620 
 621         p = (select_t *) s->data;
 622         FD_CLR (p->fd, select_set);
 623         p->callback (p->fd, p->info);
 624     }
 625 }
 626 
 627 /* --------------------------------------------------------------------------------------------- */
 628 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
 629 
 630 static void
 631 try_channels (gboolean set_timeout)
     /* [previous][next][first][last][top][bottom][index][help]  */
 632 {
 633     struct timeval time_out;
 634     static fd_set select_set;
 635 
 636     while (TRUE)
 637     {
 638         struct timeval *timeptr = NULL;
 639         int maxfdp, v;
 640 
 641         FD_ZERO (&select_set);
 642         FD_SET (input_fd, &select_set); /* Add stdin */
 643         maxfdp = MAX (add_selects (&select_set), input_fd);
 644 
 645         if (set_timeout)
 646         {
 647             time_out.tv_sec = 0;
 648             time_out.tv_usec = 100 * MC_USEC_PER_MSEC;
 649             timeptr = &time_out;
 650         }
 651 
 652         v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
 653         if (v > 0)
 654         {
 655             check_selects (&select_set);
 656             if (FD_ISSET (input_fd, &select_set))
 657                 break;
 658         }
 659     }
 660 }
 661 
 662 /* --------------------------------------------------------------------------------------------- */
 663 
 664 static key_def *
 665 create_sequence (const char *seq, int code, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 666 {
 667     key_def *base, *p, *attach;
 668 
 669     for (base = attach = NULL; *seq != '\0'; seq++)
 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 = p->next = NULL;
 680         if (seq[1] == '\0')
 681             p->action = action;
 682         else
 683             p->action = MCKEY_NOACTION;
 684         attach = p;
 685     }
 686     return base;
 687 }
 688 
 689 /* --------------------------------------------------------------------------------------------- */
 690 
 691 static void
 692 define_sequences (const key_define_t * kd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 693 {
 694     int i;
 695 
 696     for (i = 0; kd[i].code != 0; i++)
 697         define_sequence (kd[i].code, kd[i].seq, kd[i].action);
 698 }
 699 
 700 /* --------------------------------------------------------------------------------------------- */
 701 
 702 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 703 static void
 704 init_key_x11 (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 705 {
 706     if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
 707     {
 708         x11_display = mc_XOpenDisplay (0);
 709 
 710         if (x11_display != NULL)
 711             x11_window = DefaultRootWindow (x11_display);
 712     }
 713 }
 714 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 715 
 716 /* --------------------------------------------------------------------------------------------- */
 717 /* Workaround for System V Curses vt100 bug */
 718 
 719 static int
 720 getch_with_delay (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 721 {
 722     int c;
 723 
 724     /* This routine could be used on systems without mouse support,
 725        so we need to do the select check :-( */
 726     while (TRUE)
 727     {
 728         if (pending_keys == NULL)
 729             try_channels (FALSE);
 730 
 731         /* Try to get a character */
 732         c = get_key_code (0);
 733         if (c != -1)
 734             break;
 735 
 736         /* Failed -> wait 0.1 secs and try again */
 737         try_channels (TRUE);
 738     }
 739 
 740     /* Success -> return the character */
 741     return c;
 742 }
 743 
 744 /* --------------------------------------------------------------------------------------------- */
 745 
 746 static void
 747 xmouse_get_event (Gpm_Event * ev, gboolean extended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 748 {
 749     static gint64 tv1 = 0;      /* Force first click as single */
 750     static int clicks = 0;
 751     static int last_btn = 0;
 752     int btn;
 753 
 754     /* Decode Xterm mouse information to a GPM style event */
 755 
 756     if (!extended)
 757     {
 758         /* Variable btn has following meaning: */
 759         /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
 760         btn = tty_lowlevel_getch () - 32;
 761         /* Coordinates are 33-based */
 762         /* Transform them to 1-based */
 763         ev->x = tty_lowlevel_getch () - 32;
 764         ev->y = tty_lowlevel_getch () - 32;
 765     }
 766     else
 767     {
 768         /* SGR 1006 extension (e.g. "\e[<0;12;300M"):
 769            - Numbers are encoded in decimal to make it ASCII-safe
 770            and to overcome the limit of 223 columns/rows.
 771            - Mouse release is encoded by trailing 'm' rather than 'M'
 772            so that the released button can be reported.
 773            - Numbers are no longer offset by 32. */
 774         char c;
 775 
 776         btn = ev->x = ev->y = 0;
 777         ev->type = 0;           /* In case we return on an invalid sequence */
 778 
 779         while ((c = tty_lowlevel_getch ()) != ';')
 780         {
 781             if (c < '0' || c > '9')
 782                 return;
 783             btn = 10 * btn + (c - '0');
 784         }
 785         while ((c = tty_lowlevel_getch ()) != ';')
 786         {
 787             if (c < '0' || c > '9')
 788                 return;
 789             ev->x = 10 * ev->x + (c - '0');
 790         }
 791         while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
 792         {
 793             if (c < '0' || c > '9')
 794                 return;
 795             ev->y = 10 * ev->y + (c - '0');
 796         }
 797         /* Legacy mouse protocol doesn't tell which button was released,
 798            conveniently all of mc's widgets are written not to rely on this
 799            information. With the SGR extension the released button becomes
 800            known, but for the sake of simplicity we just ignore it. */
 801         if (c == 'm')
 802             btn = 3;
 803     }
 804 
 805     /* There seems to be no way of knowing which button was released */
 806     /* So we assume all the buttons were released */
 807 
 808     if (btn == 3)
 809     {
 810         if (last_btn != 0)
 811         {
 812             if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
 813             {
 814                 /* FIXME: DIRTY HACK */
 815                 /* don't generate GPM_UP after mouse wheel */
 816                 /* need for menu event handling */
 817                 ev->type = 0;
 818                 tv1 = 0;
 819             }
 820             else
 821             {
 822                 ev->type = GPM_UP | (GPM_SINGLE << clicks);
 823                 tv1 = g_get_monotonic_time ();
 824             }
 825             ev->buttons = 0;
 826             last_btn = 0;
 827             clicks = 0;
 828         }
 829         else
 830         {
 831             /* Bogus event, maybe mouse wheel */
 832             ev->type = 0;
 833         }
 834     }
 835     else
 836     {
 837         gint64 tv2;
 838 
 839         if (btn >= 32 && btn <= 34)
 840         {
 841             btn -= 32;
 842             ev->type = GPM_DRAG;
 843         }
 844         else
 845             ev->type = GPM_DOWN;
 846 
 847         tv2 = g_get_monotonic_time ();
 848         if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC)
 849         {
 850             clicks++;
 851             clicks %= 3;
 852         }
 853         else
 854             clicks = 0;
 855 
 856         switch (btn)
 857         {
 858         case 0:
 859             ev->buttons = GPM_B_LEFT;
 860             break;
 861         case 1:
 862             ev->buttons = GPM_B_MIDDLE;
 863             break;
 864         case 2:
 865             ev->buttons = GPM_B_RIGHT;
 866             break;
 867         case 64:
 868             ev->buttons = GPM_B_UP;
 869             clicks = 0;
 870             break;
 871         case 65:
 872             ev->buttons = GPM_B_DOWN;
 873             clicks = 0;
 874             break;
 875         default:
 876             /* Nothing */
 877             ev->type = 0;
 878             ev->buttons = 0;
 879             break;
 880         }
 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 /* __QNXNTO__ */
 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,
 912                           &root_y, &win_x, &win_y, &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 /* HAVE_TEXTMODE_X11_SUPPORT */
 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 /* __QNXNTO__ */
 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 /* !__linux__ */
 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 /* __QNXNTO__ */
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 /* __QNXNTO__ */
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 (char *buffer, char **p, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1176 {
1177     if (*p - buffer > 253)
1178         return;
1179 
1180     if (c == ESC_CHAR)
1181     {
1182         *(*p)++ = '\\';
1183         *(*p)++ = 'e';
1184     }
1185     else if (c < ' ')
1186     {
1187         *(*p)++ = '^';
1188         *(*p)++ = c + 'a' - 1;
1189     }
1190     else if (c == '^')
1191     {
1192         *(*p)++ = '^';
1193         *(*p)++ = '^';
1194     }
1195     else
1196         *(*p)++ = (char) c;
1197 }
1198 
1199 /* --------------------------------------------------------------------------------------------- */
1200 
1201 static void
1202 k_dispose (key_def * k)
     /* [previous][next][first][last][top][bottom][index][help]  */
1203 {
1204     if (k != NULL)
1205     {
1206         k_dispose (k->child);
1207         k_dispose (k->next);
1208         g_free (k);
1209     }
1210 }
1211 
1212 /* --------------------------------------------------------------------------------------------- */
1213 
1214 static int
1215 key_code_comparator_by_name (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1216 {
1217     const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1218     const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1219 
1220     return g_ascii_strcasecmp (n1->name, n2->name);
1221 }
1222 
1223 /* --------------------------------------------------------------------------------------------- */
1224 
1225 static int
1226 key_code_comparator_by_code (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1227 {
1228     const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1229     const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1230 
1231     return n1->code - n2->code;
1232 }
1233 
1234 /* --------------------------------------------------------------------------------------------- */
1235 
1236 static inline void
1237 sort_key_conv_tab (enum KeySortType type_sort)
     /* [previous][next][first][last][top][bottom][index][help]  */
1238 {
1239     if (has_been_sorted != type_sort)
1240     {
1241         size_t i;
1242 
1243         for (i = 0; i < key_conv_tab_size; i++)
1244             key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1245 
1246         if (type_sort == KEY_SORTBYNAME)
1247             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1248                    &key_code_comparator_by_name);
1249         else if (type_sort == KEY_SORTBYCODE)
1250             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1251                    &key_code_comparator_by_code);
1252 
1253         has_been_sorted = type_sort;
1254     }
1255 }
1256 
1257 /* --------------------------------------------------------------------------------------------- */
1258 
1259 static int
1260 lookup_keyname (const char *name, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1261 {
1262     if (name[0] != '\0')
1263     {
1264         const key_code_name_t key = { 0, name, NULL, NULL };
1265         const key_code_name_t *keyp = &key;
1266         const key_code_name_t **res;
1267 
1268         if (name[1] == '\0')
1269         {
1270             *idx = -1;
1271             return (int) name[0];
1272         }
1273 
1274         sort_key_conv_tab (KEY_SORTBYNAME);
1275 
1276         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1277                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1278 
1279         if (res != NULL)
1280         {
1281             *idx = (int) (res - key_conv_tab_sorted);
1282             return (*res)->code;
1283         }
1284     }
1285 
1286     *idx = -1;
1287     return 0;
1288 }
1289 
1290 /* --------------------------------------------------------------------------------------------- */
1291 
1292 static gboolean
1293 lookup_keycode (const long code, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1294 {
1295     if (code != 0)
1296     {
1297         const key_code_name_t key = { code, NULL, NULL, NULL };
1298         const key_code_name_t *keyp = &key;
1299         const key_code_name_t **res;
1300 
1301         sort_key_conv_tab (KEY_SORTBYCODE);
1302 
1303         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1304                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1305 
1306         if (res != NULL)
1307         {
1308             *idx = (int) (res - key_conv_tab_sorted);
1309             return TRUE;
1310         }
1311     }
1312 
1313     *idx = -1;
1314     return FALSE;
1315 }
1316 
1317 /* --------------------------------------------------------------------------------------------- */
1318 /*** public functions ****************************************************************************/
1319 /* --------------------------------------------------------------------------------------------- */
1320 /* This has to be called before init_slang or whatever routine
1321    calls any define_sequence */
1322 
1323 void
1324 init_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1325 {
1326     const char *term;
1327 
1328     term = getenv ("TERM");
1329 
1330     /* This has to be the first define_sequence */
1331     /* So, we can assume that the first keys member has ESC */
1332     define_sequences (mc_default_keys);
1333 
1334     /* Terminfo on irix does not have some keys */
1335     if (mc_global.tty.xterm_flag
1336         || (term != NULL
1337             && (strncmp (term, "iris-ansi", 9) == 0
1338                 || strncmp (term, "xterm", 5) == 0
1339                 || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0)))
1340         define_sequences (xterm_key_defines);
1341 
1342     /* load some additional keys (e.g. direct Alt-? support) */
1343     load_xtra_key_defines ();
1344 
1345 #ifdef __QNX__
1346     if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1347     {
1348         /* Modify the default value of use_8th_bit_as_meta: we would
1349          * like to provide a working mc for a newbie who knows nothing
1350          * about [Options|Display bits|Full 8 bits input]...
1351          *
1352          * Don't use 'meta'-bit, when we are dealing with a
1353          * 'qnx*'-type terminal: clear the default value!
1354          * These terminal types use 0xFF as an escape character,
1355          * so use_8th_bit_as_meta==1 must not be enabled!
1356          *
1357          * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1358          * is not used now (doesn't even depend on use_8th_bit_as_meta
1359          * as in mc-3.1.2)...GREAT!...no additional code is required!]
1360          */
1361         use_8th_bit_as_meta = FALSE;
1362     }
1363 #endif /* __QNX__ */
1364 
1365 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1366     init_key_x11 ();
1367 #endif
1368 
1369     /* Load the qansi-m key definitions
1370        if we are running under the qansi-m terminal */
1371     if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1372         define_sequences (qansi_key_defines);
1373 }
1374 
1375 /* --------------------------------------------------------------------------------------------- */
1376 /**
1377  * This has to be called after SLang_init_tty/slint_init
1378  */
1379 
1380 void
1381 init_key_input_fd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1382 {
1383 #ifdef HAVE_SLANG
1384     input_fd = SLang_TT_Read_FD;
1385 #endif
1386 }
1387 
1388 /* --------------------------------------------------------------------------------------------- */
1389 
1390 void
1391 done_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1392 {
1393     k_dispose (keys);
1394     g_slist_free_full (select_list, g_free);
1395 
1396 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1397     if (x11_display)
1398         mc_XCloseDisplay (x11_display);
1399 #endif
1400 }
1401 
1402 /* --------------------------------------------------------------------------------------------- */
1403 
1404 void
1405 add_select_channel (int fd, select_fn callback, void *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
1406 {
1407     select_t *new;
1408 
1409     new = g_new (select_t, 1);
1410     new->fd = fd;
1411     new->callback = callback;
1412     new->info = info;
1413 
1414     select_list = g_slist_prepend (select_list, new);
1415 }
1416 
1417 /* --------------------------------------------------------------------------------------------- */
1418 
1419 void
1420 delete_select_channel (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
1421 {
1422     GSList *p;
1423 
1424     p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd);
1425     if (p != NULL)
1426         select_list = g_slist_delete_link (select_list, p);
1427 }
1428 
1429 /* --------------------------------------------------------------------------------------------- */
1430 
1431 void
1432 channels_up (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1433 {
1434     if (disabled_channels == 0)
1435         fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1436     disabled_channels--;
1437 }
1438 
1439 /* --------------------------------------------------------------------------------------------- */
1440 
1441 void
1442 channels_down (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1443 {
1444     disabled_channels++;
1445 }
1446 
1447 /* --------------------------------------------------------------------------------------------- */
1448 /**
1449  * Return the code associated with the symbolic name keyname
1450  */
1451 
1452 long
1453 lookup_key (const char *name, char **label)
     /* [previous][next][first][last][top][bottom][index][help]  */
1454 {
1455     char **lc_keys, **p;
1456     char *cname;
1457     int k = -1;
1458     int key = 0;
1459     int lc_index = -1;
1460 
1461     int use_meta = -1;
1462     int use_ctrl = -1;
1463     int use_shift = -1;
1464 
1465     if (name == NULL)
1466         return 0;
1467 
1468     cname = g_strstrip (g_strdup (name));
1469     lc_keys = g_strsplit_set (cname, "-+ ", -1);
1470     g_free (cname);
1471 
1472     for (p = lc_keys; p != NULL && *p != NULL; p++)
1473     {
1474         if ((*p)[0] != '\0')
1475         {
1476             int idx;
1477 
1478             key = lookup_keyname (g_strstrip (*p), &idx);
1479 
1480             if (key == KEY_M_ALT)
1481                 use_meta = idx;
1482             else if (key == KEY_M_CTRL)
1483                 use_ctrl = idx;
1484             else if (key == KEY_M_SHIFT)
1485                 use_shift = idx;
1486             else
1487             {
1488                 k = key;
1489                 lc_index = idx;
1490                 break;
1491             }
1492         }
1493     }
1494 
1495     g_strfreev (lc_keys);
1496 
1497     /* output */
1498     if (k <= 0)
1499         return 0;
1500 
1501     if (label != NULL)
1502     {
1503         GString *s;
1504 
1505         s = g_string_new ("");
1506 
1507         if (use_meta != -1)
1508         {
1509             g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1510             g_string_append_c (s, '-');
1511         }
1512         if (use_ctrl != -1)
1513         {
1514             g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1515             g_string_append_c (s, '-');
1516         }
1517         if (use_shift != -1)
1518         {
1519             if (k < 127)
1520                 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1521             else
1522             {
1523                 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1524                 g_string_append_c (s, '-');
1525                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1526             }
1527         }
1528         else if (k < 128)
1529         {
1530             if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1531                 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1532             else
1533                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1534         }
1535         else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1536             g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1537         else
1538             g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1539 
1540         *label = g_string_free (s, FALSE);
1541     }
1542 
1543     if (use_shift != -1)
1544     {
1545         if (k < 127 && k > 31)
1546             k = g_ascii_toupper ((gchar) k);
1547         else
1548             k |= KEY_M_SHIFT;
1549     }
1550 
1551     if (use_ctrl != -1)
1552     {
1553         if (k < 256)
1554             k = XCTRL (k);
1555         else
1556             k |= KEY_M_CTRL;
1557     }
1558 
1559     if (use_meta != -1)
1560         k = ALT (k);
1561 
1562     return (long) k;
1563 }
1564 
1565 /* --------------------------------------------------------------------------------------------- */
1566 
1567 char *
1568 lookup_key_by_code (const int keycode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1569 {
1570     /* code without modifier */
1571     unsigned int k = keycode & ~KEY_M_MASK;
1572     /* modifier */
1573     unsigned int mod = keycode & KEY_M_MASK;
1574 
1575     int key_idx = -1;
1576 
1577     GString *s;
1578     int idx;
1579 
1580     s = g_string_sized_new (8);
1581 
1582     if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1583     {
1584         if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx))
1585         {
1586             g_string_append (s, key_conv_tab_sorted[idx]->name);
1587             g_string_append_c (s, '-');
1588         }
1589 
1590         if ((mod & KEY_M_CTRL) != 0)
1591         {
1592             /* non printeble chars like a CTRL-[A..Z] */
1593             if (k < 32)
1594                 k += 64;
1595 
1596             if (lookup_keycode (KEY_M_CTRL, &idx))
1597             {
1598                 g_string_append (s, key_conv_tab_sorted[idx]->name);
1599                 g_string_append_c (s, '-');
1600             }
1601         }
1602 
1603         if ((mod & KEY_M_SHIFT) != 0)
1604         {
1605             if (lookup_keycode (KEY_M_ALT, &idx))
1606             {
1607                 if (k < 127)
1608                     g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1609                 else
1610                 {
1611                     g_string_append (s, key_conv_tab_sorted[idx]->name);
1612                     g_string_append_c (s, '-');
1613                     g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1614                 }
1615             }
1616         }
1617         else if (k < 128)
1618         {
1619             if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1620                 g_string_append_c (s, (gchar) k);
1621             else
1622                 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1623         }
1624         else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1625             g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1626         else
1627             g_string_append_c (s, (gchar) keycode);
1628     }
1629 
1630     return g_string_free (s, s->len == 0);
1631 }
1632 
1633 /* --------------------------------------------------------------------------------------------- */
1634 /**
1635  * Return TRUE on success, FALSE on error.
1636  * An error happens if SEQ is a beginning of an existing longer sequence.
1637  */
1638 
1639 gboolean
1640 define_sequence (int code, const char *seq, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1641 {
1642     key_def *base;
1643 
1644     if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1645         return FALSE;
1646 
1647     for (base = keys; (base != NULL) && (*seq != '\0');)
1648         if (*seq == base->ch)
1649         {
1650             if (base->child == NULL)
1651             {
1652                 if (*(seq + 1) != '\0')
1653                     base->child = create_sequence (seq + 1, code, action);
1654                 else
1655                 {
1656                     /* The sequence matches an existing one.  */
1657                     base->code = code;
1658                     base->action = action;
1659                 }
1660                 return TRUE;
1661             }
1662 
1663             base = base->child;
1664             seq++;
1665         }
1666         else
1667         {
1668             if (base->next != NULL)
1669                 base = base->next;
1670             else
1671             {
1672                 base->next = create_sequence (seq, code, action);
1673                 return TRUE;
1674             }
1675         }
1676 
1677     if (*seq == '\0')
1678     {
1679         /* Attempt to redefine a sequence with a shorter sequence.  */
1680         return FALSE;
1681     }
1682 
1683     keys = create_sequence (seq, code, action);
1684     return TRUE;
1685 }
1686 
1687 /* --------------------------------------------------------------------------------------------- */
1688 /**
1689  * Check if we are idle, i.e. there are no pending keyboard or mouse
1690  * events.  Return 1 is idle, 0 is there are pending events.
1691  */
1692 gboolean
1693 is_idle (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1694 {
1695     int nfd;
1696     fd_set select_set;
1697     struct timeval time_out;
1698 
1699     FD_ZERO (&select_set);
1700     FD_SET (input_fd, &select_set);
1701     nfd = MAX (0, input_fd) + 1;
1702     time_out.tv_sec = 0;
1703     time_out.tv_usec = 0;
1704 #ifdef HAVE_LIBGPM
1705     if (mouse_enabled && use_mouse_p == MOUSE_GPM)
1706     {
1707         if (gpm_fd >= 0)
1708         {
1709             FD_SET (gpm_fd, &select_set);
1710             nfd = MAX (nfd, gpm_fd + 1);
1711         }
1712         else
1713         {
1714             if (mouse_fd >= 0)  /* error indicative */
1715             {
1716                 if (FD_ISSET (mouse_fd, &select_set))
1717                     FD_CLR (mouse_fd, &select_set);
1718                 mouse_fd = gpm_fd;
1719             }
1720             /* gpm_fd == -2 means under some X terminal */
1721             if (gpm_fd == -1)
1722             {
1723                 mouse_enabled = FALSE;
1724                 use_mouse_p = MOUSE_NONE;
1725             }
1726         }
1727     }
1728 #endif
1729     return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1730 }
1731 
1732 /* --------------------------------------------------------------------------------------------- */
1733 
1734 int
1735 get_key_code (int no_delay)
     /* [previous][next][first][last][top][bottom][index][help]  */
1736 {
1737     int c;
1738     static key_def *this = NULL, *parent;
1739     static gint64 esc_time = -1;
1740     static int lastnodelay = -1;
1741 
1742     if (no_delay != lastnodelay)
1743     {
1744         this = NULL;
1745         lastnodelay = no_delay;
1746     }
1747 
1748   pend_send:
1749     if (pending_keys != NULL)
1750     {
1751         gboolean bad_seq;
1752 
1753         c = *pending_keys++;
1754         while (c == ESC_CHAR)
1755             c = ALT (*pending_keys++);
1756 
1757         bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0');
1758         if (*pending_keys == '\0' || bad_seq)
1759             pending_keys = seq_append = NULL;
1760 
1761         if (bad_seq)
1762         {
1763             /* This is an unknown ESC sequence.
1764              * To prevent interpreting its tail as a random garbage,
1765              * eat and discard all buffered and quickly following chars.
1766              * Small, but non-zero timeout is needed to reconnect
1767              * escape sequence split up by e.g. a serial line.
1768              */
1769             int paranoia = 20;
1770 
1771             while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1772                 ;
1773         }
1774         else
1775         {
1776             if (c > 127 && c < 256 && use_8th_bit_as_meta)
1777                 c = ALT (c & 0x7f);
1778 
1779             goto done;
1780         }
1781     }
1782 
1783   nodelay_try_again:
1784     if (no_delay != 0)
1785         tty_nodelay (TRUE);
1786 
1787     c = tty_lowlevel_getch ();
1788 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1789     if (c == KEY_RESIZE)
1790         goto nodelay_try_again;
1791 #endif
1792 
1793     if (no_delay != 0)
1794     {
1795         tty_nodelay (FALSE);
1796         if (c == -1)
1797         {
1798             if (this == NULL || parent == NULL || parent->action != MCKEY_ESCAPE || !old_esc_mode ||
1799                 esc_time == -1 || g_get_monotonic_time () < esc_time + old_esc_mode_timeout)
1800                 return -1;
1801 
1802             this = NULL;
1803             pending_keys = seq_append = NULL;
1804             return ESC_CHAR;
1805         }
1806     }
1807     else if (c == -1)
1808     {
1809         /* Maybe we got an incomplete match.
1810            This we do only in delay mode, since otherwise
1811            tty_lowlevel_getch can return -1 at any time. */
1812         if (seq_append == NULL)
1813         {
1814             this = NULL;
1815             return -1;
1816         }
1817 
1818         pending_keys = seq_buffer;
1819         goto pend_send;
1820     }
1821 
1822     /* Search the key on the root */
1823     if (no_delay == 0 || this == NULL)
1824     {
1825         this = keys;
1826         parent = NULL;
1827 
1828         if (c > 127 && c < 256 && use_8th_bit_as_meta)
1829         {
1830             c &= 0x7f;
1831 
1832             /* The first sequence defined starts with esc */
1833             parent = keys;
1834             this = keys->child;
1835         }
1836     }
1837 
1838     while (this != NULL)
1839     {
1840         if (c == this->ch)
1841         {
1842             if (this->child == NULL)
1843             {
1844                 /* We got a complete match, return and reset search */
1845                 pending_keys = seq_append = NULL;
1846                 c = this->code;
1847                 goto done;
1848             }
1849 
1850             /* No match yet, but it may be a prefix for a valid seq */
1851             if (!push_char (c))
1852             {
1853                 pending_keys = seq_buffer;
1854                 goto pend_send;
1855             }
1856 
1857             parent = this;
1858             this = this->child;
1859             if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1860             {
1861                 if (no_delay != 0)
1862                 {
1863                     esc_time = g_get_monotonic_time ();
1864                     goto nodelay_try_again;
1865                 }
1866 
1867                 esc_time = -1;
1868                 c = getch_with_timeout (old_esc_mode_timeout);
1869                 if (c != -1)
1870                     continue;
1871 
1872                 pending_keys = seq_append = NULL;
1873                 this = NULL;
1874                 return ESC_CHAR;
1875             }
1876 
1877             if (no_delay != 0)
1878                 goto nodelay_try_again;
1879             c = tty_lowlevel_getch ();
1880             continue;
1881         }
1882 
1883         /* c != this->ch. Try other keys with this prefix */
1884         if (this->next != NULL)
1885         {
1886             this = this->next;
1887             continue;
1888         }
1889 
1890         /* No match found. Is it one of our ESC <key> specials? */
1891         if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1892         {
1893             /* Convert escape-digits to F-keys */
1894             if (g_ascii_isdigit (c))
1895                 c = KEY_F (c - '0');
1896             else if (c == ' ')
1897                 c = ESC_CHAR;
1898             else
1899                 c = ALT (c);
1900 
1901             pending_keys = seq_append = NULL;
1902             goto done;
1903         }
1904 
1905         /* Unknown sequence. Maybe a prefix of a longer one. Save it. */
1906         push_char (c);
1907         pending_keys = seq_buffer;
1908         goto pend_send;
1909     }                           /* while (this != NULL) */
1910 
1911   done:
1912     this = NULL;
1913     return correct_key_code (c);
1914 }
1915 
1916 /* --------------------------------------------------------------------------------------------- */
1917 /* Returns a character read from stdin with appropriate interpretation */
1918 /* Also takes care of generated mouse events */
1919 /* Returns EV_MOUSE if it is a mouse event */
1920 /* Returns EV_NONE  if non-blocking or interrupt set and nothing was done */
1921 
1922 int
1923 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
     /* [previous][next][first][last][top][bottom][index][help]  */
1924 {
1925     int c;
1926     int flag = 0;               /* Return value from select */
1927 #ifdef HAVE_LIBGPM
1928     static struct Gpm_Event ev; /* Mouse event */
1929 #endif
1930     struct timeval time_out;
1931     struct timeval *time_addr = NULL;
1932     static int dirty = 3;
1933 
1934     if ((dirty == 3) || is_idle ())
1935     {
1936         mc_refresh ();
1937         dirty = 1;
1938     }
1939     else
1940         dirty++;
1941 
1942     vfs_timeout_handler ();
1943 
1944     /* Ok, we use (event->x < 0) to signal that the event does not contain
1945        a suitable position for the mouse, so we can't use show_mouse_pointer
1946        on it.
1947      */
1948     if (event->x > 0)
1949     {
1950         show_mouse_pointer (event->x, event->y);
1951         if (!redo_event)
1952             event->x = -1;
1953     }
1954 
1955     /* Repeat if using mouse */
1956     while (pending_keys == NULL)
1957     {
1958         int nfd;
1959         fd_set select_set;
1960 
1961         FD_ZERO (&select_set);
1962         FD_SET (input_fd, &select_set);
1963         nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1964 
1965 #ifdef HAVE_LIBGPM
1966         if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1967         {
1968             if (gpm_fd >= 0)
1969             {
1970                 FD_SET (gpm_fd, &select_set);
1971                 nfd = MAX (nfd, gpm_fd + 1);
1972             }
1973             else
1974             {
1975                 if (mouse_fd >= 0)      /* error indicative */
1976                 {
1977                     if (FD_ISSET (mouse_fd, &select_set))
1978                         FD_CLR (mouse_fd, &select_set);
1979                     mouse_fd = gpm_fd;
1980                 }
1981                 /* gpm_fd == -2 means under some X terminal */
1982                 if (gpm_fd == -1)
1983                 {
1984                     mouse_enabled = FALSE;
1985                     use_mouse_p = MOUSE_NONE;
1986                 }
1987                 break;
1988             }
1989         }
1990 #endif
1991 
1992         if (redo_event)
1993         {
1994             time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC;
1995             time_out.tv_sec = 0;
1996 
1997             time_addr = &time_out;
1998         }
1999         else
2000         {
2001             int seconds;
2002 
2003             seconds = vfs_timeouts ();
2004             time_addr = NULL;
2005 
2006             if (seconds != 0)
2007             {
2008                 /* the timeout could be improved and actually be
2009                  * the number of seconds until the next vfs entry
2010                  * timeouts in the stamp list.
2011                  */
2012 
2013                 time_out.tv_sec = seconds;
2014                 time_out.tv_usec = 0;
2015                 time_addr = &time_out;
2016             }
2017         }
2018 
2019         if (!block || tty_got_winch ())
2020         {
2021             time_addr = &time_out;
2022             time_out.tv_sec = 0;
2023             time_out.tv_usec = 0;
2024         }
2025 
2026         tty_enable_interrupt_key ();
2027         flag = select (nfd, &select_set, NULL, NULL, time_addr);
2028         tty_disable_interrupt_key ();
2029 
2030         /* select timed out: it could be for any of the following reasons:
2031          * redo_event -> it was because of the MOU_REPEAT handler
2032          * !block     -> we did not block in the select call
2033          * else       -> 10 second timeout to check the vfs status.
2034          */
2035         if (flag == 0)
2036         {
2037             if (redo_event)
2038                 return EV_MOUSE;
2039             if (!block || tty_got_winch ())
2040                 return EV_NONE;
2041             vfs_timeout_handler ();
2042         }
2043         if (flag == -1 && errno == EINTR)
2044             return EV_NONE;
2045 
2046         check_selects (&select_set);
2047 
2048         if (FD_ISSET (input_fd, &select_set))
2049             break;
2050 
2051 #ifdef HAVE_LIBGPM
2052         if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2053         {
2054             if (gpm_fd >= 0)
2055             {
2056                 if (FD_ISSET (gpm_fd, &select_set))
2057                 {
2058                     int status;
2059 
2060                     status = Gpm_GetEvent (&ev);
2061                     if (status == 1)    /* success */
2062                     {
2063                         Gpm_FitEvent (&ev);
2064                         *event = ev;
2065                         return EV_MOUSE;
2066                     }
2067                     if (status <= 0)    /* connection closed; -1 == error */
2068                     {
2069                         if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2070                             FD_CLR (mouse_fd, &select_set);
2071 
2072                         disable_mouse ();
2073                         return EV_NONE;
2074                     }
2075                 }
2076             }
2077             else
2078             {
2079                 if (mouse_fd >= 0)      /* error indicative */
2080                 {
2081                     if (FD_ISSET (mouse_fd, &select_set))
2082                         FD_CLR (mouse_fd, &select_set);
2083                     mouse_fd = gpm_fd;
2084                 }
2085                 /* gpm_fd == -2 means under some X terminal */
2086                 if (gpm_fd == -1)
2087                 {
2088                     mouse_enabled = FALSE;
2089                     use_mouse_p = MOUSE_NONE;
2090                 }
2091                 break;
2092             }
2093         }
2094 #endif /* !HAVE_LIBGPM */
2095     }
2096 
2097 #ifndef HAVE_SLANG
2098     flag = is_wintouched (stdscr);
2099     untouchwin (stdscr);
2100 #endif /* !HAVE_SLANG */
2101     c = block ? getch_with_delay () : get_key_code (1);
2102 
2103 #ifndef HAVE_SLANG
2104     if (flag > 0)
2105         tty_touch_screen ();
2106 #endif /* !HAVE_SLANG */
2107 
2108     if (mouse_enabled && (c == MCKEY_MOUSE
2109 #ifdef KEY_MOUSE
2110                           || c == KEY_MOUSE
2111 #endif /* KEY_MOUSE */
2112                           || c == MCKEY_EXTENDED_MOUSE))
2113     {
2114         /* Mouse event. See tickets 2956 and 3954 for extended mode detection. */
2115         gboolean extended = c == MCKEY_EXTENDED_MOUSE;
2116 
2117 #ifdef KEY_MOUSE
2118         extended = extended || (c == KEY_MOUSE && xmouse_seq == NULL
2119                                 && xmouse_extended_seq != NULL);
2120 #endif /* KEY_MOUSE */
2121 
2122         xmouse_get_event (event, extended);
2123         c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2124     }
2125     else if (c == MCKEY_BRACKETED_PASTING_START)
2126     {
2127         bracketed_pasting_in_progress = TRUE;
2128         c = EV_NONE;
2129     }
2130     else if (c == MCKEY_BRACKETED_PASTING_END)
2131     {
2132         bracketed_pasting_in_progress = FALSE;
2133         c = EV_NONE;
2134     }
2135 
2136     return c;
2137 }
2138 
2139 /* --------------------------------------------------------------------------------------------- */
2140 /* Returns a key press, mouse events are discarded */
2141 
2142 int
2143 tty_getch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2144 {
2145     Gpm_Event ev;
2146     int key;
2147 
2148     ev.x = -1;
2149     while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
2150         ;
2151     return key;
2152 }
2153 
2154 /* --------------------------------------------------------------------------------------------- */
2155 
2156 char *
2157 learn_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2158 {
2159     /* LEARN_TIMEOUT in ms */
2160 #define LEARN_TIMEOUT 200
2161 
2162     fd_set Read_FD_Set;
2163     gint64 end_time;
2164     int c;
2165     char buffer[256];
2166     char *p = buffer;
2167 
2168     tty_keypad (FALSE);         /* disable intepreting keys by ncurses */
2169     c = tty_lowlevel_getch ();
2170     while (c == -1)
2171         c = tty_lowlevel_getch ();      /* Sanity check, should be unnecessary */
2172     learn_store_key (buffer, &p, c);
2173 
2174     end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC;
2175 
2176     tty_nodelay (TRUE);
2177     while (TRUE)
2178     {
2179         while ((c = tty_lowlevel_getch ()) == -1)
2180         {
2181             gint64 time_out;
2182             struct timeval tv;
2183 
2184             time_out = end_time - g_get_monotonic_time ();
2185             if (time_out <= 0)
2186                 break;
2187 
2188             tv.tv_sec = time_out / G_USEC_PER_SEC;
2189             tv.tv_usec = time_out % G_USEC_PER_SEC;
2190             FD_ZERO (&Read_FD_Set);
2191             FD_SET (input_fd, &Read_FD_Set);
2192             select (input_fd + 1, &Read_FD_Set, NULL, NULL, &tv);
2193         }
2194         if (c == -1)
2195             break;
2196         learn_store_key (buffer, &p, c);
2197     }
2198     tty_keypad (TRUE);
2199     tty_nodelay (FALSE);
2200     *p = '\0';
2201     return (buffer[0] != '\0' ? g_strdup (buffer) : NULL);
2202 #undef LEARN_TIMEOUT
2203 }
2204 
2205 /* --------------------------------------------------------------------------------------------- */
2206 /* xterm and linux console only: set keypad to numeric or application
2207    mode. Only in application keypad mode it's possible to distinguish
2208    the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2209 
2210 void
2211 numeric_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2212 {
2213     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2214     {
2215         fputs (ESC_STR ">", stdout);
2216         fflush (stdout);
2217     }
2218 }
2219 
2220 /* --------------------------------------------------------------------------------------------- */
2221 
2222 void
2223 application_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2224 {
2225     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2226     {
2227         fputs (ESC_STR "=", stdout);
2228         fflush (stdout);
2229     }
2230 }
2231 
2232 /* --------------------------------------------------------------------------------------------- */
2233 
2234 void
2235 enable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2236 {
2237     printf (ESC_STR "[?2004h");
2238     fflush (stdout);
2239 }
2240 
2241 /* --------------------------------------------------------------------------------------------- */
2242 
2243 void
2244 disable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2245 {
2246     printf (ESC_STR "[?2004l");
2247     fflush (stdout);
2248     bracketed_pasting_in_progress = FALSE;
2249 }
2250 
2251 /* --------------------------------------------------------------------------------------------- */

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