PLplot  5.10.0
xwin.c
Go to the documentation of this file.
00001 //      PLplot X-windows device driver.
00002 //
00003 // Copyright (C) 2004  Maurice LeBrun
00004 // Copyright (C) 2004  Joao Cardoso
00005 // Copyright (C) 2004  Rafael Laboissiere
00006 // Copyright (C) 2004  Andrew Ross
00007 //
00008 // This file is part of PLplot.
00009 //
00010 // PLplot is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Library General Public License as published
00012 // by the Free Software Foundation; either version 2 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // PLplot is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Library General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Library General Public License
00021 // along with PLplot; if not, write to the Free Software
00022 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00023 //
00024 //
00025 #include "plDevs.h"
00026 
00027 #define DEBUG
00028 
00029 #ifdef PLD_xwin
00030 #define NEED_PLDEBUG
00031 #include "plplotP.h"
00032 #include "plxwd.h"
00033 #include "drivers.h"
00034 #include "plevent.h"
00035 
00036 #ifdef PL_HAVE_PTHREAD
00037 #include <pthread.h>
00038 #include <signal.h>
00039 int    pthread_mutexattr_settype( pthread_mutexattr_t *attr, int kind );
00040 static void events_thread( void *pls );
00041 static pthread_mutex_t events_mutex;
00042 static int             already = 0;
00043 #endif
00044 
00045 // Device info
00046 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_xwin = "xwin:X-Window (Xlib):1:xwin:5:xw\n";
00047 
00048 static int synchronize = 0;                   // change to 1 for X synchronized operation
00049                                               // Use "-drvopt sync" cmd line option to set.
00050 
00051 static int nobuffered = 0;                    // make it a buffered device by default
00052                                               // use "-drvopt unbuffered" to make it unbuffered
00053 
00054 static int noinitcolors = 0;                  // Call InitColors by default
00055                                               // use "-drvopt noinitcolors" to leave colors uninitialized
00056 
00057 static int defaultvisual = 1;                 // use "-drvopt defvis=0" to not use the default visual
00058 
00059 static int usepthreads = 1;                   // use "-drvopt usepth=0" to not use pthreads to redisplay
00060 
00061 //
00062 // When -drvopt defvis is defined, DefaultVisual() is used to get the visual.
00063 // Otherwise, the visual is obtained using XGetVisualInfo() to make a match.
00064 //
00065 
00066 //#define HACK_STATICCOLOR
00067 
00068 // Number of instructions to skip between querying the X server for events
00069 
00070 #define MAX_INSTR    20
00071 
00072 // The xwin driver uses the xscale and yscale values to convert from virtual
00073 // to real pixels using the current size in pixels of the display window.
00074 // We define PHYSICAL here so that PLplot core knows about this rescaling
00075 // and mm values are converted to virtual pixels at a ratio consistent with
00076 // a constant ratio of DPMM real pixels per mm.
00077 #define PHYSICAL    1
00078 
00079 // These need to be distinguished since the handling is slightly different.
00080 
00081 #define LOCATE_INVOKED_VIA_API       1
00082 #define LOCATE_INVOKED_VIA_DRIVER    2
00083 
00084 // Set constants for dealing with colormap.  In brief:
00085 //
00086 //      --- custom colormaps ---
00087 // ccmap                When set, turns on custom color map
00088 // CCMAP_XWM_COLORS     Number of low "pixel" values to copy.
00089 //
00090 //      --- r/w colormaps --
00091 // RWMAP_CMAP1_COLORS   Color map 1 entries.
00092 // RWMAP_MAX_COLORS     Maximum colors in RW colormaps
00093 //
00094 //      --- r/o colormaps --
00095 // ROMAP_CMAP1_COLORS   Color map 1 entries.
00096 // TC_CMAP1_COLORS      TrueColor visual Color map 1 entries.
00097 //
00098 // See Init_CustomCmap() and  Init_DefaultCmap() for more info.
00099 // Set ccmap at your own risk -- still under development.
00100 //
00101 
00102 // plplot_ccmap is statically defined in plxwd.h.  Note that
00103 // plframe.c also includes that header and uses the variable.
00104 
00105 #define CCMAP_XWM_COLORS      70
00106 
00107 #define RWMAP_CMAP1_COLORS    50
00108 #define RWMAP_MAX_COLORS      256
00109 
00110 #define ROMAP_CMAP1_COLORS    50
00111 #define TC_CMAP1_COLORS       200
00112 
00113 // Variables to hold RGB components of given colormap.
00114 // Used in an ugly hack to get past some X11R5 and TK limitations.
00115 
00116 static int    sxwm_colors_set;
00117 static XColor sxwm_colors[RWMAP_MAX_COLORS];
00118 
00119 // Keep pointers to all the displays in use
00120 
00121 static XwDisplay *xwDisplay[PLXDISPLAYS];
00122 
00123 // Function prototypes
00124 
00125 // Driver entry and dispatch setup
00126 
00127 void plD_dispatch_init_xw( PLDispatchTable *pdt );
00128 
00129 void plD_init_xw( PLStream * );
00130 void plD_line_xw( PLStream *, short, short, short, short );
00131 void plD_polyline_xw( PLStream *, short *, short *, PLINT );
00132 void plD_eop_xw( PLStream * );
00133 void plD_bop_xw( PLStream * );
00134 void plD_tidy_xw( PLStream * );
00135 void plD_state_xw( PLStream *, PLINT );
00136 void plD_esc_xw( PLStream *, PLINT, void * );
00137 
00138 // Initialization
00139 
00140 static void  OpenXwin( PLStream *pls );
00141 static void  Init( PLStream *pls );
00142 static void  InitMain( PLStream *pls );
00143 static void  InitColors( PLStream *pls );
00144 static void  AllocCustomMap( PLStream *pls );
00145 static void  AllocCmap0( PLStream *pls );
00146 static void  AllocCmap1( PLStream *pls );
00147 static void  MapMain( PLStream *pls );
00148 static void  CreatePixmap( PLStream *pls );
00149 static void  GetVisual( PLStream *pls );
00150 static void  AllocBGFG( PLStream *pls );
00151 static int   AreWeGrayscale( Display *display );
00152 
00153 // Routines to poll the X-server
00154 
00155 static void  WaitForPage( PLStream *pls );
00156 static void  CheckForEvents( PLStream *pls );
00157 static void  HandleEvents( PLStream *pls );
00158 
00159 // Event handlers
00160 
00161 static void  MasterEH( PLStream *pls, XEvent *event );
00162 static void  ClientEH( PLStream *pls, XEvent *event );
00163 static void  ExposeEH( PLStream *pls, XEvent *event );
00164 static void  ResizeEH( PLStream *pls, XEvent *event );
00165 static void  MotionEH( PLStream *pls, XEvent *event );
00166 static void  EnterEH( PLStream *pls, XEvent *event );
00167 static void  LeaveEH( PLStream *pls, XEvent *event );
00168 static void  KeyEH( PLStream *pls, XEvent *event );
00169 static void  ButtonEH( PLStream *pls, XEvent *event );
00170 static void  LookupXKeyEvent( PLStream *pls, XEvent *event );
00171 static void  LookupXButtonEvent( PLStream *pls, XEvent *event );
00172 
00173 static void  ProcessKey( PLStream *pls );
00174 static void  LocateKey( PLStream *pls );
00175 static void  ProcessButton( PLStream *pls );
00176 static void  LocateButton( PLStream *pls );
00177 static void  Locate( PLStream *pls );
00178 
00179 // Routines for manipulating graphic crosshairs
00180 
00181 static void  CreateXhairs( PLStream *pls );
00182 static void  DestroyXhairs( PLStream *pls );
00183 static void  DrawXhairs( PLStream *pls, int x0, int y0 );
00184 static void  UpdateXhairs( PLStream *pls );
00185 
00186 // Escape function commands
00187 
00188 static void  ExposeCmd( PLStream *pls, PLDisplay *ptr );
00189 static void  RedrawCmd( PLStream *pls );
00190 static void  ResizeCmd( PLStream *pls, PLDisplay *ptr );
00191 static void  ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr );
00192 static void  GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr );
00193 static void  FillPolygonCmd( PLStream *pls );
00194 static void  XorMod( PLStream *pls, PLINT *mod );
00195 static void  DrawImage( PLStream *pls );
00196 
00197 // Miscellaneous
00198 
00199 static void  StoreCmap0( PLStream *pls );
00200 static void  StoreCmap1( PLStream *pls );
00201 static void  imageops( PLStream *pls, PLINT *ptr );
00202 static void  SetBGFG( PLStream *pls );
00203 #ifdef DUMMY
00204 static void  SaveColormap( Display *display, Colormap colormap );
00205 #endif
00206 static void  PLColor_to_XColor( PLColor *plcolor, XColor *xcolor );
00207 static void  PLColor_from_XColor( PLColor *plcolor, XColor *xcolor );
00208 
00209 static DrvOpt xwin_options[] = { { "sync",         DRV_INT, &synchronize,   "Synchronized X server operation (0|1)" },
00210                                  { "nobuffered",   DRV_INT, &nobuffered,    "Sets unbuffered operation (0|1)"       },
00211                                  { "noinitcolors", DRV_INT, &noinitcolors,  "Sets cmap0 allocation (0|1)"           },
00212                                  { "defvis",       DRV_INT, &defaultvisual, "Use the Default Visual (0|1)"          },
00213                                  { "usepth",       DRV_INT, &usepthreads,   "Use pthreads (0|1)"                    },
00214                                  { NULL,           DRV_INT, NULL,           NULL                                    } };
00215 
00216 void plD_dispatch_init_xw( PLDispatchTable *pdt )
00217 {
00218 #ifndef ENABLE_DYNDRIVERS
00219     pdt->pl_MenuStr = "X-Window (Xlib)";
00220     pdt->pl_DevName = "xwin";
00221 #endif
00222     pdt->pl_type     = plDevType_Interactive;
00223     pdt->pl_seq      = 5;
00224     pdt->pl_init     = (plD_init_fp) plD_init_xw;
00225     pdt->pl_line     = (plD_line_fp) plD_line_xw;
00226     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_xw;
00227     pdt->pl_eop      = (plD_eop_fp) plD_eop_xw;
00228     pdt->pl_bop      = (plD_bop_fp) plD_bop_xw;
00229     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_xw;
00230     pdt->pl_state    = (plD_state_fp) plD_state_xw;
00231     pdt->pl_esc      = (plD_esc_fp) plD_esc_xw;
00232 }
00233 
00234 //--------------------------------------------------------------------------
00235 // plD_init_xw()
00236 //
00237 // Initialize device.
00238 // X-dependent stuff done in OpenXwin() and Init().
00239 //--------------------------------------------------------------------------
00240 
00241 void
00242 plD_init_xw( PLStream *pls )
00243 {
00244     XwDev *dev;
00245     PLFLT pxlx, pxly;
00246     int   xmin = 0;
00247     int   xmax = PIXELS_X - 1;
00248     int   ymin = 0;
00249     int   ymax = PIXELS_Y - 1;
00250 
00251     dbug_enter( "plD_init_xw" );
00252 
00253     pls->termin      = 1;       // Is an interactive terminal
00254     pls->dev_flush   = 1;       // Handle our own flushes
00255     pls->dev_fill0   = 1;       // Handle solid fills
00256     pls->plbuf_write = 1;       // Activate plot buffer
00257     pls->dev_fastimg = 1;       // is a fast image device
00258     pls->dev_xor     = 1;       // device support xor mode
00259 
00260 #ifndef PL_HAVE_PTHREAD
00261     usepthreads = 0;
00262 #endif
00263 
00264     plParseDrvOpts( xwin_options );
00265 
00266 #ifndef PL_HAVE_PTHREAD
00267     if ( usepthreads )
00268         plwarn( "You said you want pthreads, but they are not available." );
00269 #endif
00270 
00271     if ( nobuffered )
00272         pls->plbuf_write = 0;   // deactivate plot buffer
00273 
00274 // The real meat of the initialization done here
00275 
00276     if ( pls->dev == NULL )
00277         OpenXwin( pls );
00278 
00279     dev = (XwDev *) pls->dev;
00280 
00281     Init( pls );
00282 
00283 // Get ready for plotting
00284 
00285     dev->xlen = (short) ( xmax - xmin );
00286     dev->ylen = (short) ( ymax - ymin );
00287 
00288     dev->xscale_init = (double) dev->init_width / (double) dev->xlen;
00289     dev->yscale_init = (double) dev->init_height / (double) dev->ylen;
00290 
00291     dev->xscale = dev->xscale_init;
00292     dev->yscale = dev->yscale_init;
00293 
00294 #if PHYSICAL
00295     pxlx = DPMM / dev->xscale;
00296     pxly = DPMM / dev->yscale;
00297 #else
00298     pxlx = (double) PIXELS_X / LPAGE_X;
00299     pxly = (double) PIXELS_Y / LPAGE_Y;
00300 #endif
00301 
00302     plP_setpxl( pxlx, pxly );
00303     plP_setphy( xmin, xmax, ymin, ymax );
00304 
00305 #ifdef PL_HAVE_PTHREAD
00306     if ( usepthreads )
00307     {
00308         pthread_mutexattr_t mutexatt;
00309         pthread_attr_t      pthattr;
00310 
00311         if ( !already )
00312         {
00313             pthread_mutexattr_init( &mutexatt );
00314             if ( pthread_mutexattr_settype( &mutexatt, PLPLOT_MUTEX_RECURSIVE ) )
00315                 plexit( "xwin: pthread_mutexattr_settype() failed!\n" );
00316 
00317             pthread_mutex_init( &events_mutex, &mutexatt );
00318             already = 1;
00319         }
00320         else
00321         {
00322             pthread_mutex_lock( &events_mutex );
00323             already++;
00324             pthread_mutex_unlock( &events_mutex );
00325         }
00326 
00327         pthread_attr_init( &pthattr );
00328         pthread_attr_setdetachstate( &pthattr, PTHREAD_CREATE_JOINABLE );
00329 
00330         if ( pthread_create( &( dev->updater ), &pthattr, ( void *( * )( void * ) ) & events_thread, (void *) pls ) )
00331         {
00332             pthread_mutex_lock( &events_mutex );
00333             already--;
00334             pthread_mutex_unlock( &events_mutex );
00335 
00336             if ( already == 0 )
00337             {
00338                 pthread_mutex_destroy( &events_mutex );
00339                 plexit( "xwin: pthread_create() failed!\n" );
00340             }
00341             else
00342                 plwarn( "xwin: couldn't create thread for this plot window!\n" );
00343         }
00344     }
00345 #endif
00346 }
00347 
00348 //--------------------------------------------------------------------------
00349 // plD_line_xw()
00350 //
00351 // Draw a line in the current color from (x1,y1) to (x2,y2).
00352 //--------------------------------------------------------------------------
00353 
00354 void
00355 plD_line_xw( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
00356 {
00357     XwDev     *dev = (XwDev *) pls->dev;
00358     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00359 
00360     int       x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
00361 
00362     dbug_enter( "plD_line_xw" );
00363 
00364 #ifdef PL_HAVE_PTHREAD
00365     if ( usepthreads )
00366         pthread_mutex_lock( &events_mutex );
00367 #endif
00368 
00369     CheckForEvents( pls );
00370 
00371     y1 = dev->ylen - y1;
00372     y2 = dev->ylen - y2;
00373 
00374     x1 = (int) ( x1 * dev->xscale );
00375     x2 = (int) ( x2 * dev->xscale );
00376     y1 = (int) ( y1 * dev->yscale );
00377     y2 = (int) ( y2 * dev->yscale );
00378 
00379     if ( dev->write_to_window )
00380         XDrawLine( xwd->display, dev->window, dev->gc, x1, y1, x2, y2 );
00381 
00382     if ( dev->write_to_pixmap )
00383         XDrawLine( xwd->display, dev->pixmap, dev->gc, x1, y1, x2, y2 );
00384 
00385 #ifdef PL_HAVE_PTHREAD
00386     if ( usepthreads )
00387         pthread_mutex_unlock( &events_mutex );
00388 #endif
00389 }
00390 
00391 //--------------------------------------------------------------------------
00392 // XorMod()
00393 //
00394 // Enter xor mode ( mod != 0) or leave it ( mode = 0)
00395 //--------------------------------------------------------------------------
00396 
00397 static void
00398 XorMod( PLStream *pls, PLINT *mod )
00399 {
00400     XwDev     *dev = (XwDev *) pls->dev;
00401     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00402 
00403     if ( *mod == 0 )
00404         XSetFunction( xwd->display, dev->gc, GXcopy );
00405     else
00406         XSetFunction( xwd->display, dev->gc, GXxor );
00407 }
00408 
00409 //--------------------------------------------------------------------------
00410 // plD_polyline_xw()
00411 //
00412 // Draw a polyline in the current color from (x1,y1) to (x2,y2).
00413 //--------------------------------------------------------------------------
00414 
00415 void
00416 plD_polyline_xw( PLStream *pls, short *xa, short *ya, PLINT npts )
00417 {
00418     XwDev     *dev = (XwDev *) pls->dev;
00419     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00420 
00421     PLINT     i;
00422     XPoint    _pts[PL_MAXPOLY];
00423     XPoint    *pts;
00424 
00425     if ( npts > PL_MAXPOLY )
00426     {
00427         pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) npts );
00428     }
00429     else
00430     {
00431         pts = _pts;
00432     }
00433 
00434     dbug_enter( "plD_polyline_xw" );
00435 
00436 #ifdef PL_HAVE_PTHREAD
00437     if ( usepthreads )
00438         pthread_mutex_lock( &events_mutex );
00439 #endif
00440 
00441     CheckForEvents( pls );
00442 
00443     for ( i = 0; i < npts; i++ )
00444     {
00445         pts[i].x = (short) ( dev->xscale * xa[i] );
00446         pts[i].y = (short) ( dev->yscale * ( dev->ylen - ya[i] ) );
00447     }
00448 
00449     if ( dev->write_to_window )
00450         XDrawLines( xwd->display, dev->window, dev->gc, pts, npts,
00451             CoordModeOrigin );
00452 
00453     if ( dev->write_to_pixmap )
00454         XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, npts,
00455             CoordModeOrigin );
00456 
00457 #ifdef PL_HAVE_PTHREAD
00458     if ( usepthreads )
00459         pthread_mutex_unlock( &events_mutex );
00460 #endif
00461 
00462     if ( npts > PL_MAXPOLY )
00463     {
00464         free( pts );
00465     }
00466 }
00467 
00468 //--------------------------------------------------------------------------
00469 // plD_eop_xw()
00470 //
00471 // End of page.  User must hit return (or third mouse button) to continue.
00472 //--------------------------------------------------------------------------
00473 
00474 void
00475 plD_eop_xw( PLStream *pls )
00476 {
00477     XwDev     *dev = (XwDev *) pls->dev;
00478     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00479 
00480     dbug_enter( "plD_eop_xw" );
00481 
00482 #ifdef PL_HAVE_PTHREAD
00483     if ( usepthreads )
00484         pthread_mutex_lock( &events_mutex );
00485 #endif
00486 
00487     XFlush( xwd->display );
00488     if ( pls->db )
00489         ExposeCmd( pls, NULL );
00490 
00491     if ( dev->is_main && !pls->nopause )
00492         WaitForPage( pls );
00493 
00494 #ifdef PL_HAVE_PTHREAD
00495     if ( usepthreads )
00496         pthread_mutex_unlock( &events_mutex );
00497 #endif
00498 }
00499 
00500 //--------------------------------------------------------------------------
00501 // plD_bop_xw()
00502 //
00503 // Set up for the next page.
00504 //--------------------------------------------------------------------------
00505 
00506 void
00507 plD_bop_xw( PLStream *pls )
00508 {
00509     XwDev     *dev = (XwDev *) pls->dev;
00510     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00511 
00512     dbug_enter( "plD_bop_xw" );
00513 
00514 #ifdef PL_HAVE_PTHREAD
00515     if ( usepthreads )
00516         pthread_mutex_lock( &events_mutex );
00517 #endif
00518 
00519     dev->bgcolor = xwd->cmap0[0];
00520 
00521     if ( dev->write_to_window )
00522     {
00523         XSetWindowBackground( xwd->display, dev->window, dev->bgcolor.pixel );
00524         XSetBackground( xwd->display, dev->gc, dev->bgcolor.pixel );
00525         XClearWindow( xwd->display, dev->window );
00526     }
00527     if ( dev->write_to_pixmap )
00528     {
00529         XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
00530         XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
00531             dev->width, dev->height );
00532         XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
00533     }
00534     XSync( xwd->display, 0 );
00535     pls->page++;
00536 
00537 #ifdef PL_HAVE_PTHREAD
00538     if ( usepthreads )
00539         pthread_mutex_unlock( &events_mutex );
00540 #endif
00541 }
00542 
00543 //--------------------------------------------------------------------------
00544 // plD_tidy_xw()
00545 //
00546 // Close graphics file
00547 //--------------------------------------------------------------------------
00548 
00549 void
00550 plD_tidy_xw( PLStream *pls )
00551 {
00552     XwDev     *dev = (XwDev *) pls->dev;
00553     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00554 
00555     dbug_enter( "plD_tidy_xw" );
00556 
00557 #ifdef PL_HAVE_PTHREAD
00558     if ( usepthreads )
00559     {
00560         pthread_mutex_lock( &events_mutex );
00561         if ( pthread_cancel( dev->updater ) == 0 )
00562             pthread_join( dev->updater, NULL );
00563 
00564         pthread_mutex_unlock( &events_mutex );
00565         if ( --already == 0 )
00566             pthread_mutex_destroy( &events_mutex );
00567     }
00568 #endif
00569 
00570     if ( dev->is_main )
00571     {
00572         XDestroyWindow( xwd->display, dev->window );
00573         if ( dev->write_to_pixmap )
00574             XFreePixmap( xwd->display, dev->pixmap );
00575         XFlush( xwd->display );
00576     }
00577 
00578     xwd->nstreams--;
00579     if ( xwd->nstreams == 0 )
00580     {
00581         int ixwd = xwd->ixwd;
00582         XFreeGC( xwd->display, dev->gc );
00583         XFreeGC( xwd->display, xwd->gcXor );
00584         XCloseDisplay( xwd->display );
00585         free_mem( xwd->cmap0 );
00586         free_mem( xwd->cmap1 );
00587         free_mem( xwDisplay[ixwd] );
00588     }
00589     // ANR: if we set this here the tmp file will not be closed
00590     // See also comment in tkwin.c
00591     //pls->plbuf_write = 0;
00592 }
00593 
00594 //--------------------------------------------------------------------------
00595 // plD_state_xw()
00596 //
00597 // Handle change in PLStream state (color, pen width, fill attribute, etc).
00598 //--------------------------------------------------------------------------
00599 
00600 void
00601 plD_state_xw( PLStream *pls, PLINT op )
00602 {
00603     XwDev     *dev = (XwDev *) pls->dev;
00604     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00605 
00606     dbug_enter( "plD_state_xw" );
00607 
00608 #ifdef PL_HAVE_PTHREAD
00609     if ( usepthreads )
00610         pthread_mutex_lock( &events_mutex );
00611 #endif
00612 
00613     CheckForEvents( pls );
00614 
00615     switch ( op )
00616     {
00617     case PLSTATE_WIDTH:
00618         XSetLineAttributes( xwd->display, dev->gc, (unsigned int) pls->width,
00619             LineSolid, CapRound, JoinMiter );
00620         break;
00621 
00622     case PLSTATE_COLOR0: {
00623         int icol0 = pls->icol0;
00624         if ( xwd->color )
00625         {
00626             if ( icol0 == PL_RGB_COLOR )
00627             {
00628                 PLColor_to_XColor( &pls->curcolor, &dev->curcolor );
00629                 if ( !XAllocColor( xwd->display, xwd->map, &dev->curcolor ) )
00630                 {
00631                     fprintf( stderr, "Warning: could not allocate color\n" );
00632                     dev->curcolor.pixel = xwd->fgcolor.pixel;
00633                 }
00634             }
00635             else
00636             {
00637                 dev->curcolor = xwd->cmap0[icol0];
00638             }
00639             XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
00640         }
00641         else
00642         {
00643             dev->curcolor = xwd->fgcolor;
00644             XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
00645         }
00646         break;
00647     }
00648 
00649     case PLSTATE_COLOR1: {
00650         int icol1;
00651 
00652         if ( xwd->ncol1 == 0 )
00653             AllocCmap1( pls );
00654 
00655         if ( xwd->ncol1 < 2 )
00656             break;
00657 
00658         icol1 = ( pls->icol1 * ( xwd->ncol1 - 1 ) ) / ( pls->ncol1 - 1 );
00659         if ( xwd->color )
00660             dev->curcolor = xwd->cmap1[icol1];
00661         else
00662             dev->curcolor = xwd->fgcolor;
00663 
00664         XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
00665         break;
00666     }
00667 
00668     case PLSTATE_CMAP0:
00669         SetBGFG( pls );
00670         // If ncol0 has changed, need to reallocate
00671         if ( pls->ncol0 != xwd->ncol0 )
00672             AllocCmap0( pls );
00673         StoreCmap0( pls );
00674         break;
00675 
00676     case PLSTATE_CMAP1:
00677         StoreCmap1( pls );
00678         break;
00679     }
00680 
00681 #ifdef PL_HAVE_PTHREAD
00682     if ( usepthreads )
00683         pthread_mutex_unlock( &events_mutex );
00684 #endif
00685 }
00686 
00687 //--------------------------------------------------------------------------
00688 // plD_esc_xw()
00689 //
00690 // Escape function.
00691 //
00692 // Functions:
00693 //
00694 //    PLESC_EH  Handle pending events
00695 //    PLESC_EXPOSE      Force an expose
00696 //    PLESC_FILL        Fill polygon
00697 //    PLESC_FLUSH       Flush X event buffer
00698 //    PLESC_GETC        Get coordinates upon mouse click
00699 //    PLESC_REDRAW      Force a redraw
00700 //    PLESC_RESIZE      Force a resize
00701 //    PLESC_XORMOD      set/reset xor mode
00702 //    PLESC_IMAGE     draw the image in a fast way
00703 //    PLESC_IMAGEOPS: image related operations:
00704 //         ZEROW2D      disable writing to display
00705 //         ZEROW2B      disable writing to buffer
00706 //         ONEW2D       enable  writing to display
00707 //         ONEW2B       enable  writing to buffer
00708 //    PLESC_PL2DEVCOL   convert PLColor to device color (XColor)
00709 //    PLESC_DEV2PLCOL   convert device color (XColor) to PLColor
00710 //    PLESC_SETBGFG     set BG, FG colors
00711 //    PLESC_DEVINIT     alternate device initialization
00712 //
00713 // Note the [GET|SET]DEVCOL functions go through the intermediary stream
00714 // variable tmpcolor to keep the syntax simple.
00715 //--------------------------------------------------------------------------
00716 
00717 void
00718 plD_esc_xw( PLStream *pls, PLINT op, void *ptr )
00719 {
00720     dbug_enter( "plD_esc_xw" );
00721 
00722 #ifdef PL_HAVE_PTHREAD
00723     if ( usepthreads )
00724         pthread_mutex_lock( &events_mutex );
00725 #endif
00726 
00727     switch ( op )
00728     {
00729     case PLESC_EH:
00730         HandleEvents( pls );
00731         break;
00732 
00733     case PLESC_EXPOSE:
00734         ExposeCmd( pls, (PLDisplay *) ptr );
00735         break;
00736 
00737     case PLESC_FILL:
00738         FillPolygonCmd( pls );
00739         break;
00740 
00741     case PLESC_FLUSH: {
00742         XwDev     *dev = (XwDev *) pls->dev;
00743         XwDisplay *xwd = (XwDisplay *) dev->xwd;
00744         HandleEvents( pls );
00745         XFlush( xwd->display );
00746         break;
00747     }
00748     case PLESC_GETC:
00749         GetCursorCmd( pls, (PLGraphicsIn *) ptr );
00750         break;
00751 
00752     case PLESC_REDRAW:
00753         RedrawCmd( pls );
00754         break;
00755 
00756     case PLESC_RESIZE:
00757         ResizeCmd( pls, (PLDisplay *) ptr );
00758         break;
00759 
00760     case PLESC_XORMOD:
00761         XorMod( pls, (PLINT *) ptr );
00762         break;
00763 
00764     case PLESC_DOUBLEBUFFERING:
00765         ConfigBufferingCmd( pls, (PLBufferingCB *) ptr );
00766         break;
00767 
00768     case PLESC_IMAGE:
00769         DrawImage( pls );
00770         break;
00771 
00772     case PLESC_IMAGEOPS:
00773         imageops( pls, (PLINT *) ptr );
00774         break;
00775 
00776     case PLESC_PL2DEVCOL:
00777         PLColor_to_XColor( &pls->tmpcolor, (XColor *) ptr );
00778         break;
00779 
00780     case PLESC_DEV2PLCOL:
00781         PLColor_from_XColor( &pls->tmpcolor, (XColor *) ptr );
00782         break;
00783 
00784     case PLESC_SETBGFG:
00785         SetBGFG( pls );
00786         break;
00787 
00788     case PLESC_DEVINIT:
00789         OpenXwin( pls );
00790         break;
00791     }
00792 
00793 #ifdef PL_HAVE_PTHREAD
00794     if ( usepthreads )
00795         pthread_mutex_unlock( &events_mutex );
00796 #endif
00797 }
00798 
00799 //--------------------------------------------------------------------------
00800 // GetCursorCmd()
00801 //
00802 // Waits for a graphics input event and returns coordinates.
00803 //--------------------------------------------------------------------------
00804 
00805 static void
00806 GetCursorCmd( PLStream *pls, PLGraphicsIn *ptr )
00807 {
00808     XwDev        *dev = (XwDev *) pls->dev;
00809     XwDisplay    *xwd = (XwDisplay *) dev->xwd;
00810     XEvent       event;
00811     PLGraphicsIn *gin = &( dev->gin );
00812 
00813 // Initialize
00814 
00815     plGinInit( gin );
00816     dev->locate_mode = LOCATE_INVOKED_VIA_API;
00817     CreateXhairs( pls );
00818 
00819 // Run event loop until a point is selected
00820 
00821     while ( gin->pX < 0 && dev->locate_mode )
00822     {
00823         // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
00824         XNextEvent( xwd->display, &event );
00825         MasterEH( pls, &event );
00826     }
00827     *ptr = *gin;
00828     if ( dev->locate_mode )
00829     {
00830         dev->locate_mode = 0;
00831         DestroyXhairs( pls );
00832     }
00833 }
00834 
00835 //--------------------------------------------------------------------------
00836 // FillPolygonCmd()
00837 //
00838 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
00839 // Only solid color fill supported.
00840 //--------------------------------------------------------------------------
00841 
00842 static void
00843 FillPolygonCmd( PLStream *pls )
00844 {
00845     XwDev     *dev = (XwDev *) pls->dev;
00846     XwDisplay *xwd = (XwDisplay *) dev->xwd;
00847     XPoint    _pts[PL_MAXPOLY];
00848     XPoint    *pts;
00849     int       i;
00850 
00851     if ( pls->dev_npts > PL_MAXPOLY )
00852     {
00853         pts = (XPoint *) malloc( sizeof ( XPoint ) * (size_t) ( pls->dev_npts ) );
00854     }
00855     else
00856     {
00857         pts = _pts;
00858     }
00859 
00860     CheckForEvents( pls );
00861 
00862     for ( i = 0; i < pls->dev_npts; i++ )
00863     {
00864         pts[i].x = (short) ( dev->xscale * pls->dev_x[i] );
00865         pts[i].y = (short) ( dev->yscale * ( dev->ylen - pls->dev_y[i] ) );
00866     }
00867 
00868 // Fill polygons
00869 
00870     if ( dev->write_to_window )
00871         XFillPolygon( xwd->display, dev->window, dev->gc,
00872             pts, pls->dev_npts, Complex, CoordModeOrigin );
00873 
00874     if ( dev->write_to_pixmap )
00875         XFillPolygon( xwd->display, dev->pixmap, dev->gc,
00876             pts, pls->dev_npts, Complex, CoordModeOrigin );
00877 
00878 // If in debug mode, draw outline of boxes being filled
00879 
00880 #ifdef DEBUG
00881     if ( pls->debug )
00882     {
00883         XSetForeground( xwd->display, dev->gc, xwd->fgcolor.pixel );
00884         if ( dev->write_to_window )
00885             XDrawLines( xwd->display, dev->window, dev->gc, pts, pls->dev_npts,
00886                 CoordModeOrigin );
00887 
00888         if ( dev->write_to_pixmap )
00889             XDrawLines( xwd->display, dev->pixmap, dev->gc, pts, pls->dev_npts,
00890                 CoordModeOrigin );
00891 
00892         XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
00893     }
00894 #endif
00895 
00896     if ( pls->dev_npts > PL_MAXPOLY )
00897     {
00898         free( pts );
00899     }
00900 }
00901 
00902 //--------------------------------------------------------------------------
00903 // OpenXwin()
00904 //
00905 // Performs basic driver initialization, without actually opening or
00906 // modifying a window.  May be called by the outside world before plinit
00907 // in case the caller needs early access to the driver internals (not
00908 // very common -- currently only used by the plframe widget).
00909 //--------------------------------------------------------------------------
00910 
00911 static void
00912 OpenXwin( PLStream *pls )
00913 {
00914     XwDev     *dev;
00915     XwDisplay *xwd;
00916     int       i;
00917 
00918     dbug_enter( "OpenXwin" );
00919 
00920 // Allocate and initialize device-specific data
00921 
00922     if ( pls->dev != NULL )
00923         plwarn( "OpenXwin: device pointer is already set" );
00924 
00925     pls->dev = calloc( 1, (size_t) sizeof ( XwDev ) );
00926     if ( pls->dev == NULL )
00927         plexit( "plD_init_xw: Out of memory." );
00928 
00929     dev = (XwDev *) pls->dev;
00930 
00931 // Variables used in querying the X server for events
00932 
00933     dev->instr     = 0;
00934     dev->max_instr = MAX_INSTR;
00935 
00936 // See if display matches any already in use, and if so use that
00937 
00938     dev->xwd = NULL;
00939     for ( i = 0; i < PLXDISPLAYS; i++ )
00940     {
00941         if ( xwDisplay[i] == NULL )
00942         {
00943             continue;
00944         }
00945         else if ( pls->FileName == NULL && xwDisplay[i]->displayName == NULL )
00946         {
00947             dev->xwd = xwDisplay[i];
00948             break;
00949         }
00950         else if ( pls->FileName == NULL || xwDisplay[i]->displayName == NULL )
00951         {
00952             continue;
00953         }
00954         else if ( strcmp( xwDisplay[i]->displayName, pls->FileName ) == 0 )
00955         {
00956             dev->xwd = xwDisplay[i];
00957             break;
00958         }
00959     }
00960 
00961 // If no display matched, create a new one
00962 
00963     if ( dev->xwd == NULL )
00964     {
00965         dev->xwd = (XwDisplay *) calloc( 1, (size_t) sizeof ( XwDisplay ) );
00966         if ( dev->xwd == NULL )
00967             plexit( "Init: Out of memory." );
00968 
00969         for ( i = 0; i < PLXDISPLAYS; i++ )
00970         {
00971             if ( xwDisplay[i] == NULL )
00972                 break;
00973         }
00974         if ( i == PLXDISPLAYS )
00975             plexit( "Init: Out of xwDisplay's." );
00976 
00977         xwDisplay[i]  = xwd = (XwDisplay *) dev->xwd;
00978         xwd->nstreams = 1;
00979 
00980 // Open display
00981 #ifdef PL_HAVE_PTHREAD
00982         if ( usepthreads )
00983             if ( !XInitThreads() )
00984                 plexit( "xwin: XInitThreads() not successful." );
00985 #endif
00986         xwd->display = XOpenDisplay( pls->FileName );
00987         if ( xwd->display == NULL )
00988         {
00989             plexit( "Can't open display" );
00990         }
00991         xwd->displayName = pls->FileName;
00992         xwd->screen      = DefaultScreen( xwd->display );
00993         if ( synchronize )
00994             XSynchronize( xwd->display, 1 );
00995 
00996         // Get colormap and visual
00997         xwd->map = DefaultColormap( xwd->display, xwd->screen );
00998         GetVisual( pls );
00999 
01000         //
01001         // Figure out if we have a color display or not.
01002         // Default is color IF the user hasn't specified and IF the output device
01003         // is not grayscale.
01004         //
01005         if ( pls->colorset )
01006             xwd->color = pls->color;
01007         else
01008         {
01009             pls->color = 1;
01010             xwd->color = !AreWeGrayscale( xwd->display );
01011         }
01012 
01013         // Allocate space for colors
01014         // Note cmap1 allocation is deferred
01015         xwd->ncol0_alloc = pls->ncol0;
01016         xwd->cmap0       = (XColor *) calloc( (size_t) ( pls->ncol0 ), sizeof ( XColor ) );
01017         if ( xwd->cmap0 == 0 )
01018             plexit( "couldn't allocate space for cmap0 colors" );
01019 
01020         // Allocate & set background and foreground colors
01021         AllocBGFG( pls );
01022         SetBGFG( pls );
01023     }
01024 
01025 // Display matched, so use existing display data
01026 
01027     else
01028     {
01029         xwd = (XwDisplay *) dev->xwd;
01030         xwd->nstreams++;
01031     }
01032     xwd->ixwd = i;
01033 }
01034 
01035 //--------------------------------------------------------------------------
01036 // Init()
01037 //
01038 // Xlib initialization routine.
01039 //
01040 // Controlling routine for X window creation and/or initialization.
01041 // The user may customize the window in the following ways:
01042 //
01043 // display:     pls->OutFile (use plsfnam() or -display option)
01044 // size:        pls->xlength, pls->ylength (use plspage() or -geo option)
01045 // bg color:    pls->cmap0[0] (use plscolbg() or -bg option)
01046 //--------------------------------------------------------------------------
01047 
01048 static void
01049 Init( PLStream *pls )
01050 {
01051     XwDev     *dev = (XwDev *) pls->dev;
01052     XwDisplay *xwd = (XwDisplay *) dev->xwd;
01053 
01054     Window    root;
01055     int       x, y;
01056 
01057     dbug_enter( "Init" );
01058 
01059 // If not plotting into a child window, need to create main window now
01060 
01061     if ( pls->window_id == 0 )
01062     {
01063         dev->is_main = TRUE;
01064         InitMain( pls );
01065     }
01066     else
01067     {
01068         dev->is_main = FALSE;
01069         dev->window  = (Window) pls->window_id;
01070     }
01071 
01072 // Initialize colors
01073 
01074     if ( noinitcolors == 0 )
01075         InitColors( pls );
01076     XSetWindowColormap( xwd->display, dev->window, xwd->map );
01077 
01078 // Set up GC for ordinary draws
01079 
01080     if ( !dev->gc )
01081         dev->gc = XCreateGC( xwd->display, dev->window, 0, 0 );
01082 
01083 // Set up GC for rubber-band draws
01084 
01085     if ( !xwd->gcXor )
01086     {
01087         XGCValues     gcValues;
01088         unsigned long mask;
01089 
01090         gcValues.background = xwd->cmap0[0].pixel;
01091         gcValues.foreground = 0xFF;
01092         gcValues.function   = GXxor;
01093         mask = GCForeground | GCBackground | GCFunction;
01094 
01095         xwd->gcXor = XCreateGC( xwd->display, dev->window, mask, &gcValues );
01096     }
01097 
01098 // Get initial drawing area dimensions
01099 
01100     (void) XGetGeometry( xwd->display, dev->window, &root, &x, &y,
01101         &dev->width, &dev->height, &dev->border, &xwd->depth );
01102 
01103     dev->init_width  = (long) dev->width;
01104     dev->init_height = (long) dev->height;
01105 
01106 // Set up flags that determine what we are writing to
01107 // If nopixmap is set, ignore db
01108 
01109     if ( pls->nopixmap )
01110     {
01111         dev->write_to_pixmap = 0;
01112         pls->db = 0;
01113     }
01114     else
01115     {
01116         dev->write_to_pixmap = 1;
01117     }
01118     dev->write_to_window = !pls->db;
01119 
01120 // Create pixmap for holding plot image (for expose events).
01121 
01122     if ( dev->write_to_pixmap )
01123         CreatePixmap( pls );
01124 
01125 // Set drawing color
01126 
01127     plD_state_xw( pls, PLSTATE_COLOR0 );
01128 
01129     XSetWindowBackground( xwd->display, dev->window, xwd->cmap0[0].pixel );
01130     XSetBackground( xwd->display, dev->gc, xwd->cmap0[0].pixel );
01131 
01132 // Set fill rule.
01133     if ( pls->dev_eofill )
01134         XSetFillRule( xwd->display, dev->gc, EvenOddRule );
01135     else
01136         XSetFillRule( xwd->display, dev->gc, WindingRule );
01137 
01138 // If main window, need to map it and wait for exposure
01139 
01140     if ( dev->is_main )
01141         MapMain( pls );
01142 }
01143 
01144 //--------------------------------------------------------------------------
01145 // InitMain()
01146 //
01147 // Create main window using standard Xlib calls.
01148 //--------------------------------------------------------------------------
01149 
01150 static void
01151 InitMain( PLStream *pls )
01152 {
01153     XwDev      *dev = (XwDev *) pls->dev;
01154     XwDisplay  *xwd = (XwDisplay *) dev->xwd;
01155 
01156     Window     root;
01157     XSizeHints hint;
01158     int        x, y;
01159     U_INT      width, height, border, depth;
01160 
01161     dbug_enter( "InitMain" );
01162 
01163 // Get root window geometry
01164 
01165     (void) XGetGeometry( xwd->display, DefaultRootWindow( xwd->display ),
01166         &root, &x, &y, &width, &height, &border, &depth );
01167 
01168 // Set window size
01169 // Need to be careful to set XSizeHints flags correctly
01170 
01171     hint.flags = 0;
01172     if ( pls->xlength == 0 && pls->ylength == 0 )
01173         hint.flags |= PSize;
01174     else
01175         hint.flags |= USSize;
01176 
01177     if ( pls->xlength == 0 )
01178         pls->xlength = (PLINT) ( width * 0.75 );
01179     if ( pls->ylength == 0 )
01180         pls->ylength = (PLINT) ( height * 0.75 );
01181 
01182     if ( pls->xlength > (short) width )
01183         pls->xlength = (PLINT) ( width - dev->border * 2 );
01184     if ( pls->ylength > (short) height )
01185         pls->ylength = (PLINT) ( height - dev->border * 2 );
01186 
01187     hint.width  = (int) pls->xlength;
01188     hint.height = (int) pls->ylength;
01189     dev->border = 5;
01190 
01191 // Set window position if specified by the user.
01192 // Otherwise leave up to the window manager
01193 
01194     if ( pls->xoffset != 0 || pls->yoffset != 0 )
01195     {
01196         hint.flags |= USPosition;
01197         hint.x      = (int) pls->xoffset;
01198         hint.y      = (int) pls->yoffset;
01199     }
01200     else
01201     {
01202         hint.x = 0;
01203         hint.y = 0;
01204     }
01205 
01206 // Window creation
01207 
01208     dev->window =
01209         XCreateWindow( xwd->display,
01210             DefaultRootWindow( xwd->display ),
01211             hint.x, hint.y, (unsigned int) hint.width, (unsigned int) hint.height,
01212             dev->border, (int) xwd->depth,
01213             InputOutput, xwd->visual,
01214             0, NULL );
01215 
01216     XSetStandardProperties( xwd->display, dev->window, pls->plwindow, pls->plwindow,
01217         None, 0, 0, &hint );
01218 }
01219 
01220 //--------------------------------------------------------------------------
01221 // MapMain()
01222 //
01223 // Sets up event handlers, maps main window and waits for exposure.
01224 //--------------------------------------------------------------------------
01225 
01226 static void
01227 MapMain( PLStream *pls )
01228 {
01229     XwDev     *dev = (XwDev *) pls->dev;
01230     XwDisplay *xwd = (XwDisplay *) dev->xwd;
01231     XEvent    event;
01232 
01233     dbug_enter( "MapMain" );
01234 
01235 // Input event selection
01236 
01237     dev->event_mask =
01238         ButtonPressMask |
01239         KeyPressMask |
01240         ExposureMask |
01241         ButtonMotionMask |     // drag
01242         StructureNotifyMask;
01243 
01244     XSelectInput( xwd->display, dev->window, dev->event_mask );
01245 
01246 // Window mapping
01247 
01248     XMapRaised( xwd->display, dev->window );
01249 
01250     Atom wmDelete = XInternAtom( xwd->display, "WM_DELETE_WINDOW", False );
01251     XSetWMProtocols( xwd->display, dev->window, &wmDelete, 1 );
01252 
01253 // Wait for exposure
01254 // Remove extraneous expose events from the event queue
01255 
01256     for (;; )
01257     {
01258         XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
01259         if ( event.type == Expose )
01260         {
01261             while ( XCheckWindowEvent( xwd->display, dev->window,
01262                         ExposureMask, &event ) )
01263                 ;
01264             break;
01265         }
01266     }
01267 }
01268 
01269 //--------------------------------------------------------------------------
01270 // WaitForPage()
01271 //
01272 // This routine waits for the user to advance the plot, while handling
01273 // all other events.
01274 //--------------------------------------------------------------------------
01275 
01276 static void
01277 WaitForPage( PLStream *pls )
01278 {
01279     XwDev     *dev = (XwDev *) pls->dev;
01280     XwDisplay *xwd = (XwDisplay *) dev->xwd;
01281     XEvent    event;
01282 
01283     dbug_enter( "WaitForPage" );
01284 
01285     while ( !dev->exit_eventloop )
01286     {
01287         // XWindowEvent( xwd->display, dev->window, dev->event_mask, &event );
01288         XNextEvent( xwd->display, &event );
01289         MasterEH( pls, &event );
01290     }
01291     dev->exit_eventloop = FALSE;
01292 }
01293 
01294 //--------------------------------------------------------------------------
01295 // events_thread()
01296 //
01297 // This function is being running continously by a thread and is
01298 // responsible for dealing with expose and resize X events, in
01299 // the case that the main program is too busy to honor them.
01300 //
01301 // Dealing with other X events is possible, but not desirable,
01302 // e.g. treating the "Q" or right-mouse-button would terminate
01303 // the thread (if this is desirable, the thread should kill the
01304 // main program -- not thread aware -- and kill itself afterward).
01305 //
01306 // This works pretty well, but the main program *must* be linked
01307 // with the pthread library, although not being thread aware.
01308 // This happens automatically when linking against libplplot.so,
01309 // but when building modules for extending some language such as
01310 // Python or Octave, the language providing binary itself must be
01311 // relinked with -lpthread.
01312 //
01313 //--------------------------------------------------------------------------
01314 
01315 #ifdef PL_HAVE_PTHREAD
01316 static void
01317 events_thread( void *pls )
01318 {
01319     if ( usepthreads )
01320     {
01321         PLStream        *lpls = (PLStream *) pls;
01322         XwDev           *dev  = (XwDev *) lpls->dev;
01323         XwDisplay       *xwd  = (XwDisplay *) dev->xwd;
01324         PLStream        *oplsc;
01325         struct timespec delay;
01326         XEvent          event;
01327         long            event_mask;
01328         sigset_t        set;
01329 
01330         //
01331         // only treats exposures and resizes, but remove usual events from queue,
01332         // as it can be disturbing to not have them acknowledged in real time,
01333         // because the program is busy, and suddenly all being acknowledged.
01334         // Also, the locator ("L" key) is sluggish if driven by the thread.
01335         //
01336         // But this approach is a problem when the thread removes events
01337         // from the queue while the program is responsible! The user hits 'L'
01338         // and nothing happens, as the thread removes it.
01339         //
01340         // Perhaps the "Q" key should have a different treatment, quiting the
01341         // program anyway?
01342         //
01343         // Changed: does not remove non treated events from the queue
01344         //
01345 
01346         event_mask = ExposureMask | StructureNotifyMask;
01347 
01348         // block all signal for this thread
01349         sigemptyset( &set );
01350         // sigfillset(&set);  can't be all signals, decide latter
01351         sigaddset( &set, SIGINT );
01352 
01353         sigprocmask( SIG_BLOCK, &set, NULL );
01354 
01355         pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
01356         pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
01357 
01358         delay.tv_sec  = 0;
01359         delay.tv_nsec = 10000000; // this thread runs 10 times a second. (1/10 ms)
01360 
01361         while ( 1 )
01362         {
01363             pthread_mutex_lock( &events_mutex );
01364 
01365             if ( dev->is_main && !lpls->plbuf_read &&
01366                  ++dev->instr % dev->max_instr == 0 )
01367             {
01368                 dev->instr = 0;
01369                 while ( XCheckWindowEvent( xwd->display, dev->window, event_mask, &event ) )
01370                 {
01371                     // As ResizeEH() ends up calling plRemakePlot(), that
01372                     // indirectly uses the current stream, one needs to
01373                     // temporarily set plplot current stream to this thread's
01374                     // stream
01375 
01376                     oplsc = plsc;
01377                     plsc  = lpls;
01378                     switch ( event.type )
01379                     {
01380                     case Expose:
01381                         ExposeEH( lpls, &event );
01382                         break;
01383                     case ConfigureNotify:
01384                         ResizeEH( lpls, &event );
01385                         break;
01386                     }
01387                     plsc = oplsc;
01388                 }
01389             }
01390 
01391             pthread_mutex_unlock( &events_mutex );
01392             nanosleep( &delay, NULL ); // 10ms in linux
01393             /* pthread_yield(NULL); */ /* this puts too much load on the CPU */
01394         }
01395     }
01396 }
01397 #endif
01398 
01399 //--------------------------------------------------------------------------
01400 // CheckForEvents()
01401 //
01402 // A front-end to HandleEvents(), which is only called if certain conditions
01403 // are satisfied:
01404 //
01405 // - must be the creator of the main window (i.e. PLplot is handling the
01406 //   X event loop by polling).
01407 // - must not be in the middle of a plot redisplay (else the order of event
01408 //   handling can become circuitous).
01409 // - only query X for events and process them every dev->max_instr times
01410 //   this function is called (good for performance since querying X is a
01411 //   nontrivial performance hit).
01412 //--------------------------------------------------------------------------
01413 
01414 static void
01415 CheckForEvents( PLStream *pls )
01416 {
01417     XwDev *dev = (XwDev *) pls->dev;
01418 
01419     if ( dev->is_main &&
01420          !pls->plbuf_read &&
01421          ++dev->instr % dev->max_instr == 0 )
01422     {
01423         dev->instr = 0;
01424         HandleEvents( pls );
01425     }
01426 }
01427 
01428 //--------------------------------------------------------------------------
01429 // HandleEvents()
01430 //
01431 // Just a front-end to MasterEH(), for use when not actually waiting for an
01432 // event but only checking the event queue.
01433 //--------------------------------------------------------------------------
01434 
01435 static void
01436 HandleEvents( PLStream *pls )
01437 {
01438     XwDev     *dev = (XwDev *) pls->dev;
01439     XwDisplay *xwd = (XwDisplay *) dev->xwd;
01440     XEvent    event;
01441 
01442     while ( XCheckTypedWindowEvent( xwd->display, dev->window,
01443                 ClientMessage, &event ) ||
01444             XCheckWindowEvent( xwd->display, dev->window,
01445                 dev->event_mask, &event ) )
01446         MasterEH( pls, &event );
01447 }
01448 
01449 //--------------------------------------------------------------------------
01450 // MasterEH()
01451 //
01452 // Master X event handler routine.
01453 // Redirects control to routines to handle:
01454 //    - keyboard events
01455 //    - mouse events
01456 //    - expose events
01457 //    - resize events
01458 //
01459 // By supplying a master event handler, the user can take over all event
01460 // processing.  If events other than those trapped by PLplot need handling,
01461 // just call XSelectInput with the appropriate flags.  The default PLplot
01462 // event handling can be modified arbitrarily by changing the event struct.
01463 //--------------------------------------------------------------------------
01464 
01465 static void
01466 MasterEH( PLStream *pls, XEvent *event )
01467 {
01468     XwDev *dev = (XwDev *) pls->dev;
01469 
01470     if ( dev->MasterEH != NULL )
01471         ( *dev->MasterEH )( pls, event );
01472 
01473     switch ( event->type )
01474     {
01475     case KeyPress:
01476         KeyEH( pls, event );
01477         break;
01478 
01479     case ButtonPress:
01480         ButtonEH( pls, event );
01481         break;
01482 
01483     case Expose:
01484         ExposeEH( pls, event );
01485         break;
01486 
01487     case ConfigureNotify:
01488         ResizeEH( pls, event );
01489         break;
01490 
01491     case MotionNotify:
01492         if ( event->xmotion.state )
01493             ButtonEH( pls, event ); // drag
01494         MotionEH( pls, event );
01495         break;
01496 
01497     case EnterNotify:
01498         EnterEH( pls, event );
01499         break;
01500 
01501     case LeaveNotify:
01502         LeaveEH( pls, event );
01503         break;
01504 
01505     case ClientMessage:
01506         ClientEH( pls, event );
01507         break;
01508     }
01509 }
01510 
01511 //--------------------------------------------------------------------------
01512 // ClientEH()
01513 //
01514 // Event handler routine for client message events (WM_DELETE_WINDOW)
01515 //--------------------------------------------------------------------------
01516 
01517 static void
01518 ClientEH( PLStream *pls, XEvent *event )
01519 {
01520     XwDev     *dev = (XwDev *) pls->dev;
01521     XwDisplay *xwd = (XwDisplay *) dev->xwd;
01522 
01523     if ( (Atom) event->xclient.data.l[0] == XInternAtom( xwd->display, "WM_DELETE_WINDOW", False ) )
01524     {
01525         pls->nopause        = TRUE;
01526         pls->stream_closed  = TRUE;
01527         dev->exit_eventloop = TRUE;
01528         // plexit( "" );
01529     }
01530 }
01531 
01532 
01533 //--------------------------------------------------------------------------
01534 // KeyEH()
01535 //
01536 // Event handler routine for keyboard events.
01537 //--------------------------------------------------------------------------
01538 
01539 static void
01540 KeyEH( PLStream *pls, XEvent *event )
01541 {
01542     XwDev *dev = (XwDev *) pls->dev;
01543 
01544     dbug_enter( "KeyEH" );
01545 
01546     LookupXKeyEvent( pls, event );
01547     if ( dev->locate_mode )
01548         LocateKey( pls );
01549     else
01550         ProcessKey( pls );
01551 }
01552 
01553 //--------------------------------------------------------------------------
01554 // ButtonEH()
01555 //
01556 // Event handler routine for ButtonPress events.
01557 //--------------------------------------------------------------------------
01558 
01559 static void
01560 ButtonEH( PLStream *pls, XEvent *event )
01561 {
01562     XwDev *dev = (XwDev *) pls->dev;
01563 
01564     dbug_enter( "ButtonEH" );
01565 
01566     LookupXButtonEvent( pls, event );
01567     if ( dev->locate_mode )
01568         LocateButton( pls );
01569     else
01570         ProcessButton( pls );
01571 }
01572 
01573 //--------------------------------------------------------------------------
01574 // LookupXKeyEvent()
01575 //
01576 // Fills in the PLGraphicsIn from an XKeyEvent.  The PLGraphicsIn keysym is
01577 // the same as the X keysym for all cases except for control keys that have
01578 // ASCII equivalents, i.e.:
01579 //
01580 // Name               X-keysym  ASCII   Ctrl-key
01581 // ----               --------  -----   --------
01582 // XK_BackSpace        0xFF08   0x08      ^H
01583 // XK_Tab              0xFF09   0x09      ^I
01584 // XK_Linefeed         0xFF0A   0x0A      ^J
01585 // XK_Return           0xFF0D   0x0D      ^M
01586 // XK_Escape           0xFF1B   0x1B      ^[
01587 // XK_Delete           0xFFFF   0xFF     (none)
01588 //
01589 // The ASCII representation of these characters is used for the PLGraphicsIn
01590 // keysym to simplify code logic.  It turns out that the X keysyms are
01591 // identical to the ASCII values with the upper 8 bits set.
01592 //--------------------------------------------------------------------------
01593 
01594 static void
01595 LookupXKeyEvent( PLStream *pls, XEvent *event )
01596 {
01597     XwDev          *dev      = (XwDev *) pls->dev;
01598     PLGraphicsIn   *gin      = &( dev->gin );
01599     XKeyEvent      *keyEvent = (XKeyEvent *) event;
01600     KeySym         keysym;
01601     int            nchars, ncmax = PL_MAXKEY - 1;
01602     XComposeStatus cs;
01603 
01604     gin->pX = keyEvent->x;
01605     gin->pY = keyEvent->y;
01606     gin->dX = (PLFLT) keyEvent->x / ( dev->width - 1 );
01607     gin->dY = 1.0 - (PLFLT) keyEvent->y / ( dev->height - 1 );
01608 
01609     gin->state = keyEvent->state;
01610 
01611     nchars = XLookupString( keyEvent, gin->string, ncmax, &keysym, &cs );
01612     gin->string[nchars] = '\0';
01613 
01614     pldebug( "LookupXKeyEvent",
01615         "Keysym %x, translation: %s\n", keysym, gin->string );
01616 
01617     switch ( keysym )
01618     {
01619     case XK_BackSpace:
01620     case XK_Tab:
01621     case XK_Linefeed:
01622     case XK_Return:
01623     case XK_Escape:
01624     case XK_Delete:
01625         gin->keysym = 0xFF & keysym;
01626         break;
01627 
01628     default:
01629         gin->keysym = (unsigned int) keysym;
01630     }
01631 }
01632 
01633 //--------------------------------------------------------------------------
01634 // LookupXButtonEvent()
01635 //
01636 // Fills in the PLGraphicsIn from an XButtonEvent.
01637 //--------------------------------------------------------------------------
01638 
01639 static void
01640 LookupXButtonEvent( PLStream *pls, XEvent *event )
01641 {
01642     XwDev        *dev         = (XwDev *) pls->dev;
01643     PLGraphicsIn *gin         = &( dev->gin );
01644     XButtonEvent *buttonEvent = (XButtonEvent *) event;
01645 
01646     pldebug( "LookupXButtonEvent",
01647         "Button: %d, x: %d, y: %d\n",
01648         buttonEvent->button, buttonEvent->x, buttonEvent->y );
01649 
01650     gin->pX = buttonEvent->x;
01651     gin->pY = buttonEvent->y;
01652     gin->dX = (PLFLT) buttonEvent->x / ( dev->width - 1 );
01653     gin->dY = 1.0 - (PLFLT) buttonEvent->y / ( dev->height - 1 );
01654 
01655     gin->button = buttonEvent->button;
01656     gin->state  = buttonEvent->state;
01657     gin->keysym = 0x20;
01658 }
01659 
01660 //--------------------------------------------------------------------------
01661 // ProcessKey()
01662 //
01663 // Process keyboard events other than locate input.
01664 //--------------------------------------------------------------------------
01665 
01666 static void
01667 ProcessKey( PLStream *pls )
01668 {
01669     XwDev        *dev = (XwDev *) pls->dev;
01670     PLGraphicsIn *gin = &( dev->gin );
01671 
01672     dbug_enter( "ProcessKey" );
01673 
01674 // Call user keypress event handler.  Since this is called first, the user
01675 // can disable all internal event handling by setting key.keysym to 0.
01676 //
01677     if ( pls->KeyEH != NULL )
01678         ( *pls->KeyEH )( gin, pls->KeyEH_data, &dev->exit_eventloop );
01679 
01680 // Handle internal events
01681 
01682     switch ( gin->keysym )
01683     {
01684     case PLK_Return:
01685     case PLK_Linefeed:
01686     case PLK_Next:
01687         // Advance to next page (i.e. terminate event loop) on a <eol>
01688         // Check for both <CR> and <LF> for portability, also a <Page Down>
01689         dev->exit_eventloop = TRUE;
01690         break;
01691 
01692     case 'Q':
01693         // Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake)
01694         pls->nopause = TRUE;
01695         plexit( "" );
01696         break;
01697 
01698     case 'L':
01699         // Begin locate mode
01700         dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER;
01701         CreateXhairs( pls );
01702         break;
01703     }
01704 }
01705 
01706 //--------------------------------------------------------------------------
01707 // ProcessButton()
01708 //
01709 // Process ButtonPress events other than locate input.
01710 // On:
01711 //   Button1: nothing (except when in locate mode, see ButtonLocate)
01712 //   Button2: nothing
01713 //   Button3: set page advance flag
01714 //--------------------------------------------------------------------------
01715 
01716 static void
01717 ProcessButton( PLStream *pls )
01718 {
01719     XwDev        *dev = (XwDev *) pls->dev;
01720     PLGraphicsIn *gin = &( dev->gin );
01721 
01722     dbug_enter( "ProcessButton" );
01723 
01724 // Call user event handler.  Since this is called first, the user can
01725 // disable all PLplot internal event handling by setting gin->button to 0.
01726 //
01727     if ( pls->ButtonEH != NULL )
01728         ( *pls->ButtonEH )( gin, pls->ButtonEH_data, &dev->exit_eventloop );
01729 
01730 // Handle internal events
01731 
01732     switch ( gin->button )
01733     {
01734     case Button3:
01735         dev->exit_eventloop = TRUE;
01736         break;
01737     }
01738 }
01739 
01740 //--------------------------------------------------------------------------
01741 // LocateKey()
01742 //
01743 // Front-end to locate handler for KeyPress events.
01744 // Provides for a number of special effects:
01745 //
01746 //  <Escape>            Ends locate mode
01747 //  <Cursor>            Moves cursor one pixel in the specified direction
01748 //  <Mod-Cursor>        Accelerated cursor movement (5x for each modifier)
01749 //--------------------------------------------------------------------------
01750 
01751 static void
01752 LocateKey( PLStream *pls )
01753 {
01754     XwDev        *dev = (XwDev *) pls->dev;
01755     XwDisplay    *xwd = (XwDisplay *) dev->xwd;
01756     PLGraphicsIn *gin = &( dev->gin );
01757 
01758 // End locate mode on <Escape>
01759 
01760     if ( gin->keysym == PLK_Escape )
01761     {
01762         dev->locate_mode = 0;
01763         DestroyXhairs( pls );
01764         plGinInit( gin );
01765     }
01766 
01767 // Ignore modifier keys
01768 
01769     else if ( IsModifierKey( gin->keysym ) )
01770     {
01771         plGinInit( gin );
01772     }
01773 
01774 // Now handle cursor keys
01775 
01776     else if ( IsCursorKey( gin->keysym ) )
01777     {
01778         int x1, y1, dx = 0, dy = 0;
01779         int xmin = 0, xmax = (int) dev->width - 1, ymin = 0, ymax = (int) dev->height - 1;
01780 
01781         switch ( gin->keysym )
01782         {
01783         case PLK_Left:
01784             dx = -1;
01785             break;
01786         case PLK_Right:
01787             dx = 1;
01788             break;
01789         case PLK_Up:
01790             dy = -1;
01791             break;
01792         case PLK_Down:
01793             dy = 1;
01794             break;
01795         }
01796 
01797         // Each modifier key added increases the multiplication factor by 5
01798 
01799         // Shift
01800 
01801         if ( gin->state & 0x01 )
01802         {
01803             dx *= 5;
01804             dy *= 5;
01805         }
01806 
01807         // Caps Lock
01808 
01809         if ( gin->state & 0x02 )
01810         {
01811             dx *= 5;
01812             dy *= 5;
01813         }
01814 
01815         // Control
01816 
01817         if ( gin->state & 0x04 )
01818         {
01819             dx *= 5;
01820             dy *= 5;
01821         }
01822 
01823         // Alt
01824 
01825         if ( gin->state & 0x08 )
01826         {
01827             dx *= 5;
01828             dy *= 5;
01829         }
01830 
01831         // Bounds checking so that we don't send cursor out of window
01832 
01833         x1 = gin->pX + dx;
01834         y1 = gin->pY + dy;
01835 
01836         if ( x1 < xmin )
01837             dx = xmin - gin->pX;
01838         if ( y1 < ymin )
01839             dy = ymin - gin->pY;
01840         if ( x1 > xmax )
01841             dx = xmax - gin->pX;
01842         if ( y1 > ymax )
01843             dy = ymax - gin->pY;
01844 
01845         // Engage...
01846 
01847         XWarpPointer( xwd->display, dev->window, None, 0, 0, 0, 0, dx, dy );
01848         plGinInit( gin );
01849     }
01850 
01851 // Call ordinary locate handler
01852 
01853     else
01854     {
01855         Locate( pls );
01856     }
01857 }
01858 
01859 //--------------------------------------------------------------------------
01860 // LocateButton()
01861 //
01862 // Front-end to locate handler for ButtonPress events.
01863 // Only passes control to Locate() for Button1 presses.
01864 //--------------------------------------------------------------------------
01865 
01866 static void
01867 LocateButton( PLStream *pls )
01868 {
01869     XwDev        *dev = (XwDev *) pls->dev;
01870     PLGraphicsIn *gin = &( dev->gin );
01871 
01872     switch ( gin->button )
01873     {
01874     case Button1:
01875         Locate( pls );
01876         break;
01877     }
01878 }
01879 
01880 //--------------------------------------------------------------------------
01881 // Locate()
01882 //
01883 // Handles locate mode events.
01884 //
01885 // In locate mode: move cursor to desired location and select by pressing a
01886 // key or by clicking on the mouse (if available).  Typically the world
01887 // coordinates of the selected point are reported.
01888 //
01889 // There are two ways to enter Locate mode -- via the API, or via a driver
01890 // command.  The API entry point is the call plGetCursor(), which initiates
01891 // locate mode and does not return until input has been obtained.  The
01892 // driver entry point is by entering a 'L' while the driver is waiting for
01893 // events.
01894 //
01895 // Locate mode input is reported in one of three ways:
01896 // 1. Through a returned PLGraphicsIn structure, when user has specified a
01897 //    locate handler via (*pls->LocateEH).
01898 // 2. Through a returned PLGraphicsIn structure, when locate mode is invoked
01899 //    by a plGetCursor() call.
01900 // 3. Through writes to stdout, when locate mode is invoked by a driver
01901 //    command and the user has not supplied a locate handler.
01902 //
01903 // Hitting <Escape> will at all times end locate mode.  Other keys will
01904 // typically be interpreted as locator input.  Selecting a point out of
01905 // bounds will end locate mode unless the user overrides with a supplied
01906 // Locate handler.
01907 //--------------------------------------------------------------------------
01908 
01909 static void
01910 Locate( PLStream *pls )
01911 {
01912     XwDev        *dev = (XwDev *) pls->dev;
01913     PLGraphicsIn *gin = &( dev->gin );
01914 
01915 // Call user locate mode handler if provided
01916 
01917     if ( pls->LocateEH != NULL )
01918         ( *pls->LocateEH )( gin, pls->LocateEH_data, &dev->locate_mode );
01919 
01920 // Use default procedure
01921 
01922     else
01923     {
01924         // Try to locate cursor
01925 
01926         if ( plTranslateCursor( gin ) )
01927         {
01928             // If invoked by the API, we're done
01929             // Otherwise send report to stdout
01930 
01931             if ( dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER )
01932             {
01933                 pltext();
01934                 if ( gin->keysym < 0xFF && isprint( gin->keysym ) )
01935                     printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym );
01936                 else
01937                     printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym );
01938 
01939                 plgra();
01940             }
01941         }
01942         else
01943         {
01944             // Selected point is out of bounds, so end locate mode
01945 
01946             dev->locate_mode = 0;
01947             DestroyXhairs( pls );
01948         }
01949     }
01950 }
01951 
01952 //--------------------------------------------------------------------------
01953 // MotionEH()
01954 //
01955 // Event handler routine for MotionNotify events.
01956 // If drawing crosshairs, the first and last draws must be done "by hand".
01957 //--------------------------------------------------------------------------
01958 
01959 static void
01960 MotionEH( PLStream *pls, XEvent *event )
01961 {
01962     XwDev        *dev         = (XwDev *) pls->dev;
01963     XMotionEvent *motionEvent = (XMotionEvent *) event;
01964 
01965     if ( dev->drawing_xhairs )
01966     {
01967         DrawXhairs( pls, motionEvent->x, motionEvent->y );
01968     }
01969 }
01970 
01971 //--------------------------------------------------------------------------
01972 // EnterEH()
01973 //
01974 // Event handler routine for EnterNotify events.  Only called if drawing
01975 // crosshairs -- a draw must be done here to start off the new set.
01976 //--------------------------------------------------------------------------
01977 
01978 static void
01979 EnterEH( PLStream *pls, XEvent *event )
01980 {
01981     XwDev          *dev           = (XwDev *) pls->dev;
01982     XCrossingEvent *crossingEvent = (XCrossingEvent *) event;
01983 
01984     DrawXhairs( pls, crossingEvent->x, crossingEvent->y );
01985     dev->drawing_xhairs = 1;
01986 }
01987 
01988 //--------------------------------------------------------------------------
01989 // LeaveEH()
01990 //
01991 // Event handler routine for EnterNotify or LeaveNotify events.
01992 // If drawing crosshairs, a draw must be done here to start off the new
01993 // set or erase the last set.
01994 //--------------------------------------------------------------------------
01995 
01996 static void
01997 LeaveEH( PLStream *pls, XEvent * PL_UNUSED( event ) )
01998 {
01999     XwDev *dev = (XwDev *) pls->dev;
02000 
02001     UpdateXhairs( pls );
02002     dev->drawing_xhairs = 0;
02003 }
02004 
02005 //--------------------------------------------------------------------------
02006 // CreateXhairs()
02007 //
02008 // Creates graphic crosshairs at current pointer location.
02009 //--------------------------------------------------------------------------
02010 
02011 static void
02012 CreateXhairs( PLStream *pls )
02013 {
02014     XwDev        *dev = (XwDev *) pls->dev;
02015     XwDisplay    *xwd = (XwDisplay *) dev->xwd;
02016     Window       root, child;
02017     int          root_x, root_y, win_x, win_y;
02018     unsigned int mask;
02019     XEvent       event;
02020 
02021 // Get a crosshair cursor and switch to it.
02022 
02023     if ( !xwd->xhair_cursor )
02024         xwd->xhair_cursor = XCreateFontCursor( xwd->display, XC_crosshair );
02025 
02026     XDefineCursor( xwd->display, dev->window, xwd->xhair_cursor );
02027 
02028 // Find current pointer location and draw graphic crosshairs if pointer is
02029 // inside our window
02030 
02031     if ( XQueryPointer( xwd->display, dev->window, &root, &child,
02032              &root_x, &root_y, &win_x, &win_y, &mask ) )
02033     {
02034         if ( win_x >= 0 && win_x < (int) dev->width &&
02035              win_y >= 0 && win_y < (int) dev->height )
02036         {
02037             DrawXhairs( pls, win_x, win_y );
02038             dev->drawing_xhairs = 1;
02039         }
02040     }
02041 
02042 // Sync the display and then throw away all pending motion events
02043 
02044     XSync( xwd->display, 0 );
02045     while ( XCheckWindowEvent( xwd->display, dev->window,
02046                 PointerMotionMask, &event ) )
02047         ;
02048 
02049 // Catch PointerMotion and crossing events so we can update them properly
02050 
02051     dev->event_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask;
02052     XSelectInput( xwd->display, dev->window, dev->event_mask );
02053 }
02054 
02055 //--------------------------------------------------------------------------
02056 // DestroyXhairs()
02057 //
02058 // Destroys graphic crosshairs.
02059 //--------------------------------------------------------------------------
02060 
02061 static void
02062 DestroyXhairs( PLStream *pls )
02063 {
02064     XwDev     *dev = (XwDev *) pls->dev;
02065     XwDisplay *xwd = (XwDisplay *) dev->xwd;
02066 
02067 // Switch back to boring old pointer
02068 
02069     XUndefineCursor( xwd->display, dev->window );
02070 
02071 // Don't catch PointerMotion or crossing events any more
02072 
02073     dev->event_mask &=
02074         ~PointerMotionMask & ~EnterWindowMask & ~LeaveWindowMask;
02075     XSelectInput( xwd->display, dev->window, dev->event_mask );
02076 
02077 // This draw removes the last set of graphic crosshairs
02078 
02079     UpdateXhairs( pls );
02080     dev->drawing_xhairs = 0;
02081 }
02082 
02083 //--------------------------------------------------------------------------
02084 // DrawXhairs()
02085 //
02086 // Draws graphic crosshairs at (x0, y0).  The first draw erases the old set.
02087 //--------------------------------------------------------------------------
02088 
02089 static void
02090 DrawXhairs( PLStream *pls, int x0, int y0 )
02091 {
02092     XwDev *dev = (XwDev *) pls->dev;
02093 
02094     int   xmin = 0, xmax = (int) dev->width - 1;
02095     int   ymin = 0, ymax = (int) dev->height - 1;
02096 
02097     if ( dev->drawing_xhairs )
02098         UpdateXhairs( pls );
02099 
02100     dev->xhair_x[0].x = (short) xmin; dev->xhair_x[0].y = (short) y0;
02101     dev->xhair_x[1].x = (short) xmax; dev->xhair_x[1].y = (short) y0;
02102 
02103     dev->xhair_y[0].x = (short) x0; dev->xhair_y[0].y = (short) ymin;
02104     dev->xhair_y[1].x = (short) x0; dev->xhair_y[1].y = (short) ymax;
02105 
02106     UpdateXhairs( pls );
02107 }
02108 
02109 //--------------------------------------------------------------------------
02110 // UpdateXhairs()
02111 //
02112 // Updates graphic crosshairs.  If already there, they are erased.
02113 //--------------------------------------------------------------------------
02114 
02115 static void
02116 UpdateXhairs( PLStream *pls )
02117 {
02118     XwDev     *dev = (XwDev *) pls->dev;
02119     XwDisplay *xwd = (XwDisplay *) dev->xwd;
02120 
02121     XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_x, 2,
02122         CoordModeOrigin );
02123 
02124     XDrawLines( xwd->display, dev->window, xwd->gcXor, dev->xhair_y, 2,
02125         CoordModeOrigin );
02126 }
02127 
02128 //--------------------------------------------------------------------------
02129 // ExposeEH()
02130 //
02131 // Event handler routine for expose events.
02132 // Front end to ExposeCmd() to deal with wierdnesses of Xlib.
02133 //--------------------------------------------------------------------------
02134 
02135 static void
02136 ExposeEH( PLStream *pls, XEvent *event )
02137 {
02138     XwDev        *dev         = (XwDev *) pls->dev;
02139     XwDisplay    *xwd         = (XwDisplay *) dev->xwd;
02140     XExposeEvent *exposeEvent = (XExposeEvent *) event;
02141     PLDisplay    pldis;
02142     int          redrawn;
02143 
02144     dbug_enter( "ExposeEH" );
02145 
02146     pldebug( "ExposeEH",
02147         "x = %d, y = %d, width = %d, height = %d, count = %d, pending = %d\n",
02148         exposeEvent->x, exposeEvent->y,
02149         exposeEvent->width, exposeEvent->height,
02150         exposeEvent->count, XPending( xwd->display ) );
02151 
02152 // Handle expose
02153 // If we have anything overlaid (like crosshairs), we need to refresh the
02154 // entire plot in order to have a predictable outcome.  In this case we
02155 // need to first clear window.  Otherwise it's better to not clear it, for a
02156 // smoother redraw (unobscured regions appear to stay the same).
02157 
02158     if ( dev->drawing_xhairs )
02159     {
02160         XClearWindow( xwd->display, dev->window );
02161         ExposeCmd( pls, NULL );
02162         UpdateXhairs( pls );
02163         redrawn = 1;
02164     }
02165     else
02166     {
02167         pldis.x      = (unsigned int) exposeEvent->x;
02168         pldis.y      = (unsigned int) exposeEvent->y;
02169         pldis.width  = (unsigned int) exposeEvent->width;
02170         pldis.height = (unsigned int) exposeEvent->height;
02171 
02172         ExposeCmd( pls, &pldis );
02173         redrawn = !dev->write_to_pixmap;
02174     }
02175 
02176 // If entire plot was redrawn, remove extraneous events from the queue
02177 
02178     if ( redrawn )
02179         while ( XCheckWindowEvent( xwd->display, dev->window,
02180                     ExposureMask | StructureNotifyMask, event ) )
02181             ;
02182 }
02183 
02184 //--------------------------------------------------------------------------
02185 // ResizeEH()
02186 //
02187 // Event handler routine for resize events.
02188 // Front end to ResizeCmd() to deal with wierdnesses of Xlib.
02189 //--------------------------------------------------------------------------
02190 
02191 static void
02192 ResizeEH( PLStream *pls, XEvent *event )
02193 {
02194     XwDev           *dev         = (XwDev *) pls->dev;
02195     XwDisplay       *xwd         = (XwDisplay *) dev->xwd;
02196     XConfigureEvent *configEvent = (XConfigureEvent *) event;
02197     PLDisplay       pldis;
02198 
02199     dbug_enter( "ResizeEH" );
02200 
02201     pldis.width  = (unsigned int) configEvent->width;
02202     pldis.height = (unsigned int) configEvent->height;
02203 
02204 // Only need to resize if size is actually changed
02205 
02206     if ( pldis.width == dev->width && pldis.height == dev->height )
02207         return;
02208 
02209     pldebug( "ResizeEH",
02210         "x = %d, y = %d, pending = %d\n",
02211         configEvent->width, configEvent->height, XPending( xwd->display ) );
02212 
02213 // Handle resize
02214 
02215     ResizeCmd( pls, &pldis );
02216     if ( dev->drawing_xhairs )
02217     {
02218         UpdateXhairs( pls );
02219     }
02220 
02221 // Remove extraneous Expose and ConfigureNotify events from the event queue
02222 // Exposes do not need to be handled since we've redrawn the whole plot
02223 
02224     XFlush( xwd->display );
02225     while ( XCheckWindowEvent( xwd->display, dev->window,
02226                 ExposureMask | StructureNotifyMask, event ) )
02227         ;
02228 }
02229 
02230 //--------------------------------------------------------------------------
02231 // ExposeCmd()
02232 //
02233 // Event handler routine for expose events.
02234 // These are "pure" exposures (no resize), so don't need to clear window.
02235 //--------------------------------------------------------------------------
02236 
02237 static void
02238 ExposeCmd( PLStream *pls, PLDisplay *pldis )
02239 {
02240     XwDev        *dev = (XwDev *) pls->dev;
02241     XwDisplay    *xwd = (XwDisplay *) dev->xwd;
02242     int          x, y;
02243     unsigned int width, height;
02244 
02245     dbug_enter( "ExposeCmd" );
02246 
02247 // Return if plD_init_xw hasn't been called yet
02248 
02249     if ( dev == NULL )
02250     {
02251         plwarn( "ExposeCmd: Illegal call -- driver uninitialized" );
02252         return;
02253     }
02254 
02255 // Exposed area.  If unspecified, the entire window is used.
02256 
02257     if ( pldis == NULL )
02258     {
02259         x      = 0;
02260         y      = 0;
02261         width  = dev->width;
02262         height = dev->height;
02263     }
02264     else
02265     {
02266         x      = (int) pldis->x;
02267         y      = (int) pldis->y;
02268         width  = pldis->width;
02269         height = pldis->height;
02270     }
02271 
02272 // Usual case: refresh window from pixmap
02273 // DEBUG option: draws rectangle around refreshed region
02274 
02275     XSync( xwd->display, 0 );
02276     if ( dev->write_to_pixmap )
02277     {
02278         XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc,
02279             x, y, width, height, x, y );
02280         XSync( xwd->display, 0 );
02281 #ifdef DEBUG
02282         if ( pls->debug )
02283         {
02284             XPoint pts[5];
02285             int    x0 = x, x1 = x + (int) width, y0 = y, y1 = y + (int) height;
02286             pts[0].x = (short) x0; pts[0].y = (short) y0;
02287             pts[1].x = (short) x1; pts[1].y = (short) y0;
02288             pts[2].x = (short) x1; pts[2].y = (short) y1;
02289             pts[3].x = (short) x0; pts[3].y = (short) y1;
02290             pts[4].x = (short) x0; pts[4].y = (short) y0;
02291 
02292             XDrawLines( xwd->display, dev->window, dev->gc, pts, 5,
02293                 CoordModeOrigin );
02294         }
02295 #endif
02296     }
02297     else
02298     {
02299         plRemakePlot( pls );
02300         XFlush( xwd->display );
02301     }
02302 }
02303 
02304 //--------------------------------------------------------------------------
02305 // ResizeCmd()
02306 //
02307 // Event handler routine for resize events.
02308 //--------------------------------------------------------------------------
02309 
02310 static void
02311 ResizeCmd( PLStream *pls, PLDisplay *pldis )
02312 {
02313     XwDev     *dev            = (XwDev *) pls->dev;
02314     XwDisplay *xwd            = (XwDisplay *) dev->xwd;
02315     int       write_to_window = dev->write_to_window;
02316 
02317     dbug_enter( "ResizeCmd" );
02318 
02319 // Return if plD_init_xw hasn't been called yet
02320 
02321     if ( dev == NULL )
02322     {
02323         plwarn( "ResizeCmd: Illegal call -- driver uninitialized" );
02324         return;
02325     }
02326 
02327 // Return if pointer to window not specified.
02328 
02329     if ( pldis == NULL )
02330     {
02331         plwarn( "ResizeCmd: Illegal call -- window pointer uninitialized" );
02332         return;
02333     }
02334 
02335 // Reset current window bounds
02336 
02337     dev->width  = pldis->width;
02338     dev->height = pldis->height;
02339 
02340     dev->xscale = dev->width / (double) dev->init_width;
02341     dev->yscale = dev->height / (double) dev->init_height;
02342 
02343     dev->xscale = dev->xscale * dev->xscale_init;
02344     dev->yscale = dev->yscale * dev->yscale_init;
02345 
02346 #if PHYSICAL
02347     {
02348         PLFLT pxlx = DPMM / dev->xscale;
02349         PLFLT pxly = DPMM / dev->yscale;
02350         plP_setpxl( pxlx, pxly );
02351     }
02352 #endif
02353 
02354 // Note: the following order MUST be obeyed -- if you instead redraw into
02355 // the window and then copy it to the pixmap, off-screen parts of the window
02356 // may contain garbage which is then transferred to the pixmap (and thus
02357 // will not go away after an expose).
02358 //
02359 
02360 // Resize pixmap using new dimensions
02361 
02362     if ( dev->write_to_pixmap )
02363     {
02364         dev->write_to_window = 0;
02365         XFreePixmap( xwd->display, dev->pixmap );
02366         CreatePixmap( pls );
02367     }
02368 
02369 // This allows an external agent to take over the redraw
02370     if ( pls->ext_resize_draw )
02371         return;
02372 
02373 // Initialize & redraw (to pixmap, if available).
02374 
02375     if ( dev->write_to_pixmap )
02376     {
02377         XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
02378         XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
02379             dev->width, dev->height );
02380         XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
02381     }
02382     if ( dev->write_to_window )
02383     {
02384         XClearWindow( xwd->display, dev->window );
02385     }
02386     plRemakePlot( pls );
02387     XSync( xwd->display, 0 );
02388 
02389 // If pixmap available, fake an expose
02390 
02391     if ( dev->write_to_pixmap )
02392     {
02393         dev->write_to_window = write_to_window;
02394         XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
02395             dev->width, dev->height, 0, 0 );
02396         XSync( xwd->display, 0 );
02397     }
02398 }
02399 
02400 //--------------------------------------------------------------------------
02401 // ConfigBufferingCmd()
02402 //
02403 // Allows a widget to manipulate the double buffering support in the
02404 // xwin dirver.
02405 //--------------------------------------------------------------------------
02406 
02407 static void ConfigBufferingCmd( PLStream *pls, PLBufferingCB *ptr )
02408 {
02409     XwDev *dev = (XwDev *) pls->dev;
02410 
02411     switch ( ptr->cmd )
02412     {
02413     case PLESC_DOUBLEBUFFERING_ENABLE:
02414         dev->write_to_window = 0;
02415         pls->db = 1;
02416         break;
02417 
02418     case PLESC_DOUBLEBUFFERING_DISABLE:
02419         dev->write_to_window = 1;
02420         pls->db = 0;
02421         break;
02422 
02423     case PLESC_DOUBLEBUFFERING_QUERY:
02424         ptr->result = pls->db;
02425         break;
02426 
02427     default:
02428         printf( "Unrecognized buffering request ignored.\n" );
02429         break;
02430     }
02431 }
02432 
02433 //--------------------------------------------------------------------------
02434 // RedrawCmd()
02435 //
02436 // Handles page redraw without resize (pixmap does not get reallocated).
02437 // Calling this makes sure all necessary housekeeping gets done.
02438 //--------------------------------------------------------------------------
02439 
02440 static void
02441 RedrawCmd( PLStream *pls )
02442 {
02443     XwDev     *dev            = (XwDev *) pls->dev;
02444     XwDisplay *xwd            = (XwDisplay *) dev->xwd;
02445     int       write_to_window = dev->write_to_window;
02446 
02447     dbug_enter( "RedrawCmd" );
02448 
02449 // Return if plD_init_xw hasn't been called yet
02450 
02451     if ( dev == NULL )
02452     {
02453         plwarn( "RedrawCmd: Illegal call -- driver uninitialized" );
02454         return;
02455     }
02456 
02457 // Initialize & redraw (to pixmap, if available).
02458 
02459     if ( dev->write_to_pixmap )
02460     {
02461         dev->write_to_window = 0;
02462         XSetForeground( xwd->display, dev->gc, dev->bgcolor.pixel );
02463         XFillRectangle( xwd->display, dev->pixmap, dev->gc, 0, 0,
02464             dev->width, dev->height );
02465         XSetForeground( xwd->display, dev->gc, dev->curcolor.pixel );
02466     }
02467     if ( dev->write_to_window )
02468     {
02469         XClearWindow( xwd->display, dev->window );
02470     }
02471     plRemakePlot( pls );
02472     XSync( xwd->display, 0 );
02473 
02474     dev->write_to_window = write_to_window;
02475 
02476 // If pixmap available, fake an expose
02477 
02478     if ( dev->write_to_pixmap )
02479     {
02480         XCopyArea( xwd->display, dev->pixmap, dev->window, dev->gc, 0, 0,
02481             dev->width, dev->height, 0, 0 );
02482         XSync( xwd->display, 0 );
02483     }
02484 }
02485 
02486 //--------------------------------------------------------------------------
02487 // CreatePixmapErrorHandler()
02488 //
02489 // Error handler used in CreatePixmap() to catch errors in allocating
02490 // storage for pixmap.  This way we can nicely substitute redraws for
02491 // pixmap copies if the server has insufficient memory.
02492 //--------------------------------------------------------------------------
02493 
02494 static unsigned char CreatePixmapStatus;
02495 
02496 static int
02497 CreatePixmapErrorHandler( Display *display, XErrorEvent *error )
02498 {
02499     CreatePixmapStatus = error->error_code;
02500     if ( error->error_code != BadAlloc )
02501     {
02502         char buffer[256];
02503         XGetErrorText( display, error->error_code, buffer, 256 );
02504         fprintf( stderr, "Error in XCreatePixmap: %s.\n", buffer );
02505     }
02506     return 1;
02507 }
02508 
02509 //--------------------------------------------------------------------------
02510 // CreatePixmap()
02511 //
02512 // This routine creates a pixmap, doing error trapping in case there
02513 // isn't enough memory on the server.
02514 //--------------------------------------------------------------------------
02515 
02516 static void
02517 CreatePixmap( PLStream *pls )
02518 {
02519     XwDev     *dev = (XwDev *) pls->dev;
02520     XwDisplay *xwd = (XwDisplay *) dev->xwd;
02521 
02522     int       ( *oldErrorHandler )( Display *, XErrorEvent * );
02523 
02524     oldErrorHandler = XSetErrorHandler( CreatePixmapErrorHandler );
02525 
02526     CreatePixmapStatus = Success;
02527     pldebug( "CreatePixmap",
02528         "creating pixmap: width = %d, height = %d, depth = %d\n",
02529         dev->width, dev->height, xwd->depth );
02530 
02531     dev->pixmap = XCreatePixmap( xwd->display, dev->window,
02532         dev->width, dev->height, xwd->depth );
02533     XSync( xwd->display, 0 );
02534     if ( CreatePixmapStatus != Success )
02535     {
02536         dev->write_to_pixmap = 0;
02537         dev->write_to_window = 1;
02538         pls->db = 0;
02539         fprintf( stderr, "\n\
02540 Warning: pixmap could not be allocated (insufficient memory on server).\n\
02541 Driver will redraw the entire plot to handle expose events.\n" );
02542     }
02543 
02544     XSetErrorHandler( oldErrorHandler );
02545 }
02546 
02547 //--------------------------------------------------------------------------
02548 // GetVisual()
02549 //
02550 // Get visual info.  In order to safely use a visual other than that of
02551 // the parent (which hopefully is that returned by DefaultVisual), you
02552 // must first find (using XGetRGBColormaps) or create a colormap matching
02553 // this visual and then set the colormap window attribute in the
02554 // XCreateWindow attributes and valuemask arguments.  I don't do this
02555 // right now, so this is turned off by default.
02556 //--------------------------------------------------------------------------
02557 
02558 static void
02559 GetVisual( PLStream *pls )
02560 {
02561     XwDev     *dev            = (XwDev *) pls->dev;
02562     XwDisplay *xwd            = (XwDisplay *) dev->xwd;
02563     int       visuals_matched = 0;
02564 
02565     dbug_enter( "GetVisual" );
02566 
02567     if ( !defaultvisual )
02568     {
02569         XVisualInfo vTemplate, *visualList;
02570 
02571 // Try for an 8 plane display, if unavailable go for the default
02572 
02573         vTemplate.screen = xwd->screen;
02574         vTemplate.depth  = 8;
02575 
02576         visualList = XGetVisualInfo( xwd->display,
02577             VisualScreenMask | VisualDepthMask,
02578             &vTemplate, &visuals_matched );
02579 
02580 #ifdef HACK_STATICCOLOR
02581         if ( visuals_matched )
02582         {
02583             int i, found = 0;
02584             printf( "visuals_matched = %d\n", visuals_matched );
02585             for ( i = 0; i < visuals_matched && !found; i++ )
02586             {
02587                 Visual *v = visualList[i].visual;
02588                 printf( "Checking visual %d: ", i );
02589                 switch ( v->class )
02590                 {
02591                 case PseudoColor:
02592                     printf( "PseudoColor\n" );
02593                     break;
02594                 case GrayScale:
02595                     printf( "GrayScale\n" );
02596                     break;
02597                 case DirectColor:
02598                     printf( "DirectColor\n" );
02599                     break;
02600                 case TrueColor:
02601                     printf( "TrueColor\n" );
02602                     break;
02603                 case StaticColor:
02604                     printf( "StaticColor\n" );
02605                     break;
02606                 case StaticGray:
02607                     printf( "StaticGray\n" );
02608                     break;
02609                 default:
02610                     printf( "Unknown.\n" );
02611                     break;
02612                 }
02613                 if ( v->class == StaticColor )
02614                 {
02615                     xwd->visual = v;
02616                     xwd->depth  = visualList[i].depth;
02617                     found       = 1;
02618                 }
02619             }
02620             if ( !found )
02621             {
02622                 plexit( "Unable to get a StaticColor visual." );
02623             }
02624             printf( "Found StaticColor visual, depth=%d\n", xwd->depth );
02625         }
02626 #else
02627         if ( visuals_matched )
02628         {
02629             xwd->visual = visualList->visual;   // Choose first match.
02630             xwd->depth  = (unsigned int) vTemplate.depth;
02631         }
02632 #endif  // HACK_STATICCOLOR
02633     }
02634 
02635     if ( !visuals_matched )
02636     {
02637         xwd->visual = DefaultVisual( xwd->display, xwd->screen );
02638         xwd->depth  = (unsigned int) DefaultDepth( xwd->display, xwd->screen );
02639     }
02640 
02641 // Check to see if we expect to be able to allocate r/w color cells.
02642 
02643     switch ( xwd->visual->class )
02644     {
02645     case TrueColor:
02646     case StaticColor:
02647     case StaticGray:
02648         xwd->rw_cmap = 0;
02649         break;
02650     default:
02651         xwd->rw_cmap = 1;
02652     }
02653 
02654 /*xwd->rw_cmap = 0;*/ /* debugging hack. */
02655 
02656 // Just for kicks, see what kind of visual we got.
02657 
02658     if ( pls->verbose )
02659     {
02660         fprintf( stderr, "XVisual class == " );
02661         switch ( xwd->visual->class )
02662         {
02663         case PseudoColor:
02664             fprintf( stderr, "PseudoColor\n" );
02665             break;
02666         case GrayScale:
02667             fprintf( stderr, "GrayScale\n" );
02668             break;
02669         case DirectColor:
02670             fprintf( stderr, "DirectColor\n" );
02671             break;
02672         case TrueColor:
02673             fprintf( stderr, "TrueColor\n" );
02674             break;
02675         case StaticColor:
02676             fprintf( stderr, "StaticColor\n" );
02677             break;
02678         case StaticGray:
02679             fprintf( stderr, "StaticGray\n" );
02680             break;
02681         default:
02682             fprintf( stderr, "Unknown.\n" );
02683             break;
02684         }
02685         fprintf( stderr, "xwd->rw_cmap = %d\n", xwd->rw_cmap );
02686     }
02687 }
02688 
02689 //--------------------------------------------------------------------------
02690 // AllocBGFG()
02691 //
02692 // Allocate background & foreground colors.  If possible, I choose pixel
02693 // values such that the fg pixel is the xor of the bg pixel, to make
02694 // rubber-banding easy to see.
02695 //--------------------------------------------------------------------------
02696 
02697 static void
02698 AllocBGFG( PLStream *pls )
02699 {
02700     XwDev         *dev = (XwDev *) pls->dev;
02701     XwDisplay     *xwd = (XwDisplay *) dev->xwd;
02702 
02703     int           i, j, npixels;
02704     unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
02705 
02706     dbug_enter( "AllocBGFG" );
02707 
02708 // If not on a color system, just return
02709     if ( !xwd->color )
02710         return;
02711 
02712     if ( xwd->rw_cmap &&
02713          // r/w color maps
02714          XAllocColorCells( xwd->display, xwd->map, False,
02715              plane_masks, 0, pixels, 1 ) )
02716     {
02717         // background
02718         xwd->cmap0[0].pixel = pixels[0];
02719     }
02720     else
02721     {
02722         // r/o color maps
02723         xwd->cmap0[0].pixel = BlackPixel( xwd->display, xwd->screen );
02724         xwd->fgcolor.pixel  = WhitePixel( xwd->display, xwd->screen );
02725         if ( xwd->rw_cmap && pls->verbose )
02726             fprintf( stderr, "Downgrading to r/o cmap.\n" );
02727         xwd->rw_cmap = 0;
02728         return;
02729     }
02730 
02731 // Allocate as many colors as we can
02732 
02733     npixels = RWMAP_MAX_COLORS;
02734     for (;; )
02735     {
02736         if ( XAllocColorCells( xwd->display, xwd->map, False,
02737                  plane_masks, 0, pixels, (unsigned int) npixels ) )
02738             break;
02739         npixels--;
02740         if ( npixels == 0 )
02741             break;
02742     }
02743 
02744 // Find the color with pixel = xor of the bg color pixel.
02745 // If a match isn't found, the last pixel allocated is used.
02746 
02747     for ( i = 0; i < npixels - 1; i++ )
02748     {
02749         if ( pixels[i] == ( ~xwd->cmap0[0].pixel & 0xFF ) )
02750             break;
02751     }
02752 
02753 // Use this color cell for our foreground color.  Then free the rest.
02754 
02755     xwd->fgcolor.pixel = pixels[i];
02756     for ( j = 0; j < npixels; j++ )
02757     {
02758         if ( j != i )
02759             XFreeColors( xwd->display, xwd->map, &pixels[j], 1, 0 );
02760     }
02761 }
02762 
02763 //--------------------------------------------------------------------------
02764 // SetBGFG()
02765 //
02766 // Set background & foreground colors.  Foreground over background should
02767 // have high contrast.
02768 //--------------------------------------------------------------------------
02769 
02770 static void
02771 SetBGFG( PLStream *pls )
02772 {
02773     XwDev        *dev = (XwDev *) pls->dev;
02774     XwDisplay    *xwd = (XwDisplay *) dev->xwd;
02775 
02776     PLColor      fgcolor;
02777     unsigned int gslevbg, gslevfg;
02778 
02779     dbug_enter( "SetBGFG" );
02780 
02781 //
02782 // Set background color.
02783 //
02784 // Background defaults to black on color screens, white on grayscale (many
02785 // grayscale monitors have poor contrast, and black-on-white looks better).
02786 //
02787 
02788     if ( !xwd->color )
02789     {
02790         pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
02791     }
02792     gslevbg = (unsigned int) ( ( (long) pls->cmap0[0].r +
02793                                  (long) pls->cmap0[0].g +
02794                                  (long) pls->cmap0[0].b ) / 3 );
02795 
02796     PLColor_to_XColor( &pls->cmap0[0], &xwd->cmap0[0] );
02797 
02798 //
02799 // Set foreground color.
02800 //
02801 // Used for grayscale output, since otherwise the plots can become nearly
02802 // unreadable (i.e. if colors get mapped onto grayscale values).  In this
02803 // case it becomes the grayscale level for all draws, and is taken to be
02804 // black if the background is light, and white if the background is dark.
02805 // Note that white/black allocations never fail.
02806 //
02807 
02808     if ( gslevbg > 0x7F )
02809         gslevfg = 0;
02810     else
02811         gslevfg = 0xFF;
02812 
02813     fgcolor.r = fgcolor.g = fgcolor.b = (unsigned char) gslevfg;
02814 
02815     PLColor_to_XColor( &fgcolor, &xwd->fgcolor );
02816 
02817 // Now store
02818 
02819     if ( xwd->rw_cmap && xwd->color )
02820     {
02821         XStoreColor( xwd->display, xwd->map, &xwd->fgcolor );
02822         XStoreColor( xwd->display, xwd->map, &xwd->cmap0[0] );
02823     }
02824     else
02825     {
02826         XAllocColor( xwd->display, xwd->map, &xwd->cmap0[0] );
02827         XAllocColor( xwd->display, xwd->map, &xwd->fgcolor );
02828     }
02829 }
02830 
02831 //--------------------------------------------------------------------------
02832 // InitColors()
02833 //
02834 // Does all color initialization.
02835 //--------------------------------------------------------------------------
02836 
02837 static void
02838 InitColors( PLStream *pls )
02839 {
02840     XwDev     *dev = (XwDev *) pls->dev;
02841     XwDisplay *xwd = (XwDisplay *) dev->xwd;
02842 
02843     dbug_enter( "InitColors" );
02844 
02845 // Allocate and initialize color maps.
02846 // Defer cmap1 allocation until it's actually used
02847 
02848     if ( xwd->color )
02849     {
02850         if ( plplot_ccmap )
02851         {
02852             AllocCustomMap( pls );
02853         }
02854         else
02855         {
02856             AllocCmap0( pls );
02857         }
02858     }
02859 }
02860 
02861 //--------------------------------------------------------------------------
02862 // AllocCustomMap()
02863 //
02864 // Initializes custom color map and all the cruft that goes with it.
02865 //
02866 // Assuming all color X displays do 256 colors, the breakdown is as follows:
02867 //
02868 // CCMAP_XWM_COLORS
02869 //      Number of low "pixel" values to copy.  These are typically allocated
02870 //      first, thus are in use by the window manager. I copy them to reduce
02871 //      flicker.
02872 //
02873 //
02874 // RWMAP_CMAP1_COLORS
02875 //      Color map 1 entries.  There should be as many as practical available
02876 //      for smooth shading.  On the order of 50-100 is pretty reasonable.  You
02877 //      don't really need all 256, especially if all you're going to do is to
02878 //      print it to postscript (which doesn't have any intrinsic limitation on
02879 //      the number of colors).
02880 //
02881 // It's important to leave some extra colors unallocated for Tk.  In
02882 // particular the palette tools require a fair amount.  I recommend leaving
02883 // at least 40 or so free.
02884 //--------------------------------------------------------------------------
02885 
02886 static void
02887 AllocCustomMap( PLStream *pls )
02888 {
02889     XwDev         *dev = (XwDev *) pls->dev;
02890     XwDisplay     *xwd = (XwDisplay *) dev->xwd;
02891 
02892     XColor        xwm_colors[RWMAP_MAX_COLORS];
02893     int           i, npixels;
02894     unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
02895 
02896     dbug_enter( "AllocCustomMap" );
02897 
02898 // Determine current default colors
02899 
02900     for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
02901     {
02902         xwm_colors[i].pixel = (unsigned long int) i;
02903     }
02904     XQueryColors( xwd->display, xwd->map, xwm_colors, RWMAP_MAX_COLORS );
02905 
02906 // Allocate cmap0 colors in the default colormap.
02907 // The custom cmap0 colors are later stored at the same pixel values.
02908 // This is a really cool trick to reduce the flicker when changing colormaps.
02909 //
02910 
02911     AllocCmap0( pls );
02912     XAllocColor( xwd->display, xwd->map, &xwd->fgcolor );
02913 
02914 // Create new color map
02915 
02916     xwd->map = XCreateColormap( xwd->display, DefaultRootWindow( xwd->display ),
02917         xwd->visual, AllocNone );
02918 
02919 // Now allocate all colors so we can fill the ones we want to copy
02920 
02921     npixels = RWMAP_MAX_COLORS;
02922     for (;; )
02923     {
02924         if ( XAllocColorCells( xwd->display, xwd->map, False,
02925                  plane_masks, 0, pixels, (unsigned int) npixels ) )
02926             break;
02927         npixels--;
02928         if ( npixels == 0 )
02929             plexit( "couldn't allocate any colors" );
02930     }
02931 
02932 // Fill the low colors since those are in use by the window manager
02933 
02934     for ( i = 0; i < CCMAP_XWM_COLORS; i++ )
02935     {
02936         XStoreColor( xwd->display, xwd->map, &xwm_colors[i] );
02937         pixels[xwm_colors[i].pixel] = 0;
02938     }
02939 
02940 // Fill the ones we will use in cmap0
02941 
02942     for ( i = 0; i < xwd->ncol0; i++ )
02943     {
02944         XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] );
02945         pixels[xwd->cmap0[i].pixel] = 0;
02946     }
02947 
02948 // Finally, if the colormap was saved by an external agent, see if there are
02949 // any differences from the current default map and save those!  A very cool
02950 // (or sick, depending on how you look at it) trick to get over some X and
02951 // Tk limitations.
02952 //
02953 
02954     if ( sxwm_colors_set )
02955     {
02956         for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
02957         {
02958             if ( ( xwm_colors[i].red != sxwm_colors[i].red ) ||
02959                  ( xwm_colors[i].green != sxwm_colors[i].green ) ||
02960                  ( xwm_colors[i].blue != sxwm_colors[i].blue ) )
02961             {
02962                 if ( pixels[i] != 0 )
02963                 {
02964                     XStoreColor( xwd->display, xwd->map, &xwm_colors[i] );
02965                     pixels[i] = 0;
02966                 }
02967             }
02968         }
02969     }
02970 
02971 // Now free the ones we're not interested in
02972 
02973     for ( i = 0; i < npixels; i++ )
02974     {
02975         if ( pixels[i] != 0 )
02976             XFreeColors( xwd->display, xwd->map, &pixels[i], 1, 0 );
02977     }
02978 
02979 // Allocate colors in cmap 1
02980 
02981     AllocCmap1( pls );
02982 }
02983 
02984 //--------------------------------------------------------------------------
02985 // AllocCmap0()
02986 //
02987 // Allocate & initialize cmap0 entries.
02988 //--------------------------------------------------------------------------
02989 
02990 static void
02991 AllocCmap0( PLStream *pls )
02992 {
02993     XwDev     *dev = (XwDev *) pls->dev;
02994     XwDisplay *xwd = (XwDisplay *) dev->xwd;
02995     int       i;
02996 
02997     dbug_enter( "AllocCmap0" );
02998 
02999 // Free all previous colors.  This should work for both rw & ro colormaps
03000     for ( i = 1; i < xwd->ncol0; i++ )
03001     {
03002         unsigned long pixel = xwd->cmap0[i].pixel;
03003         XFreeColors( xwd->display, xwd->map, &pixel, 1, 0 );
03004     }
03005 
03006 // If the number of colors increased, need to allocate enough space for them
03007     if ( pls->ncol0 > xwd->ncol0_alloc )
03008     {
03009         xwd->ncol0_alloc = pls->ncol0;
03010         xwd->cmap0       = (XColor *)
03011                            realloc( xwd->cmap0, (size_t) pls->ncol0 * sizeof ( XColor ) );
03012         if ( xwd->cmap0 == 0 )
03013             plexit( "couldn't allocate space for cmap0 colors" );
03014     }
03015 
03016     if ( xwd->rw_cmap )
03017     {
03018         int           npixels;
03019         unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
03020 
03021         // Allocate and assign colors in cmap 0
03022 
03023         npixels = pls->ncol0 - 1;
03024         for (;; )
03025         {
03026             if ( XAllocColorCells( xwd->display, xwd->map, False,
03027                      plane_masks, 0, &pixels[1], (unsigned int) npixels ) )
03028                 break;
03029             npixels--;
03030             if ( npixels == 0 )
03031                 plexit( "couldn't allocate any colors" );
03032         }
03033 
03034         xwd->ncol0 = npixels + 1;
03035         for ( i = 1; i < xwd->ncol0; i++ )
03036         {
03037             xwd->cmap0[i].pixel = pixels[i];
03038         }
03039 
03040         StoreCmap0( pls );
03041     }
03042     else
03043     {
03044         if ( pls->verbose )
03045             fprintf( stderr, "Attempting to allocate r/o colors in cmap0.\n" );
03046 
03047         for ( i = 1; i < pls->ncol0; i++ )
03048         {
03049             int    r;
03050             XColor c;
03051             PLColor_to_XColor( &pls->cmap0[i], &c );
03052             r = XAllocColor( xwd->display, xwd->map, &c );
03053             if ( pls->verbose )
03054                 fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) c.pixel );
03055             if ( r )
03056             {
03057                 xwd->cmap0[i]       = c;
03058                 xwd->cmap0[i].pixel = c.pixel; // needed for deallocation
03059             }
03060             else
03061             {
03062                 XColor screen_def, exact_def;
03063 
03064                 if ( pls->verbose )
03065                     fprintf( stderr,
03066                         "color alloc failed, trying by name: %s.\n",
03067                         pls->cmap0[i].name );
03068 
03069                 // Hmm, didn't work, try another approach.
03070                 r = XAllocNamedColor( xwd->display, xwd->map,
03071                     pls->cmap0[i].name,
03072                     &screen_def, &exact_def );
03073 
03074 //                 xwd->cmap0[i] = screen_def;
03075 
03076                 if ( r )
03077                 {
03078                     if ( pls->verbose )
03079                         fprintf( stderr, "yes, got a color by name.\n" );
03080 
03081                     xwd->cmap0[i]       = screen_def;
03082                     xwd->cmap0[i].pixel = screen_def.pixel;
03083                 }
03084                 else
03085                 {
03086                     r = XAllocNamedColor( xwd->display, xwd->map,
03087                         "white",
03088                         &screen_def, &exact_def );
03089                     if ( r )
03090                     {
03091                         xwd->cmap0[i]       = screen_def;
03092                         xwd->cmap0[i].pixel = screen_def.pixel;
03093                     }
03094                     else
03095                         printf( "Can't find white?! Giving up...\n" );
03096                 }
03097             }
03098         }
03099         xwd->ncol0 = i;
03100 
03101         if ( pls->verbose )
03102             fprintf( stderr, "Allocated %d colors in cmap0.\n", xwd->ncol0 );
03103     }
03104 }
03105 
03106 //--------------------------------------------------------------------------
03107 // AllocCmap1()
03108 //
03109 // Allocate & initialize cmap1 entries.
03110 //--------------------------------------------------------------------------
03111 
03112 static void
03113 AllocCmap1( PLStream *pls )
03114 {
03115     XwDev         *dev = (XwDev *) pls->dev;
03116     XwDisplay     *xwd = (XwDisplay *) dev->xwd;
03117 
03118     int           i, j, npixels;
03119     unsigned long plane_masks[1], pixels[RWMAP_MAX_COLORS];
03120 
03121     dbug_enter( "AllocCmap1" );
03122 
03123     if ( xwd->rw_cmap )
03124     {
03125         if ( pls->verbose )
03126             fprintf( stderr, "Attempting to allocate r/w colors in cmap1.\n" );
03127 
03128         // If using the default color map, must severely limit number of colors
03129         // otherwise TK won't have enough.
03130 
03131         npixels = MAX( 2, MIN( RWMAP_CMAP1_COLORS, pls->ncol1 ) );
03132         for (;; )
03133         {
03134             if ( XAllocColorCells( xwd->display, xwd->map, False,
03135                      plane_masks, 0, pixels, (unsigned int) npixels ) )
03136                 break;
03137             npixels--;
03138             if ( npixels == 0 )
03139                 break;
03140         }
03141 
03142         if ( npixels < 2 )
03143         {
03144             xwd->ncol1 = -1;
03145             fprintf( stderr, "Warning: unable to allocate sufficient colors in cmap1.\n" );
03146             return;
03147         }
03148 
03149         xwd->ncol1 = npixels;
03150         if ( pls->verbose )
03151             fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1.\n", npixels );
03152 
03153         // Allocate space if it hasn't been done yet
03154         if ( !xwd->cmap1 )
03155         {
03156             xwd->ncol1_alloc = xwd->ncol1;
03157             xwd->cmap1       = (XColor *) calloc( (size_t) ( xwd->ncol1 ), sizeof ( XColor ) );
03158             if ( !xwd->cmap1 )
03159                 plexit( "couldn't allocate space for cmap1 colors" );
03160         }
03161 
03162         // Don't assign pixels sequentially, to avoid strange problems with xor
03163         // GC's.  Skipping by 2 seems to do the job best.
03164 
03165         for ( j = i = 0; i < xwd->ncol1; i++ )
03166         {
03167             while ( pixels[j] == 0 )
03168                 j++;
03169 
03170             xwd->cmap1[i].pixel = pixels[j];
03171             pixels[j]           = 0;
03172 
03173             j += 2;
03174             if ( j >= xwd->ncol1 )
03175                 j = 0;
03176         }
03177 
03178         StoreCmap1( pls );
03179     }
03180     else
03181     {
03182         int     r, ncolors;
03183         PLColor cmap1color;
03184         XColor  xcol;
03185 
03186         if ( pls->verbose )
03187             fprintf( stderr, "Attempting to allocate r/o colors in cmap1.\n" );
03188 
03189         switch ( xwd->visual->class )
03190         {
03191         case TrueColor:
03192             ncolors = TC_CMAP1_COLORS;
03193             break;
03194         default:
03195             ncolors = ROMAP_CMAP1_COLORS;
03196         }
03197 
03198         // Allocate space if it hasn't been done yet
03199         if ( !xwd->cmap1 )
03200         {
03201             xwd->ncol1_alloc = ncolors;
03202             xwd->cmap1       = (XColor *) calloc( (size_t) ncolors, sizeof ( XColor ) );
03203             if ( !xwd->cmap1 )
03204                 plexit( "couldn't allocate space for cmap1 colors" );
03205         }
03206 
03207         for ( i = 0; i < ncolors; i++ )
03208         {
03209             plcol_interp( pls, &cmap1color, i, ncolors );
03210             PLColor_to_XColor( &cmap1color, &xcol );
03211 
03212             r = XAllocColor( xwd->display, xwd->map, &xcol );
03213             if ( pls->verbose )
03214                 fprintf( stderr, "i=%d, r=%d, pixel=%d\n", i, r, (int) xcol.pixel );
03215             if ( r )
03216                 xwd->cmap1[i] = xcol;
03217             else
03218                 break;
03219         }
03220         if ( i < ncolors )
03221         {
03222             xwd->ncol1 = -1;
03223             fprintf( stderr,
03224                 "Warning: unable to allocate sufficient colors in cmap1\n" );
03225             return;
03226         }
03227         else
03228         {
03229             xwd->ncol1 = ncolors;
03230             if ( pls->verbose )
03231                 fprintf( stderr, "AllocCmap1 (xwin.c): Allocated %d colors in cmap1\n", ncolors );
03232         }
03233     }
03234 }
03235 
03236 //--------------------------------------------------------------------------
03237 // StoreCmap0()
03238 //
03239 // Stores cmap 0 entries in X-server colormap.
03240 //--------------------------------------------------------------------------
03241 
03242 static void
03243 StoreCmap0( PLStream *pls )
03244 {
03245     XwDev     *dev = (XwDev *) pls->dev;
03246     XwDisplay *xwd = (XwDisplay *) dev->xwd;
03247     int       i;
03248 
03249     if ( !xwd->color )
03250         return;
03251 
03252     for ( i = 1; i < xwd->ncol0; i++ )
03253     {
03254         PLColor_to_XColor( &pls->cmap0[i], &xwd->cmap0[i] );
03255         if ( xwd->rw_cmap )
03256             XStoreColor( xwd->display, xwd->map, &xwd->cmap0[i] );
03257         else
03258             XAllocColor( xwd->display, xwd->map, &xwd->cmap0[i] );
03259     }
03260 }
03261 
03262 //--------------------------------------------------------------------------
03263 // StoreCmap1()
03264 //
03265 // Stores cmap 1 entries in X-server colormap.
03266 //--------------------------------------------------------------------------
03267 
03268 static void
03269 StoreCmap1( PLStream *pls )
03270 {
03271     XwDev     *dev = (XwDev *) pls->dev;
03272     XwDisplay *xwd = (XwDisplay *) dev->xwd;
03273 
03274     PLColor   cmap1color;
03275     int       i;
03276 
03277     if ( !xwd->color )
03278         return;
03279 
03280     for ( i = 0; i < xwd->ncol1; i++ )
03281     {
03282         plcol_interp( pls, &cmap1color, i, xwd->ncol1 );
03283         PLColor_to_XColor( &cmap1color, &xwd->cmap1[i] );
03284         if ( xwd->rw_cmap )
03285             XStoreColor( xwd->display, xwd->map, &xwd->cmap1[i] );
03286         else
03287             XAllocColor( xwd->display, xwd->map, &xwd->cmap1[i] );
03288     }
03289 }
03290 
03291 //--------------------------------------------------------------------------
03292 // PLColor_to_XColor()
03293 //
03294 // Copies the supplied PLColor to an XColor, padding with bits as necessary
03295 // (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
03296 // The argument types follow the same order as in the function name.
03297 //--------------------------------------------------------------------------
03298 
03299 #define ToXColor( a )     ( ( ( 0xFF & ( a ) ) << 8 ) | ( a ) )
03300 #define ToPLColor( a )    ( ( (U_LONG) a ) >> 8 )
03301 
03302 static void
03303 PLColor_to_XColor( PLColor *plcolor, XColor *xcolor )
03304 {
03305     xcolor->red   = (short unsigned) ToXColor( plcolor->r );
03306     xcolor->green = (short unsigned) ToXColor( plcolor->g );
03307     xcolor->blue  = (short unsigned) ToXColor( plcolor->b );
03308     xcolor->flags = DoRed | DoGreen | DoBlue;
03309 }
03310 
03311 //--------------------------------------------------------------------------
03312 // PLColor_from_XColor()
03313 //
03314 // Copies the supplied XColor to a PLColor, stripping off bits as
03315 // necessary.  See the previous routine for more info.
03316 //--------------------------------------------------------------------------
03317 
03318 static void
03319 PLColor_from_XColor( PLColor *plcolor, XColor *xcolor )
03320 {
03321     plcolor->r = (unsigned char) ToPLColor( xcolor->red );
03322     plcolor->g = (unsigned char) ToPLColor( xcolor->green );
03323     plcolor->b = (unsigned char) ToPLColor( xcolor->blue );
03324 }
03325 
03326 //--------------------------------------------------------------------------
03327 // AreWeGrayscale(Display *display)
03328 //
03329 // Determines if we're using a monochrome or grayscale device.
03330 // gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland.
03331 // Altered Andrew Ross 26-01-2004 to fix memory leak.
03332 //--------------------------------------------------------------------------
03333 
03334 static int
03335 AreWeGrayscale( Display *display )
03336 {
03337 #if defined ( __cplusplus ) || defined ( c_plusplus )
03338 #define THING    c_class
03339 #else
03340 #define THING    class
03341 #endif
03342 
03343     XVisualInfo *visuals;
03344     int         nitems, i, igray;
03345 
03346     // get a list of info on the visuals available
03347     visuals = XGetVisualInfo( display, 0, NULL, &nitems );
03348 
03349     igray = 1;
03350     // check the list looking for non-monochrome visual classes
03351     for ( i = 0; i < nitems; i++ )
03352         if ( ( visuals[i].THING != GrayScale ) &&
03353              ( visuals[i].THING != StaticGray ) )
03354         {
03355             igray = 0;
03356             break;
03357         }
03358 
03359     XFree( visuals );
03360     // if igray = 1 only StaticGray and GrayScale classes available
03361     return igray;
03362 }
03363 
03364 #ifdef DUMMY
03365 //--------------------------------------------------------------------------
03366 // SaveColormap()  **** DUMMY, NOT USED ANYMORE ***
03367 //
03368 // Saves RGB components of given colormap.
03369 // Used in an ugly hack to get past some X11R5 and TK limitations.
03370 // This isn't guaranteed to work under all circumstances, but hopefully
03371 // in the future there will be a nicer way to accomplish the same thing.
03372 //
03373 // Note: I tried using XCopyColormapAndFree to do the same thing, but under
03374 // HPUX 9.01/VUE/X11R5 at least it doesn't preserve the previous read-only
03375 // color cell allocations made by Tk.  Is this a bug?  Have to look at the
03376 // source to find out.
03377 //--------------------------------------------------------------------------
03378 
03379 static void
03380 SaveColormap( Display *display, Colormap colormap )
03381 {
03382     int i;
03383 
03384     if ( !plplot_ccmap )
03385         return;
03386 
03387     sxwm_colors_set = 1;
03388     for ( i = 0; i < RWMAP_MAX_COLORS; i++ )
03389     {
03390         sxwm_colors[i].pixel = i;
03391     }
03392     XQueryColors( display, colormap, sxwm_colors, RWMAP_MAX_COLORS );
03393 //
03394 //  printf("\nAt startup, default colors are: \n\n");
03395 //  for (i = 0; i < RWMAP_MAX_COLORS; i++) {
03396 //      printf(" i: %d,  pixel: %d,  r: %d,  g: %d,  b: %d\n",
03397 //             i, sxwm_colors[i].pixel,
03398 //             sxwm_colors[i].red, sxwm_colors[i].green, sxwm_colors[i].blue);
03399 //  }
03400 //
03401 }
03402 #endif
03403 
03404 //--------------------------------------------------------------------------
03405 // GetImageErrorHandler()
03406 //
03407 // Error handler used in XGetImage() to catch errors when pixmap or window
03408 // are not completely viewable.
03409 //--------------------------------------------------------------------------
03410 
03411 static int
03412 GetImageErrorHandler( Display *display, XErrorEvent *error )
03413 {
03414     if ( error->error_code != BadMatch )
03415     {
03416         char buffer[256];
03417         XGetErrorText( display, error->error_code, buffer, 256 );
03418         fprintf( stderr, "xwin: Error in XGetImage: %s.\n", buffer );
03419     }
03420     return 1;
03421 }
03422 
03423 //--------------------------------------------------------------------------
03424 // DrawImage()
03425 //
03426 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
03427 // Only solid color fill supported.
03428 //--------------------------------------------------------------------------
03429 
03430 static void
03431 DrawImage( PLStream *pls )
03432 {
03433     XwDev     *dev  = (XwDev *) pls->dev;
03434     XwDisplay *xwd  = (XwDisplay *) dev->xwd;
03435     XImage    *ximg = NULL;
03436     XColor    curcolor;
03437     PLINT     xmin, xmax, ymin, ymax, icol1;
03438 
03439     int       ( *oldErrorHandler )( Display *, XErrorEvent * );
03440 
03441     float     mlr, mtb;
03442     float     blt, brt, brb, blb;
03443     float     left, right;
03444     int       kx, ky;
03445     int       nx, ny, ix, iy;
03446     int       i, corners[4], r[4] = { 0, 0, 0, 0 };
03447 
03448     struct
03449     {
03450         float x, y;
03451     } Ppts[4];
03452 
03453     CheckForEvents( pls );
03454 
03455     xmin = (PLINT) ( dev->xscale * pls->imclxmin );
03456     xmax = (PLINT) ( dev->xscale * pls->imclxmax );
03457     ymin = (PLINT) ( dev->yscale * pls->imclymin );
03458     ymax = (PLINT) ( dev->yscale * pls->imclymax );
03459 
03460     nx = pls->dev_nptsX;
03461     ny = pls->dev_nptsY;
03462 
03463 // XGetImage() call fails if either the pixmap or window is not fully viewable!
03464     oldErrorHandler = XSetErrorHandler( GetImageErrorHandler );
03465 
03466     XFlush( xwd->display );
03467     if ( dev->write_to_pixmap )
03468         ximg = XGetImage( xwd->display, dev->pixmap, 0, 0, dev->width, dev->height,
03469             AllPlanes, ZPixmap );
03470 
03471     if ( dev->write_to_window )
03472         ximg = XGetImage( xwd->display, dev->window, 0, 0, dev->width, dev->height,
03473             AllPlanes, ZPixmap );
03474 
03475     XSetErrorHandler( oldErrorHandler );
03476 
03477     if ( ximg == NULL )
03478     {
03479         plabort( "Can't get image, the window must be partly off-screen, move it to fit screen" );
03480         return;
03481     }
03482 
03483     if ( xwd->ncol1 == 0 )
03484         AllocCmap1( pls );
03485     if ( xwd->ncol1 < 2 )
03486         return;
03487 
03488 // translate array for rotation
03489     switch ( (int) ( pls->diorot - 4. * floor( pls->diorot / 4. ) ) )
03490     {
03491     case 0:
03492         r[0] = 0; r[1] = 1; r[2] = 2; r[3] = 3; break;
03493     case 1:
03494         r[0] = 1; r[1] = 2; r[2] = 3; r[3] = 0; break;
03495     case 2:
03496         r[0] = 2; r[1] = 3; r[2] = 0; r[3] = 1; break;
03497     case 3:
03498         r[0] = 3; r[1] = 0; r[2] = 1; r[3] = 2;
03499     }
03500 
03501     // after rotation and coordinate translation, each fill
03502     // lozangue will have coordinates (Ppts), slopes (m...)
03503     // and y intercepts (b...):
03504     //
03505     //        Ppts[3]
03506     //          **
03507     // mlr,blt *  * mtb,brt
03508     //        *    *
03509     //Ppts[0]<      > Ppts[2]
03510     //        *    *
03511     // mtb,blt *  * mlr,brb
03512     //          **
03513     //        Ppts[1]
03514     //
03515 
03516 // slope of left/right and top/bottom edges
03517     mlr = (float) ( ( dev->yscale * ( pls->dev_iy[1] - pls->dev_iy[0] ) ) /
03518                     ( dev->xscale * ( pls->dev_ix[1] - pls->dev_ix[0] ) ) );
03519 
03520     mtb = (float) ( ( dev->yscale * ( pls->dev_iy[ny] - pls->dev_iy[0] ) ) /
03521                     ( dev->xscale * ( pls->dev_ix[ny] - pls->dev_ix[0] ) ) );
03522 
03523     for ( ix = 0; ix < nx - 1; ix++ )
03524     {
03525         for ( iy = 0; iy < ny - 1; iy++ )
03526         {
03527             corners[0] = ix * ny + iy;             // [ix][iy]
03528             corners[1] = ( ix + 1 ) * ny + iy;     // [ix+1][iy]
03529             corners[2] = ( ix + 1 ) * ny + iy + 1; // [ix+1][iy+1]
03530             corners[3] = ix * ny + iy + 1;         // [ix][iy+1]
03531 
03532             for ( i = 0; i < 4; i++ )
03533             {
03534                 Ppts[i].x = (float) ( dev->xscale * ( pls->dev_ix[corners[r[i]]] ) );
03535                 Ppts[i].y = (float) ( dev->yscale * ( pls->dev_iy[corners[r[i]]] ) );
03536             }
03537 
03538             // if any corner is inside the draw area
03539             if ( Ppts[0].x >= xmin || Ppts[2].x <= xmax ||
03540                  Ppts[1].y >= ymin || Ppts[3].y <= ymax )
03541             {
03542                 Ppts[0].x = MAX( Ppts[0].x, (float) xmin );
03543                 Ppts[2].x = MIN( Ppts[2].x, (float) xmax );
03544                 Ppts[1].y = MAX( Ppts[1].y, (float) ymin );
03545                 Ppts[3].y = MIN( Ppts[3].y, (float) ymax );
03546 
03547                 // the Z array has size (nx-1)*(ny-1)
03548                 icol1 = pls->dev_z[ix * ( ny - 1 ) + iy];
03549 
03550                 // only plot points within zmin/zmax range
03551                 if ( icol1 < pls->dev_zmin || icol1 > pls->dev_zmax )
03552                     continue;
03553 
03554                 icol1 = (PLINT) ( (float) icol1 / (float) USHRT_MAX * (float) ( xwd->ncol1 - 1 ) );
03555                 if ( xwd->color )
03556                     curcolor = xwd->cmap1[icol1];
03557                 else
03558                     curcolor = xwd->fgcolor;
03559 
03560                 // Fill square between current and next points.
03561 
03562                 // If the fill area is a single dot, accelerate the fill.
03563                 if ( ( fabs( Ppts[2].x - Ppts[0].x ) == 1 ) &&
03564                      ( fabs( Ppts[3].y - Ppts[1].y ) == 1 ) )
03565                 {
03566                     XPutPixel( ximg, (int) Ppts[0].x, (int) dev->height - 1 - (int) Ppts[0].y, (unsigned long) curcolor.pixel );
03567 
03568                     // integer rotate, accelerate
03569                 }
03570                 else if ( pls->diorot == floor( pls->diorot ) )
03571                 {
03572                     for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ )
03573                         for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ )
03574                             XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel );
03575 
03576                     // lozangue, scanline fill it
03577                 }
03578                 else
03579                 {
03580                     // y interception point of left/right top/bottom edges
03581                     blt = Ppts[0].y - mlr * Ppts[0].x;
03582                     brb = Ppts[2].y - mlr * Ppts[2].x;
03583 
03584                     brt = Ppts[2].y - mtb * Ppts[2].x;
03585                     blb = Ppts[0].y - mtb * Ppts[0].x;
03586 
03587                     for ( ky = (int) Ppts[1].y; ky < (int) Ppts[3].y; ky++ )
03588                     {
03589                         left  = MAX( ( ( (float) ky - blt ) / mlr ), ( ( (float) ky - blb ) / mtb ) );
03590                         right = MIN( ( ( (float) ky - brt ) / mtb ), ( ( (float) ky - brb ) / mlr ) );
03591                         for ( kx = (int) Ppts[0].x; kx < (int) Ppts[2].x; kx++ )
03592                         {
03593                             if ( kx >= rint( left ) && kx <= rint( right ) )
03594                             {
03595                                 XPutPixel( ximg, kx, (int) dev->height - 1 - ky, (unsigned int) curcolor.pixel );
03596                             }
03597                         }
03598                     }
03599                 }
03600             }
03601         }
03602     }
03603 
03604     if ( dev->write_to_pixmap )
03605         XPutImage( xwd->display, dev->pixmap, dev->gc, ximg, 0, 0, 0, 0, dev->width, dev->height );
03606 
03607     if ( dev->write_to_window )
03608         XPutImage( xwd->display, dev->window, dev->gc, ximg, 0, 0,
03609             0, 0, dev->width, dev->height );
03610 
03611     XDestroyImage( ximg );
03612 }
03613 
03614 static void
03615 imageops( PLStream *pls, PLINT *ptr )
03616 {
03617     XwDev     *dev = (XwDev *) pls->dev;
03618     XwDisplay *xwd = (XwDisplay *) dev->xwd;
03619 
03620 // TODO: store/revert to/from previous state
03621 
03622     switch ( *ptr )
03623     {
03624     case ZEROW2D:
03625         dev->write_to_window = 0;
03626         break;
03627 
03628     case ONEW2D:
03629         dev->write_to_window = 1;
03630         break;
03631 
03632     case ZEROW2B:
03633         dev->write_to_pixmap = 0;
03634         break;
03635 
03636     case ONEW2B:
03637         XFlush( xwd->display );
03638         dev->write_to_pixmap = 1;
03639         break;
03640     }
03641 }
03642 
03643 #else
03644 int
03645 pldummy_xwin()
03646 {
03647     return 0;
03648 }
03649 
03650 #endif                          // PLD_xwin
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines