summaryrefslogtreecommitdiffstats
path: root/tcliis/iis.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcliis/iis.c')
-rw-r--r--tcliis/iis.c1494
1 files changed, 1494 insertions, 0 deletions
diff --git a/tcliis/iis.c b/tcliis/iis.c
new file mode 100644
index 0000000..18fa8f8
--- /dev/null
+++ b/tcliis/iis.c
@@ -0,0 +1,1494 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#include <fcntl.h>
+
+#include "iis.h"
+#include "xim.h"
+
+/* IIS data pixel values. */
+#define CMS_DATASTART 1
+#define CMS_DATAEND 200
+#define CMS_DATARANGE 200
+
+/* IIS color assignments. */
+#define CMS_CURSOR 201
+#define CMS_BACKGROUND 202
+#define CMS_FOREGROUND 203
+#define CMS_RED 204
+#define CMS_GREEN 205
+#define CMS_BLUE 206
+#define CMS_YELLOW 207
+#define CMS_CYAN 208
+#define CMS_MAGENTA 209
+
+#define XtInputId void *
+#define XtNumber(x) MAX_CLIENTS
+
+/*
+ * IIS.C -- IRAF/IIS "imtool" protocol module. This code is responsible for
+ * accepting connections from remote network clients and communicating with
+ * them via the imtool/iis image display server communications prototcol.
+ *
+ * fd = xim_iisopen (xim)
+ * xim_iisclose (xim)
+ * xim_iisio (xim, &fd, &id)
+ *
+ * xim_frameLabel (xim)
+ * xim_encodewcs (xim, sx, sy, sz, obuf)
+ * xim_retCursorVal (xim, sx, sy, frame, wcs, key, strval)
+ *
+ * xim_iisiomap (w, iomap, &iomap_len)
+ * xim_iiscolormap (w, r, g, b, &first, &ngray, &rgb_len)
+ *
+ * xim_iisio is a callback procedure called by Xt when there is input to be
+ * processed on the stream used to communicate with the remote client.
+ */
+
+#define MEMORY 01 /* frame buffer i/o */
+#define LUT 02 /* lut i/o */
+#define FEEDBACK 05 /* used for frame clears */
+#define IMCURSOR 020 /* logical image cursor */
+#define WCS 021 /* used to set WCS */
+#define SZ_IOBUF 65536 /* max size data transfer */
+#define IO_TIMEOUT 30 /* i/o not getting anywhere */
+#define MAXCONN 5
+
+
+#define IIS_VERSION 10 /* version 10 -> 1.0 */
+
+#define SZ_IMCURVAL 160
+#define PACKED 0040000
+#define COMMAND 0100000
+#define IIS_READ 0100000
+#define IMC_SAMPLE 0040000
+#define IMT_FBCONFIG 077
+#define XYMASK 077777
+
+struct iism70 {
+ short tid;
+ short thingct;
+ short subunit;
+ short checksum;
+ short x, y, z;
+ short t;
+};
+
+/* Running id for frame mappings. We keep a separate id for each of the
+ * currently allowed MAX_FRAMES since the object id is used in the WCS
+ * code for the image along with the (frame_num * 100).
+ */
+int objid[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
+static int *wcspix_enabled = NULL;
+
+static int iis_debug = -1; /* protocol debug */
+
+static void set_fbconfig(), add_mapping();
+static void xim_connectClient(), xim_disconnectClient();
+static int chan_read(), chan_write(), decode_frameno();
+
+static CtranPtr wcs_update();
+static IoChanPtr open_fifo(), open_inet();
+#ifdef HAVE_SYS_UN_H
+static IoChanPtr open_unix();
+#endif
+static IoChanPtr get_iochan();
+static MappingPtr xim_getMapping(XimDataPtr, float, float, int);
+static void print_mappings(FrameBufPtr fr);
+extern int errno;
+
+
+/* XIM_IISOPEN -- Initialize the IIS protocol module and ready the module to
+ * accept client connections and begin processing client requests. Clients
+ * may connect to the server using a fifo connection or an internet or
+ * UNIX domain socket connection. All three types of server ports are
+ * simultaneously ready to receive client connections.
+ */
+int xim_iisOpen(XimDataPtr xim)
+{
+ int nopen = 0;
+
+ if (open_fifo (xim))
+ nopen++;
+ if (open_inet (xim))
+ nopen++;
+#ifdef HAVE_SYS_UN_H
+ if (open_unix (xim))
+ nopen++;
+#endif
+ return (nopen);
+}
+
+
+/* XIM_IISCLOSE -- Close down the IIS protocol module.
+ */
+void xim_iisClose(XimDataPtr xim)
+{
+ IoChanPtr chan;
+ int i;
+
+ for (i=0, chan=NULL; i < XtNumber(xim->chan); i++) {
+ chan = &xim->chan[i];
+ if (chan->id) {
+ xim_removeInput (xim, (int)chan->id);
+ chan->id = 0;
+ }
+
+ switch (chan->type) {
+ case IO_FIFO:
+ if (chan->keepalive >= 0)
+ close (chan->keepalive);
+ if (chan->datain >= 0)
+ close (chan->datain);
+ if (chan->dataout >= 0)
+ close (chan->dataout);
+ chan->type = 0;
+ break;
+
+ case IO_INET:
+ close (chan->datain);
+ chan->type = 0;
+ break;
+
+#ifdef HAVE_SYS_UN_H
+ case IO_UNIX:
+ close (chan->datain);
+ unlink (chan->path);
+ chan->type = 0;
+ break;
+#endif
+ }
+ }
+}
+
+
+/* OPEN_FIFO -- Ooen the (x)imtool fifo port and make ready to accept client
+ * connections and begin processing client requests. There is no client
+ * yet at this stage.
+ */
+static IoChanPtr open_fifo(XimDataPtr xim)
+{
+ IoChanPtr chan;
+ int datain, dataout;
+ int keepalive;
+
+#ifdef __DARWIN__
+ /* On OS X we don't use fifos. */
+ xim->input_fifo = "none";
+ return (NULL);
+#endif
+
+ /* Setting the input fifo to "none" or the null string disables
+ * fifo support.
+ */
+ if (!xim->input_fifo[0] || strcmp(xim->input_fifo,"none")==0)
+ return (NULL);
+
+ datain = dataout = -1;
+
+ /* Open the output fifo (which is the client's input fifo). We have
+ * to open it ourselves first as a client to get around the fifo
+ * open-no-client error.
+ */
+ if ((datain = open (xim->input_fifo, O_RDONLY|O_NDELAY)) != -1) {
+ if ((dataout = open (xim->input_fifo, O_WRONLY|O_NDELAY)) != -1)
+ fcntl (dataout, F_SETFL, O_WRONLY);
+ else
+ goto done;
+ close (datain);
+ } else
+ goto done;
+
+ /* Open the input stream, a FIFO pseudodevice file used by
+ * applications to send us commands and data.
+ */
+ if ((datain = open (xim->output_fifo, O_RDONLY|O_NDELAY)) == -1)
+ goto done;
+ else {
+ /* Clear O_NDELAY for reading. */
+ fcntl (datain, F_SETFL, O_RDONLY);
+
+ /* Open the client's output fifo as a pseudo-client to make it
+ * appear that a client is connected.
+ */
+ keepalive = open (xim->output_fifo, O_WRONLY);
+ }
+ done:
+ /* Allocate and fill in i/o channel descriptor. */
+ if (datain > 0 && dataout > 0 && ((chan = get_iochan(xim)))) {
+ chan->xim = (XtPointer) xim;
+ chan->type = IO_FIFO;
+ chan->datain = datain;
+ chan->dataout = dataout;
+ chan->keepalive = keepalive;
+ chan->reference_frame = 1;
+ chan->version = 0;
+ chan->rf_p = &xim->frames[0];
+ } else {
+ /*
+ fprintf (stderr, "Warning: cannot open %s\n", xim->output_fifo);
+ */
+ strcpy (xim->input_fifo, "none");
+ chan = NULL;
+ }
+
+ /* Register input callback. */
+ if (chan) {
+ chan->id = xim_addInput(xim, chan->datain, xim_iisio, chan);
+ } else {
+ if (datain > 0)
+ close (datain);
+ if (dataout > 0)
+ close (dataout);
+ }
+
+ return (chan);
+}
+
+
+/* OPEN_INET -- Set up a port to be used for incoming client connections
+ * using internet domain sockets.
+ */
+static IoChanPtr open_inet(XimDataPtr xim)
+{
+ int s = 0;
+ IoChanPtr chan;
+ struct sockaddr_in sockaddr;
+ int reuse = 1;
+
+ /* Setting the port to zero disables inet socket support. */
+ if (xim->port <= 0)
+ return (NULL);
+
+ if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ goto err;
+
+ memset ((void *)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons((short)xim->port);
+ sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,
+ sizeof(reuse)) < 0)
+ goto err;
+
+ if (bind (s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
+ goto err;
+
+ if (listen (s, MAXCONN) < 0)
+ goto err;
+
+ /* make sure we close on exec */
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+
+ /* Allocate and fill in i/o channel descriptor. */
+ if ((chan = get_iochan(xim))) {
+ chan->xim = (XtPointer) xim;
+ chan->type = IO_INET;
+ chan->datain = s;
+ chan->dataout = s;
+ chan->reference_frame = 1;
+ chan->version = 0;
+ chan->rf_p = &xim->frames[0];
+
+ /* Register connectClient callback. */
+ chan->id = xim_addInput(xim, s, xim_connectClient, chan);
+ return (chan);
+ }
+ err:
+ /*
+ if (errno == EADDRINUSE) {
+ fprintf (stderr,"ximtool: inet port %d already in use - disabled\n",
+ xim->port);
+ } else {
+ fprintf (stderr, "ximtool: can't open inet socket %d, errno=%d\n",
+ xim->port, errno);
+ }
+ */
+ xim->port = 0;
+ if (s)
+ close (s);
+ return (NULL);
+}
+
+
+/* OPEN_UNIX -- Set up a port to be used for incoming client connections
+ * using unix domain sockets.
+ */
+#ifdef HAVE_SYS_UN_H
+static IoChanPtr open_unix(XimDataPtr xim)
+{
+ int s = 0;
+ IoChanPtr chan;
+ struct sockaddr_un sockaddr;
+ int addrlen;
+ char path[256];
+
+ /* Setting the addr to "none" or the null string disables unix
+ * socket support.
+ */
+ if (!xim->unixaddr[0] || strcmp(xim->unixaddr,"none")==0)
+ return (NULL);
+
+ /* Get path to be used for the unix domain socket. */
+ sprintf (path, xim->unixaddr, getuid());
+ unlink (path);
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
+ goto err;
+
+ memset ((void *)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ strcpy (sockaddr.sun_path, path);
+ addrlen = sizeof(sockaddr) - sizeof(sockaddr.sun_path) + strlen(path);
+ if (bind (s, (struct sockaddr *)&sockaddr, addrlen) < 0)
+ goto err;
+
+ if (listen (s, MAXCONN) < 0)
+ goto err;
+
+ /* make sure we close on exec */
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+
+ /* Allocate and fill in i/o channel descriptor. */
+ if ((chan = get_iochan(xim))) {
+ chan->xim = (XtPointer) xim;
+ chan->type = IO_UNIX;
+ chan->datain = s;
+ chan->dataout = s;
+ chan->reference_frame = 1;
+ chan->version = 0;
+ chan->rf_p = &xim->frames[0];
+ strncpy (chan->path, path, SZ_FNAME);
+
+ /* Register connectClient callback. */
+ chan->id = xim_addInput (xim,s,xim_connectClient,(XtPointer)chan);
+ return (chan);
+ }
+ err:
+ /*
+ if (errno == EADDRINUSE) {
+ fprintf (stderr,"ximtool: unix addr %s already in use - disabled\n",
+ path);
+ } else {
+ fprintf (stderr,"ximtool: cannot open unix socket '%s', errno=%d\n",
+ path, errno);
+ }
+ */
+
+ strcpy (xim->unixaddr, "none");
+ if (s)
+ close (s);
+ return (NULL);
+}
+#endif
+
+/* XIM_CONNECTCLIENT -- Called when a client has attempted a connection on
+ * a socket port. Accept the connection and set up a new i/o channel to
+ * communicate with the new client.
+ */
+static void xim_connectClient(IoChanPtr chan_port, int *source, XtPointer id)
+{
+ XimDataPtr xim = (XimDataPtr) chan_port->xim;
+ IoChanPtr chan;
+ int s;
+
+ /* Accept connection. */
+ if ((s = accept ((int)*source, (struct sockaddr *)0, (socklen_t *)0)) < 0)
+ return;
+ /* if (fcntl (s, F_SETFL, O_RDWR|O_NDELAY) < 0) {
+ close (s);
+ return;
+ } */
+
+ /* Allocate and fill in i/o channel descriptor. */
+ if ((chan = get_iochan(xim))) {
+ chan->xim = (XtPointer) xim;
+ chan->type = chan_port->type;
+ chan->datain = s;
+ chan->dataout = s;
+ chan->reference_frame = 1;
+ chan->version = 0;
+ chan->rf_p = &xim->frames[0];
+ chan->id = xim_addInput(xim, s, xim_iisio, chan);
+ }
+}
+
+
+/* XIM_DISCONNECTCLIENT -- Called to close a client connection when EOF is
+ * seen on the input port. Close the connection and free the channel
+ * descriptor.
+ */
+static void xim_disconnectClient(IoChanPtr chan)
+{
+ switch (chan->type) {
+ case IO_INET:
+ case IO_UNIX:
+ close (chan->datain);
+ if (chan->id) {
+ xim_removeInput(chan->xim, chan->id);
+ chan->id = 0;
+ }
+ chan->type = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* GET_IOCHAN --- Get an i/o channel descriptor.
+ */
+static IoChanPtr get_iochan(XimDataPtr xim)
+{
+ int i;
+
+ for (i=0; i < XtNumber(xim->chan); i++)
+ if (!xim->chan[i].type)
+ return (&xim->chan[i]);
+
+ return (NULL);
+}
+
+
+/* XIM_IISIO -- Xt file i/o callback procedure, called when there is input
+ * pending on the data stream to the ximtool client.
+ */
+void xim_iisio (IoChanPtr chan, int *fd_addr, XtInputId *id_addr)
+{
+ XimDataPtr xim = (XimDataPtr) chan->xim;
+ MappingPtr mp = (MappingPtr) NULL;
+ FrameBufPtr fb;
+ int sum, i;
+ short *p;
+ int datain = *fd_addr;
+ int dataout = chan->dataout;
+ int ndatabytes, nbytes, n, newframe, ntrys=0;
+ struct iism70 iis;
+ char buf[SZ_FIFOBUF];
+ static int errmsg=0, bswap=0;
+
+
+ /* Initialize the debug output. */
+ if (iis_debug == -1)
+ iis_debug = (getenv("DEBUG_IIS") != (char *)NULL);
+
+ /* Get the IIS header. */
+ if ((n = chan_read (datain, (char *)&iis, sizeof(iis))) < sizeof(iis)) {
+ if (n != 0)
+ fprintf (stderr,
+ "ximtool: command input read error, n=%d of %d, errno=%d\n",
+ n, (int)sizeof(iis), errno);
+ if (n <= 0)
+ xim_disconnectClient (chan);
+ return;
+ } else if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+
+ /* Verify the checksum. If it fails swap the bytes and try again.
+ */
+ for (;;) {
+ for (i=0, sum=0, p=(short *)&iis; i < 8; i++)
+ sum += *p++;
+ if ((sum & 0177777) == 0177777)
+ break;
+
+ if (ntrys++) {
+ if (!errmsg++) {
+ fprintf (stderr, "ximtool: bad data header checksum\n");
+ if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, "noswap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, " swap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+ }
+ break;
+
+ } else {
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ bswap = !bswap;
+ }
+ }
+
+ ndatabytes = -iis.thingct;
+ if (!(iis.tid & PACKED))
+ ndatabytes *= 2;
+
+ if (iis_debug) {
+ fprintf (stderr,
+ "subunit=%06o tid=%06o nbytes=%7d x=%06o y=%06o z=%06o t=%06o\n",
+ iis.subunit & 077,
+ iis.tid,
+ ndatabytes,
+ iis.x & 0177777,
+ iis.y & 0177777,
+ iis.z & 0177777,
+ iis.t & 0177777);
+ fflush (stderr);
+ }
+
+
+ switch (iis.subunit & 077) {
+ case FEEDBACK:
+ /* The feedback unit is used only to clear a frame. The
+ * xim_eraseFrame() procedure takes care of uncaching the
+ * mappings associated with this frame.
+ */
+ newframe = decode_frameno (iis.z & 0177777);
+ xim_setReferenceFrame (chan, newframe);
+ if (newframe == chan->reference_frame)
+ xim_eraseFrame (xim, chan->reference_frame);
+
+ /* ISM: Uncache all mappings associated with this frame. */
+ fb = &xim->frames[newframe-1];
+ for (i=0; i < fb->nmaps; i++) {
+ mp = &fb->mapping[i];
+ if (mp->id) {
+ sprintf (buf, "uncache %d", mp->id);
+ ism_message (xim, "wcspix", buf);
+ wcspix_message (xim, buf);
+ mp->id = 0;
+ }
+ }
+
+ /* Reset various counters for the new frame and release the
+ * mappings.
+ */
+ fb->nmaps = 0;
+ fb->ctran.valid = 0;
+ objid[newframe-1] = 0;
+ fb->nmaps = 0;
+
+ if (iis_debug)
+ fprintf (stderr, "erase frame %d - ref = %d\n",
+ newframe, chan->reference_frame);
+ break;
+
+ case LUT:
+ /* Data mode writes to the frame lookup tables are not implemented.
+ * A command mode write to the LUT subunit is used to connect
+ * image memories up to the RGB channels, i.e., to select the frame
+ * to be displayed. We ignore any attempt to assign multiple
+ * frames to multiple color channels, and just do a simple frame
+ * select.
+ */
+ if (iis.subunit & COMMAND) {
+ int frame, z, n;
+ short x[17];
+
+ if (chan_read (datain, (char *)x, ndatabytes) == ndatabytes) {
+ if (bswap)
+ bswap2 ((char *)x, (char *)x, ndatabytes);
+
+ z = x[0];
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ frame = max (1, n + 1);
+ if (frame > xim->nframes) {
+ if (frame <= MAX_FRAMES) {
+ set_fbconfig (chan, xim->fb_configno, frame);
+ if (iis_debug)
+ fprintf (stderr, "set_fbconfig (%d, %d)\n",
+ xim->fb_configno, frame);
+ } else {
+ fprintf (stderr, "ximtool warning: ");
+ fprintf (stderr,
+ "attempt to display nonexistent frame %d\n",
+ frame);
+ return;
+ }
+ }
+
+ xim_setDisplayFrame (xim, frame);
+ if (iis_debug)
+ fprintf (stderr, "set_frame (%d)\n", frame);
+ return;
+ }
+ }
+
+ case MEMORY:
+ /* Load data into the frame buffer. Data is assumed to be byte
+ * packed.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read from the display.
+ */
+ unsigned char *ip, iobuf[SZ_IOBUF];
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to read from. */
+ xim_setReferenceFrame (chan, decode_frameno (iis.z & 0177777));
+
+ nbytes = ndatabytes;
+ x = iis.x & XYMASK;
+ y = iis.y & XYMASK;
+
+ if (x < 0 || x >= xim->width || y < 0 || y >= xim->height) {
+ fprintf (stderr,
+ "ximtool: attempted read out of bounds on framebuf\n");
+ fprintf (stderr,
+ "read %d bytes at [%d,%d]\n", nbytes, x, y);
+ memset ((void *)iobuf, 0, nbytes);
+ } else {
+ GtReadPixels (xim->gt, chan->rf_p->raster, iobuf, 8, x, y,
+ min(xim->width-x,nbytes), max(1,nbytes/xim->width));
+ if (iis_debug)
+ fprintf (stderr,
+ "read %d bytes at [%d,%d]\n", nbytes, x, y);
+ }
+
+ /* Return the data from the frame buffer. */
+ starttime = time(0);
+ for (nleft=nbytes, ip=iobuf; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = chan_write (dataout, ip, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT)) {
+ fprintf (stderr, "XIMTOOL: timeout on write\n");
+ break;
+ }
+ } else
+ ip += n;
+ }
+
+ return;
+
+ } else {
+ /* Write to the display.
+ */
+ unsigned char *op, iobuf[SZ_IOBUF];
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to be written into (encoded with a bit for
+ * each frame, 01 is frame 1, 02 is frame 2, 04 is frame 3,
+ * and so on).
+ */
+ xim_setReferenceFrame (chan, decode_frameno (iis.z & 0177777));
+
+ nbytes = ndatabytes;
+ x = iis.x & XYMASK;
+ y = iis.y & XYMASK;
+
+ /* Read the data into the frame buffer.
+ */
+ starttime = time(0);
+ for (nleft=nbytes, op=iobuf; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = chan_read (datain, op, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT)) {
+ fprintf (stderr, "XIMTOOL: timeout on read\n");
+ break;
+ }
+ } else
+ op += n;
+ }
+
+ if (x < 0 || x >= xim->width || y < 0 || y >= xim->height) {
+ fprintf (stderr,
+ "ximtool: attempted write out of bounds on framebuf\n");
+ fprintf (stderr,
+ "write %d bytes at [%d,%d]\n", nbytes, x, y);
+ memset ((void *)iobuf, 0, nbytes);
+ } else {
+ GtWritePixels (xim->gt, chan->rf_p->raster, iobuf, 8, x, y,
+ min(xim->width-x,nbytes), max(1,nbytes/xim->width));
+ if (iis_debug)
+ fprintf (stderr, "write %d bytes at x=%d, y=%d\n",
+ nbytes, x, y);
+ }
+
+ return;
+ }
+ break;
+
+ case WCS:
+ /* Read or write the WCS for a frame. The frame number to
+ * which the WCS applies is passed in Z and the frame buffer
+ * configuration in T. The client changes the frame buffer
+ * configuration in a WCS set. The WCS text follows the header
+ * as byte packed ASCII data.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Return the WCS for the referenced frame.
+ */
+ char emsg[SZ_WCSBUF];
+ char *text;
+ int frame;
+
+ memset ((char *)emsg, 0, SZ_WCSBUF);
+
+ if ((iis.x & 017777) && (iis.y & 017777)) {
+ /* This is a check by the client on our capabilities.
+ * Return with a version number which can be used by the
+ * client. However we write back using the old WCS
+ * buffer size for compatability.
+ */
+ sprintf (text=emsg, "version=%d", IIS_VERSION);
+ chan->version = IIS_VERSION;
+
+ chan_write (dataout, text, SZ_OLD_WCSBUF);
+ if (iis_debug)
+ fprintf (stderr, "version query wcs: %s\n",text);
+
+ } else if ((iis.x & 017777) && (iis.t & 017777)) {
+ /* Return the buffer for a specified WCS number.
+ */
+ CtranPtr ct = (CtranPtr) NULL;
+ FrameBufPtr fr = (FrameBufPtr) NULL;
+ int wcsnum = (iis.t & 017777);
+ int i, j;
+
+
+ /* Decode the requested wcs number. */
+ frame = decode_frameno (iis.z & 0177777);
+
+ /* Search for the requested WCS number. */
+ mp = (MappingPtr) NULL;
+ for (j=0; j < xim->nframes; j++) {
+ fr = &xim->frames[j];
+ if (fr->frameno != frame)
+ continue;
+ for (i=0; i < fr->nmaps; i++) {
+ mp = &fr->mapping[i];
+ if (mp->id == wcsnum) {
+ /* found the mapping */
+ ct = &(mp->ctran);
+ goto map_found;
+ }
+ }
+ }
+
+ /* Encode the WCS and mapping information. */
+ map_found: if (ct) {
+ char wcs[SZ_WCSBUF], mapping[SZ_WCSBUF];
+
+ sprintf (wcs, "%s\n%f %f %f %f %f %f %f %f %d\n",
+ ct->imtitle, ct->a, ct->b, ct->c, ct->d,
+ ct->tx, ct->ty, ct->z1, ct->z2, ct->zt);
+ sprintf (mapping, "%s %f %f %d %d %d %d %d %d\n%s\n",
+ mp->region, mp->sx, mp->sy, mp->snx, mp->sny,
+ mp->dx, mp->dy, mp->dnx, mp->dny, mp->ref);
+
+ strcpy (text=emsg, wcs);
+ strcat (text, mapping);
+ } else
+ strcpy (text=emsg, "[NOSUCHWCS]\n");
+
+ chan_write (dataout, text, SZ_WCSBUF);
+
+ if (iis_debug) {
+ fprintf (stderr, "query specified wcs=%d frame=%d\n",
+ wcsnum, frame);
+ write (2, text, strlen (text));
+ }
+
+ } else {
+ frame = decode_frameno (iis.z & 0177777);
+ xim_setReferenceFrame (chan, frame);
+
+ /*** waj ***/
+ /* We always want to use the wcs buffer. If frameno == 0 ds9 will
+ fill in the buffer.
+ if (chan->rf_p->frameno <= 0)
+ strcpy (text=emsg, "[NOSUCHFRAME]\n");
+ else
+ */
+ /*** waj ***/
+ text = chan->rf_p->wcsbuf;
+
+ if ((iis.x & 0777))
+ chan_write (dataout, text, SZ_WCSBUF);
+ else
+ chan_write (dataout, text, SZ_OLD_WCSBUF);
+
+ if (iis_debug) {
+ fprintf (stderr, "query wcs: frame = %d\n", frame);
+ write (2, text, strlen(text));
+ }
+ }
+
+ } else {
+ /* Set the WCS for the referenced frame.
+ */
+ CtranPtr ct;
+ int fb_config, frame, new_wcs = 0;
+
+ frame = decode_frameno (iis.z & 0177777);
+ fb_config = (iis.t & 0777) + 1;
+ new_wcs = (iis.x & 0777);
+
+ /* See if we need to change the frame buffer configuration,
+ * or allocate a new frame.
+ */
+ if (fb_config != xim->fb_configno)
+ set_fbconfig (chan, fb_config, frame);
+ else if (frame > xim->nframes && frame <= MAX_FRAMES)
+ set_fbconfig (chan, xim->fb_configno, frame);
+
+ /* Read in and set up the WCS. */
+ xim_setReferenceFrame (chan, frame);
+ memset ((char *)buf, 0, SZ_WCSBUF);
+ if (chan_read (datain, buf, ndatabytes) == ndatabytes)
+ strncpy (chan->rf_p->wcsbuf, buf,
+ (new_wcs ? SZ_WCSBUF : SZ_OLD_WCSBUF));
+
+ if (iis_debug) {
+ fprintf (stderr, "set wcs:\n");
+ write (2, buf, ndatabytes);
+ }
+
+ strcpy (chan->rf_p->ctran.format, W_DEFFORMAT);
+ chan->rf_p->ctran.imtitle[0] = '\0';
+ chan->rf_p->ctran.valid = 0;
+ ct = wcs_update (xim, chan->rf_p);
+
+ /* If we're connected to an old-style client, disable the
+ * WCSPIX ISM, otherwise just let the GUI know it capable.
+ */
+ wcspix_message (xim, (new_wcs ? "capable" : "disable"));
+
+ /* Add the mapping information. */
+ add_mapping (xim, ct, chan->rf_p->wcsbuf,
+ &xim->frames[chan->reference_frame-1]);
+
+ xim_message (xim, "frameTitle", ct->imtitle);
+ }
+ return;
+
+ case IMCURSOR:
+ /* Read or write the logical image cursor. This is an extension
+ * added to provide a high level cursor read facility; this is
+ * not the same as a low level access to the IIS cursor subunit.
+ * Cursor reads may be either nonblocking (immediate) or blocking,
+ * using the keyboard or mouse to terminate the read, and
+ * coordinates may be returned in either image (world) or frame
+ * buffer pixel coordinates.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read the logical image cursor. In the case of a blocking
+ * read all we do is initiate a cursor read; completion occurs
+ * when the user hits a key or button.
+ */
+ if (iis_debug)
+ fprintf (stderr, "read cursor position\n");
+ if (iis.tid & IMC_SAMPLE) {
+ /* Sample the cursor position and return the cursor value
+ * on the output datastream encoded in a fixed size
+ * ascii buffer.
+ */
+ int wcs = iis.z;
+ int raster, frame;
+ float sx, sy;
+ IoChanPtr sv_chan;
+
+ /* Save the cursor channel so that sampled cursor reads
+ * can occur on one channel without affecting any
+ * interactive cursor reads in progress on another
+ * channel.
+ */
+ sv_chan = xim->cursor_chan;
+ xim->cursor_chan = chan;
+ xim_getCursorPos (xim, &sx, &sy, &raster, &frame);
+ xim_retCursorVal (xim, sx, sy, frame, wcs, 0, "");
+ xim->cursor_chan = sv_chan;
+
+ } else {
+ /* Initiate a user triggered cursor read. */
+ if (xim->cursor_chan) {
+ int frame = xim->cursor_chan->reference_frame;
+ xim_retCursorVal (xim, 0., 0., frame, 0, EOF, "");
+ }
+ xim->cursor_chan = chan;
+ xim_cursorMode (xim, 1);
+ }
+
+ } else {
+ /* Write (set) the logical image cursor position. */
+ CtranPtr ct;
+ int sx = iis.x, sy = iis.y;
+ float wx = sx, wy = sy;
+ int wcs = iis.z;
+
+ if (iis_debug)
+ fprintf(stderr, "write cursor position: [%d,%d]\n", sx, sy);
+ if (wcs) {
+ ct = wcs_update (xim, xim->df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ sx = (wx - ct->tx) / ct->a;
+ if (abs(ct->d) > .001)
+ sy = (wy - ct->ty) / ct->d;
+ }
+ }
+
+ xim_setCursorPos (xim, sx, sy);
+ }
+ return;
+
+ default:
+ /* Ignore unsupported command input.
+ */
+ break;
+ }
+
+ /* Discard any data following the header. */
+ if (!(iis.tid & IIS_READ))
+ for (nbytes = ndatabytes; nbytes > 0; nbytes -= n) {
+ n = (nbytes < SZ_FIFOBUF) ? nbytes : SZ_FIFOBUF;
+ if ((n = chan_read (datain, buf, n)) <= 0) {
+ if (iis_debug)
+ fprintf (stderr,
+ "discarding %d bytes following header:\n", n);
+ break;
+ }
+ }
+}
+
+
+/* SET_FBCONFIG -- Set the frame buffer configuration, or add additional
+ * frames to the current configuration.
+ */
+static void set_fbconfig(IoChanPtr chan, int config, int frame)
+{
+ XimDataPtr xim = (XimDataPtr) chan->xim;
+ FrameBufPtr fb = &xim->frames[frame-1];
+ int i;
+
+ if (config != xim->fb_configno) {
+ /* Change the frame buffer configuration. */
+ xim_initialize (xim, config,
+ max (xim->fb_config[config-1].nframes, frame), 1);
+
+ /* Reinitialize the tile framing if enabled. */
+ /* if (xim->tileFrames)
+ xim_tileFrames (xim, xim->tileFramesList);*/
+
+ /* Initialize the ISM to uncache all images when we change
+ * frame buffer configs.
+ */
+ ism_message (xim, "wcspix", "initialize");
+
+ } else if (frame > xim->nframes) {
+ /* Add additional frames. */
+ for (i=1; i <= frame; i++) {
+ fb = &xim->frames[i-1];
+ if (fb->frameno != i) {
+ xim_initFrame (xim, i, frame,
+ &xim->fb_config[config-1], xim->memModel);
+
+ /* If we're in tile mode, add the frame to the tile list
+ * and if needed resize the tile frames.
+ */
+ if (xim->tileFrames) {
+ xim->tileFramesList |= (1 << (i-1));
+ xim->nTileFrames++;
+ /* xim_tileFrames (xim, xim->tileFramesList);*/
+ }
+ }
+ }
+ }
+
+ xim_setReferenceFrame (chan, frame);
+ if (frame != xim->display_frame)
+ xim_setDisplayFrame (xim, frame);
+}
+
+
+/* DECODE_FRAMENO -- Decode encoded IIS register frame number.
+ */
+static int decode_frameno(int z)
+{
+ int n;
+
+ /* Get the frame number, encoded with a bit for each frame, 01 is
+ * frame 1, 02 is frame 2, 04 is frame 3, and so on.
+ */
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ return (max (1, n + 1));
+}
+
+/* XIM_RETCURSORVAL -- Return the cursor value on the output datastream to
+ * the client which requested the cursor read.
+ */
+void xim_retCursorVal(XimDataPtr xim, float sx, float sy, int frame, int wcs, int key, char* strval)
+{
+ CtranPtr ct;
+ MappingPtr mp = (MappingPtr) NULL;
+ int dataout, wcscode;
+ char curval[SZ_IMCURVAL];
+ char keystr[20];
+ float wx, wy;
+
+ if (xim->cursor_chan)
+ dataout = xim->cursor_chan->dataout;
+ else
+ return;
+
+ /* Compute cursor coordinates. */
+ if (wcs) {
+ ct = wcs_update (xim, xim->df_p);
+ if (ct->valid) {
+ /* The imtool WCS assumes that the center of the first display
+ * pixel is at (0,0) but actually it is at (0.5,0.5).
+ */
+ sx -= 0.5;
+ sy -= 0.5;
+
+ if (abs(ct->a) > .001)
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ if (abs(ct->d) > .001)
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+ }
+ } else {
+ wx = sx;
+ wy = sy;
+ }
+
+ /* Compute WCS code. */
+ wcscode = frame * 100 + wcs;
+ if (wcspix_enabled != NULL && *wcspix_enabled) {
+ if ((mp = xim_getMapping (xim, sx, sy, frame))) {
+ wcscode = mp->id;
+
+ /* Return the coordinates in terms of the mapping. */
+ ct = &(mp->ctran);
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+ }
+ }
+
+ /* Encode the cursor value. */
+ if (key == EOF)
+ sprintf (curval, "EOF\n");
+ else {
+ if (isprint (key) && !isspace(key)) {
+ keystr[0] = key;
+ keystr[1] = '\0';
+ } else
+ sprintf (keystr, "\\%03o", key);
+
+ sprintf (curval, "%10.3f %10.3f %d %s %s\n",
+ wx, wy, wcscode, keystr, strval);
+ }
+
+
+ if (iis_debug) fprintf (stderr, "curval: %s", curval);
+
+ /* Send it to the client program and terminate cursor mode. */
+ chan_write (dataout, curval, sizeof(curval));
+ xim_cursorMode (xim, 0);
+ xim->cursor_chan = NULL;
+}
+
+
+
+/* XIM_ENCODEWCS -- Transform the input screen (raster) coordinates and
+ * pixel value and return a string giving X, Y, and the pixel intensity in
+ * world units.
+ */
+void xim_encodewcs (XimDataPtr xim, float sx, float sy, int sz, char* obuf)
+{
+ CtranPtr ct;
+ MappingPtr mp = (MappingPtr) NULL;
+ float wx, wy, wz;
+ int i=0, ch, map_found = 0;
+ char buf[SZ_LINE];
+
+
+ /* The first time we're called get the address of the wcspix
+ * connected flag so we can check whether to get screen pixel
+ * or real-image values.
+ */
+ if (wcspix_enabled == NULL) {
+ IsmModule ism;
+ extern ismModule ism_modules[];
+ extern int ism_nmodules;
+
+ for (i=0; i < ism_nmodules; i++) {
+ ism = &ism_modules[i];
+ if (strcmp ("wcspix", ism->name) == 0)
+ wcspix_enabled = &(ism->connected);
+ }
+ }
+
+
+ /* Now lookup the coordinate mapping and update the WCS and real
+ * pixel value if the ISM is running.
+ */
+ if (wcspix_enabled != NULL && *wcspix_enabled) {
+ if ((mp = xim_getMapping (xim, sx+1.0, sy, xim->display_frame))) {
+ ct = &(mp->ctran);
+ sx -= 0.5;
+ sy -= 0.5;
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+
+ /* Found the image mapping so request the WCS
+ * and pixel information from the WPIX ISM.
+ */
+ /* if (mp->ref != NULL) {*/
+ sprintf (buf, "wcstran %d %g %g\n", mp->id, wx, wy);
+ ism_message (xim, "wcspix", buf);
+ /* }*/
+ map_found++;
+ }
+ }
+
+ ct = wcs_update (xim, xim->df_p);
+ if (ct->valid) {
+ /* The imtool WCS assumes that the center of the first display
+ * pixel is at (0,0) but actually it is at (0.5,0.5).
+ */
+
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+
+ if (sz == 0)
+ wz = 0.0;
+ else {
+ switch (ct->zt) {
+ case W_LINEAR:
+ wz = ((sz - CMS_DATASTART) * (ct->z2 - ct->z1) /
+ (CMS_DATARANGE-1)) + ct->z1;
+ break;
+ default:
+ wz = sz;
+ break;
+ }
+ }
+ } else {
+ wx = sx;
+ wy = sy;
+ wz = sz;
+ }
+
+ ch = ' ';
+ if (sz && ct->valid) {
+ if (ct->z1 < ct->z2) {
+ if (wz < (ct->z1 + 0.01))
+ ch = '-';
+ else if (wz > (ct->z2 - 0.01))
+ ch = '+';
+ } else if (ct->z1 > ct->z2) {
+ if (wz < (ct->z2 + 0.01))
+ ch = '-';
+ else if (wz > (ct->z1 - 0.01))
+ ch = '+';
+ }
+ }
+ sprintf (obuf, ct->format, wx + 0.005, wy + 0.005, wz, ch);
+}
+
+
+/* XIM_GETMAPPING -- Return the mapping struct for the given screen coords.
+ */
+static MappingPtr xim_getMapping(XimDataPtr xim, float sx, float sy, int frame)
+{
+ FrameBufPtr fb = (FrameBufPtr) NULL;
+ MappingPtr mp = (MappingPtr) NULL;
+ int j,i=0;
+ float y = xim->height - sy;
+ int map_debug = 0;
+
+
+ /* Loop through the frame buffers until we find the current one.
+ * The mappings aren't stored in the display fb so we need to
+ * search.
+ */
+ for (j=0; j < xim->nframes; j++) {
+ fb = &xim->frames[j];
+
+ if (frame == fb->frameno) {
+ /* Got the right frame, now search for mappings on this
+ * frame which intersect the screen coords. We assume there
+ * are no overlapping image mappings.
+ */
+ for (i=0; i < fb->nmaps; i++) {
+ mp = &fb->mapping[i];
+ if (map_debug) {
+ printf ("sx=%.2f sy=%.2f / %.2f --> ", sx, sy, y);
+ printf ("mp->dx=%d+%d=%d mp->dy=%d+%d=%d",
+ mp->dx, mp->dnx, mp->dx+mp->dnx,
+ mp->dy, mp->dny, mp->dy+mp->dny);
+ }
+ if ((sx >= mp->dx && sx <= (mp->dx + mp->dnx)) &&
+ ( y >= mp->dy && y <= (mp->dy + mp->dny))) {
+ if (map_debug) printf (" YES\n");
+ return (mp);
+ }
+ if (map_debug) printf (" NO\n");
+ }
+ }
+ }
+
+ return ((MappingPtr) NULL);
+}
+
+
+/* XIM_FRAMELABEL -- Return a pointer to the frame label string for the current
+ * frame.
+ */
+char* xim_frameLabel(XimDataPtr xim)
+{
+ FrameBufPtr df_p = xim->df_p;
+
+ sprintf (df_p->label, "[%d] %s", df_p->frameno, df_p->ctran.imtitle);
+ return (df_p->label);
+}
+
+
+/* WCS_UPDATE -- Load the screen WCS, if not yet validated, from the user
+ * wcs file, if any.
+ *
+ * File format (two lines):
+ *
+ * image title (imtool header label string)\n
+ * a b c d tx ty z1 z2 zt
+ *
+ * The WCS text is passed in via the data stream as a write to the subunit
+ * WCS and left in the buffer "wcsbuf". Mapping information is parsed
+ * elsewhere if needed, our only purpose here is to extract the frame WCS.
+ */
+static CtranPtr wcs_update(XimDataPtr xim, FrameBufPtr fr)
+{
+ CtranPtr ct = &fr->ctran;
+ char buf[1024], *format;
+
+
+ /* Get the new WCS. */
+ if (!ct->valid) {
+ fr->label[0] = '\0';
+ ct->zt = W_UNITARY;
+
+ /* Attempt to read the WCS and set up a unitary transformation
+ * if the information cannot be read.
+ */
+ if (sscanf (fr->wcsbuf, "%[^\n]\n%f%f%f%f%f%f%f%f%d",
+ buf, &ct->a, &ct->b, &ct->c, &ct->d, &ct->tx, &ct->ty,
+ &ct->z1, &ct->z2, &ct->zt) < 7) {
+
+ if (fr->wcsbuf[0])
+ fprintf (stderr, "ximtool: error decoding WCS\n");
+
+ strncpy (ct->imtitle, "[NO WCS]\n", SZ_IMTITLE);
+ ct->a = ct->d = 1;
+ ct->b = ct->c = 0;
+ ct->tx = ct->ty = 0;
+ ct->zt = W_UNITARY;
+
+ } else
+ /*** waj ***/
+ xim_wcs(fr->frameno, ct->a, ct->b, ct->c, ct->d,
+ ct->tx, ct->ty, ct->z1, ct->z2, ct->zt);
+ /*** waj ***/
+ strncpy (ct->imtitle, buf, SZ_IMTITLE);
+
+ ct->valid++;
+ }
+
+ /* Determine best format for wcs output. */
+ if (ct->valid && ct->zt == W_LINEAR) {
+ float z1, z2, zrange;
+ z1 = ct->z1;
+ z2 = ct->z2;
+ zrange = (z1 > z2) ? z1 - z2 : z2 - z1;
+ if (zrange < 0.01 || (abs(z1) + abs(z2)) / 2.0 < 0.01)
+ format = " %7.2f %7.2f %9.3g%c";
+ else if (zrange < 100.0 && (abs(z1) + abs(z2)) / 2.0 < 200.0)
+ format = " %7.2f %7.2f %7.3f%c";
+ else if (zrange > 99999.0 || (abs(z1) + abs(z2)) / 2.0 > 99999.0)
+ format = " %7.2f %7.2f %9.3g%c";
+ else
+ format = W_DEFFORMAT;
+ } else
+ format = " %7.2f %7.2f %7.0f%c";
+
+ strcpy (ct->format, format);
+ return (ct);
+}
+
+/* ADD_MAPPING -- Add a mapping for the current frame.
+ *
+ * File format (two lines):
+ *
+ * image title (imtool header label string)\n
+ * a b c d tx ty z1 z2 zt \n
+ * region_name sx sy snx sny dx dy dnx dny\n
+ * object_ref
+ *
+ * The WCS text is passed in via the data stream as a write to the subunit
+ * WCS and left in the buffer "wcsbuf". Mapping information is parsed
+ * elsewhere if needed, our only purpose here is to extract the frame WCS.
+ */
+
+static void add_mapping (XimDataPtr xim, CtranPtr ctran, char* wcsbuf, FrameBufPtr fr)
+{
+ MappingPtr mp = &fr->mapping[fr->nmaps];
+ CtranPtr ct = &mp->ctran;
+ int i, j, frame = fr->frameno;
+ char buf[SZ_WCSBUF];
+
+ /* Attempt to read the WCS and set up a unitary transformation
+ * if the information cannot be read.
+ */
+ if (sscanf (wcsbuf, "%[^\n]\n%f%f%f%f%f%f%f%f%d",
+ buf, &ct->a, &ct->b, &ct->c, &ct->d, &ct->tx, &ct->ty,
+ &ct->z1, &ct->z2, &ct->zt) < 7) {
+
+ if (wcsbuf[0])
+ fprintf (stderr, "ximtool: error decoding WCS\n");
+
+ strncpy (ct->imtitle, "[NO WCS]\n", SZ_IMTITLE);
+ ct->a = ct->d = 1;
+ ct->b = ct->c = 0;
+ ct->tx = ct->ty = 0;
+ ct->zt = W_UNITARY;
+ } else
+ strncpy (ct->imtitle, buf, SZ_IMTITLE);
+
+ ct->valid = 1;
+
+
+ /* Skip over the first two lines of WCS data.
+ */
+ strcpy (buf, wcsbuf);
+ for (i=0, j=0; j < 2 && buf[i]; i++)
+ if (buf[i] == '\n')
+ j++;
+
+ /* Attempt to read the mapping.
+ */
+ mp->regid = (++objid[frame-1]) + (frame * 100);
+ mp->id = mp->regid;
+ mp->ref[0] = '\0';
+ mp->region[0] = '\0';
+
+ if (sscanf (&buf[i], "%s%f%f%d%d%d%d%d%d\n%s\n",
+ mp->region, &mp->sx, &mp->sy, &mp->snx, &mp->sny,
+ &mp->dx, &mp->dy, &mp->dnx, &mp->dny, mp->ref) < 10) {
+
+ if (!wcsbuf[0])
+ fprintf (stderr, "ximtool: error decoding WCS mapping\n");
+ strncpy (mp->region, "none", SZ_IMTITLE);
+ strncpy (mp->ref, "none", SZ_IMTITLE);
+
+ mp->sx = 1.0;
+ mp->sy = 1.0;
+ mp->snx = xim->width;
+ mp->sny = xim->height;
+ mp->dx = 1;
+ mp->dy = 1;
+ mp->dnx = xim->width;
+ mp->dny = xim->height;
+ }
+ memmove (ctran, &mp->ctran, sizeof (Ctran));
+
+
+ /* Tell the ISM to cache this mapping if we have an object ref. */
+ sprintf (buf, "cache %s %d", mp->ref, mp->id);
+ ism_message (xim, "wcspix", buf);
+ sprintf (buf, "wcslist %d", mp->id);
+ ism_message (xim, "wcspix", buf);
+
+ /* Send the object ref to the GUI. */
+ sprintf (buf, "cache %s %d %d", mp->ref, fr->frameno, mp->id);
+ wcspix_message (xim, buf);
+ sprintf (buf, "orient %d %d %d %d",
+ mp->id, fr->frameno, (int)ctran->a, (int)(-1 * ctran->d));
+ wcspix_message (xim, buf);
+
+ fr->nmaps++;
+
+
+ /* Debug the mappings. */
+ if (getenv("DEBUG_MAPPINGS") != NULL) print_mappings (fr);
+}
+
+
+/* PRINT_MAPPINGS -- Debug routine to print all mappings on a frame.
+ */
+static void print_mappings(FrameBufPtr fr)
+{
+ MappingPtr mp;
+ int i;
+
+ if (fr->nmaps == 0) printf ("No mappings for frame %d\n", fr->frameno);
+ for (i=0; i < fr->nmaps; i++) {
+ mp = &fr->mapping[i];
+ printf ("Mapping %d of %d: id=%d frame=%d:\n",
+ i+1, fr->nmaps, mp->id, fr->frameno);
+ printf ("\t%s %f %f %d %d %d %d %d %d\n\t%s\n",
+ mp->region, mp->sx, mp->sy, mp->snx, mp->sny,
+ mp->dx, mp->dy, mp->dnx, mp->dny, mp->ref);
+ }
+}
+
+
+/* CHAN_READ -- Read exactly "n" bytes from a descriptor.
+ */
+
+static int chan_read(int fd, void* vptr, int nbytes)
+{
+ char *ptr = vptr;
+ int nread = 0, nleft = nbytes, nb = 0;
+
+ while (nleft > 0) {
+ if ( (nb = read(fd, ptr, nleft)) < 0) {
+ if (errno == EINTR)
+ nb = 0; /* and call read() again */
+ else
+ return(-1);
+ } else if (nb == 0)
+ break; /* EOF */
+ nleft -= nb;
+ ptr += nb;
+ nread += nb;
+ }
+ return (nread); /* return no. of bytes read */
+}
+
+
+/* CHAN_WRITE -- Write exactly "n" bytes to a descriptor.
+ */
+
+static int chan_write (int fd, void* vptr, int nbytes)
+{
+ char *ptr = vptr;
+ int nwritten = 0, nleft = nbytes, nb = 0;
+
+ while (nleft > 0) {
+ if ( (nb = write(fd, ptr, nleft)) <= 0) {
+ if (errno == EINTR)
+ nb = 0; /* and call write() again */
+ else
+ return(-1); /* error */
+ }
+ nleft -= nb;
+ ptr += nb;
+ nwritten += nb;
+ }
+ return (nwritten);
+}
+