aboutsummaryrefslogtreecommitdiff
path: root/sys/gio/cursor
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /sys/gio/cursor
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'sys/gio/cursor')
-rw-r--r--sys/gio/cursor/README9
-rw-r--r--sys/gio/cursor/doc/cursor.hlp194
-rw-r--r--sys/gio/cursor/doc/giotr.notes330
-rw-r--r--sys/gio/cursor/giotr.x183
-rw-r--r--sys/gio/cursor/grc.h20
-rw-r--r--sys/gio/cursor/grcaxes.x402
-rw-r--r--sys/gio/cursor/grcclose.x42
-rw-r--r--sys/gio/cursor/grccmd.x533
-rw-r--r--sys/gio/cursor/grcinit.x32
-rw-r--r--sys/gio/cursor/grcopen.x105
-rw-r--r--sys/gio/cursor/grcpl.x69
-rw-r--r--sys/gio/cursor/grcread.x60
-rw-r--r--sys/gio/cursor/grcredraw.x21
-rw-r--r--sys/gio/cursor/grcscr.x49
-rw-r--r--sys/gio/cursor/grcstatus.x49
-rw-r--r--sys/gio/cursor/grctext.x57
-rw-r--r--sys/gio/cursor/grcwarn.x27
-rw-r--r--sys/gio/cursor/grcwcs.x282
-rw-r--r--sys/gio/cursor/grcwrite.x66
-rw-r--r--sys/gio/cursor/gtr.com25
-rw-r--r--sys/gio/cursor/gtr.h51
-rw-r--r--sys/gio/cursor/gtrbackup.x74
-rw-r--r--sys/gio/cursor/gtrconn.x78
-rw-r--r--sys/gio/cursor/gtrctrl.x122
-rw-r--r--sys/gio/cursor/gtrdelete.x45
-rw-r--r--sys/gio/cursor/gtrdiscon.x66
-rw-r--r--sys/gio/cursor/gtrfetch.x48
-rw-r--r--sys/gio/cursor/gtrframe.x41
-rw-r--r--sys/gio/cursor/gtrgflush.x45
-rw-r--r--sys/gio/cursor/gtrgtran.x28
-rw-r--r--sys/gio/cursor/gtrgtty.x20
-rw-r--r--sys/gio/cursor/gtrinit.x136
-rw-r--r--sys/gio/cursor/gtropenws.x206
-rw-r--r--sys/gio/cursor/gtrpage.x30
-rw-r--r--sys/gio/cursor/gtrptran.x74
-rw-r--r--sys/gio/cursor/gtrrcur.x32
-rw-r--r--sys/gio/cursor/gtrredraw.x48
-rw-r--r--sys/gio/cursor/gtrreset.x53
-rw-r--r--sys/gio/cursor/gtrset.x28
-rw-r--r--sys/gio/cursor/gtrstatus.x100
-rw-r--r--sys/gio/cursor/gtrtrunc.x39
-rw-r--r--sys/gio/cursor/gtrundo.x76
-rw-r--r--sys/gio/cursor/gtrwaitp.x94
-rw-r--r--sys/gio/cursor/gtrwcur.x19
-rw-r--r--sys/gio/cursor/gtrwritep.x68
-rw-r--r--sys/gio/cursor/gtrwsclip.x144
-rw-r--r--sys/gio/cursor/gtrwstran.x490
-rw-r--r--sys/gio/cursor/mkpkg57
-rw-r--r--sys/gio/cursor/prpsinit.x15
-rw-r--r--sys/gio/cursor/rcursor.x692
50 files changed, 5574 insertions, 0 deletions
diff --git a/sys/gio/cursor/README b/sys/gio/cursor/README
new file mode 100644
index 00000000..6534b497
--- /dev/null
+++ b/sys/gio/cursor/README
@@ -0,0 +1,9 @@
+This directory contains the source for GIOTR and cursor mode, i.e., the code
+required to process the graphics output of a graphics task, spooling and/or
+applying the workstation transformation and passing the transformed metacode
+instructions on to the builtin STDGRAPH kernel or to an external kernel. The
+procedure RCURSOR is the main entry point for cursor mode. RCURSOR is called
+by the CL to service a query for a cursor type parameter when query mode is
+in effect. The workstation transformation is used to zoom and pan on a frame
+buffer and consists of a viewport transformation in GKI coordinates with
+clipping at the viewport boundary.
diff --git a/sys/gio/cursor/doc/cursor.hlp b/sys/gio/cursor/doc/cursor.hlp
new file mode 100644
index 00000000..d9912607
--- /dev/null
+++ b/sys/gio/cursor/doc/cursor.hlp
@@ -0,0 +1,194 @@
+.help GIO Mar85 "Cursor Mode"
+.nh 3
+Cursor Mode
+
+ In cursor mode, i.e., after a call to \fBclgcur\fR or after typing "=gcur",
+a number of special keystrokes shall be recognized for interactive display
+control. All graphics output to stdgraph and stdimage is routed through the
+CL on the way to the graphics kernel. The CL will optionally spool in an
+internal buffer all graphics instructions output to an interactive device.
+This internal buffer is emptied whenever the device screen is cleared.
+In cursor mode, special keystrokes may be used to redraw all or any portion
+of the spooled graphics, e.g., one may zoom in on a portion of the plot and
+then roam about on the plot at high magnification. Since the spooled graphics
+vectors typically contain more information than can be displayed at normal
+magnification, zooming in on a feature may bring out additional detail
+(the maximum resolution is 32768 points in either axis). Increasing the
+magnification will increase the precision of the cursor by the same factor.
+
+Cursor mode is implemented by performing coordinate transformation and
+clipping on each GKI instruction in the frame buffer, passing the transformed
+and clipped instructions on to the graphics kernel.
+The cursor mode operations perform a simple geometric transformation on
+the spooled graphics frame, mapping a rectangular window of the spooled
+frame onto the device screen. The graphics frame itself is not modified,
+hence zoom out or reset and redraw will restore the original display.
+
+If the graphics frame is a typical vector plot with drawn and labeled
+axes, magnifying a portion of the plot may cause the axes to be lost.
+If this is not what is desired a keystroke is provided to draw and label
+the axes of the displayed window. The axes will be overplotted on the
+current display and will not be saved in the frame buffer, hence they
+will be lost when the frame is redrawn. In cursor mode the viewport is
+the full display area of the output device, hence the tick mark labels
+of the drawn axes will be drawn inside the viewport. This form of axes
+labeling is used because it is simple and because it is appropriate for
+both vector graphics and image display output devices (and cursor mode
+must serve both).
+
+The cursor mode keystrokes are all upper case letters, reserving lower case
+for applications programs. The terminal shift lock key may be used to
+minimize typing. The recognized cursor mode keystrokes are shown below.
+
+
+.ks
+.nf
+(*X* means not yet implemented)
+
+ ? print list of keystrokes
+ *A* draw and label the axes of current viewport
+ C print the cursor position as it moves
+ *D* draw a line by marking the endpoints
+ E expand plot by setting window corners
+ F set fast cursor (for HJKL)
+ H step cursor left
+ J step cursor down
+ K step cursor up
+ L step cursor right
+ M move point under cursor to center of screen
+ P zoom out (restore previous expansion)
+ *S* select WCS at current position of cursor
+ *T* draw a text string
+ *U* undo (delete) the last instruction in the frame buffer
+ V set slow cursor (for HJKL)
+ X zoom in, X only
+ Y zoom in, Y only
+ Z zoom in, both X and Y
+ < set lower limit of plot to the cursor y value
+ > set upper limit of plot to the cursor y value
+ *\* escape next character
+ : set cursor mode options
+ :! send a command to the host system
+ 0 reset and redraw
+ 1-9 roam
+
+.fi
+.ce
+Figure 2. Cursor Mode Keystrokes
+.ke
+
+
+The numeric keypad of the terminal (if it has one) is used for directional
+roaming. The directional significance of the numeric keys for roaming
+is obvious if the terminal has a keypad, and is illustrated below.
+
+
+.ks
+.nf
+ 7 8 9 135 090 045
+
+ 4 5 6 180 000 000
+
+ 1 2 3 225 -90 -45
+.fi
+.ke
+
+
+If the character : is typed while in cursor mode the alpha cursor will appear
+at the bottom of the screen, allowing a command line to be entered. If the
+command \fIbegins with a period it is interpreted as a cursor mode command\fR,
+otherwise the command is passed as a string to the applications program.
+Multiple commands may be entered on a line delimited by semicolons.
+The command set currently recognized is shown below. Minimum match
+abbreviations are permitted.
+
+.ls 4
+.ls 15 help
+Print a list of the cursor mode commands.
+.le
+.ls case[+-]
+Ignore case when interpreting keystrokes. If this option is selected the cursor
+mode keystrokes may conflict with those of the applications program.
+.le
+.ls clear
+Clear the alpha screen (but not the graphics screen). This is done by writing
+sufficient blank lines to scroll any text off the screen. Does not work if
+terminal has only one memory.
+.le
+.ls markcur[+-]
+Draw a small graphics mark at the position of the cursor whenever the cursor
+is read, i.e., when cursor mode exits. The default is to not mark.
+.le
+.ls off [keys]
+Disable all cursor mode keystrokes except : (colon). If followed by a list
+of keys, e.g., ":.off 0-9IC", only the listed keys are disabled.
+.le
+.ls on [keys]
+Renable all cursor mode keystrokes, or just the listed keystrokes.
+.le
+.ls page[+-]
+Clear the screen when large blocks of text are to be printed, e.g., for '?',
+show, and so on. If paging is disabled the text will overwrite the graphics
+display.
+.le
+.ls read <file>
+Load the graphics frame from the named metafile.
+The current graphics frame is discarded.
+.le
+.ls reset
+Disconnect any connected graphics kernels and free all file descriptors and
+memory used by the graphics system. Exit cursor mode.
+.le
+.ls show
+Print the values of all cursor mode parameters, show the status of any
+connected graphics kernels, summarize memory utilization, etc.
+.le
+.ls snap [device]
+Dispose of the graphics frame to the standard plotter or to the named device.
+A magnified graph will be plotted as it appears on the screen.
+.le
+.ls txset [size] [up]
+Set the text drawing parameters (character size and character up vector).
+For example, ".tx 2 180" would set the character size to 2.0 and character
+up to 180 degrees for a vertical string drawn upwards.
+.le
+.ls write <file>
+Save the graphics frame in (or append to) the named metafile.
+If an exclamation is appended to the command (e.g., "w! file") the output
+file, if any, will be overwritten. If a plus sign is appended the entire
+frame will be saved regardless of any plot expansion.
+.le
+.ls xres=N
+Set the (soft) device resolution in X. A decrease in resolution will generally
+yield an increase in plotting speed. Only plots generated on the graphics
+terminal are affected.
+.le
+.ls yres=N
+Set the (soft) device resolution in Y.
+.le
+.ls zero
+Equivalent to the numeric key 0, i.e., restore the unitary workstation
+transformation and redraw the screen.
+.le
+.le
+
+
+For example, to set the X and Y resolutions to 250 and 100, respectively,
+one could enter the following command (the computer will type the ':' at
+the bottom of the screen when the ':' key is pressed):
+
+ :.xres=250;yres=100
+
+Cursor mode may be initialized at login time by supplying a CL environment
+variable named "cminit". For example,
+
+ cl> set cminit = off
+
+would disable cursor mode, and
+
+ cl> set cminit = "mark;case-;xres=100;yres=50"
+
+would enable marking, turn off case sensitivity, and set the plotting
+resolution to 100x50. Initialization is performed only once, when cursor
+mode is first entered.
+.sh
diff --git a/sys/gio/cursor/doc/giotr.notes b/sys/gio/cursor/doc/giotr.notes
new file mode 100644
index 00000000..a9221445
--- /dev/null
+++ b/sys/gio/cursor/doc/giotr.notes
@@ -0,0 +1,330 @@
+.help GIO Feb85 "Graphics I/O"
+.nh
+Graphics I/O Dataflow
+
+ The GIO procedures are resident in an external applications task which
+does graphics. GIO writes a GKI instruction stream which, if not sent directly
+to a metafile, is sent to one of the standard graphics streams STDGRAPH,
+STDIMAGE, or STDPLOT, much as output is sent to STDOUT or STDERR.
+The procedure \fBprfilbuf\fR (directory etc$), which reads the command
+stream from a subprocess, is resident in the CL and executes all pseudofile
+i/o instructions from a subprocess. Note that \fBprfilbuf\fR is part of the
+i/o system of IRAF and operates transparently to the CL.
+
+
+.ks
+.nf
+ GIO(task) ---ipc--> PRFILBUF(CL) --> file (or pipe)
+ |
+ v external
+ GIOTR ---ipc--> graphics
+ | kernel
+ v
+ stdgraph kernel
+ |
+ v
+ (zfioty)
+ graphics terminal
+
+
+ task | cl | task
+.fi
+
+.ce
+Graphics Output Dataflow
+.ke
+
+
+The \fBprfilbuf\fR procedure passes record read or write requests for the
+pseudofiles STDIN, STDOUT or STDERR on to file descriptors assigned by the
+CL with the \fBprredir\fR procedure at task execution time. The sole function
+of the CL in graphics i/o is to control the redirection of the graphics
+i/o streams with \fBprredir\fR. The CL may redirect any of the graphics
+streams, i.e., the user may redirect any graphics stream on the command line
+when a command is entered, but by default output is directed to a filter
+resident in the CL process. This filter is a procedure named \fBgiotr\fR.
+
+ giotr (stream, buffer, nchars)
+
+The primary function of GIOTR is to pass metacode instructions on to a kernel.
+The instruction stream is scanned and special actions are taken for some of
+the GKI control instructions. In particular, GIOTR must spawn graphics kernel
+subprocesses upon demand. GIOTR is also capabable of performing an
+additional transformation upon the drawing instructions before they are passed
+to the kernel. This transformation, known as the \fBworkstation
+transformation\fR, maps a rectangular portion of the NDC space into the full
+device screen, clipping at the boundary of the viewport into NDC space.
+The workstation transformation provides a zoom and pan capability and is
+controlled interactively by the user in \fBcursor mode\fR (section 3.3).
+
+As noted earlier, the \fBstdgraph kernel\fR ("fast" kernel) is resident in
+the CL process. This is necessary for efficiency reasons and is desirable
+in any case because the CL process owns the graphics device, i.e., the
+graphics terminal. All devices except the user's graphics terminal are
+controlled by external graphics kernel processes. The STDGRAPH kernel is
+itself available as an external process and may be called as such to drive
+a graphics terminal other than the user terminal (or even to drive the user
+terminal if one is willing to shuffle output back through IPC). A graphics
+kernel may support an arbitrary number of devices, and may write to more
+than one device simultaneously. In addition to being called by GIOTR,
+a graphics kernel may be called directly as a CL task to process metacode from
+either a file or the standard input, e.g., from a pipe. This offers
+additional flexibility as the CL parameter mechanism may then be used to
+gain control over metacode translation.
+
+.nh 2
+Graphics Stream I/O
+
+ The functions performed by GIOTR are summarized in pseudocode below.
+GIOTR maintains a separate descriptor for each of the three graphics streams
+and is capable of servicing intermixed i/o requests for all streams
+simultaneously. The information stored in the descriptor
+includes the workstation name, process information, WCS storage for
+the SETWCS and GETWCS instructions, the workstation transformation,
+and the frame buffer, used to spool GKI instructions for cursor mode.
+
+
+.tp 6
+.nf
+procedure giotr (fd, buffer, nchars)
+
+fd graphics stream (STDGRAPH, etc.)
+buffer[] buffer containing GKI metacode instructions
+nchars number of chars to be read or written
+
+begin
+ # Note that a GKI instruction may span a buffer boundary.
+ # The code which gets the next instruction from the buffer
+ # must always return a full instruction, hence some local
+ # buffering is required therein to reconstruct instructions.
+
+ while (get next instruction != buffer empty) {
+
+ # Handle special instructions.
+ switch (instruction) {
+
+ case GKI_OPENWS:
+ if (device not already open) {
+ read graphcap entry for device
+ get process name from graphcap entry
+ if (process not already connected) {
+ if (some other process is connected)
+ disconnect current kernel process
+ connect new kernel process
+ }
+ }
+ output instruction
+ flush output
+ clear frame buffer
+
+ case GKI_CLOSEWS, GKI_FLUSH:
+ output instruction
+ flush output
+
+ case GKI_CANCEL:
+ output instruction
+ flush output
+ clear frame buffer
+
+ case GKI_SETWCS:
+ save WCS in descriptor
+
+ case GKI_GETWCS:
+ write saved WCS to fd
+ flush (fd)
+
+ default:
+ append unmodified instruction to frame buffer
+ perform workstation transformation upon instruction
+ output transformed instruction
+ }
+ }
+end
+.fi
+
+
+The action implied by "output instruction" above is the following:
+
+
+.ks
+.nf
+ if (kernel is resident in this process)
+ call gki_execute to execute the instruction
+ else
+ call write (process, instruction, nchars)
+.fi
+.ke
+
+
+The frame buffer (required for cursor mode) will be dynamically allocated and
+will be no larger than it has to be, but will have a fixed (user defined)
+upper limit, e.g., 128Kb. The median size for a plot is typically 5-10Kb.
+Instructions will be silently discarded if the buffer grows too large.
+Buffering can be turned off completely if desired, and will always be turned
+off for STDPLOT.
+
+.nh 2
+Cursor Mode Details
+
+ Most of the functionality required to implement cursor mode is provided
+by GIOTR. The primary functions of the cursor mode code are to read the
+cursor and keystroke, modify the workstation transformation, and redraw the
+contents of the frame buffer subject to the new workstation transformation.
+Cursor mode does not modify the contents of the frame buffer, except for
+possibly appending new graphics instructions to the frame buffer.
+A workstation transformation set with cursor mode remains in effect until
+the frame buffer is cleared, hence any additional graphics output from the
+task which initiated the cursor read (and cursor mode) will undergo the
+workstation transformation when drawn.
+
+
+.nf
+# PR_FILBUF -- Fill FIO buffer from an IPC channel subject to the CL/IPC
+# protocol for multiplexing pseudofile data streams with the command stream.
+# Each process has an associated set of pseudofile streams. Each pseudofile
+# stream is connected to one, and only one, file or pseudofile of another
+# process. I/O requests to XMIT or XFER to an ordinary file are straightforward
+# to satisfy. An i/o request from one pseudofile to another is satisfied
+# by posting the request (pushing it on a stack) and redirecting our input
+# to the process owning the pseudofile being read or written. Pseudofile
+# requests are then processed from the second process until a request is
+# received which satisfies the posted request from the original process.
+# When the original request is satisfied it is popped from the stack and input
+# will again be taken from the original process. Note that we cannot write
+# directly to the output process since that would violate the IPC protocol
+# (the second process may wish to write to its stdout or stderr rather than
+# read, etc.: the process must be allowed to complete the original request
+# itself).
+#
+# Request Packet (pushed onto stack for IPC to IPC i/o).
+#
+# pr process slot number of process placing the request
+# iomode request is a read or a write
+# count number of chars to be transferred
+# ps_server pseudofile number in server process
+# ps_receiver pseudofile number in receiver process
+#
+# The request packet describes a pending pseudofile i/o request. The named
+# pseudofile in the server process is either reading from or writing to the
+# named pseudofile in the receiver process.
+
+int procedure pr_filbuf (fd)
+
+begin
+ input = fd (the IPC input channel of a process)
+
+ repeat {
+ get a line from the input file
+ if (neither XMIT nor XFER directive)
+ if (request pending)
+ error: IPC protocol corrupted
+ else
+ return command
+
+ if (line is an XMIT directive) {
+ if (destination is a file) {
+ # Write from pseudofile to an ordinary file.
+ get data record from input
+ write data record to file
+
+ } else {
+ # Write from pseudofile to another pseudofile.
+ if (XMIT satisfies XFER request on top of stack)
+ get data record from input
+ write record to stacked process
+ restore input to stacked process
+ pop request from stack
+
+ } else {
+ # If writing to local kernel GIOTR will return a null
+ # length record and we are done.
+
+ get data record from input
+ if (writing to a graphics stream)
+ call giotr filter to transform record
+ if (anything left to output) {
+ push request on stack
+ switch input to IPC input of receiver process
+ }
+ }
+ }
+
+ } else if (line is an XFER directive) {
+ if (source is an ordinary file) {
+ # Read from a file.
+ read data record from file
+ write to active process
+
+ } else if (source is another process) {
+ # Read from another pseudofile.
+ if (XFER satisfies XMIT request on top of stack) {
+ read record from stacked process
+ write to active process
+ restore input to stacked process
+ pop request from stack
+ } else {
+ push request on stack
+ switch input to IPC input channel of receiver process
+ }
+ }
+ }
+ }
+end
+
+
+# GIOTR -- Graphics i/o filter.
+
+procedure giotr (fd, buffer, nchars)
+
+fd graphics stream (STDGRAPH, etc.)
+buffer[] buffer containing GKI metacode instructions
+nchars number of chars to be read or written
+
+begin
+ # Note that a GKI instruction may span a buffer boundary.
+ # The code which gets the next instruction from the buffer
+ # must always return a full instruction, hence some local
+ # buffering is required therein to reconstruct instructions.
+
+ while (buffer not empty) {
+
+ # Handle special instructions.
+ switch (next_instruction) {
+
+ case GKI_OPENWS:
+ if (device not already open) {
+ read graphcap entry for device
+ get process name from graphcap entry
+ if (process not already connected) {
+ if (some other process is connected)
+ disconnect current kernel process
+ connect new kernel process
+ }
+ }
+ output instruction
+ flush output
+ clear frame buffer
+
+ case GKI_CLOSEWS, GKI_FLUSH:
+ output instruction
+ flush output
+
+ case GKI_CANCEL:
+ output instruction
+ flush output
+ clear frame buffer
+
+ case GKI_SETWCS:
+ save WCS in descriptor
+
+ case GKI_GETWCS:
+ write saved WCS to fd
+ flush (fd)
+
+ default:
+ append unmodified instruction to frame buffer
+ perform workstation transformation upon instruction
+ output transformed instruction
+ }
+ }
+end
diff --git a/sys/gio/cursor/giotr.x b/sys/gio/cursor/giotr.x
new file mode 100644
index 00000000..cfc8f706
--- /dev/null
+++ b/sys/gio/cursor/giotr.x
@@ -0,0 +1,183 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <xwhen.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GIOTR -- A graphics filter, called by PR_PSIO (during normal graphics output)
+# and RCURSOR (when in cursor mode) to perform the workstation transformation
+# on a block of metacode instructions, writing the individual instructions to
+# either the inline stdgraph kernel or to an external kernel. Input is taken
+# from the frame buffer for the stream. All full instructions starting at the
+# input pointer IP and ending at the output pointer OP are processed, leaving
+# the input pointer positioned to BOI of the last (and incomplete) instruction
+# in the frame buffer. If output is to an inline kernel the kernel is called
+# to execute each instruction as it is extracted. If output is to an external
+# kernel instructions are written to the named stream, i.e., into the FIO
+# buffer associated with the stream, and later transferred to the kernel in the
+# external process when the process requests input from the named stream in an
+# XFER directive (see PR_PSIO).
+
+procedure giotr (stream)
+
+int stream # graphics stream
+
+pointer tr, gki
+int jmpbuf[LEN_JUMPBUF], fn
+int mode, xint, status, junk, nwords
+common /gtrvex/ jmpbuf
+
+pointer gtr_init(), coerce()
+extern giotr_onint(), gtr_delete()
+int gtr_fetch_next_instruction(), locpr()
+errchk gtr_init, gtr_fetch_next_instruction, gki_write
+data status /OK/, xint /NULL/
+include "gtr.com"
+
+begin
+ tr = gtr_init (stream)
+
+ # If an interrupt occurs while GIOTR is executing output is cancelled
+ # and further processing is disabled until the next frame begins.
+
+ if (xint == NULL)
+ call xwhen (X_INT, locpr(giotr_onint), xint)
+
+ call zsvjmp (jmpbuf, status)
+ if (status != OK) {
+ call gki_cancel (stream)
+ call gki_deactivatews (stream, 0)
+ }
+
+ # Fetch, optionally transform, and execute each metacode instruction
+ # in the frame buffer.
+
+ while (gtr_fetch_next_instruction (tr, gki) != EOF) {
+ switch (Mems[gki+GKI_HDR_OPCODE-1]) {
+
+ case GKI_OPENWS:
+ mode = Mems[gki+GKI_OPENWS_M-1]
+ if (mode != APPEND)
+ status = OK
+
+ if (status == OK) {
+ # If the open instruction has already been passed to the
+ # kernel by gtr_control, do not do so again here.
+
+ if (TR_SKIPOPEN(tr) == YES)
+ TR_SKIPOPEN(tr) = NO
+ else
+ call gki_write (stream, Mems[gki])
+
+ # gtr_control does not call gki_escape so always do this.
+ call gki_escape (stream, GKI_OPENWS, 0, 0)
+
+ # Discard frame buffer contents up to and including the
+ # openws instruction, so that it will only be executed
+ # once.
+
+ if (Mems[gki+GKI_OPENWS_M-1] == NEW_FILE)
+ call gtr_frame (tr, TR_IP(tr), stream)
+ }
+
+ case GKI_CLOSEWS, GKI_DEACTIVATEWS, GKI_REACTIVATEWS:
+ # These instructions are passed directly to the kernel via
+ # the PSIOCTRL stream at runtime, but are ignored in metacode
+ # to avoid unnecessary mode switching of the terminal.
+ ;
+
+ case GKI_CANCEL:
+ # Cancel any buffered graphics data.
+ call gki_write (stream, Mems[gki])
+ call gtr_frame (tr, TR_IP(tr), stream)
+
+ case GKI_FLUSH, GKI_GETCURSOR, GKI_GETCELLARRAY:
+ # Do not buffer these instructions.
+ call gki_write (stream, Mems[gki])
+ call gtr_delete (tr, gki)
+
+ case GKI_CLEAR:
+ # Clear is special because it initializes things.
+ if (status != OK) {
+ call gki_reactivatews (stream, 0)
+ status = OK
+ }
+ # Execute the instruction.
+ call gki_write (stream, Mems[gki])
+ call gki_escape (stream, GKI_CLEAR, 0, 0)
+
+ # Discard frame buffer contents up to and including the clear.
+ call gtr_frame (tr, TR_IP(tr), stream)
+
+ case GKI_SETWCS:
+ call gki_write (stream, Mems[gki])
+ nwords = Mems[gki+GKI_SETWCS_N-1]
+ call amovs (Mems[gki+GKI_SETWCS_WCS-1],
+ Mems[coerce (TR_WCSPTR(tr,1), TY_STRUCT, TY_SHORT)],
+ min (nwords, LEN_WCS * MAX_WCS * SZ_STRUCT / SZ_SHORT))
+
+ case GKI_ESCAPE:
+ if (status == OK) {
+ fn = Mems[gki+GKI_ESCAPE_FN-1]
+
+ # Execute the escape instruction.
+ if (wstranset == YES) {
+ call sge_wstran (fn, Mems[gki+GKI_ESCAPE_DC-1],
+ vx1,vy1, vx2,vy2)
+ } else
+ call gki_write (stream, Mems[gki])
+
+ # Allow the kernel escape handling code to preserve,
+ # delete, or edit the instruction.
+
+ call sge_spoolesc (tr, gki, fn, Mems[gki+GKI_ESCAPE_DC-1],
+ TR_FRAMEBUF(tr), TR_OP(tr), locpr(gtr_delete))
+ }
+
+ default:
+ if (status == OK)
+ if (wstranset == YES) {
+ # Perform the workstation transformation and output the
+ # transformed instruction, if there is anything left.
+ call gtr_wstran (Mems[gki])
+ } else
+ call gki_write (stream, Mems[gki])
+ }
+ }
+
+ # Clear the frame buffer if spooling is disabled. This is done by
+ # moving the upper part of the buffer to the beginning of the buffer,
+ # starting with the word pointed to by the second argument, preserving
+ # the partial instruction likely to be found at the end of the buffer.
+ # Truncate the buffer if it grows too large by the same technique of
+ # shifting data backwards, but in this case without destroying all
+ # of the data.
+
+ if (TR_SPOOLDATA(tr) == NO)
+ call gtr_frame (tr, TR_IP(tr), stream)
+ else if (TR_OP(tr) - TR_FRAMEBUF(tr) > TR_MAXLENFRAMEBUF(tr))
+ call gtr_truncate (tr, TR_IP(tr))
+
+ # Pop the interrupt handler.
+ if (xint != NULL) {
+ call xwhen (X_INT, xint, junk)
+ xint = NULL
+ }
+end
+
+
+# GIOTR_ONINT -- Interrupt handler for GIOTR.
+
+procedure giotr_onint (vex, next_handler)
+
+int vex # virtual exception
+int next_handler # next exception handler in chain
+int jmpbuf[LEN_JUMPBUF]
+common /gtrvex/ jmpbuf
+
+begin
+ call xer_reset()
+ call zdojmp (jmpbuf, vex)
+end
diff --git a/sys/gio/cursor/grc.h b/sys/gio/cursor/grc.h
new file mode 100644
index 00000000..35af451f
--- /dev/null
+++ b/sys/gio/cursor/grc.h
@@ -0,0 +1,20 @@
+# GRC.H -- Global definitions and data structures for the RCURSOR (cursor read)
+# procedures.
+
+define KEYSFILE "lib$scr/cursor.key"
+define KEYSTROKES "ABCDEFHJKLMPRTUVWXYZ<>0123456789:="
+define MAX_KEYS 128
+define LEN_RCSTRUCT (10+(128/SZ_STRUCT))
+
+define RC_CASE Memi[$1] # case sensitive
+define RC_MARKCUR Memi[$1+1] # mark cursor
+define RC_PHYSOPEN Memi[$1+2] # physical open by rcursor
+define RC_AXES Memi[$1+3] # draw axes if screen redrawn
+ # (open)
+define RC_KEYS Memc[P2C($1+10)+$2] # keystroke mappings
+
+define LEN_CT 2,4
+define CT_TRAN 1
+define CT_SCALE 2
+define CT_WORIGIN 3
+define CT_MORIGIN 4
diff --git a/sys/gio/cursor/grcaxes.x b/sys/gio/cursor/grcaxes.x
new file mode 100644
index 00000000..f2f69e4f
--- /dev/null
+++ b/sys/gio/cursor/grcaxes.x
@@ -0,0 +1,402 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+define LEN_POLYLINE 128 # polyline for axes and ticks
+define NTICKS 6 # default rough ticks on an axis
+define SZ_TICKFORMAT 6 # "%0.Xg"
+define SZ_TICKLABEL 10 # encoded tick label
+define TICKLEN 0.03 # tick length, ndc units
+define LABELOFFSET 1.5 # offset to tick label in ticklen units
+
+
+# GRC_AXES -- Draw and label the axes of the viewport. This is a simple
+# routine not intended to be competitive with GLABAX. We draw a box around
+# the edge of the screen, find and label the ticks within the plotting area.
+
+procedure grc_axes (stream, sx, sy, raster, rx, ry)
+
+int stream #I graphics stream
+real sx, sy #I screen coords of cursor
+int raster #I raster number
+real rx, ry #I raster coords of cursor
+
+char tickformat[SZ_TICKFORMAT], ticklabel[SZ_TICKLABEL]
+pointer tr, w, ap, save_op
+int xt, yt, nwords, nticks, wcs, lt_save
+real xb, xe, x1, dx, x, y, lw_save
+real yb, ye, y1, dy, aspect_ratio, xticklen, yticklen
+
+int gt_ndigits()
+pointer gtr_init()
+real ttygetr()
+
+errchk gtr_init, ttygetr, realloc, gax_start
+include "gtr.com"
+
+begin
+ tr = gtr_init (stream)
+
+ # Draw the axes with a solid polyline of width 2.0.
+ ap = TR_PLAP(tr)
+ lt_save = PL_LTYPE(ap); PL_LTYPE(ap) = GL_SOLID
+ lw_save = PL_WIDTH(ap); PL_WIDTH(ap) = 2.0
+
+ # Select a WCS.
+ call grc_scrtowcs (stream, sx, sy, raster, rx, ry, x1, y1, wcs)
+ w = TR_WCSPTR(tr,wcs)
+
+ # Get the coordinates of the axes corners and the tick parameters.
+ call gax_findticks (w, xb,xe,yb,ye, x1,dx,xt, y1,dy,yt)
+
+ # Mark the position in the frame buffer. The axes drawing instructions
+ # will be appended to the frame buffer by the drawing routines. When
+ # we get all done we will move these instructions to the scratch buffer
+ # and reset the frame buffer pointers, since we do not want the axes
+ # to be a permanent part of the plot.
+
+ save_op = TR_OP(tr)
+
+ # Compute the X and Y tick lengths in NDC coordinates, corrected for
+ # the aspect ratio and workstation transformation.
+
+ aspect_ratio = ttygetr (TR_TTY(tr), "ar")
+ if (aspect_ratio < .001)
+ aspect_ratio = 1.0
+ xticklen = TICKLEN / xscale * aspect_ratio
+ yticklen = TICKLEN / yscale
+
+ # Construct the polyline to draw the first two axes and ticks. We
+ # start at the lower left and draw to the lower right then upper right.
+
+ nticks = int ((xe - xb) / dx) # Bottom axis.
+ call gax_start (xb, yb)
+ call gax_draw (x1, yb)
+ call gax_tick (0., yticklen)
+
+ for (x=x1+dx; nticks > 0; nticks=nticks-1) {
+ call gax_draw (min(x,xe), yb)
+ call gax_tick (0., yticklen)
+ x = x + dx
+ }
+
+ nticks = int ((ye - yb) / dy) # Right axis.
+ call gax_draw (xe, yb)
+ call gax_draw (xe, y1)
+ call gax_tick (-xticklen, 0.)
+
+ for (y=y1+dy; nticks > 0; nticks=nticks-1) {
+ call gax_draw (xe, min(y,ye))
+ call gax_tick (-xticklen, 0.)
+ y = y + dy
+ }
+
+ call gax_draw (xe, ye)
+ call gax_flush (stream)
+
+ # Construct the polyline to draw the second two axes and ticks. We
+ # start at the lower left and draw to the upper left then upper right.
+
+ nticks = int ((ye - yb) / dy) # Left axis.
+ call gax_start (xb, yb)
+ call gax_draw (xb, y1)
+ call gax_tick (xticklen, 0.)
+
+ for (y=y1+dy; nticks > 0; nticks=nticks-1) {
+ call gax_draw (xb, min(y,ye))
+ call gax_tick (xticklen, 0.)
+ y = y + dy
+ }
+
+ nticks = int ((xe - xb) / dx) # Top axis.
+ call gax_draw (xb, ye)
+ call gax_draw (x1, ye)
+ call gax_tick (0., -yticklen)
+
+ for (x=x1+dx; nticks > 0; nticks=nticks-1) {
+ call gax_draw (min(x,xe), ye)
+ call gax_tick (0., -yticklen)
+ x = x + dx
+ }
+
+ call gax_draw (xe, ye)
+ call gax_flush (stream)
+
+ # Label the ticks on the bottom axis. The tick labels are centered
+ # just above each tick.
+
+ nticks = int ((xe - xb) / dx) + 1
+ call sprintf (tickformat, SZ_TICKFORMAT, "%%0.%dg")
+ call pargi (max (1, gt_ndigits (xb, xe, dx)) + 1)
+
+ for (x=x1; nticks > 0; nticks=nticks-1) {
+ call glb_encode (x, ticklabel, SZ_TICKLABEL, tickformat, dx)
+ call gax_ndc (x, yb, sx, sy)
+ call gax_text (stream, sx, sy + (yticklen * LABELOFFSET),
+ ticklabel, GT_CENTER, GT_BOTTOM)
+ x = x + dx
+ }
+
+ # Label the ticks on the left axis. The tick labels are left justified
+ # just to the right of each tick.
+
+ nticks = int ((ye - yb) / dy) + 1
+ call sprintf (tickformat, SZ_TICKFORMAT, "%%0.%dg")
+ call pargi (max (1, gt_ndigits (yb, ye, dy)) + 1)
+
+ for (y=y1; nticks > 0; nticks=nticks-1) {
+ call glb_encode (y, ticklabel, SZ_TICKLABEL, tickformat, dy)
+ call gax_ndc (xb, y, sx, sy)
+ call gax_text (stream, sx + (xticklen * LABELOFFSET), sy,
+ ticklabel, GT_LEFT, GT_CENTER)
+ y = y + dy
+ }
+
+ # Restore the default polyline attributes.
+ PL_LTYPE(ap) = lt_save
+ PL_WIDTH(ap) = lw_save
+
+ # Move the axes drawing and labelling instructions to the scratch
+ # buffer and fix up the frame buffer pointers.
+
+ nwords = TR_OP(tr) - save_op
+ if (nwords > TR_LENSCRATCHBUF(tr)) {
+ call realloc (TR_SCRATCHBUF(tr), nwords, TY_SHORT)
+ TR_LENSCRATCHBUF(tr) = nwords
+ }
+
+ call amovs (Mems[save_op], Mems[TR_SCRATCHBUF(tr)], nwords)
+ TR_OPSB(tr) = TR_SCRATCHBUF(tr) + nwords
+ TR_OP(tr) = save_op
+ TR_IP(tr) = save_op
+ TR_LASTOP(tr) = save_op
+end
+
+
+# GAX_FINDTICKS -- Get the coordinates of the endpoints of the axes, the first
+# tick on each axis, and the tick spacing on each axis. If log scaling is in
+# use on an axis we shall work in log coordinate units, which are linear.
+
+procedure gax_findticks (w, wx1,wx2,wy1,wy2, x1,dx,xt, y1,dy,yt)
+
+pointer w # window descriptor
+real wx1,wx2,wy1,wy2 # endpoints of axes
+real x1,dx # tick start and spacing in X
+int xt # type of scaling in X
+real y1,dy # tick start and spacing in Y
+int yt # type of scaling in Y
+
+pointer wp
+real ct[LEN_CT]
+common /ftkgcm/ wp, ct
+
+real sx1, sx2, sy1, sy2
+real elogr()
+
+begin
+ wp = w
+
+ # Set up WCS/NDC coordinate transformations.
+ call grc_settran (w, ct)
+
+ # Get NDC coords of the corners of the screen.
+ call grc_scrtondc (0.001, 0.001, sx1, sy1)
+ call grc_scrtondc (0.999, 0.999, sx2, sy2)
+
+ # Move in a bit if the graphics viewport lies within the screen area.
+ # This depends upon the workstation transformation, of course.
+ sx1 = max (WCS_SX1(w), sx1)
+ sx2 = min (WCS_SX2(w), sx2)
+ sy1 = max (WCS_SY1(w), sy1)
+ sy2 = min (WCS_SY2(w), sy2)
+
+ # Compute world coordinates of the viewport (of the axes to be drawn).
+ call grc_ndctowcs (ct, sx1, sy1, wx1, wy1)
+ call grc_ndctowcs (ct, sx2, sy2, wx2, wy2)
+
+ # Find the ticks. If log scaling is in use on an axis we shall find
+ # and draw the ticks in log coordinates.
+
+ switch (WCS_XTRAN(w)) {
+ case GW_LOG:
+ wx1 = log10 (wx1)
+ wx2 = log10 (wx2)
+ case GW_ELOG:
+ wx1 = elogr (wx1)
+ wx2 = elogr (wx2)
+ }
+ call gtickr (wx1, wx2, NTICKS, NO, x1, dx)
+
+ switch (WCS_YTRAN(w)) {
+ case GW_LOG:
+ wy1 = log10 (wy1)
+ wy2 = log10 (wy2)
+ case GW_ELOG:
+ wy1 = elogr (wy1)
+ wy2 = elogr (wy2)
+ }
+ call gtickr (wy1, wy2, NTICKS, NO, y1, dy)
+
+ xt = WCS_XTRAN(w)
+ yt = WCS_YTRAN(w)
+end
+
+
+# GAX_NDC -- Convert a pair of world or log-world coordinates to NDC
+# coordinates. GAX_FINDTICKS must be called first to set up transformation.
+
+procedure gax_ndc (wx, wy, sx, sy)
+
+real wx, wy # world coords (input)
+real sx, sy # ndc coords (output)
+
+pointer wp
+real ct[LEN_CT]
+common /ftkgcm/ wp, ct
+
+real x, y
+real aelogr()
+
+begin
+ # Get X in world coordinates.
+ switch (WCS_XTRAN(wp)) {
+ case GW_LOG:
+ x = 10.0 ** wx
+ case GW_ELOG:
+ x = aelogr (wx)
+ default:
+ x = wx
+ }
+
+ # Get Y in world coordinates.
+ switch (WCS_YTRAN(wp)) {
+ case GW_LOG:
+ y = 10.0 ** wy
+ case GW_ELOG:
+ y = aelogr (wy)
+ default:
+ y = wy
+ }
+
+ # Transform to NDC coordinates and return.
+ call grc_wcstondc (ct, x, y, sx, sy)
+end
+
+
+# GAX_DRAW -- Add a point to the output polyline for an axis. The polyline
+# is built up in NDC coordinates for output to GTR_POLYLINE. In addition to
+# the draw routine, entry points are provided for start, flush, and tick
+# drawing.
+
+procedure gax_draw (wx, wy)
+
+real wx, wy # world or log-world coords to draw to
+real sx, sy
+pointer polyline, op
+common /gaxdcm/ polyline, op
+
+begin
+ # Transform to NDC coords and add the point to the polyline.
+ call gax_ndc (wx, wy, sx, sy)
+ Memr[op] = sx
+ op = op + 1
+ Memr[op] = sy
+ op = op + 1
+end
+
+
+# GAX_TICK -- Draw a tick at the current position. The offsets to draw the
+# tick are given in NDC coordinates.
+
+procedure gax_tick (dx, dy)
+
+real dx, dy # tick offset in NDC coords for gax_tick
+real x, y
+pointer polyline, op
+common /gaxdcm/ polyline, op
+
+begin
+ x = Memr[op-2]
+ y = Memr[op-1]
+
+ Memr[op] = x + dx
+ op = op + 1
+ Memr[op] = y + dy
+ op = op + 1
+
+ Memr[op] = x
+ op = op + 1
+ Memr[op] = y
+ op = op + 1
+end
+
+
+# GAX_START -- Start a new polyline at the indicated point in world coords.
+# The polyline buffer is of a fixed length with no bounds checking.
+
+procedure gax_start (wx, wy)
+
+real wx, wy # world or log-world coords to draw to
+pointer polyline, op
+
+errchk malloc
+common /gaxdcm/ polyline, op
+
+begin
+ call malloc (polyline, LEN_POLYLINE, TY_REAL)
+ op = polyline
+ call gax_draw (wx, wy)
+end
+
+
+# GAX_FLUSH -- Flush the buffered polyline and free space on the heap.
+
+procedure gax_flush (stream)
+
+int stream # graphics stream
+pointer polyline, op
+common /gaxdcm/ polyline, op
+
+begin
+ call grc_polyline (stream, Memr[polyline], (op - polyline) / 2)
+ call mfree (polyline, TY_REAL)
+end
+
+
+# GAX_TEXT -- Draw a text string (tick label) of size 1.0 with the indicated
+# justification.
+
+procedure gax_text (stream, sx, sy, text, hjustify, vjustify)
+
+int stream # graphics stream
+real sx, sy # text coordinates, NDC
+char text[ARB] # text string to be drawn
+int hjustify # horizontal justification
+int vjustify # vertical justification
+
+pointer tr, tx
+int save_tx[LEN_TX]
+errchk gtr_init
+pointer gtr_init()
+
+begin
+ tr = gtr_init (stream)
+ tx = TR_TXAP(tr)
+ call amovi (Memi[tx], save_tx, LEN_TX)
+
+ TX_UP(tx) = 90
+ TX_SIZE(tx) = 1.0
+ TX_PATH(tx) = GT_RIGHT
+ TX_SPACING(tx) = 0
+ TX_HJUSTIFY(tx) = hjustify
+ TX_VJUSTIFY(tx) = vjustify
+ TX_FONT(tx) = GT_BOLD
+ TX_QUALITY(tx) = GT_NORMAL
+ TX_COLOR(tx) = 1
+
+ call grc_text (stream, sx, sy, text)
+ call amovi (save_tx, Memi[tx], LEN_TX)
+end
diff --git a/sys/gio/cursor/grcclose.x b/sys/gio/cursor/grcclose.x
new file mode 100644
index 00000000..304a0904
--- /dev/null
+++ b/sys/gio/cursor/grcclose.x
@@ -0,0 +1,42 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_CLOSE -- Close the workstation (kernel). Called by RCURSOR to close the
+# kernel after a cursor read. Note that a cursor read may occur while the
+# workstation is open, i.e., after gopen but before gclose, or after the
+# workstation has been closed, i.e., after a plotting program terminates.
+# If the workstation was already open (GKI_OPENWS) by the application when
+# the cursor read occurred we must leave things as they were.
+
+procedure grc_close (fd, rc)
+
+int fd # graphics stream
+pointer rc # rcursor descriptor
+
+pointer tr
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (fd)
+
+ # Decrement the logical OPENWS count and issue the actual CLOSEWS
+ # only if the counter goes to zero. If the workstation was open
+ # but deactivated when grc_open() was called (WS_ACTIVE == NO),
+ # restore it to its former (deactivated) state.
+
+ TR_WSOPEN(tr) = TR_WSOPEN(tr) - 1
+ if (TR_WSOPEN(tr) <= 0) {
+ call gki_closews (fd, TR_DEVNAME(tr))
+ TR_WSOPEN(tr) = 0
+ TR_WSACTIVE(tr) = NO
+ } else if (TR_WSACTSAVE(tr) == NO) {
+ call gki_deactivatews (fd, 0)
+ TR_WSACTIVE(tr) = NO
+ }
+
+ call gki_fflush (fd)
+end
diff --git a/sys/gio/cursor/grccmd.x b/sys/gio/cursor/grccmd.x
new file mode 100644
index 00000000..5aca0f84
--- /dev/null
+++ b/sys/gio/cursor/grccmd.x
@@ -0,0 +1,533 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ttyset.h>
+include <ctype.h>
+include <mach.h>
+include <fset.h>
+include <gset.h>
+include <gki.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+define MAX_KWLEN 10
+
+# Assign opcodes to the recognized keywords.
+
+define KW_AXES 1
+define KW_CASE 2
+define KW_CLEAR 3
+define KW_CURSOR 4
+define KW_GFLUSH 5
+define KW_HELP 6
+define KW_INIT 7
+define KW_MARKCUR 8
+define KW_OFF 9
+define KW_ON 10
+define KW_PAGE 11
+define KW_READ 12
+define KW_SHOW 13
+define KW_SNAP 14
+define KW_TXQUALITY 15
+define KW_TXSET 16
+define KW_VIEWPORT 17
+define KW_WRITE 18
+define KW_XRES 19
+define KW_YRES 20
+define KW_ZERO 21
+
+
+# GRC_COMMAND -- Process a ":." cursor mode option string. The RC structure
+# contains the current values of the cursor mode options. Some option strings
+# are commands that do something, others set options, and still others show
+# the status of the program.
+
+int procedure grc_command (rc, stream, sx, sy, raster, rx, ry, opstr)
+
+pointer rc #I rcursor descriptor
+int stream #I graphics stream
+real sx, sy #I screen coords of cursor
+int raster #I raster number
+real rx, ry #I raster coords of cursor
+char opstr[ARB] #I options string excluding the leading ":.".
+
+pointer tr, p_tr, sp, fname, lbuf, tty
+bool clobber, fullframe, auto_gflush
+int ip, op, ch, opcode, cursor
+int save1, save2, i, xres, yres, quality
+char kwname[MAX_KWLEN]
+
+pointer gtr_init(), grc_open(), ttyodes()
+int strdic(), grc_boolval(), ttygeti(), ttystati()
+real grc_realval()
+string keywords "|axes|case|clear|cursor|gflush|help|init|markcur|off|on|page|\
+read|show|snap|txquality|txset|viewport|write|xres|yres|zero|"
+errchk gtr_redraw, gki_flush, gtr_init
+define exit_ 91
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ # The terminal is left in graphics mode when the user types return to
+ # enter the command. Echo the user command to the terminal without
+ # the newline to leave the terminal in status line mode, so that any
+ # output directly to the terminal from the lower level code in the CL
+ # goes into the status line.
+
+ call strcpy (":.", Memc[lbuf], SZ_LINE)
+ op = lbuf + 2
+ for (ip=1; opstr[ip] != EOS && opstr[ip] != '\n'; ip=ip+1) {
+ Memc[op] = opstr[ip]
+ op = op + 1
+ }
+ Memc[op] = EOS
+ call stg_putline (STDERR, Memc[lbuf])
+
+ tr = gtr_init (stream)
+ ip = 1
+
+ while (ip == 1 || opstr[ip] != EOS) {
+ while (IS_WHITE(opstr[ip]))
+ ip = ip + 1
+
+ # If EOS and not first command, all done. If first command do
+ # not quit, rather assume ":.help" (see below).
+
+ if (ip > 1 && opstr[ip] == EOS)
+ break
+
+ # Extract the keyword into the KWNAME buffer. Leave the input
+ # pointer positioned to the first char following the keyword.
+
+ for (op=1; opstr[ip] != EOS; ip=ip+1) {
+ ch = opstr[ip]
+ if (IS_ALNUM(ch)) {
+ kwname[op] = ch
+ op = op + 1
+ } else
+ break
+ }
+ kwname[op] = EOS
+
+ # Look up the keyword in the dictionary. If not found ring the bell
+ # but do not return EOF (do not quit cursor mode).
+
+ if (op == 1)
+ opcode = KW_HELP
+ else {
+ opcode = strdic (kwname, kwname, MAX_KWLEN, keywords)
+ if (opcode <= 0) {
+ call fprintf (STDERR, "\7")
+ goto exit_
+ }
+ }
+
+ # Process the command.
+
+ switch (opcode) {
+ case KW_AXES:
+ # Set flag to draw axes of viewport when screen readrawn.
+ RC_AXES(rc) = grc_boolval (opstr, ip)
+
+ case KW_CASE:
+ # Enable/disable case sensitivity.
+ RC_CASE(rc) = grc_boolval (opstr, ip)
+
+ case KW_CLEAR:
+ # Clear the alpha screen.
+ iferr (tty = ttyodes ("terminal"))
+ call grc_warn (STDERR)
+ else {
+ do i = 1, ttystati (tty, TTY_NLINES) {
+ call ttygoto (STDOUT, tty, 1, i)
+ call ttyclearln (STDOUT, tty)
+ }
+ call flush (STDOUT)
+ call ttycdes (tty)
+ }
+
+ case KW_CURSOR:
+ # Select the cursor to be referenced in all subsequent reads
+ # and writes.
+
+ ip = ip + 1
+ cursor = max (0, nint (grc_realval (opstr, ip)))
+ call stg_lockcursor (cursor)
+
+ case KW_GFLUSH:
+ # Flush any buffered graphics output (dispose of spooled
+ # plotter output).
+
+ call stg_putline (STDERR, " - ")
+ call gtr_gflush (STDPLOT)
+
+ case KW_HELP:
+ # Print help text for cursor mode.
+ call gtr_page (STDERR, stream)
+ iferr (call pagefile (KEYSFILE, "cursor mode help"))
+ call grc_warn (STDERR)
+ ip = ip + 1
+
+ case KW_INIT:
+ # Disconnect all kernels and free memory. Exits cursor mode
+ # with an EOF.
+
+ call stg_putline (STDERR, " - ")
+ call gtr_reset (OK)
+ call sfree (sp)
+ return (EOF)
+
+ case KW_MARKCUR:
+ # Enable marking of the cursor position when the cursor is read.
+ RC_MARKCUR(rc) = grc_boolval (opstr, ip)
+
+ case KW_OFF:
+ # Disable the listed keys.
+ call grc_keys (rc, opstr, ip, 0)
+
+ case KW_ON:
+ # Enable or set the listed keys.
+ call grc_keys (rc, opstr, ip, 1)
+
+ case KW_PAGE:
+ # Enable screen clear when ?, show, etc. print text.
+ TR_PAGE(tr) = grc_boolval (opstr, ip)
+
+ case KW_READ:
+ # Fill the frame buffer from a metacode spool file.
+
+ call grc_word (opstr, ip, Memc[fname], SZ_FNAME)
+ call grc_read (tr, stream, Memc[fname])
+
+ case KW_SHOW:
+ # Show status of RCURSOR and GIOTR.
+
+ call gtr_page (STDERR, stream)
+ call fprintf (STDERR, "Cursor Mode Parameters:\n\n")
+ call grc_status (STDERR, rc)
+
+ call fprintf (STDERR, "\n\nGraphics Kernel Status:\n\n")
+ call gtr_status (STDERR)
+
+ case KW_SNAP:
+ # Write a snapshot of the screen to a plotter. Open a subkernel
+ # on STDPLOT, redraw the screen into the STDPLOT fio buffer,
+ # flush the buffered metacode to the kernel, then restore
+ # everything. NOTE: should restore things automatically if an
+ # interrupt occurs.
+
+ call stg_putline (STDERR, " - ")
+ call grc_word (opstr, ip, Memc[fname], SZ_FNAME)
+ iferr (p_tr = grc_open (Memc[fname], NEW_FILE, STDPLOT, rc)) {
+ call grc_warn (STDERR)
+ goto exit_
+ }
+
+ call gki_redir (stream, STDPLOT, save1, save2)
+ call fseti (STDPLOT, F_CANCEL, OK)
+
+ iferr {
+ call gtr_redraw (stream)
+ call gki_flush (STDPLOT)
+ } then
+ call grc_warn (STDERR)
+
+ call gki_redir (stream, 0, save1, save2)
+
+ auto_gflush = (ttygeti (TR_TTY(p_tr), "MF") <= 1)
+ call grc_close (STDPLOT, rc)
+
+ if (auto_gflush)
+ call gtr_gflush (STDPLOT)
+
+ call stg_putline (STDERR, " done")
+
+ case KW_VIEWPORT:
+ # Set the viewport in world coordinates.
+ call grc_viewport (tr, stream,
+ sx, sy, raster, rx, ry, opstr, ip)
+
+ case KW_WRITE:
+ # Save the contents of the frame buffer in a file.
+ # "w!" clobbers any existing file and "w+" writes the
+ # full frame. By default the frame is appended to the
+ # output file.
+
+ if (opstr[ip] == '!') {
+ clobber = true
+ ip = ip + 1
+ } else
+ clobber = false
+
+ if (opstr[ip] == '+') {
+ fullframe = true
+ ip = ip + 1
+ } else
+ fullframe = false
+
+ # Extract the filename.
+ call grc_word (opstr, ip, Memc[fname], SZ_FNAME)
+
+ # Write to the spoolfile.
+ call grc_write (tr, stream, Memc[fname], clobber, fullframe)
+
+ case KW_XRES:
+ # Set the stdgraph X resolution.
+ xres = nint (grc_realval (opstr, ip))
+ yres = 0
+ call stg_resolution (xres, yres)
+
+ case KW_YRES:
+ # Set the stdgraph Y resolution.
+ xres = 0
+ yres = nint (grc_realval (opstr, ip))
+ call stg_resolution (xres, yres)
+
+ case KW_TXQUALITY:
+ # Set character generator quality.
+
+ while (IS_WHITE(opstr[ip]))
+ ip = ip + 1
+
+ switch (opstr[ip]) {
+ case 'l':
+ quality = GT_LOW
+ case 'm':
+ quality = GT_MEDIUM
+ case 'h':
+ quality = GT_HIGH
+ default:
+ quality = 0
+ }
+ call stg_txquality (quality)
+
+ case KW_TXSET:
+ # Set the text drawing attributes.
+ call gtxset (TR_TXAP(tr), opstr, ip)
+
+ case KW_ZERO:
+ # Reset and redraw.
+ call gtr_ptran (stream, 0., 1., 0., 1.)
+ call gtr_writecursor (stream, .5, .5)
+ call gtr_redraw (stream)
+ }
+
+ # Advance to the next statement or the end of string. Any unused
+ # characters in the statement just processed are discarded.
+
+ while (opstr[ip] != ';' && opstr[ip] != EOS)
+ ip = ip + 1
+ while (opstr[ip] == ';' || opstr[ip] == '.')
+ ip = ip + 1
+ }
+exit_
+ # Restore the terminal to graphics mode if gtr_page was not called to
+ # deactivate the ws. (this leaves the waitpage flag set).
+
+ if (TR_WAITPAGE(tr) == NO)
+ call stg_putline (STDERR, "\n")
+
+ # Leave the graphics descriptor set up as we found it.
+ tr = gtr_init (stream)
+
+ call flush (STDERR)
+ call sfree (sp)
+ return (OK)
+end
+
+
+# GRC_WORD -- Extract the next whitespace delimited word from the command line.
+
+procedure grc_word (opstr, ip, outstr, maxch)
+
+char opstr[ARB] # input string
+int ip # pointer into input string
+char outstr[ARB] # output string
+int maxch # max chars out
+int op
+
+begin
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+
+ op = 1
+ while (!IS_WHITE (opstr[ip]) && opstr[ip] != EOS) {
+ outstr[op] = opstr[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+
+ outstr[op] = EOS
+end
+
+
+# GRC_BOOL -- Get the boolean value of a parameter. Upon entry, the input
+# pointer is positioned to the first character following the parameter name.
+
+int procedure grc_boolval (opstr, ip)
+
+char opstr[ARB] # command string
+int ip # input pointer
+int value
+int btoi()
+
+begin
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+
+ if (opstr[ip] == '=') {
+ ip = ip + 1
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+ value = btoi (opstr[ip] != 'n' && opstr[ip] != 'N')
+ while (IS_ALPHA (opstr[ip]))
+ ip = ip + 1
+ } else
+ value = btoi (opstr[ip] != '-')
+
+ return (value)
+end
+
+
+# GRC_REALVAL -- Get the real value of a parameter. Upon entry, the input
+# pointer is positioned to the first character following the parameter name.
+# Zero is returned if no value is given.
+
+real procedure grc_realval (opstr, ip)
+
+char opstr[ARB] # command string
+int ip # input pointer
+real value
+int ctor()
+
+begin
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+ if (opstr[ip] == '=')
+ ip = ip + 1
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+
+ if (ctor (opstr, ip, value) <= 0)
+ value = 0
+
+ return (value)
+end
+
+
+# GRC_KEYS -- Enable the listed keys or ranges of keys. The operation is
+# additive, i.e., only the named keys are affected.
+
+procedure grc_keys (rc, opstr, ip, onoff)
+
+pointer rc # rcursor descriptor
+char opstr[ARB] # command string
+int ip # next char in opstr
+int onoff # set keys on (1) or off (0)
+
+int new_value
+int ch, ch1, ch2, ip_start, i
+string keys KEYSTROKES
+
+begin
+ while (IS_WHITE (opstr[ip]))
+ ip = ip + 1
+
+ ip_start = ip
+ for (ch=opstr[ip]; ch != EOS; ch=opstr[ip]) {
+ if (ch == ';' || ch == '\n' || IS_WHITE(ch))
+ break
+
+ ch1 = ch
+ if (opstr[ip+1] == '-' && opstr[ip+2] != EOS) {
+ # Enable a range of keys.
+ ip = ip + 2
+ ch2 = opstr[ip]
+ } else if (opstr[ip+1] == '=' && opstr[ip+2] != EOS) {
+ # Assign the value of a key.
+ ip = ip + 3
+ RC_KEYS(rc,ch) = opstr[ip]
+ next
+ } else
+ ch2 = ch
+
+ for (ch=ch1; ch <= ch2; ch=ch+1) {
+ if (onoff == 0)
+ new_value = 0
+ else
+ new_value = ch
+ RC_KEYS(rc,ch) = new_value
+ }
+
+ ip = ip + 1
+ }
+
+ # If no keys were listed, set all cursor mode keys.
+ if (ip == ip_start)
+ for (i=1; keys[i] != EOS; i=i+1) {
+ ch = keys[i]
+ if (onoff == 0)
+ new_value = 0
+ else
+ new_value = ch
+ RC_KEYS(rc,ch) = new_value
+ }
+
+ # The ":" key cannot be mapped or disabled.
+ RC_KEYS(rc,':') = ':'
+end
+
+
+# GRC_VIEWPORT -- Set the viewport in world coordinates. Use the current
+# cursor position to determine the WCS, then convert the world coordinates
+# of the viewport given by the user into NDC coordinates and set the work-
+# station transformation.
+
+procedure grc_viewport (tr, stream, sx, sy, raster, rx, ry, opstr, ip)
+
+pointer tr #I giotr descriptor
+int stream #I graphics stream
+real sx, sy #I screen coordinates of cursor
+int raster #I raster number
+real rx, ry #I raster coordinates of cursor
+char opstr[ARB] #I command string
+int ip #I input pointer
+
+pointer w
+int i, wcs
+real wx, wy, value
+real vn[4], vw[4], ct[LEN_CT]
+int ctor()
+
+begin
+ # Select a WCS. We are not otherwise interested in the cursor value.
+ call grc_scrtowcs (stream, sx, sy, raster, rx, ry, wx, wy, wcs)
+ w = TR_WCSPTR(tr,wcs)
+ call grc_settran (w, ct)
+
+ # Start with the current viewport.
+ call gtr_gtran (stream, vn[1], vn[2], vn[3], vn[4])
+
+ # Transform to world coordinates.
+ call grc_ndctowcs (ct, vn[1], vn[3], vw[1], vw[3])
+ call grc_ndctowcs (ct, vn[2], vn[4], vw[2], vw[4])
+
+ # Get the new viewport (world) coordinates.
+ do i = 1, 4
+ if (ctor (opstr, ip, value) <= 0)
+ break
+ else
+ vw[i] = value
+
+ # Transform to NDC coordinates.
+ call grc_wcstondc (ct, vw[1], vw[3], vn[1], vn[3])
+ call grc_wcstondc (ct, vw[2], vw[4], vn[2], vn[4])
+
+ # Set the new workstation transformation.
+ call gtr_ptran (stream, vn[1], vn[2], vn[3], vn[4])
+
+ # Redraw the screen.
+ call gtr_redraw (stream)
+end
diff --git a/sys/gio/cursor/grcinit.x b/sys/gio/cursor/grcinit.x
new file mode 100644
index 00000000..3160203c
--- /dev/null
+++ b/sys/gio/cursor/grcinit.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include "grc.h"
+
+# GRC_INIT -- Initialize the rcursor descriptor. Allocate storage for the
+# descriptor and initialize all variables and the keystroke mapping.
+
+procedure grc_init (rc)
+
+pointer rc #U grc descriptor (pointer)
+
+int ip, ch
+string keys KEYSTROKES
+errchk malloc
+
+begin
+ if (rc == NULL)
+ call malloc (rc, LEN_RCSTRUCT, TY_STRUCT)
+ call aclri (Memi[rc], LEN_RCSTRUCT)
+
+ # Initialize variables.
+ RC_CASE(rc) = YES
+ RC_MARKCUR(rc) = NO
+ RC_PHYSOPEN(rc) = NO
+
+ # Initialize keystrokes.
+ for (ip=1; keys[ip] != EOS; ip=ip+1) {
+ ch = keys[ip]
+ RC_KEYS(rc,keys[ip]) = ch
+ }
+end
diff --git a/sys/gio/cursor/grcopen.x b/sys/gio/cursor/grcopen.x
new file mode 100644
index 00000000..8a39d191
--- /dev/null
+++ b/sys/gio/cursor/grcopen.x
@@ -0,0 +1,105 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_OPEN -- Open the workstation. Most commonly used to reopen the
+# workstation for a cursor read after plotting.
+
+pointer procedure grc_open (device, mode, stream, rc)
+
+char device[ARB] # device name (optional)
+int mode # desired access mode
+int stream # graphics stream
+pointer rc # rcursor descriptor
+
+pointer sp, devname, envvar, tr
+int envgets()
+bool streq()
+pointer gtr_init()
+
+include "gtr.com"
+string stdgraph "stdgraph"
+string stdimage "stdimage"
+string stdplot "stdplot"
+errchk syserrs, gtr_openws, gki_openws, gtr_init
+
+begin
+ call smark (sp)
+ call salloc (envvar, SZ_FNAME, TY_CHAR)
+ call salloc (devname, SZ_FNAME, TY_CHAR)
+
+ tr = gtr_init (stream)
+
+ # If the workstation is already connected and the kernel is open
+ # issue the openws directive if it has not already been issued.
+
+ if (TR_DEVNAME(tr) != EOS)
+ if (device[1] == EOS || streq (device, TR_DEVNAME(tr))) {
+ # Kernel is already physically open on this stream. Activate
+ # it if necessary; record whether or not is was active when
+ # we were called, so that we can restore the original state
+ # when grc_close() is called.
+
+ if (TR_WSOPEN(tr) <= 0) {
+ call gki_openws (stream, TR_DEVNAME(tr), mode)
+ TR_WSACTIVE(tr) = YES
+ TR_WSACTSAVE(tr) = NO
+ } else {
+ TR_WSACTSAVE(tr) = TR_WSACTIVE(tr)
+ call gki_reactivatews (stream, 0)
+ TR_WSACTIVE(tr) = YES
+ }
+
+ call gki_fflush (stream)
+
+ TR_WSOPEN(tr) = TR_WSOPEN(tr) + 1
+ call sfree (sp)
+ return (tr)
+ }
+
+ # If no device name given fetch the device name from the environment.
+
+ if (device[1] == EOS) {
+ switch (stream) {
+ case STDGRAPH:
+ call strcpy (stdgraph, Memc[envvar], SZ_FNAME)
+ case STDIMAGE:
+ call strcpy (stdimage, Memc[envvar], SZ_FNAME)
+ default:
+ call strcpy (stdplot, Memc[envvar], SZ_FNAME)
+ }
+
+ # Convert environment variable name into device name. Indirection
+ # and assumption of the value of "terminal" are allowed.
+
+ repeat {
+ if (envgets (Memc[envvar], Memc[devname], SZ_FNAME) <= 0)
+ call syserrs (SYS_ENVNF, Memc[envvar])
+ if (Memc[devname] == '@') {
+ # Indirection in environment variable name.
+ call strcpy (Memc[devname+1], Memc[envvar], SZ_FNAME)
+ } else if (streq (Memc[devname], "terminal")) {
+ call strcpy (Memc[devname], Memc[envvar], SZ_FNAME)
+ } else
+ break
+ }
+ } else
+ call strcpy (device, Memc[devname], SZ_FNAME)
+
+ # Open the workstation (kernel) on stream FD.
+ call gtr_openws (Memc[devname], mode, stream, NULL)
+
+ TR_WSOPEN(tr) = TR_WSOPEN(tr) + 1
+ TR_WSACTSAVE(tr) = NO
+ TR_WSACTIVE(tr) = YES
+
+ call gki_openws (stream, Memc[devname], mode)
+ call gki_fflush (stream)
+
+ call sfree (sp)
+ return (tr)
+end
diff --git a/sys/gio/cursor/grcpl.x b/sys/gio/cursor/grcpl.x
new file mode 100644
index 00000000..7768bf85
--- /dev/null
+++ b/sys/gio/cursor/grcpl.x
@@ -0,0 +1,69 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gki.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_POLYLINE -- Draw a solid polyline. The instruction is encoded and
+# appended to the frame buffer and GIOTR is called to draw the line,
+# possibly applying the workstation transformation in the process.
+
+procedure grc_polyline (stream, v, npts)
+
+int stream # graphics stream
+real v[ARB] # polyline, NDC units
+int npts # number of points (coord pairs) in polyline
+
+pointer tr, sp, p, pl, op, last_op
+int nwords, fd, save1, save2, i
+int stropen()
+pointer gtr_init(), gtr_writep()
+errchk gtr_init, gtr_writep, gki_redir
+
+begin
+ call smark (sp)
+ call salloc (p, npts * 2, TY_SHORT)
+
+ tr = gtr_init (stream)
+
+ # Transform the type real, NDC polyline to GKI units, type short.
+ do i = 1, npts * 2, 2 {
+ Mems[p+i-1] = v[i ] * GKI_MAXNDC
+ Mems[p+i ] = v[i+1] * GKI_MAXNDC
+ }
+
+ # Allocate space in the frame buffer for the polyline set attribute
+ # and line drawing instructions. Set the last op for undo to undo
+ # the line. This is also set by writep, hence we must wait to set
+ # TR_LASTOP until after the call to writep.
+
+ last_op = TR_OP(tr)
+ nwords = GKI_PLSET_LEN + GKI_POLYLINE_LEN + (npts * 2)
+ op = gtr_writep (stream, nwords)
+ TR_LASTOP(tr) = last_op
+
+ # Open the frame buffer as a file and redirect the graphics stream
+ # output into the buffer.
+
+ fd = stropen (Mems[op], nwords, NEW_FILE)
+ call gki_redir (stream, fd, save1, save2)
+
+ # Output a polyline set attribute instruction to ensure that a solid
+ # line is drawn. Output the polyline.
+
+ pl = TR_PLAP(tr)
+ call gki_plset (stream, pl)
+ call gki_polyline (stream, Mems[p], npts)
+
+ # Restore the normal output for the stream.
+ call gki_redir (stream, 0, save1, save2)
+ call close (fd)
+
+ # Call giotr to send the new instructions off the to the kernel,
+ # optionally applying the workstation transformation in the process.
+
+ call giotr (stream)
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/grcread.x b/sys/gio/cursor/grcread.x
new file mode 100644
index 00000000..ce95fc07
--- /dev/null
+++ b/sys/gio/cursor/grcread.x
@@ -0,0 +1,60 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <gio.h>
+include "gtr.h"
+
+# GRC_READ -- Fill the frame buffer from a metacode spool file and redraw
+# the screen. The contents of the frame buffer are overwritten.
+
+procedure grc_read (tr, stream, fname)
+
+pointer tr # graphics descriptor
+int stream # graphics stream
+char fname[ARB] # metacode file
+
+pointer sp, lbuf, op
+int fd, nchars, filelen
+long fstatl()
+pointer gtr_writep()
+int open(), read()
+errchk read
+define err_ 91
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ iferr (fd = open (fname, READ_ONLY, BINARY_FILE)) {
+ call grc_message (stream, " - cannot open file")
+ call sfree (sp)
+ return
+ }
+
+ filelen = fstatl (fd, F_FILESIZE)
+ call sprintf (Memc[lbuf], SZ_LINE, " - file size %d chars")
+ call pargi (filelen)
+ call grc_message (stream, Memc[lbuf])
+
+ # Discard the current frame.
+ call gtr_frame (tr, TR_FRAMEBUF(tr), stream)
+
+ # Read new frame buffer.
+ nchars = filelen
+ if (nchars <= 0)
+ goto err_
+ op = gtr_writep (stream, nchars)
+ if (read (fd, Mems[op], nchars) < nchars)
+ goto err_
+
+ # Redraw the new frame buffer.
+ call gtr_redraw (stream)
+
+ call close (fd)
+ call sfree (sp)
+ return
+err_
+ call close (fd)
+ call grc_message (stream, " [READ ERROR]")
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/grcredraw.x b/sys/gio/cursor/grcredraw.x
new file mode 100644
index 00000000..4317db96
--- /dev/null
+++ b/sys/gio/cursor/grcredraw.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include "grc.h"
+
+# GRC_REDRAW -- Redraw the screen, and, if the "axes" flag is set, draw the axes
+# of the plot.
+
+procedure grc_redraw (rc, stream, sx, sy, raster, rx, ry)
+
+pointer rc #I rcursor descriptor
+int stream #I graphics stream
+real sx, sy #I screen coords of cursor
+int raster #I raster number
+real rx, ry #I raster coords of cursor
+
+begin
+ call gtr_redraw (stream)
+ if (RC_AXES(rc) == YES)
+ call grc_axes (stream, sx, sy, raster, rx, ry)
+end
diff --git a/sys/gio/cursor/grcscr.x b/sys/gio/cursor/grcscr.x
new file mode 100644
index 00000000..add322b4
--- /dev/null
+++ b/sys/gio/cursor/grcscr.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GRC_SCRTONDC -- Coordinate transformation from screen coordinates to NDC
+# coordinates. Screen coordinates physically address the device screen and
+# range from 0 to 1 in either axis. NDC coordinates also range from 0 to 1
+# in either axis but differ from screen coordinates when the workstation
+# transformation is non unitary. The workstation transformation parameters
+# are cached in the GTR common. We assume that GTR_INIT has already been
+# called to initialize the common for a graphics stream.
+
+procedure grc_scrtondc (sx, sy, mx, my)
+
+real sx, sy # screen coordinates (input)
+real mx, my # NDC coordinates (output)
+include "gtr.com"
+
+begin
+ if (wstranset == YES) {
+ mx = ((sx * GKI_MAXNDC - xorigin) / xscale + mx1) / GKI_MAXNDC
+ my = ((sy * GKI_MAXNDC - yorigin) / yscale + my1) / GKI_MAXNDC
+ } else {
+ mx = sx
+ my = sy
+ }
+end
+
+
+# GRC_NDCTOSCR -- Coordinate transformation from NDC coordinates to screen
+# coordinates.
+
+procedure grc_ndctoscr (mx, my, sx, sy)
+
+real mx, my # NDC coordinates (input)
+real sx, sy # screen coordinates (output)
+include "gtr.com"
+
+begin
+ if (wstranset == YES) {
+ sx = ((mx * GKI_MAXNDC - mx1) * xscale + xorigin) / GKI_MAXNDC
+ sy = ((my * GKI_MAXNDC - my1) * yscale + yorigin) / GKI_MAXNDC
+ } else {
+ sx = mx
+ sy = my
+ }
+end
diff --git a/sys/gio/cursor/grcstatus.x b/sys/gio/cursor/grcstatus.x
new file mode 100644
index 00000000..55f44f18
--- /dev/null
+++ b/sys/gio/cursor/grcstatus.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_STATUS -- Called by ":.show" to print the values of the cursor mode
+# parameters.
+
+procedure grc_status (fd, rc)
+
+int fd # output file
+pointer rc # rcursor descriptor
+
+int ip, ch
+string keys KEYSTROKES
+include "gtr.com"
+
+begin
+ call fprintf (fd, "\tcase\t= %b\n")
+ call pargi (RC_CASE(rc))
+ call fprintf (fd, "\tmarkcur\t= %b\n")
+ call pargi (RC_MARKCUR(rc))
+ call fprintf (fd, "\taxes\t= %b\n")
+ call pargi (RC_AXES(rc))
+
+ if (wstranset == YES) {
+ call fprintf (fd, "\tview\t= %5.3f %5.3f %5.3f %5.3f\n")
+ call pargr (vx1)
+ call pargr (vx2)
+ call pargr (vy1)
+ call pargr (vy2)
+ } else
+ call fprintf (fd, "\tview\t= full screen\n")
+
+ call fprintf (fd, "\tkeys\t= %s\n")
+ call pargstr (keys)
+ call fprintf (fd, "\t\t->")
+
+ for (ip=1; keys[ip] != EOS; ip=ip+1) {
+ ch = RC_KEYS(rc,keys[ip])
+ if (ch != 0)
+ call putci (fd, ch)
+ else
+ call putci (fd, ' ')
+ }
+
+ call fprintf (fd, "\n")
+end
diff --git a/sys/gio/cursor/grctext.x b/sys/gio/cursor/grctext.x
new file mode 100644
index 00000000..5bee9b34
--- /dev/null
+++ b/sys/gio/cursor/grctext.x
@@ -0,0 +1,57 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gki.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_TEXT -- Draw a text string. The instruction is encoded and appended to
+# the frame buffer and GIOTR is called to draw the new instructions.
+
+procedure grc_text (stream, x, y, text)
+
+int stream # graphics stream
+real x, y # NDC coordinates of ll corner of first char
+char text[ARB] # text string
+
+pointer tr, op, last_op
+int fd, save1, save2, nwords
+int stropen(), strlen()
+pointer gtr_init(), gtr_writep()
+errchk gtr_init, stropen, gki_redir
+
+begin
+ tr = gtr_init (stream)
+
+ # Allocate space in the frame buffer for the text set attribute
+ # and text drawing instructions. Set the last op for undo to undo
+ # the line. This is also set by writep, hence we must wait to set
+ # TR_LASTOP until after the call to writep.
+
+ last_op = TR_OP(tr)
+ nwords = GKI_TXSET_LEN + GKI_TEXT_LEN + strlen(text)
+ op = gtr_writep (stream, nwords)
+ TR_LASTOP(tr) = last_op
+
+ # Open the frame buffer as a file and redirect the graphics stream
+ # output into the buffer.
+
+ fd = stropen (Mems[op], nwords, NEW_FILE)
+ call gki_redir (stream, fd, save1, save2)
+
+ # Output the set text attribute instruction and the text drawing
+ # instruction.
+
+ call gki_txset (stream, TR_TXAP(tr))
+ call gki_text (stream, nint(x*GKI_MAXNDC), nint(y*GKI_MAXNDC), text)
+
+ # Restore the normal output for the stream.
+ call gki_redir (stream, 0, save1, save2)
+ call close (fd)
+
+ # Call giotr to send the new instructions off to the kernel, optionally
+ # applying the workstation transformation in the process.
+
+ call giotr (stream)
+end
diff --git a/sys/gio/cursor/grcwarn.x b/sys/gio/cursor/grcwarn.x
new file mode 100644
index 00000000..ba9fcb0e
--- /dev/null
+++ b/sys/gio/cursor/grcwarn.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GRC_WARN -- Called in an error handler to intercept an error message string
+# and write it to the workstation in the status line.
+
+procedure grc_warn (fd)
+
+int fd # output stream
+
+int errcode
+pointer sp, msg, ip
+int errget()
+
+begin
+ call smark (sp)
+ call salloc (msg, SZ_LINE, TY_CHAR)
+
+ errcode = errget (Memc[msg], SZ_LINE)
+ for (ip=msg; Memc[ip] != EOS && Memc[ip] != '\n'; ip=ip+1)
+ ;
+ Memc[ip] = EOS
+
+ call stg_putline (fd, " - ")
+ call stg_putline (fd, Memc[msg])
+
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/grcwcs.x b/sys/gio/cursor/grcwcs.x
new file mode 100644
index 00000000..7c73657a
--- /dev/null
+++ b/sys/gio/cursor/grcwcs.x
@@ -0,0 +1,282 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_SCRTOWCS -- Transform screen coordinates (raw cursor coordinates) to
+# world coordinates. This is not terribly efficient, but it does not matter
+# for cursor mode applications which do not involve many coordinate
+# transformations.
+
+procedure grc_scrtowcs (stream, sx, sy, raster, rx, ry, wx, wy, wcs)
+
+int stream #I graphics stream
+real sx, sy #I screen coordinates
+int raster #I raster number
+real rx, ry #I raster coordinates
+real wx, wy #O world coordinates
+int wcs #O world coordinate system
+
+pointer w, tr
+real mx, my
+real ct[LEN_CT]
+int grc_selectwcs()
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (stream)
+
+ # Convert screen (raster 0) to NDC coordinates, undoing the effects
+ # of the workstation transformation. This is not done for raster
+ # coordinates since these are already raster-normalized coordinates
+ # as returned by the server.
+
+ if (raster == 0)
+ call grc_scrtondc (rx, ry, mx, my)
+ else {
+ mx = rx
+ my = ry
+ }
+
+ # Select a WCS. The TR_WCS variable is set only if the user
+ # explicitly fixes the WCS to override automatic selection. The
+ # best WCS for the raster is used if there is one, otherwise the
+ # best screen WCS is used.
+
+ if (TR_WCS(tr) == NULL) {
+ wcs = grc_selectwcs (tr, raster, mx, my)
+ if (wcs == 0) {
+ call grc_scrtondc (sx, sy, mx, my)
+ wcs = grc_selectwcs (tr, 0, mx, my)
+ }
+ } else
+ wcs = TR_WCS(tr)
+
+ # Set up the coordinate transformation.
+ w = TR_WCSPTR(tr,wcs)
+ call grc_settran (w, ct)
+
+ # Transform NDC coordinates to WCS coordinates.
+ call grc_ndctowcs (ct, mx, my, wx, wy)
+end
+
+
+# GRC_SETTRAN -- Set up the coordinate transformation parameters for a given
+# world coordinate system.
+
+procedure grc_settran (w, ct)
+
+pointer w # window descriptor
+real ct[LEN_CT] # transformation descriptor
+
+real worigin, scale
+real m1, m2, w1, w2
+int transformation, ax
+bool fp_equalr()
+real elogr()
+
+begin
+ # Compute world -> NDC coordinate transformation.
+
+ do ax = 1, 2 {
+ if (ax == 1) {
+ transformation = WCS_XTRAN(w)
+ w1 = WCS_WX1(w)
+ w2 = WCS_WX2(w)
+ m1 = WCS_SX1(w)
+ m2 = WCS_SX2(w)
+ } else {
+ transformation = WCS_YTRAN(w)
+ w1 = WCS_WY1(w)
+ w2 = WCS_WY2(w)
+ m1 = WCS_SY1(w)
+ m2 = WCS_SY2(w)
+ }
+
+ if (transformation == LINEAR) {
+ worigin = w1
+ if (fp_equalr (w1, w2))
+ scale = 1.0
+ else
+ scale = (m2 - m1) / (w2 - w1)
+ } else if (transformation == LOG && w1 > 0 && w2 > 0) {
+ worigin = log10 (w1)
+ if (fp_equalr (log10(w2), worigin))
+ scale = 1.0
+ else
+ scale = (m2 - m1) / (log10(w2) - worigin)
+ } else {
+ worigin = elogr (w1)
+ if (fp_equalr (elogr(w2), worigin))
+ scale = 1.0
+ else
+ scale = (m2 - m1) / (elogr(w2) - worigin)
+ }
+
+ ct[ax,CT_TRAN] = transformation
+ ct[ax,CT_SCALE] = scale
+ ct[ax,CT_WORIGIN] = worigin
+ ct[ax,CT_MORIGIN] = m1
+ }
+end
+
+
+# GRC_WCSTONDC -- Transform world coordinates to NDC coordinates using the
+# computed transformation parameters.
+
+procedure grc_wcstondc (ct, wx, wy, mx, my)
+
+real ct[LEN_CT] # coordinate transformation descriptor
+real wx, wy # world coordinates of point
+real mx, my # ndc coordinates of point
+
+real v
+int transformation, ax
+real elogr()
+
+begin
+ do ax = 1, 2 {
+ transformation = nint (ct[ax,CT_TRAN])
+ if (ax == 1)
+ v = wx
+ else
+ v = wy
+
+ if (transformation == LINEAR)
+ ;
+ else if (transformation == LOG)
+ v = log10 (v)
+ else
+ v = elogr (v)
+
+ v = ((v - ct[ax,CT_WORIGIN]) * ct[ax,CT_SCALE]) + ct[ax,CT_MORIGIN]
+ if (ax == 1)
+ mx = v
+ else
+ my = v
+ }
+end
+
+
+# GRC_NDCTOWCS -- Transform NDC coordinates to world coordinates using the
+# computed transformation parameters.
+
+procedure grc_ndctowcs (ct, mx, my, wx, wy)
+
+real ct[LEN_CT] # coordinate transformation descriptor
+real mx, my # ndc coordinates of point
+real wx, wy # world coordinates of point
+
+real v
+int transformation, ax
+real aelogr()
+
+begin
+ do ax = 1, 2 {
+ transformation = nint (ct[ax,CT_TRAN])
+ if (ax == 1)
+ v = mx
+ else
+ v = my
+
+ v = ((v - ct[ax,CT_MORIGIN]) / ct[ax,CT_SCALE]) + ct[ax,CT_WORIGIN]
+ if (transformation == LINEAR)
+ ;
+ else if (transformation == LOG)
+ v = 10.0 ** v
+ else
+ v = aelogr (v)
+
+ if (ax == 1)
+ wx = v
+ else
+ wy = v
+ }
+end
+
+
+# GRC_SELECTWCS -- Select the WCS nearest to the given position in NDC
+# coordinates. If the point falls within a single WCS then that WCS is
+# selected. If the point falls within multiple WCS then the closest WCS
+# is selected. If multiple (non unitary) WCS are defined at the same
+# distance, e.g., when the WCS share the same viewport, then the highest
+# numbered WCS is selected.
+
+int procedure grc_selectwcs (tr, raster, mx, my)
+
+pointer tr #I GTR descriptor
+int raster #I raster number
+real mx, my #I NDC coordinates of point
+
+pointer w
+int wcs, closest_wcs, flags
+real tol, sx1, sx2, sy1, sy2
+real distance, old_distance, xcen, ycen
+int nin, in[MAX_WCS]
+
+begin
+ nin = 0
+ closest_wcs = 0
+ old_distance = 1.0
+ tol = EPSILON * 10.0
+
+ # Inspect each WCS. All WCS are passed even though only one or two
+ # WCS will be set to nonunitary values for a given plot. Omitting
+ # the unitary WCS, determine the closest WCS and make a list of the
+ # WCS containing the given point.
+
+ do wcs = 1, MAX_WCS {
+ w = TR_WCSPTR(tr,wcs)
+
+ # Cache WCS params in local storage.
+ sx1 = WCS_SX1(w)
+ sx2 = WCS_SX2(w)
+ sy1 = WCS_SY1(w)
+ sy2 = WCS_SY2(w)
+ flags = WCS_FLAGS(w)
+ xcen = (sx1 + sx2) / 2.0
+ ycen = (sy1 + sy2) / 2.0
+
+ # Skip to next WCS if the raster number doesn't match.
+ if (WF_RASTER(flags) != raster)
+ next
+
+ # Skip to next WCS if this one is not defined.
+ if (and (flags, WF_NEWFORMAT) == 0) {
+ # Preserve old semantics if passed old format WCS.
+ if (sx1 == 0 && sx2 == 0 || sy1 == 0 && sy2 == 0)
+ next
+ if (abs ((sx2-sx1) - 1.0) < tol && abs ((sy2-sy1) - 1.0) < tol)
+ next
+ } else if (and (flags, WF_DEFINED) == 0)
+ next
+
+ # Determine closest WCS to point (mx,my).
+ distance = ((mx - xcen) ** 2) + ((my - ycen) ** 2)
+ if (distance <= old_distance) {
+ closest_wcs = wcs
+ old_distance = distance
+ }
+
+ # Check if point is inside this WCS.
+ if (mx >= sx1 && mx <= sx2 && my >= sy1 && my <= sy2) {
+ nin = nin + 1
+ in[nin] = wcs
+ }
+ }
+
+ # If point is inside exactly one non-unitary WCS then select that WCS.
+ if (nin == 1)
+ return (in[1])
+
+ # If point is inside more than one WCS, or if point is not inside any
+ # WCS, select the closest WCS. If multiple WCS are at the same
+ # distance we have already selected the higher numbered WCS due to
+ # the way the distance test is conducted, above.
+
+ return (closest_wcs)
+end
diff --git a/sys/gio/cursor/grcwrite.x b/sys/gio/cursor/grcwrite.x
new file mode 100644
index 00000000..c0a602a9
--- /dev/null
+++ b/sys/gio/cursor/grcwrite.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GRC_WRITE -- Write the contents of the frame buffer to a file, with or
+# without applying the workstation transformation, optionally clobbering
+# any existing file of the same name.
+
+procedure grc_write (tr, stream, fname, clobber, fullframe)
+
+pointer tr # graphics stream descriptor
+int stream # graphics stream
+char fname[ARB] # file name
+bool clobber # clobber existing file
+bool fullframe # write full frame (no workstation transform)
+
+pointer sp, lbuf
+long size1, size2
+int save1, save2, fd, nchars
+long fstatl()
+int open()
+errchk write, gtr_redraw
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ # Delete existing file if clobber requested.
+ if (clobber)
+ iferr (call delete (fname))
+ ;
+
+ # Open metacode spool file for appending.
+ iferr (fd = open (fname, APPEND, BINARY_FILE)) {
+ call grc_message (stream, " - cannot open file for appending")
+ call sfree (sp)
+ return
+ }
+
+ # Write either the full frame or the displayed frame into spool file.
+
+ size1 = fstatl (fd, F_FILESIZE)
+ if (fullframe) {
+ nchars = (TR_OP(tr) - TR_FRAMEBUF(tr)) * SZ_SHORT
+ call write (fd, Mems[TR_FRAMEBUF(tr)], nchars)
+ } else {
+ call gki_redir (stream, fd, save1, save2)
+ call gtr_redraw (stream)
+ call gki_redir (stream, 0, save1, save2)
+ }
+
+ size2 = fstatl (fd, F_FILESIZE)
+ call sprintf (Memc[lbuf], SZ_LINE, " - %d chars %s")
+ call pargi (size2 - size1)
+ if (size1 > 0)
+ call pargstr ("appended")
+ else
+ call pargstr ("")
+ call grc_message (stream, Memc[lbuf])
+
+ call close (fd)
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtr.com b/sys/gio/cursor/gtr.com
new file mode 100644
index 00000000..ae5c3ac6
--- /dev/null
+++ b/sys/gio/cursor/gtr.com
@@ -0,0 +1,25 @@
+# GTR.COM -- Polyline clipping common for the workstation transformation.
+# The length of this common in integer units from startcom to endcom inclusive
+# is a defined parameter in giotr.h. Values within the save area are saved
+# in the TR descriptor for a device and loaded into the common (which serves
+# as a cache) when GIOTR or RCURSOR is called for a device. LENGTH=28
+
+pointer trdes[MAX_PSEUDOFILES] # pointers to giotr descriptors
+int tr_stream # graphics stream currently in the cache
+int startcom # dummy entry marking start of common
+int pl_op # index of next cell in polyline array
+bool last_point_inbounds # last point was inbounds
+int pl_type # type of instruction (polyline, polymarker,...)
+int wstranset # workstation transformation has been set
+real xscale, yscale # scale factor, world to GKI, for transform
+real xorigin, yorigin # origins in GKI coords, for transform
+long cx, cy # current pen position, GKI coords
+long mx1, mx2, my1, my2 # clipping viewport, GKI coords
+real vx1, vx2, vy1, vy2 # NDC viewport, may extend beyond boundary
+long xs[4], ys[4] # last point plotted (for clipping code)
+int endcom # dummy entry marking end of saved area
+short pl[LEN_PLBUF+5] # output polyline buffer (plus GKI header)
+
+common /gtrcom/ trdes, tr_stream, startcom, pl_op, last_point_inbounds,
+ pl_type, wstranset, xscale, yscale, xorigin, yorigin, cx, cy,
+ mx1, mx2, my1, my2, vx1, vx2, vy1, vy2, xs, ys, endcom, pl
diff --git a/sys/gio/cursor/gtr.h b/sys/gio/cursor/gtr.h
new file mode 100644
index 00000000..3fbf93f5
--- /dev/null
+++ b/sys/gio/cursor/gtr.h
@@ -0,0 +1,51 @@
+# GIOTR.H -- Global definitions for the GIOTR graphics i/o workstation
+# transformation and i/o program unit. Note: requires <gio.h>.
+
+define DEF_MAXLENFRAMEBUF 128000
+define DEF_LENFRAMEBUF 8192
+define INC_LENFRAMEBUF 4096
+define DEF_LENSCRATCHBUF 256
+define INC_LENSCRATCHBUF 256
+define MAX_PSEUDOFILES 10
+define SZ_TRDEVNAME 229
+define SZ_KERNFNAME 259
+define LEN_GTRCOM 28 # see "gtr.com"
+define KSHIFT 10000 # encode pr ("etc$prpsio.x") such that
+ #
+ # ((pr*KSHIFT)+stream) > LAST_FD
+ #
+ # see also <gio.h>
+
+define LEN_TRSTRUCT (564+204)
+
+define TR_PID Memi[$1] # process id of kernel
+define TR_IN Memi[$1+1] # input from process
+define TR_OUT Memi[$1+2] # output to process
+define TR_TTY Memi[$1+3] # graphcap descriptor
+define TR_SPOOLDATA Memi[$1+4] # spool metacode instructions
+define TR_FRAMEBUF Memi[$1+5] # pointer to frame buffer
+define TR_LENFRAMEBUF Memi[$1+6] # length of the frame buffer
+define TR_MAXLENFRAMEBUF Memi[$1+7] # max length of the frame buffer
+define TR_IP Memi[$1+8] # input pointer into frame buf
+define TR_OP Memi[$1+9] # output pointer into frame buf
+define TR_LASTOP Memi[$1+10] # last OP (for undo)
+define TR_SCRATCHBUF Memi[$1+11] # for annotating plots
+define TR_LENSCRATCHBUF Memi[$1+12] # length of the scratch buffer
+define TR_OPSB Memi[$1+13] # output pointer, scratch buf
+define TR_NOPEN Memi[$1+14] # number of opens
+define TR_REDIR Memi[$1+15] # redirection information
+define TR_WCS Memi[$1+16] # WCS selected, 0 if none
+define TR_PAGE Memi[$1+17] # clear screen for text
+define TR_WAITPAGE Memi[$1+18] # grc_waitpage flag
+define TR_WSOPEN Memi[$1+19] # workstation open count
+define TR_SKIPOPEN Memi[$1+20] # skip wsopen in metacode
+define TR_WSACTIVE Memi[$1+21] # workstation activated?
+define TR_WSACTSAVE Memi[$1+22] # save old wsactive state
+define TR_INTERACTIVE Memi[$1+23] # the user graphics terminal?
+ # (open)
+define TR_TXAP ($1+30) # text drawing attributes
+define TR_PLAP ($1+40) # text drawing attributes
+define TR_DEVNAME Memc[P2C($1+44)] # device name
+define TR_KERNFNAME Memc[P2C($1+274)] # name of kernel file (or "cl")
+define TR_GTRCOM Memi[$1+534] # storage for the gtr common
+define TR_WCSPTR (($1)+564+($2)*LEN_WCS) # WCS storage (0=not used)
diff --git a/sys/gio/cursor/gtrbackup.x b/sys/gio/cursor/gtrbackup.x
new file mode 100644
index 00000000..9ab13c0b
--- /dev/null
+++ b/sys/gio/cursor/gtrbackup.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_BACKUP -- Backup one drawing instruction in the frame buffer. Erase
+# the graphics if possible. The effects of this function may be undone by
+# the UNDO operator.
+
+procedure gtr_backup (stream)
+
+int stream # graphics stream
+
+int opcode
+pointer tr, op, bp, sp, ap
+pointer gtr_init()
+errchk gtr_init
+include "gtr.com"
+
+begin
+ call smark (sp)
+ call salloc (ap, LEN_PL, TY_STRUCT)
+
+ tr = gtr_init (stream)
+
+ # Scan backward to the beginning of the last drawing instruction in the
+ # frame buffer.
+
+ op = TR_OP(tr)
+ bp = TR_FRAMEBUF(tr)
+ if (op <= bp) {
+ call sfree (sp)
+ return
+ }
+
+ repeat {
+ op = op - 1
+ while (Mems[op] != BOI)
+ if (op <= bp) {
+ TR_OP(tr) = bp
+ TR_IP(tr) = bp
+ call sfree (sp)
+ return
+ } else
+ op = op - 1
+ opcode = Mems[op+GKI_HDR_OPCODE-1]
+ } until (opcode >= GKI_POLYLINE && opcode <= GKI_PUTCELLARRAY)
+
+ # Redraw the last instruction to erase it (device permitting).
+ if (opcode == GKI_POLYLINE) {
+ PL_LTYPE(ap) = GL_CLEAR
+ PL_WIDTH(ap) = 1.0
+ PL_COLOR(ap) = 1
+ call gki_plset (stream, ap)
+
+ if (wstranset == YES)
+ call gtr_wstran (Mems[op])
+ else
+ call gki_write (stream, Mems[op])
+
+ PL_LTYPE(ap) = GL_SOLID
+ call gki_plset (stream, ap)
+ call gki_fflush (stream)
+ }
+
+ # Return the space in the buffer.
+ TR_LASTOP(tr) = TR_OP(tr)
+ TR_OP(tr) = op
+ TR_IP(tr) = min (op, TR_IP(tr))
+
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtrconn.x b/sys/gio/cursor/gtrconn.x
new file mode 100644
index 00000000..c2e6fb47
--- /dev/null
+++ b/sys/gio/cursor/gtrconn.x
@@ -0,0 +1,78 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+
+# GTR_CONNECT -- Connect a subprocess containing a graphics kernel task to a
+# graphics stream. The graphics kernel task is a conventional IRAF task
+# linked into the kernel process. After spawning the subprocess, we command
+# the process to run the named kernel task, then service the parameter
+# requests from the task as it begins running. Graphics i/o will be via one
+# of the graphics streams, leaving STDIN, STDOUT, and STDERR free to access
+# the corresponding streams in the parent (the CL). A kernel may be opened
+# either to drive a particular device (if devname is specified) or to drive
+# a device selected at runtime. If the kernel is opened to drive a particular
+# device the device name in the OPENWS instruction will be ignored. We require
+# that the graphics kernel begin processing metacode immediately after
+# receiving "yes" for the value of the parameter "generic", signifying that
+# the caller wishes a generic kernel, i.e., cannot return the values of any
+# kernel dependent parameters.
+
+int procedure gtr_connect (kernfname, taskname, devname, stream, in, out)
+
+char kernfname[ARB] # name of executable kernel file
+char taskname[ARB] # name of kernel task
+char devname[ARB] # device name or null string
+int stream # graphics stream to connect process to
+int in, out # input and output streams to process
+
+pointer sp, lbuf
+int pid
+bool streq()
+int propen(), getline()
+errchk propen, flush, getline, syserr
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ pid = propen (kernfname, in, out)
+ call fprintf (out, "%s\n")
+ call pargstr (taskname)
+ call flush (out)
+
+ # Pass values of the kernel parameters. For a kernel run as
+ # part of the graphics system there are only three parameters,
+ # the input file name (STDGRAPH, etc. for a connected kernel)
+ # the device name if the kernel is to ignore device names in
+ # OPENWS instructions, and "generic=yes", signifying that the
+ # kernel dependent parameters are not to be requested.
+
+ while (getline (in, Memc[lbuf]) != EOF) {
+ if (streq (Memc[lbuf], "=input\n")) {
+ call fprintf (out, "%s\n")
+ switch (stream) {
+ case STDGRAPH:
+ call pargstr ("STDGRAPH")
+ case STDIMAGE:
+ call pargstr ("STDIMAGE")
+ case STDPLOT:
+ call pargstr ("STDPLOT")
+ }
+ call flush (out)
+ } else if (streq (Memc[lbuf], "=device\n")) {
+ call fprintf (out, "%s\n")
+ call pargstr (devname)
+ call flush (out)
+ } else if (streq (Memc[lbuf], "=generic\n")) {
+ call putline (out, "yes\n")
+ call flush (out)
+ break
+ } else {
+ call putline (STDERR, Memc[lbuf])
+ call syserr (SYS_GKERNPARAM)
+ }
+ }
+
+ call sfree (sp)
+ return (pid)
+end
diff --git a/sys/gio/cursor/gtrctrl.x b/sys/gio/cursor/gtrctrl.x
new file mode 100644
index 00000000..8de08ccb
--- /dev/null
+++ b/sys/gio/cursor/gtrctrl.x
@@ -0,0 +1,122 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <prstat.h>
+include <config.h>
+include <fset.h>
+include <gset.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_CONTROL -- Execute a graphics control instruction, e.g., connect a
+# graphics kernel to a graphics stream and set or get the WCS for a frame.
+# The control instructions are GKI encoded instructions transmitted to the
+# pseudofile GIOCONTROL. The PR_PSIO procedure (which processes the pseudofile
+# directives from a subprocess) calls us whenever data is sent to this
+# special pseudofile.
+
+procedure gtr_control (stream, gki, source_pid)
+
+int stream # graphics stream
+short gki[ARB] # encoded graphics control instruction
+int source_pid # pid of requesting process
+
+bool redirected
+pointer tr, sp, devname, gki_out
+int flags, mode, nwords, fd, p_fd
+int prstati(), pr_getredir()
+pointer gtr_init(), coerce()
+errchk gtr_init, gtr_openws, write, flush, gki_write
+include "gtr.com"
+
+begin
+ call smark (sp)
+ call salloc (devname, SZ_TRDEVNAME, TY_CHAR)
+
+ nwords = gki[GKI_HDR_LENGTH]
+ call salloc (gki_out, nwords, TY_SHORT)
+ call amovs (gki, Mems[gki_out], nwords)
+
+ tr = gtr_init (stream)
+ p_fd = abs (pr_getredir (source_pid, stream))
+ redirected = (p_fd >= FIRST_FD && p_fd <= LAST_FD)
+
+ switch (gki[GKI_HDR_OPCODE]) {
+ case GKI_OPENWS:
+ mode = gki[GKI_OPENWS_M]
+ nwords = gki[GKI_OPENWS_N]
+
+ # Unpack the device name, passed as a short integer array.
+ call achtsc (gki[GKI_OPENWS_D], Memc[devname], nwords)
+ Memc[devname+nwords] = EOS
+
+ # Connect the kernel.
+ call fseti (stream, F_CANCEL, OK)
+ call gtr_openws (Memc[devname], mode, stream, source_pid)
+
+ # Count the logical openws.
+ TR_WSOPEN(tr) = TR_WSOPEN(tr) + 1
+ TR_WSACTIVE(tr) = YES
+ TR_WSACTSAVE(tr) = NO
+
+ # Due to a call to F_CANCEL in prpsio the openws instruction
+ # spooled by gki_write below is being lost for subkernels,
+ # so don't set the skipopen flag. This causes giotr to pass
+ # the openws on to the subkernel. For inline kernels setting
+ # skipopen prevents the openws from being executed twice.
+
+ if (TR_INTERACTIVE(tr) == YES)
+ TR_SKIPOPEN(tr) = YES
+
+ # If opening NEW_FILE, discard any previous WCS and clear the
+ # frame buffer.
+
+ if (mode == NEW_FILE) {
+ call aclri (Memi[TR_WCSPTR(tr,1)], LEN_WCS * MAX_WCS)
+ call gtr_frame (tr, TR_FRAMEBUF(tr), stream)
+ }
+
+ case GKI_CLOSEWS:
+ # Count the logical closews.
+ TR_WSOPEN(tr) = TR_WSOPEN(tr) - 1
+ TR_WSACTIVE(tr) = NO
+
+ case GKI_DEACTIVATEWS:
+ TR_WSACTIVE(tr) = NO
+ if (TR_INTERACTIVE(tr) == YES && TR_PAGE(tr) == NO) {
+ flags = gki[GKI_REACTIVATEWS_F]
+ if (and (flags, AW_CLEAR) != 0)
+ Mems[gki_out+GKI_REACTIVATEWS_F-1] = flags - AW_CLEAR
+ }
+
+ case GKI_REACTIVATEWS:
+ TR_WSACTIVE(tr) = YES
+ if (TR_INTERACTIVE(tr) == YES) {
+ flags = gki[GKI_REACTIVATEWS_F]
+ if (and (flags, AW_PAUSE) != 0)
+ call gtr_waitpage (STDERR, stream)
+ }
+
+ case GKI_SETWCS:
+ nwords = gki[GKI_SETWCS_N]
+ call amovs (gki[GKI_SETWCS_WCS],
+ Mems[coerce (TR_WCSPTR(tr,1), TY_STRUCT, TY_SHORT)],
+ min (nwords, LEN_WCS * MAX_WCS * SZ_STRUCT / SZ_SHORT))
+
+ case GKI_GETWCS:
+ nwords = gki[GKI_GETWCS_N]
+ fd = prstati (source_pid, PR_OUTFD)
+
+ call write (fd, Memi[TR_WCSPTR(tr,1)], nwords * SZ_SHORT)
+ call flush (fd)
+ }
+
+ # Pass the (possibly modified) instruction on to the kernel.
+ # We must NOT call gki_flush or gki_fflush here, as this would
+ # result in a reentrant call to prpsio when writing to a subkernel.
+
+ if (!redirected)
+ call gki_write (stream, Mems[gki_out])
+
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtrdelete.x b/sys/gio/cursor/gtrdelete.x
new file mode 100644
index 00000000..97f418a0
--- /dev/null
+++ b/sys/gio/cursor/gtrdelete.x
@@ -0,0 +1,45 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_DELETE -- Delete an instruction from the frame buffer. This prevents
+# the instruction from being executed if the frame is redrawn.
+
+procedure gtr_delete (tr, gki)
+
+pointer tr #I giotr descriptor
+pointer gki #I instruction to be deleted
+
+pointer inext
+int nwords, shift, ilen
+
+begin
+ ilen = Mems[gki+GKI_HDR_LENGTH-1]
+ inext = gki + ilen
+
+ if (inext >= TR_OP(tr)) {
+ # Instruction is the last one in the buffer.
+ TR_OP(tr) = gki
+ TR_LASTOP(tr) = TR_OP(tr)
+ if (TR_IP(tr) >= gki)
+ TR_IP(tr) = gki
+
+ } else {
+ # If the instruction is small and would be expensive to delete
+ # just change the opcode to disable it, otherwise shift the
+ # buffer contents back to overwrite the deleted instruction.
+
+ nwords = TR_OP(tr) - inext
+ if (ilen < 32 && nwords > 2048)
+ Mems[gki+GKI_HDR_OPCODE-1] = GKI_UNKNOWN
+ else {
+ call amovs (Mems[inext], Mems[gki], nwords)
+ shift = inext - gki
+ TR_IP(tr) = TR_IP(tr) - shift
+ TR_OP(tr) = TR_OP(tr) - shift
+ TR_LASTOP(tr) = TR_OP(tr)
+ }
+ }
+end
diff --git a/sys/gio/cursor/gtrdiscon.x b/sys/gio/cursor/gtrdiscon.x
new file mode 100644
index 00000000..5eba23f4
--- /dev/null
+++ b/sys/gio/cursor/gtrdiscon.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+
+# GTR_DISCONNECT -- Disconnect from a kernel subprocess. To achieve an orderly
+# shutdown we process any outstanding XMIT or XFER requests, then transmit an
+# end of file (zero length record) to the kernel task when it reads from the
+# graphics stream. The kernel should then shutdown and eventually we will
+# receive "bye" from the process. We then call PRCLOSE to shutdown the
+# process for good. Note: we do not expect anything but an XFER (read) request
+# on the graphics stream, but it seems prudent to do something reasonable if
+# some other request is received.
+
+procedure gtr_disconnect (pid, in, out, stream)
+
+int pid # process id of subprocess
+int in, out # command i/o streams of the subprocess
+int stream # graphics stream used by kernel
+
+pointer sp, sp2, lbuf, buf
+int pseudofile, nchars, junk
+bool streq()
+int getline(), read(), strncmp(), psio_isxmit(), prclose(), pr_findproc()
+errchk getline, prclose, read, write
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ while (getline (in, Memc[lbuf]) != EOF) {
+ if (streq (Memc[lbuf], "bye\n") ||
+ strncmp (Memc[lbuf], "ERROR", 5) == 0) {
+
+ junk = prclose (pid)
+ break
+
+ } else if (Memc[lbuf] == '!') {
+ # OS escape.
+ call proscmd (pr_findproc(pid), Memc[lbuf+1])
+
+ } else {
+ call smark (sp2)
+
+ switch (psio_isxmit (Memc[lbuf], pseudofile, nchars)) {
+ case XMIT:
+ call salloc (buf, nchars, TY_CHAR)
+ nchars = read (in, Memc[buf], nchars)
+ if (nchars > 0)
+ if (pseudofile == STDOUT || pseudofile == STDERR)
+ call write (pseudofile, Memc[buf], nchars)
+
+ case XFER:
+ call salloc (buf, nchars, TY_CHAR)
+ if (pseudofile == STDIN)
+ nchars = read (pseudofile, Memc[buf], nchars)
+ else
+ nchars = 0 # this is the EOF
+ call psio_xfer (out, Memc[buf], nchars)
+ }
+
+ call sfree (sp2)
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtrfetch.x b/sys/gio/cursor/gtrfetch.x
new file mode 100644
index 00000000..44ccfe60
--- /dev/null
+++ b/sys/gio/cursor/gtrfetch.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_FETCH_NEXT_INSTRUCTION -- Return a pointer to the next GKI metacode
+# instruction in the input buffer. Only complete instructions resident in
+# a contiguous section of memory are returned. EOF is returned when the
+# end of the current buffer is reached, or when the last instruction in the
+# frame buffer is not yet complete. EOF does not signify the end of the
+# metacode stream.
+
+int procedure gtr_fetch_next_instruction (tr, gki)
+
+pointer tr # pointer to giotr descriptor
+pointer gki # pointer to next instruction (output)
+
+int nleft, length
+pointer ip, itop
+
+begin
+ ip = TR_IP(tr)
+ itop = TR_OP(tr)
+
+ # Search for the beginning of the next instruction.
+ while (Mems[ip] != BOI && ip < itop)
+ ip = ip + 1
+
+ nleft = itop - ip
+ if (nleft < 3) {
+ # The length field of the next instruction is not yet present.
+ TR_IP(tr) = ip
+ return (EOF)
+ } else {
+ length = Mems[ip+GKI_HDR_LENGTH-1]
+ if (length > nleft) {
+ # Entire instruction is not yet present in buffer.
+ TR_IP(tr) = ip
+ return (EOF)
+ } else {
+ # Entire instruction is present in buffer.
+ TR_IP(tr) = ip + length
+ gki = ip
+ return (length)
+ }
+ }
+end
diff --git a/sys/gio/cursor/gtrframe.x b/sys/gio/cursor/gtrframe.x
new file mode 100644
index 00000000..baf68ffb
--- /dev/null
+++ b/sys/gio/cursor/gtrframe.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_FRAME -- Clear the frame buffer, used to spool the metacode instructions
+# required to draw a graphics frame. This is done by moving the metacode data
+# at the end of the buffer (beginning with the word pointed to by gki) to the
+# beginning of the buffer and adjusting the input and output pointers
+# accordingly. The workstation transformation is also reset to the unitary
+# transformation when the frame is cleared, i.e., zoom is cancelled.
+
+procedure gtr_frame (tr, gki, stream)
+
+pointer tr # giotr descriptor
+pointer gki # pointer to first word to be preserved
+int stream # graphics stream
+
+pointer bp
+int nwords, shift
+
+begin
+ bp = TR_FRAMEBUF(tr)
+
+ if (gki > bp) {
+ nwords = TR_OP(tr) - gki
+ call amovs (Mems[gki], Mems[bp], nwords)
+ shift = gki - bp
+ TR_IP(tr) = TR_IP(tr) - shift
+ TR_OP(tr) = TR_OP(tr) - shift
+ } else {
+ TR_IP(tr) = bp
+ TR_OP(tr) = bp
+ }
+
+ call gtr_ptran (stream, 0., 1., 0., 1.)
+ TR_OPSB(tr) = TR_SCRATCHBUF(tr)
+ TR_LASTOP(tr) = TR_OP(tr)
+ TR_WCS(tr) = NULL
+end
diff --git a/sys/gio/cursor/gtrgflush.x b/sys/gio/cursor/gtrgflush.x
new file mode 100644
index 00000000..5681e234
--- /dev/null
+++ b/sys/gio/cursor/gtrgflush.x
@@ -0,0 +1,45 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <gio.h>
+include "gtr.h"
+
+# GTR_GFLUSH -- Dispose of any buffered output on the stream STDPLOT. The last
+# plot sent to stdplot cannot be disposed of at CLOSEWS time due to the need
+# to permit APPEND mode in the next OPENWS call. We are called to dispose
+# of all output to the plotter device. Logging out or doing a reset will have
+# the same effect.
+
+procedure gtr_gflush (stream)
+
+int stream
+pointer tr
+bool streq()
+include "gtr.com"
+
+begin
+ tr = trdes[stream]
+ if (tr == NULL)
+ return
+
+ # Disconnect the kernel.
+ iferr {
+ if (streq (TR_KERNFNAME(tr), "cl"))
+ call stg_close()
+ else if (TR_DEVNAME(tr) != EOS && TR_KERNFNAME(tr) != EOS) {
+ call gtr_disconnect (TR_PID(tr), TR_IN(tr), TR_OUT(tr),
+ stream)
+ TR_PID(tr) = NULL
+ }
+ } then
+ call erract (EA_WARN)
+
+ # Free all storage.
+ call mfree (TR_FRAMEBUF(tr), TY_SHORT)
+ call mfree (TR_SCRATCHBUF(tr), TY_SHORT)
+ call mfree (tr, TY_STRUCT)
+
+ trdes[stream] = NULL
+ if (tr_stream == stream)
+ tr_stream = NULL
+end
diff --git a/sys/gio/cursor/gtrgtran.x b/sys/gio/cursor/gtrgtran.x
new file mode 100644
index 00000000..c83c83aa
--- /dev/null
+++ b/sys/gio/cursor/gtrgtran.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_GTRAN -- Get the workstation transformation.
+
+procedure gtr_gtran (fd, x1, x2, y1, y2)
+
+int fd # graphics stream to be set
+real x1, x2 # range of workstation viewport in X
+real y1, y2 # range of workstation viewport in Y
+include "gtr.com"
+
+begin
+ if (wstranset == YES) {
+ x1 = vx1
+ x2 = vx2
+ y1 = vy1
+ y2 = vy2
+ } else {
+ x1 = 0
+ x2 = 1.0
+ y1 = 0
+ y2 = 1.0
+ }
+end
diff --git a/sys/gio/cursor/gtrgtty.x b/sys/gio/cursor/gtrgtty.x
new file mode 100644
index 00000000..0e67a1fd
--- /dev/null
+++ b/sys/gio/cursor/gtrgtty.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_GTTY -- Get the graphcap descriptor for a stream.
+
+pointer procedure gtr_gtty (stream)
+
+int stream # graphics stream of interest
+
+pointer tr
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (stream)
+ return (TR_TTY(tr))
+end
diff --git a/sys/gio/cursor/gtrinit.x b/sys/gio/cursor/gtrinit.x
new file mode 100644
index 00000000..734d8202
--- /dev/null
+++ b/sys/gio/cursor/gtrinit.x
@@ -0,0 +1,136 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_INIT -- Initialize the GIOTR data structures for a graphics stream. These
+# data structures are initialized only once, when the first i/o occurs on the
+# stream. Thereafter our only function is to fault the workstation
+# transformation parameters into the cache (the gtr common).
+
+pointer procedure gtr_init (stream)
+
+int stream # graphics stream
+
+int i, len_fb, len_sb
+pointer tr, tx, ap, w
+bool first_time
+int btoi(), envgeti()
+data first_time /true/
+errchk calloc, malloc
+include "gtr.com"
+
+begin
+ if (first_time) {
+ call amovki (NULL, trdes, MAX_PSEUDOFILES)
+ tr_stream = NULL
+ first_time = false
+ }
+
+ tr = trdes[stream]
+
+ if (tr == NULL) {
+ # This is the first time the stream has been accessed.
+
+ # Allocate descriptor.
+ call calloc (tr, LEN_TRSTRUCT, TY_STRUCT)
+
+ # Don't need a frame buffer for STDPLOT, but make a dummy one
+ # anyhow so that the stream looks like the interactive ones.
+
+ if (stream == STDPLOT) {
+ len_fb = 1
+ len_sb = 1
+ } else {
+ len_fb = DEF_LENFRAMEBUF
+ len_sb = DEF_LENSCRATCHBUF
+ }
+
+ call malloc (TR_FRAMEBUF(tr), len_fb, TY_SHORT)
+ call malloc (TR_SCRATCHBUF(tr), len_sb, TY_SHORT)
+
+ trdes[stream] = tr
+ TR_IP(tr) = TR_FRAMEBUF(tr)
+ TR_OP(tr) = TR_FRAMEBUF(tr)
+ TR_OPSB(tr) = TR_SCRATCHBUF(tr)
+ TR_LENFRAMEBUF(tr) = len_fb
+ TR_LENSCRATCHBUF(tr) = len_sb
+ TR_SPOOLDATA(tr) = btoi (stream != STDPLOT)
+ TR_WAITPAGE(tr) = NO
+ TR_PAGE(tr) = YES
+
+ # Set text drawing attributes for annotating plots.
+ tx = TR_TXAP(tr)
+ TX_UP(tx) = 90
+ TX_SIZE(tx) = 1.0
+ TX_PATH(tx) = GT_RIGHT
+ TX_SPACING(tx) = 0
+ TX_HJUSTIFY(tx) = GT_LEFT
+ TX_VJUSTIFY(tx) = GT_BOTTOM
+ TX_FONT(tx) = GT_ROMAN
+ TX_QUALITY(tx) = GT_NORMAL
+ TX_COLOR(tx) = 1
+
+ # Set default polyline attributes for axis drawing.
+ ap = TR_PLAP(tr)
+ PL_LTYPE(ap) = GL_SOLID
+ PL_WIDTH(ap) = 1.0
+ PL_COLOR(ap) = 1
+
+ # The user can override the default maximum frame buffer length
+ # if they wish, permitting spooling of frames of any size.
+
+ iferr (TR_MAXLENFRAMEBUF(tr) = envgeti ("cmbuflen"))
+ TR_MAXLENFRAMEBUF(tr) = DEF_MAXLENFRAMEBUF
+
+ if (tr_stream != NULL) {
+ # Save the workstation transformation parameters for the
+ # stream currently in the cache, if any.
+
+ call amovi (startcom, TR_GTRCOM(trdes[tr_stream]), LEN_GTRCOM)
+ call amovi (TR_GTRCOM(tr), startcom, LEN_GTRCOM)
+ }
+
+ # Initialize the transformation parameters for the new stream.
+ tr_stream = stream
+ xscale = 1.0
+ yscale = 1.0
+ mx2 = GKI_MAXNDC
+ my2 = GKI_MAXNDC
+ vx2 = 1.0
+ vy2 = 1.0
+
+ # Initialize the WCS in case someone tries to read the cursor
+ # before there are any graphics.
+
+ do i = 1, MAX_WCS {
+ w = TR_WCSPTR(tr,i)
+ WCS_SX1(w) = 0.0
+ WCS_SX2(w) = 1.0
+ WCS_SY1(w) = 0.0
+ WCS_SY2(w) = 1.0
+
+ WCS_WX1(w) = 0.0
+ WCS_WX2(w) = 1.0
+ WCS_WY1(w) = 0.0
+ WCS_WY2(w) = 1.0
+ }
+
+ } else if (stream != tr_stream) {
+ # The stream has already been initialized.
+
+ # If the cache is currently validated for some different stream
+ # move the data for that stream out into its descriptor.
+
+ if (tr_stream != NULL)
+ call amovi (startcom, TR_GTRCOM(trdes[tr_stream]), LEN_GTRCOM)
+
+ # Load the data for the new stream into the cache.
+ call amovi (TR_GTRCOM(tr), startcom, LEN_GTRCOM)
+ tr_stream = stream
+ }
+
+ return (tr)
+end
diff --git a/sys/gio/cursor/gtropenws.x b/sys/gio/cursor/gtropenws.x
new file mode 100644
index 00000000..27a3072a
--- /dev/null
+++ b/sys/gio/cursor/gtropenws.x
@@ -0,0 +1,206 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <config.h>
+include <error.h>
+include <prstat.h>
+include <fset.h>
+include <fio.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_OPENWS -- Called by gtr_control(pr_psio) to connect a kernel to a
+# graphics stream and to initialize the datapath to the kernel.
+# The workstation is not physically opened until the GKI open workstation
+# directive has been sent to the kernel. There are two types of kernels,
+# the builtin (STDGRAPH) kernel, and all external kernels. The external
+# kernels reside in connected subprocesses communicating via the central
+# process (the CL process) with the graphics task in another subprocess.
+
+procedure gtr_openws (devspec, mode, stream, source_pid)
+
+char devspec[ARB] #I device specification
+int mode #I access mode
+int stream #I graphics stream
+int source_pid #I process which issued the openws directive
+
+int redir_code, dd[LEN_GKIDD], ip
+pointer sp, op, tr, tty, kernfname, taskname, device
+
+bool streq()
+pointer ttygdes()
+int pr_getredir(), ttygets(), gtr_connect(), pr_findproc(), locpr()
+extern gtr_reset(), prpsio()
+
+errchk syserr, syserrs, fseti, ttygdes, ttycdes, pr_redir, stg_close, stg_open
+errchk gtr_connect, gtr_disconnect
+include "gtr.com"
+
+begin
+ call smark (sp)
+ call salloc (kernfname, SZ_FNAME, TY_CHAR)
+ call salloc (taskname, SZ_FNAME, TY_CHAR)
+ call salloc (device, SZ_FNAME, TY_CHAR)
+
+ tr = trdes[stream]
+
+ # Extract the device name field from the device specification.
+ op = device
+ for (ip=1; devspec[ip] != EOS; ip=ip+1)
+ if (devspec[ip] == ',')
+ break
+ else {
+ Memc[op] = devspec[ip]
+ op = op + 1
+ }
+ Memc[op] = EOS
+
+ # We only connect up the i/o channels, and do not issue the OPENWS
+ # to the gio kernel, so reset the counter to zero to indicate that
+ # the workstation has not yet been (logically) opened.
+
+ TR_WSOPEN(tr) = 0
+
+ # If the stream has been redirected into a file, do not connect a
+ # kernel.
+
+ redir_code = pr_getredir (source_pid, stream)
+ if (redir_code >= FIRST_FD && redir_code <= LAST_FD) {
+ call sfree (sp)
+ return
+ }
+
+ # The graphics stream is a spoolfile in this process (the CL process).
+ # Spoolfiles are files that are fully buffered in memory and never
+ # get written to disk. Data is written into the spoolfile and then
+ # read back out by a different part of the program.
+
+ call fseti (stream, F_TYPE, SPOOL_FILE)
+ call fseti (stream, F_CANCEL, OK)
+
+ # If the device is already connected to the stream (or we are
+ # appending to a connected device) all we need do is reset the
+ # redirection code for the graphics stream. This code is reset to
+ # the default value (the code for the stream itself) by the CL when
+ # a task is spawned.
+
+ if (TR_DEVNAME(tr) != EOS && mode == APPEND ||
+ streq (devspec, TR_DEVNAME(tr))) {
+ call pr_redir (source_pid, stream, TR_REDIR(tr))
+ call sfree (sp)
+ return
+ }
+
+ # Connect the named kernel, i.e., disconnect the old kernel if any
+ # and connect the new one. Set the redirection information for the
+ # named stream of the source process.
+
+ iferr {
+ # Close device graphcap descriptor.
+ if (TR_TTY(tr) != NULL)
+ call ttycdes (TR_TTY(tr))
+
+ # Disconnect old kernel.
+ if (streq (TR_KERNFNAME(tr), "cl"))
+ call stg_close()
+ else if (TR_DEVNAME(tr) != EOS && TR_KERNFNAME(tr) != EOS) {
+ call gtr_disconnect (TR_PID(tr), TR_IN(tr), TR_OUT(tr), stream)
+ TR_PID(tr) = NULL
+ TR_IN(tr) = NULL
+ TR_OUT(tr) = NULL
+ }
+ } then {
+ TR_DEVNAME(tr) = EOS
+ call erract (EA_ERROR)
+ } else
+ TR_DEVNAME(tr) = EOS
+
+ # Get graphcap entry for the new device. The special device name
+ # "none" indicates that there is no suitable stdgraph device.
+
+ if (streq (devspec, "none")) {
+ switch (stream) {
+ case STDGRAPH:
+ call syserr (SYS_GGNONE)
+ case STDIMAGE:
+ call syserr (SYS_GINONE)
+ case STDPLOT:
+ call syserr (SYS_GPNONE)
+ default:
+ call syserr (SYS_GGNONE)
+ }
+ } else {
+ tty = ttygdes (Memc[device])
+ TR_TTY(tr) = tty
+ }
+
+ # Get the name of the executable file containing the kernel for the
+ # device. The special name "cl" signifies the builtin STDGRAPH kernel.
+
+ if (ttygets (tty, "kf", Memc[kernfname], SZ_FNAME) <= 0) {
+ call ttycdes (tty)
+ call syserrs (SYS_GNOKF, Memc[device])
+ } else if (ttygets (tty, "tn", Memc[taskname], SZ_FNAME) <= 0)
+ ;
+
+ # Connect the new kernel.
+ call strcpy (Memc[kernfname], TR_KERNFNAME(tr), SZ_KERNFNAME)
+
+ if (streq (Memc[kernfname], "cl")) {
+ # Open the stdgraph kernel. Connect the referenced GKI stream to
+ # the stdgraph kernel. Set a negative redirection code value to
+ # flag that GIOTR is to be called to filter graphics output from
+ # the process.
+
+ call stg_open (devspec, dd, STDIN, STDOUT, 0, 0, 0)
+ call gki_inline_kernel (stream, dd)
+ if (source_pid != NULL)
+ call pr_redir (source_pid, stream, -stream)
+ TR_REDIR(tr) = -stream
+ TR_INTERACTIVE(tr) = YES
+
+ } else {
+ # Spawn subprocess and start up kernel task.
+ TR_PID(tr) = gtr_connect (Memc[kernfname], Memc[taskname],
+ devspec, stream, TR_IN(tr), TR_OUT(tr))
+
+ # Encode the process slot number of the kernel process in the
+ # redirection code for the source process (the process which
+ # issued the openws). If the stream is STDGRAPH or STDIMAGE
+ # make the redirection code negative to flag that graphics
+ # output is to be processed through GIOTR (the workstation
+ # transformation).
+
+ if (source_pid != NULL) {
+ redir_code = (pr_findproc(TR_PID(tr)) * KSHIFT) + stream
+ if (stream == STDGRAPH || stream == STDIMAGE)
+ redir_code = -redir_code
+ call pr_redir (source_pid, stream, redir_code)
+ TR_REDIR(tr) = redir_code
+
+ # Mark the process busy. This flags it it as busy executing
+ # some subprotocol (in this case processing GKI metacode) and
+ # prevents commands such as chdir/set from being sent to the
+ # process and corrupting the IPC protocol.
+
+ call prseti (TR_PID(tr), PR_STATUS, P_BUSY)
+ }
+
+ call gki_subkernel (stream, TR_PID(tr), locpr(prpsio))
+ TR_INTERACTIVE(tr) = NO
+ }
+
+ # Do not change value of DEVNAME until the new kernel has been
+ # successfully connected, since this variable is used to test if
+ # the kernel is already connected.
+
+ call strcpy (devspec, TR_DEVNAME(tr), SZ_TRDEVNAME)
+
+ # Post the gtr_reset procedure to be executed upon process shutdown,
+ # to close down any connected graphics subkernels in an orderly way.
+
+ call onexit (gtr_reset)
+
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtrpage.x b/sys/gio/cursor/gtrpage.x
new file mode 100644
index 00000000..2caa53cb
--- /dev/null
+++ b/sys/gio/cursor/gtrpage.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gio.h>
+include "gtr.h"
+
+# GTR_PAGE -- Prepare the workstation for output of one or more pages of text.
+# Whether or not the terminal is paged is optional. On terminals where the
+# text and graphics are overlaid, it is possible to run the text by beneath
+# the plot without affecting the plot.
+
+procedure gtr_page (fd, stream)
+
+int fd # output file
+int stream # graphics stream
+
+pointer tr
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (stream)
+
+ if (TR_PAGE(tr) == YES)
+ call gki_deactivatews (stream, AW_CLEAR)
+ else
+ call gki_deactivatews (stream, 0)
+
+ TR_WAITPAGE(tr) = YES
+end
diff --git a/sys/gio/cursor/gtrptran.x b/sys/gio/cursor/gtrptran.x
new file mode 100644
index 00000000..eba3075e
--- /dev/null
+++ b/sys/gio/cursor/gtrptran.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_PTRAN -- Set the workstation transformation. The workstation
+# transformation is automatically zeroed whenever the screen is cleared
+# or when a workstation is opened.
+
+procedure gtr_ptran (stream, x1, x2, y1, y2)
+
+int stream # graphics stream to be set
+real x1, x2 # range of workstation viewport in X
+real y1, y2 # range of workstation viewport in Y
+
+pointer tr
+real tol, min_width, dx, dy
+real cx1, cx2, cy1, cy2
+include "gtr.com"
+
+begin
+ tr = trdes[stream]
+ tol = 5.0 * EPSILON
+
+ if (abs(x1) < tol && abs (x2 - 1.0) < tol &&
+ abs(y1) < tol && abs (y2 - 1.0) < tol) {
+
+ wstranset = NO
+
+ } else {
+ # Save viewport.
+ vx1 = x1
+ vx2 = x2
+ vy1 = y1
+ vy2 = y2
+
+ # Clip viewport at NDC boundary.
+ cx1 = max (0., min (1., x1))
+ cx2 = max (0., min (1., x2))
+ cy1 = max (0., min (1., y1))
+ cy2 = max (0., min (1., y2))
+
+ # Make sure the viewport does not have a zero extent in either
+ # axis after clipping.
+ min_width = 1E-4
+ if (cx2 - cx1 < min_width)
+ cx2 = cx1 + min_width
+ if (cy2 - cy1 < min_width)
+ cy2 = cy1 + min_width
+
+ # Set clipping viewport in input GKI space.
+ mx1 = nint (cx1 * GKI_MAXNDC)
+ mx2 = nint (cx2 * GKI_MAXNDC)
+ my1 = nint (cy1 * GKI_MAXNDC)
+ my2 = nint (cy2 * GKI_MAXNDC)
+
+ # Set transformation upon the clipped GKI coordinates.
+ dx = max (min_width, (x2 - x1))
+ dy = max (min_width, (y2 - y1))
+ xorigin = (cx1 - x1) / dx * GKI_MAXNDC
+ yorigin = (cy1 - y1) / dy * GKI_MAXNDC
+ xscale = 1. / dx
+ yscale = 1. / dy
+
+ wstranset = YES
+ }
+
+ # Clear the scratch buffer whenever the workstation viewport is
+ # changed.
+
+ TR_OPSB(tr) = TR_SCRATCHBUF(tr)
+end
diff --git a/sys/gio/cursor/gtrrcur.x b/sys/gio/cursor/gtrrcur.x
new file mode 100644
index 00000000..495117a3
--- /dev/null
+++ b/sys/gio/cursor/gtrrcur.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gki.h>
+
+# GTR_READCURSOR -- Read the graphics cursor position in NDC coordinates.
+# By the time we are called the plot has already been drawn and the
+# workstation closed, hence we must reopen the workstation to read the
+# cursor (the graphics terminal will not be in graphics mode otherwise).
+
+int procedure gtr_readcursor (fd, key, sx, sy, raster, rx, ry)
+
+int fd #I graphics stream
+int key #O keystroke value
+real sx, sy #O NDC screen coords of cursor
+int raster #O raster number
+real rx, ry #O NDC raster coords of cursor
+
+int cn
+int m_sx, m_sy
+int m_rx, m_ry
+
+begin
+ call gki_getcursor (fd, 0,
+ cn, key, m_sx, m_sy, raster, m_rx, m_ry)
+
+ sx = real(m_sx) / GKI_MAXNDC
+ sy = real(m_sy) / GKI_MAXNDC
+ rx = real(m_rx) / GKI_MAXNDC
+ ry = real(m_ry) / GKI_MAXNDC
+
+ return (key)
+end
diff --git a/sys/gio/cursor/gtrredraw.x b/sys/gio/cursor/gtrredraw.x
new file mode 100644
index 00000000..ca2191b3
--- /dev/null
+++ b/sys/gio/cursor/gtrredraw.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include "gtr.h"
+
+# GTR_REDRAW -- Redraw the screen from the metacode spooled in the frame
+# buffer.
+
+procedure gtr_redraw (stream)
+
+int stream # graphics stream to be redrawn
+
+pointer tr, ip_save, op_save
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (stream)
+
+ if (TR_SPOOLDATA(tr) == YES && TR_OP(tr) > TR_FRAMEBUF(tr)) {
+ # Rewind the input pointer into the frame buffer.
+ TR_IP(tr) = TR_FRAMEBUF(tr)
+
+ # Redraw frame buffer.
+ call gki_clear (stream)
+ call giotr (stream)
+
+ # Redraw scratch buffer (axes). Set i/o pointers to the scratch
+ # buffer and draw its contents. Turn off interrupts to prevent
+ # an interrupt from leaving the pointers pointing to the wrong
+ # buffer.
+
+ if (TR_OPSB(tr) > TR_SCRATCHBUF(tr)) {
+ call intr_disable()
+ ip_save = TR_IP(tr); TR_IP(tr) = TR_SCRATCHBUF(tr)
+ op_save = TR_OP(tr); TR_OP(tr) = TR_OPSB(tr)
+
+ call giotr (stream)
+
+ TR_IP(tr) = ip_save
+ TR_OP(tr) = op_save
+ call intr_enable()
+ }
+
+ # Flush graphics output.
+ call gki_flush (stream)
+ }
+end
diff --git a/sys/gio/cursor/gtrreset.x b/sys/gio/cursor/gtrreset.x
new file mode 100644
index 00000000..36c55a9a
--- /dev/null
+++ b/sys/gio/cursor/gtrreset.x
@@ -0,0 +1,53 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <gio.h>
+include "gtr.h"
+
+# GTR_RESET -- Reset the graphics system. Disconnect all connected subkernels
+# and free all file descriptors and memory.
+
+procedure gtr_reset (status)
+
+int status # not used (req. for ONEXIT)
+
+pointer tr
+int stream
+bool streq()
+include "gtr.com"
+
+begin
+ do stream = STDGRAPH, STDPLOT {
+ tr = trdes[stream]
+ if (tr == NULL)
+ next
+
+ iferr {
+ # Close device graphcap descriptor.
+ if (TR_TTY(tr) != NULL)
+ call ttycdes (TR_TTY(tr))
+
+ # Disconnect old kernel.
+ if (streq (TR_KERNFNAME(tr), "cl"))
+ call stg_close()
+ else if (TR_DEVNAME(tr) != EOS && TR_KERNFNAME(tr) != EOS) {
+ call gtr_disconnect (TR_PID(tr),
+ TR_IN(tr), TR_OUT(tr), stream)
+ TR_PID(tr) = NULL
+ TR_IN(tr) = NULL
+ TR_OUT(tr) = NULL
+ }
+ } then {
+ TR_DEVNAME(tr) = EOS
+ call erract (EA_WARN)
+ } else
+ TR_DEVNAME(tr) = EOS
+
+ # Free all storage.
+ call mfree (TR_FRAMEBUF(tr), TY_SHORT)
+ call mfree (TR_SCRATCHBUF(tr), TY_SHORT)
+ call mfree (tr, TY_STRUCT)
+
+ trdes[stream] = NULL
+ }
+end
diff --git a/sys/gio/cursor/gtrset.x b/sys/gio/cursor/gtrset.x
new file mode 100644
index 00000000..629ef097
--- /dev/null
+++ b/sys/gio/cursor/gtrset.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTRSET -- Set the workstation transformation. The workstation transformation
+# is automatically zeroed whenever the screen is cleared or when a workstation
+# is opened.
+
+procedure gtrset (fd, x1, x2, y1, y2)
+
+int fd # graphics stream to be set
+real x1, x2 # range of workstation viewport in X
+real y1, y2 # range of workstation viewport in Y
+include "gtr.com"
+
+begin
+ mx1 = x1 * GKI_MAXNDC
+ mx2 = x2 * GKI_MAXNDC
+ my1 = y1 * GKI_MAXNDC
+ my2 = y2 * GKI_MAXNDC
+
+ xscale = GKI_MAXNDC / (mx2 - mx1)
+ yscale = GKI_MAXNDC / (my2 - my1)
+
+ wstranset = YES
+end
diff --git a/sys/gio/cursor/gtrstatus.x b/sys/gio/cursor/gtrstatus.x
new file mode 100644
index 00000000..45b5731c
--- /dev/null
+++ b/sys/gio/cursor/gtrstatus.x
@@ -0,0 +1,100 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <gio.h>
+include "gtr.h"
+
+define LEN_NAME 10
+
+
+# GTR_STATUS -- Print information summarizing the utilization of resources
+# by each of the three graphics streams.
+
+procedure gtr_status (fd)
+
+int fd # output file
+int stream, ip
+string names "STDGRAPH:,STDIMAGE:,STDPLOT: "
+include "gtr.com"
+
+begin
+ for (ip=1; names[ip] != EOS; ip=ip+1)
+ if (names[ip] == ',')
+ names[ip] = EOS
+
+ do stream = STDGRAPH, STDPLOT {
+ ip = (stream - STDGRAPH) * LEN_NAME + 1
+ if (trdes[stream] == NULL) {
+ call fprintf (fd, "\t%s disconnected\n")
+ call pargstr (names[ip])
+ } else
+ call gtr_memusage (fd, stream, names[ip])
+ }
+
+ call fprintf (fd, "\n")
+ call flush (fd)
+end
+
+
+# GTR_MEMUSAGE -- Print information summarizing the utilization of memory and
+# other resources by a graphics stream.
+
+procedure gtr_memusage (fd, stream, name)
+
+int fd # output file
+int stream # graphics stream to be described
+char name[ARB] # name of graphics stream
+
+pointer tr, tx
+int bufsize
+int fstati()
+pointer gtr_init()
+errchk gtr_init
+
+begin
+ tr = gtr_init (stream)
+
+ call fprintf (fd, "\t%s kernel=%s, device=%s, page %s\n")
+ call pargstr (name)
+ call pargstr (TR_KERNFNAME(tr))
+ call pargstr (TR_DEVNAME(tr))
+ if (TR_PAGE(tr) == YES)
+ call pargstr ("enabled")
+ else
+ call pargstr ("disabled")
+
+ bufsize = fstati (stream, F_BUFSIZE)
+ call fprintf (fd,
+ "\t\tmemory=%d (%dfb+%dsb+%dfio), frame=%d+%d words\n")
+ call pargi (TR_LENFRAMEBUF(tr) + TR_LENSCRATCHBUF(tr) + bufsize)
+ call pargi (TR_LENFRAMEBUF(tr))
+ call pargi (TR_LENSCRATCHBUF(tr))
+ call pargi (bufsize)
+ call pargi (TR_OP(tr) - TR_FRAMEBUF(tr))
+ call pargi (TR_OPSB(tr) - TR_SCRATCHBUF(tr))
+
+ call fprintf (fd,
+ "\t\tspool=%s, nopen=%d, pid=%d, in=%d, out=%d, redir=%d, wcs=%d\n")
+ if (TR_SPOOLDATA(tr) == YES)
+ call pargstr ("yes")
+ else
+ call pargstr ("no")
+ call pargi (TR_NOPEN(tr))
+ call pargi (TR_PID(tr))
+ call pargi (TR_IN(tr))
+ call pargi (TR_OUT(tr))
+ call pargi (TR_REDIR(tr))
+ call pargi (TR_WCS(tr))
+
+ tx = TR_TXAP(tr)
+ call fprintf (fd,
+ "\t\ttext size=%g, up=%d, path=%s, hj=%s, vj=%s, color=%d\n")
+ call pargr (TX_SIZE(tx))
+ call pargi (TX_UP(tx))
+ call gkp_txparg (TX_PATH(tx))
+ call gkp_txparg (TX_HJUSTIFY(tx))
+ call gkp_txparg (TX_VJUSTIFY(tx))
+ call pargi (TX_COLOR(tx))
+
+ call fprintf (fd, "\n")
+end
diff --git a/sys/gio/cursor/gtrtrunc.x b/sys/gio/cursor/gtrtrunc.x
new file mode 100644
index 00000000..6abda3ba
--- /dev/null
+++ b/sys/gio/cursor/gtrtrunc.x
@@ -0,0 +1,39 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_TRUNCATE -- Truncate the frame buffer, which has grown larger than
+# the limit set by the user (or the system default). This is done by moving
+# the metacode data at the end of the buffer (beginning with the word pointed
+# to by gki) to the maximum upper limit of the buffer and adjusting the input
+# and output pointers accordingly.
+
+procedure gtr_truncate (tr, gki)
+
+pointer tr # giotr descriptor
+pointer gki # pointer to first word to be preserved
+pointer top
+int nwords
+
+begin
+ # Find the first instruction preceding the soft upper limit on the
+ # size of the buffer.
+
+ top = TR_FRAMEBUF(tr) + TR_MAXLENFRAMEBUF(tr)
+ while (Mems[top] != BOI && top > TR_FRAMEBUF(tr))
+ top = top - 1
+
+ # Move the partial instruction likely to be at the end of the buffer
+ # to the new "top". Note that we can only truncate (discard)
+ # instructions which have already been executed, hence the partial
+ # instruction at the end of the buffer must be preserved.
+
+ if (gki != top) {
+ nwords = TR_OP(tr) - gki
+ call amovs (Mems[gki], Mems[top], nwords)
+ TR_IP(tr) = top
+ TR_OP(tr) = top + nwords
+ }
+end
diff --git a/sys/gio/cursor/gtrundo.x b/sys/gio/cursor/gtrundo.x
new file mode 100644
index 00000000..5b8d3e02
--- /dev/null
+++ b/sys/gio/cursor/gtrundo.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gset.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_UNDO -- Undo the last frame buffer edit. Successive pairs of undos leave
+# the frame buffer unchanged.
+
+procedure gtr_undo (stream)
+
+int stream # graphics stream
+int opcode
+pointer tr, op, new_op, old_op, sp, ap
+
+pointer gtr_init()
+errchk gtr_init
+include "gtr.com"
+
+begin
+ call smark (sp)
+ call salloc (ap, LEN_PL, TY_STRUCT)
+
+ tr = gtr_init (stream)
+
+ old_op = TR_OP(tr)
+ new_op = TR_LASTOP(tr)
+ if (new_op == old_op || new_op <= TR_FRAMEBUF(tr)) {
+ call sfree (sp)
+ return
+ }
+
+ # Edit the frame buffer.
+ TR_LASTOP(tr) = old_op
+ TR_OP(tr) = new_op
+ TR_IP(tr) = min (new_op, TR_IP(tr))
+
+ # Redraw the last drawing instruction to erase it (device permitting),
+ # if we are backing up one instruction. Note that it may be necessary
+ # to skip one or more control instructions. We assume that the undo
+ # only has to undo one drawing instruction.
+
+ if (new_op < old_op) {
+ op = new_op
+ repeat {
+ opcode = Mems[op+GKI_HDR_OPCODE-1]
+ if (opcode == GKI_POLYLINE)
+ break
+ else
+ op = op + Mems[op+GKI_HDR_LENGTH-1]
+ } until (op >= old_op)
+
+ if (opcode == GKI_POLYLINE && op < old_op) {
+ PL_LTYPE(ap) = GL_CLEAR
+ PL_WIDTH(ap) = 1.0
+ PL_COLOR(ap) = 1
+ call gki_plset (stream, ap)
+
+ if (wstranset == YES)
+ call gtr_wstran (Mems[op])
+ else
+ call gki_write (stream, Mems[op])
+
+ PL_LTYPE(ap) = GL_SOLID
+ call gki_plset (stream, ap)
+ }
+
+ } else if (new_op > old_op) {
+ # Call giotr to redraw the recovered instructions.
+ call giotr (stream)
+ }
+
+ call gki_flush (stream)
+ call sfree (sp)
+end
diff --git a/sys/gio/cursor/gtrwaitp.x b/sys/gio/cursor/gtrwaitp.x
new file mode 100644
index 00000000..67f46dd8
--- /dev/null
+++ b/sys/gio/cursor/gtrwaitp.x
@@ -0,0 +1,94 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ttyset.h>
+include <error.h>
+include <fset.h>
+include <gio.h>
+include "gtr.h"
+include "grc.h"
+
+# GTR_WAITPAGE -- Print the "hit return to continue" message on the terminal
+# screen and wait for the user to respond before returning to graphics mode.
+# Redrawing of the graphics frame is optional.
+
+procedure gtr_waitpage (fd, stream)
+
+int fd # output file
+int stream # graphics stream
+
+int key, i
+pointer tty, tr
+int getci(), ttystati()
+pointer ttyodes(), gtr_init()
+errchk gtr_init, ttyodes
+
+begin
+ tr = gtr_init (stream)
+ tty = ttyodes ("terminal")
+
+ repeat {
+ # Print prompt in standout mode.
+ call ttyclearln (fd, tty)
+ call ttyso (fd, tty, YES)
+ call fprintf (fd,
+ "[space=cmhelp,return=quit+redraw,q=quit+noredraw]")
+ call ttyso (fd, tty, NO)
+ call flush (fd)
+
+ # Wait for user to hit a key. This is done in text mode via
+ # a raw getc rather than via a cursor read to avoid switching to
+ # graphics mode. On some terminals with separate text and
+ # graphics planes a switch to graphics mode turns off the text
+ # plane.
+
+ call fseti (STDIN, F_RAW, YES)
+ if (getci (STDIN, key) == EOF)
+ key = '\r'
+ call fseti (STDIN, F_RAW, NO)
+
+ # Take the action commanded by the user. At present the morehelp
+ # option merely prints cursor mode help; this is appropriate
+ # because the first waitpage call occurs after printing user help
+ # in response to ? (or after a :.show).
+
+ switch (key) {
+ case 'q':
+ # Quit, do not clear graphics and redraw.
+ if (TR_PAGE(tr) == NO) {
+ # If screen paging is disabled (text drawn underneath
+ # transparent graphics overlay), clear the text frame
+ # only, using the clear line function.
+
+ do i = 1, ttystati (tty, TTY_NLINES) {
+ call ttygoto (fd, tty, 1, i)
+ call ttyclearln (fd, tty)
+ }
+ } else
+ call ttyclearln (fd, tty)
+
+ call flush (fd)
+ call gki_reactivatews (stream, 0)
+ break
+
+ case '\r', '\n':
+ # Quit, clear graphics and redraw.
+ call ttyclearln (fd, tty)
+ call flush (fd)
+ call gki_reactivatews (stream, 0)
+ call gtr_redraw (stream)
+ break
+
+ case ' ':
+ # Print cursor mode help.
+ iferr (call pagefile (KEYSFILE, "cursor mode help"))
+ call erract (EA_WARN)
+
+ default:
+ # Illegal keystroke.
+ call printf ("\007")
+ call flush (STDOUT)
+ }
+ }
+
+ call ttycdes (tty)
+end
diff --git a/sys/gio/cursor/gtrwcur.x b/sys/gio/cursor/gtrwcur.x
new file mode 100644
index 00000000..9def0a67
--- /dev/null
+++ b/sys/gio/cursor/gtrwcur.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gki.h>
+
+# GTR_WRITECURSOR -- Write the graphics cursor position in NDC coordinates.
+
+procedure gtr_writecursor (fd, x, y)
+
+int fd # graphics stream
+real x, y # NDC coords of cursor
+
+int mx, my
+
+begin
+ mx = max(0, min(GKI_MAXNDC, nint (x * GKI_MAXNDC)))
+ my = max(0, min(GKI_MAXNDC, nint (y * GKI_MAXNDC)))
+
+ call gki_setcursor (fd, mx, my, 0)
+end
diff --git a/sys/gio/cursor/gtrwritep.x b/sys/gio/cursor/gtrwritep.x
new file mode 100644
index 00000000..d1a3fd4a
--- /dev/null
+++ b/sys/gio/cursor/gtrwritep.x
@@ -0,0 +1,68 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+# GTR_WRITEP -- Virtually write (append) to the graphics frame buffer. Return a
+# pointer to the start of the area reserved for the data and advance the
+# output pointer beyond the new data area. The use of a buffer pointer here
+# yields a very efficient graphics i/o dataflow. For the stdgraph kernel,
+# XMIT (pr_psio) places a block of metacode directly in the frame buffer at
+# the memory location we point to. GIOTR is then called to process the new
+# data block. GIOTR calls GTR_FETCH, which "fetches" the next instruction
+# by merely returning a pointer into the frame buffer. The stdgraph kernel
+# is then called to execute the instruction. Hence in the simple case, there
+# are no memory to memory copies and the contents of an instruction are
+# touched only by the kernel.
+
+pointer procedure gtr_writep (fd, nchars)
+
+int fd # graphics stream
+int nchars # nchars to reserve at end of buffer
+
+pointer tr, bufp, top, segp
+int blen, nwords, ip_offset, op_offset
+errchk syserr, realloc
+include "gtr.com"
+
+begin
+ tr = trdes[fd]
+ if (tr == NULL)
+ call syserr (SYS_GWRITEP)
+
+ nwords = nchars / SZ_SHORT
+ bufp = TR_FRAMEBUF(tr)
+ blen = TR_LENFRAMEBUF(tr)
+ segp = TR_OP(tr) # pointer to next segment
+ top = bufp + blen
+
+ # Make space available in the buffer. We must always allocate the
+ # requested space, even if the result is a buffer larger than the
+ # (soft) maximum size permitted. Buffer space will be returned
+ # after GIOTR processes the new instructions if the buffer grows
+ # too large.
+
+ if (nwords > top - segp) {
+ # Note that realloc may move the buffer, hence we must adjust any
+ # pointers into the buffer after the call to realloc.
+
+ ip_offset = TR_IP(tr) - bufp
+ op_offset = segp - bufp
+ blen = blen + max (INC_LENFRAMEBUF, nwords)
+
+ call realloc (bufp, blen, TY_SHORT)
+
+ TR_FRAMEBUF(tr) = bufp
+ TR_LENFRAMEBUF(tr) = blen
+ TR_IP(tr) = bufp + ip_offset
+ segp = bufp + op_offset
+ }
+
+ TR_OP(tr) = segp + nwords
+ TR_LASTOP(tr) = TR_OP(tr)
+
+ return (segp)
+end
diff --git a/sys/gio/cursor/gtrwsclip.x b/sys/gio/cursor/gtrwsclip.x
new file mode 100644
index 00000000..3a0a384b
--- /dev/null
+++ b/sys/gio/cursor/gtrwsclip.x
@@ -0,0 +1,144 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GTR_POLYCLIP -- Clip a convex polygon to a box. If the polygon is entirely
+# outside the box 0 is returned; if the polygon is entirely within the box 1
+# is returned, otherwise the polygon is clipped and a value other than 0 or 1
+# is returned. This is based on code by Paul Heckbert from Graphics Gems,
+# 1985/1989.
+
+int procedure gtr_polyclip (pv, npts, x1, x2, y1, y2)
+
+short pv[ARB] #U polygon to be clipped
+int npts #U number of points in polygon
+int x1,x2,y1,y2 #I clipping box
+
+pointer sp, p1, p2, pt
+int x1out, x2out, y1out, y2out, i
+int gtr_cliptoplane()
+define nopts_ 91
+
+begin
+ x1out = 0; x2out = 0
+ y1out = 0; y2out = 0
+
+ # Count vertices which are outside with respect to each of the
+ # four planes.
+
+ do i = 1, npts*2, 2 {
+ if (pv[i+0] < x1) x1out = x1out + 1
+ if (pv[i+0] > x2) x2out = x2out + 1
+ if (pv[i+1] < y1) y1out = y1out + 1
+ if (pv[i+1] > y2) y2out = y2out + 1
+ }
+
+ # Is the polygon entirely inside the clipping box?
+ if (x1out + x2out + y1out + y2out == 0)
+ return (1)
+
+ # Is the polygon entirely outside the clipping box?
+ if (x1out == npts || x2out == npts || y1out == npts || y2out == npts)
+ return (0)
+
+ # If we get here the polygon partially intersects the clipping box.
+ # Clip against each of the planes that might cut the polygon, clipping
+ # the previously clipped polygon in each step. This is done in
+ # floating point to minimize accumulation of error when interpolating
+ # to the clipping plane to compute a new polygon vertex when the plane
+ # is crossed.
+
+ call smark (sp)
+ call salloc (p1, npts * 4, TY_REAL)
+ p2 = p1 + npts * 2
+
+ call achtsr (pv, Memr[p1], npts * 2)
+
+ if (x1out > 0)
+ if (gtr_cliptoplane (p1, p2, npts, 0, -1.0, real(x1)) == 0)
+ goto nopts_
+ else {
+ pt = p1; p1 = p2; p2 = pt
+ }
+ if (x2out > 0)
+ if (gtr_cliptoplane (p1, p2, npts, 0, 1.0, real(x2)) == 0)
+ goto nopts_
+ else {
+ pt = p1; p1 = p2; p2 = pt
+ }
+ if (y1out > 0)
+ if (gtr_cliptoplane (p1, p2, npts, 1, -1.0, real(y1)) == 0)
+ goto nopts_
+ else {
+ pt = p1; p1 = p2; p2 = pt
+ }
+ if (y2out > 0)
+ if (gtr_cliptoplane (p1, p2, npts, 1, 1.0, real(y2)) == 0)
+ goto nopts_
+ else {
+ pt = p1; p1 = p2; p2 = pt
+ }
+
+ call achtrs (Memr[p1], pv, npts * 2)
+ call sfree (sp)
+ return (npts)
+
+nopts_
+ call sfree (sp)
+ return (0)
+end
+
+
+# GTR_CLIPTOPLANE -- Clip the convex polygon P1 against a plane, copying
+# the inbounds portion to the output polygon P2.
+
+int procedure gtr_cliptoplane (p1, p2, npts, index, s, ref)
+
+pointer p1 #I pointer to input polygon
+pointer p2 #I pointer to output polygon
+int npts #U number of polygon points or vertices
+int index #I index of coordinate to be tested
+real s #I sign for comparison
+real ref #I value to compare against
+
+int nout, i
+pointer op, u, v
+real tu, tv, t
+
+begin
+ nout = 0
+ op = p2
+
+ u = p1 + (npts - 1) * 2
+ tu = s * Memr[u+index] - ref
+ v = p1
+
+ do i = 1, npts {
+ # On old polygon P1, U is previous vertex, V is current vertex,
+ # TV is negative if vertex V is in.
+
+ tv = s * Memr[v+index] - ref
+
+ if (! ((tu <= 0 && tv <= 0) || (tu > 0 && tv > 0))) {
+ # Edge crosses plane; add intersection point to P2.
+ t = tu / (tu - tv)
+ Memr[op+0] = Memr[u+0] + t * (Memr[v+0] - Memr[u+0])
+ Memr[op+1] = Memr[u+1] + t * (Memr[v+1] - Memr[u+1])
+ nout = nout + 1
+ op = op + 2
+ }
+
+ if (tv <= 0) {
+ # Vertex V is in, copy it out.
+ Memr[op+0] = Memr[v+0]
+ Memr[op+1] = Memr[v+1]
+ nout = nout + 1
+ op = op + 2
+ }
+
+ u = v
+ tu = tv
+ v = v + 2
+ }
+
+ npts = nout
+ return (nout)
+end
diff --git a/sys/gio/cursor/gtrwstran.x b/sys/gio/cursor/gtrwstran.x
new file mode 100644
index 00000000..9262e00a
--- /dev/null
+++ b/sys/gio/cursor/gtrwstran.x
@@ -0,0 +1,490 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+
+define MOVE 0
+define DRAW 1
+define LEFT 0
+define RIGHT 1
+define BELOW 0
+define ABOVE 1
+define INSIDE 2
+define FIRSTPT GKI_POLYLINE_P
+
+
+# GTR_WSTRAN -- Apply the workstation transformation to an instruction and
+# write the transformed instruction to the graphics kernel. The transformation
+# parameters etc. should have been initialized in the gtr common before we
+# are called.
+
+procedure gtr_wstran (gki)
+
+short gki[ARB] #I metacode instruction to be spooled
+
+long x, y
+pointer sp, buf
+int length, npts, data
+int gtr_polyclip()
+bool sge_wsenable()
+include "gtr.com"
+
+begin
+ # Check with the graphics kernel to see if scaling of graphics
+ # instructions is enabled (it is disabled if the graphics device is
+ # already doing it for us).
+
+ if (!sge_wsenable()) {
+ call gki_write (tr_stream, gki)
+ return
+ }
+
+ switch (gki[GKI_HDR_OPCODE]) {
+ case GKI_FILLAREA:
+ npts = gki[GKI_FILLAREA_N]
+ data = GKI_FILLAREA_P
+ length = gki[GKI_HDR_LENGTH]
+ call amovs (gki, pl, length)
+
+ switch (gtr_polyclip (pl[data], npts, mx1, mx2, my1, my2)) {
+ case 0:
+ # Entire instruction out of bounds.
+ case 1:
+ # Entire instruction in bounds.
+ pl_op = GKI_POLYLINE_P + npts * 2
+ call gpt_flush()
+ default:
+ # Instruction has been clipped.
+ pl_op = GKI_POLYLINE_P + npts * 2
+ call gpt_flush()
+ }
+
+ case GKI_POLYLINE, GKI_POLYMARKER:
+ call gtr_polytran (gki)
+
+ case GKI_SETCURSOR:
+ length = gki[GKI_HDR_LENGTH]
+ call smark (sp)
+ call salloc (buf, length, TY_SHORT)
+
+ # Move cursor to edge of screen if point referenced is out of
+ # bounds.
+
+ call amovs (gki, Mems[buf], length)
+ x = gki[GKI_SETCURSOR_POS]
+ y = gki[GKI_SETCURSOR_POS+1]
+ call gtr_ctran (x, y, x, y)
+ Mems[buf+GKI_SETCURSOR_POS-1] = x
+ Mems[buf+GKI_SETCURSOR_POS] = y
+ call gki_write (tr_stream, Mems[buf])
+
+ call sfree (sp)
+
+ case GKI_TEXT:
+ length = gki[GKI_HDR_LENGTH]
+ call smark (sp)
+ call salloc (buf, length, TY_SHORT)
+
+ # Discard text drawing instruction if the point referenced is
+ # out of bounds. If in bounds, transform coordinates and draw
+ # at the transformed point.
+
+ call amovs (gki, Mems[buf], length)
+ x = gki[GKI_TEXT_P]
+ y = gki[GKI_TEXT_P+1]
+ if (x >= mx1 && x <= mx2 && y >= my1 && y <= my2) {
+ call gtr_ctran (x, y, x, y)
+ Mems[buf+GKI_TEXT_P-1] = x
+ Mems[buf+GKI_TEXT_P] = y
+ call gki_write (tr_stream, Mems[buf])
+ }
+
+ call sfree (sp)
+
+ case GKI_PUTCELLARRAY:
+ # Just filter these out for now.
+
+ default:
+ call gki_write (tr_stream, gki)
+ }
+end
+
+
+# GTR_CTRAN -- Apply the workstation transform to a set of GKI coordinates,
+# i.e., transform raw GKI coords to screen coords in GKI units.
+
+procedure gtr_ctran (mx, my, sx, sy)
+
+int mx, my # raw GKI coordinates
+int sx, sy # screen coordinates in GKI units
+include "gtr.com"
+
+begin
+ sx = max(0, min(GKI_MAXNDC, nint ((mx - mx1) * xscale + xorigin)))
+ sy = max(0, min(GKI_MAXNDC, nint ((my - my1) * yscale + yorigin)))
+end
+
+
+# GTR_POLYTRAN -- Scale a polyline, polymarker, or fill area instruction
+# by applying the workstation transformation. The workstation transformation
+# scales vectors in a viewport defined in NDC(GKI) space to fit the full
+# device screen. Vectors or segments of vectors lying outside the viewport
+# are clipped at the screen boundary.
+
+procedure gtr_polytran (gki)
+
+short gki[ARB] # gki instruction to be transformed
+long mx, my
+int last_ip, opcode, i, ip
+bool inbounds, otherside, points
+int gpt_firstpt()
+include "gtr.com"
+
+begin
+ last_ip = gki[GKI_HDR_LENGTH]
+ opcode = gki[GKI_HDR_OPCODE]
+ points = (opcode == GKI_POLYMARKER)
+
+ # In the process of clipping a polyline may be broken into several
+ # smaller polylines (or polymarkers or fillareas, all of which are
+ # very similar at the instruction level). We store the GKI header
+ # in the first few words of the PL array so that when the transformed
+ # polyline is broken it is ready for execution.
+
+ do i = 1, GKI_POLYLINE_P - 1
+ pl[i] = gki[i]
+ pl_op = GKI_POLYLINE_P
+
+ # Clip all points until either a point is encountered which is inbounds
+ # or which is on the other side of the viewport (in either axis). This
+ # is a fast way of clipping polylines which are mostly out of bounds.
+ # Return immediately if the entire vector is out of bounds.
+
+ otherside = true
+ ip = FIRSTPT
+ if (gpt_firstpt (gki, ip, last_ip) <= 0)
+ return
+
+ # Set initial position.
+ cx = gki[ip]
+ cy = gki[ip+1]
+
+ # Clip the remaining points. Clipping is performed in GKI coordinates.
+ # The workstation transformation is not applied until the clipped
+ # vector is output.
+
+ for (ip=ip+2; ip < last_ip; ip=ip+2) {
+ mx = gki[ip]
+ my = gki[ip+1]
+
+ # Check to see if this is the first point of a new polyline.
+ # If so we must set the first physical point in the output
+ # polyline to the current position, making the current point
+ # the second physical point of the output polyline.
+
+ if (pl_op <= GKI_POLYLINE_P) {
+ # Place the current pen position in the polyline as the
+ # first point if it is inbounds.
+
+ if (cy <= my2 && cy >= my1 && cx <= mx2 && cx >= mx1) {
+ last_point_inbounds = true
+ pl[pl_op] = cx
+ pl_op = pl_op + 1
+ pl[pl_op] = cy
+ pl_op = pl_op + 1
+ } else {
+ last_point_inbounds = false
+ do i = 1, 4 {
+ xs[i] = cx
+ ys[i] = cy
+ }
+ }
+ }
+
+ # Update the current position.
+
+ cx = mx
+ cy = my
+
+ # Clip at the edge of the device screen.
+
+ inbounds = (my <= my2 && my >= my1 && mx <= mx2 && mx >= mx1)
+
+ if (inbounds && (last_point_inbounds || points)) {
+ # Add point to polyline (the fast way).
+ pl[pl_op] = mx
+ pl_op = pl_op + 1
+ pl[pl_op] = my
+ pl_op = pl_op + 1
+
+ } else if ((inbounds||last_point_inbounds||otherside) && !points) {
+ # Clip at viewport boundary.
+
+ if (last_point_inbounds) {
+ # Update coords of last point drawn (necessary since we did
+ # not use the clipping code for inbounds points).
+ do i = 1, 4 {
+ xs[i] = pl[pl_op-2]
+ ys[i] = pl[pl_op-1]
+ }
+ }
+ call gpt_clipl (DRAW, mx, my)
+ otherside = false
+
+ } else {
+ # Both points are out of bounds. Scan along until a point is
+ # found which is again in bounds, or which is on the other side
+ # of the viewport, requiring clipping across the viewport.
+
+ if (gpt_firstpt (gki, ip, last_ip) > 0) {
+ do i = 1, 4 {
+ xs[i] = gki[ip]
+ ys[i] = gki[ip+1]
+ }
+ cx = gki[ip]
+ cy = gki[ip+1]
+ }
+
+ otherside = true
+ inbounds = false
+ }
+
+ last_point_inbounds = inbounds
+ }
+
+ call gpt_flush()
+end
+
+
+# GPT_FIRSTPT -- Scan a vector and return the index of the next good point.
+# A good point is a point which is either inbounds or which preceeds a point
+# which is either inbounds or on the other side of the viewport, necessitating
+# clipping across the viewport.
+
+int procedure gpt_firstpt (gki, ip, last_ip)
+
+short gki[ARB] # vector being clipped
+int last_ip # last legal value of ip
+int ip # starting index
+
+int mx, my, i
+int first_ip, new_ip
+include "gtr.com"
+
+begin
+ mx = gki[ip]
+ my = gki[ip+1]
+ first_ip = ip
+ new_ip = last_ip
+
+ if (mx < mx1) {
+ do i=ip+2, last_ip, 2
+ if (gki[i] >= mx1) {
+ new_ip = i
+ break
+ }
+ } else if (mx > mx2) {
+ do i=ip+2, last_ip, 2
+ if (gki[i] <= mx2) {
+ new_ip = i
+ break
+ }
+ } else if (my < my1) {
+ do i=ip+3, last_ip, 2
+ if (gki[i] >= my1) {
+ new_ip = i - 1
+ break
+ }
+ } else if (my > my2) {
+ do i=ip+3, last_ip, 2
+ if (gki[i] <= my2) {
+ new_ip = i - 1
+ break
+ }
+ } else
+ return (ip)
+
+ if (new_ip >= last_ip)
+ return (0) # entire vector is indefinite
+ else
+ ip = max (first_ip, new_ip - 2)
+
+ return (ip)
+end
+
+
+# GPT_CLIPL -- Clip at left boundary.
+
+procedure gpt_clipl (pen, mx, my)
+
+int pen # move or draw
+long mx, my # point to be clipped
+long new_my
+int newpen
+include "gtr.com"
+
+begin
+ # Does line cross boundary?
+ if ((mx >= mx1 && xs[1] < mx1) || (mx <= mx1 && xs[1] > mx1)) {
+ if (mx >= mx1)
+ newpen = MOVE
+ else
+ newpen = pen
+ new_my = real(my - ys[1]) * real(mx1 - mx) / real(mx - xs[1]) +
+ my + 0.5
+ call gpt_clipr (newpen, mx1, new_my)
+ }
+
+ xs[1] = mx
+ ys[1] = my
+
+ if (mx >= mx1)
+ call gpt_clipr (pen, mx, my)
+end
+
+
+# GPT_CLIPR -- Clip at right boundary.
+
+procedure gpt_clipr (pen, mx, my)
+
+int pen # move or draw
+long mx, my # point to be clipped
+long new_my
+int newpen
+include "gtr.com"
+
+begin
+ # Does line cross boundary?
+ if ((mx <= mx2 && xs[2] > mx2) || (mx >= mx2 && xs[2] < mx2)) {
+ if (mx <= mx2)
+ newpen = MOVE
+ else
+ newpen = pen
+ new_my = real(my - ys[2]) * real(mx2 - mx) / real(mx - xs[2]) +
+ my + 0.5
+ call gpt_clipb (newpen, mx2, new_my)
+ }
+
+ xs[2] = mx
+ ys[2] = my
+
+ if (mx <= mx2)
+ call gpt_clipb (pen, mx, my)
+end
+
+
+# GPT_CLIPB -- Clip at bottom boundary.
+
+procedure gpt_clipb (pen, mx, my)
+
+int pen # move or draw
+long mx, my # point to be clipped
+long new_mx
+int newpen
+include "gtr.com"
+
+begin
+ # Does line cross boundary?
+ if ((my >= my1 && ys[3] < my1) || (my <= my1 && ys[3] > my1)) {
+ if (my >= my1)
+ newpen = MOVE
+ else
+ newpen = pen
+ new_mx = real(mx - xs[3]) * real(my1 - my) / real(my - ys[3]) +
+ mx + 0.5
+ call gpt_clipt (newpen, new_mx, my1)
+ }
+
+ xs[3] = mx
+ ys[3] = my
+
+ if (my >= my1)
+ call gpt_clipt (pen, mx, my)
+end
+
+
+# GPT_CLIPT -- Clip at top boundary and put the final clipped point(s) in
+# the output polyline. Note that a "move" at this level does not affect
+# the current position (cx,cy), since the vector endpoints have been clipped
+# and the current position vector follows the unclipped vector points input
+# by the user.
+
+procedure gpt_clipt (pen, mx, my)
+
+int pen # move or draw
+long mx, my # point to be clipped
+include "gtr.com"
+
+begin
+ # Does line cross boundary?
+ if ((my <= my2 && ys[4] > my2) || (my >= my2 && ys[4] < my2)) {
+ if (my <= my2 || pen == MOVE)
+ call gpt_flush()
+ pl[pl_op] = real(mx - xs[4]) * real(my2 - my) / real(my - ys[4]) +
+ mx + 0.5
+ pl_op = pl_op + 1
+ pl[pl_op] = my2
+ pl_op = pl_op + 1
+ }
+
+ xs[4] = mx
+ ys[4] = my
+
+ if (my <= my2) {
+ if (pen == MOVE)
+ call gpt_flush()
+ pl[pl_op] = mx
+ pl_op = pl_op + 1
+ pl[pl_op] = my
+ pl_op = pl_op + 1
+ }
+end
+
+
+# GPT_FLUSH -- Flush the buffered "polyline", i.e., array of transformed and
+# clipped points. For a polyline or fill area polygon there must be at least
+# two points (4 cells) or it will be discarded. A single point polymarker is
+# permitted.
+
+procedure gpt_flush()
+
+int npts, i
+long mx, my
+include "gtr.com"
+
+begin
+ if (pl_op >= GKI_POLYLINE_P + 2) {
+ npts = (pl_op - GKI_POLYLINE_P) / 2
+
+ # Apply the workstation transformation.
+ do i = GKI_POLYLINE_P, pl_op, 2 {
+ mx = nint ((pl[i] - mx1) * xscale + xorigin)
+ my = nint ((pl[i+1] - my1) * yscale + yorigin)
+ pl[i] = max(0, min(GKI_MAXNDC, mx))
+ pl[i+1] = max(0, min(GKI_MAXNDC, my))
+ }
+
+ switch (pl[GKI_HDR_OPCODE]) {
+ case GKI_POLYMARKER:
+ pl[GKI_POLYMARKER_L] = pl_op - 1
+ pl[GKI_POLYMARKER_N] = npts
+ call gki_write (tr_stream, pl)
+
+ case GKI_FILLAREA:
+ pl[GKI_FILLAREA_L] = pl_op - 1
+ pl[GKI_FILLAREA_N] = npts
+ call gki_write (tr_stream, pl)
+
+ default:
+ if (npts >= 2) {
+ pl[GKI_POLYLINE_L] = pl_op - 1
+ pl[GKI_POLYLINE_N] = npts
+ call gki_write (tr_stream, pl)
+ }
+ }
+
+ pl_op = GKI_POLYLINE_P
+ }
+end
diff --git a/sys/gio/cursor/mkpkg b/sys/gio/cursor/mkpkg
new file mode 100644
index 00000000..f6a79332
--- /dev/null
+++ b/sys/gio/cursor/mkpkg
@@ -0,0 +1,57 @@
+# Make the CURSOR package.
+
+$checkout libcur.a lib$
+$update libcur.a
+$checkin libcur.a lib$
+$exit
+
+libcur.a:
+ # $set xflags = "$(xflags) -qfx"
+
+ giotr.x gtr.com gtr.h <config.h> <gio.h> <gki.h> <xwhen.h>
+ grcaxes.x grc.h gtr.com gtr.h <gio.h> <gset.h>
+ grcclose.x grc.h gtr.h <gio.h>
+ grccmd.x grc.h gtr.h <ctype.h> <fset.h> <gio.h> <gki.h>\
+ <gset.h> <mach.h> <ttyset.h>
+ grcinit.x grc.h <gio.h>
+ grcopen.x grc.h gtr.com gtr.h <gio.h> <gki.h>
+ grcpl.x gtr.h <gio.h> <gki.h> <gset.h> grc.h
+ grcread.x gtr.h <fset.h> <gio.h>
+ grcredraw.x grc.h <gio.h>
+ grcscr.x gtr.com gtr.h <gio.h> <gki.h>
+ grcstatus.x grc.h gtr.com gtr.h <gio.h>
+ grctext.x gtr.h <gio.h> <gki.h> <gset.h> grc.h
+ grcwarn.x
+ grcwcs.x grc.h gtr.h <gio.h> <gki.h> <mach.h>
+ grcwrite.x grc.h gtr.h <fset.h> <gio.h>
+ gtrbackup.x gtr.com gtr.h <gio.h> <gki.h> <gset.h>
+ gtrconn.x
+ gtrctrl.x gtr.com gtr.h <fset.h> <gio.h> <gki.h> <gset.h>\
+ <prstat.h> <config.h>
+ gtrdelete.x gtr.h <gio.h> <gki.h>
+ gtrdiscon.x <gio.h>
+ gtrfetch.x gtr.h <gio.h> <gki.h>
+ gtrframe.x gtr.h <gio.h> <gki.h>
+ gtrgflush.x gtr.com gtr.h <error.h> <gio.h>
+ gtrgtran.x gtr.com gtr.h <gio.h> <gki.h>
+ gtrgtty.x gtr.h <gio.h> <gki.h>
+ gtrinit.x gtr.com gtr.h <gio.h> <gki.h> <gset.h>
+ gtropenws.x gtr.com gtr.h <config.h> <error.h> <fio.h> <prstat.h>\
+ <fset.h> <gio.h> <gki.h>
+ gtrpage.x gtr.h <gio.h> <gset.h>
+ gtrptran.x gtr.com gtr.h <gio.h> <gki.h> <mach.h>
+ gtrrcur.x <gki.h>
+ gtrredraw.x gtr.h <gio.h>
+ gtrreset.x gtr.com gtr.h <error.h> <gio.h>
+ gtrset.x gtr.com gtr.h <gio.h> <gki.h>
+ gtrstatus.x gtr.com gtr.h <fset.h> <gio.h>
+ gtrtrunc.x gtr.h <gio.h> <gki.h>
+ gtrundo.x gtr.com gtr.h <gio.h> <gki.h> <gset.h>
+ gtrwaitp.x grc.h gtr.h <error.h> <fset.h> <gio.h> <ttyset.h>
+ gtrwcur.x <gki.h>
+ gtrwritep.x gtr.com <error.h> <gio.h> <gki.h> gtr.h
+ gtrwstran.x gtr.com gtr.h <gio.h> <gki.h> <mach.h>
+ gtrwsclip.x
+ prpsinit.x
+ rcursor.x grc.h gtr.com gtr.h <ctype.h> <gio.h> <gki.h> <ttset.h>
+ ;
diff --git a/sys/gio/cursor/prpsinit.x b/sys/gio/cursor/prpsinit.x
new file mode 100644
index 00000000..4959deff
--- /dev/null
+++ b/sys/gio/cursor/prpsinit.x
@@ -0,0 +1,15 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PRPSINIT -- Load the gio.cursor graphics driver for pseudofile i/o to the
+# graphics streams.
+
+procedure prpsinit()
+
+extern giotr()
+extern gtr_control(), gtr_gflush(), gtr_writep()
+extern stg_readtty(), stg_writetty()
+
+begin
+ call prpsload (giotr, gtr_control, gtr_gflush, gtr_writep,
+ stg_readtty, stg_writetty)
+end
diff --git a/sys/gio/cursor/rcursor.x b/sys/gio/cursor/rcursor.x
new file mode 100644
index 00000000..cc7dc739
--- /dev/null
+++ b/sys/gio/cursor/rcursor.x
@@ -0,0 +1,692 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <ttset.h>
+include <gio.h>
+include <gki.h>
+include "gtr.h"
+include "grc.h"
+
+define SZ_CHARCON 5
+define MARKLEN 0.01
+
+# Cursor step algorithm parameters.
+
+define MAX_STEP 0.1 # max cursor step size, cursor motions
+define MIN_STEP 0.002 # min cursor step size, cursor motions
+define LARGER_STEP 2.0 # factor by which step size is increased
+define SMALLER_STEP 0.5 # factor by step size is decreased
+define NSTEP 2 # number of steps before larger step
+define MANUAL_STEP 5.0 # gear ratio for F/V cursor control
+define SLOW 1 # for fast/slow algorithm
+define FAST 2
+
+# Zoom parameters.
+
+define X_ZOOMFACTOR 0.5 # zoom factors
+define Y_ZOOMFACTOR 0.5
+#define X_ZOOMFACTOR 0.666 # zoom factors
+#define Y_ZOOMFACTOR 0.666
+
+# Roam factors.
+
+define X_ROAM 0.333 # fraction of the current window
+define Y_ROAM 0.333 # fraction of the current window
+
+
+# RCURSOR -- Read the position of a cursor. This is the main entry point to
+# cursor mode/cursor input from the CL; we are called by the QUERY procedure
+# of the CL when a cursor type parameter is read. The cursor position is
+# returned as a string of the form
+#
+# x y wcs key stringval
+#
+# where the "stringval" field may be absent if not appropriate for a given key.
+# If EOF is returned the cursor value string is undefined.
+
+int procedure rcursor (stream, outstr, maxch)
+
+int stream # graphics stream
+char outstr[ARB] # encoded cursor value (output)
+int maxch
+
+bool cminit
+int xroam[9], yroam[9]
+pointer rc, tr, sp, lbuf, ip
+char charcon[SZ_CHARCON], ch
+real x1, x2, y1, y2, xt, yt, v[10]
+real lx1, lx2, ly1, ly2, aspect_ratio
+real x, y, rx, ry, xw, yw, dx, dy, xc, yc
+int junk, key, nukey, last_zoom, i, wcs, ppos, ucasein, raster
+
+bool ttygetb()
+pointer grc_open()
+int envfind(), ctocc(), oscmd(), gtr_readcursor(), grc_readtty()
+int grc_cursor(), grc_command(), grc_selectwcs(), grc_mapkey(), ttstati()
+real ttygetr()
+
+errchk grc_text, grc_readtty, grc_writecursor
+errchk grc_init, grc_open, grc_command, grc_cursor, grc_message
+errchk grc_readcursor, grc_mapkey, grc_redraw, envfind
+
+data xroam /1,0,-1,1,0,-1,1,0,-1/
+data yroam /1,1,1,0,0,0,-1,-1,-1/
+data rc /NULL/
+define done_ 91
+define coloncmd_ 92
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_COMMAND, TY_CHAR)
+
+ # Allocate and initialize the RCURSOR descriptor.
+ if (rc == NULL) {
+ call grc_init (rc)
+ cminit = true
+ } else
+ cminit = false
+
+ # Open or reopen the graphics kernel.
+ tr = grc_open ("", APPEND, stream, rc)
+
+ # Process CMINIT command string, if present in environment. This is
+ # only done once.
+
+ if (cminit) {
+ if (envfind ("cminit", Memc[lbuf], SZ_COMMAND) > 0) {
+ ip = lbuf
+ while (IS_WHITE(Memc[ip]) || Memc[ip] == '.')
+ ip = ip + 1
+ junk = grc_command (rc, stream, 0.,0.,0,0.,0., Memc[ip])
+ }
+ cminit = false
+ }
+
+ # If the graphics device does not permit input, i.e., does not have
+ # a cursor, return EOF.
+
+ if (!ttygetb (TR_TTY(tr), "in")) {
+ x = 0; y = 0
+ key = EOF
+ goto done_
+ }
+
+ # Determine if input keys are to be mapped to lower case by default,
+ # i.e., ucasein mode has been set for the terminal driver.
+
+ ucasein = ttstati (STDIN, TT_UCASEIN)
+
+ last_zoom = 3
+ ppos = NO
+
+ # Enter cursor mode loop. The loop terminates when a non cursor mode
+ # keystroke is typed.
+
+ while (grc_cursor (rc, stream, key,x,y, raster,rx,ry, ppos) != EOF) {
+ Memc[lbuf] = EOS
+
+ # As a rule, no processing is performed on escaped keys. The only
+ # exception is when ucasein mode is set in the terminal driver,
+ # causing upper case input to be mapped to lower case. This mapping
+ # is disabled in a raw mode cursor read, hence we must perform the
+ # mapping explicitly here, returning a lower case key to the
+ # applications program. Unescaped upper case input keystrokes will
+ # be intercepted by cursor mode when ucasein mode is in effect.
+
+ if (key == '\\') {
+ junk = gtr_readcursor (stream, key, x, y, raster, rx, ry)
+ if (ucasein == YES && IS_UPPER(key))
+ key = TO_LOWER (key)
+ break
+ }
+
+ # Map keystroke. If the keystroke maps to a null value the key
+ # is not recognized as a cursor mode keystroke and we exit.
+
+ if (grc_mapkey (rc, key, nukey) == NULL)
+ break
+
+ switch (nukey) {
+ case 'M':
+ # Move the feature under the cursor to the center of the
+ # screen without changing the scaling.
+
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, x1, x2, y1, y2)
+ xw = (x2 - x1) / 2.
+ yw = (y2 - y1) / 2.
+ call gtr_ptran (stream, xc-xw, xc+xw, yc-yw, yc+yw)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call grc_restorecurpos (stream, xc, yc)
+
+ case 'Z':
+ # Zoom in in both X and Y.
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, x1, x2, y1, y2)
+ xw = (x2 - x1) * X_ZOOMFACTOR / 2.
+ yw = (y2 - y1) * Y_ZOOMFACTOR / 2.
+ call gtr_ptran (stream, xc-xw, xc+xw, yc-yw, yc+yw)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call grc_restorecurpos (stream, xc, yc)
+ last_zoom = 3
+
+ case 'X':
+ # Zoom in in X.
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, x1, x2, y1, y2)
+ xw = (x2 - x1) * X_ZOOMFACTOR / 2.
+ call gtr_ptran (stream, xc-xw, xc+xw, y1, y2)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call grc_restorecurpos (stream, xc, yc)
+ last_zoom = 1
+
+ case 'Y':
+ # Zoom in in Y.
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, x1, x2, y1, y2)
+ yw = (y2 - y1) * Y_ZOOMFACTOR / 2.
+ call gtr_ptran (stream, x1, x2, yc-yw, yc+yw)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call grc_restorecurpos (stream, xc, yc)
+ last_zoom = 2
+
+ case '>':
+ # Zoom in in Y by setting the upper limit of the viewport
+ # to the cursor Y position.
+
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, lx1, lx2, ly1, ly2)
+ call gtr_ptran (stream, lx1, lx2, ly1, yc)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call gtr_writecursor (stream, x, 0.5)
+ last_zoom = 'E'
+
+ case '<':
+ # Zoom in in Y by setting the lower limit of the viewport
+ # to the cursor Y position.
+
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, lx1, lx2, ly1, ly2)
+ call gtr_ptran (stream, lx1, lx2, yc, ly2)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call gtr_writecursor (stream, x, 0.5)
+ last_zoom = 'E'
+
+ case 'E':
+ # Expand by marking corners of new viewport. If the range is
+ # small in either X or Y only the other axis will be expanded.
+
+ call gtr_gtran (stream, lx1, lx2, ly1, ly2)
+ call grc_scrtondc (x, y, x1, y1)
+ call grc_message (stream, "again:")
+ junk = grc_cursor (rc, stream, key,x2,y2, raster,rx,ry, ppos)
+ call grc_scrtondc (x2, y2, x2, y2)
+
+ if (x1 > x2)
+ { xt = x2; x2 = x1; x1 = xt }
+ if (y1 > y2)
+ { yt = y2; y2 = y1; y1 = yt }
+
+ if (abs (x1 - x2) < .01)
+ call gtr_ptran (stream, lx1, lx2, y1, y2)
+ else if (abs (y1 - y2) < .01)
+ call gtr_ptran (stream, x1, x2, ly1, ly2)
+ else
+ call gtr_ptran (stream, x1, x2, y1, y2)
+
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call gtr_writecursor (stream, 0.5, 0.5)
+ last_zoom = 'E'
+
+ case 'P':
+ # Zoom out.
+ call grc_scrtondc (x, y, xc, yc)
+ call gtr_gtran (stream, x1, x2, y1, y2)
+
+ if (last_zoom == 'E') {
+ call gtr_ptran (stream, lx1, lx2, ly1, ly2)
+ lx1 = x1; lx2 = x2; ly1 = y1; ly2 = y2
+ } else {
+ if (last_zoom == 1 || last_zoom == 3) {
+ xw = (x2 - x1) / X_ZOOMFACTOR / 2.
+ x1 = xc - xw
+ x2 = xc + xw
+ }
+ if (last_zoom == 2 || last_zoom == 3) {
+ yw = (y2 - y1) / Y_ZOOMFACTOR / 2.
+ y1 = yc - yw
+ y2 = yc + yw
+ }
+ call gtr_ptran (stream, x1, x2, y1, y2)
+ }
+
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ call grc_restorecurpos (stream, xc, yc)
+
+ case 'W':
+ # Select and fix WCS to be used for scr->wcs coordinate
+ # transformations.
+
+ call grc_scrtondc (x, y, xc, yc)
+ TR_WCS(tr) = grc_selectwcs (tr, raster, xc, yc)
+
+ case 'C':
+ # Running tally of cursor position.
+ #if (ppos == NO) {
+ # call grc_pcursor (stream, x, y, raster, rx, ry)
+ # ppos = YES
+ #} else {
+ # call grc_message (stream, "\n\n")
+ # ppos = NO
+ #}
+
+ call grc_pcursor (stream, x, y, raster, rx, ry)
+
+ case 'D':
+ # Draw a line by marking the endpoints.
+ call grc_scrtondc (x, y, v[1], v[2])
+ call grc_message (stream, "again:")
+ junk = grc_cursor (rc, stream, key,x2,y2, raster,rx,ry, ppos)
+ call grc_scrtondc (x2, y2, v[3], v[4])
+ call grc_polyline (stream, v, 2)
+
+ case 'T':
+ # Draw a text string.
+ if (grc_readtty (stream, "text: ", Memc[lbuf], SZ_COMMAND) <= 0)
+ next
+ call grc_scrtondc (x, y, xc, yc)
+ call grc_text (stream, xc, yc, Memc[lbuf])
+
+ case 'A':
+ # Draw and label the axes of the viewport.
+ call grc_axes (stream, x, y, raster, rx, ry)
+
+ case 'B':
+ # Backup one instruction in the frame buffer.
+ call gtr_backup (stream)
+
+ case 'U':
+ # Undo the last frame buffer edit.
+ call gtr_undo (stream)
+
+ case 'R':
+ # Redraw the screen.
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+
+ case '0':
+ # Reset and redraw.
+ call gtr_ptran (stream, 0., 1., 0., 1.)
+ call gtr_writecursor (stream, .5, .5)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+
+ case '5':
+ # Redraw (null roam request).
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+
+ case '1','2','3','4','6','7','8','9':
+ # Roam.
+ i = TO_INTEG (key)
+ if (xroam[i] != 0 || yroam[i] != 0) {
+ call gtr_gtran (stream, x1, x2, y1, y2)
+ dx = (x2 - x1) * X_ROAM * xroam[i]
+ dy = (y2 - y1) * Y_ROAM * yroam[i]
+ call gtr_ptran (stream, x1+dx, x2+dx, y1+dy, y2+dy)
+ call grc_redraw (rc, stream, x, y, raster, rx, ry)
+ }
+
+ case ':':
+ # Enter a colon command string and terminate cursor mode.
+
+ # Get the string value.
+ if (grc_readtty (stream, ":", Memc[lbuf], SZ_COMMAND) <= 0)
+ next
+
+ # All cursor mode commands must begin with a ".". An osescape
+ # begins with an "!".
+
+ if (Memc[lbuf] == '!') {
+ call gtr_page (STDERR, stream)
+ if (oscmd (Memc[lbuf+1], "", "", "") == ERR)
+ call fprintf (STDERR, "\7")
+ call gtr_waitpage (STDERR, stream)
+
+ } else if (Memc[lbuf] == '.') {
+ # Save viewport for 'P'.
+coloncmd_
+ call gtr_gtran (stream, lx1, lx2, ly1, ly2)
+ last_zoom = 'E'
+
+ TR_WAITPAGE(tr) = NO
+ if (grc_command (rc, stream, x, y, raster, rx, ry,
+ Memc[lbuf+1]) == EOF) {
+ key = EOF
+ goto done_
+ }
+
+ # The following is a no-op for most colon commands.
+ if (TR_WAITPAGE(tr) == YES)
+ call gtr_waitpage (STDERR, stream)
+ } else
+ break
+
+ case '=':
+ # Shorthand for :.snap. The latter must be used once to
+ # set the plotter device, else the default stdplot device
+ # will be used.
+
+ call strcpy (".snap", Memc[lbuf], SZ_COMMAND)
+ goto coloncmd_
+
+ default:
+ call fprintf (STDERR, "\007")
+ }
+ }
+
+ # Mark the cursor position if markcur enabled.
+ if (RC_MARKCUR(rc) == YES && key != EOF) {
+ call grc_scrtondc (x, y, xc, yc)
+ aspect_ratio = ttygetr (TR_TTY(tr), "ar")
+ if (aspect_ratio < .001)
+ aspect_ratio = 1.0
+
+ v[1] = xc - MARKLEN * aspect_ratio
+ v[2] = yc
+ v[3] = xc + MARKLEN * aspect_ratio
+ v[4] = yc
+ v[5] = xc
+ v[6] = yc
+ v[7] = xc
+ v[8] = yc - MARKLEN
+ v[9] = xc
+ v[10] = yc + MARKLEN
+ call grc_polyline (stream, v, 5)
+ }
+
+ # Close the workstation, leave graphics mode, position alpha cursor to
+ # lower left corner of graphics terminal.
+
+ call grc_close (stream, rc)
+
+ # Encode the cursor value as a string for the CL.
+done_
+ if (key != EOF) {
+ if (key == ' ')
+ call strcpy ("\\40", charcon, SZ_CHARCON)
+ else {
+ ch = char (key)
+ junk = ctocc (ch, charcon, SZ_CHARCON)
+ }
+ call grc_scrtowcs (stream, x, y, raster, rx, ry, xc, yc, wcs)
+
+ call sprintf (outstr, maxch, "%g %g %d %s %s\n")
+ call pargr (xc)
+ call pargr (yc)
+ call pargi (wcs)
+ call pargstr (charcon)
+ call pargstr (Memc[lbuf])
+ } else
+ outstr[1] = EOS
+
+ call sfree (sp)
+ return (key)
+end
+
+
+# GRC_CURSOR -- Read the position of a cursor in screen coordinates. Recognizes
+# the cursor movement keystrokes H, J, K, and L, exiting only when some other
+# keystroke is received. The cursor movement algorithm is initialized upon
+# entry. Two algorithms are provided for controlling the cursor step size.
+# The first algorithm (automatic control) starts with a large initial step
+# size. In the vicinity of a feature the cursor will overshoot the feature
+# and the user will step back in the opposite direction, causing the step size
+# to be decreased, rapidly converging to the desired position. Several steps
+# in the same direction cause the large step size to be restored. The second
+# algorithm (manual control) uses the F and V keys to directly control the step
+# size.
+
+int procedure grc_cursor (rc, stream, key, x, y, raster, rx, ry, ppos)
+
+pointer rc #I rcursor descriptor
+int stream #I graphics stream
+int key #O keystroke typed
+real x, y #O cursor screen coordinates
+int raster #O raster number
+real rx, ry #O cursor raster coordinates
+int ppos #I print cursor position flag
+
+int speed
+int xdir, ydir, nukey
+real xstep, ystep, newx, newy
+
+bool ttygetb()
+pointer gtr_gtty()
+int gtr_readcursor(), grc_mapkey()
+errchk gtr_readcursor, gtr_writecursor
+
+begin
+ # Reset the cursor step size to the default.
+ xstep = MAX_STEP
+ ystep = MAX_STEP
+ xdir = 0
+ ydir = 0
+ speed = 0
+
+ while (gtr_readcursor (stream, key, x, y, raster, rx, ry) != EOF) {
+ if (grc_mapkey (rc, key, nukey) == NULL)
+ break
+
+ newx = x
+ newy = y
+
+ switch (nukey) {
+ case 'F':
+ # Faster.
+ xstep = min (MAX_STEP, xstep * MANUAL_STEP)
+ ystep = min (MAX_STEP, ystep * MANUAL_STEP)
+ speed = FAST
+
+ case 'V':
+ # Slower.
+ xstep = max (MIN_STEP, xstep / MANUAL_STEP)
+ ystep = max (MIN_STEP, ystep / MANUAL_STEP)
+ speed = SLOW
+
+ case 'H':
+ # Step cursor left.
+ if (speed == 0)
+ if (xdir < -NSTEP) {
+ xstep = MAX_STEP
+ xdir = -1
+ } else if (xdir > 0) {
+ xstep = max (MIN_STEP, xstep * SMALLER_STEP)
+ xdir = -1
+ } else
+ xdir = xdir - 1
+ newx = newx - xstep
+ call gtr_writecursor (stream, newx, newy)
+
+ case 'J':
+ # Step cursor down.
+ if (speed == 0)
+ if (ydir < -NSTEP) {
+ ystep = MAX_STEP
+ ydir = -1
+ } else if (ydir > 0) {
+ ystep = max (MIN_STEP, ystep * SMALLER_STEP)
+ ydir = -1
+ } else
+ ydir = ydir - 1
+ newy = newy - ystep
+ call gtr_writecursor (stream, newx, newy)
+
+ case 'K':
+ # Step cursor up.
+ if (speed == 0)
+ if (ydir > NSTEP) {
+ ystep = MAX_STEP
+ ydir = 1
+ } else if (ydir < 0) {
+ ystep = max (MIN_STEP, ystep * SMALLER_STEP)
+ ydir = 1
+ } else
+ ydir = ydir + 1
+ newy = newy + ystep
+ call gtr_writecursor (stream, newx, newy)
+
+ case 'L':
+ # Step cursor right.
+ if (speed == 0)
+ if (xdir > NSTEP) {
+ xstep = MAX_STEP
+ xdir = 1
+ } else if (xdir < 0) {
+ xstep = max (MIN_STEP, xstep * SMALLER_STEP)
+ xdir = 1
+ } else
+ xdir = xdir + 1
+ newx = newx + xstep
+ call gtr_writecursor (stream, newx, newy)
+
+ default:
+ break
+ }
+
+ # We assume the cursor may have moved if the WC capability exists
+ # for this device.
+
+ if (ttygetb (gtr_gtty (stream), "WC")) {
+ x = newx
+ y = newy
+ }
+
+ # Print the cursor position.
+ if (ppos == YES)
+ call grc_pcursor (stream, x, y, raster, rx, ry)
+ }
+
+ return (key)
+end
+
+
+# GRC_MAPKEY -- Map keystroke. If the keystroke maps to a null value the key
+# is not recognized as a cursor mode keystroke and we exit. Note that if case
+# sensitivity is disabled, KEYS comparisions must be made in upper case but
+# only lower case is to be returned to the calling program.
+
+int procedure grc_mapkey (rc, key, nukey)
+
+pointer rc #I rcursor descriptor
+int key #U raw key value
+int nukey #O mapped key value
+
+begin
+ nukey = max(1, min(MAX_KEYS, key))
+ if (RC_CASE(rc) == NO && IS_LOWER(nukey))
+ nukey = TO_UPPER(nukey)
+
+ nukey = RC_KEYS(rc,nukey)
+ if (nukey == NULL) {
+ # Not a cursor mode key.
+ if (RC_CASE(rc) == NO && IS_UPPER(nukey))
+ key = TO_LOWER(key)
+ } else if (IS_LOWER(nukey))
+ nukey = TO_UPPER(nukey)
+
+ return (nukey)
+end
+
+
+# GRC_RESTORECURPOS -- Restore the cursor position in NDC coordinates
+# regardless of the current workstation transformation.
+
+procedure grc_restorecurpos (stream, x, y)
+
+int stream # graphics stream
+real x, y # new cursor position in NDC coords
+real sx, sy
+include "gtr.com"
+
+begin
+ call grc_ndctoscr (x, y, sx, sy)
+ call gtr_writecursor (stream, sx, sy)
+end
+
+
+# GRC_READTTY -- Read from the terminal via the graphics kernel. If the
+# kernel already has message data buffered we merely return that data,
+# otherwise we issue the prompt given and interactively read the data.
+
+int procedure grc_readtty (stream, prompt, obuf, maxch)
+
+int stream #I graphics stream
+char prompt[ARB] #I prompt, if read is interactive
+char obuf[ARB] #O output buffer
+int maxch #I max chars out
+
+bool issue_prompt
+int nchars, index
+int stg_msglen(), stg_readtty()
+int stridxs(), strlen()
+
+begin
+ issue_prompt = (stg_msglen(STDIN) <= 0)
+ if (issue_prompt)
+ call stg_putline (STDERR, prompt)
+
+ nchars = stg_readtty (STDIN, obuf, maxch)
+ index = stridxs ("\n", obuf)
+ if (index > 0)
+ obuf[index] = EOS
+ nchars = strlen (obuf)
+
+ if (issue_prompt && nchars == 0)
+ call grc_message (stream, "\n\n")
+
+ return (nchars)
+end
+
+
+# GRC_MESSAGE -- Write a message on the status line at the bottom of the
+# screen. If the string is not newline terminated the terminal is left in
+# status line text mode. To clear the status line and force the terminal
+# back into graphics mode, output the string "\n\n".
+
+procedure grc_message (stream, message)
+
+int stream # graphics stream
+char message[ARB] # message to be printed
+
+begin
+ call stg_putline (STDERR, message)
+end
+
+
+# GRC_PCURSOR -- Convert the cursor position in screen coordinates to world
+# coordinates and print on the standard output.
+
+procedure grc_pcursor (stream, sx, sy, raster, rx, ry)
+
+int stream #I graphics stream
+real sx, sy #I screen coords of cursor
+int raster #I raster number
+real rx, ry #I raster coords of cursor
+
+int wcs
+real xc, yc
+pointer sp, lbuf
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ call grc_scrtowcs (stream, sx, sy, raster, rx, ry, xc, yc, wcs)
+ if (abs(xc) > 1 && abs(xc) < 10000 && abs(yc) > 1 && abs(yc) < 10000)
+ call sprintf (Memc[lbuf], SZ_LINE, "%10.3f %10.3f \n")
+ else
+ call sprintf (Memc[lbuf], SZ_LINE, "%12.7g %12.7g \n")
+ call pargr (xc)
+ call pargr (yc)
+
+ call stg_putline (STDERR, Memc[lbuf])
+ call sfree (sp)
+end