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