1 /* Window-specific OpenGL functions implementation.
3 * Copyright (c) 1999 Lionel Ulmer
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.
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.
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
21 #include "wine/port.h"
34 #include "opengl_ext.h"
35 #include "wine/library.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(opengl
);
40 /* x11drv GDI escapes */
41 #define X11DRV_ESCAPE 6789
42 enum x11drv_escape_codes
44 X11DRV_GET_DISPLAY
, /* get X11 display for a DC */
45 X11DRV_GET_DRAWABLE
, /* get current drawable for a DC */
46 X11DRV_GET_FONT
, /* get current X font for a DC */
49 void (*wine_tsx11_lock_ptr
)(void) = NULL
;
50 void (*wine_tsx11_unlock_ptr
)(void) = NULL
;
52 static GLXContext default_cx
= NULL
;
53 static Display
*default_display
; /* display to use for default context */
55 static void *(*p_glXGetProcAddressARB
)(const GLubyte
*);
57 typedef struct wine_glcontext
{
62 struct wine_glcontext
*next
;
63 struct wine_glcontext
*prev
;
65 static Wine_GLContext
*context_list
;
67 static inline Wine_GLContext
*get_context_from_GLXContext(GLXContext ctx
)
70 for (ret
= context_list
; ret
; ret
= ret
->next
) if (ctx
== ret
->ctx
) break;
74 static inline void free_context(Wine_GLContext
*context
)
76 if (context
->next
!= NULL
) context
->next
->prev
= context
->prev
;
77 if (context
->prev
!= NULL
) context
->prev
->next
= context
->next
;
78 else context_list
= context
->next
;
80 HeapFree(GetProcessHeap(), 0, context
);
83 static inline Wine_GLContext
*alloc_context(void)
87 if ((ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(Wine_GLContext
))))
89 ret
->next
= context_list
;
90 if (context_list
) context_list
->prev
= ret
;
96 inline static BOOL
is_valid_context( Wine_GLContext
*ctx
)
99 for (ptr
= context_list
; ptr
; ptr
= ptr
->next
) if (ptr
== ctx
) break;
100 return (ptr
!= NULL
);
103 /* retrieve the X display to use on a given DC */
104 inline static Display
*get_display( HDC hdc
)
107 enum x11drv_escape_codes escape
= X11DRV_GET_DISPLAY
;
109 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
110 sizeof(display
), (LPSTR
)&display
)) display
= NULL
;
115 /* retrieve the X drawable to use on a given DC */
116 inline static Drawable
get_drawable( HDC hdc
)
119 enum x11drv_escape_codes escape
= X11DRV_GET_DRAWABLE
;
121 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
122 sizeof(drawable
), (LPSTR
)&drawable
)) drawable
= 0;
127 /* retrieve the X drawable to use on a given DC */
128 inline static Font
get_font( HDC hdc
)
131 enum x11drv_escape_codes escape
= X11DRV_GET_FONT
;
133 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
134 sizeof(font
), (LPSTR
)&font
)) font
= 0;
139 /***********************************************************************
140 * wglCreateContext (OPENGL32.@)
142 HGLRC WINAPI
wglCreateContext(HDC hdc
)
147 XVisualInfo
template;
148 Display
*display
= get_display( hdc
);
150 TRACE("(%p)\n", hdc
);
152 /* First, get the visual in use by the X11DRV */
153 if (!display
) return 0;
154 template.visualid
= (VisualID
)GetPropA( GetDesktopWindow(), "__wine_x11_visual_id" );
155 vis
= XGetVisualInfo(display
, VisualIDMask
, &template, &num
);
158 ERR("NULL visual !!!\n");
159 /* Need to set errors here */
163 /* The context will be allocated in the wglMakeCurrent call */
165 ret
= alloc_context();
168 ret
->display
= display
;
171 TRACE(" creating context %p (GL context creation delayed)\n", ret
);
175 /***********************************************************************
176 * wglCreateLayerContext (OPENGL32.@)
178 HGLRC WINAPI
wglCreateLayerContext(HDC hdc
,
180 TRACE("(%p,%d)\n", hdc
, iLayerPlane
);
182 if (iLayerPlane
== 0) {
183 return wglCreateContext(hdc
);
185 FIXME(" no handler for layer %d\n", iLayerPlane
);
190 /***********************************************************************
191 * wglCopyContext (OPENGL32.@)
193 BOOL WINAPI
wglCopyContext(HGLRC hglrcSrc
,
196 FIXME("(%p,%p,%d)\n", hglrcSrc
, hglrcDst
, mask
);
201 /***********************************************************************
202 * wglDeleteContext (OPENGL32.@)
204 BOOL WINAPI
wglDeleteContext(HGLRC hglrc
)
206 Wine_GLContext
*ctx
= (Wine_GLContext
*) hglrc
;
209 TRACE("(%p)\n", hglrc
);
212 /* A game (Half Life not to name it) deletes twice the same context,
213 * so make sure it is valid first */
214 if (is_valid_context( ctx
))
216 if (ctx
->ctx
) glXDestroyContext(ctx
->display
, ctx
->ctx
);
221 WARN("Error deleting context !\n");
222 SetLastError(ERROR_INVALID_HANDLE
);
230 /***********************************************************************
231 * wglDescribeLayerPlane (OPENGL32.@)
233 BOOL WINAPI
wglDescribeLayerPlane(HDC hdc
,
237 LPLAYERPLANEDESCRIPTOR plpd
) {
238 FIXME("(%p,%d,%d,%d,%p)\n", hdc
, iPixelFormat
, iLayerPlane
, nBytes
, plpd
);
243 /***********************************************************************
244 * wglGetCurrentContext (OPENGL32.@)
246 HGLRC WINAPI
wglGetCurrentContext(void) {
253 gl_ctx
= glXGetCurrentContext();
254 ret
= get_context_from_GLXContext(gl_ctx
);
257 TRACE(" returning %p (GL context %p)\n", ret
, gl_ctx
);
262 /***********************************************************************
263 * wglGetCurrentDC (OPENGL32.@)
265 HDC WINAPI
wglGetCurrentDC(void) {
272 gl_ctx
= glXGetCurrentContext();
273 ret
= get_context_from_GLXContext(gl_ctx
);
277 TRACE(" returning %p (GL context %p - Wine context %p)\n", ret
->hdc
, gl_ctx
, ret
);
280 TRACE(" no Wine context found for GLX context %p\n", gl_ctx
);
285 /***********************************************************************
286 * wglGetLayerPaletteEntries (OPENGL32.@)
288 int WINAPI
wglGetLayerPaletteEntries(HDC hdc
,
292 const COLORREF
*pcr
) {
293 FIXME("(): stub !\n");
298 /***********************************************************************
299 * wglGetProcAddress (OPENGL32.@)
301 static int compar(const void *elt_a
, const void *elt_b
) {
302 return strcmp(((OpenGL_extension
*) elt_a
)->name
,
303 ((OpenGL_extension
*) elt_b
)->name
);
306 static int wgl_compar(const void *elt_a
, const void *elt_b
) {
307 return strcmp(((WGL_extension
*) elt_a
)->func_name
,
308 ((WGL_extension
*) elt_b
)->func_name
);
311 void* WINAPI
wglGetProcAddress(LPCSTR lpszProc
) {
313 static HMODULE hm
= 0;
314 OpenGL_extension ext
;
315 OpenGL_extension
*ext_ret
;
318 TRACE("(%s)\n", lpszProc
);
321 hm
= GetModuleHandleA("opengl32");
323 /* First, look if it's not already defined in the 'standard' OpenGL functions */
324 if ((local_func
= GetProcAddress(hm
, lpszProc
)) != NULL
) {
325 TRACE(" found function in 'standard' OpenGL functions (%p)\n", local_func
);
329 if (p_glXGetProcAddressARB
== NULL
) {
330 ERR("Warning : dynamic GL extension loading not supported by native GL library.\n");
334 /* After that, search in the thunks to find the real name of the extension */
335 ext
.name
= (char *) lpszProc
;
336 ext_ret
= (OpenGL_extension
*) bsearch(&ext
, extension_registry
,
337 extension_registry_size
, sizeof(OpenGL_extension
), compar
);
339 if (ext_ret
== NULL
) {
340 WGL_extension wgl_ext
, *wgl_ext_ret
;
342 /* Try to find the function in the WGL extensions ... */
343 wgl_ext
.func_name
= (char *) lpszProc
;
344 wgl_ext_ret
= (WGL_extension
*) bsearch(&wgl_ext
, wgl_extension_registry
,
345 wgl_extension_registry_size
, sizeof(WGL_extension
), wgl_compar
);
347 if (wgl_ext_ret
== NULL
) {
348 /* Some sanity checks :-) */
350 local_func
= p_glXGetProcAddressARB(lpszProc
);
352 if (local_func
!= NULL
) {
353 ERR("Extension %s defined in the OpenGL library but NOT in opengl_ext.c... Please report (lionel.ulmer@free.fr) !\n", lpszProc
);
357 WARN("Did not find extension %s in either Wine or your OpenGL library.\n", lpszProc
);
362 if (wgl_ext_ret
->func_init
!= NULL
) {
364 if ((err_msg
= wgl_ext_ret
->func_init(p_glXGetProcAddressARB
,
365 wgl_ext_ret
->context
)) == NULL
) {
366 ret
= wgl_ext_ret
->func_address
;
368 WARN("Error when getting WGL extension '%s' : %s.\n", debugstr_a(lpszProc
), err_msg
);
372 ret
= wgl_ext_ret
->func_address
;
376 TRACE(" returning WGL function (%p)\n", ret
);
381 local_func
= p_glXGetProcAddressARB(ext_ret
->glx_name
);
384 /* After that, look at the extensions defined in the Linux OpenGL library */
385 if (local_func
== NULL
) {
389 /* Remove the 3 last letters (EXT, ARB, ...).
391 I know that some extensions have more than 3 letters (MESA, NV,
392 INTEL, ...), but this is only a stop-gap measure to fix buggy
393 OpenGL drivers (moreover, it is only useful for old 1.0 apps
394 that query the glBindTextureEXT extension).
396 strncpy(buf
, ext_ret
->glx_name
, strlen(ext_ret
->glx_name
) - 3);
397 buf
[strlen(ext_ret
->glx_name
) - 3] = '\0';
398 TRACE(" extension not found in the Linux OpenGL library, checking against libGL bug with %s..\n", buf
);
400 ret
= GetProcAddress(hm
, buf
);
402 TRACE(" found function in main OpenGL library (%p) !\n", ret
);
404 WARN("Did not find function %s (%s) in your OpenGL library !\n", lpszProc
, ext_ret
->glx_name
);
409 TRACE(" returning function (%p)\n", ext_ret
->func
);
410 *(ext_ret
->func_ptr
) = local_func
;
412 return ext_ret
->func
;
417 /***********************************************************************
418 * wglMakeCurrent (OPENGL32.@)
420 BOOL WINAPI
wglMakeCurrent(HDC hdc
,
424 TRACE("(%p,%p)\n", hdc
, hglrc
);
428 ret
= glXMakeCurrent(default_display
, None
, NULL
);
430 Wine_GLContext
*ctx
= (Wine_GLContext
*) hglrc
;
431 Drawable drawable
= get_drawable( hdc
);
433 if (ctx
->ctx
== NULL
) {
434 ctx
->ctx
= glXCreateContext(ctx
->display
, ctx
->vis
, NULL
, True
);
435 TRACE(" created a delayed OpenGL context (%p)\n", ctx
->ctx
);
437 ret
= glXMakeCurrent(ctx
->display
, drawable
, ctx
->ctx
);
440 TRACE(" returning %s\n", (ret
? "True" : "False"));
444 /***********************************************************************
445 * wglRealizeLayerPalette (OPENGL32.@)
447 BOOL WINAPI
wglRealizeLayerPalette(HDC hdc
,
455 /***********************************************************************
456 * wglSetLayerPaletteEntries (OPENGL32.@)
458 int WINAPI
wglSetLayerPaletteEntries(HDC hdc
,
462 const COLORREF
*pcr
) {
463 FIXME("(): stub !\n");
468 /***********************************************************************
469 * wglShareLists (OPENGL32.@)
471 BOOL WINAPI
wglShareLists(HGLRC hglrc1
,
473 Wine_GLContext
*org
= (Wine_GLContext
*) hglrc1
;
474 Wine_GLContext
*dest
= (Wine_GLContext
*) hglrc2
;
476 TRACE("(%p, %p)\n", org
, dest
);
478 if (dest
->ctx
!= NULL
) {
479 ERR("Could not share display lists, context already created !\n");
482 if (org
->ctx
== NULL
) {
484 org
->ctx
= glXCreateContext(org
->display
, org
->vis
, NULL
, True
);
486 TRACE(" created a delayed OpenGL context (%p) for Wine context %p\n", org
->ctx
, org
);
490 /* Create the destination context with display lists shared */
491 dest
->ctx
= glXCreateContext(org
->display
, dest
->vis
, org
->ctx
, True
);
493 TRACE(" created a delayed OpenGL context (%p) for Wine context %p sharing lists with OpenGL ctx %p\n", dest
->ctx
, dest
, org
->ctx
);
499 /***********************************************************************
500 * wglSwapLayerBuffers (OPENGL32.@)
502 BOOL WINAPI
wglSwapLayerBuffers(HDC hdc
,
504 TRACE("(%p, %08x)\n", hdc
, fuPlanes
);
506 if (fuPlanes
& WGL_SWAP_MAIN_PLANE
) {
507 if (!SwapBuffers(hdc
)) return FALSE
;
508 fuPlanes
&= ~WGL_SWAP_MAIN_PLANE
;
512 WARN("Following layers unhandled : %08x\n", fuPlanes
);
518 static BOOL
internal_wglUseFontBitmaps(HDC hdc
,
522 DWORD
WINAPI (*GetGlyphOutline_ptr
)(HDC
,UINT
,UINT
,LPGLYPHMETRICS
,DWORD
,LPVOID
,const MAT2
*))
524 /* We are running using client-side rendering fonts... */
526 static const MAT2 id
= { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
529 void *bitmap
= NULL
, *gl_bitmap
= NULL
;
533 glGetIntegerv(GL_UNPACK_ALIGNMENT
, &org_alignment
);
534 glPixelStorei(GL_UNPACK_ALIGNMENT
, 4);
537 for (glyph
= first
; glyph
< first
+ count
; glyph
++) {
538 int needed_size
= GetGlyphOutline_ptr(hdc
, glyph
, GGO_BITMAP
, &gm
, 0, NULL
, &id
);
539 int height
, width_int
;
541 if (needed_size
== GDI_ERROR
) goto error
;
542 if (needed_size
> size
) {
544 if (bitmap
) HeapFree(GetProcessHeap(), 0, bitmap
);
545 if (gl_bitmap
) HeapFree(GetProcessHeap(), 0, gl_bitmap
);
546 bitmap
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, size
);
547 gl_bitmap
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, size
);
549 if (GetGlyphOutline_ptr(hdc
, glyph
, GGO_BITMAP
, &gm
, size
, bitmap
, &id
) == GDI_ERROR
) goto error
;
550 if (TRACE_ON(opengl
)) {
551 unsigned int height
, width
, bitmask
;
552 unsigned char *bitmap_
= (unsigned char *) bitmap
;
554 DPRINTF("Glyph : %d\n", glyph
);
555 DPRINTF(" - bbox : %d x %d\n", gm
.gmBlackBoxX
, gm
.gmBlackBoxY
);
556 DPRINTF(" - origin : (%ld , %ld)\n", gm
.gmptGlyphOrigin
.x
, gm
.gmptGlyphOrigin
.y
);
557 DPRINTF(" - increment : %d - %d\n", gm
.gmCellIncX
, gm
.gmCellIncY
);
558 DPRINTF(" - size : %d\n", needed_size
);
559 DPRINTF(" - bitmap : \n");
560 for (height
= 0; height
< gm
.gmBlackBoxY
; height
++) {
562 for (width
= 0, bitmask
= 0x80; width
< gm
.gmBlackBoxX
; width
++, bitmask
>>= 1) {
567 if (*bitmap_
& bitmask
)
572 bitmap_
+= (4 - (((unsigned int) bitmap_
) & 0x03));
577 /* For some obscure reasons, I seem to need to rotate the glyph for OpenGL to be happy.
578 As Wine does not seem to support the MAT2 field, I need to do it myself.... */
579 width_int
= (gm
.gmBlackBoxX
+ 31) / 32;
580 for (height
= 0; height
< gm
.gmBlackBoxY
; height
++) {
582 for (width
= 0; width
< width_int
; width
++) {
583 ((int *) gl_bitmap
)[(gm
.gmBlackBoxY
- height
- 1) * width_int
+ width
] =
584 ((int *) bitmap
)[height
* width_int
+ width
];
589 glNewList(listBase
++, GL_COMPILE
);
590 glBitmap(gm
.gmBlackBoxX
, gm
.gmBlackBoxY
, gm
.gmptGlyphOrigin
.x
,
591 gm
.gmBlackBoxY
- gm
.gmptGlyphOrigin
.y
, gm
.gmCellIncX
, gm
.gmCellIncY
, gl_bitmap
);
597 glPixelStorei(GL_UNPACK_ALIGNMENT
, org_alignment
);
600 if (bitmap
) HeapFree(GetProcessHeap(), 0, bitmap
);
601 if (gl_bitmap
) HeapFree(GetProcessHeap(), 0, gl_bitmap
);
606 glPixelStorei(GL_UNPACK_ALIGNMENT
, org_alignment
);
609 if (bitmap
) HeapFree(GetProcessHeap(), 0, bitmap
);
610 if (gl_bitmap
) HeapFree(GetProcessHeap(), 0, gl_bitmap
);
614 /***********************************************************************
615 * wglUseFontBitmapsA (OPENGL32.@)
617 BOOL WINAPI
wglUseFontBitmapsA(HDC hdc
,
622 Font fid
= get_font( hdc
);
624 TRACE("(%p, %ld, %ld, %ld) using font %ld\n", hdc
, first
, count
, listBase
, fid
);
627 return internal_wglUseFontBitmaps(hdc
, first
, count
, listBase
, GetGlyphOutlineA
);
631 /* I assume that the glyphs are at the same position for X and for Windows */
632 glXUseXFont(fid
, first
, count
, listBase
);
637 /***********************************************************************
638 * wglUseFontBitmapsW (OPENGL32.@)
640 BOOL WINAPI
wglUseFontBitmapsW(HDC hdc
,
645 Font fid
= get_font( hdc
);
647 TRACE("(%p, %ld, %ld, %ld) using font %ld\n", hdc
, first
, count
, listBase
, fid
);
650 return internal_wglUseFontBitmaps(hdc
, first
, count
, listBase
, GetGlyphOutlineW
);
653 WARN("Using the glX API for the WCHAR variant - some characters may come out incorrectly !\n");
656 /* I assume that the glyphs are at the same position for X and for Windows */
657 glXUseXFont(fid
, first
, count
, listBase
);
662 /***********************************************************************
663 * wglUseFontOutlinesA (OPENGL32.@)
665 BOOL WINAPI
wglUseFontOutlinesA(HDC hdc
,
672 LPGLYPHMETRICSFLOAT lpgmf
) {
673 FIXME("(): stub !\n");
678 /* No need to load any other libraries as according to the ABI, libGL should be self-sufficient and
679 include all dependencies
682 #define SONAME_LIBGL "libGL.so"
685 /* This is for brain-dead applications that use OpenGL functions before even
686 creating a rendering context.... */
687 static BOOL
process_attach(void)
689 XWindowAttributes win_attr
;
692 XVisualInfo
template;
694 XVisualInfo
*vis
= NULL
;
695 Window root
= (Window
)GetPropA( GetDesktopWindow(), "__wine_x11_whole_window" );
696 HMODULE mod
= GetModuleHandleA( "x11drv.dll" );
701 ERR("X11DRV not loaded. Cannot create default context.\n");
705 wine_tsx11_lock_ptr
= (void *)GetProcAddress( mod
, "wine_tsx11_lock" );
706 wine_tsx11_unlock_ptr
= (void *)GetProcAddress( mod
, "wine_tsx11_unlock" );
709 default_display
= get_display( hdc
);
711 if (!default_display
)
713 ERR("X11DRV not loaded. Cannot get display for screen DC.\n");
719 /* Try to get the visual from the Root Window. We can't use the standard (presumably
720 double buffered) X11DRV visual with the Root Window, since we don't know if the Root
721 Window was created using the standard X11DRV visual, and glXMakeCurrent can't deal
722 with mismatched visuals. Note that the Root Window visual may not be double
723 buffered, so apps actually attempting to render this way may flicker */
724 if (XGetWindowAttributes( default_display
, root
, &win_attr
))
726 rootVisual
= win_attr
.visual
;
730 /* Get the default visual, since we can't seem to get the attributes from the
731 Root Window. Let's hope that the Root Window Visual matches the DefaultVisual */
732 rootVisual
= DefaultVisual( default_display
, DefaultScreen(default_display
) );
735 template.visualid
= XVisualIDFromVisual(rootVisual
);
736 vis
= XGetVisualInfo(default_display
, VisualIDMask
, &template, &num
);
737 if (vis
!= NULL
) default_cx
= glXCreateContext(default_display
, vis
, 0, GL_TRUE
);
738 if (default_cx
!= NULL
) glXMakeCurrent(default_display
, root
, default_cx
);
742 opengl_handle
= wine_dlopen(SONAME_LIBGL
, RTLD_NOW
|RTLD_GLOBAL
, NULL
, 0);
743 if (opengl_handle
!= NULL
) {
744 p_glXGetProcAddressARB
= wine_dlsym(opengl_handle
, "glXGetProcAddressARB", NULL
, 0);
745 wine_dlclose(opengl_handle
, NULL
, 0);
746 if (p_glXGetProcAddressARB
== NULL
)
747 TRACE("could not find glXGetProcAddressARB in libGL.\n");
750 /* Initialize also the list of supported WGL extensions. */
751 wgl_ext_initialize_extensions(default_display
, DefaultScreen(default_display
));
753 if (default_cx
== NULL
) {
754 ERR("Could not create default context.\n");
760 /**********************************************************************/
762 static void process_detach(void)
764 glXDestroyContext(default_display
, default_cx
);
766 /* Do not leak memory... */
767 wgl_ext_finalize_extensions();
770 /***********************************************************************
771 * OpenGL initialisation routine
773 BOOL WINAPI
DllMain( HINSTANCE hinst
, DWORD reason
, LPVOID reserved
)
777 case DLL_PROCESS_ATTACH
:
778 DisableThreadLibraryCalls(hinst
);
779 return process_attach();
780 case DLL_PROCESS_DETACH
: