1 /**************************************************************************
2  *
3  * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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 "eglcontext.h"
39 #include "eglsurface.h"
40 #include "egldisplay.h"
41 #include "egldriver.h"
42 #include "eglglobals.h"
43 #include "eglmutex.h"
44 #include "egllog.h"
45 
46 /* Includes for _eglNativePlatformDetectNativeDisplay */
47 #ifdef HAVE_MINCORE
48 #include <unistd.h>
49 #include <sys/mman.h>
50 #endif
51 #ifdef HAVE_WAYLAND_PLATFORM
52 #include <wayland-client.h>
53 #endif
54 #ifdef HAVE_DRM_PLATFORM
55 #include <gbm.h>
56 #endif
57 #ifdef HAVE_FBDEV_PLATFORM
58 #include <stdint.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #endif
62 
63 
64 /**
65  * Map --with-egl-platforms names to platform types.
66  */
67 static const struct {
68    _EGLPlatformType platform;
69    const char *name;
70 } egl_platforms[_EGL_NUM_PLATFORMS] = {
71    { _EGL_PLATFORM_WINDOWS, "gdi" },
72    { _EGL_PLATFORM_X11, "x11" },
73    { _EGL_PLATFORM_WAYLAND, "wayland" },
74    { _EGL_PLATFORM_DRM, "drm" },
75    { _EGL_PLATFORM_FBDEV, "fbdev" },
76    { _EGL_PLATFORM_NULL, "null" },
77    { _EGL_PLATFORM_ANDROID, "android" }
78 };
79 
80 
81 /**
82  * Return the native platform by parsing EGL_PLATFORM.
83  */
84 static _EGLPlatformType
_eglGetNativePlatformFromEnv(void)85 _eglGetNativePlatformFromEnv(void)
86 {
87    _EGLPlatformType plat = _EGL_INVALID_PLATFORM;
88    const char *plat_name;
89    EGLint i;
90 
91    plat_name = getenv("EGL_PLATFORM");
92    /* try deprecated env variable */
93    if (!plat_name || !plat_name[0])
94       plat_name = getenv("EGL_DISPLAY");
95    if (!plat_name || !plat_name[0])
96       return _EGL_INVALID_PLATFORM;
97 
98    for (i = 0; i < _EGL_NUM_PLATFORMS; i++) {
99       if (strcmp(egl_platforms[i].name, plat_name) == 0) {
100          plat = egl_platforms[i].platform;
101          break;
102       }
103    }
104 
105    return plat;
106 }
107 
108 
109 /**
110  * Perform validity checks on a generic pointer.
111  */
112 static EGLBoolean
_eglPointerIsDereferencable(void * p)113 _eglPointerIsDereferencable(void *p)
114 {
115 #ifdef HAVE_MINCORE
116    uintptr_t addr = (uintptr_t) p;
117    unsigned char valid = 0;
118    const long page_size = getpagesize();
119 
120    if (p == NULL)
121       return EGL_FALSE;
122 
123    /* align addr to page_size */
124    addr &= ~(page_size - 1);
125 
126    if (mincore((void *) addr, page_size, &valid) < 0) {
127       _eglLog(_EGL_DEBUG, "mincore failed: %m");
128       return EGL_FALSE;
129    }
130 
131    return (valid & 0x01) == 0x01;
132 #else
133    return p != NULL;
134 #endif
135 }
136 
137 
138 /**
139  * Try detecting native platform with the help of native display characteristcs.
140  */
141 static _EGLPlatformType
_eglNativePlatformDetectNativeDisplay(EGLNativeDisplayType nativeDisplay)142 _eglNativePlatformDetectNativeDisplay(EGLNativeDisplayType nativeDisplay)
143 {
144 #ifdef HAVE_FBDEV_PLATFORM
145    struct stat buf;
146 #endif
147 
148    if (nativeDisplay == EGL_DEFAULT_DISPLAY)
149       return _EGL_INVALID_PLATFORM;
150 
151 #ifdef HAVE_FBDEV_PLATFORM
152    /* fbdev is the only platform that can be a file descriptor. */
153    if (fstat((intptr_t) nativeDisplay, &buf) == 0 && S_ISCHR(buf.st_mode))
154       return _EGL_PLATFORM_FBDEV;
155 #endif
156 
157    if (_eglPointerIsDereferencable(nativeDisplay)) {
158       void *first_pointer = *(void **) nativeDisplay;
159 
160       (void) first_pointer; /* silence unused var warning */
161 
162 #ifdef HAVE_WAYLAND_PLATFORM
163       /* wl_display is a wl_proxy, which is a wl_object.
164        * wl_object's first element points to the interfacetype. */
165       if (first_pointer == &wl_display_interface)
166          return _EGL_PLATFORM_WAYLAND;
167 #endif
168 
169 #ifdef HAVE_DRM_PLATFORM
170       /* gbm has a pointer to its constructor as first element. */
171       if (first_pointer == gbm_create_device)
172          return _EGL_PLATFORM_DRM;
173 #endif
174 
175 #ifdef HAVE_X11_PLATFORM
176       /* If not matched to any other platform, fallback to x11. */
177       return _EGL_PLATFORM_X11;
178 #endif
179    }
180 
181    return _EGL_INVALID_PLATFORM;
182 }
183 
184 
185 /**
186  * Return the native platform.  It is the platform of the EGL native types.
187  */
188 _EGLPlatformType
_eglGetNativePlatform(EGLNativeDisplayType nativeDisplay)189 _eglGetNativePlatform(EGLNativeDisplayType nativeDisplay)
190 {
191    static _EGLPlatformType native_platform = _EGL_INVALID_PLATFORM;
192    char *detection_method = NULL;
193 
194    if (native_platform == _EGL_INVALID_PLATFORM) {
195       native_platform = _eglGetNativePlatformFromEnv();
196       detection_method = "environment overwrite";
197       if (native_platform == _EGL_INVALID_PLATFORM) {
198          native_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay);
199          detection_method = "autodetected";
200          if (native_platform == _EGL_INVALID_PLATFORM) {
201             native_platform = _EGL_NATIVE_PLATFORM;
202             detection_method = "build-time configuration";
203          }
204       }
205    }
206 
207    if (detection_method != NULL)
208       _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)",
209               egl_platforms[native_platform].name, detection_method);
210 
211    return native_platform;
212 }
213 
214 
215 /**
216  * Finish display management.
217  */
218 void
_eglFiniDisplay(void)219 _eglFiniDisplay(void)
220 {
221    _EGLDisplay *dpyList, *dpy;
222 
223    /* atexit function is called with global mutex locked */
224    dpyList = _eglGlobal.DisplayList;
225    while (dpyList) {
226       EGLint i;
227 
228       /* pop list head */
229       dpy = dpyList;
230       dpyList = dpyList->Next;
231 
232       for (i = 0; i < _EGL_NUM_RESOURCES; i++) {
233          if (dpy->ResourceLists[i]) {
234             _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", dpy);
235             break;
236          }
237       }
238 
239       free(dpy);
240    }
241    _eglGlobal.DisplayList = NULL;
242 }
243 
244 
245 /**
246  * Find the display corresponding to the specified native display, or create a
247  * new one.
248  */
249 _EGLDisplay *
_eglFindDisplay(_EGLPlatformType plat,void * plat_dpy)250 _eglFindDisplay(_EGLPlatformType plat, void *plat_dpy)
251 {
252    _EGLDisplay *dpy;
253 
254    if (plat == _EGL_INVALID_PLATFORM)
255       return NULL;
256 
257    _eglLockMutex(_eglGlobal.Mutex);
258 
259    /* search the display list first */
260    dpy = _eglGlobal.DisplayList;
261    while (dpy) {
262       if (dpy->Platform == plat && dpy->PlatformDisplay == plat_dpy)
263          break;
264       dpy = dpy->Next;
265    }
266 
267    /* create a new display */
268    if (!dpy) {
269       dpy = (_EGLDisplay *) calloc(1, sizeof(_EGLDisplay));
270       if (dpy) {
271          _eglInitMutex(&dpy->Mutex);
272          dpy->Platform = plat;
273          dpy->PlatformDisplay = plat_dpy;
274 
275          /* add to the display list */
276          dpy->Next = _eglGlobal.DisplayList;
277          _eglGlobal.DisplayList = dpy;
278       }
279    }
280 
281    _eglUnlockMutex(_eglGlobal.Mutex);
282 
283    return dpy;
284 }
285 
286 
287 /**
288  * Destroy the contexts and surfaces that are linked to the display.
289  */
290 void
_eglReleaseDisplayResources(_EGLDriver * drv,_EGLDisplay * display)291 _eglReleaseDisplayResources(_EGLDriver *drv, _EGLDisplay *display)
292 {
293    _EGLResource *list;
294 
295    list = display->ResourceLists[_EGL_RESOURCE_CONTEXT];
296    while (list) {
297       _EGLContext *ctx = (_EGLContext *) list;
298       list = list->Next;
299 
300       _eglUnlinkContext(ctx);
301       drv->API.DestroyContext(drv, display, ctx);
302    }
303    assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]);
304 
305    list = display->ResourceLists[_EGL_RESOURCE_SURFACE];
306    while (list) {
307       _EGLSurface *surf = (_EGLSurface *) list;
308       list = list->Next;
309 
310       _eglUnlinkSurface(surf);
311       drv->API.DestroySurface(drv, display, surf);
312    }
313    assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]);
314 }
315 
316 
317 /**
318  * Free all the data hanging of an _EGLDisplay object, but not
319  * the object itself.
320  */
321 void
_eglCleanupDisplay(_EGLDisplay * disp)322 _eglCleanupDisplay(_EGLDisplay *disp)
323 {
324    if (disp->Configs) {
325       _eglDestroyArray(disp->Configs, free);
326       disp->Configs = NULL;
327    }
328 
329    /* XXX incomplete */
330 }
331 
332 
333 /**
334  * Return EGL_TRUE if the given handle is a valid handle to a display.
335  */
336 EGLBoolean
_eglCheckDisplayHandle(EGLDisplay dpy)337 _eglCheckDisplayHandle(EGLDisplay dpy)
338 {
339    _EGLDisplay *cur;
340 
341    _eglLockMutex(_eglGlobal.Mutex);
342    cur = _eglGlobal.DisplayList;
343    while (cur) {
344       if (cur == (_EGLDisplay *) dpy)
345          break;
346       cur = cur->Next;
347    }
348    _eglUnlockMutex(_eglGlobal.Mutex);
349    return (cur != NULL);
350 }
351 
352 
353 /**
354  * Return EGL_TRUE if the given resource is valid.  That is, the display does
355  * own the resource.
356  */
357 EGLBoolean
_eglCheckResource(void * res,_EGLResourceType type,_EGLDisplay * dpy)358 _eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *dpy)
359 {
360    _EGLResource *list = dpy->ResourceLists[type];
361 
362    if (!res)
363       return EGL_FALSE;
364 
365    while (list) {
366       if (res == (void *) list) {
367          assert(list->Display == dpy);
368          break;
369       }
370       list = list->Next;
371    }
372 
373    return (list != NULL);
374 }
375 
376 
377 /**
378  * Initialize a display resource.
379  */
380 void
_eglInitResource(_EGLResource * res,EGLint size,_EGLDisplay * dpy)381 _eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *dpy)
382 {
383    memset(res, 0, size);
384    res->Display = dpy;
385    res->RefCount = 1;
386 }
387 
388 
389 /**
390  * Increment reference count for the resource.
391  */
392 void
_eglGetResource(_EGLResource * res)393 _eglGetResource(_EGLResource *res)
394 {
395    assert(res && res->RefCount > 0);
396    /* hopefully a resource is always manipulated with its display locked */
397    res->RefCount++;
398 }
399 
400 
401 /**
402  * Decrement reference count for the resource.
403  */
404 EGLBoolean
_eglPutResource(_EGLResource * res)405 _eglPutResource(_EGLResource *res)
406 {
407    assert(res && res->RefCount > 0);
408    res->RefCount--;
409    return (!res->RefCount);
410 }
411 
412 
413 /**
414  * Link a resource to its display.
415  */
416 void
_eglLinkResource(_EGLResource * res,_EGLResourceType type)417 _eglLinkResource(_EGLResource *res, _EGLResourceType type)
418 {
419    assert(res->Display);
420 
421    res->IsLinked = EGL_TRUE;
422    res->Next = res->Display->ResourceLists[type];
423    res->Display->ResourceLists[type] = res;
424    _eglGetResource(res);
425 }
426 
427 
428 /**
429  * Unlink a linked resource from its display.
430  */
431 void
_eglUnlinkResource(_EGLResource * res,_EGLResourceType type)432 _eglUnlinkResource(_EGLResource *res, _EGLResourceType type)
433 {
434    _EGLResource *prev;
435 
436    prev = res->Display->ResourceLists[type];
437    if (prev != res) {
438       while (prev) {
439          if (prev->Next == res)
440             break;
441          prev = prev->Next;
442       }
443       assert(prev);
444       prev->Next = res->Next;
445    }
446    else {
447       res->Display->ResourceLists[type] = res->Next;
448    }
449 
450    res->Next = NULL;
451    res->IsLinked = EGL_FALSE;
452    _eglPutResource(res);
453 
454    /* We always unlink before destroy.  The driver still owns a reference */
455    assert(res->RefCount);
456 }
457