8ae6c95f72ef74e60e1a683eb7f26013b5aaaaa4
[reactos.git] / reactos / lib / opengl32 / opengl32.c
1 /* $Id: opengl32.c,v 1.14 2004/02/09 08:00:15 vizzini Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: lib/opengl32/opengl32.c
6 * PURPOSE: OpenGL32 lib
7 * PROGRAMMER: Anich Gregor (blight), Royce Mitchell III
8 * UPDATE HISTORY:
9 * Feb 1, 2004: Created
10 */
11
12 #define WIN32_LEAN_AND_MEAN
13 #include <windows.h>
14 #include <winreg.h>
15 #include "teb.h"
16
17 #include <string.h>
18 #include "opengl32.h"
19
20 #define EXT_GET_DRIVERINFO 0x1101 /* ExtEscape code to get driver info */
21 typedef struct tagEXTDRIVERINFO
22 {
23 DWORD version; /* driver interface version */
24 DWORD driver_version; /* driver version */
25 WCHAR driver_name[256]; /* driver name */
26 } EXTDRIVERINFO;
27
28 /* function prototypes */
29 /*static BOOL OPENGL32_LoadDrivers();*/
30 static void OPENGL32_AppendICD( GLDRIVERDATA *icd );
31 static void OPENGL32_RemoveICD( GLDRIVERDATA *icd );
32 static GLDRIVERDATA *OPENGL32_LoadDriver( LPCWSTR regKey );
33 static DWORD OPENGL32_InitializeDriver( GLDRIVERDATA *icd );
34 static BOOL OPENGL32_UnloadDriver( GLDRIVERDATA *icd );
35
36 /* global vars */
37 /*const char* OPENGL32_funcnames[GLIDX_COUNT] SHARED =
38 {
39 #define X(func, ret, typeargs, args) #func,
40 GLFUNCS_MACRO
41 #undef X
42 };*/
43
44 DWORD OPENGL32_tls;
45 GLPROCESSDATA OPENGL32_processdata;
46
47
48 static
49 void
50 OPENGL32_ThreadDetach()
51 {
52 /* FIXME - do we need to release some HDC or something? */
53 GLTHREADDATA* lpData = NULL;
54 lpData = (GLTHREADDATA*)TlsGetValue( OPENGL32_tls );
55 if (lpData != NULL)
56 {
57 if (!HeapFree( GetProcessHeap(), 0, lpData ))
58 DBGPRINT( "Warning: HeapFree() on GLTHREADDATA failed (%d)",
59 GetLastError() );
60 }
61 }
62
63
64 BOOL
65 WINAPI
66 DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
67 {
68 GLTHREADDATA* lpData = NULL;
69 ICDTable *dispatchTable = NULL;
70 TEB *teb = NULL;
71 SECURITY_ATTRIBUTES attrib = { sizeof (SECURITY_ATTRIBUTES), /* nLength */
72 NULL, /* lpSecurityDescriptor */
73 TRUE /* bInheritHandle */ };
74
75 DBGPRINT( "Info: Called!" );
76 switch ( Reason )
77 {
78 /* The DLL is loading due to process
79 * initialization or a call to LoadLibrary.
80 */
81 case DLL_PROCESS_ATTACH:
82 OPENGL32_tls = TlsAlloc();
83 if ( 0xFFFFFFFF == OPENGL32_tls )
84 return FALSE;
85
86 memset( &OPENGL32_processdata, 0, sizeof (OPENGL32_processdata) );
87
88 /* create driver & glrc list mutex */
89 OPENGL32_processdata.driver_mutex = CreateMutex( &attrib, FALSE, NULL );
90 if (OPENGL32_processdata.driver_mutex == NULL)
91 {
92 DBGPRINT( "Error: Couldn't create driver_list mutex (%d)",
93 GetLastError() );
94 TlsFree( OPENGL32_tls );
95 }
96 OPENGL32_processdata.glrc_mutex = CreateMutex( &attrib, FALSE, NULL );
97 if (OPENGL32_processdata.glrc_mutex == NULL)
98 {
99 DBGPRINT( "Error: Couldn't create glrc_list mutex (%d)",
100 GetLastError() );
101 CloseHandle( OPENGL32_processdata.driver_mutex );
102 TlsFree( OPENGL32_tls );
103 }
104
105 /* No break: Initialize the index for first thread. */
106
107 /* The attached process creates a new thread. */
108 case DLL_THREAD_ATTACH:
109 dispatchTable = (ICDTable*)HeapAlloc( GetProcessHeap(),
110 HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
111 sizeof (ICDTable) );
112 if (dispatchTable == NULL)
113 {
114 DBGPRINT( "Error: Couldn't allocate GL dispatch table" );
115 return FALSE;
116 }
117
118 lpData = (GLTHREADDATA*)HeapAlloc( GetProcessHeap(),
119 HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
120 sizeof (GLTHREADDATA) );
121 if (lpData == NULL)
122 {
123 DBGPRINT( "Error: Couldn't allocate GLTHREADDATA" );
124 HeapFree( GetProcessHeap(), 0, dispatchTable );
125 return FALSE;
126 }
127
128 teb = NtCurrentTeb();
129
130 /* initialize dispatch table with empty functions */
131 #define X(func, ret, typeargs, args, icdidx, tebidx, stack) \
132 dispatchTable->dispatch_table[icdidx] = (PROC)glEmptyFunc##stack; \
133 if (tebidx >= 0) \
134 teb->glDispatchTable[tebidx] = (PVOID)glEmptyFunc##stack;
135 GLFUNCS_MACRO
136 #undef X
137
138 teb->glTable = dispatchTable->dispatch_table;
139 TlsSetValue( OPENGL32_tls, lpData );
140 break;
141
142 /* The thread of the attached process terminates. */
143 case DLL_THREAD_DETACH:
144 /* Release the allocated memory for this thread.*/
145 OPENGL32_ThreadDetach();
146 break;
147
148 /* DLL unload due to process termination or FreeLibrary. */
149 case DLL_PROCESS_DETACH:
150 OPENGL32_ThreadDetach();
151
152 /* FIXME: free resources (driver list, glrc list) */
153 CloseHandle( OPENGL32_processdata.driver_mutex );
154 CloseHandle( OPENGL32_processdata.glrc_mutex );
155 TlsFree(OPENGL32_tls);
156 break;
157 }
158 return TRUE;
159 }
160
161
162 /* FUNCTION: Append ICD to linked list.
163 * ARGUMENTS: [IN] icd: GLDRIVERDATA to append to list
164 * NOTES: Only call this when you hold the driver_mutex
165 */
166 static
167 void
168 OPENGL32_AppendICD( GLDRIVERDATA *icd )
169 {
170 if (OPENGL32_processdata.driver_list == NULL)
171 OPENGL32_processdata.driver_list = icd;
172 else
173 {
174 GLDRIVERDATA *p = OPENGL32_processdata.driver_list;
175 while (p->next != NULL)
176 p = p->next;
177 p->next = icd;
178 }
179 }
180
181
182 /* FUNCTION: Remove ICD from linked list.
183 * ARGUMENTS: [IN] icd: GLDRIVERDATA to remove from list
184 * NOTES: Only call this when you hold the driver_mutex
185 */
186 static
187 void
188 OPENGL32_RemoveICD( GLDRIVERDATA *icd )
189 {
190 if (icd == OPENGL32_processdata.driver_list)
191 OPENGL32_processdata.driver_list = icd->next;
192 else
193 {
194 GLDRIVERDATA *p = OPENGL32_processdata.driver_list;
195 while (p != NULL)
196 {
197 if (p->next == icd)
198 {
199 p->next = icd->next;
200 return;
201 }
202 p = p->next;
203 }
204 DBGPRINT( "Error: ICD 0x%08x not found in list!", icd );
205 }
206 }
207
208
209 /* FUNCTION: Load an ICD.
210 * ARGUMENTS: [IN] driver: Name of display driver.
211 * RETURNS: error code; ERROR_SUCCESS on success
212 *
213 * TODO: call SetLastError() where appropriate
214 */
215 static
216 GLDRIVERDATA*
217 OPENGL32_LoadDriver( LPCWSTR driver )
218 {
219 LONG ret;
220 GLDRIVERDATA *icd;
221
222 DBGPRINT( "Info: Loading driver %ws...", driver );
223
224 /* allocate driver data */
225 icd = (GLDRIVERDATA*)HeapAlloc( GetProcessHeap(),
226 HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
227 sizeof (GLDRIVERDATA) );
228 if (icd == NULL)
229 {
230 DBGPRINT( "Error: Couldn't allocate GLDRIVERDATA! (%d)", GetLastError() );
231 return NULL;
232 }
233
234 ret = OPENGL32_RegGetDriverInfo( driver, icd );
235 if (ret != ERROR_SUCCESS)
236 {
237 DBGPRINT( "Error: Couldn't query driver information (%d)", ret );
238 if (!HeapFree( GetProcessHeap(), 0, icd ))
239 DBGPRINT( "Error: HeapFree() returned false, error code = %d",
240 GetLastError() );
241 return NULL;
242 }
243
244 DBGPRINT( "Info: Dll = %ws", icd->dll );
245 DBGPRINT( "Info: Version = 0x%08x", icd->version );
246 DBGPRINT( "Info: DriverVersion = 0x%08x", icd->driver_version );
247 DBGPRINT( "Info: Flags = 0x%08x", icd->flags );
248
249 /* load/initialize ICD */
250 ret = OPENGL32_InitializeDriver( icd );
251 if (ret != ERROR_SUCCESS)
252 {
253 DBGPRINT( "Error: Couldnt initialize ICD!" );
254 if (!HeapFree( GetProcessHeap(), 0, icd ))
255 DBGPRINT( "Error: HeapFree() returned false, error code = %d",
256 GetLastError() );
257 return NULL;
258 }
259
260 /* append ICD to list */
261 OPENGL32_AppendICD( icd );
262 DBGPRINT( "Info: ICD loaded." );
263
264 return icd;
265 }
266
267
268 /* FUNCTION: Initialize a driver (Load DLL, DrvXXX and glXXX procs)
269 * ARGUMENTS: [IN] icd: ICD to initialize with the dll, version, driverVersion
270 * and flags already filled.
271 * RETURNS: error code; ERROR_SUCCESS on success
272 */
273 #define LOAD_DRV_PROC( icd, proc, required ) \
274 *(char**)&icd->proc = (char*)GetProcAddress( icd->handle, #proc ); \
275 if (required && icd->proc == NULL) { \
276 DBGPRINT( "Error: GetProcAddress(\"%s\") failed!", #proc ); \
277 FreeLibrary( icd->handle ); \
278 return GetLastError(); \
279 }
280
281 static
282 DWORD
283 OPENGL32_InitializeDriver( GLDRIVERDATA *icd )
284 {
285 /* check version */
286 if (icd->version > 2)
287 DBGPRINT( "Warning: ICD version > 2 (%d)", icd->version );
288
289 /* load dll */
290 icd->handle = LoadLibraryW( icd->dll );
291 if (icd->handle == NULL)
292 {
293 DWORD err = GetLastError();
294 DBGPRINT( "Error: Couldn't load DLL! (%d)", err );
295 return err;
296 }
297
298 /* validate version */
299 if (icd->driver_version > 1)
300 {
301 LOAD_DRV_PROC(icd, DrvValidateVersion, FALSE);
302 if (icd->DrvValidateVersion != NULL)
303 {
304 if (!icd->DrvValidateVersion( icd->driver_version ))
305 {
306 DBGPRINT( "Error: DrvValidateVersion failed!" );
307 DBGBREAK();
308 FreeLibrary( icd->handle );
309 return ERROR_INVALID_FUNCTION; /* FIXME: use better error code */
310 }
311 }
312 else
313 DBGPRINT( "Info: DrvValidateVersion not exported by ICD" );
314 }
315
316 /* load DrvXXX procs */
317 LOAD_DRV_PROC(icd, DrvCopyContext, TRUE);
318 LOAD_DRV_PROC(icd, DrvCreateContext, FALSE);
319 LOAD_DRV_PROC(icd, DrvCreateLayerContext, FALSE);
320 LOAD_DRV_PROC(icd, DrvDeleteContext, TRUE);
321 LOAD_DRV_PROC(icd, DrvDescribeLayerPlane, TRUE);
322 LOAD_DRV_PROC(icd, DrvDescribePixelFormat, TRUE);
323 LOAD_DRV_PROC(icd, DrvGetLayerPaletteEntries, TRUE);
324 LOAD_DRV_PROC(icd, DrvGetProcAddress, TRUE);
325 LOAD_DRV_PROC(icd, DrvReleaseContext, TRUE);
326 LOAD_DRV_PROC(icd, DrvRealizeLayerPalette, TRUE);
327 LOAD_DRV_PROC(icd, DrvSetContext, TRUE);
328 LOAD_DRV_PROC(icd, DrvSetLayerPaletteEntries, TRUE);
329 LOAD_DRV_PROC(icd, DrvSetPixelFormat, TRUE);
330 LOAD_DRV_PROC(icd, DrvShareLists, TRUE);
331 LOAD_DRV_PROC(icd, DrvSwapBuffers, TRUE);
332 LOAD_DRV_PROC(icd, DrvSwapLayerBuffers, TRUE);
333
334 /* we require at least one of DrvCreateContext and DrvCreateLayerContext */
335 if (icd->DrvCreateContext == NULL || icd->DrvCreateLayerContext == NULL)
336 {
337 DBGPRINT( "Error: One of DrvCreateContext/DrvCreateLayerContext is required!" );
338 FreeLibrary( icd->handle );
339 return ERROR_INVALID_FUNCTION; /* FIXME: use better error code... */
340 }
341
342 return ERROR_SUCCESS;
343 }
344
345
346 /* FUNCTION: Unload loaded ICD.
347 * RETURNS: TRUE on success, FALSE otherwise.
348 */
349 static
350 BOOL
351 OPENGL32_UnloadDriver( GLDRIVERDATA *icd )
352 {
353 BOOL allOk = TRUE;
354
355 DBGPRINT( "Info: Unloading driver %ws...", icd->driver_name );
356 if (icd->refcount != 0)
357 DBGPRINT( "Warning: ICD refcount = %d (should be 0)", icd->refcount );
358
359 /* unload dll */
360 if (!FreeLibrary( icd->handle ))
361 {
362 allOk = FALSE;
363 DBGPRINT( "Warning: FreeLibrary on ICD %ws failed! (%d)", icd->dll,
364 GetLastError() );
365 }
366
367 /* free resources */
368 OPENGL32_RemoveICD( icd );
369 if (!HeapFree( GetProcessHeap(), 0, icd ))
370 {
371 allOk = FALSE;
372 DBGPRINT( "Warning: HeapFree() returned FALSE, error code = %d",
373 GetLastError() );
374 }
375
376 return allOk;
377 }
378
379
380 /* FUNCTION: Load ICD from HDC (shared ICD data)
381 * RETURNS: GLDRIVERDATA pointer on success, NULL otherwise.
382 * NOTES: Make sure the handle you pass in is one for a DC!
383 * Increases the refcount of the ICD - use
384 * OPENGL32_UnloadICD to release the ICD.
385 */
386 GLDRIVERDATA *OPENGL32_LoadICDForHDC( HDC hdc )
387 {
388 DWORD dwInput = 0;
389 LONG ret;
390 EXTDRIVERINFO info;
391
392 /* get driver name */
393 ret = ExtEscape( hdc, EXT_GET_DRIVERINFO, sizeof (dwInput), (LPCSTR)&dwInput,
394 sizeof (EXTDRIVERINFO), (LPSTR)&info );
395 if (ret < 0)
396 {
397 DBGPRINT( "Warning: ExtEscape to get the drivername failed!!! (%d)", GetLastError() );
398 return 0;
399 }
400
401 /* load driver (or get a reference) */
402 return OPENGL32_LoadICD( info.driver_name );
403 }
404
405
406 /* FUNCTION: Load ICD (shared ICD data)
407 * RETURNS: GLDRIVERDATA pointer on success, NULL otherwise.
408 */
409 GLDRIVERDATA*
410 OPENGL32_LoadICD ( LPCWSTR driver )
411 {
412 GLDRIVERDATA *icd;
413
414 /* synchronize */
415 if (WaitForSingleObject( OPENGL32_processdata.driver_mutex, INFINITE ) ==
416 WAIT_FAILED)
417 {
418 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
419 return NULL; /* FIXME: do we have to expect such an error and handle it? */
420 }
421
422 /* look if ICD is already loaded */
423 for (icd = OPENGL32_processdata.driver_list; icd; icd = icd->next)
424 {
425 if (!_wcsicmp( driver, icd->driver_name )) /* found */
426 {
427 icd->refcount++;
428
429 /* release mutex */
430 if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
431 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
432
433 return icd;
434 }
435 }
436
437 /* not found - try to load */
438 icd = OPENGL32_LoadDriver( driver );
439 if (icd != NULL)
440 icd->refcount = 1;
441
442 /* release mutex */
443 if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
444 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
445
446 return icd;
447 }
448
449
450 /* FUNCTION: Unload ICD (shared ICD data)
451 * RETURNS: TRUE on success, FALSE otherwise.
452 */
453 BOOL
454 OPENGL32_UnloadICD( GLDRIVERDATA *icd )
455 {
456 BOOL ret = TRUE;
457
458 /* synchronize */
459 if (WaitForSingleObject( OPENGL32_processdata.driver_mutex, INFINITE ) ==
460 WAIT_FAILED)
461 {
462 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
463 return FALSE; /* FIXME: do we have to expect such an error and handle it? */
464 }
465
466 icd->refcount--;
467 if (icd->refcount == 0)
468 ret = OPENGL32_UnloadDriver( icd );
469
470 /* release mutex */
471 if (!ReleaseMutex( OPENGL32_processdata.driver_mutex ))
472 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
473
474 return ret;
475 }
476
477
478 /* FUNCTION: Enumerate OpenGLDrivers (from registry)
479 * ARGUMENTS: [IN] idx Index of the driver to get information about
480 * [OUT] name Pointer to an array of WCHARs (can be NULL)
481 * [I,O] cName Pointer to a DWORD. Input is len of name array;
482 * Output is length of the drivername.
483 * Can be NULL if name is NULL.
484 * RETURNS: Error code (ERROR_NO_MORE_ITEMS at end of list); On failure all
485 * OUT vars are left untouched.
486 */
487 DWORD
488 OPENGL32_RegEnumDrivers( DWORD idx, LPWSTR name, LPDWORD cName )
489 {
490 HKEY hKey;
491 LPCWSTR subKey =
492 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers\\";
493 LONG ret;
494 DWORD size;
495 WCHAR driver[256];
496
497 if (name == NULL)
498 return ERROR_SUCCESS; /* nothing to do */
499
500 if (cName == NULL)
501 return ERROR_INVALID_FUNCTION; /* we need cName when name is given */
502
503 /* open OpenGLDrivers registry key */
504 ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey );
505 if (ret != ERROR_SUCCESS)
506 {
507 DBGPRINT( "Error: Couldn't open registry key '%ws'", subKey );
508 return ret;
509 }
510
511 /* get subkey name */
512 size = sizeof (driver) / sizeof (driver[0]);
513 ret = RegEnumKeyW( hKey, idx, name, *cName );
514 if (ret != ERROR_SUCCESS)
515 {
516 DBGPRINT( "Error: Couldn't get OpenGLDrivers subkey name (%d)", ret );
517 RegCloseKey( hKey );
518 return ret;
519 }
520 *cName = wcslen( name );
521
522 /* close key */
523 RegCloseKey( hKey );
524 return ERROR_SUCCESS;
525 }
526
527
528 /* FUNCTION: Get registry values for a driver given a name
529 * ARGUMENTS: [IN] idx Index of the driver to get information about
530 * [OUT] icd Pointer to GLDRIVERDATA. On success the following
531 * fields are filled: driver_name, dll, version,
532 * driver_version and flags.
533 * RETURNS: Error code; On failure all OUT vars are left untouched.
534 */
535 /*static*/
536 DWORD
537 OPENGL32_RegGetDriverInfo( LPCWSTR driver, GLDRIVERDATA *icd )
538 {
539 HKEY hKey;
540 WCHAR subKey[1024] =
541 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers\\";
542 LONG ret;
543 DWORD type, size;
544
545 /* drivers registry values */
546 DWORD version = 1, driverVersion = 0, flags = 0;
547 WCHAR dll[256];
548
549 /* open driver registry key */
550 wcsncat( subKey, driver, 1024 );
551 ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey );
552 if (ret != ERROR_SUCCESS)
553 {
554 DBGPRINT( "Error: Couldn't open registry key '%ws'", subKey );
555 return ret;
556 }
557
558 /* query values */
559 size = sizeof (dll);
560 ret = RegQueryValueExW( hKey, L"Dll", 0, &type, (LPBYTE)dll, &size );
561 if (ret != ERROR_SUCCESS || type != REG_SZ)
562 {
563 DBGPRINT( "Error: Couldn't query Dll value or not a string" );
564 RegCloseKey( hKey );
565 return ret;
566 }
567
568 size = sizeof (DWORD);
569 ret = RegQueryValueExW( hKey, L"Version", 0, &type, (LPBYTE)&version, &size );
570 if (ret != ERROR_SUCCESS || type != REG_DWORD)
571 DBGPRINT( "Warning: Couldn't query Version value or not a DWORD" );
572
573 size = sizeof (DWORD);
574 ret = RegQueryValueExW( hKey, L"DriverVersion", 0, &type,
575 (LPBYTE)&driverVersion, &size );
576 if (ret != ERROR_SUCCESS || type != REG_DWORD)
577 DBGPRINT( "Warning: Couldn't query DriverVersion value or not a DWORD" );
578
579 size = sizeof (DWORD);
580 ret = RegQueryValueExW( hKey, L"Flags", 0, &type, (LPBYTE)&flags, &size );
581 if (ret != ERROR_SUCCESS || type != REG_DWORD)
582 DBGPRINT( "Warning: Couldn't query Flags value or not a DWORD" );
583
584 /* close key */
585 RegCloseKey( hKey );
586
587 /* output data */
588 /* FIXME: NUL-terminate strings? */
589 wcsncpy( icd->driver_name, driver,
590 sizeof (icd->driver_name) / sizeof (icd->driver_name[0]) - 1 );
591 wcsncpy( icd->dll, dll,
592 sizeof (icd->dll) / sizeof (icd->dll[0]) );
593 icd->version = version;
594 icd->driver_version = driverVersion;
595 icd->flags = flags;
596
597 return ERROR_SUCCESS;
598 }
599
600 /* EOF */
601