mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-26 06:46:45 -04:00
vt: underline colors and style from kitty
This commit is contained in:
parent
642f9910c2
commit
2e6bc3c61a
5 changed files with 202 additions and 3 deletions
46
csi.c
46
csi.c
|
|
@ -88,17 +88,44 @@ csi_sgr(struct terminal *term)
|
||||||
case 1: term->vt.attrs.bold = true; break;
|
case 1: term->vt.attrs.bold = true; break;
|
||||||
case 2: term->vt.attrs.dim = true; break;
|
case 2: term->vt.attrs.dim = true; break;
|
||||||
case 3: term->vt.attrs.italic = 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 5: term->vt.attrs.blink = true; break;
|
||||||
case 6: LOG_WARN("ignored: rapid blink"); break;
|
case 6: LOG_WARN("ignored: rapid blink"); break;
|
||||||
case 7: term->vt.attrs.reverse = true; break;
|
case 7: term->vt.attrs.reverse = true; break;
|
||||||
case 8: term->vt.attrs.conceal = true; break;
|
case 8: term->vt.attrs.conceal = true; break;
|
||||||
case 9: term->vt.attrs.strikethrough = 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 */
|
case 21: break; /* double-underline, not implemented */
|
||||||
|
#endif
|
||||||
case 22: term->vt.attrs.bold = term->vt.attrs.dim = false; break;
|
case 22: term->vt.attrs.bold = term->vt.attrs.dim = false; break;
|
||||||
case 23: term->vt.attrs.italic = 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;
|
case 24: term->vt.attrs.underline = false; break;
|
||||||
|
#endif
|
||||||
case 25: term->vt.attrs.blink = false; break;
|
case 25: term->vt.attrs.blink = false; break;
|
||||||
case 26: break; /* rapid blink, ignored */
|
case 26: break; /* rapid blink, ignored */
|
||||||
case 27: term->vt.attrs.reverse = false; break;
|
case 27: term->vt.attrs.reverse = false; break;
|
||||||
|
|
@ -119,6 +146,9 @@ csi_sgr(struct terminal *term)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 38:
|
case 38:
|
||||||
|
#if FOOT_EXT_UNDERLINE
|
||||||
|
case 58:
|
||||||
|
#endif
|
||||||
case 48: {
|
case 48: {
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
enum color_source src;
|
enum color_source src;
|
||||||
|
|
@ -197,18 +227,30 @@ csi_sgr(struct terminal *term)
|
||||||
if (param == 38) {
|
if (param == 38) {
|
||||||
term->vt.attrs.fg_src = src;
|
term->vt.attrs.fg_src = src;
|
||||||
term->vt.attrs.fg = color;
|
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 {
|
} else {
|
||||||
xassert(param == 48);
|
xassert(param == 48);
|
||||||
term->vt.attrs.bg_src = src;
|
term->vt.attrs.bg_src = src;
|
||||||
term->vt.attrs.bg = color;
|
term->vt.attrs.bg = color;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 39:
|
case 39:
|
||||||
term->vt.attrs.fg_src = COLOR_DEFAULT;
|
term->vt.attrs.fg_src = COLOR_DEFAULT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if FOOT_EXT_UNDERLINE
|
||||||
|
case 59:
|
||||||
|
term->vt.attrs.ul_src = COLOR_DEFAULT;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Regular background colors */
|
/* Regular background colors */
|
||||||
case 40:
|
case 40:
|
||||||
case 41:
|
case 41:
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,10 @@ add_project_arguments(
|
||||||
cc.get_supported_arguments(
|
cc.get_supported_arguments(
|
||||||
['-pedantic',
|
['-pedantic',
|
||||||
'-fstrict-aliasing',
|
'-fstrict-aliasing',
|
||||||
'-Wstrict-aliasing']),
|
'-Wstrict-aliasing']) +
|
||||||
|
(get_option('ext-underline')
|
||||||
|
? ['-DFOOT_EXT_UNDERLINE=1']
|
||||||
|
: ['-DFOOT_EXT_UNDERLINE=0']),
|
||||||
language: 'c',
|
language: 'c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)')
|
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',
|
option('utmp-default-helper-path', type: 'string', value: 'auto',
|
||||||
description: 'Default path to the utmp helper binary. Default: auto-detect')
|
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
125
render.c
|
|
@ -384,6 +384,99 @@ draw_underline(const struct terminal *term, pixman_image_t *pix,
|
||||||
x, y + y_ofs, cols * term->cell_width, thickness});
|
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
|
static void
|
||||||
draw_strikeout(const struct terminal *term, pixman_image_t *pix,
|
draw_strikeout(const struct terminal *term, pixman_image_t *pix,
|
||||||
const struct fcft_font *font,
|
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 _fg = 0;
|
||||||
uint32_t _bg = 0;
|
uint32_t _bg = 0;
|
||||||
|
#if FOOT_EXT_UNDERLINE
|
||||||
|
uint32_t _ul = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint16_t alpha = 0xffff;
|
uint16_t alpha = 0xffff;
|
||||||
|
|
||||||
if (is_selected && term->colors.use_custom_selection) {
|
if (is_selected && term->colors.use_custom_selection) {
|
||||||
_fg = term->colors.selection_fg;
|
_fg = term->colors.selection_fg;
|
||||||
_bg = term->colors.selection_bg;
|
_bg = term->colors.selection_bg;
|
||||||
|
#if FOOT_EXT_UNDERLINE
|
||||||
|
_ul = _fg;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* Use cell specific color, if set, otherwise the default colors (possible reversed) */
|
/* Use cell specific color, if set, otherwise the default colors (possible reversed) */
|
||||||
switch (cell->attrs.fg_src) {
|
switch (cell->attrs.fg_src) {
|
||||||
|
|
@ -518,6 +617,24 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
|
||||||
break;
|
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) {
|
if (cell->attrs.reverse ^ is_selected) {
|
||||||
uint32_t swap = _fg;
|
uint32_t swap = _fg;
|
||||||
_fg = _bg;
|
_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 fg = color_hex_to_pixman(_fg);
|
||||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
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);
|
struct fcft_font *font = attrs_to_font(term, &cell->attrs);
|
||||||
const struct composed *composed = NULL;
|
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);
|
pixman_image_unref(clr_pix);
|
||||||
|
|
||||||
/* Underline */
|
/* 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)
|
if (cell->attrs.underline)
|
||||||
draw_underline(term, pix, font, &fg, x, y, cell_cols);
|
draw_underline(term, pix, font, &fg, x, y, cell_cols);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (cell->attrs.strikethrough)
|
if (cell->attrs.strikethrough)
|
||||||
draw_strikeout(term, pix, font, &fg, x, y, cell_cols);
|
draw_strikeout(term, pix, font, &fg, x, y, cell_cols);
|
||||||
|
|
|
||||||
26
terminal.h
26
terminal.h
|
|
@ -31,6 +31,17 @@ enum color_source {
|
||||||
COLOR_RGB,
|
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
|
* Note: we want the cells to be as small as possible. Larger cells
|
||||||
* means fewer scrollback lines (or performance drops due to cache
|
* means fewer scrollback lines (or performance drops due to cache
|
||||||
|
|
@ -56,8 +67,19 @@ struct attributes {
|
||||||
bool selected:1;
|
bool selected:1;
|
||||||
bool url:1;
|
bool url:1;
|
||||||
uint32_t bg:24;
|
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");
|
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Last valid Unicode code point is 0x0010FFFFul */
|
/* Last valid Unicode code point is 0x0010FFFFul */
|
||||||
#define CELL_COMB_CHARS_LO 0x00200000ul
|
#define CELL_COMB_CHARS_LO 0x00200000ul
|
||||||
|
|
@ -68,7 +90,11 @@ struct cell {
|
||||||
char32_t wc;
|
char32_t wc;
|
||||||
struct attributes attrs;
|
struct attributes attrs;
|
||||||
};
|
};
|
||||||
|
#if FOOT_EXT_UNDERLINE
|
||||||
|
static_assert(sizeof(struct cell) == 16, "bad size");
|
||||||
|
#else
|
||||||
static_assert(sizeof(struct cell) == 12, "bad size");
|
static_assert(sizeof(struct cell) == 12, "bad size");
|
||||||
|
#endif
|
||||||
|
|
||||||
struct scroll_region {
|
struct scroll_region {
|
||||||
int start;
|
int start;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue