Allow setting all 256 colors in the config file

This commit is contained in:
Kovid Goyal 2018-04-01 12:24:00 +05:30
parent 1c78633d1a
commit 910cebeeb5
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 75 additions and 29 deletions

View File

@ -5,6 +5,7 @@
* Distributed under terms of the GPL3 license. * Distributed under terms of the GPL3 license.
*/ */
#define EXTRA_INIT if (PyModule_AddFunctions(module, module_methods) != 0) return false;
#include "data-types.h" #include "data-types.h"
#include <structmember.h> #include <structmember.h>
@ -29,7 +30,9 @@ static uint32_t FG_BG_256[256] = {
0xffffff, // 15 0xffffff, // 15
}; };
PyObject* create_256_color_table() { static inline void
init_FG_BG_table() {
if (UNLIKELY(FG_BG_256[255] == 0)) {
// colors 16..232: the 6x6x6 color cube // colors 16..232: the 6x6x6 color cube
const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}; const uint8_t valuerange[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
uint8_t i, j=16; uint8_t i, j=16;
@ -42,10 +45,14 @@ PyObject* create_256_color_table() {
uint8_t v = 8 + i * 10; uint8_t v = 8 + i * 10;
FG_BG_256[j] = (v << 16) | (v << 8) | v; FG_BG_256[j] = (v << 16) | (v << 8) | v;
} }
}
}
PyObject *ans = PyTuple_New(255); PyObject* create_256_color_table() {
init_FG_BG_table();
PyObject *ans = PyTuple_New(arraysz(FG_BG_256));
if (ans == NULL) return PyErr_NoMemory(); if (ans == NULL) return PyErr_NoMemory();
for (i=0; i < 255; i++) { for (size_t i=0; i < arraysz(FG_BG_256); i++) {
PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]); PyObject *temp = PyLong_FromUnsignedLong(FG_BG_256[i]);
if (temp == NULL) { Py_CLEAR(ans); return NULL; } if (temp == NULL) { Py_CLEAR(ans); return NULL; }
PyTuple_SET_ITEM(ans, i, temp); PyTuple_SET_ITEM(ans, i, temp);
@ -59,7 +66,7 @@ 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(); init_FG_BG_table();
memcpy(self->color_table, 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)); memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256));
self->dirty = true; self->dirty = true;
@ -80,12 +87,10 @@ alloc_color_profile() {
static PyObject* 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 256 basic colors"
index_type i;
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; } if (PyList_GET_SIZE(val) != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, "color table must have 256 items"); return NULL; }
for (i = 0; i < 16; i++) { for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i)); self->color_table[i] = PyLong_AsUnsignedLong(PyList_GET_ITEM(val, i));
self->orig_color_table[i] = self->color_table[i]; self->orig_color_table[i] = self->color_table[i];
} }
@ -184,6 +189,10 @@ color_table_address(ColorProfile *self, PyObject *a UNUSED) {
return PyLong_FromVoidPtr((void*)self->color_table); return PyLong_FromVoidPtr((void*)self->color_table);
} }
static PyObject*
default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
return create_256_color_table();
}
// Boilerplate {{{ // Boilerplate {{{
@ -236,5 +245,11 @@ PyTypeObject ColorProfile_Type = {
.tp_new = new, .tp_new = new,
}; };
static PyMethodDef module_methods[] = {
METHODB(default_color_table, METH_NOARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};
INIT_TYPE(ColorProfile) INIT_TYPE(ColorProfile)
// }}} // }}}

View File

@ -20,6 +20,7 @@ from .config_utils import (
from .constants import cache_dir, defconf from .constants import cache_dir, defconf
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts from .layout import all_layouts
from .rgb import color_from_int
from .utils import log_error from .utils import log_error
MINIMUM_FONT_SIZE = 4 MINIMUM_FONT_SIZE = 4
@ -333,8 +334,8 @@ for name in (
' selection_foreground selection_background url_color' ' selection_foreground selection_background url_color'
).split(): ).split():
type_map[name] = to_color type_map[name] = to_color
for i in range(16): for i in range(256):
type_map['color%d' % i] = to_color type_map['color{}'.format(i)] = to_color
for a in ('active', 'inactive'): for a in ('active', 'inactive'):
for b in ('foreground', 'background'): for b in ('foreground', 'background'):
type_map['%s_tab_%s' % (a, b)] = to_color type_map['%s_tab_%s' % (a, b)] = to_color
@ -376,7 +377,17 @@ def parse_config(lines, check_keys=True):
return ans return ans
Options, defaults = init_config(default_config_path, parse_config) def parse_defaults(lines, check_keys=False):
ans = parse_config(lines, check_keys)
dfctl = defines.default_color_table()
for i in range(16, 256):
k = 'color{}'.format(i)
ans.setdefault(k, color_from_int(dfctl[i]))
return ans
Options, defaults = init_config(default_config_path, parse_defaults)
actions = frozenset(all_key_actions) | frozenset( actions = frozenset(all_key_actions) | frozenset(
'combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'. 'combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
split() split()
@ -447,7 +458,7 @@ def load_config(*paths, overrides=None) -> Options:
if overrides is not None: if overrides is not None:
vals = parse_config(overrides) vals = parse_config(overrides)
ans = merge_configs(ans, vals) ans = merge_configs(ans, vals)
return Options(**ans) return Options(ans)
def build_ansi_color_table(opts: Options = defaults): def build_ansi_color_table(opts: Options = defaults):
@ -458,7 +469,7 @@ 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)))
return list(map(col, range(16))) return list(map(col, range(256)))
def atomic_save(data, path): def atomic_save(data, path):

