egl: Make native display detection work more than once
[mesa.git] / src / egl / main / egldisplay.c
1 /**************************************************************************
2 *
3 * Copyright 2008 VMware, Inc.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5 * Copyright 2010-2011 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30
31 /**
32 * Functions related to EGLDisplay.
33 */
34
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include "c11/threads.h"
41 #include "util/macros.h"
42 #include "util/u_atomic.h"
43
44 #include "eglcontext.h"
45 #include "eglcurrent.h"
46 #include "eglsurface.h"
47 #include "egldevice.h"
48 #include "egldisplay.h"
49 #include "egldriver.h"
50 #include "eglglobals.h"
51 #include "egllog.h"
52 #include "eglimage.h"
53 #include "eglsync.h"
54
55 /* Includes for _eglNativePlatformDetectNativeDisplay */
56 #ifdef HAVE_WAYLAND_PLATFORM
57 #include <wayland-client.h>
58 #endif
59 #ifdef HAVE_DRM_PLATFORM
60 #include <gbm.h>
61 #endif
62
63
64 /**
65 * Map build-system platform names to platform types.
66 */
67 static const struct {
68 _EGLPlatformType platform;
69 const char *name;
70 } egl_platforms[] = {
71 { _EGL_PLATFORM_X11, "x11" },
72 { _EGL_PLATFORM_WAYLAND, "wayland" },
73 { _EGL_PLATFORM_DRM, "drm" },
74 { _EGL_PLATFORM_ANDROID, "android" },
75 { _EGL_PLATFORM_HAIKU, "haiku" },
76 { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
77 { _EGL_PLATFORM_DEVICE, "device" },
78 };
79
80
81 /**
82 * Return the native platform by parsing EGL_PLATFORM.
83 */
84 static _EGLPlatformType
85 _eglGetNativePlatformFromEnv(void)
86 {
87 _EGLPlatformType plat = _EGL_INVALID_PLATFORM;
88 const char *plat_name;
89 EGLint i;
90
91 static_assert(ARRAY_SIZE(egl_platforms) == _EGL_NUM_PLATFORMS,
92 "Missing platform");
93
94 plat_name = getenv("EGL_PLATFORM");
95 /* try deprecated env variable */
96 if (!plat_name || !plat_name[0])
97 plat_name = getenv("EGL_DISPLAY");
98 if (!plat_name || !plat_name[0])
99 return _EGL_INVALID_PLATFORM;
100
101 for (i = 0; i < ARRAY_SIZE(egl_platforms); i++) {
102 if (strcmp(egl_platforms[i].name, plat_name) == 0) {
103 plat = egl_platforms[i].platform;
104 break;
105 }
106 }
107
108 if (plat == _EGL_INVALID_PLATFORM)
109 _eglLog(_EGL_WARNING, "invalid EGL_PLATFORM given");
110
111 return plat;
112 }
113
114
115 /**
116 * Try detecting native platform with the help of native display characteristcs.
117 */
118 static _EGLPlatformType
119 _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
120 {
121 if (nativeDisplay == EGL_DEFAULT_DISPLAY)
122 return _EGL_INVALID_PLATFORM;
123
124 if (_eglPointerIsDereferencable(nativeDisplay)) {
125 void *first_pointer = *(void **) nativeDisplay;
126
127 (void) first_pointer; /* silence unused var warning */
128
129 #ifdef HAVE_WAYLAND_PLATFORM
130 /* wl_display is a wl_proxy, which is a wl_object.
131 * wl_object's first element points to the interfacetype. */
132 if (first_pointer == &wl_display_interface)
133 return _EGL_PLATFORM_WAYLAND;
134 #endif
135
136 #ifdef HAVE_DRM_PLATFORM
137 /* gbm has a pointer to its constructor as first element. */
138 if (first_pointer == gbm_create_device)
139 return _EGL_PLATFORM_DRM;
140 #endif
141
142 #ifdef HAVE_X11_PLATFORM
143 /* If not matched to any other platform, fallback to x11. */
144 return _EGL_PLATFORM_X11;
145 #endif
146
147 #ifdef HAVE_HAIKU_PLATFORM
148 return _EGL_PLATFORM_HAIKU;
149 #endif
150 }
151
152 return _EGL_INVALID_PLATFORM;
153 }
154
155
156 /**
157 * Return the native platform. It is the platform of the EGL native types.
158 */
159 _EGLPlatformType
160 _eglGetNativePlatform(void *nativeDisplay)
161 {
162 _EGLPlatformType detected_platform = _eglGetNativePlatformFromEnv();
163 const char *detection_method = "environment";
164
165 if (detected_platform == _EGL_INVALID_PLATFORM) {
166 detected_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
167 detection_method = "autodetected";
168 }
169
170 if (detected_platform == _EGL_INVALID_PLATFORM) {
171 detected_platform = _EGL_NATIVE_PLATFORM;
172 detection_method = "build-time configuration";
173 }
174
175 _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
176 egl_platforms[detected_platform].name, detection_method);
177
178 return detected_platform;
179 }
180
181
182 /**
183 * Finish display management.
184 */
185 void
186 _eglFiniDisplay(void)
187 {
188 _EGLDisplay *dispList, *disp;
189
190 /* atexit function is called with global mutex locked */
191 dispList = _eglGlobal.DisplayList;
192 while (dispList) {
193 EGLint i;
194
195 /* pop list head */
196 disp = dispList;
197 dispList = dispList->Next;
198
199 for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
200 if (disp->ResourceLists[i]) {
201 _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", disp);
202 break;
203 }
204 }
205
206
207 /* The fcntl() code in _eglGetDeviceDisplay() ensures that valid fd >= 3,
208 * and invalid one is 0.
209 */
210 if (disp->Options.fd)
211 close(disp->Options.fd);
212
213 free(disp->Options.Attribs);
214 free(disp);
215 }
216 _eglGlobal.DisplayList = NULL;
217 }
218
219 static EGLBoolean
220 _eglSameAttribs(const EGLAttrib *a, const EGLAttrib *b)
221 {
222 size_t na = _eglNumAttribs(a);
223 size_t nb = _eglNumAttribs(b);
224
225 /* different numbers of attributes must be different */
226 if (na != nb)
227 return EGL_FALSE;
228
229 /* both lists NULL are the same */
230 if (!a && !b)
231 return EGL_TRUE;
232
233 /* otherwise, compare the lists */
234 return memcmp(a, b, na * sizeof(a[0])) == 0 ? EGL_TRUE : EGL_FALSE;
235 }
236
237 /**
238 * Find the display corresponding to the specified native display, or create a
239 * new one. EGL 1.5 says:
240 *
241 * Multiple calls made to eglGetPlatformDisplay with the same parameters
242 * will return the same EGLDisplay handle.
243 *
244 * We read this extremely strictly, and treat a call with NULL attribs as
245 * different from a call with attribs only equal to { EGL_NONE }. Similarly
246 * we do not sort the attribute list, so even if all attribute _values_ are
247 * identical, different attribute orders will be considered different
248 * parameters.
249 */
250 _EGLDisplay *
251 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy,
252 const EGLAttrib *attrib_list)
253 {
254 _EGLDisplay *disp;
255 size_t num_attribs;
256
257 if (plat == _EGL_INVALID_PLATFORM)
258 return NULL;
259
260 mtx_lock(_eglGlobal.Mutex);
261
262 /* search the display list first */
263 for (disp = _eglGlobal.DisplayList; disp; disp = disp->Next) {
264 if (disp->Platform == plat && disp->PlatformDisplay == plat_dpy &&
265 _eglSameAttribs(disp->Options.Attribs, attrib_list))
266 break;
267 }
268
269 /* create a new display */
270 if (!disp) {
271 disp = calloc(1, sizeof(_EGLDisplay));
272 if (disp) {
273 mtx_init(&disp->Mutex, mtx_plain);
274 disp->Platform = plat;
275 disp->PlatformDisplay = plat_dpy;
276 num_attribs = _eglNumAttribs(attrib_list);
277 if (num_attribs) {
278 disp->Options.Attribs = calloc(num_attribs, sizeof(EGLAttrib));
279 if (!disp->Options.Attribs) {
280 free(disp);
281 disp = NULL;
282 goto out;
283 }
284 memcpy(disp->Options.Attribs, attrib_list,
285 num_attribs * sizeof(EGLAttrib));
286 }
287 /* add to the display list */
288 disp->Next = _eglGlobal.DisplayList;
289 _eglGlobal.DisplayList = disp;
290 }
291 }
292
293 out:
294 mtx_unlock(_eglGlobal.Mutex);
295
296 return disp;
297 }
298
299
300 /**
301 * Destroy the contexts and surfaces that are linked to the display.
302 */
303 void
304 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
305 {
306 _EGLResource *list;
307
308 list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
309 while (list) {
310 _EGLContext *ctx = (_EGLContext *) list;
311 list = list->Next;
312
313 _eglUnlinkContext(ctx);
314 drv->API.DestroyContext(drv, display, ctx);
315 }
316 assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
317
318 list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
319 while (list) {
320 _EGLSurface *surf = (_EGLSurface *) list;
321 list = list->Next;
322
323 _eglUnlinkSurface(surf);
324 drv->API.DestroySurface(drv, display, surf);
325 }
326 assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
327
328 list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
329 while (list) {
330 _EGLImage *image = (_EGLImage *) list;
331 list = list->Next;
332
333 _eglUnlinkImage(image);
334 drv->API.DestroyImageKHR(drv, display, image);
335 }
336 assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
337
338 list = display->ResourceLists[_EGL_RESOURCE_SYNC];
339 while (list) {
340 _EGLSync *sync = (_EGLSync *) list;
341 list = list->Next;
342
343 _eglUnlinkSync(sync);
344 drv->API.DestroySyncKHR(drv, display, sync);
345 }
346 assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
347 }
348
349
350 /**
351 * Free all the data hanging of an _EGLDisplay object, but not
352 * the object itself.
353 */
354 void
355 _eglCleanupDisplay(_EGLDisplay *disp)
356 {
357 if (disp->Configs) {
358 _eglDestroyArray(disp->Configs, free);
359 disp->Configs = NULL;
360 }
361
362 /* XXX incomplete */
363 }
364
365
366 /**
367 * Return EGL_TRUE if the given handle is a valid handle to a display.
368 */
369 EGLBoolean
370 _eglCheckDisplayHandle(EGLDisplay dpy)
371 {
372 _EGLDisplay *cur;
373
374 mtx_lock(_eglGlobal.Mutex);
375 cur = _eglGlobal.DisplayList;
376 while (cur) {
377 if (cur == (_EGLDisplay *) dpy)
378 break;
379 cur = cur->Next;
380 }
381 mtx_unlock(_eglGlobal.Mutex);
382 return (cur != NULL);
383 }
384
385
386 /**
387 * Return EGL_TRUE if the given resource is valid. That is, the display does
388 * own the resource.
389 */
390 EGLBoolean
391 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *disp)
392 {
393 _EGLResource *list = disp->ResourceLists[type];
394
395 if (!res)
396 return EGL_FALSE;
397
398 while (list) {
399 if (res == (void *) list) {
400 assert(list->Display == disp);
401 break;
402 }
403 list = list->Next;
404 }
405
406 return (list != NULL);
407 }
408
409
410 /**
411 * Initialize a display resource. The size of the subclass object is
412 * specified.
413 *
414 * This is supposed to be called from the initializers of subclasses, such as
415 * _eglInitContext or _eglInitSurface.
416 */
417 void
418 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *disp)
419 {
420 memset(res, 0, size);
421 res->Display = disp;
422 res->RefCount = 1;
423 }
424
425
426 /**
427 * Increment reference count for the resource.
428 */
429 void
430 _eglGetResource(_EGLResource *res)
431 {
432 assert(res && res->RefCount > 0);
433 /* hopefully a resource is always manipulated with its display locked */
434 res->RefCount++;
435 }
436
437
438 /**
439 * Decrement reference count for the resource.
440 */
441 EGLBoolean
442 _eglPutResource(_EGLResource *res)
443 {
444 assert(res && res->RefCount > 0);
445 res->RefCount--;
446 return (!res->RefCount);
447 }
448
449
450 /**
451 * Link a resource to its display.
452 */
453 void
454 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
455 {
456 assert(res->Display);
457
458 res->IsLinked = EGL_TRUE;
459 res->Next = res->Display->ResourceLists[type];
460 res->Display->ResourceLists[type] = res;
461 _eglGetResource(res);
462 }
463
464
465 /**
466 * Unlink a linked resource from its display.
467 */
468 void
469 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
470 {
471 _EGLResource *prev;
472
473 prev = res->Display->ResourceLists[type];
474 if (prev != res) {
475 while (prev) {
476 if (prev->Next == res)
477 break;
478 prev = prev->Next;
479 }
480 assert(prev);
481 prev->Next = res->Next;
482 }
483 else {
484 res->Display->ResourceLists[type] = res->Next;
485 }
486
487 res->Next = NULL;
488 res->IsLinked = EGL_FALSE;
489 _eglPutResource(res);
490
491 /* We always unlink before destroy. The driver still owns a reference */
492 assert(res->RefCount);
493 }
494
495 #ifdef HAVE_X11_PLATFORM
496 _EGLDisplay*
497 _eglGetX11Display(Display *native_display,
498 const EGLAttrib *attrib_list)
499 {
500 /* EGL_EXT_platform_x11 recognizes exactly one attribute,
501 * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
502 */
503 if (attrib_list != NULL) {
504 for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
505 if (attrib_list[i] != EGL_PLATFORM_X11_SCREEN_EXT) {
506 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
507 return NULL;
508 }
509 }
510 }
511 return _eglFindDisplay(_EGL_PLATFORM_X11, native_display, attrib_list);
512 }
513 #endif /* HAVE_X11_PLATFORM */
514
515 #ifdef HAVE_DRM_PLATFORM
516 _EGLDisplay*
517 _eglGetGbmDisplay(struct gbm_device *native_display,
518 const EGLAttrib *attrib_list)
519 {
520 /* EGL_MESA_platform_gbm recognizes no attributes. */
521 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
522 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
523 return NULL;
524 }
525
526 return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display, attrib_list);
527 }
528 #endif /* HAVE_DRM_PLATFORM */
529
530 #ifdef HAVE_WAYLAND_PLATFORM
531 _EGLDisplay*
532 _eglGetWaylandDisplay(struct wl_display *native_display,
533 const EGLAttrib *attrib_list)
534 {
535 /* EGL_EXT_platform_wayland recognizes no attributes. */
536 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
537 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
538 return NULL;
539 }
540
541 return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display, attrib_list);
542 }
543 #endif /* HAVE_WAYLAND_PLATFORM */
544
545 #ifdef HAVE_SURFACELESS_PLATFORM
546 _EGLDisplay*
547 _eglGetSurfacelessDisplay(void *native_display,
548 const EGLAttrib *attrib_list)
549 {
550 /* This platform has no native display. */
551 if (native_display != NULL) {
552 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
553 return NULL;
554 }
555
556 /* This platform recognizes no display attributes. */
557 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
558 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
559 return NULL;
560 }
561
562 return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display,
563 attrib_list);
564 }
565 #endif /* HAVE_SURFACELESS_PLATFORM */
566
567 #ifdef HAVE_ANDROID_PLATFORM
568 _EGLDisplay*
569 _eglGetAndroidDisplay(void *native_display,
570 const EGLAttrib *attrib_list)
571 {
572
573 /* This platform recognizes no display attributes. */
574 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
575 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
576 return NULL;
577 }
578
579 return _eglFindDisplay(_EGL_PLATFORM_ANDROID, native_display,
580 attrib_list);
581 }
582 #endif /* HAVE_ANDROID_PLATFORM */
583
584 _EGLDisplay*
585 _eglGetDeviceDisplay(void *native_display,
586 const EGLAttrib *attrib_list)
587 {
588 _EGLDevice *dev;
589 _EGLDisplay *display;
590 int fd = -1;
591
592 dev = _eglLookupDevice(native_display);
593 if (!dev) {
594 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
595 return NULL;
596 }
597
598 if (attrib_list) {
599 for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
600 EGLAttrib attrib = attrib_list[i];
601 EGLAttrib value = attrib_list[i + 1];
602
603 /* EGL_EXT_platform_device does not recognize any attributes,
604 * EGL_EXT_device_drm adds the optional EGL_DRM_MASTER_FD_EXT.
605 */
606
607 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM) ||
608 attrib != EGL_DRM_MASTER_FD_EXT) {
609 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
610 return NULL;
611 }
612
613 fd = (int) value;
614 }
615 }
616
617 display = _eglFindDisplay(_EGL_PLATFORM_DEVICE, native_display, attrib_list);
618 if (!display) {
619 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
620 return NULL;
621 }
622
623 /* If the fd is explicitly provided and we did not dup() it yet, do so.
624 * The spec mandates that we do so, since we'll need it past the
625 * eglGetPlatformDispay call.
626 *
627 * The new fd is guaranteed to be 3 or greater.
628 */
629 if (fd != -1 && display->Options.fd == 0) {
630 display->Options.fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
631 if (display->Options.fd == -1) {
632 /* Do not (really) need to teardown the display */
633 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
634 return NULL;
635 }
636 }
637
638 return display;
639 }