Files
magic/graphics/grTkCommon.c
T
Patrick Kurth ea03883829 Fixed unused-parameters/unused-variables warnings
Added a ARG_UNUSED macro in utils/magic.h, which
produces a simple cast to void to silence the warnings.
The reasons this is used as opposed to an attribute is
that most compilers should support this.
Besides this, the macro can be easily searched for in
the future to potentially remove (where possible) the
unused parameters.
2025-10-10 22:12:19 +02:00

1547 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;
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[])
{
ARG_UNUSED(clientData);
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. */
{
ARG_UNUSED(typePtr);
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 */