mirror of
https://github.com/tmux/tmux.git
synced 2026-09-25 14:08:23 +00:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17d32ae40d | ||
|
|
e4129d92c2 | ||
|
|
6a5de0d8e3 | ||
|
|
ff7efbe37c | ||
|
|
ff7d45ad05 | ||
|
|
15a7944576 | ||
|
|
de69122034 | ||
|
|
f38558ee12 | ||
|
|
38c7eb7413 | ||
|
|
ad785736e9 | ||
|
|
61687f2a5b | ||
|
|
ed2b349c3e | ||
|
|
92d29e54f4 | ||
|
|
a34d2b6b21 | ||
|
|
550231e83a | ||
|
|
102d5185c1 | ||
|
|
1d9029c8d7 | ||
|
|
a23ff880a2 | ||
|
|
32dc7d5095 | ||
|
|
06ef15955c | ||
|
|
ce9bdf6734 | ||
|
|
757b15f2ca | ||
|
|
fcb4e9760a | ||
|
|
03436d273a | ||
|
|
7fdb1c90b7 | ||
|
|
55012ded8d | ||
|
|
de04ae2c65 | ||
|
|
3b931d9c20 | ||
|
|
6f387eec2b | ||
|
|
80f522f577 |
@@ -72,14 +72,14 @@ jobs:
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
sh autogen.sh
|
||||
sh autogen.sh
|
||||
./configure ${{ matrix.configure }}
|
||||
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
- name: test
|
||||
run: |
|
||||
cd regress
|
||||
${{ matrix.make }}
|
||||
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
- name: logs
|
||||
if: failure()
|
||||
|
||||
@@ -10,8 +10,8 @@ CHANGES FROM 3.7c TO 3.8
|
||||
- modal panes may be created with new-pane -O; a window can have one modal
|
||||
pane and it prevents interaction with the other panes while it is active.
|
||||
-C closes the pane when the mouse is clicked outside it and -K sends all
|
||||
keys, including the prefix key, to the pane. A modal pane is now used for
|
||||
the editor in buffer mode;
|
||||
keys, including the prefix key, to the pane. -D closes the modal pane with
|
||||
Escape or C-c. A modal pane is now used for the editor in buffer mode;
|
||||
|
||||
- new-pane -A creates a floating pane which remains above a zoomed pane;
|
||||
modal panes do this by default;
|
||||
@@ -39,6 +39,9 @@ CHANGES FROM 3.7c TO 3.8
|
||||
The old format is still accepted; control mode clients receive old layouts
|
||||
unless they set the new-layouts flag.
|
||||
|
||||
* Add remain-on-exit failed-key to keep a pane open after a failed command
|
||||
until a key is pressed.
|
||||
|
||||
* Add support for themes and improve default colours:
|
||||
|
||||
- tmux now has builtin light and dark colour themes for terminals with 256 or
|
||||
@@ -111,22 +114,16 @@ CHANGES FROM 3.7c TO 3.8
|
||||
|
||||
* Change show-options and show-hooks to use formats and add a -F flag to each.
|
||||
|
||||
* Add support for the Kitty keyboard protocol. When extended-keys is on,
|
||||
tmux uses it with terminals which support it, detected when a client
|
||||
attaches or set with a new kittykeys terminal feature, and standard
|
||||
extended keys or VT10x keys with other terminals, so terminals with
|
||||
different capabilities may be attached at the same time. Programs inside
|
||||
tmux may ask for either the Kitty protocol or modifyOtherKeys and get keys
|
||||
in the form they asked for. The Kitty protocol can report the Super and
|
||||
Hyper modifiers (key name prefixes s- and H-).
|
||||
|
||||
* Add a client_key_mode format giving the extended key reporting mode in
|
||||
use for a client, alongside the existing pane_key_mode for a pane.
|
||||
|
||||
* Allow individual terminal features to be disabled by adding @ to their names
|
||||
in terminal-features, and add a utf8 feature for terminals which support
|
||||
UTF-8 output.
|
||||
|
||||
* Add an appesc terminal feature for mintty's application escape key mode,
|
||||
avoiding unsupported sequences on other terminals (Pete Dietl, issue 5626).
|
||||
|
||||
* Preserve existing Ss and Se terminfo capabilities for cursor styles and
|
||||
restoring the default cursor (issue 5476).
|
||||
|
||||
* Add a #{A/count:frames} modifier to show a series of frames as an animation
|
||||
in the status line (issue 5412 from Fernando Daciuk).
|
||||
|
||||
@@ -201,10 +198,23 @@ CHANGES FROM 3.7c TO 3.8
|
||||
sent inside %begin/%end (issue 5458), do not let a stuck client prevent the
|
||||
server from exiting (issue 5444), and reset control mode offsets when a pane
|
||||
is respawned (issue 5498). Bound buffered command replies so a control mode
|
||||
client that stops reading cannot consume unlimited memory (issue 5565).
|
||||
client that stops reading cannot consume unlimited memory (issue 5565), and
|
||||
fix a collision between the output discard and bracketed paste flags (issue
|
||||
5622).
|
||||
|
||||
* Fix grouped sessions sometimes being left as unusable command targets while
|
||||
they are being killed (Bryce Miller, issue 5180).
|
||||
they are being killed (Bryce Miller, issue 5180), and prevent reuse of a
|
||||
session with no windows while detaching (Heon Jeong, issue 5620).
|
||||
|
||||
* Free wait-for clients when they disconnect and do not run their remaining
|
||||
queued commands (issue 5614).
|
||||
|
||||
* Expand formats in run-shell -c like other start-directory flags (reported by
|
||||
Saul Nogueras).
|
||||
|
||||
* Fix session alert flag formats to check all windows (issue 5599).
|
||||
|
||||
* Do not clear the marked pane when clicking on a pane border.
|
||||
|
||||
* Fix choose mode filtering when more than one pane exists (issue 5326), and
|
||||
fix an infinite loop in customize mode when a filter does not match.
|
||||
@@ -215,11 +225,18 @@ CHANGES FROM 3.7c TO 3.8
|
||||
status lines, menus, popups, wide characters and floating panes. This
|
||||
includes fixes for flicker with alternate screen applications and scrollbars
|
||||
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
|
||||
5098).
|
||||
5098), and misplaced wrapped lines during partial redraws (Ben Maurer, issue
|
||||
5625).
|
||||
|
||||
* Do not crash looking for the next or previous session (issue 5344), or when
|
||||
no client is available.
|
||||
|
||||
* Fix crashes when destroying a zoomed pane (Romain Francoise, issue 5591),
|
||||
copying from a blank source pane (Kaixuan Li), or opening a mode prompt when
|
||||
the client is offset into the window.
|
||||
|
||||
* Avoid an infinite loop when assigning a width to WCHAR_MAX (issue 5602).
|
||||
|
||||
CHANGES FROM 3.7b TO 3.7c
|
||||
|
||||
* Build with jemalloc on macOS to avoid what appears to be a bug in calloc
|
||||
|
||||
@@ -171,7 +171,6 @@ dist_tmux_SOURCES = \
|
||||
grid.c \
|
||||
hooks.c \
|
||||
hyperlinks.c \
|
||||
input-kitty.c \
|
||||
input-keys.c \
|
||||
input.c \
|
||||
job.c \
|
||||
@@ -213,7 +212,6 @@ dist_tmux_SOURCES = \
|
||||
tty-acs.c \
|
||||
tty-draw.c \
|
||||
tty-features.c \
|
||||
tty-kitty.c \
|
||||
tty-keys.c \
|
||||
tty-term.c \
|
||||
tty.c \
|
||||
|
||||
+14
-5
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.75 2026/09/21 10:33:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||
@@ -133,7 +133,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
} else if (strcmp(position, "back") == 0) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
@@ -142,24 +142,30 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
} else if (strcmp(position, "forward") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||
if (owp != NULL) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "backward") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_NEXT(owp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "forward-loop") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else {
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
@@ -169,6 +175,8 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
}
|
||||
} else if (strcmp(position, "backward-loop") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_NEXT(owp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
@@ -347,8 +355,10 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
|
||||
|
||||
n = 0;
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
break;
|
||||
if (owp->layout_cell == NULL)
|
||||
continue;
|
||||
if (n >= z)
|
||||
break;
|
||||
n++;
|
||||
@@ -443,7 +453,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_unzoom_window(dst_w);
|
||||
if ((s = args_get(args, 'P')) != NULL)
|
||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||
if ((s = args_get(args, 'z')) != NULL)
|
||||
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-queue.c,v 1.124 2026/09/22 06:46:50 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -580,6 +580,9 @@ cmdq_fire_command(struct cmdq_item *item)
|
||||
int flags, quiet = 0;
|
||||
char *tmp;
|
||||
|
||||
if (item->client != NULL && (item->client->flags & CLIENT_DEAD))
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
if (cfg_finished)
|
||||
cmdq_add_message(item);
|
||||
if (log_get_level() > 1) {
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-resize-pane.c,v 1.69 2026/09/21 10:33:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -91,7 +91,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_redraw_window(w);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
server_unzoom_window(w);
|
||||
if (!window_pane_is_floating(wp))
|
||||
server_unzoom_window(w);
|
||||
lc = wp->layout_cell; /* may have been replaced by unzoom */
|
||||
|
||||
if (args_has(args, 'x')) {
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-run-shell.c,v 1.94 2026/08/25 06:04:33 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-run-shell.c,v 1.95 2026/09/20 07:59:55 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||
@@ -164,7 +164,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (cdata->client != NULL)
|
||||
cdata->client->references++;
|
||||
if (args_has(args, 'c'))
|
||||
cdata->cwd = xstrdup(args_get(args, 'c'));
|
||||
cdata->cwd = format_single_from_target(item, args_get(args, 'c'));
|
||||
else
|
||||
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
|
||||
|
||||
|
||||
+35
-1
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-wait-for.c,v 1.24 2026/09/22 06:46:50 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -480,6 +480,40 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
cmd_wait_for_client_lost(struct client *c)
|
||||
{
|
||||
struct wait_channel *wc, *wc1;
|
||||
struct wait_item *wi, *wi1;
|
||||
struct wait_event_item *wei, *wei1;
|
||||
|
||||
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
|
||||
if (cmdq_get_client(wei->item) == c) {
|
||||
TAILQ_REMOVE(&wait_event_items, wei, entry);
|
||||
cmdq_continue(wei->item);
|
||||
cmd_wait_for_event_free(wei);
|
||||
}
|
||||
}
|
||||
|
||||
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
|
||||
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
|
||||
if (cmdq_get_client(wi->item) == c) {
|
||||
cmdq_continue(wi->item);
|
||||
TAILQ_REMOVE(&wc->waiters, wi, entry);
|
||||
free(wi);
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
|
||||
if (cmdq_get_client(wi->item) == c) {
|
||||
cmdq_continue(wi->item);
|
||||
TAILQ_REMOVE(&wc->lockers, wi, entry);
|
||||
free(wi);
|
||||
}
|
||||
}
|
||||
cmd_wait_for_remove_empty(wc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cmd_wait_for_flush(void)
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# configure.ac
|
||||
|
||||
AC_INIT([tmux], next-3.9)
|
||||
AC_INIT([tmux], 3.8-rc2)
|
||||
AC_PREREQ([2.60])
|
||||
|
||||
AC_CONFIG_AUX_DIR(etc)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: format.c,v 1.417 2026/09/08 15:42:26 nicm Exp $ */
|
||||
/* $OpenBSD: format.c,v 1.418 2026/09/20 08:19:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1617,41 +1617,6 @@ format_cb_client_height(struct format_tree *ft)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Callback for client_key_mode. */
|
||||
static void *
|
||||
format_cb_client_key_mode(struct format_tree *ft)
|
||||
{
|
||||
struct client *c = ft->c;
|
||||
const char *mode;
|
||||
char *s;
|
||||
int kitty, eks;
|
||||
|
||||
if (c == NULL || (~c->tty.flags & TTY_STARTED))
|
||||
return (NULL);
|
||||
|
||||
if (c->tty.flags & TTY_KKBPUSHED) {
|
||||
xasprintf(&s, "Kitty %u", c->tty.kitty_keys);
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* This mirrors the choice made in tty_update_features(). */
|
||||
kitty = ((c->tty.term->flags & TERM_KITTYKEYS) != 0);
|
||||
eks = (options_get_number(global_options, "extended-keys") != 0);
|
||||
if (kitty)
|
||||
eks = 0;
|
||||
if (eks && tty_term_has(c->tty.term, TTYC_ENEKS))
|
||||
mode = "Ext";
|
||||
else
|
||||
mode = "VT10x";
|
||||
|
||||
/* The terminal supports Kitty keys but they are not in use. */
|
||||
if (kitty) {
|
||||
xasprintf(&s, "%s (Kitty)", mode);
|
||||
return (s);
|
||||
}
|
||||
return (xstrdup(mode));
|
||||
}
|
||||
|
||||
/* Callback for client_key_table. */
|
||||
static void *
|
||||
format_cb_client_key_table(struct format_tree *ft)
|
||||
@@ -2522,14 +2487,7 @@ format_cb_pane_unseen_changes(struct format_tree *ft)
|
||||
static void *
|
||||
format_cb_pane_key_mode(struct format_tree *ft)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (ft->wp != NULL && ft->wp->screen != NULL) {
|
||||
if (ft->wp->screen->kitty_keys.flags != 0) {
|
||||
xasprintf(&s, "Kitty %u",
|
||||
ft->wp->screen->kitty_keys.flags);
|
||||
return (s);
|
||||
}
|
||||
switch (ft->wp->screen->mode & EXTENDED_KEY_MODES) {
|
||||
case MODE_KEYS_EXTENDED:
|
||||
return (xstrdup("Ext 1"));
|
||||
@@ -2918,10 +2876,10 @@ format_cb_session_activity_flag(struct format_tree *ft)
|
||||
|
||||
if (ft->s != NULL) {
|
||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||
if (ft->wl->flags & WINLINK_ACTIVITY)
|
||||
if (wl->flags & WINLINK_ACTIVITY)
|
||||
return (xstrdup("1"));
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
@@ -2936,8 +2894,8 @@ format_cb_session_bell_flag(struct format_tree *ft)
|
||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||
if (wl->flags & WINLINK_BELL)
|
||||
return (xstrdup("1"));
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
@@ -2950,10 +2908,10 @@ format_cb_session_silence_flag(struct format_tree *ft)
|
||||
|
||||
if (ft->s != NULL) {
|
||||
RB_FOREACH(wl, winlinks, &ft->s->windows) {
|
||||
if (ft->wl->flags & WINLINK_SILENCE)
|
||||
if (wl->flags & WINLINK_SILENCE)
|
||||
return (xstrdup("1"));
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (xstrdup("0"));
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
@@ -3684,9 +3642,6 @@ static const struct format_table_entry format_table[] = {
|
||||
{ "client_height", FORMAT_TABLE_STRING,
|
||||
format_cb_client_height
|
||||
},
|
||||
{ "client_key_mode", FORMAT_TABLE_STRING,
|
||||
format_cb_client_key_mode
|
||||
},
|
||||
{ "client_key_table", FORMAT_TABLE_STRING,
|
||||
format_cb_client_key_table
|
||||
},
|
||||
|
||||
+1
-10
@@ -464,8 +464,7 @@ input_key_extended(struct bufferevent *bev, key_code key)
|
||||
} else
|
||||
key &= KEYC_MASK_KEY;
|
||||
|
||||
if (options_get_number(global_options, "extended-keys-format") ==
|
||||
EXTENDED_KEYS_XTERM)
|
||||
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);
|
||||
@@ -589,10 +588,6 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Kitty keys take precedence if the application asked for them. */
|
||||
if (input_key_kitty(s, bev, key) == 0)
|
||||
return (0);
|
||||
|
||||
/* Is this backspace? */
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
|
||||
newkey = options_get_number(global_options, "backspace");
|
||||
@@ -682,10 +677,6 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
log_debug("%s: ignoring key 0x%llx", __func__, key);
|
||||
return (0);
|
||||
}
|
||||
if (key & (KEYC_SUPER|KEYC_HYPER))
|
||||
return (input_key_vt10x(bev, key));
|
||||
if (s->kitty_keys.flags & KITTY_KEY_SUPPORTED)
|
||||
return (input_key_vt10x(bev, key));
|
||||
|
||||
/*
|
||||
* No builtin key sequence; construct an extended key sequence
|
||||
|
||||
-313
@@ -1,313 +0,0 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Track pane keyboard state and encode keys for applications. */
|
||||
|
||||
struct input_kitty_key {
|
||||
key_code key;
|
||||
u_int number;
|
||||
char final;
|
||||
};
|
||||
|
||||
static const struct input_kitty_key input_kitty_keys[] = {
|
||||
{ C0_ESC, 27, 'u' },
|
||||
{ C0_CR, 13, 'u' },
|
||||
{ C0_HT, 9, 'u' },
|
||||
{ KEYC_BSPACE, 127, 'u' },
|
||||
{ KEYC_IC, 2, '~' },
|
||||
{ KEYC_DC, 3, '~' },
|
||||
{ KEYC_LEFT, 1, 'D' },
|
||||
{ KEYC_RIGHT, 1, 'C' },
|
||||
{ KEYC_UP, 1, 'A' },
|
||||
{ KEYC_DOWN, 1, 'B' },
|
||||
{ KEYC_PPAGE, 5, '~' },
|
||||
{ KEYC_NPAGE, 6, '~' },
|
||||
{ KEYC_HOME, 1, 'H' },
|
||||
{ KEYC_END, 1, 'F' },
|
||||
{ KEYC_F1, 1, 'P' },
|
||||
{ KEYC_F2, 1, 'Q' },
|
||||
{ KEYC_F3, 13, '~' },
|
||||
{ KEYC_F4, 1, 'S' },
|
||||
{ KEYC_F5, 15, '~' },
|
||||
{ KEYC_F6, 17, '~' },
|
||||
{ KEYC_F7, 18, '~' },
|
||||
{ KEYC_F8, 19, '~' },
|
||||
{ KEYC_F9, 20, '~' },
|
||||
{ KEYC_F10, 21, '~' },
|
||||
{ KEYC_F11, 23, '~' },
|
||||
{ KEYC_F12, 24, '~' },
|
||||
{ KEYC_CAPSLOCK, 57358, 'u' },
|
||||
{ KEYC_SCROLLLOCK, 57359, 'u' },
|
||||
{ KEYC_NUMLOCK, 57360, 'u' },
|
||||
{ KEYC_PRINTSCREEN, 57361, 'u' },
|
||||
{ KEYC_PAUSE, 57362, 'u' },
|
||||
{ KEYC_MENU, 57363, 'u' },
|
||||
{ KEYC_F13, 57376, 'u' },
|
||||
{ KEYC_F14, 57377, 'u' },
|
||||
{ KEYC_F15, 57378, 'u' },
|
||||
{ KEYC_F16, 57379, 'u' },
|
||||
{ KEYC_F17, 57380, 'u' },
|
||||
{ KEYC_F18, 57381, 'u' },
|
||||
{ KEYC_F19, 57382, 'u' },
|
||||
{ KEYC_F20, 57383, 'u' },
|
||||
{ KEYC_F21, 57384, 'u' },
|
||||
{ KEYC_F22, 57385, 'u' },
|
||||
{ KEYC_F23, 57386, 'u' },
|
||||
{ KEYC_F24, 57387, 'u' },
|
||||
{ KEYC_F25, 57388, 'u' },
|
||||
{ KEYC_F26, 57389, 'u' },
|
||||
{ KEYC_F27, 57390, 'u' },
|
||||
{ KEYC_F28, 57391, 'u' },
|
||||
{ KEYC_F29, 57392, 'u' },
|
||||
{ KEYC_F30, 57393, 'u' },
|
||||
{ KEYC_F31, 57394, 'u' },
|
||||
{ KEYC_F32, 57395, 'u' },
|
||||
{ KEYC_F33, 57396, 'u' },
|
||||
{ KEYC_F34, 57397, 'u' },
|
||||
{ KEYC_F35, 57398, 'u' },
|
||||
{ KEYC_KP_ZERO|KEYC_KEYPAD, 57399, 'u' },
|
||||
{ KEYC_KP_ONE|KEYC_KEYPAD, 57400, 'u' },
|
||||
{ KEYC_KP_TWO|KEYC_KEYPAD, 57401, 'u' },
|
||||
{ KEYC_KP_THREE|KEYC_KEYPAD, 57402, 'u' },
|
||||
{ KEYC_KP_FOUR|KEYC_KEYPAD, 57403, 'u' },
|
||||
{ KEYC_KP_FIVE|KEYC_KEYPAD, 57404, 'u' },
|
||||
{ KEYC_KP_SIX|KEYC_KEYPAD, 57405, 'u' },
|
||||
{ KEYC_KP_SEVEN|KEYC_KEYPAD, 57406, 'u' },
|
||||
{ KEYC_KP_EIGHT|KEYC_KEYPAD, 57407, 'u' },
|
||||
{ KEYC_KP_NINE|KEYC_KEYPAD, 57408, 'u' },
|
||||
{ KEYC_KP_PERIOD|KEYC_KEYPAD, 57409, 'u' },
|
||||
{ KEYC_KP_SLASH|KEYC_KEYPAD, 57410, 'u' },
|
||||
{ KEYC_KP_STAR|KEYC_KEYPAD, 57411, 'u' },
|
||||
{ KEYC_KP_MINUS|KEYC_KEYPAD, 57412, 'u' },
|
||||
{ KEYC_KP_PLUS|KEYC_KEYPAD, 57413, 'u' },
|
||||
{ KEYC_KP_ENTER|KEYC_KEYPAD, 57414, 'u' },
|
||||
{ KEYC_KP_EQUAL|KEYC_KEYPAD, 57415, 'u' },
|
||||
{ KEYC_KP_SEPARATOR|KEYC_KEYPAD, 57416, 'u' },
|
||||
{ KEYC_KP_LEFT|KEYC_KEYPAD, 57417, 'u' },
|
||||
{ KEYC_KP_RIGHT|KEYC_KEYPAD, 57418, 'u' },
|
||||
{ KEYC_KP_UP|KEYC_KEYPAD, 57419, 'u' },
|
||||
{ KEYC_KP_DOWN|KEYC_KEYPAD, 57420, 'u' },
|
||||
{ KEYC_KP_PPAGE|KEYC_KEYPAD, 57421, 'u' },
|
||||
{ KEYC_KP_NPAGE|KEYC_KEYPAD, 57422, 'u' },
|
||||
{ KEYC_KP_HOME|KEYC_KEYPAD, 57423, 'u' },
|
||||
{ KEYC_KP_END|KEYC_KEYPAD, 57424, 'u' },
|
||||
{ KEYC_KP_IC|KEYC_KEYPAD, 57425, 'u' },
|
||||
{ KEYC_KP_DC|KEYC_KEYPAD, 57426, 'u' },
|
||||
{ KEYC_KP_BEGIN|KEYC_KEYPAD, 57427, '~' },
|
||||
{ KEYC_MEDIA_PLAY, 57428, 'u' },
|
||||
{ KEYC_MEDIA_PAUSE, 57429, 'u' },
|
||||
{ KEYC_MEDIA_PLAYPAUSE, 57430, 'u' },
|
||||
{ KEYC_MEDIA_REVERSE, 57431, 'u' },
|
||||
{ KEYC_MEDIA_STOP, 57432, 'u' },
|
||||
{ KEYC_MEDIA_FASTFORWARD, 57433, 'u' },
|
||||
{ KEYC_MEDIA_REWIND, 57434, 'u' },
|
||||
{ KEYC_MEDIA_NEXT, 57435, 'u' },
|
||||
{ KEYC_MEDIA_PREVIOUS, 57436, 'u' },
|
||||
{ KEYC_MEDIA_RECORD, 57437, 'u' },
|
||||
{ KEYC_VOLUME_DOWN, 57438, 'u' },
|
||||
{ KEYC_VOLUME_UP, 57439, 'u' },
|
||||
{ KEYC_VOLUME_MUTE, 57440, 'u' },
|
||||
{ KEYC_LEFT_SHIFT, 57441, 'u' },
|
||||
{ KEYC_LEFT_CTRL, 57442, 'u' },
|
||||
{ KEYC_LEFT_ALT, 57443, 'u' },
|
||||
{ KEYC_LEFT_SUPER, 57444, 'u' },
|
||||
{ KEYC_LEFT_HYPER, 57445, 'u' },
|
||||
{ KEYC_LEFT_META, 57446, 'u' },
|
||||
{ KEYC_RIGHT_SHIFT, 57447, 'u' },
|
||||
{ KEYC_RIGHT_CTRL, 57448, 'u' },
|
||||
{ KEYC_RIGHT_ALT, 57449, 'u' },
|
||||
{ KEYC_RIGHT_SUPER, 57450, 'u' },
|
||||
{ KEYC_RIGHT_HYPER, 57451, 'u' },
|
||||
{ KEYC_RIGHT_META, 57452, 'u' },
|
||||
{ KEYC_ISO_LEVEL3_SHIFT, 57453, 'u' },
|
||||
{ KEYC_ISO_LEVEL5_SHIFT, 57454, 'u' }
|
||||
};
|
||||
|
||||
void
|
||||
input_kitty_reset(struct screen *s)
|
||||
{
|
||||
memset(&s->kitty_keys, 0, sizeof s->kitty_keys);
|
||||
}
|
||||
|
||||
void
|
||||
input_kitty_alternate_on(struct screen *s)
|
||||
{
|
||||
s->saved_kitty_keys = s->kitty_keys;
|
||||
input_kitty_reset(s);
|
||||
}
|
||||
|
||||
void
|
||||
input_kitty_alternate_off(struct screen *s)
|
||||
{
|
||||
s->kitty_keys = s->saved_kitty_keys;
|
||||
memset(&s->saved_kitty_keys, 0, sizeof s->saved_kitty_keys);
|
||||
}
|
||||
|
||||
void
|
||||
input_kitty_set(struct screen *s, u_int flags, int mode)
|
||||
{
|
||||
flags &= KITTY_KEY_SUPPORTED;
|
||||
if (mode == 2)
|
||||
s->kitty_keys.flags |= flags;
|
||||
else if (mode == 3)
|
||||
s->kitty_keys.flags &= ~flags;
|
||||
else
|
||||
s->kitty_keys.flags = flags;
|
||||
}
|
||||
|
||||
void
|
||||
input_kitty_push(struct screen *s, u_int flags)
|
||||
{
|
||||
s->kitty_keys.saved_flags = s->kitty_keys.flags;
|
||||
s->kitty_keys.have_saved = 1;
|
||||
s->kitty_keys.flags = flags & KITTY_KEY_SUPPORTED;
|
||||
}
|
||||
|
||||
void
|
||||
input_kitty_pop(struct screen *s, u_int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
if (s->kitty_keys.have_saved && count == 1)
|
||||
s->kitty_keys.flags = s->kitty_keys.saved_flags;
|
||||
else
|
||||
s->kitty_keys.flags = 0;
|
||||
s->kitty_keys.have_saved = 0;
|
||||
}
|
||||
|
||||
static u_int
|
||||
input_kitty_modifiers(key_code key)
|
||||
{
|
||||
u_int modifiers = 1;
|
||||
|
||||
if (key & KEYC_SHIFT)
|
||||
modifiers += 1;
|
||||
if (key & KEYC_META)
|
||||
modifiers += 2;
|
||||
if (key & KEYC_CTRL)
|
||||
modifiers += 4;
|
||||
if (key & KEYC_SUPER)
|
||||
modifiers += 8;
|
||||
if (key & KEYC_HYPER)
|
||||
modifiers += 16;
|
||||
return (modifiers);
|
||||
}
|
||||
|
||||
static const struct input_kitty_key *
|
||||
input_kitty_lookup(key_code key)
|
||||
{
|
||||
key_code lookup;
|
||||
u_int i;
|
||||
|
||||
lookup = key & (KEYC_MASK_KEY|KEYC_KEYPAD);
|
||||
for (i = 0; i < nitems(input_kitty_keys); i++) {
|
||||
if (input_kitty_keys[i].key == lookup)
|
||||
return (&input_kitty_keys[i]);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
input_key_kitty(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
{
|
||||
const struct input_kitty_key *ikk;
|
||||
struct utf8_data ud;
|
||||
wchar_t wc;
|
||||
key_code modifiers, onlykey;
|
||||
u_int flags, number, modifier;
|
||||
char tmp[64];
|
||||
size_t size;
|
||||
|
||||
flags = s->kitty_keys.flags & KITTY_KEY_SUPPORTED;
|
||||
if (flags == 0)
|
||||
return (-1);
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_BTAB)
|
||||
key = C0_HT|(key & ~KEYC_MASK_KEY)|KEYC_SHIFT;
|
||||
|
||||
modifiers = key & (KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_SUPER|KEYC_HYPER);
|
||||
ikk = input_kitty_lookup(key);
|
||||
if (ikk != NULL) {
|
||||
if ((flags & KITTY_KEY_REPORT_ALL) == 0) {
|
||||
if (ikk->key == C0_CR || ikk->key == C0_HT ||
|
||||
ikk->key == KEYC_BSPACE)
|
||||
return (-1);
|
||||
if (ikk->final != 'u' &&
|
||||
(modifiers & (KEYC_SUPER|KEYC_HYPER)) == 0)
|
||||
return (-1);
|
||||
}
|
||||
number = ikk->number;
|
||||
modifier = input_kitty_modifiers(key);
|
||||
if (ikk->final == 'u') {
|
||||
if (modifier == 1)
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%uu", number);
|
||||
else
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%u;%uu",
|
||||
number, modifier);
|
||||
} else if (ikk->final == '~') {
|
||||
if (modifier == 1)
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%u~", number);
|
||||
else
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%u;%u~",
|
||||
number, modifier);
|
||||
} else {
|
||||
if (modifier == 1)
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%c",
|
||||
ikk->final);
|
||||
else
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[1;%u%c",
|
||||
modifier, ikk->final);
|
||||
}
|
||||
goto write;
|
||||
}
|
||||
|
||||
if ((flags & KITTY_KEY_REPORT_ALL) == 0 &&
|
||||
(modifiers & (KEYC_META|KEYC_CTRL|KEYC_SUPER|KEYC_HYPER)) == 0)
|
||||
return (-1);
|
||||
|
||||
onlykey = key & KEYC_MASK_KEY;
|
||||
if (KEYC_IS_UNICODE(key)) {
|
||||
utf8_to_data(onlykey, &ud);
|
||||
if (utf8_towc(&ud, &wc) != UTF8_DONE)
|
||||
return (-1);
|
||||
number = wc;
|
||||
} else if (onlykey <= 0x7f)
|
||||
number = onlykey;
|
||||
else
|
||||
return (-1);
|
||||
if ((key & KEYC_SHIFT) && number >= 'A' && number <= 'Z')
|
||||
number += 'a' - 'A';
|
||||
|
||||
modifier = input_kitty_modifiers(key);
|
||||
if (modifier == 1)
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%uu", number);
|
||||
else
|
||||
size = xsnprintf(tmp, sizeof tmp, "\033[%u;%uu", number,
|
||||
modifier);
|
||||
|
||||
write:
|
||||
log_debug("%s: %.*s", __func__, (int)size, tmp);
|
||||
bufferevent_write(bev, tmp, size);
|
||||
return (0);
|
||||
}
|
||||
@@ -279,10 +279,6 @@ enum input_csi_type {
|
||||
INPUT_CSI_HPA,
|
||||
INPUT_CSI_ICH,
|
||||
INPUT_CSI_IL,
|
||||
INPUT_CSI_KITTY_POP,
|
||||
INPUT_CSI_KITTY_PUSH,
|
||||
INPUT_CSI_KITTY_QUERY,
|
||||
INPUT_CSI_KITTY_SET,
|
||||
INPUT_CSI_MODOFF,
|
||||
INPUT_CSI_MODSET,
|
||||
INPUT_CSI_QUERY,
|
||||
@@ -348,11 +344,7 @@ static const struct input_table_entry input_csi_table[] = {
|
||||
{ 'r', "", INPUT_CSI_DECSTBM },
|
||||
{ 's', "", INPUT_CSI_SCP },
|
||||
{ 't', "", INPUT_CSI_WINOPS },
|
||||
{ 'u', "", INPUT_CSI_RCP },
|
||||
{ 'u', "<", INPUT_CSI_KITTY_POP },
|
||||
{ 'u', "=", INPUT_CSI_KITTY_SET },
|
||||
{ 'u', ">", INPUT_CSI_KITTY_PUSH },
|
||||
{ 'u', "?", INPUT_CSI_KITTY_QUERY }
|
||||
{ 'u', "", INPUT_CSI_RCP }
|
||||
};
|
||||
|
||||
/* Input transition. */
|
||||
@@ -1565,33 +1557,6 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
if (options_get_number(global_options, "extended-keys") == 2)
|
||||
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
|
||||
break;
|
||||
case INPUT_CSI_KITTY_QUERY:
|
||||
if (options_get_number(global_options, "extended-keys") == 0)
|
||||
break;
|
||||
input_reply(ictx, 1, "\033[?%uu", s->kitty_keys.flags);
|
||||
break;
|
||||
case INPUT_CSI_KITTY_SET:
|
||||
if (options_get_number(global_options, "extended-keys") == 0)
|
||||
break;
|
||||
n = input_get(ictx, 0, 0, 0);
|
||||
m = input_get(ictx, 1, 1, 1);
|
||||
if (n >= 0 && m >= 1 && m <= 3)
|
||||
input_kitty_set(s, n, m);
|
||||
break;
|
||||
case INPUT_CSI_KITTY_PUSH:
|
||||
if (options_get_number(global_options, "extended-keys") == 0)
|
||||
break;
|
||||
n = input_get(ictx, 0, 0, 0);
|
||||
if (n >= 0)
|
||||
input_kitty_push(s, n);
|
||||
break;
|
||||
case INPUT_CSI_KITTY_POP:
|
||||
if (options_get_number(global_options, "extended-keys") == 0)
|
||||
break;
|
||||
n = input_get(ictx, 0, 1, 1);
|
||||
if (n >= 0)
|
||||
input_kitty_pop(s, n);
|
||||
break;
|
||||
case INPUT_CSI_WINOPS:
|
||||
input_csi_dispatch_winops(ictx);
|
||||
break;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: key-bindings.c,v 1.192 2026/08/06 09:05:04 nicm Exp $ */
|
||||
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -523,7 +523,7 @@ key_bindings_init(void)
|
||||
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
|
||||
|
||||
/* Mouse button 1 on border. */
|
||||
"bind -n MouseDown1Border { select-pane -M }",
|
||||
"bind -n MouseDown1Border { select-pane -t= }",
|
||||
|
||||
/* Mouse button 1 drag on border. */
|
||||
"bind -n MouseDrag1Border { resize-pane -M }",
|
||||
|
||||
+1
-81
@@ -45,29 +45,6 @@ static const struct {
|
||||
{ "F10", KEYC_F10|KEYC_IMPLIED_META },
|
||||
{ "F11", KEYC_F11|KEYC_IMPLIED_META },
|
||||
{ "F12", KEYC_F12|KEYC_IMPLIED_META },
|
||||
{ "F13", KEYC_F13|KEYC_IMPLIED_META },
|
||||
{ "F14", KEYC_F14|KEYC_IMPLIED_META },
|
||||
{ "F15", KEYC_F15|KEYC_IMPLIED_META },
|
||||
{ "F16", KEYC_F16|KEYC_IMPLIED_META },
|
||||
{ "F17", KEYC_F17|KEYC_IMPLIED_META },
|
||||
{ "F18", KEYC_F18|KEYC_IMPLIED_META },
|
||||
{ "F19", KEYC_F19|KEYC_IMPLIED_META },
|
||||
{ "F20", KEYC_F20|KEYC_IMPLIED_META },
|
||||
{ "F21", KEYC_F21|KEYC_IMPLIED_META },
|
||||
{ "F22", KEYC_F22|KEYC_IMPLIED_META },
|
||||
{ "F23", KEYC_F23|KEYC_IMPLIED_META },
|
||||
{ "F24", KEYC_F24|KEYC_IMPLIED_META },
|
||||
{ "F25", KEYC_F25|KEYC_IMPLIED_META },
|
||||
{ "F26", KEYC_F26|KEYC_IMPLIED_META },
|
||||
{ "F27", KEYC_F27|KEYC_IMPLIED_META },
|
||||
{ "F28", KEYC_F28|KEYC_IMPLIED_META },
|
||||
{ "F29", KEYC_F29|KEYC_IMPLIED_META },
|
||||
{ "F30", KEYC_F30|KEYC_IMPLIED_META },
|
||||
{ "F31", KEYC_F31|KEYC_IMPLIED_META },
|
||||
{ "F32", KEYC_F32|KEYC_IMPLIED_META },
|
||||
{ "F33", KEYC_F33|KEYC_IMPLIED_META },
|
||||
{ "F34", KEYC_F34|KEYC_IMPLIED_META },
|
||||
{ "F35", KEYC_F35|KEYC_IMPLIED_META },
|
||||
{ "IC", KEYC_IC|KEYC_IMPLIED_META },
|
||||
{ "Insert", KEYC_IC|KEYC_IMPLIED_META },
|
||||
{ "DC", KEYC_DC|KEYC_IMPLIED_META },
|
||||
@@ -145,54 +122,6 @@ static const struct {
|
||||
{ "KPEnter", KEYC_KP_ENTER|KEYC_KEYPAD },
|
||||
{ "KP0", KEYC_KP_ZERO|KEYC_KEYPAD },
|
||||
{ "KP.", KEYC_KP_PERIOD|KEYC_KEYPAD },
|
||||
{ "KP=", KEYC_KP_EQUAL|KEYC_KEYPAD },
|
||||
{ "KP,", KEYC_KP_SEPARATOR|KEYC_KEYPAD },
|
||||
{ "KPLeft", KEYC_KP_LEFT|KEYC_KEYPAD },
|
||||
{ "KPRight", KEYC_KP_RIGHT|KEYC_KEYPAD },
|
||||
{ "KPUp", KEYC_KP_UP|KEYC_KEYPAD },
|
||||
{ "KPDown", KEYC_KP_DOWN|KEYC_KEYPAD },
|
||||
{ "KPPageUp", KEYC_KP_PPAGE|KEYC_KEYPAD },
|
||||
{ "KPPageDown", KEYC_KP_NPAGE|KEYC_KEYPAD },
|
||||
{ "KPHome", KEYC_KP_HOME|KEYC_KEYPAD },
|
||||
{ "KPEnd", KEYC_KP_END|KEYC_KEYPAD },
|
||||
{ "KPInsert", KEYC_KP_IC|KEYC_KEYPAD },
|
||||
{ "KPDelete", KEYC_KP_DC|KEYC_KEYPAD },
|
||||
{ "KPBegin", KEYC_KP_BEGIN|KEYC_KEYPAD },
|
||||
|
||||
/* Other function keys. */
|
||||
{ "CapsLock", KEYC_CAPSLOCK },
|
||||
{ "ScrollLock", KEYC_SCROLLLOCK },
|
||||
{ "NumLock", KEYC_NUMLOCK },
|
||||
{ "PrintScreen", KEYC_PRINTSCREEN },
|
||||
{ "Pause", KEYC_PAUSE },
|
||||
{ "Menu", KEYC_MENU },
|
||||
{ "MediaPlay", KEYC_MEDIA_PLAY },
|
||||
{ "MediaPause", KEYC_MEDIA_PAUSE },
|
||||
{ "MediaPlayPause", KEYC_MEDIA_PLAYPAUSE },
|
||||
{ "MediaReverse", KEYC_MEDIA_REVERSE },
|
||||
{ "MediaStop", KEYC_MEDIA_STOP },
|
||||
{ "MediaFastForward", KEYC_MEDIA_FASTFORWARD },
|
||||
{ "MediaRewind", KEYC_MEDIA_REWIND },
|
||||
{ "MediaNext", KEYC_MEDIA_NEXT },
|
||||
{ "MediaPrevious", KEYC_MEDIA_PREVIOUS },
|
||||
{ "MediaRecord", KEYC_MEDIA_RECORD },
|
||||
{ "VolumeDown", KEYC_VOLUME_DOWN },
|
||||
{ "VolumeUp", KEYC_VOLUME_UP },
|
||||
{ "VolumeMute", KEYC_VOLUME_MUTE },
|
||||
{ "LeftShift", KEYC_LEFT_SHIFT },
|
||||
{ "LeftControl", KEYC_LEFT_CTRL },
|
||||
{ "LeftAlt", KEYC_LEFT_ALT },
|
||||
{ "LeftSuper", KEYC_LEFT_SUPER },
|
||||
{ "LeftHyper", KEYC_LEFT_HYPER },
|
||||
{ "LeftMeta", KEYC_LEFT_META },
|
||||
{ "RightShift", KEYC_RIGHT_SHIFT },
|
||||
{ "RightControl", KEYC_RIGHT_CTRL },
|
||||
{ "RightAlt", KEYC_RIGHT_ALT },
|
||||
{ "RightSuper", KEYC_RIGHT_SUPER },
|
||||
{ "RightHyper", KEYC_RIGHT_HYPER },
|
||||
{ "RightMeta", KEYC_RIGHT_META },
|
||||
{ "ISOLevel3Shift", KEYC_ISO_LEVEL3_SHIFT },
|
||||
{ "ISOLevel5Shift", KEYC_ISO_LEVEL5_SHIFT },
|
||||
|
||||
/* Mouse keys. */
|
||||
KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1),
|
||||
@@ -297,13 +226,8 @@ key_string_get_modifiers(const char **string)
|
||||
modifiers |= KEYC_META;
|
||||
break;
|
||||
case 'S':
|
||||
modifiers |= KEYC_SHIFT;
|
||||
break;
|
||||
case 's':
|
||||
modifiers |= KEYC_SUPER;
|
||||
break;
|
||||
case 'H':
|
||||
modifiers |= KEYC_HYPER;
|
||||
modifiers |= KEYC_SHIFT;
|
||||
break;
|
||||
default:
|
||||
*string = NULL;
|
||||
@@ -425,10 +349,6 @@ key_string_lookup_key(key_code key, int with_flags)
|
||||
strlcat(out, "M-", sizeof out);
|
||||
if (key & KEYC_SHIFT)
|
||||
strlcat(out, "S-", sizeof out);
|
||||
if (key & KEYC_SUPER)
|
||||
strlcat(out, "s-", sizeof out);
|
||||
if (key & KEYC_HYPER)
|
||||
strlcat(out, "H-", sizeof out);
|
||||
key &= KEYC_MASK_KEY;
|
||||
|
||||
/* Handle no key. */
|
||||
|
||||
+53
-19
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: layout-custom.c,v 1.41 2026/09/09 09:01:19 nicm Exp $ */
|
||||
/* $OpenBSD: layout-custom.c,v 1.43 2026/09/22 06:48:01 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -283,26 +283,59 @@ layout_checksum(const char *layout)
|
||||
char *
|
||||
layout_dump(__unused struct window *w, struct layout_cell *lcroot, int flags)
|
||||
{
|
||||
struct layout_string layout_string;
|
||||
char *out = NULL;
|
||||
struct layout_string layout_string = { 0 };
|
||||
char *out;
|
||||
|
||||
if (lcroot == NULL)
|
||||
return NULL;
|
||||
|
||||
goto bad;
|
||||
layout_string_init(&layout_string);
|
||||
|
||||
if (layout_append(lcroot, &layout_string, flags) == 0) {
|
||||
if (flags & LAYOUT_CUSTOM_OLD_FORMAT)
|
||||
xasprintf(&out, "%04hx,%s",
|
||||
layout_checksum(layout_string.dat),
|
||||
layout_string.dat);
|
||||
else
|
||||
xasprintf(&out, "{\"V\":2,\"L\":%s}",
|
||||
layout_string.dat);
|
||||
if (layout_append(lcroot, &layout_string, flags) != 0)
|
||||
goto bad;
|
||||
if (~flags & LAYOUT_CUSTOM_OLD_FORMAT)
|
||||
xasprintf(&out, "{\"V\":2,\"L\":%s}", layout_string.dat);
|
||||
else {
|
||||
xasprintf(&out, "%04hx,%s", layout_checksum(layout_string.dat),
|
||||
layout_string.dat);
|
||||
}
|
||||
layout_string_free(&layout_string);
|
||||
|
||||
return (out);
|
||||
|
||||
bad:
|
||||
layout_string_free(&layout_string);
|
||||
if (~flags & LAYOUT_CUSTOM_OLD_FORMAT)
|
||||
return (NULL);
|
||||
return (xstrdup("0000,"));
|
||||
}
|
||||
|
||||
/* Get a floating pane cell's z-index in the layout being dumped. */
|
||||
static u_int
|
||||
layout_cell_zindex(struct layout_cell *lc)
|
||||
{
|
||||
struct window_pane *wp = lc->wp, *wq;
|
||||
struct window *w = wp->window;
|
||||
struct layout_cell *other;
|
||||
int saved = (lc == wp->saved_layout_cell);
|
||||
u_int i = 0;
|
||||
|
||||
if (saved &&
|
||||
w->active != NULL &&
|
||||
(w->active->flags & PANE_ZOOMED) &&
|
||||
(w->active->saved_layout_cell->flags & LAYOUT_CELL_FLOATING)) {
|
||||
if (wp == w->active)
|
||||
return (0);
|
||||
i++;
|
||||
}
|
||||
TAILQ_FOREACH(wq, &w->z_index, zentry) {
|
||||
if (wq == wp)
|
||||
break;
|
||||
if (saved)
|
||||
other = wq->saved_layout_cell;
|
||||
else
|
||||
other = wq->layout_cell;
|
||||
if (other != NULL && (other->flags & LAYOUT_CELL_FLOATING))
|
||||
i++;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Append information for a single cell in a JSON (v2) format. */
|
||||
@@ -313,7 +346,7 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
|
||||
struct window_pane *wp;
|
||||
enum layout_type type;
|
||||
char c;
|
||||
u_int i, n;
|
||||
u_int i, n, z;
|
||||
|
||||
if (lc == NULL)
|
||||
return (-1);
|
||||
@@ -354,9 +387,10 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
|
||||
if (window_pane_index(wp, &i) != 0)
|
||||
return (-1);
|
||||
layout_string_write(ls, ",\"i\":%u", i);
|
||||
if ((lc->flags & LAYOUT_CELL_FLOATING) &&
|
||||
window_pane_zindex(wp, &i) == 0)
|
||||
layout_string_write(ls, ",\"z\":%u", i);
|
||||
if (lc->flags & LAYOUT_CELL_FLOATING) {
|
||||
z = layout_cell_zindex(lc);
|
||||
layout_string_write(ls, ",\"z\":%u", z);
|
||||
}
|
||||
layout_string_write(ls, ",\"I\":\"%%%u\"", wp->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: layout.c,v 1.100 2026/09/11 08:16:14 nicm Exp $ */
|
||||
/* $OpenBSD: layout.c,v 1.101 2026/09/20 08:42:46 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1728,7 +1728,7 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
||||
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
|
||||
char **cause)
|
||||
{
|
||||
int sx, sy, ox, oy;
|
||||
int sx, sy, ox, oy, pad;
|
||||
char *error = NULL;
|
||||
|
||||
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
|
||||
@@ -1777,12 +1777,20 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
||||
}
|
||||
}
|
||||
|
||||
if (!window_has_floating_panes(w)) {
|
||||
w->last_new_pane_x = 0;
|
||||
w->last_new_pane_y = 0;
|
||||
}
|
||||
if (ox == INT_MAX) {
|
||||
if (w->last_new_pane_x == 0)
|
||||
ox = 4;
|
||||
else {
|
||||
if (lines != PANE_LINES_NONE)
|
||||
pad = 1;
|
||||
else
|
||||
pad = 0;
|
||||
ox = w->last_new_pane_x + 4;
|
||||
if (w->last_new_pane_x > w->sx)
|
||||
if (ox + sx + pad > (int)w->sx)
|
||||
ox = 4;
|
||||
}
|
||||
w->last_new_pane_x = ox;
|
||||
@@ -1793,8 +1801,12 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
|
||||
if (w->last_new_pane_y == 0)
|
||||
oy = 2;
|
||||
else {
|
||||
if (lines != PANE_LINES_NONE)
|
||||
pad = 1;
|
||||
else
|
||||
pad = 0;
|
||||
oy = w->last_new_pane_y + 2;
|
||||
if (w->last_new_pane_y > w->sy)
|
||||
if (oy + sy + pad > (int)w->sy)
|
||||
oy = 2;
|
||||
}
|
||||
w->last_new_pane_y = oy;
|
||||
|
||||
+3
-4
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: options-table.c,v 1.246 2026/09/11 10:17:16 nicm Exp $ */
|
||||
/* $OpenBSD: options-table.c,v 1.245 2026/09/10 11:02:18 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -76,8 +76,7 @@ static const char *options_table_pane_border_indicators_list[] = {
|
||||
"off", "colour", "arrows", "both", NULL
|
||||
};
|
||||
static const char *options_table_pane_border_lines_list[] = {
|
||||
"single", "double", "heavy", "simple", "number", "spaces", "none",
|
||||
"rounded", NULL
|
||||
"single", "double", "heavy", "simple", "number", "spaces", "none", NULL
|
||||
};
|
||||
static const char *options_table_popup_border_lines_list[] = {
|
||||
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
|
||||
@@ -104,7 +103,7 @@ static const char *options_table_extended_keys_list[] = {
|
||||
"off", "on", "always", NULL
|
||||
};
|
||||
static const char *options_table_extended_keys_format_list[] = {
|
||||
"csi-u", "xterm", "kitty", NULL
|
||||
"csi-u", "xterm", NULL
|
||||
};
|
||||
static const char *options_table_allow_passthrough_list[] = {
|
||||
"off", "on", "all", NULL
|
||||
|
||||
@@ -1400,12 +1400,6 @@ options_push_changes(const char *name)
|
||||
TAILQ_FOREACH(loop, &clients, entry)
|
||||
server_client_set_key_table(loop, NULL);
|
||||
}
|
||||
if (strcmp(name, "extended-keys") == 0) {
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
if (loop->tty.flags & TTY_STARTED)
|
||||
tty_update_features(&loop->tty);
|
||||
}
|
||||
}
|
||||
if (strcmp(name, "user-keys") == 0) {
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
if (loop->tty.flags & TTY_OPENED)
|
||||
|
||||
+23
-20
@@ -1,33 +1,17 @@
|
||||
TESTS!= echo *.sh
|
||||
LOGDIR=logs
|
||||
|
||||
.PHONY: all
|
||||
.NOTPARALLEL: all
|
||||
.PHONY: all prepare $(TESTS)
|
||||
|
||||
all:
|
||||
@mkdir -p "$(LOGDIR)"; \
|
||||
rm -f "$(LOGDIR)"/*.log; \
|
||||
failed=0; failures=; \
|
||||
all: prepare $(TESTS)
|
||||
@failed=0; failures=; \
|
||||
for test in $(TESTS); do \
|
||||
base=$${test##*/}; \
|
||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||
rm -f "$$log"; \
|
||||
printf '%-40s ' "$$test"; \
|
||||
start=$$(date +%s); \
|
||||
env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||
sh -x "$$test" >"$$log" 2>&1; \
|
||||
if [ $$? -eq 0 ]; then \
|
||||
end=$$(date +%s); \
|
||||
rm -f "$$log"; \
|
||||
echo "PASS ($$((end - start))s)"; \
|
||||
else \
|
||||
end=$$(date +%s); \
|
||||
echo "FAIL ($$((end - start))s)"; \
|
||||
echo " log: $$log"; \
|
||||
if [ -f "$$log" ]; then \
|
||||
failed=1; \
|
||||
failures="$$failures $$test"; \
|
||||
fi; \
|
||||
sleep 1; \
|
||||
done; \
|
||||
if [ "$$failed" -ne 0 ]; then \
|
||||
echo; \
|
||||
@@ -39,3 +23,22 @@ all:
|
||||
rmdir "$(LOGDIR)" 2>/dev/null || true; \
|
||||
fi; \
|
||||
exit $$failed
|
||||
|
||||
prepare:
|
||||
@mkdir -p "$(LOGDIR)"
|
||||
@rm -f "$(LOGDIR)"/*.log
|
||||
|
||||
$(TESTS): prepare
|
||||
@base="$@"; base=$${base##*/}; \
|
||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||
start=$$(date +%s); \
|
||||
if env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||
sh -x "$@" >"$$log" 2>&1; then \
|
||||
end=$$(date +%s); \
|
||||
rm -f "$$log"; \
|
||||
printf '%-40s PASS (%ss)\n' "$@" "$$((end - start))"; \
|
||||
else \
|
||||
end=$$(date +%s); \
|
||||
printf '%-40s FAIL (%ss)\n log: %s\n' \
|
||||
"$@" "$$((end - start))" "$$log"; \
|
||||
fi
|
||||
|
||||
+32
-2
@@ -2,8 +2,9 @@
|
||||
|
||||
# monitor-activity, monitor-bell and monitor-silence: both the
|
||||
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
|
||||
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
|
||||
# the #, !, ~ characters in window_flags). The sessions are detached so
|
||||
# flags (window_activity_flag, window_bell_flag, window_silence_flag,
|
||||
# their session equivalents and the #, !, ~ characters in window_flags).
|
||||
# The sessions are detached so
|
||||
# alert flags are set even on the current window; the *-action options
|
||||
# still decide whether the hooks fire. Panes run cat: activity is
|
||||
# generated by the tty echo of send-keys and a bell by sending a BEL and
|
||||
@@ -111,6 +112,24 @@ flags_have()
|
||||
esac
|
||||
}
|
||||
|
||||
assert_session_flag()
|
||||
{
|
||||
flag="#{session_${1}_flag}"
|
||||
expected=$2
|
||||
|
||||
# Only mon exists here. Check both window contexts and list-sessions.
|
||||
for target in mon:w0 "mon:$3"; do
|
||||
value=$($TMUX display -pt "$target" "$flag") ||
|
||||
fail "display $flag failed"
|
||||
[ "$value" = "$expected" ] ||
|
||||
fail "expected $flag for $target to be '$expected' but got '$value'"
|
||||
done
|
||||
value=$($TMUX list-sessions -F "$flag") ||
|
||||
fail "list-sessions $flag failed"
|
||||
[ "$value" = "$expected" ] ||
|
||||
fail "expected list-sessions $flag to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
flags_lack()
|
||||
{
|
||||
target=$1
|
||||
@@ -150,10 +169,12 @@ $TMUX set-hook -g alert-silence \
|
||||
# alert-bell and sets the bell flag, shown as ! in window_flags.
|
||||
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
|
||||
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
|
||||
assert_session_flag bell 0 bellw
|
||||
flags_lack mon:bellw '!'
|
||||
bell mon:bellw
|
||||
wait_for @log '|alert-bell:mon:bellw'
|
||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
|
||||
assert_session_flag bell 1 bellw
|
||||
flags_have mon:bellw '!'
|
||||
|
||||
# Bells are not deduplicated: a second bell fires the hook again even
|
||||
@@ -164,6 +185,7 @@ wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
|
||||
# Selecting the window clears the alert flags.
|
||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
|
||||
assert_session_flag bell 0 bellw
|
||||
flags_lack mon:bellw '!'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
|
||||
@@ -173,6 +195,7 @@ $TMUX set -g @log '' || fail "reset @log failed"
|
||||
bell mon:w0
|
||||
wait_for @log '|alert-bell:mon:w0'
|
||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
|
||||
assert_session_flag bell 1 bellw
|
||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
|
||||
@@ -199,6 +222,7 @@ $TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
activity mon:actw
|
||||
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
|
||||
assert_session_flag activity 0 actw
|
||||
assert_unchanged @log ''
|
||||
|
||||
# With monitor-activity on, output fires alert-activity and sets the
|
||||
@@ -208,6 +232,7 @@ $TMUX set -wt mon:actw monitor-activity on ||
|
||||
activity mon:actw
|
||||
wait_for @log '|alert-activity:mon:actw'
|
||||
wait_for_fmt mon:actw '#{window_activity_flag}' 1
|
||||
assert_session_flag activity 1 actw
|
||||
flags_have mon:actw '#'
|
||||
|
||||
# While the flag is set further activity does not fire the hook again.
|
||||
@@ -234,6 +259,7 @@ exec 3>"$OUT/fifo"
|
||||
wait_for_fmt mon: '#{session_attached}' 1
|
||||
$TMUX selectw -t mon:actw || fail "select-window actw failed"
|
||||
wait_for_fmt mon:actw '#{window_activity_flag}' 0
|
||||
assert_session_flag activity 0 actw
|
||||
flags_lack mon:actw '#'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
exec 3>&-
|
||||
@@ -250,6 +276,7 @@ $TMUX set -wt mon:w0 monitor-activity on ||
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
activity mon:w0
|
||||
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
|
||||
assert_session_flag activity 1 actw
|
||||
assert_unchanged @log ''
|
||||
$TMUX set -wut mon:w0 monitor-activity ||
|
||||
fail "unset monitor-activity w0 failed"
|
||||
@@ -273,16 +300,19 @@ $TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
# running.
|
||||
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
|
||||
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
|
||||
assert_session_flag silence 0 silw
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
|
||||
wait_for @log '|alert-silence:mon:silw'
|
||||
wait_for_fmt mon:silw '#{window_silence_flag}' 1
|
||||
assert_session_flag silence 1 silw
|
||||
flags_have mon:silw '~'
|
||||
assert_unchanged @log '|alert-silence:mon:silw'
|
||||
$TMUX set -wt mon:silw monitor-silence 0 ||
|
||||
fail "reset monitor-silence failed"
|
||||
$TMUX selectw -t mon:silw || fail "select-window silw failed"
|
||||
wait_for_fmt mon:silw '#{window_silence_flag}' 0
|
||||
assert_session_flag silence 0 silw
|
||||
flags_lack mon:silw '~'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Width cache ranges must include their endpoint without overflowing wchar_t.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
SERVER=
|
||||
WATCHDOG=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
if [ -n "$WATCHDOG" ]; then
|
||||
kill "$WATCHDOG" 2>/dev/null
|
||||
wait "$WATCHDOG" 2>/dev/null
|
||||
fi
|
||||
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
$TMUX new-session -d 'exec sleep 60' || exit 1
|
||||
SERVER=$($TMUX display-message -p '#{pid}') || exit 1
|
||||
|
||||
# kill-server cannot stop a server stuck rebuilding the width cache.
|
||||
(
|
||||
sleep 5
|
||||
kill -9 "$SERVER" 2>/dev/null
|
||||
) &
|
||||
WATCHDOG=$!
|
||||
|
||||
$TMUX set -g codepoint-widths 'U+1F600=2' || exit 1
|
||||
|
||||
# Cover both signed and unsigned 32-bit wchar_t. Values above WCHAR_MAX
|
||||
# are ignored by the parser on platforms where they cannot be represented.
|
||||
for value in U+7FFFFFFF=1 U+7FFFFFFE-U+7FFFFFFF=2 \
|
||||
U+FFFFFFFF=1 U+FFFFFFFE-U+FFFFFFFF=2; do
|
||||
$TMUX set -g codepoint-widths "$value" || exit 1
|
||||
[ "$($TMUX display-message -p alive)" = alive ] || exit 1
|
||||
done
|
||||
|
||||
# Check that an ordinary range still includes both endpoints and stops there.
|
||||
$TMUX set -g codepoint-widths 'U+03B1-U+03B3=2' || exit 1
|
||||
$TMUX set -g @text 'αβγδ' || exit 1
|
||||
[ "$($TMUX display-message -p '#{w:@text}')" = 7 ] || exit 1
|
||||
$TMUX set -gu codepoint-widths || exit 1
|
||||
[ "$($TMUX display-message -p '#{w:@text}')" = 4 ] || exit 1
|
||||
|
||||
$TMUX kill-server || exit 1
|
||||
SERVER=
|
||||
exit 0
|
||||
@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
OUT=$(mktemp)
|
||||
|
||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
FIFO=$(mktemp -u)
|
||||
|
||||
@@ -153,11 +153,13 @@ wait_format "$p0" '#{pane_in_mode}' '0'
|
||||
wait_option @picked "$p1"
|
||||
|
||||
# Commands after display-panes run immediately while the mode remains.
|
||||
# Record the mode in the same command queue so client startup cannot race
|
||||
# the 500ms timeout.
|
||||
$TMUX set -g @after none || fail "set @after failed"
|
||||
$TMUX display-panes -Nd 500 -t "$p0" \; set -g @after fast ||
|
||||
$TMUX display-panes -Nd 500 -t "$p0" \; \
|
||||
set -gF -t "$p0" @after '#{pane_mode}' ||
|
||||
fail "display-panes immediate command failed"
|
||||
wait_option @after fast
|
||||
wait_format "$p0" '#{pane_mode}' 'panes-mode'
|
||||
wait_option @after 'panes-mode'
|
||||
wait_format "$p0" '#{pane_in_mode}' '0'
|
||||
|
||||
# Existing zoom is restored on exit.
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Moving, resizing and reordering an over-zoom float must leave the tiled
|
||||
# pane zoomed, and the changes must survive an explicit unzoom.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
$TMUX "$@" || fail "failed: $*"
|
||||
}
|
||||
|
||||
check()
|
||||
{
|
||||
got=$(run display-message -p -t "$1" "$2") || exit 1
|
||||
[ "$got" = "$3" ] || fail "$1 $2: got '$got', expected '$3'"
|
||||
}
|
||||
|
||||
check_zoom()
|
||||
{
|
||||
check "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
|
||||
check "$float" '#{pane_floating_flag}:#{pane_active}' "1:$active"
|
||||
check "$base" '#{@unzoomed}' ''
|
||||
}
|
||||
|
||||
run new-session -d -x 80 -y 24
|
||||
base=$(run display-message -p '#{pane_id}') || exit 1
|
||||
other=$(run split-window -dPF '#{pane_id}') || exit 1
|
||||
layout=$(run display-message -p '#{window_layout}') || exit 1
|
||||
run set-hook -g window-unzoomed 'set -g @unzoomed 1'
|
||||
|
||||
# Cover creation before and after zoom, with both the float and the tiled
|
||||
# pane active. Modal panes use the same over-zoom flag.
|
||||
for mode in before after detached modal; do
|
||||
if [ "$mode" != before ]; then
|
||||
run resize-pane -Z -t "$base"
|
||||
fi
|
||||
case "$mode" in
|
||||
detached) flags=-Ad; active=0 ;;
|
||||
modal) flags=-O; active=1 ;;
|
||||
*) flags=-A; active=1 ;;
|
||||
esac
|
||||
float=$(run new-pane "$flags" -PF '#{pane_id}' -t "$base" \
|
||||
-x 20 -y 8 -X 8 -Y 3 '') || exit 1
|
||||
if [ "$mode" = before ]; then
|
||||
run resize-pane -Z -t "$base"
|
||||
fi
|
||||
run set -gu @unzoomed
|
||||
check_zoom
|
||||
|
||||
run move-pane -t "$float" -D
|
||||
check "$float" '#{pane_top}' 5
|
||||
check_zoom
|
||||
run move-pane -t "$float" -U 2 -R 3 -L 1
|
||||
check "$float" '#{pane_left}:#{pane_top}' '11:3'
|
||||
check_zoom
|
||||
run move-pane -t "$float" -X 25% -Y 25%
|
||||
check "$float" '#{pane_left}:#{pane_top}' '21:7'
|
||||
check_zoom
|
||||
run move-pane -t "$float" -P bottom-right
|
||||
check "$float" '#{pane_left}:#{pane_top}' '61:17'
|
||||
check_zoom
|
||||
run resize-pane -t "$float" -x 30 -y 10
|
||||
check "$float" '#{pane_width}:#{pane_height}' '28:8'
|
||||
check_zoom
|
||||
run resize-pane -t "$float" -U 1 -L 2
|
||||
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
|
||||
'59:16:30:9'
|
||||
check_zoom
|
||||
run move-pane -t "$float" -P centre
|
||||
check "$float" '#{pane_left}:#{pane_top}' '25:7'
|
||||
check_zoom
|
||||
|
||||
# Failed commands must not change zoom either.
|
||||
for command in 'move-pane -D invalid' 'move-pane -P invalid' \
|
||||
'move-pane -z invalid' 'resize-pane -x invalid' \
|
||||
'resize-pane -D invalid'; do
|
||||
$TMUX $command -t "$float" 2>/dev/null &&
|
||||
fail "unexpected success: $command"
|
||||
check_zoom
|
||||
done
|
||||
|
||||
run resize-pane -Z -t "$base"
|
||||
check "$float" '#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \
|
||||
'25:7:30:9'
|
||||
run kill-pane -t "$float"
|
||||
check "$base" '#{window_layout}' "$layout"
|
||||
done
|
||||
|
||||
# Put an ordinary (hidden while zoomed) float between two over-zoom floats
|
||||
# in the stacking order. Reordering must skip it when counting visible
|
||||
# positions, but keep all floats ahead of the tiled panes after unzoom.
|
||||
back=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
|
||||
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
|
||||
float=$(run new-pane -AdPF '#{pane_id}' -t "$base" '') || exit 1
|
||||
active=0
|
||||
run resize-pane -Z -t "$base"
|
||||
run set -gu @unzoomed
|
||||
|
||||
for position in backward back forward-loop; do
|
||||
run move-pane -t "$float" -P front
|
||||
run move-pane -t "$float" -P "$position"
|
||||
check "$float" '#{pane_z}' 1
|
||||
check "$back" '#{pane_z}' 0
|
||||
check_zoom
|
||||
done
|
||||
for position in forward front backward-loop; do
|
||||
run move-pane -t "$float" -P back
|
||||
run move-pane -t "$float" -P "$position"
|
||||
check "$float" '#{pane_z}' 0
|
||||
check "$back" '#{pane_z}' 1
|
||||
check_zoom
|
||||
done
|
||||
for z in 1 0 99; do
|
||||
run move-pane -t "$float" -z "$z"
|
||||
want=$z
|
||||
[ "$z" = 99 ] && want=1
|
||||
check "$float" '#{pane_z}' "$want"
|
||||
check_zoom
|
||||
done
|
||||
run resize-pane -Z -t "$base"
|
||||
check "$float" '#{pane_z}' 2
|
||||
check "$base" '#{pane_z}' 4
|
||||
run kill-pane -t "$hidden"
|
||||
run kill-pane -t "$back"
|
||||
|
||||
# Explicit zoom toggling and operations on tiled panes retain their
|
||||
# existing behaviour. The over-zoom flag alone does not make a pane a float:
|
||||
# zooming the float itself gives it a tiled cell until unzoom.
|
||||
run resize-pane -Z -t "$float"
|
||||
check "$float" '#{pane_zoomed_flag}:#{pane_floating_flag}' '1:0'
|
||||
run resize-pane -t "$float" -x 25
|
||||
check "$float" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
|
||||
'0:1:23'
|
||||
run resize-pane -Z -t "$base"
|
||||
run resize-pane -t "$other" -D 1
|
||||
check "$base" '#{window_zoomed_flag}' 0
|
||||
|
||||
# A hidden ordinary float must still be unzoomed before resizing it.
|
||||
hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1
|
||||
run resize-pane -Z -t "$base"
|
||||
run resize-pane -t "$hidden" -x 25
|
||||
check "$hidden" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \
|
||||
'0:1:23'
|
||||
|
||||
exit 0
|
||||
@@ -7,6 +7,8 @@ TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -18,6 +18,24 @@ export TZ LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||
HOLD1=
|
||||
HOLD2=
|
||||
CC1=
|
||||
CC2=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
for pid in $HOLD1 $HOLD2 $CC1 $CC2; do
|
||||
kill "$pid" 2>/dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
done
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
ESC=$(printf '\033')
|
||||
|
||||
@@ -579,8 +597,6 @@ assert_alive "verbose loop expansion"
|
||||
|
||||
# L loops over attached clients. Attach two control-mode clients, each held
|
||||
# open by a background process keeping a FIFO's write end open.
|
||||
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
||||
# Hold the write ends open so the control clients stay attached.
|
||||
@@ -605,9 +621,9 @@ test_format "#{L/nr:x}" "xx"
|
||||
test_format "#{L/r:x}" "xx"
|
||||
# Now detach one and confirm the count drops to one.
|
||||
kill $HOLD2 2>/dev/null
|
||||
wait $HOLD2 2>/dev/null
|
||||
HOLD2=
|
||||
sleep 1
|
||||
test_format "#{L:x}" "x"
|
||||
kill $HOLD1 $CC1 $CC2 2>/dev/null
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -7,6 +7,8 @@ TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -76,14 +76,14 @@ $TMUX set-hook -g session-closed \
|
||||
fail "set-hook session-closed failed"
|
||||
|
||||
# The only pane of the only window of a session exits: pane-exited, then
|
||||
# window-unlinked, then session-closed, each seeing the dead object in the
|
||||
# session-closed, then window-unlinked, each seeing the dead object in the
|
||||
# hook formats.
|
||||
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
|
||||
fail "new-session doomed failed"
|
||||
wait_for @log \
|
||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
||||
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||
assert_unchanged @log \
|
||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
||||
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||
|
||||
# The dead pane, window and session cannot be used as targets but the
|
||||
# server survives.
|
||||
@@ -110,14 +110,14 @@ $TMUX set-hook -g pane-exited \
|
||||
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
||||
fail "restore pane-exited hook failed"
|
||||
|
||||
# kill-window on the last window: window-unlinked then session-closed but
|
||||
# kill-window on the last window: session-closed then window-unlinked but
|
||||
# no pane-exited for the panes in the killed window.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
|
||||
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
|
||||
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
|
||||
wait_for @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
||||
assert_unchanged @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
||||
wait_for @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||
assert_unchanged @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||
$TMUX has -t main || fail "server died after kill-window chain"
|
||||
|
||||
# kill-session: session-closed fires first, then window-unlinked for its
|
||||
|
||||
@@ -12,7 +12,8 @@ $TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
OUT=$(mktemp)
|
||||
trap "rm -f $TMP $OUT" 0 1 15
|
||||
trap 'rm -f "$TMP" "$OUT"; $TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
cat <<EOF >$TMP
|
||||
if 'true' 'wibble wobble'
|
||||
|
||||
@@ -4,6 +4,9 @@ TERM=screen
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
# Keep panes alive until the next case or cleanup, without reading replies.
|
||||
INPUT_HOLD='while :; do sleep 3600; done'
|
||||
|
||||
TMP=$(mktemp)
|
||||
EXP=$(mktemp)
|
||||
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
@@ -37,10 +40,10 @@ start_pane_hlimit()
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "$INPUT_HOLD" || exit 1
|
||||
$TMUX set-option -g history-limit "$hlimit" || exit 1
|
||||
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
|
||||
"printf '$seq'; sleep 2" || exit 1
|
||||
"printf '$seq'; $INPUT_HOLD" || exit 1
|
||||
$TMUX kill-session -t test-setup
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
+13
-1
@@ -22,7 +22,19 @@ assert_key () {
|
||||
clear-history -t$W \; \
|
||||
send-keys -t$W "$key" 'EOL' || exit 1
|
||||
|
||||
actual_code=$($TMUX capturep -pt$W | \
|
||||
# cat echoes the keys back asynchronously, so wait for the EOL marker
|
||||
# to reach the pane instead of capturing straight away.
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
screen=$($TMUX capturep -pt$W)
|
||||
case "$screen" in
|
||||
*EOL*) break ;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
actual_code=$(printf '%s\n' "$screen" | \
|
||||
head -1 | \
|
||||
sed -e 's/EOL.*$//')
|
||||
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
|
||||
. ./input-common.inc
|
||||
|
||||
# Wait for the terminator and following text to reach the parser.
|
||||
check_discard()
|
||||
{
|
||||
name=$1
|
||||
printf 'OK\n' >"$EXP"
|
||||
i=0
|
||||
while [ "$i" -lt 300 ]; do
|
||||
capture_grid "$name" >"$TMP"
|
||||
cmp -s "$TMP" "$EXP" && return 0
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "$name (timed out waiting for discard)"
|
||||
}
|
||||
|
||||
# The missing-terminator timer may expire before the string limit is reached
|
||||
# on a slow build. Clear any payload printed after the timeout, then check
|
||||
# that the string did not change the title and normal output has resumed.
|
||||
test_discard()
|
||||
{
|
||||
name=$1
|
||||
prefix=$2
|
||||
start_cmd "$name" 8 3 "$INPUT_HOLD"
|
||||
$TMUX select-pane -T discard-test || exit 1
|
||||
$TMUX respawn-pane -k \
|
||||
"perl -e 'print qq{$prefix}, q{x} x 1100000, qq{\e\\\\\e[H\e[2JOK}'; $INPUT_HOLD" || exit 1
|
||||
check_discard "$name"
|
||||
$TMUX display-message -p -t "$name:" '#{pane_title}' >"$TMP" || exit 1
|
||||
printf 'discard-test\n' >"$EXP"
|
||||
cmp "$TMP" "$EXP" || fail "$name title"
|
||||
}
|
||||
|
||||
start_cmd csi-param-discard 8 3 \
|
||||
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
|
||||
check_capture csi-param-discard 'OK'
|
||||
@@ -10,13 +42,8 @@ start_cmd csi-interm-discard 8 3 \
|
||||
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
|
||||
check_capture csi-interm-discard 'OK'
|
||||
|
||||
start_cmd osc-discard 8 3 \
|
||||
"perl -e 'print qq{\e]2;}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||
check_capture osc-discard 'OK'
|
||||
|
||||
start_cmd apc-discard 8 3 \
|
||||
"perl -e 'print qq{\e_}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||
check_capture apc-discard 'OK'
|
||||
test_discard osc-discard '\e]2;'
|
||||
test_discard apc-discard '\e_'
|
||||
|
||||
start_pane unknown-csi 8 3 '\033[?9999zOK'
|
||||
check_capture unknown-csi 'OK'
|
||||
|
||||
@@ -51,7 +51,7 @@ $TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
$TMUX new-session -d -x 5 -y 3 -s history \; \
|
||||
set-option -g history-limit 3 \; \
|
||||
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; sleep 2" || exit 1
|
||||
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; $INPUT_HOLD" || exit 1
|
||||
sleep 0.3
|
||||
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
||||
printf "%s\n" '01
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Killing a session whose window is zoomed must not crash the server:
|
||||
# window_destroy used to unzoom the window, which resized the panes and
|
||||
# fired pane-resized with the window in the payload, and releasing that
|
||||
# reference destroyed the window a second time.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX -f/dev/null new -d -sfoo || exit 1
|
||||
$TMUX split-window -d -h -tfoo:0 || exit 1
|
||||
$TMUX resize-pane -Z -tfoo:0 || exit 1
|
||||
$TMUX new -d -sbar || exit 1
|
||||
$TMUX kill-session -tfoo || exit 1
|
||||
$TMUX has-session -tbar || exit 1
|
||||
|
||||
# The same with the zoomed window in a session killed by kill-server.
|
||||
$TMUX new -d -sbaz || exit 1
|
||||
$TMUX split-window -d -v -tbaz:0 || exit 1
|
||||
$TMUX resize-pane -Z -tbaz:0 || exit 1
|
||||
$TMUX kill-server || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -1,146 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test per-client keyboard protocol detection and pane-side encoding.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
KCONF=$(mktemp)
|
||||
LCONF=$(mktemp)
|
||||
OUT=$(mktemp)
|
||||
TMP=$(mktemp)
|
||||
SOCKETS="testKmc$$ testKka$$ testKua$$ testKrt$$ testKru$$ testKlq$$ testKlu$$"
|
||||
|
||||
printf '%s\n' 'set -g extended-keys on' >"$KCONF"
|
||||
printf '%s\n' 'set -g extended-keys off' >"$LCONF"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -f "$KCONF" "$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" ]
|
||||
}
|
||||
|
||||
client_mode()
|
||||
{
|
||||
tmux=$1
|
||||
session=$2
|
||||
|
||||
if [ -z "$session" ]; then
|
||||
$tmux list-clients -F '#{client_key_mode}'
|
||||
else
|
||||
$tmux list-clients -F '#{client_session}:#{client_key_mode}' |
|
||||
sed -n "s/^$session://p"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_client_mode()
|
||||
{
|
||||
tmux=$1
|
||||
session=$2
|
||||
wanted=$3
|
||||
i=0
|
||||
while [ "$(client_mode "$tmux" "$session")" != "$wanted" ] &&
|
||||
[ "$i" -lt 50 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$(client_mode "$tmux" "$session")" = "$wanted" ]
|
||||
}
|
||||
|
||||
# Each client must be negotiated independently. A tmux with extended-keys off
|
||||
# stands in for a terminal without Kitty keys, since it does not answer the
|
||||
# query.
|
||||
M="$TEST_TMUX -LtestKmc$$ -f$KCONF"
|
||||
KA="$TEST_TMUX -LtestKka$$ -f$KCONF"
|
||||
UA="$TEST_TMUX -LtestKua$$ -f$LCONF"
|
||||
$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
|
||||
|
||||
# Each client must report the protocol its own terminal negotiated.
|
||||
wait_for_client_mode "$M" kitty 'Kitty 1' || exit 1
|
||||
wait_for_client_mode "$M" csiu 'Ext' || 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
|
||||
|
||||
# A Kitty-capable terminal must still be reported when it is not in use.
|
||||
$M set-option -g extended-keys off
|
||||
wait_for_client_mode "$M" kitty 'VT10x (Kitty)' || exit 1
|
||||
wait_for_client_mode "$M" csiu 'VT10x' || exit 1
|
||||
$M set-option -g extended-keys on
|
||||
wait_for_client_mode "$M" kitty 'Kitty 1' || 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$LCONF"
|
||||
$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_client_mode "$R" '' 'Ext' || 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
|
||||
|
||||
# An application is told the Kitty flags it asked for even when a client that
|
||||
# only supports VT10x is attached, and keys from that client are encoded as it
|
||||
# asked.
|
||||
: >"$OUT"
|
||||
: >"$TMP"
|
||||
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'; dd bs=1 count=7 2>/dev/null | od -An -v -t x1 >'$TMP'; 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")" = '1b5b3f3175' ] || exit 1
|
||||
wait_for_client_mode "$LQ" '' 'VT10x' || exit 1
|
||||
$LU send-keys C-a
|
||||
wait_for_output "$TMP" || exit 1
|
||||
[ "$(tr -d ' \n' <"$TMP")" = '1b5b39373b3575' ] || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test client-side Kitty keyboard protocol support: an outer tmux acts as a
|
||||
# Kitty-capable terminal for the inner tmux which is the one under test. The
|
||||
# inner tmux queries the outer one, the outer one replies, and the inner one
|
||||
# then parses the key sequences sent to it.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
CONF=$(mktemp)
|
||||
TMP=$(mktemp)
|
||||
|
||||
printf '%s\n' 'set -g extended-keys on' >"$CONF"
|
||||
|
||||
TMUX="$TEST_TMUX -LtestKIA$$ -f$CONF"
|
||||
TMUX2="$TEST_TMUX -LtestKIB$$ -f$CONF"
|
||||
|
||||
trap 'rm -f "$CONF" "$TMP"; $TMUX kill-server 2>/dev/null; \
|
||||
$TMUX2 kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX2 new-session -d -x80 -y24 || exit 1
|
||||
$TMUX new-session -d -x80 -y24 "$TMUX2 attach" || exit 1
|
||||
|
||||
exit_status=0
|
||||
|
||||
wait_for_mode()
|
||||
{
|
||||
i=0
|
||||
while [ "$($TMUX display-message -pt: '#{pane_key_mode}')" != "$1" ] &&
|
||||
[ "$i" -lt 50 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$($TMUX display-message -pt: '#{pane_key_mode}')" = "$1" ]
|
||||
}
|
||||
|
||||
assert_key()
|
||||
{
|
||||
keys=$1
|
||||
expected=$2
|
||||
|
||||
$TMUX2 command-prompt -k 'display-message -pl "%%"' >"$TMP" &
|
||||
sleep 0.15
|
||||
# Send raw bytes so the outer tmux does not encode them itself.
|
||||
raw=$(printf '%s' "$keys" | sed -e 's/Escape /\\033/g' -e 's/ //g')
|
||||
$TMUX send-keys -H $(printf "$raw" | od -An -v -t x1)
|
||||
wait
|
||||
|
||||
actual=$(tr -d '[:space:]' <"$TMP")
|
||||
if [ "$actual" = "$expected" ]; then
|
||||
[ -n "$VERBOSE" ] && echo "[PASS] $keys -> $actual"
|
||||
else
|
||||
echo "[FAIL] $keys -> $expected (got '$actual')"
|
||||
exit_status=1
|
||||
fi
|
||||
}
|
||||
|
||||
# The inner tmux must detect the outer one and push its flags.
|
||||
if ! wait_for_mode 'Kitty 1'; then
|
||||
echo "[FAIL] inner tmux did not enable the Kitty protocol"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Modifiers, including the Kitty-only Super and Hyper.
|
||||
assert_key 'Escape [97;2u' 'S-a'
|
||||
assert_key 'Escape [97;3u' 'M-a'
|
||||
assert_key 'Escape [97;5u' 'C-a'
|
||||
assert_key 'Escape [97;6u' 'C-S-a'
|
||||
assert_key 'Escape [97;7u' 'C-M-a'
|
||||
assert_key 'Escape [97;9u' 's-a'
|
||||
assert_key 'Escape [97;17u' 'H-a'
|
||||
assert_key 'Escape [65;2u' 'S-A'
|
||||
|
||||
# Keys with a dedicated tmux key code.
|
||||
assert_key 'Escape [9;5u' 'C-Tab'
|
||||
assert_key 'Escape [9;2u' 'BTab'
|
||||
assert_key 'Escape [13;5u' 'C-Enter'
|
||||
assert_key 'Escape [27u' 'Escape'
|
||||
assert_key 'Escape [32;5u' 'C-Space'
|
||||
assert_key 'Escape [127;5u' 'C-BSpace'
|
||||
|
||||
# Keys with a CSI final byte other than u.
|
||||
assert_key 'Escape [1;5A' 'C-Up'
|
||||
assert_key 'Escape [1;3D' 'M-Left'
|
||||
assert_key 'Escape [1;5H' 'C-Home'
|
||||
assert_key 'Escape [1;5P' 'C-F1'
|
||||
|
||||
# Functional and non-ASCII keys.
|
||||
assert_key 'Escape [57399;5u' 'C-KP0'
|
||||
assert_key 'Escape [233;5u' 'C-é'
|
||||
|
||||
# The alternate key and associated text fields are not requested but must be
|
||||
# ignored if a terminal sends them anyway.
|
||||
assert_key 'Escape [97:65;2u' 'S-a'
|
||||
assert_key 'Escape [97:65:97;5u' 'C-a'
|
||||
assert_key 'Escape [97;5;97u' 'C-a'
|
||||
|
||||
# A repeat event is a key press, a release event is dropped. The C-b that
|
||||
# follows shows that the sequence was consumed and not passed through.
|
||||
assert_key 'Escape [97;5:1u' 'C-a'
|
||||
assert_key 'Escape [97;5:2u' 'C-a'
|
||||
assert_key 'Escape [97;5:3u Escape [98;5u' 'C-b'
|
||||
|
||||
# The Meta modifier bit is not representable and is dropped.
|
||||
assert_key 'Escape [97;33u Escape [98;5u' 'C-b'
|
||||
|
||||
# Lock modifiers are ignored rather than dropping the key.
|
||||
assert_key 'Escape [97;65u' 'a'
|
||||
assert_key 'Escape [97;129u' 'a'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
exit $exit_status
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test the Kitty keyboard protocol against a real terminal rather than against
|
||||
# another copy of tmux. This drives kitty through its remote control interface
|
||||
# so that kitty itself encodes the key presses.
|
||||
#
|
||||
# This has to be run by hand from this directory. The Makefile runs tests with
|
||||
# env -i, which removes DISPLAY and HOME, so kitty cannot start and this exits
|
||||
# immediately without testing anything. It is also skipped unless kitty is
|
||||
# installed and is new enough to have send-key.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
command -v kitty >/dev/null 2>&1 || exit 0
|
||||
[ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ] || exit 0
|
||||
kitty @ --help 2>/dev/null | grep -q 'send-key' || exit 0
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
CONF=$DIR/conf
|
||||
SOCKET=$DIR/kitty
|
||||
OUT=$DIR/out
|
||||
TMUX="$TEST_TMUX -LtestKrt$$ -f$CONF"
|
||||
|
||||
printf '%s\n' 'set -g extended-keys on' \
|
||||
'set -g extended-keys-format kitty' >"$CONF"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
kitty -o allow_remote_control=yes --listen-on "unix:$SOCKET" \
|
||||
-o confirm_os_window_close=0 -e \
|
||||
$TMUX new-session -s r "sleep 120" >"$DIR/err" 2>&1 &
|
||||
|
||||
# The terminfo entry for kitty may be missing, so treat a server which never
|
||||
# appears as a reason to skip rather than as a failure.
|
||||
i=0
|
||||
while ! $TMUX has-session -t r 2>/dev/null && [ "$i" -lt 150 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX has-session -t r 2>/dev/null || exit 0
|
||||
|
||||
# The client must negotiate the protocol with the real terminal.
|
||||
i=0
|
||||
while [ "$($TMUX list-clients -F '#{client_key_mode}')" != 'Kitty 1' ] &&
|
||||
[ "$i" -lt 100 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$($TMUX list-clients -F '#{client_key_mode}')" = 'Kitty 1' ] || exit 1
|
||||
|
||||
# kitty encodes the key, tmux parses it and encodes it again for the pane.
|
||||
try_key()
|
||||
{
|
||||
key=$1
|
||||
expected=$2
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t r: \
|
||||
"stty raw -echo; printf '\033[>1u'; dd bs=1 count=7 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 20" || return 1
|
||||
sleep 1
|
||||
kitty @ --to "unix:$SOCKET" send-key "$key" 2>/dev/null || return 1
|
||||
i=0
|
||||
while [ ! -s "$OUT" ] && [ "$i" -lt 50 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$(tr -d ' \n' <"$OUT")" = "$expected" ]
|
||||
}
|
||||
|
||||
try_key ctrl+a '1b5b39373b3575' || exit 1
|
||||
try_key alt+a '1b5b39373b3375' || exit 1
|
||||
try_key ctrl+shift+a '1b5b39373b3675' || exit 1
|
||||
|
||||
# Super cannot be represented by the standard extended keys at all, so this
|
||||
# only succeeds if the whole chain used the Kitty protocol.
|
||||
try_key super+a '1b5b39373b3975' || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test pane-side Kitty keyboard protocol state and key encoding. This uses
|
||||
# synthetic protocol sequences and does not require a particular terminal.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
CONF=$(mktemp)
|
||||
OUT=$(mktemp)
|
||||
TMUX="$TEST_TMUX -LtestK$$ -f$CONF"
|
||||
|
||||
trap 'rm -f "$CONF" "$OUT"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
printf '%s\n' 'set -g extended-keys on' >"$CONF"
|
||||
|
||||
wait_for_output()
|
||||
{
|
||||
i=0
|
||||
while [ ! -s "$OUT" ] && [ "$i" -lt 50 ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ -s "$OUT" ]
|
||||
}
|
||||
|
||||
wait_for_mode()
|
||||
{
|
||||
wanted=$1
|
||||
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" ]
|
||||
}
|
||||
|
||||
check_output()
|
||||
{
|
||||
expected=$1
|
||||
actual=$(tr -d ' \n' <"$OUT")
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
echo "expected $expected, got $actual"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
$TMUX 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
|
||||
wait_for_mode 'Kitty 1'
|
||||
$TMUX send-keys -t: C-S-a
|
||||
wait_for_output
|
||||
check_output 1b5b39373b3675
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>1u'; dd bs=1 count=7 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_mode 'Kitty 1'
|
||||
$TMUX send-keys -t: s-a
|
||||
wait_for_output
|
||||
check_output 1b5b39373b3975
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>8u'; dd bs=1 count=5 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_mode 'Kitty 8'
|
||||
$TMUX send-keys -t: a
|
||||
wait_for_output
|
||||
check_output 1b5b393775
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>1u\033[?u'; dd bs=1 count=5 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_output
|
||||
check_output 1b5b3f3175
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>4;2m\033[?u'; dd bs=1 count=5 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_output
|
||||
check_output 1b5b3f3075
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>1u\033[>8u\033[<u\033[?u\033[>8u\033[<2u\033[?u'; dd bs=1 count=10 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_output
|
||||
check_output 1b5b3f31751b5b3f3075
|
||||
|
||||
: >"$OUT"
|
||||
# "always" forces modifyOtherKeys mode 1, not Kitty keys.
|
||||
$TMUX set-option -g extended-keys always
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>0u\033[?u'; dd bs=1 count=5 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_output
|
||||
check_output 1b5b3f3075
|
||||
wait_for_mode 'Ext 1'
|
||||
$TMUX set-option -g extended-keys on
|
||||
|
||||
: >"$OUT"
|
||||
$TMUX respawn-pane -k -t: \
|
||||
"stty raw -echo; printf '\033[>1u\033[?u\033[?1049h\033[?u\033[>8u\033[?u\033[?1049l\033[?u'; dd bs=1 count=20 2>/dev/null | od -An -v -t x1 >'$OUT'; sleep 5"
|
||||
wait_for_output
|
||||
check_output 1b5b3f31751b5b3f30751b5b3f38751b5b3f3175
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
@@ -50,7 +50,8 @@
|
||||
# layout it names is a single cell or a split;
|
||||
# - a window whose only tiled pane has been killed, which leaves it with a
|
||||
# floating cell as its layout root or with a root node holding nothing but
|
||||
# floating cells, producing no v1 dump at all, and being parsed as v1;
|
||||
# floating cells, producing an empty v1 body with a checksum, and being
|
||||
# parsed as v1;
|
||||
# - the %layout-change notification, in both formats at once: two control
|
||||
# clients watching one layout change, only one of which has asked for new
|
||||
# layouts, and the number of notifications a change produces in each format;
|
||||
@@ -376,10 +377,7 @@ must_equal 'Layout after select-pane back' "$(layout L:two)" "$SPLIT"
|
||||
|
||||
# With nothing zoomed the two layout formats agree.
|
||||
#
|
||||
# The zoomed case is deliberately not covered here. While a pane is zoomed
|
||||
# #{window_layout} dumps the saved (unzoomed) layout and
|
||||
# #{window_visible_layout} the zoomed one, but that depends on how zooming
|
||||
# stashes the layout root rather than on anything in layout-custom.c.
|
||||
# Zoomed layouts are checked below with floating panes.
|
||||
must_equal 'Visible layout' "$(visible_layout L:two)" "$SPLIT"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -703,6 +701,42 @@ must_contain 'Floating layout back z-index' "$floating" '"z":1'
|
||||
check_ok select-layout -t L:float "$floating"
|
||||
must_equal 'Floating layout after round trip' "$(raw_layout L:float)" "$floating"
|
||||
|
||||
# A dump taken while zoomed must retain the unzoomed floating z-indexes.
|
||||
# Check hidden floats, floats above zoom, and a mixture of the two.
|
||||
for flags in '' A mixed; do
|
||||
check_ok new-window -d -t L: -n zoom
|
||||
zt=$($TMUX display-message -p -t L:zoom '#{pane_id}')
|
||||
check_ok split-window -d -h -t "$zt"
|
||||
case "$flags" in
|
||||
A|mixed) first=-Ad ;;
|
||||
*) first=-d ;;
|
||||
esac
|
||||
case "$flags" in
|
||||
A) second=-Ad ;;
|
||||
*) second=-d ;;
|
||||
esac
|
||||
check_ok new-pane "$first" -t "$zt" -x 20 -y 6 ''
|
||||
check_ok new-pane "$second" -t "$zt" -x 30 -y 8 ''
|
||||
for pane in 0 2 3; do
|
||||
check_ok select-pane -t "L:zoom.$pane"
|
||||
before=$(raw_layout L:zoom)
|
||||
legacy=$(v1_layout L:zoom)
|
||||
check_ok resize-pane -Z -t "L:zoom.$pane"
|
||||
during=$(raw_layout L:zoom)
|
||||
must_equal 'Layout while zoomed' "$during" "$before"
|
||||
must_equal 'Legacy layout while zoomed' \
|
||||
"$(v1_layout L:zoom)" "$legacy"
|
||||
must_equal 'Zoom after dumping layout' \
|
||||
"$($TMUX display-message -p -t L:zoom '#{window_zoomed_flag}')" 1
|
||||
must_differ 'Visible layout while zoomed' \
|
||||
"$(visible_layout L:zoom)" "$(layout L:zoom)"
|
||||
check_ok select-layout -t L:zoom "$during"
|
||||
must_equal 'Round trip from zoomed layout' \
|
||||
"$(raw_layout L:zoom)" "$before"
|
||||
done
|
||||
check_ok kill-window -t L:zoom
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Floating panes and the legacy (v1) format.
|
||||
#
|
||||
@@ -838,12 +872,12 @@ must_equal 'Panes left with one floating pane' \
|
||||
"$($TMUX display-message -p -t L:gone1 '#{window_panes}')" '1'
|
||||
|
||||
# The floating cell is the root and there is nothing tiled under it, so there is
|
||||
# no v1 dump to make. In particular the floating cell must not be written out on
|
||||
# only an empty v1 body to dump. The floating cell must not be written out on
|
||||
# its own, which would be a layout claiming the window is the size and position
|
||||
# of the floating pane with no pane in it at all.
|
||||
got=$(v1_layout L:gone1)
|
||||
check_ok display-message -p alive
|
||||
must_equal 'v1 dump with one floating pane and no tiled panes' "$got" ''
|
||||
must_equal 'v1 dump with one floating pane and no tiled panes' "$got" '0000,'
|
||||
|
||||
# Two floating panes left, so the node keeps two children, does not collapse,
|
||||
# and stays the root with nothing but floating cells in it.
|
||||
@@ -856,11 +890,11 @@ must_equal 'Panes left with two floating panes' \
|
||||
"$($TMUX display-message -p -t L:gone2 '#{window_panes}')" '2'
|
||||
|
||||
# The node is the root this time rather than the floating cell, but it has no
|
||||
# tiled cell anywhere under it either, so there is still no v1 dump to make -
|
||||
# tiled cell anywhere under it either, so the v1 body is still empty -
|
||||
# and making one must not take the server with it.
|
||||
got=$(v1_layout L:gone2)
|
||||
check_ok display-message -p alive
|
||||
must_equal 'v1 dump with two floating panes and no tiled panes' "$got" ''
|
||||
must_equal 'v1 dump with two floating panes and no tiled panes' "$got" '0000,'
|
||||
|
||||
# Nor must parsing a v1 layout against it. There is no tiled pane for the
|
||||
# layout to name, so whether it is applied or rejected is the format's business;
|
||||
|
||||
@@ -86,11 +86,6 @@ check_value "-gv status-keys" "vi"
|
||||
check_fail "unknown value: bogus" set -g status-keys bogus
|
||||
check_value "-gv status-keys" "vi"
|
||||
|
||||
# pane-border-lines accepts rounded as a pane border style.
|
||||
check_ok set -gw pane-border-lines rounded
|
||||
check_value "-gwv pane-border-lines" "rounded"
|
||||
check_ok set -gw pane-border-lines single
|
||||
|
||||
# --- flag options ---------------------------------------------------------
|
||||
#
|
||||
# focus-events is an on/off flag. Setting with no value toggles it; explicit
|
||||
|
||||
@@ -15,7 +15,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/bin/sh
|
||||
|
||||
# run-shell -c should expand formats using the target pane.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
TMP=$(mktemp -d)
|
||||
trap '$TMUX kill-server 2>/dev/null; rm -rf "$TMP"' 0 1 15
|
||||
TMP=$(cd "$TMP" && pwd -P)
|
||||
mkdir "$TMP/first" "$TMP/second dir" || exit 1
|
||||
|
||||
check_directory()
|
||||
{
|
||||
actual=$(cat "$TMP/out")
|
||||
if [ "$actual" != "$1" ]; then
|
||||
echo "Expected directory '$1', got '$actual'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# The start path is available immediately, before the child is scheduled.
|
||||
# Use it to test the format context in the same command queue as creation.
|
||||
$TMUX new-session -d -s test -c "$TMP/first" 'sleep 60' \; \
|
||||
run-shell -c '#{pane_start_path}' "pwd >'$TMP/out'" || exit 1
|
||||
check_directory "$TMP/first"
|
||||
|
||||
# An explicit target must supply the format context, including with a delay.
|
||||
pane=$($TMUX new-window -d -P -F '#{pane_id}' -t test \
|
||||
-c "$TMP/second dir" 'sleep 60') || exit 1
|
||||
# The current path depends on the operating system finding the child process.
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t "$pane" '#{pane_current_path}')" != \
|
||||
"$TMP/second dir" ]; do
|
||||
if [ "$i" -ge 100 ]; then
|
||||
echo "Timed out waiting for pane current directory"
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX run-shell -t "$pane" -d 0.1 -c '#{pane_current_path}' \
|
||||
"pwd >'$TMP/out'" || exit 1
|
||||
check_directory "$TMP/second dir"
|
||||
|
||||
# Literal directories and the default client directory still work.
|
||||
$TMUX run-shell -c "$TMP/second dir" "pwd >'$TMP/out'" || exit 1
|
||||
check_directory "$TMP/second dir"
|
||||
(cd "$TMP/first" && $TMUX run-shell "pwd >'$TMP/out'") || exit 1
|
||||
check_directory "$TMP/first"
|
||||
|
||||
exit 0
|
||||
@@ -107,12 +107,6 @@ $TMUX2 new-pane -x28 -y8 -X4 -Y1 -B double \
|
||||
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
||||
compare floating-border-double
|
||||
|
||||
# Larger floating pane with rounded border lines.
|
||||
new_scene 40 12
|
||||
$TMUX2 new-pane -x28 -y8 -X4 -Y1 -B rounded \
|
||||
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
|
||||
compare floating-border-rounded
|
||||
|
||||
# Floating pane with no border lines: redraw_mark_pane_borders returns early so
|
||||
# the float has no border at all, only its (clipped) content over the base pane.
|
||||
new_scene 40 12
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
base
|
||||
╭──────────────────────────╮
|
||||
│FLOAT │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────╯
|
||||
|
||||
|
||||
|
||||
@@ -95,7 +95,8 @@ layout_tee_down() { # top/bottom, bottom split: a top tee
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
$TMUX2 new -d -x40 -y14 "sh -c 'exec sleep 100'" || exit 1
|
||||
# Keep the anchor window alive until cleanup, even on slow test runs.
|
||||
$TMUX2 new -d -x40 -y14 'exec cat' || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -g window-size manual || exit 1
|
||||
$TMUX2 set -g pane-border-format " #{pane_index} " || exit 1
|
||||
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/bin/sh
|
||||
|
||||
# When only some lines are redrawn after a synchronized update, a line
|
||||
# following a wrapped line must be drawn at its own position, not where the
|
||||
# cursor happens to be.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR=$DIR
|
||||
export TMUX_TMPDIR
|
||||
|
||||
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
||||
CONTROL=$DIR/control
|
||||
EMITTER=$DIR/emitter.pl
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_file()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ] && [ ! -e "$1" ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ -e "$1" ] || fail "$2"
|
||||
}
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
|
||||
grep -q 'sync' && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "sync-capable client did not attach"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $control = $ENV{CONTROL};
|
||||
(my $dir = $control) =~ s{/[^/]+$}{};
|
||||
|
||||
# Fill the screen with 80 column lines and no newlines so every line wraps.
|
||||
my $out = "\e[H\e[2J";
|
||||
for my $row (1 .. 24) {
|
||||
$out .= substr(sprintf("ROW%02d_", $row) . ('x' x 80), 0, 80);
|
||||
}
|
||||
syswrite STDOUT, $out . "\e[1;1H";
|
||||
|
||||
open my $painted, '>', "$dir/painted" or die "$dir/painted: $!\n";
|
||||
close $painted;
|
||||
while (!-e $control) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
|
||||
# Change rows 2 and 6 in one synchronized update.
|
||||
my $frame = "\e[?2026h" .
|
||||
"\e[2;1H" . substr("NEW02_" . ('#' x 80), 0, 80) .
|
||||
"\e[6;1H" . substr("NEW06_" . ('%' x 80), 0, 80) .
|
||||
"\e[24;1H" .
|
||||
"\e[?2026l";
|
||||
my $written = syswrite STDOUT, $frame;
|
||||
die "short synchronized frame write\n"
|
||||
unless defined $written && $written == length $frame;
|
||||
open my $done, '>', "$dir/done" or die "$dir/done: $!\n";
|
||||
close $done;
|
||||
select undef, undef, undef, 3;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 80 -y 24 \
|
||||
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -as terminal-features '*:sync' || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 80 -y 24 \
|
||||
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
wait_for_client
|
||||
|
||||
wait_for_file "$DIR/painted" "application did not paint"
|
||||
sleep 1
|
||||
: >"$CONTROL"
|
||||
wait_for_file "$DIR/done" "application did not finish"
|
||||
sleep 1
|
||||
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/screen" || exit 1
|
||||
sed -n 2p "$DIR/screen" | grep -q '^NEW02_#' || fail "row 2 not redrawn"
|
||||
sed -n 3p "$DIR/screen" | grep -q '^ROW03_x' || fail "row 3 overwritten"
|
||||
sed -n 6p "$DIR/screen" | grep -q '^NEW06_%' || fail "row 6 not redrawn"
|
||||
exit 0
|
||||
+61
-10
@@ -9,14 +9,37 @@ $TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
TMP=$(mktemp) || exit 1
|
||||
prompt_pid=
|
||||
|
||||
cleanup () {
|
||||
if [ -n "$prompt_pid" ]; then
|
||||
kill "$prompt_pid" 2>/dev/null
|
||||
wait "$prompt_pid" 2>/dev/null
|
||||
fi
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -f "$TMP"
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
fail () {
|
||||
echo "[FAIL] $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
$TMUX2 -f/dev/null new -d || exit 1
|
||||
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
|
||||
sleep 1
|
||||
i=0
|
||||
while [ -z "$($TMUX2 list-clients -F '#{client_name}')" ]; do
|
||||
[ "$i" -lt 100 ] || fail "inner client did not attach"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
exit_status=0
|
||||
assertion=0
|
||||
|
||||
format_string () {
|
||||
case $1 in
|
||||
@@ -34,12 +57,33 @@ assert_key () {
|
||||
expected_name=$2
|
||||
format_string=$(format_string "$expected_name")
|
||||
|
||||
$TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
|
||||
sleep 0.05
|
||||
# Use a different prompt each time so a stale redraw cannot look ready.
|
||||
assertion=$((assertion + 1))
|
||||
prompt="tty-keys-$assertion:"
|
||||
$TMUX2 command-prompt -k -p "$prompt" \
|
||||
'display-message -pl '"$format_string" > "$TMP" &
|
||||
prompt_pid=$!
|
||||
i=0
|
||||
while ! $TMUX capture-pane -p | grep -Fq "$prompt"; do
|
||||
kill -0 "$prompt_pid" 2>/dev/null || \
|
||||
fail "$keys -> $expected_name: prompt exited before becoming ready"
|
||||
[ "$i" -lt 100 ] || \
|
||||
fail "$keys -> $expected_name: timed out waiting for prompt"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
$TMUX send-keys $keys
|
||||
$TMUX send-keys $keys || fail "could not send $keys"
|
||||
|
||||
wait
|
||||
i=0
|
||||
while kill -0 "$prompt_pid" 2>/dev/null; do
|
||||
[ "$i" -lt 100 ] || \
|
||||
fail "$keys -> $expected_name: timed out waiting for key"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
wait "$prompt_pid" || fail "$keys -> $expected_name: prompt failed"
|
||||
prompt_pid=
|
||||
|
||||
keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
|
||||
actual_name=$(tr -d '[:space:]' < "$TMP")
|
||||
@@ -293,6 +337,16 @@ assert_key 'Escape [24@' 'C-S-F12'
|
||||
assert_key 'Escape [I' 'FocusIn'
|
||||
assert_key 'Escape [O' 'FocusOut'
|
||||
|
||||
# Focus events do not leave the prefix table.
|
||||
$TMUX2 bind C-c set -g @focus-prefix yes
|
||||
$TMUX2 set -g focus-events on
|
||||
$TMUX send-keys C-b Escape '[O' Escape '[I' C-c
|
||||
sleep 0.05
|
||||
if [ "$($TMUX2 show -gv @focus-prefix)" != yes ]; then
|
||||
echo "[FAIL] FocusIn and FocusOut left the prefix table"
|
||||
exit_status=1
|
||||
fi
|
||||
|
||||
# Paste keys
|
||||
assert_key 'Escape [200~' 'PasteStart'
|
||||
assert_key 'Escape [201~' 'PasteEnd'
|
||||
@@ -355,7 +409,4 @@ assert_key 'Escape [32;2u' 'S-Space'
|
||||
assert_key 'Escape [9;5u' 'C-Tab'
|
||||
assert_key 'Escape [1;5Z' 'C-S-Tab'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
exit $exit_status
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A disconnected waiter must release both its wait and its command queue.
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
export TERM
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX="$TEST_TMUX -S$OUT/socket -f/dev/null"
|
||||
pids=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
for pid in $pids; do
|
||||
kill "$pid" 2>/dev/null || true
|
||||
done
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_list()
|
||||
{
|
||||
expected=$1
|
||||
shift
|
||||
i=0
|
||||
while [ $i -lt 100 ]; do
|
||||
value=$($TMUX wait-for -l "$@") || fail "list waiters failed"
|
||||
[ "$value" = "$expected" ] && return
|
||||
i=$((i + 1))
|
||||
sleep 0.05
|
||||
done
|
||||
fail "expected waiters '$expected', got '$value'"
|
||||
}
|
||||
|
||||
wait_free()
|
||||
{
|
||||
client_pid=$1
|
||||
i=0
|
||||
while [ $i -lt 100 ]; do
|
||||
awk -v pid="$client_pid" '
|
||||
$4 == "IDENTIFY_CLIENTPID" && $5 == pid { address = $3 }
|
||||
address != "" && $2 == "free" && $3 == "client" &&
|
||||
$4 == address && $5 == "(0" { freed = 1 }
|
||||
END { exit !freed }
|
||||
' "$OUT"/tmux-server-*.log && return
|
||||
i=$((i + 1))
|
||||
sleep 0.05
|
||||
done
|
||||
fail "client $client_pid was not freed"
|
||||
}
|
||||
|
||||
cd "$OUT" || exit 1
|
||||
$TMUX -vv new-session -d -s test || fail "new-session failed"
|
||||
$TMUX set -g @continued 0 || fail "set option failed"
|
||||
|
||||
for signal in TERM KILL; do
|
||||
for kind in channel lock event; do
|
||||
name="$kind-$signal"
|
||||
case $kind in
|
||||
channel) args="$name"; list_args="$name" ;;
|
||||
lock)
|
||||
$TMUX wait-for -L "$name" || fail "lock failed"
|
||||
args="-L $name"; list_args="$name"
|
||||
;;
|
||||
event) args="-E @$name"; list_args="-E @$name" ;;
|
||||
esac
|
||||
|
||||
$TMUX wait-for $args \; set -g @continued 1 &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" $list_args
|
||||
|
||||
$TMUX wait-for $args &
|
||||
live=$!
|
||||
pids="$dead $live"
|
||||
wait_list "$(printf 'client-%s\nclient-%s' "$dead" "$live")" \
|
||||
$list_args
|
||||
|
||||
kill -"$signal" "$dead" || fail "kill failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids="$live"
|
||||
wait_list "client-$live" $list_args
|
||||
wait_free "$dead"
|
||||
[ "$($TMUX show -gqv @continued)" = 0 ] ||
|
||||
fail "disconnected client's next command ran"
|
||||
|
||||
case $kind in
|
||||
channel) $TMUX wait-for -S "$name" || fail "signal failed" ;;
|
||||
lock) $TMUX wait-for -U "$name" || fail "unlock failed" ;;
|
||||
event)
|
||||
$TMUX wait-for -E -w "client-$live" "@$name" ||
|
||||
fail "wake event waiter failed"
|
||||
;;
|
||||
esac
|
||||
wait_list "" $list_args
|
||||
wait_free "$live"
|
||||
wait "$live" || fail "surviving waiter failed"
|
||||
pids=
|
||||
if [ "$kind" = lock ]; then
|
||||
$TMUX wait-for -U "$name" || fail "final unlock failed"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Removing the last waiter must remove its otherwise unused channel, and must
|
||||
# not create a pending signal. Unrelated pending signals must be preserved.
|
||||
$TMUX wait-for -S saved || fail "save signal failed"
|
||||
$TMUX wait-for last &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" last
|
||||
kill -KILL "$dead" || fail "kill last waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" last
|
||||
wait_free "$dead"
|
||||
grep -F 'remove empty wait channel last' "$OUT"/tmux-server-*.log \
|
||||
>/dev/null || fail "empty channel was not removed"
|
||||
|
||||
$TMUX wait-for saved \; wait-for last &
|
||||
live=$!
|
||||
pids="$live"
|
||||
wait_list "client-$live" last
|
||||
$TMUX wait-for -S last || fail "signal last failed"
|
||||
wait_free "$live"
|
||||
wait "$live" || fail "pending signal was not preserved"
|
||||
pids=
|
||||
|
||||
# Source-file leaves a cleanup callback behind the blocked command.
|
||||
printf 'wait-for sourced\nset -g @continued 1\n' >"$OUT/source.conf"
|
||||
$TMUX source-file "$OUT/source.conf" &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" sourced
|
||||
kill -KILL "$dead" || fail "kill source-file waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" sourced
|
||||
wait_free "$dead"
|
||||
[ "$($TMUX show -gqv @continued)" = 0 ] ||
|
||||
fail "disconnected source-file client's next command ran"
|
||||
|
||||
# Firing an event after cancellation must not call the removed event sink.
|
||||
$TMUX wait-for -E window-renamed &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" -E window-renamed
|
||||
kill -KILL "$dead" || fail "kill event waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" -E window-renamed
|
||||
wait_free "$dead"
|
||||
$TMUX rename-window -t test:0 renamed || fail "rename-window failed"
|
||||
$TMUX display-message -p '#{pid}' >/dev/null || fail "server exited"
|
||||
|
||||
exit 0
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen-redraw.c,v 1.159 2026/09/09 08:31:42 nicm Exp $ */
|
||||
/* $OpenBSD: screen-redraw.c,v 1.161 2026/09/22 16:56:07 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1673,7 +1673,7 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
|
||||
prompt_draw(wp->prompt, &pdd);
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
tty_draw_line(tty, &screen, 0, offset, width, px, cy, NULL);
|
||||
tty_draw_line(tty, &screen, offset, 0, width, px, cy, NULL);
|
||||
screen_free(&screen);
|
||||
}
|
||||
|
||||
|
||||
@@ -439,7 +439,6 @@ screen_write_reset(struct screen_write_ctx *ctx)
|
||||
|
||||
if (options_get_number(global_options, "extended-keys") == 2)
|
||||
s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
|
||||
input_kitty_reset(s);
|
||||
|
||||
screen_write_clearscreen(ctx, 8);
|
||||
screen_write_set_cursor(ctx, 0, 0);
|
||||
|
||||
@@ -88,8 +88,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
||||
s->default_cstyle = SCREEN_CURSOR_DEFAULT;
|
||||
s->mode = MODE_CURSOR;
|
||||
s->default_mode = 0;
|
||||
memset(&s->kitty_keys, 0, sizeof s->kitty_keys);
|
||||
memset(&s->saved_kitty_keys, 0, sizeof s->saved_kitty_keys);
|
||||
s->ccolour = -1;
|
||||
s->default_ccolour = -1;
|
||||
s->tabs = NULL;
|
||||
@@ -120,9 +118,9 @@ screen_reinit(struct screen *s, int check)
|
||||
|
||||
if (options_get_number(global_options, "extended-keys") == 2)
|
||||
s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
|
||||
|
||||
if (SCREEN_IS_ALTERNATE(s))
|
||||
screen_alternate_off(s, NULL, 0);
|
||||
input_kitty_reset(s);
|
||||
s->saved_cx = UINT_MAX;
|
||||
s->saved_cy = UINT_MAX;
|
||||
|
||||
@@ -700,7 +698,6 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
|
||||
if (SCREEN_IS_ALTERNATE(s))
|
||||
return 0;
|
||||
input_kitty_alternate_on(s);
|
||||
sx = screen_size_x(s);
|
||||
sy = screen_size_y(s);
|
||||
|
||||
@@ -761,7 +758,6 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
s->cy = screen_size_y(s) - 1;
|
||||
return 0;
|
||||
}
|
||||
input_kitty_alternate_off(s);
|
||||
|
||||
/* Restore the saved grid. */
|
||||
grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-client.c,v 1.512 2026/09/10 11:02:18 nicm Exp $ */
|
||||
/* $OpenBSD: server-client.c,v 1.514 2026/09/22 06:46:50 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -502,6 +502,9 @@ server_client_lost(struct client *c)
|
||||
TAILQ_REMOVE(&clients, c, entry);
|
||||
log_debug("lost client %p", c);
|
||||
|
||||
cmd_wait_for_client_lost(c);
|
||||
cmdq_next(c);
|
||||
|
||||
if (c->flags & CLIENT_ATTACHED) {
|
||||
server_client_attached_lost(c);
|
||||
events_fire_client("client-detached", c);
|
||||
@@ -2194,7 +2197,6 @@ server_client_reset_state(struct client *c)
|
||||
log_debug("%s: client %s mode %s", __func__, c->name,
|
||||
screen_mode_to_string(mode));
|
||||
}
|
||||
tty_update_kitty(tty, s);
|
||||
|
||||
/* Reset region and margin. */
|
||||
tty_region_off(tty);
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-fn.c,v 1.153 2026/09/10 11:02:18 nicm Exp $ */
|
||||
/* $OpenBSD: server-fn.c,v 1.154 2026/09/21 12:43:36 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -310,8 +310,8 @@ server_link_window(struct session *src, struct winlink *srcwl,
|
||||
}
|
||||
if (killflag) {
|
||||
/*
|
||||
* Can't use session_detach as it will destroy session
|
||||
* if this makes it empty.
|
||||
* Can't use session_detach as it won't detach the only
|
||||
* window.
|
||||
*/
|
||||
events_fire_winlink("window-unlinked", dstwl);
|
||||
dstwl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: session.c,v 1.107 2026/08/05 07:35:35 nicm Exp $ */
|
||||
/* $OpenBSD: session.c,v 1.108 2026/09/21 12:43:36 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -336,10 +336,17 @@ session_attach(struct session *s, struct window *w, int idx, char **cause)
|
||||
return (wl);
|
||||
}
|
||||
|
||||
/* Detach a window from a session. */
|
||||
/*
|
||||
* Detach a window from a session. Returns 1 if the window has not been
|
||||
* detached - the caller must destroy the session.
|
||||
*/
|
||||
int
|
||||
session_detach(struct session *s, struct winlink *wl)
|
||||
{
|
||||
if (RB_MIN(winlinks, &s->windows) == wl &&
|
||||
RB_MAX(winlinks, &s->windows) == wl)
|
||||
return (1);
|
||||
|
||||
if (s->curw == wl &&
|
||||
session_last(s) != 0 &&
|
||||
session_previous(s, 0) != 0)
|
||||
@@ -352,8 +359,6 @@ session_detach(struct session *s, struct winlink *wl)
|
||||
|
||||
session_group_synchronize_from(s);
|
||||
|
||||
if (RB_EMPTY(&s->windows))
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: spawn.c,v 1.52 2026/08/20 09:19:24 nicm Exp $ */
|
||||
/* $OpenBSD: spawn.c,v 1.53 2026/09/21 12:43:36 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -166,8 +166,8 @@ spawn_window(struct spawn_context *sc, char **cause)
|
||||
}
|
||||
if (wl != NULL) {
|
||||
/*
|
||||
* Can't use session_detach as it will destroy session
|
||||
* if this makes it empty.
|
||||
* Can't use session_detach as it won't detach the only
|
||||
* window.
|
||||
*/
|
||||
wl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
events_fire_winlink("window-unlinked", wl);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: tmux.1,v 1.1170 2026/09/11 10:17:16 nicm Exp $
|
||||
.\" $OpenBSD: tmux.1,v 1.1172 2026/09/22 06:58:05 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: September 11 2026 $
|
||||
.Dd $Mdocdate: September 22 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -4907,30 +4907,14 @@ If enabled, the server will exit when there are no attached clients.
|
||||
.It Xo Ic extended\-keys
|
||||
.Op Ic on | off | always
|
||||
.Xc
|
||||
Controls how modified keys are reported.
|
||||
When enabled,
|
||||
.Nm
|
||||
uses the Kitty keyboard protocol with terminals which support it, and the
|
||||
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 with other terminals which support extended keys.
|
||||
Each client uses the best that its terminal supports, so clients with different
|
||||
terminals may be attached at the same time.
|
||||
resource.
|
||||
.Pp
|
||||
Programs inside panes may request either the Kitty keyboard protocol or
|
||||
.Ic modifyOtherKeys ,
|
||||
and keys are sent to each program in the form it requested, whichever client
|
||||
they were typed on.
|
||||
The Kitty keyboard protocol can report the Super and Hyper modifiers, whose key
|
||||
name prefixes are
|
||||
.Ql s-
|
||||
and
|
||||
.Ql H- ;
|
||||
its disambiguate and report-all enhancements are supported.
|
||||
.Pp
|
||||
For
|
||||
.Ic modifyOtherKeys ,
|
||||
when set to
|
||||
When set to
|
||||
.Ic on ,
|
||||
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
|
||||
@@ -4939,35 +4923,26 @@ When set to
|
||||
.Ic always ,
|
||||
modes 1 and 2 can still be requested by applications, but mode 1 will be forced
|
||||
instead of the standard mode.
|
||||
This applies only to applications inside panes; it does not force the Kitty
|
||||
keyboard protocol or change what
|
||||
.Nm
|
||||
requests from the terminal.
|
||||
When set to
|
||||
.Ic off ,
|
||||
this feature is disabled and only standard keys are reported.
|
||||
.Pp
|
||||
.Nm
|
||||
will always request extended keys itself if the terminal supports them.
|
||||
Support for the Kitty keyboard protocol is detected when a client attaches.
|
||||
See also the
|
||||
.Ic kittykeys
|
||||
and
|
||||
.Ic extkeys
|
||||
features for the
|
||||
feature for the
|
||||
.Ic terminal\-features
|
||||
option, the
|
||||
.Ic extended\-keys\-format
|
||||
option and the
|
||||
.Ic pane_key_mode
|
||||
and
|
||||
.Ic client_key_mode
|
||||
variables.
|
||||
variable.
|
||||
.It Xo Ic extended\-keys\-format
|
||||
.Op Ic csi\-u | xterm | kitty
|
||||
.Op Ic csi\-u | xterm
|
||||
.Xc
|
||||
Selects the format for reporting modified keys to programs which request
|
||||
.Ic modifyOtherKeys .
|
||||
Selects one of the two possible formats for reporting modified keys to
|
||||
applications.
|
||||
This is the equivalent of the
|
||||
.Ic formatOtherKeys
|
||||
.Xr xterm 1
|
||||
@@ -4980,11 +4955,6 @@ and as
|
||||
.Ql \[ha][[65;6u
|
||||
when set to
|
||||
.Ic csi\-u .
|
||||
.Ic kitty
|
||||
is accepted for compatibility and is the same as
|
||||
.Ic csi\-u ;
|
||||
programs which request the Kitty keyboard protocol receive it whatever this
|
||||
option is set to.
|
||||
.It Xo Ic focus\-events
|
||||
.Op Ic on | off
|
||||
.Xc
|
||||
@@ -5115,6 +5085,9 @@ The available features are:
|
||||
.Bl -tag -width Ds
|
||||
.It 256
|
||||
Supports 256 colours with the SGR escape sequences.
|
||||
.It appesc
|
||||
Supports application escape key mode, where the Escape key sends a sequence
|
||||
that cannot be mistaken for the start of another key.
|
||||
.It clipboard
|
||||
Allows setting the system clipboard.
|
||||
.It ccolour
|
||||
@@ -5133,9 +5106,6 @@ Ignore function keys from
|
||||
and use the
|
||||
.Nm
|
||||
internal set only.
|
||||
.It kittykeys
|
||||
Supports the Kitty keyboard protocol.
|
||||
This is added automatically if the terminal answers the Kitty keyboard query.
|
||||
.It margins
|
||||
Supports DECSLRM margins.
|
||||
.It mouse
|
||||
@@ -6013,8 +5983,6 @@ single lines using ACS or UTF\-8 characters
|
||||
double lines using UTF\-8 characters
|
||||
.It heavy
|
||||
heavy lines using UTF\-8 characters
|
||||
.It rounded
|
||||
single lines with rounded corners using UTF\-8 characters
|
||||
.It simple
|
||||
simple ASCII characters
|
||||
.It number
|
||||
@@ -7342,7 +7310,6 @@ The following variables are available, where appropriate:
|
||||
.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
|
||||
.It Li "client_flags" Ta "" Ta "List of client flags"
|
||||
.It Li "client_height" Ta "" Ta "Height of client"
|
||||
.It Li "client_key_mode" Ta "" Ta "Extended key reporting mode for this client"
|
||||
.It Li "client_key_table" Ta "" Ta "Current key table"
|
||||
.It Li "client_last_session" Ta "" Ta "Name of the client's last session"
|
||||
.It Li "client_name" Ta "" Ta "Name of client"
|
||||
@@ -9004,6 +8971,8 @@ These are set automatically if the
|
||||
capability is present.
|
||||
.It Em \&Dseks , \&Eneks
|
||||
Disable and enable extended keys.
|
||||
.It Em \&Dsesc , \&Enesc
|
||||
Disable and enable application escape key mode.
|
||||
.It Em \&Dsfcs , \&Enfcs
|
||||
Disable and enable focus reporting.
|
||||
These are set automatically if the
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux.h,v 1.1440 2026/09/11 10:17:16 nicm Exp $ */
|
||||
/* $OpenBSD: tmux.h,v 1.1447 2026/09/22 14:10:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -142,8 +142,6 @@ struct winlink;
|
||||
#define VISUAL_BOTH 2
|
||||
|
||||
/* Key modifier bits. */
|
||||
#define KEYC_SUPER 0x00010000000000ULL
|
||||
#define KEYC_HYPER 0x00020000000000ULL
|
||||
#define KEYC_META 0x00100000000000ULL
|
||||
#define KEYC_CTRL 0x00200000000000ULL
|
||||
#define KEYC_SHIFT 0x00400000000000ULL
|
||||
@@ -376,29 +374,6 @@ enum {
|
||||
KEYC_F10,
|
||||
KEYC_F11,
|
||||
KEYC_F12,
|
||||
KEYC_F13,
|
||||
KEYC_F14,
|
||||
KEYC_F15,
|
||||
KEYC_F16,
|
||||
KEYC_F17,
|
||||
KEYC_F18,
|
||||
KEYC_F19,
|
||||
KEYC_F20,
|
||||
KEYC_F21,
|
||||
KEYC_F22,
|
||||
KEYC_F23,
|
||||
KEYC_F24,
|
||||
KEYC_F25,
|
||||
KEYC_F26,
|
||||
KEYC_F27,
|
||||
KEYC_F28,
|
||||
KEYC_F29,
|
||||
KEYC_F30,
|
||||
KEYC_F31,
|
||||
KEYC_F32,
|
||||
KEYC_F33,
|
||||
KEYC_F34,
|
||||
KEYC_F35,
|
||||
KEYC_IC,
|
||||
KEYC_DC,
|
||||
KEYC_HOME,
|
||||
@@ -430,54 +405,6 @@ enum {
|
||||
KEYC_KP_ENTER,
|
||||
KEYC_KP_ZERO,
|
||||
KEYC_KP_PERIOD,
|
||||
KEYC_KP_EQUAL,
|
||||
KEYC_KP_SEPARATOR,
|
||||
KEYC_KP_LEFT,
|
||||
KEYC_KP_RIGHT,
|
||||
KEYC_KP_UP,
|
||||
KEYC_KP_DOWN,
|
||||
KEYC_KP_PPAGE,
|
||||
KEYC_KP_NPAGE,
|
||||
KEYC_KP_HOME,
|
||||
KEYC_KP_END,
|
||||
KEYC_KP_IC,
|
||||
KEYC_KP_DC,
|
||||
KEYC_KP_BEGIN,
|
||||
|
||||
/* Other function keys. */
|
||||
KEYC_CAPSLOCK,
|
||||
KEYC_SCROLLLOCK,
|
||||
KEYC_NUMLOCK,
|
||||
KEYC_PRINTSCREEN,
|
||||
KEYC_PAUSE,
|
||||
KEYC_MENU,
|
||||
KEYC_MEDIA_PLAY,
|
||||
KEYC_MEDIA_PAUSE,
|
||||
KEYC_MEDIA_PLAYPAUSE,
|
||||
KEYC_MEDIA_REVERSE,
|
||||
KEYC_MEDIA_STOP,
|
||||
KEYC_MEDIA_FASTFORWARD,
|
||||
KEYC_MEDIA_REWIND,
|
||||
KEYC_MEDIA_NEXT,
|
||||
KEYC_MEDIA_PREVIOUS,
|
||||
KEYC_MEDIA_RECORD,
|
||||
KEYC_VOLUME_DOWN,
|
||||
KEYC_VOLUME_UP,
|
||||
KEYC_VOLUME_MUTE,
|
||||
KEYC_LEFT_SHIFT,
|
||||
KEYC_LEFT_CTRL,
|
||||
KEYC_LEFT_ALT,
|
||||
KEYC_LEFT_SUPER,
|
||||
KEYC_LEFT_HYPER,
|
||||
KEYC_LEFT_META,
|
||||
KEYC_RIGHT_SHIFT,
|
||||
KEYC_RIGHT_CTRL,
|
||||
KEYC_RIGHT_ALT,
|
||||
KEYC_RIGHT_SUPER,
|
||||
KEYC_RIGHT_HYPER,
|
||||
KEYC_RIGHT_META,
|
||||
KEYC_ISO_LEVEL3_SHIFT,
|
||||
KEYC_ISO_LEVEL5_SHIFT,
|
||||
|
||||
/* Theme reporting. */
|
||||
KEYC_REPORT_DARK_THEME,
|
||||
@@ -537,6 +464,7 @@ enum tty_code_code {
|
||||
TTYC_DL1,
|
||||
TTYC_DSBP,
|
||||
TTYC_DSEKS,
|
||||
TTYC_DSESC,
|
||||
TTYC_DSFCS,
|
||||
TTYC_DSMG,
|
||||
TTYC_E3,
|
||||
@@ -547,6 +475,7 @@ enum tty_code_code {
|
||||
TTYC_ENACS,
|
||||
TTYC_ENBP,
|
||||
TTYC_ENEKS,
|
||||
TTYC_ENESC,
|
||||
TTYC_ENFCS,
|
||||
TTYC_ENMG,
|
||||
TTYC_FSL,
|
||||
@@ -775,16 +704,6 @@ enum tty_code_code {
|
||||
#define CURSOR_MODES (MODE_CURSOR|MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)
|
||||
#define EXTENDED_KEY_MODES (MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2)
|
||||
|
||||
/* Extended key output formats. */
|
||||
#define EXTENDED_KEYS_CSI_U 0
|
||||
#define EXTENDED_KEYS_XTERM 1
|
||||
#define EXTENDED_KEYS_KITTY 2
|
||||
|
||||
/* Supported Kitty keyboard protocol flags. */
|
||||
#define KITTY_KEY_DISAMBIGUATE 0x1
|
||||
#define KITTY_KEY_REPORT_ALL 0x8
|
||||
#define KITTY_KEY_SUPPORTED (KITTY_KEY_DISAMBIGUATE|KITTY_KEY_REPORT_ALL)
|
||||
|
||||
/* Mouse protocol constants. */
|
||||
#define MOUSE_PARAM_MAX 0xff
|
||||
#define MOUSE_PARAM_UTF8_MAX 0x7ff
|
||||
@@ -1151,11 +1070,6 @@ struct progress_bar {
|
||||
/* Virtual screen. */
|
||||
struct screen_sel;
|
||||
struct screen_titles;
|
||||
struct kitty_key_state {
|
||||
u_int flags;
|
||||
u_int saved_flags;
|
||||
int have_saved;
|
||||
};
|
||||
struct screen {
|
||||
char *title;
|
||||
char *path;
|
||||
@@ -1177,8 +1091,6 @@ struct screen {
|
||||
|
||||
int mode;
|
||||
int default_mode;
|
||||
struct kitty_key_state kitty_keys;
|
||||
struct kitty_key_state saved_kitty_keys;
|
||||
|
||||
u_int saved_cx;
|
||||
u_int saved_cy;
|
||||
@@ -1240,8 +1152,7 @@ enum pane_lines {
|
||||
PANE_LINES_SIMPLE,
|
||||
PANE_LINES_NUMBER,
|
||||
PANE_LINES_SPACES,
|
||||
PANE_LINES_NONE,
|
||||
PANE_LINES_ROUNDED
|
||||
PANE_LINES_NONE
|
||||
};
|
||||
|
||||
/* Pane border indicator option. */
|
||||
@@ -1829,7 +1740,7 @@ struct tty_term {
|
||||
#define TERM_VT100LIKE 0x20
|
||||
#define TERM_SIXEL 0x40
|
||||
#define TERM_INVALIDMS 0x80
|
||||
#define TERM_KITTYKEYS 0x100
|
||||
#define TERM_NOREPLACE 0x100
|
||||
int flags;
|
||||
|
||||
LIST_ENTRY(tty_term) entry;
|
||||
@@ -1910,12 +1821,9 @@ struct tty {
|
||||
#define TTY_WAITBG 0x4000
|
||||
#define TTY_BRACKETPASTE 0x8000
|
||||
#define TTY_HAVESYNC 0x10000
|
||||
#define TTY_HAVEKKB 0x20000
|
||||
#define TTY_KKBPUSHED 0x40000
|
||||
#define TTY_ALL_REQUEST_FLAGS \
|
||||
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVESYNC|TTY_HAVEKKB)
|
||||
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVESYNC)
|
||||
int flags;
|
||||
u_int kitty_keys;
|
||||
|
||||
struct tty_term *term;
|
||||
|
||||
@@ -2398,7 +2306,7 @@ struct client {
|
||||
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
|
||||
#define CLIENT_WRITE_ACK 0x4000000000ULL
|
||||
#define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL
|
||||
#define CLIENT_CONTROL_DISCARD 0x1000000000ULL
|
||||
#define CLIENT_CONTROL_DISCARD 0x10000000000ULL
|
||||
#define CLIENT_ALLREDRAWFLAGS \
|
||||
(CLIENT_REDRAWWINDOW| \
|
||||
CLIENT_REDRAWSTATUS| \
|
||||
@@ -3068,7 +2976,6 @@ int tty_open(struct tty *, char **);
|
||||
void tty_close(struct tty *);
|
||||
void tty_free(struct tty *);
|
||||
void tty_update_features(struct tty *);
|
||||
void tty_update_kitty(struct tty *, struct screen *);
|
||||
void tty_set_selection(struct tty *, const char *, const char *, size_t);
|
||||
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
|
||||
struct tty_ctx *);
|
||||
@@ -3103,7 +3010,7 @@ void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
|
||||
/* tty-term.c */
|
||||
extern struct tty_terms tty_terms;
|
||||
u_int tty_term_ncodes(void);
|
||||
void tty_term_apply(struct tty_term *, const char *, int);
|
||||
void tty_term_apply(struct tty_term *, const char *, int, int);
|
||||
void tty_term_apply_overrides(struct tty_term *);
|
||||
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, char **);
|
||||
void tty_term_free(struct tty_term *);
|
||||
@@ -3323,6 +3230,7 @@ void cmdq_print_data(struct cmdq_item *, struct evbuffer *);
|
||||
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
|
||||
|
||||
/* cmd-wait-for.c */
|
||||
void cmd_wait_for_client_lost(struct client *);
|
||||
void cmd_wait_for_flush(void);
|
||||
|
||||
/* client.c */
|
||||
@@ -3550,20 +3458,6 @@ int input_key(struct screen *, struct bufferevent *, key_code);
|
||||
int input_key_get_mouse(struct screen *, struct mouse_event *, u_int,
|
||||
u_int, const char **, size_t *);
|
||||
|
||||
/* input-kitty.c */
|
||||
void input_kitty_reset(struct screen *);
|
||||
void input_kitty_alternate_on(struct screen *);
|
||||
void input_kitty_alternate_off(struct screen *);
|
||||
void input_kitty_set(struct screen *, u_int, int);
|
||||
void input_kitty_push(struct screen *, u_int);
|
||||
void input_kitty_pop(struct screen *, u_int);
|
||||
int input_key_kitty(struct screen *, struct bufferevent *, key_code);
|
||||
|
||||
/* tty-kitty.c */
|
||||
int tty_keys_kitty(struct tty *, const char *, size_t, size_t *,
|
||||
key_code *);
|
||||
int tty_keys_kitty_query(struct tty *, const char *, size_t, size_t *);
|
||||
|
||||
/* colour.c */
|
||||
int colour_find_rgb(u_char, u_char, u_char);
|
||||
int colour_join_rgb(u_char, u_char, u_char);
|
||||
@@ -3943,6 +3837,7 @@ int window_pane_get_pane_status(struct window_pane *);
|
||||
struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
|
||||
u_int);
|
||||
int window_pane_is_floating(struct window_pane *);
|
||||
int window_pane_is_floating_with_hidden(struct window_pane *);
|
||||
|
||||
/* window-border.c */
|
||||
void window_set_fill_cells(struct window *);
|
||||
|
||||
+7
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-draw.c,v 1.15 2026/07/26 09:02:08 nicm Exp $ */
|
||||
/* $OpenBSD: tty-draw.c,v 1.17 2026/09/22 06:49:47 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -219,7 +219,12 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
}
|
||||
|
||||
/* Did the previous line wrap on to this one? */
|
||||
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
|
||||
if (py != 0 &&
|
||||
atx == 0 &&
|
||||
tty->cx >= tty->sx &&
|
||||
tty->cy != UINT_MAX &&
|
||||
tty->cy + 1 == aty &&
|
||||
nx == tty->sx) {
|
||||
gl = grid_get_line(gd, gd->hsize + py - 1);
|
||||
if (gl->flags & GRID_LINE_WRAPPED)
|
||||
wrapped = 1;
|
||||
|
||||
+18
-12
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-features.c,v 1.43 2026/08/31 12:41:03 kirill Exp $ */
|
||||
/* $OpenBSD: tty-features.c,v 1.45 2026/09/22 14:10:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -189,6 +189,18 @@ static const struct tty_feature tty_feature_focus = {
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports application escape key mode. */
|
||||
static const char *const tty_feature_appesc_capabilities[] = {
|
||||
"Enesc=\\E[?7727h",
|
||||
"Dsesc=\\E[?7727l",
|
||||
NULL
|
||||
};
|
||||
static const struct tty_feature tty_feature_appesc = {
|
||||
"appesc",
|
||||
tty_feature_appesc_capabilities,
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports cursor styles. */
|
||||
static const char *const tty_feature_cstyle_capabilities[] = {
|
||||
"Ss=\\E[%p1%d q",
|
||||
@@ -198,7 +210,7 @@ static const char *const tty_feature_cstyle_capabilities[] = {
|
||||
static const struct tty_feature tty_feature_cstyle = {
|
||||
"cstyle",
|
||||
tty_feature_cstyle_capabilities,
|
||||
0
|
||||
TERM_NOREPLACE
|
||||
};
|
||||
|
||||
/* Terminal supports cursor colours. */
|
||||
@@ -235,13 +247,6 @@ static const struct tty_feature tty_feature_sync = {
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports the Kitty keyboard protocol. */
|
||||
static const struct tty_feature tty_feature_kittykeys = {
|
||||
"kittykeys",
|
||||
NULL,
|
||||
TERM_KITTYKEYS
|
||||
};
|
||||
|
||||
/* Terminal supports extended keys. */
|
||||
static const char *const tty_feature_extkeys_capabilities[] = {
|
||||
"Eneks=\\E[>4;2m",
|
||||
@@ -385,6 +390,7 @@ static const struct tty_feature tty_feature_utf8 = {
|
||||
/* Available terminal features. */
|
||||
static const struct tty_feature *const tty_features[] = {
|
||||
&tty_feature_256,
|
||||
&tty_feature_appesc,
|
||||
&tty_feature_bpaste,
|
||||
&tty_feature_ccolour,
|
||||
&tty_feature_clipboard,
|
||||
@@ -393,7 +399,6 @@ static const struct tty_feature *const tty_features[] = {
|
||||
&tty_feature_extkeys,
|
||||
&tty_feature_focus,
|
||||
&tty_feature_ignorefkeys,
|
||||
&tty_feature_kittykeys,
|
||||
&tty_feature_margins,
|
||||
&tty_feature_mouse,
|
||||
&tty_feature_osc7,
|
||||
@@ -549,11 +554,11 @@ tty_apply_features(struct tty_term *term)
|
||||
capability = tf->capabilities;
|
||||
while (*capability != NULL) {
|
||||
log_debug("adding capability: %s", *capability);
|
||||
tty_term_apply(term, *capability, 1);
|
||||
tty_term_apply(term, *capability, 1, tf->flags);
|
||||
capability++;
|
||||
}
|
||||
}
|
||||
term->flags |= tf->flags;
|
||||
term->flags |= (tf->flags & ~TERM_NOREPLACE);
|
||||
if (tf == &tty_feature_utf8)
|
||||
c->flags |= CLIENT_UTF8;
|
||||
}
|
||||
@@ -576,6 +581,7 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"256,RGB,bpaste,clipboard,mouse,strikethrough,title"
|
||||
{ .name = "mintty",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
"appesc,"
|
||||
"ccolour,"
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
|
||||
-27
@@ -772,17 +772,6 @@ tty_keys_next(struct tty *tty)
|
||||
goto partial_key;
|
||||
}
|
||||
|
||||
/* Is this a Kitty keyboard protocol response? */
|
||||
switch (tty_keys_kitty_query(tty, buf, len, &size)) {
|
||||
case 0: /* yes */
|
||||
key = KEYC_UNKNOWN;
|
||||
goto complete_key;
|
||||
case -1: /* no, or not valid */
|
||||
break;
|
||||
case 1: /* partial */
|
||||
goto partial_key;
|
||||
}
|
||||
|
||||
/* Is this a synchronized update mode response? */
|
||||
switch (tty_keys_sync(tty, buf, len, &size)) {
|
||||
case 0: /* yes */
|
||||
@@ -869,18 +858,6 @@ tty_keys_next(struct tty *tty)
|
||||
goto partial_key;
|
||||
}
|
||||
|
||||
/* Is this an extended key press? */
|
||||
switch (tty_keys_kitty(tty, buf, len, &size, &key)) {
|
||||
case 0: /* yes */
|
||||
goto complete_key;
|
||||
case -1: /* no, or not valid */
|
||||
break;
|
||||
case -2: /* yes, but not representable by tmux */
|
||||
goto discard_key;
|
||||
case 1: /* partial */
|
||||
goto partial_key;
|
||||
}
|
||||
|
||||
/* Is this an extended key press? */
|
||||
switch (tty_keys_extended_key(tty, buf, len, &size, &key)) {
|
||||
case 0: /* yes */
|
||||
@@ -1112,8 +1089,6 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
|
||||
utf8_char uc;
|
||||
|
||||
*size = 0;
|
||||
if (tty->flags & TTY_KKBPUSHED)
|
||||
return (-1);
|
||||
|
||||
/* First two bytes are always \033[. */
|
||||
if (buf[0] != '\033')
|
||||
@@ -1552,8 +1527,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
|
||||
}
|
||||
log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf);
|
||||
|
||||
/* A DA response without a preceding keyboard response means no support. */
|
||||
tty->flags |= TTY_HAVEKKB;
|
||||
tty_update_features(tty);
|
||||
tty->flags |= TTY_HAVEDA;
|
||||
|
||||
|
||||
-430
@@ -1,430 +0,0 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Detect support in the outside terminal and decode its key sequences. */
|
||||
|
||||
struct tty_kitty_key {
|
||||
u_int number;
|
||||
key_code key;
|
||||
};
|
||||
|
||||
static const struct tty_kitty_key tty_kitty_keys[] = {
|
||||
{ 57358, KEYC_CAPSLOCK },
|
||||
{ 57359, KEYC_SCROLLLOCK },
|
||||
{ 57360, KEYC_NUMLOCK },
|
||||
{ 57361, KEYC_PRINTSCREEN },
|
||||
{ 57362, KEYC_PAUSE },
|
||||
{ 57363, KEYC_MENU },
|
||||
{ 57376, KEYC_F13 },
|
||||
{ 57377, KEYC_F14 },
|
||||
{ 57378, KEYC_F15 },
|
||||
{ 57379, KEYC_F16 },
|
||||
{ 57380, KEYC_F17 },
|
||||
{ 57381, KEYC_F18 },
|
||||
{ 57382, KEYC_F19 },
|
||||
{ 57383, KEYC_F20 },
|
||||
{ 57384, KEYC_F21 },
|
||||
{ 57385, KEYC_F22 },
|
||||
{ 57386, KEYC_F23 },
|
||||
{ 57387, KEYC_F24 },
|
||||
{ 57388, KEYC_F25 },
|
||||
{ 57389, KEYC_F26 },
|
||||
{ 57390, KEYC_F27 },
|
||||
{ 57391, KEYC_F28 },
|
||||
{ 57392, KEYC_F29 },
|
||||
{ 57393, KEYC_F30 },
|
||||
{ 57394, KEYC_F31 },
|
||||
{ 57395, KEYC_F32 },
|
||||
{ 57396, KEYC_F33 },
|
||||
{ 57397, KEYC_F34 },
|
||||
{ 57398, KEYC_F35 },
|
||||
{ 57399, KEYC_KP_ZERO|KEYC_KEYPAD },
|
||||
{ 57400, KEYC_KP_ONE|KEYC_KEYPAD },
|
||||
{ 57401, KEYC_KP_TWO|KEYC_KEYPAD },
|
||||
{ 57402, KEYC_KP_THREE|KEYC_KEYPAD },
|
||||
{ 57403, KEYC_KP_FOUR|KEYC_KEYPAD },
|
||||
{ 57404, KEYC_KP_FIVE|KEYC_KEYPAD },
|
||||
{ 57405, KEYC_KP_SIX|KEYC_KEYPAD },
|
||||
{ 57406, KEYC_KP_SEVEN|KEYC_KEYPAD },
|
||||
{ 57407, KEYC_KP_EIGHT|KEYC_KEYPAD },
|
||||
{ 57408, KEYC_KP_NINE|KEYC_KEYPAD },
|
||||
{ 57409, KEYC_KP_PERIOD|KEYC_KEYPAD },
|
||||
{ 57410, KEYC_KP_SLASH|KEYC_KEYPAD },
|
||||
{ 57411, KEYC_KP_STAR|KEYC_KEYPAD },
|
||||
{ 57412, KEYC_KP_MINUS|KEYC_KEYPAD },
|
||||
{ 57413, KEYC_KP_PLUS|KEYC_KEYPAD },
|
||||
{ 57414, KEYC_KP_ENTER|KEYC_KEYPAD },
|
||||
{ 57415, KEYC_KP_EQUAL|KEYC_KEYPAD },
|
||||
{ 57416, KEYC_KP_SEPARATOR|KEYC_KEYPAD },
|
||||
{ 57417, KEYC_KP_LEFT|KEYC_KEYPAD },
|
||||
{ 57418, KEYC_KP_RIGHT|KEYC_KEYPAD },
|
||||
{ 57419, KEYC_KP_UP|KEYC_KEYPAD },
|
||||
{ 57420, KEYC_KP_DOWN|KEYC_KEYPAD },
|
||||
{ 57421, KEYC_KP_PPAGE|KEYC_KEYPAD },
|
||||
{ 57422, KEYC_KP_NPAGE|KEYC_KEYPAD },
|
||||
{ 57423, KEYC_KP_HOME|KEYC_KEYPAD },
|
||||
{ 57424, KEYC_KP_END|KEYC_KEYPAD },
|
||||
{ 57425, KEYC_KP_IC|KEYC_KEYPAD },
|
||||
{ 57426, KEYC_KP_DC|KEYC_KEYPAD },
|
||||
{ 57427, KEYC_KP_BEGIN|KEYC_KEYPAD },
|
||||
{ 57428, KEYC_MEDIA_PLAY },
|
||||
{ 57429, KEYC_MEDIA_PAUSE },
|
||||
{ 57430, KEYC_MEDIA_PLAYPAUSE },
|
||||
{ 57431, KEYC_MEDIA_REVERSE },
|
||||
{ 57432, KEYC_MEDIA_STOP },
|
||||
{ 57433, KEYC_MEDIA_FASTFORWARD },
|
||||
{ 57434, KEYC_MEDIA_REWIND },
|
||||
{ 57435, KEYC_MEDIA_NEXT },
|
||||
{ 57436, KEYC_MEDIA_PREVIOUS },
|
||||
{ 57437, KEYC_MEDIA_RECORD },
|
||||
{ 57438, KEYC_VOLUME_DOWN },
|
||||
{ 57439, KEYC_VOLUME_UP },
|
||||
{ 57440, KEYC_VOLUME_MUTE },
|
||||
{ 57441, KEYC_LEFT_SHIFT },
|
||||
{ 57442, KEYC_LEFT_CTRL },
|
||||
{ 57443, KEYC_LEFT_ALT },
|
||||
{ 57444, KEYC_LEFT_SUPER },
|
||||
{ 57445, KEYC_LEFT_HYPER },
|
||||
{ 57446, KEYC_LEFT_META },
|
||||
{ 57447, KEYC_RIGHT_SHIFT },
|
||||
{ 57448, KEYC_RIGHT_CTRL },
|
||||
{ 57449, KEYC_RIGHT_ALT },
|
||||
{ 57450, KEYC_RIGHT_SUPER },
|
||||
{ 57451, KEYC_RIGHT_HYPER },
|
||||
{ 57452, KEYC_RIGHT_META },
|
||||
{ 57453, KEYC_ISO_LEVEL3_SHIFT },
|
||||
{ 57454, KEYC_ISO_LEVEL5_SHIFT }
|
||||
};
|
||||
|
||||
static int
|
||||
tty_kitty_parse_number(const char *s, u_int *value, int empty)
|
||||
{
|
||||
char *end;
|
||||
unsigned long number;
|
||||
|
||||
if (*s == '\0') {
|
||||
if (!empty)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
if (!isdigit((u_char)*s))
|
||||
return (-1);
|
||||
number = strtoul(s, &end, 10);
|
||||
if (*end != '\0' || number > UINT_MAX)
|
||||
return (-1);
|
||||
*value = number;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tty_kitty_parse_list(char *s, u_int *first, u_int *second, int empty,
|
||||
u_int maximum)
|
||||
{
|
||||
char *copy, *item;
|
||||
u_int n = 0, value;
|
||||
int result = 0;
|
||||
|
||||
copy = s;
|
||||
while ((item = strsep(©, ":")) != NULL) {
|
||||
if (maximum != 0 && n == maximum)
|
||||
return (-1);
|
||||
value = 0;
|
||||
if (tty_kitty_parse_number(item, &value, empty) != 0) {
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if (n == 0 && *item != '\0')
|
||||
*first = value;
|
||||
else if (n == 1 && *item != '\0')
|
||||
*second = value;
|
||||
n++;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
static int
|
||||
tty_kitty_find_end(const char *buf, size_t len, size_t *end)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 2; i < len; i++) {
|
||||
if (buf[i] == 'u' || buf[i] == '~' || buf[i] == 'A' ||
|
||||
buf[i] == 'B' || buf[i] == 'C' || buf[i] == 'D' ||
|
||||
buf[i] == 'E' || buf[i] == 'F' || buf[i] == 'H' ||
|
||||
buf[i] == 'P' || buf[i] == 'Q' || buf[i] == 'S') {
|
||||
*end = i;
|
||||
return (0);
|
||||
}
|
||||
if (!isdigit((u_char)buf[i]) && buf[i] != ';' && buf[i] != ':')
|
||||
return (-1);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static key_code
|
||||
tty_kitty_function(u_int number, char final)
|
||||
{
|
||||
u_int i;
|
||||
|
||||
if (final == 'u') {
|
||||
switch (number) {
|
||||
case 9:
|
||||
return (C0_HT);
|
||||
case 13:
|
||||
return (C0_CR);
|
||||
case 27:
|
||||
return (C0_ESC);
|
||||
case 127:
|
||||
return (KEYC_BSPACE);
|
||||
}
|
||||
for (i = 0; i < nitems(tty_kitty_keys); i++) {
|
||||
if (tty_kitty_keys[i].number == number)
|
||||
return (tty_kitty_keys[i].key);
|
||||
}
|
||||
return (KEYC_NONE);
|
||||
}
|
||||
|
||||
if (final == '~') {
|
||||
switch (number) {
|
||||
case 2: return (KEYC_IC);
|
||||
case 3: return (KEYC_DC);
|
||||
case 5: return (KEYC_PPAGE);
|
||||
case 6: return (KEYC_NPAGE);
|
||||
case 7: return (KEYC_HOME);
|
||||
case 8: return (KEYC_END);
|
||||
case 11: return (KEYC_F1);
|
||||
case 12: return (KEYC_F2);
|
||||
case 13: return (KEYC_F3);
|
||||
case 14: return (KEYC_F4);
|
||||
case 15: return (KEYC_F5);
|
||||
case 17: return (KEYC_F6);
|
||||
case 18: return (KEYC_F7);
|
||||
case 19: return (KEYC_F8);
|
||||
case 20: return (KEYC_F9);
|
||||
case 21: return (KEYC_F10);
|
||||
case 23: return (KEYC_F11);
|
||||
case 24: return (KEYC_F12);
|
||||
case 29: return (KEYC_MENU);
|
||||
case 57427: return (KEYC_KP_BEGIN|KEYC_KEYPAD);
|
||||
}
|
||||
return (KEYC_UNKNOWN);
|
||||
}
|
||||
if (number != 1)
|
||||
return (KEYC_UNKNOWN);
|
||||
switch (final) {
|
||||
case 'A': return (KEYC_UP|KEYC_CURSOR);
|
||||
case 'B': return (KEYC_DOWN|KEYC_CURSOR);
|
||||
case 'C': return (KEYC_RIGHT|KEYC_CURSOR);
|
||||
case 'D': return (KEYC_LEFT|KEYC_CURSOR);
|
||||
case 'E': return (KEYC_KP_BEGIN|KEYC_KEYPAD);
|
||||
case 'F': return (KEYC_END);
|
||||
case 'H': return (KEYC_HOME);
|
||||
case 'P': return (KEYC_F1);
|
||||
case 'Q': return (KEYC_F2);
|
||||
case 'S': return (KEYC_F4);
|
||||
}
|
||||
return (KEYC_UNKNOWN);
|
||||
}
|
||||
|
||||
int
|
||||
tty_keys_kitty_query(struct tty *tty, const char *buf, size_t len,
|
||||
size_t *size)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
size_t i;
|
||||
u_int flags = 0;
|
||||
|
||||
*size = 0;
|
||||
if (tty->flags & TTY_HAVEKKB)
|
||||
return (-1);
|
||||
|
||||
if (buf[0] != '\033')
|
||||
return (-1);
|
||||
if (len == 1)
|
||||
return (1);
|
||||
if (buf[1] != '[')
|
||||
return (-1);
|
||||
if (len == 2)
|
||||
return (1);
|
||||
if (buf[2] != '?')
|
||||
return (-1);
|
||||
if (len == 3)
|
||||
return (1);
|
||||
|
||||
for (i = 3; i < len && isdigit((u_char)buf[i]); i++) {
|
||||
if (flags > (UINT_MAX - (buf[i] - '0')) / 10)
|
||||
return (-1);
|
||||
flags = (flags * 10) + (buf[i] - '0');
|
||||
}
|
||||
if (i == len)
|
||||
return (1);
|
||||
if (i == 3 || buf[i] != 'u')
|
||||
return (-1);
|
||||
|
||||
*size = i + 1;
|
||||
tty->kitty_keys = flags;
|
||||
tty->flags |= TTY_HAVEKKB;
|
||||
log_debug("%s: received Kitty keyboard flags %u", c->name, flags);
|
||||
tty_parse_client_features(c, "kittykeys", ",");
|
||||
tty_update_features(tty);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
tty_update_kitty(struct tty *tty, struct screen *s)
|
||||
{
|
||||
u_int flags;
|
||||
char buf[32];
|
||||
|
||||
if (options_get_number(global_options, "extended-keys") == 0 ||
|
||||
(~tty->term->flags & TERM_KITTYKEYS)) {
|
||||
if (tty->flags & TTY_KKBPUSHED) {
|
||||
tty_puts(tty, "\033[<u");
|
||||
tty->flags &= ~TTY_KKBPUSHED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
flags = KITTY_KEY_DISAMBIGUATE;
|
||||
if (s != NULL && (s->kitty_keys.flags & KITTY_KEY_SUPPORTED) != 0)
|
||||
flags = s->kitty_keys.flags & KITTY_KEY_SUPPORTED;
|
||||
if ((tty->flags & TTY_KKBPUSHED) == 0) {
|
||||
xsnprintf(buf, sizeof buf, "\033[>%uu", flags);
|
||||
tty_puts(tty, buf);
|
||||
tty->flags |= TTY_KKBPUSHED;
|
||||
} else if (tty->kitty_keys != flags) {
|
||||
xsnprintf(buf, sizeof buf, "\033[=%uu", flags);
|
||||
tty_puts(tty, buf);
|
||||
}
|
||||
tty->kitty_keys = flags;
|
||||
}
|
||||
|
||||
int
|
||||
tty_keys_kitty(struct tty *tty, const char *buf, size_t len, size_t *size,
|
||||
key_code *key)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
struct utf8_data ud;
|
||||
utf8_char uc;
|
||||
size_t end;
|
||||
char tmp[128], *copy, *field, final;
|
||||
u_int number = 0, modifiers = 1, event = 1, value;
|
||||
u_int fields = 0;
|
||||
key_code nkey, onlykey;
|
||||
int result;
|
||||
|
||||
*size = 0;
|
||||
if ((tty->flags & TTY_KKBPUSHED) == 0)
|
||||
return (-1);
|
||||
if (len == 0 || buf[0] != '\033')
|
||||
return (-1);
|
||||
if (len == 1)
|
||||
return (1);
|
||||
if (buf[1] != '[')
|
||||
return (-1);
|
||||
if (len == 2)
|
||||
return (1);
|
||||
if (buf[2] == '?')
|
||||
return (-1);
|
||||
|
||||
result = tty_kitty_find_end(buf, len, &end);
|
||||
if (result != 0)
|
||||
return (result);
|
||||
if (end - 2 >= sizeof tmp)
|
||||
return (-1);
|
||||
memcpy(tmp, buf + 2, end - 2);
|
||||
tmp[end - 2] = '\0';
|
||||
final = buf[end];
|
||||
*size = end + 1;
|
||||
|
||||
copy = tmp;
|
||||
while ((field = strsep(©, ";")) != NULL) {
|
||||
if (fields == 3)
|
||||
return (-1);
|
||||
value = 0;
|
||||
if (fields == 0) {
|
||||
if (*field == '\0') {
|
||||
if (final == 'u')
|
||||
return (-1);
|
||||
number = 1;
|
||||
} else if (tty_kitty_parse_list(field, &number, &value,
|
||||
1, 3) != 0)
|
||||
return (-1);
|
||||
} else if (fields == 1) {
|
||||
if (tty_kitty_parse_list(field, &modifiers, &event, 1,
|
||||
2) != 0)
|
||||
return (-1);
|
||||
} else if (tty_kitty_parse_list(field, &value, &value, 1, 0) != 0)
|
||||
return (-1);
|
||||
fields++;
|
||||
}
|
||||
if (fields == 0 || number == 0 || modifiers == 0)
|
||||
return (-1);
|
||||
if (event == 0 || event == 3 || event > 3)
|
||||
return (-2);
|
||||
|
||||
modifiers--;
|
||||
if (modifiers & 32)
|
||||
return (-2);
|
||||
nkey = tty_kitty_function(number, final);
|
||||
if (nkey == KEYC_NONE) {
|
||||
if (final != 'u')
|
||||
return (-1);
|
||||
if (number >= 57344 && number <= 63743)
|
||||
return (-2);
|
||||
if (number <= 0x7f)
|
||||
nkey = number;
|
||||
else if (utf8_fromwc(number, &ud) != UTF8_DONE ||
|
||||
utf8_from_data(&ud, &uc) != UTF8_DONE)
|
||||
return (-2);
|
||||
else
|
||||
nkey = uc;
|
||||
} else if (nkey == KEYC_UNKNOWN)
|
||||
return (-1);
|
||||
|
||||
if (modifiers & 1)
|
||||
nkey |= KEYC_SHIFT;
|
||||
if (modifiers & 2)
|
||||
nkey |= KEYC_META|KEYC_IMPLIED_META;
|
||||
if (modifiers & 4)
|
||||
nkey |= KEYC_CTRL;
|
||||
if (modifiers & 8)
|
||||
nkey |= KEYC_SUPER;
|
||||
if (modifiers & 16)
|
||||
nkey |= KEYC_HYPER;
|
||||
|
||||
/* Shift-Tab has a dedicated tmux key code. */
|
||||
onlykey = nkey & KEYC_MASK_KEY;
|
||||
if (onlykey == C0_HT && (nkey & KEYC_SHIFT))
|
||||
nkey = KEYC_BTAB|(nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT);
|
||||
|
||||
if (log_get_level() != 0) {
|
||||
log_debug("%s: Kitty key %.*s is %llx (%s)", c->name,
|
||||
(int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
|
||||
}
|
||||
*key = nkey;
|
||||
return (0);
|
||||
}
|
||||
+9
-3
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-term.c,v 1.109 2026/08/25 08:37:08 nicm Exp $ */
|
||||
/* $OpenBSD: tty-term.c,v 1.111 2026/09/22 14:10:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -90,6 +90,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
||||
[TTYC_DL1] = { TTYCODE_STRING, "dl1" },
|
||||
[TTYC_DL] = { TTYCODE_STRING, "dl" },
|
||||
[TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" },
|
||||
[TTYC_DSESC] = { TTYCODE_STRING, "Dsesc" },
|
||||
[TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" },
|
||||
[TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" },
|
||||
[TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" },
|
||||
@@ -101,6 +102,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
||||
[TTYC_ENACS] = { TTYCODE_STRING, "enacs" },
|
||||
[TTYC_ENBP] = { TTYCODE_STRING, "Enbp" },
|
||||
[TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" },
|
||||
[TTYC_ENESC] = { TTYCODE_STRING, "Enesc" },
|
||||
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
|
||||
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
|
||||
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
|
||||
@@ -362,7 +364,8 @@ tty_term_override_next(const char *s, size_t *offset)
|
||||
}
|
||||
|
||||
void
|
||||
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet)
|
||||
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet,
|
||||
int flags)
|
||||
{
|
||||
const struct tty_term_code_entry *ent;
|
||||
struct tty_code *code;
|
||||
@@ -405,6 +408,9 @@ tty_term_apply(struct tty_term *term, const char *capabilities, int quiet)
|
||||
if (strcmp(s, ent->name) != 0)
|
||||
continue;
|
||||
code = &term->codes[i];
|
||||
if ((flags & TERM_NOREPLACE) &&
|
||||
code->type != TTYCODE_NONE)
|
||||
continue;
|
||||
|
||||
if (remove) {
|
||||
code->type = TTYCODE_NONE;
|
||||
@@ -457,7 +463,7 @@ tty_term_apply_overrides(struct tty_term *term)
|
||||
offset = 0;
|
||||
first = tty_term_override_next(s, &offset);
|
||||
if (first != NULL && fnmatch(first, term->name, 0) == 0)
|
||||
tty_term_apply(term, s + offset, 0);
|
||||
tty_term_apply(term, s + offset, 0, 0);
|
||||
a = options_array_next(a);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty.c,v 1.480 2026/08/25 08:37:08 nicm Exp $ */
|
||||
/* $OpenBSD: tty.c,v 1.482 2026/09/22 06:58:06 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -409,8 +409,6 @@ tty_send_requests(struct tty *tty)
|
||||
return;
|
||||
|
||||
if (tty->term->flags & TERM_VT100LIKE) {
|
||||
if (~tty->flags & TTY_HAVEKKB)
|
||||
tty_puts(tty, "\033[?u");
|
||||
if (~tty->flags & TTY_HAVEDA)
|
||||
tty_puts(tty, "\033[c");
|
||||
if (~tty->flags & TTY_HAVEDA2)
|
||||
@@ -505,14 +503,9 @@ tty_stop_tty(struct tty *tty)
|
||||
if (tty_term_has(tty->term, TTYC_DSBP))
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
|
||||
|
||||
if (tty->term->flags & TERM_VT100LIKE)
|
||||
tty_raw(tty, "\033[?7727l");
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSESC));
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
|
||||
if (tty->flags & TTY_KKBPUSHED) {
|
||||
tty_raw(tty, "\033[<u");
|
||||
tty->flags &= ~TTY_KKBPUSHED;
|
||||
}
|
||||
|
||||
if (tty_use_margin(tty))
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
|
||||
@@ -565,16 +558,11 @@ tty_update_features(struct tty *tty)
|
||||
|
||||
if (tty_use_margin(tty))
|
||||
tty_putcode(tty, TTYC_ENMG);
|
||||
if (options_get_number(global_options, "extended-keys") == 0 ||
|
||||
(tty->term->flags & TERM_KITTYKEYS))
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_DSEKS));
|
||||
else
|
||||
if (options_get_number(global_options, "extended-keys"))
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
|
||||
tty_update_kitty(tty, NULL);
|
||||
if (options_get_number(global_options, "focus-events"))
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
|
||||
if (tty->term->flags & TERM_VT100LIKE)
|
||||
tty_puts(tty, "\033[?7727h");
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENESC));
|
||||
|
||||
/*
|
||||
* Features might have changed since the first draw during attach. For
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: utf8.c,v 1.72 2026/09/01 12:49:49 nicm Exp $ */
|
||||
/* $OpenBSD: utf8.c,v 1.73 2026/09/20 08:11:00 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -374,8 +374,13 @@ utf8_add_to_width_cache(const char *s)
|
||||
wc_end = wc_start;
|
||||
}
|
||||
|
||||
for (wc = wc_start; wc <= wc_end; wc++)
|
||||
wc = wc_start;
|
||||
for (;;) {
|
||||
utf8_insert_width_cache(wc, width);
|
||||
if (wc == wc_end)
|
||||
break;
|
||||
wc++;
|
||||
}
|
||||
} else {
|
||||
utf8_no_width = 1;
|
||||
ud = utf8_fromcstr(copy);
|
||||
|
||||
+1
-5
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-border.c,v 1.4 2026/09/11 10:17:16 nicm Exp $ */
|
||||
/* $OpenBSD: window-border.c,v 1.3 2026/07/23 09:38:27 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -120,10 +120,6 @@ window_get_border_cell(struct window_pane *wp, enum pane_lines pane_lines,
|
||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
||||
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
|
||||
break;
|
||||
case PANE_LINES_ROUNDED:
|
||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
||||
utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
|
||||
break;
|
||||
case PANE_LINES_SIMPLE:
|
||||
gc->attr &= ~GRID_ATTR_CHARSET;
|
||||
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-copy.c,v 1.429 2026/09/01 13:04:29 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.431 2026/09/21 10:43:37 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -401,7 +401,7 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
|
||||
|
||||
sy = screen_hsize(src) + screen_size_y(src);
|
||||
if (trim) {
|
||||
while (sy > screen_hsize(src)) {
|
||||
while (sy > screen_hsize(src) + 1) {
|
||||
gl = grid_peek_line(src->grid, sy - 1);
|
||||
if (gl == NULL || gl->cellused != 0)
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window.c,v 1.374 2026/09/08 08:37:56 nicm Exp $ */
|
||||
/* $OpenBSD: window.c,v 1.377 2026/09/21 10:33:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -449,9 +449,17 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||
static void
|
||||
window_destroy(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
|
||||
log_debug("window @%u destroyed (%d references)", w->id, w->references);
|
||||
|
||||
window_unzoom(w, 0);
|
||||
if (w->flags & WINDOW_ZOOMED) {
|
||||
w->flags &= ~WINDOW_ZOOMED;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
wp->flags &= ~PANE_ZOOMED;
|
||||
wp->saved_layout_cell = NULL;
|
||||
}
|
||||
}
|
||||
RB_REMOVE(windows, &windows, w);
|
||||
|
||||
layout_free_cell(w->layout_root, 0);
|
||||
@@ -2906,3 +2914,15 @@ window_pane_is_floating(struct window_pane *wp)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
window_pane_is_floating_with_hidden(struct window_pane *wp)
|
||||
{
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
|
||||
if (lc == NULL)
|
||||
lc = wp->saved_layout_cell;
|
||||
if (lc == NULL || (lc->flags & LAYOUT_CELL_FLOATING) == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user