More work on native streams

This commit is contained in:
Kovid Goyal 2016-11-13 10:21:54 +05:30
parent 62fc6cc4a0
commit fab2213c25
20 changed files with 652 additions and 161 deletions

57
generate-unicode-data.py Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import unicodedata
import itertools
import sys
IGNORED_CATEGORIES = ('Cc', 'Cf', 'Cn', 'Cs')
def ranges(i):
for a, b in itertools.groupby(enumerate(i), lambda r: r[1] - r[0]):
b = list(b)
yield b[0][1], b[-1][1]
def generate_data(chars):
points, cranges = [], []
for l, r in ranges(chars):
if l == r:
points.append(l)
else:
cranges.append((l, r))
return points, cranges
def generate_predicate(name, chars):
points, ranges = generate_data(chars)
ranges = ['(0x%s %s ch && ch <= 0x%s)' % (l, '<' if l == 0 else '<=', r) for l, r in ranges]
points = ['(ch == 0x%x)' % p for p in points]
return '''
static inline bool %s(uint32_t ch) {
return %s || %s;
}
''' % (name, '||'.join(ranges), '||'.join(points))
def main():
combining_chars = []
igchars = []
for c in map(chr, range(sys.maxunicode + 1)):
if unicodedata.category(c) in IGNORED_CATEGORIES:
igchars.append(ord(c))
if unicodedata.combining(c):
combining_chars.append(ord(c))
cc = generate_predicate('is_combining_char', combining_chars)
ig = generate_predicate('is_ignored_char', igchars)
with open('kitty/unicode-data.h', 'w') as f:
print('#pragma once', file=f)
print(cc, file=f)
print(ig, file=f)
if __name__ == '__main__':
main()
# TODO: delete kitty/unicode.py alongwith kitty/screen.py

View File

@ -97,10 +97,10 @@ class Boss(Thread):
def on_focus(self, window, focused): def on_focus(self, window, focused):
if focused: if focused:
if self.screen.enable_focus_tracking: if self.screen.enable_focus_tracking():
self.write_to_child(b'\x1b[I') self.write_to_child(b'\x1b[I')
else: else:
if self.screen.enable_focus_tracking: if self.screen.enable_focus_tracking():
self.write_to_child(b'\x1b[O') self.write_to_child(b'\x1b[O')
def on_mouse_button(self, window, button, action, mods): def on_mouse_button(self, window, button, action, mods):
@ -110,7 +110,7 @@ class Boss(Thread):
# text = glfw.glfwGetClipboardString(window) # text = glfw.glfwGetClipboardString(window)
text = subprocess.check_output(['xsel']) text = subprocess.check_output(['xsel'])
if text: if text:
if self.screen.in_bracketed_paste_mode: if self.screen.in_bracketed_paste_mode():
text = BRACKETED_PASTE_START.encode('ascii') + text + BRACKETED_PASTE_END.encode('ascii') text = BRACKETED_PASTE_START.encode('ascii') + text + BRACKETED_PASTE_END.encode('ascii')
self.write_to_child(text) self.write_to_child(text)

View File

@ -16,7 +16,8 @@ from .fast_data_types import (
glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, glClear, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, glClear,
GL_COLOR_BUFFER_BIT, glClearColor, glViewport, glUniform2ui, glUniform4f, GL_COLOR_BUFFER_BIT, glClearColor, glViewport, glUniform2ui, glUniform4f,
glUniform1i, glUniform2f, glDrawArraysInstanced, GL_TRIANGLE_FAN, glUniform1i, glUniform2f, glDrawArraysInstanced, GL_TRIANGLE_FAN,
glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile, REVERSE glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile, REVERSE,
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
) )
Size = namedtuple('Size', 'width height') Size = namedtuple('Size', 'width height')
@ -303,11 +304,11 @@ class CharGrid:
col = cursor.color or self.default_cursor.color col = cursor.color or self.default_cursor.color
shape = cursor.shape or self.default_cursor.shape shape = cursor.shape or self.default_cursor.shape
alpha = self.opts.cursor_opacity alpha = self.opts.cursor_opacity
if alpha < 1.0 and shape == 'block': if alpha < 1.0 and shape == CURSOR_BLOCK:
glEnable(GL_BLEND) glEnable(GL_BLEND)
right = left + (width(1.5) if shape == 'beam' else sg.dx) right = left + (width(1.5) if shape == CURSOR_BEAM else sg.dx)
bottom = top - sg.dy bottom = top - sg.dy
if shape == 'underline': if shape == CURSOR_UNDERLINE:
top = bottom + width(vert=False) top = bottom + width(vert=False)
glUniform4f(ul('color'), col[0], col[1], col[2], alpha) glUniform4f(ul('color'), col[0], col[1], col[2], alpha)
glUniform2f(ul('xpos'), left, right) glUniform2f(ul('xpos'), left, right)

View File

@ -166,3 +166,33 @@ uint16_t* translation_table(char which) {
} }
return charset_translations[0]; return charset_translations[0];
} }
// UTF-8 decode taken from: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
static const uint8_t utf8_data[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};
uint32_t decode_utf8(uint32_t* state, uint32_t* codep, uint8_t byte) {
uint32_t type = utf8_data[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8_data[256 + *state*16 + type];
return *state;
}

View File

@ -63,7 +63,7 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
} }
static void static void
dealloc(Cursor* self) { dealloc(ColorProfile* self) {
Py_TYPE(self)->tp_free((PyObject*)self); Py_TYPE(self)->tp_free((PyObject*)self);
} }

View File

@ -6,6 +6,7 @@ import re
from collections import namedtuple from collections import namedtuple
from itertools import repeat from itertools import repeat
from .fast_data_types import CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$') key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
# Color definitions {{{ # Color definitions {{{
@ -186,12 +187,14 @@ def to_font_size(x):
return max(6, float(x)) return max(6, float(x))
cshapes = {'block': CURSOR_BLOCK, 'beam': CURSOR_BEAM, 'underline': CURSOR_UNDERLINE}
def to_cursor_shape(x): def to_cursor_shape(x):
shapes = 'block underline beam' try:
x = x.lower() return cshapes[x.lower()]
if x not in shapes.split(): except KeyError:
raise ValueError('Invalid cursor shape: {} allowed values are {}'.format(x, shapes)) raise ValueError('Invalid cursor shape: {} allowed values are {}'.format(x, ', '.join(cshapes)))
return x
def to_bool(x): def to_bool(x):

View File

