[mutter] Support scaling of cursor sprites given what output they are on



commit 79c86ae8903b3f1e223553937369b17b146849a9
Author: Jonas Ådahl <jadahl gmail com>
Date:   Fri Jul 17 23:16:39 2015 +0800

    Support scaling of cursor sprites given what output they are on
    
    This commits refactors cursor handling code and plugs in logic so that
    cursor sprites changes appearance as it moves across the screen.
    Renderers are adapted to handle the necessary functionality.
    
    The logic for changing the cursor sprite appearance is done outside of
    MetaCursorSprite, and actually where depends on what type of cursor it
    is. In mutter we now have two types of cursors that may have their
    appearance changed:
    
     - Themed cursors (aka root cursors)
     - wl_surface cursors
    
    Themed cursors are created by MetaScreen and when created, when
    applicable(*), it will extend the cursor via connecting to a signal
    which is emitted everytime the cursor is moved. The signal handler will
    calculate the expected scale given the monitor it is on and reload the
    theme in a correct size when needed.
    
    wl_surface cursors are created when a wl_surface is assigned the
    "cursor" role, i.e. when a client calls wl_pointer.set_cursor. A
    cursor role object is created which is connected to the cursor object
    by the position signal, and will set a correct texture scale given what
    monitor the cursor is on and what scale the wl_surface's active buffer
    is in. It will also push new buffers to the same to the cursor object
    when new ones are committed to the surface.
    
    This commit also makes texture loading lazy, since the renderer doesn't
    calculate a rectangle when the cursor position changes.
    
    The native backend is refactored to be triple-buffered; see the comment
    in meta-cursor-renderer-native.c for further explanations.
    
    * when we are running as a Wayland compositor
    
    https://bugzilla.gnome.org/show_bug.cgi?id=744932

 src/backends/meta-cursor-renderer.c                |   99 ++++---
 src/backends/meta-cursor-renderer.h                |    7 +-
 src/backends/meta-cursor-tracker.c                 |   13 +-
 src/backends/meta-cursor.c                         |  163 ++++++-----
 src/backends/meta-cursor.h                         |   40 ++--
 src/backends/meta-monitor-manager-private.h        |    3 +-
 src/backends/native/meta-cursor-renderer-native.c  |  294 ++++++++++++++++----
 src/backends/x11/meta-cursor-renderer-x11.c        |   17 +-
 .../x11/nested/meta-cursor-renderer-x11-nested.c   |    5 +-
 src/core/screen-private.h                          |    4 +
 src/core/screen.c                                  |   49 ++++
 src/wayland/meta-wayland-pointer.c                 |  179 +++++++++---
 src/wayland/meta-wayland-pointer.h                 |    2 -
 13 files changed, 630 insertions(+), 245 deletions(-)
---
diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c
index 689ac26..48cd239 100644
--- a/src/backends/meta-cursor-renderer.c
+++ b/src/backends/meta-cursor-renderer.c
@@ -37,7 +37,6 @@
 struct _MetaCursorRendererPrivate
 {
   int current_x, current_y;
-  MetaRectangle current_rect;
 
   MetaCursorSprite *displayed_cursor;
   gboolean handled_by_backend;
@@ -47,28 +46,33 @@ typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate;
 G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT);
 
 static void
-queue_redraw (MetaCursorRenderer *renderer)
+queue_redraw (MetaCursorRenderer *renderer,
+              MetaCursorSprite   *cursor_sprite)
 {
   MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
   MetaBackend *backend = meta_get_backend ();
   ClutterActor *stage = meta_backend_get_stage (backend);
   CoglTexture *texture;
+  MetaRectangle rect = { 0 };
+
+  if (cursor_sprite)
+    rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
 
   /* During early initialization, we can have no stage */
   if (!stage)
     return;
 
-  if (priv->displayed_cursor && !priv->handled_by_backend)
-    texture = meta_cursor_sprite_get_cogl_texture (priv->displayed_cursor,
-                                                   NULL, NULL);
+  if (cursor_sprite && !priv->handled_by_backend)
+    texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
   else
     texture = NULL;
 
-  meta_stage_set_cursor (META_STAGE (stage), texture, &priv->current_rect);
+  meta_stage_set_cursor (META_STAGE (stage), texture, &rect);
 }
 
 static gboolean
-meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer)
+meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer,
+                                         MetaCursorSprite   *cursor_sprite)
 {
   return FALSE;
 }
@@ -84,35 +88,50 @@ meta_cursor_renderer_init (MetaCursorRenderer *renderer)
 {
 }
 
+MetaRectangle
+meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
+                                     MetaCursorSprite   *cursor_sprite)
+{
+  MetaCursorRendererPrivate *priv =
+    meta_cursor_renderer_get_instance_private (renderer);
+  CoglTexture *texture;
+  int hot_x, hot_y;
+  int width, height;
+  float texture_scale;
+
+  texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
+  if (!texture)
+    return (MetaRectangle) { 0 };
+
+  meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y);
+  texture_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
+  width = cogl_texture_get_width (texture);
+  height = cogl_texture_get_height (texture);
+
+  return (MetaRectangle) {
+    .x = (int)roundf (priv->current_x - (hot_x * texture_scale)),
+    .y = (int)roundf (priv->current_y - (hot_y * texture_scale)),
+    .width = (int)roundf (width * texture_scale),
+    .height = (int)roundf (height * texture_scale),
+  };
+}
+
 static void
-update_cursor (MetaCursorRenderer *renderer)
+update_cursor (MetaCursorRenderer *renderer,
+               MetaCursorSprite   *cursor_sprite)
 {
   MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
   gboolean handled_by_backend;
   gboolean should_redraw = FALSE;
 
-  if (priv->displayed_cursor)
-    {
-      CoglTexture *texture;
-      int hot_x, hot_y;
-
-      texture = meta_cursor_sprite_get_cogl_texture (priv->displayed_cursor,
-                                                     &hot_x, &hot_y);
-
-      priv->current_rect.x = priv->current_x - hot_x;
-      priv->current_rect.y = priv->current_y - hot_y;
-      priv->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (texture));
-      priv->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (texture));
-    }
-  else
-    {
-      priv->current_rect.x = 0;
-      priv->current_rect.y = 0;
-      priv->current_rect.width = 0;
-      priv->current_rect.height = 0;
-    }
+  if (cursor_sprite)
+    meta_cursor_sprite_prepare_at (cursor_sprite,
+                                   priv->current_x,
+                                   priv->current_y);
 
