egl: Change the way drivers are loaded.
[mesa.git] / src / egl / main / egldriver.c
index 6e8c9b3b4c80df9baff1abbfd542f6418ce02f40..36cc2948c0ea55dd13f867875c92ae66b9326068 100644 (file)
+/**
+ * Functions for choosing and opening/loading device drivers.
+ */
+
+
 #include <assert.h>
-#include <dlfcn.h>
-#include <stdio.h>
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include "eglconfig.h"
 #include "eglcontext.h"
+#include "egldefines.h"
 #include "egldisplay.h"
 #include "egldriver.h"
 #include "eglglobals.h"
+#include "egllog.h"
+#include "eglmisc.h"
 #include "eglmode.h"
 #include "eglscreen.h"
+#include "eglstring.h"
 #include "eglsurface.h"
 
+#if defined(_EGL_PLATFORM_X)
+#include <dlfcn.h>
+#include "eglx.h"
+#elif defined(_EGL_PLATFORM_WINDOWS)
+/* Use static linking on Windows for now */
+#define WINDOWS_STATIC_LINK
+#endif
+
+/**
+ * Wrappers for dlopen/dlclose()
+ */
+#if defined(_EGL_PLATFORM_WINDOWS)
+#ifdef WINDOWS_STATIC_LINK
+   static const char *DefaultDriverName = "Windows EGL Static Library";
+#else
+   /* XXX Need to decide how to do dynamic name lookup on Windows */
+   static const char *DefaultDriverName = "TBD";
+#endif
+   static const char *SysFS = NULL;
+   typedef HMODULE lib_handle;
+
+   static HMODULE
+   open_library(const char *filename)
+   {
+#ifdef WINDOWS_STATIC_LINK
+      return 0;
+#else
+      return LoadLibrary(filename);
+#endif
+   }
+
+   static void
+   close_library(HMODULE lib)
+   {
+#ifdef WINDOWS_STATIC_LINK
+#else
+      FreeLibrary(lib);
+#endif
+   }
+
+#elif defined(_EGL_PLATFORM_X)
+   static const char *DefaultDriverName = ":0";
+   static const char *SysFS = "/sys/class";
+
+   typedef void * lib_handle;
+
+   static void *
+   open_library(const char *filename)
+   {
+      return dlopen(filename, RTLD_LAZY);
+   }
+
+   static void
+   close_library(void *lib)
+   {
+      dlclose(lib);
+   }
+   
+#endif
+
+/**
+ * Given a card number, use sysfs to determine the DRI driver name.
+ */
+const char *
+_eglChooseDRMDriver(int card)
+{
+#if 0
+   return _eglstrdup("libEGLdri");
+#else
+   char path[2000], driverName[2000];
+   FILE *f;
+   int length;
 
-const char *DefaultDriverName = "demodriver";
+   snprintf(path, sizeof(path), "%s/drm/card%d/dri_library_name", SysFS, card);
+
+   f = fopen(path, "r");
+   if (!f)
+      return NULL;
+
+   fgets(driverName, sizeof(driverName), f);
+   fclose(f);
+
+   if ((length = strlen(driverName)) > 1) {
+      /* remove the trailing newline from sysfs */
+      driverName[length - 1] = '\0';
+      strncat(driverName, "_dri", sizeof(driverName));
+      return _eglstrdup(driverName);
+   }
+   else {
+      return NULL;
+   }   
+#endif
+}
 
 
 /**
- * Choose and open/init the hardware driver for the given EGLDisplay.
- * Previously, the EGLDisplay was created with _eglNewDisplay() where
- * we recorded the user's NativeDisplayType parameter.
+ * XXX this function is totally subject change!!!
  *
- * Now we'll use the NativeDisplayType value.
  *
- * Currently, the native display value is treated as a string.
+ * Determine/return the path of the driver to use for the given native display.
+ *
+ * Try to be clever and determine if nativeDisplay is an Xlib Display
+ * ptr or a string (naming a driver or screen number, etc).
+ *
  * If the first character is ':' we interpret it as a screen or card index
  * number (i.e. ":0" or ":1", etc)
  * Else if the first character is '!' we interpret it as specific driver name
  * (i.e. "!r200" or "!i830".
+ *
+ * Whatever follows ':' is interpreted as arguments.
+ *
+ * The caller may free() the returned strings.
  */