@ -9,50 +9,30 @@
#include <structmember.h> #include <structmember.h>
#define INIT_NONE(x) Py_INCREF(Py_None); x = Py_None;
static PyObject * static PyObject *
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
Cursor *self; Cursor *self;
self = (Cursor *)type->tp_alloc(type, 0); self = (Cursor *)type->tp_alloc(type, 0);
if (self != NULL) {
self->x = PyLong_FromLong(0);
if (self->x == NULL) { Py_CLEAR(self); return NULL; }
self->y = self->x; Py_INCREF(self->y);
INIT_NONE(self->shape);
INIT_NONE(self->blink);
INIT_NONE(self->color);
self->hidden = Py_False; Py_INCREF(Py_False);
self->bold = 0; self->italic = 0; self->reverse = 0; self->strikethrough = 0; self->decoration = 0;
self->fg = 0; self->bg = 0; self->decoration_fg = 0;
}
return (PyObject*) self; return (PyObject*) self;
} }
static void static void
dealloc(Cursor* self) { dealloc(Cursor* self) {
Py_CLEAR(self->shape);
Py_CLEAR(self->blink);
Py_CLEAR(self->color);
Py_CLEAR(self->hidden);
Py_CLEAR(self->x);
Py_CLEAR(self->y);
Py_TYPE(self)->tp_free((PyObject*)self); Py_TYPE(self)->tp_free((PyObject*)self);
} }
#define EQ(x) (a->x == b->x) #define EQ(x) (a->x == b->x)
#define PEQ(x) (PyObject_RichCompareBool(a->x, b->x, Py_EQ) == 1)
static int __eq__(Cursor *a, Cursor *b) { static int __eq__(Cursor *a, Cursor *b) {
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && PEQ(x) && PEQ(y) && PEQ(shape) && PEQ(blink) && PEQ(color) && PEQ(hidden); return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(blink) && EQ(color) && EQ(hidden);
} }
#define BOOL(x) ((x) ? Py_True : Py_False) #define BOOL(x) ((x) ? Py_True : Py_False)
static PyObject * static PyObject *
repr(Cursor *self) { repr(Cursor *self) {
return PyUnicode_FromFormat( return PyUnicode_FromFormat(
"Cursor(x=%R, y=%R, shape=%R, blink=%R, hidden=%R, color=%R, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, decoration=%d, decoration_fg=#%08x)", "Cursor(x=%u, y=%u, shape=%d, blink=%R, hidden=%R, color=#%08x, fg=#%08x, bg=#%08x, bold=%R, italic=%R, reverse=%R, strikethrough=%R, decoration=%d, decoration_fg=#%08x)",
self->x, self->y, self->shape, self->blink, self->hidden, self->color, self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), self->decoration, self->decoration_fg self->x, self->y, self->shape, BOOL(self->blink), BOOL(self->hidden), self->color, self->fg, self->bg, BOOL(self->bold), BOOL(self->italic), BOOL(self->reverse), BOOL(self->strikethrough), self->decoration, self->decoration_fg
); );
} }
@ -64,29 +44,39 @@ reset_display_attrs(Cursor *self) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
void cursor_reset(Cursor *self) {
self->x = 0; self->y = 0;
self->shape = 0; self->blink = false;
self->color = 0; self->hidden = false;
}
static PyObject* static PyObject*
copy(Cursor *self, PyObject *args); copy(Cursor *self);
#define copy_doc "Create a clone of this cursor" #define copy_doc "Create a clone of this cursor"
static PyObject* color_get(Cursor *self, void UNUSED *closure) {
if (!(self->color & 0xFF)) { Py_RETURN_NONE; }
return Py_BuildValue("BBB", (self->color >> 24) & 0xFF, (self->color >> 16) & 0xFF, (self->color >> 8) & 0xFF);
}
// Boilerplate {{{ // Boilerplate {{{
BOOL_GETSET(Cursor, bold) BOOL_GETSET(Cursor, bold)
BOOL_GETSET(Cursor, italic) BOOL_GETSET(Cursor, italic)
BOOL_GETSET(Cursor, reverse) BOOL_GETSET(Cursor, reverse)
BOOL_GETSET(Cursor, strikethrough) BOOL_GETSET(Cursor, strikethrough)
BOOL_GETSET(Cursor, hidden)
BOOL_GETSET(Cursor, blink)
static PyMemberDef members[] = { static PyMemberDef members[] = {
{"x", T_OBJECT_EX, offsetof(Cursor, x), 0, "x"}, {"x", T_INT, offsetof(Cursor, x), 0, "x"},
{"y", T_OBJECT_EX, offsetof(Cursor, y), 0, "y"}, {"y", T_INT, offsetof(Cursor, y), 0, "y"},
{"shape", T_OBJECT_EX, offsetof(Cursor, shape), 0, "shape"}, {"shape", T_UBYTE, offsetof(Cursor, shape), 0, "shape"},
{"blink", T_OBJECT_EX, offsetof(Cursor, blink), 0, "blink"}, {"color", T_ULONG, offsetof(Cursor, color), 0, "color"},
{"color", T_OBJECT_EX, offsetof(Cursor, color), 0, "color"},
{"hidden", T_OBJECT_EX, offsetof(Cursor, hidden), 0, "hidden"},
{"decoration", T_UBYTE, offsetof(Cursor, decoration), 0, "decoration"}, {"decoration", T_UBYTE, offsetof(Cursor, decoration), 0, "decoration"},
{"fg", T_UINT, offsetof(Cursor, fg), 0, "fg"}, {"fg", T_ULONG, offsetof(Cursor, fg), 0, "fg"},
{"bg", T_UINT, offsetof(Cursor, bg), 0, "bg"}, {"bg", T_ULONG, offsetof(Cursor, bg), 0, "bg"},
{"decoration_fg", T_UINT, offsetof(Cursor, decoration_fg), 0, "decoration_fg"}, {"decoration_fg", T_ULONG, offsetof(Cursor, decoration_fg), 0, "decoration_fg"},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
@ -95,6 +85,9 @@ static PyGetSetDef getseters[] = {
GETSET(italic) GETSET(italic)
GETSET(reverse) GETSET(reverse)
GETSET(strikethrough) GETSET(strikethrough)
GETSET(hidden)
GETSET(blink)
{"color", (getter) color_get, NULL, "color", NULL},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
@ -127,16 +120,20 @@ RICHCMP(Cursor)
// }}} // }}}
static PyObject* Cursor*
copy(Cursor *self, PyObject UNUSED *args) { cursor_copy(Cursor *self) {
#define CPY(x) ans->x = self->x; Py_XINCREF(self->x);
#define CCY(x) ans->x = self->x; #define CCY(x) ans->x = self->x;
Cursor* ans; Cursor* ans;
ans = alloc_cursor(); ans = alloc_cursor();
if (ans == NULL) { PyErr_NoMemory(); return NULL; } if (ans == NULL) { PyErr_NoMemory(); return NULL; }
CPY(x); CPY(y); CPY(shape); CPY(blink); CPY(color); CPY(hidden); CCY(x); CCY(y); CCY(shape); CCY(blink); CCY(color); CCY(hidden);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg); CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
return (PyObject*)ans; return ans;
}
static PyObject*
copy(Cursor *self) {
return (PyObject*)cursor_copy(self);
} }
Cursor *alloc_cursor() { Cursor *alloc_cursor() {

View File

@ -6,16 +6,6 @@
*/ */
#include "data-types.h" #include "data-types.h"
extern int init_LineBuf(PyObject *);
extern int init_Cursor(PyObject *);
extern int init_Line(PyObject *);
extern int init_ColorProfile(PyObject *);
extern int init_SpriteMap(PyObject *);
extern int init_ChangeTracker(PyObject *);
extern int init_Screen(PyObject *);
extern PyObject* create_256_color_table();
extern PyObject* parse_bytes_dump(PyObject UNUSED *self, PyObject *val);
extern PyObject* parse_bytes(PyObject UNUSED *self, PyObject *val);
#include "gl.h" #include "gl.h"
#include "modes.h" #include "modes.h"
@ -58,6 +48,9 @@ PyInit_fast_data_types(void) {
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT); PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
PyModule_AddStringMacro(m, BRACKETED_PASTE_START); PyModule_AddStringMacro(m, BRACKETED_PASTE_START);
PyModule_AddStringMacro(m, BRACKETED_PASTE_END); PyModule_AddStringMacro(m, BRACKETED_PASTE_END);
PyModule_AddIntMacro(m, CURSOR_BLOCK);
PyModule_AddIntMacro(m, CURSOR_BEAM);
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
} }
return m; return m;

View File

@ -37,6 +37,12 @@ typedef unsigned int index_type;
#define HAS_BG_MASK (0xFF << COL_SHIFT) #define HAS_BG_MASK (0xFF << COL_SHIFT)
#define CC_MASK 0xFFFF #define CC_MASK 0xFFFF
#define CC_SHIFT 16 #define CC_SHIFT 16
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
#define CURSOR_BLOCK 1
#define CURSOR_BEAM 2
#define CURSOR_UNDERLINE 3
#define CURSOR_TO_ATTRS(c, w) \ #define CURSOR_TO_ATTRS(c, w) \
((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \ ((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \
@ -119,6 +125,7 @@ typedef struct {
bool continued; bool continued;
bool needs_free; bool needs_free;
} Line; } Line;
PyTypeObject Line_Type;
typedef struct { typedef struct {
@ -136,17 +143,19 @@ typedef struct {
decoration_type *decoration_fg; decoration_type *decoration_fg;
combining_type *combining_chars; combining_type *combining_chars;
} LineBuf; } LineBuf;
PyTypeObject LineBuf_Type;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *x, *y, *shape, *blink, *hidden, *color; bool bold, italic, reverse, strikethrough, blink, hidden;
bool bold, italic, reverse, strikethrough; int x, y;
uint8_t decoration; uint8_t decoration, shape;
uint32_t fg, bg, decoration_fg; unsigned long fg, bg, decoration_fg, color;
} Cursor; } Cursor;
PyTypeObject Cursor_Type;
typedef struct { typedef struct {
@ -156,6 +165,7 @@ typedef struct {
uint32_t ansi_color_table[120]; uint32_t ansi_color_table[120];
} ColorProfile; } ColorProfile;
PyTypeObject ColorProfile_Type;
typedef struct SpritePosition SpritePosition; typedef struct SpritePosition SpritePosition;
struct SpritePosition { struct SpritePosition {
@ -167,6 +177,7 @@ struct SpritePosition {
bool filled; bool filled;
bool rendered; bool rendered;
}; };
PyTypeObject SpritePosition_Type;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -177,6 +188,7 @@ typedef struct {
bool dirty; bool dirty;
} SpriteMap; } SpriteMap;
PyTypeObject SpriteMap_Type;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -190,12 +202,26 @@ typedef struct {
bool *changed_cells; bool *changed_cells;
unsigned int history_line_added_count; unsigned int history_line_added_count;
} ChangeTracker; } ChangeTracker;
PyTypeObject ChangeTracker_Type;
typedef struct { typedef struct {
bool LNM, IRM, DECTEM, DECSCNM, DECOM, DECAWM, DECCOLM; bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mBRACKETED_PASTE, mFOCUS_TRACKING;
} ScreenModes; } ScreenModes;
PyTypeObject ScreenModes_Type;
typedef struct {
PyObject_HEAD
unsigned int current_charset;
uint16_t *g0_charset, *g1_charset;
uint32_t utf8_state;
Cursor *cursor;
bool mDECOM;
bool mDECAWM;
} Savepoint;
PyTypeObject Savepoint_Type;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -215,14 +241,56 @@ typedef struct {
bool parser_has_pending_text; bool parser_has_pending_text;
} Screen; } Screen;
PyTypeObject Screen_Type;
Line* alloc_line(); Line* alloc_line();
Cursor* alloc_cursor(); Cursor* alloc_cursor();
LineBuf* alloc_linebuf(); LineBuf* alloc_linebuf();
ChangeTracker* alloc_change_tracker(); ChangeTracker* alloc_change_tracker();
Savepoint* alloc_savepoint();
#define left_shift_line(line, at, num) \ #define left_shift_line(line, at, num) \
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
COPY_CELL(line, __i__ + (num), line, __i__) \ COPY_CELL(line, __i__ + (num), line, __i__) \
} \ } \
if ((((line)->chars[(at)] >> ATTRS_SHIFT) & WIDTH_MASK) != 1) (line)->chars[(at)] = (1 << ATTRS_SHIFT) | 32; if ((((line)->chars[(at)] >> ATTRS_SHIFT) & WIDTH_MASK) != 1) (line)->chars[(at)] = (1 << ATTRS_SHIFT) | 32;
// Global functions
int init_LineBuf(PyObject *);
int init_Cursor(PyObject *);
int init_Line(PyObject *);
int init_ColorProfile(PyObject *);
int init_SpriteMap(PyObject *);
int init_ChangeTracker(PyObject *);
int init_Screen(PyObject *);
PyObject* create_256_color_table();
PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *);
PyObject* parse_bytes(PyObject UNUSED *, PyObject *);
uint16_t* translation_table(char);
uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte);
void linebuf_init_line(LineBuf *, index_type);
void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *);
void line_right_shift(Line *, unsigned int , unsigned int );
void line_add_combining_char(Line *, uint32_t , unsigned int );
void cursor_reset(Cursor*);
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
Cursor* cursor_copy(Cursor*);
void linebuf_clear(LineBuf *);
bool screen_restore_cursor(Screen *);
bool screen_save_cursor(Screen *);
bool screen_cursor_position(Screen*, unsigned int, unsigned int);
bool screen_erase_in_display(Screen *, unsigned int, bool);
bool screen_draw(Screen *screen, uint8_t *buf, unsigned int buflen);
bool update_cell_range_data(SpriteMap *, Line *, unsigned int, unsigned int, ColorProfile *, const uint32_t, const uint32_t, unsigned int *);
uint32_t to_color(ColorProfile *, uint32_t, uint32_t);
PyObject* line_text_at(char_type, combining_type);
void linebuf_init_line(LineBuf *, index_type);
#define DECLARE_CH_SCREEN_HANDLER(name) bool screen_##name(Screen *screen, uint8_t ch);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)
DECLARE_CH_SCREEN_HANDLER(tab)
DECLARE_CH_SCREEN_HANDLER(linefeed)
DECLARE_CH_SCREEN_HANDLER(carriage_return)
DECLARE_CH_SCREEN_HANDLER(shift_out)
DECLARE_CH_SCREEN_HANDLER(shift_in)

