Merge branch 'wip/otte/fractional-cairo' into 'main'

wayland: Enable fractional scaling with the Cairo renderer

See merge request GNOME/gtk!5767
This commit is contained in:
Matthias Clasen
2023-04-01 18:35:33 +00:00
11 changed files with 219 additions and 130 deletions

View File

@@ -21,6 +21,7 @@
#include "gdkcairocontext-wayland.h"
#include "gdkprivate-wayland.h"
#include "gdksurface-wayland-private.h"
#include "gdkprofilerprivate.h"
@@ -128,9 +129,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_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);

View File

@@ -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),

View File

@@ -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,
@@ -1181,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
@@ -1306,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))
@@ -1332,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)

View File

@@ -38,6 +38,7 @@
#include <gdk/wayland/primary-selection-unstable-v1-client-protocol.h>
#include <gdk/wayland/xdg-activation-v1-client-protocol.h>
#include <gdk/wayland/fractional-scale-v1-client-protocol.h>
#include <gdk/wayland/viewporter-client-protocol.h>
#include <glib.h>
#include <gdk/gdkkeys.h>
@@ -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;

View File

@@ -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;
}

View File

@@ -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,
@@ -89,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);
@@ -168,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);

View File

@@ -17,6 +17,8 @@
#pragma once
#include "gdkprivate-wayland.h"
typedef enum _PopupState
{
POPUP_STATE_IDLE,
@@ -36,6 +38,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;
@@ -50,8 +53,10 @@ struct _GdkWaylandSurface
int pending_buffer_offset_y;
gint64 pending_frame_counter;
guint32 scale;
GdkFractionalScale scale;
gboolean buffer_is_fractional;
gboolean buffer_scale_dirty;
gboolean viewport_dirty;
int shadow_left;
int shadow_right;
@@ -99,11 +104,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);

View File

@@ -59,18 +59,8 @@
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);
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
@@ -180,7 +170,8 @@ 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);
impl->viewport_dirty = TRUE;
}
void
@@ -207,30 +198,69 @@ 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;
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, 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 +465,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 +558,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);
}
@@ -587,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)
{
@@ -657,38 +677,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)
{
@@ -783,19 +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, 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
@@ -809,7 +831,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 = {
@@ -882,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;
}
@@ -1052,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);
@@ -1062,7 +1090,8 @@ 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)
impl->viewport_dirty = TRUE;
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 +1126,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 +1261,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 +1364,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);
}
}

View File

@@ -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;

View File

@@ -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', ],

View File

@@ -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'