-  handled_by_backend = META_CURSOR_RENDERER_GET_CLASS (renderer)->update_cursor (renderer);
+  handled_by_backend =
+    META_CURSOR_RENDERER_GET_CLASS (renderer)->update_cursor (renderer,
+                                                              cursor_sprite);
   if (handled_by_backend != priv->handled_by_backend)
     {
       priv->handled_by_backend = handled_by_backend;
@@ -123,7 +142,7 @@ update_cursor (MetaCursorRenderer *renderer)
     should_redraw = TRUE;
 
   if (should_redraw)
-    queue_redraw (renderer);
+    queue_redraw (renderer, cursor_sprite);
 }
 
 MetaCursorRenderer *
@@ -140,16 +159,18 @@ meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer,
 
   if (priv->displayed_cursor == cursor_sprite)
     return;
-
   priv->displayed_cursor = cursor_sprite;
-  update_cursor (renderer);
+
+  update_cursor (renderer, cursor_sprite);
 }
 
 void
 meta_cursor_renderer_force_update (MetaCursorRenderer *renderer)
 {
-  update_cursor (renderer);
-  queue_redraw (renderer);
+  MetaCursorRendererPrivate *priv =
+    meta_cursor_renderer_get_instance_private (renderer);
+
+  update_cursor (renderer, priv->displayed_cursor);
 }
 
 void
@@ -163,7 +184,7 @@ meta_cursor_renderer_set_position (MetaCursorRenderer *renderer,
   priv->current_x = x;
   priv->current_y = y;
 
-  update_cursor (renderer);
+  update_cursor (renderer, priv->displayed_cursor);
 }
 
 MetaCursorSprite *
@@ -174,14 +195,6 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
   return priv->displayed_cursor;
 }
 
-const MetaRectangle *
-meta_cursor_renderer_get_rect (MetaCursorRenderer *renderer)
-{
-  MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
-
-  return &priv->current_rect;
-}
-
 #ifdef HAVE_WAYLAND
 void
 meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,
diff --git a/src/backends/meta-cursor-renderer.h b/src/backends/meta-cursor-renderer.h
index 337d5c1..c5fcb64 100644
--- a/src/backends/meta-cursor-renderer.h
+++ b/src/backends/meta-cursor-renderer.h
@@ -42,7 +42,8 @@ struct _MetaCursorRendererClass
 {
   GObjectClass parent_class;
 
-  gboolean (* update_cursor) (MetaCursorRenderer *renderer);
+  gboolean (* update_cursor) (MetaCursorRenderer *renderer,
+                              MetaCursorSprite   *cursor_sprite);
 #ifdef HAVE_WAYLAND
   void (* realize_cursor_from_wl_buffer) (MetaCursorRenderer *renderer,
                                           MetaCursorSprite *cursor_sprite,
@@ -65,7 +66,9 @@ void meta_cursor_renderer_set_position (MetaCursorRenderer *renderer,
 void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer);
 
 MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer);
-const MetaRectangle * meta_cursor_renderer_get_rect (MetaCursorRenderer *renderer);
+
+MetaRectangle meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
+                                                   MetaCursorSprite   *cursor_sprite);
 
 #ifdef HAVE_WAYLAND
 void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer,
diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
index 544926c..6728f4b 100644
--- a/src/backends/meta-cursor-tracker.c
+++ b/src/backends/meta-cursor-tracker.c
@@ -246,10 +246,11 @@ ensure_xfixes_cursor (MetaCursorTracker *tracker)
 
   if (sprite != NULL)
     {
-      MetaCursorSprite *cursor_sprite =
-        meta_cursor_sprite_from_texture (sprite,
-                                         cursor_image->xhot,
-                                         cursor_image->yhot);
+      MetaCursorSprite *cursor_sprite = meta_cursor_sprite_new ();
+      meta_cursor_sprite_set_texture (cursor_sprite,
+                                      sprite,
+                                      cursor_image->xhot,
+                                      cursor_image->yhot);
       cogl_object_unref (sprite);
       tracker->xfixes_cursor = cursor_sprite;
     }
@@ -279,7 +280,7 @@ meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
     }
 
   if (cursor_sprite)
-    return meta_cursor_sprite_get_cogl_texture (cursor_sprite, NULL, NULL);
+    return meta_cursor_sprite_get_cogl_texture (cursor_sprite);
   else
     return NULL;
 }
@@ -311,7 +312,7 @@ meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
     }
 
   if (cursor_sprite)
-    meta_cursor_sprite_get_cogl_texture (cursor_sprite, x, y);
+    meta_cursor_sprite_get_hotspot (cursor_sprite, x, y);
   else
     {
       if (x)
diff --git a/src/backends/meta-cursor.c b/src/backends/meta-cursor.c
index a0d724f..0ac059a 100644
--- a/src/backends/meta-cursor.c
+++ b/src/backends/meta-cursor.c
@@ -35,9 +35,13 @@
 #include <X11/extensions/Xfixes.h>
 #include <X11/Xcursor/Xcursor.h>
 
-#ifdef HAVE_WAYLAND
-#include <cogl/cogl-wayland-server.h>
-#endif
+enum {
+  PREPARE_AT,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
 
 struct _MetaCursorSprite
 {
@@ -46,10 +50,14 @@ struct _MetaCursorSprite
   MetaCursor cursor;
 
   CoglTexture2D *texture;
+  float texture_scale;
   int hot_x, hot_y;
 
   int current_frame;
   XcursorImages *xcursor_images;
+
+  int theme_scale;
+  gboolean theme_dirty;
 };
 
 GType meta_cursor_sprite_get_type (void) G_GNUC_CONST;
@@ -112,11 +120,11 @@ meta_cursor_create_x_cursor (Display    *xdisplay,
 }
 
 static XcursorImages *
-load_cursor_on_client (MetaCursor cursor)
+load_cursor_on_client (MetaCursor cursor, int scale)
 {
   return XcursorLibraryLoadImages (translate_meta_cursor (cursor),
                                    meta_prefs_get_cursor_theme (),
-                                   meta_prefs_get_cursor_size ());
+                                   meta_prefs_get_cursor_size () * scale);
 }
 
 static void
@@ -129,6 +137,7 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
   CoglPixelFormat cogl_format;
   ClutterBackend *clutter_backend;
   CoglContext *cogl_context;
+  CoglTexture *texture;
 
   g_assert (self->texture == NULL);
 
@@ -144,14 +153,15 @@ meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self,
 
   clutter_backend = clutter_get_default_backend ();
   cogl_context = clutter_backend_get_cogl_context (clutter_backend);
-  self->texture = cogl_texture_2d_new_from_data (cogl_context,
-                                                 width, height,
-                                                 cogl_format,
-                                                 rowstride,
-                                                 (uint8_t *) xc_image->pixels,
-                                                 NULL);
-  self->hot_x = xc_image->xhot;
-  self->hot_y = xc_image->yhot;
+  texture = cogl_texture_2d_new_from_data (cogl_context,
+                                           width, height,
+                                           cogl_format,
+                                           rowstride,
+                                           (uint8_t *) xc_image->pixels,
+                                           NULL);
+  meta_cursor_sprite_set_texture (self, texture,
+                                  xc_image->xhot, xc_image->yhot);
+  cogl_object_unref (texture);
 
   meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image);
 }
