.help imdis Dec84 "Image Display I/O"
.ce
\fBImage display I/O Design\fR
.ce
Richard Wolff
.ce
May 1985
.sp 1
.nh
Introduction

    The image display i/o interface uses the features of the GIO interface
to provide for the reading and writing of images and for the control of
the various subunits of a typical image display device.  The cell array
calls of GIO are used for the image data, while the text and polyline
functions handle the text and line generation.  Cursor reads are also
done with standard GIO calls.  However, all the other display functions
are implemented through a series of GIO escape sequences, which are
described in this document.
.sp
.nh
Escape sequences

    Each sequence is described here, giving first a line with the count
of the number of words in the escape "instruction", followed by the data.
Since most of the data items might be more rationally considered arrays,
they are so indicated here.  This means that in most cases, the number of
words in the escape instruction cannot be determined until run-time; an
indication of this is the use of "sizeof(arrays)" to indicate the number
of words in all the pseudo arrays.
.sp
Escape 10  --  reset
.ls
.tp 5
1 hard/medium/soft
.ls
.nf
hard		Clear image and graphics planes
medium		reset all (lookup) tables to linear
soft		reset scroll, zoom, cursor, alu, etc.
.fi
.le
.le
.sp
This sequence is used to preform various reset commands.  These are not
done at GKOPENWS time because the user will not necessarily want to
upset the existing display when the image kernel is started up.
.sp
Escape 11  --  set image plane
.ls
.tp 4
sizeof(arrays) IFA IBPL
.ls
.nf
IFA(i)		image frame array
IBPL(i)		image bit plane array
.fi
.le
.le
.sp
This sequence is essentially a header to the getcell/putcell calls.  It
identifies both the frame(s) and bit plane(s) to be read or written.  IFA
is an array of (short) integers, each of which specifies a plane (using
one indexing), the last element of the array being the integer IDS_EOD
to flag the End Of Data.  IDS_EOD is a defined to be (-2).  IBPL represents
the bit planes that are to be read or written for all the frames in IFA.
The data is IBPL is terminated with IDS_EOD.  If the first element of IFA (or
IBPL) is IDS_EOD, all image frames (all bit planes) are involved in the I/O.
All "array" data are expected to be terminated with IDS_EOD, and the general
convention is maintained that IDS_EOD with no preceding data implies all
"frames", "colors", or whatever.
.sp
Escape 12  --  set graphics plane
.ls
.tp 4
sizeof(arrays) GFA GBPL
.ls
.nf
GFA(i)		graphics frame array
GBPL(i)		graphics bit plane array
.fi
.le
.le
.sp
This sequence is identical to escape 11, but refers to graphics planes
instead of image planes.  Generally, each graphics bit plane will refer to
a particular color, or perhaps, to a particular image plane.  But there is
no enforced correspondence between graphics planes and image planes or colors.
The GFA specifies a set of graphics planes, and is probably unnecessary as the
bitplane array carries adequate information.  Including it, however, retains
symmetry with escape 11.  Thus, GFA cannot be omitted, for otherwise the
kernel would not know where GBPL started, but is set to IDS_EOD, and the
kernel can then find and ignore it.
.sp
Escape 13  --  display image
.ls
.tp 6
1+sizeof(arrays) ON/OFF IFA ICOLOR IQUAD
.ls
.nf
ON/OFF		turn frame on or off
IFA(i)		image frame array
ICOLOR(i)	image color array
IQUAD(i)	image quadrant array (for split screen mode)
.fi
.le
.le
.sp
The specified image planes are all to be displayed in all the colors given
by ICOLOR.  If ICOLOR(1) is IDS_EOD, a full color display is implied.
The quadrant value specifies which quadrant the frames are
to appear in--this is needed only when the split screen mode is in effect;
otherwise, IQUAD[1] = IDS_EOD.
.sp
Escape 14  --  display graphics
.ls
.tp 6
1+sizeof(arrays) ON/OFF GBPL GCOLOR GQUAD
.ls
.nf
ON/OFF		turn referenced planes on or off
GBPL(i)		graphics bit plane array
GCOLOR(i)	graphics color array
GQUAD(i)	graphics quadrant array (for split screen mode)
.fi
.le
.le
.sp
This sequence is identical to escape 13, except for the substitution of
a bitplane array for frames, since graphics are usually treated bit by bit.
[With the IIS systems, for instance, this call requires manipulation of
the color-graphics lookup table.]
.sp
Escape 15  --  save device state
.ls
.tp 5
1+sizeof(arrays) FD IFA GFA
.ls
.nf
FD		file descriptor for save file
IFA(i)		image frame array
GFA(i)		graphics frame array
.fi
.le
.le
.sp
Saves the specified image frames and graphics planes and all the device
dependent status information in the file referenced by FD.  Not implemented
in the Kernel (yet).
.sp
Escape 16  --  restore device state
.ls
.tp 5
1+sizeof(arrays) FD IFA GFA
.ls
.nf
FD		file descriptor for restore file
IFA(i)		image frame array
GFA(i)		graphics frame array
.fi
.le
.le
.sp
Restores the specified image frames and graphics planes and all the device
dependent status information from the file referenced by FD.  Not implemented
in the Kernel (yet).
.sp
Escape 17  --  control
.ls
.tp 9
4+sizeof(arrays) REG RW N FRAME COLOR OFFSET DATA
.ls
.nf
REG(i)		control register or function
RW(i)		read or write (write is 0, read 1, wait/read is 2)
N(i)		Number of data values
FRAME(i)	frame array
COLOR(i)	color array
OFFSET(i)	offset or other datum
DATA(Ni)	array of data
.fi
.le
.le
.sp
Escape 18 is a very general sequence for writing any device
control register.  Such "registers" include such generally available
capabilities as look-up tables, as well as specifics, such as min/max
registers. The upper level code may have to consult an "imagecap"
file to determine what it can request.

