Merge branch 'multithreaded-renderer'

This commit is contained in:
Daniel Eklöf 2019-07-30 18:08:27 +02:00
commit f39d848368
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
14 changed files with 598 additions and 228 deletions

View file

@ -107,26 +107,28 @@ get_config_path(void)
} }
static bool 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) if (s == NULL)
return false; return false;
errno = 0; errno = 0;
char *end = NULL; 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); LOG_ERRNO("%s:%d: invalid color: %s", path, lineno, s);
return false; return false;
} }
if (*end != '\0') { *color = value & 0xffffff;
LOG_ERR("%s:%d: invalid color: %s", path, lineno, s);
return false;
}
*color = res & 0xffffff;
return true; return true;
} }
@ -145,8 +147,21 @@ parse_section_main(const char *key, const char *value, struct config *conf,
} }
else if (strcmp(key, "font") == 0) { else if (strcmp(key, "font") == 0) {
free(conf->font); //free(conf->font);
conf->font = strdup(value); //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) {
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 { else {
@ -393,7 +408,7 @@ config_load(struct config *conf)
*conf = (struct config) { *conf = (struct config) {
.term = strdup("foot"), .term = strdup("foot"),
.shell = get_shell(), .shell = get_shell(),
.font = strdup("monospace"), .fonts = tll_init(),
.colors = { .colors = {
.fg = default_foreground, .fg = default_foreground,
@ -427,6 +442,8 @@ config_load(struct config *conf)
.cursor = 0, .cursor = 0,
}, },
}, },
.render_worker_count = sysconf(_SC_NPROCESSORS_ONLN),
}; };
char *path = get_config_path(); char *path = get_config_path();
@ -449,6 +466,7 @@ config_load(struct config *conf)
fclose(f); fclose(f);
out: out:
tll_push_back(conf->fonts, strdup("monospace"));
free(path); free(path);
return ret; return ret;
} }
@ -458,5 +476,6 @@ config_free(struct config conf)
{ {
free(conf.term); free(conf.term);
free(conf.shell); free(conf.shell);
free(conf.font); //free(conf.font);
tll_free_and_free(conf.fonts, free);
} }

View file

@ -4,11 +4,12 @@
#include <stdbool.h> #include <stdbool.h>
#include "terminal.h" #include "terminal.h"
#include "tllist.h"
struct config { struct config {
char *term; char *term;
char *shell; char *shell;
char *font; tll(char *) fonts;
struct { struct {
uint32_t fg; uint32_t fg;
@ -24,6 +25,8 @@ struct config {
uint32_t cursor; uint32_t cursor;
} color; } color;
} cursor; } cursor;
size_t render_worker_count;
}; };
bool config_load(struct config *conf); bool config_load(struct config *conf);

25
csi.c
View file