@@ -198,92 +208,82 @@ meta_cursor_sprite_is_animated (MetaCursorSprite *self)
 }
 
 MetaCursorSprite *
-meta_cursor_sprite_from_theme (MetaCursor cursor)
+meta_cursor_sprite_new (void)
+{
+  return g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
+}
+
+static void
+meta_cursor_sprite_load_from_theme (MetaCursorSprite *self)
 {
-  MetaCursorSprite *self;
   XcursorImage *image;
 
-  self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
+  g_assert (self->cursor != META_CURSOR_NONE);
+
+  /* We might be reloading with a different scale. If so clear the old data. */
+  if (self->xcursor_images)
+    {
+      g_clear_pointer (&self->texture, cogl_object_unref);
+      XcursorImagesDestroy (self->xcursor_images);
+    }
 
-  self->cursor = cursor;
   self->current_frame = 0;
-  self->xcursor_images = load_cursor_on_client (self->cursor);
+  self->xcursor_images = load_cursor_on_client (self->cursor,
+                                                self->theme_scale);
   if (!self->xcursor_images)
     meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?");
 
   image = meta_cursor_sprite_get_current_frame_image (self);
   meta_cursor_sprite_load_from_xcursor_image (self, image);
 
-  return self;
+  self->theme_dirty = FALSE;
 }
 
 MetaCursorSprite *
-meta_cursor_sprite_from_texture (CoglTexture2D *texture,
-                                 int            hot_x,
-                                 int            hot_y)
+meta_cursor_sprite_from_theme (MetaCursor cursor)
 {
   MetaCursorSprite *self;
 
-  self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
-
-  cogl_object_ref (texture);
+  self = meta_cursor_sprite_new ();
 
-  self->texture = texture;
-  self->hot_x = hot_x;
-  self->hot_y = hot_y;
+  self->cursor = cursor;
+  self->theme_dirty = TRUE;
 
   return self;
 }
 
-#ifdef HAVE_WAYLAND
-static void
-meta_cursor_sprite_load_from_buffer (MetaCursorSprite   *self,
-                                     struct wl_resource *buffer,
-                                     int                 hot_x,
-                                     int                 hot_y)
+void
+meta_cursor_sprite_set_texture (MetaCursorSprite *self,
+                                CoglTexture      *texture,
+                                int               hot_x,
+                                int               hot_y)
 {
-  MetaBackend *meta_backend = meta_get_backend ();
-  MetaCursorRenderer *renderer =
-    meta_backend_get_cursor_renderer (meta_backend);
-  ClutterBackend *backend;
-  CoglContext *cogl_context;
-
+  g_clear_pointer (&self->texture, cogl_object_unref);
+  if (texture)
+    self->texture = cogl_object_ref (texture);
   self->hot_x = hot_x;
   self->hot_y = hot_y;
-
-  backend = clutter_get_default_backend ();
-  cogl_context = clutter_backend_get_cogl_context (backend);
-
-  self->texture = cogl_wayland_texture_2d_new_from_buffer (cogl_context, buffer, NULL);
-
-  meta_cursor_renderer_realize_cursor_from_wl_buffer (renderer, self, buffer);
 }
 
-MetaCursorSprite *
-meta_cursor_sprite_from_buffer (struct wl_resource *buffer,
-                                int                 hot_x,
-                                int                 hot_y)
+void
+meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
+                                      float             scale)
 {
-  MetaCursorSprite *self;
-
-  self = g_object_new (META_TYPE_CURSOR_SPRITE, NULL);
-
-  meta_cursor_sprite_load_from_buffer (self, buffer, hot_x, hot_y);
+  self->texture_scale = scale;
+}
 
-  return self;
+void
+meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
+                                    int               theme_scale)
+{
+  if (self->theme_scale != theme_scale)
+    self->theme_dirty = TRUE;
+  self->theme_scale = theme_scale;
 }
-#endif
 
 CoglTexture *
-meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self,
-                                     int              *hot_x,
-                                     int              *hot_y)
+meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self)
 {
-  if (hot_x)
-    *hot_x = self->hot_x;
-  if (hot_y)
-    *hot_y = self->hot_y;
-
   return COGL_TEXTURE (self->texture);
 }
 
@@ -302,21 +302,31 @@ meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
   *hot_y = self->hot_y;
 }
 
-guint
-meta_cursor_sprite_get_width (MetaCursorSprite *self)
+float
+meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self)
 {
-  return cogl_texture_get_width (COGL_TEXTURE (self->texture));
+  return self->texture_scale;
 }
 
-guint
-meta_cursor_sprite_get_height (MetaCursorSprite *self)
+void
+meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
+                               int               x,
+                               int               y)
+{
+  g_signal_emit (self, signals[PREPARE_AT], 0, x, y);
+}
+
+void
+meta_cursor_sprite_realize_texture (MetaCursorSprite *self)
 {
-  return cogl_texture_get_height (COGL_TEXTURE (self->texture));
+  if (self->theme_dirty)
+    meta_cursor_sprite_load_from_theme (self);
 }
 
 static void
 meta_cursor_sprite_init (MetaCursorSprite *self)
 {
+  self->texture_scale = 1.0f;
 }
 
 static void
@@ -338,4 +348,13 @@ meta_cursor_sprite_class_init (MetaCursorSpriteClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = meta_cursor_sprite_finalize;
+
+  signals[PREPARE_AT] = g_signal_new ("prepare-at",
+                                      G_TYPE_FROM_CLASS (object_class),
+                                      G_SIGNAL_RUN_LAST,
+                                      0,
+                                      NULL, NULL, NULL,
+                                      G_TYPE_NONE, 2,
+                                      G_TYPE_INT,
+                                      G_TYPE_INT);
 }
diff --git a/src/backends/meta-cursor.h b/src/backends/meta-cursor.h
index f0b014b..6087df6 100644
--- a/src/backends/meta-cursor.h
+++ b/src/backends/meta-cursor.h
@@ -23,6 +23,7 @@
 #define META_CURSOR_H
 
 #include <meta/common.h>
+#include <meta/boxes.h>
 
 typedef struct _MetaCursorSprite MetaCursorSprite;
 
@@ -32,35 +33,40 @@ G_DECLARE_FINAL_TYPE (MetaCursorSprite,
                       META, CURSOR_SPRITE,
                       GObject);
 
-MetaCursorSprite * meta_cursor_sprite_from_theme  (MetaCursor          cursor);
+MetaCursorSprite * meta_cursor_sprite_new (void);
 
