[mutter/wip/wayland-work: 16/22] wayland: implement HW cursors



commit a4a506af89fd41678eecb5a34854785ec2f389fa
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Thu Sep 5 16:45:41 2013 +0200

    wayland: implement HW cursors
    
    Use the DRM API and libgbm to upload cursor buffers to the
    appropriate HW plane, saving on GL calls and compositing.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=707573

 src/core/meta-cursor-tracker-private.h |    9 +-
 src/core/meta-cursor-tracker.c         |  615 +++++++++++++++++++++++++++++---
 src/core/monitor-private.h             |    3 +
 src/core/monitor.c                     |   21 +-
 src/wayland/meta-wayland-seat.c        |   15 +-
 5 files changed, 582 insertions(+), 81 deletions(-)
---
diff --git a/src/core/meta-cursor-tracker-private.h b/src/core/meta-cursor-tracker-private.h
index 7b92c6a..e18d315 100644
--- a/src/core/meta-cursor-tracker-private.h
+++ b/src/core/meta-cursor-tracker-private.h
@@ -25,6 +25,7 @@
 #define META_CURSOR_TRACKER_PRIVATE_H
 
 #include <meta/meta-cursor-tracker.h>
+#include <wayland-server.h>
 #include <clutter/clutter.h>
 
 gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
@@ -33,10 +34,10 @@ gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
 void     meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
                                               MetaCursor         cursor);
 void     meta_cursor_tracker_revert_root     (MetaCursorTracker *tracker);
-void     meta_cursor_tracker_set_sprite      (MetaCursorTracker *tracker,
-                                              CoglTexture2D     *texture,
-                                              int                hot_x,
-                                              int                hot_y);
+void     meta_cursor_tracker_set_buffer      (MetaCursorTracker  *tracker,
+                                              struct wl_resource *buffer,
+                                              int                 hot_x,
+                                              int                 hot_y);
 
 void     meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
                                              int                new_x,
diff --git a/src/core/meta-cursor-tracker.c b/src/core/meta-cursor-tracker.c
index 8c3815e..41583f0 100644
--- a/src/core/meta-cursor-tracker.c
+++ b/src/core/meta-cursor-tracker.c
@@ -30,12 +30,15 @@
  */
 
 #include <config.h>
+#include <string.h>
 #include <meta/main.h>
 #include <meta/util.h>
 #include <meta/errors.h>
 
 #include <cogl/cogl.h>
+#include <cogl/cogl-wayland-server.h>
 #include <clutter/clutter.h>
+#include <gbm.h>
 
 #include <gdk/gdk.h>
 
@@ -43,35 +46,41 @@
 
 #include "meta-cursor-tracker-private.h"
 #include "screen-private.h"
-
-#ifdef HAVE_WAYLAND
 #include "meta-wayland-private.h"
-#endif
+#include "monitor-private.h"
 
 #define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7
 #define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4
 
