cursor-sprite-xcursor: Emulate Wayland hotspot limitations

For HiDPI pointer cursors backed by Wayland surfaces, the hotspot must
be placed using integers on the logical pixel grid. In practice what
this means is that if the client loads a cursor sprite with the buffer
scale 2, and it's hotspot is not dividable by 2, it will be rounded
down to an integer that can. E.g. a wl_surface with buffer scale 2 and a
cursor image with hotspot coordinate (7, 7) will have the coordinate
(3.5, 3.5) in surface coordinate space, and will in practice be rounded
down to (3, 3) as the hotspot position in wl_pointer only takes
integers.

To not potentially shift by 1 pixel on HiDPI monitors when switching
between wl_surface backend cursor sprites and built-in ones, make the
built in one emulate the restrictions put up by the Wayland protocol.

This also initializes the theme scale of the xcursor sprite instances to
1, as they may not have been set prior to being used, it'll only happen
in response to "prepare-at" signals being emitted prior to rendering.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1092

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1107
This commit is contained in:
Jonas Ådahl 2020-03-06 11:50:38 +01:00
parent 8beef8ccd0
commit 40c345d6f3

View File

@ -25,6 +25,7 @@
#include "clutter/clutter.h" #include "clutter/clutter.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
#include "meta/prefs.h" #include "meta/prefs.h"
#include "meta/util.h"
struct _MetaCursorSpriteXcursor struct _MetaCursorSpriteXcursor
{ {
@ -124,6 +125,7 @@ load_from_current_xcursor_image (MetaCursorSpriteXcursor *sprite_xcursor)
CoglContext *cogl_context; CoglContext *cogl_context;
CoglTexture2D *texture; CoglTexture2D *texture;
GError *error = NULL; GError *error = NULL;
int hotspot_x, hotspot_y;
g_assert (!meta_cursor_sprite_get_cogl_texture (sprite)); g_assert (!meta_cursor_sprite_get_cogl_texture (sprite));
@ -152,9 +154,21 @@ load_from_current_xcursor_image (MetaCursorSpriteXcursor *sprite_xcursor)
g_error_free (error); g_error_free (error);
} }
if (meta_is_wayland_compositor ())
{
hotspot_x = ((int) (xc_image->xhot / sprite_xcursor->theme_scale) *
sprite_xcursor->theme_scale);
hotspot_y = ((int) (xc_image->yhot / sprite_xcursor->theme_scale) *
sprite_xcursor->theme_scale);
}
else
{
hotspot_x = xc_image->xhot;
hotspot_y = xc_image->yhot;
}
meta_cursor_sprite_set_texture (sprite, meta_cursor_sprite_set_texture (sprite,
COGL_TEXTURE (texture), COGL_TEXTURE (texture),
xc_image->xhot, xc_image->yhot); hotspot_x, hotspot_y);
g_clear_pointer (&texture, cogl_object_unref); g_clear_pointer (&texture, cogl_object_unref);
} }
@ -272,6 +286,7 @@ meta_cursor_sprite_xcursor_finalize (GObject *object)
static void static void
meta_cursor_sprite_xcursor_init (MetaCursorSpriteXcursor *sprite_xcursor) meta_cursor_sprite_xcursor_init (MetaCursorSpriteXcursor *sprite_xcursor)
{ {
sprite_xcursor->theme_scale = 1;
sprite_xcursor->theme_dirty = TRUE; sprite_xcursor->theme_dirty = TRUE;
} }