-#ifdef HAVE_WAYLAND
-#include <wayland-server.h>
-MetaCursorSprite * meta_cursor_sprite_from_buffer (struct wl_resource *buffer,
-                                                   int                 hot_x,
-                                                   int                 hot_y);
-#endif
+MetaCursorSprite * meta_cursor_sprite_from_theme  (MetaCursor cursor);
 
-MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self);
 
-MetaCursorSprite * meta_cursor_sprite_from_texture (CoglTexture2D *texture,
-                                                    int            hot_x,
-                                                    int            hot_y);
+void meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self,
+                                         int               scale);
+
+MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self);
 
 Cursor meta_cursor_create_x_cursor (Display    *xdisplay,
                                     MetaCursor  cursor);
 
-CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self,
-                                                  int              *hot_x,
-                                                  int              *hot_y);
+void meta_cursor_sprite_prepare_at (MetaCursorSprite *self,
+                                    int               x,
+                                    int               y);
+
+void meta_cursor_sprite_realize_texture (MetaCursorSprite *self);
+
+void meta_cursor_sprite_set_texture (MetaCursorSprite *self,
+                                     CoglTexture      *texture,
+                                     int               hot_x,
+                                     int               hot_y);
+
+void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self,
+                                           float             scale);
+
+CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self);
 
 void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self,
                                      int              *hot_x,
                                      int              *hot_y);
 
-guint meta_cursor_sprite_get_width (MetaCursorSprite *self);
-
-guint meta_cursor_sprite_get_height (MetaCursorSprite *self);
+float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self);
 
 gboolean meta_cursor_sprite_is_animated            (MetaCursorSprite *self);
 void     meta_cursor_sprite_tick_frame             (MetaCursorSprite *self);
diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h
index e6955ac..c30e8d7 100644
--- a/src/backends/meta-monitor-manager-private.h
+++ b/src/backends/meta-monitor-manager-private.h
@@ -174,7 +174,8 @@ struct _MetaCRTC
   /* Used when changing configuration */
   gboolean is_dirty;
 
-  MetaCursorSprite *cursor;
+  /* Used by cursor renderer backend */
+  void *cursor_renderer_private;
 
   gpointer driver_private;
   GDestroyNotify driver_notify;
diff --git a/src/backends/native/meta-cursor-renderer-native.c 
b/src/backends/native/meta-cursor-renderer-native.c
index b9e4b0d..3d0dcff 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -29,9 +29,13 @@
 #include <string.h>
 #include <gbm.h>
 #include <xf86drm.h>
+#include <errno.h>
 
 #include <meta/util.h>
+#include <meta/meta-backend.h>
+
 #include "meta-monitor-manager-private.h"
+#include "meta/boxes.h"
 
 #ifndef DRM_CAP_CURSOR_WIDTH
 #define DRM_CAP_CURSOR_WIDTH 0x8
@@ -40,6 +44,19 @@
 #define DRM_CAP_CURSOR_HEIGHT 0x9
 #endif
 
+/* When animating a cursor, we usually call drmModeSetCursor2 once per frame.
+ * Though, testing shows that we need to triple buffer the cursor buffer in
+ * order to avoid glitches when animating the cursor, at least when running on
+ * Intel. The reason for this might be (but is not confirmed to be) due to
+ * the user space gbm_bo cache, making us reuse and overwrite the kernel side
+ * buffer content before it was scanned out. To avoid this, we keep a user space
+ * reference to each buffer we set until at least one frame after it was drawn.
+ * In effect, this means we three active cursor gbm_bo's: one that that just has
+ * been set, one that was previously set and may or may not have been scanned
+ * out, and one pending that will be replaced if the cursor sprite changes.
+ */
+#define HW_CURSOR_BUFFER_COUNT 3
+
 static GQuark quark_cursor_sprite = 0;
 
 struct _MetaCursorRendererNativePrivate
@@ -57,8 +74,25 @@ struct _MetaCursorRendererNativePrivate
 };
 typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate;
 
+typedef enum _MetaCursorGbmBoState
+{
+  META_CURSOR_GBM_BO_STATE_NONE,
+  META_CURSOR_GBM_BO_STATE_SET,
+  META_CURSOR_GBM_BO_STATE_INVALIDATED,
+} MetaCursorGbmBoState;
+
+typedef struct _MetaCursorNativePrivate
+{
+  guint active_bo;
+  MetaCursorGbmBoState pending_bo_state;
+  struct gbm_bo *bos[HW_CURSOR_BUFFER_COUNT];
+} MetaCursorNativePrivate;
+
 G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, 
META_TYPE_CURSOR_RENDERER);
 
+static MetaCursorNativePrivate *
+ensure_cursor_priv (MetaCursorSprite *cursor_sprite);
+
 static void
 meta_cursor_renderer_native_finalize (GObject *object)
 {
@@ -74,26 +108,53 @@ meta_cursor_renderer_native_finalize (GObject *object)
   G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
 }
 
+static guint
+get_pending_cursor_sprite_gbm_bo_index (MetaCursorSprite *cursor_sprite)
+{
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+
+  return (cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT;
+}
+
 static struct gbm_bo *
-get_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
+get_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
 {
-  return g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+  guint pending_bo;
+
+  if (!cursor_priv)
+    return NULL;
+
+  pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
+  return cursor_priv->bos[pending_bo];
 }
 
-static void
-cursor_gbm_bo_free (gpointer data)
+static struct gbm_bo *
+get_active_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
 {
-  if (!data)
-    return;
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+
+  if (!cursor_priv)
+    return NULL;
 
-  gbm_bo_destroy ((struct gbm_bo *)data);
+  return cursor_priv->bos[cursor_priv->active_bo];
 }
 
 static void
-set_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, struct gbm_bo *bo)
+set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite,
+                                  struct gbm_bo    *bo)
 {
-  g_object_set_qdata_full (G_OBJECT (cursor_sprite), quark_cursor_sprite,
-                           bo, cursor_gbm_bo_free);
+  MetaCursorNativePrivate *cursor_priv;
+  guint pending_bo;
+
+  cursor_priv = ensure_cursor_priv (cursor_sprite);
+
+  pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
+  cursor_priv->bos[pending_bo] = bo;
+  cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_SET;
 }
 
 static void
@@ -104,55 +165,78 @@ set_crtc_cursor (MetaCursorRendererNative *native,
 {
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
 
-  if (crtc->cursor == cursor_sprite && !force)
-    return;
-
-  crtc->cursor = cursor_sprite;
-
   if (cursor_sprite)
     {
+      MetaCursorNativePrivate *cursor_priv =
+        g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
       struct gbm_bo *bo;
       union gbm_bo_handle handle;
       int hot_x, hot_y;
 
-      bo = get_cursor_sprite_gbm_bo (cursor_sprite);
+      if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET)
+        bo = get_pending_cursor_sprite_gbm_bo (cursor_sprite);
+      else
+        bo = get_active_cursor_sprite_gbm_bo (cursor_sprite);
+
+      if (!force && bo == crtc->cursor_renderer_private)
+        return;
+
+      crtc->cursor_renderer_private = bo;
+
       handle = gbm_bo_get_handle (bo);
       meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y);
 
       drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32,
                          priv->cursor_width, priv->cursor_height, hot_x, hot_y);
