Support for extended underline styles, enabled by adding the Smulx

capability with terminal-overrides (add something like
'vte*:Smulx=\E[4\:%p1%dm'). GitHub issue 1492.
This commit is contained in:
Nicholas Marriott 2018-10-08 13:21:37 +01:00
parent 46847e9b2e
commit 4b9e76aaaa
7 changed files with 159 additions and 43 deletions

View File

@ -25,13 +25,13 @@
const char * const char *
attributes_tostring(int attr) attributes_tostring(int attr)
{ {
static char buf[128]; static char buf[512];
size_t len; size_t len;
if (attr == 0) if (attr == 0)
return ("none"); return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s", len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_DIM) ? "dim," : "",
(attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "",
@ -39,7 +39,11 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_REVERSE) ? "reverse," : "", (attr & GRID_ATTR_REVERSE) ? "reverse," : "",
(attr & GRID_ATTR_HIDDEN) ? "hidden," : "", (attr & GRID_ATTR_HIDDEN) ? "hidden," : "",
(attr & GRID_ATTR_ITALICS) ? "italics," : "", (attr & GRID_ATTR_ITALICS) ? "italics," : "",
(attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : ""); (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "",
(attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "");
if (len > 0) if (len > 0)
buf[len - 1] = '\0'; buf[len - 1] = '\0';
@ -52,6 +56,25 @@ attributes_fromstring(const char *str)
const char delimiters[] = " ,|"; const char delimiters[] = " ,|";
int attr; int attr;
size_t end; size_t end;
u_int i;
struct {
const char* name;
int attr;
} table[] = {
{ "bright", GRID_ATTR_BRIGHT },
{ "bold", GRID_ATTR_BRIGHT },
{ "dim", GRID_ATTR_DIM },
{ "underscore", GRID_ATTR_UNDERSCORE },
{ "blink", GRID_ATTR_BLINK },
{ "reverse", GRID_ATTR_REVERSE },
{ "hidden", GRID_ATTR_HIDDEN },
{ "italics", GRID_ATTR_ITALICS },
{ "strikethrough", GRID_ATTR_STRIKETHROUGH },
{ "double-underscore", GRID_ATTR_UNDERSCORE_2 },
{ "curly-underscore", GRID_ATTR_UNDERSCORE_3 },
{ "dotted-underscore", GRID_ATTR_UNDERSCORE_4 },
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 }
};
if (*str == '\0' || strcspn(str, delimiters) == 0) if (*str == '\0' || strcspn(str, delimiters) == 0)
return (-1); return (-1);
@ -64,24 +87,15 @@ attributes_fromstring(const char *str)
attr = 0; attr = 0;
do { do {
end = strcspn(str, delimiters); end = strcspn(str, delimiters);
if ((end == 6 && strncasecmp(str, "bright", end) == 0) || for (i = 0; i < nitems(table); i++) {
(end == 4 && strncasecmp(str, "bold", end) == 0)) if (end != strlen(table[i].name))
attr |= GRID_ATTR_BRIGHT; continue;
else if (end == 3 && strncasecmp(str, "dim", end) == 0) if (strncasecmp(str, table[i].name, end) == 0) {
attr |= GRID_ATTR_DIM; attr |= table[i].attr;
else if (end == 10 && strncasecmp(str, "underscore", end) == 0) break;
attr |= GRID_ATTR_UNDERSCORE; }
else if (end == 5 && strncasecmp(str, "blink", end) == 0) }
attr |= GRID_ATTR_BLINK; if (i == nitems(table))
else if (end == 7 && strncasecmp(str, "reverse", end) == 0)
attr |= GRID_ATTR_REVERSE;
else if (end == 6 && strncasecmp(str, "hidden", end) == 0)
attr |= GRID_ATTR_HIDDEN;
else if (end == 7 && strncasecmp(str, "italics", end) == 0)
attr |= GRID_ATTR_ITALICS;
else if (end == 13 && strncasecmp(str, "strikethrough", end) == 0)
attr |= GRID_ATTR_STRIKETHROUGH;
else
return (-1); return (-1);
str += end + strspn(str + end, delimiters); str += end + strspn(str + end, delimiters);
} while (*str != '\0'); } while (*str != '\0');

16
grid.c
View File

@ -764,7 +764,11 @@ grid_string_cells_code(const struct grid_cell *lastgc,
{ GRID_ATTR_BLINK, 5 }, { GRID_ATTR_BLINK, 5 },
{ GRID_ATTR_REVERSE, 7 }, { GRID_ATTR_REVERSE, 7 },
{ GRID_ATTR_HIDDEN, 8 }, { GRID_ATTR_HIDDEN, 8 },
{ GRID_ATTR_STRIKETHROUGH, 9 } { GRID_ATTR_STRIKETHROUGH, 9 },
{ GRID_ATTR_UNDERSCORE_2, 42 },
{ GRID_ATTR_UNDERSCORE_3, 43 },
{ GRID_ATTR_UNDERSCORE_4, 44 },
{ GRID_ATTR_UNDERSCORE_5, 45 },
}; };
n = 0; n = 0;
@ -790,11 +794,15 @@ grid_string_cells_code(const struct grid_cell *lastgc,
else else
strlcat(buf, "\033[", len); strlcat(buf, "\033[", len);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (i + 1 < n) if (s[i] < 10)
xsnprintf(tmp, sizeof tmp, "%d;", s[i]);
else
xsnprintf(tmp, sizeof tmp, "%d", s[i]); xsnprintf(tmp, sizeof tmp, "%d", s[i]);
else {
xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
s[i] % 10);
}
strlcat(buf, tmp, len); strlcat(buf, tmp, len);
if (i + 1 < n)
strlcat(buf, ";", len);
} }
strlcat(buf, "m", len); strlcat(buf, "m", len);
} }

