Start work on supporting changing of entries in the color table

This commit is contained in:
Kovid Goyal 2016-11-24 15:28:52 +05:30
parent c983f002c1
commit 3031d41e72
11 changed files with 53 additions and 99 deletions

View File

@ -274,6 +274,9 @@ class Boss(Thread):
code += 1 code += 1
self.queue_action(self.apply_change_colors) self.queue_action(self.apply_change_colors)
def set_color_table_color(self, code, value):
print(11111111, code, value)
def apply_change_colors(self): def apply_change_colors(self):
self.char_grid.change_colors(self.pending_color_changes) self.char_grid.change_colors(self.pending_color_changes)
self.pending_color_changes = {} self.pending_color_changes = {}

View File

@ -57,7 +57,8 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
self = (ColorProfile *)type->tp_alloc(type, 0); self = (ColorProfile *)type->tp_alloc(type, 0);
if (self != NULL) { if (self != NULL) {
if (FG_BG_256[255] == 0) create_256_color_table(); if (FG_BG_256[255] == 0) create_256_color_table();
memcpy(self->color_table_256, FG_BG_256, sizeof(FG_BG_256)); memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256));
memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
} }
return (PyObject*) self; return (PyObject*) self;
} }
@ -72,78 +73,32 @@ static PyObject*
update_ansi_color_table(ColorProfile *self, PyObject *val) { update_ansi_color_table(ColorProfile *self, PyObject *val) {
#define update_ansi_color_table_doc "Update the 16 basic colors" #define update_ansi_color_table_doc "Update the 16 basic colors"
index_type i; index_type i;
PyObject *t;
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; } if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
if (PyList_GET_SIZE(val) != 16) { PyErr_SetString(PyExc_TypeError, "color table must have 16 items"); return NULL; }
#define TO_COLOR \ for (i = 0; i < 16; i++) {
t = PyList_GET_ITEM(val, i); \ self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
self->ansi_color_table[i] = PyLong_AsUnsignedLong(t); self->orig_color_table[i] = self->color_table[i];
for(i = 30; i < 38; i++) {
TO_COLOR;
}
i = 39; TO_COLOR;
for(i = 90; i < 98; i++) {
TO_COLOR;
}
i = 99; TO_COLOR;
for(i = 40; i < 48; i++) {
TO_COLOR;
}
i = 49; TO_COLOR;
for(i = 100; i < 108; i++) {
TO_COLOR;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject*
ansi_color(ColorProfile *self, PyObject *val) {
#define ansi_color_doc "Return the color at the specified index"
if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "index must be an int"); return NULL; }
unsigned long idx = PyLong_AsUnsignedLong(val);
if (idx >= sizeof(self->ansi_color_table) / sizeof(self->ansi_color_table[0])) {
PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL;
}
return PyLong_FromUnsignedLong(self->ansi_color_table[idx]);
}
static PyObject*
color_256(ColorProfile *self, PyObject *val) {
#define color_256_doc "Return the color at the specified 256-color index"
if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "index must be an int"); return NULL; }
unsigned long idx = PyLong_AsUnsignedLong(val);
if (idx >= 256) {
PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL;
}
return PyLong_FromUnsignedLong(self->color_table_256[idx]);
}
static PyObject* static PyObject*
as_color(ColorProfile *self, PyObject *val) { as_color(ColorProfile *self, PyObject *val) {
#define as_color_doc "Convert the specified terminal color into an (r, g, b) tuple based on the current profile values" #define as_color_doc "Convert the specified terminal color into an (r, g, b) tuple based on the current profile values"
if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "val must be an int"); return NULL; } if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "val must be an int"); return NULL; }
unsigned long entry = PyLong_AsUnsignedLong(val); unsigned long entry = PyLong_AsUnsignedLong(val);
unsigned int t = entry & 0xFF; unsigned int t = entry & 0xFF;
uint8_t r, g, b; uint8_t r;
uint32_t col = 0; uint32_t col = 0;
PyObject *ans = NULL; PyObject *ans = NULL;
switch(t) { switch(t) {
case 1: case 1:
r = (entry >> 8) & 0xff; r = (entry >> 8) & 0xff;
col = self->ansi_color_table[r]; col = self->color_table[r];
break; break;
case 2: case 2:
r = (entry >> 8) & 0xff; col = entry >> 8;
col = self->color_table_256[r];
break;
case 3:
r = (entry >> 8) & 0xff;
g = (entry >> 16) & 0xff;
b = (entry >> 24) & 0xff;
ans = Py_BuildValue("BBB", r, g, b);
break;
default: default:
ans = Py_None; Py_INCREF(Py_None); ans = Py_None; Py_INCREF(Py_None);
} }
@ -151,16 +106,14 @@ as_color(ColorProfile *self, PyObject *val) {
return ans; return ans;
} }
uint32_t to_color(ColorProfile *self, uint32_t entry, uint32_t defval) { uint32_t
to_color(ColorProfile *self, uint32_t entry, uint32_t defval) {
unsigned int t = entry & 0xFF, r; unsigned int t = entry & 0xFF, r;
switch(t) { switch(t) {
case 1: case 1:
r = (entry >> 8) & 0xff; r = (entry >> 8) & 0xff;
return self->ansi_color_table[r]; return self->color_table[r];
case 2: case 2:
r = (entry >> 8) & 0xff;
return self->color_table_256[r];
case 3:
return entry >> 8; return entry >> 8;
default: default:
return defval; return defval;
@ -172,8 +125,6 @@ uint32_t to_color(ColorProfile *self, uint32_t entry, uint32_t defval) {
static PyMethodDef methods[] = { static PyMethodDef methods[] = {
METHOD(update_ansi_color_table, METH_O) METHOD(update_ansi_color_table, METH_O)
METHOD(ansi_color, METH_O)
METHOD(color_256, METH_O)
METHOD(as_color, METH_O) METHOD(as_color, METH_O)
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };

View File

@ -5,7 +5,6 @@
import re import re
import sys import sys
from collections import namedtuple from collections import namedtuple
from itertools import repeat
import glfw_constants as glfw import glfw_constants as glfw
@ -215,7 +214,7 @@ type_map = {
'repaint_delay': int, 'repaint_delay': int,
} }
for name in 'foreground foreground_bold background cursor'.split(): for name in 'foreground background cursor'.split():
type_map[name] = lambda x: to_color(x, validate=True) type_map[name] = lambda x: to_color(x, validate=True)
for i in range(16): for i in range(16):
type_map['color%d' % i] = lambda x: to_color(x, validate=True) type_map['color%d' % i] = lambda x: to_color(x, validate=True)
@ -271,9 +270,6 @@ foreground #dddddd
# The background color # The background color
background #000000 background #000000
# The high intensity foreground color
foreground_bold #ffffff
# The cursor color # The cursor color
cursor #ffffff cursor #ffffff
@ -385,16 +381,4 @@ def build_ansi_color_table(opts: Options=defaults):
def col(i): def col(i):
return as_int(getattr(opts, 'color{}'.format(i))) return as_int(getattr(opts, 'color{}'.format(i)))
ans = list(repeat(0, 120)) return list(map(col, range(16)))
fg = {30 + i: col(i) for i in range(8)}
fg[39] = as_int(opts.foreground)
fg.update({90 + i: col(i + 8) for i in range(8)})
fg[99] = as_int(opts.foreground_bold)
bg = {40 + i: col(i) for i in range(8)}
bg[49] = as_int(opts.background)
bg.update({100 + i: col(i + 8) for i in range(8)})
for k, val in fg.items():
ans[k] = val
for k, val in bg.items():
ans[k] = val
return ans

View File

@ -179,8 +179,8 @@ PyTypeObject Cursor_Type;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
uint32_t color_table_256[256]; uint32_t color_table[256];
uint32_t ansi_color_table[120]; uint32_t orig_color_table[256];
} ColorProfile; } ColorProfile;
PyTypeObject ColorProfile_Type; PyTypeObject ColorProfile_Type;
@ -364,6 +364,7 @@ void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom);
void set_title(Screen *self, PyObject*); void set_title(Screen *self, PyObject*);
void set_icon(Screen *self, PyObject*); void set_icon(Screen *self, PyObject*);
void set_dynamic_color(Screen *self, unsigned int code, PyObject*); void set_dynamic_color(Screen *self, unsigned int code, PyObject*);
void set_color_table_color(Screen *self, unsigned int code, PyObject*);
void screen_request_capabilities(Screen *, PyObject *); void screen_request_capabilities(Screen *, PyObject *);
void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary); void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary);
void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count); void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count);

