Merge branch 'master' into no_more_overlays

This commit is contained in:
Nicholas Marriott
2026-07-21 08:35:07 +01:00
32 changed files with 941 additions and 286 deletions

162
CHANGES
View File

@@ -1,3 +1,165 @@
CHANGES FROM 3.7b TO 3.8
* Many improvements to floating panes:
- new-pane and split-window can now set the title with -T and border lines
with -B, and new-pane -W waits for the pane command to exit;
- dragging with Ctrl now creates a new floating pane;
- modal panes may be created with new-pane -O; a window can have one modal
pane and it prevents interaction with the other panes while it is active.
A modal pane is now used for the editor in buffer mode;
- move-pane and resize-pane can now move and resize floating panes, including
mouse dragging;
- split-window in a floating pane creates a new floating pane that does not
overlap the active pane, if possible;
- break-pane can float a tiled pane, join-pane can tile a floating pane, and
swap-pane works with floating panes;
- choose-tree -h and choose-client -h hide the pane containing the mode, and
-k for choose commands kills the pane when the mode exits, which is useful
for modes in floating panes;
- pane-border-status has top-floating and bottom-floating, and there are new
default bindings under C-b g for common move and resize operations;
- floating panes are unzoomed before creation to avoid a crash.
* Add support for themes and improve default colours:
- tmux now has builtin light and dark colour themes for terminals with 256 or
more colours; a new theme option controls whether tmux detects the terminal
theme, uses terminal colours, or forces light or dark (set to terminal for
the old colours);
- styles can use new theme colour names such as themeblack, themewhite and
themegreen, and colours in styles are expanded as formats;
- the terminal's own theme is reported to panes instead of being guessed from
the background, and inferred more accurately when theme reporting is not
supported (Ayman Bagabas, issue 5343);
- tree-mode-selection-style has been added, tree mode selection styling now
covers the whole line, and built-in modes use improved theme colours.
* Improvements to customize mode:
- hooks and environment variables are now shown;
- e opens the selected value in an editor;
- C toggles showing only changed items;
- array item keys can now be changed.
* Hooks (and control mode notifications) are now handled internally by using
events, each of which may carry keys and values as a payload. The control
mode subscriptions mechanism has been extended to allow general-purpose
"monitors" which check a format every second and trigger a hook when it
changes or becomes true:
- set-hook -B adds a monitor hook using the same subscription syntax as
refresh-client -B, and show-hooks -B lists them. -T runs the hook only when
the format is true;
- wait-for -E waits for hooks, notifications or user events, and set-hook -E
fires an event immediately;
- hooks have additional keys available. wait-for -v can be used to see the
keys sent with a hook event;
- new hooks to cover areas including client create and destroy, pane
activity, pane mode and prompt changes, pane movement and resizing, session
group changes and window creation, close and zoom;
- OSC 133 escape sequences now trigger events: pane-command-started,
pane-command-finished and pane-shell-prompt.
* Add switch-mode, a fast switcher bound by default to Tab for windows and BTab
(S-Tab) for sessions.
* Pane scrollbars now support auto-hide. This replaces the previous modal
behaviour where the pane was resized. With auto-hide, the scrollbar appears
while scrolling or while hovered and disappears after pane-scrollbars-timeout
(Michael Grant).
* Change command templates so %% is escaped for single quotes while %1 remains
unquoted, preventing single quotes in session or target names from
terminating quoted command sections (reported by Aliz Hammond).
* display-panes is now a mode rather than an overlay, and can be run inside
another pane. The -b flag has been removed.
* Add new-window -E, respawn-pane -E and respawn-window -E as more convenient
methods to create an empty pane (rather than using '' for the command).
* Menus now belong to the window, so appear on all clients.
* Dragging over an existing copy mode selection now adjusts it (Michael Grant).
* Rectangle selection in copy mode may extend past the end of the current line
to match vi's virtualedit=block behaviour (Mark Kelly, issue 5227).
* With mode-keys set to vi, scrolling in copy mode now keeps the cursor in the
same position relative to the text (Arseniy Simonov, issue 5216).
* Copy mode no longer exits at the bottom while a selection is in progress
(issue 5349).
* Fix scrollbar initial state so scrollbars appear on new windows (issue 5339).
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
nolink) (issues 4842 and 4280 from Moritz Angermann).
* New format modifiers: O: loops over options, V: loops over environments,
the I modifier reports client terminal information, t/d gives a time
difference in seconds, c/f and c/b emit colours, and the m operator supports
multiple terms and fuzzy matching.
* Add or improve format variables including client_colours,
pane_start_command_list, window_manual_width, window_manual_height,
pane_last_output_time, pane_modal_flag and window_modal_pane.
* Add additional pane sort orders, and a z sort order for choose-tree so
floating panes can be sorted by z-index.
* Add -f filters to kill-pane -a, kill-window -a and kill-session -a (issue
4782).
* Allow server ACLs to use groups as well as users (issue 4917).
* The mouse option now defaults to on.
* Fix send-keys -K so keys are inserted in the correct place in the input
queue, like keys from key bindings (issue 3476).
* Fix control mode clients hanging on exit if pty data was still queued (Ben
Maurer, issue 5356), and avoid sending notifications to clients which are
already exiting (Ben Maurer, issue 5357).
* Fix grouped sessions sometimes being left as unusable command targets while
they are being killed (Bryce Miller, issue 5180).
* Fix choose mode filtering when more than one pane exists (issue 5326), and
fix an infinite loop in customize mode when a filter does not match.
* Fix regsub when the pattern is anchored at the start (issue 5341).
* Fix redraw issues around synchronized updates, copy mode, tabs, padding,
status lines, menus, popups, wide characters and floating panes. This
includes fixes for flicker with alternate screen applications and scrollbars
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
5098).
* Do not crash looking for the next or previous session (issue 5344), or when
no client is available.
* Check time periodically in loops rather than for every item (issue 5367).
CHANGES FROM 3.7a TO 3.7b
* Fix so that the end of a synchronized update again triggers a redraw.

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-capture-pane.c,v 1.67 2026/07/02 10:34:14 nicm Exp $ */
/* $OpenBSD: cmd-capture-pane.c,v 1.68 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
@@ -134,6 +134,7 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
struct screen *s = &wp->base;
struct grid *gd = s->grid;
struct grid_line *gl;
struct osc133_data *od;
char *buf = xstrdup(""), *line;
char p[11];
u_int yy, xx, total = gd->hsize + gd->sy;
@@ -149,9 +150,20 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
snprintf(p, sizeof p, "-");
else
snprintf(p, sizeof p, "%u", yy - gd->hsize);
xasprintf(&line, "\tL %u (%s) flags=%s[%x] %u/%u\n", yy,
p, grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize);
od = &gl->osc133_data;
if (gl->flags & GRID_LINE_OSC133_FLAGS) {
xasprintf(&line, "\tL %u (%s) flags=%s[%x] "
"%u/%u osc133=%u,%u,%u,%u,%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize, od->prompt_col,
od->cmd_col, od->out_start_col,
od->out_end_col, od->exit_status);
} else {
xasprintf(&line, "\tL %u (%s) flags=%s[%x] "
"%u/%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize);
}
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
free(line);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-find.c,v 1.86 2026/07/02 08:47:25 nicm Exp $ */
/* $OpenBSD: cmd-find.c,v 1.87 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -884,7 +884,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (c->session != NULL) {
cmd_find_clear_state(fs, flags);
fs->wp = server_client_get_pane(c);
fs->wp = c->session->curw->window->active;
if (fs->wp == NULL) {
cmd_find_from_session(fs, c->session, flags);
return (0);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.72 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: cmd-join-pane.c,v 1.73 2026/07/17 15:24:30 nicm Exp $ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -186,6 +186,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
}
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
@@ -358,6 +359,7 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
@@ -397,6 +399,7 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
window_set_active_pane(w, wp, 1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-refresh-client.c,v 1.54 2026/07/05 08:24:00 nicm Exp $ */
/* $OpenBSD: cmd-refresh-client.c,v 1.55 2026/07/17 08:37:29 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -66,7 +66,6 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item);
const char *size = args_get(args, 'C');
u_int w, x, y;
struct client_window *cw;
if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) {
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
@@ -76,22 +75,16 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
}
log_debug("%s: client %s window @%u: size %ux%u", __func__,
tc->name, w, x, y);
cw = server_client_add_client_window(tc, w);
cw->sx = x;
cw->sy = y;
control_set_window_size(tc, w, x, y);
tc->flags |= CLIENT_WINDOWSIZECHANGED;
recalculate_sizes_now(1);
return (CMD_RETURN_NORMAL);
}
if (sscanf(size, "@%u:", &w) == 1) {
cw = server_client_get_client_window(tc, w);
if (cw != NULL) {
log_debug("%s: client %s window @%u: no size", __func__,
tc->name, w);
cw->sx = 0;
cw->sy = 0;
recalculate_sizes_now(1);
}
log_debug("%s: client %s window @%u: no size", __func__,
tc->name, w);
control_clear_window_size(tc, w);
recalculate_sizes_now(1);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-run-shell.c,v 1.92 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD: cmd-run-shell.c,v 1.93 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -90,8 +90,8 @@ cmd_run_shell_print(struct job *job, const char *msg)
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cdata->item != NULL && cdata->client != NULL)
wp = server_client_get_pane(cdata->client);
if (cdata->client != NULL && cdata->client->session != NULL)
wp = cdata->client->session->curw->window->active;
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-pane.c,v 1.76 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-select-pane.c,v 1.77 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -150,13 +150,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
const struct cmd_entry *entry = cmd_get_entry(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct event_payload *ep;
struct cmd_find_state fs;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct session *s = target->s;
struct window_pane *wp = target->wp, *activewp, *lastwp;
struct window_pane *wp = target->wp, *lastwp;
struct options *oo = wp->options;
char *title;
const char *style;
@@ -267,18 +266,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
activewp = server_client_get_pane(c);
else
activewp = w->active;
if (wp == activewp)
if (wp == w->active)
return (CMD_RETURN_NORMAL);
if (window_push_zoom(w, 0, args_has(args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
server_client_set_pane(c, wp);
else if (window_set_active_pane(w, wp, 1))
if (window_set_active_pane(w, wp, 1))
cmd_find_from_winlink_pane(current, wl, wp, 0);
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);

52
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.188 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -840,14 +840,19 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
return (wp);
}
/* Replace the first %% or %idx in template by s. */
/*
* Replace the first %% or any %idx in template by s. %% is intended for use in
* single quotes, so ' is escaped. %%% and %idx% are for double quotes, so a
* list of special characters is escaped. %idx is left unescaped.
*/
char *
cmd_template_replace(const char *template, const char *s, int idx)
{
char ch, *buf;
const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced, quoted;
size_t len, slen;
char ch, *buf;
const char *ptr, *cp, dquote[] = "\"\\$;~";
int replaced;
size_t len, slen;
enum { NQ, SQ, DQ } quote;
if (strchr(template, '%') == NULL)
return (xstrdup(template));
@@ -861,23 +866,39 @@ cmd_template_replace(const char *template, const char *s, int idx)
while (*ptr != '\0') {
switch (ch = *ptr++) {
case '%':
if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
if (*ptr >= '1' && *ptr <= '9' && *ptr - '0' == idx) {
ptr++;
quote = NQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
} else {
if (*ptr != '%' || replaced)
break;
replaced = 1;
}
ptr++;
quoted = (*ptr == '%');
if (quoted)
ptr++;
quote = SQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
}
slen = strlen(s);
if (slen >= SIZE_MAX / 3 || len > SIZE_MAX - (slen * 3) - 1)
if (slen >= SIZE_MAX / 4 ||
len > SIZE_MAX - (slen * 4) - 1)
fatalx("argument too long");
buf = xrealloc(buf, len + (slen * 3) + 1);
buf = xrealloc(buf, len + (slen * 4) + 1);
for (cp = s; *cp != '\0'; cp++) {
if (quoted && strchr(quote, *cp) != NULL)
if (quote == SQ && *cp == '\'') {
buf[len++] = '\'';
buf[len++] = '\\';
buf[len++] = '\'';
buf[len++] = '\'';
continue;
}
if (quote == DQ && strchr(dquote, *cp) != NULL)
buf[len++] = '\\';
buf[len++] = *cp;
}
@@ -891,5 +912,6 @@ cmd_template_replace(const char *template, const char *s, int idx)
buf[len] = '\0';
}
log_debug("%s: %s -> %s", __func__, template, buf);
return (buf);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: control.c,v 1.61 2026/07/10 15:45:11 nicm Exp $ */
/* $OpenBSD: control.c,v 1.62 2026/07/17 08:37:29 nicm Exp $ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -78,9 +78,20 @@ struct control_pane {
};
RB_HEAD(control_panes, control_pane);
/* Control client window size. */
struct control_window {
u_int window;
u_int sx;
u_int sy;
RB_ENTRY(control_window) entry;
};
RB_HEAD(control_windows, control_window);
/* Control client state. */
struct control_state {
struct control_panes panes;
struct control_windows windows;
TAILQ_HEAD(, control_pane) pending_list;
u_int pending_count;
@@ -120,6 +131,18 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
}
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
/* Compare control windows. */
static int
control_window_cmp(struct control_window *cw1, struct control_window *cw2)
{
if (cw1->window < cw2->window)
return (-1);
if (cw1->window > cw2->window)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_windows, control_window, entry, control_window_cmp);
/* Free a block. */
static void
control_free_block(struct control_state *cs, struct control_block *cb)
@@ -161,6 +184,66 @@ control_add_pane(struct client *c, struct window_pane *wp)
return (cp);
}
/* Get window for this client. */
static struct control_window *
control_get_window(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window cw = { .window = window };
if (cs == NULL)
return (NULL);
return (RB_FIND(control_windows, &cs->windows, &cw));
}
/* Set window size for this client. */
void
control_set_window_size(struct client *c, u_int window, u_int sx, u_int sy)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw == NULL) {
cw = xcalloc(1, sizeof *cw);
cw->window = window;
RB_INSERT(control_windows, &cs->windows, cw);
}
cw->sx = sx;
cw->sy = sy;
}
/* Get window size for this client. */
int
control_get_window_size(struct client *c, u_int window, u_int *sx, u_int *sy)
{
struct control_window *cw;
if ((cw = control_get_window(c, window)) == NULL)
return (0);
*sx = cw->sx;
*sy = cw->sy;
return (1);
}
/* Clear window size for this client. */
void
control_clear_window_size(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw != NULL) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
}
/* Discard output for a pane. */
static void
control_discard_pane(struct client *c, struct control_pane *cp)
@@ -749,6 +832,7 @@ control_start(struct client *c)
cs = c->control_state = xcalloc(1, sizeof *cs);
RB_INIT(&cs->panes);
RB_INIT(&cs->windows);
TAILQ_INIT(&cs->pending_list);
TAILQ_INIT(&cs->all_blocks);
cs->subs = monitor_create_client(c, control_sub_change, NULL);
@@ -800,6 +884,7 @@ control_stop(struct client *c)
{
struct control_state *cs = c->control_state;
struct control_block *cb, *cb1;
struct control_window *cw, *cw1;
if (cs == NULL)
return;
@@ -811,6 +896,10 @@ control_stop(struct client *c)
bufferevent_free(cs->read_event);
control_reset_offsets(c);
RB_FOREACH_SAFE(cw, control_windows, &cs->windows, cw1) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.405 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: format.c,v 1.407 2026/07/20 07:42:13 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2578,6 +2578,79 @@ format_cb_pane_tty(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_unzoomed_height. */
static void *
format_cb_pane_unzoomed_height(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
struct window *w;
struct layout_cell *lc, *root;
int status, floating;
u_int sy;
if (wp == NULL)
return (NULL);
w = wp->window;
lc = wp->saved_layout_cell;
if (lc == NULL)
lc = wp->layout_cell;
if (lc == NULL)
return (NULL);
sy = lc->g.sy;
floating = (lc->flags & LAYOUT_CELL_FLOATING);
root = w->saved_layout_root;
if (root == NULL)
root = w->layout_root;
if (lc == wp->saved_layout_cell && !floating)
status = window_get_pane_status(w);
else
status = window_pane_get_pane_status(wp);
if (!floating &&
root != NULL &&
layout_add_horizontal_border(root, lc, status) &&
sy > 1)
sy--;
return (format_printf("%u", sy));
}
/* Callback for pane_unzoomed_width. */
static void *
format_cb_pane_unzoomed_width(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
struct layout_cell *lc;
int sb_w, sb_pad;
u_int sx;
if (wp == NULL)
return (NULL);
lc = wp->saved_layout_cell;
if (lc == NULL)
lc = wp->layout_cell;
if (lc == NULL)
return (NULL);
sx = lc->g.sx;
if (window_pane_scrollbar_reserve(wp)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_w < 1)
sb_w = 1;
if (sb_pad < 0)
sb_pad = 0;
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
sx = PANE_MINIMUM;
else
sx -= sb_w + sb_pad;
}
return (format_printf("%u", sx));
}
/* Callback for pane_width. */
static void *
format_cb_pane_width(struct format_tree *ft)
@@ -3764,6 +3837,12 @@ static const struct format_table_entry format_table[] = {
{ "pane_unseen_changes", FORMAT_TABLE_STRING,
format_cb_pane_unseen_changes
},
{ "pane_unzoomed_height", FORMAT_TABLE_STRING,
format_cb_pane_unzoomed_height
},
{ "pane_unzoomed_width", FORMAT_TABLE_STRING,
format_cb_pane_unzoomed_width
},
{ "pane_width", FORMAT_TABLE_STRING,
format_cb_pane_width
},

31
grid.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: grid.c,v 1.153 2026/07/02 08:51:05 nicm Exp $ */
/* $OpenBSD: grid.c,v 1.154 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -233,6 +233,25 @@ grid_get_line(struct grid *gd, u_int line)
return (&gd->linedata[line]);
}
/* Get line time. */
time_t
grid_line_time(const struct grid_line *gl)
{
if (gl->time == 0)
return (0);
return (start_time.tv_sec + gl->time - 1);
}
/* Set line time. */
static void
grid_line_set_time(struct grid_line *gl)
{
if (current_time == 0)
gl->time = 0;
else
gl->time = current_time - start_time.tv_sec + 1;
}
/* Adjust number of lines. */
void
grid_adjust_lines(struct grid *gd, u_int lines)
@@ -486,7 +505,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time;
grid_line_set_time(&gd->linedata[gd->hsize]);
gd->hsize++;
gd->scroll_added++;
}
@@ -528,7 +547,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history);
gl_history->time = current_time;
grid_line_set_time(gl_history);
/* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@@ -1701,8 +1720,14 @@ grid_line_flags_string(int flags)
strlcat(s, "DEAD,", sizeof s);
if (flags & GRID_LINE_START_PROMPT)
strlcat(s, "START_PROMPT,", sizeof s);
if (flags & GRID_LINE_SECOND_PROMPT)
strlcat(s, "SECOND_PROMPT,", sizeof s);
if (flags & GRID_LINE_START_COMMAND)
strlcat(s, "START_COMMAND,", sizeof s);
if (flags & GRID_LINE_START_OUTPUT)
strlcat(s, "START_OUTPUT,", sizeof s);
if (flags & GRID_LINE_END_OUTPUT)
strlcat(s, "END_OUTPUT,", sizeof s);
if (flags & GRID_LINE_HYPERLINK)
strlcat(s, "HYPERLINK,", sizeof s);
if (*s == '\0')

113
input.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: input.c,v 1.268 2026/07/13 15:03:03 nicm Exp $ */
/* $OpenBSD: input.c,v 1.269 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -173,8 +173,6 @@ static void input_osc_110(struct input_ctx *, const char *);
static void input_osc_111(struct input_ctx *, const char *);
static void input_osc_112(struct input_ctx *, const char *);
static void input_osc_133(struct input_ctx *, const char *);
static void input_fire_pane_title_changed(struct window_pane *,
const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
@@ -211,21 +209,6 @@ static int input_end_bel(struct input_ctx *);
/* Command table comparison function. */
static int input_table_compare(const void *, const void *);
static void
input_fire_pane_title_changed(struct window_pane *wp, const char *title)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
}
/* Command table entry. */
struct input_table_entry {
int ch;
@@ -808,6 +791,22 @@ input_stop_utf8(struct input_ctx *ictx)
ictx->utf8started = 0;
}
/* Fire a title changed event. */
static void
input_fire_pane_title_changed(struct window_pane *wp, const char *title)
{
struct event_payload *ep;
struct cmd_find_state fs;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
}
/*
* Timer - if this expires then have been waiting for a terminator for too
* long, so reset to ground.
@@ -3190,6 +3189,35 @@ input_osc_112(struct input_ctx *ictx, const char *p)
screen_set_cursor_colour(ictx->ctx.s, -1);
}
/* Parse the OSC 133 D exit status. */
static int
input_osc_133_exit_status(const char *p)
{
const char *end;
char *copy;
const char *errstr;
long long status;
if (p[1] != ';' || p[2] == '\0' || strchr(p + 2, '=') == p + 2)
return (0);
end = strchr(p + 2, ';');
if (end == p + 2)
return (0);
if (end == NULL)
copy = xstrdup(p + 2);
else
copy = xstrndup(p + 2, end - (p + 2));
if (strchr(copy, '=') != NULL) {
free(copy);
return (0);
}
status = strtonum(copy, 0, 255, &errstr);
free(copy);
if (errstr != NULL)
return (255);
return (status);
}
/* Fire an OSC 133 command event. */
static void
input_fire_command_event(struct window_pane *wp, const char *name)
@@ -3235,27 +3263,51 @@ static void
input_osc_133(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid *gd = ictx->ctx.s->grid;
u_int line = ictx->ctx.s->cy + gd->hsize;
struct screen *s = ictx->ctx.s;
struct grid *gd = s->grid;
u_int line = s->cy + gd->hsize;
struct grid_line *gl = NULL;
const char *errstr;
const char *cp;
int status;
if (line <= gd->hsize + gd->sy - 1)
if (line < gd->hsize + gd->sy)
gl = grid_get_line(gd, line);
switch (*p) {
case 'A':
if (gl != NULL)
case 'N':
if (gl != NULL) {
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
gl->osc133_data.prompt_col = s->cx;
gl->flags |= GRID_LINE_START_PROMPT;
}
if (wp != NULL) {
wp->last_prompt_time = time(NULL);
events_fire_pane("pane-shell-prompt", wp);
}
break;
case 'P':
if (gl != NULL) {
cp = strstr(p, ";k=s");
if (cp != NULL && (cp[4] == ';' || cp[4] == '\0'))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
gl->osc133_data.prompt_col = s->cx;
}
break;
case 'B':
case 'I':
if (gl != NULL) {
gl->flags |= GRID_LINE_START_COMMAND;
gl->osc133_data.cmd_col = s->cx;
}
break;
case 'C':
if (gl != NULL)
if (gl != NULL) {
gl->flags |= GRID_LINE_START_OUTPUT;
gl->osc133_data.out_start_col = s->cx;
}
if (wp != NULL) {
wp->cmd_start_time = time(NULL);
wp->cmd_end_time = 0;
@@ -3265,17 +3317,18 @@ input_osc_133(struct input_ctx *ictx, const char *p)
}
break;
case 'D':
status = input_osc_133_exit_status(p);
if (wp != NULL) {
wp->cmd_end_time = time(NULL);
wp->flags &= ~PANE_CMDRUNNING;
wp->cmd_status = -1;
if (p[1] == ';' && p[2] != '\0') {
status = strtonum(p + 2, 0, INT_MAX, &errstr);
if (errstr == NULL)
wp->cmd_status = status;
}
wp->cmd_status = status;
input_fire_command_event(wp, "pane-command-finished");
}
if (gl != NULL) {
gl->flags |= GRID_LINE_END_OUTPUT;
gl->osc133_data.out_end_col = s->cx;
gl->osc133_data.exit_status = status;
}
break;
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.238 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.239 2026/07/19 17:36:38 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1348,7 +1348,8 @@ const struct options_table_entry options_table[] = {
{ .name = "display-panes-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#[align=right]#{pane_width}x#{pane_height}",
.default_str = "#[align=right]"
"#{pane_unzoomed_width}x#{pane_unzoomed_height}",
.text = "Format of text shown by 'display-panes', expanded for each "
"pane."
},

41
regress/cfg-client-lost.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMPDIR=$(mktemp -d) || exit 1
TMUX="$TEST_TMUX -S$TMPDIR/tmux.sock"
CONF="$TMPDIR/tmux.conf"
READY="$TMPDIR/ready"
cleanup()
{
$TMUX kill-server 2>/dev/null
rm -rf "$TMPDIR"
}
trap cleanup 0 1 15
cat <<EOF >$CONF
new-session -d -s keep 'sleep 1000'
run-shell -b 'touch $READY'
run-shell -d 1 true
source-file /dev/null
EOF
$TMUX -f$CONF new-session -d -s client 'sleep 100' &
pid=$!
i=0
while [ ! -f "$READY" ]; do
[ "$i" -eq 50 ] && exit 1
i=$((i + 1))
sleep 0.1
done
kill "$pid" 2>/dev/null
wait "$pid" 2>/dev/null
sleep 2
$TMUX has-session -t keep || exit 1
exit 0

View File

@@ -0,0 +1,181 @@
#!/bin/sh
# Exercise cmd_template_replace through command-prompt, which passes prompt
# input as argv to args_make_commands. This covers the quoting modes, indexed
# replacements, and cases where replacements are intentionally not made.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
LANG=C.UTF-8
export TERM LC_ALL LANG
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMP=$(mktemp -d) || exit 1
TMUX_TMPDIR="$TMP"
export TMUX_TMPDIR
OUT="$TEST_TMUX -LtestA$$ -f/dev/null"
IN="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{
$OUT kill-server 2>/dev/null
$IN kill-server 2>/dev/null
rm -rf "$TMP"
}
trap cleanup EXIT
fail()
{
echo "[FAIL] $1"
exit 1
}
settle()
{
sleep 0.5
}
wait_option()
{
option=$1
expected=$2
i=0
while [ "$i" -lt 30 ]; do
value=$($IN show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
accept_prompt()
{
key=$1
value=$2
$OUT send-keys "$key" || exit 1
settle
$OUT send-keys -l "$value" || exit 1
$OUT send-keys Enter || exit 1
settle
}
accept_two_prompts()
{
key=$1
first=$2
second=$3
$OUT send-keys "$key" || exit 1
settle
$OUT send-keys -l "$first" || exit 1
$OUT send-keys Enter || exit 1
settle
$OUT send-keys -l "$second" || exit 1
$OUT send-keys Enter || exit 1
settle
}
reset_options()
{
for option in \
@r @first @second @plain @raw_tail @one @two @two_again \
@double_index @marker; do
$IN set -g "$option" SENTINEL || exit 1
done
$IN set -g @marker unchanged || exit 1
}
$IN new -d -x80 -y24 "sh -c 'exec sleep 1000'" || exit 1
$IN set -g status on || exit 1
$IN set -g status-keys emacs || exit 1
$IN set -g window-size manual || exit 1
$IN bind -n M-s command-prompt -p '(single)' "set -g @r '%%'" ||
exit 1
$IN bind -n M-f command-prompt -p '(first)' \
"set -g @first '%%' ; set -g @second '%%'" ||
exit 1
$IN bind -n M-d command-prompt -p '(double)' 'set -g @r "%%%"' ||
exit 1
$IN bind -n M-r command-prompt -p '(raw)' "set -g @r %1" ||
exit 1
$IN bind -n M-a command-prompt -p '(all)' \
"set -g @first %1 ; set -g @second %1" ||
exit 1
$IN bind -n M-m command-prompt -p 'one,two' \
"set -g @one %1 ; set -g @two %2 ; set -g @two_again %2" ||
exit 1
$IN bind -n M-i command-prompt -p 'one,two' 'set -g @double_index "%2%"' ||
exit 1
$IN bind -n M-n command-prompt -p '(none)' \
"set -g @plain no-template-markers" ||
exit 1
$OUT new -d -x80 -y24 || exit 1
$OUT set -g status off || exit 1
$OUT set -g window-size manual || exit 1
$OUT send-keys -l "$IN attach" || exit 1
$OUT send-keys Enter || exit 1
sleep 1
reset_options
# %% is for templates already inside single quotes. A single quote in the
# replacement must stay data and must not close the surrounding quotes.
payload="can't ; set -g @marker changed ; done"
accept_prompt M-s "$payload"
wait_option @r "$payload"
wait_option @marker unchanged
# Only the first %% is replaced.
reset_options
payload="first'value"
accept_prompt M-f "$payload"
wait_option @first "$payload"
wait_option @second %%
# %%% keeps the previous double-quote escaping behaviour.
reset_options
payload='a"$;~\z'
accept_prompt M-d "$payload"
wait_option @r "$payload"
# %1 is intentionally left raw as an escape hatch.
reset_options
$IN set -g @raw_tail unchanged || exit 1
accept_prompt M-r 'raw ; set -g @raw_tail yes'
wait_option @r raw
wait_option @raw_tail yes
# All instances of %1 are replaced.
reset_options
accept_prompt M-a repeat
wait_option @first repeat
wait_option @second repeat
# %1 and %2 select different prompt values, and all instances of %2 are
# replaced.
reset_options
accept_two_prompts M-m one two
wait_option @one one
wait_option @two two
wait_option @two_again two
# %idx% uses double-quote escaping for indexed replacements.
reset_options
payload='b"$;~\y'
accept_two_prompts M-i ignored "$payload"
wait_option @double_index "$payload"
# A template without replacement markers is left alone.
reset_options
accept_prompt M-n 'unused ; set -g @marker changed'
wait_option @plain no-template-markers
wait_option @marker unchanged
exit 0

View File

@@ -1,8 +1,9 @@
#!/bin/sh
# 947
# size in control mode should change after refresh-client -C, and -x and -y
# should work without -d for control clients
# size in control mode should change after refresh-client -C, per-window
# control client sizes should work, and -x and -y should work without -d for
# control clients
PATH=/bin:/usr/bin
TERM=screen
@@ -47,4 +48,21 @@ $TMUX ls -F':#{window_width} #{window_height}' >>$OUT
printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
TMUX="$TEST_TMUX -LtestA$$-4 -f/dev/null"
$TMUX -f/dev/null new -d || exit 1
$TMUX neww || exit 1
w0=$($TMUX display -p -t :0 '#{window_id}')
w1=$($TMUX display -p -t :1 '#{window_id}')
cat <<EOF|$TMUX -f/dev/null -C a >$TMP
refresh -C 80,24
refresh -C ${w0}:40x10
display -p -t ${w0} 'w0 #{window_width} #{window_height}'
display -p -t ${w1} 'w1 #{window_width} #{window_height}'
refresh -C ${w0}:
display -p -t ${w0} 'w0c #{window_width} #{window_height}'
EOF
grep ^w $TMP >$OUT
printf "w0 40 10\nw1 80 24\nw0c 80 24\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -73,11 +73,14 @@ $TMUX new-session -d -s m -x 80 -y 24 'cat' || exit 1
$TMUX split-window -t m:0 'cat' || exit 1
p0=$($TMUX display-message -p -t m:0.0 '#{pane_id}')
p1=$($TMUX display-message -p -t m:0.1 '#{pane_id}')
$TMUX set -g display-panes-format 'P#{pane_index}' ||
$TMUX set -g display-panes-format \
'P#{pane_index}:#{pane_unzoomed_width}x#{pane_unzoomed_height}' ||
fail "set display-panes-format failed"
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t m" || exit 1
wait_clients 1
p0_size=$($TMUX display-message -p -t "$p0" '#{pane_width}x#{pane_height}')
p1_size=$($TMUX display-message -p -t "$p1" '#{pane_width}x#{pane_height}')
capture()
{
@@ -100,11 +103,38 @@ wait_capture()
$TMUX display-panes -d 0 -t "$p0" || fail "display-panes failed"
wait_format "$p0" '#{pane_mode}' 'panes-mode'
wait_format "$p0" '#{window_zoomed_flag}' '1'
wait_capture 'P0'
wait_capture 'P1'
wait_format "$p0" '#{pane_unzoomed_width}x#{pane_unzoomed_height}' \
"$p0_size"
wait_capture "P0:$p0_size"
wait_capture "P1:$p1_size"
$TMUX send-keys -t "$p0" q || fail "exit panes mode failed"
wait_format "$p0" '#{pane_in_mode}' '0'
wait_format "$p0" '#{window_zoomed_flag}' '0'
$TMUX set -g display-panes-format 'P#{pane_index}' ||
fail "reset display-panes-format failed"
# Unzoomed sizes match the visible pane size when scrollbars reserve space.
$TMUX set -w -t m:0 pane-scrollbars on ||
fail "set pane-scrollbars failed"
$TMUX set -w -t m:0 pane-scrollbars-style "width=2,pad=1" ||
fail "set pane-scrollbars-style failed"
p0_size=$($TMUX display-message -p -t "$p0" '#{pane_width}x#{pane_height}')
p1_size=$($TMUX display-message -p -t "$p1" '#{pane_width}x#{pane_height}')
$TMUX set -g display-panes-format \
'S#{pane_index}:#{pane_unzoomed_width}x#{pane_unzoomed_height}' ||
fail "set scrollbar display-panes-format failed"
$TMUX display-panes -d 0 -t "$p0" || fail "display-panes scrollbar failed"
wait_format "$p0" '#{window_zoomed_flag}' '1'
wait_format "$p0" '#{pane_unzoomed_width}x#{pane_unzoomed_height}' \
"$p0_size"
wait_capture "S0:$p0_size"
wait_capture "S1:$p1_size"
$TMUX send-keys -t "$p0" q || fail "exit scrollbar panes mode failed"
wait_format "$p0" '#{pane_in_mode}' '0'
$TMUX set -w -t m:0 pane-scrollbars off ||
fail "reset pane-scrollbars failed"
$TMUX set -g display-panes-format 'P#{pane_index}' ||
fail "reset display-panes-format after scrollbars failed"
# -Z starts unzoomed.
$TMUX display-panes -Zd 0 -t "$p0" || fail "display-panes -Z failed"
@@ -192,13 +222,21 @@ $TMUX set -g display-panes-format 'P#{pane_index}' ||
# pane content into the status row.
$TMUX set -w -t m:0 pane-border-status top ||
fail "set pane-border-status failed"
$TMUX set -g display-panes-format '' || fail "clear display-panes-format failed"
status_p0_size=$($TMUX display-message -p -t "$p0" \
'#{pane_width}x#{pane_height}')
status_p1_size=$($TMUX display-message -p -t "$p1" \
'#{pane_width}x#{pane_height}')
$TMUX set -g display-panes-format \
'#[align=right]T#{pane_index}:#{pane_unzoomed_width}x#{pane_unzoomed_height}' ||
fail "set status display-panes-format failed"
$TMUX respawn-pane -k -t "$p0" 'printf "STATUS-TOP\n"; exec cat' ||
fail "write status marker failed"
wait_capture 'STATUS-TOP'
$TMUX display-panes -d 0 -t "$p0" || fail "display-panes status failed"
wait_format "$p0" '#{pane_mode}' 'panes-mode'
wait_capture 'STATUS-TOP'
wait_capture "T0:$status_p0_size"
wait_capture "T1:$status_p1_size"
CAPTURED=$(capture)
first=$(printf '%s\n' "$CAPTURED" | sed -n '1p')
second=$(printf '%s\n' "$CAPTURED" | sed -n '2p')
@@ -230,13 +268,18 @@ wait_format "$fp" '#{pane_mode}' 'panes-mode'
wait_format "$fp" '#{window_zoomed_flag}' '1'
wait_capture '┌'
wait_capture '┘'
fp_size=$($TMUX display-message -p -t "$fp" \
'#{pane_unzoomed_width}x#{pane_unzoomed_height}')
[ "$fp_size" = "30x8" ] ||
fail "expected floating unzoomed size 30x8, got $fp_size"
$TMUX send-keys -t "$fp" q || fail "exit zoomed floating panes mode failed"
wait_format "$fp" '#{pane_in_mode}' '0'
wait_format "$fp" '#{window_zoomed_flag}' '0'
$TMUX resize-pane -t "$fp" -x 48 -y 14 ||
fail "resize floating pane larger failed"
$TMUX set -g display-panes-format '#[align=right]FP#{pane_width}x#{pane_height}' ||
$TMUX set -g display-panes-format \
'#[align=right]FP#{pane_unzoomed_width}x#{pane_unzoomed_height}' ||
fail "set floating display-panes-format failed"
$TMUX display-panes -Zd 0 -t "$fp" || fail "display-panes floating format failed"
wait_format "$fp" '#{pane_mode}' 'panes-mode'

View File

@@ -5,11 +5,20 @@
start_pane alternate 10 3 'MAIN\033[?1049hALT\033[?1049lZ\n'
check_capture alternate 'MAINZ'
start_pane osc133 10 4 '\033]133;A\007prompt\n\033]133;C\007output\n'
check_capture osc133 'prompt
output'
check_flags osc133 'P prompt
O output'
start_pane osc133 20 8 'xx\033]133;A\007p>\033]133;B\007cmd\nxy\033]133;P;k=s\007more\nzz\033]133;C\007out\033]133;D;7\007\nq\033]133;C\007bad\033]133;D;-1\007\nqq\033]133;C\007big\033]133;D;300\007\nzzz\033]133;C\007ok\033]133;D\007\n'
check_capture osc133 'xxp>cmd
xymore
zzout
qbad
qqbig
zzzok'
check_raw_matches osc133 \
'L 0 \(0\) flags=START_PROMPT,START_COMMAND\[[0-9a-f]+\].* osc133=2,4,0,0,0' \
'L 1 \(1\) flags=SECOND_PROMPT\[[0-9a-f]+\].* osc133=2,0,0,0,0' \
'L 2 \(2\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,7' \
'L 3 \(3\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,1,4,255' \
'L 4 \(4\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
$TMUX kill-server 2>/dev/null
exit $exit_status

View File

@@ -21,7 +21,7 @@ exit_status=0
format_string () {
case $1 in
*\')
printf '"%%%%"'
printf '"%%%%%%"'
;;
*)
printf "'%%%%'"

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: resize.c,v 1.57 2026/07/13 13:01:14 nicm Exp $ */
/* $OpenBSD: resize.c,v 1.58 2026/07/17 08:37:29 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -134,9 +134,8 @@ clients_calculate_size(int type, int current, struct client *c,
int, int, struct session *, struct window *), u_int *sx, u_int *sy,
u_int *xpixel, u_int *ypixel)
{
struct client *loop;
struct client_window *cw;
u_int cx, cy, n = 0;
struct client *loop;
u_int cx, cy, n = 0;
/*
* Start comparing with 0 for largest and UINT_MAX for smallest or
@@ -187,20 +186,10 @@ clients_calculate_size(int type, int current, struct client *c,
continue;
}
/*
* If the client has a per-window size, use this instead if it is
* smaller.
*/
if (w != NULL)
cw = server_client_get_client_window(loop, w->id);
else
cw = NULL;
/* Work out this client's size. */
if (cw != NULL && cw->sx != 0 && cw->sy != 0) {
cx = cw->sx;
cy = cw->sy;
} else {
if (w == NULL ||
!control_get_window_size(loop, w->id, &cx, &cy) ||
cx == 0 || cy == 0) {
cx = loop->tty.sx;
cy = loop->tty.sy - status_line_size(loop);
}
@@ -247,17 +236,16 @@ skip:
/* Look up per-window size if any. */
if (~loop->flags & CLIENT_WINDOWSIZECHANGED)
continue;
cw = server_client_get_client_window(loop, w->id);
if (cw == NULL)
if (!control_get_window_size(loop, w->id, &cx, &cy))
continue;
/* Clamp the size. */
log_debug("%s: %s size for @%u is %ux%u", __func__,
loop->name, w->id, cw->sx, cw->sy);
if (cw->sx != 0 && *sx > cw->sx)
*sx = cw->sx;
if (cw->sy != 0 && *sy > cw->sy)
*sy = cw->sy;
loop->name, w->id, cx, cy);
if (cx != 0 && *sx > cx)
*sx = cx;
if (cy != 0 && *sy > cy)
*sy = cy;
}
}
if (*sx != UINT_MAX && *sy != UINT_MAX)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.152 2026/07/16 10:52:32 nicm Exp $ */
/* $OpenBSD: screen-redraw.c,v 1.153 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1582,7 +1582,7 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
if (server_is_marked(s, s->curw, marked_pane.wp))
dctx->marked = marked_pane.wp;
dctx->active = server_client_get_pane(c);
dctx->active = s->curw->window->active;
lines = status_line_size(c);
if (options_get_number(oo, "status-position") == 0)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-write.c,v 1.283 2026/07/09 07:35:05 nicm Exp $ */
/* $OpenBSD: screen-write.c,v 1.284 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1601,6 +1601,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
struct grid_line *gl;
u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item;
struct osc133_data od;
u_int flags;
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
@@ -1612,10 +1613,12 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
ctx->wp->flags |= PANE_REDRAW;
#endif
flags = gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_START_OUTPUT);
flags = gl->flags & GRID_LINE_OSC133_FLAGS;
memcpy(&od, &gl->osc133_data, sizeof od);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
gl->flags |= flags;
memcpy(&gl->osc133_data, &od, sizeof gl->osc133_data);
screen_write_collect_clear(ctx, s->cy, 1);
ci->x = 0;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.494 2026/07/15 14:14:50 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.497 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -50,19 +50,6 @@ static int server_client_dispatch_identify(struct client *, struct imsg *);
static int server_client_dispatch_shell(struct client *);
static void server_client_report_theme(struct client *, enum client_theme);
/* Compare client windows. */
static int
server_client_window_cmp(struct client_window *cw1,
struct client_window *cw2)
{
if (cw1->window < cw2->window)
return (-1);
if (cw1->window > cw2->window)
return (1);
return (0);
}
RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp);
/* Number of attached clients. */
u_int
server_client_how_many(void)
@@ -188,7 +175,6 @@ server_client_create(int fd)
c->out_fd = -1;
c->queue = cmdq_new();
RB_INIT(&c->windows);
RB_INIT(&c->files);
c->tty.sx = 80;
@@ -379,8 +365,9 @@ void
server_client_lost(struct client *c)
{
struct client_file *cf, *cf1;
struct client_window *cw, *cw1;
if (cfg_client == c)
cfg_client = NULL;
c->flags |= CLIENT_DEAD;
status_prompt_clear(c);
@@ -390,10 +377,6 @@ server_client_lost(struct client *c)
cf->error = EINTR;
file_fire_done(cf);
}
RB_FOREACH_SAFE(cw, client_windows, &c->windows, cw1) {
RB_REMOVE(client_windows, &c->windows, cw);
free(cw);
}
TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c);
@@ -1985,7 +1968,7 @@ server_client_reset_state(struct client *c)
{
struct tty *tty = &c->tty;
struct window *w = c->session->curw->window;
struct window_pane *wp = server_client_get_pane(c), *loop;
struct window_pane *wp = w->active, *loop;
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0;
@@ -2880,8 +2863,6 @@ server_client_set_flags(struct client *c, const char *flags)
flag = CLIENT_READONLY;
else if (strcmp(next, "ignore-size") == 0)
flag = CLIENT_IGNORESIZE;
else if (strcmp(next, "active-pane") == 0)
flag = CLIENT_ACTIVEPANE;
else if (strcmp(next, "no-detach-on-destroy") == 0)
flag = CLIENT_NO_DETACH_ON_DESTROY;
if (flag == 0)
@@ -2930,8 +2911,6 @@ server_client_get_flags(struct client *c)
}
if (c->flags & CLIENT_READONLY)
strlcat(s, "read-only,", sizeof s);
if (c->flags & CLIENT_ACTIVEPANE)
strlcat(s, "active-pane,", sizeof s);
if (c->flags & CLIENT_SUSPENDED)
strlcat(s, "suspended,", sizeof s);
if (c->flags & CLIENT_UTF8)
@@ -2941,83 +2920,13 @@ server_client_get_flags(struct client *c)
return (s);
}
/* Get client window. */
struct client_window *
server_client_get_client_window(struct client *c, u_int id)
{
struct client_window cw = { .window = id };
return (RB_FIND(client_windows, &c->windows, &cw));
}
/* Add client window. */
struct client_window *
server_client_add_client_window(struct client *c, u_int id)
{
struct client_window *cw;
cw = server_client_get_client_window(c, id);
if (cw == NULL) {
cw = xcalloc(1, sizeof *cw);
cw->window = id;
RB_INSERT(client_windows, &c->windows, cw);
}
return (cw);
}
/* Get client active pane. */
struct window_pane *
server_client_get_pane(struct client *c)
{
struct session *s = c->session;
struct window *w;
struct client_window *cw;
if (s == NULL)
return (NULL);
w = s->curw->window;
if (w->modal != NULL)
return (w->modal);
if (~c->flags & CLIENT_ACTIVEPANE)
return (w->active);
cw = server_client_get_client_window(c, w->id);
if (cw == NULL)
return (w->active);
return (cw->pane);
}
/* Set client active pane. */
void
server_client_set_pane(struct client *c, struct window_pane *wp)
{
struct session *s = c->session;
struct client_window *cw;
if (s == NULL)
return;
if (wp->window->modal != NULL && wp != wp->window->modal)
return;
cw = server_client_add_client_window(c, s->curw->window->id);
cw->pane = wp;
log_debug("%s pane now %%%u", c->name, wp->id);
}
/* Remove pane from client lists. */
/* Remove pane from client state. */
void
server_client_remove_pane(struct window_pane *wp)
{
struct client *c;
struct window *w = wp->window;
struct client_window *cw;
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
cw = server_client_get_client_window(c, w->id);
if (cw != NULL && cw->pane == wp) {
RB_REMOVE(client_windows, &c->windows, cw);
free(cw);
}
if (c->tty.mouse_last_pane == (int)wp->id) {
c->tty.mouse_last_pane = -1;
c->tty.mouse_drag_update = NULL;
@@ -3070,7 +2979,7 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb)
goto out;
}
wp = server_client_get_pane(c);
wp = c->session->curw->window->active;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL,

