diff --git a/CHANGELOG.md b/CHANGELOG.md index f495f562..1da50b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,12 @@ (https://codeberg.org/dnkl/foot/issues/4) * memfd sealing failures are no longer fatal errors. * Selection to no longer be cleared on resize. +* The current monitor's subpixel order (RGB/BGR/V-RGB/V-BGR) is + preferred over FontConfig's `rgba` property. Only if the monitor's + subpixel order is `unknown` is FontConfig's `rgba` property used. If + the subpixel order is `none`, then grayscale antialiasing is + used. The subpixel order is ignored if antialiasing has been + disabled. ### Deprecated diff --git a/PKGBUILD b/PKGBUILD index 53294f30..aae75222 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,8 +4,8 @@ pkgrel=1 arch=('x86_64') url=https://codeberg.org/dnkl/foot license=(mit) -makedepends=('meson' 'ninja' 'scdoc' 'python' 'ncurses' 'wayland-protocols' 'tllist>=1.0.0') -depends=('libxkbcommon' 'wayland' 'pixman' 'fcft>=1.1.1') +makedepends=('meson' 'ninja' 'scdoc' 'python' 'ncurses' 'wayland-protocols' 'tllist>=1.0.1') +depends=('libxkbcommon' 'wayland' 'pixman' 'fcft>=2.0.0') source=() pkgver() { diff --git a/meson.build b/meson.build index 115f8f9c..05128f70 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('foot', 'c', version: '1.2.3', license: 'MIT', - meson_version: '>=0.47.0', + meson_version: '>=0.53.0', default_options: [ 'c_std=c11', 'warning_level=1', @@ -57,8 +57,8 @@ wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') xkb = dependency('xkbcommon') -tllist = dependency('tllist', version: '>=1.0.0', fallback: ['tllist', 'tllist']) -fcft = dependency('fcft', version: ['>=1.1.1', '<1.2.0'], fallback: ['fcft', 'fcft']) +tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') +fcft = dependency('fcft', version: ['>=2.0.0', '<2.1.0'], fallback: 'fcft') wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir') diff --git a/render.c b/render.c index fdaaf85d..32f5669c 100644 --- a/render.c +++ b/render.c @@ -196,7 +196,7 @@ static const struct wp_presentation_feedback_listener presentation_feedback_list .discarded = &discarded, }; -static struct font * +static struct fcft_font * attrs_to_font(const struct terminal *term, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; @@ -263,7 +263,7 @@ draw_unfocused_block(const struct terminal *term, pixman_image_t *pix, static void draw_bar(const struct terminal *term, pixman_image_t *pix, - const struct font *font, + const struct fcft_font *font, const pixman_color_t *color, int x, int y) { int baseline = y + font_baseline(term) - term->fonts[0]->ascent; @@ -276,7 +276,7 @@ draw_bar(const struct terminal *term, pixman_image_t *pix, static void draw_underline(const struct terminal *term, pixman_image_t *pix, - const struct font *font, + const struct fcft_font *font, const pixman_color_t *color, int x, int y, int cols) { pixman_image_fill_rectangles( @@ -288,7 +288,7 @@ draw_underline(const struct terminal *term, pixman_image_t *pix, static void draw_strikeout(const struct terminal *term, pixman_image_t *pix, - const struct font *font, + const struct fcft_font *font, const pixman_color_t *color, int x, int y, int cols) { pixman_image_fill_rectangles( @@ -300,7 +300,7 @@ draw_strikeout(const struct terminal *term, pixman_image_t *pix, static void draw_cursor(const struct terminal *term, const struct cell *cell, - const struct font *font, pixman_image_t *pix, pixman_color_t *fg, + const struct fcft_font *font, pixman_image_t *pix, pixman_color_t *fg, const pixman_color_t *bg, int x, int y, int cols) { pixman_color_t cursor_color; @@ -402,9 +402,9 @@ render_cell(struct terminal *term, pixman_image_t *pix, color_dim_for_search(&bg); } - struct font *font = attrs_to_font(term, &cell->attrs); - const struct glyph *glyph = cell->wc != 0 - ? font_glyph_for_wc(font, cell->wc, term->colors.alpha == 0xffff) + struct fcft_font *font = attrs_to_font(term, &cell->attrs); + const struct fcft_glyph *glyph = cell->wc != 0 + ? fcft_glyph_rasterize(font, cell->wc, term->font_subpixel) : NULL; int cell_cols = glyph != NULL ? max(1, glyph->cols) : 1; @@ -1527,7 +1527,7 @@ render_search_box(struct terminal *term) PIXMAN_OP_SRC, buf->pix, &transparent, 1, &(pixman_rectangle16_t){0, 0, width - visible_width, height}); - struct font *font = term->fonts[0]; + struct fcft_font *font = term->fonts[0]; int x = width - visible_width + margin; int y = margin; pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]); @@ -1553,7 +1553,9 @@ render_search_box(struct terminal *term) if (i == term->search.cursor) draw_bar(term, buf->pix, font, &fg, x, y); - const struct glyph *glyph = font_glyph_for_wc(font, term->search.buf[i], true); + const struct fcft_glyph *glyph = fcft_glyph_rasterize( + font, term->search.buf[i], true); + if (glyph == NULL) continue; diff --git a/terminal.c b/terminal.c index 44234007..252dcffd 100644 --- a/terminal.c +++ b/terminal.c @@ -512,17 +512,17 @@ initialize_render_workers(struct terminal *term) } static bool -term_set_fonts(struct terminal *term, struct font *fonts[static 4]) +term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) { for (size_t i = 0; i < 4; i++) { assert(fonts[i] != NULL); - font_destroy(term->fonts[i]); + fcft_destroy(term->fonts[i]); term->fonts[i] = fonts[i]; } - term->cell_width = term->fonts[0]->space_x_advance > 0 - ? term->fonts[0]->space_x_advance : term->fonts[0]->max_x_advance; + 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); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); @@ -579,9 +579,53 @@ get_font_dpi(const struct terminal *term) return dpi; } +static enum fcft_subpixel +get_font_subpixel(const struct terminal *term) +{ + if (term->colors.alpha != 0xffff) { + /* Can't do subpixel rendering on transparent background */ + return FCFT_SUBPIXEL_NONE; + } + + enum wl_output_subpixel wl_subpixel; + + /* + * Wayland doesn't tell us *which* part of the surface that goes + * on a specific output, only whether the surface is mapped to an + * output or not. + * + * Thus, when determining which subpixel mode to use, we can't do + * much but select *an* output. So, we pick the first one. + * + * If we're not mapped at all, we pick the first available + * monitor, and hope that's where we'll eventually get mapped. + * + * If there aren't any monitors we use the "default" subpixel + * mode. + */ + + if (tll_length(term->window->on_outputs) > 0) + wl_subpixel = tll_front(term->window->on_outputs)->subpixel; + else if (tll_length(term->wl->monitors) > 0) + wl_subpixel = tll_front(term->wl->monitors).subpixel; + else + wl_subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + + switch (wl_subpixel) { + case WL_OUTPUT_SUBPIXEL_UNKNOWN: return FCFT_SUBPIXEL_DEFAULT; + case WL_OUTPUT_SUBPIXEL_NONE: return FCFT_SUBPIXEL_NONE; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return FCFT_SUBPIXEL_HORIZONTAL_RGB; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return FCFT_SUBPIXEL_HORIZONTAL_BGR; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return FCFT_SUBPIXEL_VERTICAL_RGB; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return FCFT_SUBPIXEL_VERTICAL_BGR; + } + + return FCFT_SUBPIXEL_DEFAULT; +} + static bool load_fonts_from_conf(const struct terminal *term, const struct config *conf, - struct font *fonts[static 4]) + struct fcft_font *fonts[static 4]) { const size_t count = tll_length(conf->fonts); const char *names[count]; @@ -598,15 +642,15 @@ load_fonts_from_conf(const struct terminal *term, const struct config *conf, fonts[0] = fonts[1] = fonts[2] = fonts[3] = NULL; bool ret = - (fonts[0] = font_from_name(count, names, attrs0)) != NULL && - (fonts[1] = font_from_name(count, names, attrs1)) != NULL && - (fonts[2] = font_from_name(count, names, attrs2)) != NULL && - (fonts[3] = font_from_name(count, names, attrs3)) != NULL; + (fonts[0] = fcft_from_name(count, names, attrs0)) != NULL && + (fonts[1] = fcft_from_name(count, names, attrs1)) != NULL && + (fonts[2] = fcft_from_name(count, names, attrs2)) != NULL && + (fonts[3] = fcft_from_name(count, names, attrs3)) != NULL; if (!ret) { LOG_ERR("failed to load primary fonts"); for (size_t i = 0; i < 4; i++) { - font_destroy(fonts[i]); + fcft_destroy(fonts[i]); fonts[i] = NULL; } } @@ -686,6 +730,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .ptmx_buffer = tll_init(), .font_dpi = 0, .font_adjustments = 0, + .font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */ + ? FCFT_SUBPIXEL_DEFAULT + : FCFT_SUBPIXEL_NONE), .cursor_keys_mode = CURSOR_KEYS_NORMAL, .keypad_keys_mode = KEYPAD_NUMERICAL, .auto_margin = true, @@ -795,6 +842,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, if (!term_font_dpi_changed(term)) goto err; + term->font_subpixel = get_font_subpixel(term); + term_set_window_title(term, conf->title); /* Let the Wayland backend know we exist */ @@ -998,7 +1047,7 @@ term_destroy(struct terminal *term) tll_free_and_free(term->window_title_stack, free); for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++) - font_destroy(term->fonts[i]); + fcft_destroy(term->fonts[i]); free(term->search.buf); @@ -1233,18 +1282,18 @@ term_reset(struct terminal *term, bool hard) static bool term_font_size_adjust(struct terminal *term, double amount) { - struct font *fonts[4] = { - font_size_adjust(term->fonts[0], amount), - font_size_adjust(term->fonts[1], amount), - font_size_adjust(term->fonts[2], amount), - font_size_adjust(term->fonts[3], amount), + struct fcft_font *fonts[4] = { + fcft_size_adjust(term->fonts[0], amount), + fcft_size_adjust(term->fonts[1], amount), + fcft_size_adjust(term->fonts[2], amount), + fcft_size_adjust(term->fonts[3], amount), }; if (fonts[0] == NULL || fonts[1] == NULL || fonts[2] == NULL || fonts[3] == NULL) { for (size_t i = 0; i < 4; i++) - font_destroy(fonts[i]); + fcft_destroy(fonts[i]); return false; } @@ -1275,7 +1324,7 @@ term_font_size_decrease(struct terminal *term) bool term_font_size_reset(struct terminal *term) { - struct font *fonts[4]; + struct fcft_font *fonts[4]; if (!load_fonts_from_conf(term, term->conf, fonts)) return false; @@ -1294,7 +1343,7 @@ term_font_dpi_changed(struct terminal *term) LOG_DBG("DPI changed (%u -> %u): reloading fonts", term->font_dpi, dpi); term->font_dpi = dpi; - struct font *fonts[4]; + struct fcft_font *fonts[4]; if (!load_fonts_from_conf(term, term->conf, fonts)) return false; @@ -1305,25 +1354,25 @@ term_font_dpi_changed(struct terminal *term) double amount = term->font_adjustments * 0.5; - struct font *adjusted_fonts[4] = { - font_size_adjust(fonts[0], amount), - font_size_adjust(fonts[1], amount), - font_size_adjust(fonts[2], amount), - font_size_adjust(fonts[3], amount), + struct fcft_font *adjusted_fonts[4] = { + fcft_size_adjust(fonts[0], amount), + fcft_size_adjust(fonts[1], amount), + fcft_size_adjust(fonts[2], amount), + fcft_size_adjust(fonts[3], amount), }; if (adjusted_fonts[0] == NULL || adjusted_fonts[1] == NULL || adjusted_fonts[2] == NULL || adjusted_fonts[3] == NULL) { for (size_t i = 0; i < 4; i++) - font_destroy(adjusted_fonts[i]); + fcft_destroy(adjusted_fonts[i]); /* At least use the newly re-loaded default fonts */ term->font_adjustments = 0; return term_set_fonts(term, fonts); } else { for (size_t i = 0; i < 4; i++) - font_destroy(fonts[i]); + fcft_destroy(fonts[i]); return term_set_fonts(term, adjusted_fonts); } @@ -1331,6 +1380,31 @@ term_font_dpi_changed(struct terminal *term) return false; } +void +term_font_subpixel_changed(struct terminal *term) +{ + enum fcft_subpixel subpixel = get_font_subpixel(term); + + if (term->font_subpixel == subpixel) + return; + +#if defined(_DEBUG) && LOG_ENABLE_DBG + static const char *const str[] = { + [FCFT_SUBPIXEL_ORDER_DEFAULT] = "default", + [FCFT_SUBPIXEL_ORDER_NONE] = "disabled", + [FCFT_SUBPIXEL_ORDER_HORIZONTAL_RGB] = "RGB", + [FCFT_SUBPIXEL_ORDER_HORIZONTAL_BGR] = "BGR", + [FCFT_SUBPIXEL_ORDER_VERTICAL_RGB] = "V-RGB", + [FCFT_SUBPIXEL_ORDER_VERTICAL_BGR] = "V-BGR", + }; +#endif + + LOG_DBG("subpixel mode changed: %s -> %s", str[term->font_subpixel], str[subpixel]); + term->font_subpixel = subpixel; + term_damage_view(term); + render_refresh(term); +} + void term_damage_rows(struct terminal *term, int start, int end) { diff --git a/terminal.h b/terminal.h index 7b7b0182..dbca5290 100644 --- a/terminal.h +++ b/terminal.h @@ -214,9 +214,10 @@ struct terminal { struct grid alt; struct grid *grid; - struct font *fonts[4]; + struct fcft_font *fonts[4]; int font_dpi; int font_adjustments; + enum fcft_subpixel font_subpixel; tll(struct ptmx_buffer) ptmx_buffer; @@ -445,6 +446,8 @@ bool term_font_size_increase(struct terminal *term); bool term_font_size_decrease(struct terminal *term); bool term_font_size_reset(struct terminal *term); bool term_font_dpi_changed(struct terminal *term); +void term_font_subpixel_changed(struct terminal *term); + void term_damage_rows(struct terminal *term, int start, int end); void term_damage_rows_in_view(struct terminal *term, int start, int end); diff --git a/wayland.c b/wayland.c index 06c02313..b0a5cfc3 100644 --- a/wayland.c +++ b/wayland.c @@ -146,6 +146,7 @@ update_term_for_output_change(struct terminal *term) render_resize(term, term->width / term->scale, term->height / term->scale); term_font_dpi_changed(term); + term_font_subpixel_changed(term); wayl_reload_cursor_theme(term->wl, term); } @@ -193,6 +194,7 @@ output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, mon->inch = sqrt(pow(mon->dim.mm.width, 2) + pow(mon->dim.mm.height, 2)) * 0.03937008; mon->make = make != NULL ? strdup(make) : NULL; mon->model = model != NULL ? strdup(model) : NULL; + mon->subpixel = subpixel; output_update_ppi(mon); } diff --git a/wayland.h b/wayland.h index e2abae72..a550b247 100644 --- a/wayland.h +++ b/wayland.h @@ -60,6 +60,7 @@ struct monitor { int scale; float refresh; + enum wl_output_subpixel subpixel; char *make; char *model;