Revamp extended keys support to more closely match xterm and support

mode 2 as well as mode 1. From Stanislav Kljuhhin (GitHub issue 4038).

This changes tmux to always request mode 2 from parent terminal, change
to an unambiguous internal representation of keys, and adds an option
(extended-keys-format) to control the format similar to the xterm(1)
formatOtherKeys resource.
This commit is contained in:
nicm 2024-08-21 04:17:09 +00:00
parent de6bce057a
commit c7e61a01e5
12 changed files with 238 additions and 136 deletions

View File

@ -1962,6 +1962,23 @@ format_cb_pane_unseen_changes(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_key_mode. */
static void *
format_cb_pane_key_mode(struct format_tree *ft)
{
if (ft->wp != NULL && ft->wp->screen != NULL) {
switch (ft->wp->screen->mode & EXTENDED_KEY_MODES) {
case MODE_KEYS_EXTENDED:
return (xstrdup("Ext 1"));
case MODE_KEYS_EXTENDED_2:
return (xstrdup("Ext 2"));
default:
return (xstrdup("VT10x"));
}
}
return (NULL);
}
/* Callback for pane_last. */
static void *
format_cb_pane_last(struct format_tree *ft)
@ -2997,6 +3014,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_input_off", FORMAT_TABLE_STRING,
format_cb_pane_input_off
},
{ "pane_key_mode", FORMAT_TABLE_STRING,
format_cb_pane_key_mode
},
{ "pane_last", FORMAT_TABLE_STRING,
format_cb_pane_last
},

View File

