quartz: COM cleanup for IAMFilterMiscFlags in VideoRenderer.
[wine/multimedia.git] / dlls / opengl32 / wgl.c
blob815dc4220479ca3afd985613ff821bdfc5f370b2
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/library.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(wgl);
47 WINE_DECLARE_DEBUG_CHANNEL(opengl);
49 static struct
51 /* internal WGL functions */
52 void (WINAPI *p_wglFinish)(void);
53 void (WINAPI *p_wglFlush)(void);
54 void (WINAPI *p_wglGetIntegerv)(GLenum pname, GLint* params);
55 } wine_wgl;
57 #ifdef SONAME_LIBGLU
58 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
59 MAKE_FUNCPTR(gluNewTess)
60 MAKE_FUNCPTR(gluDeleteTess)
61 MAKE_FUNCPTR(gluTessBeginContour)
62 MAKE_FUNCPTR(gluTessBeginPolygon)
63 MAKE_FUNCPTR(gluTessCallback)
64 MAKE_FUNCPTR(gluTessEndContour)
65 MAKE_FUNCPTR(gluTessEndPolygon)
66 MAKE_FUNCPTR(gluTessVertex)
67 #undef MAKE_FUNCPTR
68 #endif /* SONAME_LIBGLU */
70 static HMODULE opengl32_handle;
71 static void* libglu_handle = NULL;
73 const GLubyte * WINAPI wine_glGetString( GLenum name );
75 /* internal GDI functions */
76 extern INT WINAPI GdiDescribePixelFormat( HDC hdc, INT fmt, UINT size, PIXELFORMATDESCRIPTOR *pfd );
77 extern BOOL WINAPI GdiSetPixelFormat( HDC hdc, INT fmt, const PIXELFORMATDESCRIPTOR *pfd );
78 extern BOOL WINAPI GdiSwapBuffers( HDC hdc );
80 /* handle management */
82 #define MAX_WGL_HANDLES 1024
84 struct wgl_handle
86 UINT handle;
87 DWORD tid;
88 struct wgl_context * context;
89 const struct wgl_funcs *funcs;
92 static struct wgl_handle wgl_handles[MAX_WGL_HANDLES];
93 static struct wgl_handle *next_free;
94 static unsigned int handle_count;
96 static CRITICAL_SECTION wgl_section;
97 static CRITICAL_SECTION_DEBUG critsect_debug =
99 0, 0, &wgl_section,
100 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
101 0, 0, { (DWORD_PTR)(__FILE__ ": wgl_section") }
103 static CRITICAL_SECTION wgl_section = { &critsect_debug, -1, 0, 0, 0, 0 };
105 static inline const struct wgl_funcs *get_dc_funcs( HDC hdc )
107 return __wine_get_wgl_driver( hdc, WINE_GDI_DRIVER_VERSION );
110 static inline HGLRC next_handle( struct wgl_handle *ptr )
112 WORD generation = HIWORD( ptr->handle ) + 1;
113 if (!generation) generation++;
114 ptr->handle = MAKELONG( ptr - wgl_handles, generation );
115 return ULongToHandle( ptr->handle );
118 /* the current handle is assumed valid and doesn't need locking */
119 static inline struct wgl_handle *get_current_handle_ptr(void)
121 if (!NtCurrentTeb()->glCurrentRC) return NULL;
122 return &wgl_handles[LOWORD(NtCurrentTeb()->glCurrentRC)];
125 static struct wgl_handle *get_handle_ptr( HGLRC handle )
127 unsigned int index = LOWORD( handle );
129 EnterCriticalSection( &wgl_section );
130 if (index < handle_count && ULongToHandle(wgl_handles[index].handle) == handle)
131 return &wgl_handles[index];
133 LeaveCriticalSection( &wgl_section );
134 SetLastError( ERROR_INVALID_HANDLE );
135 return NULL;
138 static void release_handle_ptr( struct wgl_handle *ptr )
140 if (ptr) LeaveCriticalSection( &wgl_section );
143 static HGLRC alloc_handle( struct wgl_context *context, const struct wgl_funcs *funcs )
145 HGLRC handle = 0;
146 struct wgl_handle *ptr = NULL;
148 EnterCriticalSection( &wgl_section );
149 if ((ptr = next_free))
150 next_free = (struct wgl_handle *)next_free->context;
151 else if (handle_count < MAX_WGL_HANDLES)
152 ptr = &wgl_handles[handle_count++];
154 if (ptr)
156 ptr->context = context;
157 ptr->funcs = funcs;
158 handle = next_handle( ptr );
160 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
161 LeaveCriticalSection( &wgl_section );
162 return handle;
165 static void free_handle_ptr( struct wgl_handle *ptr )
167 ptr->handle &= ~0xffff;
168 ptr->context = (struct wgl_context *)next_free;
169 ptr->funcs = NULL;
170 next_free = ptr;
171 LeaveCriticalSection( &wgl_section );
174 /***********************************************************************
175 * wglSetPixelFormat(OPENGL32.@)
177 BOOL WINAPI wglSetPixelFormat( HDC hdc, INT iPixelFormat,
178 const PIXELFORMATDESCRIPTOR *ppfd)
180 return GdiSetPixelFormat(hdc, iPixelFormat, ppfd);
183 /***********************************************************************
184 * wglCopyContext (OPENGL32.@)
186 BOOL WINAPI wglCopyContext(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask)
188 struct wgl_handle *src, *dst;
189 BOOL ret = FALSE;
191 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
192 if ((dst = get_handle_ptr( hglrcDst )))
194 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
195 else ret = src->funcs->p_wglCopyContext( src->context, dst->context, mask );
197 release_handle_ptr( dst );
198 release_handle_ptr( src );
199 return ret;
202 /***********************************************************************
203 * wglDeleteContext (OPENGL32.@)
205 BOOL WINAPI wglDeleteContext(HGLRC hglrc)
207 struct wgl_handle *ptr = get_handle_ptr( hglrc );
209 if (!ptr) return FALSE;
211 if (ptr->tid && ptr->tid != GetCurrentThreadId())
213 SetLastError( ERROR_BUSY );
214 release_handle_ptr( ptr );
215 return FALSE;
217 if (hglrc == NtCurrentTeb()->glCurrentRC) wglMakeCurrent( 0, 0 );
218 ptr->funcs->p_wglDeleteContext( ptr->context );
219 free_handle_ptr( ptr );
220 return TRUE;
223 /***********************************************************************
224 * wglMakeCurrent (OPENGL32.@)
226 BOOL WINAPI wglMakeCurrent(HDC hdc, HGLRC hglrc)
228 BOOL ret = TRUE;
229 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
231 if (hglrc)
233 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
234 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
236 ret = ptr->funcs->p_wglMakeCurrent( hdc, ptr->context );
237 if (ret)
239 if (prev) prev->tid = 0;
240 ptr->tid = GetCurrentThreadId();
241 NtCurrentTeb()->glCurrentRC = hglrc;
244 else
246 SetLastError( ERROR_BUSY );
247 ret = FALSE;
249 release_handle_ptr( ptr );
251 else if (prev)
253 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
254 prev->tid = 0;
255 NtCurrentTeb()->glCurrentRC = 0;
257 else if (!hdc)
259 SetLastError( ERROR_INVALID_HANDLE );
260 ret = FALSE;
262 return ret;
265 /***********************************************************************
266 * wglCreateContextAttribsARB (wrapper for the extension function returned by the driver)
268 static HGLRC WINAPI wglCreateContextAttribsARB( HDC hdc, HGLRC share, const int *attribs )
270 HGLRC ret = 0;
271 struct wgl_context *context;
272 struct wgl_handle *share_ptr = NULL;
273 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
275 if (!funcs) return 0;
276 if (share && !(share_ptr = get_handle_ptr( share ))) return 0;
277 if ((context = funcs->p_wglCreateContextAttribsARB( hdc, share_ptr ? share_ptr->context : NULL,
278 attribs )))
280 ret = alloc_handle( context, funcs );
281 if (!ret) funcs->p_wglDeleteContext( context );
283 release_handle_ptr( share_ptr );
284 return ret;
288 /***********************************************************************
289 * wglMakeContextCurrentARB (wrapper for the extension function returned by the driver)
291 static BOOL WINAPI wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, HGLRC hglrc )
293 BOOL ret = TRUE;
294 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
296 if (hglrc)
298 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
299 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
301 ret = ptr->funcs->p_wglMakeContextCurrentARB( draw_hdc, read_hdc, ptr->context );
302 if (ret)
304 if (prev) prev->tid = 0;
305 ptr->tid = GetCurrentThreadId();
306 NtCurrentTeb()->glCurrentRC = hglrc;
309 else
311 SetLastError( ERROR_BUSY );
312 ret = FALSE;
314 release_handle_ptr( ptr );
316 else if (prev)
318 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
319 prev->tid = 0;
320 NtCurrentTeb()->glCurrentRC = 0;
322 return ret;
325 /***********************************************************************
326 * wglShareLists (OPENGL32.@)
328 BOOL WINAPI wglShareLists(HGLRC hglrcSrc, HGLRC hglrcDst)
330 BOOL ret = FALSE;
331 struct wgl_handle *src, *dst;
333 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
334 if ((dst = get_handle_ptr( hglrcDst )))
336 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
337 else ret = src->funcs->p_wglShareLists( src->context, dst->context );
339 release_handle_ptr( dst );
340 release_handle_ptr( src );
341 return ret;
344 /***********************************************************************
345 * wglGetCurrentDC (OPENGL32.@)
347 HDC WINAPI wglGetCurrentDC(void)
349 struct wgl_handle *context = get_current_handle_ptr();
350 if (!context) return 0;
351 return context->funcs->p_wglGetCurrentDC( context->context );
354 /***********************************************************************
355 * wglCreateContext (OPENGL32.@)
357 HGLRC WINAPI wglCreateContext(HDC hdc)
359 HGLRC ret = 0;
360 struct wgl_context *context;
361 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
363 if (!funcs) return 0;
364 if (!(context = funcs->p_wglCreateContext( hdc ))) return 0;
365 ret = alloc_handle( context, funcs );
366 if (!ret) funcs->p_wglDeleteContext( context );
367 return ret;
370 /***********************************************************************
371 * wglGetCurrentContext (OPENGL32.@)
373 HGLRC WINAPI wglGetCurrentContext(void)
375 return NtCurrentTeb()->glCurrentRC;
378 /***********************************************************************
379 * wglChoosePixelFormat (OPENGL32.@)
381 INT WINAPI wglChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd)
383 PIXELFORMATDESCRIPTOR format, best;
384 int i, count, best_format;
385 int bestDBuffer = -1, bestStereo = -1;
387 TRACE_(wgl)( "%p %p: size %u version %u flags %u type %u color %u %u,%u,%u,%u "
388 "accum %u depth %u stencil %u aux %u\n",
389 hdc, ppfd, ppfd->nSize, ppfd->nVersion, ppfd->dwFlags, ppfd->iPixelType,
390 ppfd->cColorBits, ppfd->cRedBits, ppfd->cGreenBits, ppfd->cBlueBits, ppfd->cAlphaBits,
391 ppfd->cAccumBits, ppfd->cDepthBits, ppfd->cStencilBits, ppfd->cAuxBuffers );
393 count = GdiDescribePixelFormat( hdc, 0, 0, NULL );
394 if (!count) return 0;
396 best_format = 0;
397 best.dwFlags = 0;
398 best.cAlphaBits = -1;
399 best.cColorBits = -1;
400 best.cDepthBits = -1;
401 best.cStencilBits = -1;
402 best.cAuxBuffers = -1;
404 for (i = 1; i <= count; i++)
406 if (!GdiDescribePixelFormat( hdc, i, sizeof(format), &format )) continue;
408 if (ppfd->iPixelType != format.iPixelType)
410 TRACE( "pixel type mismatch for iPixelFormat=%d\n", i );
411 continue;
414 /* only use bitmap capable for formats for bitmap rendering */
415 if( (ppfd->dwFlags & PFD_DRAW_TO_BITMAP) != (format.dwFlags & PFD_DRAW_TO_BITMAP))
417 TRACE( "PFD_DRAW_TO_BITMAP mismatch for iPixelFormat=%d\n", i );
418 continue;
421 /* The behavior of PDF_STEREO/PFD_STEREO_DONTCARE and PFD_DOUBLEBUFFER / PFD_DOUBLEBUFFER_DONTCARE
422 * is not very clear on MSDN. They specify that ChoosePixelFormat tries to match pixel formats
423 * with the flag (PFD_STEREO / PFD_DOUBLEBUFFERING) set. Otherwise it says that it tries to match
424 * formats without the given flag set.
425 * A test on Windows using a Radeon 9500pro on WinXP (the driver doesn't support Stereo)
426 * has indicated that a format without stereo is returned when stereo is unavailable.
427 * So in case PFD_STEREO is set, formats that support it should have priority above formats
428 * without. In case PFD_STEREO_DONTCARE is set, stereo is ignored.
430 * To summarize the following is most likely the correct behavior:
431 * stereo not set -> prefer non-stereo formats, but also accept stereo formats
432 * stereo set -> prefer stereo formats, but also accept non-stereo formats
433 * stereo don't care -> it doesn't matter whether we get stereo or not
435 * In Wine we will treat non-stereo the same way as don't care because it makes
436 * format selection even more complicated and second drivers with Stereo advertise
437 * each format twice anyway.
440 /* Doublebuffer, see the comments above */
441 if (!(ppfd->dwFlags & PFD_DOUBLEBUFFER_DONTCARE))
443 if (((ppfd->dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) &&
444 ((format.dwFlags & PFD_DOUBLEBUFFER) == (ppfd->dwFlags & PFD_DOUBLEBUFFER)))
445 goto found;
447 if (bestDBuffer != -1 && (format.dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) continue;
450 /* Stereo, see the comments above. */
451 if (!(ppfd->dwFlags & PFD_STEREO_DONTCARE))
453 if (((ppfd->dwFlags & PFD_STEREO) != bestStereo) &&
454 ((format.dwFlags & PFD_STEREO) == (ppfd->dwFlags & PFD_STEREO)))
455 goto found;
457 if (bestStereo != -1 && (format.dwFlags & PFD_STEREO) != bestStereo) continue;
460 /* Below we will do a number of checks to select the 'best' pixelformat.
461 * We assume the precedence cColorBits > cAlphaBits > cDepthBits > cStencilBits -> cAuxBuffers.
462 * The code works by trying to match the most important options as close as possible.
463 * When a reasonable format is found, we will try to match more options.
464 * It appears (see the opengl32 test) that Windows opengl drivers ignore options
465 * like cColorBits, cAlphaBits and friends if they are set to 0, so they are considered
466 * as DONTCARE. At least Serious Sam TSE relies on this behavior. */
468 if (ppfd->cColorBits)
470 if (((ppfd->cColorBits > best.cColorBits) && (format.cColorBits > best.cColorBits)) ||
471 ((format.cColorBits >= ppfd->cColorBits) && (format.cColorBits < best.cColorBits)))
472 goto found;
474 if (best.cColorBits != format.cColorBits) /* Do further checks if the format is compatible */
476 TRACE( "color mismatch for iPixelFormat=%d\n", i );
477 continue;
480 if (ppfd->cAlphaBits)
482 if (((ppfd->cAlphaBits > best.cAlphaBits) && (format.cAlphaBits > best.cAlphaBits)) ||
483 ((format.cAlphaBits >= ppfd->cAlphaBits) && (format.cAlphaBits < best.cAlphaBits)))
484 goto found;
486 if (best.cAlphaBits != format.cAlphaBits)
488 TRACE( "alpha mismatch for iPixelFormat=%d\n", i );
489 continue;
492 if (ppfd->cDepthBits)
494 if (((ppfd->cDepthBits > best.cDepthBits) && (format.cDepthBits > best.cDepthBits)) ||
495 ((format.cDepthBits >= ppfd->cDepthBits) && (format.cDepthBits < best.cDepthBits)))
496 goto found;
498 if (best.cDepthBits != format.cDepthBits)
500 TRACE( "depth mismatch for iPixelFormat=%d\n", i );
501 continue;
504 if (ppfd->cStencilBits)
506 if (((ppfd->cStencilBits > best.cStencilBits) && (format.cStencilBits > best.cStencilBits)) ||
507 ((format.cStencilBits >= ppfd->cStencilBits) && (format.cStencilBits < best.cStencilBits)))
508 goto found;
510 if (best.cStencilBits != format.cStencilBits)
512 TRACE( "stencil mismatch for iPixelFormat=%d\n", i );
513 continue;
516 if (ppfd->cAuxBuffers)
518 if (((ppfd->cAuxBuffers > best.cAuxBuffers) && (format.cAuxBuffers > best.cAuxBuffers)) ||
519 ((format.cAuxBuffers >= ppfd->cAuxBuffers) && (format.cAuxBuffers < best.cAuxBuffers)))
520 goto found;
522 if (best.cAuxBuffers != format.cAuxBuffers)
524 TRACE( "aux mismatch for iPixelFormat=%d\n", i );
525 continue;
528 continue;
530 found:
531 best_format = i;
532 best = format;
533 bestDBuffer = format.dwFlags & PFD_DOUBLEBUFFER;
534 bestStereo = format.dwFlags & PFD_STEREO;
537 TRACE( "returning %u\n", best_format );
538 return best_format;
541 /***********************************************************************
542 * wglDescribePixelFormat (OPENGL32.@)
544 INT WINAPI wglDescribePixelFormat(HDC hdc, INT iPixelFormat, UINT nBytes,
545 LPPIXELFORMATDESCRIPTOR ppfd)
547 return GdiDescribePixelFormat(hdc, iPixelFormat, nBytes, ppfd);
550 /***********************************************************************
551 * wglGetPixelFormat (OPENGL32.@)
553 INT WINAPI wglGetPixelFormat(HDC hdc)
555 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
556 if (!funcs) return 0;
557 return funcs->p_GetPixelFormat( hdc );
560 /***********************************************************************
561 * wglCreateLayerContext (OPENGL32.@)
563 HGLRC WINAPI wglCreateLayerContext(HDC hdc,
564 int iLayerPlane) {
565 TRACE("(%p,%d)\n", hdc, iLayerPlane);
567 if (iLayerPlane == 0) {
568 return wglCreateContext(hdc);
570 FIXME("no handler for layer %d\n", iLayerPlane);
572 return NULL;
575 /***********************************************************************
576 * wglDescribeLayerPlane (OPENGL32.@)
578 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
579 int iPixelFormat,
580 int iLayerPlane,
581 UINT nBytes,
582 LPLAYERPLANEDESCRIPTOR plpd) {
583 FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
585 return FALSE;
588 /***********************************************************************
589 * wglGetLayerPaletteEntries (OPENGL32.@)
591 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
592 int iLayerPlane,
593 int iStart,
594 int cEntries,
595 const COLORREF *pcr) {
596 FIXME("(): stub!\n");
598 return 0;
601 /* check if the extension is present in the list */
602 static BOOL has_extension( const char *list, const char *ext )
604 size_t len = strlen( ext );
606 while (list)
608 while (*list == ' ') list++;
609 if (!strncmp( list, ext, len ) && (!list[len] || list[len] == ' ')) return TRUE;
610 list = strchr( list, ' ' );
612 return FALSE;
615 static int compar(const void *elt_a, const void *elt_b) {
616 return strcmp(((const OpenGL_extension *) elt_a)->name,
617 ((const OpenGL_extension *) elt_b)->name);
620 /* Check if a GL extension is supported */
621 static BOOL is_extension_supported(const char* extension)
623 const char *gl_ext_string = (const char*)wine_glGetString(GL_EXTENSIONS);
625 TRACE("Checking for extension '%s'\n", extension);
627 if(!gl_ext_string) {
628 ERR("No OpenGL extensions found, check if your OpenGL setup is correct!\n");
629 return FALSE;
632 /* We use the GetProcAddress function from the display driver to retrieve function pointers
633 * for OpenGL and WGL extensions. In case of winex11.drv the OpenGL extension lookup is done
634 * using glXGetProcAddress. This function is quite unreliable in the sense that its specs don't
635 * require the function to return NULL when an extension isn't found. For this reason we check
636 * if the OpenGL extension required for the function we are looking up is supported. */
638 /* Check if the extension is part of the GL extension string to see if it is supported. */
639 if (has_extension(gl_ext_string, extension))
640 return TRUE;
642 /* In general an OpenGL function starts as an ARB/EXT extension and at some stage
643 * it becomes part of the core OpenGL library and can be reached without the ARB/EXT
644 * suffix as well. In the extension table, these functions contain GL_VERSION_major_minor.
645 * Check if we are searching for a core GL function */
646 if(strncmp(extension, "GL_VERSION_", 11) == 0)
648 const GLubyte *gl_version = glGetString(GL_VERSION);
649 const char *version = extension + 11; /* Move past 'GL_VERSION_' */
651 if(!gl_version) {
652 ERR("No OpenGL version found!\n");
653 return FALSE;
656 /* Compare the major/minor version numbers of the native OpenGL library and what is required by the function.
657 * The gl_version string is guaranteed to have at least a major/minor and sometimes it has a release number as well. */
658 if( (gl_version[0] >= version[0]) || ((gl_version[0] == version[0]) && (gl_version[2] >= version[2])) ) {
659 return TRUE;
661 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]);
664 return FALSE;
667 static const OpenGL_extension wgl_extensions[] =
669 { "wglCreateContextAttribsARB", "WGL_ARB_create_context", wglCreateContextAttribsARB },
670 { "wglMakeContextCurrentARB", "WGL_ARB_make_current_read", wglMakeContextCurrentARB },
673 /***********************************************************************
674 * wglGetProcAddress (OPENGL32.@)
676 PROC WINAPI wglGetProcAddress(LPCSTR lpszProc) {
677 void *local_func;
678 OpenGL_extension ext;
679 const OpenGL_extension *ext_ret;
680 struct wgl_handle *context = get_current_handle_ptr();
682 TRACE("(%s)\n", lpszProc);
684 if (lpszProc == NULL)
685 return NULL;
687 /* Without an active context opengl32 doesn't know to what
688 * driver it has to dispatch wglGetProcAddress.
690 if (!context)
692 WARN("No active WGL context found\n");
693 return NULL;
696 /* Search in the thunks to find the real name of the extension */
697 ext.name = lpszProc;
698 ext_ret = bsearch(&ext, extension_registry, extension_registry_size,
699 sizeof(OpenGL_extension), compar);
701 /* If nothing was found, we are looking for a WGL extension or an unknown GL extension. */
702 if (ext_ret == NULL) {
703 /* If the function name starts with a 'w', it is a WGL extension */
704 if(lpszProc[0] == 'w')
706 local_func = context->funcs->p_wglGetProcAddress( lpszProc );
707 if (local_func == (void *)1) /* special function that needs a wrapper */
709 ext_ret = bsearch( &ext, wgl_extensions, sizeof(wgl_extensions)/sizeof(wgl_extensions[0]),
710 sizeof(OpenGL_extension), compar );
711 if (ext_ret) return ext_ret->func;
713 FIXME( "wrapper missing for %s\n", lpszProc );
714 return NULL;
716 return local_func;
719 /* We are dealing with an unknown GL extension */
720 WARN("Extension '%s' not defined in opengl32.dll's function table!\n", lpszProc);
721 return NULL;
722 } else { /* We are looking for an OpenGL extension */
724 /* Check if the GL extension required by the function is available */
725 if(!is_extension_supported(ext_ret->extension)) {
726 WARN("Extension '%s' required by function '%s' not supported!\n", ext_ret->extension, lpszProc);
729 local_func = context->funcs->p_wglGetProcAddress( ext_ret->name );
731 /* After that, look at the extensions defined in the Linux OpenGL library */
732 if (local_func == NULL) {
733 char buf[256];
734 void *ret = NULL;
736 /* Remove the last 3 letters (EXT, ARB, ...).
738 I know that some extensions have more than 3 letters (MESA, NV,
739 INTEL, ...), but this is only a stop-gap measure to fix buggy
740 OpenGL drivers (moreover, it is only useful for old 1.0 apps
741 that query the glBindTextureEXT extension).
743 memcpy(buf, ext_ret->name, strlen(ext_ret->name) - 3);
744 buf[strlen(ext_ret->name) - 3] = '\0';
745 TRACE("Extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf);
747 ret = GetProcAddress(opengl32_handle, buf);
748 if (ret != NULL) {
749 TRACE("Found function in main OpenGL library (%p)!\n", ret);
750 } else {
751 WARN("Did not find function %s (%s) in your OpenGL library!\n", lpszProc, ext_ret->name);
754 return ret;
755 } else {
756 TRACE("returning function (%p)\n", ext_ret->func);
757 extension_funcs[ext_ret - extension_registry] = local_func;
759 return ext_ret->func;
764 /***********************************************************************
765 * wglRealizeLayerPalette (OPENGL32.@)
767 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
768 int iLayerPlane,
769 BOOL bRealize) {
770 FIXME("()\n");
772 return FALSE;
775 /***********************************************************************
776 * wglSetLayerPaletteEntries (OPENGL32.@)
778 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
779 int iLayerPlane,
780 int iStart,
781 int cEntries,
782 const COLORREF *pcr) {
783 FIXME("(): stub!\n");
785 return 0;
788 /***********************************************************************
789 * wglSwapLayerBuffers (OPENGL32.@)
791 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
792 UINT fuPlanes) {
793 TRACE_(opengl)("(%p, %08x)\n", hdc, fuPlanes);
795 if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
796 if (!GdiSwapBuffers(hdc)) return FALSE;
797 fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
800 if (fuPlanes) {
801 WARN("Following layers unhandled: %08x\n", fuPlanes);
804 return TRUE;
807 /***********************************************************************
808 * wglUseFontBitmaps_common
810 static BOOL wglUseFontBitmaps_common( HDC hdc, DWORD first, DWORD count, DWORD listBase, BOOL unicode )
812 GLYPHMETRICS gm;
813 unsigned int glyph, size = 0;
814 void *bitmap = NULL, *gl_bitmap = NULL;
815 int org_alignment;
816 BOOL ret = TRUE;
818 glGetIntegerv(GL_UNPACK_ALIGNMENT, &org_alignment);
819 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
821 for (glyph = first; glyph < first + count; glyph++) {
822 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
823 unsigned int needed_size, height, width, width_int;
825 if (unicode)
826 needed_size = GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
827 else
828 needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
830 TRACE("Glyph: %3d / List: %d size %d\n", glyph, listBase, needed_size);
831 if (needed_size == GDI_ERROR) {
832 ret = FALSE;
833 break;
836 if (needed_size > size) {
837 size = needed_size;
838 HeapFree(GetProcessHeap(), 0, bitmap);
839 HeapFree(GetProcessHeap(), 0, gl_bitmap);
840 bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
841 gl_bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
843 if (unicode)
844 ret = (GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
845 else
846 ret = (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
847 if (!ret) break;
849 if (TRACE_ON(wgl)) {
850 unsigned int bitmask;
851 unsigned char *bitmap_ = bitmap;
853 TRACE(" - bbox: %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
854 TRACE(" - origin: (%d, %d)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
855 TRACE(" - increment: %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
856 if (needed_size != 0) {
857 TRACE(" - bitmap:\n");
858 for (height = 0; height < gm.gmBlackBoxY; height++) {
859 TRACE(" ");
860 for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
861 if (bitmask == 0) {
862 bitmap_ += 1;
863 bitmask = 0x80;
865 if (*bitmap_ & bitmask)
866 TRACE("*");
867 else
868 TRACE(" ");
870 bitmap_ += (4 - ((UINT_PTR)bitmap_ & 0x03));
871 TRACE("\n");
876 /* In OpenGL, the bitmap is drawn from the bottom to the top... So we need to invert the
877 * glyph for it to be drawn properly.
879 if (needed_size != 0) {
880 width_int = (gm.gmBlackBoxX + 31) / 32;
881 for (height = 0; height < gm.gmBlackBoxY; height++) {
882 for (width = 0; width < width_int; width++) {
883 ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
884 ((int *) bitmap)[height * width_int + width];
889 glNewList(listBase++, GL_COMPILE);
890 if (needed_size != 0) {
891 glBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY,
892 0 - gm.gmptGlyphOrigin.x, (int) gm.gmBlackBoxY - gm.gmptGlyphOrigin.y,
893 gm.gmCellIncX, gm.gmCellIncY,
894 gl_bitmap);
895 } else {
896 /* This is the case of 'empty' glyphs like the space character */
897 glBitmap(0, 0, 0, 0, gm.gmCellIncX, gm.gmCellIncY, NULL);
899 glEndList();
902 glPixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
903 HeapFree(GetProcessHeap(), 0, bitmap);
904 HeapFree(GetProcessHeap(), 0, gl_bitmap);
905 return ret;
908 /***********************************************************************
909 * wglUseFontBitmapsA (OPENGL32.@)
911 BOOL WINAPI wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase)
913 return wglUseFontBitmaps_common( hdc, first, count, listBase, FALSE );
916 /***********************************************************************
917 * wglUseFontBitmapsW (OPENGL32.@)
919 BOOL WINAPI wglUseFontBitmapsW(HDC hdc, DWORD first, DWORD count, DWORD listBase)
921 return wglUseFontBitmaps_common( hdc, first, count, listBase, TRUE );
924 #ifdef SONAME_LIBGLU
926 static void *load_libglu(void)
928 static int already_loaded;
929 void *handle;
931 if (already_loaded) return libglu_handle;
932 already_loaded = 1;
934 TRACE("Trying to load GLU library: %s\n", SONAME_LIBGLU);
935 handle = wine_dlopen(SONAME_LIBGLU, RTLD_NOW, NULL, 0);
936 if (!handle)
938 WARN("Failed to load %s\n", SONAME_LIBGLU);
939 return NULL;
942 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(handle, #f, NULL, 0)) == NULL) goto sym_not_found;
943 LOAD_FUNCPTR(gluNewTess)
944 LOAD_FUNCPTR(gluDeleteTess)
945 LOAD_FUNCPTR(gluTessBeginContour)
946 LOAD_FUNCPTR(gluTessBeginPolygon)
947 LOAD_FUNCPTR(gluTessCallback)
948 LOAD_FUNCPTR(gluTessEndContour)
949 LOAD_FUNCPTR(gluTessEndPolygon)
950 LOAD_FUNCPTR(gluTessVertex)
951 #undef LOAD_FUNCPTR
952 libglu_handle = handle;
953 return handle;
955 sym_not_found:
956 WARN("Unable to load function ptrs from libGLU\n");
957 /* Close the library as we won't use it */
958 wine_dlclose(handle, NULL, 0);
959 return NULL;
962 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
964 vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
965 vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
966 vertex[2] = 0.0;
969 static void tess_callback_vertex(GLvoid *vertex)
971 GLdouble *dbl = vertex;
972 TRACE("%f, %f, %f\n", dbl[0], dbl[1], dbl[2]);
973 glVertex3dv(vertex);
976 static void tess_callback_begin(GLenum which)
978 TRACE("%d\n", which);
979 glBegin(which);
982 static void tess_callback_end(void)
984 TRACE("\n");
985 glEnd();
988 /***********************************************************************
989 * wglUseFontOutlines_common
991 static BOOL wglUseFontOutlines_common(HDC hdc,
992 DWORD first,
993 DWORD count,
994 DWORD listBase,
995 FLOAT deviation,
996 FLOAT extrusion,
997 int format,
998 LPGLYPHMETRICSFLOAT lpgmf,
999 BOOL unicode)
1001 UINT glyph;
1002 const MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
1003 GLUtesselator *tess;
1004 LOGFONTW lf;
1005 HFONT old_font, unscaled_font;
1006 UINT em_size = 1024;
1007 RECT rc;
1009 TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
1010 listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
1012 if (!load_libglu())
1014 ERR("libGLU is required for this function but isn't loaded\n");
1015 return FALSE;
1018 tess = pgluNewTess();
1019 if(!tess) return FALSE;
1020 pgluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tess_callback_vertex);
1021 pgluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tess_callback_begin);
1022 pgluTessCallback(tess, GLU_TESS_END, tess_callback_end);
1024 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
1025 rc.left = rc.right = rc.bottom = 0;
1026 rc.top = em_size;
1027 DPtoLP(hdc, (POINT*)&rc, 2);
1028 lf.lfHeight = -abs(rc.top - rc.bottom);
1029 lf.lfOrientation = lf.lfEscapement = 0;
1030 unscaled_font = CreateFontIndirectW(&lf);
1031 old_font = SelectObject(hdc, unscaled_font);
1033 for (glyph = first; glyph < first + count; glyph++)
1035 DWORD needed;
1036 GLYPHMETRICS gm;
1037 BYTE *buf;
1038 TTPOLYGONHEADER *pph;
1039 TTPOLYCURVE *ppc;
1040 GLdouble *vertices;
1042 if(unicode)
1043 needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1044 else
1045 needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1047 if(needed == GDI_ERROR)
1048 goto error;
1050 buf = HeapAlloc(GetProcessHeap(), 0, needed);
1051 vertices = HeapAlloc(GetProcessHeap(), 0, needed / sizeof(POINTFX) * 3 * sizeof(GLdouble));
1053 if(unicode)
1054 GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1055 else
1056 GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1058 TRACE("glyph %d\n", glyph);
1060 if(lpgmf)
1062 lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
1063 lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
1064 lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
1065 lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
1066 lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
1067 lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
1069 TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
1070 lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
1071 lpgmf++;
1074 glNewList(listBase++, GL_COMPILE);
1075 pgluTessBeginPolygon(tess, NULL);
1077 pph = (TTPOLYGONHEADER*)buf;
1078 while((BYTE*)pph < buf + needed)
1080 TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
1082 pgluTessBeginContour(tess);
1084 fixed_to_double(pph->pfxStart, em_size, vertices);
1085 pgluTessVertex(tess, vertices, vertices);
1086 vertices += 3;
1088 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
1089 while((char*)ppc < (char*)pph + pph->cb)
1091 int i;
1093 switch(ppc->wType) {
1094 case TT_PRIM_LINE:
1095 for(i = 0; i < ppc->cpfx; i++)
1097 TRACE("\t\tline to %d, %d\n", ppc->apfx[i].x.value, ppc->apfx[i].y.value);
1098 fixed_to_double(ppc->apfx[i], em_size, vertices);
1099 pgluTessVertex(tess, vertices, vertices);
1100 vertices += 3;
1102 break;
1104 case TT_PRIM_QSPLINE:
1105 for(i = 0; i < ppc->cpfx/2; i++)
1107 /* FIXME: just connecting the control points for now */
1108 TRACE("\t\tcurve %d,%d %d,%d\n",
1109 ppc->apfx[i * 2].x.value, ppc->apfx[i * 3].y.value,
1110 ppc->apfx[i * 2 + 1].x.value, ppc->apfx[i * 3 + 1].y.value);
1111 fixed_to_double(ppc->apfx[i * 2], em_size, vertices);
1112 pgluTessVertex(tess, vertices, vertices);
1113 vertices += 3;
1114 fixed_to_double(ppc->apfx[i * 2 + 1], em_size, vertices);
1115 pgluTessVertex(tess, vertices, vertices);
1116 vertices += 3;
1118 break;
1119 default:
1120 ERR("\t\tcurve type = %d\n", ppc->wType);
1121 pgluTessEndContour(tess);
1122 goto error_in_list;
1125 ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
1126 (ppc->cpfx - 1) * sizeof(POINTFX));
1128 pgluTessEndContour(tess);
1129 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
1132 error_in_list:
1133 pgluTessEndPolygon(tess);
1134 glTranslated((GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0);
1135 glEndList();
1136 HeapFree(GetProcessHeap(), 0, buf);
1137 HeapFree(GetProcessHeap(), 0, vertices);
1140 error:
1141 DeleteObject(SelectObject(hdc, old_font));
1142 pgluDeleteTess(tess);
1143 return TRUE;
1147 #else /* SONAME_LIBGLU */
1149 static BOOL wglUseFontOutlines_common(HDC hdc,
1150 DWORD first,
1151 DWORD count,
1152 DWORD listBase,
1153 FLOAT deviation,
1154 FLOAT extrusion,
1155 int format,
1156 LPGLYPHMETRICSFLOAT lpgmf,
1157 BOOL unicode)
1159 FIXME("Unable to compile in wglUseFontOutlines support without GL/glu.h\n");
1160 return FALSE;
1163 #endif /* SONAME_LIBGLU */
1165 /***********************************************************************
1166 * wglUseFontOutlinesA (OPENGL32.@)
1168 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
1169 DWORD first,
1170 DWORD count,
1171 DWORD listBase,
1172 FLOAT deviation,
1173 FLOAT extrusion,
1174 int format,
1175 LPGLYPHMETRICSFLOAT lpgmf)
1177 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
1180 /***********************************************************************
1181 * wglUseFontOutlinesW (OPENGL32.@)
1183 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
1184 DWORD first,
1185 DWORD count,
1186 DWORD listBase,
1187 FLOAT deviation,
1188 FLOAT extrusion,
1189 int format,
1190 LPGLYPHMETRICSFLOAT lpgmf)
1192 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
1195 /***********************************************************************
1196 * glDebugEntry (OPENGL32.@)
1198 GLint WINAPI wine_glDebugEntry( GLint unknown1, GLint unknown2 )
1200 return 0;
1203 /***********************************************************************
1204 * glFinish (OPENGL32.@)
1206 void WINAPI wine_glFinish( void )
1208 TRACE("()\n");
1209 wine_wgl.p_wglFinish();
1212 /***********************************************************************
1213 * glFlush (OPENGL32.@)
1215 void WINAPI wine_glFlush( void )
1217 TRACE("()\n");
1218 wine_wgl.p_wglFlush();
1221 /* build the extension string by filtering out the disabled extensions */
1222 static char *build_gl_extensions( const char *extensions )
1224 char *p, *str, *disabled = NULL;
1225 const char *end;
1226 HKEY hkey;
1228 TRACE( "GL_EXTENSIONS:\n" );
1230 if (!extensions) extensions = "";
1232 /* @@ Wine registry key: HKCU\Software\Wine\OpenGL */
1233 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\OpenGL", &hkey ))
1235 DWORD size, ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, NULL, &size );
1236 if (!ret && (disabled = HeapAlloc( GetProcessHeap(), 0, size )))
1237 ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, (BYTE *)disabled, &size );
1238 RegCloseKey( hkey );
1239 if (ret) *disabled = 0;
1242 if ((str = HeapAlloc( GetProcessHeap(), 0, strlen(extensions) + 2 )))
1244 p = str;
1245 for (;;)
1247 while (*extensions == ' ') extensions++;
1248 if (!*extensions) break;
1249 if (!(end = strchr( extensions, ' ' ))) end = extensions + strlen( extensions );
1250 memcpy( p, extensions, end - extensions );
1251 p[end - extensions] = 0;
1252 if (!has_extension( disabled, p ))
1254 TRACE("++ %s\n", p );
1255 p += end - extensions;
1256 *p++ = ' ';
1258 else TRACE("-- %s (disabled by config)\n", p );
1259 extensions = end;
1261 *p = 0;
1263 HeapFree( GetProcessHeap(), 0, disabled );
1264 return str;
1267 /***********************************************************************
1268 * glGetString (OPENGL32.@)
1270 const GLubyte * WINAPI wine_glGetString( GLenum name )
1272 static const GLubyte *gl_extensions;
1274 /* this is for buggy nvidia driver, crashing if called from a different
1275 thread with no context */
1276 if(wglGetCurrentContext() == NULL)
1277 return NULL;
1279 if (name != GL_EXTENSIONS) return glGetString(name);
1281 if (!gl_extensions)
1283 const char *orig_ext = (const char *)glGetString(GL_EXTENSIONS);
1284 char *new_ext = build_gl_extensions( orig_ext );
1285 if (InterlockedCompareExchangePointer( (void **)&gl_extensions, new_ext, NULL ))
1286 HeapFree( GetProcessHeap(), 0, new_ext );
1288 return gl_extensions;
1291 /***********************************************************************
1292 * glGetIntegerv (OPENGL32.@)
1294 void WINAPI wine_glGetIntegerv( GLenum pname, GLint* params )
1296 wine_wgl.p_wglGetIntegerv(pname, params);
1299 /***********************************************************************
1300 * wglSwapBuffers (OPENGL32.@)
1302 BOOL WINAPI DECLSPEC_HOTPATCH wglSwapBuffers( HDC hdc )
1304 return GdiSwapBuffers(hdc);
1307 /* This is for brain-dead applications that use OpenGL functions before even
1308 creating a rendering context.... */
1309 static BOOL process_attach(void)
1311 HDC hdc = GetDC( 0 );
1312 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
1314 ReleaseDC( 0, hdc );
1316 /* internal WGL functions */
1317 wine_wgl.p_wglFinish = (void *)funcs->p_wglGetProcAddress("wglFinish");
1318 wine_wgl.p_wglFlush = (void *)funcs->p_wglGetProcAddress("wglFlush");
1319 wine_wgl.p_wglGetIntegerv = (void *)funcs->p_wglGetProcAddress("wglGetIntegerv");
1320 return TRUE;
1324 /**********************************************************************/
1326 static void process_detach(void)
1328 if (libglu_handle) wine_dlclose(libglu_handle, NULL, 0);
1331 /***********************************************************************
1332 * OpenGL initialisation routine
1334 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
1336 switch(reason)
1338 case DLL_PROCESS_ATTACH:
1339 opengl32_handle = hinst;
1340 DisableThreadLibraryCalls(hinst);
1341 return process_attach();
1342 case DLL_PROCESS_DETACH:
1343 process_detach();
1344 break;
1346 return TRUE;