vt: underline colors and style from kitty

This commit is contained in:
kraftwerk28 2022-06-22 01:54:37 +03:00
parent 642f9910c2
commit 2e6bc3c61a
5 changed files with 202 additions and 3 deletions

46
csi.c
View file

@ -88,17 +88,44 @@ csi_sgr(struct terminal *term)
case 1: term->vt.attrs.bold = true; break;
case 2: term->vt.attrs.dim = true; break;
case 3: term->vt.attrs.italic = true; break;
case 4: term->vt.attrs.underline = true; break;
case 4: {
#if FOOT_EXT_UNDERLINE
const struct vt_subparams p = term->vt.params.v[i].sub;
struct attributes *attr = &term->vt.attrs;
if (p.idx == 0) {
attr->ul_style = UNDERLINE_STRAIGHT;
break;
}
switch (p.value[0]) {
case 0: attr->ul_style = UNDERLINE_NONE; break;
case 1: attr->ul_style = UNDERLINE_STRAIGHT; break;
case 2: attr->ul_style = UNDERLINE_DOUBLE; break;
case 3: attr->ul_style = UNDERLINE_CURLY; break;
case 4: attr->ul_style = UNDERLINE_DOTTED; break;
case 5: attr->ul_style = UNDERLINE_DASHED; break;
}
#else
term->vt.attrs.underline = true;
#endif
break;
}
case 5: term->vt.attrs.blink = true; break;
case 6: LOG_WARN("ignored: rapid blink"); break;
case 7: term->vt.attrs.reverse = true; break;
case 8: term->vt.attrs.conceal = true; break;
case 9: term->vt.attrs.strikethrough = true; break;
#if FOOT_EXT_UNDERLINE
case 21: term->vt.attrs.ul_style = UNDERLINE_DOUBLE; break;
#else
case 21: break; /* double-underline, not implemented */
#endif
case 22: term->vt.attrs.bold = term->vt.attrs.dim = false; break;
case 23: term->vt.attrs.italic = false; break;
#if FOOT_EXT_UNDERLINE
case 24: term->vt.attrs.ul_style = UNDERLINE_NONE; break;
#else
case 24: term->vt.attrs.underline = false; break;
#endif
case 25: term->vt.attrs.blink = false; break;
case 26: break; /* rapid blink, ignored */
case 27: term->vt.attrs.reverse = false; break;
@ -119,6 +146,9 @@ csi_sgr(struct terminal *term)
break;
case 38:
#if FOOT_EXT_UNDERLINE
case 58:
#endif
case 48: {
uint32_t color;
enum color_source src;
@ -197,18 +227,30 @@ csi_sgr(struct terminal *term)
if (param == 38) {
term->vt.attrs.fg_src = src;
term->vt.attrs.fg = color;
#if FOOT_EXT_UNDERLINE
} else if (param == 58) {
term->vt.attrs.ul_src = src;
term->vt.attrs.ul = color;
#endif
} else {
xassert(param == 48);
term->vt.attrs.bg_src = src;
term->vt.attrs.bg = color;
}
break;
}
case 39:
term->vt.attrs.fg_src = COLOR_DEFAULT;
break;
#if FOOT_EXT_UNDERLINE
case 59:
term->vt.attrs.ul_src = COLOR_DEFAULT;
break;
#endif
/* Regular background colors */
case 40:
case 41:

View file

@ -76,7 +76,10 @@ add_project_arguments(
cc.get_supported_arguments(
['-pedantic',
'-fstrict-aliasing',
'-Wstrict-aliasing']),
'-Wstrict-aliasing']) +
(get_option('ext-underline')
? ['-DFOOT_EXT_UNDERLINE=1']
: ['-DFOOT_EXT_UNDERLINE=0']),
language: 'c',
)

View file

