[clutter/wip/wayland-surface-actor: 2/2] Adds wayland-surface actor for wayland compositors



commit 67e92d94523cd679b45a5394ae17afbd850ce75e
Author: Robert Bragg <robert linux intel com>
Date:   Fri May 13 16:54:11 2011 +0100

    Adds wayland-surface actor for wayland compositors
    
    This adds a --enable-wayland-compositor configure option which will add
    support for a ClutterWaylandSurface actor which can be used to aid in
    writing Wayland compositors using Clutter by providing a ClutterActor to
    represent Wayland client surfaces.
    
    Notably this configure option isn't tied into the flavour option since
    conceptually any of the flavours could be used to write a Wayland
    compositor.

 clutter/Makefile.am                          |    8 +
 clutter/cogl/clutter-backend-cogl.c          |   37 ++
 clutter/cogl/clutter-stage-cogl.c            |    2 +-
 clutter/wayland/clutter-wayland-compositor.h |   43 ++
 clutter/wayland/clutter-wayland-surface.c    |  560 ++++++++++++++++++++++++++
 clutter/wayland/clutter-wayland-surface.h    |   94 +++++
 configure.ac                                 |   25 ++
 doc/reference/clutter/clutter-sections.txt   |   15 +
 tests/interactive/Makefile.am                |    4 +
 tests/interactive/test-wayland-surface.c     |  447 ++++++++++++++++++++
 10 files changed, 1234 insertions(+), 1 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 943f7be..014b8af 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -509,6 +509,14 @@ backend_source_c += \
 	$(srcdir)/wayland/clutter-device-manager-wayland.c
 endif # SUPPORT_WAYLAND
 
+if SUPPORT_WAYLAND_COMPOSITOR
+backend_source_h += \
+	$(srcdir)/wayland/clutter-wayland-compositor.h		\
+	$(srcdir)/wayland/clutter-wayland-surface.h
+backend_source_c += \
+	$(srcdir)/wayland/clutter-wayland-surface.c
+endif
+
 if SUPPORT_EGL
 backend_source_h += $(cogl_source_h) $(egl_source_h)
 backend_source_c += $(cogl_source_c)
diff --git a/clutter/cogl/clutter-backend-cogl.c b/clutter/cogl/clutter-backend-cogl.c
index fdfbf38..9df1098 100644
--- a/clutter/cogl/clutter-backend-cogl.c
+++ b/clutter/cogl/clutter-backend-cogl.c
@@ -58,6 +58,9 @@
 #include "wayland/clutter-device-manager-wayland.h"
 #include "wayland/clutter-event-wayland.h"
 #endif
+#ifdef HAVE_CLUTTER_WAYLAND_COMPOSITOR
+#include <wayland-server.h>
+#endif
 
 static ClutterBackendCogl *backend_singleton = NULL;
 
@@ -69,6 +72,10 @@ static gdl_plane_id_t gdl_plane = GDL_PLANE_ID_UPP_C;
 static guint gdl_n_buffers = CLUTTER_CEX100_TRIPLE_BUFFERING;
 #endif
 
+#ifdef HAVE_CLUTTER_WAYLAND_COMPOSITOR
+static struct wl_display *wayland_compositor_display;
+#endif
+
 #ifdef COGL_HAS_X11_SUPPORT
 G_DEFINE_TYPE (ClutterBackendCogl, _clutter_backend_cogl, CLUTTER_TYPE_BACKEND_X11);
 #else
@@ -452,6 +459,11 @@ clutter_backend_cogl_create_context (ClutterBackend  *backend,
   backend->cogl_display = cogl_display_new (backend->cogl_renderer,
                                             onscreen_template);
 
+#ifdef HAVE_CLUTTER_WAYLAND_COMPOSITOR
+  cogl_wayland_display_set_compositor_display (backend->cogl_display,
+                                               wayland_compositor_display);
+#endif
+
 #ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT
   cogl_gdl_display_set_plane (backend->cogl_display, gdl_plane);
 #endif
@@ -646,3 +658,28 @@ clutter_cex100_set_buffering_mode (ClutterCex100BufferingMode mode)
   gdl_n_buffers = mode;
 }
 #endif
