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