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
206    rc = dixLookupResourceByClass((pointer *)&pTmp, id, RC_DRAWABLE, client, access);
207
208    if (rc != Success)
209	client->errorValue = id;
210
211    if (rc == BadValue)
212	return BadDrawable;
213    if (rc != Success)
214	return rc;
215    if (!((1 << pTmp->type) & (type ? type : M_DRAWABLE)))
216	return BadMatch;
217
218    *pDraw = pTmp;
219    return Success;
220}
221
222int
223dixLookupWindow(WindowPtr *pWin, XID id, ClientPtr client, Mask access)
224{
225    int rc;
226    rc = dixLookupDrawable((DrawablePtr*)pWin, id, client, M_WINDOW, access);
227    return (rc == BadDrawable) ? BadWindow : rc;
228}
229
230int
231dixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access)
232{
233    return dixLookupResourceByType((pointer *)pGC, id, RT_GC, client, access);
234}
235
236int
237dixLookupFontable(FontPtr *pFont, XID id, ClientPtr client, Mask access)
238{
239    int rc;
240    GC *pGC;
241    client->errorValue = id;		/* EITHER font or gc */
242    rc = dixLookupResourceByType((pointer *) pFont, id, RT_FONT, client, access);
243    if (rc != BadFont)
244	return rc;
245    rc = dixLookupResourceByType((pointer *) &pGC, id, RT_GC, client, access);
246    if (rc == BadGC)
247	return BadFont;
248    if (rc == Success)
249	*pFont = pGC->font;
250    return rc;
251}
252
253int
254dixLookupClient(ClientPtr *pClient, XID rid, ClientPtr client, Mask access)
255{
256    pointer pRes;
257    int rc = BadValue, clientIndex = CLIENT_ID(rid);
258
259    if (!clientIndex || !clients[clientIndex] || (rid & SERVER_BIT))
260	goto bad;
261
262    rc = dixLookupResourceByClass(&pRes, rid, RC_ANY, client, DixGetAttrAccess);
263    if (rc != Success)
264	goto bad;
265
266    rc = XaceHook(XACE_CLIENT_ACCESS, client, clients[clientIndex], access);
267    if (rc != Success)
268	goto bad;
269
270    *pClient = clients[clientIndex];
271    return Success;
272bad:
273    if(client)
274        client->errorValue = rid;
275    *pClient = NULL;
276    return rc;
277}
278
279int
280AlterSaveSetForClient(ClientPtr client, WindowPtr pWin, unsigned mode,
281                      Bool toRoot, Bool map)
282{
283    int numnow;
284    SaveSetElt *pTmp = NULL;
285    int j;
286
287    numnow = client->numSaved;
288    j = 0;
289    if (numnow)
290    {
291	pTmp = client->saveSet;
292	while ((j < numnow) && (SaveSetWindow(pTmp[j]) != (pointer)pWin))
293	    j++;
294    }
295    if (mode == SetModeInsert)
296    {
297	if (j < numnow)         /* duplicate */
298	   return Success;
299	numnow++;
300	pTmp = (SaveSetElt *)realloc(client->saveSet, sizeof(*pTmp) * numnow);
301	if (!pTmp)
302	    return BadAlloc;
303	client->saveSet = pTmp;
304       	client->numSaved = numnow;
305	SaveSetAssignWindow(client->saveSet[numnow - 1], pWin);
306	SaveSetAssignToRoot(client->saveSet[numnow - 1], toRoot);
307	SaveSetAssignMap(client->saveSet[numnow - 1], map);
308	return Success;
309    }
310    else if ((mode == SetModeDelete) && (j < numnow))
311    {
312	while (j < numnow-1)
313	{
314           pTmp[j] = pTmp[j+1];
315	   j++;
316	}
317	numnow--;
318        if (numnow)
319	{
320	    pTmp = (SaveSetElt *)realloc(client->saveSet, sizeof(*pTmp) * numnow);
321	    if (pTmp)
322		client->saveSet = pTmp;
323	}
324        else
325        {
326            free(client->saveSet);
327	    client->saveSet = (SaveSetElt *)NULL;
328	}
329	client->numSaved = numnow;
330	return Success;
331    }
332    return Success;
333}
334
335void
336DeleteWindowFromAnySaveSet(WindowPtr pWin)
337{
338    int i;
339    ClientPtr client;
340
341    for (i = 0; i< currentMaxClients; i++)
342    {
343	client = clients[i];
344	if (client && client->numSaved)
345	    (void)AlterSaveSetForClient(client, pWin, SetModeDelete, FALSE, TRUE);
346    }
347}
348
349/* No-op Don't Do Anything : sometimes we need to be able to call a procedure
350 * that doesn't do anything.  For example, on screen with only static
351 * colormaps, if someone calls install colormap, it's easier to have a dummy
352 * procedure to call than to check if there's a procedure
353 */
354void
355NoopDDA(void)
356{
357}
358
359typedef struct _BlockHandler {
360    BlockHandlerProcPtr BlockHandler;
361    WakeupHandlerProcPtr WakeupHandler;
362    pointer blockData;
363    Bool    deleted;
364} BlockHandlerRec, *BlockHandlerPtr;
365
366static BlockHandlerPtr	handlers;
367static int		numHandlers;
368static int		sizeHandlers;
369static Bool		inHandler;
370static Bool		handlerDeleted;
371
372/**
373 *
374 *  \param pTimeout   DIX doesn't want to know how OS represents time
375 *  \param pReadMask  nor how it represents the det of descriptors
376 */
377void
378BlockHandler(pointer pTimeout, pointer pReadmask)
379{
380    int i, j;
381
382    ++inHandler;
383    for (i = 0; i < screenInfo.numScreens; i++)
384	(* screenInfo.screens[i]->BlockHandler)(i,
385				screenInfo.screens[i]->blockData,
386				pTimeout, pReadmask);
387    for (i = 0; i < numHandlers; i++)
388	(*handlers[i].BlockHandler) (handlers[i].blockData,
389				     pTimeout, pReadmask);
390    if (handlerDeleted)
391    {
392	for (i = 0; i < numHandlers;)
393	    if (handlers[i].deleted)
394	    {
395	    	for (j = i; j < numHandlers - 1; j++)
396		    handlers[j] = handlers[j+1];
397	    	numHandlers--;
398	    }
399	    else
400		i++;
401	handlerDeleted = FALSE;
402    }
403    --inHandler;
404}
405
406/**
407 *
408 *  \param result    32 bits of undefined result from the wait
409 *  \param pReadmask the resulting descriptor mask
410 */
411void
412WakeupHandler(int result, pointer pReadmask)
413{
414    int i, j;
415
416    ++inHandler;
417    for (i = numHandlers - 1; i >= 0; i--)
418	(*handlers[i].WakeupHandler) (handlers[i].blockData,
419				      result, pReadmask);
420    for (i = 0; i < screenInfo.numScreens; i++)
421	(* screenInfo.screens[i]->WakeupHandler)(i,
422				screenInfo.screens[i]->wakeupData,
423				result, pReadmask);
424    if (handlerDeleted)
425    {
426	for (i = 0; i < numHandlers;)
427	    if (handlers[i].deleted)
428	    {
429	    	for (j = i; j < numHandlers - 1; j++)
430		    handlers[j] = handlers[j+1];
431	    	numHandlers--;
432	    }
433	    else
434		i++;
435	handlerDeleted = FALSE;
436    }
437    --inHandler;
438}
439
440/**
441 * Reentrant with BlockHandler and WakeupHandler, except wakeup won't
442 * get called until next time
443 */
444Bool
445RegisterBlockAndWakeupHandlers (BlockHandlerProcPtr blockHandler,
446                                WakeupHandlerProcPtr wakeupHandler,
447                                pointer blockData)
448{
449    BlockHandlerPtr new;
450
451    if (numHandlers >= sizeHandlers)
452    {
453        new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) *
454				      	  sizeof (BlockHandlerRec));
455    	if (!new)
456	    return FALSE;
457    	handlers = new;
458	sizeHandlers = numHandlers + 1;
459    }
460    handlers[numHandlers].BlockHandler = blockHandler;
461    handlers[numHandlers].WakeupHandler = wakeupHandler;
462    handlers[numHandlers].blockData = blockData;
463    handlers[numHandlers].deleted = FALSE;
464    numHandlers = numHandlers + 1;
465    return TRUE;
466}
467
468void
469RemoveBlockAndWakeupHandlers (BlockHandlerProcPtr blockHandler,
470                              WakeupHandlerProcPtr wakeupHandler,
471                              pointer blockData)
472{
473    int	    i;
474
475    for (i = 0; i < numHandlers; i++)
476	if (handlers[i].BlockHandler == blockHandler &&
477	    handlers[i].WakeupHandler == wakeupHandler &&
478	    handlers[i].blockData == blockData)
479	{
480	    if (inHandler)
481	    {
482		handlerDeleted = TRUE;
483		handlers[i].deleted = TRUE;
484	    }
485	    else
486	    {
487	    	for (; i < numHandlers - 1; i++)
488		    handlers[i] = handlers[i+1];
489	    	numHandlers--;
490	    }
491	    break;
492	}
493}
494
495void
496InitBlockAndWakeupHandlers (void)
497{
498    free(handlers);
499    handlers = (BlockHandlerPtr) 0;
500    numHandlers = 0;
501    sizeHandlers = 0;
502}
503
504/*
505 * A general work queue.  Perform some task before the server
506 * sleeps for input.
507 */
508
509WorkQueuePtr		workQueue;
510static WorkQueuePtr	*workQueueLast = &workQueue;
511
512void
513ProcessWorkQueue(void)
514{
515    WorkQueuePtr    q, *p;
516
517    p = &workQueue;
518    /*
519     * Scan the work queue once, calling each function.  Those
520     * which return TRUE are removed from the queue, otherwise
521     * they will be called again.  This must be reentrant with
522     * QueueWorkProc.
523     */
524    while ((q = *p))
525    {
526	if ((*q->function) (q->client, q->closure))
527	{
528	    /* remove q from the list */
529	    *p = q->next;    /* don't fetch until after func called */
530	    free(q);
531	}
532	else
533	{
534	    p = &q->next;    /* don't fetch until after func called */
535	}
536    }
537    workQueueLast = p;
538}
539
540void
541ProcessWorkQueueZombies(void)
542{
543    WorkQueuePtr    q, *p;
544
545    p = &workQueue;
546    while ((q = *p))
547    {
548	if (q->client && q->client->clientGone)
549	{
550	    (void) (*q->function) (q->client, q->closure);
551	    /* remove q from the list */
552	    *p = q->next;    /* don't fetch until after func called */
553	    free(q);
554	}
555	else
556	{
557	    p = &q->next;    /* don't fetch until after func called */
558	}
559    }
560    workQueueLast = p;
561}
562
563Bool
564QueueWorkProc (
565    Bool (*function)(ClientPtr /* pClient */, pointer /* closure */),
566    ClientPtr client, pointer closure)
567{
568    WorkQueuePtr    q;
569
570    q = malloc(sizeof *q);
571    if (!q)
572	return FALSE;
573    q->function = function;
574    q->client = client;
575    q->closure = closure;
576    q->next = NULL;
577    *workQueueLast = q;
578    workQueueLast = &q->next;
579    return TRUE;
580}
581
582/*
583 * Manage a queue of sleeping clients, awakening them
584 * when requested, by using the OS functions IgnoreClient
585 * and AttendClient.  Note that this *ignores* the troubles
586 * with request data interleaving itself with events, but
587 * we'll leave that until a later time.
588 */
589
590typedef struct _SleepQueue {
591    struct _SleepQueue	*next;
592    ClientPtr		client;
593    ClientSleepProcPtr  function;
594    pointer		closure;
595} SleepQueueRec, *SleepQueuePtr;
596
597static SleepQueuePtr	sleepQueue = NULL;
598
599Bool
600ClientSleep (ClientPtr client, ClientSleepProcPtr function, pointer closure)
601{
602    SleepQueuePtr   q;
603
604    q = malloc(sizeof *q);
605    if (!q)
606	return FALSE;
607
608    IgnoreClient (client);
609    q->next = sleepQueue;
610    q->client = client;
611    q->function = function;
612    q->closure = closure;
613    sleepQueue = q;
614    return TRUE;
615}
616
617Bool
618ClientSignal (ClientPtr client)
619{
620    SleepQueuePtr   q;
621
622    for (q = sleepQueue; q; q = q->next)
623	if (q->client == client)
624	{
625	    return QueueWorkProc (q->function, q->client, q->closure);
626	}
627    return FALSE;
628}
629
630void
631ClientWakeup (ClientPtr client)
632{
633    SleepQueuePtr   q, *prev;
634
635    prev = &sleepQueue;
636    while ( (q = *prev) )
637    {
638	if (q->client == client)
639	{
640	    *prev = q->next;
641	    free(q);
642	    if (client->clientGone)
643		/* Oops -- new zombie cleanup code ensures this only
644		 * happens from inside CloseDownClient; don't want to
645		 * recurse here...
646		 */
647		/* CloseDownClient(client) */;
648	    else
649		AttendClient (client);
650	    break;
651	}
652	prev = &q->next;
653    }
654}
655
656Bool
657ClientIsAsleep (ClientPtr client)
658{
659    SleepQueuePtr   q;
660
661    for (q = sleepQueue; q; q = q->next)
662	if (q->client == client)
663	    return TRUE;
664    return FALSE;
665}
666
667/*
668 *  Generic Callback Manager
669 */
670
671/* ===== Private Procedures ===== */
672
673static int numCallbackListsToCleanup = 0;
674static CallbackListPtr **listsToCleanup = NULL;
675
676static Bool
677_AddCallback(
678    CallbackListPtr *pcbl,
679    CallbackProcPtr callback,
680    pointer         data)
681{
682    CallbackPtr     cbr;
683
684    cbr = malloc(sizeof(CallbackRec));
685    if (!cbr)
686	return FALSE;
687    cbr->proc = callback;
688    cbr->data = data;
689    cbr->next = (*pcbl)->list;
690    cbr->deleted = FALSE;
691    (*pcbl)->list = cbr;
692    return TRUE;
693}
694
695static Bool
696_DeleteCallback(
697    CallbackListPtr *pcbl,
698    CallbackProcPtr callback,
699    pointer         data)
700{
701    CallbackListPtr cbl = *pcbl;
702    CallbackPtr     cbr, pcbr;
703
704    for (pcbr = NULL, cbr = cbl->list;
705	 cbr != NULL;
706	 pcbr = cbr, cbr = cbr->next)
707    {
708	if ((cbr->proc == callback) && (cbr->data == data))
709	    break;
710    }
711    if (cbr != NULL)
712    {
713	if (cbl->inCallback)
714	{
715	    ++(cbl->numDeleted);
716	    cbr->deleted = TRUE;
717	}
718	else
719	{
720	    if (pcbr == NULL)
721		cbl->list = cbr->next;
722	    else
723		pcbr->next = cbr->next;
724	    free(cbr);
725	}
726	return TRUE;
727    }
728    return FALSE;
729}
730
731void
732_CallCallbacks(
733    CallbackListPtr    *pcbl,
734    pointer	    call_data)
735{
736    CallbackListPtr cbl = *pcbl;
737    CallbackPtr     cbr, pcbr;
738
739    ++(cbl->inCallback);
740    for (cbr = cbl->list; cbr != NULL; cbr = cbr->next)
741    {
742	(*(cbr->proc)) (pcbl, cbr->data, call_data);
743    }
744    --(cbl->inCallback);
745
746    if (cbl->inCallback) return;
747
748    /* Was the entire list marked for deletion? */
749
750    if (cbl->deleted)
751    {
752	DeleteCallbackList(pcbl);
753	return;
754    }
755
756    /* Were some individual callbacks on the list marked for deletion?
757     * If so, do the deletions.
758     */
759
760    if (cbl->numDeleted)
761    {
762	for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted; )
763	{
764	    if (cbr->deleted)
765	    {
766		if (pcbr)
767		{
768		    cbr = cbr->next;
769		    free(pcbr->next);
770		    pcbr->next = cbr;
771		} else
772		{
773		    cbr = cbr->next;
774		    free(cbl->list);
775		    cbl->list = cbr;
776		}
777		cbl->numDeleted--;
778	    }
779	    else /* this one wasn't deleted */
780	    {
781		pcbr = cbr;
782		cbr = cbr->next;
783	    }
784	}
785    }
786}
787
788static void
789_DeleteCallbackList(
790    CallbackListPtr    *pcbl)
791{
792    CallbackListPtr cbl = *pcbl;
793    CallbackPtr     cbr, nextcbr;
794    int i;
795
796    if (cbl->inCallback)
797    {
798	cbl->deleted = TRUE;
799	return;
800    }
801
802    for (i = 0; i < numCallbackListsToCleanup; i++)
803    {
804	if (listsToCleanup[i] == pcbl)
805	{
806	    listsToCleanup[i] = NULL;
807	    break;
808	}
809    }
810
811    for (cbr = cbl->list; cbr != NULL; cbr = nextcbr)
812    {
813	nextcbr = cbr->next;
814	free(cbr);
815    }
816    free(cbl);
817    *pcbl = NULL;
818}
819
820static Bool
821CreateCallbackList(CallbackListPtr *pcbl)
822{
823    CallbackListPtr  cbl;
824    int i;
825
826    if (!pcbl) return FALSE;
827    cbl = malloc(sizeof(CallbackListRec));
828    if (!cbl) return FALSE;
829    cbl->inCallback = 0;
830    cbl->deleted = FALSE;
831    cbl->numDeleted = 0;
832    cbl->list = NULL;
833    *pcbl = cbl;
834
835    for (i = 0; i < numCallbackListsToCleanup; i++)
836    {
837	if (!listsToCleanup[i])
838	{
839	    listsToCleanup[i] = pcbl;
840	    return TRUE;
841	}
842    }
843
844    listsToCleanup = (CallbackListPtr **)xnfrealloc(listsToCleanup,
845		sizeof(CallbackListPtr *) * (numCallbackListsToCleanup+1));
846    listsToCleanup[numCallbackListsToCleanup] = pcbl;
847    numCallbackListsToCleanup++;
848    return TRUE;
849}
850
851/* ===== Public Procedures ===== */
852
853Bool
854AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data)
855{
856    if (!pcbl) return FALSE;
857    if (!*pcbl)
858    {	/* list hasn't been created yet; go create it */
859	if (!CreateCallbackList(pcbl))
860	    return FALSE;
861    }
862    return _AddCallback(pcbl, callback, data);
863}
864
865Bool
866DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data)
867{
868    if (!pcbl || !*pcbl) return FALSE;
869    return _DeleteCallback(pcbl, callback, data);
870}
871
872void
873DeleteCallbackList(CallbackListPtr *pcbl)
874{
875    if (!pcbl || !*pcbl) return;
876    _DeleteCallbackList(pcbl);
877}
878
879void
880InitCallbackManager(void)
881{
882    int i;
883
884    for (i = 0; i < numCallbackListsToCleanup; i++)
885    {
886	DeleteCallbackList(listsToCleanup[i]);
887    }
888    free(listsToCleanup);
889
890    numCallbackListsToCleanup = 0;
891    listsToCleanup = NULL;
892}
893