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