dsound: Merge IUnknown into the main DirectSound object.
[wine/multimedia.git] / dlls / opengl32 / wgl.c
blob54f2a4350a300998d8edcc6b73b63ed0537b722a
1 /* Window-specific OpenGL functions implementation.
3 * Copyright (c) 1999 Lionel Ulmer
4 * Copyright (c) 2005 Raphael Junqueira
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winreg.h"
32 #include "wingdi.h"
33 #include "winternl.h"
34 #include "winnt.h"
36 #include "opengl_ext.h"
37 #ifdef HAVE_GL_GLU_H
38 #undef far
39 #undef near
40 #include <GL/glu.h>
41 #endif
42 #include "wine/gdi_driver.h"
43 #include "wine/wgl_driver.h"
44 #include "wine/library.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(wgl);
48 WINE_DECLARE_DEBUG_CHANNEL(opengl);
50 #ifdef SONAME_LIBGLU
51 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
52 MAKE_FUNCPTR(gluNewTess)
53 MAKE_FUNCPTR(gluDeleteTess)
54 MAKE_FUNCPTR(gluTessBeginContour)
55 MAKE_FUNCPTR(gluTessBeginPolygon)
56 MAKE_FUNCPTR(gluTessCallback)
57 MAKE_FUNCPTR(gluTessEndContour)
58 MAKE_FUNCPTR(gluTessEndPolygon)
59 MAKE_FUNCPTR(gluTessVertex)
60 #undef MAKE_FUNCPTR
61 #endif /* SONAME_LIBGLU */
63 static HMODULE opengl32_handle;
64 static void* libglu_handle = NULL;
66 extern struct opengl_funcs null_opengl_funcs;
68 const GLubyte * WINAPI wine_glGetString( GLenum name );
70 /* internal GDI functions */
71 extern INT WINAPI GdiDescribePixelFormat( HDC hdc, INT fmt, UINT size, PIXELFORMATDESCRIPTOR *pfd );
72 extern BOOL WINAPI GdiSetPixelFormat( HDC hdc, INT fmt, const PIXELFORMATDESCRIPTOR *pfd );
73 extern BOOL WINAPI GdiSwapBuffers( HDC hdc );
75 /* handle management */
77 #define MAX_WGL_HANDLES 1024
79 struct wgl_handle
81 UINT handle;
82 DWORD tid;
83 struct wgl_context * context;
84 const struct wgl_funcs *funcs;
87 static struct wgl_handle wgl_handles[MAX_WGL_HANDLES];
88 static struct wgl_handle *next_free;
89 static unsigned int handle_count;
91 static CRITICAL_SECTION wgl_section;
92 static CRITICAL_SECTION_DEBUG critsect_debug =
94 0, 0, &wgl_section,
95 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
96 0, 0, { (DWORD_PTR)(__FILE__ ": wgl_section") }
98 static CRITICAL_SECTION wgl_section = { &critsect_debug, -1, 0, 0, 0, 0 };
100 static inline const struct wgl_funcs *get_dc_funcs( HDC hdc )
102 return __wine_get_wgl_driver( hdc, WINE_GDI_DRIVER_VERSION );
105 static inline HGLRC next_handle( struct wgl_handle *ptr )
107 WORD generation = HIWORD( ptr->handle ) + 1;
108 if (!generation) generation++;
109 ptr->handle = MAKELONG( ptr - wgl_handles, generation );
110 return ULongToHandle( ptr->handle );
113 /* the current handle is assumed valid and doesn't need locking */
114 static inline struct wgl_handle *get_current_handle_ptr(void)
116 if (!NtCurrentTeb()->glCurrentRC) return NULL;
117 return &wgl_handles[LOWORD(NtCurrentTeb()->glCurrentRC)];
120 static struct wgl_handle *get_handle_ptr( HGLRC handle )
122 unsigned int index = LOWORD( handle );
124 EnterCriticalSection( &wgl_section );
125 if (index < handle_count && ULongToHandle(wgl_handles[index].handle) == handle)
126 return &wgl_handles[index];
128 LeaveCriticalSection( &wgl_section );
129 SetLastError( ERROR_INVALID_HANDLE );
130 return NULL;
133 static void release_handle_ptr( struct wgl_handle *ptr )
135 if (ptr) LeaveCriticalSection( &wgl_section );
138 static HGLRC alloc_handle( struct wgl_context *context, const struct wgl_funcs *funcs )
140 HGLRC handle = 0;
141 struct wgl_handle *ptr = NULL;
143 EnterCriticalSection( &wgl_section );
144 if ((ptr = next_free))
145 next_free = (struct wgl_handle *)next_free->context;
146 else if (handle_count < MAX_WGL_HANDLES)
147 ptr = &wgl_handles[handle_count++];
149 if (ptr)
151 ptr->context = context;
152 ptr->funcs = funcs;
153 handle = next_handle( ptr );
155 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
156 LeaveCriticalSection( &wgl_section );
157 return handle;
160 static void free_handle_ptr( struct wgl_handle *ptr )
162 ptr->handle |= 0xffff;
163 ptr->context = (struct wgl_context *)next_free;
164 ptr->funcs = NULL;
165 next_free = ptr;
166 LeaveCriticalSection( &wgl_section );
169 /***********************************************************************
170 * wglSetPixelFormat(OPENGL32.@)
172 BOOL WINAPI wglSetPixelFormat( HDC hdc, INT iPixelFormat,
173 const PIXELFORMATDESCRIPTOR *ppfd)
175 return GdiSetPixelFormat(hdc, iPixelFormat, ppfd);
178 /***********************************************************************
179 * wglCopyContext (OPENGL32.@)
181 BOOL WINAPI wglCopyContext(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask)
183 struct wgl_handle *src, *dst;
184 BOOL ret = FALSE;
186 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
187 if ((dst = get_handle_ptr( hglrcDst )))
189 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
190 else ret = src->funcs->p_wglCopyContext( src->context, dst->context, mask );
192 release_handle_ptr( dst );
193 release_handle_ptr( src );
194 return ret;
197 /***********************************************************************
198 * wglDeleteContext (OPENGL32.@)
200 BOOL WINAPI wglDeleteContext(HGLRC hglrc)
202 struct wgl_handle *ptr = get_handle_ptr( hglrc );
204 if (!ptr) return FALSE;
206 if (ptr->tid && ptr->tid != GetCurrentThreadId())
208 SetLastError( ERROR_BUSY );
209 release_handle_ptr( ptr );
210 return FALSE;
212 if (hglrc == NtCurrentTeb()->glCurrentRC) wglMakeCurrent( 0, 0 );
213 ptr->funcs->p_wglDeleteContext( ptr->context );
214 free_handle_ptr( ptr );
215 return TRUE;
218 /***********************************************************************
219 * wglMakeCurrent (OPENGL32.@)
221 BOOL WINAPI wglMakeCurrent(HDC hdc, HGLRC hglrc)
223 BOOL ret = TRUE;
224 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
226 if (hglrc)
228 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
229 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
231 ret = ptr->funcs->p_wglMakeCurrent( hdc, ptr->context );
232 if (ret)
234 if (prev) prev->tid = 0;
235 ptr->tid = GetCurrentThreadId();
236 NtCurrentTeb()->glCurrentRC = hglrc;
239 else
241 SetLastError( ERROR_BUSY );
242 ret = FALSE;
244 release_handle_ptr( ptr );
246 else if (prev)
248 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
249 prev->tid = 0;
250 NtCurrentTeb()->glCurrentRC = 0;
251 NtCurrentTeb()->glTable = &null_opengl_funcs;
253 else if (!hdc)
255 SetLastError( ERROR_INVALID_HANDLE );
256 ret = FALSE;
258 return ret;
261 /***********************************************************************
262 * wglCreateContextAttribsARB (wrapper for the extension function returned by the driver)
264 static HGLRC WINAPI wglCreateContextAttribsARB( HDC hdc, HGLRC share, const int *attribs )
266 HGLRC ret = 0;
267 struct wgl_context *context;
268 struct wgl_handle *share_ptr = NULL;
269 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
271 if (!funcs) return 0;
272 if (share && !(share_ptr = get_handle_ptr( share ))) return 0;
273 if ((context = funcs->p_wglCreateContextAttribsARB( hdc, share_ptr ? share_ptr->context : NULL,
274 attribs )))
276 ret = alloc_handle( context, funcs );
277 if (!ret) funcs->p_wglDeleteContext( context );
279 release_handle_ptr( share_ptr );
280 return ret;
284 /***********************************************************************
285 * wglMakeContextCurrentARB (wrapper for the extension function returned by the driver)
287 static BOOL WINAPI wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, HGLRC hglrc )
289 BOOL ret = TRUE;
290 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
292 if (hglrc)
294 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
295 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
297 ret = ptr->funcs->p_wglMakeContextCurrentARB( draw_hdc, read_hdc, ptr->context );
298 if (ret)
300 if (prev) prev->tid = 0;
301 ptr->tid = GetCurrentThreadId();
302 NtCurrentTeb()->glCurrentRC = hglrc;
305 else
307 SetLastError( ERROR_BUSY );
308 ret = FALSE;
310 release_handle_ptr( ptr );
312 else if (prev)
314 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
315 prev->tid = 0;
316 NtCurrentTeb()->glCurrentRC = 0;
317 NtCurrentTeb()->glTable = &null_opengl_funcs;
319 return ret;
322 /***********************************************************************
323 * wglShareLists (OPENGL32.@)
325 BOOL WINAPI wglShareLists(HGLRC hglrcSrc, HGLRC hglrcDst)
327 BOOL ret = FALSE;
328 struct wgl_handle *src, *dst;
330 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
331 if ((dst = get_handle_ptr( hglrcDst )))
333 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
334 else ret = src->funcs->p_wglShareLists( src->context, dst->context );
336 release_handle_ptr( dst );
337 release_handle_ptr( src );
338 return ret;
341 /***********************************************************************
342 * wglGetCurrentDC (OPENGL32.@)
344 HDC WINAPI wglGetCurrentDC(void)
346 struct wgl_handle *context = get_current_handle_ptr();
347 if (!context) return 0;
348 return context->funcs->p_wglGetCurrentDC( context->context );
351 /***********************************************************************
352 * wglCreateContext (OPENGL32.@)
354 HGLRC WINAPI wglCreateContext(HDC hdc)
356 HGLRC ret = 0;
357 struct wgl_context *context;
358 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
360 if (!funcs) return 0;
361 if (!(context = funcs->p_wglCreateContext( hdc ))) return 0;
362 ret = alloc_handle( context, funcs );
363 if (!ret) funcs->p_wglDeleteContext( context );
364 return ret;
367 /***********************************************************************
368 * wglGetCurrentContext (OPENGL32.@)
370 HGLRC WINAPI wglGetCurrentContext(void)
372 return NtCurrentTeb()->glCurrentRC;
375 /***********************************************************************
376 * wglChoosePixelFormat (OPENGL32.@)
378 INT WINAPI wglChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd)
380 PIXELFORMATDESCRIPTOR format, best;
381 int i, count, best_format;
382 int bestDBuffer = -1, bestStereo = -1;
384 TRACE_(wgl)( "%p %p: size %u version %u flags %u type %u color %u %u,%u,%u,%u "
385 "accum %u depth %u stencil %u aux %u\n",
386 hdc, ppfd, ppfd->nSize, ppfd->nVersion, ppfd->dwFlags, ppfd->iPixelType,
387 ppfd->cColorBits, ppfd->cRedBits, ppfd->cGreenBits, ppfd->cBlueBits, ppfd->cAlphaBits,
388 ppfd->cAccumBits, ppfd->cDepthBits, ppfd->cStencilBits, ppfd->cAuxBuffers );
390 count = GdiDescribePixelFormat( hdc, 0, 0, NULL );
391 if (!count) return 0;
393 best_format = 0;
394 best.dwFlags = 0;
395 best.cAlphaBits = -1;
396 best.cColorBits = -1;
397 best.cDepthBits = -1;
398 best.cStencilBits = -1;
399 best.cAuxBuffers = -1;
401 for (i = 1; i <= count; i++)
403 if (!GdiDescribePixelFormat( hdc, i, sizeof(format), &format )) continue;
405 if (ppfd->iPixelType != format.iPixelType)
407 TRACE( "pixel type mismatch for iPixelFormat=%d\n", i );
408 continue;
411 /* only use bitmap capable for formats for bitmap rendering */
412 if( (ppfd->dwFlags & PFD_DRAW_TO_BITMAP) != (format.dwFlags & PFD_DRAW_TO_BITMAP))
414 TRACE( "PFD_DRAW_TO_BITMAP mismatch for iPixelFormat=%d\n", i );
415 continue;
418 /* The behavior of PDF_STEREO/PFD_STEREO_DONTCARE and PFD_DOUBLEBUFFER / PFD_DOUBLEBUFFER_DONTCARE
419 * is not very clear on MSDN. They specify that ChoosePixelFormat tries to match pixel formats
420 * with the flag (PFD_STEREO / PFD_DOUBLEBUFFERING) set. Otherwise it says that it tries to match
421 * formats without the given flag set.
422 * A test on Windows using a Radeon 9500pro on WinXP (the driver doesn't support Stereo)
423 * has indicated that a format without stereo is returned when stereo is unavailable.
424 * So in case PFD_STEREO is set, formats that support it should have priority above formats
425 * without. In case PFD_STEREO_DONTCARE is set, stereo is ignored.
427 * To summarize the following is most likely the correct behavior:
428 * stereo not set -> prefer non-stereo formats, but also accept stereo formats
429 * stereo set -> prefer stereo formats, but also accept non-stereo formats
430 * stereo don't care -> it doesn't matter whether we get stereo or not
432 * In Wine we will treat non-stereo the same way as don't care because it makes
433 * format selection even more complicated and second drivers with Stereo advertise
434 * each format twice anyway.
437 /* Doublebuffer, see the comments above */
438 if (!(ppfd->dwFlags & PFD_DOUBLEBUFFER_DONTCARE))
440 if (((ppfd->dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) &&
441 ((format.dwFlags & PFD_DOUBLEBUFFER) == (ppfd->dwFlags & PFD_DOUBLEBUFFER)))
442 goto found;
444 if (bestDBuffer != -1 && (format.dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) continue;
447 /* Stereo, see the comments above. */
448 if (!(ppfd->dwFlags & PFD_STEREO_DONTCARE))
450 if (((ppfd->dwFlags & PFD_STEREO) != bestStereo) &&
451 ((format.dwFlags & PFD_STEREO) == (ppfd->dwFlags & PFD_STEREO)))
452 goto found;
454 if (bestStereo != -1 && (format.dwFlags & PFD_STEREO) != bestStereo) continue;
457 /* Below we will do a number of checks to select the 'best' pixelformat.
458 * We assume the precedence cColorBits > cAlphaBits > cDepthBits > cStencilBits -> cAuxBuffers.
459 * The code works by trying to match the most important options as close as possible.
460 * When a reasonable format is found, we will try to match more options.
461 * It appears (see the opengl32 test) that Windows opengl drivers ignore options
462 * like cColorBits, cAlphaBits and friends if they are set to 0, so they are considered
463 * as DONTCARE. At least Serious Sam TSE relies on this behavior. */
465 if (ppfd->cColorBits)
467 if (((ppfd->cColorBits > best.cColorBits) && (format.cColorBits > best.cColorBits)) ||
468 ((format.cColorBits >= ppfd->cColorBits) && (format.cColorBits < best.cColorBits)))
469 goto found;
471 if (best.cColorBits != format.cColorBits) /* Do further checks if the format is compatible */
473 TRACE( "color mismatch for iPixelFormat=%d\n", i );
474 continue;
477 if (ppfd->cAlphaBits)
479 if (((ppfd->cAlphaBits > best.cAlphaBits) && (format.cAlphaBits > best.cAlphaBits)) ||
480 ((format.cAlphaBits >= ppfd->cAlphaBits) && (format.cAlphaBits < best.cAlphaBits)))
481 goto found;
483 if (best.cAlphaBits != format.cAlphaBits)
485 TRACE( "alpha mismatch for iPixelFormat=%d\n", i );
486 continue;
489 if (ppfd->cDepthBits)
491 if (((ppfd->cDepthBits > best.cDepthBits) && (format.cDepthBits > best.cDepthBits)) ||
492 ((format.cDepthBits >= ppfd->cDepthBits) && (format.cDepthBits < best.cDepthBits)))
493 goto found;
495 if (best.cDepthBits != format.cDepthBits)
497 TRACE( "depth mismatch for iPixelFormat=%d\n", i );
498 continue;
501 if (ppfd->cStencilBits)
503 if (((ppfd->cStencilBits > best.cStencilBits) && (format.cStencilBits > best.cStencilBits)) ||
504 ((format.cStencilBits >= ppfd->cStencilBits) && (format.cStencilBits < best.cStencilBits)))
505 goto found;
507 if (best.cStencilBits != format.cStencilBits)
509 TRACE( "stencil mismatch for iPixelFormat=%d\n", i );
510 continue;
513 if (ppfd->cAuxBuffers)
515 if (((ppfd->cAuxBuffers > best.cAuxBuffers) && (format.cAuxBuffers > best.cAuxBuffers)) ||
516 ((format.cAuxBuffers >= ppfd->cAuxBuffers) && (format.cAuxBuffers < best.cAuxBuffers)))
517 goto found;
519 if (best.cAuxBuffers != format.cAuxBuffers)
521 TRACE( "aux mismatch for iPixelFormat=%d\n", i );
522 continue;
525 continue;
527 found:
528 best_format = i;
529 best = format;
530 bestDBuffer = format.dwFlags & PFD_DOUBLEBUFFER;
531 bestStereo = format.dwFlags & PFD_STEREO;
534 TRACE( "returning %u\n", best_format );
535 return best_format;
538 /***********************************************************************
539 * wglDescribePixelFormat (OPENGL32.@)
541 INT WINAPI wglDescribePixelFormat(HDC hdc, INT iPixelFormat, UINT nBytes,
542 LPPIXELFORMATDESCRIPTOR ppfd)
544 return GdiDescribePixelFormat(hdc, iPixelFormat, nBytes, ppfd);
547 /***********************************************************************
548 * wglGetPixelFormat (OPENGL32.@)
550 INT WINAPI wglGetPixelFormat(HDC hdc)
552 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
553 if (!funcs) return 0;
554 return funcs->p_GetPixelFormat( hdc );
557 /***********************************************************************
558 * wglCreateLayerContext (OPENGL32.@)
560 HGLRC WINAPI wglCreateLayerContext(HDC hdc,
561 int iLayerPlane) {
562 TRACE("(%p,%d)\n", hdc, iLayerPlane);
564 if (iLayerPlane == 0) {
565 return wglCreateContext(hdc);
567 FIXME("no handler for layer %d\n", iLayerPlane);
569 return NULL;
572 /***********************************************************************
573 * wglDescribeLayerPlane (OPENGL32.@)
575 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
576 int iPixelFormat,
577 int iLayerPlane,
578 UINT nBytes,
579 LPLAYERPLANEDESCRIPTOR plpd) {
580 FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
582 return FALSE;
585 /***********************************************************************
586 * wglGetLayerPaletteEntries (OPENGL32.@)
588 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
589 int iLayerPlane,
590 int iStart,
591 int cEntries,
592 const COLORREF *pcr) {
593 FIXME("(): stub!\n");
595 return 0;
598 /* check if the extension is present in the list */
599 static BOOL has_extension( const char *list, const char *ext )
601 size_t len = strlen( ext );
603 while (list)
605 while (*list == ' ') list++;
606 if (!strncmp( list, ext, len ) && (!list[len] || list[len] == ' ')) return TRUE;
607 list = strchr( list, ' ' );
609 return FALSE;
612 static int compar(const void *elt_a, const void *elt_b) {
613 return strcmp(((const OpenGL_extension *) elt_a)->name,
614 ((const OpenGL_extension *) elt_b)->name);
617 /* Check if a GL extension is supported */
618 static BOOL is_extension_supported(const char* extension)
620 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
621 const char *gl_ext_string = (const char*)wine_glGetString(GL_EXTENSIONS);
623 TRACE("Checking for extension '%s'\n", extension);
625 if(!gl_ext_string) {
626 ERR("No OpenGL extensions found, check if your OpenGL setup is correct!\n");
627 return FALSE;
630 /* We use the GetProcAddress function from the display driver to retrieve function pointers
631 * for OpenGL and WGL extensions. In case of winex11.drv the OpenGL extension lookup is done
632 * using glXGetProcAddress. This function is quite unreliable in the sense that its specs don't
633 * require the function to return NULL when an extension isn't found. For this reason we check
634 * if the OpenGL extension required for the function we are looking up is supported. */
636 /* Check if the extension is part of the GL extension string to see if it is supported. */
637 if (has_extension(gl_ext_string, extension))
638 return TRUE;
640 /* In general an OpenGL function starts as an ARB/EXT extension and at some stage
641 * it becomes part of the core OpenGL library and can be reached without the ARB/EXT
642 * suffix as well. In the extension table, these functions contain GL_VERSION_major_minor.
643 * Check if we are searching for a core GL function */
644 if(strncmp(extension, "GL_VERSION_", 11) == 0)
646 const GLubyte *gl_version = funcs->gl.p_glGetString(GL_VERSION);
647 const char *version = extension + 11; /* Move past 'GL_VERSION_' */
649 if(!gl_version) {
650 ERR("No OpenGL version found!\n");
651 return FALSE;
654 /* Compare the major/minor version numbers of the native OpenGL library and what is required by the function.
655 * The gl_version string is guaranteed to have at least a major/minor and sometimes it has a release number as well. */
656 if( (gl_version[0] >= version[0]) || ((gl_version[0] == version[0]) && (gl_version[2] >= version[2])) ) {
657 return TRUE;
659 WARN("The function requires OpenGL version '%c.%c' while your drivers only provide '%c.%c'\n", version[0], version[2], gl_version[0], gl_version[2]);
662 return FALSE;
665 static const OpenGL_extension wgl_extensions[] =
667 { "wglCreateContextAttribsARB", "WGL_ARB_create_context", wglCreateContextAttribsARB },
668 { "wglMakeContextCurrentARB", "WGL_ARB_make_current_read", wglMakeContextCurrentARB },
671 /***********************************************************************
672 * wglGetProcAddress (OPENGL32.@)
674 PROC WINAPI wglGetProcAddress(LPCSTR lpszProc) {
675 void *local_func;
676 OpenGL_extension ext;
677 const OpenGL_extension *ext_ret;
678 struct wgl_handle *context = get_current_handle_ptr();
680 TRACE("(%s)\n", lpszProc);
682 if (lpszProc == NULL)
683 return NULL;
685 /* Without an active context opengl32 doesn't know to what
686 * driver it has to dispatch wglGetProcAddress.
688 if (!context)
690 WARN("No active WGL context found\n");
691 return NULL;
694 /* Search in the thunks to find the real name of the extension */
695 ext.name = lpszProc;
696 ext_ret = bsearch(&ext, extension_registry, extension_registry_size,
697 sizeof(OpenGL_extension), compar);
699 /* If nothing was found, we are looking for a WGL extension or an unknown GL extension. */
700 if (ext_ret == NULL) {
701 /* If the function name starts with a 'w', it is a WGL extension */
702 if(lpszProc[0] == 'w')
704 local_func = context->funcs->p_wglGetProcAddress( lpszProc );
705 if (local_func == (void *)1) /* special function that needs a wrapper */
707 ext_ret = bsearch( &ext, wgl_extensions, sizeof(wgl_extensions)/sizeof(wgl_extensions[0]),
708 sizeof(OpenGL_extension), compar );
709 if (ext_ret) return ext_ret->func;
711 FIXME( "wrapper missing for %s\n", lpszProc );
712 return NULL;
714 return local_func;
717 /* We are dealing with an unknown GL extension */
718 WARN("Extension '%s' not defined in opengl32.dll's function table!\n", lpszProc);
719 return NULL;
720 } else { /* We are looking for an OpenGL extension */
722 /* Check if the GL extension required by the function is available */
723 if(!is_extension_supported(ext_ret->extension)) {
724 WARN("Extension '%s' required by function '%s' not supported!\n", ext_ret->extension, lpszProc);
727 local_func = context->funcs->p_wglGetProcAddress( ext_ret->name );
729 /* After that, look at the extensions defined in the Linux OpenGL library */
730 if (local_func == NULL) {
731 char buf[256];
732 void *ret = NULL;
734 /* Remove the last 3 letters (EXT, ARB, ...).
736 I know that some extensions have more than 3 letters (MESA, NV,
737 INTEL, ...), but this is only a stop-gap measure to fix buggy
738 OpenGL drivers (moreover, it is only useful for old 1.0 apps
739 that query the glBindTextureEXT extension).
741 memcpy(buf, ext_ret->name, strlen(ext_ret->name) - 3);
742 buf[strlen(ext_ret->name) - 3] = '\0';
743 TRACE("Extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf);
745 ret = GetProcAddress(opengl32_handle, buf);
746 if (ret != NULL) {
747 TRACE("Found function in main OpenGL library (%p)!\n", ret);
748 } else {
749 WARN("Did not find function %s (%s) in your OpenGL library!\n", lpszProc, ext_ret->name);
752 return ret;
753 } else {
754 struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
755 *((void **)&funcs->ext + (ext_ret - extension_registry)) = local_func;
756 TRACE("returning function (%p)\n", ext_ret->func);
757 return ext_ret->func;
762 /***********************************************************************
763 * wglRealizeLayerPalette (OPENGL32.@)
765 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
766 int iLayerPlane,
767 BOOL bRealize) {
768 FIXME("()\n");
770 return FALSE;
773 /***********************************************************************
774 * wglSetLayerPaletteEntries (OPENGL32.@)
776 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
777 int iLayerPlane,
778 int iStart,
779 int cEntries,
780 const COLORREF *pcr) {
781 FIXME("(): stub!\n");
783 return 0;
786 /***********************************************************************
787 * wglSwapLayerBuffers (OPENGL32.@)
789 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
790 UINT fuPlanes) {
791 TRACE_(opengl)("(%p, %08x)\n", hdc, fuPlanes);
793 if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
794 if (!GdiSwapBuffers(hdc)) return FALSE;
795 fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
798 if (fuPlanes) {
799 WARN("Following layers unhandled: %08x\n", fuPlanes);
802 return TRUE;
805 /***********************************************************************
806 * wglUseFontBitmaps_common
808 static BOOL wglUseFontBitmaps_common( HDC hdc, DWORD first, DWORD count, DWORD listBase, BOOL unicode )
810 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
811 GLYPHMETRICS gm;
812 unsigned int glyph, size = 0;
813 void *bitmap = NULL, *gl_bitmap = NULL;
814 int org_alignment;
815 BOOL ret = TRUE;
817 funcs->gl.p_glGetIntegerv(GL_UNPACK_ALIGNMENT, &org_alignment);
818 funcs->gl.p_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
820 for (glyph = first; glyph < first + count; glyph++) {
821 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
822 unsigned int needed_size, height, width, width_int;
824 if (unicode)
825 needed_size = GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
826 else
827 needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
829 TRACE("Glyph: %3d / List: %d size %d\n", glyph, listBase, needed_size);
830 if (needed_size == GDI_ERROR) {
831 ret = FALSE;
832 break;
835 if (needed_size > size) {
836 size = needed_size;
837 HeapFree(GetProcessHeap(), 0, bitmap);
838 HeapFree(GetProcessHeap(), 0, gl_bitmap);
839 bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
840 gl_bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
842 if (unicode)
843 ret = (GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
844 else
845 ret = (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
846 if (!ret) break;
848 if (TRACE_ON(wgl)) {
849 unsigned int bitmask;
850 unsigned char *bitmap_ = bitmap;
852 TRACE(" - bbox: %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
853 TRACE(" - origin: (%d, %d)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
854 TRACE(" - increment: %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
855 if (needed_size != 0) {
856 TRACE(" - bitmap:\n");
857 for (height = 0; height < gm.gmBlackBoxY; height++) {
858 TRACE(" ");
859 for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
860 if (bitmask == 0) {
861 bitmap_ += 1;
862 bitmask = 0x80;
864 if (*bitmap_ & bitmask)
865 TRACE("*");
866 else
867 TRACE(" ");
869 bitmap_ += (4 - ((UINT_PTR)bitmap_ & 0x03));
870 TRACE("\n");
875 /* In OpenGL, the bitmap is drawn from the bottom to the top... So we need to invert the
876 * glyph for it to be drawn properly.
878 if (needed_size != 0) {
879 width_int = (gm.gmBlackBoxX + 31) / 32;
880 for (height = 0; height < gm.gmBlackBoxY; height++) {
881 for (width = 0; width < width_int; width++) {
882 ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
883 ((int *) bitmap)[height * width_int + width];
888 funcs->gl.p_glNewList(listBase++, GL_COMPILE);
889 if (needed_size != 0) {
890 funcs->gl.p_glBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY,
891 0 - gm.gmptGlyphOrigin.x, (int) gm.gmBlackBoxY - gm.gmptGlyphOrigin.y,
892 gm.gmCellIncX, gm.gmCellIncY,
893 gl_bitmap);
894 } else {
895 /* This is the case of 'empty' glyphs like the space character */
896 funcs->gl.p_glBitmap(0, 0, 0, 0, gm.gmCellIncX, gm.gmCellIncY, NULL);
898 funcs->gl.p_glEndList();
901 funcs->gl.p_glPixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
902 HeapFree(GetProcessHeap(), 0, bitmap);
903 HeapFree(GetProcessHeap(), 0, gl_bitmap);
904 return ret;
907 /***********************************************************************
908 * wglUseFontBitmapsA (OPENGL32.@)
910 BOOL WINAPI wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase)
912 return wglUseFontBitmaps_common( hdc, first, count, listBase, FALSE );
915 /***********************************************************************
916 * wglUseFontBitmapsW (OPENGL32.@)
918 BOOL WINAPI wglUseFontBitmapsW(HDC hdc, DWORD first, DWORD count, DWORD listBase)
920 return wglUseFontBitmaps_common( hdc, first, count, listBase, TRUE );
923 #ifdef SONAME_LIBGLU
925 static void *load_libglu(void)
927 static int already_loaded;
928 void *handle;
930 if (already_loaded) return libglu_handle;
931 already_loaded = 1;
933 TRACE("Trying to load GLU library: %s\n", SONAME_LIBGLU);
934 handle = wine_dlopen(SONAME_LIBGLU, RTLD_NOW, NULL, 0);
935 if (!handle)
937 WARN("Failed to load %s\n", SONAME_LIBGLU);
938 return NULL;
941 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(handle, #f, NULL, 0)) == NULL) goto sym_not_found;
942 LOAD_FUNCPTR(gluNewTess)
943 LOAD_FUNCPTR(gluDeleteTess)
944 LOAD_FUNCPTR(gluTessBeginContour)
945 LOAD_FUNCPTR(gluTessBeginPolygon)
946 LOAD_FUNCPTR(gluTessCallback)
947 LOAD_FUNCPTR(gluTessEndContour)
948 LOAD_FUNCPTR(gluTessEndPolygon)
949 LOAD_FUNCPTR(gluTessVertex)
950 #undef LOAD_FUNCPTR
951 libglu_handle = handle;
952 return handle;
954 sym_not_found:
955 WARN("Unable to load function ptrs from libGLU\n");
956 /* Close the library as we won't use it */
957 wine_dlclose(handle, NULL, 0);
958 return NULL;
961 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
963 vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
964 vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
965 vertex[2] = 0.0;
968 static void tess_callback_vertex(GLvoid *vertex)
970 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
971 GLdouble *dbl = vertex;
972 TRACE("%f, %f, %f\n", dbl[0], dbl[1], dbl[2]);
973 funcs->gl.p_glVertex3dv(vertex);
976 static void tess_callback_begin(GLenum which)
978 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
979 TRACE("%d\n", which);
980 funcs->gl.p_glBegin(which);
983 static void tess_callback_end(void)
985 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
986 TRACE("\n");
987 funcs->gl.p_glEnd();
990 /***********************************************************************
991 * wglUseFontOutlines_common
993 static BOOL wglUseFontOutlines_common(HDC hdc,
994 DWORD first,
995 DWORD count,
996 DWORD listBase,
997 FLOAT deviation,
998 FLOAT extrusion,
999 int format,
1000 LPGLYPHMETRICSFLOAT lpgmf,
1001 BOOL unicode)
1003 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
1004 UINT glyph;
1005 const MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
1006 GLUtesselator *tess;
1007 LOGFONTW lf;
1008 HFONT old_font, unscaled_font;
1009 UINT em_size = 1024;
1010 RECT rc;
1012 TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
1013 listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
1015 if (!load_libglu())
1017 ERR("libGLU is required for this function but isn't loaded\n");
1018 return FALSE;
1021 tess = pgluNewTess();
1022 if(!tess) return FALSE;
1023 pgluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tess_callback_vertex);
1024 pgluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tess_callback_begin);
1025 pgluTessCallback(tess, GLU_TESS_END, tess_callback_end);
1027 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
1028 rc.left = rc.right = rc.bottom = 0;
1029 rc.top = em_size;
1030 DPtoLP(hdc, (POINT*)&rc, 2);
1031 lf.lfHeight = -abs(rc.top - rc.bottom);
1032 lf.lfOrientation = lf.lfEscapement = 0;
1033 unscaled_font = CreateFontIndirectW(&lf);
1034 old_font = SelectObject(hdc, unscaled_font);
1036 for (glyph = first; glyph < first + count; glyph++)
1038 DWORD needed;
1039 GLYPHMETRICS gm;
1040 BYTE *buf;
1041 TTPOLYGONHEADER *pph;
1042 TTPOLYCURVE *ppc;
1043 GLdouble *vertices;
1045 if(unicode)
1046 needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1047 else
1048 needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1050 if(needed == GDI_ERROR)
1051 goto error;
1053 buf = HeapAlloc(GetProcessHeap(), 0, needed);
1054 vertices = HeapAlloc(GetProcessHeap(), 0, needed / sizeof(POINTFX) * 3 * sizeof(GLdouble));
1056 if(unicode)
1057 GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1058 else
1059 GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1061 TRACE("glyph %d\n", glyph);
1063 if(lpgmf)
1065 lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
1066 lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
1067 lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
1068 lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
1069 lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
1070 lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
1072 TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
1073 lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
1074 lpgmf++;
1077 funcs->gl.p_glNewList(listBase++, GL_COMPILE);
1078 pgluTessBeginPolygon(tess, NULL);
1080 pph = (TTPOLYGONHEADER*)buf;
1081 while((BYTE*)pph < buf + needed)
1083 TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
1085 pgluTessBeginContour(tess);
1087 fixed_to_double(pph->pfxStart, em_size, vertices);
1088 pgluTessVertex(tess, vertices, vertices);
1089 vertices += 3;
1091 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
1092 while((char*)ppc < (char*)pph + pph->cb)
1094 int i;
1096 switch(ppc->wType) {
1097 case TT_PRIM_LINE:
1098 for(i = 0; i < ppc->cpfx; i++)
1100 TRACE("\t\tline to %d, %d\n", ppc->apfx[i].x.value, ppc->apfx[i].y.value);
1101 fixed_to_double(ppc->apfx[i], em_size, vertices);
1102 pgluTessVertex(tess, vertices, vertices);
1103 vertices += 3;
1105 break;
1107 case TT_PRIM_QSPLINE:
1108 for(i = 0; i < ppc->cpfx/2; i++)
1110 /* FIXME: just connecting the control points for now */
1111 TRACE("\t\tcurve %d,%d %d,%d\n",
1112 ppc->apfx[i * 2].x.value, ppc->apfx[i * 3].y.value,
1113 ppc->apfx[i * 2 + 1].x.value, ppc->apfx[i * 3 + 1].y.value);
1114 fixed_to_double(ppc->apfx[i * 2], em_size, vertices);
1115 pgluTessVertex(tess, vertices, vertices);
1116 vertices += 3;
1117 fixed_to_double(ppc->apfx[i * 2 + 1], em_size, vertices);
1118 pgluTessVertex(tess, vertices, vertices);
1119 vertices += 3;
1121 break;
1122 default:
1123 ERR("\t\tcurve type = %d\n", ppc->wType);
1124 pgluTessEndContour(tess);
1125 goto error_in_list;
1128 ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
1129 (ppc->cpfx - 1) * sizeof(POINTFX));
1131 pgluTessEndContour(tess);
1132 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
1135 error_in_list:
1136 pgluTessEndPolygon(tess);
1137 funcs->gl.p_glTranslated((GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0);
1138 funcs->gl.p_glEndList();
1139 HeapFree(GetProcessHeap(), 0, buf);
1140 HeapFree(GetProcessHeap(), 0, vertices);
1143 error:
1144 DeleteObject(SelectObject(hdc, old_font));
1145 pgluDeleteTess(tess);
1146 return TRUE;
1150 #else /* SONAME_LIBGLU */
1152 static BOOL wglUseFontOutlines_common(HDC hdc,
1153 DWORD first,
1154 DWORD count,
1155 DWORD listBase,
1156 FLOAT deviation,
1157 FLOAT extrusion,
1158 int format,
1159 LPGLYPHMETRICSFLOAT lpgmf,
1160 BOOL unicode)
1162 FIXME("Unable to compile in wglUseFontOutlines support without GL/glu.h\n");
1163 return FALSE;
1166 #endif /* SONAME_LIBGLU */
1168 /***********************************************************************
1169 * wglUseFontOutlinesA (OPENGL32.@)
1171 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
1172 DWORD first,
1173 DWORD count,
1174 DWORD listBase,
1175 FLOAT deviation,
1176 FLOAT extrusion,
1177 int format,
1178 LPGLYPHMETRICSFLOAT lpgmf)
1180 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
1183 /***********************************************************************
1184 * wglUseFontOutlinesW (OPENGL32.@)
1186 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
1187 DWORD first,
1188 DWORD count,
1189 DWORD listBase,
1190 FLOAT deviation,
1191 FLOAT extrusion,
1192 int format,
1193 LPGLYPHMETRICSFLOAT lpgmf)
1195 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
1198 /***********************************************************************
1199 * glDebugEntry (OPENGL32.@)
1201 GLint WINAPI wine_glDebugEntry( GLint unknown1, GLint unknown2 )
1203 return 0;
1206 /* build the extension string by filtering out the disabled extensions */
1207 static char *build_gl_extensions( const char *extensions )
1209 char *p, *str, *disabled = NULL;
1210 const char *end;
1211 HKEY hkey;
1213 TRACE( "GL_EXTENSIONS:\n" );
1215 if (!extensions) extensions = "";
1217 /* @@ Wine registry key: HKCU\Software\Wine\OpenGL */
1218 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\OpenGL", &hkey ))
1220 DWORD size, ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, NULL, &size );
1221 if (!ret && (disabled = HeapAlloc( GetProcessHeap(), 0, size )))
1222 ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, (BYTE *)disabled, &size );
1223 RegCloseKey( hkey );
1224 if (ret) *disabled = 0;
1227 if ((str = HeapAlloc( GetProcessHeap(), 0, strlen(extensions) + 2 )))
1229 p = str;
1230 for (;;)
1232 while (*extensions == ' ') extensions++;
1233 if (!*extensions) break;
1234 if (!(end = strchr( extensions, ' ' ))) end = extensions + strlen( extensions );
1235 memcpy( p, extensions, end - extensions );
1236 p[end - extensions] = 0;
1237 if (!has_extension( disabled, p ))
1239 TRACE("++ %s\n", p );
1240 p += end - extensions;
1241 *p++ = ' ';
1243 else TRACE("-- %s (disabled by config)\n", p );
1244 extensions = end;
1246 *p = 0;
1248 HeapFree( GetProcessHeap(), 0, disabled );
1249 return str;
1252 /***********************************************************************
1253 * glGetString (OPENGL32.@)
1255 const GLubyte * WINAPI wine_glGetString( GLenum name )
1257 const struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
1258 static const GLubyte *gl_extensions;
1260 /* this is for buggy nvidia driver, crashing if called from a different
1261 thread with no context */
1262 if(wglGetCurrentContext() == NULL)
1263 return NULL;
1265 if (name != GL_EXTENSIONS) return funcs->gl.p_glGetString(name);
1267 if (!gl_extensions)
1269 const char *orig_ext = (const char *)funcs->gl.p_glGetString(GL_EXTENSIONS);
1270 char *new_ext = build_gl_extensions( orig_ext );
1271 if (InterlockedCompareExchangePointer( (void **)&gl_extensions, new_ext, NULL ))
1272 HeapFree( GetProcessHeap(), 0, new_ext );
1274 return gl_extensions;
1277 /***********************************************************************
1278 * wglSwapBuffers (OPENGL32.@)
1280 BOOL WINAPI DECLSPEC_HOTPATCH wglSwapBuffers( HDC hdc )
1282 return GdiSwapBuffers(hdc);
1285 /**********************************************************************/
1287 static void process_detach(void)
1289 if (libglu_handle) wine_dlclose(libglu_handle, NULL, 0);
1292 /***********************************************************************
1293 * OpenGL initialisation routine
1295 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
1297 switch(reason)
1299 case DLL_PROCESS_ATTACH:
1300 opengl32_handle = hinst;
1301 DisableThreadLibraryCalls(hinst);
1302 NtCurrentTeb()->glTable = &null_opengl_funcs;
1303 break;
1304 case DLL_THREAD_ATTACH:
1305 NtCurrentTeb()->glTable = &null_opengl_funcs;
1306 break;
1307 case DLL_PROCESS_DETACH:
1308 process_detach();
1309 break;
1311 return TRUE;