@ -126,7 +126,7 @@ csi_sgr(struct terminal *term)
case 35: case 35:
case 36: case 36:
case 37: 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; break;
case 38: { case 38: {
@ -141,7 +141,7 @@ csi_sgr(struct terminal *term)
color = term->colors.bright[idx - 8]; color = term->colors.bright[idx - 8];
else else
color = colors256[idx]; color = colors256[idx];
term->vt.attrs.foreground = 1 << 31 | color; term->vt.attrs.foreground = 1 << 30 | color;
i += 2; i += 2;
} }
@ -152,7 +152,7 @@ csi_sgr(struct terminal *term)
uint8_t r = term->vt.params.v[i + 2].value; uint8_t r = term->vt.params.v[i + 2].value;
uint8_t g = term->vt.params.v[i + 3].value; uint8_t g = term->vt.params.v[i + 3].value;
uint8_t b = term->vt.params.v[i + 4].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; i += 4;
} }
@ -168,7 +168,7 @@ csi_sgr(struct terminal *term)
/* 6 - CS tolerance */ /* 6 - CS tolerance */
/* 7 - color space associated with 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 { } else {
LOG_ERR("invalid CSI SGR sequence"); LOG_ERR("invalid CSI SGR sequence");
abort(); abort();
@ -195,7 +195,7 @@ csi_sgr(struct terminal *term)
case 45: case 45:
case 46: case 46:
case 47: 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; break;
case 48: { case 48: {
@ -211,7 +211,7 @@ csi_sgr(struct terminal *term)
color = term->colors.bright[idx - 8]; color = term->colors.bright[idx - 8];
else else
color = colors256[idx]; color = colors256[idx];
term->vt.attrs.background = 1 << 31 | color; term->vt.attrs.background = 1 << 30 | color;
i += 2; i += 2;
} }
@ -221,7 +221,7 @@ csi_sgr(struct terminal *term)
uint8_t r = term->vt.params.v[i + 2].value; uint8_t r = term->vt.params.v[i + 2].value;
uint8_t g = term->vt.params.v[i + 3].value; uint8_t g = term->vt.params.v[i + 3].value;
uint8_t b = term->vt.params.v[i + 4].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; i += 4;
} }
@ -238,7 +238,7 @@ csi_sgr(struct terminal *term)
/* 6 - CS tolerance */ /* 6 - CS tolerance */
/* 7 - color space associated with 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 { } else {
LOG_ERR("invalid CSI SGR sequence"); LOG_ERR("invalid CSI SGR sequence");
abort(); abort();
@ -265,7 +265,7 @@ csi_sgr(struct terminal *term)
case 95: case 95:
case 96: case 96:
case 97: 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; break;
/* Regular background colors */ /* Regular background colors */
@ -277,7 +277,7 @@ csi_sgr(struct terminal *term)
case 105: case 105:
case 106: case 106:
case 107: 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; break;
default: default:
@ -487,6 +487,9 @@ csi_dispatch(struct terminal *term, uint8_t final)
memmove(&term->grid->cur_row->cells[term->cursor.col], memmove(&term->grid->cur_row->cells[term->cursor.col],
&term->grid->cur_row->cells[term->cursor.col + count], &term->grid->cur_row->cells[term->cursor.col + count],
remaining * sizeof(term->grid->cur_row->cells[0])); 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; term->grid->cur_row->dirty = true;
/* Erase the remainder of the line */ /* 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], memmove(&term->grid->cur_row->cells[term->cursor.col + count],
&term->grid->cur_row->cells[term->cursor.col], &term->grid->cur_row->cells[term->cursor.col],
remaining * sizeof(term->grid->cur_row->cells[0])); 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; term->grid->cur_row->dirty = true;
/* Erase (insert space characters) */ /* Erase (insert space characters) */

221
font.c
View file

@ -1,33 +1,42 @@
#include "font.h" #include "font.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <wchar.h> #include <wchar.h>
#include <assert.h> #include <assert.h>
#include <threads.h>
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#define LOG_MODULE "font" #define LOG_MODULE "font"
#define LOG_ENABLE_DBG 0
#include "log.h" #include "log.h"
#define min(x, y) ((x) < (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y))
static FT_Library ft_lib; static FT_Library ft_lib;
static mtx_t ft_lock;
static const size_t cache_size = 512;
static void __attribute__((constructor)) static void __attribute__((constructor))
init(void) init(void)
{ {
FcInit(); FcInit();
FT_Init_FreeType(&ft_lib); FT_Init_FreeType(&ft_lib);
mtx_init(&ft_lock, mtx_plain);
} }
static void __attribute__((destructor)) static void __attribute__((destructor))
fini(void) fini(void)
{ {
FcFini(); mtx_destroy(&ft_lock);
FT_Done_FreeType(ft_lib); FT_Done_FreeType(ft_lib);
FcFini();
} }
#if 0
static void static void
font_populate_glyph_cache(struct font *font) 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++) for (size_t i = 0; i < 256; i++)
font_glyph_for_utf8(font, &(char){i}, &font->cache[i]); font_glyph_for_utf8(font, &(char){i}, &font->cache[i]);
} }
#endif
bool static bool
font_from_name(const char *name, struct font *font) 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)); 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); FcPattern *pattern = FcNameParse((const unsigned char *)name);
if (pattern == NULL) { if (pattern == NULL) {
LOG_ERR("%s: failed to lookup font", name); 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); LOG_DBG("loading: %s", face_file);
mtx_lock(&ft_lock);
FT_Face ft_face; FT_Face ft_face;
FT_Error ft_err = FT_New_Face(ft_lib, (const char *)face_file, 0, &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) if (ft_err != 0)
LOG_ERR("%s: failed to create FreeType face", face_file); 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) else if (fc_hinting && fc_hintstyle == FC_HINT_SLIGHT)
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT; load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT;
else if (fc_rgba == FC_RGBA_RGB) { 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_LCD;
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL; load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
} else if (fc_rgba == FC_RGBA_VRGB) { } 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_LCD_V;
load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL; load_flags |= FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
} else } else
@ -144,15 +170,17 @@ font_from_name(const char *name, struct font *font)
if (!fc_antialias) if (!fc_antialias)
render_flags |= FT_RENDER_MODE_MONO; render_flags |= FT_RENDER_MODE_MONO;
else { else {
if (false) if (fc_rgba == FC_RGBA_RGB) {
; if (!is_fallback)
#if 0 LOG_WARN("unimplemented: subpixel antialiasing");
if (fc_rgba == FC_RGBA_RGB) //render_flags |= FT_RENDER_MODE_LCD;
render_flags |= FT_RENDER_MODE_LCD; render_flags |= FT_RENDER_MODE_NORMAL;
else if (fc_rgba == FC_RGBA_VRGB) } else if (fc_rgba == FC_RGBA_VRGB) {
render_flags |= FT_RENDER_MODE_LCD_V; if (!is_fallback)
#endif LOG_WARN("unimplemented: subpixel antialiasing");
else //render_flags |= FT_RENDER_MODE_LCD_V;
render_flags |= FT_RENDER_MODE_NORMAL;
} else
render_flags |= FT_RENDER_MODE_NORMAL; render_flags |= FT_RENDER_MODE_NORMAL;
} }
@ -173,25 +201,64 @@ font_from_name(const char *name, struct font *font)
font->face = ft_face; font->face = ft_face;
font->load_flags = load_flags; font->load_flags = load_flags;
font->render_flags = render_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; return true;
} }
bool bool
font_glyph_for_utf8(struct font *font, const char *utf8, font_from_name(font_list_t names, const char *attributes, struct font *font)
struct glyph *glyph)
{ {
mbstate_t ps = {0}; if (tll_length(names) == 0)
wchar_t wc;
if (mbrtowc(&wc, utf8, 4, &ps) < 0) {
LOG_ERR("FAILED: %.4s", utf8);
return false; 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 * LCD filter is per library instance. Thus we need to re-set it
* every time... * every time...
@ -204,6 +271,28 @@ font_glyph_for_utf8(struct font *font, const char *utf8,
goto err; goto err;
FT_UInt idx = FT_Get_Char_Index(font->face, wc); 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); err = FT_Load_Glyph(font->face, idx, font->load_flags);
if (err != 0) { if (err != 0) {
LOG_ERR("load failed"); LOG_ERR("load failed");
@ -267,36 +356,102 @@ font_glyph_for_utf8(struct font *font, const char *utf8,
} }
*glyph = (struct glyph){ *glyph = (struct glyph){
.wc = wc,
.data = data, .data = data,
.surf = surf, .surf = surf,
.left = font->face->glyph->bitmap_left, .left = font->face->glyph->bitmap_left,
.top = font->face->glyph->bitmap_top, .top = font->face->glyph->bitmap_top,
.format = cr_format,
.width = bitmap->width,
.height = bitmap->rows,
.stride = stride,
}; };
mtx_unlock(&font->lock);
return true; return true;
err: err:
mtx_unlock(&font->lock);
return false; 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 void
font_destroy(struct font *font) font_destroy(struct font *font)
{ {
if (font->face != NULL) tll_free_and_free(font->fallbacks, free);
FT_Done_Face(font->face);
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++) { for (size_t i = 0; i < 256; i++) {
if (font->cache[i].surf != NULL) if (font->cache[i].surf != NULL)
cairo_surface_destroy(font->cache[i].surf); cairo_surface_destroy(font->cache[i].surf);
if (font->cache[i].data != NULL) if (font->cache[i].data != NULL)
free(font->cache[i].data); free(font->cache[i].data);
} }
#endif
mtx_destroy(&font->lock); mtx_destroy(&font->lock);
} }

55
font.h
View file

@ -3,9 +3,56 @@
#include <stdbool.h> #include <stdbool.h>
#include <threads.h> #include <threads.h>
#include "terminal.h" #include <ft2build.h>
#include FT_FREETYPE_H
#include FT_LCD_FILTER_H
#include <cairo.h>
bool font_from_name(const char *name, struct font *result); #include "tllist.h"
bool font_glyph_for_utf8( //#include "terminal.h"
struct font *font, const char *utf8, struct glyph *glyph);
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); void font_destroy(struct font *font);

2
grid.c
View file

@ -29,6 +29,8 @@ grid_row_alloc(int cols)
{ {
struct row *row = malloc(sizeof(*row)); struct row *row = malloc(sizeof(*row));
row->cells = calloc(cols, sizeof(row->cells[0])); 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? */ row->dirty = false; /* TODO: parameter? */
return row; return row;
} }

80
main.c
View file

@ -11,9 +11,9 @@
#include <errno.h> #include <errno.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
#include <sys/sysinfo.h>
#include <freetype/tttables.h> #include <freetype/tttables.h>
#include <cairo-ft.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <wayland-cursor.h> #include <wayland-cursor.h>
#include <xdg-shell.h> #include <xdg-shell.h>
@ -291,8 +291,8 @@ main(int argc, char *const *argv)
break; break;
case 'f': case 'f':
free(conf.font); tll_free_and_free(conf.fonts, free);
conf.font = strdup(optarg); tll_push_back(conf.fonts, strdup(optarg));
break; break;
case 'h': case 'h':
@ -388,8 +388,18 @@ main(int argc, char *const *argv)
.normal = {.damage = tll_init(), .scroll_damage = tll_init()}, .normal = {.damage = tll_init(), .scroll_damage = tll_init()},
.alt = {.damage = tll_init(), .scroll_damage = tll_init()}, .alt = {.damage = tll_init(), .scroll_damage = tll_init()},
.grid = &term.normal, .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 */ /* Initialize 'current' colors from the default colors */
term.colors.fg = term.colors.default_fg; term.colors.fg = term.colors.default_fg;
term.colors.bg = term.colors.default_bg; term.colors.bg = term.colors.default_bg;
@ -409,21 +419,33 @@ main(int argc, char *const *argv)
thrd_t keyboard_repeater_id; thrd_t keyboard_repeater_id;
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term); thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term);
if (!font_from_name(conf.font, &term.fonts[0])) sem_init(&term.render.workers.start, 0, 0);
goto out; 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]));
char fname[1024]; for (size_t i = 0; i < term.render.workers.count; i++) {
snprintf(fname, sizeof(fname), "%s:style=bold", conf.font); worker_context[i].term = &term;
font_from_name(fname, &term.fonts[1]); worker_context[i].my_id = 1 + i;
thrd_create(&term.render.workers.threads[i], &render_worker_thread, &worker_context[i]);
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_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;
}
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 */ /* Underline position and size */
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) { for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) {
struct font *f = &term.fonts[i]; struct font *f = &term.fonts[i];
@ -716,7 +738,7 @@ main(int argc, char *const *argv)
} }
if (fds[1].revents & POLLIN) { if (fds[1].revents & POLLIN) {
uint8_t data[8192]; uint8_t data[24 * 1024];
ssize_t count = read(term.ptmx, data, sizeof(data)); ssize_t count = read(term.ptmx, data, sizeof(data));
if (count < 0) { if (count < 0) {
if (errno != EAGAIN) if (errno != EAGAIN)
@ -809,9 +831,11 @@ main(int argc, char *const *argv)
for (int r = 0; r < term.rows; r++) { for (int r = 0; r < term.rows; r++) {
struct row *row = grid_row_in_view(term.grid, r); struct row *row = grid_row_in_view(term.grid, r);
for (int col = 0; col < term.cols; col++) { 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; row->dirty = true;
break;
} }
} }
} }
@ -827,6 +851,15 @@ out:
cnd_signal(&term.kbd.repeat.cond); cnd_signal(&term.kbd.repeat.cond);
mtx_unlock(&term.kbd.repeat.mutex); 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(); shm_fini();
if (term.render.frame_callback != NULL) if (term.render.frame_callback != NULL)
wl_callback_destroy(term.render.frame_callback); wl_callback_destroy(term.render.frame_callback);
@ -910,6 +943,17 @@ out:
thrd_join(keyboard_repeater_id, NULL); thrd_join(keyboard_repeater_id, NULL);
cnd_destroy(&term.kbd.repeat.cond); cnd_destroy(&term.kbd.repeat.cond);
mtx_destroy(&term.kbd.repeat.mutex); 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_read_fd);
close(term.kbd.repeat.pipe_write_fd); close(term.kbd.repeat.pipe_write_fd);

