PLplot  5.10.0
tkwin.c
Go to the documentation of this file.
00001 // PLplot Tk device driver.
00002 //
00003 // Copyright (C) 2004  Maurice LeBrun
00004 // Copyright (C) 2004  Joao Cardoso
00005 //
00006 // This file is part of PLplot.
00007 //
00008 // PLplot is free software; you can redistribute it and/or modify
00009 // it under the terms of the GNU Library General Public License as published
00010 // by the Free Software Foundation; either version 2 of the License, or
00011 // (at your option) any later version.
00012 //
00013 // PLplot is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public License
00019 // along with PLplot; if not, write to the Free Software
00020 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021 //
00022 // This device driver is designed to be used by a PlPlotter, and in fact requires
00023 // the existence of an enclosing PlPlotter.
00024 //
00025 // The idea is that this should develop into a completely cross-platform driver
00026 // for use by the cross platform Tk system.
00027 //
00028 //
00029 
00030 #include "plDevs.h"
00031 
00032 #define DEBUG
00033 
00034 #ifdef PLD_tkwin
00035 
00036 
00037 #define NEED_PLDEBUG
00038 #include "plplotP.h"
00039 #include "pltkwd.h"
00040 #include "drivers.h"
00041 #include "plevent.h"
00042 
00043 #define _TCLINT
00044 #ifdef USE_TCL_STUBS
00045 // Unfortunately, tkInt.h ends up loading Malloc.h under Windows
00046 // So we have to deal with this mess
00047     #undef malloc
00048     #undef free
00049     #undef realloc
00050     #undef calloc
00051 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
00052 #include <tkInt.h>
00053 #else
00054 #include <tk.h>
00055 #endif
00056     #define malloc     ckalloc
00057     #define free( m )    ckfree( (char *) m )
00058     #define realloc    ckrealloc
00059     #define calloc     ckcalloc
00060 #else
00061 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
00062 #include <tkInt.h>
00063 #else
00064 #include <tk.h>
00065 #endif
00066 #endif
00067 
00068 #ifdef ckalloc
00069 #undef ckalloc
00070 #define ckalloc    malloc
00071 #endif
00072 #ifdef ckfree
00073 #undef ckfree
00074 #define ckfree    free
00075 #endif
00076 #ifdef free
00077 #undef free
00078 #endif
00079 
00080 // Device info
00081 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tkwin = "tkwin:New tk driver:1:tkwin:45:tkwin\n";
00082 
00083 
00084 void *  ckcalloc( size_t nmemb, size_t size );
00085 
00086 //
00087 // We want to use the 'pure Tk' interface.  On Unix we can use
00088 // some direct calls to X instead of Tk, if we want, although
00089 // that code hasn't been tested for some time.  So this define
00090 // is required on Windows/MacOS and perhaps optional on Unix.
00091 //
00092 #define USE_TK
00093 
00094 #ifdef __WIN32__
00095 #define XSynchronize( display, bool )    { display->request++; }
00096 #define XSync( display, bool )           { display->request++; }
00097 #define XFlush( display )
00098 #endif
00099 
00100 // Dummy definition of PlPlotter containing first few fields
00101 typedef struct PlPlotter
00102 {
00103     Tk_Window tkwin;    // Window that embodies the frame. NULL
00104                         // means that the window has been destroyed
00105                         // but the data structures haven't yet been
00106                         // cleaned up.
00107                         //
00108     Display *display;   // Display containing widget. Used, among
00109                         // other things, so that resources can be
00110                         // freed even after tkwin has gone away.
00111                         //
00112     Tcl_Interp *interp; // Interpreter associated with
00113                         // widget. Used to delete widget
00114                         // command.
00115                         //
00116 } PlPlotter;
00117 
00118 void CopyColour( XColor* from, XColor* to );
00119 void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col );
00120 static int  pltk_AreWeGrayscale( PlPlotter *plf );
00121 void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
00122 void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
00123 
00124 static int synchronize = 0; // change to 1 for synchronized operation
00125 // for debugging only
00126 
00127 // Number of instructions to skip between querying the X server for events
00128 
00129 #define MAX_INSTR    20
00130 
00131 // Pixels/mm
00132 
00133 #define PHYSICAL    0 // Enables physical scaling..
00134 
00135 // Set constants for dealing with colormap. In brief:
00136 //
00137 // ccmap  When set, turns on custom color map
00138 //
00139 // XWM_COLORS  Number of low "pixel" values to copy.
00140 // CMAP0_COLORS  Color map 0 entries.
00141 // CMAP1_COLORS  Color map 1 entries.
00142 // MAX_COLORS  Maximum colors period.
00143 //
00144 // See Init_CustomCmap() and Init_DefaultCmap() for more info.
00145 // Set ccmap at your own risk -- still under development.
00146 //
00147 
00148 // plplot_tkwin_ccmap is statically defined in pltkwd.h.  Note that
00149 // plplotter.c also includes that header and uses that variable.
00150 
00151 #define XWM_COLORS      70
00152 #define CMAP0_COLORS    16
00153 #define CMAP1_COLORS    50
00154 #define MAX_COLORS      256
00155 
00156 #ifndef USE_TK
00157 // Variables to hold RGB components of given colormap.
00158 // Used in an ugly hack to get past some X11R5 and TK limitations.
00159 
00160 static int    sxwm_colors_set;
00161 static XColor sxwm_colors[MAX_COLORS];
00162 #endif
00163 
00164 // Keep pointers to all the displays in use
00165 
00166 static TkwDisplay *tkwDisplay[PLTKDISPLAYS];
00167 
00168 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
00169 static unsigned char CreatePixmapStatus;
00170 static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error );
00171 #endif
00172 
00173 // Function prototypes
00174 // Initialization
00175 
00176 static void Init( PLStream *pls );
00177 static void InitColors( PLStream *pls );
00178 static void AllocCustomMap( PLStream *pls );
00179 static void AllocCmap0( PLStream *pls );
00180 static void AllocCmap1( PLStream *pls );
00181 static void CreatePixmap( PLStream *pls );
00182 static void GetVisual( PLStream *pls );
00183 static void AllocBGFG( PLStream *pls );
00184 
00185 // Escape function commands
00186 
00187 static void ExposeCmd( PLStream *pls, PLDisplay *ptr );
00188 static void RedrawCmd( PLStream *pls );
00189 static void ResizeCmd( PLStream *pls, PLDisplay *ptr );
00190 #ifndef USE_TK
00191 static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr );
00192 #endif
00193 static void FillPolygonCmd( PLStream *pls );
00194 #ifdef USING_PLESC_COPY
00195 static void CopyCommand( PLStream *pls );
00196 #endif
00197 
00198 // Miscellaneous
00199 
00200 static void StoreCmap0( PLStream *pls );
00201 static void StoreCmap1( PLStream *pls );
00202 static void WaitForPage( PLStream *pls );
00203 
00204 void plD_dispatch_init_tkwin( PLDispatchTable *pdt );
00205 
00206 void plD_init_tkwin( PLStream * );
00207 void plD_line_tkwin( PLStream *, short, short, short, short );
00208 void plD_polyline_tkwin( PLStream *, short *, short *, PLINT );
00209 void plD_eop_tkwin( PLStream * );
00210 void plD_bop_tkwin( PLStream * );
00211 void plD_tidy_tkwin( PLStream * );
00212 void plD_state_tkwin( PLStream *, PLINT );
00213 void plD_esc_tkwin( PLStream *, PLINT, void * );
00214 void plD_open_tkwin( PLStream *pls );
00215 
00216 void plD_dispatch_init_tkwin( PLDispatchTable *pdt )
00217 {
00218 #ifndef ENABLE_DYNDRIVERS
00219     pdt->pl_MenuStr = "PLplot Tk plotter";
00220     pdt->pl_DevName = "tkwin";
00221 #endif
00222     pdt->pl_type     = plDevType_Interactive;
00223     pdt->pl_seq      = 45;
00224     pdt->pl_init     = (plD_init_fp) plD_init_tkwin;
00225     pdt->pl_line     = (plD_line_fp) plD_line_tkwin;
00226     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tkwin;
00227     pdt->pl_eop      = (plD_eop_fp) plD_eop_tkwin;
00228     pdt->pl_bop      = (plD_bop_fp) plD_bop_tkwin;
00229     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_tkwin;
00230     pdt->pl_state    = (plD_state_fp) plD_state_tkwin;
00231     pdt->pl_esc      = (plD_esc_fp) plD_esc_tkwin;
00232 }
00233 
00234 //--------------------------------------------------------------------------
00235 // plD_init_tkwin()
00236 //
00237 // Initialize device.
00238 // Tk-dependent stuff done in plD_open_tkwin() and Init().
00239 //--------------------------------------------------------------------------
00240 
00241 void
00242 plD_init_tkwin( PLStream *pls )
00243 {
00244     TkwDev *dev;
00245     float  pxlx, pxly;
00246     int    xmin = 0;
00247     int    xmax = PIXELS_X - 1;
00248     int    ymin = 0;
00249     int    ymax = PIXELS_Y - 1;
00250 
00251     dbug_enter( "plD_init_tkw" );
00252 
00253     pls->termin      = 1; // Is an interactive terminal
00254     pls->dev_flush   = 1; // Handle our own flushes
00255     pls->dev_fill0   = 1; // Handle solid fills
00256     pls->plbuf_write = 1; // Activate plot buffer
00257 
00258     // The real meat of the initialization done here
00259 
00260     if ( pls->dev == NULL )
00261         plD_open_tkwin( pls );
00262 
00263     dev = (TkwDev *) pls->dev;
00264 
00265     Init( pls );
00266 
00267     // Get ready for plotting
00268 
00269     dev->xlen = (short) ( xmax - xmin );
00270     dev->ylen = (short) ( ymax - ymin );
00271 
00272     dev->xscale_init = (double) dev->init_width / (double) dev->xlen;
00273     dev->yscale_init = (double) dev->init_height / (double) dev->ylen;
00274 
00275     dev->xscale = dev->xscale_init;
00276     dev->yscale = dev->yscale_init;
00277 
00278 #if PHYSICAL
00279     pxlx = (PLFLT) ( (double) PIXELS_X / dev->width * DPMM );
00280     pxly = (PLFLT) ( (double) PIXELS_Y / dev->height * DPMM );
00281 #else
00282     pxlx = (PLFLT) ( (double) PIXELS_X / LPAGE_X );
00283     pxly = (PLFLT) ( (double) PIXELS_Y / LPAGE_Y );
00284 #endif
00285 
00286     plP_setpxl( pxlx, pxly );
00287     plP_setphy( xmin, xmax, ymin, ymax );
00288 }
00289 
00290 //--------------------------------------------------------------------------
00291 // plD_open_tkwin()
00292 //
00293 // Performs basic driver initialization, without actually opening or
00294 // modifying a window. May be called by the outside world before plinit
00295 // in case the caller needs early access to the driver internals (not
00296 // very common -- currently only used externally by plplotter).
00297 //--------------------------------------------------------------------------
00298 
00299 void
00300 plD_open_tkwin( PLStream *pls )
00301 {
00302     TkwDev     *dev;
00303     TkwDisplay *tkwd;
00304     int        i;
00305 
00306     dbug_enter( "plD_open_tkw" );
00307 
00308     // Allocate and initialize device-specific data
00309 
00310     if ( pls->dev != NULL )
00311         plwarn( "plD_open_tkw: device pointer is already set" );
00312 
00313     pls->dev = (TkwDev *) calloc( 1, (size_t) sizeof ( TkwDev ) );
00314     if ( pls->dev == NULL )
00315         plexit( "plD_init_tkw: Out of memory." );
00316 
00317     dev = (TkwDev *) pls->dev;
00318 
00319     // Variables used in querying the X server for events
00320 
00321     dev->instr     = 0;
00322     dev->max_instr = MAX_INSTR;
00323 
00324     // See if display matches any already in use, and if so use that
00325 
00326     dev->tkwd = NULL;
00327     for ( i = 0; i < PLTKDISPLAYS; i++ )
00328     {
00329         if ( tkwDisplay[i] == NULL )
00330         {
00331             continue;
00332         }
00333         else if ( pls->FileName == NULL && tkwDisplay[i]->displayName == NULL )
00334         {
00335             dev->tkwd = tkwDisplay[i];
00336             break;
00337         }
00338         else if ( pls->FileName == NULL || tkwDisplay[i]->displayName == NULL )
00339         {
00340             continue;
00341         }
00342         else if ( strcmp( tkwDisplay[i]->displayName, pls->FileName ) == 0 )
00343         {
00344             dev->tkwd = tkwDisplay[i];
00345             break;
00346         }
00347     }
00348 
00349     // If no display matched, create a new one
00350 
00351     if ( dev->tkwd == NULL )
00352     {
00353         dev->tkwd = (TkwDisplay *) calloc( 1, (size_t) sizeof ( TkwDisplay ) );
00354         if ( dev->tkwd == NULL )
00355             plexit( "Init: Out of memory." );
00356 
00357         for ( i = 0; i < PLTKDISPLAYS; i++ )
00358         {
00359             if ( tkwDisplay[i] == NULL )
00360                 break;
00361         }
00362         if ( i == PLTKDISPLAYS )
00363             plexit( "Init: Out of tkwDisplay's." );
00364 
00365         tkwDisplay[i]  = tkwd = (TkwDisplay *) dev->tkwd;
00366         tkwd->nstreams = 1;
00367 
00368         //
00369         // If we don't have a tk widget we're being called on, then
00370         // abort operations now
00371         //
00372         if ( pls->plPlotterPtr == NULL )
00373         {
00374             plexit( "No tk plframe widget to connect to" );
00375         }
00376         // Old version for MacOS Tk8.0
00377         //
00378         // char deflt[] = "Macintosh:0";
00379         // pls->FileName = deflt;
00380         // tkwd->display = (Display*) TkpOpenDisplay(pls->FileName);
00381         //
00382 
00383         // Open display
00384 #if defined ( MAC_TCL ) || defined ( __WIN32__ )
00385         if ( !pls->FileName )
00386         {
00387             //
00388             // Need to strdup because Tk has allocated the screen name,
00389             // but we will actually 'free' it later ourselves, and therefore
00390             // need to own the memory.
00391             //
00392             pls->FileName = plstrdup( TkGetDefaultScreenName( NULL, NULL ) );
00393         }
00394         tkwd->display = pls->plPlotterPtr->display;
00395 #else
00396         tkwd->display = XOpenDisplay( pls->FileName );
00397 #endif
00398         if ( tkwd->display == NULL )
00399         {
00400             plexit( "Can't open display" );
00401         }
00402         tkwd->displayName = pls->FileName;
00403         tkwd->screen      = DefaultScreen( tkwd->display );
00404         if ( synchronize )
00405         {
00406             XSynchronize( tkwd->display, 1 );
00407         }
00408         // Get colormap and visual
00409 
00410         tkwd->map = Tk_Colormap( pls->plPlotterPtr->tkwin );
00411         GetVisual( pls );
00412 
00413         //
00414         // Figure out if we have a color display or not.
00415         // Default is color IF the user hasn't specified and IF the output device is
00416         // not grayscale.
00417         //
00418 
00419         if ( pls->colorset )
00420             tkwd->color = pls->color;
00421         else
00422         {
00423             pls->color  = 1;
00424             tkwd->color = !pltk_AreWeGrayscale( pls->plPlotterPtr );
00425         }
00426 
00427         // Allocate & set background and foreground colors
00428 
00429         AllocBGFG( pls );
00430         pltkwin_setBGFG( pls );
00431     }
00432 
00433     // Display matched, so use existing display data
00434 
00435     else
00436     {
00437         tkwd = (TkwDisplay *) dev->tkwd;
00438         tkwd->nstreams++;
00439     }
00440     tkwd->ixwd = i;
00441 }
00442 
00443 //--------------------------------------------------------------------------
00444 // plD_line_tkwin()
00445 //
00446 // Draw a line in the current color from (x1,y1) to (x2,y2).
00447 //--------------------------------------------------------------------------
00448 
00449 void
00450 plD_line_tkwin( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
00451 {
00452     TkwDev     *dev  = (TkwDev *) pls->dev;
00453     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00454 
00455     int        x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
00456 
00457     if ( dev->flags & 1 )
00458         return;
00459 
00460     y1 = dev->ylen - y1;
00461     y2 = dev->ylen - y2;
00462 
00463     x1 = (int) ( x1 * dev->xscale );
00464     x2 = (int) ( x2 * dev->xscale );
00465     y1 = (int) ( y1 * dev->yscale );
00466     y2 = (int) ( y2 * dev->yscale );
00467 
00468     if ( dev->write_to_window )
00469         XDrawLine( tkwd->display, dev->window, dev->gc, x1, y1, x2, y2 );
00470 
00471     if ( dev->write_to_pixmap )
00472         XDrawLine( tkwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 );
00473 }
00474 
00475 //--------------------------------------------------------------------------
00476 // plD_polyline_tkwin()
00477 //
00478 // Draw a polyline in the current color from (x1,y1) to (x2,y2).
00479 //--------------------------------------------------------------------------
00480 
00481 void
00482 plD_polyline_tkwin( PLStream *pls, short *xa, short *ya, PLINT npts )
00483 {
00484     TkwDev     *dev  = (TkwDev *) pls->dev;
00485     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00486 
00487     PLINT      i;
00488     XPoint     _pts[PL_MAXPOLY];
00489     XPoint     *pts;
00490 
00491     if ( dev->flags & 1 )
00492         return;
00493 
00494     if ( npts > PL_MAXPOLY )
00495     {
00496         pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts );
00497     }
00498     else
00499     {
00500         pts = _pts;
00501     }
00502 
00503     for ( i = 0; i < npts; i++ )
00504     {
00505         pts[i].x = (short) ( dev->xscale * xa[i] );
00506         pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) );
00507     }
00508 
00509     if ( dev->write_to_window )
00510         XDrawLines( tkwd->display, dev->window, dev->gc, pts, npts,
00511             CoordModeOrigin );
00512 
00513     if ( dev->write_to_pixmap )
00514         XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, npts,
00515             CoordModeOrigin );
00516 
00517     if ( npts > PL_MAXPOLY )
00518     {
00519         free( pts );
00520     }
00521 }
00522 
00523 //--------------------------------------------------------------------------
00524 // plD_eop_tkwin()
00525 //
00526 // End of page. User must hit return (or third mouse button) to continue.
00527 //--------------------------------------------------------------------------
00528 
00529 void
00530 plD_eop_tkwin( PLStream *pls )
00531 {
00532     TkwDev     *dev  = (TkwDev *) pls->dev;
00533     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00534 
00535     dbug_enter( "plD_eop_tkw" );
00536     if ( dev->flags & 1 )
00537         return;
00538 
00539     XFlush( tkwd->display );
00540     if ( pls->db )
00541         ExposeCmd( pls, NULL );
00542 
00543     if ( !pls->nopause )
00544         WaitForPage( pls );
00545 }
00546 
00547 //--------------------------------------------------------------------------
00548 // WaitForPage()
00549 //
00550 // This routine waits for the user to advance the plot, while handling
00551 // all other events.
00552 //--------------------------------------------------------------------------
00553 
00554 static void
00555 WaitForPage( PLStream *pls )
00556 {
00557     PlPlotter *plf = pls->plPlotterPtr;
00558     TkwDev    *dev = (TkwDev *) pls->dev;
00559 
00560     dbug_enter( "WaitForPage" );
00561 
00562     dev->flags &= 1;
00563     if ( plf == NULL )
00564     {
00565         plwarn( "WaitForPage: Illegal call --- driver can't find enclosing PlPlotter" );
00566         return;
00567     }
00568     PlplotterAtEop( plf->interp, plf );
00569 
00570     while ( !( dev->flags ) && !Tcl_InterpDeleted( plf->interp ) && ( Tk_GetNumMainWindows() > 0 ) )
00571     {
00572         Tcl_DoOneEvent( 0 );
00573     }
00574 
00575     if ( Tcl_InterpDeleted( plf->interp ) || ( Tk_GetNumMainWindows() <= 0 ) )
00576     {
00577         dev->flags |= 1;
00578     }
00579 
00580     dev->flags &= 1;
00581 }
00582 
00583 //--------------------------------------------------------------------------
00584 // plD_bop_tkwin()
00585 //
00586 // Set up for the next page.
00587 //--------------------------------------------------------------------------
00588 
00589 void
00590 plD_bop_tkwin( PLStream *pls )
00591 {
00592     PlPlotter  *plf  = pls->plPlotterPtr;
00593     TkwDev     *dev  = (TkwDev *) pls->dev;
00594     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00595 
00596     XRectangle xrect;
00597     xrect.x      = 0; xrect.y = 0;
00598     xrect.width  = (short unsigned) dev->width;
00599     xrect.height = (short unsigned) dev->height;
00600 
00601     dbug_enter( "plD_bop_tkw" );
00602     if ( dev->flags & 1 )
00603         return;
00604 
00605     if ( dev->write_to_window )
00606     {
00607 #ifdef MAC_TCL
00608         // MacTk only has these X calls
00609         XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
00610         XFillRectangles( tkwd->display, dev->window, dev->gc, &xrect, 1 );
00611         XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00612 #else
00613         XClearWindow( tkwd->display, dev->window );
00614 #endif
00615     }
00616     if ( dev->write_to_pixmap )
00617     {
00618         XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
00619         XFillRectangles( tkwd->display, dev->pixmap, dev->gc, &xrect, 1 );
00620         XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00621     }
00622     XSync( tkwd->display, 0 );
00623     pls->page++;
00624     PlplotterAtBop( plf->interp, plf );
00625 }
00626 
00627 //--------------------------------------------------------------------------
00628 // plD_tidy_tkwin()
00629 //
00630 // Close graphics file
00631 //--------------------------------------------------------------------------
00632 
00633 void
00634 plD_tidy_tkwin( PLStream *pls )
00635 {
00636     TkwDev     *dev  = (TkwDev *) pls->dev;
00637     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00638 
00639     dbug_enter( "plD_tidy_tkw" );
00640 
00641     tkwd->nstreams--;
00642     if ( tkwd->nstreams == 0 )
00643     {
00644         int ixwd = tkwd->ixwd;
00645         XFreeGC( tkwd->display, dev->gc );
00646 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
00647         XCloseDisplay( tkwd->display );
00648 #endif
00649         free_mem( tkwDisplay[ixwd] );
00650     }
00651     //
00652     // Vince removed this November 1999.  It seems as if a simple
00653     // 'plframe .p ; destroy .p' leaves a temporary buf file open
00654     // if we clear this flag here.  It should be checked and then
00655     // cleared by whoever called us.  An alternative fix would
00656     // be to carry out the check/tidy here.  The plframe widget
00657     // handles this stuff for us.
00658     //
00659     // pls->plbuf_write = 0;
00660 }
00661 
00662 //--------------------------------------------------------------------------
00663 // plD_state_tkwin()
00664 //
00665 // Handle change in PLStream state (color, pen width, fill attribute, etc).
00666 //--------------------------------------------------------------------------
00667 
00668 void
00669 plD_state_tkwin( PLStream *pls, PLINT op )
00670 {
00671     TkwDev     *dev  = (TkwDev *) pls->dev;
00672     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00673     dbug_enter( "plD_state_tkw" );
00674     if ( dev->flags & 1 )
00675         return;
00676 
00677     switch ( op )
00678     {
00679     case PLSTATE_WIDTH:
00680         break;
00681 
00682     case PLSTATE_COLOR0: {
00683         int icol0 = pls->icol0;
00684         if ( tkwd->color )
00685         {
00686             if ( icol0 == PL_RGB_COLOR )
00687             {
00688                 PLColor_to_TkColor( &pls->curcolor, &dev->curcolor );
00689                 Tkw_StoreColor( pls, tkwd, &dev->curcolor );
00690             }
00691             else
00692             {
00693                 dev->curcolor = tkwd->cmap0[icol0];
00694             }
00695             XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00696         }
00697         else
00698         {
00699             dev->curcolor = tkwd->fgcolor;
00700             XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00701         }
00702         break;
00703     }
00704 
00705     case PLSTATE_COLOR1: {
00706         int icol1;
00707 
00708         if ( tkwd->ncol1 == 0 )
00709             AllocCmap1( pls );
00710 
00711         if ( tkwd->ncol1 < 2 )
00712             break;
00713 
00714         icol1 = ( pls->icol1 * ( tkwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 );
00715         if ( tkwd->color )
00716             dev->curcolor = tkwd->cmap1[icol1];
00717         else
00718             dev->curcolor = tkwd->fgcolor;
00719 
00720         XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00721         break;
00722     }
00723 
00724     case PLSTATE_CMAP0:
00725         pltkwin_setBGFG( pls );
00726         StoreCmap0( pls );
00727         break;
00728 
00729     case PLSTATE_CMAP1:
00730         StoreCmap1( pls );
00731         break;
00732     }
00733 }
00734 
00735 //--------------------------------------------------------------------------
00736 // plD_esc_tkwin()
00737 //
00738 // Escape function.
00739 //
00740 // Functions:
00741 //
00742 // PLESC_EH Handle pending events
00743 // PLESC_EXPOSE Force an expose
00744 // PLESC_FILL Fill polygon
00745 // PLESC_FLUSH Flush X event buffer
00746 // PLESC_GETC Get coordinates upon mouse click
00747 // PLESC_REDRAW Force a redraw
00748 // PLESC_RESIZE Force a resize
00749 //--------------------------------------------------------------------------
00750 
00751 void
00752 plD_esc_tkwin( PLStream *pls, PLINT op, void *ptr )
00753 {
00754     TkwDev     *dev = (TkwDev *) pls->dev;
00755 #ifndef USE_TK
00756     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00757 #endif
00758     dbug_enter( "plD_esc_tkw" );
00759     if ( dev->flags & 1 )
00760         return;
00761 
00762     switch ( op )
00763     {
00764     case PLESC_EH:
00765 #ifndef USE_TK
00766         HandleEvents( pls );
00767 #endif
00768         break;
00769 
00770     case PLESC_EXPOSE:
00771         ExposeCmd( pls, (PLDisplay *) ptr );
00772         break;
00773 
00774     case PLESC_FILL:
00775         FillPolygonCmd( pls );
00776         break;
00777 
00778     case PLESC_FLUSH:
00779 #ifndef USE_TK
00780         HandleEvents( pls );
00781         XFlush( tkwd->display );
00782 #endif
00783         break;
00784 
00785     case PLESC_GETC:
00786 #ifndef USE_TK
00787         GetCursorCmd( pls, (PLGraphicsIn *) ptr );
00788 #endif
00789         break;
00790 
00791     case PLESC_REDRAW:
00792         RedrawCmd( pls );
00793         break;
00794 
00795     case PLESC_RESIZE:
00796         ResizeCmd( pls, (PLDisplay *) ptr );
00797         break;
00798 
00799 // Added by Vince, disabled by default since we want a minimal patch
00800 #ifdef USING_PLESC_COPY
00801     case PLESC_COPY:
00802         CopyCommand( pls );
00803         break;
00804 #endif
00805     }
00806 }
00807 
00808 #ifdef USING_PLESC_COPY
00809 //--------------------------------------------------------------------------
00810 // CopyCommand()
00811 //
00812 // Copy a rectangle to a new part of the image.
00813 // Points described in first 3 elements of pls->dev_x[] and pls->dev_y[].
00814 //--------------------------------------------------------------------------
00815 
00816 static void
00817 CopyCommand( PLStream *pls )
00818 {
00819     int        x0, w, x1, y0, h, y1;
00820     TkwDev     *dev  = (TkwDev *) pls->dev;
00821     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00822 
00823     x0 = (int) ( dev->xscale * pls->dev_x[0] );
00824     x1 = (int) ( dev->xscale * pls->dev_x[2] );
00825     y0 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[0] ) );
00826     y1 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[2] ) );
00827     w  = (int) ( dev->xscale * ( pls->dev_x[1] - pls->dev_x[0] ) );
00828     h  = (int) ( -dev->yscale * ( pls->dev_y[1] - pls->dev_y[0] ) );
00829 
00830     if ( dev->write_to_window )
00831         XCopyArea( tkwd->display, dev->window, dev->window, dev->gc,
00832             x0, y0, w, h, x1, y1 );
00833 
00834     if ( dev->write_to_pixmap )
00835         XCopyArea( tkwd->display, dev->pixmap, dev->pixmap, dev->gc,
00836             x0, y0, w, h, x1, y1 );
00837 }
00838 #endif
00839 
00840 //--------------------------------------------------------------------------
00841 // FillPolygonCmd()
00842 //
00843 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
00844 // Only solid color fill supported.
00845 //--------------------------------------------------------------------------
00846 
00847 static void
00848 FillPolygonCmd( PLStream *pls )
00849 {
00850     TkwDev     *dev  = (TkwDev *) pls->dev;
00851     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00852     XPoint     _pts[PL_MAXPOLY];
00853     XPoint     *pts;
00854     int        i;
00855 
00856     if ( pls->dev_npts > PL_MAXPOLY )
00857     {
00858         pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) );
00859     }
00860     else
00861     {
00862         pts = _pts;
00863     }
00864 
00865     for ( i = 0; i < pls->dev_npts; i++ )
00866     {
00867         pts[i].x = (short) ( dev->xscale * pls->dev_x[i] );
00868         pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) );
00869     }
00870 
00871 // Fill polygons
00872 
00873     if ( dev->write_to_window )
00874         XFillPolygon( tkwd->display, dev->window, dev->gc,
00875             pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
00876 
00877     if ( dev->write_to_pixmap )
00878         XFillPolygon( tkwd->display, dev->pixmap, dev->gc,
00879             pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
00880 
00881 // If in debug mode, draw outline of boxes being filled
00882 
00883 #ifdef DEBUG
00884     if ( pls->debug )
00885     {
00886         XSetForeground( tkwd->display, dev->gc, tkwd->fgcolor.pixel );
00887         if ( dev->write_to_window )
00888             XDrawLines( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts,
00889                 CoordModeOrigin );
00890 
00891         if ( dev->write_to_pixmap )
00892             XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts,
00893                 CoordModeOrigin );
00894 
00895         XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
00896     }
00897 #endif
00898 
00899     if ( pls->dev_npts > PL_MAXPOLY )
00900     {
00901         free( pts );
00902     }
00903 }
00904 
00905 //--------------------------------------------------------------------------
00906 // Init()
00907 //
00908 // Xlib initialization routine.
00909 //
00910 // Controlling routine for X window creation and/or initialization.
00911 // The user may customize the window in the following ways:
00912 //
00913 // display: pls->OutFile (use plsfnam() or -display option)
00914 // size: pls->xlength, pls->ylength (use plspage() or -geo option)
00915 // bg color: pls->cmap0[0] (use plscolbg() or -bg option)
00916 //--------------------------------------------------------------------------
00917 
00918 static void
00919 Init( PLStream *pls )
00920 {
00921     PlPlotter  *plf;
00922     TkwDev     *dev  = (TkwDev *) pls->dev;
00923     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
00924 
00925     dbug_enter( "Init" );
00926 
00927     dev->window = (Window) pls->window_id;
00928 
00929     plf = pls->plPlotterPtr;
00930     if ( plf == NULL )
00931     {
00932         plwarn( "Init: Illegal call --- driver can't find enclosing PlPlotter" );
00933         return;
00934     }
00935 
00936 // Initialize colors
00937     InitColors( pls );
00938 #ifndef MAC_TCL
00939     XSetWindowColormap( tkwd->display, dev->window, tkwd->map );
00940 #else
00941 #endif
00942 
00943 // Set up GC for ordinary draws
00944     if ( !dev->gc )
00945         dev->gc = XCreateGC( tkwd->display, dev->window, 0, 0 );
00946 
00947 // Set up GC for rubber-band draws
00948     if ( !tkwd->gcXor )
00949     {
00950         XGCValues     gcValues;
00951         unsigned long mask;
00952 
00953         gcValues.background = tkwd->cmap0[0].pixel;
00954         gcValues.foreground = 0xFF;
00955         gcValues.function   = GXxor;
00956         mask = GCForeground | GCBackground | GCFunction;
00957 
00958         tkwd->gcXor = XCreateGC( tkwd->display, dev->window, mask, &gcValues );
00959     }
00960 
00961 // Get initial drawing area dimensions
00962     dev->width  = (unsigned int) Tk_Width( plf->tkwin );
00963     dev->height = (unsigned int) Tk_Height( plf->tkwin );
00964     dev->border = (unsigned int) Tk_InternalBorderWidth( plf->tkwin );
00965     tkwd->depth = (unsigned int) Tk_Depth( plf->tkwin );
00966 
00967     dev->init_width  = dev->width;
00968     dev->init_height = dev->height;
00969 
00970     // Set up flags that determine what we are writing to
00971     // If nopixmap is set, ignore db
00972 
00973     if ( pls->nopixmap )
00974     {
00975         dev->write_to_pixmap = 0;
00976         pls->db = 0;
00977     }
00978     else
00979     {
00980         dev->write_to_pixmap = 1;
00981     }
00982     dev->write_to_window = !pls->db;
00983 
00984     // Create pixmap for holding plot image (for expose events).
00985 
00986     if ( dev->write_to_pixmap )
00987         CreatePixmap( pls );
00988 
00989     // Set drawing color
00990 
00991     plD_state_tkwin( pls, PLSTATE_COLOR0 );
00992 
00993     XSetWindowBackground( tkwd->display, dev->window, tkwd->cmap0[0].pixel );
00994     XSetBackground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
00995 }
00996 
00997 //--------------------------------------------------------------------------
00998 // ExposeCmd()
00999 //
01000 // Event handler routine for expose events.
01001 // These are "pure" exposures (no resize), so don't need to clear window.
01002 //--------------------------------------------------------------------------
01003 
01004 static void
01005 ExposeCmd( PLStream *pls, PLDisplay *pldis )
01006 {
01007     TkwDev     *dev  = (TkwDev *) pls->dev;
01008     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01009     int        x, y, width, height;
01010 
01011     dbug_enter( "ExposeCmd" );
01012 
01013     // Return if plD_init_tkw hasn't been called yet
01014 
01015     if ( dev == NULL )
01016     {
01017         plwarn( "ExposeCmd: Illegal call -- driver uninitialized" );
01018         return;
01019     }
01020 
01021     // Exposed area. If unspecified, the entire window is used.
01022 
01023     if ( pldis == NULL )
01024     {
01025         x      = 0;
01026         y      = 0;
01027         width  = (int) dev->width;
01028         height = (int) dev->height;
01029     }
01030     else
01031     {
01032         x      = (int) pldis->x;
01033         y      = (int) pldis->y;
01034         width  = (int) pldis->width;
01035         height = (int) pldis->height;
01036     }
01037 
01038     // Usual case: refresh window from pixmap
01039     // DEBUG option: draws rectangle around refreshed region
01040 
01041     XSync( tkwd->display, 0 );
01042     if ( dev->write_to_pixmap )
01043     {
01044         XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc,
01045             x, y, (unsigned int) width, (unsigned int) height, x, y );
01046         XSync( tkwd->display, 0 );
01047 #ifdef DEBUG
01048         if ( pls->debug )
01049         {
01050             XPoint pts[5];
01051             int    x0 = x, x1 = x + width, y0 = y, y1 = y + height;
01052             pts[0].x = (short) x0; pts[0].y = (short) y0;
01053             pts[1].x = (short) x1; pts[1].y = (short) y0;
01054             pts[2].x = (short) x1; pts[2].y = (short) y1;
01055             pts[3].x = (short) x0; pts[3].y = (short) y1;
01056             pts[4].x = (short) x0; pts[4].y = (short) y0;
01057 
01058             XDrawLines( tkwd->display, dev->window, dev->gc, pts, 5,
01059                 CoordModeOrigin );
01060         }
01061 #endif
01062     }
01063     else
01064     {
01065         plRemakePlot( pls );
01066         XFlush( tkwd->display );
01067     }
01068 }
01069 
01070 //--------------------------------------------------------------------------
01071 // ResizeCmd()
01072 //
01073 // Event handler routine for resize events.
01074 //--------------------------------------------------------------------------
01075 
01076 static void
01077 ResizeCmd( PLStream *pls, PLDisplay *pldis )
01078 {
01079     TkwDev     *dev            = (TkwDev *) pls->dev;
01080     TkwDisplay *tkwd           = (TkwDisplay *) dev->tkwd;
01081     int        write_to_window = dev->write_to_window;
01082 
01083     dbug_enter( "ResizeCmd" );
01084 
01085     // Return if plD_init_tkw hasn't been called yet
01086 
01087     if ( dev == NULL )
01088     {
01089         plwarn( "ResizeCmd: Illegal call -- driver uninitialized" );
01090         return;
01091     }
01092 
01093     // Return if pointer to window not specified.
01094 
01095     if ( pldis == NULL )
01096     {
01097         plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" );
01098         return;
01099     }
01100 
01101     // Reset current window bounds
01102 
01103     dev->width  = pldis->width;
01104     dev->height = pldis->height;
01105 
01106     dev->xscale = dev->width / (double) dev->init_width;
01107     dev->yscale = dev->height / (double) dev->init_height;
01108 
01109     dev->xscale = dev->xscale * dev->xscale_init;
01110     dev->yscale = dev->yscale * dev->yscale_init;
01111 
01112 #if PHYSICAL
01113     {
01114         float pxlx = (double) PIXELS_X / dev->width * DPMM;
01115         float pxly = (double) PIXELS_Y / dev->height * DPMM;
01116         plP_setpxl( pxlx, pxly );
01117     }
01118 #endif
01119 
01120     // Note: the following order MUST be obeyed -- if you instead redraw into
01121     // the window and then copy it to the pixmap, off-screen parts of the window
01122     // may contain garbage which is then transferred to the pixmap (and thus
01123     // will not go away after an expose).
01124     //
01125 
01126     // Resize pixmap using new dimensions
01127 
01128     if ( dev->write_to_pixmap )
01129     {
01130         dev->write_to_window = 0;
01131 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
01132         Tk_FreePixmap( tkwd->display, dev->pixmap );
01133 #else
01134         // Vince's original driver code used
01135         // Tk_FreePixmap(tkwd->display, dev->pixmap);
01136         //which is defined in tk-8.3 (and 8.2?) source as
01137         //void
01138         // Tk_FreePixmap(display, pixmap)
01139         //     Display *display;
01140         //     Pixmap pixmap;
01141         // {
01142         //    XFreePixmap(display, pixmap);
01143         //    Tk_FreeXId(display, (XID) pixmap);
01144         // }
01145         // But that bombed under Linux and tcl/tk8.2 so now just call
01146         // XFreePixmap directly.  (Not recommended as permanent solution
01147         // because you eventually run out of resources according to man
01148         // page if you don't call Tk_FreeXId.)  Vince is still looking into
01149         // how to resolve this problem.
01150         //
01151         XFreePixmap( tkwd->display, dev->pixmap );
01152 #endif
01153         CreatePixmap( pls );
01154     }
01155 
01156     // Initialize & redraw (to pixmap, if available).
01157 
01158     plD_bop_tkwin( pls );
01159     plRemakePlot( pls );
01160     XSync( tkwd->display, 0 );
01161 
01162     // If pixmap available, fake an expose
01163 
01164     if ( dev->write_to_pixmap )
01165     {
01166         dev->write_to_window = write_to_window;
01167         XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
01168             dev->width, dev->height, 0, 0 );
01169         XSync( tkwd->display, 0 );
01170     }
01171 }
01172 
01173 //--------------------------------------------------------------------------
01174 // RedrawCmd()
01175 //
01176 // Handles page redraw without resize (pixmap does not get reallocated).
01177 // Calling this makes sure all necessary housekeeping gets done.
01178 //--------------------------------------------------------------------------
01179 
01180 static void
01181 RedrawCmd( PLStream *pls )
01182 {
01183     TkwDev     *dev            = (TkwDev *) pls->dev;
01184     TkwDisplay *tkwd           = (TkwDisplay *) dev->tkwd;
01185     int        write_to_window = dev->write_to_window;
01186 
01187     dbug_enter( "RedrawCmd" );
01188 
01189     // Return if plD_init_tkw hasn't been called yet
01190 
01191     if ( dev == NULL )
01192     {
01193         plwarn( "RedrawCmd: Illegal call -- driver uninitialized" );
01194         return;
01195     }
01196 
01197     // Initialize & redraw (to pixmap, if available).
01198 
01199     if ( dev->write_to_pixmap )
01200         dev->write_to_window = 0;
01201 
01202     plD_bop_tkwin( pls );
01203     plRemakePlot( pls );
01204     XSync( tkwd->display, 0 );
01205 
01206     dev->write_to_window = write_to_window;
01207 
01208     // If pixmap available, fake an expose
01209 
01210     if ( dev->write_to_pixmap )
01211     {
01212         XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
01213             dev->width, dev->height, 0, 0 );
01214         XSync( tkwd->display, 0 );
01215     }
01216 }
01217 
01218 //--------------------------------------------------------------------------
01219 // CreatePixmap()
01220 //
01221 // This routine creates a pixmap, doing error trapping in case there
01222 // isn't enough memory on the server.
01223 //--------------------------------------------------------------------------
01224 
01225 static void
01226 CreatePixmap( PLStream *pls )
01227 {
01228     TkwDev     *dev  = (TkwDev *) pls->dev;
01229     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01230     Tk_Window  tkwin = pls->plPlotterPtr->tkwin;
01231 
01232 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
01233     int ( *oldErrorHandler )( Display *, XErrorEvent * );
01234     oldErrorHandler    = XSetErrorHandler( CreatePixmapErrorHandler );
01235     CreatePixmapStatus = Success;
01236 #endif
01237 
01238 #ifdef MAC_TCL
01239     // MAC_TCL's version of XCreatePixmap doesn't like 0 by 0 maps
01240     if ( dev->width == 0 )
01241     {
01242         dev->width = 10;
01243     }
01244     if ( dev->height == 0 )
01245     {
01246         dev->height = 10;
01247     }
01248 #endif
01249     pldebug( "CreatePixmap",
01250         "creating pixmap: width = %d, height = %d, depth = %d\n",
01251         dev->width, dev->height, tkwd->depth );
01252 //
01253 // dev->pixmap = Tk_GetPixmap(tkwd->display, dev->window,
01254 //                             dev->width, dev->height, tkwd->depth);
01255 //
01256 //
01257 // Vince's original driver code used Tk_Display(tkwin) for first argument,
01258 // but that bombed on an Linux tcl/tk 8.2 machine.  Something was wrong
01259 // with that value.  Thus, we now use tkwd->display, and that works well.
01260 // Vince is looking into why Tk_Display(tkwin) is badly defined under 8.2.
01261 // old code:
01262 //
01263 //  dev->pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
01264 //          Tk_Width(tkwin), Tk_Height(tkwin),
01265 //          DefaultDepthOfScreen(Tk_Screen(tkwin)));
01266 //
01267     dev->pixmap = Tk_GetPixmap( tkwd->display, Tk_WindowId( tkwin ),
01268         Tk_Width( tkwin ), Tk_Height( tkwin ),
01269         DefaultDepthOfScreen( Tk_Screen( tkwin ) ) );
01270     XSync( tkwd->display, 0 );
01271 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
01272     if ( CreatePixmapStatus != Success )
01273     {
01274         dev->write_to_pixmap = 0;
01275         dev->write_to_window = 1;
01276         pls->db = 0;
01277         fprintf( stderr, "\n\
01278       Warning: pixmap could not be allocated (insufficient memory on server).\n\
01279       Driver will redraw the entire plot to handle expose events.\n" );
01280     }
01281 
01282     XSetErrorHandler( oldErrorHandler );
01283 #endif
01284 }
01285 
01286 //--------------------------------------------------------------------------
01287 // GetVisual()
01288 //
01289 // Get visual info. In order to safely use a visual other than that of
01290 // the parent (which hopefully is that returned by DefaultVisual), you
01291 // must first find (using XGetRGBColormaps) or create a colormap matching
01292 // this visual and then set the colormap window attribute in the
01293 // XCreateWindow attributes and valuemask arguments. I don't do this
01294 // right now, so this is turned off by default.
01295 //--------------------------------------------------------------------------
01296 
01297 static void
01298 GetVisual( PLStream *pls )
01299 {
01300     int        depth;
01301     TkwDev     *dev  = (TkwDev *) pls->dev;
01302     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01303 
01304     dbug_enter( "GetVisual" );
01305 
01306     tkwd->visual = Tk_GetVisual( pls->plPlotterPtr->interp,
01307         pls->plPlotterPtr->tkwin,
01308         "best",
01309         &depth, NULL );
01310     tkwd->depth = (unsigned int) depth;
01311 }
01312 
01313 //--------------------------------------------------------------------------
01314 // AllocBGFG()
01315 //
01316 // Allocate background & foreground colors. If possible, I choose pixel
01317 // values such that the fg pixel is the xor of the bg pixel, to make
01318 // rubber-banding easy to see.
01319 //--------------------------------------------------------------------------
01320 
01321 static void
01322 AllocBGFG( PLStream *pls )
01323 {
01324     TkwDev     *dev  = (TkwDev *) pls->dev;
01325     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01326 
01327 #ifndef USE_TK
01328     int           i, j, npixels;
01329     unsigned long plane_masks[1], pixels[MAX_COLORS];
01330 #endif
01331 
01332     dbug_enter( "AllocBGFG" );
01333 
01334     // If not on a color system, just return
01335 
01336     if ( !tkwd->color )
01337         return;
01338 #ifndef USE_TK
01339     // Allocate r/w color cell for background
01340 
01341     if ( XAllocColorCells( tkwd->display, tkwd->map, False,
01342              plane_masks, 0, pixels, 1 ) )
01343     {
01344         tkwd->cmap0[0].pixel = pixels[0];
01345     }
01346     else
01347     {
01348         plexit( "couldn't allocate background color cell" );
01349     }
01350 
01351     // Allocate as many colors as we can
01352 
01353     npixels = MAX_COLORS;
01354     for (;; )
01355     {
01356         if ( XAllocColorCells( tkwd->display, tkwd->map, False,
01357                  plane_masks, 0, pixels, npixels ) )
01358             break;
01359         npixels--;
01360         if ( npixels == 0 )
01361             break;
01362     }
01363 
01364     // Find the color with pixel = xor of the bg color pixel.
01365     // If a match isn't found, the last pixel allocated is used.
01366 
01367     for ( i = 0; i < npixels - 1; i++ )
01368     {
01369         if ( pixels[i] == ( ~tkwd->cmap0[0].pixel & 0xFF ) )
01370             break;
01371     }
01372 
01373     // Use this color cell for our foreground color. Then free the rest.
01374 
01375     tkwd->fgcolor.pixel = pixels[i];
01376     for ( j = 0; j < npixels; j++ )
01377     {
01378         if ( j != i )
01379             XFreeColors( tkwd->display, tkwd->map, &pixels[j], 1, 0 );
01380     }
01381 #endif
01382 }
01383 
01384 //--------------------------------------------------------------------------
01385 // pltkwin_setBGFG()
01386 //
01387 // Set background & foreground colors. Foreground over background should
01388 // have high contrast.
01389 //--------------------------------------------------------------------------
01390 
01391 void
01392 pltkwin_setBGFG( PLStream *pls )
01393 {
01394     TkwDev     *dev  = (TkwDev *) pls->dev;
01395     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01396     PLColor    fgcolor;
01397     int        gslevbg, gslevfg;
01398 
01399     dbug_enter( "pltkwin_setBGFG" );
01400 
01401     //
01402     // Set background color.
01403     //
01404     // Background defaults to black on color screens, white on grayscale (many
01405     // grayscale monitors have poor contrast, and black-on-white looks better).
01406     //
01407 
01408     if ( !tkwd->color )
01409     {
01410         pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
01411     }
01412     gslevbg = (int) ( ( (long) pls->cmap0[0].r +
01413                         (long) pls->cmap0[0].g +
01414                         (long) pls->cmap0[0].b ) / 3 );
01415 
01416     PLColor_to_TkColor( &pls->cmap0[0], &tkwd->cmap0[0] );
01417 
01418     //
01419     // Set foreground color.
01420     //
01421     // Used for grayscale output, since otherwise the plots can become nearly
01422     // unreadable (i.e. if colors get mapped onto grayscale values). In this
01423     // case it becomes the grayscale level for all draws, and is taken to be
01424     // black if the background is light, and white if the background is dark.
01425     // Note that white/black allocations never fail.
01426     //
01427 
01428     if ( gslevbg > 0x7F )
01429         gslevfg = 0;
01430     else
01431         gslevfg = 0xFF;
01432 
01433     fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg;
01434 
01435     PLColor_to_TkColor( &fgcolor, &tkwd->fgcolor );
01436 
01437     // Now store
01438 #ifndef USE_TK
01439     if ( tkwd->color )
01440     {
01441         XStoreColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
01442         XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
01443     }
01444     else
01445     {
01446         XAllocColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
01447         XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
01448     }
01449 #else
01450     Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[0] );
01451     Tkw_StoreColor( pls, tkwd, &tkwd->fgcolor );
01452 #endif
01453 }
01454 
01455 //--------------------------------------------------------------------------
01456 // InitColors()
01457 //
01458 // Does all color initialization.
01459 //--------------------------------------------------------------------------
01460 
01461 static void
01462 InitColors( PLStream *pls )
01463 {
01464     TkwDev     *dev  = (TkwDev *) pls->dev;
01465     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01466 
01467     dbug_enter( "InitColors" );
01468 
01469     // Allocate and initialize color maps.
01470     // Defer cmap1 allocation until it's actually used
01471 
01472     if ( tkwd->color )
01473     {
01474         if ( plplot_tkwin_ccmap )
01475         {
01476             AllocCustomMap( pls );
01477         }
01478         else
01479         {
01480             AllocCmap0( pls );
01481         }
01482     }
01483 }
01484 
01485 //--------------------------------------------------------------------------
01486 // AllocCustomMap()
01487 //
01488 // Initializes custom color map and all the cruft that goes with it.
01489 //
01490 // Assuming all color X displays do 256 colors, the breakdown is as follows:
01491 //
01492 // XWM_COLORS Number of low "pixel" values to copy. These are typically
01493 //  allocated first, thus are in use by the window manager. I
01494 //  copy them to reduce flicker.
01495 //
01496 // CMAP0_COLORS Color map 0 entries. I allocate these both in the default
01497 //  colormap and the custom colormap to reduce flicker.
01498 //
01499 // CMAP1_COLORS Color map 1 entries. There should be as many as practical
01500 //  available for smooth shading. On the order of 50-100 is
01501 //  pretty reasonable. You don't really need all 256,
01502 //  especially if all you're going to do is to print it to
01503 //  postscript (which doesn't have any intrinsic limitation on
01504 //  the number of colors).
01505 //
01506 // It's important to leave some extra colors unallocated for Tk. In
01507 // particular the palette tools require a fair amount. I recommend leaving
01508 // at least 40 or so free.
01509 //--------------------------------------------------------------------------
01510 
01511 static void
01512 AllocCustomMap( PLStream *pls )
01513 {
01514     TkwDev        *dev  = (TkwDev *) pls->dev;
01515     TkwDisplay    *tkwd = (TkwDisplay *) dev->tkwd;
01516 
01517     XColor        xwm_colors[MAX_COLORS];
01518     int           i;
01519 #ifndef USE_TK
01520     int           npixels;
01521     unsigned long plane_masks[1], pixels[MAX_COLORS];
01522 #endif
01523 
01524     dbug_enter( "AllocCustomMap" );
01525 
01526     // Determine current default colors
01527 
01528     for ( i = 0; i < MAX_COLORS; i++ )
01529     {
01530         xwm_colors[i].pixel = (long unsigned) i;
01531     }
01532 #ifndef MAC_TCL
01533     XQueryColors( tkwd->display, tkwd->map, xwm_colors, MAX_COLORS );
01534 #endif
01535 
01536     // Allocate cmap0 colors in the default colormap.
01537     // The custom cmap0 colors are later stored at the same pixel values.
01538     // This is a really cool trick to reduce the flicker when changing colormaps.
01539     //
01540 
01541     AllocCmap0( pls );
01542     XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
01543 
01544     // Create new color map
01545 
01546     tkwd->map = XCreateColormap( tkwd->display, DefaultRootWindow( tkwd->display ),
01547         tkwd->visual, AllocNone );
01548 
01549     // Now allocate all colors so we can fill the ones we want to copy
01550 
01551 #ifndef USE_TK
01552     npixels = MAX_COLORS;
01553     for (;; )
01554     {
01555         if ( XAllocColorCells( tkwd->display, tkwd->map, False,
01556                  plane_masks, 0, pixels, npixels ) )
01557             break;
01558         npixels--;
01559         if ( npixels == 0 )
01560             plexit( "couldn't allocate any colors" );
01561     }
01562 
01563     // Fill the low colors since those are in use by the window manager
01564 
01565     for ( i = 0; i < XWM_COLORS; i++ )
01566     {
01567         XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
01568         pixels[xwm_colors[i].pixel] = 0;
01569     }
01570 
01571     // Fill the ones we will use in cmap0
01572 
01573     for ( i = 0; i < tkwd->ncol0; i++ )
01574     {
01575         XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
01576         pixels[tkwd->cmap0[i].pixel] = 0;
01577     }
01578 
01579     // Finally, if the colormap was saved by an external agent, see if there are
01580     // any differences from the current default map and save those! A very cool
01581     // (or sick, depending on how you look at it) trick to get over some X and
01582     // Tk limitations.
01583     //
01584 
01585     if ( sxwm_colors_set )
01586     {
01587         for ( i = 0; i < MAX_COLORS; i++ )
01588         {
01589             if ( ( xwm_colors[i].red != sxwm_colors[i].red ) ||
01590                  ( xwm_colors[i].green != sxwm_colors[i].green ) ||
01591                  ( xwm_colors[i].blue != sxwm_colors[i].blue ) )
01592             {
01593                 if ( pixels[i] != 0 )
01594                 {
01595                     XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
01596                     pixels[i] = 0;
01597                 }
01598             }
01599         }
01600     }
01601 
01602     // Now free the ones we're not interested in
01603 
01604     for ( i = 0; i < npixels; i++ )
01605     {
01606         if ( pixels[i] != 0 )
01607             XFreeColors( tkwd->display, tkwd->map, &pixels[i], 1, 0 );
01608     }
01609 #endif
01610     // Allocate colors in cmap 1
01611 
01612     AllocCmap1( pls );
01613 }
01614 
01615 //--------------------------------------------------------------------------
01616 // AllocCmap0()
01617 //
01618 // Allocate & initialize cmap0 entries.
01619 //--------------------------------------------------------------------------
01620 
01621 static void
01622 AllocCmap0( PLStream *pls )
01623 {
01624     TkwDev     *dev  = (TkwDev *) pls->dev;
01625     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01626 
01627 #ifndef USE_TK
01628     int           npixels;
01629     int           i;
01630     unsigned long plane_masks[1], pixels[MAX_COLORS];
01631 #endif
01632 
01633     dbug_enter( "AllocCmap0" );
01634 
01635     // Allocate and assign colors in cmap 0
01636 
01637 #ifndef USE_TK
01638     npixels = pls->ncol0 - 1;
01639     for (;; )
01640     {
01641         if ( XAllocColorCells( tkwd->display, tkwd->map, False,
01642                  plane_masks, 0, &pixels[1], npixels ) )
01643             break;
01644         npixels--;
01645         if ( npixels == 0 )
01646             plexit( "couldn't allocate any colors" );
01647     }
01648 
01649     tkwd->ncol0 = npixels + 1;
01650     for ( i = 1; i < tkwd->ncol0; i++ )
01651     {
01652         tkwd->cmap0[i].pixel = pixels[i];
01653     }
01654 #else
01655     // We use the Tk color scheme
01656     tkwd->ncol0 = pls->ncol0;
01657 #endif
01658     StoreCmap0( pls );
01659 }
01660 
01661 //--------------------------------------------------------------------------
01662 // AllocCmap1()
01663 //
01664 // Allocate & initialize cmap1 entries. If using the default color map,
01665 // must severely limit number of colors otherwise TK won't have enough.
01666 //--------------------------------------------------------------------------
01667 
01668 static void
01669 AllocCmap1( PLStream *pls )
01670 {
01671     TkwDev        *dev  = (TkwDev *) pls->dev;
01672     TkwDisplay    *tkwd = (TkwDisplay *) dev->tkwd;
01673 
01674     int           npixels;
01675 #ifndef USE_TK
01676     int           i, j;
01677     unsigned long plane_masks[1], pixels[MAX_COLORS];
01678 #endif
01679 
01680     dbug_enter( "AllocCmap1" );
01681 
01682     // Allocate colors in cmap 1
01683 
01684     npixels = MAX( 2, MIN( CMAP1_COLORS, pls->ncol1 ) );
01685 #ifndef USE_TK
01686     for (;; )
01687     {
01688         if ( XAllocColorCells( tkwd->display, tkwd->map, False,
01689                  plane_masks, 0, pixels, npixels ) )
01690             break;
01691         npixels--;
01692         if ( npixels == 0 )
01693             break;
01694     }
01695 
01696     if ( npixels < 2 )
01697     {
01698         tkwd->ncol1 = -1;
01699         fprintf( stderr,
01700             "Warning: unable to allocate sufficient colors in cmap1\n" );
01701         return;
01702     }
01703     else
01704     {
01705         tkwd->ncol1 = npixels;
01706         if ( pls->verbose )
01707             fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", npixels );
01708     }
01709 
01710     // Don't assign pixels sequentially, to avoid strange problems with xor GC's
01711     // Skipping by 2 seems to do the job best
01712 
01713     for ( j = i = 0; i < tkwd->ncol1; i++ )
01714     {
01715         while ( pixels[j] == 0 )
01716             j++;
01717 
01718         tkwd->cmap1[i].pixel = pixels[j];
01719         pixels[j]            = 0;
01720 
01721         j += 2;
01722         if ( j >= tkwd->ncol1 )
01723             j = 0;
01724     }
01725 #else
01726     tkwd->ncol1 = npixels;
01727 #endif
01728     StoreCmap1( pls );
01729 }
01730 
01731 //--------------------------------------------------------------------------
01732 // StoreCmap0()
01733 //
01734 // Stores cmap 0 entries in X-server colormap.
01735 //--------------------------------------------------------------------------
01736 
01737 static void
01738 StoreCmap0( PLStream *pls )
01739 {
01740     TkwDev     *dev  = (TkwDev *) pls->dev;
01741     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01742     int        i;
01743 
01744     if ( !tkwd->color )
01745         return;
01746 
01747     for ( i = 1; i < tkwd->ncol0; i++ )
01748     {
01749         PLColor_to_TkColor( &pls->cmap0[i], &tkwd->cmap0[i] );
01750 #ifndef USE_TK
01751         XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
01752 #else
01753         Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[i] );
01754 #endif
01755     }
01756 }
01757 
01758 void CopyColour( XColor* from, XColor* to )
01759 {
01760     to->pixel = from->pixel;
01761     to->red   = from->red;
01762     to->blue  = from->blue;
01763     to->green = from->green;
01764     to->flags = from->flags;
01765 }
01766 
01767 //--------------------------------------------------------------------------
01768 // StoreCmap1()
01769 //
01770 // Stores cmap 1 entries in X-server colormap.
01771 //--------------------------------------------------------------------------
01772 
01773 static void
01774 StoreCmap1( PLStream *pls )
01775 {
01776     TkwDev     *dev  = (TkwDev *) pls->dev;
01777     TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
01778 
01779     PLColor    cmap1color;
01780     int        i;
01781 
01782     if ( !tkwd->color )
01783         return;
01784 
01785     for ( i = 0; i < tkwd->ncol1; i++ )
01786     {
01787         plcol_interp( pls, &cmap1color, i, tkwd->ncol1 );
01788         PLColor_to_TkColor( &cmap1color, &tkwd->cmap1[i] );
01789 #ifndef USE_TK
01790         XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap1[i] );
01791 #else
01792         Tkw_StoreColor( pls, tkwd, &tkwd->cmap1[i] );
01793 #endif
01794     }
01795 }
01796 
01797 void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col )
01798 {
01799     XColor *xc;
01800 #ifndef USE_TK
01801     XStoreColor( tkwd->display, tkwd->map, col );
01802 #else
01803     (void) tkwd;  // tkwd unused in this case
01804     // We're probably losing memory here
01805     xc = Tk_GetColorByValue( pls->plPlotterPtr->tkwin, col );
01806     CopyColour( xc, col );
01807 #endif
01808 }
01809 
01810 //--------------------------------------------------------------------------
01811 // void PLColor_to_TkColor()
01812 //
01813 // Copies the supplied PLColor to an XColor, padding with bits as necessary
01814 // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
01815 // The argument types follow the same order as in the function name.
01816 //--------------------------------------------------------------------------
01817 
01818 #define ToXColor( a )     ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) )
01819 #define ToPLColor( a )    ( ( (U_LONG) a ) >> 8 )
01820 
01821 void
01822 PLColor_to_TkColor( PLColor *plcolor, XColor *xcolor )
01823 {
01824     xcolor->red   = (short unsigned) ToXColor( plcolor->r );
01825     xcolor->green = (short unsigned) ToXColor( plcolor->g );
01826     xcolor->blue  = (short unsigned) ToXColor( plcolor->b );
01827     xcolor->flags = DoRed | DoGreen | DoBlue;
01828 }
01829 
01830 //--------------------------------------------------------------------------
01831 // void PLColor_from_TkColor()
01832 //
01833 // Copies the supplied XColor to a PLColor, stripping off bits as
01834 // necessary. See the previous routine for more info.
01835 //--------------------------------------------------------------------------
01836 
01837 void
01838 PLColor_from_TkColor( PLColor *plcolor, XColor *xcolor )
01839 {
01840     plcolor->r = (unsigned char) ToPLColor( xcolor->red );
01841     plcolor->g = (unsigned char) ToPLColor( xcolor->green );
01842     plcolor->b = (unsigned char) ToPLColor( xcolor->blue );
01843 }
01844 
01845 //--------------------------------------------------------------------------
01846 // void PLColor_from_TkColor_Changed()
01847 //
01848 // Copies the supplied XColor to a PLColor, stripping off bits as
01849 // necessary. See the previous routine for more info.
01850 //
01851 // Returns 1 if the color was different from the old one.
01852 //--------------------------------------------------------------------------
01853 
01854 int
01855 PLColor_from_TkColor_Changed( PLColor *plcolor, XColor *xcolor )
01856 {
01857     int changed = 0;
01858     int color;
01859     color = ToPLColor( xcolor->red );
01860 
01861     if ( plcolor->r != color )
01862     {
01863         changed    = 1;
01864         plcolor->r = (unsigned char) color;
01865     }
01866     color = ToPLColor( xcolor->green );
01867     if ( plcolor->g != color )
01868     {
01869         changed    = 1;
01870         plcolor->g = (unsigned char) color;
01871     }
01872     color = ToPLColor( xcolor->blue );
01873     if ( plcolor->b != color )
01874     {
01875         changed    = 1;
01876         plcolor->b = (unsigned char) color;
01877     }
01878     return changed;
01879 }
01880 
01881 //--------------------------------------------------------------------------
01882 // int pltk_AreWeGrayscale(PlPlotter *plf)
01883 //
01884 // Determines if we're using a monochrome or grayscale device.
01885 // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland.
01886 // Changed July 1996 by Vince: now uses Tk to check the enclosing PlPlotter
01887 //--------------------------------------------------------------------------
01888 
01889 static int
01890 pltk_AreWeGrayscale( PlPlotter *plf )
01891 {
01892 #if defined ( __cplusplus ) || defined ( c_plusplus )
01893 #define THING    c_class
01894 #else
01895 #define THING    class
01896 #endif
01897 
01898     Visual* visual;
01899     // get the window's Visual
01900     visual = Tk_Visual( plf->tkwin );
01901     if ( ( visual->THING != GrayScale ) && ( visual->THING != StaticGray ) )
01902         return ( 0 );
01903     // if we got this far, only StaticGray and GrayScale classes available
01904     return ( 1 );
01905 }
01906 
01907 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
01908 //--------------------------------------------------------------------------
01909 // CreatePixmapErrorHandler()
01910 //
01911 // Error handler used in CreatePixmap() to catch errors in allocating
01912 // storage for pixmap. This way we can nicely substitute redraws for
01913 // pixmap copies if the server has insufficient memory.
01914 //--------------------------------------------------------------------------
01915 
01916 static int
01917 CreatePixmapErrorHandler( Display *display, XErrorEvent *error )
01918 {
01919     if ( error->error_code == BadAlloc )
01920     {
01921         CreatePixmapStatus = error->error_code;
01922     }
01923     else
01924     {
01925         char buffer[256];
01926         XGetErrorText( display, error->error_code, buffer, 256 );
01927         fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer );
01928     }
01929     return 1;
01930 }
01931 #endif
01932 
01933 #else
01934 int
01935 pldummy_tkwin()
01936 {
01937     return 0;
01938 }
01939 
01940 #endif    // PLD_tkwin
01941 
01942 void *  ckcalloc( size_t nmemb, size_t size )
01943 {
01944     long *ptr;
01945     long *p;
01946     size *= nmemb;
01947     ptr   = (long *) malloc( size );
01948     if ( !ptr )
01949         return ( 0 );
01950 
01951 #if !__POWERPC__
01952 
01953     for ( size = ( size / sizeof ( long ) ) + 1, p = ptr; --size; )
01954         *p++ = 0;
01955 
01956 #else
01957 
01958     for ( size = ( size / sizeof ( long ) ) + 1, p = ptr - 1; --size; )
01959         *++p = 0;
01960 
01961 #endif
01962 
01963     return ( ptr );
01964 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines