1 /* QuesoGLC
2 * A free implementation of the OpenGL Character Renderer (GLC)
3 * Copyright (c) 2002, 2004-2009, Bertrand Coconnier
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 /* $Id: global.c 873 2009-01-18 17:51:17Z bcoconni $ */
20
21 /** \file
22 * defines the so-called "Global commands" described in chapter 3.4 of the
23 * GLC specs.
24 */
25
26 /** \defgroup global Global Commands
27 * Commands to create, manage and destroy GLC contexts.
28 *
29 * Those commands do not use GLC context state variables and can therefore be
30 * executed successfully if the issuing thread has no current GLC context.
31 *
32 * Each GLC context has a nonzero ID of type \b GLint. When a client is linked
33 * with a GLC library, the library maintains a list of IDs that contains one
34 * entry for each of the client's GLC contexts. The list is initially empty.
35 *
36 * Each client thread has a private GLC context ID variable that always
37 * contains either the value zero, indicating that the thread has no current
38 * GLC context, or the ID of the thread's current GLC context. The initial
39 * value is zero.
40 *
41 * When the ID of a GLC context is stored in the GLC context ID variable of a
42 * client thread, the context is said to be current to the thread. It is not
43 * possible for a GLC context to be current simultaneously to multiple
44 * threads. With the exception of the per-thread GLC error code and context ID
45 * variables, all of the GLC state variables that are used during the
46 * execution of a GLC command are stored in the issuing thread's current GLC
47 * context. To make a context current, call glcContext().
48 *
49 * When a client thread issues a GLC command, the thread's current GLC context
50 * executes the command.
51 *
52 * Note that the results of issuing a GL command when there is no current GL
53 * context are undefined. Because GLC issues GL commands, you must create a GL
54 * context and make it current before calling GLC.
55 *
56 * All other GLC commands raise \b GLC_STATE_ERROR if the issuing thread has
57 * no current GLC context.
58 */
59
60 #include "internal.h"
61 #include <stdlib.h>
62
63 #ifdef __GNUC__
64 __attribute__((constructor)) void init(void);
65 __attribute__((destructor)) void fini(void);
66 #else
67 void _init(void);
68 void _fini(void);
69 #endif
70
71
72
73
74 /* Since the common area can be accessed by any thread, this function should
75 * be called before any access (read or write) to the common area. Otherwise
76 * race conditions can occur. This function must also be used whenever we call
77 * a function which is not reentrant (it is the case for some Fontconfig
78 * entries).
79 * __glcLock/__glcUnlock can be nested : they keep track of the number of
80 * time they have been called and the mutex will be released as soon as
81 * __glcUnlock() will be called as many time as __glcLock() was.
82 */
__glcLock(void)83 void __glcLock(void)
84 {
85 __GLCthreadArea *area = NULL;
86
87 area = GLC_GET_THREAD_AREA();
88 assert(area);
89
90 if (!area->lockState)
91 #ifdef __WIN32__
92 EnterCriticalSection(&__glcCommonArea.section);
93 #else
94 pthread_mutex_lock(&__glcCommonArea.mutex);
95 #endif
96
97 area->lockState++;
98 }
99
100
101
102 /* Unlock the mutex in order to allow other threads to make accesses to the
103 * common area.
104 * See also the note on nested calls in __glcLock's description.
105 */
__glcUnlock(void)106 void __glcUnlock(void)
107 {
108 __GLCthreadArea *area = NULL;
109
110 area = GLC_GET_THREAD_AREA();
111 assert(area);
112
113 area->lockState--;
114 if (!area->lockState)
115 #ifdef __WIN32__
116 LeaveCriticalSection(&__glcCommonArea.section);
117 #else
118 pthread_mutex_unlock(&__glcCommonArea.mutex);
119 #endif
120 }
121
122
123
124 #if !defined(HAVE_TLS) && !defined(__WIN32__)
125 /* This function is called each time a pthread is cancelled or exits in order
126 * to free its specific area
127 */
__glcFreeThreadArea(void * keyValue)128 static void __glcFreeThreadArea(void *keyValue)
129 {
130 __GLCthreadArea *area = (__GLCthreadArea*)keyValue;
131 __GLCcontext *ctx = NULL;
132
133 if (area) {
134 /* Release the context which is current to the thread, if any */
135 ctx = area->currentContext;
136 if (ctx)
137 ctx->isCurrent = GL_FALSE;
138 free(area); /* DO NOT use __glcFree() !!! */
139 }
140 }
141 #endif /* !HAVE_TLS && !__WIN32__ */
142
143
144
145 /* This function is called when QuesoGLC is no longer needed.
146 */
147 #ifdef __GNUC__
fini(void)148 __attribute__((destructor)) void fini(void)
149 #else
150 void _fini(void)
151 #endif
152 {
153 FT_ListNode node = NULL;
154 #if 0
155 void *key = NULL;
156 #endif
157
158 __glcLock();
159
160 /* destroy remaining contexts */
161 node = __glcCommonArea.contextList.head;
162 while (node) {
163 FT_ListNode next = node->next;
164 __glcContextDestroy((__GLCcontext*)node);
165 node = next;
166 }
167
168 #if FC_MINOR > 2
169 FcFini();
170 #endif
171
172 __glcUnlock();
173 #ifdef __WIN32__
174 DeleteCriticalSection(&__glcCommonArea.section);
175 #else
176 pthread_mutex_destroy(&__glcCommonArea.mutex);
177 #endif
178
179 #if 0
180 /* Destroy the thread local storage */
181 key = pthread_getspecific(__glcCommonArea.threadKey);
182 if (key)
183 __glcFreeThreadArea(key);
184
185 pthread_key_delete(__glcCommonArea.threadKey);
186 #endif
187 }
188
189
190
191 /* Routines for memory management of FreeType
192 * The memory manager of our FreeType library class uses the same memory
193 * allocation functions than QuesoGLC
194 */
__glcAllocFunc(FT_Memory GLC_UNUSED_ARG (inMemory),long inSize)195 static void* __glcAllocFunc(FT_Memory GLC_UNUSED_ARG(inMemory), long inSize)
196 {
197 return malloc(inSize);
198 }
199
__glcFreeFunc(FT_Memory GLC_UNUSED_ARG (inMemory),void * inBlock)200 static void __glcFreeFunc(FT_Memory GLC_UNUSED_ARG(inMemory), void *inBlock)
201 {
202 free(inBlock);
203 }
204
__glcReallocFunc(FT_Memory GLC_UNUSED_ARG (inMemory),long GLC_UNUSED_ARG (inCurSize),long inNewSize,void * inBlock)205 static void* __glcReallocFunc(FT_Memory GLC_UNUSED_ARG(inMemory),
206 long GLC_UNUSED_ARG(inCurSize),
207 long inNewSize, void* inBlock)
208 {
209 return realloc(inBlock, inNewSize);
210 }
211
212
213
214 /* This function is called before any function of QuesoGLC
215 * is used. It reserves memory and initialiazes the library, hence the name.
216 */
217 #ifdef __GNUC__
init(void)218 __attribute__((constructor)) void init(void)
219 #else
220 void _init(void)
221 #endif
222 {
223 #if !defined(__WIN32__) && !defined(HAVE_TLS)
224 /* A temporary variable is used to store the PTHREAD_ONCE_INIT value because
225 * some platforms (namely Mac OSX) define PTHREAD_ONCE_INIT as a structure
226 * initialization "{.., ..}" which is not allowed to be used by C99 anywhere
227 * but at variables declaration.
228 */
229 pthread_once_t onceInit = PTHREAD_ONCE_INIT;
230 #endif
231
232 /* Initialize fontconfig */
233 if (!FcInit())
234 goto FatalError;
235
236 __glcCommonArea.versionMajor = 0;
237 __glcCommonArea.versionMinor = 2;
238
239 /* Create the thread-local storage for GLC errors */
240 #ifdef __WIN32__
241 __glcCommonArea.threadKey = TlsAlloc();
242 if (__glcCommonArea.threadKey == 0xffffffff)
243 goto FatalError;
244 __glcCommonArea.__glcInitThreadOnce = 0;
245 #elif !defined(HAVE_TLS)
246 if (pthread_key_create(&__glcCommonArea.threadKey, __glcFreeThreadArea))
247 goto FatalError;
248 /* Now we can initialize our actual once_control variable by copying the value
249 * of the temporary variable onceInit.
250 */
251 __glcCommonArea.__glcInitThreadOnce = onceInit;
252 #endif
253
254 __glcCommonArea.memoryManager.user = NULL;
255 __glcCommonArea.memoryManager.alloc = __glcAllocFunc;
256 __glcCommonArea.memoryManager.free = __glcFreeFunc;
257 __glcCommonArea.memoryManager.realloc = __glcReallocFunc;
258
259 /* Initialize the list of context states */
260 __glcCommonArea.contextList.head = NULL;
261 __glcCommonArea.contextList.tail = NULL;
262
263 /* Initialize the mutex for access to the contextList array */
264 #ifdef __WIN32__
265 InitializeCriticalSection(&__glcCommonArea.section);
266 #else
267 if (pthread_mutex_init(&__glcCommonArea.mutex, NULL))
268 goto FatalError;
269 #endif
270
271 return;
272
273 FatalError:
274 __glcRaiseError(GLC_RESOURCE_ERROR);
275
276 /* Is there a better thing to do than that ? */
277 perror("GLC Fatal Error");
278 exit(-1);
279 }
280
281
282
283 #if defined __WIN32__ && !defined __GNUC__
DllMain(HANDLE hinstDLL,DWORD dwReason,LPVOID lpvReserved)284 BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
285 {
286 switch(dwReason) {
287 case DLL_PROCESS_ATTACH:
288 _init();
289 return TRUE;
290 case DLL_PROCESS_DETACH:
291 _fini();
292 return TRUE;
293 }
294 return TRUE;
295 }
296 #endif
297
298
299
300 /* Get the context state corresponding to a given context ID */
__glcGetContext(GLint inContext)301 static __GLCcontext* __glcGetContext(GLint inContext)
302 {
303 FT_ListNode node = NULL;
304
305 __glcLock();
306 for (node = __glcCommonArea.contextList.head; node; node = node->next)
307 if (((__GLCcontext*)node)->id == inContext) break;
308
309 __glcUnlock();
310
311 return (__GLCcontext*)node;
312 }
313
314
315
316 /** \ingroup global
317 * This command checks whether \e inContext is the ID of one of the client's
318 * GLC context and returns \b GLC_TRUE if and only if it is.
319 * \param inContext The context ID to be tested
320 * \return \b GL_TRUE if \e inContext is the ID of a GLC context,
321 * \b GL_FALSE otherwise
322 * \sa glcDeleteContext()
323 * \sa glcGenContext()
324 * \sa glcGetAllContexts()
325 * \sa glcContext()
326 */
glcIsContext(GLint inContext)327 GLboolean APIENTRY glcIsContext(GLint inContext)
328 {
329 GLC_INIT_THREAD();
330
331 return (__glcGetContext(inContext) ? GL_TRUE : GL_FALSE);
332 }
333
334
335
336 /** \ingroup global
337 * Returns the value of the issuing thread's current GLC context ID variable
338 * \return The context ID of the current thread
339 * \sa glcContext()
340 * \sa glcDeleteContext()
341 * \sa glcGenContext()
342 * \sa glcGetAllContexts()
343 * \sa glcIsContext()
344 */
glcGetCurrentContext(void)345 GLint APIENTRY glcGetCurrentContext(void)
346 {
347 __GLCcontext *ctx = NULL;
348
349 GLC_INIT_THREAD();
350
351 ctx = GLC_GET_CURRENT_CONTEXT();
352 if (!ctx)
353 return 0;
354 else
355 return ctx->id;
356 }
357
358
359
360 /** \ingroup global
361 * Marks for deletion the GLC context identified by \e inContext. If the
362 * marked context is not current to any client thread, the command deletes
363 * the marked context immediatly. Otherwise, the marked context will be
364 * deleted during the execution of the next glcContext() command that causes
365 * it not to be current to any client thread.
366 *
367 * \note glcDeleteContext() does not destroy the GL objects associated with
368 * the context \e inContext. Indeed for performance reasons, GLC does not keep
369 * track of the GL context that contains the GL objects associated with the
370 * the GLC context that is destroyed. Even if GLC would keep track of the GL
371 * context, it could lead GLC to temporarily change the GL context, delete the
372 * GL objects, then restore the correct GL context. In order not to adversely
373 * impact the performance of most of programs, it is the responsability of the
374 * user to call glcDeleteGLObjects() on a GLC context that is intended to be
375 * destroyed.
376 *
377 * The command raises \b GLC_PARAMETER_ERROR if \e inContext is not the ID of
378 * one of the client's GLC contexts.
379 * \param inContext The ID of the context to be deleted
380 * \sa glcGetAllContexts()
381 * \sa glcIsContext()
382 * \sa glcContext()
383 * \sa glcGetCurrentContext()
384 */
glcDeleteContext(GLint inContext)385 void APIENTRY glcDeleteContext(GLint inContext)
386 {
387 __GLCcontext *ctx = NULL;
388
389 GLC_INIT_THREAD();
390
391 /* Lock the "Common Area" in order to prevent race conditions. Indeed, we
392 * must prevent other threads to make current the context that we are
393 * destroying.
394 */
395 __glcLock();
396
397 /* verify if the context exists */
398 ctx = __glcGetContext(inContext);
399
400 if (!ctx) {
401 __glcRaiseError(GLC_PARAMETER_ERROR);
402 __glcUnlock();
403 return;
404 }
405
406 if (ctx->isCurrent)
407 /* The context is current to a thread : just mark for deletion */
408 ctx->pendingDelete = GL_TRUE;
409 else {
410 /* Remove the context from the context list then destroy it */
411 FT_List_Remove(&__glcCommonArea.contextList, (FT_ListNode)ctx);
412 ctx->isInGlobalCommand = GL_TRUE;
413 __glcContextDestroy(ctx);
414 }
415
416 __glcUnlock();
417 }
418
419
420
421 /** \ingroup global
422 * Assigns the value \e inContext to the issuing thread's current GLC context
423 * ID variable. If another context is already current to the thread, no error
424 * is generated but the context is released and the context identified by
425 * \e inContext is made current to the thread.
426 *
427 * Call \e glcContext with \e inContext set to zero to release a thread's
428 * current context.
429 *
430 * When a GLCcontext is made current to a thread, GLC issues the commands
431 * \code
432 * glGetString(GL_VERSION);
433 * glGetString(GL_EXTENSIONS);
434 * \endcode
435 * and stores the returned strings. If there is no GL context current to the
436 * thread, the result of the above GL commands is undefined and so is the
437 * result of glcContext().
438 *
439 * The command raises \b GLC_PARAMETER_ERROR if \e inContext is not zero
440 * and is not the ID of one of the client's GLC contexts. \n
441 * The command raises \b GLC_STATE_ERROR if \e inContext is the ID of a GLC
442 * context that is current to a thread other than the issuing thread. \n
443 * The command raises \b GLC_STATE_ERROR if the issuing thread is executing
444 * a callback function that has been called from GLC.
445 * \param inContext The ID of the context to be made current
446 * \sa glcGetCurrentContext()
447 * \sa glcDeleteContext()
448 * \sa glcGenContext()
449 * \sa glcGetAllContexts()
450 * \sa glcIsContext()
451 */
glcContext(GLint inContext)452 void APIENTRY glcContext(GLint inContext)
453 {
454 __GLCcontext *currentContext = NULL;
455 __GLCcontext *ctx = NULL;
456 __GLCthreadArea *area = NULL;
457
458 GLC_INIT_THREAD();
459
460 if (inContext < 0) {
461 __glcRaiseError(GLC_PARAMETER_ERROR);
462 return;
463 }
464
465 area = GLC_GET_THREAD_AREA();
466 assert(area);
467
468 /* Lock the "Common Area" in order to prevent race conditions */
469 __glcLock();
470
471 if (inContext) {
472 /* verify that the context exists */
473 ctx = __glcGetContext(inContext);
474
475 if (!ctx) {
476 __glcRaiseError(GLC_PARAMETER_ERROR);
477 __glcUnlock();
478 return;
479 }
480
481 /* Get the current context of the issuing thread */
482 currentContext = area->currentContext;
483
484 /* Check if the issuing thread is executing a callback
485 * function that has been called from GLC
486 */
487 if (currentContext) {
488 if (currentContext->isInCallbackFunc) {
489 __glcRaiseError(GLC_STATE_ERROR);
490 __glcUnlock();
491 return;
492 }
493 }
494
495 /* Is the context already current to a thread ? */
496 if (ctx->isCurrent) {
497 /* If the context is current to another thread => ERROR ! */
498 if (!currentContext) {
499 __glcRaiseError(GLC_STATE_ERROR);
500 }
501 else {
502 if (currentContext->id != inContext)
503 __glcRaiseError(GLC_STATE_ERROR);
504 }
505
506 /* If we get there, this means that the context 'inContext'
507 * is already current to one thread (whether it is the issuing thread
508 * or not) : there is nothing else to be done.
509 */
510 __glcUnlock();
511 return;
512 }
513
514 /* Release old current context if any */
515 if (currentContext)
516 currentContext->isCurrent = GL_FALSE;
517
518 /* Make the context current to the thread */
519 area->currentContext = ctx;
520 ctx->isCurrent = GL_TRUE;
521 }
522 else {
523 /* inContext is null, the current thread must release its context if any */
524
525 /* Gets the current context state */
526 currentContext = area->currentContext;
527
528 if (currentContext) {
529 /* Deassociate the context from the issuing thread */
530 area->currentContext = NULL;
531 /* Release the context */
532 currentContext->isCurrent = GL_FALSE;
533 }
534 }
535
536 /* Execute pending deletion if any. Here, the variable name 'currentContext'
537 * is not appropriate any more : 'currentContext' used to be the current
538 * context but it has either been replaced by another one or it has been
539 * released.
540 */
541 if (currentContext) {
542 if (currentContext->pendingDelete) {
543 FT_List_Remove(&__glcCommonArea.contextList, (FT_ListNode)currentContext);
544 currentContext->isInGlobalCommand = GL_TRUE;
545 __glcContextDestroy(currentContext);
546 }
547 }
548
549 __glcUnlock();
550
551 /* If the issuing thread has released its context then there is no point to
552 * check for OpenGL extensions.
553 */
554 if (!inContext)
555 return;
556
557 /* During its initialization, GLEW calls glGetString(GL_VERSION) and
558 * glGetString(GL_EXTENSIONS) so, not only glewInit() allows to determine the
559 * available extensions, but it also allows to be conformant with GLC specs
560 * which require to issue those GL commands.
561 *
562 * Notice however that some OpenGL implementations return a void pointer if
563 * the function glGetString() is called while no GL context is bound to the
564 * current thread. However this behaviour is not required by the GL specs, so
565 * those calls may just fail or lead to weird results or even crash the app
566 * (on Mac OSX). There is nothing that we can do against that : the GLC specs
567 * make it very clear that glGetString() is called by glcContext() and the
568 * QuesoGLC docs tell that the behaviour of GLC is undefined if no GL context
569 * is current while issuing GL commands.
570 */
571 if (glewInit() != GLEW_OK)
572 __glcRaiseError(GLC_RESOURCE_ERROR);
573 }
574
575
576
577 /** \ingroup global
578 * Generates a new GLC context and returns its ID.
579 * \return The ID of the new context
580 * \sa glcGetAllContexts()
581 * \sa glcIsContext()
582 * \sa glcContext()
583 * \sa glcGetCurrentContext()
584 */
glcGenContext(void)585 GLint APIENTRY glcGenContext(void)
586 {
587 int newContext = 0;
588 __GLCcontext *ctx = NULL;
589 FT_ListNode node = NULL;
590
591 GLC_INIT_THREAD();
592
593 /* Create a new context */
594 ctx = __glcContextCreate(0);
595 if (!ctx)
596 return 0;
597
598 /* Lock the "Common Area" in order to prevent race conditions */
599 __glcLock();
600
601 /* Search for the first context ID that is unused */
602 if (!__glcCommonArea.contextList.tail)
603 newContext = 1;
604 else
605 newContext = ((__GLCcontext*)__glcCommonArea.contextList.tail)->id + 1;
606
607 ctx->id = newContext;
608
609 node = (FT_ListNode)ctx;
610 node->data = ctx;
611 FT_List_Add(&__glcCommonArea.contextList, node);
612
613 __glcUnlock();
614
615 return newContext;
616 }
617
618
619
620 /** \ingroup global
621 * Returns a zero terminated array of GLC context IDs that contains one entry
622 * for each of the client's GLC contexts. GLC uses the ISO C library command
623 * \c malloc to allocate the array. The client should use the ISO C library
624 * command \c free to deallocate the array when it is no longer needed.
625 * \return The pointer to the array of context IDs.
626 * \sa glcContext()
627 * \sa glcDeleteContext()
628 * \sa glcGenContext()
629 * \sa glcGetCurrentContext()
630 * \sa glcIsContext()
631 */
glcGetAllContexts(void)632 GLint* APIENTRY glcGetAllContexts(void)
633 {
634 int count = 0;
635 GLint* contextArray = NULL;
636 FT_ListNode node = NULL;
637
638 GLC_INIT_THREAD();
639
640 /* Count the number of existing contexts (whether they are current to a
641 * thread or not).
642 */
643 __glcLock();
644 for (node = __glcCommonArea.contextList.head, count = 0; node;
645 node = node->next, count++);
646
647 /* Allocate memory to store the array */
648 contextArray = (GLint *)__glcMalloc(sizeof(GLint) * (count+1));
649 if (!contextArray) {
650 __glcRaiseError(GLC_RESOURCE_ERROR);
651 __glcUnlock();
652 return NULL;
653 }
654
655 /* Array must be null-terminated */
656 contextArray[count] = 0;
657
658 /* Copy the context IDs to the array */
659 for (node = __glcCommonArea.contextList.tail; node;node = node->prev)
660 contextArray[--count] = ((__GLCcontext*)node)->id;
661
662 __glcUnlock();
663
664 return contextArray;
665 }
666
667
668
669 /** \ingroup global
670 * Retrieves the value of the issuing thread's GLC error code variable,
671 * assigns the value \b GLC_NONE to that variable, and returns the retrieved
672 * value.
673 * \note In contrast to the GL function \c glGetError, \e glcGetError only
674 * returns one error, not a list of errors.
675 * \return An error code from the table below : \n\n
676 * <center>
677 * <table>
678 * <caption>Error codes</caption>
679 * <tr>
680 * <td>Name</td> <td>Enumerant</td>
681 * </tr>
682 * <tr>
683 * <td><b>GLC_NONE</b></td> <td>0x0000</td>
684 * </tr>
685 * <tr>
686 * <td><b>GLC_PARAMETER_ERROR</b></td> <td>0x0040</td>
687 * </tr>
688 * <tr>
689 * <td><b>GLC_RESOURCE_ERROR</b></td> <td>0x0041</td>
690 * </tr>
691 * <tr>
692 * <td><b>GLC_STATE_ERROR</b></td> <td>0x0042</td>
693 * </tr>
694 * </table>
695 * </center>
696 */
glcGetError(void)697 GLCenum APIENTRY glcGetError(void)
698 {
699 GLCenum error = GLC_NONE;
700 __GLCthreadArea * area = NULL;
701
702 GLC_INIT_THREAD();
703
704 area = GLC_GET_THREAD_AREA();
705 assert(area);
706
707 error = area->errorState;
708 __glcRaiseError(GLC_NONE);
709 return error;
710 }
711