1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2011 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  *
29  * Authors:
30  *   Robert Bragg <robert@linux.intel.com>
31  *   Neil Roberts <neil@linux.intel.com>
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <wayland-client.h>
39 #include <wayland-egl.h>
40 #include <string.h>
41 #include <errno.h>
42 
43 #include "cogl-winsys-egl-wayland-private.h"
44 #include "cogl-winsys-egl-private.h"
45 #include "cogl-renderer-private.h"
46 #include "cogl-onscreen-private.h"
47 #include "cogl-wayland-renderer.h"
48 #include "cogl-error-private.h"
49 #include "cogl-poll-private.h"
50 #include "cogl-frame-info-private.h"
51 
52 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
53 
54 static const CoglWinsysVtable *parent_vtable;
55 
56 typedef struct _CoglRendererWayland
57 {
58   struct wl_display *wayland_display;
59   struct wl_compositor *wayland_compositor;
60   struct wl_shell *wayland_shell;
61   struct wl_registry *wayland_registry;
62   int fd;
63 } CoglRendererWayland;
64 
65 typedef struct _CoglDisplayWayland
66 {
67   struct wl_surface *dummy_wayland_surface;
68   struct wl_egl_window *dummy_wayland_egl_native_window;
69 } CoglDisplayWayland;
70 
71 typedef struct _CoglOnscreenWayland
72 {
73   struct wl_egl_window *wayland_egl_native_window;
74   struct wl_surface *wayland_surface;
75   struct wl_shell_surface *wayland_shell_surface;
76 
77   /* Resizing a wayland framebuffer doesn't take affect
78    * until the next swap buffers request, so we have to
79    * track the resize geometry until then... */
80   int pending_width;
81   int pending_height;
82   int pending_dx;
83   int pending_dy;
84   CoglBool has_pending;
85 
86   CoglBool shell_surface_type_set;
87 
88   CoglList frame_callbacks;
89 } CoglOnscreenWayland;
90 
91 typedef struct
92 {
93   CoglList link;
94   CoglFrameInfo *frame_info;
95   struct wl_callback *callback;
96   CoglOnscreen *onscreen;
97 } FrameCallbackData;
98 
99 static void
registry_handle_global_cb(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)100 registry_handle_global_cb (void *data,
101                            struct wl_registry *registry,
102                            uint32_t id,
103                            const char *interface,
104                            uint32_t version)
105 {
106   CoglRendererEGL *egl_renderer = (CoglRendererEGL *)data;
107   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
108 
109   if (strcmp (interface, "wl_compositor") == 0)
110     wayland_renderer->wayland_compositor =
111       wl_registry_bind (registry, id, &wl_compositor_interface, 1);
112   else if (strcmp(interface, "wl_shell") == 0)
113     wayland_renderer->wayland_shell =
114       wl_registry_bind (registry, id, &wl_shell_interface, 1);
115 }
116 
117 static void
registry_handle_global_remove_cb(void * data,struct wl_registry * registry,uint32_t name)118 registry_handle_global_remove_cb (void *data,
119                                   struct wl_registry *registry,
120                                   uint32_t name)
121 {
122   /* Nothing to do for now */
123 }
124 
125 static void
_cogl_winsys_renderer_disconnect(CoglRenderer * renderer)126 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
127 {
128   CoglRendererEGL *egl_renderer = renderer->winsys;
129   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
130 
131   if (egl_renderer->edpy)
132     eglTerminate (egl_renderer->edpy);
133 
134   if (wayland_renderer->wayland_display)
135     {
136       _cogl_poll_renderer_remove_fd (renderer, wayland_renderer->fd);
137 
138       if (renderer->foreign_wayland_display == NULL)
139         wl_display_disconnect (wayland_renderer->wayland_display);
140     }
141 
142   g_slice_free (CoglRendererWayland, egl_renderer->platform);
143   g_slice_free (CoglRendererEGL, egl_renderer);
144 }
145 
146 static const struct wl_registry_listener registry_listener = {
147   registry_handle_global_cb,
148   registry_handle_global_remove_cb
149 };
150 
151 static int64_t
prepare_wayland_display_events(void * user_data)152 prepare_wayland_display_events (void *user_data)
153 {
154   CoglRenderer *renderer = user_data;
155   CoglRendererEGL *egl_renderer = renderer->winsys;
156   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
157   int flush_ret;
158 
159   flush_ret = wl_display_flush (wayland_renderer->wayland_display);
160 
161   if (flush_ret == -1)
162     {
163       /* If the socket buffer became full then we need to wake up the
164        * main loop once it is writable again */
165       if (errno == EAGAIN)
166         {
167           _cogl_poll_renderer_modify_fd (renderer,
168                                          wayland_renderer->fd,
169                                          COGL_POLL_FD_EVENT_IN |
170                                          COGL_POLL_FD_EVENT_OUT);
171         }
172       else if (errno != EINTR)
173         {
174           /* If the flush failed for some other reason then it's
175            * likely that it's going to consistently fail so we'll stop
176            * waiting on the file descriptor instead of making the
177            * application take up 100% CPU. FIXME: it would be nice if
178            * there was some way to report this to the application so
179            * that it can quit or recover */
180           _cogl_poll_renderer_remove_fd (renderer, wayland_renderer->fd);
181         }
182     }
183 
184   /* Calling this here is a bit dodgy because Cogl usually tries to
185    * say that it won't do any event processing until
186    * cogl_poll_renderer_dispatch is called. However Wayland doesn't
187    * seem to provide any way to query whether the event queue is empty
188    * and we would need to do that in order to force the main loop to
189    * wake up to call it from dispatch. */
190   wl_display_dispatch_pending (wayland_renderer->wayland_display);
191 
192   return -1;
193 }
194 
195 static void
dispatch_wayland_display_events(void * user_data,int revents)196 dispatch_wayland_display_events (void *user_data, int revents)
197 {
198   CoglRenderer *renderer = user_data;
199   CoglRendererEGL *egl_renderer = renderer->winsys;
200   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
201 
202   if ((revents & COGL_POLL_FD_EVENT_IN))
203     {
204       if (wl_display_dispatch (wayland_renderer->wayland_display) == -1 &&
205           errno != EAGAIN &&
206           errno != EINTR)
207         goto socket_error;
208     }
209 
210   if ((revents & COGL_POLL_FD_EVENT_OUT))
211     {
212       int ret = wl_display_flush (wayland_renderer->wayland_display);
213 
214       if (ret == -1)
215         {
216           if (errno != EAGAIN && errno != EINTR)
217             goto socket_error;
218         }
219       else
220         {
221           /* There is no more data to write so we don't need to wake
222            * up when the write buffer is emptied anymore */
223           _cogl_poll_renderer_modify_fd (renderer,
224                                          wayland_renderer->fd,
225                                          COGL_POLL_FD_EVENT_IN);
226         }
227     }
228 
229   return;
230 
231  socket_error:
232   /* If there was an error on the wayland socket then it's likely that
233    * it's going to consistently fail so we'll stop waiting on the file
234    * descriptor instead of making the application take up 100% CPU.
235    * FIXME: it would be nice if there was some way to report this to
236    * the application so that it can quit or recover */
237   _cogl_poll_renderer_remove_fd (renderer, wayland_renderer->fd);
238 }
239 
240 static CoglBool
_cogl_winsys_renderer_connect(CoglRenderer * renderer,CoglError ** error)241 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
242                                CoglError **error)
243 {
244   CoglRendererEGL *egl_renderer;
245   CoglRendererWayland *wayland_renderer;
246 
247   renderer->winsys = g_slice_new0 (CoglRendererEGL);
248   egl_renderer = renderer->winsys;
249   wayland_renderer = g_slice_new0 (CoglRendererWayland);
250   egl_renderer->platform = wayland_renderer;
251 
252   egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
253 
254   if (renderer->foreign_wayland_display)
255     {
256       wayland_renderer->wayland_display = renderer->foreign_wayland_display;
257     }
258   else
259     {
260       wayland_renderer->wayland_display = wl_display_connect (NULL);
261       if (!wayland_renderer->wayland_display)
262         {
263           _cogl_set_error (error, COGL_WINSYS_ERROR,
264                        COGL_WINSYS_ERROR_INIT,
265                        "Failed to connect wayland display");
266           goto error;
267         }
268     }
269 
270   wayland_renderer->wayland_registry =
271     wl_display_get_registry (wayland_renderer->wayland_display);
272 
273   wl_registry_add_listener (wayland_renderer->wayland_registry,
274                             &registry_listener,
275                             egl_renderer);
276 
277   /*
278    * Ensure that that we've received the messages setting up the
279    * compostor and shell object.
280    */
281   wl_display_roundtrip (wayland_renderer->wayland_display);
282   if (!wayland_renderer->wayland_compositor)
283     {
284       _cogl_set_error (error,
285                        COGL_WINSYS_ERROR,
286                        COGL_WINSYS_ERROR_INIT,
287                        "Unable to find wl_compositor");
288       goto error;
289     }
290 
291   egl_renderer->edpy =
292     eglGetDisplay ((EGLNativeDisplayType) wayland_renderer->wayland_display);
293 
294   if (!_cogl_winsys_egl_renderer_connect_common (renderer, error))
295     goto error;
296 
297   wayland_renderer->fd = wl_display_get_fd (wayland_renderer->wayland_display);
298 
299   if (renderer->wayland_enable_event_dispatch)
300     _cogl_poll_renderer_add_fd (renderer,
301                                 wayland_renderer->fd,
302                                 COGL_POLL_FD_EVENT_IN,
303                                 prepare_wayland_display_events,
304                                 dispatch_wayland_display_events,
305                                 renderer);
306 
307   return TRUE;
308 
309 error:
310   _cogl_winsys_renderer_disconnect (renderer);
311   return FALSE;
312 }
313 
314 static CoglBool
_cogl_winsys_egl_display_setup(CoglDisplay * display,CoglError ** error)315 _cogl_winsys_egl_display_setup (CoglDisplay *display,
316                                 CoglError **error)
317 {
318   CoglDisplayEGL *egl_display = display->winsys;
319   CoglDisplayWayland *wayland_display;
320 
321   wayland_display = g_slice_new0 (CoglDisplayWayland);
322   egl_display->platform = wayland_display;
323 
324   return TRUE;
325 }
326 
327 static void
_cogl_winsys_egl_display_destroy(CoglDisplay * display)328 _cogl_winsys_egl_display_destroy (CoglDisplay *display)
329 {
330   CoglDisplayEGL *egl_display = display->winsys;
331 
332   g_slice_free (CoglDisplayWayland, egl_display->platform);
333 }
334 
335 static CoglBool
make_dummy_surface(CoglDisplay * display,CoglError ** error)336 make_dummy_surface (CoglDisplay *display,
337                     CoglError **error)
338 {
339   CoglRenderer *renderer = display->renderer;
340   CoglRendererEGL *egl_renderer = renderer->winsys;
341   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
342   CoglDisplayEGL *egl_display = display->winsys;
343   CoglDisplayWayland *wayland_display = egl_display->platform;
344   const char *error_message;
345 
346   wayland_display->dummy_wayland_surface =
347     wl_compositor_create_surface (wayland_renderer->wayland_compositor);
348   if (!wayland_display->dummy_wayland_surface)
349     {
350       error_message= "Failed to create a dummy wayland surface";
351       goto fail;
352     }
353 
354   wayland_display->dummy_wayland_egl_native_window =
355     wl_egl_window_create (wayland_display->dummy_wayland_surface,
356                           1,
357                           1);
358   if (!wayland_display->dummy_wayland_egl_native_window)
359     {
360       error_message= "Failed to create a dummy wayland native egl surface";
361       goto fail;
362     }
363 
364   egl_display->dummy_surface =
365     eglCreateWindowSurface (egl_renderer->edpy,
366                             egl_display->egl_config,
367                             (EGLNativeWindowType)
368                             wayland_display->dummy_wayland_egl_native_window,
369                             NULL);
370   if (egl_display->dummy_surface == EGL_NO_SURFACE)
371     {
372       error_message= "Unable to create dummy window surface";
373       goto fail;
374     }
375 
376   return TRUE;
377 
378  fail:
379   _cogl_set_error (error, COGL_WINSYS_ERROR,
380                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
381                    "%s", error_message);
382 
383   return FALSE;
384 }
385 
386 static CoglBool
_cogl_winsys_egl_context_created(CoglDisplay * display,CoglError ** error)387 _cogl_winsys_egl_context_created (CoglDisplay *display,
388                                   CoglError **error)
389 {
390   CoglRenderer *renderer = display->renderer;
391   CoglRendererEGL *egl_renderer = renderer->winsys;
392   CoglDisplayEGL *egl_display = display->winsys;
393 
394   if ((egl_renderer->private_features &
395        COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0 &&
396       !make_dummy_surface(display, error))
397     return FALSE;
398 
399   if (!_cogl_winsys_egl_make_current (display,
400                                       egl_display->dummy_surface,
401                                       egl_display->dummy_surface,
402                                       egl_display->egl_context))
403     {
404       _cogl_set_error (error,
405                        COGL_WINSYS_ERROR,
406                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
407                        "%s",
408                        "Unable to eglMakeCurrent with dummy surface");
409     }
410 
411   return TRUE;
412 }
413 
414 static void
_cogl_winsys_egl_cleanup_context(CoglDisplay * display)415 _cogl_winsys_egl_cleanup_context (CoglDisplay *display)
416 {
417   CoglRenderer *renderer = display->renderer;
418   CoglRendererEGL *egl_renderer = renderer->winsys;
419   CoglDisplayEGL *egl_display = display->winsys;
420   CoglDisplayWayland *wayland_display = egl_display->platform;
421 
422   if (egl_display->dummy_surface != EGL_NO_SURFACE)
423     {
424       eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface);
425       egl_display->dummy_surface = EGL_NO_SURFACE;
426     }
427 
428   if (wayland_display->dummy_wayland_egl_native_window)
429     {
430       wl_egl_window_destroy (wayland_display->dummy_wayland_egl_native_window);
431       wayland_display->dummy_wayland_egl_native_window = NULL;
432     }
433 
434   if (wayland_display->dummy_wayland_surface)
435     {
436       wl_surface_destroy (wayland_display->dummy_wayland_surface);
437       wayland_display->dummy_wayland_surface = NULL;
438     }
439 }
440 
441 static CoglBool
_cogl_winsys_egl_context_init(CoglContext * context,CoglError ** error)442 _cogl_winsys_egl_context_init (CoglContext *context,
443                                CoglError **error)
444 {
445   context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE;
446   COGL_FLAGS_SET (context->features,
447                   COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE);
448   COGL_FLAGS_SET (context->winsys_features,
449                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
450                   TRUE);
451   COGL_FLAGS_SET (context->winsys_features,
452                   COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
453                   TRUE);
454 
455   /* We'll manually handle queueing dirty events when the surface is
456    * first shown or when it is resized. Note that this is slightly
457    * different from the emulated behaviour that CoglFramebuffer would
458    * provide if we didn't set this flag because we want to emit the
459    * event on show instead of on allocation. The Wayland protocol
460    * delays setting the surface type until the next buffer is attached
461    * so attaching a buffer before setting the type would not cause
462    * anything to be displayed */
463   COGL_FLAGS_SET (context->private_features,
464                   COGL_PRIVATE_FEATURE_DIRTY_EVENTS,
465                   TRUE);
466 
467   return TRUE;
468 }
469 
470 static CoglBool
_cogl_winsys_egl_onscreen_init(CoglOnscreen * onscreen,EGLConfig egl_config,CoglError ** error)471 _cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen,
472                                 EGLConfig egl_config,
473                                 CoglError **error)
474 {
475   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
476   CoglOnscreenWayland *wayland_onscreen;
477   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
478   CoglContext *context = framebuffer->context;
479   CoglRenderer *renderer = context->display->renderer;
480   CoglRendererEGL *egl_renderer = renderer->winsys;
481   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
482 
483   wayland_onscreen = g_slice_new0 (CoglOnscreenWayland);
484   egl_onscreen->platform = wayland_onscreen;
485 
486   _cogl_list_init (&wayland_onscreen->frame_callbacks);
487 
488   if (onscreen->foreign_surface)
489     wayland_onscreen->wayland_surface = onscreen->foreign_surface;
490   else
491     wayland_onscreen->wayland_surface =
492       wl_compositor_create_surface (wayland_renderer->wayland_compositor);
493 
494   if (!wayland_onscreen->wayland_surface)
495     {
496       _cogl_set_error (error, COGL_WINSYS_ERROR,
497                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
498                    "Error while creating wayland surface for CoglOnscreen");
499       return FALSE;
500     }
501 
502   wayland_onscreen->wayland_egl_native_window =
503     wl_egl_window_create (wayland_onscreen->wayland_surface,
504                           cogl_framebuffer_get_width (framebuffer),
505                           cogl_framebuffer_get_height (framebuffer));
506   if (!wayland_onscreen->wayland_egl_native_window)
507     {
508       _cogl_set_error (error, COGL_WINSYS_ERROR,
509                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
510                    "Error while creating wayland egl native window "
511                    "for CoglOnscreen");
512       return FALSE;
513     }
514 
515   egl_onscreen->egl_surface =
516     eglCreateWindowSurface (egl_renderer->edpy,
517                             egl_config,
518                             (EGLNativeWindowType)
519                             wayland_onscreen->wayland_egl_native_window,
520                             NULL);
521 
522   if (!onscreen->foreign_surface)
523     {
524       if (!wayland_renderer->wayland_shell)
525         {
526           _cogl_set_error (error, COGL_WINSYS_ERROR,
527               COGL_WINSYS_ERROR_CREATE_ONSCREEN,
528               "No foreign surface, and wl_shell unsupported by the compositor");
529           return FALSE;
530         }
531 
532       wayland_onscreen->wayland_shell_surface =
533         wl_shell_get_shell_surface (wayland_renderer->wayland_shell,
534                                     wayland_onscreen->wayland_surface);
535     }
536 
537   return TRUE;
538 }
539 
540 static void
free_frame_callback_data(FrameCallbackData * callback_data)541 free_frame_callback_data (FrameCallbackData *callback_data)
542 {
543   cogl_object_unref (callback_data->frame_info);
544   wl_callback_destroy (callback_data->callback);
545   _cogl_list_remove (&callback_data->link);
546   g_slice_free (FrameCallbackData, callback_data);
547 }
548 
549 static void
_cogl_winsys_egl_onscreen_deinit(CoglOnscreen * onscreen)550 _cogl_winsys_egl_onscreen_deinit (CoglOnscreen *onscreen)
551 {
552   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
553   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
554   FrameCallbackData *frame_callback_data, *tmp;
555 
556   _cogl_list_for_each_safe (frame_callback_data,
557                             tmp,
558                             &wayland_onscreen->frame_callbacks,
559                             link)
560     free_frame_callback_data (frame_callback_data);
561 
562   if (wayland_onscreen->wayland_egl_native_window)
563     {
564       wl_egl_window_destroy (wayland_onscreen->wayland_egl_native_window);
565       wayland_onscreen->wayland_egl_native_window = NULL;
566     }
567 
568   if (!onscreen->foreign_surface)
569     {
570       /* NB: The wayland protocol docs explicitly state that
571        * "wl_shell_surface_destroy() must be called before destroying
572        * the wl_surface object." ... */
573       if (wayland_onscreen->wayland_shell_surface)
574         {
575           wl_shell_surface_destroy (wayland_onscreen->wayland_shell_surface);
576           wayland_onscreen->wayland_shell_surface = NULL;
577         }
578 
579       if (wayland_onscreen->wayland_surface)
580         {
581           wl_surface_destroy (wayland_onscreen->wayland_surface);
582           wayland_onscreen->wayland_surface = NULL;
583         }
584     }
585 
586   g_slice_free (CoglOnscreenWayland, wayland_onscreen);
587 }
588 
589 static void
flush_pending_resize(CoglOnscreen * onscreen)590 flush_pending_resize (CoglOnscreen *onscreen)
591 {
592   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
593   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
594 
595   if (wayland_onscreen->has_pending)
596     {
597       wl_egl_window_resize (wayland_onscreen->wayland_egl_native_window,
598                             wayland_onscreen->pending_width,
599                             wayland_onscreen->pending_height,
600                             wayland_onscreen->pending_dx,
601                             wayland_onscreen->pending_dy);
602 
603       _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (onscreen),
604                                             wayland_onscreen->pending_width,
605                                             wayland_onscreen->pending_height);
606 
607       _cogl_onscreen_queue_full_dirty (onscreen);
608 
609       wayland_onscreen->pending_dx = 0;
610       wayland_onscreen->pending_dy = 0;
611       wayland_onscreen->has_pending = FALSE;
612     }
613 }
614 
615 static void
frame_cb(void * data,struct wl_callback * callback,uint32_t time)616 frame_cb (void *data,
617           struct wl_callback *callback,
618           uint32_t time)
619 {
620   FrameCallbackData *callback_data = data;
621   CoglFrameInfo *info = callback_data->frame_info;
622   CoglOnscreen *onscreen = callback_data->onscreen;
623 
624   g_assert (callback_data->callback == callback);
625 
626   _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
627   _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
628 
629   free_frame_callback_data (callback_data);
630 }
631 
632 static const struct wl_callback_listener
633 frame_listener =
634 {
635   frame_cb
636 };
637 
638 static void
_cogl_winsys_onscreen_swap_buffers_with_damage(CoglOnscreen * onscreen,const int * rectangles,int n_rectangles)639 _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
640                                                 const int *rectangles,
641                                                 int n_rectangles)
642 {
643   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
644   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
645   FrameCallbackData *frame_callback_data = g_slice_new (FrameCallbackData);
646 
647   flush_pending_resize (onscreen);
648 
649   /* Before calling the winsys function,
650    * cogl_onscreen_swap_buffers_with_damage() will have pushed the
651    * frame info object onto the end of the pending frames. We can grab
652    * it out of the queue now because we don't care about the order and
653    * we will just directly queue the event corresponding to the exact
654    * frame that Wayland reports as completed. This will steal the
655    * reference */
656   frame_callback_data->frame_info =
657     g_queue_pop_tail (&onscreen->pending_frame_infos);
658   frame_callback_data->onscreen = onscreen;
659 
660   frame_callback_data->callback =
661     wl_surface_frame (wayland_onscreen->wayland_surface);
662   wl_callback_add_listener (frame_callback_data->callback,
663                             &frame_listener,
664                             frame_callback_data);
665 
666   _cogl_list_insert (&wayland_onscreen->frame_callbacks,
667                      &frame_callback_data->link);
668 
669   parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
670                                                     rectangles,
671                                                     n_rectangles);
672 }
673 
674 static void
_cogl_winsys_onscreen_set_visibility(CoglOnscreen * onscreen,CoglBool visibility)675 _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
676                                       CoglBool visibility)
677 {
678   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
679   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
680 
681   /* The first time the onscreen is shown we will set it to toplevel
682    * so that it will appear on the screen. If the surface is foreign
683    * then we won't have the shell surface and we'll just let the
684    * application deal with setting the surface type. */
685   if (visibility &&
686       wayland_onscreen->wayland_shell_surface &&
687       !wayland_onscreen->shell_surface_type_set)
688     {
689       wl_shell_surface_set_toplevel (wayland_onscreen->wayland_shell_surface);
690       wayland_onscreen->shell_surface_type_set = TRUE;
691       _cogl_onscreen_queue_full_dirty (onscreen);
692     }
693 
694   /* FIXME: We should also do something here to hide the surface when
695    * visilibity == FALSE. It sounds like there are currently ongoing
696    * discussions about adding support for hiding surfaces in the
697    * Wayland protocol so we might as well wait until then to add that
698    * here. */
699 }
700 
701 void
cogl_wayland_renderer_set_foreign_display(CoglRenderer * renderer,struct wl_display * display)702 cogl_wayland_renderer_set_foreign_display (CoglRenderer *renderer,
703                                            struct wl_display *display)
704 {
705   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
706 
707   /* NB: Renderers are considered immutable once connected */
708   _COGL_RETURN_IF_FAIL (!renderer->connected);
709 
710   renderer->foreign_wayland_display = display;
711 }
712 
713 void
cogl_wayland_renderer_set_event_dispatch_enabled(CoglRenderer * renderer,CoglBool enable)714 cogl_wayland_renderer_set_event_dispatch_enabled (CoglRenderer *renderer,
715                                                   CoglBool enable)
716 {
717   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
718   /* NB: Renderers are considered immutable once connected */
719   _COGL_RETURN_IF_FAIL (!renderer->connected);
720 
721   renderer->wayland_enable_event_dispatch = enable;
722 }
723 
724 struct wl_display *
cogl_wayland_renderer_get_display(CoglRenderer * renderer)725 cogl_wayland_renderer_get_display (CoglRenderer *renderer)
726 {
727   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
728 
729   if (renderer->foreign_wayland_display)
730     return renderer->foreign_wayland_display;
731   else if (renderer->connected)
732     {
733       CoglRendererEGL *egl_renderer = renderer->winsys;
734       CoglRendererWayland *wayland_renderer = egl_renderer->platform;
735       return wayland_renderer->wayland_display;
736     }
737   else
738     return NULL;
739 }
740 
741 struct wl_surface *
cogl_wayland_onscreen_get_surface(CoglOnscreen * onscreen)742 cogl_wayland_onscreen_get_surface (CoglOnscreen *onscreen)
743 {
744   CoglOnscreenEGL *egl_onscreen;
745   CoglOnscreenWayland *wayland_onscreen;
746 
747   cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), NULL);
748 
749   egl_onscreen = onscreen->winsys;
750   wayland_onscreen = egl_onscreen->platform;
751 
752   return wayland_onscreen->wayland_surface;
753 }
754 
755 struct wl_shell_surface *
cogl_wayland_onscreen_get_shell_surface(CoglOnscreen * onscreen)756 cogl_wayland_onscreen_get_shell_surface (CoglOnscreen *onscreen)
757 {
758   CoglOnscreenEGL *egl_onscreen;
759   CoglOnscreenWayland *wayland_onscreen;
760 
761   cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), NULL);
762 
763   egl_onscreen = onscreen->winsys;
764   wayland_onscreen = egl_onscreen->platform;
765 
766   return wayland_onscreen->wayland_shell_surface;
767 }
768 
769 void
cogl_wayland_onscreen_set_foreign_surface(CoglOnscreen * onscreen,struct wl_surface * surface)770 cogl_wayland_onscreen_set_foreign_surface (CoglOnscreen *onscreen,
771                                            struct wl_surface *surface)
772 {
773   CoglFramebuffer *fb;
774 
775   fb = COGL_FRAMEBUFFER (onscreen);
776   _COGL_RETURN_IF_FAIL (!fb->allocated);
777 
778   onscreen->foreign_surface = surface;
779 }
780 
781 void
cogl_wayland_onscreen_resize(CoglOnscreen * onscreen,int width,int height,int offset_x,int offset_y)782 cogl_wayland_onscreen_resize (CoglOnscreen *onscreen,
783                               int           width,
784                               int           height,
785                               int           offset_x,
786                               int           offset_y)
787 {
788   CoglFramebuffer *fb;
789 
790   fb = COGL_FRAMEBUFFER (onscreen);
791   if (fb->allocated)
792     {
793       CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
794       CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
795 
796       if (cogl_framebuffer_get_width (fb) != width ||
797           cogl_framebuffer_get_height (fb) != height ||
798           wayland_onscreen->pending_width != width ||
799           wayland_onscreen->pending_height != height ||
800           offset_x ||
801           offset_y)
802         {
803           wayland_onscreen->pending_width = width;
804           wayland_onscreen->pending_height = height;
805           wayland_onscreen->pending_dx += offset_x;
806           wayland_onscreen->pending_dy += offset_y;
807           wayland_onscreen->has_pending = TRUE;
808 
809           /* If nothing has been drawn to the framebuffer since the
810            * last swap then wl_egl_window_resize will take effect
811            * immediately. Otherwise it might not take effect until the
812            * next swap, depending on the version of Mesa. To keep
813            * consistent behaviour we'll delay the resize until the
814            * next swap unless we're sure nothing has been drawn */
815           if (!fb->mid_scene)
816             flush_pending_resize (onscreen);
817         }
818     }
819   else
820     _cogl_framebuffer_winsys_update_size (fb, width, height);
821 }
822 
823 static const CoglWinsysEGLVtable
824 _cogl_winsys_egl_vtable =
825   {
826     .display_setup = _cogl_winsys_egl_display_setup,
827     .display_destroy = _cogl_winsys_egl_display_destroy,
828     .context_created = _cogl_winsys_egl_context_created,
829     .cleanup_context = _cogl_winsys_egl_cleanup_context,
830     .context_init = _cogl_winsys_egl_context_init,
831     .onscreen_init = _cogl_winsys_egl_onscreen_init,
832     .onscreen_deinit = _cogl_winsys_egl_onscreen_deinit
833   };
834 
835 const CoglWinsysVtable *
_cogl_winsys_egl_wayland_get_vtable(void)836 _cogl_winsys_egl_wayland_get_vtable (void)
837 {
838   static CoglBool vtable_inited = FALSE;
839   static CoglWinsysVtable vtable;
840 
841   if (!vtable_inited)
842     {
843       /* The EGL_WAYLAND winsys is a subclass of the EGL winsys so we
844          start by copying its vtable */
845 
846       parent_vtable = _cogl_winsys_egl_get_vtable ();
847       vtable = *parent_vtable;
848 
849       vtable.id = COGL_WINSYS_ID_EGL_WAYLAND;
850       vtable.name = "EGL_WAYLAND";
851 
852       vtable.renderer_connect = _cogl_winsys_renderer_connect;
853       vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
854 
855       vtable.onscreen_swap_buffers_with_damage =
856         _cogl_winsys_onscreen_swap_buffers_with_damage;
857 
858       vtable.onscreen_set_visibility =
859         _cogl_winsys_onscreen_set_visibility;
860 
861       vtable_inited = TRUE;
862     }
863 
864   return &vtable;
865 }
866