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