From f9c9dd24329fe4a2f6d350e693b1d7a7bae33b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:16:02 +0100 Subject: [PATCH 01/18] config: add line-height, letter-spacing, {horizontal,vertical}-letter-offset --- config.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 8 ++++++++ 2 files changed, 56 insertions(+) diff --git a/config.c b/config.c index 0e106584..6af8803b 100644 --- a/config.c +++ b/config.c @@ -555,6 +555,50 @@ parse_section_main(const char *key, const char *value, struct config *conf, free(copy); } + else if (strcmp(key, "line-height") == 0) { + unsigned long height; + if (!str_to_ulong(value, 10, &height)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [default]: line-height: expected an integer, got '%s'", + path, lineno, value); + return false; + } + conf->line_height = height; + } + + else if (strcmp(key, "letter-spacing") == 0) { + unsigned long spacing; + if (!str_to_ulong(value, 10, &spacing)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [default]: letter-spacing: expected an integer, got '%s'", + path, lineno, value); + return false; + } + conf->letter_spacing = spacing; + } + + else if (strcmp(key, "horizontal-letter-offset") == 0) { + unsigned long offset; + if (!str_to_ulong(value, 10, &offset)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [default]: horizontal-letter-offset: " + "expected an integer, got '%s'", path, lineno, value); + return false; + } + conf->horizontal_letter_offset = offset; + } + + else if (strcmp(key, "vertical-letter-offset") == 0) { + unsigned long offset; + if (!str_to_ulong(value, 10, &offset)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [default]: horizontal-letter-offset: " + "expected an integer, got '%s'", path, lineno, value); + return false; + } + conf->vertical_letter_offset = offset; + } + else if (strcmp(key, "dpi-aware") == 0) { if (strcmp(value, "auto") == 0) conf->dpi_aware = DPI_AWARE_AUTO; @@ -1979,6 +2023,10 @@ config_load(struct config *conf, const char *conf_path, .bell_action = BELL_ACTION_NONE, .startup_mode = STARTUP_WINDOWED, .fonts = {tll_init(), tll_init(), tll_init(), tll_init()}, + .line_height = -1, + .letter_spacing = -1, + .horizontal_letter_offset = 0, + .vertical_letter_offset = 0, .dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */ .scrollback = { .lines = 1000, diff --git a/config.h b/config.h index 21b79230..9b33b37f 100644 --- a/config.h +++ b/config.h @@ -84,6 +84,14 @@ struct config { enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware; config_font_list_t fonts[4]; + /* Custom font metrics (-1 = use real font metrics) */ + int16_t line_height; + int16_t letter_spacing; + + /* Adjusted letter x/y offsets */ + int16_t horizontal_letter_offset; + int16_t vertical_letter_offset; + struct { int lines; From 428b5ef27c49a9202788921e349d7df9a23b7003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:16:29 +0100 Subject: [PATCH 02/18] foot.ini: add new options: line-height, letter-spacing and *-letter-offset --- foot.ini | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/foot.ini b/foot.ini index 76491372..ed9e5a72 100644 --- a/foot.ini +++ b/foot.ini @@ -1,22 +1,29 @@ # -*- conf -*- +# shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) +# term=foot +# login-shell=no + # font=monospace:size=8 # font-bold= # font-italic= # font-bold-italic= +# line-height= +# letter-spacing= +# horizontal-letter-offset=0 +# vertical-letter-offset=0 # dpi-aware=yes + # initial-window-size-pixels=700x500 # Or, # initial-window-size-chars= # initial-window-mode=windowed # pad=2x2 # optionally append 'center' -# shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) -# term=foot -# login-shell=no -# workers= + # bold-text-in-bright=no # bell=none # word-delimiters=,│`|:"'()[]{}<> # notify=notify-send -a foot -i foot ${title} ${body} +# workers= [scrollback] # lines=1000 From a0c359c24e8aacd16d8f9d4db35f50c985bfdf77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:17:23 +0100 Subject: [PATCH 03/18] term: set_fonts: use custom line-height+letter-spacing, if set If the user has configured a custom line-height and/or letter-spacing, use that instead of the font metrics. --- terminal.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/terminal.c b/terminal.c index 6ba04677..c7383ae2 100644 --- a/terminal.c +++ b/terminal.c @@ -629,10 +629,14 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) const int old_cell_width = term->cell_width; const int old_cell_height = term->cell_height; - term->cell_width = term->fonts[0]->space_advance.x > 0 - ? term->fonts[0]->space_advance.x : term->fonts[0]->max_advance.x; - term->cell_height = max(term->fonts[0]->height, - term->fonts[0]->ascent + term->fonts[0]->descent); + term->cell_width = term->conf->letter_spacing >= 0 + ? term->conf->letter_spacing + : (term->fonts[0]->space_advance.x > 0 + ? term->fonts[0]->space_advance.x : term->fonts[0]->max_advance.x); + term->cell_height = term->conf->line_height >= 0 + ? term->conf->line_height + : max(term->fonts[0]->height, + term->fonts[0]->ascent + term->fonts[0]->descent); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); if (term->cell_width < old_cell_width || From 3eba48d74be2c6539668be9a792377b01ada32bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:18:07 +0100 Subject: [PATCH 04/18] render: offset all glyphs with {horizontal,vertical}-letter-offset --- render.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/render.c b/render.c index 16198589..4e531207 100644 --- a/render.c +++ b/render.c @@ -256,7 +256,7 @@ color_dim_for_search(pixman_color_t *color) static inline int font_baseline(const struct terminal *term) { - return term->fonts[0]->ascent; + return term->conf->vertical_letter_offset + term->fonts[0]->ascent; } static void @@ -541,18 +541,20 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_image_t *clr_pix = pixman_image_create_solid_fill(&fg); if (glyph != NULL) { + const int letter_x_ofs = term->conf->horizontal_letter_offset; + if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) { pixman_image_composite32( PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + letter_x_ofs + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); } } else { pixman_image_composite32( PIXMAN_OP_OVER, clr_pix, glyph->pix, pix, 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + letter_x_ofs + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); } @@ -589,7 +591,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_image_composite32( PIXMAN_OP_OVER, clr_pix, g->pix, pix, 0, 0, 0, 0, - x + x_ofs + g->x, y + font_baseline(term) - g->y, + x + letter_x_ofs + x_ofs + g->x, y + font_baseline(term) - g->y, g->width, g->height); } } @@ -1691,6 +1693,8 @@ render_osd(struct terminal *term, struct fcft_font *font = term->fonts[0]; pixman_color_t fg = color_hex_to_pixman(_fg); + const int x_ofs = term->conf->horizontal_letter_offset; + for (size_t i = 0; i < wcslen(text); i++) { const struct fcft_glyph *glyph = fcft_glyph_rasterize( font, text[i], term->font_subpixel); @@ -1701,7 +1705,7 @@ render_osd(struct terminal *term, pixman_image_t *src = pixman_image_create_solid_fill(&fg); pixman_image_composite32( PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + x_ofs + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); pixman_image_unref(src); @@ -2268,6 +2272,7 @@ render_search_box(struct terminal *term) struct fcft_font *font = term->fonts[0]; const int x_left = width - visible_width + margin; + const int x_ofs = term->conf->horizontal_letter_offset; int x = x_left; int y = margin; pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]); @@ -2415,13 +2420,13 @@ render_search_box(struct terminal *term) /* Glyph surface is a pre-rendered image (typically a color emoji...) */ pixman_image_composite32( PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + x_ofs + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); } else { pixman_image_t *src = pixman_image_create_solid_fill(&fg); pixman_image_composite32( PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + x_ofs + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); pixman_image_unref(src); } From 389570b274df25c99ef9654036c2adb614db81fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:18:25 +0100 Subject: [PATCH 05/18] box-drawing: compensate for non-zero {horizontal,vertical}-letter-offsets Box drawing characters should always cover the entire cell. No more, no less. --- box-drawing.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index 8640fc4c..0fa44cd3 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -6,6 +6,7 @@ #define LOG_MODULE "box-drawing" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "config.h" #include "macros.h" #include "stride.h" #include "terminal.h" @@ -2068,8 +2069,8 @@ box_drawing(const struct terminal *term, wchar_t wc) .wc = wc, .cols = 1, .pix = pix, - .x = 0, - .y = term->fonts[0]->ascent, + .x = -term->conf->horizontal_letter_offset, + .y = term->conf->vertical_letter_offset + term->fonts[0]->ascent, .width = width, .height = height, .advance = { From 70cfcf11fb26a1e3954e72550e4c24e0fa43a8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:46:18 +0100 Subject: [PATCH 06/18] config: letter-spacing: make this a relative value 0, the default, means no additional spacing; the cell width is defined by the font metrics. A positive value *adds* to the width from the font metrics, while a negative value *subtracts*. --- config.c | 17 +++++++++++++++-- foot.ini | 2 +- terminal.c | 9 +++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 6af8803b..8f0b334f 100644 --- a/config.c +++ b/config.c @@ -360,6 +360,19 @@ str_to_bool(const char *s) strtoul(s, NULL, 0) > 0; } +static bool +str_to_long(const char *s, int base, long *res) +{ + if (s == NULL) + return false; + + errno = 0; + char *end = NULL; + + *res = strtol(s, &end, base); + return errno == 0 && *end == '\0'; +} + static bool str_to_ulong(const char *s, int base, unsigned long *res) { @@ -567,8 +580,8 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "letter-spacing") == 0) { - unsigned long spacing; - if (!str_to_ulong(value, 10, &spacing)) { + long spacing; + if (!str_to_long(value, 10, &spacing)) { LOG_AND_NOTIFY_ERR( "%s:%d: [default]: letter-spacing: expected an integer, got '%s'", path, lineno, value); diff --git a/foot.ini b/foot.ini index ed9e5a72..f13d8e63 100644 --- a/foot.ini +++ b/foot.ini @@ -9,7 +9,7 @@ # font-italic= # font-bold-italic= # line-height= -# letter-spacing= +# letter-spacing=0 # horizontal-letter-offset=0 # vertical-letter-offset=0 # dpi-aware=yes diff --git a/terminal.c b/terminal.c index c7383ae2..c23450fb 100644 --- a/terminal.c +++ b/terminal.c @@ -629,10 +629,11 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) const int old_cell_width = term->cell_width; const int old_cell_height = term->cell_height; - term->cell_width = term->conf->letter_spacing >= 0 - ? term->conf->letter_spacing - : (term->fonts[0]->space_advance.x > 0 - ? term->fonts[0]->space_advance.x : term->fonts[0]->max_advance.x); + term->cell_width = (term->fonts[0]->space_advance.x > 0 + ? term->fonts[0]->space_advance.x + : term->fonts[0]->max_advance.x) + + term->conf->letter_spacing; + term->cell_height = term->conf->line_height >= 0 ? term->conf->line_height : max(term->fonts[0]->height, From c2be3408edcafa42f49d9fc4f3bfc72fe76b419e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:47:46 +0100 Subject: [PATCH 07/18] config; {horizontal,vertical}-letter-offsets may be negative --- config.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index 8f0b334f..e02167af 100644 --- a/config.c +++ b/config.c @@ -591,8 +591,8 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "horizontal-letter-offset") == 0) { - unsigned long offset; - if (!str_to_ulong(value, 10, &offset)) { + long offset; + if (!str_to_long(value, 10, &offset)) { LOG_AND_NOTIFY_ERR( "%s:%d: [default]: horizontal-letter-offset: " "expected an integer, got '%s'", path, lineno, value); @@ -602,8 +602,8 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "vertical-letter-offset") == 0) { - unsigned long offset; - if (!str_to_ulong(value, 10, &offset)) { + long offset; + if (!str_to_long(value, 10, &offset)) { LOG_AND_NOTIFY_ERR( "%s:%d: [default]: horizontal-letter-offset: " "expected an integer, got '%s'", path, lineno, value); From e8b673e7b2e19786f572c30cd83fa0482ec849bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:48:15 +0100 Subject: [PATCH 08/18] =?UTF-8?q?doc:=20foot.ini:=20put=20=E2=80=98shell?= =?UTF-8?q?=E2=80=99,=20=E2=80=98login-shell=E2=80=99=20and=20=E2=80=98ter?= =?UTF-8?q?m=E2=80=99=20first?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/foot.ini.5.scd | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 4bf40af2..d3d9b523 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -18,6 +18,19 @@ in this order: # SECTION: default +*shell* + Executable to launch. Typically a shell. Default: _$SHELL_ if set, + otherwise the user's default shell (as specified in + _/etc/passwd_). You can also pass arguments. For example + */bin/bash --norc*. + +*login-shell* + Boolean. If enabled, the shell will be launched as a login shell, + by prepending a '-' to argv[0]. Default: _no_. + +*term* + Value to set the environment variable *TERM* to. Default: _foot_. + *font*, *font-bold*, *font-italic*, *font-bold-italic* Comma separated list of fonts to use, in fontconfig format. That @@ -111,19 +124,6 @@ in this order: *geometry* Deprecated. Alias for *initial-window-size-pixels*. -*shell* - Executable to launch. Typically a shell. Default: _$SHELL_ if set, - otherwise the user's default shell (as specified in - _/etc/passwd_). You can also pass arguments. For example - */bin/bash --norc*. - -*login-shell* - Boolean. If enabled, the shell will be launched as a login shell, - by prepending a '-' to argv[0]. Default: _no_. - -*term* - Value to set the environment variable *TERM* to. Default: _foot_. - *title* Initial window title. Default: _foot_. From d1f43371bb6bbd5d5d81472cc117effe0c0be3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:48:50 +0100 Subject: [PATCH 09/18] doc: foot.ini: document line-height, letter-spacing and letter offsets --- doc/foot.ini.5.scd | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index d3d9b523..32e989c4 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -64,6 +64,28 @@ in this order: Default: _monospace:size=8_ (*font*), _not set_ (*font-bold*, *font-italic*, *font-bold-italic*). +*line-height* + An absolute value, in pixels, that override line height from the + font metrics. + + See also: *vertical-letter-offset* + + Default: _no set_. + +*letter-spacing* + Spacing between letters. A positive value will increase the cell + size, and a negative value shrinks it. + + See also: *horizontal-letter-offset* + + Default: _0_. + +*horizontal-letter-offset*, *vertical-letter-offset* + Configure the horizontal and vertical offsets used when + positioning glyphs within cells, relative to the top left corner. + + Default: _0_. + *dpi-aware* *auto*, *yes*, or *no*. When set to *yes*, fonts are sized using the monitor's DPI, making a font of a given size have the same From a455797bf2f467605087a8e24c5b80d7d530ab80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 11:50:13 +0100 Subject: [PATCH 10/18] changelog: line-height, letter-spacing and {horizontal,vertical}-letter-offset --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5d23a39..1bc667e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,10 @@ (https://codeberg.org/dnkl/foot/issues/270) * Double- or triple clicking then dragging now extends the selection word- or line-wise (https://codeberg.org/dnkl/foot/issues/267). +* `line-height`, `letter-spacing`, `horizontal-letter-offset` and + `vertical-letter-offset` to `foot.ini`. These options let you tweak + cell size and glyph positioning + (https://codeberg.org/dnkl/foot/issues/244). ### Deprecated From 4c9f593d917fe21025762edab9a2928412c4de03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:00:13 +0100 Subject: [PATCH 11/18] config: str_to_color(): use LOG_AND_NOTIFY(), not just LOG() --- config.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/config.c b/config.c index e02167af..03474e49 100644 --- a/config.c +++ b/config.c @@ -400,18 +400,21 @@ str_to_double(const char *s, double *res) } static bool -str_to_color(const char *s, uint32_t *color, bool allow_alpha, const char *path, int lineno, +str_to_color(const char *s, uint32_t *color, bool allow_alpha, + struct config *conf, const char *path, int lineno, const char *section, const char *key) { unsigned long value; if (!str_to_ulong(s, 16, &value)) { - LOG_ERRNO("%s:%d: [%s]: %s: invalid color: %s", path, lineno, section, key, s); + LOG_AND_NOTIFY_ERRNO( + "%s:%d: [%s]: %s: invalid color: %s", path, lineno, section, key, s); return false; } if (!allow_alpha && (value & 0xff000000) != 0) { - LOG_ERR("%s:%d: [%s]: %s: color value must not have an alpha component: %s", - path, lineno, section, key, s); + LOG_AND_NOTIFY_ERR( + "%s:%d: [%s]: %s: color value must not have an alpha component: %s", + path, lineno, section, key, s); return false; } @@ -789,7 +792,7 @@ parse_section_colors(const char *key, const char *value, struct config *conf, } uint32_t color_value; - if (!str_to_color(value, &color_value, false, path, lineno, "colors", key)) + if (!str_to_color(value, &color_value, false, conf, path, lineno, "colors", key)) return false; *color = color_value; @@ -824,8 +827,8 @@ parse_section_cursor(const char *key, const char *value, struct config *conf, uint32_t text_color, cursor_color; if (text == NULL || cursor == NULL || - !str_to_color(text, &text_color, false, path, lineno, "cursor", "color") || - !str_to_color(cursor, &cursor_color, false, path, lineno, "cursor", "color")) + !str_to_color(text, &text_color, false, conf, path, lineno, "cursor", "color") || + !str_to_color(cursor, &cursor_color, false, conf, path, lineno, "cursor", "color")) { LOG_AND_NOTIFY_ERR("%s:%d: invalid cursor colors: %s", path, lineno, value); free(value_copy); @@ -884,7 +887,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, else if (strcmp(key, "color") == 0) { uint32_t color; - if (!str_to_color(value, &color, true, path, lineno, "csd", "color")) { + if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "color")) { LOG_AND_NOTIFY_ERR("%s:%d: invalid titlebar-color: %s", path, lineno, value); return false; } @@ -915,7 +918,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, else if (strcmp(key, "button-minimize-color") == 0) { uint32_t color; - if (!str_to_color(value, &color, true, path, lineno, "csd", "button-minimize-color")) { + if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-minimize-color")) { LOG_AND_NOTIFY_ERR("%s:%d: invalid button-minimize-color: %s", path, lineno, value); return false; } @@ -926,7 +929,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, else if (strcmp(key, "button-maximize-color") == 0) { uint32_t color; - if (!str_to_color(value, &color, true, path, lineno, "csd", "button-maximize-color")) { + if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-maximize-color")) { LOG_AND_NOTIFY_ERR("%s:%d: invalid button-maximize-color: %s", path, lineno, value); return false; } @@ -937,7 +940,7 @@ parse_section_csd(const char *key, const char *value, struct config *conf, else if (strcmp(key, "button-close-color") == 0) { uint32_t color; - if (!str_to_color(value, &color, true, path, lineno, "csd", "button-close-color")) { + if (!str_to_color(value, &color, true, conf, path, lineno, "csd", "button-close-color")) { LOG_AND_NOTIFY_ERR("%s:%d: invalid button-close-color: %s", path, lineno, value); return false; } From a6fc8b5da4a3bfc9b1d3994797826518103879ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:00:58 +0100 Subject: [PATCH 12/18] config: line-height, letter-spacing: values are in pt by default, but we allow px MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the value is specified without a unit, then the value is assumed to be in points, subject to DPI scaling. The value can optionally have a ‘px’ suffix, in which case the value is treated as a raw pixel count. --- box-drawing.c | 14 ++++++-- config.c | 93 ++++++++++++++++++++++++++------------------------- config.h | 14 +++++--- render.c | 8 ++--- terminal.c | 27 +++++++++++---- terminal.h | 2 ++ 6 files changed, 97 insertions(+), 61 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index 0fa44cd3..6bfaef82 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -2064,13 +2064,23 @@ box_drawing(const struct terminal *term, wchar_t wc) }; draw_glyph(wc, &buf); + const struct config *conf = term->conf; + + int x_ofs = conf->horizontal_letter_offset.px > 0 + ? conf->horizontal_letter_offset.px + : conf->horizontal_letter_offset.pt * term->font_dpi / 72.; + int y_ofs = conf->vertical_letter_offset.px > 0 + ? conf->vertical_letter_offset.px + : conf->vertical_letter_offset.pt * term->font_dpi / 72.; + + struct fcft_glyph *glyph = xmalloc(sizeof(*glyph)); *glyph = (struct fcft_glyph){ .wc = wc, .cols = 1, .pix = pix, - .x = -term->conf->horizontal_letter_offset, - .y = term->conf->vertical_letter_offset + term->fonts[0]->ascent, + .x = -x_ofs, + .y = y_ofs + term->fonts[0]->ascent, .width = width, .height = height, .advance = { diff --git a/config.c b/config.c index 03474e49..51615389 100644 --- a/config.c +++ b/config.c @@ -360,19 +360,6 @@ str_to_bool(const char *s) strtoul(s, NULL, 0) > 0; } -static bool -str_to_long(const char *s, int base, long *res) -{ - if (s == NULL) - return false; - - errno = 0; - char *end = NULL; - - *res = strtol(s, &end, base); - return errno == 0 && *end == '\0'; -} - static bool str_to_ulong(const char *s, int base, unsigned long *res) { @@ -422,6 +409,40 @@ str_to_color(const char *s, uint32_t *color, bool allow_alpha, return true; } +static bool +str_to_pt_or_px(const char *s, union pt_or_px *res, struct config *conf, + const char *path, int lineno, const char *section, const char *key) +{ + size_t len = s != NULL ? strlen(s) : 0; + if (len >= 2 && s[len - 2] == 'p' && s[len - 1] == 'x') { + errno = 0; + char *end = NULL; + + long value = strtol(s, &end, 10); + if (!(errno == 0 && end == s + len - 2)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [%s]: %s: " + "expected an integer directly followed by 'px', got '%s'", + path, lineno, section, key, s); + return false; + } + res->pt = 0; + res->px = value; + } else { + double value; + if (!str_to_double(s, &value)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [%s]: %s: expected a decimal value, got '%s'", + path, lineno, section, key, s); + return false; + } + res->pt = value; + res->px = 0; + } + + return true; +} + static bool parse_section_main(const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) @@ -572,47 +593,29 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "line-height") == 0) { - unsigned long height; - if (!str_to_ulong(value, 10, &height)) { - LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: line-height: expected an integer, got '%s'", - path, lineno, value); + if (!str_to_pt_or_px(value, &conf->line_height, + conf, path, lineno, "default", "line-height")) return false; - } - conf->line_height = height; } else if (strcmp(key, "letter-spacing") == 0) { - long spacing; - if (!str_to_long(value, 10, &spacing)) { - LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: letter-spacing: expected an integer, got '%s'", - path, lineno, value); + if (!str_to_pt_or_px(value, &conf->letter_spacing, + conf, path, lineno, "default", "letter-spacing")) return false; - } - conf->letter_spacing = spacing; } else if (strcmp(key, "horizontal-letter-offset") == 0) { - long offset; - if (!str_to_long(value, 10, &offset)) { - LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: horizontal-letter-offset: " - "expected an integer, got '%s'", path, lineno, value); + if (!str_to_pt_or_px( + value, &conf->horizontal_letter_offset, + conf, path, lineno, "default", "horizontal-letter-offset")) return false; - } - conf->horizontal_letter_offset = offset; } else if (strcmp(key, "vertical-letter-offset") == 0) { - long offset; - if (!str_to_long(value, 10, &offset)) { - LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: horizontal-letter-offset: " - "expected an integer, got '%s'", path, lineno, value); + if (!str_to_pt_or_px( + value, &conf->horizontal_letter_offset, + conf, path, lineno, "default", "vertical-letter-offset")) return false; - } - conf->vertical_letter_offset = offset; } else if (strcmp(key, "dpi-aware") == 0) { @@ -2039,10 +2042,10 @@ config_load(struct config *conf, const char *conf_path, .bell_action = BELL_ACTION_NONE, .startup_mode = STARTUP_WINDOWED, .fonts = {tll_init(), tll_init(), tll_init(), tll_init()}, - .line_height = -1, - .letter_spacing = -1, - .horizontal_letter_offset = 0, - .vertical_letter_offset = 0, + .line_height = { .pt = 0, .px = -1, }, + .letter_spacing = { .pt = 0, .px = 0, }, + .horizontal_letter_offset = {.pt = 0, .px = 0, }, + .vertical_letter_offset = {.pt = 0, .px = 0, }, .dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */ .scrollback = { .lines = 1000, diff --git a/config.h b/config.h index 9b33b37f..e9bc310f 100644 --- a/config.h +++ b/config.h @@ -54,6 +54,12 @@ struct config_mouse_binding { } pipe; }; +/* If px == -1, neither px nor pt is valid. If px == 0, pt is valid, else px */ +union pt_or_px { + int16_t px; + float pt; +}; + struct config { char *term; char *shell; @@ -85,12 +91,12 @@ struct config { config_font_list_t fonts[4]; /* Custom font metrics (-1 = use real font metrics) */ - int16_t line_height; - int16_t letter_spacing; + union pt_or_px line_height; + union pt_or_px letter_spacing; /* Adjusted letter x/y offsets */ - int16_t horizontal_letter_offset; - int16_t vertical_letter_offset; + union pt_or_px horizontal_letter_offset; + union pt_or_px vertical_letter_offset; struct { int lines; diff --git a/render.c b/render.c index 4e531207..51341365 100644 --- a/render.c +++ b/render.c @@ -256,7 +256,7 @@ color_dim_for_search(pixman_color_t *color) static inline int font_baseline(const struct terminal *term) { - return term->conf->vertical_letter_offset + term->fonts[0]->ascent; + return term->font_y_ofs + term->fonts[0]->ascent; } static void @@ -541,7 +541,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_image_t *clr_pix = pixman_image_create_solid_fill(&fg); if (glyph != NULL) { - const int letter_x_ofs = term->conf->horizontal_letter_offset; + const int letter_x_ofs = term->font_x_ofs; if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ @@ -1693,7 +1693,7 @@ render_osd(struct terminal *term, struct fcft_font *font = term->fonts[0]; pixman_color_t fg = color_hex_to_pixman(_fg); - const int x_ofs = term->conf->horizontal_letter_offset; + const int x_ofs = term->font_x_ofs; for (size_t i = 0; i < wcslen(text); i++) { const struct fcft_glyph *glyph = fcft_glyph_rasterize( @@ -2272,7 +2272,7 @@ render_search_box(struct terminal *term) struct fcft_font *font = term->fonts[0]; const int x_left = width - visible_width + margin; - const int x_ofs = term->conf->horizontal_letter_offset; + const int x_ofs = term->font_x_ofs; int x = x_left; int y = margin; pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]); diff --git a/terminal.c b/terminal.c index c23450fb..676f0cb4 100644 --- a/terminal.c +++ b/terminal.c @@ -629,15 +629,30 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) const int old_cell_width = term->cell_width; const int old_cell_height = term->cell_height; - term->cell_width = (term->fonts[0]->space_advance.x > 0 - ? term->fonts[0]->space_advance.x - : term->fonts[0]->max_advance.x) - + term->conf->letter_spacing; + const struct config *conf = term->conf; - term->cell_height = term->conf->line_height >= 0 - ? term->conf->line_height + term->cell_width = + (term->fonts[0]->space_advance.x > 0 + ? term->fonts[0]->space_advance.x + : term->fonts[0]->max_advance.x) + + (conf->letter_spacing.px > 0 + ? conf->letter_spacing.px + : conf->letter_spacing.pt * term->font_dpi / 72.); + + term->cell_height = conf->line_height.px >= 0 + ? (conf->line_height.px > 0 + ? conf->line_height.px + : conf->line_height.pt * term->font_dpi / 72.) : max(term->fonts[0]->height, term->fonts[0]->ascent + term->fonts[0]->descent); + + term->font_x_ofs = conf->horizontal_letter_offset.px > 0 + ? conf->horizontal_letter_offset.px + : conf->horizontal_letter_offset.pt * term->font_dpi / 72.; + term->font_y_ofs = conf->vertical_letter_offset.px > 0 + ? conf->vertical_letter_offset.px + : conf->vertical_letter_offset.pt * term->font_dpi / 72.; + LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); if (term->cell_width < old_cell_width || diff --git a/terminal.h b/terminal.h index 128694e7..19fff0e2 100644 --- a/terminal.h +++ b/terminal.h @@ -265,6 +265,8 @@ struct terminal { struct config_font *font_sizes[4]; float font_dpi; int font_scale; + int16_t font_x_ofs; + int16_t font_y_ofs; enum fcft_subpixel font_subpixel; /* From 17339410efee99002574b276fbedb5deeec081c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:07:42 +0100 Subject: [PATCH 13/18] doc: foot.ini: line-height, letter-spacing and offsets are in points (by default) --- doc/foot.ini.5.scd | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 32e989c4..4a45400a 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -65,24 +65,34 @@ in this order: *font-italic*, *font-bold-italic*). *line-height* - An absolute value, in pixels, that override line height from the + An absolute value, in _points_, that override line height from the font metrics. - See also: *vertical-letter-offset* + You can specify a height in _pixels_ by using the _px_ suffix: + e.g. *line-height=12px*. + + See also: *vertical-letter-offset*. Default: _no set_. *letter-spacing* - Spacing between letters. A positive value will increase the cell - size, and a negative value shrinks it. + Spacing between letters, in _points_. A positive value will + increase the cell size, and a negative value shrinks it. - See also: *horizontal-letter-offset* + You can specify a letter spacing in _pixels_ by using the _px_ + suffix: e.g. *letter-spacing=2px*. + + See also: *horizontal-letter-offset*. Default: _0_. *horizontal-letter-offset*, *vertical-letter-offset* Configure the horizontal and vertical offsets used when - positioning glyphs within cells, relative to the top left corner. + positioning glyphs within cells, in _points_, relative to the top + left corner. + + To specify an offset in _pixels_, append _px_: + e.g. *horizontal-letter-offset=2px*. Default: _0_. From 5ea37adaf407c63d8915a0ace2437bce46cf06cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:17:17 +0100 Subject: [PATCH 14/18] term: add pt_or_px_as_pixels() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function takes a ‘union pt_or_px’ and converts its value to a pixel value. --- terminal.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/terminal.c b/terminal.c index 676f0cb4..a9c98ed5 100644 --- a/terminal.c +++ b/terminal.c @@ -608,6 +608,15 @@ err_sem_destroy: return false; } +static int +pt_or_px_as_pixels(const struct terminal *term, + const union pt_or_px *pt_or_px) +{ + return pt_or_px->px == 0 + ? pt_or_px->pt * term->font_dpi / 72 + : pt_or_px->px; +} + static bool term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) { From 1830dfca55ff45398339ddd5493d55d02d990ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:19:34 +0100 Subject: [PATCH 15/18] term: use pt_or_px_as_pixels() --- terminal.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/terminal.c b/terminal.c index a9c98ed5..a5537732 100644 --- a/terminal.c +++ b/terminal.c @@ -644,23 +644,15 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) (term->fonts[0]->space_advance.x > 0 ? term->fonts[0]->space_advance.x : term->fonts[0]->max_advance.x) - + (conf->letter_spacing.px > 0 - ? conf->letter_spacing.px - : conf->letter_spacing.pt * term->font_dpi / 72.); + + pt_or_px_as_pixels(term, &conf->letter_spacing); term->cell_height = conf->line_height.px >= 0 - ? (conf->line_height.px > 0 - ? conf->line_height.px - : conf->line_height.pt * term->font_dpi / 72.) + ? pt_or_px_as_pixels(term, &conf->line_height) : max(term->fonts[0]->height, term->fonts[0]->ascent + term->fonts[0]->descent); - term->font_x_ofs = conf->horizontal_letter_offset.px > 0 - ? conf->horizontal_letter_offset.px - : conf->horizontal_letter_offset.pt * term->font_dpi / 72.; - term->font_y_ofs = conf->vertical_letter_offset.px > 0 - ? conf->vertical_letter_offset.px - : conf->vertical_letter_offset.pt * term->font_dpi / 72.; + term->font_x_ofs = pt_or_px_as_pixels(term, &conf->horizontal_letter_offset); + term->font_y_ofs = pt_or_px_as_pixels(term, &conf->vertical_letter_offset); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); From 25c2379d5f781ca05f784d9df25dd247d2193b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:19:43 +0100 Subject: [PATCH 16/18] box-drawing: x/y offsets: use pre-calculated offset values from term struct --- box-drawing.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index 6bfaef82..7ec7be09 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -2064,23 +2064,13 @@ box_drawing(const struct terminal *term, wchar_t wc) }; draw_glyph(wc, &buf); - const struct config *conf = term->conf; - - int x_ofs = conf->horizontal_letter_offset.px > 0 - ? conf->horizontal_letter_offset.px - : conf->horizontal_letter_offset.pt * term->font_dpi / 72.; - int y_ofs = conf->vertical_letter_offset.px > 0 - ? conf->vertical_letter_offset.px - : conf->vertical_letter_offset.pt * term->font_dpi / 72.; - - struct fcft_glyph *glyph = xmalloc(sizeof(*glyph)); *glyph = (struct fcft_glyph){ .wc = wc, .cols = 1, .pix = pix, - .x = -x_ofs, - .y = y_ofs + term->fonts[0]->ascent, + .x = -term->font_x_ofs, + .y = term->font_y_ofs + term->fonts[0]->ascent, .width = width, .height = height, .advance = { From 6d0cf814bc0ceb07e84baf23502c8970132bf954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Jan 2021 17:22:27 +0100 Subject: [PATCH 17/18] =?UTF-8?q?config:=20fix=20comment:=20-1=20is=20a=20?= =?UTF-8?q?valid=20=E2=80=98px=E2=80=99=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.h b/config.h index e9bc310f..18b0e0c0 100644 --- a/config.h +++ b/config.h @@ -54,7 +54,7 @@ struct config_mouse_binding { } pipe; }; -/* If px == -1, neither px nor pt is valid. If px == 0, pt is valid, else px */ +/* If px != 0 then px is valid, otherwise pt is valid */ union pt_or_px { int16_t px; float pt; From 44db2e2d0ae65b05062f9eaef018ca9e2fa8fdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Jan 2021 09:30:08 +0100 Subject: [PATCH 18/18] =?UTF-8?q?changelog:=20move=20line-height=20entry?= =?UTF-8?q?=20from=20=E2=80=98changed=E2=80=99=20to=20=E2=80=98added?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc667e7..cf5ec6bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ top/bottom side (https://codeberg.org/dnkl/foot/issues/273). * Completions for fish shell (https://codeberg.org/dnkl/foot/issues/11) +* `line-height`, `letter-spacing`, `horizontal-letter-offset` and + `vertical-letter-offset` to `foot.ini`. These options let you tweak + cell size and glyph positioning + (https://codeberg.org/dnkl/foot/issues/244). ### Changed @@ -43,10 +47,6 @@ (https://codeberg.org/dnkl/foot/issues/270) * Double- or triple clicking then dragging now extends the selection word- or line-wise (https://codeberg.org/dnkl/foot/issues/267). -* `line-height`, `letter-spacing`, `horizontal-letter-offset` and - `vertical-letter-offset` to `foot.ini`. These options let you tweak - cell size and glyph positioning - (https://codeberg.org/dnkl/foot/issues/244). ### Deprecated