+2007-08-24 Matthew Allum <mallum@openedhand.com>
+
+ * clutter/clutter-actor.c:
+ * clutter/clutter-event.h:
+ * clutter/clutter-main.c:
+ * clutter/clutter-stage.c:
+ * clutter/clutter-stage.h:
+ * clutter/glx/clutter-backend-glx.c:
+ * clutter/glx/clutter-backend-glx.h:
+ * clutter/glx/clutter-event-glx.c:
+ * clutter/glx/clutter-stage-glx.c:
+ * clutter/glx/clutter-stage-glx.h:
+ Add initial support for stage state events.
+ Fix fullscreening for an already mapped stage.
+
+ * tests/test-events.c:
+ Print out info from the above. Blue button now toggles
+ fullscreen.
+
+ * clutter/clutter-effect.c:
+ * clutter/clutter-effect.h:
+ Add a setting for templates to ref or clone underlying
+ timelines. (As to improve sync issues like those in foofone)
+
+ * tests/test-timeline.c:
+ Also add completed signals.
+
+ * clutter/cogl/gles/cogl.c: (cogl_texture_image_2d):
+ * configure.ac:
+ Forward port from stable branch. RGB Image fixes gles
+ and check for lower case libgles_cm.
+
+
2007-08-24 Tomas Frydrych <tf@openedhand.com>
* clutter/clutter-actor.c:
signal_num = MOTION_EVENT;
break;
case CLUTTER_DELETE:
- signal_num = -1;
- break;
case CLUTTER_DESTROY_NOTIFY:
- signal_num = -1;
- break;
case CLUTTER_CLIENT_MESSAGE:
- signal_num = -1;
- break;
default:
+ signal_num = -1;
break;
}
struct _ClutterEffectTemplatePrivate
{
ClutterTimeline *timeline;
+ gboolean do_clone;
ClutterAlphaFunc alpha_func;
gpointer alpha_data;
PROP_ALPHA_FUNC,
PROP_TIMELINE,
+ PROP_DO_CLONE
};
static void
case PROP_TIMELINE:
priv->timeline = g_value_get_object (value);
g_object_ref(priv->timeline);
+ case PROP_DO_CLONE:
+ clutter_effect_template_set_timeline_clone (template,
+ g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
case PROP_TIMELINE:
g_value_set_object (value, priv->timeline);
break;
+ case PROP_DO_CLONE:
+ g_value_set_boolean (value, priv->do_clone);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
CLUTTER_TYPE_TIMELINE,
G_PARAM_CONSTRUCT_ONLY |
CLUTTER_PARAM_READWRITE));
+
+ /**
+ * ClutterEffectTemplate:clone:
+ *
+ * Controls if effects should clone or reference the templated timeline
+ *
+ * Since: 0.6
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_DO_CLONE,
+ g_param_spec_boolean ("clone",
+ "Clone",
+ "controls if effects should clone or reference the templated timeline",
+ TRUE,
+ G_PARAM_CONSTRUCT |
+ CLUTTER_PARAM_READWRITE));
+
}
static void
clutter_effect_template_init (ClutterEffectTemplate *self)
{
self->priv = EFFECT_TEMPLATE_PRIVATE (self);
+ self->priv->do_clone = TRUE;
}
static void
}
/**
+ * clutter_effect_template_set_timeline_clone:
+ * @template_: A #ClutterEffectTemplate
+ * @setting: A boolean indicating if effects should clone the timeline.
+ *
+ * Sets if effects using this template should make a copy of the
+ * templates timeline (default) or reference the effects timeline.
+ *
+ * Since: 0.6
+ */
+void
+clutter_effect_template_set_timeline_clone (ClutterEffectTemplate *template_,
+ gboolean setting)
+{
+ template_->priv->do_clone = setting;
+}
+
+/**
+ * clutter_effect_template_get_timeline_clone:
+ * @template: A #ClutterEffectTemplate
+ *
+ *
+ *
+ * Return value: TRUE if the templates timeline is to be cloned.
+ *
+ * Since: 0.6
+ */
+gboolean
+clutter_effect_template_get_timeline_clone (ClutterEffectTemplate *template_)
+{
+ return template_->priv->do_clone;
+}
+
+
+/**
* clutter_effect_template_new:
* @timeline: A #ClutterTimeline for the template (will be cloned)
* @alpha_func: An alpha func to use for the template.
*
* Since: 0.4
*/
-ClutterEffectTemplate *
+ClutterEffectTemplate*
clutter_effect_template_new (ClutterTimeline *timeline,
ClutterAlphaFunc alpha_func)
{
c->template = template;
c->actor = actor;
- c->timeline = clutter_timeline_clone (priv->timeline);
+
+ if (clutter_effect_template_get_timeline_clone (template))
+ c->timeline = clutter_timeline_clone (priv->timeline);
+ else
+ {
+ c->timeline = priv->timeline;
+ g_object_ref (priv->timeline);
+ }
+
c->alpha = clutter_alpha_new_full (c->timeline,
priv->alpha_func,
priv->alpha_data,
gpointer user_data,
GDestroyNotify notify);
+void clutter_effect_template_set_timeline_clone (ClutterEffectTemplate *template_,
+ gboolean setting);
+gboolean clutter_effect_template_get_timeline_clone (ClutterEffectTemplate *template_);
+
+
/*
* Clutter effects
*/
CLUTTER_STAGE_STATE_OFFSCREEN = (1<<2),
CLUTTER_STAGE_STATE_POINTER_ENTER = (1<<3),
CLUTTER_STAGE_STATE_POINTER_LEAVE = (1<<4),
- CLUTTER_STAGE_STATE_FOCUS_ACTIVATE = (1<<5),
- CLUTTER_STAGE_STATE_FOCUS_DEACTIVATE = (1<<6),
+ CLUTTER_STAGE_STATE_ACTIVATED = (1<<5),
} ClutterStageState;
typedef union _ClutterEvent ClutterEvent;
}
break;
case CLUTTER_STAGE_STATE:
- /* FIXME: fullscreen / focus / mouse - forward to stage */
+ /* fullscreen / focus - forward to stage */
+ clutter_stage_event (CLUTTER_STAGE(stage), event);
break;
case CLUTTER_CLIENT_MESSAGE:
break;
enum
{
- STAGE_STATE_EVENT,
- ACTIVATE_STAGE,
- DEACTIVATE_STAGE,
+ FULLSCREEN,
+ UNFULLSCREEN,
+ ACTIVATE,
+ DEACTIVATE,
LAST_SIGNAL
};
"Stage Title",
NULL,
CLUTTER_PARAM_READWRITE));
+
+ /**
+ * ClutterStage::fullscreen
+ * @stage: the stage which was fullscreened
+ *
+ * The ::fullscreen signal is emitted when the stage is made fullscreen.
+ *
+ * Since: 0.6
+ */
+ stage_signals[FULLSCREEN] =
+ g_signal_new ("fullscreen",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterStageClass, fullscreen),
+ NULL, NULL,
+ clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * ClutterStage::unfullscreen
+ * @stage: the stage which has left a fullscreen state.
+ *
+ * The ::unfullscreen signal is emitted when the stage leaves a fullscreen
+ * state.
+ *
+ * Since: 0.6
+ */
+ stage_signals[UNFULLSCREEN] =
+ g_signal_new ("unfullscreen",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterStageClass, unfullscreen),
+ NULL, NULL,
+ clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+
+ /**
+ * ClutterStage::activate
+ * @stage: the stage which was activated
+ *
+ * The ::activate signal is emitted when the stage recieves key focus
+ * from the underlying window system.
+ *
+ * Since: 0.6
+ */
+ stage_signals[ACTIVATE] =
+ g_signal_new ("activate",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterStageClass, activate),
+ NULL, NULL,
+ clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * ClutterStage::deactivate
+ * @stage: the stage which was deactivated
+ *
+ * The ::activate signal is emitted when the stage loses key focus
+ * from the underlying window system.
+ *
+ * Since: 0.6
+ */
+ stage_signals[DEACTIVATE] =
+ g_signal_new ("deactivate",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterStageClass, deactivate),
+ NULL, NULL,
+ clutter_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate));
}
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
- /* FIXME: Handle StageState Changes and Delete events */
+ if (event->type == CLUTTER_DELETE)
+ return TRUE;
+
+ if (event->type != CLUTTER_STAGE_STATE)
+ return FALSE;
+
+ /* emit raw event */
+ clutter_actor_event (CLUTTER_ACTOR(stage), event);
+
+ if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
+ {
+ if (event->stage_state.new_state & CLUTTER_STAGE_STATE_FULLSCREEN)
+ g_signal_emit (stage, stage_signals[FULLSCREEN], 0);
+ else
+ g_signal_emit (stage, stage_signals[UNFULLSCREEN], 0);
+ }
+
+ if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_ACTIVATED)
+ {
+ if (event->stage_state.new_state & CLUTTER_STAGE_STATE_ACTIVATED)
+ g_signal_emit (stage, stage_signals[ACTIVATE], 0);
+ else
+ g_signal_emit (stage, stage_signals[DEACTIVATE], 0);
+ }
return TRUE;
}
gboolean value);
/* events */
- void (* stage_state_event) (ClutterStage *stage,
- ClutterStageStateEvent *event);
+ void (* fullscreen) (ClutterStage *stage);
+ void (* unfullscreen) (ClutterStage *stage);
+ void (* activate) (ClutterStage *stage);
+ void (* deactivate) (ClutterStage *stage);
/*< private >*/
/* padding for future expansion */
const guchar* pixels)
{
GE( glTexImage2D (target,
- 0, /* No mipmap support as yet */
- internal_format,
+ 0,
+ format, /* HACK: For gles we set the internal_format equal
+ * to the pixel format. This is for RGB data (i.e
+ * jpgs) which seem to need a matching internal
+ * format rather than RGBA (which is used by GL)
+ *.
+ * This fix isn't ideal..
+ */
width,
height,
- 0, /* 0 pixel border */
+ 0,
format,
type,
pixels) );
if (clutter_synchronise)
XSynchronize (backend_glx->xdpy, True);
+
+ backend_glx->atom_WM_STATE
+ = XInternAtom (backend_glx->xdpy, "_NET_WM_STATE", False);
+ backend_glx->atom_WM_STATE_FULLSCREEN
+ = XInternAtom (backend_glx->xdpy, "_NET_WM_STATE_FULLSCREEN", False);
}
g_free (clutter_display_name);
SwapIntervalProc swap_interval;
gint dri_fd;
ClutterGLXVBlankType vblank_type;
+
+ /* props */
+ Atom atom_WM_STATE;
+ Atom atom_WM_STATE_FULLSCREEN;
};
struct _ClutterBackendGLXClass
XEvent *xevent)
{
ClutterBackendGLX *backend_glx;
- ClutterStage *stage;
- gboolean res;
- Window xwindow, stage_xwindow;
+ ClutterStageGLX *stage_glx;
+ ClutterStage *stage;
+ gboolean res;
+ Window xwindow, stage_xwindow;
- backend_glx = CLUTTER_BACKEND_GLX (backend);
- stage = CLUTTER_STAGE (_clutter_backend_get_stage (backend));
- stage_xwindow = clutter_glx_get_stage_window (stage);
+ backend_glx = CLUTTER_BACKEND_GLX (backend);
+ stage = CLUTTER_STAGE (_clutter_backend_get_stage (backend));
+ stage_glx = CLUTTER_STAGE_GLX (stage);
+ stage_xwindow = clutter_glx_get_stage_window (stage);
xwindow = xevent->xany.window;
if (xwindow == None)
switch (xevent->type)
{
+ case ConfigureNotify:
+ if (xevent->xconfigure.width
+ != clutter_actor_get_width (CLUTTER_ACTOR (stage))
+ ||
+ xevent->xconfigure.height
+ != clutter_actor_get_height (CLUTTER_ACTOR (stage)))
+ clutter_actor_set_size (CLUTTER_ACTOR (stage),
+ xevent->xconfigure.width,
+ xevent->xconfigure.height);
+ res = FALSE;
+ break;
+ case PropertyNotify:
+ {
+ if (xevent->xproperty.atom == backend_glx->atom_WM_STATE)
+ {
+ Atom type;
+ gint format;
+ gulong nitems, bytes_after;
+ guchar *data = NULL;
+ Atom *atoms = NULL;
+ gulong i;
+ gboolean fullscreen_set = FALSE;
+
+ clutter_glx_trap_x_errors ();
+ XGetWindowProperty (backend_glx->xdpy,
+ stage_xwindow,
+ backend_glx->atom_WM_STATE,
+ 0, G_MAXLONG,
+ False, XA_ATOM,
+ &type, &format, &nitems,
+ &bytes_after, &data);
+ clutter_glx_untrap_x_errors ();
+
+ if (type != None && data != NULL)
+ {
+ atoms = (Atom *)data;
+
+ i = 0;
+ while (i < nitems)
+ {
+ if (atoms[i] == backend_glx->atom_WM_STATE_FULLSCREEN)
+ fullscreen_set = TRUE;
+ i++;
+ }
+
+ if (fullscreen_set
+ != !!(stage_glx->state & CLUTTER_STAGE_STATE_FULLSCREEN))
+ {
+ if (fullscreen_set)
+ stage_glx->state |= CLUTTER_STAGE_STATE_FULLSCREEN;
+ else
+ stage_glx->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN;
+
+ event->type = CLUTTER_STAGE_STATE;
+ event->stage_state.changed_mask
+ = CLUTTER_STAGE_STATE_FULLSCREEN;
+ event->stage_state.new_state = stage_glx->state;
+ }
+ else
+ res = FALSE;
+
+ XFree (data);
+ }
+ }
+ else
+ res = FALSE;
+ }
+ break;
+ case FocusIn:
+ if (!(stage_glx->state & CLUTTER_STAGE_STATE_ACTIVATED))
+ {
+ /* TODO: check xevent->xfocus.detail ? */
+ stage_glx->state |= CLUTTER_STAGE_STATE_ACTIVATED;
+
+ event->type = CLUTTER_STAGE_STATE;
+ event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
+ event->stage_state.new_state = stage_glx->state;
+ }
+ else
+ res = FALSE;
+ break;
+ case FocusOut:
+ if (stage_glx->state & CLUTTER_STAGE_STATE_ACTIVATED)
+ {
+ stage_glx->state &= ~CLUTTER_STAGE_STATE_ACTIVATED;
+
+ event->type = CLUTTER_STAGE_STATE;
+ event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
+ event->stage_state.new_state = stage_glx->state;
+ }
+ else
+ res = FALSE;
+ break;
case Expose:
{
XEvent foo_xev;
G_DEFINE_TYPE (ClutterStageGLX, clutter_stage_glx, CLUTTER_TYPE_STAGE);
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+static void
+send_wmspec_change_state (ClutterBackendGLX *backend_glx,
+ Window window,
+ Atom state,
+ gboolean add)
+{
+ XClientMessageEvent xclient;
+
+ memset (&xclient, 0, sizeof (xclient));
+
+ xclient.type = ClientMessage;
+ xclient.window = window;
+ xclient.message_type = backend_glx->atom_WM_STATE;
+ xclient.format = 32;
+
+ xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xclient.data.l[1] = state;
+ xclient.data.l[2] = 0;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (backend_glx->xdpy,
+ DefaultRootWindow(backend_glx->xdpy),
+ False,
+ SubstructureRedirectMask|SubstructureNotifyMask,
+ (XEvent *)&xclient);
+}
+
static void
fix_window_size (ClutterStageGLX *stage_glx)
{
{
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);
+ /* Chain up to set mapped flags */
+ CLUTTER_ACTOR_CLASS (clutter_stage_glx_parent_class)->show(actor);
+
if (stage_glx->xwin)
{
/* Fire off a redraw to avoid flicker on first map.
CLUTTER_NOTE (MISC, "XSelectInput");
XSelectInput (stage_glx->xdpy, stage_glx->xwin,
StructureNotifyMask |
+ FocusChangeMask |
ExposureMask |
/* FIXME: we may want to eplicity enable MotionMask */
PointerMotionMask |
gboolean fullscreen)
{
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage);
- Atom atom_WM_STATE, atom_WM_STATE_FULLSCREEN;
+ ClutterBackendGLX *backend_glx = stage_glx->backend;
- atom_WM_STATE = XInternAtom (stage_glx->xdpy, "_NET_WM_STATE", False);
- atom_WM_STATE_FULLSCREEN = XInternAtom (stage_glx->xdpy,
- "_NET_WM_STATE_FULLSCREEN",
- False);
+ static gboolean was_resizeable = FALSE;
if (fullscreen)
{
- gint width, height;
-
- width = DisplayWidth (stage_glx->xdpy, stage_glx->xscreen);
- height = DisplayHeight (stage_glx->xdpy, stage_glx->xscreen);
-
- clutter_actor_set_size (CLUTTER_ACTOR (stage_glx), width, height);
-
if (stage_glx->xwin != None)
- XChangeProperty (stage_glx->xdpy,
- stage_glx->xwin,
- atom_WM_STATE, XA_ATOM, 32,
- PropModeReplace,
- (unsigned char *) &atom_WM_STATE_FULLSCREEN, 1);
+ {
+ if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_glx)))
+ {
+ gint width, height;
+
+ width = DisplayWidth (stage_glx->xdpy, stage_glx->xscreen);
+ height = DisplayHeight (stage_glx->xdpy, stage_glx->xscreen);
+
+ clutter_actor_set_size (CLUTTER_ACTOR (stage_glx),
+ width, height);
+ /* FIXME: This wont work if we support more states */
+ XChangeProperty
+ (stage_glx->xdpy,
+ stage_glx->xwin,
+ backend_glx->atom_WM_STATE, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char *)&backend_glx->atom_WM_STATE_FULLSCREEN,
+ 1);
+ }
+ else
+ {
+ /* We need to set window user resize-able for metacity at
+ * at least to allow the window to fullscreen *sigh*
+ */
+ if (clutter_stage_get_user_resizable (stage) == TRUE)
+ was_resizeable = TRUE;
+ else
+ clutter_stage_set_user_resizable (stage, TRUE);
+
+ send_wmspec_change_state(backend_glx,
+ stage_glx->xwin,
+ backend_glx->atom_WM_STATE_FULLSCREEN,
+ TRUE);
+ }
+ }
}
else
{
if (stage_glx->xwin != None)
- XDeleteProperty (stage_glx->xdpy, stage_glx->xwin, atom_WM_STATE);
+ {
+ if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_glx)))
+ {
+ /* FIXME: This wont work if we support more states */
+ XDeleteProperty (stage_glx->xdpy,
+ stage_glx->xwin,
+ backend_glx->atom_WM_STATE);
+ }
+ else
+ {
+ clutter_stage_set_user_resizable (stage, TRUE);
+
+ send_wmspec_change_state(backend_glx,
+ stage_glx->xwin,
+ backend_glx->atom_WM_STATE_FULLSCREEN,
+ FALSE);
+
+ /* reset the windows state - this isn't fun - see above */
+ if (!was_resizeable)
+ clutter_stage_set_user_resizable (stage, FALSE);
+
+ was_resizeable = FALSE;
+ }
+ }
}
CLUTTER_SET_PRIVATE_FLAGS(stage, CLUTTER_ACTOR_SYNC_MATRICES);
ClutterBackendGLX *backend;
+ ClutterStageState state;
};
struct _ClutterStageGLXClass
CLUTTER_COGL="gles"
AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering])
- # try for libvincent first
+ # try for libvincent first (though its not so good)
PKG_CHECK_MODULES(EGL, libvincent, HAVE_OGLES=yes, HAVE_OGLES=no)
if test "x$HAVE_OGLES" = "xno"; then
AC_CHECK_HEADERS([GLES/egl.h GLES/gl.h],,
[AC_MSG_ERROR([Unable to locate required GLES headers])])
- AC_CHECK_LIB(GLES_CM, eglInitialize, HAVE_LIBGLES=yes, HAVE_LIBGLES=no)
+ # No libvincent so start checking for upper/lower case libgles_em
+ # The powervr sdk uses lower case.
+ AC_CHECK_LIB(GLES_CM, eglInitialize, HAVE_LIBGLES=yes, HAVE_LIBGLES=no)
if test "x$HAVE_LIBGLES" = "xno"; then
+
+ AC_CHECK_LIB(gles_cm, eglInitialize, HAVE_LIBGLES=yes, HAVE_LIBGLES=no)
+ if test "x$HAVE_LIBGLES" = "xno"; then
AC_MSG_ERROR([GLES library not found and egl backend requested.]);
+ fi
+ EGL_LIBS="-lgles_cm"
+ else
+ EGL_LIBS="-lGLES_CM"
fi
-
- EGL_LIBS="-lGLES_CM"
fi
EGL_LIBS="$EGL_LIBS $X11_LIBS"
#include <clutter/clutter.h>
+gboolean IsFullScreen = FALSE;
+static void
+stage_state_cb (ClutterStage *stage,
+ gpointer data)
+{
+ gchar *detail = (gchar*)data;
+
+ printf("[stage signal] %s\n", detail);
+}
+
+static void
+blue_button_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer data)
+{
+ ClutterActor *stage;
+
+ stage = clutter_stage_get_default ();
+
+ if (IsFullScreen)
+ IsFullScreen = FALSE;
+ else
+ IsFullScreen = TRUE;
+
+ g_object_set (stage, "fullscreen", IsFullScreen, NULL);
+}
void
key_focus_in_cb (ClutterActor *actor,
stage = clutter_stage_get_default ();
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
+ g_signal_connect (stage, "fullscreen",
+ G_CALLBACK (stage_state_cb), "fullscreen");
+ g_signal_connect (stage, "unfullscreen",
+ G_CALLBACK (stage_state_cb), "unfullscreen");
+ g_signal_connect (stage, "activate",
+ G_CALLBACK (stage_state_cb), "activate");
+ g_signal_connect (stage, "deactivate",
+ G_CALLBACK (stage_state_cb), "deactivate");
focus_box = clutter_rectangle_new_with_color (&ncol);
clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
focus_box);
+ g_signal_connect (actor, "button-press-event", G_CALLBACK (blue_button_cb), NULL);
actor = clutter_rectangle_new_with_color (&ncol);
clutter_actor_set_size (actor, 400, 50);
#include <clutter/clutter.h>
static void
+timeline_1_complete (ClutterTimeline *timeline)
+{
+ g_debug ("1: Completed");
+}
+
+static void
+timeline_2_complete (ClutterTimeline *timeline)
+{
+ g_debug ("2: Completed");
+}
+
+static void
+timeline_3_complete (ClutterTimeline *timeline)
+{
+ g_debug ("3: Completed");
+}
+
+static void
timeline_1_new_frame_cb (ClutterTimeline *timeline, gint frame_no)
{
g_debug ("1: Doing frame %d.", frame_no);
clutter_init (&argc, &argv);
- timeline_1 = clutter_timeline_new (100, 50);
+ timeline_1 = clutter_timeline_new (10, 120);
timeline_2 = clutter_timeline_clone (timeline_1);
timeline_3 = clutter_timeline_clone (timeline_1);
g_signal_connect (timeline_1,
"new-frame", G_CALLBACK (timeline_1_new_frame_cb),
NULL);
+ g_signal_connect (timeline_1,
+ "completed", G_CALLBACK (timeline_1_complete),
+ NULL);
+
g_signal_connect (timeline_2,
"new-frame", G_CALLBACK (timeline_2_new_frame_cb),
NULL);
+ g_signal_connect (timeline_2,
+ "completed", G_CALLBACK (timeline_2_complete),
+ NULL);
+
g_signal_connect (timeline_3,
"new-frame", G_CALLBACK (timeline_3_new_frame_cb),
NULL);
+ g_signal_connect (timeline_3,
+ "completed", G_CALLBACK (timeline_3_complete),
+ NULL);
clutter_timeline_start (timeline_1);
clutter_timeline_start (timeline_2);