<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
 "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">

<chapter id="events">
  <title>Events</title>

  <epigraph>
    <attribution>The Tenth Doctor (David Tennant)</attribution>
    <para>Crossing into established events is strictly forbidden. Except for
    cheap tricks.</para>
  </epigraph>

  <section id="events-introduction">
    <title>Introduction</title>
    <para>Once you have set up a scene on the stage, in order to respond
    to user interaction you will have to handle events coming from the
    underlying platform.</para>

    <para>Events are relayed to actors by Clutter in form of
    <emphasis>signals</emphasis>; signals are a facility provided by the
    GObject framework to call functions depending on a unique name. A signal
    can be thought as a message that an object instance broadcasts to various
    listener functions.</para>

    <para>There are various events that Clutter will handle: mostly, they
    deal with input devices, like a mouse pointer or a keyboard; but they can
    also come from the windowing system, like the
    <emphasis>delete-event</emphasis> signal that is emitted when the user
    closes the window of the stage.</para>

    <para>Each event has a particular <emphasis>source</emphasis>, that is
    the actor that received the event. The event handling sequence is divided
    in two phases:</para>

    <orderedlist>
      <listitem><para>the <emphasis>capture</emphasis> phase, which consists
      in an emission of the <emphasis>captured-event</emphasis> signal
      starting from the stage to, following the parent-child relationship,
      the source of the event;</para></listitem>
      <listitem><para>the <emphasis>bubble</emphasis> phase, which consists
      in an emission of the <emphasis>event</emphasis> signal starting from
      the the source of the event to, following the parent-child
      relationship, the stage.</para></listitem>
    </orderedlist>

    <para>At any point during the event emission sequence a handler of either
    the captured-event or the event signals can stop it, by returning a value
    of TRUE, which means that the event has been handled. If an event hasn't
    been handled, FALSE should be returned instead.</para>
  </section>

  <section id="events-handling-key-events">
    <title>Handling key events</title>

    <section>
      <title>Problem</title>
      <para>You want to respond to key presses on an actor.</para>
    </section>

    <section>
      <title>Solutions</title>

      <para>There are two possible solutions:</para>

      <orderedlist>
        <listitem>
          <para><emphasis>Solution 1:</emphasis> Connect a callback to the
          actor; inside the callback, manually analyse which key and
          modifier(s) were pressed and react accordingly.</para>
        </listitem>
        <listitem>
          <para><emphasis>Solution 2:</emphasis> Use an actor's
          <type>ClutterBindingPool</type> to declaratively assign
          actions to specific key and modifier combinations.</para>
        </listitem>
      </orderedlist>

      <para>Each solution is covered below.</para>

      <section>
        <title>Solution 1</title>

        <para>Connect the <emphasis>key-press-event</emphasis>
        signal for an actor to a callback; then examine the event
        in the callback to determine which key and modifiers were
        pressed.</para>

        <para>First, connect an actor's
        <emphasis>key-press-event</emphasis> signal to a callback:</para>

        <informalexample>
          <programlisting>
g_signal_connect (actor, "key-press-event", G_CALLBACK (_key_press_cb), NULL);
          </programlisting>
        </informalexample>

        <para>Then, in the callback, check which key was pressed and which
        modifiers were down at the same time. For example, this callback
        checks for a press on the up arrow key and whether
        the <keycap>Shift</keycap> and/or <keycap>Ctrl</keycap>
        key were down:</para>

        <informalexample>
          <programlisting>
static gboolean
_key_press_cb (ClutterActor *actor,
             ClutterEvent *event,
             gpointer      user_data)
{
  guint keyval = clutter_event_get_key_symbol (event);

  if (CLUTTER_Up == keyval)
    {
      ClutterModifierType modifiers = clutter_event_get_state (event);

      switch (modifiers)
        {
        case CLUTTER_SHIFT_MASK:
          g_debug ("Up and shift pressed");
          break;

        case CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK:
          g_debug ("Up and shift and control pressed");
          break;

        default:
          g_debug ("Up pressed");
          break;
        }

      /* The event was handled, and the emission should stop */
      return TRUE;
    }

  /* The event was not handled, and the emission should continue */
  return FALSE;
}
          </programlisting>
        </informalexample>

        <note>
          <para>Clutter provides a range of key value definitions
          (like <constant>CLUTTER_Up</constant>, used above). These are
          generated from the list in the
          <ulink url="http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h">X.Org source code</ulink>
          (replace "XK" with "CLUTTER" in the definitions there to get the
          CLUTTER equivalents; alternatively, look at the
          <filename>clutter-keysyms.h</filename> header file for the
          list).</para>

          <para><constant>CLUTTER_SHIFT_MASK</constant>,
          <constant>CLUTTER_CONTROL_MASK</constant> and other modifiers are
          defined in the <type>ClutterModifierType</type> enum.</para>
        </note>

      </section>

      <section>
        <title>Solution 2</title>

        <para>Assign actions to an actor's <type>ClutterBindingPool</type>.
        A binding pool stores mappings from a key press (either a single key
        or a key plus modifiers) to actions; an action is simply a callback
        function with a specific signature.</para>

        <para>While this approach is trickier to implement, it is more
        flexible and removes the drudgery of writing branching code to
        handle different key presses. See the
        <link linkend="events-handling-key-events-discussion">Discussion</link>
        section for more details.</para>

        <para>To use this approach with an actor which will receive key press
        events, first get that actor's binding pool. In the example below,
        we're using the binding pool for the default
        <type>ClutterStage</type>:</para>

        <informalexample>
          <programlisting>
