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-2019
   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;
  93 int double_click_speed = 250;
  94 gboolean old_esc_mode = TRUE;
  95 /* timeout for old_esc_mode in usec */
  96 int old_esc_mode_timeout = 1000000;     /* 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 GET_TIME(tv)     (gettimeofday(&tv, (struct timezone *) NULL))
 224 #define DIF_TIME(t1, t2) ((t2.tv_sec  - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec)/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     /* rxvt keys with modifiers */
 386     {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},
 387     {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},
 388     {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},
 389     {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},
 390     {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION},
 391     {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION},
 392     {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION},
 393     {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION},
 394     {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION},
 395     {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION},
 396     {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION},
 397     {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION},
 398     {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION},
 399     {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION},
 400     {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION},
 401     {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION},
 402     {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION},
 403 
 404     /* konsole keys with modifiers */
 405     {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION},
 406     {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION},
 407 
 408     /* gnome-terminal */
 409     {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION},
 410     {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION},
 411     {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION},
 412     {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION},
 413     {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION},
 414     {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION},
 415     {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION},
 416     {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION},
 417     {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION},
 418     {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION},
 419     {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION},
 420     {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION},
 421 
 422     /* gnome-terminal - application mode */
 423     {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION},
 424     {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION},
 425     {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION},
 426     {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION},
 427     {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION},
 428     {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION},
 429     {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION},
 430     {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION},
 431 
 432     /* iTerm */
 433     {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION},
 434     {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION},
 435 
 436     /* putty */
 437     {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION},
 438     {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION},
 439 
 440     /* keypad keys */
 441     {KEY_IC, ESC_STR "Op", MCKEY_NOACTION},
 442     {KEY_DC, ESC_STR "On", MCKEY_NOACTION},
 443     {'/', ESC_STR "Oo", MCKEY_NOACTION},
 444     {'\n', ESC_STR "OM", MCKEY_NOACTION},
 445 
 446     {0, NULL, MCKEY_NOACTION},
 447 };
 448 
 449 /* qansi-m terminals have a much more key combinatios,
 450    which are undefined in termcap/terminfo */
 451 static key_define_t qansi_key_defines[] = {
 452     /* qansi-m terminal */
 453     {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION},     /* Ctrl-PgDown */
 454     {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION},     /* Ctrl-PgUp   */
 455     {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION},      /* Ctrl-Home   */
 456     {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION},       /* Ctrl-End    */
 457     {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION},        /* Ctrl-Insert */
 458     {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION},        /* Ctrl-Delete */
 459     {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION},      /* Ctrl-Left   */
 460     {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION},     /* Ctrl-Right  */
 461     {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION},      /* Ctrl-Down   */
 462     {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION},        /* Ctrl-Up     */
 463     {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION},    /* Ctrl-Gr-Plus */
 464     {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION},       /* Ctrl-Gr-Minus */
 465     {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION},  /* Ctrl-Tab    */
 466     {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab   */
 467     {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION},    /* Ctrl-F1    */
 468     {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION},    /* Ctrl-F2    */
 469     {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION},    /* Ctrl-F3    */
 470     {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION},    /* Ctrl-F4    */
 471     {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION},    /* Ctrl-F5    */
 472     {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION},    /* Ctrl-F6    */
 473     {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION},    /* Ctrl-F7    */
 474     {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION},    /* Ctrl-F8    */
 475     {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION},    /* Ctrl-F9    */
 476     {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION},  /* Ctrl-F10  */
 477     {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION},  /* Ctrl-F11  */
 478     {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION},  /* Ctrl-F12  */
 479     {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION},    /* Alt-F1    */
 480     {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION},    /* Alt-F2    */
 481     {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION},    /* Alt-F3    */
 482     {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION},    /* Alt-F4    */
 483     {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION},    /* Alt-F5    */
 484     {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION},    /* Alt-F6    */
 485     {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION},    /* Alt-F7    */
 486     {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION},    /* Alt-F8    */
 487     {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION},    /* Alt-F9    */
 488     {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION},   /* Alt-F10   */
 489     {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION},   /* Alt-F11   */
 490     {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION},   /* Alt-F12   */
 491     {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION},    /* Alt-a     */
 492     {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION},    /* Alt-b     */
 493     {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION},    /* Alt-c     */
 494     {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION},    /* Alt-d     */
 495     {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION},    /* Alt-e     */
 496     {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION},    /* Alt-f     */
 497     {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION},    /* Alt-g     */
 498     {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION},    /* Alt-h     */
 499     {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION},    /* Alt-i     */
 500     {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION},    /* Alt-j     */
 501     {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION},    /* Alt-k     */
 502     {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION},    /* Alt-l     */
 503     {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION},    /* Alt-m     */
 504     {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION},    /* Alt-n     */
 505     {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION},    /* Alt-o     */
 506     {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION},    /* Alt-p     */
 507     {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION},    /* Alt-q     */
 508     {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION},    /* Alt-r     */
 509     {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION},    /* Alt-s     */
 510     {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION},    /* Alt-t     */
 511     {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION},    /* Alt-u     */
 512     {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION},    /* Alt-v     */
 513     {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION},    /* Alt-w     */
 514     {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION},    /* Alt-x     */
 515     {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION},    /* Alt-y     */
 516     {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION},    /* Alt-z     */
 517     {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION},    /* Gr-Minus  */
 518     {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus   */
 519     {0, NULL, MCKEY_NOACTION},
 520 };
 521 
 522 /* This holds all the key definitions */
 523 static key_def *keys = NULL;
 524 
 525 static int input_fd;
 526 static int disabled_channels = 0;       /* Disable channels checking */
 527 
 528 static GSList *select_list = NULL;
 529 
 530 static int seq_buffer[SEQ_BUFFER_LEN];
 531 static int *seq_append = NULL;
 532 
 533 static int *pending_keys = NULL;
 534 
 535 #ifdef __QNXNTO__
 536 ph_dv_f ph_attach;
 537 ph_ov_f ph_input_group;
 538 ph_pqc_f ph_query_cursor;
 539 #endif
 540 
 541 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 542 static Display *x11_display;
 543 static Window x11_window;
 544 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 545 
 546 static KeySortType has_been_sorted = KEY_NOSORT;
 547 
 548 /* *INDENT-OFF* */
 549 static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1;
 550 /* *INDENT-ON* */
 551 
 552 static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1];
 553 
 554 /* --------------------------------------------------------------------------------------------- */
 555 /*** file scope functions ************************************************************************/
 556 /* --------------------------------------------------------------------------------------------- */
 557 
 558 static int
 559 select_cmp_by_fd_set (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 560 {
 561     const select_t *s = (const select_t *) a;
 562     const fd_set *f = (const fd_set *) b;
 563 
 564     return (FD_ISSET (s->fd, f) ? 0 : 1);
 565 }
 566 
 567 /* --------------------------------------------------------------------------------------------- */
 568 
 569 static int
 570 select_cmp_by_fd (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 571 {
 572     const select_t *s = (const select_t *) a;
 573     const int fd = GPOINTER_TO_INT (b);
 574 
 575     return (s->fd == fd ? 0 : 1);
 576 }
 577 
 578 /* --------------------------------------------------------------------------------------------- */
 579 
 580 static int
 581 add_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 582 {
 583     int top_fd = 0;
 584 
 585     if (disabled_channels == 0)
 586     {
 587         GSList *s;
 588 
 589         for (s = select_list; s != NULL; s = g_slist_next (s))
 590         {
 591             select_t *p = (select_t *) s->data;
 592 
 593             FD_SET (p->fd, select_set);
 594             if (p->fd > top_fd)
 595                 top_fd = p->fd;
 596         }
 597     }
 598 
 599     return top_fd;
 600 }
 601 
 602 /* --------------------------------------------------------------------------------------------- */
 603 
 604 static void
 605 check_selects (fd_set * select_set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 606 {
 607     while (disabled_channels == 0)
 608     {
 609         GSList *s;
 610         select_t *p;
 611 
 612         s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set);
 613         if (s == NULL)
 614             break;
 615 
 616         p = (select_t *) s->data;
 617         FD_CLR (p->fd, select_set);
 618         p->callback (p->fd, p->info);
 619     }
 620 }
 621 
 622 /* --------------------------------------------------------------------------------------------- */
 623 /* If set timeout is set, then we wait 0.1 seconds, else, we block */
 624 
 625 static void
 626 try_channels (gboolean set_timeout)
     /* [previous][next][first][last][top][bottom][index][help]  */
 627 {
 628     struct timeval time_out;
 629     static fd_set select_set;
 630 
 631     while (TRUE)
 632     {
 633         struct timeval *timeptr = NULL;
 634         int maxfdp, v;
 635 
 636         FD_ZERO (&select_set);
 637         FD_SET (input_fd, &select_set); /* Add stdin */
 638         maxfdp = MAX (add_selects (&select_set), input_fd);
 639 
 640         if (set_timeout)
 641         {
 642             time_out.tv_sec = 0;
 643             time_out.tv_usec = 100000;
 644             timeptr = &time_out;
 645         }
 646 
 647         v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr);
 648         if (v > 0)
 649         {
 650             check_selects (&select_set);
 651             if (FD_ISSET (input_fd, &select_set))
 652                 break;
 653         }
 654     }
 655 }
 656 
 657 /* --------------------------------------------------------------------------------------------- */
 658 
 659 static key_def *
 660 create_sequence (const char *seq, int code, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 661 {
 662     key_def *base, *p, *attach;
 663 
 664     for (base = attach = NULL; *seq != '\0'; seq++)
 665     {
 666         p = g_new (key_def, 1);
 667         if (base == NULL)
 668             base = p;
 669         if (attach != NULL)
 670             attach->child = p;
 671 
 672         p->ch = *seq;
 673         p->code = code;
 674         p->child = p->next = NULL;
 675         if (seq[1] == '\0')
 676             p->action = action;
 677         else
 678             p->action = MCKEY_NOACTION;
 679         attach = p;
 680     }
 681     return base;
 682 }
 683 
 684 /* --------------------------------------------------------------------------------------------- */
 685 
 686 static void
 687 define_sequences (const key_define_t * kd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 688 {
 689     int i;
 690 
 691     for (i = 0; kd[i].code != 0; i++)
 692         define_sequence (kd[i].code, kd[i].seq, kd[i].action);
 693 }
 694 
 695 /* --------------------------------------------------------------------------------------------- */
 696 
 697 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 698 static void
 699 init_key_x11 (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 700 {
 701     if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11)
 702     {
 703         x11_display = mc_XOpenDisplay (0);
 704 
 705         if (x11_display != NULL)
 706             x11_window = DefaultRootWindow (x11_display);
 707     }
 708 }
 709 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 710 
 711 /* --------------------------------------------------------------------------------------------- */
 712 /* Workaround for System V Curses vt100 bug */
 713 
 714 static int
 715 getch_with_delay (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 716 {
 717     int c;
 718 
 719     /* This routine could be used on systems without mouse support,
 720        so we need to do the select check :-( */
 721     while (TRUE)
 722     {
 723         if (pending_keys == NULL)
 724             try_channels (FALSE);
 725 
 726         /* Try to get a character */
 727         c = get_key_code (0);
 728         if (c != -1)
 729             break;
 730 
 731         /* Failed -> wait 0.1 secs and try again */
 732         try_channels (TRUE);
 733     }
 734 
 735     /* Success -> return the character */
 736     return c;
 737 }
 738 
 739 /* --------------------------------------------------------------------------------------------- */
 740 
 741 static void
 742 xmouse_get_event (Gpm_Event * ev, gboolean extended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 743 {
 744     static struct timeval tv1 = { 0, 0 };       /* Force first click as single */
 745     static struct timeval tv2;
 746     static int clicks = 0;
 747     static int last_btn = 0;
 748     int btn;
 749 
 750     /* Decode Xterm mouse information to a GPM style event */
 751 
 752     if (!extended)
 753     {
 754         /* Variable btn has following meaning: */
 755         /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */
 756         btn = tty_lowlevel_getch () - 32;
 757         /* Coordinates are 33-based */
 758         /* Transform them to 1-based */
 759         ev->x = tty_lowlevel_getch () - 32;
 760         ev->y = tty_lowlevel_getch () - 32;
 761     }
 762     else
 763     {
 764         /* SGR 1006 extension (e.g. "\e[<0;12;300M"):
 765            - Numbers are encoded in decimal to make it ASCII-safe
 766            and to overcome the limit of 223 columns/rows.
 767            - Mouse release is encoded by trailing 'm' rather than 'M'
 768            so that the released button can be reported.
 769            - Numbers are no longer offset by 32. */
 770         char c;
 771 
 772         btn = ev->x = ev->y = 0;
 773         ev->type = 0;           /* In case we return on an invalid sequence */
 774 
 775         while ((c = tty_lowlevel_getch ()) != ';')
 776         {
 777             if (c < '0' || c > '9')
 778                 return;
 779             btn = 10 * btn + (c - '0');
 780         }
 781         while ((c = tty_lowlevel_getch ()) != ';')
 782         {
 783             if (c < '0' || c > '9')
 784                 return;
 785             ev->x = 10 * ev->x + (c - '0');
 786         }
 787         while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm')
 788         {
 789             if (c < '0' || c > '9')
 790                 return;
 791             ev->y = 10 * ev->y + (c - '0');
 792         }
 793         /* Legacy mouse protocol doesn't tell which button was released,
 794            conveniently all of mc's widgets are written not to rely on this
 795            information. With the SGR extension the released button becomes
 796            known, but for the sake of simplicity we just ignore it. */
 797         if (c == 'm')
 798             btn = 3;
 799     }
 800 
 801     /* There seems to be no way of knowing which button was released */
 802     /* So we assume all the buttons were released */
 803 
 804     if (btn == 3)
 805     {
 806         if (last_btn != 0)
 807         {
 808             if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0)
 809             {
 810                 /* FIXME: DIRTY HACK */
 811                 /* don't generate GPM_UP after mouse wheel */
 812                 /* need for menu event handling */
 813                 ev->type = 0;
 814                 tv1.tv_sec = 0;
 815                 tv1.tv_usec = 0;
 816             }
 817             else
 818             {
 819                 ev->type = GPM_UP | (GPM_SINGLE << clicks);
 820                 GET_TIME (tv1);
 821             }
 822             ev->buttons = 0;
 823             last_btn = 0;
 824             clicks = 0;
 825         }
 826         else
 827         {
 828             /* Bogus event, maybe mouse wheel */
 829             ev->type = 0;
 830         }
 831     }
 832     else
 833     {
 834         if (btn >= 32 && btn <= 34)
 835         {
 836             btn -= 32;
 837             ev->type = GPM_DRAG;
 838         }
 839         else
 840             ev->type = GPM_DOWN;
 841 
 842         GET_TIME (tv2);
 843         if (tv1.tv_sec != 0 && DIF_TIME (tv1, tv2) < double_click_speed)
 844         {
 845             clicks++;
 846             clicks %= 3;
 847         }
 848         else
 849             clicks = 0;
 850 
 851         switch (btn)
 852         {
 853         case 0:
 854             ev->buttons = GPM_B_LEFT;
 855             break;
 856         case 1:
 857             ev->buttons = GPM_B_MIDDLE;
 858             break;
 859         case 2:
 860             ev->buttons = GPM_B_RIGHT;
 861             break;
 862         case 64:
 863             ev->buttons = GPM_B_UP;
 864             clicks = 0;
 865             break;
 866         case 65:
 867             ev->buttons = GPM_B_DOWN;
 868             clicks = 0;
 869             break;
 870         default:
 871             /* Nothing */
 872             ev->type = 0;
 873             ev->buttons = 0;
 874             break;
 875         }
 876         last_btn = ev->buttons;
 877     }
 878 }
 879 
 880 /* --------------------------------------------------------------------------------------------- */
 881 /**
 882  * Get modifier state (shift, alt, ctrl) for the last key pressed.
 883  * We are assuming that the state didn't change since the key press.
 884  * This is only correct if get_modifier() is called very fast after
 885  * the input was received, so that the user didn't release the
 886  * modifier keys yet.
 887  */
 888 
 889 static int
 890 get_modifier (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 891 {
 892     int result = 0;
 893 #ifdef __QNXNTO__
 894     static int in_photon = 0;
 895     static int ph_ig = 0;
 896 #endif /* __QNXNTO__ */
 897 
 898 #ifdef HAVE_TEXTMODE_X11_SUPPORT
 899     if (x11_window != 0)
 900     {
 901         Window root, child;
 902         int root_x, root_y;
 903         int win_x, win_y;
 904         unsigned int mask;
 905 
 906         mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x,
 907                           &root_y, &win_x, &win_y, &mask);
 908 
 909         if ((mask & ShiftMask) != 0)
 910             result |= KEY_M_SHIFT;
 911         if ((mask & ControlMask) != 0)
 912             result |= KEY_M_CTRL;
 913         return result;
 914     }
 915 #endif /* HAVE_TEXTMODE_X11_SUPPORT */
 916 
 917 #ifdef __QNXNTO__
 918     if (in_photon == 0)
 919     {
 920         /* First time here, let's load Photon library and attach to Photon */
 921         in_photon = -1;
 922 
 923         if (getenv ("PHOTON2_PATH") != NULL)
 924         {
 925             /* QNX 6.x has no support for RTLD_LAZY */
 926             void *ph_handle;
 927 
 928             ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW);
 929             if (ph_handle != NULL)
 930             {
 931                 ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach");
 932                 ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup");
 933                 ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor");
 934                 if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL)
 935                     && (*ph_attach) (0, 0) != NULL)
 936                 {
 937                     /* Attached */
 938                     ph_ig = (*ph_input_group) (0);
 939                     in_photon = 1;
 940                 }
 941             }
 942         }
 943     }
 944     /* We do not have Photon running. Assume we are in text console or xterm */
 945     if (in_photon == -1)
 946     {
 947         int mod_status;
 948         int shift_ext_status;
 949 
 950         if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) ==
 951             -1)
 952             return 0;
 953 
 954         shift_ext_status = mod_status & 0xffffff00UL;
 955         mod_status &= 0x7f;
 956         if ((mod_status & _LINESTATUS_CON_ALT) != 0)
 957             result |= KEY_M_ALT;
 958         if ((mod_status & _LINESTATUS_CON_CTRL) != 0)
 959             result |= KEY_M_CTRL;
 960         if ((mod_status & _LINESTATUS_CON_SHIFT) != 0 || (shift_ext_status & 0x00000800UL) != 0)
 961             result |= KEY_M_SHIFT;
 962     }
 963     else
 964     {
 965         PhCursorInfo_t cursor_info;
 966 
 967         (*ph_query_cursor) (ph_ig, &cursor_info);
 968         if ((cursor_info.key_mods & 0x04) != 0)
 969             result |= KEY_M_ALT;
 970         if ((cursor_info.key_mods & 0x02) != 0)
 971             result |= KEY_M_CTRL;
 972         if ((cursor_info.key_mods & 0x01) != 0)
 973             result |= KEY_M_SHIFT;
 974     }
 975 #endif /* __QNXNTO__ */
 976 
 977 #if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX)
 978     {
 979         unsigned char modifiers = 6;
 980 
 981         if (ioctl (0, TIOCLINUX, &modifiers) < 0)
 982             return 0;
 983 
 984         /* Translate Linux modifiers into mc modifiers */
 985         if ((modifiers & SHIFT_PRESSED) != 0)
 986             result |= KEY_M_SHIFT;
 987         if ((modifiers & (ALTL_PRESSED | ALTR_PRESSED)) != 0)
 988             result |= KEY_M_ALT;
 989         if ((modifiers & CONTROL_PRESSED) != 0)
 990             result |= KEY_M_CTRL;
 991     }
 992 #endif /* !__linux__ */
 993 
 994     return result;
 995 }
 996 
 997 /* --------------------------------------------------------------------------------------------- */
 998 
 999 static gboolean
