Centralize code to convert to SGR
Conversion from formatting attributes to SGR now always goes through a Cursor.
This commit is contained in:
parent
52ecdfe3a9
commit
27cd303a05
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
65
kitty/line.c
65
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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user