1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *             Jorn Baayen  <jorn@openedhand.com>
8  *             Emmanuele Bassi  <ebassi@openedhand.com>
9  *             Tomas Frydrych <tf@openedhand.com>
10  *
11  * Copyright (C) 2006, 2007, 2008 OpenedHand
12  * Copyright (C) 2009, 2010 Intel Corp.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 /**
29  * SECTION:clutter-alpha
30  * @short_description: A class for calculating a value as a function of time
31  *
32  * #ClutterAlpha is a class for calculating an floating point value
33  * dependent only on the position of a #ClutterTimeline.
34  *
35  * For newly written code, it is recommended to use the
36  * #ClutterTimeline:progress-mode property of #ClutterTimeline, or the
37  * clutter_timeline_set_progress_func() function instead of #ClutterAlpha.
38  * The #ClutterAlpha class will be deprecated in the future, and will not
39  * be available any more in the next major version of Clutter.
40  *
41  * A #ClutterAlpha binds a #ClutterTimeline to a progress function which
42  * translates the time T into an adimensional factor alpha. The factor can
43  * then be used to drive a #ClutterBehaviour, which will translate the
44  * alpha value into something meaningful for a #ClutterActor.
45  *
46  * You should provide a #ClutterTimeline and bind it to the #ClutterAlpha
47  * instance using clutter_alpha_set_timeline(). You should also set an
48  * "animation mode", either by using the #ClutterAnimationMode values that
49  * Clutter itself provides or by registering custom functions using
50  * clutter_alpha_register_func().
51  *
52  * Instead of a #ClutterAnimationMode you may provide a function returning
53  * the alpha value depending on the progress of the timeline, using
54  * clutter_alpha_set_func() or clutter_alpha_set_closure(). The alpha
55  * function will be executed each time a new frame in the #ClutterTimeline
56  * is reached.
57  *
58  * Since the alpha function is controlled by the timeline instance, you can
59  * pause, stop or resume the #ClutterAlpha from calling the alpha function by
60  * using the appropriate functions of the #ClutterTimeline object.
61  *
62  * #ClutterAlpha is used to "drive" a #ClutterBehaviour instance, and it
63  * is internally used by the #ClutterAnimation API.
64  *
65  * #ClutterAlpha is available since Clutter 0.2.
66  *
67  * #ClutterAlpha is deprecated since Clutter 1.12. #ClutterTimeline and
68  * the #ClutterTimeline:progress-mode property replace this whole class.
69  *
70  * ## ClutterAlpha custom properties for #ClutterScript
71  *
72  * #ClutterAlpha defines a custom `function` property for
73  * #ClutterScript which allows to reference a custom alpha function
74  * available in the source code. Setting the `function` property
75  * is equivalent to calling clutter_alpha_set_func() with the
76  * specified function name. No user data or #GDestroyNotify is
77  * available to be passed.
78  *
79  * The following JSON fragment defines a #ClutterAlpha
80  * using a #ClutterTimeline with id "sine-timeline" and an alpha
81  * function called `my_sine_alpha`. The defined #ClutterAlpha
82  * instance can be reused in multiple #ClutterBehaviour
83  * definitions or for #ClutterAnimation definitions.
84  *
85  * |[
86  * {
87  *   "id" : "sine-alpha",
88  *   "timeline" : {
89  *     "id" : "sine-timeline",
90  *     "duration" : 500,
91  *     "loop" : true
92  *   },
93  *   "function" : "my_sine_alpha"
94  * }
95  * ]|
96  */
97 
98 #ifdef HAVE_CONFIG_H
99 #include "config.h"
100 #endif
101 
102 #include <math.h>
103 
104 #include <gmodule.h>
105 
106 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
107 
108 #include "clutter-alpha.h"
109 #include "clutter-debug.h"
110 #include "clutter-enum-types.h"
111 #include "clutter-easing.h"
112 #include "clutter-main.h"
113 #include "clutter-marshal.h"
114 #include "clutter-private.h"
115 #include "clutter-scriptable.h"
116 #include "clutter-script-private.h"
117 
118 struct _ClutterAlphaPrivate
119 {
120   ClutterTimeline *timeline;
121   guint timeline_new_frame_id;
122 
123   gdouble alpha;
124 
125   GClosure *closure;
126 
127   ClutterAlphaFunc func;
128   gpointer user_data;
129   GDestroyNotify notify;
130 
131   gulong mode;
132 };
133 
134 enum
135 {
136   PROP_0,
137 
138   PROP_TIMELINE,
139   PROP_ALPHA,
140   PROP_MODE,
141 
142   PROP_LAST
143 };
144 
145 static GParamSpec *obj_props[PROP_LAST];
146 
147 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
148 
149 G_DEFINE_TYPE_WITH_CODE (ClutterAlpha,
150                          clutter_alpha,
151                          G_TYPE_INITIALLY_UNOWNED,
152                          G_ADD_PRIVATE (ClutterAlpha)
153                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
154                                                 clutter_scriptable_iface_init));
155 
156 static void
timeline_new_frame_cb(ClutterTimeline * timeline,guint msecs,ClutterAlpha * alpha)157 timeline_new_frame_cb (ClutterTimeline *timeline,
158                        guint            msecs,
159                        ClutterAlpha    *alpha)
160 {
161   ClutterAlphaPrivate *priv = alpha->priv;
162 
163   /* Update alpha value and notify */
164   priv->alpha = clutter_alpha_get_alpha (alpha);
165   g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_ALPHA]);
166 }
167 
168 static void
clutter_alpha_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)169 clutter_alpha_set_property (GObject      *object,
170 			    guint         prop_id,
171 			    const GValue *value,
172 			    GParamSpec   *pspec)
173 {
174   ClutterAlpha *alpha = CLUTTER_ALPHA (object);
175 
176   switch (prop_id)
177     {
178     case PROP_TIMELINE:
179       clutter_alpha_set_timeline (alpha, g_value_get_object (value));
180       break;
181 
182     case PROP_MODE:
183       clutter_alpha_set_mode (alpha, g_value_get_ulong (value));
184       break;
185 
186     default:
187       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188       break;
189   }
190 }
191 
192 static void
clutter_alpha_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)193 clutter_alpha_get_property (GObject    *object,
194 			    guint       prop_id,
195 			    GValue     *value,
196 			    GParamSpec *pspec)
197 {
198   ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv;
199 
200   switch (prop_id)
201     {
202     case PROP_TIMELINE:
203       g_value_set_object (value, priv->timeline);
204       break;
205 
206     case PROP_ALPHA:
207       g_value_set_double (value, priv->alpha);
208       break;
209 
210     case PROP_MODE:
211       g_value_set_ulong (value, priv->mode);
212       break;
213 
214     default:
215       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216       break;
217     }
218 }
219 
220 static void
clutter_alpha_finalize(GObject * object)221 clutter_alpha_finalize (GObject *object)
222 {
223   ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv;
224 
225   if (priv->notify != NULL)
226     priv->notify (priv->user_data);
227   else if (priv->closure != NULL)
228     g_closure_unref (priv->closure);
229 
230   G_OBJECT_CLASS (clutter_alpha_parent_class)->finalize (object);
231 }
232 
233 static void
clutter_alpha_dispose(GObject * object)234 clutter_alpha_dispose (GObject *object)
235 {
236   ClutterAlpha *self = CLUTTER_ALPHA(object);
237 
238   clutter_alpha_set_timeline (self, NULL);
239 
240   G_OBJECT_CLASS (clutter_alpha_parent_class)->dispose (object);
241 }
242 
243 static ClutterAlphaFunc
resolve_alpha_func(const gchar * name)244 resolve_alpha_func (const gchar *name)
245 {
246   static GModule *module = NULL;
247   ClutterAlphaFunc func;
248 
249   CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name);
250 
251   if (G_UNLIKELY (module == NULL))
252     module = g_module_open (NULL, 0);
253 
254   if (g_module_symbol (module, name, (gpointer) &func))
255     {
256       CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table",
257                     name);
258       return func;
259     }
260 
261   return NULL;
262 }
263 
264 static void
clutter_alpha_set_custom_property(ClutterScriptable * scriptable,ClutterScript * script,const gchar * name,const GValue * value)265 clutter_alpha_set_custom_property (ClutterScriptable *scriptable,
266                                    ClutterScript     *script,
267                                    const gchar       *name,
268                                    const GValue      *value)
269 {
270   if (strncmp (name, "function", 8) == 0)
271     {
272       g_assert (G_VALUE_HOLDS (value, G_TYPE_POINTER));
273       if (g_value_get_pointer (value) != NULL)
274         {
275           clutter_alpha_set_func (CLUTTER_ALPHA (scriptable),
276                                   g_value_get_pointer (value),
277                                   NULL, NULL);
278         }
279     }
280   else
281     g_object_set_property (G_OBJECT (scriptable), name, value);
282 }
283 
284 static gboolean
clutter_alpha_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)285 clutter_alpha_parse_custom_node (ClutterScriptable *scriptable,
286                                  ClutterScript     *script,
287                                  GValue            *value,
288                                  const gchar       *name,
289                                  JsonNode          *node)
290 {
291   if (strncmp (name, "function", 8) == 0)
292     {
293       const gchar *func_name = json_node_get_string (node);
294 
295       g_value_init (value, G_TYPE_POINTER);
296       g_value_set_pointer (value, resolve_alpha_func (func_name));
297 
298       return TRUE;
299     }
300 
301   /* we need to do this because we use gulong in place
302    * of ClutterAnimationMode for ClutterAlpha:mode
303    */
304   if (strncmp (name, "mode", 4) == 0)
305     {
306       gulong mode;
307 
308       mode = _clutter_script_resolve_animation_mode (node);
309 
310       g_value_init (value, G_TYPE_ULONG);
311       g_value_set_ulong (value, mode);
312 
313       return TRUE;
314     }
315 
316   return FALSE;
317 }
318 
319 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)320 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
321 {
322   iface->parse_custom_node = clutter_alpha_parse_custom_node;
323   iface->set_custom_property = clutter_alpha_set_custom_property;
324 }
325 
326 static void
clutter_alpha_class_init(ClutterAlphaClass * klass)327 clutter_alpha_class_init (ClutterAlphaClass *klass)
328 {
329   GObjectClass *object_class = G_OBJECT_CLASS (klass);
330 
331   object_class->set_property = clutter_alpha_set_property;
332   object_class->get_property = clutter_alpha_get_property;
333   object_class->finalize     = clutter_alpha_finalize;
334   object_class->dispose      = clutter_alpha_dispose;
335 
336   /**
337    * ClutterAlpha:timeline:
338    *
339    * A #ClutterTimeline instance used to drive the alpha function.
340    *
341    * Since: 0.2
342    *
343    * Deprecated: 1.12
344    */
345   obj_props[PROP_TIMELINE] =
346     g_param_spec_object ("timeline",
347                                P_("Timeline"),
348                                P_("Timeline used by the alpha"),
349                                CLUTTER_TYPE_TIMELINE,
350                                CLUTTER_PARAM_READWRITE);
351 
352   /**
353    * ClutterAlpha:alpha:
354    *
355    * The alpha value as computed by the alpha function. The linear
356    * interval is 0.0 to 1.0, but the Alpha allows overshooting by
357    * one unit in each direction, so the valid interval is -1.0 to 2.0.
358    *
359    * Since: 0.2
360    * Deprecated: 1.12: Use #ClutterTimeline::new-frame and
361    *   clutter_timeline_get_progress() instead
362    */
363   obj_props[PROP_ALPHA] =
364     g_param_spec_double ("alpha",
365                                P_("Alpha value"),
366                                P_("Alpha value as computed by the alpha"),
367                                -1.0, 2.0,
368                                0.0,
369                                CLUTTER_PARAM_READABLE);
370 
371   /**
372    * ClutterAlpha:mode:
373    *
374    * The progress function logical id - either a value from the
375    * #ClutterAnimationMode enumeration or a value returned by
376    * clutter_alpha_register_func().
377    *
378    * If %CLUTTER_CUSTOM_MODE is used then the function set using
379    * clutter_alpha_set_closure() or clutter_alpha_set_func()
380    * will be used.
381    *
382    * Since: 1.0
383    * Deprecated: 1.12: Use #ClutterTimeline:progress-mode
384    */
385   obj_props[PROP_MODE] =
386     g_param_spec_ulong ("mode",
387                               P_("Mode"),
388                               P_("Progress mode"),
389                               0, G_MAXULONG,
390                               CLUTTER_CUSTOM_MODE,
391                               G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
392 
393   g_object_class_install_properties (object_class,
394                                      PROP_LAST,
395                                      obj_props);
396 }
397 
398 static void
clutter_alpha_init(ClutterAlpha * self)399 clutter_alpha_init (ClutterAlpha *self)
400 {
401   self->priv = clutter_alpha_get_instance_private (self);
402   self->priv->mode = CLUTTER_CUSTOM_MODE;
403   self->priv->alpha = 0.0;
404 }
405 
406 /**
407  * clutter_alpha_get_alpha:
408  * @alpha: A #ClutterAlpha
409  *
410  * Query the current alpha value.
411  *
412  * Return Value: The current alpha value for the alpha
413  *
414  * Since: 0.2
415  *
416  * Deprecated: 1.12: Use clutter_timeline_get_progress()
417  */
418 gdouble
clutter_alpha_get_alpha(ClutterAlpha * alpha)419 clutter_alpha_get_alpha (ClutterAlpha *alpha)
420 {
421   ClutterAlphaPrivate *priv;
422   gdouble retval = 0;
423 
424   g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0);
425 
426   priv = alpha->priv;
427 
428   if (G_LIKELY (priv->func))
429     {
430       return priv->func (alpha, priv->user_data);
431     }
432   else if (priv->closure)
433     {
434       GValue params = G_VALUE_INIT;
435       GValue result_value = G_VALUE_INIT;
436 
437       g_object_ref (alpha);
438 
439       g_value_init (&result_value, G_TYPE_DOUBLE);
440 
441       g_value_init (&params, CLUTTER_TYPE_ALPHA);
442       g_value_set_object (&params, alpha);
443 
444       g_closure_invoke (priv->closure, &result_value, 1, &params, NULL);
445 
446       retval = g_value_get_double (&result_value);
447 
448       g_value_unset (&result_value);
449       g_value_unset (&params);
450 
451       g_object_unref (alpha);
452     }
453 
454   return retval;
455 }
456 
457 /*
458  * clutter_alpha_set_closure_internal:
459  * @alpha: a #ClutterAlpha
460  * @closure: a #GClosure
461  *
462  * Sets the @closure for @alpha. This function does not
463  * set the #ClutterAlpha:mode property and does not emit
464  * the #GObject::notify signal for it.
465  */
466 static inline void
clutter_alpha_set_closure_internal(ClutterAlpha * alpha,GClosure * closure)467 clutter_alpha_set_closure_internal (ClutterAlpha *alpha,
468                                     GClosure     *closure)
469 {
470   ClutterAlphaPrivate *priv = alpha->priv;
471 
472   if (priv->notify != NULL)
473     priv->notify (priv->user_data);
474   else if (priv->closure != NULL)
475     g_closure_unref (priv->closure);
476 
477   priv->func = NULL;
478   priv->user_data = NULL;
479   priv->notify = NULL;
480 
481   if (closure == NULL)
482     return;
483 
484   /* need to take ownership of the closure before sinking it */
485   priv->closure = g_closure_ref (closure);
486   g_closure_sink (closure);
487 
488   /* set the marshaller */
489   if (G_CLOSURE_NEEDS_MARSHAL (closure))
490     {
491       GClosureMarshal marshal = _clutter_marshal_DOUBLE__VOID;
492 
493       g_closure_set_marshal (priv->closure, marshal);
494     }
495 }
496 
497 /**
498  * clutter_alpha_set_closure:
499  * @alpha: A #ClutterAlpha
500  * @closure: A #GClosure
501  *
502  * Sets the #GClosure used to compute the alpha value at each
503  * frame of the #ClutterTimeline bound to @alpha.
504  *
505  * Since: 0.8
506  *
507  * Deprecated: 1.12: Use clutter_timeline_set_progress_func()
508  */
509 void
clutter_alpha_set_closure(ClutterAlpha * alpha,GClosure * closure)510 clutter_alpha_set_closure (ClutterAlpha *alpha,
511                            GClosure     *closure)
512 {
513   ClutterAlphaPrivate *priv;
514 
515   g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
516   g_return_if_fail (closure != NULL);
517 
518   priv = alpha->priv;
519 
520   clutter_alpha_set_closure_internal (alpha, closure);
521 
522   priv->mode = CLUTTER_CUSTOM_MODE;
523   g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]);
524 }
525 
526 /**
527  * clutter_alpha_set_func:
528  * @alpha: A #ClutterAlpha
529  * @func: A #ClutterAlphaFunc
530  * @data: user data to be passed to the alpha function, or %NULL
531  * @destroy: notify function used when disposing the alpha function
532  *
533  * Sets the #ClutterAlphaFunc function used to compute
534  * the alpha value at each frame of the #ClutterTimeline
535  * bound to @alpha.
536  *
537  * This function will not register @func as a global alpha function.
538  *
539  * Since: 0.2
540  *
541  * Deprecated: 1.12: Use clutter_timeline_set_progress_func()
542  */
543 void
clutter_alpha_set_func(ClutterAlpha * alpha,ClutterAlphaFunc func,gpointer data,GDestroyNotify destroy)544 clutter_alpha_set_func (ClutterAlpha    *alpha,
545 		        ClutterAlphaFunc func,
546                         gpointer         data,
547                         GDestroyNotify   destroy)
548 {
549   ClutterAlphaPrivate *priv;
550 
551   g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
552   g_return_if_fail (func != NULL);
553 
554   priv = alpha->priv;
555 
556   if (priv->notify != NULL)
557     {
558       priv->notify (priv->user_data);
559     }
560   else if (priv->closure != NULL)
561     {
562       g_closure_unref (priv->closure);
563       priv->closure = NULL;
564     }
565 
566   priv->func = func;
567   priv->user_data = data;
568   priv->notify = destroy;
569 
570   priv->mode = CLUTTER_CUSTOM_MODE;
571 
572   g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]);
573 }
574 
575 /**
576  * clutter_alpha_set_timeline:
577  * @alpha: A #ClutterAlpha
578  * @timeline: A #ClutterTimeline
579  *
580  * Binds @alpha to @timeline.
581  *
582  * Since: 0.2
583  *
584  * Deprecated: 1.12: Use #ClutterTimeline directly
585  */
586 void
clutter_alpha_set_timeline(ClutterAlpha * alpha,ClutterTimeline * timeline)587 clutter_alpha_set_timeline (ClutterAlpha    *alpha,
588                             ClutterTimeline *timeline)
589 {
590   ClutterAlphaPrivate *priv;
591 
592   g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
593   g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
594 
595   priv = alpha->priv;
596 
597   if (priv->timeline == timeline)
598     return;
599 
600   if (priv->timeline)
601     {
602       g_signal_handlers_disconnect_by_func (priv->timeline,
603                                             timeline_new_frame_cb,
604                                             alpha);
605 
606       g_object_unref (priv->timeline);
607       priv->timeline = NULL;
608     }
609 
610   if (timeline)
611     {
612       priv->timeline = g_object_ref (timeline);
613 
614       g_signal_connect (priv->timeline, "new-frame",
615                         G_CALLBACK (timeline_new_frame_cb),
616                         alpha);
617     }
618 
619   g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_TIMELINE]);
620 }
621 
622 /**
623  * clutter_alpha_get_timeline:
624  * @alpha: A #ClutterAlpha
625  *
626  * Gets the #ClutterTimeline bound to @alpha.
627  *
628  * Return value: (transfer none): a #ClutterTimeline instance
629  *
630  * Since: 0.2
631  *
632  * Deprecated: 1.12: Use #ClutterTimeline directlry
633  */
634 ClutterTimeline *
clutter_alpha_get_timeline(ClutterAlpha * alpha)635 clutter_alpha_get_timeline (ClutterAlpha *alpha)
636 {
637   g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
638 
639   return alpha->priv->timeline;
640 }
641 
642 /**
643  * clutter_alpha_new:
644  *
645  * Creates a new #ClutterAlpha instance.  You must set a function
646  * to compute the alpha value using clutter_alpha_set_func() and
647  * bind a #ClutterTimeline object to the #ClutterAlpha instance
648  * using clutter_alpha_set_timeline().
649  *
650  * You should use the newly created #ClutterAlpha instance inside
651  * a #ClutterBehaviour object.
652  *
653  * Return value: the newly created empty #ClutterAlpha instance.
654  *
655  * Since: 0.2
656  *
657  * Deprecated: 1.12: Use #ClutterTimeline instead
658  */
659 ClutterAlpha *
clutter_alpha_new(void)660 clutter_alpha_new (void)
661 {
662   return g_object_new (CLUTTER_TYPE_ALPHA, NULL);
663 }
664 
665 /**
666  * clutter_alpha_new_full:
667  * @timeline: #ClutterTimeline timeline
668  * @mode: animation mode
669  *
670  * Creates a new #ClutterAlpha instance and sets the timeline
671  * and animation mode.
672  *
673  * See also clutter_alpha_set_timeline() and clutter_alpha_set_mode().
674  *
675  * Return Value: the newly created #ClutterAlpha
676  *
677  * Since: 1.0
678  *
679  * Deprecated: 1.12: Use #ClutterTimeline instead
680  */
681 ClutterAlpha *
clutter_alpha_new_full(ClutterTimeline * timeline,gulong mode)682 clutter_alpha_new_full (ClutterTimeline *timeline,
683                         gulong           mode)
684 {
685   g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
686   g_return_val_if_fail (mode != CLUTTER_ANIMATION_LAST, NULL);
687 
688   return g_object_new (CLUTTER_TYPE_ALPHA,
689                        "timeline", timeline,
690                        "mode", mode,
691                        NULL);
692 }
693 
694 /**
695  * clutter_alpha_new_with_func:
696  * @timeline: a #ClutterTimeline
697  * @func: a #ClutterAlphaFunc
698  * @data: data to pass to the function, or %NULL
699  * @destroy: function to call when removing the alpha function, or %NULL
700  *
701  * Creates a new #ClutterAlpha instances and sets the timeline
702  * and the alpha function.
703  *
704  * This function will not register @func as a global alpha function.
705  *
706  * See also clutter_alpha_set_timeline() and clutter_alpha_set_func().
707  *
708  * Return value: the newly created #ClutterAlpha
709  *
710  * Since: 1.0
711  *
712  * Deprecated: 1.12: Use #ClutterTimeline instead
713  */
714 ClutterAlpha *
clutter_alpha_new_with_func(ClutterTimeline * timeline,ClutterAlphaFunc func,gpointer data,GDestroyNotify destroy)715 clutter_alpha_new_with_func (ClutterTimeline  *timeline,
716                              ClutterAlphaFunc  func,
717                              gpointer          data,
718                              GDestroyNotify    destroy)
719 {
720   ClutterAlpha *retval;
721 
722   g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
723   g_return_val_if_fail (func != NULL, NULL);
724 
725   retval = clutter_alpha_new ();
726   clutter_alpha_set_timeline (retval, timeline);
727   clutter_alpha_set_func (retval, func, data, destroy);
728 
729   return retval;
730 }
731 
732 /**
733  * clutter_alpha_get_mode:
734  * @alpha: a #ClutterAlpha
735  *
736  * Retrieves the #ClutterAnimationMode used by @alpha.
737  *
738  * Return value: the animation mode
739  *
740  * Since: 1.0
741  *
742  * Deprecated: 1.12: Use #ClutterTimeline instead
743  */
744 gulong
clutter_alpha_get_mode(ClutterAlpha * alpha)745 clutter_alpha_get_mode (ClutterAlpha *alpha)
746 {
747   g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), CLUTTER_CUSTOM_MODE);
748 
749   return alpha->priv->mode;
750 }
751 
752 typedef struct _AlphaData {
753   guint closure_set : 1;
754 
755   ClutterAlphaFunc func;
756   gpointer data;
757 
758   GClosure *closure;
759 } AlphaData;
760 
761 static GPtrArray *clutter_alphas = NULL;
762 
763 static gdouble
clutter_alpha_easing_func(ClutterAlpha * alpha,gpointer data G_GNUC_UNUSED)764 clutter_alpha_easing_func (ClutterAlpha *alpha,
765                            gpointer      data G_GNUC_UNUSED)
766 {
767   ClutterAlphaPrivate *priv = alpha->priv;
768   ClutterTimeline *timeline = priv->timeline;
769   gdouble t, d;
770 
771   if (G_UNLIKELY (priv->timeline == NULL))
772     return 0.0;
773 
774   t = clutter_timeline_get_elapsed_time (timeline);
775   d = clutter_timeline_get_duration (timeline);
776 
777   return clutter_easing_for_mode (priv->mode, t, d);
778 }
779 
780 /**
781  * clutter_alpha_set_mode:
782  * @alpha: a #ClutterAlpha
783  * @mode: a #ClutterAnimationMode
784  *
785  * Sets the progress function of @alpha using the symbolic value
786  * of @mode, as taken by the #ClutterAnimationMode enumeration or
787  * using the value returned by clutter_alpha_register_func().
788  *
789  * Since: 1.0
790  *
791  * Deprecated: 1.12: Use #ClutterTimeline and
792  *   clutter_timeline_set_progress_mode() instead
793  */
794 void
clutter_alpha_set_mode(ClutterAlpha * alpha,gulong mode)795 clutter_alpha_set_mode (ClutterAlpha *alpha,
796                         gulong        mode)
797 {
798   ClutterAlphaPrivate *priv;
799 
800   g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
801   g_return_if_fail (mode != CLUTTER_ANIMATION_LAST);
802 
803   priv = alpha->priv;
804 
805   if (mode == CLUTTER_CUSTOM_MODE)
806     {
807       priv->mode = mode;
808     }
809   else if (mode < CLUTTER_ANIMATION_LAST)
810     {
811       if (priv->mode == mode)
812         return;
813 
814       /* sanity check to avoid getting an out of sync
815        * enum/function mapping
816        */
817       g_assert (clutter_get_easing_func_for_mode (mode) != NULL);
818 
819       clutter_alpha_set_closure_internal (alpha, NULL);
820 
821       priv->mode = mode;
822 
823       CLUTTER_NOTE (ANIMATION, "New easing mode '%s'[%lu]\n",
824                     clutter_get_easing_name_for_mode (priv->mode),
825                     priv->mode);
826 
827       priv->func = clutter_alpha_easing_func;
828       priv->user_data = NULL;
829       priv->notify = NULL;
830     }
831   else if (mode > CLUTTER_ANIMATION_LAST)
832     {
833       AlphaData *alpha_data = NULL;
834       gulong real_index = 0;
835 
836       if (priv->mode == mode)
837         return;
838 
839       if (G_UNLIKELY (clutter_alphas == NULL))
840         {
841           g_warning ("No alpha functions defined for ClutterAlpha to use. "
842                      "Use clutter_alpha_register_func() to register an "
843                      "alpha function.");
844           return;
845         }
846 
847       real_index = mode - CLUTTER_ANIMATION_LAST - 1;
848 
849       alpha_data = g_ptr_array_index (clutter_alphas, real_index);
850       if (G_UNLIKELY (alpha_data == NULL))
851         {
852           g_warning ("No alpha function registered for mode %lu.",
853                      mode);
854           return;
855         }
856 
857       if (alpha_data->closure_set)
858         clutter_alpha_set_closure (alpha, alpha_data->closure);
859       else
860         {
861           clutter_alpha_set_closure_internal (alpha, NULL);
862 
863           priv->func = alpha_data->func;
864           priv->user_data = alpha_data->data;
865           priv->notify = NULL;
866         }
867 
868       priv->mode = mode;
869     }
870   else
871     g_assert_not_reached ();
872 
873   g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]);
874 }
875 
876 static gulong
register_alpha_internal(AlphaData * alpha_data)877 register_alpha_internal (AlphaData *alpha_data)
878 {
879   if (G_UNLIKELY (clutter_alphas == NULL))
880     clutter_alphas = g_ptr_array_new ();
881 
882   g_ptr_array_add (clutter_alphas, alpha_data);
883 
884   return clutter_alphas->len + CLUTTER_ANIMATION_LAST;
885 }
886 
887 /**
888  * clutter_alpha_register_func: (skip)
889  * @func: a #ClutterAlphaFunc
890  * @data: user data to pass to @func, or %NULL
891  *
892  * Registers a global alpha function and returns its logical id
893  * to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
894  *
895  * The logical id is always greater than %CLUTTER_ANIMATION_LAST.
896  *
897  * Return value: the logical id of the alpha function
898  *
899  * Since: 1.0
900  *
901  * Deprecated: 1.12: There is no direct replacement for this
902  *   function. Use clutter_timeline_set_progress_func() on each
903  *   specific #ClutterTimeline instance
904  */
905 gulong
clutter_alpha_register_func(ClutterAlphaFunc func,gpointer data)906 clutter_alpha_register_func (ClutterAlphaFunc func,
907                              gpointer         data)
908 {
909   AlphaData *alpha_data;
910 
911   g_return_val_if_fail (func != NULL, 0);
912 
913   alpha_data = g_slice_new (AlphaData);
914   alpha_data->closure_set = FALSE;
915   alpha_data->func = func;
916   alpha_data->data = data;
917 
918   return register_alpha_internal (alpha_data);
919 }
920 
921 /**
922  * clutter_alpha_register_closure: (rename-to clutter_alpha_register_func)
923  * @closure: a #GClosure
924  *
925  * #GClosure variant of clutter_alpha_register_func().
926  *
927  * Registers a global alpha function and returns its logical id
928  * to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
929  *
930  * The logical id is always greater than %CLUTTER_ANIMATION_LAST.
931  *
932  * Return value: the logical id of the alpha function
933  *
934  * Since: 1.0
935  *
936  * Deprecated: 1.12: There is no direct replacement for this
937  *   function. Use clutter_timeline_set_progress_func() on each
938  *   specific #ClutterTimeline instance
939  */
940 gulong
clutter_alpha_register_closure(GClosure * closure)941 clutter_alpha_register_closure (GClosure *closure)
942 {
943   AlphaData *alpha_data;
944 
945   g_return_val_if_fail (closure != NULL, 0);
946 
947   alpha_data = g_slice_new (AlphaData);
948   alpha_data->closure_set = TRUE;
949   alpha_data->closure = closure;
950 
951   return register_alpha_internal (alpha_data);
952 }
953