Merge branch 'master' into 5211-fix-select-layout-list-windows

This commit is contained in:
Michael Grant
2026-07-05 17:21:23 +01:00
26 changed files with 216 additions and 166 deletions

10
CHANGES
View File

@@ -1,3 +1,12 @@
CHANGES FROM 3.7 to 3.7a
* Fix crash in break-pane when no name is provided.
* Scrollbar options are now cached rather than being looked up for every redraw
(issue 5298).
* Only forbid #( in names, allow #[, empty names, : and .
CHANGES FROM 3.6b TO 3.7
* Add floating panes. These are panes which sit above the layout ("tiled
@@ -5,6 +14,7 @@ CHANGES FROM 3.6b TO 3.7
the same escape sequence support). Floating panes are created with the
new-pane command, bound to * by default.
This is an early release of this feature and they are relatively limited.
Currently floating panes can only be moved and resized using the mouse. The
default second status line (if status-format is set to 2) has changed to show
a list of panes. Many obvious features are not yet available for floating

View File

@@ -108,7 +108,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'W'))
return (cmd_break_pane_float(item, args, w, wp));
if (name != NULL && !check_name(name, WINDOW_NAME_FORBID)) {
if (name != NULL && !check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
return (CMD_RETURN_ERROR);
}
@@ -132,7 +132,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (name != NULL) {
window_set_name(w, name, WINDOW_NAME_FORBID);
window_set_name(w, name, 0);
options_set_number(w->options, "automatic-rename", 0);
}
server_unlink_window(src_s, wl);
@@ -162,7 +162,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (name == NULL) {
newname = default_window_name(w);
window_set_name(w, newname, WINDOW_NAME_FORBID);
window_set_name(w, newname, 0);
free(newname);
} else {
window_set_name(w, name, 0);

View File

@@ -101,22 +101,22 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if ((tmp = args_get(args, 'n')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename, WINDOW_NAME_FORBID)) {
if (!check_name(ename)) {
cmdq_error(item, "invalid window name: %s", ename);
free(ename);
return (CMD_RETURN_ERROR);
}
wname = clean_name(ename, WINDOW_NAME_FORBID);
wname = clean_name(ename, 0);
free(ename);
}
if ((tmp = args_get(args, 's')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename, SESSION_NAME_FORBID)) {
if (!check_name(ename)) {
cmdq_error(item, "invalid session name: %s", ename);
free(ename);
goto fail;
}
sname = clean_name(ename, SESSION_NAME_FORBID);
sname = clean_name(ename, 0);
free(ename);
}
if (args_has(args, 'A')) {
@@ -152,12 +152,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
else if (groupwith != NULL)
prefix = xstrdup(groupwith->name);
else {
if (!check_name(group, SESSION_NAME_FORBID)) {
if (!check_name(group)) {
cmdq_error(item,
"invalid session group name: %s", group);
goto fail;
}
prefix = clean_name(group, SESSION_NAME_FORBID);
prefix = clean_name(group, 0);
}
}

View File

@@ -73,12 +73,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
name = args_get(args, 'n');
if (name != NULL) {
expanded = format_single(item, name, c, s, NULL, NULL);
if (!check_name(expanded, WINDOW_NAME_FORBID)) {
if (!check_name(expanded)) {
cmdq_error(item, "invalid window name: %s", expanded);
free(expanded);
return (CMD_RETURN_ERROR);
}
wname = clean_name(expanded, WINDOW_NAME_FORBID);
wname = clean_name(expanded, 0);
free(expanded);
}
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {

View File

@@ -52,12 +52,12 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0));
if (!check_name(tmp, SESSION_NAME_FORBID)) {
if (!check_name(tmp)) {
cmdq_error(item, "invalid session name: %s", tmp);
free(tmp);
return (CMD_RETURN_ERROR);
}
newname = clean_name(tmp, SESSION_NAME_FORBID);
newname = clean_name(tmp, 0);
free(tmp);
if (strcmp(newname, s->name) == 0) {
free(newname);

View File

@@ -51,13 +51,13 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item)
char *name;
name = format_single_from_target(item, args_string(args, 0));
if (!check_name(name, WINDOW_NAME_FORBID)) {
if (!check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
window_set_name(wl->window, name, WINDOW_NAME_FORBID);
window_set_name(wl->window, name, 0);
options_set_number(wl->window->options, "automatic-rename", 0);
free(name);

View File

@@ -235,7 +235,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx, sx, sy, new_sx, new_sy;
int scrollbars, sb_pos, left, right;
int left, right;
int new_xoff, new_yoff, resizes = 0;
wp = cmd_mouse_pane(m, NULL, &wl);
@@ -247,15 +247,13 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
lc = wp->layout_cell;
sx = wp->sx;
sy = wp->sy;
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
left = wp->xoff - 1;
right = wp->xoff + sx;
if (window_pane_scrollbar_reserve(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_LEFT) {
if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_LEFT) {
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
} else if (window_pane_scrollbar_reserve(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_RIGHT) {
} else if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
}

View File

@@ -710,6 +710,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
"AFTER" };
size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL];
struct hyperlinks *hl = os->hyperlinks;
struct screen_write_ctx ctx[TOTAL];
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
u_int map[] = { LEFT,
@@ -723,7 +724,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct grid_cell gc, current_default, base_default;
struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
const char *cp, *end, *link_uri;
enum utf8_state more;
char *tmp;
struct format_range *fr = NULL, *fr1;
@@ -838,6 +839,17 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
sy.gc.fg = base->fg;
}
/*
* Resolve any hyperlink and store it in the cell. The URI
* doubles as the internal ID so repeated links share one entry
* and the ID stays stable across redraws.
*/
link_uri = style_link(&sy);
if (link_uri != NULL && hl != NULL)
sy.gc.link = hyperlinks_put(hl, link_uri, link_uri);
else
sy.gc.link = 0;
/* If this style has a fill colour, store it for later. */
if (sy.fill != 8)
fill = sy.fill;

14
input.c
View File

@@ -1953,16 +1953,14 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
case 2004:
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
break;
case 2026:
screen_write_stop_sync(ictx->wp);
break;
case 2031:
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL)
ictx->wp->flags &= ~PANE_THEMECHANGED;
break;
case 2026: /* synchronized output */
screen_write_stop_sync(ictx->wp);
if (ictx->wp != NULL)
ictx->wp->flags |= PANE_REDRAW;
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@@ -2065,7 +2063,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
ictx->wp->flags &= ~PANE_THEMECHANGED;
}
break;
case 2026: /* synchronized output */
case 2026:
screen_write_start_sync(ictx->wp);
break;
default:
@@ -2822,10 +2820,10 @@ input_exit_rename(struct input_ctx *ictx)
if (o != NULL)
options_remove_or_default(o, -1, NULL);
if (!options_get_number(w->options, "automatic-rename"))
window_set_name(w, "", WINDOW_NAME_FORBID_EXT);
window_set_name(w, "", 1);
} else {
options_set_number(w->options, "automatic-rename", 0);
window_set_name(w, ictx->input_buf, WINDOW_NAME_FORBID_EXT);
window_set_name(w, ictx->input_buf, 1);
}
server_redraw_window_borders(w);
server_status_window(w);

View File

@@ -432,13 +432,11 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{
struct window_pane *wp;
struct layout_cell *lc;
int status, scrollbars, sb_pos, sb_w, sb_pad;
int status, sb_w, sb_pad;
int old_xoff, old_yoff, changed = 0;
u_int sx, sy, old_sx, old_sy;
status = window_get_pane_status(w);
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip)
@@ -461,14 +459,14 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sy--;
}
if (window_pane_scrollbar_reserve(wp, scrollbars)) {
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 (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) {
wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM;
@@ -525,16 +523,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
struct layout_cell *lcchild;
struct style *sb_style = &w->active->scrollbar_style;
u_int available, minimum;
int status, scrollbars;
int status;
status = window_get_pane_status(w);
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
if (scrollbars == PANE_SCROLLBARS_ALWAYS)
if (w->sb == PANE_SCROLLBARS_ALWAYS)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
@@ -1246,17 +1243,16 @@ layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
{
struct style *sb_style = &wp->scrollbar_style;
u_int minimum, sx = lc->sx, sy = lc->sy;
int scrollbars, status;
int status;
if (lc->flags & LAYOUT_CELL_FLOATING)
fatalx("floating cells cannot be split");
status = window_get_pane_status(wp->window);
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
switch (type) {
case LAYOUT_LEFTRIGHT:
if (scrollbars == PANE_SCROLLBARS_ALWAYS) {
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS) {
minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad;
} else

View File

@@ -206,8 +206,6 @@ static const char* mode_tree_help_start[] = {
"#[fg=themelightgrey]"
" C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward",
"#[fg=themelightgrey]"
" C-r #[#{E:tree-mode-border-style},acs]x#[default] Search backward",
"#[fg=themelightgrey]"
" n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward",
"#[fg=themelightgrey]"
" N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward",

View File

@@ -95,7 +95,7 @@ check_window_name(struct window *w)
name = format_window_name(w);
if (strcmp(name, w->name) != 0) {
log_debug("@%u new name %s (was %s)", w->id, name, w->name);
window_set_name(w, name, WINDOW_NAME_FORBID_EXT);
window_set_name(w, name, 1);
server_redraw_window_borders(w);
server_status_window(w);
} else
@@ -166,7 +166,7 @@ parse_window_name(const char *in)
if (*name == '/')
name = basename(name);
name = clean_name(name, WINDOW_NAME_FORBID);
name = clean_name(name, 0);
free(copy);
if (name == NULL)
return (xstrdup(""));

View File

@@ -1286,8 +1286,13 @@ options_push_changes(const char *name)
if (strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows)
RB_FOREACH(w, windows, &windows) {
w->sb = options_get_number(w->options,
"pane-scrollbars");
w->sb_pos = options_get_number(w->options,
"pane-scrollbars-position");
layout_fix_panes(w, NULL);
}
}
if (strcmp(name, "pane-scrollbars") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)

View File

@@ -219,7 +219,7 @@ paste_rename(const char *oldname, const char *newname, char **cause)
return (-1);
}
name = clean_name(newname, "");
name = clean_name(newname, 0);
if (name == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", newname);
@@ -287,7 +287,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (-1);
}
newname = clean_name(name, "");
newname = clean_name(name, 0);
if (newname == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", name);

View File

@@ -212,8 +212,6 @@ struct redraw_build_ctx {
u_int sx;
u_int sy;
int sb;
int sbp;
int ind;
struct redraw_build_cell *cells;
@@ -285,16 +283,13 @@ redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
{
struct session *s = c->session;
struct window *w = s->curw->window;
struct options *oo = w->options;
memset(bctx, 0, sizeof *bctx);
bctx->c = c;
bctx->w = w;
redraw_get_window_offset(c, &bctx->ox, &bctx->oy, &bctx->sx, &bctx->sy);
bctx->sb = options_get_number(oo, "pane-scrollbars");
bctx->sbp = options_get_number(oo, "pane-scrollbars-position");
bctx->ind = options_get_number(oo, "pane-border-indicators");
bctx->ind = options_get_number(w->options, "pane-border-indicators");
}
/* Return a cell. */
@@ -769,8 +764,8 @@ redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
if (!window_pane_is_visible(wp))
return;
if (window_pane_scrollbar_visible(wp, bctx->sb)) {
overlay = window_pane_scrollbar_overlay(wp, bctx->sb);
if (window_pane_scrollbar_visible(wp)) {
overlay = window_pane_scrollbar_overlay(wp);
if (overlay) {
sb_w = wp->scrollbar_style.width +
wp->scrollbar_style.pad;
@@ -782,7 +777,7 @@ redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
} else
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
}
if (sb_w != 0 && bctx->sbp == PANE_SCROLLBARS_LEFT)
if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT)
sb_left = 1;
redraw_mark_pane_inside(bctx, wp);

View File

@@ -1011,6 +1011,8 @@ screen_write_stop_sync(struct window_pane *wp)
evtimer_del(&wp->sync_timer);
wp->base.mode &= ~MODE_SYNC;
wp->flags |= PANE_REDRAW;
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
}

View File

@@ -250,10 +250,7 @@ screen_set_title(struct screen *s, const char *title, int untrusted)
{
char *new_title;
if (untrusted)
new_title = clean_name(title, "#");
else
new_title = clean_name(title, "");
new_title = clean_name(title, untrusted);
if (new_title == NULL)
return (0);
free(s->title);
@@ -267,10 +264,7 @@ screen_set_path(struct screen *s, const char *path, int untrusted)
{
char *new_path;
if (untrusted)
new_path = clean_name(path, "#");
else
new_path = clean_name(path, "");
new_path = clean_name(path, untrusted);
if (new_path == NULL)
return (0);
free(s->path);

View File

@@ -609,10 +609,9 @@ server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
{
struct window *w = wp->window;
u_int width, pad, total;
int sb, sb_pos, start, end;
int start, end;
sb = options_get_number(w->options, "pane-scrollbars");
if (!window_pane_scrollbar_overlay(wp, sb))
if (!window_pane_scrollbar_overlay(wp))
return (0);
if (py < wp->yoff || py >= wp->yoff + (int)wp->sy)
return (0);
@@ -623,8 +622,7 @@ server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
if (total == 0 || total > wp->sx)
total = wp->sx;
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
start = wp->xoff;
end = wp->xoff + (int)total - 1;
} else {
@@ -664,17 +662,15 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
{
struct window *w = wp->window;
struct window_pane *fwp;
int pane_status, sb, sb_pos, sb_w, sb_pad;
int pane_status, sb_w, sb_pad;
int pane_status_line, sl_top, sl_bottom;
int bdr_bottom, bdr_top, bdr_left, bdr_right;
int sb_start, sb_end, sb_overlay;
sb = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
pane_status = window_pane_get_pane_status(wp);
sb_overlay = window_pane_scrollbar_overlay(wp, sb);
sb_overlay = window_pane_scrollbar_overlay(wp);
if (window_pane_scrollbar_visible(wp, sb)) {
if (window_pane_scrollbar_visible(wp)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_overlay && sb_w > (int)wp->sx)
@@ -691,13 +687,13 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
else
pane_status_line = -1; /* not used */
bdr_left = wp->xoff - 1;
if (!sb_overlay && sb_pos == PANE_SCROLLBARS_LEFT)
if (!sb_overlay && w->sb_pos == PANE_SCROLLBARS_LEFT)
bdr_left -= sb_pad + sb_w;
if (sb_overlay && sb_w != 0 &&
py >= wp->yoff && py < wp->yoff + (int)wp->sy &&
px >= wp->xoff && px < wp->xoff + (int)wp->sx) {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
sb_start = wp->xoff;
sb_end = sb_start + sb_w - 1;
} else {
@@ -724,15 +720,15 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
py != pane_status_line && py != wp->yoff + (int)wp->sy) ||
(wp->yoff == 0 && py < (int)wp->sy) ||
(py >= wp->yoff && py < wp->yoff + (int)wp->sy)) &&
((sb_pos == PANE_SCROLLBARS_RIGHT &&
((w->sb_pos == PANE_SCROLLBARS_RIGHT &&
px < wp->xoff + (int)wp->sx + sb_pad + sb_w) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(w->sb_pos == PANE_SCROLLBARS_LEFT &&
px < wp->xoff + (int)wp->sx - sb_pad - sb_w))) {
/* Check if in the scrollbar. */
if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
if ((w->sb_pos == PANE_SCROLLBARS_RIGHT &&
(px >= wp->xoff + (int)wp->sx + sb_pad &&
px < wp->xoff + (int)wp->sx + sb_pad + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(w->sb_pos == PANE_SCROLLBARS_LEFT &&
(px >= wp->xoff - sb_pad - sb_w &&
px < wp->xoff - sb_pad))) {
/* Check where inside the scrollbar. */
@@ -766,7 +762,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
if (window_pane_is_floating(fwp) &&
window_pane_get_pane_lines(fwp) == PANE_LINES_NONE)
continue;
if (window_pane_scrollbar_reserve(fwp, sb)) {
if (window_pane_scrollbar_reserve(fwp)) {
sb_w = fwp->scrollbar_style.width;
sb_pad = fwp->scrollbar_style.pad;
} else {
@@ -776,7 +772,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
bdr_top = fwp->yoff - 1;
bdr_bottom = fwp->yoff + fwp->sy;
bdr_left = fwp->xoff - 1;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
bdr_left -= sb_pad + sb_w;
bdr_right = fwp->xoff + fwp->sx;
} else {
@@ -1997,7 +1993,7 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = server_client_get_pane(c), *loop;
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0, sb;
int mode = 0, cursor, flags, pane_mode = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy, prompt = 0;
u_int sb_w;
struct visible_ranges *r;
@@ -2057,9 +2053,8 @@ server_client_reset_state(struct client *c)
if (sb_w > wp->sx)
sb_w = wp->sx;
if (sb_w != 0 &&
options_get_number(w->options,
"pane-scrollbars-position") ==
PANE_SCROLLBARS_LEFT) {
w->sb_pos ==
PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
cursor = 0;
} else if (sb_w != 0 &&
@@ -2094,11 +2089,10 @@ server_client_reset_state(struct client *c)
mode |= MODE_MOUSE_ALL;
}
}
sb = options_get_number(w->options, "pane-scrollbars");
if (options_get_number(oo, "focus-follows-mouse") ||
sb == PANE_SCROLLBARS_MODAL ||
sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
if (options_get_number(oo, "focus-follows-mouse") ||
w->sb == PANE_SCROLLBARS_MODAL ||
w->sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
else if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;
}

48
style.c
View File

@@ -42,9 +42,17 @@ static struct style style_default = {
STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT,
STYLE_DEFAULT_BASE
STYLE_DEFAULT_BASE,
0
};
/*
* Global hyperlink set holding the URIs for #[link=...] styles, so a style
* only needs to store a small ID rather than the URI itself.
*/
static struct hyperlinks *style_hyperlinks;
/* Set range string. */
static void
style_set_range_string(struct style *sy, const char *s)
@@ -91,6 +99,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.us = base->us;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
sy->link = 0;
} else if (strcasecmp(tmp, "ignore") == 0)
sy->ignore = 1;
else if (strcasecmp(tmp, "noignore") == 0)
@@ -234,7 +243,9 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
} else if (strcasecmp(tmp, "none") == 0)
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
if (strcmp(tmp + 2, "attr") == 0)
if (strcmp(tmp + 2, "link") == 0)
sy->link = 0;
else if (strcmp(tmp + 2, "attr") == 0)
sy->gc.attr |= GRID_ATTR_NOATTR;
else {
value = attributes_fromstring(tmp + 2);
@@ -262,6 +273,15 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
if (errstr != NULL)
goto error;
sy->pad = (int)n;
} else if (strncasecmp(tmp, "link=", 5) == 0) {
if (tmp[5] == '\0')
sy->link = 0;
else {
if (style_hyperlinks == NULL)
style_hyperlinks = hyperlinks_init();
sy->link = hyperlinks_put(style_hyperlinks,
tmp + 5, tmp + 5);
}
} else {
if ((value = attributes_fromstring(tmp)) == -1)
goto error;
@@ -284,8 +304,8 @@ style_tostring(struct style *sy)
{
struct grid_cell *gc = &sy->gc;
int off = 0;
const char *comma = "", *tmp = "";
static char s[1024];
const char *comma = "", *tmp = "", *uri;
static char s[2048];
char b[21];
*s = '\0';
@@ -389,15 +409,33 @@ style_tostring(struct style *sy)
comma = ",";
}
if (sy->pad >= 0) {
xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
off += xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
sy->pad);
comma = ",";
}
uri = style_link(sy);
if (uri != NULL) {
xsnprintf(s + off, sizeof s - off, "%slink=%s", comma, uri);
comma = ",";
}
if (*s == '\0')
return ("default");
return (s);
}
/* Get the hyperlink URI for a style, or NULL if it has none. */
const char *
style_link(struct style *sy)
{
const char *uri;
if (sy->link == 0 || style_hyperlinks == NULL)
return (NULL);
if (!hyperlinks_get(style_hyperlinks, sy->link, &uri, NULL, NULL))
return (NULL);
return (uri);
}
/* Apply a style on top of the given style. */
struct style *
style_add(struct grid_cell *gc, struct options *oo, const char *name,

22
tmux.1
View File

@@ -7449,6 +7449,28 @@ Set the width of the styled area.
.Ar N
may be a column count or a percentage (for example
.Ql 50% ) .
.It Xo Ic link=uri
(or
.Ic nolink )
.Xc
Make the styled text an OSC 8 hyperlink to
.Ar uri ,
for example
.Ql #[link=https://example.com]text#[nolink] .
This is emitted only to terminals with the
.Ic hyperlinks
feature (see
.Ic terminal-features ) ;
it works in the status line and in other formats drawn with styles.
The link continues until
.Ic nolink ,
.Ic default ,
or an empty
.Ic link=
is given.
The
.Ar uri
may not contain spaces or commas and is limited in length.
.It Xo Ic list=on ,
.Ic list=focus ,
.Ic list=left\-marker ,

20
tmux.c
View File

@@ -282,7 +282,7 @@ get_timer(void)
}
char *
clean_name(const char *name, const char* forbid)
clean_name(const char *name, int untrusted)
{
char *copy, *cp, *new_name;
@@ -290,10 +290,7 @@ clean_name(const char *name, const char* forbid)
return (NULL);
copy = xstrdup(name);
for (cp = copy; *cp != '\0'; cp++) {
if (*cp == '#' && strchr(forbid, '#') != NULL) {
if (cp[1] == '(')
*cp = '_';
} else if (strchr(forbid, *cp) != NULL)
if (untrusted && cp[0] == '#' && cp[1] == '(')
*cp = '_';
}
utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
@@ -301,22 +298,11 @@ clean_name(const char *name, const char* forbid)
return (new_name);
}
/*
* Check a name given by a command: reject it if it is empty, not valid UTF-8,
* or contains a forbidden character. Other characters that clean_name would
* change (for example with utf8_stravis) are allowed and fixed silently.
*/
int
check_name(const char *name, const char *forbid)
check_name(const char *name)
{
const char *cp;
if (!utf8_isvalid(name))
return (0);
for (cp = name; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)
return (0);
}
return (1);
}

26
tmux.h
View File

@@ -100,12 +100,6 @@ struct winlink;
#define TMUX_LOCK_CMD "lock -np"
#endif
/* Forbidden characters in names. */
#define WINDOW_NAME_FORBID ":."
#define WINDOW_NAME_FORBID_EXT ":.#"
#define SESSION_NAME_FORBID ":."
#define SESSION_NAME_FORBID_EXT ":.#"
/* Minimum and maximum layout cell size, NOT including border lines. */
#define PANE_MINIMUM 1
#define PANE_MAXIMUM 10000
@@ -993,6 +987,8 @@ struct style {
int pad;
enum style_default_type default_type;
u_int link;
};
#ifdef ENABLE_SIXEL
@@ -1418,6 +1414,9 @@ struct window {
u_int last_new_pane_x;
u_int last_new_pane_y;
int sb;
int sb_pos;
struct utf8_data *fill_character;
int flags;
#define WINDOW_BELL 0x1
@@ -2503,8 +2502,8 @@ int checkshell(const char *);
void setblocking(int, int);
char *shell_argv0(const char *, int);
uint64_t get_timer(void);
char *clean_name(const char *, const char *);
int check_name(const char *, const char *);
char *clean_name(const char *, int);
int check_name(const char *);
const char *sig2name(int);
const char *find_cwd(void);
const char *find_home(void);
@@ -3625,7 +3624,7 @@ void window_pane_stack_push(struct window_panes *,
struct window_pane *);
void window_pane_stack_remove(struct window_panes *,
struct window_pane *);
void window_set_name(struct window *, const char *, const char *);
void window_set_name(struct window *, const char *, int);
void window_add_ref(struct window *, const char *);
void window_remove_ref(struct window *, const char *);
void winlink_clear_flags(struct winlink *);
@@ -3639,10 +3638,10 @@ void window_pane_update_used_data(struct window_pane *,
void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
int window_pane_mode(struct window_pane *);
int window_pane_show_scrollbar(struct window_pane *, int);
int window_pane_scrollbar_reserve(struct window_pane *, int);
int window_pane_scrollbar_visible(struct window_pane *, int);
int window_pane_scrollbar_overlay(struct window_pane *, int);
int window_pane_show_scrollbar(struct window_pane *);
int window_pane_scrollbar_reserve(struct window_pane *);
int window_pane_scrollbar_visible(struct window_pane *);
int window_pane_scrollbar_overlay(struct window_pane *);
int window_pane_scrollbar_overlay_visible(struct window_pane *);
void window_pane_scrollbar_show(struct window_pane *, int);
void window_pane_scrollbar_hide(struct window_pane *);
@@ -4021,6 +4020,7 @@ int style_parse(struct style *,const struct grid_cell *,
int style_parse_colour(struct style *,
const struct grid_cell *, const char *);
const char *style_tostring(struct style *);
const char *style_link(struct style *);
struct style *style_add(struct grid_cell *, struct options *,
const char *, struct format_tree *);
void style_apply(struct grid_cell *, struct options *,

View File

@@ -580,8 +580,21 @@ tty_default_features(int *feat, const char *name, u_int version)
"cstyle,"
"extkeys,"
"focus,"
"hyperlinks,"
"usstyle"
},
{ .name = "ghostty",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"focus,"
"hyperlinks,"
"osc7,"
"sync,"
"usstyle,"
"progressbar"
},
{ .name = "XTerm",
/*
* xterm also supports DECSLRM and DECFRA, but they can be

View File

@@ -1663,6 +1663,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "foot", 0);
else if (strncmp(tmp, "WezTerm ", 7) == 0)
tty_default_features(features, "WezTerm", 0);
else if (strncmp(tmp, "ghostty ", 8) == 0)
tty_default_features(features, "ghostty", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);

View File

@@ -55,7 +55,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
struct window *w;
struct visible_range *ri;
static struct visible_ranges sr = { NULL, 0, 0 };
int found_self, sb, sb_w, sb_pos;
int found_self, sb_w, sb_pos;
int lb, rb, tb, bb, sx, ex, no_border;
u_int i, s;
@@ -95,8 +95,6 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
r->used = 1;
}
sb = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
found_self = 0;
TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
@@ -126,7 +124,9 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
continue;
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
if (!window_pane_scrollbar_reserve(wp, sb))
if (window_pane_scrollbar_reserve(wp))
sb_pos = w->sb_pos;
else
sb_w = sb_pos = 0;
for (i = 0; i < r->used; i++) {

View File

@@ -420,11 +420,11 @@ window_remove_ref(struct window *w, const char *from)
}
void
window_set_name(struct window *w, const char *new_name, const char *forbid)
window_set_name(struct window *w, const char *new_name, int untrusted)
{
char *name;
name = clean_name(new_name, forbid);
name = clean_name(new_name, untrusted);
if (name != NULL) {
free(w->name);
w->name = name;
@@ -1172,20 +1172,15 @@ window_pane_scrollbar_timer(__unused int fd, __unused short events, void *arg)
static int
window_pane_scrollbar_auto_hide(struct window_pane *wp)
{
int sb;
sb = options_get_number(wp->window->options, "pane-scrollbars");
return (sb == PANE_SCROLLBARS_MODAL || sb == PANE_SCROLLBARS_AUTOHIDE);
return (wp->window->sb == PANE_SCROLLBARS_MODAL ||
wp->window->sb == PANE_SCROLLBARS_AUTOHIDE);
}
int
window_pane_scrollbar_overlay_visible(struct window_pane *wp)
{
int sb;
sb = options_get_number(wp->window->options, "pane-scrollbars");
return (window_pane_scrollbar_overlay(wp, sb) &&
window_pane_scrollbar_visible(wp, sb));
return (window_pane_scrollbar_overlay(wp) &&
window_pane_scrollbar_visible(wp));
}
void
@@ -1786,17 +1781,13 @@ window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff,
u_int *sx, u_int *sy)
{
struct window *w = wp->window;
int pane_scrollbars;
u_int sb_w, sb_pos;
u_int sb_w;
pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
if (window_pane_scrollbar_reserve(wp, pane_scrollbars))
if (window_pane_scrollbar_reserve(wp))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
else
sb_w = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
*xoff = wp->xoff - sb_w;
*sx = wp->sx + sb_w;
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
@@ -2212,40 +2203,39 @@ window_pane_mode(struct window_pane *wp)
return (WINDOW_PANE_NO_MODE);
}
/* Return 1 if scrollbar is or should be displayed. */
int
window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
window_pane_show_scrollbar(struct window_pane *wp)
{
if (SCREEN_IS_ALTERNATE(&wp->base))
return (0);
if (sb_option == PANE_SCROLLBARS_ALWAYS ||
sb_option == PANE_SCROLLBARS_AUTOHIDE ||
(sb_option == PANE_SCROLLBARS_MODAL &&
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS ||
wp->window->sb == PANE_SCROLLBARS_AUTOHIDE ||
(wp->window->sb == PANE_SCROLLBARS_MODAL &&
window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
return (1);
return (0);
}
int
window_pane_scrollbar_reserve(struct window_pane *wp, int sb_option)
window_pane_scrollbar_reserve(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp, sb_option))
if (!window_pane_show_scrollbar(wp))
return (0);
return (sb_option == PANE_SCROLLBARS_ALWAYS);
return (wp->window->sb == PANE_SCROLLBARS_ALWAYS);
}
int
window_pane_scrollbar_overlay(struct window_pane *wp, int sb_option)
window_pane_scrollbar_overlay(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp, sb_option))
if (!window_pane_show_scrollbar(wp))
return (0);
return (window_pane_scrollbar_auto_hide(wp));
}
int
window_pane_scrollbar_visible(struct window_pane *wp, int sb_option)
window_pane_scrollbar_visible(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp, sb_option))
if (!window_pane_show_scrollbar(wp))
return (0);
if (!window_pane_scrollbar_auto_hide(wp))
return (1);
@@ -2273,12 +2263,9 @@ void
window_pane_scrollbar_show(struct window_pane *wp, int start_timer)
{
int changed = 0;
int sb;
if (!window_pane_scrollbar_auto_hide(wp))
return;
sb = options_get_number(wp->window->options, "pane-scrollbars");
if (!window_pane_show_scrollbar(wp, sb))
if (!window_pane_show_scrollbar(wp))
return;
if (!wp->sb_auto_visible) {
wp->sb_auto_visible = 1;