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.
This commit is contained in:
Daniel Eklöf 2019-08-02 18:19:07 +02:00
parent ab92abbd21
commit 4d7993b36f
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
9 changed files with 146 additions and 129 deletions

38
csi.c
View file

@ -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:

23
font.c
View file

@ -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

3
font.h
View file

@ -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);

55
input.c
View file

@ -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

View file

@ -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) {

View file

@ -5,6 +5,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <wctype.h>
#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;

View file

@ -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]));

View file

@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <wchar.h>
#include <threads.h>
#include <semaphore.h>
@ -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;

49
vt.c
View file

@ -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);
}