Merge branch 'line-height-and-letter-spacing'

Closes #244
This commit is contained in:
Daniel Eklöf 2021-01-11 09:31:29 +01:00
commit 83fd67409b
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
9 changed files with 194 additions and 41 deletions

View file

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

View file

@ -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->font_x_ofs,
.y = term->font_y_ofs + term->fonts[0]->ascent,
.width = width,
.height = height,
.advance = {

View file

@ -387,18 +387,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;
}
@ -406,6 +409,40 @@ str_to_color(const char *s, uint32_t *color, bool allow_alpha, const char *path,
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)
@ -555,6 +592,32 @@ parse_section_main(const char *key, const char *value, struct config *conf,
free(copy);
}
else if (strcmp(key, "line-height") == 0) {
if (!str_to_pt_or_px(value, &conf->line_height,
conf, path, lineno, "default", "line-height"))
return false;
}
else if (strcmp(key, "letter-spacing") == 0) {
if (!str_to_pt_or_px(value, &conf->letter_spacing,
conf, path, lineno, "default", "letter-spacing"))
return false;
}
else if (strcmp(key, "horizontal-letter-offset") == 0) {
if (!str_to_pt_or_px(
value, &conf->horizontal_letter_offset,
conf, path, lineno, "default", "horizontal-letter-offset"))
return false;
}
else if (strcmp(key, "vertical-letter-offset") == 0) {
if (!str_to_pt_or_px(
value, &conf->horizontal_letter_offset,
conf, path, lineno, "default", "vertical-letter-offset"))
return false;
}
else if (strcmp(key, "dpi-aware") == 0) {
if (strcmp(value, "auto") == 0)
conf->dpi_aware = DPI_AWARE_AUTO;
@ -732,7 +795,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;
@ -767,8 +830,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);
@ -827,7 +890,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;
}
@ -858,7 +921,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;
}
@ -869,7 +932,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;
}
@ -880,7 +943,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;
}
@ -1979,6 +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 = { .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,

View file

@ -54,6 +54,12 @@ struct config_mouse_binding {
} pipe;
};
/* If px != 0 then px is valid, otherwise pt is valid */
union pt_or_px {
int16_t px;
float pt;
};
struct config {
char *term;
char *shell;
@ -84,6 +90,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) */
union pt_or_px line_height;
union pt_or_px letter_spacing;
/* Adjusted letter x/y offsets */
union pt_or_px horizontal_letter_offset;
union pt_or_px vertical_letter_offset;
struct {
int lines;

View file

@ -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
@ -51,6 +64,38 @@ in this order:
Default: _monospace:size=8_ (*font*), _not set_ (*font-bold*,
*font-italic*, *font-bold-italic*).
*line-height*
An absolute value, in _points_, that override line height from the
font metrics.
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, in _points_. A positive value will
increase the cell size, and a negative value shrinks it.
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, in _points_, relative to the top
left corner.
To specify an offset in _pixels_, append _px_:
e.g. *horizontal-letter-offset=2px*.
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
@ -111,19 +156,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_.

View file

@ -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=<bold variant of regular font>
# font-italic=<italic variant of regular font>
# font-bold-italic=<bold+italic variant of regular font>
# line-height=<font metrics>
# letter-spacing=0
# horizontal-letter-offset=0
# vertical-letter-offset=0
# dpi-aware=yes
# initial-window-size-pixels=700x500 # Or,
# initial-window-size-chars=<COLSxROWS>
# 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=<number of logical CPUs>
# bold-text-in-bright=no
# bell=none
# word-delimiters=,│`|:"'()[]{}<>
# notify=notify-send -a foot -i foot ${title} ${body}
# workers=<number of logical CPUs>
[scrollback]
# lines=1000

View file

@ -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->font_y_ofs + 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->font_x_ofs;
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->font_x_ofs;
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->font_x_ofs;
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);
}

View file

@ -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])
{
@ -629,10 +638,22 @@ 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);
const struct config *conf = term->conf;
term->cell_width =
(term->fonts[0]->space_advance.x > 0
? term->fonts[0]->space_advance.x
: term->fonts[0]->max_advance.x)
+ pt_or_px_as_pixels(term, &conf->letter_spacing);
term->cell_height = conf->line_height.px >= 0
? 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 = 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);
if (term->cell_width < old_cell_width ||

View file

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