2007-10-10 Matthew Allum <mallum@openedhand.com>
authorMatthew Allum <mallum@openedhand.com>
Wed, 10 Oct 2007 13:04:34 +0000 (13:04 +0000)
committerMatthew Allum <mallum@openedhand.com>
Wed, 10 Oct 2007 13:04:34 +0000 (13:04 +0000)
        * clutter/clutter-actor.c:
        * clutter/clutter-actor.h:
        * clutter/clutter-event.c:
        * clutter/clutter-main.c:
        * tests/test-events.c:
        Add basic W3 DOM  event 'capture' like functionality.

ChangeLog
clutter/clutter-actor.c
clutter/clutter-actor.h
clutter/clutter-event.c
clutter/clutter-main.c
clutter/clutter-stage.c
tests/test-events.c

index 386e249..ed6e37c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-10-10  Matthew Allum  <mallum@openedhand.com>
+
+       * clutter/clutter-actor.c:
+       * clutter/clutter-actor.h:
+       * clutter/clutter-event.c:
+       * clutter/clutter-main.c:
+       * tests/test-events.c:
+       Add basic W3 DOM  event 'capture' like functionality.
+
 2007-10-10  Emmanuele Bassi  <ebassi@openedhand.com>
 
        * clutter/clutter-script-private.h:
index 0e521ee..0524022 100644 (file)
@@ -106,6 +106,7 @@ enum
   PARENT_SET,
 
   EVENT,
+  EVENT_CAPTURED,
   EVENT_AFTER,
   BUTTON_PRESS_EVENT,
   BUTTON_RELEASE_EVENT,
@@ -117,7 +118,6 @@ enum
   FOCUS_OUT,
   ENTER_EVENT,
   LEAVE_EVENT,
-
   LAST_SIGNAL
 };
 
@@ -1469,9 +1469,10 @@ clutter_actor_class_init (ClutterActorClass *klass)
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (ClutterActorClass, enter),
-                 NULL, NULL,
-                 clutter_marshal_VOID__VOID,
-                 G_TYPE_NONE, 0);
+                 _clutter_boolean_handled_accumulator, NULL,
+                 clutter_marshal_BOOLEAN__BOXED,
+                 G_TYPE_BOOLEAN, 1,
+                 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   /**
    * ClutterActor::leave:
@@ -1486,10 +1487,28 @@ clutter_actor_class_init (ClutterActorClass *klass)
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (ClutterActorClass, leave),
-                 NULL, NULL,
-                 clutter_marshal_VOID__VOID,
-                 G_TYPE_NONE, 0);
+                 _clutter_boolean_handled_accumulator, NULL,
+                 clutter_marshal_BOOLEAN__BOXED,
+                 G_TYPE_BOOLEAN, 1,
+                 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
+  /**
+   * ClutterActor::captured-event:
+   * @actor: the actor which the pointer has left
+   *
+   * The ::leave signal is emitted when the pointer leaves the @actor.
+   *
+   * Since: 0.6
+   */
+  actor_signals[EVENT_CAPTURED] =
+    g_signal_new ("captured-event",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (ClutterActorClass, captured),
+                 _clutter_boolean_handled_accumulator, NULL,
+                 clutter_marshal_BOOLEAN__BOXED,
+                 G_TYPE_BOOLEAN, 1,
+                 CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   klass->show = clutter_actor_real_show;
   klass->show_all = clutter_actor_show;
@@ -2977,6 +2996,7 @@ clutter_actor_lower_bottom (ClutterActor *self)
  * clutter_actor_event:
  * @actor: a #ClutterActor
  * @event: a #ClutterEvent
+ * @capture: TRUE if event in in capture phase, FALSE otherwise.
  *
  * This function is used to emit an event on the main stage.
  * You should rarely need to use this function, except for
@@ -2988,7 +3008,8 @@ clutter_actor_lower_bottom (ClutterActor *self)
  */
 gboolean
 clutter_actor_event (ClutterActor *actor,
-                     ClutterEvent *event)
+                     ClutterEvent *event,
+                    gboolean      capture)
 {
   gboolean retval = TRUE;
   gint signal_num = -1;
@@ -2997,51 +3018,64 @@ clutter_actor_event (ClutterActor *actor,
   g_return_val_if_fail (event != NULL, FALSE);
 
   g_object_ref (actor);
+
+  if (capture)
+    {
+      g_signal_emit (actor, actor_signals[EVENT_CAPTURED], 0, 
+                    event, &retval);
+      goto out;
+    }
   
   g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
 
   if (!retval)
     {
       switch (event->type)
-        {
-        case CLUTTER_NOTHING:
-          break;
-        case CLUTTER_BUTTON_PRESS:
-          signal_num = BUTTON_PRESS_EVENT;
-          break;
-        case CLUTTER_BUTTON_RELEASE:
-          signal_num = BUTTON_RELEASE_EVENT;
-          break;
-        case CLUTTER_SCROLL:
-          signal_num = SCROLL_EVENT;
-          break;
-        case CLUTTER_KEY_PRESS:
-          signal_num = KEY_PRESS_EVENT;
-          break;
-        case CLUTTER_KEY_RELEASE:
-          signal_num = KEY_RELEASE_EVENT;
-          break;
-        case CLUTTER_MOTION:
-          signal_num = MOTION_EVENT;
-          break;
-        case CLUTTER_ENTER:
-          signal_num = ENTER_EVENT;
-          break;
-        case CLUTTER_LEAVE:
-          signal_num = LEAVE_EVENT;
-          break;
-        case CLUTTER_DELETE:
-        case CLUTTER_DESTROY_NOTIFY:
-        case CLUTTER_CLIENT_MESSAGE:
-        default:
-          signal_num = -1;
-          break;
-        }
+       {
+       case CLUTTER_NOTHING:
+         break;
+       case CLUTTER_BUTTON_PRESS:
+         signal_num = BUTTON_PRESS_EVENT;
+         break;
+       case CLUTTER_BUTTON_RELEASE:
+         signal_num = BUTTON_RELEASE_EVENT;
+         break;
+       case CLUTTER_SCROLL:
+         signal_num = SCROLL_EVENT;
+         break;
+       case CLUTTER_KEY_PRESS:
+         signal_num = KEY_PRESS_EVENT;
+         break;
+       case CLUTTER_KEY_RELEASE:
+         signal_num = KEY_RELEASE_EVENT;
+         break;
+       case CLUTTER_MOTION:
+         signal_num = MOTION_EVENT;
+         break;
+       case CLUTTER_ENTER:
+         signal_num = ENTER_EVENT;
+         break;
+       case CLUTTER_LEAVE:
+         signal_num = LEAVE_EVENT;
+         break;
+       case CLUTTER_DELETE:
+       case CLUTTER_DESTROY_NOTIFY:
+       case CLUTTER_CLIENT_MESSAGE:
+       default:
+         signal_num = -1;
+         break;
+       }
+
       if (signal_num != -1)
-        g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval);
+       g_signal_emit (actor, actor_signals[signal_num], 0, 
+                      event, &retval);
     }
 
-  g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
+
+  if (!retval)
+    g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
+
+ out:
 
   g_object_unref (actor);
 
index 475d6a9..fc36c38 100644 (file)
@@ -211,6 +211,8 @@ struct _ClutterActorClass
                                     ClutterCrossingEvent *event);
   void     (* leave)                (ClutterActor         *actor,
                                     ClutterCrossingEvent *event);
+  void     (* captured)             (ClutterActor         *actor,
+                                    ClutterEvent         *event);
   void     (* focus_in)             (ClutterActor       *actor);
   void     (* focus_out)            (ClutterActor       *actor);
 
@@ -367,7 +369,8 @@ void                  clutter_actor_apply_transform_to_point (ClutterActor
 /* Per actor event handling - may change  */
 gboolean
 clutter_actor_event (ClutterActor *actor,
-                     ClutterEvent *event);
+                     ClutterEvent *event,
+                    gboolean      capture);
 void
 clutter_actor_set_reactive (ClutterActor *actor);
 
index 03d69af..2b13033 100644 (file)
@@ -379,10 +379,8 @@ clutter_event_free (ClutterEvent *event)
   if (G_LIKELY (event))
     {
       ClutterActor *source = NULL;
-
-      source = clutter_event_get_source (event);
-      if (source)
-       g_object_unref (source);
+      if (event->type == CLUTTER_LEAVE || event->type == CLUTTER_ENTER)
+       g_object_unref (event->crossing.related);
       g_slice_free (ClutterEvent, event);
     }
 }
index b653136..003da3c 100644 (file)
@@ -1133,6 +1133,56 @@ event_click_count_generate (ClutterEvent *event)
     }
 }
 
+static void 
+deliver_event (ClutterEvent *event, ClutterActor *source)
+{
+#define MAX_EVENT_DEPTH 512
+
+  static ClutterActor  **event_tree = NULL;
+  static gboolean        lock = FALSE;
+
+  ClutterActor          *actor;
+  gint                   i = 0, n_tree_events = 0;
+
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (lock == FALSE);
+
+  lock = TRUE; /* Guard against reentrancy */
+
+  /* Sorry Mr Bassi. */
+  if (event_tree == NULL)
+    event_tree = g_new0(ClutterActor*, MAX_EVENT_DEPTH);
+
+  actor = source;
+
+  /* Build 'tree' of events */
+  while (actor && n_tree_events < MAX_EVENT_DEPTH)
+    {
+      if (clutter_actor_is_reactive (actor) ||
+         clutter_actor_get_parent (actor) == NULL)
+       event_tree[n_tree_events++] = g_object_ref (actor);
+
+      actor = clutter_actor_get_parent (actor);
+    }
+
+  /* Capture */
+  for (i=n_tree_events-1; i >= 0; i--)
+      if (clutter_actor_event (event_tree[i], event, TRUE))
+       goto done;
+
+  /* Bubble */
+  for (i=0; i < n_tree_events; i++)
+    if (clutter_actor_event (event_tree[i], event, FALSE))
+      goto done;
+
+ done:
+
+  for (i=0; i < n_tree_events; i++)
+    g_object_unref (event_tree[i]);
+
+  lock = FALSE;
+}
+
 /** 
  * clutter_do_event
  * @event: a #ClutterEvent.
@@ -1151,6 +1201,7 @@ clutter_do_event (ClutterEvent *event)
   ClutterMainContext  *context;
   ClutterBackend      *backend;
   ClutterActor        *stage;
+
   static ClutterActor *motion_last_actor = NULL; 
 
   context = clutter_context_get_default ();
@@ -1175,18 +1226,7 @@ clutter_do_event (ClutterEvent *event)
 
        g_return_if_fail (actor != NULL);
 
-       while (actor)
-         {
-           if (clutter_actor_is_reactive (actor) ||
-                clutter_actor_get_parent (actor) == NULL /* STAGE */ )
-             {
-               CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
-               if (clutter_actor_event (actor, event))
-                  break;
-             }
-
-           actor = clutter_actor_get_parent (actor);
-         }
+       deliver_event (event, actor);
       }
       break;
     case CLUTTER_DESTROY_NOTIFY:
@@ -1203,25 +1243,16 @@ clutter_do_event (ClutterEvent *event)
 
        g_return_if_fail (actor != NULL);
 
-       event->key.source = g_object_ref (actor);
-
-       /* bubble up */
-       do
-         {
-           if (clutter_actor_event (actor, event))
-              break;
-
-           actor = clutter_actor_get_parent (actor);
-         }
-       while (actor != NULL);
+       event->key.source = actor;
+       deliver_event (event, actor);
       }
       break;
     case CLUTTER_MOTION:
       if (context->motion_events_per_actor == FALSE)
        {
          /* Only stage gets motion events */
-         event->motion.source = g_object_ref (stage);
-         clutter_actor_event (stage, event);
+         event->motion.source = stage;
+         clutter_actor_event (stage, event, FALSE);
          break;
        }
     case CLUTTER_BUTTON_PRESS:
@@ -1246,15 +1277,20 @@ clutter_do_event (ClutterEvent *event)
                                  x, y, 
                                  CLUTTER_PICK_REACTIVE);
 
