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