Compare commits

..

16 Commits

Author SHA1 Message Date
Nicholas Marriott
b98ce20a10 Minor bits missed (-T show title and popup_* format variables). 2026-09-11 09:41:33 +01:00
Nicholas Marriott
5ea118e3e0 Merge branch 'master' into no_more_overlays
# Conflicts:
#	regress/modal-pane.sh
#	server-client.c
2026-09-11 09:18:53 +01:00
Nicholas Marriott
b15c62ac07 Add some more key bits to better emulate display-popup. 2026-09-10 10:09:15 +01:00
Nicholas Marriott
7049aa826a Do not allow 0x0 panes and remove a dead member. 2026-09-09 14:39:14 +01:00
Nicholas Marriott
19320c6969 Merge branch 'master' into no_more_overlays 2026-09-09 14:00:20 +01:00
Nicholas Marriott
78c0dc5be0 Merge branch 'master' into no_more_overlays
# Conflicts:
#	cmd-display-menu.c
#	popup.c
#	screen-write.c
2026-09-01 20:28:59 +01:00
Nicholas Marriott
81fa44f263 Merge branch 'master' into no_more_overlays
# Conflicts:
#	regress/modal-pane.sh
2026-08-21 15:58:07 +01:00
Nicholas Marriott
ba1bfeeb9c Merge branch 'master' into no_more_overlays 2026-08-19 14:49:33 +01:00
Nicholas Marriott
5047e4eb1a Add a way to make a modal pane capture all key presses and use for
display-popup compatibility.
2026-08-19 09:34:39 +01:00
Nicholas Marriott
8a1f4620b0 Merge branch 'master' into no_more_overlays 2026-08-19 09:00:04 +01:00
Nicholas Marriott
7d32e7f379 Fix bad merge. 2026-08-07 16:18:30 +01:00
Nicholas Marriott
6671efcaf7 Merge branch 'master' into no_more_overlays 2026-08-07 16:10:51 +01:00
Nicholas Marriott
858a9d7376 Merge branch 'master' into no_more_overlays 2026-07-21 08:35:07 +01:00
Your Name
980877fb5c Merge branch 'master' into no_more_overlays 2026-07-16 21:26:02 +01:00
Nicholas Marriott
c7a6a735bc Another popup leftover. 2026-07-16 13:10:51 +01:00
Nicholas Marriott
381eaaa3a3 No more popups and overlays. 2026-07-16 13:04:29 +01:00
43 changed files with 422 additions and 3636 deletions

12
CHANGES
View File

@@ -111,18 +111,6 @@ 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.

View File

@@ -171,7 +171,6 @@ dist_tmux_SOURCES = \
grid.c \
hooks.c \
hyperlinks.c \
input-kitty.c \
input-keys.c \
input.c \
job.c \
@@ -189,7 +188,6 @@ dist_tmux_SOURCES = \
options-table.c \
options.c \
paste.c \
popup.c \
proc.c \
prompt-history.c \
prompt.c \
@@ -213,7 +211,6 @@ dist_tmux_SOURCES = \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-kitty.c \
tty-keys.c \
tty-term.c \
tty.c \

View File

@@ -54,8 +54,8 @@ const struct cmd_entry cmd_display_popup_entry = {
.name = "display-popup",
.alias = "popup",
.args = { "Bb:Cc:d:e:Eh:kNs:S:t:T:w:x:y:", 0, -1, NULL },
.usage = "[-BCEkN] [-b border-lines] [-c target-client] "
.args = { "Bb:Cc:d:e:Eh:ks:S:t:T:w:x:y:", 0, -1, NULL },
.usage = "[-BCEk] [-b border-lines] [-c target-client] "
"[-d start-directory] [-e environment] [-h height] "
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
" [-T title] [-w width] [-x position] [-y position] "
@@ -91,203 +91,6 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
return (type);
}
static int
cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct tty *tty = &tc->tty;
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct session *s = tc->session;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct style_ranges *ranges = NULL;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
int top;
u_int line, ox, oy, sx, sy, lines, position;
long n;
struct format_tree *ft;
/*
* Work out the position from the -x and -y arguments. This is the
* bottom-left position.
*/
/* If the popup is too big, stop now. */
if (w > tty->sx || h > tty->sy)
return (0);
/* Create format with mouse position if any. */
ft = format_create_from_target(item);
if (event->m.valid) {
format_add(ft, "popup_mouse_x", "%u", event->m.x);
format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", target->w->menu_last_px);
format_add(ft, "popup_last_y", "%u", target->w->menu_last_py + h);
/*
* If there are any status lines, add this window position and the
* status line position.
*/
top = status_at_line(tc);
if (top != -1) {
lines = status_line_size(tc);
if (top == 0)
top = lines;
else
top = 0;
position = options_get_number(s->options, "status-position");
for (line = 0; line < lines; line++) {
ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
break;
}
if (sr != NULL) {
format_add(ft, "popup_window_status_line_x", "%u",
sr->start);
if (position == 0) {
format_add(ft, "popup_window_status_line_y",
"%u", line + 1 + h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", tty->sy - lines + line);
}
}
if (position == 0)
format_add(ft, "popup_status_line_y", "%u", lines + h);
else {
format_add(ft, "popup_status_line_y", "%u",
tty->sy - lines);
}
} else
top = 0;
/* Popup width and height. */
format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h);
/* Position so popup is in the centre. */
n = (long)(tty->sx - 1) / 2 - w / 2;
if (n < 0)
format_add(ft, "popup_centre_x", "%u", 0);
else
format_add(ft, "popup_centre_x", "%ld", n);
n = (tty->sy - 1) / 2 + h / 2;
if (n >= tty->sy)
format_add(ft, "popup_centre_y", "%u", tty->sy - h);
else
format_add(ft, "popup_centre_y", "%ld", n);
/* Position of popup relative to mouse. */
if (event->m.valid) {
n = (long)event->m.x - w / 2;
if (n < 0)
format_add(ft, "popup_mouse_centre_x", "%u", 0);
else
format_add(ft, "popup_mouse_centre_x", "%ld", n);
n = event->m.y - h / 2;
if (n + h >= tty->sy) {
format_add(ft, "popup_mouse_centre_y", "%u",
tty->sy - h);
} else
format_add(ft, "popup_mouse_centre_y", "%ld", n);
n = (long)event->m.y + h;
if (n >= tty->sy)
format_add(ft, "popup_mouse_top", "%u", tty->sy - 1);
else
format_add(ft, "popup_mouse_top", "%ld", n);
n = event->m.y - h;
if (n < 0)
format_add(ft, "popup_mouse_bottom", "%u", 0);
else
format_add(ft, "popup_mouse_bottom", "%ld", n);
}
/* Position in pane. */
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
n = top + wp->yoff - oy + h;
if (n >= tty->sy)
format_add(ft, "popup_pane_top", "%u", tty->sy - h);
else
format_add(ft, "popup_pane_top", "%ld", n);
format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
n = (long)wp->xoff + wp->sx - ox - w;
if (n < 0)
format_add(ft, "popup_pane_right", "%u", 0);
else
format_add(ft, "popup_pane_right", "%ld", n);
/* Expand horizontal position. */
xp = args_get(args, 'x');
if (xp == NULL || strcmp(xp, "C") == 0)
xp = "#{popup_centre_x}";
else if (strcmp(xp, "R") == 0)
xp = "#{popup_pane_right}";
else if (strcmp(xp, "P") == 0)
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
n = strtol(p, NULL, 10);
if (n + w >= tty->sx)
n = tty->sx - w;
else if (n < 0)
n = 0;
*px = n;
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
free(p);
/* Expand vertical position */
yp = args_get(args, 'y');
if (yp == NULL || strcmp(yp, "C") == 0)
yp = "#{popup_centre_y}";
else if (strcmp(yp, "P") == 0)
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)
yp = "#{popup_window_status_line_y}";
p = format_expand(ft, yp);
n = strtol(p, NULL, 10);
if (n < h)
n = 0;
else
n -= h;
if (n + h >= tty->sy)
n = tty->sy - h;
else if (n < 0)
n = 0;
*py = n;
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
format_free(ft);
return (1);
}
static int
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
@@ -565,6 +368,22 @@ fail:
return (CMD_RETURN_ERROR);
}
static enum pane_lines
cmd_display_popup_get_lines(const char *value, char **cause)
{
const struct options_table_entry *oe;
if (value == NULL)
value = "single";
else if (strcmp(value, "rounded") == 0)
value = "single";
else if (strcmp(value, "padded") == 0)
value = "spaces";
oe = options_search("pane-border-lines");
return (options_find_choice(oe, value, cause));
}
static enum cmd_retval
cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -572,148 +391,199 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct client *tc = cmdq_get_target_client(item);
struct tty *tty = &tc->tty;
const char *value, *shell, *shellcmd = NULL;
const char *style = args_get(args, 's');
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp = NULL;
struct spawn_context sc = { 0 };
struct layout_cell *lc = NULL;
struct layout_geometry lg;
struct event_payload *ep;
struct cmd_find_state fs;
const char *value, *style = args_get(args, 's');
const char *border_style = args_get(args, 'S');
char *cwd = NULL, *cause = NULL, **argv = NULL;
char *title = NULL;
int modify = popup_present(tc);
int flags = -1, argc = 0;
enum box_lines lines = BOX_LINES_DEFAULT;
u_int px, py, w, h, count = args_count(args);
char *cause = NULL, *title = NULL;
enum pane_lines lines = PANE_LINES_SINGLE;
u_int px, py, sx, sy, count = args_count(args);
struct args_value *av;
struct environ *env = NULL;
struct options *o = s->curw->window->options;
struct options_entry *oe;
long long ll;
if (args_has(args, 'C')) {
server_client_clear_overlay(tc);
if (w->modal != NULL)
server_kill_pane(w->modal);
return (CMD_RETURN_NORMAL);
}
if (tc->flags & CLIENT_CONTROL)
return (CMD_RETURN_NORMAL);
if (!modify && tc->overlay_draw != NULL)
if (w->modal != NULL)
return (CMD_RETURN_NORMAL);
if (!modify) {
h = tty->sy / 2;
if (args_has(args, 'h')) {
h = args_percentage(args, 'h', 1, tty->sy, tty->sy,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
goto fail;
}
}
w = tty->sx / 2;
if (args_has(args, 'w')) {
w = args_percentage(args, 'w', 1, tty->sx, tty->sx,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
goto fail;
}
}
if (w > tty->sx)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_popup_pos(tc, item, args, &px, &py,
w, h))
goto out;
value = args_get(args, 'd');
if (value != NULL)
cwd = format_single_from_target(item, value);
else
cwd = xstrdup(server_client_get_cwd(tc, s));
if (count == 0) {
shellcmd = options_get_string(s->options,
"default-command");
} else if (count == 1)
shellcmd = args_string(args, 0);
if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
shellcmd = NULL;
shell = options_get_string(s->options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
cmd_append_argv(&argc, &argv, shell);
} else
args_to_vector(args, &argc, &argv);
if (args_has(args, 'e') >= 1) {
env = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(env, av->string, 0);
av = args_next_value(av);
}
}
}
value = args_get(args, 'b');
if (args_has(args, 'B'))
lines = BOX_LINES_NONE;
lines = PANE_LINES_NONE;
else if (value != NULL) {
oe = options_get(o, "popup-border-lines");
lines = options_find_choice(options_table_entry(oe), value,
&cause);
lines = cmd_display_popup_get_lines(value, &cause);
if (cause != NULL) {
cmdq_error(item, "popup-border-lines %s", cause);
cmdq_error(item, "pane-border-lines %s", cause);
goto fail;
}
}
sy = w->sy / 2;
if (args_has(args, 'h')) {
ll = args_percentage(args, 'h', 1, w->sy, w->sy, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
goto fail;
}
sy = ll;
}
sx = w->sx / 2;
if (args_has(args, 'w')) {
ll = args_percentage(args, 'w', 1, w->sx, w->sx, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
goto fail;
}
sx = ll;
}
if (sx > w->sx)
sx = w->sx;
if (sy > w->sy)
sy = w->sy;
if ((lines == PANE_LINES_NONE && (sx < 1 || sy < 1)) ||
(lines != PANE_LINES_NONE && (sx < 3 || sy < 3)))
goto out;
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
goto out;
lg.sx = sx;
lg.sy = sy;
lg.xoff = px;
lg.yoff = py;
if (lines != PANE_LINES_NONE) {
lg.sx -= 2;
lg.sy -= 2;
lg.xoff++;
lg.yoff++;
}
window_push_zoom(w, 0, 1);
lc = layout_floating_pane(w, wp, &lg);
if (lc == NULL) {
window_pop_zoom(w);
goto out;
}
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.tc = tc;
sc.wp0 = wp;
sc.lc = lc;
sc.idx = -1;
sc.cwd = args_get(args, 'd');
sc.flags = (SPAWN_FLOATING|SPAWN_MODAL|SPAWN_FLOATOVERZOOM);
if (count != 1 || *args_string(args, 0) != '\0')
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
new_wp = spawn_pane(&sc, &cause);
if (new_wp == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
cause = NULL;
window_pop_zoom(w);
goto fail;
}
window_pop_zoom(w);
new_wp->flags |= PANE_CAPTUREALLKEYS;
if (!args_has(args, 'E'))
new_wp->flags |= PANE_CLOSEONCANCEL;
options_set_number(new_wp->options, "pane-border-lines", lines);
if (args_has(args, 'E') > 1) {
if (args_has(args, 'k'))
options_set_number(new_wp->options, "remain-on-exit", 4);
else
options_set_number(new_wp->options, "remain-on-exit", 2);
} else if (args_has(args, 'E'))
options_set_number(new_wp->options, "remain-on-exit", 0);
else if (args_has(args, 'k'))
options_set_number(new_wp->options, "remain-on-exit", 3);
else
options_set_number(new_wp->options, "remain-on-exit", 1);
options_set_string(new_wp->options, "remain-on-exit-format", 0, "%s", "");
if (style != NULL) {
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
goto fail;
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
}
if (border_style != NULL) {
if (options_set_string(new_wp->options, "pane-border-style", 0,
"%s", border_style) == NULL) {
cmdq_error(item, "bad border style: %s", border_style);
goto fail;
}
options_set_string(new_wp->options, "pane-active-border-style",
0, "%s", border_style);
}
if (args_has(args, 'T'))
title = format_single_from_target(item, args_get(args, 'T'));
else
title = xstrdup("");
if (args_has(args, 'N') || !modify)
flags = 0;
if (args_has(args, 'E') > 1) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEEXITZERO;
} else if (args_has(args, 'E')) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEEXIT;
}
if (args_has(args, 'k')) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEANYKEY;
if (title != NULL) {
options_set_number(new_wp->options, "pane-border-status",
PANE_STATUS_TOP);
options_set_string(new_wp->options, "pane-border-format", 0,
"%s", "#{pane_title}");
screen_set_title(&new_wp->base, title, 0);
ep = event_payload_create();
cmd_find_from_pane(&fs, new_wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", new_wp);
event_payload_set_window(ep, "window", new_wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
}
if (modify) {
popup_modify(tc, title, style, border_style, lines, flags);
goto out;
}
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0)
goto out;
environ_free(env);
free(cwd);
new_wp->wait_item = item;
server_redraw_session(s);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(title);
cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT);
out:
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(title);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
if (new_wp != NULL) {
server_client_remove_pane(new_wp);
layout_close_pane(new_wp);
window_remove_pane(new_wp->window, new_wp);
}
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(title);
return (CMD_RETURN_ERROR);
}