1000 push_char (int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1001 {
1002     gboolean ret = FALSE;
1003 
1004     if (seq_append == NULL)
1005         seq_append = seq_buffer;
1006 
1007     if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2]))
1008     {
1009         *(seq_append++) = c;
1010         *seq_append = '\0';
1011         ret = TRUE;
1012     }
1013 
1014     return ret;
1015 }
1016 
1017 /* --------------------------------------------------------------------------------------------- */
1018 /* Apply corrections for the keycode generated in get_key_code() */
1019 
1020 static int
1021 correct_key_code (int code)
     /* [previous][next][first][last][top][bottom][index][help]  */
1022 {
1023     unsigned int c = code & ~KEY_M_MASK;        /* code without modifier */
1024     unsigned int mod = code & KEY_M_MASK;       /* modifier */
1025 #ifdef __QNXNTO__
1026     unsigned int qmod;          /* bunch of the QNX console
1027                                    modifiers needs unchanged */
1028 #endif /* __QNXNTO__ */
1029 
1030     /*
1031      * Add key modifiers directly from X11 or OS.
1032      * Ordinary characters only get modifiers from sequences.
1033      */
1034     if (c < 32 || c >= 256)
1035         mod |= get_modifier ();
1036 
1037     /* This is needed if the newline is reported as carriage return */
1038     if (c == '\r')
1039         c = '\n';
1040 
1041     /* This is reported to be useful on AIX */
1042     if (c == KEY_SCANCEL)
1043         c = '\t';
1044 
1045     /* Convert Back Tab to Shift+Tab */
1046     if (c == KEY_BTAB)
1047     {
1048         c = '\t';
1049         mod = KEY_M_SHIFT;
1050     }
1051 
1052     /* F0 is the same as F10 for out purposes */
1053     if (c == KEY_F (0))
1054         c = KEY_F (10);
1055 
1056     /*
1057      * We are not interested if Ctrl was pressed when entering control
1058      * characters, so assume that it was.  When checking for such keys,
1059      * XCTRL macro should be used.  In some cases, we are interested,
1060      * e.g. to distinguish Ctrl-Enter from Enter.
1061      */
1062     if (c == '\b')
1063     {
1064         /* Special case for backspase ('\b' < 32) */
1065         c = KEY_BACKSPACE;
1066         mod &= ~KEY_M_CTRL;
1067     }
1068     else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n')
1069         mod |= KEY_M_CTRL;
1070 
1071 #ifdef __QNXNTO__
1072     qmod = get_modifier ();
1073 
1074     if (c == 127 && mod == 0)
1075     {
1076         /* Add Ctrl/Alt/Shift-BackSpace */
1077         mod |= get_modifier ();
1078         c = KEY_BACKSPACE;
1079     }
1080 
1081     if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1082     {
1083         /* Add Shift-Insert on key pad */
1084         mod = KEY_M_SHIFT;
1085         c = KEY_IC;
1086     }
1087 
1088     if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT)
1089     {
1090         /* Add Shift-Del on key pad */
1091         mod = KEY_M_SHIFT;
1092         c = KEY_DC;
1093     }
1094 #endif /* __QNXNTO__ */
1095 
1096     /* Unrecognized 0177 is delete (preserve Ctrl) */
1097     if (c == 0177)
1098         c = KEY_BACKSPACE;
1099 
1100 #if 0
1101     /* Unrecognized Ctrl-d is delete */
1102     if (c == 'd' & 31)
1103     {
1104         c = KEY_DC;
1105         mod &= ~KEY_M_CTRL;
1106     }
1107 
1108     /* Unrecognized Ctrl-h is backspace */
1109     if (c == 'h' & 31)
1110     {
1111         c = KEY_BACKSPACE;
1112         mod &= ~KEY_M_CTRL;
1113     }
1114 #endif
1115 
1116     /* Shift+BackSpace is backspace */
1117     if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0)
1118         mod &= ~KEY_M_SHIFT;
1119 
1120     /* Convert Shift+Fn to F(n+10) */
1121     if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT) != 0)
1122         c += 10;
1123 
1124     /* Remove Shift information from function keys */
1125     if (c >= KEY_F (1) && c <= KEY_F (20))
1126         mod &= ~KEY_M_SHIFT;
1127 
1128     if (!mc_global.tty.alternate_plus_minus)
1129         switch (c)
1130         {
1131         case KEY_KP_ADD:
1132             c = '+';
1133             break;
1134         case KEY_KP_SUBTRACT:
1135             c = '-';
1136             break;
1137         case KEY_KP_MULTIPLY:
1138             c = '*';
1139             break;
1140         default:
1141             break;
1142         }
1143 
1144     return (mod | c);
1145 }
1146 
1147 /* --------------------------------------------------------------------------------------------- */
1148 
1149 static int
1150 getch_with_timeout (unsigned int delay_us)
     /* [previous][next][first][last][top][bottom][index][help]  */
