Implement SGR dim
kitty now supports the SGR DIM escape code, which makes text fade into the background. It works by alpha blending the text color into the background color. Fixes #446
This commit is contained in:
parent
9cab8a2de5
commit
6f2d63eb87
@ -19,7 +19,7 @@ in float bg_alpha;
|
||||
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
uniform sampler2DArray sprites;
|
||||
uniform float inactive_text_alpha;
|
||||
in float effective_text_alpha;
|
||||
in vec3 sprite_pos;
|
||||
in vec3 underline_pos;
|
||||
in vec3 strike_pos;
|
||||
@ -61,7 +61,7 @@ vec4 calculate_foreground() {
|
||||
// Since strike and text are the same color, we simply add the alpha values
|
||||
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
|
||||
// Underline color might be different, so alpha blend
|
||||
return alpha_blend(decoration_fg, underline_alpha * inactive_text_alpha, fg, combined_alpha * inactive_text_alpha);
|
||||
return alpha_blend(decoration_fg, underline_alpha * effective_text_alpha, fg, combined_alpha * effective_text_alpha);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#version GLSL_VERSION
|
||||
#define WHICH_PROGRAM
|
||||
#define NOT_TRANSPARENT
|
||||
#define SHIFTS
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
@ -45,12 +46,14 @@ out float bg_alpha;
|
||||
#endif
|
||||
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
uniform float inactive_text_alpha;
|
||||
out vec3 sprite_pos;
|
||||
out vec3 underline_pos;
|
||||
out vec3 strike_pos;
|
||||
out vec3 foreground;
|
||||
out vec3 decoration_fg;
|
||||
out float colored_sprite;
|
||||
out float effective_text_alpha;
|
||||
#endif
|
||||
|
||||
|
||||
@ -63,9 +66,6 @@ const uint ONE = uint(1);
|
||||
const uint TWO = uint(2);
|
||||
const uint THREE = uint(3);
|
||||
const uint FOUR = uint(4);
|
||||
const uint DECORATION_MASK = uint(3);
|
||||
const uint STRIKE_MASK = uint(1);
|
||||
const uint REVERSE_MASK = uint(1);
|
||||
|
||||
vec3 color_to_vec(uint c) {
|
||||
uint r, g, b;
|
||||
@ -134,7 +134,7 @@ void main() {
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||
uint text_attrs = sprite_coords[3];
|
||||
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
|
||||
uint is_inverted = ((text_attrs >> REVERSE_SHIFT) & ONE) + inverted;
|
||||
int fg_index = fg_index_map[is_inverted];
|
||||
int bg_index = 1 - fg_index;
|
||||
float cursor = is_cursor(c, r);
|
||||
@ -151,13 +151,15 @@ void main() {
|
||||
// Foreground
|
||||
uint resolved_fg = resolve_color(colors[fg_index], default_colors[fg_index]);
|
||||
foreground = color_to_vec(resolved_fg);
|
||||
float has_dim = float((text_attrs >> DIM_SHIFT) & ONE);
|
||||
effective_text_alpha = inactive_text_alpha * mix(1.0, 0.75, has_dim);
|
||||
// Selection
|
||||
foreground = choose_color(float(is_selected & ONE), color_to_vec(highlight_fg), foreground);
|
||||
// Underline and strike through (rendered via sprites)
|
||||
float in_url = float((is_selected & TWO) >> 1);
|
||||
decoration_fg = choose_color(in_url, color_to_vec(url_color), to_color(colors[2], resolved_fg));
|
||||
underline_pos = choose_color(in_url, to_sprite_pos(pos, url_style, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> 2) & DECORATION_MASK, ZERO, ZERO));
|
||||
strike_pos = to_sprite_pos(pos, ((text_attrs >> 7) & STRIKE_MASK) * FOUR, ZERO, ZERO);
|
||||
underline_pos = choose_color(in_url, to_sprite_pos(pos, url_style, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> DECORATION_SHIFT) & THREE, ZERO, ZERO));
|
||||
strike_pos = to_sprite_pos(pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * FOUR, ZERO, ZERO);
|
||||
|
||||
// Cursor
|
||||
foreground = choose_color(cursor, bg, foreground);
|
||||
|
||||
@ -24,7 +24,7 @@ dealloc(Cursor* self) {
|
||||
|
||||
#define EQ(x) (a->x == b->x)
|
||||
static int __eq__(Cursor *a, Cursor *b) {
|
||||
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(blink);
|
||||
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(dim) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(blink);
|
||||
}
|
||||
|
||||
static const char* cursor_names[NUM_OF_CURSOR_SHAPES] = { "NO_SHAPE", "BLOCK", "BEAM", "UNDERLINE" };
|
||||
@ -33,16 +33,16 @@ static const char* cursor_names[NUM_OF_CURSOR_SHAPES] = { "NO_SHAPE", "BLOCK", "
|
||||
static PyObject *
|
||||
repr(Cursor *self) {
|
||||
return PyUnicode_FromFormat(
|
||||
"Cursor(x=%u, y=%u, shape=%s, blink=%R, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, decoration=%d, decoration_fg=#%08x)",
|
||||
"Cursor(x=%u, y=%u, shape=%s, blink=%R, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, dim=%R, decoration=%d, decoration_fg=#%08x)",
|
||||
self->x, self->y, (self->shape < NUM_OF_CURSOR_SHAPES ? cursor_names[self->shape] : "INVALID"),
|
||||
BOOL(self->blink), self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), self->decoration, self->decoration_fg
|
||||
BOOL(self->blink), self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), BOOL(self->dim), self->decoration, self->decoration_fg
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
cursor_reset_display_attrs(Cursor *self) {
|
||||
self->bg = 0; self->fg = 0; self->decoration_fg = 0;
|
||||
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false;
|
||||
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false; self->dim = false;
|
||||
}
|
||||
|
||||
|
||||
@ -85,6 +85,8 @@ START_ALLOW_CASE_RANGE
|
||||
cursor_reset_display_attrs(self); break;
|
||||
case 1:
|
||||
self->bold = true; break;
|
||||
case 2:
|
||||
self->dim = true; break;
|
||||
case 3:
|
||||
self->italic = true; break;
|
||||
case 4:
|
||||
@ -98,7 +100,7 @@ START_ALLOW_CASE_RANGE
|
||||
case 21:
|
||||
self->decoration = 2; break;
|
||||
case 22:
|
||||
self->bold = false; break;
|
||||
self->bold = false; self->dim = false; break;
|
||||
case 23:
|
||||
self->italic = false; break;
|
||||
case 24:
|
||||
@ -138,6 +140,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
|
||||
#define RANGE for(unsigned c = 0; c < cell_count; c++, cell++)
|
||||
#define SET(shift) RANGE { cell->attrs |= (1 << shift); } break;
|
||||
#define RESET(shift) RANGE { cell->attrs &= ~(1 << shift); } break;
|
||||
#define RESET2(shift1, shift2) RANGE { cell->attrs &= ~((1 << shift1) | (1 << shift2)); } break;
|
||||
#define SETM(val, mask, shift) { RANGE { cell->attrs &= ~(mask << shift); cell->attrs |= ((val) << shift); } break; }
|
||||
#define SET_COLOR(which) { color_type color = 0; parse_color(params, &i, count, &color); if (color) { RANGE { cell->which = color; }} } break;
|
||||
#define SIMPLE(which, val) RANGE { cell->which = (val); } break;
|
||||
@ -153,6 +156,8 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
|
||||
break;
|
||||
case 1:
|
||||
SET(BOLD_SHIFT);
|
||||
case 2:
|
||||
SET(DIM_SHIFT);
|
||||
case 3:
|
||||
SET(ITALIC_SHIFT);
|
||||
case 4:
|
||||
@ -165,7 +170,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
|
||||
case 21:
|
||||
SETM(2, DECORATION_MASK, DECORATION_SHIFT);
|
||||
case 22:
|
||||
RESET(BOLD_SHIFT);
|
||||
RESET2(DIM_SHIFT, BOLD_SHIFT);
|
||||
case 23:
|
||||
RESET(ITALIC_SHIFT);
|
||||
case 24:
|
||||
@ -199,6 +204,7 @@ END_ALLOW_CASE_RANGE
|
||||
}
|
||||
}
|
||||
#undef RESET
|
||||
#undef RESET2
|
||||
#undef SET_COLOR
|
||||
#undef SET
|
||||
#undef SETM
|
||||
@ -237,7 +243,11 @@ cursor_as_sgr(Cursor *self, Cursor *prev) {
|
||||
#define SZ sizeof(buf) - (p - buf) - 2
|
||||
#define P(fmt, ...) { p += snprintf(p, SZ, fmt ";", __VA_ARGS__); }
|
||||
char *p = buf;
|
||||
if (self->bold != prev->bold) P("%d", self->bold ? 1 : 22);
|
||||
bool intensity_differs = self->bold != prev->bold || self->dim != prev->dim;
|
||||
if (intensity_differs) {
|
||||
if (!self->bold || !self->dim) { P("%d", 22); }
|
||||
else P("%d;%d", 1, 2);
|
||||
}
|
||||
if (self->italic != prev->italic) P("%d", self->italic ? 3 : 23);
|
||||
if (self->reverse != prev->reverse) P("%d", self->reverse ? 7 : 27);
|
||||
if (self->strikethrough != prev->strikethrough) P("%d", self->strikethrough ? 9 : 29);
|
||||
@ -268,7 +278,7 @@ void cursor_reset(Cursor *self) {
|
||||
void cursor_copy_to(Cursor *src, Cursor *dest) {
|
||||
#define CCY(x) dest->x = src->x;
|
||||
CCY(x); CCY(y); CCY(shape); CCY(blink);
|
||||
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
|
||||
CCY(bold); CCY(italic); CCY(strikethrough); CCY(dim); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@ -281,6 +291,7 @@ BOOL_GETSET(Cursor, bold)
|
||||
BOOL_GETSET(Cursor, italic)
|
||||
BOOL_GETSET(Cursor, reverse)
|
||||
BOOL_GETSET(Cursor, strikethrough)
|
||||
BOOL_GETSET(Cursor, dim)
|
||||
BOOL_GETSET(Cursor, blink)
|
||||
|
||||
static PyMemberDef members[] = {
|
||||
@ -299,6 +310,7 @@ static PyGetSetDef getseters[] = {
|
||||
GETSET(italic)
|
||||
GETSET(reverse)
|
||||
GETSET(strikethrough)
|
||||
GETSET(dim)
|
||||
GETSET(blink)
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@ -197,6 +197,7 @@ PyInit_fast_data_types(void) {
|
||||
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
|
||||
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);
|
||||
PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT);
|
||||
PyModule_AddIntConstant(m, "DIM", DIM_SHIFT);
|
||||
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
||||
PyModule_AddStringMacro(m, ERROR_PREFIX);
|
||||
#ifdef KITTY_VCS_REV
|
||||
|
||||
@ -58,6 +58,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
||||
#define BI_VAL(attrs) ((attrs >> 4) & 3)
|
||||
#define REVERSE_SHIFT 6
|
||||
#define STRIKE_SHIFT 7
|
||||
#define DIM_SHIFT 8
|
||||
#define COL_MASK 0xFFFFFFFF
|
||||
#define UTF8_ACCEPT 0
|
||||
#define UTF8_REJECT 1
|
||||
@ -71,11 +72,12 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
|
||||
|
||||
#define CURSOR_TO_ATTRS(c, w) \
|
||||
((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \
|
||||
((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT)))
|
||||
((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | \
|
||||
((c->strikethrough & 1) << STRIKE_SHIFT) | ((c->dim & 1) << DIM_SHIFT)))
|
||||
|
||||
#define ATTRS_TO_CURSOR(a, c) \
|
||||
(c)->decoration = (a >> DECORATION_SHIFT) & 3; (c)->bold = (a >> BOLD_SHIFT) & 1; (c)->italic = (a >> ITALIC_SHIFT) & 1; \
|
||||
(c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1;
|
||||
(c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1; (c)->dim = (a >> DIM_SHIFT) & 1;
|
||||
|
||||
#define COPY_CELL(src, s, dest, d) \
|
||||
(dest)->cells[d] = (src)->cells[s];
|
||||
@ -178,7 +180,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
bool bold, italic, reverse, strikethrough, blink;
|
||||
bool bold, italic, reverse, strikethrough, blink, dim;
|
||||
unsigned int x, y;
|
||||
uint8_t decoration;
|
||||
CursorShape shape;
|
||||
|
||||
@ -149,7 +149,7 @@ set_attribute(LineBuf *self, PyObject *args) {
|
||||
#define set_attribute_doc "set_attribute(which, val) -> Set the attribute on all cells in the line."
|
||||
unsigned int shift, val;
|
||||
if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL;
|
||||
if (shift < DECORATION_SHIFT || shift > STRIKE_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
|
||||
if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
|
||||
linebuf_set_attribute(self, shift, val);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -515,7 +515,7 @@ set_attribute(Line *self, PyObject *args) {
|
||||
#define set_attribute_doc "set_attribute(which, val) -> Set the attribute on all cells in the line."
|
||||
unsigned int shift, val;
|
||||
if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL;
|
||||
if (shift < DECORATION_SHIFT || shift > STRIKE_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
|
||||
if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
|
||||
set_attribute_on_line(self->cells, shift, val, self->xnum);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -15,9 +15,10 @@ from .constants import (
|
||||
)
|
||||
from .fast_data_types import (
|
||||
BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM,
|
||||
CELL_SPECIAL_PROGRAM, CSI, CURSOR_PROGRAM, DCS, GRAPHICS_PREMULT_PROGRAM,
|
||||
GRAPHICS_PROGRAM, OSC, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, Screen,
|
||||
add_window, compile_program, get_clipboard_string, glfw_post_empty_event,
|
||||
CELL_SPECIAL_PROGRAM, CSI, CURSOR_PROGRAM, DCS, DECORATION, DIM,
|
||||
GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL,
|
||||
SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, Screen, add_window,
|
||||
compile_program, get_clipboard_string, glfw_post_empty_event,
|
||||
init_cell_program, init_cursor_program, set_clipboard_string,
|
||||
set_titlebar_color, set_window_render_data, update_window_title,
|
||||
update_window_visibility, viewport_for_window
|
||||
@ -64,6 +65,10 @@ def load_shader_programs(semi_transparent=0):
|
||||
'FOREGROUND': CELL_FG_PROGRAM,
|
||||
}.items():
|
||||
vv, ff = v.replace('WHICH_PROGRAM', which), f.replace('WHICH_PROGRAM', which)
|
||||
shifts = '\n'.join('#define {} {}'.format(name, val) for name, val in (
|
||||
('DECORATION_SHIFT', DECORATION), ('REVERSE_SHIFT', REVERSE), ('STRIKE_SHIFT', STRIKETHROUGH), ('DIM_SHIFT', DIM),
|
||||
))
|
||||
vv = vv.replace('#define SHIFTS', shifts)
|
||||
if semi_transparent:
|
||||
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
|
||||
@ -52,7 +52,7 @@ def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):
|
||||
|
||||
def filled_cursor():
|
||||
ans = Cursor()
|
||||
ans.bold = ans.italic = ans.reverse = ans.strikethrough = True
|
||||
ans.bold = ans.italic = ans.reverse = ans.strikethrough = ans.dim = True
|
||||
ans.fg = 0x101
|
||||
ans.bg = 0x201
|
||||
ans.decoration_fg = 0x301
|
||||
|
||||
@ -179,7 +179,7 @@ class TestDataTypes(BaseTest):
|
||||
self.assertEqual(str(l0), 'a' + t[1:])
|
||||
|
||||
c = C(3, 5)
|
||||
c.bold = c.italic = c.reverse = c.strikethrough = True
|
||||
c.bold = c.italic = c.reverse = c.strikethrough = c.dim = True
|
||||
c.fg = c.bg = c.decoration_fg = 0x0101
|
||||
self.ae(c, c)
|
||||
c2, c3 = c.copy(), c.copy()
|
||||
@ -219,7 +219,7 @@ class TestDataTypes(BaseTest):
|
||||
|
||||
l3.set_text(t, 0, len(t), C())
|
||||
q = C()
|
||||
q.bold = q.italic = q.reverse = q.strikethrough = True
|
||||
q.bold = q.italic = q.reverse = q.strikethrough = c.dim = True
|
||||
q.decoration = 2
|
||||
c = C()
|
||||
c.x = 3
|
||||
@ -450,12 +450,12 @@ class TestDataTypes(BaseTest):
|
||||
self.ae(a, ['\x1b[0m' + str(lb.line(i)) + '\n' for i in range(lb.ynum)])
|
||||
l2 = lb.line(0)
|
||||
c = C()
|
||||
c.bold = c.italic = c.reverse = c.strikethrough = True
|
||||
c.bold = c.italic = c.reverse = c.strikethrough = c.dim = True
|
||||
c.fg = (4 << 8) | 1
|
||||
c.bg = (1 << 24) | (2 << 16) | (3 << 8) | 2
|
||||
c.decoration_fg = (5 << 8) | 1
|
||||
l2.set_text('1', 0, 1, c)
|
||||
self.ae(l2.as_ansi(), '\x1b[0m\x1b[1;3;7;9;34;48:2:1:2:3;58:5:5m' '1'
|
||||
self.ae(l2.as_ansi(), '\x1b[0m\x1b[1;2;3;7;9;34;48:2:1:2:3;58:5:5m' '1'
|
||||
'\x1b[22;23;27;29;39;49;59m' '0000')
|
||||
lb = filled_line_buf()
|
||||
for i in range(lb.ynum):
|
||||
|
||||
@ -118,8 +118,8 @@ class TestParser(BaseTest):
|
||||
def sgr(params):
|
||||
return (('select_graphic_rendition', '{} '.format(x)) for x in params.split())
|
||||
|
||||
pb('\033[1;3;4;7;9;34;44m', *sgr('1 3 4 7 9 34 44'))
|
||||
for attr in 'bold italic reverse strikethrough'.split():
|
||||
pb('\033[1;2;3;4;7;9;34;44m', *sgr('1 2 3 4 7 9 34 44'))
|
||||
for attr in 'bold italic reverse strikethrough dim'.split():
|
||||
self.assertTrue(getattr(s.cursor, attr))
|
||||
self.ae(s.cursor.decoration, 1)
|
||||
self.ae(s.cursor.fg, 4 << 8 | 1)
|
||||
@ -202,7 +202,7 @@ class TestParser(BaseTest):
|
||||
c.clear()
|
||||
pb('\033P$qm\033\\', ('screen_request_capabilities', ord('$'), 'm'))
|
||||
self.ae(c.wtcbuf, b'\033P1$rm\033\\')
|
||||
for sgr in '0;34;102;1;3;4 0;38:5:200;58:2:10:11:12'.split():
|
||||
for sgr in '0;34;102;1;2;3;4 0;38:5:200;58:2:10:11:12'.split():
|
||||
expected = set(sgr.split(';')) - {'0'}
|
||||
c.clear()
|
||||
parse_bytes(s, '\033[{}m\033P$qm\033\\'.format(sgr).encode('ascii'))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user