diff --git a/csi.c b/csi.c index d3a76e7f..5579be41 100644 --- a/csi.c +++ b/csi.c @@ -1208,7 +1208,7 @@ csi_dispatch(struct terminal *term, uint8_t final) if (width >= 0 && height >= 0) { char reply[64]; size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt", - height / term->scale, width / term->scale); + (int)round(height / term->scale), (int)round(width / term->scale)); term_to_slave(term, reply, n); } break; @@ -1231,8 +1231,8 @@ csi_dispatch(struct terminal *term, uint8_t final) case 16: { /* report cell size in pixels */ char reply[64]; size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt", - term->cell_height / term->scale, - term->cell_width / term->scale); + (int)round(term->cell_height / term->scale), + (int)round(term->cell_width / term->scale)); term_to_slave(term, reply, n); break; } @@ -1249,8 +1249,8 @@ csi_dispatch(struct terminal *term, uint8_t final) tll_foreach(term->window->on_outputs, it) { char reply[64]; size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt", - it->item->dim.px_real.height / term->cell_height / term->scale, - it->item->dim.px_real.width / term->cell_width / term->scale); + (int)round(it->item->dim.px_real.height / term->cell_height / term->scale), + (int)round(it->item->dim.px_real.width / term->cell_width / term->scale)); term_to_slave(term, reply, n); break; } diff --git a/fractional-scale-v1.xml b/fractional-scale-v1.xml new file mode 100644 index 00000000..090e02e3 --- /dev/null +++ b/fractional-scale-v1.xml @@ -0,0 +1,113 @@ + + + + Copyright © 2022 Kenny Levinsen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows a compositor to suggest for surfaces to render at + fractional scales. + + A client can submit scaled content by utilizing wp_viewport. This is done by + creating a wp_viewport object for the surface and setting the destination + rectangle set to the surface size before the scale factor is applied. + + If the surface is not a subsurface, then the buffer size is calculated by + multiplying the surface size by the intended scale, rounding the result + halfway away from zero. + + If the surface is a subsurface, then the calculation must account for the + surface position when rounding the buffer size. First, add the subsurface + position relative to its parent to the surface size, multiplying them by the + intended scale and rounding the result halfway away from zero. Second, take + the subsurface position relative to its parent, multiplying it by the + intended scale and rounding the result halfway away from zero. Finally, + subtract the result of the second calculation from the first calculation to + get the buffer size. + + The wl_surface buffer scale should remain set to 1 in both cases. + + In the compositor, final subsurface positions are calculated by multiplying + the position of the subsurface relative to its parent by the intended scale, + and rounding the result halfway away from zero. The positions of the parent + surfaces, calculated using the same mechanism, are then recursively added to + the result. This ensures that position and size rounding is not affected by + the movement of parent surfaces. + + If a surface has a surface-local size of 100 px by 50 px and wishes to + submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should + be used and the wp_viewport destination rectangle should be 100 px by 50 px. + + + + + A global interface for requesting surfaces to use fractional scales. + + + + + Informs the server that the client will not be using this protocol + object anymore. This does not affect any other objects, + wp_fractional_scale_v1 objects included. + + + + + + + + + + Create an add-on object for the the wl_surface to let the compositor + request fractional scales. If the given wl_surface already has a + wp_fractional_scale_v1 object associated, the fractional_scale_exists + protocol error is raised. + + + + + + + + + An additional interface to a wl_surface object which allows the compositor + to inform the client of the preferred scale. + + + + + The associated wl_surface's fractional scale object is destroyed. + + + + + + Notification of a new preferred scale for this surface that the + compositor suggests that the client should use. + + + + + diff --git a/meson.build b/meson.build index c552d2a2..a1a23a64 100644 --- a/meson.build +++ b/meson.build @@ -107,6 +107,8 @@ wl_proto_xml = [ wayland_protocols_datadir + '/unstable/primary-selection/primary-selection-unstable-v1.xml', wayland_protocols_datadir + '/stable/presentation-time/presentation-time.xml', wayland_protocols_datadir + '/unstable/text-input/text-input-unstable-v3.xml', + wayland_protocols_datadir + '/stable/viewporter/viewporter.xml', + 'fractional-scale-v1.xml', ] if wayland_protocols.version().version_compare('>=1.21') diff --git a/render.c b/render.c index 31d5a61f..c6e78779 100644 --- a/render.c +++ b/render.c @@ -1753,15 +1753,15 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) } static void -csd_commit(struct terminal *term, struct wl_surface *surf, struct buffer *buf) +csd_commit(struct terminal *term, struct wl_surf_subsurf *surf, struct buffer *buf) { - xassert(buf->width % term->scale == 0); - xassert(buf->height % term->scale == 0); +// xassert(buf->width % term->scale == 0); +// xassert(buf->height % term->scale == 0); - wl_surface_attach(surf, buf->wl_buf, 0, 0); - wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(surf, term->scale); - wl_surface_commit(surf); + wl_surface_attach(surf->surf, buf->wl_buf, 0, 0); + wl_surface_damage_buffer(surf->surf, 0, 0, buf->width, buf->height); +// wl_surface_set_buffer_scale(surf, term->scale); + wl_surface_commit(surf->surf); } static void @@ -1851,13 +1851,14 @@ render_osd(struct terminal *term, pixman_image_unref(src); pixman_image_set_clip_region32(buf->pix[0], NULL); - xassert(buf->width % term->scale == 0); - xassert(buf->height % term->scale == 0); +// xassert(buf->width % term->scale == 0); +// xassert(buf->height % term->scale == 0); quirk_weston_subsurface_desync_on(sub_surf); + wl_surface_attach(surf, buf->wl_buf, 0, 0); wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height); - wl_surface_set_buffer_scale(surf, term->scale); +// wl_surface_set_buffer_scale(surf, term->scale); struct wl_region *region = wl_compositor_create_region(term->wl->compositor); if (region != NULL) { @@ -1880,8 +1881,8 @@ render_csd_title(struct terminal *term, const struct csd_data *info, if (info->width == 0 || info->height == 0) return; - xassert(info->width % term->scale == 0); - xassert(info->height % term->scale == 0); +// xassert(info->width % term->scale == 0); +// xassert(info->height % term->scale == 0); uint32_t bg = term->conf->csd.color.title_set ? term->conf->csd.color.title @@ -1909,7 +1910,7 @@ render_csd_title(struct terminal *term, const struct csd_data *info, buf, title_text, fg, bg, margin, (buf->height - win->csd.font->height) / 2); - csd_commit(term, surf->surf, buf); + csd_commit(term, surf, buf); free(_title_text); } @@ -1920,13 +1921,15 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx, xassert(term->window->csd_mode == CSD_YES); xassert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM); - struct wl_surface *surf = term->window->csd.surface[surf_idx].surf; + struct wl_surf_subsurf *ssurf + = &term->window->csd.surface[surf_idx]; + struct wl_surface *surf = ssurf->surf; if (info->width == 0 || info->height == 0) return; - xassert(info->width % term->scale == 0); - xassert(info->height % term->scale == 0); +// xassert(info->width % term->scale == 0); +// xassert(info->height % term->scale == 0); { pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0); @@ -1998,7 +2001,7 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx, &(pixman_rectangle16_t){x, y, w, h}); } - csd_commit(term, surf, buf); + csd_commit(term, ssurf, buf); } static pixman_color_t @@ -2174,13 +2177,15 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx, xassert(term->window->csd_mode == CSD_YES); xassert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE); - struct wl_surface *surf = term->window->csd.surface[surf_idx].surf; + struct wl_surf_subsurf *ssurf = + &term->window->csd.surface[surf_idx]; + struct wl_surface *surf = ssurf->surf; if (info->width == 0 || info->height == 0) return; - xassert(info->width % term->scale == 0); - xassert(info->height % term->scale == 0); +// xassert(info->width % term->scale == 0); +// xassert(info->height % term->scale == 0); uint32_t _color; uint16_t alpha = 0xffff; @@ -2242,7 +2247,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx, break; } - csd_commit(term, surf, buf); + csd_commit(term, ssurf, buf); } static void @@ -2264,24 +2269,30 @@ render_csd(struct terminal *term) const int width = infos[i].width; const int height = infos[i].height; - struct wl_surface *surf = term->window->csd.surface[i].surf; - struct wl_subsurface *sub = term->window->csd.surface[i].sub; + struct wl_surf_subsurf *surf = &term->window->csd.surface[i]; + struct wl_surface *wl_surf = surf->surf; + struct wl_subsurface *wl_sub = surf->sub; - xassert(surf != NULL); - xassert(sub != NULL); + xassert(wl_surf != NULL); + xassert(wl_sub != NULL); if (width == 0 || height == 0) { widths[i] = heights[i] = 0; - wl_subsurface_set_position(sub, 0, 0); - wl_surface_attach(surf, NULL, 0, 0); - wl_surface_commit(surf); + wp_viewport_set_destination(surf->viewport, -1, -1); + wl_subsurface_set_position(wl_sub, 0, 0); + wl_surface_attach(wl_surf, NULL, 0, 0); + wl_surface_commit(wl_surf); continue; } - widths[i] = width; - heights[i] = height; + widths[i] = round(x + width) - round(x); + heights[i] = round(y + height) - round(y); - wl_subsurface_set_position(sub, x / term->scale, y / term->scale); + wp_viewport_set_destination(surf->viewport, + round(width / term->scale), + round(height / term->scale)); + + wl_subsurface_set_position(wl_sub, x / term->scale, y / term->scale); } struct buffer *bufs[CSD_SURF_COUNT]; @@ -2377,13 +2388,13 @@ render_scrollback_position(struct terminal *term) break; } - const int scale = term->scale; - const int margin = 3 * scale; + const double scale = term->scale; + const double margin = 3 * scale; const int width = - (2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale; + round(2.0 * margin + cell_count * term->cell_width); const int height = - (2 * margin + term->cell_height + scale - 1) / scale * scale; + round(2.0 * margin + term->cell_height); /* *Where* to render - parent relative coordinates */ int surf_top = 0; @@ -2920,7 +2931,7 @@ grid_render(struct terminal *term) term->window->frame_callback = wl_surface_frame(term->window->surface); wl_callback_add_listener(term->window->frame_callback, &frame_listener, term); - wl_surface_set_buffer_scale(term->window->surface, term->scale); +// wl_surface_set_buffer_scale(term->window->surface, term->scale); if (term->wl->presentation != NULL && term->conf->presentation_timings) { struct timespec commit_time; @@ -2954,8 +2965,11 @@ grid_render(struct terminal *term) term->window->surface, 0, 0, INT32_MAX, INT32_MAX); } - xassert(buf->width % term->scale == 0); - xassert(buf->height % term->scale == 0); +// xassert(buf->width % term->scale == 0); +// xassert(buf->height % term->scale == 0); + wp_viewport_set_destination(term->window->viewport, + round(buf->width / term->scale), + round(buf->height / term->scale)); wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0); wl_surface_commit(term->window->surface); @@ -3019,11 +3033,13 @@ 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); +// xassert(term->scale >= 1); const int scale = term->scale; const size_t margin = 3 * scale; + // These calculations do not round correctly right now + const size_t width = term->width - 2 * margin; const size_t visible_width = min( term->width - 2 * margin, @@ -3265,8 +3281,11 @@ render_search_box(struct terminal *term) margin / scale, max(0, (int32_t)term->height - height - margin) / scale); - xassert(buf->width % scale == 0); - xassert(buf->height % scale == 0); +// xassert(buf->width % scale == 0); +// xassert(buf->height % scale == 0); + wp_viewport_set_destination(term->window->search.viewport, + round(buf->width / term->scale), + round(buf->height / term->scale)); wl_surface_attach(term->window->search.surf, buf->wl_buf, 0, 0); wl_surface_damage_buffer(term->window->search.surf, 0, 0, width, height); @@ -3681,17 +3700,20 @@ maybe_resize(struct terminal *term, int width, int height, bool force) if (term->cell_width == 0 && term->cell_height == 0) return false; - int scale = -1; - tll_foreach(term->window->on_outputs, it) { - if (it->item->scale > scale) - scale = it->item->scale; + double scale = -1; + if (!term->window->fractional_scale) { + tll_foreach(term->window->on_outputs, it) { + if (it->item->scale > scale) + scale = it->item->scale; + } + } else { + scale = term->window->scale; } if (scale < 0) { /* Haven't 'entered' an output yet? */ scale = term->scale; } - width *= scale; height *= scale; @@ -3735,13 +3757,13 @@ maybe_resize(struct terminal *term, int width, int height, bool force) * Ensure we can scale to logical size, and back to * pixels without truncating. */ - if (width % scale) - width += scale - width % scale; - if (height % scale) - height += scale - height % scale; +// if (width % scale) +// width += scale - width % scale; +// if (height % scale) +// height += scale - height % scale; - xassert(width % scale == 0); - xassert(height % scale == 0); +// xassert(width % scale == 0); +// xassert(height % scale == 0); break; } } @@ -3911,8 +3933,8 @@ damage_view: 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); + round(term->width / term->scale + 2 * border_width), + round(term->height / term->scale + title_height + 2 * border_width)); } tll_free(term->normal.scroll_damage); diff --git a/terminal.c b/terminal.c index 389024f8..b8c20ec5 100644 --- a/terminal.c +++ b/terminal.c @@ -902,11 +902,10 @@ reload_fonts(struct terminal *term) bool use_px_size = term->font_sizes[i][j].px_size > 0; char size[64]; - const int scale = term->font_is_sized_by_dpi ? 1 : term->scale; - + const double scale = term->font_is_sized_by_dpi ? 1 : term->scale; if (use_px_size) snprintf(size, sizeof(size), ":pixelsize=%d", - term->font_sizes[i][j].px_size * scale); + (int)round(term->font_sizes[i][j].px_size * scale)); else snprintf(size, sizeof(size), ":size=%.2f", term->font_sizes[i][j].pt_size * (double)scale); diff --git a/terminal.h b/terminal.h index ea86db24..cd08ee18 100644 --- a/terminal.h +++ b/terminal.h @@ -445,7 +445,7 @@ struct terminal { int fd; } blink; - int scale; + double scale; int width; /* pixels */ int height; /* pixels */ int stashed_width; diff --git a/wayland.c b/wayland.c index d65a571b..94831fb0 100644 --- a/wayland.c +++ b/wayland.c @@ -31,12 +31,12 @@ #include "xmalloc.h" static void -csd_reload_font(struct wl_window *win, int old_scale) +csd_reload_font(struct wl_window *win, double old_scale) { struct terminal *term = win->term; const struct config *conf = term->conf; - const int scale = term->scale; + const double scale = term->scale; bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen; if (!enable_csd) @@ -52,9 +52,9 @@ csd_reload_font(struct wl_window *win, int old_scale) char pixelsize[32]; snprintf(pixelsize, sizeof(pixelsize), - "pixelsize=%u", conf->csd.title_height * scale * 1 / 2); + "pixelsize=%u", (int)round(conf->csd.title_height * scale * 1 / 2)); - LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%d, scale=%d)", + LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%lf, scale=%lf)", patterns[0], pixelsize, old_scale, scale); win->csd.font = fcft_from_name(conf->csd.font.count, patterns, pixelsize); @@ -317,9 +317,9 @@ update_term_for_output_change(struct terminal *term) if (tll_length(term->window->on_outputs) == 0) return; - int old_scale = term->scale; + double old_scale = term->scale; - render_resize(term, term->width / term->scale, term->height / term->scale); + render_resize(term, round(term->width / term->scale), round(term->height / term->scale)); term_font_dpi_changed(term, old_scale); term_font_subpixel_changed(term); csd_reload_font(term->window, old_scale); @@ -812,6 +812,21 @@ static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration .configure = &xdg_toplevel_decoration_configure, }; +static void fractional_scale_handle_preferred_scale(void *data, struct + wp_fractional_scale_v1 *info, wl_fixed_t scale) { + struct wl_window *win = data; + + double dscale = wl_fixed_to_double(scale); + fprintf(stderr, "Preferred scale: %lf\n", dscale); + + win->scale = dscale; + update_term_for_output_change(win->term); +} + +static const struct wp_fractional_scale_v1_listener fractional_scale_listener = { + .preferred_scale = fractional_scale_handle_preferred_scale, +}; + static bool fdm_repeat(struct fdm *fdm, int fd, int events, void *data) { @@ -1049,6 +1064,27 @@ handle_global(void *data, struct wl_registry *registry, seat_add_text_input(&it->item); } #endif + + else if (strcmp(interface, + wp_fractional_scale_manager_v1_interface.name) == 0) + { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->fractional_scale_manager = wl_registry_bind( + wayl->registry, name, + &wp_fractional_scale_manager_v1_interface, required); + } + + else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->viewporter = wl_registry_bind( + wayl->registry, name, &wp_viewporter_interface, required); + } } static void @@ -1420,6 +1456,16 @@ wayl_win_init(struct terminal *term, const char *token) wl_surface_add_listener(win->surface, &surface_listener, win); + if (wayl->fractional_scale_manager) { + win->fractional_scale = + wp_fractional_scale_manager_v1_get_fractional_scale(wayl->fractional_scale_manager, + win->surface); + wp_fractional_scale_v1_add_listener(win->fractional_scale, + &fractional_scale_listener, win); + } + win->viewport = wp_viewporter_get_viewport(wayl->viewporter, + win->surface); + win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface); xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win); @@ -1540,6 +1586,9 @@ wayl_win_destroy(struct wl_window *win) wayl_roundtrip(win->term->wl); + wp_viewport_set_destination(win->viewport, + win->configure.width, win->configure.height); + /* Main window */ wl_surface_attach(win->surface, NULL, 0, 0); wl_surface_commit(win->surface); @@ -1809,6 +1858,9 @@ wayl_win_subsurface_new_with_custom_parent( surf->surf = main_surface; surf->sub = sub; + surf->viewport = wp_viewporter_get_viewport(wayl->viewporter, + main_surface); + return true; } diff --git a/wayland.h b/wayland.h index f0c9baa3..5e77ea65 100644 --- a/wayland.h +++ b/wayland.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include #if defined(HAVE_XDG_ACTIVATION) #include @@ -131,7 +133,7 @@ struct seat { struct wl_surface *surface; struct wl_cursor_theme *theme; struct wl_cursor *cursor; - int scale; + double scale; bool hidden; const char *xcursor; @@ -288,6 +290,7 @@ struct monitor { struct wl_surf_subsurf { struct wl_surface *surf; struct wl_subsurface *sub; + struct wp_viewport *viewport; }; struct wl_url { @@ -306,6 +309,8 @@ struct wl_window { #if defined(HAVE_XDG_ACTIVATION) struct xdg_activation_token_v1 *xdg_activation_token; #endif + struct wp_viewport *viewport; + struct wp_fractional_scale_v1 *fractional_scale; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; @@ -352,6 +357,7 @@ struct wl_window { } configure; int resize_timeout_fd; + double scale; }; struct terminal; @@ -383,6 +389,10 @@ struct wayland { struct wp_presentation *presentation; uint32_t presentation_clock_id; + struct wp_viewporter *viewporter; + struct wp_fractional_scale_manager_v1 + *fractional_scale_manager; + #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED struct zwp_text_input_manager_v3 *text_input_manager; #endif