diff --git a/docs/changelog.rst b/docs/changelog.rst index 422184291..5abc24e52 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -78,6 +78,8 @@ Detailed list of changes - launch command: A new :option:`launch --bias` option to adjust the size of newly created windows declaratively (:iss:`7634`) +- A new option :opt:`second_transparent_bg` to make a second background color semi-transparent via :opt:`background_opacity`. Useful for things like cursor line highlight in editors (:iss:`7646`) + - Sessions: A new command ``focus_matching_window`` to shift focus to a specific window, useful when creating complex layouts with splits (:disc:`7635`) - Wayland: Allow fractional scales less than one (:pull:`7549`) diff --git a/gen/config.py b/gen/config.py index 7a3d979d6..c621aec33 100755 --- a/gen/config.py +++ b/gen/config.py @@ -42,7 +42,6 @@ def patch_color_list(path: str, colors: List[str], name: str, spc: str = ' ') def main(args: List[str]=sys.argv) -> None: from kitty.options.definition import definition - write_output('kitty', definition) nullable_colors = [] all_colors = [] for opt in definition.iter_all_options(): @@ -52,9 +51,10 @@ def main(args: List[str]=sys.argv) -> None: all_colors.append(opt.name) elif opt.parser_func.__name__ in ('to_color', 'titlebar_color', 'macos_titlebar_color'): all_colors.append(opt.name) - patch_color_list('kitty/rc/set_colors.py', nullable_colors, 'NULLABLE') patch_color_list('tools/cmd/at/set_colors.go', nullable_colors, 'NULLABLE') patch_color_list('tools/themes/collection.go', all_colors, 'ALL') + nc = '\n '.join(f'{x!r}' for x in nullable_colors) + write_output('kitty', definition, f'\nnullable_colors = frozenset({{\n {nc}\n}})') if __name__ == '__main__': diff --git a/kittens/query_terminal/main.py b/kittens/query_terminal/main.py index 06f30d884..dfc214bd0 100644 --- a/kittens/query_terminal/main.py +++ b/kittens/query_terminal/main.py @@ -179,14 +179,12 @@ class Foreground(Query): @staticmethod def get_result(opts: Options, window_id: int, os_window_id: int) -> str: - from kitty.fast_data_types import Color, get_boss + from kitty.fast_data_types import get_boss, get_options boss = get_boss() w = boss.window_id_map.get(window_id) if w is None: return opts.foreground.as_sharp - col = w.screen.color_profile.default_fg - r, g, b = col >> 16, (col >> 8) & 0xff, col & 0xff - return Color(r, g, b).as_sharp + return (w.screen.color_profile.default_fg or get_options().foreground).as_sharp @query @@ -196,15 +194,12 @@ class Background(Query): @staticmethod def get_result(opts: Options, window_id: int, os_window_id: int) -> str: - from kitty.fast_data_types import Color, get_boss + from kitty.fast_data_types import get_boss, get_options boss = get_boss() w = boss.window_id_map.get(window_id) if w is None: return opts.background.as_sharp - col = w.screen.color_profile.default_bg - r, g, b = col >> 16, (col >> 8) & 0xff, col & 0xff - return Color(r, g, b).as_sharp - + return (w.screen.color_profile.default_bg or get_options().background).as_sharp @query diff --git a/kitty/borders.py b/kitty/borders.py index 1257ffaac..1ccb49fe4 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -7,6 +7,7 @@ from typing import Iterable, NamedTuple, Sequence from .fast_data_types import BORDERS_PROGRAM, add_borders_rect, get_options, init_borders_program, os_window_has_background_image from .shaders import program_for from .typing import LayoutType +from .utils import color_as_int from .window_list import WindowGroup, WindowList @@ -96,7 +97,7 @@ class Borders: active_group = all_windows.active_group for i, wg in enumerate(groups): - window_bg = wg.default_bg + window_bg = color_as_int(wg.default_bg) window_bg = (window_bg << 8) | BorderColor.window_bg if draw_borders and not draw_minimal_borders: # Draw the border rectangles diff --git a/kitty/boss.py b/kitty/boss.py index a1d8a1bef..88cdfb3d3 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -122,7 +122,7 @@ from .key_encoding import get_name_to_functional_number_map from .keys import Mappings from .layout.base import set_layout_options from .notify import notification_activated -from .options.types import Options +from .options.types import Options, nullable_colors from .options.utils import MINIMUM_FONT_SIZE, KeyboardMode, KeyDefinition from .os_window_size import initial_window_size_func from .rgb import color_from_int @@ -342,9 +342,9 @@ class Boss: self.clipboard_buffers: Dict[str, str] = {} self.update_check_process: Optional['PopenType[bytes]'] = None self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary() - self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)} + self.color_settings_at_startup: Dict[str, Optional[Color]] = { + k: opts[k] for k in opts if isinstance(opts[k], Color) or k in nullable_colors} self.current_visual_select: Optional[VisualSelect] = None - self.startup_cursor_text_color = opts.cursor_text_color # A list of events received so far that are potentially part of a sequence keybinding. self.cached_values = cached_values self.os_window_map: Dict[int, TabManager] = {} @@ -2627,7 +2627,6 @@ class Boss: window.refresh() def patch_colors(self, spec: Dict[str, Optional[int]], configured: bool = False) -> None: - from kitty.rc.set_colors import nullable_colors opts = get_options() if configured: for k, v in spec.items(): diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index d12345c7a..4140eb5de 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -6,7 +6,7 @@ layout(std140) uniform CellRenderData { float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; - uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted; + uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; uint xnum, ynum, cursor_fg_sprite_idx; float cursor_x, cursor_y, cursor_w, cursor_opacity; @@ -194,7 +194,9 @@ void main() { // }}} // Background {{{ - float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[1]))); + float cell_has_non_default_bg = step(1, float(abs( + (bg_as_uint - default_colors[1]) * (bg_as_uint - second_transparent_bg) + ))); draw_bg = 1; #if (PHASE == PHASE_BACKGROUND) diff --git a/kitty/colors.c b/kitty/colors.c index d1250e069..b54946a13 100644 --- a/kitty/colors.c +++ b/kitty/colors.c @@ -47,7 +47,8 @@ init_FG_BG_table(void) { } } -PyObject* create_256_color_table(void) { +static PyObject* +create_256_color_table(void) { init_FG_BG_table(); PyObject *ans = PyTuple_New(arraysz(FG_BG_256)); if (ans == NULL) return PyErr_NoMemory(); @@ -59,21 +60,88 @@ PyObject* create_256_color_table(void) { return ans; } -static PyObject * -new_cp(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { - ColorProfile *self; +static bool +set_configured_colors(ColorProfile *self, PyObject *opts) { +#define n(which, attr) { \ + RAII_PyObject(t, PyObject_GetAttrString(opts, #attr)); \ + if (t == NULL) return false; \ + if (t == Py_None) { self->configured.which.rgb = 0; self->configured.which.type = COLOR_IS_SPECIAL; } \ + else if (PyLong_Check(t)) { \ + unsigned int x = PyLong_AsUnsignedLong(t); \ + self->configured.which.rgb = x & 0xffffff; \ + self->configured.which.type = COLOR_IS_RGB; \ + } else if (PyObject_TypeCheck(t, &Color_Type)) { \ + Color *c = (Color*)t; \ + self->configured.which.rgb = c->color.rgb; \ + self->configured.which.type = COLOR_IS_RGB; \ + } else { PyErr_SetString(PyExc_TypeError, "colors must be integers or Color objects"); return false; } \ +} + n(default_fg, foreground); n(default_bg, background); + n(cursor_color, cursor); n(cursor_text_color, cursor_text_color); + n(highlight_fg, selection_foreground); n(highlight_bg, selection_background); + n(visual_bell_color, visual_bell_color); n(second_transparent_bg, second_transparent_bg); +#undef n + return true; +} + +static bool +set_mark_colors(ColorProfile *self, PyObject *opts) { + char fgattr[] = "mark?_foreground", bgattr[] = "mark?_background"; +#define n(i, attr, which) { \ + attr[4] = '1' + i; \ + RAII_PyObject(t, PyObject_GetAttrString(opts, attr)); \ + if (t == NULL) return false; \ + if (!PyObject_TypeCheck(t, &Color_Type)) { PyErr_SetString(PyExc_TypeError, "mark color is not Color object"); return false; } \ + Color *c = (Color*)t; self->which[i] = c->color.rgb; \ +} +#define m(i) n(i, fgattr, mark_foregrounds); n(i, bgattr, mark_backgrounds); + m(0); m(1); m(2); +#undef m +#undef n + return true; +} + +static bool +set_colortable(ColorProfile *self, PyObject *opts) { + RAII_PyObject(ct, PyObject_GetAttrString(opts, "color_table")); + if (!ct) return false; + RAII_PyObject(ret, PyObject_CallMethod(ct, "buffer_info", NULL)); + if (!ret) return false; + unsigned long *color_table = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)); + size_t count = PyLong_AsSize_t(PyTuple_GET_ITEM(ret, 1)); + if (!color_table || count != arraysz(FG_BG_256)) { PyErr_SetString(PyExc_TypeError, "color_table has incorrect length"); return false; } + RAII_PyObject(r2, PyObject_GetAttrString(ct, "itemsize")); if (!r2) return false; + size_t itemsize = PyLong_AsSize_t(r2); + if (itemsize != sizeof(unsigned long)) { PyErr_Format(PyExc_TypeError, "color_table has incorrect itemsize: %zu", itemsize); return false; } + for (size_t i = 0; i < arraysz(FG_BG_256); i++) self->color_table[i] = color_table[i]; + memcpy(self->orig_color_table, self->color_table, arraysz(self->color_table) * sizeof(self->color_table[0])); + return true; +} + + +static PyObject* +new_cp(PyTypeObject *type, PyObject *args, PyObject *kwds) { + PyObject *opts = global_state.options_object; + ColorProfile *self; + static const char* kw[] = {"opts", NULL}; + if (args && !PyArg_ParseTupleAndKeywords(args, kwds, "|O", (char**)kw, &opts)) return NULL; self = (ColorProfile *)type->tp_alloc(type, 0); + RAII_PyObject(ans, (PyObject*)self); if (self != NULL) { init_FG_BG_table(); - memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256)); - memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256)); -#define S(which) self->mark_foregrounds[which] = OPT(mark##which##_foreground); self->mark_backgrounds[which] = OPT(mark##which##_background) - S(1); S(2); S(3); -#undef S + if (opts) { + if (!set_configured_colors(self, opts)) return NULL; + if (!set_mark_colors(self, opts)) return NULL; + if (!set_colortable(self, opts)) return NULL; + } else { + memcpy(self->color_table, FG_BG_256, sizeof(FG_BG_256)); + memcpy(self->orig_color_table, FG_BG_256, sizeof(FG_BG_256)); + } self->dirty = true; + Py_INCREF(ans); } - return (PyObject*) self; + return ans; } static void @@ -88,19 +156,6 @@ alloc_color_profile(void) { } -static PyObject* -update_ansi_color_table(ColorProfile *self, PyObject *val) { -#define update_ansi_color_table_doc "Update the 256 basic colors" - if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "color table must be a long"); return NULL; } - unsigned long *color_table = PyLong_AsVoidPtr(val); - for (size_t i = 0; i < arraysz(FG_BG_256); i++) { - self->color_table[i] = color_table[i]; - self->orig_color_table[i] = color_table[i]; - } - self->dirty = true; - Py_RETURN_NONE; -} - void copy_color_profile(ColorProfile *dest, ColorProfile *src) { memcpy(dest->color_table, src->color_table, sizeof(dest->color_table)); @@ -171,6 +226,7 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) { S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color); S(selection_foreground, highlight_fg); S(selection_background, highlight_bg); S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color); + S(second_transparent_bg, second_transparent_bg); #undef SI #undef S Py_RETURN_NONE; @@ -239,7 +295,7 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) { }} D(default_fg, foreground); D(default_bg, background); D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground); - D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color); + D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color); D(second_transparent_bg, second_transparent_bg); #undef D return ans; @@ -300,21 +356,6 @@ set_color(ColorProfile *self, PyObject *args) { Py_RETURN_NONE; } -static PyObject* -set_configured_colors(ColorProfile *self, PyObject *args) { -#define set_configured_colors_doc "Set the configured colors" - unsigned int default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color; - if (!PyArg_ParseTuple(args, "II|IIIII", &default_fg, &default_bg, - &cursor_color, &cursor_text_color, &highlight_fg, &highlight_bg, &visual_bell_color)) return NULL; -#define S(which) \ - self->configured.which.rgb = which & 0xffffff; \ - self->configured.which.type = (which & 0xff000000) ? COLOR_IS_RGB : COLOR_IS_SPECIAL; - S(default_fg); S(default_bg); S(cursor_color); S(cursor_text_color); S(highlight_fg); S(highlight_bg); S(visual_bell_color); -#undef S - self->dirty = true; - Py_RETURN_NONE; -} - void copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size_t stride) { size_t i; @@ -404,23 +445,39 @@ default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) { // Boilerplate {{{ -#define CGETSET(name) \ - static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { return PyLong_FromUnsignedLong(colorprofile_to_color(self, self->overridden.name, self->configured.name).rgb); } \ +static Color* alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a); +#define CGETSET(name, nullable) \ + static PyObject* name##_get(ColorProfile *self, void UNUSED *closure) { \ + DynamicColor ans = colorprofile_to_color(self, self->overridden.name, self->configured.name); \ + if (ans.type == COLOR_IS_SPECIAL) { \ + if (nullable) Py_RETURN_NONE; \ + return (PyObject*)alloc_color(0, 0, 0, 0); \ + } \ + return (PyObject*)alloc_color((ans.rgb >> 16) & 0xff, (ans.rgb >> 8) & 0xff, ans.rgb & 0xff, 0); \ + } \ static int name##_set(ColorProfile *self, PyObject *v, void UNUSED *closure) { \ - if (v == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete attribute: " #name); return -1; } \ - unsigned long val = PyLong_AsUnsignedLong(v); \ - self->overridden.name.rgb = val & 0xffffff; \ - self->overridden.name.type = (val & 0xff000000) ? COLOR_IS_RGB : COLOR_NOT_SET; \ + if (v == NULL) { self->overridden.name.val = 0; return 0; } \ + if (PyLong_Check(v)) { \ + unsigned long val = PyLong_AsUnsignedLong(v); \ + self->overridden.name.rgb = val & 0xffffff; \ + self->overridden.name.type = COLOR_IS_RGB; \ + } else if (PyObject_TypeCheck(v, &Color_Type)) { \ + Color *c = (Color*)v; self->overridden.name.rgb = c->color.rgb; self->overridden.name.type = COLOR_IS_RGB; \ + } else if (v == Py_None) { \ + if (!nullable) { PyErr_SetString(PyExc_ValueError, #name " cannot be set to None"); return -1; } \ + self->overridden.name.type = COLOR_IS_SPECIAL; self->overridden.name.rgb = 0; \ + } \ self->dirty = true; return 0; \ } -CGETSET(default_fg) -CGETSET(default_bg) -CGETSET(cursor_color) -CGETSET(cursor_text_color) -CGETSET(highlight_fg) -CGETSET(highlight_bg) -CGETSET(visual_bell_color) +CGETSET(default_fg, false) +CGETSET(default_bg, false) +CGETSET(cursor_color, true) +CGETSET(cursor_text_color, true) +CGETSET(highlight_fg, true) +CGETSET(highlight_bg, true) +CGETSET(visual_bell_color, true) +CGETSET(second_transparent_bg, true) #undef CGETSET static PyGetSetDef cp_getsetters[] = { @@ -431,6 +488,7 @@ static PyGetSetDef cp_getsetters[] = { GETSET(highlight_fg) GETSET(highlight_bg) GETSET(visual_bell_color) + GETSET(second_transparent_bg) {NULL} /* Sentinel */ }; @@ -439,15 +497,25 @@ static PyMemberDef cp_members[] = { {NULL} }; +static PyObject* +reload_from_opts(ColorProfile *self, PyObject *args UNUSED) { + PyObject *opts = global_state.options_object; + if (!PyArg_ParseTuple(args, "|O", &opts)) return NULL; + self->dirty = true; + if (!set_configured_colors(self, opts)) return NULL; + if (!set_mark_colors(self, opts)) return NULL; + if (!set_colortable(self, opts)) return NULL; + Py_RETURN_NONE; +} + static PyMethodDef cp_methods[] = { - METHOD(update_ansi_color_table, METH_O) METHOD(reset_color_table, METH_NOARGS) METHOD(as_dict, METH_NOARGS) METHOD(color_table_address, METH_NOARGS) METHOD(as_color, METH_O) METHOD(reset_color, METH_O) METHOD(set_color, METH_VARARGS) - METHOD(set_configured_colors, METH_VARARGS) + METHODB(reload_from_opts, METH_VARARGS), {NULL} /* Sentinel */ }; @@ -466,19 +534,21 @@ PyTypeObject ColorProfile_Type = { }; // }}} - -static PyObject * -new_color(PyTypeObject *type, PyObject *args, PyObject *kwds) { - static const char* kwlist[] = {"red", "green", "blue", "alpha", NULL}; - Color *self; - unsigned char r = 0, g = 0, b = 0, a = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|BBBB", (char**)kwlist, &r, &g, &b, &a)) return NULL; - - self = (Color *)type->tp_alloc(type, 0); +static Color* +alloc_color(unsigned char r, unsigned char g, unsigned char b, unsigned a) { + Color *self = (Color *)(&Color_Type)->tp_alloc(&Color_Type, 0); if (self != NULL) { self->color.r = r; self->color.g = g; self->color.b = b; self->color.a = a; } - return (PyObject*) self; + return self; +} + +static PyObject * +new_color(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds) { + static const char* kwlist[] = {"red", "green", "blue", "alpha", NULL}; + unsigned char r = 0, g = 0, b = 0, a = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|BBBB", (char**)kwlist, &r, &g, &b, &a)) return NULL; + return (PyObject*) alloc_color(r, g, b, a); } static PyObject* @@ -612,7 +682,6 @@ static PyMethodDef module_methods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; - int init_ColorProfile(PyObject *module) {\ if (PyType_Ready(&ColorProfile_Type) < 0) return 0; if (PyModule_AddObject(module, "ColorProfile", (PyObject *)&ColorProfile_Type) != 0) return 0; diff --git a/kitty/conf/generate.py b/kitty/conf/generate.py index a5f13f5aa..b3c59589f 100644 --- a/kitty/conf/generate.py +++ b/kitty/conf/generate.py @@ -435,10 +435,11 @@ def generate_c_conversion(loc: str, ctypes: List[Union[Option, MultiOption]]) -> return '\n'.join(preamble + ['', ''] + lines) -def write_output(loc: str, defn: Definition) -> None: +def write_output(loc: str, defn: Definition, extra_after_type_defn: str = '') -> None: cls, tc = generate_class(defn, loc) with open(os.path.join(*loc.split('.'), 'options', 'types.py'), 'w') as f: f.write(f'{cls}\n') + f.write(extra_after_type_defn) with open(os.path.join(*loc.split('.'), 'options', 'parse.py'), 'w') as f: f.write(f'{tc}\n') ctypes = [] diff --git a/kitty/config.py b/kitty/config.py index ba531baa8..75afbffcf 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -20,15 +20,6 @@ def option_names_for_completion() -> Tuple[str, ...]: return option_names -def build_ansi_color_table(opts: Optional[Options] = None) -> int: - if opts is None: - opts = defaults - addr, length = opts.color_table.buffer_info() - if length != 256 or opts.color_table.typecode != 'L': - raise TypeError(f'The color table has incorrect size length: {length} typecode: {opts.color_table.typecode}') - return addr - - def atomic_save(data: bytes, path: str) -> None: import shutil import tempfile diff --git a/kitty/data-types.h b/kitty/data-types.h index 3b88d18fc..2eac7cea2 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -319,22 +319,15 @@ typedef union DynamicColor { } DynamicColor; typedef struct { - DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color; + DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color, second_transparent_bg; } DynamicColors; - -typedef struct { - DynamicColors dynamic_colors; - uint32_t color_table[256]; -} ColorStackEntry; - typedef struct { PyObject_HEAD bool dirty; - uint32_t color_table[256]; - uint32_t orig_color_table[256]; - ColorStackEntry *color_stack; + uint32_t color_table[256], orig_color_table[256]; + struct { DynamicColors dynamic_colors; uint32_t color_table[256]; } *color_stack; unsigned int color_stack_idx, color_stack_sz; DynamicColors configured, overridden; color_type mark_foregrounds[MARK_MASK+1], mark_backgrounds[MARK_MASK+1]; @@ -389,7 +382,6 @@ LineBuf* alloc_linebuf(unsigned int, unsigned int); HistoryBuf* alloc_historybuf(unsigned int, unsigned int, unsigned int); ColorProfile* alloc_color_profile(void); void copy_color_profile(ColorProfile*, ColorProfile*); -PyObject* create_256_color_table(void); PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *); PyObject* parse_bytes(PyObject UNUSED *, PyObject *); void cursor_reset(Cursor*); diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 249b7ea9d..23f7c3899 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -755,8 +755,49 @@ class Color: class ColorProfile: - default_fg: int - default_bg: int + # The dynamic color properties return the current color value. Use delattr + # to reset them to configured value. + @property + def default_fg(self) -> Color: ... + @default_fg.setter + def default_fg(self, val: Union[int|Color]) -> None: ... + + @property + def default_bg(self) -> Color: ... + @default_bg.setter + def default_bg(self, val: Union[int|Color]) -> None: ... + + @property + def cursor_color(self) -> Optional[Color]: ... + @cursor_color.setter + def cursor_color(self, val: Union[None|int|Color]) -> None: ... + + @property + def cursor_text_color(self) -> Optional[Color]: ... + @cursor_text_color.setter + def cursor_text_color(self, val: Union[None|int|Color]) -> None: ... + + @property + def highlight_fg(self) -> Optional[Color]: ... + @highlight_fg.setter + def highlight_fg(self, val: Union[None|int|Color]) -> None: ... + + @property + def highlight_bg(self) -> Optional[Color]: ... + @highlight_bg.setter + def highlight_bg(self, val: Union[None|int|Color]) -> None: ... + + @property + def visual_bell_color(self) -> Optional[Color]: ... + @visual_bell_color.setter + def visual_bell_color(self, val: Union[None|int|Color]) -> None: ... + + @property + def second_transparent_bg(self) -> Optional[Color]: ... + @second_transparent_bg.setter + def second_transparent_bg(self, val: Union[None|int|Color]) -> None: ... + + def __init__(self, opts: Optional[Options] = None): ... def as_dict(self) -> Dict[str, Optional[int]]: pass @@ -773,13 +814,7 @@ class ColorProfile: def reset_color(self, num: int) -> None: pass - def update_ansi_color_table(self, val: int) -> None: - pass - - def set_configured_colors( - self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, highlight_fg: int = 0, highlight_bg: int = 0, visual_bell_color: int = 0 - ) -> None: - pass + def reload_from_opts(self, opts: Optional[Options] = None) -> None: ... def patch_color_profiles( diff --git a/kitty/options/definition.py b/kitty/options/definition.py index fba34b497..30737f757 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -1471,7 +1471,8 @@ theme with a background color in your editor, it will not be rendered as transparent. Instead you should change the default background color in your kitty config and not use a background color in the editor color scheme. Or use the escape codes to set the terminals default colors in a shell script to -launch your editor. Be aware that using a value less than 1.0 is a (possibly +launch your editor. See also :opt:`second_transparent_bg`. +Be aware that using a value less than 1.0 is a (possibly significant) performance hit. When using a low value for this setting, it is desirable that you set the :opt:`background` color to a color the matches the general color of the desktop background, for best text rendering. If you want @@ -1516,6 +1517,13 @@ opt('background_image_linear', 'no', long_text='When background image is scaled, whether linear interpolation should be used.' ) +opt('second_transparent_bg', 'none', option_type='to_color_or_none', long_text=''' +When the background color matches this color, :opt:`background_opacity` is applied to it +to render it as semi-transparent, just as for colors matching the background color. +Useful, in more complex UIs like editors where you could want more than a single background color +to be rendered as transparent. +''') + opt('dynamic_background_opacity', 'no', option_type='to_bool', ctype='bool', long_text=''' @@ -1651,32 +1659,32 @@ opt('color15', '#ffffff', ) opt('mark1_foreground', 'black', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 1' ) opt('mark1_background', '#98d3cb', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 1 (light steel blue)' ) opt('mark2_foreground', 'black', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 2' ) opt('mark2_background', '#f2dcd3', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 1 (beige)' ) opt('mark3_foreground', 'black', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 3' ) opt('mark3_background', '#f274bc', - option_type='to_color', ctype='color_as_int', + option_type='to_color', long_text='Color for marks of type 3 (violet)' ) diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 2ad54127d..b7b011cd9 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1189,6 +1189,9 @@ class Parser: def scrollback_pager_history_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['scrollback_pager_history_size'] = scrollback_pager_history_size(val) + def second_transparent_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['second_transparent_bg'] = to_color_or_none(val) + def select_by_word_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['select_by_word_characters'] = str(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index a375696a3..949905b9c 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -902,84 +902,6 @@ convert_from_opts_dim_opacity(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } -static void -convert_from_python_mark1_foreground(PyObject *val, Options *opts) { - opts->mark1_foreground = color_as_int(val); -} - -static void -convert_from_opts_mark1_foreground(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark1_foreground"); - if (ret == NULL) return; - convert_from_python_mark1_foreground(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_mark1_background(PyObject *val, Options *opts) { - opts->mark1_background = color_as_int(val); -} - -static void -convert_from_opts_mark1_background(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark1_background"); - if (ret == NULL) return; - convert_from_python_mark1_background(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_mark2_foreground(PyObject *val, Options *opts) { - opts->mark2_foreground = color_as_int(val); -} - -static void -convert_from_opts_mark2_foreground(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark2_foreground"); - if (ret == NULL) return; - convert_from_python_mark2_foreground(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_mark2_background(PyObject *val, Options *opts) { - opts->mark2_background = color_as_int(val); -} - -static void -convert_from_opts_mark2_background(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark2_background"); - if (ret == NULL) return; - convert_from_python_mark2_background(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_mark3_foreground(PyObject *val, Options *opts) { - opts->mark3_foreground = color_as_int(val); -} - -static void -convert_from_opts_mark3_foreground(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark3_foreground"); - if (ret == NULL) return; - convert_from_python_mark3_foreground(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_mark3_background(PyObject *val, Options *opts) { - opts->mark3_background = color_as_int(val); -} - -static void -convert_from_opts_mark3_background(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "mark3_background"); - if (ret == NULL) return; - convert_from_python_mark3_background(ret, opts); - Py_DECREF(ret); -} - static void convert_from_python_close_on_child_death(PyObject *val, Options *opts) { opts->close_on_child_death = PyObject_IsTrue(val); @@ -1315,18 +1237,6 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_dim_opacity(py_opts, opts); if (PyErr_Occurred()) return false; - convert_from_opts_mark1_foreground(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_mark1_background(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_mark2_foreground(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_mark2_background(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_mark3_foreground(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_mark3_background(py_opts, opts); - if (PyErr_Occurred()) return false; convert_from_opts_close_on_child_death(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_allow_hyperlinks(py_opts, opts); diff --git a/kitty/options/types.py b/kitty/options/types.py index b1424f20e..af3757dfe 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -408,6 +408,7 @@ option_names = ( # {{{ 'scrollback_lines', 'scrollback_pager', 'scrollback_pager_history_size', + 'second_transparent_bg', 'select_by_word_characters', 'select_by_word_characters_forward', 'selection_background', @@ -572,6 +573,7 @@ class Options: scrollback_lines: int = 2000 scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER'] scrollback_pager_history_size: int = 0 + second_transparent_bg: typing.Optional[kitty.fast_data_types.Color] = None select_by_word_characters: str = '@-./_~?&=%+#' select_by_word_characters_forward: str = '' selection_background: typing.Optional[kitty.fast_data_types.Color] = Color(255, 250, 205) @@ -1022,3 +1024,15 @@ defaults.mouse_map = [ # show_clicked_cmd_output_ungrabbed MouseMapping(button=1, mods=5, definition='mouse_show_command_output'), ] + +nullable_colors = frozenset({ + 'cursor' + 'cursor_text_color' + 'visual_bell_color' + 'active_border_color' + 'tab_bar_background' + 'tab_bar_margin_color' + 'second_transparent_bg' + 'selection_foreground' + 'selection_background' +}) \ No newline at end of file diff --git a/kitty/rc/set_colors.py b/kitty/rc/set_colors.py index 5a62f0dfe..7d231f43f 100644 --- a/kitty/rc/set_colors.py +++ b/kitty/rc/set_colors.py @@ -27,22 +27,8 @@ if TYPE_CHECKING: from kitty.cli_stub import SetColorsRCOptions as CLIOptions -nullable_colors = ( - # generated by gen-config.py DO NOT edit - # NULLABLE_COLORS_START - 'active_border_color', - 'cursor', - 'cursor_text_color', - 'selection_background', - 'selection_foreground', - 'tab_bar_background', - 'tab_bar_margin_color', - 'visual_bell_color', - # NULLABLE_COLORS_END -) - - def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]: + from kitty.options.types import nullable_colors colors: Dict[str, Optional[Color]] = {} nullable_color_map: Dict[str, Optional[int]] = {} for spec in args: @@ -121,8 +107,7 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors -- windows = self.windows_for_payload(boss, window, payload_get) colors: Dict[str, Optional[int]] = payload_get('colors') if payload_get('reset'): - colors = {k: int(v) for k, v in boss.startup_colors.items()} - colors['cursor_text_color'] = None if boss.startup_cursor_text_color is None else int(boss.startup_cursor_text_color) + colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()} profiles = tuple(w.screen.color_profile for w in windows if w) patch_color_profiles(colors, profiles, payload_get('configured')) boss.patch_colors(colors, payload_get('configured')) diff --git a/kitty/shaders.c b/kitty/shaders.c index 6af3ea1a2..1cad02886 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -295,7 +295,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c struct GPUCellRenderData { GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg; - GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted; + GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; GLuint xnum, ynum, cursor_fg_sprite_idx; GLfloat cursor_x, cursor_y, cursor_w, cursor_opacity; @@ -320,6 +320,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c rd->use_cell_bg_for_selection_fg = 0.f; rd->use_cell_fg_for_selection_color = 0.f; } rd->use_cell_for_selection_bg = IS_SPECIAL_COLOR(highlight_bg) ? 1. : 0.; + rd->second_transparent_bg = IS_SPECIAL_COLOR(second_transparent_bg) ? rd->default_bg : COLOR(second_transparent_bg); // Cursor position enum { BLOCK_IDX = 0, BEAM_IDX = NUM_UNDERLINE_STYLES + 3, UNDERLINE_IDX = NUM_UNDERLINE_STYLES + 4, UNFOCUSED_IDX = NUM_UNDERLINE_STYLES + 5 }; Line *line_for_cursor = NULL; diff --git a/kitty/state.c b/kitty/state.c index f396c9a67..16c1d1306 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -719,15 +719,14 @@ PYWRAP1(handle_for_window_id) { return NULL; } -static PyObject* options_object = NULL; PYWRAP0(get_options) { - if (!options_object) { + if (!global_state.options_object) { PyErr_SetString(PyExc_RuntimeError, "Must call set_options() before using get_options()"); return NULL; } - Py_INCREF(options_object); - return options_object; + Py_INCREF(global_state.options_object); + return global_state.options_object; } PYWRAP1(set_options) { @@ -735,7 +734,7 @@ PYWRAP1(set_options) { int is_wayland = 0, debug_rendering = 0, debug_font_fallback = 0; PA("O|ppp", &opts, &is_wayland, &debug_rendering, &debug_font_fallback); if (opts == Py_None) { - Py_CLEAR(options_object); + Py_CLEAR(global_state.options_object); Py_RETURN_NONE; } global_state.is_wayland = is_wayland ? true : false; @@ -746,8 +745,8 @@ PYWRAP1(set_options) { global_state.debug_rendering = debug_rendering ? true : false; global_state.debug_font_fallback = debug_font_fallback ? true : false; if (!convert_opts_from_python_opts(opts, &global_state.opts)) return NULL; - options_object = opts; - Py_INCREF(options_object); + global_state.options_object = opts; + Py_INCREF(global_state.options_object); Py_RETURN_NONE; } @@ -1159,8 +1158,6 @@ PYWRAP1(patch_global_colors) { P(active_border_color); P(inactive_border_color); P(bell_border_color); P(tab_bar_background); P(tab_bar_margin_color); if (configured) { P(background); P(url_color); - P(mark1_background); P(mark1_foreground); P(mark2_background); P(mark2_foreground); - P(mark3_background); P(mark3_foreground); } if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; @@ -1461,7 +1458,7 @@ finalize(void) { #define F(x) free(OPT(x)); OPT(x) = NULL; F(background_image); F(bell_path); F(bell_theme); F(default_window_logo); #undef F - Py_CLEAR(options_object); + Py_CLEAR(global_state.options_object); free_animation(OPT(animation.cursor)); free_animation(OPT(animation.visual_bell)); // we leak the texture here since it is not guaranteed diff --git a/kitty/state.h b/kitty/state.h index b8afcba79..8c75dd09f 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -52,7 +52,6 @@ typedef struct { char_type *select_by_word_characters; char_type *select_by_word_characters_forward; color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color; - color_type mark1_foreground, mark1_background, mark2_foreground, mark2_background, mark3_foreground, mark3_background; monotonic_t repaint_delay, input_delay; bool focus_follows_mouse; unsigned int hide_window_decorations; @@ -307,6 +306,7 @@ typedef struct { bool redirect_mouse_handling; WindowLogoTable *all_window_logos; int gl_version; + PyObject *options_object; } GlobalState; extern GlobalState global_state; diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 69a0c062f..17a55e0bc 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -17,7 +17,6 @@ from typing import ( ) from .borders import Border, BorderColor -from .config import build_ansi_color_table from .constants import config_dir from .fast_data_types import ( DECAWM, @@ -495,11 +494,8 @@ class TabBar: self.screen = s = Screen(None, 1, 10, 0, self.cell_width, cell_height) else: s = self.screen - s.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) - s.color_profile.set_configured_colors( - color_as_int(opts.inactive_tab_foreground), - color_as_int(opts.tab_bar_background or opts.background) - ) + s.color_profile.default_fg = opts.inactive_tab_foreground + s.color_profile.default_bg = opts.tab_bar_background or opts.background sep = opts.tab_separator self.trailing_spaces = self.leading_spaces = 0 while sep and sep[0] == ' ': @@ -575,8 +571,9 @@ class TabBar: ifg = color_from_int(fg) if ifg is not None: self.draw_data = self.draw_data._replace(inactive_fg=ifg) - self.screen.color_profile.set_configured_colors(fg, bg) - self.screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) + self.screen.color_profile.reload_from_opts() + self.screen.color_profile.default_fg = color_from_int(fg) + self.screen.color_profile.default_bg = color_from_int(bg) @property def current_colors(self) -> Dict[str, Color]: diff --git a/kitty/window.py b/kitty/window.py index e04724c33..d0ea75be7 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -33,7 +33,6 @@ from typing import ( from .child import ProcessDesc from .cli_stub import CLIOptions from .clipboard import ClipboardRequestManager, set_clipboard_string -from .config import build_ansi_color_table from .constants import ( appname, clear_handled_signals, @@ -98,7 +97,6 @@ from .notify import ( notify_with_command, sanitize_identifier_pat, ) -from .options.types import Options from .rgb import to_color from .terminfo import get_capabilities from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once @@ -407,19 +405,6 @@ def as_text( -def setup_colors(screen: Screen, opts: Options) -> None: - screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) - - def s(c: Optional[Color]) -> int: - return 0 if c is None else (0xff000000 | int(c)) - screen.color_profile.set_configured_colors( - s(opts.foreground), s(opts.background), - s(opts.cursor), s(opts.cursor_text_color), - s(opts.selection_foreground), s(opts.selection_background), - s(opts.visual_bell_color) - ) - - @run_once def load_paste_filter() -> Callable[[str], str]: import runpy @@ -605,8 +590,6 @@ class Window: self.screen: Screen = Screen(self, 24, 80, opts.scrollback_lines, cell_width, cell_height, self.id) if copy_colors_from is not None: self.screen.copy_colors_from(copy_colors_from.screen) - else: - setup_colors(self.screen, opts) self.remote_control_passwords = remote_control_passwords self.allow_remote_control = allow_remote_control @@ -681,9 +664,8 @@ class Window: return int(val) def apply_options(self, is_active: bool) -> None: - opts = get_options() self.update_effective_padding() - setup_colors(self.screen, opts) + self.screen.color_profile.reload_from_opts() @property def title(self) -> str: @@ -1188,33 +1170,12 @@ class Window: tab.relayout_borders() tab.on_bell(self) - def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None: - dirtied = default_bg_changed = False - - def item(raw: Optional[str]) -> int: - if raw is None: - return 0 - v = to_color(raw) - if v is None: - return 0 - return 0xff000000 | int(v) - - for which, val_ in changes.items(): - val = item(val_) - dirtied = True - setattr(self.screen.color_profile, which.name, val) - if which.name == 'default_bg': - default_bg_changed = True - if dirtied: - self.screen.mark_as_dirty() - if default_bg_changed: - get_boss().default_bg_changed_for(self.id) - def color_profile_popped(self, bg_changed: bool) -> None: if bg_changed: get_boss().default_bg_changed_for(self.id) - def report_color(self, code: str, r: int, g: int, b: int) -> None: + def report_color(self, code: str, col: Color) -> None: + r, g, b = col.red, col.green, col.blue r |= r << 8 g |= g << 8 b |= b << 8 @@ -1236,19 +1197,33 @@ class Window: ret = set_pointer_shape(self.screen, value, self.os_window_id) if ret: self.screen.send_escape_code_to_child(ESC_OSC, '22:' + ret) - color_changes: Dict[DynamicColor, Optional[str]] = {} + + dirtied = default_bg_changed = False + def change(which: DynamicColor, val: str) -> None: + nonlocal dirtied, default_bg_changed + dirtied = True + if which.name == 'default_bg': + default_bg_changed = True + v = to_color(val) if val else None + if v is None: + delattr(self.screen.color_profile, which.name) + else: + setattr(self.screen.color_profile, which.name, v) + for val in value.split(';'): w = DYNAMIC_COLOR_CODES.get(code) if w is not None: if val == '?': - col = getattr(self.screen.color_profile, w.name) - self.report_color(str(code), col >> 16, (col >> 8) & 0xff, col & 0xff) + col = getattr(self.screen.color_profile, w.name) or Color() + self.report_color(str(code), col) else: - q = None if code >= 100 else val - color_changes[w] = q + q = '' if code >= 100 else val + change(w, q) code += 1 - if color_changes: - self.change_colors(color_changes) + if dirtied: + self.screen.mark_as_dirty() + if default_bg_changed: + get_boss().default_bg_changed_for(self.id) def set_color_table_color(self, code: int, bvalue: Optional[memoryview] = None) -> None: value = str(bvalue or b'', 'utf-8', 'replace') @@ -1259,7 +1234,7 @@ class Window: if val is None: # color query qc = self.screen.color_profile.as_color((c << 8) | 1) assert qc is not None - self.report_color(f'4;{c}', qc.red, qc.green, qc.blue) + self.report_color(f'4;{c}', qc) else: changed = True cp.set_color(c, val) diff --git a/kitty/window_list.py b/kitty/window_list.py index 04db0c11b..39fb96582 100644 --- a/kitty/window_list.py +++ b/kitty/window_list.py @@ -7,6 +7,7 @@ from contextlib import suppress from itertools import count from typing import Any, Deque, Dict, Iterator, List, Optional, Tuple, Union +from .fast_data_types import Color, get_options from .types import OverlayType, WindowGeometry from .typing import EdgeLiteral, TabType, WindowType @@ -120,23 +121,23 @@ class WindowGroup: w.set_geometry(geom) @property - def default_bg(self) -> int: + def default_bg(self) -> Color: if self.windows: - w: WindowType = self.windows[-1] - return w.screen.color_profile.default_bg - return 0 + w = self.windows[-1] + return w.screen.color_profile.default_bg or get_options().background + return get_options().background @property def geometry(self) -> Optional[WindowGeometry]: if self.windows: - w: WindowType = self.windows[-1] + w = self.windows[-1] return w.geometry return None @property def is_visible_in_layout(self) -> bool: if self.windows: - w: WindowType = self.windows[-1] + w = self.windows[-1] return w.is_visible_in_layout return False diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index dee3e645c..75410c66e 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -5,7 +5,7 @@ import os import sys import tempfile -from kitty.config import build_ansi_color_table, defaults +from kitty.config import defaults from kitty.fast_data_types import ( Color, ColorProfile, @@ -477,8 +477,7 @@ class TestDataTypes(BaseTest): 'h://a\u0430b.com/El%20Ni%C3%B1o/'), 'h://xn--ab-7kc.com/El NiƱo/') def test_color_profile(self): - c = ColorProfile() - c.update_ansi_color_table(build_ansi_color_table()) + c = ColorProfile(defaults) for i in range(8): col = getattr(defaults, f'color{i}') self.ae(c.as_color(i << 8 | 1), col) diff --git a/tools/cmd/at/set_colors.go b/tools/cmd/at/set_colors.go index 0dbe5ddc0..ff2ba9c97 100644 --- a/tools/cmd/at/set_colors.go +++ b/tools/cmd/at/set_colors.go @@ -15,14 +15,15 @@ import ( var nullable_colors = map[string]bool{ // generated by gen-config.py do not edit // NULLABLE_COLORS_START - "active_border_color": true, - "cursor": true, - "cursor_text_color": true, - "selection_background": true, - "selection_foreground": true, - "tab_bar_background": true, - "tab_bar_margin_color": true, - "visual_bell_color": true, + "active_border_color": true, + "cursor": true, + "cursor_text_color": true, + "second_transparent_bg": true, + "selection_background": true, + "selection_foreground": true, + "tab_bar_background": true, + "tab_bar_margin_color": true, + "visual_bell_color": true, // NULLABLE_COLORS_END } diff --git a/tools/themes/collection.go b/tools/themes/collection.go index 409a7d536..02a846c5a 100644 --- a/tools/themes/collection.go +++ b/tools/themes/collection.go @@ -309,6 +309,7 @@ var AllColorSettingNames = map[string]bool{ // {{{ "mark2_foreground": true, "mark3_background": true, "mark3_foreground": true, + "second_transparent_bg": true, "selection_background": true, "selection_foreground": true, "tab_bar_background": true,