msxml4/tests: Copy namespaces as attributes tests.
[wine.git] / dlls / opengl32 / wgl.c
blobb3cffa00a3ec9c7d6398c840f906b17113ca255e
1 /* Window-specific OpenGL functions implementation.
3 * Copyright (c) 1999 Lionel Ulmer
4 * Copyright (c) 2005 Raphael Junqueira
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <math.h>
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "ntuser.h"
31 #include "malloc.h"
33 #include "unixlib.h"
34 #include "private.h"
36 #include "wine/glu.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(opengl);
40 WINE_DECLARE_DEBUG_CHANNEL(fps);
42 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
44 #ifndef _WIN64
46 static char **wow64_strings;
47 static SIZE_T wow64_strings_count;
49 static CRITICAL_SECTION wow64_cs;
50 static CRITICAL_SECTION_DEBUG wow64_cs_debug =
52 0, 0, &wow64_cs,
53 { &wow64_cs_debug.ProcessLocksList, &wow64_cs_debug.ProcessLocksList },
54 0, 0, { (DWORD_PTR)(__FILE__ ": wow64_cs") }
56 static CRITICAL_SECTION wow64_cs = { &wow64_cs_debug, -1, 0, 0, 0, 0 };
58 static void append_wow64_string( char *str )
60 char **tmp;
62 EnterCriticalSection( &wow64_cs );
64 if (!(tmp = realloc( wow64_strings, (wow64_strings_count + 1) * sizeof(*wow64_strings) )))
65 ERR( "Failed to allocate memory for wow64 strings\n" );
66 else
68 wow64_strings = tmp;
69 wow64_strings[wow64_strings_count] = str;
70 wow64_strings_count += 1;
73 LeaveCriticalSection( &wow64_cs );
76 static void cleanup_wow64_strings(void)
78 while (wow64_strings_count--) free( wow64_strings[wow64_strings_count] );
79 free( wow64_strings );
82 #endif
84 /***********************************************************************
85 * wglGetCurrentReadDCARB
87 * Provided by the WGL_ARB_make_current_read extension.
89 HDC WINAPI wglGetCurrentReadDCARB(void)
91 if (!NtCurrentTeb()->glCurrentRC) return 0;
92 return NtCurrentTeb()->glReserved1[1];
95 /***********************************************************************
96 * wglGetCurrentDC (OPENGL32.@)
98 HDC WINAPI wglGetCurrentDC(void)
100 if (!NtCurrentTeb()->glCurrentRC) return 0;
101 return NtCurrentTeb()->glReserved1[0];
104 /***********************************************************************
105 * wglGetCurrentContext (OPENGL32.@)
107 HGLRC WINAPI wglGetCurrentContext(void)
109 return NtCurrentTeb()->glCurrentRC;
112 /***********************************************************************
113 * wglChoosePixelFormat (OPENGL32.@)
115 INT WINAPI wglChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd)
117 PIXELFORMATDESCRIPTOR format, best;
118 int i, count, best_format;
119 int bestDBuffer = -1, bestStereo = -1;
121 TRACE( "%p %p: size %u version %u flags %lu type %u color %u %u,%u,%u,%u "
122 "accum %u depth %u stencil %u aux %u\n",
123 hdc, ppfd, ppfd->nSize, ppfd->nVersion, ppfd->dwFlags, ppfd->iPixelType,
124 ppfd->cColorBits, ppfd->cRedBits, ppfd->cGreenBits, ppfd->cBlueBits, ppfd->cAlphaBits,
125 ppfd->cAccumBits, ppfd->cDepthBits, ppfd->cStencilBits, ppfd->cAuxBuffers );
127 count = wglDescribePixelFormat( hdc, 0, 0, NULL );
128 if (!count) return 0;
130 best_format = 0;
131 best.dwFlags = 0;
132 best.cAlphaBits = -1;
133 best.cColorBits = -1;
134 best.cDepthBits = -1;
135 best.cStencilBits = -1;
136 best.cAuxBuffers = -1;
138 for (i = 1; i <= count; i++)
140 if (!wglDescribePixelFormat( hdc, i, sizeof(format), &format )) continue;
142 if ((ppfd->iPixelType == PFD_TYPE_COLORINDEX) != (format.iPixelType == PFD_TYPE_COLORINDEX))
144 TRACE( "pixel type mismatch for iPixelFormat=%d\n", i );
145 continue;
148 if ((ppfd->dwFlags & PFD_DRAW_TO_BITMAP) && !(format.dwFlags & PFD_DRAW_TO_BITMAP))
150 TRACE( "PFD_DRAW_TO_BITMAP required but not found for iPixelFormat=%d\n", i );
151 continue;
153 if ((ppfd->dwFlags & PFD_DRAW_TO_WINDOW) && !(format.dwFlags & PFD_DRAW_TO_WINDOW))
155 TRACE( "PFD_DRAW_TO_WINDOW required but not found for iPixelFormat=%d\n", i );
156 continue;
159 if ((ppfd->dwFlags & PFD_SUPPORT_GDI) && !(format.dwFlags & PFD_SUPPORT_GDI))
161 TRACE( "PFD_SUPPORT_GDI required but not found for iPixelFormat=%d\n", i );
162 continue;
164 if ((ppfd->dwFlags & PFD_SUPPORT_OPENGL) && !(format.dwFlags & PFD_SUPPORT_OPENGL))
166 TRACE( "PFD_SUPPORT_OPENGL required but not found for iPixelFormat=%d\n", i );
167 continue;
170 /* The behavior of PDF_STEREO/PFD_STEREO_DONTCARE and PFD_DOUBLEBUFFER / PFD_DOUBLEBUFFER_DONTCARE
171 * is not very clear on MSDN. They specify that ChoosePixelFormat tries to match pixel formats
172 * with the flag (PFD_STEREO / PFD_DOUBLEBUFFERING) set. Otherwise it says that it tries to match
173 * formats without the given flag set.
174 * A test on Windows using a Radeon 9500pro on WinXP (the driver doesn't support Stereo)
175 * has indicated that a format without stereo is returned when stereo is unavailable.
176 * So in case PFD_STEREO is set, formats that support it should have priority above formats
177 * without. In case PFD_STEREO_DONTCARE is set, stereo is ignored.
179 * To summarize the following is most likely the correct behavior:
180 * stereo not set -> prefer non-stereo formats, but also accept stereo formats
181 * stereo set -> prefer stereo formats, but also accept non-stereo formats
182 * stereo don't care -> it doesn't matter whether we get stereo or not
184 * In Wine we will treat non-stereo the same way as don't care because it makes
185 * format selection even more complicated and second drivers with Stereo advertise
186 * each format twice anyway.
189 /* Doublebuffer, see the comments above */
190 if (!(ppfd->dwFlags & PFD_DOUBLEBUFFER_DONTCARE))
192 if (((ppfd->dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) &&
193 ((format.dwFlags & PFD_DOUBLEBUFFER) == (ppfd->dwFlags & PFD_DOUBLEBUFFER)))
194 goto found;
196 if (bestDBuffer != -1 && (format.dwFlags & PFD_DOUBLEBUFFER) != bestDBuffer) continue;
198 else if (!best_format)
199 goto found;
201 /* Stereo, see the comments above. */
202 if (!(ppfd->dwFlags & PFD_STEREO_DONTCARE))
204 if (((ppfd->dwFlags & PFD_STEREO) != bestStereo) &&
205 ((format.dwFlags & PFD_STEREO) == (ppfd->dwFlags & PFD_STEREO)))
206 goto found;
208 if (bestStereo != -1 && (format.dwFlags & PFD_STEREO) != bestStereo) continue;
210 else if (!best_format)
211 goto found;
213 /* Below we will do a number of checks to select the 'best' pixelformat.
214 * We assume the precedence cColorBits > cAlphaBits > cDepthBits > cStencilBits -> cAuxBuffers.
215 * The code works by trying to match the most important options as close as possible.
216 * When a reasonable format is found, we will try to match more options.
217 * It appears (see the opengl32 test) that Windows opengl drivers ignore options
218 * like cColorBits, cAlphaBits and friends if they are set to 0, so they are considered
219 * as DONTCARE. At least Serious Sam TSE relies on this behavior. */
221 if (ppfd->cColorBits)
223 if (((ppfd->cColorBits > best.cColorBits) && (format.cColorBits > best.cColorBits)) ||
224 ((format.cColorBits >= ppfd->cColorBits) && (format.cColorBits < best.cColorBits)))
225 goto found;
227 if (best.cColorBits != format.cColorBits) /* Do further checks if the format is compatible */
229 TRACE( "color mismatch for iPixelFormat=%d\n", i );
230 continue;
233 if (ppfd->cAlphaBits)
235 if (((ppfd->cAlphaBits > best.cAlphaBits) && (format.cAlphaBits > best.cAlphaBits)) ||
236 ((format.cAlphaBits >= ppfd->cAlphaBits) && (format.cAlphaBits < best.cAlphaBits)))
237 goto found;
239 if (best.cAlphaBits != format.cAlphaBits)
241 TRACE( "alpha mismatch for iPixelFormat=%d\n", i );
242 continue;
245 if (ppfd->cStencilBits)
247 if (((ppfd->cStencilBits > best.cStencilBits) && (format.cStencilBits > best.cStencilBits)) ||
248 ((format.cStencilBits >= ppfd->cStencilBits) && (format.cStencilBits < best.cStencilBits)))
249 goto found;
251 if (best.cStencilBits != format.cStencilBits)
253 TRACE( "stencil mismatch for iPixelFormat=%d\n", i );
254 continue;
257 if (ppfd->cDepthBits && !(ppfd->dwFlags & PFD_DEPTH_DONTCARE))
259 if (((ppfd->cDepthBits > best.cDepthBits) && (format.cDepthBits > best.cDepthBits)) ||
260 ((format.cDepthBits >= ppfd->cDepthBits) && (format.cDepthBits < best.cDepthBits)))
261 goto found;
263 if (best.cDepthBits != format.cDepthBits)
265 TRACE( "depth mismatch for iPixelFormat=%d\n", i );
266 continue;
269 if (ppfd->cAuxBuffers)
271 if (((ppfd->cAuxBuffers > best.cAuxBuffers) && (format.cAuxBuffers > best.cAuxBuffers)) ||
272 ((format.cAuxBuffers >= ppfd->cAuxBuffers) && (format.cAuxBuffers < best.cAuxBuffers)))
273 goto found;
275 if (best.cAuxBuffers != format.cAuxBuffers)
277 TRACE( "aux mismatch for iPixelFormat=%d\n", i );
278 continue;
281 if (ppfd->dwFlags & PFD_DEPTH_DONTCARE && format.cDepthBits < best.cDepthBits)
282 goto found;
284 continue;
286 found:
287 best_format = i;
288 best = format;
289 bestDBuffer = format.dwFlags & PFD_DOUBLEBUFFER;
290 bestStereo = format.dwFlags & PFD_STEREO;
293 TRACE( "returning %u\n", best_format );
294 return best_format;
297 /***********************************************************************
298 * wglGetPixelFormat (OPENGL32.@)
300 INT WINAPI wglGetPixelFormat(HDC hdc)
302 struct wglGetPixelFormat_params args = { .teb = NtCurrentTeb(), .hdc = hdc };
303 NTSTATUS status;
305 TRACE( "hdc %p\n", hdc );
307 if ((status = UNIX_CALL( wglGetPixelFormat, &args )))
309 WARN( "wglGetPixelFormat returned %#lx\n", status );
310 SetLastError( ERROR_INVALID_PIXEL_FORMAT );
313 return args.ret;
316 /***********************************************************************
317 * wglSwapBuffers (OPENGL32.@)
319 BOOL WINAPI DECLSPEC_HOTPATCH wglSwapBuffers( HDC hdc )
321 struct wglSwapBuffers_params args = { .teb = NtCurrentTeb(), .hdc = hdc };
322 NTSTATUS status;
324 if ((status = UNIX_CALL( wglSwapBuffers, &args ))) WARN( "wglSwapBuffers returned %#lx\n", status );
325 else if (TRACE_ON(fps))
327 static long prev_time, start_time;
328 static unsigned long frames, frames_total;
330 DWORD time = GetTickCount();
331 frames++;
332 frames_total++;
333 /* every 1.5 seconds */
334 if (time - prev_time > 1500)
336 TRACE_(fps)("@ approx %.2ffps, total %.2ffps\n",
337 1000.0*frames/(time - prev_time), 1000.0*frames_total/(time - start_time));
338 prev_time = time;
339 frames = 0;
340 if (start_time == 0) start_time = time;
344 return args.ret;
347 /***********************************************************************
348 * wglCreateLayerContext (OPENGL32.@)
350 HGLRC WINAPI wglCreateLayerContext( HDC hdc, int iLayerPlane )
352 TRACE("(%p,%d)\n", hdc, iLayerPlane);
354 if (iLayerPlane == 0) return wglCreateContext( hdc );
356 FIXME("no handler for layer %d\n", iLayerPlane);
357 return NULL;
360 /***********************************************************************
361 * wglDescribeLayerPlane (OPENGL32.@)
363 BOOL WINAPI wglDescribeLayerPlane(HDC hdc,
364 int iPixelFormat,
365 int iLayerPlane,
366 UINT nBytes,
367 LPLAYERPLANEDESCRIPTOR plpd) {
368 FIXME("(%p,%d,%d,%d,%p)\n", hdc, iPixelFormat, iLayerPlane, nBytes, plpd);
370 return FALSE;
373 /***********************************************************************
374 * wglGetLayerPaletteEntries (OPENGL32.@)
376 int WINAPI wglGetLayerPaletteEntries(HDC hdc,
377 int iLayerPlane,
378 int iStart,
379 int cEntries,
380 const COLORREF *pcr) {
381 FIXME("(): stub!\n");
383 return 0;
386 /***********************************************************************
387 * wglGetProcAddress (OPENGL32.@)
389 PROC WINAPI wglGetProcAddress( LPCSTR name )
391 struct wglGetProcAddress_params args = { .teb = NtCurrentTeb(), .lpszProc = name };
392 const void *proc;
393 NTSTATUS status;
395 if (!name) return NULL;
396 if ((status = UNIX_CALL( wglGetProcAddress, &args )))
397 WARN( "wglGetProcAddress %s returned %#lx\n", debugstr_a(name), status );
398 if (args.ret == (void *)-1) return NULL;
400 proc = extension_procs[(UINT_PTR)args.ret];
401 TRACE( "returning %s -> %p\n", name, proc );
402 return proc;
405 /***********************************************************************
406 * wglRealizeLayerPalette (OPENGL32.@)
408 BOOL WINAPI wglRealizeLayerPalette(HDC hdc,
409 int iLayerPlane,
410 BOOL bRealize) {
411 FIXME("()\n");
413 return FALSE;
416 /***********************************************************************
417 * wglSetLayerPaletteEntries (OPENGL32.@)
419 int WINAPI wglSetLayerPaletteEntries(HDC hdc,
420 int iLayerPlane,
421 int iStart,
422 int cEntries,
423 const COLORREF *pcr) {
424 FIXME("(): stub!\n");
426 return 0;
429 /***********************************************************************
430 * wglGetDefaultProcAddress (OPENGL32.@)
432 PROC WINAPI wglGetDefaultProcAddress( LPCSTR name )
434 FIXME( "%s: stub\n", debugstr_a(name));
435 return NULL;
438 /***********************************************************************
439 * wglSwapLayerBuffers (OPENGL32.@)
441 BOOL WINAPI wglSwapLayerBuffers(HDC hdc,
442 UINT fuPlanes) {
443 TRACE("(%p, %08x)\n", hdc, fuPlanes);
445 if (fuPlanes & WGL_SWAP_MAIN_PLANE) {
446 if (!wglSwapBuffers( hdc )) return FALSE;
447 fuPlanes &= ~WGL_SWAP_MAIN_PLANE;
450 if (fuPlanes) {
451 WARN("Following layers unhandled: %08x\n", fuPlanes);
454 return TRUE;
457 /***********************************************************************
458 * wglUseFontBitmaps_common
460 static BOOL wglUseFontBitmaps_common( HDC hdc, DWORD first, DWORD count, DWORD listBase, BOOL unicode )
462 GLYPHMETRICS gm;
463 unsigned int glyph, size = 0;
464 void *bitmap = NULL, *gl_bitmap = NULL;
465 int org_alignment;
466 BOOL ret = TRUE;
468 glGetIntegerv( GL_UNPACK_ALIGNMENT, &org_alignment );
469 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
471 for (glyph = first; glyph < first + count; glyph++) {
472 unsigned int needed_size, height, width, width_int;
474 if (unicode)
475 needed_size = GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
476 else
477 needed_size = GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm, 0, NULL, &identity);
479 TRACE("Glyph: %3d / List: %ld size %d\n", glyph, listBase, needed_size);
480 if (needed_size == GDI_ERROR) {
481 ret = FALSE;
482 break;
485 if (needed_size > size) {
486 size = needed_size;
487 free( bitmap );
488 free( gl_bitmap );
489 bitmap = calloc( 1, size );
490 gl_bitmap = calloc( 1, size );
492 if (needed_size != 0) {
493 if (unicode)
494 ret = (GetGlyphOutlineW(hdc, glyph, GGO_BITMAP, &gm,
495 size, bitmap, &identity) != GDI_ERROR);
496 else
497 ret = (GetGlyphOutlineA(hdc, glyph, GGO_BITMAP, &gm,
498 size, bitmap, &identity) != GDI_ERROR);
499 if (!ret) break;
502 if (TRACE_ON(opengl))
504 unsigned int bitmask;
505 unsigned char *bitmap_ = bitmap;
507 TRACE(" - bbox: %d x %d\n", gm.gmBlackBoxX, gm.gmBlackBoxY);
508 TRACE(" - origin: (%ld, %ld)\n", gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
509 TRACE(" - increment: %d - %d\n", gm.gmCellIncX, gm.gmCellIncY);
510 if (needed_size != 0) {
511 TRACE(" - bitmap:\n");
512 for (height = 0; height < gm.gmBlackBoxY; height++) {
513 TRACE(" ");
514 for (width = 0, bitmask = 0x80; width < gm.gmBlackBoxX; width++, bitmask >>= 1) {
515 if (bitmask == 0) {
516 bitmap_ += 1;
517 bitmask = 0x80;
519 if (*bitmap_ & bitmask)
520 TRACE("*");
521 else
522 TRACE(" ");
524 bitmap_ += (4 - ((UINT_PTR)bitmap_ & 0x03));
525 TRACE("\n");
530 /* In OpenGL, the bitmap is drawn from the bottom to the top... So we need to invert the
531 * glyph for it to be drawn properly.
533 if (needed_size != 0) {
534 width_int = (gm.gmBlackBoxX + 31) / 32;
535 for (height = 0; height < gm.gmBlackBoxY; height++) {
536 for (width = 0; width < width_int; width++) {
537 ((int *) gl_bitmap)[(gm.gmBlackBoxY - height - 1) * width_int + width] =
538 ((int *) bitmap)[height * width_int + width];
543 glNewList( listBase++, GL_COMPILE );
544 if (needed_size != 0) {
545 glBitmap( gm.gmBlackBoxX, gm.gmBlackBoxY, 0 - gm.gmptGlyphOrigin.x,
546 (int)gm.gmBlackBoxY - gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY, gl_bitmap );
547 } else {
548 /* This is the case of 'empty' glyphs like the space character */
549 glBitmap( 0, 0, 0, 0, gm.gmCellIncX, gm.gmCellIncY, NULL );
551 glEndList();
554 glPixelStorei( GL_UNPACK_ALIGNMENT, org_alignment );
555 free( bitmap );
556 free( gl_bitmap );
557 return ret;
560 /***********************************************************************
561 * wglUseFontBitmapsA (OPENGL32.@)
563 BOOL WINAPI wglUseFontBitmapsA(HDC hdc, DWORD first, DWORD count, DWORD listBase)
565 return wglUseFontBitmaps_common( hdc, first, count, listBase, FALSE );
568 /***********************************************************************
569 * wglUseFontBitmapsW (OPENGL32.@)
571 BOOL WINAPI wglUseFontBitmapsW(HDC hdc, DWORD first, DWORD count, DWORD listBase)
573 return wglUseFontBitmaps_common( hdc, first, count, listBase, TRUE );
576 static void fixed_to_double(POINTFX fixed, UINT em_size, GLdouble vertex[3])
578 vertex[0] = (fixed.x.value + (GLdouble)fixed.x.fract / (1 << 16)) / em_size;
579 vertex[1] = (fixed.y.value + (GLdouble)fixed.y.fract / (1 << 16)) / em_size;
580 vertex[2] = 0.0;
583 typedef struct _bezier_vector {
584 GLdouble x;
585 GLdouble y;
586 } bezier_vector;
588 static double bezier_deviation_squared(const bezier_vector *p)
590 bezier_vector deviation;
591 bezier_vector vertex;
592 bezier_vector base;
593 double base_length;
594 double dot;
596 vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4 - p[0].x;
597 vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4 - p[0].y;
599 base.x = p[2].x - p[0].x;
600 base.y = p[2].y - p[0].y;
602 base_length = sqrt(base.x*base.x + base.y*base.y);
603 base.x /= base_length;
604 base.y /= base_length;
606 dot = base.x*vertex.x + base.y*vertex.y;
607 dot = min(max(dot, 0.0), base_length);
608 base.x *= dot;
609 base.y *= dot;
611 deviation.x = vertex.x-base.x;
612 deviation.y = vertex.y-base.y;
614 return deviation.x*deviation.x + deviation.y*deviation.y;
617 static int bezier_approximate(const bezier_vector *p, bezier_vector *points, FLOAT deviation)
619 bezier_vector first_curve[3];
620 bezier_vector second_curve[3];
621 bezier_vector vertex;
622 int total_vertices;
624 if(bezier_deviation_squared(p) <= deviation*deviation)
626 if(points)
627 *points = p[2];
628 return 1;
631 vertex.x = (p[0].x + p[1].x*2 + p[2].x)/4;
632 vertex.y = (p[0].y + p[1].y*2 + p[2].y)/4;
634 first_curve[0] = p[0];
635 first_curve[1].x = (p[0].x + p[1].x)/2;
636 first_curve[1].y = (p[0].y + p[1].y)/2;
637 first_curve[2] = vertex;
639 second_curve[0] = vertex;
640 second_curve[1].x = (p[2].x + p[1].x)/2;
641 second_curve[1].y = (p[2].y + p[1].y)/2;
642 second_curve[2] = p[2];
644 total_vertices = bezier_approximate(first_curve, points, deviation);
645 if(points)
646 points += total_vertices;
647 total_vertices += bezier_approximate(second_curve, points, deviation);
648 return total_vertices;
651 /***********************************************************************
652 * wglUseFontOutlines_common
654 static BOOL wglUseFontOutlines_common(HDC hdc,
655 DWORD first,
656 DWORD count,
657 DWORD listBase,
658 FLOAT deviation,
659 FLOAT extrusion,
660 int format,
661 LPGLYPHMETRICSFLOAT lpgmf,
662 BOOL unicode)
664 UINT glyph;
665 GLUtesselator *tess = NULL;
666 LOGFONTW lf;
667 HFONT old_font, unscaled_font;
668 UINT em_size = 1024;
669 RECT rc;
671 TRACE("(%p, %ld, %ld, %ld, %f, %f, %d, %p, %s)\n", hdc, first, count,
672 listBase, deviation, extrusion, format, lpgmf, unicode ? "W" : "A");
674 if(deviation <= 0.0)
675 deviation = 1.0/em_size;
677 if(format == WGL_FONT_POLYGONS)
679 tess = gluNewTess();
680 if(!tess)
682 ERR("glu32 is required for this function but isn't available\n");
683 return FALSE;
685 gluTessCallback( tess, GLU_TESS_VERTEX, (void *)glVertex3dv );
686 gluTessCallback( tess, GLU_TESS_BEGIN, (void *)glBegin );
687 gluTessCallback( tess, GLU_TESS_END, glEnd );
690 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
691 rc.left = rc.right = rc.bottom = 0;
692 rc.top = em_size;
693 DPtoLP(hdc, (POINT*)&rc, 2);
694 lf.lfHeight = -abs(rc.top - rc.bottom);
695 lf.lfOrientation = lf.lfEscapement = 0;
696 unscaled_font = CreateFontIndirectW(&lf);
697 old_font = SelectObject(hdc, unscaled_font);
699 for (glyph = first; glyph < first + count; glyph++)
701 DWORD needed;
702 GLYPHMETRICS gm;
703 BYTE *buf;
704 TTPOLYGONHEADER *pph;
705 TTPOLYCURVE *ppc;
706 GLdouble *vertices = NULL;
707 int vertex_total = -1;
709 if(unicode)
710 needed = GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
711 else
712 needed = GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, 0, NULL, &identity);
714 if(needed == GDI_ERROR)
715 goto error;
717 buf = malloc( needed );
719 if(unicode)
720 GetGlyphOutlineW(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
721 else
722 GetGlyphOutlineA(hdc, glyph, GGO_NATIVE, &gm, needed, buf, &identity);
724 TRACE("glyph %d\n", glyph);
726 if(lpgmf)
728 lpgmf->gmfBlackBoxX = (float)gm.gmBlackBoxX / em_size;
729 lpgmf->gmfBlackBoxY = (float)gm.gmBlackBoxY / em_size;
730 lpgmf->gmfptGlyphOrigin.x = (float)gm.gmptGlyphOrigin.x / em_size;
731 lpgmf->gmfptGlyphOrigin.y = (float)gm.gmptGlyphOrigin.y / em_size;
732 lpgmf->gmfCellIncX = (float)gm.gmCellIncX / em_size;
733 lpgmf->gmfCellIncY = (float)gm.gmCellIncY / em_size;
735 TRACE("%fx%f at %f,%f inc %f,%f\n", lpgmf->gmfBlackBoxX, lpgmf->gmfBlackBoxY,
736 lpgmf->gmfptGlyphOrigin.x, lpgmf->gmfptGlyphOrigin.y, lpgmf->gmfCellIncX, lpgmf->gmfCellIncY);
737 lpgmf++;
740 glNewList( listBase++, GL_COMPILE );
741 glFrontFace( GL_CCW );
742 if(format == WGL_FONT_POLYGONS)
744 glNormal3d( 0.0, 0.0, 1.0 );
745 gluTessNormal(tess, 0, 0, 1);
746 gluTessBeginPolygon(tess, NULL);
749 while(!vertices)
751 if (vertex_total != -1) vertices = malloc( vertex_total * 3 * sizeof(GLdouble) );
752 vertex_total = 0;
754 pph = (TTPOLYGONHEADER*)buf;
755 while((BYTE*)pph < buf + needed)
757 GLdouble previous[3];
758 fixed_to_double(pph->pfxStart, em_size, previous);
760 if(vertices)
761 TRACE("\tstart %d, %d\n", pph->pfxStart.x.value, pph->pfxStart.y.value);
763 if (format == WGL_FONT_POLYGONS) gluTessBeginContour( tess );
764 else glBegin( GL_LINE_LOOP );
766 if(vertices)
768 fixed_to_double(pph->pfxStart, em_size, vertices);
769 if (format == WGL_FONT_POLYGONS) gluTessVertex( tess, vertices, vertices );
770 else glVertex3d( vertices[0], vertices[1], vertices[2] );
771 vertices += 3;
773 vertex_total++;
775 ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
776 while((char*)ppc < (char*)pph + pph->cb)
778 int i, j;
779 int num;
781 switch(ppc->wType) {
782 case TT_PRIM_LINE:
783 for(i = 0; i < ppc->cpfx; i++)
785 if(vertices)
787 TRACE("\t\tline to %d, %d\n",
788 ppc->apfx[i].x.value, ppc->apfx[i].y.value);
789 fixed_to_double(ppc->apfx[i], em_size, vertices);
790 if (format == WGL_FONT_POLYGONS) gluTessVertex( tess, vertices, vertices );
791 else glVertex3d( vertices[0], vertices[1], vertices[2] );
792 vertices += 3;
794 fixed_to_double(ppc->apfx[i], em_size, previous);
795 vertex_total++;
797 break;
799 case TT_PRIM_QSPLINE:
800 for(i = 0; i < ppc->cpfx-1; i++)
802 bezier_vector curve[3];
803 bezier_vector *points;
804 GLdouble curve_vertex[3];
806 if(vertices)
807 TRACE("\t\tcurve %d,%d %d,%d\n",
808 ppc->apfx[i].x.value, ppc->apfx[i].y.value,
809 ppc->apfx[i + 1].x.value, ppc->apfx[i + 1].y.value);
811 curve[0].x = previous[0];
812 curve[0].y = previous[1];
813 fixed_to_double(ppc->apfx[i], em_size, curve_vertex);
814 curve[1].x = curve_vertex[0];
815 curve[1].y = curve_vertex[1];
816 fixed_to_double(ppc->apfx[i + 1], em_size, curve_vertex);
817 curve[2].x = curve_vertex[0];
818 curve[2].y = curve_vertex[1];
819 if(i < ppc->cpfx-2)
821 curve[2].x = (curve[1].x + curve[2].x)/2;
822 curve[2].y = (curve[1].y + curve[2].y)/2;
824 num = bezier_approximate(curve, NULL, deviation);
825 points = malloc( num * sizeof(bezier_vector) );
826 num = bezier_approximate(curve, points, deviation);
827 vertex_total += num;
828 if(vertices)
830 for(j=0; j<num; j++)
832 TRACE("\t\t\tvertex at %f,%f\n", points[j].x, points[j].y);
833 vertices[0] = points[j].x;
834 vertices[1] = points[j].y;
835 vertices[2] = 0.0;
836 if (format == WGL_FONT_POLYGONS) gluTessVertex( tess, vertices, vertices );
837 else glVertex3d( vertices[0], vertices[1], vertices[2] );
838 vertices += 3;
841 free( points );
842 previous[0] = curve[2].x;
843 previous[1] = curve[2].y;
845 break;
846 default:
847 ERR("\t\tcurve type = %d\n", ppc->wType);
848 if (format == WGL_FONT_POLYGONS) gluTessEndContour( tess );
849 else glEnd();
850 goto error_in_list;
853 ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
854 (ppc->cpfx - 1) * sizeof(POINTFX));
856 if (format == WGL_FONT_POLYGONS) gluTessEndContour( tess );
857 else glEnd();
858 pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
862 error_in_list:
863 if (format == WGL_FONT_POLYGONS) gluTessEndPolygon( tess );
864 glTranslated( (GLdouble)gm.gmCellIncX / em_size, (GLdouble)gm.gmCellIncY / em_size, 0.0 );
865 glEndList();
866 free( buf );
867 free( vertices );
870 error:
871 DeleteObject(SelectObject(hdc, old_font));
872 if(format == WGL_FONT_POLYGONS)
873 gluDeleteTess(tess);
874 return TRUE;
878 /***********************************************************************
879 * wglUseFontOutlinesA (OPENGL32.@)
881 BOOL WINAPI wglUseFontOutlinesA(HDC hdc,
882 DWORD first,
883 DWORD count,
884 DWORD listBase,
885 FLOAT deviation,
886 FLOAT extrusion,
887 int format,
888 LPGLYPHMETRICSFLOAT lpgmf)
890 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, FALSE);
893 /***********************************************************************
894 * wglUseFontOutlinesW (OPENGL32.@)
896 BOOL WINAPI wglUseFontOutlinesW(HDC hdc,
897 DWORD first,
898 DWORD count,
899 DWORD listBase,
900 FLOAT deviation,
901 FLOAT extrusion,
902 int format,
903 LPGLYPHMETRICSFLOAT lpgmf)
905 return wglUseFontOutlines_common(hdc, first, count, listBase, deviation, extrusion, format, lpgmf, TRUE);
908 /***********************************************************************
909 * glDebugEntry (OPENGL32.@)
911 GLint WINAPI glDebugEntry( GLint unknown1, GLint unknown2 )
913 return 0;
916 const GLubyte * WINAPI glGetStringi( GLenum name, GLuint index )
918 struct glGetStringi_params args =
920 .teb = NtCurrentTeb(),
921 .name = name,
922 .index = index,
924 NTSTATUS status;
925 #ifndef _WIN64
926 GLubyte *wow64_str = NULL;
927 #endif
929 TRACE( "name %d, index %d\n", name, index );
931 #ifndef _WIN64
932 if (UNIX_CALL( glGetStringi, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
933 #endif
934 if ((status = UNIX_CALL( glGetStringi, &args ))) WARN( "glGetStringi returned %#lx\n", status );
935 #ifndef _WIN64
936 if (args.ret != wow64_str) free( wow64_str );
937 else if (args.ret) append_wow64_string( (char *)args.ret );
938 #endif
939 return args.ret;
942 /***********************************************************************
943 * glGetString (OPENGL32.@)
945 const GLubyte * WINAPI glGetString( GLenum name )
947 struct glGetString_params args = { .teb = NtCurrentTeb(), .name = name };
948 NTSTATUS status;
949 #ifndef _WIN64
950 GLubyte *wow64_str = NULL;
951 #endif
953 TRACE( "name %d\n", name );
955 #ifndef _WIN64
956 if (UNIX_CALL( glGetString, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
957 #endif
958 if ((status = UNIX_CALL( glGetString, &args ))) WARN( "glGetString returned %#lx\n", status );
959 #ifndef _WIN64
960 if (args.ret != wow64_str) free( wow64_str );
961 else if (args.ret) append_wow64_string( (char *)args.ret );
962 #endif
963 return args.ret;
966 const char * WINAPI wglGetExtensionsStringARB( HDC hdc )
968 struct wglGetExtensionsStringARB_params args = { .teb = NtCurrentTeb(), .hdc = hdc };
969 NTSTATUS status;
970 #ifndef _WIN64
971 char *wow64_str = NULL;
972 #endif
974 TRACE( "hdc %p\n", hdc );
976 #ifndef _WIN64
977 if (UNIX_CALL( wglGetExtensionsStringARB, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
978 #endif
979 if ((status = UNIX_CALL( wglGetExtensionsStringARB, &args ))) WARN( "wglGetExtensionsStringARB returned %#lx\n", status );
980 #ifndef _WIN64
981 if (args.ret != wow64_str) free( wow64_str );
982 else if (args.ret) append_wow64_string( wow64_str );
983 #endif
984 return args.ret;
987 const char * WINAPI wglGetExtensionsStringEXT(void)
989 struct wglGetExtensionsStringEXT_params args = { .teb = NtCurrentTeb() };
990 NTSTATUS status;
991 #ifndef _WIN64
992 char *wow64_str = NULL;
993 #endif
995 TRACE( "\n" );
997 #ifndef _WIN64
998 if (UNIX_CALL( wglGetExtensionsStringEXT, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
999 #endif
1000 if ((status = UNIX_CALL( wglGetExtensionsStringEXT, &args ))) WARN( "wglGetExtensionsStringEXT returned %#lx\n", status );
1001 #ifndef _WIN64
1002 if (args.ret != wow64_str) free( wow64_str );
1003 else if (args.ret) append_wow64_string( wow64_str );
1004 #endif
1005 return args.ret;
1008 const GLchar * WINAPI wglQueryCurrentRendererStringWINE( GLenum attribute )
1010 struct wglQueryCurrentRendererStringWINE_params args = { .teb = NtCurrentTeb(), .attribute = attribute };
1011 NTSTATUS status;
1012 #ifndef _WIN64
1013 char *wow64_str = NULL;
1014 #endif
1016 TRACE( "attribute %d\n", attribute );
1018 #ifndef _WIN64
1019 if (UNIX_CALL( wglQueryCurrentRendererStringWINE, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
1020 #endif
1021 if ((status = UNIX_CALL( wglQueryCurrentRendererStringWINE, &args ))) WARN( "wglQueryCurrentRendererStringWINE returned %#lx\n", status );
1022 #ifndef _WIN64
1023 if (args.ret != wow64_str) free( wow64_str );
1024 else if (args.ret) append_wow64_string( wow64_str );
1025 #endif
1026 return args.ret;
1029 const GLchar * WINAPI wglQueryRendererStringWINE( HDC dc, GLint renderer, GLenum attribute )
1031 struct wglQueryRendererStringWINE_params args =
1033 .teb = NtCurrentTeb(),
1034 .dc = dc,
1035 .renderer = renderer,
1036 .attribute = attribute,
1038 NTSTATUS status;
1039 #ifndef _WIN64
1040 char *wow64_str = NULL;
1041 #endif
1043 TRACE( "dc %p, renderer %d, attribute %d\n", dc, renderer, attribute );
1045 #ifndef _WIN64
1046 if (UNIX_CALL( wglQueryCurrentRendererStringWINE, &args ) == STATUS_BUFFER_TOO_SMALL) args.ret = wow64_str = malloc( (size_t)args.ret );
1047 #endif
1048 if ((status = UNIX_CALL( wglQueryRendererStringWINE, &args ))) WARN( "wglQueryRendererStringWINE returned %#lx\n", status );
1049 #ifndef _WIN64
1050 if (args.ret != wow64_str) free( wow64_str );
1051 else if (args.ret) append_wow64_string( wow64_str );
1052 #endif
1053 return args.ret;
1056 #ifndef _WIN64
1057 static void *get_buffer_pointer( GLenum target )
1059 void (WINAPI *p_glGetBufferPointerv)( GLenum target, GLenum pname, void **params );
1060 void *ptr;
1061 if (!(p_glGetBufferPointerv = (void *)wglGetProcAddress( "glGetBufferPointerv" ))) return 0;
1062 p_glGetBufferPointerv( target, GL_BUFFER_MAP_POINTER, &ptr );
1063 return ptr;
1066 static void *get_named_buffer_pointer( GLint buffer )
1068 void (WINAPI *p_glGetNamedBufferPointerv)( GLuint buffer, GLenum pname, void **params );
1069 void *ptr;
1070 if (!(p_glGetNamedBufferPointerv = (void *)wglGetProcAddress( "glGetNamedBufferPointerv" ))) return 0;
1071 p_glGetNamedBufferPointerv( buffer, GL_BUFFER_MAP_POINTER, &ptr );
1072 return ptr;
1074 #endif
1076 static void *gl_map_buffer( enum unix_funcs code, GLenum target, GLenum access )
1078 struct glMapBuffer_params args =
1080 .teb = NtCurrentTeb(),
1081 .target = target,
1082 .access = access,
1084 NTSTATUS status;
1086 TRACE( "target %d, access %d\n", target, access );
1088 if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1089 #ifndef _WIN64
1090 if (status == STATUS_INVALID_ADDRESS)
1092 TRACE( "Unable to map wow64 buffer directly, using copy buffer!\n" );
1093 if (!(args.ret = _aligned_malloc( (size_t)args.ret, 16 ))) status = STATUS_NO_MEMORY;
1094 else if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1095 _aligned_free( args.ret );
1097 #endif
1098 WARN( "glMapBuffer returned %#lx\n", status );
1099 return args.ret;
1102 void * WINAPI glMapBuffer( GLenum target, GLenum access )
1104 return gl_map_buffer( unix_glMapBuffer, target, access );
1107 void * WINAPI glMapBufferARB( GLenum target, GLenum access )
1109 return gl_map_buffer( unix_glMapBufferARB, target, access );
1112 void * WINAPI glMapBufferRange( GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access )
1114 struct glMapBufferRange_params args =
1116 .teb = NtCurrentTeb(),
1117 .target = target,
1118 .offset = offset,
1119 .length = length,
1120 .access = access,
1122 NTSTATUS status;
1124 TRACE( "target %d, offset %Id, length %Id, access %d\n", target, offset, length, access );
1126 if (!(status = UNIX_CALL( glMapBufferRange, &args ))) return args.ret;
1127 #ifndef _WIN64
1128 if (status == STATUS_INVALID_ADDRESS)
1130 TRACE( "Unable to map wow64 buffer directly, using copy buffer!\n" );
1131 if (!(args.ret = _aligned_malloc( length, 16 ))) status = STATUS_NO_MEMORY;
1132 else if (!(status = UNIX_CALL( glMapBufferRange, &args ))) return args.ret;
1133 _aligned_free( args.ret );
1135 #endif
1136 WARN( "glMapBufferRange returned %#lx\n", status );
1137 return args.ret;
1140 static void *gl_map_named_buffer( enum unix_funcs code, GLuint buffer, GLenum access )
1142 struct glMapNamedBuffer_params args =
1144 .teb = NtCurrentTeb(),
1145 .buffer = buffer,
1146 .access = access,
1148 NTSTATUS status;
1150 TRACE( "(%d, %d)\n", buffer, access );
1152 if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1153 #ifndef _WIN64
1154 if (status == STATUS_INVALID_ADDRESS)
1156 TRACE( "Unable to map wow64 buffer directly, using copy buffer!\n" );
1157 if (!(args.ret = _aligned_malloc( (size_t)args.ret, 16 ))) status = STATUS_NO_MEMORY;
1158 else if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1159 _aligned_free( args.ret );
1161 #endif
1162 WARN( "glMapNamedBuffer returned %#lx\n", status );
1163 return args.ret;
1166 void * WINAPI glMapNamedBuffer( GLuint buffer, GLenum access )
1168 return gl_map_named_buffer( unix_glMapNamedBuffer, buffer, access );
1171 void * WINAPI glMapNamedBufferEXT( GLuint buffer, GLenum access )
1173 return gl_map_named_buffer( unix_glMapNamedBufferEXT, buffer, access );
1176 static void *gl_map_named_buffer_range( enum unix_funcs code, GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access )
1178 struct glMapNamedBufferRange_params args =
1180 .teb = NtCurrentTeb(),
1181 .buffer = buffer,
1182 .offset = offset,
1183 .length = length,
1184 .access = access,
1186 NTSTATUS status;
1188 TRACE( "buffer %d, offset %Id, length %Id, access %d\n", buffer, offset, length, access );
1190 if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1191 #ifndef _WIN64
1192 if (status == STATUS_INVALID_ADDRESS)
1194 TRACE( "Unable to map wow64 buffer directly, using copy buffer!\n" );
1195 if (!(args.ret = _aligned_malloc( length, 16 ))) status = STATUS_NO_MEMORY;
1196 else if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1197 _aligned_free( args.ret );
1199 #endif
1200 WARN( "glMapNamedBufferRange returned %#lx\n", status );
1201 return args.ret;
1204 void * WINAPI glMapNamedBufferRange( GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access )
1206 return gl_map_named_buffer_range( unix_glMapNamedBufferRange, buffer, offset, length, access );
1209 void * WINAPI glMapNamedBufferRangeEXT( GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access )
1211 return gl_map_named_buffer_range( unix_glMapNamedBufferRangeEXT, buffer, offset, length, access );
1214 static GLboolean gl_unmap_buffer( enum unix_funcs code, GLenum target )
1216 struct glUnmapBuffer_params args =
1218 .teb = NtCurrentTeb(),
1219 .target = target,
1221 NTSTATUS status;
1222 #ifndef _WIN64
1223 void *ptr = get_buffer_pointer( target );
1224 #endif
1226 TRACE( "target %d\n", target );
1228 if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1229 #ifndef _WIN64
1230 if (status == STATUS_INVALID_ADDRESS)
1232 TRACE( "Releasing wow64 copy buffer %p\n", ptr );
1233 _aligned_free( ptr );
1234 return args.ret;
1236 #endif
1237 WARN( "glUnmapBuffer returned %#lx\n", status );
1238 return args.ret;
1241 GLboolean WINAPI glUnmapBuffer( GLenum target )
1243 return gl_unmap_buffer( unix_glUnmapBuffer, target );
1246 GLboolean WINAPI glUnmapBufferARB( GLenum target )
1248 return gl_unmap_buffer( unix_glUnmapBufferARB, target );
1251 static GLboolean gl_unmap_named_buffer( enum unix_funcs code, GLuint buffer )
1253 struct glUnmapNamedBuffer_params args =
1255 .teb = NtCurrentTeb(),
1256 .buffer = buffer,
1258 NTSTATUS status;
1259 #ifndef _WIN64
1260 void *ptr = get_named_buffer_pointer( buffer );
1261 #endif
1263 TRACE( "buffer %d\n", buffer );
1265 if (!(status = WINE_UNIX_CALL( code, &args ))) return args.ret;
1266 #ifndef _WIN64
1267 if (status == STATUS_INVALID_ADDRESS)
1269 TRACE( "Releasing wow64 copy buffer %p\n", ptr );
1270 _aligned_free( ptr );
1271 return args.ret;
1273 #endif
1274 WARN( "glUnmapNamedBuffer returned %#lx\n", status );
1275 return args.ret;
1278 GLboolean WINAPI glUnmapNamedBuffer( GLuint buffer )
1280 return gl_unmap_named_buffer( unix_glUnmapNamedBuffer, buffer );
1283 GLboolean WINAPI glUnmapNamedBufferEXT( GLuint buffer )
1285 return gl_unmap_named_buffer( unix_glUnmapNamedBufferEXT, buffer );
1288 static BOOL WINAPI call_opengl_debug_message_callback( struct wine_gl_debug_message_params *params, ULONG size )
1290 params->user_callback( params->source, params->type, params->id, params->severity,
1291 params->length, params->message, params->user_data );
1292 return TRUE;
1295 /***********************************************************************
1296 * OpenGL initialisation routine
1298 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
1300 void **kernel_callback_table;
1301 NTSTATUS status;
1303 switch(reason)
1305 case DLL_PROCESS_ATTACH:
1306 if ((status = __wine_init_unix_call()))
1308 ERR( "Failed to load unixlib, status %#lx\n", status );
1309 return FALSE;
1312 kernel_callback_table = NtCurrentTeb()->Peb->KernelCallbackTable;
1313 kernel_callback_table[NtUserCallOpenGLDebugMessageCallback] = call_opengl_debug_message_callback;
1314 /* fallthrough */
1315 case DLL_THREAD_ATTACH:
1316 if ((status = UNIX_CALL( thread_attach, NtCurrentTeb() )))
1318 WARN( "Failed to initialize thread, status %#lx\n", status );
1319 return FALSE;
1321 break;
1323 case DLL_PROCESS_DETACH:
1324 if (reserved) break;
1325 UNIX_CALL( process_detach, NULL );
1326 #ifndef _WIN64
1327 cleanup_wow64_strings();
1328 #endif
1329 return TRUE;
1331 return TRUE;