+typedef struct {
+  CoglTexture2D *texture;
+  struct gbm_bo *bo;
+  int hot_x, hot_y;
+
+  int ref_count;
+} MetaCursorReference;
+
 struct _MetaCursorTracker {
   GObject parent_instance;
 
   MetaScreen *screen;
 
   gboolean is_showing;
+  gboolean has_cursor;
+  gboolean has_hw_cursor;
 
-  CoglTexture2D *sprite;
-  int hot_x, hot_y;
-
-  CoglTexture2D *root_cursor;
-  int root_hot_x, root_hot_y;
-
-  CoglTexture2D *default_cursor;
+  MetaCursorReference *sprite;
+  MetaCursorReference *root_cursor;
+  MetaCursorReference *default_cursor;
 
   int current_x, current_y;
-  cairo_rectangle_int_t current_rect;
-  cairo_rectangle_int_t previous_rect;
+  MetaRectangle current_rect;
+  MetaRectangle previous_rect;
   gboolean previous_is_valid;
 
   CoglPipeline *pipeline;
+  int drm_fd;
+  struct gbm_device *gbm;
 };
 
 struct _MetaCursorTrackerClass {
@@ -87,6 +96,321 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
+static void meta_cursor_tracker_set_sprite (MetaCursorTracker   *tracker,
+                                            MetaCursorReference *sprite);
+
+static void meta_cursor_tracker_set_crtc_has_hw_cursor (MetaCursorTracker *tracker,
+                                                        MetaCRTC          *crtc,
+                                                        gboolean           has_hw_cursor);
+
+static MetaCursorReference *
+meta_cursor_reference_ref (MetaCursorReference *self)
+{
+  g_assert (self->ref_count > 0);
+  self->ref_count++;
+
+  return self;
+}
+
+static void
+meta_cursor_reference_unref (MetaCursorReference *self)
+{
+  self->ref_count--;
+
+  if (self->ref_count == 0)
+    {
+      cogl_object_unref (self->texture);
+      if (self->bo)
+        gbm_bo_destroy (self->bo);
+
+      g_slice_free (MetaCursorReference, self);
+    }
+}
+
+static MetaCursorReference *
+meta_cursor_reference_load_file (MetaCursorTracker  *tracker,
+                                 const char         *filename,
+                                 GError            **error)
+{
+  GdkPixbuf *pixbuf;
+  int width, height, rowstride;
+  int bits_per_sample;
+  int n_channels;
+  gboolean has_alpha;
+  CoglPixelFormat cogl_format;
+  uint32_t gbm_format;
+  ClutterBackend *clutter_backend;
+  CoglContext *cogl_context;
+  MetaCursorReference *self;
+
+  pixbuf = gdk_pixbuf_new_from_file (filename, error);
+  if (!pixbuf)
+    return NULL;
+
+  has_alpha       = gdk_pixbuf_get_has_alpha (pixbuf);
+  width           = gdk_pixbuf_get_width (pixbuf);
+  height          = gdk_pixbuf_get_height (pixbuf);
+  rowstride       = gdk_pixbuf_get_rowstride (pixbuf);
+  bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf);
+  n_channels      = gdk_pixbuf_get_n_channels (pixbuf);
+
+  g_assert (bits_per_sample == 8);
+  if (has_alpha)
+    {
+      g_assert (n_channels == 4);
+      cogl_format = COGL_PIXEL_FORMAT_RGBA_8888;
+      gbm_format = GBM_FORMAT_ARGB8888;
+    }
+  else
+    {
+      g_assert (n_channels == 3);
+      cogl_format = COGL_PIXEL_FORMAT_RGB_888;
+      gbm_format = GBM_FORMAT_XRGB8888;
+    }
+
+  self = g_slice_new0 (MetaCursorReference);
+  self->ref_count = 1;
+
+  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,
+                                                 COGL_PIXEL_FORMAT_ANY,
+                                                 rowstride,
+                                                 gdk_pixbuf_get_pixels (pixbuf),
+                                                 NULL);
+
+  if (tracker->gbm)
+    {
+      if (width > 64 || height > 64)
+        {
+          meta_warning ("Invalid default cursor size (must be at most 64x64)\n");
+          goto out;
+        }
+
+      if (gbm_device_is_format_supported (tracker->gbm, gbm_format,
+                                          GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE))
+        {
+          uint8_t *data;
+          uint8_t buf[4 * 64 * 64];
+          int i, j;
+
+          self->bo = gbm_bo_create (tracker->gbm, 64, 64,
+                                    gbm_format, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
+
+          data = gdk_pixbuf_get_pixels (pixbuf);
+          memset (buf, 0, sizeof(buf));
+          for (i = 0; i < height; i++)
+            {
+              for (j = 0; j < width; j++)
+                {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                  /* The byte order is B G R (A) */
+                  buf[i * 4 * 64 + j * 4 + 0] = data[i * rowstride + j * n_channels + 2];
+                  buf[i * 4 * 64 + j * 4 + 1] = data[i * rowstride + j * n_channels + 1];
+                  buf[i * 4 * 64 + j * 4 + 2] = data[i * rowstride + j * n_channels + 0];
+                  if (has_alpha)
+                    buf[i * 4 * 64 + j * 4 + 3] = data[i * rowstride + j * n_channels + 3];
+                  else
+                    buf[i * 4 * 64 + j * 4 + 3] = 0;
+#else
+                  /* The byte order is (A) R G B */
+                  buf[i * 4 * 64 + j * 4 + 3] = data[i * rowstride + j * n_channels + 2];
+                  buf[i * 4 * 64 + j * 4 + 2] = data[i * rowstride + j * n_channels + 1];
+                  buf[i * 4 * 64 + j * 4 + 1] = data[i * rowstride + j * n_channels + 0];
+                  if (has_alpha)
+                    buf[i * 4 * 64 + j * 4 + 0] = data[i * rowstride + j * n_channels + 3];
+                  else
+                    buf[i * 4 * 64 + j * 4 + 0] = 0;
+#endif
+                }
+            }
+
+          gbm_bo_write (self->bo, buf, 64 * 64 * 4);
+        }
+      else
+        meta_warning ("HW cursor for format %d not supported\n", gbm_format);
+    }
+
+ out:
+  g_object_unref (pixbuf);
+
+  return self;
+}
+
+static MetaCursorReference *
+meta_cursor_reference_take_texture (CoglTexture2D *texture)
+{
+  MetaCursorReference *self;
+
+  self = g_slice_new0 (MetaCursorReference);
+  self->ref_count = 1;
+
+  self->texture = texture;
+
+  return self;
+}
+
+static MetaCursorReference *
+meta_cursor_reference_from_buffer (MetaCursorTracker  *tracker,
+                                   struct wl_resource *buffer,
+                                   int                 hot_x,
+                                   int                 hot_y)
+{
+  ClutterBackend *backend;
+  CoglContext *cogl_context;
+  MetaCursorReference *self;
+  CoglPixelFormat cogl_format, cogl_internal_format;
+  struct wl_shm_buffer *shm_buffer;
+  uint32_t gbm_format;
+
+  self = g_slice_new0 (MetaCursorReference);
+  self->ref_count = 1;
+ 
+  backend = clutter_get_default_backend ();
+  cogl_context = clutter_backend_get_cogl_context (backend);
+
+  shm_buffer = wl_shm_buffer_get (buffer);
+  if (shm_buffer)
+    {
+      int rowstride = wl_shm_buffer_get_stride (shm_buffer);
+      int width = wl_shm_buffer_get_width (shm_buffer);
+      int height = wl_shm_buffer_get_height (shm_buffer);
+
+      switch (wl_shm_buffer_get_format (shm_buffer))
+        {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+          case WL_SHM_FORMAT_ARGB8888:
+            cogl_format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+            cogl_internal_format = COGL_PIXEL_FORMAT_ANY;
+            gbm_format = GBM_FORMAT_ARGB8888;
+            break;
+          case WL_SHM_FORMAT_XRGB32:
+            cogl_format = COGL_PIXEL_FORMAT_ARGB_8888;
+            cogl_internal_format = COGL_PIXEL_FORMAT_RGB_888;
+            gbm_format = GBM_FORMAT_XRGB8888;
+            break;
+#else
+          case WL_SHM_FORMAT_ARGB8888:
+            cogl_format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+            cogl_internal_format = COGL_PIXEL_FORMAT_ANY;
+            gbm_format = GBM_FORMAT_ARGB8888;
+            break;
+          case WL_SHM_FORMAT_XRGB8888:
+            cogl_format = COGL_PIXEL_FORMAT_BGRA_8888;
+            cogl_internal_format = COGL_PIXEL_FORMAT_BGR_888;
+            gbm_format = GBM_FORMAT_XRGB8888;
+            break;
+#endif
+          default:
+            g_warn_if_reached ();
+            cogl_format = COGL_PIXEL_FORMAT_ARGB_8888;
+            cogl_internal_format = COGL_PIXEL_FORMAT_ANY;
+            gbm_format = GBM_FORMAT_ARGB8888;
+        }
+
+      self->texture = cogl_texture_2d_new_from_data (cogl_context,
+                                                     width, height,
+                                                     cogl_format,
+                                                     cogl_internal_format,
+                                                     rowstride,
+                                                     wl_shm_buffer_get_data (shm_buffer),
+                                                     NULL);
+
+      if (width > 64 || height > 64)
+        {
+          meta_warning ("Invalid cursor size (must be at most 64x64), falling back to GL cursors\n");
+          return self;
+        }
+
+      if (tracker->gbm)
+        {
+          if (gbm_device_is_format_supported (tracker->gbm, gbm_format,
+                                              GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE))
+            {
+              uint8_t *data;
+              uint8_t buf[4 * 64 * 64];
+              int i;
+
+              self->bo = gbm_bo_create (tracker->gbm, 64, 64,
+                                        gbm_format, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
+
+              data = wl_shm_buffer_get_data (shm_buffer);
+              memset (buf, 0, sizeof(buf));
+              for (i = 0; i < height; i++)
+                memcpy (buf + i * 4 * 64, data + i * rowstride, 4 * width);
+
+              gbm_bo_write (self->bo, buf, 64 * 64 * 4);
+            }
+          else
+            meta_warning ("HW cursor for format %d not supported\n", gbm_format);
+        }
+    }
+  else
+    {
+      int width, height;
+
+      self->texture = cogl_wayland_texture_2d_new_from_buffer (cogl_context, buffer, NULL);
+      width = cogl_texture_get_width (COGL_TEXTURE (self->texture));
+      height = cogl_texture_get_height (COGL_TEXTURE (self->texture));
+
+      /* We can't complete EGL buffers, so they must be the right size from the start */
+      if (width != 64 || height != 64)
+        {
+          meta_warning ("Invalid cursor size (must be 64x64), falling back to GL cursors\n");
+          return self;
+        }
+
+      if (tracker->gbm)
+        {
+          cogl_format = cogl_texture_get_format (COGL_TEXTURE (self->texture));
+          switch (cogl_format)
+            {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+            case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
+            case COGL_PIXEL_FORMAT_ARGB_8888:
+              gbm_format = GBM_FORMAT_BGRA8888;
+              break;
+            case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+            case COGL_PIXEL_FORMAT_BGRA_8888:
+              break;
+            case COGL_PIXEL_FORMAT_RGB_888:
+              break;
+#else
+            case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
+            case COGL_PIXEL_FORMAT_ARGB_8888:
+              gbm_format = GBM_FORMAT_ARGB8888;
+              break;
+            case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+            case COGL_PIXEL_FORMAT_BGRA_8888:
+              gbm_format = GBM_FORMAT_BGRA8888;
+              break;
+            case COGL_PIXEL_FORMAT_RGB_888:
+              gbm_format = GBM_FORMAT_RGB888;
+              break;
+#endif
+            default:
+              meta_warning ("Unknown cogl format %d\n", cogl_format);
+              return self;
+            }
+
+          if (gbm_device_is_format_supported (tracker->gbm, gbm_format,
+                                              GBM_BO_USE_CURSOR_64X64))
+            {
+              self->bo = gbm_bo_import (tracker->gbm, GBM_BO_IMPORT_WL_BUFFER,
+                                        buffer, GBM_BO_USE_CURSOR_64X64);
+              if (!self->bo)
+                meta_warning ("Importing HW cursor from wl_buffer failed\n");
+            }
+          else
+            meta_warning ("HW cursor for format %d not supported\n", gbm_format);
+        }
+     }
+
+  return self;
+}
+
 static void
 meta_cursor_tracker_init (MetaCursorTracker *self)
 {
@@ -104,13 +428,15 @@ meta_cursor_tracker_finalize (GObject *object)
   MetaCursorTracker *self = META_CURSOR_TRACKER (object);
 
   if (self->sprite)
-    cogl_object_unref (self->sprite);
+    meta_cursor_reference_unref (self->sprite);
   if (self->root_cursor)
-    cogl_object_unref (self->root_cursor);
+    meta_cursor_reference_unref (self->root_cursor);
   if (self->default_cursor)
-    cogl_object_unref (self->default_cursor);
+    meta_cursor_reference_unref (self->default_cursor);
   if (self->pipeline)
     cogl_object_unref (self->pipeline);
+  if (self->gbm)
+    gbm_device_destroy (self->gbm);
 
   G_OBJECT_CLASS (meta_cursor_tracker_parent_class)->finalize (object);
 }
@@ -130,13 +456,41 @@ meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
                                           G_TYPE_NONE, 0);
 }
 
-#ifdef HAVE_WAYLAND
+static void
+on_monitors_changed (MetaMonitorManager *monitors,
+                     MetaCursorTracker  *tracker)
+{
+  MetaCRTC *crtcs;
+  unsigned int i, n_crtcs;
+
+  if (!tracker->has_hw_cursor)
+    return;
+
+  /* Go through the new list of monitors, find out where the cursor is */
+  meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL);
+
+  for (i = 0; i < n_crtcs; i++)
+    {
+      MetaRectangle *rect = &crtcs[i].rect;
+      gboolean has;
+
+      has = meta_rectangle_overlap (&tracker->current_rect, rect);
+
+      /* Need to do it unconditionally here, our tracking is
+         wrong because we reloaded the CRTCs */
+      meta_cursor_tracker_set_crtc_has_hw_cursor (tracker, &crtcs[i], has);
+    }
+}
+
 static MetaCursorTracker *
 make_wayland_cursor_tracker (MetaScreen *screen)
 {
   MetaWaylandCompositor *compositor;
   CoglContext *ctx;
-  MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
+  MetaMonitorManager *monitors;
+  MetaCursorTracker *self;
+
+  self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
   self->screen = screen;
 
   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
@@ -145,9 +499,16 @@ make_wayland_cursor_tracker (MetaScreen *screen)
   compositor = meta_wayland_compositor_get_default ();
   compositor->seat->cursor_tracker = self;
 
+  self->drm_fd = compositor->drm_fd;
+  if (self->drm_fd >= 0)
+    self->gbm = gbm_create_device (compositor->drm_fd);
+
+  monitors = meta_monitor_manager_get ();
+  g_signal_connect_object (monitors, "monitors-changed",
+                           G_CALLBACK (on_monitors_changed), self, 0);
+
   return self;
 }
-#endif
 
 static MetaCursorTracker *
 make_x11_cursor_tracker (MetaScreen *screen)
@@ -205,7 +566,7 @@ meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
   if (notify_event->subtype != XFixesDisplayCursorNotify)
     return FALSE;
 
-  g_clear_pointer (&tracker->sprite, cogl_object_unref);
+  g_clear_pointer (&tracker->sprite, meta_cursor_reference_unref);
   g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
 
   return TRUE;
@@ -268,9 +629,9 @@ ensure_xfixes_cursor (MetaCursorTracker *tracker)
 
   if (sprite != NULL)
     {
-      tracker->sprite = sprite;
-      tracker->hot_x = cursor_image->xhot;
-      tracker->hot_y = cursor_image->yhot;
+      tracker->sprite = meta_cursor_reference_take_texture (sprite);
+      tracker->sprite->hot_x = cursor_image->xhot;
+      tracker->sprite->hot_y = cursor_image->yhot;
     }
   XFree (cursor_image);
 }
@@ -288,7 +649,10 @@ meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
   if (!meta_is_wayland_compositor ())
     ensure_xfixes_cursor (tracker);
 
-  return COGL_TEXTURE (tracker->sprite);
+  if (tracker->sprite)
+    return COGL_TEXTURE (tracker->sprite->texture);
+  else
+    return NULL;
 }
 
 /**
@@ -308,17 +672,27 @@ meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
   if (!meta_is_wayland_compositor ())
     ensure_xfixes_cursor (tracker);
 
-  if (x)
-    *x = tracker->hot_x;
-  if (y)
-    *y = tracker->hot_y;
+  if (tracker->sprite)
+    {
+      if (x)
+        *x = tracker->sprite->hot_x;
+      if (y)
+        *y = tracker->sprite->hot_y;
+    }
+  else
+    {
+      if (x)
+        *x = 0;
+      if (y)
+        *y = 0;
+    }
 }
 
 static void
 ensure_wayland_cursor (MetaCursorTracker *tracker)
 {
-  CoglBitmap *bitmap;
   char *filename;
+  GError *error;
 
   if (tracker->default_cursor)
     return;
@@ -327,12 +701,19 @@ ensure_wayland_cursor (MetaCursorTracker *tracker)
                                "cursors/left_ptr.png",
                                NULL);
 
-  bitmap = cogl_bitmap_new_from_file (filename, NULL);
-  tracker->default_cursor = cogl_texture_2d_new_from_bitmap (bitmap,
-                                                             COGL_PIXEL_FORMAT_ANY,
-                                                             NULL);
+  error = NULL;
+  tracker->default_cursor = meta_cursor_reference_load_file (tracker, filename, &error);
+  if (!tracker->default_cursor)
+    {
+      if (error)
+        g_error ("Failed to load default cursor: %s", error->message);
+      else
+        g_error ("Failed to load default cursor");
+    }
+
+  tracker->default_cursor->hot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X;
+  tracker->default_cursor->hot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y;
 
-  cogl_object_unref (bitmap);
   g_free (filename);
 }
 
@@ -356,42 +737,111 @@ meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
       /* FIXME! We need to load all the other cursors too */
       ensure_wayland_cursor (tracker);
 
