X-Git-Url: https://git.sesse.net/?p=rdpsrv;a=blobdiff_plain;f=Xserver%2Fprograms%2FXserver%2Fdix%2Fdixutils.c;fp=Xserver%2Fprograms%2FXserver%2Fdix%2Fdixutils.c;h=edabfd73572b17745d87e0591229e14270ae6767;hp=0000000000000000000000000000000000000000;hb=b6e6afccf37f4ad0515ef2a698f714fdf1bf23b3;hpb=e3340a110a3b01756b8e67531395a33b40a17d37 diff --git a/Xserver/programs/Xserver/dix/dixutils.c b/Xserver/programs/Xserver/dix/dixutils.c new file mode 100644 index 0000000..edabfd7 --- /dev/null +++ b/Xserver/programs/Xserver/dix/dixutils.c @@ -0,0 +1,1011 @@ +/*********************************************************** + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* + +(c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved. + +Permission to use, copy, modify, distribute, and sublicense this software and its +documentation for any purpose and without fee is hereby granted, provided that +the above copyright notices appear in all copies and that both those copyright +notices and this permission notice appear in supporting documentation and that +the name of Adobe Systems Incorporated not be used in advertising or publicity +pertaining to distribution of the software without specific, written prior +permission. No trademark license to use the Adobe trademarks is hereby +granted. If the Adobe trademark "Display PostScript"(tm) is used to describe +this software, its functionality or for any other purpose, such use shall be +limited to a statement that this software works in conjunction with the Display +PostScript system. Proper trademark attribution to reflect Adobe's ownership +of the trademark shall be given whenever any such reference to the Display +PostScript system is made. + +ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY +PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE +DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- +INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU +OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT +LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER +SUPPORT FOR THE SOFTWARE. + +Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems +Incorporated which may be registered in certain jurisdictions. + +Author: Adobe Systems Incorporated + +*/ + +/* $TOG: dixutils.c /main/33 1997/05/22 10:02:20 kaleb $ */ + + + + +/* $XFree86: xc/programs/Xserver/dix/dixutils.c,v 3.1.2.1 1997/05/23 12:19:35 dawes Exp $ */ + +#include "X.h" +#include "Xmd.h" +#include "misc.h" +#include "windowstr.h" +#include "dixstruct.h" +#include "pixmapstr.h" +#include "scrnintstr.h" +#define XK_LATIN1 +#include "keysymdef.h" +#ifdef XCSECURITY +#define _SECURITY_SERVER +#include "extensions/security.h" +#endif + +/* + * CompareTimeStamps returns -1, 0, or +1 depending on if the first + * argument is less than, equal to or greater than the second argument. + */ + +int +CompareTimeStamps(a, b) + TimeStamp a, b; +{ + if (a.months < b.months) + return EARLIER; + if (a.months > b.months) + return LATER; + if (a.milliseconds < b.milliseconds) + return EARLIER; + if (a.milliseconds > b.milliseconds) + return LATER; + return SAMETIME; +} + +/* + * convert client times to server TimeStamps + */ + +#define HALFMONTH ((unsigned long) 1<<31) +TimeStamp +ClientTimeToServerTime(c) + CARD32 c; +{ + TimeStamp ts; + if (c == CurrentTime) + return currentTime; + ts.months = currentTime.months; + ts.milliseconds = c; + if (c > currentTime.milliseconds) + { + if (((unsigned long) c - currentTime.milliseconds) > HALFMONTH) + ts.months -= 1; + } + else if (c < currentTime.milliseconds) + { + if (((unsigned long)currentTime.milliseconds - c) > HALFMONTH) + ts.months += 1; + } + return ts; +} + +/* + * ISO Latin-1 case conversion routine + * + * this routine always null-terminates the result, so + * beware of too-small buffers + */ + +void +CopyISOLatin1Lowered(dest, source, length) + register unsigned char *dest, *source; + int length; +{ + register int i; + + for (i = 0; i < length; i++, source++, dest++) + { + if ((*source >= XK_A) && (*source <= XK_Z)) + *dest = *source + (XK_a - XK_A); + else if ((*source >= XK_Agrave) && (*source <= XK_Odiaeresis)) + *dest = *source + (XK_agrave - XK_Agrave); + else if ((*source >= XK_Ooblique) && (*source <= XK_Thorn)) + *dest = *source + (XK_oslash - XK_Ooblique); + else + *dest = *source; + } + *dest = '\0'; +} + +#ifdef XCSECURITY + +/* SecurityLookupWindow and SecurityLookupDrawable: + * Look up the window/drawable taking into account the client doing + * the lookup and the type of access desired. Return the window/drawable + * if it exists and the client is allowed access, else return NULL. + * Most Proc* functions should be calling these instead of + * LookupWindow and LookupDrawable, which do no access checks. + */ + +WindowPtr +SecurityLookupWindow(rid, client, access_mode) + XID rid; + ClientPtr client; + Mask access_mode; +{ + WindowPtr pWin; + + client->errorValue = rid; + if(rid == INVALID) + return NULL; + if (client->trustLevel != XSecurityClientTrusted) + return (WindowPtr)SecurityLookupIDByType(client, rid, RT_WINDOW, access_mode); + if (client->lastDrawableID == rid) + { + if (client->lastDrawable->type == DRAWABLE_WINDOW) + return ((WindowPtr) client->lastDrawable); + return (WindowPtr) NULL; + } + pWin = (WindowPtr)SecurityLookupIDByType(client, rid, RT_WINDOW, access_mode); + if (pWin && pWin->drawable.type == DRAWABLE_WINDOW) { + client->lastDrawable = (DrawablePtr) pWin; + client->lastDrawableID = rid; + client->lastGCID = INVALID; + client->lastGC = (GCPtr)NULL; + } + return pWin; +} + + +pointer +SecurityLookupDrawable(rid, client, access_mode) + XID rid; + ClientPtr client; + Mask access_mode; +{ + register DrawablePtr pDraw; + + if(rid == INVALID) + return (pointer) NULL; + if (client->trustLevel != XSecurityClientTrusted) + return (DrawablePtr)SecurityLookupIDByClass(client, rid, RC_DRAWABLE, + access_mode); + if (client->lastDrawableID == rid) + return ((pointer) client->lastDrawable); + pDraw = (DrawablePtr)SecurityLookupIDByClass(client, rid, RC_DRAWABLE, + access_mode); + if (pDraw && (pDraw->type != UNDRAWABLE_WINDOW)) + return (pointer)pDraw; + return (pointer)NULL; +} + +/* We can't replace the LookupWindow and LookupDrawable functions with + * macros because of compatibility with loadable servers. + */ + +WindowPtr +LookupWindow(rid, client) + XID rid; + ClientPtr client; +{ + return SecurityLookupWindow(rid, client, SecurityUnknownAccess); +} + +pointer +LookupDrawable(rid, client) + XID rid; + ClientPtr client; +{ + return SecurityLookupDrawable(rid, client, SecurityUnknownAccess); +} + +#else /* not XCSECURITY */ + +WindowPtr +LookupWindow(rid, client) + XID rid; + ClientPtr client; +{ + WindowPtr pWin; + + client->errorValue = rid; + if(rid == INVALID) + return NULL; + if (client->lastDrawableID == rid) + { + if (client->lastDrawable->type == DRAWABLE_WINDOW) + return ((WindowPtr) client->lastDrawable); + return (WindowPtr) NULL; + } + pWin = (WindowPtr)LookupIDByType(rid, RT_WINDOW); + if (pWin && pWin->drawable.type == DRAWABLE_WINDOW) { + client->lastDrawable = (DrawablePtr) pWin; + client->lastDrawableID = rid; + client->lastGCID = INVALID; + client->lastGC = (GCPtr)NULL; + } + return pWin; +} + + +pointer +LookupDrawable(rid, client) + XID rid; + ClientPtr client; +{ + register DrawablePtr pDraw; + + if(rid == INVALID) + return (pointer) NULL; + if (client->lastDrawableID == rid) + return ((pointer) client->lastDrawable); + pDraw = (DrawablePtr)LookupIDByClass(rid, RC_DRAWABLE); + if (pDraw && (pDraw->type != UNDRAWABLE_WINDOW)) + return (pointer)pDraw; + return (pointer)NULL; +} + +#endif /* XCSECURITY */ + +ClientPtr +LookupClient(rid, client) + XID rid; + ClientPtr client; +{ + pointer pRes = (pointer)SecurityLookupIDByClass(client, rid, RC_ANY, + SecurityReadAccess); + int clientIndex = CLIENT_ID(rid); + + if (clientIndex && pRes && clients[clientIndex] && !(rid & SERVER_BIT)) + { + return clients[clientIndex]; + } + return (ClientPtr)NULL; +} + + +int +AlterSaveSetForClient(client, pWin, mode) + ClientPtr client; + WindowPtr pWin; + unsigned mode; +{ + int numnow; + pointer *pTmp; + int j; + + numnow = client->numSaved; + j = 0; + if (numnow) + { + pTmp = client->saveSet; + while ((j < numnow) && (pTmp[j] != (pointer)pWin)) + j++; + } + if (mode == SetModeInsert) + { + if (j < numnow) /* duplicate */ + return(Success); + numnow++; + pTmp = (pointer *)xrealloc(client->saveSet, sizeof(pointer) * numnow); + if (!pTmp) + return(BadAlloc); + client->saveSet = pTmp; + client->numSaved = numnow; + client->saveSet[numnow - 1] = (pointer)pWin; + return(Success); + } + else if ((mode == SetModeDelete) && (j < numnow)) + { + while (j < numnow-1) + { + pTmp[j] = pTmp[j+1]; + j++; + } + numnow--; + if (numnow) + { + pTmp = (pointer *)xrealloc(client->saveSet, + sizeof(pointer) * numnow); + if (pTmp) + client->saveSet = pTmp; + } + else + { + xfree(client->saveSet); + client->saveSet = (pointer *)NULL; + } + client->numSaved = numnow; + return(Success); + } + return(Success); +} + +void +DeleteWindowFromAnySaveSet(pWin) + WindowPtr pWin; +{ + register int i; + register ClientPtr client; + + for (i = 0; i< currentMaxClients; i++) + { + client = clients[i]; + if (client && client->numSaved) + (void)AlterSaveSetForClient(client, pWin, SetModeDelete); + } +} + +/* No-op Don't Do Anything : sometimes we need to be able to call a procedure + * that doesn't do anything. For example, on screen with only static + * colormaps, if someone calls install colormap, it's easier to have a dummy + * procedure to call than to check if there's a procedure + */ +void +NoopDDA( +#if NeedVarargsPrototypes + void* f, ... +#endif +) +{ +} + +typedef struct _BlockHandler { + BlockHandlerProcPtr BlockHandler; + WakeupHandlerProcPtr WakeupHandler; + pointer blockData; + Bool deleted; +} BlockHandlerRec, *BlockHandlerPtr; + +static BlockHandlerPtr handlers; +static int numHandlers; +static int sizeHandlers; +static Bool inHandler; +static Bool handlerDeleted; + +/* called from the OS layer */ +void +BlockHandler(pTimeout, pReadmask) +pointer pTimeout; /* DIX doesn't want to know how OS represents time */ +pointer pReadmask; /* nor how it represents the set of descriptors */ +{ + register int i, j; + + ++inHandler; + for (i = 0; i < screenInfo.numScreens; i++) + (* screenInfo.screens[i]->BlockHandler)(i, + screenInfo.screens[i]->blockData, + pTimeout, pReadmask); + for (i = 0; i < numHandlers; i++) + (*handlers[i].BlockHandler) (handlers[i].blockData, + pTimeout, pReadmask); + if (handlerDeleted) + { + for (i = 0; i < numHandlers;) + if (handlers[i].deleted) + { + for (j = i; j < numHandlers - 1; j++) + handlers[j] = handlers[j+1]; + numHandlers--; + } + else + i++; + handlerDeleted = FALSE; + } + --inHandler; +} + +void +WakeupHandler(result, pReadmask) +int result; /* 32 bits of undefined result from the wait */ +pointer pReadmask; /* the resulting descriptor mask */ +{ + register int i, j; + + ++inHandler; + for (i = numHandlers - 1; i >= 0; i--) + (*handlers[i].WakeupHandler) (handlers[i].blockData, + result, pReadmask); + for (i = 0; i < screenInfo.numScreens; i++) + (* screenInfo.screens[i]->WakeupHandler)(i, + screenInfo.screens[i]->wakeupData, + result, pReadmask); + if (handlerDeleted) + { + for (i = 0; i < numHandlers;) + if (handlers[i].deleted) + { + for (j = i; j < numHandlers - 1; j++) + handlers[j] = handlers[j+1]; + numHandlers--; + } + else + i++; + handlerDeleted = FALSE; + } + --inHandler; +} + +/* Reentrant with BlockHandler and WakeupHandler, except wakeup won't + * get called until next time + */ + +Bool +RegisterBlockAndWakeupHandlers (blockHandler, wakeupHandler, blockData) + BlockHandlerProcPtr blockHandler; + WakeupHandlerProcPtr wakeupHandler; + pointer blockData; +{ + BlockHandlerPtr new; + + if (numHandlers >= sizeHandlers) + { + new = (BlockHandlerPtr) xrealloc (handlers, (numHandlers + 1) * + sizeof (BlockHandlerRec)); + if (!new) + return FALSE; + handlers = new; + sizeHandlers = numHandlers + 1; + } + handlers[numHandlers].BlockHandler = blockHandler; + handlers[numHandlers].WakeupHandler = wakeupHandler; + handlers[numHandlers].blockData = blockData; + handlers[numHandlers].deleted = FALSE; + numHandlers = numHandlers + 1; + return TRUE; +} + +void +RemoveBlockAndWakeupHandlers (blockHandler, wakeupHandler, blockData) + BlockHandlerProcPtr blockHandler; + WakeupHandlerProcPtr wakeupHandler; + pointer blockData; +{ + int i; + + for (i = 0; i < numHandlers; i++) + if (handlers[i].BlockHandler == blockHandler && + handlers[i].WakeupHandler == wakeupHandler && + handlers[i].blockData == blockData) + { + if (inHandler) + { + handlerDeleted = TRUE; + handlers[i].deleted = TRUE; + } + else + { + for (; i < numHandlers - 1; i++) + handlers[i] = handlers[i+1]; + numHandlers--; + } + break; + } +} + +void +InitBlockAndWakeupHandlers () +{ + xfree (handlers); + handlers = (BlockHandlerPtr) 0; + numHandlers = 0; + sizeHandlers = 0; +} + +/* + * A general work queue. Perform some task before the server + * sleeps for input. + */ + +WorkQueuePtr workQueue; +static WorkQueuePtr *workQueueLast = &workQueue; + +void +ProcessWorkQueue() +{ + WorkQueuePtr q, *p; + + p = &workQueue; + /* + * Scan the work queue once, calling each function. Those + * which return TRUE are removed from the queue, otherwise + * they will be called again. This must be reentrant with + * QueueWorkProc. + */ + while (q = *p) + { + if ((*q->function) (q->client, q->closure)) + { + /* remove q from the list */ + *p = q->next; /* don't fetch until after func called */ + xfree (q); + } + else + { + p = &q->next; /* don't fetch until after func called */ + } + } + workQueueLast = p; +} + +void +ProcessWorkQueueZombies() +{ + WorkQueuePtr q, *p; + + p = &workQueue; + while (q = *p) + { + if (q->client && q->client->clientGone) + { + (void) (*q->function) (q->client, q->closure); + /* remove q from the list */ + *p = q->next; /* don't fetch until after func called */ + xfree (q); + } + else + { + p = &q->next; /* don't fetch until after func called */ + } + } + workQueueLast = p; +} + +Bool +#if NeedFunctionPrototypes +QueueWorkProc ( + Bool (*function)( +#if NeedNestedPrototypes + ClientPtr /* pClient */, + pointer /* closure */ +#endif + ), + ClientPtr client, + pointer closure) +#else +QueueWorkProc (function, client, closure) + Bool (*function)(); + ClientPtr client; + pointer closure; +#endif +{ + WorkQueuePtr q; + + q = (WorkQueuePtr) xalloc (sizeof *q); + if (!q) + return FALSE; + q->function = function; + q->client = client; + q->closure = closure; + q->next = NULL; + *workQueueLast = q; + workQueueLast = &q->next; + return TRUE; +} + +/* + * Manage a queue of sleeping clients, awakening them + * when requested, by using the OS functions IgnoreClient + * and AttendClient. Note that this *ignores* the troubles + * with request data interleaving itself with events, but + * we'll leave that until a later time. + */ + +typedef struct _SleepQueue { + struct _SleepQueue *next; + ClientPtr client; + ClientSleepProcPtr function; + pointer closure; +} SleepQueueRec, *SleepQueuePtr; + +static SleepQueuePtr sleepQueue = NULL; + +Bool +ClientSleep (client, function, closure) + ClientPtr client; + ClientSleepProcPtr function; + pointer closure; +{ + SleepQueuePtr q; + + q = (SleepQueuePtr) xalloc (sizeof *q); + if (!q) + return FALSE; + + IgnoreClient (client); + q->next = sleepQueue; + q->client = client; + q->function = function; + q->closure = closure; + sleepQueue = q; + return TRUE; +} + +Bool +ClientSignal (client) + ClientPtr client; +{ + SleepQueuePtr q; + + for (q = sleepQueue; q; q = q->next) + if (q->client == client) + { + return QueueWorkProc (q->function, q->client, q->closure); + } + return FALSE; +} + +void +ClientWakeup (client) + ClientPtr client; +{ + SleepQueuePtr q, *prev; + + prev = &sleepQueue; + while ( (q = *prev) ) + { + if (q->client == client) + { + *prev = q->next; + xfree (q); + if (client->clientGone) + CloseDownClient(client); + else + AttendClient (client); + break; + } + prev = &q->next; + } +} + +Bool +ClientIsAsleep (client) + ClientPtr client; +{ + SleepQueuePtr q; + + for (q = sleepQueue; q; q = q->next) + if (q->client == client) + return TRUE; + return FALSE; +} + +/* + * Generic Callback Manager + */ + +/* ===== Private Procedures ===== */ + +static int numCallbackListsToCleanup = 0; +static CallbackListPtr **listsToCleanup = NULL; + +static Bool +#if NeedFunctionPrototypes +_AddCallback( + CallbackListPtr *pcbl, + CallbackProcPtr callback, + pointer data) +#else +_AddCallback(pcbl, callback, data) + CallbackListPtr *pcbl; + CallbackProcPtr callback; + pointer data; +#endif +{ + CallbackPtr cbr; + + cbr = (CallbackPtr) xalloc(sizeof(CallbackRec)); + if (!cbr) + return FALSE; + cbr->proc = callback; + cbr->data = data; + cbr->next = (*pcbl)->list; + cbr->deleted = FALSE; + (*pcbl)->list = cbr; + return TRUE; +} + +static Bool +#if NeedFunctionPrototypes +_DeleteCallback( + CallbackListPtr *pcbl, + CallbackProcPtr callback, + pointer data) +#else +_DeleteCallback(pcbl, callback, data) + CallbackListPtr *pcbl; + CallbackProcPtr callback; + pointer data; +#endif +{ + CallbackListPtr cbl = *pcbl; + CallbackPtr cbr, pcbr; + + for (pcbr = NULL, cbr = cbl->list; + cbr != NULL; + pcbr = cbr, cbr = cbr->next) + { + if ((cbr->proc == callback) && (cbr->data == data)) + break; + } + if (cbr != NULL) + { + if (cbl->inCallback) + { + ++(cbl->numDeleted); + cbr->deleted = TRUE; + } + else + { + if (pcbr == NULL) + cbl->list = cbr->next; + else + pcbr->next = cbr->next; + xfree(cbr); + } + return TRUE; + } + return FALSE; +} + +static void +#if NeedFunctionPrototypes +_CallCallbacks( + CallbackListPtr *pcbl, + pointer call_data) +#else +_CallCallbacks(pcbl, call_data) + CallbackListPtr *pcbl; + pointer call_data; +#endif +{ + CallbackListPtr cbl = *pcbl; + CallbackPtr cbr, pcbr; + + ++(cbl->inCallback); + for (cbr = cbl->list; cbr != NULL; cbr = cbr->next) + { + (*(cbr->proc)) (pcbl, cbr->data, call_data); + } + --(cbl->inCallback); + + if (cbl->inCallback) return; + + /* Was the entire list marked for deletion? */ + + if (cbl->deleted) + { + DeleteCallbackList(pcbl); + return; + } + + /* Were some individual callbacks on the list marked for deletion? + * If so, do the deletions. + */ + + if (cbl->numDeleted) + { + for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted; ) + { + if (cbr->deleted) + { + if (pcbr) + { + cbr = cbr->next; + xfree(pcbr->next); + pcbr->next = cbr; + } else + { + cbr = cbr->next; + xfree(cbl->list); + cbl->list = cbr; + } + cbl->numDeleted--; + } + else /* this one wasn't deleted */ + { + pcbr = cbr; + cbr = cbr->next; + } + } + } +} + +static void +#if NeedFunctionPrototypes +_DeleteCallbackList( + CallbackListPtr *pcbl) +#else +_DeleteCallbackList(pcbl) + CallbackListPtr *pcbl; +#endif +{ + CallbackListPtr cbl = *pcbl; + CallbackPtr cbr, nextcbr; + int i; + + if (cbl->inCallback) + { + cbl->deleted = TRUE; + return; + } + + for (i = 0; i < numCallbackListsToCleanup; i++) + { + if ((listsToCleanup[i] = pcbl) != 0) + { + listsToCleanup[i] = NULL; + break; + } + } + + for (cbr = cbl->list; cbr != NULL; cbr = nextcbr) + { + nextcbr = cbr->next; + xfree(cbr); + } + xfree(cbl); + *pcbl = NULL; +} + +static CallbackFuncsRec default_cbfuncs = +{ + _AddCallback, + _DeleteCallback, + _CallCallbacks, + _DeleteCallbackList +}; + +/* ===== Public Procedures ===== */ + +Bool +CreateCallbackList(pcbl, cbfuncs) + CallbackListPtr *pcbl; + CallbackFuncsPtr cbfuncs; +{ + CallbackListPtr cbl; + int i; + + if (!pcbl) return FALSE; + cbl = (CallbackListPtr) xalloc(sizeof(CallbackListRec)); + if (!cbl) return FALSE; + cbl->funcs = cbfuncs ? *cbfuncs : default_cbfuncs; + cbl->inCallback = 0; + cbl->deleted = FALSE; + cbl->numDeleted = 0; + cbl->list = NULL; + *pcbl = cbl; + + for (i = 0; i < numCallbackListsToCleanup; i++) + { + if (!listsToCleanup[i]) + { + listsToCleanup[i] = pcbl; + return TRUE; + } + } + + listsToCleanup = (CallbackListPtr **)xnfrealloc(listsToCleanup, + sizeof(CallbackListPtr *) * (numCallbackListsToCleanup+1)); + listsToCleanup[numCallbackListsToCleanup] = pcbl; + numCallbackListsToCleanup++; + return TRUE; +} + +Bool +AddCallback(pcbl, callback, data) + CallbackListPtr *pcbl; + CallbackProcPtr callback; + pointer data; +{ + if (!pcbl) return FALSE; + if (!*pcbl) + { /* list hasn't been created yet; go create it */ + if (!CreateCallbackList(pcbl, (CallbackFuncsPtr)NULL)) + return FALSE; + } + return ((*(*pcbl)->funcs.AddCallback) (pcbl, callback, data)); +} + +Bool +DeleteCallback(pcbl, callback, data) + CallbackListPtr *pcbl; + CallbackProcPtr callback; + pointer data; +{ + if (!pcbl || !*pcbl) return FALSE; + return ((*(*pcbl)->funcs.DeleteCallback) (pcbl, callback, data)); +} + +void +CallCallbacks(pcbl, call_data) + CallbackListPtr *pcbl; + pointer call_data; +{ + if (!pcbl || !*pcbl) return; + (*(*pcbl)->funcs.CallCallbacks) (pcbl, call_data); +} + +void +DeleteCallbackList(pcbl) + CallbackListPtr *pcbl; +{ + if (!pcbl || !*pcbl) return; + (*(*pcbl)->funcs.DeleteCallbackList) (pcbl); +} + +void +InitCallbackManager() +{ + int i; + + for (i = 0; i < numCallbackListsToCleanup; i++) + { + DeleteCallbackList(listsToCleanup[i]); + } + if (listsToCleanup) xfree(listsToCleanup); + + numCallbackListsToCleanup = 0; + listsToCleanup = NULL; +}