+
+      if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET)
+        {
+          cursor_priv->active_bo =
+            (cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT;
+          cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_NONE;
+        }
     }
   else
     {
-      drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0);
+      if (force || crtc->cursor_renderer_private != NULL)
+        {
+          drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0);
+          crtc->cursor_renderer_private = NULL;
+        }
     }
 }
 
 static void
 update_hw_cursor (MetaCursorRendererNative *native,
+                  MetaCursorSprite         *cursor_sprite,
                   gboolean                  force)
 {
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
   MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
-  const MetaRectangle *cursor_rect = meta_cursor_renderer_get_rect (renderer);
-  MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
   MetaMonitorManager *monitors;
   MetaCRTC *crtcs;
   unsigned int i, n_crtcs;
+  MetaRectangle rect;
 
   monitors = meta_monitor_manager_get ();
   meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL);
 
+  if (cursor_sprite)
+    rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
+  else
+    rect = (MetaRectangle) { 0 };
+
   for (i = 0; i < n_crtcs; i++)
     {
-      gboolean crtc_should_have_cursor;
+      gboolean crtc_should_use_cursor;
       MetaCursorSprite *crtc_cursor;
       MetaRectangle *crtc_rect;
 
       crtc_rect = &crtcs[i].rect;
 
-      crtc_should_have_cursor = (priv->has_hw_cursor && meta_rectangle_overlap (cursor_rect, crtc_rect));
-      if (crtc_should_have_cursor)
+      crtc_should_use_cursor = (priv->has_hw_cursor &&
+                                meta_rectangle_overlap (&rect, crtc_rect));
+      if (crtc_should_use_cursor)
         crtc_cursor = cursor_sprite;
       else
         crtc_cursor = NULL;
@@ -162,49 +246,78 @@ update_hw_cursor (MetaCursorRendererNative *native,
       if (crtc_cursor)
         {
           drmModeMoveCursor (priv->drm_fd, crtcs[i].crtc_id,
-                             cursor_rect->x - crtc_rect->x,
-                             cursor_rect->y - crtc_rect->y);
+                             rect.x - crtc_rect->x,
+                             rect.y - crtc_rect->y);
         }
     }
 }
 
 static gboolean
-should_have_hw_cursor (MetaCursorRenderer *renderer)
+has_valid_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
 {
-  MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
 
-  if (cursor_sprite)
-    return (get_cursor_sprite_gbm_bo (cursor_sprite) != NULL);
-  else
+  switch (cursor_priv->pending_bo_state)
+    {
+    case META_CURSOR_GBM_BO_STATE_NONE:
+      return get_active_cursor_sprite_gbm_bo (cursor_sprite) != NULL;
+    case META_CURSOR_GBM_BO_STATE_SET:
+      return TRUE;
+    case META_CURSOR_GBM_BO_STATE_INVALIDATED:
+      return FALSE;
+    }
+
+  g_assert_not_reached ();
+
+  return FALSE;
+}
+
+static gboolean
+should_have_hw_cursor (MetaCursorRenderer *renderer,
+                       MetaCursorSprite   *cursor_sprite)
+{
+  CoglTexture *texture;
+
+  if (!cursor_sprite)
     return FALSE;
+
+  texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
+  if (!texture)
+    return FALSE;
+
+  if (meta_cursor_sprite_get_texture_scale (cursor_sprite) != 1)
+    return FALSE;
+
+  if (!has_valid_cursor_sprite_gbm_bo (cursor_sprite))
+    return FALSE;
+
+  return TRUE;
 }
 
 static gboolean
 meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native)
 {
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
-  MetaCursorSprite *cursor_sprite;
+  MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
+  MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
 
   priv->animation_timeout_id = 0;
-  cursor_sprite =
-    meta_cursor_renderer_get_cursor (META_CURSOR_RENDERER (native));
   meta_cursor_sprite_tick_frame (cursor_sprite);
-  meta_cursor_renderer_force_update (META_CURSOR_RENDERER (native));
+  meta_cursor_renderer_force_update (renderer);
   meta_cursor_renderer_native_force_update (native);
 
   return G_SOURCE_REMOVE;
 }
 
 static void
-meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native)
+meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native,
+                                           MetaCursorSprite         *cursor_sprite)
 {
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
-  MetaCursorSprite *cursor_sprite;
   gboolean cursor_change;
   guint delay;
 
-  cursor_sprite =
-    meta_cursor_renderer_get_cursor (META_CURSOR_RENDERER (native));
   cursor_change = cursor_sprite != priv->last_cursor;
   priv->last_cursor = cursor_sprite;
 
@@ -234,15 +347,19 @@ meta_cursor_renderer_native_trigger_frame (MetaCursorRendererNative *native)
 }
 
 static gboolean
-meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer)
+meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer,
+                                           MetaCursorSprite   *cursor_sprite)
 {
   MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
 
-  meta_cursor_renderer_native_trigger_frame (native);
+  if (cursor_sprite)
+    meta_cursor_sprite_realize_texture (cursor_sprite);
+
+  meta_cursor_renderer_native_trigger_frame (native, cursor_sprite);
 
-  priv->has_hw_cursor = should_have_hw_cursor (renderer);
-  update_hw_cursor (native, FALSE);
+  priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite);
+  update_hw_cursor (native, cursor_sprite, FALSE);
   return priv->has_hw_cursor;
 }
 
@@ -258,6 +375,38 @@ get_hardware_cursor_size (MetaCursorRendererNative *native,
 }
 
 static void
+cursor_priv_free (gpointer data)
+{
+  MetaCursorNativePrivate *cursor_priv = data;
+  guint i;
+
+  if (!data)
+    return;
+
+  for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++)
+    g_clear_pointer (&cursor_priv->bos[0], (GDestroyNotify) gbm_bo_destroy);
+  g_slice_free (MetaCursorNativePrivate, cursor_priv);
+}
+
+static MetaCursorNativePrivate *
+ensure_cursor_priv (MetaCursorSprite *cursor_sprite)
+{
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+
+  if (!cursor_priv)
+    {
+      cursor_priv = g_slice_new0 (MetaCursorNativePrivate);
+      g_object_set_qdata_full (G_OBJECT (cursor_sprite),
+                               quark_cursor_sprite,
+                               cursor_priv,
+                               cursor_priv_free);
+    }
+
+  return cursor_priv;
+}
+
+static void
 load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native,
                                MetaCursorSprite         *cursor_sprite,
                                uint8_t                  *pixels,
