config: add font-bold, font-italic and font-bold-italic options

These options lets the user configure custom fonts and styles, to use
with the bold and italic cell attributes.

By default, they are unset, meaning we use the bold/italic variants of
the regular font.

Closes #169.
This commit is contained in:
Daniel Eklöf 2020-10-20 21:04:47 +02:00
parent b507d3a55e
commit 2c101a21ee
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
8 changed files with 155 additions and 69 deletions

View file

@ -41,6 +41,10 @@
* Terminal content is now auto-scrolled when moving the mouse above or * Terminal content is now auto-scrolled when moving the mouse above or
below the window while selecting below the window while selecting
(https://codeberg.org/dnkl/foot/issues/149). (https://codeberg.org/dnkl/foot/issues/149).
* **font-bold**, **font-italic** and **font-bold-italic** options to
`foot.ini`. These options allow custom bold/italic fonts. They are
unset by default, meaning the bold/italic version of the regular
font is used (https://codeberg.org/dnkl/foot/issues/169).
### Changed ### Changed

View file

@ -547,14 +547,28 @@ parse_section_main(const char *key, const char *value, struct config *conf,
} }
} }
else if (strcmp(key, "font") == 0) { else if (strcmp(key, "font") == 0 ||
strcmp(key, "font-bold") == 0 ||
strcmp(key, "font-italic") == 0 ||
strcmp(key, "font-bold-italic") == 0)
{
size_t idx =
strcmp(key, "font") == 0 ? 0 :
strcmp(key, "font-bold") == 0 ? 1 :
strcmp(key, "font-italic") == 0 ? 2 : 3;
tll_foreach(conf->fonts[idx], it)
config_font_destroy(&it->item);
tll_free(conf->fonts[idx]);
char *copy = xstrdup(value); char *copy = xstrdup(value);
for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) { for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) {
/* Trim spaces, strictly speaking not necessary, but looks nice :) */ /* Trim spaces, strictly speaking not necessary, but looks nice :) */
while (*font != '\0' && isspace(*font)) while (*font != '\0' && isspace(*font))
font++; font++;
if (*font != '\0') if (*font != '\0')
tll_push_back(conf->fonts, config_font_parse(font)); tll_push_back(conf->fonts[idx], config_font_parse(font));
} }
free(copy); free(copy);
} }
@ -1904,7 +1918,7 @@ config_load(struct config *conf, const char *conf_path,
.pad_y = 2, .pad_y = 2,
.bell_is_urgent = false, .bell_is_urgent = false,
.startup_mode = STARTUP_WINDOWED, .startup_mode = STARTUP_WINDOWED,
.fonts = tll_init(), .fonts = {tll_init(), tll_init(), tll_init(), tll_init()},
.scrollback = { .scrollback = {
.lines = 1000, .lines = 1000,
.indicator = { .indicator = {
@ -2028,8 +2042,8 @@ config_load(struct config *conf, const char *conf_path,
conf->colors.selection_bg >> 24 == 0; conf->colors.selection_bg >> 24 == 0;
out: out:
if (ret && tll_length(conf->fonts) == 0) if (ret && tll_length(conf->fonts[0]) == 0)
tll_push_back(conf->fonts, config_font_parse("monospace")); tll_push_back(conf->fonts[0], config_font_parse("monospace"));
free(conf_file.path); free(conf_file.path);
if (conf_file.fd >= 0) if (conf_file.fd >= 0)
@ -2047,9 +2061,11 @@ config_free(struct config conf)
free(conf.app_id); free(conf.app_id);
free(conf.word_delimiters); free(conf.word_delimiters);
free(conf.scrollback.indicator.text); free(conf.scrollback.indicator.text);
tll_foreach(conf.fonts, it) for (size_t i = 0; i < ALEN(conf.fonts); i++) {
config_font_destroy(&it->item); tll_foreach(conf.fonts[i], it)
tll_free(conf.fonts); config_font_destroy(&it->item);
tll_free(conf.fonts[i]);
}
free(conf.server_socket_path); free(conf.server_socket_path);
tll_foreach(conf.bindings.key, it) { tll_foreach(conf.bindings.key, it) {

View file

@ -16,6 +16,7 @@ struct config_font {
double pt_size; double pt_size;
int px_size; int px_size;
}; };
typedef tll(struct config_font) config_font_list_t;
struct config_key_modifiers { struct config_key_modifiers {
bool shift; bool shift;
@ -77,7 +78,7 @@ struct config {
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
tll(struct config_font) fonts; config_font_list_t fonts[4];
struct { struct {
int lines; int lines;

View file

@ -20,20 +20,27 @@ in this order:
# SECTION: default # SECTION: default
*font* *font*, *font-bold*, *font-italic*, *font-bold-italic*
Comma separated list of fonts to use, in fontconfig format (see Comma separated list of fonts to use, in fontconfig format (see
*FONT FORMAT*). *FONT FORMAT*).
The first font is the primary font. The remaining fonts are For each option, the first font is the primary font. The remaining
fallback fonts that will be used whenever a glyph cannot be found fonts are fallback fonts that will be used whenever a glyph cannot
in the primary font. be found in the primary font.
The fallback fonts are searched in the order they appear. If a The fallback fonts are searched in the order they appear. If a
glyph cannot be found in any of the fallback fonts, the dynamic glyph cannot be found in any of the fallback fonts, the dynamic
fallback list from fontconfig (for the primary font) is fallback list from fontconfig (for the primary font) is
searched. searched.
Default: _monospace_. *font-bold*, *font-italic* and *font-bold-italic* allow custom
fonts to be used for bold/italic/bold+italic fonts. If left
unconfigured, the bold/italic variants of the regular font(s)
specified in *font* are used. *Note*: you _may_ have to tweak the
size(s) of the custom bold/italic fonts to match the regular font.
Default: _monospace_ (*font*), _not set_ (*font-bold*,
*font-italic*, *font-bold-italic*).
*pad* *pad*
Padding between border and glyphs, in pixels (subject to output Padding between border and glyphs, in pixels (subject to output

View file

@ -1,6 +1,9 @@
# -*- conf -*- # -*- conf -*-
# font=monospace # font=monospace
# font-bold=<bold variant of regular font>
# font-italic=<italic variant of regular font>
# font-bold-italic=<bold+italic variant of regular font>
# initial-window-size-pixels=700x500 # Or, # initial-window-size-pixels=700x500 # Or,
# initial-window-size-chars=<COLSxROWS> # initial-window-size-chars=<COLSxROWS>
# initial-window-mode=windowed # initial-window-mode=windowed

11
main.c
View file

@ -27,6 +27,7 @@
#include "server.h" #include "server.h"
#include "shm.h" #include "shm.h"
#include "terminal.h" #include "terminal.h"
#include "util.h"
#include "version.h" #include "version.h"
#include "xmalloc.h" #include "xmalloc.h"
@ -387,11 +388,13 @@ main(int argc, char *const *argv)
if (login_shell) if (login_shell)
conf.login_shell = true; conf.login_shell = true;
if (tll_length(conf_fonts) > 0) { if (tll_length(conf_fonts) > 0) {
tll_foreach(conf.fonts, it) for (size_t i = 0; i < ALEN(conf.fonts); i++) {
config_font_destroy(&it->item); tll_foreach(conf.fonts[i], it)
tll_free(conf.fonts); config_font_destroy(&it->item);
tll_free(conf.fonts[i]);
}
tll_foreach(conf_fonts, it) tll_foreach(conf_fonts, it)
tll_push_back(conf.fonts, config_font_parse(it->item)); tll_push_back(conf.fonts[0], config_font_parse(it->item));
tll_free(conf_fonts); tll_free(conf_fonts);
} }
if (conf_width > 0 && conf_height > 0) { if (conf_width > 0 && conf_height > 0) {

View file

@ -764,39 +764,77 @@ font_loader_thread(void *_data)
static bool static bool
reload_fonts(struct terminal *term) reload_fonts(struct terminal *term)
{ {
const size_t count = tll_length(term->conf->fonts); const size_t counts[4] = {
char *names[count]; tll_length(term->conf->fonts[0]),
tll_length(term->conf->fonts[1]),
tll_length(term->conf->fonts[2]),
tll_length(term->conf->fonts[3]),
};
size_t i = 0; /* Configure size (which may have been changed run-time) */
tll_foreach(term->conf->fonts, it) { char **names[4];
bool use_px_size = term->font_sizes[i].px_size > 0; for (size_t i = 0; i < 4; i++) {
char size[64]; names[i] = xmalloc(counts[i] * sizeof(names[i][0]));
if (use_px_size) size_t j = 0;
snprintf(size, sizeof(size), ":pixelsize=%d", term->font_sizes[i].px_size); tll_foreach(term->conf->fonts[i], it) {
else bool use_px_size = term->font_sizes[i][j].px_size > 0;
snprintf(size, sizeof(size), ":size=%.2f", term->font_sizes[i].pt_size); char size[64];
size_t len = strlen(it->item.pattern) + strlen(size) + 1; if (use_px_size)
names[i] = xmalloc(len); snprintf(size, sizeof(size), ":pixelsize=%d", term->font_sizes[i][j].px_size);
else
snprintf(size, sizeof(size), ":size=%.2f", term->font_sizes[i][j].pt_size);
strcpy(names[i], it->item.pattern); size_t len = strlen(it->item.pattern) + strlen(size) + 1;
strcat(names[i], size); names[i][j] = xmalloc(len);
i++;
strcpy(names[i][j], it->item.pattern);
strcat(names[i][j], size);
j++;
}
} }
char attrs0[256], attrs1[256], attrs2[256], attrs3[256]; /* Did user configure custom bold/italic fonts?
snprintf(attrs0, sizeof(attrs0), "dpi=%.2f", term->font_dpi); * Or should we use the regular font, with weight/slant attributes? */
snprintf(attrs1, sizeof(attrs1), "dpi=%.2f:weight=bold", term->font_dpi); const bool custom_bold = counts[1] > 0;
snprintf(attrs2, sizeof(attrs2), "dpi=%.2f:slant=italic", term->font_dpi); const bool custom_italic = counts[2] > 0;
snprintf(attrs3, sizeof(attrs3), "dpi=%.2f:weight=bold:slant=italic", term->font_dpi); const bool custom_bold_italic = counts[3] > 0;
const size_t count_regular = counts[0];
const char **names_regular = (const char **)names[0];
const size_t count_bold = custom_bold ? counts[1] : counts[0];
const char **names_bold = (const char **)(custom_bold ? names[1] : names[0]);
const size_t count_italic = custom_italic ? counts[2] : counts[0];
const char **names_italic = (const char **)(custom_italic ? names[2] : names[0]);
const size_t count_bold_italic = custom_bold_italic ? counts[3] : counts[0];
const char **names_bold_italic = (const char **)(custom_bold_italic ? names[3] : names[0]);
char *attrs[4] = {NULL};
int attr_len[4] = {-1, -1, -1, -1}; /* -1, so that +1 (below) results in 0 */
for (size_t i = 0; i < 2; i++) {
attr_len[0] = snprintf(attrs[0], attr_len[0] + 1, "dpi=%.2f", term->font_dpi);
attr_len[1] = snprintf(attrs[1], attr_len[1] + 1, "dpi=%.2f:%s", term->font_dpi, !custom_bold ? "weight=bold" : "");
attr_len[2] = snprintf(attrs[2], attr_len[2] + 1, "dpi=%.2f:%s", term->font_dpi, !custom_italic ? "slant=italic" : "");
attr_len[3] = snprintf(attrs[3], attr_len[3] + 1, "dpi=%.2f:%s", term->font_dpi, !custom_bold_italic ? "weight=bold:slant=italic" : "");
if (i > 0)
continue;
for (size_t i = 0; i < 4; i++)
attrs[i] = xmalloc(attr_len[i] + 1);
}
struct fcft_font *fonts[4]; struct fcft_font *fonts[4];
struct font_load_data data[4] = { struct font_load_data data[4] = {
{count, (const char **)names, attrs0, &fonts[0]}, {count_regular, names_regular, attrs[0], &fonts[0]},
{count, (const char **)names, attrs1, &fonts[1]}, {count_bold, names_bold, attrs[1], &fonts[1]},
{count, (const char **)names, attrs2, &fonts[2]}, {count_italic, names_italic, attrs[2], &fonts[2]},
{count, (const char **)names, attrs3, &fonts[3]}, {count_bold_italic, names_bold_italic, attrs[3], &fonts[3]},
}; };
thrd_t tids[4] = {0}; thrd_t tids[4] = {0};
@ -819,6 +857,13 @@ reload_fonts(struct terminal *term)
success = false; success = false;
} }
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < counts[i]; j++)
free(names[i][j]);
free(names[i]);
free(attrs[i]);
}
if (!success) { if (!success) {
LOG_ERR("failed to load primary fonts"); LOG_ERR("failed to load primary fonts");
for (size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
@ -827,19 +872,18 @@ reload_fonts(struct terminal *term)
} }
} }
for (size_t i = 0; i < count; i++)
free(names[i]);
return success ? term_set_fonts(term, fonts) : success; return success ? term_set_fonts(term, fonts) : success;
} }
static bool static bool
load_fonts_from_conf(struct terminal *term) load_fonts_from_conf(struct terminal *term)
{ {
size_t i = 0; for (size_t i = 0; i < 4; i++) {
tll_foreach(term->conf->fonts, it) { size_t j = 0;
term->font_sizes[i++] = (struct config_font){ tll_foreach(term->conf->fonts[i], it) {
.pt_size = it->item.pt_size, .px_size = it->item.px_size}; term->font_sizes[i][j++] = (struct config_font){
.pt_size = it->item.pt_size, .px_size = it->item.px_size};
}
} }
return reload_fonts(term); return reload_fonts(term);
@ -922,7 +966,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.ptmx = ptmx, .ptmx = ptmx,
.ptmx_buffers = tll_init(), .ptmx_buffers = tll_init(),
.ptmx_paste_buffers = tll_init(), .ptmx_paste_buffers = tll_init(),
.font_sizes = xmalloc(sizeof(term->font_sizes[0]) * tll_length(conf->fonts)), .font_sizes = {
xmalloc(sizeof(term->font_sizes[0][0]) * tll_length(conf->fonts[0])),
xmalloc(sizeof(term->font_sizes[1][0]) * tll_length(conf->fonts[1])),
xmalloc(sizeof(term->font_sizes[2][0]) * tll_length(conf->fonts[2])),
xmalloc(sizeof(term->font_sizes[3][0]) * tll_length(conf->fonts[3])),
},
.font_dpi = 0., .font_dpi = 0.,
.font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */ .font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */
? FCFT_SUBPIXEL_DEFAULT ? FCFT_SUBPIXEL_DEFAULT
@ -1025,10 +1074,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.cwd = xstrdup(cwd), .cwd = xstrdup(cwd),
}; };
{ for (size_t i = 0; i < 4; i++) {
size_t i = 0; size_t j = 0;
tll_foreach(conf->fonts, it) { tll_foreach(conf->fonts[i], it) {
term->font_sizes[i++] = (struct config_font){ term->font_sizes[i][j++] = (struct config_font){
.pt_size = it->item.pt_size, .px_size = it->item.px_size}; .pt_size = it->item.pt_size, .px_size = it->item.px_size};
} }
} }
@ -1277,7 +1326,8 @@ term_destroy(struct terminal *term)
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++)
fcft_destroy(term->fonts[i]); fcft_destroy(term->fonts[i]);
free(term->font_sizes); for (size_t i = 0; i < 4; i++)
free(term->font_sizes[i]);
free(term->search.buf); free(term->search.buf);
@ -1518,22 +1568,24 @@ term_reset(struct terminal *term, bool hard)
static bool static bool
term_font_size_adjust(struct terminal *term, double amount) term_font_size_adjust(struct terminal *term, double amount)
{ {
for (size_t i = 0; i < tll_length(term->conf->fonts); i++) { for (size_t i = 0; i < 4; i++) {
double old_pt_size = term->font_sizes[i].pt_size; for (size_t j = 0; j < tll_length(term->conf->fonts[i]); j++) {
double old_pt_size = term->font_sizes[i][j].pt_size;
/* /*
* To ensure primary and user-configured fallback fonts are * To ensure primary and user-configured fallback fonts are
* resizes by the same amount, convert pixel sizes to point * resizes by the same amount, convert pixel sizes to point
* sizes, and to the adjustment on point sizes only. * sizes, and to the adjustment on point sizes only.
*/ */
if (term->font_sizes[i].px_size > 0) { if (term->font_sizes[i][j].px_size > 0) {
double dpi = term->font_dpi; double dpi = term->font_dpi;
old_pt_size = term->font_sizes[i].px_size * 72. / dpi; old_pt_size = term->font_sizes[i][j].px_size * 72. / dpi;
}
term->font_sizes[i][j].pt_size = fmax(old_pt_size + amount, 0);
term->font_sizes[i][j].px_size = -1;
} }
term->font_sizes[i].pt_size = fmax(old_pt_size + amount, 0);
term->font_sizes[i].px_size = -1;
} }
return reload_fonts(term); return reload_fonts(term);

View file

@ -225,7 +225,7 @@ struct terminal {
struct composed *composed; struct composed *composed;
struct fcft_font *fonts[4]; struct fcft_font *fonts[4];
struct config_font *font_sizes; struct config_font *font_sizes[4];
float font_dpi; float font_dpi;
enum fcft_subpixel font_subpixel; enum fcft_subpixel font_subpixel;