@ -27,3 +27,6 @@ option('utmp-backend', type: 'combo', value: 'auto', choices: ['none', 'libutemp
description: 'Which utmp logging backend to use. This affects how (with what arguments) the utmp helper binary (see \'utmp-default-helper-path\')is called. Default: auto (linux=libutempter, freebsd=ulog, others=none)')
option('utmp-default-helper-path', type: 'string', value: 'auto',
description: 'Default path to the utmp helper binary. Default: auto-detect')
option('ext-underline', type: 'boolean', value: true,
description: 'Enable underline styles & colors from xterm-kitty')

125
render.c
View file

@ -384,6 +384,99 @@ draw_underline(const struct terminal *term, pixman_image_t *pix,
x, y + y_ofs, cols * term->cell_width, thickness});
}
#if FOOT_EXT_UNDERLINE
static void
draw_ext_underline(const struct terminal *term, pixman_image_t *pix,
const struct fcft_font *font,
const pixman_color_t *color,
const enum underline_style style,
int x, int y, int cols)
{
if (style == UNDERLINE_NONE)
return;
const int thickness = font->underline.thickness;
int y_ofs;
/* Make sure the line isn't positioned below the cell */
switch (style) {
case UNDERLINE_DOUBLE:
y_ofs = min(underline_offset(term, font),
term->cell_height - thickness * 3);
break;
default:
y_ofs = min(underline_offset(term, font),
term->cell_height - thickness);
break;
}
const int ceil_w = cols * term->cell_width;
switch (style) {
case UNDERLINE_DOUBLE: {
const pixman_rectangle16_t rects[] = {
{x, y + y_ofs, ceil_w, thickness},
{x, y + y_ofs + thickness * 2, ceil_w, thickness}};
pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, color, 2, rects);
break;
}
case UNDERLINE_DASHED: {
const int ceil_w = cols * term->cell_width;
const int dash_w = ceil_w / 3 + (ceil_w % 3 > 0);
const pixman_rectangle16_t rects[] = {
{x, y + y_ofs, dash_w, thickness},
{x + dash_w * 2, y + y_ofs, dash_w, thickness},
};
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, pix, color, 2, rects);
break;
}
case UNDERLINE_DOTTED: {
const int ceil_w = cols * term->cell_width;
const int nrects = min(ceil_w / thickness / 2, 16);
pixman_rectangle16_t rects[16] = {0};
for (int i = 0; i < nrects; i++) {
rects[i] = (pixman_rectangle16_t){
x + i * thickness * 2, y + y_ofs, thickness, thickness};
}
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, pix, color, nrects, rects);
break;
}
case UNDERLINE_CURLY: {
#define I(x) pixman_int_to_fixed(x)
const int top = y + y_ofs;
const int bot = top + thickness * 3;
const int th = thickness;
const int half_x = x + ceil_w / 2, full_x = x + ceil_w;
const pixman_trapezoid_t traps[] = {
{
I(top), I(bot),
{{I(x), I(bot - th)}, {I(half_x), I(top - th)}},
{{I(x), I(bot + th)}, {I(half_x), I(top + th)}},
},
{
I(top), I(bot),
{{I(half_x), I(top + th)}, {I(full_x), I(bot + th)}},
{{I(half_x), I(top - th)}, {I(full_x), I(bot - th)}},
}};
// TODO: reuse the fill across all cells
pixman_image_t *fill = pixman_image_create_solid_fill(color);
pixman_composite_trapezoids(
PIXMAN_OP_OVER, fill, pix, PIXMAN_a8, 0, 0, 0, 0, 2, traps);
pixman_image_unref(fill);
break;
}
default: {
const pixman_rectangle16_t rects[] = {
{x, y + y_ofs, ceil_w, thickness}};
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, pix, color, 1, rects);
break;
}
}
}
#endif
static void
draw_strikeout(const struct terminal *term, pixman_image_t *pix,
const struct fcft_font *font,
@ -478,12 +571,18 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
uint32_t _fg = 0;
uint32_t _bg = 0;
#if FOOT_EXT_UNDERLINE
uint32_t _ul = 0;
#endif
uint16_t alpha = 0xffff;
if (is_selected && term->colors.use_custom_selection) {
_fg = term->colors.selection_fg;
_bg = term->colors.selection_bg;
#if FOOT_EXT_UNDERLINE
_ul = _fg;
#endif
} else {
/* Use cell specific color, if set, otherwise the default colors (possible reversed) */
switch (cell->attrs.fg_src) {
@ -518,6 +617,24 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
break;
}
#if FOOT_EXT_UNDERLINE
switch (cell->attrs.ul_src) {
case COLOR_RGB:
_ul = cell->attrs.ul;
break;
case COLOR_BASE16:
case COLOR_BASE256:
xassert(cell->attrs.ul < ALEN(term->colors.table));
_ul = term->colors.table[cell->attrs.bg];
break;
case COLOR_DEFAULT:
_ul = _fg;
break;
}
#endif
if (cell->attrs.reverse ^ is_selected) {
uint32_t swap = _fg;
_fg = _bg;
@ -581,6 +698,9 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
pixman_color_t fg = color_hex_to_pixman(_fg);
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
#if FOOT_EXT_UNDERLINE
pixman_color_t ul = color_hex_to_pixman(_ul);
#endif
struct fcft_font *font = attrs_to_font(term, &cell->attrs);
const struct composed *composed = NULL;
@ -834,8 +954,13 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
pixman_image_unref(clr_pix);
/* Underline */
#if FOOT_EXT_UNDERLINE
draw_ext_underline(term, pix, font, &ul, cell->attrs.ul_style,
x, y, cell_cols);
#else
if (cell->attrs.underline)
draw_underline(term, pix, font, &fg, x, y, cell_cols);
#endif
if (cell->attrs.strikethrough)
draw_strikeout(term, pix, font, &fg, x, y, cell_cols);

View file

@ -31,6 +31,17 @@ enum color_source {
COLOR_RGB,
};
#if FOOT_EXT_UNDERLINE
enum underline_style {
UNDERLINE_NONE,
UNDERLINE_STRAIGHT,
UNDERLINE_CURLY,
UNDERLINE_DOUBLE,
UNDERLINE_DASHED,
UNDERLINE_DOTTED,
};
#endif
/*
* Note: we want the cells to be as small as possible. Larger cells
* means fewer scrollback lines (or performance drops due to cache
@ -56,8 +67,19 @@ struct attributes {
bool selected:1;
bool url:1;
uint32_t bg:24;
#if FOOT_EXT_UNDERLINE
enum underline_style ul_style:4;
enum color_source ul_src:4;
uint32_t ul:24;
#endif
};
#if FOOT_EXT_UNDERLINE
static_assert(sizeof(struct attributes) == 12, "VT attribute struct too large");
#else
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
#endif
/* Last valid Unicode code point is 0x0010FFFFul */
#define CELL_COMB_CHARS_LO 0x00200000ul
@ -68,7 +90,11 @@ struct cell {
char32_t wc;
struct attributes attrs;
};
#if FOOT_EXT_UNDERLINE
static_assert(sizeof(struct cell) == 16, "bad size");
#else
static_assert(sizeof(struct cell) == 12, "bad size");
#endif
struct scroll_region {
int start;