PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
xwin.c
Go to the documentation of this file.
1 // PLplot X-windows device driver.
2 //
3 // Copyright (C) 2004 Maurice LeBrun
4 // Copyright (C) 2004 Joao Cardoso
5 // Copyright (C) 2004 Rafael Laboissiere
6 // Copyright (C) 2004 Andrew Ross
7 //
8 // This file is part of PLplot.
9 //
10 // PLplot is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License as published
12 // by the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // PLplot is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Library General Public License for more details.
19 //
20 // You should have received a copy of the GNU Library General Public License
21 // along with PLplot; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 //
25 #include "plDevs.h"
26 
27 #define DEBUG
28 
29 #ifdef PLD_xwin
30 #define NEED_PLDEBUG
31 #include "plplotP.h"
32 #include "plxwd.h"
33 #include "drivers.h"
34 #include "plevent.h"
35 
36 #ifdef PL_HAVE_PTHREAD
37 #include <pthread.h>
38 #include <signal.h>
39 int pthread_mutexattr_settype( pthread_mutexattr_t *attr, int kind );
40 static void events_thread( void *pls );
41 static pthread_mutex_t events_mutex;
42 static int already = 0;
43 #endif
44 
45 // Device info
46 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_xwin = "xwin:X-Window (Xlib):1:xwin:5:xw\n";
47 
48 static int synchronize = 0; // change to 1 for X synchronized operation
49  // Use "-drvopt sync" cmd line option to set.
50 
51 static int nobuffered = 0; // make it a buffered device by default
52  // use "-drvopt unbuffered" to make it unbuffered
53 
54 static int noinitcolors = 0; // Call InitColors by default
55  // use "-drvopt noinitcolors" to leave colors uninitialized
56 
57 static int defaultvisual = 1; // use "-drvopt defvis=0" to not use the default visual
58 
59 static int usepthreads = 1; // use "-drvopt usepth=0" to not use pthreads to redisplay
60 
61 //
62 // When -drvopt defvis is defined, DefaultVisual() is used to get the visual.
63 // Otherwise, the visual is obtained using XGetVisualInfo() to make a match.
64 //
65 
66 //#define HACK_STATICCOLOR
67 
68 // Number of instructions to skip between querying the X server for events
69 
70 #define MAX_INSTR 20
71 
72 // The xwin driver uses the xscale and yscale values to convert from virtual
73 // to real pixels using the current size in pixels of the display window.
74 // We define PHYSICAL here so that PLplot core knows about this rescaling
75 // and mm values are converted to virtual pixels at a ratio consistent with
76 // a constant ratio of DPMM real pixels per mm.
77 #define PHYSICAL 1
78 
79 // These need to be distinguished since the handling is slightly different.
80 
81 #define LOCATE_INVOKED_VIA_API 1
82 #define LOCATE_INVOKED_VIA_DRIVER 2
83 
84 // Set constants for dealing with colormap. In brief:
85 //
86 // --- custom colormaps ---
87 // ccmap When set, turns on custom color map
88 // CCMAP_XWM_COLORS Number of low "pixel" values to copy.
89 //
90 // --- r/w colormaps --
91 // RWMAP_CMAP1_COLORS Color map 1 entries.
92 // RWMAP_MAX_COLORS Maximum colors in RW colormaps
93 //
94 // --- r/o colormaps --
95 // ROMAP_CMAP1_COLORS Color map 1 entries.
96 // TC_CMAP1_COLORS TrueColor visual Color map 1 entries.
97 //
98 // See Init_CustomCmap() and Init_DefaultCmap() for more info.
99 // Set ccmap at your own risk -- still under development.
100 //
101 
102 // plplot_ccmap is statically defined in plxwd.h. Note that
103 // plframe.c also includes that header and uses the variable.
104 
105 #define CCMAP_XWM_COLORS 70
106 
107 #define RWMAP_CMAP1_COLORS 50
108 #define RWMAP_MAX_COLORS 256
109 
110 #define ROMAP_CMAP1_COLORS 50
111 #define TC_CMAP1_COLORS 200
112 
113 // Variables to hold RGB components of given colormap.
114 // Used in an ugly hack to get past some X11R5 and TK limitations.
115 
116 static int sxwm_colors_set;
117 static XColor sxwm_colors[RWMAP_MAX_COLORS];
118 
119 // Keep pointers to all the displays in use
120 
121 static XwDisplay *xwDisplay[PLXDISPLAYS];
122 
123 // Function prototypes
124 
125 // Driver entry and dispatch setup
126 
128 
129 void plD_init_xw( PLStream * );
130 void plD_line_xw( PLStream *, short, short, short, short );
131 void plD_polyline_xw( PLStream *, short *, short *, PLINT );
132 void plD_eop_xw( PLStream * );
133 void plD_bop_xw( PLStream * );
134 void plD_tidy_xw( PLStream * );
135 void plD_state_xw( PLStream *, PLINT );
136 void plD_esc_xw( PLStream *, PLINT, void * );
137 
138 // Initialization
139 
140 static void OpenXwin( PLStream *pls );
141 static void Init( PLStream *pls );
142 static void InitMain( PLStream *pls );
143 static void InitColors( PLStream *pls );
144 static void AllocCustomMap( PLStream *pls );
145 static void AllocCmap0( PLStream *pls );
146 static void AllocCmap1( PLStream *pls );
147 static void MapMain( PLStream *pls );
148 static void CreatePixmap( PLStream *pls );
149 static void GetVisual( PLStream *pls );
150 static void AllocBGFG( PLStream *pls );
151 static int AreWeGrayscale( Display *display );
152 
153 // Routines to poll the X-server
154 
155 static void WaitForPage( PLStream *pls );
156 static void CheckForEvents( PLStream *pls );
157 static void HandleEvents( PLStream *pls );
158 
159 // Event handlers
160 
161 static void MasterEH( PLStream *pls, XEvent *event );
162 static void ClientEH( PLStream *pls, XEvent *event );
163 static void ExposeEH( PLStream *pls, XEvent *event );
164 static void ResizeEH( PLStream *pls, XEvent *event );
165 static void MotionEH( PLStream *pls, XEvent *event );
166 static void EnterEH( PLStream *pls, XEvent *event );
167 static void LeaveEH( PLStream *pls, XEvent *event );
168 static void KeyEH( PLStream *pls, XEvent *event );
169 static void ButtonEH( PLStream *pls, XEvent *event );
170 static void LookupXKeyEvent( PLStream *pls, XEvent *event );
171 static void LookupXButtonEvent( PLStream *pls, XEvent *event );
172 
173 static void ProcessKey( PLStream *pls );
174 static void LocateKey( PLStream *pls );
175 static void ProcessButton( PLStream *pls );
176 static void LocateButton( PLStream *pls );
177 static void Locate( PLStream *pls );
178 
179 // Routines for manipulating graphic crosshairs
180 
181 static void CreateXhairs( PLStream *pls );
182 static void DestroyXhairs( PLStream *pls );
183 static void DrawXhairs( PLStream *pls, int x0, int y0 );
184 static void UpdateXhairs( PLStream *pls );
185 
186 // Escape function commands
187 
188 static void ExposeCmd( PLStream *pls, PLDisplay *ptr );
189 static void RedrawCmd( PLStream *pls );
190 static void ResizeCmd( PLStream *pls, PLDisplay *ptr );
191 static void ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr );
192 static void GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr );
193 static void FillPolygonCmd( PLStream *pls );
194 static void XorMod( PLStream *pls, PLINT *mod );
195 static void DrawImage( PLStream *pls );
196 
197 // Miscellaneous
198 
199 static void StoreCmap0( PLStream *pls );
200 static void StoreCmap1( PLStream *pls );
201 static void imageops( PLStream *pls, PLINT *ptr );
202 static void SetBGFG( PLStream *pls );
203 #ifdef DUMMY
204 static void SaveColormap( Display *display, Colormap colormap );
205 #endif
206 static void PLColor_to_XColor( PLColor *plcolor, XColor *xcolor );
207 static void PLColor_from_XColor( PLColor *plcolor, XColor *xcolor );
208 
209 static DrvOpt xwin_options[] = { { "sync", DRV_INT, &synchronize, "Synchronized X server operation (0|1)" },
210  { "nobuffered", DRV_INT, &nobuffered, "Sets unbuffered operation (0|1)" },
211  { "noinitcolors", DRV_INT, &noinitcolors, "Sets cmap0 allocation (0|1)" },
212  { "defvis", DRV_INT, &defaultvisual, "Use the Default Visual (0|1)" },
213  { "usepth", DRV_INT, &usepthreads, "Use pthreads (0|1)" },
214  { NULL, DRV_INT, NULL, NULL } };
215 
217 {
218 #ifndef ENABLE_DYNDRIVERS
219  pdt->pl_MenuStr = "X-Window (Xlib)";
220  pdt->pl_DevName = "xwin";
221 #endif
223  pdt->pl_seq = 5;
224  pdt->pl_init = (plD_init_fp) plD_init_xw;
225  pdt->pl_line = (plD_line_fp) plD_line_xw;
226  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_xw;
227  pdt->pl_eop = (plD_eop_fp) plD_eop_xw;
228  pdt->pl_bop = (plD_bop_fp) plD_bop_xw;
229  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_xw;
230  pdt->pl_state = (plD_state_fp) plD_state_xw;
231  pdt->pl_esc = (plD_esc_fp) plD_esc_xw;
232 }
233 
234 //--------------------------------------------------------------------------
235 // plD_init_xw()
236 //
237 // Initialize device.
238 // X-dependent stuff done in OpenXwin() and Init().
239 //--------------------------------------------------------------------------
240 
241 void
242 plD_init_xw( PLStream *pls )
243 {
244  XwDev *dev;
245  PLFLT 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_xw" );
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  pls->dev_fastimg = 1; // is a fast image device
258  pls->dev_xor = 1; // device support xor mode
259 
260 #ifndef PL_HAVE_PTHREAD
261  usepthreads = 0;
262 #endif
263 
264  plParseDrvOpts( xwin_options );
265 
266 #ifndef PL_HAVE_PTHREAD
267  if ( usepthreads )
268  plwarn( "You said you want pthreads, but they are not available." );
269 #endif
270 
271  if ( nobuffered )
272  pls->plbuf_write = 0; // deactivate plot buffer
273 
274 // The real meat of the initialization done here
275 
276  if ( pls->dev == NULL )
277  OpenXwin( pls );
278 
279  dev = (XwDev *) pls->dev;
280 
281  Init( pls );
282 
283 // Get ready for plotting
284 
285  dev->xlen = (short) ( xmax - xmin );
286  dev->ylen = (short) ( ymax - ymin );
287 
288  dev->xscale_init = (double) dev->init_width / (double) dev->xlen;
289  dev->yscale_init = (double) dev->init_height / (double) dev->ylen;
290 
291  dev->xscale = dev->xscale_init;
292  dev->yscale = dev->yscale_init;
293 
294 #if PHYSICAL
295  pxlx = DPMM / dev->xscale;
296  pxly = DPMM / dev->yscale;
297 #else
298  pxlx = (double) PIXELS_X / LPAGE_X;
299  pxly = (double) PIXELS_Y / LPAGE_Y;
300 #endif
301 
302  plP_setpxl( pxlx, pxly );
303  plP_setphy( xmin, xmax, ymin, ymax );
304 
305 #ifdef PL_HAVE_PTHREAD
306  if ( usepthreads )
307  {
308  pthread_mutexattr_t mutexatt;
309  pthread_attr_t pthattr;
310 
311  if ( !already )
312  {
313  pthread_mutexattr_init( &mutexatt );
314  if ( pthread_mutexattr_settype( &mutexatt, PLPLOT_MUTEX_RECURSIVE ) )
315  plexit( "xwin: pthread_mutexattr_settype() failed!\n" );
316 
317  pthread_mutex_init( &events_mutex, &mutexatt );
318  already = 1;
319  }
320  else
321  {
322  pthread_mutex_lock( &events_mutex );
323  already++;
324  pthread_mutex_unlock( &events_mutex );
325  }
326 
327  pthread_attr_init( &pthattr );
328  pthread_attr_setdetachstate( &pthattr, PTHREAD_CREATE_JOINABLE );
329 
330  if ( pthread_create( &( dev->updater ), &pthattr, ( void *( * )( void * ) ) & events_thread, (void *) pls ) )
331  {
332  pthread_mutex_lock( &events_mutex );
333  already--;
334  pthread_mutex_unlock( &events_mutex );
335 
336  if ( already == 0 )
337  {
338  pthread_mutex_destroy( &events_mutex );
339  plexit( "xwin: pthread_create() failed!\n" );
340  }
341  else
342  plwarn( "xwin: couldn't create thread for this plot window!\n" );
343  }
344  }
345 #endif
346 }
347 
348 //--------------------------------------------------------------------------
349 // plD_line_xw()
350 //
351 // Draw a line in the current color from (x1,y1) to (x2,y2).
352 //--------------------------------------------------------------------------
353 
354 void
355 plD_line_xw( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
356 {
357  XwDev *dev = (XwDev *) pls->dev;
358  XwDisplay *xwd = (XwDisplay *) dev->xwd;
359 
360  int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
361 
362  dbug_enter( "plD_line_xw" );
363 
364 #ifdef PL_HAVE_PTHREAD
365  if ( usepthreads )
366  pthread_mutex_lock( &events_mutex );
367 #endif
368 
369  CheckForEvents( pls );
370 
371  y1 = dev->ylen - y1;
372  y2 = dev->ylen - y2;
373 
374  x1 = (int) ( x1 * dev->xscale );
375  x2 = (int) ( x2 * dev->xscale );
376  y1 = (int) ( y1 * dev->yscale );
377  y2 = (int) ( y2 * dev->yscale );
378 
379  if ( dev->write_to_window )
380  XDrawLine( xwd->display, dev->window, dev->gc, x1, y1, x2, y2 );
381 
382  if ( dev->write_to_pixmap )
383  XDrawLine( xwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 );
384 
385 #ifdef PL_HAVE_PTHREAD
386  if ( usepthreads )
387  pthread_mutex_unlock( &events_mutex );
388 #endif
389 }
390 
391 //--------------------------------------------------------------------------
392 // XorMod()
393 //
394 // Enter xor mode ( mod != 0) or leave it ( mode = 0)
395 //--------------------------------------------------------------------------
396 
397 static void
398 XorMod( PLStream *pls, PLINT *mod )
399 {
400  XwDev *dev = (XwDev *) pls->dev;
401  XwDisplay *xwd = (XwDisplay *) dev->xwd;
402 
403  if ( *mod == 0 )
404  XSetFunction( xwd->display, dev->gc, GXcopy );
405  else
406  XSetFunction( xwd->display, dev->gc, GXxor );
407 }
408 
409 //--------------------------------------------------------------------------
410 // plD_polyline_xw()
411 //
412 // Draw a polyline in the current color from (x1,y1) to (x2,y2).
413 //--------------------------------------------------------------------------
414 
415 void
416 plD_polyline_xw( PLStream *pls, short *xa, short *ya, PLINT npts )
417 {
418  XwDev *dev = (XwDev *) pls->dev;
419  XwDisplay *xwd = (XwDisplay *) dev->xwd;
420 
421  PLINT i;
422  XPoint _pts[PL_MAXPOLY];
423  XPoint *pts;
424 
425  if ( npts > PL_MAXPOLY )
426  {
427  pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts );
428  }
429  else
430  {
431  pts = _pts;
432  }
433 
434  dbug_enter( "plD_polyline_xw" );
435 
436 #ifdef PL_HAVE_PTHREAD
437  if ( usepthreads )
438  pthread_mutex_lock( &events_mutex );
439 #endif
440 
441  CheckForEvents( pls );
442 
443  for ( i = 0; i < npts; i++ )
444  {
445  pts[i].x = (short) ( dev->xscale * xa[i] );
446  pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) );
447  }
448 
449  if ( dev->write_to_window )
450  XDrawLines( xwd->display, dev->window, dev->gc, pts, npts,
451  CoordModeOrigin );
452 
453  if ( dev->write_to_pixmap )
454  XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, npts,
455  CoordModeOrigin );
456 
457 #ifdef PL_HAVE_PTHREAD
458  if ( usepthreads )
459  pthread_mutex_unlock( &events_mutex );
460 #endif
461 
462  if ( npts > PL_MAXPOLY )
463  {
464  free( pts );
465  }
466 }
467 
468 //--------------------------------------------------------------------------
469 // plD_eop_xw()
470 //
471 // End of page. User must hit return (or third mouse button) to continue.
472 //--------------------------------------------------------------------------
473 
474 void
475 plD_eop_xw( PLStream *pls )
476 {
477  XwDev *dev = (XwDev *) pls->dev;
478  XwDisplay *xwd = (XwDisplay *) dev->xwd;
479 
480  dbug_enter( "plD_eop_xw" );
481 
482 #ifdef PL_HAVE_PTHREAD
483  if ( usepthreads )
484  pthread_mutex_lock( &events_mutex );
485 #endif
486 
487  XFlush( xwd->display );
488  if ( pls->db )
489  ExposeCmd( pls, NULL );
490 
491  if ( dev->is_main && !pls->nopause )
492  WaitForPage( pls );
493 
494 #ifdef PL_HAVE_PTHREAD
495  if ( usepthreads )
496  pthread_mutex_unlock( &events_mutex );
497 #endif
498 }
499 
500 //--------------------------------------------------------------------------
501 // plD_bop_xw()
502 //
503 // Set up for the next page.
504 //--------------------------------------------------------------------------
505 
506 void
507 plD_bop_xw( PLStream *pls )
508 {
509  XwDev *dev = (XwDev *) pls->dev;
510  XwDisplay *xwd = (XwDisplay *) dev->xwd;
511 
512  dbug_enter( "plD_bop_xw" );
513 
514 #ifdef PL_HAVE_PTHREAD
515  if ( usepthreads )
516  pthread_mutex_lock( &events_mutex );
517 #endif
518 
519  dev->bgcolor = xwd->cmap0[0];
520 
521  if ( dev->write_to_window )
522  {
523  XSetWindowBackground( xwd->display, dev->window, dev->bgcolor.pixel );
524  XSetBackground( xwd->display, dev->gc, dev->bgcolor.pixel );
525  XClearWindow( xwd->display, dev->window );
526  }
527  if ( dev->write_to_pixmap )
528  {
529  XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
530  XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
531  dev->width, dev->height );
532  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
533  }
534  XSync( xwd->display, 0 );
535  pls->page++;
536 
537 #ifdef PL_HAVE_PTHREAD
538  if ( usepthreads )
539  pthread_mutex_unlock( &events_mutex );
540 #endif
541 }
542 
543 //--------------------------------------------------------------------------
544 // plD_tidy_xw()
545 //
546 // Close graphics file
547 //--------------------------------------------------------------------------
548 
549 void
550 plD_tidy_xw( PLStream *pls )
551 {
552  XwDev *dev = (XwDev *) pls->dev;
553  XwDisplay *xwd = (XwDisplay *) dev->xwd;
554 
555  dbug_enter( "plD_tidy_xw" );
556 
557 #ifdef PL_HAVE_PTHREAD
558  if ( usepthreads )
559  {
560  pthread_mutex_lock( &events_mutex );
561  if ( pthread_cancel( dev->updater ) == 0 )
562  pthread_join( dev->updater, NULL );
563 
564  pthread_mutex_unlock( &events_mutex );
565  if ( --already == 0 )
566  pthread_mutex_destroy( &events_mutex );
567  }
568 #endif
569 
570  if ( dev->is_main )
571  {
572  XDestroyWindow( xwd->display, dev->window );
573  if ( dev->write_to_pixmap )
574  XFreePixmap( xwd->display, dev->pixmap );
575  XFlush( xwd->display );
576  }
577 
578  xwd->nstreams--;
579  if ( xwd->nstreams == 0 )
580  {
581  int ixwd = xwd->ixwd;
582  XFreeGC( xwd->display, dev->gc );
583  XFreeGC( xwd->display, xwd->gcXor );
584  XCloseDisplay( xwd->display );
585  free_mem( xwd->cmap0 );
586  free_mem( xwd->cmap1 );
587  free_mem( xwDisplay[ixwd] );
588  }
589  // ANR: if we set this here the tmp file will not be closed
590  // See also comment in tkwin.c
591  //pls->plbuf_write = 0;
592 }
593 
594 //--------------------------------------------------------------------------
595 // plD_state_xw()
596 //
597 // Handle change in PLStream state (color, pen width, fill attribute, etc).
598 //--------------------------------------------------------------------------
599 
600 void
601 plD_state_xw( PLStream *pls, PLINT op )
602 {
603  XwDev *dev = (XwDev *) pls->dev;
604  XwDisplay *xwd = (XwDisplay *) dev->xwd;
605 
606  dbug_enter( "plD_state_xw" );
607 
608 #ifdef PL_HAVE_PTHREAD
609  if ( usepthreads )
610  pthread_mutex_lock( &events_mutex );
611 #endif
612 
613  CheckForEvents( pls );
614 
615  switch ( op )
616  {
617  case PLSTATE_WIDTH:
618  XSetLineAttributes( xwd->display, dev->gc, (unsigned int) pls->width,
619  LineSolid, CapRound, JoinMiter );
620  break;
621 
622  case PLSTATE_COLOR0: {
623  int icol0 = pls->icol0;
624  if ( xwd->color )
625  {
626  if ( icol0 == PL_RGB_COLOR )
627  {
628  PLColor_to_XColor( &pls->curcolor, &dev->curcolor );
629  if ( !XAllocColor( xwd->display, xwd->map, &dev->curcolor ) )
630  {
631  fprintf( stderr, "Warning: could not allocate color\n" );
632  dev->curcolor.pixel = xwd->fgcolor.pixel;
633  }
634  }
635  else
636  {
637  dev->curcolor = xwd->cmap0[icol0];
638  }
639  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
640  }
641  else
642  {
643  dev->curcolor = xwd->fgcolor;
644  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
645  }
646  break;
647  }
648 
649  case PLSTATE_COLOR1: {
650  int icol1;
651 
652  if ( xwd->ncol1 == 0 )
653  AllocCmap1( pls );
654 
655  if ( xwd->ncol1 < 2 )
656  break;
657 
658  icol1 = ( pls->icol1 * ( xwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 );
659  if ( xwd->color )
660  dev->curcolor = xwd->cmap1[icol1];
661  else
662  dev->curcolor = xwd->fgcolor;
663 
664  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
665  break;
666  }
667 
668  case PLSTATE_CMAP0:
669  SetBGFG( pls );
670  // If ncol0 has changed, need to reallocate
671  if ( pls->ncol0 != xwd->ncol0 )
672  AllocCmap0( pls );
673  StoreCmap0( pls );
674  break;
675 
676  case PLSTATE_CMAP1:
677  StoreCmap1( pls );
678  break;
679  }
680 
681 #ifdef PL_HAVE_PTHREAD
682  if ( usepthreads )
683  pthread_mutex_unlock( &events_mutex );
684 #endif
685 }
686 
687 //--------------------------------------------------------------------------
688 // plD_esc_xw()
689 //
690 // Escape function.
691 //
692 // Functions:
693 //
694 // PLESC_EH Handle pending events
695 // PLESC_EXPOSE Force an expose
696 // PLESC_FILL Fill polygon
697 // PLESC_FLUSH Flush X event buffer
698 // PLESC_GETC Get coordinates upon mouse click
699 // PLESC_REDRAW Force a redraw
700 // PLESC_RESIZE Force a resize
701 // PLESC_XORMOD set/reset xor mode
702 // PLESC_IMAGE draw the image in a fast way
703 // PLESC_IMAGEOPS: image related operations:
704 // ZEROW2D disable writing to display
705 // ZEROW2B disable writing to buffer
706 // ONEW2D enable writing to display
707 // ONEW2B enable writing to buffer
708 // PLESC_PL2DEVCOL convert PLColor to device color (XColor)
709 // PLESC_DEV2PLCOL convert device color (XColor) to PLColor
710 // PLESC_SETBGFG set BG, FG colors
711 // PLESC_DEVINIT alternate device initialization
712 //
713 // Note the [GET|SET]DEVCOL functions go through the intermediary stream
714 // variable tmpcolor to keep the syntax simple.
715 //--------------------------------------------------------------------------
716 
717 void
718 plD_esc_xw( PLStream *pls, PLINT op, void *ptr )
719 {
720  dbug_enter( "plD_esc_xw" );
721 
722 #ifdef PL_HAVE_PTHREAD
723  if ( usepthreads )
724  pthread_mutex_lock( &events_mutex );
725 #endif
726 
727  switch ( op )
728  {
729  case PLESC_EH:
730  HandleEvents( pls );
731  break;
732 
733  case PLESC_EXPOSE:
734  ExposeCmd( pls, (PLDisplay *) ptr );
735  break;
736 
737  case PLESC_FILL:
738  FillPolygonCmd( pls );
739  break;
740 
741  case PLESC_FLUSH: {
742  XwDev *dev = (XwDev *) pls->dev;
743  XwDisplay *xwd = (XwDisplay *) dev->xwd;
744  HandleEvents( pls );
745  XFlush( xwd->display );
746  break;
747  }
748  case PLESC_GETC:
749  GetCursorCmd( pls, (PLGraphicsIn *) ptr );
750  break;
751 
752  case PLESC_REDRAW:
753  RedrawCmd( pls );
754  break;
755 
756  case PLESC_RESIZE:
757  ResizeCmd( pls, (PLDisplay *) ptr );
758  break;
759 
760  case PLESC_XORMOD:
761  XorMod( pls, (PLINT *) ptr );
762  break;
763 
765  ConfigBufferingCmd( pls, (PLBufferingCB *) ptr );
766  break;
767 
768  case PLESC_IMAGE:
769  DrawImage( pls );
770  break;
771 
772  case PLESC_IMAGEOPS:
773  imageops( pls, (PLINT *) ptr );
774  break;
775 
776  case PLESC_PL2DEVCOL:
777  PLColor_to_XColor( &pls->tmpcolor, (XColor *) ptr );
778  break;
779 
780  case PLESC_DEV2PLCOL:
781  PLColor_from_XColor( &pls->tmpcolor, (XColor *) ptr );
782  break;
783 
784  case PLESC_SETBGFG:
785  SetBGFG( pls );
786  break;
787 
788  case PLESC_DEVINIT:
789  OpenXwin( pls );
790  break;
791  }
792 
793 #ifdef PL_HAVE_PTHREAD
794  if ( usepthreads )
795  pthread_mutex_unlock( &events_mutex );
796 #endif
797 }
798 
799 //--------------------------------------------------------------------------
800 // GetCursorCmd()
801 //
802 // Waits for a graphics input event and returns coordinates.
803 //--------------------------------------------------------------------------
804 
805 static void
806 GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr )
807 {
808  XwDev *dev = (XwDev *) pls->dev;
809  XwDisplay *xwd = (XwDisplay *) dev->xwd;
810  XEvent event;
811  PLGraphicsIn *gin = &( dev->gin );
812 
813 // Initialize
814 
815  plGinInit( gin );
817  CreateXhairs( pls );
818 
819 // Run event loop until a point is selected
820 
821  while ( gin->pX < 0 && dev->locate_mode )
822  {
823  // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
824  XNextEvent( xwd->display, &event );
825  MasterEH( pls, &event );
826  }
827  *ptr = *gin;
828  if ( dev->locate_mode )
829  {
830  dev->locate_mode = 0;
831  DestroyXhairs( pls );
832  }
833 }
834 
835 //--------------------------------------------------------------------------
836 // FillPolygonCmd()
837 //
838 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
839 // Only solid color fill supported.
840 //--------------------------------------------------------------------------
841 
842 static void
843 FillPolygonCmd( PLStream *pls )
844 {
845  XwDev *dev = (XwDev *) pls->dev;
846  XwDisplay *xwd = (XwDisplay *) dev->xwd;
847  XPoint _pts[PL_MAXPOLY];
848  XPoint *pts;
849  int i;
850 
851  if ( pls->dev_npts > PL_MAXPOLY )
852  {
853  pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) );
854  }
855  else
856  {
857  pts = _pts;
858  }
859 
860  CheckForEvents( pls );
861 
862  for ( i = 0; i < pls->dev_npts; i++ )
863  {
864  pts[i].x = (short) ( dev->xscale * pls->dev_x[i] );
865  pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) );
866  }
867 
868 // Fill polygons
869 
870  if ( dev->write_to_window )
871  XFillPolygon( xwd->display, dev->window, dev->gc,
872  pts, pls->dev_npts, Complex, CoordModeOrigin );
873 
874  if ( dev->write_to_pixmap )
875  XFillPolygon( xwd->display, dev->pixmap, dev->gc,
876  pts, pls->dev_npts, Complex, CoordModeOrigin );
877 
878 // If in debug mode, draw outline of boxes being filled
879 
880 #ifdef DEBUG
881  if ( pls->debug )
882  {
883  XSetForeground( xwd->display, dev->gc, xwd->fgcolor.pixel );
884  if ( dev->write_to_window )
885  XDrawLines( xwd->display, dev->window, dev->gc, pts, pls->dev_npts,
886  CoordModeOrigin );
887 
888  if ( dev->write_to_pixmap )
889  XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts,
890  CoordModeOrigin );
891 
892  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
893  }
894 #endif
895 
896  if ( pls->dev_npts > PL_MAXPOLY )
897  {
898  free( pts );
899  }
900 }
901 
902 //--------------------------------------------------------------------------
903 // OpenXwin()
904 //
905 // Performs basic driver initialization, without actually opening or
906 // modifying a window. May be called by the outside world before plinit
907 // in case the caller needs early access to the driver internals (not
908 // very common -- currently only used by the plframe widget).
909 //--------------------------------------------------------------------------
910 
911 static void
912 OpenXwin( PLStream *pls )
913 {
914  XwDev *dev;
915  XwDisplay *xwd;
916  int i;
917 
918  dbug_enter( "OpenXwin" );
919 
920 // Allocate and initialize device-specific data
921 
922  if ( pls->dev != NULL )
923  plwarn( "OpenXwin: device pointer is already set" );
924 
925  pls->dev = calloc( 1, (size_t) sizeof ( XwDev ) );
926  if ( pls->dev == NULL )
927  plexit( "plD_init_xw: Out of memory." );
928 
929  dev = (XwDev *) pls->dev;
930 
931 // Variables used in querying the X server for events
932 
933  dev->instr = 0;
934  dev->max_instr = MAX_INSTR;
935 
936 // See if display matches any already in use, and if so use that
937 
938  dev->xwd = NULL;
939  for ( i = 0; i < PLXDISPLAYS; i++ )
940  {
941  if ( xwDisplay[i] == NULL )
942  {
943  continue;
944  }
945  else if ( pls->FileName == NULL && xwDisplay[i]->displayName == NULL )
946  {
947  dev->xwd = xwDisplay[i];
948  break;
949  }
950  else if ( pls->FileName == NULL || xwDisplay[i]->displayName == NULL )
951  {
952  continue;
953  }
954  else if ( strcmp( xwDisplay[i]->displayName, pls->FileName ) == 0 )
955  {
956  dev->xwd = xwDisplay[i];
957  break;
958  }
959  }
960 
961 // If no display matched, create a new one
962 
963  if ( dev->xwd == NULL )
964  {
965  dev->xwd = (XwDisplay *) calloc( 1, (size_t) sizeof ( XwDisplay ) );
966  if ( dev->xwd == NULL )
967  plexit( "Init: Out of memory." );
968 
969  for ( i = 0; i < PLXDISPLAYS; i++ )
970  {
971  if ( xwDisplay[i] == NULL )
972  break;
973  }
974  if ( i == PLXDISPLAYS )
975  plexit( "Init: Out of xwDisplay's." );
976 
977  xwDisplay[i] = xwd = (XwDisplay *) dev->xwd;
978  xwd->nstreams = 1;
979 
980 // Open display
981 #ifdef PL_HAVE_PTHREAD
982  if ( usepthreads )
983  if ( !XInitThreads() )
984  plexit( "xwin: XInitThreads() not successful." );
985 #endif
986  xwd->display = XOpenDisplay( pls->FileName );
987  if ( xwd->display == NULL )
988  {
989  plexit( "Can't open display" );
990  }
991  xwd->displayName = pls->FileName;
992  xwd->screen = DefaultScreen( xwd->display );
993  if ( synchronize )
994  XSynchronize( xwd->display, 1 );
995 
996  // Get colormap and visual
997  xwd->map = DefaultColormap( xwd->display, xwd->screen );
998  GetVisual( pls );
999 
1000  //
1001  // Figure out if we have a color display or not.
1002  // Default is color IF the user hasn't specified and IF the output device
1003  // is not grayscale.
1004  //
1005  if ( pls->colorset )
1006  xwd->color = pls->color;
1007  else
1008  {
1009  pls->color = 1;
1010  xwd->color = !AreWeGrayscale( xwd->display );
1011  }
1012 
1013  // Allocate space for colors
1014  // Note cmap1 allocation is deferred
1015  xwd->ncol0_alloc = pls->ncol0;
1016  xwd->cmap0 = (XColor *) calloc( (size_t) ( pls->ncol0 ), sizeof ( XColor ) );
1017  if ( xwd->cmap0 == 0 )
1018  plexit( "couldn't allocate space for cmap0 colors" );
1019 
1020  // Allocate & set background and foreground colors
1021  AllocBGFG( pls );
1022  SetBGFG( pls );
1023  }
1024 
1025 // Display matched, so use existing display data
1026 
1027  else
1028  {
1029  xwd = (XwDisplay *) dev->xwd;
1030  xwd->nstreams++;
1031  }
1032  xwd->ixwd = i;
1033 }
1034 
1035 //--------------------------------------------------------------------------
1036 // Init()
1037 //
1038 // Xlib initialization routine.
1039 //
1040 // Controlling routine for X window creation and/or initialization.
1041 // The user may customize the window in the following ways:
1042 //
1043 // display: pls->OutFile (use plsfnam() or -display option)
1044 // size: pls->xlength, pls->ylength (use plspage() or -geo option)
1045 // bg color: pls->cmap0[0] (use plscolbg() or -bg option)
1046 //--------------------------------------------------------------------------
1047 
1048 static void
1049 Init( PLStream *pls )
1050 {
1051  XwDev *dev = (XwDev *) pls->dev;
1052  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1053 
1054  Window root;
1055  int x, y;
1056 
1057  dbug_enter( "Init" );
1058 
1059 // If not plotting into a child window, need to create main window now
1060 
1061  if ( pls->window_id == 0 )
1062  {
1063  dev->is_main = TRUE;
1064  InitMain( pls );
1065  }
1066  else
1067  {
1068  dev->is_main = FALSE;
1069  dev->window = (Window) pls->window_id;
1070  }
1071 
1072 // Initialize colors
1073 
1074  if ( noinitcolors == 0 )
1075  InitColors( pls );
1076  XSetWindowColormap( xwd->display, dev->window, xwd->map );
1077 
1078 // Set up GC for ordinary draws
1079 
1080  if ( !dev->gc )
1081  dev->gc = XCreateGC( xwd->display, dev->window, 0, 0 );
1082 
1083 // Set up GC for rubber-band draws
1084 
1085  if ( !xwd->gcXor )
1086  {
1087  XGCValues gcValues;
1088  unsigned long mask;
1089 
1090  gcValues.background = xwd->cmap0[0].pixel;
1091  gcValues.foreground = 0xFF;
1092  gcValues.function = GXxor;
1093  mask = GCForeground | GCBackground | GCFunction;
1094 
1095  xwd->gcXor = XCreateGC( xwd->display, dev->window, mask, &gcValues );
1096  }
1097 
1098 // Get initial drawing area dimensions
1099 
1100  (void) XGetGeometry( xwd->display, dev->window, &root, &x, &y,
1101  &dev->width, &dev->height, &dev->border, &xwd->depth );
1102 
1103  dev->init_width = (long) dev->width;
1104  dev->init_height = (long) dev->height;
1105 
1106 // Set up flags that determine what we are writing to
1107 // If nopixmap is set, ignore db
1108 
1109  if ( pls->nopixmap )
1110  {
1111  dev->write_to_pixmap = 0;
1112  pls->db = 0;
1113  }
1114  else
1115  {
1116  dev->write_to_pixmap = 1;
1117  }
1118  dev->write_to_window = !pls->db;
1119 
1120 // Create pixmap for holding plot image (for expose events).
1121 
1122  if ( dev->write_to_pixmap )
1123  CreatePixmap( pls );
1124 
1125 // Set drawing color
1126 
1127  plD_state_xw( pls, PLSTATE_COLOR0 );
1128 
1129  XSetWindowBackground( xwd->display, dev->window, xwd->cmap0[0].pixel );
1130  XSetBackground( xwd->display, dev->gc, xwd->cmap0[0].pixel );
1131 
1132 // Set fill rule.
1133  if ( pls->dev_eofill )
1134  XSetFillRule( xwd->display, dev->gc, EvenOddRule );
1135  else
1136  XSetFillRule( xwd->display, dev->gc, WindingRule );
1137 
1138 // If main window, need to map it and wait for exposure
1139 
1140  if ( dev->is_main )
1141  MapMain( pls );
1142 }
1143 
1144 //--------------------------------------------------------------------------
1145 // InitMain()
1146 //
1147 // Create main window using standard Xlib calls.
1148 //--------------------------------------------------------------------------
1149 
1150 static void
1151 InitMain( PLStream *pls )
1152 {
1153  XwDev *dev = (XwDev *) pls->dev;
1154  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1155 
1156  Window root;
1157  XSizeHints hint;
1158  int x, y;
1159  U_INT width, height, border, depth;
1160 
1161  dbug_enter( "InitMain" );
1162 
1163 // Get root window geometry
1164 
1165  (void) XGetGeometry( xwd->display, DefaultRootWindow( xwd->display ),
1166  &root, &x, &y, &width, &height, &border, &depth );
1167 
1168 // Set window size
1169 // Need to be careful to set XSizeHints flags correctly
1170 
1171  hint.flags = 0;
1172  if ( pls->xlength == 0 && pls->ylength == 0 )
1173  hint.flags |= PSize;
1174  else
1175  hint.flags |= USSize;
1176 
1177  if ( pls->xlength == 0 )
1178  pls->xlength = (PLINT) ( width * 0.75 );
1179  if ( pls->ylength == 0 )
1180  pls->ylength = (PLINT) ( height * 0.75 );
1181 
1182  if ( pls->xlength > (short) width )
1183  pls->xlength = (PLINT) ( width - dev->border * 2 );
1184  if ( pls->ylength > (short) height )
1185  pls->ylength = (PLINT) ( height - dev->border * 2 );
1186 
1187  hint.width = (int) pls->xlength;
1188  hint.height = (int) pls->ylength;
1189  dev->border = 5;
1190 
1191 // Set window position if specified by the user.
1192 // Otherwise leave up to the window manager
1193 
1194  if ( pls->xoffset != 0 || pls->yoffset != 0 )
1195  {
1196  hint.flags |= USPosition;
1197  hint.x = (int) pls->xoffset;
1198  hint.y = (int) pls->yoffset;
1199  }
1200  else
1201  {
1202  hint.x = 0;
1203  hint.y = 0;
1204  }
1205 
1206 // Window creation
1207 
1208  dev->window =
1209  XCreateWindow( xwd->display,
1210  DefaultRootWindow( xwd->display ),
1211  hint.x, hint.y, (unsigned int) hint.width, (unsigned int) hint.height,
1212  dev->border, (int) xwd->depth,
1213  InputOutput, xwd->visual,
1214  0, NULL );
1215 
1216  XSetStandardProperties( xwd->display, dev->window, pls->plwindow, pls->plwindow,
1217  None, 0, 0, &hint );
1218 }
1219 
1220 //--------------------------------------------------------------------------
1221 // MapMain()
1222 //
1223 // Sets up event handlers, maps main window and waits for exposure.
1224 //--------------------------------------------------------------------------
1225 
1226 static void
1227 MapMain( PLStream *pls )
1228 {
1229  XwDev *dev = (XwDev *) pls->dev;
1230  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1231  XEvent event;
1232 
1233  dbug_enter( "MapMain" );
1234 
1235 // Input event selection
1236 
1237  dev->event_mask =
1238  ButtonPressMask |
1239  KeyPressMask |
1240  ExposureMask |
1241  ButtonMotionMask | // drag
1242  StructureNotifyMask;
1243 
1244  XSelectInput( xwd->display, dev->window, dev->event_mask );
1245 
1246 // Window mapping
1247 
1248  XMapRaised( xwd->display, dev->window );
1249 
1250  Atom wmDelete = XInternAtom( xwd->display, "WM_DELETE_WINDOW", False );
1251  XSetWMProtocols( xwd->display, dev->window, &wmDelete, 1 );
1252 
1253 // Wait for exposure
1254 // Remove extraneous expose events from the event queue
1255 
1256  for (;; )
1257  {
1258  XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
1259  if ( event.type == Expose )
1260  {
1261  while ( XCheckWindowEvent( xwd->display, dev->window,
1262  ExposureMask, &event ) )
1263  ;
1264  break;
1265  }
1266  }
1267 }
1268 
1269 //--------------------------------------------------------------------------
1270 // WaitForPage()
1271 //
1272 // This routine waits for the user to advance the plot, while handling
1273 // all other events.
1274 //--------------------------------------------------------------------------
1275 
1276 static void
1277 WaitForPage( PLStream *pls )
1278 {
1279  XwDev *dev = (XwDev *) pls->dev;
1280  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1281  XEvent event;
1282 
1283  dbug_enter( "WaitForPage" );
1284 
1285  while ( !dev->exit_eventloop )
1286  {
1287  // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
1288  XNextEvent( xwd->display, &event );
1289  MasterEH( pls, &event );
1290  }
1291  dev->exit_eventloop = FALSE;
1292 }
1293 
1294 //--------------------------------------------------------------------------
1295 // events_thread()
1296 //
1297 // This function is being running continously by a thread and is
1298 // responsible for dealing with expose and resize X events, in
1299 // the case that the main program is too busy to honor them.
1300 //
1301 // Dealing with other X events is possible, but not desirable,
1302 // e.g. treating the "Q" or right-mouse-button would terminate
1303 // the thread (if this is desirable, the thread should kill the
1304 // main program -- not thread aware -- and kill itself afterward).
1305 //
1306 // This works pretty well, but the main program *must* be linked
1307 // with the pthread library, although not being thread aware.
1308 // This happens automatically when linking against libplplot.so,
1309 // but when building modules for extending some language such as
1310 // Python or Octave, the language providing binary itself must be
1311 // relinked with -lpthread.
1312 //
1313 //--------------------------------------------------------------------------
1314 
1315 #ifdef PL_HAVE_PTHREAD
1316 static void
1317 events_thread( void *pls )
1318 {
1319  if ( usepthreads )
1320  {
1321  PLStream *lpls = (PLStream *) pls;
1322  XwDev *dev = (XwDev *) lpls->dev;
1323  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1324  PLStream *oplsc;
1325  struct timespec delay;
1326  XEvent event;
1327  long event_mask;
1328  sigset_t set;
1329 
1330  //
1331  // only treats exposures and resizes, but remove usual events from queue,
1332  // as it can be disturbing to not have them acknowledged in real time,
1333  // because the program is busy, and suddenly all being acknowledged.
1334  // Also, the locator ("L" key) is sluggish if driven by the thread.
1335  //
1336  // But this approach is a problem when the thread removes events
1337  // from the queue while the program is responsible! The user hits 'L'
1338  // and nothing happens, as the thread removes it.
1339  //
1340  // Perhaps the "Q" key should have a different treatment, quiting the
1341  // program anyway?
1342  //
1343  // Changed: does not remove non treated events from the queue
1344  //
1345 
1346  event_mask = ExposureMask | StructureNotifyMask;
1347 
1348  // block all signal for this thread
1349  sigemptyset( &set );
1350  // sigfillset(&set); can't be all signals, decide latter
1351  sigaddset( &set, SIGINT );
1352 
1353  sigprocmask( SIG_BLOCK, &set, NULL );
1354 
1355  pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
1356  pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
1357 
1358  delay.tv_sec = 0;
1359  delay.tv_nsec = 10000000; // this thread runs 10 times a second. (1/10 ms)
1360 
1361  while ( 1 )
1362  {
1363  pthread_mutex_lock( &events_mutex );
1364 
1365  if ( dev->is_main && !lpls->plbuf_read &&
1366  ++dev->instr % dev->max_instr == 0 )
1367  {
1368  dev->instr = 0;
1369  while ( XCheckWindowEvent( xwd->display, dev->window, event_mask, &event ) )
1370  {
1371  // As ResizeEH() ends up calling plRemakePlot(), that
1372  // indirectly uses the current stream, one needs to
1373  // temporarily set plplot current stream to this thread's
1374  // stream
1375 
1376  oplsc = plsc;
1377  plsc = lpls;
1378  switch ( event.type )
1379  {
1380  case Expose:
1381  ExposeEH( lpls, &event );
1382  break;
1383  case ConfigureNotify:
1384  ResizeEH( lpls, &event );
1385  break;
1386  }
1387  plsc = oplsc;
1388  }
1389  }
1390 
1391  pthread_mutex_unlock( &events_mutex );
1392  nanosleep( &delay, NULL ); // 10ms in linux
1393  /* pthread_yield(NULL); */ /* this puts too much load on the CPU */
1394  }
1395  }
1396 }
1397 #endif
1398 
1399 //--------------------------------------------------------------------------
1400 // CheckForEvents()
1401 //
1402 // A front-end to HandleEvents(), which is only called if certain conditions
1403 // are satisfied:
1404 //
1405 // - must be the creator of the main window (i.e. PLplot is handling the
1406 // X event loop by polling).
1407 // - must not be in the middle of a plot redisplay (else the order of event
1408 // handling can become circuitous).
1409 // - only query X for events and process them every dev->max_instr times
1410 // this function is called (good for performance since querying X is a
1411 // nontrivial performance hit).
1412 //--------------------------------------------------------------------------
1413 
1414 static void
1415 CheckForEvents( PLStream *pls )
1416 {
1417  XwDev *dev = (XwDev *) pls->dev;
1418 
1419  if ( dev->is_main &&
1420  !pls->plbuf_read &&
1421  ++dev->instr % dev->max_instr == 0 )
1422  {
1423  dev->instr = 0;
1424  HandleEvents( pls );
1425  }
1426 }
1427 
1428 //--------------------------------------------------------------------------
1429 // HandleEvents()
1430 //
1431 // Just a front-end to MasterEH(), for use when not actually waiting for an
1432 // event but only checking the event queue.
1433 //--------------------------------------------------------------------------
1434 
1435 static void
1436 HandleEvents( PLStream *pls )
1437 {
1438  XwDev *dev = (XwDev *) pls->dev;
1439  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1440  XEvent event;
1441 
1442  while ( XCheckTypedWindowEvent( xwd->display, dev->window,
1443  ClientMessage, &event ) ||
1444  XCheckWindowEvent( xwd->display, dev->window,
1445  dev->event_mask, &event ) )
1446  MasterEH( pls, &event );
1447 }
1448 
1449 //--------------------------------------------------------------------------
1450 // MasterEH()
1451 //
1452 // Master X event handler routine.
1453 // Redirects control to routines to handle:
1454 // - keyboard events
1455 // - mouse events
1456 // - expose events
1457 // - resize events
1458 //
1459 // By supplying a master event handler, the user can take over all event
1460 // processing. If events other than those trapped by PLplot need handling,
1461 // just call XSelectInput with the appropriate flags. The default PLplot
1462 // event handling can be modified arbitrarily by changing the event struct.
1463 //--------------------------------------------------------------------------
1464 
1465 static void
1466 MasterEH( PLStream *pls, XEvent *event )
1467 {
1468  XwDev *dev = (XwDev *) pls->dev;
1469 
1470  if ( dev->MasterEH != NULL )
1471  ( *dev->MasterEH )( pls, event );
1472 
1473  switch ( event->type )
1474  {
1475  case KeyPress:
1476  KeyEH( pls, event );
1477  break;
1478 
1479  case ButtonPress:
1480  ButtonEH( pls, event );
1481  break;
1482 
1483  case Expose:
1484  ExposeEH( pls, event );
1485  break;
1486 
1487  case ConfigureNotify:
1488  ResizeEH( pls, event );
1489  break;
1490 
1491  case MotionNotify:
1492  if ( event->xmotion.state )
1493  ButtonEH( pls, event ); // drag
1494  MotionEH( pls, event );
1495  break;
1496 
1497  case EnterNotify:
1498  EnterEH( pls, event );
1499  break;
1500 
1501  case LeaveNotify:
1502  LeaveEH( pls, event );
1503  break;
1504 
1505  case ClientMessage:
1506  ClientEH( pls, event );
1507  break;
1508  }
1509 }
1510 
1511 //--------------------------------------------------------------------------
1512 // ClientEH()
1513 //
1514 // Event handler routine for client message events (WM_DELETE_WINDOW)
1515 //--------------------------------------------------------------------------
1516 
1517 static void
1518 ClientEH( PLStream *pls, XEvent *event )
1519 {
1520  XwDev *dev = (XwDev *) pls->dev;
1521  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1522 
1523  if ( (Atom) event->xclient.data.l[0] == XInternAtom( xwd->display, "WM_DELETE_WINDOW", False ) )
1524  {
1525  pls->nopause = TRUE;
1526  pls->stream_closed = TRUE;
1527  dev->exit_eventloop = TRUE;
1528  // plexit( "" );
1529  }
1530 }
1531 
1532 
1533 //--------------------------------------------------------------------------
1534 // KeyEH()
1535 //
1536 // Event handler routine for keyboard events.
1537 //--------------------------------------------------------------------------
1538 
1539 static void
1540 KeyEH( PLStream *pls, XEvent *event )
1541 {
1542  XwDev *dev = (XwDev *) pls->dev;
1543 
1544  dbug_enter( "KeyEH" );
1545 
1546  LookupXKeyEvent( pls, event );
1547  if ( dev->locate_mode )
1548  LocateKey( pls );
1549  else
1550  ProcessKey( pls );
1551 }
1552 
1553 //--------------------------------------------------------------------------
1554 // ButtonEH()
1555 //
1556 // Event handler routine for ButtonPress events.
1557 //--------------------------------------------------------------------------
1558 
1559 static void
1560 ButtonEH( PLStream *pls, XEvent *event )
1561 {
1562  XwDev *dev = (XwDev *) pls->dev;
1563 
1564  dbug_enter( "ButtonEH" );
1565 
1566  LookupXButtonEvent( pls, event );
1567  if ( dev->locate_mode )
1568  LocateButton( pls );
1569  else
1570  ProcessButton( pls );
1571 }
1572 
1573 //--------------------------------------------------------------------------
1574 // LookupXKeyEvent()
1575 //
1576 // Fills in the PLGraphicsIn from an XKeyEvent. The PLGraphicsIn keysym is
1577 // the same as the X keysym for all cases except for control keys that have
1578 // ASCII equivalents, i.e.:
1579 //
1580 // Name X-keysym ASCII Ctrl-key
1581 // ---- -------- ----- --------
1582 // XK_BackSpace 0xFF08 0x08 ^H
1583 // XK_Tab 0xFF09 0x09 ^I
1584 // XK_Linefeed 0xFF0A 0x0A ^J
1585 // XK_Return 0xFF0D 0x0D ^M
1586 // XK_Escape 0xFF1B 0x1B ^[
1587 // XK_Delete 0xFFFF 0xFF (none)
1588 //
1589 // The ASCII representation of these characters is used for the PLGraphicsIn
1590 // keysym to simplify code logic. It turns out that the X keysyms are
1591 // identical to the ASCII values with the upper 8 bits set.
1592 //--------------------------------------------------------------------------
1593 
1594 static void
1595 LookupXKeyEvent( PLStream *pls, XEvent *event )
1596 {
1597  XwDev *dev = (XwDev *) pls->dev;
1598  PLGraphicsIn *gin = &( dev->gin );
1599  XKeyEvent *keyEvent = (XKeyEvent *) event;
1600  KeySym keysym;
1601  int nchars, ncmax = PL_MAXKEY - 1;
1602  XComposeStatus cs;
1603 
1604  gin->pX = keyEvent->x;
1605  gin->pY = keyEvent->y;
1606  gin->dX = (PLFLT) keyEvent->x / ( dev->width - 1 );
1607  gin->dY = 1.0 - (PLFLT) keyEvent->y / ( dev->height - 1 );
1608 
1609  gin->state = keyEvent->state;
1610 
1611  nchars = XLookupString( keyEvent, gin->string, ncmax, &keysym, &cs );
1612  gin->string[nchars] = '\0';
1613 
1614  pldebug( "LookupXKeyEvent",
1615  "Keysym %x, translation: %s\n", keysym, gin->string );
1616 
1617  switch ( keysym )
1618  {
1619  case XK_BackSpace:
1620  case XK_Tab:
1621  case XK_Linefeed:
1622  case XK_Return:
1623  case XK_Escape:
1624  case XK_Delete:
1625  gin->keysym = 0xFF & keysym;
1626  break;
1627 
1628  default:
1629  gin->keysym = (unsigned int) keysym;
1630  }
1631 }
1632 
1633 //--------------------------------------------------------------------------
1634 // LookupXButtonEvent()
1635 //
1636 // Fills in the PLGraphicsIn from an XButtonEvent.
1637 //--------------------------------------------------------------------------
1638 
1639 static void
1640 LookupXButtonEvent( PLStream *pls, XEvent *event )
1641 {
1642  XwDev *dev = (XwDev *) pls->dev;
1643  PLGraphicsIn *gin = &( dev->gin );
1644  XButtonEvent *buttonEvent = (XButtonEvent *) event;
1645 
1646  pldebug( "LookupXButtonEvent",
1647  "Button: %d, x: %d, y: %d\n",
1648  buttonEvent->button, buttonEvent->x, buttonEvent->y );
1649 
1650  gin->pX = buttonEvent->x;
1651  gin->pY = buttonEvent->y;
1652  gin->dX = (PLFLT) buttonEvent->x / ( dev->width - 1 );
1653  gin->dY = 1.0 - (PLFLT) buttonEvent->y / ( dev->height - 1 );
1654 
1655  gin->button = buttonEvent->button;
1656  gin->state = buttonEvent->state;
1657  gin->keysym = 0x20;
1658 }
1659 
1660 //--------------------------------------------------------------------------
1661 // ProcessKey()
1662 //
1663 // Process keyboard events other than locate input.
1664 //--------------------------------------------------------------------------
1665 
1666 static void
1667 ProcessKey( PLStream *pls )
1668 {
1669  XwDev *dev = (XwDev *) pls->dev;
1670  PLGraphicsIn *gin = &( dev->gin );
1671 
1672  dbug_enter( "ProcessKey" );
1673 
1674 // Call user keypress event handler. Since this is called first, the user
1675 // can disable all internal event handling by setting key.keysym to 0.
1676 //
1677  if ( pls->KeyEH != NULL )
1678  ( *pls->KeyEH )( gin, pls->KeyEH_data, &dev->exit_eventloop );
1679 
1680 // Handle internal events
1681 
1682  switch ( gin->keysym )
1683  {
1684  case PLK_Return:
1685  case PLK_Linefeed:
1686  case PLK_Next:
1687  // Advance to next page (i.e. terminate event loop) on a <eol>
1688  // Check for both <CR> and <LF> for portability, also a <Page Down>
1689  dev->exit_eventloop = TRUE;
1690  break;
1691 
1692  case 'Q':
1693  // Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake)
1694  pls->nopause = TRUE;
1695  plexit( "" );
1696  break;
1697 
1698  case 'L':
1699  // Begin locate mode
1701  CreateXhairs( pls );
1702  break;
1703  }
1704 }
1705 
1706 //--------------------------------------------------------------------------
1707 // ProcessButton()
1708 //
1709 // Process ButtonPress events other than locate input.
1710 // On:
1711 // Button1: nothing (except when in locate mode, see ButtonLocate)
1712 // Button2: nothing
1713 // Button3: set page advance flag
1714 //--------------------------------------------------------------------------
1715 
1716 static void
1717 ProcessButton( PLStream *pls )
1718 {
1719  XwDev *dev = (XwDev *) pls->dev;
1720  PLGraphicsIn *gin = &( dev->gin );
1721 
1722  dbug_enter( "ProcessButton" );
1723 
1724 // Call user event handler. Since this is called first, the user can
1725 // disable all PLplot internal event handling by setting gin->button to 0.
1726 //
1727  if ( pls->ButtonEH != NULL )
1728  ( *pls->ButtonEH )( gin, pls->ButtonEH_data, &dev->exit_eventloop );
1729 
1730 // Handle internal events
1731 
1732  switch ( gin->button )
1733  {
1734  case Button3:
1735  dev->exit_eventloop = TRUE;
1736  break;
1737  }
1738 }
1739 
1740 //--------------------------------------------------------------------------
1741 // LocateKey()
1742 //
1743 // Front-end to locate handler for KeyPress events.
1744 // Provides for a number of special effects:
1745 //
1746 // <Escape> Ends locate mode
1747 // <Cursor> Moves cursor one pixel in the specified direction
1748 // <Mod-Cursor> Accelerated cursor movement (5x for each modifier)
1749 //--------------------------------------------------------------------------
1750 
1751 static void
1752 LocateKey( PLStream *pls )
1753 {
1754  XwDev *dev = (XwDev *) pls->dev;
1755  XwDisplay *xwd = (XwDisplay *) dev->xwd;
1756  PLGraphicsIn *gin = &( dev->gin );
1757 
1758 // End locate mode on <Escape>
1759 
1760  if ( gin->keysym == PLK_Escape )
1761  {
1762  dev->locate_mode = 0;
1763  DestroyXhairs( pls );
1764  plGinInit( gin );
1765  }
1766 
1767 // Ignore modifier keys
1768 
1769  else if ( IsModifierKey( gin->keysym ) )
1770  {
1771  plGinInit( gin );
1772  }
1773 
1774 // Now handle cursor keys
1775 
1776  else if ( IsCursorKey( gin->keysym ) )
1777  {
1778  int x1, y1, dx = 0, dy = 0;
1779  int xmin = 0, xmax = (int) dev->width - 1, ymin = 0, ymax = (int) dev->height - 1;
1780 
1781  switch ( gin->keysym )
1782  {
1783  case PLK_Left:
1784  dx = -1;
1785  break;
1786  case PLK_Right:
1787  dx = 1;
1788  break;
1789  case PLK_Up:
1790  dy = -1;
1791  break;
1792  case PLK_Down:
1793  dy = 1;
1794  break;
1795  }
1796 
1797  // Each modifier key added increases the multiplication factor by 5
1798 
1799  // Shift
1800 
1801  if ( gin->state & 0x01 )
1802  {
1803  dx *= 5;
1804  dy *= 5;
1805  }
1806 
1807  // Caps Lock
1808 
1809  if ( gin->state & 0x02 )
1810  {
1811  dx *= 5;
1812  dy *= 5;
1813  }
1814 
1815  // Control
1816 
1817  if ( gin->state & 0x04 )
1818  {
1819  dx *= 5;
1820  dy *= 5;
1821  }
1822 
1823  // Alt
1824 
1825  if ( gin->state & 0x08 )
1826  {
1827  dx *= 5;
1828  dy *= 5;
1829  }
1830 
1831  // Bounds checking so that we don't send cursor out of window
1832 
1833  x1 = gin->pX + dx;
1834  y1 = gin->pY + dy;
1835 
1836  if ( x1 < xmin )
1837  dx = xmin - gin->pX;
1838  if ( y1 < ymin )
1839  dy = ymin - gin->pY;
1840  if ( x1 > xmax )
1841  dx = xmax - gin->pX;
1842  if ( y1 > ymax )
1843  dy = ymax - gin->pY;
1844 
1845  // Engage...
1846 
1847  XWarpPointer( xwd->display, dev->window, None, 0, 0, 0, 0, dx, dy );
1848  plGinInit( gin );
1849  }
1850 
1851 // Call ordinary locate handler
1852 
1853  else
1854  {
1855  Locate( pls );
1856  }
1857 }
1858 
1859 //--------------------------------------------------------------------------
1860 // LocateButton()
1861 //
1862 // Front-end to locate handler for ButtonPress events.
1863 // Only passes control to Locate() for Button1 presses.
1864 //--------------------------------------------------------------------------
1865 
1866 static void
1867 LocateButton( PLStream *pls )
1868 {
1869  XwDev *dev = (XwDev *) pls->dev;
1870  PLGraphicsIn *gin = &( dev->gin );
1871 
1872  switch ( gin->button )
1873  {
1874  case Button1:
1875  Locate( pls );
1876  break;
1877  }
1878 }
1879 
1880 //--------------------------------------------------------------------------
1881 // Locate()
1882 //
1883 // Handles locate mode events.
1884 //
1885 // In locate mode: move cursor to desired location and select by pressing a
1886 // key or by clicking on the mouse (if available). Typically the world
1887 // coordinates of the selected point are reported.
1888 //
1889 // There are two ways to enter Locate mode -- via the API, or via a driver
1890 // command. The API entry point is the call plGetCursor(), which initiates
1891 // locate mode and does not return until input has been obtained. The
1892 // driver entry point is by entering a 'L' while the driver is waiting for
1893 // events.
1894 //
1895 // Locate mode input is reported in one of three ways:
1896 // 1. Through a returned PLGraphicsIn structure, when user has specified a
1897 // locate handler via (*pls->LocateEH).
1898 // 2. Through a returned PLGraphicsIn structure, when locate mode is invoked
1899 // by a plGetCursor() call.
1900 // 3. Through writes to stdout, when locate mode is invoked by a driver
1901 // command and the user has not supplied a locate handler.
1902 //
1903 // Hitting <Escape> will at all times end locate mode. Other keys will
1904 // typically be interpreted as locator input. Selecting a point out of
1905 // bounds will end locate mode unless the user overrides with a supplied
1906 // Locate handler.
1907 //--------------------------------------------------------------------------
1908 
1909 static void
1910 Locate( PLStream *pls )
1911 {
1912  XwDev *dev = (XwDev *) pls->dev;
1913  PLGraphicsIn *gin = &( dev->gin );
1914 
1915 // Call user locate mode handler if provided
1916 
1917  if ( pls->LocateEH != NULL )
1918  ( *pls->LocateEH )( gin, pls->LocateEH_data, &dev->locate_mode );
1919 
1920 // Use default procedure
1921 
1922  else
1923  {
1924  // Try to locate cursor
1925 
1926  if ( plTranslateCursor( gin ) )
1927  {
1928  // If invoked by the API, we're done
1929  // Otherwise send report to stdout
1930 
1931  if ( dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER )
1932  {
1933  pltext();
1934  if ( gin->keysym < 0xFF && isprint( gin->keysym ) )
1935  printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym );
1936  else
1937  printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym );
1938 
1939  plgra();
1940  }
1941  }
1942  else
1943  {
1944  // Selected point is out of bounds, so end locate mode
1945 
1946  dev->locate_mode = 0;
1947  DestroyXhairs( pls );
1948  }
1949  }
1950 }
1951 
1952 //--------------------------------------------------------------------------
1953 // MotionEH()
1954 //
1955 // Event handler routine for MotionNotify events.
1956 // If drawing crosshairs, the first and last draws must be done "by hand".
1957 //--------------------------------------------------------------------------
1958 
1959 static void
1960 MotionEH( PLStream *pls, XEvent *event )
1961 {
1962  XwDev *dev = (XwDev *) pls->dev;
1963  XMotionEvent *motionEvent = (XMotionEvent *) event;
1964 
1965  if ( dev->drawing_xhairs )
1966  {
1967  DrawXhairs( pls, motionEvent->x, motionEvent->y );
1968  }
1969 }
1970 
1971 //--------------------------------------------------------------------------
1972 // EnterEH()
1973 //
1974 // Event handler routine for EnterNotify events. Only called if drawing
1975 // crosshairs -- a draw must be done here to start off the new set.
1976 //--------------------------------------------------------------------------
1977 
1978 static void
1979 EnterEH( PLStream *pls, XEvent *event )
1980 {
1981  XwDev *dev = (XwDev *) pls->dev;
1982  XCrossingEvent *crossingEvent = (XCrossingEvent *) event;
1983 
1984  DrawXhairs( pls, crossingEvent->x, crossingEvent->y );
1985  dev->drawing_xhairs = 1;
1986 }
1987 
1988 //--------------------------------------------------------------------------
1989 // LeaveEH()
1990 //
1991 // Event handler routine for EnterNotify or LeaveNotify events.
1992 // If drawing crosshairs, a draw must be done here to start off the new
1993 // set or erase the last set.
1994 //--------------------------------------------------------------------------
1995 
1996 static void
1997 LeaveEH( PLStream *pls, XEvent * PL_UNUSED( event ) )
1998 {
1999  XwDev *dev = (XwDev *) pls->dev;
2000 
2001  UpdateXhairs( pls );
2002  dev->drawing_xhairs = 0;
2003 }
2004 
2005 //--------------------------------------------------------------------------
2006 // CreateXhairs()
2007 //
2008 // Creates graphic crosshairs at current pointer location.
2009 //--------------------------------------------------------------------------
2010 
2011 static void
2012 CreateXhairs( PLStream *pls )
2013 {
2014  XwDev *dev = (XwDev *) pls->dev;
2015  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2016  Window root, child;
2017  int root_x, root_y, win_x, win_y;
2018  unsigned int mask;
2019  XEvent event;
2020 
2021 // Get a crosshair cursor and switch to it.
2022 
2023  if ( !xwd->xhair_cursor )
2024  xwd->xhair_cursor = XCreateFontCursor( xwd->display, XC_crosshair );
2025 
2026  XDefineCursor( xwd->display, dev->window, xwd->xhair_cursor );
2027 
2028 // Find current pointer location and draw graphic crosshairs if pointer is
2029 // inside our window
2030 
2031  if ( XQueryPointer( xwd->display, dev->window, &root, &child,
2032  &root_x, &root_y, &win_x, &win_y, &mask ) )
2033  {
2034  if ( win_x >= 0 && win_x < (int) dev->width &&
2035  win_y >= 0 && win_y < (int) dev->height )
2036  {
2037  DrawXhairs( pls, win_x, win_y );
2038  dev->drawing_xhairs = 1;
2039  }
2040  }
2041 
2042 // Sync the display and then throw away all pending motion events
2043 
2044  XSync( xwd->display, 0 );
2045  while ( XCheckWindowEvent( xwd->display, dev->window,
2046  PointerMotionMask, &event ) )
2047  ;
2048 
2049 // Catch PointerMotion and crossing events so we can update them properly
2050 
2051  dev->event_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask;
2052  XSelectInput( xwd->display, dev->window, dev->event_mask );
2053 }
2054 
2055 //--------------------------------------------------------------------------
2056 // DestroyXhairs()
2057 //
2058 // Destroys graphic crosshairs.
2059 //--------------------------------------------------------------------------
2060 
2061 static void
2062 DestroyXhairs( PLStream *pls )
2063 {
2064  XwDev *dev = (XwDev *) pls->dev;
2065  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2066 
2067 // Switch back to boring old pointer
2068 
2069  XUndefineCursor( xwd->display, dev->window );
2070 
2071 // Don't catch PointerMotion or crossing events any more
2072 
2073  dev->event_mask &=
2074  ~PointerMotionMask & ~EnterWindowMask & ~LeaveWindowMask;
2075  XSelectInput( xwd->display, dev->window, dev->event_mask );
2076 
2077 // This draw removes the last set of graphic crosshairs
2078 
2079  UpdateXhairs( pls );
2080  dev->drawing_xhairs = 0;
2081 }
2082 
2083 //--------------------------------------------------------------------------
2084 // DrawXhairs()
2085 //
2086 // Draws graphic crosshairs at (x0, y0). The first draw erases the old set.
2087 //--------------------------------------------------------------------------
2088 
2089 static void
2090 DrawXhairs( PLStream *pls, int x0, int y0 )
2091 {
2092  XwDev *dev = (XwDev *) pls->dev;
2093 
2094  int xmin = 0, xmax = (int) dev->width - 1;
2095  int ymin = 0, ymax = (int) dev->height - 1;
2096 
2097  if ( dev->drawing_xhairs )
2098  UpdateXhairs( pls );
2099 
2100  dev->xhair_x[0].x = (short) xmin; dev->xhair_x[0].y = (short) y0;
2101  dev->xhair_x[1].x = (short) xmax; dev->xhair_x[1].y = (short) y0;
2102 
2103  dev->xhair_y[0].x = (short) x0; dev->xhair_y[0].y = (short) ymin;
2104  dev->xhair_y[1].x = (short) x0; dev->xhair_y[1].y = (short) ymax;
2105 
2106  UpdateXhairs( pls );
2107 }
2108 
2109 //--------------------------------------------------------------------------
2110 // UpdateXhairs()
2111 //
2112 // Updates graphic crosshairs. If already there, they are erased.
2113 //--------------------------------------------------------------------------
2114 
2115 static void
2116 UpdateXhairs( PLStream *pls )
2117 {
2118  XwDev *dev = (XwDev *) pls->dev;
2119  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2120 
2121  XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_x, 2,
2122  CoordModeOrigin );
2123 
2124  XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_y, 2,
2125  CoordModeOrigin );
2126 }
2127 
2128 //--------------------------------------------------------------------------
2129 // ExposeEH()
2130 //
2131 // Event handler routine for expose events.
2132 // Front end to ExposeCmd() to deal with wierdnesses of Xlib.
2133 //--------------------------------------------------------------------------
2134 
2135 static void
2136 ExposeEH( PLStream *pls, XEvent *event )
2137 {
2138  XwDev *dev = (XwDev *) pls->dev;
2139  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2140  XExposeEvent *exposeEvent = (XExposeEvent *) event;
2141  PLDisplay pldis;
2142  int redrawn;
2143 
2144  dbug_enter( "ExposeEH" );
2145 
2146  pldebug( "ExposeEH",
2147  "x = %d, y = %d, width = %d, height = %d, count = %d, pending = %d\n",
2148  exposeEvent->x, exposeEvent->y,
2149  exposeEvent->width, exposeEvent->height,
2150  exposeEvent->count, XPending( xwd->display ) );
2151 
2152 // Handle expose
2153 // If we have anything overlaid (like crosshairs), we need to refresh the
2154 // entire plot in order to have a predictable outcome. In this case we
2155 // need to first clear window. Otherwise it's better to not clear it, for a
2156 // smoother redraw (unobscured regions appear to stay the same).
2157 
2158  if ( dev->drawing_xhairs )
2159  {
2160  XClearWindow( xwd->display, dev->window );
2161  ExposeCmd( pls, NULL );
2162  UpdateXhairs( pls );
2163  redrawn = 1;
2164  }
2165  else
2166  {
2167  pldis.x = (unsigned int) exposeEvent->x;
2168  pldis.y = (unsigned int) exposeEvent->y;
2169  pldis.width = (unsigned int) exposeEvent->width;
2170  pldis.height = (unsigned int) exposeEvent->height;
2171 
2172  ExposeCmd( pls, &pldis );
2173  redrawn = !dev->write_to_pixmap;
2174  }
2175 
2176 // If entire plot was redrawn, remove extraneous events from the queue
2177 
2178  if ( redrawn )
2179  while ( XCheckWindowEvent( xwd->display, dev->window,
2180  ExposureMask | StructureNotifyMask, event ) )
2181  ;
2182 }
2183 
2184 //--------------------------------------------------------------------------
2185 // ResizeEH()
2186 //
2187 // Event handler routine for resize events.
2188 // Front end to ResizeCmd() to deal with wierdnesses of Xlib.
2189 //--------------------------------------------------------------------------
2190 
2191 static void
2192 ResizeEH( PLStream *pls, XEvent *event )
2193 {
2194  XwDev *dev = (XwDev *) pls->dev;
2195  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2196  XConfigureEvent *configEvent = (XConfigureEvent *) event;
2197  PLDisplay pldis;
2198 
2199  dbug_enter( "ResizeEH" );
2200 
2201  pldis.width = (unsigned int) configEvent->width;
2202  pldis.height = (unsigned int) configEvent->height;
2203 
2204 // Only need to resize if size is actually changed
2205 
2206  if ( pldis.width == dev->width && pldis.height == dev->height )
2207  return;
2208 
2209  pldebug( "ResizeEH",
2210  "x = %d, y = %d, pending = %d\n",
2211  configEvent->width, configEvent->height, XPending( xwd->display ) );
2212 
2213 // Handle resize
2214 
2215  ResizeCmd( pls, &pldis );
2216  if ( dev->drawing_xhairs )
2217  {
2218  UpdateXhairs( pls );
2219  }
2220 
2221 // Remove extraneous Expose and ConfigureNotify events from the event queue
2222 // Exposes do not need to be handled since we've redrawn the whole plot
2223 
2224  XFlush( xwd->display );
2225  while ( XCheckWindowEvent( xwd->display, dev->window,
2226  ExposureMask | StructureNotifyMask, event ) )
2227  ;
2228 }
2229 
2230 //--------------------------------------------------------------------------
2231 // ExposeCmd()
2232 //
2233 // Event handler routine for expose events.
2234 // These are "pure" exposures (no resize), so don't need to clear window.
2235 //--------------------------------------------------------------------------
2236 
2237 static void
2238 ExposeCmd( PLStream *pls, PLDisplay *pldis )
2239 {
2240  XwDev *dev = (XwDev *) pls->dev;
2241  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2242  int x, y;
2243  unsigned int width, height;
2244 
2245  dbug_enter( "ExposeCmd" );
2246 
2247 // Return if plD_init_xw hasn't been called yet
2248 
2249  if ( dev == NULL )
2250  {
2251  plwarn( "ExposeCmd: Illegal call -- driver uninitialized" );
2252  return;
2253  }
2254 
2255 // Exposed area. If unspecified, the entire window is used.
2256 
2257  if ( pldis == NULL )
2258  {
2259  x = 0;
2260  y = 0;
2261  width = dev->width;
2262  height = dev->height;
2263  }
2264  else
2265  {
2266  x = (int) pldis->x;
2267  y = (int) pldis->y;
2268  width = pldis->width;
2269  height = pldis->height;
2270  }
2271 
2272 // Usual case: refresh window from pixmap
2273 // DEBUG option: draws rectangle around refreshed region
2274 
2275  XSync( xwd->display, 0 );
2276  if ( dev->write_to_pixmap )
2277  {
2278  XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc,
2279  x, y, width, height, x, y );
2280  XSync( xwd->display, 0 );
2281 #ifdef DEBUG
2282  if ( pls->debug )
2283  {
2284  XPoint pts[5];
2285  int x0 = x, x1 = x + (int) width, y0 = y, y1 = y + (int) height;
2286  pts[0].x = (short) x0; pts[0].y = (short) y0;
2287  pts[1].x = (short) x1; pts[1].y = (short) y0;
2288  pts[2].x = (short) x1; pts[2].y = (short) y1;
2289  pts[3].x = (short) x0; pts[3].y = (short) y1;
2290  pts[4].x = (short) x0; pts[4].y = (short) y0;
2291 
2292  XDrawLines( xwd->display, dev->window, dev->gc, pts, 5,
2293  CoordModeOrigin );
2294  }
2295 #endif
2296  }
2297  else
2298  {
2299  plRemakePlot( pls );
2300  XFlush( xwd->display );
2301  }
2302 }
2303 
2304 //--------------------------------------------------------------------------
2305 // ResizeCmd()
2306 //
2307 // Event handler routine for resize events.
2308 //--------------------------------------------------------------------------
2309 
2310 static void
2311 ResizeCmd( PLStream *pls, PLDisplay *pldis )
2312 {
2313  XwDev *dev = (XwDev *) pls->dev;
2314  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2315  int write_to_window = dev->write_to_window;
2316 
2317  dbug_enter( "ResizeCmd" );
2318 
2319 // Return if plD_init_xw hasn't been called yet
2320 
2321  if ( dev == NULL )
2322  {
2323  plwarn( "ResizeCmd: Illegal call -- driver uninitialized" );
2324  return;
2325  }
2326 
2327 // Return if pointer to window not specified.
2328 
2329  if ( pldis == NULL )
2330  {
2331  plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" );
2332  return;
2333  }
2334 
2335 // Reset current window bounds
2336 
2337  dev->width = pldis->width;
2338  dev->height = pldis->height;
2339 
2340  dev->xscale = dev->width / (double) dev->init_width;
2341  dev->yscale = dev->height / (double) dev->init_height;
2342 
2343  dev->xscale = dev->xscale * dev->xscale_init;
2344  dev->yscale = dev->yscale * dev->yscale_init;
2345 
2346 #if PHYSICAL
2347  {
2348  PLFLT pxlx = DPMM / dev->xscale;
2349  PLFLT pxly = DPMM / dev->yscale;
2350  plP_setpxl( pxlx, pxly );
2351  }
2352 #endif
2353 
2354 // Note: the following order MUST be obeyed -- if you instead redraw into
2355 // the window and then copy it to the pixmap, off-screen parts of the window
2356 // may contain garbage which is then transferred to the pixmap (and thus
2357 // will not go away after an expose).
2358 //
2359 
2360 // Resize pixmap using new dimensions
2361 
2362  if ( dev->write_to_pixmap )
2363  {
2364  dev->write_to_window = 0;
2365  XFreePixmap( xwd->display, dev->pixmap );
2366  CreatePixmap( pls );
2367  }
2368 
2369 // This allows an external agent to take over the redraw
2370  if ( pls->ext_resize_draw )
2371  return;
2372 
2373 // Initialize & redraw (to pixmap, if available).
2374 
2375  if ( dev->write_to_pixmap )
2376  {
2377  XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
2378  XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
2379  dev->width, dev->height );
2380  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
2381  }
2382  if ( dev->write_to_window )
2383  {
2384  XClearWindow( xwd->display, dev->window );
2385  }
2386  plRemakePlot( pls );
2387  XSync( xwd->display, 0 );
2388 
2389 // If pixmap available, fake an expose
2390 
2391  if ( dev->write_to_pixmap )
2392  {
2393  dev->write_to_window = write_to_window;
2394  XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
2395  dev->width, dev->height, 0, 0 );
2396  XSync( xwd->display, 0 );
2397  }
2398 }
2399 
2400 //--------------------------------------------------------------------------
2401 // ConfigBufferingCmd()
2402 //
2403 // Allows a widget to manipulate the double buffering support in the
2404 // xwin dirver.
2405 //--------------------------------------------------------------------------
2406 
2407 static void ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr )
2408 {
2409  XwDev *dev = (XwDev *) pls->dev;
2410 
2411  switch ( ptr->cmd )
2412  {
2414  dev->write_to_window = 0;
2415  pls->db = 1;
2416  break;
2417 
2419  dev->write_to_window = 1;
2420  pls->db = 0;
2421  break;
2422 
2424  ptr->result = pls->db;
2425  break;
2426 
2427  default:
2428  printf( "Unrecognized buffering request ignored.\n" );
2429  break;
2430  }
2431 }
2432 
2433 //--------------------------------------------------------------------------
2434 // RedrawCmd()
2435 //
2436 // Handles page redraw without resize (pixmap does not get reallocated).
2437 // Calling this makes sure all necessary housekeeping gets done.
2438 //--------------------------------------------------------------------------
2439 
2440 static void
2441 RedrawCmd( PLStream *pls )
2442 {
2443  XwDev *dev = (XwDev *) pls->dev;
2444  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2445  int write_to_window = dev->write_to_window;
2446 
2447  dbug_enter( "RedrawCmd" );
2448 
2449 // Return if plD_init_xw hasn't been called yet
2450 
2451  if ( dev == NULL )
2452  {
2453  plwarn( "RedrawCmd: Illegal call -- driver uninitialized" );
2454  return;
2455  }
2456 
2457 // Initialize & redraw (to pixmap, if available).
2458 
2459  if ( dev->write_to_pixmap )
2460  {
2461  dev->write_to_window = 0;
2462  XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
2463  XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
2464  dev->width, dev->height );
2465  XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
2466  }
2467  if ( dev->write_to_window )
2468  {
2469  XClearWindow( xwd->display, dev->window );
2470  }
2471  plRemakePlot( pls );
2472  XSync( xwd->display, 0 );
2473 
2474  dev->write_to_window = write_to_window;
2475 
2476 // If pixmap available, fake an expose
2477 
2478  if ( dev->write_to_pixmap )
2479  {
2480  XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
2481  dev->width, dev->height, 0, 0 );
2482  XSync( xwd->display, 0 );
2483  }
2484 }
2485 
2486 //--------------------------------------------------------------------------
2487 // CreatePixmapErrorHandler()
2488 //
2489 // Error handler used in CreatePixmap() to catch errors in allocating
2490 // storage for pixmap. This way we can nicely substitute redraws for
2491 // pixmap copies if the server has insufficient memory.
2492 //--------------------------------------------------------------------------
2493 
2494 static unsigned char CreatePixmapStatus;
2495 
2496 static int
2497 CreatePixmapErrorHandler( Display *display, XErrorEvent *error )
2498 {
2499  CreatePixmapStatus = error->error_code;
2500  if ( error->error_code != BadAlloc )
2501  {
2502  char buffer[256];
2503  XGetErrorText( display, error->error_code, buffer, 256 );
2504  fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer );
2505  }
2506  return 1;
2507 }
2508 
2509 //--------------------------------------------------------------------------
2510 // CreatePixmap()
2511 //
2512 // This routine creates a pixmap, doing error trapping in case there
2513 // isn't enough memory on the server.
2514 //--------------------------------------------------------------------------
2515 
2516 static void
2517 CreatePixmap( PLStream *pls )
2518 {
2519  XwDev *dev = (XwDev *) pls->dev;
2520  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2521 
2522  int ( *oldErrorHandler )( Display *, XErrorEvent * );
2523 
2524  oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler );
2525 
2526  CreatePixmapStatus = Success;
2527  pldebug( "CreatePixmap",
2528  "creating pixmap: width = %d, height = %d, depth = %d\n",
2529  dev->width, dev->height, xwd->depth );
2530 
2531  dev->pixmap = XCreatePixmap( xwd->display, dev->window,
2532  dev->width, dev->height, xwd->depth );
2533  XSync( xwd->display, 0 );
2534  if ( CreatePixmapStatus != Success )
2535  {
2536  dev->write_to_pixmap = 0;
2537  dev->write_to_window = 1;
2538  pls->db = 0;
2539  fprintf( stderr, "\n\
2540 Warning: pixmap could not be allocated (insufficient memory on server).\n\
2541 Driver will redraw the entire plot to handle expose events.\n" );
2542  }
2543 
2544  XSetErrorHandler( oldErrorHandler );
2545 }
2546 
2547 //--------------------------------------------------------------------------
2548 // GetVisual()
2549 //
2550 // Get visual info. In order to safely use a visual other than that of
2551 // the parent (which hopefully is that returned by DefaultVisual), you
2552 // must first find (using XGetRGBColormaps) or create a colormap matching
2553 // this visual and then set the colormap window attribute in the
2554 // XCreateWindow attributes and valuemask arguments. I don't do this
2555 // right now, so this is turned off by default.
2556 //--------------------------------------------------------------------------
2557 
2558 static void
2559 GetVisual( PLStream *pls )
2560 {
2561  XwDev *dev = (XwDev *) pls->dev;
2562  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2563  int visuals_matched = 0;
2564 
2565  dbug_enter( "GetVisual" );
2566 
2567  if ( !defaultvisual )
2568  {
2569  XVisualInfo vTemplate, *visualList;
2570 
2571 // Try for an 8 plane display, if unavailable go for the default
2572 
2573  vTemplate.screen = xwd->screen;
2574  vTemplate.depth = 8;
2575 
2576  visualList = XGetVisualInfo( xwd->display,
2577  VisualScreenMask | VisualDepthMask,
2578  &vTemplate, &visuals_matched );
2579 
2580 #ifdef HACK_STATICCOLOR
2581  if ( visuals_matched )
2582  {
2583  int i, found = 0;
2584  printf( "visuals_matched = %d\n", visuals_matched );
2585  for ( i = 0; i < visuals_matched && !found; i++ )
2586  {
2587  Visual *v = visualList[i].visual;
2588  printf( "Checking visual %d: ", i );
2589  switch ( v->class )
2590  {
2591  case PseudoColor:
2592  printf( "PseudoColor\n" );
2593  break;
2594  case GrayScale:
2595  printf( "GrayScale\n" );
2596  break;
2597  case DirectColor:
2598  printf( "DirectColor\n" );
2599  break;
2600  case TrueColor:
2601  printf( "TrueColor\n" );
2602  break;
2603  case StaticColor:
2604  printf( "StaticColor\n" );
2605  break;
2606  case StaticGray:
2607  printf( "StaticGray\n" );
2608  break;
2609  default:
2610  printf( "Unknown.\n" );
2611  break;
2612  }
2613  if ( v->class == StaticColor )
2614  {
2615  xwd->visual = v;
2616  xwd->depth = visualList[i].depth;
2617  found = 1;
2618  }
2619  }
2620  if ( !found )
2621  {
2622  plexit( "Unable to get a StaticColor visual." );
2623  }
2624  printf( "Found StaticColor visual, depth=%d\n", xwd->depth );
2625  }
2626 #else
2627  if ( visuals_matched )
2628  {
2629  xwd->visual = visualList->visual; // Choose first match.
2630  xwd->depth = (unsigned int) vTemplate.depth;
2631  }
2632 #endif // HACK_STATICCOLOR
2633  }
2634 
2635  if ( !visuals_matched )
2636  {
2637  xwd->visual = DefaultVisual( xwd->display, xwd->screen );
2638  xwd->depth = (unsigned int) DefaultDepth( xwd->display, xwd->screen );
2639  }
2640 
2641 // Check to see if we expect to be able to allocate r/w color cells.
2642 
2643  switch ( xwd->visual->class )
2644  {
2645  case TrueColor:
2646  case StaticColor:
2647  case StaticGray:
2648  xwd->rw_cmap = 0;
2649  break;
2650  default:
2651  xwd->rw_cmap = 1;
2652  }
2653 
2654 /*xwd->rw_cmap = 0;*/ /* debugging hack. */
2655 
2656 // Just for kicks, see what kind of visual we got.
2657 
2658  if ( pls->verbose )
2659  {
2660  fprintf( stderr, "XVisual class == " );
2661  switch ( xwd->visual->class )
2662  {
2663  case PseudoColor:
2664  fprintf( stderr, "PseudoColor\n" );
2665  break;
2666  case GrayScale:
2667  fprintf( stderr, "GrayScale\n" );
2668  break;
2669  case DirectColor:
2670  fprintf( stderr, "DirectColor\n" );
2671  break;
2672  case TrueColor:
2673  fprintf( stderr, "TrueColor\n" );
2674  break;
2675  case StaticColor:
2676  fprintf( stderr, "StaticColor\n" );
2677  break;
2678  case StaticGray:
2679  fprintf( stderr, "StaticGray\n" );
2680  break;
2681  default:
2682  fprintf( stderr, "Unknown.\n" );
2683  break;
2684  }
2685  fprintf( stderr, "xwd->rw_cmap = %d\n", xwd->rw_cmap );
2686  }
2687 }
2688 
2689 //--------------------------------------------------------------------------
2690 // AllocBGFG()
2691 //
2692 // Allocate background & foreground colors. If possible, I choose pixel
2693 // values such that the fg pixel is the xor of the bg pixel, to make
2694 // rubber-banding easy to see.
2695 //--------------------------------------------------------------------------
2696 
2697 static void
2698 AllocBGFG( PLStream *pls )
2699 {
2700  XwDev *dev = (XwDev *) pls->dev;
2701  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2702 
2703  int i, j, npixels;
2704  unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
2705 
2706  dbug_enter( "AllocBGFG" );
2707 
2708 // If not on a color system, just return
2709  if ( !xwd->color )
2710  return;
2711 
2712  if ( xwd->rw_cmap &&
2713  // r/w color maps
2714  XAllocColorCells( xwd->display, xwd->map, False,
2715  plane_masks, 0, pixels, 1 ) )
2716  {
2717  // background
2718  xwd->cmap0[0].pixel = pixels[0];
2719  }
2720  else
2721  {
2722  // r/o color maps
2723  xwd->cmap0[0].pixel = BlackPixel( xwd->display, xwd->screen );
2724  xwd->fgcolor.pixel = WhitePixel( xwd->display, xwd->screen );
2725  if ( xwd->rw_cmap && pls->verbose )
2726  fprintf( stderr, "Downgrading to r/o cmap.\n" );
2727  xwd->rw_cmap = 0;
2728  return;
2729  }
2730 
2731 // Allocate as many colors as we can
2732 
2733  npixels = RWMAP_MAX_COLORS;
2734  for (;; )
2735  {
2736  if ( XAllocColorCells( xwd->display, xwd->map, False,
2737  plane_masks, 0, pixels, (unsigned int) npixels ) )
2738  break;
2739  npixels--;
2740  if ( npixels == 0 )
2741  break;
2742  }
2743 
2744 // Find the color with pixel = xor of the bg color pixel.
2745 // If a match isn't found, the last pixel allocated is used.
2746 
2747  for ( i = 0; i < npixels - 1; i++ )
2748  {
2749  if ( pixels[i] == ( ~xwd->cmap0[0].pixel & 0xFF ) )
2750  break;
2751  }
2752 
2753 // Use this color cell for our foreground color. Then free the rest.
2754 
2755  xwd->fgcolor.pixel = pixels[i];
2756  for ( j = 0; j < npixels; j++ )
2757  {
2758  if ( j != i )
2759  XFreeColors( xwd->display, xwd->map, &pixels[j], 1, 0 );
2760  }
2761 }
2762 
2763 //--------------------------------------------------------------------------
2764 // SetBGFG()
2765 //
2766 // Set background & foreground colors. Foreground over background should
2767 // have high contrast.
2768 //--------------------------------------------------------------------------
2769 
2770 static void
2771 SetBGFG( PLStream *pls )
2772 {
2773  XwDev *dev = (XwDev *) pls->dev;
2774  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2775 
2776  PLColor fgcolor;
2777  unsigned int gslevbg, gslevfg;
2778 
2779  dbug_enter( "SetBGFG" );
2780 
2781 //
2782 // Set background color.
2783 //
2784 // Background defaults to black on color screens, white on grayscale (many
2785 // grayscale monitors have poor contrast, and black-on-white looks better).
2786 //
2787 
2788  if ( !xwd->color )
2789  {
2790  pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
2791  }
2792  gslevbg = (unsigned int) ( ( (long) pls->cmap0[0].r +
2793  (long) pls->cmap0[0].g +
2794  (long) pls->cmap0[0].b ) / 3 );
2795 
2796  PLColor_to_XColor( &pls->cmap0[0], &xwd->cmap0[0] );
2797 
2798 //
2799 // Set foreground color.
2800 //
2801 // Used for grayscale output, since otherwise the plots can become nearly
2802 // unreadable (i.e. if colors get mapped onto grayscale values). In this
2803 // case it becomes the grayscale level for all draws, and is taken to be
2804 // black if the background is light, and white if the background is dark.
2805 // Note that white/black allocations never fail.
2806 //
2807 
2808  if ( gslevbg > 0x7F )
2809  gslevfg = 0;
2810  else
2811  gslevfg = 0xFF;
2812 
2813  fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg;
2814 
2815  PLColor_to_XColor( &fgcolor, &xwd->fgcolor );
2816 
2817 // Now store
2818 
2819  if ( xwd->rw_cmap && xwd->color )
2820  {
2821  XStoreColor( xwd->display, xwd->map, &xwd->fgcolor );
2822  XStoreColor( xwd->display, xwd->map, &xwd->cmap0[0] );
2823  }
2824  else
2825  {
2826  XAllocColor( xwd->display, xwd->map, &xwd->cmap0[0] );
2827  XAllocColor( xwd->display, xwd->map, &xwd->fgcolor );
2828  }
2829 }
2830 
2831 //--------------------------------------------------------------------------
2832 // InitColors()
2833 //
2834 // Does all color initialization.
2835 //--------------------------------------------------------------------------
2836 
2837 static void
2838 InitColors( PLStream *pls )
2839 {
2840  XwDev *dev = (XwDev *) pls->dev;
2841  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2842 
2843  dbug_enter( "InitColors" );
2844 
2845 // Allocate and initialize color maps.
2846 // Defer cmap1 allocation until it's actually used
2847 
2848  if ( xwd->color )
2849  {
2850  if ( plplot_ccmap )
2851  {
2852  AllocCustomMap( pls );
2853  }
2854  else
2855  {
2856  AllocCmap0( pls );
2857  }
2858  }
2859 }
2860 
2861 //--------------------------------------------------------------------------
2862 // AllocCustomMap()
2863 //
2864 // Initializes custom color map and all the cruft that goes with it.
2865 //
2866 // Assuming all color X displays do 256 colors, the breakdown is as follows:
2867 //
2868 // CCMAP_XWM_COLORS
2869 // Number of low "pixel" values to copy. These are typically allocated
2870 // first, thus are in use by the window manager. I copy them to reduce
2871 // flicker.
2872 //
2873 //
2874 // RWMAP_CMAP1_COLORS
2875 // Color map 1 entries. There should be as many as practical available
2876 // for smooth shading. On the order of 50-100 is pretty reasonable. You
2877 // don't really need all 256, especially if all you're going to do is to
2878 // print it to postscript (which doesn't have any intrinsic limitation on
2879 // the number of colors).
2880 //
2881 // It's important to leave some extra colors unallocated for Tk. In
2882 // particular the palette tools require a fair amount. I recommend leaving
2883 // at least 40 or so free.
2884 //--------------------------------------------------------------------------
2885 
2886 static void
2887 AllocCustomMap( PLStream *pls )
2888 {
2889  XwDev *dev = (XwDev *) pls->dev;
2890  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2891 
2892  XColor xwm_colors[RWMAP_MAX_COLORS];
2893  int i, npixels;
2894  unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
2895 
2896  dbug_enter( "AllocCustomMap" );
2897 
2898 // Determine current default colors
2899 
2900  for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
2901  {
2902  xwm_colors[i].pixel = (unsigned long int) i;
2903  }
2904  XQueryColors( xwd->display, xwd->map, xwm_colors, RWMAP_MAX_COLORS );
2905 
2906 // Allocate cmap0 colors in the default colormap.
2907 // The custom cmap0 colors are later stored at the same pixel values.
2908 // This is a really cool trick to reduce the flicker when changing colormaps.
2909 //
2910 
2911  AllocCmap0( pls );
2912  XAllocColor( xwd->display, xwd->map, &xwd->fgcolor );
2913 
2914 // Create new color map
2915 
2916  xwd->map = XCreateColormap( xwd->display, DefaultRootWindow( xwd->display ),
2917  xwd->visual, AllocNone );
2918 
2919 // Now allocate all colors so we can fill the ones we want to copy
2920 
2921  npixels = RWMAP_MAX_COLORS;
2922  for (;; )
2923  {
2924  if ( XAllocColorCells( xwd->display, xwd->map, False,
2925  plane_masks, 0, pixels, (unsigned int) npixels ) )
2926  break;
2927  npixels--;
2928  if ( npixels == 0 )
2929  plexit( "couldn't allocate any colors" );
2930  }
2931 
2932 // Fill the low colors since those are in use by the window manager
2933 
2934  for ( i = 0; i < CCMAP_XWM_COLORS; i++ )
2935  {
2936  XStoreColor( xwd->display, xwd->map, &xwm_colors[i] );
2937  pixels[xwm_colors[i].pixel] = 0;
2938  }
2939 
2940 // Fill the ones we will use in cmap0
2941 
2942  for ( i = 0; i < xwd->ncol0; i++ )
2943  {
2944  XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] );
2945  pixels[xwd->cmap0[i].pixel] = 0;
2946  }
2947 
2948 // Finally, if the colormap was saved by an external agent, see if there are
2949 // any differences from the current default map and save those! A very cool
2950 // (or sick, depending on how you look at it) trick to get over some X and
2951 // Tk limitations.
2952 //
2953 
2954  if ( sxwm_colors_set )
2955  {
2956  for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
2957  {
2958  if ( ( xwm_colors[i].red != sxwm_colors[i].red ) ||
2959  ( xwm_colors[i].green != sxwm_colors[i].green ) ||
2960  ( xwm_colors[i].blue != sxwm_colors[i].blue ) )
2961  {
2962  if ( pixels[i] != 0 )
2963  {
2964  XStoreColor( xwd->display, xwd->map, &xwm_colors[i] );
2965  pixels[i] = 0;
2966  }
2967  }
2968  }
2969  }
2970 
2971 // Now free the ones we're not interested in
2972 
2973  for ( i = 0; i < npixels; i++ )
2974  {
2975  if ( pixels[i] != 0 )
2976  XFreeColors( xwd->display, xwd->map, &pixels[i], 1, 0 );
2977  }
2978 
2979 // Allocate colors in cmap 1
2980 
2981  AllocCmap1( pls );
2982 }
2983 
2984 //--------------------------------------------------------------------------
2985 // AllocCmap0()
2986 //
2987 // Allocate & initialize cmap0 entries.
2988 //--------------------------------------------------------------------------
2989 
2990 static void
2991 AllocCmap0( PLStream *pls )
2992 {
2993  XwDev *dev = (XwDev *) pls->dev;
2994  XwDisplay *xwd = (XwDisplay *) dev->xwd;
2995  int i;
2996 
2997  dbug_enter( "AllocCmap0" );
2998 
2999 // Free all previous colors. This should work for both rw & ro colormaps
3000  for ( i = 1; i < xwd->ncol0; i++ )
3001  {
3002  unsigned long pixel = xwd->cmap0[i].pixel;
3003  XFreeColors( xwd->display, xwd->map, &pixel, 1, 0 );
3004  }
3005 
3006 // If the number of colors increased, need to allocate enough space for them
3007  if ( pls->ncol0 > xwd->ncol0_alloc )
3008  {
3009  xwd->ncol0_alloc = pls->ncol0;
3010  xwd->cmap0 = (XColor *)
3011  realloc( xwd->cmap0, (size_t) pls->ncol0 * sizeof ( XColor ) );
3012  if ( xwd->cmap0 == 0 )
3013  plexit( "couldn't allocate space for cmap0 colors" );
3014  }
3015 
3016  if ( xwd->rw_cmap )
3017  {
3018  int npixels;
3019  unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
3020 
3021  // Allocate and assign colors in cmap 0
3022 
3023  npixels = pls->ncol0 - 1;
3024  for (;; )
3025  {
3026  if ( XAllocColorCells( xwd->display, xwd->map, False,
3027  plane_masks, 0, &pixels[1], (unsigned int) npixels ) )
3028  break;
3029  npixels--;
3030  if ( npixels == 0 )
3031  plexit( "couldn't allocate any colors" );
3032  }
3033 
3034  xwd->ncol0 = npixels + 1;
3035  for ( i = 1; i < xwd->ncol0; i++ )
3036  {
3037  xwd->cmap0[i].pixel = pixels[i];
3038  }
3039 
3040  StoreCmap0( pls );
3041  }
3042  else
3043  {
3044  if ( pls->verbose )
3045  fprintf( stderr, "Attempting to allocate r/o colors in cmap0.\n" );
3046 
3047  for ( i = 1; i < pls->ncol0; i++ )
3048  {
3049  int r;
3050  XColor c;
3051  PLColor_to_XColor( &pls->cmap0[i], &c );
3052  r = XAllocColor( xwd->display, xwd->map, &c );
3053  if ( pls->verbose )
3054  fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) c.pixel );
3055  if ( r )
3056  {
3057  xwd->cmap0[i] = c;
3058  xwd->cmap0[i].pixel = c.pixel; // needed for deallocation
3059  }
3060  else
3061  {
3062  XColor screen_def, exact_def;
3063 
3064  if ( pls->verbose )
3065  fprintf( stderr,
3066  "color alloc failed, trying by name: %s.\n",
3067  pls->cmap0[i].name );
3068 
3069  // Hmm, didn't work, try another approach.
3070  r = XAllocNamedColor( xwd->display, xwd->map,
3071  pls->cmap0[i].name,
3072  &screen_def, &exact_def );
3073 
3074 // xwd->cmap0[i] = screen_def;
3075 
3076  if ( r )
3077  {
3078  if ( pls->verbose )
3079  fprintf( stderr, "yes, got a color by name.\n" );
3080 
3081  xwd->cmap0[i] = screen_def;
3082  xwd->cmap0[i].pixel = screen_def.pixel;
3083  }
3084  else
3085  {
3086  r = XAllocNamedColor( xwd->display, xwd->map,
3087  "white",
3088  &screen_def, &exact_def );
3089  if ( r )
3090  {
3091  xwd->cmap0[i] = screen_def;
3092  xwd->cmap0[i].pixel = screen_def.pixel;
3093  }
3094  else
3095  printf( "Can't find white?! Giving up...\n" );
3096  }
3097  }
3098  }
3099  xwd->ncol0 = i;
3100 
3101  if ( pls->verbose )
3102  fprintf( stderr, "Allocated %d colors in cmap0.\n", xwd->ncol0 );
3103  }
3104 }
3105 
3106 //--------------------------------------------------------------------------
3107 // AllocCmap1()
3108 //
3109 // Allocate & initialize cmap1 entries.
3110 //--------------------------------------------------------------------------
3111 
3112 static void
3113 AllocCmap1( PLStream *pls )
3114 {
3115  XwDev *dev = (XwDev *) pls->dev;
3116  XwDisplay *xwd = (XwDisplay *) dev->xwd;
3117 
3118  int i, j, npixels;
3119  unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
3120 
3121  dbug_enter( "AllocCmap1" );
3122 
3123  if ( xwd->rw_cmap )
3124  {
3125  if ( pls->verbose )
3126  fprintf( stderr, "Attempting to allocate r/w colors in cmap1.\n" );
3127 
3128  // If using the default color map, must severely limit number of colors
3129  // otherwise TK won't have enough.
3130 
3131  npixels = MAX( 2, MIN( RWMAP_CMAP1_COLORS, pls->ncol1 ) );
3132  for (;; )
3133  {
3134  if ( XAllocColorCells( xwd->display, xwd->map, False,
3135  plane_masks, 0, pixels, (unsigned int) npixels ) )
3136  break;
3137  npixels--;
3138  if ( npixels == 0 )
3139  break;
3140  }
3141 
3142  if ( npixels < 2 )
3143  {
3144  xwd->ncol1 = -1;
3145  fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1.\n" );
3146  return;
3147  }
3148 
3149  xwd->ncol1 = npixels;
3150  if ( pls->verbose )
3151  fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1.\n", npixels );
3152 
3153  // Allocate space if it hasn't been done yet
3154  if ( !xwd->cmap1 )
3155  {
3156  xwd->ncol1_alloc = xwd->ncol1;
3157  xwd->cmap1 = (XColor *) calloc( (size_t) ( xwd->ncol1 ), sizeof ( XColor ) );
3158  if ( !xwd->cmap1 )
3159  plexit( "couldn't allocate space for cmap1 colors" );
3160  }
3161 
3162  // Don't assign pixels sequentially, to avoid strange problems with xor
3163  // GC's. Skipping by 2 seems to do the job best.
3164 
3165  for ( j = i = 0; i < xwd->ncol1; i++ )
3166  {
3167  while ( pixels[j] == 0 )
3168  j++;
3169 
3170  xwd->cmap1[i].pixel = pixels[j];
3171  pixels[j] = 0;
3172 
3173  j += 2;
3174  if ( j >= xwd->ncol1 )
3175  j = 0;
3176  }
3177 
3178  StoreCmap1( pls );
3179  }
3180  else
3181  {
3182  int r, ncolors;
3183  PLColor cmap1color;
3184  XColor xcol;
3185 
3186  if ( pls->verbose )
3187  fprintf( stderr, "Attempting to allocate r/o colors in cmap1.\n" );
3188 
3189  switch ( xwd->visual->class )
3190  {
3191  case TrueColor:
3192  ncolors = TC_CMAP1_COLORS;
3193  break;
3194  default:
3195  ncolors = ROMAP_CMAP1_COLORS;
3196  }
3197 
3198  // Allocate space if it hasn't been done yet
3199  if ( !xwd->cmap1 )
3200  {
3201  xwd->ncol1_alloc = ncolors;
3202  xwd->cmap1 = (XColor *) calloc( (size_t) ncolors, sizeof ( XColor ) );
3203  if ( !xwd->cmap1 )
3204  plexit( "couldn't allocate space for cmap1 colors" );
3205  }
3206 
3207  for ( i = 0; i < ncolors; i++ )
3208  {
3209  plcol_interp( pls, &cmap1color, i, ncolors );
3210  PLColor_to_XColor( &cmap1color, &xcol );
3211 
3212  r = XAllocColor( xwd->display, xwd->map, &xcol );
3213  if ( pls->verbose )
3214  fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) xcol.pixel );
3215  if ( r )
3216  xwd->cmap1[i] = xcol;
3217  else
3218  break;
3219  }
3220  if ( i < ncolors )
3221  {
3222  xwd->ncol1 = -1;
3223  fprintf( stderr,
3224  "Warning: unable to allocate sufficient colors in cmap1\n" );
3225  return;
3226  }
3227  else
3228  {
3229  xwd->ncol1 = ncolors;
3230  if ( pls->verbose )
3231  fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", ncolors );
3232  }
3233  }
3234 }
3235 
3236 //--------------------------------------------------------------------------
3237 // StoreCmap0()
3238 //
3239 // Stores cmap 0 entries in X-server colormap.
3240 //--------------------------------------------------------------------------
3241 
3242 static void
3243 StoreCmap0( PLStream *pls )
3244 {
3245  XwDev *dev = (XwDev *) pls->dev;
3246  XwDisplay *xwd = (XwDisplay *) dev->xwd;
3247  int i;
3248 
3249  if ( !xwd->color )
3250  return;
3251 
3252  for ( i = 1; i < xwd->ncol0; i++ )
3253  {
3254  PLColor_to_XColor( &pls->cmap0[i], &xwd->cmap0[i] );
3255  if ( xwd->rw_cmap )
3256  XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] );
3257  else
3258  XAllocColor( xwd->display, xwd->map, &xwd->cmap0[i] );
3259  }
3260 }
3261 
3262 //--------------------------------------------------------------------------
3263 // StoreCmap1()
3264 //
3265 // Stores cmap 1 entries in X-server colormap.
3266 //--------------------------------------------------------------------------
3267 
3268 static void
3269 StoreCmap1( PLStream *pls )
3270 {
3271  XwDev *dev = (XwDev *) pls->dev;
3272  XwDisplay *xwd = (XwDisplay *) dev->xwd;
3273 
3274  PLColor cmap1color;
3275  int i;
3276 
3277  if ( !xwd->color )
3278  return;
3279 
3280  for ( i = 0; i < xwd->ncol1; i++ )
3281  {
3282  plcol_interp( pls, &cmap1color, i, xwd->ncol1 );
3283  PLColor_to_XColor( &cmap1color, &xwd->cmap1[i] );
3284  if ( xwd->rw_cmap )
3285  XStoreColor( xwd->display, xwd->map, &xwd->cmap1[i] );
3286  else
3287  XAllocColor( xwd->display, xwd->map, &xwd->cmap1[i] );
3288  }
3289 }
3290 
3291 //--------------------------------------------------------------------------
3292 // PLColor_to_XColor()
3293 //
3294 // Copies the supplied PLColor to an XColor, padding with bits as necessary
3295 // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
3296 // The argument types follow the same order as in the function name.
3297 //--------------------------------------------------------------------------
3298 
3299 #define ToXColor( a ) ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) )
3300 #define ToPLColor( a ) ( ( (U_LONG) a ) >> 8 )
3301 
3302 static void
3303 PLColor_to_XColor( PLColor *plcolor, XColor *xcolor )
3304 {
3305  xcolor->red = (short unsigned) ToXColor( plcolor->r );
3306  xcolor->green = (short unsigned) ToXColor( plcolor->g );
3307  xcolor->blue = (short unsigned) ToXColor( plcolor->b );
3308  xcolor->flags = DoRed | DoGreen | DoBlue;
3309 }
3310 
3311 //--------------------------------------------------------------------------
3312 // PLColor_from_XColor()
3313 //
3314 // Copies the supplied XColor to a PLColor, stripping off bits as
3315 // necessary. See the previous routine for more info.
3316 //--------------------------------------------------------------------------
3317 
3318 static void
3319 PLColor_from_XColor( PLColor *plcolor, XColor *xcolor )
3320 {
3321  plcolor->r = (unsigned char) ToPLColor( xcolor->red );
3322  plcolor->g = (unsigned char) ToPLColor( xcolor->green );
3323  plcolor->b = (unsigned char) ToPLColor( xcolor->blue );
3324 }
3325 
3326 //--------------------------------------------------------------------------
3327 // AreWeGrayscale(Display *display)
3328 //
3329 // Determines if we're using a monochrome or grayscale device.
3330 // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland.
3331 // Altered Andrew Ross 26-01-2004 to fix memory leak.
3332 //--------------------------------------------------------------------------
3333 
3334 static int
3335 AreWeGrayscale( Display *display )
3336 {
3337 #if defined ( __cplusplus ) || defined ( c_plusplus )
3338 #define THING c_class
3339 #else
3340 #define THING class
3341 #endif
3342 
3343  XVisualInfo *visuals;
3344  int nitems, i, igray;
3345 
3346  // get a list of info on the visuals available
3347  visuals = XGetVisualInfo( display, 0, NULL, &nitems );
3348 
3349  igray = 1;
3350  // check the list looking for non-monochrome visual classes
3351  for ( i = 0; i < nitems; i++ )
3352  if ( ( visuals[i].THING != GrayScale ) &&
3353  ( visuals[i].THING != StaticGray ) )
3354  {
3355  igray = 0;
3356  break;
3357  }
3358 
3359  XFree( visuals );
3360  // if igray = 1 only StaticGray and GrayScale classes available
3361  return igray;
3362 }
3363 
3364 #ifdef DUMMY
3365 //--------------------------------------------------------------------------
3366 // SaveColormap() **** DUMMY, NOT USED ANYMORE ***
3367 //
3368 // Saves RGB components of given colormap.
3369 // Used in an ugly hack to get past some X11R5 and TK limitations.
3370 // This isn't guaranteed to work under all circumstances, but hopefully
3371 // in the future there will be a nicer way to accomplish the same thing.
3372 //
3373 // Note: I tried using XCopyColormapAndFree to do the same thing, but under
3374 // HPUX 9.01/VUE/X11R5 at least it doesn't preserve the previous read-only
3375 // color cell allocations made by Tk. Is this a bug? Have to look at the
3376 // source to find out.
3377 //--------------------------------------------------------------------------
3378 
3379 static void
3380 SaveColormap( Display *display, Colormap colormap )
3381 {
3382  int i;
3383 
3384  if ( !plplot_ccmap )
3385  return;
3386 
3387  sxwm_colors_set = 1;
3388  for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
3389  {
3390  sxwm_colors[i].pixel = i;
3391  }
3392  XQueryColors( display, colormap, sxwm_colors, RWMAP_MAX_COLORS );
3393 //
3394 // printf("\nAt startup, default colors are: \n\n");
3395 // for (i = 0; i < RWMAP_MAX_COLORS; i++) {
3396 // printf(" i: %d, pixel: %d, r: %d, g: %d, b: %d\n",
3397 // i, sxwm_colors[i].pixel,
3398 // sxwm_colors[i].red, sxwm_colors[i].green, sxwm_colors[i].blue);
3399 // }
3400 //
3401 }
3402 #endif
3403 
3404 //--------------------------------------------------------------------------
3405 // GetImageErrorHandler()
3406 //
3407 // Error handler used in XGetImage() to catch errors when pixmap or window
3408 // are not completely viewable.
3409 //--------------------------------------------------------------------------
3410 
3411 static int
3412 GetImageErrorHandler( Display *display, XErrorEvent *error )
3413 {
3414  if ( error->error_code != BadMatch )
3415  {
3416  char buffer[256];
3417  XGetErrorText( display, error->error_code, buffer, 256 );
3418  fprintf( stderr, "xwin: Error in XGetImage: %s.\n", buffer );
3419  }
3420  return 1;
3421 }
3422 
3423 //--------------------------------------------------------------------------
3424 // DrawImage()
3425 //
3426 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
3427 // Only solid color fill supported.
3428 //--------------------------------------------------------------------------
3429 
3430 static void
3431 DrawImage( PLStream *pls )
3432 {
3433  XwDev *dev = (XwDev *) pls->dev;
3434  XwDisplay *xwd = (XwDisplay *) dev->xwd;
3435  XImage *ximg = NULL;
3436  XColor curcolor;
3437  PLINT xmin, xmax, ymin, ymax, icol1;
3438 
3439  int ( *oldErrorHandler )( Display *, XErrorEvent * );
3440 
3441  float mlr, mtb;
3442  float blt, brt, brb, blb;
3443  float left, right;
3444  int kx, ky;
3445  int nx, ny, ix, iy;
3446  int i, corners[4], r[4] = { 0, 0, 0, 0 };
3447 
3448  struct
3449  {
3450  float x, y;
3451  } Ppts[4];
3452 
3453  CheckForEvents( pls );
3454 
3455  xmin = (PLINT) ( dev->xscale * pls->imclxmin );
3456  xmax = (PLINT) ( dev->xscale * pls->imclxmax );
3457  ymin = (PLINT) ( dev->yscale * pls->imclymin );
3458  ymax = (PLINT) ( dev->yscale * pls->imclymax );
3459 
3460  nx = pls->dev_nptsX;
3461  ny = pls->dev_nptsY;
3462 
3463 // XGetImage() call fails if either the pixmap or window is not fully viewable!
3464  oldErrorHandler = XSetErrorHandler( GetImageErrorHandler );
3465 
3466  XFlush( xwd->display );
3467  if ( dev->write_to_pixmap )
3468  ximg = XGetImage( xwd->display, dev->pixmap, 0, 0, dev->width, dev->height,
3469  AllPlanes, ZPixmap );
3470 
3471  if ( dev->write_to_window )
3472  ximg = XGetImage( xwd->display, dev->window, 0, 0, dev->width, dev->height,
3473  AllPlanes, ZPixmap );
3474 
3475  XSetErrorHandler( oldErrorHandler );
3476 
3477  if ( ximg == NULL )
3478  {
3479  plabort( "Can't get image, the window must be partly off-screen, move it to fit screen" );
3480  return;
3481  }
3482 
3483  if ( xwd->ncol1 == 0 )
3484  AllocCmap1( pls );
3485  if ( xwd->ncol1 < 2 )
3486  return;
3487 
3488 // translate array for rotation
3489  switch ( (int) ( pls->diorot - 4. * floor( pls->diorot / 4. ) ) )
3490  {
3491  case 0:
3492  r[0] = 0; r[1] = 1; r[2] = 2; r[3] = 3; break;
3493  case 1:
3494  r[0] = 1; r[1] = 2; r[2] = 3; r[3] = 0; break;
3495  case 2:
3496  r[0] = 2; r[1] = 3; r[2] = 0; r[3] = 1; break;
3497  case 3:
3498  r[0] = 3; r[1] = 0; r[2] = 1; r[3] = 2;
3499  }
3500 
3501  // after rotation and coordinate translation, each fill
3502  // lozangue will have coordinates (Ppts), slopes (m...)
3503  // and y intercepts (b...):
3504  //
3505  // Ppts[3]
3506  // **
3507  // mlr,blt * * mtb,brt
3508  // * *
3509  //Ppts[0]< > Ppts[2]
3510  // * *
3511  // mtb,blt * * mlr,brb
3512  // **
3513  // Ppts[1]
3514  //
3515 
3516 // slope of left/right and top/bottom edges
3517  mlr = (float) ( ( dev->yscale * ( pls->dev_iy[1] - pls->dev_iy[0] ) ) /
3518  ( dev->xscale * ( pls->dev_ix[1] - pls->dev_ix[0] ) ) );
3519 
3520  mtb = (float) ( ( dev->yscale * ( pls->dev_iy[ny] - pls->dev_iy[0] ) ) /
3521  ( dev->xscale * ( pls->dev_ix[ny] - pls->dev_ix[0] ) ) );
3522 
3523  for ( ix = 0; ix < nx - 1; ix++ )
3524  {
3525  for ( iy = 0; iy < ny - 1; iy++ )
3526  {
3527  corners[0] = ix * ny + iy; // [ix][iy]
3528  corners[1] = ( ix + 1 ) * ny + iy; // [ix+1][iy]
3529  corners[2] = ( ix + 1 ) * ny + iy + 1; // [ix+1][iy+1]
3530  corners[3] = ix * ny + iy + 1; // [ix][iy+1]
3531 
3532  for ( i = 0; i < 4; i++ )
3533  {
3534  Ppts[i].x = (float) ( dev->xscale * ( pls->dev_ix[corners[r[i]]] ) );
3535  Ppts[i].y = (float) ( dev->yscale * ( pls->dev_iy[corners[r[i]]] ) );
3536  }
3537 
3538  // if any corner is inside the draw area
3539  if ( Ppts[0].x >= xmin || Ppts[2].x <= xmax ||
3540  Ppts[1].y >= ymin || Ppts[3].y <= ymax )
3541  {
3542  Ppts[0].x = MAX( Ppts[0].x, (float) xmin );
3543  Ppts[2].x = MIN( Ppts[2].x, (float) xmax );
3544  Ppts[1].y = MAX( Ppts[1].y, (float) ymin );
3545  Ppts[3].y = MIN( Ppts[3].y, (float) ymax );
3546 
3547  // the Z array has size (nx-1)*(ny-1)
3548  icol1 = pls->dev_z[ix * ( ny - 1 ) + iy];
3549 
3550  // only plot points within zmin/zmax range
3551  if ( icol1 < pls->dev_zmin || icol1 > pls->dev_zmax )
3552  continue;
3553 
3554  icol1 = (PLINT) ( (float) icol1 / (float) USHRT_MAX * (float) ( xwd->ncol1 - 1 ) );
3555  if ( xwd->color )
3556  curcolor = xwd->cmap1[icol1];
3557  else
3558  curcolor = xwd->fgcolor;
3559 
3560  // Fill square between current and next points.
3561 
3562  // If the fill area is a single dot, accelerate the fill.
3563  if ( ( fabs( Ppts[2].x - Ppts[0].x ) == 1 ) &&
3564  ( fabs( Ppts[3].y - Ppts[1].y ) == 1 ) )
3565  {
3566  XPutPixel( ximg, (int) Ppts[0].x, (int) dev->height - 1 - (int) Ppts[0].y, (unsigned long) curcolor.pixel );
3567 
3568  // integer rotate, accelerate
3569  }
3570  else if ( pls->diorot == floor( pls->diorot ) )
3571  {
3572  for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ )
3573  for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ )
3574  XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel );
3575 
3576  // lozangue, scanline fill it
3577  }
3578  else
3579  {
3580  // y interception point of left/right top/bottom edges
3581  blt = Ppts[0].y - mlr * Ppts[0].x;
3582  brb = Ppts[2].y - mlr * Ppts[2].x;
3583 
3584  brt = Ppts[2].y - mtb * Ppts[2].x;
3585  blb = Ppts[0].y - mtb * Ppts[0].x;
3586 
3587  for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ )
3588  {
3589  left = MAX( ( ( (float) ky - blt ) / mlr ), ( ( (float) ky - blb ) / mtb ) );
3590  right = MIN( ( ( (float) ky - brt ) / mtb ), ( ( (float) ky - brb ) / mlr ) );
3591  for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ )
3592  {
3593  if ( kx >= rint( left ) && kx <= rint( right ) )
3594  {
3595  XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel );
3596  }
3597  }
3598  }
3599  }
3600  }
3601  }
3602  }
3603 
3604  if ( dev->write_to_pixmap )
3605  XPutImage( xwd->display, dev->pixmap, dev->gc, ximg, 0, 0, 0, 0, dev->width, dev->height );
3606 
3607  if ( dev->write_to_window )
3608  XPutImage( xwd->display, dev->window, dev->gc, ximg, 0, 0,
3609  0, 0, dev->width, dev->height );
3610 
3611  XDestroyImage( ximg );
3612 }
3613 
3614 static void
3615 imageops( PLStream *pls, PLINT *ptr )
3616 {
3617  XwDev *dev = (XwDev *) pls->dev;
3618  XwDisplay *xwd = (XwDisplay *) dev->xwd;
3619 
3620 // TODO: store/revert to/from previous state
3621 
3622  switch ( *ptr )
3623  {
3624  case ZEROW2D:
3625  dev->write_to_window = 0;
3626  break;
3627 
3628  case ONEW2D:
3629  dev->write_to_window = 1;
3630  break;
3631 
3632  case ZEROW2B:
3633  dev->write_to_pixmap = 0;
3634  break;
3635 
3636  case ONEW2B:
3637  XFlush( xwd->display );
3638  dev->write_to_pixmap = 1;
3639  break;
3640  }
3641 }
3642 
3643 #else
3644 int
3646 {
3647  return 0;
3648 }
3649 
3650 #endif // PLD_xwin