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