-      g_clear_pointer (&tracker->root_cursor, cogl_object_unref);
-      tracker->root_cursor = cogl_object_ref (tracker->default_cursor);
-      tracker->root_hot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X;
-      tracker->root_hot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y;
+      g_clear_pointer (&tracker->root_cursor, meta_cursor_reference_unref);
+      tracker->root_cursor = meta_cursor_reference_ref (tracker->default_cursor);
     }
 }
 
 void
 meta_cursor_tracker_revert_root (MetaCursorTracker *tracker)
 {
-  meta_cursor_tracker_set_sprite (tracker,
-                                  tracker->root_cursor,
-                                  tracker->root_hot_x,
-                                  tracker->root_hot_y);
+  meta_cursor_tracker_set_sprite (tracker, tracker->root_cursor);
+}
+
+static void
+update_hw_cursor (MetaCursorTracker *tracker)
+{
+  MetaMonitorManager *monitors;
+  MetaCRTC *crtcs;
+  unsigned int i, n_crtcs;
+  gboolean enabled;
+
+  enabled = tracker->has_cursor && tracker->sprite->bo != NULL;
+  tracker->has_hw_cursor = enabled;
+
+  monitors = meta_monitor_manager_get ();
+  meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL);
+
+  for (i = 0; i < n_crtcs; i++)
+    {
+      MetaRectangle *rect = &crtcs[i].rect;
+      gboolean has;
+
+      has = enabled && meta_rectangle_overlap (&tracker->current_rect, rect);
+
+      if (has || crtcs[i].has_hw_cursor)
+        meta_cursor_tracker_set_crtc_has_hw_cursor (tracker, &crtcs[i], has);
+    }
+}
+
+static void
+move_hw_cursor (MetaCursorTracker *tracker)
+{
+  MetaMonitorManager *monitors;
+  MetaCRTC *crtcs;
+  unsigned int i, n_crtcs;
+
+  monitors = meta_monitor_manager_get ();
+  meta_monitor_manager_get_resources (monitors, NULL, NULL, &crtcs, &n_crtcs, NULL, NULL);
+
+  g_assert (tracker->has_hw_cursor);
+
+  for (i = 0; i < n_crtcs; i++)
+    {
+      MetaRectangle *rect = &crtcs[i].rect;
+      gboolean has;
+
+      has = meta_rectangle_overlap (&tracker->current_rect, rect);
+
+      if (has != crtcs[i].has_hw_cursor)
+        meta_cursor_tracker_set_crtc_has_hw_cursor (tracker, &crtcs[i], has);
+      if (has)
+        drmModeMoveCursor (tracker->drm_fd, crtcs[i].crtc_id,
+                           tracker->current_rect.x - rect->x,
+                           tracker->current_rect.y - rect->y);
+    }
 }
 
 void
