PLplot: xwin.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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