From acecab1c8bcb414521867fce46d238ad48f4b621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 11 Mar 2020 16:10:55 +0100 Subject: [PATCH] term: use logical DPI+scale factor when scaling fonts This fixes an issue where the fonts were rendered too small when the output had fractional scaling. For integral scaling, using the logical (scaled) DPI multiplied with the scaling factor results in the same final DPI value as if we had used the physical DPI. But for fractional scaling, this works around the fact that the compositor downscales the surface after we've rendered it. Closes #5 --- terminal.c | 27 ++++++++++++++++++++++++--- wayland.c | 39 +++++++++++++++++++++++++-------------- wayland.h | 37 +++++++++++++++++++++++++++++++------ 3 files changed, 80 insertions(+), 23 deletions(-) diff --git a/terminal.c b/terminal.c index 2a742268..f7219480 100644 --- a/terminal.c +++ b/terminal.c @@ -532,18 +532,39 @@ term_set_fonts(struct terminal *term, struct font *fonts[static 4]) static unsigned get_font_dpi(const struct terminal *term) { + /* + * Use output's DPI to scale font. This is to ensure the font has + * the same physical height (if measured by a ruler) regardless of + * monitor. + * + * Conceptually, we use the physical monitor specs to calculate + * the DPI, and we ignore the output's scaling factor. + * + * However, to deal with fractional scaling, where we're told to + * render at e.g. 2x, but are then downscaled by the compositor to + * e.g. 1.25, we use the scaled DPI value multiplied by the scale + * factor instead. + * + * For integral scaling factors the resulting DPI is the same as + * if we had used the physical DPI. + * + * For fractional scaling factors we'll get a DPI *larger* than + * the physical DPI, that ends up being right when later + * downscaled by the compositor. + */ + /* Use highest DPI from outputs we're mapped on */ unsigned dpi = 0; assert(term->window != NULL); tll_foreach(term->window->on_outputs, it) { - if (it->item->y_ppi > dpi) - dpi = it->item->y_ppi; + if (it->item->ppi.scaled.y > dpi) + dpi = it->item->ppi.scaled.y * term->scale; } /* If we're not mapped, use DPI from first monitor. Hopefully this is where we'll get mapped later... */ if (dpi == 0) { tll_foreach(term->wl->monitors, it) { - dpi = it->item.y_ppi; + dpi = it->item.ppi.scaled.y * term->scale; break; } } diff --git a/wayland.c b/wayland.c index 322188ae..f915673e 100644 --- a/wayland.c +++ b/wayland.c @@ -169,10 +169,16 @@ update_terms_on_monitor(struct monitor *mon) static void output_update_ppi(struct monitor *mon) { - int x_inches = mon->width_mm * 0.03937008; - int y_inches = mon->height_mm * 0.03937008; - mon->x_ppi = mon->width_px / x_inches; - mon->y_ppi = mon->height_px / y_inches; + if (mon->dim.mm.width == 0 || mon->dim.mm.height == 0) + return; + + int x_inches = mon->dim.mm.width * 0.03937008; + int y_inches = mon->dim.mm.height * 0.03937008; + mon->ppi.real.x = mon->dim.px_real.width / x_inches; + mon->ppi.real.y = mon->dim.px_real.height / y_inches; + + mon->ppi.scaled.x = mon->dim.px_scaled.width / x_inches; + mon->ppi.scaled.y = mon->dim.px_scaled.height / y_inches; } static void @@ -182,9 +188,9 @@ output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t transform) { struct monitor *mon = data; - mon->width_mm = physical_width; - mon->height_mm = physical_height; - mon->inch = sqrt(pow(mon->width_mm, 2) + pow(mon->height_mm, 2)) * 0.03937008; + mon->dim.mm.width = physical_width; + mon->dim.mm.height = physical_height; + 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; output_update_ppi(mon); @@ -199,8 +205,8 @@ output_mode(void *data, struct wl_output *wl_output, uint32_t flags, struct monitor *mon = data; mon->refresh = (float)refresh / 1000; - mon->width_px = width; - mon->height_px = height; + mon->dim.px_real.width = width; + mon->dim.px_real.height = height; output_update_ppi(mon); } @@ -238,6 +244,10 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { + struct monitor *mon = data; + mon->dim.px_scaled.width = width; + mon->dim.px_scaled.height = height; + output_update_ppi(mon); } static void @@ -786,11 +796,12 @@ wayl_init(const struct config *conf, struct fdm *fdm) tll_foreach(wayl->monitors, it) { LOG_INFO( - "%s: %dx%d+%dx%d@%dHz %s (%.2f\", PPI=%dx%d, scale=%d)", - it->item.name, it->item.width_px, it->item.height_px, - it->item.x, it->item.y, (int)round(it->item.refresh), it->item.model, it->item.inch, - it->item.x_ppi, it->item.y_ppi, - it->item.scale); + "%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d PPI=%dx%d (physical) PPI=%dx%d (logical)", + it->item.name, it->item.dim.px_real.width, it->item.dim.px_real.height, + it->item.x, it->item.y, (int)round(it->item.refresh), + it->item.model, it->item.inch, it->item.scale, + it->item.ppi.real.x, it->item.ppi.real.y, + it->item.ppi.scaled.x, it->item.ppi.scaled.y); } /* Clipboard */ diff --git a/wayland.h b/wayland.h index ed53591d..7d7b36ca 100644 --- a/wayland.h +++ b/wayland.h @@ -24,14 +24,39 @@ struct monitor { int x; int y; - int width_mm; - int height_mm; + struct { + /* Physical size, in mm */ + struct { + int width; + int height; + } mm; - int width_px; - int height_px; + /* Physical size, in pixels */ + struct { + int width; + int height; + } px_real; - int x_ppi; - int y_ppi; + /* Scaled size, in pixels */ + struct { + int width; + int height; + } px_scaled; + } dim; + + struct { + /* PPI, based on physical size */ + struct { + int x; + int y; + } real; + + /* PPI, logical, based on scaled size */ + struct { + int x; + int y; + } scaled; + } ppi; int scale; float refresh;