-meta_cursor_tracker_set_sprite (MetaCursorTracker *tracker,
-                                CoglTexture2D     *sprite,
-                                int                hot_x,
-                                int                hot_y)
+meta_cursor_tracker_set_buffer (MetaCursorTracker  *tracker,
+                                struct wl_resource *buffer,
+                                int                 hot_x,
+                                int                 hot_y)
+{
+  MetaCursorReference *new_cursor;
+
+  if (buffer)
+    {
+      new_cursor = meta_cursor_reference_from_buffer (tracker, buffer, hot_x, hot_y);
+      meta_cursor_tracker_set_sprite (tracker, new_cursor);
+      meta_cursor_reference_unref (new_cursor);
+    }
+  else
+    meta_cursor_tracker_set_sprite (tracker, NULL);
+}
+
+static void
+meta_cursor_tracker_set_sprite (MetaCursorTracker   *tracker,
+                                MetaCursorReference *sprite)
 {
   g_assert (meta_is_wayland_compositor ());
 
-  g_clear_pointer (&tracker->sprite, cogl_object_unref);
+  if (sprite == tracker->sprite)
+    return;
+
+  g_clear_pointer (&tracker->sprite, meta_cursor_reference_unref);
 
   if (sprite)
     {
-      tracker->sprite = cogl_object_ref (sprite);
-      tracker->hot_x = hot_x;
-      tracker->hot_y = hot_y;
-      cogl_pipeline_set_layer_texture (tracker->pipeline, 0, COGL_TEXTURE (tracker->sprite));
+      tracker->sprite = meta_cursor_reference_ref (sprite);
+      cogl_pipeline_set_layer_texture (tracker->pipeline, 0, COGL_TEXTURE (tracker->sprite->texture));
     }
   else
     cogl_pipeline_set_layer_texture (tracker->pipeline, 0, NULL);
 
+  tracker->has_cursor = tracker->sprite != NULL && tracker->is_showing;
+  update_hw_cursor (tracker);
+
   g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
 
   meta_cursor_tracker_update_position (tracker, tracker->current_x, tracker->current_y);
@@ -406,19 +856,24 @@ meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
 
   tracker->current_x = new_x;
   tracker->current_y = new_y;
-  tracker->current_rect.x = tracker->current_x - tracker->hot_x;
-  tracker->current_rect.y = tracker->current_y - tracker->hot_y;
 
   if (tracker->sprite)
     {
-      tracker->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (tracker->sprite));
-      tracker->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (tracker->sprite));
+      tracker->current_rect.x = tracker->current_x - tracker->sprite->hot_x;
+      tracker->current_rect.y = tracker->current_y - tracker->sprite->hot_y;
+      tracker->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (tracker->sprite->texture));
+      tracker->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (tracker->sprite->texture));
     }
   else
     {
+      tracker->current_rect.x = 0;
+      tracker->current_rect.y = 0;
       tracker->current_rect.width = 0;
       tracker->current_rect.height = 0;
     }