FRAME, OFFSET, and COLOR, may not be needed for a particular operation,
but these arrays cannot be omitted; rather, use a one element array with
the value IDS_EOD.  Should additional information be needed for an operation,
it can be transmitted in DATA.
.sp
.nh
Examples

.sp
To clear all frames, one would issue the following sequence
.ls
.tp 4
.nf
GKI_ESCAPE  11  IFA[1] = IDS_EOD  IBPL[1] = IDS_EOD
GKI_CLEARWS
GKI_ESCAPE  12  IFA[1] = IDS_EOD  IBPL[1] = IDS_EOD
GKI_CLEARWS
.fi
.le
.sp
To write an image to frame 2 ( IIS internal frame number 1 )
.ls
.tp 2
.nf
GKI_ESCAPE  11  IFA[1] = 2  IFA[2] = IDS_EOD  IBPL[1] = IDS_EOD
GKI_PCELL   data
.fi
.le
.sp
To activate frame 1 in red and green
.ls
.tp 2
.nf
GKI_ESCAPE  13  IFA[1] = 1  IFA[2] = IDS_EOD  ICOLOR[1] = IDS_RED
		ICOLOR[2] = IDS_GREEN  ICOLOR[3] = IDS_EOD
		IQUAD[1] = IDS_EOD
.fi
.le
.sp
.bp
.nh
Defines

This section presents the value and intended use of each of the various
defined constants.  This list is likely to expand.

.nf
define	IDS_EOD         (-2)		# flag for end of data

define	IDS_RESET	10		# escape 10
define	IDS_R_HARD	 0		# hard reset
define	IDS_R_MEDIUM	 1		# medium
define	IDS_R_SOFT	 2
define	IDS_R_SNAPDONE	 3		# end snap

define	IDS_SET_IP	11		# escape 11
define	IDS_SET_GP	12		# escape 12
define	IDS_DISPLAY_I	13		# escape 13
define	IDS_DISPLAY_G	14		# escape 14
define	IDS_SAVE	15		# escape 15
define	IDS_RESTORE	16		# escape 16

# max sizes

define	IDS_MAXIMPL	16		# maximum number of image planes
define	IDS_MAXGRPL	16		# maximum number of graphics planes
define	IDS_MAXBITPL	16		# maximum bit planes per frame
define	IDS_MAXGCOLOR	 8		# maximum number of colors (graphics)
define	IDS_MAXDATA   8192		# maximum data structure in display

