diff --git a/ChangeLog b/ChangeLog index 53cf2c72c..f9234b94d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2007-05-14 Matthew Allum + + * clutter/clutter-behaviour-path.c: + Fix bug where last knot position wouldn't get reached. + + * clutter/clutter-group.c: + Add some docs + + * clutter/clutter-timeline.h: + * clutter/clutter-timeline.c: + Add clutter_timeline_copy (needed for ClutterEffect) + + * clutter/clutter-version.h.in: + Export windowing system / GL backend etc defines. + + * clutter/Makefile.am: + * clutter/clutter-effect.c: + * clutter/clutter-effect.h: + * clutter/clutter.h: + + * clutter/glx/clutter-backend-glx.c: + Minor clean ups. + + * clutter/clutter-alpha.h: + Add a fixme. + + * configure.ac: + Add FPU define. + + * examples/Makefile.am: + * examples/slider.c: + Add Robs slider game. + 2007-05-10 Matthew Allum * clutter/egl/clutter-backend-egl.c: diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 179792153..13527232c 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -59,6 +59,7 @@ source_h = \ $(srcdir)/clutter-stage.h \ $(srcdir)/clutter-texture.h \ $(srcdir)/clutter-timeline.h \ + $(srcdir)/clutter-effect.h \ $(srcdir)/clutter-util.h \ $(srcdir)/clutter-version.h \ $(NULL) @@ -146,6 +147,7 @@ source_c = \ clutter-rectangle.c \ clutter-texture.c \ clutter-timeline.c \ + $(srcdir)/clutter-effect.c \ clutter-util.c \ $(NULL) diff --git a/clutter/clutter-alpha.h b/clutter/clutter-alpha.h index b4d732c2d..5faf704dd 100644 --- a/clutter/clutter-alpha.h +++ b/clutter/clutter-alpha.h @@ -104,6 +104,7 @@ ClutterTimeline *clutter_alpha_get_timeline (ClutterAlpha *alpha); #define CLUTTER_ALPHA_RAMP_DEC clutter_ramp_dec_func #define CLUTTER_ALPHA_RAMP clutter_ramp_func #define CLUTTER_ALPHA_SINE clutter_sine_func +/* FIXME add SINE_INC/DEC */ #define CLUTTER_ALPHA_SQUARE clutter_square_func guint32 clutter_ramp_inc_func (ClutterAlpha *alpha, diff --git a/clutter/clutter-behaviour-path.c b/clutter/clutter-behaviour-path.c index c0727c3bc..c82dbca90 100644 --- a/clutter/clutter-behaviour-path.c +++ b/clutter/clutter-behaviour-path.c @@ -145,6 +145,8 @@ actor_apply_knot_foreach (ClutterBehaviour *behaviour, { ClutterKnot *knot = data; + CLUTTER_NOTE (BEHAVIOUR, "Setting actor to %ix%i", knot->x, knot->y); + clutter_actor_set_position (actor, knot->x, knot->y); } @@ -172,8 +174,13 @@ path_alpha_to_position (ClutterBehaviourPath *behave, total_len = path_total_length (behave); offset = (alpha * total_len) / CLUTTER_ALPHA_MAX_ALPHA; + CLUTTER_NOTE (BEHAVIOUR, "alpha %i vs %i, len: %i vs %i", + alpha, CLUTTER_ALPHA_MAX_ALPHA, + offset, total_len); + if (offset == 0) { + /* first knot */ clutter_behaviour_actors_foreach (behaviour, actor_apply_knot_foreach, priv->knots->data); @@ -182,6 +189,20 @@ path_alpha_to_position (ClutterBehaviourPath *behave, priv->knots->data); return; } + + if (offset == total_len) + { + /* Special case for last knot */ + ClutterKnot *last_knot = (g_slist_last (priv->knots))->data; + + clutter_behaviour_actors_foreach (behaviour, + actor_apply_knot_foreach, + last_knot); + + g_signal_emit (behave, path_signals[KNOT_REACHED], 0, last_knot); + + return; + } for (l = priv->knots; l != NULL; l = l->next) { @@ -192,6 +213,7 @@ path_alpha_to_position (ClutterBehaviourPath *behave, ClutterKnot *next = l->next->data; dist_to_next = node_distance (knot, next); + if (offset >= dist && offset < (dist + dist_to_next)) { ClutterKnot new; diff --git a/clutter/clutter-effect.c b/clutter/clutter-effect.c new file mode 100644 index 000000000..07490894d --- /dev/null +++ b/clutter/clutter-effect.c @@ -0,0 +1,348 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * Jorn Baayen + * Emmanuele Bassi + * + * Copyright (C) 2006 OpenedHand + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:clutter-effect-template + * @short_description: A utility class + * + * + * Since: 0.4 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-alpha.h" +#include "clutter-main.h" +#include "clutter-marshal.h" +#include "clutter-private.h" +#include "clutter-debug.h" +#include "clutter-behaviour-bspline.h" +#include "clutter-behaviour-ellipse.h" +#include "clutter-behaviour-opacity.h" +#include "clutter-behaviour-path.h" +#include "clutter-behaviour-rotate.h" +#include "clutter-behaviour-scale.h" + +#include "clutter-effect.h" + +typedef struct ClutterEffectClosure +{ + ClutterActor *actor; + ClutterTimeline *timeline; + ClutterAlpha *alpha; + ClutterBehaviour *behave; + gulong signal_id; + ClutterEffectCompleteFunc completed_func; + gpointer completed_data; + ClutterEffectTemplate *template; +} +ClutterEffectClosure; + +G_DEFINE_TYPE (ClutterEffectTemplate, clutter_effect_template, G_TYPE_OBJECT); + +#define EFFECT_TEMPLATE_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + CLUTTER_TYPE_EFFECT_TEMPLATE, \ + ClutterEffectTemplatePrivate)) + +typedef struct _ClutterEffectTemplatePrivate ClutterEffectTemplatePrivate; + +struct _ClutterEffectTemplatePrivate +{ + ClutterTimeline *timeline; + ClutterAlphaFunc alpha_func; +}; + +enum +{ + PROP_0, + PROP_ALPHA_FUNC, + PROP_TIMELINE, +}; + +static void +clutter_effect_template_dispose (GObject *object) +{ + ClutterEffectTemplate *template; + ClutterEffectTemplatePrivate *priv; + + template = CLUTTER_EFFECT_TEMPLATE(object); + priv = EFFECT_TEMPLATE_PRIVATE(template); + + g_object_unref (priv->timeline); + + priv->timeline = NULL; + priv->alpha_func = NULL; + + if (G_OBJECT_CLASS (clutter_effect_template_parent_class)->dispose) + G_OBJECT_CLASS (clutter_effect_template_parent_class)->dispose (object); +} + +static void +clutter_effect_template_finalize (GObject *object) +{ + + G_OBJECT_CLASS (clutter_effect_template_parent_class)->finalize (object); +} + +static void +clutter_effect_template_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterEffectTemplate *template; + ClutterEffectTemplatePrivate *priv; + + template = CLUTTER_EFFECT_TEMPLATE(object); + priv = EFFECT_TEMPLATE_PRIVATE(template); + + switch (prop_id) + { + case PROP_ALPHA_FUNC: + priv->alpha_func = g_value_get_pointer (value); + break; + case PROP_TIMELINE: + priv->timeline = g_value_get_object (value); + g_object_ref(priv->timeline); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_effect_template_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterEffectTemplate *template; + ClutterEffectTemplatePrivate *priv; + + template = CLUTTER_EFFECT_TEMPLATE(object); + priv = EFFECT_TEMPLATE_PRIVATE(template); + + switch (prop_id) + { + case PROP_ALPHA_FUNC: + g_value_set_pointer (value, priv->alpha_func); + break; + case PROP_TIMELINE: + g_value_set_object (value, priv->timeline); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_effect_template_class_init (ClutterEffectTemplateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ClutterEffectTemplatePrivate)); + + object_class->dispose = clutter_effect_template_dispose; + object_class->finalize = clutter_effect_template_finalize; + + object_class->set_property = clutter_effect_template_set_property; + object_class->get_property = clutter_effect_template_get_property; + + g_object_class_install_property + (object_class, + PROP_ALPHA_FUNC, + g_param_spec_pointer ("alpha-func", + "Alpha-Function", + "Alpha reference Function", + G_PARAM_CONSTRUCT_ONLY | + CLUTTER_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, + PROP_TIMELINE, + g_param_spec_object ("timeline", + "Timeline", + "Timeline to use as a reference for the Template", + CLUTTER_TYPE_TIMELINE, + G_PARAM_CONSTRUCT_ONLY | + CLUTTER_PARAM_READWRITE)); +} + +static void +clutter_effect_template_init (ClutterEffectTemplate *self) +{ +} + +/** + * clutter_effect_template_new: + * + * FIXME + * + * Return value: a #ClutterEffectTemplate + * + * Since: 0.4 + */ +ClutterEffectTemplate* +clutter_effect_template_new (ClutterTimeline *timeline, + ClutterAlphaFunc alpha_func) +{ + return g_object_new (CLUTTER_TYPE_EFFECT_TEMPLATE, + "timeline", timeline, + "alpha-func", alpha_func, + NULL); +} + +static void +clutter_effect_closure_destroy (ClutterEffectClosure *c) +{ + g_signal_handler_disconnect (c->timeline, c->signal_id); + clutter_behaviour_remove (c->behave, c->actor); + + g_object_unref (c->actor); + g_object_unref (c->template); + g_object_unref (c->behave); + g_object_unref (c->alpha); + g_object_unref (c->timeline); + + g_slice_free (ClutterEffectClosure, c); +} + +static ClutterEffectClosure* +clutter_effect_closure_new (ClutterEffectTemplate *template, + ClutterActor *actor, + GCallback complete) +{ + ClutterEffectClosure *c; + ClutterEffectTemplatePrivate *priv = EFFECT_TEMPLATE_PRIVATE(template); + + c = g_slice_new0(ClutterEffectClosure); + + g_object_ref (actor); + g_object_ref (template); + + c->template = template; + c->actor = actor; + c->timeline = clutter_timeline_copy (priv->timeline); + c->alpha = clutter_alpha_new_full (c->timeline, + priv->alpha_func, + NULL, NULL); + + c->signal_id = g_signal_connect (c->timeline, + "completed", + G_CALLBACK(complete), + c); + return c; +} + +static void +on_effect_complete (ClutterTimeline *timeline, + gpointer user_data) +{ + ClutterEffectClosure *c = (ClutterEffectClosure*)user_data; + + if (c->completed_func) + c->completed_func(c->actor, c->completed_data); + + clutter_effect_closure_destroy (c); +} + +/** + * clutter_effect_fade: + * + * FIXME + * + * Return value: an alpha value. + * + * Since: 0.4 + */ +ClutterTimeline* +clutter_effect_fade (ClutterEffectTemplate *template, + ClutterActor *actor, + guint8 start_opacity, + guint8 end_opacity, + ClutterEffectCompleteFunc completed_func, + gpointer completed_userdata) +{ + ClutterEffectClosure *c; + + c = clutter_effect_closure_new (template, + actor, + G_CALLBACK (on_effect_complete)); + + c->completed_func = completed_func; + c->completed_data = completed_userdata; + + c->behave = clutter_behaviour_opacity_new (c->alpha, + start_opacity, + end_opacity); + + clutter_behaviour_apply (c->behave, actor); + clutter_timeline_start (c->timeline); + + return c->timeline; +} + +/** + * clutter_effect_move: + * + * FIXME + * + * Return value: an alpha value. + * + * Since: 0.4 + */ +ClutterTimeline* +clutter_effect_move (ClutterEffectTemplate *template, + ClutterActor *actor, + const ClutterKnot *knots, + guint n_knots, + ClutterEffectCompleteFunc completed_func, + gpointer completed_userdata) +{ + ClutterEffectClosure *c; + + c = clutter_effect_closure_new (template, + actor, + G_CALLBACK (on_effect_complete)); + + c->completed_func = completed_func; + c->completed_data = completed_userdata; + + c->behave = clutter_behaviour_path_new (c->alpha, knots, n_knots); + + clutter_behaviour_apply (c->behave, actor); + clutter_timeline_start (c->timeline); + + return c->timeline; +} diff --git a/clutter/clutter-effect.h b/clutter/clutter-effect.h new file mode 100644 index 000000000..4824b37a2 --- /dev/null +++ b/clutter/clutter-effect.h @@ -0,0 +1,97 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006, 2007 OpenedHand + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _CLUTTER_EFFECT +#define _CLUTTER_EFFECT + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef void (*ClutterEffectCompleteFunc) (ClutterActor *actor, + gpointer user_data); + +#define CLUTTER_TYPE_EFFECT_TEMPLATE clutter_effect_template_get_type() + +#define CLUTTER_EFFECT_TEMPLATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_TYPE_EFFECT_TEMPLATE, ClutterEffectTemplate)) + +#define CLUTTER_EFFECT_TEMPLATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_TYPE_EFFECT_TEMPLATE, ClutterEffectTemplateClass)) + +#define CLUTTER_IS_EFFECT_TEMPLATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_TYPE_EFFECT_TEMPLATE)) + +#define CLUTTER_IS_EFFECT_TEMPLATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_TYPE_EFFECT_TEMPLATE)) + +#define CLUTTER_EFFECT_TEMPLATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_TYPE_EFFECT_TEMPLATE, ClutterEffectTemplateClass)) + +typedef struct { + GObject parent; +} ClutterEffectTemplate; + +typedef struct { + GObjectClass parent_class; +} ClutterEffectTemplateClass; + +GType clutter_effect_template_get_type (void); + +ClutterEffectTemplate* +clutter_effect_template_new (ClutterTimeline *timeline, + ClutterAlphaFunc alpha_func); + +ClutterTimeline* +clutter_effect_fade (ClutterEffectTemplate *template, + ClutterActor *actor, + guint8 start_opacity, + guint8 end_opacity, + ClutterEffectCompleteFunc completed_func, + gpointer completed_data); + +ClutterTimeline* +clutter_effect_move (ClutterEffectTemplate *template, + ClutterActor *actor, + const ClutterKnot *knots, + guint n_knots, + ClutterEffectCompleteFunc completed_func, + gpointer completed_data); + + +G_END_DECLS + +#endif /* _CLUTTER_EFFECT */ + +G_END_DECLS + diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index fb450b8d3..a568aad85 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -25,10 +25,15 @@ /** * SECTION:clutter-group - * @short_description: Base class for actors which contain multiple child + * @short_description: Actor class containing multiple children. * actors. * - * A #ClutterGroup is an Actor which can contain multiple child actors. + * A #ClutterGroup is an Actor which contains multiple child actors positioned + * relative to the #ClutterGroup position. Other operations such as scaling, + * rotating and clipping of the group will child actors. + * + * A ClutterGroup's size is defined by the size and position of it + * it children. Resize requests via parent #ClutterActor will be ignored. */ #include "config.h" diff --git a/clutter/clutter-timeline.c b/clutter/clutter-timeline.c index 2d879adb2..8b8218401 100644 --- a/clutter/clutter-timeline.c +++ b/clutter/clutter-timeline.c @@ -627,6 +627,33 @@ clutter_timeline_is_playing (ClutterTimeline *timeline) return (timeline->priv->timeout_id != 0); } +/** + * clutter_timeline_copy: + * @timeline: #ClutterTimeline to duplicate. + * + * Create a new #ClutterTimeline instance which has property values + * matching that of supplied timeline. + * + * Return Value: a new #ClutterTimeline + * + * Since 0.4 + */ +ClutterTimeline* +clutter_timeline_copy (ClutterTimeline *timeline) +{ + ClutterTimeline *copy; + + g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); + + copy = g_object_new (CLUTTER_TYPE_TIMELINE, NULL); + + copy->priv->fps = timeline->priv->fps; + copy->priv->n_frames = timeline->priv->n_frames; + copy->priv->loop = timeline->priv->loop; + + return copy; +} + /** * clutter_timeline_new: * @n_frames: the number of frames diff --git a/clutter/clutter-timeline.h b/clutter/clutter-timeline.h index c7953437d..92f077635 100644 --- a/clutter/clutter-timeline.h +++ b/clutter/clutter-timeline.h @@ -106,6 +106,7 @@ void clutter_timeline_set_n_frames (ClutterTimeline *timeline, guint n_frames); guint clutter_timeline_get_n_frames (ClutterTimeline *timeline); gboolean clutter_timeline_is_playing (ClutterTimeline *timeline); +ClutterTimeline* clutter_timeline_copy (ClutterTimeline *timeline); G_END_DECLS diff --git a/clutter/clutter-version.h.in b/clutter/clutter-version.h.in index 24237ca43..f8db809b8 100644 --- a/clutter/clutter-version.h.in +++ b/clutter/clutter-version.h.in @@ -41,6 +41,13 @@ (CLUTTER_MAJOR_VERSION == (major) && CLUTTER_MINOR_VERSION > (minor)) || \ (CLUTTER_MAJOR_VERSION == (major) && CLUTTER_MINOR_VERSION == (minor) && CLUTTER_MICRO_VERSION > (micro))) +/* GL Windowing system used */ #define CLUTTER_FLAVOUR "@CLUTTER_FLAVOUR@" +/* cogl backend - gl or gles currently */ +#define CLUTTER_COGL "@CLUTTER_COGL@" + +/* Set to 1 if clutter built without FPU (i.e fixed math), 0 otherwise */ +#define CLUTTER_NO_FPU @CLUTTER_NO_FPU@ + #endif /* __CLUTTER_VERSION_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 3448f6dd4..c57b44eef 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -39,6 +39,7 @@ #include "clutter-behaviour-path.h" #include "clutter-behaviour-rotate.h" #include "clutter-behaviour-scale.h" +#include "clutter-effect.h" #include "clutter-stage.h" #include "clutter-actor.h" #include "clutter-rectangle.h" diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 721c2a1b3..2d8e0fc91 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -167,6 +167,7 @@ clutter_backend_glx_init_stage (ClutterBackend *backend, } /* At least GL 1.2 is needed for CLAMP_TO_EDGE */ + /* FIXME: move to cogl... */ if (!is_gl_version_at_least_12 ()) { g_set_error (error, CLUTTER_INIT_ERROR, @@ -186,7 +187,7 @@ clutter_backend_glx_init_events (ClutterBackend *backend) _clutter_backend_glx_events_init (backend); } -static ClutterActor * +static ClutterActor* clutter_backend_glx_get_stage (ClutterBackend *backend) { ClutterBackendGlx *backend_glx = CLUTTER_BACKEND_GLX (backend); diff --git a/configure.ac b/configure.ac index 74f59322d..4dea30363 100644 --- a/configure.ac +++ b/configure.ac @@ -239,6 +239,8 @@ fi AC_SUBST(CLUTTER_DEBUG_CFLAGS) +CLUTTER_NO_FPU=0 + AC_ARG_WITH(fpu, AS_HELP_STRING([--without-fpu], [Assume target hardware has no fpu]), @@ -246,9 +248,12 @@ AC_ARG_WITH(fpu, [with_fpu=yes]) if test "x$with_fpu" != "xyes" ; then + CLUTTER_NO_FPU=1 CLUTTER_FIXED_CFLAGS="-DCFX_NO_FPU" fi +AC_SUBST(CLUTTER_HAS_FPU) + dnl = GTK Doc check ======================================================== GTK_DOC_CHECK([1.4]) diff --git a/examples/Makefile.am b/examples/Makefile.am index 0e957a23e..7efa6d564 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,8 +1,13 @@ -noinst_PROGRAMS = test super-oh behave test-text +noinst_PROGRAMS = test super-oh behave test-text slider INCLUDES = -I$(top_srcdir)/ LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la +AM_CFLAGS = $(CLUTTER_CFLAGS) +LDFLAGS = $(CLUTTER_LIBS) + +slider_SOURCES = slider.c + test_SOURCES = test.c test_CFLAGS = $(CLUTTER_CFLAGS) $(GCONF_CFLAGS) test_LDFLAGS = \ diff --git a/examples/slider.c b/examples/slider.c new file mode 100644 index 000000000..31385c6f9 --- /dev/null +++ b/examples/slider.c @@ -0,0 +1,183 @@ +#include + +typedef struct Tile +{ + ClutterActor *actor; + gint orig_pos; +} +Tile; + +static Tile *Tiles[4][4]; +static int TileW, TileH, BlankTileX, BlankTileY; +static ClutterEffectTemplate *Template; +static ClutterTimeline *EffectTimeline; + +ClutterActor* +make_tiles (GdkPixbuf *pixbuf) +{ + int x, y , w, h; + int i = 0, j = 0; + int pos = 0; + ClutterActor *group; + + group = clutter_group_new(); + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + + TileW = w / 4; + TileH = h / 4; + + for (y = 0; y < h; y += TileH) + { + for (x = 0; x < w; x += TileW) + { + GdkPixbuf *subpixbuf; + Tile *tile; + + subpixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, + 8, TileW, TileH); + + gdk_pixbuf_copy_area (pixbuf, x, y, TileW, TileH, + subpixbuf, 0, 0); + + tile = g_slice_new0 (Tile); + + if (pos != 15) + { + tile->actor = clutter_texture_new_from_pixbuf (subpixbuf); + clutter_group_add (CLUTTER_GROUP (group), tile->actor); + clutter_actor_set_position (tile->actor, x, y); + } + else + { + /* blank tile */ + tile->actor = NULL; + BlankTileX = i; + BlankTileY = j; + } + + g_object_unref (subpixbuf); + + tile->orig_pos = pos; + Tiles[j][i] = tile; + + pos++; i++; + } + i=0; j++; + } + + return group; +} + +static void +switch_blank_tile (int i, int j) +{ + Tile *tmp; + ClutterKnot knots[2]; + + knots[0].x = i * TileW; + knots[0].y = j * TileH; + + knots[1].x = BlankTileX * TileW; + knots[1].y = BlankTileY * TileH; + + EffectTimeline = clutter_effect_move (Template, + Tiles[j][i]->actor, + knots, + 2, + NULL, + NULL); + + /* Add a week pointer to returned timeline so we know whilst its + * playing and thus valid. + */ + g_object_add_weak_pointer (G_OBJECT(EffectTimeline), + (gpointer*)&EffectTimeline); + + tmp = Tiles[BlankTileY][BlankTileX]; + Tiles[BlankTileY][BlankTileX] = Tiles[j][i]; + Tiles[j][i] = tmp; + + BlankTileY = j; + BlankTileX = i; +} + +static void +key_press_event_cb (ClutterStage *stage, + ClutterKeyEvent *event, + gpointer user_data) +{ + Tile *tmp, *tmp2; + + if (clutter_key_event_symbol(event) == CLUTTER_q) + clutter_main_quit(); + + /* Do move if there is a move already happening */ + if (EffectTimeline != NULL) + return; + + switch (clutter_key_event_symbol(event)) + { + case CLUTTER_Up: + if (BlankTileY < 3) + switch_blank_tile (BlankTileX, BlankTileY+1); + break; + case CLUTTER_Down: + if (BlankTileY > 0) + switch_blank_tile (BlankTileX, BlankTileY-1); + break; + case CLUTTER_Left: + if (BlankTileX < 3) + switch_blank_tile (BlankTileX+1, BlankTileY); + break; + case CLUTTER_Right: + if (BlankTileX > 0) + switch_blank_tile (BlankTileX-1, BlankTileY); + break; + default: + break; + } +} + +int +main (int argc, char **argv) +{ + GdkPixbuf *pixbuf; + ClutterActor *stage, *group; + ClutterColor bgcolour; + + /* Initiate clutter */ + clutter_init (&argc, &argv); + + /* Setup the stage */ + stage = clutter_stage_get_default (); + g_object_set (stage, "fullscreen", TRUE, NULL); + + clutter_color_parse ("#000000", &bgcolour); + clutter_stage_set_color (CLUTTER_STAGE (stage), &bgcolour); + + /* Create Tiles */ + pixbuf = gdk_pixbuf_new_from_file ("image.jpg", NULL); + group = make_tiles (pixbuf); + + /* Add to stage and center */ + clutter_group_add (CLUTTER_GROUP (stage), group); + clutter_actor_set_position (group, + (clutter_actor_get_width (stage) - clutter_actor_get_width (group)) / 2, + (clutter_actor_get_height (stage) - clutter_actor_get_height (group)) / 2); + + /* Link up event collection */ + g_signal_connect (stage, + "key-press-event", + G_CALLBACK(key_press_event_cb), + NULL); + + /* Template to use for slider animation */ + Template = clutter_effect_template_new (clutter_timeline_new (15, 60), + CLUTTER_ALPHA_RAMP_INC); + + clutter_actor_show_all (stage); + + clutter_main(); +}