From 4d7993b36fa3f97e26c527c37b36eb9321b79791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 2 Aug 2019 18:19:07 +0200 Subject: [PATCH] cell: pack more efficiently and store glyph as a wchar The 'attributes' struct is now 8 bytes and naturally packed (used to be 9 bytes, artificially packed). 'cell' struct is now 12 bytes, naturally packed (used to be 13 bytes, artificially packed). Furthermore, the glyph is stored as a wchar instead of a char*. This makes it easier (faster) to do glyph lookup when rendering. --- csi.c | 38 ++++++++++++++++++++++-------------- font.c | 23 +++++++--------------- font.h | 3 ++- input.c | 55 ++++++++++++++++++++++++++++++----------------------- render.c | 12 ++++++------ selection.c | 49 +++++++++++++++++++++++++---------------------- terminal.c | 7 ++++--- terminal.h | 39 +++++++++++++++++++++---------------- vt.c | 49 ++++++++++++++++++++++------------------------- 9 files changed, 146 insertions(+), 129 deletions(-) diff --git a/csi.c b/csi.c index 03f80ccc..8eb57de9 100644 --- a/csi.c +++ b/csi.c @@ -50,8 +50,8 @@ static void sgr_reset(struct terminal *term) { memset(&term->vt.attrs, 0, sizeof(term->vt.attrs)); - term->vt.attrs.foreground = term->colors.fg; - term->vt.attrs.background = term->colors.bg; + term->vt.attrs.fg = term->colors.fg; + term->vt.attrs.bg = term->colors.bg; } static const char * @@ -129,7 +129,8 @@ csi_sgr(struct terminal *term) case 35: case 36: case 37: - term->vt.attrs.foreground = 1 << 30 | term->colors.regular[param - 30]; + term->vt.attrs.have_fg = 1; + term->vt.attrs.fg = term->colors.regular[param - 30]; break; case 38: { @@ -144,7 +145,8 @@ csi_sgr(struct terminal *term) color = term->colors.bright[idx - 8]; else color = colors256[idx]; - term->vt.attrs.foreground = 1 << 30 | color; + term->vt.attrs.have_fg = 1; + term->vt.attrs.fg = color; i += 2; } @@ -155,7 +157,8 @@ csi_sgr(struct terminal *term) uint8_t r = term->vt.params.v[i + 2].value; uint8_t g = term->vt.params.v[i + 3].value; uint8_t b = term->vt.params.v[i + 4].value; - term->vt.attrs.foreground = 1 << 30 | r << 16 | g << 8 | b; + term->vt.attrs.have_fg = 1; + term->vt.attrs.fg = r << 16 | g << 8 | b; i += 4; } @@ -171,7 +174,8 @@ csi_sgr(struct terminal *term) /* 6 - CS tolerance */ /* 7 - color space associated with tolerance */ - term->vt.attrs.foreground = 1 << 30 | r << 16 | g << 8 | b; + term->vt.attrs.have_fg = 1; + term->vt.attrs.fg = r << 16 | g << 8 | b; } else UNHANDLED_SGR(); } @@ -183,7 +187,7 @@ csi_sgr(struct terminal *term) } case 39: - term->vt.attrs.foreground = 0; + term->vt.attrs.have_fg = 0; break; /* Regular background colors */ @@ -195,7 +199,8 @@ csi_sgr(struct terminal *term) case 45: case 46: case 47: - term->vt.attrs.background = 1 << 30 | term->colors.regular[param - 40]; + term->vt.attrs.have_bg = 1; + term->vt.attrs.bg = term->colors.regular[param - 40]; break; case 48: { @@ -211,7 +216,8 @@ csi_sgr(struct terminal *term) color = term->colors.bright[idx - 8]; else color = colors256[idx]; - term->vt.attrs.background = 1 << 30 | color; + term->vt.attrs.have_bg = 1; + term->vt.attrs.bg = color; i += 2; } @@ -221,7 +227,8 @@ csi_sgr(struct terminal *term) uint8_t r = term->vt.params.v[i + 2].value; uint8_t g = term->vt.params.v[i + 3].value; uint8_t b = term->vt.params.v[i + 4].value; - term->vt.attrs.background = 1 << 30 | r << 16 | g << 8 | b; + term->vt.attrs.have_bg = 1; + term->vt.attrs.bg = r << 16 | g << 8 | b; i += 4; } @@ -238,7 +245,8 @@ csi_sgr(struct terminal *term) /* 6 - CS tolerance */ /* 7 - color space associated with tolerance */ - term->vt.attrs.background = 1 << 30 | r << 16 | g << 8 | b; + term->vt.attrs.have_bg = 1; + term->vt.attrs.bg = r << 16 | g << 8 | b; } else UNHANDLED_SGR(); } @@ -249,7 +257,7 @@ csi_sgr(struct terminal *term) break; } case 49: - term->vt.attrs.background = 0; + term->vt.attrs.have_bg = 0; break; /* Bright foreground colors */ @@ -261,7 +269,8 @@ csi_sgr(struct terminal *term) case 95: case 96: case 97: - term->vt.attrs.foreground = 1 << 30 | term->colors.bright[param - 90]; + term->vt.attrs.have_fg = 1; + term->vt.attrs.fg = term->colors.bright[param - 90]; break; /* Regular background colors */ @@ -273,7 +282,8 @@ csi_sgr(struct terminal *term) case 105: case 106: case 107: - term->vt.attrs.background = 1 << 30 | term->colors.bright[param - 100]; + term->vt.attrs.have_bg = 1; + term->vt.attrs.bg = term->colors.bright[param - 100]; break; default: diff --git a/font.c b/font.c index 9c4a9b50..8deb2a3a 100644 --- a/font.c +++ b/font.c @@ -381,29 +381,23 @@ glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) .left = font->face->glyph->bitmap_left, .top = font->face->glyph->bitmap_top, .pixel_size_fixup = font->pixel_size_fixup, + .valid = true, }; return true; err: + *glyph = (struct glyph){ + .valid = false, + }; return false; } const struct glyph * -font_glyph_for_utf8(struct font *font, const char *utf8) +font_glyph_for_wc(struct font *font, wchar_t wc) { mtx_lock(&font->lock); - mbstate_t ps = {0}; - wchar_t wc; - if (mbrtowc(&wc, utf8, 4, &ps) < 0) { - LOG_DBG("failed to convert utf-8 sequence %02x %02x %02x %02x to unicode", - (unsigned char)utf8[0], (unsigned char)utf8[1], - (unsigned char)utf8[2], (unsigned char)utf8[3]); - mtx_unlock(&font->lock); - return NULL; - } - assert(font->cache != NULL); size_t hash_idx = hash_index(wc); hash_entry_t *hash_entry = font->cache[hash_idx]; @@ -418,10 +412,7 @@ font_glyph_for_utf8(struct font *font, const char *utf8) } struct glyph glyph; - if (!glyph_for_wchar(font, wc, &glyph)) { - mtx_unlock(&font->lock); - return NULL; - } + bool got_glyph = glyph_for_wchar(font, wc, &glyph); if (hash_entry == NULL) { hash_entry = calloc(1, sizeof(*hash_entry)); @@ -434,7 +425,7 @@ font_glyph_for_utf8(struct font *font, const char *utf8) tll_push_back(*hash_entry, glyph); mtx_unlock(&font->lock); - return &tll_back(*hash_entry); + return got_glyph ? &tll_back(*hash_entry) : NULL; } void diff --git a/font.h b/font.h index 9746eaac..90a4b1dd 100644 --- a/font.h +++ b/font.h @@ -22,6 +22,7 @@ struct glyph { int top; double pixel_size_fixup; + bool valid; }; typedef tll(struct glyph) hash_entry_t; @@ -51,5 +52,5 @@ struct font { }; bool font_from_name(font_list_t names, const char *attributes, struct font *result); -const struct glyph *font_glyph_for_utf8(struct font *font, const char *utf8); +const struct glyph *font_glyph_for_wc(struct font *font, wchar_t wc); void font_destroy(struct font *font); diff --git a/input.c b/input.c index 783d8f52..596bce1f 100644 --- a/input.c +++ b/input.c @@ -60,12 +60,6 @@ static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - LOG_DBG("enter"); -#if 0 - uint32_t *key; - wl_array_for_each(key, keys) - xkb_state_update_key(xkb_state, *key, 1); -#endif struct terminal *term = data; term->input_serial = serial; term_focus_in(term); @@ -76,7 +70,6 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { struct terminal *term = data; - term_focus_out(term); mtx_lock(&term->kbd.repeat.mutex); if (term->kbd.repeat.cmd != REPEAT_EXIT) { @@ -84,6 +77,35 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, cnd_signal(&term->kbd.repeat.cond); } mtx_unlock(&term->kbd.repeat.mutex); + + term_focus_out(term); +} + +static void +start_repeater(struct terminal *term, uint32_t key) +{ + mtx_lock(&term->kbd.repeat.mutex); + if (!term->kbd.repeat.dont_re_repeat) { + if (term->kbd.repeat.cmd != REPEAT_EXIT) { + term->kbd.repeat.cmd = REPEAT_START; + term->kbd.repeat.key = key; + cnd_signal(&term->kbd.repeat.cond); + } + } + mtx_unlock(&term->kbd.repeat.mutex); +} + +static void +stop_repeater(struct terminal *term, uint32_t key) +{ + mtx_lock(&term->kbd.repeat.mutex); + if (term->kbd.repeat.key == key) { + if (term->kbd.repeat.cmd != REPEAT_EXIT) { + term->kbd.repeat.cmd = REPEAT_STOP; + cnd_signal(&term->kbd.repeat.cond); + } + } + mtx_unlock(&term->kbd.repeat.mutex); } static void @@ -97,14 +119,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, const xkb_mod_mask_t shift = 1 << term->kbd.mod_shift; if (state == XKB_KEY_UP) { - mtx_lock(&term->kbd.repeat.mutex); - if (term->kbd.repeat.key == key) { - if (term->kbd.repeat.cmd != REPEAT_EXIT) { - term->kbd.repeat.cmd = REPEAT_STOP; - cnd_signal(&term->kbd.repeat.cond); - } - } - mtx_unlock(&term->kbd.repeat.mutex); + stop_repeater(term, key); return; } @@ -261,15 +276,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, } } - mtx_lock(&term->kbd.repeat.mutex); - if (!term->kbd.repeat.dont_re_repeat) { - if (term->kbd.repeat.cmd != REPEAT_EXIT) { - term->kbd.repeat.cmd = REPEAT_START; - term->kbd.repeat.key = key - 8; - cnd_signal(&term->kbd.repeat.cond); - } - } - mtx_unlock(&term->kbd.repeat.mutex); + start_repeater(term, key - 8); } static void diff --git a/render.c b/render.c index 67a1703f..c0e959e3 100644 --- a/render.c +++ b/render.c @@ -154,11 +154,11 @@ render_cell(struct terminal *term, cairo_t *cr, bool block_cursor = has_cursor && term->cursor_style == CURSOR_BLOCK; bool is_selected = coord_is_selected(term, col, row); - uint32_t _fg = cell->attrs.foreground >> 30 - ? cell->attrs.foreground + uint32_t _fg = cell->attrs.have_fg + ? cell->attrs.fg : !term->reverse ? term->colors.fg : term->colors.bg; - uint32_t _bg = cell->attrs.background >> 30 - ? cell->attrs.background + uint32_t _bg = cell->attrs.have_bg + ? cell->attrs.bg : !term->reverse ? term->colors.bg : term->colors.fg; /* If *one* is set, we reverse */ @@ -185,7 +185,7 @@ render_cell(struct terminal *term, cairo_t *cr, } struct font *font = attrs_to_font(term, &cell->attrs); - const struct glyph *glyph = font_glyph_for_utf8(font, cell->c); + const struct glyph *glyph = font_glyph_for_wc(font, cell->wc); int cell_cols = glyph != NULL ? max(1, glyph->width) : 1; @@ -210,7 +210,7 @@ render_cell(struct terminal *term, cairo_t *cr, arm_blink_timer(term); } - if (cell->c[0] == '\0' || cell->attrs.conceal) + if (cell->wc == 0 || cell->attrs.conceal) return cell_cols; if (glyph != NULL) { diff --git a/selection.c b/selection.c index 4c8ab0ca..bc35fde9 100644 --- a/selection.c +++ b/selection.c @@ -5,6 +5,7 @@ #include #include #include +#include #define LOG_MODULE "selection" #define LOG_ENABLE_DBG 0 @@ -58,11 +59,12 @@ extract_selection(const struct terminal *term) /* TODO: replace '\0' with spaces, then trim lines */ for (int col = start_col; col < term->cols; col++) { const struct cell *cell = &row->cells[col]; - if (cell->c[0] == '\0') + if (cell->wc == 0) continue; - size_t len = strnlen(cell->c, 4); - memcpy(&buf[idx], cell->c, len); + mbstate_t ps = {0}; + size_t len = wcrtomb(&buf[idx], cell->wc, &ps); + assert(len >= 0); /* All wchars were valid multibyte strings to begin with */ idx += len; } @@ -76,11 +78,12 @@ extract_selection(const struct terminal *term) const struct row *row = grid_row_in_view(term->grid, end->row - term->grid->view); for (int col = start_col; row != NULL && col <= end->col; col++) { const struct cell *cell = &row->cells[col]; - if (cell->c[0] == '\0') + if (cell->wc == 0) continue; - size_t len = strnlen(cell->c, 4); - memcpy(&buf[idx], cell->c, len); + mbstate_t ps = {0}; + size_t len = wcrtomb(&buf[idx], cell->wc, &ps); + assert(len >= 0); /* All wchars were valid multibyte strings to begin with */ idx += len; } } @@ -214,19 +217,19 @@ selection_cancel(struct terminal *term) } static bool -isword(int c) +isword(wint_t c) { switch (c) { - default: return !isspace(c); + default: return !iswspace(c); - case '{': case '}': - case '[': case ']': - case '(': case ')': - case '`': - case '\'': - case '"': - case ',': case '.': - case ':': case ';': + case L'{': case L'}': + case L'[': case L']': + case L'(': case L')': + case L'`': + case L'\'': + case L'"': + case L',': case L'.': + case L':': case L';': return false; } } @@ -243,9 +246,9 @@ selection_mark_word(struct terminal *term, int col, int row, uint32_t serial) struct coord end = {col, row}; const struct row *r = grid_row_in_view(term->grid, start.row); - unsigned char c = r->cells[start.col].c[0]; + wchar_t c = r->cells[start.col].wc; - if (!(c == '\0' || !isword(c))) { + if (!(c == 0 || !isword(c))) { while (true) { int next_col = start.col - 1; int next_row = start.row; @@ -259,8 +262,8 @@ selection_mark_word(struct terminal *term, int col, int row, uint32_t serial) const struct row *row = grid_row_in_view(term->grid, next_row); - unsigned char c = row->cells[next_col].c[0]; - if (c == '\0' || !isword(c)) + c = row->cells[next_col].wc; + if (c == 0 || !isword(c)) break; start.col = next_col; @@ -269,9 +272,9 @@ selection_mark_word(struct terminal *term, int col, int row, uint32_t serial) } r = grid_row_in_view(term->grid, end.row); - c = r->cells[end.col].c[0]; + c = r->cells[end.col].wc; - if (!(c == '\0' || !isword(c))) { + if (!(c == 0 || !isword(c))) { while (true) { int next_col = end.col + 1; int next_row = end.row; @@ -285,7 +288,7 @@ selection_mark_word(struct terminal *term, int col, int row, uint32_t serial) const struct row *row = grid_row_in_view(term->grid, next_row); - unsigned char c = row->cells[next_col].c[0]; + c = row->cells[next_col].wc; if (c == '\0' || !isword(c)) break; diff --git a/terminal.c b/terminal.c index 8226f38e..65912cfa 100644 --- a/terminal.c +++ b/terminal.c @@ -163,11 +163,12 @@ erase_cell_range(struct terminal *term, struct row *row, int start, int end) assert(start < term->cols); assert(end < term->cols); - if (unlikely(term->vt.attrs.background >> 30)) { + if (unlikely(term->vt.attrs.have_bg)) { for (int col = start; col <= end; col++) { - row->cells[col].c[0] = '\0'; + row->cells[col].wc = 0; row->cells[col].attrs.clean = 0; - row->cells[col].attrs.background = term->vt.attrs.background; + row->cells[col].attrs.have_bg = 1; + row->cells[col].attrs.bg = term->vt.attrs.bg; } } else { memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0])); diff --git a/terminal.h b/terminal.h index 0d6f00f3..195ad26b 100644 --- a/terminal.h +++ b/terminal.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -50,28 +51,34 @@ struct rgb { float r, g, b; }; /* * Note: we want the cells to be as small as possible. Larger cells * means fewer scrollback lines (or performance drops due to cache - * misses) */ + * misses) + * + * Note that the members are laid out optimized for x86 + */ struct attributes { - uint8_t bold:1; - uint8_t dim:1; - uint8_t italic:1; - uint8_t underline:1; - uint8_t strikethrough:1; - uint8_t blink:1; - uint8_t conceal:1; - uint8_t reverse:1; + uint32_t bold:1; + uint32_t dim:1; + uint32_t italic:1; + uint32_t underline:1; + uint32_t strikethrough:1; + uint32_t blink:1; + uint32_t conceal:1; + uint32_t reverse:1; + uint32_t fg:24; uint32_t clean:1; - uint32_t foreground:31; - - uint32_t reserved:1; - uint32_t background:31; -} __attribute__((packed)); + uint32_t have_fg:1; + uint32_t have_bg:1; + uint32_t reserved:5; + uint32_t bg:24; +}; +static_assert(sizeof(struct attributes) == 8, "bad size"); struct cell { + wchar_t wc; struct attributes attrs; - char c[4]; -} __attribute__((packed)); +}; +static_assert(sizeof(struct cell) == 12, "bad size"); struct scroll_region { int start; diff --git a/vt.c b/vt.c index 6ffc12f6..439a4421 100644 --- a/vt.c +++ b/vt.c @@ -728,33 +728,29 @@ action_print_utf8(struct terminal *term) print_insert(term); - //LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data); - memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx); - cell->c[term->vt.utf8.idx] = '\0'; + mbstate_t ps = {0}; + if (mbrtowc(&cell->wc, (const char *)term->vt.utf8.data, term->vt.utf8.idx, &ps) < 0) + cell->wc = 0; + term->vt.utf8.idx = 0; cell->attrs = term->vt.attrs; - /* Hack: zero- and double-width characters */ - mbstate_t ps = {0}; - wchar_t wc; - if (mbrtowc(&wc, cell->c, 4, &ps) >= 0) { - int width = wcwidth(wc); - if (width <= 0) { - /* Skip post_print() below - i.e. don't advance cursor */ - return; - } + int width = wcwidth(cell->wc); + if (width <= 0) { + /* Skip post_print() below - i.e. don't advance cursor */ + return; + } - /* Advance cursor the 'additional' columns (last step is done - * by post_print()) */ - for (int i = 1; i < width && term->cursor.col < term->cols - 1; i++) { - term_cursor_right(term, 1); + /* Advance cursor the 'additional' columns (last step is done + * by post_print()) */ + for (int i = 1; i < width && term->cursor.col < term->cols - 1; i++) { + term_cursor_right(term, 1); - assert(term->cursor.col < term->cols); - struct cell *cell = &row->cells[term->cursor.col]; - cell->c[0] = '\0'; - cell->attrs.clean = 0; - } + assert(term->cursor.col < term->cols); + struct cell *cell = &row->cells[term->cursor.col]; + cell->wc = 0; + cell->attrs.clean = 0; } post_print(term); @@ -788,16 +784,17 @@ action_print(struct terminal *term, uint8_t c) c >= 0x41 && c <= 0x7e) { const char *glyph = vt100_0[c - 0x41]; - if (glyph != NULL) - strncpy(cell->c, glyph, sizeof(cell->c)); + if (glyph != NULL) { + mbstate_t ps = {0}; + if (mbrtowc(&cell->wc, glyph, strlen(glyph), &ps) < 0) + cell->wc = 0; + } } else { //LOG_DBG("print: ASCII: %c", c); - cell->c[0] = c; - cell->c[1] = '\0'; + cell->wc = c; } cell->attrs = term->vt.attrs; - post_print(term); }