diff --git a/kitty/cursor.c b/kitty/cursor.c index aab732ff8..e09dcbf6c 100644 --- a/kitty/cursor.c +++ b/kitty/cursor.c @@ -129,34 +129,42 @@ color_as_sgr(char *buf, size_t sz, unsigned long val, unsigned simple_code, unsi case 1: val >>= 8; if (val < 16 && simple_code) { - return snprintf(buf, sz, "%ld;", (val < 8) ? simple_code + val : aix_code + (val - 8)); + return snprintf(buf, sz, "%lu;", (val < 8) ? simple_code + val : aix_code + (val - 8)); } - return snprintf(buf, sz, "%d:5:%ld;", complex_code, val); + return snprintf(buf, sz, "%u:5:%lu;", complex_code, val); case 2: - return snprintf(buf, sz, "%d:2:%ld:%ld:%ld;", complex_code, (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff); + return snprintf(buf, sz, "%u:2:%lu:%lu:%lu;", complex_code, (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff); default: - return 0; + return snprintf(buf, sz, "%u;", complex_code + 1); // reset + } +} + +static inline const char* +decoration_as_sgr(uint8_t decoration) { + switch(decoration) { + case 1: return "4"; + case 2: return "6"; // UNDERCURL_CODE + default: return "24"; } } const char* -cursor_as_sgr(Cursor *self) { +cursor_as_sgr(Cursor *self, Cursor *prev) { static char buf[128], *p; #define SZ sizeof(buf) - (p - buf) - 2 #define P(fmt, ...) { p += snprintf(p, SZ, fmt ";", __VA_ARGS__); } p = buf; - if (self->bold) P("%d", 1); - if (self->italic) P("%d", 3); - if (self->reverse) P("%d", 7); - if (self->strikethrough) P("%d", 9); - if (self->decoration) P("%d", self->decoration == 1 ? 4 : UNDERCURL_CODE); - if (self->fg) p += color_as_sgr(p, SZ, self->fg, 30, 90, 38); - if (self->bg) p += color_as_sgr(p, SZ, self->bg, 40, 100, 48); - if (self->decoration_fg) p += color_as_sgr(p, SZ, self->decoration_fg, 0, 0, DECORATION_FG_CODE); + if (self->bold != prev->bold) P("%d", self->bold ? 1 : 22); + 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); + if (self->decoration != prev->decoration) P("%s", decoration_as_sgr(self->decoration)); + if (self->fg != prev->fg) p += color_as_sgr(p, SZ, self->fg, 30, 90, 38); + if (self->bg != prev->bg) p += color_as_sgr(p, SZ, self->bg, 40, 100, 48); + if (self->decoration_fg != prev->decoration_fg) p += color_as_sgr(p, SZ, self->decoration_fg, 0, 0, DECORATION_FG_CODE); #undef P #undef SZ if (p > buf) *(p - 1) = 0; // remove trailing semi-colon - else *(p++) = '0'; // no formatting *p = 0; // ensure string is null-terminated return buf; } diff --git a/kitty/data-types.h b/kitty/data-types.h index 5431d0292..4ce6f8db1 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -72,8 +72,8 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; ((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_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)->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; #define COPY_CELL(src, s, dest, d) \ (dest)->cells[d] = (src)->cells[s]; @@ -256,7 +256,7 @@ Cursor* cursor_copy(Cursor*); void cursor_copy_to(Cursor *src, Cursor *dest); void cursor_reset_display_attrs(Cursor*); void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count); -const char* cursor_as_sgr(Cursor*); +const char* cursor_as_sgr(Cursor*, Cursor*); double monotonic(); PyObject* cm_thread_write(PyObject *self, PyObject *args); diff --git a/kitty/line.c b/kitty/line.c index d3aa7a71a..c2af8f98f 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -229,28 +229,10 @@ sprite_at(Line* self, PyObject *x) { return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z); } - static inline bool -write_sgr(unsigned int val, Py_UCS4 *buf, index_type buflen, index_type *i) { - static char s[20] = {0}; - unsigned int num = snprintf(s, 20, "\x1b[%um", val); - if (buflen - (*i) < num + 3) return false; - for(unsigned int si=0; si < num; si++) buf[(*i)++] = s[si]; - return true; -} - -static inline bool -write_color(uint32_t val, int code, Py_UCS4 *buf, index_type buflen, index_type *i) { - static char s[50] = {0}; - unsigned int num; - switch(val & 3) { - case 1: - num = snprintf(s, 50, "\x1b[%d;5;%um", code, (val >> 8) & 0xFF); break; - case 2: - num = snprintf(s, 50, "\x1b[%d;2;%u;%u;%um", code, (val >> 24) & 0xFF, (val >> 16) & 0xFF, (val >> 8) & 0xFF); break; - default: - return true; - } +write_sgr(const char *val, Py_UCS4 *buf, index_type buflen, index_type *i) { + static char s[128]; + unsigned int num = snprintf(s, sizeof(s), "\x1b[%sm", val); if (buflen - (*i) < num + 3) return false; for(unsigned int si=0; si < num; si++) buf[(*i)++] = s[si]; return true; @@ -258,46 +240,29 @@ write_color(uint32_t val, int code, Py_UCS4 *buf, index_type buflen, index_type index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { -#define WRITE_SGR(val) if (!write_sgr(val, buf, buflen, &i)) return i; -#define WRITE_COLOR(val, code) if (val) { if (!write_color(val, code, buf, buflen, &i)) return i; } else { WRITE_SGR(code+1); } -#define CHECK_BOOL(name, shift, on, off) \ - if (((attrs >> shift) & 1) != name) { \ - name ^= 1; \ - if (name) { WRITE_SGR(on); } else { WRITE_SGR(off); } \ - } -#define CHECK_COLOR(name, val, off_code) if (name != (val)) { name = (val); WRITE_COLOR(name, off_code); } +#define WRITE_SGR(val) { if (!write_sgr(val, buf, buflen, &i)) return i; } #define WRITE_CH(val) if (i > buflen - 1) return i; buf[i++] = val; index_type limit = xlimit_for_line(self), i=0; - bool bold = false, italic = false, reverse = false, strike = false; - uint32_t fg = 0, bg = 0, decoration_fg = 0, decoration = 0; char_type previous_width = 0; - WRITE_SGR(0); + WRITE_SGR("0"); + Cursor c1 = {{0}}, c2 = {{0}}; + Cursor *cursor = &c1, *prev_cursor = &c2, *t; + for (index_type pos=0; pos < limit; pos++) { char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; } ch = ' '; } - CHECK_BOOL(bold, BOLD_SHIFT, 1, 22); - CHECK_BOOL(italic, ITALIC_SHIFT, 3, 23); - CHECK_BOOL(reverse, REVERSE_SHIFT, 7, 27); - CHECK_BOOL(strike, STRIKE_SHIFT, 9, 29); - if (((attrs >> DECORATION_SHIFT) & DECORATION_MASK) != decoration) { - decoration = ((attrs >> DECORATION_SHIFT) & DECORATION_MASK); - switch(decoration) { - case 1: - WRITE_SGR(4); break; - case 2: - WRITE_SGR(UNDERCURL_CODE); break; - default: - WRITE_SGR(0); break; - } - } - CHECK_COLOR(fg, self->cells[pos].fg, 38); - CHECK_COLOR(bg, self->cells[pos].bg, 48); - CHECK_COLOR(decoration_fg, self->cells[pos].decoration_fg, DECORATION_FG_CODE); + ATTRS_TO_CURSOR(attrs, cursor); + cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg; + cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK; + + const char *sgr = cursor_as_sgr(cursor, prev_cursor); + t = prev_cursor; prev_cursor = cursor; cursor = t; + if (*sgr) WRITE_SGR(sgr); WRITE_CH(ch); char_type cc = self->cells[pos].cc; Py_UCS4 cc1 = cc & CC_MASK; diff --git a/kitty/screen.c b/kitty/screen.c index acc09b969..e89d01f51 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1072,6 +1072,7 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) { static char buf[128]; int shape = 0; const char *query; + Cursor blank_cursor = {{0}}; switch(c) { case '+': CALLBACK("request_capabilities", "O", q); @@ -1095,7 +1096,7 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) { shape = snprintf(buf, sizeof(buf), "\033P1$r%d q\033\\", shape); } else if (strcmp("m", query) == 0) { // SGR - shape = snprintf(buf, sizeof(buf), "\033P1$r%sm\033\\", cursor_as_sgr(self->cursor)); + shape = snprintf(buf, sizeof(buf), "\033P1$r%sm\033\\", cursor_as_sgr(self->cursor, &blank_cursor)); } else if (strcmp("r", query) == 0) { shape = snprintf(buf, sizeof(buf), "\033P1$r%u;%ur\033\\", self->margin_top + 1, self->margin_bottom + 1); } else { diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index aaa563350..ddc244f70 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -399,8 +399,8 @@ class TestDataTypes(BaseTest): 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[1m\x1b[3m\x1b[7m\x1b[9m\x1b[38;5;4m\x1b[48;2;1;2;3m\x1b[58;5;5m' '1' - '\x1b[22m\x1b[23m\x1b[27m\x1b[29m\x1b[39m\x1b[49m\x1b[59m' '0000') + self.ae(l2.as_ansi(), '\x1b[0m\x1b[1;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): lb.set_continued(i, True) diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index c01b28430..6f01fbba4 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -204,7 +204,7 @@ class TestParser(BaseTest): self.ae(c.wtcbuf, b'\033P1$r1 q\033\\') c.clear() pb('\033P$qm\033\\', ('screen_request_capabilities', ord('$'), 'm')) - self.ae(c.wtcbuf, b'\033P1$r0m\033\\') + 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(): expected = set(sgr.split(';')) - {'0'} c.clear()