Clutter: a beginner's tutorial

Code

There are lots of cool things in Moblin, but Clutter is our #1 most favourite thing of all. Why? If its OpenGL-accelerated, object-oriented, GTK-integrated API isn't enough to convince you, then perhaps its powerful animation framework, easy texture manipulation, and lightning-fast object picking system might.

But one problem with Clutter is a distinct lack of documentation - a lot of folks want to learn how to use it, but the web is somewhat lacking in tutorials right now. We hate to see awesome free software projects without the userbase they deserve, so we spent a few hours putting together a kickstart guide for people who want to get into Clutter in the minimum time.

Intel has been kind enough to donate some cash towards TuxRadar to help keep us off the streets, hence the temporary Moblin adverts you can see next to some of our articles. Linux Format subscribers who use our subscriber-only area will have seen that our next cover feature is devoted to mobile Linux, and from the following issue we're going to be running a tutorial series on Python programming for Moblin.

We had been planning both of those for a while, but, with Intel being so nice and all, we figured we should put together a special primer for those folks looking to try some Moblin technology without waiting to read our upcoming issues.

As you may know, one of the really interesting bits of Moblin is Clutter, Intel's graphics library that is used extensively for front-end things such as the Moblin and Ubuntu Netbook Remix user interfaces. Clutter is easy to learn, easy to use, and quite fun, too. If you've never had the time to try it before, relax: we're going to show you the no-hassle way to get into Clutter coding. And if you want to know more, you could always click the Moblin advert - there's a lot of other material to read if you know where to look.

Please keep in mind this article is just a stop-gap measure to do our part in filling what we see as a documentation hole right now - we are working on a more comprehensive set of Clutter tutorials based around Python, but they are a couple of months away from completion. In the meantime, please accept this humble offering, and have fun with Clutter!

First steps

We're not going to hang around - let's dive right into Clutter without hanging around chattering about theory. First, install the requirements to build Clutter apps - if you're on Debian/Ubuntu you'll want libclutter-1.0.0 and libclutter-1.0-dev, plus GCC of course.

A Clutter window is called a "stage", and we can add objects ("actors") to that stage. When an actor is on a stage, Clutter will handle drawing it for us - we can move it around freely, rotate it or scale it, and Clutter handles it all for us.

We're going to start with something really simple, so you can be sure your system is all set up for development. Copy and paste this code into a file called example.c:

#include <clutter/clutter.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
	clutter_init(&argc, &argv);

	ClutterColor stage_color = { 0, 0, 0, 255 };

	ClutterActor *stage = clutter_stage_get_default();
	clutter_actor_set_size(stage, 512, 512);
	clutter_stage_set_color(CLUTTER_STAGE(stage), &stage_color);
	clutter_actor_show(stage);

	clutter_main();

	return EXIT_SUCCESS;
}

Now run this from the command line:

gcc example.c -o clutterapp `pkg-config clutter-1.0 --cflags --libs`

All being well, there'll be no errors and you can run "./clutterapp" to run your program. If you're using an older distro (eg Ubuntu 9.04) you may find that Clutter 1.0 is not available in your package manager - you'll have to use Clutter 0.9 instead, but be warned that older versions aren't 100% compatible with Clutter 1.0. If you don't want to update and are willing to try continuing without Clutter 1.0 knowing full well you'll have to modify some of the function calls to fit older specifications, modify the pkg-config call to match your version, eg clutter-0.9 for Ubuntu 9.04.

Once you get it working, what you'll see isn't very interesting: a black window will appear, "./clutterapp" will be in its title bar, and, well, not much else. It's not impressive, but it is important: it's proof that you have Clutter all set up and ready to go.

The first Clutter app - it's a big black window, yes, but it's also proof that your development environment is sound.

The first Clutter app - it's a big black window, yes, but it's also proof that your development environment is sound.

Let's just skim over what that program does...

  • clutter_init() fires up Clutter so you can use its functions. We pass in argv and argc because Clutter has some command-line options that can be set. We'll be looking at them later; for now all you need to know is that if a Clutter-specific option is passed in, Clutter automatically adjusts argc and argv so that the option is removed and your app never sees it.
  • ClutterColor is a simple structure holding red, green, blue and alpha values for a colour. You can specify them as values between 0 to 255 is the range, or 0x00 to 0xff if you're more hexadecimally minded. Notice that "Color" is the American spelling, which is cunning really because Clutter also talks about behaviours and spells that with the British spelling. This may hurt your brain.
  • ClutterActor is the most important data type in Clutter, and in this example we're using it to grab the default stage that Clutter creates for us when we called clutter_init(). Technically, a Clutter stage is its own specific data type, but behind the scenes it's just a specialised type of ClutterActor so we can cast it to and from those types as needed.
  • clutter_actor_set_size() sets you, er, set the size of an actor. Nice and easy one, this. Pass your actor (in this case, our stage) as the first parameter, then the X and Y size as the second and third parameters.
  • clutter_stage_set_color sets the colour of the stage. Obviously.
  • clutter_actor_show() shows the actor - this is important, and without it your window (in this example) or your actor won't appear.
  • clutter_main() is the Clutter main event loop - control gets handed off to Clutter at this point, and it will call back to us when something interesting happens. But, as you've seen, nothing of interest happens in this code, which means Clutter will sit around doing nothing until you close the window.

