platform_wayland.c revision eaf6884621661288624fc060974c3aa485771324
1/*
2 * Copyright © 2011 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Kristian Høgsberg <krh@bitplanet.net>
26 *    Benjamin Franzke <benjaminfranzke@googlemail.com>
27 */
28
29#include <stdlib.h>
30#include <string.h>
31#include <limits.h>
32#include <dlfcn.h>
33#include <errno.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <xf86drm.h>
37
38#include "egl_dri2.h"
39
40#include <wayland-client.h>
41#include "wayland-drm-client-protocol.h"
42
43enum wl_drm_format_flags {
44   HAS_ARGB8888 = 1,
45   HAS_XRGB8888 = 2
46};
47
48static void
49sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
50{
51   int *done = data;
52
53   *done = 1;
54   wl_callback_destroy(callback);
55}
56
57static const struct wl_callback_listener sync_listener = {
58   sync_callback
59};
60
61static int
62roundtrip(struct dri2_egl_display *dri2_dpy)
63{
64   struct wl_callback *callback;
65   int done = 0, ret = 0;
66
67   callback = wl_display_sync(dri2_dpy->wl_dpy);
68   wl_callback_add_listener(callback, &sync_listener, &done);
69   wl_proxy_set_queue((struct wl_proxy *) callback, dri2_dpy->wl_queue);
70   while (ret != -1 && !done)
71      ret = wl_display_dispatch_queue(dri2_dpy->wl_dpy, dri2_dpy->wl_queue);
72
73   return ret;
74}
75
76static void
77wl_buffer_release(void *data, struct wl_buffer *buffer)
78{
79   struct dri2_egl_surface *dri2_surf = data;
80   int i;
81
82   for (i = 0; i < WL_BUFFER_COUNT; ++i)
83      if (dri2_surf->wl_drm_buffer[i] == buffer)
84         break;
85
86   assert(i <= WL_BUFFER_COUNT);
87
88   /* not found? */
89   if (i == WL_BUFFER_COUNT)
90      return;
91
92   dri2_surf->wl_buffer_lock[i] = 0;
93
94}
95
96static struct wl_buffer_listener wl_buffer_listener = {
97   wl_buffer_release
98};
99
100static void
101resize_callback(struct wl_egl_window *wl_win, void *data)
102{
103   struct dri2_egl_surface *dri2_surf = data;
104   struct dri2_egl_display *dri2_dpy =
105      dri2_egl_display(dri2_surf->base.Resource.Display);
106
107   (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
108}
109
110/**
111 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
112 */
113static _EGLSurface *
114dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
115		    _EGLConfig *conf, EGLNativeWindowType window,
116		    const EGLint *attrib_list)
117{
118   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
119   struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
120   struct dri2_egl_surface *dri2_surf;
121   int i;
122
123   (void) drv;
124
125   dri2_surf = malloc(sizeof *dri2_surf);
126   if (!dri2_surf) {
127      _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
128      return NULL;
129   }
130
131   if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
132      goto cleanup_surf;
133
134   for (i = 0; i < WL_BUFFER_COUNT; ++i) {
135      dri2_surf->wl_drm_buffer[i] = NULL;
136      dri2_surf->wl_buffer_lock[i] = 0;
137   }
138
139   for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
140      dri2_surf->dri_buffers[i] = NULL;
141
142   dri2_surf->pending_buffer = NULL;
143   dri2_surf->third_buffer = NULL;
144   dri2_surf->frame_callback = NULL;
145
146   if (conf->AlphaSize == 0)
147      dri2_surf->format = WL_DRM_FORMAT_XRGB8888;
148   else
149      dri2_surf->format = WL_DRM_FORMAT_ARGB8888;
150
151   switch (type) {
152   case EGL_WINDOW_BIT:
153      dri2_surf->wl_win = (struct wl_egl_window *) window;
154
155      dri2_surf->wl_win->private = dri2_surf;
156      dri2_surf->wl_win->resize_callback = resize_callback;
157
158      dri2_surf->base.Width =  -1;
159      dri2_surf->base.Height = -1;
160      break;
161   default:
162      goto cleanup_surf;
163   }
164
165   dri2_surf->dri_drawable =
166      (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
167					    type == EGL_WINDOW_BIT ?
168					    dri2_conf->dri_double_config :
169					    dri2_conf->dri_single_config,
170					    dri2_surf);
171   if (dri2_surf->dri_drawable == NULL) {
172      _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
173      goto cleanup_dri_drawable;
174   }
175
176   return &dri2_surf->base;
177
178 cleanup_dri_drawable:
179   dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
180 cleanup_surf:
181   free(dri2_surf);
182
183   return NULL;
184}
185
186/**
187 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
188 */
189static _EGLSurface *
190dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
191			   _EGLConfig *conf, EGLNativeWindowType window,
192			   const EGLint *attrib_list)
193{
194   return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
195			      window, attrib_list);
196}
197
198/**
199 * Called via eglDestroySurface(), drv->API.DestroySurface().
200 */
201static EGLBoolean
202dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
203{
204   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
205   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
206   int i;
207
208   (void) drv;
209
210   if (!_eglPutSurface(surf))
211      return EGL_TRUE;
212
213   (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);
214
215   for (i = 0; i < WL_BUFFER_COUNT; ++i)
216      if (dri2_surf->wl_drm_buffer[i])
217         wl_buffer_destroy(dri2_surf->wl_drm_buffer[i]);
218
219   for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
220      if (dri2_surf->dri_buffers[i])
221         dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
222                                       dri2_surf->dri_buffers[i]);
223
224   if (dri2_surf->third_buffer) {
225      dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
226                                    dri2_surf->third_buffer);
227   }
228
229   if (dri2_surf->frame_callback)
230      wl_callback_destroy(dri2_surf->frame_callback);
231
232
233   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
234      dri2_surf->wl_win->private = NULL;
235      dri2_surf->wl_win->resize_callback = NULL;
236   }
237
238   free(surf);
239
240   return EGL_TRUE;
241}
242
243static struct wl_buffer *
244wayland_create_buffer(struct dri2_egl_surface *dri2_surf,
245                      __DRIbuffer *buffer)
246{
247   struct dri2_egl_display *dri2_dpy =
248      dri2_egl_display(dri2_surf->base.Resource.Display);
249   struct wl_buffer *buf;
250
251   buf = wl_drm_create_buffer(dri2_dpy->wl_drm, buffer->name,
252                              dri2_surf->base.Width, dri2_surf->base.Height,
253                              buffer->pitch, dri2_surf->format);
254   wl_buffer_add_listener(buf, &wl_buffer_listener, dri2_surf);
255
256   return buf;
257}
258
259static void
260dri2_process_back_buffer(struct dri2_egl_surface *dri2_surf, unsigned format)
261{
262   struct dri2_egl_display *dri2_dpy =
263      dri2_egl_display(dri2_surf->base.Resource.Display);
264
265   (void) format;
266
267   switch (dri2_surf->base.Type) {
268   case EGL_WINDOW_BIT:
269      /* allocate a front buffer for our double-buffered window*/
270      if (dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] != NULL)
271         break;
272      dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] =
273         dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
274               __DRI_BUFFER_FRONT_LEFT, format,
275               dri2_surf->base.Width, dri2_surf->base.Height);
276      break;
277   default:
278      break;
279   }
280}
281
282static void
283dri2_release_pending_buffer(void *data,
284			    struct wl_callback *callback, uint32_t time)
285{
286   struct dri2_egl_surface *dri2_surf = data;
287   struct dri2_egl_display *dri2_dpy =
288      dri2_egl_display(dri2_surf->base.Resource.Display);
289
290   /* FIXME: print internal error */
291   if (!dri2_surf->pending_buffer)
292      return;
293
294   dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
295                                 dri2_surf->pending_buffer);
296   dri2_surf->pending_buffer = NULL;
297
298   wl_callback_destroy(callback);
299}
300
301static const struct wl_callback_listener release_buffer_listener = {
302   dri2_release_pending_buffer
303};
304
305static void
306dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
307{
308   struct dri2_egl_display *dri2_dpy =
309      dri2_egl_display(dri2_surf->base.Resource.Display);
310   struct wl_callback *callback;
311   int i;
312
313   if (dri2_surf->third_buffer) {
314      dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
315                                    dri2_surf->third_buffer);
316      dri2_surf->third_buffer = NULL;
317   }
318
319   for (i = 0; i < __DRI_BUFFER_COUNT; ++i) {
320      if (dri2_surf->dri_buffers[i]) {
321         switch (i) {
322         case __DRI_BUFFER_FRONT_LEFT:
323            if (dri2_surf->pending_buffer)
324               roundtrip(dri2_dpy);
325            dri2_surf->pending_buffer = dri2_surf->dri_buffers[i];
326            callback = wl_display_sync(dri2_dpy->wl_dpy);
327	    wl_callback_add_listener(callback,
328				     &release_buffer_listener, dri2_surf);
329            wl_proxy_set_queue((struct wl_proxy *) callback,
330                               dri2_dpy->wl_queue);
331            break;
332         default:
333            dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
334                                          dri2_surf->dri_buffers[i]);
335            break;
336         }
337         dri2_surf->dri_buffers[i] = NULL;
338      }
339   }
340}
341
342static inline void
343pointer_swap(const void **p1, const void **p2)
344{
345   const void *tmp = *p1;
346   *p1 = *p2;
347   *p2 = tmp;
348}
349
350static void
351destroy_third_buffer(struct dri2_egl_surface *dri2_surf)
352{
353   struct dri2_egl_display *dri2_dpy =
354      dri2_egl_display(dri2_surf->base.Resource.Display);
355
356   if (dri2_surf->third_buffer == NULL)
357      return;
358
359   dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
360                                 dri2_surf->third_buffer);
361   dri2_surf->third_buffer = NULL;
362
363   if (dri2_surf->wl_drm_buffer[WL_BUFFER_THIRD])
364      wl_buffer_destroy(dri2_surf->wl_drm_buffer[WL_BUFFER_THIRD]);
365   dri2_surf->wl_drm_buffer[WL_BUFFER_THIRD] = NULL;
366   dri2_surf->wl_buffer_lock[WL_BUFFER_THIRD] = 0;
367}
368
369static void
370swap_wl_buffers(struct dri2_egl_surface *dri2_surf,
371                enum wayland_buffer_type a, enum wayland_buffer_type b)
372{
373   int tmp;
374
375   tmp = dri2_surf->wl_buffer_lock[a];
376   dri2_surf->wl_buffer_lock[a] = dri2_surf->wl_buffer_lock[b];
377   dri2_surf->wl_buffer_lock[b] = tmp;
378
379   pointer_swap((const void **) &dri2_surf->wl_drm_buffer[a],
380                (const void **) &dri2_surf->wl_drm_buffer[b]);
381}
382
383static void
384swap_back_and_third(struct dri2_egl_surface *dri2_surf)
385{
386   if (dri2_surf->wl_buffer_lock[WL_BUFFER_THIRD])
387      destroy_third_buffer(dri2_surf);
388
389   pointer_swap((const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT],
390                (const void **) &dri2_surf->third_buffer);
391
392   swap_wl_buffers(dri2_surf, WL_BUFFER_BACK, WL_BUFFER_THIRD);
393}
394
395static void
396dri2_prior_buffer_creation(struct dri2_egl_surface *dri2_surf,
397                           unsigned int type)
398{
399   switch (type) {
400   case __DRI_BUFFER_BACK_LEFT:
401         if (dri2_surf->wl_buffer_lock[WL_BUFFER_BACK])
402            swap_back_and_third(dri2_surf);
403         else if (dri2_surf->third_buffer)
404            destroy_third_buffer(dri2_surf);
405         break;
406   default:
407         break;
408
409   }
410}
411
412static __DRIbuffer *
413dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
414			     int *width, int *height,
415			     unsigned int *attachments, int count,
416			     int *out_count, void *loaderPrivate)
417{
418   struct dri2_egl_surface *dri2_surf = loaderPrivate;
419   struct dri2_egl_display *dri2_dpy =
420      dri2_egl_display(dri2_surf->base.Resource.Display);
421   int i;
422
423   if (dri2_surf->base.Type == EGL_WINDOW_BIT &&
424       (dri2_surf->base.Width != dri2_surf->wl_win->width ||
425        dri2_surf->base.Height != dri2_surf->wl_win->height)) {
426
427      dri2_release_buffers(dri2_surf);
428
429      dri2_surf->base.Width  = dri2_surf->wl_win->width;
430      dri2_surf->base.Height = dri2_surf->wl_win->height;
431      dri2_surf->dx = dri2_surf->wl_win->dx;
432      dri2_surf->dy = dri2_surf->wl_win->dy;
433
434      for (i = 0; i < WL_BUFFER_COUNT; ++i) {
435         if (dri2_surf->wl_drm_buffer[i])
436            wl_buffer_destroy(dri2_surf->wl_drm_buffer[i]);
437         dri2_surf->wl_drm_buffer[i]  = NULL;
438         dri2_surf->wl_buffer_lock[i] = 0;
439      }
440   }
441
442   dri2_surf->buffer_count = 0;
443   for (i = 0; i < 2*count; i+=2) {
444      assert(attachments[i] < __DRI_BUFFER_COUNT);
445      assert(dri2_surf->buffer_count < 5);
446
447      dri2_prior_buffer_creation(dri2_surf, attachments[i]);
448
449      if (dri2_surf->dri_buffers[attachments[i]] == NULL) {
450
451         dri2_surf->dri_buffers[attachments[i]] =
452            dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
453                  attachments[i], attachments[i+1],
454                  dri2_surf->base.Width, dri2_surf->base.Height);
455
456         if (!dri2_surf->dri_buffers[attachments[i]])
457            continue;
458
459         if (attachments[i] == __DRI_BUFFER_BACK_LEFT)
460            dri2_process_back_buffer(dri2_surf, attachments[i+1]);
461      }
462
463      memcpy(&dri2_surf->buffers[dri2_surf->buffer_count],
464             dri2_surf->dri_buffers[attachments[i]],
465             sizeof(__DRIbuffer));
466
467      dri2_surf->buffer_count++;
468   }
469
470   assert(dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
471
472   *out_count = dri2_surf->buffer_count;
473   if (dri2_surf->buffer_count == 0)
474	   return NULL;
475
476   *width = dri2_surf->base.Width;
477   *height = dri2_surf->base.Height;
478
479   return dri2_surf->buffers;
480}
481
482static __DRIbuffer *
483dri2_get_buffers(__DRIdrawable * driDrawable,
484		 int *width, int *height,
485		 unsigned int *attachments, int count,
486		 int *out_count, void *loaderPrivate)
487{
488   unsigned int *attachments_with_format;
489   __DRIbuffer *buffer;
490   const unsigned int format = 32;
491   int i;
492
493   attachments_with_format = calloc(count * 2, sizeof(unsigned int));
494   if (!attachments_with_format) {
495      *out_count = 0;
496      return NULL;
497   }
498
499   for (i = 0; i < count; ++i) {
500      attachments_with_format[2*i] = attachments[i];
501      attachments_with_format[2*i + 1] = format;
502   }
503
504   buffer =
505      dri2_get_buffers_with_format(driDrawable,
506				   width, height,
507				   attachments_with_format, count,
508				   out_count, loaderPrivate);
509
510   free(attachments_with_format);
511
512   return buffer;
513}
514
515
516static void
517dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
518{
519   (void) driDrawable;
520
521   /* FIXME: Does EGL support front buffer rendering at all? */
522
523#if 0
524   struct dri2_egl_surface *dri2_surf = loaderPrivate;
525
526   dri2WaitGL(dri2_surf);
527#else
528   (void) loaderPrivate;
529#endif
530}
531
532static void
533wayland_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
534{
535   struct dri2_egl_surface *dri2_surf = data;
536
537   dri2_surf->frame_callback = NULL;
538   wl_callback_destroy(callback);
539}
540
541static const struct wl_callback_listener frame_listener = {
542	wayland_frame_callback
543};
544
545/**
546 * Called via eglSwapBuffers(), drv->API.SwapBuffers().
547 */
548static EGLBoolean
549dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
550{
551   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
552   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
553   struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv);
554   int ret = 0;
555
556   while (dri2_surf->frame_callback && ret != -1)
557      ret = wl_display_dispatch_queue(dri2_dpy->wl_dpy, dri2_dpy->wl_queue);
558   if (ret < 0)
559      return EGL_FALSE;
560
561   dri2_surf->frame_callback = wl_surface_frame(dri2_surf->wl_win->surface);
562   wl_callback_add_listener(dri2_surf->frame_callback,
563                            &frame_listener, dri2_surf);
564   wl_proxy_set_queue((struct wl_proxy *) dri2_surf->frame_callback,
565                      dri2_dpy->wl_queue);
566
567   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
568      pointer_swap(
569	    (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT],
570	    (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
571
572      dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]->attachment =
573	 __DRI_BUFFER_FRONT_LEFT;
574      dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]->attachment =
575	 __DRI_BUFFER_BACK_LEFT;
576
577      swap_wl_buffers(dri2_surf, WL_BUFFER_FRONT, WL_BUFFER_BACK);
578
579      if (!dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT])
580	 dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT] =
581	    wayland_create_buffer(dri2_surf,
582		  dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]);
583
584      wl_surface_attach(dri2_surf->wl_win->surface,
585	    dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT],
586	    dri2_surf->dx, dri2_surf->dy);
587      dri2_surf->wl_buffer_lock[WL_BUFFER_FRONT] = 1;
588
589      dri2_surf->wl_win->attached_width  = dri2_surf->base.Width;
590      dri2_surf->wl_win->attached_height = dri2_surf->base.Height;
591      /* reset resize growing parameters */
592      dri2_surf->dx = 0;
593      dri2_surf->dy = 0;
594
595      wl_surface_damage(dri2_surf->wl_win->surface, 0, 0,
596	    dri2_surf->base.Width, dri2_surf->base.Height);
597
598      wl_surface_commit(dri2_surf->wl_win->surface);
599   }
600
601   _EGLContext *ctx;
602   if (dri2_drv->glFlush) {
603      ctx = _eglGetCurrentContext();
604      if (ctx && ctx->DrawSurface == &dri2_surf->base)
605         dri2_drv->glFlush();
606   }
607
608   (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
609
610   return EGL_TRUE;
611}
612
613static int
614dri2_wayland_authenticate(_EGLDisplay *disp, uint32_t id)
615{
616   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
617   int ret = 0;
618
619   dri2_dpy->authenticated = 0;
620
621   wl_drm_authenticate(dri2_dpy->wl_drm, id);
622   if (roundtrip(dri2_dpy) < 0)
623      ret = -1;
624
625   if (!dri2_dpy->authenticated)
626      ret = -1;
627
628   /* reset authenticated */
629   dri2_dpy->authenticated = 1;
630
631   return ret;
632}
633
634/**
635 * Called via eglTerminate(), drv->API.Terminate().
636 */
637static EGLBoolean
638dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
639{
640   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
641
642   _eglReleaseDisplayResources(drv, disp);
643   _eglCleanupDisplay(disp);
644
645   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
646   close(dri2_dpy->fd);
647   dlclose(dri2_dpy->driver);
648   free(dri2_dpy->driver_name);
649   free(dri2_dpy->device_name);
650   wl_drm_destroy(dri2_dpy->wl_drm);
651   if (dri2_dpy->own_device)
652      wl_display_disconnect(dri2_dpy->wl_dpy);
653   free(dri2_dpy);
654   disp->DriverData = NULL;
655
656   return EGL_TRUE;
657}
658
659static void
660drm_handle_device(void *data, struct wl_drm *drm, const char *device)
661{
662   struct dri2_egl_display *dri2_dpy = data;
663   drm_magic_t magic;
664
665   dri2_dpy->device_name = strdup(device);
666   if (!dri2_dpy->device_name)
667      return;
668
669#ifdef O_CLOEXEC
670   dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR | O_CLOEXEC);
671   if (dri2_dpy->fd == -1 && errno == EINVAL)
672#endif
673   {
674      dri2_dpy->fd = open(dri2_dpy->device_name, O_RDWR);
675      if (dri2_dpy->fd != -1)
676         fcntl(dri2_dpy->fd, F_SETFD, fcntl(dri2_dpy->fd, F_GETFD) |
677            FD_CLOEXEC);
678   }
679   if (dri2_dpy->fd == -1) {
680      _eglLog(_EGL_WARNING, "wayland-egl: could not open %s (%s)",
681	      dri2_dpy->device_name, strerror(errno));
682      return;
683   }
684
685   drmGetMagic(dri2_dpy->fd, &magic);
686   wl_drm_authenticate(dri2_dpy->wl_drm, magic);
687}
688
689static void
690drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
691{
692   struct dri2_egl_display *dri2_dpy = data;
693
694   switch (format) {
695   case WL_DRM_FORMAT_ARGB8888:
696      dri2_dpy->formats |= HAS_ARGB8888;
697      break;
698   case WL_DRM_FORMAT_XRGB8888:
699      dri2_dpy->formats |= HAS_XRGB8888;
700      break;
701   }
702}
703
704static void
705drm_handle_authenticated(void *data, struct wl_drm *drm)
706{
707   struct dri2_egl_display *dri2_dpy = data;
708
709   dri2_dpy->authenticated = 1;
710}
711
712static const struct wl_drm_listener drm_listener = {
713	drm_handle_device,
714	drm_handle_format,
715	drm_handle_authenticated
716};
717
718static void
719registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
720		       const char *interface, uint32_t version)
721{
722   struct dri2_egl_display *dri2_dpy = data;
723
724   if (strcmp(interface, "wl_drm") == 0) {
725      dri2_dpy->wl_drm =
726         wl_registry_bind(registry, name, &wl_drm_interface, 1);
727      wl_drm_add_listener(dri2_dpy->wl_drm, &drm_listener, dri2_dpy);
728   }
729}
730
731static const struct wl_registry_listener registry_listener = {
732	registry_handle_global
733};
734
735EGLBoolean
736dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
737{
738   struct dri2_egl_display *dri2_dpy;
739   const __DRIconfig *config;
740   uint32_t types;
741   int i;
742   static const unsigned int argb_masks[4] =
743      { 0xff0000, 0xff00, 0xff, 0xff000000 };
744   static const unsigned int rgb_masks[4] = { 0xff0000, 0xff00, 0xff, 0 };
745
746   drv->API.CreateWindowSurface = dri2_create_window_surface;
747   drv->API.DestroySurface = dri2_destroy_surface;
748   drv->API.SwapBuffers = dri2_swap_buffers;
749   drv->API.Terminate = dri2_terminate;
750
751   dri2_dpy = malloc(sizeof *dri2_dpy);
752   if (!dri2_dpy)
753      return _eglError(EGL_BAD_ALLOC, "eglInitialize");
754
755   memset(dri2_dpy, 0, sizeof *dri2_dpy);
756
757   disp->DriverData = (void *) dri2_dpy;
758   if (disp->PlatformDisplay == NULL) {
759      dri2_dpy->wl_dpy = wl_display_connect(NULL);
760      if (dri2_dpy->wl_dpy == NULL)
761         goto cleanup_dpy;
762      dri2_dpy->own_device = 1;
763   } else {
764      dri2_dpy->wl_dpy = disp->PlatformDisplay;
765   }
766
767   dri2_dpy->wl_queue = wl_display_create_queue(dri2_dpy->wl_dpy);
768   dri2_dpy->wl_registry = wl_display_get_registry(dri2_dpy->wl_dpy);
769   wl_proxy_set_queue((struct wl_proxy *) dri2_dpy->wl_registry,
770                      dri2_dpy->wl_queue);
771   wl_registry_add_listener(dri2_dpy->wl_registry,
772                            &registry_listener, dri2_dpy);
773   if (roundtrip(dri2_dpy) < 0 || dri2_dpy->wl_drm == NULL)
774      goto cleanup_dpy;
775
776   if (roundtrip(dri2_dpy) < 0 || dri2_dpy->fd == -1)
777      goto cleanup_drm;
778
779   if (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated)
780      goto cleanup_fd;
781
782   dri2_dpy->driver_name = dri2_get_driver_for_fd(dri2_dpy->fd);
783   if (dri2_dpy->driver_name == NULL) {
784      _eglError(EGL_BAD_ALLOC, "DRI2: failed to get driver name");
785      goto cleanup_fd;
786   }
787
788   if (!dri2_load_driver(disp))
789      goto cleanup_driver_name;
790
791   dri2_dpy->dri2_loader_extension.base.name = __DRI_DRI2_LOADER;
792   dri2_dpy->dri2_loader_extension.base.version = 3;
793   dri2_dpy->dri2_loader_extension.getBuffers = dri2_get_buffers;
794   dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_flush_front_buffer;
795   dri2_dpy->dri2_loader_extension.getBuffersWithFormat =
796      dri2_get_buffers_with_format;
797
798   dri2_dpy->extensions[0] = &dri2_dpy->dri2_loader_extension.base;
799   dri2_dpy->extensions[1] = &image_lookup_extension.base;
800   dri2_dpy->extensions[2] = &use_invalidate.base;
801   dri2_dpy->extensions[3] = NULL;
802
803   if (!dri2_create_screen(disp))
804      goto cleanup_driver;
805
806   types = EGL_WINDOW_BIT;
807   for (i = 0; dri2_dpy->driver_configs[i]; i++) {
808      config = dri2_dpy->driver_configs[i];
809      if (dri2_dpy->formats & HAS_XRGB8888)
810	 dri2_add_config(disp, config, i + 1, 0, types, NULL, rgb_masks);
811      if (dri2_dpy->formats & HAS_ARGB8888)
812	 dri2_add_config(disp, config, i + 1, 0, types, NULL, argb_masks);
813   }
814
815   disp->Extensions.WL_bind_wayland_display = EGL_TRUE;
816   dri2_dpy->authenticate = dri2_wayland_authenticate;
817
818   /* we're supporting EGL 1.4 */
819   disp->VersionMajor = 1;
820   disp->VersionMinor = 4;
821
822   return EGL_TRUE;
823
824 cleanup_driver:
825   dlclose(dri2_dpy->driver);
826 cleanup_driver_name:
827   free(dri2_dpy->driver_name);
828 cleanup_fd:
829   close(dri2_dpy->fd);
830 cleanup_drm:
831   free(dri2_dpy->device_name);
832   wl_drm_destroy(dri2_dpy->wl_drm);
833 cleanup_dpy:
834   free(dri2_dpy);
835
836   return EGL_FALSE;
837}
838