Revamp extended keys support to more closely match xterm and support
authornicm <nicm@openbsd.org>
Wed, 21 Aug 2024 04:17:09 +0000 (04:17 +0000)
committernicm <nicm@openbsd.org>
Wed, 21 Aug 2024 04:17:09 +0000 (04:17 +0000)
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.

15 files changed:
usr.bin/tmux/format.c
usr.bin/tmux/input-keys.c
usr.bin/tmux/input.c
usr.bin/tmux/key-string.c
usr.bin/tmux/menu.c
usr.bin/tmux/mode-tree.c
usr.bin/tmux/options-table.c
usr.bin/tmux/popup.c
usr.bin/tmux/screen-write.c
usr.bin/tmux/screen.c
usr.bin/tmux/status.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/tty-features.c
usr.bin/tmux/tty-keys.c

index 2377697..aa17e92 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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
        },
index 3dbf465..7a83b16 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -308,20 +308,6 @@ static struct input_key_entry input_key_defaults[] = {
        { .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,
@@ -427,14 +413,163 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
        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))
@@ -455,36 +590,45 @@ input_key(struct screen *s, struct bufferevent *bev, key_code 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))
@@ -493,10 +637,9 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
                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);
@@ -506,78 +649,34 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
                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. */
index 00268e8..eb23e89 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -1408,17 +1408,29 @@ input_csi_dispatch(struct input_ctx *ictx)
        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);
index 9b22d2e..d9ba95a 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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);
index 79ad009..10ad664 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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);
        }
index 8c62602..41c2201 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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,
index f1e8560..dd1782b 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -94,6 +94,9 @@ static const char *options_table_detach_on_destroy_list[] = {
 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
 };
@@ -310,11 +313,19 @@ const struct options_table_entry options_table[] = {
          .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,
@@ -614,7 +625,7 @@ const struct options_table_entry options_table[] = {
        { .name = "prefix",
          .type = OPTIONS_TABLE_KEY,
          .scope = OPTIONS_TABLE_SESSION,
-         .default_num = '\002',
+         .default_num = 'b'|KEYC_CTRL,
          .text = "The prefix key."
        },
 
index 8f543f8..81d749d 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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)) {
index 350ff67..17dfb1d 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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);
index ff73a8c..182d6a8 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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);
 }
index 5c75db3..f85937f 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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) {
index 11ac32c..983383c 100644 (file)
@@ -1,4 +1,4 @@
-.\" $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>
 .\"
@@ -14,7 +14,7 @@
 .\" 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
@@ -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"
index a1ffc50..7c6f7c7 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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
index 6e75ebc..3d5529f 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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
 };
index 67b7f58..f859747 100644 (file)
@@ -1,4 +1,4 @@
-/* $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>
@@ -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);
 }