mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
wip: initial multithreaded renderer
This commit is contained in:
parent
d1b88f67e4
commit
c531795b83
8 changed files with 287 additions and 89 deletions
31
config.c
31
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();
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -24,6 +24,8 @@ struct config {
|
|||
uint32_t cursor;
|
||||
} color;
|
||||
} cursor;
|
||||
|
||||
size_t render_worker_count;
|
||||
};
|
||||
|
||||
bool config_load(struct config *conf);
|
||||
|
|
|
|||
44
main.c
44
main.c
|
|
@ -11,9 +11,9 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include <freetype/tttables.h>
|
||||
#include <cairo-ft.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include <xdg-shell.h>
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
199
render.c
199
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);
|
||||
|
|
|
|||
6
render.h
6
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);
|
||||
|
|
|
|||
69
shm.c
69
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);
|
||||
|
||||
|
|
|
|||
7
shm.h
7
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);
|
||||
|
|
|
|||
18
terminal.h
18
terminal.h
|
|
@ -5,9 +5,11 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <threads.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_LCD_FILTER_H
|
||||
|
||||
#include <cairo.h>
|
||||
#include <wayland-client.h>
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue