diff --git a/kitty/data-types.c b/kitty/data-types.c index fbbd1e62e..0e9cbd4c6 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -37,6 +37,11 @@ PyInit_fast_data_types(void) { if (!init_Line(m)) return NULL; if (!init_Cursor(m)) return NULL; if (!add_module_gl_constants(m)) return NULL; + PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT); + PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT); + PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT); + PyModule_AddIntConstant(m, "STRIKETHROUGH", STRIKE_SHIFT); + PyModule_AddIntConstant(m, "DECORATION", DECORATION_SHIFT); } return m; diff --git a/kitty/data-types.h b/kitty/data-types.h index 49d3b6b90..f4d8698d8 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -44,6 +44,12 @@ typedef unsigned int index_type; c->decoration = (a >> DECORATION_SHIFT) & 3; c->bold = (a >> BOLD_SHIFT) & 1; c->italic = (a >> ITALIC_SHIFT) & 1; \ c->reverse = (a >> REVERSE_SHIFT) & 1; c->strikethrough = (a >> STRIKE_SHIFT) & 1; +#define SET_ATTRIBUTE(chars, shift, val) \ + mask = shift == DECORATION_SHIFT ? 3 : 1; \ + val = (val & mask) << (ATTRS_SHIFT + shift); \ + mask = ~(mask << (ATTRS_SHIFT + shift)); \ + for (index_type i = 0; i < self->xnum; i++) (chars)[i] = ((chars)[i] & mask) | val; + #define COPY_CELL(src, s, dest, d) \ (dest)->chars[d] = (self)->chars[s]; \ (dest)->colors[d] = (self)->colors[s]; \ diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 8fb0aef8a..c7c64dd9b 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -101,6 +101,19 @@ line(LineBuf *self, PyObject *y) { return (PyObject*)self->line; } +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); + } + Py_RETURN_NONE; +} + // Boilerplate {{{ static PyObject* @@ -111,6 +124,7 @@ static PyMethodDef methods[] = { METHOD(line, METH_O) METHOD(copy_old, METH_O) METHOD(clear, METH_NOARGS) + METHOD(set_attribute, METH_VARARGS) {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/line.c b/kitty/line.c index dbc9b999e..37f023f6e 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -245,6 +245,17 @@ set_char(Line *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject* +set_attribute(Line *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; } + SET_ATTRIBUTE(self->chars, shift, val); + Py_RETURN_NONE; +} + static Py_ssize_t __len__(PyObject *self) { return (Py_ssize_t)(((Line*)self)->ynum); @@ -281,6 +292,7 @@ static PyMethodDef methods[] = { METHOD(right_shift, METH_VARARGS) METHOD(left_shift, METH_VARARGS) METHOD(set_char, METH_VARARGS) + METHOD(set_attribute, METH_VARARGS) {NULL} /* Sentinel */ }; diff --git a/kitty/screen.py b/kitty/screen.py index 1d45b774d..14c533992 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -12,7 +12,7 @@ from pyte import charsets as cs, graphics as g, modes as mo from .data_types import Line, Cursor, rewrap_lines from .utils import wcwidth, is_simple_string, sanitize_title from .unicode import ignore_pat -from .fast_data_types import LineBuf +from .fast_data_types import LineBuf, REVERSE #: A container for screen's scroll margins. @@ -79,7 +79,7 @@ class Screen: self.tophistorybuf.copy_old(previous) def line(self, i): - return self.linebuf[i] + return self.linebuf.line(i) def __repr__(self): return ("{0}({1}, {2})".format(self.__class__.__name__, @@ -121,7 +121,6 @@ class Screen: if self.linebuf is self.alt_linebuf: self.toggle_screen_buffer() self.linebuf.clear() - self.linebuf[:] = (Line(self.columns) for i in range(self.lines)) self.mode = {mo.DECAWM, mo.DECTCEM} self.margins = Margins(0, self.lines - 1) @@ -236,9 +235,7 @@ class Screen: # Mark all displayed characters as reverse. if mo.DECSCNM in modes: - for line in self.linebuf: - for i in range(len(line)): - line.set_reverse(i, True) + self.linebuf.set_attribute(REVERSE, True) self.update_screen() self.select_graphic_rendition(7) # +reverse. @@ -282,9 +279,7 @@ class Screen: self.cursor_position() if mo.DECSCNM in modes: - for line in self.linebuf: - for i in range(len(line)): - line.set_reverse(i, False) + self.linebuf.set_attribute(REVERSE, False) self.update_screen() self.select_graphic_rendition(27) # -reverse. diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 2af028314..e03002623 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -7,7 +7,7 @@ import codecs from . import BaseTest, filled_line_buf, filled_cursor from kitty.utils import is_simple_string, wcwidth, sanitize_title -from kitty.fast_data_types import LineBuf, Cursor as C +from kitty.fast_data_types import LineBuf, Cursor as C, REVERSE class TestDataTypes(BaseTest): @@ -19,6 +19,12 @@ class TestDataTypes(BaseTest): self.ae(new.line(0), old.line(1)) new.clear() self.ae(str(new.line(0)), ' ' * new.xnum) + old.set_attribute(REVERSE, False) + for y in range(old.ynum): + for x in range(old.xnum): + c = old.line(y).cursor_from(x) + self.assertFalse(c.reverse) + self.assertTrue(c.bold) def test_line(self): lb = LineBuf(2, 3)