magic/graphics/grTkCommon.c

1546 lines
40 KiB
C

/* 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 <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#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 */