@ -56,12 +56,47 @@ static const struct {
{ "PPage", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "PageUp", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "PgUp", KEYC_PPAGE|KEYC_IMPLIED_META },
{ "Tab", '\011' },
{ "BTab", KEYC_BTAB },
{ "Space", ' ' },
{ "BSpace", KEYC_BSPACE },
{ "Enter", '\r' },
{ "Escape", '\033' },
/*
* C0 control characters, with the exception of Tab, Enter,
* and Esc, should never appear as keys. We still render them,
* so to be able to spot them in logs in case of an abnormality.
*/
{ "[NUL]", C0_NUL },
{ "[SOH]", C0_SOH },
{ "[STX]", C0_STX },
{ "[ETX]", C0_ETX },
{ "[EOT]", C0_EOT },
{ "[ENQ]", C0_ENQ },
{ "[ASC]", C0_ASC },
{ "[BEL]", C0_BEL },
{ "[BS]", C0_BS },
{ "Tab", C0_HT },
{ "[LF]", C0_LF },
{ "[VT]", C0_VT },
{ "[FF]", C0_FF },
{ "Enter", C0_CR },
{ "[SO]", C0_SO },
{ "[SI]", C0_SI },
{ "[DLE]", C0_DLE },
{ "[DC1]", C0_DC1 },
{ "[DC2]", C0_DC2 },
{ "[DC3]", C0_DC3 },
{ "[DC4]", C0_DC4 },
{ "[NAK]", C0_NAK },
{ "[SYN]", C0_SYN },
{ "[ETB]", C0_ETB },
{ "[CAN]", C0_CAN },
{ "[EM]", C0_EM },
{ "[SUB]", C0_SUB },
{ "Escape", C0_ESC },
{ "[FS]", C0_FS },
{ "[GS]", C0_GS },
{ "[RS]", C0_RS },
{ "[US]", C0_US },
/* Arrow keys. */
{ "Up", KEYC_UP|KEYC_CURSOR|KEYC_IMPLIED_META },
@ -206,7 +241,6 @@ key_string_get_modifiers(const char **string)
key_code
key_string_lookup_string(const char *string)
{
static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/";
key_code key, modifiers;
u_int u, i;
struct utf8_data ud, *udp;
@ -281,26 +315,6 @@ key_string_lookup_string(const char *string)
key &= ~KEYC_IMPLIED_META;
}
/* Convert the standard control keys. */
if (key <= 127 &&
(modifiers & KEYC_CTRL) &&
strchr(other, key) == NULL &&
key != 9 &&
key != 13 &&
key != 27) {
if (key >= 97 && key <= 122)
key -= 96;
else if (key >= 64 && key <= 95)
key -= 64;
else if (key == 32)
key = 0;
else if (key == 63)
key = 127;
else
return (KEYC_UNKNOWN);
modifiers &= ~KEYC_CTRL;
}
return (key|modifiers);
}
@ -324,10 +338,6 @@ key_string_lookup_key(key_code key, int with_flags)
goto out;
}
/* Display C-@ as C-Space. */
if ((key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) == 0)
key = ' '|KEYC_CTRL;
/* Fill in the modifiers. */
if (key & KEYC_CTRL)
strlcat(out, "C-", sizeof out);
@ -427,13 +437,8 @@ key_string_lookup_key(key_code key, int with_flags)
goto out;
}
/* Check for standard or control key. */
if (key <= 32) {
if (key == 0 || key > 26)
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key));
else
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key));
} else if (key >= 32 && key <= 126) {
/* Printable ASCII keys. */
if (key > 32 && key <= 126) {
tmp[0] = key;
tmp[1] = '\0';
} else if (key == 127)
@ -460,8 +465,6 @@ out:
strlcat(out, "I", sizeof out);
if (saved & KEYC_BUILD_MODIFIERS)
strlcat(out, "B", sizeof out);
if (saved & KEYC_EXTENDED)
strlcat(out, "E", sizeof out);
if (saved & KEYC_SENT)
strlcat(out, "S", sizeof out);
strlcat(out, "]", sizeof out);

8
menu.c
View File

@ -335,7 +335,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case KEYC_PPAGE:
case '\002': /* C-b */
case 'b'|KEYC_CTRL:
if (md->choice < 6)
md->choice = 0;
else {
@ -394,13 +394,13 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case '\006': /* C-f */
case 'f'|KEYC_CTRL:
break;
case '\r':
goto chosen;
case '\033': /* Escape */
case '\003': /* C-c */
case '\007': /* C-g */
case 'c'|KEYC_CTRL:
case 'g'|KEYC_CTRL:
case 'q':
return (1);
}

View File

@ -1088,22 +1088,22 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
switch (*key) {
case 'q':
case '\033': /* Escape */
case '\007': /* C-g */
case 'g'|KEYC_CTRL:
return (1);
case KEYC_UP:
case 'k':
case KEYC_WHEELUP_PANE:
case '\020': /* C-p */
case 'p'|KEYC_CTRL:
mode_tree_up(mtd, 1);
break;
case KEYC_DOWN:
case 'j':
case KEYC_WHEELDOWN_PANE:
case '\016': /* C-n */
case 'n'|KEYC_CTRL:
mode_tree_down(mtd, 1);
break;
case KEYC_PPAGE:
case '\002': /* C-b */
case 'b'|KEYC_CTRL:
for (i = 0; i < mtd->height; i++) {
if (mtd->current == 0)
break;
@ -1111,7 +1111,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
}
break;
case KEYC_NPAGE:
case '\006': /* C-f */
case 'f'|KEYC_CTRL:
for (i = 0; i < mtd->height; i++) {
if (mtd->current == mtd->line_size - 1)
break;
@ -1155,7 +1155,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
for (i = 0; i < mtd->line_size; i++)
mtd->line_list[i].item->tagged = 0;
break;
case '\024': /* C-t */
case 't'|KEYC_CTRL:
for (i = 0; i < mtd->line_size; i++) {
if ((mtd->line_list[i].item->parent == NULL &&
!mtd->line_list[i].item->no_tag) ||
@ -1211,7 +1211,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
break;
case '?':
case '/':
case '\023': /* C-s */
case 's'|KEYC_CTRL:
mtd->references++;
status_prompt_set(c, NULL, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,

View File

@ -543,7 +543,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
}
if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
pd->job == NULL) &&
(event->key == '\033' || event->key == '\003'))
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
return (1);
if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) {

View File

@ -326,8 +326,9 @@ screen_write_reset(struct screen_write_ctx *ctx)
screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
s->mode = MODE_CURSOR|MODE_WRAP;
if (options_get_number(global_options, "extended-keys") == 2)
s->mode |= MODE_KEXTENDED;
s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
screen_write_clearscreen(ctx, 8);
screen_write_set_cursor(ctx, 0, 0);

View File

@ -107,8 +107,9 @@ screen_reinit(struct screen *s)
s->rlower = screen_size_y(s) - 1;
s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF);
if (options_get_number(global_options, "extended-keys") == 2)
s->mode |= MODE_KEXTENDED;
s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
if (s->saved_grid != NULL)
screen_alternate_off(s, NULL, 0);
@ -714,8 +715,10 @@ screen_mode_to_string(int mode)
strlcat(tmp, "ORIGIN,", sizeof tmp);
if (mode & MODE_CRLF)
strlcat(tmp, "CRLF,", sizeof tmp);
if (mode & MODE_KEXTENDED)
strlcat(tmp, "KEXTENDED,", sizeof tmp);
if (mode & MODE_KEYS_EXTENDED)
strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp);
if (mode & MODE_KEYS_EXTENDED_2)
strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp);
tmp[strlen(tmp) - 1] = '\0';
return (tmp);
}