46
input.c
View File

@ -1835,10 +1835,11 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i)
static void static void
input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
{ {
char *s = ictx->param_list[i].str, *copy, *ptr, *out; struct grid_cell *gc = &ictx->cell.cell;
int p[8]; char *s = ictx->param_list[i].str, *copy, *ptr, *out;
u_int n; int p[8];
const char *errstr; u_int n;
const char *errstr;
for (n = 0; n < nitems(p); n++) for (n = 0; n < nitems(p); n++)
p[n] = -1; p[n] = -1;
@ -1857,7 +1858,39 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
} }
free(copy); free(copy);
if (n == 0 || (p[0] != 38 && p[0] != 48)) if (n == 0)
return;
if (p[0] == 4) {
if (n != 2)
return;
switch (p[1]) {
case 0:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
break;
case 1:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE;
break;
case 2:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_2;
break;
case 3:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_3;
break;
case 4:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_4;
break;
case 5:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_5;
break;
}
return;
}
if (p[0] != 38 && p[0] != 48)
return; return;
if (p[1] == -1) if (p[1] == -1)
i = 2; i = 2;
@ -1927,6 +1960,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr |= GRID_ATTR_ITALICS; gc->attr |= GRID_ATTR_ITALICS;
break; break;
case 4: case 4:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE;
break; break;
case 5: case 5:
@ -1948,7 +1982,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr &= ~GRID_ATTR_ITALICS; gc->attr &= ~GRID_ATTR_ITALICS;
break; break;
case 24: case 24:
gc->attr &= ~GRID_ATTR_UNDERSCORE; gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
break; break;
case 25: case 25:
gc->attr &= ~GRID_ATTR_BLINK; gc->attr &= ~GRID_ATTR_BLINK;

11
tmux.1
View File

@ -2857,8 +2857,12 @@ or a comma-delimited list of one or more of:
.Ic reverse , .Ic reverse ,
.Ic hidden , .Ic hidden ,
.Ic italics , .Ic italics ,
.Ic strikethrough ,
.Ic double-underscore
.Ic curly-underscore
.Ic dotted-underscore
or or
.Ic strikethrough .Ic dashed-underscore
to turn an attribute on, or an attribute prefixed with to turn an attribute on, or an attribute prefixed with
.Ql no .Ql no
to turn one off. to turn one off.
@ -4525,6 +4529,11 @@ to change the cursor colour from inside
.Bd -literal -offset indent .Bd -literal -offset indent
$ printf '\e033]12;red\e033\e\e' $ printf '\e033]12;red\e033\e\e'
.Ed .Ed
.It Em \&Smulx
Set a styled underline.
The single parameter is one of: 0 for no underline, 1 for normal
underline, 2 for double underline, 3 for curly underline, 4 for dotted
underline and 5 for dashed underline.
.It Em \&Ss , Se .It Em \&Ss , Se
Set or reset the cursor style. Set or reset the cursor style.
If set, a sequence such as this may be used If set, a sequence such as this may be used

13
tmux.h
View File