ClutterBindingPool *binding_pool;
GObjectClass *stage_class;

stage_class = CLUTTER_STAGE_GET_CLASS (stage);
binding_pool = clutter_binding_pool_get_for_class (stage_class);
          </programlisting>
        </informalexample>

        <para>Next, install actions into the binding pool. For example, to
        install an action bound to the up arrow key, which calls the
        <function>_move_up()</function> function when that key is pressed,
        you would do:</para>

        <informalexample>
          <programlisting>
clutter_binding_pool_install_action (binding_pool,
                                     "move-up",     /* identifier */
                                     CLUTTER_Up,    /* up arrow pressed */
                                     0,             /* no modifiers pressed */
                                     G_CALLBACK (_move_up),
                                     NULL,          /* no user data passed */
                                     NULL);
          </programlisting>
        </informalexample>

        <para>Another example, binding up arrow +
        <keycap>Shift</keycap> + <keycap>Ctrl</keycap> to an action
        which calls <function>_move_up_shift_control()</function> when
        activated:</para>

        <informalexample>
          <programlisting>
clutter_binding_pool_install_action (binding_pool,
                                     "move-up-shift-control",
                                     CLUTTER_Up,
                                     CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK,
                                     G_CALLBACK (_move_up_shift_control),
                                     NULL,
                                     NULL);
          </programlisting>
        </informalexample>

        <para>The function called when an action is activated looks
        like this (for <function>_move_up()</function>):</para>

        <informalexample>
          <programlisting>
static void
_move_up (GObject             *instance,
          const gchar         *action_name,
          guint                key_val,
          ClutterModifierType  modifiers,
          gpointer             user_data)
{
  g_debug ("Up pressed");
}
          </programlisting>
        </informalexample>

        <para>Then bind the <emphasis>key-press-event</emphasis> signal
        for the actor (in our case, the stage) to a callback:</para>

        <informalexample>
          <programlisting>
g_signal_connect (stage,
                  "key-press-event",
                  G_CALLBACK (_key_press_cb),
                  NULL);
          </programlisting>
        </informalexample>

        <para>Finally, inside the callback, pass control to the actor's
        binding pool rather than dissecting the key press event
        yourself:</para>

        <informalexample>
          <programlisting>
static gboolean
_key_press_cb (ClutterActor *actor,
             ClutterEvent *event,
             gpointer      user_data)
{
  ClutterBindingPool *pool;

  pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));

  return clutter_binding_pool_activate (pool,
                                        clutter_event_get_key_symbol (event),
                                        clutter_event_get_state (event),
                                        G_OBJECT (actor));
}
          </programlisting>
        </informalexample>

        <para>Now, when a key + modifiers that have been bound to an action
        are pressed on the actor, the appropriate action is activated.</para>

      </section>
    </section>

    <section id="events-handling-key-events-discussion">
      <title>Discussion</title>

      <section>
        <title>Pros and cons of Solution 1 and Solution 2</title>

        <para>Solution 1 is the simplest (in terms of the amount of code you
        have to write for simple cases), but could quickly turn into a mess if
        you need many conditions or want to capture many key combinations.
        Also, if multiple actors need to respond to key press events, you'll
        need similar event dissection code in each callback.</para>

        <para>Solution 2 is more complicated to implement, but scales better
        if you have many different key combinations on multiple actors.
        The binding pool protects you from the minutiae of detecting which
        keys were pressed, leaving you to concentrate on the
        triggered actions instead. This could simplify your control
        logic.</para>

        <para>In addition, Solution 2 lets you write a single callback to
        handle all key press events for all actors. This callback could then
        use <function>clutter_binding_pool_find()</function>
        (as in the example code) to determine which binding pool to
        activate (depending on which actor received the key press
        event).</para>

        <para>Finally, a binding pool allows you to block and unblock actions.
        This means you can make the response to a key press event conditional
        on application state. For example, let's say you wanted the up arrow
        key to move an actor, but only when the actor is at the bottom
        of the stage. To implement this, you could disable the up arrow key
        action in the binding pool initially; then, once the actor reaches the
        bottom of the stage, enable the up arrow key action again. While this
        is possible with Solution 1, you would have to implement more of the
        state management code yourself.</para>
      </section>

      <section>
        <title>Other useful things to know about key press events</title>

        <itemizedlist>
          <listitem>
            <para>A <type>ClutterKeyEvent</type> contains only a
            <emphasis>single</emphasis> key value, plus possibly one
            or more modifier keys (like <keycap>Shift</keycap>,
            <keycap>Ctrl</keycap>, <keycap>Alt</keycap> etc.).
            There are no functions in the Clutter API which return
            events for tracking near-simultaneous presses on multiple
            keys.</para>
          </listitem>

          <listitem>
            <para>By default, the stage receives all key events.
            To make another actor receive key events, use
            <function>clutter_stage_set_key_focus()</function>:</para>

            <informalexample>
              <programlisting>
/*
 * stage is a ClutterStage instance;
 * actor is the ClutterActor instance which should receive key events
 */
clutter_stage_set_key_focus (stage, actor);
              </programlisting>
            </informalexample>
          </listitem>
        </itemizedlist>

      </section>

    </section>

  </section>
</chapter>