PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
tkwin.c
Go to the documentation of this file.
1 // $Id: tkwin.c 11975 2011-10-19 11:05:10Z andrewross $
2 //
3 // PLplot Tk device driver.
4 //
5 // Copyright (C) 2004 Maurice LeBrun
6 // Copyright (C) 2004 Joao Cardoso
7 //
8 // This file is part of PLplot.
9 //
10 // PLplot is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License as published
12 // by the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // PLplot is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Library General Public License for more details.
19 //
20 // You should have received a copy of the GNU Library General Public License
21 // along with PLplot; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // This device driver is designed to be used by a PlPlotter, and in fact requires
25 // the existence of an enclosing PlPlotter.
26 //
27 // The idea is that this should develop into a completely cross-platform driver
28 // for use by the cross platform Tk system.
29 //
30 //
31 
32 #include "plDevs.h"
33 
34 #define DEBUG
35 
36 #ifdef PLD_tkwin
37 
38 
39 #define NEED_PLDEBUG
40 #include "plplotP.h"
41 #include "pltkwd.h"
42 #include "drivers.h"
43 #include "plevent.h"
44 
45 #define _TCLINT
46 #ifdef USE_TCL_STUBS
47 // Unfortunately, tkInt.h ends up loading Malloc.h under Windows
48 // So we have to deal with this mess
49  #undef malloc
50  #undef free
51  #undef realloc
52  #undef calloc
53 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
54 #include <tkInt.h>
55 #else
56 #include <tk.h>
57 #endif
58  #define malloc ckalloc
59  #define free( m ) ckfree( (char *) m )
60  #define realloc ckrealloc
61  #define calloc ckcalloc
62 #else
63 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
64 #include <tkInt.h>
65 #else
66 #include <tk.h>
67 #endif
68 #endif
69 
70 #ifdef ckalloc
71 #undef ckalloc
72 #define ckalloc malloc
73 #endif
74 #ifdef ckfree
75 #undef ckfree
76 #define ckfree free
77 #endif
78 #ifdef free
79 #undef free
80 #endif
81 
82 // Device info
83 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tkwin = "tkwin:New tk driver:1:tkwin:45:tkwin\n";
84 
85 
86 void * ckcalloc( size_t nmemb, size_t size );
87 
88 //
89 // We want to use the 'pure Tk' interface. On Unix we can use
90 // some direct calls to X instead of Tk, if we want, although
91 // that code hasn't been tested for some time. So this define
92 // is required on Windows/MacOS and perhaps optional on Unix.
93 //
94 #define USE_TK
95 
96 #ifdef __WIN32__
97 #define XSynchronize( display, bool ) { display->request++; }
98 #define XSync( display, bool ) { display->request++; }
99 #define XFlush( display )
100 #endif
101 
102 // Dummy definition of PlPlotter containing first few fields
103 typedef struct PlPlotter
104 {
105  Tk_Window tkwin; // Window that embodies the frame. NULL
106  // means that the window has been destroyed
107  // but the data structures haven't yet been
108  // cleaned up.
109  //
110  Display *display; // Display containing widget. Used, among
111  // other things, so that resources can be
112  // freed even after tkwin has gone away.
113  //
114  Tcl_Interp *interp; // Interpreter associated with
115  // widget. Used to delete widget
116  // command.
117  //
118 } PlPlotter;
119 
120 void CopyColour( XColor* from, XColor* to );
121 void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col );
122 static int pltk_AreWeGrayscale( PlPlotter *plf );
123 void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
124 void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
125 
126 static int synchronize = 0; // change to 1 for synchronized operation
127 // for debugging only
128 
129 // Number of instructions to skip between querying the X server for events
130 
131 #define MAX_INSTR 20
132 
133 // Pixels/mm
134 
135 #define PHYSICAL 0 // Enables physical scaling..
136 
137 // Set constants for dealing with colormap. In brief:
138 //
139 // ccmap When set, turns on custom color map
140 //
141 // XWM_COLORS Number of low "pixel" values to copy.
142 // CMAP0_COLORS Color map 0 entries.
143 // CMAP1_COLORS Color map 1 entries.
144 // MAX_COLORS Maximum colors period.
145 //
146 // See Init_CustomCmap() and Init_DefaultCmap() for more info.
147 // Set ccmap at your own risk -- still under development.
148 //
149 
150 // plplot_tkwin_ccmap is statically defined in pltkwd.h. Note that
151 // plplotter.c also includes that header and uses that variable.
152 
153 #define XWM_COLORS 70
154 #define CMAP0_COLORS 16
155 #define CMAP1_COLORS 50
156 #define MAX_COLORS 256
157 
158 #ifndef USE_TK
159 // Variables to hold RGB components of given colormap.
160 // Used in an ugly hack to get past some X11R5 and TK limitations.
161 
162 static int sxwm_colors_set;
163 static XColor sxwm_colors[MAX_COLORS];
164 #endif
165 
166 // Keep pointers to all the displays in use
167 
168 static TkwDisplay *tkwDisplay[PLTKDISPLAYS];
169 
170 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
171 static unsigned char CreatePixmapStatus;
172 static int CreatePixmapErrorHandler( Display *display, XErrorEvent *error );
173 #endif
174 
175 // Function prototypes
176 // Initialization
177 
178 static void Init( PLStream *pls );
179 static void InitColors( PLStream *pls );
180 static void AllocCustomMap( PLStream *pls );
181 static void AllocCmap0( PLStream *pls );
182 static void AllocCmap1( PLStream *pls );
183 static void CreatePixmap( PLStream *pls );
184 static void GetVisual( PLStream *pls );
185 static void AllocBGFG( PLStream *pls );
186 
187 // Escape function commands
188 
189 static void ExposeCmd( PLStream *pls, PLDisplay *ptr );
190 static void RedrawCmd( PLStream *pls );
191 static void ResizeCmd( PLStream *pls, PLDisplay *ptr );
192 #ifndef USE_TK
193 static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr );
194 #endif
195 static void FillPolygonCmd( PLStream *pls );
196 #ifdef USING_PLESC_COPY
197 static void CopyCommand( PLStream *pls );
198 #endif
199 
200 // Miscellaneous
201 
202 static void StoreCmap0( PLStream *pls );
203 static void StoreCmap1( PLStream *pls );
204 static void WaitForPage( PLStream *pls );
205 
207 
208 void plD_init_tkwin( PLStream * );
209 void plD_line_tkwin( PLStream *, short, short, short, short );
210 void plD_polyline_tkwin( PLStream *, short *, short *, PLINT );
211 void plD_eop_tkwin( PLStream * );
212 void plD_bop_tkwin( PLStream * );
213 void plD_tidy_tkwin( PLStream * );
214 void plD_state_tkwin( PLStream *, PLINT );
215 void plD_esc_tkwin( PLStream *, PLINT, void * );
216 void plD_open_tkwin( PLStream *pls );
217 
219 {
220 #ifndef ENABLE_DYNDRIVERS
221  pdt->pl_MenuStr = "PLplot Tk plotter";
222  pdt->pl_DevName = "tkwin";
223 #endif
225  pdt->pl_seq = 45;
226  pdt->pl_init = (plD_init_fp) plD_init_tkwin;
227  pdt->pl_line = (plD_line_fp) plD_line_tkwin;
228  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tkwin;
229  pdt->pl_eop = (plD_eop_fp) plD_eop_tkwin;
230  pdt->pl_bop = (plD_bop_fp) plD_bop_tkwin;
231  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tkwin;
232  pdt->pl_state = (plD_state_fp) plD_state_tkwin;
233  pdt->pl_esc = (plD_esc_fp) plD_esc_tkwin;
234 }
235 
236 //--------------------------------------------------------------------------
237 // plD_init_tkwin()
238 //
239 // Initialize device.
240 // Tk-dependent stuff done in plD_open_tkwin() and Init().
241 //--------------------------------------------------------------------------
242 
243 void
244 plD_init_tkwin( PLStream *pls )
245 {
246  TkwDev *dev;
247  float pxlx, pxly;
248  int xmin = 0;
249  int xmax = PIXELS_X - 1;
250  int ymin = 0;
251  int ymax = PIXELS_Y - 1;
252 
253  dbug_enter( "plD_init_tkw" );
254 
255  pls->termin = 1; // Is an interactive terminal
256  pls->dev_flush = 1; // Handle our own flushes
257  pls->dev_fill0 = 1; // Handle solid fills
258  pls->plbuf_write = 1; // Activate plot buffer
259 
260  // The real meat of the initialization done here
261 
262  if ( pls->dev == NULL )
263  plD_open_tkwin( pls );
264 
265  dev = (TkwDev *) pls->dev;
266 
267  Init( pls );
268 
269  // Get ready for plotting
270 
271  dev->xlen = (short) ( xmax - xmin );
272  dev->ylen = (short) ( ymax - ymin );
273 
274  dev->xscale_init = (double) dev->init_width / (double) dev->xlen;
275  dev->yscale_init = (double) dev->init_height / (double) dev->ylen;
276 
277  dev->xscale = dev->xscale_init;
278  dev->yscale = dev->yscale_init;
279 
280 #if PHYSICAL
281  pxlx = (PLFLT) ( (double) PIXELS_X / dev->width * DPMM );
282  pxly = (PLFLT) ( (double) PIXELS_Y / dev->height * DPMM );
283 #else
284  pxlx = (PLFLT) ( (double) PIXELS_X / LPAGE_X );
285  pxly = (PLFLT) ( (double) PIXELS_Y / LPAGE_Y );
286 #endif
287 
288  plP_setpxl( pxlx, pxly );
289  plP_setphy( xmin, xmax, ymin, ymax );
290 }
291 
292 //--------------------------------------------------------------------------
293 // plD_open_tkwin()
294 //
295 // Performs basic driver initialization, without actually opening or
296 // modifying a window. May be called by the outside world before plinit
297 // in case the caller needs early access to the driver internals (not
298 // very common -- currently only used externally by plplotter).
299 //--------------------------------------------------------------------------
300 
301 void
302 plD_open_tkwin( PLStream *pls )
303 {
304  TkwDev *dev;
305  TkwDisplay *tkwd;
306  int i;
307 
308  dbug_enter( "plD_open_tkw" );
309 
310  // Allocate and initialize device-specific data
311 
312  if ( pls->dev != NULL )
313  plwarn( "plD_open_tkw: device pointer is already set" );
314 
315  pls->dev = (TkwDev *) calloc( 1, (size_t) sizeof ( TkwDev ) );
316  if ( pls->dev == NULL )
317  plexit( "plD_init_tkw: Out of memory." );
318 
319  dev = (TkwDev *) pls->dev;
320 
321  // Variables used in querying the X server for events
322 
323  dev->instr = 0;
324  dev->max_instr = MAX_INSTR;
325 
326  // See if display matches any already in use, and if so use that
327 
328  dev->tkwd = NULL;
329  for ( i = 0; i < PLTKDISPLAYS; i++ )
330  {
331  if ( tkwDisplay[i] == NULL )
332  {
333  continue;
334  }
335  else if ( pls->FileName == NULL && tkwDisplay[i]->displayName == NULL )
336  {
337  dev->tkwd = tkwDisplay[i];
338  break;
339  }
340  else if ( pls->FileName == NULL || tkwDisplay[i]->displayName == NULL )
341  {
342  continue;
343  }
344  else if ( strcmp( tkwDisplay[i]->displayName, pls->FileName ) == 0 )
345  {
346  dev->tkwd = tkwDisplay[i];
347  break;
348  }
349  }
350 
351  // If no display matched, create a new one
352 
353  if ( dev->tkwd == NULL )
354  {
355  dev->tkwd = (TkwDisplay *) calloc( 1, (size_t) sizeof ( TkwDisplay ) );
356  if ( dev->tkwd == NULL )
357  plexit( "Init: Out of memory." );
358 
359  for ( i = 0; i < PLTKDISPLAYS; i++ )
360  {
361  if ( tkwDisplay[i] == NULL )
362  break;
363  }
364  if ( i == PLTKDISPLAYS )
365  plexit( "Init: Out of tkwDisplay's." );
366 
367  tkwDisplay[i] = tkwd = (TkwDisplay *) dev->tkwd;
368  tkwd->nstreams = 1;
369 
370  //
371  // If we don't have a tk widget we're being called on, then
372  // abort operations now
373  //
374  if ( pls->plPlotterPtr == NULL )
375  {
376  plexit( "No tk plframe widget to connect to" );
377  }
378  // Old version for MacOS Tk8.0
379  //
380  // char deflt[] = "Macintosh:0";
381  // pls->FileName = deflt;
382  // tkwd->display = (Display*) TkpOpenDisplay(pls->FileName);
383  //
384 
385  // Open display
386 #if defined ( MAC_TCL ) || defined ( __WIN32__ )
387  if ( !pls->FileName )
388  {
389  //
390  // Need to strdup because Tk has allocated the screen name,
391  // but we will actually 'free' it later ourselves, and therefore
392  // need to own the memory.
393  //
394  pls->FileName = plstrdup( TkGetDefaultScreenName( NULL, NULL ) );
395  }
396  tkwd->display = pls->plPlotterPtr->display;
397 #else
398  tkwd->display = XOpenDisplay( pls->FileName );
399 #endif
400  if ( tkwd->display == NULL )
401  {
402  plexit( "Can't open display" );
403  }
404  tkwd->displayName = pls->FileName;
405  tkwd->screen = DefaultScreen( tkwd->display );
406  if ( synchronize )
407  {
408  XSynchronize( tkwd->display, 1 );
409  }
410  // Get colormap and visual
411 
412  tkwd->map = Tk_Colormap( pls->plPlotterPtr->tkwin );
413  GetVisual( pls );
414 
415  //
416  // Figure out if we have a color display or not.
417  // Default is color IF the user hasn't specified and IF the output device is
418  // not grayscale.
419  //
420 
421  if ( pls->colorset )
422  tkwd->color = pls->color;
423  else
424  {
425  pls->color = 1;
426  tkwd->color = !pltk_AreWeGrayscale( pls->plPlotterPtr );
427  }
428 
429  // Allocate & set background and foreground colors
430 
431  AllocBGFG( pls );
432  pltkwin_setBGFG( pls );
433  }
434 
435  // Display matched, so use existing display data
436 
437  else
438  {
439  tkwd = (TkwDisplay *) dev->tkwd;
440  tkwd->nstreams++;
441  }
442  tkwd->ixwd = i;
443 }
444 
445 //--------------------------------------------------------------------------
446 // plD_line_tkwin()
447 //
448 // Draw a line in the current color from (x1,y1) to (x2,y2).
449 //--------------------------------------------------------------------------
450 
451 void
452 plD_line_tkwin( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
453 {
454  TkwDev *dev = (TkwDev *) pls->dev;
455  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
456 
457  int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
458 
459  if ( dev->flags & 1 )
460  return;
461 
462  y1 = dev->ylen - y1;
463  y2 = dev->ylen - y2;
464 
465  x1 = (int) ( x1 * dev->xscale );
466  x2 = (int) ( x2 * dev->xscale );
467  y1 = (int) ( y1 * dev->yscale );
468  y2 = (int) ( y2 * dev->yscale );
469 
470  if ( dev->write_to_window )
471  XDrawLine( tkwd->display, dev->window, dev->gc, x1, y1, x2, y2 );
472 
473  if ( dev->write_to_pixmap )
474  XDrawLine( tkwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 );
475 }
476 
477 //--------------------------------------------------------------------------
478 // plD_polyline_tkwin()
479 //
480 // Draw a polyline in the current color from (x1,y1) to (x2,y2).
481 //--------------------------------------------------------------------------
482 
483 void
484 plD_polyline_tkwin( PLStream *pls, short *xa, short *ya, PLINT npts )
485 {
486  TkwDev *dev = (TkwDev *) pls->dev;
487  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
488 
489  PLINT i;
490  XPoint _pts[PL_MAXPOLY];
491  XPoint *pts;
492 
493  if ( dev->flags & 1 )
494  return;
495 
496  if ( npts > PL_MAXPOLY )
497  {
498  pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts );
499  }
500  else
501  {
502  pts = _pts;
503  }
504 
505  for ( i = 0; i < npts; i++ )
506  {
507  pts[i].x = (short) ( dev->xscale * xa[i] );
508  pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) );
509  }
510 
511  if ( dev->write_to_window )
512  XDrawLines( tkwd->display, dev->window, dev->gc, pts, npts,
513  CoordModeOrigin );
514 
515  if ( dev->write_to_pixmap )
516  XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, npts,
517  CoordModeOrigin );
518 
519  if ( npts > PL_MAXPOLY )
520  {
521  free( pts );
522  }
523 }
524 
525 //--------------------------------------------------------------------------
526 // plD_eop_tkwin()
527 //
528 // End of page. User must hit return (or third mouse button) to continue.
529 //--------------------------------------------------------------------------
530 
531 void
532 plD_eop_tkwin( PLStream *pls )
533 {
534  TkwDev *dev = (TkwDev *) pls->dev;
535  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
536 
537  dbug_enter( "plD_eop_tkw" );
538  if ( dev->flags & 1 )
539  return;
540 
541  XFlush( tkwd->display );
542  if ( pls->db )
543  ExposeCmd( pls, NULL );
544 
545  if ( !pls->nopause )
546  WaitForPage( pls );
547 }
548 
549 //--------------------------------------------------------------------------
550 // WaitForPage()
551 //
552 // This routine waits for the user to advance the plot, while handling
553 // all other events.
554 //--------------------------------------------------------------------------
555 
556 static void
557 WaitForPage( PLStream *pls )
558 {
559  PlPlotter *plf = pls->plPlotterPtr;
560  TkwDev *dev = (TkwDev *) pls->dev;
561 
562  dbug_enter( "WaitForPage" );
563 
564  dev->flags &= 1;
565  if ( plf == NULL )
566  {
567  plwarn( "WaitForPage: Illegal call --- driver can't find enclosing PlPlotter" );
568  return;
569  }
570  PlplotterAtEop( plf->interp, plf );
571 
572  while ( !( dev->flags ) && !Tcl_InterpDeleted( plf->interp ) && ( Tk_GetNumMainWindows() > 0 ) )
573  {
574  Tcl_DoOneEvent( 0 );
575  }
576 
577  if ( Tcl_InterpDeleted( plf->interp ) || ( Tk_GetNumMainWindows() <= 0 ) )
578  {
579  dev->flags |= 1;
580  }
581 
582  dev->flags &= 1;
583 }
584 
585 //--------------------------------------------------------------------------
586 // plD_bop_tkwin()
587 //
588 // Set up for the next page.
589 //--------------------------------------------------------------------------
590 
591 void
592 plD_bop_tkwin( PLStream *pls )
593 {
594  PlPlotter *plf = pls->plPlotterPtr;
595  TkwDev *dev = (TkwDev *) pls->dev;
596  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
597 
598  XRectangle xrect;
599  xrect.x = 0; xrect.y = 0;
600  xrect.width = (short unsigned) dev->width;
601  xrect.height = (short unsigned) dev->height;
602 
603  dbug_enter( "plD_bop_tkw" );
604  if ( dev->flags & 1 )
605  return;
606 
607  if ( dev->write_to_window )
608  {
609 #ifdef MAC_TCL
610  // MacTk only has these X calls
611  XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
612  XFillRectangles( tkwd->display, dev->window, dev->gc, &xrect, 1 );
613  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
614 #else
615  XClearWindow( tkwd->display, dev->window );
616 #endif
617  }
618  if ( dev->write_to_pixmap )
619  {
620  XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
621  XFillRectangles( tkwd->display, dev->pixmap, dev->gc, &xrect, 1 );
622  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
623  }
624  XSync( tkwd->display, 0 );
625  pls->page++;
626  PlplotterAtBop( plf->interp, plf );
627 }
628 
629 //--------------------------------------------------------------------------
630 // plD_tidy_tkwin()
631 //
632 // Close graphics file
633 //--------------------------------------------------------------------------
634 
635 void
636 plD_tidy_tkwin( PLStream *pls )
637 {
638  TkwDev *dev = (TkwDev *) pls->dev;
639  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
640 
641  dbug_enter( "plD_tidy_tkw" );
642 
643  tkwd->nstreams--;
644  if ( tkwd->nstreams == 0 )
645  {
646  int ixwd = tkwd->ixwd;
647  XFreeGC( tkwd->display, dev->gc );
648 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
649  XCloseDisplay( tkwd->display );
650 #endif
651  free_mem( tkwDisplay[ixwd] );
652  }
653  //
654  // Vince removed this November 1999. It seems as if a simple
655  // 'plframe .p ; destroy .p' leaves a temporary buf file open
656  // if we clear this flag here. It should be checked and then
657  // cleared by whoever called us. An alternative fix would
658  // be to carry out the check/tidy here. The plframe widget
659  // handles this stuff for us.
660  //
661  // pls->plbuf_write = 0;
662 }
663 
664 //--------------------------------------------------------------------------
665 // plD_state_tkwin()
666 //
667 // Handle change in PLStream state (color, pen width, fill attribute, etc).
668 //--------------------------------------------------------------------------
669 
670 void
671 plD_state_tkwin( PLStream *pls, PLINT op )
672 {
673  TkwDev *dev = (TkwDev *) pls->dev;
674  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
675  dbug_enter( "plD_state_tkw" );
676  if ( dev->flags & 1 )
677  return;
678 
679  switch ( op )
680  {
681  case PLSTATE_WIDTH:
682  break;
683 
684  case PLSTATE_COLOR0: {
685  int icol0 = pls->icol0;
686  if ( tkwd->color )
687  {
688  if ( icol0 == PL_RGB_COLOR )
689  {
690  PLColor_to_TkColor( &pls->curcolor, &dev->curcolor );
691  Tkw_StoreColor( pls, tkwd, &dev->curcolor );
692  }
693  else
694  {
695  dev->curcolor = tkwd->cmap0[icol0];
696  }
697  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
698  }
699  else
700  {
701  dev->curcolor = tkwd->fgcolor;
702  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
703  }
704  break;
705  }
706 
707  case PLSTATE_COLOR1: {
708  int icol1;
709 
710  if ( tkwd->ncol1 == 0 )
711  AllocCmap1( pls );
712 
713  if ( tkwd->ncol1 < 2 )
714  break;
715 
716  icol1 = ( pls->icol1 * ( tkwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 );
717  if ( tkwd->color )
718  dev->curcolor = tkwd->cmap1[icol1];
719  else
720  dev->curcolor = tkwd->fgcolor;
721 
722  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
723  break;
724  }
725 
726  case PLSTATE_CMAP0:
727  pltkwin_setBGFG( pls );
728  StoreCmap0( pls );
729  break;
730 
731  case PLSTATE_CMAP1:
732  StoreCmap1( pls );
733  break;
734  }
735 }
736 
737 //--------------------------------------------------------------------------
738 // plD_esc_tkwin()
739 //
740 // Escape function.
741 //
742 // Functions:
743 //
744 // PLESC_EH Handle pending events
745 // PLESC_EXPOSE Force an expose
746 // PLESC_FILL Fill polygon
747 // PLESC_FLUSH Flush X event buffer
748 // PLESC_GETC Get coordinates upon mouse click
749 // PLESC_REDRAW Force a redraw
750 // PLESC_RESIZE Force a resize
751 //--------------------------------------------------------------------------
752 
753 void
754 plD_esc_tkwin( PLStream *pls, PLINT op, void *ptr )
755 {
756  TkwDev *dev = (TkwDev *) pls->dev;
757 #ifndef USE_TK
758  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
759 #endif
760  dbug_enter( "plD_esc_tkw" );
761  if ( dev->flags & 1 )
762  return;
763 
764  switch ( op )
765  {
766  case PLESC_EH:
767 #ifndef USE_TK
768  HandleEvents( pls );
769 #endif
770  break;
771 
772  case PLESC_EXPOSE:
773  ExposeCmd( pls, (PLDisplay *) ptr );
774  break;
775 
776  case PLESC_FILL:
777  FillPolygonCmd( pls );
778  break;
779 
780  case PLESC_FLUSH:
781 #ifndef USE_TK
782  HandleEvents( pls );
783  XFlush( tkwd->display );
784 #endif
785  break;
786 
787  case PLESC_GETC:
788 #ifndef USE_TK
789  GetCursorCmd( pls, (PLGraphicsIn *) ptr );
790 #endif
791  break;
792 
793  case PLESC_REDRAW:
794  RedrawCmd( pls );
795  break;
796 
797  case PLESC_RESIZE:
798  ResizeCmd( pls, (PLDisplay *) ptr );
799  break;
800 
801 // Added by Vince, disabled by default since we want a minimal patch
802 #ifdef USING_PLESC_COPY
803  case PLESC_COPY:
804  CopyCommand( pls );
805  break;
806 #endif
807  }
808 }
809 
810 #ifdef USING_PLESC_COPY
811 //--------------------------------------------------------------------------
812 // CopyCommand()
813 //
814 // Copy a rectangle to a new part of the image.
815 // Points described in first 3 elements of pls->dev_x[] and pls->dev_y[].
816 //--------------------------------------------------------------------------
817 
818 static void
819 CopyCommand( PLStream *pls )
820 {
821  int x0, w, x1, y0, h, y1;
822  TkwDev *dev = (TkwDev *) pls->dev;
823  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
824 
825  x0 = (int) ( dev->xscale * pls->dev_x[0] );
826  x1 = (int) ( dev->xscale * pls->dev_x[2] );
827  y0 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[0] ) );
828  y1 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[2] ) );
829  w = (int) ( dev->xscale * ( pls->dev_x[1] - pls->dev_x[0] ) );
830  h = (int) ( -dev->yscale * ( pls->dev_y[1] - pls->dev_y[0] ) );
831 
832  if ( dev->write_to_window )
833  XCopyArea( tkwd->display, dev->window, dev->window, dev->gc,
834  x0, y0, w, h, x1, y1 );
835 
836  if ( dev->write_to_pixmap )
837  XCopyArea( tkwd->display, dev->pixmap, dev->pixmap, dev->gc,
838  x0, y0, w, h, x1, y1 );
839 }
840 #endif
841 
842 //--------------------------------------------------------------------------
843 // FillPolygonCmd()
844 //
845 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
846 // Only solid color fill supported.
847 //--------------------------------------------------------------------------
848 
849 static void
850 FillPolygonCmd( PLStream *pls )
851 {
852  TkwDev *dev = (TkwDev *) pls->dev;
853  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
854  XPoint _pts[PL_MAXPOLY];
855  XPoint *pts;
856  int i;
857 
858  if ( pls->dev_npts > PL_MAXPOLY )
859  {
860  pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) );
861  }
862  else
863  {
864  pts = _pts;
865  }
866 
867  for ( i = 0; i < pls->dev_npts; i++ )
868  {
869  pts[i].x = (short) ( dev->xscale * pls->dev_x[i] );
870  pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) );
871  }
872 
873 // Fill polygons
874 
875  if ( dev->write_to_window )
876  XFillPolygon( tkwd->display, dev->window, dev->gc,
877  pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
878 
879  if ( dev->write_to_pixmap )
880  XFillPolygon( tkwd->display, dev->pixmap, dev->gc,
881  pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
882 
883 // If in debug mode, draw outline of boxes being filled
884 
885 #ifdef DEBUG
886  if ( pls->debug )
887  {
888  XSetForeground( tkwd->display, dev->gc, tkwd->fgcolor.pixel );
889  if ( dev->write_to_window )
890  XDrawLines( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts,
891  CoordModeOrigin );
892 
893  if ( dev->write_to_pixmap )
894  XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts,
895  CoordModeOrigin );
896 
897  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
898  }
899 #endif
900 
901  if ( pls->dev_npts > PL_MAXPOLY )
902  {
903  free( pts );
904  }
905 }
906 
907 //--------------------------------------------------------------------------
908 // Init()
909 //
910 // Xlib initialization routine.
911 //
912 // Controlling routine for X window creation and/or initialization.
913 // The user may customize the window in the following ways:
914 //
915 // display: pls->OutFile (use plsfnam() or -display option)
916 // size: pls->xlength, pls->ylength (use plspage() or -geo option)
917 // bg color: pls->cmap0[0] (use plscolbg() or -bg option)
918 //--------------------------------------------------------------------------
919 
920 static void
921 Init( PLStream *pls )
922 {
923  PlPlotter *plf;
924  TkwDev *dev = (TkwDev *) pls->dev;
925  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
926 
927  dbug_enter( "Init" );
928 
929  dev->window = (Window) pls->window_id;
930 
931  plf = pls->plPlotterPtr;
932  if ( plf == NULL )
933  {
934  plwarn( "Init: Illegal call --- driver can't find enclosing PlPlotter" );
935  return;
936  }
937 
938 // Initialize colors
939  InitColors( pls );
940 #ifndef MAC_TCL
941  XSetWindowColormap( tkwd->display, dev->window, tkwd->map );
942 #else
943 #endif
944 
945 // Set up GC for ordinary draws
946  if ( !dev->gc )
947  dev->gc = XCreateGC( tkwd->display, dev->window, 0, 0 );
948 
949 // Set up GC for rubber-band draws
950  if ( !tkwd->gcXor )
951  {
952  XGCValues gcValues;
953  unsigned long mask;
954 
955  gcValues.background = tkwd->cmap0[0].pixel;
956  gcValues.foreground = 0xFF;
957  gcValues.function = GXxor;
958  mask = GCForeground | GCBackground | GCFunction;
959 
960  tkwd->gcXor = XCreateGC( tkwd->display, dev->window, mask, &gcValues );
961  }
962 
963 // Get initial drawing area dimensions
964  dev->width = (unsigned int) Tk_Width( plf->tkwin );
965  dev->height = (unsigned int) Tk_Height( plf->tkwin );
966  dev->border = (unsigned int) Tk_InternalBorderWidth( plf->tkwin );
967  tkwd->depth = (unsigned int) Tk_Depth( plf->tkwin );
968 
969  dev->init_width = dev->width;
970  dev->init_height = dev->height;
971 
972  // Set up flags that determine what we are writing to
973  // If nopixmap is set, ignore db
974 
975  if ( pls->nopixmap )
976  {
977  dev->write_to_pixmap = 0;
978  pls->db = 0;
979  }
980  else
981  {
982  dev->write_to_pixmap = 1;
983  }
984  dev->write_to_window = !pls->db;
985 
986  // Create pixmap for holding plot image (for expose events).
987 
988  if ( dev->write_to_pixmap )
989  CreatePixmap( pls );
990 
991  // Set drawing color
992 
993  plD_state_tkwin( pls, PLSTATE_COLOR0 );
994 
995  XSetWindowBackground( tkwd->display, dev->window, tkwd->cmap0[0].pixel );
996  XSetBackground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
997 }
998 
999 //--------------------------------------------------------------------------
1000 // ExposeCmd()
1001 //
1002 // Event handler routine for expose events.
1003 // These are "pure" exposures (no resize), so don't need to clear window.
1004 //--------------------------------------------------------------------------
1005 
1006 static void
1007 ExposeCmd( PLStream *pls, PLDisplay *pldis )
1008 {
1009  TkwDev *dev = (TkwDev *) pls->dev;
1010  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1011  int x, y, width, height;
1012 
1013  dbug_enter( "ExposeCmd" );
1014 
1015  // Return if plD_init_tkw hasn't been called yet
1016 
1017  if ( dev == NULL )
1018  {
1019  plwarn( "ExposeCmd: Illegal call -- driver uninitialized" );
1020  return;
1021  }
1022 
1023  // Exposed area. If unspecified, the entire window is used.
1024 
1025  if ( pldis == NULL )
1026  {
1027  x = 0;
1028  y = 0;
1029  width = (int) dev->width;
1030  height = (int) dev->height;
1031  }
1032  else
1033  {
1034  x = (int) pldis->x;
1035  y = (int) pldis->y;
1036  width = (int) pldis->width;
1037  height = (int) pldis->height;
1038  }
1039 
1040  // Usual case: refresh window from pixmap
1041  // DEBUG option: draws rectangle around refreshed region
1042 
1043  XSync( tkwd->display, 0 );
1044  if ( dev->write_to_pixmap )
1045  {
1046  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc,
1047  x, y, (unsigned int) width, (unsigned int) height, x, y );
1048  XSync( tkwd->display, 0 );
1049 #ifdef DEBUG
1050  if ( pls->debug )
1051  {
1052  XPoint pts[5];
1053  int x0 = x, x1 = x + width, y0 = y, y1 = y + height;
1054  pts[0].x = (short) x0; pts[0].y = (short) y0;
1055  pts[1].x = (short) x1; pts[1].y = (short) y0;
1056  pts[2].x = (short) x1; pts[2].y = (short) y1;
1057  pts[3].x = (short) x0; pts[3].y = (short) y1;
1058  pts[4].x = (short) x0; pts[4].y = (short) y0;
1059 
1060  XDrawLines( tkwd->display, dev->window, dev->gc, pts, 5,
1061  CoordModeOrigin );
1062  }
1063 #endif
1064  }
1065  else
1066  {
1067  plRemakePlot( pls );
1068  XFlush( tkwd->display );
1069  }
1070 }
1071 
1072 //--------------------------------------------------------------------------
1073 // ResizeCmd()
1074 //
1075 // Event handler routine for resize events.
1076 //--------------------------------------------------------------------------
1077 
1078 static void
1079 ResizeCmd( PLStream *pls, PLDisplay *pldis )
1080 {
1081  TkwDev *dev = (TkwDev *) pls->dev;
1082  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1083  int write_to_window = dev->write_to_window;
1084 
1085  dbug_enter( "ResizeCmd" );
1086 
1087  // Return if plD_init_tkw hasn't been called yet
1088 
1089  if ( dev == NULL )
1090  {
1091  plwarn( "ResizeCmd: Illegal call -- driver uninitialized" );
1092  return;
1093  }
1094 
1095  // Return if pointer to window not specified.
1096 
1097  if ( pldis == NULL )
1098  {
1099  plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" );
1100  return;
1101  }
1102 
1103  // Reset current window bounds
1104 
1105  dev->width = pldis->width;
1106  dev->height = pldis->height;
1107 
1108  dev->xscale = dev->width / (double) dev->init_width;
1109  dev->yscale = dev->height / (double) dev->init_height;
1110 
1111  dev->xscale = dev->xscale * dev->xscale_init;
1112  dev->yscale = dev->yscale * dev->yscale_init;
1113 
1114 #if PHYSICAL
1115  {
1116  float pxlx = (double) PIXELS_X / dev->width * DPMM;
1117  float pxly = (double) PIXELS_Y / dev->height * DPMM;
1118  plP_setpxl( pxlx, pxly );
1119  }
1120 #endif
1121 
1122  // Note: the following order MUST be obeyed -- if you instead redraw into
1123  // the window and then copy it to the pixmap, off-screen parts of the window
1124  // may contain garbage which is then transferred to the pixmap (and thus
1125  // will not go away after an expose).
1126  //
1127 
1128  // Resize pixmap using new dimensions
1129 
1130  if ( dev->write_to_pixmap )
1131  {
1132  dev->write_to_window = 0;
1133 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
1134  Tk_FreePixmap( tkwd->display, dev->pixmap );
1135 #else
1136  // Vince's original driver code used
1137  // Tk_FreePixmap(tkwd->display, dev->pixmap);
1138  //which is defined in tk-8.3 (and 8.2?) source as
1139  //void
1140  // Tk_FreePixmap(display, pixmap)
1141  // Display *display;
1142  // Pixmap pixmap;
1143  // {
1144  // XFreePixmap(display, pixmap);
1145  // Tk_FreeXId(display, (XID) pixmap);
1146  // }
1147  // But that bombed under Linux and tcl/tk8.2 so now just call
1148  // XFreePixmap directly. (Not recommended as permanent solution
1149  // because you eventually run out of resources according to man
1150  // page if you don't call Tk_FreeXId.) Vince is still looking into
1151  // how to resolve this problem.
1152  //
1153  XFreePixmap( tkwd->display, dev->pixmap );
1154 #endif
1155  CreatePixmap( pls );
1156  }
1157 
1158  // Initialize & redraw (to pixmap, if available).
1159 
1160  plD_bop_tkwin( pls );
1161  plRemakePlot( pls );
1162  XSync( tkwd->display, 0 );
1163 
1164  // If pixmap available, fake an expose
1165 
1166  if ( dev->write_to_pixmap )
1167  {
1168  dev->write_to_window = write_to_window;
1169  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
1170  dev->width, dev->height, 0, 0 );
1171  XSync( tkwd->display, 0 );
1172  }
1173 }
1174 
1175 //--------------------------------------------------------------------------
1176 // RedrawCmd()
1177 //
1178 // Handles page redraw without resize (pixmap does not get reallocated).
1179 // Calling this makes sure all necessary housekeeping gets done.
1180 //--------------------------------------------------------------------------
1181 
1182 static void
1183 RedrawCmd( PLStream *pls )
1184 {
1185  TkwDev *dev = (TkwDev *) pls->dev;
1186  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1187  int write_to_window = dev->write_to_window;
1188 
1189  dbug_enter( "RedrawCmd" );
1190 
1191  // Return if plD_init_tkw hasn't been called yet
1192 
1193  if ( dev == NULL )
1194  {
1195  plwarn( "RedrawCmd: Illegal call -- driver uninitialized" );
1196  return;
1197  }
1198 
1199  // Initialize & redraw (to pixmap, if available).
1200 
1201  if ( dev->write_to_pixmap )
1202  dev->write_to_window = 0;
1203 
1204  plD_bop_tkwin( pls );
1205  plRemakePlot( pls );
1206  XSync( tkwd->display, 0 );
1207 
1208  dev->write_to_window = write_to_window;
1209 
1210  // If pixmap available, fake an expose
1211 
1212  if ( dev->write_to_pixmap )
1213  {
1214  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
1215  dev->width, dev->height, 0, 0 );
1216  XSync( tkwd->display, 0 );
1217  }
1218 }
1219 
1220 //--------------------------------------------------------------------------
1221 // CreatePixmap()
1222 //
1223 // This routine creates a pixmap, doing error trapping in case there
1224 // isn't enough memory on the server.
1225 //--------------------------------------------------------------------------
1226 
1227 static void
1228 CreatePixmap( PLStream *pls )
1229 {
1230  TkwDev *dev = (TkwDev *) pls->dev;
1231  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1232  Tk_Window tkwin = pls->plPlotterPtr->tkwin;
1233 
1234 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1235  int ( *oldErrorHandler )( Display *, XErrorEvent * );
1236  oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler );
1237  CreatePixmapStatus = Success;
1238 #endif
1239 
1240 #ifdef MAC_TCL
1241  // MAC_TCL's version of XCreatePixmap doesn't like 0 by 0 maps
1242  if ( dev->width == 0 )
1243  {
1244  dev->width = 10;
1245  }
1246  if ( dev->height == 0 )
1247  {
1248  dev->height = 10;
1249  }
1250 #endif
1251  pldebug( "CreatePixmap",
1252  "creating pixmap: width = %d, height = %d, depth = %d\n",
1253  dev->width, dev->height, tkwd->depth );
1254 //
1255 // dev->pixmap = Tk_GetPixmap(tkwd->display, dev->window,
1256 // dev->width, dev->height, tkwd->depth);
1257 //
1258 //
1259 // Vince's original driver code used Tk_Display(tkwin) for first argument,
1260 // but that bombed on an Linux tcl/tk 8.2 machine. Something was wrong
1261 // with that value. Thus, we now use tkwd->display, and that works well.
1262 // Vince is looking into why Tk_Display(tkwin) is badly defined under 8.2.
1263 // old code:
1264 //
1265 // dev->pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1266 // Tk_Width(tkwin), Tk_Height(tkwin),
1267 // DefaultDepthOfScreen(Tk_Screen(tkwin)));
1268 //
1269  dev->pixmap = Tk_GetPixmap( tkwd->display, Tk_WindowId( tkwin ),
1270  Tk_Width( tkwin ), Tk_Height( tkwin ),
1271  DefaultDepthOfScreen( Tk_Screen( tkwin ) ) );
1272  XSync( tkwd->display, 0 );
1273 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1274  if ( CreatePixmapStatus != Success )
1275  {
1276  dev->write_to_pixmap = 0;
1277  dev->write_to_window = 1;
1278  pls->db = 0;
1279  fprintf( stderr, "\n\
1280  Warning: pixmap could not be allocated (insufficient memory on server).\n\
1281  Driver will redraw the entire plot to handle expose events.\n" );
1282  }
1283 
1284  XSetErrorHandler( oldErrorHandler );
1285 #endif
1286 }
1287 
1288 //--------------------------------------------------------------------------
1289 // GetVisual()
1290 //
1291 // Get visual info. In order to safely use a visual other than that of
1292 // the parent (which hopefully is that returned by DefaultVisual), you
1293 // must first find (using XGetRGBColormaps) or create a colormap matching
1294 // this visual and then set the colormap window attribute in the
1295 // XCreateWindow attributes and valuemask arguments. I don't do this
1296 // right now, so this is turned off by default.
1297 //--------------------------------------------------------------------------
1298 
1299 static void
1300 GetVisual( PLStream *pls )
1301 {
1302  int depth;
1303  TkwDev *dev = (TkwDev *) pls->dev;
1304  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1305 
1306  dbug_enter( "GetVisual" );
1307 
1308  tkwd->visual = Tk_GetVisual( pls->plPlotterPtr->interp,
1309  pls->plPlotterPtr->tkwin,
1310  "best",
1311  &depth, NULL );
1312  tkwd->depth = (unsigned int) depth;
1313 }
1314 
1315 //--------------------------------------------------------------------------
1316 // AllocBGFG()
1317 //
1318 // Allocate background & foreground colors. If possible, I choose pixel
1319 // values such that the fg pixel is the xor of the bg pixel, to make
1320 // rubber-banding easy to see.
1321 //--------------------------------------------------------------------------
1322 
1323 static void
1324 AllocBGFG( PLStream *pls )
1325 {
1326  TkwDev *dev = (TkwDev *) pls->dev;
1327  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1328 
1329 #ifndef USE_TK
1330  int i, j, npixels;
1331  unsigned long plane_masks[1], pixels[MAX_COLORS];
1332 #endif
1333 
1334  dbug_enter( "AllocBGFG" );
1335 
1336  // If not on a color system, just return
1337 
1338  if ( !tkwd->color )
1339  return;
1340 #ifndef USE_TK
1341  // Allocate r/w color cell for background
1342 
1343  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1344  plane_masks, 0, pixels, 1 ) )
1345  {
1346  tkwd->cmap0[0].pixel = pixels[0];
1347  }
1348  else
1349  {
1350  plexit( "couldn't allocate background color cell" );
1351  }
1352 
1353  // Allocate as many colors as we can
1354 
1355  npixels = MAX_COLORS;
1356  for (;; )
1357  {
1358  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1359  plane_masks, 0, pixels, npixels ) )
1360  break;
1361  npixels--;
1362  if ( npixels == 0 )
1363  break;
1364  }
1365 
1366  // Find the color with pixel = xor of the bg color pixel.
1367  // If a match isn't found, the last pixel allocated is used.
1368 
1369  for ( i = 0; i < npixels - 1; i++ )
1370  {
1371  if ( pixels[i] == ( ~tkwd->cmap0[0].pixel & 0xFF ) )
1372  break;
1373  }
1374 
1375  // Use this color cell for our foreground color. Then free the rest.
1376 
1377  tkwd->fgcolor.pixel = pixels[i];
1378  for ( j = 0; j < npixels; j++ )
1379  {
1380  if ( j != i )
1381  XFreeColors( tkwd->display, tkwd->map, &pixels[j], 1, 0 );
1382  }
1383 #endif
1384 }
1385 
1386 //--------------------------------------------------------------------------
1387 // pltkwin_setBGFG()
1388 //
1389 // Set background & foreground colors. Foreground over background should
1390 // have high contrast.
1391 //--------------------------------------------------------------------------
1392 
1393 void
1394 pltkwin_setBGFG( PLStream *pls )
1395 {
1396  TkwDev *dev = (TkwDev *) pls->dev;
1397  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1398  PLColor fgcolor;
1399  int gslevbg, gslevfg;
1400 
1401  dbug_enter( "pltkwin_setBGFG" );
1402 
1403  //
1404  // Set background color.
1405  //
1406  // Background defaults to black on color screens, white on grayscale (many
1407  // grayscale monitors have poor contrast, and black-on-white looks better).
1408  //
1409 
1410  if ( !tkwd->color )
1411  {
1412  pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
1413  }
1414  gslevbg = (int) ( ( (long) pls->cmap0[0].r +
1415  (long) pls->cmap0[0].g +
1416  (long) pls->cmap0[0].b ) / 3 );
1417 
1418  PLColor_to_TkColor( &pls->cmap0[0], &tkwd->cmap0[0] );
1419 
1420  //
1421  // Set foreground color.
1422  //
1423  // Used for grayscale output, since otherwise the plots can become nearly
1424  // unreadable (i.e. if colors get mapped onto grayscale values). In this
1425  // case it becomes the grayscale level for all draws, and is taken to be
1426  // black if the background is light, and white if the background is dark.
1427  // Note that white/black allocations never fail.
1428  //
1429 
1430  if ( gslevbg > 0x7F )
1431  gslevfg = 0;
1432  else
1433  gslevfg = 0xFF;
1434 
1435  fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg;
1436 
1437  PLColor_to_TkColor( &fgcolor, &tkwd->fgcolor );
1438 
1439  // Now store
1440 #ifndef USE_TK
1441  if ( tkwd->color )
1442  {
1443  XStoreColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1444  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
1445  }
1446  else
1447  {
1448  XAllocColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
1449  XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1450  }
1451 #else
1452  Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[0] );
1453  Tkw_StoreColor( pls, tkwd, &tkwd->fgcolor );
1454 #endif
1455 }
1456 
1457 //--------------------------------------------------------------------------
1458 // InitColors()
1459 //
1460 // Does all color initialization.
1461 //--------------------------------------------------------------------------
1462 
1463 static void
1464 InitColors( PLStream *pls )
1465 {
1466  TkwDev *dev = (TkwDev *) pls->dev;
1467  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1468 
1469  dbug_enter( "InitColors" );
1470 
1471  // Allocate and initialize color maps.
1472  // Defer cmap1 allocation until it's actually used
1473 
1474  if ( tkwd->color )
1475  {
1476  if ( plplot_tkwin_ccmap )
1477  {
1478  AllocCustomMap( pls );
1479  }
1480  else
1481  {
1482  AllocCmap0( pls );
1483  }
1484  }
1485 }
1486 
1487 //--------------------------------------------------------------------------
1488 // AllocCustomMap()
1489 //
1490 // Initializes custom color map and all the cruft that goes with it.
1491 //
1492 // Assuming all color X displays do 256 colors, the breakdown is as follows:
1493 //
1494 // XWM_COLORS Number of low "pixel" values to copy. These are typically
1495 // allocated first, thus are in use by the window manager. I
1496 // copy them to reduce flicker.
1497 //
1498 // CMAP0_COLORS Color map 0 entries. I allocate these both in the default
1499 // colormap and the custom colormap to reduce flicker.
1500 //
1501 // CMAP1_COLORS Color map 1 entries. There should be as many as practical
1502 // available for smooth shading. On the order of 50-100 is
1503 // pretty reasonable. You don't really need all 256,
1504 // especially if all you're going to do is to print it to
1505 // postscript (which doesn't have any intrinsic limitation on
1506 // the number of colors).
1507 //
1508 // It's important to leave some extra colors unallocated for Tk. In
1509 // particular the palette tools require a fair amount. I recommend leaving
1510 // at least 40 or so free.
1511 //--------------------------------------------------------------------------
1512 
1513 static void
1514 AllocCustomMap( PLStream *pls )
1515 {
1516  TkwDev *dev = (TkwDev *) pls->dev;
1517  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1518 
1519  XColor xwm_colors[MAX_COLORS];
1520  int i;
1521 #ifndef USE_TK
1522  int npixels;
1523  unsigned long plane_masks[1], pixels[MAX_COLORS];
1524 #endif
1525 
1526  dbug_enter( "AllocCustomMap" );
1527 
1528  // Determine current default colors
1529 
1530  for ( i = 0; i < MAX_COLORS; i++ )
1531  {
1532  xwm_colors[i].pixel = (long unsigned) i;
1533  }
1534 #ifndef MAC_TCL
1535  XQueryColors( tkwd->display, tkwd->map, xwm_colors, MAX_COLORS );
1536 #endif
1537 
1538  // Allocate cmap0 colors in the default colormap.
1539  // The custom cmap0 colors are later stored at the same pixel values.
1540  // This is a really cool trick to reduce the flicker when changing colormaps.
1541  //
1542 
1543  AllocCmap0( pls );
1544  XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1545 
1546  // Create new color map
1547 
1548  tkwd->map = XCreateColormap( tkwd->display, DefaultRootWindow( tkwd->display ),
1549  tkwd->visual, AllocNone );
1550 
1551  // Now allocate all colors so we can fill the ones we want to copy
1552 
1553 #ifndef USE_TK
1554  npixels = MAX_COLORS;
1555  for (;; )
1556  {
1557  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1558  plane_masks, 0, pixels, npixels ) )
1559  break;
1560  npixels--;
1561  if ( npixels == 0 )
1562  plexit( "couldn't allocate any colors" );
1563  }
1564 
1565  // Fill the low colors since those are in use by the window manager
1566 
1567  for ( i = 0; i < XWM_COLORS; i++ )
1568  {
1569  XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
1570  pixels[xwm_colors[i].pixel] = 0;
1571  }
1572 
1573  // Fill the ones we will use in cmap0
1574 
1575  for ( i = 0; i < tkwd->ncol0; i++ )
1576  {
1577  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
1578  pixels[tkwd->cmap0[i].pixel] = 0;
1579  }
1580 
1581  // Finally, if the colormap was saved by an external agent, see if there are
1582  // any differences from the current default map and save those! A very cool
1583  // (or sick, depending on how you look at it) trick to get over some X and
1584  // Tk limitations.
1585  //
1586 
1587  if ( sxwm_colors_set )
1588  {
1589  for ( i = 0; i < MAX_COLORS; i++ )
1590  {
1591  if ( ( xwm_colors[i].red != sxwm_colors[i].red ) ||
1592  ( xwm_colors[i].green != sxwm_colors[i].green ) ||
1593  ( xwm_colors[i].blue != sxwm_colors[i].blue ) )
1594  {
1595  if ( pixels[i] != 0 )
1596  {
1597  XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
1598  pixels[i] = 0;
1599  }
1600  }
1601  }
1602  }
1603 
1604  // Now free the ones we're not interested in
1605 
1606  for ( i = 0; i < npixels; i++ )
1607  {
1608  if ( pixels[i] != 0 )
1609  XFreeColors( tkwd->display, tkwd->map, &pixels[i], 1, 0 );
1610  }
1611 #endif
1612  // Allocate colors in cmap 1
1613 
1614  AllocCmap1( pls );
1615 }
1616 
1617 //--------------------------------------------------------------------------
1618 // AllocCmap0()
1619 //
1620 // Allocate & initialize cmap0 entries.
1621 //--------------------------------------------------------------------------
1622 
1623 static void
1624 AllocCmap0( PLStream *pls )
1625 {
1626  TkwDev *dev = (TkwDev *) pls->dev;
1627  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1628 
1629 #ifndef USE_TK
1630  int npixels;
1631  int i;
1632  unsigned long plane_masks[1], pixels[MAX_COLORS];
1633 #endif
1634 
1635  dbug_enter( "AllocCmap0" );
1636 
1637  // Allocate and assign colors in cmap 0
1638 
1639 #ifndef USE_TK
1640  npixels = pls->ncol0 - 1;
1641  for (;; )
1642  {
1643  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1644  plane_masks, 0, &pixels[1], npixels ) )
1645  break;
1646  npixels--;
1647  if ( npixels == 0 )
1648  plexit( "couldn't allocate any colors" );
1649  }
1650 
1651  tkwd->ncol0 = npixels + 1;
1652  for ( i = 1; i < tkwd->ncol0; i++ )
1653  {
1654  tkwd->cmap0[i].pixel = pixels[i];
1655  }
1656 #else
1657  // We use the Tk color scheme
1658  tkwd->ncol0 = pls->ncol0;
1659 #endif
1660  StoreCmap0( pls );
1661 }
1662 
1663 //--------------------------------------------------------------------------
1664 // AllocCmap1()
1665 //
1666 // Allocate & initialize cmap1 entries. If using the default color map,
1667 // must severely limit number of colors otherwise TK won't have enough.
1668 //--------------------------------------------------------------------------
1669 
1670 static void
1671 AllocCmap1( PLStream *pls )
1672 {
1673  TkwDev *dev = (TkwDev *) pls->dev;
1674  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1675 
1676  int npixels;
1677 #ifndef USE_TK
1678  int i, j;
1679  unsigned long plane_masks[1], pixels[MAX_COLORS];
1680 #endif
1681 
1682  dbug_enter( "AllocCmap1" );
1683 
1684  // Allocate colors in cmap 1
1685 
1686  npixels = MAX( 2, MIN( CMAP1_COLORS, pls->ncol1 ) );
1687 #ifndef USE_TK
1688  for (;; )
1689  {
1690  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1691  plane_masks, 0, pixels, npixels ) )
1692  break;
1693  npixels--;
1694  if ( npixels == 0 )
1695  break;
1696  }
1697 
1698  if ( npixels < 2 )
1699  {
1700  tkwd->ncol1 = -1;
1701  fprintf( stderr,
1702  "Warning: unable to allocate sufficient colors in cmap1\n" );
1703  return;
1704  }
1705  else
1706  {
1707  tkwd->ncol1 = npixels;
1708  if ( pls->verbose )
1709  fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", npixels );
1710  }
1711 
1712  // Don't assign pixels sequentially, to avoid strange problems with xor GC's
1713  // Skipping by 2 seems to do the job best
1714 
1715  for ( j = i = 0; i < tkwd->ncol1; i++ )
1716  {
1717  while ( pixels[j] == 0 )
1718  j++;
1719 
1720  tkwd->cmap1[i].pixel = pixels[j];
1721  pixels[j] = 0;
1722 
1723  j += 2;
1724  if ( j >= tkwd->ncol1 )
1725  j = 0;
1726  }
1727 #else
1728  tkwd->ncol1 = npixels;
1729 #endif
1730  StoreCmap1( pls );
1731 }
1732 
1733 //--------------------------------------------------------------------------
1734 // StoreCmap0()
1735 //
1736 // Stores cmap 0 entries in X-server colormap.
1737 //--------------------------------------------------------------------------
1738 
1739 static void
1740 StoreCmap0( PLStream *pls )
1741 {
1742  TkwDev *dev = (TkwDev *) pls->dev;
1743  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1744  int i;
1745 
1746  if ( !tkwd->color )
1747  return;
1748 
1749  for ( i = 1; i < tkwd->ncol0; i++ )
1750  {
1751  PLColor_to_TkColor( &pls->cmap0[i], &tkwd->cmap0[i] );
1752 #ifndef USE_TK
1753  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
1754 #else
1755  Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[i] );
1756 #endif
1757  }
1758 }
1759 
1760 void CopyColour( XColor* from, XColor* to )
1761 {
1762  to->pixel = from->pixel;
1763  to->red = from->red;
1764  to->blue = from->blue;
1765  to->green = from->green;
1766  to->flags = from->flags;
1767 }
1768 
1769 //--------------------------------------------------------------------------
1770 // StoreCmap1()
1771 //
1772 // Stores cmap 1 entries in X-server colormap.
1773 //--------------------------------------------------------------------------
1774 
1775 static void
1776 StoreCmap1( PLStream *pls )
1777 {
1778  TkwDev *dev = (TkwDev *) pls->dev;
1779  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1780 
1781  PLColor cmap1color;
1782  int i;
1783 
1784  if ( !tkwd->color )
1785  return;
1786 
1787  for ( i = 0; i < tkwd->ncol1; i++ )
1788  {
1789  plcol_interp( pls, &cmap1color, i, tkwd->ncol1 );
1790  PLColor_to_TkColor( &cmap1color, &tkwd->cmap1[i] );
1791 #ifndef USE_TK
1792  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap1[i] );
1793 #else
1794  Tkw_StoreColor( pls, tkwd, &tkwd->cmap1[i] );
1795 #endif
1796  }
1797 }
1798 
1799 void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col )
1800 {
1801  XColor *xc;
1802 #ifndef USE_TK
1803  XStoreColor( tkwd->display, tkwd->map, col );
1804 #else
1805  (void) tkwd; // tkwd unused in this case
1806  // We're probably losing memory here
1807  xc = Tk_GetColorByValue( pls->plPlotterPtr->tkwin, col );
1808  CopyColour( xc, col );
1809 #endif
1810 }
1811 
1812 //--------------------------------------------------------------------------
1813 // void PLColor_to_TkColor()
1814 //
1815 // Copies the supplied PLColor to an XColor, padding with bits as necessary
1816 // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
1817 // The argument types follow the same order as in the function name.
1818 //--------------------------------------------------------------------------
1819 
1820 #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) )
1821 #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 )
1822 
1823 void
1824 PLColor_to_TkColor( PLColor *plcolor, XColor *xcolor )
1825 {
1826  xcolor->red = (short unsigned) ToXColor( plcolor->r );
1827  xcolor->green = (short unsigned) ToXColor( plcolor->g );
1828  xcolor->blue = (short unsigned) ToXColor( plcolor->b );
1829  xcolor->flags = DoRed | DoGreen | DoBlue;
1830 }
1831 
1832 //--------------------------------------------------------------------------
1833 // void PLColor_from_TkColor()
1834 //
1835 // Copies the supplied XColor to a PLColor, stripping off bits as
1836 // necessary. See the previous routine for more info.
1837 //--------------------------------------------------------------------------
1838 
1839 void
1840 PLColor_from_TkColor( PLColor *plcolor, XColor *xcolor )
1841 {
1842  plcolor->r = (unsigned char) ToPLColor( xcolor->red );
1843  plcolor->g = (unsigned char) ToPLColor( xcolor->green );
1844  plcolor->b = (unsigned char) ToPLColor( xcolor->blue );
1845 }
1846 
1847 //--------------------------------------------------------------------------
1848 // void PLColor_from_TkColor_Changed()
1849 //
1850 // Copies the supplied XColor to a PLColor, stripping off bits as
1851 // necessary. See the previous routine for more info.
1852 //
1853 // Returns 1 if the color was different from the old one.
1854 //--------------------------------------------------------------------------
1855 
1856 int
1857 PLColor_from_TkColor_Changed( PLColor *plcolor, XColor *xcolor )
1858 {
1859  int changed = 0;
1860  int color;
1861  color = ToPLColor( xcolor->red );
1862 
1863  if ( plcolor->r != color )
1864  {
1865  changed = 1;
1866  plcolor->r = (unsigned char) color;
1867  }
1868  color = ToPLColor( xcolor->green );
1869  if ( plcolor->g != color )
1870  {
1871  changed = 1;
1872  plcolor->g = (unsigned char) color;
1873  }
1874  color = ToPLColor( xcolor->blue );
1875  if ( plcolor->b != color )
1876  {
1877  changed = 1;
1878  plcolor->b = (unsigned char) color;
1879  }
1880  return changed;
1881 }
1882 
1883 //--------------------------------------------------------------------------
1884 // int pltk_AreWeGrayscale(PlPlotter *plf)
1885 //
1886 // Determines if we're using a monochrome or grayscale device.
1887 // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland.
1888 // Changed July 1996 by Vince: now uses Tk to check the enclosing PlPlotter
1889 //--------------------------------------------------------------------------
1890 
1891 static int
1892 pltk_AreWeGrayscale( PlPlotter *plf )
1893 {
1894 #if defined ( __cplusplus ) || defined ( c_plusplus )
1895 #define THING c_class
1896 #else
1897 #define THING class
1898 #endif
1899 
1900  Visual* visual;
1901  // get the window's Visual
1902  visual = Tk_Visual( plf->tkwin );
1903  if ( ( visual->THING != GrayScale ) && ( visual->THING != StaticGray ) )
1904  return ( 0 );
1905  // if we got this far, only StaticGray and GrayScale classes available
1906  return ( 1 );
1907 }
1908 
1909 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1910 //--------------------------------------------------------------------------
1911 // CreatePixmapErrorHandler()
1912 //
1913 // Error handler used in CreatePixmap() to catch errors in allocating
1914 // storage for pixmap. This way we can nicely substitute redraws for
1915 // pixmap copies if the server has insufficient memory.
1916 //--------------------------------------------------------------------------
1917 
1918 static int
1919 CreatePixmapErrorHandler( Display *display, XErrorEvent *error )
1920 {
1921  if ( error->error_code == BadAlloc )
1922  {
1923  CreatePixmapStatus = error->error_code;
1924  }
1925  else
1926  {
1927  char buffer[256];
1928  XGetErrorText( display, error->error_code, buffer, 256 );
1929  fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer );
1930  }
1931  return 1;
1932 }
1933 #endif
1934 
1935 #else
1936 int
1938 {
1939  return 0;
1940 }
1941 
1942 #endif // PLD_tkwin
1943 
1944 void * ckcalloc( size_t nmemb, size_t size )
1945 {
1946  long *ptr;
1947  long *p;
1948  size *= nmemb;
1949  ptr = (long *) malloc( size );
1950  if ( !ptr )
1951  return ( 0 );
1952 
1953 #if !__POWERPC__
1954 
1955  for ( size = ( size / sizeof ( long ) ) + 1, p = ptr; --size; )
1956  *p++ = 0;
1957 
1958 #else
1959 
1960  for ( size = ( size / sizeof ( long ) ) + 1, p = ptr - 1; --size; )
1961  *++p = 0;
1962 
1963 #endif
1964 
1965  return ( ptr );
1966 }