More work on migrating Screen

This commit is contained in:
Kovid Goyal 2016-11-13 16:51:14 +05:30
parent 3c73da224a
commit fa106c2585
6 changed files with 161 additions and 44 deletions

View File

@ -36,15 +36,20 @@ repr(Cursor *self) {
); );
} }
static inline void cursor_reset_display_attrs(Cursor *self) {
self->bg = 0; self->fg = 0; self->decoration_fg = 0;
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false;
}
static PyObject * static PyObject *
reset_display_attrs(Cursor *self) { reset_display_attrs(Cursor *self) {
#define reset_display_attrs_doc "Reset all display attributes to unset" #define reset_display_attrs_doc "Reset all display attributes to unset"
self->bg = 0; self->fg = 0; self->decoration_fg = 0; cursor_reset_display_attrs(self);
self->decoration = 0; self->bold = false; self->italic = false; self->reverse = false; self->strikethrough = false;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
void cursor_reset(Cursor *self) { void cursor_reset(Cursor *self) {
cursor_reset_display_attrs(self);
self->x = 0; self->y = 0; self->x = 0; self->y = 0;
self->shape = 0; self->blink = false; self->shape = 0; self->blink = false;
self->color = 0; self->hidden = false; self->color = 0; self->hidden = false;

View File

@ -271,28 +271,33 @@ PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *);
PyObject* parse_bytes(PyObject UNUSED *, PyObject *); PyObject* parse_bytes(PyObject UNUSED *, PyObject *);
uint16_t* translation_table(char); uint16_t* translation_table(char);
uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte); uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte);
void linebuf_init_line(LineBuf *, index_type); void cursor_reset(Cursor*);
Cursor* cursor_copy(Cursor*);
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 line_clear_text(Line *self, unsigned int at, unsigned int num, int ch);
void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char);
void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *); void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *);
void line_right_shift(Line *, unsigned int , unsigned int ); void line_right_shift(Line *, unsigned int , unsigned int );
void line_add_combining_char(Line *, uint32_t , 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 ); void linebuf_init_line(LineBuf *, index_type);
Cursor* cursor_copy(Cursor*);
void linebuf_clear(LineBuf *); void linebuf_clear(LineBuf *);
void linebuf_init_line(LineBuf *, index_type);
void linebuf_index(LineBuf* self, index_type top, index_type bottom);
void linebuf_clear_line(LineBuf *self, index_type y);
void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom);
void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom);
void linebuf_set_attribute(LineBuf *, unsigned int , unsigned int );
void screen_restore_cursor(Screen *); void screen_restore_cursor(Screen *);
void screen_save_cursor(Screen *); void screen_save_cursor(Screen *);
void screen_cursor_position(Screen*, unsigned int, unsigned int); void screen_cursor_position(Screen*, unsigned int, unsigned int);
void screen_erase_in_display(Screen *, unsigned int, bool); void screen_erase_in_display(Screen *, unsigned int, bool);
void screen_draw(Screen *screen, uint8_t *buf, unsigned int buflen); void 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);
void linebuf_index(LineBuf* self, index_type top, index_type bottom);
void linebuf_clear_line(LineBuf *self, index_type y);
void screen_ensure_bounds(Screen *self, bool use_margins); void screen_ensure_bounds(Screen *self, bool use_margins);
void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch);
void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char);
void screen_toggle_screen_buffer(Screen *self); void screen_toggle_screen_buffer(Screen *self);
void screen_normal_keypad_mode(Screen *self); void screen_normal_keypad_mode(Screen *self);
void screen_alternate_keypad_mode(Screen *self); void screen_alternate_keypad_mode(Screen *self);

View File

