diff --git a/kitty/core_text.m b/kitty/core_text.m index 2412d539a..9f931eb85 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) { } PyObject* -create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) { +create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) { CTFace *self = (CTFace*)base_face; CTFontRef new_font; if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL); diff --git a/kitty/cursor.c b/kitty/cursor.c index 15848a5b6..759ffd473 100644 --- a/kitty/cursor.c +++ b/kitty/cursor.c @@ -136,7 +136,7 @@ END_ALLOW_CASE_RANGE } void -apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) { +apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) { #define RANGE for(unsigned c = 0; c < cell_count; c++, cell++) #define SET(shift) RANGE { cell->attrs |= (1 << shift); } break; #define RESET(shift) RANGE { cell->attrs &= ~(1 << shift); } break; @@ -148,7 +148,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para unsigned int i = 0, attr; if (!count) { params[0] = 0; count = 1; } while (i < count) { - Cell *cell = first_cell; + GPUCell *cell = first_cell; attr = params[i++]; switch(attr) { case 0: diff --git a/kitty/data-types.h b/kitty/data-types.h index 07bfda4d8..236bcef24 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -80,7 +80,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; (c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1; (c)->dim = (a >> DIM_SHIFT) & 1; #define COPY_CELL(src, s, dest, d) \ - (dest)->cells[d] = (src)->cells[s]; + (dest)->cpu_cells[d] = (src)->cpu_cells[s]; (dest)->gpu_cells[d] = (src)->gpu_cells[s]; #define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d) @@ -140,15 +140,19 @@ typedef struct { color_type fg, bg, decoration_fg; sprite_index sprite_x, sprite_y, sprite_z; attrs_type attrs; - // The following are only needed on the CPU, not the GPU +} GPUCell; + +typedef struct { char_type ch; combining_type cc_idx[2]; -} Cell; +} CPUCell; + typedef struct { PyObject_HEAD - Cell *cells; + GPUCell *gpu_cells; + CPUCell *cpu_cells; index_type xnum, ynum; bool continued, needs_free, has_dirty_text; } Line; @@ -157,14 +161,16 @@ typedef struct { typedef struct { PyObject_HEAD - Cell *buf; + GPUCell *gpu_cell_buf; + CPUCell *cpu_cell_buf; index_type xnum, ynum, *line_map, *scratch; line_attrs_type *line_attrs; Line *line; } LineBuf; typedef struct { - Cell *cells; + GPUCell *gpu_cells; + CPUCell *cpu_cells; line_attrs_type *line_attrs; } HistoryBufSegment; @@ -226,10 +232,10 @@ typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE; for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ COPY_CELL(line, __i__ + (num), line, __i__) \ } \ - if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \ - (line)->cells[(at)].ch = BLANK_CHAR; \ - (line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ - clear_sprite_position((line)->cells[(at)]); \ + if ((((line)->gpu_cells[(at)].attrs) & WIDTH_MASK) != 1) { \ + (line)->cpu_cells[(at)].ch = BLANK_CHAR; \ + (line)->gpu_cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ + clear_sprite_position((line)->gpu_cells[(at)]); \ }\ } @@ -260,7 +266,7 @@ Cursor* cursor_copy(Cursor*); void cursor_copy_to(Cursor *src, Cursor *dest); void cursor_reset_display_attrs(Cursor*); void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count); -void apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count); +void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count); const char* cursor_as_sgr(Cursor*, Cursor*); double monotonic(); diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index d45bf4ae9..9123de462 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -194,7 +194,7 @@ end: } PyObject* -create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) { +create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) { PyObject *ans = NULL; FcPattern *pat = FcPatternCreate(); if (pat == NULL) return PyErr_NoMemory(); diff --git a/kitty/fonts.c b/kitty/fonts.c index 0338fbba7..e3fcd1eb9 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -420,12 +420,12 @@ face_has_codepoint(PyObject* face, char_type cp) { } static inline bool -has_emoji_presentation(Cell *cell) { - return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15; +has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) { + return (gpu_cell->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15; } static inline bool -has_cell_text(Font *self, Cell *cell) { +has_cell_text(Font *self, CPUCell *cell) { if (!face_has_codepoint(self->face, cell->ch)) return false; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { combining_type cc_idx = cell->cc_idx[i]; @@ -436,7 +436,7 @@ has_cell_text(Font *self, Cell *cell) { } static inline void -output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) { +output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) { printf("U+%x ", cell->ch); for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { printf("U+%x ", codepoint_for_mark(cell->cc_idx[i])); @@ -450,7 +450,7 @@ output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_present } static inline ssize_t -load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji_presentation) { +load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool emoji_presentation) { if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; } ssize_t f; @@ -475,21 +475,21 @@ load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji } static inline ssize_t -fallback_font(FontGroup *fg, Cell *cell) { - bool bold = (cell->attrs >> BOLD_SHIFT) & 1; - bool italic = (cell->attrs >> ITALIC_SHIFT) & 1; - bool emoji_presentation = has_emoji_presentation(cell); +fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { + bool bold = (gpu_cell->attrs >> BOLD_SHIFT) & 1; + bool italic = (gpu_cell->attrs >> ITALIC_SHIFT) & 1; + bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell); // Check if one of the existing fallback fonts has this text for (size_t i = 0, j = fg->first_fallback_font_idx; i < fg->fallback_fonts_count; i++, j++) { Font *ff = fg->fonts +j; - if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) { - if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, ff->face, false); + if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cpu_cell)) { + if (global_state.debug_font_fallback) output_cell_fallback_data(cpu_cell, bold, italic, emoji_presentation, ff->face, false); return j; } } - return load_fallback_font(fg, cell, bold, italic, emoji_presentation); + return load_fallback_font(fg, cpu_cell, bold, italic, emoji_presentation); } static inline ssize_t @@ -502,10 +502,10 @@ in_symbol_maps(FontGroup *fg, char_type ch) { static ssize_t -font_for_cell(FontGroup *fg, Cell *cell) { +font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { START_ALLOW_CASE_RANGE ssize_t ans; - switch(cell->ch) { + switch(cpu_cell->ch) { case 0: case ' ': return BLANK_FONT; @@ -517,9 +517,9 @@ START_ALLOW_CASE_RANGE case 0xe0b6: return BOX_FONT; default: - ans = in_symbol_maps(fg, cell->ch); + ans = in_symbol_maps(fg, cpu_cell->ch); if (ans > -1) return ans; - switch(BI_VAL(cell->attrs)) { + switch(BI_VAL(gpu_cell->attrs)) { case 0: ans = fg->medium_font_idx; break; case 1: @@ -530,14 +530,14 @@ START_ALLOW_CASE_RANGE ans = fg->bi_font_idx; break; } if (ans < 0) ans = fg->medium_font_idx; - if (!has_emoji_presentation(cell) && has_cell_text(fg->fonts + ans, cell)) return ans; - return fallback_font(fg, cell); + if (!has_emoji_presentation(cpu_cell, gpu_cell) && has_cell_text(fg->fonts + ans, cpu_cell)) return ans; + return fallback_font(fg, cpu_cell, gpu_cell); } END_ALLOW_CASE_RANGE } static inline void -set_sprite(Cell *cell, sprite_index x, sprite_index y, sprite_index z) { +set_sprite(GPUCell *cell, sprite_index x, sprite_index y, sprite_index z) { cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z; } @@ -577,21 +577,21 @@ render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *de } static void -render_box_cell(FontGroup *fg, Cell *cell) { +render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { int error = 0; - glyph_index glyph = box_glyph_id(cell->ch); + glyph_index glyph = box_glyph_id(cpu_cell->ch); static ExtraGlyphs extra_glyphs = {{0}}; SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], glyph, &extra_glyphs, false, &error); if (sp == NULL) { sprite_map_set_error(error); PyErr_Print(); - set_sprite(cell, 0, 0, 0); + set_sprite(gpu_cell, 0, 0, 0); return; } - set_sprite(cell, sp->x, sp->y, sp->z); + set_sprite(gpu_cell, sp->x, sp->y, sp->z); if (sp->rendered) return; sp->rendered = true; sp->colored = false; - PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0); + PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cpu_cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0); if (ret == NULL) { PyErr_Print(); return; } uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)); clear_canvas(fg); @@ -602,17 +602,17 @@ render_box_cell(FontGroup *fg, Cell *cell) { } static inline void -load_hb_buffer(Cell *first_cell, index_type num_cells) { +load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells) { index_type num; hb_buffer_clear_contents(harfbuzz_buffer); while (num_cells) { attrs_type prev_width = 0; - for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc_idx); first_cell++, num_cells--) { + for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cpu_cell->cc_idx); first_cpu_cell++, first_gpu_cell++, num_cells--) { if (prev_width == 2) { prev_width = 0; continue; } - shape_buffer[num++] = first_cell->ch; - prev_width = first_cell->attrs & WIDTH_MASK; - for (unsigned i = 0; i < arraysz(first_cell->cc_idx) && first_cell->cc_idx[i]; i++) { - shape_buffer[num++] = codepoint_for_mark(first_cell->cc_idx[i]); + shape_buffer[num++] = first_cpu_cell->ch; + prev_width = first_gpu_cell->attrs & WIDTH_MASK; + for (unsigned i = 0; i < arraysz(first_cpu_cell->cc_idx) && first_cpu_cell->cc_idx[i]; i++) { + shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]); } } hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num); @@ -622,7 +622,7 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) { static inline void -set_cell_sprite(Cell *cell, SpritePosition *sp) { +set_cell_sprite(GPUCell *cell, SpritePosition *sp) { cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z; if (sp->colored) cell->sprite_z |= 0x4000; } @@ -636,7 +636,7 @@ extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) } static inline void -render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) { +render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) { static SpritePosition* sprite_position[16]; int error = 0; num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells); @@ -645,19 +645,19 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; } } if (sprite_position[0]->rendered) { - for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(cells + i, sprite_position[i]); } + for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(gpu_cells + i, sprite_position[i]); } return; } clear_canvas(fg); - bool was_colored = (cells->attrs & WIDTH_MASK) == 2 && is_emoji(cells->ch); + bool was_colored = (gpu_cells->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cells->ch); render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg); if (PyErr_Occurred()) PyErr_Print(); for (unsigned int i = 0; i < num_cells; i++) { sprite_position[i]->rendered = true; sprite_position[i]->colored = was_colored; - set_cell_sprite(cells + i, sprite_position[i]); + set_cell_sprite(gpu_cells + i, sprite_position[i]); pixel *buf = num_cells == 1 ? fg->canvas : extract_cell_from_canvas(fg, i, num_cells); current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf); } @@ -665,7 +665,8 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel } typedef struct { - Cell *cell; + CPUCell *cpu_cell; + GPUCell *gpu_cell; unsigned int num_codepoints; unsigned int codepoints_consumed; char_type current_codepoint; @@ -681,7 +682,8 @@ typedef struct { CellData current_cell_data; Group *groups; size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs; - Cell *first_cell, *last_cell; + CPUCell *first_cpu_cell, *last_cpu_cell; + GPUCell *first_gpu_cell, *last_gpu_cell; hb_glyph_info_t *info; hb_glyph_position_t *positions; } GroupState; @@ -689,14 +691,14 @@ typedef struct { static GroupState group_state = {0}; static inline unsigned int -num_codepoints_in_cell(Cell *cell) { +num_codepoints_in_cell(CPUCell *cell) { unsigned int ans = 1; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++; return ans; } static inline void -shape(Cell *first_cell, index_type num_cells, hb_font_t *font) { +shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font) { if (group_state.groups_capacity <= 2 * num_cells) { group_state.groups_capacity = MAX(128, 2 * num_cells); // avoid unnecessary reallocs group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity); @@ -705,15 +707,21 @@ shape(Cell *first_cell, index_type num_cells, hb_font_t *font) { group_state.previous_cluster = UINT32_MAX; group_state.prev_was_special = false; group_state.prev_was_empty = false; - group_state.current_cell_data.cell = first_cell; group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cell); group_state.current_cell_data.codepoints_consumed = 0; group_state.current_cell_data.current_codepoint = first_cell->ch; + group_state.current_cell_data.cpu_cell = first_cpu_cell; + group_state.current_cell_data.gpu_cell = first_gpu_cell; + group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cpu_cell); + group_state.current_cell_data.codepoints_consumed = 0; + group_state.current_cell_data.current_codepoint = first_cpu_cell->ch; memset(group_state.groups, 0, sizeof(Group) * group_state.groups_capacity); group_state.group_idx = 0; group_state.glyph_idx = 0; group_state.cell_idx = 0; group_state.num_cells = num_cells; - group_state.first_cell = first_cell; - group_state.last_cell = first_cell + (num_cells ? num_cells - 1 : 0); - load_hb_buffer(first_cell, num_cells); + group_state.first_cpu_cell = first_cpu_cell; + group_state.first_gpu_cell = first_gpu_cell; + group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0); + group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0); + load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells); hb_shape(font, harfbuzz_buffer, NULL, 0); unsigned int info_length, positions_length; group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length); @@ -752,24 +760,25 @@ is_empty_glyph(glyph_index glyph_id, Font *font) { } static inline unsigned int -check_cell_consumed(CellData *cell_data, Cell *last_cell) { +check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) { cell_data->codepoints_consumed++; if (cell_data->codepoints_consumed >= cell_data->num_codepoints) { - attrs_type width = cell_data->cell->attrs & WIDTH_MASK; - cell_data->cell += MAX(1, width); + attrs_type width = cell_data->gpu_cell->attrs & WIDTH_MASK; + cell_data->cpu_cell += MAX(1, width); + cell_data->gpu_cell += MAX(1, width); cell_data->codepoints_consumed = 0; - if (cell_data->cell <= last_cell) { - cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell); - cell_data->current_codepoint = cell_data->cell->ch; + if (cell_data->cpu_cell <= last_cpu_cell) { + cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell); + cell_data->current_codepoint = cell_data->cpu_cell->ch; } else cell_data->current_codepoint = 0; return width; } else { switch(cell_data->codepoints_consumed) { case 0: - cell_data->current_codepoint = cell_data->cell->ch; + cell_data->current_codepoint = cell_data->cpu_cell->ch; break; default: - cell_data->current_codepoint = codepoint_for_mark(cell_data->cell->cc_idx[cell_data->codepoints_consumed - 1]); + cell_data->current_codepoint = codepoint_for_mark(cell_data->cpu_cell->cc_idx[cell_data->codepoints_consumed - 1]); break; } } @@ -778,8 +787,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) { static inline void -shape_run(Cell *first_cell, index_type num_cells, Font *font) { - shape(first_cell, num_cells, harfbuzz_font_for_face(font->face)); +shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font) { + shape(first_cpu_cell, first_gpu_cell, num_cells, harfbuzz_font_for_face(font->face)); #if 0 // You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, (char*)canvas, sizeof(pixel) * CELLS_IN_CANVAS * cell_width * cell_height, NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS); @@ -853,7 +862,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) { } else { unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx); while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) { - unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cell)); + unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell)); G(cell_idx) += w; num_cells_consumed += w; num_codepoints_used_by_glyph--; @@ -915,7 +924,7 @@ render_groups(FontGroup *fg, Font *font) { int last = -1; for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; } if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0; - render_group(fg, group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed); + render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed); idx++; } } @@ -927,7 +936,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) { int index = 0; if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL; index_type num = 0; - while(num < line->xnum && line->cells[num].ch) num += line->cells[num].attrs & WIDTH_MASK; + while(num < line->xnum && line->cpu_cells[num].ch) num += line->gpu_cells[num].attrs & WIDTH_MASK; PyObject *face = NULL; Font *font; if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; } @@ -940,7 +949,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) { FontGroup *fg = font_groups; font = fg->fonts + fg->medium_font_idx; } - shape_run(line->cells, num, font); + shape_run(line->cpu_cells, line->gpu_cells, num, font); PyObject *ans = PyList_New(0); unsigned int idx = 0; @@ -961,21 +970,21 @@ test_shape(PyObject UNUSED *self, PyObject *args) { #undef G static inline void -render_run(FontGroup *fg, Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) { +render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) { switch(font_idx) { default: - shape_run(first_cell, num_cells, &fg->fonts[font_idx]); + shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx]); if (pua_space_ligature) merge_groups_for_pua_space_ligature(); render_groups(fg, &fg->fonts[font_idx]); break; case BLANK_FONT: - while(num_cells--) set_sprite(first_cell++, 0, 0, 0); + while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; } break; case BOX_FONT: - while(num_cells--) render_box_cell(fg, first_cell++); + while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell); first_cpu_cell++; first_gpu_cell++; } break; case MISSING_FONT: - while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0); + while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; } break; } } @@ -987,32 +996,33 @@ is_private_use(char_type ch) { void render_line(FONTS_DATA_HANDLE fg_, Line *line) { -#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false); +#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false); FontGroup *fg = (FontGroup*)fg_; ssize_t run_font_idx = NO_FONT; index_type first_cell_in_run, i; attrs_type prev_width = 0; for (i=0, first_cell_in_run=0; i < line->xnum; i++) { if (prev_width == 2) { prev_width = 0; continue; } - Cell *cell = line->cells + i; - ssize_t cell_font_idx = font_for_cell(fg, cell); - if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) { + CPUCell *cpu_cell = line->cpu_cells + i; + GPUCell *gpu_cell = line->gpu_cells + i; + ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell); + if (is_private_use(cpu_cell->ch) && i + 1 < line->xnum && (line->cpu_cells[i+1].ch == ' ' || line->cpu_cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) { // We have a private use char followed by a space char, render it as a two cell ligature. - Cell *space_cell = line->cells + i+1; + GPUCell *space_cell = line->gpu_cells + i+1; // Ensure the space cell uses the foreground colors from the PUA cell // This is needed because there are stupid applications like // powerline that use PUA+space with different foreground colors // for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467 - space_cell->fg = cell->fg; space_cell->decoration_fg = cell->decoration_fg; + space_cell->fg = gpu_cell->fg; space_cell->decoration_fg = gpu_cell->decoration_fg; RENDER; - render_run(fg, line->cells + i, 2, cell_font_idx, true); + render_run(fg, line->cpu_cells + i, line->gpu_cells + i, 2, cell_font_idx, true); run_font_idx = NO_FONT; first_cell_in_run = i + 2; - prev_width = line->cells[i+1].attrs & WIDTH_MASK; + prev_width = line->gpu_cells[i+1].attrs & WIDTH_MASK; i++; continue; } - prev_width = cell->attrs & WIDTH_MASK; + prev_width = gpu_cell->attrs & WIDTH_MASK; if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx; if (run_font_idx == cell_font_idx) continue; RENDER; @@ -1259,15 +1269,16 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) { PyObject *text; int bold, italic; if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL; - Cell cell = {0}; - static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)]; + CPUCell cpu_cell = {0}; + GPUCell gpu_cell = {0}; + static Py_UCS4 char_buf[2 + arraysz(cpu_cell.cc_idx)]; if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL; - cell.ch = char_buf[0]; - for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]); - if (bold) cell.attrs |= 1 << BOLD_SHIFT; - if (italic) cell.attrs |= 1 << ITALIC_SHIFT; + cpu_cell.ch = char_buf[0]; + for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cpu_cell.cc_idx); i++) cpu_cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]); + if (bold) gpu_cell.attrs |= 1 << BOLD_SHIFT; + if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT; FontGroup *fg = font_groups; - ssize_t ans = fallback_font(fg, &cell); + ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell); if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; } return fg->fonts[ans].face; } diff --git a/kitty/fonts.h b/kitty/fonts.h index 380de0957..0e5a7aacb 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -22,7 +22,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*); bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE); void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE); -PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); +PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE); PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE); PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE); diff --git a/kitty/history.c b/kitty/history.c index 7a43eda57..9a77da866 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -18,9 +18,10 @@ add_segment(HistoryBuf *self) { self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments); if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment"); HistoryBufSegment *s = self->segments + self->num_segments - 1; - s->cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(Cell)); + s->cpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(CPUCell)); + s->gpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(GPUCell)); s->line_attrs = PyMem_Calloc(SEGMENT_SIZE, sizeof(line_attrs_type)); - if (s->cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment"); + if (s->cpu_cells == NULL || s->gpu_cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment"); } static inline index_type @@ -37,9 +38,14 @@ segment_for(HistoryBuf *self, index_type y) { return self->segments[seg_num].which + y * stride; \ } -static inline Cell* -lineptr(HistoryBuf *self, index_type y) { - seg_ptr(cells, self->xnum); +static inline CPUCell* +cpu_lineptr(HistoryBuf *self, index_type y) { + seg_ptr(cpu_cells, self->xnum); +} + +static inline GPUCell* +gpu_lineptr(HistoryBuf *self, index_type y) { + seg_ptr(gpu_cells, self->xnum); } @@ -64,12 +70,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (self != NULL) { self->xnum = xnum; self->ynum = ynum; - self->line = alloc_line(); self->num_segments = 0; add_segment(self); + self->line = alloc_line(); self->line->xnum = xnum; for(index_type y = 0; y < self->ynum; y++) { - clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR); + clear_chars_in_line(cpu_lineptr(self, y), gpu_lineptr(self, y), self->xnum, BLANK_CHAR); } } @@ -80,7 +86,8 @@ static void dealloc(HistoryBuf* self) { Py_CLEAR(self->line); for (size_t i = 0; i < self->num_segments; i++) { - PyMem_Free(self->segments[i].cells); + PyMem_Free(self->segments[i].cpu_cells); + PyMem_Free(self->segments[i].gpu_cells); PyMem_Free(self->segments[i].line_attrs); } PyMem_Free(self->segments); @@ -99,7 +106,8 @@ index_of(HistoryBuf *self, index_type lnum) { static inline void init_line(HistoryBuf *self, index_type num, Line *l) { // Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num - l->cells = lineptr(self, num); + l->cpu_cells = cpu_lineptr(self, num); + l->gpu_cells = gpu_lineptr(self, num); l->continued = *attrptr(self, num) & CONTINUED_MASK; l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false; } @@ -286,7 +294,8 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) { if (other->xnum == self->xnum && other->ynum == self->ynum) { // Fast path for (index_type i = 0; i < self->num_segments; i++) { - memcpy(other->segments[i].cells, self->segments[i].cells, SEGMENT_SIZE * self->xnum * sizeof(Cell)); + memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell)); + memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell)); memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(line_attrs_type)); } other->count = self->count; other->start_of_data = self->start_of_data; diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 2b298faa2..0e8179639 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -12,19 +12,25 @@ extern PyTypeObject Line_Type; extern PyTypeObject HistoryBuf_Type; -static inline Cell* -lineptr(LineBuf *linebuf, index_type y) { - return linebuf->buf + y * linebuf->xnum; +static inline CPUCell* +cpu_lineptr(LineBuf *linebuf, index_type y) { + return linebuf->cpu_cell_buf + y * linebuf->xnum; +} + +static inline GPUCell* +gpu_lineptr(LineBuf *linebuf, index_type y) { + return linebuf->gpu_cell_buf + y * linebuf->xnum; } static inline void clear_chars_to(LineBuf* linebuf, index_type y, char_type ch) { - clear_chars_in_line(lineptr(linebuf, y), linebuf->xnum, ch); + clear_chars_in_line(cpu_lineptr(linebuf, y), gpu_lineptr(linebuf, y), linebuf->xnum, ch); } void linebuf_clear(LineBuf *self, char_type ch) { - memset(self->buf, 0, self->xnum * self->ynum * sizeof(Cell)); + memset(self->cpu_cell_buf, 0, self->xnum * self->ynum * sizeof(CPUCell)); + memset(self->gpu_cell_buf, 0, self->xnum * self->ynum * sizeof(GPUCell)); memset(self->line_attrs, 0, self->ynum * sizeof(line_attrs_type)); for (index_type i = 0; i < self->ynum; i++) self->line_map[i] = i; if (ch != 0) { @@ -73,14 +79,15 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (self != NULL) { self->xnum = xnum; self->ynum = ynum; - self->buf = PyMem_Calloc(xnum * ynum, sizeof(Cell)); + self->cpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(CPUCell)); + self->gpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(GPUCell)); self->line_map = PyMem_Calloc(ynum, sizeof(index_type)); self->scratch = PyMem_Calloc(ynum, sizeof(index_type)); self->line_attrs = PyMem_Calloc(ynum, sizeof(line_attrs_type)); self->line = alloc_line(); - if (self->buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) { + if (self->cpu_cell_buf == NULL || self->gpu_cell_buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) { PyErr_NoMemory(); - PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line); + PyMem_Free(self->cpu_cell_buf); PyMem_Free(self->gpu_cell_buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line); Py_CLEAR(self); } else { self->line->xnum = xnum; @@ -96,7 +103,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { static void dealloc(LineBuf* self) { - PyMem_Free(self->buf); + PyMem_Free(self->cpu_cell_buf); + PyMem_Free(self->gpu_cell_buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); PyMem_Free(self->scratch); @@ -106,7 +114,8 @@ dealloc(LineBuf* self) { static inline void init_line(LineBuf *lb, Line *l, index_type ynum) { - l->cells = lineptr(lb, ynum); + l->cpu_cells = cpu_lineptr(lb, ynum); + l->gpu_cells = gpu_lineptr(lb, ynum); } void @@ -133,13 +142,13 @@ line(LineBuf *self, PyObject *y) { unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y) { - return (lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK; + return (gpu_lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK; } void linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) { for (index_type y = 0; y < self->ynum; y++) { - set_attribute_on_line(lineptr(self, y), shift, val, self->xnum); + set_attribute_on_line(gpu_lineptr(self, y), shift, val, self->xnum); self->line_attrs[y] |= TEXT_DIRTY_MASK; } } @@ -181,12 +190,14 @@ dirty_lines(LineBuf *self, PyObject *a UNUSED) { static inline bool allocate_line_storage(Line *line, bool initialize) { if (initialize) { - line->cells = PyMem_Calloc(line->xnum, sizeof(Cell)); - if (line->cells == NULL) { PyErr_NoMemory(); return false; } - if (BLANK_CHAR != 0) clear_chars_in_line(line->cells, line->xnum, BLANK_CHAR); + line->cpu_cells = PyMem_Calloc(line->xnum, sizeof(CPUCell)); + line->gpu_cells = PyMem_Calloc(line->xnum, sizeof(GPUCell)); + if (line->cpu_cells == NULL || line->gpu_cells) { PyErr_NoMemory(); return false; } + if (BLANK_CHAR != 0) clear_chars_in_line(line->cpu_cells, line->gpu_cells, line->xnum, BLANK_CHAR); } else { - line->cells = PyMem_Malloc(line->xnum * sizeof(Cell)); - if (line->cells == NULL) { PyErr_NoMemory(); return false; } + line->cpu_cells = PyMem_Malloc(line->xnum * sizeof(CPUCell)); + line->gpu_cells = PyMem_Malloc(line->xnum * sizeof(GPUCell)); + if (line->cpu_cells == NULL || line->gpu_cells == NULL) { PyErr_NoMemory(); return false; } } line->needs_free = 1; return true; @@ -232,8 +243,9 @@ copy_line_to(LineBuf *self, PyObject *args) { static inline void clear_line_(Line *l, index_type xnum) { - memset(l->cells, 0, xnum * sizeof(Cell)); - if (BLANK_CHAR != 0) clear_chars_in_line(l->cells, xnum, BLANK_CHAR); + memset(l->cpu_cells, 0, xnum * sizeof(CPUCell)); + memset(l->gpu_cells, 0, xnum * sizeof(GPUCell)); + if (BLANK_CHAR != 0) clear_chars_in_line(l->cpu_cells, l->gpu_cells, xnum, BLANK_CHAR); l->has_dirty_text = false; } @@ -515,7 +527,8 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo if (other->xnum == self->xnum && other->ynum == self->ynum) { memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum); memcpy(other->line_attrs, self->line_attrs, sizeof(bool) * self->ynum); - memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell)); + memcpy(other->cpu_cell_buf, self->cpu_cell_buf, self->xnum * self->ynum * sizeof(CPUCell)); + memcpy(other->gpu_cell_buf, self->gpu_cell_buf, self->xnum * self->ynum * sizeof(GPUCell)); *num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum; return; } @@ -524,7 +537,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo first = self->ynum; do { first--; - Cell *cells = lineptr(self, self->line_map[first]); + CPUCell *cells = cpu_lineptr(self, self->line_map[first]); for(i = 0; i < self->xnum; i++) { if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; } } diff --git a/kitty/line.c b/kitty/line.c index 0af7ac390..b545be181 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -20,7 +20,8 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { static void dealloc(Line* self) { if (self->needs_free) { - PyMem_Free(self->cells); + PyMem_Free(self->cpu_cells); + PyMem_Free(self->gpu_cells); } Py_TYPE(self)->tp_free((PyObject*)self); } @@ -29,13 +30,13 @@ unsigned int line_length(Line *self) { index_type last = self->xnum - 1; for (index_type i = 0; i < self->xnum; i++) { - if ((self->cells[last - i].ch) != BLANK_CHAR) return self->xnum - i; + if ((self->cpu_cells[last - i].ch) != BLANK_CHAR) return self->xnum - i; } return 0; } PyObject* -cell_text(Cell *cell) { +cell_text(CPUCell *cell) { PyObject *ans; unsigned num = 1; static Py_UCS4 buf[arraysz(cell->cc_idx) + 1]; @@ -59,7 +60,7 @@ find_colon_slash(Line *self, index_type x, index_type limit) { limit = MAX(2, limit); if (pos < limit) return 0; do { - char_type ch = self->cells[pos].ch; + char_type ch = self->cpu_cells[pos].ch; if (!is_url_char(ch)) return false; switch(state) { case ANY: @@ -83,7 +84,7 @@ prefix_matches(Line *self, index_type at, const char* prefix, index_type prefix_ if (prefix_len > at) return false; index_type p, i; for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) { - if ((self->cells[p].ch) != (unsigned char)prefix[i]) return false; + if ((self->cpu_cells[p].ch) != (unsigned char)prefix[i]) return false; } return i == prefix_len; } @@ -108,7 +109,7 @@ static inline bool has_url_beyond(Line *self, index_type x) { if (self->xnum <= x + MIN_URL_LEN + 3) return false; for (index_type i = x; i < MIN(x + MIN_URL_LEN + 3, self->xnum); i++) { - if (!is_url_char(self->cells[i].ch)) return false; + if (!is_url_char(self->cpu_cells[i].ch)) return false; } return true; } @@ -134,9 +135,9 @@ index_type line_url_end_at(Line *self, index_type x, bool check_short) { index_type ans = x; if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0; - while (ans < self->xnum && is_url_char(self->cells[ans].ch)) ans++; + while (ans < self->xnum && is_url_char(self->cpu_cells[ans].ch)) ans++; if (ans) ans--; - while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch)) ans--; + while (ans > x && can_strip_from_end_of_url(self->cpu_cells[ans].ch)) ans--; return ans; } @@ -158,11 +159,11 @@ static PyObject* text_at(Line* self, Py_ssize_t xval) { #define text_at_doc "[x] -> Return the text in the specified cell" if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } - return cell_text(self->cells + xval); + return cell_text(self->cpu_cells + xval); } size_t -cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) { +cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) { size_t n = 1; buf[0] = cell->ch ? cell->ch : zero_char; if (include_cc) { @@ -172,7 +173,7 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) } size_t -cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type zero_char) { +cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type zero_char) { size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf); if (include_cc) { for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) n += encode_utf8(codepoint_for_mark(cell->cc_idx[i]), buf + n); @@ -188,13 +189,13 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc static Py_UCS4 buf[4096]; if (leading_char) buf[n++] = leading_char; char_type previous_width = 0; - for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc_idx); i++) { - char_type ch = self->cells[i].ch; + for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cpu_cells->cc_idx); i++) { + char_type ch = self->cpu_cells[i].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; }; } - n += cell_as_unicode(self->cells + i, include_cc, buf + n, ' '); - previous_width = self->cells[i].attrs & WIDTH_MASK; + n += cell_as_unicode(self->cpu_cells + i, include_cc, buf + n, ' '); + previous_width = self->gpu_cells[i].attrs & WIDTH_MASK; } return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n); } @@ -209,7 +210,7 @@ sprite_at(Line* self, PyObject *x) { #define sprite_at_doc "[x] -> Return the sprite in the specified cell" unsigned long xval = PyLong_AsUnsignedLong(x); if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } - Cell *c = self->cells + xval; + GPUCell *c = self->gpu_cells + xval; return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z); } @@ -236,21 +237,21 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { Cursor *cursor = &c1, *prev_cursor = &c2, *t; for (index_type pos=0; pos < limit; pos++) { - char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch; + char_type attrs = self->gpu_cells[pos].attrs, ch = self->cpu_cells[pos].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; } ch = ' '; } ATTRS_TO_CURSOR(attrs, cursor); - cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg; - cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK; + cursor->fg = self->gpu_cells[pos].fg; cursor->bg = self->gpu_cells[pos].bg; + cursor->decoration_fg = self->gpu_cells[pos].decoration_fg & COL_MASK; const char *sgr = cursor_as_sgr(cursor, prev_cursor); t = prev_cursor; prev_cursor = cursor; cursor = t; if (*sgr) WRITE_SGR(sgr); WRITE_CH(ch); - for(unsigned c = 0; c < arraysz(self->cells[pos].cc_idx) && self->cells[pos].cc_idx[c]; c++) { - WRITE_CH(codepoint_for_mark(self->cells[pos].cc_idx[c])); + for(unsigned c = 0; c < arraysz(self->cpu_cells[pos].cc_idx) && self->cpu_cells[pos].cc_idx[c]; c++) { + WRITE_CH(codepoint_for_mark(self->cpu_cells[pos].cc_idx[c])); } previous_width = attrs & WIDTH_MASK; } @@ -293,13 +294,13 @@ width(Line *self, PyObject *val) { #define width_doc "width(x) -> the width of the character at x" unsigned long x = PyLong_AsUnsignedLong(val); if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } - char_type attrs = self->cells[x].attrs; + char_type attrs = self->gpu_cells[x].attrs; return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK)); } void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) { - Cell *cell = self->cells + x; + CPUCell *cell = self->cpu_cells + x; if (!cell->ch) return; // dont allow adding combining chars to a null cell for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) { if (!cell->cc_idx[i]) { cell->cc_idx[i] = mark_for_codepoint(ch); return; } @@ -349,12 +350,12 @@ set_text(Line* self, PyObject *args) { color_type dfg = cursor->decoration_fg & COL_MASK; for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) { - self->cells[i].ch = (PyUnicode_READ(kind, buf, offset)); - self->cells[i].attrs = attrs; - self->cells[i].fg = fg; - self->cells[i].bg = bg; - self->cells[i].decoration_fg = dfg; - memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); + self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset)); + self->gpu_cells[i].attrs = attrs; + self->gpu_cells[i].fg = fg; + self->gpu_cells[i].bg = bg; + self->gpu_cells[i].decoration_fg = dfg; + memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); } Py_RETURN_NONE; @@ -373,10 +374,10 @@ cursor_from(Line* self, PyObject *args) { ans = alloc_cursor(); if (ans == NULL) { PyErr_NoMemory(); return NULL; } ans->x = x; ans->y = y; - char_type attrs = self->cells[x].attrs; + char_type attrs = self->gpu_cells[x].attrs; ATTRS_TO_CURSOR(attrs, ans); - ans->fg = self->cells[x].fg; ans->bg = self->cells[x].bg; - ans->decoration_fg = self->cells[x].decoration_fg & COL_MASK; + ans->fg = self->gpu_cells[x].fg; ans->bg = self->gpu_cells[x].bg; + ans->decoration_fg = self->gpu_cells[x].decoration_fg & COL_MASK; return (PyObject*)ans; } @@ -386,8 +387,8 @@ line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) { attrs_type width = ch ? 1 : 0; #define PREFIX \ for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \ - self->cells[i].ch = ch; memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); \ - self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ + self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \ + self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ } if (CHAR_IS_BLANK(ch)) { PREFIX @@ -415,16 +416,16 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, for (index_type i = at; i < self->xnum && i < at + num; i++) { if (clear_char) { - self->cells[i].ch = BLANK_CHAR; - memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); - self->cells[i].attrs = attrs; - clear_sprite_position(self->cells[i]); + self->cpu_cells[i].ch = BLANK_CHAR; + memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); + self->gpu_cells[i].attrs = attrs; + clear_sprite_position(self->gpu_cells[i]); } else { - attrs_type w = self->cells[i].attrs & WIDTH_MASK; - self->cells[i].attrs = attrs | w; + attrs_type w = self->gpu_cells[i].attrs & WIDTH_MASK; + self->gpu_cells[i].attrs = attrs | w; } - self->cells[i].fg = fg; self->cells[i].bg = bg; - self->cells[i].decoration_fg = dfg; + self->gpu_cells[i].fg = fg; self->gpu_cells[i].bg = bg; + self->gpu_cells[i].decoration_fg = dfg; } } @@ -444,11 +445,11 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) { COPY_SELF_CELL(i - num, i) } // Check if a wide character was split at the right edge - char_type w = (self->cells[self->xnum - 1].attrs) & WIDTH_MASK; + char_type w = (self->gpu_cells[self->xnum - 1].attrs) & WIDTH_MASK; if (w != 1) { - self->cells[self->xnum - 1].ch = BLANK_CHAR; - self->cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; - clear_sprite_position(self->cells[self->xnum - 1]); + self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR; + self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; + clear_sprite_position(self->gpu_cells[self->xnum - 1]); } } @@ -483,15 +484,15 @@ left_shift(Line *self, PyObject *args) { void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) { if (cursor == NULL) { - self->cells[at].attrs = (self->cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; + self->gpu_cells[at].attrs = (self->gpu_cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; } else { - self->cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); - self->cells[at].fg = (cursor->fg & COL_MASK); - self->cells[at].bg = (cursor->bg & COL_MASK); - self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; + self->gpu_cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); + self->gpu_cells[at].fg = (cursor->fg & COL_MASK); + self->gpu_cells[at].bg = (cursor->bg & COL_MASK); + self->gpu_cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; } - self->cells[at].ch = ch; - memset(self->cells[at].cc_idx, 0, sizeof(self->cells[at].cc_idx)); + self->cpu_cells[at].ch = ch; + memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx)); } static PyObject* @@ -516,7 +517,7 @@ set_attribute(Line *self, PyObject *args) { unsigned int shift, val; if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL; if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; } - set_attribute_on_line(self->cells, shift, val, self->xnum); + set_attribute_on_line(self->gpu_cells, shift, val, self->xnum); Py_RETURN_NONE; } @@ -525,8 +526,9 @@ __len__(PyObject *self) { return (Py_ssize_t)(((Line*)self)->xnum); } -static int __eq__(Line *a, Line *b) { - return a->xnum == b->xnum && memcmp(a->cells, b->cells, sizeof(Cell) * a->xnum) == 0; +static int +__eq__(Line *a, Line *b) { + return a->xnum == b->xnum && memcmp(a->cpu_cells, b->cpu_cells, sizeof(CPUCell) * a->xnum) == 0 && memcmp(a->gpu_cells, b->gpu_cells, sizeof(GPUCell) * a->xnum) == 0; } // Boilerplate {{{ diff --git a/kitty/lineops.h b/kitty/lineops.h index b2a238a28..6592f81c7 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -9,7 +9,7 @@ #include "data-types.h" static inline void -set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum) { +set_attribute_on_line(GPUCell *cells, uint32_t shift, uint32_t val, index_type xnum) { // Set a single attribute on all cells in the line attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1; attrs_type aval = (val & mask) << shift; @@ -17,21 +17,18 @@ set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum for (index_type i = 0; i < xnum; i++) cells[i].attrs = (cells[i].attrs & mask) | aval; } -static inline void -copy_cells(const Cell *src, Cell *dest, index_type xnum) { - memcpy(dest, src, sizeof(Cell) * xnum); -} static inline void copy_line(const Line *src, Line *dest) { - copy_cells(src->cells, dest->cells, MIN(src->xnum, dest->xnum)); + memcpy(dest->cpu_cells, src->cpu_cells, sizeof(CPUCell) * MIN(src->xnum, dest->xnum)); + memcpy(dest->gpu_cells, src->gpu_cells, sizeof(GPUCell) * MIN(src->xnum, dest->xnum)); } static inline void -clear_chars_in_line(Cell *cells, index_type xnum, char_type ch) { +clear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_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 if (ch) { - for (index_type i = 0; i < xnum; i++) { cells[i].ch = ch; cells[i].attrs = 1; } + for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; gpu_cells[i].attrs = 1; } } } @@ -39,7 +36,7 @@ static inline index_type xlimit_for_line(Line *line) { index_type xlimit = line->xnum; if (BLANK_CHAR == 0) { - while (xlimit > 0 && (line->cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--; + while (xlimit > 0 && (line->cpu_cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--; } return xlimit; } @@ -54,8 +51,8 @@ index_type line_url_start_at(Line *self, index_type x); index_type line_url_end_at(Line *self, index_type x, bool); index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen); unsigned int line_length(Line *self); -size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type); -size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type); +size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type); +size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type); PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char); PyObject* line_as_unicode(Line *); diff --git a/kitty/rewrap.h b/kitty/rewrap.h index ce82543ea..e58c93d08 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -43,7 +43,8 @@ static inline void copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) { - memcpy(dest->cells + dest_at, src->cells + src_at, num * sizeof(Cell)); + memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell)); + memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell)); } @@ -60,7 +61,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf src_x_limit = src->xnum; if (!src_line_is_continued) { // Trim trailing blanks since there is a hard line break at the end of this line - while(src_x_limit && (src->line->cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--; + while(src_x_limit && (src->line->cpu_cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--; } if (is_tracked_line && *track_x >= src_x_limit) *track_x = MAX(1, src_x_limit) - 1; diff --git a/kitty/screen.c b/kitty/screen.c index 917290641..3aa2b747e 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -280,9 +280,10 @@ screen_designate_charset(Screen *self, uint32_t which, uint32_t as) { } static inline void -move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) { +move_widened_char(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) { self->cursor->x = xpos; self->cursor->y = ypos; - Cell src = *cell, *dest; + CPUCell src_cpu = *cpu_cell, *dest_cpu; + GPUCell src_gpu = *gpu_cell, *dest_gpu; line_clear_text(self->linebuf->line, xpos, 1, BLANK_CHAR); if (self->modes.mDECAWM) { // overflow goes onto next line @@ -290,14 +291,17 @@ move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) { screen_linefeed(self); self->linebuf->line_attrs[self->cursor->y] |= CONTINUED_MASK; linebuf_init_line(self->linebuf, self->cursor->y); - dest = self->linebuf->line->cells; + dest_cpu = self->linebuf->line->cpu_cells; + dest_gpu = self->linebuf->line->gpu_cells; self->cursor->x = MIN(2, self->columns); linebuf_mark_line_dirty(self->linebuf, self->cursor->y); } else { - dest = cell - 1; + dest_cpu = cpu_cell - 1; + dest_gpu = gpu_cell - 1; self->cursor->x = self->columns; } - *dest = src; + *dest_cpu = src_cpu; + *dest_gpu = src_gpu; } static inline void @@ -320,10 +324,11 @@ draw_combining_char(Screen *self, char_type ch) { self->is_dirty = true; linebuf_mark_line_dirty(self->linebuf, ypos); if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji - Cell *cell = self->linebuf->line->cells + xpos; - if ((cell->attrs & WIDTH_MASK) != 2 && cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cell->ch)) { - cell->attrs = (cell->attrs & !WIDTH_MASK) | 2; - if (xpos == self->columns - 1) move_widened_char(self, cell, xpos, ypos); + CPUCell *cpu_cell = self->linebuf->line->cpu_cells + xpos; + GPUCell *gpu_cell = self->linebuf->line->gpu_cells + xpos; + if ((gpu_cell->attrs & WIDTH_MASK) != 2 && cpu_cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cpu_cell->ch)) { + gpu_cell->attrs = (gpu_cell->attrs & !WIDTH_MASK) | 2; + if (xpos == self->columns - 1) move_widened_char(self, cpu_cell, gpu_cell, xpos, ypos); else self->cursor->x++; } } @@ -409,7 +414,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count, num = MIN(num, self->columns - x); for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) { linebuf_init_line(self->linebuf, y); - apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count); + apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count); } } else { index_type x, num; @@ -418,7 +423,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count, else if (y == region.bottom) { x = 0; num = MIN(region.right + 1, self->columns); } else { x = 0; num = self->columns; } linebuf_init_line(self->linebuf, y); - apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count); + apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count); } } } else cursor_from_sgr(self->cursor, params, count); @@ -1280,8 +1285,8 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) { // Rendering {{{ static inline void update_line_data(Line *line, unsigned int dest_y, uint8_t *data) { - size_t base = dest_y * line->xnum * sizeof(Cell); - memcpy(data + base, line->cells, line->xnum * sizeof(Cell)); + size_t base = dest_y * line->xnum * sizeof(GPUCell); + memcpy(data + base, line->gpu_cells, line->xnum * sizeof(GPUCell)); } @@ -1718,8 +1723,8 @@ screen_selection_range_for_line(Screen *self, index_type y, index_type *start, i if (y >= self->lines) { return false; } Line *line = visual_line_(self, y); index_type xlimit = line->xnum, xstart = 0; - while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--; - while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++; + while (xlimit > 0 && CHAR_IS_BLANK(line->cpu_cells[xlimit - 1].ch)) xlimit--; + while (xstart < xlimit && CHAR_IS_BLANK(line->cpu_cells[xstart].ch)) xstart++; *start = xstart; *end = xlimit; return true; } @@ -1738,7 +1743,7 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type *y1, inde index_type start, end; Line *line = visual_line_(self, *y1); *y2 = *y1; -#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch)) +#define is_ok(x) (is_word_char((line->cpu_cells[x].ch)) || is_opt_word_char(line->cpu_cells[x].ch)) if (!is_ok(x)) { start = x; end = x + 1; } else { diff --git a/kitty/shaders.c b/kitty/shaders.c index d15d4c1c3..4c3844c91 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -183,7 +183,7 @@ create_cell_vao() { #define A(name, size, dtype, offset, stride) \ add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \ /*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1); -#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(Cell, offset)), sizeof(Cell)) +#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell)) add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); @@ -267,7 +267,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa ensure_sprite_map(fonts_data); if (screen->scroll_changed || screen->is_dirty) { - sz = sizeof(Cell) * screen->lines * screen->columns; + sz = sizeof(GPUCell) * screen->lines * screen->columns; address = alloc_and_map_vao_buffer(vao_idx, sz, cell_data_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); screen_update_cell_data(screen, address, sz, fonts_data); unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL;