opengl32: Call OpenGL extension functions through the TEB function table.
[wine/multimedia.git] / dlls / opengl32 / wgl.c
blob8acc173c2dedffb2c9fa6c8bf88b0de06609116f
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 static struct
52 /* internal WGL functions */
53 void (WINAPI *p_wglFinish)(void);
54 void (WINAPI *p_wglFlush)(void);
55 void (WINAPI *p_wglGetIntegerv)(GLenum pname, GLint* params);
56 } wine_wgl;
58 #ifdef SONAME_LIBGLU
59 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
60 MAKE_FUNCPTR(gluNewTess)
61 MAKE_FUNCPTR(gluDeleteTess)
62 MAKE_FUNCPTR(gluTessBeginContour)
63 MAKE_FUNCPTR(gluTessBeginPolygon)
64 MAKE_FUNCPTR(gluTessCallback)
65 MAKE_FUNCPTR(gluTessEndContour)
66 MAKE_FUNCPTR(gluTessEndPolygon)
67 MAKE_FUNCPTR(gluTessVertex)
68 #undef MAKE_FUNCPTR
69 #endif /* SONAME_LIBGLU */
71 static HMODULE opengl32_handle;
72 static void* libglu_handle = NULL;
74 extern struct opengl_funcs null_opengl_funcs;
76 const GLubyte * WINAPI wine_glGetString( GLenum name );
78 /* internal GDI functions */
79 extern INT WINAPI GdiDescribePixelFormat( HDC hdc, INT fmt, UINT size, PIXELFORMATDESCRIPTOR *pfd );
80 extern BOOL WINAPI GdiSetPixelFormat( HDC hdc, INT fmt, const PIXELFORMATDESCRIPTOR *pfd );
81 extern BOOL WINAPI GdiSwapBuffers( HDC hdc );
83 /* handle management */
85 #define MAX_WGL_HANDLES 1024
87 struct wgl_handle
89 UINT handle;
90 DWORD tid;
91 struct wgl_context * context;
92 const struct wgl_funcs *funcs;
95 static struct wgl_handle wgl_handles[MAX_WGL_HANDLES];
96 static struct wgl_handle *next_free;
97 static unsigned int handle_count;
99 static CRITICAL_SECTION wgl_section;
100 static CRITICAL_SECTION_DEBUG critsect_debug =
102 0, 0, &wgl_section,
103 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
104 0, 0, { (DWORD_PTR)(__FILE__ ": wgl_section") }
106 static CRITICAL_SECTION wgl_section = { &critsect_debug, -1, 0, 0, 0, 0 };
108 static inline const struct wgl_funcs *get_dc_funcs( HDC hdc )
110 return __wine_get_wgl_driver( hdc, WINE_GDI_DRIVER_VERSION );
113 static inline HGLRC next_handle( struct wgl_handle *ptr )
115 WORD generation = HIWORD( ptr->handle ) + 1;
116 if (!generation) generation++;
117 ptr->handle = MAKELONG( ptr - wgl_handles, generation );
118 return ULongToHandle( ptr->handle );
121 /* the current handle is assumed valid and doesn't need locking */
122 static inline struct wgl_handle *get_current_handle_ptr(void)
124 if (!NtCurrentTeb()->glCurrentRC) return NULL;
125 return &wgl_handles[LOWORD(NtCurrentTeb()->glCurrentRC)];
128 static struct wgl_handle *get_handle_ptr( HGLRC handle )
130 unsigned int index = LOWORD( handle );
132 EnterCriticalSection( &wgl_section );
133 if (index < handle_count && ULongToHandle(wgl_handles[index].handle) == handle)
134 return &wgl_handles[index];
136 LeaveCriticalSection( &wgl_section );
137 SetLastError( ERROR_INVALID_HANDLE );
138 return NULL;
141 static void release_handle_ptr( struct wgl_handle *ptr )
143 if (ptr) LeaveCriticalSection( &wgl_section );
146 static HGLRC alloc_handle( struct wgl_context *context, const struct wgl_funcs *funcs )
148 HGLRC handle = 0;
149 struct wgl_handle *ptr = NULL;
151 EnterCriticalSection( &wgl_section );
152 if ((ptr = next_free))
153 next_free = (struct wgl_handle *)next_free->context;
154 else if (handle_count < MAX_WGL_HANDLES)
155 ptr = &wgl_handles[handle_count++];
157 if (ptr)
159 ptr->context = context;
160 ptr->funcs = funcs;
161 handle = next_handle( ptr );
163 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
164 LeaveCriticalSection( &wgl_section );
165 return handle;
168 static void free_handle_ptr( struct wgl_handle *ptr )
170 ptr->handle |= 0xffff;
171 ptr->context = (struct wgl_context *)next_free;
172 ptr->funcs = NULL;
173 next_free = ptr;
174 LeaveCriticalSection( &wgl_section );
177 /***********************************************************************
178 * wglSetPixelFormat(OPENGL32.@)
180 BOOL WINAPI wglSetPixelFormat( HDC hdc, INT iPixelFormat,
181 const PIXELFORMATDESCRIPTOR *ppfd)
183 return GdiSetPixelFormat(hdc, iPixelFormat, ppfd);
186 /***********************************************************************
187 * wglCopyContext (OPENGL32.@)
189 BOOL WINAPI wglCopyContext(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask)
191 struct wgl_handle *src, *dst;
192 BOOL ret = FALSE;
194 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
195 if ((dst = get_handle_ptr( hglrcDst )))
197 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
198 else ret = src->funcs->p_wglCopyContext( src->context, dst->context, mask );
200 release_handle_ptr( dst );
201 release_handle_ptr( src );
202 return ret;
205 /***********************************************************************
206 * wglDeleteContext (OPENGL32.@)
208 BOOL WINAPI wglDeleteContext(HGLRC hglrc)
210 struct wgl_handle *ptr = get_handle_ptr( hglrc );
212 if (!ptr) return FALSE;
214 if (ptr->tid && ptr->tid != GetCurrentThreadId())
216 SetLastError( ERROR_BUSY );
217 release_handle_ptr( ptr );
218 return FALSE;
220 if (hglrc == NtCurrentTeb()->glCurrentRC) wglMakeCurrent( 0, 0 );
221 ptr->funcs->p_wglDeleteContext( ptr->context );
222 free_handle_ptr( ptr );
223 return TRUE;
226 /***********************************************************************
227 * wglMakeCurrent (OPENGL32.@)
229 BOOL WINAPI wglMakeCurrent(HDC hdc, HGLRC hglrc)
231 BOOL ret = TRUE;
232 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
234 if (hglrc)
236 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
237 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
239 ret = ptr->funcs->p_wglMakeCurrent( hdc, ptr->context );
240 if (ret)
242 if (prev) prev->tid = 0;
243 ptr->tid = GetCurrentThreadId();
244 NtCurrentTeb()->glCurrentRC = hglrc;
247 else
249 SetLastError( ERROR_BUSY );
250 ret = FALSE;
252 release_handle_ptr( ptr );
254 else if (prev)
256 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
257 prev->tid = 0;
258 NtCurrentTeb()->glCurrentRC = 0;
259 NtCurrentTeb()->glTable = &null_opengl_funcs;
261 else if (!hdc)
263 SetLastError( ERROR_INVALID_HANDLE );
264 ret = FALSE;
266 return ret;
269 /***********************************************************************
270 * wglCreateContextAttribsARB (wrapper for the extension function returned by the driver)
272 static HGLRC WINAPI wglCreateContextAttribsARB( HDC hdc, HGLRC share, const int *attribs )
274 HGLRC ret = 0;
275 struct wgl_context *context;
276 struct wgl_handle *share_ptr = NULL;
277 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
279 if (!funcs) return 0;
280 if (share && !(share_ptr = get_handle_ptr( share ))) return 0;
281 if ((context = funcs->p_wglCreateContextAttribsARB( hdc, share_ptr ? share_ptr->context : NULL,
282 attribs )))
284 ret = alloc_handle( context, funcs );
285 if (!ret) funcs->p_wglDeleteContext( context );
287 release_handle_ptr( share_ptr );
288 return ret;
292 /***********************************************************************
293 * wglMakeContextCurrentARB (wrapper for the extension function returned by the driver)
295 static BOOL WINAPI wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, HGLRC hglrc )
297 BOOL ret = TRUE;
298 struct wgl_handle *ptr, *prev = get_current_handle_ptr();
300 if (hglrc)
302 if (!(ptr = get_handle_ptr( hglrc ))) return FALSE;
303 if (!ptr->tid || ptr->tid == GetCurrentThreadId())
305 ret = ptr->funcs->p_wglMakeContextCurrentARB( draw_hdc, read_hdc, ptr->context );
306 if (ret)
308 if (prev) prev->tid = 0;
309 ptr->tid = GetCurrentThreadId();
310 NtCurrentTeb()->glCurrentRC = hglrc;
313 else
315 SetLastError( ERROR_BUSY );
316 ret = FALSE;
318 release_handle_ptr( ptr );
320 else if (prev)
322 if (!prev->funcs->p_wglMakeCurrent( 0, NULL )) return FALSE;
323 prev->tid = 0;
324 NtCurrentTeb()->glCurrentRC = 0;
325 NtCurrentTeb()->glTable = &null_opengl_funcs;
327 return ret;
330 /***********************************************************************
331 * wglShareLists (OPENGL32.@)
333 BOOL WINAPI wglShareLists(HGLRC hglrcSrc, HGLRC hglrcDst)
335 BOOL ret = FALSE;
336 struct wgl_handle *src, *dst;
338 if (!(src = get_handle_ptr( hglrcSrc ))) return FALSE;
339 if ((dst = get_handle_ptr( hglrcDst )))
341 if (src->funcs != dst->funcs) SetLastError( ERROR_INVALID_HANDLE );
342 else ret = src->funcs->p_wglShareLists( src->context, dst->context );
344 release_handle_ptr( dst );
345 release_handle_ptr( src );
346 return ret;
349 /***********************************************************************
350 * wglGetCurrentDC (OPENGL32.@)
352 HDC WINAPI wglGetCurrentDC(void)
354 struct wgl_handle *context = get_current_handle_ptr();
355 if (!context) return 0;
356 return context->funcs->p_wglGetCurrentDC( context->context );
359 /***********************************************************************
360 * wglCreateContext (OPENGL32.@)
362 HGLRC WINAPI wglCreateContext(HDC hdc)
364 HGLRC ret = 0;
365 struct wgl_context *context;
366 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
368 if (!funcs) return 0;
369 if (!(context = funcs->p_wglCreateContext( hdc ))) return 0;
370 ret = alloc_handle( context, funcs );
371 if (!ret) funcs->p_wglDeleteContext( context );
372 return ret;
375 /***********************************************************************
376 * wglGetCurrentContext (OPENGL32.@)
378 HGLRC WINAPI wglGetCurrentContext(void)
380 return NtCurrentTeb()->glCurrentRC;
383 /***********************************************************************
384 * wglChoosePixelFormat (OPENGL32.@)
386 INT WINAPI wglChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd)
388 PIXELFORMATDESCRIPTOR format, best;
389 int i, count, best_format;
390 int bestDBuffer = -1, bestStereo = -1;
392 TRACE_(wgl)( "%p %p: size %u version %u flags %u type %u color %u %u,%u,%u,%u "
393 "accum %u depth %u stencil %u aux %u\n",
394 hdc, ppfd, ppfd->nSize, ppfd->nVersion, ppfd->dwFlags, ppfd->iPixelType,
395 ppfd->cColorBits, ppfd->cRedBits, ppfd->cGreenBits, ppfd->cBlueBits, ppfd->cAlphaBits,
396 ppfd->cAccumBits, ppfd->cDepthBits, ppfd->cStencilBits, ppfd->cAuxBuffers );
398 count = GdiDescribePixelFormat( hdc, 0, 0, NULL );
399 if (!count) return 0;
401 best_format = 0;
402 best.dwFlags = 0;
403 best.cAlphaBits = -1;
404 best.cColorBits = -1;
405 best.cDepthBits = -1;
406 best.cStencilBits = -1;
407 best.cAuxBuffers = -1;
409 for (i = 1; i <= count; i++)
411 if (!GdiDescribePixelFormat( hdc, i, sizeof(format), &format )) continue;
413 if (ppfd->iPixelType != format.iPixelType)
415 TRACE( "pixel type mismatch for iPixelFormat=%d\n", i );
416 continue;
419 /* only use bitmap capable for formats for bitmap rendering */
420 if( (ppfd->dwFlags & PFD_DRAW_TO_BITMAP) != (format.dwFlags & PFD_DRAW_TO_BITMAP))
422 TRACE( "PFD_DRAW_TO_BITMAP mismatch for iPixelFormat=%d\n", i );
423 continue;
426 /* The behavior of PDF_STEREO/PFD_STEREO_DONTCARE and PFD_DOUBLEBUFFER / PFD_DOUBLEBUFFER_DONTCARE
427 * is not very clear on MSDN. They specify that ChoosePixelFormat tries to match pixel formats
428 * with the flag (PFD_STEREO / PFD_DOUBLEBUFFERING) set. Otherwise it says that it tries to match
429 * formats without the given flag set.
430 * A test on Windows using a Radeon 9500pro on WinXP (the driver doesn't support Stereo)
431 * has indicated that a format without stereo is returned when stereo is unavailable.
432 * So in case PFD_STEREO is set, formats that support it should have priority above formats
433 * without. In case PFD_STEREO_DONTCARE is set, stereo is ignored.
435 * To summarize the following is most likely the correct behavior:
436 * stereo not set -> prefer non-stereo formats, but also accept stereo formats
437 * stereo set -> prefer stereo formats, but also accept non-stereo formats
438 * stereo don't care -> it doesn't matter whether we get stereo or not
440 * In Wine we will treat non-stereo the same way as don't care because it makes
441 * format selection even more complicated and second drivers with Stereo advertise
442 * each format twice anyway.
445 /* Doublebuffer, see the comments above */
446 if (!(ppfd->dwFlags & PFD_DOUBLEBUFFER_DONTCARE))
448 if (((ppfd->dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) &&
449 ((format.dwFlags & PFD_DOUBLEBUFFER) == (ppfd->dwFlags & PFD_DOUBLEBUFFER)))
450 goto found;
452 if (bestDBuffer != -1 && (format.dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) continue;
455 /* Stereo, see the comments above. */
456 if (!(ppfd->dwFlags & PFD_STEREO_DONTCARE))
458 if (((ppfd->dwFlags & PFD_STEREO) != bestStereo) &&
459 ((format.dwFlags & PFD_STEREO) == (ppfd->dwFlags & PFD_STEREO)))
460 goto found;
462 if (bestStereo != -1 && (format.dwFlags & PFD_STEREO) != bestStereo) continue;
465 /* Below we will do a number of checks to select the 'best' pixelformat.
466 * We assume the precedence cColorBits > cAlphaBits > cDepthBits > cStencilBits -> cAuxBuffers.
467 * The code works by trying to match the most important options as close as possible.
468 * When a reasonable format is found, we will try to match more options.
469 * It appears (see the opengl32 test) that Windows opengl drivers ignore options
470 * like cColorBits, cAlphaBits and friends if they are set to 0, so they are considered
471 * as DONTCARE. At least Serious Sam TSE relies on this behavior. */
473 if (ppfd->cColorBits)
475 if (((ppfd->cColorBits > best.cColorBits) && (format.cColorBits > best.cColorBits)) ||
476 ((format.cColorBits >= ppfd->cColorBits) && (format.cColorBits < best.cColorBits)))
477 goto found;
479 if (best.cColorBits != format.cColorBits) /* Do further checks if the format is compatible */
481 TRACE( "color mismatch for iPixelFormat=%d\n", i );
482 continue;
485 if (ppfd->cAlphaBits)
487 if (((ppfd->cAlphaBits > best.cAlphaBits) && (format.cAlphaBits > best.cAlphaBits)) ||
488 ((format.cAlphaBits >= ppfd->cAlphaBits) && (format.cAlphaBits < best.cAlphaBits)))
489 goto found;
491 if (best.cAlphaBits != format.cAlphaBits)
493 TRACE( "alpha mismatch for iPixelFormat=%d\n", i );
494 continue;
497 if (ppfd->cDepthBits)
499 if (((ppfd->cDepthBits > best.cDepthBits) && (format.cDepthBits > best.cDepthBits)) ||
500 ((format.cDepthBits >= ppfd->cDepthBits) && (format.cDepthBits < best.cDepthBits)))
501 goto found;
503 if (best.cDepthBits != format.cDepthBits)
505 TRACE( "depth mismatch for iPixelFormat=%d\n", i );
506 continue;
509 if (ppfd->cStencilBits)
511 if (((ppfd->cStencilBits > best.cStencilBits) && (format.cStencilBits > best.cStencilBits)) ||
512 ((format.cStencilBits >= ppfd->cStencilBits) && (format.cStencilBits < best.cStencilBits)))
513 goto found;
515 if (best.cStencilBits != format.cStencilBits)
517 TRACE( "stencil mismatch for iPixelFormat=%d\n", i );
518 continue;
521 if (ppfd->cAuxBuffers)
523 if (((ppfd->cAuxBuffers > best.cAuxBuffers) && (format.cAuxBuffers > best.cAuxBuffers)) ||
524 ((format.cAuxBuffers >= ppfd->cAuxBuffers) && (format.cAuxBuffers < best.cAuxBuffers)))
525 goto found;
527 if (best.cAuxBuffers != format.cAuxBuffers)
529 TRACE( "aux mismatch for iPixelFormat=%d\n", i );
530 continue;
533 continue;
535 found:
536 best_format = i;
537 best = format;
538 bestDBuffer = format.dwFlags & PFD_DOUBLEBUFFER;
539 bestStereo = format.dwFlags & PFD_STEREO;
542 TRACE( "returning %u\n", best_format );
543 return best_format;
546 /***********************************************************************
547 * wglDescribePixelFormat (OPENGL32.@)
549 INT WINAPI wglDescribePixelFormat(HDC hdc, INT iPixelFormat, UINT nBytes,
550 LPPIXELFORMATDESCRIPTOR ppfd)
552 return GdiDescribePixelFormat(hdc, iPixelFormat, nBytes, ppfd);
555 /***********************************************************************
556 * wglGetPixelFormat (OPENGL32.@)
558 INT WINAPI wglGetPixelFormat(HDC hdc)
560 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
561 if (!funcs) return 0;
562 return funcs->p_GetPixelFormat( hdc );
565 /***********************************************************************
566 * wglCreateLayerContext (OPENGL32.@)
568 HGLRC WINAPI wglCreateLayerContext(HDC hdc,
569 int iLayerPlane) {
570 TRACE("(%p,%d)\n", hdc, iLayerPlane);
572 if (iLayerPlane == 0) {
573 return wglCreateContext(hdc);
575 FIXME("no handler for layer %d\n", iLayerPlane);
577 return NULL;
580 /***********************************************************************
581 * wglDescribeLayerPlane (OPENGL32.@)
583 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
584 int iPixelFormat,
585 int iLayerPlane,
586 UINT nBytes,
587 LPLAYERPLANEDESCRIPTOR plpd) {
588 FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
590 return FALSE;
593 /***********************************************************************
594 * wglGetLayerPaletteEntries (OPENGL32.@)
596 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
597 int iLayerPlane,
598 int iStart,
599 int cEntries,
600 const COLORREF *pcr) {
601 FIXME("(): stub!\n");
603 return 0;
606 /* check if the extension is present in the list */
607 static BOOL has_extension( const char *list, const char *ext )
609 size_t len = strlen( ext );
611 while (list)
613 while (*list == ' ') list++;
614 if (!strncmp( list, ext, len ) && (!list[len] || list[len] == ' ')) return TRUE;
615 list = strchr( list, ' ' );
617 return FALSE;
620 static int compar(const void *elt_a, const void *elt_b) {
621 return strcmp(((const OpenGL_extension *) elt_a)->name,
622 ((const OpenGL_extension *) elt_b)->name);
625 /* Check if a GL extension is supported */
626 static BOOL is_extension_supported(const char* extension)
628 const char *gl_ext_string = (const char*)wine_glGetString(GL_EXTENSIONS);
630 TRACE("Checking for extension '%s'\n", extension);
632 if(!gl_ext_string) {
633 ERR("No OpenGL extensions found, check if your OpenGL setup is correct!\n");
634 return FALSE;
637 /* We use the GetProcAddress function from the display driver to retrieve function pointers
638 * for OpenGL and WGL extensions. In case of winex11.drv the OpenGL extension lookup is done
639 * using glXGetProcAddress. This function is quite unreliable in the sense that its specs don't
640 * require the function to return NULL when an extension isn't found. For this reason we check
641 * if the OpenGL extension required for the function we are looking up is supported. */
643 /* Check if the extension is part of the GL extension string to see if it is supported. */
644 if (has_extension(gl_ext_string, extension))
645 return TRUE;
647 /* In general an OpenGL function starts as an ARB/EXT extension and at some stage
648 * it becomes part of the core OpenGL library and can be reached without the ARB/EXT
649 * suffix as well. In the extension table, these functions contain GL_VERSION_major_minor.
650 * Check if we are searching for a core GL function */
651 if(strncmp(extension, "GL_VERSION_", 11) == 0)
653 const GLubyte *gl_version = glGetString(GL_VERSION);
654 const char *version = extension + 11; /* Move past 'GL_VERSION_' */
656 if(!gl_version) {
657 ERR("No OpenGL version found!\n");
658 return FALSE;
661 /* Compare the major/minor version numbers of the native OpenGL library and what is required by the function.
662 * The gl_version string is guaranteed to have at least a major/minor and sometimes it has a release number as well. */
663 if( (gl_version[0] >= version[0]) || ((gl_version[0] == version[0]) && (gl_version[2] >= version[2])) ) {
664 return TRUE;
666 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]);
669 return FALSE;
672 static const OpenGL_extension wgl_extensions[] =
674 { "wglCreateContextAttribsARB", "WGL_ARB_create_context", wglCreateContextAttribsARB },
675 { "wglMakeContextCurrentARB", "WGL_ARB_make_current_read", wglMakeContextCurrentARB },
678 /***********************************************************************
679 * wglGetProcAddress (OPENGL32.@)
681 PROC WINAPI wglGetProcAddress(LPCSTR lpszProc) {
682 void *local_func;
683 OpenGL_extension ext;
684 const OpenGL_extension *ext_ret;
685 struct wgl_handle *context = get_current_handle_ptr();
687 TRACE("(%s)\n", lpszProc);
689 if (lpszProc == NULL)
690 return NULL;
692 /* Without an active context opengl32 doesn't know to what
693 * driver it has to dispatch wglGetProcAddress.
695 if (!context)
697 WARN("No active WGL context found\n");
698 return NULL;
701 /* Search in the thunks to find the real name of the extension */
702 ext.name = lpszProc;
703 ext_ret = bsearch(&ext, extension_registry, extension_registry_size,
704 sizeof(OpenGL_extension), compar);
706 /* If nothing was found, we are looking for a WGL extension or an unknown GL extension. */
707 if (ext_ret == NULL) {
708 /* If the function name starts with a 'w', it is a WGL extension */
709 if(lpszProc[0] == 'w')
711 local_func = context->funcs->p_wglGetProcAddress( lpszProc );
712 if (local_func == (void *)1) /* special function that needs a wrapper */
714 ext_ret = bsearch( &ext, wgl_extensions, sizeof(wgl_extensions)/sizeof(wgl_extensions[0]),
715 sizeof(OpenGL_extension), compar );
716 if (ext_ret) return ext_ret->func;
718 FIXME( "wrapper missing for %s\n", lpszProc );
719 return NULL;
721 return local_func;
724 /* We are dealing with an unknown GL extension */
725 WARN("Extension '%s' not defined in opengl32.dll's function table!\n", lpszProc);
726 return NULL;
727 } else { /* We are looking for an OpenGL extension */
729 /* Check if the GL extension required by the function is available */
730 if(!is_extension_supported(ext_ret->extension)) {
731 WARN("Extension '%s' required by function '%s' not supported!\n", ext_ret->extension, lpszProc);
734 local_func = context->funcs->p_wglGetProcAddress( ext_ret->name );
736 /* After that, look at the extensions defined in the Linux OpenGL library */
737 if (local_func == NULL) {
738 char buf[256];
739 void *ret = NULL;
741 /* Remove the last 3 letters (EXT, ARB, ...).
743 I know that some extensions have more than 3 letters (MESA, NV,
744 INTEL, ...), but this is only a stop-gap measure to fix buggy
745 OpenGL drivers (moreover, it is only useful for old 1.0 apps
746 that query the glBindTextureEXT extension).
748 memcpy(buf, ext_ret->name, strlen(ext_ret->name) - 3);
749 buf[strlen(ext_ret->name) - 3] = '\0';
750 TRACE("Extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf);
752 ret = GetProcAddress(opengl32_handle, buf);
753 if (ret != NULL) {
754 TRACE("Found function in main OpenGL library (%p)!\n", ret);
755 } else {
756 WARN("Did not find function %s (%s) in your OpenGL library!\n", lpszProc, ext_ret->name);
759 return ret;
760 } else {
761 struct opengl_funcs *funcs = NtCurrentTeb()->glTable;
762 *((void **)&funcs->ext + (ext_ret - extension_registry)) = local_func;
763 TRACE("returning function (%p)\n", ext_ret->func);
764 return ext_ret->func;
769 /***********************************************************************
770 * wglRealizeLayerPalette (OPENGL32.@)
772 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
773 int iLayerPlane,
774 BOOL bRealize) {
775 FIXME("()\n");
777 return FALSE;
780 /***********************************************************************
781 * wglSetLayerPaletteEntries (OPENGL32.@)
783 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
784 int iLayerPlane,
785 int iStart,
786 int cEntries,
787 const COLORREF *pcr) {
788 FIXME("(): stub!\n");
790 return 0;
793 /***********************************************************************
794 * wglSwapLayerBuffers (OPENGL32.@)
796 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
797 UINT fuPlanes) {
798 TRACE_(opengl)("(%p, %08x)\n", hdc, fuPlanes);
800 if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
801 if (!GdiSwapBuffers(hdc)) return FALSE;
802 fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
805 if (fuPlanes) {
806 WARN("Following layers unhandled: %08x\n", fuPlanes);
809 return TRUE;
812 /***********************************************************************
813 * wglUseFontBitmaps_common
815 static BOOL wglUseFontBitmaps_common( HDC hdc, DWORD first, DWORD count, DWORD listBase, BOOL unicode )
817 GLYPHMETRICS gm;
818 unsigned int glyph, size = 0;
819 void *bitmap = NULL, *gl_bitmap = NULL;
820 int org_alignment;
821 BOOL ret = TRUE;
823 glGetIntegerv(GL_UNPACK_ALIGNMENT, &org_alignment);
824 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
826 for (glyph = first; glyph < first + count; glyph++) {
827 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
828 unsigned int needed_size, height, width, width_int;
830 if (unicode)
831 needed_size = GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
832 else
833 needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
835 TRACE("Glyph: %3d / List: %d size %d\n", glyph, listBase, needed_size);
836 if (needed_size == GDI_ERROR) {
837 ret = FALSE;
838 break;
841 if (needed_size > size) {
842 size = needed_size;
843 HeapFree(GetProcessHeap(), 0, bitmap);
844 HeapFree(GetProcessHeap(), 0, gl_bitmap);
845 bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
846 gl_bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
848 if (unicode)
849 ret = (GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
850 else
851 ret = (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &identity) != GDI_ERROR);
852 if (!ret) break;
854 if (TRACE_ON(wgl)) {
855 unsigned int bitmask;
856 unsigned char *bitmap_ = bitmap;
858 TRACE(" - bbox: %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
859 TRACE(" - origin: (%d, %d)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
860 TRACE(" - increment: %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
861 if (needed_size != 0) {
862 TRACE(" - bitmap:\n");
863 for (height = 0; height < gm.gmBlackBoxY; height++) {
864 TRACE(" ");
865 for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
866 if (bitmask == 0) {
867 bitmap_ += 1;
868 bitmask = 0x80;
870 if (*bitmap_ & bitmask)
871 TRACE("*");
872 else
873 TRACE(" ");
875 bitmap_ += (4 - ((UINT_PTR)bitmap_ & 0x03));
876 TRACE("\n");
881 /* In OpenGL, the bitmap is drawn from the bottom to the top... So we need to invert the
882 * glyph for it to be drawn properly.
884 if (needed_size != 0) {
885 width_int = (gm.gmBlackBoxX + 31) / 32;
886 for (height = 0; height < gm.gmBlackBoxY; height++) {
887 for (width = 0; width < width_int; width++) {
888 ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
889 ((int *) bitmap)[height * width_int + width];
894 glNewList(listBase++, GL_COMPILE);
895 if (needed_size != 0) {
896 glBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY,
897 0 - gm.gmptGlyphOrigin.x, (int) gm.gmBlackBoxY - gm.gmptGlyphOrigin.y,
898 gm.gmCellIncX, gm.gmCellIncY,
899 gl_bitmap);
900 } else {
901 /* This is the case of 'empty' glyphs like the space character */
902 glBitmap(0, 0, 0, 0, gm.gmCellIncX, gm.gmCellIncY, NULL);
904 glEndList();
907 glPixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
908 HeapFree(GetProcessHeap(), 0, bitmap);
909 HeapFree(GetProcessHeap(), 0, gl_bitmap);
910 return ret;
913 /***********************************************************************
914 * wglUseFontBitmapsA (OPENGL32.@)
916 BOOL WINAPI wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase)
918 return wglUseFontBitmaps_common( hdc, first, count, listBase, FALSE );
921 /***********************************************************************
922 * wglUseFontBitmapsW (OPENGL32.@)
924 BOOL WINAPI wglUseFontBitmapsW(HDC hdc, DWORD first, DWORD count, DWORD listBase)
926 return wglUseFontBitmaps_common( hdc, first, count, listBase, TRUE );
929 #ifdef SONAME_LIBGLU
931 static void *load_libglu(void)
933 static int already_loaded;
934 void *handle;
936 if (already_loaded) return libglu_handle;
937 already_loaded = 1;
939 TRACE("Trying to load GLU library: %s\n", SONAME_LIBGLU);
940 handle = wine_dlopen(SONAME_LIBGLU, RTLD_NOW, NULL, 0);
941 if (!handle)
943 WARN("Failed to load %s\n", SONAME_LIBGLU);
944 return NULL;
947 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(handle, #f, NULL, 0)) == NULL) goto sym_not_found;
948 LOAD_FUNCPTR(gluNewTess)
949 LOAD_FUNCPTR(gluDeleteTess)
950 LOAD_FUNCPTR(gluTessBeginContour)
951 LOAD_FUNCPTR(gluTessBeginPolygon)
952 LOAD_FUNCPTR(gluTessCallback)
953 LOAD_FUNCPTR(gluTessEndContour)
954 LOAD_FUNCPTR(gluTessEndPolygon)
955 LOAD_FUNCPTR(gluTessVertex)
956 #undef LOAD_FUNCPTR
957 libglu_handle = handle;
958 return handle;
960 sym_not_found:
961 WARN("Unable to load function ptrs from libGLU\n");
962 /* Close the library as we won't use it */
963 wine_dlclose(handle, NULL, 0);
964 return NULL;
967 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
969 vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
970 vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
971 vertex[2] = 0.0;
974 static void tess_callback_vertex(GLvoid *vertex)
976 GLdouble *dbl = vertex;
977 TRACE("%f, %f, %f\n", dbl[0], dbl[1], dbl[2]);
978 glVertex3dv(vertex);
981 static void tess_callback_begin(GLenum which)
983 TRACE("%d\n", which);
984 glBegin(which);
987 static void tess_callback_end(void)
989 TRACE("\n");
990 glEnd();
993 /***********************************************************************
994 * wglUseFontOutlines_common
996 static BOOL wglUseFontOutlines_common(HDC hdc,
997 DWORD first,
998 DWORD count,
999 DWORD listBase,
1000 FLOAT deviation,
1001 FLOAT extrusion,
1002 int format,
1003 LPGLYPHMETRICSFLOAT lpgmf,
1004 BOOL unicode)
1006 UINT glyph;
1007 const MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
1008 GLUtesselator *tess;
1009 LOGFONTW lf;
1010 HFONT old_font, unscaled_font;
1011 UINT em_size = 1024;
1012 RECT rc;
1014 TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
1015 listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
1017 if (!load_libglu())
1019 ERR("libGLU is required for this function but isn't loaded\n");
1020 return FALSE;
1023 tess = pgluNewTess();
1024 if(!tess) return FALSE;
1025 pgluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tess_callback_vertex);
1026 pgluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tess_callback_begin);
1027 pgluTessCallback(tess, GLU_TESS_END, tess_callback_end);
1029 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
1030 rc.left = rc.right = rc.bottom = 0;
1031 rc.top = em_size;
1032 DPtoLP(hdc, (POINT*)&rc, 2);
1033 lf.lfHeight = -abs(rc.top - rc.bottom);
1034 lf.lfOrientation = lf.lfEscapement = 0;
1035 unscaled_font = CreateFontIndirectW(&lf);
1036 old_font = SelectObject(hdc, unscaled_font);
1038 for (glyph = first; glyph < first + count; glyph++)
1040 DWORD needed;
1041 GLYPHMETRICS gm;
1042 BYTE *buf;
1043 TTPOLYGONHEADER *pph;
1044 TTPOLYCURVE *ppc;
1045 GLdouble *vertices;
1047 if(unicode)
1048 needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1049 else
1050 needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
1052 if(needed == GDI_ERROR)
1053 goto error;
1055 buf = HeapAlloc(GetProcessHeap(), 0, needed);
1056 vertices = HeapAlloc(GetProcessHeap(), 0, needed / sizeof(POINTFX) * 3 * sizeof(GLdouble));
1058 if(unicode)
1059 GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1060 else
1061 GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
1063 TRACE("glyph %d\n", glyph);
1065 if(lpgmf)
1067 lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
1068 lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
1069 lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
1070 lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
1071 lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
1072 lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
1074 TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
1075 lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
1076 lpgmf++;
1079 glNewList(listBase++, GL_COMPILE);
1080 pgluTessBeginPolygon(tess, NULL);
1082 pph = (TTPOLYGONHEADER*)buf;
1083 while((BYTE*)pph < buf + needed)
1085 TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
1087 pgluTessBeginContour(tess);
1089 fixed_to_double(pph->pfxStart, em_size, vertices);
1090 pgluTessVertex(tess, vertices, vertices);
1091 vertices += 3;
1093 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
1094 while((char*)ppc < (char*)pph + pph->cb)
1096 int i;
1098 switch(ppc->wType) {
1099 case TT_PRIM_LINE:
1100 for(i = 0; i < ppc->cpfx; i++)
1102 TRACE("\t\tline to %d, %d\n", ppc->apfx[i].x.value, ppc->apfx[i].y.value);
1103 fixed_to_double(ppc->apfx[i], em_size, vertices);
1104 pgluTessVertex(tess, vertices, vertices);
1105 vertices += 3;
1107 break;
1109 case TT_PRIM_QSPLINE:
1110 for(i = 0; i < ppc->cpfx/2; i++)
1112 /* FIXME: just connecting the control points for now */
1113 TRACE("\t\tcurve %d,%d %d,%d\n",
1114 ppc->apfx[i * 2].x.value, ppc->apfx[i * 3].y.value,
1115 ppc->apfx[i * 2 + 1].x.value, ppc->apfx[i * 3 + 1].y.value);
1116 fixed_to_double(ppc->apfx[i * 2], em_size, vertices);
1117 pgluTessVertex(tess, vertices, vertices);
1118 vertices += 3;
1119 fixed_to_double(ppc->apfx[i * 2 + 1], em_size, vertices);
1120 pgluTessVertex(tess, vertices, vertices);
1121 vertices += 3;
1123 break;
1124 default:
1125 ERR("\t\tcurve type = %d\n", ppc->wType);
1126 pgluTessEndContour(tess);
1127 goto error_in_list;
1130 ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
1131 (ppc->cpfx - 1) * sizeof(POINTFX));
1133 pgluTessEndContour(tess);
1134 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
1137 error_in_list:
1138 pgluTessEndPolygon(tess);
1139 glTranslated((GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0);
1140 glEndList();
1141 HeapFree(GetProcessHeap(), 0, buf);
1142 HeapFree(GetProcessHeap(), 0, vertices);
1145 error:
1146 DeleteObject(SelectObject(hdc, old_font));
1147 pgluDeleteTess(tess);
1148 return TRUE;
1152 #else /* SONAME_LIBGLU */
1154 static BOOL wglUseFontOutlines_common(HDC hdc,
1155 DWORD first,
1156 DWORD count,
1157 DWORD listBase,
1158 FLOAT deviation,
1159 FLOAT extrusion,
1160 int format,
1161 LPGLYPHMETRICSFLOAT lpgmf,
1162 BOOL unicode)
1164 FIXME("Unable to compile in wglUseFontOutlines support without GL/glu.h\n");
1165 return FALSE;
1168 #endif /* SONAME_LIBGLU */
1170 /***********************************************************************
1171 * wglUseFontOutlinesA (OPENGL32.@)
1173 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
1174 DWORD first,
1175 DWORD count,
1176 DWORD listBase,
1177 FLOAT deviation,
1178 FLOAT extrusion,
1179 int format,
1180 LPGLYPHMETRICSFLOAT lpgmf)
1182 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
1185 /***********************************************************************
1186 * wglUseFontOutlinesW (OPENGL32.@)
1188 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
1189 DWORD first,
1190 DWORD count,
1191 DWORD listBase,
1192 FLOAT deviation,
1193 FLOAT extrusion,
1194 int format,
1195 LPGLYPHMETRICSFLOAT lpgmf)
1197 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
1200 /***********************************************************************
1201 * glDebugEntry (OPENGL32.@)
1203 GLint WINAPI wine_glDebugEntry( GLint unknown1, GLint unknown2 )
1205 return 0;
1208 /***********************************************************************
1209 * glFinish (OPENGL32.@)
1211 void WINAPI wine_glFinish( void )
1213 TRACE("()\n");
1214 wine_wgl.p_wglFinish();
1217 /***********************************************************************
1218 * glFlush (OPENGL32.@)
1220 void WINAPI wine_glFlush( void )
1222 TRACE("()\n");
1223 wine_wgl.p_wglFlush();
1226 /* build the extension string by filtering out the disabled extensions */
1227 static char *build_gl_extensions( const char *extensions )
1229 char *p, *str, *disabled = NULL;
1230 const char *end;
1231 HKEY hkey;
1233 TRACE( "GL_EXTENSIONS:\n" );
1235 if (!extensions) extensions = "";
1237 /* @@ Wine registry key: HKCU\Software\Wine\OpenGL */
1238 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\OpenGL", &hkey ))
1240 DWORD size, ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, NULL, &size );
1241 if (!ret && (disabled = HeapAlloc( GetProcessHeap(), 0, size )))
1242 ret = RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, (BYTE *)disabled, &size );
1243 RegCloseKey( hkey );
1244 if (ret) *disabled = 0;
1247 if ((str = HeapAlloc( GetProcessHeap(), 0, strlen(extensions) + 2 )))
1249 p = str;
1250 for (;;)
1252 while (*extensions == ' ') extensions++;
1253 if (!*extensions) break;
1254 if (!(end = strchr( extensions, ' ' ))) end = extensions + strlen( extensions );
1255 memcpy( p, extensions, end - extensions );
1256 p[end - extensions] = 0;
1257 if (!has_extension( disabled, p ))
1259 TRACE("++ %s\n", p );
1260 p += end - extensions;
1261 *p++ = ' ';
1263 else TRACE("-- %s (disabled by config)\n", p );
1264 extensions = end;
1266 *p = 0;
1268 HeapFree( GetProcessHeap(), 0, disabled );
1269 return str;
1272 /***********************************************************************
1273 * glGetString (OPENGL32.@)
1275 const GLubyte * WINAPI wine_glGetString( GLenum name )
1277 static const GLubyte *gl_extensions;
1279 /* this is for buggy nvidia driver, crashing if called from a different
1280 thread with no context */
1281 if(wglGetCurrentContext() == NULL)
1282 return NULL;
1284 if (name != GL_EXTENSIONS) return glGetString(name);
1286 if (!gl_extensions)
1288 const char *orig_ext = (const char *)glGetString(GL_EXTENSIONS);
1289 char *new_ext = build_gl_extensions( orig_ext );
1290 if (InterlockedCompareExchangePointer( (void **)&gl_extensions, new_ext, NULL ))
1291 HeapFree( GetProcessHeap(), 0, new_ext );
1293 return gl_extensions;
1296 /***********************************************************************
1297 * glGetIntegerv (OPENGL32.@)
1299 void WINAPI wine_glGetIntegerv( GLenum pname, GLint* params )
1301 wine_wgl.p_wglGetIntegerv(pname, params);
1304 /***********************************************************************
1305 * wglSwapBuffers (OPENGL32.@)
1307 BOOL WINAPI DECLSPEC_HOTPATCH wglSwapBuffers( HDC hdc )
1309 return GdiSwapBuffers(hdc);
1312 /* This is for brain-dead applications that use OpenGL functions before even
1313 creating a rendering context.... */
1314 static BOOL process_attach(void)
1316 HDC hdc = GetDC( 0 );
1317 const struct wgl_funcs *funcs = get_dc_funcs( hdc );
1319 ReleaseDC( 0, hdc );
1321 /* internal WGL functions */
1322 wine_wgl.p_wglFinish = (void *)funcs->p_wglGetProcAddress("wglFinish");
1323 wine_wgl.p_wglFlush = (void *)funcs->p_wglGetProcAddress("wglFlush");
1324 wine_wgl.p_wglGetIntegerv = (void *)funcs->p_wglGetProcAddress("wglGetIntegerv");
1325 return TRUE;
1329 /**********************************************************************/
1331 static void process_detach(void)
1333 if (libglu_handle) wine_dlclose(libglu_handle, NULL, 0);
1336 /***********************************************************************
1337 * OpenGL initialisation routine
1339 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
1341 switch(reason)
1343 case DLL_PROCESS_ATTACH:
1344 opengl32_handle = hinst;
1345 DisableThreadLibraryCalls(hinst);
1346 NtCurrentTeb()->glTable = &null_opengl_funcs;
1347 return process_attach();
1348 case DLL_THREAD_ATTACH:
1349 NtCurrentTeb()->glTable = &null_opengl_funcs;
1350 break;
1351 case DLL_PROCESS_DETACH:
1352 process_detach();
1353 break;
1355 return TRUE;