-_EGLDriver *
-_eglChooseDriver(EGLDisplay display)
+static char *
+_eglChooseDriver(_EGLDisplay *dpy, char **argsRet)
 {
-   _EGLDisplay *dpy = _eglLookupDisplay(display);
-   _EGLDriver *drv;
-   const char *driverName = DefaultDriverName;
-   const char *name;
-
-   assert(dpy);
-
-   name = dpy->Name;
-   if (!name) {
-      /* use default */
-   }
-   else if (name[0] == ':' && (name[1] >= '0' && name[1] <= '9') && !name[2]) {
-      printf("EGL: Use driver for screen: %s\n", name);
-      /* XXX probe hardware here to determine which driver to open */
-      driverName = "libEGLdri";
-   }
-   else if (name[0] == '!') {
-      /* use specified driver name */
-      driverName = name + 1;
-      printf("EGL: Use driver named %s\n", driverName);
-   }
-   else {
-      /* Maybe display was returned by XOpenDisplay? */
-      printf("EGL: can't parse display pointer\n");
+   char *path = NULL;
+   const char *args = NULL;
+
+   path = getenv("EGL_DRIVER");
+   if (path)
+      path = _eglstrdup(path);
+
+#if defined(_EGL_PLATFORM_X)
+   (void) DefaultDriverName;
+
+   if (!path && dpy->NativeDisplay) {
+      const char *dpyString = (const char *) dpy->NativeDisplay;
+      char *p;
+      /* parse the display string */
+      if (dpyString[0] == '!' || dpyString[0] == ':') {
+         if (dpyString[0] == '!') {
+            path = _eglstrdup(dpyString);
+            p = strchr(path, ':');
+            if (p)
+               *p++ = '\0';
+         } else {
+            p = strchr(dpyString, ':');
+            if (p)
+               p++;
+         }
+
+         if (p) {
+            if (!path && p[0] >= '0' && p[0] <= '9' && !p[1]) {
+               int card = atoi(p);
+               path = (char *) _eglChooseDRMDriver(card);
+            }
+            args = p;
+         }
+      }
+      else {
+         path = (char *) _xeglChooseDriver(dpy);
+      }
    }
+#elif defined(_EGL_PLATFORM_WINDOWS)
+   if (!path)
+      path = _eglstrdup(DefaultDriverName);
+#endif /* _EGL_PLATFORM_X */
 
-   drv = _eglOpenDriver(dpy, driverName);
-   dpy->Driver = drv;
+   if (path && argsRet)
+      *argsRet = (args) ? _eglstrdup(args) : NULL;
 
-   return drv;
+   return path;
 }
 
 
 /**
- * Open/load the named driver and call its bootstrap function: _eglMain().
- * \return  new _EGLDriver object.
+ * Open the named driver and find its bootstrap function: _eglMain().
  */
-_EGLDriver *
-_eglOpenDriver(_EGLDisplay *dpy, const char *driverName)
+static _EGLMain_t
+_eglOpenLibrary(const char *driverName, lib_handle *handle)
 {
-   _EGLDriver *drv;
    _EGLMain_t mainFunc;
-   void *lib;
+   lib_handle lib;
    char driverFilename[1000];
 
+   assert(driverName);
+
+#if defined(_EGL_PLATFORM_WINDOWS)
+/* Use static linking on Windows for now */
+#ifdef WINDOWS_STATIC_LINK
+   lib = 0;
+   mainFunc = (_EGLMain_t)_eglMain;
+#else
+   /* XXX untested */
+   sprintf(driverFilename, "%s.dll", driverName);
+   _eglLog(_EGL_DEBUG, "dlopen(%s)", driverFilename);
+   lib = open_library(driverFilename);
+   if (!lib) {
+      _eglLog(_EGL_WARNING, "Could not open %s",
+              driverFilename);
+      return NULL;
+   }
+   mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
+#endif
+#elif defined(_EGL_PLATFORM_X)
    /* XXX also prepend a directory path??? */
    sprintf(driverFilename, "%s.so", driverName);
-
-   lib = dlopen(driverFilename, RTLD_NOW);
+   _eglLog(_EGL_DEBUG, "dlopen(%s)", driverFilename);
+   lib = open_library(driverFilename);
    if (!lib) {
-      fprintf(stderr, "EGLdebug: Error opening %s: %s\n", driverFilename, dlerror());
+      _eglLog(_EGL_WARNING, "Could not open %s (%s)",
+              driverFilename, dlerror());
       return NULL;
    }
-
    mainFunc = (_EGLMain_t) dlsym(lib, "_eglMain");
+#endif
+
    if (!mainFunc) {
-      fprintf(stderr, "_eglMain not found in %s", (char *) driverFilename);
-      dlclose(lib);
+      _eglLog(_EGL_WARNING, "_eglMain not found in %s", driverFilename);
+      if (lib)
+         close_library(lib);
       return NULL;
    }
 
-   drv = mainFunc(dpy);
+   *handle = lib;
+   return mainFunc;
+}
+
+
+/**
+ * Load the named driver.  The path and args passed will be
+ * owned by the driver and freed.
+ */
+static _EGLDriver *
+_eglLoadDriver(_EGLDisplay *dpy, char *path, char *args)
+{
+   _EGLMain_t mainFunc;
+   lib_handle lib;
+   _EGLDriver *drv = NULL;
+
+   mainFunc = _eglOpenLibrary(path, &lib);
+   if (!mainFunc)
+      return NULL;
+
+   drv = mainFunc(dpy, args);
    if (!drv) {
-      dlclose(lib);
+      if (lib)
+         close_library(lib);
       return NULL;
    }
-   /* with a recurvise open you want the inner most handle */
-   if (!drv->LibHandle)
-      drv->LibHandle = lib;
-   else
-      dlclose(lib);
 
-   drv->Display = dpy;
+   if (!drv->Name) {
+      _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", path);
+      drv->Name = "UNNAMED";
+   }
+
+   drv->Path = path;
+   drv->Args = args;
+   drv->LibHandle = lib;
+
+   return drv;
+}
+
+
+/**
+ * Match a display to a preloaded driver.
+ */
+static _EGLDriver *
+_eglMatchDriver(_EGLDisplay *dpy)
+{
+   _EGLDriver *defaultDriver = NULL;
+   EGLint i;
+
+   for (i = 0; i < _eglGlobal.NumDrivers; i++) {
+      _EGLDriver *drv = _eglGlobal.Drivers[i];
+
+      /* display specifies a driver */
+      if (dpy->DriverName) {
+         if (strcmp(dpy->DriverName, drv->Name) == 0)
+            return drv;
+      }
+      else if (drv->Probe) {
+         if (drv->Probe(drv, dpy))
+            return drv;
+      }
+      else {
+         if (!defaultDriver)
+            defaultDriver = drv;
+      }
+   }
+
+   return defaultDriver;
+}
+
+
+/**
+ * Load a driver and save it.
+ */
+const char *
+_eglPreloadDriver(_EGLDisplay *dpy)
+{
+   char *path, *args;
+   _EGLDriver *drv;
+   EGLint i;
+
+   path = _eglChooseDriver(dpy, &args);
+   if (!path)
+      return NULL;
+
+   for (i = 0; i < _eglGlobal.NumDrivers; i++) {
+      drv = _eglGlobal.Drivers[i];
+      if (strcmp(drv->Path, path) == 0) {
+         _eglLog(_EGL_DEBUG, "Driver %s is already preloaded",
+                 drv->Name);
+         free(path);
+         if (args)
+            free(args);
+         return drv->Name;
+      }
+   }
+
+   drv = _eglLoadDriver(dpy, path, args);
+   if (!drv)
+      return NULL;
+
+   /* update the global notion of supported APIs */
+   _eglGlobal.ClientAPIsMask |= drv->ClientAPIsMask;
+
+   _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv;
+
+   return drv->Name;
+}
+
+
+/**
+ * Open a preloaded driver.
+ */
+_EGLDriver *
+_eglOpenDriver(_EGLDisplay *dpy)
+{
+   _EGLDriver *drv = _eglMatchDriver(dpy);
    return drv;
 }
 
 
+/**
+ * Close a preloaded driver.
+ */
 EGLBoolean
-_eglCloseDriver(_EGLDriver *drv, EGLDisplay dpy)
+_eglCloseDriver(_EGLDriver *drv, _EGLDisplay *dpy)
 {
-   void *handle = drv->LibHandle;
-   EGLBoolean b;
-   fprintf(stderr, "EGL debug: Closing driver\n");
+   _eglReleaseDisplayResources(drv, dpy);
+   drv->API.Terminate(drv, dpy);
+   return EGL_TRUE;
+}
 
-   /*
-    * XXX check for currently bound context/surfaces and delete them?
-    */
 
-   b = drv->Terminate(drv, dpy);
-   dlclose(handle);
-   return b;
+/**
+ * Unload preloaded drivers.
+ */
+void
+_eglUnloadDrivers(void)
+{
+   EGLint i;
+   for (i = 0; i < _eglGlobal.NumDrivers; i++) {
+      _EGLDriver *drv = _eglGlobal.Drivers[i];
+      lib_handle handle = drv->LibHandle;
+
+      if (drv->Path)
+         free((char *) drv->Path);
+      if (drv->Args)
+         free((char *) drv->Args);
+
+      /* destroy driver */
+      if (drv->Unload)
+         drv->Unload(drv);
+
+      if (handle)
+         close_library(handle);
+      _eglGlobal.Drivers[i] = NULL;
+   }
+
+   _eglGlobal.NumDrivers = 0;
 }
 
 
@@ -149,103 +421,96 @@ _eglInitDriverFallbacks(_EGLDriver *drv)
    /* If a pointer is set to NULL, then the device driver _really_ has
     * to implement it.
     */
-   drv->Initialize = NULL;
-   drv->Terminate = NULL;
-
-   drv->GetConfigs = _eglGetConfigs;
-   drv->ChooseConfig = _eglChooseConfig;
-   drv->GetConfigAttrib = _eglGetConfigAttrib;
-
-   drv->CreateContext = _eglCreateContext;
-   drv->DestroyContext = _eglDestroyContext;
-   drv->MakeCurrent = _eglMakeCurrent;
-   drv->QueryContext = _eglQueryContext;
-
-   drv->CreateWindowSurface = _eglCreateWindowSurface;
-   drv->CreatePixmapSurface = _eglCreatePixmapSurface;
-   drv->CreatePbufferSurface = _eglCreatePbufferSurface;
-   drv->DestroySurface = _eglDestroySurface;
-   drv->QuerySurface = _eglQuerySurface;
-   drv->SurfaceAttrib = _eglSurfaceAttrib;
-   drv->BindTexImage = _eglBindTexImage;
-   drv->ReleaseTexImage = _eglReleaseTexImage;
-   drv->SwapInterval = _eglSwapInterval;
-   drv->SwapBuffers = _eglSwapBuffers;
-   drv->CopyBuffers = _eglCopyBuffers;
-
-   drv->QueryString = _eglQueryString;
-   drv->WaitGL = _eglWaitGL;
-   drv->WaitNative = _eglWaitNative;
-
-   /* EGL_MESA_screen */
-   drv->ChooseModeMESA = _eglChooseModeMESA; 
-   drv->GetModesMESA = _eglGetModesMESA;
-   drv->GetModeAttribMESA = _eglGetModeAttribMESA;
-   drv->GetScreensMESA = _eglGetScreensMESA;
-   drv->CreateScreenSurfaceMESA = _eglCreateScreenSurfaceMESA;
-   drv->ShowSurfaceMESA = _eglShowSurfaceMESA;
-   drv->ScreenPositionMESA = _eglScreenPositionMESA;
-   drv->QueryScreenMESA = _eglQueryScreenMESA;
-   drv->QueryScreenSurfaceMESA = _eglQueryScreenSurfaceMESA;
-   drv->QueryScreenModeMESA = _eglQueryScreenModeMESA;
-   drv->QueryModeStringMESA = _eglQueryModeStringMESA;
+   drv->API.Initialize = NULL;
+   drv->API.Terminate = NULL;
+
+   drv->API.GetConfigs = _eglGetConfigs;
+   drv->API.ChooseConfig = _eglChooseConfig;
+   drv->API.GetConfigAttrib = _eglGetConfigAttrib;
+
+   drv->API.CreateContext = _eglCreateContext;
+   drv->API.DestroyContext = _eglDestroyContext;
+   drv->API.MakeCurrent = _eglMakeCurrent;
+   drv->API.QueryContext = _eglQueryContext;
+
+   drv->API.CreateWindowSurface = _eglCreateWindowSurface;
+   drv->API.CreatePixmapSurface = _eglCreatePixmapSurface;
+   drv->API.CreatePbufferSurface = _eglCreatePbufferSurface;
+   drv->API.DestroySurface = _eglDestroySurface;
+   drv->API.QuerySurface = _eglQuerySurface;
+   drv->API.SurfaceAttrib = _eglSurfaceAttrib;
+   drv->API.BindTexImage = _eglBindTexImage;
+   drv->API.ReleaseTexImage = _eglReleaseTexImage;
+   drv->API.SwapInterval = _eglSwapInterval;
+   drv->API.SwapBuffers = _eglSwapBuffers;
+   drv->API.CopyBuffers = _eglCopyBuffers;
+
+   drv->API.QueryString = _eglQueryString;
+   drv->API.WaitGL = _eglWaitGL;
+   drv->API.WaitNative = _eglWaitNative;
+
+#ifdef EGL_MESA_screen_surface
+   drv->API.ChooseModeMESA = _eglChooseModeMESA; 
+   drv->API.GetModesMESA = _eglGetModesMESA;
+   drv->API.GetModeAttribMESA = _eglGetModeAttribMESA;
+   drv->API.GetScreensMESA = _eglGetScreensMESA;
+   drv->API.CreateScreenSurfaceMESA = _eglCreateScreenSurfaceMESA;
+   drv->API.ShowScreenSurfaceMESA = _eglShowScreenSurfaceMESA;
+   drv->API.ScreenPositionMESA = _eglScreenPositionMESA;
+   drv->API.QueryScreenMESA = _eglQueryScreenMESA;
+   drv->API.QueryScreenSurfaceMESA = _eglQueryScreenSurfaceMESA;
+   drv->API.QueryScreenModeMESA = _eglQueryScreenModeMESA;
+   drv->API.QueryModeStringMESA = _eglQueryModeStringMESA;
+#endif /* EGL_MESA_screen_surface */
+
+#ifdef EGL_VERSION_1_2
+   drv->API.CreatePbufferFromClientBuffer = _eglCreatePbufferFromClientBuffer;
+#endif /* EGL_VERSION_1_2 */
 }
 
 
