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

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