Make Kitty keyboard negotiation per client

This commit is contained in:
Michael Grant
2026-09-15 05:45:58 +01:00
parent 75cb580e36
commit 10ea823da6
6 changed files with 149 additions and 9 deletions

View File

@@ -419,9 +419,8 @@ input_key_client_supports_extended(struct client *c, key_code key)
return (1);
if (options_get_number(global_options, "extended-keys") == 0)
return (0);
if (options_get_number(global_options, "extended-keys-format") ==
EXTENDED_KEYS_KITTY)
return ((c->tty.flags & TTY_KKBPUSHED) != 0);
if (c->tty.flags & TTY_KKBPUSHED)
return (1);
return (tty_term_has(c->tty.term, TTYC_ENEKS));
}

29
input.c
View File

@@ -162,6 +162,7 @@ static void input_set_state(struct input_ctx *,
const struct input_transition *);
static void input_reset_cell(struct input_ctx *);
static void input_report_current_theme(struct input_ctx *);
static int input_pane_supports_extended(struct window_pane *);
static void input_osc_4(struct input_ctx *, const char *);
static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_9(struct input_ctx *, const char *);
@@ -1179,6 +1180,29 @@ input_send_reply(struct input_ctx *ictx, const char *reply)
}
}
/* Return whether attached tty clients can provide extended keys to a pane. */
static int
input_pane_supports_extended(struct window_pane *wp)
{
struct client *c;
if (wp == NULL)
return (1);
TAILQ_FOREACH(c, &clients, entry) {
if ((c->flags & (CLIENT_TERMINAL|CLIENT_ATTACHED)) !=
(CLIENT_TERMINAL|CLIENT_ATTACHED))
continue;
if (c->flags &
(CLIENT_UNATTACHEDFLAGS|CLIENT_CONTROL|CLIENT_READONLY))
continue;
if (c->session == NULL || !session_has(c->session, wp->window))
continue;
if (!input_key_client_supports_extended(c, 0))
return (0);
}
return (1);
}
/* Reply to terminal query. */
static void printflike(3, 4)
input_reply(struct input_ctx *ictx, int add, const char *fmt, ...)
@@ -1576,7 +1600,10 @@ input_csi_dispatch(struct input_ctx *ictx)
options_get_number(global_options, "extended-keys-format") !=
EXTENDED_KEYS_KITTY)
break;
input_reply(ictx, 1, "\033[?%uu", s->kitty_keys.flags);
n = s->kitty_keys.flags;
if (!input_pane_supports_extended(ictx->wp))
n = 0;
input_reply(ictx, 1, "\033[?%uu", n);
break;
case INPUT_CSI_KITTY_SET:
if (options_get_number(global_options, "extended-keys") == 0 ||

103
regress/kitty-keys-clients.sh Executable file
View File

@@ -0,0 +1,103 @@
#!/bin/sh
# Test per-client keyboard protocol negotiation and pane-side fallback.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
KCONF=$(mktemp)
UCONF=$(mktemp)
LCONF=$(mktemp)
OUT=$(mktemp)
TMP=$(mktemp)
SOCKETS="testKmc$$ testKka$$ testKua$$ testKrt$$ testKru$$ testKlq$$ testKlu$$"
printf '%s\n' 'set -g extended-keys on' \
'set -g extended-keys-format kitty' >"$KCONF"
printf '%s\n' 'set -g extended-keys on' \
'set -g extended-keys-format csi-u' >"$UCONF"
printf '%s\n' 'set -g extended-keys off' >"$LCONF"
cleanup()
{
rm -f "$KCONF" "$UCONF" "$LCONF" "$OUT" "$TMP"
for socket in $SOCKETS; do
$TEST_TMUX -L"$socket" kill-server 2>/dev/null
done
}
trap cleanup 0 1 15
wait_for_mode()
{
tmux=$1
wanted=$2
i=0
while [ "$($tmux display-message -pt: '#{pane_key_mode}')" != "$wanted" ] &&
[ "$i" -lt 50 ]; do
sleep 0.1
i=$((i + 1))
done
[ "$($tmux display-message -pt: '#{pane_key_mode}')" = "$wanted" ]
}
wait_for_output()
{
i=0
while [ ! -s "$1" ] && [ "$i" -lt 50 ]; do
sleep 0.1
i=$((i + 1))
done
[ -s "$1" ]
}
# A server preferring Kitty must negotiate each client independently.
M="$TEST_TMUX -LtestKmc$$ -f$KCONF"
KA="$TEST_TMUX -LtestKka$$ -f$KCONF"
UA="$TEST_TMUX -LtestKua$$ -f$UCONF"
$M new-session -d -x80 -y24 -s kitty || exit 1
$M new-session -d -x80 -y24 -s csiu || exit 1
$KA new-session -d -x80 -y24 "$M attach-session -t kitty" || exit 1
$UA new-session -d -x80 -y24 "$M attach-session -t csiu" || exit 1
wait_for_mode "$KA" 'Kitty 1' || exit 1
wait_for_mode "$UA" 'Ext 2' || exit 1
uc=$($M list-clients -F '#{client_name} #{client_session}' |
awk '$2 == "csiu" { print $1 }')
[ -n "$uc" ] || exit 1
$M command-prompt -t"$uc" -k 'display-message -pl "%%"' >"$TMP" &
pid=$!
sleep 0.2
$UA send-keys 'Escape [97;5u'
wait "$pid"
[ "$(tr -d '[:space:]' <"$TMP")" = 'C-a' ] || exit 1
$KA kill-server 2>/dev/null
$UA kill-server 2>/dev/null
$M kill-server 2>/dev/null
# Standard extended input must be translated for a Kitty-requesting pane.
: >"$OUT"
R="$TEST_TMUX -LtestKrt$$ -f$KCONF"
RU="$TEST_TMUX -LtestKru$$ -f$UCONF"
$R new-session -d -x80 -y24 \
"stty raw -echo; printf '\033[>1u'; dd bs=1 count=7 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5" || exit 1
$RU new-session -d -x80 -y24 "$R attach-session" || exit 1
wait_for_mode "$RU" 'Ext 2' || exit 1
$RU send-keys C-a
wait_for_output "$OUT" || exit 1
[ "$(tr -d ' \n' <"$OUT")" = '1b5b39373b3575' ] || exit 1
$RU kill-server 2>/dev/null
$R kill-server 2>/dev/null
# Do not report Kitty active when an attached client only supports VT10x.
: >"$OUT"
printf '%s\n' "set -as terminal-overrides ',*:Eneks@:Dseks@'" >>"$KCONF"
LQ="$TEST_TMUX -LtestKlq$$ -f$KCONF"
LU="$TEST_TMUX -LtestKlu$$ -f$LCONF"
$LQ new-session -d -x80 -y24 \
"stty raw -echo; sleep 2; printf '\033[>1u\033[?u'; dd bs=1 count=5 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5" || exit 1
$LU new-session -d -x80 -y24 "$LQ attach-session" || exit 1
wait_for_output "$OUT" || exit 1
[ "$(tr -d ' \n' <"$OUT")" = '1b5b3f3075' ] || exit 1
exit 0

7
tmux.1
View File

@@ -4935,6 +4935,13 @@ this feature is disabled and only standard keys are reported.
.Pp
.Nm
will always request extended keys itself if the terminal supports them.
When the
.Ic kitty
format is selected, it is preferred separately for each client; clients which
do not support it use standard extended keys if available, or standard VT10x
keys otherwise.
Input from standard extended-key clients is translated to Kitty encoding for
applications which have requested it.
See also the
.Ic extkeys
feature for the

View File

@@ -1112,8 +1112,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
utf8_char uc;
*size = 0;
if (options_get_number(global_options, "extended-keys-format") ==
EXTENDED_KEYS_KITTY)
if (tty->flags & TTY_KKBPUSHED)
return (-1);
/* First two bytes are always \033[. */

11
tty.c
View File

@@ -562,16 +562,21 @@ void
tty_update_features(struct tty *tty)
{
struct client *c = tty->client;
int extended_keys, format;
if (tty_apply_features(tty->term))
tty_term_apply_overrides(tty->term);
if (tty_use_margin(tty))
tty_putcode(tty, TTYC_ENMG);
if (options_get_number(global_options, "extended-keys-format") ==
EXTENDED_KEYS_KITTY)
extended_keys = options_get_number(global_options, "extended-keys");
format = options_get_number(global_options, "extended-keys-format");
if (extended_keys == 0 ||
(format == EXTENDED_KEYS_KITTY &&
(tty->flags & (TTY_HAVEKKB|TTY_KKBSUPPORT)) ==
(TTY_HAVEKKB|TTY_KKBSUPPORT)))
tty_puts(tty, tty_term_string(tty->term, TTYC_DSEKS));
else if (options_get_number(global_options, "extended-keys"))
else
tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
tty_update_kitty(tty, NULL);
if (options_get_number(global_options, "focus-events"))