Create a branch for working on csrss and co.
[reactos.git] / dll / opengl / opengl32 / wgl.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: lib/opengl32/wgl.c
5 * PURPOSE: OpenGL32 lib, rosglXXX functions
6 * PROGRAMMER: Anich Gregor (blight)
7 * UPDATE HISTORY:
8 * Feb 2, 2004: Created
9 */
10
11 #define OPENGL32_GL_FUNC_PROTOTYPES
12 #include "opengl32.h"
13
14 #ifdef __cplusplus
15 extern "C" {
16 #endif /* __cplusplus */
17
18 #if !defined(UNIMPLEMENTED)
19 # define UNIMPLEMENTED DBGPRINT( "UNIMPLEMENTED" )
20 #endif
21
22
23 typedef struct _OPENGL_INFO
24 {
25 DWORD Version; /*!< Driver interface version */
26 DWORD DriverVersion; /*!< Driver version */
27 WCHAR DriverName[256]; /*!< Driver name */
28 } OPENGL_INFO, *POPENGL_INFO;
29
30 /*! \brief Append OpenGL Rendering Context (GLRC) to list
31 *
32 * \param glrc [IN] Pointer to GLRC to append to list
33 */
34 static
35 void
36 ROSGL_AppendContext( GLRC *glrc )
37 {
38 /* synchronize */
39 if (WaitForSingleObject( OPENGL32_processdata.glrc_mutex, INFINITE ) ==
40 WAIT_FAILED)
41 {
42 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
43 return; /* FIXME: do we have to expect such an error and handle it? */
44 }
45
46 if (OPENGL32_processdata.glrc_list == NULL)
47 OPENGL32_processdata.glrc_list = glrc;
48 else
49 {
50 GLRC *p = OPENGL32_processdata.glrc_list;
51 while (p->next != NULL)
52 p = p->next;
53 p->next = glrc;
54 }
55
56 /* release mutex */
57 if (!ReleaseMutex( OPENGL32_processdata.glrc_mutex ))
58 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
59 }
60
61
62 /*! \brief Remove OpenGL Rendering Context (GLRC) from list
63 *
64 * \param glrc [IN] Pointer to GLRC to remove from list
65 */
66 static
67 void
68 ROSGL_RemoveContext( GLRC *glrc )
69 {
70 /* synchronize */
71 if (WaitForSingleObject( OPENGL32_processdata.glrc_mutex, INFINITE ) ==
72 WAIT_FAILED)
73 {
74 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
75 return; /* FIXME: do we have to expect such an error and handle it? */
76 }
77
78 if (glrc == OPENGL32_processdata.glrc_list)
79 OPENGL32_processdata.glrc_list = glrc->next;
80 else
81 {
82 GLRC *p = OPENGL32_processdata.glrc_list;
83 while (p != NULL)
84 {
85 if (p->next == glrc)
86 {
87 p->next = glrc->next;
88 break;
89 }
90 p = p->next;
91 }
92 if (p == NULL)
93 DBGPRINT( "Error: GLRC 0x%08x not found in list!", glrc );
94 }
95
96 /* release mutex */
97 if (!ReleaseMutex( OPENGL32_processdata.glrc_mutex ))
98 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
99 }
100
101
102 /*! \brief Create a new GL Context (GLRC) and append it to the list
103 *
104 * \return Pointer to new GLRC on success
105 * \retval NULL Returned on failure (i.e. Out of memory)
106 */
107 static
108 GLRC *
109 ROSGL_NewContext(void)
110 {
111 GLRC *glrc;
112
113 /* allocate GLRC */
114 glrc = (GLRC*)HeapAlloc( GetProcessHeap(),
115 HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, sizeof (GLRC) );
116
117 /* append to list */
118 ROSGL_AppendContext( glrc );
119
120 return glrc;
121 }
122
123 /*! \brief Delete all GLDCDATA with this IDC
124 *
125 * \param icd [IN] Pointer to a ICD
126 */
127 static
128 VOID
129 ROSGL_DeleteDCDataForICD( GLDRIVERDATA *icd )
130 {
131 GLDCDATA *p, **pptr;
132
133 return;
134
135 /* synchronize */
136 if (WaitForSingleObject( OPENGL32_processdata.dcdata_mutex, INFINITE ) ==
137 WAIT_FAILED)
138 {
139 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
140 return;
141 }
142
143 p = OPENGL32_processdata.dcdata_list;
144 pptr = &OPENGL32_processdata.dcdata_list;
145 while (p != NULL)
146 {
147 if (p->icd == icd)
148 {
149 *pptr = p->next;
150 OPENGL32_UnloadICD( p->icd );
151
152 if (!HeapFree( GetProcessHeap(), 0, p ))
153 DBGPRINT( "Warning: HeapFree() on GLDCDATA failed (%d)",
154 GetLastError() );
155
156 p = *pptr;
157 }
158 else
159 {
160 pptr = &p->next;
161 p = p->next;
162 }
163 }
164
165 /* release mutex */
166 if (!ReleaseMutex( OPENGL32_processdata.dcdata_mutex ))
167 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
168 }
169
170
171 /*! \brief Delete a GL Context (GLRC) and remove it from the list
172 *
173 * \param glrc [IN] Pointer to GLRC to delete
174 *
175 * \retval TRUE Success
176 * \retval FALSE Failure
177 */
178 static
179 BOOL
180 ROSGL_DeleteContext( GLRC *glrc )
181 {
182 /* unload icd */
183 if ((glrc->icd != NULL) && (!InterlockedDecrement((LONG*)&glrc->icd->refcount)))
184 {
185 /* This is the last context, remove the ICD*/
186 ROSGL_DeleteDCDataForICD( glrc->icd );
187 }
188
189 /* remove from list */
190 ROSGL_RemoveContext( glrc );
191
192 /* free memory */
193 HeapFree( GetProcessHeap(), 0, glrc );
194
195 return TRUE;
196 }
197
198
199 /*! \brief Check wether a GLRC is in the list
200 *
201 * \param glrc [IN] Pointer to GLRC to look for in the list
202 *
203 * \retval TRUE GLRC was found
204 * \retval FALSE GLRC was not found
205 */
206 static
207 BOOL
208 ROSGL_ContainsContext( GLRC *glrc )
209 {
210 GLRC *p;
211 BOOL found = FALSE;
212
213 /* synchronize */
214 if (WaitForSingleObject( OPENGL32_processdata.glrc_mutex, INFINITE ) ==
215 WAIT_FAILED)
216 {
217 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
218 return FALSE; /* FIXME: do we have to expect such an error and handle it? */
219 }
220
221 p = OPENGL32_processdata.glrc_list;
222 while (p != NULL)
223 {
224 if (p == glrc)
225 {
226 found = TRUE;
227 break;
228 }
229 p = p->next;
230 }
231
232 /* release mutex */
233 if (!ReleaseMutex( OPENGL32_processdata.glrc_mutex ))
234 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
235
236 return found;
237 }
238
239
240 /*! \brief Get GL private DC data.
241 *
242 * This function adds an empty GLDCDATA to the list if there is no data for the
243 * given DC yet.
244 *
245 * \param hdc [IN] Handle to a Device Context for which to get the data
246 *
247 * \return Pointer to GLDCDATA on success
248 * \retval NULL on failure
249 */
250 static
251 GLDCDATA *
252 ROSGL_GetPrivateDCData( HDC hdc )
253 {
254 GLDCDATA *data;
255 HANDLE handle;
256
257 /* check hdc */
258 if (GetObjectType( hdc ) != OBJ_DC && GetObjectType( hdc ) != OBJ_MEMDC)
259 {
260 DBGPRINT( "Error: hdc is not a DC handle!" );
261 SetLastError( ERROR_INVALID_HANDLE );
262 return FALSE;
263 }
264
265 /* synchronize */
266 if (WaitForSingleObject( OPENGL32_processdata.dcdata_mutex, INFINITE ) ==
267 WAIT_FAILED)
268 {
269 DBGPRINT( "Error: WaitForSingleObject() failed (%d)", GetLastError() );
270 return NULL; /* FIXME: do we have to expect such an error and handle it? */
271 }
272
273 /* We must use the window to identify our data, as pixel format is
274 * specific to a window for device context */
275 handle = WindowFromDC(hdc);
276 if(!handle)
277 handle = hdc;
278
279 /* look for data in list */
280 data = OPENGL32_processdata.dcdata_list;
281 while (data != NULL)
282 {
283 if (data->handle == handle) /* found */
284 break;
285 data = data->next;
286 }
287
288 /* allocate new data if not found in list */
289 if (data == NULL)
290 {
291 data = HeapAlloc( GetProcessHeap(),
292 HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS,
293 sizeof (GLDCDATA) );
294 if (data == NULL)
295 {
296 DBGPRINT( "Error: HeapAlloc() failed (%d)", GetLastError() );
297 }
298 else
299 {
300 data->handle = handle;
301
302 /* append data to list */
303 if (OPENGL32_processdata.dcdata_list == NULL)
304 OPENGL32_processdata.dcdata_list = data;
305 else
306 {
307 GLDCDATA *p = OPENGL32_processdata.dcdata_list;
308 while (p->next != NULL)
309 p = p->next;
310 p->next = data;
311 }
312 }
313 }
314
315 /* release mutex */
316 if (!ReleaseMutex( OPENGL32_processdata.dcdata_mutex ))
317 DBGPRINT( "Error: ReleaseMutex() failed (%d)", GetLastError() );
318
319 return data;
320 }
321
322
323 /*! \brief Get ICD from HDC.
324 *
325 * This function asks the display driver which OpenGL ICD to load for the given
326 * HDC, loads it and returns a pointer to a GLDRIVERDATA struct on success.
327 *
328 * \param hdc [IN] Handle for DC for which to load/get the ICD
329 *
330 * \return Pointer to GLDRIVERDATA
331 * \retval NULL Failure.
332 */
333 static
334 GLDRIVERDATA *
335 ROSGL_ICDForHDC( HDC hdc )
336 {
337 GLDCDATA *dcdata;
338 GLDRIVERDATA *drvdata;
339
340 dcdata = ROSGL_GetPrivateDCData( hdc );
341 if (dcdata == NULL)
342 return NULL;
343
344 if (dcdata->icd == NULL)
345 {
346 LPCWSTR driverName;
347 OPENGL_INFO info;
348
349 /* NOTE: This might be done by multiple threads simultaneously, but only the fastest
350 actually gets to set the ICD! */
351
352 driverName = _wgetenv( L"OPENGL32_DRIVER" );
353 if (driverName == NULL)
354 {
355 DWORD dwInput;
356 LONG ret;
357
358 /* get driver name */
359 dwInput = OPENGL_GETINFO;
360 ret = ExtEscape( hdc, QUERYESCSUPPORT, sizeof (dwInput), (LPCSTR)&dwInput, 0, NULL );
361 if (ret > 0)
362 {
363 dwInput = 0;
364 ret = ExtEscape( hdc, OPENGL_GETINFO, sizeof (dwInput),
365 (LPCSTR)&dwInput, sizeof (OPENGL_INFO),
366 (LPSTR)&info );
367 }
368 if (ret <= 0)
369 {
370 HKEY hKey;
371 DWORD type, size;
372
373 if (ret < 0)
374 {
375 DBGPRINT( "Warning: ExtEscape to get the drivername failed! (%d)", GetLastError() );
376 if (MessageBox( WindowFromDC( hdc ), L"Couldn't get installable client driver name!\nUsing default driver.",
377 L"OPENGL32.dll: Warning", MB_OKCANCEL | MB_ICONWARNING ) == IDCANCEL)
378 {
379 return NULL;
380 }
381 }
382
383 /* open registry key */
384 ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, OPENGL_DRIVERS_SUBKEY, 0, KEY_QUERY_VALUE, &hKey );
385 if (ret != ERROR_SUCCESS)
386 {
387 DBGPRINT( "Error: Couldn't open registry key '%ws'", OPENGL_DRIVERS_SUBKEY );
388 SetLastError( ret );
389 return NULL;
390 }
391
392 /* query value */
393 size = sizeof (info.DriverName);
394 ret = RegQueryValueExW( hKey, L"DefaultDriver", 0, &type, (LPBYTE)info.DriverName, &size );
395 RegCloseKey( hKey );
396 if (ret != ERROR_SUCCESS || type != REG_SZ)
397 {
398 DBGPRINT( "Error: Couldn't query DefaultDriver value or not a string" );
399 SetLastError( ret );
400 return NULL;
401 }
402 }
403 }
404 else
405 {
406 wcsncpy( info.DriverName, driverName, sizeof (info.DriverName) / sizeof (info.DriverName[0]) );
407 }
408 /* load driver (or get a reference) */
409 drvdata = OPENGL32_LoadICD( info.DriverName );
410 if (drvdata == NULL)
411 {
412 WCHAR Buffer[256];
413 snwprintf(Buffer, sizeof(Buffer)/sizeof(WCHAR),
414 L"Couldn't load driver \"%s\".", info.DriverName);
415 MessageBox(WindowFromDC( hdc ), Buffer,
416 L"OPENGL32.dll: Warning",
417 MB_OK | MB_ICONWARNING);
418 }
419 else
420 {
421 /* Atomically set the ICD!!! */
422 if (InterlockedCompareExchangePointer((PVOID*)&dcdata->icd,
423 (PVOID)drvdata,
424 NULL) != NULL)
425 {
426 /* Too bad, somebody else was faster... */
427 DBGTRACE("ICD is already set!\n");
428 }
429 }
430 }
431
432 return dcdata->icd;
433 }
434
435
436 /*! \brief SetContextCallBack passed to DrvSetContext.
437 *
438 * This function gets called by the OpenGL driver whenever the current GL
439 * context (dispatch table) is to be changed.
440 *
441 * \param table [IN] Function pointer table (first DWORD is number of functions)
442 *
443 * \return unkown (maybe void? ERROR_SUCCESS at the moment)
444 */
445 DWORD
446 CALLBACK
447 ROSGL_SetContextCallBack( const ICDTable *table )
448 {
449 TEB *teb;
450 PROC *tebTable, *tebDispatchTable;
451 INT size;
452
453 teb = NtCurrentTeb();
454 tebTable = (PROC *)teb->glTable;
455 tebDispatchTable = (PROC *)teb->glDispatchTable;
456
457 DBGTRACE( "Called!" );
458
459 if (table != NULL)
460 {
461 DBGPRINT( "Function count: %d\n", table->num_funcs );
462
463 /* save table */
464 size = sizeof (PROC) * table->num_funcs;
465 memcpy( tebTable, table->dispatch_table, size );
466 memset( tebTable + table->num_funcs, 0, DISPATCH_TABLE_SIZE - size );
467 }
468 else
469 {
470 DBGPRINT( "Unsetting current context" );
471 memset( tebTable, 0, DISPATCH_TABLE_SIZE );
472 }
473
474 /* put in empty functions as long as we dont have a fallback */
475 #define X(func, ret, typeargs, args, icdidx, tebidx, stack) \
476 if (tebTable[icdidx] == NULL) \
477 { \
478 if (table != NULL) \
479 DBGPRINT( "Warning: GL proc '%s' is NULL", #func ); \
480 tebTable[icdidx] = (PROC)glEmptyFunc##stack; \
481 }
482 GLFUNCS_MACRO
483 #undef X
484
485 /* fill teb->glDispatchTable for fast calls */
486 #define X(func, ret, typeargs, args, icdidx, tebidx, stack) \
487 if (tebidx >= 0) \
488 tebDispatchTable[tebidx] = tebTable[icdidx];
489 GLFUNCS_MACRO
490 #undef X
491
492 DBGPRINT( "Done." );
493
494 return ERROR_SUCCESS;
495 }
496
497
498 /*! \brief Returns the current pixelformat.
499 *
500 * \param hdc [IN] Handle to DC to get the pixelformat from
501 *
502 * \return Pixelformat index
503 * \retval 0 Failure
504 */
505 int
506 WINAPI
507 rosglGetPixelFormat( HDC hdc )
508 {
509 GLDCDATA *dcdata;
510
511 DBGTRACE( "Called!" );
512
513 dcdata = ROSGL_GetPrivateDCData( hdc );
514 if (dcdata == NULL)
515 {
516 DBGPRINT( "Error: ROSGL_GetPrivateDCData failed!" );
517 return 0;
518 }
519
520 return dcdata->pixel_format;
521 }
522
523
524 /*! \brief Attempts to find the best matching pixel format for HDC
525 *
526 * This function is comparing each available format with the preferred one
527 * and returns the one which is closest to it.
528 * If PFD_DOUBLEBUFFER, PFD_STEREO or one of PFD_DRAW_TO_WINDOW,
529 * PFD_DRAW_TO_BITMAP, PFD_SUPPORT_GDI and PDF_SUPPORT_OPENGL is given then
530 * only formats which also support those will be enumerated (unless
531 * PFD_DOUBLEBUFFER_DONTCARE or PFD_STEREO_DONTCARE is also set)
532 *
533 * \param hdc [IN] Handle to DC for which to get a pixel format index
534 * \param pfd [IN] PFD describing what kind of format you want
535 *
536 * \return Pixel format index
537 * \retval 0 Failed to find a suitable format
538 */
539 #define BUFFERDEPTH_SCORE(want, have) \
540 ((want == 0) ? (0) : ((want < have) ? (1) : ((want > have) ? (3) : (0))))
541
542 /* Score if we want and not have it */
543 #define FLAG_SCORE(want, have, flag) \
544 (((want & ~have) & flag) ? (1) : (0))
545
546 /* Score if what we want is different than what we have, except when
547 _DONTCARE was set */
548 #define FLAG_SCORE_DONTCARE(want, have, flag) \
549 ((!(have & flag ## _DONTCARE)) && ((want & flag) != (have & flag)) ? (1) : (0))
550
551 int
552 APIENTRY
553 rosglChoosePixelFormat( HDC hdc, CONST PIXELFORMATDESCRIPTOR *pfd )
554 {
555 GLDRIVERDATA *icd;
556 PIXELFORMATDESCRIPTOR icdPfd;
557 int i;
558 int best = 0;
559 int score, bestScore = 0x7fff; /* used to choose a pfd if no exact match */
560 int icdNumFormats;
561
562 DBGTRACE( "Called!" );
563
564 /* load ICD */
565 icd = ROSGL_ICDForHDC( hdc );
566 if (icd == NULL)
567 return 0;
568
569 /* check input */
570 if (pfd->nSize != sizeof (PIXELFORMATDESCRIPTOR) || pfd->nVersion != 1)
571 {
572 SetLastError( ERROR_INVALID_PARAMETER );
573 return 0;
574 }
575
576 /* get number of formats */
577 icdNumFormats = icd->DrvDescribePixelFormat( hdc, 1,
578 sizeof (PIXELFORMATDESCRIPTOR), &icdPfd );
579 if (icdNumFormats == 0)
580 {
581 DBGPRINT( "Error: DrvDescribePixelFormat failed (%d)", GetLastError() );
582 return 0;
583 }
584 DBGPRINT( "Info: Enumerating %d pixelformats", icdNumFormats );
585
586 /* try to find best format */
587 for (i = 0; i < icdNumFormats; i++)
588 {
589 if (icd->DrvDescribePixelFormat( hdc, i + 1,
590 sizeof (PIXELFORMATDESCRIPTOR), &icdPfd ) == 0)
591 {
592 DBGPRINT( "Warning: DrvDescribePixelFormat failed (%d)",
593 GetLastError() );
594 break;
595 }
596
597 if ((pfd->dwFlags & PFD_GENERIC_ACCELERATED) != 0) /* we do not support such kind of drivers */
598 {
599 continue;
600 }
601
602 score = 0; /* higher is worse */
603
604 /* compare flags */
605 score += FLAG_SCORE(pfd->dwFlags, icdPfd.dwFlags, PFD_DRAW_TO_WINDOW);
606 score += FLAG_SCORE(pfd->dwFlags, icdPfd.dwFlags, PFD_DRAW_TO_BITMAP);
607 score += FLAG_SCORE(pfd->dwFlags, icdPfd.dwFlags, PFD_SUPPORT_GDI);
608 score += FLAG_SCORE(pfd->dwFlags, icdPfd.dwFlags, PFD_SUPPORT_OPENGL);
609 score += FLAG_SCORE_DONTCARE(pfd->dwFlags, icdPfd.dwFlags, PFD_DOUBLEBUFFER);
610 score += FLAG_SCORE_DONTCARE(pfd->dwFlags, icdPfd.dwFlags, PFD_STEREO);
611
612 /* check other attribs */
613 if (pfd->iPixelType != icdPfd.iPixelType)
614 score += 5; /* this is really bad i think */
615 if (pfd->iLayerType != icdPfd.iLayerType)
616 score += 15; /* this is very very bad ;) */
617
618 score += BUFFERDEPTH_SCORE(pfd->cAlphaBits, icdPfd.cAlphaBits);
619 score += BUFFERDEPTH_SCORE(pfd->cAccumBits, icdPfd.cAccumBits);
620 score += BUFFERDEPTH_SCORE(pfd->cDepthBits, icdPfd.cDepthBits);
621 score += BUFFERDEPTH_SCORE(pfd->cStencilBits, icdPfd.cStencilBits);
622 score += BUFFERDEPTH_SCORE(pfd->cAuxBuffers, icdPfd.cAuxBuffers);
623
624 /* check score */
625 if (score < bestScore)
626 {
627 bestScore = score;
628 best = i + 1;
629 if (bestScore == 0)
630 break;
631 }
632 }
633
634 if (best == 0)
635 SetLastError( 0 ); /* FIXME: set appropriate error */
636
637 DBGPRINT( "Info: Suggesting pixelformat %d", best );
638 return best;
639 }
640
641
642 /*! \brief Copy data specified by mask from one GLRC to another.
643 *
644 * \param src [IN] Source GLRC
645 * \param src [OUT] Destination GLRC
646 * \param mask [IN] Bitfield like given to glPushAttrib()
647 *
648 * \retval TRUE Success
649 * \retval FALSE Failure
650 */
651 BOOL
652 APIENTRY
653 rosglCopyContext( HGLRC hsrc, HGLRC hdst, UINT mask )
654 {
655 GLRC *src = (GLRC *)hsrc;
656 GLRC *dst = (GLRC *)hdst;
657
658 /* check glrcs */
659 if (!ROSGL_ContainsContext( src ))
660 {
661 DBGPRINT( "Error: src GLRC not found!" );
662 SetLastError( ERROR_INVALID_HANDLE );
663 return FALSE;
664 }
665 if (!ROSGL_ContainsContext( dst ))
666 {
667 DBGPRINT( "Error: dst GLRC not found!" );
668 SetLastError( ERROR_INVALID_HANDLE );
669 return FALSE;
670 }
671
672 /* I think this is only possible within one ICD */
673 if (src->icd != dst->icd)
674 {
675 DBGPRINT( "Error: src and dst GLRC use different ICDs!" );
676 SetLastError( ERROR_INVALID_HANDLE );
677 return FALSE;
678 }
679
680 /* copy data (call ICD) */
681 return src->icd->DrvCopyContext( src->hglrc, dst->hglrc, mask );
682 }
683
684
685 /*! \brief Create a new GL Rendering Context.
686 *
687 * This function can create over- or underlay surfaces.
688 *
689 * \param hdc [IN] Handle for DC for which to create context
690 * \param layer [IN] Layer number to bind (draw?) to
691 *
692 * \return Handle for the created GLRC
693 * \retval NULL Failure
694 */
695 HGLRC
696 APIENTRY
697 rosglCreateLayerContext( HDC hdc, int layer )
698 {
699 GLDRIVERDATA *icd = NULL;
700 GLRC *glrc;
701 HGLRC drvHglrc = NULL;
702
703 DBGTRACE( "Called!" );
704
705 /* if (GetObjectType( hdc ) != OBJ_DC)
706 {
707 DBGPRINT( "Error: hdc is not a DC handle!" );
708 return NULL;
709 }
710 */
711
712 /* load ICD */
713 icd = ROSGL_ICDForHDC( hdc );
714 if (icd == NULL)
715 {
716 DBGPRINT( "Couldn't get ICD by HDC :-(" );
717 /* FIXME: fallback? */
718 return NULL;
719 }
720
721 /* create new GLRC */
722 glrc = ROSGL_NewContext();
723 if (glrc == NULL)
724 return NULL;
725
726 /* Don't forget to refcount it, icd will be released when last context is deleted */
727 InterlockedIncrement((LONG*)&icd->refcount);
728
729 /* You can't create a context without first setting a valid pixel format */
730 if(rosglGetPixelFormat(hdc) == 0)
731 {
732 ROSGL_DeleteContext( glrc );
733 SetLastError(ERROR_INVALID_PIXEL_FORMAT);
734 return NULL;
735 }
736
737
738 /* create context */
739 if (icd->DrvCreateLayerContext != NULL)
740 drvHglrc = icd->DrvCreateLayerContext( hdc, layer );
741 if (drvHglrc == NULL)
742 {
743 if (layer == 0 && icd->DrvCreateContext != NULL)
744 drvHglrc = icd->DrvCreateContext( hdc );
745 else
746 DBGPRINT( "Warning: CreateLayerContext not supported by ICD!" );
747 }
748
749 if (drvHglrc == NULL)
750 {
751 /* FIXME: fallback to mesa? */
752 DBGPRINT( "Error: DrvCreate[Layer]Context failed! (%d)", GetLastError() );
753 ROSGL_DeleteContext( glrc );
754 return NULL;
755 }
756
757 /* we have our GLRC in glrc and the ICD's GLRC in drvHglrc */
758 glrc->hglrc = drvHglrc;
759 glrc->icd = icd;
760
761 return (HGLRC)glrc;
762 }
763
764
765 /*! \brief Create a new GL Rendering Context.
766 *
767 * \param hdc [IN] Handle for DC for which to create context
768 *
769 * \return Handle for the created GLRC
770 * \retval NULL Failure
771 */
772 HGLRC
773 APIENTRY
774 rosglCreateContext( HDC hdc )
775 {
776 return rosglCreateLayerContext( hdc, 0 );
777 }
778
779
780 /*! \brief Delete an OpenGL context
781 *
782 * \param hglrc [IN] Handle to GLRC to delete;
783 *
784 * \retval TRUE Success
785 * \retval FALSE Failure (i.e. GLRC is current for a thread)
786 */
787 BOOL
788 APIENTRY
789 rosglDeleteContext( HGLRC hglrc )
790 {
791 GLRC *glrc = (GLRC *)hglrc;
792
793 /* check if we know about this context */
794 if (!ROSGL_ContainsContext( glrc ))
795 {
796 DBGPRINT( "Error: hglrc not found!" );
797 SetLastError( ERROR_INVALID_HANDLE );
798 return FALSE;
799 }
800
801 /* On windows, this is allowed to delete a context which is current */
802 if (glrc->is_current)
803 {
804 /* But only for its own thread */
805 if(glrc->thread_id != GetCurrentThreadId())
806 {
807 DBGPRINT( "Error: GLRC is current for DC 0x%08x", glrc->hdc );
808 SetLastError( ERROR_INVALID_FUNCTION );
809 return FALSE;
810 }
811 /* Unset it before going further */
812 rosglMakeCurrent(NULL, NULL);
813 }
814
815 /* release ICD's context */
816 if (glrc->hglrc != NULL)
817 {
818 if (!glrc->icd->DrvDeleteContext( glrc->hglrc ))
819 {
820 DBGPRINT( "Warning: DrvDeleteContext() failed (%d)", GetLastError() );
821 return FALSE;
822 }
823 }
824
825 /* free resources */
826 return ROSGL_DeleteContext( glrc );
827 }
828
829
830 BOOL
831 APIENTRY
832 rosglDescribeLayerPlane( HDC hdc, int iPixelFormat, int iLayerPlane,
833 UINT nBytes, LPLAYERPLANEDESCRIPTOR plpd )
834 {
835 UNIMPLEMENTED;
836 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
837 return FALSE;
838 }
839
840
841 /*! \brief Gets information about a pixelformat.
842 *
843 * \param hdc [IN] Handle to DC
844 * \param iFormat [IN] Pixelformat index
845 * \param nBytes [IN] sizeof (pfd) - at most nBytes are copied into pfd
846 * \param pfd [OUT] Pointer to a PIXELFORMATDESCRIPTOR
847 *
848 * \return Maximum pixelformat index/number of formats
849 * \retval 0 Failure
850 */
851 int
852 APIENTRY
853 rosglDescribePixelFormat( HDC hdc, int iFormat, UINT nBytes,
854 LPPIXELFORMATDESCRIPTOR pfd )
855 {
856 int ret = 0;
857 GLDRIVERDATA *icd = ROSGL_ICDForHDC( hdc );
858
859 if (icd != NULL)
860 {
861 ret = icd->DrvDescribePixelFormat( hdc, iFormat, nBytes, pfd );
862 if (ret == 0)
863 DBGPRINT( "Error: DrvDescribePixelFormat(format=%d) failed (%d)", iFormat, GetLastError() );
864 }
865 else
866 {
867 SetLastError( ERROR_INVALID_FUNCTION );
868 }
869
870 return ret;
871 }
872
873
874 /*! \brief Return the thread's current GLRC
875 *
876 * \return Handle for thread's current GLRC
877 * \retval NULL No current GLRC set
878 */
879 HGLRC
880 APIENTRY
881 rosglGetCurrentContext()
882 {
883 return NtCurrentTeb()->glCurrentRC;
884 }
885
886
887 /*! \brief Return the thread's current DC
888 *
889 * \return Handle for thread's current DC
890 * \retval NULL No current DC/GLRC set
891 */
892 HDC
893 APIENTRY
894 rosglGetCurrentDC()
895 {
896 GLRC* glrc = (GLRC*)NtCurrentTeb()->glCurrentRC;
897 if(glrc) return glrc->hdc;
898 return NULL;
899 }
900
901
902 int
903 APIENTRY
904 rosglGetLayerPaletteEntries( HDC hdc, int iLayerPlane, int iStart,
905 int cEntries, COLORREF *pcr )
906 {
907 UNIMPLEMENTED;
908 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
909 return 0;
910 }
911
912
913 /*! \brief Get the address for an OpenGL extension function.
914 *
915 * The addresses this function returns are only valid within the same thread
916 * which it was called from.
917 *
918 * \param proc [IN] Name of the function to look for
919 *
920 * \return The address of the proc
921 * \retval NULL Failure
922 */
923 PROC
924 APIENTRY
925 rosglGetProcAddress( LPCSTR proc )
926 {
927 PROC func;
928 GLRC* glrc = (GLRC*)NtCurrentTeb()->glCurrentRC;
929
930 /* FIXME we should Flush the gl here */
931
932 if (glrc == NULL)
933 {
934 DBGPRINT( "Error: No current GLRC!" );
935 SetLastError( ERROR_INVALID_FUNCTION );
936 return NULL;
937 }
938
939 func = glrc->icd->DrvGetProcAddress( proc );
940 if (func != NULL)
941 {
942 DBGPRINT( "Info: Proc \"%s\" loaded from ICD.", proc );
943 return func;
944 }
945
946 /* FIXME: Should we return wgl/gl 1.1 functions? */
947 SetLastError( ERROR_PROC_NOT_FOUND );
948 return NULL;
949 }
950
951 PROC
952 APIENTRY
953 rosglGetDefaultProcAddress( LPCSTR proc )
954 {
955 PROC func;
956 GLRC* glrc = (GLRC*)NtCurrentTeb()->glCurrentRC;
957
958 /* wglGetDefaultProcAddress does not flush the gl */
959
960 if (glrc == NULL)
961 {
962 DBGPRINT( "Error: No current GLRC!" );
963 SetLastError( ERROR_INVALID_FUNCTION );
964 return NULL;
965 }
966
967 func = glrc->icd->DrvGetProcAddress( proc );
968 if (func != NULL)
969 {
970 DBGPRINT( "Info: Proc \"%s\" loaded from ICD.", proc );
971 return func;
972 }
973
974 /* FIXME: Should we return wgl/gl 1.1 functions? */
975 SetLastError( ERROR_PROC_NOT_FOUND );
976 return NULL;
977 }
978
979
980 /*! \brief Make the given GLRC the threads current GLRC for hdc
981 *
982 * \param hdc [IN] Handle for a DC to be drawn on
983 * \param hglrc [IN] Handle for a GLRC to make current
984 *
985 * \retval TRUE Success
986 * \retval FALSE Failure
987 */
988 BOOL
989 APIENTRY
990 rosglMakeCurrent( HDC hdc, HGLRC hglrc )
991 {
992 GLRC *glrc = (GLRC *)hglrc;
993 ICDTable *icdTable = NULL;
994
995 DBGTRACE( "Called!" );
996
997 /* Is t a new context ? */
998 if (glrc != NULL)
999 {
1000 /* check hdc */
1001 if (GetObjectType( hdc ) != OBJ_DC && GetObjectType( hdc ) != OBJ_MEMDC)
1002 {
1003 DBGPRINT( "Error: hdc is not a DC handle!" );
1004 SetLastError( ERROR_INVALID_HANDLE );
1005 return FALSE;
1006 }
1007
1008 /* check if we know about this glrc */
1009 if (!ROSGL_ContainsContext( glrc ))
1010 {
1011 DBGPRINT( "Error: hglrc not found!" );
1012 SetLastError( ERROR_INVALID_HANDLE );
1013 return FALSE;
1014 }
1015
1016 /* check if it is available */
1017 if (glrc->is_current && glrc->thread_id != GetCurrentThreadId()) /* used by another thread */
1018 {
1019 DBGPRINT( "Error: hglrc is current for thread 0x%08x", glrc->thread_id );
1020 SetLastError( ERROR_INVALID_HANDLE );
1021 return FALSE;
1022 }
1023
1024 /* Unset previous one */
1025 if (NtCurrentTeb()->glCurrentRC != NULL)
1026 {
1027 GLRC *oldglrc = (GLRC*)NtCurrentTeb()->glCurrentRC;
1028 oldglrc->is_current = FALSE;
1029 oldglrc->thread_id = 0;
1030 oldglrc->hdc = NULL;
1031 oldglrc->icd->DrvReleaseContext(oldglrc->hglrc);
1032 }
1033
1034 /* call the ICD */
1035 if (glrc->hglrc != NULL)
1036 {
1037 DBGPRINT( "Info: Calling DrvSetContext!" );
1038 SetLastError( ERROR_SUCCESS );
1039 icdTable = glrc->icd->DrvSetContext( hdc, glrc->hglrc,
1040 (void *)ROSGL_SetContextCallBack );
1041 if (icdTable == NULL)
1042 {
1043 DBGPRINT( "Error: DrvSetContext failed (%d)\n", GetLastError() );
1044 NtCurrentTeb()->glCurrentRC = NULL;
1045 return FALSE;
1046 }
1047 DBGPRINT( "Info: DrvSetContext succeeded!" );
1048 }
1049
1050 /* make it current */
1051 glrc->is_current = TRUE;
1052 glrc->thread_id = GetCurrentThreadId();
1053 glrc->hdc = hdc;
1054 NtCurrentTeb()->glCurrentRC = (HGLRC)glrc;
1055 }
1056 else if(NtCurrentTeb()->glCurrentRC)
1057 {
1058 /* This is a call to unset the context */
1059 GLRC *oldglrc = (GLRC*)NtCurrentTeb()->glCurrentRC;
1060 oldglrc->is_current = FALSE;
1061 oldglrc->thread_id = 0;
1062 oldglrc->hdc = NULL;
1063 oldglrc->icd->DrvReleaseContext(oldglrc->hglrc);
1064 NtCurrentTeb()->glCurrentRC = NULL;
1065 }
1066 else
1067 {
1068 /*
1069 * To make wine tests happy.
1070 * Now, who cares if MakeCurrentContext(NULL, NULL) fails when current context is already NULL
1071 */
1072 if (GetObjectType( hdc ) != OBJ_DC && GetObjectType( hdc ) != OBJ_MEMDC)
1073 {
1074 DBGPRINT( "Error: hdc is not a DC handle!" );
1075 SetLastError( ERROR_INVALID_HANDLE );
1076 return FALSE;
1077 }
1078 }
1079
1080 if (ROSGL_SetContextCallBack( icdTable ) != ERROR_SUCCESS && icdTable == NULL)
1081 {
1082 DBGPRINT( "Warning: ROSGL_SetContextCallBack failed!" );
1083 }
1084
1085 return TRUE;
1086 }
1087
1088
1089 BOOL
1090 APIENTRY
1091 rosglRealizeLayerPalette( HDC hdc, int iLayerPlane, BOOL bRealize )
1092 {
1093 UNIMPLEMENTED;
1094 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1095 return FALSE;
1096 }
1097
1098
1099 int
1100 APIENTRY
1101 rosglSetLayerPaletteEntries( HDC hdc, int iLayerPlane, int iStart,
1102 int cEntries, CONST COLORREF *pcr )
1103 {
1104 UNIMPLEMENTED;
1105 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1106 return 0;
1107 }
1108
1109
1110 /*! \brief Set a DCs pixelformat
1111 *
1112 * \param hdc [IN] Handle to DC for which to set the format
1113 * \param iFormat [IN] Index of the pixelformat to set
1114 * \param pfd [IN] Not sure what this is for
1115 *
1116 * \retval TRUE Success
1117 * \retval FALSE Failure
1118 */
1119 BOOL
1120 WINAPI
1121 rosglSetPixelFormat( HDC hdc, int iFormat, CONST PIXELFORMATDESCRIPTOR *pfd )
1122 {
1123 GLDCDATA *dcdata;
1124 GLDRIVERDATA* icd;
1125
1126 DBGTRACE( "Called!" );
1127
1128 /* Get private DC data */
1129 dcdata = ROSGL_GetPrivateDCData( hdc );
1130 if (dcdata == NULL)
1131 {
1132 DBGPRINT( "Error: ROSGL_GetPrivateDCData() failed!" );
1133 return FALSE;
1134 }
1135 /* you can set the same pixel format twice, but you can't modify it */
1136 if(dcdata->pixel_format) return dcdata->pixel_format == iFormat;
1137
1138 icd = ROSGL_ICDForHDC(hdc);
1139 if(icd == NULL)
1140 return 0;
1141
1142 /* Call ICD function */
1143 if (!icd->DrvSetPixelFormat( hdc, iFormat, pfd ))
1144 {
1145 DBGPRINT( "Warning: DrvSetPixelFormat(format=%d) failed (%d)",
1146 iFormat, GetLastError() );
1147 return FALSE;
1148 }
1149 dcdata->pixel_format = iFormat;
1150
1151 return TRUE;
1152 }
1153
1154
1155 /*! \brief Enable display-list sharing between multiple GLRCs
1156 *
1157 * This will only work if both GLRCs are from the same driver.
1158 *
1159 * \param hglrc1 [IN] GLRC number 1
1160 * \param hglrc2 [IN] GLRC number 2
1161 *
1162 * \retval TRUE Success
1163 * \retval FALSE Failure
1164 */
1165 BOOL
1166 APIENTRY
1167 rosglShareLists( HGLRC hglrc1, HGLRC hglrc2 )
1168 {
1169 GLRC *glrc1 = (GLRC *)hglrc1;
1170 GLRC *glrc2 = (GLRC *)hglrc2;
1171
1172 /* check glrcs */
1173 if (!ROSGL_ContainsContext( glrc1 ))
1174 {
1175 DBGPRINT( "Error: hglrc1 not found!" );
1176 SetLastError( ERROR_INVALID_HANDLE );
1177 return FALSE;
1178 }
1179 if (!ROSGL_ContainsContext( glrc2 ))
1180 {
1181 DBGPRINT( "Error: hglrc2 not found!" );
1182 SetLastError( ERROR_INVALID_HANDLE );
1183 return FALSE;
1184 }
1185
1186 /* I think this is only possible within one ICD */
1187 if (glrc1->icd != glrc2->icd)
1188 {
1189 DBGPRINT( "Error: hglrc1 and hglrc2 use different ICDs!" );
1190 SetLastError( ERROR_INVALID_HANDLE );
1191 return FALSE;
1192 }
1193
1194 /* share lists (call ICD) */
1195 return glrc1->icd->DrvShareLists( glrc1->hglrc, glrc2->hglrc );
1196 }
1197
1198
1199 /*! \brief Flushes GL and swaps front/back buffer if appropriate
1200 *
1201 * \param hdc [IN] Handle to device context to swap buffers for
1202 *
1203 * \retval TRUE Success
1204 * \retval FALSE Failure
1205 */
1206 BOOL
1207 APIENTRY
1208 rosglSwapBuffers( HDC hdc )
1209 {
1210 GLDRIVERDATA *icd = ROSGL_ICDForHDC( hdc );
1211 DBGTRACE( "Called!" );
1212 if (icd != NULL)
1213 {
1214 DBGPRINT( "Swapping buffers!" );
1215 if (!icd->DrvSwapBuffers( hdc ))
1216 {
1217 DBGPRINT( "Error: DrvSwapBuffers failed (%d)", GetLastError() );
1218 return FALSE;
1219 }
1220 return TRUE;
1221 }
1222
1223 /* FIXME: implement own functionality? */
1224 SetLastError( ERROR_INVALID_FUNCTION );
1225 return FALSE;
1226 }
1227
1228
1229 BOOL
1230 APIENTRY
1231 rosglSwapLayerBuffers( HDC hdc, UINT fuPlanes )
1232 {
1233 BOOL ret = FALSE;
1234
1235 if(fuPlanes & WGL_SWAP_MAIN_PLANE)
1236 ret = rosglSwapBuffers(hdc);
1237
1238 if(fuPlanes &~WGL_SWAP_MAIN_PLANE)
1239 DBGTRACE("wglSwapLayerBuffers is not fully implemented\n");
1240
1241 return ret;
1242 }
1243
1244
1245 BOOL
1246 APIENTRY
1247 rosglUseFontBitmapsA( HDC hdc, DWORD first, DWORD count, DWORD listBase )
1248 {
1249 return IntUseFontBitmapsA(hdc, first, count, listBase);
1250 }
1251
1252
1253 BOOL
1254 APIENTRY
1255 rosglUseFontBitmapsW( HDC hdc, DWORD first, DWORD count, DWORD listBase )
1256 {
1257 return IntUseFontBitmapsW(hdc, first, count, listBase);
1258 }
1259
1260 BOOL
1261 APIENTRY
1262 rosglUseFontOutlinesA( HDC hdc, DWORD first, DWORD count, DWORD listBase,
1263 FLOAT deviation, FLOAT extrusion, int format,
1264 GLYPHMETRICSFLOAT *pgmf )
1265 {
1266 return IntUseFontOutlinesA(hdc, first, count, listBase, deviation, extrusion, format, pgmf);
1267 }
1268
1269
1270 BOOL
1271 APIENTRY
1272 rosglUseFontOutlinesW( HDC hdc, DWORD first, DWORD count, DWORD listBase,
1273 FLOAT deviation, FLOAT extrusion, int format,
1274 GLYPHMETRICSFLOAT *pgmf )
1275 {
1276 return IntUseFontOutlinesW(hdc, first, count, listBase, deviation, extrusion, format, pgmf);
1277 }
1278
1279 #ifdef __cplusplus
1280 }; /* extern "C" */
1281 #endif /* __cplusplus */
1282
1283 /* EOF */