View File

@@ -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"));
@@ -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
},

View File

@@ -44,7 +44,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
wp = window_add_pane(w, NULL, 0, 0);
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
wp->ictx = input_init(wp, vpty[0], NULL, NULL);
wp->ictx = input_init(wp, vpty[0], NULL);
window_add_ref(w, __func__);
wp->fd = open("/dev/null", O_WRONLY);

View File

@@ -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

View File

@@ -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);
}

51
input.c
View File

@@ -101,7 +101,6 @@ struct input_ctx {
struct bufferevent *event;
struct screen_write_ctx ctx;
struct colour_palette *palette;
struct client *c;
struct input_cell cell;
struct input_cell old_cell;
@@ -279,10 +278,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 +343,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. */
@@ -882,7 +873,7 @@ input_restore_state(struct input_ctx *ictx)
/* Initialise input parser. */
struct input_ctx *
input_init(struct window_pane *wp, struct bufferevent *bev,
struct colour_palette *palette, struct client *c)
struct colour_palette *palette)
{
struct input_ctx *ictx;
@@ -890,7 +881,6 @@ input_init(struct window_pane *wp, struct bufferevent *bev,
ictx->wp = wp;
ictx->event = bev;
ictx->palette = palette;
ictx->c = c;
ictx->input_space = INPUT_BUF_START;
ictx->input_buf = xmalloc(INPUT_BUF_START);
@@ -1565,33 +1555,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;
@@ -3460,15 +3423,9 @@ input_osc_52(struct input_ctx *ictx, const char *p)
return;
if (wp == NULL) {
/* Popup window. */
if (ictx->c == NULL) {
free(out);
return;
}
tty_set_selection(&ictx->c->tty, clip, out, outlen);
paste_add(NULL, out, outlen);
free(out);
return;
} else {
/* Normal window. */
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, clip, out, outlen);
screen_write_stop(&ctx);

View File

@@ -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. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout.c,v 1.100 2026/09/11 08:16:14 nicm Exp $ */
/* $OpenBSD: layout.c,v 1.99 2026/09/09 07:03:39 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -768,49 +768,6 @@ layout_free(struct window *w, int only_nodes)
layout_free_cell(w->layout_root, only_nodes);
}
/* Move and resize floating panes so they stay inside the window. */
static void
layout_clamp_floating_panes(struct window *w, u_int sx, u_int sy)
{
struct window_pane *wp;
struct layout_cell *lc;
u_int pad, avail, csx, csy;
TAILQ_FOREACH(wp, &w->z_index, zentry) {
lc = wp->layout_cell;
if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING))
continue;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
pad = 0;
else
pad = 1;
csx = lc->g.sx;
avail = (sx > 2 * pad) ? sx - 2 * pad : 0;
if (csx > avail)
csx = (avail > PANE_MINIMUM) ? avail : PANE_MINIMUM;
csy = lc->g.sy;
avail = (sy > 2 * pad) ? sy - 2 * pad : 0;
if (csy > avail)
csy = (avail > PANE_MINIMUM) ? avail : PANE_MINIMUM;
if (csx != lc->g.sx || csy != lc->g.sy)
layout_set_size(lc, csx, csy, lc->g.xoff, lc->g.yoff);
if (lc->g.xoff + lc->g.sx + pad > sx) {
if (lc->g.sx + 2 * pad >= sx)
lc->g.xoff = pad;
else
lc->g.xoff = sx - lc->g.sx - pad;
}
if (lc->g.yoff + lc->g.sy + pad > sy) {
if (lc->g.sy + 2 * pad >= sy)
lc->g.yoff = pad;
else
lc->g.yoff = sy - lc->g.sy - pad;
}
}
}
/* Resize the entire layout after window resize. */
void
layout_resize(struct window *w, u_int sx, u_int sy)
@@ -831,11 +788,8 @@ layout_resize(struct window *w, u_int sx, u_int sy)
* out proportionately - this should leave the layout fitting the new
* window size.
*/
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING)) {
layout_clamp_floating_panes(w, sx, sy);
layout_fix_panes(w, NULL);
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
return;
}
xchange = sx - lc->g.sx;
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit)
@@ -865,7 +819,6 @@ layout_resize(struct window *w, u_int sx, u_int sy)
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_clamp_floating_panes(w, sx, sy);
layout_fix_panes(w, NULL);
}

View File

