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