+
+#ifdef HAVE_CLUTTER_WAYLAND_COMPOSITOR
+/**
+ * clutter_wayland_set_compositor_display:
+ * @display: A compositor side struct wl_display pointer
+ *
+ * This informs Clutter of your compositor side Wayland display
+ * object. This must be called before calling clutter_init().
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+void
+clutter_wayland_set_compositor_display (struct wl_display *display)
+{
+  if (_clutter_context_is_initialized ())
+    {
+      g_warning ("%s() can only be used before calling clutter_init()",
+                 G_STRFUNC);
+      return;
+    }
+
+  wayland_compositor_display = display;
+}
+#endif
diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c
index be42827..055dc94 100644
--- a/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/cogl/clutter-stage-cogl.c
@@ -158,7 +158,7 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
 
 #ifdef COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT
     wl_surface = cogl_wayland_onscreen_get_surface (stage_cogl->onscreen);
-    wl_input_device_set_user_data (wl_surface, stage_cogl);
+    wl_surface_set_user_data (wl_surface, stage_cogl);
 #endif
 
   if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT))
diff --git a/clutter/wayland/clutter-wayland-compositor.h b/clutter/wayland/clutter-wayland-compositor.h
new file mode 100644
index 0000000..f7fa96e
--- /dev/null
+++ b/clutter/wayland/clutter-wayland-compositor.h
@@ -0,0 +1,43 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *   Robert Bragg <robert linux intel com>
+ */
+
+/**
+ * SECTION:clutter-wayland-compositor
+ * @short_description: Wayland compositor specific APIs
+ *
+ * Clutter provides some Wayland specific APIs to aid in writing
+ * Clutter based compositors.
+ *
+ * The Clutter Wayland compositor API is available since Clutter 1.8
+ */
+
+#include <wayland-server.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+
+G_BEGIN_DECLS
+
+void
+clutter_wayland_set_compositor_display (struct wl_display *display);
+
+G_END_DECLS
diff --git a/clutter/wayland/clutter-wayland-surface.c b/clutter/wayland/clutter-wayland-surface.c
new file mode 100644
index 0000000..da9a029
--- /dev/null
+++ b/clutter/wayland/clutter-wayland-surface.c
@@ -0,0 +1,560 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Robert Bragg <robert linux intel com>
+ */
+
+/**
+ * SECTION:clutter-wayland-surface
+ * @Title: ClutterWaylandSurface
+ * @short_description: An actor which displays the content of a client surface
+ *
+ * #ClutterWaylandSurface is an actor for displaying the contents of a client
+ * surface. It is intended to support developers implementing Clutter based
+ * wayland compositors.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-wayland-surface.h"
+
+#include "clutter-actor-private.h"
+#include "clutter-marshal.h"
+#include "clutter-paint-volume-private.h"
+#include "clutter-private.h"
+#include "clutter-backend-private.h"
+
+#include <cogl/cogl.h>
+
+#include <wayland-server.h>
+
+enum
+{
+  PROP_SURFACE = 1,
+  PROP_WIDTH,
+  PROP_HEIGHT
+};
+
+#if 0
+enum
+{
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+#endif
+
+struct _ClutterWaylandSurfacePrivate
+{
+  struct wl_surface *surface;
+  CoglTexture2D *buffer;
+  int width, height;
+  CoglPipeline *pipeline;
+  GArray *damage;
+};
+
+G_DEFINE_TYPE (ClutterWaylandSurface,
+               clutter_wayland_surface,
+               CLUTTER_TYPE_ACTOR);
+
+static gboolean
+clutter_wayland_surface_get_paint_volume (ClutterActor *self,
+                                          ClutterPaintVolume *volume)
+{
+  return clutter_paint_volume_set_from_allocation (volume, self);
+}
+
+static void
+clutter_wayland_surface_queue_damage_redraw (ClutterWaylandSurface *texture,
+                                             gint x,
+                                             gint y,
+                                             gint width,
+                                             gint height)
+{
+  ClutterWaylandSurfacePrivate *priv = texture->priv;
+  ClutterActor *self = CLUTTER_ACTOR (texture);
+  ClutterActorBox allocation;
+  float scale_x;
+  float scale_y;
+  ClutterVertex origin;
+  ClutterPaintVolume clip;
+
+  /* NB: clutter_actor_queue_clipped_redraw expects a box in the actor's
+   * coordinate space so we need to convert from surface coordinates to
+   * actor coordinates...
+   */
+
+  /* Calling clutter_actor_get_allocation_box() is enormously expensive
+   * if the actor has an out-of-date allocation, since it triggers
+   * a full redraw. clutter_actor_queue_clipped_redraw() would redraw
+   * the whole stage anyways in that case, so just go ahead and do
+   * it here.
+   */
+  if (!clutter_actor_has_allocation (self))
+    {
+      clutter_actor_queue_redraw (self);
+      return;
+    }
+
+  if (priv->width == 0 || priv->height == 0)
+    return;
+
+  clutter_actor_get_allocation_box (self, &allocation);
+
+  scale_x = (allocation.x2 - allocation.x1) / priv->width;
+  scale_y = (allocation.y2 - allocation.y1) / priv->height;
+
+  _clutter_paint_volume_init_static (&clip, self);
+
+  origin.x = x * scale_x;
+  origin.y = y * scale_y;
+  origin.z = 0;
+  clutter_paint_volume_set_origin (&clip, &origin);
+  clutter_paint_volume_set_width (&clip, width * scale_x);
+  clutter_paint_volume_set_height (&clip, height * scale_y);
+
+  _clutter_actor_queue_redraw_with_clip (self, 0, &clip);
+  clutter_paint_volume_free (&clip);
+}
+
+static void
+free_pipeline (ClutterWaylandSurface *self)
+{
+  ClutterWaylandSurfacePrivate *priv = self->priv;
+
+  if (priv->pipeline)
+    {
+      cogl_object_unref (priv->pipeline);
+      priv->pipeline = NULL;
+    }
+}
+
+static void
+opacity_change_cb (ClutterWaylandSurface *self)
+{
+  free_pipeline (self);
+}
+
+static void
+clutter_wayland_surface_init (ClutterWaylandSurface *self)
+{
+  ClutterWaylandSurfacePrivate *priv =
+      G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                   CLUTTER_WAYLAND_TYPE_SURFACE,
+                                   ClutterWaylandSurfacePrivate);
+
+  priv->surface = NULL;
+  priv->width = 0;
+  priv->height = 0;
+  priv->damage = g_array_new (FALSE, FALSE, sizeof (int));
+
+  self->priv = priv;
+
+  g_signal_connect (self, "notify::opacity", G_CALLBACK (opacity_change_cb), NULL);
+}
+
+static void
+clutter_wayland_surface_dispose (GObject *object)
+{
+  ClutterWaylandSurface *self = CLUTTER_WAYLAND_SURFACE (object);
+  ClutterWaylandSurfacePrivate *priv = self->priv;
+
+  if (priv->damage)
+    {
+      g_array_free (priv->damage, TRUE);
+      priv->damage = NULL;
+    }
+
+  G_OBJECT_CLASS (clutter_wayland_surface_parent_class)->dispose (object);
+}
+
+static void
+set_size (ClutterWaylandSurface *self,
+          int width,
+          int height)
+{
+  ClutterWaylandSurfacePrivate *priv = self->priv;
+
+  if (priv->width != width)
+    {
+      priv->width = width;
+      g_object_notify (G_OBJECT (self), "width");
+    }
+  if (priv->height != height)
+    {
+      priv->height = height;
+      g_object_notify (G_OBJECT (self), "height");
+    }
+}
+
+static void
+clutter_wayland_surface_set_surface (ClutterWaylandSurface *self,
+                                     struct wl_surface *surface)
+{
+  ClutterWaylandSurfacePrivate *priv;
+
+  g_return_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self));
+
+  priv = self->priv;
+
+  g_return_if_fail (priv->surface == NULL);
+  priv->surface = surface;
+
+  /* XXX: should we freeze/thaw notifications? */
+
+  g_object_notify (G_OBJECT (self), "surface");
+
+  /* We have to wait until the next attach event to find out the surface
+   * geometry... */
+  set_size (self, 0, 0);
+}
+
+static void
+clutter_wayland_surface_set_property (GObject *object,
+                                      guint prop_id,
+                                      const GValue *value,
+                                      GParamSpec *pspec)
+{
+  ClutterWaylandSurface *self = CLUTTER_WAYLAND_SURFACE (object);
+
+  switch (prop_id)
+    {
+    case PROP_SURFACE:
+      clutter_wayland_surface_set_surface (self, g_value_get_pointer (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_wayland_surface_get_property (GObject *object,
+                                      guint prop_id,
+                                      GValue *value,
+                                      GParamSpec *pspec)
+{
+  ClutterWaylandSurface *self = CLUTTER_WAYLAND_SURFACE (object);
+  ClutterWaylandSurfacePrivate *priv = self->priv;
+
+  switch (prop_id)
+    {
+    case PROP_SURFACE:
+      g_value_set_pointer (value, priv->surface);
+      break;
+    case PROP_WIDTH:
+      g_value_set_uint (value, priv->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_uint (value, priv->height);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_wayland_surface_paint (ClutterActor *self)
+{
+  ClutterWaylandSurfacePrivate *priv;
+  ClutterActorBox box;
+
+  g_return_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self));
+
+  priv = CLUTTER_WAYLAND_SURFACE (self)->priv;
+
+  if (G_UNLIKELY (priv->pipeline == NULL))
+    {
+      guint8 paint_opacity = clutter_actor_get_paint_opacity (self);
+
+      priv->pipeline = cogl_pipeline_new ();
+      cogl_pipeline_set_color4ub (priv->pipeline,
+                                  paint_opacity,
+                                  paint_opacity,
+                                  paint_opacity,
+                                  paint_opacity);
+      cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->buffer);
+    }
+
+  cogl_set_source (priv->pipeline);
+  clutter_actor_get_allocation_box (self, &box);
+  cogl_rectangle (0, 0, box.x2 - box.x1, box.y2 - box.y1);
+}
+
+static void
+clutter_wayland_surface_pick (ClutterActor *self,
+                              const ClutterColor *color)
+{
+  ClutterActorBox box;
+
+  cogl_set_source_color4ub (color->red, color->green, color->blue,
+                            color->alpha);
+  clutter_actor_get_allocation_box (self, &box);
+  cogl_rectangle (0, 0, box.x2 - box.x1, box.y2 - box.y1);
+}
+
+static void
+clutter_wayland_surface_get_preferred_width (ClutterActor *self,
+                                             gfloat for_height,
+                                             gfloat *min_width_p,
+                                             gfloat *natural_width_p)
+{
+  ClutterWaylandSurfacePrivate *priv;
+
+  g_return_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self));
+
+  priv = CLUTTER_WAYLAND_SURFACE (self)->priv;
+
+  if (min_width_p)
+    *min_width_p = 0;
+
+  if (natural_width_p)
+    *natural_width_p = priv->width;
+}
+
+static void
+clutter_wayland_surface_get_preferred_height (ClutterActor *self,
+                                              gfloat for_width,
+                                              gfloat *min_height_p,
+                                              gfloat *natural_height_p)
+{
+  ClutterWaylandSurfacePrivate *priv;
+
+  g_return_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self));
+
+  priv = CLUTTER_WAYLAND_SURFACE (self)->priv;
+
+  if (min_height_p)
+    *min_height_p = 0;
+
+  if (natural_height_p)
+    *natural_height_p = priv->height;
+}
+
+static gboolean
+clutter_wayland_surface_has_overlaps (ClutterActor *self)
+{
+  /* Rectangles never need an offscreen redirect because there are
+     never any overlapping primitives */
+  return FALSE;
+}
+
+static void
+clutter_wayland_surface_class_init (ClutterWaylandSurfaceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ClutterWaylandSurfacePrivate));
+
+  actor_class->get_paint_volume = clutter_wayland_surface_get_paint_volume;
+  actor_class->paint = clutter_wayland_surface_paint;
+  actor_class->pick = clutter_wayland_surface_pick;
+  actor_class->get_preferred_width =
+    clutter_wayland_surface_get_preferred_width;
+  actor_class->get_preferred_height =
+    clutter_wayland_surface_get_preferred_height;
+  actor_class->has_overlaps = clutter_wayland_surface_has_overlaps;
+
+  object_class->dispose      = clutter_wayland_surface_dispose;
+  object_class->set_property = clutter_wayland_surface_set_property;
+  object_class->get_property = clutter_wayland_surface_get_property;
+
+  pspec = g_param_spec_pointer ("surface",
+			        P_("Surface"),
+			        P_("The underlying wayland surface"),
+                                CLUTTER_PARAM_READWRITE|
+                                G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_property (object_class, PROP_SURFACE, pspec);
+
+  pspec = g_param_spec_uint ("width",
+                             P_("Surface width"),
+                             P_("The width of the underlying wayland surface"),
+                             0, G_MAXUINT,
+                             0,
+                             G_PARAM_READABLE);
+
+  g_object_class_install_property (object_class, PROP_WIDTH, pspec);
+
+  pspec = g_param_spec_uint ("height",
+                             P_("Surface height"),
+                             P_("The height of the underlying wayland surface"),
+                             0, G_MAXUINT,
+                             0,
+                             G_PARAM_READABLE);
+
+  g_object_class_install_property (object_class, PROP_HEIGHT, pspec);
+}
+
+/**
+ * clutter_wayland_surface_new:
+ * @surface: the Wayland surface this actor should represent
+ *
+ * Creates a new #ClutterWaylandSurface for @surface
+ *
+ * Return value: A new #ClutterWaylandSurface representing @surface
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+ClutterActor *
+clutter_wayland_surface_new (struct wl_surface *surface)
+{
+  ClutterActor *actor;
+
+  actor = g_object_new (CLUTTER_WAYLAND_TYPE_SURFACE,
+                        "surface", surface,
+                        NULL);
+
+  return actor;
+}
+
+static void
+free_surface_buffers (ClutterWaylandSurface *self)
+{
+  ClutterWaylandSurfacePrivate *priv = self->priv;
+
+  if (priv->buffer)
+    {
+      cogl_object_unref (priv->buffer);
+      priv->buffer = NULL;
+      free_pipeline (self);
+    }
+}
+
+/**
+ * clutter_wayland_surface_attach_buffer:
+ * @self: A #ClutterWaylandSurface actor
+ * @buffer: A compositor side struct wl_buffer pointer
+ * @error: A #GError
+ *
+ * This associates a client's buffer with the #ClutterWaylandSurface
+ * actor @self. This will automatically result in @self being re-drawn
+ * with the new buffer contents.
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+gboolean
+clutter_wayland_surface_attach_buffer (ClutterWaylandSurface *self,
+                                       struct wl_buffer *buffer,
+                                       GError **error)
+{
+  ClutterWaylandSurfacePrivate *priv;
+  ClutterBackend *backend = clutter_get_default_backend ();
+  CoglContext *context = clutter_backend_get_cogl_context (backend);
+
+  g_return_val_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self), TRUE);
+
+  priv = self->priv;
+
+  free_surface_buffers (self);
+
+  set_size (self, buffer->width, buffer->height);
+
+  priv->buffer =
+    cogl_wayland_texture_2d_new_from_buffer (context, buffer, error);
+
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+
+  if (!priv->buffer)
+    return FALSE;
+
+  return TRUE;
+}
+
+static CoglPixelFormat
+get_buffer_format (struct wl_buffer *wayland_buffer)
+{
+  struct wl_compositor *compositor = wayland_buffer->compositor;
+  struct wl_visual *visual = wayland_buffer->visual;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  if (visual == &compositor->premultiplied_argb_visual)
+    return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+  else if (visual == &compositor->argb_visual)
+    return COGL_PIXEL_FORMAT_ARGB_8888;
+  else if (visual == &compositor->rgb_visual)
+    return COGL_PIXEL_FORMAT_RGB_888;
+#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
+  if (visual == &compositor->premultiplied_argb_visual)
+    return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+  else if (visual == &compositor->argb_visual)
+    return COGL_PIXEL_FORMAT_BGRA_8888;
+  else if (visual == &compositor->rgb_visual)
+    return COGL_PIXEL_FORMAT_BGR_888;
+#endif
+  else
+    g_return_val_if_reached (COGL_PIXEL_FORMAT_ANY);
+}
+
+/**
+ * clutter_wayland_surface_damage_buffer:
+ * @self: A #ClutterWaylandSurface actor
+ * @buffer: A compositor side struct wl_buffer pointer
+ * @x: The x coordinate of the damaged rectangle
+ * @y: The y coordinate of the damaged rectangle
+ * @width: The width of the damaged rectangle
+ * @height: The height of the damaged rectangle
+ *
+ * This marks a region of the given @buffer has having been changed by
+ * the client. This will automatically result in the corresponding damaged
+ * region of the actor @self being redrawn.
+ *
+ * If multiple regions are changed then this should be called multiple
+ * times with different damage rectangles.
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+void
+clutter_wayland_surface_damage_buffer (ClutterWaylandSurface *self,
+                                       struct wl_buffer *buffer,
+                                       gint32 x,
+                                       gint32 y,
+                                       gint32 width,
+                                       gint32 height)
+{
+  ClutterWaylandSurfacePrivate *priv;
+
+  g_return_if_fail (CLUTTER_WAYLAND_IS_SURFACE (self));
+
+  priv = self->priv;
+
+  if (priv->buffer && wl_buffer_is_shm (buffer))
+    {
+      cogl_texture_set_region (priv->buffer,
+                               x, y,
+                               x, y,
+                               width, height,
+                               width, height,
+                               get_buffer_format (buffer),
+                               wl_shm_buffer_get_stride (buffer),
+                               wl_shm_buffer_get_data (buffer));
+    }
+
+  clutter_wayland_surface_queue_damage_redraw (self, x, y, width, height);
+}
diff --git a/clutter/wayland/clutter-wayland-surface.h b/clutter/wayland/clutter-wayland-surface.h
new file mode 100644
index 0000000..187ca80
--- /dev/null
+++ b/clutter/wayland/clutter-wayland-surface.h
@@ -0,0 +1,94 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *   Robert Bragg <robert linux intel com>
+ *
+ */
+
+#ifndef __CLUTTER_WAYLAND_SURFACE_H__
+#define __CLUTTER_WAYLAND_SURFACE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+#include <wayland-server.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_WAYLAND_TYPE_SURFACE                 (clutter_wayland_surface_get_type ())
+#define CLUTTER_WAYLAND_SURFACE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_WAYLAND_TYPE_SURFACE, ClutterWaylandSurface))
+#define CLUTTER_WAYLAND_SURFACE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_WAYLAND_TYPE_SURFACE, ClutterWaylandSurfaceClass))
+#define CLUTTER_WAYLAND_IS_SURFACE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_WAYLAND_TYPE_SURFACE))
+#define CLUTTER_WAYLAND_IS_SURFACE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_WAYLAND_TYPE_SURFACE))
+#define CLUTTER_WAYLAND_SURFACE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_WAYLAND_TYPE_SURFACE, ClutterWaylandSurfaceClass))
+
+typedef struct _ClutterWaylandSurface        ClutterWaylandSurface;
+typedef struct _ClutterWaylandSurfaceClass   ClutterWaylandSurfaceClass;
+typedef struct _ClutterWaylandSurfacePrivate ClutterWaylandSurfacePrivate;
+
+/**
+ * ClutterWaylandSurface:
+ *
+ * The #ClutterWaylandSurface structure contains only private data
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+struct _ClutterWaylandSurface
+{
+  /*< private >*/
+  ClutterActor parent;
+
+  ClutterWaylandSurfacePrivate *priv;
+};
+
+/**
+ * ClutterWaylandSurfaceClass:
+ *
+ * The #ClutterWaylandSurfaceClass structure contains only private data
+ *
+ * Since: 0.8
+ * Stability: unstable
+ */
+struct _ClutterWaylandSurfaceClass
+{
+  /*< private >*/
+  ClutterActorClass parent_class;
+};
+
+GType clutter_wayland_surface_get_type (void) G_GNUC_CONST;
+
+ClutterActor *clutter_wayland_surface_new               (struct wl_surface *surface);
+gboolean      clutter_wayland_surface_attach_buffer     (ClutterWaylandSurface *self,
+                                                         struct wl_buffer *buffer,
+                                                         GError **error);
+void          clutter_wayland_surface_damage_buffer     (ClutterWaylandSurface *self,
+                                                         struct wl_buffer *buffer,
+                                                         gint32 x,
+                                                         gint32 y,
+                                                         gint32 width,
+                                                         gint32 height);
+
+G_END_DECLS
+
+#endif
diff --git a/configure.ac b/configure.ac
index 1206e72..14c79a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -395,6 +395,29 @@ AS_IF([test "x$SUPPORT_X11" = "x1"],
                          [])
       ])
 