228
render.c
View file

@ -76,45 +76,52 @@ gseq_flush(struct terminal *term, struct buffer *buf)
} }
static void 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, struct rgb color, double x, double y)
{ {
//const struct font *font = attrs_to_font(term, &cell->attrs); //const struct font *font = attrs_to_font(term, &cell->attrs);
double baseline = y + term->fextents.height - term->fextents.descent; double baseline = y + term->fextents.height - term->fextents.descent;
double width = font->underline.thickness; double width = font->underline.thickness;
double y_under = baseline - font->underline.position - width / 2.; 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_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_line_width(buf->cairo, width); cairo_set_source_rgb(cr, color.r, color.g, color.b);
cairo_move_to(buf->cairo, x, round(y_under) + 0.5); cairo_set_line_width(cr, width);
cairo_rel_line_to(buf->cairo, term->cell_width, 0); cairo_move_to(cr, x, round(y_under) + 0.5);
cairo_stroke(buf->cairo); cairo_rel_line_to(cr, term->cell_width, 0);
cairo_stroke(cr);
} }
static void static void
draw_bar(const struct terminal *term, struct buffer *buf, struct rgb color, draw_bar(const struct terminal *term, struct buffer *buf, size_t buf_idx,
double x, double y) struct rgb color, double x, double y)
{ {
cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b); cairo_t *cr = buf->cairo[buf_idx];
cairo_set_line_width(buf->cairo, 1.0);
cairo_move_to(buf->cairo, x + 0.5, y); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_rel_line_to(buf->cairo, 0, term->cell_height); cairo_set_source_rgb(cr, color.r, color.g, color.b);
cairo_stroke(buf->cairo); 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 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) const struct font *font, struct rgb color, double x, double y)
{ {
double baseline = y + term->fextents.height - term->fextents.descent; double baseline = y + term->fextents.height - term->fextents.descent;
double width = font->strikeout.thickness; double width = font->strikeout.thickness;
double y_strike = baseline - font->strikeout.position - width / 2.; 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_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_line_width(buf->cairo, width); cairo_set_source_rgb(cr, color.r, color.g, color.b);
cairo_move_to(buf->cairo, x, round(y_strike) + 0.5); cairo_set_line_width(cr, width);
cairo_rel_line_to(buf->cairo, term->cell_width, 0); cairo_move_to(cr, x, round(y_strike) + 0.5);
cairo_stroke(buf->cairo); cairo_rel_line_to(cr, term->cell_width, 0);
cairo_stroke(cr);
} }
static bool static bool
@ -166,9 +173,15 @@ arm_blink_timer(struct terminal *term)
} }
static void static void
render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell, render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx,
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 width = term->cell_width;
double height = term->cell_height; double height = term->cell_height;
double x = col * width; double x = col * width;
@ -177,10 +190,10 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
bool block_cursor = has_cursor && term->cursor_style == CURSOR_BLOCK; bool block_cursor = has_cursor && term->cursor_style == CURSOR_BLOCK;
bool is_selected = coord_is_selected(term, col, row); 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 ? cell->attrs.foreground
: !term->reverse ? term->colors.fg : term->colors.bg; : !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 ? cell->attrs.background
: !term->reverse ? term->colors.bg : term->colors.fg; : !term->reverse ? term->colors.bg : term->colors.fg;
@ -208,18 +221,19 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
} }
/* Background */ /* Background */
cairo_set_source_rgb(buf->cairo, bg.r, bg.g, bg.b); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle(buf->cairo, x, y, width, height); cairo_set_source_rgb(cr, bg.r, bg.g, bg.b);
cairo_fill(buf->cairo); cairo_rectangle(cr, x, y, width, height);
cairo_fill(cr);
/* Non-block cursors */ /* Non-block cursors */
if (has_cursor) { if (has_cursor) {
struct rgb cursor_color = color_hex_to_rgb(term->cursor_color.cursor); struct rgb cursor_color = color_hex_to_rgb(term->cursor_color.cursor);
if (term->cursor_style == CURSOR_BAR) 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) else if (term->cursor_style == CURSOR_UNDERLINE)
draw_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) { if (cell->attrs.blink && !term->blink.active) {
@ -232,10 +246,10 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
/* Underline */ /* Underline */
if (cell->attrs.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) 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 * cairo_show_glyphs() apparently works *much* faster when
@ -259,28 +273,12 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
} }
struct font *font = attrs_to_font(term, &cell->attrs); struct font *font = attrs_to_font(term, &cell->attrs);
const struct glyph *glyph = font_glyph_for_utf8(font, cell->c);
struct glyph *glyph = NULL; if (glyph != NULL) {
if (strnlen(cell->c, 4) == 1) { cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
if (font->cache[(unsigned char)cell->c[0]].surf != NULL) cairo_set_source_rgb(cr, fg.r, fg.g, fg.b);
glyph = &font->cache[(unsigned char)cell->c[0]]; cairo_mask_surface(
} cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top);
struct glyph _glyph;
if (glyph == NULL) {
if (!font_glyph_for_utf8(font, cell->c, &_glyph))
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);
if (glyph == &_glyph) {
cairo_surface_destroy(_glyph.surf);
free(_glyph.data);
} }
} }
@ -303,11 +301,11 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
buf->size); buf->size);
if (height > 0) { if (height > 0) {
cairo_surface_flush(buf->cairo_surface); cairo_surface_flush(buf->cairo_surface[0]);
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface); uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]);
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); 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); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
} }
@ -332,26 +330,74 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
buf->size); buf->size);
if (height > 0) { if (height > 0) {
cairo_surface_flush(buf->cairo_surface); cairo_surface_flush(buf->cairo_surface[0]);
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface); uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]);
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride); 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); wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
} }
} }
static void 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++) 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( wl_surface_damage_buffer(
term->wl.surface, term->wl.surface,
0, row_no * term->cell_height, 0, row_no * term->cell_height,
term->width, 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( static void frame_callback(
@ -374,8 +420,8 @@ grid_render(struct terminal *term)
assert(term->width > 0); assert(term->width > 0);
assert(term->height > 0); assert(term->height > 0);
struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height); struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count);
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_SOURCE);
gseq.g = gseq.glyphs; gseq.g = gseq.glyphs;
gseq.count = 0; gseq.count = 0;
@ -384,9 +430,12 @@ grid_render(struct terminal *term)
/* Erase old cursor (if we rendered a cursor last time) */ /* Erase old cursor (if we rendered a cursor last time) */
if (term->render.last_cursor.cell != NULL) { if (term->render.last_cursor.cell != NULL) {
struct cell *hack = (struct cell *)term->render.last_cursor.cell;
hack->attrs.clean = 0;
render_cell( render_cell(
term, buf, 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.col,
term->render.last_cursor.in_view.row, false); term->render.last_cursor.in_view.row, false);
@ -424,11 +473,11 @@ grid_render(struct terminal *term)
uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
struct rgb bg = color_hex_to_rgb(_bg); 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], rmargin, 0, rmargin_width, term->height);
cairo_rectangle(buf->cairo, 0, bmargin, term->width, bmargin_height); cairo_rectangle(buf->cairo[0], 0, bmargin, term->width, bmargin_height);
cairo_fill(buf->cairo); cairo_fill(buf->cairo[0]);
wl_surface_damage_buffer( wl_surface_damage_buffer(
term->wl.surface, rmargin, 0, rmargin_width, term->height); term->wl.surface, rmargin, 0, rmargin_width, term->height);
@ -456,20 +505,38 @@ grid_render(struct terminal *term)
tll_remove(term->grid->scroll_damage, it); 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++) { for (int r = 0; r < term->rows; r++) {
struct row *row = grid_row_in_view(term->grid, r); struct row *row = grid_row_in_view(term->grid, r);
if (!row->dirty) if (!row->dirty)
continue; 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; row->dirty = false;
all_clean = 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) { if (term->blink.active) {
/* Check if there are still any visible blinking cells */ /* Check if there are still any visible blinking cells */
bool none_is_blinking = true; bool none_is_blinking = true;
@ -516,6 +583,10 @@ grid_render(struct terminal *term)
cursor_is_visible = true; 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) { if (cursor_is_visible && !term->hide_cursor) {
/* Remember cursor coordinates so that we can erase it next /* Remember cursor coordinates so that we can erase it next
* time. Note that we need to re-align it against the view. */ * time. Note that we need to re-align it against the view. */
@ -528,11 +599,12 @@ grid_render(struct terminal *term)
term->cursor.col, view_aligned_row}; term->cursor.col, view_aligned_row};
struct row *row = grid_row_in_view(term->grid, 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( render_cell(
term, buf, term->render.last_cursor.cell, term, buf, 0, cell, term->cursor.col, view_aligned_row, true);
term->cursor.col, view_aligned_row, true);
wl_surface_damage_buffer( wl_surface_damage_buffer(
term->wl.surface, term->wl.surface,
@ -549,10 +621,10 @@ grid_render(struct terminal *term)
} }
if (term->flash.active) { if (term->flash.active) {
cairo_set_source_rgba(buf->cairo, 1.0, 1.0, 0.0, 0.5); cairo_set_source_rgba(buf->cairo[0], 1.0, 1.0, 0.0, 0.5);
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_OVER);
cairo_rectangle(buf->cairo, 0, 0, term->width, term->height); cairo_rectangle(buf->cairo[0], 0, 0, term->width, term->height);
cairo_fill(buf->cairo); cairo_fill(buf->cairo[0]);
wl_surface_damage_buffer( wl_surface_damage_buffer(
term->wl.surface, 0, 0, term->width, term->height); term->wl.surface, 0, 0, term->width, term->height);
@ -561,7 +633,7 @@ grid_render(struct terminal *term)
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows); assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
assert(term->grid->view >= 0 && term->grid->view < 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); wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0);
assert(term->render.frame_callback == NULL); assert(term->render.frame_callback == NULL);

View file

@ -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_set_title(struct terminal *term, const char *title);
void render_update_cursor_surface(struct terminal *term); void render_update_cursor_surface(struct terminal *term);
void render_refresh(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
View file

@ -27,8 +27,10 @@ static const struct wl_buffer_listener buffer_listener = {
}; };
struct buffer * 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) { tll_foreach(buffers, it) {
if (it->item.width != width || it->item.height != height) if (it->item.width != width || it->item.height != height)
continue; continue;
@ -57,8 +59,8 @@ shm_get_buffer(struct wl_shm *shm, int width, int height)
struct wl_shm_pool *pool = NULL; struct wl_shm_pool *pool = NULL;
struct wl_buffer *buf = NULL; struct wl_buffer *buf = NULL;
cairo_surface_t *cairo_surface = NULL; cairo_surface_t **cairo_surface = NULL;
cairo_t *cairo = NULL; cairo_t **cairo = NULL;
/* Backing memory for SHM */ /* Backing memory for SHM */
pool_fd = memfd_create("f00sel-wayland-shm-buffer-pool", MFD_CLOEXEC); 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; close(pool_fd); pool_fd = -1;
/* Create a cairo surface around the mmapped memory */ /* Create a cairo surface around the mmapped memory */
cairo_surface = cairo_image_surface_create_for_data( cairo_surface = calloc(copies, sizeof(cairo_surface[0]));
mmapped, CAIRO_FORMAT_ARGB32, width, height, stride); cairo = calloc(copies, sizeof(cairo[0]));
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 = cairo_create(cairo_surface); for (size_t i = 0; i < copies; i++) {
if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) { cairo_surface[i] = cairo_image_surface_create_for_data(
LOG_ERR("failed to create cairo context: %s", mmapped, CAIRO_FORMAT_ARGB32, width, height, stride);
cairo_status_to_string(cairo_status(cairo)));
goto err; 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' */ /* 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, .size = size,
.mmapped = mmapped, .mmapped = mmapped,
.wl_buf = buf, .wl_buf = buf,
.copies = copies,
.cairo_surface = cairo_surface, .cairo_surface = cairo_surface,
.cairo = cairo} .cairo = cairo}
) )
@ -134,10 +143,18 @@ shm_get_buffer(struct wl_shm *shm, int width, int height)
return ret; return ret;
err: err:
if (cairo != NULL) if (cairo != NULL) {
cairo_destroy(cairo); for (size_t i = 0; i < copies; i++)
if (cairo_surface != NULL) if (cairo[i] != NULL)
cairo_surface_destroy(cairo_surface); 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) if (buf != NULL)
wl_buffer_destroy(buf); wl_buffer_destroy(buf);
if (pool != NULL) if (pool != NULL)
@ -156,8 +173,18 @@ shm_fini(void)
tll_foreach(buffers, it) { tll_foreach(buffers, it) {
struct buffer *buf = &it->item; struct buffer *buf = &it->item;
cairo_destroy(buf->cairo); if (buf->cairo != NULL) {
cairo_surface_destroy(buf->cairo_surface); 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); wl_buffer_destroy(buf->wl_buf);
munmap(buf->mmapped, buf->size); munmap(buf->mmapped, buf->size);

7
shm.h
View file

@ -16,9 +16,10 @@ struct buffer {
struct wl_buffer *wl_buf; struct wl_buffer *wl_buf;
cairo_surface_t *cairo_surface; size_t copies;
cairo_t *cairo; 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); void shm_fini(void);

View file

@ -20,29 +20,36 @@ void
term_damage_rows(struct terminal *term, int start, int end) term_damage_rows(struct terminal *term, int start, int end)
{ {
assert(start <= end); assert(start <= end);
for (int r = start; r <= end; r++) for (int r = start; r <= end; r++) {
grid_row(term->grid, r)->dirty = true; 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 void
term_damage_rows_in_view(struct terminal *term, int start, int end) term_damage_rows_in_view(struct terminal *term, int start, int end)
{ {
assert(start <= end); assert(start <= end);
for (int r = start; r <= end; r++) for (int r = start; r <= end; r++) {
grid_row_in_view(term->grid, r)->dirty = true; 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 void
term_damage_all(struct terminal *term) term_damage_all(struct terminal *term)
{ {
term_damage_rows(term, 0, term->rows); term_damage_rows(term, 0, term->rows - 1);
} }
void void
term_damage_view(struct terminal *term) term_damage_view(struct terminal *term)
{ {
for (int i = 0; i < term->rows; i++) term_damage_rows_in_view(term, 0, term->rows - 1);
grid_row_in_view(term->grid, i)->dirty = true;
} }
void void
@ -73,9 +80,10 @@ erase_cell_range(struct terminal *term, struct row *row, int start, int end)
assert(start < term->cols); assert(start < term->cols);
assert(end < 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++) { for (int col = start; col <= end; col++) {
row->cells[col].c[0] = '\0'; row->cells[col].c[0] = '\0';
row->cells[col].attrs.clean = 0;
row->cells[col].attrs.background = term->vt.attrs.background; row->cells[col].attrs.background = term->vt.attrs.background;
} }
} else { } else {

View file

@ -5,9 +5,7 @@
#include <stddef.h> #include <stddef.h>
#include <threads.h> #include <threads.h>
#include <semaphore.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <cairo.h> #include <cairo.h>
#include <wayland-client.h> #include <wayland-client.h>
@ -15,7 +13,7 @@
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h> #include <xkbcommon/xkbcommon-keysyms.h>
#include "font.h"
#include "tllist.h" #include "tllist.h"
#define likely(c) __builtin_expect(!!(c), 1) #define likely(c) __builtin_expect(!!(c), 1)
@ -63,8 +61,11 @@ struct attributes {
uint8_t conceal:1; uint8_t conceal:1;
uint8_t reverse:1; uint8_t reverse:1;
uint32_t foreground; uint32_t clean:1;
uint32_t background; uint32_t foreground:31;
uint32_t reserved:1;
uint32_t background:31;
} __attribute__((packed)); } __attribute__((packed));
struct cell { struct cell {
@ -206,31 +207,6 @@ struct primary {
uint32_t serial; uint32_t serial;
}; };
struct glyph {
void *data;
cairo_surface_t *surf;
int left;
int top;
};
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 }; enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR };
struct terminal { struct terminal {
@ -341,6 +317,17 @@ struct terminal {
struct { struct {
struct wl_callback *frame_callback; 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 */ /* Last rendered cursor position */
struct { struct {
struct coord actual; /* Absolute */ struct coord actual; /* Absolute */

10
vt.c
View file

@ -698,7 +698,6 @@ pre_print(struct terminal *term)
static inline void static inline void
post_print(struct terminal *term) post_print(struct terminal *term)
{ {
term->grid->cur_row->dirty = true;
if (term->cursor.col < term->cols - 1) if (term->cursor.col < term->cols - 1)
term_cursor_right(term, 1); term_cursor_right(term, 1);
else else
@ -716,13 +715,6 @@ print_insert(struct terminal *term)
&row[term->cursor.col + 1], &row[term->cursor.col + 1],
&row[term->cursor.col], &row[term->cursor.col],
term->cols - term->cursor.col - 1); 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); term_damage_update(term, term->cursor.linear, 1);
#else #else
row->dirty = true; row->dirty = true;
cell->attrs.clean = 0;
#endif #endif
print_insert(term); print_insert(term);
@ -761,6 +754,7 @@ action_print(struct terminal *term, uint8_t c)
term_damage_update(term, term->cursor.linear, 1); term_damage_update(term, term->cursor.linear, 1);
#else #else
row->dirty = true; row->dirty = true;
cell->attrs.clean = 0;
#endif #endif
print_insert(term); print_insert(term);