/* 
 * tkXId.c --
 *
 *	This file provides a replacement function for the default X
 *	resource allocator (_XAllocID).  The problem with the default
 *	allocator is that it never re-uses ids, which causes long-lived
 *	applications to crash when X resource identifiers wrap around.
 *	The replacement functions in this file re-use old identifiers
 *	to prevent this problem.
 *
 *	The code in this file is based on similar implementations by
 *	George C. Kaplan and Michael Hoegeman.
 *
 * Copyright (c) 1993 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#ifndef lint
static char sccsid[] = "@(#) tkXId.c 1.5 95/01/08 13:57:37";
#endif /* not lint */

/*
 * The definition below is needed on some systems so that we can access
 * the resource_alloc field of Display structures in order to replace
 * the resource allocator.
 */

#define XLIB_ILLEGAL_ACCESS 1

#include "tkInt.h"
#include "tkPort.h"

/*
 * A structure of the following type is used to hold one or more
 * available resource identifiers.  There is a list of these structures
 * for each display.
 */

#define IDS_PER_STACK 10
typedef struct TkIdStack {
    XID ids[IDS_PER_STACK];		/* Array of free identifiers. */
    int numUsed;			/* Indicates how many of the entries
					 * in ids are currently in use. */
    struct TkIdStack *nextPtr;		/* Next bunch of free identifiers
					 * for the same display. */
} TkIdStack;

/*
 * Forward declarations for procedures defined in this file:
 */

static XID		AllocXId _ANSI_ARGS_((Display *display));

/*
 *----------------------------------------------------------------------
 *
 * TkInitXId --
 *
 *	This procedure is called to initialize the id allocator for
 *	a given display.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The official allocator for the display is set up to be Tk_AllocXID.
 *
 *----------------------------------------------------------------------
 */

void
TkInitXId(dispPtr)
    TkDisplay *dispPtr;			/* Tk's information about the
					 * display. */
{
    dispPtr->idStackPtr = NULL;
    dispPtr->defaultAllocProc = dispPtr->display->resource_alloc;
    dispPtr->display->resource_alloc = AllocXId;
}

/*
 *----------------------------------------------------------------------
 *
 * AllocXId --
 *
 *	This procedure is invoked by Xlib as the resource allocator
 *	for a display.
 *
 * Results:
 *	The return value is an X resource identifier that isn't currently
 *	in use.
 *
 * Side effects:
 *	The identifier is removed from the stack of free identifiers,
 *	if it was previously on the stack.
 *
 *----------------------------------------------------------------------
 */

static XID
AllocXId(display)
    Display *display;			/* Display for which to allocate. */
{
    TkDisplay *dispPtr;
    TkIdStack *stackPtr;

    /*
     * Find Tk's information about the display.
     */

    for (dispPtr = tkDisplayList; dispPtr->display != display;
	    dispPtr = dispPtr->nextPtr) {
	/* Empty loop body. */
    }

    /*
     * If the topmost chunk on the stack is empty then free it.  Then
     * check for a free id on the stack and return it if it exists.
     */

    stackPtr = dispPtr->idStackPtr;
    if (stackPtr != NULL) {
	if (stackPtr->numUsed == 0) {
	    dispPtr->idStackPtr = stackPtr->nextPtr;
	    ckfree((char *) stackPtr);
	    stackPtr = dispPtr->idStackPtr;
	    if (stackPtr == NULL) {
		goto defAlloc;
	    }
	}
	stackPtr->numUsed--;
	return stackPtr->ids[stackPtr->numUsed];
    }

    /*
     * No free ids in the stack:  just get one from the default
     * allocator.
     */

    defAlloc:
    return (*dispPtr->defaultAllocProc)(display);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_FreeXId --
 *
 *	This procedure is called to indicate that an X resource identifier
 *	is now free.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The identifier is added to the stack of free identifiers for its
 *	display, so that it can be re-used.
 *
 *----------------------------------------------------------------------
 */

void
Tk_FreeXId(display, xid)
    Display *display;			/* Display for which xid was
					 * allocated. */
    XID xid;				/* Identifier that is no longer
					 * in use. */
{
    TkDisplay *dispPtr;
    TkIdStack *stackPtr;

    /*
     * Find Tk's information about the display.
     */

    for (dispPtr = tkDisplayList; dispPtr->display != display;
	    dispPtr = dispPtr->nextPtr) {
	/* Empty loop body. */
    }

    /*
     * Add a new chunk to the stack if the current chunk is full.
     */

    stackPtr = dispPtr->idStackPtr;
    if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
	stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
	stackPtr->numUsed = 0;
	stackPtr->nextPtr = dispPtr->idStackPtr;
	dispPtr->idStackPtr = stackPtr;
    }

    /*
     * Add the id to the current chunk.
     */

    stackPtr->ids[stackPtr->numUsed] = xid;
    stackPtr->numUsed++;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetPixmap --
 *
 *	Same as the XCreatePixmap procedure except that it manages
 *	resource identifiers better.
 *
 * Results:
 *	Returns a new pixmap.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Pixmap
Tk_GetPixmap(display, d, width, height, depth)
    Display *display;		/* Display for new pixmap. */
    Drawable d;			/* Drawable where pixmap will be used. */
    unsigned int width, height;	/* Dimensions of pixmap. */
    unsigned int depth;		/* Bits per pixel for pixmap. */
{
    return XCreatePixmap(display, d, width, height, depth);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_FreePixmap --
 *
 *	Same as the XFreePixmap procedure except that it also marks
 *	the resource identifier as free.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The pixmap is freed in the X server and its resource identifier
 *	is saved for re-use.
 *
 *----------------------------------------------------------------------
 */

void
Tk_FreePixmap(display, pixmap)
    Display *display;		/* Display for which pixmap was allocated. */
    Pixmap pixmap;		/* Identifier for pixmap. */
{
    XFreePixmap(display, pixmap);
    Tk_FreeXId(display, (XID) pixmap);
}
