Merge branch 'master' into local_windows

This commit is contained in:
Nicholas Marriott
2026-07-21 08:34:31 +01:00
18 changed files with 528 additions and 82 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-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: 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)
@@ -3813,6 +3886,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>
@@ -174,8 +174,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 *);
@@ -212,21 +210,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;
@@ -809,6 +792,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.
@@ -3192,6 +3191,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)
@@ -3237,27 +3265,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;
@@ -3267,17 +3319,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>
@@ -1349,7 +1349,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."
},

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: 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>
@@ -1606,6 +1606,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);
@@ -1617,10 +1618,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;

8
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1143 2026/07/17 12:42:51 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 17 2026 $
.Dd $Mdocdate: July 19 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -6610,7 +6610,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
@@ -7348,6 +7348,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;
}

38
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1408 2026/07/17 12:42:51 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. */
@@ -3477,6 +3498,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);

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>