PUI & default-backend: add force argument to the control function of animation
[platform/core/uifw/libpui.git] / src / PUI_ani.c
1 /*
2  * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include "PUI_internal.h"
27 #include "PUI_backend.h"
28 #include "PUI.h"
29 #include <Eina.h>
30
31 static int KEY_WL_BUFFER = 0xabcdbeaf;
32 static int KEY_CLIENT = 0xdcbabeaf;
33
34 static void
35 _pui_ani_cb_frame_done(Ecore_Wl2_Window *win, uint32_t timestamp EINA_UNUSED, void *data)
36 {
37         pui_h handle = (pui_h) data;
38
39         pui_info("Frame done ! (window=0x%x)\n", ecore_wl2_window_id_get(win));
40
41         /* TODO : make use of handle */
42         (void) handle;
43
44         return;
45 }
46
47 static void
48 _buffer_release(void *data, struct wl_buffer *buffer)
49 {
50         tbm_surface_h surface = (tbm_surface_h)data;
51         pui_h handle;
52
53         tbm_surface_internal_get_user_data(surface, (unsigned long)&KEY_CLIENT, (void **)&handle);
54         tbm_surface_queue_release(handle->tbm_queue, surface);
55
56         pui_info("[UPDATE] release wl_buffer:%p, surface:%p\n", buffer, surface);
57 }
58
59 static const struct wl_buffer_listener buffer_listener = {
60     _buffer_release
61 };
62
63 pui_ani_control_buffer *
64 pui_ani_get_buffer(pui_ani_h ani_h)
65 {
66         pui_h handle = NULL;
67         tbm_surface_error_e ret;
68         tbm_surface_h surface;
69         pui_ani_control_buffer *buffer = NULL;
70
71         if (!ani_h)
72         {
73                 pui_err("Invalid pui ani handle !\n");
74                 return NULL;
75         }
76
77         handle = ani_h->pui_handle;
78
79         if (!handle)
80         {
81                 pui_err("Error : PUI_INT_ERROR_INVALID_HANDLE\n");
82                 return NULL;
83         }
84
85         if (handle->current_surface)
86         {
87                 pui_warn("Current_surface is not used !\n");
88         }
89
90         if (!tbm_surface_queue_can_dequeue(handle->tbm_queue, 0))
91         {
92                 pui_err("[UPDATE] Cannot dequeue (error : PUI_ERROR_INTERNAL)\n");
93                 return NULL;
94         }
95
96         ret = tbm_surface_queue_dequeue(handle->tbm_queue, &surface);
97
98         if (ret != TBM_SURFACE_ERROR_NONE)
99         {
100                 pui_err("[UPDATE] dequeue err:%d\n", ret);
101                 return NULL;
102         }
103
104         tbm_surface_map(surface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &handle->current_sinfo);
105
106         handle->current_surface = surface;
107         buffer = (pui_ani_control_buffer *)&(handle->current_sinfo.planes[0]);
108
109         return buffer;
110 }
111
112 pui_int_error
113 pui_ani_set_buffer(pui_ani_h ani_h, pui_ani_control_buffer *buffer)
114 {
115         pui_h handle = NULL;
116
117         if (!ani_h)
118         {
119                 pui_err("Invalid ani handle !\n");
120                 return PUI_INT_ERROR_INVALID_HANDLE;
121         }
122
123         handle = ani_h->pui_handle;
124
125         if (!handle)
126         {
127                 pui_err("Error : PUI_INT_ERROR_INVALID_HANDLE\n");
128                 return PUI_INT_ERROR_INVALID_HANDLE;
129         }
130
131         if (!handle->current_surface)
132         {
133                 pui_err("Current_surface is not valid !\n");
134                 return PUI_INT_ERROR_INVALID_SURFACE;
135         }
136
137         if (!buffer || !buffer->ptr || !buffer->size)
138                 return PUI_INT_ERROR_INVALID_BUFFER;
139
140         handle->is_buffer_set = 1;
141
142         return PUI_INT_ERROR_NONE;
143 }
144
145 pui_int_error
146 pui_ani_update(pui_ani_h ani_h)
147 {
148         tbm_surface_h surface;
149         tbm_surface_error_e ret;
150         struct wl_buffer *wl_buffer = NULL;
151
152         pui_h handle = NULL;
153
154         if (!ani_h)
155         {
156                 pui_err("Invalid ani handle !\n");
157                 return PUI_INT_ERROR_INVALID_HANDLE;
158         }
159
160         handle = ani_h->pui_handle;
161
162         if (!handle)
163         {
164                 pui_err("Error : PUI_INT_ERROR_INVALID_HANDLE\n");
165                 return PUI_INT_ERROR_INVALID_HANDLE;
166         }
167
168         if (!handle->current_surface)
169                 return PUI_INT_ERROR_INVALID_SURFACE;
170
171         if (!handle->is_buffer_set)
172         {
173                 pui_err("Buffer is not set !\n");
174                 return PUI_INT_ERROR_INVALID_BUFFER;
175         }
176
177         surface = handle->current_surface;
178         handle->current_surface = NULL;
179
180         tbm_surface_unmap(surface);
181
182         ret = tbm_surface_queue_enqueue(handle->tbm_queue, surface);
183
184         if (ret != TBM_SURFACE_ERROR_NONE)
185         {
186                 pui_err("[UPDATE] enqueue err:%d\n", ret);
187                 return PUI_INT_ERROR_INVALID_SURFACE;
188         }
189
190         ret = tbm_surface_queue_acquire(handle->tbm_queue, &surface);
191
192         if (ret != TBM_SURFACE_ERROR_NONE)
193         {
194                 pui_err("[UPDATE] acquire err:%d\n", ret);
195                 return PUI_INT_ERROR_INVALID_SURFACE;
196         }
197
198         if (!tbm_surface_internal_get_user_data(surface, (unsigned long)&KEY_WL_BUFFER, (void **)&wl_buffer)) {
199                 wl_buffer = wayland_tbm_client_create_buffer(handle->wl_tbm_client, surface);
200
201                 if (!wl_buffer)
202                 {
203                         pui_err("[UPDATE] failed to create wl_buffer tbm_surface:%p\n", surface);
204                         return PUI_INT_ERROR_INVALID_BUFFER;
205                 }
206
207                 wl_buffer_add_listener(wl_buffer, &buffer_listener, surface);
208
209                 tbm_surface_internal_add_user_data(surface, (unsigned long)&KEY_WL_BUFFER, NULL);
210                 tbm_surface_internal_set_user_data(surface, (unsigned long)&KEY_WL_BUFFER, wl_buffer);
211                 tbm_surface_internal_add_user_data(surface, (unsigned long)&KEY_CLIENT, NULL);
212                 tbm_surface_internal_set_user_data(surface, (unsigned long)&KEY_CLIENT, handle);
213         }
214
215         if (!wl_buffer)
216         {
217                 pui_err("[UPDATE] dequeue err:%d\n", ret);
218                 return PUI_INT_ERROR_INVALID_BUFFER;
219         }
220
221         ecore_wl2_window_buffer_attach(handle->win, wl_buffer, 0, 0, 0);
222         ecore_wl2_window_damage(handle->win, NULL, 0);
223         ecore_wl2_window_commit(handle->win, EINA_TRUE);
224
225         pui_info("[UPDATE] commit wl_buffer:%p, surface:%p\n", wl_buffer, surface);
226
227         handle->is_buffer_set = 0;
228
229         return PUI_INT_ERROR_NONE;
230 }
231
232 static Eina_Bool
233 _pui_ani_frame_cb(void *data)
234 {
235         Eina_Bool ret;
236
237         pui_ani_t *ani = (pui_ani_t *)data;
238
239         if (!ani)
240         {
241                 pui_err("Invalid pui ani !\n");
242                 return ECORE_CALLBACK_CANCEL;
243         }
244
245         if (!ani->backend_frame_cb)
246         {
247                 pui_err("Invalid backend frame_cb !\n");
248                 return ECORE_CALLBACK_CANCEL;
249         }
250
251         if (!ani->frame_cb_timer)
252         {
253                 pui_err("Invalid frame_cb timer !\n");
254                 return ECORE_CALLBACK_CANCEL;
255         }
256
257         pui_info("...\n");
258
259         ret = (Eina_Bool)ani->backend_frame_cb(ani, ++ani->serial);
260
261         if (!ret)
262         {
263                 pui_err("Failed on backend's frame_cb !Frame_cb will be removed forcefuly !\n");
264
265                 pui_ani_remove_frame_cb(ani);
266
267                 return EINA_FALSE;
268         }
269
270         if (ret && PUI_ANI_STATUS_STARTED == ani->status)
271                 pui_ani_status_update(ani, PUI_ANI_STATUS_RUNNING);
272
273         return EINA_TRUE;
274 }
275
276 pui_bool
277 pui_ani_add_frame_cb(pui_ani_t *ani, pui_bool (*frame_cb)(void *data, int serial), double frame_interval)
278 {
279         Ecore_Timer *timer = NULL;
280
281         if (!ani)
282         {
283                 pui_err("Invalid put ani !\n");
284                 return 0;
285         }
286
287         if (frame_interval <= 0.0f)
288         {
289                 pui_err("Invalid frame interval (%.2f) ! frame interval must be larger than 0.\n", frame_interval);
290                 return 0;
291         }
292
293         ani->frame_cb = _pui_ani_frame_cb;
294         ani->backend_frame_cb = frame_cb;
295         ani->frame_interval = frame_interval;
296         ani->frame_cb_data = ani;
297         ani->serial = 0;
298
299         timer = ecore_timer_add(frame_interval, _pui_ani_frame_cb, ani);
300
301         if (!timer)
302         {
303                 pui_err("Failed to add ecore timer !\n");
304                 return 0;
305         }
306
307         ani->frame_cb_timer = timer;
308
309         pui_info("[Frame callback added][ani id=%s] frame_interval=%.2f\n", ani->id, frame_interval);
310
311         /* call frame_cb for starting the first frame */
312         _pui_ani_frame_cb(ani);
313
314         return 1;
315 }
316
317 void
318 pui_ani_remove_frame_cb(pui_ani_t *ani)
319 {
320         if (!ani)
321         {
322                 pui_err("Invalid put ani !\n");
323                 return;
324         }
325
326         if (ani->frame_cb_timer)
327         {
328                 ecore_timer_del(ani->frame_cb_timer);
329                 ani->frame_cb_timer = NULL;
330         }
331
332         ani->frame_cb = NULL;
333         ani->backend_frame_cb = NULL;
334         ani->frame_interval = 0;
335         ani->frame_cb_data = NULL;
336
337         pui_info("[Frame callback removed][ani id=%s]\n", ani->id);
338 }
339
340 pui_id
341 pui_ani_get_id(pui_ani_h ani_h)
342 {
343         if (!ani_h || !ani_h->ani)
344                 return NULL;
345
346         return ani_h->ani->id;
347 }
348
349 pui_ani_cmd
350 pui_ani_get_cmd(pui_ani_h ani_h)
351 {
352         if (!ani_h || !ani_h->ani)
353                 return PUI_ANI_CMD_NONE;
354
355         return ani_h->ani->cmd;
356 }
357
358 int
359 pui_ani_get_repeat(pui_ani_h ani_h)
360 {
361         if (!ani_h || !ani_h->ani)
362                 return 0;
363
364         return ani_h->ani->repeat;
365 }
366
367 pui_backend_ani_data *
368 pui_ani_get_ani_data(pui_ani_t *ani)
369 {
370         if (!ani)
371                 return NULL;
372
373         return ani->ani_data;
374 }
375
376 void
377 pui_ani_status_update(pui_ani_t *ani, pui_ani_status status)
378 {
379         int ev_type = -1;
380         pui_ani_h ani_h;
381         PUI_Event_Animation_Status *e = NULL;
382
383         if (!ani)
384         {
385                 pui_err("Invalid pui ani !\n");
386                 return;
387         }
388
389         if (ani->status == status)
390                 return;
391
392         ani_h = ani->ani_h;
393
394         e = (PUI_Event_Animation_Status *)calloc(1, sizeof(PUI_Event_Animation_Status));
395
396         if (!e)
397         {
398                 pui_err("Failed to allocate memory for PUI Event !\n");
399                 return;
400         }
401
402         ani->status = status;
403         e->ani_h = ani_h;
404         e->win = ecore_wl2_window_id_get(ani_h->pui_handle->win);
405         e->status = status;
406
407         switch (status)
408         {
409                 case PUI_ANI_STATUS_STARTED:
410                         ev_type = PUI_EVENT_ANI_STARTED;
411                         pui_info("[Status update][ani id:%s] PUI_EVENT_ANI_STARTED event has been added.\n", ani->id);
412                         break;
413
414                 case PUI_ANI_STATUS_RUNNING:
415                         pui_info("[Status update][ani id:%s] PUI_ANI_STATUS_RUNNING !\n", ani->id);
416                         break;
417
418                 case PUI_ANI_STATUS_PAUSED:
419                         ev_type = PUI_EVENT_ANI_PAUSED;
420                         pui_info("[Status update][ani id:%s] PUI_EVENT_ANI_PAUSED event has been added.\n", ani->id);
421                         break;
422
423                 case PUI_ANI_STATUS_STOPPED:
424                         ev_type = PUI_EVENT_ANI_STOPPED;
425                         pui_info("[Status update][ani id:%s] PUI_EVENT_ANI_STOPPED event has been added.\n", ani->id);
426                         break;
427
428                 default:
429                         pui_err("Unknown status !(ani status=%d, id=%s) !\n", status, ani->id);
430                         return;
431         }
432
433         if (ev_type > 0)
434         {
435                 ecore_event_add(ev_type, e, NULL, ani_h);
436         }
437 }
438
439 pui_ani_status
440 pui_ani_status_get(pui_ani_t *ani)
441 {
442         pui_ani_status status = PUI_ANI_STATUS_UNKNOWN;
443
444         if (!ani)
445         {
446                 pui_err("Invalid pui ani !\n");
447                 return status;
448         }
449
450         return ani->status;
451 }
452
453 static pui_error
454 _pui_ani_control_with_force(pui_ani_h ani_h, pui_ani_cmd cmd, int repeat, pui_bool force)
455 {
456         pui_int_error ei = PUI_INT_ERROR_NONE;
457         pui_ani_t *ani = NULL;
458         pui_h handle = NULL;
459         pui_backend_ani_func *ani_func = NULL;
460
461         if (!ani_h)
462                 return PUI_ERROR_INVALID_ANI_HANDLE;
463
464         if (cmd < PUI_ANI_CMD_START || cmd >= PUI_ANI_CMD_LAST)
465         {
466                 pui_err("Invalid cmd ! (cmd=%d)\n", cmd);
467                 return PUI_ERROR_INVALID_ANI_CMD;
468         }
469
470         if (repeat < -1)
471         {
472                 pui_err("Invalid repeat count ! (repeat=%d)\n", repeat);
473                 return PUI_ERROR_INVALID_ANI_REPEAT;
474         }
475
476         handle = ani_h->pui_handle;
477         ani = ani_h->ani;
478
479         if (!ani || !ani->ani_data)
480         {
481                 pui_err("Invalid ani or ani_data !\n");
482                 return PUI_ERROR_INTERNAL;
483         }
484
485         ani_func = ani->ani_data->ani_func;
486
487         ani->cmd = cmd;
488         ani->repeat = repeat;
489
490         if (cmd == PUI_ANI_CMD_START)
491         {
492                 if (handle->current_ani_h && handle->current_ani_h != ani_h)
493                 {
494                         pui_ani_t *current_ani = handle->current_ani_h->ani;
495
496                         if (current_ani->status >= PUI_ANI_STATUS_STARTED &&
497                                 current_ani->status <= PUI_ANI_STATUS_RUNNING)
498                         {
499                                 pui_info("current_ani id=%s, status=%d\n", current_ani->id, current_ani->status);
500
501                                 ei = _pui_ani_control_with_force(handle->current_ani_h, PUI_ANI_CMD_STOP, 0, force);
502
503                                 if (ei != PUI_INT_ERROR_NONE)
504                                         pui_info("Failed to stop running previous animation ! (id:%s)\n", current_ani->id);
505
506                                 current_ani = NULL;
507                                 handle->current_ani_h = NULL;
508                         }
509                 }
510
511                 ei = ani_func->ani_start(ani, repeat);
512
513                 if (ei != PUI_INT_ERROR_NONE)
514                 {
515                         pui_err("Error on starting animation ! (id:%s, repeat:%d, status=%d))\n", ani->id, repeat, ani->status);
516
517                         if (ani->status != PUI_ANI_STATUS_RUNNING)
518                                 _pui_ani_control_with_force(ani_h, PUI_ANI_CMD_STOP, 0, 0);
519
520                         return PUI_ERROR_INTERNAL;
521                 }
522
523                 handle->current_ani_h = ani_h;
524         }
525         else//cmd == PUI_ANI_CMD_STOP
526         {
527                 ei = ani_func->ani_stop(ani, force);
528
529                 if (ei != PUI_INT_ERROR_NONE)
530                 {
531                         pui_err("Failied on stopping animation ! (id:%s)\n", ani->id);
532
533                         if (ani->status != PUI_ANI_STATUS_STOPPED)
534                                 pui_ani_status_update(ani, PUI_ANI_STATUS_STOPPED);
535
536                         if (ani->frame_cb_timer)
537                                 pui_ani_remove_frame_cb(ani);
538
539                         return PUI_ERROR_INTERNAL;
540                 }
541
542                 return PUI_ERROR_NONE;
543         }
544
545         ani_h->frame_done_cb = ecore_wl2_window_frame_callback_add(handle->win, _pui_ani_cb_frame_done, handle);
546
547         if (!ani_h->frame_done_cb)
548         {
549                 pui_err("Failed to add frame callback !");
550                 goto err;
551         }
552
553         return PUI_ERROR_NONE;
554
555 err:
556         return PUI_ERROR_INTERNAL;
557 }
558
559 pui_error
560 pui_ani_control(pui_ani_h ani_h, pui_ani_cmd cmd, int repeat)
561 {
562         return _pui_ani_control_with_force(ani_h, cmd, repeat, 0);
563 }
564
565 static Eina_Bool
566 _cb_visibility_change(void *data, int type EINA_UNUSED, void *event)
567 {
568         pui_ani_h ani_h =  (pui_ani_h)data;
569         pui_ani_t *ani = ani_h->ani;
570         pui_h ph = ani_h->pui_handle;
571
572         Ecore_Wl2_Event_Window_Visibility_Change *ev;
573         PUI_Event_Animation_Status *e = NULL;
574
575         ev = event;
576
577         /* check if this is needed */
578         ph->visibility = !(ev->fully_obscured);
579
580         if (ev->fully_obscured)
581         {
582                 if (ani->status == PUI_ANI_STATUS_RUNNING)
583                 {
584                         pui_info("animation(%s) will be stopped as it lost its priority !\n", ani->id);
585
586                         _pui_ani_control_with_force(ani_h, PUI_ANI_CMD_STOP, 0, 1);
587                 }
588         }
589         else
590         {
591                 if (ani->status == PUI_ANI_STATUS_PAUSED)
592                 {
593                         e = (PUI_Event_Animation_Status *)calloc(1, sizeof(PUI_Event_Animation_Status));
594
595                         if (!e)
596                         {
597                                 pui_err("Failed to allocate memory for PUI Event !\n");
598                                 return ECORE_CALLBACK_PASS_ON;
599                         }
600
601                         e->ani_h = ani_h;
602                         e->win = ev->win;
603                         e->status = ani->status;
604
605                         if(ani->status == PUI_ANI_STATUS_PAUSED)
606                         {
607                                 pui_info("[Event added][ani id:%s] PUI_EVENT_ANI_READY_TO_RESUME event has been added.\n", ani_h->id);
608                                 ecore_event_add(PUI_EVENT_ANI_READY_TO_RESUME, e, NULL, ani_h);
609                         }
610                 }
611         }
612
613         return ECORE_CALLBACK_PASS_ON;
614 }
615
616 static void
617 _pui_ani_event_handlers_init(pui_ani_h ani_h)
618 {
619         Ecore_Event_Handler *h = NULL;
620
621         if (!ani_h)
622         {
623                 pui_err("Invalid handle !\n");
624                 return;
625         }
626
627         if (!ani_h->ecore_event_hdls)
628                 ani_h->ecore_event_hdls = eina_array_new(1);
629
630         h = ecore_event_handler_add(ECORE_WL2_EVENT_WINDOW_VISIBILITY_CHANGE, _cb_visibility_change, ani_h);
631         eina_array_push(ani_h->ecore_event_hdls, h);
632
633 }
634
635 static void
636 _pui_ani_event_handlers_shutdown(pui_ani_h ani_h)
637 {
638         if (!ani_h)
639         {
640                 pui_err("Invalid handle !\n");
641                 return;
642         }
643
644         if (ani_h->ecore_event_hdls)
645         {
646                 while (eina_array_count(ani_h->ecore_event_hdls))
647                         ecore_event_handler_del(eina_array_pop(ani_h->ecore_event_hdls));
648
649                 eina_array_free(ani_h->ecore_event_hdls);
650                 ani_h->ecore_event_hdls = NULL;
651         }
652 }
653
654 pui_ani_h
655 pui_ani_create(pui_h handle, pui_id id)
656 {
657         pui_ani_h ani_h = NULL;
658         pui_ani_t *ani = NULL;
659         pui_backend_ani_data *ani_data = NULL;
660
661         if (!handle || !handle->backend_module_data)
662         {
663                 pui_err("Invalid pui handle or backend module data !\n");
664                 return NULL;
665         }
666
667         ani_data = handle->backend_module_data->ani_create(id);
668
669         if (!ani_data)
670         {
671                 pui_err("Invalid ani data from backend module data !\n");
672                 return NULL;
673         }
674
675         ani_h = (pui_ani_h)calloc(1, sizeof(pui_ani));
676
677         if (!ani_h)
678         {
679                 pui_err("Failed to allocate memory for pui ani handle !\n");
680                 goto err;
681         }
682
683         ani_h->id = strdup(id);
684         ani_h->pui_handle = handle;
685         ani_h->ecore_event_hdls = NULL;
686
687         _pui_ani_event_handlers_init(ani_h);
688
689         ani = (pui_ani_t *)calloc(1, sizeof(pui_ani_t));
690
691         if (!ani)
692         {
693                 pui_err("Failed to allocate memory for pui ani mgr !\n");
694                 goto err;
695         }
696
697         ani->ani_h = ani_h;
698         ani->id = id;
699         ani->cmd = PUI_ANI_CMD_NONE;
700         ani->repeat = 0;
701         ani->status = PUI_ANI_STATUS_INITIAL;
702         ani->ani_data = ani_data;
703
704         ani_h->ani = ani;
705
706         handle->ani_handles = eina_list_append(handle->ani_handles, ani_h);
707
708         return ani_h;
709
710 err:
711         if (ani_data)
712         {
713                 handle->backend_module_data->ani_destroy(ani_data);
714         }
715
716         if (ani_h)
717                 free(ani_h);
718
719         return NULL;
720 }
721
722 void
723 pui_ani_destroy(pui_ani_h ani_h)
724 {
725         pui_h handle = NULL;
726         pui_ani_t *ani = NULL;
727         pui_backend_module_data *backend_module_data = NULL;
728
729         if (!ani_h || !ani_h->pui_handle)
730                 return;
731
732         handle = ani_h->pui_handle;
733         ani = ani_h->ani;
734
735         /* stop the animation being played already if any */
736         if (ani->status == PUI_ANI_STATUS_STARTED || ani->status == PUI_ANI_STATUS_RUNNING)
737                 pui_ani_control(ani_h, PUI_ANI_CMD_STOP, 0);
738
739         backend_module_data = handle->backend_module_data;
740         backend_module_data->ani_destroy(ani->ani_data);
741         ani->ani_data = NULL;
742
743         if (ani->frame_cb_timer)
744         {
745                 ecore_timer_del(ani->frame_cb_timer);
746                 ani->frame_cb_timer = NULL;
747         }
748
749         free(ani_h->ani);
750
751         _pui_ani_event_handlers_shutdown(ani_h);
752
753         if (ani_h->frame_done_cb)
754         {
755                 ecore_wl2_window_frame_callback_del(ani_h->frame_done_cb);
756                 ani_h->frame_done_cb = NULL;
757         }
758
759         handle->ani_handles = eina_list_remove(handle->ani_handles, ani_h);
760
761         free(ani_h->id);
762         free(ani_h);
763 }
764