diff options
Diffstat (limited to 'tcliis/iis.c')
-rw-r--r-- | tcliis/iis.c | 1494 |
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); +} + |