That there is your basic Clutter program - you can use that as a template, adding bits and pieces as you go, and you'll find it easy to create new programs quickly. In fact, that's exactly what we're going to do: we're going to add an actor to the stage.

Clutter provides a selection of actor types for us to work with, but the one we're interested in is called simply ClutterRectangle. As with the stage, we can store a ClutterRectangle as a ClutterActor, so let's do that - put this code immediately before the clutter_actor_show() line from the previous example:

ClutterColor actor_color = { 0, 255, 0, 128 };
ClutterActor *rect = clutter_rectangle_new_with_color(&actor_color);
clutter_actor_set_size(rect, 100, 100);
clutter_actor_set_position(rect, 100, 100);
clutter_container_add_actor(CLUTTER_CONTAINER(stage), rect);
clutter_actor_show(rect);

That creates a new rectangle coloured green (with 50% opacity), gives it a size and position, then adds it to the stage. Note the use of the CLUTTER_STAGE() macro - you'll need to use this to cast our stage actor to the ClutterStage type.

Once the rectangle has been created, we add it to the stage and show it so that it will be drawn by Clutter.

Run the code and you'll find our window has become fractionally more interesting: there's a rectangle in there now! Yes, we know that's a bit boring, but relax - we're working up to something here...

The Clutter window now contains a ClutterRectangle actor, and so is vaguely more interesting.

The Clutter window now contains a ClutterRectangle actor, and so is vaguely more interesting.

Rectangles on demand

We're going to shuffle things around a little so that we can create and position rectangles more easily. So, add this function to your code:

ClutterActor *create_rect(ClutterColor col) {
	ClutterActor *rect = clutter_rectangle_new_with_color(&col);
	clutter_actor_set_size(rect, 256, 128);
	clutter_actor_set_position(rect, 128, 128);
	clutter_container_add_actor(CLUTTER_CONTAINER(stage), rect);
	clutter_actor_show(rect);

	return rect;
}

That will create a rectangle in a given colour, then position it at a specific spot on the screen. To make it work, you need to move the definition of the "stage" somewhere that can be reached from anywhere in the program, and you should also declare space for a rectangle too. So, put these two lines just after the #include statements in your file:

ClutterActor *stage;
ClutterActor *rect1 = NULL;

IMPORTANT: We're not using a header file to define all our functions ahead of time. As a result, you need to make sure you put your functions in your code file in the correct order - if function A calls function B, then function B needs to appear in the file before function A. As create_rect() is going to be used inside main(), it needs to go before main(). We're going to be creating other functions later, and you will need to put them into the code file before they are used.

Now modify your main() method to this:

int main(int argc, char *argv[]) {
	clutter_init(&argc, &argv);

	ClutterColor stage_color = { 0, 0, 0, 255 };

	// use the global stage variable rather than a local one
	stage = clutter_stage_get_default();
	clutter_actor_set_size(stage, 512, 512);
	clutter_stage_set_color(CLUTTER_STAGE(stage), &stage_color);

	// create a rectangle using our new function call
	ClutterColor red = { 255, 0, 0, 128 };
	rect1 = create_rect(red);

	clutter_actor_show(stage);

	clutter_main();

	return EXIT_SUCCESS;
}

Not much has changed there. In fact, if you compile and run your code now, you'll see the same thing as before. But because we're able to create rectangles on demand, we can create several different rectangles of different colours then animate them. Animate? Ah, yes; we haven't mentioned that yet - don't worry, though; it's quite easy.

Animation in Clutter is done using timelines, and each time the animation "ticks" you'll be notified so you can update your scene however you want.

Let's start by modifying the code so that it creates and draws several rectangles. We're not going to include animation at first - let's just get the rectangles working. Modify your variable definitions at the top of your code file to this:

#include <clutter/clutter.h>
#include <stdlib.h>

ClutterActor *stage;
ClutterActor *rect1 = NULL;
ClutterActor *rect2 = NULL;
ClutterActor *rect3 = NULL;
ClutterActor *rect4 = NULL;
ClutterActor *rect5 = NULL;
ClutterActor *rect6 = NULL;

So, we have the stage as before, plus six rectangles. We can create them all in different colours by using the create_rect() method, then modifying the main() method to this:

