A ColorProfile to manage colors
This commit is contained in:
parent
32e4de1c79
commit
1884cc17c1
100
kitty/colors.c
100
kitty/colors.c
@ -7,7 +7,7 @@
|
||||
|
||||
#include "data-types.h"
|
||||
|
||||
static uint32_t FG_BG_256[255] = {
|
||||
static uint32_t FG_BG_256[256] = {
|
||||
0x000000, // 0
|
||||
0xcd0000, // 1
|
||||
0x00cd00, // 2
|
||||
@ -34,8 +34,8 @@ PyObject* create_256_color_table() {
|
||||
uint8_t r = valuerange[(i / 36) % 6], g = valuerange[(i / 6) % 6], b = valuerange[i % 6];
|
||||
FG_BG_256[j] = (r << 16) | (g << 8) | b;
|
||||
}
|
||||
// colors 233..253: grayscale
|
||||
for(i = 1; i < 22; i++, j++) {
|
||||
// colors 233..255: grayscale
|
||||
for(i = 1; i < 24; i++, j++) {
|
||||
uint8_t v = 8 + i * 10;
|
||||
FG_BG_256[j] = (v << 16) | (v << 8) | v;
|
||||
}
|
||||
@ -49,3 +49,97 @@ PyObject* create_256_color_table() {
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||
ColorProfile *self;
|
||||
|
||||
self = (ColorProfile *)type->tp_alloc(type, 0);
|
||||
if (self != NULL) {
|
||||
if (FG_BG_256[255] == 0) create_256_color_table();
|
||||
memcpy(self->color_table_256, FG_BG_256, sizeof(FG_BG_256));
|
||||
}
|
||||
return (PyObject*) self;
|
||||
}
|
||||
|
||||
static void
|
||||
dealloc(Cursor* self) {
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
||||
#define update_ansi_color_table_doc "Update the 16 basic colors"
|
||||
index_type i;
|
||||
PyObject *t;
|
||||
|
||||
if (!PyList_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a list"); return NULL; }
|
||||
|
||||
#define to_color \
|
||||
t = PyList_GET_ITEM(val, i); \
|
||||
self->ansi_color_table[i] = PyLong_AsUnsignedLong(t);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
// Boilerplate {{{
|
||||
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
METHOD(update_ansi_color_table, METH_O)
|
||||
METHOD(ansi_color, METH_O)
|
||||
METHOD(color_256, METH_O)
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
static PyTypeObject ColorProfile_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "fast_data_types.ColorProfile",
|
||||
.tp_basicsize = sizeof(ColorProfile),
|
||||
.tp_dealloc = (destructor)dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "ColorProfile",
|
||||
.tp_methods = methods,
|
||||
.tp_new = new,
|
||||
};
|
||||
|
||||
INIT_TYPE(ColorProfile)
|
||||
// }}}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import re
|
||||
from collections import namedtuple
|
||||
from typing import Tuple
|
||||
from itertools import repeat
|
||||
|
||||
|
||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||
@ -298,23 +298,22 @@ def load_config(path: str) -> Options:
|
||||
return Options(**ans)
|
||||
|
||||
|
||||
def build_ansi_color_tables(opts: Options) -> Tuple[dict, dict]:
|
||||
def build_ansi_color_table(opts: Options=defaults):
|
||||
def as_int(x):
|
||||
return (x[0] << 16) | (x[1] << 8) | x[2]
|
||||
|
||||
def col(i):
|
||||
return getattr(opts, 'color{}'.format(i))
|
||||
return as_int(getattr(opts, 'color{}'.format(i)))
|
||||
ans = list(repeat(0, 120))
|
||||
fg = {30 + i: col(i) for i in range(8)}
|
||||
fg[39] = opts.foreground
|
||||
fg[39] = as_int(opts.foreground)
|
||||
fg.update({90 + i: col(i + 8) for i in range(8)})
|
||||
fg[99] = opts.foreground_bold
|
||||
fg[99] = as_int(opts.foreground_bold)
|
||||
bg = {40 + i: col(i) for i in range(8)}
|
||||
bg[49] = opts.background
|
||||
bg[49] = as_int(opts.background)
|
||||
bg.update({100 + i: col(i + 8) for i in range(8)})
|
||||
build_ansi_color_tables.fg, build_ansi_color_tables.bg = fg, bg
|
||||
build_ansi_color_tables(defaults)
|
||||
|
||||
|
||||
def fg_color_table():
|
||||
return build_ansi_color_tables.fg
|
||||
|
||||
|
||||
def bg_color_table():
|
||||
return build_ansi_color_tables.bg
|
||||
for k, val in fg.items():
|
||||
ans[k] = val
|
||||
for k, val in bg.items():
|
||||
ans[k] = val
|
||||
return ans
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
extern int init_LineBuf(PyObject *);
|
||||
extern int init_Cursor(PyObject *);
|
||||
extern int init_Line(PyObject *);
|
||||
extern int init_ColorProfile(PyObject *);
|
||||
extern PyObject* create_256_color_table();
|
||||
#include "gl.h"
|
||||
|
||||
@ -37,8 +38,8 @@ PyInit_fast_data_types(void) {
|
||||
if (!init_LineBuf(m)) return NULL;
|
||||
if (!init_Line(m)) return NULL;
|
||||
if (!init_Cursor(m)) return NULL;
|
||||
if (!init_ColorProfile(m)) return NULL;
|
||||
if (!add_module_gl_constants(m)) return NULL;
|
||||
if (PyModule_AddObject(m, "FG_BG_256", create_256_color_table()) != 0) return NULL;
|
||||
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
|
||||
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
|
||||
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);
|
||||
|
||||
@ -148,6 +148,16 @@ typedef struct {
|
||||
|
||||
} Cursor;
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
uint32_t color_table_256[256];
|
||||
uint32_t ansi_color_table[120];
|
||||
|
||||
} ColorProfile;
|
||||
|
||||
|
||||
Line* alloc_line();
|
||||
Cursor* alloc_cursor();
|
||||
|
||||
|
||||
@ -6,8 +6,6 @@ from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
from typing import Set, Tuple, Iterator
|
||||
|
||||
from .data_types import Cursor
|
||||
|
||||
|
||||
def merge_ranges(ranges: Set[Tuple[int]]) -> Iterator[Tuple[int]]:
|
||||
if ranges:
|
||||
@ -44,11 +42,11 @@ class ChangeTracker:
|
||||
self._dirty = True
|
||||
self.mark_dirtied()
|
||||
|
||||
def cursor_changed(self, cursor: Cursor) -> None:
|
||||
def cursor_changed(self, cursor) -> None:
|
||||
self.changed_cursor = cursor
|
||||
self.dirty()
|
||||
|
||||
def cursor_position_changed(self, cursor: Cursor) -> None:
|
||||
def cursor_position_changed(self, cursor) -> None:
|
||||
self.changed_cursor = cursor
|
||||
self.dirty()
|
||||
|
||||
|
||||
@ -6,8 +6,9 @@ import codecs
|
||||
|
||||
from . import BaseTest, filled_line_buf, filled_cursor
|
||||
|
||||
from kitty.config import build_ansi_color_table, defaults
|
||||
from kitty.utils import is_simple_string, wcwidth, sanitize_title
|
||||
from kitty.fast_data_types import LineBuf, Cursor as C, REVERSE
|
||||
from kitty.fast_data_types import LineBuf, Cursor as C, REVERSE, ColorProfile
|
||||
|
||||
|
||||
def create_lbuf(*lines):
|
||||
@ -259,3 +260,11 @@ class TestDataTypes(BaseTest):
|
||||
self.assertTrue(is_simple_string(d(s.encode('utf-8'))))
|
||||
self.assertFalse(is_simple_string('a1コ'))
|
||||
self.assertEqual(sanitize_title('a\0\01 \t\n\f\rb'), 'a b')
|
||||
|
||||
def test_color_profile(self):
|
||||
c = ColorProfile()
|
||||
c.update_ansi_color_table(build_ansi_color_table())
|
||||
for i in range(8):
|
||||
col = getattr(defaults, 'color{}'.format(i))
|
||||
self.assertEqual(c.ansi_color(30 + i), col[0] << 16 | col[1] << 8 | col[2])
|
||||
self.ae(c.color_256(255), 0xeeeeee)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user