clutter: Move ClutterCanvas to gnome-shell

Since StDrawingArea in gnome-shell is the only user of ClutterCanvas,
it is possible to move ClutterCanvas completely out of Mutter to
gnome-shell. This allows to remove another Cairo dependency from
Mutter.

This patch removes ClutterCanvas code from Mutter.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3470>
This commit is contained in:
Shmuel Melamud 2023-12-21 06:41:34 +02:00 committed by Bilal Elmoussaoui
parent 41a7e8e3e0
commit 237e505cc7
9 changed files with 0 additions and 1542 deletions

View File

@ -1,624 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2012 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/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* ClutterCanvas:
*
* Content for 2D painting
*
* The #ClutterCanvas class is a [iface@Clutter.Content] implementation that allows
* drawing using the Cairo API on a 2D surface.
*
* In order to draw on a #ClutterCanvas, you should connect a handler to the
* [signal@Clutter.Canvas::draw] signal; the signal will receive a #cairo_t context
* that can be used to draw. #ClutterCanvas will emit the [signal@Clutter.Canvas::draw]
* signal when invalidated using [method@Clutter.Content.invalidate].
*/
#include "config.h"
#include <math.h>
#include <cairo-gobject.h>
#include "cogl/cogl.h"
#include "clutter/clutter-canvas.h"
#include "clutter/clutter-actor-private.h"
#include "clutter/clutter-backend.h"
#include "clutter/clutter-color.h"
#include "clutter/clutter-content-private.h"
#include "clutter/clutter-debug.h"
#include "clutter/clutter-marshal.h"
#include "clutter/clutter-paint-node.h"
#include "clutter/clutter-paint-nodes.h"
#include "clutter/clutter-private.h"
#include "clutter/clutter-settings.h"
typedef struct _ClutterCanvasPrivate
{
cairo_t *cr;
int width;
int height;
float scale_factor;
CoglTexture *texture;
gboolean dirty;
CoglBitmap *buffer;
} ClutterCanvasPrivate;
enum
{
PROP_0,
PROP_WIDTH,
PROP_HEIGHT,
PROP_SCALE_FACTOR,
LAST_PROP
};
static GParamSpec *obj_props[LAST_PROP] = { NULL, };
enum
{
DRAW,
LAST_SIGNAL
};
static guint canvas_signals[LAST_SIGNAL] = { 0, };
static void clutter_content_iface_init (ClutterContentInterface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterCanvas, clutter_canvas, G_TYPE_OBJECT,
G_ADD_PRIVATE (ClutterCanvas)
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
clutter_content_iface_init))
static void
clutter_cairo_context_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[1]);
cairo_save (cr);
_clutter_marshal_BOOLEAN__BOXED_INT_INT (closure,
return_value,
n_param_values,
param_values,
invocation_hint,
marshal_data);
cairo_restore (cr);
}
static void
clutter_canvas_finalize (GObject *gobject)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (CLUTTER_CANVAS (gobject));
g_clear_object (&priv->buffer);
g_clear_object (&priv->texture);
G_OBJECT_CLASS (clutter_canvas_parent_class)->finalize (gobject);
}
static void
clutter_canvas_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (CLUTTER_CANVAS (gobject));
switch (prop_id)
{
case PROP_WIDTH:
{
gint new_size = g_value_get_int (value);
if (priv->width != new_size)
{
priv->width = new_size;
clutter_content_invalidate (CLUTTER_CONTENT (gobject));
}
}
break;
case PROP_HEIGHT:
{
gint new_size = g_value_get_int (value);
if (priv->height != new_size)
{
priv->height = new_size;
clutter_content_invalidate (CLUTTER_CONTENT (gobject));
}
}
break;
case PROP_SCALE_FACTOR:
{
gfloat new_scale_factor = g_value_get_float (value);
if (priv->scale_factor != new_scale_factor)
{
priv->scale_factor = new_scale_factor;
clutter_content_invalidate (CLUTTER_CONTENT (gobject));
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_canvas_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (CLUTTER_CANVAS (gobject));
switch (prop_id)
{
case PROP_WIDTH:
g_value_set_int (value, priv->width);
break;
case PROP_HEIGHT:
g_value_set_int (value, priv->height);
break;
case PROP_SCALE_FACTOR:
g_value_set_float (value, priv->scale_factor);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_canvas_class_init (ClutterCanvasClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
/**
* ClutterCanvas:width:
*
* The width of the canvas.
*/
obj_props[PROP_WIDTH] =
g_param_spec_int ("width", NULL, NULL,
-1, G_MAXINT,
-1,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* ClutterCanvas:height:
*
* The height of the canvas.
*/
obj_props[PROP_HEIGHT] =
g_param_spec_int ("height", NULL, NULL,
-1, G_MAXINT,
-1,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* ClutterCanvas:scale-factor:
*
* The height of the canvas.
*/
obj_props[PROP_SCALE_FACTOR] =
g_param_spec_float ("scale-factor", NULL, NULL,
0.01f, G_MAXFLOAT,
1.0f,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* ClutterCanvas::draw:
* @canvas: the #ClutterCanvas that emitted the signal
* @cr: the Cairo context used to draw
* @width: the width of the @canvas
* @height: the height of the @canvas
*
* The #ClutterCanvas::draw signal is emitted each time a canvas is
* invalidated.
*
* It is safe to connect multiple handlers to this signal: each
* handler invocation will be automatically protected by cairo_save()
* and cairo_restore() pairs.
*
* Return value: %TRUE if the signal emission should stop, and
* %FALSE otherwise
*/
canvas_signals[DRAW] =
g_signal_new (I_("draw"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
G_STRUCT_OFFSET (ClutterCanvasClass, draw),
_clutter_boolean_handled_accumulator, NULL,
clutter_cairo_context_draw_marshaller,
G_TYPE_BOOLEAN, 3,
CAIRO_GOBJECT_TYPE_CONTEXT,
G_TYPE_INT,
G_TYPE_INT);
gobject_class->set_property = clutter_canvas_set_property;
gobject_class->get_property = clutter_canvas_get_property;
gobject_class->finalize = clutter_canvas_finalize;
g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
}
static void
clutter_canvas_init (ClutterCanvas *self)
{
ClutterCanvasPrivate *priv = clutter_canvas_get_instance_private (self);
priv->width = -1;
priv->height = -1;
priv->scale_factor = 1.0f;
}
static void
clutter_canvas_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *root,
ClutterPaintContext *paint_context)
{
ClutterCanvas *self = CLUTTER_CANVAS (content);
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (self);
ClutterPaintNode *node;
if (priv->buffer == NULL)
return;
if (priv->dirty)
g_clear_object (&priv->texture);
if (priv->texture == NULL)
priv->texture = cogl_texture_2d_new_from_bitmap (priv->buffer);
if (priv->texture == NULL)
return;
node = clutter_actor_create_texture_paint_node (actor, priv->texture);
clutter_paint_node_set_static_name (node, "Canvas Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
priv->dirty = FALSE;
}
static void
clutter_canvas_emit_draw (ClutterCanvas *self)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (self);
int real_width, real_height;
cairo_surface_t *surface;
gboolean mapped_buffer;
unsigned char *data;
CoglBuffer *buffer;
gboolean res;
cairo_t *cr;
g_assert (priv->height > 0 && priv->width > 0);
priv->dirty = TRUE;
real_width = ceilf (priv->width * priv->scale_factor);
real_height = ceilf (priv->height * priv->scale_factor);
CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d",
priv->width, priv->height);
if (priv->buffer == NULL)
{
CoglContext *ctx;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
priv->buffer = cogl_bitmap_new_with_size (ctx,
real_width,
real_height,
COGL_PIXEL_FORMAT_CAIRO_ARGB32_COMPAT);
}
buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer));
if (buffer == NULL)
return;
cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
data = cogl_buffer_map (buffer,
COGL_BUFFER_ACCESS_READ_WRITE,
COGL_BUFFER_MAP_HINT_DISCARD);
if (data != NULL)
{
int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
real_width,
real_height,
bitmap_stride);
mapped_buffer = TRUE;
}
else
{
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
real_width,
real_height);
mapped_buffer = FALSE;
}
cairo_surface_set_device_scale (surface,
priv->scale_factor,
priv->scale_factor);
priv->cr = cr = cairo_create (surface);
g_signal_emit (self, canvas_signals[DRAW], 0,
cr, priv->width, priv->height,
&res);
#ifdef CLUTTER_ENABLE_DEBUG
if (_clutter_diagnostic_enabled () && cairo_status (cr))
{
g_warning ("Drawing failed for <ClutterCanvas>[%p]: %s",
self,
cairo_status_to_string (cairo_status (cr)));
}
#endif
priv->cr = NULL;
cairo_destroy (cr);
if (mapped_buffer)
cogl_buffer_unmap (buffer);
else
{
int size = cairo_image_surface_get_stride (surface) * priv->height;
cogl_buffer_set_data (buffer,
0,
cairo_image_surface_get_data (surface),
size);
}
cairo_surface_destroy (surface);
}
static void
clutter_canvas_invalidate (ClutterContent *content)
{
ClutterCanvas *self = CLUTTER_CANVAS (content);
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (self);
g_clear_object (&priv->buffer);
if (priv->width <= 0 || priv->height <= 0)
return;
clutter_canvas_emit_draw (self);
}
static gboolean
clutter_canvas_get_preferred_size (ClutterContent *content,
gfloat *width,
gfloat *height)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (CLUTTER_CANVAS (content));
if (priv->width < 0 || priv->height < 0)
return FALSE;
if (width != NULL)
*width = ceilf (priv->width * priv->scale_factor);
if (height != NULL)
*height = ceilf (priv->height * priv->scale_factor);
return TRUE;
}
static void
clutter_content_iface_init (ClutterContentInterface *iface)
{
iface->invalidate = clutter_canvas_invalidate;
iface->paint_content = clutter_canvas_paint_content;
iface->get_preferred_size = clutter_canvas_get_preferred_size;
}
/**
* clutter_canvas_new:
*
* Creates a new instance of #ClutterCanvas.
*
* You should call [method@Clutter.Canvas.set_size] to set the size of the canvas.
*
* You should call [method@Clutter.Content.invalidate] every time you wish to
* draw the contents of the canvas.
*
* Return value: (transfer full): The newly allocated instance of
* #ClutterCanvas. Use g_object_unref() when done.
*/
ClutterContent *
clutter_canvas_new (void)
{
return g_object_new (CLUTTER_TYPE_CANVAS, NULL);
}
static gboolean
clutter_canvas_invalidate_internal (ClutterCanvas *canvas,
int width,
int height)
{
ClutterCanvasPrivate *priv =
clutter_canvas_get_instance_private (canvas);
gboolean width_changed = FALSE, height_changed = FALSE;
gboolean res = FALSE;
GObject *obj;
obj = G_OBJECT (canvas);
g_object_freeze_notify (obj);
if (priv->width != width)
{
priv->width = width;
width_changed = TRUE;
g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
}
if (priv->height != height)
{
priv->height = height;
height_changed = TRUE;
g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
}
if (width_changed || height_changed)
{
clutter_content_invalidate (CLUTTER_CONTENT (canvas));
res = TRUE;
}
g_object_thaw_notify (obj);
return res;
}
/**
* clutter_canvas_set_size:
* @canvas: a #ClutterCanvas
* @width: the width of the canvas, in pixels
* @height: the height of the canvas, in pixels
*
* Sets the size of the @canvas, and invalidates the content.
*
* This function will cause the @canvas to be invalidated only
* if the size of the canvas surface has changed.
*
* If you want to invalidate the contents of the @canvas when setting
* the size, you can use the return value of the function to conditionally
* call [method@Clutter.Content.invalidate]:
*
* ```c
* if (!clutter_canvas_set_size (canvas, width, height))
* clutter_content_invalidate (CLUTTER_CONTENT (canvas));
* ```
*
* Return value: this function returns %TRUE if the size change
* caused a content invalidation, and %FALSE otherwise
*/
gboolean
clutter_canvas_set_size (ClutterCanvas *canvas,
int width,
int height)
{
g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), FALSE);
g_return_val_if_fail (width >= -1 && height >= -1, FALSE);
return clutter_canvas_invalidate_internal (canvas, width, height);
}
/**
* clutter_canvas_set_scale_factor:
* @canvas: a #ClutterCanvas
* @scale: the integer scaling factor of the canvas
*
* Sets the scaling factor of the @canvas, and invalidates the content.
*
* This function will cause the @canvas to be invalidated only
* if the scale factor of the canvas surface has changed.
*/
void
clutter_canvas_set_scale_factor (ClutterCanvas *canvas,
float scale)
{
ClutterCanvasPrivate *priv;
g_return_if_fail (CLUTTER_IS_CANVAS (canvas));
g_return_if_fail (scale > 0.0f);
priv = clutter_canvas_get_instance_private (canvas);
if (priv->scale_factor != scale)
{
priv->scale_factor = scale;
g_object_freeze_notify (G_OBJECT (canvas));
clutter_content_invalidate (CLUTTER_CONTENT (canvas));
g_object_thaw_notify (G_OBJECT (canvas));
g_object_notify_by_pspec (G_OBJECT (canvas), obj_props[PROP_SCALE_FACTOR]);
}
}
/**
* clutter_canvas_get_scale_factor:
* @canvas: a #ClutterCanvas
*
* Gets the scale factor of the @canvas.
*
* Return value: the current @canvas scale factor or -1 if invalid
*/
float
clutter_canvas_get_scale_factor (ClutterCanvas *canvas)
{
ClutterCanvasPrivate *priv;
g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), -1.0f);
priv = clutter_canvas_get_instance_private (canvas);
return priv->scale_factor;
}