View File

@ -7,7 +7,6 @@
#include "data-types.h" #include "data-types.h"
#include <structmember.h> #include <structmember.h>
extern PyTypeObject Line_Type;
static inline void static inline void
clear_chars_to_space(LineBuf* linebuf, index_type y) { clear_chars_to_space(LineBuf* linebuf, index_type y) {
@ -15,15 +14,19 @@ clear_chars_to_space(LineBuf* linebuf, index_type y) {
for (index_type i = 0; i < linebuf->xnum; i++) chars[i] = (1 << ATTRS_SHIFT) | 32; for (index_type i = 0; i < linebuf->xnum; i++) chars[i] = (1 << ATTRS_SHIFT) | 32;
} }
static PyObject* void linebuf_clear(LineBuf *self) {
clear(LineBuf *self) {
#define clear_doc "Clear all lines in this LineBuf"
memset(self->buf, 0, self->block_size * CELL_SIZE); memset(self->buf, 0, self->block_size * CELL_SIZE);
memset(self->continued_map, 0, self->ynum * sizeof(bool)); memset(self->continued_map, 0, self->ynum * sizeof(bool));
for (index_type i = 0; i < self->ynum; i++) { for (index_type i = 0; i < self->ynum; i++) {
clear_chars_to_space(self, i); clear_chars_to_space(self, i);
self->line_map[i] = i; self->line_map[i] = i;
} }
}
static PyObject*
clear(LineBuf *self) {
#define clear_doc "Clear all lines in this LineBuf"
linebuf_clear(self);
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -110,16 +113,20 @@ line(LineBuf *self, PyObject *y) {
return (PyObject*)self->line; return (PyObject*)self->line;
} }
void linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) {
char_type mask;
for (index_type y = 0; y < self->ynum; y++) {
SET_ATTRIBUTE(self->chars + y * self->xnum, shift, val);
}
}
static PyObject* static PyObject*
set_attribute(LineBuf *self, PyObject *args) { set_attribute(LineBuf *self, PyObject *args) {
#define set_attribute_doc "set_attribute(which, val) -> Set the attribute on all cells in the line." #define set_attribute_doc "set_attribute(which, val) -> Set the attribute on all cells in the line."
unsigned int shift, val; unsigned int shift, val;
char_type mask;
if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL; if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL;
if (shift < DECORATION_SHIFT || shift > STRIKE_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; } if (shift < DECORATION_SHIFT || shift > STRIKE_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; }
for (index_type y = 0; y < self->ynum; y++) { linebuf_set_attribute(self, shift, val);
SET_ATTRIBUTE(self->chars + y * self->xnum, shift, val);
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -6,7 +6,6 @@
*/ */
#include "data-types.h" #include "data-types.h"
extern PyTypeObject Cursor_Type;
static PyObject * static PyObject *
new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
@ -108,6 +107,12 @@ basic_cell_data(Line *self, PyObject *val) {
return Py_BuildValue("IBK", (unsigned int)(ch & CHAR_MASK), (unsigned char)(ch >> ATTRS_SHIFT), (unsigned long long)self->colors[x]); return Py_BuildValue("IBK", (unsigned int)(ch & CHAR_MASK), (unsigned char)(ch >> ATTRS_SHIFT), (unsigned long long)self->colors[x]);
} }
void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) {
combining_type c = self->combining_chars[x];
if (c & CC_MASK) self->combining_chars[x] = (c & CC_MASK) | ( (ch & CC_MASK) << CC_SHIFT );
else self->combining_chars[x] = ch & CC_MASK;
}
static PyObject* static PyObject*
add_combining_char(Line* self, PyObject *args) { 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." #define add_combining_char_doc "add_combining_char(x, ch) -> Add the specified character as a combining char to the specified cell."
@ -118,9 +123,7 @@ add_combining_char(Line* self, PyObject *args) {
PyErr_SetString(PyExc_ValueError, "Column index out of bounds"); PyErr_SetString(PyExc_ValueError, "Column index out of bounds");
return NULL; return NULL;
} }
combining_type c = self->combining_chars[x]; line_add_combining_char(self, new_char, x);
if (c & CC_MASK) self->combining_chars[x] = (c & CC_MASK) | ( (new_char & CC_MASK) << CC_SHIFT );
else self->combining_chars[x] = new_char & CC_MASK;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -134,7 +137,6 @@ set_text(Line* self, PyObject *args) {
Cursor *cursor; Cursor *cursor;
int kind; int kind;
void *buf; void *buf;
unsigned long x;
if (!PyArg_ParseTuple(args, "UnnO!", &src, &offset, &sz, &Cursor_Type, &cursor)) return NULL; if (!PyArg_ParseTuple(args, "UnnO!", &src, &offset, &sz, &Cursor_Type, &cursor)) return NULL;
if (PyUnicode_READY(src) != 0) { if (PyUnicode_READY(src) != 0) {
@ -148,12 +150,11 @@ set_text(Line* self, PyObject *args) {
PyErr_SetString(PyExc_ValueError, "Out of bounds offset/sz"); PyErr_SetString(PyExc_ValueError, "Out of bounds offset/sz");
return NULL; return NULL;
} }
x = PyLong_AsUnsignedLong(cursor->x);
attrs = CURSOR_TO_ATTRS(cursor, 1); attrs = CURSOR_TO_ATTRS(cursor, 1);
color_type col = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT); color_type col = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT);
decoration_type dfg = cursor->decoration_fg & COL_MASK; decoration_type dfg = cursor->decoration_fg & COL_MASK;
for (index_type i = x; offset < limit && i < self->xnum; i++, offset++) { for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
self->chars[i] = (PyUnicode_READ(kind, buf, offset) & CHAR_MASK) | attrs; self->chars[i] = (PyUnicode_READ(kind, buf, offset) & CHAR_MASK) | attrs;
self->colors[i] = col; self->colors[i] = col;
self->decoration_fg[i] = dfg; self->decoration_fg[i] = dfg;
@ -166,23 +167,16 @@ set_text(Line* self, PyObject *args) {
static PyObject* static PyObject*
cursor_from(Line* self, PyObject *args) { cursor_from(Line* self, PyObject *args) {
#define cursor_from_doc "cursor_from(x, y=0) -> Create a cursor object based on the formatting attributes at the specified x position. The y value of the cursor is set as specified." #define cursor_from_doc "cursor_from(x, y=0) -> Create a cursor object based on the formatting attributes at the specified x position. The y value of the cursor is set as specified."
unsigned long x, y = 0; unsigned int x, y = 0;
PyObject *xo, *yo;
Cursor* ans; Cursor* ans;
if (!PyArg_ParseTuple(args, "k|k", &x, &y)) return NULL; if (!PyArg_ParseTuple(args, "I|I", &x, &y)) return NULL;
if (x >= self->xnum) { if (x >= self->xnum) {
PyErr_SetString(PyExc_ValueError, "Out of bounds x"); PyErr_SetString(PyExc_ValueError, "Out of bounds x");
return NULL; return NULL;
} }
ans = alloc_cursor(); ans = alloc_cursor();
if (ans == NULL) { PyErr_NoMemory(); return NULL; } if (ans == NULL) { PyErr_NoMemory(); return NULL; }
xo = PyLong_FromUnsignedLong(x); yo = PyLong_FromUnsignedLong(y); ans->x = x; ans->y = y;
if (xo == NULL || yo == NULL) {
Py_CLEAR(ans); Py_CLEAR(xo); Py_CLEAR(yo);
PyErr_NoMemory(); return NULL;
}
Py_CLEAR(ans->x); Py_CLEAR(ans->y);
ans->x = xo; ans->y = yo;
char_type attrs = self->chars[x] >> ATTRS_SHIFT; char_type attrs = self->chars[x] >> ATTRS_SHIFT;
ATTRS_TO_CURSOR(attrs, ans); ATTRS_TO_CURSOR(attrs, ans);
COLORS_TO_CURSOR(self->colors[x], ans); COLORS_TO_CURSOR(self->colors[x], ans);
@ -228,6 +222,15 @@ apply_cursor(Line* self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
void line_right_shift(Line *self, unsigned int at, unsigned int num) {
for(index_type i = self->xnum - 1; i >= at + num; i--) {
COPY_SELF_CELL(i - num, i)
}
// Check if a wide character was split at the right edge
char_type w = (self->chars[self->xnum - 1] >> ATTRS_SHIFT) & 3;
if (w != 1) self->chars[self->xnum - 1] = (1 << ATTRS_SHIFT) | 32;
}
static PyObject* static PyObject*
right_shift(Line *self, PyObject *args) { right_shift(Line *self, PyObject *args) {
#define right_shift_doc "right_shift(at, num) -> ..." #define right_shift_doc "right_shift(at, num) -> ..."
@ -238,12 +241,7 @@ right_shift(Line *self, PyObject *args) {
return NULL; return NULL;
} }
if (num > 0) { if (num > 0) {
for(index_type i = self->xnum - 1; i >= at + num; i--) { line_right_shift(self, at, num);
COPY_SELF_CELL(i - num, i)
}
// Check if a wide character was split at the right edge
char_type w = (self->chars[self->xnum - 1] >> ATTRS_SHIFT) & 3;
if (w != 1) self->chars[self->xnum - 1] = (1 << ATTRS_SHIFT) | 32;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -261,20 +259,8 @@ left_shift(Line *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject* void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor) {
set_char(Line *self, PyObject *args) {
#define set_char_doc "set_char(at, ch, width=1, cursor=None) -> Set the character at the specified cell. If cursor is not None, also set attributes from that cursor."
unsigned int at, width=1;
int ch;
Cursor *cursor = NULL;
char_type attrs; char_type attrs;
if (!PyArg_ParseTuple(args, "IC|IO!", &at, &ch, &width, &Cursor_Type, &cursor)) return NULL;
if (at >= self->xnum) {
PyErr_SetString(PyExc_ValueError, "Out of bounds");
return NULL;
}
if (cursor == NULL) { if (cursor == NULL) {
attrs = (((self->chars[at] >> ATTRS_SHIFT) & ~3) | (width & 3)) << ATTRS_SHIFT; attrs = (((self->chars[at] >> ATTRS_SHIFT) & ~3) | (width & 3)) << ATTRS_SHIFT;
} else { } else {
@ -284,6 +270,21 @@ set_char(Line *self, PyObject *args) {
} }
self->chars[at] = (ch & CHAR_MASK) | attrs; self->chars[at] = (ch & CHAR_MASK) | attrs;
self->combining_chars[at] = 0; self->combining_chars[at] = 0;
}
static PyObject*
set_char(Line *self, PyObject *args) {
#define set_char_doc "set_char(at, ch, width=1, cursor=None) -> Set the character at the specified cell. If cursor is not None, also set attributes from that cursor."
unsigned int at, width=1;
int ch;
Cursor *cursor = NULL;
if (!PyArg_ParseTuple(args, "IC|IO!", &at, &ch, &width, &Cursor_Type, &cursor)) return NULL;
if (at >= self->xnum) {
PyErr_SetString(PyExc_ValueError, "Out of bounds");
return NULL;
}
line_set_char(self, at, ch, width, cursor);
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -18,7 +18,7 @@
right margin are lost. Otherwise, new display characters replace right margin are lost. Otherwise, new display characters replace
old display characters at the cursor position. old display characters at the cursor position.
*/ */
#define IRM #define IRM 4
// Private modes. // Private modes.

View File

@ -8,24 +8,12 @@
#include "data-types.h" #include "data-types.h"
#include "control-codes.h" #include "control-codes.h"
extern PyTypeObject Screen_Type;
#define NORMAL_STATE 0 #define NORMAL_STATE 0
#define ESC_STATE 1 #define ESC_STATE 1
#define CSI_STATE 2 #define CSI_STATE 2
#define OSC_STATE 3 #define OSC_STATE 3
#define DCS_STATE 4 #define DCS_STATE 4
#define DECLARE_CH_SCREEN_HANDLER(name) extern bool screen_##name(Screen *screen, uint8_t ch);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)
DECLARE_CH_SCREEN_HANDLER(tab)
DECLARE_CH_SCREEN_HANDLER(linefeed)
DECLARE_CH_SCREEN_HANDLER(carriage_return)
DECLARE_CH_SCREEN_HANDLER(shift_out)
DECLARE_CH_SCREEN_HANDLER(shift_in)
extern bool screen_draw(Screen *screen, uint8_t *buf, unsigned int buflen);
// Parse text {{{ // Parse text {{{
static inline bool static inline bool
read_text(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) { read_text(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) {

47
kitty/savepoints.c Normal file
View File

@ -0,0 +1,47 @@
/*
* savepoints.c
* Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "data-types.h"
static PyObject *
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
Savepoint *self;
self = (Savepoint *)type->tp_alloc(type, 0);
return (PyObject*) self;
}
static void
dealloc(Savepoint* self) {
Py_TYPE(self)->tp_free((PyObject*)self);
}
// Boilerplate {{{
static PyMethodDef methods[] = {
{NULL} /* Sentinel */
};
PyTypeObject Savepoint_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Savepoint",
.tp_basicsize = sizeof(Savepoint),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Savepoint",
.tp_methods = methods,
.tp_new = new,
};
INIT_TYPE(Savepoint)
Savepoint *alloc_savepoint() {
return (Savepoint*)new(&Savepoint_Type, NULL, NULL);
}
// }}}

View File

@ -6,19 +6,27 @@
*/ */
#include "data-types.h" #include "data-types.h"
#include <structmember.h>
#include "unicode-data.h"
#include "tracker.h"
#include "modes.h"
static const ScreenModes empty_modes = {0}; static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true};
// Constructor/destructor {{{
static PyObject* static PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
Screen *self; Screen *self;
PyObject *callbacks = Py_None; PyObject *callbacks = Py_None;
if (!PyArg_ParseTuple(args, "|O", &callbacks)) return NULL; unsigned int columns, lines;
if (!PyArg_ParseTuple(args, "|OII", &callbacks, &lines, &columns)) return NULL;
self = (Screen *)type->tp_alloc(type, 0); self = (Screen *)type->tp_alloc(type, 0);
if (self != NULL) { if (self != NULL) {
self->current_charset = 2; self->current_charset = 2;
self->columns = 80; self->lines=24; self->g0_charset = translation_table('B');
self->g1_charset = translation_table('0');
self->columns = columns; self->lines = lines;
self->modes = empty_modes; self->modes = empty_modes;
self->utf8_state = 0; self->utf8_state = 0;
self->margin_top = 0; self->margin_bottom = self->lines - 1; self->margin_top = 0; self->margin_bottom = self->lines - 1;
@ -42,60 +50,349 @@ dealloc(Screen* self) {
Py_CLEAR(self->cursor); Py_CLEAR(self->main_linebuf); Py_CLEAR(self->alt_linebuf); Py_CLEAR(self->cursor); Py_CLEAR(self->main_linebuf); Py_CLEAR(self->alt_linebuf);
Py_CLEAR(self->main_savepoints); Py_CLEAR(self->alt_savepoints); Py_CLEAR(self->change_tracker); Py_CLEAR(self->main_savepoints); Py_CLEAR(self->alt_savepoints); Py_CLEAR(self->change_tracker);
Py_TYPE(self)->tp_free((PyObject*)self); Py_TYPE(self)->tp_free((PyObject*)self);
} } // }}}
bool screen_bell(Screen UNUSED *scr, uint8_t ch) { bool screen_bell(Screen UNUSED *self, uint8_t ch) { // {{{
FILE *f = fopen("/dev/tty", "w"); FILE *f = fopen("/dev/tty", "w");
if (f != NULL) { if (f != NULL) {
fwrite(&ch, 1, 1, f); fwrite(&ch, 1, 1, f);
fclose(f); fclose(f);
} }
return true; return true;
} } // }}}
bool screen_draw(Screen UNUSED *scr, uint8_t UNUSED ch) {
bool screen_linefeed(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_backspace(Screen UNUSED *scr, uint8_t UNUSED ch) { bool screen_carriage_return(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this
return true;
}
bool screen_tab(Screen UNUSED *scr, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_linefeed(Screen UNUSED *scr, uint8_t UNUSED ch) { // Draw text {{{
static inline int safe_wcwidth(uint32_t ch) {
int ans = wcwidth(ch);
if (ans < 0) ans = 1;
return MIN(2, ans);
}
static inline bool
draw_codepoint(Screen UNUSED *self, uint32_t ch) {
if (is_ignored_char(ch)) return true;
int char_width = safe_wcwidth(ch);
int space_left_in_line = self->columns - self->cursor->x;
if (space_left_in_line < char_width) {
if (self->modes.mDECAWM) {
if (!screen_carriage_return(self, 13)) return false;
if (!screen_linefeed(self, 10)) return false;
self->linebuf->continued_map[self->cursor->y] = true;
} else {
self->cursor->x = self->columns - char_width;
}
}
if (char_width > 0) {
unsigned int cx = self->cursor->x;
linebuf_init_line(self->linebuf, self->cursor->y);
if (self->modes.mIRM) {
line_right_shift(self->linebuf->line, self->cursor->x, char_width);
}
line_set_char(self->linebuf->line, self->cursor->x, ch, char_width, self->cursor);
self->cursor->x++;
if (char_width == 2) {
line_set_char(self->linebuf->line, self->cursor->x, 0, 0, self->cursor);
self->cursor->x++;
}
unsigned int right = self->modes.mIRM ? self->columns - 1 : MIN((unsigned int)(MAX(self->cursor->x, 1) - 1), self->columns - 1);
tracker_update_cell_range(self->change_tracker, self->cursor->y, cx, right);
} else if (is_combining_char(ch)) {
if (self->cursor->x > 0) {
linebuf_init_line(self->linebuf, self->cursor->y);
line_add_combining_char(self->linebuf->line, ch, self->cursor->x - 1);
tracker_update_cell_range(self->change_tracker, self->cursor->y, self->cursor->x - 1, self->cursor->x - 1);
} else if (self->cursor->y > 0) {
linebuf_init_line(self->linebuf, self->cursor->y - 1);
line_add_combining_char(self->linebuf->line, ch, self->columns - 1);
tracker_update_cell_range(self->change_tracker, self->cursor->y - 1, self->columns - 1, self->columns - 1);
}
}
return true;
}
static inline bool
screen_draw_utf8(Screen *self, uint8_t *buf, unsigned int buflen) {
uint32_t prev = UTF8_ACCEPT, codepoint = 0;
for (unsigned int i = 0; i < buflen; i++, prev = self->utf8_state) {
switch (decode_utf8(&self->utf8_state, &codepoint, buf[i])) {
case UTF8_ACCEPT:
if (!draw_codepoint(self, codepoint)) return false;
break;
case UTF8_REJECT:
self->utf8_state = UTF8_ACCEPT;
if (prev != UTF8_ACCEPT) i--;
break;
}
}
return true;
}
static inline bool
screen_draw_charset(Screen *self, unsigned short *table, uint8_t *buf, unsigned int buflen) {
for (unsigned int i = 0; i < buflen; i++) {
if (!draw_codepoint(self, table[buf[i]])) return false;
}
return true;
}
bool screen_draw(Screen *self, uint8_t *buf, unsigned int buflen) {
switch(self->current_charset) {
case 0:
return screen_draw_charset(self, self->g0_charset, buf, buflen);
break;
case 1:
return screen_draw_charset(self, self->g1_charset, buf, buflen);
break;
default:
return screen_draw_utf8(self, buf, buflen); break;
}
}
// }}}
bool screen_backspace(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_carriage_return(Screen UNUSED *scr, uint8_t UNUSED ch) { bool screen_tab(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_shift_out(Screen UNUSED *scr, uint8_t UNUSED ch) { bool screen_shift_out(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_shift_in(Screen UNUSED *scr, uint8_t UNUSED ch) { bool screen_shift_in(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
return true; return true;
} }
bool screen_toggle_screen_buffer(Screen *self) {
if (!screen_save_cursor(self)) return false;
if (self->linebuf == self->main_linebuf) {
self->linebuf = self->alt_linebuf;
self->savepoints = self->alt_savepoints;
} else {
self->linebuf = self->main_linebuf;
self->savepoints = self->main_savepoints;
}
screen_restore_cursor(self);
tracker_update_screen(self->change_tracker);
return true;
}
// Modes {{{
static inline void set_mode_from_const(Screen *self, int mode, bool val) {
switch(mode) {
case LNM:
self->modes.mLNM = val; break;
case IRM:
self->modes.mIRM = val; break;
case DECTCEM:
self->modes.mDECTCEM = val; break;
case DECSCNM:
self->modes.mDECSCNM = val; break;
case DECOM:
self->modes.mDECOM = val; break;
case DECAWM:
self->modes.mDECAWM = val; break;
case DECCOLM:
self->modes.mDECCOLM = val; break;
case BRACKETED_PASTE:
self->modes.mBRACKETED_PASTE = val; break;
case FOCUS_TRACKING:
self->modes.mFOCUS_TRACKING = val; break;
}
}
bool screen_set_mode(Screen *self, int mode) {
if (mode == DECCOLM) {
// When DECCOLM mode is set, the screen is erased and the cursor
// moves to the home position.
if (!screen_erase_in_display(self, 2, false)) return false;
if (!screen_cursor_position(self, 1, 1)) return false;
}
// According to `vttest`, DECOM should also home the cursor, see
// vttest/main.c:303.
if (mode == DECOM) { if (!screen_cursor_position(self, 1, 1)) return false; }
if (mode == DECSCNM) {
// Mark all displayed characters as reverse.
linebuf_set_attribute(self->linebuf, REVERSE_SHIFT, 1);
tracker_update_screen(self->change_tracker);
self->cursor->reverse = true;
tracker_cursor_changed(self->change_tracker);
}
if (mode == DECTCEM && self->cursor->hidden) {
self->cursor->hidden = false;
tracker_cursor_changed(self->change_tracker);
}
if (mode == ALTERNATE_SCREEN && self->linebuf == self->main_linebuf) {
if (!screen_toggle_screen_buffer(self)) return false;
}
set_mode_from_const(self, mode, true);
return true;
}
static PyObject*
in_bracketed_paste_mode(Screen *self) {
#define in_bracketed_paste_mode_doc ""
PyObject *ans = self->modes.mBRACKETED_PASTE ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyObject*
enable_focus_tracking(Screen *self) {
#define enable_focus_tracking_doc ""
PyObject *ans = self->modes.mFOCUS_TRACKING ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
bool screen_reset_mode(Screen *self, int mode) {
if (mode == DECCOLM) {
// When DECCOLM mode is set, the screen is erased and the cursor
// moves to the home position.
if (!screen_erase_in_display(self, 2, false)) return false;
if (!screen_cursor_position(self, 1, 1)) return false;
}
// According to `vttest`, DECOM should also home the cursor, see
// vttest/main.c:303.
if (mode == DECOM) { if (!screen_cursor_position(self, 1, 1)) return false; }
if (mode == DECSCNM) {
// Mark all displayed characters as reverse.
linebuf_set_attribute(self->linebuf, REVERSE_SHIFT, 0);
tracker_update_screen(self->change_tracker);
self->cursor->reverse = false;
tracker_cursor_changed(self->change_tracker);
}
if (mode == DECTCEM && !self->cursor->hidden) {
self->cursor->hidden = true;
tracker_cursor_changed(self->change_tracker);
}
if (mode == ALTERNATE_SCREEN && self->linebuf != self->main_linebuf) {
if (!screen_toggle_screen_buffer(self)) return false;
}
set_mode_from_const(self, mode, false);
return true;
}
// }}}
// Cursor {{{
bool screen_save_cursor(Screen *self) {
Savepoint *sp = alloc_savepoint();
if (sp == NULL) return false;
sp->cursor = cursor_copy(self->cursor);
if (sp->cursor == NULL) { Py_CLEAR(sp); return NULL; }
sp->g0_charset = self->g0_charset;
sp->g1_charset = self->g1_charset;
sp->current_charset = self->current_charset;
sp->mDECOM = self->modes.mDECOM;
sp->mDECAWM = self->modes.mDECAWM;
sp->utf8_state = self->utf8_state;
bool ret = PyList_Append(self->savepoints, (PyObject*)sp) == 0;
Py_CLEAR(sp);
return ret;
}
bool screen_restore_cursor(Screen *self) {
Py_ssize_t sz = PyList_GET_SIZE(self->savepoints);
if (sz > 0) {
Savepoint *sp = (Savepoint*)PyList_GET_ITEM(self->savepoints, sz - 1);
self->g0_charset = sp->g0_charset;
self->g1_charset = sp->g1_charset;
self->current_charset = sp->current_charset;
self->utf8_state = sp->utf8_state;
if (sp->mDECOM) screen_set_mode(self, DECOM);
if (sp->mDECAWM) screen_set_mode(self, DECAWM);
PyList_SetSlice(self->savepoints, sz-1, sz, NULL);
} else {
screen_cursor_position(self, 1, 1);
tracker_cursor_changed(self->change_tracker);
screen_reset_mode(self, DECOM);
}
return true;
}
bool screen_cursor_position(Screen UNUSED *self, unsigned int UNUSED line, unsigned int UNUSED column) {
return true; // TODO: Implement this
}
// }}}
// Editing {{{
bool screen_erase_in_display(Screen UNUSED *self, unsigned int UNUSED how, bool UNUSED private) {
return true; // TODO: Implement this
}
// }}}
bool screen_reset(Screen *self) {
if (self->linebuf == self->alt_linebuf) {if (!screen_toggle_screen_buffer(self)) return false; }
linebuf_clear(self->linebuf);
// TODO: Implement this
return true;
}
static PyObject*
line(Screen *self, PyObject *val) {
#define line_doc ""
unsigned long y = PyLong_AsUnsignedLong(val);
if (y >= self->lines) { PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL; }
linebuf_init_line(self->linebuf, y);
Py_INCREF(self->linebuf->line);
return (PyObject*) self->linebuf->line;
}
static PyObject*
draw(Screen *self, PyObject *args) {
#define draw_doc ""
Py_buffer pybuf;
if(!PyArg_ParseTuple(args, "y*", &pybuf)) return NULL;
if (!screen_draw(self, pybuf.buf, pybuf.len)) return NULL;
Py_RETURN_NONE;
}
// Boilerplate {{{ // Boilerplate {{{
static PyMethodDef methods[] = { static PyMethodDef methods[] = {
METHOD(line, METH_O)
METHOD(draw, METH_VARARGS)
METHOD(enable_focus_tracking, METH_NOARGS)
METHOD(in_bracketed_paste_mode, METH_NOARGS)
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyMemberDef members[] = {
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), 0, "cursor"},
{"linebuf", T_OBJECT_EX, offsetof(Screen, linebuf), 0, "linebuf"},
{NULL}
};
PyTypeObject Screen_Type = { PyTypeObject Screen_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Screen", .tp_name = "fast_data_types.Screen",
@ -104,6 +401,7 @@ PyTypeObject Screen_Type = {
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Screen", .tp_doc = "Screen",
.tp_methods = methods, .tp_methods = methods,
.tp_members = members,
.tp_new = new, .tp_new = new,
}; };

View File

@ -11,7 +11,7 @@ from typing import Sequence
from pyte import charsets as cs, graphics as g, modes as mo from pyte import charsets as cs, graphics as g, modes as mo
from .utils import wcwidth, is_simple_string, sanitize_title from .utils import wcwidth, is_simple_string, sanitize_title
from .unicode import ignore_pat from .unicode import ignore_pat
from .fast_data_types import LineBuf, REVERSE, Cursor, ChangeTracker from .fast_data_types import LineBuf, REVERSE, Cursor, ChangeTracker, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
#: A container for screen's scroll margins. #: A container for screen's scroll margins.
@ -251,11 +251,9 @@ class Screen:
if mo.ALTERNATE_SCREEN in self.mode and self.linebuf is self.main_linebuf: if mo.ALTERNATE_SCREEN in self.mode and self.linebuf is self.main_linebuf:
self.toggle_screen_buffer() self.toggle_screen_buffer()
@property
def in_bracketed_paste_mode(self): def in_bracketed_paste_mode(self):
return mo.BRACKETED_PASTE in self.mode return mo.BRACKETED_PASTE in self.mode
@property
def enable_focus_tracking(self): def enable_focus_tracking(self):
return mo.FOCUS_TRACKING in self.mode return mo.FOCUS_TRACKING in self.mode
@ -302,7 +300,6 @@ class Screen:
from ``b"B0UK"``, otherwise ignored. from ``b"B0UK"``, otherwise ignored.
:param str mode: if ``"("`` ``G0`` charset is defined, if :param str mode: if ``"("`` ``G0`` charset is defined, if
``")"`` -- we operate on ``G1``. ``")"`` -- we operate on ``G1``.
.. warning:: User-defined charsets are currently not supported. .. warning:: User-defined charsets are currently not supported.
""" """
if code in cs.MAPS: if code in cs.MAPS:
@ -984,7 +981,7 @@ class Screen:
shape = blink = None shape = blink = None
if mode > 0: if mode > 0:
blink = bool(mode % 2) blink = bool(mode % 2)
shape = 'block' if mode < 3 else 'underline' if mode < 5 else 'beam' if mode < 7 else None shape = CURSOR_BLOCK if mode < 3 else CURSOR_UNDERLINE if mode < 5 else CURSOR_BEAM if mode < 7 else None
if shape != self.cursor.shape or blink != self.cursor.blink: if shape != self.cursor.shape or blink != self.cursor.blink:
self.cursor.shape, self.cursor.blink = shape, blink self.cursor.shape, self.cursor.blink = shape, blink
self.cursor_changed() self.cursor_changed()

View File

@ -7,10 +7,6 @@
#include "data-types.h" #include "data-types.h"
#include <structmember.h> #include <structmember.h>
extern PyTypeObject Line_Type;
extern PyTypeObject ColorProfile_Type;
extern uint32_t to_color(ColorProfile *, uint32_t, uint32_t);
extern PyObject* line_text_at(char_type, combining_type);
static PyObject* static PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {

View File

@ -7,11 +7,6 @@
#include "data-types.h" #include "data-types.h"
#include "tracker.h" #include "tracker.h"
extern PyTypeObject SpriteMap_Type;
extern PyTypeObject ColorProfile_Type;
extern PyTypeObject LineBuf_Type;
extern bool update_cell_range_data(SpriteMap *, Line *, unsigned int, unsigned int, ColorProfile *, const uint32_t, const uint32_t, unsigned int *);
extern void linebuf_init_line(LineBuf *, index_type);
#define RESET_STATE_VARS(self) \ #define RESET_STATE_VARS(self) \
self->screen_changed = false; self->cursor_changed = false; self->dirty = false; self->history_line_added_count = 0; self->screen_changed = false; self->cursor_changed = false; self->dirty = false; self->history_line_added_count = 0;
@ -273,7 +268,7 @@ static PyMethodDef methods[] = {
}; };
static PyTypeObject ChangeTracker_Type = { PyTypeObject ChangeTracker_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.ChangeTracker", .tp_name = "fast_data_types.ChangeTracker",
.tp_basicsize = sizeof(ChangeTracker), .tp_basicsize = sizeof(ChangeTracker),

11
kitty/unicode-data.h Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,8 +4,8 @@
from unittest import TestCase from unittest import TestCase
from kitty.screen import Screen from kitty.screen import Screen as S
from kitty.fast_data_types import LineBuf, Cursor from kitty.fast_data_types import LineBuf, Cursor, Screen
def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()): def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):
@ -31,8 +31,10 @@ class BaseTest(TestCase):
ae = TestCase.assertEqual ae = TestCase.assertEqual
def create_screen(self, cols=5, lines=5, history_size=5): def create_screen(self, cols=5, lines=5, history_size=5):
s = Screen(history_size, columns=cols, lines=lines) return S(history_size, columns=cols, lines=lines)
return s
def create_screen2(self, cols=5, lines=5, history_size=5):
return Screen(history_size, None, lines, cols)
def assertEqualAttributes(self, c1, c2): def assertEqualAttributes(self, c1, c2):
x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0 x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0