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