define	IDS_RED		 1
define	IDS_GREEN	 2
define	IDS_BLUE	 3
define	IDS_YELLOW	 4
define	IDS_RDBL	 5
define	IDS_GRBL	 6
define	IDS_WHITE	 7
define	IDS_BLACK	 8

define	IDS_QUAD_UR	 1		# upper right quad.: split screen mode
define	IDS_QUAD_UL	 2
define	IDS_QUAD_LL	 3
define	IDS_QUAD_LR	 4

define	IDS_CONTROL	17		# escape 17
define	IDS_CTRL_LEN	 6
define	IDS_CTRL_REG	 1		# what to control
define	IDS_CTRL_RW	 2		# read/write field in control instr.
define	IDS_CTRL_N	 3		# count of DATA items
define	IDS_CTRL_FRAME	 4		# pertinent frame(s)
define	IDS_CTRL_COLOR	 5		#   and color
define	IDS_CTRL_OFFSET	 6		# generalized "register"
define	IDS_CTRL_DATA	 7		# data array

define	IDS_WRITE	 0		# write command
define	IDS_READ	 1		# read command
define	IDS_READ_WT	 2		# wait for action, then read
define	IDS_OFF		 1		# turn whatever off
define	IDS_ON		 2
define	IDS_CBLINK	 3		# cursor blink
define	IDS_CSHAPE	 4		# cursor shape

define	IDS_CSTEADY	 1		# cursor blink - steady (no blink)
define	IDS_CFAST	 2		# cursor blink - fast
define	IDS_CMEDIUM	 3		# cursor blink - medium
define	IDS_CSLOW	 4		# cursor blink - slow

define	IDS_FRAME_LUT	 1		# look-up table for image frame
define	IDS_GR_MAP	 2		# graphics color map...lookup table per
					# se makes little sense for bit plane
define	IDS_INPUT_LUT	 3		# global input lut
define	IDS_OUTPUT_LUT	 4		# final lut
define	IDS_SPLIT	 5		# split screen coordinates
define	IDS_SCROLL	 6		# scroll coordinates
define	IDS_ZOOM	 7		# zoom magnification
define	IDS_OUT_OFFSET	 8		# output bias
define	IDS_MIN		 9		# data minimum
define	IDS_MAX		10		# data maximum
define	IDS_RANGE	11		# output range select
define	IDS_HISTOGRAM	12		# output data histogram
define	IDS_ALU_FCN	13		# arithmetic feedback function
define	IDS_FEEDBACK	14		# feedback control
define	IDS_SLAVE	15		# auxiliary host or slave processor

define	IDS_CURSOR	20		# cursor control - on/off/blink/shape
define	IDS_TBALL	21		# trackball control - on/off
define	IDS_DIGITIZER	22		# digitizer control - on/off

define	IDS_BLINK	23		# for blink request
define	IDS_SNAP	24		# snap function
define	IDS_MATCH	25		# match lookup tables

# snap codes ... just reuse color codes from above.
define	IDS_SNAP_RED	 IDS_RED	# snap the blue image
define	IDS_SNAP_GREEN	 IDS_GREEN	# green
define	IDS_SNAP_BLUE	 IDS_BLUE	# blue
define	IDS_SNAP_RGB	 IDS_BLACK	# rgb image --- do all three
define	IDS_SNAP_MONO	 IDS_WHITE	# do just one

# cursor parameters

define	IDS_CSET	128		# number of cursors per "group"

define	IDS_CSPECIAL	4097		# special "cursors"
			# must be > (IDS_CSET * number of cursor groups)
define	IDS_CRAW	IDS_CSPECIAL	# raw cursor read
define	IDS_BUT_RD	4098		# "cursor number" for read buttons cmd
define	IDS_BUT_WT	4099		# wait for button press, then read
define	IDS_CRAW2	4100		# a second "raw" cursor
.fi
.nh
Explanation

    Most of the control functions of an image display do not fit within
the standard GIO protocols, which is why the escape function is provided.
However, image displays exhibit a wide range of functionality, and some
balance must be achieved between code portability/device independence and
use of (possibly peculiar) capabilities of a particular device.  The control
functions (such as IDS_FRAME_LUT, IDS_CURSOR, IDS_SLAVE) "selected" here
are, for the most part, general functions, but the code was written with
the IIS Model 70 at hand (and in mind), and some "defines" reflect this.

    The model of the display is a device with some number of image frames,