@ -266,13 +266,9 @@ is_continued(LineBuf *self, PyObject *val) {
Py_RETURN_FALSE; Py_RETURN_FALSE;
} }
static PyObject* void linebuf_insert_lines(LineBuf *self, unsigned int num, unsigned int y, unsigned int bottom) {
insert_lines(LineBuf *self, PyObject *args) {
#define insert_lines_doc "insert_lines(num, y, bottom) -> Insert num blank lines at y, only changing lines in the range [y, bottom]."
unsigned int y, num, bottom;
index_type i; index_type i;
if (!PyArg_ParseTuple(args, "III", &num, &y, &bottom)) return NULL; if (y >= self->ynum || y > bottom || bottom >= self->ynum) return;
if (y >= self->ynum || y > bottom || bottom >= self->ynum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; }
index_type ylimit = bottom + 1; index_type ylimit = bottom + 1;
num = MIN(ylimit - y, num); num = MIN(ylimit - y, num);
if (num > 0) { if (num > 0) {
@ -294,12 +290,22 @@ insert_lines(LineBuf *self, PyObject *args) {
self->continued_map[i] = 0; self->continued_map[i] = 0;
} }
} }
}
static PyObject*
insert_lines(LineBuf *self, PyObject *args) {
#define insert_lines_doc "insert_lines(num, y, bottom) -> Insert num blank lines at y, only changing lines in the range [y, bottom]."
unsigned int y, num, bottom;
if (!PyArg_ParseTuple(args, "III", &num, &y, &bottom)) return NULL;
linebuf_insert_lines(self, num, y, bottom);
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static inline void do_delete(LineBuf *self, index_type num, index_type y, index_type bottom) { void linebuf_delete_lines(LineBuf *self, index_type num, index_type y, index_type bottom) {
index_type i; index_type i;
index_type ylimit = bottom + 1; index_type ylimit = bottom + 1;
num = MIN(bottom + 1 - y, num);
if (y >= self->ynum || y > bottom || bottom >= self->ynum || num < 1) return;
for (i = y; i < y + num; i++) { for (i = y; i < y + num; i++) {
self->scratch[i] = self->line_map[i]; self->scratch[i] = self->line_map[i];
} }
@ -324,11 +330,7 @@ delete_lines(LineBuf *self, PyObject *args) {
#define delete_lines_doc "delete_lines(num, y, bottom) -> Delete num blank lines at y, only changing lines in the range [y, bottom]." #define delete_lines_doc "delete_lines(num, y, bottom) -> Delete num blank lines at y, only changing lines in the range [y, bottom]."
unsigned int y, num, bottom; unsigned int y, num, bottom;
if (!PyArg_ParseTuple(args, "III", &num, &y, &bottom)) return NULL; if (!PyArg_ParseTuple(args, "III", &num, &y, &bottom)) return NULL;
if (y >= self->ynum || y > bottom || bottom >= self->ynum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } linebuf_delete_lines(self, num, y, bottom);
num = MIN(bottom + 1 - y, num);
if (num > 0) {
do_delete(self, num, y, bottom);
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -207,12 +207,16 @@ void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int
char_type attrs = CURSOR_TO_ATTRS(cursor, 1); char_type 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;
if (!clear_char) attrs = ((attrs >> ATTRS_SHIFT) & ~WIDTH_MASK) << ATTRS_SHIFT;
for (index_type i = at; i < self->xnum && i < at + num; i++) { for (index_type i = at; i < self->xnum && i < at + num; i++) {
if (clear_char) { if (clear_char) {
self->chars[i] = 32 | attrs; self->chars[i] = 32 | attrs;
self->combining_chars[i] = 0; self->combining_chars[i] = 0;
} else self->chars[i] = (self->chars[i] & CHAR_MASK) | attrs; } else {
char_type w = ((self->chars[i] >> ATTRS_SHIFT) & WIDTH_MASK) << ATTRS_SHIFT;
self->chars[i] = (self->chars[i] & CHAR_MASK) | attrs | w;
}
self->colors[i] = col; self->colors[i] = col;
self->decoration_fg[i] = dfg; self->decoration_fg[i] = dfg;
} }

View File

@ -34,6 +34,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->cursor = alloc_cursor(); self->cursor = alloc_cursor();
self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns); self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns);
self->linebuf = self->main_linebuf; self->linebuf = self->main_linebuf;
// TODO: Change the savepoints objects to use a circular buffer, so there are no mallocs during normal operation
self->main_savepoints = PyList_New(0); self->alt_savepoints = PyList_New(0); self->main_savepoints = PyList_New(0); self->alt_savepoints = PyList_New(0);
self->savepoints = self->main_savepoints; self->savepoints = self->main_savepoints;
self->change_tracker = alloc_change_tracker(lines, columns); self->change_tracker = alloc_change_tracker(lines, columns);
@ -184,19 +185,6 @@ void screen_shift_in(Screen UNUSED *self, uint8_t UNUSED ch) {
// TODO: Implement this // TODO: Implement this
} }
void screen_toggle_screen_buffer(Screen *self) {
screen_save_cursor(self);
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);
}
// Graphics {{{ // Graphics {{{
void screen_change_default_color(Screen *self, unsigned int which, uint32_t col) { void screen_change_default_color(Screen *self, unsigned int which, uint32_t col) {
if (self->callbacks == Py_None) return; if (self->callbacks == Py_None) return;
@ -210,6 +198,19 @@ void screen_change_default_color(Screen *self, unsigned int which, uint32_t col)
// Modes {{{ // Modes {{{
void screen_toggle_screen_buffer(Screen *self) {
screen_save_cursor(self);
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);
}
void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI
void screen_alternate_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI void screen_alternate_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI
@ -517,6 +518,61 @@ void screen_erase_in_display(Screen *self, unsigned int how, bool private) {
screen_erase_in_line(self, how, private); screen_erase_in_line(self, how, private);
} }
} }
void screen_insert_lines(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= (unsigned int)self->cursor->y && (unsigned int)self->cursor->y <= bottom) {
linebuf_insert_lines(self->linebuf, count, self->cursor->y, bottom);
tracker_update_line_range(self->change_tracker, self->cursor->y, bottom);
screen_carriage_return(self, 13);
}
}
void screen_delete_lines(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= (unsigned int)self->cursor->y && (unsigned int)self->cursor->y <= bottom) {
linebuf_delete_lines(self->linebuf, count, self->cursor->y, bottom);
tracker_update_line_range(self->change_tracker, self->cursor->y, bottom);
screen_carriage_return(self, 13);
}
}
void screen_insert_characters(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= (unsigned int)self->cursor->y && (unsigned int)self->cursor->y <= bottom) {
unsigned int x = self->cursor->x;
unsigned int num = MIN(self->columns - x, count);
linebuf_init_line(self->linebuf, self->cursor->y);
line_right_shift(self->linebuf->line, x, num);
line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);
tracker_update_cell_range(self->change_tracker, self->cursor->y, x, self->columns - 1);
}
}
void screen_delete_characters(Screen *self, unsigned int count) {
unsigned int top = self->margin_top, bottom = self->margin_bottom;
if (count == 0) count = 1;
if (top <= (unsigned int)self->cursor->y && (unsigned int)self->cursor->y <= bottom) {
unsigned int x = self->cursor->x;
unsigned int num = MIN(self->columns - x, count);
linebuf_init_line(self->linebuf, self->cursor->y);
left_shift_line(self->linebuf->line, x, num);
line_apply_cursor(self->linebuf->line, self->cursor, self->columns - num, num, true);
tracker_update_cell_range(self->change_tracker, self->cursor->y, x, self->columns - 1);
}
}
void screen_erase_characters(Screen *self, unsigned int count) {
if (count == 0) count = 1;
unsigned int x = self->cursor->x;
unsigned int num = MIN(self->columns - x, count);
linebuf_init_line(self->linebuf, self->cursor->y);
line_apply_cursor(self->linebuf->line, self->cursor, x, num, true);
tracker_update_cell_range(self->change_tracker, self->cursor->y, x, MIN(x + num, self->columns) - 1);
}
// }}} // }}}
// Python interface {{{ // Python interface {{{
@ -590,6 +646,40 @@ cursor_back(Screen *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject*
erase_in_line(Screen *self, PyObject *args) {
#define erase_in_line_doc ""
bool private = false;
unsigned int how = 0;
if (!PyArg_ParseTuple(args, "|Ip", &how, &private)) return NULL;
screen_erase_in_line(self, how, private);
Py_RETURN_NONE;
}
static PyObject*
erase_in_display(Screen *self, PyObject *args) {
#define erase_in_display_doc ""
bool private = false;
unsigned int how = 0;
if (!PyArg_ParseTuple(args, "|Ip", &how, &private)) return NULL;
screen_erase_in_display(self, how, private);
Py_RETURN_NONE;
}
#define COUNT_WRAP(name) \
static PyObject* name(Screen *self, PyObject *args) { \
unsigned int count = 1; \
if (!PyArg_ParseTuple(args, "|I", &count)) return NULL; \
screen_##name(self, count); \
Py_RETURN_NONE; }
COUNT_WRAP(insert_lines)
COUNT_WRAP(delete_lines)
COUNT_WRAP(insert_characters)
COUNT_WRAP(delete_characters)
COUNT_WRAP(erase_characters)
#define MND(name, args) {#name, (PyCFunction)name, args, ""},
static PyMethodDef methods[] = { static PyMethodDef methods[] = {
METHOD(line, METH_O) METHOD(line, METH_O)
METHOD(draw, METH_VARARGS) METHOD(draw, METH_VARARGS)
@ -601,6 +691,13 @@ static PyMethodDef methods[] = {
METHOD(reset_dirty, METH_NOARGS) METHOD(reset_dirty, METH_NOARGS)
METHOD(consolidate_changes, METH_NOARGS) METHOD(consolidate_changes, METH_NOARGS)
METHOD(cursor_back, METH_VARARGS) METHOD(cursor_back, METH_VARARGS)
METHOD(erase_in_line, METH_VARARGS)
METHOD(erase_in_display, METH_VARARGS)
MND(insert_lines, METH_VARARGS)
MND(delete_lines, METH_VARARGS)
MND(insert_characters, METH_VARARGS)
MND(delete_characters, METH_VARARGS)
MND(erase_characters, METH_VARARGS)
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };

View File

@ -4,7 +4,7 @@
from . import BaseTest from . import BaseTest
from kitty.fast_data_types import DECAWM, IRM from kitty.fast_data_types import DECAWM, IRM, Cursor
class TestScreen(BaseTest): class TestScreen(BaseTest):
@ -55,7 +55,7 @@ class TestScreen(BaseTest):
def test_draw_char(self): def test_draw_char(self):
# Test in line-wrap, non-insert mode # Test in line-wrap, non-insert mode
s = self.create_screen() s = self.create_screen2()
s.draw('ココx'.encode('utf-8')) s.draw('ココx'.encode('utf-8'))
self.ae(str(s.line(0)), 'ココx') self.ae(str(s.line(0)), 'ココx')
self.ae(tuple(map(s.line(0).width, range(5))), (2, 0, 2, 0, 1)) self.ae(tuple(map(s.line(0).width, range(5))), (2, 0, 2, 0, 1))
@ -103,7 +103,7 @@ class TestScreen(BaseTest):
self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)}) self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)})
def test_char_manipulation(self): def test_char_manipulation(self):
s = self.create_screen() s = self.create_screen2()
def init(): def init():
s.reset(), s.reset_dirty() s.reset(), s.reset_dirty()
@ -128,6 +128,10 @@ class TestScreen(BaseTest):
s.insert_characters(1) s.insert_characters(1)
self.ae(str(s.line(0)), ' xコ ') self.ae(str(s.line(0)), ' xコ ')
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
c = Cursor()
c.bold = True
s.line(0).apply_cursor(c, 0, 5)
self.ae(s.line(0).width(2), 2)
init() init()
s.delete_characters(2) s.delete_characters(2)
@ -160,7 +164,7 @@ class TestScreen(BaseTest):
self.ae(str(s.line(0)), ' ') self.ae(str(s.line(0)), ' ')
self.assertChanges(s, cells={0: ((0, 4),)}) self.assertChanges(s, cells={0: ((0, 4),)})
init() init()
s.erase_in_line(2, private=True) s.erase_in_line(2, True)
self.ae((False, False, False, False, False), tuple(map(lambda i: s.line(0).cursor_from(i).bold, range(5)))) self.ae((False, False, False, False, False), tuple(map(lambda i: s.line(0).cursor_from(i).bold, range(5))))
def test_erase_in_screen(self): def test_erase_in_screen(self):