1151 {
1152     fd_set Read_FD_Set;
1153     int c;
1154     struct timeval time_out;
1155 
1156     time_out.tv_sec = delay_us / 1000000u;
1157     time_out.tv_usec = delay_us % 1000000u;
1158     tty_nodelay (TRUE);
1159     FD_ZERO (&Read_FD_Set);
1160     FD_SET (input_fd, &Read_FD_Set);
1161     select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
1162     c = tty_lowlevel_getch ();
1163     tty_nodelay (FALSE);
1164     return c;
1165 }
1166 
1167 /* --------------------------------------------------------------------------------------------- */
1168 
1169 static void
1170 learn_store_key (char *buffer, char **p, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
1171 {
1172     if (*p - buffer > 253)
1173         return;
1174 
1175     if (c == ESC_CHAR)
1176     {
1177         *(*p)++ = '\\';
1178         *(*p)++ = 'e';
1179     }
1180     else if (c < ' ')
1181     {
1182         *(*p)++ = '^';
1183         *(*p)++ = c + 'a' - 1;
1184     }
1185     else if (c == '^')
1186     {
1187         *(*p)++ = '^';
1188         *(*p)++ = '^';
1189     }
1190     else
1191         *(*p)++ = (char) c;
1192 }
1193 
1194 /* --------------------------------------------------------------------------------------------- */
1195 
1196 static void
1197 k_dispose (key_def * k)
     /* [previous][next][first][last][top][bottom][index][help]  */
1198 {
1199     if (k != NULL)
1200     {
1201         k_dispose (k->child);
1202         k_dispose (k->next);
1203         g_free (k);
1204     }
1205 }
1206 
1207 /* --------------------------------------------------------------------------------------------- */
1208 
1209 static int
1210 key_code_comparator_by_name (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1211 {
1212     const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1213     const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1214 
1215     return g_ascii_strcasecmp (n1->name, n2->name);
1216 }
1217 
1218 /* --------------------------------------------------------------------------------------------- */
1219 
1220 static int
1221 key_code_comparator_by_code (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1222 {
1223     const key_code_name_t *n1 = *(const key_code_name_t * const *) p1;
1224     const key_code_name_t *n2 = *(const key_code_name_t * const *) p2;
1225 
1226     return n1->code - n2->code;
1227 }
1228 
1229 /* --------------------------------------------------------------------------------------------- */
1230 
1231 static inline void
1232 sort_key_conv_tab (enum KeySortType type_sort)
     /* [previous][next][first][last][top][bottom][index][help]  */
1233 {
1234     if (has_been_sorted != type_sort)
1235     {
1236         size_t i;
1237 
1238         for (i = 0; i < key_conv_tab_size; i++)
1239             key_conv_tab_sorted[i] = &key_name_conv_tab[i];
1240 
1241         if (type_sort == KEY_SORTBYNAME)
1242             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1243                    &key_code_comparator_by_name);
1244         else if (type_sort == KEY_SORTBYCODE)
1245             qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]),
1246                    &key_code_comparator_by_code);
1247 
1248         has_been_sorted = type_sort;
1249     }
1250 }
1251 
1252 /* --------------------------------------------------------------------------------------------- */
1253 
1254 static int
1255 lookup_keyname (const char *name, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1256 {
1257     if (name[0] != '\0')
1258     {
1259         const key_code_name_t key = { 0, name, NULL, NULL };
1260         const key_code_name_t *keyp = &key;
1261         const key_code_name_t **res;
1262 
1263         if (name[1] == '\0')
1264         {
1265             *idx = -1;
1266             return (int) name[0];
1267         }
1268 
1269         sort_key_conv_tab (KEY_SORTBYNAME);
1270 
1271         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1272                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name);
1273 
1274         if (res != NULL)
1275         {
1276             *idx = (int) (res - key_conv_tab_sorted);
1277             return (*res)->code;
1278         }
1279     }
1280 
1281     *idx = -1;
1282     return 0;
1283 }
1284 
1285 /* --------------------------------------------------------------------------------------------- */
1286 
1287 static gboolean
1288 lookup_keycode (const long code, int *idx)
     /* [previous][next][first][last][top][bottom][index][help]  */
