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