+dnl Note this is separate from the flavour since it doesn't correspond to a backend
+dnl and conceptually you could support the wayland compositor features with multiple
+dnl backends.
+AC_ARG_ENABLE([wayland-compositor],
+              [AS_HELP_STRING([--enable-wayland-compositor], [Enable Wayland compositor features])],
+              [],
+              [AS_IF([test "x$SUPPORT_EGL" = "x1"],
+                     [enable_wayland_compositor=yes],
+                     [enable_wayland_compositor=no])
+              ])
+
+AS_IF([test "x$enable_wayland_compositor" = "xyes"],
+      [
+        AS_IF([test "x$SUPPORT_EGL" != "x1"],
+              [AC_MSG_ERROR([Wayland compositor features currently only available for EGL flavours])])
+
+        PKG_CHECK_EXISTS([wayland-server],
+			 [BACKEND_PC_FILES="$BACKEND_PC_FILES wayland-server"], [])
+        SUPPORT_WAYLAND_COMPOSITOR=1
+        AC_DEFINE([HAVE_CLUTTER_WAYLAND_COMPOSITOR], [1], [Have wayland compositor support])
+      ])
+AM_CONDITIONAL(SUPPORT_WAYLAND_COMPOSITOR, [test "x$SUPPORT_WAYLAND_COMPOSITOR" = "x1"])
+
 AS_IF([test "x$SUPPORT_GLX" = "x1"],
       [
         AC_DEFINE([HAVE_CLUTTER_GLX], [1], [Have the GLX backend])
@@ -1039,6 +1062,8 @@ echo "        Enable XKB: ${have_xkb}"
 echo "        Enable X11 tests: ${x11_tests}"
 fi
 
+echo "        Wayland compositor features: ${SUPPORT_WAYLAND_COMPOSITOR}"
+
 echo ""
 
 # General warning about experimental features
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 29d7c3b..8ab05be 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -1337,6 +1337,21 @@ clutter_glx_texture_pixmap_get_type
 </SECTION>
 
 <SECTION>
+<FILE>clutter-wayland-compositor</FILE>
+<TITLE>Wayland compositor specific support</TITLE>
+clutter_wayland_set_compositor_display
+</SECTION>
+
+<SECTION>
+<FILE>clutter-wayland-surface</FILE>
+ClutterWaylandSurface
+ClutterWaylandSurfaceClass
+clutter_wayland_surface_new
+clutter_wayland_surface_attach_buffer
+clutter_wayland_surface_damage_buffer
+</SECTION>
+
+<SECTION>
 <FILE>clutter-win32</FILE>
 <TITLE>Win32 Specific Support</TITLE>
 clutter_win32_disable_event_retrieval
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index cf324ec..9ed4498 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -65,6 +65,10 @@ if X11_TESTS
 UNIT_TESTS += test-pixmap.c test-devices.c
 endif
 
+if SUPPORT_WAYLAND_COMPOSITOR
+UNIT_TESTS += test-wayland-surface.c
+endif
+
 if OS_WIN32
 SHEXT =
 else
diff --git a/tests/interactive/test-wayland-surface.c b/tests/interactive/test-wayland-surface.c
new file mode 100644
index 0000000..de5311b
--- /dev/null
+++ b/tests/interactive/test-wayland-surface.c
@@ -0,0 +1,447 @@
+#define COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <clutter/clutter.h>
+#include <clutter/wayland/clutter-wayland-compositor.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+
+#include <glib.h>
+#include <sys/time.h>
+#include <string.h>
+
+#include <wayland-server.h>
+
+typedef struct _TWSCompositor TWSCompositor;
+
+typedef struct
+{
+  struct wl_buffer *wayland_buffer;
+  GList *surfaces_attached_to;
+} TWSBuffer;
+
+typedef struct
+{
+  TWSCompositor *compositor;
+  struct wl_surface wayland_surface;
+  int x;
+  int y;
+  TWSBuffer *buffer;
+  ClutterActor *actor;
+} TWSSurface;
+
+typedef struct
+{
+  struct wl_object wayland_output;
+  int x;
+  int y;
+  int width;
+  int height;
+  /* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
+} TWSOutput;
+
+typedef struct
+{
+  GSource source;
+  GPollFD pfd;
+  struct wl_event_loop *loop;
+} WaylandEventSource;
+
+struct _TWSCompositor
+{
+  struct wl_display *wayland_display;
+  struct wl_compositor wayland_compositor;
+  struct wl_shm *wayland_shm;
+  struct wl_event_loop *wayland_loop;
+  ClutterActor *stage;
+  GList *outputs;
+  GSource *wayland_event_source;
+  GList *surfaces;
+};
+
+static guint32
+get_time (void)
+{
+  struct timeval tv;
+  gettimeofday (&tv, NULL);
+  return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static gboolean
+wayland_event_source_prepare (GSource *base, int *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+wayland_event_source_check (GSource *base)
+{
+  WaylandEventSource *source = (WaylandEventSource *)base;
+  return source->pfd.revents;
+}
+
+static gboolean
+wayland_event_source_dispatch (GSource *base,
+                               GSourceFunc callback,
+                               void *data)
+{
+  WaylandEventSource *source = (WaylandEventSource *)base;
+  wl_event_loop_dispatch (source->loop, 0);
+  return TRUE;
+}
+
+static GSourceFuncs wayland_event_source_funcs =
+{
+  wayland_event_source_prepare,
+  wayland_event_source_check,
+  wayland_event_source_dispatch,
+  NULL
+};
+
+GSource *
+wayland_event_source_new (struct wl_event_loop *loop)
+{
+  WaylandEventSource *source;
+
+  source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
+                                                sizeof (WaylandEventSource));
+  source->loop = loop;
+  source->pfd.fd = wl_event_loop_get_fd (loop);
+  source->pfd.events = G_IO_IN | G_IO_ERR;
+  g_source_add_poll (&source->source, &source->pfd);
+
+  return &source->source;
+}
+
+static TWSBuffer *
+tws_buffer_new (struct wl_buffer *wayland_buffer)
+{
+  TWSBuffer *buffer = g_slice_new (TWSBuffer);
+
+  buffer->wayland_buffer = wayland_buffer;
+  buffer->surfaces_attached_to = NULL;
+
+  return buffer;
+}
+
+static void
+tws_buffer_free (TWSBuffer *buffer)
+{
+  GList *l;
+
+  buffer->wayland_buffer->user_data = NULL;
+
+  for (l = buffer->surfaces_attached_to; l; l = l->next)
+    {
+      TWSSurface *surface = l->data;
+      surface->buffer = NULL;
+    }
+
+  g_list_free (buffer->surfaces_attached_to);
+  g_slice_free (TWSBuffer, buffer);
+}
+
+static void
+shm_buffer_created (struct wl_buffer *wayland_buffer)
+{
+  wayland_buffer->user_data = tws_buffer_new (wayland_buffer);
+}
+
+static void
+shm_buffer_damaged (struct wl_buffer *wayland_buffer,
+		    gint32 x,
+                    gint32 y,
+                    gint32 width,
+                    gint32 height)
+{
+  TWSBuffer *buffer = wayland_buffer->user_data;
+  GList *l;
+
+  for (l = buffer->surfaces_attached_to; l; l = l->next)
+    {
+      TWSSurface *surface = l->data;
+      ClutterWaylandSurface *surface_actor =
+        CLUTTER_WAYLAND_SURFACE (surface->actor);
+      clutter_wayland_surface_damage_buffer (surface_actor,
+                                             wayland_buffer,
+                                             x, y, width, height);
+    }
+}
+
+static void
+shm_buffer_destroyed (struct wl_buffer *wayland_buffer)
+{
+  if (wayland_buffer->user_data)
+    tws_buffer_free ((TWSBuffer *)wayland_buffer->user_data);
+}
+
+const static struct wl_shm_callbacks shm_callbacks = {
+  shm_buffer_created,
+  shm_buffer_damaged,
+  shm_buffer_destroyed
+};
+
+static void
+tws_surface_destroy (struct wl_client *wayland_client,
+                     struct wl_surface *wayland_surface)
+{
+  wl_resource_destroy (&wayland_surface->resource, wayland_client, get_time ());
+}
+
+static void
+tws_surface_detach_buffer (TWSSurface *surface)
+{
+  TWSBuffer *buffer = surface->buffer;
+
+  if (buffer)
+    {
+      buffer->surfaces_attached_to =
+        g_list_remove (buffer->surfaces_attached_to, surface);
+      if (buffer->surfaces_attached_to == NULL)
+        tws_buffer_free (buffer);
+      surface->buffer = NULL;
+    }
+}
+
+static void
+tws_surface_attach_buffer (struct wl_client *wayland_client,
+                           struct wl_surface *wayland_surface,
+                           struct wl_buffer *wayland_buffer,
+                           gint32 dx, gint32 dy)
+{
+  TWSBuffer *buffer = wayland_buffer->user_data;
+  TWSSurface *surface =
+    container_of (wayland_surface, TWSSurface, wayland_surface);
+  TWSCompositor *compositor = surface->compositor;
+  ClutterWaylandSurface *surface_actor;
+
+  tws_surface_detach_buffer (surface);
+
+  /* XXX: we will have been notified of shm buffers already via the
+   * callbacks, but this will be the first we know of drm buffers */
+  if (!buffer)
+    {
+      buffer = tws_buffer_new (wayland_buffer);
+      wayland_buffer->user_data = buffer;
+    }
+
+  /* wayland-drm.c: drm_create_buffer doesn't fill this in for us...*/
+  if (!wayland_buffer->compositor)
+    wayland_buffer->compositor = &compositor->wayland_compositor;
+
+  g_return_if_fail (g_list_find (buffer->surfaces_attached_to, surface) == NULL);
+
+  buffer->surfaces_attached_to = g_list_prepend (buffer->surfaces_attached_to,
+                                                 surface);
+
+  if (!surface->actor)
+    {
+      surface->actor = clutter_wayland_surface_new (wayland_surface);
+      clutter_container_add_actor (CLUTTER_CONTAINER (compositor->stage),
+                                   surface->actor);
+    }
+
+  surface_actor = CLUTTER_WAYLAND_SURFACE (surface->actor);
+  if (!clutter_wayland_surface_attach_buffer (surface_actor, wayland_buffer,
+                                              NULL))
+    g_warning ("Failed to attach buffer to ClutterWaylandSurface");
+
+  surface->buffer = buffer;
+}
+
+static void
+tws_surface_map_toplevel (struct wl_client *client,
+                          struct wl_surface *surface)
+{
+}
+
+static void
+tws_surface_map_transient (struct wl_client *client,
+                           struct wl_surface *surface,
+                           struct wl_surface *parent,
+                           gint32 dx,
+                           gint32 dy,
+                           guint32 flags)
+{
+}
+
+static void
+tws_surface_map_fullscreen (struct wl_client *client,
+                            struct wl_surface *surface)
+{
+}
+
+static void
+tws_surface_damage (struct wl_client *client,
+                    struct wl_surface *surface,
+                    gint32 x,
+                    gint32 y,
+                    gint32 width,
+                    gint32 height)
+{
+}
+
+const struct wl_surface_interface tws_surface_interface = {
+  tws_surface_destroy,
+  tws_surface_attach_buffer,
+  tws_surface_map_toplevel,
+  tws_surface_map_transient,
+  tws_surface_map_fullscreen,
+  tws_surface_damage
+};
+
+static void
+tws_surface_free (TWSSurface *surface)
+{
+  TWSCompositor *compositor = surface->compositor;
+  compositor->surfaces = g_list_remove (compositor->surfaces, surface);
+  tws_surface_detach_buffer (surface);
+
+  clutter_actor_destroy (surface->actor);
+
+  g_slice_free (TWSSurface, surface);
+}
+
+static void
+tws_surface_resource_destroy_cb (struct wl_resource *wayland_resource,
+                                 struct wl_client *wayland_client)
+{
+  TWSSurface *surface =
+    container_of (wayland_resource, TWSSurface, wayland_surface.resource);
+  tws_surface_free (surface);
+}
+
+static void
+tws_compositor_create_surface (struct wl_client *wayland_client,
+                               struct wl_compositor *wayland_compositor,
+                               guint32 wayland_id)
+{
+  TWSCompositor *compositor =
+    container_of (wayland_compositor, TWSCompositor, wayland_compositor);
+  TWSSurface *surface = g_slice_new0 (TWSSurface);
+  surface->compositor = compositor;
+
+  surface->wayland_surface.resource.destroy =
+    tws_surface_resource_destroy_cb;
+
+  surface->wayland_surface.resource.object.id = wayland_id;
+  surface->wayland_surface.resource.object.interface = &wl_surface_interface;
+  surface->wayland_surface.resource.object.implementation =
+          (void (**)(void)) &tws_surface_interface;
+  surface->wayland_surface.client = wayland_client;
+
+  wl_client_add_resource (wayland_client, &surface->wayland_surface.resource);
+
+  compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
+}
+
+const static struct wl_compositor_interface tws_compositor_interface = {
+  tws_compositor_create_surface,
+};
+
+static void
+tws_output_post_geometry (struct wl_client *wayland_client,
+                          struct wl_object *wayland_output,
+                          guint32 version)
+{
+  TWSOutput *output =
+    container_of (wayland_output, TWSOutput, wayland_output);
+
+  wl_client_post_event (wayland_client,
+                        wayland_output,
+                        WL_OUTPUT_GEOMETRY,
+                        output->x, output->y,
+                        output->width, output->height);
+}
+
+static void
+paint_finished_cb (ClutterActor *self, void *user_data)
+{
+  TWSCompositor *compositor = user_data;
+  GList *l;
+
+  for (l = compositor->surfaces; l; l = l->next)
+    {
+      TWSSurface *surface = l->data;
+      wl_display_post_frame (compositor->wayland_display,
+                             &surface->wayland_surface, get_time ());
+    }
+}
+
+static void
+tws_compositor_create_output (TWSCompositor *compositor,
+                              int x,
+                              int y,
+                              int width,
+                              int height)
+{
+  TWSOutput *output = g_slice_new0 (TWSOutput);
+
+  output->wayland_output.interface = &wl_output_interface;
+
+  wl_display_add_object (compositor->wayland_display, &output->wayland_output);
+  wl_display_add_global (compositor->wayland_display, &output->wayland_output,
+                         tws_output_post_geometry);
+
+  output->x = x;
+  output->y = y;
+  output->width = width;
+  output->height = height;
+
+  /* XXX: eventually we will support sliced stages and an output should
+   * correspond to a slice/CoglFramebuffer, but for now we only support
+   * one output so we make sure it always matches the size of the stage
+   */
+  clutter_actor_set_size (compositor->stage, width, height);
+
+  compositor->outputs = g_list_prepend (compositor->outputs, output);
+}
+
+G_MODULE_EXPORT int
+test_wayland_surface_main (int argc, char **argv)
+{
+  TWSCompositor compositor;
+  GMainLoop *loop;
+
+  memset (&compositor, 0, sizeof (compositor));
+
+  compositor.wayland_display = wl_display_create ();
+  if (compositor.wayland_display == NULL)
+    g_error ("failed to create wayland display");
+
+  if (wl_compositor_init (&compositor.wayland_compositor,
+                          &tws_compositor_interface,
+                          compositor.wayland_display) < 0)
+    g_error ("Failed to init wayland compositor");
+
+  compositor.wayland_shm = wl_shm_init (compositor.wayland_display,
+                                        &shm_callbacks);
+  if (!compositor.wayland_shm)
+    g_error ("Failed to allocate setup wayland shm callbacks");
+
+  loop = g_main_loop_new (NULL, FALSE);
+  compositor.wayland_loop =
+    wl_display_get_event_loop (compositor.wayland_display);
+  compositor.wayland_event_source =
+    wayland_event_source_new (compositor.wayland_loop);
+  g_source_attach (compositor.wayland_event_source, NULL);
+
+  clutter_wayland_set_compositor_display (compositor.wayland_display);
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return 1;
+
+  compositor.stage = clutter_stage_get_default ();
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor.stage), FALSE);
+  g_signal_connect_after (compositor.stage, "paint",
+                          G_CALLBACK (paint_finished_cb), &compositor);
+
+  tws_compositor_create_output (&compositor, 0, 0, 800, 600);
+
+  clutter_actor_show (compositor.stage);
+
+  if (wl_display_add_socket (compositor.wayland_display, "wayland-0"))
+    g_error ("Failed to create socket");
+
+  g_main_loop_run (loop);
+
+  return 0;
+}



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