View File

@ -1,76 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2012 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/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#pragma once
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#include "clutter/clutter-types.h"
G_BEGIN_DECLS
#define CLUTTER_TYPE_CANVAS (clutter_canvas_get_type ())
CLUTTER_EXPORT
G_DECLARE_DERIVABLE_TYPE (ClutterCanvas,
clutter_canvas,
CLUTTER,
CANVAS,
GObject)
/**
* ClutterCanvasClass:
* @draw: class handler for the #ClutterCanvas::draw signal
*
* The #ClutterCanvasClass structure contains
* private data.
*/
struct _ClutterCanvasClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
gboolean (* draw) (ClutterCanvas *canvas,
cairo_t *cr,
int width,
int height);
};
CLUTTER_EXPORT
ClutterContent * clutter_canvas_new (void);
CLUTTER_EXPORT
gboolean clutter_canvas_set_size (ClutterCanvas *canvas,
int width,
int height);
CLUTTER_EXPORT
void clutter_canvas_set_scale_factor (ClutterCanvas *canvas,
float scale);
CLUTTER_EXPORT
float clutter_canvas_get_scale_factor (ClutterCanvas *canvas);
G_END_DECLS

View File

@ -41,7 +41,6 @@
#include "clutter/clutter-blur-effect.h"
#include "clutter/clutter-box-layout.h"
#include "clutter/clutter-brightness-contrast-effect.h"
#include "clutter/clutter-canvas.h"
#include "clutter/clutter-click-action.h"
#include "clutter/clutter-clone.h"
#include "clutter/clutter-color.h"

View File

@ -15,7 +15,6 @@ clutter_headers = [
'clutter-blur-effect.h',
'clutter-box-layout.h',
'clutter-brightness-contrast-effect.h',
'clutter-canvas.h',
'clutter-click-action.h',
'clutter-clone.h',
'clutter-color-state.h',
@ -100,7 +99,6 @@ clutter_sources = [
'clutter-blur-effect.c',
'clutter-box-layout.c',
'clutter-brightness-contrast-effect.c',
'clutter-canvas.c',
'clutter-click-action.c',
'clutter-clone.c',
'clutter-color.c',

View File

@ -21,19 +21,15 @@ clutter_tests_interactive_test_sources = [
'test-cogl-offscreen.c',
'test-cogl-tex-polygon.c',
'test-animation.c',
'test-easing.c',
'test-binding-pool.c',
'test-text.c',
'test-text-field.c',
'test-cairo-clock.c',
'test-cairo-flowers.c',
'test-stage-sizing.c',
'test-swipe-action.c',
'test-cogl-point-sprites.c',
'test-devices.c',
'test-content.c',
'test-keyframe-transition.c',
'test-touch-events.c',
'test-rotate-zoom.c',
'test-image.c',
]

View File

@ -1,134 +0,0 @@
#include <stdlib.h>
#include <math.h>
#include <cairo.h>
#include <clutter/clutter.h>
#include "tests/clutter-test-utils.h"
int
test_cairo_clock_main (int argc, char *argv[]);
const char *
test_cairo_clock_describe (void);
static gboolean
draw_clock (ClutterCanvas *canvas,
cairo_t *cr,
int width,
int height)
{
GDateTime *now;
float hours, minutes, seconds;
/* get the current time and compute the angles */
now = g_date_time_new_now_local ();
seconds = g_date_time_get_second (now) * G_PI / 30;
minutes = g_date_time_get_minute (now) * G_PI / 30;
hours = g_date_time_get_hour (now) * G_PI / 6;
/* clear the contents of the canvas, to avoid painting
* over the previous frame
*/
cairo_save (cr);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_restore (cr);
/* scale the modelview to the size of the surface */
cairo_scale (cr, width, height);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_width (cr, 0.1);
/* the black rail that holds the seconds indicator */
cairo_set_source_rgb (cr,
CLUTTER_COLOR_Black->red / 255.0,
CLUTTER_COLOR_Black->green / 255.0,
CLUTTER_COLOR_Black->blue / 255.0);
cairo_translate (cr, 0.5, 0.5);
cairo_arc (cr, 0, 0, 0.4, 0, G_PI * 2);
cairo_stroke (cr);
/* the seconds indicator */
cairo_set_source_rgb (cr,
CLUTTER_COLOR_White->red / 255.0,
CLUTTER_COLOR_White->green / 255.0,
CLUTTER_COLOR_White->blue / 255.0);
cairo_move_to (cr, 0, 0);
cairo_arc (cr, sinf (seconds) * 0.4, - cosf (seconds) * 0.4, 0.05, 0, G_PI * 2);
cairo_fill (cr);
/* the minutes hand */
cairo_set_source_rgb (cr,
CLUTTER_COLOR_DarkChameleon->red / 255.0,
CLUTTER_COLOR_DarkChameleon->green / 255.0,
CLUTTER_COLOR_DarkChameleon->blue / 255.0);
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, sinf (minutes) * 0.4, -cosf (minutes) * 0.4);
cairo_stroke (cr);
/* the hours hand */
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, sinf (hours) * 0.2, -cosf (hours) * 0.2);
cairo_stroke (cr);
g_date_time_unref (now);
/* we're done drawing */
return TRUE;
}
static gboolean
invalidate_clock (gpointer data_)
{
/* invalidate the contents of the canvas */
clutter_content_invalidate (data_);
/* keep the timeout source */
return TRUE;
}
G_MODULE_EXPORT int
test_cairo_clock_main (int argc, char *argv[])
{
ClutterActor *stage;
ClutterContent *canvas;
/* initialize Clutter */
clutter_test_init (&argc, &argv);
/* create a resizable stage */
stage = clutter_test_get_stage ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock");
clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue);
clutter_actor_set_size (stage, 300, 300);
clutter_actor_show (stage);
/* our 2D canvas, courtesy of Cairo */
canvas = clutter_canvas_new ();
clutter_canvas_set_size (CLUTTER_CANVAS (canvas), 300, 300);
clutter_actor_set_content (stage, canvas);
/* quit on destroy */
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_test_quit), NULL);
/* connect our drawing code */
g_signal_connect (canvas, "draw", G_CALLBACK (draw_clock), NULL);
/* invalidate the canvas, so that we can draw before the main loop starts */
clutter_content_invalidate (canvas);
/* set up a timer that invalidates the canvas every second */
clutter_threads_add_timeout (1000, invalidate_clock, canvas);
clutter_test_main ();
return EXIT_SUCCESS;
}
G_MODULE_EXPORT const char *
test_cairo_clock_describe (void)
{
return "Simple 2D canvas using a Cairo texture actor";
}

View File

@ -1,262 +0,0 @@
/*
* Pretty cairo flower hack.
*/
#include <clutter/clutter.h>
#include "tests/clutter-test-utils.h"
#ifndef _MSC_VER
#include <unistd.h> /* for sleep(), used for screenshots */
#endif
#include <stdlib.h>
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#define PETAL_MIN 20
#define PETAL_VAR 40
#define N_FLOWERS 40 /* reduce if you have a small card */
typedef struct Flower
{
ClutterActor *ctex;
gint x,y,rot,v,rv;
}
Flower;
static ClutterActor *stage = NULL;
int
test_cairo_flowers_main (int argc, char **argv);
const char *
test_cairo_flowers_describe (void);
static gboolean
draw_flower (ClutterCanvas *canvas,
cairo_t *cr,
gint width,
gint height,
gpointer user_data)
{
/* No science here, just a hack from toying */
gint i, j;
double colors[] = {
0.71, 0.81, 0.83,
1.0, 0.78, 0.57,
0.64, 0.30, 0.35,
0.73, 0.40, 0.39,
0.91, 0.56, 0.64,
0.70, 0.47, 0.45,
0.92, 0.75, 0.60,
0.82, 0.86, 0.85,
0.51, 0.56, 0.67,
1.0, 0.79, 0.58,
};
gint size;
gint petal_size;
gint n_groups; /* Num groups of petals 1-3 */
gint n_petals; /* num of petals 4 - 8 */
gint pm1, pm2;
gint idx, last_idx = -1;
petal_size = GPOINTER_TO_INT (user_data);
size = petal_size * 8;
n_groups = rand() % 3 + 1;
cairo_set_tolerance (cr, 0.1);
/* Clear */
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_translate(cr, size/2, size/2);
for (i=0; i<n_groups; i++)
{
n_petals = rand() % 5 + 4;
cairo_save (cr);
cairo_rotate (cr, rand() % 6);
do {
idx = (rand() % (sizeof (colors) / sizeof (double) / 3)) * 3;
} while (idx == last_idx);
cairo_set_source_rgba (cr,
colors[idx], colors[idx+1],
colors[idx+2], 0.5);
last_idx = idx;
/* some bezier randomness */
pm1 = rand() % 20;
pm2 = rand() % 4;
for (j=1; j<n_petals+1; j++)
{
cairo_save (cr);
cairo_rotate (cr, ((2*M_PI)/n_petals)*j);
/* Petals are made up beziers */
cairo_new_path (cr);
cairo_move_to (cr, 0, 0);
cairo_rel_curve_to (cr,
petal_size, petal_size,
(pm2 + 2) * petal_size, petal_size,
(2 * petal_size) + pm1, 0);
cairo_rel_curve_to (cr,
0 + (pm2 * petal_size), -petal_size,
-petal_size, -petal_size,
-((2 * petal_size) + pm1), 0);
cairo_close_path (cr);
cairo_fill (cr);
cairo_restore (cr);
}
petal_size -= rand() % (size/8);
cairo_restore (cr);
}
/* Finally draw flower center */
do {
idx = (rand() % (sizeof (colors) / sizeof (double) / 3)) * 3;
} while (idx == last_idx);
if (petal_size < 0)
petal_size = rand() % 10;
cairo_set_source_rgba (cr, colors[idx], colors[idx+1], colors[idx+2], 0.5);
cairo_arc(cr, 0, 0, petal_size, 0, M_PI * 2);
cairo_fill(cr);
return TRUE;
}
static ClutterActor *
make_flower_actor (void)
{
gint petal_size = PETAL_MIN + rand() % PETAL_VAR;
gint size = petal_size * 8;
ClutterActor *ctex;
ClutterContent *canvas;
canvas = clutter_canvas_new ();
g_signal_connect (canvas, "draw",
G_CALLBACK (draw_flower), GINT_TO_POINTER (petal_size));
clutter_canvas_set_size (CLUTTER_CANVAS (canvas), size, size);
ctex = g_object_new (CLUTTER_TYPE_ACTOR,
"content", canvas,
"width", (gfloat) size,
"height", (gfloat) size,
NULL);
clutter_actor_set_pivot_point (ctex, 0.5, 0.5);
g_object_unref (canvas);
return ctex;
}
static void
tick (ClutterTimeline *timeline,
gint msecs,
gpointer data)
{
Flower **flowers = data;
gint i = 0;
for (i = 0; i < N_FLOWERS; i++)
{
flowers[i]->y += flowers[i]->v;
flowers[i]->rot += flowers[i]->rv;
if (flowers[i]->y > (gint) clutter_actor_get_height (stage))
flowers[i]->y = -clutter_actor_get_height (flowers[i]->ctex);
clutter_actor_set_position (flowers[i]->ctex,
flowers[i]->x, flowers[i]->y);
clutter_actor_set_rotation_angle (flowers[i]->ctex,
CLUTTER_Z_AXIS,
flowers[i]->rot);
}
}
static void
stop_and_quit (ClutterActor *actor,
ClutterTimeline *timeline)
{
clutter_timeline_stop (timeline);
clutter_test_quit ();
}
G_MODULE_EXPORT int
test_cairo_flowers_main (int argc, char **argv)
{
Flower *flowers[N_FLOWERS];
ClutterTimeline *timeline;
int i;
srand (time (NULL));
clutter_test_init (&argc, &argv);
stage = clutter_test_get_stage ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "Cairo Flowers");
/* Create a timeline to manage animation */
timeline = clutter_timeline_new_for_actor (stage, 6000);
clutter_timeline_set_repeat_count (timeline, -1);
g_signal_connect (stage, "destroy", G_CALLBACK (stop_and_quit), timeline);
clutter_actor_set_background_color (stage, CLUTTER_COLOR_Black);
for (i=0; i< N_FLOWERS; i++)
{
flowers[i] = g_new0(Flower, 1);
flowers[i]->ctex = make_flower_actor();
flowers[i]->x = rand() % (int) clutter_actor_get_width (stage)
- (PETAL_MIN + PETAL_VAR) * 2;
flowers[i]->y = rand() % (int) clutter_actor_get_height (stage);
flowers[i]->rv = rand() % 5 + 1;
flowers[i]->v = rand() % 10 + 2;
clutter_actor_add_child (stage, flowers[i]->ctex);
clutter_actor_set_position (flowers[i]->ctex,
flowers[i]->x,
flowers[i]->y);
}
/* fire a callback for frame change */
g_signal_connect (timeline, "new-frame", G_CALLBACK (tick), flowers);
clutter_actor_show (stage);
clutter_timeline_start (timeline);
g_signal_connect (stage, "key-press-event",
G_CALLBACK (clutter_test_quit),
NULL);
clutter_test_main ();
g_object_unref (timeline);
return EXIT_SUCCESS;
}
G_MODULE_EXPORT const char *
test_cairo_flowers_describe (void)
{
return "Drawing pretty flowers with Cairo";
}

