From 8f8e9baedcbb77cf15f1502c2f44d4c4560bda81 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 10 Nov 2016 11:07:39 +0530 Subject: [PATCH] Remove the no longer needed data types module --- kitty/char_grid.py | 30 ++-- kitty/colors.c | 33 ++++ kitty/data_types.py | 362 -------------------------------------------- kitty/line.c | 14 +- kitty/shaders.py | 5 +- 5 files changed, 64 insertions(+), 380 deletions(-) delete mode 100644 kitty/data_types.py diff --git a/kitty/char_grid.py b/kitty/char_grid.py index 95c7407ee..f98a350b6 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -8,8 +8,7 @@ from ctypes import c_uint from itertools import chain, repeat from queue import Queue, Empty -from .config import build_ansi_color_tables, to_color, fg_color_table, bg_color_table -from .data_types import COL_MASK, COL_SHIFT, REVERSE_MASK, as_color +from .config import build_ansi_color_table, to_color from .fonts import set_font_family from .shaders import Sprites, ShaderProgram from .utils import get_logical_dpi @@ -17,12 +16,15 @@ from .fast_data_types import ( glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, glClear, GL_COLOR_BUFFER_BIT, glClearColor, glViewport, glUniform2ui, glUniform4f, glUniform1i, glUniform2f, glDrawArraysInstanced, GL_TRIANGLE_FAN, - glEnable, glDisable, GL_BLEND, glDrawArrays + glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile, REVERSE ) Size = namedtuple('Size', 'width height') Cursor = namedtuple('Cursor', 'x y hidden shape color blink') ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy') +COL_MASK = 0xFFFFFFFF +COL_SHIFT = 32 +REVERSE_MASK = 1 << REVERSE # cell shader {{{ @@ -144,6 +146,8 @@ class CharGrid: def __init__(self, screen, opts, window_width, window_height): self.dpix, self.dpiy = get_logical_dpi() self.width, self.height = window_width, window_height + self.color_profile = ColorProfile() + self.as_color = self.color_profile.as_color self.screen = screen self.opts = opts self.original_bg = opts.background @@ -169,7 +173,7 @@ class CharGrid: def apply_opts(self, opts): self.dpix, self.dpiy = get_logical_dpi() self.opts = opts - build_ansi_color_tables(opts) + self.color_profile.update_ansi_color_table(build_ansi_color_table(opts)) self.default_cursor = Cursor(0, 0, False, opts.cursor_shape, opts.cursor, opts.cursor_blink) self.opts = opts self.original_bg = opts.background @@ -222,17 +226,15 @@ class CharGrid: lines = changes['lines'] cell_ranges = changes['cells'] - fgct = fg_color_table() - bgct = bg_color_table() dfbg = self.default_bg dffg = self.default_fg for y in lines: - self.update_line(y, range(sg.xnum), fgct, bgct, dffg, dfbg) + self.update_line(y, range(sg.xnum), dffg, dfbg) for y, ranges in cell_ranges.items(): self.update_line(y, chain.from_iterable(range(start, stop + 1) for start, stop in ranges), - fgct, bgct, dffg, dfbg) + dffg, dfbg) rd.cell_data = copy(self.sprite_map), self.sprite_text[:] rd.sprite_layout = self.sprites.layout @@ -241,23 +243,23 @@ class CharGrid: rd.cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink) self.render_queue.put(rd) - def update_line(self, y, cell_range, fgct, bgct, dffg, dfbg): + def update_line(self, y, cell_range, dffg, dfbg): line = self.screen.line(y) for x in cell_range: - self.update_cell(line, x, y, fgct, bgct, dffg, dfbg) + self.update_cell(line, x, y, dffg, dfbg) - def update_cell(self, line, x, y, fgct, bgct, dffg, dfbg): + def update_cell(self, line, x, y, dffg, dfbg): ch, attrs, colors = line.basic_cell_data(x) idx = x + y * self.screen_geometry.xnum offset = idx * 9 bgcol = colors >> COL_SHIFT if bgcol: - bgcol = as_color(bgcol, bgct) or dfbg + bgcol = self.as_color(bgcol) or dfbg else: bgcol = dfbg fgcol = colors & COL_MASK if fgcol: - fgcol = as_color(fgcol, fgct) or dffg + fgcol = self.as_color(fgcol) or dffg else: fgcol = dffg if attrs & REVERSE_MASK: @@ -269,7 +271,7 @@ class CharGrid: if ch == 0 or ch == 32: self.sprite_text[idx] = empty_cell else: - self.sprite_text[idx] = line.text_at(x), attrs + self.sprite_text[idx] = line[x], attrs def render(self): ' This is the only method in this class called in the UI thread (apart from __init__) ' diff --git a/kitty/colors.c b/kitty/colors.c index bcaae1c3e..09a1dd3b9 100644 --- a/kitty/colors.c +++ b/kitty/colors.c @@ -119,6 +119,38 @@ color_256(ColorProfile *self, PyObject *val) { } return PyLong_FromUnsignedLong(self->color_table_256[idx]); } + +static PyObject* +as_color(ColorProfile *self, PyObject *val) { +#define as_color_doc "Convert the specified terminal color into an (r, g, b) tuple based on the current profile values" + if (!PyLong_Check(val)) { PyErr_SetString(PyExc_TypeError, "val must be an int"); return NULL; } + unsigned long entry = PyLong_AsUnsignedLong(val); + unsigned int t = entry & 0xFF; + uint8_t r, g, b; + uint32_t col = 0; + PyObject *ans = NULL; + switch(t) { + case 1: + r = (entry >> 8) & 0xff; + col = self->ansi_color_table[r]; + break; + case 2: + r = (entry >> 8) & 0xff; + col = self->color_table_256[r]; + break; + case 3: + r = (entry >> 8) & 0xff; + g = (entry >> 16) & 0xff; + b = (entry >> 24) & 0xff; + ans = Py_BuildValue("BBB", r, g, b); + break; + default: + ans = Py_None; Py_INCREF(Py_None); + } + if (ans == NULL) ans = Py_BuildValue("BBB", (unsigned char)(col >> 16), (unsigned char)((col >> 8) & 0xFF), (unsigned char)(col & 0xFF)); + return ans; +} + // Boilerplate {{{ @@ -126,6 +158,7 @@ static PyMethodDef methods[] = { METHOD(update_ansi_color_table, METH_O) METHOD(ansi_color, METH_O) METHOD(color_256, METH_O) + METHOD(as_color, METH_O) {NULL} /* Sentinel */ }; diff --git a/kitty/data_types.py b/kitty/data_types.py deleted file mode 100644 index 11aaa6549..000000000 --- a/kitty/data_types.py +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 -# License: GPL v3 Copyright: 2016, Kovid Goyal - -import array -from typing import Tuple, Dict, Union, Iterator, Sequence -from itertools import repeat - -from pyte.graphics import FG_BG_256 -from .config import fg_color_table, bg_color_table - -code = 'I' if array.array('I').itemsize >= 4 else 'L' -lcode = 'L' if array.array('L').itemsize >= 8 else 'Q' - - -def get_zeroes(sz: int) -> Tuple[array.array]: - if get_zeroes.current_size != sz: - get_zeroes.current_size = sz - get_zeroes.ans = ( - array.array(lcode, repeat(0, sz)), - array.array(code, repeat(0, sz)), - array.array(code, repeat(32, sz)), - ) - return get_zeroes.ans -get_zeroes.current_size = None - - -class Cursor: - - __slots__ = ("x", "y", 'shape', 'blink', "hidden", 'color', 'fg', 'bg', 'bold', 'italic', 'reverse', 'strikethrough', 'decoration', 'decoration_fg',) - - def __init__(self, x: int=0, y: int=0): - self.x = x - self.y = y - self.hidden = False - self.shape = None - self.blink = None - self.color = None - self.reset_display_attrs() - - def reset_display_attrs(self): - self.fg = self.bg = self.decoration_fg = 0 - self.bold = self.italic = self.reverse = self.strikethrough = False - self.decoration = 0 - - def copy(self): - ans = Cursor(self.x, self.y) - ans.hidden = self.hidden - ans.fg, ans.bg, ans.decoration_fg = self.fg, self.bg, self.decoration_fg - ans.bold, ans.italic, ans.reverse, ans.strikethrough = self.bold, self.italic, self.reverse, self.strikethrough - ans.decoration = self.decoration - return ans - - def __eq__(self, other): - if not isinstance(other, Cursor): - return False - for x in self.__slots__: - if getattr(self, x) != getattr(other, x): - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return self.__class__.__name__ + '({})'.format(', '.join( - '{}={}'.format(x, getattr(self, x)) for x in self.__slots__)) - - def colors(self): - return as_color(self.fg, fg_color_table()), as_color(self.bg, bg_color_table()), as_color(self.decoration_fg, fg_color_table()) - -CHAR_MASK = 0xFFFFFF -ATTRS_SHIFT = 24 -WIDTH_MASK = 0xFF -DECORATION_SHIFT = 2 -BOLD_SHIFT, ITALIC_SHIFT, REVERSE_SHIFT, STRIKE_SHIFT = range(DECORATION_SHIFT + 2, DECORATION_SHIFT + 6) -DECORATION_MASK = 1 << DECORATION_SHIFT -BOLD_MASK = 1 << BOLD_SHIFT -ITALIC_MASK = 1 << ITALIC_SHIFT -REVERSE_MASK = 1 << REVERSE_SHIFT -STRIKE_MASK = 1 << STRIKE_SHIFT -COL_MASK = 0xFFFFFFFF -COL_SHIFT = 32 -HAS_BG_MASK = 0xFF << COL_SHIFT - - -class Line: - - __slots__ = 'char color decoration_fg continued combining_chars'.split() - - def __init__(self, sz: int, other=None): - if other is None: - z8, z4, spaces = get_zeroes(sz) - self.char = spaces[:] - self.color = z8[:] - self.decoration_fg = z4[:] - self.continued = False - self.combining_chars = {} - else: - self.char = other.char[:] - self.color = other.color[:] - self.decoration_fg = other.decoration_fg[:] - self.continued = other.continued - self.combining_chars = other.combining_chars.copy() - - # Read API {{{ - - def __eq__(self, other): - if not isinstance(other, Line): - return False - for x in self.__slots__: - if getattr(self, x) != getattr(other, x): - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.char) - - def copy(self): - return Line(len(self.char), self) - - def cursor_to_attrs(self, c: Cursor) -> int: - return ((c.decoration & 0b11) << DECORATION_SHIFT) | ((c.bold & 0b1) << BOLD_SHIFT) | \ - ((c.italic & 0b1) << ITALIC_SHIFT) | ((c.reverse & 0b1) << REVERSE_SHIFT) | ((c.strikethrough & 0b1) << STRIKE_SHIFT) - - def cursor_from(self, x: int, ypos: int=0) -> Cursor: - c = Cursor(x, ypos) - c.decoration_fg = self.decoration_fg[x] - col = self.color[x] - c.fg = col & COL_MASK - c.bg = col >> COL_SHIFT - attrs = self.char[x] >> ATTRS_SHIFT - c.decoration = (attrs >> DECORATION_SHIFT) & 0b11 - c.bold = bool(attrs & BOLD_MASK) - c.italic = bool(attrs & ITALIC_MASK) - c.reverse = bool(attrs & REVERSE_MASK) - c.strikethrough = bool(attrs & STRIKE_MASK) - return c - - def basic_cell_data(self, pos: int): - c = self.char[pos] - cols = self.color[pos] - return c & CHAR_MASK, c >> ATTRS_SHIFT, cols - - def bgcolor(self, pos: int) -> int: - cols = self.color[pos] - return cols >> COL_SHIFT - - def __iter__(self): - for i in range(len(self)): - yield self.text_at(i) - - def __str__(self) -> str: - return ''.join(self) - - def __repr__(self) -> str: - return repr(str(self)) - - def width(self, i): - return (self.char[i] >> ATTRS_SHIFT) & 0b11 - - def text_at(self, i): - ch = self.char[i] & CHAR_MASK - if ch: - ans = chr(ch) - cc = self.combining_chars.get(i) - if cc is not None: - ans += cc - return ans - return '' - # }}} - - # Write API {{{ - - def copy_char(self, src: int, to, dest: int) -> None: - to.char[dest] = self.char[src] - to.color[dest] = self.color[src] - to.decoration_fg[dest] = self.decoration_fg[src] - to.combining_chars.pop(dest, None) - cc = self.combining_chars.get(src) - if cc is not None: - to.combining_chars[dest] = cc - - def set_text(self, text: str, offset_in_text: int, sz: int, cursor: Cursor) -> None: - ' Set the specified text in this line, with attributes taken from the cursor ' - attrs = self.cursor_to_attrs(cursor) | 1 - fg, bg, dfg = cursor.fg, cursor.bg, cursor.decoration_fg - col = (fg & COL_MASK) | ((bg & COL_MASK) << COL_SHIFT) - dx = cursor.x - for cpos in range(offset_in_text, offset_in_text + sz): - ch = ord(text[cpos]) & CHAR_MASK - self.char[dx] = ch | (attrs << ATTRS_SHIFT) - self.color[dx], self.decoration_fg[dx] = col, dfg - dx += 1 - if self.combining_chars: - for i in range(cursor.x, cursor.x + sz): - self.combining_chars.pop(i, None) - - def clear_text(self, start, num, clear_char=' '): - ' Clear the text in the specified range, preserving existing attributes ' - ch = ord(clear_char) & CHAR_MASK - for i in range(start, min(len(self), start + num)): - self.char[i] = (self.char[i] & ~CHAR_MASK) | ch - - def copy_slice(self, src, dest, num): - if self.combining_chars: - scc = self.combining_chars.copy() - for i in range(num): - cc = scc.get(src + i) - if cc is None: - self.combining_chars.pop(dest + i, None) - else: - self.combining_chars[dest + i] = cc - src, dest = slice(src, src + num), slice(dest, dest + num) - for a in (self.char, self.color, self.decoration_fg): - a[dest] = a[src] - - def right_shift(self, at: int, num: int) -> None: - src_start, dest_start = at, at + num - ls = len(self) - dnum = min(ls - dest_start, ls) - if dnum: - self.copy_slice(src_start, dest_start, dnum) - # Check if a wide character was split at the right edge - w = (self.char[-1] >> ATTRS_SHIFT) & 0b11 - if w != 1: - self.char[-1] = (w << ATTRS_SHIFT) | ord(' ') - - def left_shift(self, at: int, num: int) -> None: - src_start, dest_start = at + num, at - ls = len(self) - snum = min(ls - src_start, ls) - if snum: - self.copy_slice(src_start, dest_start, snum) - - def apply_cursor(self, c: Cursor, at: int=0, num: int=1, clear_char=False) -> None: - col = ((c.bg & COL_MASK) << COL_SHIFT) | (c.fg & COL_MASK) - dfg = c.decoration_fg - s, e = at, min(len(self), at + num) - chara, color, dfga = self.char, self.color, self.decoration_fg - cattrs = self.cursor_to_attrs(c) - if clear_char: - ch = (32 & CHAR_MASK) | ((cattrs | 1) << ATTRS_SHIFT) - for i in range(s, e): - chara[i] = ch - color[i] = col - dfga[i] = dfg - if self.combining_chars: - if e - s >= len(self): - self.combining_chars.clear() - else: - for i in range(s, e): - self.combining_chars.pop(i, None) - else: - for i in range(s, e): - color[i] = col - dfga[i] = dfg - sc = chara[i] - sattrs = sc >> ATTRS_SHIFT - w = sattrs & WIDTH_MASK - attrs = w | cattrs - chara[i] = (sc & CHAR_MASK) | (attrs << ATTRS_SHIFT) - - def set_char(self, i: int, ch: str, width: int=1, cursor: Cursor=None) -> None: - if cursor is None: - c = self.char[i] - a = (c >> ATTRS_SHIFT) & ~WIDTH_MASK - else: - a = self.cursor_to_attrs(cursor) - col = (cursor.fg & COL_MASK) | ((cursor.bg & COL_MASK) << COL_SHIFT) - self.color[i], self.decoration_fg[i] = col, cursor.decoration_fg - a |= width & WIDTH_MASK - self.char[i] = (a << ATTRS_SHIFT) | (ord(ch) & CHAR_MASK) - self.combining_chars.pop(i, None) - - def add_combining_char(self, i: int, ch: str): - # TODO: Handle the case when i is the second cell of a double-width char - self.combining_chars[i] = self.combining_chars.get(i, '') + ch - - def set_bold(self, i, val): - c = self.char[i] - a = c >> ATTRS_SHIFT - a = (a & ~BOLD_MASK) | ((val & 0b1) << BOLD_SHIFT) - self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) - - def set_italic(self, i, val): - c = self.char[i] - a = c >> ATTRS_SHIFT - a = (a & ~ITALIC_MASK) | ((val & 0b1) << ITALIC_SHIFT) - self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) - - def set_reverse(self, i, val): - c = self.char[i] - a = c >> ATTRS_SHIFT - a = (a & ~REVERSE_MASK) | ((val & 0b1) << REVERSE_SHIFT) - self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) - - def set_strikethrough(self, i, val): - c = self.char[i] - a = c >> ATTRS_SHIFT - a = (a & ~STRIKE_MASK) | ((val & 0b1) << STRIKE_SHIFT) - self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) - - def set_decoration(self, i, val): - c = self.char[i] - a = c >> ATTRS_SHIFT - a = (a & ~DECORATION_MASK) | ((val & 0b11) << DECORATION_SHIFT) - self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) - # }}} - - -def as_color(entry: int, color_table: Dict[int, Tuple[int]]) -> Union[Tuple[int], None]: - t = entry & 0xff - if t == 1: - r = (entry >> 8) & 0xff - return color_table.get(r) - if t == 2: - r = (entry >> 8) & 0xff - return FG_BG_256[r] - if t == 3: - r = (entry >> 8) & 0xff - g = (entry >> 16) & 0xff - b = (entry >> 24) & 0xff - return r, g, b - - -def copy_char(src: Line, dest: Line, src_pos: int, dest_pos: int) -> None: - for i in range(src.width(src_pos)): - src.copy_char(src_pos + i, dest, dest_pos + i) - - -def rewrap_lines(lines: Sequence[Line], width: int) -> Iterator[Line]: - if lines: - current_line, current_dest_pos = Line(width), 0 - src_limit = len(lines[0]) - 1 - for i, src in enumerate(lines): - current_src_pos = 0 - while current_src_pos <= src_limit: - cw = src.width(current_src_pos) - if cw == 0: - # Hard line break, start a new line - yield current_line - current_line, current_dest_pos = Line(width), 0 - break - if cw + current_dest_pos > width: - # dest line does not have enough space to hold the current source char - yield current_line - current_line, current_dest_pos = Line(width), 0 - current_line.continued = True - copy_char(src, current_line, current_src_pos, current_dest_pos) - current_dest_pos += cw - current_src_pos += cw - else: - hard_break = src is not lines[-1] and not lines[i + 1].continued - if hard_break: - yield current_line - current_line, current_dest_pos = Line(width), 0 - if current_dest_pos: - yield current_line diff --git a/kitty/line.c b/kitty/line.c index 6b78545a9..d0dc7daa7 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -32,7 +32,7 @@ text_at(Line* self, Py_ssize_t xval) { combining_type cc; PyObject *ans; - if (xval >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Column number out of bounds"); return NULL; } + if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } ch = self->chars[xval] & CHAR_MASK; cc = self->combining_chars[xval]; @@ -89,13 +89,22 @@ __repr__(Line* self) { static PyObject* width(Line *self, PyObject *val) { -#define width_doc "width_doc(x) -> the width of the character at x" +#define width_doc "width(x) -> the width of the character at x" unsigned long x = PyLong_AsUnsignedLong(val); if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } char_type attrs = self->chars[x] >> ATTRS_SHIFT; return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK)); } +static PyObject* +basic_cell_data(Line *self, PyObject *val) { +#define basic_cell_data_doc "basic_cell_data(x) -> ch, attrs, colors" + unsigned long x = PyLong_AsUnsignedLong(val); + if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } + char_type ch = self->chars[x]; + return Py_BuildValue("IBK", (unsigned int)(ch & CHAR_MASK), (unsigned char)(ch >> ATTRS_SHIFT), (unsigned long long)self->colors[x]); +} + static PyObject* add_combining_char(Line* self, PyObject *args) { #define add_combining_char_doc "add_combining_char(x, ch) -> Add the specified character as a combining char to the specified cell." @@ -325,6 +334,7 @@ static PyMethodDef methods[] = { METHOD(set_char, METH_VARARGS) METHOD(set_attribute, METH_VARARGS) METHOD(width, METH_O) + METHOD(basic_cell_data, METH_O) {NULL} /* Sentinel */ }; diff --git a/kitty/shaders.py b/kitty/shaders.py index b63d2bbab..8cc1c2f8d 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -6,7 +6,6 @@ from ctypes import addressof, sizeof from functools import lru_cache from .fonts import render_cell -from .data_types import ITALIC_MASK, BOLD_MASK from .fast_data_types import ( glCreateProgram, glAttachShader, GL_FRAGMENT_SHADER, GL_VERTEX_SHADER, glLinkProgram, GL_TRUE, GL_LINK_STATUS, glGetProgramiv, @@ -21,11 +20,13 @@ from .fast_data_types import ( GL_NEAREST, GL_TEXTURE_WRAP_T, glGenBuffers, GL_R8, GL_RED, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_STATIC_DRAW, GL_TEXTURE_BUFFER, GL_RGB32UI, glBindBuffer, glPixelStorei, glTexBuffer, glActiveTexture, - glTexStorage3D, glCopyImageSubData, glTexSubImage3D + glTexStorage3D, glCopyImageSubData, glTexSubImage3D, ITALIC, BOLD ) GL_VERSION = (3, 3) VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10 +ITALIC_MASK = 1 << ITALIC +BOLD_MASK = 1 << BOLD class Sprites: