/* 
 * tkBitmap.c --
 *
 *	This file maintains a database of read-only bitmaps for the Tk
 *	toolkit.  This allows bitmaps to be shared between widgets and
 *	also avoids interactions with the X server.
 *
 * Copyright (c) 1990-1994 The Regents of the University of California.
 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkBitmap.c,v 1.1.1.1 1999/06/25 17:35:56 damon Exp $
 */

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

/*
 * The includes below are for pre-defined bitmaps.
 *
 * Platform-specific issue: Windows complains when the bitmaps are
 * included, because an array of characters is being initialized with
 * integers as elements.  For lint purposes, the following pragmas
 * temporarily turn off that warning message.
 */

#if defined(__WIN32__) || defined(_WIN32)
#pragma warning (disable : 4305)
#endif

#include "error.bmp"
#include "gray12.bmp"
#include "gray25.bmp"
#include "gray50.bmp"
#include "gray75.bmp"
#include "hourglass.bmp"
#include "info.bmp"
#include "questhead.bmp"
#include "question.bmp"
#include "warning.bmp"

#if defined(__WIN32__) || defined(_WIN32)
#pragma warning (default : 4305)
#endif

/*
 * One of the following data structures exists for each bitmap that is
 * currently in use.  Each structure is indexed with both "idTable" and
 * "nameTable".
 */

typedef struct TkBitmap {
    Pixmap bitmap;		/* X identifier for bitmap.  None means this
				 * bitmap was created by Tk_DefineBitmap
				 * and it isn't currently in use. */
    int width, height;		/* Dimensions of bitmap. */
    Display *display;		/* Display for which bitmap is valid. */
    int resourceRefCount;	/* Number of active uses of this bitmap (each
				 * active use corresponds to a call to
				 * Tk_AllocBitmapFromObj or Tk_GetBitmap).
				 * If this count is 0, then this TkBitmap
				 * structure is no longer valid and it isn't
				 * present in nameTable: it is being kept
				 * around only because there are objects
				 * referring to it.  The structure is freed
				 * when resourceRefCount and objRefCount
				 * are both 0. */
    int objRefCount;		/* Number of Tcl_Obj's that reference
				 * this structure. */
    Tcl_HashEntry *nameHashPtr;	/* Entry in nameTable for this structure
				 * (needed when deleting). */
    Tcl_HashEntry *idHashPtr;	/* Entry in idTable for this structure
				 * (needed when deleting). */
    struct TkBitmap *nextPtr;	/* Points to the next TkBitmap structure with
				 * the same name.  All bitmaps with the
				 * same name (but different displays) are
				 * chained together off a single entry in
				 * nameTable. */
} TkBitmap;

/* 
 * Used in bitmapDataTable, stored in the TkDisplay structure, to map
 * between in-core data about a bitmap to its TkBitmap structure.
 */

typedef struct {
    char *source;		/* Bitmap bits. */
    int width, height;		/* Dimensions of bitmap. */
} DataKey;

typedef struct ThreadSpecificData {
    int initialized;            /* 0 means table below needs initializing. */
    Tcl_HashTable predefBitmapTable;
                                /* Hash table created by Tk_DefineBitmap 
				 * to map from a name to a collection 
				 * of in-core data about a bitmap.  The 
				 * table is indexed by the address of the 
				 * data for the bitmap, and the entries
				 * contain pointers to TkPredefBitmap 
				 * structures. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

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

static void		BitmapInit _ANSI_ARGS_((TkDisplay *dispPtr));
static void		DupBitmapObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr,
			    Tcl_Obj *dupObjPtr));
static void		FreeBitmap _ANSI_ARGS_((TkBitmap *bitmapPtr));
static void		FreeBitmapObjProc _ANSI_ARGS_((Tcl_Obj *objPtr));
static TkBitmap *	GetBitmap _ANSI_ARGS_((Tcl_Interp *interp,
			    Tk_Window tkwin, CONST char *name));
static TkBitmap *	GetBitmapFromObj _ANSI_ARGS_((Tk_Window tkwin,
			    Tcl_Obj *objPtr));
static void		InitBitmapObj _ANSI_ARGS_((Tcl_Obj *objPtr));

/*
 * The following structure defines the implementation of the "bitmap" Tcl
 * object, which maps a string bitmap name to a TkBitmap object.  The
 * ptr1 field of the Tcl_Obj points to a TkBitmap object.
 */

static Tcl_ObjType bitmapObjType = {
    "bitmap",			/* name */
    FreeBitmapObjProc,		/* freeIntRepProc */
    DupBitmapObjProc,		/* dupIntRepProc */
    NULL,			/* updateStringProc */
    NULL			/* setFromAnyProc */
};

/*
 *----------------------------------------------------------------------
 *
 * Tk_AllocBitmapFromObj --
 *
 *	Given a Tcl_Obj *, map the value to a corresponding
 *	Pixmap structure based on the tkwin given.
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(i.e. a Pixmap with a single plane), unless string couldn't be
 *	parsed correctly.  In this case, None is returned and an error
 *	message is left in the interp's result.  The caller should never
 *	modify the bitmap that is returned, and should eventually call
 *	Tk_FreeBitmapFromObj when the bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmapFromObj, so that the database can be cleaned up 
 *	when bitmaps aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

Pixmap
Tk_AllocBitmapFromObj(interp, tkwin, objPtr)
    Tcl_Interp *interp;		/* Interp for error results. This may 
				 * be NULL. */
    Tk_Window tkwin;		/* Need the screen the bitmap is used on.*/
    Tcl_Obj *objPtr;		/* Object describing bitmap; see manual
				 * entry for legal syntax of string value. */
{
    TkBitmap *bitmapPtr;

    if (objPtr->typePtr != &bitmapObjType) {
	InitBitmapObj(objPtr);
    }
    bitmapPtr = (TkBitmap *) objPtr->internalRep.twoPtrValue.ptr1;

    /*
     * If the object currently points to a TkBitmap, see if it's the
     * one we want.  If so, increment its reference count and return.
     */

    if (bitmapPtr != NULL) {
	if (bitmapPtr->resourceRefCount == 0) {
	    /*
	     * This is a stale reference: it refers to a TkBitmap that's
	     * no longer in use.  Clear the reference.
	     */

	    FreeBitmapObjProc(objPtr);
	    bitmapPtr = NULL;
	} else if (Tk_Display(tkwin) == bitmapPtr->display) {
	    bitmapPtr->resourceRefCount++;
	    return bitmapPtr->bitmap;
	}
    }

    /*
     * The object didn't point to the TkBitmap that we wanted.  Search
     * the list of TkBitmaps with the same name to see if one of the
     * others is the right one.
     */

    if (bitmapPtr != NULL) {
	TkBitmap *firstBitmapPtr =
		(TkBitmap *) Tcl_GetHashValue(bitmapPtr->nameHashPtr);
	FreeBitmapObjProc(objPtr);
	for (bitmapPtr = firstBitmapPtr; bitmapPtr != NULL;
		bitmapPtr = bitmapPtr->nextPtr) {
	    if (Tk_Display(tkwin) == bitmapPtr->display) {
		bitmapPtr->resourceRefCount++;
		bitmapPtr->objRefCount++;
		objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) bitmapPtr;
		return bitmapPtr->bitmap;
	    }
	}
    }

    /*
     * Still no luck.  Call GetBitmap to allocate a new TkBitmap object.
     */

    bitmapPtr = GetBitmap(interp, tkwin, Tcl_GetString(objPtr));
    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) bitmapPtr;
    if (bitmapPtr == NULL) {
	return None;
    }
    bitmapPtr->objRefCount++;
    return bitmapPtr->bitmap;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetBitmap --
 *
 *	Given a string describing a bitmap, locate (or create if necessary)
 *	a bitmap that fits the description.
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(i.e. a Pixmap with a single plane), unless string couldn't be
 *	parsed correctly.  In this case, None is returned and an error
 *	message is left in the interp's result.  The caller should never
 *	modify the bitmap that is returned, and should eventually call
 *	Tk_FreeBitmap when the bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps
 *	aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

Pixmap
Tk_GetBitmap(interp, tkwin, string)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting,
				 * this may be NULL. */
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    CONST char *string;		/* Description of bitmap.  See manual entry
				 * for details on legal syntax. */
{
    TkBitmap *bitmapPtr = GetBitmap(interp, tkwin, string);
    if (bitmapPtr == NULL) {
	return None;
    }
    return bitmapPtr->bitmap;
}

