Files
wireplumber/lib/wp/event-hook.c
George Kiagiadakis 4736d56557 log: implement a log topics system, like pipewire
The intention is to make checks for enabled log topics faster.

Every topic has its own structure that is statically defined in the file
where the logs are printed from. The structure is initialized transparently
when it is first used and it contains all the log level flags for the levels
that this topic should print messages. It is then checked on the wp_log()
macro before printing the message.

Topics from SPA/PipeWire are also handled natively, so messages are printed
directly without checking if the topic is enabled, since the PipeWire and SPA
macros do the checking themselves.

Messages coming from GLib are checked inside the handler.

An internal WpLogFields object is used to manage the state of each log
message, populating all the fields appropriately from the place they
are coming from (wp_log, spa_log, glib log), formatting the message and
then printing it. For printing to the journald, we still use the glib
message handler, converting all the needed fields to GLogField on demand.
That message handler does not do any checks for the topic or the level, so
we can just call it to send the message.
2023-05-16 20:42:28 +03:00

747 lines
22 KiB
C

/* WirePlumber
*
* Copyright © 2022 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "event-hook.h"
#include "event-dispatcher.h"
#include "transition.h"
#include "log.h"
#include "wpenums.h"
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-event-hook")
typedef struct _WpEventHookPrivate WpEventHookPrivate;
struct _WpEventHookPrivate
{
GWeakRef dispatcher;
gchar *name;
gchar **before;
gchar **after;
};
enum {
PROP_0,
PROP_NAME,
PROP_RUNS_BEFORE_HOOKS,
PROP_RUNS_AFTER_HOOKS,
PROP_DISPATCHER,
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEventHook, wp_event_hook, G_TYPE_OBJECT)
static void
wp_event_hook_init (WpEventHook * self)
{
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
g_weak_ref_init (&priv->dispatcher, NULL);
}
static void
wp_event_hook_finalize (GObject * object)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
g_weak_ref_clear (&priv->dispatcher);
g_strfreev (priv->before);
g_strfreev (priv->after);
g_free (priv->name);
G_OBJECT_CLASS (wp_event_hook_parent_class)->finalize (object);
}
static void
wp_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
switch (property_id) {
case PROP_NAME:
priv->name = g_value_dup_string (value);
break;
case PROP_RUNS_BEFORE_HOOKS:
priv->before = g_value_dup_boxed (value);
break;
case PROP_RUNS_AFTER_HOOKS:
priv->after = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_event_hook_get_property (GObject * object, guint property_id, GValue * value,
GParamSpec * pspec)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
switch (property_id) {
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_RUNS_BEFORE_HOOKS:
g_value_set_boxed (value, priv->before);
break;
case PROP_RUNS_AFTER_HOOKS:
g_value_set_boxed (value, priv->after);
break;
case PROP_DISPATCHER:
g_value_take_object (value, wp_event_hook_get_dispatcher (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_event_hook_class_init (WpEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = wp_event_hook_finalize;
object_class->set_property = wp_event_hook_set_property;
object_class->get_property = wp_event_hook_get_property;
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name", "name", "The hook name", "",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_RUNS_BEFORE_HOOKS,
g_param_spec_boxed ("runs-before-hooks", "runs-before-hooks",
"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_object_class_install_property (object_class, PROP_DISPATCHER,
g_param_spec_object ("dispatcher", "dispatcher",
"The associated event dispatcher", WP_TYPE_EVENT_DISPATCHER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Returns the name of the hook
*
* \ingroup wpeventhook
* \param self the event hook
* \return the event hook name
*/
const gchar *
wp_event_hook_get_name (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), 0);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
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
*
* \ingroup wpeventhook
* \param self the event hook
* \return (transfer full)(nullable): the event dispatcher on which this hook
* is registered, or NULL if the hook is not registered
*/
WpEventDispatcher *
wp_event_hook_get_dispatcher (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
return g_weak_ref_get (&priv->dispatcher);
}
void
wp_event_hook_set_dispatcher (WpEventHook * self, WpEventDispatcher * dispatcher)
{
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
g_weak_ref_set (&priv->dispatcher, dispatcher);
}
/*!
* \brief Checks if the hook should be executed for a given event
*
* \ingroup wpeventhook
* \param self the event hook
* \param event the event
* \return TRUE if the hook should be executed for the given event,
* FALSE otherwise
*/
gboolean
wp_event_hook_runs_for_event (WpEventHook * self, WpEvent * event)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), FALSE);
g_return_val_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->runs_for_event, FALSE);
return WP_EVENT_HOOK_GET_CLASS (self)->runs_for_event (self, event);
}
/*!
* \brief Runs the hook on the given event
*
* \ingroup wpeventhook
* \param self the event hook
* \param event the event that triggered the hook
* \param cancellable (nullable): a GCancellable to cancel the async operation
* \param callback (scope async): a callback to fire after execution of the hook
* has completed
* \param callback_data (closure): data for the callback
*/
void
wp_event_hook_run (WpEventHook * self,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
g_return_if_fail (WP_IS_EVENT_HOOK (self));
g_return_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->run);
WP_EVENT_HOOK_GET_CLASS (self)->run (self, event, cancellable, callback,
callback_data);
}
/*!
* \brief Finishes the async operation that was started by wp_event_hook_run()
*
* \ingroup wpeventhook
* \param self the event hook
* \param res the async operation result
* \param error (out) (optional): the error of the operation, if any
* \returns FALSE if there was an error, TRUE otherwise
*/
gboolean
wp_event_hook_finish (WpEventHook * self, GAsyncResult * res, GError ** error)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), FALSE);
g_return_val_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->finish, FALSE);
return WP_EVENT_HOOK_GET_CLASS (self)->finish (self, res, error);
}
/*!
* \struct WpInterestEventHook
*
* An event hook that declares interest in specific events. This subclass
* implements the WpEventHook.runs_for_event() vmethod and returns TRUE from
* that method if the given event has properties that match one of the declared
* interests.
*/
typedef struct _WpInterestEventHookPrivate WpInterestEventHookPrivate;
struct _WpInterestEventHookPrivate
{
/* element-type: WpObjectInterest* */
GPtrArray *interests;
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpInterestEventHook,
wp_interest_event_hook, WP_TYPE_EVENT_HOOK)
static void
wp_interest_event_hook_init (WpInterestEventHook * self)
{
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
priv->interests = g_ptr_array_new_with_free_func (
(GDestroyNotify) wp_object_interest_unref);
}
static void
wp_interest_event_hook_finalize (GObject * object)
{
WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (object);
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_clear_pointer (&priv->interests, g_ptr_array_unref);
G_OBJECT_CLASS (wp_interest_event_hook_parent_class)->finalize (object);
}
static gboolean
wp_interest_event_hook_runs_for_event (WpEventHook * hook, WpEvent * event)
{
WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (hook);
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_autoptr (WpProperties) properties = wp_event_get_properties (event);
g_autoptr (GObject) subject = wp_event_get_subject (event);
GType gtype = subject ? G_OBJECT_TYPE (subject) : WP_TYPE_EVENT;
guint i;
WpObjectInterest *interest = NULL;
WpInterestMatch match;
const int MATCH_ALL_PROPS = (WP_INTEREST_MATCH_PW_GLOBAL_PROPERTIES |
WP_INTEREST_MATCH_PW_PROPERTIES |
WP_INTEREST_MATCH_G_PROPERTIES);
for (i = 0; i < priv->interests->len; i++) {
interest = g_ptr_array_index (priv->interests, i);
match = wp_object_interest_matches_full (interest,
WP_INTEREST_MATCH_FLAGS_CHECK_ALL,
gtype, subject, properties, properties);
/* the interest may have a GType that matches the GType of the subject
or it may have WP_TYPE_EVENT as its GType, in which case it will
match any type of subject */
if (match == WP_INTEREST_MATCH_ALL)
return TRUE;
else if (subject && (match & MATCH_ALL_PROPS) == MATCH_ALL_PROPS) {
match = wp_object_interest_matches_full (interest, 0,
WP_TYPE_EVENT, NULL, NULL, NULL);
if (match & WP_INTEREST_MATCH_GTYPE)
return TRUE;
}
}
return FALSE;
}
static void
wp_interest_event_hook_class_init (WpInterestEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_interest_event_hook_finalize;
hook_class->runs_for_event = wp_interest_event_hook_runs_for_event;
}
/*!
* \brief Equivalent to:
* \code
* WpObjectInterest *i = wp_object_interest_new (WP_TYPE_EVENT, ...);
* wp_interest_event_hook_add_interest_full (self, i);
* \endcode
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* \ingroup wpeventhook
* \param self the event hook
* \param ... a list of constraints, terminated by NULL
*/
void
wp_interest_event_hook_add_interest (WpInterestEventHook * self, ...)
{
WpObjectInterest *interest;
va_list args;
g_return_if_fail (WP_IS_INTEREST_EVENT_HOOK (self));
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_EVENT, &args);
wp_interest_event_hook_add_interest_full (self, interest);
va_end (args);
}
/*!
* \brief Declares interest on events. The interest must be constructed to
* match WP_TYPE_EVENT objects and it is going to be matched against
* the WpEvent's properties.
*
* \param self the event hook
* \param interest (transfer full): the event interest
*/
void
wp_interest_event_hook_add_interest_full (WpInterestEventHook * self,
WpObjectInterest * interest)
{
g_autoptr (GError) error = NULL;
g_return_if_fail (WP_IS_INTEREST_EVENT_HOOK (self));
if (G_UNLIKELY (!wp_object_interest_validate (interest, &error))) {
wp_critical_object (self, "interest validation failed: %s",
error->message);
wp_object_interest_unref (interest);
return;
}
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_ptr_array_add (priv->interests, interest);
}
/*!
* \struct WpSimpleEventHook
*
* An event hook that runs a GClosure, synchronously.
*/
struct _WpSimpleEventHook
{
WpInterestEventHook parent;
GClosure *closure;
};
enum {
PROP_SIMPLE_0,
PROP_SIMPLE_CLOSURE,
};
G_DEFINE_TYPE (WpSimpleEventHook, wp_simple_event_hook,
WP_TYPE_INTEREST_EVENT_HOOK)
static void
wp_simple_event_hook_init (WpSimpleEventHook * self)
{
}
static void
wp_simple_event_hook_finalize (GObject * object)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (object);
g_clear_pointer (&self->closure, g_closure_unref);
G_OBJECT_CLASS (wp_simple_event_hook_parent_class)->finalize (object);
}
static void
wp_simple_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (object);
switch (property_id) {
case PROP_SIMPLE_CLOSURE:
self->closure = g_value_dup_boxed (value);
g_closure_sink (self->closure);
if (G_CLOSURE_NEEDS_MARSHAL (self->closure))
g_closure_set_marshal (self->closure, g_cclosure_marshal_generic);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_simple_event_hook_run (WpEventHook * hook,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (hook);
GValue values[2] = { G_VALUE_INIT };
g_value_init (&values[0], WP_TYPE_EVENT);
g_value_set_boxed (&values[0], event);
g_closure_invoke (self->closure, NULL, 1, values, NULL);
g_value_unset (&values[0]);
callback ((GObject *) self, NULL, callback_data);
}
static gboolean
wp_simple_event_hook_finish (WpEventHook * hook, GAsyncResult * res,
GError ** error)
{
return TRUE;
}
static void
wp_simple_event_hook_class_init (WpSimpleEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_simple_event_hook_finalize;
object_class->set_property = wp_simple_event_hook_set_property;
hook_class->run = wp_simple_event_hook_run;
hook_class->finish = wp_simple_event_hook_finish;
g_object_class_install_property (object_class, PROP_SIMPLE_CLOSURE,
g_param_spec_boxed ("closure", "closure", "The closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Constructs a new simple event hook
*
* \param name the name 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
* should accept two parameters: the event dispatcher and the event, returning
* nothing
* \return a new simple event hook
*/
WpEventHook *
wp_simple_event_hook_new (const gchar *name,
const gchar * before[], const gchar * after[], GClosure * closure)
{
g_return_val_if_fail (closure != NULL, NULL);
return g_object_new (WP_TYPE_SIMPLE_EVENT_HOOK,
"name", name,
"runs-before-hooks", before,
"runs-after-hooks", after,
"closure", closure,
NULL);
}
/* WpAsyncEventHookTransition */
#define WP_TYPE_ASYNC_EVENT_HOOK_TRANSITION \
(wp_async_event_hook_transition_get_type ())
G_DECLARE_FINAL_TYPE (WpAsyncEventHookTransition,
wp_async_event_hook_transition,
WP, ASYNC_EVENT_HOOK_TRANSITION, WpTransition)
/*!
* \struct WpAsyncEventHook
*
* An event hook that runs a WpTransition, implemented with closures.
*/
struct _WpAsyncEventHook
{
WpInterestEventHook parent;
GClosure *get_next_step;
GClosure *execute_step;
};
enum {
PROP_ASYNC_0,
PROP_ASYNC_GET_NEXT_STEP,
PROP_ASYNC_EXECUTE_STEP,
};
G_DEFINE_TYPE (WpAsyncEventHook, wp_async_event_hook,
WP_TYPE_INTEREST_EVENT_HOOK)
static void
wp_async_event_hook_init (WpAsyncEventHook * self)
{
}
static void
wp_async_event_hook_finalize (GObject * object)
{
WpAsyncEventHook *self = WP_ASYNC_EVENT_HOOK (object);
g_clear_pointer (&self->get_next_step, g_closure_unref);
g_clear_pointer (&self->execute_step, g_closure_unref);
G_OBJECT_CLASS (wp_async_event_hook_parent_class)->finalize (object);
}
static void
wp_async_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpAsyncEventHook *self = WP_ASYNC_EVENT_HOOK (object);
switch (property_id) {
case PROP_ASYNC_GET_NEXT_STEP:
self->get_next_step = g_value_dup_boxed (value);
g_closure_sink (self->get_next_step);
if (G_CLOSURE_NEEDS_MARSHAL (self->get_next_step))
g_closure_set_marshal (self->get_next_step, g_cclosure_marshal_generic);
break;
case PROP_ASYNC_EXECUTE_STEP:
self->execute_step = g_value_dup_boxed (value);
g_closure_sink (self->execute_step);
if (G_CLOSURE_NEEDS_MARSHAL (self->execute_step))
g_closure_set_marshal (self->execute_step, g_cclosure_marshal_VOID__UINT);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_async_event_hook_run (WpEventHook * hook,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
WpTransition *transition = wp_transition_new (
WP_TYPE_ASYNC_EVENT_HOOK_TRANSITION,
hook, cancellable, callback, callback_data);
wp_transition_set_data (transition, wp_event_ref (event),
(GDestroyNotify) wp_event_unref);
wp_transition_set_source_tag (transition, wp_async_event_hook_run);
wp_transition_advance (transition);
}
static gboolean
wp_async_event_hook_finish (WpEventHook * hook, GAsyncResult * res,
GError ** error)
{
g_return_val_if_fail (g_async_result_is_tagged (res, wp_async_event_hook_run),
FALSE);
return wp_transition_finish (res, error);
}
static void
wp_async_event_hook_class_init (WpAsyncEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_async_event_hook_finalize;
object_class->set_property = wp_async_event_hook_set_property;
hook_class->run = wp_async_event_hook_run;
hook_class->finish = wp_async_event_hook_finish;
g_object_class_install_property (object_class, PROP_ASYNC_GET_NEXT_STEP,
g_param_spec_boxed ("get-next-step", "get-next-step",
"The get-next-step closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ASYNC_EXECUTE_STEP,
g_param_spec_boxed ("execute-step", "execute-step",
"The execute-step closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Constructs a new async event hook
*
* \param name the name 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 execute_step the closure to invoke to execute the step
* \return a new async event hook
*/
WpEventHook *
wp_async_event_hook_new (const gchar *name,
const gchar * before[], const gchar * after[],
GClosure * get_next_step, GClosure * execute_step)
{
g_return_val_if_fail (get_next_step != NULL, NULL);
g_return_val_if_fail (execute_step != NULL, NULL);
return g_object_new (WP_TYPE_ASYNC_EVENT_HOOK,
"name", name,
"runs-before-hooks", before,
"runs-after-hooks", after,
"get-next-step", get_next_step,
"execute-step", execute_step,
NULL);
}
/* WpAsyncEventHookTransition - implementation */
struct _WpAsyncEventHookTransition
{
WpTransition parent;
};
G_DEFINE_TYPE (WpAsyncEventHookTransition,
wp_async_event_hook_transition,
WP_TYPE_TRANSITION)
static void
wp_async_event_hook_transition_init (
WpAsyncEventHookTransition * transition)
{
}
static guint
wp_async_event_hook_transition_get_next_step (
WpTransition * transition, guint step)
{
WpAsyncEventHook *hook =
WP_ASYNC_EVENT_HOOK (wp_transition_get_source_object (transition));
guint next_step = WP_TRANSITION_STEP_ERROR;
GValue ret = G_VALUE_INIT;
GValue values[2] = { G_VALUE_INIT, G_VALUE_INIT };
g_value_init (&ret, G_TYPE_UINT);
g_value_init (&values[0], G_TYPE_OBJECT);
g_value_init (&values[1], G_TYPE_UINT);
g_value_set_object (&values[0], transition);
g_value_set_uint (&values[1], step);
g_closure_invoke (hook->get_next_step, &ret, 2, values, NULL);
g_value_unset (&values[0]);
g_value_unset (&values[1]);
next_step = g_value_get_uint (&ret);
g_value_unset (&ret);
return next_step;
}
static void
wp_async_event_hook_transition_execute_step (
WpTransition * transition, guint step)
{
WpAsyncEventHook *hook =
WP_ASYNC_EVENT_HOOK (wp_transition_get_source_object (transition));
GValue values[2] = { G_VALUE_INIT, G_VALUE_INIT };
g_value_init (&values[0], G_TYPE_OBJECT);
g_value_init (&values[1], G_TYPE_UINT);
g_value_set_object (&values[0], transition);
g_value_set_uint (&values[1], step);
g_closure_invoke (hook->execute_step, NULL, 2, values, NULL);
g_value_unset (&values[0]);
g_value_unset (&values[1]);
}
static void
wp_async_event_hook_transition_class_init (
WpAsyncEventHookTransitionClass * klass)
{
WpTransitionClass *transition_class = (WpTransitionClass *) klass;
transition_class->get_next_step =
wp_async_event_hook_transition_get_next_step;
transition_class->execute_step =
wp_async_event_hook_transition_execute_step;
}