@@ -288,17 +437,45 @@ load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native,
 
       bo = gbm_bo_create (priv->gbm, cursor_width, cursor_height,
                           gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+      if (!bo)
+        {
+          meta_warning ("Failed to allocate HW cursor buffer\n");
+          return;
+        }
 
       memset (buf, 0, sizeof(buf));
       for (i = 0; i < height; i++)
         memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4);
+      if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0)
+        {
+          meta_warning ("Failed to write cursors buffer data: %s",
+                        g_strerror (errno));
+          gbm_bo_destroy (bo);
+          return;
+        }
 
-      gbm_bo_write (bo, buf, cursor_width * cursor_height * 4);
-
-      set_cursor_sprite_gbm_bo (cursor_sprite, bo);
+      set_pending_cursor_sprite_gbm_bo (cursor_sprite, bo);
     }
   else
-    meta_warning ("HW cursor for format %d not supported\n", gbm_format);
+    {
+      meta_warning ("HW cursor for format %d not supported\n", gbm_format);
+    }
+}
+
+static void
+invalidate_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
+{
+  MetaCursorNativePrivate *cursor_priv =
+    g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
+  guint pending_bo;
+
+  if (!cursor_priv)
+    return;
+
+  pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite);
+  g_clear_pointer (&cursor_priv->bos[pending_bo],
+                   (GDestroyNotify) gbm_bo_destroy);
+  cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED;
 }
 
 #ifdef HAVE_WAYLAND
@@ -312,10 +489,17 @@ meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *r
     meta_cursor_renderer_native_get_instance_private (native);
   uint32_t gbm_format;
   uint64_t cursor_width, cursor_height;
+  CoglTexture *texture;
   uint width, height;
 
-  width = meta_cursor_sprite_get_width (cursor_sprite);
-  height = meta_cursor_sprite_get_height (cursor_sprite);
+  /* Destroy any previous pending cursor buffer; we'll always either fail (which
+   * should unset, or succeed, which will set new buffer.
+   */
+  invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite);
+
+  texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
+  width = cogl_texture_get_width (texture);
+  height = cogl_texture_get_height (texture);
 
   struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (buffer);
   if (shm_buffer)
@@ -384,7 +568,7 @@ meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *r
           return;
         }
 
-      set_cursor_sprite_gbm_bo (cursor_sprite, bo);
+      set_pending_cursor_sprite_gbm_bo (cursor_sprite, bo);
     }
 }
 #endif
@@ -396,6 +580,8 @@ meta_cursor_renderer_native_realize_cursor_from_xcursor (MetaCursorRenderer *ren
 {
   MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer);
 
+  invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite);
+
   load_cursor_sprite_gbm_buffer (native,
                                  cursor_sprite,
                                  (uint8_t *) xc_image->pixels,
@@ -424,11 +610,19 @@ meta_cursor_renderer_native_class_init (MetaCursorRendererNativeClass *klass)
 }
 
 static void
+force_update_hw_cursor (MetaCursorRendererNative *native)
+{
+  MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native);
+
+  update_hw_cursor (native, meta_cursor_renderer_get_cursor (renderer), TRUE);
+}
+
+static void
 on_monitors_changed (MetaMonitorManager       *monitors,
                      MetaCursorRendererNative *native)
 {
   /* Our tracking is all messed up, so force an update. */
-  update_hw_cursor (native, TRUE);
+  force_update_hw_cursor (native);
 }
 
 static void
@@ -476,5 +670,5 @@ meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *native)
 void
 meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native)
 {
-  update_hw_cursor (native, TRUE);
+  force_update_hw_cursor (native);
 }
diff --git a/src/backends/x11/meta-cursor-renderer-x11.c b/src/backends/x11/meta-cursor-renderer-x11.c
index f34f6bd..3911ffd 100644
--- a/src/backends/x11/meta-cursor-renderer-x11.c
+++ b/src/backends/x11/meta-cursor-renderer-x11.c
@@ -40,25 +40,29 @@ typedef struct _MetaCursorRendererX11Private MetaCursorRendererX11Private;
 G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererX11, meta_cursor_renderer_x11, META_TYPE_CURSOR_RENDERER);
 
 static gboolean
-meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer)
+meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer,
+                                        MetaCursorSprite   *cursor_sprite)
 {
   MetaCursorRendererX11 *x11 = META_CURSOR_RENDERER_X11 (renderer);
   MetaCursorRendererX11Private *priv = meta_cursor_renderer_x11_get_instance_private (x11);
 
   MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ());
   Window xwindow = meta_backend_x11_get_xwindow (backend);
+  Display *xdisplay = meta_backend_x11_get_xdisplay (backend);
 
   if (xwindow == None)
-    return FALSE;
-
-  Display *xdisplay = meta_backend_x11_get_xdisplay (backend);
+    {
+      if (cursor_sprite)
+        meta_cursor_sprite_realize_texture (cursor_sprite);
+      return FALSE;
+    }
 
-  MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer);
   gboolean has_server_cursor = FALSE;
 
   if (cursor_sprite)
     {
       MetaCursor cursor = meta_cursor_sprite_get_meta_cursor (cursor_sprite);
+
       if (cursor != META_CURSOR_NONE)
         {
           Cursor xcursor = meta_cursor_create_x_cursor (xdisplay, cursor);
@@ -80,6 +84,9 @@ meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer)
       priv->server_cursor_visible = has_server_cursor;
     }
 
+  if (!priv->server_cursor_visible && cursor_sprite)
+    meta_cursor_sprite_realize_texture (cursor_sprite);
+
   return priv->server_cursor_visible;
 }
 
diff --git a/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c 
b/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
index 15ce57b..4fda18d 100644
--- a/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
+++ b/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c
@@ -39,8 +39,11 @@ G_DEFINE_TYPE (MetaCursorRendererX11Nested, meta_cursor_renderer_x11_nested,
                META_TYPE_CURSOR_RENDERER);
 
 static gboolean
