diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl index 7cf8f7cec..b5de428e3 100644 --- a/kitty/cell_fragment.glsl +++ b/kitty/cell_fragment.glsl @@ -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 diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 4cd5a1ea9..5b69c5fc7 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -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); diff --git a/kitty/cursor.c b/kitty/cursor.c index 630d2ee33..15848a5b6 100644 --- a/kitty/cursor.c +++ b/kitty/cursor.c @@ -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 */ }; diff --git a/kitty/data-types.c b/kitty/data-types.c index 3de790170..37ff33c1f 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -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 diff --git a/kitty/data-types.h b/kitty/data-types.h index 3fcf9d3e7..e189257ba 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -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; diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 38f4940a8..2b298faa2 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -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; } diff --git a/kitty/line.c b/kitty/line.c index 1f35936f5..0af7ac390 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -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; } diff --git a/kitty/window.py b/kitty/window.py index a6a00995e..91a3f20b6 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -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') diff --git a/kitty_tests/__init__.py b/kitty_tests/__init__.py index 306e51aee..a53fea0e5 100644 --- a/kitty_tests/__init__.py +++ b/kitty_tests/__init__.py @@ -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 diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index fe27cb953..d1c66b5f0 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -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): diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 1643cd7e4..3f79ffcde 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -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'))