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
below the window while selecting
(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

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

View file

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

View file

@ -20,20 +20,27 @@ in this order:
# SECTION: default
*font*
*font*, *font-bold*, *font-italic*, *font-bold-italic*
Comma separated list of fonts to use, in fontconfig format (see
*FONT FORMAT*).
The first font is the primary font. The remaining fonts are
fallback fonts that will be used whenever a glyph cannot be found
in the primary font.
For each option, the first font is the primary font. The remaining
fonts are fallback fonts that will be used whenever a glyph cannot
be found in the primary font.
The fallback fonts are searched in the order they appear. If a
glyph cannot be found in any of the fallback fonts, the dynamic
fallback list from fontconfig (for the primary font) is
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*
Padding between border and glyphs, in pixels (subject to output

View file

@ -1,6 +1,9 @@
# -*- conf -*-
# 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-chars=<COLSxROWS>
# initial-window-mode=windowed

11
main.c
View file

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

View file

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

View file

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