View File

@ -198,6 +198,7 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
static inline void static inline void
dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) { dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
#define DISPATCH_OSC(name) REPORT_OSC(name, string); name(screen, string); #define DISPATCH_OSC(name) REPORT_OSC(name, string); name(screen, string);
#define SET_COLOR(name) REPORT_OSC(name, string); name(screen, code, string);
const unsigned int limit = screen->parser_buf_pos; const unsigned int limit = screen->parser_buf_pos;
unsigned int code=0, i; unsigned int code=0, i;
for (i = 0; i < MIN(limit, 5); i++) { for (i = 0; i < MIN(limit, 5); i++) {
@ -220,12 +221,15 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
case 2: case 2:
DISPATCH_OSC(set_title); DISPATCH_OSC(set_title);
break; break;
case 4:
case 104:
SET_COLOR(set_color_table_color);
break;
case 10: case 10:
case 11: case 11:
case 110: case 110:
case 111: case 111:
REPORT_OSC(set_dynamic_color, string); SET_COLOR(set_dynamic_color);
set_dynamic_color(screen, code, string);
break; break;
default: default:
REPORT_ERROR("Unknown OSC code: %u", code); REPORT_ERROR("Unknown OSC code: %u", code);
@ -234,6 +238,7 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
Py_CLEAR(string); Py_CLEAR(string);
} }
#undef DISPATCH_OSC #undef DISPATCH_OSC
#undef SET_COLOR
} }
// }}} // }}}

