For all DLLs with defined DllMain and which do not require
[wine] / dlls / opengl32 / wgl.c
1 /* Window-specific OpenGL functions implementation.
2  *
3  * Copyright (c) 1999 Lionel Ulmer
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29
30 #include "wgl.h"
31 #include "opengl_ext.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(opengl);
35
36 /* x11drv GDI escapes */
37 #define X11DRV_ESCAPE 6789
38 enum x11drv_escape_codes
39 {
40     X11DRV_GET_DISPLAY,   /* get X11 display for a DC */
41     X11DRV_GET_DRAWABLE,  /* get current drawable for a DC */
42     X11DRV_GET_FONT,      /* get current X font for a DC */
43 };
44
45 void (*wine_tsx11_lock_ptr)(void) = NULL;
46 void (*wine_tsx11_unlock_ptr)(void) = NULL;
47
48 static GLXContext default_cx = NULL;
49 static Display *default_display;  /* display to use for default context */
50
51 typedef struct wine_glcontext {
52   HDC hdc;
53   Display *display;
54   GLXContext ctx;
55   XVisualInfo *vis;
56   struct wine_glcontext *next;
57   struct wine_glcontext *prev;
58 } Wine_GLContext;
59 static Wine_GLContext *context_list;
60
61 static inline Wine_GLContext *get_context_from_GLXContext(GLXContext ctx)
62 {
63     Wine_GLContext *ret;
64     for (ret = context_list; ret; ret = ret->next) if (ctx == ret->ctx) break;
65     return ret;
66 }
67
68 static inline void free_context(Wine_GLContext *context)
69 {
70   if (context->next != NULL) context->next->prev = context->prev;
71   if (context->prev != NULL) context->prev->next = context->next;
72   else context_list = context->next;
73
74   HeapFree(GetProcessHeap(), 0, context);
75 }
76
77 static inline Wine_GLContext *alloc_context(void)
78 {
79   Wine_GLContext *ret;
80
81   if ((ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Wine_GLContext))))
82   {
83       ret->next = context_list;
84       if (context_list) context_list->prev = ret;
85       context_list = ret;
86   }
87   return ret;
88 }
89
90 inline static BOOL is_valid_context( Wine_GLContext *ctx )
91 {
92     Wine_GLContext *ptr;
93     for (ptr = context_list; ptr; ptr = ptr->next) if (ptr == ctx) break;
94     return (ptr != NULL);
95 }
96
97 /* retrieve the X display to use on a given DC */
98 inline static Display *get_display( HDC hdc )
99 {
100     Display *display;
101     enum x11drv_escape_codes escape = X11DRV_GET_DISPLAY;
102
103     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
104                     sizeof(display), (LPSTR)&display )) display = NULL;
105     return display;
106 }
107
108
109 /* retrieve the X drawable to use on a given DC */
110 inline static Drawable get_drawable( HDC hdc )
111 {
112     Drawable drawable;
113     enum x11drv_escape_codes escape = X11DRV_GET_DRAWABLE;
114
115     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
116                     sizeof(drawable), (LPSTR)&drawable )) drawable = 0;
117     return drawable;
118 }
119
120
121 /* retrieve the X drawable to use on a given DC */
122 inline static Font get_font( HDC hdc )
123 {
124     Font font;
125     enum x11drv_escape_codes escape = X11DRV_GET_FONT;
126
127     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
128                     sizeof(font), (LPSTR)&font )) font = 0;
129     return font;
130 }
131
132
133 /***********************************************************************
134  *              wglCreateContext (OPENGL32.@)
135  */
136 HGLRC WINAPI wglCreateContext(HDC hdc)
137 {
138   XVisualInfo *vis;
139   Wine_GLContext *ret;
140   int num;
141   XVisualInfo template;
142   Display *display = get_display( hdc );
143
144   TRACE("(%p)\n", hdc);
145
146   /* First, get the visual in use by the X11DRV */
147   if (!display) return 0;
148   template.visualid = (VisualID)GetPropA( GetDesktopWindow(), "__wine_x11_visual_id" );
149   vis = XGetVisualInfo(display, VisualIDMask, &template, &num);
150
151   if (vis == NULL) {
152     ERR("NULL visual !!!\n");
153     /* Need to set errors here */
154     return NULL;
155   }
156
157   /* The context will be allocated in the wglMakeCurrent call */
158   ENTER_GL();
159   ret = alloc_context();
160   LEAVE_GL();
161   ret->hdc = hdc;
162   ret->display = display;
163   ret->vis = vis;
164
165   TRACE(" creating context %p (GL context creation delayed)\n", ret);
166   return (HGLRC) ret;
167 }
168
169 /***********************************************************************
170  *              wglCreateLayerContext (OPENGL32.@)
171  */
172 HGLRC WINAPI wglCreateLayerContext(HDC hdc,
173                                    int iLayerPlane) {
174   TRACE("(%p,%d)\n", hdc, iLayerPlane);
175
176   if (iLayerPlane == 0) {
177       return wglCreateContext(hdc);
178   }
179   FIXME(" no handler for layer %d\n", iLayerPlane);
180
181   return NULL;
182 }
183
184 /***********************************************************************
185  *              wglCopyContext (OPENGL32.@)
186  */
187 BOOL WINAPI wglCopyContext(HGLRC hglrcSrc,
188                            HGLRC hglrcDst,
189                            UINT mask) {
190   FIXME("(%p,%p,%d)\n", hglrcSrc, hglrcDst, mask);
191
192   return FALSE;
193 }
194
195 /***********************************************************************
196  *              wglDeleteContext (OPENGL32.@)
197  */
198 BOOL WINAPI wglDeleteContext(HGLRC hglrc)
199 {
200   Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
201   BOOL ret = TRUE;
202
203   TRACE("(%p)\n", hglrc);
204
205   ENTER_GL();
206   /* A game (Half Life not to name it) deletes twice the same context,
207    * so make sure it is valid first */
208   if (is_valid_context( ctx ))
209   {
210       if (ctx->ctx) glXDestroyContext(ctx->display, ctx->ctx);
211       free_context(ctx);
212   }
213   else
214   {
215     WARN("Error deleting context !\n");
216     SetLastError(ERROR_INVALID_HANDLE);
217     ret = FALSE;
218   }
219   LEAVE_GL();
220
221   return ret;
222 }
223
224 /***********************************************************************
225  *              wglDescribeLayerPlane (OPENGL32.@)
226  */
227 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
228                                   int iPixelFormat,
229                                   int iLayerPlane,
230                                   UINT nBytes,
231                                   LPLAYERPLANEDESCRIPTOR plpd) {
232   FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
233
234   return FALSE;
235 }
236
237 /***********************************************************************
238  *              wglGetCurrentContext (OPENGL32.@)
239  */
240 HGLRC WINAPI wglGetCurrentContext(void) {
241   GLXContext gl_ctx;
242   Wine_GLContext *ret;
243
244   TRACE("()\n");
245
246   ENTER_GL();
247   gl_ctx = glXGetCurrentContext();
248   ret = get_context_from_GLXContext(gl_ctx);
249   LEAVE_GL();
250
251   TRACE(" returning %p (GL context %p)\n", ret, gl_ctx);
252
253   return ret;
254 }
255
256 /***********************************************************************
257  *              wglGetCurrentDC (OPENGL32.@)
258  */
259 HDC WINAPI wglGetCurrentDC(void) {
260   GLXContext gl_ctx;
261   Wine_GLContext *ret;
262
263   TRACE("()\n");
264
265   ENTER_GL();
266   gl_ctx = glXGetCurrentContext();
267   ret = get_context_from_GLXContext(gl_ctx);
268   LEAVE_GL();
269
270   if (ret) {
271     TRACE(" returning %p (GL context %p - Wine context %p)\n", ret->hdc, gl_ctx, ret);
272     return ret->hdc;
273   } else {
274     TRACE(" no Wine context found for GLX context %p\n", gl_ctx);
275     return 0;
276   }
277 }
278
279 /***********************************************************************
280  *              wglGetLayerPaletteEntries (OPENGL32.@)
281  */
282 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
283                                      int iLayerPlane,
284                                      int iStart,
285                                      int cEntries,
286                                      const COLORREF *pcr) {
287   FIXME("(): stub !\n");
288
289   return 0;
290 }
291
292 /***********************************************************************
293  *              wglGetProcAddress (OPENGL32.@)
294  */
295 static int compar(const void *elt_a, const void *elt_b) {
296   return strcmp(((OpenGL_extension *) elt_a)->name,
297                 ((OpenGL_extension *) elt_b)->name);
298 }
299
300 void* WINAPI wglGetProcAddress(LPCSTR  lpszProc) {
301   void *local_func;
302   static HMODULE hm = 0;
303   OpenGL_extension  ext;
304   OpenGL_extension *ext_ret;
305
306
307   TRACE("(%s)\n", lpszProc);
308
309   if (hm == 0)
310       hm = GetModuleHandleA("opengl32");
311
312   /* First, look if it's not already defined in the 'standard' OpenGL functions */
313   if ((local_func = GetProcAddress(hm, lpszProc)) != NULL) {
314     TRACE(" found function in 'standard' OpenGL functions (%p)\n", local_func);
315     return local_func;
316   }
317
318   /* After that, search in the thunks to find the real name of the extension */
319   ext.name = (char *) lpszProc;
320   ext_ret = (OpenGL_extension *) bsearch(&ext, extension_registry,
321                                          extension_registry_size, sizeof(OpenGL_extension), compar);
322
323   if (ext_ret == NULL) {
324     /* Some sanity checks :-) */
325     if (glXGetProcAddressARB(lpszProc) != NULL) {
326       ERR("Extension %s defined in the OpenGL library but NOT in opengl_ext.c... Please report (lionel.ulmer@free.fr) !\n", lpszProc);
327       return NULL;
328     }
329
330     WARN("Did not find extension %s in either Wine or your OpenGL library.\n", lpszProc);
331     return NULL;
332   } else {
333     /* After that, look at the extensions defined in the Linux OpenGL library */
334     if ((local_func = glXGetProcAddressARB(ext_ret->glx_name)) == NULL) {
335       char buf[256];
336       void *ret = NULL;
337
338       /* Remove the 3 last letters (EXT, ARB, ...).
339
340          I know that some extensions have more than 3 letters (MESA, NV,
341          INTEL, ...), but this is only a stop-gap measure to fix buggy
342          OpenGL drivers (moreover, it is only useful for old 1.0 apps
343          that query the glBindTextureEXT extension).
344       */
345       strncpy(buf, ext_ret->glx_name, strlen(ext_ret->glx_name) - 3);
346       buf[strlen(ext_ret->glx_name) - 3] = '\0';
347       TRACE(" extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf);
348
349       ret = GetProcAddress(hm, buf);
350       if (ret != NULL) {
351         TRACE(" found function in main OpenGL library (%p) !\n", ret);
352       } else {
353         WARN("Did not find function %s (%s) in your OpenGL library !\n", lpszProc, ext_ret->glx_name);
354       }
355
356       return ret;
357     } else {
358       TRACE(" returning function  (%p)\n", ext_ret->func);
359       *(ext_ret->func_ptr) = local_func;
360
361       return ext_ret->func;
362     }
363   }
364 }
365
366 /***********************************************************************
367  *              wglMakeCurrent (OPENGL32.@)
368  */
369 BOOL WINAPI wglMakeCurrent(HDC hdc,
370                            HGLRC hglrc) {
371   BOOL ret;
372
373   TRACE("(%p,%p)\n", hdc, hglrc);
374
375   ENTER_GL();
376   if (hglrc == NULL) {
377       ret = glXMakeCurrent(default_display, None, NULL);
378   } else {
379       Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
380       Drawable drawable = get_drawable( hdc );
381
382       if (ctx->ctx == NULL) {
383         ctx->ctx = glXCreateContext(ctx->display, ctx->vis, NULL, True);
384         TRACE(" created a delayed OpenGL context (%p)\n", ctx->ctx);
385       }
386       ret = glXMakeCurrent(ctx->display, drawable, ctx->ctx);
387   }
388   LEAVE_GL();
389   TRACE(" returning %s\n", (ret ? "True" : "False"));
390   return ret;
391 }
392
393 /***********************************************************************
394  *              wglRealizeLayerPalette (OPENGL32.@)
395  */
396 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
397                                    int iLayerPlane,
398                                    BOOL bRealize) {
399   FIXME("()\n");
400
401   return FALSE;
402 }
403
404 /***********************************************************************
405  *              wglSetLayerPaletteEntries (OPENGL32.@)
406  */
407 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
408                                      int iLayerPlane,
409                                      int iStart,
410                                      int cEntries,
411                                      const COLORREF *pcr) {
412   FIXME("(): stub !\n");
413
414   return 0;
415 }
416
417 /***********************************************************************
418  *              wglShareLists (OPENGL32.@)
419  */
420 BOOL WINAPI wglShareLists(HGLRC hglrc1,
421                           HGLRC hglrc2) {
422   Wine_GLContext *org  = (Wine_GLContext *) hglrc1;
423   Wine_GLContext *dest = (Wine_GLContext *) hglrc2;
424
425   TRACE("(%p, %p)\n", org, dest);
426
427   if (dest->ctx != NULL) {
428     ERR("Could not share display lists, context already created !\n");
429     return FALSE;
430   } else {
431     if (org->ctx == NULL) {
432       ENTER_GL();
433       org->ctx = glXCreateContext(org->display, org->vis, NULL, True);
434       LEAVE_GL();
435       TRACE(" created a delayed OpenGL context (%p) for Wine context %p\n", org->ctx, org);
436     }
437
438     ENTER_GL();
439     /* Create the destination context with display lists shared */
440     dest->ctx = glXCreateContext(org->display, dest->vis, org->ctx, True);
441     LEAVE_GL();
442     TRACE(" created a delayed OpenGL context (%p) for Wine context %p sharing lists with OpenGL ctx %p\n", dest->ctx, dest, org->ctx);
443   }
444
445   return TRUE;
446 }
447
448 /***********************************************************************
449  *              wglSwapLayerBuffers (OPENGL32.@)
450  */
451 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
452                                 UINT fuPlanes) {
453   TRACE("(%p, %08x)\n", hdc, fuPlanes);
454
455   if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
456     if (!SwapBuffers(hdc)) return FALSE;
457     fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
458   }
459
460   if (fuPlanes) {
461     WARN("Following layers unhandled : %08x\n", fuPlanes);
462   }
463
464   return TRUE;
465 }
466
467 /***********************************************************************
468  *              wglUseFontBitmapsA (OPENGL32.@)
469  */
470 BOOL WINAPI wglUseFontBitmapsA(HDC hdc,
471                                DWORD first,
472                                DWORD count,
473                                DWORD listBase)
474 {
475   Font fid = get_font( hdc );
476
477   TRACE("(%p, %ld, %ld, %ld) using font %ld\n", hdc, first, count, listBase, fid);
478
479   if (fid == 0) {
480     /* We are running using client-side rendering fonts... */
481     GLYPHMETRICS gm;
482     static const MAT2 id = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
483     int glyph;
484     int size = 0;
485     void *bitmap = NULL, *gl_bitmap = NULL;
486     int org_alignment;
487
488     ENTER_GL();
489     glGetIntegerv(GL_UNPACK_ALIGNMENT, &org_alignment);
490     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
491     LEAVE_GL();
492
493     for (glyph = first; glyph < first + count; glyph++) {
494       int needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &id);
495       int height, width_int;
496
497       if (needed_size == GDI_ERROR) goto error;
498       if (needed_size > size) {
499         size = needed_size;
500         if (bitmap) HeapFree(GetProcessHeap(), 0, bitmap);
501         if (gl_bitmap) HeapFree(GetProcessHeap(), 0, gl_bitmap);
502         bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
503         gl_bitmap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
504       }
505       if (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, size, bitmap, &id) == GDI_ERROR) goto error;
506       if (TRACE_ON(opengl)) {
507         unsigned int height, width, bitmask;
508         unsigned char *bitmap_ = (unsigned char *) bitmap;
509
510         DPRINTF("Glyph : %d\n", glyph);
511         DPRINTF("  - bbox : %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
512         DPRINTF("  - origin : (%ld , %ld)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
513         DPRINTF("  - increment : %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
514         DPRINTF("  - size : %d\n", needed_size);
515         DPRINTF("  - bitmap : \n");
516         for (height = 0; height < gm.gmBlackBoxY; height++) {
517           DPRINTF("      ");
518           for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
519             if (bitmask == 0) {
520               bitmap_ += 1;
521               bitmask = 0x80;
522             }
523             if (*bitmap_ & bitmask)
524               DPRINTF("*");
525             else
526               DPRINTF(" ");
527           }
528           bitmap_ += (4 - (((unsigned int) bitmap_) & 0x03));
529           DPRINTF("\n");
530         }
531       }
532
533       /* For some obscure reasons, I seem to need to rotate the glyph for OpenGL to be happy.
534          As Wine does not seem to support the MAT2 field, I need to do it myself.... */
535       width_int = (gm.gmBlackBoxX + 31) / 32;
536       for (height = 0; height < gm.gmBlackBoxY; height++) {
537         int width;
538         for (width = 0; width < width_int; width++) {
539           ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
540             ((int *) bitmap)[height * width_int + width];
541         }
542       }
543
544       ENTER_GL();
545       glNewList(listBase++, GL_COMPILE);
546       glBitmap(gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, gm.gmBlackBoxY - gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY, gl_bitmap);
547       glEndList();
548       LEAVE_GL();
549     }
550
551     ENTER_GL();
552     glPixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
553     LEAVE_GL();
554
555     if (bitmap) HeapFree(GetProcessHeap(), 0, bitmap);
556     if (gl_bitmap) HeapFree(GetProcessHeap(), 0, gl_bitmap);
557     return TRUE;
558
559   error:
560     ENTER_GL();
561     glPixelStorei(GL_UNPACK_ALIGNMENT, org_alignment);
562     LEAVE_GL();
563
564     if (bitmap) HeapFree(GetProcessHeap(), 0, bitmap);
565     if (gl_bitmap) HeapFree(GetProcessHeap(), 0, gl_bitmap);
566     return FALSE;
567   }
568
569   ENTER_GL();
570   /* I assume that the glyphs are at the same position for X and for Windows */
571   glXUseXFont(fid, first, count, listBase);
572   LEAVE_GL();
573   return TRUE;
574 }
575
576 /***********************************************************************
577  *              wglUseFontOutlinesA (OPENGL32.@)
578  */
579 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
580                                 DWORD first,
581                                 DWORD count,
582                                 DWORD listBase,
583                                 FLOAT deviation,
584                                 FLOAT extrusion,
585                                 int format,
586                                 LPGLYPHMETRICSFLOAT lpgmf) {
587   FIXME("(): stub !\n");
588
589   return FALSE;
590 }
591
592
593 /* This is for brain-dead applications that use OpenGL functions before even
594    creating a rendering context.... */
595 static BOOL process_attach(void)
596 {
597   XWindowAttributes win_attr;
598   Visual *rootVisual;
599   int num;
600   XVisualInfo template;
601   HDC hdc;
602   XVisualInfo *vis = NULL;
603   Window root = (Window)GetPropA( GetDesktopWindow(), "__wine_x11_whole_window" );
604   HMODULE mod = GetModuleHandleA( "x11drv.dll" );
605
606   if (!root || !mod)
607   {
608       ERR("X11DRV not loaded. Cannot create default context.\n");
609       return FALSE;
610   }
611
612   wine_tsx11_lock_ptr   = (void *)GetProcAddress( mod, "wine_tsx11_lock" );
613   wine_tsx11_unlock_ptr = (void *)GetProcAddress( mod, "wine_tsx11_unlock" );
614
615   hdc = GetDC(0);
616   default_display = get_display( hdc );
617   ReleaseDC( 0, hdc );
618   if (!default_display)
619   {
620       ERR("X11DRV not loaded. Cannot get display for screen DC.\n");
621       return FALSE;
622   }
623
624   ENTER_GL();
625
626   /* Try to get the visual from the Root Window.  We can't use the standard (presumably
627      double buffered) X11DRV visual with the Root Window, since we don't know if the Root
628      Window was created using the standard X11DRV visual, and glXMakeCurrent can't deal
629      with mismatched visuals.  Note that the Root Window visual may not be double
630      buffered, so apps actually attempting to render this way may flicker */
631   if (XGetWindowAttributes( default_display, root, &win_attr ))
632   {
633     rootVisual = win_attr.visual;
634   }
635   else
636   {
637     /* Get the default visual, since we can't seem to get the attributes from the
638        Root Window.  Let's hope that the Root Window Visual matches the DefaultVisual */
639     rootVisual = DefaultVisual( default_display, DefaultScreen(default_display) );
640   }
641
642   template.visualid = XVisualIDFromVisual(rootVisual);
643   vis = XGetVisualInfo(default_display, VisualIDMask, &template, &num);
644   if (vis != NULL) default_cx = glXCreateContext(default_display, vis, 0, GL_TRUE);
645   if (default_cx != NULL) glXMakeCurrent(default_display, root, default_cx);
646   XFree(vis);
647   LEAVE_GL();
648
649   if (default_cx == NULL) {
650     ERR("Could not create default context.\n");
651   }
652   return TRUE;
653 }
654
655 /* Some WGL extensions... */
656 static const char *WGL_extensions = "WGL_ARB_extensions_string WGL_EXT_extensions_string";
657
658 const char * WINAPI wglGetExtensionsStringEXT(void) {
659     TRACE("() returning \"%s\"\n", WGL_extensions);
660
661     return WGL_extensions;
662 }
663
664 static void process_detach(void)
665 {
666   glXDestroyContext(default_display, default_cx);
667 }
668
669 /***********************************************************************
670  *              wglGetExtensionsStringARB(OPENGL32.@)
671  */
672 const char * WINAPI wglGetExtensionsStringARB(HDC hdc) {
673
674   return wglGetExtensionsStringEXT();
675 }
676
677
678 /***********************************************************************
679  *           OpenGL initialisation routine
680  */
681 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
682 {
683     switch(reason)
684     {
685     case DLL_PROCESS_ATTACH:
686         DisableThreadLibraryCalls(hinst);
687         return process_attach();
688     case DLL_PROCESS_DETACH:
689         process_detach();
690         break;
691     }
692     return TRUE;
693 }