1289 {
1290     if (code != 0)
1291     {
1292         const key_code_name_t key = { code, NULL, NULL, NULL };
1293         const key_code_name_t *keyp = &key;
1294         const key_code_name_t **res;
1295 
1296         sort_key_conv_tab (KEY_SORTBYCODE);
1297 
1298         res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size,
1299                        sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code);
1300 
1301         if (res != NULL)
1302         {
1303             *idx = (int) (res - key_conv_tab_sorted);
1304             return TRUE;
1305         }
1306     }
1307 
1308     *idx = -1;
1309     return FALSE;
1310 }
1311 
1312 /* --------------------------------------------------------------------------------------------- */
1313 /*** public functions ****************************************************************************/
1314 /* --------------------------------------------------------------------------------------------- */
1315 /* This has to be called before init_slang or whatever routine
1316    calls any define_sequence */
1317 
1318 void
1319 init_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1320 {
1321     const char *term;
1322 
1323     term = getenv ("TERM");
1324 
1325     /* This has to be the first define_sequence */
1326     /* So, we can assume that the first keys member has ESC */
1327     define_sequences (mc_default_keys);
1328 
1329     /* Terminfo on irix does not have some keys */
1330     if (mc_global.tty.xterm_flag
1331         || (term != NULL
1332             && (strncmp (term, "iris-ansi", 9) == 0
1333                 || strncmp (term, "xterm", 5) == 0
1334                 || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0)))
1335         define_sequences (xterm_key_defines);
1336 
1337     /* load some additional keys (e.g. direct Alt-? support) */
1338     load_xtra_key_defines ();
1339 
1340 #ifdef __QNX__
1341     if ((term != NULL) && (strncmp (term, "qnx", 3) == 0))
1342     {
1343         /* Modify the default value of use_8th_bit_as_meta: we would
1344          * like to provide a working mc for a newbie who knows nothing
1345          * about [Options|Display bits|Full 8 bits input]...
1346          *
1347          * Don't use 'meta'-bit, when we are dealing with a
1348          * 'qnx*'-type terminal: clear the default value!
1349          * These terminal types use 0xFF as an escape character,
1350          * so use_8th_bit_as_meta==1 must not be enabled!
1351          *
1352          * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff
1353          * is not used now (doesn't even depend on use_8th_bit_as_meta
1354          * as in mc-3.1.2)...GREAT!...no additional code is required!]
1355          */
1356         use_8th_bit_as_meta = FALSE;
1357     }
1358 #endif /* __QNX__ */
1359 
1360 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1361     init_key_x11 ();
1362 #endif
1363 
1364     /* Load the qansi-m key definitions
1365        if we are running under the qansi-m terminal */
1366     if (term != NULL && (strncmp (term, "qansi-m", 7) == 0))
1367         define_sequences (qansi_key_defines);
1368 }
1369 
1370 /* --------------------------------------------------------------------------------------------- */
1371 /**
1372  * This has to be called after SLang_init_tty/slint_init
1373  */
1374 
1375 void
1376 init_key_input_fd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1377 {
1378 #ifdef HAVE_SLANG
1379     input_fd = SLang_TT_Read_FD;
1380 #endif
1381 }
1382 
1383 /* --------------------------------------------------------------------------------------------- */
1384 
1385 void
1386 done_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1387 {
1388     k_dispose (keys);
1389     g_slist_free_full (select_list, g_free);
1390 
1391 #ifdef HAVE_TEXTMODE_X11_SUPPORT
1392     if (x11_display)
1393         mc_XCloseDisplay (x11_display);
1394 #endif
1395 }
1396 
1397 /* --------------------------------------------------------------------------------------------- */
1398 
1399 void
1400 add_select_channel (int fd, select_fn callback, void *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
1401 {
1402     select_t *new;
1403 
1404     new = g_new (select_t, 1);
1405     new->fd = fd;
1406     new->callback = callback;
1407     new->info = info;
1408 
1409     select_list = g_slist_prepend (select_list, new);
1410 }
1411 
1412 /* --------------------------------------------------------------------------------------------- */
1413 
1414 void
1415 delete_select_channel (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
1416 {
1417     GSList *p;
1418 
1419     p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd);
1420     if (p != NULL)
1421         select_list = g_slist_delete_link (select_list, p);
1422 }
1423 
1424 /* --------------------------------------------------------------------------------------------- */
1425 
1426 void
1427 channels_up (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1428 {
1429     if (disabled_channels == 0)
1430         fputs ("Error: channels_up called with disabled_channels = 0\n", stderr);
1431     disabled_channels--;
1432 }
1433 
1434 /* --------------------------------------------------------------------------------------------- */
1435 
1436 void
1437 channels_down (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1438 {
1439     disabled_channels++;
1440 }
1441 
1442 /* --------------------------------------------------------------------------------------------- */
1443 /**
1444  * Return the code associated with the symbolic name keyname
1445  */
1446 
1447 long
1448 lookup_key (const char *name, char **label)
     /* [previous][next][first][last][top][bottom][index][help]  */
1449 {
1450     char **lc_keys, **p;
1451     char *cname;
1452     int k = -1;
1453     int key = 0;
1454     int lc_index = -1;
1455 
1456     int use_meta = -1;
1457     int use_ctrl = -1;
1458     int use_shift = -1;
1459 
1460     if (name == NULL)
1461         return 0;
1462 
1463     cname = g_strstrip (g_strdup (name));
1464     lc_keys = g_strsplit_set (cname, "-+ ", -1);
1465     g_free (cname);
1466 
1467     for (p = lc_keys; p != NULL && *p != NULL; p++)
1468     {
1469         if ((*p)[0] != '\0')
1470         {
1471             int idx;
1472 
1473             key = lookup_keyname (g_strstrip (*p), &idx);
1474 
1475             if (key == KEY_M_ALT)
1476                 use_meta = idx;
1477             else if (key == KEY_M_CTRL)
1478                 use_ctrl = idx;
1479             else if (key == KEY_M_SHIFT)
1480                 use_shift = idx;
1481             else
1482             {
1483                 k = key;
1484                 lc_index = idx;
1485                 break;
1486             }
1487         }
1488     }
1489 
1490     g_strfreev (lc_keys);
1491 
1492     /* output */
1493     if (k <= 0)
1494         return 0;
1495 
1496     if (label != NULL)
1497     {
1498         GString *s;
1499 
1500         s = g_string_new ("");
1501 
1502         if (use_meta != -1)
1503         {
1504             g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut);
1505             g_string_append_c (s, '-');
1506         }
1507         if (use_ctrl != -1)
1508         {
1509             g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut);
1510             g_string_append_c (s, '-');
1511         }
1512         if (use_shift != -1)
1513         {
1514             if (k < 127)
1515                 g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1516             else
1517             {
1518                 g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut);
1519                 g_string_append_c (s, '-');
1520                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1521             }
1522         }
1523         else if (k < 128)
1524         {
1525             if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL))
1526                 g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k));
1527             else
1528                 g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1529         }
1530         else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL))
1531             g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut);
1532         else
1533             g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key));
1534 
1535         *label = g_string_free (s, FALSE);
1536     }
1537 
1538     if (use_shift != -1)
1539     {
1540         if (k < 127 && k > 31)
1541             k = g_ascii_toupper ((gchar) k);
1542         else
1543             k |= KEY_M_SHIFT;
1544     }
1545 
1546     if (use_ctrl != -1)
1547     {
1548         if (k < 256)
1549             k = XCTRL (k);
1550         else
1551             k |= KEY_M_CTRL;
1552     }
1553 
1554     if (use_meta != -1)
1555         k = ALT (k);
1556 
1557     return (long) k;
1558 }
1559 
1560 /* --------------------------------------------------------------------------------------------- */
1561 
1562 char *
1563 lookup_key_by_code (const int keycode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1564 {
1565     /* code without modifier */
1566     unsigned int k = keycode & ~KEY_M_MASK;
1567     /* modifier */
1568     unsigned int mod = keycode & KEY_M_MASK;
1569 
1570     int key_idx = -1;
1571 
1572     GString *s;
1573     int idx;
1574 
1575     s = g_string_sized_new (8);
1576 
1577     if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256))
1578     {
1579         if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx))
1580         {
1581             g_string_append (s, key_conv_tab_sorted[idx]->name);
1582             g_string_append_c (s, '-');
1583         }
1584 
1585         if ((mod & KEY_M_CTRL) != 0)
1586         {
1587             /* non printeble chars like a CTRL-[A..Z] */
1588             if (k < 32)
1589                 k += 64;
1590 
1591             if (lookup_keycode (KEY_M_CTRL, &idx))
1592             {
1593                 g_string_append (s, key_conv_tab_sorted[idx]->name);
1594                 g_string_append_c (s, '-');
1595             }
1596         }
1597 
1598         if ((mod & KEY_M_SHIFT) != 0)
1599         {
1600             if (lookup_keycode (KEY_M_ALT, &idx))
1601             {
1602                 if (k < 127)
1603                     g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k));
1604                 else
1605                 {
1606                     g_string_append (s, key_conv_tab_sorted[idx]->name);
1607                     g_string_append_c (s, '-');
1608                     g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1609                 }
1610             }
1611         }
1612         else if (k < 128)
1613         {
1614             if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL))
1615                 g_string_append_c (s, (gchar) k);
1616             else
1617                 g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1618         }
1619         else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL))
1620             g_string_append (s, key_conv_tab_sorted[key_idx]->name);
1621         else
1622             g_string_append_c (s, (gchar) keycode);
1623     }
1624 
1625     return g_string_free (s, s->len == 0);
1626 }
1627 
1628 /* --------------------------------------------------------------------------------------------- */
1629 /**
1630  * Return TRUE on success, FALSE on error.
1631  * An error happens if SEQ is a beginning of an existing longer sequence.
1632  */
1633 
1634 gboolean
1635 define_sequence (int code, const char *seq, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1636 {
1637     key_def *base;
1638 
1639     if (strlen (seq) > SEQ_BUFFER_LEN - 1)
1640         return FALSE;
1641 
1642     for (base = keys; (base != NULL) && (*seq != '\0');)
1643         if (*seq == base->ch)
1644         {
1645             if (base->child == NULL)
1646             {
1647                 if (*(seq + 1) != '\0')
1648                     base->child = create_sequence (seq + 1, code, action);
1649                 else
1650                 {
1651                     /* The sequence matches an existing one.  */
1652                     base->code = code;
1653                     base->action = action;
1654                 }
1655                 return TRUE;
1656             }
1657 
1658             base = base->child;
1659             seq++;
1660         }
1661         else
1662         {
1663             if (base->next != NULL)
1664                 base = base->next;
1665             else
1666             {
1667                 base->next = create_sequence (seq, code, action);
1668                 return TRUE;
1669             }
1670         }
1671 
1672     if (*seq == '\0')
1673     {
1674         /* Attempt to redefine a sequence with a shorter sequence.  */
1675         return FALSE;
1676     }
1677 
1678     keys = create_sequence (seq, code, action);
1679     return TRUE;
1680 }
1681 
1682 /* --------------------------------------------------------------------------------------------- */
1683 /**
1684  * Check if we are idle, i.e. there are no pending keyboard or mouse
1685  * events.  Return 1 is idle, 0 is there are pending events.
1686  */
1687 gboolean
1688 is_idle (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1689 {
1690     int nfd;
1691     fd_set select_set;
1692     struct timeval time_out;
1693 
1694     FD_ZERO (&select_set);
1695     FD_SET (input_fd, &select_set);
1696     nfd = MAX (0, input_fd) + 1;
1697     time_out.tv_sec = 0;
1698     time_out.tv_usec = 0;
1699 #ifdef HAVE_LIBGPM
1700     if (mouse_enabled && use_mouse_p == MOUSE_GPM)
1701     {
1702         if (gpm_fd >= 0)
1703         {
1704             FD_SET (gpm_fd, &select_set);
1705             nfd = MAX (nfd, gpm_fd + 1);
1706         }
1707         else
1708         {
1709             if (mouse_fd >= 0)  /* error indicative */
1710             {
1711                 if (FD_ISSET (mouse_fd, &select_set))
1712                     FD_CLR (mouse_fd, &select_set);
1713                 mouse_fd = gpm_fd;
1714             }
1715             /* gpm_fd == -2 means under some X terminal */
1716             if (gpm_fd == -1)
1717             {
1718                 mouse_enabled = FALSE;
1719                 use_mouse_p = MOUSE_NONE;
1720             }
1721         }
1722     }
1723 #endif
1724     return (select (nfd, &select_set, 0, 0, &time_out) <= 0);
1725 }
1726 
1727 /* --------------------------------------------------------------------------------------------- */
1728 
1729 int
1730 get_key_code (int no_delay)
     /* [previous][next][first][last][top][bottom][index][help]  */
1731 {
1732     int c;
1733     static key_def *this = NULL, *parent;
1734     static struct timeval esctime = { -1, -1 };
1735     static int lastnodelay = -1;
1736 
1737     if (no_delay != lastnodelay)
1738     {
1739         this = NULL;
1740         lastnodelay = no_delay;
1741     }
1742 
1743   pend_send:
1744     if (pending_keys != NULL)
1745     {
1746         int d;
1747         gboolean bad_seq;
1748 
1749         d = *pending_keys++;
1750         while (d == ESC_CHAR)
1751             d = ALT (*pending_keys++);
1752 
1753         bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != 0);
1754         if (*pending_keys == '\0' || bad_seq)
1755             pending_keys = seq_append = NULL;
1756 
1757         if (bad_seq)
1758         {
1759             /* This is an unknown ESC sequence.
1760              * To prevent interpreting its tail as a random garbage,
1761              * eat and discard all buffered and quickly following chars.
1762              * Small, but non-zero timeout is needed to reconnect
1763              * escape sequence split up by e.g. a serial line.
1764              */
1765             int paranoia = 20;
1766 
1767             while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0)
1768                 ;
1769             goto nodelay_try_again;
1770         }
1771 
1772         if (d > 127 && d < 256 && use_8th_bit_as_meta)
1773             d = ALT (d & 0x7f);
1774 
1775         this = NULL;
1776         return correct_key_code (d);
1777     }
1778 
1779   nodelay_try_again:
1780     if (no_delay != 0)
1781         tty_nodelay (TRUE);
1782 
1783     c = tty_lowlevel_getch ();
1784 #if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE)
1785     if (c == KEY_RESIZE)
1786         goto nodelay_try_again;
1787 #endif
1788 
1789     if (no_delay != 0)
1790     {
1791         tty_nodelay (FALSE);
1792         if (c == -1)
1793         {
1794             if (this != NULL && parent != NULL && parent->action == MCKEY_ESCAPE && old_esc_mode)
1795             {
1796                 struct timeval current, time_out;
1797 
1798                 if (esctime.tv_sec == -1)
1799                     return -1;
1800 
1801                 GET_TIME (current);
1802                 time_out.tv_sec = old_esc_mode_timeout / 1000000 + esctime.tv_sec;
1803                 time_out.tv_usec = old_esc_mode_timeout % 1000000 + esctime.tv_usec;
1804                 if (time_out.tv_usec > 1000000)
1805                 {
1806                     time_out.tv_usec -= 1000000;
1807                     time_out.tv_sec++;
1808                 }
1809                 if (current.tv_sec < time_out.tv_sec ||
1810                     (current.tv_sec == time_out.tv_sec && current.tv_usec < time_out.tv_usec))
1811                     return -1;
1812                 this = NULL;
1813                 pending_keys = seq_append = NULL;
1814                 return ESC_CHAR;
1815             }
1816 
1817             return -1;
1818         }
1819     }
1820     else if (c == -1)
1821     {
1822         /* Maybe we got an incomplete match.
1823            This we do only in delay mode, since otherwise
1824            tty_lowlevel_getch can return -1 at any time. */
1825         if (seq_append != NULL)
1826         {
1827             pending_keys = seq_buffer;
1828             goto pend_send;
1829         }
1830         this = NULL;
1831         return -1;
1832     }
1833 
1834     /* Search the key on the root */
1835     if (no_delay == 0 || this == NULL)
1836     {
1837         this = keys;
1838         parent = NULL;
1839 
1840         if (c > 127 && c < 256 && use_8th_bit_as_meta)
1841         {
1842             c &= 0x7f;
1843 
1844             /* The first sequence defined starts with esc */
1845             parent = keys;
1846             this = keys->child;
1847         }
1848     }
1849 
1850     while (this != NULL)
1851     {
1852         if (c == this->ch)
1853         {
1854             if (this->child == NULL)
1855             {
1856                 /* We got a complete match, return and reset search */
1857                 int code;
1858 
1859                 pending_keys = seq_append = NULL;
1860                 code = this->code;
1861                 this = NULL;
1862                 return correct_key_code (code);
1863             }
1864 
1865             /* No match yet, but it may be a prefix for a valid seq */
1866             if (!push_char (c))
1867             {
1868                 pending_keys = seq_buffer;
1869                 goto pend_send;
1870             }
1871 
1872             parent = this;
1873             this = this->child;
1874             if (parent->action == MCKEY_ESCAPE && old_esc_mode)
1875             {
1876                 if (no_delay != 0)
1877                 {
1878                     GET_TIME (esctime);
1879                     goto nodelay_try_again;
1880                 }
1881 
1882                 esctime.tv_sec = -1;
1883                 c = getch_with_timeout (old_esc_mode_timeout);
1884                 if (c == -1)
1885                 {
1886                     pending_keys = seq_append = NULL;
1887                     this = NULL;
1888                     return ESC_CHAR;
1889                 }
1890                 continue;
1891             }
1892 
1893             if (no_delay != 0)
1894                 goto nodelay_try_again;
1895             c = tty_lowlevel_getch ();
1896             continue;
1897         }
1898 
1899         /* c != this->ch. Try other keys with this prefix */
1900         if (this->next != NULL)
1901         {
1902             this = this->next;
1903             continue;
1904         }
1905 
1906         /* No match found. Is it one of our ESC <key> specials? */
1907         if ((parent != NULL) && (parent->action == MCKEY_ESCAPE))
1908         {
1909             /* Convert escape-digits to F-keys */
1910             if (g_ascii_isdigit (c))
1911                 c = KEY_F (c - '0');
1912             else if (c == ' ')
1913                 c = ESC_CHAR;
1914             else
1915                 c = ALT (c);
1916 
1917             pending_keys = seq_append = NULL;
1918             this = NULL;
1919             return correct_key_code (c);
1920         }
1921 
1922         /* Unknown sequence. Maybe a prefix of a longer one. Save it. */
1923         push_char (c);
1924         pending_keys = seq_buffer;
1925         goto pend_send;
1926     }                           /* while (this != NULL) */
1927 
1928     this = NULL;
1929     return correct_key_code (c);
1930 }
1931 
1932 /* --------------------------------------------------------------------------------------------- */
1933 /* Returns a character read from stdin with appropriate interpretation */
1934 /* Also takes care of generated mouse events */
1935 /* Returns EV_MOUSE if it is a mouse event */
1936 /* Returns EV_NONE  if non-blocking or interrupt set and nothing was done */
1937 
1938 int
1939 tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block)
     /* [previous][next][first][last][top][bottom][index][help]  */
