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 */
67static 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 */
84static _EGLPlatformType
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 */
112static EGLBoolean
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 */
141static _EGLPlatformType
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
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 */
218void
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 *
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 */
290void
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 */
321void
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 */
336EGLBoolean
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 */
357EGLBoolean
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 */
380void
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 */
392void
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 */
404EGLBoolean
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 */
416void
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 */
431void
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