diff --git a/CHANGES b/CHANGES index cb92e2ff0..172a8d533 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 8453c7e86..7eb371c25 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -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); diff --git a/cmd-new-session.c b/cmd-new-session.c index dbee4e559..ba02c72d4 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -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); } } diff --git a/cmd-new-window.c b/cmd-new-window.c index c65745a26..bb6113fd6 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -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) { diff --git a/cmd-rename-session.c b/cmd-rename-session.c index ca6a2eacb..b399483b8 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -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); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 8428e23f3..8ab9b658a 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -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); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 185844f2e..348923528 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -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; } diff --git a/format-draw.c b/format-draw.c index 510f28e08..47bf192e6 100644 --- a/format-draw.c +++ b/format-draw.c @@ -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; diff --git a/input.c b/input.c index cd664360b..5f7f9d132 100644 --- a/input.c +++ b/input.c @@ -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); diff --git a/layout.c b/layout.c index 87366d601..3bd4a7a0c 100644 --- a/layout.c +++ b/layout.c @@ -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 diff --git a/mode-tree.c b/mode-tree.c index fae0f3d8a..3a126129c 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -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", diff --git a/names.c b/names.c index 756af5b07..cee310366 100644 --- a/names.c +++ b/names.c @@ -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("")); diff --git a/options.c b/options.c index e05930463..e34320869 100644 --- a/options.c +++ b/options.c @@ -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) diff --git a/paste.c b/paste.c index 081e3dbeb..5ee80afd1 100644 --- a/paste.c +++ b/paste.c @@ -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); diff --git a/screen-redraw.c b/screen-redraw.c index 6c1d65a4e..1cd314bb7 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -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); diff --git a/screen-write.c b/screen-write.c index e363471e3..6cedb3e78 100644 --- a/screen-write.c +++ b/screen-write.c @@ -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); } diff --git a/screen.c b/screen.c index 3e8f2fa50..8e513758e 100644 --- a/screen.c +++ b/screen.c @@ -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); diff --git a/server-client.c b/server-client.c index 0a304713a..19300eefa 100644 --- a/server-client.c +++ b/server-client.c @@ -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; } diff --git a/style.c b/style.c index 25b8b0196..2ca5e37af 100644 --- a/style.c +++ b/style.c @@ -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, diff --git a/tmux.1 b/tmux.1 index 03047cd13..d2276faf4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -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 , diff --git a/tmux.c b/tmux.c index 912cd0d1c..132c39217 100644 --- a/tmux.c +++ b/tmux.c @@ -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); } diff --git a/tmux.h b/tmux.h index 9bdf205fb..1d4565da7 100644 --- a/tmux.h +++ b/tmux.h @@ -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 *, diff --git a/tty-features.c b/tty-features.c index 01b9bff38..6e168758f 100644 --- a/tty-features.c +++ b/tty-features.c @@ -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 diff --git a/tty-keys.c b/tty-keys.c index d3a93f10f..f48fb1b71 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -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); diff --git a/window-visible.c b/window-visible.c index 7ec7fcfb5..f46967ec7 100644 --- a/window-visible.c +++ b/window-visible.c @@ -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++) { diff --git a/window.c b/window.c index fe8a9c1f2..04488fd52 100644 --- a/window.c +++ b/window.c @@ -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;