diff --git a/clutter/clutter-event-private.h b/clutter/clutter-event-private.h index 955db260e..00d627d12 100644 --- a/clutter/clutter-event-private.h +++ b/clutter/clutter-event-private.h @@ -11,6 +11,8 @@ void _clutter_event_set_pointer_emulated (ClutterEvent *eve /* Reinjecting queued events for processing */ void _clutter_process_event (ClutterEvent *event); +gboolean _clutter_event_process_filters (ClutterEvent *event); + /* clears the event queue inside the main context */ void _clutter_clear_events_queue (void); void _clutter_clear_events_queue_for_stage (ClutterStage *stage); diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 03655f6c3..06870c61d 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -64,6 +64,15 @@ typedef struct _ClutterEventPrivate { guint is_pointer_emulated : 1; } ClutterEventPrivate; +typedef struct _ClutterEventFilter { + int id; + + ClutterStage *stage; + ClutterEventFilterFunc func; + GDestroyNotify notify; + gpointer user_data; +} ClutterEventFilter; + static GHashTable *all_events = NULL; G_DEFINE_BOXED_TYPE (ClutterEvent, clutter_event, @@ -1720,3 +1729,100 @@ clutter_event_is_pointer_emulated (const ClutterEvent *event) return ((ClutterEventPrivate *) event)->is_pointer_emulated; } + +gboolean +_clutter_event_process_filters (ClutterEvent *event) +{ + ClutterMainContext *context = _clutter_context_get_default (); + GList *l, *next; + + /* Event filters are handled in order from least recently added to + * most recently added */ + + for (l = context->event_filters; l; l = next) + { + ClutterEventFilter *event_filter = l->data; + + next = l->next; + + if (event_filter->stage && event_filter->stage != event->any.stage) + continue; + + if (event_filter->func (event, event_filter->user_data) == CLUTTER_EVENT_STOP) + return CLUTTER_EVENT_STOP; + } + + return CLUTTER_EVENT_PROPAGATE; +} + +/** + * clutter_event_add_filter: + * @stage: (allow-none): The #ClutterStage to capture events for + * @func: The callback function which will be passed all events. + * @notify: A #GDestroyNotify + * @user_data: A data pointer to pass to the function. + * + * Adds a function which will be called for all events that Clutter + * processes. The function will be called before any signals are + * emitted for the event and it will take precedence over any grabs. + * + * Return value: an identifier for the event filter, to be used + * with clutter_event_remove_filter(). + * + * Since: 1.18 + */ +guint +clutter_event_add_filter (ClutterStage *stage, + ClutterEventFilterFunc func, + GDestroyNotify notify, + gpointer user_data) +{ + ClutterMainContext *context = _clutter_context_get_default (); + ClutterEventFilter *event_filter = g_slice_new (ClutterEventFilter); + static guint event_filter_id = 0; + + event_filter->stage = stage; + event_filter->id = ++event_filter_id; + event_filter->func = func; + event_filter->notify = notify; + event_filter->user_data = user_data; + + /* The event filters are kept in order from least recently added to + * most recently added so we must add it to the end */ + context->event_filters = g_list_append (context->event_filters, event_filter); + + return event_filter->id; +} + +/** + * clutter_event_remove_filter: + * @id: The ID of the event filter, as returned from clutter_event_add_filter() + * + * Removes an event filter that was previously added with + * clutter_event_add_filter(). + * + * Since: 1.18 + */ +void +clutter_event_remove_filter (guint id) +{ + ClutterMainContext *context = _clutter_context_get_default (); + GList *l; + + for (l = context->event_filters; l; l = l->next) + { + ClutterEventFilter *event_filter = l->data; + + if (event_filter->id == id) + { + if (event_filter->notify) + event_filter->notify (event_filter->user_data); + + context->event_filters = g_list_delete_link (context->event_filters, l); + g_slice_free (ClutterEventFilter, event_filter); + return; + } + } + + g_warning ("No event filter found for id: %d\n", id); +} diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 30e84d988..f41375c78 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -405,6 +405,24 @@ union _ClutterEvent ClutterTouchEvent touch; }; +/** + * ClutterEventFilterFunc: + * @event: the event that is going to be emitted + * @user_data: the data pointer passed to clutter_event_add_filter() + * + * A function pointer type used by event filters that are added with + * clutter_event_add_filter(). + * + * Return value: %CLUTTER_EVENT_STOP to indicate that the event + * has been handled or %CLUTTER_EVENT_PROPAGATE otherwise. + * Returning %CLUTTER_EVENT_STOP skips any further filter + * functions and prevents the signal emission for the event. + * + * Since: 1.18 + */ +typedef gboolean (* ClutterEventFilterFunc) (const ClutterEvent *event, + gpointer user_data); + GType clutter_event_get_type (void) G_GNUC_CONST; gboolean clutter_events_pending (void); @@ -412,6 +430,14 @@ ClutterEvent * clutter_event_get (void); ClutterEvent * clutter_event_peek (void); void clutter_event_put (const ClutterEvent *event); +CLUTTER_AVAILABLE_IN_1_18 +guint clutter_event_add_filter (ClutterStage *stage, + ClutterEventFilterFunc func, + GDestroyNotify notify, + gpointer user_data); +CLUTTER_AVAILABLE_IN_1_18 +void clutter_event_remove_filter (guint id); + ClutterEvent * clutter_event_new (ClutterEventType type); ClutterEvent * clutter_event_copy (const ClutterEvent *event); void clutter_event_free (ClutterEvent *event); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index ef83665d9..6363ad9e2 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2279,6 +2279,9 @@ emit_pointer_event (ClutterEvent *event, { ClutterMainContext *context = _clutter_context_get_default (); + if (_clutter_event_process_filters (event)) + return; + if (context->pointer_grab_actor == NULL && (device == NULL || device->pointer_grab_actor == NULL)) { @@ -2306,6 +2309,9 @@ emit_touch_event (ClutterEvent *event, { ClutterActor *grab_actor = NULL; + if (_clutter_event_process_filters (event)) + return; + if (device->sequence_grab_actors != NULL) { grab_actor = g_hash_table_lookup (device->sequence_grab_actors, @@ -2330,6 +2336,9 @@ emit_keyboard_event (ClutterEvent *event, { ClutterMainContext *context = _clutter_context_get_default (); + if (_clutter_event_process_filters (event)) + return; + if (context->keyboard_grab_actor == NULL && (device == NULL || device->keyboard_grab_actor == NULL)) { @@ -2493,6 +2502,10 @@ _clutter_process_event_details (ClutterActor *stage, case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DELETE: event->any.source = stage; + + if (_clutter_event_process_filters (event)) + break; + /* the stage did not handle the event, so we just quit */ clutter_stage_event (CLUTTER_STAGE (stage), event); break; @@ -2505,6 +2518,9 @@ _clutter_process_event_details (ClutterActor *stage, /* Only stage gets motion events */ event->any.source = stage; + if (_clutter_event_process_filters (event)) + break; + /* global grabs */ if (context->pointer_grab_actor != NULL) { @@ -2632,6 +2648,9 @@ _clutter_process_event_details (ClutterActor *stage, /* Only stage gets motion events */ event->any.source = stage; + if (_clutter_event_process_filters (event)) + break; + /* global grabs */ if (device->sequence_grab_actors != NULL) { @@ -2735,7 +2754,8 @@ _clutter_process_event_details (ClutterActor *stage, case CLUTTER_STAGE_STATE: /* fullscreen / focus - forward to stage */ event->any.source = stage; - clutter_stage_event (CLUTTER_STAGE (stage), event); + if (!_clutter_event_process_filters (event)) + clutter_stage_event (CLUTTER_STAGE (stage), event); break; case CLUTTER_CLIENT_MESSAGE: diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 2136efe75..65154926f 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -137,6 +137,10 @@ struct _ClutterMainContext /* the main event queue */ GQueue *events_queue; + /* the event filters added via clutter_event_add_filter. these are + * ordered from least recently added to most recently added */ + GList *event_filters; + ClutterPickMode pick_mode; /* mapping between reused integer ids and actors */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 6029cef15..d3c7f5bd1 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1132,6 +1132,9 @@ clutter_event_get clutter_event_peek clutter_event_put clutter_events_pending +ClutterEventFilterFunc +clutter_event_add_filter +clutter_event_remove_filter CLUTTER_BUTTON_PRIMARY