mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-08 05:34:00 -04: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
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
config.h
5
config.h
|
|
@ -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
25
csi.c
|
|
@ -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
221
font.c
|
|
@ -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
55
font.h
|
|
@ -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
2
grid.c
|
|
@ -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
80
main.c
|
|
@ -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
228
render.c
|
|
@ -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);
|
||||||
|
|
|
||||||
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_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
69
shm.c
|
|
@ -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
7
shm.h
|
|
@ -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);
|
||||||
|
|
|
||||||
24
terminal.c
24
terminal.c
|
|
@ -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 {
|
||||||
|
|
|
||||||
49
terminal.h
49
terminal.h
|
|
@ -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
10
vt.c
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue