1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include "ecore_wl2_private.h"
6 
7 #include "linux-dmabuf-unstable-v1-client-protocol.h"
8 #include "efl-hints-client-protocol.h"
9 
10 static Eina_Hash *_server_displays = NULL;
11 static Eina_Hash *_client_displays = NULL;
12 
13 static Eina_Bool _cb_connect_data(void *data, Ecore_Fd_Handler *hdl);
14 static Eina_Bool _ecore_wl2_display_connect(Ecore_Wl2_Display *ewd, Eina_Bool sync);
15 
16 static void _ecore_wl2_display_sync_add(Ecore_Wl2_Display *ewd);
17 
18 void
_display_event_free(void * d,void * event)19 _display_event_free(void *d, void *event)
20 {
21    ecore_wl2_display_disconnect(d);
22    free(event);
23 }
24 
25 static void
_ecore_wl2_display_event(Ecore_Wl2_Display * ewd,int event)26 _ecore_wl2_display_event(Ecore_Wl2_Display *ewd, int event)
27 {
28    Ecore_Wl2_Event_Connect *ev;
29 
30    ev = calloc(1, sizeof(Ecore_Wl2_Event_Connect));
31    EINA_SAFETY_ON_NULL_RETURN(ev);
32    ev->display = ewd;
33    ewd->refs++;
34    ecore_event_add(event, ev, _display_event_free, ewd);
35 }
36 
37 static void
_ecore_wl2_display_signal_exit(void)38 _ecore_wl2_display_signal_exit(void)
39 {
40    Ecore_Event_Signal_Exit *ev;
41 
42    ev = calloc(1, sizeof(Ecore_Event_Signal_Exit));
43    if (!ev) return;
44 
45    ev->quit = EINA_TRUE;
46    ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, ev, NULL, NULL);
47 }
48 
49 static void
_dmabuf_cb_format(void * data EINA_UNUSED,struct zwp_linux_dmabuf_v1 * dmabuf EINA_UNUSED,uint32_t format EINA_UNUSED)50 _dmabuf_cb_format(void *data EINA_UNUSED, struct zwp_linux_dmabuf_v1 *dmabuf EINA_UNUSED, uint32_t format EINA_UNUSED)
51 {
52    /* It would be awfully nice if this actually happened */
53 };
54 
55 static const struct zwp_linux_dmabuf_v1_listener _dmabuf_listener =
56 {
57    _dmabuf_cb_format,
58    NULL
59 };
60 
61 static void
_xdg_shell_cb_ping(void * data,struct xdg_wm_base * shell,uint32_t serial)62 _xdg_shell_cb_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
63 {
64    xdg_wm_base_pong(shell, serial);
65    ecore_wl2_display_flush(data);
66 }
67 
68 static const struct xdg_wm_base_listener _xdg_shell_listener =
69 {
70    _xdg_shell_cb_ping,
71 };
72 
73 static void
_zxdg_shell_cb_ping(void * data,struct zxdg_shell_v6 * shell,uint32_t serial)74 _zxdg_shell_cb_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
75 {
76    zxdg_shell_v6_pong(shell, serial);
77    ecore_wl2_display_flush(data);
78 }
79 
80 static const struct zxdg_shell_v6_listener _zxdg_shell_listener =
81 {
82    _zxdg_shell_cb_ping,
83 };
84 
85 static void
_session_recovery_create_uuid(void * data EINA_UNUSED,struct zwp_e_session_recovery * session_recovery EINA_UNUSED,struct wl_surface * surface,const char * uuid)86 _session_recovery_create_uuid(void *data EINA_UNUSED, struct zwp_e_session_recovery *session_recovery EINA_UNUSED, struct wl_surface *surface, const char *uuid)
87 {
88    Ecore_Wl2_Window *win;
89 
90    /* surface may have been destroyed */
91    if (!surface) return;
92    win = wl_surface_get_user_data(surface);
93 
94    eina_stringshare_replace(&win->uuid, uuid);
95 }
96 
97 static const struct zwp_e_session_recovery_listener _session_listener =
98 {
99    _session_recovery_create_uuid,
100 };
101 
102 static void
_aux_hints_supported_aux_hints(void * data,struct efl_aux_hints * aux_hints EINA_UNUSED,struct wl_surface * surface_resource,struct wl_array * hints,uint32_t num_hints)103 _aux_hints_supported_aux_hints(void *data, struct efl_aux_hints *aux_hints EINA_UNUSED, struct wl_surface *surface_resource, struct wl_array *hints, uint32_t num_hints)
104 {
105    Ecore_Wl2_Display *ewd = data;
106    struct wl_surface *surface = surface_resource;
107    Ecore_Wl2_Window *win = NULL;
108    char *p = NULL;
109    char **str = NULL;
110    const char *hint = NULL;
111    unsigned int i = 0;
112    Ecore_Wl2_Event_Aux_Hint_Supported *ev;
113 
114    if (!surface) return;
115    win = _ecore_wl2_display_window_surface_find(ewd, surface_resource);
116    if (!win) return;
117 
118    p = hints->data;
119    str = calloc(num_hints, sizeof(char *));
120    if (!str) return;
121 
122    while ((const char *)p < ((const char *)hints->data + hints->size))
123      {
124         str[i] = (char *)eina_stringshare_add(p);
125         p += strlen(p) + 1;
126         i++;
127      }
128    for (i = 0; i < num_hints; i++)
129      {
130         hint = eina_stringshare_add(str[i]);
131         win->supported_aux_hints =
132                eina_list_append(win->supported_aux_hints, hint);
133      }
134    if (str)
135      {
136         for (i = 0; i < num_hints; i++)
137           {
138              if (str[i])
139                {
140                   eina_stringshare_del(str[i]);
141                   str[i] = NULL;
142                }
143           }
144         free(str);
145      }
146 
147    if (!(ev = calloc(1, sizeof(Ecore_Wl2_Event_Aux_Hint_Supported)))) return;
148    ev->win = win;
149    ev->display = ewd;
150    ewd->refs++;
151    ecore_event_add(ECORE_WL2_EVENT_AUX_HINT_SUPPORTED, ev,
152                    _display_event_free, ewd);
153 }
154 
155 static void
_aux_hints_allowed_aux_hint(void * data,struct efl_aux_hints * aux_hints EINA_UNUSED,struct wl_surface * surface_resource,int id)156 _aux_hints_allowed_aux_hint(void *data, struct efl_aux_hints *aux_hints  EINA_UNUSED, struct wl_surface *surface_resource, int id)
157 {
158    struct wl_surface *surface = surface_resource;
159    Ecore_Wl2_Window *win = NULL;
160    Ecore_Wl2_Display *ewd = data;
161    Ecore_Wl2_Event_Aux_Hint_Allowed *ev;
162 
163    if (!surface) return;
164    win = _ecore_wl2_display_window_surface_find(ewd, surface_resource);
165    if (!win) return;
166 
167    if (!(ev = calloc(1, sizeof(Ecore_Wl2_Event_Aux_Hint_Allowed)))) return;
168    ev->win = win;
169    ev->id = id;
170    ev->display = ewd;
171    ewd->refs++;
172    ecore_event_add(ECORE_WL2_EVENT_AUX_HINT_ALLOWED, ev,
173                    _display_event_free, ewd);
174 }
175 
176  static void
_cb_aux_message_free(void * data EINA_UNUSED,void * event)177 _cb_aux_message_free(void *data EINA_UNUSED, void *event)
178 {
179    Ecore_Wl2_Event_Aux_Message *ev;
180    char *str;
181 
182    ev = event;
183    ecore_wl2_display_disconnect(ev->display);
184    eina_stringshare_del(ev->key);
185    eina_stringshare_del(ev->val);
186    EINA_LIST_FREE(ev->options, str)
187      eina_stringshare_del(str);
188    free(ev);
189 }
190 
191  static void
_aux_hints_aux_message(void * data,struct efl_aux_hints * aux_hints EINA_UNUSED,struct wl_surface * surface_resource,const char * key,const char * val,struct wl_array * options)192 _aux_hints_aux_message(void *data, struct efl_aux_hints *aux_hints EINA_UNUSED, struct wl_surface *surface_resource, const char *key, const char *val, struct wl_array *options)
193 {
194    Ecore_Wl2_Window *win = NULL;
195    Ecore_Wl2_Event_Aux_Message *ev;
196    char *p = NULL, *str = NULL;
197    Eina_List *opt_list = NULL;
198    Ecore_Wl2_Display *ewd = data;
199 
200    if (!surface_resource) return;
201    win = _ecore_wl2_display_window_surface_find(ewd, surface_resource);
202    if (!win) return;
203 
204    if (!(ev = calloc(1, sizeof(Ecore_Wl2_Event_Aux_Message)))) return;
205 
206    if ((options) && (options->size))
207      {
208         p = options->data;
209         while ((const char *)p < ((const char *)options->data + options->size))
210           {
211              str = (char *)eina_stringshare_add(p);
212              opt_list = eina_list_append(opt_list, str);
213              p += strlen(p) + 1;
214           }
215      }
216 
217    ev->win = win;
218    ev->key = eina_stringshare_add(key);
219    ev->val = eina_stringshare_add(val);
220    ev->options = opt_list;
221    ev->display = ewd;
222    ewd->refs++;
223 
224    ecore_event_add(ECORE_WL2_EVENT_AUX_MESSAGE, ev, _cb_aux_message_free, NULL);
225 }
226 
227 static const struct efl_aux_hints_listener _aux_hints_listener =
228 {
229    _aux_hints_supported_aux_hints,
230    _aux_hints_allowed_aux_hint,
231    _aux_hints_aux_message,
232 };
233 
234 static void
_cb_global_event_free(void * data EINA_UNUSED,void * event)235 _cb_global_event_free(void *data EINA_UNUSED, void *event)
236 {
237    Ecore_Wl2_Event_Global *ev;
238 
239    ev = event;
240    eina_stringshare_del(ev->interface);
241    ecore_wl2_display_disconnect(ev->display);
242    free(ev);
243 }
244 
245 static void
_cb_global_add(void * data,struct wl_registry * registry,unsigned int id,const char * interface,unsigned int version)246 _cb_global_add(void *data, struct wl_registry *registry, unsigned int id, const char *interface, unsigned int version)
247 {
248    Ecore_Wl2_Display *ewd;
249    Ecore_Wl2_Event_Global *ev;
250 
251    ewd = data;
252 
253    /* test to see if we have already added this global to our hash */
254    if (!eina_hash_find(ewd->globals, &id))
255      {
256         Ecore_Wl2_Global *global;
257 
258         /* allocate space for new global */
259         global = calloc(1, sizeof(Ecore_Wl2_Global));
260         if (!global) return;
261 
262         global->id = id;
263         global->interface = eina_stringshare_add(interface);
264         global->version = version;
265 
266         /* add this global to our hash */
267         if (!eina_hash_add(ewd->globals, &global->id, global))
268           {
269              eina_stringshare_del(global->interface);
270              free(global);
271           }
272      }
273    else
274      goto event;
275 
276    if (!strcmp(interface, "wl_compositor"))
277      {
278         Ecore_Wl2_Window *window;
279         ewd->wl.compositor_version = MIN(version, 4);
280         ewd->wl.compositor =
281           wl_registry_bind(registry, id, &wl_compositor_interface,
282                            ewd->wl.compositor_version);
283         EINA_INLIST_FOREACH(ewd->windows, window)
284           _ecore_wl2_window_surface_create(window);
285      }
286    else if (!strcmp(interface, "wl_subcompositor"))
287      {
288         ewd->wl.subcompositor =
289           wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
290      }
291    else if (!strcmp(interface, "wl_shm"))
292      {
293         ewd->wl.shm =
294           wl_registry_bind(registry, id, &wl_shm_interface, 1);
295      }
296    else if (!strcmp(interface, "zwp_linux_dmabuf_v1") && (version >= 2))
297      {
298         ewd->wl.dmabuf =
299           wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, 2);
300         zwp_linux_dmabuf_v1_add_listener(ewd->wl.dmabuf, &_dmabuf_listener, ewd);
301         _ecore_wl2_buffer_test(ewd);
302         _ecore_wl2_display_sync_add(ewd);
303      }
304    else if (!strcmp(interface, "wl_data_device_manager"))
305      {
306         ewd->wl.data_device_manager_version = MIN(version, 3);
307         ewd->wl.data_device_manager =
308           wl_registry_bind(registry, id, &wl_data_device_manager_interface, ewd->wl.data_device_manager_version);
309      }
310    else if ((eina_streq(interface, "www")) &&
311             (getenv("EFL_WAYLAND_ENABLE_WWW")))
312      {
313         Ecore_Wl2_Window *window;
314 
315         ewd->wl.www = wl_registry_bind(registry, id, &www_interface, 1);
316         EINA_INLIST_FOREACH(ewd->windows, window)
317           _ecore_wl2_window_www_surface_init(window);
318      }
319    else if ((!strcmp(interface, "zwp_e_session_recovery")) &&
320             (!no_session_recovery))
321      {
322         ewd->wl.session_recovery =
323           wl_registry_bind(registry, id,
324                            &zwp_e_session_recovery_interface, 1);
325         zwp_e_session_recovery_add_listener(ewd->wl.session_recovery,
326                                             &_session_listener, ewd);
327      }
328    else if (!strcmp(interface, "efl_aux_hints"))
329      {
330         Ecore_Wl2_Window *window;
331         ewd->wl.efl_aux_hints =
332           wl_registry_bind(registry, id,
333                            &efl_aux_hints_interface, 1);
334         efl_aux_hints_add_listener(ewd->wl.efl_aux_hints, &_aux_hints_listener, ewd);
335         EINA_INLIST_FOREACH(ewd->windows, window)
336           if (window->surface) efl_aux_hints_get_supported_aux_hints(ewd->wl.efl_aux_hints, window->surface);
337      }
338    else if (!strcmp(interface, "zwp_teamwork"))
339      {
340         ewd->wl.teamwork =
341           wl_registry_bind(registry, id,
342                            &zwp_teamwork_interface, EFL_TEAMWORK_VERSION);
343      }
344    else if (!strcmp(interface, "wl_output"))
345      _ecore_wl2_output_add(ewd, id);
346    else if (!strcmp(interface, "wl_seat"))
347      _ecore_wl2_input_add(ewd, id, version);
348    else if (!strcmp(interface, "efl_hints"))
349      {
350         Ecore_Wl2_Window *window;
351 
352         ewd->wl.efl_hints = wl_registry_bind(registry, id, &efl_hints_interface, MIN(version, 2));
353         EINA_INLIST_FOREACH(ewd->windows, window)
354           {
355              if (!window->xdg_surface) continue;
356              if (window->aspect.set)
357                efl_hints_set_aspect(window->display->wl.efl_hints, window->xdg_surface,
358                  window->aspect.w, window->aspect.h, window->aspect.aspect);
359              if (window->weight.set)
360                efl_hints_set_weight(window->display->wl.efl_hints,
361                  window->xdg_surface, window->weight.w, window->weight.h);
362           }
363      }
364 
365 event:
366    /* allocate space for event structure */
367    ev = calloc(1, sizeof(Ecore_Wl2_Event_Global));
368    if (!ev) return;
369 
370    ev->id = id;
371    ev->display = ewd;
372    ewd->refs++;
373    ev->version = version;
374    ev->interface = eina_stringshare_add(interface);
375 
376    /* raise an event saying a new global has been added */
377    ecore_event_add(ECORE_WL2_EVENT_GLOBAL_ADDED, ev,
378                    _cb_global_event_free, NULL);
379 }
380 
381 static void
_cb_global_remove(void * data,struct wl_registry * registry EINA_UNUSED,unsigned int id)382 _cb_global_remove(void *data, struct wl_registry *registry EINA_UNUSED, unsigned int id)
383 {
384    Ecore_Wl2_Display *ewd;
385    Ecore_Wl2_Global *global;
386    Ecore_Wl2_Event_Global *ev;
387 
388    ewd = data;
389 
390    /* try to find this global in our hash */
391    global = eina_hash_find(ewd->globals, &id);
392    if (!global) return;
393 
394    /* allocate space for event structure */
395    ev = calloc(1, sizeof(Ecore_Wl2_Event_Global));
396    if (!ev) return;
397 
398    ev->id = id;
399    ev->display = ewd;
400    ewd->refs++;
401    ev->version = global->version;
402    ev->interface = eina_stringshare_add(global->interface);
403 
404    /* raise an event saying a global has been removed */
405    ecore_event_add(ECORE_WL2_EVENT_GLOBAL_REMOVED, ev,
406                    _cb_global_event_free, NULL);
407 
408    /* delete this global from our hash */
409    if (ewd->globals) eina_hash_del_by_key(ewd->globals, &id);
410 }
411 
412 static const struct wl_registry_listener _registry_listener =
413 {
414    _cb_global_add,
415    _cb_global_remove
416 };
417 
418 static Eina_Bool
_cb_create_data(void * data,Ecore_Fd_Handler * hdl EINA_UNUSED)419 _cb_create_data(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED)
420 {
421    Ecore_Wl2_Display *ewd = data;
422    struct wl_event_loop *loop;
423 
424    loop = wl_display_get_event_loop(ewd->wl.display);
425    wl_event_loop_dispatch(loop, 0);
426 
427    /* wl_display_flush_clients(ewd->wl.display); */
428 
429    return ECORE_CALLBACK_RENEW;
430 }
431 
432 static void
_cb_create_prepare(void * data,Ecore_Fd_Handler * hdlr EINA_UNUSED)433 _cb_create_prepare(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
434 {
435    Ecore_Wl2_Display *ewd = data;
436 
437    wl_display_flush_clients(ewd->wl.display);
438 }
439 
440 static Eina_Bool
_recovery_timer(Ecore_Wl2_Display * ewd)441 _recovery_timer(Ecore_Wl2_Display *ewd)
442 {
443    if (!_ecore_wl2_display_connect(ewd, 1))
444      return EINA_TRUE;
445 
446    ewd->recovery_timer = NULL;
447    return EINA_FALSE;
448 }
449 
450 static void
_ecore_wl2_display_globals_cleanup(Ecore_Wl2_Display * ewd)451 _ecore_wl2_display_globals_cleanup(Ecore_Wl2_Display *ewd)
452 {
453    if (ewd->wl.session_recovery)
454      zwp_e_session_recovery_destroy(ewd->wl.session_recovery);
455    if (ewd->wl.www) www_destroy(ewd->wl.www);
456    if (ewd->wl.xdg_wm_base) xdg_wm_base_destroy(ewd->wl.xdg_wm_base);
457    if (ewd->wl.zxdg_shell) zxdg_shell_v6_destroy(ewd->wl.zxdg_shell);
458    if (ewd->wl.shm) wl_shm_destroy(ewd->wl.shm);
459    if (ewd->wl.data_device_manager)
460      wl_data_device_manager_destroy(ewd->wl.data_device_manager);
461    if (ewd->wl.compositor) wl_compositor_destroy(ewd->wl.compositor);
462    if (ewd->wl.subcompositor) wl_subcompositor_destroy(ewd->wl.subcompositor);
463    if (ewd->wl.dmabuf) zwp_linux_dmabuf_v1_destroy(ewd->wl.dmabuf);
464    if (ewd->wl.efl_aux_hints) efl_aux_hints_destroy(ewd->wl.efl_aux_hints);
465    if (ewd->wl.efl_hints) efl_hints_destroy(ewd->wl.efl_hints);
466 
467    if (ewd->wl.registry) wl_registry_destroy(ewd->wl.registry);
468 }
469 
470 static void
_recovery_timer_add(Ecore_Wl2_Display * ewd)471 _recovery_timer_add(Ecore_Wl2_Display *ewd)
472 {
473    Eina_Inlist *tmp, *tmp2;
474    Ecore_Wl2_Output *output;
475    Ecore_Wl2_Input *input;
476    Ecore_Wl2_Window *window;
477 
478    eina_hash_free_buckets(ewd->globals);
479 
480    ecore_main_fd_handler_del(ewd->fd_hdl);
481    ewd->fd_hdl = NULL;
482 
483    ewd->shell_done = EINA_FALSE;
484    ewd->sync_done = EINA_FALSE;
485    ewd->recovering = EINA_TRUE;
486 
487    _ecore_wl2_display_globals_cleanup(ewd);
488 
489    memset(&ewd->wl, 0, sizeof(ewd->wl));
490    EINA_INLIST_FOREACH_SAFE(ewd->inputs, tmp, input)
491      _ecore_wl2_input_del(input);
492 
493    EINA_INLIST_FOREACH_SAFE(ewd->outputs, tmp, output)
494      _ecore_wl2_output_del(output);
495 
496    EINA_INLIST_FOREACH_SAFE(ewd->windows, tmp, window)
497      {
498         Ecore_Wl2_Subsurface *subsurf;
499 
500         EINA_INLIST_FOREACH_SAFE(window->subsurfs, tmp2, subsurf)
501           _ecore_wl2_subsurf_unmap(subsurf);
502         _ecore_wl2_window_semi_free(window);
503         window->set_config.serial = 0;
504         window->req_config.serial = 0;
505         window->xdg_configure_ack = NULL;
506         window->xdg_set_min_size = NULL;
507         window->xdg_set_max_size = NULL;
508         window->zxdg_configure_ack = NULL;
509         window->zxdg_set_min_size = NULL;
510         window->zxdg_set_max_size = NULL;
511      }
512 
513    ewd->recovery_timer =
514      ecore_timer_add(0.5, (Ecore_Task_Cb)_recovery_timer, ewd);
515    _ecore_wl2_display_event(ewd, ECORE_WL2_EVENT_DISCONNECT);
516 }
517 
518 static void
_begin_recovery_maybe(Ecore_Wl2_Display * ewd,int code)519 _begin_recovery_maybe(Ecore_Wl2_Display *ewd, int code)
520 {
521    if ((_server_displays || (code != EPROTO)) && ewd->wl.session_recovery)// && (errno == EPIPE))
522      _recovery_timer_add(ewd);
523    else if (!_server_displays)
524      {
525         ERR("Wayland Socket Error: %s", eina_error_msg_get(errno));
526         _ecore_wl2_display_signal_exit();
527      }
528 }
529 
530 static void
_cb_connect_pre(void * data,Ecore_Fd_Handler * hdl EINA_UNUSED)531 _cb_connect_pre(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED)
532 {
533    Ecore_Wl2_Display *ewd = data;
534    int ret = 0, code;
535 
536    while ((wl_display_prepare_read(ewd->wl.display) != 0) && (ret >= 0))
537      ret = wl_display_dispatch_pending(ewd->wl.display);
538 
539    if (ret < 0) goto err;
540 
541    ret = wl_display_get_error(ewd->wl.display);
542    if (ret < 0) goto err;
543 
544    return;
545 
546 err:
547    code = errno;
548    if ((ret < 0) && (code != EAGAIN))
549      {
550         _begin_recovery_maybe(ewd, code);
551      }
552 }
553 
554 static Eina_Bool
_cb_connect_data(void * data,Ecore_Fd_Handler * hdl)555 _cb_connect_data(void *data, Ecore_Fd_Handler *hdl)
556 {
557    Ecore_Wl2_Display *ewd = data;
558    int ret = 0, code;
559 
560    if (ecore_main_fd_handler_active_get(hdl, ECORE_FD_READ))
561      {
562         ret = wl_display_read_events(ewd->wl.display);
563         code = errno;
564         if ((ret < 0) && (code != EAGAIN)) goto err;
565      }
566    else
567      wl_display_cancel_read(ewd->wl.display);
568 
569    wl_display_dispatch_pending(ewd->wl.display);
570    if (ecore_main_fd_handler_active_get(hdl, ECORE_FD_WRITE))
571      {
572         ret = wl_display_flush(ewd->wl.display);
573         code = errno;
574         if (ret >= 0)
575           ecore_main_fd_handler_active_set(hdl, ECORE_FD_READ | ECORE_FD_ALWAYS);
576 
577         if ((ret < 0) && (code != EAGAIN)) goto err;
578      }
579 
580    return ECORE_CALLBACK_RENEW;
581 
582 err:
583    ewd->fd_hdl = NULL;
584    _begin_recovery_maybe(ewd, code);
585 
586    return ECORE_CALLBACK_CANCEL;
587 }
588 
589 static void
_cb_globals_hash_del(void * data)590 _cb_globals_hash_del(void *data)
591 {
592    Ecore_Wl2_Global *global;
593 
594    global = data;
595 
596    eina_stringshare_del(global->interface);
597 
598    free(global);
599 }
600 
601 static Ecore_Wl2_Global *
_ecore_wl2_global_find(Ecore_Wl2_Display * ewd,const char * interface)602 _ecore_wl2_global_find(Ecore_Wl2_Display *ewd, const char *interface)
603 {
604    Eina_Iterator *itr;
605    Ecore_Wl2_Global *global = NULL, *g = NULL;
606 
607    itr = eina_hash_iterator_data_new(ewd->globals);
608    if (!itr) return NULL;
609 
610    EINA_ITERATOR_FOREACH(itr, g)
611      {
612         if (!strcmp(g->interface, interface))
613           {
614              global = g;
615              break;
616           }
617      }
618 
619    eina_iterator_free(itr);
620    return global;
621 }
622 
623 static void
_ecore_wl2_shell_bind(Ecore_Wl2_Display * ewd)624 _ecore_wl2_shell_bind(Ecore_Wl2_Display *ewd)
625 {
626    Ecore_Wl2_Global *global = NULL;
627    const char **itr;
628    const char *shells[] =
629      {
630         "xdg_wm_base",
631         "zxdg_shell_v6",
632         NULL
633      };
634 
635    if (ewd->shell_done) return;
636 
637    for (itr = shells; *itr != NULL; itr++)
638      {
639         global = _ecore_wl2_global_find(ewd, *itr);
640         if (!global) continue;
641         break;
642      }
643 
644    if (!global) return;
645 
646    if (!strcmp(global->interface, "xdg_wm_base"))
647      {
648         ewd->wl.xdg_wm_base =
649           wl_registry_bind(ewd->wl.registry, global->id,
650                            &xdg_wm_base_interface, 1);
651         xdg_wm_base_add_listener(ewd->wl.xdg_wm_base,
652                                    &_xdg_shell_listener, ewd);
653         ewd->shell_done = EINA_TRUE;
654      }
655    else if (!strcmp(global->interface, "zxdg_shell_v6"))
656      {
657         ewd->wl.zxdg_shell =
658           wl_registry_bind(ewd->wl.registry, global->id,
659                            &zxdg_shell_v6_interface, 1);
660         zxdg_shell_v6_add_listener(ewd->wl.zxdg_shell,
661                                    &_zxdg_shell_listener, ewd);
662         ewd->shell_done = EINA_TRUE;
663      }
664 }
665 
666 static void
_cb_sync_done(void * data,struct wl_callback * cb,uint32_t serial EINA_UNUSED)667 _cb_sync_done(void *data, struct wl_callback *cb, uint32_t serial EINA_UNUSED)
668 {
669    Ecore_Wl2_Event_Sync_Done *ev;
670    Ecore_Wl2_Display *ewd;
671 
672    ewd = data;
673    if (--ewd->syncs) return;
674    if (ewd->sync_done) return;
675 
676    ewd->sync_done = EINA_TRUE;
677 
678    _ecore_wl2_shell_bind(ewd);
679 
680    wl_callback_destroy(cb);
681    ecore_wl2_display_flush(ewd);
682 
683    ev = calloc(1, sizeof(Ecore_Wl2_Event_Sync_Done));
684    if (!ev) return;
685 
686    ev->display = ewd;
687    ewd->refs++;
688    ecore_event_add(ECORE_WL2_EVENT_SYNC_DONE, ev, _display_event_free, ewd);
689 }
690 
691 static const struct wl_callback_listener _sync_listener =
692 {
693    _cb_sync_done
694 };
695 
696 static void
_ecore_wl2_display_sync_add(Ecore_Wl2_Display * ewd)697 _ecore_wl2_display_sync_add(Ecore_Wl2_Display *ewd)
698 {
699    struct wl_callback *cb;
700 
701    ewd->syncs++;
702    cb = wl_display_sync(ewd->wl.display);
703    wl_callback_add_listener(cb, &_sync_listener, ewd);
704 }
705 
706 static Eina_Bool
_ecore_wl2_display_connect(Ecore_Wl2_Display * ewd,Eina_Bool sync)707 _ecore_wl2_display_connect(Ecore_Wl2_Display *ewd, Eina_Bool sync)
708 {
709    /* try to connect to wayland display with this name */
710    ewd->wl.display = wl_display_connect(ewd->name);
711    if (!ewd->wl.display) return EINA_FALSE;
712 
713    ewd->recovering = EINA_FALSE;
714 
715    ewd->wl.registry = wl_display_get_registry(ewd->wl.display);
716    wl_registry_add_listener(ewd->wl.registry, &_registry_listener, ewd);
717 
718    _ecore_wl2_display_sync_add(ewd);
719 
720    if (sync)
721      {
722         /* NB: If we are connecting (as a client), then we will need to setup
723          * a callback for display_sync and wait for it to complete. There is no
724          * other option here as we need the compositor, shell, etc, to be setup
725          * before we can allow a user to make use of the API functions */
726         while (!ewd->sync_done)
727           {
728              int ret;
729 
730              ret = wl_display_dispatch(ewd->wl.display);
731              if ((ret < 0) && (errno != EAGAIN))
732                {
733                   ERR("Received Fatal Error on Wayland Display");
734 
735                   wl_registry_destroy(ewd->wl.registry);
736                   return EINA_FALSE;
737                }
738           }
739      }
740 
741    ewd->fd_hdl =
742      ecore_main_fd_handler_add(wl_display_get_fd(ewd->wl.display),
743                                ECORE_FD_READ | ECORE_FD_WRITE | ECORE_FD_ERROR | ECORE_FD_ALWAYS,
744                                _cb_connect_data, ewd, NULL, ewd);
745 
746    ecore_main_fd_handler_prepare_callback_set
747      (ewd->fd_hdl, _cb_connect_pre, ewd);
748 
749    _ecore_wl2_display_event(ewd, ECORE_WL2_EVENT_CONNECT);
750    return EINA_TRUE;
751 }
752 
753 static void
_ecore_wl2_display_cleanup(Ecore_Wl2_Display * ewd)754 _ecore_wl2_display_cleanup(Ecore_Wl2_Display *ewd)
755 {
756    Ecore_Wl2_Output *output;
757    Ecore_Wl2_Input *input;
758    Eina_Inlist *tmp;
759 
760    if (ewd->xkb_context) xkb_context_unref(ewd->xkb_context);
761 
762    /* free each input */
763    EINA_INLIST_FOREACH_SAFE(ewd->inputs, tmp, input)
764      _ecore_wl2_input_del(input);
765 
766    /* free each output */
767    EINA_INLIST_FOREACH_SAFE(ewd->outputs, tmp, output)
768      _ecore_wl2_output_del(output);
769 
770    if (ewd->fd_hdl) ecore_main_fd_handler_del(ewd->fd_hdl);
771 
772    eina_hash_free(ewd->globals);
773 
774    _ecore_wl2_display_globals_cleanup(ewd);
775 }
776 
777 Ecore_Wl2_Window *
_ecore_wl2_display_window_surface_find(Ecore_Wl2_Display * display,struct wl_surface * wl_surface)778 _ecore_wl2_display_window_surface_find(Ecore_Wl2_Display *display, struct wl_surface *wl_surface)
779 {
780    Ecore_Wl2_Window *window;
781 
782    if ((!display) || (!wl_surface)) return NULL;
783 
784    EINA_INLIST_FOREACH(display->windows, window)
785      {
786         if ((window->surface) &&
787             (window->surface == wl_surface))
788           return window;
789      }
790 
791    return NULL;
792 }
793 
794 EAPI Ecore_Wl2_Display *
ecore_wl2_display_create(const char * name)795 ecore_wl2_display_create(const char *name)
796 {
797    Ecore_Wl2_Display *ewd;
798    struct wl_event_loop *loop;
799 
800    if (!_server_displays)
801      _server_displays = eina_hash_string_superfast_new(NULL);
802 
803    if (name)
804      {
805         /* someone wants to create a server with a specific display */
806 
807         /* check hash of cached server displays for this name */
808         ewd = eina_hash_find(_server_displays, name);
809         if (ewd) goto found;
810      }
811 
812    /* allocate space for display structure */
813    ewd = calloc(1, sizeof(Ecore_Wl2_Display));
814    if (!ewd) return NULL;
815 
816    ewd->refs++;
817    ewd->pid = getpid();
818 
819    /* try to create new wayland display */
820    ewd->wl.display = wl_display_create();
821    if (!ewd->wl.display)
822      {
823         ERR("Could not create wayland display");
824         goto create_err;
825      }
826 
827    if (!name)
828      {
829         const char *n;
830 
831         n = wl_display_add_socket_auto(ewd->wl.display);
832         if (!n)
833           {
834              ERR("Failed to add display socket");
835              goto socket_err;
836           }
837 
838         ewd->name = strdup(n);
839      }
840    else
841      {
842         if (wl_display_add_socket(ewd->wl.display, name))
843           {
844              ERR("Failed to add display socket");
845              goto socket_err;
846           }
847 
848         ewd->name = strdup(name);
849      }
850 
851    setenv("WAYLAND_DISPLAY", ewd->name, 1);
852    DBG("WAYLAND_DISPLAY: %s", ewd->name);
853 
854    loop = wl_display_get_event_loop(ewd->wl.display);
855 
856    ewd->fd_hdl =
857      ecore_main_fd_handler_add(wl_event_loop_get_fd(loop),
858                                ECORE_FD_READ | ECORE_FD_ERROR,
859                                _cb_create_data, ewd, NULL, NULL);
860 
861    ecore_main_fd_handler_prepare_callback_set(ewd->fd_hdl,
862                                               _cb_create_prepare, ewd);
863 
864    /* add this new server display to hash */
865    eina_hash_add(_server_displays, ewd->name, ewd);
866 
867    return ewd;
868 
869 socket_err:
870    wl_display_destroy(ewd->wl.display);
871 
872 create_err:
873    free(ewd);
874    return NULL;
875 
876 found:
877    ewd->refs++;
878    return ewd;
879 }
880 
881 Eina_Bool
_ecore_wl2_display_sync_get(void)882 _ecore_wl2_display_sync_get(void)
883 {
884    return !_server_displays || !eina_hash_population(_server_displays);
885 }
886 
887 EAPI Ecore_Wl2_Display *
ecore_wl2_display_connect(const char * name)888 ecore_wl2_display_connect(const char *name)
889 {
890    Ecore_Wl2_Display *ewd;
891    const char *n;
892    Eina_Bool hash_create = !_client_displays;
893 
894    if (!_client_displays)
895      _client_displays = eina_hash_string_superfast_new(NULL);
896 
897    if (!name)
898      {
899         /* client wants to connect to default display */
900         n = getenv("WAYLAND_DISPLAY");
901         if (!n) n = "wayland-0";
902 
903         /* we have a default wayland display */
904 
905         /* check hash of cached client displays for this name */
906         ewd = eina_hash_find(_client_displays, n);
907         if (ewd) goto found;
908      }
909    else
910      {
911         /* client wants to connect to specific display */
912 
913         /* check hash of cached client displays for this name */
914         ewd = eina_hash_find(_client_displays, name);
915         if (ewd) goto found;
916      }
917 
918    /* allocate space for display structure */
919    ewd = calloc(1, sizeof(Ecore_Wl2_Display));
920    if (!ewd) return NULL;
921 
922    ewd->refs++;
923 
924    if (name)
925      ewd->name = strdup(name);
926    else if (n)
927      ewd->name = strdup(n);
928 
929    ewd->globals = eina_hash_int32_new(_cb_globals_hash_del);
930 
931    ewd->xkb_context = xkb_context_new(0);
932    if (!ewd->xkb_context) goto context_err;
933 
934    /* check server display hash and match on pid. If match, skip sync */
935    if (!_ecore_wl2_display_connect(ewd, _ecore_wl2_display_sync_get()))
936      goto connect_err;
937 
938    /* add this new client display to hash */
939    eina_hash_add(_client_displays, ewd->name, ewd);
940 
941    return ewd;
942 
943 connect_err:
944    xkb_context_unref(ewd->xkb_context);
945    ewd->xkb_context = NULL;
946 
947 context_err:
948    eina_hash_free(ewd->globals);
949    free(ewd->name);
950    free(ewd);
951 
952    if (hash_create)
953      {
954         eina_hash_free(_client_displays);
955         _client_displays = NULL;
956      }
957    return NULL;
958 
959 found:
960    ewd->refs++;
961    return ewd;
962 }
963 
964 EAPI void
ecore_wl2_display_disconnect(Ecore_Wl2_Display * display)965 ecore_wl2_display_disconnect(Ecore_Wl2_Display *display)
966 {
967    EINA_SAFETY_ON_NULL_RETURN(display);
968    int ret;
969 
970    do
971      {
972         ret = wl_display_dispatch_pending(display->wl.display);
973      } while (ret > 0);
974 
975    --display->refs;
976    if (display->refs == 0)
977      {
978         _ecore_wl2_display_cleanup(display);
979 
980         wl_display_disconnect(display->wl.display);
981 
982         /* remove this client display from hash */
983         eina_hash_del_by_key(_client_displays, display->name);
984 
985         free(display->name);
986         free(display);
987      }
988 }
989 
990 EAPI void
ecore_wl2_display_destroy(Ecore_Wl2_Display * display)991 ecore_wl2_display_destroy(Ecore_Wl2_Display *display)
992 {
993    EINA_SAFETY_ON_NULL_RETURN(display);
994 
995    --display->refs;
996    if (display->refs == 0)
997      {
998         /* this ensures that things like wl_registry are destroyed
999          * before we destroy the actual wl_display */
1000         _ecore_wl2_display_cleanup(display);
1001 
1002         wl_display_destroy(display->wl.display);
1003 
1004         /* remove this client display from hash */
1005         eina_hash_del_by_key(_server_displays, display->name);
1006         ecore_timer_del(display->recovery_timer);
1007 
1008         free(display->name);
1009         free(display);
1010      }
1011 }
1012 
1013 EAPI void
ecore_wl2_display_terminate(Ecore_Wl2_Display * display)1014 ecore_wl2_display_terminate(Ecore_Wl2_Display *display)
1015 {
1016    EINA_SAFETY_ON_NULL_RETURN(display);
1017    wl_display_terminate(display->wl.display);
1018 }
1019 
1020 EAPI struct wl_display *
ecore_wl2_display_get(Ecore_Wl2_Display * display)1021 ecore_wl2_display_get(Ecore_Wl2_Display *display)
1022 {
1023    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1024    return display->wl.display;
1025 }
1026 
1027 EAPI struct wl_shm *
ecore_wl2_display_shm_get(Ecore_Wl2_Display * display)1028 ecore_wl2_display_shm_get(Ecore_Wl2_Display *display)
1029 {
1030    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1031    return display->wl.shm;
1032 }
1033 
1034 EAPI void *
ecore_wl2_display_dmabuf_get(Ecore_Wl2_Display * display)1035 ecore_wl2_display_dmabuf_get(Ecore_Wl2_Display *display)
1036 {
1037    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1038    return display->wl.dmabuf;
1039 }
1040 
1041 EAPI Eina_Iterator *
ecore_wl2_display_globals_get(Ecore_Wl2_Display * display)1042 ecore_wl2_display_globals_get(Ecore_Wl2_Display *display)
1043 {
1044    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1045    EINA_SAFETY_ON_NULL_RETURN_VAL(display->globals, NULL);
1046 
1047    return eina_hash_iterator_data_new(display->globals);
1048 }
1049 
1050 EAPI void
ecore_wl2_display_screen_size_get(Ecore_Wl2_Display * display,int * w,int * h)1051 ecore_wl2_display_screen_size_get(Ecore_Wl2_Display *display, int *w, int *h)
1052 {
1053    Ecore_Wl2_Output *output;
1054    int ow = 0, oh = 0;
1055 
1056    EINA_SAFETY_ON_NULL_RETURN(display);
1057 
1058    if (w) *w = 0;
1059    if (h) *h = 0;
1060 
1061    EINA_INLIST_FOREACH(display->outputs, output)
1062      {
1063         switch (output->transform)
1064           {
1065            case WL_OUTPUT_TRANSFORM_90:
1066            case WL_OUTPUT_TRANSFORM_270:
1067            case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1068            case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1069              ow += output->geometry.h;
1070              oh += output->geometry.w;
1071              break;
1072            default:
1073              ow += output->geometry.w;
1074              oh += output->geometry.h;
1075              break;
1076           }
1077      }
1078 
1079    if (w) *w = ow;
1080    if (h) *h = oh;
1081 }
1082 
1083 EAPI struct wl_registry *
ecore_wl2_display_registry_get(Ecore_Wl2_Display * display)1084 ecore_wl2_display_registry_get(Ecore_Wl2_Display *display)
1085 {
1086    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1087 
1088    return display->wl.registry;
1089 }
1090 
1091 EAPI int
ecore_wl2_display_compositor_version_get(Ecore_Wl2_Display * display)1092 ecore_wl2_display_compositor_version_get(Ecore_Wl2_Display *display)
1093 {
1094    EINA_SAFETY_ON_NULL_RETURN_VAL(display, 0);
1095 
1096    return display->wl.compositor_version;
1097 }
1098 
1099 EAPI Eina_Iterator *
ecore_wl2_display_inputs_get(Ecore_Wl2_Display * display)1100 ecore_wl2_display_inputs_get(Ecore_Wl2_Display *display)
1101 {
1102    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1103    EINA_SAFETY_ON_TRUE_RETURN_VAL(display->pid, NULL);
1104    return eina_inlist_iterator_new(display->inputs);
1105 }
1106 
1107 EAPI Ecore_Wl2_Input *
ecore_wl2_display_input_find(const Ecore_Wl2_Display * display,unsigned int id)1108 ecore_wl2_display_input_find(const Ecore_Wl2_Display *display, unsigned int id)
1109 {
1110    Ecore_Wl2_Input *input;
1111 
1112    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1113    EINA_SAFETY_ON_TRUE_RETURN_VAL(display->pid, NULL);
1114    EINA_INLIST_FOREACH(display->inputs, input)
1115      if (input->id == id) return input;
1116    return NULL;
1117 }
1118 
1119 EAPI Ecore_Wl2_Input *
ecore_wl2_display_input_find_by_name(const Ecore_Wl2_Display * display,const char * name)1120 ecore_wl2_display_input_find_by_name(const Ecore_Wl2_Display *display, const char *name)
1121 {
1122    Ecore_Wl2_Input *input;
1123 
1124    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1125    EINA_SAFETY_ON_TRUE_RETURN_VAL(display->pid, NULL);
1126    EINA_INLIST_FOREACH(display->inputs, input)
1127      if (eina_streq(input->name, name)) return input;
1128    return NULL;
1129 }
1130 
1131 EAPI Eina_Bool
ecore_wl2_display_sync_is_done(const Ecore_Wl2_Display * display)1132 ecore_wl2_display_sync_is_done(const Ecore_Wl2_Display *display)
1133 {
1134    EINA_SAFETY_ON_NULL_RETURN_VAL(display, EINA_FALSE);
1135    return display->sync_done;
1136 }
1137 
1138 EAPI const char *
ecore_wl2_display_name_get(const Ecore_Wl2_Display * display)1139 ecore_wl2_display_name_get(const Ecore_Wl2_Display *display)
1140 {
1141    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1142    return display->name;
1143 }
1144 
1145 EAPI void
ecore_wl2_display_flush(Ecore_Wl2_Display * display)1146 ecore_wl2_display_flush(Ecore_Wl2_Display *display)
1147 {
1148    int ret, code;
1149 
1150    EINA_SAFETY_ON_NULL_RETURN(display);
1151 
1152    ret = wl_display_flush(display->wl.display);
1153    if (ret >= 0) return;
1154 
1155    code = errno;
1156    if (code == EAGAIN)
1157      {
1158         ecore_main_fd_handler_active_set(display->fd_hdl,
1159                                          (ECORE_FD_READ | ECORE_FD_WRITE | ECORE_FD_ALWAYS));
1160         return;
1161      }
1162 
1163    _begin_recovery_maybe(display, code);
1164 }
1165 
1166 EAPI Ecore_Wl2_Window *
ecore_wl2_display_window_find_by_surface(Ecore_Wl2_Display * display,struct wl_surface * surface)1167 ecore_wl2_display_window_find_by_surface(Ecore_Wl2_Display *display, struct wl_surface *surface)
1168 {
1169    return _ecore_wl2_display_window_surface_find(display, surface);
1170 }
1171 
1172 EAPI Ecore_Wl2_Display *
ecore_wl2_connected_display_get(const char * name)1173 ecore_wl2_connected_display_get(const char *name)
1174 {
1175    Ecore_Wl2_Display *ewd;
1176 
1177    EINA_SAFETY_ON_NULL_RETURN_VAL(_client_displays, NULL);
1178 
1179    if (!name)
1180      {
1181         const char *n;
1182 
1183         /* client wants to connected to default display */
1184         n = getenv("WAYLAND_DISPLAY");
1185         if (!n) n = "wayland-0";
1186 
1187         /* we have a default wayland display */
1188 
1189         /* check hash of cached client displays for this name */
1190         ewd = eina_hash_find(_client_displays, n);
1191      }
1192    else
1193      {
1194         /* client wants to connect to specific display */
1195 
1196         /* check hash of cached client displays for this name */
1197         ewd = eina_hash_find(_client_displays, name);
1198      }
1199 
1200    return ewd;
1201 }
1202 
1203 EAPI struct wl_compositor *
ecore_wl2_display_compositor_get(Ecore_Wl2_Display * display)1204 ecore_wl2_display_compositor_get(Ecore_Wl2_Display *display)
1205 {
1206    EINA_SAFETY_ON_NULL_RETURN_VAL(display, NULL);
1207    return display->wl.compositor;
1208 }
1209