diff --git a/.gitignore b/.gitignore index 6d8cb8b62..48d6748b3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.la *~ *.cache +*.log +*.orig *.tar.gz # autoconf runs "mktemp ./confXXXXXX" /conf??????/ @@ -44,6 +46,7 @@ TAGS doc/devel/ doc/html/ .deps +lib/stdckdint.h libtool make.log make.clang diff --git a/doc/man/es/mc.1.in b/doc/man/es/mc.1.in index b06798549..875f756e8 100644 --- a/doc/man/es/mc.1.in +++ b/doc/man/es/mc.1.in @@ -3965,6 +3965,13 @@ For example: .nf autodetect_codeset=russian .fi +.TP +.I kitty_keyboard_protocol +De forma predeterminada, Midnight Commander envía una secuencia de escape que habilita el +protocolo de teclado de Kitty, lo que permite un manejo preciso del teclado (incluyendo +combinaciones de teclas como C\-Enter o C\-S\-Enter) cuando se conecta desde terminales +que admiten este protocolo. Si se establece en falso, Midnight Commander no intentará +habilitar este protocolo. .\"NODE "Parameters for external editor or viewer" .SH "Parámetros para editor o visor externo" Midnight Commander permite especificar opciones para editores y visores diff --git a/doc/man/hu/mc.1.in b/doc/man/hu/mc.1.in index 07ec0ebc9..deda5cbc2 100644 --- a/doc/man/hu/mc.1.in +++ b/doc/man/hu/mc.1.in @@ -3014,6 +3014,14 @@ utolsó fájl a panelben. .IP Ha ez a változó be van állítva (alapértelmezésben) meg foja jelölni azt a fájl parancsot, amelyhez a +.PP +.I kitty_keyboard_protocol +Alapértelmezés szerint a Midnight Commander egy olyan escape szekvenciát +küld, amely engedélyezi a Kitty billentyűzetprotokollt. Ez lehetővé teszi +a billentyűzet pontos kezelését (beleértve az olyan billentyűkombinációkat +is, mint a C\-Enter vagy a C\-S\-Enter), amikor olyan terminálról +csatlakoznak, amely támogatja ezt a protokollt. Ha az érték hamisra van +állítva, a Midnight Commander nem próbálja meg engedélyezni ezt a protokollt. .\"LINK2" Társításokban .\"Edit Extension File" diff --git a/doc/man/it/mc.1.in b/doc/man/it/mc.1.in index db00bf961..790e356a2 100644 --- a/doc/man/it/mc.1.in +++ b/doc/man/it/mc.1.in @@ -3014,6 +3014,14 @@ pannello. .I use_file_to_guess_type Se questa variabile è abilitata (valore predefinito) userà il comando file per trovare delle corrispondenze sui tipi di file elencati nel +.TP +.I kitty_keyboard_protocol +Per impostazione predefinita, Midnight Commander invia una sequenza di +escape che abilita il Kitty Keyboard Protocol, il quale consente una +gestione precisa della tastiera (incluse combinazioni di tasti come +C\-Enter o C\-S\-Enter) quando ci si connette da terminali che supportano +questo protocollo. Se impostato su false, Midnight Commander non tenterà +di abilitare questo protocollo. .\"LINK2" file mc.ext.ini\&. .\"Edit Extension File" diff --git a/doc/man/mc.1.in b/doc/man/mc.1.in index 1ba004697..39eebcb6d 100644 --- a/doc/man/mc.1.in +++ b/doc/man/mc.1.in @@ -4208,6 +4208,13 @@ For example: .nf autodetect_codeset=russian .fi +.TP +.I kitty_keyboard_protocol +By default, Midnight Commander sends an escape sequence that enables the Kitty +Keyboard Protocol, which allows precise keyboard handling (including key +combinations such as C\-Enter or C\-S\-Enter) when connected from terminals that +support this protocol. If set to false, Midnight Commander will not attempt to +enable this protocol. .\"NODE "Parameters for external editor or viewer" .SH "Parameters for external editor or viewer" Midnight Commander provides a way for specify an options for external editors diff --git a/doc/man/pl/mc.1.in b/doc/man/pl/mc.1.in index e00e710f0..9ef338bf6 100644 --- a/doc/man/pl/mc.1.in +++ b/doc/man/pl/mc.1.in @@ -2708,6 +2708,13 @@ Jeśli ta opcja jest włączona (standardowo tak nie jest) kiedy przeglądasz pl w panelu drzewa, będzie on automatycznie przeładowywał drugi panel na zawartość wybranego katalogu. .PP +.I kitty_keyboard_protocol +Domyślnie Midnight Commander wysyła sekwencję escape, która włącza protokół +klawiatury Kitty. Umożliwia on precyzyjną obsługę klawiatury (w tym kombinacje +klawiszy takie jak C\-Enter czy C\-S\-Enter) podczas połączenia z terminalami +obsługującymi ten protokół. Jeśli ustawione na false, Midnight Commander +nie będzie próbował włączyć tego protokołu. +.PP .\"NODE "Terminal databases" .SH Baza danych terminali (Terminal databases) Midnight Commander pozwala ci na naprawienie bazy danych terminali bez diff --git a/doc/man/ru/mc.1.in b/doc/man/ru/mc.1.in index 758ac9144..07a2ef901 100644 --- a/doc/man/ru/mc.1.in +++ b/doc/man/ru/mc.1.in @@ -4663,6 +4663,13 @@ clipboard_paste=xclip \-o .nf autodetect_codeset=russian .fi +.PP +.I kitty_keyboard_protocol +По умолчанию Midnight Commander отправляет управляющую последовательность, которая +включает протокол клавиатуры Kitty. Этот протокол обеспечивает точную обработку +клавиатуры (включая такие комбинации клавиш, как C\-Enter или C\-S\-Enter) при +подключении из терминалов, поддерживающих данный протокол. Если установить значение +в false, Midnight Commander не будет пытаться включить этот протокол. .\"NODE "Parameters for external editor or viewer" .SH "Параметры для внешних редакторов и программ просмотра" Midnight Commander позволяет задать некоторые параметрыы для внешних редакторов diff --git a/doc/man/sr/mc.1.in b/doc/man/sr/mc.1.in index 8f0a41931..11c26d41d 100644 --- a/doc/man/sr/mc.1.in +++ b/doc/man/sr/mc.1.in @@ -4044,6 +4044,14 @@ clipboard_paste=xclip \-o .nf autodetect_codeset=russian .fi +.TP +.I kitty_keyboard_protocol +Подразумевано, Midnight Commander шаље escape секвенцу која омогућава +Kitty протокол тастатуре, што омогућава прецизно руковање тастатуром +(укључујући комбинације тастера као што су C\-Enter или C\-S\-Enter) +када се повезује са терминала који подржавају овај протокол. Ако је +постављено на false, Midnight Commander неће покушати да омогући овај +протокол. .\"NODE "Parameters for external editor or viewer" .SH "Параметри спољашњег уређивача или прегледача" Поноћни наредник обезбеђује начин да се задају опције за спољашње уређиваче и diff --git a/lib/global.c b/lib/global.c index 2fef6a5a9..ff536562c 100644 --- a/lib/global.c +++ b/lib/global.c @@ -103,7 +103,8 @@ mc_global_t mc_global = .disable_colors = FALSE, .ugly_line_drawing = FALSE, .old_mouse = FALSE, - .alternate_plus_minus = FALSE + .alternate_plus_minus = FALSE, + .kitty_keyboard_protocol = TRUE }, .vfs = diff --git a/lib/global.h b/lib/global.h index b8613f943..0059138cf 100644 --- a/lib/global.h +++ b/lib/global.h @@ -206,6 +206,10 @@ typedef struct /* If true, use + and \ keys normally and select/unselect do if M-+ / M-\. and M-- and keypad + / - */ gboolean alternate_plus_minus; + + // If true, send Kitty Keyboard Protocol initialization string to the terminal + // Defaults to true, because terminals that don't support it simply do nothing + gboolean kitty_keyboard_protocol; } tty; struct diff --git a/lib/terminal.c b/lib/terminal.c index a7e460130..e087e907d 100644 --- a/lib/terminal.c +++ b/lib/terminal.c @@ -156,6 +156,7 @@ parse_csi (csi_command_t *out, const char **sptr, const char *end) { out->private_mode = private_mode; out->param_count = param_count; + out->final_byte = c; } invalid_sequence: diff --git a/lib/terminal.h b/lib/terminal.h index a5e453635..d47b61956 100644 --- a/lib/terminal.h +++ b/lib/terminal.h @@ -21,6 +21,7 @@ typedef struct char private_mode; uint32_t params[16][4]; size_t param_count; + char final_byte; } csi_command_t; /*** global variables defined in .c file *********************************************************/ diff --git a/lib/tty/key.c b/lib/tty/key.c index 62a0f1095..052fe0472 100644 --- a/lib/tty/key.c +++ b/lib/tty/key.c @@ -56,6 +56,7 @@ #include "tty-internal.h" // mouse_enabled #include "mouse.h" #include "key.h" +#include "lib/terminal.h" #include "lib/widget.h" // mc_refresh() @@ -221,8 +222,8 @@ const key_code_name_t key_name_conv_tab[] = { #define MC_USEC_PER_MSEC 1000 -/* The maximum sequence length (32 + null terminator) */ -#define SEQ_BUFFER_LEN 33 +/* The maximum sequence length */ +#define SEQ_BUFFER_LEN 100 /*** file scope type declarations ****************************************************************/ @@ -253,6 +254,13 @@ typedef struct int action; } key_define_t; +typedef struct +{ + int code; + unsigned int kitty_code; + const char final_byte; +} key_kitty_t; + /* File descriptor monitoring add/remove routines */ typedef struct { @@ -283,25 +291,39 @@ static key_define_t mc_default_keys[] = { { 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 }, + + // Linux function keys F1-F5 + // They must be defined here because the generic CSI handler in get_key_code() can't ignore them + // quietly, as they violate standardized CSI syntax + { KEY_F (1), ESC_STR "[[A", MCKEY_NOACTION }, + { KEY_F (2), ESC_STR "[[B", MCKEY_NOACTION }, + { KEY_F (3), ESC_STR "[[C", MCKEY_NOACTION }, + { KEY_F (4), ESC_STR "[[D", MCKEY_NOACTION }, + { KEY_F (5), ESC_STR "[[E", MCKEY_NOACTION }, + + // DEC VT100 cursor application mode keys + // Since there must be at least one SS3 definition for the get_key_code() sequence parser to + // work correctly, define these cursor sequences as they are very common + { KEY_UP, ESC_STR "OA", MCKEY_NOACTION }, + { KEY_DOWN, ESC_STR "OB", MCKEY_NOACTION }, + { KEY_RIGHT, ESC_STR "OC", MCKEY_NOACTION }, + { KEY_LEFT, ESC_STR "OD", MCKEY_NOACTION }, + { 0, NULL, MCKEY_NOACTION }, }; /* Broken terminfo and termcap databases on xterminals */ static key_define_t xterm_key_defines[] = { + + /* The Kitty Keyboard Protocol extends the most common key escape sequences in a backwards + * compatible way. Thus standard sequences are implicitly handled by the Kitty handler, and + * don't need to be listed here. + */ + { 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 }, @@ -309,39 +331,9 @@ static key_define_t xterm_key_defines[] = { { 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 /* @@ -353,39 +345,7 @@ static key_define_t xterm_key_defines[] = { { 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 }, @@ -434,14 +394,6 @@ static key_define_t xterm_key_defines[] = { { 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 }, @@ -524,6 +476,113 @@ static key_define_t qansi_key_defines[] = { { 0, NULL, MCKEY_NOACTION }, }; +static key_kitty_t kitty_key_defines[] = { + { ESC_CHAR, 27, 'u' }, + { (int) '\r', 13, 'u' }, + { KEY_IC, 2, '~' }, + { KEY_DC, 3, '~' }, + { KEY_UP, 1, 'A' }, + { KEY_DOWN, 1, 'B' }, + { KEY_RIGHT, 1, 'C' }, + { KEY_LEFT, 1, 'D' }, + { KEY_PPAGE, 5, '~' }, + { KEY_NPAGE, 6, '~' }, + { KEY_HOME, 1, 'H' }, + { KEY_HOME, 7, '~' }, + { KEY_END, 1, 'F' }, + { KEY_END, 8, '~' }, + { -1, 57361, 'u' }, // PRINT_SCREEN + { -1, 57362, 'u' }, // PAUSE + { -1, 57363, 'u' }, // MENU + { KEY_F (1), 11, '~' }, + { KEY_F (1), 1, 'P' }, + { KEY_F (2), 12, '~' }, + { KEY_F (2), 1, 'Q' }, + { KEY_F (3), 13, '~' }, + { KEY_F (4), 14, '~' }, + { KEY_F (4), 1, 'S' }, + { KEY_F (5), 15, '~' }, + { KEY_F (6), 17, '~' }, + { KEY_F (7), 18, '~' }, + { KEY_F (8), 19, '~' }, + { KEY_F (9), 20, '~' }, + { KEY_F (10), 21, '~' }, + { KEY_F (11), 23, '~' }, + { KEY_F (12), 24, '~' }, + { KEY_F (13), 57376, 'u' }, + { KEY_F (14), 57377, '~' }, + { KEY_F (15), 57378, '~' }, + { KEY_F (16), 57379, '~' }, + { KEY_F (17), 57380, '~' }, + { KEY_F (18), 57381, '~' }, + { KEY_F (19), 57382, '~' }, + { KEY_F (20), 57383, '~' }, + { KEY_F (21), 57384, '~' }, + { KEY_F (22), 57385, '~' }, + { KEY_F (23), 57386, '~' }, + { KEY_F (24), 57387, '~' }, + { KEY_F (25), 57388, '~' }, + { KEY_F (26), 57389, '~' }, + { KEY_F (27), 57390, '~' }, + { KEY_F (28), 57391, '~' }, + { KEY_F (29), 57392, '~' }, + { KEY_F (30), 57393, '~' }, + { KEY_F (31), 57394, '~' }, + { KEY_F (32), 57395, '~' }, + { KEY_F (33), 57396, '~' }, + { KEY_F (34), 57397, '~' }, + { KEY_F (35), 57398, '~' }, + { (int) '0', 57399, 'u' }, // KP_0 + { (int) '1', 57400, 'u' }, // KP_1 + { (int) '2', 57401, 'u' }, // KP_2 + { (int) '3', 57402, 'u' }, // KP_3 + { (int) '4', 57403, 'u' }, // KP_4 + { (int) '5', 57404, 'u' }, // KP_5 + { (int) '6', 57405, 'u' }, // KP_6 + { (int) '7', 57406, 'u' }, // KP_7 + { (int) '8', 57407, 'u' }, // KP_8 + { (int) '9', 57408, 'u' }, // KP_9 + { (int) '.', 57409, 'u' }, // KP_DECIMAL + { (int) '/', 57410, 'u' }, // KP_DIVIDE + { KEY_KP_MULTIPLY, 57411, 'u' }, + { KEY_KP_SUBTRACT, 57412, 'u' }, + { KEY_KP_ADD, 57413, 'u' }, + { KEY_ENTER, 57414, 'u' }, // KP_ENTER + { -1, 57415, 'u' }, // KP_EQUAL + { -1, 57416, 'u' }, // KP_SEPARATOR + { KEY_LEFT, 57417, 'u' }, // KP_LEFT + { KEY_RIGHT, 57418, 'u' }, // KP_RIGHT + { KEY_UP, 57419, 'u' }, // KP_UP + { KEY_DOWN, 57420, 'u' }, // KP_DOWN + { KEY_PPAGE, 57421, 'u' }, // KP_PAGE_UP + { KEY_NPAGE, 57422, 'u' }, // KP_PAGE_DOWN + { KEY_HOME, 57423, 'u' }, // KP_HOME + { KEY_END, 57424, 'u' }, // KP_END + { KEY_IC, 57425, 'u' }, // KP_INSERT + { KEY_DC, 57426, 'u' }, // KP_DELETE + { -1, 1, 'E' }, // KP_BEGIN + { -1, 57427, '~' }, // KP_BEGIN + + // MEDIA_* keys omitted + + { -1, 57441, 'u' }, // LEFT_SHIFT + { -1, 57442, 'u' }, // LEFT_CONTROL + { -1, 57443, 'u' }, // LEFT_ALT + { -1, 57444, 'u' }, // LEFT_SUPER + { -1, 57445, 'u' }, // LEFT_HYPER + { -1, 57446, 'u' }, // LEFT_META + { -1, 57447, 'u' }, // RIGHT_SHIFT + { -1, 57448, 'u' }, // RIGHT_CONTROL + { -1, 57449, 'u' }, // RIGHT_ALT + { -1, 57450, 'u' }, // RIGHT_SUPER + { -1, 57451, 'u' }, // RIGHT_HYPER + { -1, 57452, 'u' }, // RIGHT_META + { -1, 57453, 'u' }, // ISO_LEVEL3_SHIFT + { -1, 57454, 'u' }, // ISO_LEVEL5_SHIFT + + { 0, 0, 0 }, +}; + /* This holds all the key definitions */ static key_def *keys = NULL; @@ -1064,7 +1123,7 @@ correct_key_code (int code) */ if (c == '\b') { - // Special case for backspase ('\b' < 32) + // Special case for backspace ('\b' < 32) c = KEY_BACKSPACE; mod &= ~KEY_M_CTRL; } @@ -1149,26 +1208,6 @@ correct_key_code (int code) /* --------------------------------------------------------------------------------------------- */ -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 (GString *buffer, int c) { @@ -1303,6 +1342,116 @@ lookup_keycode (const int code, int *idx) return FALSE; } +/* --------------------------------------------------------------------------------------------- */ +/** + * Check for Kitty Keyboard Protocol including backward compatibility sequences: + * + * CSI parameters [u~ABCDEFHPQS] + * + * https://sw.kovidgoyal.net/kitty/keyboard-protocol/ + * + * Examples: + * + * \E[2~ or \E[2;1~ -> KEY_IC + * \E[1;2A -> KEY_M_SHIFT | KEY_UP + * \E[13;6u -> KEY_M_CTRL | KEY_M_SHIFT | '\n' + * \E[111;5u\E[57421;5:2u -> XCTRL(KEY_M_CTRL | 'o'), KEY_M_CTRL | KEY_PPAGE + * \E[44:63;132u -> KEY_M_ALT | '?' (on Czech keyboard, with Num Lock on) + */ +static int +parse_kitty (csi_command_t csi) +{ + int c; + guint unicode_key_code, shifted_key, base_layout_key, modifiers; + gchar utf8char[MB_LEN_MAX + 1]; + + if (!((csi.final_byte == 'u' && csi.param_count >= 1) + || (csi.final_byte == '~' && csi.param_count >= 1) + || (csi.final_byte >= 'A' && csi.final_byte <= 'F') || (csi.final_byte == 'H') + || (csi.final_byte >= 'P' && csi.final_byte <= 'S'))) + return -1; + + // Private mode is used for reporting protocol presence and mode from terminal + // (CSI ? flags u). Currently we don't need this info so we don't process it + if (csi.private_mode != 0) + return -1; + + unicode_key_code = csi.params[0][0]; + shifted_key = csi.params[0][1]; + base_layout_key = csi.params[0][2]; + modifiers = csi.params[1][0]; + + if (modifiers != 0) + modifiers = ((modifiers - 1) << 12) & KEY_M_MASK; + + // Zero parameters are allowed. In that case, assume key code 1 as the default + if (csi.param_count == 0) + unicode_key_code = 1; + + // Check for non-literal keys using a predefined table + for (int j = 0; kitty_key_defines[j].code != 0; j++) + { + if (kitty_key_defines[j].kitty_code == unicode_key_code + && kitty_key_defines[j].final_byte == csi.final_byte) + { + c = kitty_key_defines[j].code; + if (c == -1) + return -1; + c |= modifiers; + + return c; + } + } + + // All possible keys (except for a few backward compatibility exceptions) are + // defined in the Unicode Basic Multilingual Plane Private Use Area. Discard + // unknown ones + if (unicode_key_code >= 0xE000 && unicode_key_code <= 0xF8FF) + return -1; + + // Translate shifted chars so command line input works + if (shifted_key) + { + c = shifted_key; + modifiers &= ~KEY_M_SHIFT; + } + else + { + c = unicode_key_code; + if ((base_layout_key || (c >= '0' && c <= '9'))) + modifiers &= ~KEY_M_SHIFT; + } + + // Check if it is a non-Basic Latin Unicode character. If so, convert it to UTF-8 + // and return the remaining bytes into the input queue. + if (c >= 0x80) + { + // If it's a CTRL or ALT combination and we have a base layout key, use it instead. + if (base_layout_key && (modifiers & (KEY_M_CTRL | KEY_M_ALT)) != 0) + c = base_layout_key; + // Otherwise, use the Unicode character and ignore modifiers. + else + { + const int utflen = g_unichar_to_utf8 (c, (gchar *) &utf8char); + + c = utf8char[0] & 0xFF; + for (int j = 1; j < utflen; j++) + push_char (utf8char[j] & 0xFF); + + pending_keys = seq_buffer; + return c; + } + } + + // Mask the resulting value with 0x1F if the CTRL modifier is set + if ((modifiers & KEY_M_CTRL) && (c == ' ' || (c >= 0x40 && c <= 0x7e))) + c = XCTRL (c); + + c |= modifiers; + + return c; +} + /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ @@ -1716,31 +1865,74 @@ get_key_code (int no_delay) pend_send: if (pending_keys != NULL) { - gboolean bad_seq; + /* At this point, no sequence was found in the keys tree. + * + * Now check for all CSI and SS3 sequences, as these two are the de-facto standard for + * sending key sequences in most terminals. The only well-known deviations are: + * - Linux console (it sends invalid CSI for F1-F5, as double '[' is not allowed in CSI); + * this is handled by defining them in our key defines table (see above). + * - xterm with modify*Keys:0 (the old behavior, which sends parametrized SS3 sequences), + * and many terminals that inherited this ill behavior, as SS3 can't have any parameters. + * As a workaround, we treat SS3 like CSI. + * - Some terminals begin sequences with ESC ESC to indicate Alt modifier (for example + * PuTTY). + * + * Parse the sequence, check if it matches any of the complex sequences we are interested + * in, and discard it completely if it is unknown. Leave any keys following these sequences + * in stdin. + */ + + if (pending_keys[0] == ESC_CHAR && pending_keys[1] == ESC_CHAR) + pending_keys++; + if (pending_keys[0] == ESC_CHAR && (pending_keys[1] == '[' || pending_keys[1] == 'O')) + { + int seqlen; + + c = seq_append[-1]; + + // Get all parameter bytes and intermediate bytes before the final byte + while (c >= ' ' && c <= '?') + { + c = tty_lowlevel_getch (); + push_char (c); + } + + char seq_char_buffer[SEQ_BUFFER_LEN]; // Must have same length as seq_buffer + + for (seqlen = 0; pending_keys[seqlen] != '\0'; seqlen++) + seq_char_buffer[seqlen] = pending_keys[seqlen]; + seq_char_buffer[seqlen] = '\0'; + + pending_keys = seq_append = NULL; + + if (seq_char_buffer[1] == '[') // CSI + { + csi_command_t csi; + + const char *seq_char_buffer_ptr = &seq_char_buffer[2]; + if (!parse_csi (&csi, &seq_char_buffer_ptr, &seq_char_buffer[seqlen])) + return -1; + + // Check for Kitty Keyboard Protocol sequence (or any common backwards compatible + // sequence) + c = parse_kitty (csi); + if (c != -1) + goto done; + } + + return -1; + } + + // Most terminals indicate the Alt key by prepending to the character sent 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) + if (*pending_keys == '\0') 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 - goto done; + goto done; } nodelay_try_again: @@ -2107,6 +2299,26 @@ tty_getch (void) /* --------------------------------------------------------------------------------------------- */ +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; +} + +/* --------------------------------------------------------------------------------------------- */ + char * learn_key (void) { diff --git a/lib/tty/key.h b/lib/tty/key.h index 3d902678e..fce70cd21 100644 --- a/lib/tty/key.h +++ b/lib/tty/key.h @@ -5,8 +5,9 @@ #ifndef MC__KEY_H #define MC__KEY_H -#include "lib/global.h" // -#include "tty.h" // KEY_F macro +#include "lib/global.h" // +#include "tty.h" // KEY_F macro +#include "lib/terminal.h" // csi_command_t struct /*** typedefs(not structures) and defined constants **********************************************/ @@ -80,6 +81,7 @@ char *tty_keycode_to_keyname (const int keycode); int tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block); gboolean is_idle (void); int tty_getch (void); +MC_MOCKABLE int getch_with_timeout (unsigned int delay_us); /* While waiting for input, the program can select on more than one file */ typedef int (*select_fn) (int fd, void *info); diff --git a/lib/tty/tty-internal.h b/lib/tty/tty-internal.h index c7a89e7ab..af6bda4b2 100644 --- a/lib/tty/tty-internal.h +++ b/lib/tty/tty-internal.h @@ -35,12 +35,14 @@ extern int sigwinch_pipe[2]; /*** declarations of public functions ************************************************************/ +void load_terminfo_keys (void); + void tty_create_winch_pipe (void); void tty_destroy_winch_pipe (void); char *mc_tty_normalize_from_utf8 (const char *str); void tty_init_xterm_support (gboolean is_xterm); -int tty_lowlevel_getch (void); +MC_MOCKABLE int tty_lowlevel_getch (void); void tty_colorize_area (int y, int x, int rows, int cols, int color); diff --git a/lib/tty/tty-ncurses.c b/lib/tty/tty-ncurses.c index 96c24707d..32add8c01 100644 --- a/lib/tty/tty-ncurses.c +++ b/lib/tty/tty-ncurses.c @@ -307,6 +307,7 @@ tty_init (gboolean mouse_enable, gboolean is_xterm) noecho (); keypad (stdscr, TRUE); nodelay (stdscr, FALSE); + tty_kitty (TRUE); tty_setup_sigwinch (sigwinch_handler); } @@ -317,6 +318,7 @@ void tty_shutdown (void) { tty_destroy_winch_pipe (); + tty_kitty (FALSE); tty_reset_shell_mode (); tty_noraw_mode (); tty_keypad (FALSE); @@ -723,6 +725,15 @@ tty_print_string (const char *s) /* --------------------------------------------------------------------------------------------- */ +void +tty_putp (const char *s) +{ + putp (s); + fflush (stdout); +} + +/* --------------------------------------------------------------------------------------------- */ + void tty_printf (const char *fmt, ...) { diff --git a/lib/tty/tty-slang.c b/lib/tty/tty-slang.c index e70b26354..f3e38c093 100644 --- a/lib/tty/tty-slang.c +++ b/lib/tty/tty-slang.c @@ -210,9 +210,11 @@ do_define_key (int code, const char *strcap) define_sequence (code, seq, MCKEY_NOACTION); } +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ -static void +void load_terminfo_keys (void) { int i; @@ -327,6 +329,7 @@ tty_init (gboolean mouse_enable, gboolean is_xterm) tty_enter_ca_mode (); tty_keypad (TRUE); tty_nodelay (FALSE); + tty_kitty (TRUE); tty_setup_sigwinch (sigwinch_handler); } @@ -339,6 +342,7 @@ tty_shutdown (void) char *op_cap; tty_destroy_winch_pipe (); + tty_kitty (FALSE); tty_reset_shell_mode (); tty_noraw_mode (); tty_keypad (FALSE); @@ -679,6 +683,16 @@ tty_print_string (const char *s) /* --------------------------------------------------------------------------------------------- */ +void +tty_putp (const char *s) +{ + // S-Lang has SLtt_tputs(), but it only passes the chars through one-by-one without any + // processing + SLtt_write_string ((char *) s); +} + +/* --------------------------------------------------------------------------------------------- */ + void tty_printf (const char *fmt, ...) { @@ -723,6 +737,23 @@ tty_tigetstr (const char *terminfo_cap, const char *termcap_cap) /* --------------------------------------------------------------------------------------------- */ +// Warning: S-Lang doesn't support more than two parameters +char * +tty_tiparm (const char *str, ...) +{ + va_list args; + int p1, p2; + + va_start (args, str); + p1 = va_arg (args, int); + p2 = va_arg (args, int); + va_end (args); + + return SLtt_tgoto ((SLFUTURE_CONST char *) str, p2, p1); +} + +/* --------------------------------------------------------------------------------------------- */ + void tty_refresh (void) { diff --git a/lib/tty/tty.c b/lib/tty/tty.c index 5a9ed7f3a..4ff01cb75 100644 --- a/lib/tty/tty.c +++ b/lib/tty/tty.c @@ -422,3 +422,23 @@ tty_init_xterm_support (gboolean is_xterm) } /* --------------------------------------------------------------------------------------------- */ + +/** Enable or disable Kitty Keyboard Protocol */ +void +tty_kitty (gboolean set) +{ + if (!mc_global.tty.kitty_keyboard_protocol) + return; + + if (set) + { + // 1 = Disambiguate escape codes + // 4 = Report alternate keys (required for detecting Alt+Shift+key) + // 8 = Report all keys as escape codes (required for distinguishing keypad keys [+-*]) + tty_putp (ESC_STR "[>13u"); + } + else + tty_putp (ESC_STR "[