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.
-/* $OpenBSD: format.c,v 1.318 2023/09/08 06:52:31 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.319 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
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)
{ "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
},
-/* $OpenBSD: input-keys.c,v 1.94 2023/01/12 18:49:11 nicm Exp $ */
+/* $OpenBSD: input-keys.c,v 1.95 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~"
},
-
- /* Tab and modifiers. */
- { .key = '\011'|KEYC_CTRL,
- .data = "\011"
- },
- { .key = '\011'|KEYC_CTRL|KEYC_EXTENDED,
- .data = "\033[9;5u"
- },
- { .key = '\011'|KEYC_CTRL|KEYC_SHIFT,
- .data = "\033[Z"
- },
- { .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED,
- .data = "\033[1;5Z"
- }
};
static const key_code input_key_modifiers[] = {
0,
bufferevent_write(bev, data, size);
}
+/*
+ * Encode and write an extended key escape sequence in one of the two
+ * possible formats, depending on the configured output mode.
+ */
+static int
+input_key_extended(struct bufferevent *bev, key_code key)
+{
+ char tmp[64], modifier;
+ struct utf8_data ud;
+ wchar_t wc;
+
+ switch (key & KEYC_MASK_MODIFIERS) {
+ case KEYC_SHIFT:
+ modifier = '2';
+ break;
+ case KEYC_META:
+ modifier = '3';
+ break;
+ case KEYC_SHIFT|KEYC_META:
+ modifier = '4';
+ break;
+ case KEYC_CTRL:
+ modifier = '5';
+ break;
+ case KEYC_SHIFT|KEYC_CTRL:
+ modifier = '6';
+ break;
+ case KEYC_META|KEYC_CTRL:
+ modifier = '7';
+ break;
+ case KEYC_SHIFT|KEYC_META|KEYC_CTRL:
+ modifier = '8';
+ break;
+ default:
+ return (-1);
+ }
+
+ if (KEYC_IS_UNICODE(key)) {
+ utf8_to_data(key & KEYC_MASK_KEY, &ud);
+ if (utf8_towc(&ud, &wc) == UTF8_DONE)
+ key = wc;
+ else
+ return (-1);
+ } else
+ key &= KEYC_MASK_KEY;
+
+ if (options_get_number(global_options, "extended-keys-format") == 1)
+ xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
+ else
+ xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
+
+ input_key_write(__func__, bev, tmp, strlen(tmp));
+ return (0);
+}
+
+/*
+ * Outputs the key in the "standard" mode. This is by far the most
+ * complicated output mode, with a lot of remapping in order to
+ * emulate quirks of terminals that today can be only found in museums.
+ */
+static int
+input_key_vt10x(struct bufferevent *bev, key_code key)
+{
+ struct utf8_data ud;
+ key_code onlykey;
+ char *p;
+ static const char *standard_map[2] = {
+ "1!9(0)=+;:'\",<.>/-8? 2",
+ "119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
+ };
+
+ log_debug("%s: key in %llx", __func__, key);
+
+ if (key & KEYC_META)
+ input_key_write(__func__, bev, "\033", 1);
+
+ /*
+ * There's no way to report modifiers for unicode keys in standard mode
+ * so lose the modifiers.
+ */
+ if (KEYC_IS_UNICODE(key)) {
+ utf8_to_data(key, &ud);
+ input_key_write(__func__, bev, ud.data, ud.size);
+ return (0);
+ }
+
+ onlykey = key & KEYC_MASK_KEY;
+
+ /* Prevent TAB and RET from being swallowed by C0 remapping logic. */
+ if (onlykey == '\r' || onlykey == '\t')
+ key &= ~KEYC_CTRL;
+
+ /*
+ * Convert keys with Ctrl modifier into corresponding C0 control codes,
+ * with the exception of *some* keys, which are remapped into printable
+ * ASCII characters.
+ *
+ * There is no special handling for Shift modifier, which is pretty
+ * much redundant anyway, as no terminal will send <base key>|SHIFT,
+ * but only <shifted key>|SHIFT.
+ */
+ if (key & KEYC_CTRL) {
+ p = strchr(standard_map[0], onlykey);
+ if (p != NULL)
+ key = standard_map[1][p - standard_map[0]];
+ else if (onlykey >= '3' && onlykey <= '7')
+ key = onlykey - '\030';
+ else if (onlykey >= '@' && onlykey <= '~')
+ key = onlykey & 0x1f;
+ else
+ return (-1);
+ }
+
+ log_debug("%s: key out %llx", __func__, key);
+
+ ud.data[0] = key & 0x7f;
+ input_key_write(__func__, bev, &ud.data[0], 1);
+ return (0);
+}
+
+/* Pick keys that are reported as vt10x keys in modifyOtherKeys=1 mode. */
+static int
+input_key_mode1(struct bufferevent *bev, key_code key)
+{
+ key_code onlykey;
+
+ log_debug("%s: key in %llx", __func__, key);
+
+ /*
+ * As per
+ * https://invisible-island.net/xterm/modified-keys-us-pc105.html.
+ */
+ onlykey = key & KEYC_MASK_KEY;
+ if ((key & (KEYC_META | KEYC_CTRL)) == KEYC_CTRL &&
+ (onlykey == '/' || onlykey == '@' || onlykey == '^' ||
+ (onlykey >= '2' && onlykey <= '8') ||
+ (onlykey >= '@' && onlykey <= '~')))
+ return (input_key_vt10x(bev, key));
+
+ /*
+ * A regular or shifted Unicode key + Meta. In the absence of a
+ * standard to back this, we mimic what iTerm 2 does.
+ */
+ if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META &&
+ KEYC_IS_UNICODE(key))
+ return (input_key_vt10x(bev, key));
+
+ return (-1);
+}
+
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike = NULL;
- key_code justkey, newkey, outkey, modifiers;
+ key_code newkey;
struct utf8_data ud;
- char tmp[64], modifier;
/* Mouse keys need a pane. */
if (KEYC_IS_MOUSE(key))
key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS));
}
+ /* Is this backtab? */
+ if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
+ if (s->mode & EXTENDED_KEY_MODES) {
+ /* When in xterm extended mode, remap into S-Tab. */
+ key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
+ } else {
+ /* Otherwise clear modifiers. */
+ key &= ~KEYC_MASK_MODIFIERS;
+ }
+ }
+
/*
- * If this is a normal 7-bit key, just send it, with a leading escape
- * if necessary. If it is a UTF-8 key, split it and send it.
+ * A trivial case, that is a 7-bit key, excluding C0 control characters
+ * that can't be entered from the keyboard, and no modifiers; or a UTF-8
+ * key and no modifiers.
*/
- justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
- if (justkey <= 0x7f) {
- if (key & KEYC_META)
- input_key_write(__func__, bev, "\033", 1);
- ud.data[0] = justkey;
- input_key_write(__func__, bev, &ud.data[0], 1);
- return (0);
- }
- if (KEYC_IS_UNICODE(justkey)) {
- if (key & KEYC_META)
- input_key_write(__func__, bev, "\033", 1);
- utf8_to_data(justkey, &ud);
- input_key_write(__func__, bev, ud.data, ud.size);
- return (0);
+ if (!(key & ~KEYC_MASK_KEY)) {
+ if (key == C0_BS || key == C0_HT ||
+ key == C0_CR || key == C0_ESC ||
+ (key >= 0x20 && key <= 0x7f)) {
+ ud.data[0] = key;
+ input_key_write(__func__, bev, &ud.data[0], 1);
+ return (0);
+ }
+ if (KEYC_IS_UNICODE(key)) {
+ utf8_to_data(key, &ud);
+ input_key_write(__func__, bev, ud.data, ud.size);
+ return (0);
+ }
}
/*
- * Look up in the tree. If not in application keypad or cursor mode,
- * remove the flags from the key.
+ * Look up the standard VT10x keys in the tree. If not in application
+ * keypad or cursor mode, remove the respective flags from the key.
*/
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
- if (s->mode & MODE_KEXTENDED)
- ike = input_key_get(key|KEYC_EXTENDED);
if (ike == NULL)
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
- if (ike == NULL && (key & KEYC_EXTENDED))
- ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) {
- log_debug("found key 0x%llx: \"%s\"", key, ike->data);
+ log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
+ ike->data);
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
(~s->mode & MODE_BRACKETPASTE))
return (0);
return (0);
}
- /* No builtin key sequence; construct an extended key sequence. */
- if (~s->mode & MODE_KEXTENDED) {
- if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL)
- goto missing;
- justkey = (key & KEYC_MASK_KEY);
- switch (justkey) {
- case ' ':
- case '2':
- key = 0|(key & ~KEYC_MASK_KEY);
- break;
- case '|':
- key = 28|(key & ~KEYC_MASK_KEY);
- break;
- case '6':
- key = 30|(key & ~KEYC_MASK_KEY);
- break;
- case '-':
- case '/':
- key = 31|(key & ~KEYC_MASK_KEY);
- break;
- case '?':
- key = 127|(key & ~KEYC_MASK_KEY);
- break;
- default:
- if (justkey >= 'A' && justkey <= '_')
- key = (justkey - 'A')|(key & ~KEYC_MASK_KEY);
- else if (justkey >= 'a' && justkey <= '~')
- key = (justkey - 96)|(key & ~KEYC_MASK_KEY);
- else
- return (0);
- break;
- }
- return (input_key(s, bev, key & ~KEYC_CTRL));
- }
- outkey = (key & KEYC_MASK_KEY);
- modifiers = (key & KEYC_MASK_MODIFIERS);
- if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) {
- outkey = 64 + outkey;
- modifiers |= KEYC_CTRL;
- }
- switch (modifiers) {
- case KEYC_SHIFT:
- modifier = '2';
- break;
- case KEYC_META:
- modifier = '3';
- break;
- case KEYC_SHIFT|KEYC_META:
- modifier = '4';
- break;
- case KEYC_CTRL:
- modifier = '5';
- break;
- case KEYC_SHIFT|KEYC_CTRL:
- modifier = '6';
- break;
- case KEYC_META|KEYC_CTRL:
- modifier = '7';
- break;
- case KEYC_SHIFT|KEYC_META|KEYC_CTRL:
- modifier = '8';
- break;
+ /*
+ * No builtin key sequence; construct an extended key sequence
+ * depending on the client mode.
+ *
+ * If something invalid reaches here, an invalid output may be
+ * produced. For example Ctrl-Shift-2 is invalid (as there's
+ * no way to enter it). The correct form is Ctrl-Shift-@, at
+ * least in US English keyboard layout.
+ */
+ switch (s->mode & EXTENDED_KEY_MODES) {
+ case MODE_KEYS_EXTENDED_2:
+ /*
+ * The simplest mode to handle - *all* modified keys are
+ * reported in the extended form.
+ */
+ return (input_key_extended(bev, key));
+ case MODE_KEYS_EXTENDED:
+ /*
+ * Some keys are still reported in standard mode, to maintain
+ * compatibility with applications unaware of extended keys.
+ */
+ if (input_key_mode1(bev, key) == -1)
+ return (input_key_extended(bev, key));
+ return (0);
default:
- goto missing;
+ /* The standard mode. */
+ return (input_key_vt10x(bev, key));
}
- xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
- input_key_write(__func__, bev, tmp, strlen(tmp));
- return (0);
-
-missing:
- log_debug("key 0x%llx missing", key);
- return (-1);
}
/* Get mouse event string. */
-/* $OpenBSD: input.c,v 1.226 2024/08/19 08:31:36 nicm Exp $ */
+/* $OpenBSD: input.c,v 1.227 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
case INPUT_CSI_MODSET:
n = input_get(ictx, 0, 0, 0);
m = input_get(ictx, 1, 0, 0);
+ /*
+ * Set the extended key reporting mode as per the client request,
+ * unless "extended-keys always" forces us into mode 1.
+ */
if (options_get_number(global_options, "extended-keys") == 2)
break;
- if (n == 0 || (n == 4 && m == 0))
- screen_write_mode_clear(sctx, MODE_KEXTENDED);
- else if (n == 4 && (m == 1 || m == 2))
- screen_write_mode_set(sctx, MODE_KEXTENDED);
+ screen_write_mode_clear(sctx,
+ MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2);
+ if (n == 4 && m == 1)
+ screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
+ if (n == 4 && m == 2)
+ screen_write_mode_set(sctx, MODE_KEYS_EXTENDED_2);
break;
case INPUT_CSI_MODOFF:
n = input_get(ictx, 0, 0, 0);
- if (n == 4)
- screen_write_mode_clear(sctx, MODE_KEXTENDED);
+ /*
+ * Clear the extended key reporting mode as per the client request,
+ * unless "extended-keys always" forces us into mode 1.
+ */
+ if (n == 4) {
+ screen_write_mode_clear(sctx,
+ MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2);
+ }
break;
case INPUT_CSI_WINOPS:
input_csi_dispatch_winops(ictx);
-/* $OpenBSD: key-string.c,v 1.71 2023/01/16 11:26:14 nicm Exp $ */
+/* $OpenBSD: key-string.c,v 1.72 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
{ "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 },
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;
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);
}
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);
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)
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);
-/* $OpenBSD: menu.c,v 1.52 2023/08/15 07:01:47 nicm Exp $ */
+/* $OpenBSD: menu.c,v 1.53 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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 {
}
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);
}
-/* $OpenBSD: mode-tree.c,v 1.68 2024/08/04 08:53:43 nicm Exp $ */
+/* $OpenBSD: mode-tree.c,v 1.69 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
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;
}
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;
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) ||
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,
-/* $OpenBSD: options-table.c,v 1.175 2024/08/04 09:35:30 nicm Exp $ */
+/* $OpenBSD: options-table.c,v 1.176 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL
};
+static const char *options_table_extended_keys_format_list[] = {
+ "csi-u", "xterm", NULL
+};
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_extended_keys_list,
- .default_num = 0,
+ .default_num = 1,
.text = "Whether to request extended key sequences from terminals "
"that support it."
},
+ { .name = "extended-keys-format",
+ .type = OPTIONS_TABLE_CHOICE,
+ .scope = OPTIONS_TABLE_SERVER,
+ .choices = options_table_extended_keys_format_list,
+ .default_num = 1,
+ .text = "The format of emitted extended key sequences."
+ },
+
{ .name = "focus-events",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SERVER,
{ .name = "prefix",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SESSION,
- .default_num = '\002',
+ .default_num = 'b'|KEYC_CTRL,
.text = "The prefix key."
},
-/* $OpenBSD: popup.c,v 1.53 2024/03/21 11:30:42 nicm Exp $ */
+/* $OpenBSD: popup.c,v 1.54 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
}
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)) {
-/* $OpenBSD: screen-write.c,v 1.225 2024/03/21 12:10:57 nicm Exp $ */
+/* $OpenBSD: screen-write.c,v 1.226 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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);
-/* $OpenBSD: screen.c,v 1.85 2024/03/21 11:26:28 nicm Exp $ */
+/* $OpenBSD: screen.c,v 1.86 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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);
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);
}
-/* $OpenBSD: status.c,v 1.242 2024/05/15 08:39:30 nicm Exp $ */
+/* $OpenBSD: status.c,v 1.243 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
{
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:
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 */
return (1);
case 'C':
case 'D':
- *new_key = '\013'; /* C-k */
+ *new_key = 'k'|KEYC_CTRL;
return (1);
case KEYC_BSPACE:
case 'X':
*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;
*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:
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);
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;
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;
}
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,
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;
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)
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)
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++;
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) {
} 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) {
-.\" $OpenBSD: tmux.1,v 1.947 2024/08/04 09:01:18 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.948 2024/08/21 04:17:09 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: August 4 2024 $
+.Dd $Mdocdate: August 21 2024 $
.Dt TMUX 1
.Os
.Sh NAME
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
.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
.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
.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"
-/* $OpenBSD: tmux.h,v 1.1220 2024/08/04 08:53:43 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1221 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
#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
*/
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. */
#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
-/* $OpenBSD: tty-features.c,v 1.30 2023/11/14 15:38:33 nicm Exp $ */
+/* $OpenBSD: tty-features.c,v 1.31 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
/* Terminal supports extended keys. */
static const char *const tty_feature_extkeys_capabilities[] = {
- "Eneks=\\E[>4;1m",
+ "Eneks=\\E[>4;2m",
"Dseks=\\E[>4m",
NULL
};
-/* $OpenBSD: tty-keys.c,v 1.176 2024/08/19 08:29:16 nicm Exp $ */
+/* $OpenBSD: tty-keys.c,v 1.177 2024/08/21 04:17:09 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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;
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:
char tmp[64];
cc_t bspace;
key_code nkey;
- key_code onlykey;
struct utf8_data ud;
utf8_char uc;
/* 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 */
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);
}