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