0a77054edcbd54b92ed13975ff2c7152437d8b3d
[platform/core/api/efl-util.git] / src / efl_util.c
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License. 
15  */
16
17
18 #define LOG_TAG "TIZEN_N_EFL_UTIL"
19
20 #include <efl_util.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <Elementary.h>
25
26 #if X11
27 #include <Ecore_X.h>
28 #include <utilX.h>
29 #endif
30
31 #if WAYLAND
32 #include <Ecore_Wayland.h>
33 #include <wayland-client.h>
34 #include "tizen_notification-client-protocol.h"
35 #endif
36
37 typedef struct _notification_error_cb_info
38 {
39    Evas_Object *window;
40    efl_util_notification_window_level_error_cb err_cb;
41    void *user_data;
42 } notification_error_cb_info;
43
44 Eina_List *_g_notification_error_cb_info_list;
45 static Ecore_Event_Handler* _noti_level_access_result_handler = NULL;
46 static int _noti_handler_count = 0;
47
48 static notification_error_cb_info *_notification_error_cb_info_find(Evas_Object *window);
49 static Eina_Bool _efl_util_notification_info_add(Evas_Object *window, efl_util_notification_window_level_error_cb callback, void *user_data);
50 static Eina_Bool _efl_util_notification_info_del(Evas_Object *window);
51
52 #if X11
53 static unsigned int _noti_level_access_result_atom = 0;
54
55 static Eina_Bool _efl_util_client_message(void *data, int type, void *event);
56 static notification_error_cb_info *_notification_error_cb_info_find_by_xwin(unsigned int xwin);
57 #endif
58
59 #if WAYLAND
60 typedef struct _Surface_Level
61 {
62    struct wl_surface *surface;
63    int32_t level;
64    Eina_Bool wait_set_level_done;
65 } Surface_Level;
66
67 static void _cb_handle_registry_global(void *data, struct wl_registry *registry, unsigned int name, const char *interface, unsigned int version);
68 static void _cb_handle_registry_global_remove(void *data, struct wl_registry *registry, unsigned int name);
69 static void _notification_set_level_done(void *data, struct tizen_notification *tizen_notification, struct wl_surface *surface, int32_t level, uint32_t error_state);
70 static notification_error_cb_info *_notification_error_cb_info_find_by_wl_surface(struct wl_surface *surface);
71
72 static const struct wl_registry_listener _registry_listener =
73 {
74    _cb_handle_registry_global,
75    _cb_handle_registry_global_remove
76 };
77
78 struct tizen_notification_listener _tizen_notification_listener =
79 {
80    _notification_set_level_done,
81 };
82
83 static struct tizen_notification *_tizen_notification = NULL;
84 static Eina_Bool _efl_util_init_done = EINA_FALSE;
85 static Eina_Hash *hash_surface_levels = NULL;
86
87 static void
88 _cb_handle_registry_global(void *data, struct wl_registry *registry, unsigned int name, const char *interface, unsigned int version)
89 {
90    if (!strcmp(interface, "tizen_notification"))
91      {
92         _tizen_notification = wl_registry_bind(registry, name, &tizen_notification_interface, 1);
93         if (!_tizen_notification) return;
94         tizen_notification_add_listener(_tizen_notification, &_tizen_notification_listener, NULL);
95         _efl_util_init_done = EINA_TRUE;
96         hash_surface_levels = eina_hash_pointer_new(free);
97      }
98 }
99
100 # define _FREE_FUNC(_h, _fn) do { if (_h) { _fn((void*)_h); _h = NULL; } } while (0)
101 static void
102 _cb_handle_registry_global_remove(void *data, struct wl_registry *registry, unsigned int name)
103 {
104    _tizen_notification = NULL;
105    _efl_util_init_done = EINA_FALSE;
106    _FREE_FUNC(hash_surface_levels, eina_hash_free);
107    /* no-op */
108 }
109
110 static void
111 _notification_set_level_done(void *data,
112                              struct tizen_notification *tizen_notification,
113                              struct wl_surface *surface,
114                              int32_t level,
115                              uint32_t error_state)
116 {
117    Surface_Level *sl;
118    notification_error_cb_info *cb_info = NULL;
119    efl_util_error_e error_cb_state = EFL_UTIL_ERROR_NONE;
120
121    if (hash_surface_levels)
122      {
123         sl = eina_hash_find(hash_surface_levels, &surface);
124         if (sl)
125           {
126              sl->level = level;
127              sl->wait_set_level_done = EINA_FALSE;
128           }
129      }
130
131    cb_info = _notification_error_cb_info_find_by_wl_surface(surface);
132    if (cb_info)
133      {
134         switch (error_state)
135           {
136              case TIZEN_NOTIFICATION_ERROR_STATE_NONE:
137                 error_cb_state = EFL_UTIL_ERROR_NONE;
138                 break;
139              case TIZEN_NOTIFICATION_ERROR_STATE_PERMISSION_DENIED:
140              default:
141                 error_cb_state = EFL_UTIL_ERROR_PERMISSION_DENIED;
142                 break;
143           }
144         if (cb_info->err_cb)
145           cb_info->err_cb(cb_info->window, error_cb_state , cb_info->user_data);
146      }
147 }
148
149 static void
150 _efl_util_wl_init(void)
151 {
152    static Eina_Bool init = EINA_FALSE;
153    if (!init)
154      {
155         wl_registry_add_listener(wl_display_get_registry(ecore_wl_display_get()),
156                                  &_registry_listener, NULL);
157         init = EINA_TRUE;
158      }
159    while (!_efl_util_init_done)
160      wl_display_dispatch(ecore_wl_display_get());
161 }
162 #endif
163
164 int
165 efl_util_set_notification_window_level(Evas_Object *window, efl_util_notification_level_e level)
166 {
167    EINA_SAFETY_ON_NULL_RETURN_VAL(window, EFL_UTIL_ERROR_INVALID_PARAMETER);
168    EINA_SAFETY_ON_FALSE_RETURN_VAL((level >= EFL_UTIL_NOTIFICATION_LEVEL_1) &&
169                                    (level <= EFL_UTIL_NOTIFICATION_LEVEL_TOP),
170                                    EFL_UTIL_ERROR_INVALID_PARAMETER);
171
172 #if X11
173    Ecore_X_Window xwin = elm_win_xwindow_get(window);
174    if (xwin)
175      {
176         Ecore_X_Window_Type window_type;
177         if(ecore_x_netwm_window_type_get(xwin, &window_type) == EINA_TRUE)
178           {
179              // success to get window type
180              if(window_type != ECORE_X_WINDOW_TYPE_NOTIFICATION)
181                {
182                   // given EFL window's type is not notification type.
183                   return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
184                }
185           }
186         else
187           return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
188
189         utilx_set_system_notification_level(ecore_x_display_get(), xwin,
190                                             level);
191         return EFL_UTIL_ERROR_NONE;
192      }
193 #endif
194
195 #if WAYLAND
196    Ecore_Wl_Window *wl_win = elm_win_wl_window_get(window);
197    if (wl_win)
198      {
199         _efl_util_wl_init();
200
201         if (hash_surface_levels)
202           {
203              Surface_Level *sl;
204              struct wl_surface *surface = ecore_wl_window_surface_get(wl_win);
205              sl = eina_hash_find(hash_surface_levels, &surface);
206              if (!sl)
207                {
208                   sl = calloc(1, sizeof(Surface_Level));
209                   if (sl)
210                     {
211                        sl->surface = surface;
212                        sl->level = EFL_UTIL_NOTIFICATION_LEVEL_DEFAULT;
213                        sl->wait_set_level_done = EINA_TRUE;
214                        eina_hash_add(hash_surface_levels, &surface, sl);
215                     }
216                }
217              else
218                {
219                   sl->wait_set_level_done = EINA_TRUE;
220                }
221           }
222
223         //Add notification window type check
224         tizen_notification_set_level(_tizen_notification,
225                                      ecore_wl_window_surface_get(wl_win),
226                                      level);
227         return EFL_UTIL_ERROR_NONE;
228      }
229 #endif
230
231    return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
232 }
233
234 int
235 efl_util_get_notification_window_level(Evas_Object *window, efl_util_notification_level_e *level)
236 {
237
238    EINA_SAFETY_ON_NULL_RETURN_VAL(window, EFL_UTIL_ERROR_INVALID_PARAMETER);
239    EINA_SAFETY_ON_NULL_RETURN_VAL(level, EFL_UTIL_ERROR_INVALID_PARAMETER);
240
241 #if X11
242    Ecore_X_Window_Type window_type;
243    Utilx_Notification_Level utilx_level;
244    Ecore_X_Window xwin = elm_win_xwindow_get(window);
245    if (xwin)
246      {
247         if(ecore_x_netwm_window_type_get(xwin, &window_type) == EINA_TRUE)
248           {
249              // success to get window type
250              if(window_type != ECORE_X_WINDOW_TYPE_NOTIFICATION)
251                {
252                   // given EFL window's type is not notification type.
253                   return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
254                }
255
256              utilx_level = utilx_get_system_notification_level (ecore_x_display_get(), xwin);
257
258              if(utilx_level == UTILX_NOTIFICATION_LEVEL_LOW)
259                {
260                   *level = EFL_UTIL_NOTIFICATION_LEVEL_1;
261                }
262              else if(utilx_level == UTILX_NOTIFICATION_LEVEL_NORMAL)
263                {
264                   *level = EFL_UTIL_NOTIFICATION_LEVEL_2;
265                }
266              else if(utilx_level == UTILX_NOTIFICATION_LEVEL_HIGH)
267                {
268                   *level = EFL_UTIL_NOTIFICATION_LEVEL_3;
269                }
270              else
271                {
272                   return EFL_UTIL_ERROR_INVALID_PARAMETER;
273                }
274
275           }
276         else
277           {
278              // fail to get window type
279              return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
280           }
281
282         return EFL_UTIL_ERROR_NONE;
283      }
284 #endif
285
286 #if WAYLAND
287    Ecore_Wl_Window *wl_win = elm_win_wl_window_get(window);
288    if (wl_win)
289      {
290         Surface_Level *sl;
291         struct wl_surface *surface = ecore_wl_window_surface_get(wl_win);
292
293         sl = eina_hash_find(hash_surface_levels, &surface);
294         if (sl)
295           {
296             if (sl->wait_set_level_done)
297               {
298                   if (ecore_wl_window_shell_surface_get(wl_win) ||
299                       ecore_wl_window_xdg_surface_get(wl_win))
300                     {
301                        while (sl->wait_set_level_done)
302                          {
303                             ecore_wl_flush();
304                             wl_display_dispatch(ecore_wl_display_get());
305                          }
306                     }
307                   else
308                     {
309                        *level = EFL_UTIL_NOTIFICATION_LEVEL_DEFAULT;
310                        return EFL_UTIL_ERROR_INVALID_PARAMETER;
311                     }
312               }
313
314             switch (sl->level)
315               {
316                  case TIZEN_NOTIFICATION_LEVEL_1:
317                    *level = EFL_UTIL_NOTIFICATION_LEVEL_1;
318                    break;
319                  case TIZEN_NOTIFICATION_LEVEL_2:
320                    *level = EFL_UTIL_NOTIFICATION_LEVEL_2;
321                    break;
322                  case TIZEN_NOTIFICATION_LEVEL_3:
323                    *level = EFL_UTIL_NOTIFICATION_LEVEL_3;
324                    break;
325                  case TIZEN_NOTIFICATION_LEVEL_NONE:
326                    *level = EFL_UTIL_NOTIFICATION_LEVEL_NONE;
327                    break;
328                  case TIZEN_NOTIFICATION_LEVEL_DEFAULT:
329                    *level = EFL_UTIL_NOTIFICATION_LEVEL_DEFAULT;
330                    break;
331                  case TIZEN_NOTIFICATION_LEVEL_MEDIUM:
332                    *level = EFL_UTIL_NOTIFICATION_LEVEL_MEDIUM;
333                    break;
334                  case TIZEN_NOTIFICATION_LEVEL_HIGH:
335                    *level = EFL_UTIL_NOTIFICATION_LEVEL_HIGH;
336                    break;
337                  case TIZEN_NOTIFICATION_LEVEL_TOP:
338                    *level = EFL_UTIL_NOTIFICATION_LEVEL_TOP;
339                    break;
340                  default:
341                    *level = EFL_UTIL_NOTIFICATION_LEVEL_DEFAULT;
342                    return EFL_UTIL_ERROR_INVALID_PARAMETER;
343               }
344             return EFL_UTIL_ERROR_NONE;
345           }
346         else
347           {
348              *level = EFL_UTIL_NOTIFICATION_LEVEL_DEFAULT;
349           }
350      }
351 #endif
352    return EFL_UTIL_ERROR_NOT_SUPPORTED_WINDOW_TYPE;
353 }
354
355 int
356 efl_util_set_notification_window_level_error_cb(Evas_Object *window, efl_util_notification_window_level_error_cb callback, void *user_data)
357 {
358    Eina_Bool ret = EINA_FALSE;
359
360    EINA_SAFETY_ON_NULL_RETURN_VAL(window, EFL_UTIL_ERROR_INVALID_PARAMETER);
361    EINA_SAFETY_ON_NULL_RETURN_VAL(callback, EFL_UTIL_ERROR_INVALID_PARAMETER);
362
363    ret = _efl_util_notification_info_add(window, callback, user_data);
364    if (ret)
365      {
366 #if X11
367         if (!_noti_level_access_result_atom)
368           _noti_level_access_result_atom = ecore_x_atom_get("_E_NOTIFICATION_LEVEL_ACCESS_RESULT");
369
370         if (!_noti_level_access_result_handler)
371           _noti_level_access_result_handler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, _efl_util_client_message, NULL);
372         _noti_handler_count++;
373
374         return EFL_UTIL_ERROR_NONE;
375 #endif
376
377 #if WAYLAND
378         return EFL_UTIL_ERROR_NONE;
379 #endif
380      }
381    return EFL_UTIL_ERROR_OUT_OF_MEMORY;
382 }
383
384 int
385 efl_util_unset_notification_window_level_error_cb(Evas_Object *window)
386 {
387    Eina_Bool ret = EINA_FALSE;
388
389    EINA_SAFETY_ON_NULL_RETURN_VAL(window, EFL_UTIL_ERROR_INVALID_PARAMETER);
390
391    ret = _efl_util_notification_info_del(window);
392    if (ret)
393      {
394         _noti_handler_count--;
395         if (_noti_handler_count == 0)
396           {
397              if (_noti_level_access_result_handler)
398                {
399                   ecore_event_handler_del(_noti_level_access_result_handler);
400                   _noti_level_access_result_handler = NULL;
401                }
402           }
403         return EFL_UTIL_ERROR_NONE;
404      }
405
406    return EFL_UTIL_ERROR_INVALID_PARAMETER;
407 }
408
409 #if X11
410 static Eina_Bool
411 _efl_util_client_message(void *data, int type, void *event)
412 {
413    Ecore_X_Event_Client_Message *ev;
414
415    ev = event;
416    if (!ev) return ECORE_CALLBACK_PASS_ON;
417
418    if (ev->message_type == _noti_level_access_result_atom)
419      {
420         Ecore_X_Window xwin;
421         xwin = ev->win;
422
423         notification_error_cb_info *cb_info = NULL;
424         cb_info = _notification_error_cb_info_find_by_xwin(xwin);
425         if (cb_info)
426           {
427              int access = ev->data.l[1];
428              if (access == 0) // permission denied
429                {
430                   if (cb_info->err_cb)
431                     {
432                        cb_info->err_cb(cb_info->window, EFL_UTIL_ERROR_PERMISSION_DENIED, cb_info->user_data);
433                     }
434                }
435           }
436      }
437
438    return ECORE_CALLBACK_PASS_ON;
439 }
440
441 static notification_error_cb_info *
442 _notification_error_cb_info_find_by_xwin(unsigned int xwin)
443 {
444    Eina_List *l;
445    notification_error_cb_info* temp;
446    unsigned int temp_xwin;
447
448    EINA_LIST_FOREACH(_g_notification_error_cb_info_list, l, temp)
449      {
450         if (temp->window)
451           {
452              temp_xwin = elm_win_xwindow_get(temp->window);
453              if (xwin == temp_xwin)
454                {
455                   return temp;
456                }
457           }
458      }
459
460    return NULL;
461 }
462 #endif
463
464 #if WAYLAND
465 static notification_error_cb_info *
466 _notification_error_cb_info_find_by_wl_surface(struct wl_surface *surface)
467 {
468    Eina_List *l;
469    notification_error_cb_info* temp;
470    struct wl_surface *temp_surface;
471
472    EINA_LIST_FOREACH(_g_notification_error_cb_info_list, l, temp)
473      {
474         if (temp->window)
475           {
476              temp_surface = ecore_wl_window_surface_get(elm_win_wl_window_get(temp->window));
477              if (surface == temp_surface)
478                {
479                   return temp;
480                }
481           }
482      }
483
484    return NULL;
485 }
486 #endif
487
488 static notification_error_cb_info *
489 _notification_error_cb_info_find(Evas_Object *window)
490 {
491    Eina_List *l;
492    notification_error_cb_info* temp;
493
494    EINA_LIST_FOREACH(_g_notification_error_cb_info_list, l, temp)
495      {
496         if (temp->window == window)
497           {
498              return temp;
499           }
500      }
501
502    return NULL;
503 }
504
505 static Eina_Bool
506 _efl_util_notification_info_add(Evas_Object *window, efl_util_notification_window_level_error_cb callback, void *user_data)
507 {
508    notification_error_cb_info* _err_info = _notification_error_cb_info_find(window);
509
510    if (_err_info)
511      {
512         _g_notification_error_cb_info_list = eina_list_remove(_g_notification_error_cb_info_list, _err_info);
513         free(_err_info);
514         _err_info = NULL;
515      }
516
517    _err_info = (notification_error_cb_info*)calloc(1, sizeof(notification_error_cb_info));
518    if (!_err_info)
519      {
520         return EINA_FALSE;
521      }
522    _err_info->window = window;
523    _err_info->err_cb = callback;
524    _err_info->user_data = user_data;
525
526    _g_notification_error_cb_info_list = eina_list_append(_g_notification_error_cb_info_list, _err_info);
527
528    return EINA_TRUE;
529 }
530
531 static Eina_Bool
532 _efl_util_notification_info_del(Evas_Object *window)
533 {
534    notification_error_cb_info* _err_info = _notification_error_cb_info_find(window);
535    if (!_err_info)
536      {
537         return EINA_FALSE;
538      }
539
540    _g_notification_error_cb_info_list = eina_list_remove(_g_notification_error_cb_info_list, _err_info);
541    free(_err_info);
542
543    return EINA_TRUE;
544 }