mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-06 04:06:06 -05:00
Merge branch 'multithreaded-renderer'
This commit is contained in:
commit
f39d848368
14 changed files with 598 additions and 228 deletions
45
config.c
45
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;
|
||||
}
|
||||
|
||||
|
|
@ -145,8 +147,21 @@ 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) {
|
||||
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 {
|
||||
|
|
@ -393,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,
|
||||
|
|
@ -427,6 +442,8 @@ config_load(struct config *conf)
|
|||
.cursor = 0,
|
||||
},
|
||||
},
|
||||
|
||||
.render_worker_count = sysconf(_SC_NPROCESSORS_ONLN),
|
||||
};
|
||||
|
||||
char *path = get_config_path();
|
||||
|
|
@ -449,6 +466,7 @@ config_load(struct config *conf)
|
|||
fclose(f);
|
||||
|
||||
out:
|
||||
tll_push_back(conf->fonts, strdup("monospace"));
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -458,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);
|
||||
}
|
||||
|
|
|
|||
5
config.h
5
config.h
|
|
@ -4,11 +4,12 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
#include "terminal.h"
|
||||
#include "tllist.h"
|
||||
|
||||
struct config {
|
||||
char *term;
|
||||
char *shell;
|
||||
char *font;
|
||||
tll(char *) fonts;
|
||||
|
||||
struct {
|
||||
uint32_t fg;
|
||||
|
|
@ -24,6 +25,8 @@ struct config {
|
|||
uint32_t cursor;
|
||||
} color;
|
||||
} cursor;
|
||||
|
||||
size_t render_worker_count;
|
||||
};
|
||||
|
||||
bool config_load(struct config *conf);
|
||||
|
|
|
|||
25
csi.c
25
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) */
|
||||
|
|
|
|||
221
font.c
221
font.c
|
|
@ -1,33 +1,42 @@
|
|||
#include "font.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <wchar.h>
|
||||
#include <assert.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
55
font.h
55
font.h
|
|
@ -3,9 +3,56 @@
|
|||
#include <stdbool.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);
|
||||
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);
|
||||
|
|
|
|||
2
grid.c
2
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;
|
||||
}
|
||||
|
|
|
|||
80
main.c
80
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>
|
||||
|
|
@ -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':
|
||||
|
|
@ -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,21 +419,33 @@ main(int argc, char *const *argv)
|
|||
thrd_t keyboard_repeater_id;
|
||||
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term);
|
||||
|
||||
if (!font_from_name(conf.font, &term.fonts[0]))
|
||||
goto out;
|
||||
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);
|
||||
|
||||
{
|
||||
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]);
|
||||
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]);
|
||||
}
|
||||
|
||||
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 */
|
||||
for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) {
|
||||
struct font *f = &term.fonts[i];
|
||||
|
|
@ -716,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)
|
||||
|
|
@ -809,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -827,6 +851,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 +943,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);
|
||||
|
||||
|
|
|
|||
228
render.c
228
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,15 @@ 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,
|
||||
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;
|
||||
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 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;
|
||||
|
||||
|
|
@ -208,18 +221,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 +246,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
|
||||
|
|
@ -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 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))
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,11 +301,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 +330,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 +420,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;
|
||||
|
|
@ -384,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,
|
||||
term->render.last_cursor.cell,
|
||||
term, buf, 0,
|
||||
//term->render.last_cursor.cell,
|
||||
hack,
|
||||
term->render.last_cursor.in_view.col,
|
||||
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;
|
||||
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 +505,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 +583,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. */
|
||||
|
|
@ -528,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, 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,
|
||||
|
|
@ -549,10 +621,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 +633,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);
|
||||
|
|
|
|||
24
terminal.c
24
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 {
|
||||
|
|
|
|||
49
terminal.h
49
terminal.h
|
|
@ -5,9 +5,7 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <threads.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <wayland-client.h>
|
||||
|
|
@ -15,7 +13,7 @@
|
|||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
|
||||
#include "font.h"
|
||||
#include "tllist.h"
|
||||
|
||||
#define likely(c) __builtin_expect(!!(c), 1)
|
||||
|
|
@ -63,8 +61,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 {
|
||||
|
|
@ -206,31 +207,6 @@ struct primary {
|
|||
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 };
|
||||
|
||||
struct terminal {
|
||||
|
|
@ -341,6 +317,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 */
|
||||
|
|
|
|||
10
vt.c
10
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue