More work on native streams
This commit is contained in:
parent
62fc6cc4a0
commit
fab2213c25
57
generate-unicode-data.py
Normal file
57
generate-unicode-data.py
Normal 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
|
||||
@ -97,10 +97,10 @@ class Boss(Thread):
|
||||
|
||||
def on_focus(self, window, focused):
|
||||
if focused:
|
||||
if self.screen.enable_focus_tracking:
|
||||
if self.screen.enable_focus_tracking():
|
||||
self.write_to_child(b'\x1b[I')
|
||||
else:
|
||||
if self.screen.enable_focus_tracking:
|
||||
if self.screen.enable_focus_tracking():
|
||||
self.write_to_child(b'\x1b[O')
|
||||
|
||||
def on_mouse_button(self, window, button, action, mods):
|
||||
@ -110,7 +110,7 @@ class Boss(Thread):
|
||||
# text = glfw.glfwGetClipboardString(window)
|
||||
text = subprocess.check_output(['xsel'])
|
||||
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')
|
||||
self.write_to_child(text)
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@ 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, ColorProfile, REVERSE
|
||||
glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile, REVERSE,
|
||||
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||
)
|
||||
|
||||
Size = namedtuple('Size', 'width height')
|
||||
@ -303,11 +304,11 @@ class CharGrid:
|
||||
col = cursor.color or self.default_cursor.color
|
||||
shape = cursor.shape or self.default_cursor.shape
|
||||
alpha = self.opts.cursor_opacity
|
||||
if alpha < 1.0 and shape == 'block':
|
||||
if alpha < 1.0 and shape == CURSOR_BLOCK:
|
||||
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
|
||||
if shape == 'underline':
|
||||
if shape == CURSOR_UNDERLINE:
|
||||
top = bottom + width(vert=False)
|
||||
glUniform4f(ul('color'), col[0], col[1], col[2], alpha)
|
||||
glUniform2f(ul('xpos'), left, right)
|
||||
|
||||
@ -166,3 +166,33 @@ uint16_t* translation_table(char which) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||
}
|
||||
|
||||
static void
|
||||
dealloc(Cursor* self) {
|
||||
dealloc(ColorProfile* self) {
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import re
|
||||
from collections import namedtuple
|
||||
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+(.+)$')
|
||||
# Color definitions {{{
|
||||
@ -186,12 +187,14 @@ def to_font_size(x):
|
||||
return max(6, float(x))
|
||||
|
||||
|
||||
cshapes = {'block': CURSOR_BLOCK, 'beam': CURSOR_BEAM, 'underline': CURSOR_UNDERLINE}
|
||||
|
||||
|
||||
def to_cursor_shape(x):
|
||||
shapes = 'block underline beam'
|
||||
x = x.lower()
|
||||
if x not in shapes.split():
|
||||
raise ValueError('Invalid cursor shape: {} allowed values are {}'.format(x, shapes))
|
||||
return x
|
||||
try:
|
||||
return cshapes[x.lower()]
|
||||
except KeyError:
|
||||
raise ValueError('Invalid cursor shape: {} allowed values are {}'.format(x, ', '.join(cshapes)))
|
||||
|
||||
|
||||
def to_bool(x):
|
||||
|
||||
@ -9,50 +9,30 @@
|
||||
|
||||
#include <structmember.h>
|
||||
|
||||
#define INIT_NONE(x) Py_INCREF(Py_None); x = Py_None;
|
||||
|
||||
static PyObject *
|
||||
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
|
||||
Cursor *self;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
#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) {
|
||||
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)
|
||||
static PyObject *
|
||||
repr(Cursor *self) {
|
||||
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)",
|
||||
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
|
||||
"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, 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;
|
||||
}
|
||||
|
||||
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*
|
||||
copy(Cursor *self, PyObject *args);
|
||||
copy(Cursor *self);
|
||||
#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 {{{
|
||||
|
||||
BOOL_GETSET(Cursor, bold)
|
||||
BOOL_GETSET(Cursor, italic)
|
||||
BOOL_GETSET(Cursor, reverse)
|
||||
BOOL_GETSET(Cursor, strikethrough)
|
||||
BOOL_GETSET(Cursor, hidden)
|
||||
BOOL_GETSET(Cursor, blink)
|
||||
|
||||
static PyMemberDef members[] = {
|
||||
{"x", T_OBJECT_EX, offsetof(Cursor, x), 0, "x"},
|
||||
{"y", T_OBJECT_EX, offsetof(Cursor, y), 0, "y"},
|
||||
{"shape", T_OBJECT_EX, offsetof(Cursor, shape), 0, "shape"},
|
||||
{"blink", T_OBJECT_EX, offsetof(Cursor, blink), 0, "blink"},
|
||||
{"color", T_OBJECT_EX, offsetof(Cursor, color), 0, "color"},
|
||||
{"hidden", T_OBJECT_EX, offsetof(Cursor, hidden), 0, "hidden"},
|
||||
|
||||
{"x", T_INT, offsetof(Cursor, x), 0, "x"},
|
||||
{"y", T_INT, offsetof(Cursor, y), 0, "y"},
|
||||
{"shape", T_UBYTE, offsetof(Cursor, shape), 0, "shape"},
|
||||
{"color", T_ULONG, offsetof(Cursor, color), 0, "color"},
|
||||
{"decoration", T_UBYTE, offsetof(Cursor, decoration), 0, "decoration"},
|
||||
{"fg", T_UINT, offsetof(Cursor, fg), 0, "fg"},
|
||||
{"bg", T_UINT, offsetof(Cursor, bg), 0, "bg"},
|
||||
{"decoration_fg", T_UINT, offsetof(Cursor, decoration_fg), 0, "decoration_fg"},
|
||||
{"fg", T_ULONG, offsetof(Cursor, fg), 0, "fg"},
|
||||
{"bg", T_ULONG, offsetof(Cursor, bg), 0, "bg"},
|
||||
{"decoration_fg", T_ULONG, offsetof(Cursor, decoration_fg), 0, "decoration_fg"},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@ -95,6 +85,9 @@ static PyGetSetDef getseters[] = {
|
||||
GETSET(italic)
|
||||
GETSET(reverse)
|
||||
GETSET(strikethrough)
|
||||
GETSET(hidden)
|
||||
GETSET(blink)
|
||||
{"color", (getter) color_get, NULL, "color", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@ -127,16 +120,20 @@ RICHCMP(Cursor)
|
||||
|
||||
// }}}
|
||||
|
||||
static PyObject*
|
||||
copy(Cursor *self, PyObject UNUSED *args) {
|
||||
#define CPY(x) ans->x = self->x; Py_XINCREF(self->x);
|
||||
Cursor*
|
||||
cursor_copy(Cursor *self) {
|
||||
#define CCY(x) ans->x = self->x;
|
||||
Cursor* ans;
|
||||
ans = alloc_cursor();
|
||||
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);
|
||||
return (PyObject*)ans;
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
copy(Cursor *self) {
|
||||
return (PyObject*)cursor_copy(self);
|
||||
}
|
||||
|
||||
Cursor *alloc_cursor() {
|
||||
|
||||
@ -6,16 +6,6 @@
|
||||
*/
|
||||
|
||||
#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 "modes.h"
|
||||
|
||||
@ -58,6 +48,9 @@ PyInit_fast_data_types(void) {
|
||||
PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT);
|
||||
PyModule_AddStringMacro(m, BRACKETED_PASTE_START);
|
||||
PyModule_AddStringMacro(m, BRACKETED_PASTE_END);
|
||||
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
||||
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
||||
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
||||
}
|
||||
|
||||
return m;
|
||||
|
||||
@ -37,6 +37,12 @@ typedef unsigned int index_type;
|
||||
#define HAS_BG_MASK (0xFF << COL_SHIFT)
|
||||
#define CC_MASK 0xFFFF
|
||||
#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) \
|
||||
((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \
|
||||
@ -119,6 +125,7 @@ typedef struct {
|
||||
bool continued;
|
||||
bool needs_free;
|
||||
} Line;
|
||||
PyTypeObject Line_Type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
@ -136,17 +143,19 @@ typedef struct {
|
||||
decoration_type *decoration_fg;
|
||||
combining_type *combining_chars;
|
||||
} LineBuf;
|
||||
PyTypeObject LineBuf_Type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
PyObject *x, *y, *shape, *blink, *hidden, *color;
|
||||
bool bold, italic, reverse, strikethrough;
|
||||
uint8_t decoration;
|
||||
uint32_t fg, bg, decoration_fg;
|
||||
bool bold, italic, reverse, strikethrough, blink, hidden;
|
||||
int x, y;
|
||||
uint8_t decoration, shape;
|
||||
unsigned long fg, bg, decoration_fg, color;
|
||||
|
||||
} Cursor;
|
||||
PyTypeObject Cursor_Type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
@ -156,6 +165,7 @@ typedef struct {
|
||||
uint32_t ansi_color_table[120];
|
||||
|
||||
} ColorProfile;
|
||||
PyTypeObject ColorProfile_Type;
|
||||
|
||||
typedef struct SpritePosition SpritePosition;
|
||||
struct SpritePosition {
|
||||
@ -167,6 +177,7 @@ struct SpritePosition {
|
||||
bool filled;
|
||||
bool rendered;
|
||||
};
|
||||
PyTypeObject SpritePosition_Type;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
@ -177,6 +188,7 @@ typedef struct {
|
||||
bool dirty;
|
||||
|
||||
} SpriteMap;
|
||||
PyTypeObject SpriteMap_Type;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
@ -190,12 +202,26 @@ typedef struct {
|
||||
bool *changed_cells;
|
||||
unsigned int history_line_added_count;
|
||||
} ChangeTracker;
|
||||
PyTypeObject ChangeTracker_Type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool LNM, IRM, DECTEM, DECSCNM, DECOM, DECAWM, DECCOLM;
|
||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mBRACKETED_PASTE, mFOCUS_TRACKING;
|
||||
} 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 {
|
||||
PyObject_HEAD
|
||||
@ -215,14 +241,56 @@ typedef struct {
|
||||
bool parser_has_pending_text;
|
||||
|
||||
} Screen;
|
||||
PyTypeObject Screen_Type;
|
||||
|
||||
Line* alloc_line();
|
||||
Cursor* alloc_cursor();
|
||||
LineBuf* alloc_linebuf();
|
||||
ChangeTracker* alloc_change_tracker();
|
||||
Savepoint* alloc_savepoint();
|
||||
|
||||
#define left_shift_line(line, at, num) \
|
||||
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
|
||||
COPY_CELL(line, __i__ + (num), line, __i__) \
|
||||
} \
|
||||
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)
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
|
||||
#include "data-types.h"
|
||||
#include <structmember.h>
|
||||
extern PyTypeObject Line_Type;
|
||||
|
||||
static inline void
|
||||
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;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
clear(LineBuf *self) {
|
||||
#define clear_doc "Clear all lines in this LineBuf"
|
||||
void linebuf_clear(LineBuf *self) {
|
||||
memset(self->buf, 0, self->block_size * CELL_SIZE);
|
||||
memset(self->continued_map, 0, self->ynum * sizeof(bool));
|
||||
for (index_type i = 0; i < self->ynum; i++) {
|
||||
clear_chars_to_space(self, 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;
|
||||
}
|
||||
|
||||
@ -110,16 +113,20 @@ line(LineBuf *self, PyObject *y) {
|
||||
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*
|
||||
set_attribute(LineBuf *self, PyObject *args) {
|
||||
#define set_attribute_doc "set_attribute(which, val) -> Set the attribute on all cells in the line."
|
||||
unsigned int shift, val;
|
||||
char_type mask;
|
||||
if (!PyArg_ParseTuple(args, "II", &shift, &val)) 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++) {
|
||||
SET_ATTRIBUTE(self->chars + y * self->xnum, shift, val);
|
||||
}
|
||||
linebuf_set_attribute(self, shift, val);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
73
kitty/line.c
73
kitty/line.c
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "data-types.h"
|
||||
extern PyTypeObject Cursor_Type;
|
||||
|
||||
static PyObject *
|
||||
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]);
|
||||
}
|
||||
|
||||
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*
|
||||
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."
|
||||
@ -118,9 +123,7 @@ add_combining_char(Line* self, PyObject *args) {
|
||||
PyErr_SetString(PyExc_ValueError, "Column index out of bounds");
|
||||
return NULL;
|
||||
}
|
||||
combining_type c = self->combining_chars[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;
|
||||
line_add_combining_char(self, new_char, x);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -134,7 +137,6 @@ set_text(Line* self, PyObject *args) {
|
||||
Cursor *cursor;
|
||||
int kind;
|
||||
void *buf;
|
||||
unsigned long x;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "UnnO!", &src, &offset, &sz, &Cursor_Type, &cursor)) return NULL;
|
||||
if (PyUnicode_READY(src) != 0) {
|
||||
@ -148,12 +150,11 @@ set_text(Line* self, PyObject *args) {
|
||||
PyErr_SetString(PyExc_ValueError, "Out of bounds offset/sz");
|
||||
return NULL;
|
||||
}
|
||||
x = PyLong_AsUnsignedLong(cursor->x);
|
||||
attrs = CURSOR_TO_ATTRS(cursor, 1);
|
||||
color_type col = (cursor->fg & COL_MASK) | ((color_type)(cursor->bg & COL_MASK) << COL_SHIFT);
|
||||
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->colors[i] = col;
|
||||
self->decoration_fg[i] = dfg;
|
||||
@ -166,23 +167,16 @@ set_text(Line* self, PyObject *args) {
|
||||
static PyObject*
|
||||
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."
|
||||
unsigned long x, y = 0;
|
||||
PyObject *xo, *yo;
|
||||
unsigned int x, y = 0;
|
||||
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) {
|
||||
PyErr_SetString(PyExc_ValueError, "Out of bounds x");
|
||||
return NULL;
|
||||
}
|
||||
ans = alloc_cursor();
|
||||
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
|
||||
xo = PyLong_FromUnsignedLong(x); yo = PyLong_FromUnsignedLong(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;
|
||||
ans->x = x; ans->y = y;
|
||||
char_type attrs = self->chars[x] >> ATTRS_SHIFT;
|
||||
ATTRS_TO_CURSOR(attrs, ans);
|
||||
COLORS_TO_CURSOR(self->colors[x], ans);
|
||||
@ -228,6 +222,15 @@ apply_cursor(Line* self, PyObject *args) {
|
||||
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*
|
||||
right_shift(Line *self, PyObject *args) {
|
||||
#define right_shift_doc "right_shift(at, num) -> ..."
|
||||
@ -238,12 +241,7 @@ right_shift(Line *self, PyObject *args) {
|
||||
return NULL;
|
||||
}
|
||||
if (num > 0) {
|
||||
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;
|
||||
line_right_shift(self, at, num);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@ -261,20 +259,8 @@ left_shift(Line *self, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
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;
|
||||
void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor) {
|
||||
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) {
|
||||
attrs = (((self->chars[at] >> ATTRS_SHIFT) & ~3) | (width & 3)) << ATTRS_SHIFT;
|
||||
} else {
|
||||
@ -284,6 +270,21 @@ set_char(Line *self, PyObject *args) {
|
||||
}
|
||||
self->chars[at] = (ch & CHAR_MASK) | attrs;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
right margin are lost. Otherwise, new display characters replace
|
||||
old display characters at the cursor position.
|
||||
*/
|
||||
#define IRM
|
||||
#define IRM 4
|
||||
|
||||
|
||||
// Private modes.
|
||||
|
||||
@ -8,24 +8,12 @@
|
||||
#include "data-types.h"
|
||||
#include "control-codes.h"
|
||||
|
||||
extern PyTypeObject Screen_Type;
|
||||
|
||||
#define NORMAL_STATE 0
|
||||
#define ESC_STATE 1
|
||||
#define CSI_STATE 2
|
||||
#define OSC_STATE 3
|
||||
#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 {{{
|
||||
static inline bool
|
||||
read_text(Screen *screen, uint8_t *buf, unsigned int buflen, unsigned int *pos) {
|
||||
|
||||
47
kitty/savepoints.c
Normal file
47
kitty/savepoints.c
Normal 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);
|
||||
}
|
||||
|
||||
// }}}
|
||||
332
kitty/screen.c
332
kitty/screen.c
@ -6,19 +6,27 @@
|
||||
*/
|
||||
|
||||
#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*
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
Screen *self;
|
||||
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);
|
||||
if (self != NULL) {
|
||||
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->utf8_state = 0;
|
||||
self->margin_top = 0; self->margin_bottom = self->lines - 1;
|
||||
@ -42,59 +50,348 @@ dealloc(Screen* self) {
|
||||
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_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");
|
||||
if (f != NULL) {
|
||||
fwrite(&ch, 1, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
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
|
||||
return true;
|
||||
}
|
||||
|
||||
bool screen_backspace(Screen UNUSED *scr, uint8_t UNUSED ch) {
|
||||
// TODO: Implement this
|
||||
return true;
|
||||
}
|
||||
|
||||
bool screen_tab(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_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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 {{{
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
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 = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
@ -104,6 +401,7 @@ PyTypeObject Screen_Type = {
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "Screen",
|
||||
.tp_methods = methods,
|
||||
.tp_members = members,
|
||||
.tp_new = new,
|
||||
};
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ from typing import Sequence
|
||||
from pyte import charsets as cs, graphics as g, modes as mo
|
||||
from .utils import wcwidth, is_simple_string, sanitize_title
|
||||
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.
|
||||
@ -251,11 +251,9 @@ class Screen:
|
||||
if mo.ALTERNATE_SCREEN in self.mode and self.linebuf is self.main_linebuf:
|
||||
self.toggle_screen_buffer()
|
||||
|
||||
@property
|
||||
def in_bracketed_paste_mode(self):
|
||||
return mo.BRACKETED_PASTE in self.mode
|
||||
|
||||
@property
|
||||
def enable_focus_tracking(self):
|
||||
return mo.FOCUS_TRACKING in self.mode
|
||||
|
||||
@ -302,7 +300,6 @@ class Screen:
|
||||
from ``b"B0UK"``, otherwise ignored.
|
||||
:param str mode: if ``"("`` ``G0`` charset is defined, if
|
||||
``")"`` -- we operate on ``G1``.
|
||||
|
||||
.. warning:: User-defined charsets are currently not supported.
|
||||
"""
|
||||
if code in cs.MAPS:
|
||||
@ -984,7 +981,7 @@ class Screen:
|
||||
shape = blink = None
|
||||
if mode > 0:
|
||||
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:
|
||||
self.cursor.shape, self.cursor.blink = shape, blink
|
||||
self.cursor_changed()
|
||||
|
||||
@ -7,10 +7,6 @@
|
||||
|
||||
#include "data-types.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*
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
|
||||
@ -7,11 +7,6 @@
|
||||
|
||||
#include "data-types.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) \
|
||||
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)
|
||||
.tp_name = "fast_data_types.ChangeTracker",
|
||||
.tp_basicsize = sizeof(ChangeTracker),
|
||||
|
||||
11
kitty/unicode-data.h
Normal file
11
kitty/unicode-data.h
Normal file
File diff suppressed because one or more lines are too long
@ -4,8 +4,8 @@
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from kitty.screen import Screen
|
||||
from kitty.fast_data_types import LineBuf, Cursor
|
||||
from kitty.screen import Screen as S
|
||||
from kitty.fast_data_types import LineBuf, Cursor, Screen
|
||||
|
||||
|
||||
def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):
|
||||
@ -31,8 +31,10 @@ class BaseTest(TestCase):
|
||||
ae = TestCase.assertEqual
|
||||
|
||||
def create_screen(self, cols=5, lines=5, history_size=5):
|
||||
s = Screen(history_size, columns=cols, lines=lines)
|
||||
return s
|
||||
return S(history_size, columns=cols, lines=lines)
|
||||
|
||||
def create_screen2(self, cols=5, lines=5, history_size=5):
|
||||
return Screen(history_size, None, lines, cols)
|
||||
|
||||
def assertEqualAttributes(self, c1, c2):
|
||||
x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user