Centralize code to convert to SGR

Conversion from formatting attributes to SGR now always goes through a
Cursor.
This commit is contained in:
Kovid Goyal 2017-12-04 10:51:06 +05:30
parent 52ecdfe3a9
commit 27cd303a05
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 45 additions and 71 deletions

View File

@ -129,34 +129,42 @@ color_as_sgr(char *buf, size_t sz, unsigned long val, unsigned simple_code, unsi
case 1: case 1:
val >>= 8; val >>= 8;
if (val < 16 && simple_code) { 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: 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: 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* const char*
cursor_as_sgr(Cursor *self) { cursor_as_sgr(Cursor *self, Cursor *prev) {
static char buf[128], *p; static char buf[128], *p;
#define SZ sizeof(buf) - (p - buf) - 2 #define SZ sizeof(buf) - (p - buf) - 2
#define P(fmt, ...) { p += snprintf(p, SZ, fmt ";", __VA_ARGS__); } #define P(fmt, ...) { p += snprintf(p, SZ, fmt ";", __VA_ARGS__); }
p = buf; p = buf;
if (self->bold) P("%d", 1); if (self->bold != prev->bold) P("%d", self->bold ? 1 : 22);
if (self->italic) P("%d", 3); if (self->italic != prev->italic) P("%d", self->italic ? 3 : 23);
if (self->reverse) P("%d", 7); if (self->reverse != prev->reverse) P("%d", self->reverse ? 7 : 27);
if (self->strikethrough) P("%d", 9); if (self->strikethrough != prev->strikethrough) P("%d", self->strikethrough ? 9 : 29);
if (self->decoration) P("%d", self->decoration == 1 ? 4 : UNDERCURL_CODE); if (self->decoration != prev->decoration) P("%s", decoration_as_sgr(self->decoration));
if (self->fg) p += color_as_sgr(p, SZ, self->fg, 30, 90, 38); if (self->fg != prev->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->bg != prev->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->decoration_fg != prev->decoration_fg) p += color_as_sgr(p, SZ, self->decoration_fg, 0, 0, DECORATION_FG_CODE);
#undef P #undef P
#undef SZ #undef SZ
if (p > buf) *(p - 1) = 0; // remove trailing semi-colon if (p > buf) *(p - 1) = 0; // remove trailing semi-colon
else *(p++) = '0'; // no formatting
*p = 0; // ensure string is null-terminated *p = 0; // ensure string is null-terminated
return buf; return buf;
} }

View File

@ -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))) ((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT)))
#define ATTRS_TO_CURSOR(a, c) \ #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)->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;
#define COPY_CELL(src, s, dest, d) \ #define COPY_CELL(src, s, dest, d) \
(dest)->cells[d] = (src)->cells[s]; (dest)->cells[d] = (src)->cells[s];
@ -256,7 +256,7 @@ Cursor* cursor_copy(Cursor*);
void cursor_copy_to(Cursor *src, Cursor *dest); void cursor_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*); void cursor_reset_display_attrs(Cursor*);
void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count); 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(); double monotonic();
PyObject* cm_thread_write(PyObject *self, PyObject *args); PyObject* cm_thread_write(PyObject *self, PyObject *args);

View File

