[clutter/wip/actor-content: 23/33] Add ClutterDrawingArea



commit bf1eb82e875220615deebfb8ad63b304bac4d7a9
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Wed Apr 13 18:39:44 2011 +0100

    Add ClutterDrawingArea
    
    ClutterDrawingArea is a simple Content implementation that allows 2D
    drawing using Cairo.

 clutter/Makefile.am                   |    2 +
 clutter/clutter-cairo-texture.c       |   14 +-
 clutter/clutter-drawing-area.c        |  412 +++++++++++++++++++++++++++++++++
 clutter/clutter-drawing-area.h        |  104 +++++++++
 clutter/clutter-main.c                |   16 ++-
 clutter/clutter-marshal.list          |    2 +
 clutter/clutter-private.h             |    4 +
 clutter/clutter.h                     |    1 +
 tests/interactive/Makefile.am         |    3 +-
 tests/interactive/test-drawing-area.c |  107 +++++++++
 10 files changed, 650 insertions(+), 15 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 7463971..e7eab57 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -97,6 +97,7 @@ source_h =					\
 	$(srcdir)/clutter-desaturate-effect.h	\
 	$(srcdir)/clutter-device-manager.h	\
 	$(srcdir)/clutter-drag-action.h		\
+	$(srcdir)/clutter-drawing-area.h	\
 	$(srcdir)/clutter-effect.h		\
 	$(srcdir)/clutter-event.h 		\
 	$(srcdir)/clutter-feature.h 		\
@@ -184,6 +185,7 @@ source_c = \
 	$(srcdir)/clutter-desaturate-effect.c	\
 	$(srcdir)/clutter-device-manager.c	\
 	$(srcdir)/clutter-drag-action.c		\
+	$(srcdir)/clutter-drawing-area.c	\
 	$(srcdir)/clutter-effect.c		\
 	$(srcdir)/clutter-event.c 		\
 	$(srcdir)/clutter-feature.c 		\
diff --git a/clutter/clutter-cairo-texture.c b/clutter/clutter-cairo-texture.c
index 3ac096a..c5ba738 100644
--- a/clutter/clutter-cairo-texture.c
+++ b/clutter/clutter-cairo-texture.c
@@ -357,18 +357,6 @@ clutter_cairo_texture_create_surface (ClutterCairoTexture *self,
   return surface;
 }
 
-static gboolean
-create_surface_accum (GSignalInvocationHint *ihint,
-                      GValue                *return_accu,
-                      const GValue          *handler_return,
-                      gpointer               data)
-{
-  g_value_copy (handler_return, return_accu);
-
-  /* stop on the first non-NULL return value */
-  return g_value_get_boxed (handler_return) == NULL;
-}
-
 static void
 clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
 {
@@ -451,7 +439,7 @@ clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (ClutterCairoTextureClass, create_surface),
-                  create_surface_accum, NULL,
+                  _clutter_create_surface_accumulator, NULL,
                   _clutter_marshal_BOXED__UINT_UINT,
                   CAIRO_GOBJECT_TYPE_SURFACE, 2,
                   G_TYPE_UINT,
diff --git a/clutter/clutter-drawing-area.c b/clutter/clutter-drawing-area.c
new file mode 100644
index 0000000..18eb843
--- /dev/null
+++ b/clutter/clutter-drawing-area.c
@@ -0,0 +1,412 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010  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/>.
+ */
+
+/**
+ * SECTION:clutter-drawing-area
+ * @Title: #ClutterDrawingArea
+ * @Short_Desc: A content for 2D drawing
+ *
+ * #ClutterDrawingArea is an implementation of the #ClutterContent interface
+ * aimed at 2D drawing using the Cairo API.
+ *
+ * A #ClutterDrawingArea instance should be used when a #ClutterActor should
+ * display some content that has to be programmatically drawn, as opposed to
+ * text or image data.
+ *
+ * Users of #ClutterDrawingArea should connect to the #ClutterDrawingArea::draw
+ * signal. Signal handlers will be provided with the #ClutterActor currently
+ * being painted, to access the scene graph attributes like size; and a
+ * #cairo_t context, already set up to cover the allocation of the actor.
+ *
+ * #ClutterDrawingArea is available since Clutter 1.8
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+
+#include <cairo-gobject.h>
+
+#include "clutter-drawing-area.h"
+
+#include "clutter-cairo-texture.h"
+#include "clutter-debug.h"
+#include "clutter-marshal.h"
+#include "clutter-private.h"
+
+struct _ClutterDrawingAreaPrivate
+{
+  CoglMaterial *material;
+  CoglHandle texture;
+
+  cairo_t *context;
+
+  guint needs_repaint : 1;
+  guint in_repaint    : 1;
+};
+
+enum
+{
+  CREATE_SURFACE,
+  DRAW,
+
+  LAST_SIGNAL
+};
+
+static guint area_signals[LAST_SIGNAL] = { 0, };
+static ClutterContentIface *clutter_content_parent_iface = NULL;
+
+static void clutter_content_iface_init (ClutterContentIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterDrawingArea,
+                         clutter_drawing_area,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+                                                clutter_content_iface_init));
+
+static cairo_surface_t *
+clutter_drawing_area_real_create_surface (ClutterDrawingArea *area,
+                                          ClutterActor       *actor,
+                                          guint               width,
+                                          guint               height)
+{
+  return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+}
+
+static cairo_surface_t *
+clutter_drawing_area_create_surface (ClutterDrawingArea *area,
+                                     ClutterActor       *actor,
+                                     guint               width,
+                                     guint               height)
+{
+  cairo_surface_t *retval;
+
+  g_signal_emit (area, area_signals[CREATE_SURFACE], 0,
+                 actor,
+                 width,
+                 height,
+                 &retval);
+
+  return retval;
+}
+
+static gboolean
+clutter_drawing_area_setup_material (ClutterContent *content,
+                                     ClutterActor   *actor)
+{
+  ClutterDrawingArea *area = CLUTTER_DRAWING_AREA (content);
+  ClutterDrawingAreaPrivate *priv = area->priv;
+  ClutterActorBox box;
+  gint width, height;
+
+  /* avoid calling setup_material() from within draw() */
+  if (priv->in_repaint)
+    return FALSE;
+
+  clutter_actor_get_allocation_box (actor, &box);
+
+  /* round up to the nearest pixel */
+  width = (gint) ceilf (clutter_actor_box_get_width (&box));
+  height = (gint) ceilf (clutter_actor_box_get_height (&box));
+
+  if (width == 0 || height == 0)
+    return FALSE;
+
+  if (priv->texture != NULL)
+    {
+      gint tex_width, tex_height;
+
+      tex_width = cogl_texture_get_width (priv->texture);
+      tex_height = cogl_texture_get_height (priv->texture);
+
+      if (tex_width != width || tex_height != height)
+        {
+          cogl_object_unref (priv->texture);
+          priv->texture = NULL;
+        }
+    }
+
+  if (G_UNLIKELY (priv->material == NULL))
+    priv->material = cogl_material_new ();
+
+  if (priv->texture == NULL)
+    {
+      priv->texture = cogl_texture_new_with_size (width, height,
+                                                  COGL_TEXTURE_NONE,
+                                                  CLUTTER_CAIRO_FORMAT_ARGB32);
+      priv->needs_repaint = TRUE;
+    }
+
+  if (priv->needs_repaint)
+    {
+      cairo_surface_t *surface;
+      gboolean retval;
+
+      surface = clutter_drawing_area_create_surface (area, actor,
+                                                     width,
+                                                     height);
+      if (surface == NULL)
+        return FALSE;
+
+      priv->context = cairo_create (surface);
+      priv->in_repaint = TRUE;
+      priv->needs_repaint = FALSE;
+
+      /* help out Cairo discard geometry that goes outside the
+       * allocation, since our default paint volume matches the
+       * allocation
+       */
+      cairo_rectangle (priv->context, 0, 0, width, height);
+      cairo_clip (priv->context);
+
+      g_signal_emit (area, area_signals[DRAW], 0,
+                     actor,
+                     priv->context,
+                     &retval);
+
+      cairo_destroy (priv->context);
+      priv->context = NULL;
+      priv->in_repaint = FALSE;
+
+      if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
+        {
+          cogl_texture_set_region (priv->texture,
+                                   0, 0, 0, 0,
+                                   width, height,
+                                   width, height,
+                                   CLUTTER_CAIRO_FORMAT_ARGB32,
+                                   cairo_image_surface_get_stride (surface),
+                                   cairo_image_surface_get_data (surface));
+        }
+
+      cairo_surface_destroy (surface);
+    }
+
+  cogl_material_set_layer (priv->material, 0, priv->texture);
+
+  if (priv->texture != NULL)
+    {
+      guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
+      CoglColor *color = cogl_color_new ();
+
+      cogl_color_init_from_4ub (color,
+                                paint_opacity,
+                                paint_opacity,
+                                paint_opacity,
+                                paint_opacity);
+
+      cogl_material_set_color (priv->material, color);
+
+      cogl_set_source (priv->material);
+
+      cogl_color_free (color);
+    }
+
+  return TRUE;
+}
+
+static void
+clutter_drawing_area_update_geometry (ClutterContent *content,
+                                      ClutterActor   *actor)
+{
+  ClutterDrawingAreaPrivate *priv = CLUTTER_DRAWING_AREA (content)->priv;
+
+  if (priv->texture != NULL)
+    {
+      ClutterActorBox box;
+      gfloat width, height;
+
+      clutter_actor_get_allocation_box (actor, &box);
+      clutter_actor_box_get_size (&box, &width, &height);
+
+      cogl_rectangle_with_texture_coords (0, 0, width, height,
+                                          0.0f, 0.0f,
+                                          1.0f, 1.0f);
+    }
+}
+
+static void
+clutter_drawing_area_invalidate (ClutterContent *content)
+{
+  /* invalidate() should recreate the contents */
+  CLUTTER_DRAWING_AREA (content)->priv->needs_repaint = TRUE;
+
+  /* chain up for the default implementation */
+  clutter_content_parent_iface->invalidate (content);
+}
+
+static void
+clutter_drawing_area_finalize (GObject *gobject)
+{
+  ClutterDrawingAreaPrivate *priv = CLUTTER_DRAWING_AREA (gobject)->priv;
+
+  if (priv->texture)
+    cogl_object_unref (priv->texture);
+
+  if (priv->material)
+    cogl_object_unref (priv->material);
+
+  G_OBJECT_CLASS (clutter_drawing_area_parent_class)->finalize (gobject);
+}
+
+static void
+clutter_content_iface_init (ClutterContentIface *iface)
+{
+  /* store a pointer to the default implementation, for chaining up */
+  clutter_content_parent_iface =
+    g_type_default_interface_peek (CLUTTER_TYPE_CONTENT);
+
+  iface->setup_material = clutter_drawing_area_setup_material;
+  iface->update_geometry = clutter_drawing_area_update_geometry;
+  iface->invalidate = clutter_drawing_area_invalidate;
+}
+
+/* note: this won't save us from unbalanced save/restore
+ * pairs in the handlers; but, then again, nothing will.
+ */
+static void
+clutter_safe_draw_marshaller (GClosure     *closure,
+                              GValue       *return_value,
+                              guint         n_param_values,
+                              const GValue *param_values,
+                              gpointer      invocation_hint,
+                              gpointer      marshal_data)
+{
+  cairo_t *cr = g_value_get_boxed (&param_values[2]);
+
+  cairo_save (cr);
+
+  _clutter_marshal_BOOLEAN__OBJECT_BOXED (closure,
+                                          return_value,
+                                          n_param_values,
+                                          param_values,
+                                          invocation_hint,
+                                          marshal_data);
+
+  cairo_restore (cr);
+}
+
+static void
+clutter_drawing_area_class_init (ClutterDrawingAreaClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ClutterDrawingAreaPrivate));
+
+  gobject_class->finalize = clutter_drawing_area_finalize;
+
+  /**
+   * ClutterDrawingArea::draw:
+   * @area: the #ClutterDrawingArea that emitted the signal
+   * @actor: the #ClutterActor currently being painted
+   * @cr: the #cairo_t for the drawing operation
+   *
+   * The ::draw signal is emitted each time the @area should be
+   * painted.
+   *
+   * The passed Cairo context is owned by the #ClutterDrawingArea
+   * instance and should not be destroyed.
+   *
+   * The ::draw signal marshaller will take care of saving and
+   * restoring the context for each handler, so it is not necessary
+   * to call cairo_save() and cairo_restore() manually around the
+   * drawing code.
+   *
+   * Return value: %FALSE if the signal emission should continue,
+   *   or %TRUE if the signal emission should stop
+   *
+   * Since: 1.8
+   */
+  area_signals[DRAW] =
+    g_signal_new (I_("draw"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (ClutterDrawingAreaClass, draw),
+                  _clutter_boolean_handled_accumulator, NULL,
+                  clutter_safe_draw_marshaller,
+                  G_TYPE_BOOLEAN, 2,
+                  CLUTTER_TYPE_ACTOR,
+                  CAIRO_GOBJECT_TYPE_CONTEXT);
+
+  /**
+   * ClutterDrawingArea:create-surface:
+   * @area: the #ClutterDrawingArea that emitted the signal
+   * @actor: the #ClutterActor currently being painted
+   * @width: the width of the surface
+   * @height: the height of the surface
+   *
+   * The ::create-surface signal is emitted each the @area needs
+   * to create a Cairo surface.
+   *
+   * The first signal handler returning a non-%NULL, valid
+   * #cairo_surface_t will stop the signal emission.
+   *
+   * Return value: the newly created #cairo_surface_t
+   *
+   * Since: 1.8
+   */
+  area_signals[CREATE_SURFACE] =
+    g_signal_new (I_("create-surface"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (ClutterDrawingAreaClass, create_surface),
+                  _clutter_create_surface_accumulator, NULL,
+                  _clutter_marshal_BOXED__OBJECT_UINT_UINT,
+                  CAIRO_GOBJECT_TYPE_SURFACE, 3,
+                  CLUTTER_TYPE_ACTOR,
+                  G_TYPE_UINT,
+                  G_TYPE_UINT);
+
+  klass->create_surface = clutter_drawing_area_real_create_surface;
+}
+
+static void
+clutter_drawing_area_init (ClutterDrawingArea *area)
+{
+  area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, CLUTTER_TYPE_DRAWING_AREA,
+                                            ClutterDrawingAreaPrivate);
+
+  area->priv->needs_repaint = TRUE;
+}
+
+/**
+ * clutter_drawing_area_new:
+ *
+ * Creates a new #ClutterDrawingArea instance.
+ *
+ * Drawing on the returned #ClutterDrawingArea can be performed by
+ * connecting to the #ClutterDrawingArea::draw signal.
+ *
+ * The returned #ClutterDrawingArea can be assigned to any #ClutterActor
+ * using clutter_actor_set_content().
+ *
+ * Return value: (transfer full): the newly created #ClutterDrawingArea.
+ *   Use g_object_unref() when done.
+ *
+ * Since: 1.8
+ */
+ClutterContent *
+clutter_drawing_area_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_DRAWING_AREA, NULL);
+}
diff --git a/clutter/clutter-drawing-area.h b/clutter/clutter-drawing-area.h
new file mode 100644
index 0000000..2a6b64a
--- /dev/null
+++ b/clutter/clutter-drawing-area.h
@@ -0,0 +1,104 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010  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/>.
+ */
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_DRAWING_AREA_H__
+#define __CLUTTER_DRAWING_AREA_H__
+
+#include <cairo.h>
+#include <clutter/clutter-actor.h>
+#include <clutter/clutter-content.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_DRAWING_AREA               (clutter_drawing_area_get_type ())
+#define CLUTTER_DRAWING_AREA(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DRAWING_AREA, ClutterDrawingArea))
+#define CLUTTER_IS_DRAWING_AREA(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DRAWING_AREA))
+#define CLUTTER_DRAWING_AREA_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DRAWING_AREA, ClutterDrawingAreaClass))
+#define CLUTTER_IS_DRAWING_AREA_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DRAWING_AREA))
+#define CLUTTER_DRAWING_AREA_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DRAWING_AREA, ClutterDrawingAreaClass))
+
+typedef struct _ClutterDrawingArea              ClutterDrawingArea;
+typedef struct _ClutterDrawingAreaPrivate       ClutterDrawingAreaPrivate;
+typedef struct _ClutterDrawingAreaClass         ClutterDrawingAreaClass;
+
+/**
+ * ClutterDrawingArea:
+ *
+ * The <structname>ClutterDrawingArea</structname> structure contains only
+ * private data and should be accessed using the provided API.
+ *
+ * Since: 1.8
+ */
+struct _ClutterDrawingArea
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  ClutterDrawingAreaPrivate *priv;
+};
+
+/**
+ * ClutterDrawingAreaClass:
+ * @create_surface: class handler for the #ClutterDrawingArea::create_surface
+ *   signal
+ * @draw: class handler for the #ClutterDrawingArea::draw signal
+ *
+ * The <structname>ClutterDrawingAreaClass</structname> structure contains
+ * only private data.
+ *
+ * Since: 1.8
+ */
+struct _ClutterDrawingAreaClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+  cairo_surface_t *(* create_surface) (ClutterDrawingArea *area,
+                                       ClutterActor       *actor,
+                                       guint               width,
+                                       guint               height);
+
+  gboolean         (* draw)           (ClutterDrawingArea *area,
+                                       ClutterActor       *actor,
+                                       cairo_t            *cr);
+
+  /*< private >*/
+  void (*_clutter_drawing_area_0) (void);
+  void (*_clutter_drawing_area_1) (void);
+  void (*_clutter_drawing_area_2) (void);
+  void (*_clutter_drawing_area_3) (void);
+  void (*_clutter_drawing_area_4) (void);
+  void (*_clutter_drawing_area_5) (void);
+  void (*_clutter_drawing_area_6) (void);
+  void (*_clutter_drawing_area_7) (void);
+};
+
+GType clutter_drawing_area_get_type (void) G_GNUC_CONST;
+
+ClutterContent *clutter_drawing_area_new (void);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_DRAWING_AREA_H__ */
diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c
index 4193b1d..ff7f4fe 100644
--- a/clutter/clutter-main.c
+++ b/clutter/clutter-main.c
@@ -91,9 +91,11 @@
 #endif
 
 #include <stdlib.h>
+#include <locale.h>
+
 #include <glib/gi18n-lib.h>
+
 #include <gio/gio.h>
-#include <locale.h>
 
 #include "clutter-actor.h"
 #include "clutter-backend-private.h"
@@ -1775,6 +1777,18 @@ _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
   return continue_emission;
 }
 
+gboolean
+_clutter_create_surface_accumulator (GSignalInvocationHint *ihint,
+                                     GValue                *return_accu,
+                                     const GValue          *handler_return,
+                                     gpointer               data)
+{
+  g_value_copy (handler_return, return_accu);
+
+  /* stop on the first non-NULL return value */
+  return g_value_get_boxed (handler_return) == NULL;
+}
+
 static void
 event_click_count_generate (ClutterEvent *event)
 {
diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list
index 49745ef..aa5c041 100644
--- a/clutter/clutter-marshal.list
+++ b/clutter/clutter-marshal.list
@@ -1,5 +1,7 @@
 BOOLEAN:BOXED
+BOOLEAN:OBJECT,BOXED
 BOOLEAN:STRING,UINT,FLAGS
+BOXED:OBJECT,UINT,UINT
 BOXED:UINT,UINT
 DOUBLE:VOID
 UINT:VOID
diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h
index 92c3cdd..fd8a187 100644
--- a/clutter/clutter-private.h
+++ b/clutter/clutter-private.h
@@ -208,6 +208,10 @@ gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
                                                GValue                *return_accu,
                                                const GValue          *handler_return,
                                                gpointer               dummy);
+gboolean _clutter_create_surface_accumulator (GSignalInvocationHint *ihint,
+                                              GValue                *return_accu,
+                                              const GValue          *handler_return,
+                                              gpointer               dummy);
 
 void _clutter_run_repaint_functions (void);
 
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 5029be4..4140ec3 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -69,6 +69,7 @@
 #include "clutter-desaturate-effect.h"
 #include "clutter-device-manager.h"
 #include "clutter-drag-action.h"
+#include "clutter-drawing-area.h"
 #include "clutter-effect.h"
 #include "clutter-event.h"
 #include "clutter-feature.h"
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index b7d7598..a0dbb89 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -58,7 +58,8 @@ UNIT_TESTS = \
 	test-snap-constraint.c \
 	test-rgba-content.c \
 	test-image-content.c \
-	test-border-image.c
+	test-border-image.c \
+	test-drawing-area.c
 
 if X11_TESTS
 UNIT_TESTS += test-pixmap.c test-devices.c
diff --git a/tests/interactive/test-drawing-area.c b/tests/interactive/test-drawing-area.c
new file mode 100644
index 0000000..5d4e12f
--- /dev/null
+++ b/tests/interactive/test-drawing-area.c
@@ -0,0 +1,107 @@
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#include <cairo.h>
+#include <clutter/clutter.h>
+
+#define PADDING         18.0f
+
+static gboolean
+on_area_draw (ClutterDrawingArea *area,
+              ClutterActor       *actor,
+              cairo_t            *cr)
+{
+  GDateTime *now = g_date_time_new_now_local ();
+  double hours = g_date_time_get_hour (now) * G_PI / 6.0;
+  double minutes = g_date_time_get_minute (now) * G_PI / 30;
+  double seconds = g_date_time_get_seconds (now) * G_PI / 30;
+  float width = clutter_actor_get_width (actor);
+  float height = clutter_actor_get_height (actor);
+
+  g_print ("Time: %02d:%02d:%02.1f\r",
+           g_date_time_get_hour (now),
+           g_date_time_get_minute (now),
+           g_date_time_get_seconds (now));
+
+  g_date_time_unref (now);
+
+  /* clear the current contents */
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  cairo_paint (cr);
+
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+  /* who doesn't want all those new line settings? :-) */
+  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+  cairo_set_line_width (cr, 0.1);
+
+  /* normalize and translate to the center of the rendering context */
+  cairo_scale (cr, width, height);
+  cairo_translate (cr, 0.5, 0.5);
+
+  /* clock outline */
+  cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
+  cairo_arc (cr, 0, 0, 0.4, 0, G_PI * 2);
+  cairo_stroke (cr);
+
+  /* draw a white dot on the current second */
+  cairo_set_source_rgba (cr, 1, 1, 1, 0.6);
+  cairo_arc (cr, sin (seconds) * 0.4, -cos (seconds) * 0.4, 0.05, 0, G_PI * 2);
+  cairo_fill (cr);
+
+  /* minutes hand */
+  cairo_set_source_rgba (cr, 0.2, 0.2, 1, 0.6);
+  cairo_move_to (cr, 0, 0);
+  cairo_line_to (cr, sin (minutes) * 0.4, -cos (minutes) * 0.4);
+  cairo_stroke (cr);
+
+  /* hours hand */
+  cairo_move_to (cr, 0, 0);
+  cairo_line_to (cr, sin (hours) * 0.2, -cos (hours) * 0.2);
+  cairo_stroke (cr);
+
+  return TRUE;
+}
+
+static gboolean
+on_timeout (gpointer data)
+{
+  ClutterContent *content = data;
+
+  /* invalidate the content on timeout */
+  clutter_content_invalidate (content);
+
+  return TRUE;
+}
+
+G_MODULE_EXPORT int
+test_drawing_area_main (int argc, char *argv[])
+{
+  ClutterActor *stage, *actor;
+  ClutterContent *area;
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  stage = clutter_stage_new ();
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Drawing");
+  clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Aluminium5);
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+  clutter_actor_show (stage);
+
+  area = clutter_drawing_area_new ();
+  g_signal_connect (area, "draw", G_CALLBACK (on_area_draw), NULL);
+
+  actor = clutter_actor_new ();
+  clutter_actor_set_content (actor, area);
+  clutter_actor_add_constraint (actor, clutter_bind_constraint_new (stage, CLUTTER_BIND_POSITION, PADDING));
+  clutter_actor_add_constraint (actor, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, -2.0 * PADDING));
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+
+  clutter_threads_add_timeout (1000, on_timeout, area);
+
+  clutter_main ();
+
+  return EXIT_SUCCESS;
+}



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