event-dispatcher: refactor to use before/after dependencies on hooks
* Remove entirely the hook priority numbers and use before/after dependencies * Split the WpEvent code out of WpEventDispatcher * Add methods on WpEvent to interface with it from the WpEventDispatcher. As a bonus, we can now also implement tooling to inspect which hooks would in theory run for an event and write tests around that * Removed some internal debugging facilities and log calls, will redo it later. * Using spa_list now for the list of hooks, to reduce the number of allocations happening in the "hook collection" algorithm * Switched some internal data to use g_new0 instead of g_slice_new0 * Added g_free to free WpEvent structures... surprisingly, we were leaking them before
This commit is contained in:

committed by
Julian Bouzas

parent
5a435fae11
commit
3a23fb451a
@@ -9,270 +9,36 @@
|
|||||||
#define G_LOG_DOMAIN "wp-event-dispatcher"
|
#define G_LOG_DOMAIN "wp-event-dispatcher"
|
||||||
|
|
||||||
#include "event-dispatcher.h"
|
#include "event-dispatcher.h"
|
||||||
#include "event-hook.h"
|
|
||||||
#include "private/registry.h"
|
#include "private/registry.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include <spa/support/plugin.h>
|
#include <spa/support/plugin.h>
|
||||||
#include <spa/support/system.h>
|
#include <spa/support/system.h>
|
||||||
|
|
||||||
typedef struct _WpEventHookData WpEventHookData;
|
typedef struct _EventData EventData;
|
||||||
struct _WpEventHookData
|
struct _EventData
|
||||||
{
|
{
|
||||||
WpEvent *event;
|
WpEvent *event;
|
||||||
WpEventHook *hook;
|
WpIterator *hooks_iter;
|
||||||
|
WpEventHook *current_hook_in_async;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline EventData *
|
||||||
|
event_data_new (WpEvent * event)
|
||||||
|
{
|
||||||
|
EventData *event_data = g_new0 (EventData, 1);
|
||||||
|
event_data->event = wp_event_ref (event);
|
||||||
|
event_data->hooks_iter = wp_event_new_hooks_iterator (event);
|
||||||
|
return event_data;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_hook_data_unref (WpEventHookData *self)
|
event_data_free (EventData * self)
|
||||||
{
|
{
|
||||||
g_clear_pointer (&self->event, wp_event_unref);
|
g_clear_pointer (&self->event, wp_event_unref);
|
||||||
g_clear_object (&self->hook);
|
g_clear_pointer (&self->hooks_iter, wp_iterator_unref);
|
||||||
g_slice_free (WpEventHookData, self);
|
g_clear_object (&self->current_hook_in_async);
|
||||||
}
|
g_free (self);
|
||||||
|
|
||||||
struct _WpEvent
|
|
||||||
{
|
|
||||||
grefcount ref;
|
|
||||||
GData *datalist;
|
|
||||||
|
|
||||||
/* immutable fields */
|
|
||||||
gint priority;
|
|
||||||
WpProperties *properties;
|
|
||||||
GObject *source;
|
|
||||||
GObject *subject;
|
|
||||||
GCancellable *cancellable;
|
|
||||||
|
|
||||||
/* managed by the dispatcher */
|
|
||||||
GList *hooks;
|
|
||||||
gchar *hooks_chain;
|
|
||||||
gchar *name;
|
|
||||||
WpEventHookData *current_hook_in_async;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_BOXED_TYPE (WpEvent, wp_event, wp_event_ref, wp_event_unref)
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
form_event_name (WpEvent *e)
|
|
||||||
{
|
|
||||||
WpProperties *props = e->properties;
|
|
||||||
const gchar *type = wp_properties_get (props, "event.type");
|
|
||||||
const gchar *subject_type = wp_properties_get (props, "event.subject.type");
|
|
||||||
const gchar *metadata_name = wp_properties_get (props, "metadata.name");
|
|
||||||
const gchar *param = wp_properties_get (props, "event.subject.param-id");
|
|
||||||
|
|
||||||
return g_strdup_printf ("<%p>%s%s%s%s%s%s%s", e, (type ? type : ""),
|
|
||||||
((type && subject_type) ? "@" : ""),
|
|
||||||
(subject_type ? subject_type : ""),
|
|
||||||
((subject_type && metadata_name) ? "@" : ""),
|
|
||||||
(metadata_name ? metadata_name : ""),
|
|
||||||
((param && subject_type) ? "@" : ""),
|
|
||||||
(param ? param : "")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_proxy_destroyed (GObject* self, WpEvent* e)
|
|
||||||
{
|
|
||||||
if (e->subject == self) {
|
|
||||||
const gchar* type = wp_properties_get (e->properties, "event.type");
|
|
||||||
/* object removal needs to be processed by hooks */
|
|
||||||
if (g_str_equal (type, "object-removed"))
|
|
||||||
wp_properties_set (e->properties, "pw-proxy-destroyed", "true");
|
|
||||||
else
|
|
||||||
g_cancellable_cancel (e->cancellable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Creates a new event
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param type the type of the event
|
|
||||||
* \param priority the priority of the event
|
|
||||||
* \param properties (transfer full)(nullable): properties of the event
|
|
||||||
* \param source (transfer none): the source of the event
|
|
||||||
* \param subject (transfer none)(nullable): the object that the event is about
|
|
||||||
* \return (transfer full): the newly constructed event
|
|
||||||
*/
|
|
||||||
WpEvent *
|
|
||||||
wp_event_new (const gchar * type, gint priority, WpProperties * properties,
|
|
||||||
GObject * source, GObject * subject)
|
|
||||||
{
|
|
||||||
WpEvent * self = g_slice_new0 (WpEvent);
|
|
||||||
g_ref_count_init (&self->ref);
|
|
||||||
g_datalist_init (&self->datalist);
|
|
||||||
|
|
||||||
self->priority = priority;
|
|
||||||
self->properties = properties ?
|
|
||||||
wp_properties_ensure_unique_owner (properties) :
|
|
||||||
wp_properties_new_empty ();
|
|
||||||
|
|
||||||
self->source = source ? g_object_ref (source) : NULL;
|
|
||||||
self->subject = subject ? g_object_ref (subject) : NULL;
|
|
||||||
self->cancellable = g_cancellable_new ();
|
|
||||||
|
|
||||||
if (self->subject) {
|
|
||||||
/* merge properties from subject */
|
|
||||||
/* PW properties */
|
|
||||||
GParamSpec *pspec = g_object_class_find_property (
|
|
||||||
G_OBJECT_GET_CLASS (self->subject), "properties");
|
|
||||||
if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == WP_TYPE_PROPERTIES) {
|
|
||||||
g_autoptr (WpProperties) subj_props = NULL;
|
|
||||||
g_object_get (self->subject, "properties", &subj_props, NULL);
|
|
||||||
if (subj_props) {
|
|
||||||
wp_properties_update (self->properties, subj_props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* global properties */
|
|
||||||
pspec = g_object_class_find_property ( G_OBJECT_GET_CLASS (self->subject),
|
|
||||||
"global-properties");
|
|
||||||
if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == WP_TYPE_PROPERTIES) {
|
|
||||||
g_autoptr (WpProperties) subj_props = NULL;
|
|
||||||
g_object_get (self->subject, "global-properties", &subj_props, NULL);
|
|
||||||
if (subj_props) {
|
|
||||||
wp_properties_update (self->properties, subj_props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* watch for subject pw-proxy-destroyed and cancel event */
|
|
||||||
if (g_type_is_a (G_OBJECT_TYPE (self->subject), WP_TYPE_PROXY)) {
|
|
||||||
g_signal_connect (self->subject, "pw-proxy-destroyed",
|
|
||||||
(GCallback) on_proxy_destroyed, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_properties_set (self->properties, "event.type", type);
|
|
||||||
self->name = form_event_name (self);
|
|
||||||
|
|
||||||
wp_trace ("event(%s) created", self->name);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wp_event_free (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_datalist_clear (&self->datalist);
|
|
||||||
g_clear_pointer (&self->properties, wp_properties_unref);
|
|
||||||
g_clear_object (&self->source);
|
|
||||||
g_clear_object (&self->subject);
|
|
||||||
g_clear_object (&self->cancellable);
|
|
||||||
g_clear_pointer (&self->current_hook_in_async, wp_hook_data_unref);
|
|
||||||
g_free (self->hooks_chain);
|
|
||||||
g_free (self->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
WpEvent *
|
|
||||||
wp_event_ref (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_ref_count_inc (&self->ref);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
wp_event_unref (WpEvent * self)
|
|
||||||
{
|
|
||||||
if (g_ref_count_dec (&self->ref))
|
|
||||||
wp_event_free (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets the properties of the Event
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param self the handle
|
|
||||||
* \return (transfer full): the properties of the event
|
|
||||||
*/
|
|
||||||
WpProperties *
|
|
||||||
wp_event_get_properties (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(self != NULL, NULL);
|
|
||||||
return wp_properties_ref (self->properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets the Source Object of the Event
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param self the handle
|
|
||||||
* \return (transfer full): the source of the event
|
|
||||||
*/
|
|
||||||
GObject *
|
|
||||||
wp_event_get_source (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(self != NULL, NULL);
|
|
||||||
return self->source ? g_object_ref (self->source) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets the Subject Object of the Event
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param self the handle
|
|
||||||
* \return (transfer full): the subject of the event
|
|
||||||
*/
|
|
||||||
GObject *
|
|
||||||
wp_event_get_subject (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(self != NULL, NULL);
|
|
||||||
return self->subject ? g_object_ref (self->subject) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
wp_event_stop_processing (WpEvent * self)
|
|
||||||
{
|
|
||||||
g_return_if_fail (self != NULL);
|
|
||||||
wp_debug ("stopping event(%s)", self->name);
|
|
||||||
g_cancellable_cancel (self->cancellable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
destroy_event_data (gpointer data)
|
|
||||||
{
|
|
||||||
g_value_unset ((GValue *) data);
|
|
||||||
g_free (data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Stores \a data on the event, associated with the specified \a key
|
|
||||||
*
|
|
||||||
* This can be used to exchange arbitrary data between hooks that run for
|
|
||||||
* this event.
|
|
||||||
*
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param self the event
|
|
||||||
* \param key the key to associate \a data with
|
|
||||||
* \param data (transfer none)(nullable): the data element, or \c NULL to
|
|
||||||
* remove any previous data associated with this \a key
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
wp_event_set_data (WpEvent * self, const gchar * key, const GValue * data)
|
|
||||||
{
|
|
||||||
g_return_if_fail (self != NULL);
|
|
||||||
g_return_if_fail (key != NULL);
|
|
||||||
GValue *data_copy = NULL;
|
|
||||||
|
|
||||||
if (data && G_IS_VALUE (data)) {
|
|
||||||
data_copy = g_new0 (GValue, 1);
|
|
||||||
g_value_init (data_copy, G_VALUE_TYPE (data));
|
|
||||||
g_value_copy (data, data_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_datalist_set_data_full (&self->datalist, key, data_copy,
|
|
||||||
data_copy ? destroy_event_data : NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets the data that was previously associated with \a key by
|
|
||||||
* wp_event_set_data()
|
|
||||||
* \ingroup wpevent
|
|
||||||
* \param self the event
|
|
||||||
* \return (transfer none)(nullable): the data associated with \a key or \c NULL
|
|
||||||
*/
|
|
||||||
const GValue *
|
|
||||||
wp_event_get_data (WpEvent * self, const gchar * key)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (self != NULL, NULL);
|
|
||||||
g_return_val_if_fail (key != NULL, NULL);
|
|
||||||
|
|
||||||
return g_datalist_get_data (&self->datalist, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _WpEventDispatcher
|
struct _WpEventDispatcher
|
||||||
@@ -283,7 +49,6 @@ struct _WpEventDispatcher
|
|||||||
GPtrArray *hooks; /* registered hooks */
|
GPtrArray *hooks; /* registered hooks */
|
||||||
GSource *source; /* the event loop source */
|
GSource *source; /* the event loop source */
|
||||||
GList *events; /* the events stack */
|
GList *events; /* the events stack */
|
||||||
gchar *events_chain; /* chain of events for an event run */
|
|
||||||
struct spa_system *system;
|
struct spa_system *system;
|
||||||
int eventfd;
|
int eventfd;
|
||||||
};
|
};
|
||||||
@@ -305,37 +70,26 @@ wp_event_source_check (GSource * s)
|
|||||||
{
|
{
|
||||||
WpEventDispatcher *d = WP_EVENT_SOURCE_DISPATCHER (s);
|
WpEventDispatcher *d = WP_EVENT_SOURCE_DISPATCHER (s);
|
||||||
return d && d->events &&
|
return d && d->events &&
|
||||||
!((WpEvent *) g_list_first (d->events)->data)->current_hook_in_async;
|
!((EventData *) g_list_first (d->events)->data)->current_hook_in_async;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_event_hook_done (WpEventHook * hook, GAsyncResult * res, WpEvent * event)
|
on_event_hook_done (WpEventHook * hook, GAsyncResult * res, EventData * data)
|
||||||
{
|
{
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr (GError) error = NULL;
|
||||||
g_autoptr (WpEventDispatcher) dispatcher =
|
g_autoptr (WpEventDispatcher) dispatcher =
|
||||||
wp_event_hook_get_dispatcher (hook);
|
wp_event_hook_get_dispatcher (hook);
|
||||||
|
|
||||||
g_assert (event->current_hook_in_async->hook == hook);
|
g_assert (data->current_hook_in_async == hook);
|
||||||
|
|
||||||
if (!wp_event_hook_finish (hook, res, &error) && error &&
|
if (!wp_event_hook_finish (hook, res, &error) && error &&
|
||||||
error->domain != G_IO_ERROR && error->code != G_IO_ERROR_CANCELLED)
|
error->domain != G_IO_ERROR && error->code != G_IO_ERROR_CANCELLED)
|
||||||
wp_message_object (hook, "failed: %s", error->message);
|
wp_message_object (hook, "failed: %s", error->message);
|
||||||
|
|
||||||
g_clear_pointer (&event->current_hook_in_async, wp_hook_data_unref);
|
g_clear_object (&data->current_hook_in_async);
|
||||||
spa_system_eventfd_write (dispatcher->system, dispatcher->eventfd, 1);
|
spa_system_eventfd_write (dispatcher->system, dispatcher->eventfd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar *
|
|
||||||
build_chain (const gchar *link, gint link_priority, gchar *chain)
|
|
||||||
{
|
|
||||||
gchar *temp = g_strdup_printf ("%s%s%s(%d)", (chain ? chain : ""),
|
|
||||||
(chain ? " -> " : ""), link, link_priority);
|
|
||||||
g_free (chain);
|
|
||||||
chain = temp;
|
|
||||||
|
|
||||||
return chain;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
wp_event_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
wp_event_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
||||||
{
|
{
|
||||||
@@ -348,59 +102,46 @@ wp_event_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
|||||||
/* get the highest priority event */
|
/* get the highest priority event */
|
||||||
GList *levent = g_list_first (d->events);
|
GList *levent = g_list_first (d->events);
|
||||||
while (levent) {
|
while (levent) {
|
||||||
WpEvent *event = (WpEvent *) (levent->data);
|
EventData *event_data = (EventData *) (levent->data);
|
||||||
|
WpEvent *event = event_data->event;
|
||||||
|
GCancellable *cancellable = wp_event_get_cancellable (event);
|
||||||
|
g_auto (GValue) value = G_VALUE_INIT;
|
||||||
|
gboolean has_next = FALSE;
|
||||||
|
|
||||||
/* event hook is still in progress, we will continue later */
|
/* event hook is still in progress, we will continue later */
|
||||||
if (event->current_hook_in_async)
|
if (event_data->current_hook_in_async)
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
|
|
||||||
/* remove the remaining hooks if the event was cancelled */
|
/* check if the event was cancelled */
|
||||||
if (g_cancellable_is_cancelled (event->cancellable) && event->hooks) {
|
if (g_cancellable_is_cancelled (cancellable)) {
|
||||||
wp_debug_object (d, "event(%s) cancelled remove it", event->name);
|
wp_debug_object (d, "event(%p) cancelled remove it", event);
|
||||||
g_list_free_full (g_steal_pointer (&event->hooks),
|
has_next = FALSE;
|
||||||
(GDestroyNotify) wp_hook_data_unref);
|
} else {
|
||||||
} else
|
|
||||||
/* avoid duplicate entries in chain */
|
|
||||||
if (!d->events_chain || !strstr (d->events_chain, event->name)) {
|
|
||||||
d->events_chain = build_chain (event->name, event->priority,
|
|
||||||
d->events_chain);
|
|
||||||
wp_debug_object (d, "dispatching event (%s)" WP_OBJECT_FORMAT " priority(%d)",
|
|
||||||
event->name, WP_OBJECT_ARGS (event->subject), event->priority);
|
|
||||||
}
|
|
||||||
/* get the highest priority hook */
|
/* get the highest priority hook */
|
||||||
GList *lhook = g_list_first (event->hooks);
|
has_next = wp_iterator_next (event_data->hooks_iter, &value);
|
||||||
if (lhook) {
|
}
|
||||||
WpEventHookData *hook_data = (WpEventHookData *) (lhook->data);
|
|
||||||
WpEventHook *hook = hook_data->hook;
|
if (has_next) {
|
||||||
|
WpEventHook *hook = g_value_get_object (&value);
|
||||||
const gchar *name = wp_event_hook_get_name (hook);
|
const gchar *name = wp_event_hook_get_name (hook);
|
||||||
gint priority = wp_event_hook_get_priority (hook);
|
|
||||||
|
|
||||||
event->current_hook_in_async = hook_data;
|
event_data->current_hook_in_async = g_object_ref (hook);
|
||||||
event->hooks = g_list_delete_link (event->hooks,
|
|
||||||
g_steal_pointer (&lhook));
|
|
||||||
|
|
||||||
event->hooks_chain = build_chain (name, priority, event->hooks_chain);
|
|
||||||
wp_trace_object (d, "running hook <%p>(%s)", hook, name);
|
wp_trace_object (d, "running hook <%p>(%s)", hook, name);
|
||||||
|
|
||||||
/* execute the hook, possibly async */
|
/* execute the hook, possibly async */
|
||||||
wp_event_hook_run (hook, event, event->cancellable,
|
wp_event_hook_run (hook, event, cancellable,
|
||||||
(GAsyncReadyCallback) on_event_hook_done, event);
|
(GAsyncReadyCallback) on_event_hook_done, event_data);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
/* clear the event after all hooks are done */
|
/* clear the event after all hooks are done */
|
||||||
if (!event->hooks && !event->current_hook_in_async) {
|
|
||||||
d->events = g_list_delete_link (d->events, g_steal_pointer (&levent));
|
d->events = g_list_delete_link (d->events, g_steal_pointer (&levent));
|
||||||
g_clear_pointer (&event, wp_event_unref);
|
g_clear_pointer (&event_data, event_data_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get the next event */
|
/* get the next event */
|
||||||
levent = g_list_first (d->events);
|
levent = g_list_first (d->events);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* an event run completed reset the events_chain */
|
|
||||||
g_free (d->events_chain);
|
|
||||||
d->events_chain = NULL;
|
|
||||||
|
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,21 +166,13 @@ wp_event_dispatcher_init (WpEventDispatcher * self)
|
|||||||
g_source_set_priority (self->source, G_PRIORITY_HIGH_IDLE);
|
g_source_set_priority (self->source, G_PRIORITY_HIGH_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
clear_event (WpEvent * event)
|
|
||||||
{
|
|
||||||
g_cancellable_cancel (event->cancellable);
|
|
||||||
g_list_free_full (g_steal_pointer (&event->hooks), g_object_unref);
|
|
||||||
wp_event_unref (event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_event_dispatcher_finalize (GObject * object)
|
wp_event_dispatcher_finalize (GObject * object)
|
||||||
{
|
{
|
||||||
WpEventDispatcher *self = WP_EVENT_DISPATCHER (object);
|
WpEventDispatcher *self = WP_EVENT_DISPATCHER (object);
|
||||||
|
|
||||||
g_list_free_full (g_steal_pointer (&self->events),
|
g_list_free_full (g_steal_pointer (&self->events),
|
||||||
(GDestroyNotify) clear_event);
|
(GDestroyNotify) event_data_free);
|
||||||
|
|
||||||
((WpEventSource *) self->source)->dispatcher = NULL;
|
((WpEventSource *) self->source)->dispatcher = NULL;
|
||||||
g_source_destroy (self->source);
|
g_source_destroy (self->source);
|
||||||
@@ -449,7 +182,6 @@ wp_event_dispatcher_finalize (GObject * object)
|
|||||||
|
|
||||||
g_clear_pointer (&self->hooks, g_ptr_array_unref);
|
g_clear_pointer (&self->hooks, g_ptr_array_unref);
|
||||||
g_weak_ref_clear (&self->core);
|
g_weak_ref_clear (&self->core);
|
||||||
g_free (self->events_chain);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (wp_event_dispatcher_parent_class)->finalize (object);
|
G_OBJECT_CLASS (wp_event_dispatcher_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
@@ -502,16 +234,9 @@ wp_event_dispatcher_get_instance (WpCore * core)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
event_cmp_func (const WpEvent *a, const WpEvent *b)
|
event_cmp_func (const EventData *a, const EventData *b)
|
||||||
{
|
{
|
||||||
return b->priority - a->priority;
|
return wp_event_get_priority (b->event) - wp_event_get_priority (a->event);
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
|
||||||
hook_cmp_func (const WpEventHookData *new_hook, const WpEventHookData *listed_hook)
|
|
||||||
{
|
|
||||||
return wp_event_hook_get_priority ((WpEventHook *) listed_hook->hook) -
|
|
||||||
wp_event_hook_get_priority ((WpEventHook *) new_hook->hook);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -527,37 +252,20 @@ wp_event_dispatcher_push_event (WpEventDispatcher * self, WpEvent * event)
|
|||||||
{
|
{
|
||||||
g_return_if_fail (WP_IS_EVENT_DISPATCHER (self));
|
g_return_if_fail (WP_IS_EVENT_DISPATCHER (self));
|
||||||
g_return_if_fail (event != NULL);
|
g_return_if_fail (event != NULL);
|
||||||
gboolean hooks_added = FALSE;
|
|
||||||
|
|
||||||
/* attach hooks that run for this event */
|
if (wp_event_collect_hooks (event, self)) {
|
||||||
for (guint i = 0; i < self->hooks->len; i++) {
|
EventData *event_data = event_data_new (event);
|
||||||
WpEventHook *hook = g_ptr_array_index (self->hooks, i);
|
|
||||||
|
|
||||||
if (wp_event_hook_runs_for_event (hook, event)) {
|
self->events = g_list_insert_sorted (self->events, event_data,
|
||||||
WpEventHookData *hook_data = g_slice_new0 (WpEventHookData);
|
|
||||||
const gchar *name = wp_event_hook_get_name (hook);
|
|
||||||
gint priority = wp_event_hook_get_priority (hook);
|
|
||||||
|
|
||||||
hook_data->hook = g_object_ref (hook);
|
|
||||||
event->hooks = g_list_insert_sorted (event->hooks, hook_data,
|
|
||||||
(GCompareFunc) hook_cmp_func);
|
|
||||||
hooks_added = true;
|
|
||||||
|
|
||||||
wp_debug_object (self, "added hook <%p>(%s(%d))", hook, name, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hooks_added) {
|
|
||||||
self->events = g_list_insert_sorted (self->events, event,
|
|
||||||
(GCompareFunc) event_cmp_func);
|
(GCompareFunc) event_cmp_func);
|
||||||
wp_debug_object (self, "pushed event (%s)" WP_OBJECT_FORMAT " priority(%d)",
|
// wp_debug_object (self, "pushed event (%s)" WP_OBJECT_FORMAT " priority(%d)",
|
||||||
event->name, WP_OBJECT_ARGS (event->subject), event->priority);
|
// event->name, WP_OBJECT_ARGS (event->subject), event->priority);
|
||||||
|
|
||||||
/* wakeup the GSource */
|
/* wakeup the GSource */
|
||||||
spa_system_eventfd_write (self->system, self->eventfd, 1);
|
spa_system_eventfd_write (self->system, self->eventfd, 1);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
g_clear_pointer (&event, wp_event_unref);
|
wp_event_unref (event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@@ -9,57 +9,12 @@
|
|||||||
#ifndef __WIREPLUMBER_EVENT_DISPATCHER_H__
|
#ifndef __WIREPLUMBER_EVENT_DISPATCHER_H__
|
||||||
#define __WIREPLUMBER_EVENT_DISPATCHER_H__
|
#define __WIREPLUMBER_EVENT_DISPATCHER_H__
|
||||||
|
|
||||||
#include "properties.h"
|
|
||||||
#include "event-hook.h"
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "event-hook.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/*! \defgroup wpevent WpEvent */
|
|
||||||
/*!
|
|
||||||
* \struct WpEvent
|
|
||||||
*
|
|
||||||
* WpEvent describes an event, an event is an entity which can be pushed on to
|
|
||||||
* event stack and the event dispatcher is going to pick and dispatch it.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define WP_TYPE_EVENT (wp_event_get_type ())
|
|
||||||
WP_API
|
|
||||||
GType wp_event_get_type (void) G_GNUC_CONST;
|
|
||||||
|
|
||||||
typedef struct _WpEvent WpEvent;
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
WpEvent * wp_event_new (const gchar * type, gint priority,
|
|
||||||
WpProperties * properties, GObject * source, GObject * subject);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
WpEvent * wp_event_ref (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
void wp_event_unref (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
WpProperties * wp_event_get_properties (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
GObject * wp_event_get_source (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
GObject * wp_event_get_subject (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
void wp_event_stop_processing (WpEvent * self);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
void wp_event_set_data (WpEvent * self, const gchar * key, const GValue * data);
|
|
||||||
|
|
||||||
WP_API
|
|
||||||
const GValue * wp_event_get_data (WpEvent * self, const gchar * key);
|
|
||||||
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpEvent, wp_event_unref)
|
|
||||||
|
|
||||||
|
|
||||||
/*! \defgroup wpeventdispatcher WpEventDispatcher */
|
/*! \defgroup wpeventdispatcher WpEventDispatcher */
|
||||||
/*!
|
/*!
|
||||||
* \struct WpEventDispatcher
|
* \struct WpEventDispatcher
|
||||||
|
@@ -17,15 +17,17 @@
|
|||||||
typedef struct _WpEventHookPrivate WpEventHookPrivate;
|
typedef struct _WpEventHookPrivate WpEventHookPrivate;
|
||||||
struct _WpEventHookPrivate
|
struct _WpEventHookPrivate
|
||||||
{
|
{
|
||||||
gint priority;
|
|
||||||
GWeakRef dispatcher;
|
GWeakRef dispatcher;
|
||||||
gchar *name;
|
gchar *name;
|
||||||
|
gchar **before;
|
||||||
|
gchar **after;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_NAME,
|
PROP_NAME,
|
||||||
PROP_PRIORITY,
|
PROP_RUNS_BEFORE_HOOKS,
|
||||||
|
PROP_RUNS_AFTER_HOOKS,
|
||||||
PROP_DISPATCHER,
|
PROP_DISPATCHER,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,6 +47,8 @@ wp_event_hook_finalize (GObject * object)
|
|||||||
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
|
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
|
||||||
|
|
||||||
g_weak_ref_clear (&priv->dispatcher);
|
g_weak_ref_clear (&priv->dispatcher);
|
||||||
|
g_strfreev (priv->before);
|
||||||
|
g_strfreev (priv->after);
|
||||||
g_free (priv->name);
|
g_free (priv->name);
|
||||||
|
|
||||||
G_OBJECT_CLASS (wp_event_hook_parent_class)->finalize (object);
|
G_OBJECT_CLASS (wp_event_hook_parent_class)->finalize (object);
|
||||||
@@ -61,8 +65,11 @@ wp_event_hook_set_property (GObject * object, guint property_id,
|
|||||||
case PROP_NAME:
|
case PROP_NAME:
|
||||||
priv->name = g_value_dup_string (value);
|
priv->name = g_value_dup_string (value);
|
||||||
break;
|
break;
|
||||||
case PROP_PRIORITY:
|
case PROP_RUNS_BEFORE_HOOKS:
|
||||||
priv->priority = g_value_get_int (value);
|
priv->before = g_value_dup_boxed (value);
|
||||||
|
break;
|
||||||
|
case PROP_RUNS_AFTER_HOOKS:
|
||||||
|
priv->after = g_value_dup_boxed (value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
@@ -81,8 +88,11 @@ wp_event_hook_get_property (GObject * object, guint property_id, GValue * value,
|
|||||||
case PROP_NAME:
|
case PROP_NAME:
|
||||||
g_value_set_string (value, priv->name);
|
g_value_set_string (value, priv->name);
|
||||||
break;
|
break;
|
||||||
case PROP_PRIORITY:
|
case PROP_RUNS_BEFORE_HOOKS:
|
||||||
g_value_set_int (value, priv->priority);
|
g_value_set_boxed (value, priv->before);
|
||||||
|
break;
|
||||||
|
case PROP_RUNS_AFTER_HOOKS:
|
||||||
|
g_value_set_boxed (value, priv->after);
|
||||||
break;
|
break;
|
||||||
case PROP_DISPATCHER:
|
case PROP_DISPATCHER:
|
||||||
g_value_take_object (value, wp_event_hook_get_dispatcher (self));
|
g_value_take_object (value, wp_event_hook_get_dispatcher (self));
|
||||||
@@ -106,9 +116,14 @@ wp_event_hook_class_init (WpEventHookClass * klass)
|
|||||||
g_param_spec_string ("name", "name", "The hook name", "",
|
g_param_spec_string ("name", "name", "The hook name", "",
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_PRIORITY,
|
g_object_class_install_property (object_class, PROP_RUNS_BEFORE_HOOKS,
|
||||||
g_param_spec_int ("priority", "priority",
|
g_param_spec_boxed ("runs-before-hooks", "runs-before-hooks",
|
||||||
"The priority of the hook", -G_MAXINT, G_MAXINT, 0,
|
"runs-before-hooks", G_TYPE_STRV,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class, PROP_RUNS_AFTER_HOOKS,
|
||||||
|
g_param_spec_boxed ("runs-after-hooks", "runs-after-hooks",
|
||||||
|
"runs-after-hooks", G_TYPE_STRV,
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_DISPATCHER,
|
g_object_class_install_property (object_class, PROP_DISPATCHER,
|
||||||
@@ -117,21 +132,6 @@ wp_event_hook_class_init (WpEventHookClass * klass)
|
|||||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Returns the priority of the hook
|
|
||||||
*
|
|
||||||
* \ingroup wpeventhook
|
|
||||||
* \param self the event hook
|
|
||||||
* \return the event hook priority
|
|
||||||
*/
|
|
||||||
gint
|
|
||||||
wp_event_hook_get_priority (WpEventHook * self)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), 0);
|
|
||||||
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
|
|
||||||
return priv->priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the name of the hook
|
* \brief Returns the name of the hook
|
||||||
*
|
*
|
||||||
@@ -147,6 +147,40 @@ wp_event_hook_get_name (WpEventHook * self)
|
|||||||
return priv->name;
|
return priv->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the names of the hooks that should run after this hook,
|
||||||
|
* or in other words, this hook should run before them
|
||||||
|
*
|
||||||
|
* \ingroup wpeventhook
|
||||||
|
* \param self the event hook
|
||||||
|
* \return (array zero-terminated=1)(element-type utf8)(transfer none):
|
||||||
|
* a NULL-terminated array of hook names
|
||||||
|
*/
|
||||||
|
const gchar * const *
|
||||||
|
wp_event_hook_get_runs_before_hooks (WpEventHook * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
|
||||||
|
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
|
||||||
|
return (const gchar * const *) priv->before;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the names of the hooks that should run before this hook,
|
||||||
|
* or in other words, this hook should run after them
|
||||||
|
*
|
||||||
|
* \ingroup wpeventhook
|
||||||
|
* \param self the event hook
|
||||||
|
* \return (array zero-terminated=1)(element-type utf8)(transfer none):
|
||||||
|
* a NULL-terminated array of hook names
|
||||||
|
*/
|
||||||
|
const gchar * const *
|
||||||
|
wp_event_hook_get_runs_after_hooks (WpEventHook * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
|
||||||
|
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
|
||||||
|
return (const gchar * const *) priv->after;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the associated event dispatcher
|
* \brief Returns the associated event dispatcher
|
||||||
*
|
*
|
||||||
@@ -471,20 +505,25 @@ wp_simple_event_hook_class_init (WpSimpleEventHookClass * klass)
|
|||||||
* \brief Constructs a new simple event hook
|
* \brief Constructs a new simple event hook
|
||||||
*
|
*
|
||||||
* \param name the name of the hook
|
* \param name the name of the hook
|
||||||
* \param priority the priority of the hook
|
* \param before (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
|
||||||
|
* an array of hook names that should run after this hook
|
||||||
|
* \param after (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
|
||||||
|
* an array of hook names that should run before this hook
|
||||||
* \param closure the closure to invoke when the hook is executed; the closure
|
* \param closure the closure to invoke when the hook is executed; the closure
|
||||||
* should accept two parameters: the event dispatcher and the event, returning
|
* should accept two parameters: the event dispatcher and the event, returning
|
||||||
* nothing
|
* nothing
|
||||||
* \return a new simple event hook
|
* \return a new simple event hook
|
||||||
*/
|
*/
|
||||||
WpEventHook *
|
WpEventHook *
|
||||||
wp_simple_event_hook_new (const gchar *name, gint priority, GClosure * closure)
|
wp_simple_event_hook_new (const gchar *name,
|
||||||
|
const gchar * before[], const gchar * after[], GClosure * closure)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (closure != NULL, NULL);
|
g_return_val_if_fail (closure != NULL, NULL);
|
||||||
|
|
||||||
return g_object_new (WP_TYPE_SIMPLE_EVENT_HOOK,
|
return g_object_new (WP_TYPE_SIMPLE_EVENT_HOOK,
|
||||||
"name", name,
|
"name", name,
|
||||||
"priority", priority,
|
"runs-before-hooks", before,
|
||||||
|
"runs-after-hooks", after,
|
||||||
"closure", closure,
|
"closure", closure,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
@@ -611,13 +650,17 @@ wp_async_event_hook_class_init (WpAsyncEventHookClass * klass)
|
|||||||
* \brief Constructs a new async event hook
|
* \brief Constructs a new async event hook
|
||||||
*
|
*
|
||||||
* \param name the name of the hook
|
* \param name the name of the hook
|
||||||
* \param priority the priority of the hook
|
* \param before (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
|
||||||
|
* an array of hook names that should run after this hook
|
||||||
|
* \param after (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
|
||||||
|
* an array of hook names that should run before this hook
|
||||||
* \param get_next_step the closure to invoke to get the next step
|
* \param get_next_step the closure to invoke to get the next step
|
||||||
* \param execute_step the closure to invoke to execute the step
|
* \param execute_step the closure to invoke to execute the step
|
||||||
* \return a new async event hook
|
* \return a new async event hook
|
||||||
*/
|
*/
|
||||||
WpEventHook *
|
WpEventHook *
|
||||||
wp_async_event_hook_new (const gchar *name, gint priority,
|
wp_async_event_hook_new (const gchar *name,
|
||||||
|
const gchar * before[], const gchar * after[],
|
||||||
GClosure * get_next_step, GClosure * execute_step)
|
GClosure * get_next_step, GClosure * execute_step)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (get_next_step != NULL, NULL);
|
g_return_val_if_fail (get_next_step != NULL, NULL);
|
||||||
@@ -625,7 +668,8 @@ wp_async_event_hook_new (const gchar *name, gint priority,
|
|||||||
|
|
||||||
return g_object_new (WP_TYPE_ASYNC_EVENT_HOOK,
|
return g_object_new (WP_TYPE_ASYNC_EVENT_HOOK,
|
||||||
"name", name,
|
"name", name,
|
||||||
"priority", priority,
|
"runs-before-hooks", before,
|
||||||
|
"runs-after-hooks", after,
|
||||||
"get-next-step", get_next_step,
|
"get-next-step", get_next_step,
|
||||||
"execute-step", execute_step,
|
"execute-step", execute_step,
|
||||||
NULL);
|
NULL);
|
||||||
|
@@ -17,18 +17,6 @@ G_BEGIN_DECLS
|
|||||||
typedef struct _WpEvent WpEvent;
|
typedef struct _WpEvent WpEvent;
|
||||||
typedef struct _WpEventDispatcher WpEventDispatcher;
|
typedef struct _WpEventDispatcher WpEventDispatcher;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WP_EVENT_HOOK_PRIORITY_LOWEST = -500,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_ULTRA_LOW = -300,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_VERY_LOW = -200,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_LOW = -100,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL = 0,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_HIGH = 100,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_VERY_HIGH = 200,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_ULTRA_HIGH = 300,
|
|
||||||
WP_EVENT_HOOK_PRIORITY_HIGHEST = 500,
|
|
||||||
} WpEventHookPriority;
|
|
||||||
|
|
||||||
/*! \defgroup wpeventhook WpEventHook */
|
/*! \defgroup wpeventhook WpEventHook */
|
||||||
/*!
|
/*!
|
||||||
* \struct WpEventHook
|
* \struct WpEventHook
|
||||||
@@ -56,10 +44,13 @@ struct _WpEventHookClass
|
|||||||
};
|
};
|
||||||
|
|
||||||
WP_API
|
WP_API
|
||||||
gint wp_event_hook_get_priority (WpEventHook * self);
|
const gchar * wp_event_hook_get_name (WpEventHook * self);
|
||||||
|
|
||||||
WP_API
|
WP_API
|
||||||
const gchar * wp_event_hook_get_name (WpEventHook * self);
|
const gchar * const * wp_event_hook_get_runs_before_hooks (WpEventHook * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
const gchar * const * wp_event_hook_get_runs_after_hooks (WpEventHook * self);
|
||||||
|
|
||||||
WP_PRIVATE_API
|
WP_PRIVATE_API
|
||||||
WpEventDispatcher * wp_event_hook_get_dispatcher (WpEventHook * self);
|
WpEventDispatcher * wp_event_hook_get_dispatcher (WpEventHook * self);
|
||||||
@@ -117,7 +108,8 @@ G_DECLARE_FINAL_TYPE (WpSimpleEventHook, wp_simple_event_hook,
|
|||||||
WP, SIMPLE_EVENT_HOOK, WpInterestEventHook)
|
WP, SIMPLE_EVENT_HOOK, WpInterestEventHook)
|
||||||
|
|
||||||
WP_API
|
WP_API
|
||||||
WpEventHook * wp_simple_event_hook_new (const gchar *name, gint priority,
|
WpEventHook * wp_simple_event_hook_new (const gchar *name,
|
||||||
|
const gchar * before[], const gchar * after[],
|
||||||
GClosure * closure);
|
GClosure * closure);
|
||||||
|
|
||||||
|
|
||||||
@@ -131,7 +123,8 @@ G_DECLARE_FINAL_TYPE (WpAsyncEventHook, wp_async_event_hook,
|
|||||||
WP, ASYNC_EVENT_HOOK, WpInterestEventHook)
|
WP, ASYNC_EVENT_HOOK, WpInterestEventHook)
|
||||||
|
|
||||||
WP_API
|
WP_API
|
||||||
WpEventHook * wp_async_event_hook_new (const gchar *name, gint priority,
|
WpEventHook * wp_async_event_hook_new (const gchar *name,
|
||||||
|
const gchar * before[], const gchar * after[],
|
||||||
GClosure * get_next_step, GClosure * execute_step);
|
GClosure * get_next_step, GClosure * execute_step);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
566
lib/wp/event.c
Normal file
566
lib/wp/event.c
Normal file
@@ -0,0 +1,566 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2022 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define G_LOG_DOMAIN "wp-event"
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "event-dispatcher.h"
|
||||||
|
#include "event-hook.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#include <spa/utils/defs.h>
|
||||||
|
#include <spa/utils/list.h>
|
||||||
|
|
||||||
|
typedef struct _HookData HookData;
|
||||||
|
struct _HookData
|
||||||
|
{
|
||||||
|
struct spa_list link;
|
||||||
|
WpEventHook *hook;
|
||||||
|
GPtrArray *dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline HookData *
|
||||||
|
hook_data_new (WpEventHook * hook)
|
||||||
|
{
|
||||||
|
HookData *hook_data = g_new0 (HookData, 1);
|
||||||
|
spa_list_init (&hook_data->link);
|
||||||
|
hook_data->hook = g_object_ref (hook);
|
||||||
|
hook_data->dependencies = g_ptr_array_new ();
|
||||||
|
return hook_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hook_data_free (HookData *self)
|
||||||
|
{
|
||||||
|
g_clear_object (&self->hook);
|
||||||
|
g_clear_pointer (&self->dependencies, g_ptr_array_unref);
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _WpEvent
|
||||||
|
{
|
||||||
|
grefcount ref;
|
||||||
|
GData *datalist;
|
||||||
|
struct spa_list hooks;
|
||||||
|
|
||||||
|
/* immutable fields */
|
||||||
|
gint priority;
|
||||||
|
WpProperties *properties;
|
||||||
|
GObject *source;
|
||||||
|
GObject *subject;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
gchar *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (WpEvent, wp_event, wp_event_ref, wp_event_unref)
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
form_event_name (WpEvent *e)
|
||||||
|
{
|
||||||
|
WpProperties *props = e->properties;
|
||||||
|
const gchar *type = wp_properties_get (props, "event.type");
|
||||||
|
const gchar *subject_type = wp_properties_get (props, "event.subject.type");
|
||||||
|
const gchar *metadata_name = wp_properties_get (props, "metadata.name");
|
||||||
|
const gchar *param = wp_properties_get (props, "event.subject.param-id");
|
||||||
|
|
||||||
|
return g_strdup_printf ("<%p>%s%s%s%s%s%s%s", e, (type ? type : ""),
|
||||||
|
((type && subject_type) ? "@" : ""),
|
||||||
|
(subject_type ? subject_type : ""),
|
||||||
|
((subject_type && metadata_name) ? "@" : ""),
|
||||||
|
(metadata_name ? metadata_name : ""),
|
||||||
|
((param && subject_type) ? "@" : ""),
|
||||||
|
(param ? param : "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_proxy_destroyed (GObject* self, WpEvent* e)
|
||||||
|
{
|
||||||
|
if (e->subject == self) {
|
||||||
|
const gchar* type = wp_properties_get (e->properties, "event.type");
|
||||||
|
/* object removal needs to be processed by hooks */
|
||||||
|
if (g_str_equal (type, "object-removed"))
|
||||||
|
wp_properties_set (e->properties, "pw-proxy-destroyed", "true");
|
||||||
|
else
|
||||||
|
g_cancellable_cancel (e->cancellable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Creates a new event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param type the type of the event
|
||||||
|
* \param priority the priority of the event
|
||||||
|
* \param properties (transfer full)(nullable): properties of the event
|
||||||
|
* \param source (transfer none): the source of the event
|
||||||
|
* \param subject (transfer none)(nullable): the object that the event is about
|
||||||
|
* \return (transfer full): the newly constructed event
|
||||||
|
*/
|
||||||
|
WpEvent *
|
||||||
|
wp_event_new (const gchar * type, gint priority, WpProperties * properties,
|
||||||
|
GObject * source, GObject * subject)
|
||||||
|
{
|
||||||
|
WpEvent * self = g_new0 (WpEvent, 1);
|
||||||
|
g_ref_count_init (&self->ref);
|
||||||
|
g_datalist_init (&self->datalist);
|
||||||
|
spa_list_init (&self->hooks);
|
||||||
|
|
||||||
|
self->priority = priority;
|
||||||
|
self->properties = properties ?
|
||||||
|
wp_properties_ensure_unique_owner (properties) :
|
||||||
|
wp_properties_new_empty ();
|
||||||
|
|
||||||
|
self->source = source ? g_object_ref (source) : NULL;
|
||||||
|
self->subject = subject ? g_object_ref (subject) : NULL;
|
||||||
|
self->cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
if (self->subject) {
|
||||||
|
/* merge properties from subject */
|
||||||
|
/* PW properties */
|
||||||
|
GParamSpec *pspec = g_object_class_find_property (
|
||||||
|
G_OBJECT_GET_CLASS (self->subject), "properties");
|
||||||
|
if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == WP_TYPE_PROPERTIES) {
|
||||||
|
g_autoptr (WpProperties) subj_props = NULL;
|
||||||
|
g_object_get (self->subject, "properties", &subj_props, NULL);
|
||||||
|
if (subj_props) {
|
||||||
|
wp_properties_update (self->properties, subj_props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* global properties */
|
||||||
|
pspec = g_object_class_find_property ( G_OBJECT_GET_CLASS (self->subject),
|
||||||
|
"global-properties");
|
||||||
|
if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == WP_TYPE_PROPERTIES) {
|
||||||
|
g_autoptr (WpProperties) subj_props = NULL;
|
||||||
|
g_object_get (self->subject, "global-properties", &subj_props, NULL);
|
||||||
|
if (subj_props) {
|
||||||
|
wp_properties_update (self->properties, subj_props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* watch for subject pw-proxy-destroyed and cancel event */
|
||||||
|
if (g_type_is_a (G_OBJECT_TYPE (self->subject), WP_TYPE_PROXY)) {
|
||||||
|
g_signal_connect (self->subject, "pw-proxy-destroyed",
|
||||||
|
(GCallback) on_proxy_destroyed, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_properties_set (self->properties, "event.type", type);
|
||||||
|
self->name = form_event_name (self);
|
||||||
|
|
||||||
|
wp_trace ("event(%s) created", self->name);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_event_free (WpEvent * self)
|
||||||
|
{
|
||||||
|
HookData *hook_data;
|
||||||
|
spa_list_consume (hook_data, &self->hooks, link) {
|
||||||
|
spa_list_remove (&hook_data->link);
|
||||||
|
hook_data_free (hook_data);
|
||||||
|
}
|
||||||
|
g_datalist_clear (&self->datalist);
|
||||||
|
g_clear_pointer (&self->properties, wp_properties_unref);
|
||||||
|
g_clear_object (&self->source);
|
||||||
|
g_clear_object (&self->subject);
|
||||||
|
g_cancellable_cancel (self->cancellable);
|
||||||
|
g_clear_object (&self->cancellable);
|
||||||
|
g_free (self->name);
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpEvent *
|
||||||
|
wp_event_ref (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_ref_count_inc (&self->ref);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wp_event_unref (WpEvent * self)
|
||||||
|
{
|
||||||
|
if (g_ref_count_dec (&self->ref))
|
||||||
|
wp_event_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the priority of the event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the event
|
||||||
|
* \return the event priority
|
||||||
|
*/
|
||||||
|
gint
|
||||||
|
wp_event_get_priority (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (self != NULL, 0);
|
||||||
|
return self->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the properties of the Event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the handle
|
||||||
|
* \return (transfer full): the properties of the event
|
||||||
|
*/
|
||||||
|
WpProperties *
|
||||||
|
wp_event_get_properties (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(self != NULL, NULL);
|
||||||
|
return wp_properties_ref (self->properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the Source Object of the Event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the handle
|
||||||
|
* \return (transfer full): the source of the event
|
||||||
|
*/
|
||||||
|
GObject *
|
||||||
|
wp_event_get_source (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(self != NULL, NULL);
|
||||||
|
return self->source ? g_object_ref (self->source) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the Subject Object of the Event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the handle
|
||||||
|
* \return (transfer full): the subject of the event
|
||||||
|
*/
|
||||||
|
GObject *
|
||||||
|
wp_event_get_subject (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(self != NULL, NULL);
|
||||||
|
return self->subject ? g_object_ref (self->subject) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the internal GCancellable that is used to track whether this
|
||||||
|
* event has been stopped by wp_event_stop_processing()
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the event
|
||||||
|
* \return (transfer none): the cancellable
|
||||||
|
*/
|
||||||
|
GCancellable *
|
||||||
|
wp_event_get_cancellable (WpEvent * self)
|
||||||
|
{
|
||||||
|
return self->cancellable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Stops processing of this event; any further hooks will not be executed
|
||||||
|
* from this moment onwards and the event will be discarded from the stack
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the event
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
wp_event_stop_processing (WpEvent * self)
|
||||||
|
{
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
wp_debug ("stopping event(%s)", self->name);
|
||||||
|
g_cancellable_cancel (self->cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_event_data (gpointer data)
|
||||||
|
{
|
||||||
|
g_value_unset ((GValue *) data);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Stores \a data on the event, associated with the specified \a key
|
||||||
|
*
|
||||||
|
* This can be used to exchange arbitrary data between hooks that run for
|
||||||
|
* this event.
|
||||||
|
*
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the event
|
||||||
|
* \param key the key to associate \a data with
|
||||||
|
* \param data (transfer none)(nullable): the data element, or \c NULL to
|
||||||
|
* remove any previous data associated with this \a key
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
wp_event_set_data (WpEvent * self, const gchar * key, const GValue * data)
|
||||||
|
{
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
g_return_if_fail (key != NULL);
|
||||||
|
GValue *data_copy = NULL;
|
||||||
|
|
||||||
|
if (data && G_IS_VALUE (data)) {
|
||||||
|
data_copy = g_new0 (GValue, 1);
|
||||||
|
g_value_init (data_copy, G_VALUE_TYPE (data));
|
||||||
|
g_value_copy (data, data_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_datalist_set_data_full (&self->datalist, key, data_copy,
|
||||||
|
data_copy ? destroy_event_data : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the data that was previously associated with \a key by
|
||||||
|
* wp_event_set_data()
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param self the event
|
||||||
|
* \return (transfer none)(nullable): the data associated with \a key or \c NULL
|
||||||
|
*/
|
||||||
|
const GValue *
|
||||||
|
wp_event_get_data (WpEvent * self, const gchar * key)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (self != NULL, NULL);
|
||||||
|
g_return_val_if_fail (key != NULL, NULL);
|
||||||
|
|
||||||
|
return g_datalist_get_data (&self->datalist, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
record_dependency (struct spa_list *list, const gchar *target,
|
||||||
|
const gchar *dependency)
|
||||||
|
{
|
||||||
|
HookData *hook_data;
|
||||||
|
spa_list_for_each (hook_data, list, link) {
|
||||||
|
if (!g_strcmp0 (wp_event_hook_get_name (hook_data->hook), target)) {
|
||||||
|
g_ptr_array_insert (hook_data->dependencies, -1, (gchar *) dependency);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
hook_exists_in (const gchar *hook_name, struct spa_list *list)
|
||||||
|
{
|
||||||
|
HookData *hook_data;
|
||||||
|
if (!spa_list_is_empty (list)) {
|
||||||
|
spa_list_for_each (hook_data, list, link) {
|
||||||
|
if (!g_strcmp0 (wp_event_hook_get_name (hook_data->hook), hook_name)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Collects all the hooks registered in the \a dispatcher that run for
|
||||||
|
* this \a event
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param event the event
|
||||||
|
* \param dispatcher the event dispatcher
|
||||||
|
* \return TRUE if at least one hook has been collected,
|
||||||
|
* FALSE if no hooks run for this event or an error occurred
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
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;
|
||||||
|
|
||||||
|
g_return_val_if_fail (event != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (WP_IS_EVENT_DISPATCHER (dispatcher), FALSE);
|
||||||
|
|
||||||
|
/* hooks already collected */
|
||||||
|
if (!spa_list_is_empty (&event->hooks))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
spa_list_init (&collected);
|
||||||
|
spa_list_init (&result);
|
||||||
|
spa_list_init (&remaining);
|
||||||
|
|
||||||
|
/* collect hooks that run for this event */
|
||||||
|
all_hooks = wp_event_dispatcher_new_hooks_iterator (dispatcher);
|
||||||
|
while (wp_iterator_next (all_hooks, &value)) {
|
||||||
|
WpEventHook *hook = g_value_get_object (&value);
|
||||||
|
|
||||||
|
if (wp_event_hook_runs_for_event (hook, event)) {
|
||||||
|
HookData *hook_data = hook_data_new (hook);
|
||||||
|
|
||||||
|
/* record "after" dependencies directly */
|
||||||
|
const gchar * const * strv =
|
||||||
|
wp_event_hook_get_runs_after_hooks (hook_data->hook);
|
||||||
|
while (strv && *strv) {
|
||||||
|
g_ptr_array_insert (hook_data->dependencies, -1, (gchar *) *strv);
|
||||||
|
strv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_list_append (&collected, &hook_data->link);
|
||||||
|
|
||||||
|
wp_trace_boxed (WP_TYPE_EVENT, event, "added "WP_OBJECT_FORMAT"(%s)",
|
||||||
|
WP_OBJECT_ARGS (hook), wp_event_hook_get_name (hook));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_unset (&value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spa_list_is_empty (&collected)) {
|
||||||
|
HookData *hook_data;
|
||||||
|
|
||||||
|
/* convert "before" dependencies into "after" dependencies */
|
||||||
|
spa_list_for_each (hook_data, &collected, link) {
|
||||||
|
const gchar * const * strv =
|
||||||
|
wp_event_hook_get_runs_before_hooks (hook_data->hook);
|
||||||
|
while (strv && *strv) {
|
||||||
|
/* record hook_data->hook as a dependency of the *strv hook */
|
||||||
|
record_dependency (&collected, *strv,
|
||||||
|
wp_event_hook_get_name (hook_data->hook));
|
||||||
|
strv++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort */
|
||||||
|
while (!spa_list_is_empty (&collected)) {
|
||||||
|
gboolean made_progress = FALSE;
|
||||||
|
|
||||||
|
/* examine each hook to see if its dependencies are satisfied in the
|
||||||
|
result list; if yes, then append it to the result too */
|
||||||
|
spa_list_consume (hook_data, &collected, link) {
|
||||||
|
guint deps_satisfied = 0;
|
||||||
|
|
||||||
|
spa_list_remove (&hook_data->link);
|
||||||
|
|
||||||
|
for (guint i = 0; i < hook_data->dependencies->len; i++) {
|
||||||
|
const gchar *dep = g_ptr_array_index (hook_data->dependencies, i);
|
||||||
|
/* if the dependency is already in the sorted result list or if
|
||||||
|
it doesn't exist at all, we consider it satisfied */
|
||||||
|
if (hook_exists_in (dep, &result) ||
|
||||||
|
!(hook_exists_in (dep, &collected) ||
|
||||||
|
hook_exists_in (dep, &remaining))) {
|
||||||
|
deps_satisfied++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps_satisfied == hook_data->dependencies->len) {
|
||||||
|
spa_list_append (&result, &hook_data->link);
|
||||||
|
made_progress = TRUE;
|
||||||
|
} else {
|
||||||
|
spa_list_append (&remaining, &hook_data->link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (made_progress) {
|
||||||
|
/* run again with the remaining hooks */
|
||||||
|
spa_list_insert_list (&collected, &remaining);
|
||||||
|
spa_list_init (&remaining);
|
||||||
|
}
|
||||||
|
else if (!spa_list_is_empty (&remaining)) {
|
||||||
|
/* if we did not make any progress towards growing the result list,
|
||||||
|
it means the dependencies cannot be satisfied because of circles */
|
||||||
|
wp_critical_boxed (WP_TYPE_EVENT, event, "detected circular "
|
||||||
|
"dependencies in the collected hooks!");
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
spa_list_consume (hook_data, &result, link) {
|
||||||
|
spa_list_remove (&hook_data->link);
|
||||||
|
hook_data_free (hook_data);
|
||||||
|
}
|
||||||
|
spa_list_consume (hook_data, &remaining, link) {
|
||||||
|
spa_list_remove (&hook_data->link);
|
||||||
|
hook_data_free (hook_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_list_insert_list (&event->hooks, &result);
|
||||||
|
return !spa_list_is_empty (&event->hooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct event_hooks_iterator_data
|
||||||
|
{
|
||||||
|
WpEvent *event;
|
||||||
|
HookData *cur;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
event_hooks_iterator_reset (WpIterator *it)
|
||||||
|
{
|
||||||
|
struct event_hooks_iterator_data *it_data = wp_iterator_get_user_data (it);
|
||||||
|
struct spa_list *list = &it_data->event->hooks;
|
||||||
|
|
||||||
|
if (!spa_list_is_empty (list))
|
||||||
|
it_data->cur = spa_list_first (&it_data->event->hooks, HookData, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
event_hooks_iterator_next (WpIterator *it, GValue *item)
|
||||||
|
{
|
||||||
|
struct event_hooks_iterator_data *it_data = wp_iterator_get_user_data (it);
|
||||||
|
struct spa_list *list = &it_data->event->hooks;
|
||||||
|
|
||||||
|
if (!spa_list_is_empty (list) &&
|
||||||
|
!spa_list_is_end (it_data->cur, list, link)) {
|
||||||
|
g_value_init (item, WP_TYPE_EVENT_HOOK);
|
||||||
|
g_value_set_object (item, it_data->cur->hook);
|
||||||
|
it_data->cur = spa_list_next (it_data->cur, link);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
event_hooks_iterator_fold (WpIterator *it, WpIteratorFoldFunc func, GValue *ret,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
struct event_hooks_iterator_data *it_data = wp_iterator_get_user_data (it);
|
||||||
|
struct spa_list *list = &it_data->event->hooks;
|
||||||
|
HookData *hook_data;
|
||||||
|
|
||||||
|
if (!spa_list_is_empty (list)) {
|
||||||
|
spa_list_for_each (hook_data, list, link) {
|
||||||
|
g_auto (GValue) item = G_VALUE_INIT;
|
||||||
|
g_value_init (&item, WP_TYPE_EVENT_HOOK);
|
||||||
|
g_value_set_object (&item, hook_data->hook);
|
||||||
|
if (!func (&item, ret, data))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
event_hooks_iterator_finalize (WpIterator *it)
|
||||||
|
{
|
||||||
|
struct event_hooks_iterator_data *it_data = wp_iterator_get_user_data (it);
|
||||||
|
wp_event_unref (it_data->event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const WpIteratorMethods event_hooks_iterator_methods = {
|
||||||
|
.version = WP_ITERATOR_METHODS_VERSION,
|
||||||
|
.reset = event_hooks_iterator_reset,
|
||||||
|
.next = event_hooks_iterator_next,
|
||||||
|
.fold = event_hooks_iterator_fold,
|
||||||
|
.finalize = event_hooks_iterator_finalize,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns an iterator that iterates over all the hooks that were
|
||||||
|
* collected by wp_event_collect_hooks()
|
||||||
|
* \ingroup wpevent
|
||||||
|
* \param event the event
|
||||||
|
* \return (transfer full): the new iterator
|
||||||
|
*/
|
||||||
|
WpIterator *
|
||||||
|
wp_event_new_hooks_iterator (WpEvent * event)
|
||||||
|
{
|
||||||
|
WpIterator *it = NULL;
|
||||||
|
struct event_hooks_iterator_data *it_data;
|
||||||
|
|
||||||
|
g_return_val_if_fail (event != NULL, NULL);
|
||||||
|
|
||||||
|
it = wp_iterator_new (&event_hooks_iterator_methods,
|
||||||
|
sizeof (struct event_hooks_iterator_data));
|
||||||
|
it_data = wp_iterator_get_user_data (it);
|
||||||
|
it_data->event = wp_event_ref (event);
|
||||||
|
event_hooks_iterator_reset (it);
|
||||||
|
return it;
|
||||||
|
}
|
78
lib/wp/event.h
Normal file
78
lib/wp/event.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2022 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WIREPLUMBER_EVENT_H__
|
||||||
|
#define __WIREPLUMBER_EVENT_H__
|
||||||
|
|
||||||
|
#include "properties.h"
|
||||||
|
#include "iterator.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _WpEventDispatcher WpEventDispatcher;
|
||||||
|
|
||||||
|
/*! \defgroup wpevent WpEvent */
|
||||||
|
/*!
|
||||||
|
* \struct WpEvent
|
||||||
|
*
|
||||||
|
* WpEvent describes an event, an event is an entity which can be pushed on to
|
||||||
|
* event stack and the event dispatcher is going to pick and dispatch it.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define WP_TYPE_EVENT (wp_event_get_type ())
|
||||||
|
WP_API
|
||||||
|
GType wp_event_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
typedef struct _WpEvent WpEvent;
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
WpEvent * wp_event_new (const gchar * type, gint priority,
|
||||||
|
WpProperties * properties, GObject * source, GObject * subject);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
WpEvent * wp_event_ref (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
void wp_event_unref (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
gint wp_event_get_priority (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
WpProperties * wp_event_get_properties (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
GObject * wp_event_get_source (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
GObject * wp_event_get_subject (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
GCancellable * wp_event_get_cancellable (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
void wp_event_stop_processing (WpEvent * self);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
void wp_event_set_data (WpEvent * self, const gchar * key, const GValue * data);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
const GValue * wp_event_get_data (WpEvent * self, const gchar * key);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
gboolean wp_event_collect_hooks (WpEvent * event,
|
||||||
|
WpEventDispatcher * dispatcher);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
WpIterator * wp_event_new_hooks_iterator (WpEvent * event);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpEvent, wp_event_unref)
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
@@ -6,6 +6,7 @@ wp_lib_sources = files(
|
|||||||
'device.c',
|
'device.c',
|
||||||
'endpoint.c',
|
'endpoint.c',
|
||||||
'error.c',
|
'error.c',
|
||||||
|
'event.c',
|
||||||
'event-dispatcher.c',
|
'event-dispatcher.c',
|
||||||
'event-hook.c',
|
'event-hook.c',
|
||||||
'factory.c',
|
'factory.c',
|
||||||
@@ -49,6 +50,7 @@ wp_lib_headers = files(
|
|||||||
'device.h',
|
'device.h',
|
||||||
'endpoint.h',
|
'endpoint.h',
|
||||||
'error.h',
|
'error.h',
|
||||||
|
'event.h',
|
||||||
'event-dispatcher.h',
|
'event-dispatcher.h',
|
||||||
'event-hook.h',
|
'event-hook.h',
|
||||||
'global-proxy.h',
|
'global-proxy.h',
|
||||||
|
@@ -151,7 +151,7 @@ wp_default_nodes_api_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* default metadata added */
|
/* default metadata added */
|
||||||
hook = wp_simple_event_hook_new ("metadata-added@default-nodes-api",
|
hook = wp_simple_event_hook_new ("metadata-added@default-nodes-api",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_metadata_added, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_added, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "metadata-added",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "metadata-added",
|
||||||
@@ -162,7 +162,7 @@ wp_default_nodes_api_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* default metadata changed */
|
/* default metadata changed */
|
||||||
hook = wp_simple_event_hook_new ("metadata-changed@default-nodes-api",
|
hook = wp_simple_event_hook_new ("metadata-changed@default-nodes-api",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_metadata_changed_hook, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_changed_hook, self, NULL));
|
||||||
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
@@ -592,7 +592,7 @@ wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* default metadata added */
|
/* default metadata added */
|
||||||
hook = wp_simple_event_hook_new ("metadata-added@default-nodes",
|
hook = wp_simple_event_hook_new ("metadata-added@default-nodes",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_metadata_added, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_added, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "metadata-added",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "metadata-added",
|
||||||
@@ -603,7 +603,7 @@ wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* default metadata changed */
|
/* default metadata changed */
|
||||||
hook = wp_simple_event_hook_new ("metadata-changed@default-nodes",
|
hook = wp_simple_event_hook_new ("metadata-changed@default-nodes",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_metadata_changed, self, NULL));
|
g_cclosure_new ((GCallback) on_metadata_changed, self, NULL));
|
||||||
|
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
@@ -632,7 +632,7 @@ wp_default_nodes_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* register rescan hook as an after event */
|
/* register rescan hook as an after event */
|
||||||
hook = wp_simple_event_hook_new("rescan@default-nodes",
|
hook = wp_simple_event_hook_new("rescan@default-nodes",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) rescan, self, NULL));
|
g_cclosure_new ((GCallback) rescan, self, NULL));
|
||||||
|
|
||||||
/* default.configured.audio.sink changed */
|
/* default.configured.audio.sink changed */
|
||||||
|
@@ -269,7 +269,7 @@ wp_default_profile_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* device added */
|
/* device added */
|
||||||
hook = wp_simple_event_hook_new ("device-added@m-default-profile",
|
hook = wp_simple_event_hook_new ("device-added@m-default-profile",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_device_added, self, NULL));
|
g_cclosure_new ((GCallback) on_device_added, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "device-added",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "device-added",
|
||||||
@@ -279,7 +279,7 @@ wp_default_profile_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* device params changed */
|
/* device params changed */
|
||||||
hook = wp_simple_event_hook_new ("device-parms-changed@m-default-profile",
|
hook = wp_simple_event_hook_new ("device-parms-changed@m-default-profile",
|
||||||
WP_EVENT_HOOK_PRIORITY_NORMAL,
|
NULL, NULL,
|
||||||
g_cclosure_new ((GCallback) on_device_params_changed_hook, self, NULL));
|
g_cclosure_new ((GCallback) on_device_params_changed_hook, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "device-params-changed",
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "device-params-changed",
|
||||||
|
@@ -1916,40 +1916,105 @@ static int
|
|||||||
simple_event_hook_new (lua_State *L)
|
simple_event_hook_new (lua_State *L)
|
||||||
{
|
{
|
||||||
WpEventHook *hook = NULL;
|
WpEventHook *hook = NULL;
|
||||||
gint priority = 0;
|
int before_size = 0, after_size = 0, i = 0;
|
||||||
gint priority_type = 0;
|
const gchar **before, **after;
|
||||||
const gchar *name;
|
const gchar *name;
|
||||||
GClosure *closure = NULL;
|
GClosure *closure = NULL;
|
||||||
|
|
||||||
|
/* discard any possible arguments after the first one to avoid
|
||||||
|
any surprises when working with absolute stack indices below */
|
||||||
|
lua_settop (L, 1);
|
||||||
|
|
||||||
/* validate arguments */
|
/* validate arguments */
|
||||||
luaL_checktype (L, 1, LUA_TTABLE);
|
luaL_checktype (L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
lua_pushliteral (L, "name");
|
if (lua_getfield (L, 1, "name") != LUA_TSTRING)
|
||||||
if (lua_gettable (L, 1) != LUA_TSTRING)
|
|
||||||
luaL_error(L, "SimpleEventHook: expected 'name' as string");
|
luaL_error(L, "SimpleEventHook: expected 'name' as string");
|
||||||
name = lua_tostring (L, -1);
|
|
||||||
lua_pop (L, 1);
|
|
||||||
|
|
||||||
lua_pushliteral (L, "priority");
|
if (lua_getfield (L, 1, "execute") != LUA_TFUNCTION)
|
||||||
priority_type = lua_gettable (L, 1);
|
|
||||||
if (priority_type == LUA_TNUMBER)
|
|
||||||
priority = lua_tointeger (L, -1);
|
|
||||||
else
|
|
||||||
luaL_error (L, "SimpleEventHook: expected 'priority' as number");
|
|
||||||
lua_pop (L, 1);
|
|
||||||
|
|
||||||
lua_pushliteral (L, "execute");
|
|
||||||
if (lua_gettable (L, 1) == LUA_TFUNCTION)
|
|
||||||
closure = wplua_function_to_closure (L, -1);
|
|
||||||
else
|
|
||||||
luaL_error (L, "SimpleEventHook: expected 'execute' as function");
|
luaL_error (L, "SimpleEventHook: expected 'execute' as function");
|
||||||
lua_pop (L, 1);
|
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new (name, priority, closure);
|
switch (lua_getfield (L, 1, "before")) {
|
||||||
|
case LUA_TTABLE:
|
||||||
|
lua_len (L, -1);
|
||||||
|
before_size = lua_tointeger (L, -1);
|
||||||
|
lua_pop (L, 1);
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
before_size = 1;
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
before_size = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
luaL_error(L, "SimpleEventHook: unexpected value type for 'before'; "
|
||||||
|
"should be table or string");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lua_getfield (L, 1, "after")) {
|
||||||
|
case LUA_TTABLE:
|
||||||
|
lua_len (L, -1);
|
||||||
|
after_size = lua_tointeger (L, -1);
|
||||||
|
lua_pop (L, 1);
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
after_size = 1;
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
after_size = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
luaL_error(L, "SimpleEventHook: unexpected value type for 'after'; "
|
||||||
|
"should be table or string");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate C stack space for before & after arrays */
|
||||||
|
before = before_size > 0 ?
|
||||||
|
(const gchar **) g_newa (gpointer, before_size + 1) : NULL;
|
||||||
|
after = after_size > 0 ?
|
||||||
|
(const gchar **) g_newa (gpointer, after_size + 1) : NULL;
|
||||||
|
|
||||||
|
/* parse before */
|
||||||
|
if (lua_type (L, 4) == LUA_TTABLE && before_size > 0) {
|
||||||
|
i = 0;
|
||||||
|
lua_pushnil (L);
|
||||||
|
while (lua_next (L, 4) && i < before_size) {
|
||||||
|
before[i++] = luaL_checkstring (L, -1);
|
||||||
|
/* bring the key on top without popping the string value */
|
||||||
|
lua_rotate (L, lua_gettop (L) - 1, 1);
|
||||||
|
}
|
||||||
|
before[i] = NULL;
|
||||||
|
} else if (lua_type (L, 4) == LUA_TSTRING) {
|
||||||
|
before[0] = lua_tostring (L, 4);
|
||||||
|
before[1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse after */
|
||||||
|
if (lua_type (L, 5) == LUA_TTABLE && after_size > 0) {
|
||||||
|
i = 0;
|
||||||
|
lua_pushnil (L);
|
||||||
|
while (lua_next (L, 5) && i < after_size - 1) {
|
||||||
|
after[i++] = luaL_checkstring (L, -1);
|
||||||
|
/* bring the key on top without popping the string value */
|
||||||
|
lua_rotate (L, lua_gettop (L) - 1, 1);
|
||||||
|
}
|
||||||
|
after[i] = NULL;
|
||||||
|
} else if (lua_type (L, 5) == LUA_TSTRING) {
|
||||||
|
after[0] = lua_tostring (L, 5);
|
||||||
|
after[1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = lua_tostring (L, 2);
|
||||||
|
closure = wplua_function_to_closure (L, 3);
|
||||||
|
|
||||||
|
hook = wp_simple_event_hook_new (name, before, after, closure);
|
||||||
|
|
||||||
|
/* clear the lua stack now to make some space */
|
||||||
|
lua_settop (L, 1);
|
||||||
|
|
||||||
wplua_pushobject (L, hook);
|
wplua_pushobject (L, hook);
|
||||||
|
|
||||||
lua_pushliteral (L, "interests");
|
if (lua_getfield (L, 1, "interests") == LUA_TTABLE) {
|
||||||
if (lua_gettable (L, 1) == LUA_TTABLE) {
|
|
||||||
lua_pushnil (L);
|
lua_pushnil (L);
|
||||||
while (lua_next (L, -2)) {
|
while (lua_next (L, -2)) {
|
||||||
WpObjectInterest *interest =
|
WpObjectInterest *interest =
|
||||||
@@ -2090,49 +2155,117 @@ static int
|
|||||||
async_event_hook_new (lua_State *L)
|
async_event_hook_new (lua_State *L)
|
||||||
{
|
{
|
||||||
WpEventHook *hook = NULL;
|
WpEventHook *hook = NULL;
|
||||||
|
int before_size = 0, after_size = 0, i = 0;
|
||||||
|
const gchar **before, **after;
|
||||||
const gchar *name;
|
const gchar *name;
|
||||||
gint priority = 0;
|
|
||||||
gint priority_type = 0;
|
|
||||||
GClosure *get_next_step = NULL;
|
GClosure *get_next_step = NULL;
|
||||||
GClosure *execute_step = NULL;
|
GClosure *execute_step = NULL;
|
||||||
|
|
||||||
|
/* discard any possible arguments after the first one to avoid
|
||||||
|
any surprises when working with absolute stack indices below */
|
||||||
|
lua_settop (L, 1);
|
||||||
|
|
||||||
/* validate arguments */
|
/* validate arguments */
|
||||||
luaL_checktype (L, 1, LUA_TTABLE);
|
luaL_checktype (L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
lua_pushliteral (L, "name");
|
if (lua_getfield (L, 1, "name") != LUA_TSTRING)
|
||||||
if (lua_gettable (L, 1) != LUA_TSTRING)
|
luaL_error(L, "AsyncEventHook: expected 'name' as string");
|
||||||
luaL_error (L, "AsyncEventHook: expected 'name' as string");
|
|
||||||
name = lua_tostring (L, -1);
|
|
||||||
lua_pop (L, 1);
|
|
||||||
|
|
||||||
lua_pushliteral (L, "priority");
|
if (lua_getfield (L, 1, "steps") != LUA_TTABLE)
|
||||||
priority_type = lua_gettable(L, 1);
|
|
||||||
if (priority_type == LUA_TNUMBER)
|
|
||||||
priority = lua_tointeger (L, -1);
|
|
||||||
else
|
|
||||||
luaL_error(L, "AsyncEventHook: expected 'priority' as number");
|
|
||||||
lua_pop (L, 1);
|
|
||||||
|
|
||||||
lua_pushliteral (L, "steps");
|
|
||||||
if (lua_gettable (L, 1) != LUA_TTABLE)
|
|
||||||
luaL_error (L, "AsyncEventHook: expected 'steps' as table");
|
luaL_error (L, "AsyncEventHook: expected 'steps' as table");
|
||||||
|
|
||||||
async_event_hook_prepare_steps_table (L, -1);
|
switch (lua_getfield (L, 1, "before")) {
|
||||||
|
case LUA_TTABLE:
|
||||||
|
lua_len (L, -1);
|
||||||
|
before_size = lua_tointeger (L, -1);
|
||||||
|
lua_pop (L, 1);
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
before_size = 1;
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
before_size = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
luaL_error(L, "AsyncEventHook: unexpected value type for 'before'; "
|
||||||
|
"should be table or string");
|
||||||
|
}
|
||||||
|
|
||||||
lua_pushvalue (L, -1);
|
switch (lua_getfield (L, 1, "after")) {
|
||||||
|
case LUA_TTABLE:
|
||||||
|
lua_len (L, -1);
|
||||||
|
after_size = lua_tointeger (L, -1);
|
||||||
|
lua_pop (L, 1);
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
after_size = 1;
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
after_size = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
luaL_error(L, "AsyncEventHook: unexpected value type for 'after'; "
|
||||||
|
"should be table or string");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate C stack space for before & after arrays */
|
||||||
|
before = before_size > 0 ?
|
||||||
|
(const gchar **) g_newa (gpointer, before_size + 1) : NULL;
|
||||||
|
after = after_size > 0 ?
|
||||||
|
(const gchar **) g_newa (gpointer, after_size + 1) : NULL;
|
||||||
|
|
||||||
|
/* parse before */
|
||||||
|
if (lua_type (L, 4) == LUA_TTABLE && before_size > 0) {
|
||||||
|
i = 0;
|
||||||
|
lua_pushnil (L);
|
||||||
|
while (lua_next (L, 4) && i < before_size) {
|
||||||
|
before[i++] = luaL_checkstring (L, -1);
|
||||||
|
/* bring the key on top without popping the string value */
|
||||||
|
lua_rotate (L, lua_gettop (L) - 1, 1);
|
||||||
|
}
|
||||||
|
before[i] = NULL;
|
||||||
|
} else if (lua_type (L, 4) == LUA_TSTRING) {
|
||||||
|
before[0] = lua_tostring (L, 4);
|
||||||
|
before[1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse after */
|
||||||
|
if (lua_type (L, 5) == LUA_TTABLE && after_size > 0) {
|
||||||
|
i = 0;
|
||||||
|
lua_pushnil (L);
|
||||||
|
while (lua_next (L, 5) && i < after_size - 1) {
|
||||||
|
after[i++] = luaL_checkstring (L, -1);
|
||||||
|
/* bring the key on top without popping the string value */
|
||||||
|
lua_rotate (L, lua_gettop (L) - 1, 1);
|
||||||
|
}
|
||||||
|
after[i] = NULL;
|
||||||
|
} else if (lua_type (L, 5) == LUA_TSTRING) {
|
||||||
|
after[0] = lua_tostring (L, 5);
|
||||||
|
after[1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = lua_tostring (L, 2);
|
||||||
|
async_event_hook_prepare_steps_table (L, 3);
|
||||||
|
|
||||||
|
lua_pushvalue (L, 3); /* pass 'steps' table as upvalue */
|
||||||
lua_pushcclosure (L, async_event_hook_get_next_step, 1);
|
lua_pushcclosure (L, async_event_hook_get_next_step, 1);
|
||||||
get_next_step = wplua_function_to_closure (L, -1);
|
get_next_step = wplua_function_to_closure (L, -1);
|
||||||
lua_pop (L, 1);
|
lua_pop (L, 1);
|
||||||
|
|
||||||
|
lua_pushvalue (L, 3); /* pass 'steps' table as upvalue */
|
||||||
lua_pushcclosure (L, async_event_hook_execute_step, 1);
|
lua_pushcclosure (L, async_event_hook_execute_step, 1);
|
||||||
execute_step = wplua_function_to_closure (L, -1);
|
execute_step = wplua_function_to_closure (L, -1);
|
||||||
lua_pop (L, 1);
|
lua_pop (L, 1);
|
||||||
|
|
||||||
hook = wp_async_event_hook_new (name, priority, get_next_step, execute_step);
|
hook = wp_async_event_hook_new (name, before, after, get_next_step,
|
||||||
|
execute_step);
|
||||||
|
|
||||||
|
/* clear the lua stack now to make some space */
|
||||||
|
lua_settop (L, 1);
|
||||||
|
|
||||||
wplua_pushobject (L, hook);
|
wplua_pushobject (L, hook);
|
||||||
|
|
||||||
lua_pushliteral (L, "interests");
|
if (lua_getfield (L, 1, "interests") == LUA_TTABLE) {
|
||||||
if (lua_gettable (L, 1) == LUA_TTABLE) {
|
|
||||||
lua_pushnil (L);
|
lua_pushnil (L);
|
||||||
while (lua_next (L, -2)) {
|
while (lua_next (L, -2)) {
|
||||||
WpObjectInterest *interest =
|
WpObjectInterest *interest =
|
||||||
|
@@ -191,24 +191,11 @@ local Feature = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local HookPriority = {
|
|
||||||
LOWEST = -500,
|
|
||||||
ULTRA_LOW = -300,
|
|
||||||
VERY_LOW = -200,
|
|
||||||
LOW = -100,
|
|
||||||
NORMAL = 0,
|
|
||||||
HIGH = 100,
|
|
||||||
VERY_HIGH = 200,
|
|
||||||
ULTRA_HIGH = 300,
|
|
||||||
HIGHEST = 500,
|
|
||||||
}
|
|
||||||
|
|
||||||
SANDBOX_EXPORT = {
|
SANDBOX_EXPORT = {
|
||||||
Debug = Debug,
|
Debug = Debug,
|
||||||
Id = Id,
|
Id = Id,
|
||||||
Features = Features,
|
Features = Features,
|
||||||
Feature = Feature,
|
Feature = Feature,
|
||||||
HookPriority = HookPriority,
|
|
||||||
GLib = GLib,
|
GLib = GLib,
|
||||||
I18n = I18n,
|
I18n = I18n,
|
||||||
Log = WpLog,
|
Log = WpLog,
|
||||||
|
@@ -322,7 +322,7 @@ wp_standard_event_source_enable (WpPlugin * plugin, WpTransition * transition)
|
|||||||
|
|
||||||
/* install hook to restore the rescan_scheduled state after rescanning */
|
/* install hook to restore the rescan_scheduled state after rescanning */
|
||||||
self->rescan_done_hook = wp_simple_event_hook_new (
|
self->rescan_done_hook = wp_simple_event_hook_new (
|
||||||
"rescan-done@std-event-source", WP_EVENT_HOOK_PRIORITY_LOWEST,
|
"rescan-done@std-event-source", NULL, NULL,
|
||||||
g_cclosure_new_object ((GCallback) on_rescan_done, G_OBJECT (self)));
|
g_cclosure_new_object ((GCallback) on_rescan_done, G_OBJECT (self)));
|
||||||
wp_interest_event_hook_add_interest (
|
wp_interest_event_hook_add_interest (
|
||||||
WP_INTEREST_EVENT_HOOK (self->rescan_done_hook),
|
WP_INTEREST_EVENT_HOOK (self->rescan_done_hook),
|
||||||
|
@@ -60,39 +60,51 @@ test_events_basic (TestFixture *self, gconstpointer user_data)
|
|||||||
g_autoptr (WpEventDispatcher) dispatcher = NULL;
|
g_autoptr (WpEventDispatcher) dispatcher = NULL;
|
||||||
g_autoptr (WpEventHook) hook = NULL;
|
g_autoptr (WpEventHook) hook = NULL;
|
||||||
WpEvent *event1 = NULL, *event2 = NULL, *event3 = NULL;
|
WpEvent *event1 = NULL, *event2 = NULL, *event3 = NULL;
|
||||||
|
const gchar **before, **after;
|
||||||
|
|
||||||
dispatcher = wp_event_dispatcher_get_instance (self->base.core);
|
dispatcher = wp_event_dispatcher_get_instance (self->base.core);
|
||||||
g_assert_nonnull (dispatcher);
|
g_assert_nonnull (dispatcher);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-a", 10,
|
before = (const gchar *[]) { "hook-d", "hook-b", NULL };
|
||||||
|
after = (const gchar *[]) { "hook-c", NULL };
|
||||||
|
hook = wp_simple_event_hook_new ("hook-a", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_a, self, NULL));
|
g_cclosure_new ((GCallback) hook_a, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-b", -200,
|
before = NULL;
|
||||||
|
after = NULL;
|
||||||
|
hook = wp_simple_event_hook_new ("hook-b", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_b, self, NULL));
|
g_cclosure_new ((GCallback) hook_b, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-c", 100,
|
before = NULL;
|
||||||
|
after = NULL;
|
||||||
|
hook = wp_simple_event_hook_new ("hook-c", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_c, self, NULL));
|
g_cclosure_new ((GCallback) hook_c, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-d", 0,
|
/* wrong before/after dependencies shouldn't matter as this hook is not used */
|
||||||
|
before = (const gchar *[]) { "hook-c", "hook-a", NULL };
|
||||||
|
after = (const gchar *[]) { "hook-b", NULL };
|
||||||
|
hook = wp_simple_event_hook_new ("hook-d", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_d, self, NULL));
|
g_cclosure_new ((GCallback) hook_d, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type2", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type2", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-quit", -1000,
|
before = NULL;
|
||||||
|
after = (const gchar *[]) { "hook-a", "hook-b", "hook-c", "hook-d", NULL };
|
||||||
|
hook = wp_simple_event_hook_new ("hook-quit", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_quit, self, NULL));
|
g_cclosure_new ((GCallback) hook_quit, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
@@ -219,32 +231,41 @@ test_events_async_hook (TestFixture *self, gconstpointer user_data)
|
|||||||
{
|
{
|
||||||
g_autoptr (WpEventDispatcher) dispatcher = NULL;
|
g_autoptr (WpEventDispatcher) dispatcher = NULL;
|
||||||
g_autoptr (WpEventHook) hook = NULL;
|
g_autoptr (WpEventHook) hook = NULL;
|
||||||
|
const gchar **before, **after;
|
||||||
|
|
||||||
dispatcher = wp_event_dispatcher_get_instance (self->base.core);
|
dispatcher = wp_event_dispatcher_get_instance (self->base.core);
|
||||||
g_assert_nonnull (dispatcher);
|
g_assert_nonnull (dispatcher);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-a", 10,
|
before = NULL;
|
||||||
|
after = NULL;
|
||||||
|
hook = wp_simple_event_hook_new ("hook-a", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_a, self, NULL));
|
g_cclosure_new ((GCallback) hook_a, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-b", -200,
|
before = (const gchar *[]) { "hook-quit", NULL };
|
||||||
|
after = (const gchar *[]) { "hook-a", NULL };
|
||||||
|
hook = wp_simple_event_hook_new ("hook-b", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_b, self, NULL));
|
g_cclosure_new ((GCallback) hook_b, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-c", 100,
|
before = (const gchar *[]) { "hook-a", NULL };
|
||||||
|
after = NULL;
|
||||||
|
hook = wp_simple_event_hook_new ("hook-c", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_c, self, NULL));
|
g_cclosure_new ((GCallback) hook_c, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_simple_event_hook_new ("hook-quit", -1000,
|
before = NULL;
|
||||||
|
after = NULL;
|
||||||
|
hook = wp_simple_event_hook_new ("hook-quit", before, after,
|
||||||
g_cclosure_new ((GCallback) hook_quit, self, NULL));
|
g_cclosure_new ((GCallback) hook_quit, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
WP_CONSTRAINT_TYPE_PW_PROPERTY, "event.type", "=s", "type1", NULL);
|
||||||
@@ -253,7 +274,9 @@ test_events_async_hook (TestFixture *self, gconstpointer user_data)
|
|||||||
wp_event_dispatcher_register_hook (dispatcher, hook);
|
wp_event_dispatcher_register_hook (dispatcher, hook);
|
||||||
g_clear_object (&hook);
|
g_clear_object (&hook);
|
||||||
|
|
||||||
hook = wp_async_event_hook_new ("async-test-hook", 50,
|
before = (const gchar *[]) { "hook-a", NULL };
|
||||||
|
after = (const gchar *[]) { "hook-c", NULL };
|
||||||
|
hook = wp_async_event_hook_new ("async-test-hook", before, after,
|
||||||
g_cclosure_new ((GCallback) async_hook_get_next_step, self, NULL),
|
g_cclosure_new ((GCallback) async_hook_get_next_step, self, NULL),
|
||||||
g_cclosure_new ((GCallback) async_hook_execute_step, self, NULL));
|
g_cclosure_new ((GCallback) async_hook_execute_step, self, NULL));
|
||||||
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
wp_interest_event_hook_add_interest (WP_INTEREST_EVENT_HOOK (hook),
|
||||||
|
@@ -14,11 +14,12 @@ local function check_results()
|
|||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
assert(tags[inc()] == "simple-first")
|
||||||
assert(tags[inc()] == "simple-1")
|
assert(tags[inc()] == "simple-1")
|
||||||
assert(tags[inc()] == "async-start")
|
assert(tags[inc()] == "async-start")
|
||||||
assert(tags[inc()] == "async-start-advance")
|
assert(tags[inc()] == "async-start-advance")
|
||||||
assert(tags[inc()] == "async-step2")
|
assert(tags[inc()] == "async-step2")
|
||||||
assert(tags[inc()] == "simple-2")
|
assert(tags[inc()] == "simple-last")
|
||||||
end
|
end
|
||||||
|
|
||||||
local common_interests = {
|
local common_interests = {
|
||||||
@@ -29,7 +30,8 @@ local common_interests = {
|
|||||||
|
|
||||||
AsyncEventHook {
|
AsyncEventHook {
|
||||||
name = "test-async-hook",
|
name = "test-async-hook",
|
||||||
priority = 10,
|
before = "test-last-hook" ,
|
||||||
|
after = { "test-first-hook", "test-simple-hook" },
|
||||||
interests = common_interests,
|
interests = common_interests,
|
||||||
steps = {
|
steps = {
|
||||||
start = {
|
start = {
|
||||||
@@ -53,9 +55,19 @@ AsyncEventHook {
|
|||||||
},
|
},
|
||||||
}:register()
|
}:register()
|
||||||
|
|
||||||
|
SimpleEventHook {
|
||||||
|
name = "test-first-hook",
|
||||||
|
before = { "test-simple-hook", "test-last-hook" },
|
||||||
|
interests = common_interests,
|
||||||
|
execute = function (event)
|
||||||
|
checkpoint("simple-first")
|
||||||
|
end
|
||||||
|
}:register()
|
||||||
|
|
||||||
SimpleEventHook {
|
SimpleEventHook {
|
||||||
name = "test-simple-hook",
|
name = "test-simple-hook",
|
||||||
priority = 15,
|
after = { "test-first-hook" },
|
||||||
|
before = {},
|
||||||
interests = common_interests,
|
interests = common_interests,
|
||||||
execute = function (event)
|
execute = function (event)
|
||||||
checkpoint("simple-1")
|
checkpoint("simple-1")
|
||||||
@@ -64,10 +76,9 @@ SimpleEventHook {
|
|||||||
|
|
||||||
SimpleEventHook {
|
SimpleEventHook {
|
||||||
name = "test-last-hook",
|
name = "test-last-hook",
|
||||||
priority = -1000,
|
|
||||||
interests = common_interests,
|
interests = common_interests,
|
||||||
execute = function (event)
|
execute = function (event)
|
||||||
checkpoint("simple-2")
|
checkpoint("simple-last")
|
||||||
check_results()
|
check_results()
|
||||||
Script:finish_activation()
|
Script:finish_activation()
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user