/*1* tkBitmap.c --2*3* This file maintains a database of read-only bitmaps for the Tk4* toolkit. This allows bitmaps to be shared between widgets and5* also avoids interactions with the X server.6*7* Copyright (c) 1990-1994 The Regents of the University of California.8* Copyright (c) 1994-1995 Sun Microsystems, Inc.9*10* See the file "license.terms" for information on usage and redistribution11* of this file, and for a DISCLAIMER OF ALL WARRANTIES.12*13* SCCS: @(#) tkBitmap.c 1.37 96/07/23 16:54:4014*/1516#include "tkInt.h"1718/*19* The includes below are for pre-defined bitmaps.20*/2122#include "error.bmp"23#include "gray12.bmp"24#include "gray25.bmp"25#include "gray50.bmp"26#include "hourglass.bmp"27#include "info.bmp"28#include "questhead.bmp"29#include "question.bmp"30#include "warning.bmp"3132/*33* One of the following data structures exists for each bitmap that is34* currently in use. Each structure is indexed with both "idTable" and35* "nameTable".36*/3738typedef struct {39Pixmap bitmap; /* X identifier for bitmap. None means this40* bitmap was created by Tk_DefineBitmap41* and it isn't currently in use. */42int width, height; /* Dimensions of bitmap. */43Display *display; /* Display for which bitmap is valid. */44int refCount; /* Number of active uses of bitmap. */45Tcl_HashEntry *hashPtr; /* Entry in nameTable for this structure46* (needed when deleting). */47} TkBitmap;4849/*50* Hash table to map from a textual description of a bitmap to the51* TkBitmap record for the bitmap, and key structure used in that52* hash table:53*/5455static Tcl_HashTable nameTable;56typedef struct {57Tk_Uid name; /* Textual name for desired bitmap. */58Screen *screen; /* Screen on which bitmap will be used. */59} NameKey;6061/*62* Hash table that maps from <display + bitmap id> to the TkBitmap structure63* for the bitmap. This table is used by Tk_FreeBitmap.64*/6566static Tcl_HashTable idTable;67typedef struct {68Display *display; /* Display for which bitmap was allocated. */69Pixmap pixmap; /* X identifier for pixmap. */70} IdKey;7172/*73* For each call to Tk_DefineBitmap one of the following structures is74* created to hold information about the bitmap.75*/7677typedef struct {78char *source; /* Bits for bitmap. */79int width, height; /* Dimensions of bitmap. */80} PredefBitmap;8182/*83* Hash table create by Tk_DefineBitmap to map from a name to a84* collection of in-core data about a bitmap. The table is85* indexed by the address of the data for the bitmap, and the entries86* contain pointers to PredefBitmap structures.87*/8889static Tcl_HashTable predefTable;9091/*92* Hash table used by Tk_GetBitmapFromData to map from a collection93* of in-core data about a bitmap to a Tk_Uid giving an automatically-94* generated name for the bitmap:95*/9697static Tcl_HashTable dataTable;98typedef struct {99char *source; /* Bitmap bits. */100int width, height; /* Dimensions of bitmap. */101} DataKey;102103static int initialized = 0; /* 0 means static structures haven't been104* initialized yet. */105106/*107* Forward declarations for procedures defined in this file:108*/109110static void BitmapInit _ANSI_ARGS_((void));111112/*113*----------------------------------------------------------------------114*115* Tk_GetBitmap --116*117* Given a string describing a bitmap, locate (or create if necessary)118* a bitmap that fits the description.119*120* Results:121* The return value is the X identifer for the desired bitmap122* (i.e. a Pixmap with a single plane), unless string couldn't be123* parsed correctly. In this case, None is returned and an error124* message is left in interp->result. The caller should never125* modify the bitmap that is returned, and should eventually call126* Tk_FreeBitmap when the bitmap is no longer needed.127*128* Side effects:129* The bitmap is added to an internal database with a reference count.130* For each call to this procedure, there should eventually be a call131* to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps132* aren't needed anymore.133*134*----------------------------------------------------------------------135*/136137Pixmap138Tk_GetBitmap(interp, tkwin, string)139Tcl_Interp *interp; /* Interpreter to use for error reporting. */140Tk_Window tkwin; /* Window in which bitmap will be used. */141Tk_Uid string; /* Description of bitmap. See manual entry142* for details on legal syntax. */143{144NameKey nameKey;145IdKey idKey;146Tcl_HashEntry *nameHashPtr, *idHashPtr, *predefHashPtr;147register TkBitmap *bitmapPtr;148PredefBitmap *predefPtr;149int new;150Pixmap bitmap;151int width, height;152int dummy2;153154if (!initialized) {155BitmapInit();156}157158nameKey.name = string;159nameKey.screen = Tk_Screen(tkwin);160nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);161if (!new) {162bitmapPtr = (TkBitmap *) Tcl_GetHashValue(nameHashPtr);163bitmapPtr->refCount++;164return bitmapPtr->bitmap;165}166167/*168* No suitable bitmap exists. Create a new bitmap from the169* information contained in the string. If the string starts170* with "@" then the rest of the string is a file name containing171* the bitmap. Otherwise the string must refer to a bitmap172* defined by a call to Tk_DefineBitmap.173*/174175if (*string == '@') {176Tcl_DString buffer;177int result;178179string = Tcl_TranslateFileName(interp, string + 1, &buffer);180if (string == NULL) {181goto error;182}183result = XReadBitmapFile(Tk_Display(tkwin),184RootWindowOfScreen(nameKey.screen), string,185(unsigned int *) &width, (unsigned int *) &height,186&bitmap, &dummy2, &dummy2);187Tcl_DStringFree(&buffer);188if (result != BitmapSuccess) {189Tcl_AppendResult(interp, "error reading bitmap file \"", string,190"\"", (char *) NULL);191goto error;192}193} else {194predefHashPtr = Tcl_FindHashEntry(&predefTable, string);195if (predefHashPtr == NULL) {196/*197* The check for a NULL interpreter is a special hack that198* allows this procedure to be called from GetShadows in199* tk3d.c, where it doesn't have an intepreter handle.200*/201202if (interp != NULL) {203Tcl_AppendResult(interp, "bitmap \"", string,204"\" not defined", (char *) NULL);205}206goto error;207}208predefPtr = (PredefBitmap *) Tcl_GetHashValue(predefHashPtr);209width = predefPtr->width;210height = predefPtr->height;211bitmap = XCreateBitmapFromData(Tk_Display(tkwin),212RootWindowOfScreen(nameKey.screen), predefPtr->source,213(unsigned) width, (unsigned) height);214}215216/*217* Add information about this bitmap to our database.218*/219220bitmapPtr = (TkBitmap *) ckalloc(sizeof(TkBitmap));221bitmapPtr->bitmap = bitmap;222bitmapPtr->width = width;223bitmapPtr->height = height;224bitmapPtr->display = Tk_Display(tkwin);225bitmapPtr->refCount = 1;226bitmapPtr->hashPtr = nameHashPtr;227idKey.display = bitmapPtr->display;228idKey.pixmap = bitmap;229idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) &idKey,230&new);231if (!new) {232panic("bitmap already registered in Tk_GetBitmap");233}234Tcl_SetHashValue(nameHashPtr, bitmapPtr);235Tcl_SetHashValue(idHashPtr, bitmapPtr);236return bitmapPtr->bitmap;237238error:239Tcl_DeleteHashEntry(nameHashPtr);240return None;241}242243/*244*----------------------------------------------------------------------245*246* Tk_DefineBitmap --247*248* This procedure associates a textual name with a binary bitmap249* description, so that the name may be used to refer to the250* bitmap in future calls to Tk_GetBitmap.251*252* Results:253* A standard Tcl result. If an error occurs then TCL_ERROR is254* returned and a message is left in interp->result.255*256* Side effects:257* "Name" is entered into the bitmap table and may be used from258* here on to refer to the given bitmap.259*260*----------------------------------------------------------------------261*/262263int264Tk_DefineBitmap(interp, name, source, width, height)265Tcl_Interp *interp; /* Interpreter to use for error reporting. */266Tk_Uid name; /* Name to use for bitmap. Must not already267* be defined as a bitmap. */268char *source; /* Address of bits for bitmap. */269int width; /* Width of bitmap. */270int height; /* Height of bitmap. */271{272int new;273Tcl_HashEntry *predefHashPtr;274PredefBitmap *predefPtr;275276if (!initialized) {277BitmapInit();278}279280predefHashPtr = Tcl_CreateHashEntry(&predefTable, name, &new);281if (!new) {282Tcl_AppendResult(interp, "bitmap \"", name,283"\" is already defined", (char *) NULL);284return TCL_ERROR;285}286predefPtr = (PredefBitmap *) ckalloc(sizeof(PredefBitmap));287predefPtr->source = source;288predefPtr->width = width;289predefPtr->height = height;290Tcl_SetHashValue(predefHashPtr, predefPtr);291return TCL_OK;292}293294/*295*--------------------------------------------------------------296*297* Tk_NameOfBitmap --298*299* Given a bitmap, return a textual string identifying the300* bitmap.301*302* Results:303* The return value is the string name associated with bitmap.304*305* Side effects:306* None.307*308*--------------------------------------------------------------309*/310311Tk_Uid312Tk_NameOfBitmap(display, bitmap)313Display *display; /* Display for which bitmap was314* allocated. */315Pixmap bitmap; /* Bitmap whose name is wanted. */316{317IdKey idKey;318Tcl_HashEntry *idHashPtr;319TkBitmap *bitmapPtr;320void *ptr;321322if (!initialized) {323unknown:324panic("Tk_NameOfBitmap received unknown bitmap argument");325}326327idKey.display = display;328idKey.pixmap = bitmap;329idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);330if (idHashPtr == NULL) {331goto unknown;332}333bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);334ptr = bitmapPtr->hashPtr->key.words;335return ((NameKey *) ptr)->name;336}337338/*339*--------------------------------------------------------------340*341* Tk_SizeOfBitmap --342*343* Given a bitmap managed by this module, returns the width344* and height of the bitmap.345*346* Results:347* The words at *widthPtr and *heightPtr are filled in with348* the dimenstions of bitmap.349*350* Side effects:351* If bitmap isn't managed by this module then the procedure352* panics..353*354*--------------------------------------------------------------355*/356357void358Tk_SizeOfBitmap(display, bitmap, widthPtr, heightPtr)359Display *display; /* Display for which bitmap was360* allocated. */361Pixmap bitmap; /* Bitmap whose size is wanted. */362int *widthPtr; /* Store bitmap width here. */363int *heightPtr; /* Store bitmap height here. */364{365IdKey idKey;366Tcl_HashEntry *idHashPtr;367TkBitmap *bitmapPtr;368369if (!initialized) {370unknownBitmap:371panic("Tk_SizeOfBitmap received unknown bitmap argument");372}373374idKey.display = display;375idKey.pixmap = bitmap;376idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);377if (idHashPtr == NULL) {378goto unknownBitmap;379}380bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);381*widthPtr = bitmapPtr->width;382*heightPtr = bitmapPtr->height;383}384385/*386*----------------------------------------------------------------------387*388* Tk_FreeBitmap --389*390* This procedure is called to release a bitmap allocated by391* Tk_GetBitmap or TkGetBitmapFromData.392*393* Results:394* None.395*396* Side effects:397* The reference count associated with bitmap is decremented, and398* it is officially deallocated if no-one is using it anymore.399*400*----------------------------------------------------------------------401*/402403void404Tk_FreeBitmap(display, bitmap)405Display *display; /* Display for which bitmap was406* allocated. */407Pixmap bitmap; /* Bitmap to be released. */408{409Tcl_HashEntry *idHashPtr;410register TkBitmap *bitmapPtr;411IdKey idKey;412413if (!initialized) {414panic("Tk_FreeBitmap called before Tk_GetBitmap");415}416417idKey.display = display;418idKey.pixmap = bitmap;419idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);420if (idHashPtr == NULL) {421panic("Tk_FreeBitmap received unknown bitmap argument");422}423bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);424bitmapPtr->refCount--;425if (bitmapPtr->refCount == 0) {426Tk_FreePixmap(bitmapPtr->display, bitmapPtr->bitmap);427Tcl_DeleteHashEntry(idHashPtr);428Tcl_DeleteHashEntry(bitmapPtr->hashPtr);429ckfree((char *) bitmapPtr);430}431}432433/*434*----------------------------------------------------------------------435*436* Tk_GetBitmapFromData --437*438* Given a description of the bits for a bitmap, make a bitmap that439* has the given properties. *** NOTE: this procedure is obsolete440* and really shouldn't be used anymore. ***441*442* Results:443* The return value is the X identifer for the desired bitmap444* (a one-plane Pixmap), unless it couldn't be created properly.445* In this case, None is returned and an error message is left in446* interp->result. The caller should never modify the bitmap that447* is returned, and should eventually call Tk_FreeBitmap when the448* bitmap is no longer needed.449*450* Side effects:451* The bitmap is added to an internal database with a reference count.452* For each call to this procedure, there should eventually be a call453* to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps454* aren't needed anymore.455*456*----------------------------------------------------------------------457*/458459/* ARGSUSED */460Pixmap461Tk_GetBitmapFromData(interp, tkwin, source, width, height)462Tcl_Interp *interp; /* Interpreter to use for error reporting. */463Tk_Window tkwin; /* Window in which bitmap will be used. */464char *source; /* Bitmap data for bitmap shape. */465int width, height; /* Dimensions of bitmap. */466{467DataKey nameKey;468Tcl_HashEntry *dataHashPtr;469Tk_Uid name = NULL; /* Initialization need only to prevent470* compiler warning. */471int new;472static int autoNumber = 0;473char string[20];474475if (!initialized) {476BitmapInit();477}478479nameKey.source = source;480nameKey.width = width;481nameKey.height = height;482dataHashPtr = Tcl_CreateHashEntry(&dataTable, (char *) &nameKey, &new);483if (!new) {484name = (Tk_Uid) Tcl_GetHashValue(dataHashPtr);485} else {486autoNumber++;487sprintf(string, "_tk%d", autoNumber);488name = Tk_GetUid(string);489Tcl_SetHashValue(dataHashPtr, name);490if (Tk_DefineBitmap(interp, name, source, width, height) != TCL_OK) {491Tcl_DeleteHashEntry(dataHashPtr);492return TCL_ERROR;493}494}495return Tk_GetBitmap(interp, tkwin, name);496}497498/*499*----------------------------------------------------------------------500*501* BitmapInit --502*503* Initialize the structures used for bitmap management.504*505* Results:506* None.507*508* Side effects:509* Read the code.510*511*----------------------------------------------------------------------512*/513514static void515BitmapInit()516{517Tcl_Interp *dummy;518519dummy = Tcl_CreateInterp();520initialized = 1;521Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));522Tcl_InitHashTable(&dataTable, sizeof(DataKey)/sizeof(int));523Tcl_InitHashTable(&predefTable, TCL_ONE_WORD_KEYS);524525/*526* The call below is tricky: can't use sizeof(IdKey) because it527* gets padded with extra unpredictable bytes on some 64-bit528* machines.529*/530531Tcl_InitHashTable(&idTable, (sizeof(Display *) + sizeof(Pixmap))532/sizeof(int));533534Tk_DefineBitmap(dummy, Tk_GetUid("error"), (char *) error_bits,535error_width, error_height);536Tk_DefineBitmap(dummy, Tk_GetUid("gray50"), (char *) gray50_bits,537gray50_width, gray50_height);538Tk_DefineBitmap(dummy, Tk_GetUid("gray25"), (char *) gray25_bits,539gray25_width, gray25_height);540Tk_DefineBitmap(dummy, Tk_GetUid("gray12"), (char *) gray12_bits,541gray12_width, gray12_height);542Tk_DefineBitmap(dummy, Tk_GetUid("hourglass"), (char *) hourglass_bits,543hourglass_width, hourglass_height);544Tk_DefineBitmap(dummy, Tk_GetUid("info"), (char *) info_bits,545info_width, info_height);546Tk_DefineBitmap(dummy, Tk_GetUid("questhead"), (char *) questhead_bits,547questhead_width, questhead_height);548Tk_DefineBitmap(dummy, Tk_GetUid("question"), (char *) question_bits,549question_width, question_height);550Tk_DefineBitmap(dummy, Tk_GetUid("warning"), (char *) warning_bits,551warning_width, warning_height);552Tcl_DeleteInterp(dummy);553}554555556