-meta_cursor_renderer_x11_nested_update_cursor (MetaCursorRenderer *renderer)
+meta_cursor_renderer_x11_nested_update_cursor (MetaCursorRenderer *renderer,
+                                               MetaCursorSprite   *cursor_sprite)
 {
+  if (cursor_sprite)
+    meta_cursor_sprite_realize_texture (cursor_sprite);
   return FALSE;
 }
 
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 40eae6e..438d1f4 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -152,6 +152,10 @@ const MetaMonitorInfo* meta_screen_get_monitor_for_rect   (MetaScreen    *screen
 const MetaMonitorInfo* meta_screen_calculate_monitor_for_window (MetaScreen    *screen,
                                                                  MetaWindow    *window);
 
+const MetaMonitorInfo* meta_screen_get_monitor_for_point (MetaScreen    *screen,
+                                                          int            x,
+                                                          int            y);
+
 
 const MetaMonitorInfo* meta_screen_get_monitor_neighbor (MetaScreen *screen,
                                                          int         which_monitor,
diff --git a/src/core/screen.c b/src/core/screen.c
index c3d9686..d2f30cc 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -43,6 +43,7 @@
 #include <meta/meta-enum-types.h>
 #include "core.h"
 #include "meta-cursor-tracker-private.h"
+#include "boxes-private.h"
 
 #include <X11/extensions/Xinerama.h>
 #include <X11/extensions/Xcomposite.h>
@@ -1255,6 +1256,31 @@ update_num_workspaces (MetaScreen *screen,
   g_object_notify (G_OBJECT (screen), "n-workspaces");
 }
 
+static void
+root_cursor_prepare_at (MetaCursorSprite *cursor_sprite,
+                        int x,
+                        int y,
+                        MetaScreen *screen)
+{
+  const MetaMonitorInfo *monitor;
+
+  monitor = meta_screen_get_monitor_for_point (screen, x, y);
+
+  /* Reload the cursor texture if the scale has changed. */
+  meta_cursor_sprite_set_theme_scale (cursor_sprite, monitor->scale);
+}
+
+static void
+manage_root_cursor_sprite_scale (MetaScreen *screen,
+                                 MetaCursorSprite *cursor_sprite)
+{
+  g_signal_connect_object (cursor_sprite,
+                           "prepare-at",
+                           G_CALLBACK (root_cursor_prepare_at),
+                           screen,
+                           0);
+}
+
 void
 meta_screen_update_cursor (MetaScreen *screen)
 {
@@ -1265,6 +1291,10 @@ meta_screen_update_cursor (MetaScreen *screen)
   MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (screen);
 
   cursor_sprite = meta_cursor_sprite_from_theme (cursor);
+
+  if (meta_is_wayland_compositor ())
+    manage_root_cursor_sprite_scale (screen, cursor_sprite);
+
   meta_cursor_tracker_set_root_cursor (tracker, cursor_sprite);
   g_object_unref (cursor_sprite);
 
@@ -1454,6 +1484,25 @@ meta_screen_get_monitor_index_for_rect (MetaScreen    *screen,
   return monitor->number;
 }
 
+const MetaMonitorInfo *
+meta_screen_get_monitor_for_point (MetaScreen *screen,
+                                   int         x,
+                                   int         y)
+{
+  int i;
+
+  if (screen->n_monitor_infos == 1)
+    return &screen->monitor_infos[0];
+
+  for (i = 0; i < screen->n_monitor_infos; i++)
+    {
+      if (POINT_IN_RECT (x, y, screen->monitor_infos[i].rect))
+        return &screen->monitor_infos[i];
+    }
+
+  return NULL;
+}
+
 const MetaMonitorInfo*
 meta_screen_get_monitor_neighbor (MetaScreen         *screen,
                                   int                 which_monitor,
diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
index 22ccfec..1492129 100644
--- a/src/wayland/meta-wayland-pointer.c
+++ b/src/wayland/meta-wayland-pointer.c
@@ -44,15 +44,22 @@
 #include "config.h"
 
 #include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <cogl/cogl-wayland-server.h>
 #include <linux/input.h>
 
 #include "meta-wayland-pointer.h"
 #include "meta-wayland-popup.h"
 #include "meta-wayland-private.h"
+#include "meta-wayland-surface.h"
 #include "meta-wayland-buffer.h"
 #include "meta-cursor.h"
 #include "meta-cursor-tracker-private.h"
 #include "meta-surface-actor-wayland.h"
+#include "meta/meta-cursor-tracker.h"
+#include "backends/meta-backend-private.h"
+#include "backends/meta-cursor-tracker-private.h"
+#include "backends/meta-cursor-renderer.h"
 
 #include <string.h>
 
@@ -61,6 +68,10 @@
 struct _MetaWaylandSurfaceRoleCursor
 {
   MetaWaylandSurfaceRole parent;
+
+  int hot_x;
+  int hot_y;
+  MetaCursorSprite *cursor_sprite;
 };
 
 GType meta_wayland_surface_role_cursor_get_type (void) G_GNUC_CONST;
@@ -216,32 +227,6 @@ sync_focus_surface (MetaWaylandPointer *pointer)
 }
 
 static void
-set_cursor_surface (MetaWaylandPointer *pointer,
-                    MetaWaylandSurface *surface)
-{
-  if (pointer->cursor_surface == surface)
-    return;
-
-  if (pointer->cursor_surface)
-    wl_list_remove (&pointer->cursor_surface_destroy_listener.link);
-
-  pointer->cursor_surface = surface;
-
-  if (pointer->cursor_surface)
-    wl_resource_add_destroy_listener (pointer->cursor_surface->resource,
-                                      &pointer->cursor_surface_destroy_listener);
-}
-
-static void
-pointer_handle_cursor_surface_destroy (struct wl_listener *listener, void *data)
-{
-  MetaWaylandPointer *pointer = wl_container_of (listener, pointer, cursor_surface_destroy_listener);
-
-  set_cursor_surface (pointer, NULL);
-  meta_wayland_pointer_update_cursor_surface (pointer);
-}
-
-static void
 pointer_handle_focus_surface_destroy (struct wl_listener *listener, void *data)
 {
   MetaWaylandPointer *pointer = wl_container_of (listener, pointer, focus_surface_listener);
@@ -376,7 +361,6 @@ meta_wayland_pointer_init (MetaWaylandPointer *pointer,
   pointer->focus_surface_listener.notify = pointer_handle_focus_surface_destroy;
 
   pointer->cursor_surface = NULL;
-  pointer->cursor_surface_destroy_listener.notify = pointer_handle_cursor_surface_destroy;
 
   pointer->default_grab.interface = &default_pointer_grab_interface;
   pointer->default_grab.pointer = pointer;
@@ -390,10 +374,10 @@ void
 meta_wayland_pointer_release (MetaWaylandPointer *pointer)
 {
   meta_wayland_pointer_set_focus (pointer, NULL);
-  set_cursor_surface (pointer, NULL);
 
   g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref);
   pointer->display = NULL;
+  pointer->cursor_surface = NULL;
 }
 
 static int
@@ -742,22 +726,17 @@ meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer)
 
   if (pointer->current)
     {
-      MetaCursorSprite *cursor_sprite;
+      MetaCursorSprite *cursor_sprite = NULL;
 
-      if (pointer->cursor_surface && pointer->cursor_surface->buffer)
+      if (pointer->cursor_surface)
         {
-          struct wl_resource *buffer = pointer->cursor_surface->buffer->resource;
-          cursor_sprite = meta_cursor_sprite_from_buffer (buffer,
-                                                          pointer->hotspot_x,
-                                                          pointer->hotspot_y);
+          MetaWaylandSurfaceRoleCursor *cursor_role =
+            META_WAYLAND_SURFACE_ROLE_CURSOR (pointer->cursor_surface->role);
+
+          cursor_sprite = cursor_role->cursor_sprite;
         }
-      else
-        cursor_sprite = NULL;
 
       meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite);
-
-      if (cursor_sprite)
-        g_object_unref (cursor_sprite);
     }
   else
     {
@@ -766,11 +745,75 @@ meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer)
 }
 
 static void
+update_cursor_sprite_texture (MetaWaylandSurface *surface)
+{
+  MetaCursorRenderer *cursor_renderer =
+    meta_backend_get_cursor_renderer (meta_get_backend ());
+  MetaWaylandSurfaceRoleCursor *cursor_role =
+    META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
+  MetaCursorSprite *cursor_sprite = cursor_role->cursor_sprite;
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
+  CoglTexture *texture;
+
+  if (surface->buffer)
+    {
+      struct wl_resource *buffer;
+
+      buffer = surface->buffer->resource;
+      texture = cogl_wayland_texture_2d_new_from_buffer (cogl_context,
+                                                         buffer,
+                                                         NULL);
+
+      meta_cursor_sprite_set_texture (cursor_sprite,
+                                      texture,
+                                      cursor_role->hot_x * surface->scale,
+                                      cursor_role->hot_y * surface->scale);
+      meta_cursor_renderer_realize_cursor_from_wl_buffer (cursor_renderer,
+                                                          cursor_sprite,
+                                                          buffer);
+      cogl_object_unref (texture);
+    }
+  else
+    {
+      meta_cursor_sprite_set_texture (cursor_sprite, NULL, 0, 0);
+    }
+
+  meta_cursor_renderer_force_update (cursor_renderer);
+}
+
+static void
+cursor_sprite_prepare_at (MetaCursorSprite *cursor_sprite,
+                          int x,
+                          int y,
+                          MetaWaylandSurfaceRoleCursor *cursor_role)
+{
+  MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_role);
+  MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role);
+  MetaDisplay *display = meta_get_display ();
+  MetaScreen *screen = display->screen;
+  const MetaMonitorInfo *monitor;
+
+  monitor = meta_screen_get_monitor_for_point (screen, x, y);
+  meta_cursor_sprite_set_texture_scale (cursor_sprite,
+                                        (float)monitor->scale / surface->scale);
+}
+
+static void
+meta_wayland_pointer_set_cursor_surface (MetaWaylandPointer *pointer,
+                                         MetaWaylandSurface *cursor_surface)
+{
+  pointer->cursor_surface = cursor_surface;
+  meta_wayland_pointer_update_cursor_surface (pointer);
+}
+
+static void
 pointer_set_cursor (struct wl_client *client,
                     struct wl_resource *resource,
                     uint32_t serial,
                     struct wl_resource *surface_resource,
-                    int32_t x, int32_t y)
+                    int32_t hot_x, int32_t hot_y)
 {
   MetaWaylandPointer *pointer = wl_resource_get_user_data (resource);
   MetaWaylandSurface *surface;
@@ -794,10 +837,28 @@ pointer_set_cursor (struct wl_client *client,
       return;
     }
 
-  pointer->hotspot_x = x;
-  pointer->hotspot_y = y;
-  set_cursor_surface (pointer, surface);
-  meta_wayland_pointer_update_cursor_surface (pointer);
+  if (surface)
+    {
+      MetaWaylandSurfaceRoleCursor *cursor_role;
+
+      cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
+      if (!cursor_role->cursor_sprite)
+        {
+          cursor_role->cursor_sprite = meta_cursor_sprite_new ();
+          g_signal_connect_object (cursor_role->cursor_sprite,
+                                   "prepare-at",
+                                   G_CALLBACK (cursor_sprite_prepare_at),
+                                   cursor_role,
+                                   0);
+        }
+
+      cursor_role->hot_x = hot_x;
+      cursor_role->hot_y = hot_y;
+
+      update_cursor_sprite_texture (surface);
+    }
+
+  meta_wayland_pointer_set_cursor_surface (pointer, surface);
 }
 
 static void