View File

@ -839,19 +839,19 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
{
if (c->prompt_mode == PROMPT_ENTRY) {
switch (key) {
case '\001': /* C-a */
case '\003': /* C-c */
case '\005': /* C-e */
case '\007': /* C-g */
case '\010': /* C-h */
case 'a'|KEYC_CTRL:
case 'c'|KEYC_CTRL:
case 'e'|KEYC_CTRL:
case 'g'|KEYC_CTRL:
case 'h'|KEYC_CTRL:
case '\011': /* Tab */
case '\013': /* C-k */
case '\016': /* C-n */
case '\020': /* C-p */
case '\024': /* C-t */
case '\025': /* C-u */
case '\027': /* C-w */
case '\031': /* C-y */
case 'k'|KEYC_CTRL:
case 'n'|KEYC_CTRL:
case 'p'|KEYC_CTRL:
case 't'|KEYC_CTRL:
case 'u'|KEYC_CTRL:
case 'w'|KEYC_CTRL:
case 'y'|KEYC_CTRL:
case '\n':
case '\r':
case KEYC_LEFT|KEYC_CTRL:
@ -890,7 +890,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
case 'S':
c->prompt_mode = PROMPT_ENTRY;
c->flags |= CLIENT_REDRAWSTATUS;
*new_key = '\025'; /* C-u */
*new_key = 'u'|KEYC_CTRL;
return (1);
case 'i':
case '\033': /* Escape */
@ -911,7 +911,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
return (1);
case 'C':
case 'D':
*new_key = '\013'; /* C-k */
*new_key = 'k'|KEYC_CTRL;
return (1);
case KEYC_BSPACE:
case 'X':
@ -924,7 +924,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
*new_key = 'B'|KEYC_VI;
return (1);
case 'd':
*new_key = '\025'; /* C-u */
*new_key = 'u'|KEYC_CTRL;
return (1);
case 'e':
*new_key = 'e'|KEYC_VI;
@ -939,10 +939,10 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
*new_key = 'W'|KEYC_VI;
return (1);
case 'p':
*new_key = '\031'; /* C-y */
*new_key = 'y'|KEYC_CTRL;
return (1);
case 'q':
*new_key = '\003'; /* C-c */
*new_key = 'c'|KEYC_CTRL;
return (1);
case 's':
case KEYC_DC:
@ -966,8 +966,8 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
case 'k':
*new_key = KEYC_UP;
return (1);
case '\010' /* C-h */:
case '\003' /* C-c */:
case 'h'|KEYC_CTRL:
case 'c'|KEYC_CTRL:
case '\n':
case '\r':
return (1);
@ -1263,28 +1263,28 @@ status_prompt_key(struct client *c, key_code key)
process_key:
switch (key) {
case KEYC_LEFT:
case '\002': /* C-b */
case 'b'|KEYC_CTRL:
if (c->prompt_index > 0) {
c->prompt_index--;
break;
}
break;
case KEYC_RIGHT:
case '\006': /* C-f */
case 'f'|KEYC_CTRL:
if (c->prompt_index < size) {
c->prompt_index++;
break;
}
break;
case KEYC_HOME:
case '\001': /* C-a */
case 'a'|KEYC_CTRL:
if (c->prompt_index != 0) {
c->prompt_index = 0;
break;
}
break;
case KEYC_END:
case '\005': /* C-e */
case 'e'|KEYC_CTRL:
if (c->prompt_index != size) {
c->prompt_index = size;
break;
@ -1295,7 +1295,7 @@ process_key:
goto changed;
break;
case KEYC_BSPACE:
case '\010': /* C-h */
case 'h'|KEYC_CTRL:
if (c->prompt_index != 0) {
if (c->prompt_index == size)
c->prompt_buffer[--c->prompt_index].size = 0;
@ -1310,7 +1310,7 @@ process_key:
}
break;
case KEYC_DC:
case '\004': /* C-d */
case 'd'|KEYC_CTRL:
if (c->prompt_index != size) {
memmove(c->prompt_buffer + c->prompt_index,
c->prompt_buffer + c->prompt_index + 1,
@ -1319,17 +1319,17 @@ process_key:
goto changed;
}
break;
case '\025': /* C-u */
case 'u'|KEYC_CTRL:
c->prompt_buffer[0].size = 0;
c->prompt_index = 0;
goto changed;
case '\013': /* C-k */
case 'k'|KEYC_CTRL:
if (c->prompt_index < size) {
c->prompt_buffer[c->prompt_index].size = 0;
goto changed;
}
break;
case '\027': /* C-w */
case 'w'|KEYC_CTRL:
separators = options_get_string(oo, "word-separators");
idx = c->prompt_index;
@ -1397,7 +1397,7 @@ process_key:
status_prompt_backward_word(c, separators);
goto changed;
case KEYC_UP:
case '\020': /* C-p */
case 'p'|KEYC_CTRL:
histstr = status_prompt_up_history(c->prompt_hindex,
c->prompt_type);
if (histstr == NULL)
@ -1407,7 +1407,7 @@ process_key:
c->prompt_index = utf8_strlen(c->prompt_buffer);
goto changed;
case KEYC_DOWN:
case '\016': /* C-n */
case 'n'|KEYC_CTRL:
histstr = status_prompt_down_history(c->prompt_hindex,
c->prompt_type);
if (histstr == NULL)
@ -1416,11 +1416,11 @@ process_key:
c->prompt_buffer = utf8_fromcstr(histstr);
c->prompt_index = utf8_strlen(c->prompt_buffer);
goto changed;
case '\031': /* C-y */
case 'y'|KEYC_CTRL:
if (status_prompt_paste(c))
goto changed;
break;
case '\024': /* C-t */
case 't'|KEYC_CTRL:
idx = c->prompt_index;
if (idx < size)
idx++;
@ -1443,12 +1443,12 @@ process_key:
free(s);
break;
case '\033': /* Escape */
case '\003': /* C-c */
case '\007': /* C-g */
case 'c'|KEYC_CTRL:
case 'g'|KEYC_CTRL:
if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0)
status_prompt_clear(c);
break;
case '\022': /* C-r */
case 'r'|KEYC_CTRL:
if (~c->prompt_flags & PROMPT_INCREMENTAL)
break;
if (c->prompt_buffer[0].size == 0) {
@ -1459,7 +1459,7 @@ process_key:
} else
prefix = '-';
goto changed;
case '\023': /* C-s */
case 's'|KEYC_CTRL:
if (~c->prompt_flags & PROMPT_INCREMENTAL)
break;
if (c->prompt_buffer[0].size == 0) {

66
tmux.1
View File

@ -3733,6 +3733,10 @@ Note that aliases are expanded when a command is parsed rather than when it is
executed, so binding an alias with
.Ic bind-key
will bind the expanded form.
.It Ic copy-command Ar shell-command
Give the command to pipe to if the
.Ic copy-pipe
copy mode command is used without arguments.
.It Ic default-terminal Ar terminal
Set the default terminal for new windows created in this session - the
default value of the
@ -3746,10 +3750,6 @@ be set to
.Ql screen ,
.Ql tmux
or a derivative of them.
.It Ic copy-command Ar shell-command
Give the command to pipe to if the
.Ic copy-pipe
copy mode command is used without arguments.
.It Ic escape-time Ar time
Set the time in milliseconds for which
.Nm
@ -3771,22 +3771,53 @@ If enabled, the server will exit when there are no attached clients.
.It Xo Ic extended-keys
.Op Ic on | off | always
.Xc
When
.Ic on
or
.Ic always ,
the escape sequence to enable extended keys is sent to the terminal, if
.Nm
knows that it is supported.
.Nm
always recognises extended keys itself.
If this option is
Controls how modified keys (keys pressed together with Control, Meta, or Shift)
are reported.
This is the equivalent of the
.Ic modifyOtherKeys
.Xr xterm 1
resource.
.Pp
When set to
.Ic on ,
.Nm
will only forward extended keys to applications when they request them; if
the program inside the pane can request one of two modes: mode 1 which changes
the sequence for only keys which lack an existing well-known representation; or
mode 2 which changes the sequence for all keys.
When set to
.Ic always ,
mode 1 output is forced and the program cannot change it.
When set to
.Ic off ,
this feature is disabled and only standard keys are reported.
.Pp
.Nm
will always forward the keys.
will always request extended keys itself if the terminal supports them.
See also the
.Ic extkeys
feature for the
.Ic terminal-features
option, the
.Ic extended-keys-format
option and the
.Ic pane_key_mode
variable.
.It Xo Ic extended-keys-format
.Op Ic csi-u | xterm
.Xc
Selects one of the two possible formats for reporting modified keys to
applications.
This is the equivalent of the
.Ic formatOtherKeys
.Xr xterm 1
resource.
For example, C-S-a will be reported as
.Ql ^[[27;6;65~
when set to
.Ic xterm ,
and as
.Ql ^[[65;6u
when set to
.Ic csi-u .
.It Xo Ic focus-events
.Op Ic on | off
.Xc
@ -5512,6 +5543,7 @@ The following variables are available, where appropriate:
.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode"
.It Li "pane_index" Ta "#P" Ta "Index of pane"
.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled"
.It Li "pane_key_mode" Ta "" Ta "Extended key reporting mode in this pane"
.It Li "pane_last" Ta "" Ta "1 if last pane"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"

43
tmux.h
View File

@ -138,8 +138,7 @@ struct winlink;
#define KEYC_IMPLIED_META 0x08000000000000ULL
#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL
#define KEYC_VI 0x20000000000000ULL
#define KEYC_EXTENDED 0x40000000000000ULL
#define KEYC_SENT 0x80000000000000ULL
#define KEYC_SENT 0x40000000000000ULL
/* Masks for key bits. */
#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL
@ -187,6 +186,42 @@ struct winlink;
*/
typedef unsigned long long key_code;
/* C0 control characters */
enum {
C0_NUL,
C0_SOH,
C0_STX,
C0_ETX,
C0_EOT,
C0_ENQ,
C0_ASC,
C0_BEL,
C0_BS,
C0_HT,
C0_LF,
C0_VT,
C0_FF,
C0_CR,
C0_SO,
C0_SI,
C0_DLE,
C0_DC1,
C0_DC2,
C0_DC3,
C0_DC4,
C0_NAK,
C0_SYN,
C0_ETB,
C0_CAN,
C0_EM,
C0_SUB,
C0_ESC,
C0_FS,
C0_GS,
C0_RS,
C0_US
};
/* Special key codes. */
enum {
/* Focus events. */
@ -582,14 +617,16 @@ enum tty_code_code {
#define MODE_MOUSE_ALL 0x1000
#define MODE_ORIGIN 0x2000
#define MODE_CRLF 0x4000
#define MODE_KEXTENDED 0x8000
#define MODE_KEYS_EXTENDED 0x8000
#define MODE_CURSOR_VERY_VISIBLE 0x10000
#define MODE_CURSOR_BLINKING_SET 0x20000
#define MODE_KEYS_EXTENDED_2 0x40000
#define ALL_MODES 0xffffff
#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
#define MOTION_MOUSE_MODES (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
#define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)
#define EXTENDED_KEY_MODES (MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2)
/* Mouse protocol constants. */
#define MOUSE_PARAM_MAX 0xff

View File

@ -227,7 +227,7 @@ static const struct tty_feature tty_feature_sync = {
/* Terminal supports extended keys. */
static const char *const tty_feature_extkeys_capabilities[] = {
"Eneks=\\E[>4;1m",
"Eneks=\\E[>4;2m",
"Dseks=\\E[>4m",
NULL
};

View File

@ -664,7 +664,7 @@ tty_keys_next(struct tty *tty)
size_t len, size;
cc_t bspace;
int delay, expired = 0, n;
key_code key;
key_code key, onlykey;
struct mouse_event m = { 0 };
struct key_event *event;
@ -801,6 +801,26 @@ first_key:
key = (u_char)buf[0];
size = 1;
}
/* C-Space is special. */
if ((key & KEYC_MASK_KEY) == C0_NUL)
key = ' ' | KEYC_CTRL | (key & KEYC_META);
/*
* Fix up all C0 control codes that don't have a dedicated key into
* corresponding Ctrl keys. Convert characters in the A-Z range into
* lowercase, so ^A becomes a|CTRL.
*/
onlykey = key & KEYC_MASK_KEY;
if (onlykey < 0x20 && onlykey != C0_BS &&
onlykey != C0_HT && onlykey != C0_CR &&
onlykey != C0_ESC) {
onlykey |= 0x40;
if (onlykey >= 'A' && onlykey <= 'Z')
onlykey |= 0x20;
key = onlykey | KEYC_CTRL | (key & KEYC_META);
}
goto complete_key;
partial_key:
@ -910,7 +930,6 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
char tmp[64];
cc_t bspace;
key_code nkey;
key_code onlykey;
struct utf8_data ud;
utf8_char uc;
@ -974,7 +993,13 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
/* Update the modifiers. */
if (modifiers > 0) {
modifiers--;
if (modifiers & 1)
/*
* The Shift modifier may not be reported in some input modes,
* which is unfortunate, as in general case determining if a
* character is shifted or not requires knowing the input
* keyboard layout. So we only fix up the trivial case.
*/
if (modifiers & 1 || (nkey >= 'A' && nkey <= 'Z'))
nkey |= KEYC_SHIFT;
if (modifiers & 2)
nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */
@ -984,34 +1009,15 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */
}
/*
* Don't allow both KEYC_CTRL and as an implied modifier. Also convert
* C-X into C-x and so on.
*/
if (nkey & KEYC_CTRL) {
onlykey = (nkey & KEYC_MASK_KEY);
if (onlykey < 32 &&
onlykey != 9 &&
onlykey != 13 &&
onlykey != 27)
/* nothing */;
else if (onlykey >= 97 && onlykey <= 122)
onlykey -= 96;
else if (onlykey >= 64 && onlykey <= 95)
onlykey -= 64;
else if (onlykey == 32)
onlykey = 0;
else if (onlykey == 63)
onlykey = 127;
else
onlykey |= KEYC_CTRL;
nkey = onlykey|((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL);
}
/* Convert S-Tab into Backtab. */
if ((nkey & KEYC_MASK_KEY) == '\011' && (nkey & KEYC_SHIFT))
nkey = KEYC_BTAB | (nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT);
if (log_get_level() != 0) {
log_debug("%s: extended key %.*s is %llx (%s)", c->name,
(int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
}
*key = nkey;
return (0);
}