From c543206d791977b921a7952bb3cc4c65d9d29958 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 26 May 2022 22:28:57 -0400 Subject: [PATCH 1/2] wayland: Sanity check cursor image size On Wayland it is a protocol violation to upload buffers with dimensions that are not an integer multiple of the buffer scale. Until recently, Mutter did not enforce this. When it started doing so, some users started seeing crashes in GTK apps because the cursor theme ended up with e.g. a 15x16 pixel image at scale of 2. Add a small sanity check for this case. --- gdk/wayland/gdkcursor-wayland.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/gdk/wayland/gdkcursor-wayland.c b/gdk/wayland/gdkcursor-wayland.c index c859f3ccac..a5602bcbaf 100644 --- a/gdk/wayland/gdkcursor-wayland.c +++ b/gdk/wayland/gdkcursor-wayland.c @@ -178,6 +178,7 @@ _gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display, if (c) { struct wl_cursor_image *image; + int cursor_scale; if (image_index >= c->image_count) { @@ -189,12 +190,22 @@ _gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display, image = c->images[image_index]; - *hotspot_x = image->hotspot_x / desired_scale; - *hotspot_y = image->hotspot_y / desired_scale; + cursor_scale = desired_scale; + if ((image->width % cursor_scale != 0) || + (image->height % cursor_scale != 0)) + { + g_warning (G_STRLOC " cursor image size (%dx%d) not an integer" + "multiple of scale (%d)", image->width, image->height, + cursor_scale); + cursor_scale = 1; + } - *width = image->width / desired_scale; - *height = image->height / desired_scale; - *scale = desired_scale; + *hotspot_x = image->hotspot_x / cursor_scale; + *hotspot_y = image->hotspot_y / cursor_scale; + + *width = image->width / cursor_scale; + *height = image->height / cursor_scale; + *scale = cursor_scale; return wl_cursor_image_get_buffer (image); } From c47759bd3dd23a4c9ab9049f20bb56efb622693a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 26 May 2022 22:23:31 -0400 Subject: [PATCH 2/2] wayland: Make cursors have the right size The Wayland protocol requires that width and height of cursor surfaces is an integer multiple of the surface scale. Make it so. Fixes: #4746 --- gdk/wayland/cursor/wayland-cursor.c | 46 +++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/gdk/wayland/cursor/wayland-cursor.c b/gdk/wayland/cursor/wayland-cursor.c index 572b056b30..071824d914 100644 --- a/gdk/wayland/cursor/wayland-cursor.c +++ b/gdk/wayland/cursor/wayland-cursor.c @@ -203,12 +203,16 @@ static struct wl_cursor * wl_cursor_create_from_xcursor_images(XcursorImages *images, struct wl_cursor_theme *theme, const char *name, - unsigned int load_size) + unsigned int size, + unsigned int scale) { struct cursor *cursor; struct cursor_image *image; - int i, size; + int i, nbytes; + unsigned int load_size; + int width, height; + load_size = size * scale; cursor = malloc(sizeof *cursor); if (!cursor) return NULL; @@ -232,22 +236,37 @@ wl_cursor_create_from_xcursor_images(XcursorImages *images, image->theme = theme; image->buffer = NULL; - image->image.width = images->images[i]->width; - image->image.height = images->images[i]->height; + /* ensure that width and height are multiples of scale */ + width = images->images[i]->width; + if ((width % scale) != 0) + width = (width / scale + 1) * scale; + + height = images->images[i]->height; + if ((height % scale) != 0) + height = (height / scale + 1) * scale; + + image->image.width = width; + image->image.height = height; image->image.hotspot_x = images->images[i]->xhot; image->image.hotspot_y = images->images[i]->yhot; image->image.delay = images->images[i]->delay; - size = image->image.width * image->image.height * 4; - image->offset = shm_pool_allocate(theme->pool, size); + nbytes = image->image.width * image->image.height * 4; + image->offset = shm_pool_allocate(theme->pool, nbytes); if (image->offset < 0) { free(image); break; } /* copy pixels to shm pool */ - memcpy(theme->pool->data + image->offset, - images->images[i]->pixels, size); + /* pad at the right and bottom with transparent pixels */ + memset (theme->pool->data + image->offset, 0, nbytes); + for (int y = 0; y < height; y++) + { + memcpy(theme->pool->data + image->offset + y * width * 4, + images->images[i]->pixels + y * images->images[i]->width * 4, + images->images[i]->width * 4); + } cursor->total_delay += image->image.delay; cursor->cursor.images[i] = (struct wl_cursor_image *) image; } @@ -264,20 +283,23 @@ wl_cursor_create_from_xcursor_images(XcursorImages *images, } static void -load_cursor(struct wl_cursor_theme *theme, const char *name, unsigned int size) +load_cursor(struct wl_cursor_theme *theme, + const char *name, + unsigned int size, + unsigned int scale) { XcursorImages *images; struct wl_cursor *cursor; char *path; path = g_strconcat (theme->path, "/", name, NULL); - images = xcursor_load_images (path, size); + images = xcursor_load_images (path, size * scale); g_free (path); if (!images) return; - cursor = wl_cursor_create_from_xcursor_images(images, theme, name, size); + cursor = wl_cursor_create_from_xcursor_images(images, theme, name, size, scale); if (cursor) { theme->cursor_count++; @@ -373,7 +395,7 @@ wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme, return theme->cursors[i]; } - load_cursor (theme, name, size); + load_cursor (theme, name, theme->size, scale); if (i < theme->cursor_count) { if (size == theme->cursors[i]->size &&