From 40ac6f22a3d2012488e54f72335ea9e9d7cf34a8 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 17:31:47 +0200 Subject: [PATCH 1/6] wayland: Introduce GdkFractionalScale type We want to use it later for various things, this just adds the infrastructure we will need. --- gdk/wayland/gdkprivate-wayland.h | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index 73b934d371..0276d4c70d 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -47,6 +47,45 @@ */ #define GDK_WAYLAND_LOCAL_DND_MIME_TYPE "application/x-gtk-local-dnd" +#define GDK_FRACTIONAL_SCALE_FACTOR 120 + +typedef struct _GdkFractionalScale GdkFractionalScale; + +struct _GdkFractionalScale +{ + guint32 scale; +}; + +#define GDK_FRACTIONAL_SCALE_INIT(fractional_scale) (GdkFractionalScale) { fractional_scale } +#define GDK_FRACTIONAL_SCALE_INIT_INT(scale) GDK_FRACTIONAL_SCALE_INIT (scale * GDK_FRACTIONAL_SCALE_FACTOR) + +static inline int +gdk_fractional_scale_to_int (const GdkFractionalScale *self) +{ + /* ceil() */ + return (self->scale + GDK_FRACTIONAL_SCALE_FACTOR - 1) / GDK_FRACTIONAL_SCALE_FACTOR; +} + +static inline double +gdk_fractional_scale_to_double (const GdkFractionalScale *self) +{ + return (double) self->scale / GDK_FRACTIONAL_SCALE_FACTOR; +} + +static inline int +gdk_fractional_scale_scale (const GdkFractionalScale *self, + int value) +{ + return (value * self->scale + GDK_FRACTIONAL_SCALE_FACTOR / 2) / GDK_FRACTIONAL_SCALE_FACTOR; +} + +static inline gboolean +gdk_fractional_scale_equal (const GdkFractionalScale *a, + const GdkFractionalScale *b) +{ + return a->scale == b->scale; +} + GdkKeymap *_gdk_wayland_keymap_new (GdkDisplay *display); void _gdk_wayland_keymap_update_from_fd (GdkKeymap *keymap, uint32_t format, From 8dd5d649b8848addba260db60a77236106b1a0ba Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 13:40:14 +0200 Subject: [PATCH 2/6] wayland: Use GdkFractionalScale for surface scale We still always round it to integers when we read it, but we store it as a fraction. So we could now use it for fractional scaling. --- gdk/wayland/gdkdragsurface-wayland.c | 2 +- gdk/wayland/gdksurface-wayland-private.h | 12 +- gdk/wayland/gdksurface-wayland.c | 143 +++++++++++------------ gdk/wayland/gdktoplevel-wayland.c | 4 +- 4 files changed, 80 insertions(+), 81 deletions(-) diff --git a/gdk/wayland/gdkdragsurface-wayland.c b/gdk/wayland/gdkdragsurface-wayland.c index 22220a49e4..a52b7e6148 100644 --- a/gdk/wayland/gdkdragsurface-wayland.c +++ b/gdk/wayland/gdkdragsurface-wayland.c @@ -93,7 +93,7 @@ gdk_wayland_drag_surface_compute_size (GdkSurface *surface) gdk_wayland_surface_update_size (surface, impl->next_layout.configured_width, impl->next_layout.configured_height, - impl->scale); + &impl->scale); impl->next_layout.surface_geometry_dirty = FALSE; } diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index 4689945cc7..e6c08a4bb4 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -50,7 +50,7 @@ struct _GdkWaylandSurface int pending_buffer_offset_y; gint64 pending_frame_counter; - guint32 scale; + GdkFractionalScale scale; gboolean buffer_scale_dirty; int shadow_left; @@ -99,11 +99,11 @@ struct _GdkWaylandSurfaceClass #define GDK_WAYLAND_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass)) -void gdk_wayland_surface_create_wl_surface (GdkSurface *surface); -void gdk_wayland_surface_update_size (GdkSurface *surface, - int32_t width, - int32_t height, - int scale); +void gdk_wayland_surface_create_wl_surface (GdkSurface *surface); +void gdk_wayland_surface_update_size (GdkSurface *surface, + int32_t width, + int32_t height, + const GdkFractionalScale *scale); void gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface); void _gdk_wayland_surface_save_size (GdkSurface *surface); diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 7c952af083..2581015a2e 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -59,11 +59,6 @@ G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE) -static void gdk_wayland_surface_maybe_resize (GdkSurface *surface, - int width, - int height, - int scale); - static void gdk_wayland_surface_configure (GdkSurface *surface); static void gdk_wayland_surface_sync_shadow (GdkSurface *surface); @@ -180,7 +175,7 @@ wl_region_from_cairo_region (GdkWaylandDisplay *display, static void gdk_wayland_surface_init (GdkWaylandSurface *impl) { - impl->scale = 1; + impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (1); } void @@ -207,30 +202,66 @@ gdk_wayland_surface_thaw_state (GdkSurface *surface) gdk_wayland_surface_configure (surface); } +static void +gdk_wayland_surface_maybe_resize (GdkSurface *surface, + int width, + int height, + const GdkFractionalScale *scale) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + gboolean hide_temporarily; + + if (surface->width == width && + surface->height == height && + gdk_fractional_scale_equal (&impl->scale, scale)) + return; + + /* For xdg_popup using an xdg_positioner, there is a race condition if + * the application tries to change the size after it's mapped, but before + * the initial configure is received, so hide and show the surface again + * force the new size onto the compositor. See bug #772505. + */ + hide_temporarily = GDK_IS_WAYLAND_POPUP (surface) && + gdk_surface_get_mapped (surface) && + !impl->initial_configure_received; + + if (hide_temporarily) + gdk_surface_hide (surface); + + gdk_wayland_surface_update_size (surface, width, height, scale); + + if (hide_temporarily) + gdk_wayland_surface_create_wl_surface (surface); +} + void -gdk_wayland_surface_update_size (GdkSurface *surface, - int32_t width, - int32_t height, - int scale) +gdk_wayland_surface_update_size (GdkSurface *surface, + int32_t width, + int32_t height, + const GdkFractionalScale *scale) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); gboolean width_changed, height_changed, scale_changed; width_changed = surface->width != width; height_changed = surface->height != height; - scale_changed = impl->scale != scale; + scale_changed = !gdk_fractional_scale_equal (&impl->scale, scale); if (!width_changed && !height_changed && !scale_changed) return; surface->width = width; surface->height = height; - impl->scale = scale; + if (scale_changed) + { + impl->scale = *scale; + impl->buffer_scale_dirty = TRUE; + } if (impl->display_server.egl_window) - wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0); - if (scale_changed) - impl->buffer_scale_dirty = TRUE; + wl_egl_window_resize (impl->display_server.egl_window, + width * gdk_fractional_scale_to_int (scale), + height * gdk_fractional_scale_to_int (scale), 0, 0); gdk_surface_invalidate_rect (surface, NULL); @@ -435,27 +466,23 @@ gdk_wayland_surface_update_scale (GdkSurface *surface) return; if (!impl->display_server.outputs) - { - scale = impl->scale; - } - else - { - scale = 1; - for (l = impl->display_server.outputs; l != NULL; l = l->next) - { - struct wl_output *output = l->data; - uint32_t output_scale; + return; - output_scale = gdk_wayland_display_get_output_scale (display_wayland, - output); - scale = MAX (scale, output_scale); - } + scale = 1; + for (l = impl->display_server.outputs; l != NULL; l = l->next) + { + struct wl_output *output = l->data; + uint32_t output_scale; + + output_scale = gdk_wayland_display_get_output_scale (display_wayland, + output); + scale = MAX (scale, output_scale); } /* Notify app that scale changed */ gdk_wayland_surface_maybe_resize (surface, surface->width, surface->height, - scale); + &GDK_FRACTIONAL_SCALE_INIT_INT (scale)); } GdkSurface * @@ -532,10 +559,13 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, if (monitor) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + guint32 monitor_scale = gdk_monitor_get_scale_factor (monitor); - impl->scale = gdk_monitor_get_scale_factor (monitor); - if (impl->scale != 1) - impl->buffer_scale_dirty = TRUE; + if (monitor_scale != 1) + { + impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (monitor_scale); + impl->buffer_scale_dirty = TRUE; + } g_object_unref (monitor); } @@ -657,38 +687,6 @@ gdk_wayland_surface_finalize (GObject *object) G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->finalize (object); } -static void -gdk_wayland_surface_maybe_resize (GdkSurface *surface, - int width, - int height, - int scale) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - gboolean hide_temporarily; - - if (surface->width == width && - surface->height == height && - impl->scale == scale) - return; - - /* For xdg_popup using an xdg_positioner, there is a race condition if - * the application tries to change the size after it's mapped, but before - * the initial configure is received, so hide and show the surface again - * force the new size onto the compositor. See bug #772505. - */ - hide_temporarily = GDK_IS_WAYLAND_POPUP (surface) && - gdk_surface_get_mapped (surface) && - !impl->initial_configure_received; - - if (hide_temporarily) - gdk_surface_hide (surface); - - gdk_wayland_surface_update_size (surface, width, height, scale); - - if (hide_temporarily) - gdk_wayland_surface_create_wl_surface (surface); -} - static void gdk_wayland_surface_sync_shadow (GdkSurface *surface) { @@ -793,7 +791,8 @@ gdk_wayland_surface_sync_buffer_scale (GdkSurface *surface) /* Only set the buffer scale if supported by the compositor */ if (wl_surface_get_version (impl->display_server.wl_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) - wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); + wl_surface_set_buffer_scale (impl->display_server.wl_surface, + gdk_fractional_scale_to_int (&impl->scale)); impl->buffer_scale_dirty = FALSE; } @@ -809,7 +808,7 @@ gdk_wayland_surface_fractional_scale_preferred_scale_cb (void *data, /* Notify app that scale changed */ gdk_wayland_surface_maybe_resize (surface, surface->width, surface->height, - ceil (scale / 120.0)); + &GDK_FRACTIONAL_SCALE_INIT (scale)); } static const struct wp_fractional_scale_v1_listener fractional_scale_listener = { @@ -1062,7 +1061,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface) impl->has_uncommitted_ack_configure = FALSE; impl->input_region_dirty = TRUE; impl->opaque_region_dirty = TRUE; - if (impl->scale != 1) + if (!gdk_fractional_scale_equal (&impl->scale, &GDK_FRACTIONAL_SCALE_INIT_INT (1))) impl->buffer_scale_dirty = TRUE; impl->last_sent_window_geometry = (GdkRectangle) { 0 }; @@ -1097,7 +1096,7 @@ gdk_wayland_surface_move_resize (GdkSurface *surface, surface->x = x; surface->y = y; - gdk_wayland_surface_maybe_resize (surface, width, height, impl->scale); + gdk_wayland_surface_maybe_resize (surface, width, height, &impl->scale); } static void @@ -1232,7 +1231,7 @@ gdk_wayland_surface_get_scale_factor (GdkSurface *surface) if (GDK_SURFACE_DESTROYED (surface)) return 1; - return impl->scale; + return gdk_fractional_scale_to_int (&impl->scale); } static void @@ -1335,8 +1334,8 @@ gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface) { impl->display_server.egl_window = wl_egl_window_create (impl->display_server.wl_surface, - surface->width * impl->scale, - surface->height * impl->scale); + surface->width * gdk_fractional_scale_to_int (&impl->scale), + surface->height * gdk_fractional_scale_to_int (&impl->scale)); gdk_surface_set_egl_native_window (surface, impl->display_server.egl_window); } } diff --git a/gdk/wayland/gdktoplevel-wayland.c b/gdk/wayland/gdktoplevel-wayland.c index f40f5af219..6954795f0e 100644 --- a/gdk/wayland/gdktoplevel-wayland.c +++ b/gdk/wayland/gdktoplevel-wayland.c @@ -434,7 +434,7 @@ gdk_wayland_toplevel_compute_size (GdkSurface *surface) width, height, &width, &height); } - gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); + gdk_wayland_surface_update_size (surface, width, height, &wayland_surface->scale); if (!wayland_toplevel->next_layout.size_is_fixed) { @@ -452,7 +452,7 @@ gdk_wayland_toplevel_compute_size (GdkSurface *surface) gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height); - gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); + gdk_wayland_surface_update_size (surface, width, height, &wayland_surface->scale); } wayland_surface->next_layout.surface_geometry_dirty = FALSE; From 1a71e82fc52f896bbb6a87d113e4fe9c9dc8937d Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 14:41:30 +0200 Subject: [PATCH 3/6] wayland: Use wp_viewport to set buffer scale Instead of setting the buffer scale via the buffer-scale command, set it via the viewport. This technically allows setting fractional scales, but we're not doing that. --- gdk/wayland/gdkdisplay-wayland.c | 7 +++ gdk/wayland/gdkdisplay-wayland.h | 2 + gdk/wayland/gdksurface-wayland-private.h | 2 + gdk/wayland/gdksurface-wayland.c | 74 +++++++++++++++++------- gdk/wayland/meson.build | 1 + 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 4a27302904..9fbf93b938 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -516,6 +516,13 @@ gdk_registry_handle_global (void *data, &wp_fractional_scale_manager_v1_interface, MIN (version, 1)); } + else if (strcmp (interface, "wp_viewporter") == 0) + { + display_wayland->viewporter = + wl_registry_bind (display_wayland->wl_registry, id, + &wp_viewporter_interface, + MIN (version, 1)); + } g_hash_table_insert (display_wayland->known_globals, diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 6dbe793157..b7d9a90c0c 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,7 @@ struct _GdkWaylandDisplay struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; struct xdg_activation_v1 *xdg_activation; struct wp_fractional_scale_manager_v1 *fractional_scale; + struct wp_viewporter *viewporter; GList *async_roundtrips; diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index e6c08a4bb4..7f33b44856 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -36,6 +36,7 @@ struct _GdkWaylandSurface struct zxdg_surface_v6 *zxdg_surface_v6; struct wl_egl_window *egl_window; struct wp_fractional_scale_v1 *fractional_scale; + struct wp_viewport *viewport; } display_server; struct wl_event_queue *event_queue; @@ -52,6 +53,7 @@ struct _GdkWaylandSurface gint64 pending_frame_counter; GdkFractionalScale scale; gboolean buffer_scale_dirty; + gboolean viewport_dirty; int shadow_left; int shadow_right; diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 2581015a2e..f19887460c 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -61,11 +61,6 @@ G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE) static void gdk_wayland_surface_configure (GdkSurface *surface); -static void gdk_wayland_surface_sync_shadow (GdkSurface *surface); -static void gdk_wayland_surface_sync_input_region (GdkSurface *surface); -static void gdk_wayland_surface_sync_opaque_region (GdkSurface *surface); -static void gdk_wayland_surface_sync_buffer_scale (GdkSurface *surface); - /* {{{ Utilities */ static void @@ -176,6 +171,7 @@ static void gdk_wayland_surface_init (GdkWaylandSurface *impl) { impl->scale = GDK_FRACTIONAL_SCALE_INIT_INT (1); + impl->viewport_dirty = TRUE; } void @@ -256,7 +252,10 @@ gdk_wayland_surface_update_size (GdkSurface *surface, { impl->scale = *scale; impl->buffer_scale_dirty = TRUE; + impl->viewport_dirty = TRUE; } + if (width_changed || height_changed) + impl->viewport_dirty = TRUE; if (impl->display_server.egl_window) wl_egl_window_resize (impl->display_server.egl_window, @@ -617,15 +616,6 @@ gdk_wayland_surface_attach_image (GdkSurface *surface, } } -void -gdk_wayland_surface_sync (GdkSurface *surface) -{ - gdk_wayland_surface_sync_shadow (surface); - gdk_wayland_surface_sync_opaque_region (surface); - gdk_wayland_surface_sync_input_region (surface); - gdk_wayland_surface_sync_buffer_scale (surface); -} - static gboolean gdk_wayland_surface_beep (GdkSurface *surface) { @@ -781,20 +771,53 @@ gdk_wayland_surface_sync_input_region (GdkSurface *surface) static void gdk_wayland_surface_sync_buffer_scale (GdkSurface *surface) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface); - if (!impl->display_server.wl_surface) + if (!self->display_server.wl_surface) return; - if (!impl->buffer_scale_dirty) + if (!self->buffer_scale_dirty) return; - /* Only set the buffer scale if supported by the compositor */ - if (wl_surface_get_version (impl->display_server.wl_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) - wl_surface_set_buffer_scale (impl->display_server.wl_surface, - gdk_fractional_scale_to_int (&impl->scale)); + if (self->display_server.viewport) + { + /* The viewport takes care of buffer scale */ + } + else if (wl_surface_get_version (self->display_server.wl_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + { + wl_surface_set_buffer_scale (self->display_server.wl_surface, + gdk_fractional_scale_to_int (&self->scale)); + } - impl->buffer_scale_dirty = FALSE; + self->buffer_scale_dirty = FALSE; +} + +static void +gdk_wayland_surface_sync_viewport (GdkSurface *surface) +{ + GdkWaylandSurface *self = GDK_WAYLAND_SURFACE (surface); + + if (!self->display_server.viewport) + return; + + if (!self->viewport_dirty) + return; + + wp_viewport_set_destination (self->display_server.viewport, + surface->width, + surface->height); + + self->viewport_dirty = FALSE; +} + +void +gdk_wayland_surface_sync (GdkSurface *surface) +{ + gdk_wayland_surface_sync_shadow (surface); + gdk_wayland_surface_sync_opaque_region (surface); + gdk_wayland_surface_sync_input_region (surface); + gdk_wayland_surface_sync_buffer_scale (surface); + gdk_wayland_surface_sync_viewport (surface); } static void @@ -881,6 +904,11 @@ gdk_wayland_surface_create_wl_surface (GdkSurface *surface) wp_fractional_scale_v1_add_listener (self->display_server.fractional_scale, &fractional_scale_listener, self); } + if (display_wayland->viewporter) + { + self->display_server.viewport = + wp_viewporter_get_viewport (display_wayland->viewporter, wl_surface); + } self->display_server.wl_surface = wl_surface; } @@ -1051,6 +1079,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface) } g_clear_pointer (&impl->display_server.fractional_scale, wp_fractional_scale_v1_destroy); + g_clear_pointer (&impl->display_server.viewport, wp_viewport_destroy); g_clear_pointer (&impl->display_server.wl_surface, wl_surface_destroy); @@ -1061,6 +1090,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface) impl->has_uncommitted_ack_configure = FALSE; impl->input_region_dirty = TRUE; impl->opaque_region_dirty = TRUE; + impl->viewport_dirty = TRUE; if (!gdk_fractional_scale_equal (&impl->scale, &GDK_FRACTIONAL_SCALE_INIT_INT (1))) impl->buffer_scale_dirty = TRUE; diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index 67d16e01f5..d325b4edea 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -55,6 +55,7 @@ proto_sources = [ ['gtk-shell', 'private', ], ['primary-selection', 'unstable', 'v1', ], ['pointer-gestures', 'unstable', 'v1', ], + ['viewporter', 'stable', ], ['xdg-shell', 'unstable', 'v6', ], ['xdg-shell', 'stable', ], ['xdg-foreign', 'unstable', 'v1', ], From ea82f50d13ae3da35b7e57880b919559a3f8bae2 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 13:59:52 +0200 Subject: [PATCH 4/6] build: Bump wayland-protocols requirement The new fractional-scale support requires v1.31 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9d8bf1db63..3c2887d031 100644 --- a/meson.build +++ b/meson.build @@ -17,7 +17,7 @@ harfbuzz_req = '>= 2.6.0' fribidi_req = '>= 1.0.6' cairo_req = '>= 1.14.0' gdk_pixbuf_req = '>= 2.30.0' -wayland_proto_req = '>= 1.25' +wayland_proto_req = '>= 1.31' wayland_req = '>= 1.21.0' graphene_req = '>= 1.10.0' epoxy_req = '>= 1.4' From 84b235aac1f460721ca79efdebcf1894a21b0942 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 16:09:38 +0200 Subject: [PATCH 5/6] wayland: Allow creating fractional Cairo surfaces We don't do that yet, because the buffer scale code can't deal with it, but we can do it now. --- gdk/wayland/gdkcairocontext-wayland.c | 6 ++--- gdk/wayland/gdkcursor-wayland.c | 8 +++---- gdk/wayland/gdkdisplay-wayland.c | 29 ++++++++++++++---------- gdk/wayland/gdkprivate-wayland.h | 18 +++++++-------- gdk/wayland/gdksurface-wayland-private.h | 3 +++ 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/gdk/wayland/gdkcairocontext-wayland.c b/gdk/wayland/gdkcairocontext-wayland.c index b75353b577..eb54c9ccab 100644 --- a/gdk/wayland/gdkcairocontext-wayland.c +++ b/gdk/wayland/gdkcairocontext-wayland.c @@ -128,9 +128,9 @@ gdk_wayland_cairo_context_create_surface (GdkWaylandCairoContext *self) width = gdk_surface_get_width (surface); height = gdk_surface_get_height (surface); - cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland, - width, height, - gdk_surface_get_scale_factor (surface)); + cairo_surface = gdk_wayland_display_create_shm_surface (display_wayland, + width, height, + &GDK_FRACTIONAL_SCALE_INIT_INT (gdk_surface_get_scale_factor (surface))); buffer = _gdk_wayland_shm_surface_get_wl_buffer (cairo_surface); wl_buffer_add_listener (buffer, &buffer_listener, cairo_surface); gdk_wayland_cairo_context_add_surface (self, cairo_surface); diff --git a/gdk/wayland/gdkcursor-wayland.c b/gdk/wayland/gdkcursor-wayland.c index 41cd2e4fc4..35a07b564a 100644 --- a/gdk/wayland/gdkcursor-wayland.c +++ b/gdk/wayland/gdkcursor-wayland.c @@ -221,10 +221,10 @@ from_texture: surface = g_hash_table_lookup (display->cursor_surface_cache, cursor); if (surface == NULL) { - surface = _gdk_wayland_display_create_shm_surface (display, - gdk_texture_get_width (texture), - gdk_texture_get_height (texture), - 1); + surface = gdk_wayland_display_create_shm_surface (display, + gdk_texture_get_width (texture), + gdk_texture_get_height (texture), + &GDK_FRACTIONAL_SCALE_INIT_INT (1)); gdk_texture_download (texture, cairo_image_surface_get_data (surface), diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 9fbf93b938..1aa66ae09b 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -1188,7 +1188,7 @@ typedef struct _GdkWaylandCairoSurfaceData { struct wl_shm_pool *pool; struct wl_buffer *buffer; GdkWaylandDisplay *display; - uint32_t scale; + GdkFractionalScale scale; } GdkWaylandCairoSurfaceData; static int @@ -1313,25 +1313,28 @@ gdk_wayland_cairo_surface_destroy (void *p) } cairo_surface_t * -_gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, - int width, - int height, - guint scale) +gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, + int width, + int height, + const GdkFractionalScale *scale) { GdkWaylandCairoSurfaceData *data; cairo_surface_t *surface = NULL; cairo_status_t status; + int scaled_width, scaled_height; int stride; data = g_new (GdkWaylandCairoSurfaceData, 1); data->display = display; data->buffer = NULL; - data->scale = scale; + data->scale = *scale; - stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width * scale); + scaled_width = gdk_fractional_scale_scale (scale, width); + scaled_height = gdk_fractional_scale_scale (scale, height); + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, scaled_width); data->pool = create_shm_pool (display->shm, - height * scale * stride, + scaled_height * stride, &data->buf_length, &data->buf); if (G_UNLIKELY (data->pool == NULL)) @@ -1339,18 +1342,20 @@ _gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, surface = cairo_image_surface_create_for_data (data->buf, CAIRO_FORMAT_ARGB32, - width * scale, - height * scale, + scaled_width, + scaled_height, stride); data->buffer = wl_shm_pool_create_buffer (data->pool, 0, - width * scale, height * scale, + scaled_width, scaled_height, stride, WL_SHM_FORMAT_ARGB8888); cairo_surface_set_user_data (surface, &gdk_wayland_shm_surface_cairo_key, data, gdk_wayland_cairo_surface_destroy); - cairo_surface_set_device_scale (surface, scale, scale); + cairo_surface_set_device_scale (surface, + gdk_fractional_scale_to_double (scale), + gdk_fractional_scale_to_double (scale)); status = cairo_surface_status (surface); if (status != CAIRO_STATUS_SUCCESS) diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index 0276d4c70d..8db2dd8f49 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -128,11 +128,11 @@ guint _gdk_wayland_cursor_get_next_image_index (GdkWaylandDisplay *display, guint current_image_index, guint *next_image_delay); -void gdk_wayland_surface_sync (GdkSurface *surface); -void gdk_wayland_surface_commit (GdkSurface *surface); -void gdk_wayland_surface_notify_committed (GdkSurface *surface); -void gdk_wayland_surface_request_frame (GdkSurface *surface); -gboolean gdk_wayland_surface_has_surface (GdkSurface *surface); +void gdk_wayland_surface_sync (GdkSurface *surface); +void gdk_wayland_surface_commit (GdkSurface *surface); +void gdk_wayland_surface_notify_committed (GdkSurface *surface); +void gdk_wayland_surface_request_frame (GdkSurface *surface); +gboolean gdk_wayland_surface_has_surface (GdkSurface *surface); void gdk_wayland_surface_attach_image (GdkSurface *surface, cairo_surface_t *cairo_surface, const cairo_region_t *damage); @@ -207,10 +207,10 @@ GdkMonitor *gdk_wayland_display_get_monitor_for_output (GdkDisplay *displa void _gdk_wayland_surface_set_grab_seat (GdkSurface *surface, GdkSeat *seat); -cairo_surface_t * _gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, - int width, - int height, - guint scale); +cairo_surface_t * gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display, + int width, + int height, + const GdkFractionalScale *scale); struct wl_buffer *_gdk_wayland_shm_surface_get_wl_buffer (cairo_surface_t *surface); gboolean _gdk_wayland_is_shm_surface (cairo_surface_t *surface); diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index 7f33b44856..1d87cc4b09 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -17,6 +17,8 @@ #pragma once +#include "gdkprivate-wayland.h" + typedef enum _PopupState { POPUP_STATE_IDLE, @@ -52,6 +54,7 @@ struct _GdkWaylandSurface gint64 pending_frame_counter; GdkFractionalScale scale; + gboolean buffer_is_fractional; gboolean buffer_scale_dirty; gboolean viewport_dirty; From 6d4d9af14e729682cef1506fd785de08a23dfa19 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 1 Apr 2023 17:25:33 +0200 Subject: [PATCH 6/6] wayland: Use fractional scaling with the Cairo renderer Cairo can do that, so just enable it: * Create surfaces with the correct fractionally scaled size. * Set the Cairo surface's device scale to that number. --- gdk/wayland/gdkcairocontext-wayland.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdk/wayland/gdkcairocontext-wayland.c b/gdk/wayland/gdkcairocontext-wayland.c index eb54c9ccab..2e4ad16477 100644 --- a/gdk/wayland/gdkcairocontext-wayland.c +++ b/gdk/wayland/gdkcairocontext-wayland.c @@ -21,6 +21,7 @@ #include "gdkcairocontext-wayland.h" #include "gdkprivate-wayland.h" +#include "gdksurface-wayland-private.h" #include "gdkprofilerprivate.h" @@ -130,7 +131,7 @@ gdk_wayland_cairo_context_create_surface (GdkWaylandCairoContext *self) height = gdk_surface_get_height (surface); cairo_surface = gdk_wayland_display_create_shm_surface (display_wayland, width, height, - &GDK_FRACTIONAL_SCALE_INIT_INT (gdk_surface_get_scale_factor (surface))); + &GDK_WAYLAND_SURFACE (surface)->scale); buffer = _gdk_wayland_shm_surface_get_wl_buffer (cairo_surface); wl_buffer_add_listener (buffer, &buffer_listener, cairo_surface); gdk_wayland_cairo_context_add_surface (self, cairo_surface);