int main(int argc, char *argv[]) {
	clutter_init(&argc, &argv);

	ClutterColor stage_color = { 0, 0, 0, 255 };

	stage = clutter_stage_get_default();
	clutter_actor_set_size(stage, 512, 512);
	clutter_stage_set_color(CLUTTER_STAGE(stage), &stage_color);

	ClutterColor red = { 255, 0, 0, 128 };
	ClutterColor green = { 0, 255, 0, 128 };
	ClutterColor blue = { 0, 0, 255, 128 };
	ClutterColor yellow = { 255, 255, 0, 128 };
	ClutterColor cyan = { 0, 255, 255, 128 };
	ClutterColor purple = { 255, 0, 255, 128 };

	rect1 = create_rect(red);
	rect2 = create_rect(green);
	rect3 = create_rect(blue);
	rect4 = create_rect(yellow);
	rect5 = create_rect(cyan);
	rect6 = create_rect(purple);
	clutter_actor_show(stage);

	clutter_main();

	return EXIT_SUCCESS;
}

If you compile that code, you won't see six rectangles - you'll see just one. This is because they all exist on the same spot, and so overlap perfectly.

Now for the interesting stuff: let's make those rectangles rotate. First, set up a variable that stores a rotation value - add this to your variable definitions:

gdouble rotation = 0;

And now to create and enable our timeline. To create a timeline, use clutter_timeline_new() and tell Clutter how long the timeline should last for. This value should be expressed in milliseconds, so 1000 means "one second". With the timeline created, we need to hook up the "new-frame" signal to the function we want to be called when we need to update the animation. Finally, we're going to make the timeline loop, then start it running.

Put this into your main() method just after the call to create_rect() for rect6:

ClutterTimeline *timeline = clutter_timeline_new(60);
g_signal_connect(timeline, "new-frame", G_CALLBACK(on_timeline_new_frame), NULL);
clutter_timeline_set_loop(timeline, TRUE); 
clutter_timeline_start(timeline);

The on_timeline_new_frame function is what gets called when Clutter passes control back to us so we can update the scene. This takes very specific parameters, but that's OK - you can drop it in once then copy and paste it afterwards.

What our function is going to do is simple: it's going to add a little bit to the value of "rotation", then use the clutter_actor_set_rotation() function to modify the rotation of each of our rectangles by different amounts. Because we're storing all the rectangles in global variables, they are very easy to reach, so just drop this function into your code:

void on_timeline_new_frame(ClutterTimeline *timeline, gint frame_num, gpointer data) {
	rotation += 0.3;

	clutter_actor_set_rotation(rect1, CLUTTER_Z_AXIS, rotation * 5, 0, 0, 0);
	clutter_actor_set_rotation(rect2, CLUTTER_Z_AXIS, rotation * 4, 0, 0, 0);
	clutter_actor_set_rotation(rect3, CLUTTER_Z_AXIS, rotation * 3, 0, 0, 0);
	clutter_actor_set_rotation(rect4, CLUTTER_Z_AXIS, rotation * 2, 0, 0, 0);
	clutter_actor_set_rotation(rect5, CLUTTER_Z_AXIS, rotation, 0, 0, 0);
	clutter_actor_set_rotation(rect6, CLUTTER_Z_AXIS, rotation * 0.5, 0, 0, 0);
}

You can run the code now if you want, and what you'll see is all the boxes spinning around, with the rotation being calculated from their top-left corners. Before we continue, there are two small changes you should make:

  1. Clutter is very good about automatically freeing up memory of objects that belong to it, but this usually applies only to things that are actually attached to the stage. Our timeline is not attached to the stage, which means you need to free up its memory manually. To do that, add this line immediately after clutter_main() in your main() function:
    g_object_unref(timeline);
    
  2. Rotating around the top-left corner of an object is rarely what you want to do. Instead, it's much nicer to rotate aroud an actor's centre, and we can do that by modifying what Clutter calls the "anchor point" - where an actor is rotated around. Modify your create_rect() function to include this line:
    clutter_actor_set_anchor_point(rect, 128, 64);
    
Spinning rectangles with opacity? Now we're getting somewhere...

Spinning rectangles with opacity? Now we're getting somewhere...

Scaling and picking

So, you've seen that Clutter can spin things around easily enough - let's push that a little further by making it scale the rectangles too. Now, Clutter has a very clever system to let you calculate values over a period of time, but, to be honest, it's a bit out of the remit of this beginner's tutorial. So, to help you along we're going to use a simple C header file we put together that lets us do some neat effects without any real work - download this file to your working directory, rename it to mathhacks.h, then add it to your list of #includes.

With that header file, we can interpolate between two values with easing on either side, meaning that we can make the rectangles grow larger and smaller smoothly simply by passing in a value between 0 and 1. That value needs to be stored somewhere, so add this line next to the "gdouble rotation" line:

gdouble scale = 0;

With that variable in place, we can add some scaling to the on_timeline_new_frame() method from before - put these lines in that function, after the rotation code:

