patch by blight:
[reactos.git] / reactos / lib / opengl32 / opengl32.c
index 33ed831..ac35879 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: opengl32.c,v 1.6 2004/02/02 17:59:23 navaraf Exp $
+/* $Id: opengl32.c,v 1.15 2004/02/12 23:56:15 royce Exp $
  *
  * COPYRIGHT:            See COPYING in the top level directory
  * PROJECT:              ReactOS kernel
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <winreg.h>
+#include "teb.h"
 
 #include <string.h>
-//#include <GL/gl.h>
 #include "opengl32.h"
 
-#define EXPORT __declspec(dllexport)
-#define NAKED __attribute__((naked))
+#define EXT_GET_DRIVERINFO             0x1101 /* ExtEscape code to get driver info */
+typedef struct tagEXTDRIVERINFO
+{
+       DWORD version;          /* driver interface version */
+       DWORD driver_version;   /* driver version */
+       WCHAR driver_name[256]; /* driver name */
+} EXTDRIVERINFO;
 
 /* function prototypes */
-static BOOL OPENGL32_LoadDrivers();
+/*static BOOL OPENGL32_LoadDrivers();*/
 static void OPENGL32_AppendICD( GLDRIVERDATA *icd );
 static void OPENGL32_RemoveICD( GLDRIVERDATA *icd );
-static GLDRIVERDATA *OPENGL32_LoadDriver ( LPCWSTR regKey );
+static GLDRIVERDATA *OPENGL32_LoadDriver( LPCWSTR regKey );
 static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd );
 static BOOL OPENGL32_UnloadDriver( GLDRIVERDATA *icd );
 
 /* global vars */
-const char* OPENGL32_funcnames[GLIDX_COUNT]
-            __attribute__((section("shared"), shared)) =
-{
-#define X(X) #X,
-       GLFUNCS_MACRO
-#undef X
-};
-
-GLPROCESSDATA OPENGL32_processdata;
 DWORD OPENGL32_tls;
+GLPROCESSDATA OPENGL32_processdata;
 
