Added regedit unit test, a couple minor changes to regedit.
[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)\n", hdc, first, count, listBase);
463
464   ENTER_GL();
465   /* I assume that the glyphs are at the same position for X and for Windows */
466   glXUseXFont(fid, first, count, listBase);
467   LEAVE_GL();
468   return TRUE;
469 }
470
471 /***********************************************************************
472  *              wglUseFontOutlinesA (OPENGL32.@)
473  */
474 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
475                                 DWORD first,
476                                 DWORD count,
477                                 DWORD listBase,
478                                 FLOAT deviation,
479                                 FLOAT extrusion,
480                                 int format,
481                                 LPGLYPHMETRICSFLOAT lpgmf) {
482   FIXME("(): stub !\n");
483
484   return FALSE;
485 }
486
487
488 /* This is for brain-dead applications that use OpenGL functions before even
489    creating a rendering context.... */
490 static BOOL process_attach(void)
491 {
492   XWindowAttributes win_attr;
493   Visual *rootVisual;
494   int num;
495   XVisualInfo template;
496   HDC hdc;
497   XVisualInfo *vis = NULL;
498   Window root = (Window)GetPropA( GetDesktopWindow(), "__wine_x11_whole_window" );
499
500   if (!root)
501   {
502       ERR("X11DRV not loaded. Cannot create default context.\n");
503       return FALSE;
504   }
505
506   hdc = GetDC(0);
507   default_display = get_display( hdc );
508   ReleaseDC( 0, hdc );
509   if (!default_display)
510   {
511       ERR("X11DRV not loaded. Cannot get display for screen DC.\n");
512       return FALSE;
513   }
514
515   ENTER_GL();
516
517   /* Try to get the visual from the Root Window.  We can't use the standard (presumably
518      double buffered) X11DRV visual with the Root Window, since we don't know if the Root
519      Window was created using the standard X11DRV visual, and glXMakeCurrent can't deal
520      with mismatched visuals.  Note that the Root Window visual may not be double
521      buffered, so apps actually attempting to render this way may flicker */
522   if (XGetWindowAttributes( default_display, root, &win_attr ))
523   {
524     rootVisual = win_attr.visual;
525   }
526   else
527   {
528     /* Get the default visual, since we can't seem to get the attributes from the
529        Root Window.  Let's hope that the Root Window Visual matches the DefaultVisual */
530     rootVisual = DefaultVisual( default_display, DefaultScreen(default_display) );
531   }
532
533   template.visualid = XVisualIDFromVisual(rootVisual);
534   vis = XGetVisualInfo(default_display, VisualIDMask, &template, &num);
535   if (vis != NULL) default_cx = glXCreateContext(default_display, vis, 0, GL_TRUE);
536   if (default_cx != NULL) glXMakeCurrent(default_display, root, default_cx);
537   XFree(vis);
538   LEAVE_GL();
539
540   if (default_cx == NULL) {
541     ERR("Could not create default context.\n");
542   }
543   return TRUE;
544 }
545
546 static void process_detach(void)
547 {
548   glXDestroyContext(default_display, default_cx);
549 }
550
551 /***********************************************************************
552  *           OpenGL initialisation routine
553  */
554 BOOL WINAPI OpenGL32_Init( HINSTANCE hinst, DWORD reason, LPVOID reserved )
555 {
556     switch(reason)
557     {
558     case DLL_PROCESS_ATTACH:
559         return process_attach();
560     case DLL_PROCESS_DETACH:
561         process_detach();
562         break;
563     }
564     return TRUE;
565 }