Imported Upstream version 1.15.1
[deb_xorg-server.git] / dix / dixutils.c
CommitLineData
a09e091a
JB
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
25Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27 All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45******************************************************************/
46
47/*
48
49(c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved.
50
51Permission to use, copy, modify, distribute, and sublicense this software and its
52documentation for any purpose and without fee is hereby granted, provided that
53the above copyright notices appear in all copies and that both those copyright
54notices and this permission notice appear in supporting documentation and that
55the name of Adobe Systems Incorporated not be used in advertising or publicity
56pertaining to distribution of the software without specific, written prior
57permission. No trademark license to use the Adobe trademarks is hereby
58granted. If the Adobe trademark "Display PostScript"(tm) is used to describe
59this software, its functionality or for any other purpose, such use shall be
60limited to a statement that this software works in conjunction with the Display
61PostScript system. Proper trademark attribution to reflect Adobe's ownership
62of the trademark shall be given whenever any such reference to the Display
63PostScript system is made.
64
65ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY
66PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE
67DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
68WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
69INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU
70OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
71DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT
72LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR
73PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER
74SUPPORT FOR THE SOFTWARE.
75
76Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
77Incorporated which may be registered in certain jurisdictions.
78
79Author: 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
104int
105CompareTimeStamps(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)
123TimeStamp
124ClientTimeToServerTime(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
150static unsigned char
151ISOLatin1ToLower(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
166int
167CompareISOLatin1Lowered(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 */
193int
194dixLookupDrawable(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
219int
220dixLookupWindow(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
236int
237dixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access)
238{
239 return dixLookupResourceByType((pointer *) pGC, id, RT_GC, client, access);
240}
241
242int
243dixLookupFontable(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
261int
262dixLookupClient(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
287int
288AlterSaveSetForClient(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
338void
339DeleteWindowFromAnySaveSet(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 */
357void
358NoopDDA(void)
359{
360}
361
362typedef struct _BlockHandler {
363 BlockHandlerProcPtr BlockHandler;
364 WakeupHandlerProcPtr WakeupHandler;
365 pointer blockData;
366 Bool deleted;
367} BlockHandlerRec, *BlockHandlerPtr;
368
369static BlockHandlerPtr handlers;
370static int numHandlers;
371static int sizeHandlers;
372static Bool inHandler;
373static 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 */
380void
381BlockHandler(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 */
415void
416WakeupHandler(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 */
449Bool
450RegisterBlockAndWakeupHandlers(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
472void
473RemoveBlockAndWakeupHandlers(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
496void
497InitBlockAndWakeupHandlers(void)
498{
499 free(handlers);
500 handlers = (BlockHandlerPtr) 0;
501 numHandlers = 0;
502 sizeHandlers = 0;
503}
504
505/*
506 * A general work queue. Perform some task before the server
507 * sleeps for input.
508 */
509
510WorkQueuePtr workQueue;
511static WorkQueuePtr *workQueueLast = &workQueue;
512
513void
514ProcessWorkQueue(void)
515{
516 WorkQueuePtr q, *p;
517
518 p = &workQueue;
519 /*
520 * Scan the work queue once, calling each function. Those
521 * which return TRUE are removed from the queue, otherwise
522 * they will be called again. This must be reentrant with
523 * QueueWorkProc.
524 */
525 while ((q = *p)) {
526 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
538void
539ProcessWorkQueueZombies(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
558Bool
559QueueWorkProc(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
585typedef struct _SleepQueue {
586 struct _SleepQueue *next;
587 ClientPtr client;
588 ClientSleepProcPtr function;
589 pointer closure;
590} SleepQueueRec, *SleepQueuePtr;
591
592static SleepQueuePtr sleepQueue = NULL;
593
594Bool
595ClientSleep(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
612Bool
613ClientSignal(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
624void
625ClientWakeup(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
648Bool
649ClientIsAsleep(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
665static int numCallbackListsToCleanup = 0;
666static CallbackListPtr **listsToCleanup = NULL;
667
668static 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
684static 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
711void
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
761static 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
788static Bool
789CreateCallbackList(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
823Bool
824AddCallback(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
835Bool
836DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data)
837{
838 if (!pcbl || !*pcbl)
839 return FALSE;
840 return _DeleteCallback(pcbl, callback, data);
841}
842
843void
844DeleteCallbackList(CallbackListPtr *pcbl)
845{
846 if (!pcbl || !*pcbl)
847 return;
848 _DeleteCallbackList(pcbl);
849}
850
851void
852DeleteCallbackManager(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
865void
866InitCallbackManager(void)
867{
868 DeleteCallbackManager();
869}