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