Remove the no longer needed data types module

This commit is contained in:
Kovid Goyal 2016-11-10 11:07:39 +05:30
parent 1884cc17c1
commit 8f8e9baedc
5 changed files with 64 additions and 380 deletions

View File

@ -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__) '

View File

@ -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 */
};

View File

@ -1,362 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
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

View File

@ -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 */
};

View File

@ -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: