mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 08:00:42 -05:00
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:
parent
41a7e8e3e0
commit
237e505cc7
@ -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 (¶m_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;
|
||||
}
|
@ -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
|
@ -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"
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
Loading…
Reference in New Issue
Block a user