+        /* FIXME: for an optimisation should check if there are
+        * actually any reactive actors and avoid the pick all togeather
+        * (signalling just the stage). Should be big help for gles.
+        */
+
        CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p", 
                      x, y, actor);
 
        g_return_if_fail (actor != NULL);
 
        if (event->type == CLUTTER_SCROLL)
-         event->scroll.source = g_object_ref (actor);
+         event->scroll.source = actor;
        else
-         event->button.source = g_object_ref (actor);
+         event->button.source = actor;
 
        /* Motion enter leave events */
        if (event->type == CLUTTER_MOTION)
@@ -1270,7 +1306,8 @@ clutter_do_event (ClutterEvent *event)
                    cev.crossing.flags   = 0; 
                    cev.crossing.x       = x;
                    cev.crossing.y       = y;
-                   cev.crossing.source  = g_object_ref (motion_last_actor);
+                   cev.crossing.source  = motion_last_actor;
+                   /* unref in free  */
                    cev.crossing.related = g_object_ref (actor);
 
                    clutter_event_put (&cev); /* copys */
@@ -1280,7 +1317,7 @@ clutter_do_event (ClutterEvent *event)
                    cev.crossing.flags   = 0; 
                    cev.crossing.x       = x;
                    cev.crossing.y       = y;
-                   cev.crossing.source  = g_object_ref (actor);
+                   cev.crossing.source  = actor;
                    cev.crossing.related = g_object_ref (motion_last_actor);
 
                    clutter_event_put (&cev);
@@ -1291,27 +1328,7 @@ clutter_do_event (ClutterEvent *event)
        else
          event_click_count_generate (event);
        
-       /* Send the event to the actor and all parents always the 
-        * stage.  
-         *
-        * FIXME: for an optimisation should check if there are
-        * actually any reactive actors and avoid the pick all togeather
-        * (signalling just the stage). Should be big help for gles.
-        *
-        * FIXME: Actors be able to stop emission.
-        */
-       while (actor)
-         {
-           if (clutter_actor_is_reactive (actor) ||
-                clutter_actor_get_parent (actor) == NULL /* STAGE */ )
-             {
-               CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
-               if (clutter_actor_event (actor, event))
-                  break;
-             }
-
-           actor = clutter_actor_get_parent (actor);
-         }
+       deliver_event (event, actor);
       }
       break;
     case CLUTTER_STAGE_STATE:
index c5344e2..ee36601 100644 (file)
@@ -828,7 +828,7 @@ clutter_stage_event (ClutterStage *stage,
     return FALSE;
 
   /* emit raw event */
-  if (clutter_actor_event (CLUTTER_ACTOR (stage), event))
+  if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
     return TRUE;
 
   if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
index 13c232a..fbc6aaa 100644 (file)
@@ -42,6 +42,18 @@ red_button_cb (ClutterActor    *actor,
     IsMotion = TRUE;
 
   clutter_enable_motion_events (IsMotion);
+
+  return FALSE;
+}
+
+static gboolean
+capture_cb (ClutterActor    *actor,
+           ClutterEvent    *event,
+           gpointer         data)
+{
+  printf("captured event for type '%s'\n", G_OBJECT_TYPE_NAME(actor));
+
+  return FALSE;
 }
 
 static void
@@ -152,6 +164,7 @@ main (int argc, char *argv[])
 
   stage = clutter_stage_get_default ();
   g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
+
   g_signal_connect (stage, "fullscreen", 
                    G_CALLBACK (stage_state_cb), "fullscreen");
   g_signal_connect (stage, "unfullscreen", 
@@ -161,6 +174,8 @@ main (int argc, char *argv[])
   g_signal_connect (stage, "deactivate", 
                    G_CALLBACK (stage_state_cb), "deactivate");
 
+  g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);
+
   focus_box = clutter_rectangle_new_with_color (&ncol);
   clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
 
@@ -193,6 +208,8 @@ main (int argc, char *argv[])
   g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), 
                    focus_box);
 
+  g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL);
+
   actor = clutter_rectangle_new_with_color (&bcol);
   clutter_actor_set_size (actor, 100, 100);
   clutter_actor_set_position (actor, 400, 100);