/*1* tkCursor.c --2*3* This file maintains a database of read-only cursors for the Tk4* toolkit. This allows cursors to be shared between widgets and5* also avoids round-trips to 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: @(#) tkCursor.c 1.27 96/02/15 18:52:4014*/1516#include "tkInt.h"1718/*19* A TkCursor structure exists for each cursor that is currently20* active. Each structure is indexed with two hash tables defined21* below. One of the tables is idTable, and the other is either22* nameTable or dataTable, also defined below.23*/2425/*26* Hash table to map from a textual description of a cursor to the27* TkCursor record for the cursor, and key structure used in that28* hash table:29*/3031static Tcl_HashTable nameTable;32typedef struct {33Tk_Uid name; /* Textual name for desired cursor. */34Display *display; /* Display for which cursor will be used. */35} NameKey;3637/*38* Hash table to map from a collection of in-core data about a39* cursor (bitmap contents, etc.) to a TkCursor structure:40*/4142static Tcl_HashTable dataTable;43typedef struct {44char *source; /* Cursor bits. */45char *mask; /* Mask bits. */46int width, height; /* Dimensions of cursor (and data47* and mask). */48int xHot, yHot; /* Location of cursor hot-spot. */49Tk_Uid fg, bg; /* Colors for cursor. */50Display *display; /* Display on which cursor will be used. */51} DataKey;5253/*54* Hash table that maps from <display + cursor id> to the TkCursor structure55* for the cursor. This table is used by Tk_FreeCursor.56*/5758static Tcl_HashTable idTable;59typedef struct {60Display *display; /* Display for which cursor was allocated. */61Tk_Cursor cursor; /* Cursor identifier. */62} IdKey;6364static int initialized = 0; /* 0 means static structures haven't been65* initialized yet. */6667/*68* Forward declarations for procedures defined in this file:69*/7071static void CursorInit _ANSI_ARGS_((void));7273/*74*----------------------------------------------------------------------75*76* Tk_GetCursor --77*78* Given a string describing a cursor, locate (or create if necessary)79* a cursor that fits the description.80*81* Results:82* The return value is the X identifer for the desired cursor,83* unless string couldn't be parsed correctly. In this case,84* None is returned and an error message is left in interp->result.85* The caller should never modify the cursor that is returned, and86* should eventually call Tk_FreeCursor when the cursor is no longer87* needed.88*89* Side effects:90* The cursor is added to an internal database with a reference count.91* For each call to this procedure, there should eventually be a call92* to Tk_FreeCursor, so that the database can be cleaned up when cursors93* aren't needed anymore.94*95*----------------------------------------------------------------------96*/9798Tk_Cursor99Tk_GetCursor(interp, tkwin, string)100Tcl_Interp *interp; /* Interpreter to use for error reporting. */101Tk_Window tkwin; /* Window in which cursor will be used. */102Tk_Uid string; /* Description of cursor. See manual entry103* for details on legal syntax. */104{105NameKey nameKey;106IdKey idKey;107Tcl_HashEntry *nameHashPtr, *idHashPtr;108register TkCursor *cursorPtr;109int new;110111if (!initialized) {112CursorInit();113}114115nameKey.name = string;116nameKey.display = Tk_Display(tkwin);117nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);118if (!new) {119cursorPtr = (TkCursor *) Tcl_GetHashValue(nameHashPtr);120cursorPtr->refCount++;121return cursorPtr->cursor;122}123124cursorPtr = TkGetCursorByName(interp, tkwin, string);125126if (cursorPtr == NULL) {127Tcl_DeleteHashEntry(nameHashPtr);128return None;129}130131/*132* Add information about this cursor to our database.133*/134135cursorPtr->refCount = 1;136cursorPtr->otherTable = &nameTable;137cursorPtr->hashPtr = nameHashPtr;138idKey.display = nameKey.display;139idKey.cursor = cursorPtr->cursor;140idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) &idKey, &new);141if (!new) {142panic("cursor already registered in Tk_GetCursor");143}144Tcl_SetHashValue(nameHashPtr, cursorPtr);145Tcl_SetHashValue(idHashPtr, cursorPtr);146147return cursorPtr->cursor;148}149150/*151*----------------------------------------------------------------------152*153* Tk_GetCursorFromData --154*155* Given a description of the bits and colors for a cursor,156* make a cursor that has the given properties.157*158* Results:159* The return value is the X identifer for the desired cursor,160* unless it couldn't be created properly. In this case, None is161* returned and an error message is left in interp->result. The162* caller should never modify the cursor that is returned, and163* should eventually call Tk_FreeCursor when the cursor is no164* longer needed.165*166* Side effects:167* The cursor is added to an internal database with a reference count.168* For each call to this procedure, there should eventually be a call169* to Tk_FreeCursor, so that the database can be cleaned up when cursors170* aren't needed anymore.171*172*----------------------------------------------------------------------173*/174175Tk_Cursor176Tk_GetCursorFromData(interp, tkwin, source, mask, width, height,177xHot, yHot, fg, bg)178Tcl_Interp *interp; /* Interpreter to use for error reporting. */179Tk_Window tkwin; /* Window in which cursor will be used. */180char *source; /* Bitmap data for cursor shape. */181char *mask; /* Bitmap data for cursor mask. */182int width, height; /* Dimensions of cursor. */183int xHot, yHot; /* Location of hot-spot in cursor. */184Tk_Uid fg; /* Foreground color for cursor. */185Tk_Uid bg; /* Background color for cursor. */186{187DataKey dataKey;188IdKey idKey;189Tcl_HashEntry *dataHashPtr, *idHashPtr;190register TkCursor *cursorPtr;191int new;192XColor fgColor, bgColor;193194if (!initialized) {195CursorInit();196}197198dataKey.source = source;199dataKey.mask = mask;200dataKey.width = width;201dataKey.height = height;202dataKey.xHot = xHot;203dataKey.yHot = yHot;204dataKey.fg = fg;205dataKey.bg = bg;206dataKey.display = Tk_Display(tkwin);207dataHashPtr = Tcl_CreateHashEntry(&dataTable, (char *) &dataKey, &new);208if (!new) {209cursorPtr = (TkCursor *) Tcl_GetHashValue(dataHashPtr);210cursorPtr->refCount++;211return cursorPtr->cursor;212}213214/*215* No suitable cursor exists yet. Make one using the data216* available and add it to the database.217*/218219if (XParseColor(dataKey.display, Tk_Colormap(tkwin), fg, &fgColor) == 0) {220Tcl_AppendResult(interp, "invalid color name \"", fg, "\"",221(char *) NULL);222goto error;223}224if (XParseColor(dataKey.display, Tk_Colormap(tkwin), bg, &bgColor) == 0) {225Tcl_AppendResult(interp, "invalid color name \"", bg, "\"",226(char *) NULL);227goto error;228}229230cursorPtr = TkCreateCursorFromData(tkwin, source, mask, width, height,231xHot, yHot, fgColor, bgColor);232233if (cursorPtr == NULL) {234goto error;235}236237cursorPtr->refCount = 1;238cursorPtr->otherTable = &dataTable;239cursorPtr->hashPtr = dataHashPtr;240idKey.display = dataKey.display;241idKey.cursor = cursorPtr->cursor;242idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) &idKey, &new);243if (!new) {244panic("cursor already registered in Tk_GetCursorFromData");245}246Tcl_SetHashValue(dataHashPtr, cursorPtr);247Tcl_SetHashValue(idHashPtr, cursorPtr);248return cursorPtr->cursor;249250error:251Tcl_DeleteHashEntry(dataHashPtr);252return None;253}254255/*256*--------------------------------------------------------------257*258* Tk_NameOfCursor --259*260* Given a cursor, return a textual string identifying it.261*262* Results:263* If cursor was created by Tk_GetCursor, then the return264* value is the "string" that was used to create it.265* Otherwise the return value is a string giving the X266* identifier for the cursor. The storage for the returned267* string is only guaranteed to persist up until the next268* call to this procedure.269*270* Side effects:271* None.272*273*--------------------------------------------------------------274*/275276char *277Tk_NameOfCursor(display, cursor)278Display *display; /* Display for which cursor was allocated. */279Tk_Cursor cursor; /* Identifier for cursor whose name is280* wanted. */281{282IdKey idKey;283Tcl_HashEntry *idHashPtr;284TkCursor *cursorPtr;285void *ptr;286static char string[20];287288if (!initialized) {289printid:290sprintf(string, "cursor id %p", cursor);291return string;292}293idKey.display = display;294idKey.cursor = cursor;295idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);296if (idHashPtr == NULL) {297goto printid;298}299cursorPtr = (TkCursor *) Tcl_GetHashValue(idHashPtr);300if (cursorPtr->otherTable != &nameTable) {301goto printid;302}303ptr = cursorPtr->hashPtr->key.words;304return ((NameKey *) ptr)->name;305}306307/*308*----------------------------------------------------------------------309*310* Tk_FreeCursor --311*312* This procedure is called to release a cursor allocated by313* Tk_GetCursor or TkGetCursorFromData.314*315* Results:316* None.317*318* Side effects:319* The reference count associated with cursor is decremented, and320* it is officially deallocated if no-one is using it anymore.321*322*----------------------------------------------------------------------323*/324325void326Tk_FreeCursor(display, cursor)327Display *display; /* Display for which cursor was allocated. */328Tk_Cursor cursor; /* Identifier for cursor to be released. */329{330IdKey idKey;331Tcl_HashEntry *idHashPtr;332register TkCursor *cursorPtr;333334if (!initialized) {335panic("Tk_FreeCursor called before Tk_GetCursor");336}337338idKey.display = display;339idKey.cursor = cursor;340idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);341if (idHashPtr == NULL) {342panic("Tk_FreeCursor received unknown cursor argument");343}344cursorPtr = (TkCursor *) Tcl_GetHashValue(idHashPtr);345cursorPtr->refCount--;346if (cursorPtr->refCount == 0) {347Tcl_DeleteHashEntry(cursorPtr->hashPtr);348Tcl_DeleteHashEntry(idHashPtr);349TkFreeCursor(cursorPtr);350}351}352353/*354*----------------------------------------------------------------------355*356* CursorInit --357*358* Initialize the structures used for cursor management.359*360* Results:361* None.362*363* Side effects:364* Read the code.365*366*----------------------------------------------------------------------367*/368369static void370CursorInit()371{372initialized = 1;373Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));374Tcl_InitHashTable(&dataTable, sizeof(DataKey)/sizeof(int));375376/*377* The call below is tricky: can't use sizeof(IdKey) because it378* gets padded with extra unpredictable bytes on some 64-bit379* machines.380*/381382Tcl_InitHashTable(&idTable, (sizeof(Display *) + sizeof(Tk_Cursor))383/sizeof(int));384}385386387