/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: lib/opengl32/opengl32.c * PURPOSE: OpenGL32 lib * PROGRAMMER: Anich Gregor (blight), Royce Mitchell III * UPDATE HISTORY: * Feb 1, 2004: Created */ #include "opengl32.h" /* function prototypes */ static void OPENGL32_AppendICD( GLDRIVERDATA *icd ); static void OPENGL32_RemoveICD( GLDRIVERDATA *icd ); static GLDRIVERDATA *OPENGL32_LoadDriver( LPCWSTR regKey ); static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd ); static BOOL OPENGL32_UnloadDriver( GLDRIVERDATA *icd ); static DWORD OPENGL32_RegGetDriverInfo( LPCWSTR driver, GLDRIVERDATA *icd ); /* global vars */ /* Do not assume it have the free value MAXDWORD set, any value can be in here */ DWORD OPENGL32_tls = MAXDWORD; GLPROCESSDATA OPENGL32_processdata; static BOOL OPENGL32_ThreadAttach( void ) { GLTHREADDATA* lpData = NULL; PROC *dispatchTable = NULL; TEB *teb = NULL; dispatchTable = (PROC*)HeapAlloc( GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, sizeof (((ICDTable *)(0))->dispatch_table) ); if (dispatchTable == NULL) { 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; } 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 ); return TRUE; } static void OPENGL32_ThreadDetach( void ) { GLTHREADDATA* lpData = NULL; PROC *dispatchTable = NULL; rosglMakeCurrent( NULL, NULL ); lpData = (GLTHREADDATA*)TlsGetValue( OPENGL32_tls ); if (lpData != NULL) { if (!HeapFree( GetProcessHeap(), 0, lpData )) DBGPRINT( "Warning: HeapFree() on GLTHREADDATA failed (%d)", GetLastError() ); lpData = NULL; } dispatchTable = NtCurrentTeb()->glTable; if (dispatchTable != NULL) { if (!HeapFree( GetProcessHeap(), 0, dispatchTable )) DBGPRINT( "Warning: HeapFree() on dispatch table failed (%d)", GetLastError() ); } } static BOOL OPENGL32_ProcessAttach( void ) { SECURITY_ATTRIBUTES attrib = { sizeof (SECURITY_ATTRIBUTES), /* nLength */ NULL, /* lpSecurityDescriptor */ TRUE /* bInheritHandle */ }; OPENGL32_tls = TlsAlloc(); if (OPENGL32_tls == MAXDWORD) return FALSE; memset( &OPENGL32_processdata, 0, sizeof (OPENGL32_processdata) ); /* create driver, glrc & dcdata 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() ); return FALSE; } OPENGL32_processdata.glrc_mutex = CreateMutex( &attrib, FALSE, NULL ); if (OPENGL32_processdata.glrc_mutex == NULL) { DBGPRINT( "Error: Couldn't create glrc_list mutex (%d)", GetLastError() ); return FALSE; } OPENGL32_processdata.dcdata_mutex = CreateMutex( &attrib, FALSE, NULL ); if (OPENGL32_processdata.dcdata_mutex == NULL) { DBGPRINT( "Error: Couldn't create dcdata_list mutex (%d)", GetLastError() ); return FALSE; } return TRUE; } static void OPENGL32_ProcessDetach( void ) { GLDRIVERDATA *icd, *icd2; GLDCDATA *dcdata, *dcdata2; GLRC *glrc, *glrc2; /* free lists */ for (dcdata = OPENGL32_processdata.dcdata_list; dcdata != NULL;) { dcdata2 = dcdata; dcdata = dcdata->next; if (!HeapFree( GetProcessHeap(), 0, dcdata2 )) DBGPRINT( "Warning: HeapFree() on DCDATA 0x%08x failed (%d)", dcdata2, GetLastError() ); } for (glrc = OPENGL32_processdata.glrc_list; glrc != NULL;) { glrc2 = glrc; glrc = glrc->next; if (!HeapFree( GetProcessHeap(), 0, glrc2 )) DBGPRINT( "Warning: HeapFree() on GLRC 0x%08x failed (%d)", glrc2, GetLastError() ); } for (icd = OPENGL32_processdata.driver_list; icd != NULL;) { icd2 = icd; icd = icd->next; if (!HeapFree( GetProcessHeap(), 0, icd2 )) DBGPRINT( "Warning: HeapFree() on DRIVERDATA 0x%08x failed (%d)", icd2, GetLastError() ); } /* free mutexes */ if (OPENGL32_processdata.driver_mutex != NULL) CloseHandle( OPENGL32_processdata.driver_mutex ); if (OPENGL32_processdata.glrc_mutex != NULL) CloseHandle( OPENGL32_processdata.glrc_mutex ); if (OPENGL32_processdata.dcdata_mutex != NULL) CloseHandle( OPENGL32_processdata.dcdata_mutex ); /* free TLS */ if (OPENGL32_tls != MAXDWORD) TlsFree(OPENGL32_tls); } BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved) { DBGPRINT( "Info: Called!" ); switch ( Reason ) { /* The DLL is loading due to process * initialization or a call to LoadLibrary. */ case DLL_PROCESS_ATTACH: DBGTRACE( "Process attach" ); if (!OPENGL32_ProcessAttach()) return FALSE; /* No break: Initialize the index for first thread. */ /* The attached process creates a new thread. */ case DLL_THREAD_ATTACH: DBGTRACE( "Thread attach" ); if (!OPENGL32_ThreadAttach()) return FALSE; break; /* The thread of the attached process terminates. */ case DLL_THREAD_DETACH: DBGTRACE( "Thread detach" ); /* Release the allocated memory for this thread.*/ OPENGL32_ThreadDetach(); break; /* DLL unload due to process termination or FreeLibrary. */ case DLL_PROCESS_DETACH: DBGTRACE( "Process detach" ); OPENGL32_ThreadDetach(); OPENGL32_ProcessDetach(); break; } return TRUE; } /*! \brief Append ICD to linked list. * * \param icd GLDRIVERDATA to append to list * * \note Only call this when you hold the driver_mutex. */ static void OPENGL32_AppendICD( GLDRIVERDATA *icd ) { if (OPENGL32_processdata.driver_list == NULL) OPENGL32_processdata.driver_list = icd; else { GLDRIVERDATA *p = OPENGL32_processdata.driver_list; while (p->next != NULL) p = p->next; p->next = icd; } } /*! \brief Remove ICD from linked list. * * \param icd GLDRIVERDATA to remove from list * * \note Only call this when you hold the driver_mutex. */ 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 != NULL) { if (p->next == icd) { p->next = icd->next; return; } p = p->next; } DBGPRINT( "Error: ICD 0x%08x not found in list!", icd ); } } /*! \brief Load an ICD. * * \param driver Name of installable client driver. * * \return Pointer to an allocated GLDRIVERDATA struct. * \retval NULL Failure. * * \todo Call SetLastError() where appropriate. */ static GLDRIVERDATA * OPENGL32_LoadDriver( LPCWSTR driver ) { LONG ret; GLDRIVERDATA *icd; DBGPRINT( "Info: Loading driver %ws...", driver ); /* allocate driver data */ icd = (GLDRIVERDATA*)HeapAlloc( GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, sizeof (GLDRIVERDATA) ); if (icd == NULL) { DBGPRINT( "Error: Couldn't allocate GLDRIVERDATA! (%d)", GetLastError() ); return NULL; } ret = OPENGL32_RegGetDriverInfo( driver, icd ); if (ret != ERROR_SUCCESS) { 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; } 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/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", GetLastError() ); return NULL; } /* append ICD to list */ OPENGL32_AppendICD( icd ); DBGPRINT( "Info: ICD loaded." ); return icd; } /*! \brief Initialize a driver (Load DLL and DrvXxx procs) * * \param icd ICD to initialize with the dll, version, driverVersion * and flags already filled. * \return Error code. * \retval ERROR_SUCCESS Success */ #define LOAD_DRV_PROC( icd, proc, required ) \ *(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 ) { /* check version */ if (icd->version > 2) DBGPRINT( "Warning: ICD version > 2 (%d)", icd->version ); /* load dll */ icd->handle = LoadLibraryW( icd->dll ); if (icd->handle == NULL) { DWORD err = GetLastError(); DBGPRINT( "Error: Couldn't load DLL! (%d)", err ); return err; } /* validate version */ if (icd->driver_version > 1) { 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, 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); LOAD_DRV_PROC(icd, DrvGetLayerPaletteEntries, TRUE); LOAD_DRV_PROC(icd, DrvGetProcAddress, TRUE); LOAD_DRV_PROC(icd, DrvReleaseContext, TRUE); LOAD_DRV_PROC(icd, DrvRealizeLayerPalette, TRUE); LOAD_DRV_PROC(icd, DrvSetContext, TRUE); LOAD_DRV_PROC(icd, DrvSetLayerPaletteEntries, TRUE); LOAD_DRV_PROC(icd, DrvSetPixelFormat, TRUE); LOAD_DRV_PROC(icd, DrvShareLists, TRUE); LOAD_DRV_PROC(icd, DrvSwapBuffers, TRUE); LOAD_DRV_PROC(icd, DrvSwapLayerBuffers, TRUE); /* we require at least one of DrvCreateContext and DrvCreateLayerContext */ if (icd->DrvCreateContext == NULL && icd->DrvCreateLayerContext == NULL) { DBGPRINT( "Error: One of DrvCreateContext/DrvCreateLayerContext is required!" ); FreeLibrary( icd->handle ); return ERROR_INVALID_FUNCTION; /* FIXME: use better error code... */ } return ERROR_SUCCESS; } /*! \brief Unload ICD. * * \retval TRUE Success. * \retval FALSE Failure. */ static BOOL OPENGL32_UnloadDriver( GLDRIVERDATA *icd ) { BOOL allOk = TRUE; 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! (%d)", icd->dll, GetLastError() ); } /* free resources */ OPENGL32_RemoveICD( icd ); if (!HeapFree( GetProcessHeap(), 0, icd )) { allOk = FALSE; DBGPRINT( "Warning: HeapFree() returned FALSE, error code = %d", GetLastError() ); } return allOk; } /*! \brief Load ICD (shared ICD data) * * \return Pointer to an allocated GLDRIVERDATA on success. * \retval NULL Failure. */ 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 */ { /* 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 ); /* release mutex */ if (!ReleaseMutex( OPENGL32_processdata.driver_mutex )) DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() ); return icd; } /*! \brief Unload ICD (shared ICD data) * * \retval TRUE Success. * \retval FALSE Failure. */ 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? */ } if (icd->refcount == 0) ret = OPENGL32_UnloadDriver( icd ); /* release mutex */ if (!ReleaseMutex( OPENGL32_processdata.driver_mutex )) DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() ); return ret; } /*! \brief Enumerate OpenGLDrivers (from registry) * * \param idx Index of the driver to get information about. * \param name Pointer to an array of WCHARs (can be NULL) * \param cName Pointer to a DWORD. Input is len of name array. * Output is length of the drivername. * Can be NULL if name is NULL. * * \return Error code * \retval ERROR_NO_MORE_ITEMS End of driver list. * \retval ERROR_SUCCESS Success. */ #if 0 /* unused */ DWORD OPENGL32_RegEnumDrivers( DWORD idx, LPWSTR name, LPDWORD cName ) { HKEY hKey; LPCWSTR subKey = OPENGL_DRIVERS_SUBKEY; 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; } #endif /* 0 -- unused */ /*! \brief Get registry values for a driver given a name. * * \param driver Name of the driver to get information about. * \param icd Pointer to GLDRIVERDATA. * * \return Error code. * \retval ERROR_SUCCESS Success. * * \note On success the following fields of \a icd are filled: \a driver_name, * \a dll, \a version, \a driver_version and \a flags. */ static DWORD OPENGL32_RegGetDriverInfo( LPCWSTR driver, GLDRIVERDATA *icd ) { HKEY hKey; WCHAR subKey[1024] = OPENGL_DRIVERS_SUBKEY2; 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 */