Sync with the latest tizen 2.x
[apps/native/widget/widget.git] / src / binder.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #define __USE_GNU
4 #include <dlfcn.h>
5 #define _GNU_SOURCE
6 #include <string.h>
7
8 #include <Elementary.h>
9 #include <Ecore_Evas.h>
10
11 #include <widget_errno.h>
12 #include <widget_service.h>
13 #include <widget_service_internal.h>
14 #include <widget_provider.h>
15 #include <widget_provider_buffer.h>
16 #include <widget_conf.h>
17
18 #include <dlog.h>
19
20 #include "widget.h"
21 #include "widget_internal.h"
22 #include "binder.h"
23 #include "debug.h"
24 #include "util.h"
25
26 #define PUBLIC __attribute__((visibility("default")))
27
28 /**
29  * @brief These functions are defined in the data-provider-slave
30  */
31 static struct info {
32         const char *(*find_pkgname)(const char *filename);
33         int (*request_update_by_id)(const char *uri);
34         int (*trigger_update_monitor)(const char *id, int is_gbar);
35         int (*update_extra_info)(const char *id, const char *content, const char *title, const char *icon, const char *name);
36         int (*add_pre_callback)(widget_pre_callback_e type, widget_pre_callback_t cb, void *data);
37         int (*del_pre_callback)(widget_pre_callback_e type, widget_pre_callback_t cb, void *data);
38         int (*orientation)(const char *id);
39         int (*get_last_ctrl_mode)(const char *id, int *cmd, int *value);
40         Ecore_Evas *(*alloc_canvas)(int w, int h, void *(*a)(void *data, int size), void (*f)(void *data, void *ptr), void *data);
41         Ecore_Evas *(*alloc_canvas_with_stride)(int w, int h, void *(*a)(void *data, int size, int *stride, int *bpp), void (*f)(void *data, void *ptr), void *data);
42         Ecore_Evas *(*alloc_canvas_with_pixmap)(const char *disp_name, Ecore_X_Window parent, int x, int y, int w, int h, Ecore_X_Pixmap (*alloc_cb)(void *data, Ecore_X_Window parent, int w, int h, int depth), void (*free_cb)(void *data, Ecore_X_Pixmap pixmap), void *data);
43
44         enum load_type {
45                 LOAD_TYPE_UNKNOWN = -1,
46                 LOAD_TYPE_SLAVE   = 0,
47                 LOAD_TYPE_APP     = 1
48         } type;
49
50         union _updated {
51                 struct _slave {
52                         int (*send)(const char *pkgname, const char *id, widget_buffer_h handle, int idx, int x, int y, int w, int h, int gbar, const char *descfile);
53                 } slave;
54
55                 struct _app {
56                         int (*send)(widget_buffer_h handle, int idx, int x, int y, int w, int h, int gbar);
57                 } app;
58         } binder;
59 } s_info = {
60         .find_pkgname = NULL,
61         .request_update_by_id = NULL,
62         .trigger_update_monitor = NULL,
63         .update_extra_info = NULL,
64         .add_pre_callback = NULL,
65         .del_pre_callback = NULL,
66         .orientation = NULL,
67         .get_last_ctrl_mode = NULL,
68         .alloc_canvas = NULL,
69         .alloc_canvas_with_stride = NULL,
70         .alloc_canvas_with_pixmap = NULL,
71         .type = LOAD_TYPE_UNKNOWN,    /* Not initialized */
72         .binder = {
73                 .slave = {
74                         .send = NULL,
75                 },
76                 .app = {
77                         .send = NULL,
78                 },
79         },
80 };
81
82 #define FUNC_PREFIX                               "widget_"
83 #define FUNC_WIDGET_SEND_UPDATED                  FUNC_PREFIX "send_buffer_updated"
84 #define FUNC_WIDGET_FIND_PKGNAME                  FUNC_PREFIX "find_pkgname"
85 #define FUNC_WIDGET_REQUEST_UPDATE_BY_ID          FUNC_PREFIX "request_update_by_id"
86 #define FUNC_WIDGET_TRIGGER_UPDATE_MONITOR        FUNC_PREFIX "trigger_update_monitor"
87 #define FUNC_WIDGET_UPDATE_EXTRA_INFO             FUNC_PREFIX "update_extra_info"
88
89 #define FUNC_WIDGET_PROVIDER_APP_UPDATED          FUNC_PREFIX "provider_app_send_buffer_updated_event"
90
91 #define FUNC_WIDGET_PROVIDER_APP_ADD_PRE_CALLBACK FUNC_PREFIX "provider_app_add_pre_callback"
92 #define FUNC_WIDGET_PROVIDER_APP_DEL_PRE_CALLBACK FUNC_PREFIX "provider_app_del_pre_callback"
93 #define FUNC_WIDGET_PROVIDER_APP_ORIENTATION      FUNC_PREFIX "provider_app_get_orientation"
94 #define FUNC_WIDGET_PROVIDER_APP_LAST_CTRL_MODE   FUNC_PREFIX "provider_app_get_last_ctrl_mode"
95
96 static inline void load_ecore_evas_function(void)
97 {
98         if (!s_info.alloc_canvas && !s_info.alloc_canvas_with_stride && !s_info.alloc_canvas_with_pixmap) {
99                 s_info.alloc_canvas_with_pixmap = dlsym(RTLD_DEFAULT, "ecore_evas_gl_x11_pixmap_allocfunc_new");
100                 if (!s_info.alloc_canvas_with_pixmap) {
101                         DbgPrint("pixmap_allocfunc_new is not found\n");
102                 }
103
104                 s_info.alloc_canvas_with_stride = dlsym(RTLD_DEFAULT, "ecore_evas_buffer_allocfunc_with_stride_new");
105                 if (!s_info.alloc_canvas_with_stride) {
106                         DbgPrint("allocfunc_with_stirde_new is not found\n");
107                 }
108
109                 s_info.alloc_canvas = dlsym(RTLD_DEFAULT, "ecore_evas_buffer_allocfunc_new");
110                 if (!s_info.alloc_canvas) {
111                         ErrPrint("allocfunc_new is not found\n");
112                 }
113
114                 if (!s_info.alloc_canvas_with_stride && !s_info.alloc_canvas && !s_info.alloc_canvas_with_pixmap) {
115                         ErrPrint("No way to allocate canvas\n");
116                 }
117         }
118 }
119
120 static inline void load_update_function(void)
121 {
122         /* Must to be checked the slave function first. */
123         s_info.binder.slave.send = dlsym(RTLD_DEFAULT, FUNC_WIDGET_SEND_UPDATED);
124         if (s_info.binder.slave.send) {
125                 s_info.type = LOAD_TYPE_SLAVE;
126                 DbgPrint("Slave detected\n");
127         } else {
128                 s_info.binder.app.send = dlsym(RTLD_DEFAULT, FUNC_WIDGET_PROVIDER_APP_UPDATED);
129                 if (s_info.binder.app.send) {
130                         s_info.type = LOAD_TYPE_APP;
131                         DbgPrint("App detected\n");
132                 }
133         }
134 }
135
136 PUBLIC int widget_get_last_ctrl_mode(const char *id, int *cmd, int *value)
137 {
138         if (!s_info.get_last_ctrl_mode) {
139                 s_info.get_last_ctrl_mode = dlsym(RTLD_DEFAULT, FUNC_WIDGET_PROVIDER_APP_LAST_CTRL_MODE);
140                 if (!s_info.get_last_ctrl_mode) {
141                         return WIDGET_ERROR_NOT_SUPPORTED;
142                 }
143         }
144
145         return s_info.get_last_ctrl_mode(id, cmd, value);
146 }
147
148 PUBLIC int widget_get_orientation(const char *id)
149 {
150         if (!s_info.orientation) {
151                 s_info.orientation = dlsym(RTLD_DEFAULT, FUNC_WIDGET_PROVIDER_APP_ORIENTATION);
152                 if (!s_info.orientation) {
153                         return WIDGET_ERROR_NOT_SUPPORTED;
154                 }
155         }
156
157         return s_info.orientation(id);
158 }
159
160 PUBLIC int widget_add_pre_callback(widget_pre_callback_e type, widget_pre_callback_t cb, void *data)
161 {
162         if (!s_info.add_pre_callback) {
163                 s_info.add_pre_callback = dlsym(RTLD_DEFAULT, FUNC_WIDGET_PROVIDER_APP_ADD_PRE_CALLBACK);
164                 if (!s_info.add_pre_callback) {
165                         return WIDGET_ERROR_NOT_SUPPORTED;
166                 }
167         }
168
169         return s_info.add_pre_callback(type, cb, data);
170 }
171
172 PUBLIC int widget_del_pre_callback(widget_pre_callback_e type, widget_pre_callback_t cb, void *data)
173 {
174         if (!s_info.del_pre_callback) {
175                 s_info.del_pre_callback = dlsym(RTLD_DEFAULT, FUNC_WIDGET_PROVIDER_APP_DEL_PRE_CALLBACK);
176                 if (!s_info.del_pre_callback) {
177                         return WIDGET_ERROR_NOT_SUPPORTED;
178                 }
179         }
180
181         return s_info.del_pre_callback(type, cb, data);
182 }
183
184 int binder_widget_send_updated(const char *pkgname, const char *id, widget_buffer_h handle, int idx, int x, int y, int w, int h, int gbar, const char *descfile)
185 {
186         int ret = WIDGET_ERROR_INVALID_PARAMETER;
187
188         if (s_info.type == LOAD_TYPE_UNKNOWN) {
189                 load_update_function();
190         }
191
192         if (s_info.type == LOAD_TYPE_APP) {
193                 ret = s_info.binder.app.send(handle, idx, x, y, w, h, gbar);
194         } else if (s_info.type == LOAD_TYPE_SLAVE) {
195                 /**
196                  * pkgname, id are used for finding handle of direct connection.
197                  */
198                 ret = s_info.binder.slave.send(pkgname, id, handle, idx, x, y, w, h, gbar, descfile);
199         } else {
200                 widget_damage_region_s region = {
201                         .x = x,
202                         .y = y,
203                         .w = w,
204                         .h = h,
205                 };
206                 ret = widget_provider_send_buffer_updated(handle, idx, &region, gbar, descfile);
207         }
208
209         return ret;
210 }
211
212 PUBLIC int widget_content_is_updated(const char *filename, int is_gbar)
213 {
214         if (!s_info.trigger_update_monitor) {
215                 s_info.trigger_update_monitor = dlsym(RTLD_DEFAULT, FUNC_WIDGET_TRIGGER_UPDATE_MONITOR);
216                 if (!s_info.trigger_update_monitor) {
217                         ErrPrint("Trigger update monitor function is not exists\n");
218                         return WIDGET_ERROR_FAULT;
219                 }
220         }
221
222         return s_info.trigger_update_monitor(filename, is_gbar);
223 }
224
225 const char *binder_widget_find_pkgname(const char *uri)
226 {
227         const char *pkgname;
228
229         if (!s_info.find_pkgname) {
230                 s_info.find_pkgname = dlsym(RTLD_DEFAULT, FUNC_WIDGET_FIND_PKGNAME);
231                 if (!s_info.find_pkgname) {
232                         ErrPrint("Failed to find a \"widget_find_pkgname\"\n");
233                         return NULL;
234                 }
235         }
236
237         pkgname = s_info.find_pkgname(uri);
238         if (!pkgname) {
239                 ErrPrint("Failed to find a package (%s)\n", uri);
240                 return NULL;
241         }
242
243         return pkgname;
244 }
245
246 PUBLIC int widget_request_update(const char *filename)
247 {
248         char *uri;
249         int ret;
250
251         if (!filename) {
252                 ErrPrint("Invalid argument\n");
253                 return WIDGET_ERROR_INVALID_PARAMETER;
254         }
255
256         uri = util_id_to_uri(filename);
257         if (!uri) {
258                 ErrPrint("Heap: %d\n", errno);
259                 return WIDGET_ERROR_OUT_OF_MEMORY;
260         }
261
262         if (!s_info.request_update_by_id) {
263                 s_info.request_update_by_id = dlsym(RTLD_DEFAULT, FUNC_WIDGET_REQUEST_UPDATE_BY_ID);
264                 if (!s_info.request_update_by_id) {
265                         ErrPrint("\"widget_request_update_by_id\" is not exists\n");
266                         free(uri);
267                         return WIDGET_ERROR_FAULT;
268                 }
269         }
270
271         ret = s_info.request_update_by_id(uri);
272         free(uri);
273         return ret;
274 }
275
276 int binder_widget_update_extra_info(const char *id, const char *content, const char *title, const char *icon, const char *name)
277 {
278         if (!s_info.update_extra_info) {
279                 s_info.update_extra_info = dlsym(RTLD_DEFAULT, FUNC_WIDGET_UPDATE_EXTRA_INFO);
280                 if (!s_info.update_extra_info) {
281                         ErrPrint("Failed to find a \"widget_update_extra_info\"\n");
282                         return WIDGET_ERROR_INVALID_PARAMETER;
283                 }
284         }
285
286         return s_info.update_extra_info(id, content, title, icon, name);
287 }
288
289 /**
290  * @note
291  * This callback can be called twice (or more) to get a several pixmaps
292  * Acquired pixmaps are used for double/tripple buffering for canvas
293  */
294 static Ecore_X_Pixmap alloc_pixmap_cb(void *data, Ecore_X_Window parent, int w, int h, int depth)
295 {
296         vwin_info_t info = data;
297         Ecore_X_Pixmap pixmap;
298
299         if (!info->handle) {
300                 ErrPrint("Invalid handle\n");
301                 return 0u;
302         }
303
304         info->w = w;
305         info->h = h;
306         DbgPrint("Size of ee is updated: %dx%d - %d (info: %p)\n", info->w, info->h, depth, info);
307         depth >>= 3;
308
309         if (widget_viewer_get_resource_id(info->handle, WIDGET_PRIMARY_BUFFER) == 0u) {
310                 /**
311                  * @note
312                  * Need to allocate a primary buffer
313                  */
314                 widget_viewer_acquire_buffer(info->handle, WIDGET_PRIMARY_BUFFER, info->w, info->h, depth);
315                 if (!info->handle) {
316                         ErrPrint("Failed to get the buffer\n");
317                         return 0u;
318                 }
319
320                 pixmap = (Ecore_X_Pixmap)widget_viewer_get_resource_id(info->handle, WIDGET_PRIMARY_BUFFER);
321         } else if (WIDGET_CONF_EXTRA_BUFFER_COUNT > 0) {
322                 int idx;
323
324                 if (!info->resource_array) {
325                         info->resource_array = calloc(WIDGET_CONF_EXTRA_BUFFER_COUNT, sizeof(*info->resource_array));
326                         if (!info->resource_array) {
327                                 ErrPrint("Out of memory: %d\n", errno);
328                                 return 0u;
329                         }
330
331                         idx = 0;
332                 } else {
333                         for (idx = 0; idx < WIDGET_CONF_EXTRA_BUFFER_COUNT; idx++) {
334                                 if (info->resource_array[idx] == 0u) {
335                                         break;
336                                 }
337                         }
338
339                         if (idx == WIDGET_CONF_EXTRA_BUFFER_COUNT) {
340                                 ErrPrint("Out of index: %d\n", idx);
341                                 return 0u;
342                         }
343                 }
344
345                 if (widget_viewer_acquire_buffer(info->handle, idx, info->w, info->h, depth) < 0) {
346                         ErrPrint("Failed to acquire a buffer for %d\n", idx);
347                         return 0u;
348                 }
349
350                 info->resource_array[idx] = widget_viewer_get_resource_id(info->handle, idx);
351                 if (info->resource_array[idx] == 0u) {
352                         ErrPrint("Failed to allocate pixmap\n");
353                 }
354
355                 DbgPrint("Allocated index: %d/%d - %u\n", idx, WIDGET_CONF_EXTRA_BUFFER_COUNT, info->resource_array[idx]);
356                 pixmap = info->resource_array[idx];
357         } else {
358                 ErrPrint("Unable to allocate pixmap\n");
359                 pixmap = 0u;
360         }
361
362         /**
363          * Acquire a buffer for canvas.
364          */
365         info->type = VWIN_PIXMAP;
366         info->resource_cnt += !!(unsigned int)pixmap;
367         return pixmap;
368 }
369
370 static void free_pixmap_cb(void *data, Ecore_X_Pixmap pixmap)
371 {
372         vwin_info_t info = data;
373
374         if (!info->handle) {
375                 return;
376         }
377
378         if (info->type != VWIN_PIXMAP) {
379                 ErrPrint("Impossible\n");
380         }
381
382         if (widget_viewer_get_resource_id(info->handle, WIDGET_PRIMARY_BUFFER) == pixmap) {
383                 if (widget_viewer_release_buffer(info->handle, WIDGET_PRIMARY_BUFFER) < 0) {
384                         DbgPrint("Failed to release buffer\n");
385                 }
386                 info->resource_cnt--;
387         } else {
388                 int idx;
389
390                 for (idx = 0; idx < WIDGET_CONF_EXTRA_BUFFER_COUNT; idx++) {
391                         /**
392                          * @note
393                          * Find a index to release it
394                          */
395                         if (info->resource_array[idx] == pixmap) {
396                                 if (widget_viewer_release_buffer(info->handle, idx) < 0) {
397                                         DbgPrint("Failed to release buffer\n");
398                                 }
399                                 info->resource_array[idx] = 0u;
400                                 info->resource_cnt--;
401                                 break;
402                         }
403                 }
404         }
405
406         if (info->flags.field.deleted && info->resource_cnt == 0) {
407                 DbgPrint("Destroy buffer handle\n");
408
409                 info->state = VWIN_INFO_DESTROYED;
410                 widget_destroy_buffer(info->handle);
411                 free(info->resource_array);
412                 free(info->id);
413                 free(info);
414         }
415 }
416
417 static void *alloc_fb(void *data, int size)
418 {
419         vwin_info_t info = data;
420         void *buffer;
421
422         if (info->ee) {
423                 ecore_evas_geometry_get(info->ee, NULL, NULL, &info->w, &info->h);
424                 DbgPrint("Size of ee is updated: %dx%d (info: %p)\n", info->w, info->h, info);
425         }
426
427         if (!info->handle) {
428                 ErrPrint("Failed to create a buffer\n");
429                 return NULL;
430         }
431
432         if (widget_viewer_acquire_buffer(info->handle, WIDGET_PRIMARY_BUFFER, info->w, info->h, sizeof(int)) < 0) {
433                 ErrPrint("Failed to acquire buffer\n");
434                 return NULL;
435         }
436
437         /**
438          * If it supports the H/W accelerated buffer,
439          * Use it.
440          */
441         if (widget_support_hw_buffer(info->handle)) {
442                 if (widget_create_hw_buffer(info->handle) == 0) {
443                         buffer = widget_buffer_hw_buffer(info->handle);
444                         if (buffer) {
445                                 DbgPrint("HW Accelerated buffer is created %p, (%dx%d)\n", info, info->w, info->h);
446                                 info->type = VWIN_GEM;
447                                 return buffer;
448                         }
449                 }
450
451                 ErrPrint("Failed to allocate HW Accelerated buffer\n");
452         }
453
454         /**
455          * Or use the buffer of a S/W backend.
456          */
457         buffer = widget_ref_buffer(info->handle);
458         DbgPrint("SW buffer is created (%dx%d)\n", info->w, info->h);
459         info->type = VWIN_SW_BUF;
460         return buffer;
461 }
462
463 static void *alloc_stride_fb(void *data, int size, int *stride, int *bpp)
464 {
465         void *buffer;
466
467         buffer = alloc_fb(data, size);
468         if (buffer) {
469                 vwin_info_t info = data;
470                 int _stride;
471
472                 *bpp = sizeof(int);
473                 _stride = widget_buffer_stride(info->handle);
474                 if (_stride < 0) {
475                         _stride = info->w * *bpp;
476                 }
477
478                 *stride = _stride;
479                 *bpp <<= 3;
480                 DbgPrint("bpp: %d, stride: %d\n", *bpp, *stride);
481         }
482
483         return buffer;
484 }
485
486 static void free_fb(void *data, void *ptr)
487 {
488         vwin_info_t info = data;
489
490         if (!info->handle) {
491                 return;
492         }
493
494         if (info->type == VWIN_GEM) {
495                 if (widget_destroy_hw_buffer(info->handle) == 0) {
496                         DbgPrint("HW Accelerated buffer is destroyed\n");
497                 }
498         } else if (info->type == VWIN_SW_BUF) {
499                 DbgPrint("SW buffer is destroyed, %p\n", info);
500                 widget_unref_buffer(ptr);
501         } else if (info->type == VWIN_PIXMAP) {
502                 ErrPrint("Unable to reach to here\n");
503         }
504
505         if (widget_viewer_release_buffer(info->handle, WIDGET_PRIMARY_BUFFER) < 0) {
506                 ErrPrint("Failed to release buffer\n");
507         }
508
509         if (info->flags.field.deleted) {
510                 info->state = VWIN_INFO_DESTROYED;
511                 widget_destroy_buffer(info->handle);
512                 free(info->resource_array);
513                 free(info->id);
514                 free(info);
515         }
516 }
517
518 static inline int check_gl_engine(const char *engine)
519 {
520         /**
521          * @todo
522          * If the "engine" string going to be changed to more complicated formatted string,
523          * We should replace this with sytanx recognizer (parser).
524          */
525         return !!strcasestr(engine, GL_ENGINE);
526 }
527
528 Ecore_Evas *binder_ecore_evas_new(vwin_info_t info)
529 {
530         const char *engine;
531         Ecore_Evas *ee = NULL;
532
533         load_ecore_evas_function();
534
535         engine = elm_config_accel_preference_get();
536
537         DbgPrint("Preferred engine: %s (%s or %s)\n", engine, GL_ENGINE, GL_ENGINE_X11);
538         if (engine && check_gl_engine(engine)) {
539                 if (s_info.alloc_canvas_with_pixmap) {
540                         ee = s_info.alloc_canvas_with_pixmap(NULL, 0u, 0, 0, info->w, info->h, alloc_pixmap_cb, free_pixmap_cb, info);
541                         if (!ee) {
542                                 ErrPrint("Unable to create a ee for pixmap\n");
543                         }
544                 }
545         }
546
547         if (!ee) {
548                 if (!widget_conf_auto_align() && s_info.alloc_canvas_with_stride) {
549                         ee = s_info.alloc_canvas_with_stride(info->w, info->h, alloc_stride_fb, free_fb, info);
550                 } else if (s_info.alloc_canvas) {
551                         ee = s_info.alloc_canvas(info->w, info->h, alloc_fb, free_fb, info);
552                 }
553         }
554
555         return ee;
556 }
557
558 int binder_widget_auto_align(void)
559 {
560         load_ecore_evas_function();
561
562         return widget_conf_auto_align() || !s_info.alloc_canvas_with_stride;
563 }
564
565 /* End of a file */