/*
 *----------------------------------------------------------------------
 *
 * GetBitmap --
 *
 *	Given a string describing a bitmap, locate (or create if necessary)
 *	a bitmap that fits the description. This routine returns the
 *	internal data structure for the bitmap. This avoids extra
 *	hash table lookups in Tk_AllocBitmapFromObj.
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(i.e. a Pixmap with a single plane), unless string couldn't be
 *	parsed correctly.  In this case, None is returned and an error
 *	message is left in the interp's result.  The caller should never
 *	modify the bitmap that is returned, and should eventually call
 *	Tk_FreeBitmap when the bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmap or Tk_FreeBitmapFromObj, so that the database can
 *	be cleaned up when bitmaps aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

static TkBitmap *
GetBitmap(interp, tkwin, string)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting,
				 * this may be NULL. */
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    CONST char *string;		/* Description of bitmap.  See manual entry
				 * for details on legal syntax. */
{
    Tcl_HashEntry *nameHashPtr, *predefHashPtr;
    TkBitmap *bitmapPtr, *existingBitmapPtr;
    TkPredefBitmap *predefPtr;
    int new;
    Pixmap bitmap;
    int width, height;
    int dummy2;
    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    if (!dispPtr->bitmapInit) {
	BitmapInit(dispPtr);
    }

    nameHashPtr = Tcl_CreateHashEntry(&dispPtr->bitmapNameTable, string, &new);
    if (!new) {
	existingBitmapPtr = (TkBitmap *) Tcl_GetHashValue(nameHashPtr);
	for (bitmapPtr = existingBitmapPtr; bitmapPtr != NULL;
		bitmapPtr = bitmapPtr->nextPtr) {
	    if (Tk_Display(tkwin) == bitmapPtr->display) {
		bitmapPtr->resourceRefCount++;
		return bitmapPtr;
	    }
	}
    } else {
	existingBitmapPtr = NULL;
    }

    /*
     * No suitable bitmap exists.  Create a new bitmap from the
     * information contained in the string.  If the string starts
     * with "@" then the rest of the string is a file name containing
     * the bitmap.  Otherwise the string must refer to a bitmap
     * defined by a call to Tk_DefineBitmap.
     */

    if (*string == '@') {	/* INTL: ISO char */
	Tcl_DString buffer;
	int result;

        if (Tcl_IsSafe(interp)) {
            Tcl_AppendResult(interp, "can't specify bitmap with '@' in a",
                    " safe interpreter", (char *) NULL);
            goto error;
        }

	/*
	 * Note that we need to cast away the CONST from the string because
	 * Tcl_TranslateFileName is non const, even though it doesn't modify
	 * the string.
	 */

	string = Tcl_TranslateFileName(interp, (char *) string + 1, &buffer);
	if (string == NULL) {
	    goto error;
	}
	result = TkReadBitmapFile(Tk_Display(tkwin),
		RootWindowOfScreen(Tk_Screen(tkwin)), string,
		(unsigned int *) &width, (unsigned int *) &height,
		&bitmap, &dummy2, &dummy2);
	if (result != BitmapSuccess) {
	    if (interp != NULL) {
		Tcl_AppendResult(interp, "error reading bitmap file \"", string,
		    "\"", (char *) NULL);
	    }
	    Tcl_DStringFree(&buffer);
	    goto error;
	}
	Tcl_DStringFree(&buffer);
    } else {
	predefHashPtr = Tcl_FindHashEntry(&tsdPtr->predefBitmapTable, 
                string);
	if (predefHashPtr == NULL) {
	    /*
	     * The following platform specific call allows the user to
	     * define bitmaps that may only exist during run time.  If
	     * it returns None nothing was found and we return the error.
	     */
	    bitmap = TkpGetNativeAppBitmap(Tk_Display(tkwin), string,
		    &width, &height);
	    
	    if (bitmap == None) {
		if (interp != NULL) {
		    Tcl_AppendResult(interp, "bitmap \"", string,
			"\" not defined", (char *) NULL);
		}
		goto error;
	    }
	} else {
	    predefPtr = (TkPredefBitmap *) Tcl_GetHashValue(predefHashPtr);
	    width = predefPtr->width;
	    height = predefPtr->height;
	    if (predefPtr->native) {
		bitmap = TkpCreateNativeBitmap(Tk_Display(tkwin),
		    predefPtr->source);
		if (bitmap == None) {
		    panic("native bitmap creation failed");
		}
	    } else {
		bitmap = XCreateBitmapFromData(Tk_Display(tkwin),
		    RootWindowOfScreen(Tk_Screen(tkwin)), 
		    predefPtr->source,
		    (unsigned) width, (unsigned) height);
	    }
	}
    }

    /*
     * Add information about this bitmap to our database.
     */

    bitmapPtr = (TkBitmap *) ckalloc(sizeof(TkBitmap));
    bitmapPtr->bitmap = bitmap;
    bitmapPtr->width = width;
    bitmapPtr->height = height;
    bitmapPtr->display = Tk_Display(tkwin);
    bitmapPtr->resourceRefCount = 1;
    bitmapPtr->objRefCount = 0;
    bitmapPtr->nameHashPtr = nameHashPtr;
    bitmapPtr->idHashPtr = Tcl_CreateHashEntry(&dispPtr->bitmapIdTable, 
            (char *) bitmap, &new);
    if (!new) {
	panic("bitmap already registered in Tk_GetBitmap");
    }
    bitmapPtr->nextPtr = existingBitmapPtr;
    Tcl_SetHashValue(nameHashPtr, bitmapPtr);
    Tcl_SetHashValue(bitmapPtr->idHashPtr, bitmapPtr);
    return bitmapPtr;

    error:
    if (new) {
	Tcl_DeleteHashEntry(nameHashPtr);
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DefineBitmap --
 *
 *	This procedure associates a textual name with a binary bitmap
 *	description, so that the name may be used to refer to the
 *	bitmap in future calls to Tk_GetBitmap.
 *
 * Results:
 *	A standard Tcl result.  If an error occurs then TCL_ERROR is
 *	returned and a message is left in the interp's result.
 *
 * Side effects:
 *	"Name" is entered into the bitmap table and may be used from
 *	here on to refer to the given bitmap.
 *
 *----------------------------------------------------------------------
 */

int
Tk_DefineBitmap(interp, name, source, width, height)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    CONST char *name;		/* Name to use for bitmap.  Must not already
				 * be defined as a bitmap. */
    char *source;		/* Address of bits for bitmap. */
    int width;			/* Width of bitmap. */
    int height;			/* Height of bitmap. */
{
    int new;
    Tcl_HashEntry *predefHashPtr;
    TkPredefBitmap *predefPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /* 
     * Initialize the Bitmap module if not initialized already for this
     * thread.  Since the current TkDisplay structure cannot be 
     * introspected from here, pass a NULL pointer to BitmapInit,
     * which will know to initialize only the data in the 
     * ThreadSpecificData structure for the current thread.
     */ 

    if (!tsdPtr->initialized) {
	BitmapInit((TkDisplay *) NULL);
    }

    predefHashPtr = Tcl_CreateHashEntry(&tsdPtr->predefBitmapTable, 
            name, &new);
    if (!new) {
        Tcl_AppendResult(interp, "bitmap \"", name,
		"\" is already defined", (char *) NULL);
	return TCL_ERROR;
    }
    predefPtr = (TkPredefBitmap *) ckalloc(sizeof(TkPredefBitmap));
    predefPtr->source = source;
    predefPtr->width = width;
    predefPtr->height = height;
    predefPtr->native = 0;
    Tcl_SetHashValue(predefHashPtr, predefPtr);
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_NameOfBitmap --
 *
 *	Given a bitmap, return a textual string identifying the
 *	bitmap.
 *
 * Results:
 *	The return value is the string name associated with bitmap.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

char *
Tk_NameOfBitmap(display, bitmap)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
    Pixmap bitmap;			/* Bitmap whose name is wanted. */
{
    Tcl_HashEntry *idHashPtr;
    TkBitmap *bitmapPtr;
    TkDisplay *dispPtr = TkGetDisplay(display);

    if (dispPtr == NULL || !dispPtr->bitmapInit) {
	unknown:
	panic("Tk_NameOfBitmap received unknown bitmap argument");
    }

    idHashPtr = Tcl_FindHashEntry(&dispPtr->bitmapIdTable, (char *) bitmap);
    if (idHashPtr == NULL) {
	goto unknown;
    }
    bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);
    return bitmapPtr->nameHashPtr->key.string;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_SizeOfBitmap --
 *
 *	Given a bitmap managed by this module, returns the width
 *	and height of the bitmap.
 *
 * Results:
 *	The words at *widthPtr and *heightPtr are filled in with
 *	the dimenstions of bitmap.
 *
 * Side effects:
 *	If bitmap isn't managed by this module then the procedure
 *	panics..
 *
 *--------------------------------------------------------------
 */

void
Tk_SizeOfBitmap(display, bitmap, widthPtr, heightPtr)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
    Pixmap bitmap;			/* Bitmap whose size is wanted. */
    int *widthPtr;			/* Store bitmap width here. */
    int *heightPtr;			/* Store bitmap height here. */
{
    Tcl_HashEntry *idHashPtr;
    TkBitmap *bitmapPtr;
    TkDisplay *dispPtr = TkGetDisplay(display);

    if (!dispPtr->bitmapInit) {
	unknownBitmap:
	panic("Tk_SizeOfBitmap received unknown bitmap argument");
    }

    idHashPtr = Tcl_FindHashEntry(&dispPtr->bitmapIdTable, (char *) bitmap);
    if (idHashPtr == NULL) {
	goto unknownBitmap;
    }
    bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);
    *widthPtr = bitmapPtr->width;
    *heightPtr = bitmapPtr->height;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeBitmap --
 *
 *	This procedure does all the work of releasing a bitmap allocated by
 *	Tk_GetBitmap or TkGetBitmapFromData.  It is invoked by both
 *	Tk_FreeBitmap and Tk_FreeBitmapFromObj
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The reference count associated with bitmap is decremented, and
 *	it is officially deallocated if no-one is using it anymore.
 *
 *----------------------------------------------------------------------
 */

static void
FreeBitmap(bitmapPtr)
    TkBitmap *bitmapPtr;			/* Bitmap to be released. */
{
    TkBitmap *prevPtr;

    bitmapPtr->resourceRefCount--;
    if (bitmapPtr->resourceRefCount > 0) {
	return;
    }

    Tk_FreePixmap(bitmapPtr->display, bitmapPtr->bitmap);
    Tcl_DeleteHashEntry(bitmapPtr->idHashPtr);
    prevPtr = (TkBitmap *) Tcl_GetHashValue(bitmapPtr->nameHashPtr);
    if (prevPtr == bitmapPtr) {
	if (bitmapPtr->nextPtr == NULL) {
	    Tcl_DeleteHashEntry(bitmapPtr->nameHashPtr);
	} else {
	    Tcl_SetHashValue(bitmapPtr->nameHashPtr, bitmapPtr->nextPtr);
	}
    } else {
	while (prevPtr->nextPtr != bitmapPtr) {
	    prevPtr = prevPtr->nextPtr;
	}
	prevPtr->nextPtr = bitmapPtr->nextPtr;
    }
    if (bitmapPtr->objRefCount == 0) {
	ckfree((char *) bitmapPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_FreeBitmap --
 *
 *	This procedure is called to release a bitmap allocated by
 *	Tk_GetBitmap or TkGetBitmapFromData.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The reference count associated with bitmap is decremented, and
 *	it is officially deallocated if no-one is using it anymore.
 *
 *----------------------------------------------------------------------
 */

void
Tk_FreeBitmap(display, bitmap)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
    Pixmap bitmap;			/* Bitmap to be released. */
{
    Tcl_HashEntry *idHashPtr;
    TkDisplay *dispPtr = TkGetDisplay(display);

    if (!dispPtr->bitmapInit) {
	panic("Tk_FreeBitmap called before Tk_GetBitmap");
    }

    idHashPtr = Tcl_FindHashEntry(&dispPtr->bitmapIdTable, (char *) bitmap);
    if (idHashPtr == NULL) {
	panic("Tk_FreeBitmap received unknown bitmap argument");
    }
    FreeBitmap((TkBitmap *) Tcl_GetHashValue(idHashPtr));
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_FreeBitmapFromObj --
 *
 *	This procedure is called to release a bitmap allocated by
 *	Tk_AllocBitmapFromObj. It does not throw away the Tcl_Obj *;
 *	it only gets rid of the hash table entry for this bitmap
 *	and clears the cached value that is normally stored in the object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The reference count associated with the bitmap represented by
 *	objPtr is decremented, and the bitmap is released to X if there are 
 *	no remaining uses for it.
 *
 *----------------------------------------------------------------------
 */

void
Tk_FreeBitmapFromObj(tkwin, objPtr)
    Tk_Window tkwin;		/* The window this bitmap lives in. Needed
				 * for the display value. */
    Tcl_Obj *objPtr;		/* The Tcl_Obj * to be freed. */
{
    FreeBitmap(GetBitmapFromObj(tkwin, objPtr));
}

/*
 *---------------------------------------------------------------------------
 *
 * FreeBitmapObjProc -- 
 *
 *	This proc is called to release an object reference to a bitmap.
 *	Called when the object's internal rep is released or when
 *	the cached bitmapPtr needs to be changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The object reference count is decremented. When both it
 *	and the hash ref count go to zero, the color's resources
 *	are released.
 *
 *---------------------------------------------------------------------------
 */

static void
FreeBitmapObjProc(objPtr)
    Tcl_Obj *objPtr;		/* The object we are releasing. */
{
    TkBitmap *bitmapPtr = (TkBitmap *) objPtr->internalRep.twoPtrValue.ptr1;

    if (bitmapPtr != NULL) {
	bitmapPtr->objRefCount--;
	if ((bitmapPtr->objRefCount == 0)
		&& (bitmapPtr->resourceRefCount == 0)) {
	    ckfree((char *) bitmapPtr);
	}
	objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * DupBitmapObjProc -- 
 *
 *	When a cached bitmap object is duplicated, this is called to
 *	update the internal reps.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The color's objRefCount is incremented and the internal rep
 *	of the copy is set to point to it.
 *
 *---------------------------------------------------------------------------
 */

static void
DupBitmapObjProc(srcObjPtr, dupObjPtr)
    Tcl_Obj *srcObjPtr;		/* The object we are copying from. */
    Tcl_Obj *dupObjPtr;		/* The object we are copying to. */
{
    TkBitmap *bitmapPtr = (TkBitmap *) srcObjPtr->internalRep.twoPtrValue.ptr1;
    
    dupObjPtr->typePtr = srcObjPtr->typePtr;
    dupObjPtr->internalRep.twoPtrValue.ptr1 = (VOID *) bitmapPtr;

    if (bitmapPtr != NULL) {
	bitmapPtr->objRefCount++;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetBitmapFromData --
 *
 *	Given a description of the bits for a bitmap, make a bitmap that
 *	has the given properties. *** NOTE:  this procedure is obsolete
 *	and really shouldn't be used anymore. ***
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(a one-plane Pixmap), unless it couldn't be created properly.
 *	In this case, None is returned and an error message is left in
 *	the interp's result.  The caller should never modify the bitmap that
 *	is returned, and should eventually call Tk_FreeBitmap when the
 *	bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps
 *	aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
Pixmap
Tk_GetBitmapFromData(interp, tkwin, source, width, height)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    char *source;		/* Bitmap data for bitmap shape. */
    int width, height;		/* Dimensions of bitmap. */
{
    DataKey nameKey;
    Tcl_HashEntry *dataHashPtr;
    int new;
    char string[16 + TCL_INTEGER_SPACE];
    char *name;
    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;

    BitmapInit(dispPtr);

    nameKey.source = source;
    nameKey.width = width;
    nameKey.height = height;
    dataHashPtr = Tcl_CreateHashEntry(&dispPtr->bitmapDataTable, 
            (char *) &nameKey, &new);
    if (!new) {
	name = (char *) Tcl_GetHashValue(dataHashPtr);
    } else {
	dispPtr->bitmapAutoNumber++;
	sprintf(string, "_tk%d", dispPtr->bitmapAutoNumber);
	name = string;
	Tcl_SetHashValue(dataHashPtr, name);
	if (Tk_DefineBitmap(interp, name, source, width, height) != TCL_OK) {
	    Tcl_DeleteHashEntry(dataHashPtr);
	    return TCL_ERROR;
	}
    }
    return Tk_GetBitmap(interp, tkwin, name);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetBitmapFromObj --
 *
 *	Returns the bitmap referred to by a Tcl object.  The bitmap must
 *	already have been allocated via a call to Tk_AllocBitmapFromObj
 *	or Tk_GetBitmap.
 *
 * Results:
 *	Returns the Pixmap that matches the tkwin and the string rep
 *	of objPtr.
 *
 * Side effects:
 *	If the object is not already a bitmap, the conversion will free
 *	any old internal representation. 
 *
 *----------------------------------------------------------------------
 */

Pixmap
Tk_GetBitmapFromObj(tkwin, objPtr)
    Tk_Window tkwin;
    Tcl_Obj *objPtr;		/* The object from which to get pixels. */
{
    TkBitmap *bitmapPtr = GetBitmapFromObj(tkwin, objPtr);
    return bitmapPtr->bitmap;
}

/*
 *----------------------------------------------------------------------
 *
 * GetBitmapFromObj --
 *
 *	Returns the bitmap referred to by a Tcl object.  The bitmap must
 *	already have been allocated via a call to Tk_AllocBitmapFromObj
 *	or Tk_GetBitmap.
 *
 * Results:
 *	Returns the TkBitmap * that matches the tkwin and the string rep
 *	of  objPtr.
 *
 * Side effects:
 *	If the object is not already a bitmap, the conversion will free
 *	any old internal representation. 
 *
 *----------------------------------------------------------------------
 */

static TkBitmap *
GetBitmapFromObj(tkwin, objPtr)
    Tk_Window tkwin;		/* Window in which the bitmap will be used. */
    Tcl_Obj *objPtr;		/* The object that describes the desired
				 * bitmap. */
{
    TkBitmap *bitmapPtr; 
    Tcl_HashEntry *hashPtr;
    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;

    if (objPtr->typePtr != &bitmapObjType) {
	InitBitmapObj(objPtr);
    }

    bitmapPtr = (TkBitmap *) objPtr->internalRep.twoPtrValue.ptr1;
    if (bitmapPtr != NULL) { 
	if ((bitmapPtr->resourceRefCount > 0)
		&& (Tk_Display(tkwin) == bitmapPtr->display)) {
	    return bitmapPtr;
	}
	hashPtr = bitmapPtr->nameHashPtr;
	FreeBitmapObjProc(objPtr);
    } else {
	hashPtr = Tcl_FindHashEntry(&dispPtr->bitmapNameTable, 
                Tcl_GetString(objPtr));
	if (hashPtr == NULL) {
	    goto error;
	}
    } 

    /*
     * At this point we've got a hash table entry, off of which hang
     * one or more TkBitmap structures.  See if any of them will work.
     */

    for (bitmapPtr = (TkBitmap *) Tcl_GetHashValue(hashPtr);
	    bitmapPtr != NULL;  bitmapPtr = bitmapPtr->nextPtr) {
	if (Tk_Display(tkwin) == bitmapPtr->display) {
	    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) bitmapPtr;
	    bitmapPtr->objRefCount++;
	    return bitmapPtr;
	}
    }

    error:
    panic("GetBitmapFromObj called with non-existent bitmap!");
    /*
     * The following code isn't reached; it's just there to please compilers.
     */
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * InitBitmapObj --
 *
 *	Bookeeping procedure to change an objPtr to a bitmap type.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The old internal rep of the object is freed. The internal
 *	rep is cleared. The final form of the object is set
 *	by either Tk_AllocBitmapFromObj or GetBitmapFromObj.
 *
 *----------------------------------------------------------------------
 */

static void
InitBitmapObj(objPtr)
    Tcl_Obj *objPtr;		/* The object to convert. */
{
    Tcl_ObjType *typePtr;

    /*
     * Free the old internalRep before setting the new one. 
     */

    Tcl_GetString(objPtr);
    typePtr = objPtr->typePtr;
    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
	(*typePtr->freeIntRepProc)(objPtr);
    }
    objPtr->typePtr = &bitmapObjType;
    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * BitmapInit --
 *	Initializes hash tables used by this module.  Initializes 
 *      tables stored in TkDisplay structure if a TkDisplay pointer
 *      is passed in.  Iinitializes the thread-local data
 *      in the current thread's ThreadSpecificData structure.
 *
 * Results:
 *      None.
 *  
 * Side effects:
 *	Read the code.
 *
 *----------------------------------------------------------------------
 */

static void
BitmapInit(dispPtr)
    TkDisplay *dispPtr;         /* TkDisplay structure encapsulating 
				 * thread-specific data used by this 
				 * module, or NULL if unavailable. */
{
    Tcl_Interp *dummy;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /* 
     * First initialize the data in the ThreadSpecificData strucuture,
     * if needed.
     */

    if (!tsdPtr->initialized) {
	tsdPtr->initialized = 1;
        dummy = Tcl_CreateInterp();
	Tcl_InitHashTable(&tsdPtr->predefBitmapTable, TCL_STRING_KEYS);

        Tk_DefineBitmap(dummy, "error", (char *) error_bits,
		error_width, error_height);
        Tk_DefineBitmap(dummy, "gray75", (char *) gray75_bits,
                gray75_width, gray75_height);
        Tk_DefineBitmap(dummy, "gray50", (char *) gray50_bits,
                gray50_width, gray50_height);
        Tk_DefineBitmap(dummy, "gray25", (char *) gray25_bits,
                gray25_width, gray25_height);
        Tk_DefineBitmap(dummy, "gray12", (char *) gray12_bits,
                gray12_width, gray12_height);
        Tk_DefineBitmap(dummy, "hourglass", (char *) hourglass_bits,
                hourglass_width, hourglass_height);
        Tk_DefineBitmap(dummy, "info", (char *) info_bits,
	        info_width, info_height);
        Tk_DefineBitmap(dummy, "questhead", (char *) questhead_bits,
	        questhead_width, questhead_height);
        Tk_DefineBitmap(dummy, "question", (char *) question_bits,
	        question_width, question_height);
        Tk_DefineBitmap(dummy, "warning", (char *) warning_bits,
	        warning_width, warning_height);

        TkpDefineNativeBitmaps();
        Tcl_DeleteInterp(dummy);
    }

    /*
     * Was a valid TkDisplay pointer passed?  If so, initialize the
     * Bitmap module tables in that structure.
     */

    if (dispPtr != NULL) {
        dispPtr->bitmapInit = 1;
	Tcl_InitHashTable(&dispPtr->bitmapNameTable, TCL_STRING_KEYS);
	Tcl_InitHashTable(&dispPtr->bitmapDataTable, sizeof(DataKey)
                /sizeof(int));

	/*
	 * The call below is tricky:  can't use sizeof(IdKey) because it
	 * gets padded with extra unpredictable bytes on some 64-bit
	 * machines.
	 */

	/*
	 * The comment above doesn't make sense...
	 */
	Tcl_InitHashTable(&dispPtr->bitmapIdTable, TCL_ONE_WORD_KEYS);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkReadBitmapFile --
 *
 *	Loads a bitmap image in X bitmap format into the specified
 *	drawable.  This is equivelent to the XReadBitmapFile in X.
 *
 * Results:
 *	Sets the size, hotspot, and bitmap on success.
 *
 * Side effects:
 *	Creates a new bitmap from the file data.
 *
 *----------------------------------------------------------------------
 */

int
TkReadBitmapFile(display, d, filename, width_return, height_return,
	bitmap_return, x_hot_return, y_hot_return) 
    Display* display;
    Drawable d;
    CONST char* filename;
    unsigned int* width_return;
    unsigned int* height_return;
    Pixmap* bitmap_return;
    int* x_hot_return;
    int* y_hot_return;
{
    char *data;

    data = TkGetBitmapData(NULL, NULL, (char *) filename,
	    (int *) width_return, (int *) height_return, x_hot_return,
	    y_hot_return);
    if (data == NULL) {
	return BitmapFileInvalid;
    }

    *bitmap_return = XCreateBitmapFromData(display, d, data, *width_return,
	    *height_return);

    ckfree(data);
    return BitmapSuccess;
  }

/*
 *----------------------------------------------------------------------
 *
 * TkDebugBitmap --
 *
 *	This procedure returns debugging information about a bitmap.
 *
 * Results:
 *	The return value is a list with one sublist for each TkBitmap
 *	corresponding to "name".  Each sublist has two elements that
 *	contain the resourceRefCount and objRefCount fields from the
 *	TkBitmap structure.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TkDebugBitmap(tkwin, name)
    Tk_Window tkwin;		/* The window in which the bitmap will be
				 * used (not currently used). */
    char *name;			/* Name of the desired color. */
{
    TkBitmap *bitmapPtr;
    Tcl_HashEntry *hashPtr;
    Tcl_Obj *resultPtr, *objPtr;
    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;

    resultPtr = Tcl_NewObj();
    hashPtr = Tcl_FindHashEntry(&dispPtr->bitmapNameTable, name);
    if (hashPtr != NULL) {
	bitmapPtr = (TkBitmap *) Tcl_GetHashValue(hashPtr);
	if (bitmapPtr == NULL) {
	    panic("TkDebugBitmap found empty hash table entry");
	}
	for ( ; (bitmapPtr != NULL); bitmapPtr = bitmapPtr->nextPtr) {
	    objPtr = Tcl_NewObj();
	    Tcl_ListObjAppendElement(NULL, objPtr,
		    Tcl_NewIntObj(bitmapPtr->resourceRefCount));
	    Tcl_ListObjAppendElement(NULL, objPtr,
		    Tcl_NewIntObj(bitmapPtr->objRefCount)); 
	    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
	}
    }
    return resultPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * TkGetBitmapPredefTable --
 *      This procedure is used by tkMacBitmap.c to access the thread-
 *      specific predefBitmap table that maps from the names of 
 *      the predefined bitmaps to data associated with those 
 *      bitmaps.  It is required because the table is allocated in 
 *      thread-local storage and is not visible outside this file.

 * Results:
 *      Returns a pointer to the predefined bitmap hash table for 
 *      the current thread.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
Tcl_HashTable *
TkGetBitmapPredefTable()
{
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    return &tsdPtr->predefBitmapTable;
}
