dixutils.c revision 1b5d61b8
1/***********************************************************
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27                        All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45******************************************************************/
46
47/*
48
49(c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved.
50
51Permission to use, copy, modify, distribute, and sublicense this software and its
52documentation for any purpose and without fee is hereby granted, provided that
53the above copyright notices appear in all copies and that both those copyright
54notices and this permission notice appear in supporting documentation and that
55the name of Adobe Systems Incorporated not be used in advertising or publicity
56pertaining to distribution of the software without specific, written prior
57permission.  No trademark license to use the Adobe trademarks is hereby
58granted.  If the Adobe trademark "Display PostScript"(tm) is used to describe
59this software, its functionality or for any other purpose, such use shall be
60limited to a statement that this software works in conjunction with the Display
61PostScript system.  Proper trademark attribution to reflect Adobe's ownership
62of the trademark shall be given whenever any such reference to the Display
63PostScript system is made.
64
65ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY
66PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.  ADOBE
67DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
68WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
69INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE TO YOU
70OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
71DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT
72LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR
73PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER
74SUPPORT FOR THE SOFTWARE.
75
76Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
77Incorporated which may be registered in certain jurisdictions.
78
79Author:  Adobe Systems Incorporated
80
81*/
82
83#ifdef HAVE_DIX_CONFIG_H
84#include <dix-config.h>
85#endif
86
87#include <X11/X.h>
88#include <X11/Xmd.h>
89#include "misc.h"
90#include "windowstr.h"
91#include "dixstruct.h"
92#include "pixmapstr.h"
93#include "gcstruct.h"
94#include "scrnintstr.h"
95#define  XK_LATIN1
96#include <X11/keysymdef.h>
97#include "xace.h"
98
99/*
100 * CompareTimeStamps returns -1, 0, or +1 depending on if the first
101 * argument is less than, equal to or greater than the second argument.
102 */
103
104int
105CompareTimeStamps(TimeStamp a, TimeStamp b)
106{
107    if (a.months < b.months)
108        return EARLIER;
109    if (a.months > b.months)
110        return LATER;
111    if (a.milliseconds < b.milliseconds)
112        return EARLIER;
113    if (a.milliseconds > b.milliseconds)
114        return LATER;
115    return SAMETIME;
116}
117
118/*
119 * convert client times to server TimeStamps
120 */
121
122#define HALFMONTH ((unsigned long) 1<<31)
123TimeStamp
124ClientTimeToServerTime(CARD32 c)
125{
126    TimeStamp ts;
127
128    if (c == CurrentTime)
129        return currentTime;
130    ts.months = currentTime.months;
131    ts.milliseconds = c;
132    if (c > currentTime.milliseconds) {
133        if (((unsigned long) c - currentTime.milliseconds) > HALFMONTH)
134            ts.months -= 1;
135    }
136    else if (c < currentTime.milliseconds) {
137        if (((unsigned long) currentTime.milliseconds - c) > HALFMONTH)
138            ts.months += 1;
139    }
140    return ts;
141}
142
143/*
144 * ISO Latin-1 case conversion routine
145 *
146 * this routine always null-terminates the result, so
147 * beware of too-small buffers
148 */
149
150static unsigned char
151ISOLatin1ToLower(unsigned char source)
152{
153    unsigned char dest;
154
155    if ((source >= XK_A) && (source <= XK_Z))
156        dest = source + (XK_a - XK_A);
157    else if ((source >= XK_Agrave) && (source <= XK_Odiaeresis))
158        dest = source + (XK_agrave - XK_Agrave);
159    else if ((source >= XK_Ooblique) && (source <= XK_Thorn))
160        dest = source + (XK_oslash - XK_Ooblique);
161    else
162        dest = source;
163    return dest;
164}
165
166int
167CompareISOLatin1Lowered(const unsigned char *s1, int s1len,
168                        const unsigned char *s2, int s2len)
169{
170    unsigned char c1, c2;
171
172    for (;;) {
173        /* note -- compare against zero so that -1 ignores len */
174        c1 = s1len-- ? *s1++ : '\0';
175        c2 = s2len-- ? *s2++ : '\0';
176        if (!c1 ||
177            (c1 != c2 &&
178             (c1 = ISOLatin1ToLower(c1)) != (c2 = ISOLatin1ToLower(c2))))
179            break;
180    }
181    return (int) c1 - (int) c2;
182}
183
184/*
185 * dixLookupWindow and dixLookupDrawable:
186 * Look up the window/drawable taking into account the client doing the
187 * lookup, the type of drawable desired, and the type of access desired.
188 * Return Success with *pDraw set if the window/drawable exists and the client
189 * is allowed access, else return an error code with *pDraw set to NULL.  The
190 * access mask values are defined in resource.h.  The type mask values are
191 * defined in pixmap.h, with zero equivalent to M_DRAWABLE.
192 */
193int
194dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client,
195                  Mask type, Mask access)
196{
197    DrawablePtr pTmp;
198    int rc;
199
200    *pDraw = NULL;
201
202    rc = dixLookupResourceByClass((void **) &pTmp, id, RC_DRAWABLE, client,
203                                  access);
204
205    if (rc != Success)
206        client->errorValue = id;
207
208    if (rc == BadValue)
209        return BadDrawable;
210    if (rc != Success)
211        return rc;
212    if (!((1 << pTmp->type) & (type ? type : M_DRAWABLE)))
213        return BadMatch;
214
215    *pDraw = pTmp;
216    return Success;
217}
218
219int
220dixLookupWindow(WindowPtr *pWin, XID id, ClientPtr client, Mask access)
221{
222    int rc;
223
224    rc = dixLookupDrawable((DrawablePtr *) pWin, id, client, M_WINDOW, access);
225    /* dixLookupDrawable returns BadMatch iff id is a valid Drawable
226       but is not a Window. Users of dixLookupWindow expect a BadWindow
227       error in this case; they don't care that it's a valid non-Window XID */
228    if (rc == BadMatch)
229        rc = BadWindow;
230    /* Similarly, users of dixLookupWindow don't want BadDrawable. */
231    if (rc == BadDrawable)
232        rc = BadWindow;
233    return rc;
234}
235
236int
237dixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access)
238{
239    return dixLookupResourceByType((void **) pGC, id, RT_GC, client, access);
240}
241
242int
243dixLookupFontable(FontPtr *pFont, XID id, ClientPtr client, Mask access)
244{
245    int rc;
246    GC *pGC;
247
248    client->errorValue = id;    /* EITHER font or gc */
249    rc = dixLookupResourceByType((void **) pFont, id, RT_FONT, client,
250                                 access);
251    if (rc != BadFont)
252        return rc;
253    rc = dixLookupResourceByType((void **) &pGC, id, RT_GC, client, access);
254    if (rc == BadGC)
255        return BadFont;
256    if (rc == Success)
257        *pFont = pGC->font;
258    return rc;
259}
260
261int
262dixLookupClient(ClientPtr *pClient, XID rid, ClientPtr client, Mask access)
263{
264    void *pRes;
265    int rc = BadValue, clientIndex = CLIENT_ID(rid);
266
267    if (!clientIndex || !clients[clientIndex] || (rid & SERVER_BIT))
268        goto bad;
269
270    rc = dixLookupResourceByClass(&pRes, rid, RC_ANY, client, DixGetAttrAccess);
271    if (rc != Success)
272        goto bad;
273
274    rc = XaceHook(XACE_CLIENT_ACCESS, client, clients[clientIndex], access);
275    if (rc != Success)
276        goto bad;
277
278    *pClient = clients[clientIndex];
279    return Success;
280 bad:
281    if (client)
282        client->errorValue = rid;
283    *pClient = NULL;
284    return rc;
285}
286
287int
288AlterSaveSetForClient(ClientPtr client, WindowPtr pWin, unsigned mode,
289                      Bool toRoot, Bool map)
290{
291    int numnow;
292    SaveSetElt *pTmp = NULL;
293    int j;
294
295    numnow = client->numSaved;
296    j = 0;
297    if (numnow) {
298        pTmp = client->saveSet;
299        while ((j < numnow) && (SaveSetWindow(pTmp[j]) != (void *) pWin))
300            j++;
301    }
302    if (mode == SetModeInsert) {
303        if (j < numnow)         /* duplicate */
304            return Success;
305        numnow++;
306        pTmp = (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow);
307        if (!pTmp)
308            return BadAlloc;
309        client->saveSet = pTmp;
310        client->numSaved = numnow;
311        SaveSetAssignWindow(client->saveSet[numnow - 1], pWin);
312        SaveSetAssignToRoot(client->saveSet[numnow - 1], toRoot);
313        SaveSetAssignMap(client->saveSet[numnow - 1], map);
314        return Success;
315    }
316    else if ((mode == SetModeDelete) && (j < numnow)) {
317        while (j < numnow - 1) {
318            pTmp[j] = pTmp[j + 1];
319            j++;
320        }
321        numnow--;
322        if (numnow) {
323            pTmp =
324                (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow);
325            if (pTmp)
326                client->saveSet = pTmp;
327        }
328        else {
329            free(client->saveSet);
330            client->saveSet = (SaveSetElt *) NULL;
331        }
332        client->numSaved = numnow;
333        return Success;
334    }
335    return Success;
336}
337
338void
339DeleteWindowFromAnySaveSet(WindowPtr pWin)
340{
341    int i;
342    ClientPtr client;
343
344    for (i = 0; i < currentMaxClients; i++) {
345        client = clients[i];
346        if (client && client->numSaved)
347            (void) AlterSaveSetForClient(client, pWin, SetModeDelete, FALSE,
348                                         TRUE);
349    }
350}
351
352/* No-op Don't Do Anything : sometimes we need to be able to call a procedure
353 * that doesn't do anything.  For example, on screen with only static
354 * colormaps, if someone calls install colormap, it's easier to have a dummy
355 * procedure to call than to check if there's a procedure
356 */
357void
358NoopDDA(void)
359{
360}
361
362typedef struct _BlockHandler {
363    ServerBlockHandlerProcPtr BlockHandler;
364    ServerWakeupHandlerProcPtr WakeupHandler;
365    void *blockData;
366    Bool deleted;
367} BlockHandlerRec, *BlockHandlerPtr;
368
369static BlockHandlerPtr handlers;
370static int numHandlers;
371static int sizeHandlers;
372static Bool inHandler;
373static Bool handlerDeleted;
374
375/**
376 *
377 *  \param pTimeout   DIX doesn't want to know how OS represents time
378 *  \param pReadMask  nor how it represents the det of descriptors
379 */
380void
381BlockHandler(void *pTimeout)
382{
383    int i, j;
384
385    ++inHandler;
386    for (i = 0; i < numHandlers; i++)
387        if (!handlers[i].deleted)
388            (*handlers[i].BlockHandler) (handlers[i].blockData, pTimeout);
389
390    for (i = 0; i < screenInfo.numGPUScreens; i++)
391        (*screenInfo.gpuscreens[i]->BlockHandler) (screenInfo.gpuscreens[i], pTimeout);
392
393    for (i = 0; i < screenInfo.numScreens; i++)
394        (*screenInfo.screens[i]->BlockHandler) (screenInfo.screens[i], pTimeout);
395
396    if (handlerDeleted) {
397        for (i = 0; i < numHandlers;)
398            if (handlers[i].deleted) {
399                for (j = i; j < numHandlers - 1; j++)
400                    handlers[j] = handlers[j + 1];
401                numHandlers--;
402            }
403            else
404                i++;
405        handlerDeleted = FALSE;
406    }
407    --inHandler;
408}
409
410/**
411 *
412 *  \param result    32 bits of undefined result from the wait
413 *  \param pReadmask the resulting descriptor mask
414 */
415void
416WakeupHandler(int result)
417{
418    int i, j;
419
420    ++inHandler;
421    for (i = 0; i < screenInfo.numScreens; i++)
422        (*screenInfo.screens[i]->WakeupHandler) (screenInfo.screens[i], result);
423    for (i = 0; i < screenInfo.numGPUScreens; i++)
424        (*screenInfo.gpuscreens[i]->WakeupHandler) (screenInfo.gpuscreens[i], result);
425    for (i = numHandlers - 1; i >= 0; i--)
426        if (!handlers[i].deleted)
427            (*handlers[i].WakeupHandler) (handlers[i].blockData, result);
428    if (handlerDeleted) {
429        for (i = 0; i < numHandlers;)
430            if (handlers[i].deleted) {
431                for (j = i; j < numHandlers - 1; j++)
432                    handlers[j] = handlers[j + 1];
433                numHandlers--;
434            }
435            else
436                i++;
437        handlerDeleted = FALSE;
438    }
439    --inHandler;
440}
441
442/**
443 * Reentrant with BlockHandler and WakeupHandler, except wakeup won't
444 * get called until next time
445 */
446Bool
447RegisterBlockAndWakeupHandlers(ServerBlockHandlerProcPtr blockHandler,
448                               ServerWakeupHandlerProcPtr wakeupHandler,
449                               void *blockData)
450{
451    BlockHandlerPtr new;
452
453    if (numHandlers >= sizeHandlers) {
454        new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) *
455                                        sizeof(BlockHandlerRec));
456        if (!new)
457            return FALSE;
458        handlers = new;
459        sizeHandlers = numHandlers + 1;
460    }
461    handlers[numHandlers].BlockHandler = blockHandler;
462    handlers[numHandlers].WakeupHandler = wakeupHandler;
463    handlers[numHandlers].blockData = blockData;
464    handlers[numHandlers].deleted = FALSE;
465    numHandlers = numHandlers + 1;
466    return TRUE;
467}
468
469void
470RemoveBlockAndWakeupHandlers(ServerBlockHandlerProcPtr blockHandler,
471                             ServerWakeupHandlerProcPtr wakeupHandler,
472                             void *blockData)
473{
474    int i;
475
476    for (i = 0; i < numHandlers; i++)
477        if (handlers[i].BlockHandler == blockHandler &&
478            handlers[i].WakeupHandler == wakeupHandler &&
479            handlers[i].blockData == blockData) {
480            if (inHandler) {
481                handlerDeleted = TRUE;
482                handlers[i].deleted = TRUE;
483            }
484            else {
485                for (; i < numHandlers - 1; i++)
486                    handlers[i] = handlers[i + 1];
487                numHandlers--;
488            }
489            break;
490        }
491}
492
493void
494InitBlockAndWakeupHandlers(void)
495{
496    free(handlers);
497    handlers = (BlockHandlerPtr) 0;
498    numHandlers = 0;
499    sizeHandlers = 0;
500}
501
502/*
503 * A general work queue.  Perform some task before the server
504 * sleeps for input.
505 */
506
507WorkQueuePtr workQueue;
508static WorkQueuePtr *workQueueLast = &workQueue;
509
510void
511ProcessWorkQueue(void)
512{
513    WorkQueuePtr q, *p;
514
515    p = &workQueue;
516    /*
517     * Scan the work queue once, calling each function.  Those
518     * which return TRUE are removed from the queue, otherwise
519     * they will be called again.  This must be reentrant with
520     * QueueWorkProc.
521     */
522    while ((q = *p)) {
523        if ((*q->function) (q->client, q->closure)) {
524            /* remove q from the list */
525            *p = q->next;       /* don't fetch until after func called */
526            free(q);
527        }
528        else {
529            p = &q->next;       /* don't fetch until after func called */
530        }
531    }
532    workQueueLast = p;
533}
534
535void
536ProcessWorkQueueZombies(void)
537{
538    WorkQueuePtr q, *p;
539
540    p = &workQueue;
541    while ((q = *p)) {
542        if (q->client && q->client->clientGone) {
543            (void) (*q->function) (q->client, q->closure);
544            /* remove q from the list */
545            *p = q->next;       /* don't fetch until after func called */
546            free(q);
547        }
548        else {
549            p = &q->next;       /* don't fetch until after func called */
550        }
551    }
552    workQueueLast = p;
553}
554
555Bool
556QueueWorkProc(Bool (*function) (ClientPtr pClient, void *closure),
557              ClientPtr client, void *closure)
558{
559    WorkQueuePtr q;
560
561    q = malloc(sizeof *q);
562    if (!q)
563        return FALSE;
564    q->function = function;
565    q->client = client;
566    q->closure = closure;
567    q->next = NULL;
568    *workQueueLast = q;
569    workQueueLast = &q->next;
570    return TRUE;
571}
572
573/*
574 * Manage a queue of sleeping clients, awakening them
575 * when requested, by using the OS functions IgnoreClient
576 * and AttendClient.  Note that this *ignores* the troubles
577 * with request data interleaving itself with events, but
578 * we'll leave that until a later time.
579 */
580
581typedef struct _SleepQueue {
582    struct _SleepQueue *next;
583    ClientPtr client;
584    ClientSleepProcPtr function;
585    void *closure;
586} SleepQueueRec, *SleepQueuePtr;
587
588static SleepQueuePtr sleepQueue = NULL;
589
590Bool
591ClientSleep(ClientPtr client, ClientSleepProcPtr function, void *closure)
592{
593    SleepQueuePtr q;
594
595    q = malloc(sizeof *q);
596    if (!q)
597        return FALSE;
598
599    IgnoreClient(client);
600    q->next = sleepQueue;
601    q->client = client;
602    q->function = function;
603    q->closure = closure;
604    sleepQueue = q;
605    return TRUE;
606}
607
608Bool
609ClientSignal(ClientPtr client)
610{
611    SleepQueuePtr q;
612
613    for (q = sleepQueue; q; q = q->next)
614        if (q->client == client) {
615            return QueueWorkProc(q->function, q->client, q->closure);
616        }
617    return FALSE;
618}
619
620int
621ClientSignalAll(ClientPtr client, ClientSleepProcPtr function, void *closure)
622{
623    SleepQueuePtr q;
624    int count = 0;
625
626    for (q = sleepQueue; q; q = q->next) {
627        if (!(client == CLIENT_SIGNAL_ANY || q->client == client))
628            continue;
629
630        if (!(function == CLIENT_SIGNAL_ANY || q->function == function))
631            continue;
632
633        if (!(closure == CLIENT_SIGNAL_ANY || q->closure == closure))
634            continue;
635
636        count += QueueWorkProc(q->function, q->client, q->closure);
637    }
638
639    return count;
640}
641
642void
643ClientWakeup(ClientPtr client)
644{
645    SleepQueuePtr q, *prev;
646
647    prev = &sleepQueue;
648    while ((q = *prev)) {
649        if (q->client == client) {
650            *prev = q->next;
651            free(q);
652            if (client->clientGone)
653                /* Oops -- new zombie cleanup code ensures this only
654                 * happens from inside CloseDownClient; don't want to
655                 * recurse here...
656                 */
657                /* CloseDownClient(client) */ ;
658            else
659                AttendClient(client);
660            break;
661        }
662        prev = &q->next;
663    }
664}
665
666Bool
667ClientIsAsleep(ClientPtr client)
668{
669    SleepQueuePtr q;
670
671    for (q = sleepQueue; q; q = q->next)
672        if (q->client == client)
673            return TRUE;
674    return FALSE;
675}
676
677/*
678 *  Generic Callback Manager
679 */
680
681/* ===== Private Procedures ===== */
682
683static int numCallbackListsToCleanup = 0;
684static CallbackListPtr **listsToCleanup = NULL;
685
686static Bool
687_AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data)
688{
689    CallbackPtr cbr;
690
691    cbr = malloc(sizeof(CallbackRec));
692    if (!cbr)
693        return FALSE;
694    cbr->proc = callback;
695    cbr->data = data;
696    cbr->next = (*pcbl)->list;
697    cbr->deleted = FALSE;
698    (*pcbl)->list = cbr;
699    return TRUE;
700}
701
702static Bool
703_DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data)
704{
705    CallbackListPtr cbl = *pcbl;
706    CallbackPtr cbr, pcbr;
707
708    for (pcbr = NULL, cbr = cbl->list; cbr != NULL; pcbr = cbr, cbr = cbr->next) {
709        if ((cbr->proc == callback) && (cbr->data == data))
710            break;
711    }
712    if (cbr != NULL) {
713        if (cbl->inCallback) {
714            ++(cbl->numDeleted);
715            cbr->deleted = TRUE;
716        }
717        else {
718            if (pcbr == NULL)
719                cbl->list = cbr->next;
720            else
721                pcbr->next = cbr->next;
722            free(cbr);
723        }
724        return TRUE;
725    }
726    return FALSE;
727}
728
729void
730_CallCallbacks(CallbackListPtr *pcbl, void *call_data)
731{
732    CallbackListPtr cbl = *pcbl;
733    CallbackPtr cbr, pcbr;
734
735    ++(cbl->inCallback);
736    for (cbr = cbl->list; cbr != NULL; cbr = cbr->next) {
737        (*(cbr->proc)) (pcbl, cbr->data, call_data);
738    }
739    --(cbl->inCallback);
740
741    if (cbl->inCallback)
742        return;
743
744    /* Was the entire list marked for deletion? */
745
746    if (cbl->deleted) {
747        DeleteCallbackList(pcbl);
748        return;
749    }
750
751    /* Were some individual callbacks on the list marked for deletion?
752     * If so, do the deletions.
753     */
754
755    if (cbl->numDeleted) {
756        for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted;) {
757            if (cbr->deleted) {
758                if (pcbr) {
759                    cbr = cbr->next;
760                    free(pcbr->next);
761                    pcbr->next = cbr;
762                }
763                else {
764                    cbr = cbr->next;
765                    free(cbl->list);
766                    cbl->list = cbr;
767                }
768                cbl->numDeleted--;
769            }
770            else {              /* this one wasn't deleted */
771
772                pcbr = cbr;
773                cbr = cbr->next;
774            }
775        }
776    }
777}
778
779static void
780_DeleteCallbackList(CallbackListPtr *pcbl)
781{
782    CallbackListPtr cbl = *pcbl;
783    CallbackPtr cbr, nextcbr;
784    int i;
785
786    if (cbl->inCallback) {
787        cbl->deleted = TRUE;
788        return;
789    }
790
791    for (i = 0; i < numCallbackListsToCleanup; i++) {
792        if (listsToCleanup[i] == pcbl) {
793            listsToCleanup[i] = NULL;
794            break;
795        }
796    }
797
798    for (cbr = cbl->list; cbr != NULL; cbr = nextcbr) {
799        nextcbr = cbr->next;
800        free(cbr);
801    }
802    free(cbl);
803    *pcbl = NULL;
804}
805
806static Bool
807CreateCallbackList(CallbackListPtr *pcbl)
808{
809    CallbackListPtr cbl;
810    int i;
811
812    if (!pcbl)
813        return FALSE;
814    cbl = malloc(sizeof(CallbackListRec));
815    if (!cbl)
816        return FALSE;
817    cbl->inCallback = 0;
818    cbl->deleted = FALSE;
819    cbl->numDeleted = 0;
820    cbl->list = NULL;
821    *pcbl = cbl;
822
823    for (i = 0; i < numCallbackListsToCleanup; i++) {
824        if (!listsToCleanup[i]) {
825            listsToCleanup[i] = pcbl;
826            return TRUE;
827        }
828    }
829
830    listsToCleanup = (CallbackListPtr **) xnfrealloc(listsToCleanup,
831                                                     sizeof(CallbackListPtr *) *
832                                                     (numCallbackListsToCleanup
833                                                      + 1));
834    listsToCleanup[numCallbackListsToCleanup] = pcbl;
835    numCallbackListsToCleanup++;
836    return TRUE;
837}
838
839/* ===== Public Procedures ===== */
840
841Bool
842AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data)
843{
844    if (!pcbl)
845        return FALSE;
846    if (!*pcbl) {               /* list hasn't been created yet; go create it */
847        if (!CreateCallbackList(pcbl))
848            return FALSE;
849    }
850    return _AddCallback(pcbl, callback, data);
851}
852
853Bool
854DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data)
855{
856    if (!pcbl || !*pcbl)
857        return FALSE;
858    return _DeleteCallback(pcbl, callback, data);
859}
860
861void
862DeleteCallbackList(CallbackListPtr *pcbl)
863{
864    if (!pcbl || !*pcbl)
865        return;
866    _DeleteCallbackList(pcbl);
867}
868
869void
870DeleteCallbackManager(void)
871{
872    int i;
873
874    for (i = 0; i < numCallbackListsToCleanup; i++) {
875        DeleteCallbackList(listsToCleanup[i]);
876    }
877    free(listsToCleanup);
878
879    numCallbackListsToCleanup = 0;
880    listsToCleanup = NULL;
881}
882
883void
884InitCallbackManager(void)
885{
886    DeleteCallbackManager();
887}
888
889/**
890 * Coordinates the global GL context used by modules in the X Server
891 * doing rendering with OpenGL.
892 *
893 * When setting a GL context (glXMakeCurrent() or eglMakeCurrent()),
894 * there is an expensive implied glFlush() required by the GLX and EGL
895 * APIs, so modules don't want to have to do it on every request.  But
896 * the individual modules using GL also don't know about each other,
897 * so they have to coordinate who owns the current context.
898 *
899 * When you're about to do a MakeCurrent, you should set this variable
900 * to your context's address, and you can skip MakeCurrent if it's
901 * already set to yours.
902 *
903 * When you're about to do a DestroyContext, you should set this to
904 * NULL if it's set to your context.
905 *
906 * When you're about to do an unbindContext on a DRI driver, you
907 * should set this to NULL.  Despite the unbindContext interface
908 * sounding like it only unbinds the passed in context, it actually
909 * unconditionally clears the dispatch table even if the given
910 * context wasn't current.
911 */
912void *lastGLContext = NULL;
913