@@ -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,10 +76,9 @@ 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[] = {
static const char *options_table_box_lines_list[] = {
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
};
static const char *options_table_set_clipboard_list[] = {
@@ -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
@@ -493,7 +492,7 @@ const struct options_table_entry options_table[] = {
{ .name = "menu-border-lines",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_popup_border_lines_list,
.choices = options_table_box_lines_list,
.default_num = BOX_LINES_SINGLE,
.text = "Type of characters used to draw menu border lines. Some of "
"these are only supported on terminals with UTF-8 support."
@@ -1666,33 +1665,6 @@ const struct options_table_entry options_table[] = {
.text = "Pane scrollbar position."
},
{ .name = "popup-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=themedarkgrey,fg=themewhite",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of popups."
},
{ .name = "popup-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=themedarkgrey,fg=themelightgrey",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of popup borders."
},
{ .name = "popup-border-lines",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_popup_border_lines_list,
.default_num = BOX_LINES_SINGLE,
.text = "Type of characters used to draw popup border lines. Some of "
"these are only supported on terminals with UTF-8 support."
},
{ .name = "remain-on-exit",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,

View File

@@ -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)

661
popup.c
View File

@@ -1,661 +0,0 @@
/* $OpenBSD: popup.c,v 1.77 2026/08/28 08:02:16 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* 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 <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
struct popup_data {
struct client *c;
struct cmdq_item *item;
int flags;
char *title;
char *style;
char *border_style;
struct grid_cell border_cell;
enum box_lines border_lines;
struct screen s;
struct grid_cell defaults;
struct colour_palette palette;
struct visible_ranges r;
struct job *job;
struct input_ctx *ictx;
int status;
popup_close_cb cb;
void *arg;
int close;
/* Current position and size. */
u_int px;
u_int py;
u_int sx;
u_int sy;
/* Preferred position and size. */
u_int ppx;
u_int ppy;
u_int psx;
u_int psy;
enum { OFF, MOVE, SIZE } dragging;
u_int dx;
u_int dy;
u_int lx;
u_int ly;
u_int lb;
};
static void
popup_free(struct popup_data *pd)
{
server_client_unref(pd->c);
if (pd->job != NULL)
job_free(pd->job);
if (pd->ictx != NULL)
input_free(pd->ictx);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
free(pd->title);
free(pd->style);
free(pd->border_style);
free(pd);
}
static void
popup_reapply_styles(struct popup_data *pd)
{
struct client *c = pd->c;
struct session *s = c->session;
struct options *o;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
/* Reapply popup style from options. */
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
style_apply(&pd->defaults, o, "popup-style", ft);
if (pd->style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->defaults, pd->style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
pd->defaults.bg = sytmp.gc.bg;
}
}
pd->defaults.attr = 0;
/* Reapply border style from options. */
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
style_apply(&pd->border_cell, o, "popup-border-style", ft);
if (pd->border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->border_cell,
pd->border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
pd->border_cell.bg = sytmp.gc.bg;
}
}
pd->border_cell.attr = 0;
format_free(ft);
}
static void
popup_redraw_cb(const struct tty_ctx *ttyctx)
{
struct popup_data *pd = ttyctx->arg;
pd->c->flags |= CLIENT_REDRAWOVERLAY;
}
static int
popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{
struct popup_data *pd = ttyctx->arg;
if (c != pd->c)
return (0);
if (pd->c->flags & CLIENT_REDRAWOVERLAY)
return (0);
ttyctx->wox = 0;
ttyctx->woy = 0;
ttyctx->wsx = c->tty.sx;
ttyctx->wsy = c->tty.sy;
if (pd->border_lines == BOX_LINES_NONE) {
ttyctx->xoff = ttyctx->rxoff = pd->px;
ttyctx->yoff = ttyctx->ryoff = pd->py;
} else {
ttyctx->xoff = ttyctx->rxoff = pd->px + 1;
ttyctx->yoff = ttyctx->ryoff = pd->py + 1;
}
return (1);
}
static void
popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
{
struct popup_data *pd = ctx->arg;
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
ttyctx->style_ctx.defaults = &ttyctx->defaults;
ttyctx->style_ctx.palette = &pd->palette;
ttyctx->redraw_cb = popup_redraw_cb;
ttyctx->set_client_cb = popup_set_client_cb;
ttyctx->arg = pd;
}
static struct screen *
popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
{
struct popup_data *pd = data;
if (pd->border_lines == BOX_LINES_NONE) {
*cx = pd->px + pd->s.cx;
*cy = pd->py + pd->s.cy;
} else {
*cx = pd->px + 1 + pd->s.cx;
*cy = pd->py + 1 + pd->s.cy;
}
return (&pd->s);
}
/* Return parts of the input range which are not obstructed by the popup. */
static struct visible_ranges *
popup_check_cb(__unused struct client* c, void *data, u_int px, u_int py,
u_int nx)
{
struct popup_data *pd = data;
struct visible_ranges *r = &pd->r;
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r);
return (r);
}
static void
popup_draw_cb(struct client *c, void *data)
{
struct popup_data *pd = data;
struct tty *tty = &c->tty;
struct screen s;
struct screen_write_ctx ctx;
u_int i, px = pd->px, py = pd->py;
struct grid_cell defaults;
struct tty_style_ctx style_ctx;
popup_reapply_styles(pd);
screen_init(&s, pd->sx, pd->sy, 0);
if (pd->s.hyperlinks != NULL) {
hyperlinks_free(s.hyperlinks);
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
}
screen_write_start(&ctx, &s);
screen_write_clearscreen(&ctx, 8);
if (pd->border_lines == BOX_LINES_NONE) {
screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy);
} else if (pd->sx > 2 && pd->sy > 2) {
screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines,
&pd->border_cell, pd->title);
screen_write_cursormove(&ctx, 1, 1, 0);
screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2,
pd->sy - 2);
}
screen_write_stop(&ctx);
memcpy(&defaults, &pd->defaults, sizeof defaults);
if (defaults.fg == 8)
defaults.fg = pd->palette.fg;
if (defaults.bg == 8)
defaults.bg = pd->palette.bg;
style_ctx.defaults = &defaults;
style_ctx.palette = &pd->palette;
style_ctx.dim = 0;
style_ctx.hyperlinks = s.hyperlinks;
c->overlay_check = NULL;
c->overlay_data = NULL;
for (i = 0; i < pd->sy; i++)
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx);
screen_free(&s);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
}
static void
popup_free_cb(__unused struct client *c, void *data)
{
struct popup_data *pd = data;
struct cmdq_item *item = pd->item;
if (pd->cb != NULL)
pd->cb(pd->status, pd->arg);
if (item != NULL) {
if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = pd->status;
cmdq_continue(item);
}
popup_free(pd);
}
static void
popup_resize_cb(__unused struct client *c, void *data)
{
struct popup_data *pd = data;
struct tty *tty = &c->tty;
if (pd == NULL)
return;
/* Adjust position and size. */
if (pd->psy > tty->sy)
pd->sy = tty->sy;
else
pd->sy = pd->psy;
if (pd->psx > tty->sx)
pd->sx = tty->sx;
else
pd->sx = pd->psx;
if (pd->ppy + pd->sy > tty->sy)
pd->py = tty->sy - pd->sy;
else
pd->py = pd->ppy;
if (pd->ppx + pd->sx > tty->sx)
pd->px = tty->sx - pd->sx;
else
pd->px = pd->ppx;
/* Avoid zero size screens. */
if (pd->border_lines == BOX_LINES_NONE) {
screen_resize(&pd->s, pd->sx, pd->sy, 0);
if (pd->job != NULL)
job_resize(pd->job, pd->sx, pd->sy );
} else if (pd->sx > 2 && pd->sy > 2) {
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
if (pd->job != NULL)
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
}
}
static void
popup_handle_drag(struct client *c, struct popup_data *pd,
struct mouse_event *m)
{
u_int px, py;
if (!MOUSE_DRAG(m->b))
pd->dragging = OFF;
else if (pd->dragging == MOVE) {
if (m->x < pd->dx)
px = 0;
else if (m->x - pd->dx + pd->sx > c->tty.sx)
px = c->tty.sx - pd->sx;
else
px = m->x - pd->dx;
if (m->y < pd->dy)
py = 0;
else if (m->y - pd->dy + pd->sy > c->tty.sy)
py = c->tty.sy - pd->sy;
else
py = m->y - pd->dy;
pd->px = px;
pd->py = py;
pd->dx = m->x - pd->px;
pd->dy = m->y - pd->py;
pd->ppx = px;
pd->ppy = py;
server_redraw_client(c);
} else if (pd->dragging == SIZE) {
if (pd->border_lines == BOX_LINES_NONE) {
if (m->x < pd->px + 1)
return;
if (m->y < pd->py + 1)
return;
} else {
if (m->x < pd->px + 3)
return;
if (m->y < pd->py + 3)
return;
}
pd->sx = m->x - pd->px;
pd->sy = m->y - pd->py;
pd->psx = pd->sx;
pd->psy = pd->sy;
if (pd->border_lines == BOX_LINES_NONE) {
screen_resize(&pd->s, pd->sx, pd->sy, 0);
if (pd->job != NULL)
job_resize(pd->job, pd->sx, pd->sy);
} else {
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
if (pd->job != NULL)
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
}
server_redraw_client(c);
}
}
static int
popup_key_cb(struct client *c, void *data, struct key_event *event)
{
struct popup_data *pd = data;
struct mouse_event *m = &event->m;
const char *buf;
size_t len;
u_int px, py;
enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE;
if (KEYC_IS_MOUSE(event->key)) {
if (pd->dragging != OFF) {
popup_handle_drag(c, pd, m);
goto out;
}
if (m->x < pd->px ||
m->x > pd->px + pd->sx - 1 ||
m->y < pd->py ||
m->y > pd->py + pd->sy - 1) {
return (0);
}
if (pd->border_lines != BOX_LINES_NONE) {
if (m->x == pd->px)
border = LEFT;
else if (m->x == pd->px + pd->sx - 1)
border = RIGHT;
else if (m->y == pd->py)
border = TOP;
else if (m->y == pd->py + pd->sy - 1)
border = BOTTOM;
}
if ((m->b & MOUSE_MASK_MODIFIERS) == 0 &&
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 &&
(border == LEFT || border == TOP))
goto out;
if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
(border != NONE && !MOUSE_DRAG(m->lb))) {
if (!MOUSE_DRAG(m->b))
goto out;
if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1)
pd->dragging = MOVE;
else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3)
pd->dragging = SIZE;
pd->dx = m->lx - pd->px;
pd->dy = m->ly - pd->py;
goto out;
}
}
if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
pd->job == NULL) &&
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
return (1);
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
return (1);
if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) {
/* Must be inside, checked already. */
if (pd->border_lines == BOX_LINES_NONE) {
px = m->x - pd->px;
py = m->y - pd->py;
} else {
px = m->x - pd->px - 1;
py = m->y - pd->py - 1;
}
if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len))
return (0);
bufferevent_write(job_get_event(pd->job), buf, len);
return (0);
}
input_key(&pd->s, job_get_event(pd->job), event->key);
}
return (0);
out:
pd->lx = m->x;
pd->ly = m->y;
pd->lb = m->b;
return (0);
}
static void
popup_job_update_cb(struct job *job)
{
struct popup_data *pd = job_get_data(job);
struct evbuffer *evb = job_get_event(job)->input;
struct client *c = pd->c;
struct screen *s = &pd->s;
void *data = EVBUFFER_DATA(evb);
size_t size = EVBUFFER_LENGTH(evb);
if (size == 0)
return;
c->overlay_check = NULL;
c->overlay_data = NULL;
input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
evbuffer_drain(evb, size);
}
static void
popup_job_complete_cb(struct job *job)
{
struct popup_data *pd = job_get_data(job);
int status;
status = job_get_status(pd->job);
if (WIFEXITED(status))
pd->status = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
pd->status = WTERMSIG(status);
else
pd->status = 0;
pd->job = NULL;
if ((pd->flags & POPUP_CLOSEEXIT) ||
((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0))
server_client_clear_overlay(pd->c);
}
int
popup_present(struct client *c)
{
return (c->overlay_draw == popup_draw_cb);
}
int
popup_modify(struct client *c, const char *title, const char *style,
const char *border_style, enum box_lines lines, int flags)
{
struct popup_data *pd = c->overlay_data;
struct style sytmp;
if (title != NULL) {
if (pd->title != NULL)
free(pd->title);
pd->title = xstrdup(title);
}
if (border_style != NULL) {
free(pd->border_style);
pd->border_style = xstrdup(border_style);
style_set(&sytmp, &pd->border_cell);
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
pd->border_cell.bg = sytmp.gc.bg;
}
}
if (style != NULL) {
free(pd->style);
pd->style = xstrdup(style);
style_set(&sytmp, &pd->defaults);
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
pd->defaults.bg = sytmp.gc.bg;
}
}
if (lines != BOX_LINES_DEFAULT) {
if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx, pd->sy, 1);
job_resize(pd->job, pd->sx, pd->sy);
} else if (pd->border_lines == BOX_LINES_NONE &&
pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
}
pd->border_lines = lines;
tty_resize(&c->tty);
}
if (flags != -1)
pd->flags = flags;
server_redraw_client(c);
return (0);
}
int
popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd,
int argc, char **argv, const char *cwd, const char *title, struct client *c,
struct session *s, const char *style, const char *border_style,
popup_close_cb cb, void *arg)
{
struct popup_data *pd;
u_int jx, jy;
struct options *o;
struct style sytmp;
if (s != NULL)
o = s->curw->window->options;
else
o = c->session->curw->window->options;
if (lines == BOX_LINES_DEFAULT)
lines = options_get_number(o, "popup-border-lines");
if (lines == BOX_LINES_NONE) {
if (sx < 1 || sy < 1)
return (-1);
jx = sx;
jy = sy;
} else {
if (sx < 3 || sy < 3)
return (-1);
jx = sx - 2;
jy = sy - 2;
}
if (c->tty.sx < sx || c->tty.sy < sy)
return (-1);
pd = xcalloc(1, sizeof *pd);
pd->item = item;
pd->flags = flags;
if (title != NULL)
pd->title = xstrdup(title);
if (style != NULL)
pd->style = xstrdup(style);
if (border_style != NULL)
pd->border_style = xstrdup(border_style);
pd->c = c;
pd->c->references++;
pd->cb = cb;
pd->arg = arg;
pd->status = 128 + SIGHUP;
pd->border_lines = lines;
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
style_apply(&pd->border_cell, o, "popup-border-style", NULL);
if (border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
pd->border_cell.bg = sytmp.gc.bg;
}
}
pd->border_cell.attr = 0;
screen_init(&pd->s, jx, jy, 0);
screen_set_default_cursor(&pd->s, global_w_options);
colour_palette_init(&pd->palette);
colour_palette_from_option(&pd->palette, global_w_options);
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
style_apply(&pd->defaults, o, "popup-style", NULL);
if (style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
pd->defaults.bg = sytmp.gc.bg;
}
}
pd->defaults.attr = 0;
pd->px = px;
pd->py = py;
pd->sx = sx;
pd->sy = sy;
pd->ppx = px;
pd->ppy = py;
pd->psx = sx;
pd->psy = sy;
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
if (pd->job == NULL) {
popup_free(pd);
return (-1);
}
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
return (0);
}