+
+  if (tracker->has_hw_cursor)
+    move_hw_cursor (tracker);
 }
 
 void
@@ -426,10 +881,9 @@ meta_cursor_tracker_paint (MetaCursorTracker *tracker)
 {
   g_assert (meta_is_wayland_compositor ());
 
-  if (tracker->sprite == NULL || tracker->is_showing == FALSE)
+  if (tracker->has_hw_cursor || !tracker->has_cursor)
     return;
 
-  /* FIXME: try to use a DRM cursor when possible */
   cogl_framebuffer_draw_rectangle (cogl_get_draw_framebuffer (),
                                    tracker->pipeline,
                                    tracker->current_rect.x,
@@ -447,18 +901,58 @@ void
 meta_cursor_tracker_queue_redraw (MetaCursorTracker *tracker,
                                   ClutterActor      *stage)
 {
+  cairo_rectangle_int_t clip;
+
   g_assert (meta_is_wayland_compositor ());
 
   if (tracker->previous_is_valid)
     {
-      clutter_actor_queue_redraw_with_clip (stage, &tracker->previous_rect);
+      cairo_rectangle_int_t clip = {
+        .x = tracker->previous_rect.x,
+        .y = tracker->previous_rect.y,
+        .width = tracker->previous_rect.width,
+        .height = tracker->previous_rect.height
+      };
+      clutter_actor_queue_redraw_with_clip (stage, &clip);
       tracker->previous_is_valid = FALSE;
     }
 
-  if (tracker->sprite == NULL)
+  if (tracker->has_hw_cursor || !tracker->has_cursor)
     return;
 
-  clutter_actor_queue_redraw_with_clip (stage, &tracker->current_rect);
+  clip.x = tracker->current_rect.x;
+  clip.y = tracker->current_rect.y;
+  clip.width = tracker->current_rect.width;
+  clip.height = tracker->current_rect.height;
+  clutter_actor_queue_redraw_with_clip (stage, &clip);
+}
+
+static void
+meta_cursor_tracker_set_crtc_has_hw_cursor (MetaCursorTracker *tracker,
+                                            MetaCRTC          *crtc,
+                                            gboolean           has)
+{
+  if (has)
+    {
+      union gbm_bo_handle handle;
+      int width, height;
+      int hot_x, hot_y;
+
+      handle = gbm_bo_get_handle (tracker->sprite->bo);
+      width = gbm_bo_get_width (tracker->sprite->bo);
+      height = gbm_bo_get_height (tracker->sprite->bo);
+      hot_x = tracker->sprite->hot_x;
+      hot_y = tracker->sprite->hot_y;
+
+      drmModeSetCursor2 (tracker->drm_fd, crtc->crtc_id, handle.u32,
+                         width, height, hot_x, hot_y);
+      crtc->has_hw_cursor = TRUE;
+    }
+  else
+    {
+      drmModeSetCursor2 (tracker->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0);
+      crtc->has_hw_cursor = FALSE;
+    }
 }
 
 static void
@@ -527,6 +1021,9 @@ meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker,
       MetaWaylandCompositor *compositor;
 
       compositor = meta_wayland_compositor_get_default ();
+
+      tracker->has_cursor = tracker->sprite != NULL && visible;
+      update_hw_cursor (tracker);
       meta_cursor_tracker_queue_redraw (tracker, compositor->stage);
     }
   else
diff --git a/src/core/monitor-private.h b/src/core/monitor-private.h
index 2b1cea1..a8eeb67 100644
--- a/src/core/monitor-private.h
+++ b/src/core/monitor-private.h
@@ -136,6 +136,9 @@ struct _MetaCRTC
 
   /* Used when changing configuration */
   gboolean is_dirty;
+
+  /* Updated by MetaCursorTracker */
+  gboolean has_hw_cursor;
 };
 
 struct _MetaMonitorMode
