390bfab6cf92a0e48f7155d37a5238173d3f322f
[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 "default_backend.h"
27
28 pui_backend_ani_func *ani_func = NULL;
29 Eina_Hash *_animations_hash = NULL;
30 pui_backend_ani_data *g_ani_data = NULL;
31
32 #if 0
33 static double
34 _ani_backend_ease_function_get_intensity(pui_effect_func func, double interval)
35 {
36         double intensity = interval;
37         switch (func)
38         {
39                 case None:
40                         break;
41                 case Linear:
42                         break;
43                 case EaseInSine:
44                         intensity = 1 - cos(PI / 2 * interval);
45                         break;
46                 case EaseOutSine:
47                         intensity = sin(PI / 2 * interval);
48                         break;
49                 case EaseInQuart:
50                         intensity = interval * interval;
51                         break;
52                 case EaseOutQuart:
53                         intensity = interval * (2 - interval);
54                         break;
55                 default:
56                         break;
57         }
58
59         return intensity;
60 }
61
62 static unsigned int
63 _ani_backend_get_value(unsigned int end_frame, unsigned int start_frame, double interval)
64 {
65         double res = 0.0;
66
67         // interval: frame ratio between key frame to key frame
68         // end_frame and start_frame is key frame
69
70         res = (end_frame - start_frame) * interval + start_frame;
71
72         return res;
73 }
74 #endif
75
76 static pui_bool
77 _ani_backend_frame_cb(void *data, int serial)
78 {
79         pui_int_error e = PUI_INT_ERROR_NONE;
80         pui_ani_t *ani = (pui_ani_t *)data;
81         pui_backend_ani_data *ani_data = NULL;
82         pui_ani_control_buffer *buffer = NULL;
83         double now;
84
85         ani_data = pui_backend_ani_get_ani_data(ani);
86         default_ani_info *ani_info = (default_ani_info *)ani_data->ani_info;
87
88         now = ecore_time_unix_get();
89
90         pui_info("[time:%.3f] serial=%d\n", now, serial);
91
92         /* TODO : make use of ani_info */
93         (void) ani_info;
94
95         buffer = pui_backend_ani_get_buffer(ani);
96
97         if (!buffer)
98         {
99                 pui_err("Failed to get buffer !\n");
100                 return 0;
101         }
102
103         /* test example */
104         for(int i = 0; i<12; i++)
105         {
106                 buffer->ptr[4*i] = 0;
107                 buffer->ptr[4*i + 1] = (ani_info->frames[ani_info->frame_idx].leds[i].color & LED_MASK_RED) >> 16;//R
108                 buffer->ptr[4*i + 2] = (ani_info->frames[ani_info->frame_idx].leds[i].color & LED_MASK_GREEN) >> 8;//G
109                 buffer->ptr[4*i + 3] = ani_info->frames[ani_info->frame_idx].leds[i].color & LED_MASK_BLUE;//B
110         }
111
112         e = pui_backend_ani_set_buffer(ani, buffer);
113
114         if (e != PUI_INT_ERROR_NONE)
115         {
116                 pui_err("Failed on setting buffer on animation !(e=%d)\n", e);
117                 return (pui_bool)0;
118         }
119
120         e = pui_backend_ani_update(ani);
121
122         if (e != PUI_INT_ERROR_NONE)
123         {
124                 pui_err("Failed on updating animation !(e=%d)\n", e);
125                 return (pui_bool)0;
126         }
127
128         pui_info("... update (serial=%d)\n", serial);
129
130         return (pui_bool)1;
131 }
132
133 default_ani_info *
134 get_ani_info_from_ani_collection(pui_id id)
135 {
136         default_ani_info *ani_info = NULL;
137
138         if (!_animations_hash)
139                 return NULL;
140
141         pui_info("... id: %s\n", id);
142
143         ani_info = eina_hash_find(_animations_hash, id);
144
145         if (!ani_info)
146         {
147                 pui_err("ani_info has NOT been found ! (id:%s)\n", id);
148                 return NULL;
149         }
150
151         pui_info("ani_info has been found ! (id:%s)\n", id);
152
153         return ani_info;
154 }
155
156 pui_error
157 _ani_start(pui_ani_t *ani, int repeat)
158 {
159         pui_bool ret = 0;
160         pui_int_error e = PUI_INT_ERROR_NONE;
161         pui_backend_ani_data *ani_data = NULL;
162
163         ani_data = pui_backend_ani_get_ani_data(ani);
164         default_ani_info *ani_info = (default_ani_info *)ani_data->ani_info;
165
166         pui_info("... info->id: %s, repeat : %d\n", ani_info->id, repeat);
167
168         pui_backend_ani_status_update(ani, PUI_ANI_STATUS_STARTED);
169         ret = pui_backend_ani_add_frame_cb(ani, _ani_backend_frame_cb, (double)ani_info->interval / 1000);
170
171         if (!ret)
172         {
173                 pui_err("Failed to add frame callback !\n");
174                 e = PUI_INT_ERROR_INVALID_RESOURCES;
175         }
176
177         return e;
178 }
179
180 pui_error
181 _ani_stop(pui_ani_t *ani, pui_bool force)
182 {
183         pui_int_error e = PUI_INT_ERROR_NONE;
184         pui_backend_ani_data *ani_data = NULL;
185
186         ani_data = pui_backend_ani_get_ani_data(ani);
187         default_ani_info *info = (default_ani_info *)ani_data->ani_info;
188
189         //TODO
190         (void) info;
191
192         pui_info("... info->id: %s, force=%d\n", info->id, force);
193
194         pui_backend_ani_remove_frame_cb(ani);
195
196         if (force)
197                 pui_backend_ani_status_update(ani, PUI_ANI_STATUS_PAUSED);
198         else
199                 pui_backend_ani_status_update(ani, PUI_ANI_STATUS_STOPPED);
200
201         return e;
202 }
203
204 static char *
205 _read_json_file(const char *path, int *data_size)
206 {
207         FILE *fp = fopen(path, "rb");
208         unsigned int size;
209         char *buffer;
210         ERROR_CHECK(fp, return NULL, "Failed to open file: %s\n", path);
211
212         fseek(fp, 0, SEEK_END);
213         size = (unsigned int)ftell(fp);
214         fseek(fp, 0, SEEK_SET);
215
216         buffer = (char *)calloc(sizeof(char), size + 1);
217
218         if (fread(buffer, size, 1, fp) < 1) {
219                 goto error;
220         }
221
222         *data_size = size;
223         fclose(fp);
224         return buffer;
225
226 error:
227         *data_size = 0;
228         if (buffer) free(buffer);
229         fclose(fp);
230         return NULL;
231 }
232
233 static default_ani_info *
234 _read_json(const char *path)
235 {
236         char *buffer, *type;
237         json_object *root_obj, *data_obj, *frame_obj, *frame_data_obj, *led_obj, *led_data_obj;
238         default_ani_info *ani_info = NULL;
239         int frame_id = 0, frame_len = 0, led_id = 0, led_len = 0;
240         int data_size = 0, i, j;
241
242         buffer = _read_json_file(path, &data_size);
243         ERROR_CHECK(buffer && data_size > 0, goto error, "File %s has no data\n", path);
244         root_obj = json_tokener_parse(buffer);
245         ERROR_CHECK(root_obj, goto error, "Failed to tokenize json object\n");
246
247         ani_info = (default_ani_info *)calloc(1, sizeof(default_ani_info));
248         ERROR_CHECK(ani_info, goto error, "Failed to alloc for animation info\n");
249
250         data_obj = json_object_object_get(root_obj, "type");
251         //printf("type: %s\n", json_object_get_string(data_obj));
252         type = (char *)json_object_get_string(data_obj);
253         ani_info->id = strndup(type, strlen(type));
254
255         data_obj = json_object_object_get(root_obj, "interval");
256         //printf("interval: %d\n", json_object_get_int(data_obj));
257         ani_info->interval = json_object_get_int(data_obj);
258
259         data_obj = json_object_object_get(root_obj, "frame");
260         frame_len = json_object_array_length(data_obj);
261
262         ani_info->num_key_frames = frame_len;
263         ani_info->frames = (default_frame_info_t *)calloc(sizeof(default_frame_info_t), frame_len);
264         ERROR_CHECK(ani_info->frames, goto error, "Failed to alloc for default_frame_info_t\n");
265
266         for (i = 0; i < frame_len; i++) {
267                 frame_obj = json_object_array_get_idx(data_obj, i);
268
269                 frame_data_obj = json_object_object_get(frame_obj, "frame_id");
270                 //printf("\tframe id: %d\n", json_object_get_int(frame_data_obj));
271                 frame_id = json_object_get_int(frame_data_obj);
272
273                 frame_data_obj = json_object_object_get(frame_obj, "frame_duration");
274                 if (frame_data_obj)
275                         ani_info->frames[frame_id - 1].frame_duration = json_object_get_int(frame_data_obj);
276                 else
277                         ani_info->frames[frame_id - 1].frame_duration = ani_info->interval;
278
279                 frame_data_obj = json_object_object_get(frame_obj, "led");
280                 led_len = json_object_array_length(frame_data_obj);
281
282                 ani_info->frames[frame_id - 1].num_led = led_len;
283                 ani_info->frames[frame_id - 1].leds = (default_led_info_t *)calloc(sizeof(default_led_info_t), led_len);
284                 ERROR_CHECK(ani_info->frames[frame_id - 1].leds, goto error, "Failed to alloc for default_led_info_t\n");
285
286                 for (j = 0; j < led_len; j++) {
287                         led_obj = json_object_array_get_idx(frame_data_obj, j);
288
289                         led_data_obj = json_object_object_get(led_obj, "id");
290                         //printf("\t\tid: %2d  ", json_object_get_int(led_data_obj));
291                         led_id = json_object_get_int(led_data_obj);
292
293                         led_data_obj = json_object_object_get(led_obj, "color");
294                         //printf("color: %s\n", json_object_get_string(led_data_obj));
295                         ani_info->frames[frame_id - 1].leds[led_id - 1].color =
296                                 strtol(json_object_get_string(led_data_obj), NULL, 16);
297                 }
298         }
299
300         free(buffer);
301         return ani_info;
302
303 error:
304         if (buffer) free(buffer);
305         if (ani_info) {
306                 if (ani_info->frames) {
307                         for (i = 0; i < ani_info->num_key_frames; i++) {
308                                 if (ani_info->frames[i].leds)
309                                         free(ani_info->frames[i].leds);
310                         }
311                         free(ani_info->frames);
312                 }
313                 free(ani_info->id);
314                 free(ani_info);
315         }
316         return NULL;
317 }
318
319 int
320 _find_directory(const char *path, Eina_List **json_list)
321 {
322         DIR *dir;
323         struct dirent *cur;
324         int count = 0, ret_cnt = 0;
325
326         dir = opendir(path);
327         if (!dir)
328         {
329                 pui_err("Failed to open %s.\n", path);
330                 return count;
331         }
332
333         while ((cur = readdir(dir)) != NULL)
334         {
335                 char next_path[MAX_STR] = {0, };
336                 if (cur->d_type == DT_DIR)
337                 {
338                         if (!strncmp(cur->d_name, ".", sizeof(".")) ||
339                                 !strncmp(cur->d_name, "..", sizeof("..")))
340                                 continue;
341
342                         snprintf(next_path, MAX_STR, "%s/%s", path, cur->d_name);
343                         ret_cnt = _find_directory(next_path, json_list);
344                         count += ret_cnt;
345                 }
346                 else
347                 {
348                         if (strstr(cur->d_name, ".json"))
349                         {
350                                 snprintf(next_path, MAX_STR, "%s/%s", path, cur->d_name);
351                                 *json_list = eina_list_append(*json_list, eina_stringshare_add(next_path));
352                                 count++;
353                         }
354                 }
355         }
356         closedir(dir);
357
358         return count;
359 }
360
361
362 pui_int_error
363 _create_ani_collection(void)
364 {
365         pui_int_error e = PUI_INT_ERROR_NONE;
366         default_ani_info *ani_info;
367         Eina_List *json_list = NULL;
368         Eina_List *l, *l_next;
369         Eina_Stringshare *path;
370
371         // load and store all of animation data
372
373         _find_directory(ANI_COLLECTION_DIR, &json_list);
374
375         EINA_LIST_FOREACH_SAFE(json_list, l, l_next, path)
376         {
377                 ani_info = _read_json(path);
378                 if (ani_info) {
379                         eina_hash_add(_animations_hash, ani_info->id, ani_info);
380                         pui_info("Success to load %s animation (id: %s)\n", path, ani_info->id);
381                 }
382                 eina_stringshare_del(path);
383                 json_list = eina_list_remove_list(json_list, l);
384         }
385
386         //TODO
387
388         return e;
389 }
390
391 pui_bool
392 _geometry_get(int *width, int *height)
393 {
394         if (!width || !height)
395                 return 0;
396
397         if (width)
398                 *width = DEFAULT_BACKEND_GEOM_WIDTH;
399         if (height)
400                 *height = DEFAULT_BACKEND_GEOM_HEIGHT;
401
402         return 1;
403 }
404
405 pui_int_error
406 _is_ani_supported(pui_id id)
407 {
408         pui_int_error e = PUI_INT_ERROR_NONE;
409         default_ani_info *ani_info;
410
411         //TODO
412         /* if the given id is not supported, return PUI_INT_ERROR_ID_NOT_SUPPORTED. */
413         if (!id) {
414                 e = PUI_INT_ERROR_ID_NOT_SUPPORTED;
415                 return e;
416         }
417         ani_info = eina_hash_find(_animations_hash, id);
418
419         if (!ani_info)
420                 e = PUI_INT_ERROR_ID_NOT_SUPPORTED;
421
422         return e;
423 }
424
425 static void
426 _ani_info_cleanup(default_ani_info *ani_info)
427 {
428         int i;
429
430         if (!ani_info)
431                 return;
432
433         if (ani_info->frames)
434         {
435                 for (i = 0; i < ani_info->num_key_frames; i++)
436                 {
437                         if (ani_info->frames[i].leds)
438                                 free(ani_info->frames[i].leds);
439                 }
440
441                 free(ani_info->frames);
442         }
443
444         free(ani_info->id);
445 }
446
447 pui_backend_ani_data *
448 _ani_create(pui_id id)
449 {
450         pui_backend_ani_data *ani_data = NULL;
451         pui_backend_ani_func *ani_func = NULL;
452
453         /* backend's animation specific info */
454         default_ani_info *ani_info = NULL;
455
456         //TODO : return NULL if the animation correspond to the given id dones't exist.
457         if (PUI_INT_ERROR_NONE != _is_ani_supported(id))
458         {
459                 pui_err("The animation(%s) doesn't supported !\n", id);
460                 return NULL;
461         }
462
463         /* allocation of the structure of function pointers that will be called from pui_ani_control() */
464         ani_func = pui_backend_ani_alloc_ani_func();
465
466         if (!ani_func)
467         {
468                 pui_err("Failed to allocate memory ! (pui backend ani func)\n");
469                 return NULL;
470         }
471
472         /* get animation info associate with the given id from animation collection */
473         ani_info = get_ani_info_from_ani_collection(id);
474
475         if (!ani_info)
476         {
477                 pui_err("Failed to get ani info from animation collection !\n");
478                 goto err;
479         }
480
481         /* allocate backend ani_data and return it to pui ani core */
482         ani_data = (pui_backend_ani_data *)calloc(1, sizeof(pui_backend_ani_data));
483
484         if (!ani_data)
485         {
486                 pui_err("Failed to allocate memory for pui backend ani data !\n");
487                 goto err;
488         }
489
490         /* Assign each function pointer that corresponds to the given id if needed. */
491         if (!strncmp(ani_info->id, "system/easy_setup", sizeof("system/easy_setup")))
492         {
493                 pui_default_backend_ani_easysetup_func_set(ani_func);
494         }
495         else if (!strncmp(ani_info->id, "system/processing", sizeof("system/processing")))
496         {
497                 pui_default_backend_ani_system_processing_func_set(ani_func);
498         }
499         else if (!strncmp(ani_info->id, "system/sw_update_done", sizeof("system/sw_update_done")))
500         {
501                 pui_default_backend_ani_swupdatedone_func_set(ani_func);
502         }
503         else if (!strncmp(ani_info->id, "system/mic_off", sizeof("system/mic_off")))
504         {
505                 pui_default_backend_ani_micoff_func_set(ani_func);
506         }
507         else if (!strncmp(ani_info->id, "voice/listening", sizeof("voice/listening")))
508         {
509                 pui_default_backend_ani_listening_func_set(ani_func);
510         }
511         else if (!strncmp(ani_info->id, "voice/streaming", sizeof("voice/streaming")))
512         {
513                 pui_default_backend_ani_streaming_func_set(ani_func);
514         }
515         else if (!strncmp(ani_info->id, "voice/processing", sizeof("voice/processing")))
516         {
517                 pui_default_backend_ani_processing_func_set(ani_func);
518         }
519         else if (!strncmp(ani_info->id, "voice/speaking", sizeof("voice/speaking")))
520         {
521                 pui_default_backend_ani_speaking_func_set(ani_func);
522         }
523         else if (!strncmp(ani_info->id, "voice/timeout", sizeof("voice/timeout")))
524         {
525                 pui_default_backend_ani_timeout_func_set(ani_func);
526         }
527         else if (!strncmp(ani_info->id, "notification/normal", sizeof("notification/normal")))
528         {
529                 pui_default_backend_ani_normal_func_set(ani_func);
530         }
531         else if (!strncmp(ani_info->id, "notification/emergency", sizeof("notification/emergency")))
532         {
533                 pui_default_backend_ani_emergency_func_set(ani_func);
534         }
535         else if (!strncmp(ani_info->id, "notification/network_error", sizeof("notification/network_error")))
536         {
537                 pui_default_backend_ani_networkerror_func_set(ani_func);
538         }
539         else if (!strncmp(ani_info->id, "notification/error", sizeof("notification/error")))
540         {
541                 pui_default_backend_ani_error_func_set(ani_func);
542         }
543         else if (!strncmp(ani_info->id, "notification/alarm", sizeof("notification/alarm")))
544         {
545                 pui_default_backend_ani_alarm_func_set(ani_func);
546         }
547         else if (!strncmp(ani_info->id, "bt/pairing", sizeof("bt/pairing")))
548         {
549                 pui_default_backend_ani_pairing_func_set(ani_func);
550         }
551         else if (!strncmp(ani_info->id, "bt/connected", sizeof("bt/connected")))
552         {
553                 pui_default_backend_ani_connected_func_set(ani_func);
554         }
555         else
556         {
557                 pui_info("%s animation has no animation handler, using default handler\n", ani_info->id);
558                 /* Assign each function pointer that corresponds to the given id if needed. */
559                 ani_func->ani_start = _ani_start;
560                 ani_func->ani_stop = _ani_stop;
561         }
562
563         ani_data->ani_func = ani_func;
564         ani_data->ani_info = (pui_backend_ani_info *)ani_info;
565
566         g_ani_data = ani_data;
567
568         return ani_data;
569
570 err:
571         if (ani_func)
572         {
573                 pui_backend_ani_free_ani_func(ani_func);
574                 ani_func = NULL;
575         }
576
577         return NULL;
578 }
579
580 void
581 _ani_destroy(pui_backend_ani_data *ani_data)
582 {
583         if (!ani_data)
584                 return;
585
586         if (ani_data->ani_func)
587         {
588                 pui_backend_ani_free_ani_func(ani_data->ani_func);
589                 ani_data->ani_func = NULL;
590         }
591
592         ani_data->ani_info = NULL;
593         g_ani_data = NULL;
594 }
595
596 static void
597 _animation_data_free_cb(void *data)
598 {
599         default_ani_info *ani_info = (default_ani_info *)data;
600
601         _ani_info_cleanup(ani_info);
602 }
603
604 static pui_backend_module_data *
605 pui_default_backend_init(void)
606 {
607         pui_backend_module_data *backend_data = NULL;
608
609         backend_data = (pui_backend_module_data *)calloc(1, sizeof(pui_backend_module_data));
610
611         if (!backend_data)
612         {
613                 pui_err("Failed to allocate memory for pui backend module data !\n");
614                 return NULL;
615         }
616
617         backend_data->create_ani_collection = _create_ani_collection;
618         backend_data->geometry_get = _geometry_get;
619         backend_data->ani_create = _ani_create;
620         backend_data->ani_destroy = _ani_destroy;
621
622         /* Allocate backend specific data if needed. Now it will be empty. */
623         backend_data->data = NULL;
624         _animations_hash = eina_hash_string_superfast_new(NULL);
625
626         return backend_data;
627 }
628
629 static void
630 pui_default_backend_deinit(pui_backend_module_data *backend_data)
631 {
632         Eina_Iterator *it;
633         default_ani_info *ani_info = NULL;
634
635         if (!backend_data)
636                 return;
637
638         if (backend_data->data)
639         {
640                 //TODO : free variables of backend_data
641
642                 free(backend_data->data);
643         }
644
645         if (g_ani_data)
646         {
647                 if (g_ani_data->ani_func)
648                 {
649                         pui_backend_ani_free_ani_func(g_ani_data->ani_func);
650                         g_ani_data->ani_func = NULL;
651                 }
652
653                 g_ani_data->ani_info = NULL;
654         }
655
656         if (_animations_hash)
657         {
658                 it = eina_hash_iterator_data_new(_animations_hash);
659
660                 EINA_ITERATOR_FOREACH(it, ani_info)
661                 {
662                         _animation_data_free_cb(ani_info);
663                         free(ani_info);
664                 }
665
666                 eina_iterator_free(it);
667
668                 eina_hash_free(_animations_hash);
669                 _animations_hash = NULL;
670         }
671
672         backend_data->create_ani_collection = NULL;
673         backend_data->geometry_get = NULL;
674         backend_data->ani_create = NULL;
675         backend_data->ani_destroy = NULL;
676
677         free(backend_data);
678         backend_data = NULL;
679 }
680
681 pui_backend_module pui_backend_module_info = {
682         "Tizen Reference Speaker Backend",
683         "Samsung",
684         PUI_BACKEND_SET_ABI_VERSION(1, 0),
685         pui_default_backend_init,
686         pui_default_backend_deinit
687 };
688