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