diff options
Diffstat (limited to '')
-rw-r--r-- | lib/tty/key.c | 2250 |
1 files changed, 2250 insertions, 0 deletions
diff --git a/lib/tty/key.c b/lib/tty/key.c new file mode 100644 index 0000000..dd893e9 --- /dev/null +++ b/lib/tty/key.c @@ -0,0 +1,2250 @@ +/* + Keyboard support routines. + + Copyright (C) 1994-2022 + Free Software Foundation, Inc. + + Written by: + Miguel de Icaza, 1994, 1995 + Janne Kukonlehto, 1994, 1995 + Jakub Jelinek, 1995 + Norbert Warmuth, 1997 + Denys Vlasenko <vda.linux@googlemail.com>, 2013 + Slava Zanko <slavazanko@gmail.com>, 2013 + Egmont Koblinger <egmont@gmail.com>, 2013 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file key.c + * \brief Source: keyboard support routines + */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "lib/global.h" + +#include "lib/vfs/vfs.h" + +#include "tty.h" +#include "tty-internal.h" /* mouse_enabled */ +#include "mouse.h" +#include "key.h" + +#include "lib/widget.h" /* mc_refresh() */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +#include "x11conn.h" +#endif + +#ifdef __linux__ +#if defined(__GLIBC__) && (__GLIBC__ < 2) +#include <linux/termios.h> /* TIOCLINUX */ +#else +#include <termios.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#endif /* __linux__ */ + +#ifdef __CYGWIN__ +#include <termios.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#endif /* __CYGWIN__ */ + +#ifdef __QNXNTO__ +#include <dlfcn.h> +#include <Ph.h> +#include <sys/dcmd_chr.h> +#endif /* __QNXNTO__ */ + +/*** global variables ****************************************************************************/ + +int mou_auto_repeat = 100; /* ms */ +int double_click_speed = 250; /* ms */ +gboolean old_esc_mode = TRUE; +/* timeout for old_esc_mode in usec */ +int old_esc_mode_timeout = G_USEC_PER_SEC; /* us, settable via env */ +gboolean use_8th_bit_as_meta = FALSE; + +gboolean bracketed_pasting_in_progress = FALSE; + +/* This table is a mapping between names and the constants we use + * We use this to allow users to define alternate definitions for + * certain keys that may be missing from the terminal database + */ +const key_code_name_t key_name_conv_tab[] = { + {ESC_CHAR, "escape", N_("Escape"), "Esc"}, + /* KEY_F(0) is not here, since we are mapping it to f10, so there is no reason + to define f0 as well. Also, it makes Learn keys a bunch of problems :( */ + {KEY_F (1), "f1", N_("Function key 1"), "F1"}, + {KEY_F (2), "f2", N_("Function key 2"), "F2"}, + {KEY_F (3), "f3", N_("Function key 3"), "F3"}, + {KEY_F (4), "f4", N_("Function key 4"), "F4"}, + {KEY_F (5), "f5", N_("Function key 5"), "F5"}, + {KEY_F (6), "f6", N_("Function key 6"), "F6"}, + {KEY_F (7), "f7", N_("Function key 7"), "F7"}, + {KEY_F (8), "f8", N_("Function key 8"), "F8"}, + {KEY_F (9), "f9", N_("Function key 9"), "F9"}, + {KEY_F (10), "f10", N_("Function key 10"), "F10"}, + {KEY_F (11), "f11", N_("Function key 11"), "F11"}, + {KEY_F (12), "f12", N_("Function key 12"), "F12"}, + {KEY_F (13), "f13", N_("Function key 13"), "F13"}, + {KEY_F (14), "f14", N_("Function key 14"), "F14"}, + {KEY_F (15), "f15", N_("Function key 15"), "F15"}, + {KEY_F (16), "f16", N_("Function key 16"), "F16"}, + {KEY_F (17), "f17", N_("Function key 17"), "F17"}, + {KEY_F (18), "f18", N_("Function key 18"), "F18"}, + {KEY_F (19), "f19", N_("Function key 19"), "F19"}, + {KEY_F (20), "f20", N_("Function key 20"), "F20"}, + {ALT ('\t'), "complete", N_("Completion/M-tab"), "Meta-Tab"}, + {KEY_BTAB, "backtab", N_("BackTab/S-tab"), "Shift-Tab"}, + {KEY_BACKSPACE, "backspace", N_("Backspace"), "Backspace"}, + {KEY_UP, "up", N_("Up arrow"), "Up"}, + {KEY_DOWN, "down", N_("Down arrow"), "Down"}, + {KEY_LEFT, "left", N_("Left arrow"), "Left"}, + {KEY_RIGHT, "right", N_("Right arrow"), "Right"}, + {KEY_IC, "insert", N_("Insert"), "Ins"}, + {KEY_DC, "delete", N_("Delete"), "Del"}, + {KEY_HOME, "home", N_("Home"), "Home"}, + {KEY_END, "end", N_("End key"), "End"}, + {KEY_PPAGE, "pgup", N_("Page Up"), "PgUp"}, + {KEY_NPAGE, "pgdn", N_("Page Down"), "PgDn"}, + {(int) '/', "kpslash", N_("/ on keypad"), "/"}, + {KEY_KP_MULTIPLY, "kpasterisk", N_("* on keypad"), "*"}, + {KEY_KP_SUBTRACT, "kpminus", N_("- on keypad"), "-"}, + {KEY_KP_ADD, "kpplus", N_("+ on keypad"), "+"}, + + /* From here on, these won't be shown in Learn keys (no space) */ + {KEY_LEFT, "kpleft", N_("Left arrow keypad"), "Left"}, + {KEY_RIGHT, "kpright", N_("Right arrow keypad"), "Right"}, + {KEY_UP, "kpup", N_("Up arrow keypad"), "Up"}, + {KEY_DOWN, "kpdown", N_("Down arrow keypad"), "Down"}, + {KEY_HOME, "kphome", N_("Home on keypad"), "Home"}, + {KEY_END, "kpend", N_("End on keypad"), "End"}, + {KEY_NPAGE, "kpnpage", N_("Page Down keypad"), "PgDn"}, + {KEY_PPAGE, "kpppage", N_("Page Up keypad"), "PgUp"}, + {KEY_IC, "kpinsert", N_("Insert on keypad"), "Ins"}, + {KEY_DC, "kpdelete", N_("Delete on keypad"), "Del"}, + {(int) '\n', "kpenter", N_("Enter on keypad"), "Enter"}, + {KEY_F (21), "f21", N_("Function key 21"), "F21"}, + {KEY_F (22), "f22", N_("Function key 22"), "F22"}, + {KEY_F (23), "f23", N_("Function key 23"), "F23"}, + {KEY_F (24), "f24", N_("Function key 24"), "F24"}, + {KEY_A1, "a1", N_("A1 key"), "A1"}, + {KEY_C1, "c1", N_("C1 key"), "C1"}, + + /* Alternative label */ + {ESC_CHAR, "esc", N_("Escape"), "Esc"}, + {KEY_BACKSPACE, "bs", N_("Backspace"), "Bakspace"}, + {KEY_IC, "ins", N_("Insert"), "Ins"}, + {KEY_DC, "del", N_("Delete"), "Del"}, + {(int) '*', "asterisk", N_("Asterisk"), "*"}, + {(int) '-', "minus", N_("Minus"), "-"}, + {(int) '+', "plus", N_("Plus"), "+"}, + {(int) '.', "dot", N_("Dot"), "."}, + {(int) '<', "lt", N_("Less than"), "<"}, + {(int) '>', "gt", N_("Great than"), ">"}, + {(int) '=', "equal", N_("Equal"), "="}, + {(int) ',', "comma", N_("Comma"), ","}, + {(int) '\'', "apostrophe", N_("Apostrophe"), "\'"}, + {(int) ':', "colon", N_("Colon"), ":"}, + {(int) ';', "semicolon", N_("Semicolon"), ";"}, + {(int) '!', "exclamation", N_("Exclamation mark"), "!"}, + {(int) '?', "question", N_("Question mark"), "?"}, + {(int) '&', "ampersand", N_("Ampersand"), "&"}, + {(int) '$', "dollar", N_("Dollar sign"), "$"}, + {(int) '"', "quota", N_("Quotation mark"), "\""}, + {(int) '%', "percent", N_("Percent sign"), "%"}, + {(int) '^', "caret", N_("Caret"), "^"}, + {(int) '~', "tilda", N_("Tilda"), "~"}, + {(int) '`', "prime", N_("Prime"), "`"}, + {(int) '_', "underline", N_("Underline"), "_"}, + {(int) '_', "understrike", N_("Understrike"), "_"}, + {(int) '|', "pipe", N_("Pipe"), "|"}, + {(int) '(', "lparenthesis", N_("Left parenthesis"), "("}, + {(int) ')', "rparenthesis", N_("Right parenthesis"), ")"}, + {(int) '[', "lbracket", N_("Left bracket"), "["}, + {(int) ']', "rbracket", N_("Right bracket"), "]"}, + {(int) '{', "lbrace", N_("Left brace"), "{"}, + {(int) '}', "rbrace", N_("Right brace"), "}"}, + {(int) '\n', "enter", N_("Enter"), "Enter"}, + {(int) '\t', "tab", N_("Tab key"), "Tab"}, + {(int) ' ', "space", N_("Space key"), "Space"}, + {(int) '/', "slash", N_("Slash key"), "/"}, + {(int) '\\', "backslash", N_("Backslash key"), "\\"}, + {(int) '#', "number", N_("Number sign #"), "#"}, + {(int) '#', "hash", N_("Number sign #"), "#"}, + /* TRANSLATORS: Please translate as in "at sign" (@). */ + {(int) '@', "at", N_("At sign"), "@"}, + + /* meta keys */ + {KEY_M_CTRL, "control", N_("Ctrl"), "C"}, + {KEY_M_CTRL, "ctrl", N_("Ctrl"), "C"}, + {KEY_M_ALT, "meta", N_("Alt"), "M"}, + {KEY_M_ALT, "alt", N_("Alt"), "M"}, + {KEY_M_ALT, "ralt", N_("Alt"), "M"}, + {KEY_M_SHIFT, "shift", N_("Shift"), "S"}, + + {0, NULL, NULL, NULL} +}; + +/*** file scope macro definitions ****************************************************************/ + +#define MC_USEC_PER_MSEC 1000 + +/* The maximum sequence length (32 + null terminator) */ +#define SEQ_BUFFER_LEN 33 + +/*** file scope type declarations ****************************************************************/ + +/* Linux console keyboard modifiers */ +typedef enum +{ + SHIFT_PRESSED = (1 << 0), + ALTR_PRESSED = (1 << 1), + CONTROL_PRESSED = (1 << 2), + ALTL_PRESSED = (1 << 3) +} mod_pressed_t; + +typedef struct key_def +{ + char ch; /* Holds the matching char code */ + int code; /* The code returned, valid if child == NULL */ + struct key_def *next; + struct key_def *child; /* sequence continuation */ + int action; /* optional action to be done. Now used only + to mark that we are just after the first + Escape */ +} key_def; + +typedef struct +{ + int code; + const char *seq; + int action; +} key_define_t; + +/* File descriptor monitoring add/remove routines */ +typedef struct +{ + int fd; + select_fn callback; + void *info; +} select_t; + +typedef enum KeySortType +{ + KEY_NOSORT = 0, + KEY_SORTBYNAME, + KEY_SORTBYCODE +} KeySortType; + +#ifdef __QNXNTO__ +typedef int (*ph_dv_f) (void *, void *); +typedef int (*ph_ov_f) (void *); +typedef int (*ph_pqc_f) (unsigned short, PhCursorInfo_t *); +#endif + +/*** file scope variables ************************************************************************/ + +static key_define_t mc_default_keys[] = { + {ESC_CHAR, ESC_STR, MCKEY_ESCAPE}, + {ESC_CHAR, ESC_STR ESC_STR, MCKEY_NOACTION}, + {MCKEY_BRACKETED_PASTING_START, ESC_STR "[200~", MCKEY_NOACTION}, + {MCKEY_BRACKETED_PASTING_END, ESC_STR "[201~", MCKEY_NOACTION}, + {0, NULL, MCKEY_NOACTION}, +}; + +/* Broken terminfo and termcap databases on xterminals */ +static key_define_t xterm_key_defines[] = { + {KEY_F (1), ESC_STR "OP", MCKEY_NOACTION}, + {KEY_F (2), ESC_STR "OQ", MCKEY_NOACTION}, + {KEY_F (3), ESC_STR "OR", MCKEY_NOACTION}, + {KEY_F (4), ESC_STR "OS", MCKEY_NOACTION}, + {KEY_F (1), ESC_STR "[11~", MCKEY_NOACTION}, + {KEY_F (2), ESC_STR "[12~", MCKEY_NOACTION}, + {KEY_F (3), ESC_STR "[13~", MCKEY_NOACTION}, + {KEY_F (4), ESC_STR "[14~", MCKEY_NOACTION}, + {KEY_F (5), ESC_STR "[15~", MCKEY_NOACTION}, + {KEY_F (6), ESC_STR "[17~", MCKEY_NOACTION}, + {KEY_F (7), ESC_STR "[18~", MCKEY_NOACTION}, + {KEY_F (8), ESC_STR "[19~", MCKEY_NOACTION}, + {KEY_F (9), ESC_STR "[20~", MCKEY_NOACTION}, + {KEY_F (10), ESC_STR "[21~", MCKEY_NOACTION}, + + /* old xterm Shift-arrows */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "O2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "O2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "O2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "O2D", MCKEY_NOACTION}, + + /* new xterm Shift-arrows */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[1;2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[1;2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[1;2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[1;2D", MCKEY_NOACTION}, + + /* more xterm keys with modifiers */ + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_IC, ESC_STR "[2;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DC, ESC_STR "[3;5~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_HOME, ESC_STR "[1;5H", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_END, ESC_STR "[1;5F", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_HOME, ESC_STR "[1;2H", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "[1;2F", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "[1;5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_IC, ESC_STR "[2;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DC, ESC_STR "[3;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[1;6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[1;6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[1;6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[1;6D", MCKEY_NOACTION}, + {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, + + /* putty */ + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[[1;6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[[1;6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[[1;6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[[1;6D", MCKEY_NOACTION}, + + /* putty alt-arrow keys */ + /* removed as source esc esc esc trouble */ + /* + { KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "OA", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "OB", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "OC", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "OD", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[5~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[6~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1~", MCKEY_NOACTION }, + { KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[4~", MCKEY_NOACTION }, + + { KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR ESC_STR "[1;2A", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR ESC_STR "[1;2B", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR ESC_STR "[1;2C", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR ESC_STR "[1;2D", MCKEY_NOACTION }, + + { KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR ESC_STR "[[5;5~", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR ESC_STR "[[6;5~", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR ESC_STR "[1;5H", MCKEY_NOACTION }, + { KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR ESC_STR "[1;5F", MCKEY_NOACTION }, + */ + /* xterm alt-arrow keys */ + {KEY_M_ALT | KEY_UP, ESC_STR "[1;3A", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_DOWN, ESC_STR "[1;3B", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;3C", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_LEFT, ESC_STR "[1;3D", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;3~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;3~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_HOME, ESC_STR "[1~", MCKEY_NOACTION}, + {KEY_M_ALT | KEY_END, ESC_STR "[4~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_UP, ESC_STR "[1;7A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;7B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;7C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;7D", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_PPAGE, ESC_STR "[5;7~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_NPAGE, ESC_STR "[6;7~", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_HOME, ESC_STR "OH", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_M_ALT | KEY_END, ESC_STR "OF", MCKEY_NOACTION}, + + {KEY_M_SHIFT | KEY_M_ALT | KEY_UP, ESC_STR "[1;4A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_DOWN, ESC_STR "[1;4B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_RIGHT, ESC_STR "[1;4C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_ALT | KEY_LEFT, ESC_STR "[1;4D", MCKEY_NOACTION}, + + /* rxvt keys with modifiers */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "Oa", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "Ob", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "Oc", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "Od", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[5^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[6^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_HOME, ESC_STR "[7^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_END, ESC_STR "[8^", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_HOME, ESC_STR "[7$", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "[8$", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_IC, ESC_STR "[2^", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DC, ESC_STR "[3^", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DC, ESC_STR "[3$", MCKEY_NOACTION}, + + /* konsole keys with modifiers */ + {KEY_M_SHIFT | KEY_HOME, ESC_STR "O2H", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_END, ESC_STR "O2F", MCKEY_NOACTION}, + + /* gnome-terminal */ + {KEY_M_SHIFT | KEY_UP, ESC_STR "[2A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_DOWN, ESC_STR "[2B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_RIGHT, ESC_STR "[2C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_LEFT, ESC_STR "[2D", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_UP, ESC_STR "[5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "[6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "[6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "[6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "[6D", MCKEY_NOACTION}, + + /* gnome-terminal - application mode */ + {KEY_M_CTRL | KEY_UP, ESC_STR "O5A", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_DOWN, ESC_STR "O5B", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "O5C", MCKEY_NOACTION}, + {KEY_M_CTRL | KEY_LEFT, ESC_STR "O5D", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_UP, ESC_STR "O6A", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_DOWN, ESC_STR "O6B", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_RIGHT, ESC_STR "O6C", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_M_CTRL | KEY_LEFT, ESC_STR "O6D", MCKEY_NOACTION}, + + /* iTerm */ + {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[5;2~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[6;2~", MCKEY_NOACTION}, + + /* putty */ + {KEY_M_SHIFT | KEY_PPAGE, ESC_STR "[[5;53~", MCKEY_NOACTION}, + {KEY_M_SHIFT | KEY_NPAGE, ESC_STR "[[6;53~", MCKEY_NOACTION}, + + /* keypad keys */ + {KEY_IC, ESC_STR "Op", MCKEY_NOACTION}, + {KEY_DC, ESC_STR "On", MCKEY_NOACTION}, + {'/', ESC_STR "Oo", MCKEY_NOACTION}, + {'\n', ESC_STR "OM", MCKEY_NOACTION}, + + {0, NULL, MCKEY_NOACTION}, +}; + +/* qansi-m terminals have a much more key combinatios, + which are undefined in termcap/terminfo */ +static key_define_t qansi_key_defines[] = { + /* qansi-m terminal */ + {KEY_M_CTRL | KEY_NPAGE, ESC_STR "[u", MCKEY_NOACTION}, /* Ctrl-PgDown */ + {KEY_M_CTRL | KEY_PPAGE, ESC_STR "[v", MCKEY_NOACTION}, /* Ctrl-PgUp */ + {KEY_M_CTRL | KEY_HOME, ESC_STR "[h", MCKEY_NOACTION}, /* Ctrl-Home */ + {KEY_M_CTRL | KEY_END, ESC_STR "[y", MCKEY_NOACTION}, /* Ctrl-End */ + {KEY_M_CTRL | KEY_IC, ESC_STR "[`", MCKEY_NOACTION}, /* Ctrl-Insert */ + {KEY_M_CTRL | KEY_DC, ESC_STR "[p", MCKEY_NOACTION}, /* Ctrl-Delete */ + {KEY_M_CTRL | KEY_LEFT, ESC_STR "[d", MCKEY_NOACTION}, /* Ctrl-Left */ + {KEY_M_CTRL | KEY_RIGHT, ESC_STR "[c", MCKEY_NOACTION}, /* Ctrl-Right */ + {KEY_M_CTRL | KEY_DOWN, ESC_STR "[b", MCKEY_NOACTION}, /* Ctrl-Down */ + {KEY_M_CTRL | KEY_UP, ESC_STR "[a", MCKEY_NOACTION}, /* Ctrl-Up */ + {KEY_M_CTRL | KEY_KP_ADD, ESC_STR "[s", MCKEY_NOACTION}, /* Ctrl-Gr-Plus */ + {KEY_M_CTRL | KEY_KP_SUBTRACT, ESC_STR "[t", MCKEY_NOACTION}, /* Ctrl-Gr-Minus */ + {KEY_M_CTRL | '\t', ESC_STR "[z", MCKEY_NOACTION}, /* Ctrl-Tab */ + {KEY_M_SHIFT | '\t', ESC_STR "[Z", MCKEY_NOACTION}, /* Shift-Tab */ + {KEY_M_CTRL | KEY_F (1), ESC_STR "[1~", MCKEY_NOACTION}, /* Ctrl-F1 */ + {KEY_M_CTRL | KEY_F (2), ESC_STR "[2~", MCKEY_NOACTION}, /* Ctrl-F2 */ + {KEY_M_CTRL | KEY_F (3), ESC_STR "[3~", MCKEY_NOACTION}, /* Ctrl-F3 */ + {KEY_M_CTRL | KEY_F (4), ESC_STR "[4~", MCKEY_NOACTION}, /* Ctrl-F4 */ + {KEY_M_CTRL | KEY_F (5), ESC_STR "[5~", MCKEY_NOACTION}, /* Ctrl-F5 */ + {KEY_M_CTRL | KEY_F (6), ESC_STR "[6~", MCKEY_NOACTION}, /* Ctrl-F6 */ + {KEY_M_CTRL | KEY_F (7), ESC_STR "[7~", MCKEY_NOACTION}, /* Ctrl-F7 */ + {KEY_M_CTRL | KEY_F (8), ESC_STR "[8~", MCKEY_NOACTION}, /* Ctrl-F8 */ + {KEY_M_CTRL | KEY_F (9), ESC_STR "[9~", MCKEY_NOACTION}, /* Ctrl-F9 */ + {KEY_M_CTRL | KEY_F (10), ESC_STR "[10~", MCKEY_NOACTION}, /* Ctrl-F10 */ + {KEY_M_CTRL | KEY_F (11), ESC_STR "[11~", MCKEY_NOACTION}, /* Ctrl-F11 */ + {KEY_M_CTRL | KEY_F (12), ESC_STR "[12~", MCKEY_NOACTION}, /* Ctrl-F12 */ + {KEY_M_ALT | KEY_F (1), ESC_STR "[17~", MCKEY_NOACTION}, /* Alt-F1 */ + {KEY_M_ALT | KEY_F (2), ESC_STR "[18~", MCKEY_NOACTION}, /* Alt-F2 */ + {KEY_M_ALT | KEY_F (3), ESC_STR "[19~", MCKEY_NOACTION}, /* Alt-F3 */ + {KEY_M_ALT | KEY_F (4), ESC_STR "[20~", MCKEY_NOACTION}, /* Alt-F4 */ + {KEY_M_ALT | KEY_F (5), ESC_STR "[21~", MCKEY_NOACTION}, /* Alt-F5 */ + {KEY_M_ALT | KEY_F (6), ESC_STR "[22~", MCKEY_NOACTION}, /* Alt-F6 */ + {KEY_M_ALT | KEY_F (7), ESC_STR "[23~", MCKEY_NOACTION}, /* Alt-F7 */ + {KEY_M_ALT | KEY_F (8), ESC_STR "[24~", MCKEY_NOACTION}, /* Alt-F8 */ + {KEY_M_ALT | KEY_F (9), ESC_STR "[25~", MCKEY_NOACTION}, /* Alt-F9 */ + {KEY_M_ALT | KEY_F (10), ESC_STR "[26~", MCKEY_NOACTION}, /* Alt-F10 */ + {KEY_M_ALT | KEY_F (11), ESC_STR "[27~", MCKEY_NOACTION}, /* Alt-F11 */ + {KEY_M_ALT | KEY_F (12), ESC_STR "[28~", MCKEY_NOACTION}, /* Alt-F12 */ + {KEY_M_ALT | 'a', ESC_STR "Na", MCKEY_NOACTION}, /* Alt-a */ + {KEY_M_ALT | 'b', ESC_STR "Nb", MCKEY_NOACTION}, /* Alt-b */ + {KEY_M_ALT | 'c', ESC_STR "Nc", MCKEY_NOACTION}, /* Alt-c */ + {KEY_M_ALT | 'd', ESC_STR "Nd", MCKEY_NOACTION}, /* Alt-d */ + {KEY_M_ALT | 'e', ESC_STR "Ne", MCKEY_NOACTION}, /* Alt-e */ + {KEY_M_ALT | 'f', ESC_STR "Nf", MCKEY_NOACTION}, /* Alt-f */ + {KEY_M_ALT | 'g', ESC_STR "Ng", MCKEY_NOACTION}, /* Alt-g */ + {KEY_M_ALT | 'h', ESC_STR "Nh", MCKEY_NOACTION}, /* Alt-h */ + {KEY_M_ALT | 'i', ESC_STR "Ni", MCKEY_NOACTION}, /* Alt-i */ + {KEY_M_ALT | 'j', ESC_STR "Nj", MCKEY_NOACTION}, /* Alt-j */ + {KEY_M_ALT | 'k', ESC_STR "Nk", MCKEY_NOACTION}, /* Alt-k */ + {KEY_M_ALT | 'l', ESC_STR "Nl", MCKEY_NOACTION}, /* Alt-l */ + {KEY_M_ALT | 'm', ESC_STR "Nm", MCKEY_NOACTION}, /* Alt-m */ + {KEY_M_ALT | 'n', ESC_STR "Nn", MCKEY_NOACTION}, /* Alt-n */ + {KEY_M_ALT | 'o', ESC_STR "No", MCKEY_NOACTION}, /* Alt-o */ + {KEY_M_ALT | 'p', ESC_STR "Np", MCKEY_NOACTION}, /* Alt-p */ + {KEY_M_ALT | 'q', ESC_STR "Nq", MCKEY_NOACTION}, /* Alt-q */ + {KEY_M_ALT | 'r', ESC_STR "Nr", MCKEY_NOACTION}, /* Alt-r */ + {KEY_M_ALT | 's', ESC_STR "Ns", MCKEY_NOACTION}, /* Alt-s */ + {KEY_M_ALT | 't', ESC_STR "Nt", MCKEY_NOACTION}, /* Alt-t */ + {KEY_M_ALT | 'u', ESC_STR "Nu", MCKEY_NOACTION}, /* Alt-u */ + {KEY_M_ALT | 'v', ESC_STR "Nv", MCKEY_NOACTION}, /* Alt-v */ + {KEY_M_ALT | 'w', ESC_STR "Nw", MCKEY_NOACTION}, /* Alt-w */ + {KEY_M_ALT | 'x', ESC_STR "Nx", MCKEY_NOACTION}, /* Alt-x */ + {KEY_M_ALT | 'y', ESC_STR "Ny", MCKEY_NOACTION}, /* Alt-y */ + {KEY_M_ALT | 'z', ESC_STR "Nz", MCKEY_NOACTION}, /* Alt-z */ + {KEY_KP_SUBTRACT, ESC_STR "[S", MCKEY_NOACTION}, /* Gr-Minus */ + {KEY_KP_ADD, ESC_STR "[T", MCKEY_NOACTION}, /* Gr-Plus */ + {0, NULL, MCKEY_NOACTION}, +}; + +/* This holds all the key definitions */ +static key_def *keys = NULL; + +static int input_fd; +static int disabled_channels = 0; /* Disable channels checking */ + +static GSList *select_list = NULL; + +static int seq_buffer[SEQ_BUFFER_LEN]; +static int *seq_append = NULL; + +static int *pending_keys = NULL; + +#ifdef __QNXNTO__ +ph_dv_f ph_attach; +ph_ov_f ph_input_group; +ph_pqc_f ph_query_cursor; +#endif + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +static Display *x11_display; +static Window x11_window; +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +static KeySortType has_been_sorted = KEY_NOSORT; + +/* *INDENT-OFF* */ +static const size_t key_conv_tab_size = G_N_ELEMENTS (key_name_conv_tab) - 1; +/* *INDENT-ON* */ + +static const key_code_name_t *key_conv_tab_sorted[G_N_ELEMENTS (key_name_conv_tab) - 1]; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +select_cmp_by_fd_set (gconstpointer a, gconstpointer b) +{ + const select_t *s = (const select_t *) a; + const fd_set *f = (const fd_set *) b; + + return (FD_ISSET (s->fd, f) ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +select_cmp_by_fd (gconstpointer a, gconstpointer b) +{ + const select_t *s = (const select_t *) a; + const int fd = GPOINTER_TO_INT (b); + + return (s->fd == fd ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +add_selects (fd_set * select_set) +{ + int top_fd = 0; + + if (disabled_channels == 0) + { + GSList *s; + + for (s = select_list; s != NULL; s = g_slist_next (s)) + { + select_t *p = (select_t *) s->data; + + FD_SET (p->fd, select_set); + if (p->fd > top_fd) + top_fd = p->fd; + } + } + + return top_fd; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +check_selects (fd_set * select_set) +{ + while (disabled_channels == 0) + { + GSList *s; + select_t *p; + + s = g_slist_find_custom (select_list, select_set, select_cmp_by_fd_set); + if (s == NULL) + break; + + p = (select_t *) s->data; + FD_CLR (p->fd, select_set); + p->callback (p->fd, p->info); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/* If set timeout is set, then we wait 0.1 seconds, else, we block */ + +static void +try_channels (gboolean set_timeout) +{ + struct timeval time_out; + static fd_set select_set; + + while (TRUE) + { + struct timeval *timeptr = NULL; + int maxfdp, v; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); /* Add stdin */ + maxfdp = MAX (add_selects (&select_set), input_fd); + + if (set_timeout) + { + time_out.tv_sec = 0; + time_out.tv_usec = 100 * MC_USEC_PER_MSEC; + timeptr = &time_out; + } + + v = select (maxfdp + 1, &select_set, NULL, NULL, timeptr); + if (v > 0) + { + check_selects (&select_set); + if (FD_ISSET (input_fd, &select_set)) + break; + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static key_def * +create_sequence (const char *seq, int code, int action) +{ + key_def *base, *p, *attach; + + for (base = attach = NULL; *seq != '\0'; seq++) + { + p = g_new (key_def, 1); + if (base == NULL) + base = p; + if (attach != NULL) + attach->child = p; + + p->ch = *seq; + p->code = code; + p->child = p->next = NULL; + if (seq[1] == '\0') + p->action = action; + else + p->action = MCKEY_NOACTION; + attach = p; + } + return base; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +define_sequences (const key_define_t * kd) +{ + int i; + + for (i = 0; kd[i].code != 0; i++) + define_sequence (kd[i].code, kd[i].seq, kd[i].action); +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT +static void +init_key_x11 (void) +{ + if (getenv ("DISPLAY") != NULL && !mc_global.tty.disable_x11) + { + x11_display = mc_XOpenDisplay (0); + + if (x11_display != NULL) + x11_window = DefaultRootWindow (x11_display); + } +} +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +/* --------------------------------------------------------------------------------------------- */ +/* Workaround for System V Curses vt100 bug */ + +static int +getch_with_delay (void) +{ + int c; + + /* This routine could be used on systems without mouse support, + so we need to do the select check :-( */ + while (TRUE) + { + if (pending_keys == NULL) + try_channels (FALSE); + + /* Try to get a character */ + c = get_key_code (0); + if (c != -1) + break; + + /* Failed -> wait 0.1 secs and try again */ + try_channels (TRUE); + } + + /* Success -> return the character */ + return c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +xmouse_get_event (Gpm_Event * ev, gboolean extended) +{ + static gint64 tv1 = 0; /* Force first click as single */ + static int clicks = 0; + static int last_btn = 0; + int btn; + + /* Decode Xterm mouse information to a GPM style event */ + + if (!extended) + { + /* Variable btn has following meaning: */ + /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */ + btn = tty_lowlevel_getch () - 32; + /* Coordinates are 33-based */ + /* Transform them to 1-based */ + ev->x = tty_lowlevel_getch () - 32; + ev->y = tty_lowlevel_getch () - 32; + } + else + { + /* SGR 1006 extension (e.g. "\e[<0;12;300M"): + - Numbers are encoded in decimal to make it ASCII-safe + and to overcome the limit of 223 columns/rows. + - Mouse release is encoded by trailing 'm' rather than 'M' + so that the released button can be reported. + - Numbers are no longer offset by 32. */ + char c; + + btn = ev->x = ev->y = 0; + ev->type = 0; /* In case we return on an invalid sequence */ + + while ((c = tty_lowlevel_getch ()) != ';') + { + if (c < '0' || c > '9') + return; + btn = 10 * btn + (c - '0'); + } + while ((c = tty_lowlevel_getch ()) != ';') + { + if (c < '0' || c > '9') + return; + ev->x = 10 * ev->x + (c - '0'); + } + while ((c = tty_lowlevel_getch ()) != 'M' && c != 'm') + { + if (c < '0' || c > '9') + return; + ev->y = 10 * ev->y + (c - '0'); + } + /* Legacy mouse protocol doesn't tell which button was released, + conveniently all of mc's widgets are written not to rely on this + information. With the SGR extension the released button becomes + known, but for the sake of simplicity we just ignore it. */ + if (c == 'm') + btn = 3; + } + + /* There seems to be no way of knowing which button was released */ + /* So we assume all the buttons were released */ + + if (btn == 3) + { + if (last_btn != 0) + { + if ((last_btn & (GPM_B_UP | GPM_B_DOWN)) != 0) + { + /* FIXME: DIRTY HACK */ + /* don't generate GPM_UP after mouse wheel */ + /* need for menu event handling */ + ev->type = 0; + tv1 = 0; + } + else + { + ev->type = GPM_UP | (GPM_SINGLE << clicks); + tv1 = g_get_monotonic_time (); + } + ev->buttons = 0; + last_btn = 0; + clicks = 0; + } + else + { + /* Bogus event, maybe mouse wheel */ + ev->type = 0; + } + } + else + { + gint64 tv2; + + if (btn >= 32 && btn <= 34) + { + btn -= 32; + ev->type = GPM_DRAG; + } + else + ev->type = GPM_DOWN; + + tv2 = g_get_monotonic_time (); + if (tv1 != 0 && tv2 - tv1 < (gint64) double_click_speed * MC_USEC_PER_MSEC) + { + clicks++; + clicks %= 3; + } + else + clicks = 0; + + switch (btn) + { + case 0: + ev->buttons = GPM_B_LEFT; + break; + case 1: + ev->buttons = GPM_B_MIDDLE; + break; + case 2: + ev->buttons = GPM_B_RIGHT; + break; + case 64: + ev->buttons = GPM_B_UP; + clicks = 0; + break; + case 65: + ev->buttons = GPM_B_DOWN; + clicks = 0; + break; + default: + /* Nothing */ + ev->type = 0; + ev->buttons = 0; + break; + } + last_btn = ev->buttons; + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Get modifier state (shift, alt, ctrl) for the last key pressed. + * We are assuming that the state didn't change since the key press. + * This is only correct if get_modifier() is called very fast after + * the input was received, so that the user didn't release the + * modifier keys yet. + */ + +static int +get_modifier (void) +{ + int result = 0; +#ifdef __QNXNTO__ + static int in_photon = 0; + static int ph_ig = 0; +#endif /* __QNXNTO__ */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + if (x11_window != 0) + { + Window root, child; + int root_x, root_y; + int win_x, win_y; + unsigned int mask; + + mc_XQueryPointer (x11_display, x11_window, &root, &child, &root_x, + &root_y, &win_x, &win_y, &mask); + + if ((mask & ShiftMask) != 0) + result |= KEY_M_SHIFT; + if ((mask & ControlMask) != 0) + result |= KEY_M_CTRL; + return result; + } +#endif /* HAVE_TEXTMODE_X11_SUPPORT */ + +#ifdef __QNXNTO__ + if (in_photon == 0) + { + /* First time here, let's load Photon library and attach to Photon */ + in_photon = -1; + + if (getenv ("PHOTON2_PATH") != NULL) + { + /* QNX 6.x has no support for RTLD_LAZY */ + void *ph_handle; + + ph_handle = dlopen ("/usr/lib/libph.so", RTLD_NOW); + if (ph_handle != NULL) + { + ph_attach = (ph_dv_f) dlsym (ph_handle, "PhAttach"); + ph_input_group = (ph_ov_f) dlsym (ph_handle, "PhInputGroup"); + ph_query_cursor = (ph_pqc_f) dlsym (ph_handle, "PhQueryCursor"); + if ((ph_attach != NULL) && (ph_input_group != NULL) && (ph_query_cursor != NULL) + && (*ph_attach) (0, 0) != NULL) + { + /* Attached */ + ph_ig = (*ph_input_group) (0); + in_photon = 1; + } + } + } + } + /* We do not have Photon running. Assume we are in text console or xterm */ + if (in_photon == -1) + { + int mod_status; + int shift_ext_status; + + if (devctl (fileno (stdin), DCMD_CHR_LINESTATUS, &mod_status, sizeof (mod_status), NULL) == + -1) + return 0; + + shift_ext_status = mod_status & 0xffffff00UL; + mod_status &= 0x7f; + if ((mod_status & _LINESTATUS_CON_ALT) != 0) + result |= KEY_M_ALT; + if ((mod_status & _LINESTATUS_CON_CTRL) != 0) + result |= KEY_M_CTRL; + if ((mod_status & _LINESTATUS_CON_SHIFT) != 0 || (shift_ext_status & 0x00000800UL) != 0) + result |= KEY_M_SHIFT; + } + else + { + PhCursorInfo_t cursor_info; + + (*ph_query_cursor) (ph_ig, &cursor_info); + if ((cursor_info.key_mods & 0x04) != 0) + result |= KEY_M_ALT; + if ((cursor_info.key_mods & 0x02) != 0) + result |= KEY_M_CTRL; + if ((cursor_info.key_mods & 0x01) != 0) + result |= KEY_M_SHIFT; + } +#endif /* __QNXNTO__ */ + +#if defined __linux__ || (defined __CYGWIN__ && defined TIOCLINUX) + { + unsigned char modifiers = 6; + + if (ioctl (0, TIOCLINUX, &modifiers) < 0) + return 0; + + /* Translate Linux modifiers into mc modifiers */ + if ((modifiers & SHIFT_PRESSED) != 0) + result |= KEY_M_SHIFT; + if ((modifiers & (ALTL_PRESSED | ALTR_PRESSED)) != 0) + result |= KEY_M_ALT; + if ((modifiers & CONTROL_PRESSED) != 0) + result |= KEY_M_CTRL; + } +#endif /* !__linux__ */ + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +push_char (int c) +{ + gboolean ret = FALSE; + + if (seq_append == NULL) + seq_append = seq_buffer; + + if (seq_append != &(seq_buffer[SEQ_BUFFER_LEN - 2])) + { + *(seq_append++) = c; + *seq_append = '\0'; + ret = TRUE; + } + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Apply corrections for the keycode generated in get_key_code() */ + +static int +correct_key_code (int code) +{ + unsigned int c = code & ~KEY_M_MASK; /* code without modifier */ + unsigned int mod = code & KEY_M_MASK; /* modifier */ +#ifdef __QNXNTO__ + unsigned int qmod; /* bunch of the QNX console + modifiers needs unchanged */ +#endif /* __QNXNTO__ */ + + /* + * Add key modifiers directly from X11 or OS. + * Ordinary characters only get modifiers from sequences. + */ + if (c < 32 || c >= 256) + mod |= get_modifier (); + + /* This is needed if the newline is reported as carriage return */ + if (c == '\r') + c = '\n'; + + /* This is reported to be useful on AIX */ + if (c == KEY_SCANCEL) + c = '\t'; + + /* Convert Back Tab to Shift+Tab */ + if (c == KEY_BTAB) + { + c = '\t'; + mod = KEY_M_SHIFT; + } + + /* F0 is the same as F10 for out purposes */ + if (c == KEY_F (0)) + c = KEY_F (10); + + /* + * We are not interested if Ctrl was pressed when entering control + * characters, so assume that it was. When checking for such keys, + * XCTRL macro should be used. In some cases, we are interested, + * e.g. to distinguish Ctrl-Enter from Enter. + */ + if (c == '\b') + { + /* Special case for backspase ('\b' < 32) */ + c = KEY_BACKSPACE; + mod &= ~KEY_M_CTRL; + } + else if (c < 32 && c != ESC_CHAR && c != '\t' && c != '\n') + mod |= KEY_M_CTRL; + +#ifdef __QNXNTO__ + qmod = get_modifier (); + + if (c == 127 && mod == 0) + { + /* Add Ctrl/Alt/Shift-BackSpace */ + mod |= get_modifier (); + c = KEY_BACKSPACE; + } + + if (c == '0' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT) + { + /* Add Shift-Insert on key pad */ + mod = KEY_M_SHIFT; + c = KEY_IC; + } + + if (c == '.' && mod == 0 && (qmod & KEY_M_SHIFT) == KEY_M_SHIFT) + { + /* Add Shift-Del on key pad */ + mod = KEY_M_SHIFT; + c = KEY_DC; + } +#endif /* __QNXNTO__ */ + + /* Unrecognized 0177 is delete (preserve Ctrl) */ + if (c == 0177) + c = KEY_BACKSPACE; + +#if 0 + /* Unrecognized Ctrl-d is delete */ + if (c == 'd' & 31) + { + c = KEY_DC; + mod &= ~KEY_M_CTRL; + } + + /* Unrecognized Ctrl-h is backspace */ + if (c == 'h' & 31) + { + c = KEY_BACKSPACE; + mod &= ~KEY_M_CTRL; + } +#endif + + /* Shift+BackSpace is backspace */ + if (c == KEY_BACKSPACE && (mod & KEY_M_SHIFT) != 0) + mod &= ~KEY_M_SHIFT; + + /* Convert Shift+Fn to F(n+10) */ + if (c >= KEY_F (1) && c <= KEY_F (10) && (mod & KEY_M_SHIFT) != 0) + c += 10; + + /* Remove Shift information from function keys */ + if (c >= KEY_F (1) && c <= KEY_F (20)) + mod &= ~KEY_M_SHIFT; + + if (!mc_global.tty.alternate_plus_minus) + switch (c) + { + case KEY_KP_ADD: + c = '+'; + break; + case KEY_KP_SUBTRACT: + c = '-'; + break; + case KEY_KP_MULTIPLY: + c = '*'; + break; + default: + break; + } + + return (mod | c); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +getch_with_timeout (unsigned int delay_us) +{ + fd_set Read_FD_Set; + int c; + struct timeval time_out; + + time_out.tv_sec = delay_us / G_USEC_PER_SEC; + time_out.tv_usec = delay_us % G_USEC_PER_SEC; + tty_nodelay (TRUE); + FD_ZERO (&Read_FD_Set); + FD_SET (input_fd, &Read_FD_Set); + select (input_fd + 1, &Read_FD_Set, NULL, NULL, &time_out); + c = tty_lowlevel_getch (); + tty_nodelay (FALSE); + return c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +learn_store_key (char *buffer, char **p, int c) +{ + if (*p - buffer > 253) + return; + + if (c == ESC_CHAR) + { + *(*p)++ = '\\'; + *(*p)++ = 'e'; + } + else if (c < ' ') + { + *(*p)++ = '^'; + *(*p)++ = c + 'a' - 1; + } + else if (c == '^') + { + *(*p)++ = '^'; + *(*p)++ = '^'; + } + else + *(*p)++ = (char) c; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +k_dispose (key_def * k) +{ + if (k != NULL) + { + k_dispose (k->child); + k_dispose (k->next); + g_free (k); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +key_code_comparator_by_name (const void *p1, const void *p2) +{ + const key_code_name_t *n1 = *(const key_code_name_t * const *) p1; + const key_code_name_t *n2 = *(const key_code_name_t * const *) p2; + + return g_ascii_strcasecmp (n1->name, n2->name); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +key_code_comparator_by_code (const void *p1, const void *p2) +{ + const key_code_name_t *n1 = *(const key_code_name_t * const *) p1; + const key_code_name_t *n2 = *(const key_code_name_t * const *) p2; + + return n1->code - n2->code; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +sort_key_conv_tab (enum KeySortType type_sort) +{ + if (has_been_sorted != type_sort) + { + size_t i; + + for (i = 0; i < key_conv_tab_size; i++) + key_conv_tab_sorted[i] = &key_name_conv_tab[i]; + + if (type_sort == KEY_SORTBYNAME) + qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]), + &key_code_comparator_by_name); + else if (type_sort == KEY_SORTBYCODE) + qsort (key_conv_tab_sorted, key_conv_tab_size, sizeof (key_conv_tab_sorted[0]), + &key_code_comparator_by_code); + + has_been_sorted = type_sort; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +lookup_keyname (const char *name, int *idx) +{ + if (name[0] != '\0') + { + const key_code_name_t key = { 0, name, NULL, NULL }; + const key_code_name_t *keyp = &key; + const key_code_name_t **res; + + if (name[1] == '\0') + { + *idx = -1; + return (int) name[0]; + } + + sort_key_conv_tab (KEY_SORTBYNAME); + + res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size, + sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_name); + + if (res != NULL) + { + *idx = (int) (res - key_conv_tab_sorted); + return (*res)->code; + } + } + + *idx = -1; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +lookup_keycode (const long code, int *idx) +{ + if (code != 0) + { + const key_code_name_t key = { code, NULL, NULL, NULL }; + const key_code_name_t *keyp = &key; + const key_code_name_t **res; + + sort_key_conv_tab (KEY_SORTBYCODE); + + res = bsearch (&keyp, key_conv_tab_sorted, key_conv_tab_size, + sizeof (key_conv_tab_sorted[0]), key_code_comparator_by_code); + + if (res != NULL) + { + *idx = (int) (res - key_conv_tab_sorted); + return TRUE; + } + } + + *idx = -1; + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/* This has to be called before init_slang or whatever routine + calls any define_sequence */ + +void +init_key (void) +{ + const char *term; + + term = getenv ("TERM"); + + /* This has to be the first define_sequence */ + /* So, we can assume that the first keys member has ESC */ + define_sequences (mc_default_keys); + + /* Terminfo on irix does not have some keys */ + if (mc_global.tty.xterm_flag + || (term != NULL + && (strncmp (term, "iris-ansi", 9) == 0 + || strncmp (term, "xterm", 5) == 0 + || strncmp (term, "rxvt", 4) == 0 || strncmp (term, "screen", 6) == 0))) + define_sequences (xterm_key_defines); + + /* load some additional keys (e.g. direct Alt-? support) */ + load_xtra_key_defines (); + +#ifdef __QNX__ + if ((term != NULL) && (strncmp (term, "qnx", 3) == 0)) + { + /* Modify the default value of use_8th_bit_as_meta: we would + * like to provide a working mc for a newbie who knows nothing + * about [Options|Display bits|Full 8 bits input]... + * + * Don't use 'meta'-bit, when we are dealing with a + * 'qnx*'-type terminal: clear the default value! + * These terminal types use 0xFF as an escape character, + * so use_8th_bit_as_meta==1 must not be enabled! + * + * [mc-4.1.21+,slint.c/getch(): the DEC_8BIT_HACK stuff + * is not used now (doesn't even depend on use_8th_bit_as_meta + * as in mc-3.1.2)...GREAT!...no additional code is required!] + */ + use_8th_bit_as_meta = FALSE; + } +#endif /* __QNX__ */ + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + init_key_x11 (); +#endif + + /* Load the qansi-m key definitions + if we are running under the qansi-m terminal */ + if (term != NULL && (strncmp (term, "qansi-m", 7) == 0)) + define_sequences (qansi_key_defines); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * This has to be called after SLang_init_tty/slint_init + */ + +void +init_key_input_fd (void) +{ +#ifdef HAVE_SLANG + input_fd = SLang_TT_Read_FD; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +done_key (void) +{ + k_dispose (keys); + g_slist_free_full (select_list, g_free); + +#ifdef HAVE_TEXTMODE_X11_SUPPORT + if (x11_display) + mc_XCloseDisplay (x11_display); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +add_select_channel (int fd, select_fn callback, void *info) +{ + select_t *new; + + new = g_new (select_t, 1); + new->fd = fd; + new->callback = callback; + new->info = info; + + select_list = g_slist_prepend (select_list, new); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +delete_select_channel (int fd) +{ + GSList *p; + + p = g_slist_find_custom (select_list, GINT_TO_POINTER (fd), select_cmp_by_fd); + if (p != NULL) + select_list = g_slist_delete_link (select_list, p); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +channels_up (void) +{ + if (disabled_channels == 0) + fputs ("Error: channels_up called with disabled_channels = 0\n", stderr); + disabled_channels--; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +channels_down (void) +{ + disabled_channels++; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Return the code associated with the symbolic name keyname + */ + +long +lookup_key (const char *name, char **label) +{ + char **lc_keys, **p; + char *cname; + int k = -1; + int key = 0; + int lc_index = -1; + + int use_meta = -1; + int use_ctrl = -1; + int use_shift = -1; + + if (name == NULL) + return 0; + + cname = g_strstrip (g_strdup (name)); + lc_keys = g_strsplit_set (cname, "-+ ", -1); + g_free (cname); + + for (p = lc_keys; p != NULL && *p != NULL; p++) + { + if ((*p)[0] != '\0') + { + int idx; + + key = lookup_keyname (g_strstrip (*p), &idx); + + if (key == KEY_M_ALT) + use_meta = idx; + else if (key == KEY_M_CTRL) + use_ctrl = idx; + else if (key == KEY_M_SHIFT) + use_shift = idx; + else + { + k = key; + lc_index = idx; + break; + } + } + } + + g_strfreev (lc_keys); + + /* output */ + if (k <= 0) + return 0; + + if (label != NULL) + { + GString *s; + + s = g_string_new (""); + + if (use_meta != -1) + { + g_string_append (s, key_conv_tab_sorted[use_meta]->shortcut); + g_string_append_c (s, '-'); + } + if (use_ctrl != -1) + { + g_string_append (s, key_conv_tab_sorted[use_ctrl]->shortcut); + g_string_append_c (s, '-'); + } + if (use_shift != -1) + { + if (k < 127) + g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k)); + else + { + g_string_append (s, key_conv_tab_sorted[use_shift]->shortcut); + g_string_append_c (s, '-'); + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + } + } + else if (k < 128) + { + if ((k >= 'A') || (lc_index < 0) || (key_conv_tab_sorted[lc_index]->shortcut == NULL)) + g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) k)); + else + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + } + else if ((lc_index != -1) && (key_conv_tab_sorted[lc_index]->shortcut != NULL)) + g_string_append (s, key_conv_tab_sorted[lc_index]->shortcut); + else + g_string_append_c (s, (gchar) g_ascii_tolower ((gchar) key)); + + *label = g_string_free (s, FALSE); + } + + if (use_shift != -1) + { + if (k < 127 && k > 31) + k = g_ascii_toupper ((gchar) k); + else + k |= KEY_M_SHIFT; + } + + if (use_ctrl != -1) + { + if (k < 256) + k = XCTRL (k); + else + k |= KEY_M_CTRL; + } + + if (use_meta != -1) + k = ALT (k); + + return (long) k; +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +lookup_key_by_code (const int keycode) +{ + /* code without modifier */ + unsigned int k = keycode & ~KEY_M_MASK; + /* modifier */ + unsigned int mod = keycode & KEY_M_MASK; + + int key_idx = -1; + + GString *s; + int idx; + + s = g_string_sized_new (8); + + if (lookup_keycode (k, &key_idx) || (k > 0 && k < 256)) + { + if ((mod & KEY_M_ALT) != 0 && lookup_keycode (KEY_M_ALT, &idx)) + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + } + + if ((mod & KEY_M_CTRL) != 0) + { + /* non printeble chars like a CTRL-[A..Z] */ + if (k < 32) + k += 64; + + if (lookup_keycode (KEY_M_CTRL, &idx)) + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + } + } + + if ((mod & KEY_M_SHIFT) != 0) + { + if (lookup_keycode (KEY_M_ALT, &idx)) + { + if (k < 127) + g_string_append_c (s, (gchar) g_ascii_toupper ((gchar) k)); + else + { + g_string_append (s, key_conv_tab_sorted[idx]->name); + g_string_append_c (s, '-'); + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + } + } + } + else if (k < 128) + { + if ((k >= 'A') || (key_idx < 0) || (key_conv_tab_sorted[key_idx]->name == NULL)) + g_string_append_c (s, (gchar) k); + else + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + } + else if ((key_idx != -1) && (key_conv_tab_sorted[key_idx]->name != NULL)) + g_string_append (s, key_conv_tab_sorted[key_idx]->name); + else + g_string_append_c (s, (gchar) keycode); + } + + return g_string_free (s, s->len == 0); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Return TRUE on success, FALSE on error. + * An error happens if SEQ is a beginning of an existing longer sequence. + */ + +gboolean +define_sequence (int code, const char *seq, int action) +{ + key_def *base; + + if (strlen (seq) > SEQ_BUFFER_LEN - 1) + return FALSE; + + for (base = keys; (base != NULL) && (*seq != '\0');) + if (*seq == base->ch) + { + if (base->child == NULL) + { + if (*(seq + 1) != '\0') + base->child = create_sequence (seq + 1, code, action); + else + { + /* The sequence matches an existing one. */ + base->code = code; + base->action = action; + } + return TRUE; + } + + base = base->child; + seq++; + } + else + { + if (base->next != NULL) + base = base->next; + else + { + base->next = create_sequence (seq, code, action); + return TRUE; + } + } + + if (*seq == '\0') + { + /* Attempt to redefine a sequence with a shorter sequence. */ + return FALSE; + } + + keys = create_sequence (seq, code, action); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Check if we are idle, i.e. there are no pending keyboard or mouse + * events. Return 1 is idle, 0 is there are pending events. + */ +gboolean +is_idle (void) +{ + int nfd; + fd_set select_set; + struct timeval time_out; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); + nfd = MAX (0, input_fd) + 1; + time_out.tv_sec = 0; + time_out.tv_usec = 0; +#ifdef HAVE_LIBGPM + if (mouse_enabled && use_mouse_p == MOUSE_GPM) + { + if (gpm_fd >= 0) + { + FD_SET (gpm_fd, &select_set); + nfd = MAX (nfd, gpm_fd + 1); + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + } + } +#endif + return (select (nfd, &select_set, 0, 0, &time_out) <= 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +get_key_code (int no_delay) +{ + int c; + static key_def *this = NULL, *parent; + static gint64 esc_time = -1; + static int lastnodelay = -1; + + if (no_delay != lastnodelay) + { + this = NULL; + lastnodelay = no_delay; + } + + pend_send: + if (pending_keys != NULL) + { + gboolean bad_seq; + + c = *pending_keys++; + while (c == ESC_CHAR) + c = ALT (*pending_keys++); + + bad_seq = (*pending_keys != ESC_CHAR && *pending_keys != '\0'); + if (*pending_keys == '\0' || bad_seq) + pending_keys = seq_append = NULL; + + if (bad_seq) + { + /* This is an unknown ESC sequence. + * To prevent interpreting its tail as a random garbage, + * eat and discard all buffered and quickly following chars. + * Small, but non-zero timeout is needed to reconnect + * escape sequence split up by e.g. a serial line. + */ + int paranoia = 20; + + while (getch_with_timeout (old_esc_mode_timeout) >= 0 && --paranoia != 0) + ; + } + else + { + if (c > 127 && c < 256 && use_8th_bit_as_meta) + c = ALT (c & 0x7f); + + goto done; + } + } + + nodelay_try_again: + if (no_delay != 0) + tty_nodelay (TRUE); + + c = tty_lowlevel_getch (); +#if (defined(USE_NCURSES) || defined(USE_NCURSESW)) && defined(KEY_RESIZE) + if (c == KEY_RESIZE) + goto nodelay_try_again; +#endif + + if (no_delay != 0) + { + tty_nodelay (FALSE); + if (c == -1) + { + if (this == NULL || parent == NULL || parent->action != MCKEY_ESCAPE || !old_esc_mode || + esc_time == -1 || g_get_monotonic_time () < esc_time + old_esc_mode_timeout) + return -1; + + this = NULL; + pending_keys = seq_append = NULL; + return ESC_CHAR; + } + } + else if (c == -1) + { + /* Maybe we got an incomplete match. + This we do only in delay mode, since otherwise + tty_lowlevel_getch can return -1 at any time. */ + if (seq_append == NULL) + { + this = NULL; + return -1; + } + + pending_keys = seq_buffer; + goto pend_send; + } + + /* Search the key on the root */ + if (no_delay == 0 || this == NULL) + { + this = keys; + parent = NULL; + + if (c > 127 && c < 256 && use_8th_bit_as_meta) + { + c &= 0x7f; + + /* The first sequence defined starts with esc */ + parent = keys; + this = keys->child; + } + } + + while (this != NULL) + { + if (c == this->ch) + { + if (this->child == NULL) + { + /* We got a complete match, return and reset search */ + pending_keys = seq_append = NULL; + c = this->code; + goto done; + } + + /* No match yet, but it may be a prefix for a valid seq */ + if (!push_char (c)) + { + pending_keys = seq_buffer; + goto pend_send; + } + + parent = this; + this = this->child; + if (parent->action == MCKEY_ESCAPE && old_esc_mode) + { + if (no_delay != 0) + { + esc_time = g_get_monotonic_time (); + goto nodelay_try_again; + } + + esc_time = -1; + c = getch_with_timeout (old_esc_mode_timeout); + if (c != -1) + continue; + + pending_keys = seq_append = NULL; + this = NULL; + return ESC_CHAR; + } + + if (no_delay != 0) + goto nodelay_try_again; + c = tty_lowlevel_getch (); + continue; + } + + /* c != this->ch. Try other keys with this prefix */ + if (this->next != NULL) + { + this = this->next; + continue; + } + + /* No match found. Is it one of our ESC <key> specials? */ + if ((parent != NULL) && (parent->action == MCKEY_ESCAPE)) + { + /* Convert escape-digits to F-keys */ + if (g_ascii_isdigit (c)) + c = KEY_F (c - '0'); + else if (c == ' ') + c = ESC_CHAR; + else + c = ALT (c); + + pending_keys = seq_append = NULL; + goto done; + } + + /* Unknown sequence. Maybe a prefix of a longer one. Save it. */ + push_char (c); + pending_keys = seq_buffer; + goto pend_send; + } /* while (this != NULL) */ + + done: + this = NULL; + return correct_key_code (c); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a character read from stdin with appropriate interpretation */ +/* Also takes care of generated mouse events */ +/* Returns EV_MOUSE if it is a mouse event */ +/* Returns EV_NONE if non-blocking or interrupt set and nothing was done */ + +int +tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) +{ + int c; + int flag = 0; /* Return value from select */ +#ifdef HAVE_LIBGPM + static struct Gpm_Event ev; /* Mouse event */ +#endif + struct timeval time_out; + struct timeval *time_addr = NULL; + static int dirty = 3; + + if ((dirty == 3) || is_idle ()) + { + mc_refresh (); + dirty = 1; + } + else + dirty++; + + vfs_timeout_handler (); + + /* Ok, we use (event->x < 0) to signal that the event does not contain + a suitable position for the mouse, so we can't use show_mouse_pointer + on it. + */ + if (event->x > 0) + { + show_mouse_pointer (event->x, event->y); + if (!redo_event) + event->x = -1; + } + + /* Repeat if using mouse */ + while (pending_keys == NULL) + { + int nfd; + fd_set select_set; + + FD_ZERO (&select_set); + FD_SET (input_fd, &select_set); + nfd = MAX (add_selects (&select_set), MAX (0, input_fd)) + 1; + +#ifdef HAVE_LIBGPM + if (mouse_enabled && (use_mouse_p == MOUSE_GPM)) + { + if (gpm_fd >= 0) + { + FD_SET (gpm_fd, &select_set); + nfd = MAX (nfd, gpm_fd + 1); + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + break; + } + } +#endif + + if (redo_event) + { + time_out.tv_usec = mou_auto_repeat * MC_USEC_PER_MSEC; + time_out.tv_sec = 0; + + time_addr = &time_out; + } + else + { + int seconds; + + seconds = vfs_timeouts (); + time_addr = NULL; + + if (seconds != 0) + { + /* the timeout could be improved and actually be + * the number of seconds until the next vfs entry + * timeouts in the stamp list. + */ + + time_out.tv_sec = seconds; + time_out.tv_usec = 0; + time_addr = &time_out; + } + } + + if (!block || tty_got_winch ()) + { + time_addr = &time_out; + time_out.tv_sec = 0; + time_out.tv_usec = 0; + } + + tty_enable_interrupt_key (); + flag = select (nfd, &select_set, NULL, NULL, time_addr); + tty_disable_interrupt_key (); + + /* select timed out: it could be for any of the following reasons: + * redo_event -> it was because of the MOU_REPEAT handler + * !block -> we did not block in the select call + * else -> 10 second timeout to check the vfs status. + */ + if (flag == 0) + { + if (redo_event) + return EV_MOUSE; + if (!block || tty_got_winch ()) + return EV_NONE; + vfs_timeout_handler (); + } + if (flag == -1 && errno == EINTR) + return EV_NONE; + + check_selects (&select_set); + + if (FD_ISSET (input_fd, &select_set)) + break; + +#ifdef HAVE_LIBGPM + if (mouse_enabled && use_mouse_p == MOUSE_GPM) + { + if (gpm_fd >= 0) + { + if (FD_ISSET (gpm_fd, &select_set)) + { + int status; + + status = Gpm_GetEvent (&ev); + if (status == 1) /* success */ + { + Gpm_FitEvent (&ev); + *event = ev; + return EV_MOUSE; + } + if (status <= 0) /* connection closed; -1 == error */ + { + if (mouse_fd >= 0 && FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + + disable_mouse (); + return EV_NONE; + } + } + } + else + { + if (mouse_fd >= 0) /* error indicative */ + { + if (FD_ISSET (mouse_fd, &select_set)) + FD_CLR (mouse_fd, &select_set); + mouse_fd = gpm_fd; + } + /* gpm_fd == -2 means under some X terminal */ + if (gpm_fd == -1) + { + mouse_enabled = FALSE; + use_mouse_p = MOUSE_NONE; + } + break; + } + } +#endif /* !HAVE_LIBGPM */ + } + +#ifndef HAVE_SLANG + flag = is_wintouched (stdscr); + untouchwin (stdscr); +#endif /* !HAVE_SLANG */ + c = block ? getch_with_delay () : get_key_code (1); + +#ifndef HAVE_SLANG + if (flag > 0) + tty_touch_screen (); +#endif /* !HAVE_SLANG */ + + if (mouse_enabled && (c == MCKEY_MOUSE +#ifdef KEY_MOUSE + || c == KEY_MOUSE +#endif /* KEY_MOUSE */ + || c == MCKEY_EXTENDED_MOUSE)) + { + /* Mouse event. See tickets 2956 and 3954 for extended mode detection. */ + gboolean extended = c == MCKEY_EXTENDED_MOUSE; + +#ifdef KEY_MOUSE + extended = extended || (c == KEY_MOUSE && xmouse_seq == NULL + && xmouse_extended_seq != NULL); +#endif /* KEY_MOUSE */ + + xmouse_get_event (event, extended); + c = (event->type != 0) ? EV_MOUSE : EV_NONE; + } + else if (c == MCKEY_BRACKETED_PASTING_START) + { + bracketed_pasting_in_progress = TRUE; + c = EV_NONE; + } + else if (c == MCKEY_BRACKETED_PASTING_END) + { + bracketed_pasting_in_progress = FALSE; + c = EV_NONE; + } + + return c; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a key press, mouse events are discarded */ + +int +tty_getch (void) +{ + Gpm_Event ev; + int key; + + ev.x = -1; + while ((key = tty_get_event (&ev, FALSE, TRUE)) == EV_NONE) + ; + return key; +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +learn_key (void) +{ + /* LEARN_TIMEOUT in ms */ +#define LEARN_TIMEOUT 200 + + fd_set Read_FD_Set; + gint64 end_time; + int c; + char buffer[256]; + char *p = buffer; + + tty_keypad (FALSE); /* disable intepreting keys by ncurses */ + c = tty_lowlevel_getch (); + while (c == -1) + c = tty_lowlevel_getch (); /* Sanity check, should be unnecessary */ + learn_store_key (buffer, &p, c); + + end_time = g_get_monotonic_time () + LEARN_TIMEOUT * MC_USEC_PER_MSEC; + + tty_nodelay (TRUE); + while (TRUE) + { + while ((c = tty_lowlevel_getch ()) == -1) + { + gint64 time_out; + struct timeval tv; + + time_out = end_time - g_get_monotonic_time (); + if (time_out <= 0) + break; + + tv.tv_sec = time_out / G_USEC_PER_SEC; + tv.tv_usec = time_out % G_USEC_PER_SEC; + FD_ZERO (&Read_FD_Set); + FD_SET (input_fd, &Read_FD_Set); + select (input_fd + 1, &Read_FD_Set, NULL, NULL, &tv); + } + if (c == -1) + break; + learn_store_key (buffer, &p, c); + } + tty_keypad (TRUE); + tty_nodelay (FALSE); + *p = '\0'; + return (buffer[0] != '\0' ? g_strdup (buffer) : NULL); +#undef LEARN_TIMEOUT +} + +/* --------------------------------------------------------------------------------------------- */ +/* xterm and linux console only: set keypad to numeric or application + mode. Only in application keypad mode it's possible to distinguish + the '+' key and the '+' on the keypad ('*' and '-' ditto) */ + +void +numeric_keypad_mode (void) +{ + if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag) + { + fputs (ESC_STR ">", stdout); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +application_keypad_mode (void) +{ + if (mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag) + { + fputs (ESC_STR "=", stdout); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +enable_bracketed_paste (void) +{ + printf (ESC_STR "[?2004h"); + fflush (stdout); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +disable_bracketed_paste (void) +{ + printf (ESC_STR "[?2004l"); + fflush (stdout); + bracketed_pasting_in_progress = FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ |