From beb91d76763b3b3aede1be1da8bc3c1f8184ed8a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 15 Mar 2012 11:02:30 +0000 Subject: [PATCH] interval: Add variadic arguments for initial/final setters As a convenience for the C API. Language bindings should already be using the GValue variants. This commit also moves the custom progress functions out of the clutter-interval.c, as they are meant to be generic interpolation functions and not ClutterInterval-specific. --- clutter/clutter-interval.c | 277 ++++++++++++++++++------------------- clutter/clutter-interval.h | 33 +---- clutter/clutter-private.h | 7 + clutter/clutter-types.h | 28 ++++ clutter/clutter-util.c | 148 ++++++++++++++++++++ 5 files changed, 322 insertions(+), 171 deletions(-) diff --git a/clutter/clutter-interval.c b/clutter/clutter-interval.c index c15123168..d0ccec0db 100644 --- a/clutter/clutter-interval.c +++ b/clutter/clutter-interval.c @@ -63,14 +63,6 @@ #include "clutter-private.h" #include "clutter-units.h" -typedef struct -{ - GType value_type; - ClutterProgressFunc func; -} ProgressData; - -static GHashTable *progress_funcs = NULL; - enum { PROP_0, @@ -195,23 +187,15 @@ clutter_interval_real_compute_value (ClutterInterval *interval, value_type = clutter_interval_get_value_type (interval); - if (G_UNLIKELY (progress_funcs != NULL)) + if (_clutter_has_progress_function (value_type)) { - ProgressData *p_data; - - p_data = - g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type)); - - /* if we have a progress function, and that function was - * successful in computing the progress, then we bail out - * as fast as we can - */ - if (p_data != NULL) - { - retval = p_data->func (initial, final, factor, value); - if (retval) - return retval; - } + retval = _clutter_run_progress_function (value_type, + initial, + final, + factor, + value); + if (retval) + return TRUE; } switch (G_TYPE_FUNDAMENTAL (value_type)) @@ -413,16 +397,44 @@ clutter_interval_init (ClutterInterval *self) priv->values = g_malloc0 (sizeof (GValue) * N_VALUES); } -static void -clutter_interval_set_interval_valist (ClutterInterval *interval, - va_list var_args) +static inline void +clutter_interval_set_value_internal (ClutterInterval *interval, + gint index_, + const GValue *value) +{ + ClutterIntervalPrivate *priv = interval->priv; + + g_assert (index_ >= INITIAL && index_ <= RESULT); + + if (G_IS_VALUE (&priv->values[index_])) + g_value_unset (&priv->values[index_]); + + g_value_init (&priv->values[index_], priv->value_type); + g_value_copy (value, &priv->values[index_]); +} + +static inline void +clutter_interval_get_value_internal (ClutterInterval *interval, + gint index_, + GValue *value) +{ + ClutterIntervalPrivate *priv = interval->priv; + + g_assert (index_ >= INITIAL && index_ <= RESULT); + + g_value_copy (&priv->values[index_], value); +} + +static gboolean +clutter_interval_set_initial_internal (ClutterInterval *interval, + va_list *args) { GType gtype = interval->priv->value_type; GValue value = { 0, }; gchar *error; /* initial value */ - G_VALUE_COLLECT_INIT (&value, gtype, var_args, 0, &error); + G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error); if (error) { @@ -433,26 +445,42 @@ clutter_interval_set_interval_valist (ClutterInterval *interval, * undefined behaviour */ g_free (error); - return; + return FALSE; } - clutter_interval_set_initial_value (interval, &value); + clutter_interval_set_value_internal (interval, INITIAL, &value); g_value_unset (&value); - /* final value */ - G_VALUE_COLLECT_INIT (&value, gtype, var_args, 0, &error); + return TRUE; +} + +static gboolean +clutter_interval_set_final_internal (ClutterInterval *interval, + va_list *args) +{ + GType gtype = interval->priv->value_type; + GValue value = { 0, }; + gchar *error; + + /* initial value */ + G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); - /* see above */ + /* we leak the value here as it might not be in a valid state + * given the error and calling g_value_unset() might lead to + * undefined behaviour + */ g_free (error); - return; + return FALSE; } - clutter_interval_set_final_value (interval, &value); + clutter_interval_set_value_internal (interval, FINAL, &value); g_value_unset (&value); + + return TRUE; } static void @@ -524,7 +552,13 @@ clutter_interval_new (GType gtype, retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); va_start (args, gtype); - clutter_interval_set_interval_valist (retval, args); + + if (!clutter_interval_set_initial_internal (retval, &args)) + goto out; + + clutter_interval_set_final_internal (retval, &args); + +out: va_end (args); return retval; @@ -616,34 +650,6 @@ clutter_interval_get_value_type (ClutterInterval *interval) return interval->priv->value_type; } -static inline void -clutter_interval_set_value_internal (ClutterInterval *interval, - gint index_, - const GValue *value) -{ - ClutterIntervalPrivate *priv = interval->priv; - - g_assert (index_ >= INITIAL && index_ <= RESULT); - - if (G_IS_VALUE (&priv->values[index_])) - g_value_unset (&priv->values[index_]); - - g_value_init (&priv->values[index_], priv->value_type); - g_value_copy (value, &priv->values[index_]); -} - -static inline void -clutter_interval_get_value_internal (ClutterInterval *interval, - gint index_, - GValue *value) -{ - ClutterIntervalPrivate *priv = interval->priv; - - g_assert (index_ >= INITIAL && index_ <= RESULT); - - g_value_copy (&priv->values[index_], value); -} - /** * clutter_interval_set_initial_value: * @interval: a #ClutterInterval @@ -652,6 +658,8 @@ clutter_interval_get_value_internal (ClutterInterval *interval, * Sets the initial value of @interval to @value. The value is copied * inside the #ClutterInterval. * + * Rename to: clutter_interval_set_initial + * * Since: 1.0 */ void @@ -670,6 +678,33 @@ clutter_interval_set_initial_value (ClutterInterval *interval, clutter_interval_set_value_internal (interval, INITIAL, value); } +/** + * clutter_interval_set_initial: (skip) + * @interval: a #ClutterInterval + * @...: the initial value of the interval. + * + * Variadic arguments version of clutter_interval_set_initial_value(). + * + * This function is meant as a convenience for the C API. + * + * Language bindings should use clutter_interval_set_initial_value() + * instead. + * + * Since: 1.10 + */ +void +clutter_interval_set_initial (ClutterInterval *interval, + ...) +{ + va_list args; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + + va_start (args, interval); + clutter_interval_set_initial_internal (interval, &args); + va_end (args); +} + /** * clutter_interval_get_initial_value: * @interval: a #ClutterInterval @@ -721,6 +756,8 @@ clutter_interval_peek_initial_value (ClutterInterval *interval) * Sets the final value of @interval to @value. The value is * copied inside the #ClutterInterval. * + * Rename to: clutter_interval_set_final + * * Since: 1.0 */ void @@ -762,6 +799,32 @@ clutter_interval_get_final_value (ClutterInterval *interval, clutter_interval_get_value_internal (interval, FINAL, value); } +/** + * clutter_interval_set_final: (skip) + * @interval: a #ClutterInterval + * @...: the final value of the interval + * + * Variadic arguments version of clutter_interval_set_final_value(). + * + * This function is meant as a convenience for the C API. + * + * Language bindings should use clutter_interval_set_final_value() instead. + * + * Since: 1.10 + */ +void +clutter_interval_set_final (ClutterInterval *interval, + ...) +{ + va_list args; + + g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); + + va_start (args, interval); + clutter_interval_set_final_internal (interval, &args); + va_end (args); +} + /** * clutter_interval_peek_final_value: * @interval: a #ClutterInterval @@ -812,7 +875,13 @@ clutter_interval_set_interval (ClutterInterval *interval, g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID); va_start (args, interval); - clutter_interval_set_interval_valist (interval, args); + + if (!clutter_interval_set_initial_internal (interval, &args)) + goto out; + + clutter_interval_set_final_internal (interval, &args); + +out: va_end (args); } @@ -941,81 +1010,3 @@ clutter_interval_compute (ClutterInterval *interval, return NULL; } - -/** - * clutter_interval_register_progress_func: (skip) - * @value_type: a #GType - * @func: a #ClutterProgressFunc, or %NULL to unset a previously - * set progress function - * - * Sets the progress function for a given @value_type, like: - * - * |[ - * clutter_interval_register_progress_func (MY_TYPE_FOO, - * my_foo_progress); - * ]| - * - * Whenever a #ClutterInterval instance using the default - * #ClutterInterval::compute_value implementation is set as an - * interval between two #GValue of type @value_type, it will call - * @func to establish the value depending on the given progress, - * for instance: - * - * |[ - * static gboolean - * my_int_progress (const GValue *a, - * const GValue *b, - * gdouble progress, - * GValue *retval) - * { - * gint ia = g_value_get_int (a); - * gint ib = g_value_get_int (b); - * gint res = factor * (ib - ia) + ia; - * - * g_value_set_int (retval, res); - * - * return TRUE; - * } - * - * clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress); - * ]| - * - * To unset a previously set progress function of a #GType, pass %NULL - * for @func. - * - * Since: 1.0 - */ -void -clutter_interval_register_progress_func (GType value_type, - ClutterProgressFunc func) -{ - ProgressData *progress_func; - - g_return_if_fail (value_type != G_TYPE_INVALID); - - if (G_UNLIKELY (progress_funcs == NULL)) - progress_funcs = g_hash_table_new (NULL, NULL); - - progress_func = - g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type)); - if (G_UNLIKELY (progress_func)) - { - if (func == NULL) - { - g_hash_table_remove (progress_funcs, GUINT_TO_POINTER (value_type)); - g_slice_free (ProgressData, progress_func); - } - else - progress_func->func = func; - } - else - { - progress_func = g_slice_new (ProgressData); - progress_func->value_type = value_type; - progress_func->func = func; - - g_hash_table_replace (progress_funcs, - GUINT_TO_POINTER (value_type), - progress_func); - } -} diff --git a/clutter/clutter-interval.h b/clutter/clutter-interval.h index 44174f347..f845de9f3 100644 --- a/clutter/clutter-interval.h +++ b/clutter/clutter-interval.h @@ -43,34 +43,6 @@ G_BEGIN_DECLS typedef struct _ClutterIntervalPrivate ClutterIntervalPrivate; typedef struct _ClutterIntervalClass ClutterIntervalClass; -/** - * ClutterProgressFunc: - * @a: the initial value of an interval - * @b: the final value of an interval - * @progress: the progress factor, between 0 and 1 - * @retval: the value used to store the progress - * - * Prototype of the progress function used to compute the value - * between the two ends @a and @b of an interval depending on - * the value of @progress. - * - * The #GValue in @retval is already initialized with the same - * type as @a and @b. - * - * This function will be called by #ClutterInterval if the - * type of the values of the interval was registered using - * clutter_interval_register_progress_func(). - * - * Return value: %TRUE if the function successfully computed - * the value and stored it inside @retval - * - * Since: 1.0 - */ -typedef gboolean (* ClutterProgressFunc) (const GValue *a, - const GValue *b, - gdouble progress, - GValue *retval); - /** * ClutterInterval: * @@ -131,11 +103,16 @@ ClutterInterval *clutter_interval_new_with_values (GType gtype, ClutterInterval *clutter_interval_clone (ClutterInterval *interval); GType clutter_interval_get_value_type (ClutterInterval *interval); + +void clutter_interval_set_initial (ClutterInterval *interval, + ...); void clutter_interval_set_initial_value (ClutterInterval *interval, const GValue *value); void clutter_interval_get_initial_value (ClutterInterval *interval, GValue *value); GValue * clutter_interval_peek_initial_value (ClutterInterval *interval); +void clutter_interval_set_final (ClutterInterval *interval, + ...); void clutter_interval_set_final_value (ClutterInterval *interval, const GValue *value); void clutter_interval_get_final_value (ClutterInterval *interval, diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 916ee732e..bd099e970 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -265,6 +265,13 @@ typedef enum _ClutterCullResult CLUTTER_CULL_RESULT_PARTIAL } ClutterCullResult; +gboolean _clutter_has_progress_function (GType gtype); +gboolean _clutter_run_progress_function (GType gtype, + const GValue *initial, + const GValue *final, + gdouble progress, + GValue *retval); + G_END_DECLS #endif /* __CLUTTER_PRIVATE_H__ */ diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index d2b5fe267..fdc81fa37 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -287,6 +287,34 @@ ClutterMargin * clutter_margin_new (void) G_GNUC_MALLOC; ClutterMargin * clutter_margin_copy (const ClutterMargin *margin_); void clutter_margin_free (ClutterMargin *margin_); +/** + * ClutterProgressFunc: + * @a: the initial value of an interval + * @b: the final value of an interval + * @progress: the progress factor, between 0 and 1 + * @retval: the value used to store the progress + * + * Prototype of the progress function used to compute the value + * between the two ends @a and @b of an interval depending on + * the value of @progress. + * + * The #GValue in @retval is already initialized with the same + * type as @a and @b. + * + * This function will be called by #ClutterInterval if the + * type of the values of the interval was registered using + * clutter_interval_register_progress_func(). + * + * Return value: %TRUE if the function successfully computed + * the value and stored it inside @retval + * + * Since: 1.0 + */ +typedef gboolean (* ClutterProgressFunc) (const GValue *a, + const GValue *b, + gdouble progress, + GValue *retval); + G_END_DECLS #endif /* __CLUTTER_TYPES_H__ */ diff --git a/clutter/clutter-util.c b/clutter/clutter-util.c index eeecb6365..b95629c4d 100644 --- a/clutter/clutter-util.c +++ b/clutter/clutter-util.c @@ -37,6 +37,7 @@ #include #include "clutter-main.h" +#include "clutter-interval.h" #include "clutter-private.h" #include "deprecated/clutter-util.h" @@ -180,3 +181,150 @@ _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1, dest->x = dest_x; dest->y = dest_y; } + +typedef struct +{ + GType value_type; + ClutterProgressFunc func; +} ProgressData; + +G_LOCK_DEFINE_STATIC (progress_funcs); +static GHashTable *progress_funcs = NULL; + +gboolean +_clutter_has_progress_function (GType gtype) +{ + const char *type_name = g_type_name (gtype); + + if (progress_funcs == NULL) + return FALSE; + + return g_hash_table_lookup (progress_funcs, type_name) != NULL; +} + +gboolean +_clutter_run_progress_function (GType gtype, + const GValue *initial, + const GValue *final, + gdouble progress, + GValue *retval) +{ + ProgressData *pdata; + gboolean res; + + G_LOCK (progress_funcs); + + if (G_UNLIKELY (progress_funcs == NULL)) + { + res = FALSE; + goto out; + } + + pdata = g_hash_table_lookup (progress_funcs, g_type_name (gtype)); + if (G_UNLIKELY (pdata == NULL)) + { + res = FALSE; + goto out; + } + + res = pdata->func (initial, final, progress, retval); + +out: + G_UNLOCK (progress_funcs); + + return res; +} + +static void +progress_data_destroy (gpointer data_) +{ + g_slice_free (ProgressData, data_); +} + +/** + * clutter_interval_register_progress_func: (skip) + * @value_type: a #GType + * @func: a #ClutterProgressFunc, or %NULL to unset a previously + * set progress function + * + * Sets the progress function for a given @value_type, like: + * + * |[ + * clutter_interval_register_progress_func (MY_TYPE_FOO, + * my_foo_progress); + * ]| + * + * Whenever a #ClutterInterval instance using the default + * #ClutterInterval::compute_value implementation is set as an + * interval between two #GValue of type @value_type, it will call + * @func to establish the value depending on the given progress, + * for instance: + * + * |[ + * static gboolean + * my_int_progress (const GValue *a, + * const GValue *b, + * gdouble progress, + * GValue *retval) + * { + * gint ia = g_value_get_int (a); + * gint ib = g_value_get_int (b); + * gint res = factor * (ib - ia) + ia; + * + * g_value_set_int (retval, res); + * + * return TRUE; + * } + * + * clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress); + * ]| + * + * To unset a previously set progress function of a #GType, pass %NULL + * for @func. + * + * Since: 1.0 + */ +void +clutter_interval_register_progress_func (GType value_type, + ClutterProgressFunc func) +{ + ProgressData *progress_func; + const char *type_name; + + g_return_if_fail (value_type != G_TYPE_INVALID); + + type_name = g_type_name (value_type); + + G_LOCK (progress_funcs); + + if (G_UNLIKELY (progress_funcs == NULL)) + progress_funcs = g_hash_table_new_full (NULL, NULL, + NULL, + progress_data_destroy); + + progress_func = + g_hash_table_lookup (progress_funcs, type_name); + + if (G_UNLIKELY (progress_func)) + { + if (func == NULL) + { + g_hash_table_remove (progress_funcs, type_name); + g_slice_free (ProgressData, progress_func); + } + else + progress_func->func = func; + } + else + { + progress_func = g_slice_new (ProgressData); + progress_func->value_type = value_type; + progress_func->func = func; + + g_hash_table_replace (progress_funcs, + (gpointer) type_name, + progress_func); + } + + G_UNLOCK (progress_funcs); +}