@ -429,6 +429,7 @@ enum tty_code_code {
TTYC_SMCUP, TTYC_SMCUP,
TTYC_SMKX, TTYC_SMKX,
TTYC_SMSO, TTYC_SMSO,
TTYC_SMULX,
TTYC_SMUL, TTYC_SMUL,
TTYC_SMXX, TTYC_SMXX,
TTYC_SS, TTYC_SS,
@ -554,6 +555,18 @@ enum utf8_state {
#define GRID_ATTR_ITALICS 0x40 #define GRID_ATTR_ITALICS 0x40
#define GRID_ATTR_CHARSET 0x80 /* alternative character set */ #define GRID_ATTR_CHARSET 0x80 /* alternative character set */
#define GRID_ATTR_STRIKETHROUGH 0x100 #define GRID_ATTR_STRIKETHROUGH 0x100
#define GRID_ATTR_UNDERSCORE_2 0x200
#define GRID_ATTR_UNDERSCORE_3 0x400
#define GRID_ATTR_UNDERSCORE_4 0x800
#define GRID_ATTR_UNDERSCORE_5 0x1000
/* All underscore attributes. */
#define GRID_ATTR_ALL_UNDERSCORE \
(GRID_ATTR_UNDERSCORE| \
GRID_ATTR_UNDERSCORE_2| \
GRID_ATTR_UNDERSCORE_3| \
GRID_ATTR_UNDERSCORE_4| \
GRID_ATTR_UNDERSCORE_5)
/* Grid flags. */ /* Grid flags. */
#define GRID_FLAG_FG256 0x1 #define GRID_FLAG_FG256 0x1

View File

@ -256,6 +256,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" },
[TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, [TTYC_SMKX] = { TTYCODE_STRING, "smkx" },
[TTYC_SMSO] = { TTYCODE_STRING, "smso" }, [TTYC_SMSO] = { TTYCODE_STRING, "smso" },
[TTYC_SMULX] = { TTYCODE_STRING, "Smulx" },
[TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" },
[TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" },
[TTYC_SS] = { TTYCODE_STRING, "Ss" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" },
@ -302,25 +303,50 @@ tty_term_strip(const char *s)
return (xstrdup(buf)); return (xstrdup(buf));
} }
static char *
tty_term_override_next(const char *s, size_t *offset)
{
static char value[BUFSIZ];
size_t n = 0, at = *offset;
if (s[at] == '\0')
return (NULL);
while (s[at] != '\0' && s[at] != ':') {
if (s[at] == '\\' && s[at + 1] == ':') {
value[n++] = ':';
at += 2;
} else {
value[n++] = s[at];
at++;
}
if (n == (sizeof value) - 1)
return (NULL);
}
if (s[at] != '\0')
*offset = at + 1;
else
*offset = at;
value[n] = '\0';
return (value);
}
static void static void
tty_term_override(struct tty_term *term, const char *override) tty_term_override(struct tty_term *term, const char *override)
{ {
const struct tty_term_code_entry *ent; const struct tty_term_code_entry *ent;
struct tty_code *code; struct tty_code *code;
char *next, *s, *copy, *cp, *value; size_t offset = 0;
char *cp, *value, *s;
const char *errstr; const char *errstr;
u_int i; u_int i;
int n, remove; int n, remove;
copy = next = xstrdup(override); s = tty_term_override_next(override, &offset);
if (s == NULL || fnmatch(s, term->name, 0) != 0)
s = strsep(&next, ":");
if (s == NULL || next == NULL || fnmatch(s, term->name, 0) != 0) {
free(copy);
return; return;
}
while ((s = strsep(&next, ":")) != NULL) { while ((s = tty_term_override_next(override, &offset)) != NULL) {
if (*s == '\0') if (*s == '\0')
continue; continue;
value = NULL; value = NULL;
@ -341,6 +367,8 @@ tty_term_override(struct tty_term *term, const char *override)
if (remove) if (remove)
log_debug("%s override: %s@", term->name, s); log_debug("%s override: %s@", term->name, s);
else if (*value == '\0')
log_debug("%s override: %s", term->name, s);
else else
log_debug("%s override: %s=%s", term->name, s, value); log_debug("%s override: %s=%s", term->name, s, value);
@ -379,7 +407,6 @@ tty_term_override(struct tty_term *term, const char *override)
free(value); free(value);
} }
free(s);
} }
struct tty_term * struct tty_term *

15
tty.c
View File

@ -2163,8 +2163,19 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
tty_putcode(tty, TTYC_DIM); tty_putcode(tty, TTYC_DIM);
if (changed & GRID_ATTR_ITALICS) if (changed & GRID_ATTR_ITALICS)
tty_set_italics(tty); tty_set_italics(tty);
if (changed & GRID_ATTR_UNDERSCORE) if (changed & GRID_ATTR_ALL_UNDERSCORE) {
tty_putcode(tty, TTYC_SMUL); if ((changed & GRID_ATTR_UNDERSCORE) ||
!tty_term_has(tty->term, TTYC_SMULX))
tty_putcode(tty, TTYC_SMUL);
else if (changed & GRID_ATTR_UNDERSCORE_2)
tty_putcode1(tty, TTYC_SMULX, 2);
else if (changed & GRID_ATTR_UNDERSCORE_3)
tty_putcode1(tty, TTYC_SMULX, 3);
else if (changed & GRID_ATTR_UNDERSCORE_4)
tty_putcode1(tty, TTYC_SMULX, 4);
else if (changed & GRID_ATTR_UNDERSCORE_5)
tty_putcode1(tty, TTYC_SMULX, 5);
}
if (changed & GRID_ATTR_BLINK) if (changed & GRID_ATTR_BLINK)
tty_putcode(tty, TTYC_BLINK); tty_putcode(tty, TTYC_BLINK);
if (changed & GRID_ATTR_REVERSE) { if (changed & GRID_ATTR_REVERSE) {