From 753c4b5d4fb51574524af06ba56333f4241b1a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Jul 2023 20:07:05 +0200 Subject: [PATCH 1/4] render: round scaled border/title/button widths And calculation of compounded offsets/widths/heights, to compensate for compositor rounding when positioning and scaling/sizing subsurfaces. Closes #1441 --- render.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/render.c b/render.c index 6771fae5..ffac3d70 100644 --- a/render.c +++ b/render.c @@ -1808,33 +1808,49 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) const bool borders_visible = wayl_win_csd_borders_visible(term->window); const bool title_visible = wayl_win_csd_titlebar_visible(term->window); - /* Only title bar is rendered in maximized mode */ + const float scale = term->scale; + const int border_width = borders_visible - ? term->conf->csd.border_width * term->scale : 0; + ? roundf(term->conf->csd.border_width * scale) : 0; const int title_height = title_visible - ? term->conf->csd.title_height * term->scale : 0; + ? roundf(term->conf->csd.title_height * scale) : 0; const int button_width = title_visible - ? term->conf->csd.button_width * term->scale : 0; + ? roundf(term->conf->csd.button_width * scale) : 0; const int button_close_width = term->width >= 1 * button_width ? button_width : 0; const int button_maximize_width = term->width >= 2 * button_width && term->window->wm_capabilities.maximize - ? button_width : 0; + ? button_width : 0; const int button_minimize_width = term->width >= 3 * button_width && term->window->wm_capabilities.minimize - ? button_width : 0; + ? button_width : 0; + + /* + * With fractional scaling, we must ensure the offset, when + * divided by the scale (in set_position()), and the scaled back + * (by the compositor), matches the actual pixel count made up by + * the titlebar and the border. + */ + const int top_offset = roundf( + scale * (roundf(-title_height / scale) - roundf(border_width / scale))); + + const int top_bottom_width = roundf( + scale * (roundf(term->width / scale) + 2 * roundf(border_width / scale))); + + const int left_right_height = roundf( + scale * (roundf(title_height / scale) + roundf(term->height / scale))); switch (surf_idx) { - case CSD_SURF_TITLE: return (struct csd_data){ 0, -title_height, term->width, title_height}; - case CSD_SURF_LEFT: return (struct csd_data){-border_width, -title_height, border_width, title_height + term->height}; - case CSD_SURF_RIGHT: return (struct csd_data){ term->width, -title_height, border_width, title_height + term->height}; - case CSD_SURF_TOP: return (struct csd_data){-border_width, -title_height - border_width, term->width + 2 * border_width, border_width}; - case CSD_SURF_BOTTOM: return (struct csd_data){-border_width, term->height, term->width + 2 * border_width, border_width}; + case CSD_SURF_TITLE: return (struct csd_data){ 0, -title_height, term->width, title_height}; + case CSD_SURF_LEFT: return (struct csd_data){-border_width, -title_height, border_width, left_right_height}; + case CSD_SURF_RIGHT: return (struct csd_data){ term->width, -title_height, border_width, left_right_height}; + case CSD_SURF_TOP: return (struct csd_data){-border_width, top_offset, top_bottom_width, border_width}; + case CSD_SURF_BOTTOM: return (struct csd_data){-border_width, term->height, top_bottom_width, border_width}; /* Positioned relative to CSD_SURF_TITLE */ case CSD_SURF_MINIMIZE: return (struct csd_data){term->width - 3 * button_width, 0, button_minimize_width, title_height}; From 17824744812c16560dd214b98f28977b0d2fb47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 Jul 2023 15:28:10 +0200 Subject: [PATCH 2/4] fractional scaling: another round(!) of rounding fixes * Ensure buffer sizes are valid. That is, ensure that size / scale * scale == size. * Do size calculation of the window geometry in the same way we calculate the CSD offsets. --- render.c | 132 ++++++++++++++++++++++++++++-------------------------- wayland.c | 7 +-- 2 files changed, 73 insertions(+), 66 deletions(-) diff --git a/render.c b/render.c index ffac3d70..893b461a 100644 --- a/render.c +++ b/render.c @@ -2386,6 +2386,7 @@ render_csd(struct terminal *term) if (term->window->is_fullscreen) return; + const float scale = term->scale; struct csd_data infos[CSD_SURF_COUNT]; int widths[CSD_SURF_COUNT]; int heights[CSD_SURF_COUNT]; @@ -2413,11 +2414,7 @@ render_csd(struct terminal *term) widths[i] = width; heights[i] = height; - - wl_subsurface_set_position( - sub, - (int32_t)roundf(x / term->scale), - (int32_t)roundf(y / term->scale)); + wl_subsurface_set_position(sub, roundf(x / scale), roundf(y / scale)); } struct buffer *bufs[CSD_SURF_COUNT]; @@ -2518,16 +2515,14 @@ render_scrollback_position(struct terminal *term) break; } - const int iscale = (int)ceilf(term->scale); - const int margin = (int)roundf(3. * term->scale); + const float scale = term->scale; + const int margin = (int)roundf(3. * scale); int width = margin + cell_count * term->cell_width + margin; int height = margin + term->cell_height + margin; - if (!term_fractional_scaling(term)) { - width = (width + iscale - 1) / iscale * iscale; - height = (height + iscale - 1) / iscale * iscale; - } + width = roundf(scale * ceilf(width / scale)); + height = roundf(scale * ceilf(height / scale)); /* *Where* to render - parent relative coordinates */ int surf_top = 0; @@ -2558,10 +2553,8 @@ render_scrollback_position(struct terminal *term) int x = term->width - margin - width; int y = term->margins.top + surf_top; - if (!term_fractional_scaling(term)) { - x = (x + iscale - 1) / iscale * iscale; - y = (y + iscale - 1) / iscale * iscale; - } + x = roundf(scale * ceilf(x / scale)); + y = roundf(scale * ceilf(y / scale)); if (y + height > term->height) { wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0); @@ -2573,9 +2566,7 @@ render_scrollback_position(struct terminal *term) struct buffer *buf = shm_get_buffer(chain, width, height); wl_subsurface_set_position( - win->scrollback_indicator.sub, - (int32_t)roundf(x / term->scale), - (int32_t)roundf(y / term->scale)); + win->scrollback_indicator.sub, roundf(x / scale), roundf(y / scale)); uint32_t fg = term->colors.table[0]; uint32_t bg = term->colors.table[8 + 4]; @@ -2604,25 +2595,23 @@ render_render_timer(struct terminal *term, struct timespec render_time) char32_t text[256]; mbstoc32(text, usecs_str, ALEN(text)); - const int iscale = (int)ceilf(term->scale); + const float scale = term->scale; const int cell_count = c32len(text); - const int margin = (int)roundf(3. * term->scale); + const int margin = (int)roundf(3. * scale); int width = margin + cell_count * term->cell_width + margin; int height = margin + term->cell_height + margin; - if (!term_fractional_scaling(term)) { - width = (width + iscale - 1) / iscale * iscale; - height = (height + iscale - 1) / iscale * iscale; - } + width = roundf(scale * ceilf(width / scale)); + height = roundf(scale * ceilf(height / scale)); struct buffer_chain *chain = term->render.chains.render_timer; struct buffer *buf = shm_get_buffer(chain, width, height); wl_subsurface_set_position( win->render_timer.sub, - (int32_t)roundf(margin / term->scale), - (int32_t)roundf((term->margins.top + term->cell_height - margin) / term->scale)); + roundf(margin / scale), + roundf((term->margins.top + term->cell_height - margin) / scale)); render_osd( term, @@ -3167,24 +3156,22 @@ render_search_box(struct terminal *term) const size_t total_cells = c32swidth(text, text_len); const size_t wanted_visible_cells = max(20, total_cells); - xassert(term->scale >= 1.); - const size_t margin = (size_t)roundf(3 * term->scale); - - const size_t width = term->width - 2 * margin; - size_t visible_width = min( - term->width - 2 * margin, - margin + wanted_visible_cells * term->cell_width + margin); + const float scale = term->scale; + xassert(scale >= 1.); + const size_t margin = (size_t)roundf(3 * scale); + size_t width = term->width - 2 * margin; size_t height = min( term->height - 2 * margin, margin + 1 * term->cell_height + margin); - if (!term_fractional_scaling(term)) { - const int iscale = (int)ceilf(term->scale); - visible_width = (visible_width + iscale - 1) / iscale * iscale; - height = (height + iscale - 1) / iscale * iscale; - } - + width = roundf(scale * ceilf((term->width - 2 * margin) / scale)); + height = roundf(scale * ceilf(height / scale)); + + size_t visible_width = min( + term->width - 2 * margin, + margin + wanted_visible_cells * term->cell_width + margin); + const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width; size_t glyph_offset = term->render.search_glyph_offset; @@ -3430,10 +3417,10 @@ render_search_box(struct terminal *term) /* TODO: this is only necessary on a window resize */ wl_subsurface_set_position( term->window->search.sub, - (int32_t)roundf(margin / term->scale), - (int32_t)roundf(max(0, (int32_t)term->height - height - margin) / term->scale)); + roundf(margin / scale), + roundf(max(0, (int32_t)term->height - height - margin) / scale)); - wayl_surface_scale(term->window, &term->window->search.surface, buf, term->scale); + wayl_surface_scale(term->window, &term->window->search.surface, buf, scale); wl_surface_attach(term->window->search.surface.surf, buf->wl_buf, 0, 0); wl_surface_damage_buffer(term->window->search.surface.surf, 0, 0, width, height); @@ -3460,8 +3447,9 @@ render_urls(struct terminal *term) struct wl_window *win = term->window; xassert(tll_length(win->urls) > 0); - const int x_margin = (int)roundf(2 * term->scale); - const int y_margin = (int)roundf(1 * term->scale); + const float scale = term->scale; + const int x_margin = (int)roundf(2 * scale); + const int y_margin = (int)roundf(1 * scale); /* Calculate view start, counted from the *current* scrollback start */ const int scrollback_end @@ -3634,11 +3622,8 @@ render_urls(struct terminal *term) int width = x_margin + cols * term->cell_width + x_margin; int height = y_margin + term->cell_height + y_margin; - if (!term_fractional_scaling(term)) { - const int iscale = (int)ceilf(term->scale); - width = (width + iscale - 1) / iscale * iscale; - height = (height + iscale - 1) / iscale * iscale; - } + width = roundf(scale * ceilf(width / scale)); + height = roundf(scale * ceilf(height / scale)); info[render_count].url = &it->item; info[render_count].text = xc32dup(label); @@ -3674,8 +3659,8 @@ render_urls(struct terminal *term) wl_subsurface_set_position( sub_surf->sub, - (int32_t)roundf((term->margins.left + x) / term->scale), - (int32_t)roundf((term->margins.top + y) / term->scale)); + roundf((term->margins.left + x) / scale), + roundf((term->margins.top + y) / scale)); render_osd( term, sub_surf, term->fonts[0], bufs[i], label, @@ -3974,8 +3959,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int min_rows = 1; /* Minimum window size (must be divisible by the scaling factor)*/ - const int min_width = (min_cols * term->cell_width + scale - 1) / scale * scale; - const int min_height = (min_rows * term->cell_height + scale - 1) / scale * scale; + const int min_width = roundf(scale * ceilf((min_cols * term->cell_width) / scale)); + const int min_height = roundf(scale * ceilf((min_rows * term->cell_height) / scale)); width = max(width, min_width); height = max(height, min_height); @@ -4235,22 +4220,43 @@ damage_view: const bool title_shown = wayl_win_csd_titlebar_visible(term->window); const bool border_shown = wayl_win_csd_borders_visible(term->window); - const int title_height = - title_shown ? term->conf->csd.title_height : 0; - const int border_width = - border_shown ? term->conf->csd.border_width_visible : 0; + const int title = title_shown + ? roundf(term->conf->csd.title_height * scale) + : 0; + const int border = border_shown + ? roundf(term->conf->csd.border_width_visible * scale) + : 0; + + /* Must use surface logical coordinates (same calculations as + in get_csd_data(), but with different inputs) */ + const int toplevel_min_width = roundf(border / scale) + + roundf(min_width / scale) + + roundf(border / scale); + + const int toplevel_min_height = roundf(border / scale) + + roundf(title / scale) + + roundf(min_height / scale) + + roundf(border / scale); + + const int toplevel_width = roundf(border / scale) + + roundf(term->width / scale) + + roundf(border / scale); + + const int toplevel_height = roundf(border / scale) + + roundf(title / scale) + + roundf(term->height / scale) + + roundf(border / scale); + + const int x = roundf(-border / scale); + const int y = roundf(-title / scale) - roundf(border / scale); xdg_toplevel_set_min_size( term->window->xdg_toplevel, - min_width / scale + 2 * border_width, - min_height / scale + title_height + 2 * border_width); + toplevel_min_width, toplevel_min_height); xdg_surface_set_window_geometry( term->window->xdg_surface, - -border_width, - -title_height - border_width, - term->width / term->scale + 2 * border_width, - term->height / term->scale + title_height + 2 * border_width); + x, y, toplevel_width, toplevel_height); } tll_free(term->normal.scroll_damage); diff --git a/wayland.c b/wayland.c index 9a6609f4..66e06c10 100644 --- a/wayland.c +++ b/wayland.c @@ -2010,11 +2010,12 @@ wayl_surface_scale_explicit_width_height( LOG_DBG("scaling by a factor of %.2f using fractional scaling " "(width=%d, height=%d) ", scale, width, height); + xassert((int)roundf(scale * (int)roundf(width / scale)) == width); + xassert((int)roundf(scale * (int)roundf(height / scale)) == height); + wl_surface_set_buffer_scale(surf->surf, 1); wp_viewport_set_destination( - surf->viewport, - (int32_t)roundf((float)width / scale), - (int32_t)roundf((float)height / scale)); + surf->viewport, roundf(width / scale), roundf(height / scale)); #else BUG("wayl_fraction_scaling() returned true, " "but fractional scaling was not available at compile time"); From 764248bb0d846f65c20931024c1f4adca57aae29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Jul 2023 08:18:00 +0200 Subject: [PATCH 3/4] =?UTF-8?q?wayl=5Fsurface=5Fscale=5Fexplicit=5Fwidth?= =?UTF-8?q?=5Fheight():=20don=E2=80=99t=20assert=20width/height=20are=20va?= =?UTF-8?q?lid=20for=20scale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is only called directly when scaling the mouse pointer. The mouse pointer is never guaranteed to have a valid width and height, so skip the width/height assertions for it. --- wayland.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/wayland.c b/wayland.c index 66e06c10..c338bef8 100644 --- a/wayland.c +++ b/wayland.c @@ -2000,18 +2000,31 @@ wayl_roundtrip(struct wayland *wayl) wayl_flush(wayl); } -void -wayl_surface_scale_explicit_width_height( +static void +surface_scale_explicit_width_height( const struct wl_window *win, const struct wayl_surface *surf, - int width, int height, float scale) + int width, int height, float scale, bool verify) { if (term_fractional_scaling(win->term)) { #if defined(HAVE_FRACTIONAL_SCALE) LOG_DBG("scaling by a factor of %.2f using fractional scaling " "(width=%d, height=%d) ", scale, width, height); - xassert((int)roundf(scale * (int)roundf(width / scale)) == width); - xassert((int)roundf(scale * (int)roundf(height / scale)) == height); + if (verify) { + if ((int)roundf(scale * (int)roundf(width / scale)) != width) { + BUG("width=%d is not valid with scaling factor %.2f (%d != %d)", + width, scale, + (int)roundf(scale * (int)roundf(width / scale)), + width); + } + + if ((int)roundf(scale * (int)roundf(height / scale)) != height) { + BUG("height=%d is not valid with scaling factor %.2f (%d != %d)", + height, scale, + (int)roundf(scale * (int)roundf(height / scale)), + height); + } + } wl_surface_set_buffer_scale(surf->surf, 1); wp_viewport_set_destination( @@ -2034,12 +2047,20 @@ wayl_surface_scale_explicit_width_height( } } +void +wayl_surface_scale_explicit_width_height( + const struct wl_window *win, const struct wayl_surface *surf, + int width, int height, float scale) +{ + surface_scale_explicit_width_height(win, surf, width, height, scale, false); +} + void wayl_surface_scale(const struct wl_window *win, const struct wayl_surface *surf, const struct buffer *buf, float scale) { - wayl_surface_scale_explicit_width_height( - win, surf, buf->width, buf->height, scale); + surface_scale_explicit_width_height( + win, surf, buf->width, buf->height, scale, true); } void From aea687c0a1d6b14a302aeb7916358abb0ba10b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Jul 2023 09:09:59 +0200 Subject: [PATCH 4/4] changelog: CSDs with fractional scaling --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60ccef5..6a5bd8be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,11 +71,13 @@ ([#1430][1430]). * Crash when compositor does not implement the _viewporter_ interface ([#1444][1444]). +* CSD rendering with fractional scaling ([#1441][1441]). [1423]: https://codeberg.org/dnkl/foot/issues/1423 [1431]: https://codeberg.org/dnkl/foot/issues/1431 [1430]: https://codeberg.org/dnkl/foot/issues/1430 [1444]: https://codeberg.org/dnkl/foot/issues/1444 +[1441]: https://codeberg.org/dnkl/foot/issues/1441 ### Security