each of which has associated with it an INPUT look-up table, used for
scaling or bit selection as data is written into the image frame;
a FRAME look-up table for each of the three primary colors, used to
alter the video stream from the image frame; combining logic that sums the
output of the various FRAME tables, forming three data streams, one for
each color; an OUTPUT look-up table that forms a final transformation
on each color prior to the data being converted to analog form; and
possibly, bias (OUT_OFFSET) and RANGE scaling applied somewhere in the
data stream (most likely near the OUTPUT look-up tables).

    Each image plane can be SCROLLed and ZOOMed independently (though
of course, not all devices can do this), and there may be SPLIT screen
capability, with the possibility of displaying parts of four images
simultaneously.

    Hooks have been provided in case there is a ALU or FEEDBACK hardware,
or there is a SLAVE processor, but use of these functions is likely to
be quite device dependent.  The IIS can return to the user the MINimum
and MAXimum of a color data stream, and can also run a histogram on
selected areas of the display:  There are "defines" pointing to these
functions, but their use is not yet specified and there is not yet
a clean way, within the GIO protocols, for reading back such data.

    Three functions that not so hardware oriented have "defines":
BLINK, MATCH and SNAP.  The first is used if the hardware supports
blink.  MATCH allows the kernel code to copy look-up tables---something
the upper level code could do were there a well defined mechanism for
reading non-image data back.  SNAP is used to set-up the kernel so that
a subsequent set of get_cellarray calls can be used to return a data
stream that represents the digital data arriving at the
digital-to-analog converters:  the kernel mimics the hardware and so
provides a digital snapshot of the image display screen.

    Images are loaded by a series of put_cellarray calls, preceded
by one IDS_SET_IP escape to configure the kernel to write the put_cell
data into the correct image planes (and optionally, specific bit planes).
The graphics planes are written to in the same manner, except that
IDS_SET_GP is used.  It is not guaranteed that the SET_IP and SET_GP
are independent, and so the appropriate one should be given before
each put_cell sequence.  Put_cells can be done for any arbitrary
rectangular array; they are turned into a series of writes to a
sequence of image rows by the GIO interface code.

    Calls to put_cell require the mapping of pixel coordinates
to NDC, which is made more complex than one might first
guess by the fact that the cell array operations are specified
by *inclusive* end points...See the write-up in "Note.pixel".

    Images planes are erased by the standard GIO gclear call, which
must be preceded by a SET_IP (or SET_GP for graphics).  This is
perceived as reasonably consistent with the image loading as erasure
is loading with zeros, but presumably can be done far more efficiently
in most devices than with a series of put_cell calls.

    Images planes are turned on and off with IDS_DISPLAY_I, and graphics
planes with IDS_DISPLAY_G.  Color and quadrant information must be
supplied as mentioned in the descriptions for escapes 13 and 14.

    The look-up tables are specified to the lower level code by giving
the end points of the line segments which describe the table function.
The end points are specified in NDC.  This makes for a
simple, and device independent, upper level code.  However, there is no
obvious (to the writer at least) code to invert the process, and return
end points for the simplest line segments that would describe a given
look-up table.  (Moreover, there is no mechanism to return such information
to the upper level.)  Therefore, the kernel code is asymmetric, in that
writes to the tables are fed data in the form of end points, but reads from
the tables (needed for the kernel implementation of SNAP) return the
requested number data values as obtained from the hardware.

    The control sequence for the ZOOM function requires, in addition to
the usual frame/color information, a zoom power followed by the GKI
coordinates of the pixel to be placed at the screen center.  Likewise,
the SCROLL and SPLIT screen functions require GKI center coordinates.

    The OFFSET and RANGE sequences provide for bias and scaling of the
image data.  Where they take effect is not specified.  Offset requires
a signed number to be added to the referenced data; range is specified
by a small integer which selects the "range" of the data.

    Control of hardware cursors, trackballs, etc is provided:  CURSOR
