PLplot  5.11.1
 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_wait_tkwin( PLStream * );
215 void plD_open_tkwin( PLStream *pls );
216 
218 {
219 #ifndef ENABLE_DYNDRIVERS
220  pdt->pl_MenuStr = "PLplot Tk plotter";
221  pdt->pl_DevName = "tkwin";
222 #endif
224  pdt->pl_seq = 45;
225  pdt->pl_init = (plD_init_fp) plD_init_tkwin;
226  pdt->pl_line = (plD_line_fp) plD_line_tkwin;
227  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tkwin;
228  pdt->pl_eop = (plD_eop_fp) plD_eop_tkwin;
229  pdt->pl_bop = (plD_bop_fp) plD_bop_tkwin;
230  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tkwin;
231  pdt->pl_state = (plD_state_fp) plD_state_tkwin;
232  pdt->pl_esc = (plD_esc_fp) plD_esc_tkwin;
233  pdt->pl_wait = (plD_wait_fp) plD_wait_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.
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 // plD_wait_tkwin()
551 //
552 // User must hit return (or third mouse button) to continue.
553 //--------------------------------------------------------------------------
554 
555 void
556 plD_wait_tkwin( PLStream *pls )
557 {
558  TkwDev *dev = (TkwDev *) pls->dev;
559  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
560 
561  dbug_enter( "plD_wait_tkw" );
562  if ( dev->flags & 1 )
563  return;
564 
565  if ( !pls->nopause )
566  WaitForPage( pls );
567 }
568 
569 //--------------------------------------------------------------------------
570 // WaitForPage()
571 //
572 // This routine waits for the user to advance the plot, while handling
573 // all other events.
574 //--------------------------------------------------------------------------
575 
576 static void
577 WaitForPage( PLStream *pls )
578 {
579  PlPlotter *plf = pls->plPlotterPtr;
580  TkwDev *dev = (TkwDev *) pls->dev;
581 
582  dbug_enter( "WaitForPage" );
583 
584  dev->flags &= 1;
585  if ( plf == NULL )
586  {
587  plwarn( "WaitForPage: Illegal call --- driver can't find enclosing PlPlotter" );
588  return;
589  }
590  PlplotterAtEop( plf->interp, plf );
591 
592  while ( !( dev->flags ) && !Tcl_InterpDeleted( plf->interp ) && ( Tk_GetNumMainWindows() > 0 ) )
593  {
594  Tcl_DoOneEvent( 0 );
595  }
596 
597  if ( Tcl_InterpDeleted( plf->interp ) || ( Tk_GetNumMainWindows() <= 0 ) )
598  {
599  dev->flags |= 1;
600  }
601 
602  dev->flags &= 1;
603 }
604 
605 //--------------------------------------------------------------------------
606 // plD_bop_tkwin()
607 //
608 // Set up for the next page.
609 //--------------------------------------------------------------------------
610 
611 void
612 plD_bop_tkwin( PLStream *pls )
613 {
614  PlPlotter *plf = pls->plPlotterPtr;
615  TkwDev *dev = (TkwDev *) pls->dev;
616  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
617 
618  XRectangle xrect;
619  xrect.x = 0; xrect.y = 0;
620  xrect.width = (short unsigned) dev->width;
621  xrect.height = (short unsigned) dev->height;
622 
623  dbug_enter( "plD_bop_tkw" );
624  if ( dev->flags & 1 )
625  return;
626 
627  if ( dev->write_to_window )
628  {
629 #ifdef MAC_TCL
630  // MacTk only has these X calls
631  XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
632  XFillRectangles( tkwd->display, dev->window, dev->gc, &xrect, 1 );
633  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
634 #else
635  XClearWindow( tkwd->display, dev->window );
636 #endif
637  }
638  if ( dev->write_to_pixmap )
639  {
640  XSetForeground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
641  XFillRectangles( tkwd->display, dev->pixmap, dev->gc, &xrect, 1 );
642  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
643  }
644  XSync( tkwd->display, 0 );
645  pls->page++;
646  PlplotterAtBop( plf->interp, plf );
647 }
648 
649 //--------------------------------------------------------------------------
650 // plD_tidy_tkwin()
651 //
652 // Close graphics file
653 //--------------------------------------------------------------------------
654 
655 void
656 plD_tidy_tkwin( PLStream *pls )
657 {
658  TkwDev *dev = (TkwDev *) pls->dev;
659  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
660 
661  dbug_enter( "plD_tidy_tkw" );
662 
663  tkwd->nstreams--;
664  if ( tkwd->nstreams == 0 )
665  {
666  int ixwd = tkwd->ixwd;
667  XFreeGC( tkwd->display, dev->gc );
668 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
669  XCloseDisplay( tkwd->display );
670 #endif
671  free_mem( tkwDisplay[ixwd] );
672  }
673  //
674  // Vince removed this November 1999. It seems as if a simple
675  // 'plframe .p ; destroy .p' leaves a temporary buf file open
676  // if we clear this flag here. It should be checked and then
677  // cleared by whoever called us. An alternative fix would
678  // be to carry out the check/tidy here. The plframe widget
679  // handles this stuff for us.
680  //
681  // pls->plbuf_write = 0;
682 }
683 
684 //--------------------------------------------------------------------------
685 // plD_state_tkwin()
686 //
687 // Handle change in PLStream state (color, pen width, fill attribute, etc).
688 //--------------------------------------------------------------------------
689 
690 void
691 plD_state_tkwin( PLStream *pls, PLINT op )
692 {
693  TkwDev *dev = (TkwDev *) pls->dev;
694  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
695  dbug_enter( "plD_state_tkw" );
696  if ( dev->flags & 1 )
697  return;
698 
699  switch ( op )
700  {
701  case PLSTATE_WIDTH:
702  break;
703 
704  case PLSTATE_COLOR0: {
705  int icol0 = pls->icol0;
706  if ( tkwd->color )
707  {
708  if ( icol0 == PL_RGB_COLOR )
709  {
710  PLColor_to_TkColor( &pls->curcolor, &dev->curcolor );
711  Tkw_StoreColor( pls, tkwd, &dev->curcolor );
712  }
713  else
714  {
715  dev->curcolor = tkwd->cmap0[icol0];
716  }
717  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
718  }
719  else
720  {
721  dev->curcolor = tkwd->fgcolor;
722  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
723  }
724  break;
725  }
726 
727  case PLSTATE_COLOR1: {
728  int icol1;
729 
730  if ( tkwd->ncol1 == 0 )
731  AllocCmap1( pls );
732 
733  if ( tkwd->ncol1 < 2 )
734  break;
735 
736  icol1 = ( pls->icol1 * ( tkwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 );
737  if ( tkwd->color )
738  dev->curcolor = tkwd->cmap1[icol1];
739  else
740  dev->curcolor = tkwd->fgcolor;
741 
742  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
743  break;
744  }
745 
746  case PLSTATE_CMAP0:
747  pltkwin_setBGFG( pls );
748  StoreCmap0( pls );
749  break;
750 
751  case PLSTATE_CMAP1:
752  StoreCmap1( pls );
753  break;
754  }
755 }
756 
757 //--------------------------------------------------------------------------
758 // plD_esc_tkwin()
759 //
760 // Escape function.
761 //
762 // Functions:
763 //
764 // PLESC_EH Handle pending events
765 // PLESC_EXPOSE Force an expose
766 // PLESC_FILL Fill polygon
767 // PLESC_FLUSH Flush X event buffer
768 // PLESC_GETC Get coordinates upon mouse click
769 // PLESC_REDRAW Force a redraw
770 // PLESC_RESIZE Force a resize
771 //--------------------------------------------------------------------------
772 
773 void
774 plD_esc_tkwin( PLStream *pls, PLINT op, void *ptr )
775 {
776  TkwDev *dev = (TkwDev *) pls->dev;
777 #ifndef USE_TK
778  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
779 #endif
780  dbug_enter( "plD_esc_tkw" );
781  if ( dev->flags & 1 )
782  return;
783 
784  switch ( op )
785  {
786  case PLESC_EH:
787 #ifndef USE_TK
788  HandleEvents( pls );
789 #endif
790  break;
791 
792  case PLESC_EXPOSE:
793  ExposeCmd( pls, (PLDisplay *) ptr );
794  break;
795 
796  case PLESC_FILL:
797  FillPolygonCmd( pls );
798  break;
799 
800  case PLESC_FLUSH:
801 #ifndef USE_TK
802  HandleEvents( pls );
803  XFlush( tkwd->display );
804 #endif
805  break;
806 
807  case PLESC_GETC:
808 #ifndef USE_TK
809  GetCursorCmd( pls, (PLGraphicsIn *) ptr );
810 #endif
811  break;
812 
813  case PLESC_REDRAW:
814  RedrawCmd( pls );
815  break;
816 
817  case PLESC_RESIZE:
818  ResizeCmd( pls, (PLDisplay *) ptr );
819  break;
820 
821 // Added by Vince, disabled by default since we want a minimal patch
822 #ifdef USING_PLESC_COPY
823  case PLESC_COPY:
824  CopyCommand( pls );
825  break;
826 #endif
827  }
828 }
829 
830 #ifdef USING_PLESC_COPY
831 //--------------------------------------------------------------------------
832 // CopyCommand()
833 //
834 // Copy a rectangle to a new part of the image.
835 // Points described in first 3 elements of pls->dev_x[] and pls->dev_y[].
836 //--------------------------------------------------------------------------
837 
838 static void
839 CopyCommand( PLStream *pls )
840 {
841  int x0, w, x1, y0, h, y1;
842  TkwDev *dev = (TkwDev *) pls->dev;
843  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
844 
845  x0 = (int) ( dev->xscale * pls->dev_x[0] );
846  x1 = (int) ( dev->xscale * pls->dev_x[2] );
847  y0 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[0] ) );
848  y1 = (int) ( dev->yscale * ( dev->ylen - pls->dev_y[2] ) );
849  w = (int) ( dev->xscale * ( pls->dev_x[1] - pls->dev_x[0] ) );
850  h = (int) ( -dev->yscale * ( pls->dev_y[1] - pls->dev_y[0] ) );
851 
852  if ( dev->write_to_window )
853  XCopyArea( tkwd->display, dev->window, dev->window, dev->gc,
854  x0, y0, w, h, x1, y1 );
855 
856  if ( dev->write_to_pixmap )
857  XCopyArea( tkwd->display, dev->pixmap, dev->pixmap, dev->gc,
858  x0, y0, w, h, x1, y1 );
859 }
860 #endif
861 
862 //--------------------------------------------------------------------------
863 // FillPolygonCmd()
864 //
865 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
866 // Only solid color fill supported.
867 //--------------------------------------------------------------------------
868 
869 static void
870 FillPolygonCmd( PLStream *pls )
871 {
872  TkwDev *dev = (TkwDev *) pls->dev;
873  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
874  XPoint _pts[PL_MAXPOLY];
875  XPoint *pts;
876  int i;
877 
878  if ( pls->dev_npts > PL_MAXPOLY )
879  {
880  pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) );
881  }
882  else
883  {
884  pts = _pts;
885  }
886 
887  for ( i = 0; i < pls->dev_npts; i++ )
888  {
889  pts[i].x = (short) ( dev->xscale * pls->dev_x[i] );
890  pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) );
891  }
892 
893 // Fill polygons
894 
895  if ( dev->write_to_window )
896  XFillPolygon( tkwd->display, dev->window, dev->gc,
897  pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
898 
899  if ( dev->write_to_pixmap )
900  XFillPolygon( tkwd->display, dev->pixmap, dev->gc,
901  pts, pls->dev_npts, Nonconvex, CoordModeOrigin );
902 
903 // If in debug mode, draw outline of boxes being filled
904 
905 #ifdef DEBUG
906  if ( pls->debug )
907  {
908  XSetForeground( tkwd->display, dev->gc, tkwd->fgcolor.pixel );
909  if ( dev->write_to_window )
910  XDrawLines( tkwd->display, dev->window, dev->gc, pts, pls->dev_npts,
911  CoordModeOrigin );
912 
913  if ( dev->write_to_pixmap )
914  XDrawLines( tkwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts,
915  CoordModeOrigin );
916 
917  XSetForeground( tkwd->display, dev->gc, dev->curcolor.pixel );
918  }
919 #endif
920 
921  if ( pls->dev_npts > PL_MAXPOLY )
922  {
923  free( pts );
924  }
925 }
926 
927 //--------------------------------------------------------------------------
928 // Init()
929 //
930 // Xlib initialization routine.
931 //
932 // Controlling routine for X window creation and/or initialization.
933 // The user may customize the window in the following ways:
934 //
935 // display: pls->OutFile (use plsfnam() or -display option)
936 // size: pls->xlength, pls->ylength (use plspage() or -geo option)
937 // bg color: pls->cmap0[0] (use plscolbg() or -bg option)
938 //--------------------------------------------------------------------------
939 
940 static void
941 Init( PLStream *pls )
942 {
943  PlPlotter *plf;
944  TkwDev *dev = (TkwDev *) pls->dev;
945  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
946 
947  dbug_enter( "Init" );
948 
949  dev->window = (Window) pls->window_id;
950 
951  plf = pls->plPlotterPtr;
952  if ( plf == NULL )
953  {
954  plwarn( "Init: Illegal call --- driver can't find enclosing PlPlotter" );
955  return;
956  }
957 
958 // Initialize colors
959  InitColors( pls );
960 #ifndef MAC_TCL
961  XSetWindowColormap( tkwd->display, dev->window, tkwd->map );
962 #else
963 #endif
964 
965 // Set up GC for ordinary draws
966  if ( !dev->gc )
967  dev->gc = XCreateGC( tkwd->display, dev->window, 0, 0 );
968 
969 // Set up GC for rubber-band draws
970  if ( !tkwd->gcXor )
971  {
972  XGCValues gcValues;
973  unsigned long mask;
974 
975  gcValues.background = tkwd->cmap0[0].pixel;
976  gcValues.foreground = 0xFF;
977  gcValues.function = GXxor;
978  mask = GCForeground | GCBackground | GCFunction;
979 
980  tkwd->gcXor = XCreateGC( tkwd->display, dev->window, mask, &gcValues );
981  }
982 
983 // Get initial drawing area dimensions
984  dev->width = (unsigned int) Tk_Width( plf->tkwin );
985  dev->height = (unsigned int) Tk_Height( plf->tkwin );
986  dev->border = (unsigned int) Tk_InternalBorderWidth( plf->tkwin );
987  tkwd->depth = (unsigned int) Tk_Depth( plf->tkwin );
988 
989  dev->init_width = dev->width;
990  dev->init_height = dev->height;
991 
992  // Set up flags that determine what we are writing to
993  // If nopixmap is set, ignore db
994 
995  if ( pls->nopixmap )
996  {
997  dev->write_to_pixmap = 0;
998  pls->db = 0;
999  }
1000  else
1001  {
1002  dev->write_to_pixmap = 1;
1003  }
1004  dev->write_to_window = !pls->db;
1005 
1006  // Create pixmap for holding plot image (for expose events).
1007 
1008  if ( dev->write_to_pixmap )
1009  CreatePixmap( pls );
1010 
1011  // Set drawing color
1012 
1013  plD_state_tkwin( pls, PLSTATE_COLOR0 );
1014 
1015  XSetWindowBackground( tkwd->display, dev->window, tkwd->cmap0[0].pixel );
1016  XSetBackground( tkwd->display, dev->gc, tkwd->cmap0[0].pixel );
1017 }
1018 
1019 //--------------------------------------------------------------------------
1020 // ExposeCmd()
1021 //
1022 // Event handler routine for expose events.
1023 // These are "pure" exposures (no resize), so don't need to clear window.
1024 //--------------------------------------------------------------------------
1025 
1026 static void
1027 ExposeCmd( PLStream *pls, PLDisplay *pldis )
1028 {
1029  TkwDev *dev = (TkwDev *) pls->dev;
1030  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1031  int x, y, width, height;
1032 
1033  dbug_enter( "ExposeCmd" );
1034 
1035  // Return if plD_init_tkw hasn't been called yet
1036 
1037  if ( dev == NULL )
1038  {
1039  plwarn( "ExposeCmd: Illegal call -- driver uninitialized" );
1040  return;
1041  }
1042 
1043  // Exposed area. If unspecified, the entire window is used.
1044 
1045  if ( pldis == NULL )
1046  {
1047  x = 0;
1048  y = 0;
1049  width = (int) dev->width;
1050  height = (int) dev->height;
1051  }
1052  else
1053  {
1054  x = (int) pldis->x;
1055  y = (int) pldis->y;
1056  width = (int) pldis->width;
1057  height = (int) pldis->height;
1058  }
1059 
1060  // Usual case: refresh window from pixmap
1061  // DEBUG option: draws rectangle around refreshed region
1062 
1063  XSync( tkwd->display, 0 );
1064  if ( dev->write_to_pixmap )
1065  {
1066  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc,
1067  x, y, (unsigned int) width, (unsigned int) height, x, y );
1068  XSync( tkwd->display, 0 );
1069 #ifdef DEBUG
1070  if ( pls->debug )
1071  {
1072  XPoint pts[5];
1073  int x0 = x, x1 = x + width, y0 = y, y1 = y + height;
1074  pts[0].x = (short) x0; pts[0].y = (short) y0;
1075  pts[1].x = (short) x1; pts[1].y = (short) y0;
1076  pts[2].x = (short) x1; pts[2].y = (short) y1;
1077  pts[3].x = (short) x0; pts[3].y = (short) y1;
1078  pts[4].x = (short) x0; pts[4].y = (short) y0;
1079 
1080  XDrawLines( tkwd->display, dev->window, dev->gc, pts, 5,
1081  CoordModeOrigin );
1082  }
1083 #endif
1084  }
1085  else
1086  {
1087  plRemakePlot( pls );
1088  XFlush( tkwd->display );
1089  }
1090 }
1091 
1092 //--------------------------------------------------------------------------
1093 // ResizeCmd()
1094 //
1095 // Event handler routine for resize events.
1096 //--------------------------------------------------------------------------
1097 
1098 static void
1099 ResizeCmd( PLStream *pls, PLDisplay *pldis )
1100 {
1101  TkwDev *dev = (TkwDev *) pls->dev;
1102  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1103  int write_to_window = dev->write_to_window;
1104 
1105  dbug_enter( "ResizeCmd" );
1106 
1107  // Return if plD_init_tkw hasn't been called yet
1108 
1109  if ( dev == NULL )
1110  {
1111  plwarn( "ResizeCmd: Illegal call -- driver uninitialized" );
1112  return;
1113  }
1114 
1115  // Return if pointer to window not specified.
1116 
1117  if ( pldis == NULL )
1118  {
1119  plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" );
1120  return;
1121  }
1122 
1123  // Reset current window bounds
1124 
1125  dev->width = pldis->width;
1126  dev->height = pldis->height;
1127 
1128  dev->xscale = dev->width / (double) dev->init_width;
1129  dev->yscale = dev->height / (double) dev->init_height;
1130 
1131  dev->xscale = dev->xscale * dev->xscale_init;
1132  dev->yscale = dev->yscale * dev->yscale_init;
1133 
1134 #if PHYSICAL
1135  {
1136  float pxlx = (double) PIXELS_X / dev->width * DPMM;
1137  float pxly = (double) PIXELS_Y / dev->height * DPMM;
1138  plP_setpxl( pxlx, pxly );
1139  }
1140 #endif
1141 
1142  // Note: the following order MUST be obeyed -- if you instead redraw into
1143  // the window and then copy it to the pixmap, off-screen parts of the window
1144  // may contain garbage which is then transferred to the pixmap (and thus
1145  // will not go away after an expose).
1146  //
1147 
1148  // Resize pixmap using new dimensions
1149 
1150  if ( dev->write_to_pixmap )
1151  {
1152  dev->write_to_window = 0;
1153 #if defined ( __WIN32__ ) || defined ( MAC_TCL )
1154  Tk_FreePixmap( tkwd->display, dev->pixmap );
1155 #else
1156  // Vince's original driver code used
1157  // Tk_FreePixmap(tkwd->display, dev->pixmap);
1158  //which is defined in tk-8.3 (and 8.2?) source as
1159  //void
1160  // Tk_FreePixmap(display, pixmap)
1161  // Display *display;
1162  // Pixmap pixmap;
1163  // {
1164  // XFreePixmap(display, pixmap);
1165  // Tk_FreeXId(display, (XID) pixmap);
1166  // }
1167  // But that bombed under Linux and tcl/tk8.2 so now just call
1168  // XFreePixmap directly. (Not recommended as permanent solution
1169  // because you eventually run out of resources according to man
1170  // page if you don't call Tk_FreeXId.) Vince is still looking into
1171  // how to resolve this problem.
1172  //
1173  XFreePixmap( tkwd->display, dev->pixmap );
1174 #endif
1175  CreatePixmap( pls );
1176  }
1177 
1178  // Initialize & redraw (to pixmap, if available).
1179 
1180  plD_bop_tkwin( pls );
1181  plRemakePlot( pls );
1182  XSync( tkwd->display, 0 );
1183 
1184  // If pixmap available, fake an expose
1185 
1186  if ( dev->write_to_pixmap )
1187  {
1188  dev->write_to_window = write_to_window;
1189  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
1190  dev->width, dev->height, 0, 0 );
1191  XSync( tkwd->display, 0 );
1192  }
1193 }
1194 
1195 //--------------------------------------------------------------------------
1196 // RedrawCmd()
1197 //
1198 // Handles page redraw without resize (pixmap does not get reallocated).
1199 // Calling this makes sure all necessary housekeeping gets done.
1200 //--------------------------------------------------------------------------
1201 
1202 static void
1203 RedrawCmd( PLStream *pls )
1204 {
1205  TkwDev *dev = (TkwDev *) pls->dev;
1206  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1207  int write_to_window = dev->write_to_window;
1208 
1209  dbug_enter( "RedrawCmd" );
1210 
1211  // Return if plD_init_tkw hasn't been called yet
1212 
1213  if ( dev == NULL )
1214  {
1215  plwarn( "RedrawCmd: Illegal call -- driver uninitialized" );
1216  return;
1217  }
1218 
1219  // Initialize & redraw (to pixmap, if available).
1220 
1221  if ( dev->write_to_pixmap )
1222  dev->write_to_window = 0;
1223 
1224  plD_bop_tkwin( pls );
1225  plRemakePlot( pls );
1226  XSync( tkwd->display, 0 );
1227 
1228  dev->write_to_window = write_to_window;
1229 
1230  // If pixmap available, fake an expose
1231 
1232  if ( dev->write_to_pixmap )
1233  {
1234  XCopyArea( tkwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
1235  dev->width, dev->height, 0, 0 );
1236  XSync( tkwd->display, 0 );
1237  }
1238 }
1239 
1240 //--------------------------------------------------------------------------
1241 // CreatePixmap()
1242 //
1243 // This routine creates a pixmap, doing error trapping in case there
1244 // isn't enough memory on the server.
1245 //--------------------------------------------------------------------------
1246 
1247 static void
1248 CreatePixmap( PLStream *pls )
1249 {
1250  TkwDev *dev = (TkwDev *) pls->dev;
1251  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1252  Tk_Window tkwin = pls->plPlotterPtr->tkwin;
1253 
1254 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1255  int ( *oldErrorHandler )( Display *, XErrorEvent * );
1256  oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler );
1257  CreatePixmapStatus = Success;
1258 #endif
1259 
1260 #ifdef MAC_TCL
1261  // MAC_TCL's version of XCreatePixmap doesn't like 0 by 0 maps
1262  if ( dev->width == 0 )
1263  {
1264  dev->width = 10;
1265  }
1266  if ( dev->height == 0 )
1267  {
1268  dev->height = 10;
1269  }
1270 #endif
1271  pldebug( "CreatePixmap",
1272  "creating pixmap: width = %d, height = %d, depth = %d\n",
1273  dev->width, dev->height, tkwd->depth );
1274 //
1275 // dev->pixmap = Tk_GetPixmap(tkwd->display, dev->window,
1276 // dev->width, dev->height, tkwd->depth);
1277 //
1278 //
1279 // Vince's original driver code used Tk_Display(tkwin) for first argument,
1280 // but that bombed on an Linux tcl/tk 8.2 machine. Something was wrong
1281 // with that value. Thus, we now use tkwd->display, and that works well.
1282 // Vince is looking into why Tk_Display(tkwin) is badly defined under 8.2.
1283 // old code:
1284 //
1285 // dev->pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1286 // Tk_Width(tkwin), Tk_Height(tkwin),
1287 // DefaultDepthOfScreen(Tk_Screen(tkwin)));
1288 //
1289  dev->pixmap = Tk_GetPixmap( tkwd->display, Tk_WindowId( tkwin ),
1290  Tk_Width( tkwin ), Tk_Height( tkwin ),
1291  DefaultDepthOfScreen( Tk_Screen( tkwin ) ) );
1292  XSync( tkwd->display, 0 );
1293 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1294  if ( CreatePixmapStatus != Success )
1295  {
1296  dev->write_to_pixmap = 0;
1297  dev->write_to_window = 1;
1298  pls->db = 0;
1299  fprintf( stderr, "\n\
1300  Warning: pixmap could not be allocated (insufficient memory on server).\n\
1301  Driver will redraw the entire plot to handle expose events.\n" );
1302  }
1303 
1304  XSetErrorHandler( oldErrorHandler );
1305 #endif
1306 }
1307 
1308 //--------------------------------------------------------------------------
1309 // GetVisual()
1310 //
1311 // Get visual info. In order to safely use a visual other than that of
1312 // the parent (which hopefully is that returned by DefaultVisual), you
1313 // must first find (using XGetRGBColormaps) or create a colormap matching
1314 // this visual and then set the colormap window attribute in the
1315 // XCreateWindow attributes and valuemask arguments. I don't do this
1316 // right now, so this is turned off by default.
1317 //--------------------------------------------------------------------------
1318 
1319 static void
1320 GetVisual( PLStream *pls )
1321 {
1322  int depth;
1323  TkwDev *dev = (TkwDev *) pls->dev;
1324  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1325 
1326  dbug_enter( "GetVisual" );
1327 
1328  tkwd->visual = Tk_GetVisual( pls->plPlotterPtr->interp,
1329  pls->plPlotterPtr->tkwin,
1330  "best",
1331  &depth, NULL );
1332  tkwd->depth = (unsigned int) depth;
1333 }
1334 
1335 //--------------------------------------------------------------------------
1336 // AllocBGFG()
1337 //
1338 // Allocate background & foreground colors. If possible, I choose pixel
1339 // values such that the fg pixel is the xor of the bg pixel, to make
1340 // rubber-banding easy to see.
1341 //--------------------------------------------------------------------------
1342 
1343 static void
1344 AllocBGFG( PLStream *pls )
1345 {
1346  TkwDev *dev = (TkwDev *) pls->dev;
1347  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1348 
1349 #ifndef USE_TK
1350  int i, j, npixels;
1351  unsigned long plane_masks[1], pixels[MAX_COLORS];
1352 #endif
1353 
1354  dbug_enter( "AllocBGFG" );
1355 
1356  // If not on a color system, just return
1357 
1358  if ( !tkwd->color )
1359  return;
1360 #ifndef USE_TK
1361  // Allocate r/w color cell for background
1362 
1363  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1364  plane_masks, 0, pixels, 1 ) )
1365  {
1366  tkwd->cmap0[0].pixel = pixels[0];
1367  }
1368  else
1369  {
1370  plexit( "couldn't allocate background color cell" );
1371  }
1372 
1373  // Allocate as many colors as we can
1374 
1375  npixels = MAX_COLORS;
1376  for (;; )
1377  {
1378  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1379  plane_masks, 0, pixels, npixels ) )
1380  break;
1381  npixels--;
1382  if ( npixels == 0 )
1383  break;
1384  }
1385 
1386  // Find the color with pixel = xor of the bg color pixel.
1387  // If a match isn't found, the last pixel allocated is used.
1388 
1389  for ( i = 0; i < npixels - 1; i++ )
1390  {
1391  if ( pixels[i] == ( ~tkwd->cmap0[0].pixel & 0xFF ) )
1392  break;
1393  }
1394 
1395  // Use this color cell for our foreground color. Then free the rest.
1396 
1397  tkwd->fgcolor.pixel = pixels[i];
1398  for ( j = 0; j < npixels; j++ )
1399  {
1400  if ( j != i )
1401  XFreeColors( tkwd->display, tkwd->map, &pixels[j], 1, 0 );
1402  }
1403 #endif
1404 }
1405 
1406 //--------------------------------------------------------------------------
1407 // pltkwin_setBGFG()
1408 //
1409 // Set background & foreground colors. Foreground over background should
1410 // have high contrast.
1411 //--------------------------------------------------------------------------
1412 
1413 void
1414 pltkwin_setBGFG( PLStream *pls )
1415 {
1416  TkwDev *dev = (TkwDev *) pls->dev;
1417  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1418  PLColor fgcolor;
1419  int gslevbg, gslevfg;
1420 
1421  dbug_enter( "pltkwin_setBGFG" );
1422 
1423  //
1424  // Set background color.
1425  //
1426  // Background defaults to black on color screens, white on grayscale (many
1427  // grayscale monitors have poor contrast, and black-on-white looks better).
1428  //
1429 
1430  if ( !tkwd->color )
1431  {
1432  pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
1433  }
1434  gslevbg = (int) ( ( (long) pls->cmap0[0].r +
1435  (long) pls->cmap0[0].g +
1436  (long) pls->cmap0[0].b ) / 3 );
1437 
1438  PLColor_to_TkColor( &pls->cmap0[0], &tkwd->cmap0[0] );
1439 
1440  //
1441  // Set foreground color.
1442  //
1443  // Used for grayscale output, since otherwise the plots can become nearly
1444  // unreadable (i.e. if colors get mapped onto grayscale values). In this
1445  // case it becomes the grayscale level for all draws, and is taken to be
1446  // black if the background is light, and white if the background is dark.
1447  // Note that white/black allocations never fail.
1448  //
1449 
1450  if ( gslevbg > 0x7F )
1451  gslevfg = 0;
1452  else
1453  gslevfg = 0xFF;
1454 
1455  fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg;
1456 
1457  PLColor_to_TkColor( &fgcolor, &tkwd->fgcolor );
1458 
1459  // Now store
1460 #ifndef USE_TK
1461  if ( tkwd->color )
1462  {
1463  XStoreColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1464  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
1465  }
1466  else
1467  {
1468  XAllocColor( tkwd->display, tkwd->map, &tkwd->cmap0[0] );
1469  XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1470  }
1471 #else
1472  Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[0] );
1473  Tkw_StoreColor( pls, tkwd, &tkwd->fgcolor );
1474 #endif
1475 }
1476 
1477 //--------------------------------------------------------------------------
1478 // InitColors()
1479 //
1480 // Does all color initialization.
1481 //--------------------------------------------------------------------------
1482 
1483 static void
1484 InitColors( PLStream *pls )
1485 {
1486  TkwDev *dev = (TkwDev *) pls->dev;
1487  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1488 
1489  dbug_enter( "InitColors" );
1490 
1491  // Allocate and initialize color maps.
1492  // Defer cmap1 allocation until it's actually used
1493 
1494  if ( tkwd->color )
1495  {
1496  if ( plplot_tkwin_ccmap )
1497  {
1498  AllocCustomMap( pls );
1499  }
1500  else
1501  {
1502  AllocCmap0( pls );
1503  }
1504  }
1505 }
1506 
1507 //--------------------------------------------------------------------------
1508 // AllocCustomMap()
1509 //
1510 // Initializes custom color map and all the cruft that goes with it.
1511 //
1512 // Assuming all color X displays do 256 colors, the breakdown is as follows:
1513 //
1514 // XWM_COLORS Number of low "pixel" values to copy. These are typically
1515 // allocated first, thus are in use by the window manager. I
1516 // copy them to reduce flicker.
1517 //
1518 // CMAP0_COLORS Color map 0 entries. I allocate these both in the default
1519 // colormap and the custom colormap to reduce flicker.
1520 //
1521 // CMAP1_COLORS Color map 1 entries. There should be as many as practical
1522 // available for smooth shading. On the order of 50-100 is
1523 // pretty reasonable. You don't really need all 256,
1524 // especially if all you're going to do is to print it to
1525 // postscript (which doesn't have any intrinsic limitation on
1526 // the number of colors).
1527 //
1528 // It's important to leave some extra colors unallocated for Tk. In
1529 // particular the palette tools require a fair amount. I recommend leaving
1530 // at least 40 or so free.
1531 //--------------------------------------------------------------------------
1532 
1533 static void
1534 AllocCustomMap( PLStream *pls )
1535 {
1536  TkwDev *dev = (TkwDev *) pls->dev;
1537  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1538 
1539  XColor xwm_colors[MAX_COLORS];
1540  int i;
1541 #ifndef USE_TK
1542  int npixels;
1543  unsigned long plane_masks[1], pixels[MAX_COLORS];
1544 #endif
1545 
1546  dbug_enter( "AllocCustomMap" );
1547 
1548  // Determine current default colors
1549 
1550  for ( i = 0; i < MAX_COLORS; i++ )
1551  {
1552  xwm_colors[i].pixel = (long unsigned) i;
1553  }
1554 #ifndef MAC_TCL
1555  XQueryColors( tkwd->display, tkwd->map, xwm_colors, MAX_COLORS );
1556 #endif
1557 
1558  // Allocate cmap0 colors in the default colormap.
1559  // The custom cmap0 colors are later stored at the same pixel values.
1560  // This is a really cool trick to reduce the flicker when changing colormaps.
1561  //
1562 
1563  AllocCmap0( pls );
1564  XAllocColor( tkwd->display, tkwd->map, &tkwd->fgcolor );
1565 
1566  // Create new color map
1567 
1568  tkwd->map = XCreateColormap( tkwd->display, DefaultRootWindow( tkwd->display ),
1569  tkwd->visual, AllocNone );
1570 
1571  // Now allocate all colors so we can fill the ones we want to copy
1572 
1573 #ifndef USE_TK
1574  npixels = MAX_COLORS;
1575  for (;; )
1576  {
1577  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1578  plane_masks, 0, pixels, npixels ) )
1579  break;
1580  npixels--;
1581  if ( npixels == 0 )
1582  plexit( "couldn't allocate any colors" );
1583  }
1584 
1585  // Fill the low colors since those are in use by the window manager
1586 
1587  for ( i = 0; i < XWM_COLORS; i++ )
1588  {
1589  XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
1590  pixels[xwm_colors[i].pixel] = 0;
1591  }
1592 
1593  // Fill the ones we will use in cmap0
1594 
1595  for ( i = 0; i < tkwd->ncol0; i++ )
1596  {
1597  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
1598  pixels[tkwd->cmap0[i].pixel] = 0;
1599  }
1600 
1601  // Finally, if the colormap was saved by an external agent, see if there are
1602  // any differences from the current default map and save those! A very cool
1603  // (or sick, depending on how you look at it) trick to get over some X and
1604  // Tk limitations.
1605  //
1606 
1607  if ( sxwm_colors_set )
1608  {
1609  for ( i = 0; i < MAX_COLORS; i++ )
1610  {
1611  if ( ( xwm_colors[i].red != sxwm_colors[i].red ) ||
1612  ( xwm_colors[i].green != sxwm_colors[i].green ) ||
1613  ( xwm_colors[i].blue != sxwm_colors[i].blue ) )
1614  {
1615  if ( pixels[i] != 0 )
1616  {
1617  XStoreColor( tkwd->display, tkwd->map, &xwm_colors[i] );
1618  pixels[i] = 0;
1619  }
1620  }
1621  }
1622  }
1623 
1624  // Now free the ones we're not interested in
1625 
1626  for ( i = 0; i < npixels; i++ )
1627  {
1628  if ( pixels[i] != 0 )
1629  XFreeColors( tkwd->display, tkwd->map, &pixels[i], 1, 0 );
1630  }
1631 #endif
1632  // Allocate colors in cmap 1
1633 
1634  AllocCmap1( pls );
1635 }
1636 
1637 //--------------------------------------------------------------------------
1638 // AllocCmap0()
1639 //
1640 // Allocate & initialize cmap0 entries.
1641 //--------------------------------------------------------------------------
1642 
1643 static void
1644 AllocCmap0( PLStream *pls )
1645 {
1646  TkwDev *dev = (TkwDev *) pls->dev;
1647  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1648 
1649 #ifndef USE_TK
1650  int npixels;
1651  int i;
1652  unsigned long plane_masks[1], pixels[MAX_COLORS];
1653 #endif
1654 
1655  dbug_enter( "AllocCmap0" );
1656 
1657  // Allocate and assign colors in cmap 0
1658 
1659 #ifndef USE_TK
1660  npixels = pls->ncol0 - 1;
1661  for (;; )
1662  {
1663  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1664  plane_masks, 0, &pixels[1], npixels ) )
1665  break;
1666  npixels--;
1667  if ( npixels == 0 )
1668  plexit( "couldn't allocate any colors" );
1669  }
1670 
1671  tkwd->ncol0 = npixels + 1;
1672  for ( i = 1; i < tkwd->ncol0; i++ )
1673  {
1674  tkwd->cmap0[i].pixel = pixels[i];
1675  }
1676 #else
1677  // We use the Tk color scheme
1678  tkwd->ncol0 = pls->ncol0;
1679 #endif
1680  StoreCmap0( pls );
1681 }
1682 
1683 //--------------------------------------------------------------------------
1684 // AllocCmap1()
1685 //
1686 // Allocate & initialize cmap1 entries. If using the default color map,
1687 // must severely limit number of colors otherwise TK won't have enough.
1688 //--------------------------------------------------------------------------
1689 
1690 static void
1691 AllocCmap1( PLStream *pls )
1692 {
1693  TkwDev *dev = (TkwDev *) pls->dev;
1694  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1695 
1696  int npixels;
1697 #ifndef USE_TK
1698  int i, j;
1699  unsigned long plane_masks[1], pixels[MAX_COLORS];
1700 #endif
1701 
1702  dbug_enter( "AllocCmap1" );
1703 
1704  // Allocate colors in cmap 1
1705 
1706  npixels = MAX( 2, MIN( CMAP1_COLORS, pls->ncol1 ) );
1707 #ifndef USE_TK
1708  for (;; )
1709  {
1710  if ( XAllocColorCells( tkwd->display, tkwd->map, False,
1711  plane_masks, 0, pixels, npixels ) )
1712  break;
1713  npixels--;
1714  if ( npixels == 0 )
1715  break;
1716  }
1717 
1718  if ( npixels < 2 )
1719  {
1720  tkwd->ncol1 = -1;
1721  fprintf( stderr,
1722  "Warning: unable to allocate sufficient colors in cmap1\n" );
1723  return;
1724  }
1725  else
1726  {
1727  tkwd->ncol1 = npixels;
1728  if ( pls->verbose )
1729  fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", npixels );
1730  }
1731 
1732  // Don't assign pixels sequentially, to avoid strange problems with xor GC's
1733  // Skipping by 2 seems to do the job best
1734 
1735  for ( j = i = 0; i < tkwd->ncol1; i++ )
1736  {
1737  while ( pixels[j] == 0 )
1738  j++;
1739 
1740  tkwd->cmap1[i].pixel = pixels[j];
1741  pixels[j] = 0;
1742 
1743  j += 2;
1744  if ( j >= tkwd->ncol1 )
1745  j = 0;
1746  }
1747 #else
1748  tkwd->ncol1 = npixels;
1749 #endif
1750  StoreCmap1( pls );
1751 }
1752 
1753 //--------------------------------------------------------------------------
1754 // StoreCmap0()
1755 //
1756 // Stores cmap 0 entries in X-server colormap.
1757 //--------------------------------------------------------------------------
1758 
1759 static void
1760 StoreCmap0( PLStream *pls )
1761 {
1762  TkwDev *dev = (TkwDev *) pls->dev;
1763  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1764  int i;
1765 
1766  if ( !tkwd->color )
1767  return;
1768 
1769  for ( i = 1; i < tkwd->ncol0; i++ )
1770  {
1771  PLColor_to_TkColor( &pls->cmap0[i], &tkwd->cmap0[i] );
1772 #ifndef USE_TK
1773  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap0[i] );
1774 #else
1775  Tkw_StoreColor( pls, tkwd, &tkwd->cmap0[i] );
1776 #endif
1777  }
1778 }
1779 
1780 void CopyColour( XColor* from, XColor* to )
1781 {
1782  to->pixel = from->pixel;
1783  to->red = from->red;
1784  to->blue = from->blue;
1785  to->green = from->green;
1786  to->flags = from->flags;
1787 }
1788 
1789 //--------------------------------------------------------------------------
1790 // StoreCmap1()
1791 //
1792 // Stores cmap 1 entries in X-server colormap.
1793 //--------------------------------------------------------------------------
1794 
1795 static void
1796 StoreCmap1( PLStream *pls )
1797 {
1798  TkwDev *dev = (TkwDev *) pls->dev;
1799  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
1800 
1801  PLColor cmap1color;
1802  int i;
1803 
1804  if ( !tkwd->color )
1805  return;
1806 
1807  for ( i = 0; i < tkwd->ncol1; i++ )
1808  {
1809  plcol_interp( pls, &cmap1color, i, tkwd->ncol1 );
1810  PLColor_to_TkColor( &cmap1color, &tkwd->cmap1[i] );
1811 #ifndef USE_TK
1812  XStoreColor( tkwd->display, tkwd->map, &tkwd->cmap1[i] );
1813 #else
1814  Tkw_StoreColor( pls, tkwd, &tkwd->cmap1[i] );
1815 #endif
1816  }
1817 }
1818 
1819 void Tkw_StoreColor( PLStream* pls, TkwDisplay* tkwd, XColor* col )
1820 {
1821  XColor *xc;
1822 #ifndef USE_TK
1823  XStoreColor( tkwd->display, tkwd->map, col );
1824 #else
1825  (void) tkwd; // tkwd unused in this case
1826  // We're probably losing memory here
1827  xc = Tk_GetColorByValue( pls->plPlotterPtr->tkwin, col );
1828  CopyColour( xc, col );
1829 #endif
1830 }
1831 
1832 //--------------------------------------------------------------------------
1833 // void PLColor_to_TkColor()
1834 //
1835 // Copies the supplied PLColor to an XColor, padding with bits as necessary
1836 // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
1837 // The argument types follow the same order as in the function name.
1838 //--------------------------------------------------------------------------
1839 
1840 #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) )
1841 #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 )
1842 
1843 void
1844 PLColor_to_TkColor( PLColor *plcolor, XColor *xcolor )
1845 {
1846  xcolor->red = (short unsigned) ToXColor( plcolor->r );
1847  xcolor->green = (short unsigned) ToXColor( plcolor->g );
1848  xcolor->blue = (short unsigned) ToXColor( plcolor->b );
1849  xcolor->flags = DoRed | DoGreen | DoBlue;
1850 }
1851 
1852 //--------------------------------------------------------------------------
1853 // void PLColor_from_TkColor()
1854 //
1855 // Copies the supplied XColor to a PLColor, stripping off bits as
1856 // necessary. See the previous routine for more info.
1857 //--------------------------------------------------------------------------
1858 
1859 void
1860 PLColor_from_TkColor( PLColor *plcolor, XColor *xcolor )
1861 {
1862  plcolor->r = (unsigned char) ToPLColor( xcolor->red );
1863  plcolor->g = (unsigned char) ToPLColor( xcolor->green );
1864  plcolor->b = (unsigned char) ToPLColor( xcolor->blue );
1865 }
1866 
1867 //--------------------------------------------------------------------------
1868 // void PLColor_from_TkColor_Changed()
1869 //
1870 // Copies the supplied XColor to a PLColor, stripping off bits as
1871 // necessary. See the previous routine for more info.
1872 //
1873 // Returns 1 if the color was different from the old one.
1874 //--------------------------------------------------------------------------
1875 
1876 int
1877 PLColor_from_TkColor_Changed( PLColor *plcolor, XColor *xcolor )
1878 {
1879  int changed = 0;
1880  int color;
1881  color = ToPLColor( xcolor->red );
1882 
1883  if ( plcolor->r != color )
1884  {
1885  changed = 1;
1886  plcolor->r = (unsigned char) color;
1887  }
1888  color = ToPLColor( xcolor->green );
1889  if ( plcolor->g != color )
1890  {
1891  changed = 1;
1892  plcolor->g = (unsigned char) color;
1893  }
1894  color = ToPLColor( xcolor->blue );
1895  if ( plcolor->b != color )
1896  {
1897  changed = 1;
1898  plcolor->b = (unsigned char) color;
1899  }
1900  return changed;
1901 }
1902 
1903 //--------------------------------------------------------------------------
1904 // int pltk_AreWeGrayscale(PlPlotter *plf)
1905 //
1906 // Determines if we're using a monochrome or grayscale device.
1907 // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland.
1908 // Changed July 1996 by Vince: now uses Tk to check the enclosing PlPlotter
1909 //--------------------------------------------------------------------------
1910 
1911 static int
1912 pltk_AreWeGrayscale( PlPlotter *plf )
1913 {
1914 #if defined ( __cplusplus ) || defined ( c_plusplus )
1915 #define THING c_class
1916 #else
1917 #define THING class
1918 #endif
1919 
1920  Visual* visual;
1921  // get the window's Visual
1922  visual = Tk_Visual( plf->tkwin );
1923  if ( ( visual->THING != GrayScale ) && ( visual->THING != StaticGray ) )
1924  return ( 0 );
1925  // if we got this far, only StaticGray and GrayScale classes available
1926  return ( 1 );
1927 }
1928 
1929 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
1930 //--------------------------------------------------------------------------
1931 // CreatePixmapErrorHandler()
1932 //
1933 // Error handler used in CreatePixmap() to catch errors in allocating
1934 // storage for pixmap. This way we can nicely substitute redraws for
1935 // pixmap copies if the server has insufficient memory.
1936 //--------------------------------------------------------------------------
1937 
1938 static int
1939 CreatePixmapErrorHandler( Display *display, XErrorEvent *error )
1940 {
1941  if ( error->error_code == BadAlloc )
1942  {
1943  CreatePixmapStatus = error->error_code;
1944  }
1945  else
1946  {
1947  char buffer[256];
1948  XGetErrorText( display, error->error_code, buffer, 256 );
1949  fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer );
1950  }
1951  return 1;
1952 }
1953 #endif
1954 
1955 #else
1956 int
1958 {
1959  return 0;
1960 }
1961 
1962 #endif // PLD_tkwin
1963 
1964 void * ckcalloc( size_t nmemb, size_t size )
1965 {
1966  long *ptr;
1967  long *p;
1968  size *= nmemb;
1969  ptr = (long *) malloc( size );
1970  if ( !ptr )
1971  return ( 0 );
1972 
1973 #if !__POWERPC__
1974 
1975  for ( size = ( size / sizeof ( long ) ) + 1, p = ptr; --size; )
1976  *p++ = 0;
1977 
1978 #else
1979 
1980  for ( size = ( size / sizeof ( long ) ) + 1, p = ptr - 1; --size; )
1981  *++p = 0;
1982 
1983 #endif
1984 
1985  return ( ptr );
1986 }