View File

@ -199,14 +199,14 @@ void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int c
switch(attr) { \ switch(attr) { \
case 5: \ case 5: \
if (i < count) \ if (i < count) \
self->cursor->which = (params[i++] & 0xFF) << 8 | 2; \ self->cursor->which = (params[i++] & 0xFF) << 8 | 1; \
break; \ break; \
case 2: \ case 2: \
if (i < count - 2) { \ if (i < count - 2) { \
r = params[i++] & 0xFF; \ r = params[i++] & 0xFF; \
g = params[i++] & 0xFF; \ g = params[i++] & 0xFF; \
b = params[i++] & 0xFF; \ b = params[i++] & 0xFF; \
self->cursor->which = r << 24 | g << 16 | b << 8 | 3; \ self->cursor->which = r << 24 | g << 16 | b << 8 | 2; \
}\ }\
break; \ break; \
} \ } \
@ -243,14 +243,18 @@ void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int c
self->cursor->strikethrough = false; break; self->cursor->strikethrough = false; break;
START_ALLOW_CASE_RANGE START_ALLOW_CASE_RANGE
case 30 ... 37: case 30 ... 37:
self->cursor->fg = ((attr - 30) << 8) | 1; break;
case 39: case 39:
case 90 ... 97: self->cursor->fg = 0; break;
self->cursor->fg = (attr << 8) | 1; break;
case 40 ... 47: case 40 ... 47:
self->cursor->bg = ((attr - 40) << 8) | 1; break;
case 49: case 49:
self->cursor->bg = 0; break;
case 90 ... 97:
self->cursor->fg = ((attr - 90 + 8) << 8) | 1; break;
case 100 ... 107: case 100 ... 107:
self->cursor->bg = ((attr - 100 + 8) << 8) | 1; break;
END_ALLOW_CASE_RANGE END_ALLOW_CASE_RANGE
self->cursor->bg = (attr << 8) | 1; break;
case 38: case 38:
SET_COLOR(fg); SET_COLOR(fg);
case 48: case 48:
@ -810,6 +814,11 @@ void set_dynamic_color(Screen *self, unsigned int code, PyObject *color) {
if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
} }
void set_color_table_color(Screen *self, unsigned int code, PyObject *color) {
PyObject_CallMethod(self->callbacks, "set_color_table_color", "IO", code, color);
if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
}
void screen_request_capabilities(Screen *self, PyObject *q) { void screen_request_capabilities(Screen *self, PyObject *q) {
PyObject_CallMethod(self->callbacks, "request_capabilities", "O", q); PyObject_CallMethod(self->callbacks, "request_capabilities", "O", q);
if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }

View File

@ -130,8 +130,8 @@ string_capabilities = {
'ind': r'^J', 'ind': r'^J',
'indn': r'\E[%p1%dS', 'indn': r'\E[%p1%dS',
# initialize color (set dynamic colors) # initialize color (set dynamic colors)
# 'initc': r'\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\', 'initc': r'\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\',
# Set all color pairs to original values # Set all colors to original values
'oc': r'\E]104\007', 'oc': r'\E]104\007',
# turn on blank mode (characters invisible) # turn on blank mode (characters invisible)
# 'invis': r'\E[8m', # 'invis': r'\E[8m',
@ -278,7 +278,7 @@ termcap_aliases.update({
'al': 'il1', 'al': 'il1',
'sf': 'ind', 'sf': 'ind',
'SF': 'indn', 'SF': 'indn',
# 'Ic': 'initc', 'Ic': 'initc',
'oc': 'oc', 'oc': 'oc',
# 'mk': 'invis', # 'mk': 'invis',
'kb': 'kbs', 'kb': 'kbs',

View File

@ -273,8 +273,8 @@ class TestDataTypes(BaseTest):
c.update_ansi_color_table(build_ansi_color_table()) c.update_ansi_color_table(build_ansi_color_table())
for i in range(8): for i in range(8):
col = getattr(defaults, 'color{}'.format(i)) col = getattr(defaults, 'color{}'.format(i))
self.assertEqual(c.ansi_color(30 + i), col[0] << 16 | col[1] << 8 | col[2]) self.assertEqual(c.as_color(i << 8 | 1), (col[0], col[1], col[2]))
self.ae(c.color_256(255), 0xeeeeee) self.ae(c.as_color(255 << 8 | 1), (0xee, 0xee, 0xee))
def test_sprite_map(self): def test_sprite_map(self):
s = SpriteMap(10, 2) s = SpriteMap(10, 2)

View File

@ -123,14 +123,14 @@ class TestParser(BaseTest):
for attr in 'bold italic reverse strikethrough'.split(): for attr in 'bold italic reverse strikethrough'.split():
self.assertTrue(getattr(s.cursor, attr)) self.assertTrue(getattr(s.cursor, attr))
self.ae(s.cursor.decoration, 1) self.ae(s.cursor.decoration, 1)
self.ae(s.cursor.fg, 34 << 8 | 1) self.ae(s.cursor.fg, 4 << 8 | 1)
self.ae(s.cursor.bg, 44 << 8 | 1) self.ae(s.cursor.bg, 4 << 8 | 1)
pb('\033[38;5;1;48;5;7m', ('select_graphic_rendition', 6)) pb('\033[38;5;1;48;5;7m', ('select_graphic_rendition', 6))
self.ae(s.cursor.fg, 1 << 8 | 2) self.ae(s.cursor.fg, 1 << 8 | 1)
self.ae(s.cursor.bg, 7 << 8 | 2) self.ae(s.cursor.bg, 7 << 8 | 1)
pb('\033[38;2;1;2;3;48;2;7;8;9m', ('select_graphic_rendition', 10)) pb('\033[38;2;1;2;3;48;2;7;8;9m', ('select_graphic_rendition', 10))
self.ae(s.cursor.fg, 1 << 24 | 2 << 16 | 3 << 8 | 3) self.ae(s.cursor.fg, 1 << 24 | 2 << 16 | 3 << 8 | 2)
self.ae(s.cursor.bg, 7 << 24 | 8 << 16 | 9 << 8 | 3) self.ae(s.cursor.bg, 7 << 24 | 8 << 16 | 9 << 8 | 2)
c = Callbacks() c = Callbacks()
s.callbacks = c s.callbacks = c
pb('\033[5n', ('report_device_status', 5, 0)) pb('\033[5n', ('report_device_status', 5, 0))

View File

@ -50,6 +50,7 @@ xterm-kitty|KovIdTTY,
il1=\E[L, il1=\E[L,
ind=^J, ind=^J,
indn=\E[%p1%dS, indn=\E[%p1%dS,
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
kEND=\E[1;2F, kEND=\E[1;2F,
kHOM=\E[1;2H, kHOM=\E[1;2H,
kLFT=\E[1;2D, kLFT=\E[1;2D,

Binary file not shown.