View File

@@ -1,6 +1,7 @@
#!/bin/sh
# Popups require a tty overlay and cannot be displayed by a control client.
# Popups require an attached terminal and cannot be displayed by a control
# client.
# A popup command from control mode must be ignored cleanly, leaving the
# client command queue and server usable.

View File

@@ -268,149 +268,5 @@ $TMUX kill-pane -t "$floating" || exit 1
$TMUX kill-pane -t "$right" || exit 1
$TMUX kill-pane -t "$lower" || exit 1
# --- Floating panes clamped when the window shrinks (issue #5581, PR #5582) ---
#
# layout_resize clamps floating panes back inside the window when it shrinks:
# move them and, only if they cannot fit, shrink them (never below
# PANE_MINIMUM). A floating cell is the pane's content, so with the default
# single-line border the clamp counts 1 cell of border per side, exactly as
# layout_floating_args_parse does on creation; with no border it counts 0.
# Each case below gets its own window, since resize-window fixes a window at
# a manual size for the rest of its life.
# Case 1: a lone floating pane -- break-pane -W on a window's only pane --
# takes the early-return path in layout_resize, added during PR #5582's
# review round, since there is no tiled tree to walk.
win=$($TMUX new-window -dPF '#{window_id}') ||
fail "new-window for lone float failed"
# -x 20 -y 6 -> pane 18x4; -X 60 -Y 18 -> pane_left 61, pane_top 19: with the
# border, the footprint is columns 60-79, rows 18-23, flush with the 80x24
# window's right and bottom edges, so the float fits before it is shrunk.
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
fail "break-pane -W for lone float failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 61
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 19
# Shrink to 40x16. The float still fits at its own size (18 <= 40-2, 4 <=
# 16-2) so only its position moves, flush to the new right/bottom edges:
# xoff = 40 - 18 - 1 = 21; yoff = 16 - 4 - 1 = 11.
$TMUX resize-window -t "$win" -x 40 -y 16 ||
fail "resize-window (lone float) failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
# Case 5: grow the window back. The clamp must not chase the window back
# outward -- it only ever pulls a float in, never restores where it was.
$TMUX resize-window -t "$win" -x 80 -y 24 ||
fail "resize-window grow (lone float) failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
$TMUX kill-window -t "$win" || exit 1
# Case 2: the same float, but with a tiled sibling surviving alongside it, so
# the window's root cell stays tiled and layout_resize takes the normal path
# (the clamp call after layout_fix_offsets) instead of the early return
# above. Same geometry as case 1, so the same numbers should come out.
win=$($TMUX new-window -dPF '#{window_id}') ||
fail "new-window for tiled sibling failed"
$TMUX split-window -t "$win" 'sleep 100' ||
fail "split-window for tiled sibling failed"
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
fail "break-pane -W for tiled sibling failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 61
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 19
$TMUX resize-window -t "$win" -x 40 -y 16 ||
fail "resize-window (tiled sibling) failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 21
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 11
$TMUX kill-window -t "$win" || exit 1
# Case 3: new-pane float, the original repro from issue #5581 and the PR
# body -- also the normal path, via the tiled base pane new-window creates.
win=$($TMUX new-window -dPF '#{window_id}') ||
fail "new-window for new-pane repro failed"
$TMUX resize-window -t "$win" -x 120 -y 40 ||
fail "resize-window to 120x40 failed"
# -x 30 -y 10 -> pane 28x8; -X 85 -Y 25 -> pane_left 86, pane_top 26.
floating=$($TMUX new-pane -t "$win" -dPF '#{pane_id}' \
-x 30 -y 10 -X 85 -Y 25 'sleep 100') ||
fail "new-pane for new-pane repro failed"
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 28
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 8
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 86
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 26
# Shrink to 60x20: xoff = 60 - 28 - 1 = 31; yoff = 20 - 8 - 1 = 11.
$TMUX resize-window -t "$win" -x 60 -y 20 ||
fail "resize-window (new-pane repro) failed"
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 28
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 8
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 31
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 11
$TMUX kill-window -t "$win" || exit 1
# Case 4: a float that already fits inside the shrunk window is left alone --
# assert position and size are both unchanged, not just one of them.
win=$($TMUX new-window -dPF '#{window_id}') ||
fail "new-window for untouched float failed"
# -x 20 -y 6 -> pane 18x4; -X 8 -Y 3 -> pane_left 9, pane_top 4 (as at the top
# of this file). Footprint columns 8-27, rows 3-8: well inside 60x20 too.
floating=$($TMUX new-pane -t "$win" -dPF '#{pane_id}' \
-x 20 -y 6 -X 8 -Y 3 'sleep 100') ||
fail "new-pane for untouched float failed"
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 9
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 4
$TMUX resize-window -t "$win" -x 60 -y 20 ||
fail "resize-window (untouched float) failed"
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_left}')" 9
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 4
$TMUX kill-window -t "$win" || exit 1
# Case 6: pad = 0, via the pane-border-lines window option set to none. The
# border arithmetic differs here (no -2/+1 adjustment either on creation or
# in the clamp).
win=$($TMUX new-window -dPF '#{window_id}') ||
fail "new-window for pad=0 float failed"
$TMUX set-option -w -t "$win" pane-border-lines none ||
fail "set pane-border-lines none failed"
# No border: -x 20 -y 6 -> pane 20x6 directly; -X 60 -Y 18 -> pane_left 60,
# pane_top 18 directly. Footprint (== content, no border) is columns 60-79,
# rows 18-23: flush right/bottom of the 80x24 window, same as case 1.
$TMUX break-pane -W -s "$win" -x 20 -y 6 -X 60 -Y 18 ||
fail "break-pane -W for pad=0 float failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 20
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 6
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 60
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 18
# Shrink to 40x16 with pad=0: xoff = 40 - 20 - 0 = 20; yoff = 16 - 6 - 0 = 10.
$TMUX resize-window -t "$win" -x 40 -y 16 ||
fail "resize-window (pad=0 float) failed"
must_equal "$($TMUX display-message -p -t "$win" '#{pane_width}')" 20
must_equal "$($TMUX display-message -p -t "$win" '#{pane_height}')" 6
must_equal "$($TMUX display-message -p -t "$win" '#{pane_left}')" 20
must_equal "$($TMUX display-message -p -t "$win" '#{pane_top}')" 10
$TMUX kill-window -t "$win" || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -400,6 +400,107 @@ $TMUX2 send-keys -t "$OUTER" a
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
$TMUX bind P display-popup -E -t "$p0" -w 20 -h 5 -T popup-title 'cat'
$TMUX2 send-keys -t "$OUTER" C-b P
sleep 1
modal=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$modal" ] || fail "display-popup did not create a modal pane"
must_equal "$(fmt "$modal" '#{pane_title}')" popup-title
must_equal "$($TMUX show-options -pv -t "$modal" pane-border-status)" top
must_equal "$($TMUX show-options -pv -t "$modal" pane-border-format)" \
'#{pane_title}'
$TMUX2 send-keys -t "$OUTER" C-b x z Enter
sleep 1
must_equal "$($TMUX show -gv @modal-prefix)" no
must_equal "$($TMUX show -gv @modal-root)" no
case "$($TMUX capture-pane -pt "$modal")" in
*xz*) ;;
*) fail "keys did not reach display-popup pane" ;;
esac
$TMUX2 send-keys -t "$OUTER" Escape
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
$TMUX2 send-keys -t "$OUTER" C-c
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
# Creating a popup pane must not fire the split-window hook.
check_ok set-hook -t modal after-split-window \
"set-option -g @popup-after-split yes"
check_ok set-option -g @popup-after-split no
check_ok display-popup -E -t "$p0" true
must_equal "$($TMUX show-option -gv @popup-after-split)" no
check_ok set-hook -u -t modal after-split-window
# A borderless popup must not create a zero-sized pane in a tiny window.
check_ok new-window -d -t modal: -n popup-small 'cat'
check_ok set-option -w -t modal:popup-small window-size manual
check_ok resize-window -t modal:popup-small -x 1 -y 1
small=$(fmt modal:popup-small '#{pane_id}')
check_ok bind Z display-popup -B -t "$small" 'cat'
$TMUX2 send-keys -t "$OUTER" C-b Z
sleep 1
must_equal "$(fmt modal:popup-small '#{window_panes}')" 1
must_equal "$(fmt modal:popup-small '#{window_modal_pane}')" ''
$TMUX bind D display-popup -t "$p0" -w 20 -h 5 'printf done'
$TMUX2 send-keys -t "$OUTER" C-b D
sleep 2
modal=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$modal" ] || fail "retained display-popup was not created"
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
case "$($TMUX capture-pane -pt "$modal")" in
*'Pane is dead'*) fail "display-popup showed remain-on-exit message" ;;
esac
$TMUX2 send-keys -t "$OUTER" a
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
$TMUX2 send-keys -t "$OUTER" Escape
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
$TMUX bind K display-popup -k -t "$p0" -w 20 -h 5 'printf done'
$TMUX2 send-keys -t "$OUTER" C-b K
sleep 2
modal=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$modal" ] || fail "display-popup -k was not created"
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
$TMUX2 send-keys -t "$OUTER" a
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
$TMUX bind F display-popup -EE -t "$p0" -w 20 -h 5 'exit 1'
$TMUX2 send-keys -t "$OUTER" C-b F
sleep 2
modal=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$modal" ] || fail "failed display-popup -EE did not remain"
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
$TMUX2 send-keys -t "$OUTER" a
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
$TMUX2 send-keys -t "$OUTER" Escape
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
check_ok kill-pane -t "$modal"
sleep 1
check_ok display-popup -EE -t "$p0" true
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
$TMUX bind G display-popup -EE -k -t "$p0" -w 20 -h 5 'exit 1'
$TMUX2 send-keys -t "$OUTER" C-b G
sleep 2
modal=$(fmt modal:0 '#{window_modal_pane}')
[ -n "$modal" ] || fail "failed display-popup -EE -k did not remain"
must_equal "$(fmt "$modal" '#{pane_dead}')" 1
must_equal "$($TMUX show-options -pv -t "$modal" remain-on-exit)" failed-key
$TMUX2 send-keys -t "$OUTER" a
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
check_ok display-popup -EE -k -t "$p0" true
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
# A nonmodal floating pane may remain above zoom, and switching between it and
# the zoomed tiled pane must not unzoom the window.
check_ok new-window -d -t modal: -n float-over-zoom 'cat'