diff --git a/src/core/monitor.c b/src/core/monitor.c
index 12f1cd7..5b5093c 100644
--- a/src/core/monitor.c
+++ b/src/core/monitor.c
@@ -1361,12 +1361,21 @@ meta_monitor_manager_get_resources (MetaMonitorManager  *manager,
                                     MetaOutput         **outputs,
                                     unsigned int        *n_outputs)
 {
-  *modes = manager->modes;
-  *n_modes = manager->n_modes;
-  *crtcs = manager->crtcs;
-  *n_crtcs = manager->n_crtcs;
-  *outputs = manager->outputs;
-  *n_outputs = manager->n_outputs;
+  if (modes)
+    {
+      *modes = manager->modes;
+      *n_modes = manager->n_modes;
+    }
+  if (crtcs)
+    {
+      *crtcs = manager->crtcs;
+      *n_crtcs = manager->n_crtcs;
+    }
+  if (outputs)
+    {
+      *outputs = manager->outputs;
+      *n_outputs = manager->n_outputs;
+    }
 }
 
 int
diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c
index 1b76744..a55071e 100644
--- a/src/wayland/meta-wayland-seat.c
+++ b/src/wayland/meta-wayland-seat.c
@@ -77,7 +77,7 @@ pointer_unmap_sprite (MetaWaylandSeat *seat)
 {
   if (seat->cursor_tracker)
     {
-      meta_cursor_tracker_set_sprite (seat->cursor_tracker,
+      meta_cursor_tracker_set_buffer (seat->cursor_tracker,
                                      NULL, 0, 0);
 
       if (seat->current_stage)
@@ -95,29 +95,20 @@ pointer_unmap_sprite (MetaWaylandSeat *seat)
 void
 meta_wayland_seat_update_sprite (MetaWaylandSeat *seat)
 {
-  ClutterBackend *backend;
-  CoglContext *context;
   struct wl_resource *buffer;
-  CoglTexture2D *texture;
 
   if (seat->cursor_tracker == NULL)
     return;
 
-  backend = clutter_get_default_backend ();
-  context = clutter_backend_get_cogl_context (backend);
   buffer = seat->sprite->buffer_ref.buffer->resource;
-  texture = cogl_wayland_texture_2d_new_from_buffer (context, buffer, NULL);
-
-  meta_cursor_tracker_set_sprite (seat->cursor_tracker,
-                                 texture,
+  meta_cursor_tracker_set_buffer (seat->cursor_tracker,
+                                 buffer,
                                  seat->hotspot_x,
                                  seat->hotspot_y);
 
   if (seat->current_stage)
     meta_cursor_tracker_queue_redraw (seat->cursor_tracker,
                                      CLUTTER_ACTOR (seat->current_stage));
-
-  cogl_object_unref (texture);
 }
 
 static void


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