config: add inheritable option “font-shaping”

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
This commit is contained in:
Daniel Eklöf 2022-02-23 18:43:13 +01:00
parent 265188ca4c
commit ffccabbb13
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
17 changed files with 106 additions and 8 deletions

View file

@ -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

View file

@ -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;

View file

@ -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},

View file

@ -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);

View file

@ -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,
};

View file

@ -3,6 +3,7 @@
#include <fcft/fcft.h>
#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;
};

View file

@ -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

View file

@ -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

7
font-shaping.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
enum font_shaping {
FONT_SHAPE_NONE,
FONT_SHAPE_GRAPHEMES,
FONT_SHAPE_FULL,
};

View file

@ -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',

View file

@ -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) {

View file

@ -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}

View file

@ -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);

View file

@ -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
};

View file

@ -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,
};

View file

@ -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;

View file

@ -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);