View File

@@ -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

View File

@@ -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

View File

@@ -1,101 +0,0 @@
#!/bin/sh
# Exercise drawing of popups (display-popup) over the window scene. A popup is an
# overlay drawn on top of the redraw scene (the overlay_draw path in
# screen-redraw.c), so this guards against regressions in how popups appear.
#
# A popup is modal and stays open until its command exits, so each scene fully
# re-creates the servers and re-attaches; the popup is opened in the background
# (display-popup blocks the client that runs it) and the outer pane is captured
# while it is open.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX=
TMUX2=
SETUP=0
RESULTS=screen-redraw-results
TMP=$(mktemp)
cleanup() {
rm -f "$TMP"
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
}
trap cleanup 0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
C="sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"POP%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'"
# setup: fresh inner window attached inside a fresh outer pane, 40x14.
setup() {
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
SETUP=$((SETUP + 1))
TMUX="$TEST_TMUX -LtestA$$-$SETUP -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$-$SETUP -f/dev/null"
$TMUX2 new -d -x40 -y14 "$C" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX2 resizew -x40 -y14 || exit 1
$TMUX new -d -x40 -y14 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
}
# popup <args>: open a popup running a fixed command, in the background (it stays
# open because the command sleeps; the servers are killed at the next setup).
popup() {
$TMUX2 display-popup "$@" -E "sh -c 'printf POPUP; exec sleep 100'" &
sleep 1
}
# Basic popup over a single pane.
setup
popup -w20 -h6 -x6 -y3
compare popup-basic
# Popup over a split: drawn on top of the pane border.
setup
$TMUX2 splitw -h "$C" || exit 1
popup -w24 -h8 -x8 -y3
compare popup-over-split
# Popup with no border lines (-B).
setup
popup -B -w20 -h6 -x6 -y3
compare popup-noborder
# Popup with double border lines.
setup
popup -b double -w20 -h6 -x6 -y3
compare popup-double
exit 0

View File

@@ -1,12 +0,0 @@
base
╭──────────────────────────╮
│FLOAT │
│ │
│ │
│ │
│ │
│ │
╰──────────────────────────╯

View File

@@ -1,14 +0,0 @@
POP00 ┌──────────────────┐
POP01 │POPUP │
POP02 │ │
POP03 │ │
POP04 │ │
POP05 └──────────────────┘
POP06 abcdefghij
POP07 abcdefghij
POP08 abcdefghij
POP09 abcdefghij
POP10 abcdefghij
POP11 abcdefghij
POP12 abcdefghij

View File

@@ -1,14 +0,0 @@
POP00 ╔══════════════════╗
POP01 ║POPUP ║
POP02 ║ ║
POP03 ║ ║
POP04 ║ ║
POP05 ╚══════════════════╝
POP06 abcdefghij
POP07 abcdefghij
POP08 abcdefghij
POP09 abcdefghij
POP10 abcdefghij
POP11 abcdefghij
POP12 abcdefghij

View File

@@ -1,14 +0,0 @@
POP00 POPUP
POP01
POP02
POP03
POP04
POP05
POP06 abcdefghij
POP07 abcdefghij
POP08 abcdefghij
POP09 abcdefghij
POP10 abcdefghij
POP11 abcdefghij
POP12 abcdefghij

View File

@@ -1,14 +0,0 @@
POP00 ab┌──────────────────────┐fghij
POP01 ab│POPUP │fghij
POP02 ab│ │fghij
POP03 ab│ │fghij
POP04 ab│ │fghij
POP05 ab│ │fghij
POP06 ab│ │fghij
POP07 ab└──────────────────────┘fghij
POP08 abcdefghij │POP08 abcdefghij
POP09 abcdefghij │POP09 abcdefghij
POP10 abcdefghij │POP10 abcdefghij
POP11 abcdefghij │POP11 abcdefghij
POP12 abcdefghij │POP12 abcdefghij

View File

