From ffccabbb13cdd8e681b739e0e93a2466e858f2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Feb 2022 18:43:13 +0100 Subject: [PATCH] =?UTF-8?q?config:=20add=20inheritable=20option=20?= =?UTF-8?q?=E2=80=9Cfont-shaping=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds an inheritable option, “font-shaping”, that controls whether a particle that renders text should enable font-shaping or not. The option works similar to the ‘font’ option: one can set it at the top-level, and it gets inherited down through all modules and to their particles. Or, you can set it on a module and it gets inherited to all its particles, but not to other modules’ particles. Finally, you can set it on individual particles, in which case it only applies to them (or “child” particles). When font-shaping is enabled (the default), the string particle shapes full text runs using the fcft_rasterize_text_run_utf32() API. In fcft, this results in HarfBuzz being used to shape the string. When disabled, the string particle instead uses the simpler fcft_rasterize_char_utf32() API, which rasterizes individual characters. This gives user greater control over the font rendering. One example is bitmap fonts, which HarfBuzz often doesn’t get right. Closes #159 --- CHANGELOG.md | 3 ++ bar/bar.h | 2 ++ config-verify.c | 8 ++++++ config-verify.h | 1 + config.c | 56 ++++++++++++++++++++++++++++++++++++-- config.h | 3 ++ doc/yambar-particles.5.scd | 6 ++++ doc/yambar.5.scd | 6 ++++ font-shaping.h | 7 +++++ meson.build | 1 + particle.c | 5 ++-- particle.h | 6 +++- particles/list.c | 2 +- particles/map.c | 1 + particles/progress-bar.c | 1 + particles/ramp.c | 2 +- particles/string.c | 4 ++- 17 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 font-shaping.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f48f6..db94656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ * overline: new decoration (https://codeberg.org/dnkl/yambar/issues/153). * i3/sway: boolean option `strip-workspace-numbers`. +* font-shaping: new inheritable configuration option, allowing you to + configure whether strings should be _shaped_ using HarfBuzz, or not + (https://codeberg.org/dnkl/yambar/issues/159). ### Changed diff --git a/bar/bar.h b/bar/bar.h index c9f94f4..717b690 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -1,6 +1,7 @@ #pragma once #include "../color.h" +#include "../font-shaping.h" #include "../module.h" struct bar { @@ -26,6 +27,7 @@ struct bar_config { const char *monitor; enum bar_layer layer; enum bar_location location; + enum font_shaping font_shaping; int height; int left_spacing, right_spacing; int left_margin, right_margin; diff --git a/config-verify.c b/config-verify.c index b92fb71..68a50c8 100644 --- a/config-verify.c +++ b/config-verify.c @@ -228,6 +228,13 @@ conf_verify_font(keychain_t *chain, const struct yml_node *node) return true; } +bool +conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node) +{ + return conf_verify_enum( + chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2); +} + bool conf_verify_decoration(keychain_t *chain, const struct yml_node *node) { @@ -450,6 +457,7 @@ conf_verify_bar(const struct yml_node *bar) {"border", false, &verify_bar_border}, {"font", false, &conf_verify_font}, + {"font-shaping", false, &conf_verify_font_shaping}, {"foreground", false, &conf_verify_color}, {"left", false, &verify_module_list}, diff --git a/config-verify.h b/config-verify.h index 8e5ab29..0a4ae34 100644 --- a/config-verify.h +++ b/config-verify.h @@ -45,6 +45,7 @@ bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node); bool conf_verify_color(keychain_t *chain, const struct yml_node *node); bool conf_verify_font(keychain_t *chain, const struct yml_node *node); +bool conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index 580781b..d2c11a6 100644 --- a/config.c +++ b/config.c @@ -101,6 +101,44 @@ conf_to_font(const struct yml_node *node) return ret; } +enum font_shaping +conf_to_font_shaping(const struct yml_node *node) +{ + const char *v = yml_value_as_string(node); + + if (strcmp(v, "none") == 0) + return FONT_SHAPE_NONE; + + else if (strcmp(v, "graphemes") == 0) { + static bool have_warned = false; + + if (!have_warned && + !(fcft_capabilities() & FCFT_CAPABILITY_GRAPHEME_SHAPING)) + { + have_warned = true; + LOG_WARN("cannot enable grapheme shaping; no support in fcft"); + } + return FONT_SHAPE_GRAPHEMES; + } + + else if (strcmp(v, "full") == 0) { + static bool have_warned = false; + + if (!have_warned && + !(fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING)) + { + have_warned = true; + LOG_WARN("cannot enable full text shaping; no support in fcft"); + } + return FONT_SHAPE_FULL; + } + + else { + assert(false); + return FONT_SHAPE_NONE; + } +} + struct deco * conf_to_deco(const struct yml_node *node) { @@ -144,7 +182,8 @@ particle_simple_list_from_config(const struct yml_node *node, } struct particle *common = particle_common_new( - 0, 0, NULL, fcft_clone(inherited.font), inherited.foreground, NULL); + 0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping, + inherited.foreground, NULL); return particle_list_new(common, parts, count, 0, 2); } @@ -163,6 +202,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_margin = yml_get_value(pair.value, "right-margin"); const struct yml_node *on_click = yml_get_value(pair.value, "on-click"); const struct yml_node *font_node = yml_get_value(pair.value, "font"); + const struct yml_node *font_shaping_node = yml_get_value(pair.value, "font-shaping"); const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground"); const struct yml_node *deco_node = yml_get_value(pair.value, "deco"); @@ -215,12 +255,14 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) */ struct fcft_font *font = font_node != NULL ? conf_to_font(font_node) : fcft_clone(inherited.font); + enum font_shaping font_shaping = font_shaping_node != NULL + ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping; pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground; /* Instantiate base/common particle */ struct particle *common = particle_common_new( - left, right, on_click_templates, font, foreground, deco); + left, right, on_click_templates, font, font_shaping, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); @@ -237,6 +279,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct bar_config conf = { .backend = backend, .layer = BAR_LAYER_BOTTOM, + .font_shaping = FONT_SHAPE_FULL, }; /* @@ -359,6 +402,7 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * foreground color at top-level. */ struct fcft_font *font = fcft_from_name(1, &(const char *){"sans"}, NULL); + enum font_shaping font_shaping = FONT_SHAPE_FULL; pixman_color_t foreground = {0xffff, 0xffff, 0xffff, 0xffff}; /* White */ const struct yml_node *font_node = yml_get_value(bar, "font"); @@ -367,12 +411,17 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) font = conf_to_font(font_node); } + const struct yml_node *font_shaping_node = yml_get_value(bar, "font-shaping"); + if (font_shaping_node != NULL) + font_shaping = conf_to_font_shaping(font_shaping_node); + const struct yml_node *foreground_node = yml_get_value(bar, "foreground"); if (foreground_node != NULL) foreground = conf_to_color(foreground_node); struct conf_inherit inherited = { .font = font, + .font_shaping = font_shaping, .foreground = foreground, }; @@ -402,12 +451,15 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * applied to all its particles. */ const struct yml_node *mod_font = yml_get_value(m.value, "font"); + const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping"); const struct yml_node *mod_foreground = yml_get_value( m.value, "foreground"); struct conf_inherit mod_inherit = { .font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font, + .font_shaping = mod_font_shaping != NULL + ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping, .foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground, }; diff --git a/config.h b/config.h index 56f5b2e..ceb4b85 100644 --- a/config.h +++ b/config.h @@ -3,6 +3,7 @@ #include #include "yml.h" #include "bar/bar.h" +#include "font-shaping.h" struct bar; struct particle; @@ -16,9 +17,11 @@ struct bar *conf_to_bar(const struct yml_node *bar, enum bar_backend backend); pixman_color_t conf_to_color(const struct yml_node *node); struct fcft_font *conf_to_font(const struct yml_node *node); +enum font_shaping conf_to_font_shaping(const struct yml_node *node); struct conf_inherit { const struct fcft_font *font; + enum font_shaping font_shaping; pixman_color_t foreground; }; diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index a633269..312fb99 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -31,6 +31,12 @@ following attributes are supported by all particles: : Font to use. Note that this is an inherited attribute; i.e. you can set it on e.g. a _list_ particle, and it will apply to all particles in the list. +| font-shaping +: enum +: no +: font-shaping; one of _full_ or _none_. When set to _full_ (the + default), strings will be "shaped" using HarfBuzz. Requires support + in fcft. | foreground : color : no diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 4e70ef2..35c87b8 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -130,6 +130,12 @@ types that are frequently used: the primary font, and the rest fallback fonts. These are yambar custom fallback fonts that will be searched before the fontconfig provided fallback list. +| font-shaping +: enum +: no +: Default setting for font-shaping, for use in particles. One of + _full_ or _none_. When set to _full_ (the default), strings will be + "shaped" using HarfBuzz. Requires support in fcft. | foreground : color : no diff --git a/font-shaping.h b/font-shaping.h new file mode 100644 index 0000000..3ae3817 --- /dev/null +++ b/font-shaping.h @@ -0,0 +1,7 @@ +#pragma once + +enum font_shaping { + FONT_SHAPE_NONE, + FONT_SHAPE_GRAPHEMES, + FONT_SHAPE_FULL, +}; diff --git a/meson.build b/meson.build index 4a479ec..b43d3fb 100644 --- a/meson.build +++ b/meson.build @@ -116,6 +116,7 @@ yambar = executable( 'config-verify.c', 'config-verify.h', 'config.c', 'config.h', 'decoration.h', + 'font-shaping.h', 'log.c', 'log.h', 'main.c', 'module.c', 'module.h', diff --git a/particle.c b/particle.c index 1b06f9a..7fd747c 100644 --- a/particle.c +++ b/particle.c @@ -31,14 +31,15 @@ particle_default_destroy(struct particle *particle) struct particle * particle_common_new(int left_margin, int right_margin, const char **on_click_templates, - struct fcft_font *font, pixman_color_t foreground, - struct deco *deco) + struct fcft_font *font, enum font_shaping font_shaping, + pixman_color_t foreground, struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; p->right_margin = right_margin; p->foreground = foreground; p->font = font; + p->font_shaping = font_shaping; p->deco = deco; if (on_click_templates != NULL) { diff --git a/particle.h b/particle.h index 7c5685e..bdf01f2 100644 --- a/particle.h +++ b/particle.h @@ -5,6 +5,7 @@ #include "color.h" #include "decoration.h" +#include "font-shaping.h" #include "tag.h" enum mouse_event { @@ -35,6 +36,7 @@ struct particle { pixman_color_t foreground; struct fcft_font *font; + enum font_shaping font_shaping; struct deco *deco; void (*destroy)(struct particle *particle); @@ -61,7 +63,8 @@ struct exposable { struct particle *particle_common_new( int left_margin, int right_margin, const char *on_click_templates[], - struct fcft_font *font, pixman_color_t foreground, struct deco *deco); + struct fcft_font *font, enum font_shaping font_shaping, + pixman_color_t foreground, struct deco *deco); void particle_default_destroy(struct particle *particle); @@ -82,6 +85,7 @@ void exposable_default_on_mouse( {"right-margin", false, &conf_verify_unsigned}, \ {"on-click", false, &conf_verify_on_click}, \ {"font", false, &conf_verify_font}, \ + {"font-shaping", false, &conf_verify_font_shaping}, \ {"foreground", false, &conf_verify_color}, \ {"deco", false, &conf_verify_decoration}, \ {NULL, false, NULL} diff --git a/particles/list.c b/particles/list.c index e5cf883..a2c37c6 100644 --- a/particles/list.c +++ b/particles/list.c @@ -197,7 +197,7 @@ from_conf(const struct yml_node *node, struct particle *common) yml_list_next(&it), idx++) { parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->foreground}); + it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } return particle_list_new(common, parts, count, left_spacing, right_spacing); diff --git a/particles/map.c b/particles/map.c index 980bdd1..abf76a6 100644 --- a/particles/map.c +++ b/particles/map.c @@ -215,6 +215,7 @@ from_conf(const struct yml_node *node, struct particle *common) struct conf_inherit inherited = { .font = common->font, + .font_shaping = common->font_shaping, .foreground = common->foreground }; diff --git a/particles/progress-bar.c b/particles/progress-bar.c index 462d536..f9e3999 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -304,6 +304,7 @@ from_conf(const struct yml_node *node, struct particle *common) struct conf_inherit inherited = { .font = common->font, + .font_shaping = common->font_shaping, .foreground = common->foreground, }; diff --git a/particles/ramp.c b/particles/ramp.c index 7815d99..0127519 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -209,7 +209,7 @@ from_conf(const struct yml_node *node, struct particle *common) yml_list_next(&it), idx++) { parts[idx] = conf_to_particle( - it.node, (struct conf_inherit){common->font, common->foreground}); + it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); } long min_v = min != NULL ? yml_value_as_int(min) : 0; diff --git a/particles/string.c b/particles/string.c index c5ade9f..f1e1faf 100644 --- a/particles/string.c +++ b/particles/string.c @@ -202,7 +202,9 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + if (particle->font_shaping == FONT_SHAPE_FULL && + fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) + { struct fcft_text_run *run = fcft_rasterize_text_run_utf32( font, chars, wtext, FCFT_SUBPIXEL_NONE);