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