@@ -33,7 +33,7 @@
* this is done at various points, such as when a pane is moved or resized. The
* scene only includes the part of the client used for the window: panes, pane
* status lines, borders, scrollbars, and any area outside the window. The
* client status line and overlay are not included.
* client status line is not included.
*
* A scene is made from spans. A span is a horizontal run of cells on one
* visible line that can be drawn in the same way. Each span has a type, for
@@ -93,7 +93,6 @@ enum redraw_span_type {
#define REDRAW_PANE_SCROLLBAR 0x20
#define REDRAW_STATUS 0x40
#define REDRAW_MENU 0x80
#define REDRAW_OVERLAY 0x100
/* Draw everything. */
#define REDRAW_ALL 0x7fffffff
@@ -264,8 +263,6 @@ redraw_flags_to_string(int flags)
strlcat(s, "scrollbar ", sizeof s);
if (flags & REDRAW_MENU)
strlcat(s, "menu ", sizeof s);
if (flags & REDRAW_OVERLAY)
strlcat(s, "overlay ", sizeof s);
if (REDRAW_IS_ALL(flags))
strlcat(s, "all ", sizeof s);
if (*s != '\0')
@@ -1381,45 +1378,30 @@ static void
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
u_int y)
{
struct redraw_scene *scene = dctx->scene;
struct redraw_span_data *data = &span->data;
enum redraw_span_type type = data->type;
struct client *c = scene->c;
struct tty *tty = &c->tty;
struct visible_ranges *r;
struct visible_range *rr;
u_int i, x, n;
if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
return;
r = tty_check_overlay_range(tty, span->x, y, span->width);
for (i = 0; i < r->used; i++) {
rr = &r->ranges[i];
if (rr->nx == 0)
continue;
x = rr->px;
n = rr->nx;
switch (span->data.type) {
case REDRAW_SPAN_PANE:
redraw_draw_pane_span(dctx, span, x, y, n);
break;
case REDRAW_SPAN_BORDER:
case REDRAW_SPAN_EMPTY:
case REDRAW_SPAN_OUTSIDE:
redraw_draw_border_span(dctx, span, x, y, n);
break;
case REDRAW_SPAN_STATUS:
redraw_draw_status_span(dctx, span, x, y, n);
break;
case REDRAW_SPAN_SCROLLBAR:
redraw_draw_scrollbar_span(dctx, span, x, y, n);
break;
case REDRAW_SPAN_MENU:
redraw_draw_menu_span(dctx, span, x, y, n);
break;
}
switch (span->data.type) {
case REDRAW_SPAN_PANE:
redraw_draw_pane_span(dctx, span, span->x, y, span->width);
break;
case REDRAW_SPAN_BORDER:
case REDRAW_SPAN_EMPTY:
case REDRAW_SPAN_OUTSIDE:
redraw_draw_border_span(dctx, span, span->x, y, span->width);
break;
case REDRAW_SPAN_STATUS:
redraw_draw_status_span(dctx, span, span->x, y, span->width);
break;
case REDRAW_SPAN_SCROLLBAR:
redraw_draw_scrollbar_span(dctx, span, span->x, y, span->width);
break;
case REDRAW_SPAN_MENU:
redraw_draw_menu_span(dctx, span, span->x, y, span->width);
break;
}
}
@@ -1688,10 +1670,8 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
struct screen *sl;
struct redraw_scene *scene;
struct window_pane *loop;
u_int width, i, y, lines, j;
u_int width, i, y, lines;
struct redraw_span *first;
struct visible_ranges *r;
struct visible_range *rr;
int redraw;
if (c->flags & CLIENT_SUSPENDED)
@@ -1799,19 +1779,9 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
else
y = c->tty.sy - lines;
sl = c->status.active;
for (i = 0; i < lines; i++) {
r = tty_check_overlay_range(tty, 0, y + i, tty->sx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx == 0)
continue;
tty_draw_line(tty, sl, rr->px, i, rr->nx,
rr->px, y + i, NULL);
}
}
for (i = 0; i < lines; i++)
tty_draw_line(tty, sl, 0, i, tty->sx, 0, y + i, NULL);
}
if (c->overlay_draw != NULL && (flags & REDRAW_OVERLAY))
c->overlay_draw(c, c->overlay_data);
tty_reset(tty);
@@ -1867,18 +1837,13 @@ redraw_screen(struct client *c)
{
int flags = 0;
if (c->flags & CLIENT_REDRAWWINDOW) {
if (c->flags & CLIENT_REDRAWOVERLAY)
redraw_draw(c, NULL, REDRAW_ALL);
else
redraw_draw(c, NULL, REDRAW_ALL & ~REDRAW_OVERLAY);
} else {
if (c->flags & CLIENT_REDRAWWINDOW)
redraw_draw(c, NULL, REDRAW_ALL);
else {
if (c->flags & CLIENT_REDRAWBORDERS)
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
if (c->flags & CLIENT_REDRAWSTATUS)
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
if (c->flags & CLIENT_REDRAWOVERLAY)
flags |= REDRAW_OVERLAY;
if (c->flags & CLIENT_REDRAWMENU)
flags |= REDRAW_MENU;
if (c->session->curw->window->menu != NULL)

View File

@@ -305,21 +305,16 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
if (~ctx->flags & SCREEN_WRITE_SYNC) {
/*
* For the active pane showing its base screen or for an
* overlay (no pane), only use synchronized updates if
* requested (commands that move the cursor); for other panes
* or a pane in a mode, always use it, since the cursor will
* have to move.
* For the active pane showing its base screen, only use
* synchronized updates if requested (commands that move the
* cursor); for other panes or a pane in a mode, always use it,
* since the cursor will have to move.
*/
if (ctx->wp != NULL && (ctx->wp != ctx->wp->window->active ||
ctx->wp->screen != &ctx->wp->base))
ttyctx->flags |= TTY_CTX_SYNC;
else {
if (ctx->wp == NULL)
ttyctx->flags |= TTY_CTX_OVERLAY_SYNC;
if (is_sync)
ttyctx->flags |= TTY_CTX_SYNC;
}
else if (is_sync)
ttyctx->flags |= TTY_CTX_SYNC;
tty_write(tty_cmd_syncstart, ttyctx);
ctx->flags |= SCREEN_WRITE_SYNC;
}
@@ -439,7 +434,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);
@@ -715,7 +709,7 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
if (!window_position_is_visible(r, xoff + s->cx))
break;
ttyctx.cell = &gc;
ttyctx.flags &= (TTY_CTX_OVERLAY_SYNC|TTY_CTX_SYNC);
ttyctx.flags &= TTY_CTX_SYNC;
tty_write(tty_cmd_cell, &ttyctx);
ttyctx.ocx++;

View File

@@ -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,

View File

@@ -67,77 +67,6 @@ server_client_how_many(void)
return (n);
}
/* Overlay timer callback. */
static void
server_client_overlay_timer(__unused int fd, __unused short events, void *data)
{
server_client_clear_overlay(data);
}
/* Set an overlay on client. */
void
server_client_set_overlay(struct client *c, u_int delay,
overlay_check_cb checkcb, overlay_mode_cb modecb,
overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb,
overlay_resize_cb resizecb, void *data)
{
struct timeval tv;
if (c->overlay_draw != NULL)
server_client_clear_overlay(c);
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
if (event_initialized(&c->overlay_timer))
evtimer_del(&c->overlay_timer);
evtimer_set(&c->overlay_timer, server_client_overlay_timer, c);
if (delay != 0)
evtimer_add(&c->overlay_timer, &tv);
c->overlay_check = checkcb;
c->overlay_mode = modecb;
c->overlay_draw = drawcb;
c->overlay_key = keycb;
c->overlay_free = freecb;
c->overlay_resize = resizecb;
c->overlay_data = data;
if (c->overlay_check == NULL)
c->tty.flags |= TTY_FREEZE;
if (c->overlay_mode == NULL)
c->tty.flags |= TTY_NOCURSOR;
window_update_focus(c->session->curw->window);
server_redraw_client(c);
}
/* Clear overlay mode on client. */
void
server_client_clear_overlay(struct client *c)
{
if (c->overlay_draw == NULL)
return;
if (event_initialized(&c->overlay_timer))
evtimer_del(&c->overlay_timer);
if (c->overlay_free != NULL)
c->overlay_free(c, c->overlay_data);
c->overlay_check = NULL;
c->overlay_mode = NULL;
c->overlay_draw = NULL;
c->overlay_key = NULL;
c->overlay_free = NULL;
c->overlay_resize = NULL;
c->overlay_data = NULL;
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
if (c->session != NULL)
window_update_focus(c->session->curw->window);
server_redraw_client(c);
}
/* Are these ranges empty? That is, nothing is visible. */
int
server_client_ranges_is_empty(struct visible_ranges *r)
@@ -161,52 +90,6 @@ server_client_ensure_ranges(struct visible_ranges *r, u_int n)
r->size = n;
}
/*
* Given overlay position and dimensions, return parts of the input range which
* are visible.
*/
void
server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
u_int py, u_int nx, struct visible_ranges *r)
{
u_int ox, onx;
/* Trivial case of no overlap in the y direction. */
if (py < y || py > y + sy - 1) {
server_client_ensure_ranges(r, 1);
r->ranges[0].px = px;
r->ranges[0].nx = nx;
r->used = 1;
return;
}
server_client_ensure_ranges(r, 2);
/* Visible bit to the left of the popup. */
if (px < x) {
r->ranges[0].px = px;
r->ranges[0].nx = x - px;
if (r->ranges[0].nx > nx)
r->ranges[0].nx = nx;
} else {
r->ranges[0].px = 0;
r->ranges[0].nx = 0;
}
/* Visible bit to the right of the popup. */
ox = x + sx;
if (px > ox)
ox = px;
onx = px + nx;
if (onx > ox) {
r->ranges[1].px = ox;
r->ranges[1].nx = onx - ox;
} else {
r->ranges[1].px = 0;
r->ranges[1].nx = 0;
}
r->used = 2;
}
/* Check if this client is inside this server. */
int
server_client_check_nested(struct client *c)
@@ -490,7 +373,6 @@ server_client_lost(struct client *c)
cfg_client = NULL;
c->flags |= CLIENT_DEAD;
server_client_clear_overlay(c);
status_prompt_clear(c);
status_message_clear(c);
@@ -1746,10 +1628,6 @@ server_client_handle_key0(struct client *c, struct key_event *event,
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return (0);
/*
* Handle theme reporting keys before overlays so they work even when a
* popup is open.
*/
if (event->key == KEYC_REPORT_LIGHT_THEME) {
server_client_report_theme(c, THEME_LIGHT);
return (0);
@@ -1760,10 +1638,9 @@ server_client_handle_key0(struct client *c, struct key_event *event,
}
/*
* Key presses in overlay mode, dead panes waiting for a key, modal cancel
* keys, panes capturing all keys and the command prompt are special cases.
* The queue might be blocked so they need to be processed immediately
* rather than queued.
* Dead panes waiting for a key, modal cancel keys, panes capturing all keys
* and the command prompt are special cases. The queue might be blocked so
* they need to be processed immediately rather than queued.
*/
if (~c->flags & CLIENT_READONLY) {
if (c->message_string != NULL) {
@@ -1772,22 +1649,11 @@ server_client_handle_key0(struct client *c, struct key_event *event,
status_message_clear(c);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, c->overlay_data, event)) {
case 0:
return (0);
case 1:
server_client_clear_overlay(c);
return (0);
}
}
server_client_clear_overlay(c);
wp = s->curw->window->active;
if (server_client_handle_dead_key(wp, event->key))
return (0);
if (wp != NULL && wp == wp->window->modal &&
if (wp != NULL &&
wp == wp->window->modal &&
(wp->flags & PANE_CLOSEONCANCEL) &&
(event->key == '\033' || event->key == ('c'|KEYC_CTRL))) {
server_kill_pane(wp);
@@ -2177,11 +2043,8 @@ server_client_reset_state(struct client *c)
flags = (tty->flags & TTY_BLOCK);
tty->flags &= ~TTY_BLOCK;
/* Get mode from overlay if any, else from screen. */
if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (w->menu != NULL) {
/* Get mode from the menu if any, else from the screen. */
if (w->menu != NULL) {
menu_get_cursor(w->menu, &cx, &cy);
s = menu_screen(w->menu);
} else if (wp != NULL && c->prompt == NULL)
@@ -2194,7 +2057,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);
@@ -2204,7 +2066,7 @@ server_client_reset_state(struct client *c)
if (c->prompt != NULL) {
prompt = 1;
status_prompt_cursor(c, &cx, &cy);
} else if (wp != NULL && c->overlay_draw == NULL) {
} else if (wp != NULL) {
if (w->menu != NULL) {
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (cx < ox || cx >= ox + sx ||
@@ -2259,7 +2121,7 @@ server_client_reset_state(struct client *c)
if (!cursor)
mode &= ~MODE_CURSOR;
}
} else if (c->overlay_mode == NULL || s == NULL)
} else if (s == NULL)
mode &= ~MODE_CURSOR;
if (~pane_mode & MODE_SYNC) {
log_debug("%s: cursor to %u,%u", __func__, cx, cy);
@@ -2276,7 +2138,7 @@ server_client_reset_state(struct client *c)
* movement events.
*/
if (options_get_number(oo, "mouse")) {
if (c->overlay_draw == NULL && w->menu == NULL) {
if (w->menu == NULL) {
mode &= ~ALL_MOUSE_MODES;
TAILQ_FOREACH(loop, &w->panes, entry) {
if (loop->screen->mode & MODE_MOUSE_ALL)
@@ -2292,7 +2154,7 @@ server_client_reset_state(struct client *c)
}
/* Clear bracketed paste mode if at the prompt. */
if (c->overlay_draw == NULL && prompt)
if (prompt)
mode &= ~MODE_BRACKETPASTE;
/* Set the terminal mode and reset attributes. */
@@ -2494,11 +2356,10 @@ server_client_check_redraw(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
log_debug("%s: redraw%s%s%s%s%s", c->name,
log_debug("%s: redraw%s%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "");
}
@@ -2706,10 +2567,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
tty_resize(&c->tty);
tty_repeat_requests(&c->tty, 0);
recalculate_sizes();
if (c->overlay_resize == NULL)
server_client_clear_overlay(c);
else
c->overlay_resize(c, c->overlay_data);
server_redraw_client(c);
if (c->session != NULL)
server_client_fire_resized(c, old_sx, old_sy);

View File

@@ -577,8 +577,6 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
struct prompt_create_data pd;
struct status_prompt_data *spd;
server_client_clear_overlay(c);
status_message_clear(c);
status_prompt_clear(c);
status_push_screen(c);

255
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1170 2026/09/11 10:17:16 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1169 2026/09/10 11:02:18 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 10 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
@@ -5133,9 +5103,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
@@ -5386,10 +5353,16 @@ section on how to specify
.Ar style .
.It Ic menu\-border\-lines Ar type
Set the type of characters used for drawing menu borders.
See
.Ic popup\-border\-lines
for possible values for
.Ar border\-lines .
.Ar type
may be one of
.Ic single ,
.Ic rounded ,
.Ic double ,
.Ic heavy ,
.Ic simple ,
.Ic padded ,
or
.Ic none .
.It Ic message\-command\-style Ar style
Set status line message command style.
This is used for the command prompt with
@@ -6013,8 +5986,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
@@ -6049,48 +6020,6 @@ see the
section.
Attributes are ignored.
.Pp
.It Ic popup\-style Ar style
Set the popup style.
See the
.Sx STYLES
section on how to specify
.Ar style .
Attributes are ignored.
.Pp
.It Ic popup\-border\-style Ar style
Set the popup border style.
See the
.Sx STYLES
section on how to specify
.Ar style .
Attributes are ignored.
.Pp
.It Ic popup\-border\-lines Ar type
Set the type of characters used for drawing popup borders.
.Ar type
may be one of:
.Bl -tag -width Ds
.It single
single lines using ACS or UTF\-8 characters (default)
.It rounded
variation of single with rounded corners using UTF\-8 characters
.It double
double lines using UTF\-8 characters
.It heavy
heavy lines using UTF\-8 characters
.It simple
simple ASCII characters
.It padded
simple ASCII space character
.It none
no border
.El
.Pp
.Ql double
and
.Ql heavy
will fall back to standard ACS line drawing when UTF\-8 is not supported.
.Pp
.It Xo Ic pane\-scrollbars
.Op Ic off | modal | on | auto\-hide
.Xc
@@ -7342,7 +7271,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"
@@ -8238,10 +8166,16 @@ command should be omitted.
.Pp
.Fl b
sets the type of characters used for drawing menu borders.
See
.Ic popup\-border\-lines
for possible values for
.Ar border\-lines .
.Ar border\-lines
may be one of
.Ic single ,
.Ic rounded ,
.Ic double ,
.Ic heavy ,
.Ic simple ,
.Ic padded ,
or
.Ic none .
.Pp
.Fl H
sets the style for the selected menu item (see
@@ -8277,15 +8211,18 @@ Both may be a row or column number, or one of the following special values:
.It Li "S" Ta Fl y Ta "The line above or below the status line"
.El
.Pp
Or a format, which is expanded including the following additional variables:
Or a format, which is expanded with the following additional variables.
The
.Ql popup_
prefix is retained for compatibility:
.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
.It Sy "Variable name" Ta Sy "Replaced with"
.It Li "popup_centre_x" Ta "Centered in the client"
.It Li "popup_centre_y" Ta "Centered in the client"
.It Li "popup_height" Ta "Height of menu or popup"
.It Li "popup_centre_x" Ta "Centered in the window"
.It Li "popup_centre_y" Ta "Centered in the window"
.It Li "popup_height" Ta "Height of the menu"
.It Li "popup_last_x" Ta "Left of the last menu"
.It Li "popup_last_y" Ta "Bottom of the last menu"
.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse"
.It Li "popup_mouse_bottom" Ta "Bottom at the mouse"
.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse"
.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse"
.It Li "popup_mouse_top" Ta "Top at the mouse"
@@ -8296,7 +8233,7 @@ Or a format, which is expanded including the following additional variables:
.It Li "popup_pane_right" Ta "Right of the pane"
.It Li "popup_pane_top" Ta "Top of the pane"
.It Li "popup_status_line_y" Ta "Above or below the status line"
.It Li "popup_width" Ta "Width of menu or popup"
.It Li "popup_width" Ta "Width of the menu"
.It Li "popup_window_status_line_x" Ta "At the window position in status line"
.It Li "popup_window_status_line_y" Ta "At the status line showing the window"
.El
@@ -8382,116 +8319,6 @@ lists the format variables and their values.
.Fl I
forwards any input read from stdin to the empty pane given by
.Ar target\-pane .
.Tg popup
.It Xo Ic display\-popup
.Op Fl BCEkN
.Op Fl b Ar border\-lines
.Op Fl c Ar target\-client
.Op Fl d Ar start\-directory
.Op Fl e Ar environment
.Op Fl h Ar height
.Op Fl s Ar style
.Op Fl S Ar border\-style
.Op Fl t Ar target\-pane
.Op Fl T Ar title
.Op Fl w Ar width
.Op Fl x Ar position
.Op Fl y Ar position
.Op Ar shell\-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic popup
Display a popup running
.Ar shell\-command
(or
.Ar default\-command
when omitted) on
.Ar target\-client .
A popup is a rectangular box drawn over the top of any panes.
If the command is run inside an existing popup, that popup is modified.
Only the
.Fl b ,
.Fl B ,
.Fl C ,
.Fl E ,
.Fl EE ,
.Fl K ,
.Fl N ,
.Fl s ,
and
.Fl S
options are accepted in this case;
all others are ignored.
.Pp
.Fl E
closes the popup automatically when
.Ar shell\-command
exits.
Two
.Fl E
closes the popup only if
.Ar shell\-command
exited with success.
.Fl k
allows any key to dismiss the popup instead of only
.Ql Escape
or
.Ql C\-c .
.Pp
.Fl x
and
.Fl y
give the position of the popup, they have the same meaning as for the
.Ic display\-menu
command.
.Fl w
and
.Fl h
give the width and height - both may be a percentage (followed by
.Ql % ) .
If omitted, half of the terminal size is used.
.Pp
.Fl B
does not surround the popup by a border.
.Pp
.Fl b
sets the type of characters used for drawing popup borders.
When
.Fl B
is specified, the
.Fl b
option is ignored.
See
.Ic popup\-border\-lines
for possible values for
.Ar border\-lines .
.Pp
.Fl s
sets the style for the popup and
.Fl S
sets the style for the popup border (see
.Sx STYLES ) .
.Pp
.Fl e
takes the form
.Ql VARIABLE=value
and sets an environment variable for the popup; it may be specified multiple
times.
.Pp
.Fl T
is a format for the popup title (see
.Sx FORMATS ) .
.Pp
The
.Fl C
flag closes any popup on the client.
.Pp
.Fl N
disables any previously specified
.Fl E ,
.Fl EE ,
or
.Fl k
option.
.Tg showphist
.It Xo Ic show\-prompt\-history
.Op Fl T Ar prompt\-type

164
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1440 2026/09/11 10:17:16 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1439 2026/09/10 11:02:18 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,
@@ -775,16 +702,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 +1068,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 +1089,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 +1150,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 +1738,6 @@ struct tty_term {
#define TERM_VT100LIKE 0x20
#define TERM_SIXEL 0x40
#define TERM_INVALIDMS 0x80
#define TERM_KITTYKEYS 0x100
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1888,7 +1796,6 @@ struct tty {
size_t discarded;
struct termios tio;
struct visible_ranges r;
struct grid_cell cell;
struct grid_cell last_cell;
@@ -1910,12 +1817,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;
@@ -1953,7 +1857,6 @@ struct tty_ctx {
#define TTY_CTX_INVISIBLE_PANES 0x2
#define TTY_CTX_WINDOW_BIGGER 0x4
#define TTY_CTX_SYNC 0x8
#define TTY_CTX_OVERLAY_SYNC 0x10
#define TTY_CTX_CELL_INVALIDATE 0x20
#define TTY_CTX_PANE_OBSCURED 0x40
@@ -2288,16 +2191,6 @@ struct prompt_draw_data {
u_int prompt_line;
};
/* Overlay callbacks */
typedef struct visible_ranges *(*overlay_check_cb)(struct client *, void *,
u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
u_int *);
typedef void (*overlay_draw_cb)(struct client *, void *);
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *);
/* Client connection. */
struct client {
const char *name;
@@ -2383,7 +2276,7 @@ struct client {
#define CLIENT_SIZECHANGED 0x400000
#define CLIENT_STATUSOFF 0x800000
#define CLIENT_REDRAWSTATUSALWAYS 0x1000000
#define CLIENT_REDRAWOVERLAY 0x2000000
/* 0x2000000 unused */
#define CLIENT_CONTROL_NOOUTPUT 0x4000000
#define CLIENT_DEFAULTSOCKET 0x8000000
#define CLIENT_STARTSERVER 0x10000000
@@ -2404,7 +2297,6 @@ struct client {
CLIENT_REDRAWSTATUS| \
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY| \
CLIENT_REDRAWMENU)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
@@ -2450,15 +2342,6 @@ struct client {
u_int pan_ox;
u_int pan_oy;
overlay_check_cb overlay_check;
overlay_mode_cb overlay_mode;
overlay_draw_cb overlay_draw;
overlay_key_cb overlay_key;
overlay_free_cb overlay_free;
overlay_resize_cb overlay_resize;
void *overlay_data;
struct event overlay_timer;
struct client_files files;
u_int source_file_depth;
@@ -3060,15 +2943,12 @@ void tty_default_attributes(struct tty *, u_int,
void tty_update_mode(struct tty *, int, struct screen *);
const struct grid_cell *tty_check_codeset(struct tty *,
const struct grid_cell *);
struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
u_int);
void tty_sync_start(struct tty *);
void tty_sync_end(struct tty *);
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 *);
@@ -3413,14 +3293,8 @@ int server_create_socket(uint64_t, char **);
/* server-client.c */
u_int server_client_how_many(void);
void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
overlay_free_cb, overlay_resize_cb, void *);
void server_client_clear_overlay(struct client *);
void server_client_ensure_ranges(struct visible_ranges *, u_int);
int server_client_ranges_is_empty(struct visible_ranges *);
void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int,
u_int, struct visible_ranges *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *);
@@ -3529,7 +3403,7 @@ void recalculate_sizes_now(int);
/* input.c */
#define INPUT_BUF_DEFAULT_SIZE 1048576
struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
struct colour_palette *, struct client *);
struct colour_palette *);
void input_free(struct input_ctx *);
void input_reset(struct input_ctx *, int);
struct evbuffer *input_pending(struct input_ctx *);
@@ -3550,20 +3424,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);
@@ -4298,20 +4158,6 @@ void menu_get_cursor(struct menu_data *, u_int *, u_int *);
void menu_resize(struct menu_data *, struct window *);
int menu_key(struct client *, struct menu_data *, struct key_event *);
/* popup.c */
#define POPUP_CLOSEEXIT 0x1
#define POPUP_CLOSEEXITZERO 0x2
#define POPUP_CLOSEANYKEY 0x4
typedef void (*popup_close_cb)(int, void *);
int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
u_int, u_int, u_int, struct environ *, const char *, int,
char **, const char *, const char *, struct client *,
struct session *, const char *, const char *,
popup_close_cb, void *);
int popup_present(struct client *);
int popup_modify(struct client *, const char *, const char *,
const char *, enum box_lines, int);
/* style.c */
int style_parse(struct style *,const struct grid_cell *,
const char *);

View File

@@ -52,8 +52,7 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
return;
/* If genuine BCE is available, can try escape sequences. */
if (tty->client->overlay_check == NULL &&
!wrapped &&
if (!wrapped &&
nx >= 10 &&
!tty_fake_bce(tty, defaults, bg)) {
/* Off the end of the line, use EL if available. */

View File

@@ -235,13 +235,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",
@@ -393,7 +386,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,

View File

@@ -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;

View File

@@ -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(&copy, ":")) != 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(&copy, ";")) != 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);
}

232
tty.c
View File

@@ -66,7 +66,6 @@ static void tty_redraw_region(struct tty *, const struct tty_ctx *);
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
enum tty_code_code, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static int tty_check_overlay(struct tty *, u_int, u_int);
#ifdef ENABLE_SIXEL
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
@@ -409,8 +408,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)
@@ -509,10 +506,6 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, "\033[?7727l");
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));
@@ -551,8 +544,6 @@ void
tty_free(struct tty *tty)
{
tty_close(tty);
free(tty->r.ranges);
}
void
@@ -565,12 +556,8 @@ 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)
@@ -1211,9 +1198,6 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
struct visible_ranges *r;
struct visible_range *rr;
u_int i;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
@@ -1222,7 +1206,7 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
return;
/* If genuine BCE is available, can try escape sequences. */
if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
if (!tty_fake_bce(tty, defaults, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
@@ -1245,18 +1229,9 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
}
}
/*
* Couldn't use an escape sequence, use spaces. Clear only the visible
* bit if there is an overlay.
*/
r = tty_check_overlay_range(tty, px, py, nx);
for (i = 0; i < r->used; i++) {
rr = &r->ranges[i];
if (rr->nx != 0) {
tty_cursor(tty, rr->px, py);
tty_repeat_space(tty, rr->nx);
}
}
/* Couldn't use an escape sequence, use spaces. */
tty_cursor(tty, px, py);
tty_repeat_space(tty, nx);
}
/* Clear a line, adjusting to visible part of pane. */
@@ -1265,22 +1240,12 @@ tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
u_int px, u_int nx, u_int bg)
{
struct client *c = tty->client;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, l, x, rx, ry;
u_int l, x, rx, ry;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
r = tty_check_overlay_range(tty, x, ry, rx);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
tty_clear_line(tty, &ctx->defaults, ry, ri->px, ri->nx,
bg);
}
}
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry))
tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
}
/* Clamp area position to visible part of pane. */
@@ -1361,11 +1326,8 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
if (nx == 0 || ny == 0)
return;
/*
* If there is an overlay or BCE is not available, cannot clear as a
* region.
*/
if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
/* If BCE is available, can try to clear as a region. */
if (!tty_fake_bce(tty, defaults, bg)) {
/* Use ED if clearing off the bottom of the terminal. */
if (px == 0 &&
px + nx >= tty->sx &&
@@ -1437,53 +1399,28 @@ static void
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
{
struct screen *s = ctx->s;
u_int nx = ctx->sx, i, x, rx, ry, j;
struct visible_ranges *r;
struct visible_range *rr;
u_int nx = ctx->sx, i, x, rx, ry;
log_debug("%s: %s %u", __func__, tty->client->name, py);
if (~ctx->flags & TTY_CTX_WINDOW_BIGGER) {
r = tty_check_overlay_range(tty, ctx->xoff, ctx->yoff + py, nx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx == 0)
continue;
tty_draw_line(tty, s, rr->px - ctx->xoff, py, rr->nx,
rr->px, ctx->yoff + py, &ctx->style_ctx);
}
tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
&ctx->style_ctx);
return;
}
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
r = tty_check_overlay_range(tty, x, ry, rx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx == 0)
continue;
tty_draw_line(tty, s, i + rr->px - x, py, rr->nx,
rr->px, ry, &ctx->style_ctx);
}
}
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry))
tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->style_ctx);
}
void
tty_cmd_redrawline(struct tty *tty, const struct tty_ctx *ctx)
{
u_int i, x, rx, ry, j;
struct visible_ranges *r;
struct visible_range *rr;
u_int i, x, rx, ry;
if (tty_clamp_line(tty, ctx, ctx->ocx, ctx->ocy, ctx->n,
&i, &x, &rx, &ry)) {
r = tty_check_overlay_range(tty, x, ry, rx);
for (j = 0; j < r->used; j++) {
rr = &r->ranges[j];
if (rr->nx == 0)
continue;
tty_draw_line(tty, ctx->s, ctx->ocx + i + rr->px - x,
ctx->ocy, rr->nx, rr->px, ry, &ctx->style_ctx);
}
}
&i, &x, &rx, &ry))
tty_draw_line(tty, ctx->s, ctx->ocx + i, ctx->ocy, rx, x, ry,
&ctx->style_ctx);
}
/* Check if character needs to be mapped for codeset. */
@@ -1520,36 +1457,6 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new);
}
/* Check if a single character is covered by the overlay. */
static int
tty_check_overlay(struct tty *tty, u_int px, u_int py)
{
struct visible_ranges *r;
/*
* With a single character, if there is anything visible (that is, the
* range is not empty), it must be that character.
*/
r = tty_check_overlay_range(tty, px, py, 1);
return (!server_client_ranges_is_empty(r));
}
/* Return parts of the input range which are visible. */
struct visible_ranges *
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
{
struct client *c = tty->client;
if (c->overlay_check == NULL) {
server_client_ensure_ranges(&tty->r, 1);
tty->r.ranges[0].px = px;
tty->r.ranges[0].nx = nx;
tty->r.used = 1;
return (&tty->r);
}
return (c->overlay_check(c, c->overlay_data, px, py, nx));
}
#ifdef ENABLE_SIXEL
/* Update context for client. */
static int
@@ -1693,14 +1600,11 @@ tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
void
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
!tty_full_width(tty, ctx) ||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
(!tty_term_has(tty->term, TTYC_ICH) &&
!tty_term_has(tty->term, TTYC_ICH1)) ||
c->overlay_check != NULL) {
!tty_term_has(tty->term, TTYC_ICH1))) {
tty_draw_pane(tty, ctx, ctx->ocy);
return;
}
@@ -1715,14 +1619,11 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
!tty_full_width(tty, ctx) ||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
(!tty_term_has(tty->term, TTYC_DCH) &&
!tty_term_has(tty->term, TTYC_DCH1)) ||
c->overlay_check != NULL) {
!tty_term_has(tty->term, TTYC_DCH1))) {
tty_draw_pane(tty, ctx, ctx->ocy);
return;
}
@@ -1745,16 +1646,13 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
!tty_full_width(tty, ctx) ||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
!tty_term_has(tty->term, TTYC_CSR) ||
!tty_term_has(tty->term, TTYC_IL1) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1772,16 +1670,13 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
!tty_full_width(tty, ctx) ||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
!tty_term_has(tty->term, TTYC_CSR) ||
!tty_term_has(tty->term, TTYC_DL1) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1825,8 +1720,6 @@ tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if (ctx->ocy != ctx->orupper)
return;
@@ -1837,8 +1730,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
(!tty_term_has(tty->term, TTYC_RI) &&
!tty_term_has(tty->term, TTYC_RIN)) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1858,8 +1750,6 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if (ctx->ocy != ctx->orlower)
return;
@@ -1868,8 +1758,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
tty_fake_bce(tty, &ctx->defaults, 8) ||
!tty_term_has(tty->term, TTYC_CSR) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1900,7 +1789,6 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
u_int i;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
@@ -1908,8 +1796,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
tty_fake_bce(tty, &ctx->defaults, 8) ||
!tty_term_has(tty->term, TTYC_CSR) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -1939,7 +1826,6 @@ void
tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
{
u_int i;
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
@@ -1948,8 +1834,7 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
(!tty_term_has(tty->term, TTYC_RI) &&
!tty_term_has(tty->term, TTYC_RIN)) ||
ctx->sx == 1 ||
ctx->sy == 1 ||
c->overlay_check != NULL) {
ctx->sy == 1) {
tty_redraw_region(tty, ctx);
return;
}
@@ -2037,11 +1922,9 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
u_int i, j;
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
c->overlay_check != NULL) {
if (ctx->flags & TTY_CTX_WINDOW_BIGGER) {
ctx->redraw_cb(ctx);
return;
}
@@ -2061,29 +1944,9 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
{
const struct grid_cell *gcp = ctx->cell;
struct screen *s = ctx->s;
struct visible_ranges *r;
u_int px, py, i, vis = 0;
px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy;
if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1))
return;
if (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))
return;
if (gcp->data.width > 1) { /* could be partially obscured */
r = tty_check_overlay_range(tty, px, py, gcp->data.width);
for (i = 0; i < r->used; i++)
vis += r->ranges[i].nx;
if (vis < gcp->data.width) {
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
px, py, &ctx->style_ctx);
return;
}
}
if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
ctx->ocy == ctx->orlower &&
tty_full_width(tty, ctx))
@@ -2103,9 +1966,6 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
{
struct visible_ranges *r;
struct visible_range *ri;
u_int i, px, py, cx;
const char *cp = ctx->data.data;
size_t n = ctx->data.size;
@@ -2131,20 +1991,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_attributes(tty, ctx->cell, &ctx->style_ctx);
/* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy;
r = tty_check_overlay_range(tty, px, py, n);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx != 0) {
cx = ri->px - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
}
}
tty_putn(tty, cp, n, n);
}
void
@@ -2238,23 +2085,8 @@ tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
{
struct client *c = tty->client;
if ((ctx->flags & TTY_CTX_OVERLAY_SYNC) &&
(ctx->flags & TTY_CTX_SYNC)) {
/*
* This is an overlay and a command that moves the cursor so
* start synchronized updates.
*/
if (ctx->flags & TTY_CTX_SYNC)
tty_sync_start(tty);
} else if (~ctx->flags & TTY_CTX_OVERLAY_SYNC) {
/*
* This is a pane. If there is an overlay, always start;
* otherwise, only if requested.
*/
if ((ctx->flags & TTY_CTX_SYNC) || c->overlay_draw != NULL)
tty_sync_start(tty);
}
}
void
@@ -2273,10 +2105,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
if (gc->flags & GRID_FLAG_PADDING)
return;
/* Check if character is covered by overlay or floating pane. */
if (!tty_check_overlay(tty, tty->cx, tty->cy))
return;
/* Check the output codeset and apply attributes. */
gcp = tty_check_codeset(tty, gc);
tty_attributes(tty, gcp, style_ctx);

View File

@@ -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]);

View File

@@ -665,7 +665,7 @@ window_copy_view_init(struct window_mode_entry *wme,
data->backing = xmalloc(sizeof *data->backing);
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
data->ictx = input_init(NULL, NULL, NULL, NULL);
data->ictx = input_init(NULL, NULL, NULL);
data->mx = data->cx;
data->my = screen_hsize(data->backing) + data->cy - data->oy;
data->showmark = 0;

View File

@@ -683,7 +683,6 @@ window_pane_update_focus(struct window_pane *wp)
c->session->attached != 0 &&
(c->flags & CLIENT_FOCUSED) &&
c->session->curw->window == wp->window &&
c->overlay_draw == NULL &&
wp->window->menu == NULL) {
focused = 1;
break;
@@ -1625,7 +1624,7 @@ window_pane_set_event(struct window_pane *wp)
NULL, window_pane_error_callback, wp);
if (wp->event == NULL)
fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette, NULL);
wp->ictx = input_init(wp, wp->event, &wp->palette);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
}