1940 {
1941     int c;
1942     int flag = 0;               /* Return value from select */
1943 #ifdef HAVE_LIBGPM
1944     static struct Gpm_Event ev; /* Mouse event */
1945 #endif
1946     struct timeval time_out;
1947     struct timeval *time_addr = NULL;
1948     static int dirty = 3;
1949 
1950     if ((dirty == 3) || is_idle ())
1951     {
1952         mc_refresh ();
1953         dirty = 1;
1954     }
1955     else
1956         dirty++;
1957 
1958     vfs_timeout_handler ();
1959 
1960     /* Ok, we use (event->x < 0) to signal that the event does not contain
1961        a suitable position for the mouse, so we can't use show_mouse_pointer
1962        on it.
1963      */
1964     if (event->x > 0)
1965     {
1966         show_mouse_pointer (event->x, event->y);
1967         if (!redo_event)
1968             event->x = -1;
1969     }
1970 
1971     /* Repeat if using mouse */
1972     while (pending_keys == NULL)
1973     {
1974         int nfd;
1975         fd_set select_set;
1976 
1977         FD_ZERO (&select_set);
1978         FD_SET (input_fd, &select_set);
1979         nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1;
1980 
1981 #ifdef HAVE_LIBGPM
1982         if (mouse_enabled && (use_mouse_p == MOUSE_GPM))
1983         {
1984             if (gpm_fd >= 0)
1985             {
1986                 FD_SET (gpm_fd, &select_set);
1987                 nfd = MAX (nfd, gpm_fd + 1);
1988             }
1989             else
1990             {
1991                 if (mouse_fd >= 0)      /* error indicative */
1992                 {
1993                     if (FD_ISSET (mouse_fd, &select_set))
1994                         FD_CLR (mouse_fd, &select_set);
1995                     mouse_fd = gpm_fd;
1996                 }
1997                 /* gpm_fd == -2 means under some X terminal */
1998                 if (gpm_fd == -1)
1999                 {
2000                     mouse_enabled = FALSE;
2001                     use_mouse_p = MOUSE_NONE;
2002                 }
2003                 break;
2004             }
2005         }
2006 #endif
2007 
2008         if (redo_event)
2009         {
2010             time_out.tv_usec = mou_auto_repeat * 1000;
2011             time_out.tv_sec = 0;
2012 
2013             time_addr = &time_out;
2014         }
2015         else
2016         {
2017             int seconds;
2018 
2019             seconds = vfs_timeouts ();
2020             time_addr = NULL;
2021 
2022             if (seconds != 0)
2023             {
2024                 /* the timeout could be improved and actually be
2025                  * the number of seconds until the next vfs entry
2026                  * timeouts in the stamp list.
2027                  */
2028 
2029                 time_out.tv_sec = seconds;
2030                 time_out.tv_usec = 0;
2031                 time_addr = &time_out;
2032             }
2033         }
2034 
2035         if (!block || tty_got_winch ())
2036         {
2037             time_addr = &time_out;
2038             time_out.tv_sec = 0;
2039             time_out.tv_usec = 0;
2040         }
2041 
2042         tty_enable_interrupt_key ();
2043         flag = select (nfd, &select_set, NULL, NULL, time_addr);
2044         tty_disable_interrupt_key ();
2045 
2046         /* select timed out: it could be for any of the following reasons:
2047          * redo_event -> it was because of the MOU_REPEAT handler
2048          * !block     -> we did not block in the select call
2049          * else       -> 10 second timeout to check the vfs status.
2050          */
2051         if (flag == 0)
2052         {
2053             if (redo_event)
2054                 return EV_MOUSE;
2055             if (!block || tty_got_winch ())
2056                 return EV_NONE;
2057             vfs_timeout_handler ();
2058         }
2059         if (flag == -1 && errno == EINTR)
2060             return EV_NONE;
2061 
2062         check_selects (&select_set);
2063 
2064         if (FD_ISSET (input_fd, &select_set))
2065             break;
2066 
2067 #ifdef HAVE_LIBGPM
2068         if (mouse_enabled && use_mouse_p == MOUSE_GPM)
2069         {
2070             if (gpm_fd >= 0)
2071             {
2072                 if (FD_ISSET (gpm_fd, &select_set))
2073                 {
2074                     int status;
2075 
2076                     status = Gpm_GetEvent (&ev);
2077                     if (status == 1)    /* success */
2078                     {
2079                         Gpm_FitEvent (&ev);
2080                         *event = ev;
2081                         return EV_MOUSE;
2082                     }
2083                     if (status <= 0)    /* connection closed; -1 == error */
2084                     {
2085                         if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set))
2086                             FD_CLR (mouse_fd, &select_set);
2087 
2088                         disable_mouse ();
2089                         return EV_NONE;
2090                     }
2091                 }
2092             }
2093             else
2094             {
2095                 if (mouse_fd >= 0)      /* error indicative */
2096                 {
2097                     if (FD_ISSET (mouse_fd, &select_set))
2098                         FD_CLR (mouse_fd, &select_set);
2099                     mouse_fd = gpm_fd;
2100                 }
2101                 /* gpm_fd == -2 means under some X terminal */
2102                 if (gpm_fd == -1)
2103                 {
2104                     mouse_enabled = FALSE;
2105                     use_mouse_p = MOUSE_NONE;
2106                 }
2107                 break;
2108             }
2109         }
2110 #endif /* !HAVE_LIBGPM */
2111     }
2112 
2113 #ifndef HAVE_SLANG
2114     flag = is_wintouched (stdscr);
2115     untouchwin (stdscr);
2116 #endif /* !HAVE_SLANG */
2117     c = block ? getch_with_delay () : get_key_code (1);
2118 
2119 #ifndef HAVE_SLANG
2120     if (flag > 0)
2121         tty_touch_screen ();
2122 #endif /* !HAVE_SLANG */
2123 
2124     if (mouse_enabled && (c == MCKEY_MOUSE
2125 #ifdef KEY_MOUSE
2126                           || c == KEY_MOUSE
2127 #endif /* KEY_MOUSE */
2128                           || c == MCKEY_EXTENDED_MOUSE))
2129     {
2130         /* Mouse event */
2131         xmouse_get_event (event, c == MCKEY_EXTENDED_MOUSE);
2132         c = (event->type != 0) ? EV_MOUSE : EV_NONE;
2133     }
2134     else if (c == MCKEY_BRACKETED_PASTING_START)
2135     {
2136         bracketed_pasting_in_progress = TRUE;
2137         c = EV_NONE;
2138     }
2139     else if (c == MCKEY_BRACKETED_PASTING_END)
2140     {
2141         bracketed_pasting_in_progress = FALSE;
2142         c = EV_NONE;
2143     }
2144 
2145     return c;
2146 }
2147 
2148 /* --------------------------------------------------------------------------------------------- */
2149 /* Returns a key press, mouse events are discarded */
2150 
2151 int
2152 tty_getch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2153 {
2154     Gpm_Event ev;
2155     int key;
2156 
2157     ev.x = -1;
2158     while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE)
2159         ;
2160     return key;
2161 }
2162 
2163 /* --------------------------------------------------------------------------------------------- */
2164 
2165 char *
2166 learn_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2167 {
2168     /* LEARN_TIMEOUT in usec */
2169 #define LEARN_TIMEOUT 200000
2170 
2171     fd_set Read_FD_Set;
2172     struct timeval endtime;
2173     struct timeval time_out;
2174     int c;
2175     char buffer[256];
2176     char *p = buffer;
2177 
2178     tty_keypad (FALSE);         /* disable intepreting keys by ncurses */
2179     c = tty_lowlevel_getch ();
2180     while (c == -1)
2181         c = tty_lowlevel_getch ();      /* Sanity check, should be unnecessary */
2182     learn_store_key (buffer, &p, c);
2183 
2184     GET_TIME (endtime);
2185     endtime.tv_usec += LEARN_TIMEOUT;
2186     if (endtime.tv_usec > 1000000)
2187     {
2188         endtime.tv_usec -= 1000000;
2189         endtime.tv_sec++;
2190     }
2191 
2192     tty_nodelay (TRUE);
2193     while (TRUE)
2194     {
2195         while ((c = tty_lowlevel_getch ()) == -1)
2196         {
2197             GET_TIME (time_out);
2198             time_out.tv_usec = endtime.tv_usec - time_out.tv_usec;
2199             if (time_out.tv_usec < 0)
2200                 time_out.tv_sec++;
2201             time_out.tv_sec = endtime.tv_sec - time_out.tv_sec;
2202             if (time_out.tv_sec >= 0 && time_out.tv_usec > 0)
2203             {
2204                 FD_ZERO (&Read_FD_Set);
2205                 FD_SET (input_fd, &Read_FD_Set);
2206                 select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out);
2207             }
2208             else
2209                 break;
2210         }
2211         if (c == -1)
2212             break;
2213         learn_store_key (buffer, &p, c);
2214     }
2215     tty_keypad (TRUE);
2216     tty_nodelay (FALSE);
2217     *p = '\0';
2218     return g_strdup (buffer);
2219 #undef LEARN_TIMEOUT
2220 }
2221 
2222 /* --------------------------------------------------------------------------------------------- */
2223 /* xterm and linux console only: set keypad to numeric or application
2224    mode. Only in application keypad mode it's possible to distinguish
2225    the '+' key and the '+' on the keypad ('*' and '-' ditto) */
2226 
2227 void
2228 numeric_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2229 {
2230     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2231     {
2232         fputs (ESC_STR ">", stdout);
2233         fflush (stdout);
2234     }
2235 }
2236 
2237 /* --------------------------------------------------------------------------------------------- */
2238 
2239 void
2240 application_keypad_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2241 {
2242     if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag)
2243     {
2244         fputs (ESC_STR "=", stdout);
2245         fflush (stdout);
2246     }
2247 }
2248 
2249 /* --------------------------------------------------------------------------------------------- */
2250 
2251 void
2252 enable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2253 {
2254     printf (ESC_STR "[?2004h");
2255     fflush (stdout);
2256 }
2257 
2258 /* --------------------------------------------------------------------------------------------- */
2259 
2260 void
2261 disable_bracketed_paste (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2262 {
2263     printf (ESC_STR "[?2004l");
2264     fflush (stdout);
2265     bracketed_pasting_in_progress = FALSE;
2266 }
2267 
2268 /* --------------------------------------------------------------------------------------------- */

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