Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-06-23 07:35:51 +01:00
22 changed files with 3812 additions and 1346 deletions

1897
Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -209,6 +209,7 @@ dist_tmux_SOURCES = \
tty.c \ tty.c \
utf8-combined.c \ utf8-combined.c \
utf8.c \ utf8.c \
window-border.c \
window-buffer.c \ window-buffer.c \
window-client.c \ window-client.c \
window-clock.c \ window-clock.c \

View File

@@ -318,6 +318,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
grid_clear_history(wp->base.grid); grid_clear_history(wp->base.grid);
if (args_has(args, 'H')) if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen); screen_reset_hyperlinks(wp->screen);
server_redraw_window(wp->window);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -48,6 +48,16 @@ struct cmd_display_panes_data {
struct args_command_state *state; struct args_command_state *state;
}; };
struct cmd_display_panes_ctx {
struct client *c;
int ox;
int oy;
u_int sx;
u_int sy;
u_int statuslines;
int statustop;
};
static enum args_parse_type static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx, cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause) __unused char **cause)
@@ -56,7 +66,7 @@ cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
} }
static void static void
cmd_display_panes_put(struct screen_redraw_ctx *ctx, cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len) struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
{ {
struct client *c = ctx->c; struct client *c = ctx->c;
@@ -76,7 +86,7 @@ cmd_display_panes_put(struct screen_redraw_ctx *ctx,
} }
static void static void
cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int xoff, u_int yoff, u_int sx, struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
const struct grid_cell *gc) const struct grid_cell *gc)
{ {
@@ -112,7 +122,7 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx,
} }
static void static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp) struct window_pane *wp)
{ {
struct client *c = ctx->c; struct client *c = ctx->c;
@@ -254,17 +264,30 @@ out:
} }
static void static void
cmd_display_panes_draw(struct client *c, __unused void *data, cmd_display_panes_draw(struct client *c, __unused void *data)
struct screen_redraw_ctx *ctx)
{ {
struct window *w = c->session->curw->window; struct session *s = c->session;
struct window_pane *wp; struct window *w = s->curw->window;
struct window_pane *wp;
struct cmd_display_panes_ctx ctx;
u_int lines;
log_debug("%s: %s @%u", __func__, c->name, w->id); log_debug("%s: %s @%u", __func__, c->name, w->id);
memset(&ctx, 0, sizeof ctx);
ctx.c = c;
tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy);
if (options_get_number(s->options, "status-position") == 0) {
lines = status_line_size(c);
if (c->message_string != NULL || c->prompt_string != NULL)
lines = (lines == 0 ? 1 : lines);
ctx.statuslines = lines;
ctx.statustop = 1;
}
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_visible(wp)) if (window_pane_is_visible(wp))
cmd_display_panes_draw_pane(ctx, wp); cmd_display_panes_draw_pane(&ctx, wp);
} }
} }

View File

@@ -109,6 +109,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(w, wp, 1); window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0); cmd_find_from_winlink_pane(current, wl, wp, 0);
window_pop_zoom(w); window_pop_zoom(w);
redraw_invalidate_scene(w);
server_redraw_window(w); server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -149,9 +149,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
colour_palette_from_option(&src_wp->palette, src_wp->options); colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options); colour_palette_from_option(&dst_wp->palette, dst_wp->options);
layout_fix_panes(src_w, NULL); layout_fix_panes(src_w, NULL);
redraw_invalidate_scene(src_w);
server_redraw_window(src_w); server_redraw_window(src_w);
} }
layout_fix_panes(dst_w, NULL); layout_fix_panes(dst_w, NULL);
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w); notify_window("window-layout-changed", src_w);

View File

