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;