8c0c2924ae
Front/back seems like the wrong terminology when discussing actors arranged in layers. Top/bottom fits better with Clutter API function names and other recipes, so renamed variables.
183 lines
4.9 KiB
C
183 lines
4.9 KiB
C
/*
|
|
* Simple slideshow application, cycling images between
|
|
* two ClutterTextures
|
|
*
|
|
* Run by passing one or more image paths or directory globs
|
|
* which will pick up image files
|
|
*
|
|
* When running, press any key to go to the next image
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <clutter/clutter.h>
|
|
|
|
static guint stage_side = 600;
|
|
static guint animation_duration_ms = 1500;
|
|
|
|
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
|
|
|
typedef struct {
|
|
ClutterActor *top;
|
|
ClutterActor *bottom;
|
|
ClutterState *transitions;
|
|
GSList *image_paths;
|
|
guint next_image_index;
|
|
} State;
|
|
|
|
static gboolean
|
|
load_next_image (State *app)
|
|
{
|
|
/* don't do anything if already animating */
|
|
ClutterTimeline *timeline = clutter_state_get_timeline (app->transitions);
|
|
|
|
if (clutter_timeline_is_playing (timeline) == 1)
|
|
{
|
|
g_debug ("Animation is running already");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!app->next_image_index)
|
|
app->next_image_index = 0;
|
|
|
|
gpointer next = g_slist_nth_data (app->image_paths, app->next_image_index);
|
|
|
|
if (next == NULL)
|
|
return FALSE;
|
|
|
|
gchar *image_path = (gchar *)next;
|
|
|
|
g_debug ("Loading %s", image_path);
|
|
|
|
CoglHandle *cogl_texture;
|
|
cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (app->top));
|
|
|
|
if (cogl_texture != NULL)
|
|
{
|
|
/* copy the current texture into the background */
|
|
clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (app->bottom), cogl_texture);
|
|
|
|
/* make the bottom opaque and top transparent */
|
|
clutter_state_warp_to_state (app->transitions, "show-bottom");
|
|
}
|
|
|
|
/* load the next image into the top */
|
|
GError *error = NULL;
|
|
clutter_texture_set_from_file (CLUTTER_TEXTURE (app->top),
|
|
image_path,
|
|
&error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("Error loading %s\n%s", image_path, error->message);
|
|
g_error_free (error);
|
|
return FALSE;
|
|
}
|
|
|
|
/* fade in the top texture and fade out the bottom texture */
|
|
clutter_state_set_state (app->transitions, "show-top");
|
|
|
|
app->next_image_index++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_key_pressed_cb (ClutterActor *actor,
|
|
ClutterEvent *event,
|
|
gpointer user_data)
|
|
{
|
|
State *app = (State *)user_data;
|
|
|
|
load_next_image (app);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
if (argc < 2)
|
|
{
|
|
g_print ("Usage: %s <image paths to load>\n", argv[0]);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
State *app = g_new0 (State, 1);
|
|
app->image_paths = NULL;
|
|
|
|
/*
|
|
* NB if your shell globs arguments to this program so argv
|
|
* includes non-image files, they will fail to load and throw errors
|
|
*/
|
|
guint i;
|
|
for (i = 1; i < argc; i++)
|
|
app->image_paths = g_slist_append (app->image_paths, argv[i]);
|
|
|
|
GError *error = NULL;
|
|
|
|
/* UI */
|
|
ClutterActor *stage;
|
|
ClutterLayoutManager *layout;
|
|
ClutterActor *box;
|
|
|
|
clutter_init (&argc, &argv);
|
|
|
|
stage = clutter_stage_get_default ();
|
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
|
clutter_stage_set_title (CLUTTER_STAGE (stage), "cross-fade");
|
|
clutter_actor_set_size (stage, stage_side, stage_side);
|
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
|
|
|
layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
|
|
CLUTTER_BIN_ALIGNMENT_CENTER);
|
|
|
|
box = clutter_box_new (layout);
|
|
clutter_actor_set_size (box, stage_side, stage_side);
|
|
|
|
app->bottom = clutter_texture_new ();
|
|
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->bottom), TRUE);
|
|
|
|
app->top = clutter_texture_new ();
|
|
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (app->top), TRUE);
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (box), app->bottom);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (box), app->top);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
|
|
|
|
/* animations */
|
|
app->transitions = clutter_state_new ();
|
|
clutter_state_set (app->transitions, NULL, "show-top",
|
|
app->top, "opacity", CLUTTER_EASE_IN_CUBIC, 255,
|
|
app->bottom, "opacity", CLUTTER_EASE_IN_CUBIC, 0,
|
|
NULL);
|
|
clutter_state_set (app->transitions, NULL, "show-bottom",
|
|
app->top, "opacity", CLUTTER_LINEAR, 0,
|
|
app->bottom, "opacity", CLUTTER_LINEAR, 255,
|
|
NULL);
|
|
clutter_state_set_duration (app->transitions,
|
|
NULL,
|
|
NULL,
|
|
animation_duration_ms);
|
|
|
|
/* display the next (first) image */
|
|
load_next_image (app);
|
|
|
|
/* key press displays the next image */
|
|
g_signal_connect (stage,
|
|
"key-press-event",
|
|
G_CALLBACK (_key_pressed_cb),
|
|
app);
|
|
|
|
clutter_actor_show (stage);
|
|
|
|
clutter_main ();
|
|
|
|
g_slist_free (app->image_paths);
|
|
g_object_unref (app->transitions);
|
|
g_free (app);
|
|
|
|
if (error != NULL)
|
|
g_error_free (error);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|