Manual pages: mcmcdiffmceditmcview

root/lib/tty/key.c

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

DEFINITIONS

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

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

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