default_backend: leave timestamp inside frame callback and make use of ani_info for...
[platform/core/uifw/libpui.git] / backends / default_backend.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 <stdio.h>
27 #include <stdlib.h>
28 #include <PUI_backend.h>
29
30 #include <json-c/json.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <Eina.h>
34 #include <Ecore.h>
35 #include <config.h>
36
37 #define ANI_COLLECTION_DIR "/usr/share/pui/"
38 #define MAX_STR 1024
39
40 #define ERROR_CHECK(exp, action, fmt, ...) \
41         do { \
42                 if (!(exp)) \
43                 { \
44                         printf(fmt, ##__VA_ARGS__);     \
45                         action; \
46                 } \
47         } while (0)
48
49 pui_backend_ani_func *ani_func = NULL;
50 Eina_Hash *_animations_hash = NULL;
51
52 typedef enum
53 {
54         None,
55         Linear,
56         EaseInSine,
57         EaseOutSine,
58         EaseInQuart,
59         EaseOutQuart
60 } pui_effect_func;
61
62 typedef struct _default_ani_info default_ani_info;
63 typedef struct _default_frame_info_t default_frame_info_t;
64 typedef struct _default_led_info_t default_led_info_t;
65
66 struct _default_ani_info
67 {
68         pui_id id;
69         pui_ani_status status;
70         pui_ani_control_buffer *buffer;
71         unsigned int repeat;
72
73         unsigned int key_frame_idx;
74         unsigned int num_key_frames;
75         default_frame_info_t *frames;
76         int interval;
77         pui_effect_func effect_func;
78 };
79
80 struct _default_frame_info_t
81 {
82         default_led_info_t *leds;
83         int num_led;
84 };
85
86 struct _default_led_info_t
87 {
88         unsigned int color;
89 };
90
91 pui_backend_ani_data *g_ani_data = NULL;
92
93 static pui_bool
94 _ani_backend_frame_cb(void *data, int serial)
95 {
96         pui_int_error e = PUI_INT_ERROR_NONE;
97         pui_ani_t *ani = (pui_ani_t *)data;
98         pui_backend_ani_data *ani_data = NULL;
99         pui_ani_control_buffer *buffer = NULL;
100         double now;
101
102         ani_data = pui_backend_ani_get_ani_data(ani);
103         default_ani_info *ani_info = (default_ani_info *)ani_data->ani_info;
104
105         now = ecore_time_unix_get();
106
107         pui_info("[time:%.3f] serial=%d\n", now, serial);
108
109         /* TODO : make use of ani_info */
110         (void) ani_info;
111
112         buffer = pui_backend_ani_get_buffer(ani);
113
114         if (!buffer)
115         {
116                 pui_err("Failed to get buffer !\n");
117                 return 0;
118         }
119
120         /* test example */
121         for(int i = 0; i<12; i++)
122         {
123                 buffer->ptr[4*i] = 0;
124                 buffer->ptr[4*i + 1] = (ani_info->frames[ani_info->key_frame_idx].leds[i].color & 0xFF0000) >> 16;//R
125                 buffer->ptr[4*i + 2] = (ani_info->frames[ani_info->key_frame_idx].leds[i].color & 0x00FF00) >> 8;//G
126                 buffer->ptr[4*i + 3] = ani_info->frames[ani_info->key_frame_idx].leds[i].color & 0x0000FF;//B
127         }
128
129         e = pui_backend_ani_set_buffer(ani, buffer);
130
131         if (e != PUI_INT_ERROR_NONE)
132         {
133                 pui_err("Failed on setting buffer on animation !(e=%d)\n", e);
134                 return (pui_bool)0;
135         }
136
137         e = pui_backend_ani_update(ani);
138
139         if (e != PUI_INT_ERROR_NONE)
140         {
141                 pui_err("Failed on updating animation !(e=%d)\n", e);
142                 return (pui_bool)0;
143         }
144
145         pui_info("... update (serial=%d)\n", serial);
146
147         return (pui_bool)1;
148 }
149
150 default_ani_info *
151 get_ani_info_from_ani_collection(pui_id id)
152 {
153         default_ani_info *ani_info = NULL;
154
155         if (!_animations_hash)
156                 return NULL;
157
158         pui_info("... id: %s\n", id);
159
160         ani_info = eina_hash_find(_animations_hash, id);
161
162         if (!ani_info)
163         {
164                 pui_err("ani_info has NOT been found ! (id:%s)\n", id);
165                 return NULL;
166         }
167
168         pui_info("ani_info has been found ! (id:%s)\n", id);
169
170         return ani_info;
171 }
172
173 pui_error
174 _ani_start(pui_ani_t *ani, int repeat)
175 {
176         pui_bool ret = 0;
177         pui_int_error e = PUI_INT_ERROR_NONE;
178         pui_backend_ani_data *ani_data = NULL;
179
180         ani_data = pui_backend_ani_get_ani_data(ani);
181         default_ani_info *ani_info = (default_ani_info *)ani_data->ani_info;
182
183         pui_info("... info->id: %s, repeat : %d\n", ani_info->id, repeat);
184
185         pui_backend_ani_status_update(ani, PUI_ANI_STATUS_STARTED);
186         ret = pui_backend_ani_add_frame_cb(ani, _ani_backend_frame_cb, (double)ani_info->interval / 1000);
187
188         if (!ret)
189         {
190                 pui_err("Failed to add frame callback !\n");
191                 e = PUI_INT_ERROR_INVALID_RESOURCES;
192         }
193
194         return e;
195 }
196
197 pui_error
198 _ani_stop(pui_ani_t *ani)
199 {
200         pui_int_error e = PUI_INT_ERROR_NONE;
201         pui_backend_ani_data *ani_data = NULL;
202
203         ani_data = pui_backend_ani_get_ani_data(ani);
204         default_ani_info *info = (default_ani_info *)ani_data->ani_info;
205
206         //TODO
207         (void) info;
208
209         pui_info("... info->id: %s\n", info->id);
210
211         pui_backend_ani_remove_frame_cb(ani);
212         pui_backend_ani_status_update(ani, PUI_ANI_STATUS_STOPPED);
213
214         return e;
215 }
216
217 static char *
218 _read_json_file(const char *path, int *data_size)
219 {
220         FILE *fp = fopen(path, "rb");
221         int size;
222         char *buffer;
223         ERROR_CHECK(fp, return NULL, "Failed to open file: %s\n", path);
224
225         fseek(fp, 0, SEEK_END);
226         size = ftell(fp);
227         fseek(fp, 0, SEEK_SET);
228
229         buffer = (char *)calloc(sizeof(char), size + 1);
230
231         if (fread(buffer, size, 1, fp) < 1) {
232                 *data_size = 0;
233                 free(buffer);
234                 fclose(fp);
235                 return NULL;
236         }
237
238         *data_size = size;
239         fclose(fp);
240         return buffer;
241 }
242
243 static default_ani_info *
244 _read_json(const char *path)
245 {
246         char *buffer, *type;
247         json_object *root_obj, *data_obj, *frame_obj, *frame_data_obj, *led_obj, *led_data_obj;
248         default_ani_info *ani_info = NULL;
249         int frame_id = 0, frame_len = 0, led_id = 0, led_len = 0;
250         int data_size = 0, i, j;
251
252         buffer = _read_json_file(path, &data_size);
253         ERROR_CHECK(buffer && data_size > 0, return EINA_FALSE, "File %s has no data\n", path);
254         root_obj = json_tokener_parse(buffer);
255         ERROR_CHECK(root_obj, goto error, "Failed to tokenize json object\n");
256
257         ani_info = (default_ani_info *)calloc(1, sizeof(default_ani_info));
258         ERROR_CHECK(ani_info, goto error, "Failed to alloc for animation info\n");
259
260         data_obj = json_object_object_get(root_obj, "type");
261         //printf("type: %s\n", json_object_get_string(data_obj));
262         type = (char *)json_object_get_string(data_obj);
263         ani_info->id = strndup(type, strlen(type));
264
265         data_obj = json_object_object_get(root_obj, "interval");
266         //printf("interval: %d\n", json_object_get_int(data_obj));
267         ani_info->interval = json_object_get_int(data_obj);
268
269         data_obj = json_object_object_get(root_obj, "frame");
270         frame_len = json_object_array_length(data_obj);
271
272         ani_info->num_key_frames = frame_len;
273         ani_info->frames = (default_frame_info_t *)calloc(sizeof(default_frame_info_t), frame_len);
274         ERROR_CHECK(ani_info->frames, goto error, "Failed to alloc for default_frame_info_t\n");
275         
276         for (i = 0; i < frame_len; i++) {
277                 frame_obj = json_object_array_get_idx(data_obj, i);
278
279                 frame_data_obj = json_object_object_get(frame_obj, "frame_id");
280                 //printf("\tframe id: %d\n", json_object_get_int(frame_data_obj));
281                 frame_id = json_object_get_int(frame_data_obj);
282
283                 frame_data_obj = json_object_object_get(frame_obj, "led");
284                 led_len = json_object_array_length(frame_data_obj);
285                 
286                 ani_info->frames[frame_id - 1].num_led = led_len;
287                 ani_info->frames[frame_id - 1].leds = (default_led_info_t *)calloc(sizeof(default_led_info_t), led_len);
288                 ERROR_CHECK(ani_info->frames[frame_id - 1].leds, goto error, "Failed to alloc for default_led_info_t\n");
289                 
290                 for (j = 0; j < led_len; j++) {
291                         led_obj = json_object_array_get_idx(frame_data_obj, j);
292
293                         led_data_obj = json_object_object_get(led_obj, "id");
294                         //printf("\t\tid: %2d  ", json_object_get_int(led_data_obj));
295                         led_id = json_object_get_int(led_data_obj);
296
297                         led_data_obj = json_object_object_get(led_obj, "color");
298                         //printf("color: %s\n", json_object_get_string(led_data_obj));
299                         ani_info->frames[frame_id - 1].leds[led_id - 1].color =
300                                 strtol(json_object_get_string(led_data_obj), NULL, 16);
301                 }
302         }
303
304         free(buffer);
305         return ani_info;
306
307 error:
308         free(buffer);
309         if (ani_info) {
310                 if (ani_info->frames) {
311                         for (i = 0; i < ani_info->num_key_frames; i++) {
312                                 if (ani_info->frames[i].leds)
313                                         free(ani_info->frames[i].leds);
314                         }
315                         free(ani_info->frames);
316                 }
317                 free(ani_info->id);
318                 free(ani_info);
319         }
320         return NULL;
321 }
322
323 static int
324 _scandir_filter(const struct dirent *dirent)
325 {
326         if (!strncmp(dirent->d_name, ".", sizeof(".")) ||
327                 !strncmp(dirent->d_name, "..", sizeof("..")))
328                 return 0;
329         if (!strstr(dirent->d_name, ".json"))
330                 return 0;
331
332         return 1;
333 }
334
335
336 pui_int_error
337 _create_ani_collection(void)
338 {
339         pui_int_error e = PUI_INT_ERROR_NONE;
340         default_ani_info *ani_info;
341         struct dirent **files;
342         int count, i;
343         char file_path[MAX_STR] = {0, };
344
345         // load and store all of animation data
346
347         if ((count = scandir(ANI_COLLECTION_DIR, &files, _scandir_filter, alphasort)) == -1) {
348                 printf("Failed to get %s directory (%s)\n", ANI_COLLECTION_DIR, strerror(errno));
349                 e = PUI_INT_ERROR_INVALID_RESOURCES;
350                 return e;
351         }
352
353         for (i = 0; i < count; i++) {
354                 snprintf(file_path, sizeof(file_path), "%s%s", ANI_COLLECTION_DIR, files[i]->d_name);
355
356                 ani_info = _read_json(file_path);
357                 if (ani_info) {
358                         eina_hash_add(_animations_hash, ani_info->id, ani_info);
359                         printf("Success to load %s animation\n", files[i]->d_name);
360                 }
361         }
362
363         for (i = 0; i < count; i++) {
364                 free(files[i]);
365         }
366         free(files);
367
368         //TODO
369
370         return e;
371 }
372
373 pui_int_error
374 _is_ani_supported(pui_id id)
375 {
376         pui_int_error e = PUI_INT_ERROR_NONE;
377         default_ani_info *ani_info;
378
379         //TODO
380         /* if the given id is not supported, return PUI_INT_ERROR_ID_NOT_SUPPORTED. */
381         if (!id) {
382                 e = PUI_INT_ERROR_ID_NOT_SUPPORTED;
383                 return e;
384         }
385         ani_info = eina_hash_find(_animations_hash, id);
386
387         if (!ani_info)
388                 e = PUI_INT_ERROR_ID_NOT_SUPPORTED;
389
390         return e;
391 }
392
393 static void
394 _ani_info_cleanup(default_ani_info *ani_info)
395 {
396         int i;
397
398         if (!ani_info)
399                 return;
400
401         if (ani_info->frames)
402         {
403                 for (i = 0; i < ani_info->num_key_frames; i++)
404                 {
405                         if (ani_info->frames[i].leds)
406                                 free(ani_info->frames[i].leds);
407                 }
408
409                 free(ani_info->frames);
410         }
411
412         free(ani_info->id);
413 }
414
415 pui_backend_ani_data *
416 _ani_create(pui_id id)
417 {
418         pui_backend_ani_data *ani_data = NULL;
419         pui_backend_ani_func *ani_func = NULL;
420
421         /* backend's animation specific info */
422         default_ani_info *ani_info = NULL;
423
424         //TODO : return NULL if the animation correspond to the given id dones't exist.
425         if (PUI_INT_ERROR_NONE != _is_ani_supported(id))
426         {
427                 pui_err("The animation(%s) doesn't supported !\n", id);
428                 return NULL;
429         }
430
431         /* allocation of the structure of function pointers that will be called from pui_ani_control() */
432         ani_func = pui_backend_ani_alloc_ani_func();
433
434         if (!ani_func)
435         {
436                 pui_err("Failed to allocate memory ! (pui backend ani func)\n");
437                 return NULL;
438         }
439
440         /* Assign each function pointer that corresponds to the given id if needed. */
441         ani_func->ani_start = _ani_start;
442         ani_func->ani_stop = _ani_stop;
443
444         /* get animation info associate with the given id from animation collection */
445         ani_info = get_ani_info_from_ani_collection(id);
446         
447         if (!ani_info)
448         {
449                 pui_err("Failed to get ani info from animation collection !\n");
450                 goto err;
451         }
452
453         /* allocate backend ani_data and return it to pui ani core */
454         ani_data = (pui_backend_ani_data *)calloc(1, sizeof(pui_backend_ani_data));
455         
456         if (!ani_data)
457         {
458                 pui_err("Failed to allocate memory for pui backend ani data !\n");
459                 goto err;
460         }
461         
462         ani_data->ani_func = ani_func;
463         ani_data->ani_info = (pui_backend_ani_info *)ani_info;
464
465         g_ani_data = ani_data;
466
467         return ani_data;
468
469 err:
470         if (ani_func)
471         {
472                 pui_backend_ani_free_ani_func(ani_func);
473                 ani_func = NULL;
474         }
475
476         return NULL;
477 }
478
479 void
480 _ani_destroy(pui_backend_ani_data *ani_data)
481 {
482         if (!ani_data)
483                 return;
484
485         if (ani_data->ani_func)
486         {
487                 pui_backend_ani_free_ani_func(ani_data->ani_func);
488                 ani_data->ani_func = NULL;
489         }
490
491         ani_data->ani_info = NULL;
492         g_ani_data = NULL;
493 }
494
495 static void
496 _animation_data_free_cb(void *data)
497 {
498         default_ani_info *ani_info = (default_ani_info *)data;
499
500         _ani_info_cleanup(ani_info);
501 }
502
503 static pui_backend_module_data *
504 pui_default_backend_init(void)
505 {
506         pui_backend_module_data *backend_data = NULL;
507
508         backend_data = (pui_backend_module_data *)calloc(1, sizeof(pui_backend_module_data));
509
510         if (!backend_data)
511         {
512                 pui_err("Failed to allocate memory for pui backend module data !\n");
513                 return NULL;
514         }
515
516         backend_data->create_ani_collection = _create_ani_collection;
517         backend_data->ani_create = _ani_create;
518         backend_data->ani_destroy = _ani_destroy;
519
520         /* Allocate backend specific data if needed. Now it will be empty. */
521         backend_data->data = NULL;
522         _animations_hash = eina_hash_string_superfast_new(NULL);
523
524         return backend_data;
525 }
526
527 static void
528 pui_default_backend_deinit(pui_backend_module_data *backend_data)
529 {
530         Eina_Iterator *it;
531         default_ani_info *ani_info = NULL;
532
533         if (!backend_data)
534                 return;
535
536         if (backend_data->data)
537         {
538                 //TODO : free variables of backend_data
539
540                 free(backend_data->data);
541         }
542
543         if (g_ani_data)
544         {
545                 if (g_ani_data->ani_func)
546                 {
547                         pui_backend_ani_free_ani_func(g_ani_data->ani_func);
548                         g_ani_data->ani_func = NULL;
549                 }
550
551                 g_ani_data->ani_info = NULL;
552         }
553
554         if (_animations_hash)
555         {
556                 it = eina_hash_iterator_data_new(_animations_hash);
557
558                 EINA_ITERATOR_FOREACH(it, ani_info)
559                 {
560                         _animation_data_free_cb(ani_info);
561                         free(ani_info);
562                 }
563
564                 eina_iterator_free(it);
565
566                 eina_hash_free(_animations_hash);
567                 _animations_hash = NULL;
568         }
569
570         backend_data->create_ani_collection = NULL;
571         backend_data->ani_create = NULL;
572         backend_data->ani_destroy = NULL;
573
574         free(backend_data);
575         backend_data = NULL;
576 }
577
578 pui_backend_module pui_backend_module_info = {
579         "Tizen Reference Speaker Backend",
580         "Samsung",
581         PUI_BACKEND_SET_ABI_VERSION(1, 0),
582         pui_default_backend_init,
583         pui_default_backend_deinit
584 };
585