17
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1142 2026/07/15 13:02:33 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1144 2026/07/19 17:36:38 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: July 15 2026 $
.Dd $Mdocdate: July 19 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -1112,8 +1112,6 @@ detaching the client, typically causing it to exit.
sets a comma-separated list of client flags.
The flags are:
.Bl -tag -width Ds
.It active\-pane
the client has an independent active pane
.It ignore\-size
the client does not affect the size of other clients
.It no\-detach\-on\-destroy
@@ -1143,13 +1141,6 @@ When a client is read-only, only keys bound to the
or
.Ic switch\-client
commands have any effect.
A client with the
.Ar active\-pane
flag allows the active pane to be selected independently of the window's active
pane used by clients without the flag.
This only affects the cursor position and commands issued from the client;
other features such as hooks and styles continue to use the window's active
pane.
.Pp
If no server is started,
.Ic attach\-session
@@ -6573,7 +6564,7 @@ shows the subscriptions installed with
.Sh MOUSE SUPPORT
If the
.Ic mouse
option is on (the default is off),
option is on,
.Nm
allows mouse events to be bound as keys.
The name of each key is made up of a mouse event (such as
@@ -7308,6 +7299,8 @@ The following variables are available, where appropriate:
.It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode"
.It Li "pane_unzoomed_height" Ta "" Ta "Height of pane when not zoomed"
.It Li "pane_unzoomed_width" Ta "" Ta "Width of pane when not zoomed"
.It Li "pane_width" Ta "" Ta "Width of pane"
.It Li "pane_x" Ta "" Ta "X position of pane"
.It Li "pane_y" Ta "" Ta "Y position of pane"

4
tmux.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.c,v 1.221 2026/06/29 18:17:28 nicm Exp $ */
/* $OpenBSD: tmux.c,v 1.222 2026/07/19 19:09:30 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -375,7 +375,7 @@ find_home(void)
if (home == NULL || *home == '\0') {
pw = getpwuid(getuid());
if (pw != NULL)
home = pw->pw_dir;
home = xstrdup(pw->pw_dir);
else
home = NULL;
}

62
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1406 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1411 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -809,8 +809,19 @@ struct colour_palette {
#define GRID_LINE_EXTENDED 0x2
#define GRID_LINE_DEAD 0x4
#define GRID_LINE_START_PROMPT 0x8
#define GRID_LINE_START_OUTPUT 0x10
#define GRID_LINE_HYPERLINK 0x20
#define GRID_LINE_SECOND_PROMPT 0x10
#define GRID_LINE_START_COMMAND 0x20
#define GRID_LINE_START_OUTPUT 0x40
#define GRID_LINE_END_OUTPUT 0x80
#define GRID_LINE_HYPERLINK 0x100
/* All OSC 133 flags. */
#define GRID_LINE_OSC133_FLAGS \
(GRID_LINE_START_PROMPT| \
GRID_LINE_SECOND_PROMPT| \
GRID_LINE_START_COMMAND| \
GRID_LINE_START_OUTPUT| \
GRID_LINE_END_OUTPUT)
/* Grid string flags. */
#define GRID_STRING_WITH_SEQUENCES 0x1
@@ -879,17 +890,27 @@ struct grid_cell_entry {
u_char flags;
} __packed;
/* OSC 133 data for a grid line. */
struct osc133_data {
u_short prompt_col;
u_short cmd_col;
u_short out_start_col;
u_short out_end_col;
u_char exit_status;
};
/* Grid line. */
struct grid_line {
struct grid_cell_entry *celldata;
u_int cellused;
u_int cellsize;
struct grid_extd_entry *extddata;
u_short cellused;
u_short cellsize;
u_int extdsize;
int flags;
time_t time;
u_int time;
struct osc133_data osc133_data;
u_short flags;
};
/* Entire grid of cells. */
@@ -2073,18 +2094,6 @@ struct client_file {
};
RB_HEAD(client_files, client_file);
/* Client window. */
struct client_window {
u_int window;
struct window_pane *pane;
u_int sx;
u_int sy;
RB_ENTRY(client_window) entry;
};
RB_HEAD(client_windows, client_window);
/* Maximum time to be pasting. */
#define CLIENT_PASTE_TIME_LIMIT 5
@@ -2178,8 +2187,6 @@ struct client {
const char *user;
struct cmdq_list *queue;
struct client_windows windows;
struct control_state *control_state;
u_int pause_age;
@@ -2260,7 +2267,7 @@ struct client {
#define CLIENT_STARTSERVER 0x10000000
#define CLIENT_REDRAWMENU 0x20000000
#define CLIENT_NOFORK 0x40000000
#define CLIENT_ACTIVEPANE 0x80000000ULL
/* 0x80000000ULL unused */
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
@@ -3259,7 +3266,6 @@ void printflike(1, 2) server_add_message(const char *, ...);
int server_create_socket(uint64_t, char **);
/* server-client.c */
RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp);
u_int server_client_how_many(void);
void server_client_ensure_ranges(struct visible_ranges *, u_int);
int server_client_ranges_is_empty(struct visible_ranges *);
@@ -3281,10 +3287,6 @@ void server_client_loop(void);
const char *server_client_get_cwd(struct client *, struct session *);
void server_client_set_flags(struct client *, const char *);
const char *server_client_get_flags(struct client *);
struct client_window *server_client_get_client_window(struct client *, u_int);
struct client_window *server_client_add_client_window(struct client *, u_int);
struct window_pane *server_client_get_pane(struct client *);
void server_client_set_pane(struct client *, struct window_pane *);
void server_client_remove_pane(struct window_pane *);
void server_client_print(struct client *, int, struct evbuffer *);
@@ -3441,6 +3443,7 @@ int grid_compare(struct grid *, struct grid *);
const char *grid_line_flags_string(int);
const char *grid_cell_flags_string(int);
const char *grid_cell_attr_string(int);
time_t grid_line_time(const struct grid_line *);
void grid_collect_history(struct grid *, int);
void grid_remove_history(struct grid *, u_int );
void grid_scroll_history(struct grid *, u_int);
@@ -3980,6 +3983,9 @@ void control_set_pane_on(struct client *, struct window_pane *);
void control_set_pane_off(struct client *, struct window_pane *);
void control_continue_pane(struct client *, struct window_pane *);
void control_pause_pane(struct client *, struct window_pane *);
void control_set_window_size(struct client *, u_int, u_int, u_int);
int control_get_window_size(struct client *, u_int, u_int *, u_int *);
void control_clear_window_size(struct client *, u_int);
struct window_pane_offset *control_pane_offset(struct client *,
struct window_pane *, int *);
void control_reset_offsets(struct client *);

4
tty.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tty.c,v 1.476 2026/06/26 11:36:22 nicm Exp $ */
/* $OpenBSD: tty.c,v 1.477 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -959,7 +959,7 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
{
struct client *c = tty->client;
struct window *w = c->session->curw->window;
struct window_pane *wp = server_client_get_pane(c);
struct window_pane *wp = w->active;
u_int cx, cy, lines;
lines = status_line_size(c);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-border.c,v 1.1 2026/06/22 08:47:46 nicm Exp $ */
/* $OpenBSD: window-border.c,v 1.2 2026/07/17 12:42:51 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -93,7 +93,7 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c,
struct grid_cell *saved;
int *flag;
if (wp == server_client_get_pane(c)) {
if (wp == c->session->curw->window->active) {
flag = &wp->active_border_gc_set;
saved = &wp->active_border_gc;
option = "pane-active-border-style";

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-copy.c,v 1.421 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: window-copy.c,v 1.422 2026/07/20 11:16:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1107,9 +1107,11 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
u_int hsize = screen_hsize(data->backing);
u_int position, limit;
struct grid_line *gl;
time_t t;
gl = grid_get_line(data->backing->grid, hsize - data->oy);
format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time);
t = grid_line_time(gl);
format_add(ft, "top_line_time", "%llu", (unsigned long long)t);
format_add(ft, "scroll_position", "%d", data->oy);
if (window_copy_line_number_is_absolute(wme)) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-panes.c,v 1.1 2026/07/14 17:17:18 nicm Exp $ */
/* $OpenBSD: window-panes.c,v 1.3 2026/07/19 19:53:11 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -57,6 +57,7 @@ struct window_panes_modedata {
u_int source_session;
u_int source_window;
struct screen screen;
struct screen *preview;
struct event timer;
@@ -103,6 +104,24 @@ window_panes_get_source(struct window_panes_modedata *data, struct session **sp,
return (1);
}
static void
window_panes_set_preview(struct window_panes_modedata *data)
{
struct window_pane *wp = data->wp;
struct screen *src = &wp->base, *dst;
struct screen_write_ctx ctx;
u_int sx = screen_size_x(src), sy = screen_size_y(src);
data->preview = dst = xmalloc(sizeof *data->preview);
screen_init(dst, sx, sy, 0);
screen_write_start(&ctx, dst);
screen_write_fast_copy(&ctx, src, 0, src->grid->hsize, sx, sy);
screen_write_stop(&ctx);
dst->mode = src->mode;
dst->cx = src->cx;
dst->cy = src->cy;
}
static void
window_panes_free_areas(struct window_panes_modedata *data)
{
@@ -379,7 +398,7 @@ window_panes_mark_pane_status_borders(u_char *map, struct window *w,
}
static int
window_panes_get_floating_frame(struct window_pane *wp, u_int osx, u_int osy,
window_panes_get_floating_borders(struct window_pane *wp, u_int osx, u_int osy,
u_int dsx, u_int dsy, int *xp, int *yp, int *x2p, int *y2p)
{
struct layout_cell *lc;
@@ -404,12 +423,12 @@ window_panes_get_floating_frame(struct window_pane *wp, u_int osx, u_int osy,
}
static int
window_panes_clip_floating_body(struct window_pane *wp, u_int osx, u_int osy,
window_panes_clip_floating_pane(struct window_pane *wp, u_int osx, u_int osy,
u_int dsx, u_int dsy, u_int *xp, u_int *yp, u_int *sxp, u_int *syp)
{
int x, y, x2, y2, bx, by, bx2, by2;
if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y,
if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
&x2, &y2))
return (1);
@@ -601,7 +620,7 @@ window_panes_draw_floating_border(struct screen_write_ctx *ctx,
if (dsx == 0 || dsy == 0)
return;
if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y,
if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
&x2, &y2))
return;
@@ -634,7 +653,7 @@ window_panes_clear_floating_area(struct screen_write_ctx *ctx,
struct grid_cell gc;
int x, y, x2, y2, xx, yy;
if (!window_panes_get_floating_frame(wp, osx, osy, dsx, dsy, &x, &y,
if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
&x2, &y2))
return;
@@ -778,6 +797,7 @@ window_panes_draw_pane(struct window_panes_modedata *data,
struct screen_write_ctx *ctx, struct window_pane *wp,
struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy)
{
struct screen *s = &wp->base;
u_int pane, x, y, sx, sy;
if (!window_panes_pane_visible(wp))
@@ -785,7 +805,7 @@ window_panes_draw_pane(struct window_panes_modedata *data,
if (!window_panes_get_geometry(wp, root, osx, osy, dsx, dsy, &x, &y,
&sx, &sy))
return;
if (!window_panes_clip_floating_body(wp, osx, osy, dsx, dsy, &x, &y,
if (!window_panes_clip_floating_pane(wp, osx, osy, dsx, dsy, &x, &y,
&sx, &sy))
return;
if (window_pane_index(wp, &pane) != 0)
@@ -793,11 +813,15 @@ window_panes_draw_pane(struct window_panes_modedata *data,
window_panes_add_area(data, wp, x, y, sx, sy);
screen_write_cursormove(ctx, x, y, 0);
if (data->preview != NULL &&
wp == data->wp &&
sx <= screen_size_x(data->preview) &&
sy <= screen_size_y(data->preview))
s = data->preview;
if (osx <= dsx && osy <= dsy)
screen_write_fast_copy(ctx, &wp->base, 0, wp->base.grid->hsize,
sx, sy);
screen_write_fast_copy(ctx, s, 0, s->grid->hsize, sx, sy);
else
screen_write_preview(ctx, &wp->base, sx, sy);
screen_write_preview(ctx, s, sx, sy);
window_panes_draw_number(data, ctx, wp, pane, x, y, sx, sy);
}
@@ -917,6 +941,8 @@ window_panes_init(struct window_mode_entry *wme, struct cmdq_item *item,
data->zoomed = -1;
else {
data->zoomed = (w->flags & WINDOW_ZOOMED);
if (!data->zoomed)
window_panes_set_preview(data);
if (!data->zoomed && window_zoom(wp) == 0)
server_redraw_window(w);
}
@@ -949,6 +975,10 @@ window_panes_free(struct window_mode_entry *wme)
if (data->state != NULL)
args_make_commands_free(data->state);
window_panes_free_areas(data);
if (data->preview != NULL) {
screen_free(data->preview);
free(data->preview);
}
screen_free(&data->screen);
free(data);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-visible.c,v 1.4 2026/06/29 19:03:34 nicm Exp $ */
/* $OpenBSD: window-visible.c,v 1.5 2026/07/19 17:25:38 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -81,7 +81,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
}
w = base_wp->window;
if ((u_int)py >= w->sy)
if ((u_int)py >= w->sy || (u_int)px >= w->sx)
goto empty;
if (px + width > w->sx)
width = w->sx - px;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.364 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD: window.c,v 1.366 2026/07/19 19:53:11 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>