can be used to select cursor shape, blink rate, etc.  Devices such as
(trackball) buttons are interrogated as if they are cursors, with a
cursor number that is greater than to IDS_CSPECIAL.  The "key" value
returned by a "read" call to devices such as the trackball buttons will
be zero if no button was pressed or some positive number to represent
the activated device.  Any "read" may be instructed to return
immediately (IDS_READ) or wait for some action (IDS_READ_WT); for
buttons, there are special IDS_BUT_RD/IDS_BUT_WT.

    Cursors are read and written through the standard GIO interface.
The cursor number ranges from 1 up through IDS_CSPECIAL-1.  Each
frame has a set of set of cursors associated with it: frame n has
cursors numbered n, IDS_CSET+n, 2*IDS_CSET+n, etc.  Currently,
IDS_CSPECIAL is 4097, and IDS_CSET is 128, so there can be 128
different frames, each with 32 cursors.  The coordinates associated
with a given cursor, and hence frame, are NDC for the pixel on which
the cursor is positioned.  If a frame is not being displayed, a cursor
read for that frame will return NDC for the pixel that would appear at
the current cursor position if the frame were enabled.  Note that the
NDC used in the cursor_set and cursor_read calls are relative to
the image planes in the display device; the fact the image data may
have come from a much larger user "world" is not, and can not be,
of any concern to the kernel code.

    Cursor 0 is special, and is not associated with a particular frame;
rather, the kernel is allowed to choose which frame to associate with
each cursor zero read or write.  The IIS code picks the lowest numbered
frame that is on (being displayed).  With split screen activated, a
frame can be "on" and not be seen; for cursor zero, what matters is
whether the frame video is active, not whether the split position
happens to be hiding the frame.  The "key" value returned by the cursor
read routine is the frame number selected by the kernel.  Cursor
IDS_CSPECIAL is also unusual, since it refers to the screen coordinates
and returns NDC for the screen.  It is referred in the code as IDS_CRAW
(a "raw" cursor) and is needed for positioning the cursor at specific
points of the screen.

    The MATCH function requires that the frame and color information
of the control escape sequence point to the reference table; the
tables to be changed are given in the "data" part with the (IDS_EOD
terminated) frame sequence preceding the color information.  The RW
field specifies which type of look-up table is to be changed.
.sp
.nh
Interface Routines

    The routines listed here are those used to implement the video
control package, and are found in the file "cvutil.x".
Arguments relating to image frames, image colors, display quadrants,
offset, range, and look-up table data are short integer arrays,
terminated by IDS_EOD.  Cursor position (x and y) are NDC (hence, real).
All other arguments are integers.