View File

@ -4,7 +4,6 @@
import os import os
import re import re
from collections import namedtuple
from .rgb import to_color as as_color from .rgb import to_color as as_color
from .utils import log_error from .utils import log_error
@ -80,9 +79,25 @@ def parse_config_base(
_parse(lines, type_map, special_handling, ans, all_keys) _parse(lines, type_map, special_handling, ans, all_keys)
def create_options_class(keys):
keys = tuple(sorted(keys))
slots = keys + ('_fields',)
def __init__(self, kw):
for k, v in kw.items():
setattr(self, k, v)
def _asdict(self):
return {k: getattr(self, k) for k in self._fields}
ans = type('Options', (), {'__slots__': slots, '__init__': __init__, '_asdict': _asdict})
ans._fields = keys
return ans
def init_config(defaults_path, parse_config): def init_config(defaults_path, parse_config):
with open(defaults_path, encoding='utf-8', errors='replace') as f: with open(defaults_path, encoding='utf-8', errors='replace') as f:
defaults = parse_config(f, check_keys=False) defaults = parse_config(f, check_keys=False)
Options = namedtuple('Defaults', ','.join(defaults.keys())) Options = create_options_class(defaults.keys())
defaults = Options(**defaults) defaults = Options(defaults)
return Options, defaults return Options, defaults

View File

@ -247,7 +247,8 @@ selection_foreground #000000
selection_background #FFFACD selection_background #FFFACD
# The 16 terminal colors. There are 8 basic colors, each color has a dull and # The 16 terminal colors. There are 8 basic colors, each color has a dull and
# bright version. # bright version. You can also set the remaining colors from the 256 color table
# as color16 to color256.
# black # black
color0 #000000 color0 #000000

4
kitty/rgb.py generated
View File

@ -27,6 +27,10 @@ def parse_rgb(spec):
return Color(*map(parse_single_color, colors)) return Color(*map(parse_single_color, colors))
def color_from_int(x):
return Color((x >> 16) & 255, (x >> 8) & 255, x & 255)
def to_color(raw, validate=False): def to_color(raw, validate=False):
# See man XParseColor # See man XParseColor
x = raw.strip().lower() x = raw.strip().lower()