@@ -877,12 +938,35 @@ cursor_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
 {
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (surface_role);
-  MetaWaylandPointer *pointer = &surface->compositor->seat->pointer;
 
   meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
 
   if (pending->newly_attached)
-    meta_wayland_pointer_update_cursor_surface (pointer);
+    update_cursor_sprite_texture (surface);
+}
+
+static void
+cursor_surface_role_dispose (GObject *object)
+{
+  MetaWaylandSurfaceRoleCursor *cursor_role =
+    META_WAYLAND_SURFACE_ROLE_CURSOR (object);
+  MetaWaylandSurface *surface =
+    meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object));
+  MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
+  MetaWaylandPointer *pointer = &compositor->seat->pointer;
+  MetaCursorTracker *cursor_tracker = meta_cursor_tracker_get_for_screen (NULL);
+
+  g_signal_handlers_disconnect_by_func (cursor_tracker,
+                                        (gpointer) cursor_sprite_prepare_at,
+                                        cursor_role);
+
+  if (pointer->cursor_surface == surface)
+    pointer->cursor_surface = NULL;
+  meta_wayland_pointer_update_cursor_surface (pointer);
+
+  g_clear_object (&cursor_role->cursor_sprite);
+
+  G_OBJECT_CLASS (meta_wayland_surface_role_cursor_parent_class)->dispose (object);
 }
 
 static void
@@ -895,7 +979,10 @@ meta_wayland_surface_role_cursor_class_init (MetaWaylandSurfaceRoleCursorClass *
 {
   MetaWaylandSurfaceRoleClass *surface_role_class =
     META_WAYLAND_SURFACE_ROLE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   surface_role_class->assigned = cursor_surface_role_assigned;
   surface_role_class->commit = cursor_surface_role_commit;
+
+  object_class->dispose = cursor_surface_role_dispose;
 }
diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h
index 95cf5fd..74e1cc2 100644
--- a/src/wayland/meta-wayland-pointer.h
+++ b/src/wayland/meta-wayland-pointer.h
@@ -73,8 +73,6 @@ struct _MetaWaylandPointer
   guint32 click_serial;
 
   MetaWaylandSurface *cursor_surface;
-  struct wl_listener cursor_surface_destroy_listener;
-  int hotspot_x, hotspot_y;
 
   MetaWaylandPointerGrab *grab;
   MetaWaylandPointerGrab default_grab;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]