Use a faster implementation of the Color type
Now implemented in C
This commit is contained in:
parent
40c046f86b
commit
2443dc135c
44
kittens/diff/options/types.py
generated
44
kittens/diff/options/types.py
generated
@ -3,8 +3,8 @@
|
|||||||
import typing
|
import typing
|
||||||
from kitty.conf.utils import KeyAction, KittensKeyMap
|
from kitty.conf.utils import KeyAction, KittensKeyMap
|
||||||
import kitty.conf.utils
|
import kitty.conf.utils
|
||||||
from kitty.rgb import Color
|
from kitty.fast_data_types import Color
|
||||||
import kitty.rgb
|
import kitty.fast_data_types
|
||||||
from kitty.types import ParsedShortcut
|
from kitty.types import ParsedShortcut
|
||||||
import kitty.types
|
import kitty.types
|
||||||
|
|
||||||
@ -39,31 +39,31 @@ option_names = ( # {{{
|
|||||||
|
|
||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
added_bg: Color = Color(red=230, green=255, blue=237)
|
added_bg: Color = Color(230, 255, 237)
|
||||||
added_margin_bg: Color = Color(red=205, green=255, blue=216)
|
added_margin_bg: Color = Color(205, 255, 216)
|
||||||
background: Color = Color(red=255, green=255, blue=255)
|
background: Color = Color(255, 255, 255)
|
||||||
diff_cmd: str = 'auto'
|
diff_cmd: str = 'auto'
|
||||||
filler_bg: Color = Color(red=250, green=251, blue=252)
|
filler_bg: Color = Color(250, 251, 252)
|
||||||
foreground: Color = Color(red=0, green=0, blue=0)
|
foreground: Color = Color(0, 0, 0)
|
||||||
highlight_added_bg: Color = Color(red=172, green=242, blue=189)
|
highlight_added_bg: Color = Color(172, 242, 189)
|
||||||
highlight_removed_bg: Color = Color(red=253, green=184, blue=192)
|
highlight_removed_bg: Color = Color(253, 184, 192)
|
||||||
hunk_bg: Color = Color(red=241, green=248, blue=255)
|
hunk_bg: Color = Color(241, 248, 255)
|
||||||
hunk_margin_bg: Color = Color(red=219, green=237, blue=255)
|
hunk_margin_bg: Color = Color(219, 237, 255)
|
||||||
margin_bg: Color = Color(red=250, green=251, blue=252)
|
margin_bg: Color = Color(250, 251, 252)
|
||||||
margin_fg: Color = Color(red=170, green=170, blue=170)
|
margin_fg: Color = Color(170, 170, 170)
|
||||||
margin_filler_bg: typing.Optional[kitty.rgb.Color] = None
|
margin_filler_bg: typing.Optional[kitty.fast_data_types.Color] = None
|
||||||
num_context_lines: int = 3
|
num_context_lines: int = 3
|
||||||
pygments_style: str = 'default'
|
pygments_style: str = 'default'
|
||||||
removed_bg: Color = Color(red=255, green=238, blue=240)
|
removed_bg: Color = Color(255, 238, 240)
|
||||||
removed_margin_bg: Color = Color(red=255, green=220, blue=224)
|
removed_margin_bg: Color = Color(255, 220, 224)
|
||||||
replace_tab_by: str = ' '
|
replace_tab_by: str = ' '
|
||||||
search_bg: Color = Color(red=68, green=68, blue=68)
|
search_bg: Color = Color(68, 68, 68)
|
||||||
search_fg: Color = Color(red=255, green=255, blue=255)
|
search_fg: Color = Color(255, 255, 255)
|
||||||
select_bg: Color = Color(red=180, green=213, blue=254)
|
select_bg: Color = Color(180, 213, 254)
|
||||||
select_fg: typing.Optional[kitty.rgb.Color] = Color(red=0, green=0, blue=0)
|
select_fg: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0)
|
||||||
syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'}
|
syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'}
|
||||||
title_bg: Color = Color(red=255, green=255, blue=255)
|
title_bg: Color = Color(255, 255, 255)
|
||||||
title_fg: Color = Color(red=0, green=0, blue=0)
|
title_fg: Color = Color(0, 0, 0)
|
||||||
map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = []
|
map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = []
|
||||||
key_definitions: KittensKeyMap = {}
|
key_definitions: KittensKeyMap = {}
|
||||||
config_paths: typing.Tuple[str, ...] = ()
|
config_paths: typing.Tuple[str, ...] = ()
|
||||||
|
|||||||
@ -18,7 +18,7 @@ from urllib.request import Request, urlopen
|
|||||||
from kitty.config import atomic_save, parse_config
|
from kitty.config import atomic_save, parse_config
|
||||||
from kitty.constants import cache_dir, config_dir
|
from kitty.constants import cache_dir, config_dir
|
||||||
from kitty.options.types import Options as KittyOptions
|
from kitty.options.types import Options as KittyOptions
|
||||||
from kitty.rgb import Color
|
from kitty.fast_data_types import Color
|
||||||
from kitty.utils import reload_conf_in_all_kitties
|
from kitty.utils import reload_conf_in_all_kitties
|
||||||
|
|
||||||
from ..choose.match import match
|
from ..choose.match import match
|
||||||
|
|||||||
@ -6,10 +6,11 @@ from contextlib import contextmanager
|
|||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import (
|
from typing import (
|
||||||
IO, Any, Callable, Dict, Generator, Optional, Tuple, TypeVar, Union
|
IO, Any, Callable, Dict, Generator, Optional, TypeVar, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from kitty.rgb import Color, color_as_sharp, to_color
|
from kitty.fast_data_types import Color
|
||||||
|
from kitty.rgb import color_as_sharp, to_color
|
||||||
from kitty.typing import GraphicsCommandType, HandlerType, ScreenSize
|
from kitty.typing import GraphicsCommandType, HandlerType, ScreenSize
|
||||||
|
|
||||||
from .operations_stub import CMD
|
from .operations_stub import CMD
|
||||||
@ -170,7 +171,7 @@ UNDERLINE_STYLES = {name: i + 1 for i, name in enumerate(
|
|||||||
'straight double curly'.split())}
|
'straight double curly'.split())}
|
||||||
|
|
||||||
|
|
||||||
ColorSpec = Union[int, str, Tuple[int, int, int]]
|
ColorSpec = Union[int, str, Color]
|
||||||
|
|
||||||
|
|
||||||
def color_code(color: ColorSpec, intense: bool = False, base: int = 30) -> str:
|
def color_code(color: ColorSpec, intense: bool = False, base: int = 30) -> str:
|
||||||
@ -179,7 +180,7 @@ def color_code(color: ColorSpec, intense: bool = False, base: int = 30) -> str:
|
|||||||
elif isinstance(color, int):
|
elif isinstance(color, int):
|
||||||
e = f'{base + 8}:5:{max(0, min(color, 255))}'
|
e = f'{base + 8}:5:{max(0, min(color, 255))}'
|
||||||
else:
|
else:
|
||||||
e = '{}:2:{}:{}:{}'.format(base + 8, *color)
|
e = f'{base + 8}{color.as_sgr}'
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
|
||||||
@ -454,7 +455,7 @@ def as_type_stub() -> str:
|
|||||||
ans = [
|
ans = [
|
||||||
'from typing import * # noqa',
|
'from typing import * # noqa',
|
||||||
'from kitty.typing import GraphicsCommandType, ScreenSize',
|
'from kitty.typing import GraphicsCommandType, ScreenSize',
|
||||||
'from kitty.rgb import Color',
|
'from kitty.fast_data_types import Color',
|
||||||
'import kitty.rgb',
|
'import kitty.rgb',
|
||||||
'import kittens.tui.operations',
|
'import kittens.tui.operations',
|
||||||
]
|
]
|
||||||
|
|||||||
@ -36,7 +36,7 @@ from .fast_data_types import (
|
|||||||
os_window_font_size, patch_global_colors, redirect_mouse_handling,
|
os_window_font_size, patch_global_colors, redirect_mouse_handling,
|
||||||
ring_bell, safe_pipe, set_application_quit_request, set_background_image,
|
ring_bell, safe_pipe, set_application_quit_request, set_background_image,
|
||||||
set_boss, set_clipboard_string, set_in_sequence_mode, set_options,
|
set_boss, set_clipboard_string, set_in_sequence_mode, set_options,
|
||||||
set_os_window_size, thread_write, toggle_fullscreen, toggle_maximized
|
set_os_window_size, thread_write, toggle_fullscreen, toggle_maximized, Color
|
||||||
)
|
)
|
||||||
from .key_encoding import get_name_to_functional_number_map
|
from .key_encoding import get_name_to_functional_number_map
|
||||||
from .keys import get_shortcut, shortcut_matches
|
from .keys import get_shortcut, shortcut_matches
|
||||||
@ -45,7 +45,7 @@ from .notify import notification_activated
|
|||||||
from .options.types import Options
|
from .options.types import Options
|
||||||
from .options.utils import MINIMUM_FONT_SIZE, SubSequenceMap
|
from .options.utils import MINIMUM_FONT_SIZE, SubSequenceMap
|
||||||
from .os_window_size import initial_window_size_func
|
from .os_window_size import initial_window_size_func
|
||||||
from .rgb import Color, color_from_int
|
from .rgb import color_from_int
|
||||||
from .session import Session, create_sessions, get_os_window_sizing_data
|
from .session import Session, create_sessions, get_os_window_sizing_data
|
||||||
from .tabs import (
|
from .tabs import (
|
||||||
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
||||||
|
|||||||
175
kitty/colors.c
175
kitty/colors.c
@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
#include "colors.h"
|
||||||
|
|
||||||
PyTypeObject ColorProfile_Type;
|
|
||||||
|
|
||||||
static uint32_t FG_BG_256[256] = {
|
static uint32_t FG_BG_256[256] = {
|
||||||
0x000000, // 0
|
0x000000, // 0
|
||||||
@ -60,7 +60,7 @@ PyObject* create_256_color_table() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
new_cp(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||||
ColorProfile *self;
|
ColorProfile *self;
|
||||||
|
|
||||||
self = (ColorProfile *)type->tp_alloc(type, 0);
|
self = (ColorProfile *)type->tp_alloc(type, 0);
|
||||||
@ -77,14 +77,14 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dealloc(ColorProfile* self) {
|
dealloc_cp(ColorProfile* self) {
|
||||||
if (self->color_stack) free(self->color_stack);
|
if (self->color_stack) free(self->color_stack);
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorProfile*
|
ColorProfile*
|
||||||
alloc_color_profile() {
|
alloc_color_profile() {
|
||||||
return (ColorProfile*)new(&ColorProfile_Type, NULL, NULL);
|
return (ColorProfile*)new_cp(&ColorProfile_Type, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -248,7 +248,6 @@ as_color(ColorProfile *self, PyObject *val) {
|
|||||||
unsigned int t = entry & 0xFF;
|
unsigned int t = entry & 0xFF;
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
uint32_t col = 0;
|
uint32_t col = 0;
|
||||||
PyObject *ans = NULL;
|
|
||||||
switch(t) {
|
switch(t) {
|
||||||
case 1:
|
case 1:
|
||||||
r = (entry >> 8) & 0xff;
|
r = (entry >> 8) & 0xff;
|
||||||
@ -258,10 +257,11 @@ as_color(ColorProfile *self, PyObject *val) {
|
|||||||
col = entry >> 8;
|
col = entry >> 8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ans = Py_None; Py_INCREF(Py_None);
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
if (ans == NULL) ans = Py_BuildValue("BBB", (unsigned char)(col >> 16), (unsigned char)((col >> 8) & 0xFF), (unsigned char)(col & 0xFF));
|
Color *ans = PyObject_New(Color, &Color_Type);
|
||||||
return ans;
|
if (ans) ans->color.rgb = col;
|
||||||
|
return (PyObject*)ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@ -412,8 +412,9 @@ CGETSET(cursor_color)
|
|||||||
CGETSET(cursor_text_color)
|
CGETSET(cursor_text_color)
|
||||||
CGETSET(highlight_fg)
|
CGETSET(highlight_fg)
|
||||||
CGETSET(highlight_bg)
|
CGETSET(highlight_bg)
|
||||||
|
#undef CGETSET
|
||||||
|
|
||||||
static PyGetSetDef getsetters[] = {
|
static PyGetSetDef cp_getsetters[] = {
|
||||||
GETSET(default_fg)
|
GETSET(default_fg)
|
||||||
GETSET(default_bg)
|
GETSET(default_bg)
|
||||||
GETSET(cursor_color)
|
GETSET(cursor_color)
|
||||||
@ -424,11 +425,11 @@ static PyGetSetDef getsetters[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static PyMemberDef members[] = {
|
static PyMemberDef cp_members[] = {
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef cp_methods[] = {
|
||||||
METHOD(update_ansi_color_table, METH_O)
|
METHOD(update_ansi_color_table, METH_O)
|
||||||
METHOD(reset_color_table, METH_NOARGS)
|
METHOD(reset_color_table, METH_NOARGS)
|
||||||
METHOD(as_dict, METH_NOARGS)
|
METHOD(as_dict, METH_NOARGS)
|
||||||
@ -445,14 +446,151 @@ PyTypeObject ColorProfile_Type = {
|
|||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "fast_data_types.ColorProfile",
|
.tp_name = "fast_data_types.ColorProfile",
|
||||||
.tp_basicsize = sizeof(ColorProfile),
|
.tp_basicsize = sizeof(ColorProfile),
|
||||||
.tp_dealloc = (destructor)dealloc,
|
.tp_dealloc = (destructor)dealloc_cp,
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
.tp_doc = "ColorProfile",
|
.tp_doc = "ColorProfile",
|
||||||
.tp_members = members,
|
.tp_members = cp_members,
|
||||||
.tp_methods = methods,
|
.tp_methods = cp_methods,
|
||||||
.tp_getset = getsetters,
|
.tp_getset = cp_getsetters,
|
||||||
.tp_new = new,
|
.tp_new = new_cp,
|
||||||
};
|
};
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (self != NULL) {
|
||||||
|
self->color.r = r; self->color.g = g; self->color.b = b; self->color.a = a;
|
||||||
|
}
|
||||||
|
return (PyObject*) self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
color_as_int(Color *self) {
|
||||||
|
return PyLong_FromUnsignedLong(self->color.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
color_truediv(Color *self, PyObject *divisor) {
|
||||||
|
DECREF_AFTER_FUNCTION PyObject *o = PyNumber_Float(divisor);
|
||||||
|
if (o == NULL) return NULL;
|
||||||
|
double r = self->color.r, g = self->color.g, b = self->color.b, a = self->color.a;
|
||||||
|
double d = PyFloat_AS_DOUBLE(o) * 255.;
|
||||||
|
return Py_BuildValue("dddd", r/d, g/d, b/d, a/d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyNumberMethods color_number_methods = {
|
||||||
|
.nb_int = (unaryfunc)color_as_int,
|
||||||
|
.nb_true_divide = (binaryfunc)color_truediv,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CGETSET(name) \
|
||||||
|
static PyObject* name##_get(Color *self, void UNUSED *closure) { return PyLong_FromUnsignedLong(self->color.name); }
|
||||||
|
CGETSET(red)
|
||||||
|
CGETSET(green)
|
||||||
|
CGETSET(blue)
|
||||||
|
CGETSET(alpha)
|
||||||
|
#undef CGETSET
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
rgb_get(Color *self, void *closure UNUSED) {
|
||||||
|
return PyLong_FromUnsignedLong(self->color.rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
luminance_get(Color *self, void *closure UNUSED) {
|
||||||
|
return PyFloat_FromDouble(rgb_luminance(self->color));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
sgr_get(Color* self, void *closure UNUSED) {
|
||||||
|
char buf[32];
|
||||||
|
int sz = snprintf(buf, sizeof(buf), ":2:%u:%u:%u", self->color.r, self->color.g, self->color.b);
|
||||||
|
return PyUnicode_FromStringAndSize(buf, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
sharp_get(Color* self, void *closure UNUSED) {
|
||||||
|
char buf[32];
|
||||||
|
int sz;
|
||||||
|
if (self->color.alpha) sz = snprintf(buf, sizeof(buf), "#%02x%02x%02x%02x", self->color.a, self->color.r, self->color.g, self->color.b);
|
||||||
|
else sz = snprintf(buf, sizeof(buf), "#%02x%02x%02x", self->color.r, self->color.g, self->color.b);
|
||||||
|
return PyUnicode_FromStringAndSize(buf, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
color_cmp(PyObject *self, PyObject *other, int op) {
|
||||||
|
if (op != Py_EQ && op != Py_NE) return Py_NotImplemented;
|
||||||
|
if (!PyObject_TypeCheck(other, &Color_Type)) {
|
||||||
|
if (op == Py_EQ) Py_RETURN_FALSE;
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
Color *a = (Color*)self, *b = (Color*)other;
|
||||||
|
switch (op) {
|
||||||
|
case Py_EQ: { if (a->color.val == b->color.val) { Py_RETURN_TRUE; } Py_RETURN_FALSE; }
|
||||||
|
case Py_NE: { if (a->color.val != b->color.val) { Py_RETURN_TRUE; } Py_RETURN_FALSE; }
|
||||||
|
default:
|
||||||
|
return Py_NotImplemented;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyGetSetDef color_getsetters[] = {
|
||||||
|
{"rgb", (getter) rgb_get, NULL, "rgb", NULL},
|
||||||
|
{"red", (getter) red_get, NULL, "red", NULL},
|
||||||
|
{"green", (getter) green_get, NULL, "green", NULL},
|
||||||
|
{"blue", (getter) blue_get, NULL, "blue", NULL},
|
||||||
|
{"alpha", (getter) alpha_get, NULL, "alpha", NULL},
|
||||||
|
{"luminance", (getter) luminance_get, NULL, "luminance", NULL},
|
||||||
|
{"as_sgr", (getter) sgr_get, NULL, "as_sgr", NULL},
|
||||||
|
{"as_sharp", (getter) sharp_get, NULL, "as_sharp", NULL},
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
contrast(Color* self, PyObject *o) {
|
||||||
|
if (!PyObject_TypeCheck(o, &Color_Type)) { PyErr_SetString(PyExc_TypeError, "Not a Color"); return NULL; }
|
||||||
|
Color *other = (Color*) o;
|
||||||
|
return PyFloat_FromDouble(rgb_contrast(self->color, other->color));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef color_methods[] = {
|
||||||
|
METHODB(contrast, METH_O),
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
repr(Color *self) {
|
||||||
|
if (self->color.alpha) return PyUnicode_FromFormat("Color(red=%u, green=%u, blue=%u, alpha=%u)", self->color.r, self->color.g, self->color.b, self->color.a);
|
||||||
|
return PyUnicode_FromFormat("Color(%u, %u, %u)", self->color.r, self->color.g, self->color.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Py_hash_t
|
||||||
|
color_hash(PyObject *x) {
|
||||||
|
return ((Color*)x)->color.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyTypeObject Color_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
.tp_name = "kitty.fast_data_types.Color",
|
||||||
|
.tp_basicsize = sizeof(Color),
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
.tp_doc = "Color",
|
||||||
|
.tp_new = new_color,
|
||||||
|
.tp_getset = color_getsetters,
|
||||||
|
.tp_as_number = &color_number_methods,
|
||||||
|
.tp_methods = color_methods,
|
||||||
|
.tp_repr = (reprfunc)repr,
|
||||||
|
.tp_hash = color_hash,
|
||||||
|
.tp_richcompare = color_cmp,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHODB(default_color_table, METH_NOARGS),
|
METHODB(default_color_table, METH_NOARGS),
|
||||||
@ -465,6 +603,11 @@ int init_ColorProfile(PyObject *module) {\
|
|||||||
if (PyType_Ready(&ColorProfile_Type) < 0) return 0;
|
if (PyType_Ready(&ColorProfile_Type) < 0) return 0;
|
||||||
if (PyModule_AddObject(module, "ColorProfile", (PyObject *)&ColorProfile_Type) != 0) return 0;
|
if (PyModule_AddObject(module, "ColorProfile", (PyObject *)&ColorProfile_Type) != 0) return 0;
|
||||||
Py_INCREF(&ColorProfile_Type);
|
Py_INCREF(&ColorProfile_Type);
|
||||||
|
|
||||||
|
if (PyType_Ready(&Color_Type) < 0) return 0;
|
||||||
|
if (PyModule_AddObject(module, "Color", (PyObject *)&Color_Type) != 0) return 0;
|
||||||
|
Py_INCREF(&Color_Type);
|
||||||
|
|
||||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
75
kitty/colors.h
Normal file
75
kitty/colors.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data-types.h"
|
||||||
|
|
||||||
|
typedef union ARGB32 {
|
||||||
|
color_type val;
|
||||||
|
struct {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
uint8_t b: 8;
|
||||||
|
uint8_t g: 8;
|
||||||
|
uint8_t r: 8;
|
||||||
|
uint8_t a: 8;
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint8_t a: 8;
|
||||||
|
uint8_t r: 8;
|
||||||
|
uint8_t g: 8;
|
||||||
|
uint8_t b: 8;
|
||||||
|
#else
|
||||||
|
#error "Unsupported endianness"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
uint8_t blue: 8;
|
||||||
|
uint8_t green: 8;
|
||||||
|
uint8_t red: 8;
|
||||||
|
uint8_t alpha: 8;
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint8_t alpha: 8;
|
||||||
|
uint8_t red: 8;
|
||||||
|
uint8_t green: 8;
|
||||||
|
uint8_t blue: 8;
|
||||||
|
#else
|
||||||
|
#error "Unsupported endianness"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
color_type rgb: 24;
|
||||||
|
uint8_t _ignore_me: 8;
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
uint8_t _ignore_me: 8;
|
||||||
|
color_type rgb: 24;
|
||||||
|
#else
|
||||||
|
#error "Unsupported endianness"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
} ARGB32;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
|
||||||
|
ARGB32 color;
|
||||||
|
} Color;
|
||||||
|
|
||||||
|
extern PyTypeObject ColorProfile_Type;
|
||||||
|
extern PyTypeObject Color_Type;
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
rgb_luminance(ARGB32 c) {
|
||||||
|
return 0.299 * c.red + 0.587 * c.green + 0.114 * c.blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double
|
||||||
|
rgb_contrast(ARGB32 a, ARGB32 b) {
|
||||||
|
double al = rgb_luminance(a), bl = rgb_luminance(b);
|
||||||
|
if (al < bl) SWAP(al, bl);
|
||||||
|
return (al + 0.05) / (bl + 0.05);
|
||||||
|
}
|
||||||
@ -9,7 +9,8 @@ from typing import (
|
|||||||
Sequence, Set, Tuple, TypeVar, Union, Generic
|
Sequence, Set, Tuple, TypeVar, Union, Generic
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..rgb import Color, to_color as as_color
|
from ..rgb import to_color as as_color
|
||||||
|
from ..fast_data_types import Color
|
||||||
from ..types import ConvertibleToNumbers, ParsedShortcut
|
from ..types import ConvertibleToNumbers, ParsedShortcut
|
||||||
from ..typing import Protocol
|
from ..typing import Protocol
|
||||||
from ..utils import expandvars, log_error
|
from ..utils import expandvars, log_error
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
#define MIN(x, y) __extension__ ({ \
|
#define MIN(x, y) __extension__ ({ \
|
||||||
__typeof__ (x) a = (x); __typeof__ (y) b = (y); \
|
__typeof__ (x) a = (x); __typeof__ (y) b = (y); \
|
||||||
a < b ? a : b;})
|
a < b ? a : b;})
|
||||||
|
#define SWAP(x, y) do { __typeof__(x) _sw_ = y; y = x; x = _sw_; } while(0)
|
||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|||||||
@ -20,10 +20,10 @@ from .conf.utils import KeyAction
|
|||||||
from .constants import (
|
from .constants import (
|
||||||
extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path
|
extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path
|
||||||
)
|
)
|
||||||
from .fast_data_types import num_users
|
from .fast_data_types import num_users, Color
|
||||||
from .options.types import Options as KittyOpts, defaults
|
from .options.types import Options as KittyOpts, defaults
|
||||||
from .options.utils import MouseMap
|
from .options.utils import MouseMap
|
||||||
from .rgb import Color, color_as_sharp
|
from .rgb import color_as_sharp
|
||||||
from .types import MouseEvent, SingleKey
|
from .types import MouseEvent, SingleKey
|
||||||
from .typing import SequenceMap
|
from .typing import SequenceMap
|
||||||
|
|
||||||
|
|||||||
@ -619,6 +619,61 @@ def patch_global_colors(spec: Dict[str, Optional[int]], configured: bool) -> Non
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Color:
|
||||||
|
@property
|
||||||
|
def rgb(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def red(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def green(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def blue(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alpha(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def luminance(self) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_sgr(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_sharp(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, red: int = 0, green: int = 0, blue: int = 0, alpha: int = 0) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __truediv__(self, divisor: float) -> Tuple[float, float, float, float]: # (r, g, b, a)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __int__(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __ne__(self, other: Any) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def contrast(self, other: 'Color') -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ColorProfile:
|
class ColorProfile:
|
||||||
|
|
||||||
default_bg: int
|
default_bg: int
|
||||||
@ -626,7 +681,7 @@ class ColorProfile:
|
|||||||
def as_dict(self) -> Dict[str, int]:
|
def as_dict(self) -> Dict[str, int]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def as_color(self, val: int) -> Tuple[int, int, int]:
|
def as_color(self, val: int) -> Optional[Color]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_color(self, num: int, val: int) -> None:
|
def set_color(self, num: int, val: int) -> None:
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../state.h"
|
#include "../state.h"
|
||||||
|
#include "../colors.h"
|
||||||
|
|
||||||
static inline float
|
static inline float
|
||||||
PyFloat_AsFloat(PyObject *o) {
|
PyFloat_AsFloat(PyObject *o) {
|
||||||
@ -15,10 +16,9 @@ PyFloat_AsFloat(PyObject *o) {
|
|||||||
|
|
||||||
static inline color_type
|
static inline color_type
|
||||||
color_as_int(PyObject *color) {
|
color_as_int(PyObject *color) {
|
||||||
if (!PyTuple_Check(color)) { PyErr_SetString(PyExc_TypeError, "Not a color tuple"); return 0; }
|
if (!PyObject_TypeCheck(color, &Color_Type)) { PyErr_SetString(PyExc_TypeError, "Not a Color object"); return 0; }
|
||||||
#define I(n, s) ((PyLong_AsUnsignedLong(PyTuple_GET_ITEM(color, n)) & 0xff) << s)
|
Color *c = (Color*)color;
|
||||||
return (I(0, 16) | I(1, 8) | I(2, 0)) & 0xffffff;
|
return c->color.val & 0xffffff;
|
||||||
#undef I
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline color_type
|
static inline color_type
|
||||||
|
|||||||
48
kitty/options/types.py
generated
48
kitty/options/types.py
generated
@ -6,10 +6,10 @@ from kitty.conf.utils import KeyAction
|
|||||||
import kitty.conf.utils
|
import kitty.conf.utils
|
||||||
from kitty.constants import is_macos
|
from kitty.constants import is_macos
|
||||||
import kitty.constants
|
import kitty.constants
|
||||||
|
from kitty.fast_data_types import Color
|
||||||
|
import kitty.fast_data_types
|
||||||
from kitty.options.utils import KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap, TabBarMarginHeight
|
from kitty.options.utils import KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap, TabBarMarginHeight
|
||||||
import kitty.options.utils
|
import kitty.options.utils
|
||||||
from kitty.rgb import Color
|
|
||||||
import kitty.rgb
|
|
||||||
from kitty.types import FloatEdges, SingleKey
|
from kitty.types import FloatEdges, SingleKey
|
||||||
import kitty.types
|
import kitty.types
|
||||||
|
|
||||||
@ -441,23 +441,23 @@ option_names = ( # {{{
|
|||||||
|
|
||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
active_border_color: typing.Optional[kitty.rgb.Color] = Color(red=0, green=255, blue=0)
|
active_border_color: typing.Optional[kitty.fast_data_types.Color] = Color(0, 255, 0)
|
||||||
active_tab_background: Color = Color(red=238, green=238, blue=238)
|
active_tab_background: Color = Color(238, 238, 238)
|
||||||
active_tab_font_style: typing.Tuple[bool, bool] = (True, True)
|
active_tab_font_style: typing.Tuple[bool, bool] = (True, True)
|
||||||
active_tab_foreground: Color = Color(red=0, green=0, blue=0)
|
active_tab_foreground: Color = Color(0, 0, 0)
|
||||||
active_tab_title_template: typing.Optional[str] = None
|
active_tab_title_template: typing.Optional[str] = None
|
||||||
adjust_baseline: typing.Union[int, float] = 0
|
adjust_baseline: typing.Union[int, float] = 0
|
||||||
adjust_column_width: typing.Union[int, float] = 0
|
adjust_column_width: typing.Union[int, float] = 0
|
||||||
adjust_line_height: typing.Union[int, float] = 0
|
adjust_line_height: typing.Union[int, float] = 0
|
||||||
allow_hyperlinks: int = 1
|
allow_hyperlinks: int = 1
|
||||||
allow_remote_control: str = 'n'
|
allow_remote_control: str = 'n'
|
||||||
background: Color = Color(red=0, green=0, blue=0)
|
background: Color = Color(0, 0, 0)
|
||||||
background_image: typing.Optional[str] = None
|
background_image: typing.Optional[str] = None
|
||||||
background_image_layout: choices_for_background_image_layout = 'tiled'
|
background_image_layout: choices_for_background_image_layout = 'tiled'
|
||||||
background_image_linear: bool = False
|
background_image_linear: bool = False
|
||||||
background_opacity: float = 1.0
|
background_opacity: float = 1.0
|
||||||
background_tint: float = 0
|
background_tint: float = 0
|
||||||
bell_border_color: Color = Color(red=255, green=90, blue=0)
|
bell_border_color: Color = Color(255, 90, 0)
|
||||||
bell_on_tab: bool = True
|
bell_on_tab: bool = True
|
||||||
bell_path: typing.Optional[str] = None
|
bell_path: typing.Optional[str] = None
|
||||||
bold_font: str = 'auto'
|
bold_font: str = 'auto'
|
||||||
@ -472,12 +472,12 @@ class Options:
|
|||||||
command_on_bell: typing.List[str] = ['none']
|
command_on_bell: typing.List[str] = ['none']
|
||||||
confirm_os_window_close: int = 0
|
confirm_os_window_close: int = 0
|
||||||
copy_on_select: str = ''
|
copy_on_select: str = ''
|
||||||
cursor: typing.Optional[kitty.rgb.Color] = Color(red=204, green=204, blue=204)
|
cursor: typing.Optional[kitty.fast_data_types.Color] = Color(204, 204, 204)
|
||||||
cursor_beam_thickness: float = 1.5
|
cursor_beam_thickness: float = 1.5
|
||||||
cursor_blink_interval: float = -1.0
|
cursor_blink_interval: float = -1.0
|
||||||
cursor_shape: int = 1
|
cursor_shape: int = 1
|
||||||
cursor_stop_blinking_after: float = 15.0
|
cursor_stop_blinking_after: float = 15.0
|
||||||
cursor_text_color: typing.Optional[kitty.rgb.Color] = Color(red=17, green=17, blue=17)
|
cursor_text_color: typing.Optional[kitty.fast_data_types.Color] = Color(17, 17, 17)
|
||||||
cursor_underline_thickness: float = 2.0
|
cursor_underline_thickness: float = 2.0
|
||||||
default_pointer_shape: choices_for_default_pointer_shape = 'beam'
|
default_pointer_shape: choices_for_default_pointer_shape = 'beam'
|
||||||
detect_urls: bool = True
|
detect_urls: bool = True
|
||||||
@ -493,12 +493,12 @@ class Options:
|
|||||||
font_family: str = 'monospace'
|
font_family: str = 'monospace'
|
||||||
font_size: float = 11.0
|
font_size: float = 11.0
|
||||||
force_ltr: bool = False
|
force_ltr: bool = False
|
||||||
foreground: Color = Color(red=221, green=221, blue=221)
|
foreground: Color = Color(221, 221, 221)
|
||||||
hide_window_decorations: int = 0
|
hide_window_decorations: int = 0
|
||||||
inactive_border_color: Color = Color(red=204, green=204, blue=204)
|
inactive_border_color: Color = Color(204, 204, 204)
|
||||||
inactive_tab_background: Color = Color(red=153, green=153, blue=153)
|
inactive_tab_background: Color = Color(153, 153, 153)
|
||||||
inactive_tab_font_style: typing.Tuple[bool, bool] = (False, False)
|
inactive_tab_font_style: typing.Tuple[bool, bool] = (False, False)
|
||||||
inactive_tab_foreground: Color = Color(red=68, green=68, blue=68)
|
inactive_tab_foreground: Color = Color(68, 68, 68)
|
||||||
inactive_text_alpha: float = 1.0
|
inactive_text_alpha: float = 1.0
|
||||||
initial_window_height: typing.Tuple[int, str] = (400, 'px')
|
initial_window_height: typing.Tuple[int, str] = (400, 'px')
|
||||||
initial_window_width: typing.Tuple[int, str] = (640, 'px')
|
initial_window_width: typing.Tuple[int, str] = (640, 'px')
|
||||||
@ -516,12 +516,12 @@ class Options:
|
|||||||
macos_titlebar_color: int = 0
|
macos_titlebar_color: int = 0
|
||||||
macos_traditional_fullscreen: bool = False
|
macos_traditional_fullscreen: bool = False
|
||||||
macos_window_resizable: bool = True
|
macos_window_resizable: bool = True
|
||||||
mark1_background: Color = Color(red=152, green=211, blue=203)
|
mark1_background: Color = Color(152, 211, 203)
|
||||||
mark1_foreground: Color = Color(red=0, green=0, blue=0)
|
mark1_foreground: Color = Color(0, 0, 0)
|
||||||
mark2_background: Color = Color(red=242, green=220, blue=211)
|
mark2_background: Color = Color(242, 220, 211)
|
||||||
mark2_foreground: Color = Color(red=0, green=0, blue=0)
|
mark2_foreground: Color = Color(0, 0, 0)
|
||||||
mark3_background: Color = Color(red=242, green=116, blue=188)
|
mark3_background: Color = Color(242, 116, 188)
|
||||||
mark3_foreground: Color = Color(red=0, green=0, blue=0)
|
mark3_foreground: Color = Color(0, 0, 0)
|
||||||
mouse_hide_wait: float = 0.0 if is_macos else 3.0
|
mouse_hide_wait: float = 0.0 if is_macos else 3.0
|
||||||
open_url_with: typing.List[str] = ['default']
|
open_url_with: typing.List[str] = ['default']
|
||||||
placement_strategy: choices_for_placement_strategy = 'center'
|
placement_strategy: choices_for_placement_strategy = 'center'
|
||||||
@ -537,8 +537,8 @@ class Options:
|
|||||||
scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER']
|
scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER']
|
||||||
scrollback_pager_history_size: int = 0
|
scrollback_pager_history_size: int = 0
|
||||||
select_by_word_characters: str = '@-./_~?&=%+#'
|
select_by_word_characters: str = '@-./_~?&=%+#'
|
||||||
selection_background: Color = Color(red=255, green=250, blue=205)
|
selection_background: Color = Color(255, 250, 205)
|
||||||
selection_foreground: typing.Optional[kitty.rgb.Color] = Color(red=0, green=0, blue=0)
|
selection_foreground: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0)
|
||||||
shell: str = '.'
|
shell: str = '.'
|
||||||
shell_integration: str = 'enabled'
|
shell_integration: str = 'enabled'
|
||||||
single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)
|
single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)
|
||||||
@ -547,9 +547,9 @@ class Options:
|
|||||||
sync_to_monitor: bool = True
|
sync_to_monitor: bool = True
|
||||||
tab_activity_symbol: typing.Optional[str] = None
|
tab_activity_symbol: typing.Optional[str] = None
|
||||||
tab_bar_align: choices_for_tab_bar_align = 'left'
|
tab_bar_align: choices_for_tab_bar_align = 'left'
|
||||||
tab_bar_background: typing.Optional[kitty.rgb.Color] = None
|
tab_bar_background: typing.Optional[kitty.fast_data_types.Color] = None
|
||||||
tab_bar_edge: int = 3
|
tab_bar_edge: int = 3
|
||||||
tab_bar_margin_color: typing.Optional[kitty.rgb.Color] = None
|
tab_bar_margin_color: typing.Optional[kitty.fast_data_types.Color] = None
|
||||||
tab_bar_margin_height: TabBarMarginHeight = TabBarMarginHeight(outer=0, inner=0)
|
tab_bar_margin_height: TabBarMarginHeight = TabBarMarginHeight(outer=0, inner=0)
|
||||||
tab_bar_margin_width: float = 0
|
tab_bar_margin_width: float = 0
|
||||||
tab_bar_min_tabs: int = 2
|
tab_bar_min_tabs: int = 2
|
||||||
@ -562,7 +562,7 @@ class Options:
|
|||||||
term: str = 'xterm-kitty'
|
term: str = 'xterm-kitty'
|
||||||
touch_scroll_multiplier: float = 1.0
|
touch_scroll_multiplier: float = 1.0
|
||||||
update_check_interval: float = 24.0
|
update_check_interval: float = 24.0
|
||||||
url_color: Color = Color(red=0, green=135, blue=189)
|
url_color: Color = Color(0, 135, 189)
|
||||||
url_excluded_characters: str = ''
|
url_excluded_characters: str = ''
|
||||||
url_prefixes: typing.Tuple[str, ...] = ('http', 'https', 'file', 'ftp', 'gemini', 'irc', 'gopher', 'mailto', 'news', 'git')
|
url_prefixes: typing.Tuple[str, ...] = ('http', 'https', 'file', 'ftp', 'gemini', 'irc', 'gopher', 'mailto', 'news', 'git')
|
||||||
url_style: int = 3
|
url_style: int = 3
|
||||||
|
|||||||
@ -16,13 +16,13 @@ from kitty.conf.utils import (
|
|||||||
python_string, to_bool, to_cmdline, to_color, uniq, unit_float
|
python_string, to_bool, to_cmdline, to_color, uniq, unit_float
|
||||||
)
|
)
|
||||||
from kitty.constants import config_dir, is_macos
|
from kitty.constants import config_dir, is_macos
|
||||||
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, Color
|
||||||
from kitty.fonts import FontFeature
|
from kitty.fonts import FontFeature
|
||||||
from kitty.key_names import (
|
from kitty.key_names import (
|
||||||
character_key_name_aliases, functional_key_name_aliases,
|
character_key_name_aliases, functional_key_name_aliases,
|
||||||
get_key_name_lookup
|
get_key_name_lookup
|
||||||
)
|
)
|
||||||
from kitty.rgb import Color, color_as_int
|
from kitty.rgb import color_as_int
|
||||||
from kitty.types import FloatEdges, MouseEvent, SingleKey
|
from kitty.types import FloatEdges, MouseEvent, SingleKey
|
||||||
from kitty.utils import expandvars, log_error
|
from kitty.utils import expandvars, log_error
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from kitty.rgb import Color, color_as_sharp, color_from_int
|
from kitty.fast_data_types import Color
|
||||||
|
from kitty.rgb import color_as_sharp, color_from_int
|
||||||
from kitty.utils import natsort_ints
|
from kitty.utils import natsort_ints
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
|
|||||||
@ -6,8 +6,7 @@ import os
|
|||||||
from typing import TYPE_CHECKING, Dict, Iterable, Optional
|
from typing import TYPE_CHECKING, Dict, Iterable, Optional
|
||||||
|
|
||||||
from kitty.config import parse_config
|
from kitty.config import parse_config
|
||||||
from kitty.fast_data_types import patch_color_profiles
|
from kitty.fast_data_types import patch_color_profiles, Color
|
||||||
from kitty.rgb import Color
|
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, ParsingOfArgsFailed,
|
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, ParsingOfArgsFailed,
|
||||||
|
|||||||
37
kitty/rgb.py
generated
37
kitty/rgb.py
generated
@ -3,37 +3,8 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import NamedTuple, Optional, Tuple
|
from typing import Optional
|
||||||
|
from .fast_data_types import Color
|
||||||
|
|
||||||
class Color(NamedTuple):
|
|
||||||
red: int = 0
|
|
||||||
green: int = 0
|
|
||||||
blue: int = 0
|
|
||||||
|
|
||||||
def __truediv__(self, denom: float) -> Tuple[float, float, float]:
|
|
||||||
return self.red / denom, self.green / denom, self.blue / denom
|
|
||||||
|
|
||||||
def as_sgr(self) -> str:
|
|
||||||
return ':2:{}:{}:{}'.format(*self)
|
|
||||||
|
|
||||||
def luminance(self) -> float:
|
|
||||||
return 0.299 * self.red + 0.587 * self.green + 0.114 * self.blue
|
|
||||||
|
|
||||||
def contrast(self, other: 'Color') -> float:
|
|
||||||
a = self.luminance()
|
|
||||||
b = other.luminance()
|
|
||||||
if a < b:
|
|
||||||
a, b = b, a
|
|
||||||
return (a + 0.05) / (b + 0.05)
|
|
||||||
|
|
||||||
def __int__(self) -> int:
|
|
||||||
return self.red << 16 | self.green << 8 | self.blue
|
|
||||||
|
|
||||||
def as_bytearray(self, alpha: Optional[int] = None) -> bytearray:
|
|
||||||
if alpha is None:
|
|
||||||
return bytearray((self.red, self.green, self.blue))
|
|
||||||
return bytearray((self.red, self.green, self.blue, alpha))
|
|
||||||
|
|
||||||
|
|
||||||
def alpha_blend_channel(top_color: int, bottom_color: int, alpha: float) -> int:
|
def alpha_blend_channel(top_color: int, bottom_color: int, alpha: float) -> int:
|
||||||
@ -78,11 +49,11 @@ def color_as_int(x: Color) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def color_as_sharp(x: Color) -> str:
|
def color_as_sharp(x: Color) -> str:
|
||||||
return '#{:02x}{:02x}{:02x}'.format(*x)
|
return x.as_sharp
|
||||||
|
|
||||||
|
|
||||||
def color_as_sgr(x: Color) -> str:
|
def color_as_sgr(x: Color) -> str:
|
||||||
return x.as_sgr()
|
return x.as_sgr
|
||||||
|
|
||||||
|
|
||||||
def to_color(raw: str, validate: bool = False) -> Optional[Color]:
|
def to_color(raw: str, validate: bool = False) -> Optional[Color]:
|
||||||
|
|||||||
@ -12,9 +12,9 @@ from .config import build_ansi_color_table
|
|||||||
from .constants import config_dir
|
from .constants import config_dir
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
DECAWM, Region, Screen, cell_size_for_window, get_options, pt_to_px,
|
DECAWM, Region, Screen, cell_size_for_window, get_options, pt_to_px,
|
||||||
set_tab_bar_render_data, viewport_for_window
|
set_tab_bar_render_data, viewport_for_window, Color
|
||||||
)
|
)
|
||||||
from .rgb import Color, alpha_blend, color_as_sgr, color_from_int, to_color
|
from .rgb import alpha_blend, color_as_sgr, color_from_int, to_color
|
||||||
from .types import WindowGeometry, run_once
|
from .types import WindowGeometry, run_once
|
||||||
from .typing import EdgeLiteral, PowerlineStyle
|
from .typing import EdgeLiteral, PowerlineStyle
|
||||||
from .utils import color_as_int, log_error
|
from .utils import color_as_int, log_error
|
||||||
|
|||||||
@ -21,9 +21,10 @@ from .constants import (
|
|||||||
appname, is_macos, is_wayland, read_kitty_resource, shell_path,
|
appname, is_macos, is_wayland, read_kitty_resource, shell_path,
|
||||||
supports_primary_selection
|
supports_primary_selection
|
||||||
)
|
)
|
||||||
from .rgb import Color, to_color
|
from .rgb import to_color
|
||||||
from .types import run_once
|
from .types import run_once
|
||||||
from .typing import AddressFamily, PopenType, Socket, StartupCtx
|
from .typing import AddressFamily, PopenType, Socket, StartupCtx
|
||||||
|
from .fast_data_types import Color
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .fast_data_types import OSWindowSize
|
from .fast_data_types import OSWindowSize
|
||||||
@ -95,8 +96,8 @@ def sanitize_title(x: str) -> str:
|
|||||||
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19\x80-\x9f]', '', x))
|
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19\x80-\x9f]', '', x))
|
||||||
|
|
||||||
|
|
||||||
def color_as_int(val: Tuple[int, int, int]) -> int:
|
def color_as_int(val: Color) -> int:
|
||||||
return val[0] << 16 | val[1] << 8 | val[2]
|
return int(val) & 0xffffff
|
||||||
|
|
||||||
|
|
||||||
def color_from_int(val: int) -> Color:
|
def color_from_int(val: int) -> Color:
|
||||||
@ -118,8 +119,7 @@ def parse_color_set(raw: str) -> Generator[Tuple[int, Optional[int]], None, None
|
|||||||
else:
|
else:
|
||||||
q = to_color(spec)
|
q = to_color(spec)
|
||||||
if q is not None:
|
if q is not None:
|
||||||
r, g, b = q
|
yield c, int(q) & 0xffffff
|
||||||
yield c, r << 16 | g << 8 | b
|
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ from .constants import appname, is_macos, wakeup
|
|||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
|
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
|
||||||
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK,
|
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK,
|
||||||
CURSOR_UNDERLINE, DCS, DECORATION, DIM, GLFW_MOD_CONTROL,
|
CURSOR_UNDERLINE, DCS, DECORATION, DIM, GLFW_MOD_CONTROL, Color,
|
||||||
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
|
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
|
||||||
MARK, MARK_MASK, NO_CURSOR_SHAPE, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE,
|
MARK, MARK_MASK, NO_CURSOR_SHAPE, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE,
|
||||||
SCROLL_PAGE, SEVEN_SEGMENT_PROGRAM, STRIKETHROUGH, TINT_PROGRAM, KeyEvent,
|
SCROLL_PAGE, SEVEN_SEGMENT_PROGRAM, STRIKETHROUGH, TINT_PROGRAM, KeyEvent,
|
||||||
@ -36,7 +36,7 @@ from .fast_data_types import (
|
|||||||
from .keys import keyboard_mode_name, mod_mask
|
from .keys import keyboard_mode_name, mod_mask
|
||||||
from .notify import NotificationCommand, handle_notification_cmd
|
from .notify import NotificationCommand, handle_notification_cmd
|
||||||
from .options.types import Options
|
from .options.types import Options
|
||||||
from .rgb import Color, to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .types import MouseEvent, ScreenGeometry, WindowGeometry, ac
|
from .types import MouseEvent, ScreenGeometry, WindowGeometry, ac
|
||||||
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||||
@ -813,7 +813,9 @@ class Window:
|
|||||||
changed = False
|
changed = False
|
||||||
for c, val in parse_color_set(value):
|
for c, val in parse_color_set(value):
|
||||||
if val is None: # color query
|
if val is None: # color query
|
||||||
self.report_color(f'4;{c}', *self.screen.color_profile.as_color((c << 8) | 1))
|
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)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
cp.set_color(c, val)
|
cp.set_color(c, val)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import tempfile
|
|||||||
|
|
||||||
from kitty.config import build_ansi_color_table, defaults
|
from kitty.config import build_ansi_color_table, defaults
|
||||||
from kitty.fast_data_types import (
|
from kitty.fast_data_types import (
|
||||||
ColorProfile, Cursor as C, HistoryBuf, LineBuf,
|
ColorProfile, Cursor as C, HistoryBuf, LineBuf, Color,
|
||||||
parse_input_from_terminal, truncate_point_for_length, wcswidth, wcwidth
|
parse_input_from_terminal, truncate_point_for_length, wcswidth, wcwidth
|
||||||
)
|
)
|
||||||
from kitty.rgb import to_color
|
from kitty.rgb import to_color
|
||||||
@ -32,8 +32,12 @@ class TestDataTypes(BaseTest):
|
|||||||
for x in 'xxx #12 #1234 rgb:a/b'.split():
|
for x in 'xxx #12 #1234 rgb:a/b'.split():
|
||||||
self.assertIsNone(to_color(x))
|
self.assertIsNone(to_color(x))
|
||||||
|
|
||||||
def c(spec, r=0, g=0, b=0):
|
def c(spec, r=0, g=0, b=0, a=0):
|
||||||
self.ae(tuple(to_color(spec)), (r, g, b))
|
c = to_color(spec)
|
||||||
|
self.ae(c.red, r)
|
||||||
|
self.ae(c.green, g)
|
||||||
|
self.ae(c.blue, b)
|
||||||
|
self.ae(c.alpha, a)
|
||||||
|
|
||||||
c('#eee', 0xee, 0xee, 0xee)
|
c('#eee', 0xee, 0xee, 0xee)
|
||||||
c('#234567', 0x23, 0x45, 0x67)
|
c('#234567', 0x23, 0x45, 0x67)
|
||||||
@ -42,6 +46,15 @@ class TestDataTypes(BaseTest):
|
|||||||
c('rgb:23/45/67', 0x23, 0x45, 0x67)
|
c('rgb:23/45/67', 0x23, 0x45, 0x67)
|
||||||
c('rgb:abc/abc/def', 0xab, 0xab, 0xde)
|
c('rgb:abc/abc/def', 0xab, 0xab, 0xde)
|
||||||
c('red', 0xff)
|
c('red', 0xff)
|
||||||
|
self.ae(int(Color(1, 2, 3)), 0x10203)
|
||||||
|
base = Color(12, 12, 12)
|
||||||
|
a = Color(23, 23, 23)
|
||||||
|
b = Color(100, 100, 100)
|
||||||
|
self.assertLess(base.contrast(a), base.contrast(b))
|
||||||
|
self.ae(Color(1, 2, 3).as_sgr, ':2:1:2:3')
|
||||||
|
self.ae(Color(1, 2, 3).as_sharp, '#010203')
|
||||||
|
self.ae(Color(1, 2, 3, 4).as_sharp, '#04010203')
|
||||||
|
self.ae(Color(1, 2, 3, 4).rgb, 0x10203)
|
||||||
|
|
||||||
def test_linebuf(self):
|
def test_linebuf(self):
|
||||||
old = filled_line_buf(2, 3, filled_cursor())
|
old = filled_line_buf(2, 3, filled_cursor())
|
||||||
@ -423,8 +436,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, f'color{i}')
|
col = getattr(defaults, f'color{i}')
|
||||||
self.assertEqual(c.as_color(i << 8 | 1), (col[0], col[1], col[2]))
|
self.assertEqual(c.as_color(i << 8 | 1), col)
|
||||||
self.ae(c.as_color(255 << 8 | 1), (0xee, 0xee, 0xee))
|
self.ae(c.as_color(255 << 8 | 1), Color(0xee, 0xee, 0xee))
|
||||||
|
|
||||||
def test_historybuf(self):
|
def test_historybuf(self):
|
||||||
lb = filled_line_buf()
|
lb = filled_line_buf()
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
from . import BaseTest
|
from . import BaseTest
|
||||||
from kitty.utils import log_error
|
from kitty.utils import log_error
|
||||||
from kitty.options.utils import DELETE_ENV_VAR
|
from kitty.options.utils import DELETE_ENV_VAR
|
||||||
|
from kitty.fast_data_types import Color
|
||||||
|
|
||||||
|
|
||||||
class TestConfParsing(BaseTest):
|
class TestConfParsing(BaseTest):
|
||||||
@ -37,7 +38,7 @@ class TestConfParsing(BaseTest):
|
|||||||
opts = p('font_size 11.37', 'clear_all_shortcuts y', 'color23 red')
|
opts = p('font_size 11.37', 'clear_all_shortcuts y', 'color23 red')
|
||||||
self.ae(opts.font_size, 11.37)
|
self.ae(opts.font_size, 11.37)
|
||||||
self.ae(opts.mouse_hide_wait, 0 if is_macos else 3)
|
self.ae(opts.mouse_hide_wait, 0 if is_macos else 3)
|
||||||
self.ae(tuple(opts.color23), (255, 0, 0))
|
self.ae(opts.color23, Color(255, 0, 0))
|
||||||
self.assertFalse(opts.keymap)
|
self.assertFalse(opts.keymap)
|
||||||
opts = p('clear_all_shortcuts y', 'map f1 next_window')
|
opts = p('clear_all_shortcuts y', 'map f1 next_window')
|
||||||
self.ae(len(opts.keymap), 1)
|
self.ae(len(opts.keymap), 1)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user