efl/src/lib/ecore/ecore_anim.c

1116 lines
28 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_SYS_TIMERFD_H)
# define HAVE_EPOLL 1
# include <sys/epoll.h>
# include <sys/timerfd.h>
#endif
#ifdef _WIN32
# include <winsock2.h>
# include <evil_private.h> /* pipe */
# define pipe_write(fd, buffer, size) send((fd), (char *)(buffer), size, 0)
# define pipe_read(fd, buffer, size) recv((fd), (char *)(buffer), size, 0)
# define pipe_close(fd) closesocket(fd)
# define PIPE_FD_ERROR SOCKET_ERROR
#else
# include <sys/select.h>
# include <fcntl.h>
# define pipe_write(fd, buffer, size) write((fd), buffer, size)
# define pipe_read(fd, buffer, size) read((fd), buffer, size)
# define pipe_close(fd) close(fd)
# define PIPE_FD_ERROR -1
#endif /* ! _WIN32 */
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#endif
#include "Ecore.h"
#include "ecore_private.h"
static int _ecore_anim_log_dom = -1;
static Eina_Bool _ee_animators_setup = EINA_FALSE;
#ifdef ERR
# undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_ecore_anim_log_dom, __VA_ARGS__)
#ifdef DBG
# undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_ecore_anim_log_dom, __VA_ARGS__)
#ifdef INF
# undef INF
#endif
#define INF(...) EINA_LOG_DOM_INFO(_ecore_anim_log_dom, __VA_ARGS__)
#ifdef WRN
# undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_ecore_anim_log_dom, __VA_ARGS__)
#ifdef CRI
# undef CRI
#endif
#define CRI(...) EINA_LOG_DOM_CRIT(_ecore_anim_log_dom, __VA_ARGS__)
static void _do_tick(void);
static Eina_Bool _ecore_animator_run(void *data);
static int animators_delete_me = 0;
static Ecore_Animator *animators = NULL;
static volatile double animators_frametime = 1.0 / 60.0;
static unsigned int animators_suspended = 0;
static Ecore_Animator_Source src = ECORE_ANIMATOR_SOURCE_TIMER;
static int ticking = 0;
static Ecore_Cb begin_tick_cb = NULL;
static const void *begin_tick_data = NULL;
static Ecore_Cb end_tick_cb = NULL;
static const void *end_tick_data = NULL;
static Eina_Bool animator_ran = EINA_FALSE;
static volatile int timer_fd_read = -1;
static volatile int timer_fd_write = -1;
static Ecore_Thread *timer_thread = NULL;
static volatile int timer_event_is_busy = 0;
static Eina_Spinlock tick_queue_lock;
static int tick_queue_count = 0;
static Eina_Bool tick_skip = EINA_FALSE;
#ifndef _WIN32
extern volatile int exit_signal_received;
#endif
static Ecore_Evas_Object_Animator_Interface _anim_iface;
static void
_tick_send(signed char val)
{
DBG("_tick_send(%i)", val);
if (pipe_write(timer_fd_write, &val, 1) != 1)
{
ERR("Cannot write to animator control fd");
}
}
static void
_timer_send_time(double t, Ecore_Thread *thread)
{
double *tim = malloc(sizeof(*tim));
if (tim)
{
*tim = t;
DBG(" ... send %1.8f", t);
eina_spinlock_take(&tick_queue_lock);
tick_queue_count++;
eina_spinlock_release(&tick_queue_lock);
ecore_thread_feedback(thread, tim);
}
}
static void
_timer_tick_core(void *data EINA_UNUSED, Ecore_Thread *thread)
{
#ifdef HAVE_EPOLL
int pollfd = -1, timerfd = -1;
struct epoll_event pollev = { 0 };
struct epoll_event pollincoming[2];
uint64_t timerfdbuf;
int i;
struct itimerspec tspec_new;
struct itimerspec tspec_old;
#endif
double t_out, t_gap, t_target;
fd_set rfds, wfds, exfds;
struct timeval tv;
Eina_Bool data_control;
Eina_Bool data_timeout;
unsigned int t;
signed char tick = 0;
double t0, d, ft;
int ret;
eina_thread_name_set(eina_thread_self(), "Eanimator-timer");
#ifdef HAVE_PRCTL
prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0);
#endif
#ifdef HAVE_EPOLL
pollfd = epoll_create(1);
if (pollfd >= 0) eina_file_close_on_exec(pollfd, EINA_TRUE);
#if defined(TFD_NONBLOCK) && defined(TFD_CLOEXEC)
timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
#endif
if (timerfd < 0)
{
timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timerfd >= 0) eina_file_close_on_exec(timerfd, EINA_TRUE);
}
if ((timerfd < 0) && (pollfd >= 0))
{
close(pollfd);
pollfd = -1;
}
#define INPUT_TIMER_CONTROL (&(pollincoming[0]))
#define INPUT_TIMER_TIMERFD (&(pollincoming[1]))
if (pollfd >= 0)
{
pollev.data.ptr = INPUT_TIMER_CONTROL;
pollev.events = EPOLLIN;
if (epoll_ctl(pollfd, EPOLL_CTL_ADD, timer_fd_read, &pollev) != 0)
{
close(timerfd);
timerfd = -1;
close(pollfd);
pollfd = -1;
}
if (pollfd >= 0)
{
pollev.data.ptr = INPUT_TIMER_TIMERFD;
pollev.events = EPOLLIN;
if (epoll_ctl(pollfd, EPOLL_CTL_ADD, timerfd, &pollev) != 0)
{
close(timerfd);
timerfd = -1;
close(pollfd);
pollfd = -1;
}
}
}
if (pollfd >= 0)
{
while (!ecore_thread_check(thread))
{
data_control = EINA_FALSE;
data_timeout = EINA_FALSE;
ft = animators_frametime;
DBG("------- timer_event_is_busy=%i", timer_event_is_busy);
t0 = ecore_time_get();
d = fmod(t0, ft);
t_target = t0 - d + ft;
if (tick)
{
t = (ft - d) * 1000000000.0;
tspec_new.it_value.tv_sec = t / 1000000000;
tspec_new.it_value.tv_nsec = t % 1000000000;
tspec_new.it_interval.tv_sec = 0;
tspec_new.it_interval.tv_nsec = 0;
timerfd_settime(timerfd, 0, &tspec_new, &tspec_old);
ret = epoll_wait(pollfd, pollincoming, 2, 200);
t_out = ecore_time_get();
t_gap = t_out - t0;
if (t_gap > (ft * 2.0))
fprintf(stderr,
"ERROR: ecore_animator thread - epoll_wait(..., 200) at %1.5f should have slept ~ %1.5fs but took %1.5fs!\n",
t0, ft, t_out - t0);
}
else
{
tspec_new.it_value.tv_sec = 0;
tspec_new.it_value.tv_nsec = 0;
tspec_new.it_interval.tv_sec = 0;
tspec_new.it_interval.tv_nsec = 0;
timerfd_settime(timerfd, 0, &tspec_new, &tspec_old);
ret = epoll_wait(pollfd, pollincoming, 2, -1);
}
for (i = 0; i < ret; i++)
{
if (pollincoming[i].events & EPOLLIN)
{
if (pollincoming[i].data.ptr == INPUT_TIMER_TIMERFD)
{
if (read(timerfd, &timerfdbuf, sizeof(timerfdbuf)) == -1)
{
ERR("Cannot read from timer descriptor. %m.");
}
data_timeout = EINA_TRUE;
}
else if (pollincoming[i].data.ptr == INPUT_TIMER_CONTROL)
data_control = EINA_TRUE;
}
}
if (data_control)
{
if (pipe_read(timer_fd_read, &tick, sizeof(tick)) != 1)
{
ERR("Cannot read from animator control fd");
}
if (tick == -1) goto done;
}
else if (data_timeout)
{
if (tick) _timer_send_time(t_target, thread);
}
}
}
else
#endif
{
while (!ecore_thread_check(thread))
{
data_control = EINA_FALSE;
data_timeout = EINA_FALSE;
ft = animators_frametime;
DBG("------- timer_event_is_busy=%i", timer_event_is_busy);
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&exfds);
FD_SET(timer_fd_read, &rfds);
t0 = ecore_time_get();
d = fmod(t0, ft);
t_target = t0 - d + ft;
if (tick)
{
t = (ft - d) * 1000000.0;
tv.tv_sec = t / 1000000;
tv.tv_usec = t % 1000000;
ret = select(timer_fd_read + 1, &rfds, &wfds, &exfds, &tv);
t_out = ecore_time_get();
t_gap = t_out - t0;
if (t_gap > (ft * 2.0))
fprintf(stderr,
"ERROR: ecore_animator thread - select() at %1.5f should have slept ~ %1.5fs but took %1.5fs!\n",
t0, ft, t_out - t0);
}
else
{
ret = select(timer_fd_read + 1, &rfds, &wfds, &exfds, NULL);
}
if ((ret == 1) && (FD_ISSET(timer_fd_read, &rfds)))
data_control = EINA_TRUE;
else if (ret == 0)
data_timeout = EINA_TRUE;
if (data_control)
{
if (pipe_read(timer_fd_read, &tick, sizeof(tick)) != 1)
{
ERR("Cannot read from animator control fd");
}
DBG("tick = %i", tick);
if (tick == -1) goto done;
}
else if (data_timeout)
{
if (tick) _timer_send_time(t_target, thread);
}
}
}
done:
#ifdef HAVE_EPOLL
if (pollfd >= 0)
{
close(pollfd);
pollfd = -1;
}
if (timerfd >= 0)
{
close(timerfd);
timerfd = -1;
}
#endif
pipe_close(timer_fd_read);
timer_fd_read = -1;
pipe_close(timer_fd_write);
timer_fd_write = -1;
}
static void
_timer_tick_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void *msg)
{
int tick_queued;
eina_spinlock_take(&tick_queue_lock);
tick_queued = tick_queue_count;
tick_queue_count--;
eina_spinlock_release(&tick_queue_lock);
DBG("notify.... %3.3f %i", *((double *)msg), timer_event_is_busy);
if (timer_event_is_busy)
{
double *t = msg;
static double pt = 0.0;
DBG("VSYNC %1.8f = delt %1.8f", *t, *t - pt);
if ((!tick_skip) || (tick_queued == 1))
{
ecore_loop_time_set(*t);
#ifndef _WIN32
if (!exit_signal_received)
#endif
_do_tick();
_ecore_animator_flush();
}
pt = *t;
}
free(msg);
}
static void
_timer_tick_finished(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED)
{
eina_spinlock_free(&tick_queue_lock);
timer_thread = NULL;
tick_queue_count = 0;
if (timer_fd_read >= 0)
{
pipe_close(timer_fd_read);
timer_fd_read = -1;
}
if (timer_fd_write >= 0)
{
pipe_close(timer_fd_write);
timer_fd_write = -1;
}
}
static void
_timer_tick_begin(void)
{
if (timer_fd_read < 0)
{
int fds[2];
if (pipe(fds) != 0) return;
eina_file_close_on_exec(fds[0], EINA_TRUE);
eina_file_close_on_exec(fds[1], EINA_TRUE);
timer_fd_read = fds[0];
timer_fd_write = fds[1];
if (getenv("ECORE_ANIMATOR_SKIP")) tick_skip = EINA_TRUE;
tick_queue_count = 0;
eina_spinlock_new(&tick_queue_lock);
timer_thread = ecore_thread_feedback_run(_timer_tick_core,
_timer_tick_notify,
_timer_tick_finished,
_timer_tick_finished,
NULL, EINA_TRUE);
}
timer_event_is_busy = 1;
_tick_send(1);
}
static void
_timer_tick_end(void)
{
if (timer_fd_read < 0) return;
timer_event_is_busy = 0;
_tick_send(0);
}
static void
_timer_tick_quit(void)
{
if (timer_fd_read < 0) return;
_tick_send(-1);
if (timer_thread) ecore_thread_wait(timer_thread, 0.5);
}
static Eina_Bool
_have_animators(void)
{
if ((animators) &&
(animators_suspended < eina_inlist_count(EINA_INLIST_GET(animators)))
)
return EINA_TRUE;
return EINA_FALSE;
}
static void
_begin_tick(void)
{
if (ticking) return;
eina_evlog(">animator", NULL, 0.0, NULL);
ticking = 1;
switch (src)
{
case ECORE_ANIMATOR_SOURCE_TIMER:
DBG("General animator registered with timer source.");
_timer_tick_begin();
break;
case ECORE_ANIMATOR_SOURCE_CUSTOM:
DBG("General animator registered with custom source.");
if (begin_tick_cb) begin_tick_cb((void *)begin_tick_data);
break;
default:
break;
}
}
static void
_end_tick(void)
{
if (!ticking) return;
eina_evlog("<animator", NULL, 0.0, NULL);
ticking = 0;
DBG("General animator unregistered.");
_timer_tick_end();
if ((src == ECORE_ANIMATOR_SOURCE_CUSTOM) && end_tick_cb)
end_tick_cb((void *)end_tick_data);
}
static void
_do_tick(void)
{
Ecore_Animator *animator;
Eina_Inlist *tmp;
DBG("General animator tick.");
EINA_INLIST_FOREACH(animators, animator)
{
animator->just_added = EINA_FALSE;
}
if (animators) eina_evlog("!FRAME", NULL, ecore_loop_time_get(), NULL);
EINA_INLIST_FOREACH_SAFE(animators, tmp, animator)
{
if ((!animator->delete_me) &&
(!animator->suspended) &&
(!animator->just_added))
{
animator_ran = EINA_TRUE;
eina_evlog("+animator", animator, 0.0, NULL);
if (!_ecore_call_task_cb(animator->func, animator->data))
{
animator->delete_me = EINA_TRUE;
animators_delete_me++;
}
eina_evlog("-animator", animator, 0.0, NULL);
}
else animator->just_added = EINA_FALSE;
}
}
static Ecore_Animator *
_ecore_animator_add(Ecore_Task_Cb func,
const void *data)
{
Ecore_Animator *animator;
EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
if (!func)
{
ERR("callback function must be set up for an Ecore_Animator object.");
return NULL;
}
animator = calloc(1, sizeof (Ecore_Animator));
if (!animator) return NULL;
animator->func = func;
animator->data = (void *)data;
animator->just_added = EINA_TRUE;
animators = (Ecore_Animator *)eina_inlist_append(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
_begin_tick();
return animator;
}
EAPI Ecore_Animator *
ecore_animator_add(Ecore_Task_Cb func,
const void *data)
{
return _ecore_animator_add(func, data);
}
EAPI Ecore_Animator *
ecore_animator_timeline_add(double runtime,
Ecore_Timeline_Cb func,
const void *data)
{
Ecore_Animator *animator;
if (runtime <= 0.0) runtime = 0.0;
animator = _ecore_animator_add(_ecore_animator_run, NULL);
if (!animator)
return NULL;
animator->data = animator;
animator->run_func = func;
animator->run_data = (void *)data;
animator->start = ecore_loop_time_get();
animator->run = runtime;
return animator;
}
static double
_pos_map_sin(double in)
{
return eina_f32p32_double_to(eina_f32p32_sin(eina_f32p32_double_from(in)));
}
#if 0
static double
_pos_map_cos(double in)
{
return eina_f32p32_double_to(eina_f32p32_cos(eina_f32p32_double_from(in)));
}
#endif
static double
_pos_map_accel_factor(double pos,
double v1)
{
int i, fact = (int)v1;
double p, o1 = pos, o2, v;
p = 1.0 - _pos_map_sin((M_PI / 2.0) + ((pos * M_PI) / 2.0));
o2 = p;
for (i = 0; i < fact; i++)
{
o1 = o2;
o2 = o2 * p;
}
v = v1 - (double)fact;
pos = (v * o2) + ((1.0 - v) * o1);
return pos;
}
static double
_pos_map_pow(double pos,
double divis,
int p)
{
double v = 1.0;
int i;
for (i = 0; i < p; i++) v *= pos;
return ((pos * divis) * (1.0 - v)) + (pos * v);
}
static double
_pos_map_spring(double pos,
int bounces,
double decfac)
{
int segnum, segpos, b1, b2;
double len, decay, decpos, p2;
if (bounces < 0) bounces = 0;
p2 = _pos_map_pow(pos, 0.5, 3);
len = (M_PI / 2.0) + ((double)bounces * M_PI);
segnum = (bounces * 2) + 1;
segpos = 2 * (((int)(p2 * segnum) + 1) / 2);
b1 = segpos;
b2 = segnum + 1;
if (b1 < 0) b1 = 0;
decpos = (double)b1 / (double)b2;
decay = _pos_map_accel_factor(1.0 - decpos, decfac);
return _pos_map_sin((M_PI / 2.0) + (p2 * len)) * decay;
}
static inline double
cuberoot(double v)
{
if (v < 0.0)
return -pow(-v, 1. / 3.);
else
return pow(v, 1. / 3.);
}
static double
_bezier_t_get(double _x, double _x1, double _x2)
{
if (_x < 0.0 || _x > 1.0) return _x;
// Cardano's algorithm
double\
pa = _x - 0.0,
pb = _x - _x1,
pc = _x - _x2,
pd = _x - 1.0;
double\
a = 3*pa-6*pb+3*pc,
b = -3*pa+3*pb,
c = pa,
d = -pa+3*pb-3*pc+pd;
a /= d;
b /= d;
c /= d;
double\
p = (3*b-a*a)/3.0,
p3 = p/3.0,
q = (2*a*a*a-9*a*b+27*c)/27.0,
q2 = q/2.0,
discriminant = q2*q2 + p3*p3*p3;
double u1, v1, root1, root2, root3;
if (discriminant < 0)
{
double\
mp3 = -p/3.0,
mp33 = mp3*mp3*mp3,
r = sqrt(mp33),
t = -q / (2*r),
cosphi = t<-1.0 ? -1.0 : t>1.0 ? 1.0 : t,
phi = acos(cosphi),
crtr = cuberoot(r),
t1 = 2*crtr;
root1 = t1 * cos(phi/3.0) - a/3.0;
root2 = t1 * cos((phi+2*M_PI)/3.0) - a/3.0;
root3 = t1 * cos((phi+4*M_PI)/3.0) - a/3.0;
if (root1 >= 0.0 && root1 <= 1.0) return root1;
if (root2 >= 0.0 && root2 <= 1.0) return root2;
if (root3 >= 0.0 && root3 <= 1.0) return root3;
}
else if (discriminant == 0)
{
u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2);
root1 = 2*u1 - a/3.0;
root2 = -u1 - a/3.0;
if (root1 >= 0.0 && root1 <= 1.0) return root1;
if (root2 >= 0.0 && root2 <= 1.0) return root2;
}
else
{
double sd = sqrt(discriminant);
u1 = cuberoot(sd - q2);
v1 = cuberoot(sd + q2);
root1 = u1 - v1 - a/3.0;
if (root1 >= 0.0 && root1 <= 1.0) return root1;
}
return _x;
}
static double
_bezier_calc(double t, double y1, double y2)
{
double y0 = 0.0;
double y3 = 1.0;
double u = 1.0 - t;
double t2 = t*t;
double t3 = t*t*t;
double u2 = u*u;
double u3 = u*u*u;
return u3 * y0 +
3.0 * u2 * t * y1 +
3.0 * u * t2 * y2 +
t3 * y3;
}
static double
_pos_map_cubic_bezier(double pos,
double x1,
double y1,
double x2,
double y2)
{
if (EINA_DBL_EQ(x1, y1) &&
EINA_DBL_EQ(x2, y2))
return pos;
return _bezier_calc(_bezier_t_get(pos, x1, x2), y1, y2);
}
#define DBL_TO(Fp) eina_f32p32_double_to(Fp)
#define DBL_FROM(D) eina_f32p32_double_from(D)
#define INT_FROM(I) eina_f32p32_int_from(I)
#define SIN(Fp) eina_f32p32_sin(Fp)
#define COS(Fp) eina_f32p32_cos(Fp)
#define ADD(A, B) eina_f32p32_add(A, B)
#define SUB(A, B) eina_f32p32_sub(A, B)
#define MUL(A, B) eina_f32p32_mul(A, B)
EAPI double
ecore_animator_pos_map_n(double pos,
Ecore_Pos_Map map,
int v_size,
double *v)
{
double v0 = 0, v1 = 0, v2 = 0, v3 = 0;
/* purely functional - locking not required */
if (pos >= 1.0) return 1.0;
else if (pos <= 0.0)
return 0.0;
switch (map)
{
case ECORE_POS_MAP_LINEAR:
return pos;
case ECORE_POS_MAP_ACCELERATE:
/* pos = 1 - sin(Pi / 2 + pos * Pi / 2); */
pos = DBL_TO(SUB(INT_FROM(1), SIN(ADD((EINA_F32P32_PI >> 1), MUL(DBL_FROM(pos), (EINA_F32P32_PI >> 1))))));
return pos;
case ECORE_POS_MAP_DECELERATE:
/* pos = sin(pos * Pi / 2); */
pos = DBL_TO(SIN(MUL(DBL_FROM(pos), (EINA_F32P32_PI >> 1))));
return pos;
case ECORE_POS_MAP_SINUSOIDAL:
/* pos = (1 - cos(pos * Pi)) / 2 */
pos = DBL_TO((SUB(INT_FROM(1), COS(MUL(DBL_FROM(pos), EINA_F32P32_PI)))) >> 1);
return pos;
case ECORE_POS_MAP_ACCELERATE_FACTOR:
if (v_size > 0) v0 = v[0];
pos = _pos_map_accel_factor(pos, v0);
return pos;
case ECORE_POS_MAP_DECELERATE_FACTOR:
if (v_size > 0) v0 = v[0];
pos = 1.0 - _pos_map_accel_factor(1.0 - pos, v0);
return pos;
case ECORE_POS_MAP_SINUSOIDAL_FACTOR:
if (v_size > 0) v0 = v[0];
if (pos < 0.5) pos = _pos_map_accel_factor(pos * 2.0, v0) / 2.0;
else pos = 1.0 - (_pos_map_accel_factor((1.0 - pos) * 2.0, v0) / 2.0);
return pos;
case ECORE_POS_MAP_DIVISOR_INTERP:
if (v_size > 0) v0 = v[0];
if (v_size > 1) v1 = v[1];
pos = _pos_map_pow(pos, v0, (int)v1);
return pos;
case ECORE_POS_MAP_BOUNCE:
if (v_size > 0) v0 = v[0];
if (v_size > 1) v1 = v[1];
pos = _pos_map_spring(pos, (int)v1, v0);
if (pos < 0.0) pos = -pos;
pos = 1.0 - pos;
return pos;
case ECORE_POS_MAP_SPRING:
if (v_size > 0) v0 = v[0];
if (v_size > 1) v1 = v[1];
pos = 1.0 - _pos_map_spring(pos, (int)v1, v0);
return pos;
case ECORE_POS_MAP_CUBIC_BEZIER:
if (v_size > 0) v0 = v[0];
if (v_size > 1) v1 = v[1];
if (v_size > 2) v2 = v[2];
if (v_size > 3) v3 = v[3];
pos = _pos_map_cubic_bezier(pos, v0, v1, v2, v3);
return pos;
default:
return pos;
}
return pos;
}
EAPI double
ecore_animator_pos_map(double pos,
Ecore_Pos_Map map,
double v1,
double v2)
{
double v[2];
v[0] = v1;
v[1] = v2;
return ecore_animator_pos_map_n(pos, map, 2, v);
}
EAPI void *
ecore_animator_del(Ecore_Animator *animator)
{
void *data = NULL;
if (!animator) return NULL;
EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
if (animator->ee) return _anim_iface.del(animator);
if (animator->delete_me)
{
data = animator->data;
goto end;
}
animator->delete_me = EINA_TRUE;
animators_delete_me++;
if (animator->run_func)
data = animator->run_data;
else
data = animator->data;
end:
if (!in_main_loop) _ecore_animator_flush();
return data;
}
EAPI void
ecore_animator_frametime_set(double frametime)
{
EINA_MAIN_LOOP_CHECK_RETURN;
if (frametime < 0.0) frametime = 0.0;
if (EINA_DBL_EQ(animators_frametime, frametime)) return ;
animators_frametime = frametime;
_end_tick();
if (_have_animators()) _begin_tick();
}
EAPI double
ecore_animator_frametime_get(void)
{
EINA_MAIN_LOOP_CHECK_RETURN_VAL(0.0);
return animators_frametime;
}
EAPI void
ecore_animator_freeze(Ecore_Animator *animator)
{
EINA_MAIN_LOOP_CHECK_RETURN;
if (!animator) return;
if (animator->delete_me) return;
if (animator->suspended) return;
if (animator->ee)
{
_anim_iface.freeze(animator);
return;
}
animator->suspended = EINA_TRUE;
animators_suspended++;
if (!_have_animators()) _end_tick();
}
EAPI void
ecore_animator_thaw(Ecore_Animator *animator)
{
EINA_MAIN_LOOP_CHECK_RETURN;
if (!animator) return;
if (animator->delete_me) return;
if (!animator->suspended) return;
if (animator->ee)
{
_anim_iface.thaw(animator);
return;
}
animator->suspended = EINA_FALSE;
animators_suspended--;
if (_have_animators()) _begin_tick();
}
EAPI void
ecore_animator_source_set(Ecore_Animator_Source source)
{
EINA_MAIN_LOOP_CHECK_RETURN;
_end_tick();
src = source;
DBG("New source set to %s.",
source == ECORE_ANIMATOR_SOURCE_TIMER ? "TIMER" :
source == ECORE_ANIMATOR_SOURCE_CUSTOM ? "CUSTOM" :
"UNKNOWN");
if (_have_animators()) _begin_tick();
}
EAPI Ecore_Animator_Source
ecore_animator_source_get(void)
{
EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
return src;
}
EAPI void
ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func,
const void *data)
{
EINA_MAIN_LOOP_CHECK_RETURN;
_end_tick();
begin_tick_cb = func;
begin_tick_data = data;
if (_have_animators()) _begin_tick();
}
EAPI void
ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func,
const void *data)
{
EINA_MAIN_LOOP_CHECK_RETURN;
_end_tick();
end_tick_cb = func;
end_tick_data = data;
if (_have_animators()) _begin_tick();
}
EAPI void
ecore_animator_custom_tick(void)
{
EINA_MAIN_LOOP_CHECK_RETURN;
if (src != ECORE_ANIMATOR_SOURCE_CUSTOM) return;
#ifndef _WIN32
if (!exit_signal_received)
#endif
_do_tick();
_ecore_animator_flush();
}
void
_ecore_animator_shutdown(void)
{
Ecore_Animator *animator;
_timer_tick_quit();
_end_tick();
EINA_INLIST_FREE(animators, animator)
{
if (animator->suspended) animators_suspended--;
if (animator->delete_me) animators_delete_me--;
animators = (Ecore_Animator *) eina_inlist_remove
(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
free(animator);
}
eina_log_domain_unregister(_ecore_anim_log_dom);
_ecore_anim_log_dom = -1;
}
void
_ecore_animator_run_reset(void)
{
animator_ran = EINA_FALSE;
}
Eina_Bool
_ecore_animator_run_get(void)
{
return animator_ran;
}
static Eina_Bool
_ecore_animator_run(void *data)
{
Ecore_Animator *animator = data;
double pos = 0.0, t;
Eina_Bool run_ret;
t = ecore_loop_time_get();
if (animator->run > 0.0)
{
pos = (t - animator->start) / animator->run;
if (pos > 1.0) pos = 1.0;
else if (pos < 0.0)
pos = 0.0;
}
run_ret = animator->run_func(animator->run_data, pos);
if (eina_dbl_exact(pos, 1.0)) run_ret = EINA_FALSE;
return run_ret;
}
Eina_Bool
_ecore_animator_flush(void)
{
Ecore_Animator *animator;
if (animators_delete_me)
{
Ecore_Animator *l;
for (l = animators; l; )
{
animator = l;
l = (Ecore_Animator *)EINA_INLIST_GET(l)->next;
if (animator->delete_me)
{
if (animator->suspended) animators_suspended--;
animators = (Ecore_Animator *)
eina_inlist_remove(EINA_INLIST_GET(animators),
EINA_INLIST_GET(animator));
free(animator);
animators_delete_me--;
if (animators_delete_me == 0) break;
}
}
}
if (!_have_animators())
{
_end_tick();
return EINA_FALSE;
}
return EINA_TRUE;
}
void
_ecore_animator_init(void)
{
_ecore_anim_log_dom = eina_log_domain_register("ecore_animator", ECORE_DEFAULT_LOG_COLOR);
if (_ecore_anim_log_dom < 0)
{
EINA_LOG_ERR("Ecore was unable to create a log domain.");
}
}
void
ecore_evas_object_animator_init(Ecore_Evas_Object_Animator_Interface *iface)
{
_anim_iface = *iface;
_ee_animators_setup = EINA_TRUE;
}
Ecore_Animator *
ecore_evas_animator_timeline_add(void *evo, double runtime, Ecore_Timeline_Cb func, const void *data)
{
Ecore_Animator *anim = NULL;
if (_ee_animators_setup)
anim = _anim_iface.timeline_add(evo, runtime, func, data);
if (anim) return anim;
return ecore_animator_timeline_add(runtime, func, data);
}
Ecore_Animator *
ecore_evas_animator_add(void *evo, Ecore_Task_Cb func, const void *data)
{
Ecore_Animator *anim = NULL;
if (_ee_animators_setup)
anim = _anim_iface.add(evo, func, data);
if (anim) return anim;
return ecore_animator_add(func, data);
}