.ls cvclearg (frame, color)
Clears (erases) the given color (or colors) in the graphics frame given
by the argument "frame".  For the IIS display, the "frame" argument
is not relevant, there being only one set of graphics frames.
.le
.ls cvcleari (frames)
Clears (erases) all bits in the given image display frames.
.le
.ls cv_rdbut
Reads the buttons on whatever device the kernel code associates with
this call, and returns an integer representing the button most recently
pressed.  If none pressed, returns zero.
.le
.ls cv_wtbut
Same as cv_rdbut, but if no button pressed, waits until one is.  This
routine will, therefore, always return a non-zero (positive) integer.
.le
.ls cv_rcur (cnum, x, y)
Reads the cursor "cnum" returning the NDC coordinates in x and y.  The
mapping of cursor number to frame is described in the preceding
section: for cursors with numbers below IDS_CSET (128), the cursor
refers to the frame (cnum equal 5 means frame 5).
.le
.ls cv_scur (cnum, x, y)
Sets the cursor to the NDC given by x and y for the frame referenced by
cnum.
.le
.ls cv_scraw (x, y)
Sets the "raw cursor" to position (x,y).
.le
.ls cv_rcraw (x, y)
Reads the "raw cursor" position in (screen) NDC.
.le
.ls cvcur (cmd)
Turns the cursor on (cmd is IDS_ON) or off (IDS_OFF).
.le
.ls cvdisplay (instruction, device, frame, color, quad)
Turns on ("instruction" equals IDS_ON) image plane ("device" equals
IDS_DISPLAY_I) frame (or frames) in specified colors and quadrants.
Turn them off if "instruction" equals IDS_OFF.  Manipulates graphics
planes instead if "device" equals IDS_DISPLAY_G.
.le
.ls cvmatch (type, refframe, refcolor, frames, color)
Copies the reference frame and reference color into the given frames
and color.  For the IIS, "type" is either IDS_FRAME_LUT, referring to the
look-up tables associated with each frame, or IDS_OUTPUT_LUT, referring
to the global Output Function Memory tables.
.le
.ls cvoffset (color, data)
Sets the offset constants for the specified colors to values given in
"data"; if there are more colors given than corresponding data items,
the kernel will reuse the last data item as often as necessary.
.le
.ls cvpan (frames, x, y)
Moves the given frames so that the NDC position (x,y) is at the center
of the display.
.le
.ls cvrange (color, range)
Scales the output for the given colors; if there are more colors given
than corresponding range items, the kernel will reuse the last data item
as often as necessary.  Range is a small number which specifies which
range the data is to be "put" in.  For the IIS, there are only 4 useful
values (1,2,4, and 8); the kernel will map the requested value to the
next smallest legitimate one.
.le
.ls cvreset (code)
Resets the part of the display referenced by "code".  For the IIS, a code
of IDS_R_HARD refers to (erasing) the image and graphics planes, IDS_R_MEDIUM
resets the various look-up tables, and IDS_R_SOFT resets the various registers
(such as zoom, scroll, range, split screen, and so forth).
.le
.ls cvsnap (filename, snap_color)
Creates an IRAF image file, named "filename", which represents the image
display video output for the specified color (IDS_SNAP_RED, IDS_SNAP_MONO,
etc).  "filename" is a "char" array.  The image is of the full display,
though, since the data is obtained from the kernel line by line via
get_cellarray calls, partial snapshots can be implemented easily.
.le
.ls cvsplit (x,y)
Sets the split screen point at NDC position (x,y).
.le
.ls cvtext (x, y, text, size)
Writes the given text at NDC position (x,y) in the specified size.
Currently, font and text direction are set to NORMAL.
.le
.ls cvwhich (frame)
Tells which frames are on.  In the current implementation, this relies
on reading cursor 0:  in this special case, the cursor variable passed
to ggcur() is changed by the kernel to reflect which frame it selected
(or ERR if no frame is active).
.le
.ls cvwlut (device, frames, color, data, n)
Writes the look-up tables associated with "frames" and "color".  "device"
is IDS_FRAME_LUT or IDS_OUTPUT_LUT.  The data to be written is given as
a series of line segments, and hence is described as a series of GKI
(x,y) pairs representing the line end points.  For connected lines,
the first pair gives the first line segment starting coordinates, and all
following pairs the endpoints.  The variable "n" gives the number of
values in "data"; there is no terminating IDS_EOD.
.le
.ls cvzoom (frames, power, x, y)
Zooms, to the given power, the specified frames with each frame
centered, after the zoom, at the given NDC position.
.le

    The following two support routines are included in the interface
package.
.ls cv_move (in, out)
Copies the short array "in" into the short array "out", up to and
including a trailing IDS_EOD.  This procedure returns the number of
items copied.
.le
.ls cv_iset (frames)
Implements the image display escape sequence, with the bitplane
argument to that escape sequence set to "all".
.le
.ls cv_gset (colors)
Implements the graphics display escape sequence, with the image
argument to that escape sequence set to "all".
.le
.sp
.nh
Example

    The following code is used to pan (scroll) the image in response
to a changing cursor position.  It is assumed that the "frame" array
consists of a list of frames to be panned together, terminated, as
is almost everything in this code, by IDS_EOD.
.nf

# Pan subroutine

procedure pansub (frames)

short	frames[ARB]			# frames to pan

int	button
int	cnum, cv_rdbut()
real	x,y, xc, yc
real	oldx, oldy