scale += 0.01;
if (scale > 1.0) scale = 0;
float scale_amount = smooth_step2(1.0, 2.0, scale);

clutter_actor_set_scale(rect1, scale_amount, scale_amount);
clutter_actor_set_scale(rect2, scale_amount, scale_amount);
clutter_actor_set_scale(rect3, scale_amount, scale_amount);
clutter_actor_set_scale(rect4, scale_amount, scale_amount);
clutter_actor_set_scale(rect5, scale_amount, scale_amount);
clutter_actor_set_scale(rect6, scale_amount, scale_amount);

The smooth_step2() function is one of the quick little hacks we included in mathhacks.h, and what it does is provide a value between X and Y according to Z, where Z is a value between 0 and 1. In our example above, we'll get a value between 1.0 and 2.0 depending on scale.

The "2" part of that function name is there because when "scale" is less than 0.5, the function will interpolate from 1.0 to 2.0; but when "scale" is greater than 0.5, the function will interpolate between 2.0 and 1.0. In essence, this means our rectangles will scale up to 2.0x their size, then back down again, nice and smoothly. We've included a simple smooth_step() function in there as well, which doesn't return to the original value.

That's all it takes - run that code now and you'll see the rectangles spinning and growing/shrinking as the animation progresses. Easy? Definitely, but we're only just getting started - let's move on to something a bit more interesting: picking.

With lots of objects in the scene, how do you know which one has been clicked? In the case of rectangles, a simple "is this point inside the bounds of the rectangle" works OK when the rectangle is neither scaled nor rotated, but when you factor in those transformations things are much more complicated. Fortunately, Moblin makes it a cinch: we can ask it to run a function when the screen is clicked, then ask it to tell us which actor was clicked on. If it sounds easy, it's because it is easy - start by putting this line directly after the last call to create_rect() in main():

g_signal_connect(stage, "button-press-event", G_CALLBACK(on_stage_button_press), NULL);

That hooks the button-press-event signal up to a new function called on_stage_button_press(). As with the timeline new frame function, this needs to accept very specific parameters so you might as well copy and paste it as needed. What we're going to do in this function is a) ask Clutter to tell us the X and Y co-ordinates that were clicked, b) use those co-ordinates to figure out which actor was clicked, then c) hide that actor.

Here's the code for the function - remember it needs to come before main() in your code:

void on_stage_button_press(ClutterStage *stage, ClutterEvent *event, gpointer data) {
	// find out which part of the screen was clicked
	gfloat x = 0;
	gfloat y = 0;
	clutter_event_get_coords(event, &x, &y);

	// find which actor was clicked
	ClutterActor* clicked = clutter_stage_get_actor_at_pos(CLUTTER_STAGE(stage), CLUTTER_PICK_ALL, x, y);

	// ignore clicks on the stage
	if (clicked == CLUTTER_ACTOR(stage)) return;

	// hide the actor that was clicked
	clutter_actor_hide(clicked);
}

If you compile and run that code now, you'll see the same spinning, scaling boxes as before, but now you can click on any box to hide it. Clutter depth sorts the rectangles according to the time they were added to the stage, so clicking where two boxes overlap will hide only the top-most one.

Clutter can scale our rectangles up while spinning them without batting an eyelid. Hurray for OpenGL!

Clutter can scale our rectangles up while spinning them without batting an eyelid. Hurray for OpenGL!

Keeping score

Our timeline allows us to animate the objects in the scene freely, but it's a bit crude - everything gets lumped into one function, which means that complex animations will get messy. Again, Clutter comes to the rescue with scores: collections of timelines that can be run in parallel or in sequence, depending on your needs. To demonstrate this, we first need to split our curent on_timeline_new_frame() function into two parts - one for rotating and one for scaling. Here's how it should look:

void on_timeline_rotation_new_frame(ClutterTimeline *timeline, gint frame_num, gpointer data) {
	rotation += 0.3;

	clutter_actor_set_rotation(rect1, CLUTTER_Z_AXIS, rotation * 5, 0, 0, 0);
	clutter_actor_set_rotation(rect2, CLUTTER_Z_AXIS, rotation * 4, 0, 0, 0);
	clutter_actor_set_rotation(rect3, CLUTTER_Z_AXIS, rotation * 3, 0, 0, 0);
	clutter_actor_set_rotation(rect4, CLUTTER_Z_AXIS, rotation * 2, 0, 0, 0);
	clutter_actor_set_rotation(rect5, CLUTTER_Z_AXIS, rotation, 0, 0, 0);
	clutter_actor_set_rotation(rect6, CLUTTER_Z_AXIS, rotation * 0.5, 0, 0, 0);
}