@@ -4525,6 +4525,25 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
return (list); return (list);
} }
/* Fuzzy match strings. */
static int
format_fuzzy_match(const char *pattern, const char *text, int icase)
{
while (*pattern != '\0') {
if (*text == '\0')
return (0);
if (icase) {
if (tolower((u_char)*pattern) == tolower((u_char)*text))
pattern++;
} else {
if (*pattern == *text)
pattern++;
}
text++;
}
return (1);
}
/* Match against an fnmatch(3) pattern or regular expression. */ /* Match against an fnmatch(3) pattern or regular expression. */
static char * static char *
format_match(struct format_modifier *fm, const char *pattern, const char *text) format_match(struct format_modifier *fm, const char *pattern, const char *text)
@@ -4535,7 +4554,10 @@ format_match(struct format_modifier *fm, const char *pattern, const char *text)
if (fm->argc >= 1) if (fm->argc >= 1)
s = fm->argv[0]; s = fm->argv[0];
if (strchr(s, 'r') == NULL) { if (strchr(s, 'z') != NULL) {
if (!format_fuzzy_match(pattern, text, strchr(s, 'i') != NULL))
return (xstrdup("0"));
} else if (strchr(s, 'r') == NULL) {
if (strchr(s, 'i') != NULL) if (strchr(s, 'i') != NULL)
flags |= FNM_CASEFOLD; flags |= FNM_CASEFOLD;
if (fnmatch(pattern, text, flags) != 0) if (fnmatch(pattern, text, flags) != 0)

View File

@@ -405,7 +405,8 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc; struct layout_cell *lc;
int status, scrollbars, sb_pos, sb_w, sb_pad; int status, scrollbars, sb_pos, sb_w, sb_pad;
u_int sx, sy; int old_xoff, old_yoff, changed = 0;
u_int sx, sy, old_sx, old_sy;
status = window_get_pane_status(w); status = window_get_pane_status(w);
scrollbars = options_get_number(w->options, "pane-scrollbars"); scrollbars = options_get_number(w->options, "pane-scrollbars");
@@ -415,6 +416,11 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
if ((lc = wp->layout_cell) == NULL || wp == skip) if ((lc = wp->layout_cell) == NULL || wp == skip)
continue; continue;
old_xoff = wp->xoff;
old_yoff = wp->yoff;
old_sx = wp->sx;
old_sy = wp->sy;
wp->xoff = lc->xoff; wp->xoff = lc->xoff;
wp->yoff = lc->yoff; wp->yoff = lc->yoff;
sx = lc->sx; sx = lc->sx;
@@ -435,7 +441,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
if (sb_pad < 0) if (sb_pad < 0)
sb_pad = 0; sb_pad = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) { if (sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w < PANE_MINIMUM) { if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) {
wp->xoff = wp->xoff + wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM; (int)sx - PANE_MINIMUM;
sx = PANE_MINIMUM; sx = PANE_MINIMUM;
@@ -452,7 +458,15 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
} }
window_pane_resize(wp, sx, sy); window_pane_resize(wp, sx, sy);
if (wp->xoff != old_xoff ||
wp->yoff != old_yoff ||
wp->sx != old_sx ||
wp->sy != old_sy)
changed = 1;
} }
if (changed)
redraw_invalidate_scene(w);
} }
/* Count the number of available cells in a layout. */ /* Count the number of available cells in a layout. */
@@ -818,10 +832,16 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
return; return;
} }
if (type == LAYOUT_TOPBOTTOM) if (type == LAYOUT_TOPBOTTOM) {
if (lc->sy == size)
return;
lc->sy = size; lc->sy = size;
else } else {
if (lc->sx == size)
return;
lc->sx = size; lc->sx = size;
}
redraw_invalidate_scene(wp->window);
} }
/* Resize a floating pane relative to its current size. */ /* Resize a floating pane relative to its current size. */
@@ -836,6 +856,8 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
*cause = xstrdup("pane is not floating"); *cause = xstrdup("pane is not floating");
return; return;
} }
if (change == 0)
return;
if (type == LAYOUT_TOPBOTTOM) { if (type == LAYOUT_TOPBOTTOM) {
size = lc->sy + change; size = lc->sy + change;
@@ -856,6 +878,7 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
if (opposite) if (opposite)
lc->xoff -= change; lc->xoff -= change;
} }
redraw_invalidate_scene(wp->window);
} }
/* Resize a layout cell. */ /* Resize a layout cell. */

7
menu.c
View File

@@ -257,8 +257,7 @@ menu_reapply_styles(struct menu_data *md, struct client *c)
} }
void void
menu_draw_cb(struct client *c, void *data, menu_draw_cb(struct client *c, void *data)
__unused struct screen_redraw_ctx *rctx)
{ {
struct menu_data *md = data; struct menu_data *md = data;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
@@ -636,7 +635,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
style, selected_style, border_style, fs, cb, data); style, selected_style, border_style, fs, cb, data);
if (md == NULL) if (md == NULL)
return (-1); return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, server_client_set_overlay(c, 0, menu_check_cb, menu_mode_cb,
menu_key_cb, menu_free_cb, menu_resize_cb, md); menu_draw_cb, menu_key_cb, menu_free_cb, menu_resize_cb, md);
return (0); return (0);
} }

