[clutter] tests: Adds a simple wayland compositor example



commit e5bde0b0743f718904d53731ea30d414ad915f4c
Author: Robert Bragg <robert linux intel com>
Date:   Mon Dec 5 14:05:57 2011 +0000

    tests: Adds a simple wayland compositor example
    
    This adds an extremely minimal wayland compositor to tests/interactive
    to test the ClutterWaylandSurface actor. Currently this minimal
    compositor doesn't support any input, it simply paints client surfaces
    fixed at the top-left of the stage.
    
    Reviewed-by: Emmanuele Bassi <ebassi linux intel com>

 tests/interactive/Makefile.am            |    4 +
 tests/interactive/test-wayland-surface.c |  567 ++++++++++++++++++++++++++++++
 2 files changed, 571 insertions(+), 0 deletions(-)
---
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 0139ee1..357b6bd 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -64,6 +64,10 @@ if X11_TESTS
 UNIT_TESTS += test-pixmap.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..2627412
--- /dev/null
+++ b/tests/interactive/test-wayland-surface.c
@@ -0,0 +1,567 @@
+#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
+{
+  guint32 flags;
+  int width;
+  int height;
+  int refresh;
+} TWSMode;
+
+typedef struct
+{
+  struct wl_object wayland_output;
+  int x;
+  int y;
+  int width_mm;
+  int height_mm;
+  /* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
+
+  GList *modes;
+} TWSOutput;
+
+typedef struct
+{
+  GSource source;
+  GPollFD pfd;
+  struct wl_event_loop *loop;
+} WaylandEventSource;
+
+typedef struct
+{
+  struct wl_resource resource;
+} TWSFrameCallback;
+
+struct _TWSCompositor
+{
+  struct wl_display *wayland_display;
+  struct wl_shm *wayland_shm;
+  struct wl_event_loop *wayland_loop;
+  ClutterActor *stage;
+  GList *outputs;
+  GSource *wayland_event_source;
+  GList *surfaces;
+  GArray *frame_callbacks;
+};
+
+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_resource *wayland_resource)
+{
+  wl_resource_destroy (wayland_resource, 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_resource *wayland_surface_resource,
+                           struct wl_resource *wayland_buffer_resource,
+                           gint32 dx, gint32 dy)
+{
+  struct wl_buffer *wayland_buffer = wayland_buffer_resource->data;
+  TWSBuffer *buffer = wayland_buffer->user_data;
+  TWSSurface *surface = wayland_surface_resource->data;
+  TWSCompositor *compositor = surface->compositor;
+  ClutterWaylandSurface *surface_actor;
+
+  /* XXX: in the case where we are reattaching the same buffer we can
+   * simply bail out. Note this is important because if we don't bail
+   * out then the _detach_buffer will actually end up destroying the
+   * buffer we're trying to attach. */
+  if (buffer && surface->buffer == buffer)
+    return;
+
+  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;
+    }
+
+  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 (&surface->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_damage (struct wl_client *client,
+                    struct wl_resource *resource,
+                    gint32 x,
+                    gint32 y,
+                    gint32 width,
+                    gint32 height)
+{
+}
+
+static void
+destroy_frame_callback (struct wl_resource *callback_resource)
+{
+  TWSFrameCallback *callback = callback_resource->data;
+
+  g_slice_free (TWSFrameCallback, callback);
+}
+
+static void
+tws_surface_frame (struct wl_client *client,
+                   struct wl_resource *surface_resource,
+                   guint32 callback_id)
+{
+  TWSFrameCallback *callback;
+  TWSSurface *surface = surface_resource->data;
+
+  callback = g_slice_new0 (TWSFrameCallback);
+  callback->resource.object.interface = &wl_callback_interface;
+  callback->resource.object.id = callback_id;
+  callback->resource.destroy = destroy_frame_callback;
+  callback->resource.data = callback;
+
+  wl_client_add_resource (client, &callback->resource);
+
+  g_array_append_val (surface->compositor->frame_callbacks, callback);
+}
+
+const struct wl_surface_interface tws_surface_interface = {
+  tws_surface_destroy,
+  tws_surface_attach_buffer,
+  tws_surface_damage,
+  tws_surface_frame
+};
+
+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_surface_resource)
+{
+  TWSSurface *surface = wayland_surface_resource->data;
+  tws_surface_free (surface);
+}
+
+static void
+tws_compositor_create_surface (struct wl_client *wayland_client,
+                               struct wl_resource *wayland_compositor_resource,
+                               guint32 id)
+{
+  TWSCompositor *compositor = wayland_compositor_resource->data;
+  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 = id;
+  surface->wayland_surface.resource.object.interface = &wl_surface_interface;
+  surface->wayland_surface.resource.object.implementation =
+          (void (**)(void)) &tws_surface_interface;
+  surface->wayland_surface.resource.data = surface;
+
+  wl_client_add_resource (wayland_client, &surface->wayland_surface.resource);
+
+  compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
+}
+
+static void
+bind_output (struct wl_client *client,
+             void *data,
+             guint32 version,
+             guint32 id)
+{
+  TWSOutput *output = data;
+  struct wl_resource *resource =
+    wl_client_add_object (client, &wl_output_interface, NULL, id, data);
+  GList *l;
+
+  wl_resource_post_event (resource,
+                          WL_OUTPUT_GEOMETRY,
+                          output->x, output->y,
+                          output->width_mm,
+                          output->height_mm,
+                          0, /* subpixel: unknown */
+                          "unknown", /* make */
+                          "unknown"); /* model */
+
+  for (l = output->modes; l; l = l->next)
+    {
+      TWSMode *mode = l->data;
+      wl_resource_post_event (resource,
+                              WL_OUTPUT_MODE,
+                              mode->flags,
+                              mode->width,
+                              mode->height,
+                              mode->refresh);
+    }
+}
+
+static void
+tws_compositor_create_output (TWSCompositor *compositor,
+                              int x,
+                              int y,
+                              int width_mm,
+                              int height_mm)
+{
+  TWSOutput *output = g_slice_new0 (TWSOutput);
+
+  output->wayland_output.interface = &wl_output_interface;
+
+  output->x = x;
+  output->y = y;
+  output->width_mm = width_mm;
+  output->height_mm = height_mm;
+
+  wl_display_add_global (compositor->wayland_display,
+                         &wl_output_interface,
+                         output,
+                         bind_output);
+
+  /* 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_mm, height_mm);
+
+  compositor->outputs = g_list_prepend (compositor->outputs, output);
+}
+
+const static struct wl_compositor_interface tws_compositor_interface = {
+  tws_compositor_create_surface,
+};
+
+static void
+paint_finished_cb (ClutterActor *self, void *user_data)
+{
+  TWSCompositor *compositor = user_data;
+  int i;
+
+  for (i = 0; i < compositor->frame_callbacks->len; i++)
+    {
+      TWSFrameCallback *callback =
+        g_array_index (compositor->frame_callbacks, TWSFrameCallback *, i);
+
+      wl_resource_post_event (&callback->resource,
+                              WL_CALLBACK_DONE, get_time ());
+      wl_resource_destroy (&callback->resource, 0);
+    }
+  g_array_set_size (compositor->frame_callbacks, 0);
+}
+
+static void
+compositor_bind (struct wl_client *client,
+		 void *data,
+                 guint32 version,
+                 guint32 id)
+{
+  TWSCompositor *compositor = data;
+
+  wl_client_add_object (client, &wl_compositor_interface,
+                        &tws_compositor_interface, id, compositor);
+}
+
+static void
+shell_move(struct wl_client *client,
+           struct wl_resource *resource,
+           struct wl_resource *surface_resource,
+           struct wl_resource *input_resource,
+           guint32 time)
+{
+}
+
+static void
+shell_resize (struct wl_client *client,
+              struct wl_resource *resource,
+              struct wl_resource *surface_resource,
+              struct wl_resource *input_resource,
+              guint32 time,
+              guint32 edges)
+{
+}
+
+static void
+shell_set_toplevel (struct wl_client *client,
+		    struct wl_resource *resource,
+		    struct wl_resource *surface_resource)
+{
+}
+
+static void
+shell_set_transient (struct wl_client *client,
+		     struct wl_resource *resource,
+		     struct wl_resource *surface_resource,
+		     struct wl_resource *parent_resource,
+		     int x, int y, uint32_t flags)
+{
+}
+
+static void
+shell_set_fullscreen (struct wl_client *client,
+		      struct wl_resource *resource,
+		      struct wl_resource *surface_resource)
+{
+}
+
+static const struct wl_shell_interface tws_shell_interface =
+{
+  shell_move,
+  shell_resize,
+  shell_set_toplevel,
+  shell_set_transient,
+  shell_set_fullscreen
+};
+
+static void
+bind_shell (struct wl_client *client,
+            void *data,
+            guint32 version,
+            guint32 id)
+{
+  wl_client_add_object (client, &wl_shell_interface,
+                        &tws_shell_interface, id, data);
+}
+
+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");
+
+  compositor.frame_callbacks = g_array_new (FALSE, FALSE, sizeof (void *));
+
+  if (!wl_display_add_global (compositor.wayland_display,
+                              &wl_compositor_interface,
+			      &compositor,
+                              compositor_bind))
+    g_error ("Failed to register wayland compositor object");
+
+  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);
+
+  if (wl_display_add_global (compositor.wayland_display, &wl_shell_interface,
+                             &compositor, bind_shell) == NULL)
+    g_error ("Failed to register a global shell object");
+
+  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]