diff --git a/kitty/core_text.m b/kitty/core_text.m index 906a9ab55..d3ae0962b 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -180,7 +180,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) { PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) { CTFace *self = (CTFace*)base_face; - char text[128] = {0}; + char text[256] = {0}; cell_as_utf8(cell, true, text, ' '); CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8); if (str == NULL) return PyErr_NoMemory(); diff --git a/kitty/data-types.h b/kitty/data-types.h index 2f0c62443..d5dcc0924 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -31,7 +31,7 @@ typedef unsigned long long id_type; typedef uint32_t char_type; typedef uint32_t color_type; -typedef uint32_t combining_type; +typedef uint16_t combining_type; typedef uint32_t pixel; typedef unsigned int index_type; typedef uint16_t sprite_index; @@ -56,7 +56,6 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; #define REVERSE_SHIFT 6 #define STRIKE_SHIFT 7 #define COL_MASK 0xFFFFFFFF -#define CC_MASK 0xFFFF #define CC_SHIFT 16 #define UTF8_ACCEPT 0 #define UTF8_REJECT 1 @@ -139,7 +138,7 @@ typedef struct { attrs_type attrs; // The following are only needed on the CPU, not the GPU char_type ch; - combining_type cc; + combining_type cc[2]; } Cell; typedef struct { diff --git a/kitty/fonts.c b/kitty/fonts.c index d5f32d30d..28a5620ac 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -55,7 +55,7 @@ typedef struct { static GPUSpriteTracker sprite_tracker = {0}; static hb_buffer_t *harfbuzz_buffer = NULL; -static char_type shape_buffer[2048] = {0}; +static char_type shape_buffer[4096] = {0}; typedef struct { @@ -327,10 +327,8 @@ face_has_codepoint(PyObject* face, char_type cp) { static inline bool has_cell_text(Font *self, Cell *cell) { if (!face_has_codepoint(self->face, cell->ch)) return false; - if (cell->cc) { - if (!face_has_codepoint(self->face, cell->cc & CC_MASK)) return false; - char_type cc = cell->cc >> 16; - if (cc && !face_has_codepoint(self->face, cc)) return false; + for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) { + if (!face_has_codepoint(self->face, cell->cc[i])) return false; } return true; } @@ -494,14 +492,12 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) { hb_buffer_clear_contents(harfbuzz_buffer); while (num_cells) { attrs_type prev_width = 0; - for (num = 0; num_cells && num < sizeof(shape_buffer)/sizeof(shape_buffer[0]) - 20; first_cell++, num_cells--) { + for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc); first_cell++, num_cells--) { if (prev_width == 2) { prev_width = 0; continue; } shape_buffer[num++] = first_cell->ch; prev_width = first_cell->attrs & WIDTH_MASK; - if (first_cell->cc) { - shape_buffer[num++] = first_cell->cc & CC_MASK; - combining_type cc2 = first_cell->cc >> 16; - if (cc2) shape_buffer[num++] = cc2 & CC_MASK; + for (unsigned i = 0; i < arraysz(first_cell->cc) && first_cell->cc[i]; i++) { + shape_buffer[num++] = first_cell->cc[i]; } } hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num); @@ -580,7 +576,7 @@ static GroupState group_state = {0}; static inline unsigned int num_codepoints_in_cell(Cell *cell) { unsigned int ans = 1; - if (cell->cc) ans += ((cell->cc >> CC_SHIFT) & CC_MASK) ? 2 : 1; + for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) ans++; return ans; } @@ -657,14 +653,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) { case 0: cell_data->current_codepoint = cell_data->cell->ch; break; - case 1: - cell_data->current_codepoint = cell_data->cell->cc & CC_MASK; - break; - case 2: - cell_data->current_codepoint = (cell_data->cell->cc >> CC_SHIFT) & CC_MASK; - break; default: - cell_data->current_codepoint = 0; + cell_data->current_codepoint = cell_data->cell->cc[cell_data->codepoints_consumed - 1]; break; } } @@ -1053,12 +1043,11 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) { PyObject *text; int bold, italic; if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL; - static Py_UCS4 char_buf[16]; - if (!PyUnicode_AsUCS4(text, char_buf, sizeof(char_buf)/sizeof(char_buf[0]), 1)) return NULL; Cell cell = {0}; + static Py_UCS4 char_buf[2 + arraysz(cell.cc)]; + if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL; cell.ch = char_buf[0]; - if (PyUnicode_GetLength(text) > 1) cell.cc |= char_buf[1] & CC_MASK; - if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16; + for (unsigned i = 0; i + 1 < PyUnicode_GetLength(text) && i < arraysz(cell.cc); i++) cell.cc[i] = char_buf[i + 1]; if (bold) cell.attrs |= 1 << BOLD_SHIFT; if (italic) cell.attrs |= 1 << ITALIC_SHIFT; ssize_t ans = fallback_font(&cell); diff --git a/kitty/line.c b/kitty/line.c index df05c21de..c2dc70e03 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -35,15 +35,13 @@ line_length(Line *self) { } PyObject* -line_text_at(char_type ch, combining_type cc) { +cell_text(Cell *cell) { PyObject *ans; - if (LIKELY(cc == 0)) { - ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &ch, 1); - } else { - Py_UCS4 buf[3]; - buf[0] = ch; buf[1] = cc & CC_MASK; buf[2] = cc >> 16; - ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, buf[2] ? 3 : 2); - } + unsigned num = 1; + static Py_UCS4 buf[arraysz(cell->cc) + 1]; + buf[0] = cell->ch; + for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) buf[num++] = cell->cc[i]; + ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num); return ans; } @@ -160,7 +158,7 @@ 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 line_text_at(self->cells[xval].ch, self->cells[xval].cc); + return cell_text(self->cells + xval); } size_t @@ -168,13 +166,7 @@ cell_as_unicode(Cell *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) { - char_type cc = cell->cc; - Py_UCS4 cc1 = cc & CC_MASK, cc2; - if (cc1) { - buf[1] = cc1; n++; - cc2 = cc >> 16; - if (cc2) { buf[2] = cc2; n++; } - } + for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) buf[n++] = cell->cc[i]; } return n; } @@ -183,13 +175,7 @@ size_t cell_as_utf8(Cell *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) { - char_type cc = cell->cc; - Py_UCS4 cc1 = cc & CC_MASK, cc2; - if (cc1) { - n += encode_utf8(cc1, buf + n); - cc2 = cc >> 16; - if (cc2) { n += encode_utf8(cc2, buf + n); } - } + for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) n += encode_utf8(cell->cc[i], buf + n); } buf[n] = 0; return n; @@ -202,7 +188,7 @@ 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 < sizeof(buf)/sizeof(buf[0]) - 4; i++) { + for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc); i++) { char_type ch = self->cells[i].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; }; @@ -263,12 +249,8 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { t = prev_cursor; prev_cursor = cursor; cursor = t; if (*sgr) WRITE_SGR(sgr); WRITE_CH(ch); - char_type cc = self->cells[pos].cc; - Py_UCS4 cc1 = cc & CC_MASK; - if (cc1) { - WRITE_CH(cc1); - cc1 = cc >> 16; - if (cc1) { WRITE_CH(cc1); } + for(unsigned c = 0; c < arraysz(self->cells[pos].cc) && self->cells[pos].cc[c]; c++) { + WRITE_CH(self->cells[pos].cc[c]); } previous_width = attrs & WIDTH_MASK; } @@ -317,10 +299,12 @@ width(Line *self, PyObject *val) { void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) { - if (!self->cells[x].ch) return; // dont allow adding combining chars to a null cell - 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; + Cell *cell = self->cells + x; + if (!cell->ch) return; // dont allow adding combining chars to a null cell + for (unsigned i = 0; i < arraysz(cell->cc); i++) { + if (!cell->cc[i]) { cell->cc[i] = (combining_type)ch; return; } + } + cell->cc[arraysz(cell->cc) - 1] = (combining_type)ch; } static PyObject* @@ -370,7 +354,7 @@ set_text(Line* self, PyObject *args) { self->cells[i].fg = fg; self->cells[i].bg = bg; self->cells[i].decoration_fg = dfg; - self->cells[i].cc = 0; + memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc)); } Py_RETURN_NONE; @@ -402,7 +386,7 @@ 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; self->cells[i].cc = 0; \ + self->cells[i].ch = ch; memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc)); \ self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ } if (CHAR_IS_BLANK(ch)) { @@ -432,7 +416,7 @@ 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; - self->cells[i].cc = 0; + memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc)); self->cells[i].attrs = attrs; clear_sprite_position(self->cells[i]); } else { @@ -507,7 +491,7 @@ line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Curs self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; } self->cells[at].ch = ch; - self->cells[at].cc = 0; + memset(self->cells[at].cc, 0, sizeof(self->cells[at].cc)); } static PyObject* diff --git a/kitty/lineops.h b/kitty/lineops.h index 6e67247a6..741ecaea8 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -44,7 +44,6 @@ xlimit_for_line(Line *line) { return xlimit; } -PyObject* line_text_at(char_type, combining_type); void line_clear_text(Line *self, unsigned int at, unsigned int num, char_type 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 *, bool);