diff --git a/lib/wp/event-dispatcher.c b/lib/wp/event-dispatcher.c index d99e1d83..430e2558 100644 --- a/lib/wp/event-dispatcher.c +++ b/lib/wp/event-dispatcher.c @@ -49,7 +49,8 @@ struct _WpEventDispatcher GObject parent; GWeakRef core; - GPtrArray *hooks; /* registered hooks */ + GHashTable *defined_hooks; /* registered hooks for defined events */ + GPtrArray *undefined_hooks; /* registered hooks for undefined events */ GSource *source; /* the event loop source */ GList *events; /* the events stack */ struct spa_system *system; @@ -160,7 +161,9 @@ static void wp_event_dispatcher_init (WpEventDispatcher * self) { g_weak_ref_init (&self->core, NULL); - self->hooks = g_ptr_array_new_with_free_func (g_object_unref); + self->defined_hooks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_ptr_array_unref); + self->undefined_hooks = g_ptr_array_new_with_free_func (g_object_unref); self->source = g_source_new (&source_funcs, sizeof (WpEventSource)); ((WpEventSource *) self->source)->dispatcher = self; @@ -184,7 +187,8 @@ wp_event_dispatcher_finalize (GObject * object) close (self->eventfd); - g_clear_pointer (&self->hooks, g_ptr_array_unref); + g_clear_pointer (&self->defined_hooks, g_hash_table_unref); + g_clear_pointer (&self->undefined_hooks, g_ptr_array_unref); g_weak_ref_clear (&self->core); G_OBJECT_CLASS (wp_event_dispatcher_parent_class)->finalize (object); @@ -284,6 +288,10 @@ void wp_event_dispatcher_register_hook (WpEventDispatcher * self, WpEventHook * hook) { + g_autoptr (GPtrArray) event_types = NULL; + gboolean is_defined = FALSE; + const gchar *hook_name; + g_return_if_fail (WP_IS_EVENT_DISPATCHER (self)); g_return_if_fail (WP_IS_EVENT_HOOK (hook)); @@ -292,7 +300,57 @@ wp_event_dispatcher_register_hook (WpEventDispatcher * self, g_return_if_fail (already_registered_dispatcher == NULL); wp_event_hook_set_dispatcher (hook, self); - g_ptr_array_add (self->hooks, g_object_ref (hook)); + + /* Register the event hook in the defined hooks table if it is defined */ + hook_name = wp_event_hook_get_name (hook); + event_types = wp_event_hook_get_matching_event_types (hook); + if (event_types) { + for (guint i = 0; i < event_types->len; i++) { + const gchar *event_type = g_ptr_array_index (event_types, i); + GPtrArray *hooks; + + wp_debug_object (self, "Registering hook %s for defined event type %s", + hook_name, event_type); + + /* Check if the event type was registered in the hash table */ + hooks = g_hash_table_lookup (self->defined_hooks, event_type); + if (hooks) { + g_ptr_array_add (hooks, g_object_ref (hook)); + } else { + GPtrArray *new_hooks = g_ptr_array_new_with_free_func (g_object_unref); + /* Add undefined hooks */ + for (guint i = 0; i < self->undefined_hooks->len; i++) { + WpEventHook *uh = g_ptr_array_index (self->undefined_hooks, i); + g_ptr_array_add (new_hooks, g_object_ref (uh)); + } + /* Add current hook */ + g_ptr_array_add (new_hooks, g_object_ref (hook)); + g_hash_table_insert (self->defined_hooks, g_strdup (event_type), + new_hooks); + } + + is_defined = TRUE; + } + } + + /* Otherwise just register it as undefined hook */ + if (!is_defined) { + GHashTableIter iter; + gpointer value; + + wp_debug_object (self, "Registering hook %s for undefined event types", + hook_name); + + /* Add it to the defined hooks table */ + g_hash_table_iter_init (&iter, self->defined_hooks); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + GPtrArray *defined_hooks = value; + g_ptr_array_add (defined_hooks, g_object_ref (hook)); + } + + /* Add it to the undefined hooks */ + g_ptr_array_add (self->undefined_hooks, g_object_ref (hook)); + } } /*! @@ -306,6 +364,9 @@ void wp_event_dispatcher_unregister_hook (WpEventDispatcher * self, WpEventHook * hook) { + GHashTableIter iter; + gpointer value; + g_return_if_fail (WP_IS_EVENT_DISPATCHER (self)); g_return_if_fail (WP_IS_EVENT_HOOK (hook)); @@ -314,11 +375,29 @@ wp_event_dispatcher_unregister_hook (WpEventDispatcher * self, g_return_if_fail (already_registered_dispatcher == self); wp_event_hook_set_dispatcher (hook, NULL); - g_ptr_array_remove_fast (self->hooks, hook); + + /* Remove hook from defined table and undefined list */ + g_hash_table_iter_init (&iter, self->defined_hooks); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + GPtrArray *defined_hooks = value; + g_ptr_array_remove (defined_hooks, hook); + } + g_ptr_array_remove (self->undefined_hooks, hook); +} + +static void +add_unique (GPtrArray *array, WpEventHook * hook) +{ + for (guint i = 0; i < array->len; i++) + if (g_ptr_array_index (array, i) == hook) + return; + g_ptr_array_add (array, g_object_ref (hook)); } /*! * \brief Returns an iterator to iterate over all the registered hooks + * \deprecated Use \ref wp_event_dispatcher_new_hooks_for_event_type_iterator + * instead. * \ingroup wpeventdispatcher * * \param self the event dispatcher @@ -327,7 +406,56 @@ wp_event_dispatcher_unregister_hook (WpEventDispatcher * self, WpIterator * wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self) { - GPtrArray *items = - g_ptr_array_copy (self->hooks, (GCopyFunc) g_object_ref, NULL); + GPtrArray *items = g_ptr_array_new_with_free_func (g_object_unref); + GHashTableIter iter; + gpointer value; + + /* Add all defined hooks */ + g_hash_table_iter_init (&iter, self->defined_hooks); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + GPtrArray *hooks = value; + for (guint i = 0; i < hooks->len; i++) { + WpEventHook *hook = g_ptr_array_index (hooks, i); + add_unique (items, hook); + } + } + + /* Add all undefined hooks */ + for (guint i = 0; i < self->undefined_hooks->len; i++) { + WpEventHook *hook = g_ptr_array_index (self->undefined_hooks, i); + add_unique (items, hook); + } + + return wp_iterator_new_ptr_array (items, WP_TYPE_EVENT_HOOK); +} + +/*! + * \brief Returns an iterator to iterate over the registered hooks for a + * particular event type. + * \ingroup wpeventdispatcher + * + * \param self the event dispatcher + * \param event_type the event type + * \return (transfer full): a new iterator + * \since 0.5.13 + */ +WpIterator * +wp_event_dispatcher_new_hooks_for_event_type_iterator ( + WpEventDispatcher * self, const gchar *event_type) +{ + GPtrArray *items; + GPtrArray *hooks; + + hooks = g_hash_table_lookup (self->defined_hooks, event_type); + if (hooks) { + wp_debug_object (self, "Using %d defined hooks for event type %s", + hooks->len, event_type); + } else { + hooks = self->undefined_hooks; + wp_debug_object (self, "Using %d undefined hooks for event type %s", + hooks->len, event_type); + } + + items = g_ptr_array_copy (hooks, (GCopyFunc) g_object_ref, NULL); return wp_iterator_new_ptr_array (items, WP_TYPE_EVENT_HOOK); } diff --git a/lib/wp/event-dispatcher.h b/lib/wp/event-dispatcher.h index 94c13a86..d9801c28 100644 --- a/lib/wp/event-dispatcher.h +++ b/lib/wp/event-dispatcher.h @@ -41,7 +41,12 @@ void wp_event_dispatcher_unregister_hook (WpEventDispatcher * self, WpEventHook * hook); WP_API -WpIterator * wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self); +WpIterator * wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self) + G_GNUC_DEPRECATED_FOR (wp_event_dispatcher_new_hooks_for_event_type_iterator); + +WP_API +WpIterator * wp_event_dispatcher_new_hooks_for_event_type_iterator ( + WpEventDispatcher * self, const gchar *event_type); G_END_DECLS diff --git a/lib/wp/event-hook.c b/lib/wp/event-hook.c index d1e04049..921bb934 100644 --- a/lib/wp/event-hook.c +++ b/lib/wp/event-hook.c @@ -254,6 +254,24 @@ wp_event_hook_run (WpEventHook * self, callback_data); } +/*! + * \brief Gets all the matching event types for this hook if any. + * + * \ingroup wpeventhook + * \param self the event hook + * \returns (element-type gchar*) (transfer full) (nullable): the matching + * event types for this hook if any. + * \since 0.5.13 + */ +GPtrArray * +wp_event_hook_get_matching_event_types (WpEventHook * self) +{ + g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL); + g_return_val_if_fail ( + WP_EVENT_HOOK_GET_CLASS (self)->get_matching_event_types, NULL); + return WP_EVENT_HOOK_GET_CLASS (self)->get_matching_event_types (self); +} + /*! * \brief Finishes the async operation that was started by wp_event_hook_run() * @@ -334,6 +352,50 @@ wp_interest_event_hook_runs_for_event (WpEventHook * hook, WpEvent * event) return FALSE; } +static void +add_unique (GPtrArray *array, const gchar * lookup) +{ + for (guint i = 0; i < array->len; i++) + if (g_str_equal (g_ptr_array_index (array, i), lookup)) + return; + g_ptr_array_add (array, g_strdup (lookup)); +} + +static GPtrArray * +wp_interest_event_hook_get_matching_event_types (WpEventHook * hook) +{ + WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (hook); + WpInterestEventHookPrivate *priv = + wp_interest_event_hook_get_instance_private (self); + GPtrArray *res = g_ptr_array_new_with_free_func (g_free); + guint i; + + for (i = 0; i < priv->interests->len; i++) { + WpObjectInterest *interest = g_ptr_array_index (priv->interests, i); + if (wp_object_interest_matches_full (interest, WP_INTEREST_MATCH_FLAGS_NONE, + WP_TYPE_EVENT, NULL, NULL, NULL) & WP_INTEREST_MATCH_GTYPE) { + g_autoptr (GPtrArray) values = + wp_object_interest_find_defined_constraint_values (interest, + WP_CONSTRAINT_TYPE_NONE, "event.type"); + if (!values || values->len == 0) { + /* We always consider the hook undefined if it has at least one interest + * without a defined 'event.type' constraint */ + return NULL; + } else { + for (guint j = 0; j < values->len; j++) { + GVariant *v = g_ptr_array_index (values, j); + if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) { + const gchar *v_str = g_variant_get_string (v, NULL); + add_unique (res, v_str); + } + } + } + } + } + + return res; +} + static void wp_interest_event_hook_class_init (WpInterestEventHookClass * klass) { @@ -342,6 +404,8 @@ wp_interest_event_hook_class_init (WpInterestEventHookClass * klass) object_class->finalize = wp_interest_event_hook_finalize; hook_class->runs_for_event = wp_interest_event_hook_runs_for_event; + hook_class->get_matching_event_types = + wp_interest_event_hook_get_matching_event_types; } /*! diff --git a/lib/wp/event-hook.h b/lib/wp/event-hook.h index 1cf8b66d..9ec4447f 100644 --- a/lib/wp/event-hook.h +++ b/lib/wp/event-hook.h @@ -39,8 +39,10 @@ struct _WpEventHookClass gboolean (*finish) (WpEventHook * self, GAsyncResult * res, GError ** error); + GPtrArray * (*get_matching_event_types) (WpEventHook *self); + /*< private >*/ - WP_PADDING(5) + WP_PADDING(4) }; WP_API @@ -67,6 +69,9 @@ void wp_event_hook_run (WpEventHook * self, WpEvent * event, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer callback_data); +WP_API +GPtrArray * wp_event_hook_get_matching_event_types (WpEventHook * self); + WP_API gboolean wp_event_hook_finish (WpEventHook * self, GAsyncResult * res, GError ** error); diff --git a/lib/wp/event.c b/lib/wp/event.c index 046b0c11..1070df65 100644 --- a/lib/wp/event.c +++ b/lib/wp/event.c @@ -358,6 +358,7 @@ wp_event_collect_hooks (WpEvent * event, WpEventDispatcher * dispatcher) struct spa_list collected, result, remaining; g_autoptr (WpIterator) all_hooks = NULL; g_auto (GValue) value = G_VALUE_INIT; + const gchar *event_type = NULL; g_return_val_if_fail (event != NULL, FALSE); g_return_val_if_fail (WP_IS_EVENT_DISPATCHER (dispatcher), FALSE); @@ -370,8 +371,14 @@ wp_event_collect_hooks (WpEvent * event, WpEventDispatcher * dispatcher) spa_list_init (&result); spa_list_init (&remaining); + /* Get the event type */ + event_type = wp_properties_get (event->properties, "event.type"); + wp_debug_object (dispatcher, "Collecting hooks for event %s with type %s", + event->name, event_type); + /* collect hooks that run for this event */ - all_hooks = wp_event_dispatcher_new_hooks_iterator (dispatcher); + all_hooks = wp_event_dispatcher_new_hooks_for_event_type_iterator (dispatcher, + event_type); while (wp_iterator_next (all_hooks, &value)) { WpEventHook *hook = g_value_get_object (&value); diff --git a/lib/wp/object-interest.c b/lib/wp/object-interest.c index e575167f..067360af 100644 --- a/lib/wp/object-interest.c +++ b/lib/wp/object-interest.c @@ -881,3 +881,51 @@ wp_object_interest_matches_full (WpObjectInterest * self, } return result; } + +/*! + * \brief Finds all the defined constraint values for a subject in \a self. + * + * A defined constraint value is the value of a constraint with the 'equal' or + * 'in-list' verb, because the full value must be defined with those verbs. This + * can be useful for cases where we want to enumerate interests that are + * interested in specific subjects. + * + * \ingroup wpobjectinterest + * \param self the object interest + * \param type the constraint type + * \param subject the subject that the constraint applies to + * \returns (element-type GVariant) (transfer full) (nullable): the defined + * constraint values for this object interest. + * \since 0.5.13 + */ +GPtrArray * +wp_object_interest_find_defined_constraint_values (WpObjectInterest * self, + WpConstraintType type, const gchar * subject) +{ + GPtrArray *res = g_ptr_array_new_with_free_func ( + (GDestroyNotify)g_variant_unref); + struct constraint *c; + + pw_array_for_each (c, &self->constraints) { + if ((c->type == type || WP_CONSTRAINT_TYPE_NONE == type) && + g_str_equal (c->subject, subject)) { + switch (c->verb) { + case WP_CONSTRAINT_VERB_EQUALS: + g_ptr_array_add (res, g_variant_ref (c->value)); + break; + case WP_CONSTRAINT_VERB_IN_LIST: { + GVariantIter iter; + GVariant *child; + g_variant_iter_init (&iter, c->value); + while ((child = g_variant_iter_next_value (&iter))) + g_ptr_array_add (res, child); + break; + } + default: + break; + } + } + } + + return res; +} diff --git a/lib/wp/object-interest.h b/lib/wp/object-interest.h index 315fac52..abda2b8f 100644 --- a/lib/wp/object-interest.h +++ b/lib/wp/object-interest.h @@ -130,6 +130,10 @@ WpInterestMatch wp_object_interest_matches_full (WpObjectInterest * self, WpInterestMatchFlags flags, GType object_type, gpointer object, WpProperties * pw_props, WpProperties * pw_global_props); +WP_API +GPtrArray * wp_object_interest_find_defined_constraint_values ( + WpObjectInterest * self, WpConstraintType type, const gchar * subject); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpObjectInterest, wp_object_interest_unref) G_END_DECLS