void on_timeline_scale_new_frame(ClutterTimeline *timeline, gint frame_num, gpointer data) {
	scale += 0.01;
	if (scale > 1.0) scale = 0;
	float scale_amount = smooth_step2(1.0, 2.0, scale);

	clutter_actor_set_scale(rect1, scale_amount, scale_amount);
	clutter_actor_set_scale(rect2, scale_amount, scale_amount);
	clutter_actor_set_scale(rect3, scale_amount, scale_amount);
	clutter_actor_set_scale(rect4, scale_amount, scale_amount);
	clutter_actor_set_scale(rect5, scale_amount, scale_amount);
	clutter_actor_set_scale(rect6, scale_amount, scale_amount);
}

There's not really any new code in there - it just takes the existing function and breaks it into two. Now, we can recreate the old behaviour using a score by first creating the sccore, then adding both timelines to it to execute in parallel. This time we don't want to use the clutter_timeline_set_loop() function because we don't want the timelines to loop - we want the score to loop. Clutter's consistent function naming should allow you to guess that what we want to do is use clutter_score_set_loop() so that the whole score loops rather than individual timelines.

As with timelines, scores need to be destroyed by us directly - they aren't automatically destroyed when the stage is destroyed. So, modify the end part of your main() function to this:

// create a new score and set it to loop
ClutterScore *score = clutter_score_new();
clutter_score_set_loop(score, TRUE);

// create a new timeline and set its duration to half a second
ClutterTimeline *timeline_rotation = clutter_timeline_new(500);
g_signal_connect(timeline_rotation, "new-frame", G_CALLBACK(on_timeline_rotation_new_frame), NULL);

// add the timeline to the score
clutter_score_append(score, NULL, timeline_rotation);

// create another timelime, also with half a second duration
ClutterTimeline *timeline_scale = clutter_timeline_new(500);
g_signal_connect(timeline_scale, "new-frame", G_CALLBACK(on_timeline_scale_new_frame), NULL);

// add that to the score as well
clutter_score_append(score, NULL, timeline_scale);

// now start the score
clutter_score_start(score);

clutter_main();

// release the memory we allocated
g_object_unref(timeline_rotation);
g_object_unref(timeline_scale);
g_object_unref(score);

return EXIT_SUCCESS;

If you run the code now, you'll see that nothing has changed: the rectangles continue to spin and scale as they did before. The reason for this is that we're adding the timelines to the score and asking Clutter to execute them in parallel - on_timeline_rotation_new_frame() is called once, then on_timeline_scale_new_frame() is called one, etc.

What we want to do is to make Clutter play the entire 500ms of on_timeline_rotation_new_frame() before switching over to play 500ms of on_timeline_scale_new_frame(). To do that, we just need to modify the call to clutter_score_append() - that second parameter, NULL, means "this has no parent timeline - let it execute when it can." But try changing that second clutter_score_append() call to this:

clutter_score_append(score, timeline_rotation, timeline_scale);

Now we're adding the scaling timeline to the score and giving it the rotation timeline as its parent. That means the two timelines will be executed in sequence: rotation first, then scaling. And if you compile and run the code now, that's exactly what you'll see - the boxes will spin a bit, then stop and scale a bit, then stop and spin a bit more, and so on.

More than just coloured rectangles

As we said earlier, Clutter has a few different kinds of actors available for us to use, but the magic is that we can use them in almost exactly the same way. For example, if you want to have pictures spinning and scaling around rather than plain rectangles, all you need to do is change one line in create_rect() - rather than creating a rectangle from a colour value, we can use a texture instead, with this line:

ClutterActor *rect = clutter_texture_new_from_file("media/card_1.png", NULL);

Nothing else has to change: if you compile and run that program now, you'll see the texture on the screen rather than plain old rectangles.

Clutter is able to load textures from PNG and JPEG, and treat them like any other kind of actor.

Clutter is able to load textures from PNG and JPEG, and treat them like any other kind of actor.

Clutter can even handle more complicated objects such as text boxes - change that previous line for this new one and you'll see just how easy it is:

ClutterActor *rect = clutter_text_new_full("Sans 24", "Hello, world", &col);

Yes, using that will change all our coloured rectangles into coloured text messages on the screen, spinning and scaling as before. The first parameter to clutter_text_new_full() should be the name and size of the font you want to use; the second parameter is the text to print, and the third one is the colour to use.

But that's not just some simple text you can see there - it's actually a full text entry box capable of reading keyboard input and wrapping lines when the user presses enter. To give that a try, we need to make two small changes. First, put this line directly beneath clutter_text_new_full() in create_rect():

clutter_text_set_editable(CLUTTER_TEXT(rect), TRUE);

And now change on_stage_button_press() so rather than hiding the actor that was clicked on, instead we tell it to grab keyboard focus so the user can start typing. Modify the end of the function to this:

