diff --git a/kitty/data-types.h b/kitty/data-types.h index cfa4468c3..d50169996 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -59,6 +59,7 @@ typedef uint16_t sprite_index; #define UTF8_REJECT 1 #define UNDERCURL_CODE 6 #define DECORATION_FG_CODE 58 +#define CHAR_IS_BLANK(ch) ((ch & CHAR_MASK) == 32 || (ch & CHAR_MASK) == 0) #define CURSOR_BLOCK 1 #define CURSOR_BEAM 2 @@ -130,6 +131,7 @@ typedef struct { char_type ch; color_type fg, bg, decoration_fg; combining_type cc; + sprite_index sprite_x, sprite_y, sprite_z; } Cell; typedef struct { @@ -288,11 +290,16 @@ typedef struct { } ChildMonitor; PyTypeObject ChildMonitor_Type; +#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0; + #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)->cells[(at)].ch >> ATTRS_SHIFT) & WIDTH_MASK) != 1) (line)->cells[(at)].ch = (1 << ATTRS_SHIFT) | BLANK_CHAR; + if ((((line)->cells[(at)].ch >> ATTRS_SHIFT) & WIDTH_MASK) != 1) { \ + (line)->cells[(at)].ch = (1 << ATTRS_SHIFT) | BLANK_CHAR; \ + clear_sprite_position((line)->cells[(at)]); \ + } // Global functions @@ -324,6 +331,7 @@ Cursor* cursor_copy(Cursor*); void cursor_copy_to(Cursor *src, Cursor *dest); void cursor_reset_display_attrs(Cursor*); bool update_cell_range_data(ScreenModes *modes, Line *, unsigned int, unsigned int, unsigned int *); +void set_sprite_position(Cell *cell, Cell *previous_cell); PyObject* line_text_at(char_type, combining_type); void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch); diff --git a/kitty/line.c b/kitty/line.c index 90f256f2d..73c9b351a 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -219,11 +219,14 @@ width(Line *self, PyObject *val) { return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK)); } +#define set_sprite_position_at(x) set_sprite_position(self->cells + x, x == 0 ? NULL : self->cells + x - 1); + void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) { combining_type c = self->cells[x].cc; if (c & CC_MASK) self->cells[x].cc = (c & CC_MASK) | ( (ch & CC_MASK) << CC_SHIFT ); else self->cells[x].cc = ch & CC_MASK; + set_sprite_position_at(x); } static PyObject* @@ -273,6 +276,7 @@ set_text(Line* self, PyObject *args) { self->cells[i].bg = bg; self->cells[i].decoration_fg = dfg; self->cells[i].cc = 0; + set_sprite_position_at(i); } Py_RETURN_NONE; @@ -302,9 +306,16 @@ cursor_from(Line* self, PyObject *args) { void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch) { const char_type repl = ((char_type)ch & CHAR_MASK) | (1 << ATTRS_SHIFT); - for (index_type i = at; i < MIN(self->xnum, at + num); i++) { - self->cells[i].ch = (self->cells[i].ch & ATTRS_MASK_WITHOUT_WIDTH) | repl; - self->cells[i].cc = 0; +#define PREFIX \ + for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \ + self->cells[i].ch = (self->cells[i].ch & ATTRS_MASK_WITHOUT_WIDTH) | repl; \ + self->cells[i].cc = 0; + if (CHAR_IS_BLANK(ch)) { + PREFIX + clear_sprite_position(self->cells[i]); } + } else { + PREFIX + set_sprite_position_at(i)} } } @@ -329,9 +340,11 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, if (clear_char) { self->cells[i].ch = BLANK_CHAR | attrs; self->cells[i].cc = 0; + clear_sprite_position(self->cells[i]); } else { char_type w = ((self->cells[i].ch >> ATTRS_SHIFT) & WIDTH_MASK) << ATTRS_SHIFT; self->cells[i].ch = (self->cells[i].ch & CHAR_MASK) | attrs | w; + set_sprite_position_at(i); } self->cells[i].fg = fg; self->cells[i].bg = bg; self->cells[i].decoration_fg = dfg; @@ -355,7 +368,10 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) { } // Check if a wide character was split at the right edge char_type w = (self->cells[self->xnum - 1].ch >> ATTRS_SHIFT) & WIDTH_MASK; - if (w != 1) self->cells[self->xnum - 1].ch = (1 << ATTRS_SHIFT) | BLANK_CHAR; + if (w != 1) { + self->cells[self->xnum - 1].ch = (1 << ATTRS_SHIFT) | BLANK_CHAR; + clear_sprite_position(self->cells[self->xnum - 1]); + } } static PyObject* @@ -399,6 +415,8 @@ line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Curs } self->cells[at].ch = (ch & CHAR_MASK) | attrs; self->cells[at].cc = 0; + if (CHAR_IS_BLANK(ch)) { clear_sprite_position(self->cells[at]); } + else set_sprite_position_at(at); } static PyObject* diff --git a/kitty/lineops.h b/kitty/lineops.h index 60a96f4f8..b27f21ed3 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -8,12 +8,23 @@ #include "data-types.h" +static inline void +update_sprites_in_line(Cell *cells, index_type xnum) { + if (LIKELY(xnum > 0)) { + set_sprite_position(cells, NULL); + for (index_type i = 1; i < xnum; i++) { + set_sprite_position(cells + i, cells + i - 1); + } + } +} + static inline void set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum) { uint32_t mask = shift == DECORATION_SHIFT ? 3 : 1; uint32_t aval = (val & mask) << (ATTRS_SHIFT + shift); mask = ~(mask << (ATTRS_SHIFT + shift)); for (index_type i = 0; i < xnum; i++) cells[i].ch = (cells[i].ch & mask) | aval; + if (shift == BOLD_SHIFT || shift == ITALIC_SHIFT) update_sprites_in_line(cells, xnum); } static inline void @@ -28,6 +39,7 @@ copy_line(const Line *src, Line *dest) { static inline void clear_chars_in_line(Cell *cells, index_type xnum, char_type ch) { + // Clear only the char part of each cell, the rest must have been cleared by a memset or similar char_type c = (1 << ATTRS_SHIFT) | ch; for (index_type i = 0; i < xnum; i++) cells[i].ch = c; } diff --git a/kitty/sprites.c b/kitty/sprites.c index 127614023..ea1539f20 100644 --- a/kitty/sprites.c +++ b/kitty/sprites.c @@ -99,6 +99,21 @@ sprite_map_position_for(char_type ch, combining_type cc, bool is_second, int *er return s; } + +void +set_sprite_position(Cell *cell, Cell *previous_cell) { + SpritePosition *sp; + static int error; + if (UNLIKELY(previous_cell != NULL && ((previous_cell->ch >> ATTRS_SHIFT) & WIDTH_MASK) == 2)) { + sp = sprite_map_position_for(previous_cell->ch, 0, true, &error); + } else { + sp = sprite_map_position_for(cell->ch, cell->cc, false, &error); + } + cell->sprite_x = sp->x; + cell->sprite_y = sp->y; + cell->sprite_z = sp->z; +} + PyObject* sprite_map_increment() { #define increment_doc "Increment the current position and return the old (x, y, z) values"