egl: warn user if they set an invalid EGL_PLATFORM
[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 static _EGLPlatformType native_platform = _EGL_INVALID_PLATFORM;
163 _EGLPlatformType detected_platform = native_platform;
164
165 if (detected_platform == _EGL_INVALID_PLATFORM) {
166 const char *detection_method;
167
168 detected_platform = _eglGetNativePlatformFromEnv();
169 detection_method = "environment overwrite";
170
171 if (detected_platform == _EGL_INVALID_PLATFORM) {
172 detected_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
173 detection_method = "autodetected";
174 }
175
176 if (detected_platform == _EGL_INVALID_PLATFORM) {
177 detected_platform = _EGL_NATIVE_PLATFORM;
178 detection_method = "build-time configuration";
179 }
180
181 _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
182 egl_platforms[detected_platform].name, detection_method);
183
184 p_atomic_cmpxchg(&native_platform, _EGL_INVALID_PLATFORM,
185 detected_platform);
186 }
187
188 return native_platform;
189 }
190
191
192 /**
193 * Finish display management.
194 */
195 void
196 _eglFiniDisplay(void)
197 {
198 _EGLDisplay *dispList, *disp;
199
200 /* atexit function is called with global mutex locked */
201 dispList = _eglGlobal.DisplayList;
202 while (dispList) {
203 EGLint i;
204
205 /* pop list head */
206 disp = dispList;
207 dispList = dispList->Next;
208
209 for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
210 if (disp->ResourceLists[i]) {
211 _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", disp);
212 break;
213 }
214 }
215
216
217 /* The fcntl() code in _eglGetDeviceDisplay() ensures that valid fd >= 3,
218 * and invalid one is 0.
219 */
220 if (disp->Options.fd)
221 close(disp->Options.fd);
222
223 free(disp->Options.Attribs);
224 free(disp);
225 }
226 _eglGlobal.DisplayList = NULL;
227 }
228
229 static EGLBoolean
230 _eglSameAttribs(const EGLAttrib *a, const EGLAttrib *b)
231 {
232 size_t na = _eglNumAttribs(a);
233 size_t nb = _eglNumAttribs(b);
234
235 /* different numbers of attributes must be different */
236 if (na != nb)
237 return EGL_FALSE;
238
239 /* both lists NULL are the same */
240 if (!a && !b)
241 return EGL_TRUE;
242
243 /* otherwise, compare the lists */
244 return memcmp(a, b, na * sizeof(a[0])) == 0 ? EGL_TRUE : EGL_FALSE;
245 }
246
247 /**
248 * Find the display corresponding to the specified native display, or create a
249 * new one. EGL 1.5 says:
250 *
251 * Multiple calls made to eglGetPlatformDisplay with the same parameters
252 * will return the same EGLDisplay handle.
253 *
254 * We read this extremely strictly, and treat a call with NULL attribs as
255 * different from a call with attribs only equal to { EGL_NONE }. Similarly
256 * we do not sort the attribute list, so even if all attribute _values_ are
257 * identical, different attribute orders will be considered different
258 * parameters.
259 */
260 _EGLDisplay *
261 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy,
262 const EGLAttrib *attrib_list)
263 {
264 _EGLDisplay *disp;
265 size_t num_attribs;
266
267 if (plat == _EGL_INVALID_PLATFORM)
268 return NULL;
269
270 mtx_lock(_eglGlobal.Mutex);
271
272 /* search the display list first */
273 for (disp = _eglGlobal.DisplayList; disp; disp = disp->Next) {
274 if (disp->Platform == plat && disp->PlatformDisplay == plat_dpy &&
275 _eglSameAttribs(disp->Options.Attribs, attrib_list))
276 break;
277 }
278
279 /* create a new display */
280 if (!disp) {
281 disp = calloc(1, sizeof(_EGLDisplay));
282 if (disp) {
283 mtx_init(&disp->Mutex, mtx_plain);
284 disp->Platform = plat;
285 disp->PlatformDisplay = plat_dpy;
286 num_attribs = _eglNumAttribs(attrib_list);
287 if (num_attribs) {
288 disp->Options.Attribs = calloc(num_attribs, sizeof(EGLAttrib));
289 if (!disp->Options.Attribs) {
290 free(disp);
291 disp = NULL;
292 goto out;
293 }
294 memcpy(disp->Options.Attribs, attrib_list,
295 num_attribs * sizeof(EGLAttrib));
296 }
297 /* add to the display list */
298 disp->Next = _eglGlobal.DisplayList;
299 _eglGlobal.DisplayList = disp;
300 }
301 }
302
303 out:
304 mtx_unlock(_eglGlobal.Mutex);
305
306 return disp;
307 }
308
309
310 /**
311 * Destroy the contexts and surfaces that are linked to the display.
312 */
313 void
314 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
315 {
316 _EGLResource *list;
317
318 list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
319 while (list) {
320 _EGLContext *ctx = (_EGLContext *) list;
321 list = list->Next;
322
323 _eglUnlinkContext(ctx);
324 drv->API.DestroyContext(drv, display, ctx);
325 }
326 assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
327
328 list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
329 while (list) {
330 _EGLSurface *surf = (_EGLSurface *) list;
331 list = list->Next;
332
333 _eglUnlinkSurface(surf);
334 drv->API.DestroySurface(drv, display, surf);
335 }
336 assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
337
338 list = display->ResourceLists[_EGL_RESOURCE_IMAGE];
339 while (list) {
340 _EGLImage *image = (_EGLImage *) list;
341 list = list->Next;
342
343 _eglUnlinkImage(image);
344 drv->API.DestroyImageKHR(drv, display, image);
345 }
346 assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]);
347
348 list = display->ResourceLists[_EGL_RESOURCE_SYNC];
349 while (list) {
350 _EGLSync *sync = (_EGLSync *) list;
351 list = list->Next;
352
353 _eglUnlinkSync(sync);
354 drv->API.DestroySyncKHR(drv, display, sync);
355 }
356 assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]);
357 }
358
359
360 /**
361 * Free all the data hanging of an _EGLDisplay object, but not
362 * the object itself.
363 */
364 void
365 _eglCleanupDisplay(_EGLDisplay *disp)
366 {
367 if (disp->Configs) {
368 _eglDestroyArray(disp->Configs, free);
369 disp->Configs = NULL;
370 }
371
372 /* XXX incomplete */
373 }
374
375
376 /**
377 * Return EGL_TRUE if the given handle is a valid handle to a display.
378 */
379 EGLBoolean
380 _eglCheckDisplayHandle(EGLDisplay dpy)
381 {
382 _EGLDisplay *cur;
383
384 mtx_lock(_eglGlobal.Mutex);
385 cur = _eglGlobal.DisplayList;
386 while (cur) {
387 if (cur == (_EGLDisplay *) dpy)
388 break;
389 cur = cur->Next;
390 }
391 mtx_unlock(_eglGlobal.Mutex);
392 return (cur != NULL);
393 }
394
395
396 /**
397 * Return EGL_TRUE if the given resource is valid. That is, the display does
398 * own the resource.
399 */
400 EGLBoolean
401 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *disp)
402 {
403 _EGLResource *list = disp->ResourceLists[type];
404
405 if (!res)
406 return EGL_FALSE;
407
408 while (list) {
409 if (res == (void *) list) {
410 assert(list->Display == disp);
411 break;
412 }
413 list = list->Next;
414 }
415
416 return (list != NULL);
417 }
418
419
420 /**
421 * Initialize a display resource. The size of the subclass object is
422 * specified.
423 *
424 * This is supposed to be called from the initializers of subclasses, such as
425 * _eglInitContext or _eglInitSurface.
426 */
427 void
428 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *disp)
429 {
430 memset(res, 0, size);
431 res->Display = disp;
432 res->RefCount = 1;
433 }
434
435
436 /**
437 * Increment reference count for the resource.
438 */
439 void
440 _eglGetResource(_EGLResource *res)
441 {
442 assert(res && res->RefCount > 0);
443 /* hopefully a resource is always manipulated with its display locked */
444 res->RefCount++;
445 }
446
447
448 /**
449 * Decrement reference count for the resource.
450 */
451 EGLBoolean
452 _eglPutResource(_EGLResource *res)
453 {
454 assert(res && res->RefCount > 0);
455 res->RefCount--;
456 return (!res->RefCount);
457 }
458
459
460 /**
461 * Link a resource to its display.
462 */
463 void
464 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
465 {
466 assert(res->Display);
467
468 res->IsLinked = EGL_TRUE;
469 res->Next = res->Display->ResourceLists[type];
470 res->Display->ResourceLists[type] = res;
471 _eglGetResource(res);
472 }
473
474
475 /**
476 * Unlink a linked resource from its display.
477 */
478 void
479 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
480 {
481 _EGLResource *prev;
482
483 prev = res->Display->ResourceLists[type];
484 if (prev != res) {
485 while (prev) {
486 if (prev->Next == res)
487 break;
488 prev = prev->Next;
489 }
490 assert(prev);
491 prev->Next = res->Next;
492 }
493 else {
494 res->Display->ResourceLists[type] = res->Next;
495 }
496
497 res->Next = NULL;
498 res->IsLinked = EGL_FALSE;
499 _eglPutResource(res);
500
501 /* We always unlink before destroy. The driver still owns a reference */
502 assert(res->RefCount);
503 }
504
505 #ifdef HAVE_X11_PLATFORM
506 _EGLDisplay*
507 _eglGetX11Display(Display *native_display,
508 const EGLAttrib *attrib_list)
509 {
510 /* EGL_EXT_platform_x11 recognizes exactly one attribute,
511 * EGL_PLATFORM_X11_SCREEN_EXT, which is optional.
512 */
513 if (attrib_list != NULL) {
514 for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
515 if (attrib_list[i] != EGL_PLATFORM_X11_SCREEN_EXT) {
516 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
517 return NULL;
518 }
519 }
520 }
521 return _eglFindDisplay(_EGL_PLATFORM_X11, native_display, attrib_list);
522 }
523 #endif /* HAVE_X11_PLATFORM */
524
525 #ifdef HAVE_DRM_PLATFORM
526 _EGLDisplay*
527 _eglGetGbmDisplay(struct gbm_device *native_display,
528 const EGLAttrib *attrib_list)
529 {
530 /* EGL_MESA_platform_gbm recognizes no attributes. */
531 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
532 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
533 return NULL;
534 }
535
536 return _eglFindDisplay(_EGL_PLATFORM_DRM, native_display, attrib_list);
537 }
538 #endif /* HAVE_DRM_PLATFORM */
539
540 #ifdef HAVE_WAYLAND_PLATFORM
541 _EGLDisplay*
542 _eglGetWaylandDisplay(struct wl_display *native_display,
543 const EGLAttrib *attrib_list)
544 {
545 /* EGL_EXT_platform_wayland recognizes no attributes. */
546 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
547 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
548 return NULL;
549 }
550
551 return _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display, attrib_list);
552 }
553 #endif /* HAVE_WAYLAND_PLATFORM */
554
555 #ifdef HAVE_SURFACELESS_PLATFORM
556 _EGLDisplay*
557 _eglGetSurfacelessDisplay(void *native_display,
558 const EGLAttrib *attrib_list)
559 {
560 /* This platform has no native display. */
561 if (native_display != NULL) {
562 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
563 return NULL;
564 }
565
566 /* This platform recognizes no display attributes. */
567 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
568 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
569 return NULL;
570 }
571
572 return _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, native_display,
573 attrib_list);
574 }
575 #endif /* HAVE_SURFACELESS_PLATFORM */
576
577 #ifdef HAVE_ANDROID_PLATFORM
578 _EGLDisplay*
579 _eglGetAndroidDisplay(void *native_display,
580 const EGLAttrib *attrib_list)
581 {
582
583 /* This platform recognizes no display attributes. */
584 if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
585 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
586 return NULL;
587 }
588
589 return _eglFindDisplay(_EGL_PLATFORM_ANDROID, native_display,
590 attrib_list);
591 }
592 #endif /* HAVE_ANDROID_PLATFORM */
593
594 _EGLDisplay*
595 _eglGetDeviceDisplay(void *native_display,
596 const EGLAttrib *attrib_list)
597 {
598 _EGLDevice *dev;
599 _EGLDisplay *display;
600 int fd = -1;
601
602 dev = _eglLookupDevice(native_display);
603 if (!dev) {
604 _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay");
605 return NULL;
606 }
607
608 if (attrib_list) {
609 for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) {
610 EGLAttrib attrib = attrib_list[i];
611 EGLAttrib value = attrib_list[i + 1];
612
613 /* EGL_EXT_platform_device does not recognize any attributes,
614 * EGL_EXT_device_drm adds the optional EGL_DRM_MASTER_FD_EXT.
615 */
616
617 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM) ||
618 attrib != EGL_DRM_MASTER_FD_EXT) {
619 _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
620 return NULL;
621 }
622
623 fd = (int) value;
624 }
625 }
626
627 display = _eglFindDisplay(_EGL_PLATFORM_DEVICE, native_display, attrib_list);
628 if (!display) {
629 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
630 return NULL;
631 }
632
633 /* If the fd is explicitly provided and we did not dup() it yet, do so.
634 * The spec mandates that we do so, since we'll need it past the
635 * eglGetPlatformDispay call.
636 *
637 * The new fd is guaranteed to be 3 or greater.
638 */
639 if (fd != -1 && display->Options.fd == 0) {
640 display->Options.fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
641 if (display->Options.fd == -1) {
642 /* Do not (really) need to teardown the display */
643 _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay");
644 return NULL;
645 }
646 }
647
648 return display;
649 }