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 ®istry_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