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