ClutterActor* clicked = clutter_stage_get_actor_at_pos(CLUTTER_STAGE(stage), CLUTTER_PICK_ALL, x, y);
if (clicked == CLUTTER_ACTOR(stage)) return;
clutter_actor_grab_key_focus(clicked);
Clutter can handle text, even editable text, without any hassle. NB: you get better results with vsync enabled.

Clutter can handle text, even editable text, without any hassle. NB: you get better results with vsync enabled.

Final tweaks

Before we wrap up this lightning dip into the world of Clutter, there are three more things you should try. First, it's high time you ditched the "./clutterapp" title in your app's window, so put this line of code into main() before calling clutter_actor_show():

clutter_stage_set_title(CLUTTER_STAGE(stage), "Spinny boxes ahoy!");

Next, if you hadn't figured it out by now, every Clutter actor can also have events associated with it - mouse clicks and motion, key presses and more can all have actions associated with them. The key is to set the actor to be reactive, which means you're telling Clutter that this actor should have events triggered for it. By default, most things in Clutter aren't reactive, because it's a waste of CPU time. So, enable the ones you want or you'll wonder why they don't work!

To create a reactive rectangle, you'd do something like this:

ClutterActor *rect = clutter_rectangle_new_with_color(&col);
clutter_actor_set_size(rect, 256, 128);
clutter_actor_set_position(rect, 128, 128);
clutter_actor_set_reactive (rect, TRUE);
g_signal_connect (rect, "button-press-event", G_CALLBACK (on_rect_button_press), NULL);

As you can see, that attaches a function called on_rect_button_press() to the rectangle. This is pretty much identical to the on_stage_button_press() function from earlier, although this time (as you can imagine) we need to access a ClutterActor as the first parameter. In this simple example, we're going to make the rectangle change colour when it gets clicked on - put this function into your code:

gboolean on_rect_button_press(ClutterActor *actor, ClutterEvent *event, gpointer data) {
    ClutterColor newcol = { rand() % 256, rand() % 256, rand() % 256, 255 };
    clutter_rectangle_set_color(CLUTTER_RECTANGLE(actor), &newcol);

    return TRUE;
}

Clutter passes us the actor that was clicked as the first parameter, which means we could attach the same function to a dozen different rectangles and only the one that was clicked would be affected. If you want to go back and use the general picking system we looked at when handling stage clicks, try changing CLUTTER_PICK_ALL to CLUTTER_PICK_REACTIVE to have only reactive elements pickable.

Finally, we skipped over most of the animation discussion here, because it gets a bit trickier there and is outside the bounds of a beginner's tutorial. Still, we'd be remiss if we didn't at least mention that Clutter does have some special functionality to make animation really easy. This is all wrapped up inside a function called clutter_actor_animate(), which basically hides away all the complexity of animation and makes it so easy that we're even tempted to call it fun.

To make this example work, we need to trim out some of the stuff from our existing code - those timelines, for example, really aren't needed. Instead, here's what you need:

#include <clutter/clutter.h>
#include <stdlib.h>

// six empty rectangles and our stage
ClutterActor *stage = NULL;
ClutterActor *rect1 = NULL;
ClutterActor *rect2 = NULL;
ClutterActor *rect3 = NULL;
ClutterActor *rect4 = NULL;
ClutterActor *rect5 = NULL;
ClutterActor *rect6 = NULL;

// the create_rect() function needs to create actual rectangles rather than text boxes
ClutterActor *create_rect(ClutterColor col) {
  ClutterActor *rect = clutter_rectangle_new_with_color(&col);
  clutter_actor_set_size(rect, 256, 128);
  clutter_actor_set_position(rect, 256, 256);
  clutter_actor_set_anchor_point(rect, 128, 64);
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
  clutter_actor_show (rect);

  return rect;
}

// we'll be filling in this function later
void on_stage_button_press (ClutterStage *stage, ClutterEvent *event, gpointer data) {
	// do stuff here
}

int main(int argc, char *argv[]) {
  clutter_init (&argc, &argv);

  ClutterColor stage_color = { 0, 0, 0, 255 };

  stage = clutter_stage_get_default();
  clutter_actor_set_size (stage, 512, 512);
  clutter_stage_set_color (CLUTTER_STAGE(stage), &stage_color);

  ClutterColor red = { 255, 0, 0, 128 };
  ClutterColor green = { 0, 255, 0, 128 };
  ClutterColor blue = { 0, 0, 255, 128 };
  ClutterColor yellow = { 255, 255, 0, 128 };
  ClutterColor cyan = { 0, 255, 255, 128 };
  ClutterColor purple = { 255, 0, 255, 128 };

  rect1 = create_rect(red);
  rect2 = create_rect(green);
  rect3 = create_rect(blue);
  rect4 = create_rect(yellow);
  rect5 = create_rect(cyan);
  rect6 = create_rect(purple);

  g_signal_connect (stage, "button-press-event", G_CALLBACK (on_stage_button_press), NULL);
  clutter_stage_set_title(CLUTTER_STAGE(stage), "Spinny boxes ahoy!");
  clutter_actor_show (stage);

  clutter_main ();

  return EXIT_SUCCESS;
}

