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 (¶ms, CLUTTER_TYPE_ALPHA);
442 g_value_set_object (¶ms, alpha);
443
444 g_closure_invoke (priv->closure, &result_value, 1, ¶ms, NULL);
445
446 retval = g_value_get_double (&result_value);
447
448 g_value_unset (&result_value);
449 g_value_unset (¶ms);
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