View File

@ -1,254 +0,0 @@
#include <stdlib.h>
#include <gmodule.h>
#include <clutter/clutter.h>
#include "tests/clutter-test-utils.h"
/* all the easing modes provided by Clutter */
static const struct {
const gchar *name;
ClutterAnimationMode mode;
} easing_modes[] = {
{ "linear", CLUTTER_LINEAR },
{ "easeInQuad", CLUTTER_EASE_IN_QUAD },
{ "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
{ "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
{ "easeInCubic", CLUTTER_EASE_IN_CUBIC },
{ "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
{ "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
{ "easeInQuart", CLUTTER_EASE_IN_QUART },
{ "easeOutQuart", CLUTTER_EASE_OUT_QUART },
{ "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
{ "easeInQuint", CLUTTER_EASE_IN_QUINT },
{ "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
{ "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
{ "easeInSine", CLUTTER_EASE_IN_SINE },
{ "easeOutSine", CLUTTER_EASE_OUT_SINE },
{ "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
{ "easeInExpo", CLUTTER_EASE_IN_EXPO },
{ "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
{ "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
{ "easeInCirc", CLUTTER_EASE_IN_CIRC },
{ "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
{ "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
{ "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
{ "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
{ "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
{ "easeInBack", CLUTTER_EASE_IN_BACK },
{ "easeOutBack", CLUTTER_EASE_OUT_BACK },
{ "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
{ "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
{ "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
{ "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
};
#define HELP_TEXT "Easing mode: %s (%d of %d)\n" \
"Left click to tween\n" \
"Right click to change the easing mode"
static const gint n_easing_modes = G_N_ELEMENTS (easing_modes);
static gint current_mode = 0;
#define DURATION 1
static ClutterActor *main_stage = NULL;
static ClutterActor *easing_mode_label = NULL;
int
test_easing_main (int argc, char *argv[]);
const char *
test_easing_describe (void);
/* recenter_bouncer:
*
* repositions (through an animation) the bouncer at the center of the stage
*/
static void
recenter_bouncer (ClutterActor *rectangle)
{
gfloat base_x, base_y;
gint cur_mode;
cur_mode = easing_modes[current_mode].mode;
base_x = clutter_actor_get_width (main_stage) / 2;
base_y = clutter_actor_get_height (main_stage) / 2;
clutter_actor_set_easing_duration (rectangle, 250);
clutter_actor_set_easing_mode (rectangle, cur_mode);
clutter_actor_set_position (rectangle, base_x, base_y);
g_signal_connect_after (rectangle, "transition-completed",
G_CALLBACK (clutter_actor_restore_easing_state),
NULL);
}
static gboolean
on_button_press (ClutterActor *actor,
ClutterButtonEvent *event,
ClutterActor *rectangle)
{
if (clutter_event_get_button ((ClutterEvent *) event) == CLUTTER_BUTTON_SECONDARY)
{
gchar *text;
/* cycle through the various easing modes */
current_mode = (current_mode + 1 < n_easing_modes)
? current_mode + 1
: 0;
/* update the text of the label */
text = g_strdup_printf (HELP_TEXT,
easing_modes[current_mode].name,
current_mode + 1,
n_easing_modes);
clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text);
g_free (text);
}
else if (clutter_event_get_button ((ClutterEvent *) event) == CLUTTER_BUTTON_PRIMARY)
{
ClutterAnimationMode cur_mode;
float x, y;
cur_mode = easing_modes[current_mode].mode;
clutter_actor_save_easing_state (rectangle);
clutter_actor_set_easing_duration (rectangle, DURATION * 1000);
clutter_actor_set_easing_mode (rectangle, cur_mode);
clutter_event_get_coords ((ClutterEvent *) event, &x, &y);
clutter_actor_set_position (rectangle, x, y);
/* if we were asked to, recenter the bouncer at the end of the
* animation. we keep track of the animation to avoid connecting
* the signal handler to the same Animation twice.
*/
g_signal_connect_after (rectangle, "transition-completed",
G_CALLBACK (recenter_bouncer),
rectangle);
}
return TRUE;
}
static gboolean
draw_bouncer (ClutterCanvas *canvas,
cairo_t *cr,
int width,
int height)
{
const ClutterColor *bouncer_color;
cairo_pattern_t *pattern;
float radius;
radius = MAX (width, height);
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_restore (cr);
cairo_arc (cr, radius / 2, radius / 2, radius / 2, 0.0, 2.0 * G_PI);
bouncer_color = CLUTTER_COLOR_DarkScarletRed;
pattern = cairo_pattern_create_radial (radius / 2, radius / 2, 0,
radius, radius, radius);
cairo_pattern_add_color_stop_rgba (pattern,
0,
bouncer_color->red / 255.0,
bouncer_color->green / 255.0,
bouncer_color->blue / 255.0,
bouncer_color->alpha / 255.0);
cairo_pattern_add_color_stop_rgba (pattern,
0.85,
bouncer_color->red / 255.0,
bouncer_color->green / 255.0,
bouncer_color->blue / 255.0,
0.25);
cairo_set_source (cr, pattern);
cairo_fill_preserve (cr);
cairo_pattern_destroy (pattern);
return TRUE;
}
static ClutterActor *
make_bouncer (gfloat width,
gfloat height)
{
ClutterContent *canvas;
ClutterActor *retval;
canvas = clutter_canvas_new ();
clutter_canvas_set_size (CLUTTER_CANVAS (canvas), width, height);
g_signal_connect (canvas, "draw", G_CALLBACK (draw_bouncer), NULL);
retval = g_object_new (CLUTTER_TYPE_ACTOR,
"content", canvas,
NULL);
clutter_actor_set_name (retval, "bouncer");
clutter_actor_set_size (retval, width, height);
clutter_actor_set_translation (retval, -width / 2.f, -height / 2.f, 0.f);
clutter_actor_set_reactive (retval, TRUE);
clutter_content_invalidate (canvas);
return retval;
}
G_MODULE_EXPORT int
test_easing_main (int argc, char *argv[])
{
ClutterActor *stage, *rect, *label;
gchar *text;
gfloat stage_width, stage_height;
clutter_test_init (&argc, &argv);
stage = clutter_test_get_stage ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "Easing Modes");
clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue);
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_test_quit), NULL);
main_stage = stage;
clutter_actor_get_size (stage, &stage_width, &stage_height);
/* create the actor that we want to tween */
rect = make_bouncer (50, 50);
clutter_actor_add_child (stage, rect);
clutter_actor_set_position (rect, stage_width / 2, stage_height / 2);
text = g_strdup_printf (HELP_TEXT,
easing_modes[current_mode].name,
current_mode + 1,
n_easing_modes);
label = clutter_text_new ();
clutter_actor_add_child (stage, label);
clutter_text_set_text (CLUTTER_TEXT (label), text);
clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.95));
clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95));
easing_mode_label = label;
g_free (text);
g_signal_connect (stage,
"button-press-event", G_CALLBACK (on_button_press),
rect);
clutter_actor_show (stage);
clutter_test_main ();
return EXIT_SUCCESS;
}
G_MODULE_EXPORT const char *
test_easing_describe (void)
{
return "Visualize all easing modes provided by Clutter";
}

View File

@ -1,185 +0,0 @@
/*
* Copyright (C) 2012 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <math.h>
#include <cairo.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "tests/clutter-test-utils.h"
#define STAGE_WIDTH 800
#define STAGE_HEIGHT 550
#define NUM_COLORS 10
#define NUM_ACTORS 10
static GQueue events = G_QUEUE_INIT;
static GQueue all_events = G_QUEUE_INIT;
static gboolean new_surface = TRUE;
static const ClutterColor static_colors[] = {
{ 0xff, 0x00, 0x00, 0xff }, /* red */
{ 0x80, 0x00, 0x00, 0xff }, /* dark red */
{ 0x00, 0xff, 0x00, 0xff }, /* green */
{ 0x00, 0x80, 0x00, 0xff }, /* dark green */
{ 0x00, 0x00, 0xff, 0xff }, /* blue */
{ 0x00, 0x00, 0x80, 0xff }, /* dark blue */
{ 0x00, 0xff, 0xff, 0xff }, /* cyan */
{ 0x00, 0x80, 0x80, 0xff }, /* dark cyan */
{ 0xff, 0x00, 0xff, 0xff }, /* magenta */
{ 0xff, 0xff, 0x00, 0xff }, /* yellow */
};
static GHashTable *sequence_to_color = NULL;
int
test_touch_events_main (int argc, char *argv[]);
const char *
test_touch_events_describe (void);
static void
draw_touch (ClutterEvent *event,
cairo_t *cr)
{
ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
const ClutterColor *color;
float x, y;
color = g_hash_table_lookup (sequence_to_color, sequence);
if (color == NULL)
{
color = &static_colors[g_random_int_range (0, NUM_COLORS)];
g_hash_table_insert (sequence_to_color, (gpointer) sequence, (gpointer) color);
}
cairo_set_source_rgba (cr, color->red / 255,
color->green / 255,
color->blue / 255,
color->alpha / 255);
clutter_event_get_coords (event, &x, &y);
cairo_arc (cr, x, y, 5, 0, 2 * G_PI);
cairo_fill (cr);
}
static gboolean
draw_touches (ClutterCanvas *canvas,
cairo_t *cr,
int width,
int height)
{
g_queue_foreach (new_surface ? &all_events : &events, (GFunc) draw_touch, cr);
g_queue_clear (&events);
new_surface = FALSE;
return TRUE;
}
static gboolean
event_cb (ClutterActor *actor, ClutterEvent *event, ClutterActor *canvas)
{
ClutterEvent *copy;
if (clutter_event_type (event) != CLUTTER_TOUCH_UPDATE)
return FALSE;
copy = clutter_event_copy (event);
g_queue_push_tail (&events, copy);
g_queue_push_tail (&all_events, copy);
clutter_actor_queue_redraw (canvas);
return TRUE;
}
static gboolean
rect_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer data)
{
ClutterColor color;
if (clutter_event_type (event) != CLUTTER_TOUCH_BEGIN)
return FALSE;
color = static_colors[g_random_int_range (0, NUM_COLORS)];
clutter_actor_set_background_color (actor, &color);
return TRUE;
}
G_MODULE_EXPORT int
test_touch_events_main (int argc, char *argv[])
{
ClutterActor *stage, *canvas_actor;
ClutterContent *canvas;
int i;
/* initialize Clutter */
clutter_test_init (&argc, &argv);
/* create a resizable stage */
stage = clutter_test_get_stage ();
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_test_quit), NULL);
clutter_stage_set_title (CLUTTER_STAGE (stage), "Touch events");
clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
clutter_actor_set_reactive (stage, TRUE);
clutter_actor_show (stage);
/* our 2D canvas, courtesy of Cairo */
canvas = clutter_canvas_new ();
clutter_canvas_set_size (CLUTTER_CANVAS (canvas), STAGE_WIDTH, STAGE_HEIGHT);
g_signal_connect (canvas, "draw", G_CALLBACK (draw_touches), NULL);
canvas_actor = g_object_new (CLUTTER_TYPE_ACTOR,
"content", canvas,
NULL);
clutter_actor_add_child (stage, canvas_actor);
g_signal_connect (stage, "event", G_CALLBACK (event_cb), canvas_actor);
for (i = 0; i < NUM_ACTORS; i++)
{
gfloat size = STAGE_HEIGHT / NUM_ACTORS;
ClutterColor color = static_colors[i % NUM_COLORS];
ClutterActor *rectangle = clutter_actor_new ();
clutter_actor_set_background_color (rectangle, &color);
/* Test that event delivery to actors work */
g_signal_connect (rectangle, "event", G_CALLBACK (rect_event_cb), NULL);
clutter_actor_add_child (stage, rectangle);
clutter_actor_set_size (rectangle, size, size);
clutter_actor_set_position (rectangle, 0, i * size);
clutter_actor_set_reactive (rectangle, TRUE);
}
sequence_to_color = g_hash_table_new (NULL, NULL);
clutter_test_main ();
g_queue_foreach (&all_events, (GFunc) clutter_event_free, NULL);
g_queue_clear (&events);
g_queue_clear (&all_events);
g_hash_table_destroy (sequence_to_color);
return EXIT_SUCCESS;
}
G_MODULE_EXPORT const char *
test_touch_events_describe (void)
{
return "Draw shapes based on touch events";
}