"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / winsys / cogl-winsys-egl-wayland.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2011 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  *
23  * Authors:
24  *   Robert Bragg <robert@linux.intel.com>
25  *   Neil Roberts <neil@linux.intel.com>
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <wayland-client.h>
33 #include <wayland-egl.h>
34 #include <string.h>
35
36 #include "cogl-winsys-egl-wayland-private.h"
37 #include "cogl-winsys-egl-private.h"
38 #include "cogl-renderer-private.h"
39 #include "cogl-onscreen-private.h"
40 #include "cogl-wayland-renderer.h"
41
42 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
43
44 static const CoglWinsysVtable *parent_vtable;
45
46 typedef struct _CoglRendererWayland
47 {
48   struct wl_display *wayland_display;
49   struct wl_compositor *wayland_compositor;
50   struct wl_shell *wayland_shell;
51 } CoglRendererWayland;
52
53 typedef struct _CoglDisplayWayland
54 {
55   struct wl_surface *wayland_surface;
56   struct wl_egl_window *wayland_egl_native_window;
57 } CoglDisplayWayland;
58
59 typedef struct _CoglOnscreenWayland
60 {
61   struct wl_egl_window *wayland_egl_native_window;
62   struct wl_surface *wayland_surface;
63   struct wl_shell_surface *wayland_shell_surface;
64
65   /* Resizing a wayland framebuffer doesn't take affect
66    * until the next swap buffers request, so we have to
67    * track the resize geometry until then... */
68   int pending_width;
69   int pending_height;
70   int pending_dx;
71   int pending_dy;
72   gboolean has_pending;
73 } CoglOnscreenWayland;
74
75 static void
76 display_handle_global_cb (struct wl_display *display,
77                           uint32_t id,
78                           const char *interface,
79                           uint32_t version,
80                           void *data)
81 {
82   CoglRendererEGL *egl_renderer = (CoglRendererEGL *)data;
83   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
84
85   if (strcmp (interface, "wl_compositor") == 0)
86     wayland_renderer->wayland_compositor =
87       wl_display_bind (display, id, &wl_compositor_interface);
88   else if (strcmp(interface, "wl_shell") == 0)
89     wayland_renderer->wayland_shell =
90       wl_display_bind (display, id, &wl_shell_interface);
91 }
92
93 static void
94 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
95 {
96   CoglRendererEGL *egl_renderer = renderer->winsys;
97
98   eglTerminate (egl_renderer->edpy);
99
100   g_slice_free (CoglRendererWayland, egl_renderer->platform);
101   g_slice_free (CoglRendererEGL, egl_renderer);
102 }
103
104 static gboolean
105 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
106                                GError **error)
107 {
108   CoglRendererEGL *egl_renderer;
109   CoglRendererWayland *wayland_renderer;
110
111   renderer->winsys = g_slice_new0 (CoglRendererEGL);
112   egl_renderer = renderer->winsys;
113   wayland_renderer = g_slice_new0 (CoglRendererWayland);
114   egl_renderer->platform = wayland_renderer;
115
116   egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
117
118   /* The EGL API doesn't provide for a way to explicitly select a
119    * platform when the driver can support multiple. Mesa allows
120    * selection using an environment variable though so that's what
121    * we're doing here... */
122   g_setenv ("EGL_PLATFORM", "wayland", 1);
123
124   if (renderer->foreign_wayland_display)
125     {
126       wayland_renderer->wayland_display = renderer->foreign_wayland_display;
127       /* XXX: For now we have to assume that if a foreign display is
128        * given then a foreign compositor and shell must also have been
129        * given because wayland doesn't provide a way to
130        * retrospectively be notified of the these objects. */
131       g_assert (renderer->foreign_wayland_compositor);
132       g_assert (renderer->foreign_wayland_shell);
133       wayland_renderer->wayland_compositor =
134         renderer->foreign_wayland_compositor;
135       wayland_renderer->wayland_shell = renderer->foreign_wayland_shell;
136     }
137   else
138     {
139       wayland_renderer->wayland_display = wl_display_connect (NULL);
140       if (!wayland_renderer->wayland_display)
141         {
142           g_set_error (error, COGL_WINSYS_ERROR,
143                        COGL_WINSYS_ERROR_INIT,
144                        "Failed to connect wayland display");
145           goto error;
146         }
147
148       wl_display_add_global_listener (wayland_renderer->wayland_display,
149                                       display_handle_global_cb,
150                                       egl_renderer);
151     }
152
153   /*
154    * Ensure that that we've received the messages setting up the
155    * compostor and shell object. This is better than just
156    * wl_display_iterate since it will always ensure that something
157    * is available to be read
158    */
159   while (!(wayland_renderer->wayland_compositor &&
160            wayland_renderer->wayland_shell))
161     wl_display_roundtrip (wayland_renderer->wayland_display);
162
163   egl_renderer->edpy =
164     eglGetDisplay ((EGLNativeDisplayType) wayland_renderer->wayland_display);
165
166   if (!_cogl_winsys_egl_renderer_connect_common (renderer, error))
167     goto error;
168
169   return TRUE;
170
171 error:
172   _cogl_winsys_renderer_disconnect (renderer);
173   return FALSE;
174 }
175
176 static gboolean
177 _cogl_winsys_egl_display_setup (CoglDisplay *display,
178                                 GError **error)
179 {
180   CoglDisplayEGL *egl_display = display->winsys;
181   CoglDisplayWayland *wayland_display;
182
183   wayland_display = g_slice_new0 (CoglDisplayWayland);
184   egl_display->platform = wayland_display;
185
186   return TRUE;
187 }
188
189 static void
190 _cogl_winsys_egl_display_destroy (CoglDisplay *display)
191 {
192   CoglDisplayEGL *egl_display = display->winsys;
193
194   g_slice_free (CoglDisplayWayland, egl_display->platform);
195 }
196
197 static gboolean
198 _cogl_winsys_egl_context_created (CoglDisplay *display,
199                                   GError **error)
200 {
201   CoglRenderer *renderer = display->renderer;
202   CoglRendererEGL *egl_renderer = renderer->winsys;
203   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
204   CoglDisplayEGL *egl_display = display->winsys;
205   CoglDisplayWayland *wayland_display = egl_display->platform;
206   const char *error_message;
207
208   wayland_display->wayland_surface =
209     wl_compositor_create_surface (wayland_renderer->wayland_compositor);
210   if (!wayland_display->wayland_surface)
211     {
212       error_message= "Failed to create a dummy wayland surface";
213       goto fail;
214     }
215
216   wayland_display->wayland_egl_native_window =
217     wl_egl_window_create (wayland_display->wayland_surface,
218                           1,
219                           1);
220   if (!wayland_display->wayland_egl_native_window)
221     {
222       error_message= "Failed to create a dummy wayland native egl surface";
223       goto fail;
224     }
225
226   egl_display->dummy_surface =
227     eglCreateWindowSurface (egl_renderer->edpy,
228                             egl_display->egl_config,
229                             (EGLNativeWindowType)
230                             wayland_display->wayland_egl_native_window,
231                             NULL);
232   if (egl_display->dummy_surface == EGL_NO_SURFACE)
233     {
234       error_message= "Unable to eglMakeCurrent with dummy surface";
235       goto fail;
236     }
237
238   if (!eglMakeCurrent (egl_renderer->edpy,
239                        egl_display->dummy_surface,
240                        egl_display->dummy_surface,
241                        egl_display->egl_context))
242     {
243       error_message = "Unable to eglMakeCurrent with dummy surface";
244       goto fail;
245     }
246
247   return TRUE;
248
249  fail:
250   g_set_error (error, COGL_WINSYS_ERROR,
251                COGL_WINSYS_ERROR_CREATE_CONTEXT,
252                "%s", error_message);
253   return FALSE;
254 }
255
256 static void
257 _cogl_winsys_egl_cleanup_context (CoglDisplay *display)
258 {
259   CoglRenderer *renderer = display->renderer;
260   CoglRendererEGL *egl_renderer = renderer->winsys;
261   CoglDisplayEGL *egl_display = display->winsys;
262   CoglDisplayWayland *wayland_display = egl_display->platform;
263
264   if (egl_display->dummy_surface != EGL_NO_SURFACE)
265     {
266       eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface);
267       egl_display->dummy_surface = EGL_NO_SURFACE;
268     }
269
270   if (wayland_display->wayland_egl_native_window)
271     {
272       wl_egl_window_destroy (wayland_display->wayland_egl_native_window);
273       wayland_display->wayland_egl_native_window = NULL;
274     }
275
276   if (wayland_display->wayland_surface)
277     {
278       wl_surface_destroy (wayland_display->wayland_surface);
279       wayland_display->wayland_surface = NULL;
280     }
281 }
282
283 static gboolean
284 _cogl_winsys_egl_context_init (CoglContext *context,
285                                GError **error)
286 {
287   context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE;
288   COGL_FLAGS_SET (context->features,
289                   COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE);
290   COGL_FLAGS_SET (context->winsys_features,
291                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
292                   TRUE);
293
294   return TRUE;
295 }
296
297 static gboolean
298 _cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen,
299                                 EGLConfig egl_config,
300                                 GError **error)
301 {
302   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
303   CoglOnscreenWayland *wayland_onscreen;
304   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
305   CoglContext *context = framebuffer->context;
306   CoglRenderer *renderer = context->display->renderer;
307   CoglRendererEGL *egl_renderer = renderer->winsys;
308   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
309
310   wayland_onscreen = g_slice_new0 (CoglOnscreenWayland);
311   egl_onscreen->platform = wayland_onscreen;
312
313   wayland_onscreen->wayland_surface =
314     wl_compositor_create_surface (wayland_renderer->wayland_compositor);
315   if (!wayland_onscreen->wayland_surface)
316     {
317       g_set_error (error, COGL_WINSYS_ERROR,
318                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
319                    "Error while creating wayland surface for CoglOnscreen");
320       return FALSE;
321     }
322
323   wayland_onscreen->wayland_shell_surface =
324     wl_shell_get_shell_surface (wayland_renderer->wayland_shell,
325                                 wayland_onscreen->wayland_surface);
326
327   wayland_onscreen->wayland_egl_native_window =
328     wl_egl_window_create (wayland_onscreen->wayland_surface,
329                           cogl_framebuffer_get_width (framebuffer),
330                           cogl_framebuffer_get_height (framebuffer));
331   if (!wayland_onscreen->wayland_egl_native_window)
332     {
333       g_set_error (error, COGL_WINSYS_ERROR,
334                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
335                    "Error while creating wayland egl native window "
336                    "for CoglOnscreen");
337       return FALSE;
338     }
339
340   egl_onscreen->egl_surface =
341     eglCreateWindowSurface (egl_renderer->edpy,
342                             egl_config,
343                             (EGLNativeWindowType)
344                             wayland_onscreen->wayland_egl_native_window,
345                             NULL);
346
347   wl_shell_surface_set_toplevel (wayland_onscreen->wayland_shell_surface);
348
349   return TRUE;
350 }
351
352 static void
353 _cogl_winsys_egl_onscreen_deinit (CoglOnscreen *onscreen)
354 {
355   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
356   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
357
358   if (wayland_onscreen->wayland_egl_native_window)
359     {
360       wl_egl_window_destroy (wayland_onscreen->wayland_egl_native_window);
361       wayland_onscreen->wayland_egl_native_window = NULL;
362     }
363
364   if (wayland_onscreen->wayland_surface)
365     {
366       wl_surface_destroy (wayland_onscreen->wayland_surface);
367       wayland_onscreen->wayland_surface = NULL;
368     }
369
370   g_slice_free (CoglOnscreenWayland, wayland_onscreen);
371 }
372
373 static void
374 _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
375 {
376   CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
377   CoglContext *context = fb->context;
378   CoglRenderer *renderer = context->display->renderer;
379   CoglRendererEGL *egl_renderer = renderer->winsys;
380   CoglRendererWayland *wayland_renderer = egl_renderer->platform;
381   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
382   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
383
384   if (wayland_onscreen->has_pending)
385     {
386       wl_egl_window_resize (wayland_onscreen->wayland_egl_native_window,
387                             wayland_onscreen->pending_width,
388                             wayland_onscreen->pending_height,
389                             wayland_onscreen->pending_dx,
390                             wayland_onscreen->pending_dy);
391
392       _cogl_framebuffer_winsys_update_size (fb,
393                                             wayland_onscreen->pending_width,
394                                             wayland_onscreen->pending_height);
395       wayland_onscreen->has_pending = FALSE;
396     }
397
398   /* chain-up */
399   parent_vtable->onscreen_swap_buffers (onscreen);
400
401   /*
402    * The implementation of eglSwapBuffers may do a flush however the semantics
403    * of eglSwapBuffers on Wayland has changed in the past. So to be safe to
404    * the implementation changing we should explicitly ensure all messages are
405    * sent.
406    */
407   wl_display_flush (wayland_renderer->wayland_display);
408 }
409
410 void
411 cogl_wayland_renderer_set_foreign_display (CoglRenderer *renderer,
412                                            struct wl_display *display)
413 {
414   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
415
416   /* NB: Renderers are considered immutable once connected */
417   _COGL_RETURN_IF_FAIL (!renderer->connected);
418
419   renderer->foreign_wayland_display = display;
420 }
421
422 struct wl_display *
423 cogl_wayland_renderer_get_display (CoglRenderer *renderer)
424 {
425   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
426
427   if (renderer->foreign_wayland_display)
428     return renderer->foreign_wayland_display;
429   else if (renderer->connected)
430     {
431       CoglRendererEGL *egl_renderer = renderer->winsys;
432       CoglRendererWayland *wayland_renderer = egl_renderer->platform;
433       return wayland_renderer->wayland_display;
434     }
435   else
436     return NULL;
437 }
438
439 void
440 cogl_wayland_renderer_set_foreign_compositor (CoglRenderer *renderer,
441                                               struct wl_compositor *compositor)
442 {
443   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
444
445   /* NB: Renderers are considered immutable once connected */
446   _COGL_RETURN_IF_FAIL (!renderer->connected);
447
448   renderer->foreign_wayland_compositor = compositor;
449 }
450
451 struct wl_compositor *
452 cogl_wayland_renderer_get_compositor (CoglRenderer *renderer)
453 {
454   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
455
456   if (renderer->foreign_wayland_compositor)
457     return renderer->foreign_wayland_compositor;
458   else if (renderer->connected)
459     {
460       CoglRendererEGL *egl_renderer = renderer->winsys;
461       CoglRendererWayland *wayland_renderer = egl_renderer->platform;
462       return wayland_renderer->wayland_compositor;
463     }
464   else
465     return NULL;
466 }
467
468 void
469 cogl_wayland_renderer_set_foreign_shell (CoglRenderer *renderer,
470                                          struct wl_shell *shell)
471 {
472   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
473
474   /* NB: Renderers are considered immutable once connected */
475   _COGL_RETURN_IF_FAIL (!renderer->connected);
476
477   renderer->foreign_wayland_shell = shell;
478 }
479
480 struct wl_shell *
481 cogl_wayland_renderer_get_shell (CoglRenderer *renderer)
482 {
483   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
484
485   if (renderer->foreign_wayland_shell)
486     return renderer->foreign_wayland_shell;
487   else if (renderer->connected)
488     {
489       CoglRendererEGL *egl_renderer = renderer->winsys;
490       CoglRendererWayland *wayland_renderer = egl_renderer->platform;
491       return wayland_renderer->wayland_shell;
492     }
493   else
494     return NULL;
495 }
496
497 struct wl_surface *
498 cogl_wayland_onscreen_get_surface (CoglOnscreen *onscreen)
499 {
500   CoglFramebuffer *fb;
501
502   fb = COGL_FRAMEBUFFER (onscreen);
503   if (fb->allocated)
504     {
505       CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
506       CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
507       return wayland_onscreen->wayland_surface;
508     }
509   else
510     return NULL;
511 }
512
513 struct wl_shell_surface *
514 cogl_wayland_onscreen_get_shell_surface (CoglOnscreen *onscreen)
515 {
516   CoglFramebuffer *fb;
517
518   fb = COGL_FRAMEBUFFER (onscreen);
519   if (fb->allocated)
520     {
521       CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
522       CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
523       return wayland_onscreen->wayland_shell_surface;
524     }
525   else
526     return NULL;
527 }
528
529 void
530 cogl_wayland_onscreen_resize (CoglOnscreen *onscreen,
531                               int           width,
532                               int           height,
533                               int           offset_x,
534                               int           offset_y)
535 {
536   CoglFramebuffer *fb;
537
538   fb = COGL_FRAMEBUFFER (onscreen);
539   if (fb->allocated)
540     {
541       CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
542       CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
543
544       if (cogl_framebuffer_get_width (fb) != width ||
545           cogl_framebuffer_get_height (fb) != height ||
546           offset_x ||
547           offset_y)
548         {
549           wayland_onscreen->pending_width = width;
550           wayland_onscreen->pending_height = height;
551           wayland_onscreen->pending_dx += offset_x;
552           wayland_onscreen->pending_dy += offset_y;
553           wayland_onscreen->has_pending = TRUE;
554         }
555     }
556   else
557     _cogl_framebuffer_winsys_update_size (fb, width, height);
558 }
559
560 static const CoglWinsysEGLVtable
561 _cogl_winsys_egl_vtable =
562   {
563     .display_setup = _cogl_winsys_egl_display_setup,
564     .display_destroy = _cogl_winsys_egl_display_destroy,
565     .context_created = _cogl_winsys_egl_context_created,
566     .cleanup_context = _cogl_winsys_egl_cleanup_context,
567     .context_init = _cogl_winsys_egl_context_init,
568     .onscreen_init = _cogl_winsys_egl_onscreen_init,
569     .onscreen_deinit = _cogl_winsys_egl_onscreen_deinit
570   };
571
572 const CoglWinsysVtable *
573 _cogl_winsys_egl_wayland_get_vtable (void)
574 {
575   static gboolean vtable_inited = FALSE;
576   static CoglWinsysVtable vtable;
577
578   if (!vtable_inited)
579     {
580       /* The EGL_WAYLAND winsys is a subclass of the EGL winsys so we
581          start by copying its vtable */
582
583       parent_vtable = _cogl_winsys_egl_get_vtable ();
584       vtable = *parent_vtable;
585
586       vtable.id = COGL_WINSYS_ID_EGL_WAYLAND;
587       vtable.name = "EGL_WAYLAND";
588
589       vtable.renderer_connect = _cogl_winsys_renderer_connect;
590       vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
591
592       vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
593
594       vtable_inited = TRUE;
595     }
596
597   return &vtable;
598 }