From ffcb09dd756532108759a830e545c8e84602c95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 15 Feb 2020 19:05:33 +0100 Subject: [PATCH 1/3] wayland: break out scale/resize updating to a new function And call this function once from output_done(), rather than from each output_*() callback. --- wayland.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/wayland.c b/wayland.c index 8a9b8012..8b529a9c 100644 --- a/wayland.c +++ b/wayland.c @@ -95,6 +95,31 @@ static const struct wl_seat_listener seat_listener = { .name = seat_handle_name, }; +static void +update_term_for_output_change(struct terminal *term) +{ + render_resize(term, term->width / term->scale, term->height / term->scale); + term_font_dpi_changed(term); + wayl_reload_cursor_theme(term->wl, term); +} + +static void +update_terms_on_monitor(struct monitor *mon) +{ + struct wayland *wayl = mon->wayl; + + tll_foreach(wayl->terms, it) { + struct terminal *term = it->item; + + tll_foreach(term->window->on_outputs, it2) { + if (it2->item == mon) { + update_term_for_output_change(term); + break; + } + } + } +} + static void output_update_ppi(struct monitor *mon) { @@ -133,22 +158,15 @@ output_mode(void *data, struct wl_output *wl_output, uint32_t flags, static void output_done(void *data, struct wl_output *wl_output) { + struct monitor *mon = data; + update_terms_on_monitor(mon); } static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) { struct monitor *mon = data; - mon->scale = factor; - - tll_foreach(mon->wayl->terms, it) { - struct terminal *term = it->item; - int scale = term->scale; - - render_resize(term, term->width / scale, term->height / scale); - wayl_reload_cursor_theme(mon->wayl, term); - } } static const struct wl_output_listener output_listener = { @@ -370,11 +388,7 @@ surface_enter(void *data, struct wl_surface *wl_surface, if (it->item.output == wl_output) { LOG_DBG("mapped on %s", it->item.name); tll_push_back(term->window->on_outputs, &it->item); - - /* Resize, since scale-to-use may have changed */ - int scale = term->scale; - render_resize(term, term->width / scale, term->height / scale); - wayl_reload_cursor_theme(term->wl, term); + update_term_for_output_change(term); return; } } @@ -395,11 +409,7 @@ surface_leave(void *data, struct wl_surface *wl_surface, LOG_DBG("unmapped from %s", it->item->name); tll_remove(term->window->on_outputs, it); - - /* Resize, since scale-to-use may have changed */ - int scale = term->scale; - render_resize(term, term->width / scale, term->height / scale); - wayl_reload_cursor_theme(term->wl, term); + update_term_for_output_change(term); return; } From 027696e9c6ac0cf4b6a7bd7905358104f9bed6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 15 Feb 2020 19:06:28 +0100 Subject: [PATCH 2/3] wayland: calculate DPI from physical size, not logical size xdg_output_handle_logical_size() reports _logical_ output size. That is, it is scaled by the output's scale factor. We want the _actual_ DPI. The _real_ output size is reported by output_mode(), so use that instead. --- wayland.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wayland.c b/wayland.c index 8b529a9c..8528f987 100644 --- a/wayland.c +++ b/wayland.c @@ -153,6 +153,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; } static void @@ -189,10 +191,6 @@ 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->width_px = width; - mon->height_px = height; - output_update_ppi(mon); } static void From 4d3ab6176dcf055e0f3b385aec0f4384e8cbba63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 15 Feb 2020 19:08:14 +0100 Subject: [PATCH 3/3] term: implement term_font_dpi_changed() This function reloads the font *if* the DPI has changed. To handle user run-time adjusted font sizes, we record the number of adjustments made. Then, when re-loading the font, we first load the font as specified in the configuration. Then, we re-apply the size adjustment using font_size_adjust(). Note that this means we end up loading the fonts twice; first using the default size (but with adjusted DPI), and then again with the adjusted size. This can probably be improved upon. The existing font code has been refactored to avoid code duplication. For example, term_init() now calls term_font_dpi_changed() to load the initial fonts, instead of directly instantiating them. Finally, the way we calculate the DPI to use has changed: instead of using the highest DPI of all available outputs, we use the highest DPI of the output's we're actually mapped on. If we're not mapped at all, we use the globally highest DPI. Doing it this way means we usually only have to load the fonts once. Otherwise, we'd end up using the default DPI of 96 when the terminal is first instantiated (since it's not mapped at that time). On a single monitor system, we'll use the globally highest DPI at first, before being mapped. Then when we get mapped, we re-load the fonts using the highest mapped DPI. But since they'll be the same, we can skip actually reloading the fonts. --- terminal.c | 194 ++++++++++++++++++++++++++++++++++++++--------------- terminal.h | 9 ++- 2 files changed, 147 insertions(+), 56 deletions(-) diff --git a/terminal.c b/terminal.c index bec0ce7b..89c18c75 100644 --- a/terminal.c +++ b/terminal.c @@ -507,8 +507,54 @@ initialize_render_workers(struct terminal *term) } static bool -initialize_fonts(const struct terminal *term, const struct config *conf, - struct font *fonts[static 4]) +term_set_fonts(struct terminal *term, struct font *fonts[static 4]) +{ + for (size_t i = 0; i < 4; i++) { + assert(fonts[i] != NULL); + + font_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_height = term->fonts[0]->height; + LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); + + render_resize_force(term, term->width, term->height); + return true; +} + +static unsigned +get_font_dpi(const struct terminal *term) +{ + /* 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 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; + break; + } + } + + if (dpi == 0) { + /* No monitors? */ + dpi = 96; + } + + return dpi; +} + +static bool +load_fonts_from_conf(const struct terminal *term, const struct config *conf, + struct font *fonts[static 4]) { const size_t count = tll_length(conf->fonts); const char *names[count]; @@ -517,18 +563,11 @@ initialize_fonts(const struct terminal *term, const struct config *conf, tll_foreach(conf->fonts, it) names[i++] = it->item; - /* Use highest DPI available */ - unsigned dpi = 96; - tll_foreach(term->wl->monitors, it) { - if (it->item.y_ppi > dpi) - dpi = it->item.y_ppi; - } - char attrs0[64], attrs1[64], attrs2[64], attrs3[64]; - snprintf(attrs0, sizeof(attrs0), "dpi=%u", dpi); - snprintf(attrs1, sizeof(attrs1), "dpi=%u:weight=bold", dpi); - snprintf(attrs2, sizeof(attrs2), "dpi=%u:slant=italic", dpi); - snprintf(attrs3, sizeof(attrs3), "dpi=%u:weight=bold:slant=italic", dpi); + snprintf(attrs0, sizeof(attrs0), "dpi=%u", term->font_dpi); + snprintf(attrs1, sizeof(attrs1), "dpi=%u:weight=bold", term->font_dpi); + snprintf(attrs2, sizeof(attrs2), "dpi=%u:slant=italic", term->font_dpi); + snprintf(attrs3, sizeof(attrs3), "dpi=%u:weight=bold:slant=italic", term->font_dpi); fonts[0] = fonts[1] = fonts[2] = fonts[3] = NULL; bool ret = @@ -619,6 +658,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .quit = false, .ptmx = ptmx, .ptmx_buffer = tll_init(), + .font_dpi = 0, + .font_adjustments = 0, .cursor_keys_mode = CURSOR_KEYS_NORMAL, .keypad_keys_mode = KEYPAD_NUMERICAL, .auto_margin = true, @@ -706,21 +747,16 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .cwd = strdup(cwd), }; + /* Guess scale; we're not mapped yet, so we don't know on which + * output we'll be. Pick highest scale we find for now */ + tll_foreach(term->wl->monitors, it) { + if (it->item.scale > term->scale) + term->scale = it->item.scale; + } + initialize_color_cube(term); if (!initialize_render_workers(term)) goto err; - if (!initialize_fonts(term, conf, term->fonts)) - goto err; - - /* Cell dimensions are based on the font metrics. Obviously */ - term->cell_width = term->fonts[0]->space_x_advance > 0 - ? term->fonts[0]->space_x_advance : term->fonts[0]->max_x_advance; - term->cell_height = term->fonts[0]->height; - LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); - - /* Start the slave/client */ - if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell)) == -1) - goto err; /* Initialize the Wayland window backend */ if ((term->window = wayl_win_init(term)) == NULL) @@ -729,9 +765,21 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, /* Let the Wayland backend know we exist */ tll_push_back(wayl->terms, term); wayl_roundtrip(term->wl); - term_set_window_title(term, "foot"); + /* Load fonts */ +#if 0 + struct font *fonts[4]; + if (!load_fonts_from_conf(term, conf, fonts)) + goto err; + term_set_fonts(term, fonts); +#endif + term_font_dpi_changed(term); + + /* Start the slave/client */ + if ((term->slave = slave_spawn(term->ptmx, argc, term->cwd, argv, term_env, conf->shell)) == -1) + goto err; + if (term->width == 0 && term->height == 0) { /* Try to use user-configured window dimentions */ @@ -1112,25 +1160,7 @@ term_reset(struct terminal *term, bool hard) term_damage_all(term); } -static void -term_set_fonts(struct terminal *term, struct font *fonts[static 4]) -{ - for (size_t i = 0; i < 4; i++) { - assert(fonts[i] != NULL); - - font_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_height = term->fonts[0]->height; - LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); - - render_resize_force(term, term->width, term->height); -} - -static void +static bool term_font_size_adjust(struct terminal *term, double amount) { struct font *fonts[4] = { @@ -1145,32 +1175,90 @@ term_font_size_adjust(struct terminal *term, double amount) { for (size_t i = 0; i < 4; i++) font_destroy(fonts[i]); - return; + return false; } term_set_fonts(term, fonts); + return true; } -void +bool term_font_size_increase(struct terminal *term) { - term_font_size_adjust(term, 0.5); + if (!term_font_size_adjust(term, 0.5)) + return false; + + term->font_adjustments++; + return true; } -void +bool term_font_size_decrease(struct terminal *term) { - term_font_size_adjust(term, -0.5); + if (!term_font_size_adjust(term, -0.5)) + return false; + + term->font_adjustments--; + return true; } -void +bool term_font_size_reset(struct terminal *term) { struct font *fonts[4]; - if (!initialize_fonts(term, term->conf, fonts)) - return; + if (!load_fonts_from_conf(term, term->conf, fonts)) + return false; term_set_fonts(term, fonts); + term->font_adjustments = 0; + return true; +} + +bool +term_font_dpi_changed(struct terminal *term) +{ + unsigned dpi = get_font_dpi(term); + if (dpi == term->font_dpi) + return true; + + LOG_DBG("DPI changed (%u -> %u): reloading fonts", term->font_dpi, dpi); + term->font_dpi = dpi; + + struct font *fonts[4]; + if (!load_fonts_from_conf(term, term->conf, fonts)) + return false; + + if (term->font_adjustments == 0) + return term_set_fonts(term, fonts); + + /* User has adjusted the font size run-time, re-apply */ + + 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), + }; + + 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]); + + /* 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]); + return term_set_fonts(term, adjusted_fonts); + } + + assert(false); + return false; } void diff --git a/terminal.h b/terminal.h index 1d19c9d7..d400dca5 100644 --- a/terminal.h +++ b/terminal.h @@ -187,6 +187,8 @@ struct terminal { struct grid *grid; struct font *fonts[4]; + int font_dpi; + int font_adjustments; tll(struct ptmx_buffer) ptmx_buffer; @@ -364,9 +366,10 @@ int term_destroy(struct terminal *term); void term_reset(struct terminal *term, bool hard); bool term_to_slave(struct terminal *term, const void *data, size_t len); -void term_font_size_increase(struct terminal *term); -void term_font_size_decrease(struct terminal *term); -void term_font_size_reset(struct terminal *term); +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_damage_rows(struct terminal *term, int start, int end); void term_damage_rows_in_view(struct terminal *term, int start, int end);