wayland-egl: Force roundtrips to get device name and authenticate correctly
[mesa.git] / src / egl / drivers / dri2 / platform_wayland.c
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
36 #include "egl_dri2.h"
37
38 /**
39 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
40 */
41 static _EGLSurface *
42 dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
43 _EGLConfig *conf, EGLNativeWindowType window,
44 const EGLint *attrib_list)
45 {
46 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
47 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
48 struct dri2_egl_surface *dri2_surf;
49 struct dri2_egl_buffer *dri2_buf;
50 int i;
51
52 (void) drv;
53
54 dri2_surf = malloc(sizeof *dri2_surf);
55 if (!dri2_surf) {
56 _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
57 return NULL;
58 }
59
60 if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
61 goto cleanup_surf;
62
63 for (i = 0; i < WL_BUFFER_COUNT; ++i)
64 dri2_surf->wl_drm_buffer[i] = NULL;
65
66 for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
67 dri2_surf->dri_buffers[i] = NULL;
68
69 dri2_surf->block_swap_buffers = EGL_FALSE;
70
71 switch (type) {
72 case EGL_WINDOW_BIT:
73 dri2_surf->wl_win = (struct wl_egl_window *) window;
74 dri2_surf->type = DRI2_WINDOW_SURFACE;
75
76 dri2_surf->base.Width = -1;
77 dri2_surf->base.Height = -1;
78 break;
79 case EGL_PIXMAP_BIT:
80 dri2_surf->wl_pix = (struct wl_egl_pixmap *) window;
81 dri2_surf->type = DRI2_PIXMAP_SURFACE;
82
83 dri2_surf->base.Width = dri2_surf->wl_pix->width;
84 dri2_surf->base.Height = dri2_surf->wl_pix->height;
85
86 if (dri2_surf->wl_pix->name > 0) {
87 dri2_buf = dri2_surf->wl_pix->driver_private;
88 dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] = dri2_buf->dri_buffer;
89 }
90 break;
91 default:
92 goto cleanup_surf;
93 }
94
95 dri2_surf->dri_drawable =
96 (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
97 dri2_conf->dri_config, dri2_surf);
98 if (dri2_surf->dri_drawable == NULL) {
99 _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
100 goto cleanup_dri_drawable;
101 }
102
103 return &dri2_surf->base;
104
105 cleanup_dri_drawable:
106 dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
107 cleanup_surf:
108 free(dri2_surf);
109
110 return NULL;
111 }
112
113 /**
114 * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
115 */
116 static _EGLSurface *
117 dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
118 _EGLConfig *conf, EGLNativeWindowType window,
119 const EGLint *attrib_list)
120 {
121 return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
122 window, attrib_list);
123 }
124
125 static _EGLSurface *
126 dri2_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
127 _EGLConfig *conf, EGLNativePixmapType pixmap,
128 const EGLint *attrib_list)
129 {
130 return dri2_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
131 pixmap, attrib_list);
132 }
133
134 /**
135 * Called via eglDestroySurface(), drv->API.DestroySurface().
136 */
137 static EGLBoolean
138 dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
139 {
140 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
141 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
142 int i;
143
144 (void) drv;
145
146 if (!_eglPutSurface(surf))
147 return EGL_TRUE;
148
149 (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);
150
151 for (i = 0; i < WL_BUFFER_COUNT; ++i)
152 if (dri2_surf->wl_drm_buffer[i])
153 wl_buffer_destroy(dri2_surf->wl_drm_buffer[i]);
154
155 for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
156 if (dri2_surf->dri_buffers[i] && !(i == __DRI_BUFFER_FRONT_LEFT &&
157 dri2_surf->type == DRI2_PIXMAP_SURFACE))
158 dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
159 dri2_surf->dri_buffers[i]);
160
161 free(surf);
162
163 return EGL_TRUE;
164 }
165
166 static void
167 dri2_wl_egl_pixmap_destroy(struct wl_egl_pixmap *egl_pixmap)
168 {
169 struct dri2_egl_buffer *dri2_buf = egl_pixmap->driver_private;
170
171 assert(dri2_buf);
172
173 dri2_buf->dri2_dpy->dri2->releaseBuffer(dri2_buf->dri2_dpy->dri_screen,
174 dri2_buf->dri_buffer);
175
176 free(dri2_buf);
177
178 egl_pixmap->driver_private = NULL;
179 egl_pixmap->destroy = NULL;
180 egl_pixmap->name = 0;
181 }
182
183 static void
184 dri2_process_front_buffer(struct dri2_egl_surface *dri2_surf, unsigned format)
185 {
186 struct dri2_egl_display *dri2_dpy =
187 dri2_egl_display(dri2_surf->base.Resource.Display);
188 struct dri2_egl_buffer *dri2_buf;
189
190 /* allocate a back buffer for our double-buffered window*/
191 switch (dri2_surf->type) {
192 case DRI2_WINDOW_SURFACE:
193 dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT] =
194 dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
195 __DRI_BUFFER_BACK_LEFT, format,
196 dri2_surf->base.Width, dri2_surf->base.Height);
197 break;
198 case DRI2_PIXMAP_SURFACE:
199 dri2_buf = malloc(sizeof *dri2_buf);
200 if (!dri2_buf)
201 return;
202
203 dri2_buf->dri_buffer = dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT];
204 dri2_buf->dri2_dpy = dri2_dpy;
205
206 dri2_surf->wl_pix->name = dri2_buf->dri_buffer->name;
207 dri2_surf->wl_pix->stride = dri2_buf->dri_buffer->pitch;
208 dri2_surf->wl_pix->driver_private = dri2_buf;
209 dri2_surf->wl_pix->destroy = dri2_wl_egl_pixmap_destroy;
210 break;
211 default:
212 break;
213 }
214 }
215
216 static __DRIbuffer *
217 dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
218 int *width, int *height,
219 unsigned int *attachments, int count,
220 int *out_count, void *loaderPrivate)
221 {
222 struct dri2_egl_surface *dri2_surf = loaderPrivate;
223 struct dri2_egl_display *dri2_dpy =
224 dri2_egl_display(dri2_surf->base.Resource.Display);
225 int i;
226
227 if (dri2_surf->type == DRI2_WINDOW_SURFACE &&
228 (dri2_surf->base.Width != dri2_surf->wl_win->width ||
229 dri2_surf->base.Height != dri2_surf->wl_win->height)) {
230 for (i = 0; i < __DRI_BUFFER_COUNT; ++i) {
231 if (dri2_surf->dri_buffers[i]) {
232 dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
233 dri2_surf->dri_buffers[i]);
234 dri2_surf->dri_buffers[i] = NULL;
235 }
236 }
237
238 dri2_surf->base.Width = dri2_surf->wl_win->width;
239 dri2_surf->base.Height = dri2_surf->wl_win->height;
240 dri2_surf->dx = dri2_surf->wl_win->dx;
241 dri2_surf->dy = dri2_surf->wl_win->dy;
242
243 for (i = 0; i < WL_BUFFER_COUNT; ++i) {
244 if (dri2_surf->wl_drm_buffer[i])
245 wl_buffer_destroy(dri2_surf->wl_drm_buffer[i]);
246 dri2_surf->wl_drm_buffer[i] = NULL;
247 }
248 }
249
250 dri2_surf->buffer_count = 0;
251 for (i = 0; i < 2*count; i+=2) {
252 assert(attachments[i] < __DRI_BUFFER_COUNT);
253 assert(dri2_surf->buffer_count < 5);
254
255 if (dri2_surf->dri_buffers[attachments[i]] == NULL) {
256
257 dri2_surf->dri_buffers[attachments[i]] =
258 dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
259 attachments[i], attachments[i+1],
260 dri2_surf->base.Width, dri2_surf->base.Height);
261
262 if (!dri2_surf->dri_buffers[attachments[i]])
263 continue;
264
265 if (attachments[i] == __DRI_BUFFER_FRONT_LEFT)
266 dri2_process_front_buffer(dri2_surf, attachments[i+1]);
267 }
268
269 memcpy(&dri2_surf->buffers[dri2_surf->buffer_count],
270 dri2_surf->dri_buffers[attachments[i]],
271 sizeof(__DRIbuffer));
272
273 dri2_surf->buffer_count++;
274 }
275
276 assert(dri2_surf->type == DRI2_PIXMAP_SURFACE ||
277 dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
278
279 *out_count = dri2_surf->buffer_count;
280 if (dri2_surf->buffer_count == 0)
281 return NULL;
282
283 *width = dri2_surf->base.Width;
284 *height = dri2_surf->base.Height;
285
286 return dri2_surf->buffers;
287 }
288
289 static __DRIbuffer *
290 dri2_get_buffers(__DRIdrawable * driDrawable,
291 int *width, int *height,
292 unsigned int *attachments, int count,
293 int *out_count, void *loaderPrivate)
294 {
295 unsigned int *attachments_with_format;
296 __DRIbuffer *buffer;
297 const unsigned int format = 32;
298 int i;
299
300 attachments_with_format = calloc(count * 2, sizeof(unsigned int));
301 if (!attachments_with_format) {
302 *out_count = 0;
303 return NULL;
304 }
305
306 for (i = 0; i < count; ++i) {
307 attachments_with_format[2*i] = attachments[i];
308 attachments_with_format[2*i + 1] = format;
309 }
310
311 buffer =
312 dri2_get_buffers_with_format(driDrawable,
313 width, height,
314 attachments_with_format, count,
315 out_count, loaderPrivate);
316
317 free(attachments_with_format);
318
319 return buffer;
320 }
321
322
323 static void
324 dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
325 {
326 (void) driDrawable;
327
328 /* FIXME: Does EGL support front buffer rendering at all? */
329
330 #if 0
331 struct dri2_egl_surface *dri2_surf = loaderPrivate;
332
333 dri2WaitGL(dri2_surf);
334 #else
335 (void) loaderPrivate;
336 #endif
337 }
338
339 static struct wl_buffer *
340 wayland_create_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
341 {
342 struct dri2_egl_display *dri2_dpy =
343 dri2_egl_display(dri2_surf->base.Resource.Display);
344
345 return wl_drm_create_buffer(dri2_dpy->wl_dpy->drm, buffer->name,
346 dri2_surf->base.Width, dri2_surf->base.Height,
347 buffer->pitch, dri2_surf->wl_win->visual);
348 }
349
350 static void
351 wayland_frame_callback(void *data, uint32_t time)
352 {
353 struct dri2_egl_surface *dri2_surf = data;
354
355 dri2_surf->block_swap_buffers = EGL_FALSE;
356 }
357
358 static inline void
359 pointer_swap(const void **p1, const void **p2)
360 {
361 const void *tmp = *p1;
362 *p1 = *p2;
363 *p2 = tmp;
364 }
365
366 /**
367 * Called via eglSwapBuffers(), drv->API.SwapBuffers().
368 */
369 static EGLBoolean
370 dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
371 {
372 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
373 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
374 struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv);
375
376 while (dri2_surf->block_swap_buffers)
377 wl_display_iterate(dri2_dpy->wl_dpy->display, WL_DISPLAY_READABLE);
378
379 dri2_surf->block_swap_buffers = EGL_TRUE;
380 wl_display_frame_callback(dri2_dpy->wl_dpy->display,
381 wayland_frame_callback, dri2_surf);
382
383 if (dri2_surf->type == DRI2_WINDOW_SURFACE) {
384 pointer_swap(
385 (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT],
386 (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
387
388 dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]->attachment =
389 __DRI_BUFFER_FRONT_LEFT;
390 dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]->attachment =
391 __DRI_BUFFER_BACK_LEFT;
392
393 pointer_swap((const void **) &dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT],
394 (const void **) &dri2_surf->wl_drm_buffer[WL_BUFFER_BACK]);
395
396 if (!dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT])
397 dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT] =
398 wayland_create_buffer(dri2_surf,
399 dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
400
401 wl_surface_attach(dri2_surf->wl_win->surface,
402 dri2_surf->wl_drm_buffer[WL_BUFFER_FRONT],
403 dri2_surf->dx, dri2_surf->dy);
404
405 dri2_surf->wl_win->attached_width = dri2_surf->base.Width;
406 dri2_surf->wl_win->attached_height = dri2_surf->base.Height;
407 /* reset resize growing parameters */
408 dri2_surf->dx = 0;
409 dri2_surf->dy = 0;
410
411 wl_surface_damage(dri2_surf->wl_win->surface, 0, 0,
412 dri2_surf->base.Width, dri2_surf->base.Height);
413 }
414
415 _EGLContext *ctx;
416 if (dri2_drv->glFlush) {
417 ctx = _eglGetCurrentContext();
418 if (ctx && ctx->DrawSurface == &dri2_surf->base)
419 dri2_drv->glFlush();
420 }
421
422 (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
423 (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
424
425 return EGL_TRUE;
426 }
427
428 /**
429 * Called via eglCreateImageKHR(), drv->API.CreateImageKHR().
430 */
431 static _EGLImage *
432 dri2_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
433 EGLClientBuffer buffer, const EGLint *attr_list)
434 {
435 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
436 struct wl_egl_pixmap *wl_egl_pixmap = (struct wl_egl_pixmap *) buffer;
437 struct dri2_egl_buffer *dri2_buf;
438 EGLint wl_attr_list[] = {
439 EGL_WIDTH, 0,
440 EGL_HEIGHT, 0,
441 EGL_DRM_BUFFER_STRIDE_MESA, 0,
442 EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
443 EGL_NONE
444 };
445
446 dri2_buf = malloc(sizeof *dri2_buf);
447 if (!dri2_buf)
448 return NULL;
449
450 dri2_buf->dri2_dpy = dri2_dpy;
451 dri2_buf->dri_buffer =
452 dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
453 __DRI_BUFFER_FRONT_LEFT, 32,
454 wl_egl_pixmap->width,
455 wl_egl_pixmap->height);
456
457 wl_egl_pixmap->name = dri2_buf->dri_buffer->name;
458 wl_egl_pixmap->stride = dri2_buf->dri_buffer->pitch;
459 wl_egl_pixmap->destroy = dri2_wl_egl_pixmap_destroy;
460 wl_egl_pixmap->driver_private = dri2_buf;
461
462 wl_attr_list[1] = wl_egl_pixmap->width;
463 wl_attr_list[3] = wl_egl_pixmap->height;
464 wl_attr_list[5] = wl_egl_pixmap->stride / 4;
465
466
467 return dri2_create_image_khr(disp->Driver, disp, ctx, EGL_DRM_BUFFER_MESA,
468 (EGLClientBuffer)(intptr_t) wl_egl_pixmap->name, wl_attr_list);
469 }
470
471 static _EGLImage *
472 dri2_wayland_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
473 _EGLContext *ctx, EGLenum target,
474 EGLClientBuffer buffer, const EGLint *attr_list)
475 {
476 (void) drv;
477
478 switch (target) {
479 case EGL_NATIVE_PIXMAP_KHR:
480 return dri2_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
481 default:
482 return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
483 }
484 }
485
486 /**
487 * Called via eglTerminate(), drv->API.Terminate().
488 */
489 static EGLBoolean
490 dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
491 {
492 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
493
494 _eglReleaseDisplayResources(drv, disp);
495 _eglCleanupDisplay(disp);
496
497 dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
498 close(dri2_dpy->fd);
499 dlclose(dri2_dpy->driver);
500 free(dri2_dpy->driver_name);
501 free(dri2_dpy);
502 disp->DriverData = NULL;
503
504 return EGL_TRUE;
505 }
506
507 static void
508 sync_callback(void *data)
509 {
510 int *done = data;
511
512 *done = 1;
513 }
514
515 static void
516 force_roundtrip(struct wl_display *display)
517 {
518 int done = 0;
519
520 wl_display_sync_callback(display, sync_callback, &done);
521 wl_display_iterate(display, WL_DISPLAY_WRITABLE);
522 while (!done)
523 wl_display_iterate(display, WL_DISPLAY_READABLE);
524 }
525
526 EGLBoolean
527 dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
528 {
529 struct dri2_egl_display *dri2_dpy;
530 int i;
531
532 drv->API.CreateWindowSurface = dri2_create_window_surface;
533 drv->API.CreatePixmapSurface = dri2_create_pixmap_surface;
534 drv->API.DestroySurface = dri2_destroy_surface;
535 drv->API.SwapBuffers = dri2_swap_buffers;
536 drv->API.CreateImageKHR = dri2_wayland_create_image_khr;
537 drv->API.Terminate = dri2_terminate;
538
539 dri2_dpy = malloc(sizeof *dri2_dpy);
540 if (!dri2_dpy)
541 return _eglError(EGL_BAD_ALLOC, "eglInitialize");
542
543 disp->DriverData = (void *) dri2_dpy;
544 dri2_dpy->wl_dpy = disp->PlatformDisplay;
545
546 if (dri2_dpy->wl_dpy->fd == -1)
547 force_roundtrip(dri2_dpy->wl_dpy->display);
548 if (dri2_dpy->wl_dpy->fd == -1)
549 goto cleanup_dpy;
550
551 dri2_dpy->fd = dup(dri2_dpy->wl_dpy->fd);
552 if (dri2_dpy->fd < 0) {
553 _eglError(EGL_BAD_ALLOC, "DRI2: failed to dup fd");
554 goto cleanup_dpy;
555 }
556
557 if (!dri2_dpy->wl_dpy->authenticated)
558 force_roundtrip(dri2_dpy->wl_dpy->display);
559 if (!dri2_dpy->wl_dpy->authenticated)
560 goto cleanup_dpy;
561
562 dri2_dpy->driver_name = dri2_get_driver_for_fd(dri2_dpy->fd);
563 if (dri2_dpy->driver_name == NULL) {
564 _eglError(EGL_BAD_ALLOC, "DRI2: failed to get driver name");
565 goto cleanup_fd;
566 }
567
568 if (!dri2_load_driver(disp))
569 goto cleanup_driver_name;
570
571 dri2_dpy->loader_extension.base.name = __DRI_DRI2_LOADER;
572 dri2_dpy->loader_extension.base.version = 3;
573 dri2_dpy->loader_extension.getBuffers = dri2_get_buffers;
574 dri2_dpy->loader_extension.flushFrontBuffer = dri2_flush_front_buffer;
575 dri2_dpy->loader_extension.getBuffersWithFormat =
576 dri2_get_buffers_with_format;
577
578 dri2_dpy->extensions[0] = &dri2_dpy->loader_extension.base;
579 dri2_dpy->extensions[1] = &image_lookup_extension.base;
580 dri2_dpy->extensions[2] = NULL;
581
582 if (!dri2_create_screen(disp))
583 goto cleanup_driver;
584
585 for (i = 0; dri2_dpy->driver_configs[i]; i++)
586 dri2_add_config(disp, dri2_dpy->driver_configs[i], i + 1, 0,
587 EGL_WINDOW_BIT | EGL_PIXMAP_BIT);
588
589
590 disp->Extensions.MESA_drm_image = EGL_TRUE;
591 disp->Extensions.KHR_image_base = EGL_TRUE;
592 disp->Extensions.KHR_image_pixmap = EGL_TRUE;
593 disp->Extensions.KHR_gl_renderbuffer_image = EGL_TRUE;
594 disp->Extensions.KHR_gl_texture_2D_image = EGL_TRUE;
595
596 /* we're supporting EGL 1.4 */
597 disp->VersionMajor = 1;
598 disp->VersionMinor = 4;
599
600 return EGL_TRUE;
601
602 cleanup_driver:
603 dlclose(dri2_dpy->driver);
604 cleanup_driver_name:
605 free(dri2_dpy->driver_name);
606 cleanup_fd:
607 close(dri2_dpy->fd);
608 cleanup_dpy:
609 free(dri2_dpy);
610
611 return EGL_FALSE;
612 }