/* grTkCommon.c * * Functions common to all graphics routines running under Tcl/Tk * * Copyright 2003 Open Circuit Design, Inc., for MultiGiG Ltd. */ /* We should be here if this is not set! */ #ifdef MAGIC_WRAPPER #include #include #include #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/main.h" #include "utils/styles.h" #include "utils/geometry.h" #include "utils/hash.h" #include "utils/malloc.h" #include "utils/utils.h" #include "tiles/tile.h" #include "database/database.h" #include "windows/windows.h" #include "dbwind/dbwtech.h" #include "utils/styles.h" #include "graphics/graphics.h" #include "graphics/graphicsInt.h" #include "dbwind/dbwind.h" #include "graphics/grTkCommon.h" #include "graphics/glyphs.h" #include "textio/textio.h" /* Variables declared by grClip.c */ extern int grCurFill, grCurOutline, grCurColor; extern void grInformDriver(); /* Global variables used by both the Tk and TOGL interfaces */ /* should be defined here. */ Display *grXdpy; int grXscrn; Tk_Cursor grCursors[MAX_CURSORS]; Tk_Font grTkFonts[4]; /* Used by Tk interface. Defined for both Tk and TOGL, but */ /* unused by the TOGL interface. */ bool GrTkInstalledCMap = FALSE; /*--------------------------------------------------------- * grTkLoadFont * This local routine loads the Tk fonts used by Magic. * * Results: * Success/Failure. * * Side Effects: * Font information saved in grTkFonts array. *--------------------------------------------------------- */ bool grTkLoadFont() { Tk_Window tkwind; int i; char *s; char *unable = "Unable to load font"; static char *fontnames[4] = { TK_FONT_SMALL, TK_FONT_MEDIUM, TK_FONT_LARGE, TK_FONT_XLARGE }; static char *optionnames[4] = { "small", "medium", "large", "xlarge"}; tkwind = Tk_MainWindow(magicinterp); for (i = 0; i < 4; i++) { s = XGetDefault(grXdpy, "magic", optionnames[i]); if (s) fontnames[i] = s; if ((grTkFonts[i] = Tk_GetFont(magicinterp, tkwind, fontnames[i])) == NULL) { TxError("%s %s\n", unable, fontnames[i]); if ((grTkFonts[i] = Tk_GetFont(magicinterp, tkwind, TK_DEFAULT_FONT)) == NULL) { TxError("%s %s\n", unable, TK_DEFAULT_FONT); return FALSE; } } } return TRUE; } /*--------------------------------------------------------- * grTkFreeFonts * This local routine frees the Tk font cache. * * Results: * None. * * Side Effects: * Memory deallocation. *--------------------------------------------------------- */ void grTkFreeFonts() { int i; for (i = 0; i < 4; i++) Tk_FreeFont(grTkFonts[i]); } /* * ---------------------------------------------------------------------------- * grTkFreeCursors: * * Remove cursors from the Tk cursor cache. * * Results: * None. * * Side effects: * Tk forgets about the glyph cursors. * ---------------------------------------------------------------------------- */ void grTkFreeCursors(glyphs) GrGlyphs *glyphs; { int i; for (i = 0; i < glyphs->gr_num; i++) Tk_FreeCursor(grXdpy, grCursors[i]); } /* * ---------------------------------------------------------------------------- * grTkDefineCursor: * * Define a new set of cursors. Use Tk-style cursors (portable) * * Results: * None. * * Side effects: * The given matrix is stored in the graphics display, and it can be * used as the cursor by calling GrSetCursor. * ---------------------------------------------------------------------------- */ typedef struct { unsigned char source[32]; unsigned char mask[32]; } CursorCache; void grTkDefineCursor(glyphs) GrGlyphs *glyphs; { char *fgname, *bgname; int glyphnum; Rect oldClip; int red, green, blue; Tk_Window tkwin; bool fg_needs_free; if (glyphs->gr_num <= 0) return; if (glyphs->gr_num > MAX_CURSORS) { TxError("magic/Tk only has room for %d cursors\n", MAX_CURSORS); return; } tkwin = Tk_MainWindow(magicinterp); /* expand clipping amount for off-screen access on the X */ GrLock(GR_LOCK_SCREEN, FALSE); oldClip = grCurClip; grCurClip = GrScreenRect; grCurClip.r_ytop += 16; /* enter the glyphs */ for (glyphnum = 0; glyphnum < glyphs->gr_num; glyphnum++) { int i, *p, fgstyle; XColor curcolor; GrGlyph *g; int x, y; CursorCache *glyphcache; g = glyphs->gr_glyph[glyphnum]; if ((g->gr_xsize != 16) || (g->gr_ysize != 16)) { TxError("Tk/OpenGL Cursors must be 16 X 16 pixels.\n"); return; } glyphcache = (CursorCache *)mallocMagic(sizeof(CursorCache)); g->gr_cache = (ClientData)glyphcache; g->gr_free = freeMagic; /* Find the foreground and background colors of the glyph */ p = &(g->gr_pixels[0]); fgstyle = STYLE_TRANSPARENT; fg_needs_free = FALSE; for (x = 0; x < 256; x++) { if (*p != STYLE_TRANSPARENT) { fgstyle = *p; GrGetColor(GrStyleTable[*p].color, &red, &green, &blue); curcolor.red = (unsigned short)(red << 8); curcolor.green = (unsigned short)(green << 8); curcolor.blue = (unsigned short)(blue << 8); curcolor.flags = DoRed | DoGreen | DoBlue; fgname = (char *)Tk_NameOfColor(Tk_GetColorByValue(tkwin, &curcolor)); break; } p++; } if (x == 256) fgname = "black"; for (; x < 256; x++) { if ((*p != STYLE_TRANSPARENT) && (*p != fgstyle)) { GrGetColor(GrStyleTable[*p].color, &red, &green, &blue); curcolor.red = (unsigned short)(red << 8); curcolor.green = (unsigned short)(green << 8); curcolor.blue = (unsigned short)(blue << 8); curcolor.flags = DoRed | DoGreen | DoBlue; bgname = StrDup((char **)NULL, fgname); fgname = bgname; fg_needs_free = TRUE; bgname = (char *)Tk_NameOfColor(Tk_GetColorByValue(tkwin, &curcolor)); break; } p++; } if (x >= 256) bgname = "white"; /* Perform transposition on the glyph matrix since X displays * the least significant bit on the left hand side. */ p = &(g->gr_pixels[0]); for (y = 0; y < 32; y++) { i = (y & 1) ? (32 - y) : (30 - y); glyphcache->source[i] = glyphcache->mask[i] = 0; for (x = 0; x < 8; x++) { if (*p == fgstyle) glyphcache->source[i] |= (1 << x); if (*p != STYLE_TRANSPARENT) glyphcache->mask[i] |= (1 << x); p++; } } grCursors[glyphnum] = Tk_GetCursorFromData(magicinterp, Tk_MainWindow(magicinterp), (char *)glyphcache->source, (char *)glyphcache->mask, 16, 16, g->gr_origin.p_x, (15 - g->gr_origin.p_y), Tk_GetUid(fgname), Tk_GetUid(bgname)); if (fg_needs_free) freeMagic(fgname); } /* Restore clipping */ grCurClip = oldClip; GrUnlock(GR_LOCK_SCREEN); } /* *------------------------------------------------------------------------- * GrTkWindowName -- * Get the window name from the indicated MagWindow structure * * Results: * The Tk path name of the window "mw" (char * string). * * Side Effects: * None. *------------------------------------------------------------------------- */ char * GrTkWindowName(mw) MagWindow *mw; { Tk_Window tkwind; char *tkname; tkwind = (Tk_Window) mw->w_grdata; return Tk_PathName(tkwind); } /* * ---------------------------------------------------------------------------- * grtkFreeBackingStore -- * Free up Pixmap memory for a backing store cell. * * Results: * None. * * Side effects: * memory Free'd * ---------------------------------------------------------------------------- */ void grtkFreeBackingStore(MagWindow *window) { Pixmap pmap = (Pixmap)window->w_backingStore; if (pmap == (Pixmap)NULL) return; XFreePixmap(grXdpy, pmap); window->w_backingStore = (ClientData)NULL; /* TxPrintf("grtkFreeBackingStore called\n"); */ } /* * ---------------------------------------------------------------------------- * grtkCreateBackingStore -- * Create Pixmap memory for a backing store cell and copy data * from the window into it. * * Results: * None. * * Side effects: * memory Allocated. * * ---------------------------------------------------------------------------- */ void grtkCreateBackingStore(MagWindow *w) { Pixmap pmap; Tk_Window tkwind = (Tk_Window)w->w_grdata; Window wind; unsigned int width, height; /* ignore all windows other than layout */ if (w->w_client != DBWclientID) return; /* deferred */ if (tkwind == NULL) return; wind = Tk_WindowId(tkwind); width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot; height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot; if (w->w_backingStore != (ClientData)NULL) grtkFreeBackingStore(w); pmap = XCreatePixmap(grXdpy, wind, width, height, Tk_Depth(tkwind)); w->w_backingStore = (ClientData)pmap; /* TxPrintf("grtkCreateBackingStore area %d %d %d %d\n", w->w_screenArea.r_xbot, w->w_screenArea.r_ybot, w->w_screenArea.r_xtop, w->w_screenArea.r_ytop); */ } /* * ---------------------------------------------------------------------------- * grtkGetBackingStore -- * Copy data from a backing store Pixmap into the indicated window. * * Results: * TRUE if backing store was copied successfully, FALSE if not. * * Side effects: * Data copied into Pixmap memory. * * ---------------------------------------------------------------------------- */ bool grtkGetBackingStore(MagWindow *w, Rect *area) { Pixmap pmap; Tk_Window tkwind = (Tk_Window)w->w_grdata; Window wind = Tk_WindowId(tkwind); unsigned int width, height; int ybot; int xoff, yoff; GC gc; XGCValues gcValues; Rect r; pmap = (Pixmap)w->w_backingStore; if (pmap == (Pixmap)NULL) return FALSE; gcValues.graphics_exposures = FALSE; gc = Tk_GetGC(tkwind, GCGraphicsExposures, &gcValues); /* Make a local copy of area so we don't disturb the */ /* original. Expand by one pixel to deal with different */ /* boundary conditions between X11 and OpenGL. The redraw */ /* mechanism allows for at least this much slop. */ GEO_EXPAND(area, 1, &r); GeoClip(&r, &(w->w_screenArea)); width = r.r_xtop - r.r_xbot; height = r.r_ytop - r.r_ybot; ybot = grXtransY(w, r.r_ytop); xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot; yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop; XCopyArea(grXdpy, pmap, wind, gc, r.r_xbot - xoff, ybot - yoff, width, height, r.r_xbot, ybot); /* This is really only necessary for OpenGL, to avoid event */ /* timing conflicts between OpenGL calls and XCopyArea. */ /* It does not make sense for this to come AFTER the */ /* XCopyArea() command. Yet that is what works. So I'm */ /* sure that I do not entirely understand the situation. */ (*GrFlushPtr)(); /* TxPrintf("grtkGetBackingStore %d %d %d %d\n", r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); */ return TRUE; } /* * ---------------------------------------------------------------------------- * grtkScrollBackingStore -- * Enable fast scrolling by shifting part of the backing store * from one position to another, with the amount of shift indicated * by the X and/or Y value of the indicated point. * * Results: * TRUE on success, FALSE on failure. * * Side effects: * Data shifted in Pixmap memory. * * ---------------------------------------------------------------------------- */ bool grtkScrollBackingStore(MagWindow *w, Point *shift) { Pixmap pmap; Tk_Window tkwind = (Tk_Window)w->w_grdata; unsigned int width, height; int xorigin, yorigin, xshift, yshift; GC gc; XGCValues gcValues; pmap = (Pixmap)w->w_backingStore; if (pmap == (Pixmap)NULL) { TxPrintf("grtkScrollBackingStore %d %d failure\n", shift->p_x, shift->p_y); return FALSE; } gcValues.graphics_exposures = FALSE; gc = Tk_GetGC(tkwind, GCGraphicsExposures, &gcValues); width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot; height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot; xorigin = 0; yorigin = 0; xshift = shift->p_x; yshift = -shift->p_y; if (xshift > 0) width -= xshift; else if (xshift < 0) { width += xshift; xorigin = -xshift; xshift = 0; } if (yshift > 0) height -= yshift; else if (yshift < 0) { height += yshift; yorigin = -yshift; yshift = 0; } XCopyArea(grXdpy, pmap, pmap, gc, xorigin, yorigin, width, height, xshift, yshift); /* TxPrintf("grtkScrollBackingStore %d %d\n", shift->p_x, shift->p_y); */ return TRUE; } /* * ---------------------------------------------------------------------------- * grtkPutBackingStore -- * Copy data from the window into backing store. * * Results: * None. * * Side effects: * Graphics drawing into the window. * ---------------------------------------------------------------------------- */ void grtkPutBackingStore(MagWindow *w, Rect *area) { Pixmap pmap = (Pixmap)w->w_backingStore; Tk_Window tkwind = (Tk_Window)w->w_grdata; Window wind = Tk_WindowId(tkwind); unsigned int width, height; int ybot, xbot, xoff, yoff; GC gc; XGCValues gcValues; if (pmap == (Pixmap)NULL) return; /* Attempting to write backing store into an obscured */ /* window immediately invalidates everything in backing */ /* store. This is extreme, but is much simpler and under */ /* normal conditions faster than tracking all obscured */ /* areas separately. */ if (w->w_flags & WIND_OBSCURED) { grtkFreeBackingStore(w); w->w_backingStore = (ClientData)NULL; return; } width = area->r_xtop - area->r_xbot; height = area->r_ytop - area->r_ybot; ybot = grXtransY(w, area->r_ytop); xbot = area->r_xbot; gcValues.graphics_exposures = FALSE; gc = Tk_GetGC(tkwind, GCGraphicsExposures, &gcValues); xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot; yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop; /* This area may need to be expanded by a pixel and/or */ /* expanded by GrPixelCorrect to compensate for the OpenGL */ /* coordinate system. */ if (GrPixelCorrect == 0) { height--; width--; xbot++; } XCopyArea(grXdpy, wind, pmap, gc, xbot, ybot, width, height, xbot - xoff, ybot - yoff); /* TxPrintf("grtkPutBackingStore %d %d %d %d\n", xbot, area->r_ybot, area->r_xtop, area->r_ytop); */ } /* *--------------------------------------------------------- * GrTkGetColorByName -- * * Returns a string appropriate for setting a color * value in a Tk window (referenced to the Tk colormap) * for the given color of the magic style referenced * by the short name or long name (as given in the dstyle5 * file). * * Note that the colormaps grXcmap and Tk_Colormap(tkwind) * are the same unless we are in 8-bit PseudoColor mode, * in which case this function maps between the two. * * Note that when given an invalid short name, GrStyleNames[] * always returns 0, which is the index for style * "no_color_at_all". On the other hand, long names are not * uniquely defined between "normal" and "pale" styles. * * Results: * A string allocated by Tcl_Alloc() which will need to * be free'd by the calling function using Tcl_Free(). * * Side Effects: * None. * *--------------------------------------------------------- */ char * GrTkGetColorByName(name) char *name; { Tk_Window tkwind = Tk_MainWindow(magicinterp); int style, red, green, blue; XColor falsecolor; char *colstring; if (strlen(name) == 1) style = GrStyleNames[name[0] & 0x7f]; else if (DBWNumStyles == 0) { TxError("No style table exists.\n"); return NULL; } else { for (style = 0; style < TECHBEGINSTYLES + DBWNumStyles; style++) if (GrStyleTable[style].longname != NULL) if (!strcmp(name, GrStyleTable[style].longname)) break; } if (style >= TECHBEGINSTYLES + DBWNumStyles) { TxError("Style does not exist or style is not accessible\n"); return NULL; } falsecolor.pixel = GrStyleTable[style].color; if (GrTkInstalledCMap) { XQueryColor(grXdpy, Tk_Colormap(tkwind), &falsecolor); colstring = Tcl_Alloc(14); sprintf(colstring, "#%04x%04x%04x", falsecolor.red, falsecolor.green, falsecolor.blue); } else { /* Note that XColor colors are unsigned short, but GrGetColor */ /* expects an int *. */ GrGetColor(falsecolor.pixel, &red, &green, &blue); falsecolor.red = red; falsecolor.green = green; falsecolor.blue = blue; colstring = Tcl_Alloc(8); sprintf(colstring, "#%02x%02x%02x", falsecolor.red, falsecolor.green, falsecolor.blue); } return colstring; } /* *--------------------------------------------------------- * _magic_magiccolor -- * Tcl command callback for GrTkGetColorByName * * Results: * TCL result type * * Side effects: * None. *--------------------------------------------------------- */ static int _magic_magiccolor(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { char *result; char *name; if (argc != 2) { TxError("Usage: magiccolor name\n"); return TCL_ERROR; } name = argv[1]; result = GrTkGetColorByName(name); if (result) { Tcl_SetResult(interp, result, TCL_DYNAMIC); return TCL_OK; } else { TxError("No such color name \"%s\" in style file.\n", name); return TCL_ERROR; } } /* * The following data structure represents the master for a layer * image: */ typedef struct LayerMaster { Tk_ImageMaster tkMaster; /* Tk's token for image master. NULL means * the image is being deleted. */ Tcl_Interp *interp; /* Interpreter for application that is * using image. */ Tcl_Command imageCmd; /* Token for image command (used to delete * it when the image goes away). NULL means * the image command has already been * deleted. */ int width, height; /* Dimensions of image. */ int layerOff; /* If TRUE layer is displayed in non-edit style */ int layerLock; /* Layer is displayed with a cursor icon */ char *layerString; /* Value of -layer option (malloc'ed). */ struct LayerInstance *instancePtr; /* First in list of all instances associated * with this master. */ } LayerMaster; /* * The following data structure represents all of the instances of an * image that lie within a particular window: */ typedef struct LayerInstance { int refCount; /* Number of instances that share this * data structure. */ LayerMaster *masterPtr; /* Pointer to master for image. */ Tk_Window tkwin; /* Window in which the instances will be * displayed. */ Pixmap pixmap; /* The bitmap to display. */ GC gc; /* Graphics context for displaying pixmap */ struct LayerInstance *nextPtr; /* Next in list of all instance structures * associated with masterPtr (NULL means * end of list). */ } LayerInstance; /* * The type record for bitmap images: */ #if TCL_MAJOR_VERSION < 9 static int ImgLayerCreate (Tcl_Interp *interp, const char *name, int argc, Tcl_Obj *const objv[], const Tk_ImageType *typePtr, Tk_ImageMaster master, ClientData *clientDataPtr); #else static int ImgLayerCreate (Tcl_Interp *interp, const char *name, Tcl_Size argc, Tcl_Obj *const objv[], const Tk_ImageType *typePtr, Tk_ImageMaster master, ClientData *clientDataPtr); #endif static ClientData ImgLayerGet (Tk_Window tkwin, ClientData clientData); static void ImgLayerDisplay (ClientData clientData, Display *display, Drawable drawable, int imageX, int imageY, int width, int height, int drawableX, int drawableY); static void ImgLayerFree (ClientData clientData, Display *display); static void ImgLayerDelete (ClientData clientData); Tk_ImageType tkLayerImageType = { "layer", /* name */ ImgLayerCreate, /* createProc */ ImgLayerGet, /* getProc */ ImgLayerDisplay, /* displayProc */ ImgLayerFree, /* freeProc */ ImgLayerDelete, /* deleteProc */ NULL, /* postscriptProc */ (Tk_ImageType *) NULL /* nextPtr */ }; /* * Information used for parsing configuration specs: * Size defaults to a 16x16 area. */ static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_STRING, "-name", (char *) NULL, (char *) NULL, (char *) NULL, offsetof(LayerMaster, layerString), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-disabled", (char *) NULL, (char *) NULL, (char *) "0", offsetof(LayerMaster, layerOff), 0}, {TK_CONFIG_INT, "-icon", (char *) NULL, (char *) NULL, (char *) "-1", offsetof(LayerMaster, layerLock), 0}, {TK_CONFIG_INT, "-width", (char *) NULL, (char *) NULL, (char *) "16", offsetof(LayerMaster, width), 0}, {TK_CONFIG_INT, "-height", (char *) NULL, (char *) NULL, (char *) "16", offsetof(LayerMaster, height), 0}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; /* * Prototypes for procedures used only locally in this file: */ static int ImgLayerCmd (ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]); static void ImgLayerCmdDeletedProc ( ClientData clientData); static void ImgLayerConfigureInstance ( LayerInstance *instancePtr); static int ImgLayerConfigureMaster ( LayerMaster *masterPtr, int argc, Tcl_Obj *const objv[], int flags); /* *---------------------------------------------------------------------- * * ImgLayerCreate -- * * This procedure is called by the Tk image code to create "test" * images. * * Results: * A standard Tcl result. * * Side effects: * The data structure for a new image is allocated. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int ImgLayerCreate(interp, name, argc, argv, typePtr, master, clientDataPtr) Tcl_Interp *interp; /* Interpreter for application containing * image. */ const char *name; /* Name to use for image. */ #if TCL_MAJOR_VERSION < 9 int argc; /* Number of arguments. */ #else Tcl_Size argc; /* Number of arguments. */ #endif Tcl_Obj *const argv[]; /* Argument objects for options (doesn't * include image name or type). */ const Tk_ImageType *typePtr;/* Pointer to our type record (not used). */ Tk_ImageMaster master; /* Token for image, to be used by us in * later callbacks. */ ClientData *clientDataPtr; /* Store manager's token for image here; * it will be returned in later callbacks. */ { LayerMaster *masterPtr; masterPtr = (LayerMaster *) Tcl_Alloc(sizeof(LayerMaster)); masterPtr->tkMaster = master; masterPtr->interp = interp; masterPtr->imageCmd = Tcl_CreateObjCommand(interp, name, ImgLayerCmd, (ClientData) masterPtr, ImgLayerCmdDeletedProc); masterPtr->width = masterPtr->height = 0; masterPtr->layerOff = 0; masterPtr->layerLock = -1; masterPtr->layerString = NULL; masterPtr->instancePtr = NULL; if (ImgLayerConfigureMaster(masterPtr, argc, argv, 0) != TCL_OK) { ImgLayerDelete((ClientData) masterPtr); return TCL_ERROR; } *clientDataPtr = (ClientData) masterPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * ImgLayerConfigureMaster -- * * This procedure is called when a bitmap image is created or * reconfigured. It process configuration options and resets * any instances of the image. * * Results: * A standard Tcl return value. If TCL_ERROR is returned then * an error message is left in the masterPtr->interp's result. * * Side effects: * Existing instances of the image will be redisplayed to match * the new configuration options. * *---------------------------------------------------------------------- */ static int ImgLayerConfigureMaster(masterPtr, objc, objv, flags) LayerMaster *masterPtr; /* Pointer to data structure describing * overall pixmap image to (reconfigure). */ int objc; /* Number of entries in objv. */ Tcl_Obj *const objv[]; /* Pairs of configuration options for image. */ int flags; /* Flags to pass to Tk_ConfigureWidget, * such as TK_CONFIG_ARGV_ONLY. */ { LayerInstance *instancePtr; int dummy1; #if TCL_MAJOR_VERSION < 9 char **tmp_argv = (char **) Tcl_Alloc((objc+1) * sizeof(char *)); for (dummy1 = 0; dummy1 < objc; dummy1++) { tmp_argv[dummy1]=Tcl_GetString(objv[dummy1]); } tmp_argv[objc] = NULL; int argc = objc; const char **argv = (const char **)tmp_argv; #else Tcl_Size argc = objc; Tcl_Obj *const *argv = (Tcl_Obj *const *)objv; #endif if (Tk_ConfigureWidget(masterPtr->interp, Tk_MainWindow(masterPtr->interp), configSpecs, argc, argv, (char *) masterPtr, flags) != TCL_OK) { #if TCL_MAJOR_VERSION < 9 Tcl_Free((char *) tmp_argv); #endif return TCL_ERROR; } #if TCL_MAJOR_VERSION < 9 Tcl_Free((char *) tmp_argv); #endif /* * Cycle through all of the instances of this image, regenerating * the information for each instance. Then force the image to be * redisplayed everywhere that it is used. */ for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { ImgLayerConfigureInstance(instancePtr); } Tk_ImageChanged(masterPtr->tkMaster, 0, 0, masterPtr->width, masterPtr->height, masterPtr->width, masterPtr->height); return TCL_OK; } /* *---------------------------------------------------------------------- * grDrawOffScreenBox -- * Draw a box on an off-screen drawable (convenience function for * the following procedure ImgLayerConfigureInstance() * * Results: * None. * * Side effects: * Draws into an off-screen drawable destination. * *---------------------------------------------------------------------- */ void grDrawOffScreenBox(rect) Rect *rect; { (*grDrawLinePtr)(rect->r_xbot, rect->r_ytop - 1, rect->r_xtop - 1, rect->r_ytop - 1); (*grDrawLinePtr)(rect->r_xbot, rect->r_ybot, rect->r_xtop - 1, rect->r_ybot); (*grDrawLinePtr)(rect->r_xbot, rect->r_ybot, rect->r_xbot, rect->r_ytop - 1); (*grDrawLinePtr)(rect->r_xtop - 1, rect->r_ybot, rect->r_xtop - 1, rect->r_ytop - 1); } /* *---------------------------------------------------------------------- * * ImgLayerConfigureInstance -- * * This procedure is called to create displaying information for * a layer image instance based on the configuration information * in the master. It is invoked both when new instances are * created and when the master is reconfigured. * * Results: * None. * * Side effects: * Generates errors via Tcl_BackgroundError if there are problems * in setting up the instance. * *---------------------------------------------------------------------- */ #define LAYER_NORMAL 0 #define LAYER_LABELS 1 #define LAYER_SUBCELL 2 #define LAYER_LAYOUT 3 static void ImgLayerConfigureInstance(instancePtr) LayerInstance *instancePtr; /* Instance to reconfigure. */ { LayerMaster *masterPtr = instancePtr->masterPtr; XGCValues gcValues; GC gc; unsigned int gcmask; TileType layer; TileTypeBitMask *mask; int i, special = LAYER_NORMAL; Rect r; Tk_Window tkwind = instancePtr->tkwin; MagWindow *mw, tmpmw; int saveStyle; if (Tk_WindowId(tkwind) == 0) Tk_MakeWindowExist(tkwind); if (Tk_WindowId(tkwind) == 0) { Tcl_AddErrorInfo(masterPtr->interp, "No ID exists for window"); goto error; } /* * For each of the options in masterPtr, translate the string * form into an internal form appropriate for instancePtr. */ if (masterPtr->width <= 0 || masterPtr->height <= 0) { Tcl_AddErrorInfo(masterPtr->interp, "Image width or height is negative"); goto error; } if (instancePtr->pixmap != None) { mw = WindSearchData((ClientData)instancePtr->pixmap); if (mw != NULL) { windUnlink(mw); windReClip(); windFree(mw); } Tk_FreePixmap(grXdpy, instancePtr->pixmap); instancePtr->pixmap = None; } if (masterPtr->layerString != NULL) { if (!strcmp(masterPtr->layerString, "none")) layer = TT_SPACE; else if (!strcmp(masterPtr->layerString, "errors")) layer = TT_ERROR_P; else if (!strcmp(masterPtr->layerString, "labels")) { layer = TT_SPACE; special = LAYER_LABELS; } else if (!strcmp(masterPtr->layerString, "subcell")) { layer = TT_SPACE; special = LAYER_SUBCELL; } else layer = DBTechNameType(masterPtr->layerString); if (layer < 0) { layer = (*GrWindowIdPtr)(masterPtr->layerString); if (layer >= 0) special = LAYER_LAYOUT; else { Tcl_AddErrorInfo(masterPtr->interp, "Unknown layer type"); goto error; } } r.r_xbot = r.r_ybot = 0; r.r_xtop = masterPtr->width; r.r_ytop = masterPtr->height; gcValues.graphics_exposures = FALSE; gcmask = GCGraphicsExposures; gc = Tk_GetGC(tkwind, gcmask, &gcValues); if (instancePtr->gc != None) Tk_FreeGC(grXdpy, instancePtr->gc); instancePtr->gc = gc; if (special == LAYER_LAYOUT) /* Off-Screen Rendering */ { Rect screenRect; Tk_Window pixwind; mw = WindSearchWid(layer); if (mw == NULL) { Tcl_AddErrorInfo(masterPtr->interp, "Unknown window ID\n"); goto error; } pixwind = (Tk_Window)mw->w_grdata; instancePtr->pixmap = Tk_GetPixmap(grXdpy, Tk_WindowId(pixwind), masterPtr->width, masterPtr->height, Tk_Depth(pixwind)); (*GrDeleteWindowPtr)(mw); mw->w_flags |= WIND_OFFSCREEN; mw->w_grdata = (ClientData)instancePtr->pixmap; screenRect.r_xbot = 0; screenRect.r_ybot = 0; screenRect.r_xtop = masterPtr->width; screenRect.r_ytop = masterPtr->height; WindReframe(mw, &screenRect, FALSE, FALSE); WindRedisplay(mw); return; } instancePtr->pixmap = Tk_GetPixmap(grXdpy, Tk_WindowId(tkwind), masterPtr->width, masterPtr->height, Tk_Depth(tkwind)); tmpmw.w_flags = WIND_OFFSCREEN; tmpmw.w_grdata = (ClientData)instancePtr->pixmap; tmpmw.w_grdata2 = (ClientData)NULL; tmpmw.w_allArea = r; tmpmw.w_clipAgainst = NULL; GrLock(&tmpmw, FALSE); /* Save the current state */ saveStyle = grCurDStyle; /* First fill with background style */ GrSetStuff(STYLE_ERASEALL); grInformDriver(); (*grFillRectPtr)(&r); for (i = 0; i < DBWNumStyles; i++) { mask = DBWStyleToTypes(i); if (TTMaskHasType(mask, layer)) { GrSetStuff(i + TECHBEGINSTYLES + ((masterPtr->layerOff == 0) ? 0 : DBWNumStyles)); grInformDriver(); /* Solid areas */ if ((grCurFill == GR_STSOLID) || (grCurFill == GR_STSTIPPLE)) (*grFillRectPtr)(&r); /* Outlines */ if (grCurOutline != 0) grDrawOffScreenBox(&r); /* Contact crosses */ if (grCurFill == GR_STCROSS) { (*grDrawLinePtr)(r.r_xbot, r.r_ybot, r.r_xtop - 1, r.r_ytop - 1); (*grDrawLinePtr)(r.r_xbot, r.r_ytop - 1, r.r_xtop - 1, r.r_ybot); } } } switch(special) { case LAYER_LABELS: GrSetStuff(STYLE_LABEL); grInformDriver(); grDrawOffScreenBox(&r); break; case LAYER_SUBCELL: GrSetStuff(STYLE_BBOX); grInformDriver(); grDrawOffScreenBox(&r); break; } if (masterPtr->layerLock >= 0) { GrSetStuff(STYLE_BLACK); grInformDriver(); GrDrawGlyphNum(masterPtr->layerLock, 0, 0); } /* Restore the original state */ GrSetStuff(saveStyle); grInformDriver(); GrUnlock(&tmpmw); } return; error: /* * An error occurred: clear the graphics context in the instance to * make it clear that this instance cannot be displayed. Then report * the error. */ if (instancePtr->gc != None) Tk_FreeGC(grXdpy, instancePtr->gc); instancePtr->gc = None; Tcl_AddErrorInfo(masterPtr->interp, "\n (while configuring image \""); Tcl_AddErrorInfo(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster)); Tcl_AddErrorInfo(masterPtr->interp, "\")"); Tcl_BackgroundError(masterPtr->interp); } /* *-------------------------------------------------------------- * * ImgLayerCmd -- * * This procedure is invoked to process the Tcl command * that corresponds to an image managed by this module. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ static int ImgLayerCmd(clientData, interp, objc, objv) ClientData clientData; /* Information about the image master. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *const objv[]; /* Argument objects. */ { static char *layerOptions[] = {"cget", "configure", (char *) NULL}; LayerMaster *masterPtr = (LayerMaster *) clientData; int code, index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], (const char **)layerOptions, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case 0: { if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); return TCL_ERROR; } return Tk_ConfigureValue(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, Tcl_GetString(objv[2]), 0); } case 1: { if (objc == 2) { code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, (char *) NULL, 0); } else if (objc == 3) { code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, Tcl_GetString(objv[2]), 0); } else { code = ImgLayerConfigureMaster(masterPtr, objc-2, objv+2, TK_CONFIG_ARGV_ONLY); } return code; } default: { TxError("bad const entries to layerOptions in ImgLayerCmd\n"); MainExit(1); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * ImgLayerGet -- * * This procedure is called for each use of a layer image in a * widget. * * Results: * The return value is a token for the instance, which is passed * back to us in calls to ImgLayerDisplay and ImgLayerFree. * * Side effects: * A data structure is set up for the instance (or, an existing * instance is re-used for the new one). * *---------------------------------------------------------------------- */ static ClientData ImgLayerGet(tkwin, masterData) Tk_Window tkwin; /* Window in which the instance will be * used. */ ClientData masterData; /* Pointer to our master structure for the * image. */ { LayerMaster *masterPtr = (LayerMaster *) masterData; LayerInstance *instancePtr; /* * See if there is already an instance for this window. If so * then just re-use it. */ for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { if (instancePtr->tkwin == tkwin) { instancePtr->refCount++; return (ClientData) instancePtr; } } /* * The image isn't already in use in this window. Make a new * instance of the image. */ instancePtr = (LayerInstance *) Tcl_Alloc(sizeof(LayerInstance)); instancePtr->refCount = 1; instancePtr->masterPtr = masterPtr; instancePtr->tkwin = tkwin; instancePtr->pixmap = None; instancePtr->gc = None; instancePtr->nextPtr = masterPtr->instancePtr; masterPtr->instancePtr = instancePtr; ImgLayerConfigureInstance(instancePtr); /* * If this is the first instance, must set the size of the image. */ if (instancePtr->nextPtr == NULL) { Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, masterPtr->width, masterPtr->height); } return (ClientData) instancePtr; } /* *---------------------------------------------------------------------- * * ImgLayerDisplay -- * * This procedure is invoked to draw a layer image. * * Results: * None. * * Side effects: * A portion of the image gets rendered in a pixmap or window. * *---------------------------------------------------------------------- */ static void ImgLayerDisplay(clientData, display, drawable, imageX, imageY, width, height, drawableX, drawableY) ClientData clientData; /* Pointer to LayerInstance structure for * for instance to be displayed. */ Display *display; /* Display on which to draw image. */ Drawable drawable; /* Pixmap or window in which to draw image. */ int imageX, imageY; /* Upper-left corner of region within image * to draw. */ int width, height; /* Dimensions of region within image to draw. */ int drawableX, drawableY; /* Coordinates within drawable that * correspond to imageX and imageY. */ { LayerInstance *instancePtr = (LayerInstance *) clientData; /* * If there's no GC, then an error occurred during image creation * and it should not be displayed. */ if (instancePtr->gc == None) return; XCopyArea(display, instancePtr->pixmap, drawable, instancePtr->gc, imageX, imageY, (unsigned) width, (unsigned) height, drawableX, drawableY); } /* *---------------------------------------------------------------------- * * ImgLayerFree -- * * This procedure is called when a widget ceases to use a * particular instance of an image. * * Results: * None. * * Side effects: * Internal data structures get cleaned up. * *---------------------------------------------------------------------- */ static void ImgLayerFree(clientData, display) ClientData clientData; /* Pointer to LayerInstance structure for * for instance to be displayed. */ Display *display; /* Display containing window that used image. */ { LayerInstance *instancePtr = (LayerInstance *) clientData; LayerInstance *prevPtr; instancePtr->refCount--; if (instancePtr->refCount > 0) { return; } /* * There are no more uses of the image within this widget. Free * the instance structure. */ if (instancePtr->pixmap != None) { MagWindow *mw; mw = WindSearchData((ClientData)instancePtr->pixmap); if (mw != NULL) { windUnlink(mw); windReClip(); windFree(mw); } Tk_FreePixmap(display, instancePtr->pixmap); } if (instancePtr->masterPtr->instancePtr == instancePtr) { instancePtr->masterPtr->instancePtr = instancePtr->nextPtr; } else { for (prevPtr = instancePtr->masterPtr->instancePtr; prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) { /* Empty loop body */ } prevPtr->nextPtr = instancePtr->nextPtr; } Tcl_Free((char *) instancePtr); } /* *---------------------------------------------------------------------- * * ImgLayerDelete -- * * This procedure is called by the image code to delete the * master structure for an image. * * Results: * None. * * Side effects: * Resources associated with the image get freed. * *---------------------------------------------------------------------- */ static void ImgLayerDelete(masterData) ClientData masterData; /* Pointer to BitmapMaster structure for * image. Must not have any more instances. */ { LayerMaster *masterPtr = (LayerMaster *) masterData; if (masterPtr->instancePtr != NULL) { TxError("tried to delete layer image when instances still exist\n"); MainExit(1); } masterPtr->tkMaster = NULL; if (masterPtr->imageCmd != NULL) { Tcl_DeleteCommandFromToken(masterPtr->interp, masterPtr->imageCmd); } Tk_FreeOptions(configSpecs, (char *) masterPtr, (Display *) NULL, 0); Tcl_Free((char *) masterPtr); } /* *---------------------------------------------------------------------- * * ImgLayerCmdDeletedProc -- * * This procedure is invoked when the image command for an image * is deleted. It deletes the image. * * Results: * None. * * Side effects: * The image is deleted. * *---------------------------------------------------------------------- */ static void ImgLayerCmdDeletedProc(clientData) ClientData clientData; /* Pointer to BitmapMaster structure for * image. */ { LayerMaster *masterPtr = (LayerMaster *) clientData; masterPtr->imageCmd = NULL; if (masterPtr->tkMaster != NULL) { Tk_DeleteImage(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster)); } } /* *--------------------------------------------------------- * RegisterTkCommands -- * Register the "magiccolor" command with Tcl. * Register the layer type with the Tk "image" command * * Results: * None. * * Side Effects: * Command registration with the TCL interpreter. *--------------------------------------------------------- */ void RegisterTkCommands(Tcl_Interp *interp) { Tcl_CreateCommand(interp, "magic::magiccolor", (Tcl_CmdProc *)_magic_magiccolor, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); Tk_CreateImageType(&tkLayerImageType); } #endif /* MAGIC_WRAPPER */