From c531795b8372eb0e69aa874476f3e96cd7c09dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Jul 2019 20:13:26 +0200 Subject: [PATCH 1/4] wip: initial multithreaded renderer --- config.c | 31 ++++++--- config.h | 2 + main.c | 44 +++++++++++- render.c | 199 ++++++++++++++++++++++++++++++++++++++--------------- render.h | 6 ++ shm.c | 69 +++++++++++++------ shm.h | 7 +- terminal.h | 18 +++++ 8 files changed, 287 insertions(+), 89 deletions(-) diff --git a/config.c b/config.c index 707f7d93..dda3c635 100644 --- a/config.c +++ b/config.c @@ -107,26 +107,28 @@ get_config_path(void) } static bool -str_to_color(const char *s, uint32_t *color, const char *path, int lineno) +str_to_ulong(const char *s, int base, unsigned long *res) { if (s == NULL) return false; errno = 0; char *end = NULL; - unsigned long res = strtoul(s, &end, 16); - if (errno != 0) { + *res = strtoul(s, &end, base); + return errno == 0 && *end == '\0'; +} + +static bool +str_to_color(const char *s, uint32_t *color, const char *path, int lineno) +{ + unsigned long value; + if (!str_to_ulong(s, 16, &value)) { LOG_ERRNO("%s:%d: invalid color: %s", path, lineno, s); return false; } - if (*end != '\0') { - LOG_ERR("%s:%d: invalid color: %s", path, lineno, s); - return false; - } - - *color = res & 0xffffff; + *color = value & 0xffffff; return true; } @@ -149,6 +151,15 @@ parse_section_main(const char *key, const char *value, struct config *conf, conf->font = strdup(value); } + else if (strcmp(key, "workers") == 0) { + unsigned long count; + if (!str_to_ulong(value, 10, &count)) { + LOG_ERR("%s:%d: expected an integer: %s", path, lineno, value); + return false; + } + conf->render_worker_count = count; + } + else { LOG_WARN("%s:%u: invalid key: %s", path, lineno, key); return false; @@ -427,6 +438,8 @@ config_load(struct config *conf) .cursor = 0, }, }, + + .render_worker_count = sysconf(_SC_NPROCESSORS_ONLN), }; char *path = get_config_path(); diff --git a/config.h b/config.h index 68d6f0a1..e965f142 100644 --- a/config.h +++ b/config.h @@ -24,6 +24,8 @@ struct config { uint32_t cursor; } color; } cursor; + + size_t render_worker_count; }; bool config_load(struct config *conf); diff --git a/main.c b/main.c index 7d0fa355..0368c687 100644 --- a/main.c +++ b/main.c @@ -11,9 +11,9 @@ #include #include +#include #include -#include #include #include #include @@ -388,8 +388,18 @@ main(int argc, char *const *argv) .normal = {.damage = tll_init(), .scroll_damage = tll_init()}, .alt = {.damage = tll_init(), .scroll_damage = tll_init()}, .grid = &term.normal, + .render = { + .workers = { + .count = conf.render_worker_count, + .queue = tll_init(), + }, + }, }; + LOG_INFO("using %zu rendering threads", term.render.workers.count); + + struct render_worker_context worker_context[term.render.workers.count]; + /* Initialize 'current' colors from the default colors */ term.colors.fg = term.colors.default_fg; term.colors.bg = term.colors.default_bg; @@ -409,6 +419,18 @@ main(int argc, char *const *argv) thrd_t keyboard_repeater_id; thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term); + sem_init(&term.render.workers.start, 0, 0); + sem_init(&term.render.workers.done, 0, 0); + mtx_init(&term.render.workers.lock, mtx_plain); + cnd_init(&term.render.workers.cond); + + term.render.workers.threads = calloc(term.render.workers.count, sizeof(term.render.workers.threads[0])); + for (size_t i = 0; i < term.render.workers.count; i++) { + worker_context[i].term = &term; + worker_context[i].my_id = 1 + i; + thrd_create(&term.render.workers.threads[i], &render_worker_thread, &worker_context[i]); + } + if (!font_from_name(conf.font, &term.fonts[0])) goto out; @@ -827,6 +849,15 @@ out: cnd_signal(&term.kbd.repeat.cond); mtx_unlock(&term.kbd.repeat.mutex); + mtx_lock(&term.render.workers.lock); + assert(tll_length(term.render.workers.queue) == 0); + for (size_t i = 0; i < term.render.workers.count; i++) { + sem_post(&term.render.workers.start); + tll_push_back(term.render.workers.queue, -2); + } + cnd_broadcast(&term.render.workers.cond); + mtx_unlock(&term.render.workers.lock); + shm_fini(); if (term.render.frame_callback != NULL) wl_callback_destroy(term.render.frame_callback); @@ -910,6 +941,17 @@ out: thrd_join(keyboard_repeater_id, NULL); cnd_destroy(&term.kbd.repeat.cond); mtx_destroy(&term.kbd.repeat.mutex); + + for (size_t i = 0; i < term.render.workers.count; i++) + thrd_join(term.render.workers.threads[i], NULL); + free(term.render.workers.threads); + cnd_destroy(&term.render.workers.cond); + mtx_destroy(&term.render.workers.lock); + sem_destroy(&term.render.workers.start); + sem_destroy(&term.render.workers.done); + assert(tll_length(term.render.workers.queue) == 0); + tll_free(term.render.workers.queue); + close(term.kbd.repeat.pipe_read_fd); close(term.kbd.repeat.pipe_write_fd); diff --git a/render.c b/render.c index cf6c013e..123e5593 100644 --- a/render.c +++ b/render.c @@ -76,45 +76,52 @@ gseq_flush(struct terminal *term, struct buffer *buf) } static void -draw_underline(const struct terminal *term, struct buffer *buf, +draw_underline(const struct terminal *term, struct buffer *buf, size_t buf_idx, const struct font *font, struct rgb color, double x, double y) { //const struct font *font = attrs_to_font(term, &cell->attrs); double baseline = y + term->fextents.height - term->fextents.descent; double width = font->underline.thickness; double y_under = baseline - font->underline.position - width / 2.; + cairo_t *cr = buf->cairo[buf_idx]; - cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b); - cairo_set_line_width(buf->cairo, width); - cairo_move_to(buf->cairo, x, round(y_under) + 0.5); - cairo_rel_line_to(buf->cairo, term->cell_width, 0); - cairo_stroke(buf->cairo); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, color.r, color.g, color.b); + cairo_set_line_width(cr, width); + cairo_move_to(cr, x, round(y_under) + 0.5); + cairo_rel_line_to(cr, term->cell_width, 0); + cairo_stroke(cr); } static void -draw_bar(const struct terminal *term, struct buffer *buf, struct rgb color, - double x, double y) +draw_bar(const struct terminal *term, struct buffer *buf, size_t buf_idx, + struct rgb color, double x, double y) { - cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b); - cairo_set_line_width(buf->cairo, 1.0); - cairo_move_to(buf->cairo, x + 0.5, y); - cairo_rel_line_to(buf->cairo, 0, term->cell_height); - cairo_stroke(buf->cairo); + cairo_t *cr = buf->cairo[buf_idx]; + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, color.r, color.g, color.b); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, x + 0.5, y); + cairo_rel_line_to(cr, 0, term->cell_height); + cairo_stroke(cr); } static void -draw_strikeout(const struct terminal *term, struct buffer *buf, +draw_strikeout(const struct terminal *term, struct buffer *buf, size_t buf_idx, const struct font *font, struct rgb color, double x, double y) { double baseline = y + term->fextents.height - term->fextents.descent; double width = font->strikeout.thickness; double y_strike = baseline - font->strikeout.position - width / 2.; + cairo_t *cr = buf->cairo[buf_idx]; - cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b); - cairo_set_line_width(buf->cairo, width); - cairo_move_to(buf->cairo, x, round(y_strike) + 0.5); - cairo_rel_line_to(buf->cairo, term->cell_width, 0); - cairo_stroke(buf->cairo); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, color.r, color.g, color.b); + cairo_set_line_width(cr, width); + cairo_move_to(cr, x, round(y_strike) + 0.5); + cairo_rel_line_to(cr, term->cell_width, 0); + cairo_stroke(cr); } static bool @@ -166,9 +173,10 @@ arm_blink_timer(struct terminal *term) } static void -render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, - int col, int row, bool has_cursor) +render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx, + const struct cell *cell, int col, int row, bool has_cursor) { + cairo_t *cr = buf->cairo[buf_idx]; double width = term->cell_width; double height = term->cell_height; double x = col * width; @@ -208,18 +216,19 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, } /* Background */ - cairo_set_source_rgb(buf->cairo, bg.r, bg.g, bg.b); - cairo_rectangle(buf->cairo, x, y, width, height); - cairo_fill(buf->cairo); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(cr, bg.r, bg.g, bg.b); + cairo_rectangle(cr, x, y, width, height); + cairo_fill(cr); /* Non-block cursors */ if (has_cursor) { struct rgb cursor_color = color_hex_to_rgb(term->cursor_color.cursor); if (term->cursor_style == CURSOR_BAR) - draw_bar(term, buf, cursor_color, x, y); + draw_bar(term, buf, buf_idx, cursor_color, x, y); else if (term->cursor_style == CURSOR_UNDERLINE) draw_underline( - term, buf, attrs_to_font(term, &cell->attrs), cursor_color, x, y); + term, buf, buf_idx, attrs_to_font(term, &cell->attrs), cursor_color, x, y); } if (cell->attrs.blink && !term->blink.active) { @@ -232,10 +241,10 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, /* Underline */ if (cell->attrs.underline) - draw_underline(term, buf, attrs_to_font(term, &cell->attrs), fg, x, y); + draw_underline(term, buf, buf_idx, attrs_to_font(term, &cell->attrs), fg, x, y); if (cell->attrs.strikethrough) - draw_strikeout(term, buf, attrs_to_font(term, &cell->attrs), fg, x, y); + draw_strikeout(term, buf, buf_idx, attrs_to_font(term, &cell->attrs), fg, x, y); /* * cairo_show_glyphs() apparently works *much* faster when @@ -268,15 +277,25 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, struct glyph _glyph; if (glyph == NULL) { - if (!font_glyph_for_utf8(font, cell->c, &_glyph)) + if (!font_glyph_for_utf8(font, cell->c, &_glyph)) { + LOG_ERR("FAILED: %.4s", cell->c); return; + } glyph = &_glyph; } assert(glyph != NULL); - cairo_set_source_rgb(buf->cairo, fg.r, fg.g, fg.b); - cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); - cairo_mask_surface(buf->cairo, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); + +#if 0 + cairo_surface_t *surf = cairo_image_surface_create_for_data( + glyph->data, glyph->format, glyph->width, glyph->height, glyph->stride); + cairo_mask_surface(cr, surf, x + glyph->left, y + term->fextents.ascent - glyph->top); + cairo_surface_destroy(surf); +#else + cairo_mask_surface(cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); +#endif if (glyph == &_glyph) { cairo_surface_destroy(_glyph.surf); @@ -303,11 +322,11 @@ grid_render_scroll(struct terminal *term, struct buffer *buf, buf->size); if (height > 0) { - cairo_surface_flush(buf->cairo_surface); - uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface); + cairo_surface_flush(buf->cairo_surface[0]); + uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); - cairo_surface_mark_dirty(buf->cairo_surface); + cairo_surface_mark_dirty(buf->cairo_surface[0]); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); } @@ -332,26 +351,74 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, buf->size); if (height > 0) { - cairo_surface_flush(buf->cairo_surface); - uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface); + cairo_surface_flush(buf->cairo_surface[0]); + uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]); memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); - cairo_surface_mark_dirty(buf->cairo_surface); + cairo_surface_mark_dirty(buf->cairo_surface[0]); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height); } } static void -render_row(struct terminal *term, struct buffer *buf, struct row *row, int row_no) +render_row(struct terminal *term, struct buffer *buf, size_t buf_idx, struct row *row, int row_no) { for (int col = 0; col < term->cols; col++) - render_cell(term, buf, &row->cells[col], col, row_no, false); + render_cell(term, buf, buf_idx, &row->cells[col], col, row_no, false); +#if 0 wl_surface_damage_buffer( term->wl.surface, 0, row_no * term->cell_height, term->width, term->cell_height); +#endif +} + +int +render_worker_thread(void *_ctx) +{ + struct render_worker_context *ctx = _ctx; + struct terminal *term = ctx->term; + const int my_id = ctx->my_id; + + sem_t *start = &term->render.workers.start; + sem_t *done = &term->render.workers.done; + mtx_t *lock = &term->render.workers.lock; + cnd_t *cond = &term->render.workers.cond; + + while (true) { + sem_wait(start); + + struct buffer *buf = term->render.workers.buf; + bool frame_done = false; + + while (!frame_done) { + mtx_lock(lock); + while (tll_length(term->render.workers.queue) == 0) + cnd_wait(cond, lock); + + int row_no = tll_pop_front(term->render.workers.queue); + mtx_unlock(lock); + + switch (row_no) { + default: + assert(buf != NULL); + render_row(term, buf, my_id, grid_row_in_view(term->grid, row_no), row_no); + break; + + case -1: + frame_done = true; + sem_post(done); + break; + + case -2: + return 0; + } + } + }; + + return -1; } static void frame_callback( @@ -374,8 +441,8 @@ grid_render(struct terminal *term) assert(term->width > 0); assert(term->height > 0); - struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height); - cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); + struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count); + cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_SOURCE); gseq.g = gseq.glyphs; gseq.count = 0; @@ -385,7 +452,7 @@ grid_render(struct terminal *term) /* Erase old cursor (if we rendered a cursor last time) */ if (term->render.last_cursor.cell != NULL) { render_cell( - term, buf, + term, buf, 0, term->render.last_cursor.cell, term->render.last_cursor.in_view.col, term->render.last_cursor.in_view.row, false); @@ -424,11 +491,11 @@ grid_render(struct terminal *term) uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; struct rgb bg = color_hex_to_rgb(_bg); - cairo_set_source_rgb(buf->cairo, bg.r, bg.g, bg.b); + cairo_set_source_rgb(buf->cairo[0], bg.r, bg.g, bg.b); - cairo_rectangle(buf->cairo, rmargin, 0, rmargin_width, term->height); - cairo_rectangle(buf->cairo, 0, bmargin, term->width, bmargin_height); - cairo_fill(buf->cairo); + cairo_rectangle(buf->cairo[0], rmargin, 0, rmargin_width, term->height); + cairo_rectangle(buf->cairo[0], 0, bmargin, term->width, bmargin_height); + cairo_fill(buf->cairo[0]); wl_surface_damage_buffer( term->wl.surface, rmargin, 0, rmargin_width, term->height); @@ -456,20 +523,38 @@ grid_render(struct terminal *term) tll_remove(term->grid->scroll_damage, it); } + term->render.workers.buf = buf; + for (size_t i = 0; i < term->render.workers.count; i++) + sem_post(&term->render.workers.start); + + assert(tll_length(term->render.workers.queue) == 0); + for (int r = 0; r < term->rows; r++) { struct row *row = grid_row_in_view(term->grid, r); if (!row->dirty) continue; - //LOG_WARN("rendering line: %d", r); + mtx_lock(&term->render.workers.lock); + tll_push_back(term->render.workers.queue, r); + cnd_signal(&term->render.workers.cond); + mtx_unlock(&term->render.workers.lock); row->dirty = false; all_clean = false; - render_row(term, buf, row, r); + wl_surface_damage_buffer( + term->wl.surface, + 0, r * term->cell_height, + term->width, term->cell_height); } + mtx_lock(&term->render.workers.lock); + for (size_t i = 0; i < term->render.workers.count; i++) + tll_push_back(term->render.workers.queue, -1); + cnd_broadcast(&term->render.workers.cond); + mtx_unlock(&term->render.workers.lock); + if (term->blink.active) { /* Check if there are still any visible blinking cells */ bool none_is_blinking = true; @@ -516,6 +601,10 @@ grid_render(struct terminal *term) cursor_is_visible = true; } + for (size_t i = 0; i < term->render.workers.count; i++) + sem_wait(&term->render.workers.done); + term->render.workers.buf = NULL; + if (cursor_is_visible && !term->hide_cursor) { /* Remember cursor coordinates so that we can erase it next * time. Note that we need to re-align it against the view. */ @@ -531,7 +620,7 @@ grid_render(struct terminal *term) term->render.last_cursor.cell = &row->cells[term->cursor.col]; render_cell( - term, buf, term->render.last_cursor.cell, + term, buf, 0, term->render.last_cursor.cell, term->cursor.col, view_aligned_row, true); wl_surface_damage_buffer( @@ -549,10 +638,10 @@ grid_render(struct terminal *term) } if (term->flash.active) { - cairo_set_source_rgba(buf->cairo, 1.0, 1.0, 0.0, 0.5); - cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); - cairo_rectangle(buf->cairo, 0, 0, term->width, term->height); - cairo_fill(buf->cairo); + cairo_set_source_rgba(buf->cairo[0], 1.0, 1.0, 0.0, 0.5); + cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_OVER); + cairo_rectangle(buf->cairo[0], 0, 0, term->width, term->height); + cairo_fill(buf->cairo[0]); wl_surface_damage_buffer( term->wl.surface, 0, 0, term->width, term->height); @@ -561,7 +650,7 @@ grid_render(struct terminal *term) assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows); assert(term->grid->view >= 0 && term->grid->view < term->grid->num_rows); - cairo_surface_flush(buf->cairo_surface); + cairo_surface_flush(buf->cairo_surface[0]); wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0); assert(term->render.frame_callback == NULL); diff --git a/render.h b/render.h index fd923e7d..0b7a40ca 100644 --- a/render.h +++ b/render.h @@ -10,3 +10,9 @@ void render_resize(struct terminal *term, int width, int height); void render_set_title(struct terminal *term, const char *title); void render_update_cursor_surface(struct terminal *term); void render_refresh(struct terminal *term); + +struct render_worker_context { + int my_id; + struct terminal *term; +}; +int render_worker_thread(void *_ctx); diff --git a/shm.c b/shm.c index dd85ef3f..ca078544 100644 --- a/shm.c +++ b/shm.c @@ -27,8 +27,10 @@ static const struct wl_buffer_listener buffer_listener = { }; struct buffer * -shm_get_buffer(struct wl_shm *shm, int width, int height) +shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies) { + assert(copies >= 1); + tll_foreach(buffers, it) { if (it->item.width != width || it->item.height != height) continue; @@ -57,8 +59,8 @@ shm_get_buffer(struct wl_shm *shm, int width, int height) struct wl_shm_pool *pool = NULL; struct wl_buffer *buf = NULL; - cairo_surface_t *cairo_surface = NULL; - cairo_t *cairo = NULL; + cairo_surface_t **cairo_surface = NULL; + cairo_t **cairo = NULL; /* Backing memory for SHM */ pool_fd = memfd_create("f00sel-wayland-shm-buffer-pool", MFD_CLOEXEC); @@ -99,19 +101,25 @@ shm_get_buffer(struct wl_shm *shm, int width, int height) close(pool_fd); pool_fd = -1; /* Create a cairo surface around the mmapped memory */ - cairo_surface = cairo_image_surface_create_for_data( - mmapped, CAIRO_FORMAT_ARGB32, width, height, stride); - if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) { - LOG_ERR("failed to create cairo surface: %s", - cairo_status_to_string(cairo_surface_status(cairo_surface))); - goto err; - } + cairo_surface = calloc(copies, sizeof(cairo_surface[0])); + cairo = calloc(copies, sizeof(cairo[0])); - cairo = cairo_create(cairo_surface); - if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) { - LOG_ERR("failed to create cairo context: %s", - cairo_status_to_string(cairo_status(cairo))); - goto err; + for (size_t i = 0; i < copies; i++) { + cairo_surface[i] = cairo_image_surface_create_for_data( + mmapped, CAIRO_FORMAT_ARGB32, width, height, stride); + + if (cairo_surface_status(cairo_surface[i]) != CAIRO_STATUS_SUCCESS) { + LOG_ERR("failed to create cairo surface: %s", + cairo_status_to_string(cairo_surface_status(cairo_surface[i]))); + goto err; + } + + cairo[i] = cairo_create(cairo_surface[i]); + if (cairo_status(cairo[i]) != CAIRO_STATUS_SUCCESS) { + LOG_ERR("failed to create cairo context: %s", + cairo_status_to_string(cairo_status(cairo[i]))); + goto err; + } } /* Push to list of available buffers, but marked as 'busy' */ @@ -124,6 +132,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height) .size = size, .mmapped = mmapped, .wl_buf = buf, + .copies = copies, .cairo_surface = cairo_surface, .cairo = cairo} ) @@ -134,10 +143,18 @@ shm_get_buffer(struct wl_shm *shm, int width, int height) return ret; err: - if (cairo != NULL) - cairo_destroy(cairo); - if (cairo_surface != NULL) - cairo_surface_destroy(cairo_surface); + if (cairo != NULL) { + for (size_t i = 0; i < copies; i++) + if (cairo[i] != NULL) + cairo_destroy(cairo[i]); + free(cairo); + } + if (cairo_surface != NULL) { + for (size_t i = 0; i < copies; i++) + if (cairo_surface[i] != NULL) + cairo_surface_destroy(cairo_surface[i]); + free(cairo_surface); + } if (buf != NULL) wl_buffer_destroy(buf); if (pool != NULL) @@ -156,8 +173,18 @@ shm_fini(void) tll_foreach(buffers, it) { struct buffer *buf = &it->item; - cairo_destroy(buf->cairo); - cairo_surface_destroy(buf->cairo_surface); + if (buf->cairo != NULL) { + for (size_t i = 0; i < buf->copies; i++) + if (buf->cairo[i] != NULL) + cairo_destroy(buf->cairo[i]); + free(buf->cairo); + } + if (buf->cairo_surface != NULL) { + for (size_t i = 0; i < buf->copies; i++) + if (buf->cairo_surface[i] != NULL) + cairo_surface_destroy(buf->cairo_surface[i]); + free(buf->cairo_surface); + } wl_buffer_destroy(buf->wl_buf); munmap(buf->mmapped, buf->size); diff --git a/shm.h b/shm.h index 35360db6..9982cb8d 100644 --- a/shm.h +++ b/shm.h @@ -16,9 +16,10 @@ struct buffer { struct wl_buffer *wl_buf; - cairo_surface_t *cairo_surface; - cairo_t *cairo; + size_t copies; + cairo_surface_t **cairo_surface; + cairo_t **cairo; }; -struct buffer *shm_get_buffer(struct wl_shm *shm, int width, int height); +struct buffer *shm_get_buffer(struct wl_shm *shm, int width, int height, size_t copies); void shm_fini(void); diff --git a/terminal.h b/terminal.h index 86b92d17..de359576 100644 --- a/terminal.h +++ b/terminal.h @@ -5,9 +5,11 @@ #include #include +#include #include #include FT_FREETYPE_H +#include FT_LCD_FILTER_H #include #include @@ -211,6 +213,11 @@ struct glyph { cairo_surface_t *surf; int left; int top; + + int format; + int width; + int height; + int stride; }; struct font { @@ -341,6 +348,17 @@ struct terminal { struct { struct wl_callback *frame_callback; + struct { + size_t count; + sem_t start; + sem_t done; + cnd_t cond; + mtx_t lock; + tll(int) queue; + thrd_t *threads; + struct buffer *buf; + } workers; + /* Last rendered cursor position */ struct { struct coord actual; /* Absolute */ From 85ef9df586dfca22d16d04c9777dc38ff896223a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Jul 2019 18:03:03 +0200 Subject: [PATCH 2/4] render: add a 'clean' bit to each cell; only render cells that aren't clean This patch takes a bit from the foreground color value in a cell (todo: split up foreground/background into bitfields with a separate field for 'foreground/background' has been set), and only re-renders cells that aren't marked as clean. Note: we use a 'clean' bit rather than a 'dirty' bit to make it easy to erase cells - we can (keep doing) do that by simply memsetting a cell range to 0. --- csi.c | 25 +++++++++++++++---------- grid.c | 2 ++ main.c | 6 ++++-- render.c | 11 ++++++++--- terminal.c | 24 ++++++++++++++++-------- terminal.h | 7 +++++-- vt.c | 10 ++-------- 7 files changed, 52 insertions(+), 33 deletions(-) diff --git a/csi.c b/csi.c index 5f37663c..a82f03f3 100644 --- a/csi.c +++ b/csi.c @@ -126,7 +126,7 @@ csi_sgr(struct terminal *term) case 35: case 36: case 37: - term->vt.attrs.foreground = 1 << 31 | term->colors.regular[param - 30]; + term->vt.attrs.foreground = 1 << 30 | term->colors.regular[param - 30]; break; case 38: { @@ -141,7 +141,7 @@ csi_sgr(struct terminal *term) color = term->colors.bright[idx - 8]; else color = colors256[idx]; - term->vt.attrs.foreground = 1 << 31 | color; + term->vt.attrs.foreground = 1 << 30 | color; i += 2; } @@ -152,7 +152,7 @@ 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 << 31 | r << 16 | g << 8 | b; + term->vt.attrs.foreground = 1 << 30 | r << 16 | g << 8 | b; i += 4; } @@ -168,7 +168,7 @@ csi_sgr(struct terminal *term) /* 6 - CS tolerance */ /* 7 - color space associated with tolerance */ - term->vt.attrs.foreground = 1 << 31 | r << 16 | g << 8 | b; + term->vt.attrs.foreground = 1 << 30 | r << 16 | g << 8 | b; } else { LOG_ERR("invalid CSI SGR sequence"); abort(); @@ -195,7 +195,7 @@ csi_sgr(struct terminal *term) case 45: case 46: case 47: - term->vt.attrs.background = 1 << 31 | term->colors.regular[param - 40]; + term->vt.attrs.background = 1 << 30 | term->colors.regular[param - 40]; break; case 48: { @@ -211,7 +211,7 @@ csi_sgr(struct terminal *term) color = term->colors.bright[idx - 8]; else color = colors256[idx]; - term->vt.attrs.background = 1 << 31 | color; + term->vt.attrs.background = 1 << 30 | color; i += 2; } @@ -221,7 +221,7 @@ 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 << 31 | r << 16 | g << 8 | b; + term->vt.attrs.background = 1 << 30 | r << 16 | g << 8 | b; i += 4; } @@ -238,7 +238,7 @@ csi_sgr(struct terminal *term) /* 6 - CS tolerance */ /* 7 - color space associated with tolerance */ - term->vt.attrs.background = 1 << 31 | r << 16 | g << 8 | b; + term->vt.attrs.background = 1 << 30 | r << 16 | g << 8 | b; } else { LOG_ERR("invalid CSI SGR sequence"); abort(); @@ -265,7 +265,7 @@ csi_sgr(struct terminal *term) case 95: case 96: case 97: - term->vt.attrs.foreground = 1 << 31 | term->colors.bright[param - 90]; + term->vt.attrs.foreground = 1 << 30 | term->colors.bright[param - 90]; break; /* Regular background colors */ @@ -277,7 +277,7 @@ csi_sgr(struct terminal *term) case 105: case 106: case 107: - term->vt.attrs.background = 1 << 31 | term->colors.bright[param - 100]; + term->vt.attrs.background = 1 << 30 | term->colors.bright[param - 100]; break; default: @@ -487,6 +487,9 @@ csi_dispatch(struct terminal *term, uint8_t final) memmove(&term->grid->cur_row->cells[term->cursor.col], &term->grid->cur_row->cells[term->cursor.col + count], remaining * sizeof(term->grid->cur_row->cells[0])); + + for (size_t c = 0; c < remaining; c++) + term->grid->cur_row->cells[term->cursor.col + c].attrs.clean = 0; term->grid->cur_row->dirty = true; /* Erase the remainder of the line */ @@ -511,6 +514,8 @@ csi_dispatch(struct terminal *term, uint8_t final) memmove(&term->grid->cur_row->cells[term->cursor.col + count], &term->grid->cur_row->cells[term->cursor.col], remaining * sizeof(term->grid->cur_row->cells[0])); + for (size_t c = 0; c < remaining; c++) + term->grid->cur_row->cells[term->cursor.col + count + c].attrs.clean = 0; term->grid->cur_row->dirty = true; /* Erase (insert space characters) */ diff --git a/grid.c b/grid.c index 47e09065..2ff82bd7 100644 --- a/grid.c +++ b/grid.c @@ -29,6 +29,8 @@ grid_row_alloc(int cols) { struct row *row = malloc(sizeof(*row)); row->cells = calloc(cols, sizeof(row->cells[0])); + for (size_t c = 0; c < cols; c++) + row->cells[c].attrs.clean = 1; row->dirty = false; /* TODO: parameter? */ return row; } diff --git a/main.c b/main.c index 0368c687..bdb1de3e 100644 --- a/main.c +++ b/main.c @@ -831,9 +831,11 @@ main(int argc, char *const *argv) for (int r = 0; r < term.rows; r++) { struct row *row = grid_row_in_view(term.grid, r); for (int col = 0; col < term.cols; col++) { - if (row->cells[col].attrs.blink) { + struct cell *cell = &row->cells[col]; + + if (cell->attrs.blink) { + cell->attrs.clean = 0; row->dirty = true; - break; } } } diff --git a/render.c b/render.c index 123e5593..60aa43b4 100644 --- a/render.c +++ b/render.c @@ -174,8 +174,13 @@ arm_blink_timer(struct terminal *term) static void render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx, - const struct cell *cell, int col, int row, bool has_cursor) + struct cell *cell, int col, int row, bool has_cursor) { + if (cell->attrs.clean) + return; + + cell->attrs.clean = 1; + cairo_t *cr = buf->cairo[buf_idx]; double width = term->cell_width; double height = term->cell_height; @@ -185,10 +190,10 @@ render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx, 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 >> 31 + uint32_t _fg = cell->attrs.foreground >> 30 ? cell->attrs.foreground : !term->reverse ? term->colors.fg : term->colors.bg; - uint32_t _bg = cell->attrs.background >> 31 + uint32_t _bg = cell->attrs.background >> 30 ? cell->attrs.background : !term->reverse ? term->colors.bg : term->colors.fg; diff --git a/terminal.c b/terminal.c index 871e6dcb..d6742259 100644 --- a/terminal.c +++ b/terminal.c @@ -20,29 +20,36 @@ void term_damage_rows(struct terminal *term, int start, int end) { assert(start <= end); - for (int r = start; r <= end; r++) - grid_row(term->grid, r)->dirty = true; + for (int r = start; r <= end; r++) { + struct row *row = grid_row(term->grid, r); + row->dirty = true; + for (int c = 0; c < term->grid->num_cols; c++) + row->cells[c].attrs.clean = 0; + } } void term_damage_rows_in_view(struct terminal *term, int start, int end) { assert(start <= end); - for (int r = start; r <= end; r++) - grid_row_in_view(term->grid, r)->dirty = true; + for (int r = start; r <= end; r++) { + struct row *row = grid_row_in_view(term->grid, r); + row->dirty = true; + for (int c = 0; c < term->grid->num_cols; c++) + row->cells[c].attrs.clean = 0; + } } void term_damage_all(struct terminal *term) { - term_damage_rows(term, 0, term->rows); + term_damage_rows(term, 0, term->rows - 1); } void term_damage_view(struct terminal *term) { - for (int i = 0; i < term->rows; i++) - grid_row_in_view(term->grid, i)->dirty = true; + term_damage_rows_in_view(term, 0, term->rows - 1); } void @@ -73,9 +80,10 @@ 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 >> 31)) { + if (unlikely(term->vt.attrs.background >> 30)) { for (int col = start; col <= end; col++) { row->cells[col].c[0] = '\0'; + row->cells[col].attrs.clean = 0; row->cells[col].attrs.background = term->vt.attrs.background; } } else { diff --git a/terminal.h b/terminal.h index de359576..3d2bbf72 100644 --- a/terminal.h +++ b/terminal.h @@ -65,8 +65,11 @@ struct attributes { uint8_t conceal:1; uint8_t reverse:1; - uint32_t foreground; - uint32_t background; + uint32_t clean:1; + uint32_t foreground:31; + + uint32_t reserved:1; + uint32_t background:31; } __attribute__((packed)); struct cell { diff --git a/vt.c b/vt.c index 7ee59acb..399583ea 100644 --- a/vt.c +++ b/vt.c @@ -698,7 +698,6 @@ pre_print(struct terminal *term) static inline void post_print(struct terminal *term) { - term->grid->cur_row->dirty = true; if (term->cursor.col < term->cols - 1) term_cursor_right(term, 1); else @@ -716,13 +715,6 @@ print_insert(struct terminal *term) &row[term->cursor.col + 1], &row[term->cursor.col], term->cols - term->cursor.col - 1); - -#if 0 - term_damage_update( - term, term->cursor.linear + 1, term->cols - term->cursor.col - 1); -#else - row->dirty = true; -#endif } } @@ -737,6 +729,7 @@ action_print_utf8(struct terminal *term) term_damage_update(term, term->cursor.linear, 1); #else row->dirty = true; + cell->attrs.clean = 0; #endif print_insert(term); @@ -761,6 +754,7 @@ action_print(struct terminal *term, uint8_t c) term_damage_update(term, term->cursor.linear, 1); #else row->dirty = true; + cell->attrs.clean = 0; #endif print_insert(term); From 73b4d5d05ab3d3059017a0d3e2a5c798a241da87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Jul 2019 18:04:28 +0200 Subject: [PATCH 3/4] font: add support for fallback fonts A top-level font now has a list of fallback fonts. When a glyph cannot be found, we try each fallback font in turn, until we either find one that has the glyph, or until we've exhausted the list. To make this actually work in practise (read: to make performance acceptable), the cache is re-worked and is now populated on demand. It also supports non-ASCII characters, by using the 4-byte unicode character as index instead. Since having an array that can be indexed by a 4-byte value isn't really viable, we now have a simple hash table instead of an array. --- config.c | 14 +++- config.h | 3 +- font.c | 221 +++++++++++++++++++++++++++++++++++++++++++++-------- font.h | 55 ++++++++++++- main.c | 28 +++---- render.c | 50 ++++-------- terminal.h | 36 +-------- 7 files changed, 280 insertions(+), 127 deletions(-) diff --git a/config.c b/config.c index dda3c635..08156d16 100644 --- a/config.c +++ b/config.c @@ -147,8 +147,12 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "font") == 0) { - free(conf->font); - conf->font = strdup(value); + //free(conf->font); + //conf->font = strdup(value); + char *copy = strdup(value); + for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) + tll_push_back(conf->fonts, strdup(font)); + free(copy); } else if (strcmp(key, "workers") == 0) { @@ -404,7 +408,7 @@ config_load(struct config *conf) *conf = (struct config) { .term = strdup("foot"), .shell = get_shell(), - .font = strdup("monospace"), + .fonts = tll_init(), .colors = { .fg = default_foreground, @@ -462,6 +466,7 @@ config_load(struct config *conf) fclose(f); out: + tll_push_back(conf->fonts, strdup("monospace")); free(path); return ret; } @@ -471,5 +476,6 @@ config_free(struct config conf) { free(conf.term); free(conf.shell); - free(conf.font); + //free(conf.font); + tll_free_and_free(conf.fonts, free); } diff --git a/config.h b/config.h index e965f142..0686127e 100644 --- a/config.h +++ b/config.h @@ -4,11 +4,12 @@ #include #include "terminal.h" +#include "tllist.h" struct config { char *term; char *shell; - char *font; + tll(char *) fonts; struct { uint32_t fg; diff --git a/font.c b/font.c index c6a118c9..6842a439 100644 --- a/font.c +++ b/font.c @@ -1,33 +1,42 @@ #include "font.h" #include +#include #include #include #include +#include #include #define LOG_MODULE "font" +#define LOG_ENABLE_DBG 0 #include "log.h" #define min(x, y) ((x) < (y) ? (x) : (y)) static FT_Library ft_lib; +static mtx_t ft_lock; + +static const size_t cache_size = 512; static void __attribute__((constructor)) init(void) { FcInit(); FT_Init_FreeType(&ft_lib); + mtx_init(&ft_lock, mtx_plain); } static void __attribute__((destructor)) fini(void) { - FcFini(); + mtx_destroy(&ft_lock); FT_Done_FreeType(ft_lib); + FcFini(); } +#if 0 static void font_populate_glyph_cache(struct font *font) { @@ -35,12 +44,25 @@ font_populate_glyph_cache(struct font *font) for (size_t i = 0; i < 256; i++) font_glyph_for_utf8(font, &(char){i}, &font->cache[i]); } +#endif -bool -font_from_name(const char *name, struct font *font) +static bool +from_name(const char *base_name, const font_list_t *fallbacks, const char *attributes, struct font *font, bool is_fallback) { memset(font, 0, sizeof(*font)); + size_t attr_len = attributes == NULL ? 0 : strlen(attributes); + bool have_attrs = attr_len > 0; + + char name[strlen(base_name) + (have_attrs ? 1 : 0) + attr_len + 1]; + strcpy(name, base_name); + if (have_attrs){ + strcat(name, ":"); + strcat(name, attributes); + } + + LOG_DBG("instantiating %s", name); + FcPattern *pattern = FcNameParse((const unsigned char *)name); if (pattern == NULL) { LOG_ERR("%s: failed to lookup font", name); @@ -86,8 +108,10 @@ font_from_name(const char *name, struct font *font) LOG_DBG("loading: %s", face_file); + mtx_lock(&ft_lock); FT_Face ft_face; FT_Error ft_err = FT_New_Face(ft_lib, (const char *)face_file, 0, &ft_face); + mtx_unlock(&ft_lock); if (ft_err != 0) LOG_ERR("%s: failed to create FreeType face", face_file); @@ -122,11 +146,13 @@ font_from_name(const char *name, struct font *font) else if (fc_hinting && fc_hintstyle == FC_HINT_SLIGHT) load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT; else if (fc_rgba == FC_RGBA_RGB) { - LOG_WARN("unimplemented: subpixel antialiasing"); + if (!is_fallback) + LOG_WARN("unimplemented: subpixel antialiasing"); // load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD; load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL; } else if (fc_rgba == FC_RGBA_VRGB) { - LOG_WARN("unimplemented: subpixel antialiasing"); + if (!is_fallback) + LOG_WARN("unimplemented: subpixel antialiasing"); //load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD_V; load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL; } else @@ -144,15 +170,17 @@ font_from_name(const char *name, struct font *font) if (!fc_antialias) render_flags |= FT_RENDER_MODE_MONO; else { - if (false) - ; -#if 0 - if (fc_rgba == FC_RGBA_RGB) - render_flags |= FT_RENDER_MODE_LCD; - else if (fc_rgba == FC_RGBA_VRGB) - render_flags |= FT_RENDER_MODE_LCD_V; -#endif - else + if (fc_rgba == FC_RGBA_RGB) { + if (!is_fallback) + LOG_WARN("unimplemented: subpixel antialiasing"); + //render_flags |= FT_RENDER_MODE_LCD; + render_flags |= FT_RENDER_MODE_NORMAL; + } else if (fc_rgba == FC_RGBA_VRGB) { + if (!is_fallback) + LOG_WARN("unimplemented: subpixel antialiasing"); + //render_flags |= FT_RENDER_MODE_LCD_V; + render_flags |= FT_RENDER_MODE_NORMAL; + } else render_flags |= FT_RENDER_MODE_NORMAL; } @@ -173,25 +201,64 @@ font_from_name(const char *name, struct font *font) font->face = ft_face; font->load_flags = load_flags; font->render_flags = render_flags; - font_populate_glyph_cache(font); + font->is_fallback = is_fallback; + + if (fallbacks != NULL) { + tll_foreach(*fallbacks, it) { + size_t len = strlen(it->item) + (have_attrs ? 1 : 0) + attr_len + 1; + char *fallback = malloc(len); + + strcpy(fallback, it->item); + if (have_attrs) { + strcat(fallback, ":"); + strcat(fallback, attributes); + } + + LOG_DBG("%s: adding fallback: %s", name, fallback); + tll_push_back(font->fallbacks, fallback); + } + } + + if (is_fallback) + return true; + + //font_populate_glyph_cache(font); + font->cache = calloc(cache_size, sizeof(font->cache[0])); return true; } bool -font_glyph_for_utf8(struct font *font, const char *utf8, - struct glyph *glyph) +font_from_name(font_list_t names, const char *attributes, struct font *font) { - mbstate_t ps = {0}; - wchar_t wc; - if (mbrtowc(&wc, utf8, 4, &ps) < 0) { - LOG_ERR("FAILED: %.4s", utf8); + if (tll_length(names) == 0) return false; + + font_list_t fallbacks = tll_init(); + bool skip_first = true; + tll_foreach(names, it) { + if (skip_first) { + skip_first = false; + continue; + } + + tll_push_back(fallbacks, it->item); } - wprintf(L"CONVERTED: %.1s\n", &wc); + bool ret = from_name(tll_front(names), &fallbacks, attributes, font, false); - mtx_lock(&font->lock); + tll_free(fallbacks); + return ret; +} +static size_t +hash_index(wchar_t wc) +{ + return wc % cache_size; +} + +static bool +glyph_for_wchar(struct font *font, wchar_t wc, struct glyph *glyph) +{ /* * LCD filter is per library instance. Thus we need to re-set it * every time... @@ -204,6 +271,28 @@ font_glyph_for_utf8(struct font *font, const char *utf8, goto err; FT_UInt idx = FT_Get_Char_Index(font->face, wc); + if (idx == 0) { + /* LOG_DBG("no glyph found for %02x %02x %02x %02x", */ + /* (unsigned char)utf8[0], (unsigned char)utf8[1], */ + /* (unsigned char)utf8[2], (unsigned char)utf8[3]); */ + + /* Try fallback fonts */ + tll_foreach(font->fallbacks, it) { + struct font fallback; + if (from_name(it->item, NULL, "", &fallback, true)) { + if (glyph_for_wchar(&fallback, wc, glyph)) { + font_destroy(&fallback); + return true; + } + + font_destroy(&fallback); + } + } + + if (font->is_fallback) + return false; + } + err = FT_Load_Glyph(font->face, idx, font->load_flags); if (err != 0) { LOG_ERR("load failed"); @@ -267,36 +356,102 @@ font_glyph_for_utf8(struct font *font, const char *utf8, } *glyph = (struct glyph){ + .wc = wc, .data = data, .surf = surf, .left = font->face->glyph->bitmap_left, .top = font->face->glyph->bitmap_top, - - .format = cr_format, - .width = bitmap->width, - .height = bitmap->rows, - .stride = stride, }; - mtx_unlock(&font->lock); + return true; err: - mtx_unlock(&font->lock); return false; } +const struct glyph * +font_glyph_for_utf8(struct font *font, const char *utf8) +{ + 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]; + + if (hash_entry != NULL) { + tll_foreach(*hash_entry, it) { + if (it->item.wc == wc) { + mtx_unlock(&font->lock); + return &it->item; + } + } + } + + struct glyph glyph; + if (!glyph_for_wchar(font, wc, &glyph)) { + mtx_unlock(&font->lock); + return NULL; + } + + if (hash_entry == NULL) { + hash_entry = calloc(1, sizeof(*hash_entry)); + + assert(font->cache[hash_idx] == NULL); + font->cache[hash_idx] = hash_entry; + } + + assert(hash_entry != NULL); + tll_push_back(*hash_entry, glyph); + + mtx_unlock(&font->lock); + return &tll_back(*hash_entry); +} + void font_destroy(struct font *font) { - if (font->face != NULL) - FT_Done_Face(font->face); + tll_free_and_free(font->fallbacks, free); + if (font->face != NULL) { + mtx_lock(&ft_lock); + FT_Done_Face(font->face); + mtx_unlock(&ft_lock); + } + + if (font->cache != NULL) { + for (size_t i = 0; i < cache_size; i++) { + if (font->cache[i] == NULL) + continue; + + tll_foreach(*font->cache[i], it) { + cairo_surface_destroy(it->item.surf); + free(it->item.data); + } + + tll_free(*font->cache[i]); + free(font->cache[i]); + } + free(font->cache); + } + +#if 0 for (size_t i = 0; i < 256; i++) { if (font->cache[i].surf != NULL) cairo_surface_destroy(font->cache[i].surf); if (font->cache[i].data != NULL) free(font->cache[i].data); } - +#endif + mtx_destroy(&font->lock); } diff --git a/font.h b/font.h index 192640a9..0f2c9833 100644 --- a/font.h +++ b/font.h @@ -3,9 +3,56 @@ #include #include -#include "terminal.h" +#include +#include FT_FREETYPE_H +#include FT_LCD_FILTER_H +#include -bool font_from_name(const char *name, struct font *result); -bool font_glyph_for_utf8( - struct font *font, const char *utf8, struct glyph *glyph); +#include "tllist.h" +//#include "terminal.h" + +typedef tll(const char *) font_list_t; + +struct glyph { + wchar_t wc; + + void *data; + cairo_surface_t *surf; + int left; + int top; + +#if 0 + int format; + int width; + int height; + int stride; +#endif +}; + +typedef tll(struct glyph) hash_entry_t; + +struct font { + FT_Face face; + int load_flags; + int render_flags; + FT_LcdFilter lcd_filter; + struct { + double position; + double thickness; + } underline; + struct { + double position; + double thickness; + } strikeout; + + bool is_fallback; + tll(char *) fallbacks; + + //struct glyph cache[256]; + hash_entry_t **cache; + mtx_t lock; +}; + +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); void font_destroy(struct font *font); diff --git a/main.c b/main.c index bdb1de3e..cc2fd054 100644 --- a/main.c +++ b/main.c @@ -291,8 +291,8 @@ main(int argc, char *const *argv) break; case 'f': - free(conf.font); - conf.font = strdup(optarg); + tll_free_and_free(conf.fonts, free); + tll_push_back(conf.fonts, strdup(optarg)); break; case 'h': @@ -431,21 +431,21 @@ main(int argc, char *const *argv) thrd_create(&term.render.workers.threads[i], &render_worker_thread, &worker_context[i]); } - if (!font_from_name(conf.font, &term.fonts[0])) + font_list_t font_names = tll_init(); + tll_foreach(conf.fonts, it) + tll_push_back(font_names, it->item); + + if (!font_from_name(font_names, "", &term.fonts[0])) { + tll_free(font_names); goto out; - - { - char fname[1024]; - snprintf(fname, sizeof(fname), "%s:style=bold", conf.font); - font_from_name(fname, &term.fonts[1]); - - snprintf(fname, sizeof(fname), "%s:style=italic", conf.font); - font_from_name(fname, &term.fonts[2]); - - snprintf(fname, sizeof(fname), "%s:style=bold italic", conf.font); - font_from_name(fname, &term.fonts[3]); } + font_from_name(font_names, "style=bold", &term.fonts[1]); + font_from_name(font_names, "style=italic", &term.fonts[2]); + font_from_name(font_names, "style=bold italic", &term.fonts[3]); + + tll_free(font_names); + /* Underline position and size */ for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) { struct font *f = &term.fonts[i]; diff --git a/render.c b/render.c index 60aa43b4..b2d6c16e 100644 --- a/render.c +++ b/render.c @@ -273,38 +273,12 @@ render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx, } struct font *font = attrs_to_font(term, &cell->attrs); - - struct glyph *glyph = NULL; - if (strnlen(cell->c, 4) == 1) { - if (font->cache[(unsigned char)cell->c[0]].surf != NULL) - glyph = &font->cache[(unsigned char)cell->c[0]]; - } - - struct glyph _glyph; - if (glyph == NULL) { - if (!font_glyph_for_utf8(font, cell->c, &_glyph)) { - LOG_ERR("FAILED: %.4s", cell->c); - return; - } - glyph = &_glyph; - } - - assert(glyph != NULL); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); - -#if 0 - cairo_surface_t *surf = cairo_image_surface_create_for_data( - glyph->data, glyph->format, glyph->width, glyph->height, glyph->stride); - cairo_mask_surface(cr, surf, x + glyph->left, y + term->fextents.ascent - glyph->top); - cairo_surface_destroy(surf); -#else - cairo_mask_surface(cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); -#endif - - if (glyph == &_glyph) { - cairo_surface_destroy(_glyph.surf); - free(_glyph.data); + const struct glyph *glyph = font_glyph_for_utf8(font, cell->c); + if (glyph != NULL) { + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgb(cr, fg.r, fg.g, fg.b); + cairo_mask_surface( + cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top); } } @@ -456,9 +430,12 @@ grid_render(struct terminal *term) /* Erase old cursor (if we rendered a cursor last time) */ if (term->render.last_cursor.cell != NULL) { + struct cell *hack = (struct cell *)term->render.last_cursor.cell; + hack->attrs.clean = 0; render_cell( term, buf, 0, - term->render.last_cursor.cell, + //term->render.last_cursor.cell, + hack, term->render.last_cursor.in_view.col, term->render.last_cursor.in_view.row, false); @@ -622,11 +599,12 @@ grid_render(struct terminal *term) term->cursor.col, view_aligned_row}; struct row *row = grid_row_in_view(term->grid, view_aligned_row); + struct cell *cell = &row->cells[term->cursor.col]; - term->render.last_cursor.cell = &row->cells[term->cursor.col]; + cell->attrs.clean = 0; + term->render.last_cursor.cell = cell; render_cell( - term, buf, 0, term->render.last_cursor.cell, - term->cursor.col, view_aligned_row, true); + term, buf, 0, cell, term->cursor.col, view_aligned_row, true); wl_surface_damage_buffer( term->wl.surface, diff --git a/terminal.h b/terminal.h index 3d2bbf72..a49f7bd5 100644 --- a/terminal.h +++ b/terminal.h @@ -7,17 +7,13 @@ #include #include -#include -#include FT_FREETYPE_H -#include FT_LCD_FILTER_H - #include #include #include #include #include - +#include "font.h" #include "tllist.h" #define likely(c) __builtin_expect(!!(c), 1) @@ -211,36 +207,6 @@ struct primary { uint32_t serial; }; -struct glyph { - void *data; - cairo_surface_t *surf; - int left; - int top; - - int format; - int width; - int height; - int stride; -}; - -struct font { - FT_Face face; - int load_flags; - int render_flags; - FT_LcdFilter lcd_filter; - struct { - double position; - double thickness; - } underline; - struct { - double position; - double thickness; - } strikeout; - - struct glyph cache[256]; - mtx_t lock; -}; - enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR }; struct terminal { From 7d702756ad10dff096d33ebfffefca78f2a72f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 30 Jul 2019 18:07:44 +0200 Subject: [PATCH 4/4] main: pty buffers appear to be ~20KB on modern linux kernels --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index cc2fd054..3228e8f8 100644 --- a/main.c +++ b/main.c @@ -738,7 +738,7 @@ main(int argc, char *const *argv) } if (fds[1].revents & POLLIN) { - uint8_t data[8192]; + uint8_t data[24 * 1024]; ssize_t count = read(term.ptmx, data, sizeof(data)); if (count < 0) { if (errno != EAGAIN)