begin
	button = cv_rdbut()		# clear buttons by reading them
	call eprintf ("Press any button when done\n")

	# Where is cursor now?
	# cv_rcraw uses the "RAW CURSOR" which reads and writes in
	# screen (NDC) coordinates instead of image NDC.

	call cv_rcraw (xc,yc)

	# Pixel to NDC transformation is discussed in the file 
	# "Note.pixel"

	x = x_screen_center_in_NDC
	y = y_screen_center_in_NDC

	call cv_scraw (x, y)	# put cursor at screen center

	# Select a cursor---at least one per frame (conceptually at least)

	cnum = frames[1]

	# If cnum == IDS_EOD, the calling code did not select a frame.  So,
	# if cnum is 0, the kernel will select an active frame as the
	# one to use when mapping NDC cursor positions to screen
	# coordinates.

	if (cnum == IDS_EOD)
	    cnum = 0

	# Determine NDC at screen center (where cursor was moved to)
	# for frame of interest
	call cv_rcur (cnum, x, y)

	# Restore cursor to original position
	call cv_scraw (xc, yc)

	repeat {
	    oldx = xc
	    oldy = yc
	    repeat {
	        call cv_rcraw (xc, yc)
	        button = cv_rdbut()
	    } until ( (xc != oldx) || (yc != oldy) || (button > 0))
	    # Determine change and reflect it about current screen
	    # center so image moves in direction cursor moves.
	    x = x - (xc - oldx)
	    y = y - (yc - oldy)
	    # If x or y are <0 or > 1.0, add or subtract 1.0
	    "adjust x,y"
	    call cvpan (frames, x, y)
	} until (button > 0)
end
.fi
    [The call to cvpan may in fact need to be a series of calls, with
the array "frames" specifying one frame at a time, and (x,y) being the
new cursor position for that particular frame, so that differently panned
frames retain their relative offsets.]
    The cursor and button routines are given here.
.nf

# CV_RDBUT -- read button on trackball (or whatever)
# if none pressed, will get zero back

int procedure cv_rdbut()

int	oldcnum
real	x, y
int	button
int	gstati

include "cv.com"

begin
	oldcnum = gstati (cv_gp, G_CURSOR)
	call gseti (cv_gp, G_CURSOR, IDS_BUT_RD)
	call ggcur (cv_gp, x, y, button)
	call gseti (cv_gp, G_CURSOR, oldcnum)
	return(button)
end

# CV_RCUR -- read cursor.  The cursor read/set routines do not restore
# the cursor number...this to avoid numerous stati/seti calls that
# usually are not needed.

procedure cv_rcur (cnum, x, y)

int	cnum
real	x,y
int	junk

include	"cv.com"

begin
	call gseti (cv_gp, G_CURSOR, cnum)
	call ggcur (cv_gp, x, y, junk)
end

# CV_SCUR -- set cursor

procedure cv_scur (cnum, x, y)

int	cnum
real	x,y

include "cv.com"

begin
	call gseti (cv_gp, G_CURSOR, cnum)
	call gscur (cv_gp, x, y)
end

# CV_SCRAW -- set raw cursor

procedure cv_scraw (x, y)

real	x,y

begin
	call cv_scur (IDS_CRAW, x, y)
end
.fi

    The routine cv_move copies its first argument to the second up through
the required IDS_EOD termination, returning the number of items copied.
"cv_stack" is a pointer to a pre-allocated stack area that is used to
build the data array passed to the GIO escape function.

.nf
# cvpan -- move the image(s) around

procedure cvpan (frames, x, y)

short	frames[ARB]
real	x,y					# position in NDC
int	count, cv_move()

include	"cv.com"

begin
	Mems[cv_stack] = IDS_SCROLL		# Control Unit
	Mems[cv_stack+1] = IDS_WRITE		# Read/Write

	# Three is the number of data items (two coordinates) plus the
	# terminating IDS_EOD.  In many escape sequences, this number
	# must be determined from the data rather than known in advance.

	Mems[cv_stack+2] = 3

	# Move the frame data, which is of "unknown" length

	count = cv_move (frames, Mems[cv_stack+3])

	# Color is unimportant here, but the color data must exist.  The
	# simplest solution is to use IDS_EOD by itself.

	Mems[cv_stack+3+count] = IDS_EOD	# default to all colors
	Mems[cv_stack+4+count] = 1		# (unused) offset
	Mems[cv_stack+5+count] = x * GKI_MAXNDC
	Mems[cv_stack+6+count] = y * GKI_MAXNDC
	Mems[cv_stack+7+count] = IDS_EOD	# for all frames
	call gescape (cv_gp, IDS_CONTROL, Mems[cv_stack], count+8)
end
.fi
.endhelp