@ -229,28 +229,10 @@ sprite_at(Line* self, PyObject *x) {
return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z); return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z);
} }
static inline bool static inline bool
write_sgr(unsigned int val, Py_UCS4 *buf, index_type buflen, index_type *i) { write_sgr(const char *val, Py_UCS4 *buf, index_type buflen, index_type *i) {
static char s[20] = {0}; static char s[128];
unsigned int num = snprintf(s, 20, "\x1b[%um", val); 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;
}
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;
}
if (buflen - (*i) < num + 3) return false; if (buflen - (*i) < num + 3) return false;
for(unsigned int si=0; si < num; si++) buf[(*i)++] = s[si]; for(unsigned int si=0; si < num; si++) buf[(*i)++] = s[si];
return true; return true;
@ -258,46 +240,29 @@ write_color(uint32_t val, int code, Py_UCS4 *buf, index_type buflen, index_type
index_type index_type
line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { 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_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_CH(val) if (i > buflen - 1) return i; buf[i++] = val; #define WRITE_CH(val) if (i > buflen - 1) return i; buf[i++] = val;
index_type limit = xlimit_for_line(self), i=0; 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; 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++) { for (index_type pos=0; pos < limit; pos++) {
char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch; char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch;
if (ch == 0) { if (ch == 0) {
if (previous_width == 2) { previous_width = 0; continue; } if (previous_width == 2) { previous_width = 0; continue; }
ch = ' '; ch = ' ';
} }
CHECK_BOOL(bold, BOLD_SHIFT, 1, 22); ATTRS_TO_CURSOR(attrs, cursor);
CHECK_BOOL(italic, ITALIC_SHIFT, 3, 23); cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg;
CHECK_BOOL(reverse, REVERSE_SHIFT, 7, 27); cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK;
CHECK_BOOL(strike, STRIKE_SHIFT, 9, 29);
if (((attrs >> DECORATION_SHIFT) & DECORATION_MASK) != decoration) { const char *sgr = cursor_as_sgr(cursor, prev_cursor);
decoration = ((attrs >> DECORATION_SHIFT) & DECORATION_MASK); t = prev_cursor; prev_cursor = cursor; cursor = t;
switch(decoration) { if (*sgr) WRITE_SGR(sgr);
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);
WRITE_CH(ch); WRITE_CH(ch);
char_type cc = self->cells[pos].cc; char_type cc = self->cells[pos].cc;
Py_UCS4 cc1 = cc & CC_MASK; Py_UCS4 cc1 = cc & CC_MASK;

View File

@ -1072,6 +1072,7 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) {
static char buf[128]; static char buf[128];
int shape = 0; int shape = 0;
const char *query; const char *query;
Cursor blank_cursor = {{0}};
switch(c) { switch(c) {
case '+': case '+':
CALLBACK("request_capabilities", "O", q); 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); shape = snprintf(buf, sizeof(buf), "\033P1$r%d q\033\\", shape);
} else if (strcmp("m", query) == 0) { } else if (strcmp("m", query) == 0) {
// SGR // 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) { } else if (strcmp("r", query) == 0) {
shape = snprintf(buf, sizeof(buf), "\033P1$r%u;%ur\033\\", self->margin_top + 1, self->margin_bottom + 1); shape = snprintf(buf, sizeof(buf), "\033P1$r%u;%ur\033\\", self->margin_top + 1, self->margin_bottom + 1);
} else { } else {

View File

@ -399,8 +399,8 @@ class TestDataTypes(BaseTest):
c.bg = (1 << 24) | (2 << 16) | (3 << 8) | 2 c.bg = (1 << 24) | (2 << 16) | (3 << 8) | 2
c.decoration_fg = (5 << 8) | 1 c.decoration_fg = (5 << 8) | 1
l2.set_text('1', 0, 1, c) 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' self.ae(l2.as_ansi(), '\x1b[0m\x1b[1;3;7;9;34;48:2:1:2:3;58:5:5m' '1'
'\x1b[22m\x1b[23m\x1b[27m\x1b[29m\x1b[39m\x1b[49m\x1b[59m' '0000') '\x1b[22;23;27;29;39;49;59m' '0000')
lb = filled_line_buf() lb = filled_line_buf()
for i in range(lb.ynum): for i in range(lb.ynum):
lb.set_continued(i, True) lb.set_continued(i, True)

View File

@ -204,7 +204,7 @@ class TestParser(BaseTest):
self.ae(c.wtcbuf, b'\033P1$r1 q\033\\') self.ae(c.wtcbuf, b'\033P1$r1 q\033\\')
c.clear() c.clear()
pb('\033P$qm\033\\', ('screen_request_capabilities', ord('$'), 'm')) 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(): for sgr in '0;34;102;1;3;4 0;38:5:200;58:2:10:11:12'.split():
expected = set(sgr.split(';')) - {'0'} expected = set(sgr.split(';')) - {'0'}
c.clear() c.clear()