-static void OPENGL32_ThreadDetach()
-{
-        GLTHREADDATA *lpvData;
 
+static
+void
+OPENGL32_ThreadDetach()
+{
        /* FIXME - do we need to release some HDC or something? */
-       lpvData = (GLTHREADDATA *)TlsGetValue ( OPENGL32_tls );
-       if ( lpvData != NULL )
-               LocalFree((HLOCAL) lpvData );
+       GLTHREADDATA* lpData = NULL;
+       PROC *dispatchTable = NULL;
+
+       lpData = (GLTHREADDATA*)TlsGetValue( OPENGL32_tls );
+       if (lpData != NULL)
+       {
+               if (!HeapFree( GetProcessHeap(), 0, lpData ))
+                       DBGPRINT( "Warning: HeapFree() on GLTHREADDATA failed (%d)",
+                                 GetLastError() );
+       }
+
+
+       dispatchTable = NtCurrentTeb()->glTable;
+
+       if (dispatchTable != NULL)
+       {
+               if (!HeapFree( GetProcessHeap(), 0, dispatchTable ))
+                       DBGPRINT( "Warning: HeapFree() on dispatch table failed (%d)",
+                                 GetLastError() );
+       }
 }
 
 
-BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
+BOOL
+WINAPI
+DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 {
        GLTHREADDATA* lpData = NULL;
-       DBGPRINT( "DllMain called!\n" );
+       PROC *dispatchTable = NULL;
+       TEB *teb = NULL;
+       SECURITY_ATTRIBUTES attrib = { sizeof (SECURITY_ATTRIBUTES), /* nLength */
+                                      NULL, /* lpSecurityDescriptor */
+                                      TRUE /* bInheritHandle */ };
 
+       DBGPRINT( "Info: Called!" );
        switch ( Reason )
        {
        /* The DLL is loading due to process
@@ -68,23 +90,58 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 
                memset( &OPENGL32_processdata, 0, sizeof (OPENGL32_processdata) );
 
-               /* get list of ICDs from registry: */
-               OPENGL32_LoadDrivers();
+               /* create driver & glrc list mutex */
+               OPENGL32_processdata.driver_mutex = CreateMutex( &attrib, FALSE, NULL );
+               if (OPENGL32_processdata.driver_mutex == NULL)
+               {
+                       DBGPRINT( "Error: Couldn't create driver_list mutex (%d)",
+                                 GetLastError() );
+                       TlsFree( OPENGL32_tls );
+               }
+               OPENGL32_processdata.glrc_mutex = CreateMutex( &attrib, FALSE, NULL );
+               if (OPENGL32_processdata.glrc_mutex == NULL)
+               {
+                       DBGPRINT( "Error: Couldn't create glrc_list mutex (%d)",
+                                 GetLastError() );
+                       CloseHandle( OPENGL32_processdata.driver_mutex );
+                       TlsFree( OPENGL32_tls );
+               }
+
                /* No break: Initialize the index for first thread. */
 
        /* The attached process creates a new thread. */
        case DLL_THREAD_ATTACH:
-               lpData = (GLTHREADDATA*)LocalAlloc(LPTR, sizeof(GLTHREADDATA));
-               if ( lpData != NULL )
+               dispatchTable = (PROC*)HeapAlloc( GetProcessHeap(),
+                                           HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
+                                           sizeof (((ICDTable *)(0))->dispatch_table) );
+               if (dispatchTable == NULL)
                {
-                       memset ( lpData, 0, sizeof(GLTHREADDATA) );
-                       (void)TlsSetValue ( OPENGL32_tls, lpData );
+                       DBGPRINT( "Error: Couldn't allocate GL dispatch table" );
+                       return FALSE;
+               }
+
+               lpData = (GLTHREADDATA*)HeapAlloc( GetProcessHeap(),
+                                           HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
+                                           sizeof (GLTHREADDATA) );
+               if (lpData == NULL)
+               {
+                       DBGPRINT( "Error: Couldn't allocate GLTHREADDATA" );
+                       HeapFree( GetProcessHeap(), 0, dispatchTable );
+                       return FALSE;
                }
-#if 0
-               lpData->hdc = NULL;
-               /* FIXME - defaulting to mesa3d, but shouldn't */
-               lpData->list = OPENGL32_processdata.list[0];
-#endif
+
+               teb = NtCurrentTeb();
+
+               /* initialize dispatch table with empty functions */
+               #define X(func, ret, typeargs, args, icdidx, tebidx, stack)            \
+                       dispatchTable[icdidx] = (PROC)glEmptyFunc##stack;                  \
+                       if (tebidx >= 0)                                                   \
+                               teb->glDispatchTable[tebidx] = (PVOID)glEmptyFunc##stack;
+               GLFUNCS_MACRO
+               #undef X
+
+               teb->glTable = dispatchTable;
+               TlsSetValue( OPENGL32_tls, lpData );
                break;
 
        /* The thread of the attached process terminates. */
@@ -96,7 +153,10 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
        /* DLL unload due to process termination or FreeLibrary. */
        case DLL_PROCESS_DETACH:
                OPENGL32_ThreadDetach();
-               /* FIXME: free resources */
+
+               /* FIXME: free resources (driver list, glrc list) */
+               CloseHandle( OPENGL32_processdata.driver_mutex );
+               CloseHandle( OPENGL32_processdata.glrc_mutex );
                TlsFree(OPENGL32_tls);
                break;
        }
@@ -106,16 +166,18 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 
 /* FUNCTION: Append ICD to linked list.
  * ARGUMENTS: [IN] icd: GLDRIVERDATA to append to list
- * TODO: protect from race conditions
+ * NOTES: Only call this when you hold the driver_mutex
  */
-static void OPENGL32_AppendICD( GLDRIVERDATA *icd )
+static
+void
+OPENGL32_AppendICD( GLDRIVERDATA *icd )
 {
-       if (!OPENGL32_processdata.driver_list)
+       if (OPENGL32_processdata.driver_list == NULL)
                OPENGL32_processdata.driver_list = icd;
        else
        {
                GLDRIVERDATA *p = OPENGL32_processdata.driver_list;
-               while (p->next)
+               while (p->next != NULL)
                        p = p->next;
                p->next = icd;
        }
@@ -124,16 +186,18 @@ static void OPENGL32_AppendICD( GLDRIVERDATA *icd )
 
 /* FUNCTION: Remove ICD from linked list.
  * ARGUMENTS: [IN] icd: GLDRIVERDATA to remove from list
- * TODO: protect from race conditions
+ * NOTES: Only call this when you hold the driver_mutex
  */
-static void OPENGL32_RemoveICD( GLDRIVERDATA *icd )
+static
+void
+OPENGL32_RemoveICD( GLDRIVERDATA *icd )
 {
        if (icd == OPENGL32_processdata.driver_list)
                OPENGL32_processdata.driver_list = icd->next;
        else
        {
                GLDRIVERDATA *p = OPENGL32_processdata.driver_list;
-               while (p)
+               while (p != NULL)
                {
                        if (p->next == icd)
                        {
@@ -142,35 +206,10 @@ static void OPENGL32_RemoveICD( GLDRIVERDATA *icd )
                        }
                        p = p->next;
                }
-               DBGPRINT( "RemoveICD: ICD 0x%08x not found in list!\n" );
+               DBGPRINT( "Error: ICD 0x%08x not found in list!", icd );
        }
 }
 
-/* FIXME - I'm assuming we want to return TRUE if we find at least *one* ICD */
-static BOOL OPENGL32_LoadDrivers()
-{
-       const WCHAR* OpenGLDrivers = L"SOFTWARE\\Microsoft\\Windows NT\\"
-               "CurrentVersion\\OpenGLDrivers\\";
-       HKEY hkey;
-       WCHAR name[1024];
-       int i;
-
-       /* FIXME - detect if we've already done this from another process */
-       /* FIXME - special-case load of MESA3D as generic implementation ICD. */
-
-       if ( ERROR_SUCCESS != RegOpenKeyW( HKEY_LOCAL_MACHINE, OpenGLDrivers, &hkey ) )
-               return FALSE;
-       for ( i = 0; RegEnumKeyW(hkey,i,name,sizeof(name)/sizeof(name[0])) == ERROR_SUCCESS; i++ )
-       {
-               /* ignoring return value, because OPENGL32_LoadICD() is doing *all* the work... */
-               /* GLDRIVERDATA* gldd =*/ OPENGL32_LoadICD ( name );
-       }
-       RegCloseKey ( hkey );
-       if ( i > 0 )
-               return TRUE;
-       else
-               return FALSE;
-}
 
 /* FUNCTION:  Load an ICD.
  * ARGUMENTS: [IN] driver:  Name of display driver.
@@ -178,99 +217,54 @@ static BOOL OPENGL32_LoadDrivers()
  *
  * TODO: call SetLastError() where appropriate
  */
-static GLDRIVERDATA *OPENGL32_LoadDriver ( LPCWSTR driver )
+static
+GLDRIVERDATA*
+OPENGL32_LoadDriver( LPCWSTR driver )
 {
-       HKEY hKey;
-       WCHAR subKey[1024] = L"SOFTWARE\\Microsoft\\Windows NT\\"
-                             "CurrentVersion\\OpenGLDrivers\\";
        LONG ret;
-       DWORD type, size;
-
-       DWORD version, driverVersion, flags; /* registry values */
-       WCHAR dll[256];
        GLDRIVERDATA *icd;
 
-       DBGPRINT( "Loading driver %ws...\n", driver );
+       DBGPRINT( "Info: Loading driver %ws...", driver );
 
-       /* open registry key */
-       wcsncat( subKey, driver, 1024 );
-       ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey );
-       if (ret != ERROR_SUCCESS)
-       {
-               DBGPRINT( "Error: Couldn't open registry key '%ws'\n", subKey );
-               return 0;
-       }
-
-       /* query values */
-       size = sizeof (dll);
-       ret = RegQueryValueExW( hKey, L"Dll", 0, &type, (LPBYTE)dll, &size );
-       if (ret != ERROR_SUCCESS || type != REG_SZ)
-       {
-               DBGPRINT( "Error: Couldn't query Dll value or not a string\n" );
-               RegCloseKey( hKey );
-               return 0;
-       }
-
-       size = sizeof (DWORD);
-       ret = RegQueryValueExW( hKey, L"Version", 0, &type, (LPBYTE)&version, &size );
-       if (ret != ERROR_SUCCESS || type != REG_DWORD)
-       {
-               DBGPRINT( "Warning: Couldn't query Version value or not a DWORD\n" );
-               version = 0;
-       }
-
-       size = sizeof (DWORD);
-       ret = RegQueryValueExW( hKey, L"DriverVersion", 0, &type,
-                               (LPBYTE)&driverVersion, &size );
-       if (ret != ERROR_SUCCESS || type != REG_DWORD)
+       /* allocate driver data */
+       icd = (GLDRIVERDATA*)HeapAlloc( GetProcessHeap(),
+                                       HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
+                                       sizeof (GLDRIVERDATA) );
+       if (icd == NULL)
        {
-               DBGPRINT( "Warning: Couldn't query DriverVersion value or not a DWORD\n" );
-               driverVersion = 0;
+               DBGPRINT( "Error: Couldn't allocate GLDRIVERDATA! (%d)", GetLastError() );
+               return NULL;
        }
 
-       size = sizeof (DWORD);
-       ret = RegQueryValueExW( hKey, L"Flags", 0, &type, (LPBYTE)&flags, &size );
-       if (ret != ERROR_SUCCESS || type != REG_DWORD)
+       ret = OPENGL32_RegGetDriverInfo( driver, icd );
+       if (ret != ERROR_SUCCESS)
        {
-               DBGPRINT( "Warning: Couldn't query Flags value or not a DWORD\n" );
-               flags = 0;
+               DBGPRINT( "Error: Couldn't query driver information (%d)", ret );
+               if (!HeapFree( GetProcessHeap(), 0, icd ))
+                       DBGPRINT( "Error: HeapFree() returned false, error code = %d",
+                                 GetLastError() );
+               return NULL;
        }
 
-       /* close key */
-       RegCloseKey( hKey );
-
-       DBGPRINT( "Dll = %ws\n", dll );
-       DBGPRINT( "Version = 0x%08x\n", version );
-       DBGPRINT( "DriverVersion = 0x%08x\n", driverVersion );
-       DBGPRINT( "Flags = 0x%08x\n", flags );
-
-       /* allocate driver data */
-       icd = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof (GLDRIVERDATA) );
-       if (!icd)
-       {
-               DBGPRINT( "Error: Couldnt allocate GLDRIVERDATA!\n" );
-               return 0;
-       }
-       wcsncpy( icd->driver_name, driver, 256 );
-       wcsncpy( icd->dll, dll, 256 );
-       icd->version = version;
-       icd->driver_version = driverVersion;
-       icd->flags = flags;
+       DBGPRINT( "Info: Dll = %ws", icd->dll );
+       DBGPRINT( "Info: Version = 0x%08x", icd->version );
+       DBGPRINT( "Info: DriverVersion = 0x%08x", icd->driver_version );
+       DBGPRINT( "Info: Flags = 0x%08x", icd->flags );
 
-       /* load ICD */
+       /* load/initialize ICD */
        ret = OPENGL32_InitializeDriver( icd );
        if (ret != ERROR_SUCCESS)
        {
+               DBGPRINT( "Error: Couldnt initialize ICD!" );
                if (!HeapFree( GetProcessHeap(), 0, icd ))
-                       DBGPRINT( "Error: HeapFree() returned false, error code = %d\n",
+                       DBGPRINT( "Error: HeapFree() returned false, error code = %d",
                                  GetLastError() );
-               DBGPRINT( "Error: Couldnt initialize ICD!\n" );
-               return 0;
+               return NULL;
        }
 
        /* append ICD to list */
        OPENGL32_AppendICD( icd );
-       DBGPRINT( "ICD loaded.\n" );
+       DBGPRINT( "Info: ICD loaded." );
 
        return icd;
 }
@@ -282,28 +276,52 @@ static GLDRIVERDATA *OPENGL32_LoadDriver ( LPCWSTR driver )
  * RETURNS:   error code; ERROR_SUCCESS on success
  */
 #define LOAD_DRV_PROC( icd, proc, required ) \
-       icd->proc = GetProcAddress( icd->handle, #proc ); \
-       if (required && !icd->proc) { \
+       *(char**)&icd->proc = (char*)GetProcAddress( icd->handle, #proc ); \
+       if (required && icd->proc == NULL) { \
                DBGPRINT( "Error: GetProcAddress(\"%s\") failed!", #proc ); \
+               FreeLibrary( icd->handle ); \
                return GetLastError(); \
        }
 
-static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd )
+static
+DWORD
+OPENGL32_InitializeDriver( GLDRIVERDATA *icd )
 {
-       UINT i;
+       /* check version */
+       if (icd->version > 2)
+               DBGPRINT( "Warning: ICD version > 2 (%d)", icd->version );
 
        /* load dll */
        icd->handle = LoadLibraryW( icd->dll );
-       if (!icd->handle)
+       if (icd->handle == NULL)
+       {
+               DWORD err = GetLastError();
+               DBGPRINT( "Error: Couldn't load DLL! (%d)", err );
+               return err;
+       }
+
+       /* validate version */
+       if (icd->driver_version > 1)
        {
-               DBGPRINT( "Error: Couldn't load DLL! (%d)\n", GetLastError() );
-               return GetLastError();
+               LOAD_DRV_PROC(icd, DrvValidateVersion, FALSE);
+               if (icd->DrvValidateVersion != NULL)
+               {
+                       if (!icd->DrvValidateVersion( icd->driver_version ))
+                       {
+                               DBGPRINT( "Error: DrvValidateVersion failed!" );
+                               DBGBREAK();
+                               FreeLibrary( icd->handle );
+                               return ERROR_INVALID_FUNCTION; /* FIXME: use better error code */
+                       }
+               }
+               else
+                       DBGPRINT( "Info: DrvValidateVersion not exported by ICD" );
        }
 
        /* load DrvXXX procs */
        LOAD_DRV_PROC(icd, DrvCopyContext, TRUE);
-       LOAD_DRV_PROC(icd, DrvCreateContext, TRUE);
-       LOAD_DRV_PROC(icd, DrvCreateLayerContext, TRUE);
+       LOAD_DRV_PROC(icd, DrvCreateContext, FALSE);
+       LOAD_DRV_PROC(icd, DrvCreateLayerContext, FALSE);
        LOAD_DRV_PROC(icd, DrvDeleteContext, TRUE);
        LOAD_DRV_PROC(icd, DrvDescribeLayerPlane, TRUE);
        LOAD_DRV_PROC(icd, DrvDescribePixelFormat, TRUE);
@@ -317,18 +335,13 @@ static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd )
        LOAD_DRV_PROC(icd, DrvShareLists, TRUE);
        LOAD_DRV_PROC(icd, DrvSwapBuffers, TRUE);
        LOAD_DRV_PROC(icd, DrvSwapLayerBuffers, TRUE);
-       LOAD_DRV_PROC(icd, DrvValidateVersion, TRUE);
 
-       /* now load the glXXX functions */
-       for (i = 0; i < GLIDX_COUNT; i++)
+       /* we require at least one of DrvCreateContext and DrvCreateLayerContext */
+       if (icd->DrvCreateContext == NULL && icd->DrvCreateLayerContext == NULL)
        {
-               icd->func_list[i] = icd->DrvGetProcAddress( OPENGL32_funcnames[i] );
-#ifdef DEBUG_OPENGL32_ICD_EXPORTS
-               if ( icd->func_list[i] )
-               {
-                       DBGPRINT( "Found function %s in ICD.\n", OPENGL32_funcnames[i] );
-               }
-#endif
+               DBGPRINT( "Error: One of DrvCreateContext/DrvCreateLayerContext is required!" );
+               FreeLibrary( icd->handle );
+               return ERROR_INVALID_FUNCTION; /* FIXME: use better error code... */
        }
 
        return ERROR_SUCCESS;
@@ -338,50 +351,103 @@ static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd )
 /* FUNCTION: Unload loaded ICD.
  * RETURNS:  TRUE on success, FALSE otherwise.
  */
-static BOOL OPENGL32_UnloadDriver( GLDRIVERDATA *icd )
+static
+BOOL
+OPENGL32_UnloadDriver( GLDRIVERDATA *icd )
 {
        BOOL allOk = TRUE;
 
-       DBGPRINT( "Unloading driver %ws...\n", icd->driver_name );
-       if (icd->refcount)
-               DBGPRINT( "Warning: ICD refcount = %d (should be 0)\n", icd->refcount );
+       DBGPRINT( "Info: Unloading driver %ws...", icd->driver_name );
+       if (icd->refcount != 0)
+               DBGPRINT( "Warning: ICD refcount = %d (should be 0)", icd->refcount );
 
        /* unload dll */
        if (!FreeLibrary( icd->handle ))
        {
                allOk = FALSE;
-               DBGPRINT( "Warning: FreeLibrary on ICD %ws failed!\n", icd->dll );
+               DBGPRINT( "Warning: FreeLibrary on ICD %ws failed! (%d)", icd->dll,
+                         GetLastError() );
        }
 
        /* free resources */
        OPENGL32_RemoveICD( icd );
-       HeapFree( GetProcessHeap(), 0, icd );
+       if (!HeapFree( GetProcessHeap(), 0, icd ))
+       {
+               allOk = FALSE;
+               DBGPRINT( "Warning: HeapFree() returned FALSE, error code = %d",
+                         GetLastError() );
+       }
 
        return allOk;
 }
 
 
+/* FUNCTION: Load ICD from HDC (shared ICD data)
+ * RETURNS:  GLDRIVERDATA pointer on success, NULL otherwise.
+ * NOTES: Make sure the handle you pass in is one for a DC!
+ *        Increases the refcount of the ICD - use
+ *        OPENGL32_UnloadICD to release the ICD.
+ */
+GLDRIVERDATA *OPENGL32_LoadICDForHDC( HDC hdc )
+{
+       DWORD dwInput = 0;
+       LONG ret;
+       EXTDRIVERINFO info;
+
+       /* get driver name */
+       ret = ExtEscape( hdc, EXT_GET_DRIVERINFO, sizeof (dwInput), (LPCSTR)&dwInput,
+                        sizeof (EXTDRIVERINFO), (LPSTR)&info );
+       if (ret < 0)
+       {
+               DBGPRINT( "Warning: ExtEscape to get the drivername failed!!! (%d)", GetLastError() );
+               return 0;
+       }
+
+       /* load driver (or get a reference) */
+       return OPENGL32_LoadICD( info.driver_name );
+}
+
+
 /* FUNCTION: Load ICD (shared ICD data)
  * RETURNS:  GLDRIVERDATA pointer on success, NULL otherwise.
  */
-GLDRIVERDATA *OPENGL32_LoadICD ( LPCWSTR driver )
+GLDRIVERDATA*
+OPENGL32_LoadICD ( LPCWSTR driver )
 {
        GLDRIVERDATA *icd;
 
+       /* synchronize */
+       if (WaitForSingleObject( OPENGL32_processdata.driver_mutex, INFINITE ) ==
+           WAIT_FAILED)
+       {
+               DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
+               return NULL; /* FIXME: do we have to expect such an error and handle it? */
+       }
+
        /* look if ICD is already loaded */
        for (icd = OPENGL32_processdata.driver_list; icd; icd = icd->next)
        {
                if (!_wcsicmp( driver, icd->driver_name )) /* found */
                {
                        icd->refcount++;
+
+                       /* release mutex */
+                       if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
+                               DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
+
                        return icd;
                }
        }
 
        /* not found - try to load */
-       icd = OPENGL32_LoadDriver ( driver );
-       if (icd)
+       icd = OPENGL32_LoadDriver( driver );
+       if (icd != NULL)
                icd->refcount = 1;
+
+       /* release mutex */
+       if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
+               DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
+
        return icd;
 }
 
@@ -389,12 +455,154 @@ GLDRIVERDATA *OPENGL32_LoadICD ( LPCWSTR driver )
 /* FUNCTION: Unload ICD (shared ICD data)
  * RETURNS:  TRUE on success, FALSE otherwise.
  */
-BOOL OPENGL32_UnloadICD( GLDRIVERDATA *icd )
+BOOL
+OPENGL32_UnloadICD( GLDRIVERDATA *icd )
 {
+       BOOL ret = TRUE;
+
+       /* synchronize */
+       if (WaitForSingleObject( OPENGL32_processdata.driver_mutex, INFINITE ) ==
+           WAIT_FAILED)
+       {
+               DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
+               return FALSE; /* FIXME: do we have to expect such an error and handle it? */
+       }
+
        icd->refcount--;
-       if (icd->refcount == 0)
-               return OPENGL32_UnloadDriver( icd );
+       //if (icd->refcount == 0)
+       if (0)
+               ret = OPENGL32_UnloadDriver( icd );
+       /* FIXME: InitializeICD crashes when called a second time */
 
-       return TRUE;
+       /* release mutex */
+       if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
+               DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
+
+       return ret;
+}
+
+
+/* FUNCTION: Enumerate OpenGLDrivers (from registry)
+ * ARGUMENTS: [IN]  idx   Index of the driver to get information about
+ *            [OUT] name  Pointer to an array of WCHARs (can be NULL)
+ *            [I,O] cName Pointer to a DWORD. Input is len of name array;
+ *                        Output is length of the drivername.
+ *                        Can be NULL if name is NULL.
+ * RETURNS: Error code (ERROR_NO_MORE_ITEMS at end of list); On failure all
+ *          OUT vars are left untouched.
+ */
+DWORD
+OPENGL32_RegEnumDrivers( DWORD idx, LPWSTR name, LPDWORD cName )
+{
+       HKEY hKey;
+       LPCWSTR subKey =
+               L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers\\";
+       LONG ret;
+       DWORD size;
+       WCHAR driver[256];
+
+       if (name == NULL)
+               return ERROR_SUCCESS; /* nothing to do */
+
+       if (cName == NULL)
+               return ERROR_INVALID_FUNCTION; /* we need cName when name is given */
+
+       /* open OpenGLDrivers registry key */
+       ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey );
+       if (ret != ERROR_SUCCESS)
+       {
+               DBGPRINT( "Error: Couldn't open registry key '%ws'", subKey );
+               return ret;
+       }
+
+       /* get subkey name */
+       size = sizeof (driver) / sizeof (driver[0]);
+       ret = RegEnumKeyW( hKey, idx, name, *cName );
+       if (ret != ERROR_SUCCESS)
+       {
+               DBGPRINT( "Error: Couldn't get OpenGLDrivers subkey name (%d)", ret );
+               RegCloseKey( hKey );
+               return ret;
+       }
+       *cName = wcslen( name );
+
+       /* close key */
+       RegCloseKey( hKey );
+       return ERROR_SUCCESS;
+}
+
+
+/* FUNCTION: Get registry values for a driver given a name
+ * ARGUMENTS: [IN]  idx   Index of the driver to get information about
+ *            [OUT] icd   Pointer to GLDRIVERDATA. On success the following
+ *                        fields are filled: driver_name, dll, version,
+ *                        driver_version and flags.
+ * RETURNS: Error code; On failure all OUT vars are left untouched.
+ */
+/*static*/
+DWORD
+OPENGL32_RegGetDriverInfo( LPCWSTR driver, GLDRIVERDATA *icd )
+{
+       HKEY hKey;
+       WCHAR subKey[1024] =
+               L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers\\";
+       LONG ret;
+       DWORD type, size;
+
+       /* drivers registry values */
+       DWORD version = 1, driverVersion = 0, flags = 0;
+       WCHAR dll[256];
+
+       /* open driver registry key */
+       wcsncat( subKey, driver, 1024 );
+       ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey );
+       if (ret != ERROR_SUCCESS)
+       {
+               DBGPRINT( "Error: Couldn't open registry key '%ws'", subKey );
+               return ret;
+       }
+
+       /* query values */
+       size = sizeof (dll);
+       ret = RegQueryValueExW( hKey, L"Dll", 0, &type, (LPBYTE)dll, &size );
+       if (ret != ERROR_SUCCESS || type != REG_SZ)
+       {
+               DBGPRINT( "Error: Couldn't query Dll value or not a string" );
+               RegCloseKey( hKey );
+               return ret;
+       }
+
+       size = sizeof (DWORD);
+       ret = RegQueryValueExW( hKey, L"Version", 0, &type, (LPBYTE)&version, &size );
+       if (ret != ERROR_SUCCESS || type != REG_DWORD)
+               DBGPRINT( "Warning: Couldn't query Version value or not a DWORD" );
+
+       size = sizeof (DWORD);
+       ret = RegQueryValueExW( hKey, L"DriverVersion", 0, &type,
+                                                       (LPBYTE)&driverVersion, &size );
+       if (ret != ERROR_SUCCESS || type != REG_DWORD)
+               DBGPRINT( "Warning: Couldn't query DriverVersion value or not a DWORD" );
+
+       size = sizeof (DWORD);
+       ret = RegQueryValueExW( hKey, L"Flags", 0, &type, (LPBYTE)&flags, &size );
+       if (ret != ERROR_SUCCESS || type != REG_DWORD)
+               DBGPRINT( "Warning: Couldn't query Flags value or not a DWORD" );
+
+       /* close key */
+       RegCloseKey( hKey );
+
+       /* output data */
+       /* FIXME: NUL-terminate strings? */
+       wcsncpy( icd->driver_name, driver,
+                sizeof (icd->driver_name) / sizeof (icd->driver_name[0]) - 1 );
+       wcsncpy( icd->dll, dll,
+                sizeof (icd->dll) / sizeof (icd->dll[0]) );
+       icd->version = version;
+       icd->driver_version = driverVersion;
+       icd->flags = flags;
+
+       return ERROR_SUCCESS;
 }
 
+/* EOF */
+