// PLplot Tk device driver. // // Copyright (C) 2004 Maurice LeBrun // Copyright (C) 2004 Joao Cardoso // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // This device driver is designed to be used by a PlPlotter, and in fact requires // the existence of an enclosing PlPlotter. // // The idea is that this should develop into a completely cross-platform driver // for use by the cross platform Tk system. // // #include "plDevs.h" #define DEBUG #ifdef PLD_tkwin #define NEED_PLDEBUG #include "plplotP.h" #include "pltkwd.h" #include "drivers.h" #include "plevent.h" #define _TCLINT #ifdef USE_TCL_STUBS // Unfortunately, tkInt.h ends up loading Malloc.h under Windows // So we have to deal with this mess #undef malloc #undef free #undef realloc #undef calloc #if defined ( _WIN32 ) || defined ( MAC_TCL ) #include <tkInt.h> #else #include <tk.h> #endif #define malloc ckalloc #define free( m ) ckfree( (char *) m ) #define realloc ckrealloc #define calloc ckcalloc #else #if defined ( _WIN32 ) || defined ( MAC_TCL ) #include <tkInt.h> #else #include <tk.h> #endif #endif #ifdef ckalloc #undef ckalloc #define ckalloc malloc #endif #ifdef ckfree #undef ckfree #define ckfree free #endif #ifdef free #undef free #endif // Device info PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tkwin = "tkwin:New tk driver:1:tkwin:45:tkwin\n"; void * ckcalloc( size_t nmemb, size_t size ); // // We want to use the 'pure Tk' interface. On Unix we can use // some direct calls to X instead of Tk, if we want, although // that code hasn't been tested for some time. So this define // is required on Windows/MacOS and perhaps optional on Unix. // #define USE_TK #ifdef _WIN32 #define XSynchronize( display, bool ) { display->request++; } #define XSync( display, bool ) { display->request++; } #define XFlush( display ) #endif // Dummy definition of PlPlotter containing first few fields typedef struct PlPlotter { Tk_Window tkwin; // Window that embodies the frame. NULL // means that the window has been destroyed // but the data structures haven't yet been // cleaned up. // Display *display; // Display containing widget. Used, among // other things, so that resources can be // freed even after tkwin has gone away. // Tcl_Interp *interp; // Interpreter associated with // widget. Used to delete widget // command. // } PlPlotter; void CopyColour( XColor* from, XColor* to ); void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col ); static int pltk_AreWeGrayscale( PlPlotter *plf ); void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); static int synchronize = 0; // change to 1 for synchronized operation // for debugging only // Number of instructions to skip between querying the X server for events #define MAX_INSTR 20 // Pixels/mm #define PHYSICAL 0 // Enables physical scaling.. // Set constants for dealing with colormap. In brief: // // ccmap When set, turns on custom color map // // XWM_COLORS Number of low "pixel" values to copy. // CMAP0_COLORS Color map 0 entries. // CMAP1_COLORS Color map 1 entries. // MAX_COLORS Maximum colors period. // // See Init_CustomCmap() and Init_DefaultCmap() for more info. // Set ccmap at your own risk -- still under development. // // plplot_tkwin_ccmap is statically defined in pltkwd.h. Note that // plplotter.c also includes that header and uses that variable. #define XWM_COLORS 70 #define CMAP0_COLORS 16 #define CMAP1_COLORS 50 #define MAX_COLORS 256 #ifndef USE_TK // Variables to hold RGB components of given colormap. // Used in an ugly hack to get past some X11R5 and TK limitations. static int sxwm_colors_set; static XColor sxwm_colors[MAX_COLORS]; #endif // Keep pointers to all the displays in use static TkwDisplay *tkwDisplay[PLTKDISPLAYS]; #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) static unsigned char CreatePixmapStatus; static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error ); #endif // Function prototypes // Initialization static void Init( PLStream *pls ); static void InitColors( PLStream *pls ); static void AllocCustomMap( PLStream *pls ); static void AllocCmap0( PLStream *pls ); static void AllocCmap1( PLStream *pls ); static void CreatePixmap( PLStream *pls ); static void GetVisual( PLStream *pls ); static void AllocBGFG( PLStream *pls ); // Escape function commands static void ExposeCmd( PLStream *pls, PLDisplay *ptr ); static void RedrawCmd( PLStream *pls ); static void ResizeCmd( PLStream *pls, PLDisplay *ptr ); #ifndef USE_TK static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr ); #endif static void FillPolygonCmd( PLStream *pls ); #ifdef USING_PLESC_COPY static void CopyCommand( PLStream *pls ); #endif // Miscellaneous static void StoreCmap0( PLStream *pls ); static void StoreCmap1( PLStream *pls ); static void WaitForPage( PLStream *pls ); void plD_dispatch_init_tkwin( PLDispatchTable *pdt ); void plD_init_tkwin( PLStream * ); void plD_line_tkwin( PLStream *, short, short, short, short ); void plD_polyline_tkwin( PLStream *, short *, short *, PLINT ); void plD_eop_tkwin( PLStream * ); void plD_bop_tkwin( PLStream * ); void plD_tidy_tkwin( PLStream * ); void plD_state_tkwin( PLStream *, PLINT ); void plD_esc_tkwin( PLStream *, PLINT, void * ); void plD_wait_tkwin( PLStream * ); void plD_open_tkwin( PLStream *pls ); void plD_dispatch_init_tkwin( PLDispatchTable *pdt ) { #ifndef ENABLE_DYNDRIVERS pdt->pl_MenuStr = "PLplot Tk plotter"; pdt->pl_DevName = "tkwin"; #endif pdt->pl_type = plDevType_Interactive; pdt->pl_seq = 45; pdt->pl_init = (plD_init_fp) plD_init_tkwin; pdt->pl_line = (plD_line_fp) plD_line_tkwin; pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tkwin; pdt->pl_eop = (plD_eop_fp) plD_eop_tkwin; pdt->pl_bop = (plD_bop_fp) plD_bop_tkwin; pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tkwin; pdt->pl_state = (plD_state_fp) plD_state_tkwin; pdt->pl_esc = (plD_esc_fp) plD_esc_tkwin; pdt->pl_wait = (plD_wait_fp) plD_wait_tkwin; } //-------------------------------------------------------------------------- // plD_init_tkwin() // // Initialize device. // Tk-dependent stuff done in plD_open_tkwin() and Init(). //-------------------------------------------------------------------------- void plD_init_tkwin( PLStream *pls ) { TkwDev *dev; float pxlx, pxly; int xmin = 0; int xmax = PIXELS_X - 1; int ymin = 0; int ymax = PIXELS_Y - 1; dbug_enter( "plD_init_tkw" ); pls->termin = 1; // Is an interactive terminal pls->dev_flush = 1; // Handle our own flushes pls->dev_fill0 = 1; // Handle solid fills pls->plbuf_write = 1; // Activate plot buffer // The real meat of the initialization done here if ( pls->dev == NULL ) plD_open_tkwin( pls ); dev = (TkwDev *) pls->dev; Init( pls ); // Get ready for plotting dev->xlen = (short) ( xmax - xmin ); dev->ylen = (short) ( ymax - ymin ); dev->xscale_init = (double) dev->init_width / (double) dev->xlen; dev->yscale_init = (double) dev->init_height / (double) dev->ylen; dev->xscale = dev->xscale_init; dev->yscale = dev->yscale_init; #if PHYSICAL pxlx = (PLFLT) ( (double) PIXELS_X / dev->width * DPMM ); pxly = (PLFLT) ( (double) PIXELS_Y / dev->height * DPMM ); #else pxlx = (PLFLT) ( (double) PIXELS_X / LPAGE_X ); pxly = (PLFLT) ( (double) PIXELS_Y / LPAGE_Y ); #endif plP_setpxl( pxlx, pxly ); plP_setphy( xmin, xmax, ymin, ymax ); } //-------------------------------------------------------------------------- // plD_open_tkwin() // // Performs basic driver initialization, without actually opening or // modifying a window. May be called by the outside world before plinit // in case the caller needs early access to the driver internals (not // very common -- currently only used externally by plplotter). //-------------------------------------------------------------------------- void plD_open_tkwin( PLStream *pls ) { TkwDev *dev; TkwDisplay *tkwd; int i; dbug_enter( "plD_open_tkw" ); // Allocate and initialize device-specific data if ( pls->dev != NULL ) plwarn( "plD_open_tkw: device pointer is already set" ); pls->dev = (TkwDev *) calloc( 1, (size_t) sizeof ( TkwDev ) ); if ( pls->dev == NULL ) plexit( "plD_init_tkw: Out of memory." ); dev = (TkwDev *) pls->dev; // Variables used in querying the X server for events dev->instr = 0; dev->max_instr = MAX_INSTR; // See if display matches any already in use, and if so use that dev->tkwd = NULL; for ( i = 0; i < PLTKDISPLAYS; i++ ) { if ( tkwDisplay[i] == NULL ) { continue; } else if ( pls->FileName == NULL && tkwDisplay[i]->displayName == NULL ) { dev->tkwd = tkwDisplay[i]; break; } else if ( pls->FileName == NULL || tkwDisplay[i]->displayName == NULL ) { continue; } else if ( strcmp( tkwDisplay[i]->displayName, pls->FileName ) == 0 ) { dev->tkwd = tkwDisplay[i]; break; } } // If no display matched, create a new one if ( dev->tkwd == NULL ) { dev->tkwd = (TkwDisplay *) calloc( 1, (size_t) sizeof ( TkwDisplay ) ); if ( dev->tkwd == NULL ) plexit( "Init: Out of memory." ); for ( i = 0; i < PLTKDISPLAYS; i++ ) { if ( tkwDisplay[i] == NULL ) break; } if ( i == PLTKDISPLAYS ) plexit( "Init: Out of tkwDisplay's." ); tkwDisplay[i] = tkwd = (TkwDisplay *) dev->tkwd; tkwd->nstreams = 1; // // If we don't have a tk widget we're being called on, then // abort operations now // if ( pls->plPlotterPtr == NULL ) { plexit( "No tk plframe widget to connect to" ); } // Old version for MacOS Tk8.0 // // char deflt[] = "Macintosh:0"; // pls->FileName = deflt; // tkwd->display = (Display*) TkpOpenDisplay(pls->FileName); // // Open display #if defined ( MAC_TCL ) || defined ( _WIN32 ) if ( !pls->FileName ) { // // Need to strdup because Tk has allocated the screen name, // but we will actually 'free' it later ourselves, and therefore // need to own the memory. // pls->FileName = plstrdup( TkGetDefaultScreenName( NULL, NULL ) ); } tkwd->display = pls->plPlotterPtr->display; #else tkwd->display = XOpenDisplay( pls->FileName ); #endif if ( tkwd->display == NULL ) { plexit( "Can't open display" ); } tkwd->displayName = pls->FileName; tkwd->screen = DefaultScreen( tkwd->display ); if ( synchronize ) { XSynchronize( tkwd->display, 1 ); } // Get colormap and visual tkwd->map = Tk_Colormap( pls->plPlotterPtr->tkwin ); GetVisual( pls ); // // Figure out if we have a color display or not. // Default is color IF the user hasn't specified and IF the output device is // not grayscale. // if ( pls->colorset ) tkwd->color = pls->color; else { pls->color = 1; tkwd->color = !pltk_AreWeGrayscale( pls->plPlotterPtr ); } // Allocate & set background and foreground colors AllocBGFG( pls ); pltkwin_setBGFG( pls ); } // Display matched, so use existing display data else { tkwd = (TkwDisplay *) dev->tkwd; tkwd->nstreams++; } tkwd->ixwd = i; } //-------------------------------------------------------------------------- // plD_line_tkwin() // // Draw a line in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_line_tkwin( PLStream *pls, short x1a, short y1a, short x2a, short y2a ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a; if ( dev->flags & 1 ) return; y1 = dev->ylen - y1; y2 = dev->ylen - y2; x1 = (int) ( x1 * dev->xscale ); x2 = (int) ( x2 * dev->xscale ); y1 = (int) ( y1 * dev->yscale ); y2 = (int) ( y2 * dev->yscale ); if ( dev->write_to_window ) XDrawLine( tkwd->display, dev->window, dev->gc, x1, y1, x2, y2 ); if ( dev->write_to_pixmap ) XDrawLine( tkwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 ); } //-------------------------------------------------------------------------- // plD_polyline_tkwin() // // Draw a polyline in the current color from (x1,y1) to (x2,y2). //-------------------------------------------------------------------------- void plD_polyline_tkwin( PLStream *pls, short *xa, short *ya, PLINT npts ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLINT i; XPoint _pts[PL_MAXPOLY]; XPoint *pts; if ( dev->flags & 1 ) return; if ( npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts ); } else { pts = _pts; } for ( i = 0; i < npts; i++ ) { pts[i].x = (short) ( dev->xscale * xa[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) ); } if ( dev->write_to_window ) XDrawLines( tkwd->display, dev->window, dev->gc, pts, npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, npts, CoordModeOrigin ); if ( npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // plD_eop_tkwin() // // End of page. //-------------------------------------------------------------------------- void plD_eop_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_eop_tkw" ); if ( dev->flags & 1 ) return; XFlush( tkwd->display ); if ( pls->db ) ExposeCmd( pls, NULL ); } //-------------------------------------------------------------------------- // plD_wait_tkwin() // // User must hit return (or third mouse button) to continue. //-------------------------------------------------------------------------- void plD_wait_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_wait_tkw" ); if ( dev->flags & 1 ) return; WaitForPage( pls ); } //-------------------------------------------------------------------------- // WaitForPage() // // This routine waits for the user to advance the plot, while handling // all other events. //-------------------------------------------------------------------------- static void WaitForPage( PLStream *pls ) { PlPlotter *plf = pls->plPlotterPtr; TkwDev *dev = (TkwDev *) pls->dev; dbug_enter( "WaitForPage" ); dev->flags &= 1; if ( plf == NULL ) { plwarn( "WaitForPage: Illegal call --- driver can't find enclosing PlPlotter" ); return; } PlplotterAtEop( plf->interp, plf ); while ( !( dev->flags ) && !Tcl_InterpDeleted( plf->interp ) && ( Tk_GetNumMainWindows() > 0 ) ) { Tcl_DoOneEvent( 0 ); } if ( Tcl_InterpDeleted( plf->interp ) || ( Tk_GetNumMainWindows() <= 0 ) ) { dev->flags |= 1; } dev->flags &= 1; } //-------------------------------------------------------------------------- // plD_bop_tkwin() // // Set up for the next page. //-------------------------------------------------------------------------- void plD_bop_tkwin( PLStream *pls ) { PlPlotter *plf = pls->plPlotterPtr; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XRectangle xrect; xrect.x = 0; xrect.y = 0; xrect.width = (short unsigned) dev->width; xrect.height = (short unsigned) dev->height; dbug_enter( "plD_bop_tkw" ); if ( dev->flags & 1 ) return; if ( dev->write_to_window ) { #ifdef MAC_TCL // MacTk only has these X calls XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); XFillRectangles( tkwd->display, dev->window, dev->gc, &xrect, 1 ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); #else XClearWindow( tkwd->display, dev->window ); #endif } if ( dev->write_to_pixmap ) { XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); XFillRectangles( tkwd->display, dev->pixmap, dev->gc, &xrect, 1 ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } XSync( tkwd->display, 0 ); pls->page++; PlplotterAtBop( plf->interp, plf ); } //-------------------------------------------------------------------------- // plD_tidy_tkwin() // // Close graphics file //-------------------------------------------------------------------------- void plD_tidy_tkwin( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_tidy_tkw" ); tkwd->nstreams--; if ( tkwd->nstreams == 0 ) { int ixwd = tkwd->ixwd; XFreeGC( tkwd->display, dev->gc ); #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) XCloseDisplay( tkwd->display ); #endif free_mem( tkwDisplay[ixwd] ); } // // Vince removed this November 1999. It seems as if a simple // 'plframe .p ; destroy .p' leaves a temporary buf file open // if we clear this flag here. It should be checked and then // cleared by whoever called us. An alternative fix would // be to carry out the check/tidy here. The plframe widget // handles this stuff for us. // // pls->plbuf_write = 0; } //-------------------------------------------------------------------------- // plD_state_tkwin() // // Handle change in PLStream state (color, pen width, fill attribute, etc). //-------------------------------------------------------------------------- void plD_state_tkwin( PLStream *pls, PLINT op ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "plD_state_tkw" ); if ( dev->flags & 1 ) return; switch ( op ) { case PLSTATE_WIDTH: break; case PLSTATE_COLOR0: { int icol0 = pls->icol0; if ( tkwd->color ) { if ( icol0 == PL_RGB_COLOR ) { PLColor_to_TkColor( &pls->curcolor, &dev->curcolor ); Tkw_StoreColor( pls, tkwd, &dev->curcolor ); } else { dev->curcolor = tkwd->cmap0[icol0]; } XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } else { dev->curcolor = tkwd->fgcolor; XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } break; } case PLSTATE_COLOR1: { int icol1; if ( tkwd->ncol1 == 0 ) AllocCmap1( pls ); if ( tkwd->ncol1 < 2 ) break; icol1 = ( pls->icol1 * ( tkwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 ); if ( tkwd->color ) dev->curcolor = tkwd->cmap1[icol1]; else dev->curcolor = tkwd->fgcolor; XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); break; } case PLSTATE_CMAP0: pltkwin_setBGFG( pls ); StoreCmap0( pls ); break; case PLSTATE_CMAP1: StoreCmap1( pls ); break; } } //-------------------------------------------------------------------------- // plD_esc_tkwin() // // Escape function. // // Functions: // // PLESC_EH Handle pending events // PLESC_EXPOSE Force an expose // PLESC_FILL Fill polygon // PLESC_FLUSH Flush X event buffer // PLESC_GETC Get coordinates upon mouse click // PLESC_REDRAW Force a redraw // PLESC_RESIZE Force a resize //-------------------------------------------------------------------------- void plD_esc_tkwin( PLStream *pls, PLINT op, void *ptr ) { TkwDev *dev = (TkwDev *) pls->dev; #ifndef USE_TK TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #endif dbug_enter( "plD_esc_tkw" ); if ( dev->flags & 1 ) return; switch ( op ) { case PLESC_EH: #ifndef USE_TK HandleEvents( pls ); #endif break; case PLESC_EXPOSE: ExposeCmd( pls, (PLDisplay *) ptr ); break; case PLESC_FILL: FillPolygonCmd( pls ); break; case PLESC_FLUSH: #ifndef USE_TK HandleEvents( pls ); XFlush( tkwd->display ); #endif break; case PLESC_GETC: #ifndef USE_TK GetCursorCmd( pls, (PLGraphicsIn *) ptr ); #endif break; case PLESC_REDRAW: RedrawCmd( pls ); break; case PLESC_RESIZE: ResizeCmd( pls, (PLDisplay *) ptr ); break; // Added by Vince, disabled by default since we want a minimal patch #ifdef USING_PLESC_COPY case PLESC_COPY: CopyCommand( pls ); break; #endif } } #ifdef USING_PLESC_COPY //-------------------------------------------------------------------------- // CopyCommand() // // Copy a rectangle to a new part of the image. // Points described in first 3 elements of pls->dev_x[] and pls->dev_y[]. //-------------------------------------------------------------------------- static void CopyCommand( PLStream *pls ) { int x0, w, x1, y0, h, y1; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; x0 = (int) ( dev->xscale * pls->dev_x[0] ); x1 = (int) ( dev->xscale * pls->dev_x[2] ); y0 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[0] ) ); y1 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[2] ) ); w = (int) ( dev->xscale * ( pls->dev_x[1] - pls->dev_x[0] ) ); h = (int) ( -dev->yscale * ( pls->dev_y[1] - pls->dev_y[0] ) ); if ( dev->write_to_window ) XCopyArea( tkwd->display, dev->window, dev->window, dev->gc, x0, y0, w, h, x1, y1 ); if ( dev->write_to_pixmap ) XCopyArea( tkwd->display, dev->pixmap, dev->pixmap, dev->gc, x0, y0, w, h, x1, y1 ); } #endif //-------------------------------------------------------------------------- // FillPolygonCmd() // // Fill polygon described in points pls->dev_x[] and pls->dev_y[]. // Only solid color fill supported. //-------------------------------------------------------------------------- static void FillPolygonCmd( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XPoint _pts[PL_MAXPOLY]; XPoint *pts; int i; if ( pls->dev_npts > PL_MAXPOLY ) { pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) ); } else { pts = _pts; } for ( i = 0; i < pls->dev_npts; i++ ) { pts[i].x = (short) ( dev->xscale * pls->dev_x[i] ); pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) ); } // Fill polygons if ( dev->write_to_window ) XFillPolygon( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts, Nonconvex, CoordModeOrigin ); if ( dev->write_to_pixmap ) XFillPolygon( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, Nonconvex, CoordModeOrigin ); // If in debug mode, draw outline of boxes being filled #ifdef DEBUG if ( pls->debug ) { XSetForeground( tkwd->display, dev->gc, tkwd->fgcolor.pixel ); if ( dev->write_to_window ) XDrawLines( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); if ( dev->write_to_pixmap ) XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts, CoordModeOrigin ); XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel ); } #endif if ( pls->dev_npts > PL_MAXPOLY ) { free( pts ); } } //-------------------------------------------------------------------------- // Init() // // Xlib initialization routine. // // Controlling routine for X window creation and/or initialization. // The user may customize the window in the following ways: // // display: pls->OutFile (use plsfnam() or -display option) // size: pls->xlength, pls->ylength (use plspage() or -geo option) // bg color: pls->cmap0[0] (use plscolbg() or -bg option) //-------------------------------------------------------------------------- static void Init( PLStream *pls ) { PlPlotter *plf; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "Init" ); dev->window = (Window) pls->window_id; plf = pls->plPlotterPtr; if ( plf == NULL ) { plwarn( "Init: Illegal call --- driver can't find enclosing PlPlotter" ); return; } // Initialize colors InitColors( pls ); #ifndef MAC_TCL XSetWindowColormap( tkwd->display, dev->window, tkwd->map ); #else #endif // Set up GC for ordinary draws if ( !dev->gc ) dev->gc = XCreateGC( tkwd->display, dev->window, 0, 0 ); // Set up GC for rubber-band draws if ( !tkwd->gcXor ) { XGCValues gcValues; unsigned long mask; gcValues.background = tkwd->cmap0[0].pixel; gcValues.foreground = 0xFF; gcValues.function = GXxor; mask = GCForeground | GCBackground | GCFunction; tkwd->gcXor = XCreateGC( tkwd->display, dev->window, mask, &gcValues ); } // Get initial drawing area dimensions dev->width = (unsigned int) Tk_Width( plf->tkwin ); dev->height = (unsigned int) Tk_Height( plf->tkwin ); dev->border = (unsigned int) Tk_InternalBorderWidth( plf->tkwin ); tkwd->depth = (unsigned int) Tk_Depth( plf->tkwin ); dev->init_width = dev->width; dev->init_height = dev->height; // Set up flags that determine what we are writing to // If nopixmap is set, ignore db if ( pls->nopixmap ) { dev->write_to_pixmap = 0; pls->db = 0; } else { dev->write_to_pixmap = 1; } dev->write_to_window = !pls->db; // Create pixmap for holding plot image (for expose events). if ( dev->write_to_pixmap ) CreatePixmap( pls ); // Set drawing color plD_state_tkwin( pls, PLSTATE_COLOR0 ); XSetWindowBackground( tkwd->display, dev->window, tkwd->cmap0[0].pixel ); XSetBackground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel ); } //-------------------------------------------------------------------------- // ExposeCmd() // // Event handler routine for expose events. // These are "pure" exposures (no resize), so don't need to clear window. //-------------------------------------------------------------------------- static void ExposeCmd( PLStream *pls, PLDisplay *pldis ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int x, y, width, height; dbug_enter( "ExposeCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "ExposeCmd: Illegal call -- driver uninitialized" ); return; } // Exposed area. If unspecified, the entire window is used. if ( pldis == NULL ) { x = 0; y = 0; width = (int) dev->width; height = (int) dev->height; } else { x = (int) pldis->x; y = (int) pldis->y; width = (int) pldis->width; height = (int) pldis->height; } // Usual case: refresh window from pixmap // DEBUG option: draws rectangle around refreshed region XSync( tkwd->display, 0 ); if ( dev->write_to_pixmap ) { XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, x, y, (unsigned int) width, (unsigned int) height, x, y ); XSync( tkwd->display, 0 ); #ifdef DEBUG if ( pls->debug ) { XPoint pts[5]; int x0 = x, x1 = x + width, y0 = y, y1 = y + height; pts[0].x = (short) x0; pts[0].y = (short) y0; pts[1].x = (short) x1; pts[1].y = (short) y0; pts[2].x = (short) x1; pts[2].y = (short) y1; pts[3].x = (short) x0; pts[3].y = (short) y1; pts[4].x = (short) x0; pts[4].y = (short) y0; XDrawLines( tkwd->display, dev->window, dev->gc, pts, 5, CoordModeOrigin ); } #endif } else { plRemakePlot( pls ); XFlush( tkwd->display ); } } //-------------------------------------------------------------------------- // ResizeCmd() // // Event handler routine for resize events. //-------------------------------------------------------------------------- static void ResizeCmd( PLStream *pls, PLDisplay *pldis ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int write_to_window = dev->write_to_window; dbug_enter( "ResizeCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "ResizeCmd: Illegal call -- driver uninitialized" ); return; } // Return if pointer to window not specified. if ( pldis == NULL ) { plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" ); return; } // Reset current window bounds dev->width = pldis->width; dev->height = pldis->height; dev->xscale = dev->width / (double) dev->init_width; dev->yscale = dev->height / (double) dev->init_height; dev->xscale = dev->xscale * dev->xscale_init; dev->yscale = dev->yscale * dev->yscale_init; #if PHYSICAL { float pxlx = (double) PIXELS_X / dev->width * DPMM; float pxly = (double) PIXELS_Y / dev->height * DPMM; plP_setpxl( pxlx, pxly ); } #endif // Note: the following order MUST be obeyed -- if you instead redraw into // the window and then copy it to the pixmap, off-screen parts of the window // may contain garbage which is then transferred to the pixmap (and thus // will not go away after an expose). // // Resize pixmap using new dimensions if ( dev->write_to_pixmap ) { dev->write_to_window = 0; #if defined ( _WIN32 ) || defined ( MAC_TCL ) Tk_FreePixmap( tkwd->display, dev->pixmap ); #else // Vince's original driver code used // Tk_FreePixmap(tkwd->display, dev->pixmap); //which is defined in tk-8.3 (and 8.2?) source as //void // Tk_FreePixmap(display, pixmap) // Display *display; // Pixmap pixmap; // { // XFreePixmap(display, pixmap); // Tk_FreeXId(display, (XID) pixmap); // } // But that bombed under Linux and tcl/tk8.2 so now just call // XFreePixmap directly. (Not recommended as permanent solution // because you eventually run out of resources according to man // page if you don't call Tk_FreeXId.) Vince is still looking into // how to resolve this problem. // XFreePixmap( tkwd->display, dev->pixmap ); #endif CreatePixmap( pls ); } // Initialize & redraw (to pixmap, if available). plD_bop_tkwin( pls ); plRemakePlot( pls ); XSync( tkwd->display, 0 ); // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { dev->write_to_window = write_to_window; XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( tkwd->display, 0 ); } } //-------------------------------------------------------------------------- // RedrawCmd() // // Handles page redraw without resize (pixmap does not get reallocated). // Calling this makes sure all necessary housekeeping gets done. //-------------------------------------------------------------------------- static void RedrawCmd( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int write_to_window = dev->write_to_window; dbug_enter( "RedrawCmd" ); // Return if plD_init_tkw hasn't been called yet if ( dev == NULL ) { plwarn( "RedrawCmd: Illegal call -- driver uninitialized" ); return; } // Initialize & redraw (to pixmap, if available). if ( dev->write_to_pixmap ) dev->write_to_window = 0; plD_bop_tkwin( pls ); plRemakePlot( pls ); XSync( tkwd->display, 0 ); dev->write_to_window = write_to_window; // If pixmap available, fake an expose if ( dev->write_to_pixmap ) { XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0, dev->width, dev->height, 0, 0 ); XSync( tkwd->display, 0 ); } } //-------------------------------------------------------------------------- // CreatePixmap() // // This routine creates a pixmap, doing error trapping in case there // isn't enough memory on the server. //-------------------------------------------------------------------------- static void CreatePixmap( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; Tk_Window tkwin = pls->plPlotterPtr->tkwin; #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) int ( *oldErrorHandler )( Display *, XErrorEvent * ); oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler ); CreatePixmapStatus = Success; #endif #ifdef MAC_TCL // MAC_TCL's version of XCreatePixmap doesn't like 0 by 0 maps if ( dev->width == 0 ) { dev->width = 10; } if ( dev->height == 0 ) { dev->height = 10; } #endif pldebug( "CreatePixmap", "creating pixmap: width = %d, height = %d, depth = %d\n", dev->width, dev->height, tkwd->depth ); // // dev->pixmap = Tk_GetPixmap(tkwd->display, dev->window, // dev->width, dev->height, tkwd->depth); // // // Vince's original driver code used Tk_Display(tkwin) for first argument, // but that bombed on an Linux tcl/tk 8.2 machine. Something was wrong // with that value. Thus, we now use tkwd->display, and that works well. // Vince is looking into why Tk_Display(tkwin) is badly defined under 8.2. // old code: // // dev->pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), // Tk_Width(tkwin), Tk_Height(tkwin), // DefaultDepthOfScreen(Tk_Screen(tkwin))); // dev->pixmap = Tk_GetPixmap( tkwd->display, Tk_WindowId( tkwin ), Tk_Width( tkwin ), Tk_Height( tkwin ), DefaultDepthOfScreen( Tk_Screen( tkwin ) ) ); XSync( tkwd->display, 0 ); #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) if ( CreatePixmapStatus != Success ) { dev->write_to_pixmap = 0; dev->write_to_window = 1; pls->db = 0; fprintf( stderr, "\n\ Warning: pixmap could not be allocated (insufficient memory on server).\n\ Driver will redraw the entire plot to handle expose events.\n" ); } XSetErrorHandler( oldErrorHandler ); #endif } //-------------------------------------------------------------------------- // GetVisual() // // Get visual info. In order to safely use a visual other than that of // the parent (which hopefully is that returned by DefaultVisual), you // must first find (using XGetRGBColormaps) or create a colormap matching // this visual and then set the colormap window attribute in the // XCreateWindow attributes and valuemask arguments. I don't do this // right now, so this is turned off by default. //-------------------------------------------------------------------------- static void GetVisual( PLStream *pls ) { int depth; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "GetVisual" ); tkwd->visual = Tk_GetVisual( pls->plPlotterPtr->interp, pls->plPlotterPtr->tkwin, "best", &depth, NULL ); tkwd->depth = (unsigned int) depth; } //-------------------------------------------------------------------------- // AllocBGFG() // // Allocate background & foreground colors. If possible, I choose pixel // values such that the fg pixel is the xor of the bg pixel, to make // rubber-banding easy to see. //-------------------------------------------------------------------------- static void AllocBGFG( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #ifndef USE_TK int i, j, npixels; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocBGFG" ); // If not on a color system, just return if ( !tkwd->color ) return; #ifndef USE_TK // Allocate r/w color cell for background if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, 1 ) ) { tkwd->cmap0[0].pixel = pixels[0]; } else { plexit( "couldn't allocate background color cell" ); } // Allocate as many colors as we can npixels = MAX_COLORS; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) break; } // Find the color with pixel = xor of the bg color pixel. // If a match isn't found, the last pixel allocated is used. for ( i = 0; i < npixels - 1; i++ ) { if ( pixels[i] == ( ~tkwd->cmap0[0].pixel & 0xFF ) ) break; } // Use this color cell for our foreground color. Then free the rest. tkwd->fgcolor.pixel = pixels[i]; for ( j = 0; j < npixels; j++ ) { if ( j != i ) XFreeColors( tkwd->display, tkwd->map, &pixels[j], 1, 0 ); } #endif } //-------------------------------------------------------------------------- // pltkwin_setBGFG() // // Set background & foreground colors. Foreground over background should // have high contrast. //-------------------------------------------------------------------------- void pltkwin_setBGFG( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLColor fgcolor; int gslevbg, gslevfg; dbug_enter( "pltkwin_setBGFG" ); // // Set background color. // // Background defaults to black on color screens, white on grayscale (many // grayscale monitors have poor contrast, and black-on-white looks better). // if ( !tkwd->color ) { pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF; } gslevbg = (int) ( ( (long) pls->cmap0[0].r + (long) pls->cmap0[0].g + (long) pls->cmap0[0].b ) / 3 ); PLColor_to_TkColor( &pls->cmap0[0], &tkwd->cmap0[0] ); // // Set foreground color. // // Used for grayscale output, since otherwise the plots can become nearly // unreadable (i.e. if colors get mapped onto grayscale values). In this // case it becomes the grayscale level for all draws, and is taken to be // black if the background is light, and white if the background is dark. // Note that white/black allocations never fail. // if ( gslevbg > 0x7F ) gslevfg = 0; else gslevfg = 0xFF; fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg; PLColor_to_TkColor( &fgcolor, &tkwd->fgcolor ); // Now store #ifndef USE_TK if ( tkwd->color ) { XStoreColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] ); } else { XAllocColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] ); XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); } #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[0] ); Tkw_StoreColor( pls, tkwd, &tkwd->fgcolor ); #endif } //-------------------------------------------------------------------------- // InitColors() // // Does all color initialization. //-------------------------------------------------------------------------- static void InitColors( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; dbug_enter( "InitColors" ); // Allocate and initialize color maps. // Defer cmap1 allocation until it's actually used if ( tkwd->color ) { if ( plplot_tkwin_ccmap ) { AllocCustomMap( pls ); } else { AllocCmap0( pls ); } } } //-------------------------------------------------------------------------- // AllocCustomMap() // // Initializes custom color map and all the cruft that goes with it. // // Assuming all color X displays do 256 colors, the breakdown is as follows: // // XWM_COLORS Number of low "pixel" values to copy. These are typically // allocated first, thus are in use by the window manager. I // copy them to reduce flicker. // // CMAP0_COLORS Color map 0 entries. I allocate these both in the default // colormap and the custom colormap to reduce flicker. // // CMAP1_COLORS Color map 1 entries. There should be as many as practical // available for smooth shading. On the order of 50-100 is // pretty reasonable. You don't really need all 256, // especially if all you're going to do is to print it to // postscript (which doesn't have any intrinsic limitation on // the number of colors). // // It's important to leave some extra colors unallocated for Tk. In // particular the palette tools require a fair amount. I recommend leaving // at least 40 or so free. //-------------------------------------------------------------------------- static void AllocCustomMap( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XColor xwm_colors[MAX_COLORS]; int i; #ifndef USE_TK int npixels; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCustomMap" ); // Determine current default colors for ( i = 0; i < MAX_COLORS; i++ ) { xwm_colors[i].pixel = (long unsigned) i; } #ifndef MAC_TCL XQueryColors( tkwd->display, tkwd->map, xwm_colors, MAX_COLORS ); #endif // Allocate cmap0 colors in the default colormap. // The custom cmap0 colors are later stored at the same pixel values. // This is a really cool trick to reduce the flicker when changing colormaps. // AllocCmap0( pls ); XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor ); // Create new color map tkwd->map = XCreateColormap( tkwd->display, DefaultRootWindow( tkwd->display ), tkwd->visual, AllocNone ); // Now allocate all colors so we can fill the ones we want to copy #ifndef USE_TK npixels = MAX_COLORS; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } // Fill the low colors since those are in use by the window manager for ( i = 0; i < XWM_COLORS; i++ ) { XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] ); pixels[xwm_colors[i].pixel] = 0; } // Fill the ones we will use in cmap0 for ( i = 0; i < tkwd->ncol0; i++ ) { XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] ); pixels[tkwd->cmap0[i].pixel] = 0; } // Finally, if the colormap was saved by an external agent, see if there are // any differences from the current default map and save those! A very cool // (or sick, depending on how you look at it) trick to get over some X and // Tk limitations. // if ( sxwm_colors_set ) { for ( i = 0; i < MAX_COLORS; i++ ) { if ( ( xwm_colors[i].red != sxwm_colors[i].red ) || ( xwm_colors[i].green != sxwm_colors[i].green ) || ( xwm_colors[i].blue != sxwm_colors[i].blue ) ) { if ( pixels[i] != 0 ) { XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] ); pixels[i] = 0; } } } } // Now free the ones we're not interested in for ( i = 0; i < npixels; i++ ) { if ( pixels[i] != 0 ) XFreeColors( tkwd->display, tkwd->map, &pixels[i], 1, 0 ); } #endif // Allocate colors in cmap 1 AllocCmap1( pls ); } //-------------------------------------------------------------------------- // AllocCmap0() // // Allocate & initialize cmap0 entries. //-------------------------------------------------------------------------- static void AllocCmap0( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; #ifndef USE_TK int npixels; int i; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCmap0" ); // Allocate and assign colors in cmap 0 #ifndef USE_TK npixels = pls->ncol0 - 1; for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, &pixels[1], npixels ) ) break; npixels--; if ( npixels == 0 ) plexit( "couldn't allocate any colors" ); } tkwd->ncol0 = npixels + 1; for ( i = 1; i < tkwd->ncol0; i++ ) { tkwd->cmap0[i].pixel = pixels[i]; } #else // We use the Tk color scheme tkwd->ncol0 = pls->ncol0; #endif StoreCmap0( pls ); } //-------------------------------------------------------------------------- // AllocCmap1() // // Allocate & initialize cmap1 entries. If using the default color map, // must severely limit number of colors otherwise TK won't have enough. //-------------------------------------------------------------------------- static void AllocCmap1( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int npixels; #ifndef USE_TK int i, j; unsigned long plane_masks[1], pixels[MAX_COLORS]; #endif dbug_enter( "AllocCmap1" ); // Allocate colors in cmap 1 npixels = MAX( 2, MIN( CMAP1_COLORS, pls->ncol1 ) ); #ifndef USE_TK for (;; ) { if ( XAllocColorCells( tkwd->display, tkwd->map, False, plane_masks, 0, pixels, npixels ) ) break; npixels--; if ( npixels == 0 ) break; } if ( npixels < 2 ) { tkwd->ncol1 = -1; fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1\n" ); return; } else { tkwd->ncol1 = npixels; if ( pls->verbose ) fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", npixels ); } // Don't assign pixels sequentially, to avoid strange problems with xor GC's // Skipping by 2 seems to do the job best for ( j = i = 0; i < tkwd->ncol1; i++ ) { while ( pixels[j] == 0 ) j++; tkwd->cmap1[i].pixel = pixels[j]; pixels[j] = 0; j += 2; if ( j >= tkwd->ncol1 ) j = 0; } #else tkwd->ncol1 = npixels; #endif StoreCmap1( pls ); } //-------------------------------------------------------------------------- // StoreCmap0() // // Stores cmap 0 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap0( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; int i; if ( !tkwd->color ) return; for ( i = 1; i < tkwd->ncol0; i++ ) { PLColor_to_TkColor( &pls->cmap0[i], &tkwd->cmap0[i] ); #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] ); #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[i] ); #endif } } void CopyColour( XColor* from, XColor* to ) { to->pixel = from->pixel; to->red = from->red; to->blue = from->blue; to->green = from->green; to->flags = from->flags; } //-------------------------------------------------------------------------- // StoreCmap1() // // Stores cmap 1 entries in X-server colormap. //-------------------------------------------------------------------------- static void StoreCmap1( PLStream *pls ) { TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; PLColor cmap1color; int i; if ( !tkwd->color ) return; for ( i = 0; i < tkwd->ncol1; i++ ) { plcol_interp( pls, &cmap1color, i, tkwd->ncol1 ); PLColor_to_TkColor( &cmap1color, &tkwd->cmap1[i] ); #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap1[i] ); #else Tkw_StoreColor( pls, tkwd, &tkwd->cmap1[i] ); #endif } } void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col ) { XColor *xc; #ifndef USE_TK XStoreColor( tkwd->display, tkwd->map, col ); #else (void) tkwd; // tkwd unused in this case // We're probably losing memory here xc = Tk_GetColorByValue( pls->plPlotterPtr->tkwin, col ); CopyColour( xc, col ); #endif } //-------------------------------------------------------------------------- // void PLColor_to_TkColor() // // Copies the supplied PLColor to an XColor, padding with bits as necessary // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits). // The argument types follow the same order as in the function name. //-------------------------------------------------------------------------- #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) ) #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 ) void PLColor_to_TkColor( PLColor *plcolor, XColor *xcolor ) { xcolor->red = (short unsigned) ToXColor( plcolor->r ); xcolor->green = (short unsigned) ToXColor( plcolor->g ); xcolor->blue = (short unsigned) ToXColor( plcolor->b ); xcolor->flags = DoRed | DoGreen | DoBlue; } //-------------------------------------------------------------------------- // void PLColor_from_TkColor() // // Copies the supplied XColor to a PLColor, stripping off bits as // necessary. See the previous routine for more info. //-------------------------------------------------------------------------- void PLColor_from_TkColor( PLColor *plcolor, XColor *xcolor ) { plcolor->r = (unsigned char) ToPLColor( xcolor->red ); plcolor->g = (unsigned char) ToPLColor( xcolor->green ); plcolor->b = (unsigned char) ToPLColor( xcolor->blue ); } //-------------------------------------------------------------------------- // void PLColor_from_TkColor_Changed() // // Copies the supplied XColor to a PLColor, stripping off bits as // necessary. See the previous routine for more info. // // Returns 1 if the color was different from the old one. //-------------------------------------------------------------------------- int PLColor_from_TkColor_Changed( PLColor *plcolor, XColor *xcolor ) { int changed = 0; int color; color = ToPLColor( xcolor->red ); if ( plcolor->r != color ) { changed = 1; plcolor->r = (unsigned char) color; } color = ToPLColor( xcolor->green ); if ( plcolor->g != color ) { changed = 1; plcolor->g = (unsigned char) color; } color = ToPLColor( xcolor->blue ); if ( plcolor->b != color ) { changed = 1; plcolor->b = (unsigned char) color; } return changed; } //-------------------------------------------------------------------------- // int pltk_AreWeGrayscale(PlPlotter *plf) // // Determines if we're using a monochrome or grayscale device. // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland. // Changed July 1996 by Vince: now uses Tk to check the enclosing PlPlotter //-------------------------------------------------------------------------- static int pltk_AreWeGrayscale( PlPlotter *plf ) { #if defined ( __cplusplus ) || defined ( c_plusplus ) #define THING c_class #else #define THING class #endif Visual* visual; // get the window's Visual visual = Tk_Visual( plf->tkwin ); if ( ( visual->THING != GrayScale ) && ( visual->THING != StaticGray ) ) return ( 0 ); // if we got this far, only StaticGray and GrayScale classes available return ( 1 ); } #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) //-------------------------------------------------------------------------- // CreatePixmapErrorHandler() // // Error handler used in CreatePixmap() to catch errors in allocating // storage for pixmap. This way we can nicely substitute redraws for // pixmap copies if the server has insufficient memory. //-------------------------------------------------------------------------- static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error ) { if ( error->error_code == BadAlloc ) { CreatePixmapStatus = error->error_code; } else { char buffer[256]; XGetErrorText( display, error->error_code, buffer, 256 ); fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer ); } return 1; } #endif #else int pldummy_tkwin() { return 0; } #endif // PLD_tkwin void * ckcalloc( size_t nmemb, size_t size ) { long *ptr; long *p; size *= nmemb; ptr = (long *) malloc( size ); if ( !ptr ) return ( 0 ); #if !__POWERPC__ for ( size = ( size / sizeof ( long ) ) + 1, p = ptr; --size; ) *p++ = 0; #else for ( size = ( size / sizeof ( long ) ) + 1, p = ptr - 1; --size; ) *++p = 0; #endif return ( ptr ); }