<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>3. Connecting to signals in ClutterScript</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot"><link rel="home" href="index.html" title="The Clutter Cookbook"><link rel="up" href="script.html" title="Chapter 8. Script"><link rel="prev" href="script-ui.html" title="2. Defining a user interface with JSON"><link rel="next" href="script-state.html" title="4. Connecting ClutterState states in ClutterScript"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3. Connecting to signals in <span class="type">ClutterScript</span></th></tr><tr><td width="20%" align="left"><a accesskey="p" href="script-ui.html">Prev</a> </td><th width="60%" align="center">Chapter 8. Script</th><td width="20%" align="right"> <a accesskey="n" href="script-state.html">Next</a></td></tr></table><hr></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="script-signals"></a>3. Connecting to signals in <span class="type">ClutterScript</span></h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200507595856"></a>3.1. Problem</h3></div></div></div><p>You have declared an actor using JSON, and want to add
handlers for signals emitted by it.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200507594432"></a>3.2. Solution</h3></div></div></div><p>Add a <code class="varname">signals</code> property to the actor's
JSON definition.</p><p>Here's how to connect a <span class="type">ClutterStage's</span>
<code class="code">destroy</code> signal to the
<code class="function">clutter_main_quit()</code> function:</p><div class="informalexample"><pre class="programlisting">{
"id" : "stage",
"type" : "ClutterStage",
"width" : 300,
"height" : 300,
<span class="emphasis"><em>"signals" : [
{ "name" : "destroy", "handler" : "clutter_main_quit" }
]</em></span>
}</pre></div><p>The highlighted part of the code is where the
signal is connected. In this case, a Clutter function is used
as the handler; in most cases, you'll want to define your own
handlers, rather than using functions from other libraries,
as follows:</p><div class="informalexample"><pre class="programlisting">{
"id" : "rectangle",
"type" : "ClutterRectangle",
"width" : 200,
"height" : 200,
"reactive" : true,
<span class="emphasis"><em>"signals" : [
{ "name" : "motion-event", "handler" : "foo_pointer_motion_cb" }
]</em></span>
}</pre></div><p>This signal handler definition sets
<code class="function">foo_pointer_motion_cb()</code>
as the handler for the <code class="code">motion-event</code>
signal on the rectangle. (NB the rectangle has
<code class="varname">reactive</code> set to true, otherwise it
can't emit this signal.)</p><p>As per standard event handling in Clutter,
you define the handler function next. For example:</p><div class="informalexample"><pre class="programlisting">/* handler which just prints the position of the pointer at each motion event */
gboolean
foo_pointer_motion_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer user_data)
{
gfloat x, y;
clutter_event_get_coords (event, &x, &y);
g_print ("Pointer movement at %.0f,%.0f\n", x, y);
return TRUE;
}</pre></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>See the
<a class="link" href="script-signals.html#script-signals-discussion-writing-handlers" title="3.3.2. Writing handler functions">Discussion</a>
section for more about writing handler functions.</p></div><p>To make the signal connections active in your code,
call the <code class="function">clutter_script_connect_signals()</code>
function after loading the JSON:</p><div class="informalexample"><pre class="programlisting">GError *error = NULL;
/* load JSON from a file */
ClutterScript *ui = clutter_script_new ();
clutter_script_load_from_file (ui, filename, &error);
/* ...handle errors etc... */
/* connect the signals defined in the JSON file
*
* the first argument is the script into which the JSON
* definition was loaded
*
* the second argument is passed as user_data to all
* handlers: in this case, we pass the script as user_data
* to all handlers, so that all the objects in the UI
* are available to callback functions
*/
clutter_script_connect_signals (ui, ui);</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="script-signals-discussion"></a>3.3. Discussion</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm140200507576736"></a>3.3.1. Options for connecting signals to handlers</h4></div></div></div><p>Every connection between a signal and handler requires
a JSON object with <code class="varname">name</code> and
<code class="varname">handler</code> keys. The <code class="varname">name</code>
is the name of the signal you're connecting a handler to; the
<code class="varname">handler</code> is the name of the function which
will handle the signal.</p><p>You can also specify these optional keys for a handler
object:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><code class="code">"after" : true</code> configures the handler
to run after the default handler for the signal. (Default is
<code class="code">"after" : false</code>).</p></li><li class="listitem"><p><code class="varname">"swapped" : true</code> specifies that
the instance and the user data passed to the
handler function are swapped around; i.e. the instance emitting
the signal is passed in as the user data argument (usually the
last argument), and any user data is passed in as the first
argument. (Default is <code class="code">"swapped" : false</code>).</p></li></ol></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>While the connections to signals were specified in JSON
above, it is still possible to connect handlers to signals in
code (e.g. if you need to conditionally connect a handler). Just
retrieve the object from the <span class="type">ClutterScript</span> and
connect to its signals with
<code class="function">g_signal_connect()</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="script-signals-discussion-writing-handlers"></a>3.3.2. Writing handler functions</h4></div></div></div><p>The handler function has the usual signature required
for the signal. However, the function cannot be static, otherwise
the function is invisible to GModule (the mechanism used by
<span class="type">ClutterScript</span> to look up functions named
in the JSON definition). Consequently, callback functions should be
namespaced in such a way that they won't clash with function
definitions in other parts of your code or in libraries you link
to.</p><p>You should also ensure that you use the
<code class="option">-export-dynamic</code> flag when you compile your
application: either by passing it on the command line (if you're
calling <span class="command"><strong>gcc</strong></span> directly); or by adding
it to the appropriate <code class="varname">LDFLAGS</code> variable in
your <code class="filename">Makefile</code> (if you're using
<span class="command"><strong>make</strong></span>); or by whatever other mechanism is
appropriate for your build environment.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idm140200506698944"></a>3.3.3. Passing objects to handler functions</h4></div></div></div><p>In a typical Clutter application, handler functions
require access to objects other than the one which emitted a
signal. For example, a button may move another actor when
clicked. Typically, you would pass any required objects
to the handler function as user data, like this:</p><div class="informalexample"><pre class="programlisting">g_signal_connect (button,
"clicked",
G_CALLBACK (_button_clicked_cb),
actor_to_move);</pre></div><p>Note how <code class="varname">actor_to_move</code> is passed
as user data to the handler.</p><p>However, the JSON definition doesn't allow you to specify
that different user data be passed to different handlers. So,
to get at all required objects in the handler, a simple
solution is to pass the <span class="type">ClutterScript</span> to
<span class="emphasis"><em>every</em></span> handler function; then inside
<span class="emphasis"><em>each</em></span> handler function, retrieve
the required objects from the script.</p><p>This was done in the code example above, by passing
the <span class="type">ClutterScript</span> instance as two arguments to
<code class="function">clutter_script_connect_signals()</code>:
the first argument specifies the script which defines the
signal handlers; the second specifies the user data passed to every
handler function. This ensures that each handler has access
to all of the elements defined in the JSON file.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>Alternatively, you could create some other structure to
hold the objects you need and pass it to all handler functions.
But this would effectively be a reimplementation of some aspects
of <span class="type">ClutterScript</span>.</p></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idm140200506690384"></a>3.4. Full examples</h3></div></div></div><div class="example"><a name="script-signals-examples-1"></a><p class="title"><b>Example 8.3. <span class="type">ClutterScript</span> JSON with signal handler
definitions</b></p><div class="example-contents"><pre class="programlisting">[
{
"id" : "stage",
"type" : "ClutterStage",
"width" : 300,
"height" : 300,
"color" : "#335",
"signals" : [
{ "name" : "destroy", "handler" : "clutter_main_quit" }
],
"children" : [ "rectangle" ]
},
{
"id" : "rectangle",
"type" : "ClutterRectangle",
"width" : 200,
"height" : 200,
"x" : 50,
"y" : 50,
"color" : "#a90",
"rotation-center-z-gravity" : "center",
"reactive" : true,
"signals" : [
{ "name" : "motion-event", "handler" : "foo_pointer_motion_cb" }
],
"actions" : [
{
"type" : "ClutterClickAction",
"signals" : [
{ "name" : "clicked", "handler" : "foo_button_clicked_cb" }
]
}
]
}
]
</pre></div></div><br class="example-break"><div class="example"><a name="script-signals-examples-2"></a><p class="title"><b>Example 8.4. Loading a JSON file into a <span class="type">ClutterScript</span>
and connecting signal handlers</b></p><div class="example-contents"><pre class="programlisting">#include <stdlib.h>
#include <clutter/clutter.h>
/* callbacks cannot be declared static as they
* are looked up dynamically by ClutterScript
*/
gboolean
foo_pointer_motion_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer user_data)
{
gfloat x, y;
clutter_event_get_coords (event, &x, &y);
g_print ("Pointer movement at %.0f,%.0f\n", x, y);
return TRUE;
}
void
foo_button_clicked_cb (ClutterClickAction *action,
ClutterActor *actor,
gpointer user_data)
{
gfloat z_angle;
/* get the UI definition passed to the handler */
ClutterScript *ui = CLUTTER_SCRIPT (user_data);
/* get the rectangle defined in the JSON */
ClutterActor *rectangle;
clutter_script_get_objects (ui,
"rectangle", &rectangle,
NULL);
/* do nothing if the actor is already animating */
if (clutter_actor_get_animation (rectangle) != NULL)
return;
/* get the current rotation and increment it */
z_angle = clutter_actor_get_rotation (rectangle,
CLUTTER_Z_AXIS,
NULL, NULL, NULL);
if (clutter_click_action_get_button (action) == 1)
z_angle += 90.0;
else
z_angle -= 90.0;
/* animate to new rotation angle */
clutter_actor_animate (rectangle,
CLUTTER_EASE_OUT_CUBIC,
1000,
"rotation-angle-z", z_angle,
NULL);
}
int
main (int argc, char *argv[])
{
ClutterActor *stage;
ClutterScript *ui;
gchar *filename = "script-signals.json";
GError *error = NULL;
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
ui = clutter_script_new ();
clutter_script_load_from_file (ui, filename, &error);
if (error != NULL)
{
g_critical ("Error loading ClutterScript file %s\n%s", filename, error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
clutter_script_get_objects (ui,
"stage", &stage,
NULL);
/* make the objects in the script available to all signals
* by passing the script as the second argument
* to clutter_script_connect_signals()
*/
clutter_script_connect_signals (ui, ui);
clutter_actor_show (stage);
clutter_main ();
g_object_unref (ui);
return EXIT_SUCCESS;
}
</pre></div></div><br class="example-break"></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="script-ui.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="script.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="script-state.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">2. Defining a user interface with JSON </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 4. Connecting <span class="type">ClutterState</span> states in <span class="type">ClutterScript</span></td></tr></table></div></body></html>