svn update: 60286 (latest:60286)
[profile/ivi/ecore.git] / src / lib / ecore / ecore_anim.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <stdlib.h>
6 #include <math.h>
7
8 #include "Ecore.h"
9 #include "ecore_private.h"
10
11
12 struct _Ecore_Animator
13 {
14    EINA_INLIST;
15    ECORE_MAGIC;
16
17    Ecore_Task_Cb func;
18    void          *data;
19
20    double             start, run;
21    Ecore_Timeline_Cb  run_func;
22    void              *run_data;
23
24    Eina_Bool     delete_me : 1;
25    Eina_Bool     suspended : 1;
26 };
27
28
29 static Eina_Bool _ecore_animator_run(void *data);
30 static Eina_Bool _ecore_animator(void *data);
31
32 static int                    animators_delete_me = 0;
33 static Ecore_Animator        *animators = NULL;
34 static double                 animators_frametime = 1.0 / 30.0;
35
36 static Ecore_Animator_Source  src = ECORE_ANIMATOR_SOURCE_TIMER;
37 static Ecore_Timer           *timer = NULL;
38 static int                    ticking = 0;
39 static Ecore_Cb               begin_tick_cb = NULL;
40 static const void            *begin_tick_data = NULL;
41 static Ecore_Cb               end_tick_cb = NULL;
42 static const void            *end_tick_data = NULL;
43
44 static void
45 _begin_tick(void)
46 {
47    if (ticking) return;
48    ticking = 1;
49    switch (src)
50      {
51       case ECORE_ANIMATOR_SOURCE_TIMER:
52         if (!timer)
53           {
54              double t_loop = ecore_loop_time_get();
55              double sync_0 = 0.0;
56              double d = -fmod(t_loop - sync_0, animators_frametime);
57              
58              timer = ecore_timer_loop_add(animators_frametime, 
59                                           _ecore_animator, NULL);
60              ecore_timer_delay(timer, d);
61           }
62         break;
63       case ECORE_ANIMATOR_SOURCE_CUSTOM:
64         if (begin_tick_cb) begin_tick_cb((void *)begin_tick_data);
65         break;
66       default:
67         break;
68      }
69 }
70
71 static void
72 _end_tick(void)
73 {
74    if (!ticking) return;
75    ticking = 0;
76    switch (src)
77      {
78       case ECORE_ANIMATOR_SOURCE_TIMER:
79         if (timer)
80           {
81              ecore_timer_del(timer);
82              timer = NULL;
83           }
84         break;
85       case ECORE_ANIMATOR_SOURCE_CUSTOM:
86         if (end_tick_cb) end_tick_cb((void *)end_tick_data);
87         break;
88       default:
89         break;
90      }
91 }
92
93 static Eina_Bool
94 _do_tick(void)
95 {
96    Ecore_Animator *animator;
97
98    EINA_INLIST_FOREACH(animators, animator)
99      {
100         if (!animator->delete_me && !animator->suspended)
101           {
102              if (!animator->func(animator->data))
103                {
104                   animator->delete_me = EINA_TRUE;
105                   animators_delete_me++;
106                }
107           }
108      }
109    if (animators_delete_me)
110      {
111         Ecore_Animator *l;
112         for (l = animators; l;)
113           {
114              animator = l;
115              l = (Ecore_Animator *) EINA_INLIST_GET(l)->next;
116              if (animator->delete_me)
117                {
118                   animators = (Ecore_Animator *)
119                      eina_inlist_remove(EINA_INLIST_GET(animators), 
120                                         EINA_INLIST_GET(animator));
121                   ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE);
122                   free(animator);
123                   animators_delete_me--;
124                   if (animators_delete_me == 0) break;
125                }
126           }
127      }
128    if (!animators)
129      {
130         _end_tick();
131         return ECORE_CALLBACK_CANCEL;
132      }
133    return ECORE_CALLBACK_RENEW;
134 }
135
136 /**
137  * @addtogroup Ecore_Group Ecore - Main Loop and Job Functions.
138  *
139  * @{
140  */
141
142 /**
143  * @addtogroup Ecore_Animator_Group Ecore Animator functions
144  *
145  * @{
146  */
147
148 /**
149  * Add a animator to tick off at every animaton tick during main loop execution.
150  * @param func The function to call when it ticks off
151  * @param data The data to pass to the function
152  * @return A handle to the new animator
153  *
154  * This function adds a animator and returns its handle on success and NULL on
155  * failure. The function @p func will be called every N seconds where N is the
156  * frametime interval set by ecore_animator_frametime_set(). The function will
157  * be passed the @p data pointer as its parameter.
158  *
159  * When the animator @p func is called, it must return a value of either 1 or 0.
160  * If it returns 1 (or ECORE_CALLBACK_RENEW), it will be called again at the
161  * next tick, or if it returns 0 (or ECORE_CALLBACK_CANCEL) it will be deleted
162  * automatically making any references/handles for it invalid.
163  */
164 EAPI Ecore_Animator *
165 ecore_animator_add(Ecore_Task_Cb func, const void *data)
166 {
167    Ecore_Animator *animator;
168
169    if (!func) return NULL;
170    animator = calloc(1, sizeof(Ecore_Animator));
171    if (!animator) return NULL;
172    ECORE_MAGIC_SET(animator, ECORE_MAGIC_ANIMATOR);
173    animator->func = func;
174    animator->data = (void *)data;
175    animators = (Ecore_Animator *)eina_inlist_append(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
176    _begin_tick();
177    return animator;
178 }
179
180 /**
181  * Add a animator that runs for a limited time
182  * @param runtime The time to run in seconds
183  * @param func The function to call when it ticks off
184  * @param data The data to pass to the function
185  * @return A handle to the new animator
186  *
187  * This function is just like ecore_animator_add() except the animator only
188  * runs for a limited time specified in seconds by @p runtime. Once the runtime
189  * the animator has elapsed (animator finished) it will automatically be
190  * deleted. The callback function @p func can return ECORE_CALLBACK_RENEW to
191  * keep the animator running or ECORE_CALLBACK_CANCEL ro stop it and have
192  * it be deleted automatically at any time.
193  *
194  * The @p func will ALSO be passed a position parameter that will be in value
195  * from 0.0 to 1.0 to indicate where along the timeline (0.0 start, 1.0 end)
196  * the animator run is at. If the callback wishes not to have a linear
197  * transition it can "map" this value to one of several curves and mappings
198  * via ecore_animator_pos_map().
199  *
200  * @since 1.1.0
201  */
202 EAPI Ecore_Animator *
203 ecore_animator_timeline_add(double runtime, Ecore_Timeline_Cb func, const void *data)
204 {
205    Ecore_Animator *animator;
206
207    if (runtime <= 0.0) runtime = 0.0;
208    animator = ecore_animator_add(_ecore_animator_run, NULL);
209    animator->data = animator;
210    animator->run_func = func;
211    animator->run_data = (void *)data;
212    animator->start = ecore_loop_time_get();
213    animator->run = runtime;
214    return animator;
215 }
216
217 static double
218 _pos_map_sin(double in)
219 {
220    return eina_f32p32_double_to(eina_f32p32_sin(eina_f32p32_double_from(in)));
221 }
222
223 static double
224 _pos_map_cos(double in)
225 {
226    return eina_f32p32_double_to(eina_f32p32_cos(eina_f32p32_double_from(in)));
227 }
228
229 static double
230 _pos_map_accel_factor(double pos, double v1)
231 {
232    int i, fact = (int)v1;
233    double p, o1 = pos, o2 = pos, v;
234    p = 1.0 - _pos_map_sin((M_PI / 2.0) + ((pos * M_PI) / 2.0));
235    o2 = p;
236    for (i = 0; i < fact; i++)
237      {
238         o1 = o2;
239         o2 = o2 * p;
240      }
241    v = v1 - (double)fact;
242    pos = (v * o2) + ((1.0 - v) * o1);
243    return pos;
244 }
245
246 static double
247 _pos_map_pow(double pos, double divis, int p)
248 {
249    double v = 1.0;
250    int i;
251    for (i = 0; i < p; i++) v *= pos;
252    return ((pos * divis) * (1.0 - v)) + (pos * v);
253 }
254
255 static double
256 _pos_map_spring(double pos, int bounces, double decfac)
257 {
258    int segnum, segpos, b1, b2;
259    double len, decay, decpos, p2;
260    if (bounces < 0) bounces = 0;
261    p2 = _pos_map_pow(pos, 0.5, 3);
262    len = (M_PI / 2.0) + ((double)bounces * M_PI);
263    segnum = (bounces * 2) + 1;
264    segpos = 2 * (((int)(p2 * segnum) + 1) / 2);
265    b1 = segpos;
266    b2 = segnum + 1;
267    if (b1 < 0) b1 = 0;
268    decpos = (double)b1 / (double)b2;
269    decay = _pos_map_accel_factor(1.0 - decpos, decfac);
270    return _pos_map_sin((M_PI / 2.0) + (p2 * len)) * decay;
271 }
272
273 /**
274  * Maps an input position from 0.0 to 1.0 along a timeline to another position
275  *
276  * Takes an input position (0.0 to 1.0) and maps to a new position (normally
277  * between 0.0 and 1.0, but it may go above/below 0.0 or 1.0 to show that it
278  * has "overshot" the mark) using some interpolation (mapping) algorithm.
279  *
280  * You might normally use this like:
281  * @code
282  * double pos; // input position in a timeline from 0.0 to 1.0
283  * double out; // output position after mapping
284  * int x1, y1, x2, y2; // x1 & y1 are start position, x2 & y2 are end position
285  * int x, y; // x & y are the calculated position
286  *
287  * out = ecore_animator_pos_map(pos, ECORE_POS_MAP_BOUNCE, 1.8, 7);
288  * x = (x1 * out) + (x2 * (1.0 - out));
289  * y = (y1 * out) + (y2 * (1.0 - out));
290  * move_my_object_to(myobject, x, y);
291  * @endcode
292  *
293  * @param pos The input position to map
294  * @param map The mapping to use
295  * @param v1 A parameter use by the mapping (pass 0.0 if not used)
296  * @param v2 A parameter use by the mapping (pass 0.0 if not used)
297  * @return The mapped value
298  *
299  * @since 1.1.0
300  */
301 EAPI double
302 ecore_animator_pos_map(double pos, Ecore_Pos_Map map, double v1, double v2)
303 {
304    if (pos > 1.0) pos = 1.0;
305    else if (pos < 0.0) pos = 0.0;
306    switch (map)
307      {
308       case ECORE_POS_MAP_LINEAR:
309         return pos;
310       case ECORE_POS_MAP_ACCELERATE:
311         pos = 1.0 - _pos_map_sin((M_PI / 2.0) + ((pos * M_PI) / 2.0));
312         return pos;
313       case ECORE_POS_MAP_DECELERATE:
314         pos = _pos_map_sin((pos * M_PI) / 2.0);
315         return pos;
316       case ECORE_POS_MAP_SINUSOIDAL:
317         pos = (1.0 - _pos_map_cos(pos * M_PI)) / 2.0;
318         return pos;
319       case ECORE_POS_MAP_ACCELERATE_FACTOR:
320         pos = _pos_map_accel_factor(pos, v1);
321         return pos;
322       case ECORE_POS_MAP_DECELERATE_FACTOR:
323         pos = 1.0 - _pos_map_accel_factor(1.0 - pos, v1);
324         return pos;
325       case ECORE_POS_MAP_SINUSOIDAL_FACTOR:
326         if (pos < 0.5) pos = _pos_map_accel_factor(pos * 2.0, v1) / 2.0;
327         else pos = 1.0 - (_pos_map_accel_factor((1.0 - pos) * 2.0, v1) / 2.0);
328         return pos;
329       case ECORE_POS_MAP_DIVISOR_INTERP:
330         pos = _pos_map_pow(pos, v1, (int)v2);
331         return pos;
332       case ECORE_POS_MAP_BOUNCE:
333         pos = _pos_map_spring(pos, (int)v2, v1);
334         if (pos < 0.0) pos = -pos;
335         pos = 1.0 - pos;
336         return pos;
337       case ECORE_POS_MAP_SPRING:
338         pos = 1.0 - _pos_map_spring(pos, (int)v2, v1);
339         return pos;
340       default:
341         return pos;
342      }
343    return pos;
344    v2 = 0.0;
345 }
346
347 /**
348  * Delete the specified animator from the animator list.
349  * @param animator The animator to delete
350  * @return The data pointer set for the animator on add
351  *
352  * Delete the specified @p aqnimator from the set of animators that are executed
353  * during main loop execution. This function returns the data parameter that
354  * was being passed to the callback on success, or NULL on failure. After this
355  * call returns the specified animator object @p animator is invalid and should not
356  * be used again. It will not get called again after deletion.
357  */
358 EAPI void *
359 ecore_animator_del(Ecore_Animator *animator)
360 {
361    if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
362      {
363         ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
364                          "ecore_animator_del");
365         return NULL;
366      }
367    if (animator->delete_me) return animator->data;
368    animator->delete_me = EINA_TRUE;
369    animators_delete_me++;
370    if (animator->run_func) return animator->run_data;
371    return animator->data;
372 }
373
374 /**
375  * Set the animator call interval in seconds.
376  * @param frametime The time in seconds in between animator ticks.
377  *
378  * This function sets the time interval (in seconds) between animator ticks.
379  */
380 EAPI void
381 ecore_animator_frametime_set(double frametime)
382 {
383    if (frametime < 0.0) frametime = 0.0;
384    if (animators_frametime == frametime) return;
385    animators_frametime = frametime;
386    _end_tick();
387    if (animators) _begin_tick();
388 }
389
390 /**
391  * Get the animator call interval in seconds.
392  * @return The time in second in between animator ticks.
393  *
394  * this function retrieves the time between animator ticks, in seconds.
395  */
396 EAPI double
397 ecore_animator_frametime_get(void)
398 {
399    return animators_frametime;
400 }
401
402 /**
403  * Suspend the specified animator.
404  * @param animator The animator to delete
405  *
406  * The specified @p animator will be temporarly removed from the set of animators
407  * that are executed during main loop execution.
408  */
409 EAPI void
410 ecore_animator_freeze(Ecore_Animator *animator)
411 {
412    if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
413      {
414         ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
415                          "ecore_animator_del");
416         return;
417      }
418    if (animator->delete_me) return;
419    animator->suspended = EINA_TRUE;
420 }
421
422 /**
423  * Restore execution of the specified animator.
424  * @param animator The animator to delete
425  *
426  * The specified @p animator will be put back in the set of animators
427  * that are executed during main loop execution.
428  */
429 EAPI void
430 ecore_animator_thaw(Ecore_Animator *animator)
431 {
432    if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
433      {
434         ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
435                          "ecore_animator_del");
436         return;
437      }
438    if (animator->delete_me) return;
439    animator->suspended = EINA_FALSE;
440 }
441
442 /**
443  * Set the source of animator ticks for the mainloop
444  * 
445  * @param source The source of animator ticks to use
446  *
447  * This sets the source of animator ticks. When an animator is active the
448  * mainloop will "tick" over frame by frame calling all animators that are
449  * registered until none are. The mainloop will tick at a given rate based
450  * on the animator source. The default source is the system clock timer
451  * source - ECORE_ANIMATOR_SOURCE_TIMER. This source uses the system clock
452  * to tick over every N seconds (specified by ecore_animator_frametime_set(),
453  * with the default being 1/30th of a second unless set otherwise). You can
454  * set a custom tick source by setting the source to 
455  * ECORE_ANIMATOR_SOURCE_CUSTOM and then drive it yourself based on some input
456  * tick source (like another application via ipc, some vertical blanking
457  * interrupt etc.) using ecore_animator_custom_source_tick_begin_callback_set()
458  * and ecore_animator_custom_source_tick_end_callback_set() to set the
459  * functions that will be called to start and stop the ticking source, which
460  * when it gets a "tick" should call ecore_animator_custom_tick() to make
461  * the animator "tick" over 1 frame.
462  */
463 EAPI void
464 ecore_animator_source_set(Ecore_Animator_Source source)
465 {
466    src = source;
467    _end_tick();
468    if (animators) _begin_tick();
469 }
470
471 /**
472  * Get the animator source currently set
473  * @return The current animator source
474  *
475  * This gets the current animator source. See ecore_animator_source_set() for
476  * more information.
477  */
478 EAPI Ecore_Animator_Source
479 ecore_animator_source_get(void)
480 {
481    return src;
482 }
483
484 /**
485  * Set the function that begins a custom animator tick source
486  * 
487  * @param func The function to call when ticking is to begin
488  * @param data The data passed to the tick begin function as its parameter
489  *
490  * The Ecore Animator infrastructure handles tracking if animators are needed
491  * or not and which ones need to be called and when, but when the tick source
492  * is custom, you have to provide a tick source by calling
493  * ecore_animator_custom_tick() to indicate a frame tick happened. In order
494  * to allow the source of ticks to be dynamically enabled or disabled as
495  * needed, the @p func when set is called to enable the tick source to
496  * produce tick events that call ecore_animator_custom_tick(). If @p func
497  * is NULL then no function is called to begin custom ticking.
498  */
499 EAPI void
500 ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func, const void *data)
501 {
502    begin_tick_cb = func;
503    begin_tick_data = data;
504    _end_tick();
505    if (animators) _begin_tick();
506 }
507
508 /**
509  * Set the function that ends a custom animator tick source
510  * @param func The function to call when ticking is to end
511  * @param data The data passed to the tick end function as its parameter
512  *
513  * This function is a matching pair to the function set by
514  * ecore_animator_custom_source_tick_begin_callback_set() and is called
515  * when ticking is to stop. If @p func is NULL then no function will be
516  * called to stop ticking. For more information please see
517  * ecore_animator_custom_source_tick_begin_callback_set().
518  */
519 EAPI void
520 ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func, const void *data)
521 {
522    end_tick_cb = func;
523    end_tick_data = data;
524    _end_tick();
525    if (animators) _begin_tick();
526 }
527
528 /**
529  * Trigger a custom animator tick
530  *
531  * When animator source is set to ECORE_ANIMATOR_SOURCE_CUSTOM, then calling
532  * this function triggers a run of all animators currently registered with
533  * Ecore as this indicates a "frame tick" happened. This will do nothing
534  * if the animator source (set by ecore_animator_source_set() ) is not set
535  * to ECORE_ANIMATOR_SOURCE_CUSTOM.
536  */
537 EAPI void
538 ecore_animator_custom_tick(void)
539 {
540    if (src == ECORE_ANIMATOR_SOURCE_CUSTOM) _do_tick();
541 }
542
543 void
544 _ecore_animator_shutdown(void)
545 {
546    _end_tick();
547    while (animators)
548      {
549         Ecore_Animator *animator;
550
551         animator = animators;
552         animators = (Ecore_Animator *) eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animators));
553         ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE);
554         free(animator);
555      }
556 }
557
558 static Eina_Bool
559 _ecore_animator_run(void *data)
560 {
561    Ecore_Animator *animator = data;
562    double pos = 0.0, t;
563    Eina_Bool run_ret;
564
565    t = ecore_loop_time_get();
566    if (animator->run > 0.0)
567      {
568         pos = (t - animator->start) / animator->run;
569         if (pos > 1.0) pos = 1.0;
570         else if (pos < 0.0) pos = 0.0;
571      }
572    run_ret = animator->run_func(animator->run_data, pos);
573    if (t >= (animator->start + animator->run)) run_ret = EINA_FALSE;
574    return run_ret;
575 }
576
577 static Eina_Bool
578 _ecore_animator(void *data __UNUSED__)
579 {
580    return _do_tick();
581 }
582
583 /**
584  * @}
585  */
586
587 /**
588  * @}
589  */