2006-06-21 Matthew Allum <mallum@openedhand.com>

* clutter/Makefile.am:
        Make sure clutter.h included.
        * clutter/clutter-stage.c: (clutter_stage_realize),
        (clutter_stage_get_actor_at_pos):
        Select for motion events on X window.
        Return highest rather than lowest found actor for actor_at_pos.
        * examples/Makefile.am:
        Remove test-text, remane test video.
        * examples/README:
        Add info about the included examples.
        * examples/test-video.c:
        * examples/video-player.c:
        Redo test-video a little nicer and rename.
        * examples/video-cube.c:
        Fix so it at least 'works' again.
        * examples/test.c:
        Clean up a little.
This commit is contained in:
Matthew Allum 2006-06-21 16:29:28 +00:00
parent 177add8768
commit d61ee2240b
12 changed files with 477 additions and 193 deletions

View File

@ -1,3 +1,23 @@
2006-06-21 Matthew Allum <mallum@openedhand.com>
* clutter/Makefile.am:
Make sure clutter.h included.
* clutter/clutter-stage.c: (clutter_stage_realize),
(clutter_stage_get_actor_at_pos):
Select for motion events on X window.
Return highest rather than lowest found actor for actor_at_pos.
* examples/Makefile.am:
Remove test-text, remane test video.
* examples/README:
Add info about the included examples.
* examples/test-video.c:
* examples/video-player.c:
Redo test-video a little nicer and rename.
* examples/video-cube.c:
Fix so it at least 'works' again.
* examples/test.c:
Clean up a little.
2006-06-21 Ross Burton <ross@openedhand.com> 2006-06-21 Ross Burton <ross@openedhand.com>
* Makefile.am: * Makefile.am:

View File

