-# $OpenBSD: Makefile,v 1.109 2022/05/30 12:48:57 nicm Exp $
+# $OpenBSD: Makefile,v 1.110 2022/06/30 09:55:53 nicm Exp $
PROG= tmux
SRCS= alerts.c \
grid-reader.c \
grid-view.c \
grid.c \
+ hyperlinks.c \
input-keys.c \
input.c \
job.c \
-/* $OpenBSD: cmd-capture-pane.c,v 1.56 2022/06/07 10:02:19 nicm Exp $ */
+/* $OpenBSD: cmd-capture-pane.c,v 1.57 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
.name = "clear-history",
.alias = "clearhist",
- .args = { "t:", 0, 0, NULL },
- .usage = CMD_TARGET_PANE_USAGE,
+ .args = { "Ht:", 0, 0, NULL },
+ .usage = "[-H] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
if (cmd_get_entry(self) == &cmd_clear_history_entry) {
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
+ if (args_has(args, 'H'))
+ screen_reset_hyperlinks(wp->screen);
return (CMD_RETURN_NORMAL);
}
-/* $OpenBSD: cmd-display-panes.c,v 1.44 2021/08/25 08:51:55 nicm Exp $ */
+/* $OpenBSD: cmd-display-panes.c,v 1.45 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
llen = 0;
if (sx < len * 6 || sy < 5) {
- tty_attributes(tty, &fgc, &grid_default_cell, NULL);
+ tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
px -= len * 3;
py -= 2;
- tty_attributes(tty, &bgc, &grid_default_cell, NULL);
+ tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
if (sy <= 6)
goto out;
- tty_attributes(tty, &fgc, &grid_default_cell, NULL);
+ tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);
-/* $OpenBSD: grid.c,v 1.124 2022/06/21 09:30:01 nicm Exp $ */
+/* $OpenBSD: grid.c,v 1.125 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
/* Default grid cell data. */
const struct grid_cell grid_default_cell = {
- { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
+ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
};
/*
* appears in the grid - because of this, they are always extended cells.
*/
static const struct grid_cell grid_padding_cell = {
- { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
+ { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0, 0
};
/* Cleared grid cell data. */
static const struct grid_cell grid_cleared_cell = {
- { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
+ { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
+ if (gc->link != 0)
+ return (1);
return (0);
}
gee->fg = gc->fg;
gee->bg = gc->bg;
gee->us = gc->us;
+ gee->link = gc->link;
return (gee);
}
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
return (0);
+ if (gc1->link != gc2->link)
+ return (0);
return (1);
}
gc->fg = gee->fg;
gc->bg = gee->bg;
gc->us = gee->us;
+ gc->link = gee->link;
utf8_to_data(gee->data, &gc->data);
}
return;
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
utf8_set(&gc->data, gce->data.data);
+ gc->link = 0;
}
/* Get cell for reading. */
--- /dev/null
+/* $OpenBSD: hyperlinks.c,v 1.1 2022/06/30 09:55:53 nicm Exp $ */
+
+/*
+ * Copyright (c) 2021 Will <author@will.party>
+ * Copyright (c) 2022 Jeff Chiang <pobomp@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 <vis.h>
+
+#include "tmux.h"
+
+/*
+ * OSC 8 hyperlinks, described at:
+ *
+ * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+ *
+ * Each hyperlink and ID combination is assigned a number ("inner" in this
+ * file) which is stored in an extended grid cell and maps into a tree here.
+ *
+ * Each URI has one inner number and one external ID (which tmux uses to send
+ * the hyperlink to the terminal) and one internal ID (which is received from
+ * the sending application inside tmux).
+ *
+ * Anonymous hyperlinks are each unique and are not reused even if they have
+ * the same URI (terminals will not want to tie them together).
+ */
+
+#define MAX_HYPERLINKS 5000
+
+static uint64_t hyperlinks_next_external_id = 1;
+static u_int global_hyperlinks_count;
+
+struct hyperlinks_uri {
+ struct hyperlinks *tree;
+
+ u_int inner;
+ const char *internal_id;
+ const char *external_id;
+ const char *uri;
+
+ TAILQ_ENTRY(hyperlinks_uri) list_entry;
+ RB_ENTRY(hyperlinks_uri) by_inner_entry;
+ RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */
+};
+RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri);
+RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri);
+
+TAILQ_HEAD(hyperlinks_list, hyperlinks_uri);
+static struct hyperlinks_list global_hyperlinks =
+ TAILQ_HEAD_INITIALIZER(global_hyperlinks);
+
+struct hyperlinks {
+ u_int next_inner;
+ struct hyperlinks_by_inner_tree by_inner;
+ struct hyperlinks_by_uri_tree by_uri;
+};
+
+static int
+hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right)
+{
+ int r;
+
+ if (*left->internal_id == '\0' || *right->internal_id == '\0') {
+ /*
+ * If both URIs are anonymous, use the inner for comparison so
+ * that they do not match even if the URI is the same - each
+ * anonymous URI should be unique.
+ */
+ if (*left->internal_id != '\0')
+ return (-1);
+ if (*right->internal_id != '\0')
+ return (1);
+ return (left->inner - right->inner);
+ }
+
+ r = strcmp(left->internal_id, right->internal_id);
+ if (r != 0)
+ return (r);
+ return (strcmp(left->uri, right->uri));
+}
+RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
+ hyperlinks_by_uri_cmp);
+RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
+ hyperlinks_by_uri_cmp);
+
+static int
+hyperlinks_by_inner_cmp(struct hyperlinks_uri *left,
+ struct hyperlinks_uri *right)
+{
+ return (left->inner - right->inner);
+}
+RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
+ hyperlinks_by_inner_cmp);
+RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
+ hyperlinks_by_inner_cmp);
+
+/* Remove a hyperlink. */
+static void
+hyperlinks_remove(struct hyperlinks_uri *hlu)
+{
+ struct hyperlinks *hl = hlu->tree;
+
+ TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry);
+ global_hyperlinks_count--;
+
+ RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
+ RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
+
+ free((void *)hlu->internal_id);
+ free((void *)hlu->external_id);
+ free((void *)hlu->uri);
+ free(hlu);
+}
+
+/* Store a new hyperlink or return if it already exists. */
+u_int
+hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
+ const char *internal_id_in)
+{
+ struct hyperlinks_uri find, *hlu;
+ char *uri, *internal_id, *external_id;
+
+ /*
+ * Anonymous URI are stored with an empty internal ID and the tree
+ * comparator will make sure they never match each other (so each
+ * anonymous URI is unique).
+ */
+ if (internal_id_in == NULL)
+ internal_id_in = "";
+
+ utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
+ utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
+
+ if (*internal_id_in != '\0') {
+ find.uri = uri;
+ find.internal_id = internal_id;
+
+ hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find);
+ if (hlu != NULL) {
+ free (uri);
+ free (internal_id);
+ return (hlu->inner);
+ }
+ }
+ xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++);
+
+ hlu = xcalloc(1, sizeof *hlu);
+ hlu->inner = hl->next_inner++;
+ hlu->internal_id = internal_id;
+ hlu->external_id = external_id;
+ hlu->uri = uri;
+ hlu->tree = hl;
+ RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
+ RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
+
+ TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry);
+ if (++global_hyperlinks_count == MAX_HYPERLINKS)
+ hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks));
+
+ return (hlu->inner);
+}
+
+/* Get hyperlink by inner number. */
+int
+hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
+ const char **external_id_out)
+{
+ struct hyperlinks_uri find, *hlu;
+
+ find.inner = inner;
+
+ hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
+ if (hlu == NULL)
+ return (0);
+ *external_id_out = hlu->external_id;
+ *uri_out = hlu->uri;
+ return (1);
+}
+
+/* Initialize hyperlink set. */
+struct hyperlinks *
+hyperlinks_init(void)
+{
+ struct hyperlinks *hl;
+
+ hl = xcalloc(1, sizeof *hl);
+ hl->next_inner = 1;
+ RB_INIT(&hl->by_uri);
+ RB_INIT(&hl->by_inner);
+ return (hl);
+}
+
+/* Free all hyperlinks but not the set itself. */
+void
+hyperlinks_reset(struct hyperlinks *hl)
+{
+ struct hyperlinks_uri *hlu, *hlu1;
+
+ RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1)
+ hyperlinks_remove(hlu);
+}
+
+/* Free hyperlink set. */
+void
+hyperlinks_free(struct hyperlinks *hl)
+{
+ hyperlinks_reset(hl);
+ free(hl);
+}
-/* $OpenBSD: input.c,v 1.205 2022/06/11 16:59:33 nicm Exp $ */
+/* $OpenBSD: input.c,v 1.206 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
static void input_reset_cell(struct input_ctx *);
static void input_osc_4(struct input_ctx *, const char *);
+static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(struct input_ctx *, const char *);
static void input_osc_12(struct input_ctx *, const char *);
}
}
break;
+ case 8:
+ input_osc_8(ictx, p);
+ break;
case 10:
input_osc_10(ictx, p);
break;
free(copy);
}
+/* Handle the OSC 8 sequence for embedding hyperlinks. */
+static void
+input_osc_8(struct input_ctx *ictx, const char *p)
+{
+ struct hyperlinks *hl = ictx->ctx.s->hyperlinks;
+ struct grid_cell *gc = &ictx->cell.cell;
+ const char *start, *end, *uri;
+ char *id = NULL;
+
+ for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) {
+ if (end - start >= 4 && strncmp(start, "id=", 3) == 0) {
+ if (id != NULL)
+ goto bad;
+ id = xstrndup(start + 3, end - start - 3);
+ }
+
+ /* The first ; is the end of parameters and start of the URI. */
+ if (*end == ';')
+ break;
+ }
+ if (end == NULL || *end != ';')
+ goto bad;
+ uri = end + 1;
+ if (*uri == '\0') {
+ gc->link = 0;
+ free(id);
+ return;
+ }
+ gc->link = hyperlinks_put(hl, uri, id);
+ if (id == NULL)
+ log_debug("hyperlink (anonymous) %s = %u", uri, gc->link);
+ else
+ log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link);
+ free(id);
+ return;
+
+bad:
+ log_debug("bad OSC 8 %s", p);
+ free(id);
+}
+
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
-/* $OpenBSD: screen-redraw.c,v 1.95 2022/03/16 17:00:17 nicm Exp $ */
+/* $OpenBSD: screen-redraw.c,v 1.96 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
}
}
- tty_cell(tty, &gc, &grid_default_cell, NULL);
+ tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
if (isolates)
tty_puts(tty, START_ISOLATE);
}
-/* $OpenBSD: screen.c,v 1.80 2022/05/30 12:55:25 nicm Exp $ */
+/* $OpenBSD: screen.c,v 1.81 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
s->sel = NULL;
s->write_list = NULL;
+ s->hyperlinks = NULL;
screen_reinit(s);
}
screen_clear_selection(s);
screen_free_titles(s);
+ screen_reset_hyperlinks(s);
+}
+
+/* Reset hyperlinks of a screen. */
+void
+screen_reset_hyperlinks(struct screen *s)
+{
+ if (s->hyperlinks == NULL)
+ s->hyperlinks = hyperlinks_init();
+ else
+ hyperlinks_reset(s->hyperlinks);
}
/* Destroy a screen. */
grid_destroy(s->saved_grid);
grid_destroy(s->grid);
+ if (s->hyperlinks != NULL)
+ hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
}
-/* $OpenBSD: server.c,v 1.202 2022/06/21 09:30:01 nicm Exp $ */
+/* $OpenBSD: server.c,v 1.203 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
RB_INIT(&sessions);
key_bindings_init();
TAILQ_INIT(&message_log);
-
gettimeofday(&start_time, NULL);
server_fd = server_create_socket(flags, &cause);
-/* $OpenBSD: style.c,v 1.30 2021/08/12 20:46:30 nicm Exp $ */
+/* $OpenBSD: style.c,v 1.31 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
/* Default style. */
static struct style style_default = {
- { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
+ { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
0,
8,
-.\" $OpenBSD: tmux.1,v 1.892 2022/06/20 07:59:37 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.893 2022/06/30 09:55:53 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: June 20 2022 $
+.Dd $Mdocdate: June 30 2022 $
.Dt TMUX 1
.Os
.Sh NAME
Supports extended keys.
.It focus
Supports focus reporting.
+.It hyperlinks
+Supports OSC 8 hyperlinks.
.It ignorefkeys
Ignore function keys from
.Xr terminfo 5
starts without the preview.
This command works only if at least one client is attached.
.Tg clearhist
-.It Ic clear-history Op Fl t Ar target-pane
+.It Xo Ic clear-history
+.Op Fl H
+.Op Fl t Ar target-pane
+.Xc
.D1 Pq alias: Ic clearhist
Remove and free the history for the specified pane.
+.Fl H
+also removes all hyperlinks.
.Tg deleteb
.It Ic delete-buffer Op Fl b Ar buffer-name
.D1 Pq alias: Ic deleteb
These are set automatically if the
.Em XT
capability is present.
+.It Em \&Hls
+Set or clear a hyperlink annotation.
.It Em \&Rect
Tell
.Nm
-/* $OpenBSD: tmux.h,v 1.1175 2022/06/21 09:30:01 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1176 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
struct environ;
struct format_job_tree;
struct format_tree;
+struct hyperlinks_uri;
+struct hyperlinks;
struct input_ctx;
struct job;
struct menu_data;
TTYC_ENFCS,
TTYC_ENMG,
TTYC_FSL,
+ TTYC_HLS,
TTYC_HOME,
TTYC_HPA,
TTYC_ICH,
int fg;
int bg;
int us;
+ u_int link;
};
/* Grid extended cell entry. */
int fg;
int bg;
int us;
+ u_int link;
} __packed;
/* Grid cell entry. */
struct screen_sel *sel;
struct screen_write_cline *write_list;
+
+ struct hyperlinks *hyperlinks;
};
/* Screen write context. */
void tty_update_client_offset(struct client *);
void tty_raw(struct tty *, const char *);
void tty_attributes(struct tty *, const struct grid_cell *,
- const struct grid_cell *, struct colour_palette *);
+ const struct grid_cell *, struct colour_palette *,
+ struct hyperlinks *);
void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int);
void tty_cell(struct tty *, const struct grid_cell *,
- const struct grid_cell *, struct colour_palette *);
+ const struct grid_cell *, struct colour_palette *,
+ struct hyperlinks *);
int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void screen_reinit(struct screen *);
void screen_free(struct screen *);
void screen_reset_tabs(struct screen *);
+void screen_reset_hyperlinks(struct screen *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *);
int server_acl_join(struct client *);
uid_t server_acl_get_uid(struct server_acl_user *);
+/* hyperlink.c */
+u_int hyperlinks_put(struct hyperlinks *, const char *,
+ const char *);
+int hyperlinks_get(struct hyperlinks *, u_int,
+ const char **, const char **);
+struct hyperlinks *hyperlinks_init(void);
+void hyperlinks_reset(struct hyperlinks *);
+void hyperlinks_free(struct hyperlinks *);
+
#endif /* TMUX_H */
-/* $OpenBSD: tty-features.c,v 1.24 2022/06/14 07:29:00 nicm Exp $ */
+/* $OpenBSD: tty-features.c,v 1.25 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
0
};
+/* Terminal supports OSC 8 hyperlinks. */
+static const char *tty_feature_hyperlinks_capabilities[] = {
+ "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\",
+ NULL
+};
+static const struct tty_feature tty_feature_hyperlinks = {
+ "hyperlinks",
+ tty_feature_hyperlinks_capabilities,
+ 0
+};
+
/*
* Terminal supports RGB colour. This replaces setab and setaf also since
* terminals with RGB have versions that do not allow setting colours from the
&tty_feature_bpaste,
&tty_feature_ccolour,
&tty_feature_clipboard,
+ &tty_feature_hyperlinks,
&tty_feature_cstyle,
&tty_feature_extkeys,
&tty_feature_focus,
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM
- ",ccolour,cstyle,focus,overline,usstyle"
+ ",ccolour,cstyle,focus,overline,usstyle,hyperlinks"
},
{ .name = "rxvt-unicode",
.features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys"
},
{ .name = "iTerm2",
.features = TTY_FEATURES_BASE_MODERN_XTERM
- ",cstyle,extkeys,margins,usstyle,sync,osc7"
+ ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks"
},
{ .name = "XTerm",
/*
-/* $OpenBSD: tty-term.c,v 1.92 2022/03/24 09:05:57 nicm Exp $ */
+/* $OpenBSD: tty-term.c,v 1.93 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
+ [TTYC_HLS] = { TTYCODE_STRING, "Hls" },
[TTYC_HOME] = { TTYCODE_STRING, "home" },
[TTYC_HPA] = { TTYCODE_STRING, "hpa" },
[TTYC_ICH1] = { TTYCODE_STRING, "ich1" },
-/* $OpenBSD: tty.c,v 1.420 2022/06/09 09:12:55 nicm Exp $ */
+/* $OpenBSD: tty.c,v 1.421 2022/06/30 09:55:53 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
static void tty_repeat_space(struct tty *, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static void tty_default_attributes(struct tty *, const struct grid_cell *,
- struct colour_palette *, u_int);
+ struct colour_palette *, u_int, struct hyperlinks *);
static int tty_check_overlay(struct tty *, u_int, u_int);
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, defaults, 8) &&
c->overlay_check == NULL) {
- tty_default_attributes(tty, defaults, palette, 8);
+ tty_default_attributes(tty, defaults, palette, 8,
+ s->hyperlinks);
tty_cursor(tty, nx - 1, aty);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
+ gcp->link != last.link ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux,
ux += gcp->data.width;
} else if (hidden != 0 || ux + gcp->data.width > nx) {
if (~gcp->flags & GRID_FLAG_PADDING) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
if (r.nx[j] == 0)
continue;
}
}
} else if (gcp->attr & GRID_ATTR_CHARSET) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
}
}
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux, width,
if (!cleared && ux < nx) {
log_debug("%s: %u to end of line (%zu cleared)", __func__,
nx - ux, len);
- tty_default_attributes(tty, defaults, palette, 8);
+ tty_default_attributes(tty, defaults, palette, 8,
+ s->hyperlinks);
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
}
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
void
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
}
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
void
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
}
{
u_int nx = ctx->sx - ctx->ocx;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
}
void
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
}
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
return;
}
- tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette);
+ tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
- tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette);
+ tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
+ ctx->s->hyperlinks);
}
void
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
- tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette);
+ tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
/* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox;
void
tty_cell(struct tty *tty, const struct grid_cell *gc,
- const struct grid_cell *defaults, struct colour_palette *palette)
+ const struct grid_cell *defaults, struct colour_palette *palette,
+ struct hyperlinks *hl)
{
const struct grid_cell *gcp;
/* Check the output codeset and apply attributes. */
gcp = tty_check_codeset(tty, gc);
- tty_attributes(tty, gcp, defaults, palette);
+ tty_attributes(tty, gcp, defaults, palette, hl);
/* If it is a single character, write with putc to handle ACS. */
if (gcp->data.size == 1) {
- tty_attributes(tty, gcp, defaults, palette);
+ tty_attributes(tty, gcp, defaults, palette, hl);
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return;
tty_putc(tty, *gcp->data.data);
struct grid_cell *gc = &tty->cell;
if (!grid_cells_equal(gc, &grid_default_cell)) {
+ if (gc->link != 0)
+ tty_putcode_ptr2(tty, TTYC_HLS, "", "");
if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0);
tty->cy = cy;
}
+static void
+tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
+ struct hyperlinks *hl)
+{
+ const char *uri, *id;
+
+ if (gc->link == tty->cell.link)
+ return;
+ tty->cell.link = gc->link;
+
+ if (hl == NULL)
+ return;
+
+ if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, &id))
+ tty_putcode_ptr2(tty, TTYC_HLS, "", "");
+ else
+ tty_putcode_ptr2(tty, TTYC_HLS, id, uri);
+}
+
void
tty_attributes(struct tty *tty, const struct grid_cell *gc,
- const struct grid_cell *defaults, struct colour_palette *palette)
+ const struct grid_cell *defaults, struct colour_palette *palette,
+ struct hyperlinks *hl)
{
struct grid_cell *tc = &tty->cell, gc2;
int changed;
if (gc2.attr == tty->last_cell.attr &&
gc2.fg == tty->last_cell.fg &&
gc2.bg == tty->last_cell.bg &&
- gc2.us == tty->last_cell.us)
+ gc2.us == tty->last_cell.us &&
+ gc2.link == tty->last_cell.link)
return;
/*
if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_SMACS);
+ /* Set hyperlink if any. */
+ tty_hyperlink(tty, gc, hl);
+
memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
}
static void
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
- struct colour_palette *palette, u_int bg)
+ struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{
struct grid_cell gc;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = bg;
- tty_attributes(tty, &gc, defaults, palette);
+ tty_attributes(tty, &gc, defaults, palette, hl);
}
static void