First import
[xorg_rtime.git] / xorg-server-1.4 / dix / dixutils.c
blobc1e30ff1811756d8be0324f6f79ac608a854d15c
1 /***********************************************************
3 Copyright 1987, 1998 The Open Group
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
26 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
28 All Rights Reserved
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44 SOFTWARE.
46 ******************************************************************/
50 (c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved.
52 Permission to use, copy, modify, distribute, and sublicense this software and its
53 documentation for any purpose and without fee is hereby granted, provided that
54 the above copyright notices appear in all copies and that both those copyright
55 notices and this permission notice appear in supporting documentation and that
56 the name of Adobe Systems Incorporated not be used in advertising or publicity
57 pertaining to distribution of the software without specific, written prior
58 permission. No trademark license to use the Adobe trademarks is hereby
59 granted. If the Adobe trademark "Display PostScript"(tm) is used to describe
60 this software, its functionality or for any other purpose, such use shall be
61 limited to a statement that this software works in conjunction with the Display
62 PostScript system. Proper trademark attribution to reflect Adobe's ownership
63 of the trademark shall be given whenever any such reference to the Display
64 PostScript system is made.
66 ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY
67 PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE
68 DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
69 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
70 INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU
71 OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
72 DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT
73 LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR
74 PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER
75 SUPPORT FOR THE SOFTWARE.
77 Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
78 Incorporated which may be registered in certain jurisdictions.
80 Author: Adobe Systems Incorporated
85 #ifdef HAVE_DIX_CONFIG_H
86 #include <dix-config.h>
87 #endif
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 "scrnintstr.h"
96 #define XK_LATIN1
97 #include <X11/keysymdef.h>
98 #include "xace.h"
101 * CompareTimeStamps returns -1, 0, or +1 depending on if the first
102 * argument is less than, equal to or greater than the second argument.
105 _X_EXPORT int
106 CompareTimeStamps(TimeStamp a, TimeStamp b)
108 if (a.months < b.months)
109 return EARLIER;
110 if (a.months > b.months)
111 return LATER;
112 if (a.milliseconds < b.milliseconds)
113 return EARLIER;
114 if (a.milliseconds > b.milliseconds)
115 return LATER;
116 return SAMETIME;
120 * convert client times to server TimeStamps
123 #define HALFMONTH ((unsigned long) 1<<31)
124 _X_EXPORT TimeStamp
125 ClientTimeToServerTime(CARD32 c)
127 TimeStamp ts;
128 if (c == CurrentTime)
129 return currentTime;
130 ts.months = currentTime.months;
131 ts.milliseconds = c;
132 if (c > currentTime.milliseconds)
134 if (((unsigned long) c - currentTime.milliseconds) > HALFMONTH)
135 ts.months -= 1;
137 else if (c < currentTime.milliseconds)
139 if (((unsigned long)currentTime.milliseconds - c) > HALFMONTH)
140 ts.months += 1;
142 return ts;
146 * ISO Latin-1 case conversion routine
148 * this routine always null-terminates the result, so
149 * beware of too-small buffers
152 static unsigned char
153 ISOLatin1ToLower (unsigned char source)
155 unsigned char dest;
156 if ((source >= XK_A) && (source <= XK_Z))
157 dest = source + (XK_a - XK_A);
158 else if ((source >= XK_Agrave) && (source <= XK_Odiaeresis))
159 dest = source + (XK_agrave - XK_Agrave);
160 else if ((source >= XK_Ooblique) && (source <= XK_Thorn))
161 dest = source + (XK_oslash - XK_Ooblique);
162 else
163 dest = source;
164 return dest;
168 _X_EXPORT void
169 CopyISOLatin1Lowered(unsigned char *dest, unsigned char *source, int length)
171 int i;
173 for (i = 0; i < length; i++, source++, dest++)
174 *dest = ISOLatin1ToLower (*source);
175 *dest = '\0';
179 CompareISOLatin1Lowered(unsigned char *s1, int s1len,
180 unsigned char *s2, int s2len)
182 unsigned char c1, c2;
184 for (;;)
186 /* note -- compare against zero so that -1 ignores len */
187 c1 = s1len-- ? *s1++ : '\0';
188 c2 = s2len-- ? *s2++ : '\0';
189 if (!c1 ||
190 (c1 != c2 &&
191 (c1 = ISOLatin1ToLower (c1)) != (c2 = ISOLatin1ToLower (c2))))
192 break;
194 return (int) c1 - (int) c2;
198 * dixLookupWindow and dixLookupDrawable:
199 * Look up the window/drawable taking into account the client doing the
200 * lookup, the type of drawable desired, and the type of access desired.
201 * Return Success with *pDraw set if the window/drawable exists and the client
202 * is allowed access, else return an error code with *pDraw set to NULL. The
203 * access mask values are defined in resource.h. The type mask values are
204 * defined in pixmap.h, with zero equivalent to M_DRAWABLE.
206 _X_EXPORT int
207 dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client,
208 Mask type, Mask access)
210 DrawablePtr pTmp;
211 RESTYPE rtype;
212 *pDraw = NULL;
213 client->errorValue = id;
215 if (id == INVALID)
216 return BadDrawable;
218 if (id == client->lastDrawableID) {
219 pTmp = client->lastDrawable;
221 /* an access check is required for cached drawables */
222 rtype = (type & M_WINDOW) ? RT_WINDOW : RT_PIXMAP;
223 if (!XaceHook(XACE_RESOURCE_ACCESS, client, id, rtype, access, pTmp))
224 return BadDrawable;
225 } else
226 pTmp = (DrawablePtr)SecurityLookupIDByClass(client, id, RC_DRAWABLE,
227 access);
228 if (!pTmp)
229 return BadDrawable;
230 if (!((1 << pTmp->type) & (type ? type : M_DRAWABLE)))
231 return BadMatch;
233 if (type & M_DRAWABLE) {
234 client->lastDrawable = pTmp;
235 client->lastDrawableID = id;
236 client->lastGCID = INVALID;
237 client->lastGC = (GCPtr)NULL;
239 *pDraw = pTmp;
240 return Success;
243 _X_EXPORT int
244 dixLookupWindow(WindowPtr *pWin, XID id, ClientPtr client, Mask access)
246 int rc;
247 rc = dixLookupDrawable((DrawablePtr*)pWin, id, client, M_WINDOW, access);
248 return (rc == BadDrawable) ? BadWindow : rc;
251 _X_EXPORT int
252 dixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access)
254 GCPtr pTmp = (GCPtr)SecurityLookupIDByType(client, id, RT_GC, access);
255 if (pTmp) {
256 *pGC = pTmp;
257 return Success;
259 client->errorValue = id;
260 *pGC = NULL;
261 return BadGC;
264 _X_EXPORT int
265 dixLookupClient(ClientPtr *pClient, XID rid, ClientPtr client, Mask access)
267 pointer pRes = (pointer)SecurityLookupIDByClass(client, rid, RC_ANY,
268 access);
269 int clientIndex = CLIENT_ID(rid);
270 client->errorValue = rid;
272 if (clientIndex && pRes && clients[clientIndex] && !(rid & SERVER_BIT)) {
273 *pClient = clients[clientIndex];
274 return Success;
276 *pClient = NULL;
277 return BadValue;
281 * These are deprecated compatibility functions and will be removed soon!
282 * Please use the new dixLookup*() functions above.
284 _X_EXPORT _X_DEPRECATED WindowPtr
285 SecurityLookupWindow(XID id, ClientPtr client, Mask access_mode)
287 WindowPtr pWin;
288 int i = dixLookupWindow(&pWin, id, client, access_mode);
289 static int warn = 1;
290 if (warn-- > 0)
291 ErrorF("Warning: LookupWindow()/SecurityLookupWindow() "
292 "are deprecated. Please convert your driver/module "
293 "to use dixLookupWindow().\n");
294 return (i == Success) ? pWin : NULL;
297 _X_EXPORT _X_DEPRECATED WindowPtr
298 LookupWindow(XID id, ClientPtr client)
300 return SecurityLookupWindow(id, client, DixUnknownAccess);
303 _X_EXPORT _X_DEPRECATED pointer
304 SecurityLookupDrawable(XID id, ClientPtr client, Mask access_mode)
306 DrawablePtr pDraw;
307 int i = dixLookupDrawable(&pDraw, id, client, M_DRAWABLE, access_mode);
308 static int warn = 1;
309 if (warn-- > 0)
310 ErrorF("Warning: LookupDrawable()/SecurityLookupDrawable() "
311 "are deprecated. Please convert your driver/module "
312 "to use dixLookupDrawable().\n");
313 return (i == Success) ? pDraw : NULL;
316 _X_EXPORT _X_DEPRECATED pointer
317 LookupDrawable(XID id, ClientPtr client)
319 return SecurityLookupDrawable(id, client, DixUnknownAccess);
322 _X_EXPORT _X_DEPRECATED ClientPtr
323 LookupClient(XID id, ClientPtr client)
325 ClientPtr pClient;
326 int i = dixLookupClient(&pClient, id, client, DixUnknownAccess);
327 static int warn = 1;
328 if (warn-- > 0)
329 ErrorF("Warning: LookupClient() is deprecated. Please convert your "
330 "driver/module to use dixLookupClient().\n");
331 return (i == Success) ? pClient : NULL;
334 /* end deprecated functions */
337 AlterSaveSetForClient(ClientPtr client, WindowPtr pWin, unsigned mode,
338 Bool toRoot, Bool remap)
340 int numnow;
341 SaveSetElt *pTmp = NULL;
342 int j;
344 numnow = client->numSaved;
345 j = 0;
346 if (numnow)
348 pTmp = client->saveSet;
349 while ((j < numnow) && (SaveSetWindow(pTmp[j]) != (pointer)pWin))
350 j++;
352 if (mode == SetModeInsert)
354 if (j < numnow) /* duplicate */
355 return(Success);
356 numnow++;
357 pTmp = (SaveSetElt *)xrealloc(client->saveSet, sizeof(*pTmp) * numnow);
358 if (!pTmp)
359 return(BadAlloc);
360 client->saveSet = pTmp;
361 client->numSaved = numnow;
362 SaveSetAssignWindow(client->saveSet[numnow - 1], pWin);
363 SaveSetAssignToRoot(client->saveSet[numnow - 1], toRoot);
364 SaveSetAssignRemap(client->saveSet[numnow - 1], remap);
365 return(Success);
367 else if ((mode == SetModeDelete) && (j < numnow))
369 while (j < numnow-1)
371 pTmp[j] = pTmp[j+1];
372 j++;
374 numnow--;
375 if (numnow)
377 pTmp = (SaveSetElt *)xrealloc(client->saveSet, sizeof(*pTmp) * numnow);
378 if (pTmp)
379 client->saveSet = pTmp;
381 else
383 xfree(client->saveSet);
384 client->saveSet = (SaveSetElt *)NULL;
386 client->numSaved = numnow;
387 return(Success);
389 return(Success);
392 void
393 DeleteWindowFromAnySaveSet(WindowPtr pWin)
395 int i;
396 ClientPtr client;
398 for (i = 0; i< currentMaxClients; i++)
400 client = clients[i];
401 if (client && client->numSaved)
402 (void)AlterSaveSetForClient(client, pWin, SetModeDelete, FALSE, TRUE);
406 /* No-op Don't Do Anything : sometimes we need to be able to call a procedure
407 * that doesn't do anything. For example, on screen with only static
408 * colormaps, if someone calls install colormap, it's easier to have a dummy
409 * procedure to call than to check if there's a procedure
411 _X_EXPORT void
412 NoopDDA(void)
416 typedef struct _BlockHandler {
417 BlockHandlerProcPtr BlockHandler;
418 WakeupHandlerProcPtr WakeupHandler;
419 pointer blockData;
420 Bool deleted;
421 } BlockHandlerRec, *BlockHandlerPtr;
423 static BlockHandlerPtr handlers;
424 static int numHandlers;
425 static int sizeHandlers;
426 static Bool inHandler;
427 static Bool handlerDeleted;
431 * \param pTimeout DIX doesn't want to know how OS represents time
432 * \param pReadMask nor how it represents the det of descriptors
434 void
435 BlockHandler(pointer pTimeout, pointer pReadmask)
437 int i, j;
439 ++inHandler;
440 for (i = 0; i < screenInfo.numScreens; i++)
441 (* screenInfo.screens[i]->BlockHandler)(i,
442 screenInfo.screens[i]->blockData,
443 pTimeout, pReadmask);
444 for (i = 0; i < numHandlers; i++)
445 (*handlers[i].BlockHandler) (handlers[i].blockData,
446 pTimeout, pReadmask);
447 if (handlerDeleted)
449 for (i = 0; i < numHandlers;)
450 if (handlers[i].deleted)
452 for (j = i; j < numHandlers - 1; j++)
453 handlers[j] = handlers[j+1];
454 numHandlers--;
456 else
457 i++;
458 handlerDeleted = FALSE;
460 --inHandler;
465 * \param result 32 bits of undefined result from the wait
466 * \param pReadmask the resulting descriptor mask
468 void
469 WakeupHandler(int result, pointer pReadmask)
471 int i, j;
473 ++inHandler;
474 for (i = numHandlers - 1; i >= 0; i--)
475 (*handlers[i].WakeupHandler) (handlers[i].blockData,
476 result, pReadmask);
477 for (i = 0; i < screenInfo.numScreens; i++)
478 (* screenInfo.screens[i]->WakeupHandler)(i,
479 screenInfo.screens[i]->wakeupData,
480 result, pReadmask);
481 if (handlerDeleted)
483 for (i = 0; i < numHandlers;)
484 if (handlers[i].deleted)
486 for (j = i; j < numHandlers - 1; j++)
487 handlers[j] = handlers[j+1];
488 numHandlers--;
490 else
491 i++;
492 handlerDeleted = FALSE;
494 --inHandler;
498 * Reentrant with BlockHandler and WakeupHandler, except wakeup won't
499 * get called until next time
501 _X_EXPORT Bool
502 RegisterBlockAndWakeupHandlers (BlockHandlerProcPtr blockHandler,
503 WakeupHandlerProcPtr wakeupHandler,
504 pointer blockData)
506 BlockHandlerPtr new;
508 if (numHandlers >= sizeHandlers)
510 new = (BlockHandlerPtr) xrealloc (handlers, (numHandlers + 1) *
511 sizeof (BlockHandlerRec));
512 if (!new)
513 return FALSE;
514 handlers = new;
515 sizeHandlers = numHandlers + 1;
517 handlers[numHandlers].BlockHandler = blockHandler;
518 handlers[numHandlers].WakeupHandler = wakeupHandler;
519 handlers[numHandlers].blockData = blockData;
520 handlers[numHandlers].deleted = FALSE;
521 numHandlers = numHandlers + 1;
522 return TRUE;
525 _X_EXPORT void
526 RemoveBlockAndWakeupHandlers (BlockHandlerProcPtr blockHandler,
527 WakeupHandlerProcPtr wakeupHandler,
528 pointer blockData)
530 int i;
532 for (i = 0; i < numHandlers; i++)
533 if (handlers[i].BlockHandler == blockHandler &&
534 handlers[i].WakeupHandler == wakeupHandler &&
535 handlers[i].blockData == blockData)
537 if (inHandler)
539 handlerDeleted = TRUE;
540 handlers[i].deleted = TRUE;
542 else
544 for (; i < numHandlers - 1; i++)
545 handlers[i] = handlers[i+1];
546 numHandlers--;
548 break;
552 void
553 InitBlockAndWakeupHandlers (void)
555 xfree (handlers);
556 handlers = (BlockHandlerPtr) 0;
557 numHandlers = 0;
558 sizeHandlers = 0;
562 * A general work queue. Perform some task before the server
563 * sleeps for input.
566 WorkQueuePtr workQueue;
567 static WorkQueuePtr *workQueueLast = &workQueue;
569 void
570 ProcessWorkQueue(void)
572 WorkQueuePtr q, *p;
574 p = &workQueue;
576 * Scan the work queue once, calling each function. Those
577 * which return TRUE are removed from the queue, otherwise
578 * they will be called again. This must be reentrant with
579 * QueueWorkProc.
581 while ((q = *p))
583 if ((*q->function) (q->client, q->closure))
585 /* remove q from the list */
586 *p = q->next; /* don't fetch until after func called */
587 xfree (q);
589 else
591 p = &q->next; /* don't fetch until after func called */
594 workQueueLast = p;
597 void
598 ProcessWorkQueueZombies(void)
600 WorkQueuePtr q, *p;
602 p = &workQueue;
603 while ((q = *p))
605 if (q->client && q->client->clientGone)
607 (void) (*q->function) (q->client, q->closure);
608 /* remove q from the list */
609 *p = q->next; /* don't fetch until after func called */
610 xfree (q);
612 else
614 p = &q->next; /* don't fetch until after func called */
617 workQueueLast = p;
620 _X_EXPORT Bool
621 QueueWorkProc (
622 Bool (*function)(ClientPtr /* pClient */, pointer /* closure */),
623 ClientPtr client, pointer closure)
625 WorkQueuePtr q;
627 q = (WorkQueuePtr) xalloc (sizeof *q);
628 if (!q)
629 return FALSE;
630 q->function = function;
631 q->client = client;
632 q->closure = closure;
633 q->next = NULL;
634 *workQueueLast = q;
635 workQueueLast = &q->next;
636 return TRUE;
640 * Manage a queue of sleeping clients, awakening them
641 * when requested, by using the OS functions IgnoreClient
642 * and AttendClient. Note that this *ignores* the troubles
643 * with request data interleaving itself with events, but
644 * we'll leave that until a later time.
647 typedef struct _SleepQueue {
648 struct _SleepQueue *next;
649 ClientPtr client;
650 ClientSleepProcPtr function;
651 pointer closure;
652 } SleepQueueRec, *SleepQueuePtr;
654 static SleepQueuePtr sleepQueue = NULL;
656 _X_EXPORT Bool
657 ClientSleep (ClientPtr client, ClientSleepProcPtr function, pointer closure)
659 SleepQueuePtr q;
661 q = (SleepQueuePtr) xalloc (sizeof *q);
662 if (!q)
663 return FALSE;
665 IgnoreClient (client);
666 q->next = sleepQueue;
667 q->client = client;
668 q->function = function;
669 q->closure = closure;
670 sleepQueue = q;
671 return TRUE;
674 Bool
675 ClientSignal (ClientPtr client)
677 SleepQueuePtr q;
679 for (q = sleepQueue; q; q = q->next)
680 if (q->client == client)
682 return QueueWorkProc (q->function, q->client, q->closure);
684 return FALSE;
687 _X_EXPORT void
688 ClientWakeup (ClientPtr client)
690 SleepQueuePtr q, *prev;
692 prev = &sleepQueue;
693 while ( (q = *prev) )
695 if (q->client == client)
697 *prev = q->next;
698 xfree (q);
699 if (client->clientGone)
700 /* Oops -- new zombie cleanup code ensures this only
701 * happens from inside CloseDownClient; don't want to
702 * recurse here...
704 /* CloseDownClient(client) */;
705 else
706 AttendClient (client);
707 break;
709 prev = &q->next;
713 Bool
714 ClientIsAsleep (ClientPtr client)
716 SleepQueuePtr q;
718 for (q = sleepQueue; q; q = q->next)
719 if (q->client == client)
720 return TRUE;
721 return FALSE;
725 * Generic Callback Manager
728 /* ===== Private Procedures ===== */
730 static int numCallbackListsToCleanup = 0;
731 static CallbackListPtr **listsToCleanup = NULL;
733 static Bool
734 _AddCallback(
735 CallbackListPtr *pcbl,
736 CallbackProcPtr callback,
737 pointer data)
739 CallbackPtr cbr;
741 cbr = (CallbackPtr) xalloc(sizeof(CallbackRec));
742 if (!cbr)
743 return FALSE;
744 cbr->proc = callback;
745 cbr->data = data;
746 cbr->next = (*pcbl)->list;
747 cbr->deleted = FALSE;
748 (*pcbl)->list = cbr;
749 return TRUE;
752 static Bool
753 _DeleteCallback(
754 CallbackListPtr *pcbl,
755 CallbackProcPtr callback,
756 pointer data)
758 CallbackListPtr cbl = *pcbl;
759 CallbackPtr cbr, pcbr;
761 for (pcbr = NULL, cbr = cbl->list;
762 cbr != NULL;
763 pcbr = cbr, cbr = cbr->next)
765 if ((cbr->proc == callback) && (cbr->data == data))
766 break;
768 if (cbr != NULL)
770 if (cbl->inCallback)
772 ++(cbl->numDeleted);
773 cbr->deleted = TRUE;
775 else
777 if (pcbr == NULL)
778 cbl->list = cbr->next;
779 else
780 pcbr->next = cbr->next;
781 xfree(cbr);
783 return TRUE;
785 return FALSE;
788 static void
789 _CallCallbacks(
790 CallbackListPtr *pcbl,
791 pointer call_data)
793 CallbackListPtr cbl = *pcbl;
794 CallbackPtr cbr, pcbr;
796 ++(cbl->inCallback);
797 for (cbr = cbl->list; cbr != NULL; cbr = cbr->next)
799 (*(cbr->proc)) (pcbl, cbr->data, call_data);
801 --(cbl->inCallback);
803 if (cbl->inCallback) return;
805 /* Was the entire list marked for deletion? */
807 if (cbl->deleted)
809 DeleteCallbackList(pcbl);
810 return;
813 /* Were some individual callbacks on the list marked for deletion?
814 * If so, do the deletions.
817 if (cbl->numDeleted)
819 for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted; )
821 if (cbr->deleted)
823 if (pcbr)
825 cbr = cbr->next;
826 xfree(pcbr->next);
827 pcbr->next = cbr;
828 } else
830 cbr = cbr->next;
831 xfree(cbl->list);
832 cbl->list = cbr;
834 cbl->numDeleted--;
836 else /* this one wasn't deleted */
838 pcbr = cbr;
839 cbr = cbr->next;
845 static void
846 _DeleteCallbackList(
847 CallbackListPtr *pcbl)
849 CallbackListPtr cbl = *pcbl;
850 CallbackPtr cbr, nextcbr;
851 int i;
853 if (cbl->inCallback)
855 cbl->deleted = TRUE;
856 return;
859 for (i = 0; i < numCallbackListsToCleanup; i++)
861 if ((listsToCleanup[i] = pcbl) != 0)
863 listsToCleanup[i] = NULL;
864 break;
868 for (cbr = cbl->list; cbr != NULL; cbr = nextcbr)
870 nextcbr = cbr->next;
871 xfree(cbr);
873 xfree(cbl);
874 *pcbl = NULL;
877 static CallbackFuncsRec default_cbfuncs =
879 _AddCallback,
880 _DeleteCallback,
881 _CallCallbacks,
882 _DeleteCallbackList
885 static Bool
886 CreateCallbackList(CallbackListPtr *pcbl, CallbackFuncsPtr cbfuncs)
888 CallbackListPtr cbl;
889 int i;
891 if (!pcbl) return FALSE;
892 cbl = (CallbackListPtr) xalloc(sizeof(CallbackListRec));
893 if (!cbl) return FALSE;
894 cbl->funcs = cbfuncs ? *cbfuncs : default_cbfuncs;
895 cbl->inCallback = 0;
896 cbl->deleted = FALSE;
897 cbl->numDeleted = 0;
898 cbl->list = NULL;
899 *pcbl = cbl;
901 for (i = 0; i < numCallbackListsToCleanup; i++)
903 if (!listsToCleanup[i])
905 listsToCleanup[i] = pcbl;
906 return TRUE;
910 listsToCleanup = (CallbackListPtr **)xnfrealloc(listsToCleanup,
911 sizeof(CallbackListPtr *) * (numCallbackListsToCleanup+1));
912 listsToCleanup[numCallbackListsToCleanup] = pcbl;
913 numCallbackListsToCleanup++;
914 return TRUE;
917 /* ===== Public Procedures ===== */
919 _X_EXPORT Bool
920 AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data)
922 if (!pcbl) return FALSE;
923 if (!*pcbl)
924 { /* list hasn't been created yet; go create it */
925 if (!CreateCallbackList(pcbl, (CallbackFuncsPtr)NULL))
926 return FALSE;
928 return ((*(*pcbl)->funcs.AddCallback) (pcbl, callback, data));
931 _X_EXPORT Bool
932 DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data)
934 if (!pcbl || !*pcbl) return FALSE;
935 return ((*(*pcbl)->funcs.DeleteCallback) (pcbl, callback, data));
938 void
939 CallCallbacks(CallbackListPtr *pcbl, pointer call_data)
941 if (!pcbl || !*pcbl) return;
942 (*(*pcbl)->funcs.CallCallbacks) (pcbl, call_data);
945 void
946 DeleteCallbackList(CallbackListPtr *pcbl)
948 if (!pcbl || !*pcbl) return;
949 (*(*pcbl)->funcs.DeleteCallbackList) (pcbl);
952 void
953 InitCallbackManager(void)
955 int i;
957 for (i = 0; i < numCallbackListsToCleanup; i++)
959 DeleteCallbackList(listsToCleanup[i]);
961 if (listsToCleanup) xfree(listsToCleanup);
963 numCallbackListsToCleanup = 0;
964 listsToCleanup = NULL;