View File

@@ -1219,6 +1219,15 @@ options_push_changes(const char *name)
if (strcmp(name, "status") == 0 || if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0) strcmp(name, "status-interval") == 0)
status_timer_start_all(); status_timer_start_all();
if (strcmp(name, "status") == 0 ||
strcmp(name, "status-position") == 0 ||
strcmp(name, "pane-border-indicators") == 0 ||
strcmp(name, "pane-border-lines") == 0 ||
strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0 ||
strcmp(name, "pane-scrollbars-style") == 0)
redraw_invalidate_all_scenes();
if (strcmp(name, "monitor-silence") == 0) if (strcmp(name, "monitor-silence") == 0)
alerts_reset_all(); alerts_reset_all();
if (strcmp(name, "window-style") == 0 || if (strcmp(name, "window-style") == 0 ||

View File

@@ -273,7 +273,7 @@ popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
} }
static void static void
popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) popup_draw_cb(struct client *c, void *data)
{ {
struct popup_data *pd = data; struct popup_data *pd = data;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
@@ -328,7 +328,7 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
menu_draw_cb(c, pd->md, rctx); menu_draw_cb(c, pd->md);
} }
c->overlay_check = popup_check_cb; c->overlay_check = popup_check_cb;
c->overlay_data = pd; c->overlay_data = pd;

File diff suppressed because it is too large Load Diff

View File

@@ -729,19 +729,19 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
gc.attr |= GRID_ATTR_CHARSET; gc.attr |= GRID_ATTR_CHARSET;
if (left) if (left)
screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc); screen_write_box_border_set(lines, CELL_URD, &gc);
else else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); screen_write_box_border_set(lines, CELL_LR, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++) for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
if (right) if (right)
screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc); screen_write_box_border_set(lines, CELL_ULD, &gc);
else else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); screen_write_box_border_set(lines, CELL_LR, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_set_cursor(ctx, cx, cy); screen_write_set_cursor(ctx, cx, cy);
@@ -844,26 +844,26 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
gc.flags |= GRID_FLAG_NOPALETTE; gc.flags |= GRID_FLAG_NOPALETTE;
/* Draw top border */ /* Draw top border */
screen_write_box_border_set(lines, CELL_TOPLEFT, &gc); screen_write_box_border_set(lines, CELL_RD, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++) for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc); screen_write_box_border_set(lines, CELL_LD, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
/* Draw bottom border */ /* Draw bottom border */
screen_write_set_cursor(ctx, cx, cy + ny - 1); screen_write_set_cursor(ctx, cx, cy + ny - 1);
screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc); screen_write_box_border_set(lines, CELL_RU, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++) for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc); screen_write_box_border_set(lines, CELL_LU, &gc);
screen_write_cell(ctx, &gc); screen_write_cell(ctx, &gc);
/* Draw sides */ /* Draw sides */
screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc); screen_write_box_border_set(lines, CELL_UD, &gc);
for (i = 1; i < ny - 1; i++) { for (i = 1; i < ny - 1; i++) {
/* left side */ /* left side */
screen_write_set_cursor(ctx, cx, cy + i); screen_write_set_cursor(ctx, cx, cy + i);

View File

@@ -530,6 +530,7 @@ server_client_free(__unused int fd, __unused short events, void *arg)
log_debug("free client %p (%d references)", c, c->references); log_debug("free client %p (%d references)", c, c->references);
redraw_free_scene(c->redraw_scene);
cmdq_free(c->queue); cmdq_free(c->queue);
if (c->references == 0) { if (c->references == 0) {
@@ -2101,11 +2102,11 @@ server_client_check_redraw(struct client *c)
if (wp->flags & PANE_REDRAW) { if (wp->flags & PANE_REDRAW) {
log_debug("%s: redraw pane %%%u", __func__, log_debug("%s: redraw pane %%%u", __func__,
wp->id); wp->id);
screen_redraw_pane(c, wp, 0); redraw_pane(c, wp);
} else if (wp->flags & PANE_REDRAWSCROLLBAR) { } else if (wp->flags & PANE_REDRAWSCROLLBAR) {
log_debug("%s: redraw scrollbar %%%u", __func__, log_debug("%s: redraw scrollbar %%%u", __func__,
wp->id); wp->id);
screen_redraw_pane(c, wp, 1); redraw_pane_scrollbar(c, wp);
} }
} }
} }
@@ -2120,7 +2121,7 @@ server_client_check_redraw(struct client *c)
server_client_set_path(c); server_client_set_path(c);
} }
server_client_set_progress_bar(c); server_client_set_progress_bar(c);
screen_redraw_screen(c); redraw_screen(c);
} }
/* Put the tty back how it was. */ /* Put the tty back how it was. */

View File

@@ -1424,6 +1424,22 @@ status_prompt_backward_word(struct client *c, const char *separators)
c->prompt_index = idx; c->prompt_index = idx;
} }
/* Should this key exit incremental prompt? */
static int
status_prompt_incremental_exit_key(key_code key)
{
switch (key) {
case KEYC_UP:
case KEYC_DOWN:
case KEYC_LEFT:
case KEYC_RIGHT:
case KEYC_PPAGE:
case KEYC_NPAGE:
return (1);
}
return (0);
}
/* Handle keys in prompt. */ /* Handle keys in prompt. */
int int
status_prompt_key(struct client *c, key_code key) status_prompt_key(struct client *c, key_code key)
@@ -1482,6 +1498,15 @@ status_prompt_key(struct client *c, key_code key)
} }
process_key: process_key:
if ((c->prompt_flags & PROMPT_INCREMENTAL) &&
status_prompt_incremental_exit_key(key)) {
s = utf8_tocstr(c->prompt_buffer);
c->prompt_inputcb(c, c->prompt_data, s, 1);
status_prompt_clear(c);
free(s);
return (1);
}
switch (key) { switch (key) {
case KEYC_LEFT: case KEYC_LEFT:
case 'b'|KEYC_CTRL: case 'b'|KEYC_CTRL:

30
style.c
View File

@@ -454,22 +454,26 @@ void
style_set_scrollbar_style_from_option(struct style *sb_style, style_set_scrollbar_style_from_option(struct style *sb_style,
struct options *oo) struct options *oo)
{ {
struct style *sy; const struct options_table_entry *oe;
struct options_entry *o;
const char *s;
sy = options_string_to_style(oo, "pane-scrollbars-style", NULL); style_set(sb_style, &grid_default_cell);
if (sy == NULL) { o = options_get(oo, "pane-scrollbars-style");
style_set(sb_style, &grid_default_cell); if (o == NULL)
fatalx("missing pane-scrollbars-style");
oe = options_table_entry(o);
if (style_parse(sb_style, &grid_default_cell, oe->default_str) != 0)
fatalx("bad pane-scrollbars-style default");
s = options_get_string(oo, "pane-scrollbars-style");
if (s != NULL && style_parse(sb_style, &grid_default_cell, s) != 0)
style_parse(sb_style, &grid_default_cell, oe->default_str);
if (sb_style->width < 1)
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH; sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
if (sb_style->pad < 0)
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING; sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER); utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
} else {
style_copy(sb_style, sy);
if (sb_style->width < 1)
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
if (sb_style->pad < 0)
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
}
} }
/* Initialize style ranges. */ /* Initialize style ranges. */

10
tmux.1
View File

@@ -6381,13 +6381,19 @@ An optional argument specifies flags:
.Ql r .Ql r
means the pattern is a regular expression instead of the default means the pattern is a regular expression instead of the default
.Xr glob 7 .Xr glob 7
pattern, and pattern,
.Ql z
means the pattern is a fuzzy match, that is, it matches if its characters
appear in the string in order but not necessarily consecutively,
and
.Ql i .Ql i
means to ignore case. means to ignore case.
For example: For example:
.Ql #{m:*foo*,#{host}} .Ql #{m:*foo*,#{host}}
or or
.Ql #{m/ri:\[ha]A,MYVAR} . .Ql #{m/ri:\[ha]A,MYVAR}
or
.Ql #{m/z:abc,aXbXc} .
A A
.Ql C .Ql C
performs a search for a performs a search for a

86
tmux.h
View File

@@ -63,6 +63,8 @@ struct mouse_event;
struct options; struct options;
struct options_array_item; struct options_array_item;
struct options_entry; struct options_entry;
struct redraw_scene;
struct redraw_span;
struct screen_write_citem; struct screen_write_citem;
struct screen_write_cline; struct screen_write_cline;
struct screen_write_ctx; struct screen_write_ctx;
@@ -796,20 +798,23 @@ struct colour_palette {
#define GRID_STRING_USED_ONLY 0x8 #define GRID_STRING_USED_ONLY 0x8
#define GRID_STRING_EMPTY_CELLS 0x10 #define GRID_STRING_EMPTY_CELLS 0x10
/* Cell positions. */ /*
* Cell border characters. Border cells are named for the directions they
* connect to: U for up, D for down, L for left and R for right.
*/
#define CELL_INSIDE 0 #define CELL_INSIDE 0
#define CELL_TOPBOTTOM 1 #define CELL_UD 1
#define CELL_LEFTRIGHT 2 #define CELL_LR 2
#define CELL_TOPLEFT 3 #define CELL_RD 3
#define CELL_TOPRIGHT 4 #define CELL_LD 4
#define CELL_BOTTOMLEFT 5 #define CELL_RU 5
#define CELL_BOTTOMRIGHT 6 #define CELL_LU 6
#define CELL_TOPJOIN 7 #define CELL_LRD 7
#define CELL_BOTTOMJOIN 8 #define CELL_LRU 8
#define CELL_LEFTJOIN 9 #define CELL_URD 9
#define CELL_RIGHTJOIN 10 #define CELL_ULD 10
#define CELL_JOIN 11 #define CELL_LRUD 11
#define CELL_OUTSIDE 12 #define CELL_NONE 12
#define CELL_SCROLLBAR 13 #define CELL_SCROLLBAR 13
/* Cell borders. */ /* Cell borders. */
@@ -1113,27 +1118,6 @@ enum pane_lines {
#define WINDOW_PANE_COPY_MODE 1 #define WINDOW_PANE_COPY_MODE 1
#define WINDOW_PANE_VIEW_MODE 2 #define WINDOW_PANE_VIEW_MODE 2
/* Screen redraw context. */
struct screen_redraw_ctx {
struct client *c;
u_int statuslines;
int statustop;
enum pane_lines pane_lines;
int pane_scrollbars;
int pane_scrollbars_pos;
struct grid_cell no_pane_gc;
int no_pane_gc_set;
u_int sx;
u_int sy;
int ox;
int oy;
};
/* Screen size. */ /* Screen size. */
#define screen_size_x(s) ((s)->grid->sx) #define screen_size_x(s) ((s)->grid->sx)
#define screen_size_y(s) ((s)->grid->sy) #define screen_size_y(s) ((s)->grid->sy)
@@ -1284,7 +1268,7 @@ struct window_pane {
#define PANE_FOCUSED 0x4 #define PANE_FOCUSED 0x4
#define PANE_VISITED 0x8 #define PANE_VISITED 0x8
#define PANE_ZOOMED 0x10 #define PANE_ZOOMED 0x10
/* 0x20 unused */ #define PANE_NEWSTATUS 0x20
#define PANE_INPUTOFF 0x40 #define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80 #define PANE_CHANGED 0x80
#define PANE_EXITED 0x100 #define PANE_EXITED 0x100
@@ -1340,7 +1324,6 @@ struct window_pane {
struct screen base; struct screen base;
struct screen status_screen; struct screen status_screen;
size_t status_size;
TAILQ_HEAD(, window_mode_entry) modes; TAILQ_HEAD(, window_mode_entry) modes;
@@ -1405,6 +1388,8 @@ struct window {
u_int new_xpixel; u_int new_xpixel;
u_int new_ypixel; u_int new_ypixel;
uint64_t redraw_scene_generation;
u_int last_new_pane_x; u_int last_new_pane_x;
u_int last_new_pane_y; u_int last_new_pane_y;
@@ -2049,12 +2034,11 @@ RB_HEAD(client_windows, client_window);
/* Client connection. */ /* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *); typedef void (*prompt_free_cb)(void *);
typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *, typedef struct visible_ranges *(*overlay_check_cb)(struct client *, void *,
u_int, u_int, u_int); u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *, typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
u_int *); u_int *);
typedef void (*overlay_draw_cb)(struct client *, void *, typedef void (*overlay_draw_cb)(struct client *, void *);
struct screen_redraw_ctx *);
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *); typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *); typedef void (*overlay_resize_cb)(struct client *, void *);
@@ -2100,6 +2084,8 @@ struct client {
size_t discarded; size_t discarded;
size_t redraw; size_t redraw;
struct redraw_scene *redraw_scene;
struct event repeat_timer; struct event repeat_timer;
struct event click_timer; struct event click_timer;
@@ -3387,8 +3373,13 @@ void screen_write_alternateoff(struct screen_write_ctx *,
struct grid_cell *, int); struct grid_cell *, int);
/* screen-redraw.c */ /* screen-redraw.c */
void screen_redraw_screen(struct client *); void redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *, int); void redraw_pane(struct client *, struct window_pane *);
void redraw_pane_scrollbar(struct client *, struct window_pane *);
void redraw_free_scene(struct redraw_scene *);
void redraw_invalidate_scene(struct window *);
void redraw_invalidate_all_scenes(void);
int redraw_get_status_border_cell_type(struct redraw_span **, u_int);
/* screen.c */ /* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int); void screen_init(struct screen *, u_int, u_int, u_int);
@@ -3536,6 +3527,16 @@ struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
u_int); u_int);
int window_pane_is_floating(struct window_pane *); int window_pane_is_floating(struct window_pane *);
/* window-border.c */
void window_get_border_cell(struct window *, struct window_pane *,
enum pane_lines, int, struct grid_cell *);
void window_pane_get_border_cell(struct window_pane *, int,
struct grid_cell *);
void window_pane_get_border_style(struct window_pane *,
struct client *, struct grid_cell *);
int window_make_pane_status(struct window_pane *, struct client *,
u_int, struct redraw_span *);
/* window-visible.c */ /* window-visible.c */
int window_position_is_visible(struct visible_ranges *, u_int); int window_position_is_visible(struct visible_ranges *, u_int);
struct visible_ranges *window_visible_ranges(struct window_pane *, int, int, struct visible_ranges *window_visible_ranges(struct window_pane *, int, int,
@@ -3833,8 +3834,7 @@ int menu_display(struct menu *, int, int, struct cmdq_item *,
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int, struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
u_int); u_int);
void menu_draw_cb(struct client *, void *, void menu_draw_cb(struct client *, void *);
struct screen_redraw_ctx *);
void menu_free_cb(struct client *, void *); void menu_free_cb(struct client *, void *);
int menu_key_cb(struct client *, void *, struct key_event *); int menu_key_cb(struct client *, void *, struct key_event *);

171
window-border.c Normal file
View File

@@ -0,0 +1,171 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Get border cell. */
void
window_get_border_cell(struct window *w, struct window_pane *wp,
enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
{
u_int idx;
if (cell_type == CELL_NONE && w->fill_character != NULL) {
utf8_copy(&gc->data, &w->fill_character[0]);
return;
}
switch (pane_lines) {
case PANE_LINES_NUMBER:
if (cell_type == CELL_NONE) {
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[CELL_NONE]);
break;
}
gc->attr &= ~GRID_ATTR_CHARSET;
if (wp != NULL && window_pane_index(wp, &idx) == 0)
utf8_set(&gc->data, '0' + (idx % 10));
else
utf8_set(&gc->data, '*');
break;
case PANE_LINES_DOUBLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
break;
case PANE_LINES_HEAVY:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
break;
case PANE_LINES_SIMPLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
break;
case PANE_LINES_NONE:
case PANE_LINES_SPACES:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, ' ');
break;
default:
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[cell_type]);
break;
}
}
/* Get pane border cell. */
void
window_pane_get_border_cell(struct window_pane *wp, int cell_type,
struct grid_cell *gc)
{
enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
window_get_border_cell(wp->window, wp, pane_lines, cell_type, gc);
}
/* Get pane border style. */
void
window_pane_get_border_style(struct window_pane *wp, struct client *c,
struct grid_cell *gc)
{
struct session *s = c->session;
struct format_tree *ft;
const char *option;
struct grid_cell *saved;
int *flag;
if (wp == server_client_get_pane(c)) {
flag = &wp->active_border_gc_set;
saved = &wp->active_border_gc;
option = "pane-active-border-style";
} else {
flag = &wp->border_gc_set;
saved = &wp->border_gc;
option = "pane-border-style";
}
if (!*flag) {
ft = format_create_defaults(NULL, c, s, s->curw, wp);
style_apply(saved, wp->options, option, ft);
format_free(ft);
*flag = 1;
}
memcpy(gc, saved, sizeof *gc);
}
/* Build pane status line. */
int
window_make_pane_status(struct window_pane *wp, struct client *c, u_int width,
struct redraw_span *span)
{
struct grid_cell gc;
const char *fmt;
struct format_tree *ft;
struct style_line_entry *sle = &wp->border_status_line;
struct screen_write_ctx ctx;
struct screen old;
char *expanded;
u_int i;
enum pane_lines pane_lines;
int pane_status, cell_type;
pane_status = window_pane_get_pane_status(wp);
if (pane_status == PANE_STATUS_OFF || width == 0)
return (0);
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
format_defaults(ft, c, c->session, c->session->curw, wp);
fmt = options_get_string(wp->options, "pane-border-format");
expanded = format_expand_time(ft, fmt);
memcpy(&old, &wp->status_screen, sizeof old);
screen_init(&wp->status_screen, width, 1, 0);
wp->status_screen.mode = 0;
screen_write_start(&ctx, &wp->status_screen);
window_pane_get_border_style(wp, c, &gc);
pane_lines = window_pane_get_pane_lines(wp);
for (i = 0; i < width; i++) {
cell_type = redraw_get_status_border_cell_type(&span, i);
window_get_border_cell(wp->window, wp, pane_lines, cell_type, &gc);
screen_write_cell(&ctx, &gc);
}
gc.attr &= ~GRID_ATTR_CHARSET;
screen_write_cursormove(&ctx, 0, 0, 0);
style_ranges_free(&sle->ranges);
format_draw(&ctx, &gc, width, expanded, &sle->ranges, 0);
screen_write_stop(&ctx);
format_free(ft);
free(sle->expanded);
sle->expanded = expanded;
if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
screen_free(&old);
return (0);
}
screen_free(&old);
return (1);
}

View File

@@ -800,7 +800,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
return; return;
/* /*
* See screen_redraw_draw_pane_scrollbar - this is the inverse of the * See redraw_draw_pane_scrollbar - this is the inverse of the
* formula used there. * formula used there.
*/ */
new_offset = new_slider_y * ((float)(size + sb_height) / sb_height); new_offset = new_slider_y * ((float)(size + sb_height) / sb_height);

View File

@@ -740,8 +740,10 @@ window_customize_draw_option(struct window_customize_modedata *data,
if (strcmp(expanded, value) != 0) { if (strcmp(expanded, value) != 0) {
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
0, &grid_default_cell, "This expands to: %s", 0, &grid_default_cell, "This expands to: %s",
expanded)) expanded)) {
free(expanded);
goto out; goto out;
}
} }
free(expanded); free(expanded);
} }

View File

@@ -762,6 +762,7 @@ window_zoom(struct window_pane *wp)
w->flags |= WINDOW_ZOOMED; w->flags |= WINDOW_ZOOMED;
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
redraw_invalidate_scene(w);
return (0); return (0);
} }
@@ -788,6 +789,7 @@ window_unzoom(struct window *w, int notify)
if (notify) if (notify)
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
redraw_invalidate_scene(w);
return (0); return (0);
} }
@@ -844,6 +846,7 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
else { else {
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
} }
redraw_invalidate_scene(w);
return (wp); return (wp);
} }
@@ -870,6 +873,7 @@ window_lost_pane(struct window *w, struct window_pane *wp)
window_update_focus(w); window_update_focus(w);
} }
} }
redraw_invalidate_scene(w);
} }
void void
@@ -878,6 +882,7 @@ window_remove_pane(struct window *w, struct window_pane *wp)
window_lost_pane(w, wp); window_lost_pane(w, wp);
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_REMOVE(&w->z_index, wp, zentry);
redraw_invalidate_scene(w);
window_pane_destroy(wp); window_pane_destroy(wp);
} }