wined3d: Rename renderUpsideDown to render_offscreen.
[wine/testsucceed.git] / dlls / opengl32 / wgl.c
blob11918507e2c820b8679d3b5c819ae640cc7fb2b4
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 "winerror.h"
32 #include "winreg.h"
33 #include "wingdi.h"
34 #include "winternl.h"
35 #include "winnt.h"
37 #include "opengl_ext.h"
38 #ifdef HAVE_GL_GLU_H
39 #undef far
40 #undef near
41 #include <GL/glu.h>
42 #endif
43 #include "wine/library.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(wgl);
47 WINE_DECLARE_DEBUG_CHANNEL(opengl);
49 typedef struct wine_wgl_s {
50 PROC WINAPI (*p_wglGetProcAddress)(LPCSTR lpszProc);
52 void WINAPI (*p_wglGetIntegerv)(GLenum pname, GLint* params);
53 } wine_wgl_t;
55 /** global wgl object */
56 static wine_wgl_t wine_wgl;
58 /* x11drv GDI escapes */
59 #define X11DRV_ESCAPE 6789
60 enum x11drv_escape_codes
62 X11DRV_GET_DISPLAY, /* get X11 display for a DC */
63 X11DRV_GET_DRAWABLE, /* get current drawable for a DC */
64 X11DRV_GET_FONT, /* get current X font for a DC */
65 X11DRV_SET_DRAWABLE, /* set current drawable for a DC */
66 X11DRV_START_EXPOSURES, /* start graphics exposures */
67 X11DRV_END_EXPOSURES, /* end graphics exposures */
68 X11DRV_GET_DCE, /* get the DCE pointer */
69 X11DRV_SET_DCE, /* set the DCE pointer */
70 X11DRV_GET_GLX_DRAWABLE, /* get current glx drawable for a DC */
71 X11DRV_SYNC_PIXMAP /* sync the dibsection to its pixmap */
74 void (*wine_tsx11_lock_ptr)(void) = NULL;
75 void (*wine_tsx11_unlock_ptr)(void) = NULL;
77 static HMODULE opengl32_handle;
79 static char internal_gl_disabled_extensions[512];
80 static char* internal_gl_extensions = NULL;
82 typedef struct wine_glcontext {
83 HDC hdc;
84 Display *display;
85 XVisualInfo *vis;
86 GLXFBConfig fb_conf;
87 GLXContext ctx;
88 BOOL do_escape;
89 struct wine_glcontext *next;
90 struct wine_glcontext *prev;
91 } Wine_GLContext;
93 void enter_gl(void)
95 Wine_GLContext *curctx = (Wine_GLContext *) NtCurrentTeb()->glContext;
97 if (curctx && curctx->do_escape)
99 enum x11drv_escape_codes escape = X11DRV_SYNC_PIXMAP;
100 ExtEscape(curctx->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL);
103 wine_tsx11_lock_ptr();
104 return;
107 /***********************************************************************
108 * wglCreateLayerContext (OPENGL32.@)
110 HGLRC WINAPI wglCreateLayerContext(HDC hdc,
111 int iLayerPlane) {
112 TRACE("(%p,%d)\n", hdc, iLayerPlane);
114 if (iLayerPlane == 0) {
115 return wglCreateContext(hdc);
117 FIXME(" no handler for layer %d\n", iLayerPlane);
119 return NULL;
122 /***********************************************************************
123 * wglCopyContext (OPENGL32.@)
125 BOOL WINAPI wglCopyContext(HGLRC hglrcSrc,
126 HGLRC hglrcDst,
127 UINT mask) {
128 FIXME("(%p,%p,%d)\n", hglrcSrc, hglrcDst, mask);
130 return FALSE;
133 /***********************************************************************
134 * wglDescribeLayerPlane (OPENGL32.@)
136 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
137 int iPixelFormat,
138 int iLayerPlane,
139 UINT nBytes,
140 LPLAYERPLANEDESCRIPTOR plpd) {
141 FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
143 return FALSE;
146 /***********************************************************************
147 * wglGetLayerPaletteEntries (OPENGL32.@)
149 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
150 int iLayerPlane,
151 int iStart,
152 int cEntries,
153 const COLORREF *pcr) {
154 FIXME("(): stub !\n");
156 return 0;
159 static int compar(const void *elt_a, const void *elt_b) {
160 return strcmp(((const OpenGL_extension *) elt_a)->name,
161 ((const OpenGL_extension *) elt_b)->name);
164 /* Check if a GL extension is supported */
165 static BOOL is_extension_supported(const char* extension)
167 const char *gl_ext_string = (const char*)internal_glGetString(GL_EXTENSIONS);
169 TRACE("Checking for extension '%s'\n", extension);
171 if(!gl_ext_string) {
172 ERR("No OpenGL extensions found, check if your OpenGL setup is correct!\n");
173 return FALSE;
176 /* We use the GetProcAddress function from the display driver to retrieve function pointers
177 * for OpenGL and WGL extensions. In case of winex11.drv the OpenGL extension lookup is done
178 * using glXGetProcAddress. This function is quite unreliable in the sense that its specs don't
179 * require the function to return NULL when a extension isn't found. For this reason we check
180 * if the OpenGL extension required for the function we are looking up is supported. */
182 /* Check if the extension is part of the GL extension string to see if it is supported. */
183 if(strstr(gl_ext_string, extension) != NULL)
184 return TRUE;
186 /* In general an OpenGL function starts as an ARB/EXT extension and at some stage
187 * it becomes part of the core OpenGL library and can be reached without the ARB/EXT
188 * suffix as well. In the extension table, these functions contain GL_VERSION_major_minor.
189 * Check if we are searching for a core GL function */
190 if(strncmp(extension, "GL_VERSION_", 11) == 0)
192 const GLubyte *gl_version = glGetString(GL_VERSION);
193 const const char *version = extension + 11; /* Move past 'GL_VERSION_' */
195 if(!gl_version) {
196 ERR("Error no OpenGL version found,\n");
197 return FALSE;
200 /* Compare the major/minor version numbers of the native OpenGL library and what is required by the function.
201 * The gl_version string is guaranteed to have at least a major/minor and sometimes it has a release number as well. */
202 if( (gl_version[0] >= version[0]) || ((gl_version[0] == version[0]) && (gl_version[2] >= version[2])) ) {
203 return TRUE;
205 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]);
208 return FALSE;
211 /***********************************************************************
212 * wglGetProcAddress (OPENGL32.@)
214 PROC WINAPI wglGetProcAddress(LPCSTR lpszProc) {
215 void *local_func;
216 OpenGL_extension ext;
217 const OpenGL_extension *ext_ret;
219 TRACE("(%s)\n", lpszProc);
221 /* First, look if it's not already defined in the 'standard' OpenGL functions */
222 if ((local_func = GetProcAddress(opengl32_handle, lpszProc)) != NULL) {
223 TRACE(" found function in 'standard' OpenGL functions (%p)\n", local_func);
224 return local_func;
227 /* After that, search in the thunks to find the real name of the extension */
228 ext.name = lpszProc;
229 ext_ret = (const OpenGL_extension *) bsearch(&ext, extension_registry,
230 extension_registry_size, sizeof(OpenGL_extension), compar);
232 /* If nothing was found, we are looking for a WGL extension */
233 if (ext_ret == NULL) {
234 WARN("Extension '%s' not defined in opengl32.dll's function table!\n", lpszProc);
235 return wine_wgl.p_wglGetProcAddress(lpszProc);
236 } else { /* We are looking for an OpenGL extension */
238 /* Check if the GL extension required by the function is available */
239 if(!is_extension_supported(ext_ret->extension)) {
240 WARN("Extension '%s' required by function '%s' not supported!\n", ext_ret->extension, lpszProc);
241 return NULL;
244 local_func = wine_wgl.p_wglGetProcAddress(ext_ret->name);
246 /* After that, look at the extensions defined in the Linux OpenGL library */
247 if (local_func == NULL) {
248 char buf[256];
249 void *ret = NULL;
251 /* Remove the 3 last letters (EXT, ARB, ...).
253 I know that some extensions have more than 3 letters (MESA, NV,
254 INTEL, ...), but this is only a stop-gap measure to fix buggy
255 OpenGL drivers (moreover, it is only useful for old 1.0 apps
256 that query the glBindTextureEXT extension).
258 memcpy(buf, ext_ret->name, strlen(ext_ret->name) - 3);
259 buf[strlen(ext_ret->name) - 3] = '\0';
260 TRACE(" extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf);
262 ret = GetProcAddress(opengl32_handle, buf);
263 if (ret != NULL) {
264 TRACE(" found function in main OpenGL library (%p) !\n", ret);
265 } else {
266 WARN("Did not find function %s (%s) in your OpenGL library !\n", lpszProc, ext_ret->name);
269 return ret;
270 } else {
271 TRACE(" returning function (%p)\n", ext_ret->func);
272 extension_funcs[ext_ret - extension_registry] = local_func;
274 return ext_ret->func;
279 /***********************************************************************
280 * wglRealizeLayerPalette (OPENGL32.@)
282 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
283 int iLayerPlane,
284 BOOL bRealize) {
285 FIXME("()\n");
287 return FALSE;
290 /***********************************************************************
291 * wglSetLayerPaletteEntries (OPENGL32.@)
293 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
294 int iLayerPlane,
295 int iStart,
296 int cEntries,
297 const COLORREF *pcr) {
298 FIXME("(): stub !\n");
300 return 0;
303 /***********************************************************************
304 * wglSwapLayerBuffers (OPENGL32.@)
306 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
307 UINT fuPlanes) {
308 TRACE_(opengl)("(%p, %08x)\n", hdc, fuPlanes);
310 if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
311 if (!SwapBuffers(hdc)) return FALSE;
312 fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
315 if (fuPlanes) {
316 WARN("Following layers unhandled : %08x\n", fuPlanes);
319 return TRUE;
322 #ifdef HAVE_GL_GLU_H
324 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
326 vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
327 vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
328 vertex[2] = 0.0;
331 static void tess_callback_vertex(GLvoid *vertex)
333 GLdouble *dbl = vertex;
334 TRACE("%f, %f, %f\n", dbl[0], dbl[1], dbl[2]);
335 glVertex3dv(vertex);
338 static void tess_callback_begin(GLenum which)
340 TRACE("%d\n", which);
341 glBegin(which);
344 static void tess_callback_end(void)
346 TRACE("\n");
347 glEnd();
350 /***********************************************************************
351 * wglUseFontOutlines_common
353 BOOL WINAPI wglUseFontOutlines_common(HDC hdc,
354 DWORD first,
355 DWORD count,
356 DWORD listBase,
357 FLOAT deviation,
358 FLOAT extrusion,
359 int format,
360 LPGLYPHMETRICSFLOAT lpgmf,
361 BOOL unicode)
363 UINT glyph;
364 const MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
365 GLUtesselator *tess;
366 LOGFONTW lf;
367 HFONT old_font, unscaled_font;
368 UINT em_size = 1024;
369 RECT rc;
371 TRACE("(%p, %d, %d, %d, %f, %f, %d, %p, %s)\n", hdc, first, count,
372 listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
375 ENTER_GL();
376 tess = gluNewTess();
377 if(tess)
379 gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tess_callback_vertex);
380 gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tess_callback_begin);
381 gluTessCallback(tess, GLU_TESS_END, tess_callback_end);
383 LEAVE_GL();
385 if(!tess) return FALSE;
387 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
388 rc.left = rc.right = rc.bottom = 0;
389 rc.top = em_size;
390 DPtoLP(hdc, (POINT*)&rc, 2);
391 lf.lfHeight = -abs(rc.top - rc.bottom);
392 lf.lfOrientation = lf.lfEscapement = 0;
393 unscaled_font = CreateFontIndirectW(&lf);
394 old_font = SelectObject(hdc, unscaled_font);
396 for (glyph = first; glyph < first + count; glyph++)
398 DWORD needed;
399 GLYPHMETRICS gm;
400 BYTE *buf;
401 TTPOLYGONHEADER *pph;
402 TTPOLYCURVE *ppc;
403 GLdouble *vertices;
405 if(unicode)
406 needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
407 else
408 needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
410 if(needed == GDI_ERROR)
411 goto error;
413 buf = HeapAlloc(GetProcessHeap(), 0, needed);
414 vertices = HeapAlloc(GetProcessHeap(), 0, needed / sizeof(POINTFX) * 3 * sizeof(GLdouble));
416 if(unicode)
417 GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
418 else
419 GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
421 TRACE("glyph %d\n", glyph);
423 if(lpgmf)
425 lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
426 lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
427 lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
428 lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
429 lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
430 lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
432 TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
433 lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
434 lpgmf++;
437 ENTER_GL();
438 glNewList(listBase++, GL_COMPILE);
439 gluTessBeginPolygon(tess, NULL);
441 pph = (TTPOLYGONHEADER*)buf;
442 while((BYTE*)pph < buf + needed)
444 TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
446 gluTessBeginContour(tess);
448 fixed_to_double(pph->pfxStart, em_size, vertices);
449 gluTessVertex(tess, vertices, vertices);
450 vertices += 3;
452 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
453 while((char*)ppc < (char*)pph + pph->cb)
455 int i;
457 switch(ppc->wType) {
458 case TT_PRIM_LINE:
459 for(i = 0; i < ppc->cpfx; i++)
461 TRACE("\t\tline to %d, %d\n", ppc->apfx[i].x.value, ppc->apfx[i].y.value);
462 fixed_to_double(ppc->apfx[i], em_size, vertices);
463 gluTessVertex(tess, vertices, vertices);
464 vertices += 3;
466 break;
468 case TT_PRIM_QSPLINE:
469 for(i = 0; i < ppc->cpfx/2; i++)
471 /* FIXME just connecting the control points for now */
472 TRACE("\t\tcurve %d,%d %d,%d\n",
473 ppc->apfx[i * 2].x.value, ppc->apfx[i * 3].y.value,
474 ppc->apfx[i * 2 + 1].x.value, ppc->apfx[i * 3 + 1].y.value);
475 fixed_to_double(ppc->apfx[i * 2], em_size, vertices);
476 gluTessVertex(tess, vertices, vertices);
477 vertices += 3;
478 fixed_to_double(ppc->apfx[i * 2 + 1], em_size, vertices);
479 gluTessVertex(tess, vertices, vertices);
480 vertices += 3;
482 break;
483 default:
484 ERR("\t\tcurve type = %d\n", ppc->wType);
485 gluTessEndContour(tess);
486 goto error_in_list;
489 ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
490 (ppc->cpfx - 1) * sizeof(POINTFX));
492 gluTessEndContour(tess);
493 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
496 error_in_list:
497 gluTessEndPolygon(tess);
498 glTranslated((GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0);
499 glEndList();
500 LEAVE_GL();
501 HeapFree(GetProcessHeap(), 0, buf);
502 HeapFree(GetProcessHeap(), 0, vertices);
505 error:
506 DeleteObject(SelectObject(hdc, old_font));
507 gluDeleteTess(tess);
508 return TRUE;
512 #else /* HAVE_GL_GLU_H */
514 BOOL WINAPI wglUseFontOutlines_common(HDC hdc,
515 DWORD first,
516 DWORD count,
517 DWORD listBase,
518 FLOAT deviation,
519 FLOAT extrusion,
520 int format,
521 LPGLYPHMETRICSFLOAT lpgmf,
522 BOOL unicode)
524 FIXME("Unable to compile in wglUseFontOutlines support without GL/glu.h\n");
525 return FALSE;
528 #endif /* HAVE_GL_GLU_H */
530 /***********************************************************************
531 * wglUseFontOutlinesA (OPENGL32.@)
533 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
534 DWORD first,
535 DWORD count,
536 DWORD listBase,
537 FLOAT deviation,
538 FLOAT extrusion,
539 int format,
540 LPGLYPHMETRICSFLOAT lpgmf)
542 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
545 /***********************************************************************
546 * wglUseFontOutlinesW (OPENGL32.@)
548 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
549 DWORD first,
550 DWORD count,
551 DWORD listBase,
552 FLOAT deviation,
553 FLOAT extrusion,
554 int format,
555 LPGLYPHMETRICSFLOAT lpgmf)
557 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
560 const GLubyte * internal_glGetString(GLenum name) {
561 const char* GL_Extensions = NULL;
563 if (GL_EXTENSIONS != name) {
564 return glGetString(name);
567 if (NULL == internal_gl_extensions) {
568 GL_Extensions = (const char *) glGetString(GL_EXTENSIONS);
570 TRACE("GL_EXTENSIONS reported:\n");
571 if (NULL == GL_Extensions) {
572 ERR("GL_EXTENSIONS returns NULL\n");
573 return NULL;
574 } else {
575 size_t len = strlen(GL_Extensions);
576 internal_gl_extensions = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len + 2);
578 while (*GL_Extensions != 0x00) {
579 const char* Start = GL_Extensions;
580 char ThisExtn[256];
582 memset(ThisExtn, 0x00, sizeof(ThisExtn));
583 while (*GL_Extensions != ' ' && *GL_Extensions != 0x00) {
584 GL_Extensions++;
586 memcpy(ThisExtn, Start, (GL_Extensions - Start));
587 TRACE("- %s:", ThisExtn);
589 /* test if supported API is disabled by config */
590 if (NULL == strstr(internal_gl_disabled_extensions, ThisExtn)) {
591 strcat(internal_gl_extensions, " ");
592 strcat(internal_gl_extensions, ThisExtn);
593 TRACE(" active\n");
594 } else {
595 TRACE(" deactived (by config)\n");
598 if (*GL_Extensions == ' ') GL_Extensions++;
602 return (const GLubyte *) internal_gl_extensions;
605 void internal_glGetIntegerv(GLenum pname, GLint* params) {
606 TRACE("pname: 0x%x, params %p\n", pname, params);
607 glGetIntegerv(pname, params);
608 /* A few parameters like GL_DEPTH_BITS differ between WGL and GLX, the wglGetIntegerv helper function handles those */
609 wine_wgl.p_wglGetIntegerv(pname, params);
613 /* No need to load any other libraries as according to the ABI, libGL should be self-sufficient and
614 include all dependencies
616 #ifndef SONAME_LIBGL
617 #define SONAME_LIBGL "libGL.so"
618 #endif
620 /* This is for brain-dead applications that use OpenGL functions before even
621 creating a rendering context.... */
622 static BOOL process_attach(void)
624 HMODULE mod_x11, mod_gdi32;
625 DWORD size = sizeof(internal_gl_disabled_extensions);
626 HKEY hkey = 0;
628 GetDesktopWindow(); /* make sure winex11 is loaded (FIXME) */
629 mod_x11 = GetModuleHandleA( "winex11.drv" );
630 mod_gdi32 = GetModuleHandleA( "gdi32.dll" );
632 if (!mod_x11 || !mod_gdi32)
634 ERR("X11DRV or GDI32 not loaded. Cannot create default context.\n");
635 return FALSE;
638 wine_tsx11_lock_ptr = (void *)GetProcAddress( mod_x11, "wine_tsx11_lock" );
639 wine_tsx11_unlock_ptr = (void *)GetProcAddress( mod_x11, "wine_tsx11_unlock" );
641 wine_wgl.p_wglGetProcAddress = (void *)GetProcAddress(mod_gdi32, "wglGetProcAddress");
643 /* Interal WGL function */
644 wine_wgl.p_wglGetIntegerv = (void *)wine_wgl.p_wglGetProcAddress("wglGetIntegerv");
646 internal_gl_disabled_extensions[0] = 0;
647 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\OpenGL", &hkey)) {
648 if (!RegQueryValueExA( hkey, "DisabledExtensions", 0, NULL, (LPBYTE)internal_gl_disabled_extensions, &size)) {
649 TRACE("found DisabledExtensions=\"%s\"\n", internal_gl_disabled_extensions);
651 RegCloseKey(hkey);
654 return TRUE;
658 /**********************************************************************/
660 static void process_detach(void)
662 HeapFree(GetProcessHeap(), 0, internal_gl_extensions);
665 /***********************************************************************
666 * OpenGL initialisation routine
668 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
670 switch(reason)
672 case DLL_PROCESS_ATTACH:
673 opengl32_handle = hinst;
674 DisableThreadLibraryCalls(hinst);
675 return process_attach();
676 case DLL_PROCESS_DETACH:
677 process_detach();
678 break;
680 return TRUE;