@ -5,22 +5,22 @@ GLIB_MKENUMS=`pkg-config --variable=glib_mkenums glib-2.0`
BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES) BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES)
source_h = \ source_h = clutter.h \
$(srcdir)/clutter-keysyms.h \ clutter-keysyms.h \
$(srcdir)/clutter-util.h \ clutter-util.h \
$(srcdir)/clutter-media.h \ clutter-media.h \
$(srcdir)/clutter-event.h \ clutter-event.h \
$(srcdir)/clutter-color.h \ clutter-color.h \
$(srcdir)/clutter-timeline.h \ clutter-timeline.h \
$(srcdir)/clutter-actor.h \ clutter-actor.h \
$(srcdir)/clutter-group.h \ clutter-group.h \
$(srcdir)/clutter-stage.h \ clutter-stage.h \
$(srcdir)/clutter-rectangle.h \ clutter-rectangle.h \
$(srcdir)/clutter-texture.h \ clutter-texture.h \
$(srcdir)/clutter-clone-texture.h \ clutter-clone-texture.h \
$(srcdir)/clutter-video-texture.h \ clutter-video-texture.h \
$(srcdir)/clutter-label.h \ clutter-label.h \
$(srcdir)/clutter-main.h clutter-main.h
clutter-marshal.h: clutter-marshal.list clutter-marshal.h: clutter-marshal.list
( $(GLIB_GENMARSHAL) --prefix=clutter_marshal \ ( $(GLIB_GENMARSHAL) --prefix=clutter_marshal \

View File

@ -403,6 +403,8 @@ clutter_stage_realize (ClutterActor *actor)
priv->xwin, priv->xwin,
StructureNotifyMask StructureNotifyMask
|ExposureMask |ExposureMask
/* FIXME: we may want to eplicity enable MotionMask */
|PointerMotionMask
|KeyPressMask |KeyPressMask
|KeyReleaseMask |KeyReleaseMask
|ButtonPressMask |ButtonPressMask
@ -1058,7 +1060,8 @@ clutter_stage_get_actor_at_pos (ClutterStage *stage,
g_print ("Hit at %i\n", buff[i * 4 + 3]); g_print ("Hit at %i\n", buff[i * 4 + 3]);
#endif #endif
found = clutter_group_find_child_by_id (CLUTTER_GROUP (stage), buff[3]); found = clutter_group_find_child_by_id (CLUTTER_GROUP (stage),
buff[(hits-1) * 4 + 3]);
} }
sync_gl_viewport (stage); sync_gl_viewport (stage);

View File

@ -1,4 +1,4 @@
noinst_PROGRAMS = test test-video video-cube test-text super-oh noinst_PROGRAMS = test video-player video-cube super-oh
INCLUDES = -I$(top_srcdir)/ INCLUDES = -I$(top_srcdir)/
@ -10,9 +10,9 @@ test_LDFLAGS = \
$(GCONF_LIBS) \ $(GCONF_LIBS) \
$(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la $(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la
test_video_SOURCES = test-video.c video_player_SOURCES = video-player.c
test_video_CFLAGS = $(CLUTTER_CFLAGS) $(GST_CFLAGS) $(GCONF_CFLAGS) video_player_CFLAGS = $(CLUTTER_CFLAGS) $(GST_CFLAGS) $(GCONF_CFLAGS)
test_video_LDFLAGS = \ video_player_LDFLAGS = \
$(CLUTTER_LIBS) \ $(CLUTTER_LIBS) \
$(GST_LIBS) \ $(GST_LIBS) \
$(GCONF_LIBS) \ $(GCONF_LIBS) \
@ -26,14 +26,6 @@ video_cube_LDFLAGS = \
$(GCONF_LIBS) \ $(GCONF_LIBS) \
$(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la $(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la
test_text_SOURCES = test-text.c
test_text_CFLAGS = $(CLUTTER_CFLAGS) $(GST_CFLAGS) $(GCONF_CFLAGS)
test_text_LDFLAGS = \
$(CLUTTER_LIBS) \
$(GST_LIBS) \
$(GCONF_LIBS) \
$(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la
super_oh_SOURCES = super-oh.c super_oh_SOURCES = super-oh.c
super_oh_CFLAGS = $(CLUTTER_CFLAGS) $(GST_CFLAGS) $(GCONF_CFLAGS) super_oh_CFLAGS = $(CLUTTER_CFLAGS) $(GST_CFLAGS) $(GCONF_CFLAGS)
super_oh_LDFLAGS = \ super_oh_LDFLAGS = \
@ -42,4 +34,9 @@ super_oh_LDFLAGS = \
$(GCONF_LIBS) \ $(GCONF_LIBS) \
$(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la $(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la
EXTRA_DIST = redhand.png clutter-logo-800x600.png EXTRA_DIST = redhand.png \
clutter-logo-800x600.png \
media-actions-pause.png \
media-actions-start.png \
vid-panel.png \
README

37
examples/README Normal file
View File

@ -0,0 +1,37 @@
examples/
===
This directory contains a number of simple hacks come tests come
examples come clutter demos.
There are:
o test
Lots of randomness. Scratchpad to test new features.
o test.py
Like above but more randomness in python.
o super-oh
Spinning OH logos. Click to dissapear.
o video-player.
Simple fullscreen video player.
- Move mouse to see controls.
- Hit q or Esc to exit, p to toggle pause state.
- Has some useless cheesy effects built in.
- Hit e to flip the video.
o video-cube.
Pure evil. A nasty hack to do a video cube with clutter.
Could be done much nicer.
Also see http://svn.o-hand.com/repos/misc/trunk/opt for a simple
clutter based presentation program.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -1,158 +0,0 @@
#include <clutter/clutter.h>
ClutterActor *rect; /* um... */
void input_cb (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
ClutterVideoTexture *vtex = CLUTTER_VIDEO_TEXTURE(user_data);
static gint paused = 0;
if (event->type == CLUTTER_KEY_RELEASE)
{
if (paused)
{
clutter_media_set_playing (CLUTTER_MEDIA(vtex), TRUE);
paused = 0;
}
else
{
clutter_media_set_playing (CLUTTER_MEDIA(vtex), FALSE);
paused = 1;
}
}
}
void
size_change (ClutterTexture *texture,
gint width,
gint height,
gpointer user_data)
{
ClutterActor *stage;
ClutterGeometry stage_geom;
gint vid_width, vid_height, new_y, new_height;
stage = clutter_stage_get_default ();
clutter_actor_get_geometry (stage, &stage_geom);
clutter_texture_get_base_size (texture, &vid_width, &vid_height);
printf("*** vid : %ix%i stage %ix%i ***\n",
vid_width, vid_height, stage_geom.width, stage_geom.height);
new_height = ( vid_height * stage_geom.width ) / vid_width;
new_y = (stage_geom.height - new_height) / 2;
clutter_actor_set_position (CLUTTER_ACTOR (texture), 0, new_y);
clutter_actor_set_size (CLUTTER_ACTOR (texture),
stage_geom.width,
new_height);
// clutter_actor_set_opacity (CLUTTER_ACTOR (texture), 50);
printf("*** Pos set to +%i+%i , %ix%i ***\n",
0, new_y, stage_geom.width, new_height);
}
void
tick (GObject *object,
GParamSpec *pspec,
ClutterLabel *label)
{
ClutterVideoTexture *vtex;
gint w, h, position, duration;
gchar buf[256];
vtex = CLUTTER_VIDEO_TEXTURE(object);
position = clutter_media_get_position (CLUTTER_MEDIA(vtex));
duration = clutter_media_get_duration (CLUTTER_MEDIA(vtex));
g_snprintf(buf, 256, "%i / %i", position, duration);
clutter_label_set_text (label, buf);
clutter_texture_get_base_size (CLUTTER_TEXTURE(label), &w, &h);
clutter_actor_set_size(rect, w+10, h+10);
}
int
main (int argc, char *argv[])
{
ClutterActor *label, *vtexture, *ctexture;
ClutterActor *stage;
ClutterColor rect_color = { 0xde, 0xde, 0xdf, 0xaa };
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 };
GError *err = NULL;
if (argc < 2)
g_error("%s <video file>", argv[0]);
clutter_init (&argc, &argv);
vtexture = clutter_video_texture_new ();
clutter_media_set_filename(CLUTTER_MEDIA(vtexture), argv[1]);
stage = clutter_stage_get_default ();
/* Broken..
g_object_set(vtexture, "repeat-x", TRUE, NULL);
g_object_set(vtexture, "repeat-y", TRUE, NULL);
*/
if (vtexture == NULL || err != NULL)
{
g_error("failed to create vtexture, err: %s", err->message);
}
label = clutter_label_new_with_text ("Sans Bold 24", "Loading...");
clutter_actor_set_position(label, 10, 10);
rect = clutter_rectangle_new_with_color (&rect_color);
clutter_actor_set_size(rect, 0, 0);
clutter_actor_set_position(rect, 5, 5);
ctexture = clutter_clone_texture_new (CLUTTER_TEXTURE(vtexture));
clutter_actor_set_opacity (CLUTTER_ACTOR (ctexture), 100);
clutter_actor_set_size (ctexture, 640, 50);
clutter_actor_set_position (ctexture, 0, 430);
clutter_group_add_many (CLUTTER_GROUP (stage),
vtexture, rect, label, ctexture, NULL);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
g_signal_connect (stage, "input-event",
G_CALLBACK (input_cb),
vtexture);
clutter_group_show_all (CLUTTER_GROUP (stage));
clutter_media_set_playing (CLUTTER_MEDIA(vtexture), TRUE);
g_signal_connect (CLUTTER_MEDIA(vtexture),
"notify::position",
G_CALLBACK (tick),
label);
g_object_set (G_OBJECT(vtexture), "sync-size", FALSE, NULL);
g_signal_connect (CLUTTER_TEXTURE(vtexture),
"size-change",
G_CALLBACK (size_change), NULL);
clutter_main();
g_object_unref (stage);
return 0;
}

View File

@ -91,6 +91,7 @@ main (int argc, char *argv[])
para = clutter_label_new_with_text ("Sans 24", PARA_TEXT); para = clutter_label_new_with_text ("Sans 24", PARA_TEXT);
clutter_actor_set_position(para, 10, 10); clutter_actor_set_position(para, 10, 10);
clutter_label_set_text_extents (CLUTTER_LABEL(para), 200, 0);
clutter_group_add (CLUTTER_GROUP (stage), texture); clutter_group_add (CLUTTER_GROUP (stage), texture);
clutter_group_add (CLUTTER_GROUP (stage), label); clutter_group_add (CLUTTER_GROUP (stage), label);

BIN
examples/vid-panel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -228,7 +228,7 @@ int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
ClutterActor *label, *texture, *vtexture; ClutterActor *label, *texture, *vtexture;
ClutterActor *stage = clutter_stage_get_default (); ClutterActor *stage;
GdkPixbuf *pixbuf; GdkPixbuf *pixbuf;
GError *err = NULL; GError *err = NULL;
@ -237,6 +237,8 @@ main (int argc, char *argv[])
clutter_init (&argc, &argv); clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
pixbuf = gdk_pixbuf_new_from_file ("clutter-logo-800x600.png", NULL); pixbuf = gdk_pixbuf_new_from_file ("clutter-logo-800x600.png", NULL);
if (!pixbuf) if (!pixbuf)
@ -254,7 +256,6 @@ main (int argc, char *argv[])
g_error("failed to create vtexture, err: %s", err->message); g_error("failed to create vtexture, err: %s", err->message);
} }
clutter_media_set_filename (CLUTTER_MEDIA(vtexture), argv[1]); clutter_media_set_filename (CLUTTER_MEDIA(vtexture), argv[1]);
clutter_group_add (CLUTTER_GROUP (stage), texture); clutter_group_add (CLUTTER_GROUP (stage), texture);

383
examples/video-player.c Normal file
View File

@ -0,0 +1,383 @@
#include <clutter/clutter.h>
#define SEEK_H 20
#define SEEK_W 690
typedef struct VideoApp
{
ClutterActor *vtexture;
ClutterActor *control, *control_bg, *control_play, *control_pause,
*control_seek1, *control_seek2, *control_seekbar, *control_label;
gboolean controls_showing, paused;
guint controls_timeout;
ClutterTimeline *controls_tl, *effect1_tl;
}
VideoApp;
static void
show_controls (VideoApp *app, gboolean vis);
void
control_tl_cb (ClutterTimeline *timeline,
gint frame_num,
VideoApp *app)
{
guint8 opacity;
clutter_group_show_all (CLUTTER_GROUP (app->control));
clutter_actor_hide (app->paused ? app->control_pause : app->control_play);
clutter_actor_show (app->paused ? app->control_play : app->control_pause);
opacity = ( frame_num * 0xde ) / clutter_timeline_get_n_frames(timeline);
if (!app->controls_showing)
opacity = 0xde - opacity;
clutter_actor_set_opacity (app->control, opacity);
}
void
control_tl_complete_cb (ClutterTimeline *timeline,
VideoApp *app)
{
if (!app->controls_showing)
clutter_group_hide_all (CLUTTER_GROUP (app->control));
app->controls_timeout = 0;
}
static gboolean
controls_timeout_cb (VideoApp *app)
{
show_controls (app, FALSE);
return FALSE;
}
static void
show_controls (VideoApp *app, gboolean vis)
{
if (clutter_timeline_is_playing (app->controls_tl))
return;
if (vis == TRUE && app->controls_showing == FALSE)
{
app->controls_showing = TRUE;
clutter_timeline_start (app->controls_tl);
app->controls_timeout = g_timeout_add (5 * 1000,
(GSourceFunc)controls_timeout_cb,
app);
return;
}
if (vis == TRUE && app->controls_showing == TRUE)
{
if (app->controls_timeout)
{
g_source_remove (app->controls_timeout);
app->controls_timeout
= g_timeout_add (5 * 1000,
(GSourceFunc)controls_timeout_cb,
app);
}
return;
}
if (vis == FALSE && app->controls_showing == TRUE)
{
app->controls_showing = FALSE;
clutter_timeline_start (app->controls_tl);
return;
}
}
void
toggle_pause_state (VideoApp *app)
{
if (app->paused)
{
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture),
TRUE);
app->paused = FALSE;
clutter_actor_hide (app->control_play);
clutter_actor_show (app->control_pause);
}
else
{
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture),
FALSE);
app->paused = TRUE;
clutter_actor_hide (app->control_pause);
clutter_actor_show (app->control_play);
}
}
void
input_cb (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
VideoApp *app = (VideoApp*)user_data;
switch (event->type)
{
case CLUTTER_MOTION:
show_controls (app, TRUE);
break;
case CLUTTER_BUTTON_PRESS:
if (app->controls_showing)
{
ClutterActor *actor;
ClutterButtonEvent *bev = (ClutterButtonEvent *) event;
actor
= clutter_stage_get_actor_at_pos
(CLUTTER_STAGE(clutter_stage_get_default()),
bev->x, bev->y);
if (actor == app->control_pause || actor == app->control_play)
{
toggle_pause_state (app);
return;
}
if (actor == app->control_seek1
|| actor == app->control_seek2
|| actor == app->control_seekbar)
{
gint x, y, dist, pos;
clutter_actor_get_abs_position (app->control_seekbar, &x, &y);
dist = bev->x - x;
CLAMP(dist, 0, SEEK_W);
pos = (dist * clutter_media_get_duration
(CLUTTER_MEDIA(app->vtexture))) / SEEK_W;
clutter_media_set_position (CLUTTER_MEDIA(app->vtexture),
pos);
}
}
break;
case CLUTTER_KEY_RELEASE:
{
ClutterKeyEvent* kev = (ClutterKeyEvent *) event;
switch (clutter_key_event_symbol (kev))
{
case CLUTTER_q:
case CLUTTER_Escape:
clutter_main_quit ();
break;
case CLUTTER_e:
if (!clutter_timeline_is_playing (app->effect1_tl))
clutter_timeline_start (app->effect1_tl);
break;
default:
toggle_pause_state (app);
break;
}
}
default:
break;
}
}
void
size_change (ClutterTexture *texture,
gint width,
gint height,
gpointer user_data)
{
ClutterActor *stage;
ClutterGeometry stage_geom;
gint vid_width, vid_height, new_y, new_height;
clutter_texture_get_base_size (texture, &vid_width, &vid_height);
new_height = ( vid_height * CLUTTER_STAGE_WIDTH() ) / vid_width;
new_y = ( CLUTTER_STAGE_HEIGHT() - new_height) / 2;
clutter_actor_set_position (CLUTTER_ACTOR (texture), 0, new_y);
clutter_actor_set_size (CLUTTER_ACTOR (texture),
CLUTTER_STAGE_WIDTH(),
new_height);
}
void
tick (GObject *object,
GParamSpec *pspec,
VideoApp *app)
{
ClutterVideoTexture *vtex;
gint w, h, position, duration, seek_w;
gchar buf[256];
vtex = CLUTTER_VIDEO_TEXTURE(object);
position = clutter_media_get_position (CLUTTER_MEDIA(vtex));
duration = clutter_media_get_duration (CLUTTER_MEDIA(vtex));
if (duration == 0 || position == 0)
return;
clutter_actor_set_size (app->control_seekbar,
(position * SEEK_W) / duration,
SEEK_H);
}
int
effect1_tl_cb (ClutterTimeline *timeline,
gint frame_num,
VideoApp *app)
{
clutter_actor_rotate_y (app->vtexture,
frame_num * 12,
CLUTTER_STAGE_WIDTH()/2,
0);
}
int
main (int argc, char *argv[])
{
VideoApp *app = NULL;
ClutterActor *stage;
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 };
ClutterColor control_color1 = { 73, 74, 77, 0xee };
ClutterColor control_color2 = { 0xcc, 0xcc, 0xcc, 0xff };
GdkPixbuf *pixb;
gint x,y;
if (argc < 2)
g_error("%s <video file>", argv[0]);
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
g_object_set (stage, "fullscreen", TRUE, NULL);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
app = g_new0(VideoApp, 1);
app->vtexture = clutter_video_texture_new ();
if (app->vtexture == NULL)
{
g_error("failed to create vtexture");
}
/* Dont let the underlying pixbuf dictate size */
g_object_set (G_OBJECT(app->vtexture), "sync-size", FALSE, NULL);
/* Handle it ourselves so can scale up for fullscreen better */
g_signal_connect (CLUTTER_TEXTURE(app->vtexture),
"size-change",
G_CALLBACK (size_change), NULL);
/* Load up out video texture */
clutter_media_set_filename(CLUTTER_MEDIA(app->vtexture), argv[1]);
/* Create the control UI */
app->control = clutter_group_new ();
pixb = gdk_pixbuf_new_from_file ("vid-panel.png", NULL);
if (pixb == NULL)
g_error("Unable to load vid-panel.png");
app->control_bg = clutter_texture_new_from_pixbuf (pixb);
pixb = gdk_pixbuf_new_from_file("media-actions-start.png", NULL);
if (pixb == NULL)
g_error("Unable to load media-actions-start.png");
app->control_play = clutter_texture_new_from_pixbuf (pixb);
pixb = gdk_pixbuf_new_from_file("media-actions-pause.png", NULL);
if (pixb == NULL)
g_error("Unable to load media-actions-pause.png");
app->control_pause = clutter_texture_new_from_pixbuf (pixb);
app->control_seek1 = clutter_rectangle_new_with_color (&control_color1);
app->control_seek2 = clutter_rectangle_new_with_color (&control_color2);
app->control_seekbar = clutter_rectangle_new_with_color (&control_color1);
clutter_actor_set_opacity (app->control_seekbar, 0x99);
app->control_label
= clutter_label_new_with_text("Sans Bold 24",
g_path_get_basename(argv[1]));
clutter_label_set_color (CLUTTER_LABEL(app->control_label), &control_color1);
clutter_group_add_many (CLUTTER_GROUP (app->control),
app->control_bg,
app->control_play,
app->control_pause,
app->control_seek1,
app->control_seek2,
app->control_seekbar,
app->control_label,
NULL);
x = (CLUTTER_STAGE_WIDTH() - clutter_actor_get_width(app->control))/2;
y = CLUTTER_STAGE_HEIGHT() - (CLUTTER_STAGE_HEIGHT()/3);
clutter_actor_set_position (app->control, x, y);
clutter_actor_set_opacity (app->control, 0xee);
clutter_actor_set_position (app->control_play, 30, 30);
clutter_actor_set_position (app->control_pause, 30, 30);
clutter_actor_set_size (app->control_seek1, SEEK_W+10, SEEK_H+10);
clutter_actor_set_position (app->control_seek1, 200, 100);
clutter_actor_set_size (app->control_seek2, SEEK_W, SEEK_H);
clutter_actor_set_position (app->control_seek2, 205, 105);
clutter_actor_set_size (app->control_seekbar, 0, SEEK_H);
clutter_actor_set_position (app->control_seekbar, 205, 105);
clutter_actor_set_position (app->control_label, 200, 40);
/* Add control UI to stage */
clutter_group_add_many (CLUTTER_GROUP (stage),
app->vtexture, app->control, NULL);
/* hook up a time line for fading controls */
app->controls_tl = clutter_timeline_new (10, 30);
g_signal_connect (app->controls_tl, "new-frame",
G_CALLBACK (control_tl_cb), app);
g_signal_connect (app->controls_tl, "completed",
G_CALLBACK (control_tl_complete_cb), app);
app->effect1_tl = clutter_timeline_new (30, 90);
g_signal_connect (app->effect1_tl, "new-frame",
G_CALLBACK (effect1_tl_cb), app);
/* Hook up other events */
g_signal_connect (stage, "input-event",
G_CALLBACK (input_cb),
app);
g_signal_connect (CLUTTER_MEDIA(app->vtexture),
"notify::position",
G_CALLBACK (tick),
app);
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture), TRUE);
clutter_group_show_all (CLUTTER_GROUP (stage));
clutter_main();
g_object_unref (stage);
return 0;
}