+
 /**
- * Examine the individual extension enable/disable flags and recompute
- * the driver's Extensions string.
+ * Try to determine which EGL APIs (OpenGL, OpenGL ES, OpenVG, etc)
+ * are supported on the system by looking for standard library names.
  */
-static void
-UpdateExtensionsString(_EGLDriver *drv)
-{
-   drv->Extensions[0] = 0;
-
-   if (drv->MESA_screen_surface)
-      strcat(drv->Extensions, "EGL_MESA_screen_surface ");
-   if (drv->MESA_copy_context)
-      strcat(drv->Extensions, "EGL_MESA_copy_context ");
-   assert(strlen(drv->Extensions) < MAX_EXTENSIONS_LEN);
-}
-
-
-
-const char *
-_eglQueryString(_EGLDriver *drv, EGLDisplay dpy, EGLint name)
+EGLint
+_eglFindAPIs(void)
 {
-   (void) drv;
-   (void) dpy;
-   switch (name) {
-   case EGL_VENDOR:
-      return "Mesa Project";
-   case EGL_VERSION:
-      return "1.0";
-   case EGL_EXTENSIONS:
-      UpdateExtensionsString(drv);
-      return drv->Extensions;
-   default:
-      _eglError(EGL_BAD_PARAMETER, "eglQueryString");
-      return NULL;
+   EGLint mask = 0x0;
+   lib_handle lib;
+#if defined(_EGL_PLATFORM_WINDOWS)
+   /* XXX not sure about these names */
+   const char *es1_libname = "libGLESv1_CM.dll";
+   const char *es2_libname = "libGLESv2.dll";
+   const char *gl_libname = "OpenGL32.dll";
+   const char *vg_libname = "libOpenVG.dll";
+#elif defined(_EGL_PLATFORM_X)
+   const char *es1_libname = "libGLESv1_CM.so";
+   const char *es2_libname = "libGLESv2.so";
+   const char *gl_libname = "libGL.so";
+   const char *vg_libname = "libOpenVG.so";
+#endif
+
+   if ((lib = open_library(es1_libname))) {
+      close_library(lib);
+      mask |= EGL_OPENGL_ES_BIT;
    }
-}
 
+   if ((lib = open_library(es2_libname))) {
+      close_library(lib);
+      mask |= EGL_OPENGL_ES2_BIT;
+   }
 
-EGLBoolean
-_eglWaitGL(_EGLDriver *drv, EGLDisplay dpy)
-{
-   /* just a placeholder */
-   (void) drv;
-   (void) dpy;
-   return EGL_TRUE;
-}
+   if ((lib = open_library(gl_libname))) {
+      close_library(lib);
+      mask |= EGL_OPENGL_BIT;
+   }
 
+   if ((lib = open_library(vg_libname))) {
+      close_library(lib);
+      mask |= EGL_OPENVG_BIT;
+   }
 
-EGLBoolean
-_eglWaitNative(_EGLDriver *drv, EGLDisplay dpy, EGLint engine)
-{
-   /* just a placeholder */
-   (void) drv;
-   (void) dpy;
-   (void) engine;
-   return EGL_TRUE;
+   return mask;
 }