When you compile and run that you'll see all the boxes sitting in the middle, waiting to spring into life. What we're going to do is use clutter_actor_animate() to make each of those boxes spring off into different parts of the screen, rotating as they move. There are several awesome things about clutter_actor_animate(), but three are particuarly key: 1) you can change the animation type very easily, 2) you can animate several things in a single function call, and 3) if you call clutter_actor_animate() for a given actor before its previous animation has completed, Clutter handles it gracefully.

In the last chunk of code, the function that handles the scene being clicked contains "// do stuff here". Put this code in there:

move_rect(rect1);
move_rect(rect2);
move_rect(rect3);
move_rect(rect4);
move_rect(rect5);
move_rect(rect6);

We haven't defined the move_rect() function yet, but you can see we pass into it each of our rectangles. What this function is going to do is move a rectangle to a random position on the screen with a random rotation, but - and this is important - we must specify all three of those values as floats rather than integers. If you use integers, your program will most likely crash, so just typecast to a float and you'll be fine.

Here's the code:

void move_rect(ClutterActor *rect) {
	clutter_actor_animate (rect,
		CLUTTER_LINEAR,
		1000,
		"x", (float)(rand() % 512),
		"y", (float)(rand() % 512),
		"rotation-angle-z", (float)(rand() % 360),
		NULL);
}

We've split the call to clutter_actor_animate() across multiple lines so you can get a sense of how it works. Let's break it down:

  1. CLUTTER_LINEAR is the animation type. It means that the animation moves at an equal speed the entire time.
  2. 1000 is the amount of time, in milliseconds, the animation will take to run.
  3. The "x" line sets the X position of the actor to a random value. "x" is actually a property of the actor, specified as a string. Ditto for the "y" line.
  4. "rotation-angle-z" is what creates the spin on the actor.
  5. When you've specified all the properties you want to change, you must end with NULL.

If you run the program now, you'll find that clicking anywhere makes all the squares move. But it's a bit dull, right? Try changing CLUTTER_LINEAR to CLUTTER_EASE_IN_OUT_BACK and run the program again - we're not going to tell you what it does, but we think you'll like it...

Boxes that whizz around the screen beautifully - they could just as easily be textures or text, or any other kind of actor you care to drop into Clutter.

Boxes that whizz around the screen beautifully - they could just as easily be textures or text, or any other kind of actor you care to drop into Clutter.

All done... for now

This has only been a brief primer into Clutter, but we think we've managed to cover a lot of ground in the few hours we spent. It should at least be apparent now why Canonical chose to use Clutter for the Ubuntu Netbook Remix GUI - it really is the fastest and simplest way to get a smart and attractive user interface working, and it does so much of the hard work for you. In fact, you should really be able to look at the UNR interface now and think to yourself, "I could probably make that." And the best thing is: you probably could.

You should follow us on Identi.ca or Twitter


Your comments

First one? ;-)

Great article. Keep up the good work folks!

Really useful article! I

Really useful article! I enjoyed it!

Look a bit old and limited

Great article.
I have done the examples, and I don't get what's the point of clutter in the end. QGraphicsView do the same and is much more powerfull. Clutter look like a step backward.

Are there stuff you can do with Clutter an not with the other framework?

Thanks

I enjoyed it a lot building the samples (although some tweaking was needed to make them work with clutter 0.9).

@Anonimous Penguin:
thanks for the pointer to QGraphicsView. The obvious answer to your question is: GTK+ integration (clutter uses Cairo and the GObject type system). Also, clutter is OpenGL-accelerated, I'm unsure if QGraphicsView is too.

LOVELYYYYYY....

LOVELYYYYYY....

nice!

thanks!

Good, but How i can to set

Good, but How i can to set opacity to the stage?

> Good, but How i can to set

> Good, but How i can to set opacity to the stage?

you currently can't - but with Clutter 1.2 (which is going to be released February/March '10) and if you're running with a compositor engine on X11, then you'll be able to set the opacity using clutter_actor_set_opacity() on the stage, just like you can on every other actor.

Yes, it is useful for newbie!!

thanks!

Compiling in Eclipse

These work great compiled from the command line with the command you gave:

gcc example.c -o clutterapp `pkg-config clutter-1.0 --cflags --libs`

How can I run them from Eclipse CDT? Please be as explicit as an XXX film, as I've already tried and failed. _Exactly_ what goes into the various parts of the project's preferences settings? Compiler, linker, etc.

CLUTTER_STAGE vs CLUTTER_CONTAINER

Just above the simple rectangle image is this:
"Note the use of the CLUTTER_STAGE() macro"
but the example uses CLUTTER_CONTAINER

Otherwise really nice article

Clutter vs QGraphicsView

QGraphicsView can be OpenGL accelerated too by setting an OpenGL viewport to it (somebody asked that...)

Basically, Clutter and QGraphicsView have more or less the same funcionality except for some details, being (IMO) the main one that Clutter is 2.5D while QGraphicsView is 2D.

Howto use pkg-config in eclipse

to use pkg-config in eclipse,

Properties -> C/C++ Build -> Settings
add `pkg-config clutter-1.0 --cflags --libs`
to the "Other Flags" field found under "Miscellaneous"
for BOTH of "GCC C++ Compiler" and "GCC C++ Linker"
and or "GCC C Compiler" if using straight C

Timelines

Sorry to post here, but how can you use timelines to call a function every x number of seconds...

I have this:
----------------------------
//snip
ClutterTimeline *timeline = clutter_timeline_new(1000);
g_signal_connect(timeline, "new-frame", G_CALLBACK(on_timeline_new_feed), NULL);
clutter_timeline_set_loop(timeline, TRUE);
clutter_timeline_start(timeline);

clutter_main();
----------------------------
but the function is fired a lot quicker than every second

Good tutorial

...and contains everything to, say, port the silly bubble game over from mono that features elsewhere on the site :)

Thank you!

Arrays

instead of declaring pile of pointers, why not array of pointers
ClutterActor *rect[10]={NULL};
then do this
for(i=0;i<10;i++)
rect[i]=create_rect(&col,and something else in rewritten function, lets say position or size or both)
ClutterActor *create_rect(ClutterColor col,int width,int height,int x, inty) {
ClutterActor *rect = clutter_rectangle_new_with_color(&col);
clutter_actor_set_size(rect, width, height);
clutter_actor_set_position(rect, x, y);
clutter_container_add_actor(CLUTTER_CONTAINER(stage), rect);
clutter_actor_show(rect);

return rect;
}
any difference?

Stage Opacity in Clutter 1.2?

I am trying to make the stage opaque such that the window itself allows items behind it to become visible -- rather like an overlay.

Is this possible in Clutter 1.2?
Any other way to achieve this?
Will window transparency only work on certain environments?
- i.e. such as with the Intel IEGD option for blend/overlay

I notice that when you move any window in the Moblin 2.1 OS (running on an Intel with IEGD) then it becomes semi-transparent, so it must be possible to do. Note that Moblin 2.1 has Clutter-1.0 and I thought Moblin was built around Clutter so I'm wondering how they achieve this.

Anyway, here are the functions I have been playing with (note the comments following each function which is based on my experience)

// this affects the actors, not the window itself
clutter_actor_set_opacity ( CLUTTER_ACTOR(stage), 0x66 );

// no effect on Ubuntu VM; maybe on Moblin?
clutter_stage_set_use_alpha( stage, TRUE );

Alas I am unable to upgrade Clutter-1.2 on Moblin to test the second function above, because it there are build errors regarding GL (something peculiar about Moblin).

building requierments

Nice article.

Where can I find information about installation requirements to build Clutter apps??

I'm going to develop for Angstrom - BeagleBoard.

Thanks

Awesome

Thanks a lot. Love it.

Found clutter today...

I'm impressed. This is what I have been looking for. Ahhh...

Thanks!!!

It was all I need!!
Could you do more tutorials about Clutter??
i.e. using gst...

Thanks a lot!!!

Great Article

Is a very powerfull toy this one...and great tutorial, for people like u, one of this days we'll get the freedom..
Keep working on that...

Clutter-Gtk and Clicks

I'm using your "on_stage_button_press" code with a Clutter stage embedded in a Gtk window and it is always returning the stage as being clicked, ideas?

Very helpful tutorial by the way! thanks!

Need more !!

Thank you very very much, this intro was exactly what I needed and you convinced me to go on learning.

No relation

I'm not related to Marc Chocolat (although we have similar names) but thanks for the tutorial, I'll be trying my hand as some Clutter soon.

Can u explain how to add a

Can u explain how to add a label to a stage? I get an undefined error if i try to.

Impossible to implement timeline

Dear Clutter users,
I try the tutorial but at the paragraph "Keeping score"; it don't work.
Can someone post the code wich is ok?
Best regards
Olihya

Impossible to implement timeline

Ok, i have found the solution.
Thanks for your nice tutorial.

Very nice tutorial, thanks.

Very nice tutorial, thanks. Many of the functions are deprecated with Clutter 1.0 though. Is there a tutorial around with non-deprecated commands?

Great !

Awesome tutorial, good job !

a perfect tutorial

Cant find anything better than this, to understand about the topic. Extremely very well written. Made very easy to understand. Great job!!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

CAPTCHA
We can't accept links (unless you obfuscate them). You also need to negotiate the following CAPTCHA...

Username:   Password:
Create Account | About TuxRadar