mirror of
https://github.com/brl/mutter.git
synced 2024-12-24 12:02:04 +00:00
03358aca6c
The venerable test-actors used the default stage, which meant that
closing the window just stopped the main loop but left the scene intact.
This does not happen with a normal stage created through
clutter_stage_new().
The change happened in 6ed6b2a54b
, in an
unhelpfully named commit that was just supposed to switch to static
colors. My bad.
Anyway, the change led to some assertion failures when closing the stage
in the middle of an animation.
The correct thing to do when using an actor from another object (a
Timeline ::new-frame callback in this case) without owning the instance
is to connect to the ::destroy signal and clean up the pointer to avoid
calling an invalid actor.
309 lines
7.5 KiB
C
309 lines
7.5 KiB
C
#undef CLUTTER_DISABLE_DEPRECATED
|
|
#include <clutter/clutter.h>
|
|
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <glib.h>
|
|
#include <gmodule.h>
|
|
|
|
#define NHANDS 6
|
|
|
|
typedef struct SuperOH
|
|
{
|
|
ClutterActor **hand;
|
|
ClutterActor *bgtex;
|
|
ClutterActor *real_hand;
|
|
ClutterActor *group;
|
|
ClutterActor *stage;
|
|
|
|
gint stage_width;
|
|
gint stage_height;
|
|
gfloat radius;
|
|
|
|
ClutterBehaviour *scaler_1;
|
|
ClutterBehaviour *scaler_2;
|
|
ClutterTimeline *timeline;
|
|
} SuperOH;
|
|
|
|
static gint n_hands = NHANDS;
|
|
|
|
static GOptionEntry super_oh_entries[] = {
|
|
{
|
|
"num-hands", 'n',
|
|
0,
|
|
G_OPTION_ARG_INT, &n_hands,
|
|
"Number of hands", "HANDS"
|
|
},
|
|
{ NULL }
|
|
};
|
|
|
|
static void
|
|
on_group_destroy (ClutterActor *actor,
|
|
SuperOH *oh)
|
|
{
|
|
oh->group = NULL;
|
|
}
|
|
|
|
static void
|
|
on_hand_destroy (ClutterActor *actor,
|
|
SuperOH *oh)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n_hands; i++)
|
|
{
|
|
if (oh->hand[i] == actor)
|
|
oh->hand[i] = NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
on_button_press_event (ClutterActor *actor,
|
|
ClutterEvent *event,
|
|
SuperOH *oh)
|
|
{
|
|
gfloat x, y;
|
|
|
|
clutter_event_get_coords (event, &x, &y);
|
|
|
|
g_print ("*** button press event (button:%d) at %.2f, %.2f on %s ***\n",
|
|
clutter_event_get_button (event),
|
|
x, y,
|
|
clutter_actor_get_name (actor));
|
|
|
|
clutter_actor_hide (actor);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
input_cb (ClutterActor *stage,
|
|
ClutterEvent *event,
|
|
gpointer data)
|
|
{
|
|
SuperOH *oh = data;
|
|
|
|
if (event->type == CLUTTER_KEY_RELEASE)
|
|
{
|
|
g_print ("*** key press event (key:%c) ***\n",
|
|
clutter_event_get_key_symbol (event));
|
|
|
|
if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_q)
|
|
{
|
|
clutter_main_quit ();
|
|
|
|
return TRUE;
|
|
}
|
|
else if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_r)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; i < n_hands; i++)
|
|
{
|
|
if (oh->hand[i] != NULL)
|
|
clutter_actor_show (oh->hand[i]);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Timeline handler */
|
|
static void
|
|
frame_cb (ClutterTimeline *timeline,
|
|
gint msecs,
|
|
gpointer data)
|
|
{
|
|
SuperOH *oh = data;
|
|
gint i;
|
|
float rotation = clutter_timeline_get_progress (timeline) * 360.0f;
|
|
|
|
/* Rotate everything clockwise about stage center*/
|
|
if (oh->group != NULL)
|
|
clutter_actor_set_rotation (oh->group,
|
|
CLUTTER_Z_AXIS,
|
|
rotation,
|
|
oh->stage_width / 2,
|
|
oh->stage_height / 2,
|
|
0);
|
|
|
|
for (i = 0; i < n_hands; i++)
|
|
{
|
|
/* Rotate each hand around there centers - to get this we need
|
|
* to take into account any scaling.
|
|
*/
|
|
if (oh->hand[i] != NULL)
|
|
clutter_actor_set_rotation (oh->hand[i],
|
|
CLUTTER_Z_AXIS,
|
|
-6.0 * rotation,
|
|
0, 0, 0);
|
|
}
|
|
}
|
|
|
|
static gdouble
|
|
my_sine_wave (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
|
|
gdouble progress = clutter_timeline_get_progress (timeline);
|
|
|
|
return sin (progress * G_PI);
|
|
}
|
|
|
|
G_MODULE_EXPORT int
|
|
test_actors_main (int argc, char *argv[])
|
|
{
|
|
ClutterAlpha *alpha;
|
|
ClutterActor *stage;
|
|
SuperOH *oh;
|
|
gint i;
|
|
GError *error;
|
|
ClutterActor *real_hand;
|
|
gchar *file;
|
|
|
|
error = NULL;
|
|
|
|
clutter_init_with_args (&argc, &argv,
|
|
NULL,
|
|
super_oh_entries,
|
|
NULL,
|
|
&error);
|
|
if (error)
|
|
{
|
|
g_warning ("Unable to initialise Clutter:\n%s",
|
|
error->message);
|
|
g_error_free (error);
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
stage = clutter_stage_new ();
|
|
clutter_actor_set_size (stage, 800, 600);
|
|
clutter_actor_set_name (stage, "Default Stage");
|
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
|
|
|
clutter_stage_set_title (CLUTTER_STAGE (stage), "Actors");
|
|
clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_LightSkyBlue);
|
|
clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
|
|
|
|
oh = g_new (SuperOH, 1);
|
|
oh->stage = stage;
|
|
|
|
/* Create a timeline to manage animation */
|
|
oh->timeline = clutter_timeline_new (6000);
|
|
clutter_timeline_set_loop (oh->timeline, TRUE);
|
|
|
|
/* fire a callback for frame change */
|
|
g_signal_connect (oh->timeline, "new-frame", G_CALLBACK (frame_cb), oh);
|
|
|
|
/* Set up some behaviours to handle scaling */
|
|
alpha = clutter_alpha_new_with_func (oh->timeline, my_sine_wave, NULL, NULL);
|
|
|
|
oh->scaler_1 = clutter_behaviour_scale_new (alpha, 0.5, 0.5, 1.0, 1.0);
|
|
oh->scaler_2 = clutter_behaviour_scale_new (alpha, 1.0, 1.0, 0.5, 0.5);
|
|
|
|
file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL);
|
|
real_hand = clutter_texture_new_from_file (file, &error);
|
|
if (real_hand == NULL)
|
|
g_error ("image load failed: %s", error->message);
|
|
|
|
g_free (file);
|
|
|
|
/* create a new group to hold multiple actors in a group */
|
|
oh->group = clutter_group_new();
|
|
clutter_actor_set_name (oh->group, "Group");
|
|
g_signal_connect (oh->group, "destroy", G_CALLBACK (on_group_destroy), oh);
|
|
|
|
oh->hand = g_new (ClutterActor*, n_hands);
|
|
|
|
oh->stage_width = clutter_actor_get_width (stage);
|
|
oh->stage_height = clutter_actor_get_height (stage);
|
|
oh->radius = (oh->stage_width + oh->stage_height)
|
|
/ n_hands;
|
|
|
|
for (i = 0; i < n_hands; i++)
|
|
{
|
|
gint x, y, w, h;
|
|
|
|
if (i == 0)
|
|
{
|
|
oh->hand[i] = real_hand;
|
|
clutter_actor_set_name (oh->hand[i], "Real Hand");
|
|
}
|
|
else
|
|
{
|
|
oh->hand[i] = clutter_clone_new (real_hand);
|
|
clutter_actor_set_name (oh->hand[i], "Clone Hand");
|
|
}
|
|
|
|
clutter_actor_set_reactive (oh->hand[i], TRUE);
|
|
|
|
clutter_actor_set_size (oh->hand[i], 200, 213);
|
|
|
|
/* Place around a circle */
|
|
w = clutter_actor_get_width (oh->hand[i]);
|
|
h = clutter_actor_get_height (oh->hand[i]);
|
|
|
|
x = oh->stage_width / 2
|
|
+ oh->radius
|
|
* cos (i * G_PI / (n_hands / 2))
|
|
- w / 2;
|
|
|
|
y = oh->stage_height / 2
|
|
+ oh->radius
|
|
* sin (i * G_PI / (n_hands / 2))
|
|
- h / 2;
|
|
|
|
clutter_actor_set_position (oh->hand[i], x, y);
|
|
|
|
clutter_actor_move_anchor_point_from_gravity (oh->hand[i],
|
|
CLUTTER_GRAVITY_CENTER);
|
|
|
|
/* Add to our group group */
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (oh->group), oh->hand[i]);
|
|
|
|
g_signal_connect (oh->hand[i], "button-press-event",
|
|
G_CALLBACK (on_button_press_event),
|
|
oh);
|
|
|
|
g_signal_connect (oh->hand[i], "destroy",
|
|
G_CALLBACK (on_hand_destroy),
|
|
oh);
|
|
|
|
if (i % 2)
|
|
clutter_behaviour_apply (oh->scaler_1, oh->hand[i]);
|
|
else
|
|
clutter_behaviour_apply (oh->scaler_2, oh->hand[i]);
|
|
}
|
|
|
|
/* Add the group to the stage */
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), oh->group);
|
|
|
|
/* Show everying */
|
|
clutter_actor_show (stage);
|
|
|
|
g_signal_connect (stage, "key-release-event",
|
|
G_CALLBACK (input_cb),
|
|
oh);
|
|
|
|
/* and start it */
|
|
clutter_timeline_start (oh->timeline);
|
|
|
|
clutter_main ();
|
|
|
|
clutter_timeline_stop (oh->timeline);
|
|
|
|
/* clean up */
|
|
g_object_unref (oh->scaler_1);
|
|
g_object_unref (oh->scaler_2);
|
|
g_object_unref (oh->timeline);
|
|
g_free (oh->hand);
|
|
g_free (oh);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|