Files
sicspsi/tecs/pg_plus/xwdriv.c
cvs aa9ab52528 - extended evcontroller
- remote objects
- new ev drivers for oxford IPS,ITC,ILM and LC
M.Z.
2004-11-17 11:32:05 +00:00

4516 lines
133 KiB
C

/*.......................................................................
* PGPLOT driver for workstations running X Windows.
* Version 1.0 - 1989 Nov 06 - A. L. Fey
* Version 3.0 - 1994 Nov 06 - M. C. Shepherd (mcs@astro.caltech.edu).
* Re-write. Visible changes include:
* 1. Corrected expose-event handling.
* 2. The driver now runs a window server,
* so that if requested, windows
* persist for re-use by later programs.
* 3. Support for gray-scale and truecolor visuals.
* 4. Support for private color maps.
* 5. Support for window-resizing by the user.
* 6. New X-resources: pgxwin.geometry,
* pgxwin.iconize, pgxwin.minColors,
* pgxwin.maxColors, pgxwin.visual,
* pgxwin.acceptQuit, pgxwin.crosshair.
* 7. "rubber-band" cursor options for use with
* pgband().
* 8. Corrected selective event handling.
* 9. Fixed input focus code so that iconizing
* doesn't kill the program.
* 10. Arranged for the window manager to ignore
* the delete-window option unless the
* pgxwin.acceptQuit resource is assigned a
* truth value.
* 11. Added XErrorEvent handling to prevent program
* crashes.
* 12. Cursor warps are defered until the cursor
* enters the /xw window, and can be turned
* off entirely with the appropriate argument
* to PGBAND().
* 13. Colormap updates are now buffered.
* 14. Support for multiple open devices.
* 15. The cursor can now be moved with the
* keyboard arrow keys.
* Version 3.0+ - 2003 Mar 19 - M.Zolliker
* It is now possible to specify a timeout
* for xw_read_cursor
*
*
* Scope: This driver should work with all unix workstations running
* X Windows (Version 11). It also works on VMS and OpenVMS
* workstations running X.
* Color: Visual color maps of types, PsuedoColor, StaticColor, GrayScale
* and StaticGray are supported. Where insufficient colors are
* available in the default colormap, a private colormap is
* requested. If this fails, the device is treated as
* monochrome.
* Cursor: The cursor is controlled by a mouse or equivalent. Buttons
* 1 2 3 are mapped to characters A D X. The cursor can also
* be moved horizontally and vertically with the arrow keys.
* Each press of an arrow key moves the cursor one pixel. This
* can be increased to 10 pixels by pressing the shift key.
* Size: The initial size and position of the window number #,
* is determined with the following heirachy of specifications,
* missing details at each level are supplied by those below.
*
* 1. X-resource: pgxwin.win#.geometry: WIDTHxHEIGHT+X+Y
* 2. X-resource: pgxwin.Win.geometry: WIDTHxHEIGHT+X+Y
* 3. Environment variable: PGPLOT_XW_WIDTH [fractional display width]
* 4. #define's: XW_DEF_WIDTH, XW_DEF_HEIGHT, XW_DEF_ASPECT
*
* There are too many other configuration options to document here, but
* complete documentation of the driver is available over the WEB at URL:
*
* http://astro.caltech.edu/~tjp/xwdriv.html
*
*/
/*
* Certain symbols in fcntl.h may not get defined
* unless the _POSIX_SOURCE feature-test macro is set.
*/
/*
* Allow xwdriv to be calleable by FORTRAN using the two commonest
* calling conventions. Both conventions append length arguments for
* each FORTRAN string at the end of the argument list, and convert the
* name to lower-case, but one post-pends an underscore to the function
* name (PG_PPU) while the other doesn't. Note the VMS is handled
* separately below. For other calling conventions you must write a
* C wrapper routine to call xwdriv() or xwdriv_().
*/
#ifdef PG_PPU
#define XWDRIV xwdriv_
#else
#define XWDRIV xwdriv
#endif
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#ifndef convex
#include <string.h>
#endif
/*
* VAX VMS includes etc..
*/
#ifdef VMS
#include <signal.h> /* sleep() is prototyped here */
#include <unixio.h> /* access() is prototyped here */
#include <descrip.h>
#include <ssdef.h>
#include <clidef.h>
#include <libclidef.h>
#include <lib$routines.h>
typedef struct dsc$descriptor_s VMS_string;
#define VMS_STRING(dsc, string) \
dsc.dsc$w_length = strlen(string); \
dsc.dsc$b_dtype = DSC$K_DTYPE_T; \
dsc.dsc$b_class = DSC$K_CLASS_S; \
dsc.dsc$a_pointer = string;
static int vms_define_command(char *file, char *command);
static int vms_spawn_nowait(char *command);
#endif
#ifndef VMS
#include <fcntl.h>
#include <unistd.h>
#endif
/* X-Window include files */
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
/*
* Record the client/server protocol revision implemented herein.
*/
#define PGXWIN_REVISION 0
/*
* Allow the pgplot /xw server name to be changed by compile
* time pre-definition of PGXWIN_SERVER. If pgxwin_server is modified in
* such a way as to become incompatible with an earlier version of xwdriv.c,
* its name should be changed by postfixing a small increasing integer
* to the name of the executable. New and old pgplot programs can then
* coexist as long as both versions of the server are
* retained. In order not to clutter up system directories, don't change
* this name unless absolutely necessary. This is also the name given to
* the server selection atom, so be sure that it remains valid for this
* purpose.
*/
#ifndef PGXWIN_SERVER
#define PGXWIN_SERVER "pgxwin_server"
#endif
#define NCOLORS 16 /* Number of pre-defined PGPLOT colors */
#define XW_IMAGE_LEN 1280 /* Length of the line-of-pixels buffer */
#define COLORMULT 65535 /* Normalized color intensity multiplier */
#define XW_IDENT "PGPLOT /xw" /* Name to prefix messages to user */
#define XW_DEF_ASPECT (8.5/11.0) /* Default aspect (height/width) of window */
#define XW_DEF_WIDTH 867 /* Default width (pixels) */
#define XW_DEF_HEIGHT ((int) XW_DEF_WIDTH * XW_DEF_ASPECT)
/* Default height (pixels) */
#define XW_SERVER_TIMEOUT 10 /* Max time to allow for server startup */
/*
* Define equivalence values for the XParseGeometry bitmask bits, using
* values agreed upon by xwdriv.c and pgxwin_server.c, for use in
* communicating geometries between client and server.
*/
#define XW_WidthValue 1
#define XW_HeightValue 2
#define XW_XValue 4
#define XW_YValue 8
#define XW_XNegative 16
#define XW_YNegative 32
/*
* Enumerate the supported window close-down modes.
*/
#define XW_DELETE 1
#define XW_PERSIST 2
#define XW_ICONIZE 3
/*
* Enumerate property-data formats, named after the internal types that
* are used to communicate them with XChangeProperty() and
* XGetWindowProperty().
*/
#define XW_CHAR_PROP 8
#define XW_SHORT_PROP 16
#define XW_LONG_PROP 32
/*
* Set the degree to which holding down the shift-key speeds up cursor
* motion when an arrow key is held down.
*/
#define ARROW_KEY_VELOCITY 10
/*
* The following macro must enclose all function prototype arguments.
* This allows pre-ANSI compilers to compile this code, by discarding
* the prototype arguments if __STDC__ is not set.
*/
#ifdef __STDC__
#define ARGS(args) args
#else
#define ARGS(args) ()
#endif
/* A container used to record the geometry of the X-window */
typedef struct {
Atom geom_atom; /* Client/server geometry transaction atom */
int x,y; /* Locus of top left corner of window (pixels) */
unsigned int width; /* Width of window (pixels) */
unsigned int height; /* Height of window (pixels) */
int xpix_per_inch; /* Number of pixels per inch along X */
int ypix_per_inch; /* Number of pixels per inch along Y */
int xmargin; /* X-axis 1/4" margin in pixels */
int ymargin; /* Y-axis 1/4" margin in pixels */
int xmin,xmax; /* Min/max X-axis pixels excluding 1/4" margins */
int ymin,ymax; /* Min/max X-axis pixels excluding 1/4" margins */
} XWgeom;
/*
* Declare a colormap descriptor.
*/
typedef struct {
XVisualInfo *vi; /* The visual info descriptor for the colormap */
Colormap cmap; /* Colormap ID */
int ncol; /* The number of colors available. ci = [0...ncol-1] */
int monochrome; /* True we have to use a monochrome screen */
unsigned long *pixel; /* 'ncol' colormap pixel indexes. */
XColor *xcolor; /* 'ncol' colormap color representations */
int initialized; /* True after the first call to xw_init_colors() */
int nbuff; /* The number of buffered color representation updates */
int sbuff; /* The index of the first buffered color representation */
} XWcolor;
/*
* Declare a polygon descriptor.
*/
typedef struct {
XPoint *points; /* Temporary array of polygon vertexes */
int npoint; /* Number of points in polygon */
int ndone; /* The number of points received so far */
} XWpoly;
/*
* Declare a container used to record the extent of the rectangular
* pixmap area that has been modified since the last xw_flush().
*/
typedef struct {
int modified; /* True if 'pixmap' has been modified since last update */
int xmin,xmax; /* X-axis extent of modified region (pixels) */
int ymin,ymax; /* Y-axis extent of modified region (pixels) */
} XWupdate;
/*
* Declare a container to encapsulate the buffers needed to
* draw a line of pixels.
*/
typedef struct {
XImage *xi; /* Line of pixels Xlib image object */
} XWimage;
/*
* Declare a container used to hold event state information.
*/
typedef struct {
long mask; /* Current event mask */
int no_buttons; /* True after failure to acquire ButtonPressMask */
} XWevent;
/*
* Declare a function type, instances of which are to be called to flush
* buffered opcodes, and return 0 if OK, or 1 on error.
*/
struct XWdev;
typedef int (*Flush_Opcode_fn) ARGS((struct XWdev *xw));
/*
* The following container is used to retain state information for /xw
* connections.
*/
typedef struct XWdev {
Display *display; /* Display descriptor */
Window parent; /* The ID of the parent window */
Window window; /* Window ID */
Window client; /* Client communication window */
Window server; /* Server communication window */
Atom server_atom; /* Server selection atom */
Atom client_data; /* Client data property atom */
int protocol; /* Client/server communication protocol to use */
int number; /* PGPLOT window number */
int screen; /* The screen in which the window is displayed */
int disposition; /* Close-down mode: XW_PERSIST, XW_ICONIZE, XW_DELETE */
int bad_device; /* Set to 1 by xw_bad_device() after fatal errors. */
int last_error; /* The last error code trapped by xw_error() */
Pixmap pixmap; /* Pixmap ID */
Cursor norm_cursor;/* ID of normal cursor */
Cursor live_cursor;/* ID of active cursor */
int crosshair; /* Show intersecting line cursor if true */
XWpoly poly; /* Polygon-fill accumulation descriptor */
XWcolor color; /* Colormap descriptor */
XWgeom geom; /* The size and position of the window */
XWupdate update; /* Descriptor of un-drawn area of pixmap */
XWevent event; /* Event state container */
XWimage image; /* Line of pixels container */
XGCValues gcv; /* Publicly visible contents of 'gc' */
GC gc; /* Graphical context descriptor */
int last_opcode; /* Index of last opcode */
int nofocus; /* set to 1 after a timeout in xw_read_cursor M.Z. */
Flush_Opcode_fn flush_opcode_fn; /* Function to flush a buffered opcode */
struct XWdev *next;/* Pointer to next /xw device in list */
} XWdev;
/* Create an alias for the standard X error handling function type */
typedef int (*Xerrorfn) ARGS((Display *, XErrorEvent *));
/* Private method functions that operate on XWdev descriptors */
static XWdev *new_XWdev ARGS((char *display, int mode));
static XWdev *del_XWdev ARGS((XWdev *xw, int partial));
static int xw_bad_device ARGS((XWdev *xw));
static int xw_ok ARGS((XWdev *xw));
static int xw_set_rgb ARGS((XWdev *xw, int ci, float red, float green, float blue));
static int xw_get_visual ARGS((XWdev *xw));
static int xw_init_colors ARGS((XWdev *xw));
static Window xw_get_window ARGS((XWdev *xw));
static Pixmap xw_get_pixmap ARGS((XWdev *xw));
static int xw_get_cursors ARGS((XWdev *xw));
static int xw_get_image ARGS((XWdev *xw, int npix));
static Window xw_get_server ARGS((XWdev *xw));
static int xw_query_server ARGS((XWdev *xw, XEvent *event));
static int xw_set_cursor ARGS((XWdev *xw, int norm));
static int xw_clear ARGS((XWdev *xw));
static int xw_set_ci ARGS((XWdev *xw, int ci));
static void xw_mark_modified ARGS((XWdev *xw, int x, int y, int diameter));
static int xw_flush ARGS((XWdev *xw));
static void xw_XPoint_to_xy ARGS((XWdev *xw, XPoint *xp, float *xy));
static void xw_xy_to_XPoint ARGS((XWdev *xw, float *xy, XPoint *xp));
static float xw_xcolor_to_rgb ARGS((unsigned short urgb));
static int xw_rgb_to_xcolor ARGS((float rgb));
static int xw_next_page ARGS((XWdev *xw, unsigned int width, unsigned int height));
static int xw_image_line ARGS((XWdev *xw, XPoint *start, float *cells, int ncell));
static int xw_read_cursor ARGS((XWdev *xw, int mode, int posn, XPoint *ref,
XPoint *pos, char *key));
static int xw_shift_cursor ARGS((XWdev *xw, KeySym keysym, \
unsigned int modifiers));
static int xw_expose ARGS((XWdev *xw, XEvent *event));
static int xw_new_geom ARGS((XWdev *xw, int x, int y, unsigned int width,
unsigned int height,int mask));
static int xw_error ARGS((Display *display, XErrorEvent *event));
static int xw_locate_cursor ARGS((XWdev *xw, XPoint *pos, int warp, XPoint *loc));
static int xw_next_event ARGS((XWdev *xw, XEvent *event));
static int xw_next_event_tmo ARGS((XWdev *xw, XEvent *event, int tmo_10)); /*M.Z.*/
static int xw_check_window_event ARGS((XWdev *xw, Window window,
long event_mask, XEvent *event));
static unsigned long xw_get_data ARGS((XWdev *xw, char *data, int form, unsigned long n));
static XVisualInfo *xw_visual_info ARGS((Display *display, int screen,
Visual *visual));
static void xw_limit_pcoords ARGS((XWdev *xw, XPoint *coord));
static void xw_scroll_rect ARGS((XWdev *xw, float *rbuf));
/* Container for rubber-band cursor resources and status */
typedef struct {
int line_width; /* Rubber-band line width */
int mode; /* Cursor mode 1=line, 2=rectangle */
XPoint ref; /* Reference vertex of cursor */
XPoint end; /* End point of cursor */
} Band;
static Band *xw_new_Band ARGS((XWdev *xw, int mode, XPoint *ref));
static int xw_draw_cursor ARGS((XWdev *xw, Band *bc, XPoint *end));
static int xw_erase_cursor ARGS((XWdev *xw, Band *bc));
static int xw_end_cursor ARGS((XWdev *xw, Band *bc, int status));
static Band *xw_del_Band ARGS((XWdev *xw, Band *bc));
static int xw_bound_cursor ARGS((XWdev *xw, XPoint *xp));
static int xw_cursor_line ARGS((XWdev *xw, int xa, int ya, int xb, int yb));
static int xw_add_events ARGS((XWdev *xw, long events));
static int xw_rem_events ARGS((XWdev *xw, long events));
/* Functions used to flush buffered opcodes */
static int xw_update_colors ARGS((XWdev *xw));
/*
* Declare the head of the list of open XW device descriptors.
* This has to have file scope to allow the X error handler to get at it.
*/
static XWdev *device_list = NULL;
static XWdev *xw_insert_device ARGS((XWdev *xw));
static XWdev *xw_select_device ARGS((int number));
static XWdev *xw_remove_device ARGS((XWdev *xw));
static char *find_exe ARGS((char *path, char *program));
static int xw_nint ARGS((float f));
/*.......................................................................
* This is the only external entry point to the /xw device driver.
* It is called by PGPLOT to open, perform operations on, return
* information about and close /xw windows.
*
* Input:
* ifunc int * The PGPLOT operation code to be executed.
* Input/output:
* rbuf float * A general buffer for input/output of float values.
* nbuf int * Where relevant this is used to return the number of
* elements in rbuf[]. Also used on input to specify
* number of pixels in the line-of-pixels primitive.
* chr char * A general buffer for string I/O.
* lchr int * Where relevant this is used to send and return the
* number of significant characters in chr.
* Input:
* mode int * The value of *mode specifies the disposition of
* the device:
* 1 - /XWINDOW => non-persistent window.
* 2 - /XSERVE => persistent window.
* len int Added to the call line by the FORTRAN compiler.
* This contains the declared size of chr[].
*/
#ifdef VMS
void xwdriv(ifunc, rbuf, nbuf, chrdsc, lchr, mode)
int *ifunc;
float rbuf[];
int *nbuf;
struct dsc$descriptor_s *chrdsc; /* VMS FORTRAN string descriptor */
int *lchr;
int *mode;
{
int len = chrdsc->dsc$w_length;
char *chr = chrdsc->dsc$a_pointer;
#else
void XWDRIV(ifunc, rbuf, nbuf, chr, lchr, mode, len)
int *ifunc, *nbuf, *lchr, *mode;
int len;
float rbuf[];
char *chr;
{
#endif
static XWdev *xw = NULL; /* The descriptor of the currently selected device */
int i;
/*
* If there is a buffered opcode and the latest opcode is not the same
* as the last opcode, call the given flush function for the
* buffered opcode.
*/
if(xw && !xw->bad_device) {
if(xw->last_opcode != *ifunc) {
if(xw->flush_opcode_fn != (Flush_Opcode_fn) 0) {
(*xw->flush_opcode_fn)(xw);
xw->flush_opcode_fn = (Flush_Opcode_fn) 0;
};
/*
* Record the current opcode for next time.
*/
xw->last_opcode = *ifunc;
};
};
/* Branch on opcode. */
switch(*ifunc) {
/*--- IFUNC=1, Return device name ---------------------------------------*/
case 1:
{
char *dev_name;
switch(*mode) { /* Locate the name used to select the given mode */
case 1: default:
dev_name = "XWINDOW (X window window@node:display.screen/xw)";
break;
case 2:
dev_name = "XSERVE (A /XWINDOW window that persists for re-use)";
break;
};
strncpy(chr, dev_name, len);
*lchr = strlen(dev_name);
for(i = *lchr; i < len; i++)
chr[i] = ' ';
};
break;
/*--- IFUNC=2, Return physical min and max for plot device, and range
of color indices -----------------------------------------*/
case 2:
rbuf[0] = 0.0;
rbuf[1] = -1.0; /* Report no effective max plot width */
rbuf[2] = 0.0;
rbuf[3] = -1.0; /* Report no effective max plot height */
rbuf[4] = 0.0;
rbuf[5] = (xw && !xw->bad_device) ? xw->color.ncol-1 : 1;
*nbuf = 6;
break;
/*--- IFUNC=3, Return device resolution ---------------------------------*/
case 3:
if(xw_ok(xw)) {
rbuf[0] = xw->geom.xpix_per_inch;
rbuf[1] = xw->geom.ypix_per_inch;
} else {
rbuf[0] = 1.0;
rbuf[1] = 1.0;
};
rbuf[2] = 1.0; /* Device coordinates per pixel */
*nbuf = 3;
break;
/*--- IFUNC=4, Return misc device info ----------------------------------*/
case 4:
chr[0] = 'I'; /* Interactive device */
chr[1] = 'C'; /* Cursor is available */
chr[2] = 'N'; /* No dashed lines */
chr[3] = 'A'; /* Area fill available */
chr[4] = 'T'; /* Thick lines */
chr[5] = 'R'; /* Rectangle fill available */
chr[6] = 'P'; /* Line of pixels available */
/*
* Tell PGPLOT to prompt on PGEND only if the window goes away.
*/
chr[7] = xw && xw->disposition==XW_PERSIST ? 'N':'V';
chr[8] = 'Y'; /* Can return color representation */
chr[9] = 'N'; /* Not used */
chr[10]= 'S'; /* Area-scroll available */
*lchr = 11;
break;
/*--- IFUNC=5, Return default file name ---------------------------------*/
case 5:
chr[0] = '\0'; /* Default name is "" */
*lchr = 0;
break;
/*--- IFUNC=6, Return default physical size of plot ---------------------*/
case 6:
if(xw && !xw->bad_device) { /* Return the size of the current window */
XWindowAttributes attr;
XGetWindowAttributes(xw->display, xw->window, &attr);
if(!xw->bad_device) {
rbuf[0] = 0.0;
rbuf[1] = (float) (attr.width - 2 * xw->geom.xmargin);
rbuf[2] = 0.0;
rbuf[3] = (float) (attr.height - 2 * xw->geom.ymargin);
} else {
rbuf[0] = 0.0;
rbuf[1] = (float) xw->geom.width;
rbuf[2] = 0.0;
rbuf[3] = (float) xw->geom.height;
};
} else {
rbuf[0] = 0.0;
rbuf[1] = XW_DEF_WIDTH;
rbuf[2] = 0.0;
rbuf[3] = XW_DEF_HEIGHT;
};
*nbuf = 4;
break;
/*--- IFUNC=7, Return misc defaults -------------------------------------*/
case 7:
rbuf[0] = 1.0;
*nbuf = 1;
break;
/*--- IFUNC=8, Select plot ----------------------------------------------*/
case 8:
xw = xw_select_device((int)(rbuf[1]+0.5));
break;
/*--- IFUNC=9, Open workstation -----------------------------------------*/
case 9:
/*
* Assign the returned device unit number and success indicator.
* Assume failure to open until the workstation is open.
*/
rbuf[0] = rbuf[1] = 0.0;
*nbuf = 2;
/*
* Prepare the display name.
*/
if(*lchr >= len) {
fprintf(stderr, "%s: Display name too long.\n", XW_IDENT);
return;
} else {
chr[*lchr] = '\0';
};
/*
* Connect to the server and create the window.
*/
xw = new_XWdev(chr, *mode);
if(xw==NULL)
return;
/*
* Insert the device in the list of open devices.
*/
xw_insert_device(xw);
rbuf[0] = xw->number; /* Number used to select this device */
rbuf[1] = 1.0;
*nbuf = 2;
break;
/*--- IFUNC=10, Close workstation ---------------------------------------*/
case 10:
/*
* Remove the device from the list of open devices and delete it.
*/
xw_remove_device(xw);
xw = del_XWdev(xw,0);
break;
/*--- IFUNC=11, Begin picture -------------------------------------------*/
case 11:
if(xw_ok(xw)) {
/*
* Convert the passed max X and Y coordinates into the total width of the
* new window. Add 1/4" margins to the requested area.
*/
unsigned int width = (int) (rbuf[0] + 0.5) + 2 * xw->geom.xmargin;
unsigned int height = (int) (rbuf[1] + 0.5) + 2 * xw->geom.ymargin;
/*
* Re-size the window if required.
*/
xw_next_page(xw, width, height);
};
break;
/*--- IFUNC=12, Draw line -----------------------------------------------*/
case 12:
if(xw_ok(xw) && xw->pixmap!=None) {
XPoint start;
XPoint end;
xw_xy_to_XPoint(xw, &rbuf[0], &start);
xw_xy_to_XPoint(xw, &rbuf[2], &end);
XDrawLine(xw->display, xw->pixmap, xw->gc, start.x,start.y, end.x,end.y);
xw_mark_modified(xw, start.x, start.y, xw->gcv.line_width);
xw_mark_modified(xw, end.x, end.y, xw->gcv.line_width);
};
break;
/*--- IFUNC=13, Draw dot ------------------------------------------------*/
case 13:
if(xw_ok(xw) && xw->pixmap!=None) {
XPoint xp;
int radius = xw->gcv.line_width/2;
xw_xy_to_XPoint(xw, rbuf, &xp);
if(radius < 1) {
XDrawPoint(xw->display, xw->pixmap, xw->gc, xp.x, xp.y);
} else {
unsigned int diameter = radius*2;
int x = xp.x - radius;
int y = xp.y - radius;
XFillArc(xw->display, xw->pixmap, xw->gc, x, y, diameter, diameter,
0, 23040);
};
xw_mark_modified(xw, xp.x, xp.y, xw->gcv.line_width);
};
break;
/*--- IFUNC=14, End picture ---------------------------------------------*/
case 14:
break;
/*--- IFUNC=15, Select color index --------------------------------------*/
case 15:
if(xw_ok(xw))
xw_set_ci(xw, (int) (rbuf[0] + 0.5));
break;
/*--- IFUNC=16, Flush buffer. -------------------------------------------*/
case 16:
if(xw_ok(xw))
xw_flush(xw);
break;
/*--- IFUNC=17, Read cursor. --------------------------------------------*/
case 17:
if(xw_ok(xw)) {
XPoint ref; /* Reference cursor coordinates */
XPoint pos; /* Input/Output cursor coordinates */
int mode = 0; /* Cursor band mode */
int posn = 1; /* True to position the cursor */
xw_xy_to_XPoint(xw, rbuf, &pos);
xw_xy_to_XPoint(xw, &rbuf[2], &ref);
mode = (int)(rbuf[4]+0.5);
if (rbuf[4]<0.0) mode--; /* M.Z. */
posn = (int)(rbuf[5]+0.5) > 0;
if(xw_read_cursor(xw, mode, posn, &ref, &pos, chr)==0)
xw_XPoint_to_xy(xw, &pos, rbuf);
else
*chr = '\0';
} else {
*chr = '\0';
};
*lchr = 1;
*nbuf = 2;
break;
/*--- IFUNC=18, Erase alpha screen. -------------------------------------*/
/* (Not implemented: no alpha screen) */
case 18:
break;
/*--- IFUNC=19, Set line style. -----------------------------------------*/
/* (Not implemented: should not be called) */
case 19:
break;
/*--- IFUNC=20, Polygon fill. -------------------------------------------*/
case 20:
if(xw_ok(xw) && xw->pixmap != None) {
/*
* The first call specifies just the number of vertixes in the polygon.
*/
if(xw->poly.npoint == 0) {
xw->poly.npoint = (int) (rbuf[0] + 0.5);
xw->poly.points = (XPoint *) malloc(sizeof(XPoint) * xw->poly.npoint);
if(xw->poly.points == NULL)
fprintf(stderr, "%s: Insufficient memory for polygon points.\n",
XW_IDENT);
xw->poly.ndone = 0;
/*
* The next xw->poly.npoint calls specify the vertexes of the polygon.
*/
} else {
/*
* Ignore the points if the above malloc() failed.
*/
if(xw->poly.points) {
XPoint *xp = &xw->poly.points[xw->poly.ndone];
xw_xy_to_XPoint(xw, rbuf, xp);
xw_mark_modified(xw, xp->x, xp->y, 1);
};
/*
* Maintain the count of the number of points, even if no memory for the
* points is available. Thus we can just ignore all calls until
* xw->poly.ndone == xw->poly.npoint.
*/
xw->poly.ndone++;
/*
* On the last call display the filled polygon and release the memory used
* to store its vertexes.
*/
if(xw->poly.ndone >= xw->poly.npoint) {
if(xw->poly.points) {
XFillPolygon(xw->display, xw->pixmap, xw->gc, xw->poly.points,
xw->poly.npoint, Complex, CoordModeOrigin);
free((char *)xw->poly.points);
xw->poly.points = NULL;
};
xw->poly.npoint = 0;
};
};
};
break;
/*--- IFUNC=21, Set color representation. -------------------------------*/
case 21:
if(xw_ok(xw)) {
if(!xw->color.initialized)
xw_init_colors(xw);
xw_set_rgb(xw, (int)(rbuf[0]+0.5), rbuf[1],rbuf[2],rbuf[3]);
};
break;
/*--- IFUNC=22, Set line width. -----------------------------------------*/
case 22:
/*
* The line width is provided in multiples of 0.005 inches.
*/
if(xw_ok(xw)) {
xw->gcv.line_width = (int)(rbuf[0]*0.005 * xw->geom.xpix_per_inch);
XChangeGC(xw->display, xw->gc, (unsigned long) GCLineWidth, &xw->gcv);
};
break;
/*--- IFUNC=23, Escape --------------------------------------------------*/
/* (Not implemented: ignored) */
case 23:
break;
/*--- IFUNC=24, Rectangle Fill. -----------------------------------------*/
case 24:
if(xw_ok(xw) && xw->pixmap != None) {
XPoint blc;
XPoint trc;
xw_xy_to_XPoint(xw, &rbuf[0], &blc);
xw_xy_to_XPoint(xw, &rbuf[2], &trc);
XFillRectangle(xw->display, xw->pixmap, xw->gc, blc.x, trc.y,
(unsigned)(trc.x-blc.x+1), (unsigned)(blc.y-trc.y+1));
xw_mark_modified(xw, blc.x, blc.y, 1);
xw_mark_modified(xw, trc.x, trc.y, 1);
};
break;
/*--- IFUNC=25, ---------------------------------------------------------*/
/* (Not implemented: ignored) */
case 25:
break;
/*--- IFUNC=26, Line of pixels ------------------------------------------*/
case 26:
if(xw_ok(xw)) {
XPoint start;
xw_xy_to_XPoint(xw, rbuf, &start);
xw_image_line(xw, &start, &rbuf[2], *nbuf - 2);
};
break;
/*--- IFUNC=29, Query color representation ------------------------------*/
case 29:
if(xw_ok(xw)) {
int ci = (int) (rbuf[0] + 0.5);
if(!xw->color.initialized)
xw_init_colors(xw);
rbuf[1] = xw_xcolor_to_rgb(xw->color.xcolor[ci].red);
rbuf[2] = xw_xcolor_to_rgb(xw->color.xcolor[ci].green);
rbuf[3] = xw_xcolor_to_rgb(xw->color.xcolor[ci].blue);
} else {
rbuf[1] = rbuf[2] = rbuf[3] = 0;
};
*nbuf = 4;
break;
/*--- IFUNC=30, Scroll rectangle ----------------------------------------*/
case 30:
xw_scroll_rect(xw, rbuf);
break;
/*--- IFUNC=?, ----------------------------------------------------------*/
default:
fprintf(stderr, "%s: Ignoring unimplemented opcode=%d.\n",XW_IDENT, *ifunc);
*nbuf = -1;
break;
};
/*
* After a server error, close the connection to the display and set all
* server resources to 'None'. This both prevents calls on bad resources
* and by deleting the client communication window, tells the server to
* close the connection if the server hasn't already died.
*/
if(xw && xw->bad_device && xw->display)
del_XWdev(xw, 1);
return;
}
/*.......................................................................
* Assign a given RGB color representation to a given color index.
*
* Input:
* xw XWdev * The /xw device descriptor.
* ci int The color index to assign the color to. Out of range
* indexes are quietly ignored.
* red float The fractional red brightness 0..1.
* green float The fractional green brightness 0..1.
* blue float The fractional blue brightness 0..1.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_set_rgb(XWdev *xw, int ci, float red, float green, float blue)
#else
static int xw_set_rgb(xw, ci, red, green, blue)
XWdev *xw; int ci; float red; float green; float blue;
#endif
{
float gray; /* Gray-scale intensity */
XColor *xc; /* The descriptor of the new color */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Limit RGB values to be between 0 and 1.
*/
if(red < 0.0) red = 0.0;
if(green < 0.0) green = 0.0;
if(blue < 0.0) blue = 0.0;
if(red > 1.0) red = 1.0;
if(green > 1.0) green = 1.0;
if(blue > 1.0) blue = 1.0;
/*
* Color index in range?
*/
if(!xw->color.monochrome && ci >= 0 && ci < xw->color.ncol) {
/*
* Get the color representation descriptor.
*/
xc = &xw->color.xcolor[ci];
/*
* Get the pixel to be assigned the new color representation.
*/
xc->pixel = xw->color.pixel[ci];
xc->flags = DoRed | DoGreen | DoBlue;
xc->pad = 0;
/*
* Determine the appropriate RGB values for the type of colormap.
*/
switch(xw->color.vi->class) {
case PseudoColor:
case StaticColor:
case DirectColor:
case TrueColor:
xc->red = xw_rgb_to_xcolor(red);
xc->green = xw_rgb_to_xcolor(green);
xc->blue = xw_rgb_to_xcolor(blue);
break;
case GrayScale:
case StaticGray:
/*
* For gray-scale colormaps the red,green and blue intensities must all be
* equal. Weight the colors so that what is brightest to the eye, is also
* brighter in grayscale, and so that different colors of equal intensity
* appear different in grayscale. Note that the 3 weights must add up to 1.0.
* The black and white TV standard says to use 0.3*R+0.59*G+0.11*B.
* Unfortunately blue pretty much dissapears in this scheme. The following
* is a compromise between making all colors visible and making different
* colors look different in grayscale.
*/
gray = 0.35*red + 0.40*green + 0.25*blue;
xc->red = xc->green = xc->blue = xw_rgb_to_xcolor(gray);
break;
};
/*
* Update the recorded range of color indexes whose color representations
* have been changed since the last call to xw_update_colors().
*/
if(xw->color.nbuff<=0) {
xw->color.sbuff = ci;
xw->color.nbuff = 1;
} else if(ci < xw->color.sbuff) {
xw->color.nbuff += xw->color.sbuff - ci;
xw->color.sbuff = ci;
} else if(ci > xw->color.sbuff + xw->color.nbuff-1) {
xw->color.nbuff = ci - xw->color.sbuff + 1;
};
/*
* Register xw_update_colors() to be called to flush the colors to the
* window.
*/
xw->flush_opcode_fn = (Flush_Opcode_fn) xw_update_colors;
};
return 0;
}
/*.......................................................................
* Map floating point color intenisties between 0.0 and 1.0 to XColor
* intensities between 0 to 65535. Numbers outside of this range are
* limited to the nearest of the two limits.
*
* Input:
* rgb float The PGPLOT normalized intensity to be converted.
* Output:
* return unsigned short The equivalent XColor RGB intensity.
*/
#ifdef __STDC__
static int xw_rgb_to_xcolor(float rgb)
#else
static int xw_rgb_to_xcolor(rgb)
float rgb;
#endif
{
long lrgb; /* Use to check output before casting to unsigned short */
/*
* Check for limiting input values.
*/
if(rgb < 0.0)
return 0;
if(rgb > 1.0)
return COLORMULT;
/*
* Form the xcolor intensity in a long int so that its range can be checked
* before casting to unsigned short.
*/
lrgb = rgb * COLORMULT + 0.5;
return lrgb > COLORMULT ? COLORMULT : lrgb;
}
/*.......................................................................
* Map XColor intensities between 0 and 65535 to floating point color
* intenisties between 0.0 and 1.0. Numbers outside of this range are
* limited to the nearest of the two limits.
*
* Input:
* unsigned short The equivalent XColor RGB intensity.
* Output:
* return float The PGPLOT normalized intensity to be converted.
*/
#ifdef __STDC__
static float xw_xcolor_to_rgb(unsigned short urgb)
#else
static float xw_xcolor_to_rgb(urgb)
unsigned short urgb;
#endif
{
float rgb; /* The output value */
rgb = (float) urgb / (float) COLORMULT;
/*
* Check for limiting input values.
*/
if(rgb < 0.0)
return 0.0;
if(rgb > 1.0)
return 1.0;
return rgb;
}
/*.......................................................................
* Flush color-representation changes made by xw_set_rgb() to the /xw
* window. This updates the window colormap. If color index 0 is changed
* then the background color is also updated.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* If xw->color.nbuff > 0 {
* For(ci=xw->color.sbuff; ci<xw->color.sbuff + xw->color.nbuff; ci++) {
* xw->color.pixel[ci] = Color pixel to be changed.
* xw->color.xcolor[ci]= Requested color representation.
* };
* };
* Output:
* If xw->color.nbuff > 0 {
* For(ci=xw->color.sbuff; ci<xw->color.sbuff + xw->color.nbuff; ci++) {
* xw->color.pixel[ci] = New color pixel if the colormap is readonly.
* xw->color.xcolor[ci]= Actual color representation installed.
* };
* };
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_update_colors(XWdev *xw)
#else
static int xw_update_colors(xw)
XWdev *xw;
#endif
{
int bad_colors = 0; /* The number of failed color assignments */
int i;
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Are there any colors to be updated?
*/
if(!xw->color.monochrome && xw->color.nbuff > 0) {
XColor *xc = &xw->color.xcolor[xw->color.sbuff];
unsigned long *pixel = &xw->color.pixel[xw->color.sbuff];
int nbuff = xw->color.nbuff;
/*
* Install the colors in the color map.
*/
switch(xw->color.vi->class) {
case PseudoColor:
case GrayScale:
case DirectColor:
XStoreColors(xw->display, xw->color.cmap, xc, nbuff);
break;
case StaticColor:
case StaticGray:
case TrueColor:
for(i=0; i<nbuff && !xw->bad_device; i++) {
if(XAllocColor(xw->display, xw->color.cmap, &xc[i])) {
if(xw->color.initialized)
XFreeColors(xw->display, xw->color.cmap, &pixel[i], 1, (long)0);
pixel[i] = xc[i].pixel;
} else {
bad_colors++;
};
};
break;
};
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Update the background color?
*/
if(xw->color.sbuff == 0)
XSetWindowBackground(xw->display, xw->window, pixel[0]);
/*
* Did any of the color assignments fail?
*/
if(bad_colors > 0) {
fprintf(stderr,
"%s: Error setting the color representations of %d colors.\n",
XW_IDENT, bad_colors);
};
};
/*
* Reset buffer pointers.
*/
xw->color.nbuff = 0;
xw->color.sbuff = 0;
return xw->bad_device!=0;
}
/*.......................................................................
* Set up the visual and colormap for the /xw window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* xw->color.vi The info descriptor of the visual to be used.
* xw->color.cmap The ID of the colormap to use.
* xw->color.ncol The number of colors available.
* xw->color.pixel[0..ncol] The color cell pixel indexes.
* xw->color.xcolor[0..ncol]The color pixel definitions.
* xw->color.monochrome If true, use black and white instead of the above
* values.
* xw->color.nbuff The number of buffered color representations.
* xw->color.sbuff The index of the first buffered color rep.
*
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_get_visual(XWdev *xw)
#else
static int xw_get_visual(xw)
XWdev *xw;
#endif
{
XEvent event; /* Descriptor of XClientMessage communication descriptor */
XWindowAttributes attr;
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Assume that we have a monochrome display until proven otherwise.
*/
xw->color.monochrome = 1;
xw->color.ncol = 2;
xw->color.nbuff = 0; /* No color representations buffered yet */
xw->color.sbuff = 0;
/*
* Inquire the current visual details of the window.
*/
if(!XGetWindowAttributes(xw->display, xw->window, &attr)) {
fprintf(stderr,
"%s: (xw_get_visual) Error getting attributes for window 0x%lx.\n",
XW_IDENT, (unsigned long) xw->window);
return 1;
};
xw->color.vi = xw_visual_info(xw->display, xw->screen, attr.visual);
xw->color.cmap = attr.colormap;
if(xw->color.vi == NULL || xw->color.cmap == None)
return 1;
/*
* Ask the server for other colormap details.
*/
event.xclient.message_type = XA_COLORMAP;
if(xw_query_server(xw, &event))
return 1;
xw->color.monochrome = event.xclient.data.l[0] == None;
xw->color.ncol = event.xclient.data.l[1];
/*
* Allocate memory for the array of color pixels and color pixel
* representations.
*/
if(xw->color.ncol > 0) {
xw->color.pixel = (unsigned long *) malloc(sizeof(unsigned long) *
xw->color.ncol);
xw->color.xcolor = (XColor *) malloc(sizeof(XColor) * xw->color.ncol);
if(xw->color.pixel==NULL || xw->color.xcolor==NULL)
xw->color.ncol = 0;
};
/*
* If we got a colormap, wait for the array of 'ncol' color-cell pixel
* indexes to be placed in the PGXWIN_CLIENT_DATA property on the
* client communication window, then read it and delete the property.
*/
if(!xw->color.monochrome) {
xw->color.ncol = xw_get_data(xw, (char *) &xw->color.pixel[0],
XW_LONG_PROP, (unsigned long) xw->color.ncol);
if(xw->color.ncol==0)
xw->color.monochrome = 1;
};
return 0;
}
/*.......................................................................
* Initialize the color representations in the color table.
* xw_get_visual() must have been called prior to calling this function,
* so that we have a visual and colormap to define the colors in.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* xw->color.xcolor[0..ncol] The color pixel definitions.
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_init_colors(XWdev *xw)
#else
static int xw_init_colors(xw)
XWdev *xw;
#endif
{
/*
* Define the standard PGPLOT line colors (RGB).
*/
static float ctable[NCOLORS][3] = {
{0.0,0.0,0.0}, {1.0,1.0,1.0}, {1.0,0.0,0.0}, {0.0,1.0,0.0},
{0.0,0.0,1.0}, {0.0,1.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,0.0},
{1.0,0.5,0.0}, {0.5,1.0,0.0}, {0.0,1.0,0.5}, {0.0,0.5,1.0},
{0.5,0.0,1.0}, {1.0,0.0,0.5}, {0.333,0.333,0.333},
{0.667,0.667,0.667}
};
int i;
/*
* Initialize the color-table with the standard PGPLOT line colors.
*/
if(!xw->color.monochrome) {
int ncol = (NCOLORS < xw->color.ncol) ? NCOLORS : xw->color.ncol;
for(i=0; i<ncol; i++) {
if(xw_set_rgb(xw, i, ctable[i][0], ctable[i][1], ctable[i][2]))
return 1;
};
/*
* Initialize the rest of the colors with a grey-scale ramp.
*/
for(i=ncol; i<xw->color.ncol; i++) {
float grey = (float)(i-NCOLORS) / (float)(xw->color.ncol-1-NCOLORS);
if(xw_set_rgb(xw, i, grey, grey, grey))
return 1;
};
};
/*
* Flush the new color definitions to the display.
*/
if(xw_update_colors(xw))
return 1;
/*
* Record the new colormap state.
*/
xw->color.initialized = 1;
/*
* Start with the foreground color set to white.
*/
if(xw_set_ci(xw, 1))
return 1;
return 0;
}
/*.......................................................................
* Get a new PGPLOT window from the server.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* xw->window The new window ID.
* return Window The window ID, or 'None' on error.
*/
#ifdef __STDC__
static Window xw_get_window(XWdev *xw)
#else
static Window xw_get_window(xw)
XWdev *xw;
#endif
{
XEvent event; /* Descriptor of XClientMessage communication descriptor */
int number; /* The requested window number */
/*
* Device error?
*/
if(xw->bad_device)
return None;
/*
* Keep a record of the window number that was requested.
*/
number = xw->number;
/*
* Ask the server for other colormap details.
*/
event.xclient.message_type = XA_WINDOW;
event.xclient.data.l[0] = PGXWIN_REVISION;
event.xclient.data.l[1] = xw->number;
event.xclient.data.l[2] = xw->screen;
event.xclient.data.l[3] = xw->disposition;
if(xw_query_server(xw, &event))
return None;
xw->protocol = event.xclient.data.l[0];
xw->number = event.xclient.data.l[1];
xw->window = event.xclient.data.l[2];
xw->disposition = event.xclient.data.l[3];
/*
* Did the server refuse to give us the requested window?
*/
if(xw->window == None) {
if(number != 0)
fprintf(stderr, "%s: Window %d is unavailable.\n", XW_IDENT, number);
else
fprintf(stderr, "%s: Failed to acquire a PGPLOT window.\n", XW_IDENT);
};
return xw->window;
}
/*.......................................................................
* Get a new pixmap from the server. This should be called whenever a
* new page is started.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* xw->pixmap The new pixmap ID.
* return Pixmap The pixmap ID, or 'None' on error.
*/
#ifdef __STDC__
static Pixmap xw_get_pixmap(XWdev *xw)
#else
static Pixmap xw_get_pixmap(xw)
XWdev *xw;
#endif
{
XEvent event; /* Descriptor of XClientMessage communication descriptor */
/*
* Device error?
*/
if(xw->bad_device)
return None;
/*
* Ask the server for other colormap details.
*/
event.xclient.message_type = XA_PIXMAP;
event.xclient.data.l[0] = xw->color.monochrome ? BlackPixel(xw->display, xw->screen) : xw->color.pixel[0];
if(xw_query_server(xw, &event))
return 1;
xw->pixmap = event.xclient.data.l[0];
/*
* Did the server fail to give us the requested pixmap?
*/
if(xw->pixmap == None)
fprintf(stderr, "%s: Failed to allocate pixmap.\n", XW_IDENT);
return xw->pixmap;
}
/*.......................................................................
* Get the IDs of the normal and active cursors.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* xw->norm_cursor The ID of the idle cursor.
* xw->live_cursor The ID of the live cursor.
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_get_cursors(XWdev *xw)
#else
static int xw_get_cursors(xw)
XWdev *xw;
#endif
{
XEvent event; /* Descriptor of XClientMessage communication descriptor */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Ask the server for other colormap details.
*/
event.xclient.message_type = XA_CURSOR;
if(xw_query_server(xw, &event))
return 1;
xw->norm_cursor = event.xclient.data.l[0];
xw->live_cursor = event.xclient.data.l[1];
xw->crosshair = event.xclient.data.l[2];
return xw->bad_device!=0;
}
/*.......................................................................
* Agree upon a window geometry with the PGPLOT /xw server.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor. Only the display
* and screen members are required.
* mask int A bit mask to specify which values have been provided
* and how they should be interpretted. The mask is the
* union of the following:
* WidthValue - Use the given width value.
* HeightValue - Use the given height value.
* XValue - Use the given value of 'x'.
* YValue - Use the given value of 'y'.
* XNegative - x is wrt the right of the display.
* YNegative - y is wrt the left of the display.
* x int The left edge of the window.
* y int The top edge of the window.
* width unsigned The width of the window.
* height unsigned The height of the window.
* Output:
* xw->geom XWgeom The new window geometry.
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_new_geom(XWdev *xw, int x, int y, unsigned int width,
unsigned int height, int mask)
#else
static int xw_new_geom(xw, x, y, width, height, mask)
XWdev *xw; int x; int y; unsigned int width; unsigned int height; int mask;
#endif
{
XEvent event; /* Descriptor of XClientMessage communication descriptor */
unsigned int d_pix_width; /* Display width in pixels */
unsigned int d_pix_height; /* Display height in pixels */
unsigned int d_mm_width; /* Display width in mm */
unsigned int d_mm_height; /* DIsplay height in mm */
int xw_mask=0; /* PGXWIN communication version of 'mask' */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Get the PGXWIN_GEOMETRY transaction atom.
*/
if(xw->geom.geom_atom == None)
xw->geom.geom_atom = XInternAtom(xw->display, "PGXWIN_GEOMETRY", False);
/*
* Translate the local bitmask values to a PGXWIN defined bitmask for
* communication, since different Xlibs may define the XParseGeometry
* bitmask values differently. They will be translated back in the server.
*/
if(mask & WidthValue)
xw_mask |= XW_WidthValue;
if(mask & HeightValue)
xw_mask |= XW_HeightValue;
if(mask & XValue)
xw_mask |= XW_XValue;
if(mask & YValue)
xw_mask |= XW_YValue;
if(mask & XNegative)
xw_mask |= XW_XNegative;
if(mask & YNegative)
xw_mask |= XW_YNegative;
/*
* Send geometry prefences to the server and receive the resulting
* geometry.
*/
event.xclient.message_type = xw->geom.geom_atom;
event.xclient.data.l[0] = x;
event.xclient.data.l[1] = y;
event.xclient.data.l[2] = width;
event.xclient.data.l[3] = height;
event.xclient.data.l[4] = xw_mask;
if(xw_query_server(xw, &event))
return 1;
/*
* Record the geometry that the server sent.
*/
xw->geom.x = event.xclient.data.l[0];
xw->geom.y = event.xclient.data.l[1];
xw->geom.width = event.xclient.data.l[2];
xw->geom.height = event.xclient.data.l[3];
/*
* Determine the current display width and height in mm and pixels.
*/
d_pix_width = DisplayWidth(xw->display, xw->screen);
d_mm_width = DisplayWidthMM(xw->display, xw->screen);
d_pix_height = DisplayHeight(xw->display, xw->screen);
d_mm_height = DisplayHeightMM(xw->display, xw->screen);
/*
* Determine the device resolution in pixels per inch.
*/
xw->geom.xpix_per_inch = 25.4 * ((double)d_pix_width / (double)d_mm_width);
xw->geom.ypix_per_inch = 25.4 * ((double)d_pix_height / (double)d_mm_height);
/*
* Determine the number of pixels needed to form a 1/4" margin around the
* the plot area.
*/
xw->geom.xmargin = (int) (0.25 * xw->geom.xpix_per_inch + 0.5);
xw->geom.ymargin = (int) (0.25 * xw->geom.ypix_per_inch + 0.5);
/*
* Determine the pixel indexes that enclose an area bounded by 1/4" margins.
*/
xw->geom.xmin = xw->geom.xmargin;
xw->geom.xmax = xw->geom.width - xw->geom.xmargin;
xw->geom.ymin = xw->geom.ymargin;
xw->geom.ymax = xw->geom.height - xw->geom.ymargin;
return 0;
}
/*.......................................................................
* Instate the given cursor type.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* norm int If norm!=0 instate the normal idle cursor.
* If norm==0 instate the active cursor.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_set_cursor(XWdev *xw, int norm)
#else
static int xw_set_cursor(xw, norm)
XWdev *xw; int norm;
#endif
{
Cursor cursor; /* The ID of the cursor to be instated */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Get the cursor ID and color to use.
*/
cursor = norm ? xw->norm_cursor : xw->live_cursor;
/*
* Register the cursor to the window.
*/
XDefineCursor(xw->display, xw->window, cursor);
if(xw->bad_device)
return 1;
XFlush(xw->display);
return xw->bad_device!=0;
}
/*.......................................................................
* Clear the window and pixmap to start a new page.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_clear(XWdev *xw)
#else
static int xw_clear(xw)
XWdev *xw;
#endif
{
unsigned long fg; /* Saved foreground color */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* We are about to change the current foreground color, so save the
* current value to be re-instated shortly.
*/
fg = xw->gcv.foreground;
/*
* Clear the pixmap by drawing an opaque rectangle over it in the background
* color.
*/
xw_set_ci(xw, 0);
if(xw->pixmap != None) {
XFillRectangle(xw->display, xw->pixmap, xw->gc, 0, 0,
xw->geom.width, xw->geom.height);
if(xw->bad_device)
return 1;
};
/*
* Re-instate the foreground color.
*/
xw->gcv.foreground = fg;
XSetForeground(xw->display, xw->gc, xw->gcv.foreground);
if(xw->bad_device)
return 1;
/*
* Mark the pixmap as unmodified.
*/
xw->update.modified = 0;
/*
* Clear the window itself.
*/
XClearWindow(xw->display, xw->window);
if(xw->bad_device)
return 1;
XFlush(xw->display);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Set the foreground color.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* ci int The PGPLOT color index to instate as the foreground
* color.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_set_ci(XWdev *xw, int ci)
#else
static int xw_set_ci(xw, ci)
XWdev *xw; int ci;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Assign white to out-of range color indexes.
*/
if(ci < 0 || ci >= xw->color.ncol)
ci = 1;
/*
* Determine the color pixel associated with the given color index.
*/
if(xw->color.monochrome) {
xw->gcv.foreground = ci==1 ? WhitePixel(xw->display, xw->screen) :
BlackPixel(xw->display, xw->screen);
} else {
xw->gcv.foreground = xw->color.pixel[ci];
};
/*
* Instate the new foreground color.
*/
XSetForeground(xw->display, xw->gc, xw->gcv.foreground);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Update the vertices of the rectangular area that has been modified
* since the last time the window was updated from the pixmap.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* x int The x-axis pixel index that the rectangular update area
* must be extended to include.
* y int The y-axis pixel index that the rectangular update area
* must be extended to include.
* diameter int The diameter of the locus in pixels. For line or
* point drawing operations this is usually the line width.
*/
#ifdef __STDC__
static void xw_mark_modified(XWdev *xw, int x, int y, int diameter)
#else
static void xw_mark_modified(xw, x, y, diameter)
XWdev *xw; int x; int y; int diameter;
#endif
{
int radius = diameter/2;
/*
* Expand the current rectangle to include point (x,y).
*/
if(xw->update.modified) {
if(x - radius < xw->update.xmin)
xw->update.xmin = x - radius;
if(x + radius > xw->update.xmax)
xw->update.xmax = x + radius;
if(y - radius < xw->update.ymin)
xw->update.ymin = y - radius;
if(y + radius > xw->update.ymax)
xw->update.ymax = y + radius;
} else {
xw->update.xmin = x - radius;
xw->update.xmax = x + radius;
xw->update.ymin = y - radius;
xw->update.ymax = y + radius;
xw->update.modified = 1;
};
return;
}
/*.......................................................................
* Flush changes to the pixmap to the window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_flush(XWdev *xw)
#else
static int xw_flush(xw)
XWdev *xw;
#endif
{
if(xw->bad_device)
return 1;
/*
* Flush buffered opcodes if necessary.
*/
if(xw->flush_opcode_fn != (Flush_Opcode_fn) 0) {
(*xw->flush_opcode_fn)(xw);
xw->flush_opcode_fn = (Flush_Opcode_fn) 0;
if(xw->bad_device)
return 1;
};
/*
* Copy the modified rectangular area of the pixmap to the /xw window.
*/
if(xw->update.modified) {
/*
* Enforce bounds on the area to be updated.
*/
if(xw->update.xmin < 0)
xw->update.xmin = 0;
if(xw->update.ymin < 0)
xw->update.ymin = 0;
if(xw->update.xmax > xw->geom.width - 1)
xw->update.xmax = xw->geom.width - 1;
if(xw->update.ymax > xw->geom.height - 1)
xw->update.ymax = xw->geom.height - 1;
/*
* Copy the area to be updated from the pixmap to the window.
*/
if(xw->pixmap != None && !xw->bad_device) {
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc,
xw->update.xmin, xw->update.ymin,
(unsigned) (xw->update.xmax - xw->update.xmin + 1),
(unsigned) (xw->update.ymax - xw->update.ymin + 1),
xw->update.xmin, xw->update.ymin);
if(xw->bad_device)
return 1;
};
xw->update.modified = 0;
};
XFlush(xw->display);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Open a /xw window and return an initialized /xw PGPLOT device descriptor.
*
* Input:
* display char * A '\0' terminated string containing the name of
* the display.
* mode int The type of window to open.
* 1 - None-persistent window.
* 2 - Persistent window.
* Output:
* return XWdev * THe PGPLOT /xw device descriptor, or NULL on error.
*/
#ifdef __STDC__
static XWdev *new_XWdev(char *display, int mode)
#else
static XWdev *new_XWdev(display, mode)
char *display; int mode;
#endif
{
XWdev *xw; /* The descriptor to be returned */
/*
* Allocate the descriptor.
*/
xw = (XWdev *) malloc(sizeof(XWdev));
if(xw==NULL)
return del_XWdev(xw,0);
/*
* Initialize all members of the descriptor at least to the point at which
* the descriptor can safely be sent to del_XWdev(xw,0). All pointers must
* be assigned NULL and XIDs assigned None, so that del_XWdev() knows what
* hasn't been allocated yet.
*/
xw->display = NULL;
xw->parent = None;
xw->window = None;
xw->client = None;
xw->server = None;
xw->number = 0;
xw->screen = 0;
xw->disposition = mode==2 ? XW_PERSIST : XW_DELETE;
xw->bad_device = 0;
xw->last_error = 0;
xw->pixmap = None;
xw->color.cmap = None;
xw->norm_cursor = None;
xw->live_cursor = None;
xw->crosshair = 0;
xw->poly.points = NULL;
xw->poly.ndone = xw->poly.npoint = 0;
xw->gc = NULL;
xw->color.vi = NULL;
xw->color.cmap = None;
xw->color.ncol = 0;
xw->color.monochrome = 1;
xw->color.pixel = NULL;
xw->color.xcolor = NULL;
xw->color.initialized = 0;
xw->color.nbuff = 0;
xw->color.sbuff = 0;
xw->geom.geom_atom = None;
xw->update.modified = 0;
xw->event.mask = NoEventMask;
xw->event.no_buttons = 0;
xw->image.xi = NULL;
xw->last_opcode = 0;
xw->nofocus = 0; /* M.Z */
xw->flush_opcode_fn = (Flush_Opcode_fn) 0;
/*
* See if the device name is prefixed with a window number.
* The device name is encoded as [window@][display] | [window].
* Leave the trailing display name in display.
*/
{
char *endp;
long number = strtol(display, &endp, 10);
switch(*endp) {
case '@':
display = endp+1;
xw->number = number;
break;
case '\0':
display = endp;
xw->number = number;
break;
};
};
/*
* Treat -ve window numbers as equivalent to 0.
*/
if(xw->number < 0)
xw->number = 0;
/*
* Open a connection to the X display server.
*/
xw->display = XOpenDisplay(display);
if(xw->display==NULL) {
fprintf(stderr, "%s: cannot connect to X server [%s]\n", XW_IDENT,
XDisplayName(display));
return del_XWdev(xw,0);
};
/*
* Install an error handler for non-fatal errors. If we don't do this then
* Xlib will do its own error handling, which includes killing the program.
*/
XSetErrorHandler(xw_error);
/*
* Get the index of the screen cited in the display string.
*/
xw->screen = DefaultScreen(xw->display);
/*
* Also record the parent window ID.
*/
xw->parent = RootWindow(xw->display, xw->screen);
/*
* Create a simple window for communication with the server.
*/
xw->client = XCreateSimpleWindow(xw->display, xw->parent,
0, 0, (unsigned)1, (unsigned)1, (unsigned)1,
BlackPixel(xw->display, xw->screen),
BlackPixel(xw->display, xw->screen));
if(xw->client == None || xw->bad_device) {
fprintf(stderr, "%s: Unable to create window.\n", XW_IDENT);
return del_XWdev(xw,0);
};
/*
* We want notice of changes of the PGXWIN_CLIENT_DATA property.
*/
XSelectInput(xw->display, xw->client, (long) PropertyChangeMask);
if(xw->bad_device)
return del_XWdev(xw,0);
/*
* Get the server selection atom and the client data transfer atom.
*/
xw->server_atom = XInternAtom(xw->display, PGXWIN_SERVER, False);
if(xw->bad_device)
return del_XWdev(xw,0);
xw->client_data = XInternAtom(xw->display, "PGXWIN_CLIENT_DATA", False);
if(xw->bad_device)
return del_XWdev(xw,0);
/*
* Get the server window ID.
*/
if(xw_get_server(xw) == None)
return del_XWdev(xw,0);
/*
* Get a new PGPLOT window.
*/
if(xw_get_window(xw) == None)
return del_XWdev(xw,0);
/*
* We want to know if the PGPLOT window gets destroyed.
*/
if(xw_add_events(xw, (long) StructureNotifyMask))
return del_XWdev(xw,0);
/*
* Get the visual and colormap of the window.
*/
if(xw_get_visual(xw))
return del_XWdev(xw,0);
/*
* Set/get the current geometry for the window.
*/
if(xw_new_geom(xw, 0,0, 0,0, 0))
return del_XWdev(xw,0);
/*
* Get the IDs of the normal and active cursors.
*/
if(xw_get_cursors(xw))
return del_XWdev(xw,0);
/*
* Instate the normal cursor for the window.
*/
if(xw_set_cursor(xw, 1))
return del_XWdev(xw,0);
/*
* Create and initialize a graphical context descriptor. This is where
* Line widths, line styles, fill styles, plot color etc.. are
* recorded.
*/
xw->gcv.line_width = 1;
xw->gcv.cap_style = CapRound;
xw->gcv.join_style = JoinRound;
xw->gcv.fill_rule = EvenOddRule;
xw->gcv.graphics_exposures = False;
xw->gcv.foreground = WhitePixel(xw->display, xw->screen);
xw->gc = XCreateGC(xw->display, xw->window, (unsigned long) (GCLineWidth |
GCCapStyle | GCJoinStyle | GCFillRule | GCGraphicsExposures |
GCForeground), &xw->gcv);
if(xw->gc==NULL || xw->bad_device) {
fprintf(stderr, "%s: Failed to allocate graphical context.\n", XW_IDENT);
return del_XWdev(xw,0);
};
/*
* Allocate the buffers that will be used to compose a line
* of pixels.
*/
if(xw_get_image(xw, XW_IMAGE_LEN))
return del_XWdev(xw,0);
/*
* Return the initialized descriptor for use.
*/
return xw;
}
/*.......................................................................
* Delete a PGPLOT /xw device and its descriptor.
*
* Input:
* xw XWdev * The descriptor of the device to be deleted.
* partial int 0 - Normal deletion - delete everything.
* 1 - Close the display connection and mark all
* resources as deleted but don't delete the
* container - also set xw->bad_device==1.
* Output:
* return XWdev * Allways NULL. Use like xw = del_XWdev(xw,0);
*/
#ifdef __STDC__
static XWdev *del_XWdev(XWdev *xw, int partial)
#else
static XWdev *del_XWdev(xw, partial)
XWdev *xw; int partial;
#endif
{
if(xw) {
/*
* Mark the device as unusable as the first operation so that if
* any X errors are generated during cleanup, they are not reported.
*/
xw->bad_device = 1;
/*
* Delete the graphical context descriptor.
*/
if(xw->gc)
XFreeGC(xw->display, xw->gc);
xw->gc = NULL;
/*
* Delete the image buffers.
*/
if(xw->image.xi)
XDestroyImage(xw->image.xi);
xw->image.xi = NULL;
/*
* Check for un-freed polygon points.
*/
if(xw->poly.points)
free((char *)xw->poly.points);
xw->poly.points = NULL;
/*
* Zap the arrays of color pixels and color pixel definitions.
*/
if(xw->color.pixel)
free((char *)xw->color.pixel);
if(xw->color.xcolor)
free((char *)xw->color.xcolor);
/*
* Discard the visual info descriptor.
*/
if(xw->color.vi)
XFree((char *)xw->color.vi);
/*
* Close the connection to the display server - this will also delete
* all X-resources.
*/
if(xw->display != NULL) {
/*
* Explicitly clear the local event mask for the PGPLOT /xw window in case
* XCloseDisplay fails to do this.
*/
if(xw->window != None)
XSelectInput(xw->display, xw->window, (long) NoEventMask);
XCloseDisplay(xw->display);
xw->display = NULL;
};
/*
* Mark effected resources as deleted.
*/
xw->client = xw->server = xw->window = xw->parent = None;
xw->server_atom = xw->client_data = None;
xw->pixmap = xw->norm_cursor = xw->live_cursor = None;
xw->flush_opcode_fn = (Flush_Opcode_fn) 0;
xw->update.modified = 0;
/*
* Delete the descriptor if required.
*/
if(!partial) {
free((char *)xw);
xw = NULL;
};
};
return xw;
}
/*.......................................................................
* Before using a given /xw device descriptor call this function to check
* that it is usable. If it isn't, 0 will be returned and you should not
* attempt to use the descriptor. If the descriptor is NULL an error
* message will be presented.
*
* Input:
* xw XWdev * The device descriptor to be checked.
* Output:
* return int 1 - Descriptor OK.
* 0 - Error - don't use /xw.
*/
#ifdef __STDC__
static int xw_ok(XWdev *xw)
#else
static int xw_ok(xw)
XWdev *xw;
#endif
{
if(xw==NULL) {
fprintf(stderr, "%s: Device not open.\n", XW_IDENT);
return 0;
};
/*
* If the window is marked as unusable, it must have been set that way
* after an error was detected. Assume that the error must already
* have been reported.
*/
if(xw->bad_device)
return 0;
return 1;
}
/*.......................................................................
* Present the active cursor, wait for the user to press a button or
* keyboard key, then retrack the active cursor and return the cursor
* position and key pressed.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* mode int 0 - No rubber banding.
* 1 - Maintain a rectangle outline with opposing
* vertexes at (xin,yin) and the cursor.
* 2 - Maintain a line between (xin,yin) and the cursor.
* negative: -mode = timeout in deciSec
* posn int 0 - Don't attempt to position the cursor.
* 1 - Do try to pre-position the cursor.
* ref XPoint * The reference position of the cursor (can be the same
* as 'pos').
* Input/Output:
* pos XPoint * The start position of the cursor. On output this is the
* selected position of the cursor.
* Output:
* key char * If key!=NULL, the selection key will be assigned to
* the caller's variable pointed to by key.
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_read_cursor(XWdev *xw, int mode, int posn, XPoint *ref,
XPoint *pos, char *key)
#else
static int xw_read_cursor(xw, mode, posn, ref, pos, key)
XWdev *xw; int mode; int posn; XPoint *ref; XPoint *pos; char *key;
#endif
{
int finished = 0; /* True when cursor succesfully read */
XEvent event; /* The latest event */
XPoint last; /* Last recorded position of cursor */
Band *bc=NULL; /* Band-cursor descriptor */
int warped=0; /* Zero until the cursor has been positioned */
int tmo_10=0;
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Ensure that the input positions are within the pixmap and window bounds.
*/
if(xw_bound_cursor(xw, ref) || xw_bound_cursor(xw, pos))
return 1;
/*
* Present the active cursor.
*/
if(xw_set_cursor(xw, 0))
return xw_end_cursor(xw, bc, 1);
/*
* Make sure that the window is up to date.
*/
if(xw_flush(xw))
return xw_end_cursor(xw, bc, 1);
/*
* get timeout
*/
tmo_10=0; /* M.Z */
if (mode<0) {
tmo_10=-mode*10;
}
/*
* De-iconify and bring the window to the foreground.
*/
if (xw->nofocus) { /* M.Z. */
XMapWindow(xw->display, xw->window);
} else {
XMapRaised(xw->display, xw->window);
}
if(xw->bad_device)
return xw_end_cursor(xw, bc, 1);
XSync(xw->display, False);
if(xw->bad_device)
return xw_end_cursor(xw, bc, 1);
/*
* Set up for modes that maintain elastic lines following the cursor.
*/
if((bc=xw_new_Band(xw, mode, ref))==NULL)
return xw_end_cursor(xw, bc, 1);
/*
* If the cursor is in the window, locate its position,
* after warping if requested.
*/
if(xw_locate_cursor(xw, pos, posn, &last)) {
warped = 1;
/*
* Draw the cursor.
*/
if(xw->bad_device || xw_bound_cursor(xw, &last) ||
xw_draw_cursor(xw, bc, &last))
return xw_end_cursor(xw, bc, 1);
};
/*
* Discard un-handled ButtonPress, KeyPress and MotionNotify events.
*/
/* removed M.Z.
if (xw->nofocus) {
while(xw_check_window_event(xw, xw->window, (long)
(ButtonPressMask | PointerMotionMask), &event));
} else {
while(xw_check_window_event(xw, xw->window, (long)
(ButtonPressMask | KeyPressMask | PointerMotionMask), &event));
}
*/
if(xw->bad_device)
return xw_end_cursor(xw, bc, 1);
xw->nofocus=0; /* M.Z. */
/*
* Loop for cursor events.
*/
while(!finished) {
/*
* Handle the next selected event.
*/
if(xw_next_event_tmo(xw, &event, tmo_10)) /* M.Z */
return xw_end_cursor(xw, bc, 1);
switch(event.type) {
case 0: /* time out M.Z.*/
xw->nofocus=1;
return xw_end_cursor(xw, bc, 1);
case Expose:
if(xw_expose(xw, &event))
return xw_end_cursor(xw, bc, 1);
break;
case ButtonPress:
/*
* Return the position at which the cursor was selected.
*/
pos->x = event.xbutton.x;
pos->y = event.xbutton.y;
/*
* Return the key alias of the button that selected the cursor.
*/
if(key) {
switch(event.xbutton.button) {
case Button1:
*key = 'A';
break;
case Button2:
*key = 'D';
break;
default:
*key = 'X';
break;
};
};
finished = 1;
break;
case KeyPress:
{
char buffer[10]; /* Buffer to read key definition into */
KeySym keysym; /* Key code of pressed keyboard key */
int nret; /* The number of characters in buffer[] */
/*
* Get the ASCII encoding associated with the key.
*/
nret = XLookupString((XKeyEvent *)&event, buffer,
(int) (sizeof(buffer)/sizeof(char)), &keysym, NULL);
if(xw->bad_device)
return xw_end_cursor(xw, bc, 1);
/*
* Ignore modifier keys and all but single character keys.
*/
if(nret==1 && (keysym < XK_Shift_L || keysym > XK_Hyper_R)) {
pos->x = event.xkey.x;
pos->y = event.xkey.y;
if(key)
*key = buffer[0];
finished = 1;
};
/*
* Check for arrow keys.
*/
switch(keysym) {
#ifdef XK_KP_Left
case XK_KP_Left:
#endif
case XK_Left:
#ifdef XK_KP_Right
case XK_KP_Right:
#endif
case XK_Right:
#ifdef XK_KP_Up
case XK_KP_Up:
#endif
case XK_Up:
#ifdef XK_KP_Down
case XK_KP_Down:
#endif
case XK_Down:
if(xw_shift_cursor(xw, keysym, event.xkey.state))
return xw_end_cursor(xw, bc, 1);
break;
};
};
break;
case EnterNotify:
/*
* The cursor may still be drawn if a button was pressed when the
* cursor was last moved out of the window. The resulting
* passive grab will have continued to deliver motion events to
* the PGPLOT window.
*/
if(xw_erase_cursor(xw, bc))
return xw_end_cursor(xw, bc, 1);
/*
* If the cursor is in the window, locate its position. If this is
* the first time that the cursor has been in the window and warping
* has been requested, this also inolves pre-positioning the cursor
* and setting input focus.
*/
if(xw_locate_cursor(xw, pos, posn && !warped, &last)) {
warped = 1;
/*
* Draw the cursor.
*/
if(xw->bad_device || xw_bound_cursor(xw, &last) ||
xw_draw_cursor(xw, bc, &last))
return xw_end_cursor(xw, bc, 1);
};
break;
case LeaveNotify:
if(xw_erase_cursor(xw, bc))
return xw_end_cursor(xw, bc, 1);
break;
case MotionNotify:
/*
* Discard all but the last MotionNotify event.
*/
while(xw_check_window_event(xw, xw->window, (long)(PointerMotionMask),
&event));
if(xw->bad_device || xw_erase_cursor(xw, bc))
return xw_end_cursor(xw, bc, 1);
last.x = event.xmotion.x;
last.y = event.xmotion.y;
if(xw_bound_cursor(xw, &last) || xw_draw_cursor(xw, bc, &last))
return xw_end_cursor(xw, bc, 1);
break;
default:
break;
};
};
/*
* Clean up.
*/
return xw_end_cursor(xw, bc, xw->bad_device!=0);
}
/*.......................................................................
* This is a private function of xw_read_cursor(). If the user has just
* pressed one of the keyboard or keypad arrow keys, it moves the cursor
* by one pixel in the corresponding direction. If one of the shift keys
* is also held down, then the cursor is moved by ARROW_KEY_VELOCITY
* pixels instead of one. If the resulting shift would move the cursor
* out of the bounds of the pgplot window pixmap, then the motion is
* aborted.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* keysym KeySym The key symbol returned by XLookupString() wrt
* the arrow-button key-press.
* modifiers unsigned The Event::xkey.state key-modifier mask
* associated with the key-press.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_shift_cursor(XWdev *xw, KeySym keysym, unsigned int modifiers)
#else
static int xw_shift_cursor(xw, keysym, modifiers)
XWdev *xw; KeySym keysym; unsigned int modifiers;
#endif
{
Window p_child; /* The child window that contains the pointer */
int p_win_x, p_win_y; /* The pointer coordinates wrt xw->window */
int p_root_x, p_root_y; /* The pointer coordinates wrt the root window */
Window p_root_win; /* The root window that contains the cursor */
unsigned int p_mask; /* The bit-mask of button states etc.. */
int dx=0; /* The amount to move the cursor in X */
int dy=0; /* The amount to move the cursor in Y */
/*
* Determine the current position of the cursor.
*/
XQueryPointer(xw->display, xw->window, &p_root_win, &p_child,
&p_root_x, &p_root_y, &p_win_x, &p_win_y, &p_mask);
if(xw->bad_device)
return 1;
/*
* Work out the position increments in x and y.
*/
switch(keysym) {
#ifdef XK_KP_Left
case XK_KP_Left:
#endif
case XK_Left:
dx = -1;
break;
#ifdef XK_KP_Right
case XK_KP_Right:
#endif
case XK_Right:
dx = 1;
break;
#ifdef XK_KP_Up
case XK_KP_Up:
#endif
case XK_Up:
dy = -1;
break;
#ifdef XK_KP_Down
case XK_KP_Down:
#endif
case XK_Down:
dy = 1;
break;
default:
return 0;
break;
};
/*
* If one of the shift keys is held down, increase the size of the
* move to ARROW_KEY_VELOCITY pixels in the specified direction.
*/
if(modifiers & ShiftMask) {
dx *= ARROW_KEY_VELOCITY;
dy *= ARROW_KEY_VELOCITY;
};
/*
* Determine the final position of the pointer wrt the top left corner
* of the window.
*/
p_win_x += dx;
p_win_y += dy;
/*
* Abort the shift operation if the final position lies outside of the
* bounds of the pgplot window pixmap. Note that this simple test doesn't
* take account of the fact that another window may lie over the pgplot
* window, or that the window may have been resized to a smaller size
* than the pixmap. To do this properly one would have to perform the
* move, then check for LeaveNotify events and put the cursor back if
* one was detected. This would be hard to code without breaking
* xw_read_cursor() which also wants LeaveNotify events, would be
* slower to operate and would be unavoidably subject to race conditions.
*/
if(p_win_x < 0 || p_win_x >= xw->geom.width ||
p_win_y < 0 || p_win_y >= xw->geom.height)
return 0;
/*
* Move the cursor to the new location.
*/
XWarpPointer(xw->display, None, xw->window, 0, 0, 0, 0, p_win_x, p_win_y);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Private return function of xw_read_cursor().
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* bc Band * The cursor banding descriptor to be deleted.
* status int Required xw_read_cursor() return status.
* Output:
* return int The value of 'status'.
*/
#ifdef __STDC__
static int xw_end_cursor(XWdev *xw, Band *bc, int status)
#else
static int xw_end_cursor(xw, bc, status)
XWdev *xw; Band *bc; int status;
#endif
{
if(bc) {
if(xw_erase_cursor(xw, bc))
status=1;
if(xw_flush(xw))
status=1;
bc = xw_del_Band(xw, bc);
};
if(xw_set_cursor(xw,1))
status=1;
return status;
}
/*.......................................................................
* Convert from the coordinates sent by PGPLOT in rbuf[...] to an
* X-windows point in the coordinate system of the window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* xy float [2] Array of two floats containing PGPLOT coordinates
* arranged as x followed by y.
* Output:
* xp XPoint * The converted coordinates will be assigned to xp->x
* and xp->y.
*/
#ifdef __STDC__
static void xw_xy_to_XPoint(XWdev *xw, float *xy, XPoint *xp)
#else
static void xw_xy_to_XPoint(xw, xy, xp)
XWdev *xw; float *xy; XPoint *xp;
#endif
{
xp->x = xw->geom.xmin + (int)(xy[0] + 0.5);
xp->y = xw->geom.ymax - (int)(xy[1] + 0.5);
}
/*.......................................................................
* Convert from window pixel coordinates to PGPLOT coordinates, in a
* form that can be returned to PGPLOT via rbuf[...].
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* xp XPoint * The window pixel-coordinates to be converted.
* Output:
* xy float [2] Output array of two floats in which to place the
* PGPLOT coordinates, arranged as x followed by y.
*/
#ifdef __STDC__
static void xw_XPoint_to_xy(XWdev *xw, XPoint *xp, float *xy)
#else
static void xw_XPoint_to_xy(xw, xp, xy)
XWdev *xw; XPoint *xp; float *xy;
#endif
{
xy[0] = (float) (xp->x - xw->geom.xmin);
xy[1] = (float) (xw->geom.ymax - xp->y);
}
/*.......................................................................
* Start a new page by clearing and possibly re-sizing the given /xw
* window and its pixmap. If the current size of the window is equal
* to the requested new size, then only the window clearing operation
* will be performed.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor of the window to be
* resized.
* width unsigned The new width for the re-sized window (pixels).
* height unsigned The new height for the re-sized window (pixels).
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_next_page(XWdev *xw, unsigned int width, unsigned int height)
#else
static int xw_next_page(xw, width, height)
XWdev *xw; unsigned int width; unsigned int height;
#endif
{
int need_resize; /* True if the current pixmap size needs to be changed */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Does the pixmap need to be resized?
*/
need_resize = width != xw->geom.width || height != xw->geom.height;
/*
* Establish the new geometry with the server.
*/
if(need_resize) {
if(xw_new_geom(xw, 0,0, width,height, (WidthValue|HeightValue)))
return 1;
};
/*
* Reset the colormap color representations if necessary.
*/
if(!xw->color.initialized && xw_init_colors(xw))
return 1;
/*
* Allocate a new pixmap?
*/
if(xw->pixmap==None || need_resize) {
xw_get_pixmap(xw);
if(xw->bad_device)
return 1;
/*
* If a new pixmap is not required, simply clear the window and pixmap.
*/
} else {
if(xw_clear(xw))
return 1;
/*
* Also ensure that the window has the same size as the pixmap.
*/
XResizeWindow(xw->display, xw->window, xw->geom.width, xw->geom.height);
};
return 0;
}
/*.......................................................................
* Draw a horizontal line of pixels at a given location, from a float
* array of PGPLOT color indexes.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* start XPoint * The position to start the line at.
* cells float * An array of ncell pixel PGPLOT color indexes.
* ncell int The number of cells in cells[].
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_image_line(XWdev *xw, XPoint *start, float *cells, int ncell)
#else
static int xw_image_line(xw, start, cells, ncell)
XWdev *xw; XPoint *start; float *cells; int ncell;
#endif
{
int ndone; /* The number of pixels drawn so far */
int i;
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Quietly ignore the call if we don't have a pixmap.
*/
if(xw->pixmap != None) {
/*
* Draw up to xw->image.npix pixels at a time. This is the size of the
* xw->image.buff[] buffer.
*/
for(ndone=0; !xw->bad_device && ndone<ncell; ndone += XW_IMAGE_LEN) {
int ntodo = ncell-ndone;
int nimage = ntodo < XW_IMAGE_LEN ? ntodo : XW_IMAGE_LEN;
/*
* Load the image buffer with the color cell indexes assigned to the
* given PGPLOT color indexes.
*/
if(xw->color.vi->depth == 8) {
for(i=0; i<nimage; i++)
xw->image.xi->data[i] = xw->color.pixel[(int)(cells[ndone+i] + 0.5)];
} else {
for(i=0; i<nimage; i++) {
XPutPixel(xw->image.xi, i, 0,
xw->color.pixel[(int) (cells[ndone+i] + 0.5)]);
};
};
/*
* Display the image.
*/
XPutImage(xw->display, xw->pixmap, xw->gc, xw->image.xi, 0, 0,
start->x+ndone, start->y, (unsigned) nimage, (unsigned) 1);
};
/*
* Extend the region to be updated on the next flush.
*/
xw_mark_modified(xw, start->x, start->y, 1);
xw_mark_modified(xw, start->x + ncell - 1, start->y, 1);
};
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Call this function when an Expose event is received. It will then
* re-draw the exposed region from the xw->pixmap.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* event XEvent * The expose event.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_expose(XWdev *xw, XEvent *event)
#else
static int xw_expose(xw, event)
XWdev *xw; XEvent *event;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
if(event->type == Expose && xw->pixmap != None) {
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc,
event->xexpose.x, event->xexpose.y,
(unsigned) event->xexpose.width, (unsigned) event->xexpose.height,
event->xexpose.x, event->xexpose.y);
if(xw->bad_device)
return 1;
XFlush(xw->display);
if(xw->bad_device)
return 1;
};
return 0;
}
/*.......................................................................
* Set up for a band cursor and return its descriptor.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* mode int 0 - No band cursor.
* 1 - Line between reference position and cursor.
* 2 - Rectangle drawn with opposite corners at reference
* and cursor position.
* ref XPoint * The reference position.
* Output:
* return Band * A pointer to a static internal container of cursor
* resources. Call xw_del_Band() to release these resources
* and return the event mask to normal.
*/
#ifdef __STDC__
static Band *xw_new_Band(XWdev *xw, int mode, XPoint *ref)
#else
static Band *xw_new_Band(xw, mode, ref)
XWdev *xw; int mode; XPoint *ref;
#endif
{
static Band band; /* Return container */
long event_mask=0; /* Bit map of events to be caught */
/*
* Device error?
*/
if(xw->bad_device)
return NULL;
/*
* Initialize values at least to the point at which xw_del_Band() can
* safely be called.
*/
band.line_width = xw->gcv.line_width;
band.mode = mode;
band.ref = *ref;
band.end = *ref;
/*
* All cursor types require us to catch the following events.
*/
event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
EnterWindowMask | LeaveWindowMask;
/*
* Set up for a band cursor?
*/
if(band.mode > 0 || xw->crosshair) { /* M.Z. */
/*
* Arrange for the band cursor to be drawn with a line width of 0.
*/
if(band.line_width != 0) {
XGCValues attr;
band.line_width = attr.line_width = 0;
XChangeGC(xw->display, xw->gc, (unsigned long) GCLineWidth, &attr);
if(xw->bad_device)
return NULL;
};
/*
* Select for cursor motion events along with the normal events.
*/
event_mask |= PointerMotionMask;
};
/*
* Register the additional event types that are now to be caught.
*/
if(xw_add_events(xw, event_mask))
return NULL;
return &band;
}
/*.......................................................................
* Release band cursor resources and return the window event mask to
* its normal state.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* bc Band * The band-cursor descriptor.
* Output:
* return Band * Always NULL.
*/
#ifdef __STDC__
static Band *xw_del_Band(XWdev *xw, Band *bc)
#else
static Band *xw_del_Band(xw, bc)
XWdev *xw; Band *bc;
#endif
{
/*
* Prevent the event buffer from overflowing by removing superflous events
* from the set of those to be caught.
*/
xw_rem_events(xw, (long) (ExposureMask | KeyPressMask | ButtonPressMask |
EnterWindowMask | LeaveWindowMask |
PointerMotionMask));
/*
* If the line width was changed for rubber banding, re-instate the
* original line width.
*/
if(bc->line_width != xw->gcv.line_width)
XChangeGC(xw->display, xw->gc, (unsigned long) GCLineWidth, &xw->gcv);
return NULL;
}
/*.......................................................................
* Trim a coordinate to lie within the current pixmap and window area.
* This prevents the cursor being displayed or returned for a position
* outside the currently visible window and pixmap areas.
*
* Input:
* xp XPoint * The point to be limited.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_bound_cursor(XWdev *xw, XPoint *xp)
#else
static int xw_bound_cursor(xw, xp)
XWdev *xw; XPoint *xp;
#endif
{
XWindowAttributes attr; /* Current window attributes */
int xmax, ymax; /* Max usable X and Y coordinates */
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Get the current window dimensions.
*/
XGetWindowAttributes(xw->display, xw->window, &attr);
if(xw->bad_device)
return 1;
/*
* With NorthWest pixmap gravity, coordinates 0,0 are always visible at the
* top left corner of the plot.
*/
if(xp->x < 0)
xp->x = 0;
if(xp->y < 0)
xp->y = 0;
/*
* Determine the max X and Y coordinates that fall both within the pixmap and
* the window.
*/
xmax = ((xw->geom.width < attr.width) ? xw->geom.width : attr.width) - 1;
ymax = ((xw->geom.height < attr.height) ? xw->geom.height : attr.height) - 1;
/*
* Limit the coordinates to the above range.
*/
if(xp->x > xmax)
xp->x = xmax;
if(xp->y > ymax)
xp->y = ymax;
return 0;
}
/*.......................................................................
* Draw the current cursor.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* bc Band * A cursor descriptor returned by xw_new_band().
* end XPoint * The current cursor position.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_draw_cursor(XWdev *xw, Band *bc, XPoint *end)
#else
static int xw_draw_cursor(xw, bc, end)
XWdev *xw; Band *bc; XPoint *end;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Store the new end point.
*/
bc->end = *end;
/*
* Draw the cursor.
*/
switch(bc->mode) {
case 0: default:
if(xw->gc!=NULL && xw->crosshair) {
XDrawLine(xw->display, xw->window, xw->gc, 0, bc->end.y,
(int)xw->geom.width-1, bc->end.y);
if(xw->bad_device)
return 1;
XDrawLine(xw->display, xw->window, xw->gc, bc->end.x, 0,
bc->end.x, (int)xw->geom.height-1);
};
break;
case 1:
XDrawLine(xw->display, xw->window, xw->gc, bc->ref.x, bc->ref.y,
bc->end.x, bc->end.y);
break;
case 2: /* Draw a rectangle */
{
int x = bc->ref.x < bc->end.x ? bc->ref.x : bc->end.x;
int y = bc->ref.y < bc->end.y ? bc->ref.y : bc->end.y;
unsigned int width = (unsigned int) abs(bc->ref.x - bc->end.x);
unsigned int height = (unsigned int) abs(bc->ref.y - bc->end.y);
XDrawRectangle(xw->display, xw->window, xw->gc, x, y, width, height);
};
break;
case 3: /* Two horizontal lines */
XDrawLine(xw->display, xw->window, xw->gc, 0, bc->end.y,
(int)xw->geom.width-1, bc->end.y);
if(xw->bad_device)
return 1;
XDrawLine(xw->display, xw->window, xw->gc, 0, bc->ref.y,
(int)xw->geom.width-1, bc->ref.y);
break;
case 4: /* Two vertical lines */
XDrawLine(xw->display, xw->window, xw->gc, bc->end.x, 0,
bc->end.x, (int)xw->geom.height-1);
if(xw->bad_device)
return 1;
XDrawLine(xw->display, xw->window, xw->gc, bc->ref.x, 0,
bc->ref.x, (int)xw->geom.height-1);
break;
case 5: /* One horizontal line through the cursor */
XDrawLine(xw->display, xw->window, xw->gc, 0, bc->end.y,
(int)xw->geom.width-1, bc->end.y);
break;
case 6: /* One vertical line through the cursor */
XDrawLine(xw->display, xw->window, xw->gc, bc->end.x, 0,
bc->end.x, (int)xw->geom.height-1);
break;
case 7: /* Cross hair */
XDrawLine(xw->display, xw->window, xw->gc, 0, bc->end.y,
(int)xw->geom.width-1, bc->end.y);
if(xw->bad_device)
return 1;
XDrawLine(xw->display, xw->window, xw->gc, bc->end.x, 0,
bc->end.x, (int)xw->geom.height-1);
break;
};
if(xw->bad_device)
return 1;
XFlush(xw->display);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Erase a previously drawn cursor.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* bc Band * A cursor descriptor returned by xw_new_band().
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_erase_cursor(XWdev *xw, Band *bc)
#else
static int xw_erase_cursor(xw, bc)
XWdev *xw; Band *bc;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Erase the cursor.
*/
switch(bc->mode) {
case 0: default:
if(xw->crosshair) {
if(xw_cursor_line(xw, 0, bc->end.y, (int)xw->geom.width-1,bc->end.y) ||
xw_cursor_line(xw, bc->end.x, 0, bc->end.x, (int)xw->geom.height-1))
return 1;
};
break;
case 1: /* Line cursor */
if(xw_cursor_line(xw, bc->ref.x, bc->ref.y, bc->end.x, bc->end.y))
return 1;
break;
case 2: /* Rectangle cursor */
if(xw_cursor_line(xw, bc->ref.x, bc->ref.y, bc->ref.x, bc->end.y) ||
xw_cursor_line(xw, bc->ref.x, bc->end.y, bc->end.x, bc->end.y) ||
xw_cursor_line(xw, bc->end.x, bc->end.y, bc->end.x, bc->ref.y) ||
xw_cursor_line(xw, bc->end.x, bc->ref.y, bc->ref.x, bc->ref.y))
return 1;
break;
case 3: /* Two horizontal lines */
if(xw_cursor_line(xw, 0, bc->end.y, (int)xw->geom.width-1,bc->end.y) ||
xw_cursor_line(xw, 0, bc->ref.y, (int)xw->geom.width-1,bc->ref.y))
return 1;
break;
case 4: /* Two vertical lines */
if(xw_cursor_line(xw, bc->end.x, 0, bc->end.x, (int)xw->geom.height-1) ||
xw_cursor_line(xw, bc->ref.x, 0, bc->ref.x, (int)xw->geom.height-1))
return 1;
break;
case 5: /* One horizontal line through the cursor */
if(xw_cursor_line(xw, 0, bc->end.y, (int)xw->geom.width-1,bc->end.y))
return 1;
break;
case 6: /* One vertical line through the cursor */
if(xw_cursor_line(xw, bc->end.x, 0, bc->end.x, (int)xw->geom.height-1))
return 1;
break;
case 7: /* Cross hair */
if(xw_cursor_line(xw, 0, bc->end.y, (int)xw->geom.width-1,bc->end.y) ||
xw_cursor_line(xw, bc->end.x, 0, bc->end.x, (int)xw->geom.height-1))
return 1;
break;
};
return 0;
}
/*.......................................................................
* Restore the pixels under a given line.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* xa, ya int The start pixel of the line.
* xb, yb int The end pixel of the line.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_cursor_line(XWdev *xw, int xa, int ya, int xb, int yb)
#else
static int xw_cursor_line(xw, xa, ya, xb, yb)
XWdev *xw; int xa; int ya; int xb; int yb;
#endif
{
int xlen = xb - xa; /* X-axis displacement of line */
int ylen = yb - ya; /* Y-axis displacement of line */
int xmin,xmax; /* Min/max X-axis end points */
int ymin,ymax; /* Min/max Y-axis end points */
#define PIXINC 51
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Silently ignore the call if a pixmap is not available.
*/
if(xw->pixmap != None) {
/*
* Get sorted versions of xa and xb.
*/
if(xlen > 0) {
xmin = xa;
xmax = xb;
} else {
xmin = xb;
xmax = xa;
};
/*
* Get sorted versions of ya and yb.
*/
if(ylen > 0) {
ymin = ya;
ymax = yb;
} else {
ymin = yb;
ymax = ya;
};
/*
* Vertical line?
*/
if(xlen==0) {
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc, xmin, ymin,
(unsigned) 1, (unsigned) (ymax-ymin+1), xmin, ymin);
}
/*
* Horizontal line?
*/
else if(ylen==0) {
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc, xmin, ymin,
(unsigned) (xmax-xmin+1), (unsigned) 1, xmin, ymin);
}
/*
* Diagonal line encompasing fewer x-axis lines that y-axis lines?
*/
else if(abs(xlen) <= abs(ylen)) {
int x; /* The X coordinate of the line of pixels being drawn */
int y1,y2; /* The start and end Y coordinates of the pixel line */
double yperx = (double) ylen / (double) xlen;
double yhalf = 0.5 * yperx; /* Y-step over half a pixel */
double ydelt = (PIXINC+0.5) * yperx; /* Y-step over PIXINC+0.5 pixels */
double ylo = yperx > 0 ? yhalf : -ydelt;
double yhi = yperx > 0 ? ydelt : -yhalf;
/*
* Draw the block of pixels that encompases the line between X-axis
* pixels the outer edges of pixels x -> x+PIXINC, for each consecutive
* block of PIXINC pixels along X.
*/
for(x=xmin; x <= xmax; x += PIXINC+1) {
double ycent = ya + (x - xa) * yperx;
y1 = (int)(ycent - ylo); /* Note round-down semantics */
y2 = (int)(ycent + yhi+0.5);/* Note round-up semantics */
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc,
x, y1, (unsigned) (PIXINC+1), (unsigned) (y2-y1+1), x, y1);
};
/*
* Diagonal line encompasing fewer y-axis lines that x-axis lines?
*/
} else {
int y; /* The Y coordinate of the line of pixels being drawn */
int x1,x2; /* The start and end X coordinates of the pixel line */
double xpery = (double) xlen / (double) ylen;
double xhalf = 0.5 * xpery; /* X-step over half a pixel */
double xdelt = (PIXINC+0.5) * xpery; /* X-step over PIXINC+0.5 pixels */
double xlo = xpery > 0 ? xhalf : -xdelt;
double xhi = xpery > 0 ? xdelt : -xhalf;
/*
* Draw the block of pixels that encompases the line between Y-axis
* pixels the outer edges of pixels y -> y+PIXINC, for each consecutive
* block of PIXINC pixels along Y.
*/
for(y=ymin; y <= ymax; y += PIXINC+1) {
double xcent = xa + (y - ya) * xpery;
x1 = (int)(xcent - xlo); /* Note round-down semantics */
x2 = (int)(xcent + xhi+0.5);/* Note round-up semantics */
XCopyArea(xw->display, xw->pixmap, xw->window, xw->gc,
x1, y, (unsigned) (x2-x1+1), (unsigned) (PIXINC+1), x1, y);
};
};
};
/*
* Check for device errors.
*/
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* Add to the set of events to be caught.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* events long The bit mask of events to be added to those already
* being selected.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_add_events(XWdev *xw, long events)
#else
static int xw_add_events(xw, events)
XWdev *xw; long events;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Get the union of the new events with the current event mask.
*/
xw->event.mask |= events;
/*
* Register the modified selection with the server.
*/
XSync(xw->display, False);
if(xw->bad_device)
return 1;
xw->last_error = 0;
XSelectInput(xw->display, xw->window, xw->event.mask);
if(xw->bad_device)
return 1;
/*
* Force the new selections through to the server and check for access
* errors that indicate that ButtonPress's are currently selected by
* another client.
*/
XSync(xw->display, False);
if(xw->bad_device)
return 1;
if(xw->last_error == BadAccess) {
/*
* Only one client can select ButtonPress events. If another client
* already has them selected, then the above XSelectInputs() will have
* generated an error and the event mask will not have been installed.
*/
if(xw->event.mask & ButtonPressMask) {
if(!xw->event.no_buttons) {
fprintf(stderr,
"%s: Failed to acquire pointer buttons - use keys A,D,X.\n",
XW_IDENT);
};
xw->event.no_buttons = 1;
};
/*
* Retry, but with the events that could have caused the BadAccess removed.
*/
xw->event.mask &= ~(SubstructureRedirectMask | ResizeRedirectMask |
ButtonPressMask);
XSelectInput(xw->display, xw->window, xw->event.mask);
if(xw->bad_device)
return 1;
XSync(xw->display, False);
if(xw->bad_device)
return 1;
};
/*
* Have we successfully acquired ButtonPress events?
*/
if(xw->event.mask & ButtonPressMask)
xw->event.no_buttons = 0;
return 0;
}
/*.......................................................................
* Remove selected events from the set of events to be caught.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* events long The bit mask of events to be removed from the set
* being selected.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_rem_events(XWdev *xw, long events)
#else
static int xw_rem_events(xw, events)
XWdev *xw; long events;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Clear the bits in our current event mask that correspond to the events
* to be removed.
*/
xw->event.mask &= ~events;
/*
* Register the modified selection with the server.
*/
XSelectInput(xw->display, xw->window, xw->event.mask);
if(xw->bad_device)
return 1;
XSync(xw->display, False);
if(xw->bad_device)
return 1;
return 0;
}
/*.......................................................................
* This function is called by X whenever a non-fatal error occurs
* on a given display connection.
*
* Input:
* display Display * The display connection on which the error occured.
* event XErrorEvent * The descriptor of the error event.
* Output:
* return int The return value is not specified by Xlib, so
* we will simply return 0.
*/
#ifdef __STDC__
static int xw_error(Display *display, XErrorEvent *event)
#else
static int xw_error(display, event)
Display *display; XErrorEvent *event;
#endif
{
char errtxt[81]; /* Buffer to receive error message in */
XWdev *xw;
/*
* Find the device that is the source of the error.
*/
for(xw=device_list; xw!=NULL && xw->display!=display; xw = xw->next);
/*
* If a device was located, check if the error implies that server resources
* have become unusable for that device.
*/
if(xw && !xw->bad_device) {
xw->last_error = event->error_code;
switch(event->error_code) {
case BadAtom: case BadColor: case BadCursor: case BadDrawable:
case BadGC: case BadIDChoice: case BadPixmap: case BadWindow:
/*
* Get a message describing the error.
*/
XGetErrorText(display, (int)event->error_code,errtxt,(int)sizeof(errtxt));
fprintf(stderr, "%s: XErrorEvent: %s\n", XW_IDENT, errtxt);
/*
* Report the operation that caused it. These opcode numbers are listed in
* <X11/Xproto.h>.
*/
fprintf(stderr, "%s: Major opcode of failed request: %d\n", XW_IDENT,
(int) event->request_code);
/*
* Report the loss of the window and mark the device as unusable.
*/
xw_bad_device(xw);
break;
};
};
return 0;
}
/*.......................................................................
* If a PGPLOT /xw server has not already been started, start one.
* Return the ID of the server communication window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Output:
* return Window The XID of the server communication window, or None
* on error.
*/
#ifdef __STDC__
static Window xw_get_server(XWdev *xw)
#else
static Window xw_get_server(xw)
XWdev *xw;
#endif
{
int i;
/*
* See if a server has already been started.
*/
xw->server = XGetSelectionOwner(xw->display, xw->server_atom);
/*
* Start a new server if necessary.
*/
if(xw->server == None) {
char *exe=NULL; /* The name of the pgxwin_server executable */
char *command=NULL; /* The command-line string used to run it */
char *display_string; /* The display-name string */
unsigned long slen; /* Length of command line excluding '\0' */
int waserr = 0; /* Set to true if an error occurs */
/*
* Describe the format of the command line.
*/
#ifdef VMS
char *format = "%s -display %s";
#else
char *format = "%s -display %s </dev/null &";
#endif
/*
* Determine the name of the display that the user selected.
*/
display_string = DisplayString(xw->display);
/*
* Locate the server program.
*/
#ifdef VMS
if((exe=find_exe("PGPLOT_DIR", PGXWIN_SERVER))==NULL) {
fprintf(stderr,"%s: Failed to find \"pgplot_dir:%s.exe\".\n",
XW_IDENT, PGXWIN_SERVER);
return None;
};
#else
if((exe=find_exe(getenv("PGPLOT_DIR"), PGXWIN_SERVER))==NULL &&
(exe=find_exe(getenv("PATH"), PGXWIN_SERVER))==NULL) {
fprintf(stderr,
"%s: Couldn't find program \"%s\" in the directory named\n",
XW_IDENT, PGXWIN_SERVER);
fprintf(stderr,
"%s: in your PGPLOT_DIR environment variable, or in any directory\n",
XW_IDENT);
fprintf(stderr,
"%s: listed in your PATH environment variable.\n",XW_IDENT);
return None;
};
#endif
/*
* Make it possible to determine which server is being started.
*/
if(getenv("PGPLOT_XW_DEBUG"))
printf("Starting %s.\n", exe);
/*
* Determine the length of the comand-line string required, as defined by:
* sprintf(command, format, PGXWIN_SERVER, display_string)
*/
slen = strlen(format) + strlen(exe) + strlen(display_string);
command = (char *) malloc(sizeof(char) * (slen + 1));
if(command==NULL) {
fprintf(stderr, "%s: Insufficient memory to run %s.\n", XW_IDENT, exe);
waserr = 1;
} else {
/*
* Compile the command-line string.
*/
sprintf(command, format, exe, display_string);
/*
* Spawn the server.
*/
#ifdef VMS
waserr = vms_spawn_nowait(command);
#else
/*
* Stipulate that the existing socket connection to the server be closed
* on the following exec(). This prevents the child from holding the
* connection open when the parent terminates.
*/
fcntl(ConnectionNumber(xw->display), F_SETFD, 1);
/*
* Run the server.
*/
system(command);
#endif
};
/*
* Release the malloc'd command line string.
*/
if(command)
free(command);
/*
* Check once per second for up to XW_SERVER_TIMEOUT seconds for the
* server to start.
*/
if(!waserr) {
for(i=0; xw->server==None && i<XW_SERVER_TIMEOUT; i++) {
sleep(1);
if(i==3)
printf("%s: Waiting for %s to start (timeout in %d seconds).\n",
XW_IDENT, exe, XW_SERVER_TIMEOUT-i);
xw->server = XGetSelectionOwner(xw->display, xw->server_atom);
};
/*
* Contact with server not acheived?
*/
if(xw->server == None) {
fprintf(stderr, "%s: Timed out waiting for program %s to start\n",
XW_IDENT, exe);
};
};
/*
* Discard the string that contained the name of the server executable.
*/
if(exe)
free(exe);
};
return xw->server;
}
/*.......................................................................
* Send a ClientMessage query to the server and read its reply.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Input/Output:
* event XEvent * The input ClientMessage event.
* On input set:
* event.xclient.message_type = Type of message.
* event.xclient.data.l[0..4] = Message data.
* On output:
* event.xclient.data.l[0..4] = Reply data.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_query_server(XWdev *xw, XEvent *event)
#else
static int xw_query_server(xw, event)
XWdev *xw; XEvent *event;
#endif
{
/*
* Device error?
*/
if(xw->bad_device)
return 1;
/*
* Initialize the generic parts of the event descriptor.
*/
event->type = ClientMessage;
event->xclient.window = xw->client;
event->xclient.format = 32;
if(!XSendEvent(xw->display, xw->server, False, (long)0, event) ||
xw->bad_device) {
fprintf(stderr, "%s: Error talking to PGPLOT /xw server.\n", XW_IDENT);
return 1;
};
XFlush(xw->display);
if(xw->bad_device)
return 1;
/*
* Read the server's reply.
*/
do {
if(xw_next_event(xw, event))
return 1;
} while(event->type != ClientMessage || event->xclient.window != xw->client);
/*
* A returned message type of None denotes an error.
*/
if(event->xclient.message_type == None)
return 1;
return 0;
}
/*.......................................................................
* Front end to XNextEvent() to get the next event from the X server,
* while checking for DestroyNotify events on the PGPLOT window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* Input/Output:
* event XEvent * The event structure for the returned event.
* Output:
* return int 0 - OK.
* 1 - The PGPLOT window has been destroyed.
*/
#ifdef __STDC__
static int xw_next_event(XWdev *xw, XEvent *event)
#else
static int xw_next_event(xw, event)
XWdev *xw; XEvent *event;
#endif
{
/*
* Check that we still have a window.
*/
if(xw->bad_device)
return 1;
/*
* Wait for the next event.
*/
XNextEvent(xw->display, event);
switch(event->type) {
case DestroyNotify:
if(event->xdestroywindow.window == xw->window)
return xw_bad_device(xw);
};
return 0;
}
/*.......................................................................
* like xw_next_event (see above), but with timeout in deciSecs (M.Z.)
*/
#ifdef __STDC__
static int xw_next_event_tmo(XWdev *xw, XEvent *event, int tmo_10)
#else
static int xw_next_event_tmo(xw, event, tmo_10)
XWdev *xw; XEvent *event; int tmo_10;
#endif
{
int tim;
/*
* Check that we still have a window.
*/
if(xw->bad_device)
return 1;
/*
* Wait for the next event.
*/
if (tmo_10 > 0) {
tim=tmo_10;
while (tim>0 && !XPending(xw->display)) {
#ifdef VMS
float f=0.1; lib$wait(&f);
#else
usleep(100000);
#endif
tim--;
}
if (!XPending(xw->display)) {
event->type=0;
return 0;
}
}
return xw_next_event(xw, event);
}
/*.......................................................................
* Front end to XCheckWindowEvent() to check and return for the next event
* that matches the given event_mask, without blocking if no matching event
* is there and while also checking for DestroyNotify events on said window.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* window Window The window on which to watch for events.
* event_mask long The bit mask of event types wanted.
* Input/Output:
* event XEvent * The event structure for the returned event.
* Output:
* return int 0 - No matching event.
* 1 - Got an event that matches the mask.
*/
#ifdef __STDC__
static int xw_check_window_event(XWdev *xw, Window window, long event_mask,
XEvent *event)
#else
static int xw_check_window_event(xw, window, event_mask, event)
XWdev *xw; Window window; long event_mask; XEvent *event;
#endif
{
int want_structure = 0; /* True if the caller selects StructureNotifyMask */
/*
* Check that we still have a window.
*/
if(xw->bad_device)
return 1;
/*
* Did the user also want StructureNotifyMask events?
*/
want_structure = event_mask & StructureNotifyMask;
/*
* We also want DestroyNotify events.
*/
event_mask |= StructureNotifyMask;
/*
* Wait for the next event.
*/
while(XCheckWindowEvent(xw->display, window, event_mask, event)==True) {
switch(event->type) {
case DestroyNotify:
if(window == xw->window) { /* Have we lost the plot window? */
xw_bad_device(xw);
return want_structure;
} else if(want_structure) {
return 1;
};
break;
case CirculateNotify:
case ConfigureNotify:
if(want_structure) /* Ignore unselected StructureNotifyMask events */
return 1;
break;
default:
return 1; /* One of the requested events was found */
};
};
return 0;
}
/*.......................................................................
* Insert a new PGPLOT /xw device descriptor onto the list of open
* devices. The descriptor is inserted such that the list is maintained
* in order of window number xw->number.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor to be inserted.
* Output:
* return XWdev * The same as the input.
*/
#ifdef __STDC__
static XWdev *xw_insert_device(XWdev *xw)
#else
static XWdev *xw_insert_device(xw)
XWdev *xw;
#endif
{
XWdev *prev; /* Pointer to previous device in list */
XWdev *next; /* Pointer to next device in list */
/*
* Find the correct position for the device in the device list.
*/
prev = NULL;
next = device_list;
while(next && next->number > xw->number) {
prev = next;
next = next->next;
};
/*
* Insert the device between 'prev' and 'next'.
*/
xw->next = next;
if(prev==NULL)
device_list = xw;
else
prev->next = xw;
return xw;
}
/*.......................................................................
* Remove a given PGPLOT /xw device descriptor from the list of open
* devices.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor to be removed.
* Output:
* return XWdev * The removed descriptor.
*/
#ifdef __STDC__
static XWdev *xw_remove_device(XWdev *xw)
#else
static XWdev *xw_remove_device(xw)
XWdev *xw;
#endif
{
XWdev *prev; /* Pointer to previous device in list */
XWdev *next; /* Pointer to next device in list */
/*
* Find the position of the device in the device list.
*/
prev = NULL;
next = device_list;
while(next && next!=xw) {
prev = next;
next = next->next;
};
/*
* Relink around the window if it was found.
*/
if(next) {
if(prev==NULL)
device_list = next->next;
else
prev->next = next->next;
};
/*
* The descriptor is no longer in a list.
*/
xw->next = NULL;
return xw;
}
/*.......................................................................
* Select a given device by its PGPLOT window number: xw->number.
*
* Input:
* number int The device number to search for.
* Output:
* return XWdev * The descriptor of the located device, or NULL
* on error.
*/
#ifdef __STDC__
static XWdev *xw_select_device(int number)
#else
static XWdev *xw_select_device(number)
int number;
#endif
{
/*
* Search for the cited device.
*/
XWdev *xw = device_list;
while(xw && xw->number != number)
xw = xw->next;
if(xw==NULL || xw->number!=number) {
fprintf(stderr, "%s: No such device (%d).\n", XW_IDENT, number);
return NULL;
};
return xw;
}
/*.......................................................................
* Wait for data to become available on the xw->client_data property
* and read it into the given buffer, after performing any necessary
* data-type conversions between different sized integers.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* data char * The buffer to return the data in, cast to (char *).
* form int The format for the property. Recognised values and
* the data types used to accept them in data[] are:
* XW_CHAR_PROP - (char)
* XW_SHORT_PROP - (short)
* XW_LONG_PROP - (long)
* n unsigned long The number of items to be returned in data[].
* Output:
* return unsigned long The number of items read, or 0 on error.
*/
#ifdef __STDC__
static unsigned long xw_get_data(XWdev *xw, char *data, int form,
unsigned long n)
#else
static unsigned long xw_get_data(xw, data, form, n)
XWdev *xw; char *data; int form; unsigned long n;
#endif
{
XEvent event; /* Used to check for property-notify events */
unsigned long ndone; /* The number of items read so far */
unsigned long nread; /* The number of items read in the latest iteration */
Atom ret_type; /* Returned data-type */
int ret_form; /* Returned data-format */
unsigned long nret; /* Number of elements returned */
unsigned long nleft; /* Number of bytes unread */
unsigned char *prop; /* Property value */
unsigned long size; /* Size of property data element */
/*
* Device error?
*/
if(xw->bad_device)
return 0;
/*
* The property data returned by XGetWindowProperty is arranged as an array of
* (char) if form=8, (short) if form=16, and (long) if form=32,
* irrespective of the sizes of these types. Get the size of one such
* element in bytes.
*/
switch(form) {
case XW_CHAR_PROP:
size = sizeof(char);
break;
case XW_SHORT_PROP:
size = sizeof(short);
break;
case XW_LONG_PROP:
size = sizeof(long);
break;
default:
fprintf(stderr, "%s: Unkown property format: %d\n", XW_IDENT, form);
xw_bad_device(xw);
return 0;
break;
};
/*
* The property data may not appear in one go, so it may take a
* few iterations to get all the data. The server signals completion
* by sending a 0-length property.
*/
ndone = nread = 0;
do {
/*
* Wait for the property to be updated.
*/
do {
if(xw_next_event(xw, &event))
return 0;
} while(!(event.type == PropertyNotify &&
event.xproperty.window == xw->client &&
event.xproperty.atom == xw->client_data &&
event.xproperty.state == PropertyNewValue));
/*
* Determine the format of the data stored in the property but defer
* reading the data, by asking for 0 items.
*/
if(XGetWindowProperty(xw->display, xw->client, xw->client_data,
(long)0, (long)0, False, AnyPropertyType, &ret_type,
&ret_form, &nret, &nleft, &prop) != Success) {
fprintf(stderr, "%s: Error reading property.\n", XW_IDENT);
xw_bad_device(xw);
return 0;
} else {
/*
* Delete the copied 0-length (+1 byte Xlib added padding) property value.
*/
XFree((char *) prop);
/*
* Make sure that the property has the expected type.
*/
if(ret_form != form) {
fprintf(stderr, "%s: Inconsistent property format.\n", XW_IDENT);
xw_bad_device(xw);
return 0;
};
/*
* Since XGetWindowProperty requires one to specify the amount to be
* read in multiples of 4 8-bit bytes, round-up 'nleft' to the nearest
* 4-byte multiple >= nleft.
*/
nleft = 4 * ((nleft+3)/4);
if(nleft > 0) {
/*
* Read the property value.
*/
if(XGetWindowProperty(xw->display, xw->client, xw->client_data, (long)0,
(long)nleft/4, False, ret_type, &ret_type,
&ret_form, &nread, &nleft, &prop) != Success) {
fprintf(stderr, "%s: Error reading property.\n", XW_IDENT);
xw_bad_device(xw);
return 0;
} else {
/*
* Accumulate up to n items in the output data array.
*/
if(ndone < n) {
unsigned long ncopy = (ndone+nread<=n) ? nread : (n-ndone);
unsigned long icopy;
for(icopy=0; icopy < ncopy*size; icopy++)
data[ndone*size+icopy] = ((char *)prop)[icopy];
ndone += ncopy;
};
/*
* Delete the property data buffer.
*/
XFree((char *)prop);
};
} else {
nread = 0; /* 0-length property detected. */
};
/*
* Delete the property, both to release resources and to signal completion
* to the server.
*/
XDeleteProperty(xw->display, xw->client, xw->client_data);
};
/*
* The server signals that there is no more data to be read by sending
* a zero-length property. Don't stop reading until this has been
* detected, even if all expected data have been read.
*/
} while(nread>0);
/*
* Nothing read?
*/
if(n!=0 && ndone==0) {
fprintf(stderr, "%s: Failed to read property data.\n", XW_IDENT);
del_XWdev(xw,1);
};
return ndone;
}
/*.......................................................................
* After a fatal error has occured, this function should be called to
* mark the specified device as unusable. It emits an error message
* and sets xw->bad_device=1.
*
* Input:
* xw XWdev * The descriptor of the device on which the error
* occurred.
* Output:
* xw->bad_device This flag is set to 1.
* return int Allways 1 (intended as a boolean to say that the
* device is unusable). This can be used as the return
* value for functions that use 1 to denote an error
* return. eg.
* if(error_occurred)
* return xw_bad_device(xw);
*/
#ifdef __STDC__
static int xw_bad_device(XWdev *xw)
#else
static int xw_bad_device(xw)
XWdev *xw;
#endif
{
/*
* Only report an error if this is the first time that this function
* has been called on this device.
*/
if(xw && !xw->bad_device) {
fprintf(stderr, "%s: Lost PGPLOT window %d.\n", XW_IDENT, xw->number);
xw->bad_device = 1;
};
return 1;
}
/*.......................................................................
* If the cursor is within the plot window, warp it to a given position.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* pos XPoint * The location to warp the pointer to if 'warp' is true
* and the cursor is in the window.
* warp int If true, and the cursor is in the window, warp the
* cursor to position 'pos'.
* Output:
* loc XPoint * The located position of the cursor, if it is in
* the window.
* return int 0 - Cursor not in window - 'loc' is unchanged.
* 1 - Cursor is in window - 'loc' records the position.
*/
#ifdef __STDC__
static int xw_locate_cursor(XWdev *xw, XPoint *pos, int warp, XPoint *loc)
#else
static int xw_locate_cursor(xw, pos, warp, loc)
XWdev *xw; XPoint *pos; int warp; XPoint *loc;
#endif
{
XWindowAttributes attr; /* Current window attributes */
Window p_child; /* The child of /xw (None) containing the pointer */
int p_win_x, p_win_y; /* The pointer coordinates in xw->window */
int p_root_x, p_root_y; /* The pointer coordinates in the root window */
Window p_root_win; /* The root window containing the cursor */
unsigned int p_mask; /* Bit mask of button states etc.. */
int inwindow=0; /* True if the cursor is in the window */
/*
* Device error?
*/
if(xw->bad_device)
return 0;
/*
* Query the current state of the window.
*/
XSync(xw->display, False);
if(xw->bad_device)
return 0;
XGetWindowAttributes(xw->display, xw->window, &attr);
if(xw->bad_device)
return 0;
/*
* Determine the current position of the pointer.
*/
XQueryPointer(xw->display, xw->window, &p_root_win, &p_child,
&p_root_x, &p_root_y, &p_win_x, &p_win_y, &p_mask);
if(xw->bad_device)
return 0;
/*
* Is the cursor within the bounds of the window?
*/
inwindow = ((attr.map_state != IsUnmapped) &&
(p_win_x >= 0 && p_win_x < attr.width) &&
(p_win_y >= 0 && p_win_y < attr.height));
if(inwindow) {
/*
* Warp the cursor?
*/
if(warp) {
XWarpPointer(xw->display, None, xw->window, 0, 0, 0, 0, pos->x, pos->y);
if(xw->bad_device)
return 0;
loc->x = pos->x;
loc->y = pos->y;
/*
* Return the current position of the cursor without warping.
*/
} else {
loc->x = p_win_x;
loc->y = p_win_y;
};
};
return inwindow;
}
#ifdef VMS
/*.......................................................................
* Define a given executable as a DCL foreign command. This has to be
* done before the program can be run with command-line arguments.
*
* Input:
* file char * The full file name of the executable.
* command char * The name to give the command that invokes 'file'.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
static int vms_define_command(char *file, char *command)
{
VMS_string value_dsc; /* Foreign command string */
VMS_string symbol_dsc; /* Symbol name for foreign command */
long table = LIB$K_CLI_LOCAL_SYM; /* Table to add symbol to */
char *value = NULL; /* Dynamically allocated symbol value string */
int waserr = 0; /* True after error */
/*
* Compose a VMS symbol value to use to define 'command' as a foreign
* command that takes C-style arguments.
*/
if((value = (char *) malloc(1+strlen(file)+1))==NULL) {
fprintf(stderr, "%s: Insufficient memory to define command for: %s.\n",
XW_IDENT, file);
waserr = 1;
} else {
sprintf(value, "$%s", file);
VMS_STRING(value_dsc, value)
VMS_STRING(symbol_dsc, command)
/*
* Register the symbol value to symbol 'command'.
*/
lib$set_symbol(&symbol_dsc, &value_dsc, &table);
};
/*
* Release resources.
*/
if(value)
free((char *)value);
return waserr != 0;
}
/*.......................................................................
* Run a PGPLOT program on a VAX VMS machine in the background, with
* sys$input redirected from NL: so that the child process does not
* get sent its parent's signals.
* Unfortunately system() can't be used. For some unknown reason
* system("spawn/nowait/sys$input=nl: command_line")
* doesn't appear to do anything. It doesn't even produce an error
* message to say why. If the nowait qualifier is removed it works fine,
* but that is not what is wanted.
*
* Input:
* command char * The command line.
* Output:
* return int 0 - No error detected.
* 1 - Definate error detected.
*/
static int vms_spawn_nowait(char *command)
{
VMS_string comm_dsc; /* VMS string descriptor for 'command'. */
VMS_string sysinp; /* VMS string descriptor of SYS$INPUT string */
long flags = CLI$M_NOKEYPAD | CLI$M_NOWAIT;
/*
* Construct VMS descriptors of C strings.
*/
VMS_STRING(comm_dsc, command)
VMS_STRING(sysinp, "NL:")
if(lib$spawn(&comm_dsc,&sysinp,0,&flags,0,0,0,0,0,0,0) != SS$_NORMAL) {
fprintf(stderr, "%s: Unable to execute command line: %s\n", XW_IDENT,
command);
return 1;
};
return 0;
}
#endif
/*.......................................................................
* Locate an executable by searching for it in a list of directories.
* The list of directories is a string containing directory paths
* separated by ':'s (',' under VMS). If the first or last character
* in the path is one of these terminators, or if two of these terminators
* are adjacent in the path, then the current directory is included in
* the search. Each directory in the path is searched in order and the
* first match is returned.
*
* Note that these semantics are identical to the UNIX Bourne-shell
* treatment of the PATH environment variable.
*
* In order that getenv() can be used directly as an argument to this
* function without invoking error messages when getenv() returns
* NULL, either or both of the 'path' and 'program' arguments can be
* NULL. In this case find_exe() quietly returns NULL as though it had
* searched for the executable and failed to find it.
*
* Input:
* program char * The name of the program to be located.
* In the case of this being NULL, find_exe() will
* quietly abort and return NULL. This allows one
* to use getenv() without worrying about the NULL
* return case.
* path char * A colon-separated (comma-separated under VMS),
* '\0' terminated list of directory paths to search
* for the program in.
* Output:
* return char * The full name of the executable, or NULL if not
* found. The returned pointer is malloc()d
* memory and should be free()d by the caller when
* no longer required.
*/
#ifdef __STDC__
static char *find_exe(char *path, char *program)
#else
static char *find_exe(path, program)
char *path; char *program;
#endif
{
char *dir; /* Pointer to start of directory in 'path' */
char *buf=NULL; /* A buffer used to compile file names in */
int buflen=0; /* The size of the dynamic buffer in char's */
int prog_len; /* Length of program name */
int dirlen; /* Length of directory name pointed to by 'dir' */
int path_len; /* Length of latest path name */
#ifdef VMS
char *exe = ".exe";/* VMS executable extension */
char *sep = ""; /* VMS directory/file separator (overriden below) */
int term = ','; /* Directory separator in path (in addition to '\0') */
#else
char *exe = ""; /* UNIX doesn't add extensions */
char *sep = "/"; /* UNIX directory/file separator */
int term = ':'; /* Directory separator in path (in addition to '\0') */
#endif
/*
* No path or executable?
*/
if(path==NULL || program==NULL)
return NULL;
/*
* Allocate memory for the filename buffer.
*/
buflen = strlen(program) + 40;
buf = (char *) malloc(sizeof(char) * (buflen+1));
if(buf==NULL) {
fprintf(stderr, "%s: Insufficient memory to locate program: %s\n",
XW_IDENT, program);
return buf;
};
/*
* Determine the length of the program name.
*/
prog_len = strlen(program);
/*
* Seek the program in each 'term' separated path name.
*/
do {
/*
* Maintain a pointer to the start of the directory path.
*/
dir = path;
/*
* Find the directory terminator.
*/
while(*path && *path != term)
path++;
/*
* Record the path length.
*/
dirlen = path - dir;
/*
* Skip the trailing terminator unless at the end of the path.
*/
if(*path)
path++;
/*
* Under VMS a separator is not required if the directory is given
* explicitly rather than with logical variables.
*/
#ifdef VMS
sep = dirlen>0 && dir[dirlen-1]==']' ? "" : ":";
#endif
/*
* Combine the directory and command file name into a full program
* name.
*/
path_len = dirlen + strlen(sep) + prog_len + strlen(exe) ;
if(path_len > buflen) {
char *new_buf = realloc(buf, (path_len+1) * sizeof(char));
if(new_buf==NULL) {
fprintf(stderr, "%s: Insufficient memory to locate program: %s\n",
XW_IDENT, program);
free(buf);
return buf;
};
buf = new_buf;
};
sprintf(buf, "%.*s%s%s%s", dirlen, dir, dirlen==0 ? "":sep, program, exe);
/*
* See if the executable file exists.
*/
#ifndef X_OK
#define X_OK 1
#endif
#ifndef R_OK
#define R_OK 4
#endif
#ifdef VMS
if(access(buf, X_OK)==0 || access(buf, R_OK)==0) {
if(vms_define_command(buf, program)) /* Define a foreign VMS command */
break;
strcpy(buf, program);
return buf;
};
#else
if(access(buf, X_OK)==0)
return buf;
#endif
} while(*path);
/*
* Executable file not found.
*/
free(buf);
return NULL;
}
/*.......................................................................
* Return a dynamically allocated visual info structure for a given
* visual. This is simply a more convenient interface to XGetVisualInfo()
* and XVisualIDFromVisual().
*
* Input:
* display Display * The display connection to which the visual
* belongs.
* screen int The screen to which the visual belongs.
* visual Visual * The visual for which information is required.
* Output:
* return XVisualInfo * The required information descriptor, or NULL
* on error.
*/
#ifdef __STDC__
static XVisualInfo *xw_visual_info(Display *display, int screen, Visual *visual)
#else
static XVisualInfo *xw_visual_info(display, screen, visual)
Display *display; int screen; Visual *visual;
#endif
{
XVisualInfo *vi=NULL; /* The return descriptor */
XVisualInfo template; /* The search template */
int nret = 0; /* The number of descriptors returned */
/*
* Using the visual ID and the screen should unambiguously select the
* information for the specified visual.
*/
template.visualid = XVisualIDFromVisual(visual);
template.screen = screen;
vi = XGetVisualInfo(display, (long)(VisualIDMask | VisualScreenMask),
&template, &nret);
if(vi == NULL || nret < 1) {
fprintf(stderr,
"%s: Error getting visual information for visual ID 0x%lx, screen %d.\n",
XW_IDENT, (unsigned long)template.visualid, screen);
vi = NULL;
};
return vi;
}
/*.......................................................................
* Allocate the contents of xw->image. This contains buffers used to
* construct and dispatch line-of-pixel images to the display.
*
* Note that xw_get_visual() must have been called before this function.
*
* Input:
* xw XWdev * The PGPLOT /xw device descriptor.
* npix int The length of the buffer in pixels.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
#ifdef __STDC__
static int xw_get_image(XWdev *xw, int npix)
#else
static int xw_get_image(xw, npix)
XWdev *xw; int npix;
#endif
{
/*
* Create the X image that we use to compose lines of pixels with given
* colors.
*/
xw->image.xi = XCreateImage(xw->display, xw->color.vi->visual,
(unsigned)xw->color.vi->depth, ZPixmap, 0,
NULL, (unsigned)npix, 1, 32, 0);
if(xw->image.xi==NULL) {
fprintf(stderr, "%s: Failed to allocate XImage container.\n", XW_IDENT);
return 1;
};
/*
* Allocate the image buffer.
*/
xw->image.xi->data = malloc((size_t) xw->image.xi->bytes_per_line);
if(!xw->image.xi->data) {
fprintf(stderr, "%s: Failed to allocate image buffer.\n", XW_IDENT);
return 1;
};
return 0;
}
/*.......................................................................
* Limit pixmap coordinates to lie within the pixmap area.
*
* Input:
* xw XWdev * The PGPLOT window context.
* Input/Output:
* coord XPoint * The coordinates to be modified.
*/
#ifdef __STDC__
static void xw_limit_pcoords(XWdev *xw, XPoint *coord)
#else
static void xw_limit_pcoords(xw, coord)
XWdev *xw; XPoint *coord;
#endif
{
if(xw->pixmap != None) {
if(coord->x >= xw->geom.width)
coord->x = xw->geom.width - 1;
if(coord->y >= xw->geom.height)
coord->y = xw->geom.height - 1;
if(coord->x < 0)
coord->x = 0;
if(coord->y < 0)
coord->y = 0;
};
return;
}
/*.......................................................................
* Return the nearest integer to a given floating point number.
*
* Input:
* f float The floating point number to be rounded.
* Output:
* return int The nearest integer to f.
*/
#ifdef __STDC__
static int xw_nint(float f)
#else
static int xw_nint(f)
float f;
#endif
{
return (int) (f >= 0.0 ? (f + 0.5) : (f - 0.5));
}
/*.......................................................................
* Scroll a rectanglular area vertically and/or horizontally.
*
* Input:
* xw XWdev * The PGPLOT window context.
* rbuf float * The array of float arguments sent by the PGPLOT
* GREXEC() subroutine.
*/
#ifdef __STDC__
static void xw_scroll_rect(XWdev *xw, float *rbuf)
#else
static void xw_scroll_rect(xw, rbuf)
XWdev *xw; float *rbuf;
#endif
{
if(!xw->bad_device && xw->pixmap != None) {
XPoint blc, trc; /* The bottom left and top right rectangle corners */
XPoint blc_orig, trc_orig; /* The vertices of the rectangle to be copied */
XPoint blc_dest, trc_dest; /* The vertices of the destination of the copy */
int dx, dy; /* The amounts to scroll right and down */
unsigned long fg; /* The foreground color to be reinstated */
/*
* Convert the rectangle vertices from PGPLOT coordinates to X coordinates.
*/
xw_xy_to_XPoint(xw, &rbuf[0], &blc);
xw_xy_to_XPoint(xw, &rbuf[2], &trc);
/*
* Get the scroll offsets in X coordinates.
*/
dx = xw_nint(rbuf[4]);
dy = xw_nint(-rbuf[5]);
/*
* Selected parts of the pixmap will need to be erased by drawing an
* opaque rectangle over them in the background color. Set the foreground
* color to equal the background. Keep a record of the previous foreground
* color, so that it can be re-instated.
*/
fg = xw->gcv.foreground;
XSetForeground(xw->display, xw->gc, xw->color.pixel[0]);
/*
* If either scroll extent exceeds the length of the associated
* axis, then fill the area with the background color.
*/
if(abs(dx) > trc.x - blc.x || abs(dy) > blc.y - trc.y) {
XFillRectangle(xw->display, xw->pixmap, xw->gc, blc.x, trc.y,
(unsigned)(trc.x-blc.x+1), (unsigned)(blc.y-trc.y+1));
/*
* Scroll within the rectangle by copying the area that is to be preserved
* to a new location shifted appropriately in X and/or Y. Then clear the
* vacated areas.
*/
} else {
/*
* Calculate the vertices of the source and destination rectangles to
* be copied.
*/
blc_orig = blc_dest = blc;
trc_orig = trc_dest = trc;
if(dx > 0) {
trc_orig.x = trc.x - dx;
blc_dest.x = blc.x + dx;
} else if(dx < 0) {
blc_orig.x = blc.x - dx;
trc_dest.x = trc.x + dx;
};
if(dy > 0) {
blc_orig.y = blc.y - dy;
trc_dest.y = trc.y + dy;
} else if(dy < 0) {
trc_orig.y = trc.y - dy;
blc_dest.y = blc.y + dy;
};
/*
* Constrain the coordinates to lie within the pixmap.
*/
xw_limit_pcoords(xw, &blc_orig);
xw_limit_pcoords(xw, &blc_dest);
xw_limit_pcoords(xw, &trc_orig);
xw_limit_pcoords(xw, &trc_dest);
/*
* Scroll the rectangle to its shifted location.
*/
XCopyArea(xw->display, xw->pixmap, xw->pixmap, xw->gc,
blc_orig.x, trc_orig.y,
trc_orig.x - blc_orig.x + 1,
blc_orig.y - trc_orig.y + 1,
blc_dest.x, trc_dest.y);
/*
* Clear the vacated area to the left or right of the copied area.
*/
if(dx > 0) {
XFillRectangle(xw->display, xw->pixmap, xw->gc,
blc.x, trc.y,
(unsigned) dx,
(unsigned) (blc.y - trc.y + 1));
} else if(dx < 0) {
XFillRectangle(xw->display, xw->pixmap, xw->gc,
trc_dest.x, trc.y,
(unsigned) (-dx),
(unsigned) (blc.y - trc.y + 1));
};
/*
* Clear the vacated area above or below the copied area.
*/
if(dy > 0) {
XFillRectangle(xw->display, xw->pixmap, xw->gc,
blc.x, trc.y,
(unsigned) (trc.x - blc.x + 1),
(unsigned) dy);
} else if(dy < 0) {
XFillRectangle(xw->display, xw->pixmap, xw->gc,
blc.x, blc_dest.y,
(unsigned) (trc.x - blc.x + 1),
(unsigned) (-dy));
};
};
/*
* Record the extent of the modified part of the pixmap.
*/
xw_mark_modified(xw, blc.x, blc.y, 1);
xw_mark_modified(xw, trc.x, trc.y, 1);
/*
* Re-instate the original foreground color.
*/
XSetForeground(xw->display, xw->gc, fg);
};
return;
}