aboutsummaryrefslogtreecommitdiff
path: root/unix/sun
diff options
context:
space:
mode:
Diffstat (limited to 'unix/sun')
-rw-r--r--unix/sun/Gterm.hlp198
-rw-r--r--unix/sun/Imtool.hlp420
-rw-r--r--unix/sun/Makefile67
-rw-r--r--unix/sun/README5
-rw-r--r--unix/sun/arrow.c66
-rw-r--r--unix/sun/fifo.c759
-rw-r--r--unix/sun/gterm.c1984
-rw-r--r--unix/sun/gterm.esc46
-rw-r--r--unix/sun/gterm.h18
-rw-r--r--unix/sun/gterm.icon34
-rw-r--r--unix/sun/gterm.icon.OLD34
-rw-r--r--unix/sun/gterm.man784
-rw-r--r--unix/sun/gtermio.c1224
-rw-r--r--unix/sun/halley.lut257
-rw-r--r--unix/sun/heat.lut257
-rw-r--r--unix/sun/imtool.c4488
-rw-r--r--unix/sun/imtool.cross4
-rw-r--r--unix/sun/imtool.cursor4
-rw-r--r--unix/sun/imtool.h13
-rw-r--r--unix/sun/imtool.icon66
-rw-r--r--unix/sun/imtool.icon.NEW34
-rw-r--r--unix/sun/imtool.man713
-rw-r--r--unix/sun/imtool.square4
-rw-r--r--unix/sun/imtoolrc48
-rwxr-xr-xunix/sun/mksuntool.csh39
-rw-r--r--unix/sun/mouse.c47
-rw-r--r--unix/sun/notify_read.c85
-rw-r--r--unix/sun/screendump.c549
-rw-r--r--unix/sun/ss1.patch31
29 files changed, 12278 insertions, 0 deletions
diff --git a/unix/sun/Gterm.hlp b/unix/sun/Gterm.hlp
new file mode 100644
index 00000000..d97b4407
--- /dev/null
+++ b/unix/sun/Gterm.hlp
@@ -0,0 +1,198 @@
+.help gterm Dec86 "Vector Graphics for the Sun Workstation"
+.sp 2
+.ce
+\fBVector Graphics for IRAF on the Sun Workstation\fR
+.ce
+Doug Tody
+.ce
+December, 1986
+.ce
+(design document)
+.sp 3
+.NH
+Graphics Terminal Model
+
+ The window based, native vector graphics facility for Sun/IRAF will be
+implemented as a purely data driven interface based on the concept of an
+intelligent virtual graphics terminal. The virtual graphics terminal will
+be implemented in software as a process running under a window system such
+as Sunview or X.
+
+A virtual terminal interface will allow the Sun workstation to be used as an
+ordinary graphics terminal to access IRAF or other software on either the
+local or a foreign host via any data stream interface, e.g., rlogin, telnet,
+or a modem connection. For example, one will be able to use telnet running
+in a GTERM window to login on a VMS machine, then run IRAF on the VMS machine
+in the normal fashion. Conversely, since the IRAF software on the Sun node
+will always think that it is talking to a conventional data driven graphics
+terminal, the Sun/IRAF software may in fact be run from a conventional
+graphics terminal, via either a direct or remote login. Finally, a pure data
+stream TTY interface where all details of the windowing system are isolated
+into a server process makes it possible for a single version of IRAF to support
+multiple window systems on the same host.
+
+The graphics terminal to be implemented by GTERM shall consist of independent
+text and graphics frames. These will be implemented as separate windows
+(frames) under SunView, allowing them to be moved, resized, exposed, hidden,
+open, closed, etc., independently of each other. Note however that since both
+frames are connected to the controlling process via a single terminal i/o
+connection, the controlling process (e.g., the CL) can only read from or
+write to one frame at a time.
+
+In normal use, the user will work for a time in the text frame, issuing
+commands to run tasks which do not require interactive graphics facilities.
+When a graphics program is run the graphics frame will be opened or exposed
+and the plot drawn. If the graphics program is interactive, i.e., uses
+cursor input, the cursor will be moved to the center of the window and the
+crosshairs turned on to indicate that cursor input is required. A sequence
+of keystroke or mouse button triggered cursor reads will follow, and
+eventually graphics mode will be exited by transmission of the close
+workstation sequence to GTERM by GIO running in the applications program.
+
+When graphics mode is exited GTERM terminal i/o is redirected to the text
+frame and the cursor is restored to its former position in the text frame,
+automatically restoring interactive control to the text frame. The graphics
+frame may or may not be closed or hidden, depending upon the GTERM defaults
+set up by the user. If the graphics frame is to be closed or hidden this
+should probably require interactive confirmation by the user, else the plot
+might disappear immediately after being drawn. Since control naturally
+toggles back and forth between the text and graphics frames and since screen
+space is always limited and a large, high resolution graph is generally
+desired, it is expected that the graphics frame will normally be displayed
+only while in graphics mode.
+
+.NH
+GTERM Graphics Language
+
+ The tektronix graphics language is a simple and efficient standard language
+for graphics terminal i/o which will work about as well as anything in our
+application. In particular, the vector encoding scheme is simple, compact and
+efficient, and well suited to use with a variety of terminal drivers and
+modems (since binary data is avoided). The best approach to implementing the
+GTERM graphics language would seem to be to take the tektronix language as a
+starting point and add additional functionality by extending the language
+with new control sequences. Some of the extensions defined here a taken from
+the Retrographics vt640 and the Pericom. Others more specific to window based
+applications will be added in the future in an upwards compatible fashion.
+
+
+.ks
+.nf
+ GS open workstation if not already open
+ open/expose graphics frame (icon -> full window)
+ direct terminal i/o to graphics frame
+ save cursor (mouse) position
+ issue GS dark vector command (see below)
+ CAN close workstation
+ restore control to text frame (restore cursor)
+ close/hide graphics frame (full window -> icon)
+ keystroke required to trigger close-window?
+
+
+ GS (workstation already opened)
+ enter vector drawing mode, dark vector
+ FS enter point plot mode
+ US,CR enter alpha mode
+ text is drawn in graphics frame
+ defaults to transparent text, erase permitted
+ ESC CR enter text mode
+ lines of text are drawn on status line in
+ nontransparent (overwrite) mode
+
+ ESC SUB enter crosshair mode
+ ESC FF clear screen, initialize drawing modes
+ ESC/f [x,y] set crosshair position
+ GS [x,y] US sets alpha cursor position
+
+ ESC[0123] set character size (normal,2x,3x,4x)
+ ESC[`abcd] set line drawing style (normal,dotted,dotdash,sd,ld)
+ ESC/[012]d set drawing mode (set, clear, toggle)
+ ESC/[0123]w set polyline linewidth (normal,2x,3x,4x)
+
+ Standard screen resolution is 1024 by 780.
+ In graphics mode, chars are interpreted as [x,y] coordinate pairs.
+ In alpha mode, chars are interpreted as characters to be drawn.
+.fi
+.ke
+
+
+A number of extensions to this basic language are being considered for future
+implementation. Support for color, multiple text fonts, general polymarkers,
+area fill, and possibly cell array are contemplated, as well as simple
+extensions for window control and pop-down menus. Support for the standard
+textronix features which have been omitted (vector point elimination,
+incremental point mode, status enquiry) is possible. The interface is easily
+extensible hence there is no need to attempt to define or support such features
+in the initial interface.
+
+.NH
+Implementation Notes
+
+ The GTERM virtual terminal consists of two independent frames, the text
+frame and the graphics frame. Selection of the frame to which output is to
+be delivered is done at the lowest possible level, i.e., when the read system
+call is issued by the terminal to accept terminal output from the pty
+(pseudo-terminal driver) stream for the terminal. This is done by linking in
+a custom version of the notify_read() primitive, the low level read primitive
+for notifer based programs. Output is initially to the text frame. Output
+is redirected to the graphics frame when GS is seen in the output stream,
+and later restored to the text frame when CAN is seen. Input is controlled
+independently by the two frames at a high level, hence does not have to be
+switched.
+
+All terminal operations are event driven. Graphics data is deposited into a
+circular input buffer by an event driven read routine, which can issue
+<ctrl/s>/<ctrl/q> to the terminal driver to control the rate at which input
+is accumulated. A separate drawing routine running off an interval timer
+is used to process data appearing in the input buffer. The drawing code is
+always in one of three modes: vector drawing mode, alpha mode (randomly
+addressable character drawing), or text mode (textual output to the status
+line).
+
+.SH
+Appendix: Tektronix Graphics Language (Retrographics vt640)
+
+.ks
+.nf
+ *GS enter graphics mode; dark vector
+ *FS enter point plot mode
+ RS enter incremental point mode
+ *US,CR enter alpha mode, graphics plane, tek font
+ CAN disable graphics mode, restore output to terminal plane
+
+ ESC"0g alias for enter transparent mode
+ ESC"4g alias for enter crosshair mode
+ ESC"5g enter light pen mode
+
+ *ESC SUB enter crosshair mode
+ *ESC FF clear screen, initialize drawing modes
+ *ESC ENQ return cursor position, other status
+
+ ESC[0123] set character size (normal,2x,3x,4x)
+ ESC[`abcd] set line drawing style (normal,dotted,dotdash,sd,ld)
+ ESC/[012]d set drawing mode (set, clear, toggle)
+
+ ESC/f set crosshair position
+ *GS [x,y] US sets alpha cursor position
+
+ Vector coordinate encoding (max 10 bits = 1024 points res.):
+
+ high y P01XXXXX
+ low y P11XXXXX
+ high x P01XXXXX
+ low x P10XXXXX
+
+ Cursor return structure:
+
+ key PXXXXXXX
+ high x P01XXXXX
+ low x P01XXXXX
+ high y P01XXXXX
+ low y P01XXXXX
+ trailer <cr>
+
+ Standard 4010/4012 screen resolution is 1024 by 780.
+ In graphics mode, chars are interpreted as [x,y] coordinate pairs.
+ In alpha mode, chars are interpreted as characters to be drawn.
+.fi
+.ke
diff --git a/unix/sun/Imtool.hlp b/unix/sun/Imtool.hlp
new file mode 100644
index 00000000..39d42dbe
--- /dev/null
+++ b/unix/sun/Imtool.hlp
@@ -0,0 +1,420 @@
+.help imtool Jan87 "Image Display on the Sun Workstation"
+.sp 3
+.ce
+\fBImage Display on the Sun Workstation\fR
+.ce
+Design Specification (prototype display)
+.ce
+Doug Tody
+.ce
+January 1987
+
+.NH
+Introduction
+
+ This document describes the prototype Sunview based SUN/IRAF image display.
+The standard Sun-3 color workstation provides an 1152 by 900 by 8 bit frame
+buffer (some models are larger) with an associated 256 entry color table and
+an RGB display. A keyboard and a three button mouse are provided for
+interactive control and command entry.
+
+In typical interactive use the user will have a number of windows open on the
+screen, e.g., a GTERM virtual graphics terminal with text and graphics windows,
+the image display window, a small console window, and several icons.
+Due to limitations on the amount of screen space available and due to speed
+limitations when refreshing a full 8 bit deep window, it is desirable to limit
+the size of the image display window to a fraction of the screen space most
+of the time.
+
+Due to the complexity of the task of designing a fully functional, device
+independent image display interface, and the need to get at least a basic
+image display capability functioning for SUN/IRAF as soon as possible,
+a prototype display will be implemented first in January 1987 followed by
+the full interface later in the year. Although the prototype will not provide
+full functionality or system and device independence, the basic features are
+expected to be provided im much the same form that they will take in the final
+interface.
+
+.NH
+User Interfaces
+
+ The prototype image display will use the existing TV/DISPLAY program to
+load images into the image display. None of the other tasks in the TV package
+will be supported and the entire contents of the TV package will disappear
+later this year. Display interaction and cursor readback will be via the
+same cursor mode interface currently used for the graphics terminal interface.
+Additional display-level interaction will be available under mouse control,
+e.g., to support display dependent functions not otherwise supported in cursor
+mode, such as greyscale windowing, filtering (resampling), region of interest
+control, and the like.
+
+.NH
+Display Features
+.NH 2
+Image and Graphics Planes
+
+ The display size is a setup configuration parameter but is fixed while the
+display is in use. Typical display sizes might be 512, 800, 1024, or 2048
+pixels square, or some rectangular size. Sufficient physical memory should be
+available so that the internal frame buffers may be locked into memory.
+
+The image display will be 7 bits deep with 1 bit of graphics
+overlay, leaving half of the color table entries for use by other
+graphics windows. The image may be windowed without affecting the
+color table entries of the other windows on the screen. If 7 bits
+of greyscale proves to be a serious limitation something on the order
+of 7.9 bits is possible by playing tricks with the software (clever
+use of the color table). This latter feature will not be available
+in the initial prototype.
+
+The prototype display will provide N image frames, all of which must be the
+same size, each with its own independent lookup table and viewport.
+The frame size and number of frames are arbitrary and will be a setup option.
+Blinking, split screen, true color representation, and other operations
+involving simultaneous display of multiple frames will not be implemented
+n the prototype display.
+
+The full display will support N image frames, each with an associated 1 bit
+graphics overlay, with all frame-graphics pairs the same size, and with
+support for multiple screen operations such as blink and split screen.
+Independent lookup tables will not be possible in split screen mode,
+but this may not be a serious problem given the autoscaling algorithm in
+the DISPLAY task, and the possibility of normalization of multiple image
+frames to use a common lookup table (by modifying the loaded pixel values),
+once the lookup table has been independently adjusted for each frame.
+
+.NH 2
+Pseudocolor and True Color
+
+ Pseudocolor will probably not be supported in the initial prototype,
+although it may be added to the prototype before the final interface is written.
+True color can probably be provided in the final interface by maintaining an
+independent lookup table for each of the three frames (RGB) in software,
+generating the compound output in software, and displaying the result in
+hardware using a software generated linear pseudocolor lookup table. Once the
+true color image is generated all of the zoom, pan, etc. functions will work
+with their usual speed. Since true color display is used infrequently (and
+fully buffered memory to memory operations are fairly fast anyhow) this
+technique should prove quite adequate.
+
+.NH 2
+The Display Window
+
+ The user views the displayed image through a window which may be resized and
+moved about on the screen at any time, under interactive control of the mouse.
+Resizing the window changes only the size of the subraster of the fixed-size
+image frame to be displayed. Moving the window under Sunview does not change
+the window into the image, merely the position of the image display window on
+the screen.
+
+Zoom and pan is implemented in software. Zoom is implemented by pixel
+replication and dezoom by pixel subsampling, operating simultaneously on both
+the greyscale and graphics planes. Each operation results in a reload of the
+portion of the image displayed in the image window from data stored in the
+fixed size internal frame buffer. Zoom, dezoom, and roam are controlled
+interactively using either the normal cursor mode keystrokes (ZXYEP,1-9M,0),
+the mouse, or both.
+
+Once the region of interest has been adjusted on the display via the full pixel
+sampling techniques, a "resample" operation may be performed to recompute the
+contents of the displayed subraster using block averaging and/or bilinear
+interpolation techniques. A possible variation on this is to compute several
+block averaged versions of the full image at various dezoom factors, e.g.,
+2, 4, and 8 times dezoom, and read from these rather than the full frame when
+dezoom is selected. For example, if the frame size is 2048 square, the display
+would simultaneously store 2048, 1024, 512, and 256 square block-averaged
+versions of the frame and dynamically select the version to be used depending
+upon the dezoom factor. The percentage of additional memory required to hold
+the dezoomed frames is only about 40% greater than the fully sampled frame.
+Timing tests need to be performed to see if this is worthwhile.
+
+.NH 2
+Region of Interest
+
+ Due to screen space limitations the image window will rarely exceed about
+512 square pixels in size, hence one will normally view either a dezoomed
+version of the image or a subraster of the full frame (full screen display
+will of course be an option). One possible way to live with dezoom without
+suffering loss of resolution is to use an icon, e.g., 64 pixels square,
+to always display the region under the cursor at full resolution or better.
+The display would consist of, for example, a 512 square main window displaying
+the dezoomed image, and an independently movable 64 square by 8 bit icon
+displaying an enlarged version of the image cursor, crosshairs, graphics
+overlay, and all.
+
+Alternatively, the cursor could be positioned on the full window and then a
+mouse button pressed to display the cursor at full resolution (or better),
+at the position of the cursor, with the display returning to normal when the
+mouse button is released. Yet another possibility is for the cursor to act
+as a magnifier which is moved about over the dezoomed image. Most probably
+all these choices will be provided as options, since they are all variations
+on the same thing.
+
+.NH 2
+Windowing
+
+ The lookup tables will be readable and writable under software control for
+sophisticated applications such as interactive entry of a piecewise linear
+transfer function. Most commonly, however, greyscale windowing will be
+performed by the image display under control of the mouse. The most attractive
+way to do this is probably to display a histogram of the image in the graphics
+plane, overlaid by the current transfer function, which typically looks like
+this:
+
+.ks
+.nf
+ /------------
+ /
+ /
+ ---------/
+.fi
+.ke
+
+Moving the cursor to the left will lower the threshold (move the displayed
+curve to the left), and moving the cursor up will increase the contrast
+(increase the slope of the transfer function). Ideally window mode would
+be entered by pressing a mouse button (or some combination of a mouse button
+and a control or shift key), windowing performed with the mouse button held
+down, and window mode exited when the mouse button is released.
+
+.NH 2
+Reading the Cursor
+
+ The image display cursor may be any 64 pixel square object with a shape
+which is suitable for centering on two dimensional objects. The default cursor
+will be a pair of 64 pixel long crosshairs with a gap in the middle. When a
+cursor read is in progress the cursor will blink rapidly, e.g., at 4-8 Hz,
+signaling to the user that the program is waiting for a cursor read (this is
+like turning on the full crosshairs on the GTERM graphics screen). The cursor
+may be moved either with the mouse or with the HJKL keys in cursor mode.
+A cursor read will be terminated by typing a key on the keyboard, or by
+pressing a mouse button which has been aliased to a key (as in GTERM).
+
+All cursor input will be via the standard IRAF cursor mode cursor input
+facility. The result of an image cursor read will be a line of text identical
+to that returned for a graphics cursor read, i.e., containing the cursor
+position in world coordinates (image pixel coordinates in this case), the
+WCS number, the key typed, and so on. Semi-automatic readout of the image
+cursor coordinates may be obtained via the "C" function in cursor mode,
+perhaps aliased to a mouse button.
+
+.NH 2
+Hardcopy Output
+
+ While there will be no snap/crtpict support initially, it will be possible
+to get pseudo-greyscale output hardcopy on the laserwriter via menu selection,
+as in GTERM, by taking advantage of the greyscale rendering capabilities of
+Postscript and the 300 dpi resolution of the laserwriter. It also should be
+possible to use :.snap or "=" to get hardcopy output in cursor mode.
+
+.NH
+Features Not Supported
+
+ The prototype display will provide as IRAF callable functions only image
+frame buffer i/o, screen clear, viewport control (zoom/pan), and read and write
+cursor position. Greyscale windowing, hardcopy output, and window resize and
+move will be handled directly by the image display and Sunview, under mouse
+control. Initially there will be no access to the graphics overlay, and only
+monochrome display will be supported (pseudocolor may be added to the prototype
+at some point). Full functionality will follow when the GIO imaging extensions
+and IDI interface specification have been completed.
+
+.NH
+Implementation
+
+ The following subsystems need to be added or modified to implement the
+prototype SUN/IRAF Sunview image display.
+.ls
+.ls [1]
+The software image display program (new code). This will be implemented as
+a Sunview tool (process), to be linked into the "basetools" executable (where
+GTERM lives). The image display process (or processes if there are multiple
+displays) will communicate with other processes via sockets and/or shared
+memory. A special file entry will be required in /dev for each display
+pseudo-device. The Sunview server process will normally be set up when the
+user's .suntools file is interpreted, but will not be displayed until the
+display is first loaded. Commands and data will be passed between the server
+process and the client (e.g.,some IRAF process) via the conventional socket
+based IPC mechanism and an application defined RPC mechanism. Shared memory
+will be used to eliminate pixel i/o to the image frame buffers when the server
+resides on the same machine as the client.
+.le
+.ls [2]
+The low level code in the DISPLAY program must be modified to talk with the
+display server. For the prototype, this is probably best done using the FIO
+based display interface rather than GIO, since the current display program
+is written to use FIO. The display task will directly read and write the
+display server frame buffer using the Sys V shared memory facilities. The
+final interface will do the same (or use a socket if the server is on a remote
+machine), but will use the more flexible GIO interface. The DISPLAY task
+must also be modified to compute the WCS and output it to the cursor mode
+code via GIO, in order for cursor readback to function.
+.le
+.ls [3]
+Cursor mode must be modified to support cursor input from imaging devices.
+The principle modification seems to be the addition of a set device viewport
+instruction, to be used to pass zoom and pan instructions on to the device
+kernel rather than implementing them directly in the cursor mode software as
+is done for the STDGRAPH devices. The open / close workstation, activate /
+deactivate workstation, clear screen, set cursor, and get cursor functions
+are also needed, but need not be different than the equivalent STDGRAPH
+functions.
+.le
+.le
+
+Cursor mode requires implementation of some portion of a GIO device interface
+for the image display. Probably this will be implemented using an inline
+kernel, at least for the prototype display. Both the GIO kernel and the
+DISPLAY task will communicate directly with the display server via IPC.
+
+.NH 2
+Display Control Interface
+
+ Applications programs such as the DISPLAY task or the CL cursor mode code
+will access the display via IPC carrying a GKI command and data stream. Most
+of the GKI instructions used are already provided by the current GIO/GKI
+interface. These are summarized below for reference.
+
+
+.ks
+.nf
+ GKI_EOF = BOI 0 L
+* GKI_OPENWS = BOI 1 L M N D
+* GKI_CLOSEWS = BOI 2 L N D
+* GKI_REACTIVATEWS = BOI 3 L
+* GKI_DEACTIVATEWS = BOI 4 L
+* GKI_MFTITLE = BOI 5 L N T
+* GKI_CLEARWS = BOI 6 L
+ GKI_CANCEL = BOI 7 L
+ GKI_FLUSH = BOI 8 L
+ GKI_POLYLINE = BOI 9 L N P
+ GKI_POLYMARKER = BOI 10 L N P
+ GKI_TEXT = BOI 11 L P N T
+ GKI_FILLAREA = BOI 12 L N P
+* GKI_PUTCELLARRAY = BOI 13 L LL UR NC NL P
+* GKI_SETCURSOR = BOI 14 L CN POS
+ GKI_PLSET = BOI 15 L LT LW CI
+ GKI_PMSET = BOI 16 L MT MW CI
+ GKI_TXSET = BOI 17 L UP SZ SP P HJ VJ F Q CI
+ GKI_FASET = BOI 18 L FS CI
+* GKI_GETCURSOR = BOI 19 L CN
+ GKI_CURSORVALUE = BOI 19 L CN POS KEY
+* GKI_GETCELLARRAY = BOI 20 L LL UR NC NL
+ GKI_CELLARRAY = BOI 20 L NP P
+ GKI_ESCAPE = BOI 25 L FN N DC
+ GKI_SETWCS = BOI 26 L N WCS
+ GKI_GETWCS = BOI 27 L N
+.fi
+.ke
+
+
+Those instructions which are required in the prototype interface are marked
+with an asterisk. In addition we need a number of new instructions to
+perform various common image display control functions. This is the topic
+of the GIO imaging extensions project, which we do not wish to attempt to
+seriously address in the prototype display. The new GKI instructions outlined
+in the remainder of this section are intended only to permit the implementation
+of the prototype SUN image display, and will be replaced by a full and much
+more carefully defined set of instructions when the full interface is designed.
+
+A new instruction is needed to set the device viewport, i.e., the portion of
+the frame buffer to be displayed in the display window. The set viewport
+instructions are used to implement zoom and pan at the cursor level.
+
+
+.ks
+.nf
+ GKI_SETVIEWPORT = BOI 28 L XC YC ZX ZY
+ GKI_GETVIEWPORT = BOI 29 L XC YC ZX ZY
+ GKI_VIEWPORT = BOI 29 L XC YC ZX ZY
+.fi
+.ke
+
+
+The device viewport is specified by the [x,y] center of the displayed area in
+frame buffer pixel coordinates (1-indexed), and the zoom or dezoom factor in
+each axis. This representation is used, rather than specifying the corner
+points of the intended viewport, so that the viewport may be specified
+independently of the size of the actual device window used, and to avoid
+questions about what happens near the edge of the frame.
+
+Instructions are also needed to set and inquire the index of the frame
+currently being displayed, and the index of the reference frame for the i/o
+and control functions; these need not be the same frame.
+
+
+.ks
+.nf
+ GKI_SETFRAME = BOI 30 L RF DF
+ GKI_GETFRAME = BOI 31 L
+ GKI_FRAME = BOI 31 L RF DF
+.fi
+.ke
+
+
+Here, RF is the index of the reference frame, i.e., the frame to be operated
+upon or referenced in all set, get, and pixel i/o operations, and DF is the
+index of the frame currently being displayed or to be displayed. The frames
+are numbered 1, 2, ..., N; 0 indicates that the reference frame or display
+frame is not to be changed.
+
+The concept of the display frame is important in cursor reads. In a cursor
+read or write operation, a CN (cursor number) index of 0 indicates that the
+cursor of the display frame is to be referenced; the index of the frame
+actually referenced will be returned in the CN field of the cursor value
+structure. If a nonzero CN is given the cursor of the indicated frame is
+referenced, whether or not the indicated reference frame is currently
+displayed.
+
+Lastly, we need an inquiry function to determine the size and depth of the
+image frame buffer, and the number of image planes. It is assumed that the
+frame buffer size, etc., will be set up in advance by the user, either using
+the mouse and the display setup panel, or via command line arguments in the
+.suntools file when the display server is initially spawned.
+
+
+.ks
+.nf
+ GKI_GETFRAMESIZE = BOI 32 L
+ GKI_FRAMESIZE = BOI 32 L NP NX NY NZ
+.fi
+.ke
+
+
+Here, NP is the number of image planes, NX and NY are the size in pixels of
+each image plane, and NZ is the number of displayable greylevels, e.g., 256.
+Note that NZ need not be a power of two as the entire colortable may not be
+available.
+
+The image display will be a standard GIO (inline or sub) kernel down to the
+level where some action occurs, after decoding the i/o instruction to be
+executed. At that point a device dependent C subroutine will be called which
+will pass the command on to the display server, which will always reside in a
+separate process (it has to, due to the peculiar notifier based nature of the
+Sunview environment).
+
+.NH 2
+Display Internals
+
+ The primary functions of the display server are [1] to maintain a frame
+buffer, and respond to requests to display portions of the frame buffer in the
+display window at some zoom or dezoom factor, and [2] to read and execute
+commands from a client process.
+
+Case [1] includes both refresh of the primary window, which occurs whenever a
+new image is loaded, the device viewport changes, or the window is resized or
+uncovered, and refresh of the region of interest (cursor) window, which occurs
+whenever any of the above events occurs or whenever the cursor is moved.
+In fact it appears that both operations are really the same thing, except that
+the cursor window always hides the main window, has a narrower border and no
+label, and is refreshed more often than the main window. The refresh operation
+sets up a mapping between the frame buffer and a window and then either does
+a series of pixrect operations to refresh the window (which may be partially
+or fully covered), or carries out a resampling operation on a region of the
+frame buffer, writing the zoomed or dezoomed data to the display window.
+
+The commands to be executed in case [2] are the GKI functions described in the
+previous section. In the prototype display the primary commands are open /
+close workstation, set viewport, set cursor position, and read cursor.
+The set viewport function merely sets up a transformation and calls the
+window refresh code described in the previous paragraph.
diff --git a/unix/sun/Makefile b/unix/sun/Makefile
new file mode 100644
index 00000000..029b957e
--- /dev/null
+++ b/unix/sun/Makefile
@@ -0,0 +1,67 @@
+# Make the Sunview GTERM graphics terminal server, IMTOOL image display server.
+
+SRCS= arrow.c mouse.c notify_read.c screendump.c
+OBJS= arrow.o mouse.o notify_read.o screendump.o
+LIBS= -lsuntool -lsunwindow -lpixrect -lm
+SUNOBJS= imtool.o gterm.o gtermio.o screendump.o arrow.o mouse.o notify_read.o
+
+# Set the following to "-f68881" on a Sun-3, to "" on a Sun-4 or 386i.
+#FLOAT= -f68881
+FLOAT=
+
+# Make the custom suntools executable only on versions of SunOS prior to 4.0.
+default:
+ sh -c 'if [ "`grep Release.4 /etc/motd`" != ""\
+ -o `mach` = "i386" ]; then\
+ make gterm.e; make imtool.e;\
+ else\
+ make suntools.e;\
+ fi'
+
+# Make a custom SUNTOOLS with GTERM, IMTOOL, linked in.
+suntools.e: gterm.c imtool.c gtermio.o $(OBJS)
+ chmod +x mksuntool.csh; mksuntool.csh
+ cc -c -O $(FLOAT) gterm.c imtool.c
+ mv -f gterm.o imtool.o suntool
+ cp gtermio.o $(OBJS) suntool
+ (cd ./suntool; make basetools MOREOBJS="$(SUNOBJS)" CPUFLAG="" ;\
+ mv -f suntools ../suntools.e)
+
+# Make the GTERM test executable.
+gterm.e: gterm.o gtermio.o $(OBJS)
+ cc $(FLOAT) gterm.o gtermio.o $(OBJS) $(LIBS) -o gterm.e
+
+# Make the IMTOOL test executable.
+imtool.e: imtool.o $(OBJS)
+ cc $(FLOAT) imtool.o $(OBJS) $(LIBS) -o imtool.e
+
+# Install the executables in iraf$local, which is where the INSTALL script
+# expects to find it.
+
+install:
+ strip *.e; mv -f *.e $$iraf/unix/bin.`mach`
+
+clean:
+ rm -f *.[aoe]
+ rm -rf suntool
+
+gterm.o: gterm.c gterm.h gterm.icon
+ cc -c -O -DSTANDALONE $(FLOAT) gterm.c
+
+imtool.o: imtool.c imtool.h imtool.icon imtool.cursor
+ cc -c -O -DSTANDALONE $(FLOAT) imtool.c
+
+gtermio.o: gtermio.c gterm.h gterm.esc
+ cc -c -O $(FLOAT) gtermio.c
+
+arrow.o: arrow.c
+ cc -c -O $(FLOAT) arrow.c
+
+mouse.o: mouse.c
+ cc -c -O $(FLOAT) mouse.c
+
+notify_read.o: notify_read.c
+ cc -c -O $(FLOAT) notify_read.c
+
+screendump.o: screendump.c
+ cc -c -O $(FLOAT) screendump.c
diff --git a/unix/sun/README b/unix/sun/README
new file mode 100644
index 00000000..4196b336
--- /dev/null
+++ b/unix/sun/README
@@ -0,0 +1,5 @@
+GTERM -- Graphics terminal emulator.
+IMTOOL -- Image display server.
+
+See gterm.man, imtool.man for detailed documentation.
+This software is installed by the $iraf/unix/hlib/install script.
diff --git a/unix/sun/arrow.c b/unix/sun/arrow.c
new file mode 100644
index 00000000..1cc55aad
--- /dev/null
+++ b/unix/sun/arrow.c
@@ -0,0 +1,66 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <sys/types.h>
+#include <sundev/kbio.h>
+#include <sundev/kbd.h>
+
+/*
+ * ARROW.C -- Code to enable and disable the arrow key function-key mappings
+ * (R8,10,12,14).
+ */
+
+#define NKEYS 4
+static unsigned char station[NKEYS] = { 0x45, 0x5b, 0x5d, 0x71 };
+static unsigned char entry[NKEYS] = { RF(8), RF(10), RF(12), RF(14) };
+static struct kiockey o_key[NKEYS];
+
+
+/* DISABLE_ARROW_KEYS -- Save the arrow key keyboard translation table
+ * entries, and then disable the mapping of the function keys to the ANSI
+ * arrow key sequences. This is necessary to read the function key as
+ * an event rather than an escape sequence in a Sunview event handler.
+ */
+disable_arrow_keys()
+{
+ register int fd, i;
+ struct kiockey key;
+ int status = 0;
+
+ if ((fd = open ("/dev/kbd", 2)) == -1)
+ return (-1);
+
+ for (i=0; i < NKEYS; i++) {
+ o_key[i].kio_station = station[i];
+ if ((status = ioctl (fd, KIOCGETKEY, &o_key[i])) != 0)
+ break;
+ key = o_key[i];
+ key.kio_entry = entry[i];
+ if ((status = ioctl (fd, KIOCSETKEY, &key)) != 0)
+ break;
+ }
+
+ close (fd);
+ return (status);
+}
+
+
+/* ENABLE_ARROW_KEYS -- Restore the saved arrow key keyboard translation table
+ * entries.
+ */
+enable_arrow_keys()
+{
+ register int fd, i;
+ struct kiockey key;
+ int status = 0;
+
+ if ((fd = open ("/dev/kbd", 2)) == -1)
+ return (-1);
+
+ for (i=0; i < NKEYS; i++)
+ if ((status = ioctl (fd, KIOCSETKEY, &o_key[i])) != 0)
+ break;
+
+ close (fd);
+ return (status);
+}
diff --git a/unix/sun/fifo.c b/unix/sun/fifo.c
new file mode 100644
index 00000000..65471a77
--- /dev/null
+++ b/unix/sun/fifo.c
@@ -0,0 +1,759 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#define SUNOS4
+
+/*
+ * FIFO.C -- Test fifo i/o. This is a server process much like IMTOOL,
+ * except that all the process does is respond to datastream requests
+ * to read and write an internal 512 sq frame buffer maintained as an
+ * array in memory (only one frame buffer is supported). A log is kept
+ * to the stderr of all datastream requests.
+ *
+ * Used to debug fifo i/o - NOT USED IN THE ONLINE PROGRAMS.
+ *
+ * To make: cc fifo.c -o fifo.e
+ *
+ * Usage: fifo.e >& spool run server, logging output to spool
+ * fifo.e -i run interactively
+ *
+ * In interactive mode, cursor value strings may be typed in on the fifo.e
+ * stdin in response to cursor read requests from the client. Otherwise,
+ * a constant cursor value "1.0 1.0 101 q" is returned.
+ */
+
+#define I_DEVNAME "/dev/imt1o"
+#define O_DEVNAME "/dev/imt1i"
+#define OLD_DEVNAME "/dev/imt1"
+#define IO_TIMEOUT 30
+#define SZ_FIFOBUF 4000
+#define SZ_WCSBUF 320 /* WCS text buffer size */
+#define MAX_FRAMES 1
+#define SZ_FNAME 256
+
+#define MEMORY 01 /* frame buffer i/o */
+#define LUT 02 /* lut i/o */
+#define FEEDBACK 05 /* used for frame clears */
+#define IMCURSOR 020 /* logical image cursor */
+#define WCS 021 /* used to set WCS */
+
+#define SZ_IMCURVAL 160
+#define PACKED 0040000
+#define COMMAND 0100000
+#define IIS_READ 0100000
+#define IMC_SAMPLE 0040000
+#define IMT_FBCONFIG 077
+
+struct iism70 {
+ short tid;
+ short thingct;
+ short subunit;
+ short checksum;
+ short x, y, z;
+ short t;
+};
+
+#ifndef abs
+#define abs(a) (((a)<0)?(-a):(a))
+#endif
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) ((a)<(b)?(b):(a))
+#endif
+
+static int interactive=0;
+static int background=0;
+static int datain, dataout=0;
+static char framebuf[512*512];
+static int frame=1, reference_frame=1, display_frame=1;
+static int fb_config_index=1, fb_nframes=1;
+static int Fb_width = 512, Fb_height = 512;
+static char wcsbuf[MAX_FRAMES][SZ_WCSBUF];
+
+
+/* FIFO -- Text fifo i/o.
+ */
+main (argc, argv)
+int argc;
+char **argv;
+{
+ fd_set fds;
+
+ if (argc > 1)
+ if (strcmp (argv[1], "-i") == 0)
+ interactive++; /* type in cursor values */
+
+ /* Open the output fifo. We have to open it ourselves first as a
+ * client to get around the fifo open-no-client error.
+ */
+ if ((datain = open (O_DEVNAME, O_RDONLY|O_NDELAY)) != -1) {
+ if ((dataout = open (O_DEVNAME, O_WRONLY|O_NDELAY)) != -1)
+ fcntl (dataout, F_SETFL, O_WRONLY);
+ close (datain);
+ }
+
+ /* Open the input stream, a FIFO pseudodevice file used by
+ * applications to send us commands and data.
+ */
+ if ((datain = open (I_DEVNAME, O_RDONLY|O_NDELAY)) == -1) {
+ if ((datain = open (OLD_DEVNAME, O_RDONLY|O_NDELAY)) == -1)
+ fprintf (stderr, "Warning: cannot open %s\n", I_DEVNAME);
+ } else {
+ /* Clear O_NDELAY for reading. */
+ fcntl (datain, F_SETFL, O_RDONLY);
+ }
+
+ FD_ZERO (&fds); FD_SET (datain, &fds);
+ while (select (FD_SETSIZE, &fds, NULL, NULL, NULL) > 0) {
+ ev_cmdinput();
+ fflush (stdout);
+ fflush (stderr);
+ FD_ZERO (&fds); FD_SET (datain, &fds);
+ }
+
+ close (datain);
+ exit (0);
+}
+
+
+/* EV_CMDINPUT -- Called when command or data input has arrived via the
+ * pseudodevice input stream from some applications process.
+ */
+ev_cmdinput()
+{
+ register unsigned char *cp;
+ register int sum, i;
+ register short *p;
+ int ndatabytes, nbytes, n, ntrys=0;
+ static int errmsg=0, bswap=0;
+ struct iism70 iis;
+ char buf[SZ_FIFOBUF];
+ int fb_index;
+
+ /* Get the IIS header. */
+ if (read (datain, (char *)&iis, sizeof(iis)) < sizeof(iis)) {
+ fprintf (stderr, "imtool: command input read error\n");
+ fflush (stderr);
+ return (0);
+ } else if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+
+ /* Verify the checksum. If it fails swap the bytes and try again.
+ */
+ for (;;) {
+ for (i=0, sum=0, p=(short *)&iis; i < 8; i++)
+ sum += *p++;
+ if ((sum & 0177777) == 0177777)
+ break;
+
+ if (ntrys++) {
+ if (!errmsg++) {
+ fprintf (stderr, "imtool: bad data header checksum\n");
+ fflush (stderr);
+ if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, "noswap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, " swap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ }
+ break;
+
+ } else {
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ bswap = !bswap;
+ }
+ }
+
+ ndatabytes = -iis.thingct;
+ if (!(iis.tid & PACKED))
+ ndatabytes *= 2;
+
+ /* Log command. */
+ fprintf (stderr,
+ "subunit=%06o tid=%06o nbytes=%7d x=%06o y=%06o z=%06o\n",
+ iis.subunit & 077,
+ iis.tid,
+ ndatabytes,
+ iis.x & 0177777,
+ iis.y & 0177777,
+ iis.z & 0177777);
+ fflush (stderr);
+
+ switch (iis.subunit & 077) {
+ case FEEDBACK:
+ /* The feedback unit is used only to clear a frame.
+ */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+ /* erase (rf_p); */
+ fprintf (stderr, "erase frame %d\n", reference_frame);
+ break;
+
+ case LUT:
+ /* Data mode writes to the frame lookup tables are not implemented.
+ * A command mode write to the LUT subunit is used to connect
+ * image memories up to the RGB channels, i.e., to select the frame
+ * to be displayed. We ignore any attempt to assign multiple
+ * frames to multiple color channels, and just do a simple frame
+ * select.
+ */
+ if (iis.subunit & COMMAND) {
+ int frame, z, n;
+ short x[14];
+
+ if (read (datain, (char *)x, ndatabytes) == ndatabytes) {
+ if (bswap)
+ bswap2 ((char *)x, (char *)x, ndatabytes);
+
+ z = x[0];
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ frame = max (1, n + 1);
+ if (frame > fb_nframes) {
+ if (frame < MAX_FRAMES) {
+ /* set_fbconfig (fb_config_index, frame); */
+ fprintf (stderr, "set_fbconfig (%d, %d)\n",
+ fb_config_index, frame);
+ } else {
+ fprintf (stderr, "imtool warning: ");
+ fprintf (stderr,
+ "attempt to display nonexistent frame %d\n", frame);
+ frame = fb_nframes - 1;
+ }
+ }
+
+ /* set_frame (frame); */
+ fprintf (stderr, "set_frame (%d)\n", frame);
+ return (0);
+ }
+ }
+
+ case MEMORY:
+ /* Load data into the frame buffer. Data is assumed to be byte
+ * packed.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read from the display.
+ */
+ unsigned char *fb, *ip;
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to be read from. */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+
+ fb = (unsigned char *)framebuf;
+ nbytes = ndatabytes;
+ x = iis.x & 01777;
+ y = iis.y & 01777;
+
+ ip = max (fb, min (fb + Fb_width * Fb_height - nbytes,
+ fb + y * Fb_width + x));
+ if (ip != fb + y * Fb_width + x) {
+ fprintf (stderr,
+ "imtool: attempted read out of bounds on framebuf\n");
+ fprintf (stderr,
+ "read %d bytes at [%d,%d]\n", nbytes, x, y);
+ }
+
+ /* Log i/o command. */
+ fprintf (stderr, "read %d bytes at x=%d, y=%d\n",
+ nbytes, x, y);
+
+ /* Return the data from the frame buffer. */
+ starttime = time(0);
+ for (nleft = nbytes; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = write (dataout, ip, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT)) {
+ fprintf (stderr, "IMTOOL: timeout on write\n");
+ break;
+ }
+ } else
+ ip += n;
+ }
+
+ return (0);
+
+ } else {
+ /* Write to the display.
+ */
+ unsigned char *fb, *op;
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to be written into (encoded with a bit for
+ * each frame, 01 is frame 1, 02 is frame 2, 04 is frame 3,
+ * and so on).
+ */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+
+ /* Get a pointer into the frame buffer where the data will
+ * be put.
+ */
+ fb = (unsigned char *)framebuf;
+ nbytes = ndatabytes;
+ x = iis.x & 07777;
+ y = iis.y & 07777;
+
+ op = max (fb, min (fb + Fb_width * Fb_height - nbytes,
+ fb + y * Fb_width + x));
+ if (op != fb + y * Fb_width + x) {
+ fprintf (stderr,
+ "imtool: attempted write out of bounds on framebuf\n");
+ fprintf (stderr,
+ "write %d bytes to [%d,%d]\n", nbytes, x, y);
+ }
+
+ /* Log i/o command. */
+ fprintf (stderr, "write %d bytes at x=%d, y=%d\n",
+ nbytes, x, y);
+
+ /* Read the data into the frame buffer.
+ */
+ starttime = time(0);
+ for (nleft = nbytes; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = read (datain, op, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT))
+ break;
+ } else {
+ /* Set any zeroed pixels to the background color,
+ * if a special background color is specified.
+ */
+ if (background)
+ for (cp=op, i=n; --i >= 0; cp++)
+ if (!*cp)
+ *cp = background;
+ op += n;
+ }
+ }
+
+ /* Refresh the display, if the current display frame is the
+ * same as the reference frame.
+ if (rf_p == df_p) {
+ BRect fb_r, pw_r;
+
+ fb_r.r_left = x * zoom;
+ fb_r.r_top = y * zoom;
+ fb_r.r_width = min (nbytes * zoom, fb_width);
+ fb_r.r_height = ((nbytes*zoom*zoom + fb_width-1)/fb_width);
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ Bpw_lock (gio_pw, &pw_rect);
+
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+
+ if (maprect (&fb_rect, &fb_r, &pw_rect, &pw_r))
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r)) {
+ ds_write (gio_pw,
+ pw_r.r_left, pw_r.r_top,
+ pw_r.r_width, pw_r.r_height,
+ PIX_SRC | PIX_COLOR(NGREY-1),
+ df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+
+ if (pw_r.r_top + pw_r.r_height >= pw_rect.r_height
+ - cb_height)
+ put_colorbar();
+ }
+
+ Bpw_unlock (gio_pw);
+ }
+ */
+
+ return (0);
+ }
+ break;
+
+ case WCS:
+ /* Read or write the WCS for a frame. The frame number to
+ * which the WCS applies is passed in Z and the frame buffer
+ * configuration in T. The client changes the frame buffer
+ * configuration in a WCS set. The WCS text follows the header
+ * as byte packed ASCII data.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Return the WCS for the referenced frame.
+ */
+ register char *op;
+ register int n;
+ char emsg[SZ_WCSBUF];
+ char *text;
+ int frame;
+
+ for (op=emsg, n=SZ_WCSBUF; --n >=0; )
+ *op++ = 0;
+
+ frame = decode_frameno (iis.z & 07777);
+ if (frame > fb_nframes)
+ strcpy (text=emsg, "[NOSUCHFRAME]\n");
+ else {
+ set_reference_frame (frame);
+ text = wcsbuf[reference_frame-1];
+ }
+
+ fprintf (stderr, "query wcs:\n");
+ write (2, text, SZ_WCSBUF);
+
+ write (dataout, text, SZ_WCSBUF);
+
+ } else {
+ /* Set the WCS for the referenced frame.
+ */
+ char buf[1024];
+ int fb_config, frame;
+
+ frame = decode_frameno (iis.z & 07777);
+ if (frame > fb_nframes)
+ if (frame < MAX_FRAMES) {
+ /* set_fbconfig (fb_config_index, frame); */
+ fprintf (stderr, "set_fbconfig (%d, %d)\n",
+ fb_config_index, frame);
+ }
+
+ set_reference_frame (frame);
+ if ((fb_config = iis.t & 077) != fb_config_index) {
+ /* set_fbconfig (fb_config_index, frame); */
+ fprintf (stderr, "set_fbconfig (%d, %d)\n",
+ fb_config_index, frame);
+ }
+
+ /* Read in and set up the WCS. */
+ if (read (datain, buf, ndatabytes) == ndatabytes)
+ strncpy (wcsbuf[reference_frame-1], buf, SZ_WCSBUF);
+
+ fprintf (stderr, "set wcs:\n");
+ write (2, buf, SZ_WCSBUF);
+
+ /*
+ strcpy (rf_p->fb_ctran.format, W_DEFFORMAT);
+ rf_p->fb_ctran.imtitle[0] = '\0';
+ rf_p->fb_ctran.valid = 0;
+ rf_p->fb_imageno++;
+ rf_p->fb_objno = 1;
+
+ wcs_update (rf_p);
+ if (rf_p == df_p)
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ */
+ }
+
+ return (0);
+ break;
+
+ case IMCURSOR:
+ /* Read or write the logical image cursor. This is an extension
+ * added to provide a high level cursor read facility; this is
+ * not the same as a low level access to the IIS cursor subunit.
+ * Cursor reads may be either nonblocking (immediate) or blocking,
+ * using the keyboard or mouse to terminate the read, and
+ * coordinates may be returned in either image (world) or frame
+ * buffer pixel coordinates.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read the logical image cursor. In the case of a blocking
+ * read all we do is initiate a cursor read; completion occurs
+ * when the user hits a key or button.
+ */
+ fprintf (stderr, "read cursor position\n");
+ if (iis.tid & IMC_SAMPLE) {
+ /* Sample the cursor position. */
+ /*
+ register struct ctran *ct;
+ int wcs = iis.z;
+ int sx, sy;
+ float wx, wy;
+
+ wx = sx = last_x + pw_rect.r_left;
+ wy = sy = last_y + pw_rect.r_top;
+
+ if (wcs) {
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ if (abs(ct->d) > .001)
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+ }
+ }
+ */
+
+ int wcs = iis.z, key = 'q';
+ float wx=1.0, wy=1.0;
+
+ /* Return the cursor value on the output datastream encoded
+ * in a fixed size ascii buffer.
+ */
+ gio_retcursorval (wx, wy, display_frame*100+wcs, key, "");
+
+ } else {
+ /* Initiate a user triggered cursor read. */
+ /* gio_readcursor (iis.z); */
+ int wcs = iis.z, key = 'q';
+ float wx=1.0, wy=1.0;
+ gio_retcursorval (wx, wy, display_frame*100+wcs, key, "");
+ }
+
+ } else {
+ /* Write (set) the logical image cursor position. */
+ /*
+ fprintf (stderr, "write cursor position\n");
+ register struct ctran *ct;
+ int sx = iis.x, sy = iis.y;
+ float wx = sx, wy = sy;
+ int wcs = iis.z;
+
+ if (wcs) {
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ sx = (wx - ct->tx) / ct->a;
+ if (abs(ct->d) > .001)
+ sy = (wy - ct->ty) / ct->d;
+ }
+ }
+
+ gio_setcursorpos (sx - pw_rect.r_left, sy - pw_rect.r_top);
+ */
+ }
+
+ return (0);
+ break;
+
+ default:
+ /* Ignore unsupported command input.
+ */
+ break;
+ }
+
+ /* Discard any data following the header. */
+ if (!(iis.tid & IIS_READ))
+ for (nbytes = ndatabytes; nbytes > 0; nbytes -= n) {
+ n = (nbytes < SZ_FIFOBUF) ? nbytes : SZ_FIFOBUF;
+ if ((n = read (datain, buf, n)) <= 0)
+ break;
+ }
+
+ fflush (stderr);
+ return (0);
+}
+
+
+/* SET_REFERENCE_FRAME -- Set reference frame. If the frame referenced is
+ * greater than the current number of frames, attempt to increase the number
+ * of frames.
+ */
+static
+set_reference_frame (n)
+register int n;
+{
+ reference_frame = max (1, n);
+ if (reference_frame > fb_nframes) {
+ if (reference_frame < MAX_FRAMES) {
+ /* set_fbconfig (fb_config_index, reference_frame); */
+ fprintf (stderr, "set_fbconfig %d %d\n",
+ fb_config_index, reference_frame);
+ } else {
+ fprintf (stderr, "imtool warning: ");
+ fprintf (stderr,
+ "attempt to reference nonexistent frame %d\n",
+ reference_frame);
+ reference_frame = fb_nframes;
+ }
+ }
+
+ /* rf_p = frames + (reference_frame - 1); */
+}
+
+
+/* DECODE_FRAMENO -- Decode encoded IIS register frame number.
+ */
+static
+decode_frameno (z)
+register int z;
+{
+ register int n;
+
+ /* Get the frame number, encoded with a bit for each frame, 01 is
+ * frame 1, 02 is frame 2, 04 is frame 3, and so on.
+ */
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ return (max (1, n + 1));
+}
+
+
+/* BSWAP2 - Move bytes from array "a" to array "b", swapping successive
+ * pairs of bytes. The two arrays may be the same but may not be offset
+ * and overlapping.
+ */
+static
+bswap2 (a, b, nbytes)
+char *a, *b; /* input array */
+int nbytes; /* number of bytes to swap */
+{
+ register char *ip=a, *op=b, *otop;
+ register unsigned temp;
+
+ /* Swap successive pairs of bytes.
+ */
+ for (otop = op + (nbytes & ~1); op < otop; ) {
+ temp = *ip++;
+ *op++ = *ip++;
+ *op++ = temp;
+ }
+
+ /* If there is an odd byte left, move it to the output array.
+ */
+ if (nbytes & 1)
+ *op = *ip;
+}
+
+
+/* GIO_RETCURSORVAL -- Return the cursor value on the output datastream to
+ * the client which requested the cursor read.
+ */
+static
+gio_retcursorval (wx, wy, wcs, key, strval)
+float wx, wy; /* cursor coordinates */
+int wcs; /* encoded WCS value */
+int key; /* keystroke used as trigger */
+char *strval; /* optional string value */
+{
+ register char *op;
+ register int n;
+ char curval[SZ_IMCURVAL];
+ char keystr[20];
+
+ for (op=curval, n=SZ_IMCURVAL; --n >=0; )
+ *op++ = 0;
+
+ /* If running FIFO in interactive mode, allow the user to type
+ * in the cursor value on the standard input.
+ */
+ if (interactive) {
+ fprintf (stderr, "enter cursor value string: ");
+ fflush (stderr);
+ if (fgets (curval, SZ_IMCURVAL, stdin) != NULL)
+ goto ret;
+ }
+
+ /* Encode the cursor value. */
+ if (key == EOF)
+ sprintf (curval, "EOF\n");
+ else {
+ if (isprint (key) && !isspace(key)) {
+ keystr[0] = key;
+ keystr[1] = '\0';
+ } else
+ sprintf (keystr, "\\%03o", key);
+
+ sprintf (curval, "%10.3f %10.3f %d %s %s\n",
+ wx, wy, wcs, keystr, strval);
+ }
+ret:
+ fprintf (stderr, "%s", curval);
+
+ /* Send it to the client program. */
+ write (dataout, curval, sizeof(curval));
+}
+
+
+#define mask(s) (1<<((s)-1))
+#define setvec(vec, a) \
+ vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
+
+static int ringring;
+
+
+/* WMSEC -- Suspend task execution (sleep) for the specified number
+ * of milliseconds.
+ */
+wmsec (msec)
+int msec;
+{
+ struct itimerval itv, oitv;
+ register struct itimerval *itp = &itv;
+ struct sigvec vec, ovec;
+#ifdef SUNOS4
+ void napmsx();
+#else
+ int napmsx();
+#endif
+ int omask;
+
+ if (msec == 0)
+ return;
+
+ timerclear (&itp->it_interval);
+ timerclear (&itp->it_value);
+ if (setitimer (ITIMER_REAL, itp, &oitv) < 0)
+ return;
+
+ setvec (ovec, SIG_DFL);
+ omask = sigblock(0);
+
+ itp->it_value.tv_usec = (msec * 1000) % 1000000;
+ itp->it_value.tv_sec = (msec * 1000) / 1000000;
+
+ if (timerisset (&oitv.it_value)) {
+ if (timercmp(&oitv.it_value, &itp->it_value, >))
+ oitv.it_value.tv_sec -= itp->it_value.tv_sec;
+ else {
+ itp->it_value = oitv.it_value;
+ /* This is a hack, but we must have time to
+ * return from the setitimer after the alarm
+ * or else it'll be restarted. And, anyway,
+ * sleep never did anything more than this before.
+ */
+ oitv.it_value.tv_sec = 1;
+ oitv.it_value.tv_usec = 0;
+ }
+ }
+
+ setvec (vec, napmsx);
+ (void) sigvec (SIGALRM, &vec, &ovec);
+ ringring = 0;
+ (void) setitimer (ITIMER_REAL, itp, (struct itimerval *)0);
+
+ while (!ringring)
+ sigpause (omask &~ mask(SIGALRM));
+
+ (void) sigvec (SIGALRM, &ovec, (struct sigvec *)0);
+ (void) setitimer (ITIMER_REAL, &oitv, (struct itimerval *)0);
+}
+
+
+#ifdef SUNOS4
+static void
+#else
+static int
+#endif
+napmsx()
+{
+ ringring = 1;
+}
diff --git a/unix/sun/gterm.c b/unix/sun/gterm.c
new file mode 100644
index 00000000..57c4eb43
--- /dev/null
+++ b/unix/sun/gterm.c
@@ -0,0 +1,1984 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <suntool/sunview.h>
+#include <suntool/canvas.h>
+#include <suntool/panel.h>
+#include <suntool/tty.h>
+#include <suntool/walkmenu.h>
+#include <suntool/tool_struct.h>
+#include <sunwindow/win_cursor.h>
+#include <sgtty.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "gterm.h"
+
+/*
+ * GTERM -- Graphics terminal emulator. This code implements a virtual
+ * graphics terminal consisting of independent text and graphics frames.
+ * The text (tty) frame is a standard Sun TTY frame. The graphics (gio)
+ * frame is a tektronix 4012 with extensions, some of which are patterned
+ * after the Retrographics vt100, Pericom, etc., and others of which are or
+ * will be added to provide GKS like capabilities via a conventional data
+ * driven graphics terminal interface. The tty and gio frames are independent
+ * frames under Sunview, hence they may be resized, moved, opened or closed,
+ * hidden or exposed, etc., independently of each other. Both frames are
+ * active at all times, but since a single terminal i/o stream is used to
+ * drive both frames an applications program can communicate with only one
+ * frame at a time, just like a conventional graphics terminal.
+ *
+ * D.Tody, January 1987 (NOAO/IRAF project)
+ */
+
+#define MAX_ARGS 50 /* max tty or gio cmdline args */
+#define DEF_TEKXRES 1024 /* default logical X resolution */
+#define DEF_TEKYRES 780 /* default logical Y resolution */
+
+#define NO_ACTION 0 /* no action on open/close ws */
+#define SHOW_FRAME 1 /* show or unshow gio frame */
+#define UNSHOW_FRAME 1 /* show or unshow gio frame */
+#define EXPOSE_FRAME 2 /* expose or hide gio frame */
+#define HIDE_FRAME 2 /* expose or hide gio frame */
+#define CROSSHAIR_OFF 1 /* crosshair cursor off */
+#define CROSSHAIR_ON 2 /* turn crosshair cursor on */
+#define SZ_LOGBUF 1024 /* size of logfile buffer */
+#define SZ_GINMODETERM 10 /* ginmode string length */
+#define SZ_FNAME 128
+
+#define R_TYPE 0 /* 0=postscript, 1=rasterfile */
+#define R_DISPOSE "lpr -s %s" /* dispose command */
+#define R_FILENAME "" /* output filename */
+
+/* External variables. */
+extern int gio_graphicsenabled; /* set when in graphics mdoe */
+int cursor_show = -1; /* cursor state: on or off */
+Window gt_baseframe, gt_ttysw, gio_frame, gio_canvas;
+FILE *gt_logfp = NULL; /* logfile file pointer */
+
+/* Screendump stuff. */
+int r_type = R_TYPE;
+char r_dispose[SZ_FNAME+1] = R_DISPOSE;
+char r_filename[SZ_FNAME+1] = R_FILENAME;
+
+/* Both external and a user option. */
+int clip_graphics = 1; /* disable rasterop clipping */
+
+/* User options. */
+static int graphics_enable; /* enable graphics plane */
+static int openws_action; /* default action on openws */
+static int closews_action; /* default action on closews */
+static int closews_pause; /* pause/confirm closews? */
+static int canvas_retained; /* retain the canvas pixwin? */
+static int reverse_video; /* normal or reverse video */
+static int color; /* color or monochrome graphics */
+static int win_xsize; /* standard size graphics frame */
+static int win_ysize;
+static int win_xoff; /* standard offset from tty fr. */
+static int win_yoff;
+static int ignore; /* ignore suspend/stop codes? */
+static char gin_modeterm[SZ_GINMODETERM+1];
+static char logfile[SZ_FNAME+1] = "gterm.out";
+
+/* The following are used to save and later restore the option parameters. */
+static int s_clip_graphics = 1;
+static int s_graphics_enable = GRAPHICS_ON;
+static int s_openws_action = SHOW_FRAME;
+static int s_closews_action= HIDE_FRAME;
+static int s_closews_pause = 0;
+static int s_canvas_retained = 1;
+static int s_reverse_video = 0;
+static int s_color = 1;
+static int s_win_xsize = 800;
+static int s_win_ysize = 630;
+static int s_win_xoff = 100;
+static int s_win_yoff = 19;
+static int s_ignore = 0;
+static char s_gin_modeterm[SZ_GINMODETERM] = "015";
+
+/* Internal state variables. */
+static int cursor_type = -1; /* crosshair or small cursor */
+static int cursor_read_pending=0; /* waiting for cursor event */
+static int key_left = 0; /* key aliased to left msbutton */
+static int key_middle = 0; /* key aliased to mid msbutton */
+static int key_right = 0; /* key aliased to mid msbutton */
+static int last_key = 0; /* last cursor read key */
+static int last_x= -1,last_y= -1; /* last cursor read position */
+static int pause_mode = 0; /* waiting for closews event */
+static int cursor_used = 0; /* set if cursor is used */
+static int gio_frame_has_moved=0; /* set when user moves giowin */
+static int setup_xoff = 140; /* offset to setup panel */
+static int setup_yoff = 60;
+static int tty_lastx, tty_lasty; /* cursor position in gt_ttysw */
+static int height_is_set = 0; /* tty w. height set on cmdline */
+static int tty_nlines = 0; /* number of lines in tty w. */
+static int shell = 0; /* we are running a shell */
+static char t_suspc, t_dsuspc, t_set=0;
+
+/* The following is a kludge and should be determined at runtime */
+static int pty_fd = 3; /* fd of tty pseudoterminal */
+
+/* Graphics fonts. */
+static char fb_courier[] = "/usr/lib/fonts/fixedwidthfonts/cour.b.%d";
+static char fr_courier[] = "/usr/lib/fonts/fixedwidthfonts/cour.r.%d";
+static char fr_screen[] = "/usr/lib/fonts/fixedwidthfonts/screen.r.%d";
+static char fb_screen[] = "/usr/lib/fonts/fixedwidthfonts/screen.b.%d";
+
+#define FULLSCREEN 5
+struct fonttab alpha_fonts[] = {
+ 10, 7, 12, 560, 420, NULL, fb_courier, "10:[560x420]",
+ 12, 8, 14, 640, 490, NULL, fb_courier, "12:[640x490]",
+ 14, 9, 16, 720, 560, NULL, fb_courier, "14:[720x560]",
+ 16, 10, 18, 800, 630, NULL, fb_courier, "16:[800x630]",
+ 18, 11, 19, 880, 665, NULL, fb_courier, "18:[880x665]",
+ 24, 14, 25, 1142, 890, NULL, fb_courier, "24:fullscreen",
+ /* Note nentries must match panel choice list */
+ 0, 0, 0, 0, 0, NULL, NULL, NULL
+};
+
+struct fonttab text_fonts[] = {
+ 11, 7, 11, 560, 0, NULL, fr_screen, "screen.r.11",
+ 12, 8, 14, 640, 0, NULL, fb_screen, "screen.b.12",
+ 14, 9, 16, 720, 0, NULL, fb_screen, "screen.b.14",
+ 16, 10, 18, 800, 0, NULL, fb_courier, "courier.b.16",
+ 18, 11, 19, 880, 0, NULL, fb_courier, "courier.b.18",
+ 24, 14, 25, 1142, 0, NULL, fb_courier, "courier.b.24",
+ /* Note nentries must match panel choice list */
+ 0, 0, 0, 0, 0, NULL, NULL, NULL
+};
+
+#define DEF_TEXTFONT 4
+static int alpha_font_index;
+static int text_font_index = DEF_TEXTFONT;
+static struct fonttab *alpha_font = NULL;
+static struct fonttab *text_font = NULL;
+
+#define SWAP(a,b) {int temp; temp=a;a=b;b=temp;}
+#define HEIGHTADJUST \
+ (tool_headerheight((int)window_get(gt_baseframe, FRAME_SHOW_LABEL)) + \
+ TOOL_BORDERWIDTH)
+
+static short iconimage[] = {
+#include "gterm.icon"
+};
+DEFINE_ICON_FROM_IMAGE (icon, iconimage);
+
+static int main_argc, tty_argc, gio_argc;
+static char **main_argv, *tty_argv[MAX_ARGS], *gio_argv[MAX_ARGS];
+static unsigned char red[2] = { 0, 255 };
+static unsigned char green[2] = { 0, 255 };
+static unsigned char blue[2] = { 128, 0 };
+
+static Menu_item logitem;
+static struct rect screen;
+static struct pixwin *pw;
+static Panel setup_panel, pause_panel;
+static Window setup_frame, pause_frame;
+static Notify_value ev_gt_ttysw();
+static Notify_value ev_ttyframe();
+static Notify_value ev_gioframe();
+static Notify_value ev_gioinput();
+static Notify_func sig_tstp();
+static tty_adjustheight();
+extern char *getenv();
+
+
+/* GTERM_MAIN -- Create the graphics terminal window tree, i.e., the panel
+ * subwindow and tty subwindow, the gio subframe canvas in which graphics
+ * will be drawn, and the setup popup which is used to set the terminal
+ * options. Only the panel and tty (text) window is shown initially; this
+ * is functionally equivalent to a shelltool window with all the same command
+ * line arguments. Additional command line arguments are recognized for
+ * initialization of the graphics frame.
+ */
+#ifdef STANDALONE
+main (argc, argv)
+#else
+gterm_main (argc, argv)
+#endif
+int argc;
+char **argv;
+{
+ char *s;
+
+ /* Set user settable options to their initial compiled in values. */
+ restore_params();
+
+ main_argc = argc;
+ main_argv = argv;
+ parse_args (argc, argv, &tty_argc, tty_argv, &gio_argc, gio_argv);
+
+ /* Screendump stuff. */
+ if (s = getenv ("R_DISPOSE"))
+ strcpy (r_dispose, s);
+ if (s = getenv ("R_FILENAME"))
+ strcpy (r_filename, s);
+ if (s = getenv ("R_RASTERFILE")) {
+ strcpy (r_filename, s);
+ r_type = 1;
+ }
+
+ /* Create the base frame for all the GTERM windows. */
+ gt_baseframe = window_create (NULL, FRAME,
+ FRAME_ICON, &icon,
+ FRAME_LABEL, "gterm - NOAO/IRAF Sunview Graphics Terminal V1.2",
+ FRAME_ARGC_PTR_ARGV, &tty_argc, tty_argv,
+ FRAME_NO_CONFIRM, FALSE,
+ 0);
+ if (gt_baseframe == NULL)
+ _exit (1);
+ screen = *(struct rect *) window_get (gt_baseframe, WIN_SCREEN_RECT);
+
+ create_tty_subwindow (tty_argc, tty_argv);
+ create_gio_popup (gio_argc, gio_argv);
+ create_frame_menu (gt_baseframe);
+
+ /* Save initial options settings for later reset (in setup panel). */
+ save_params();
+
+ notify_interpose_event_func (gt_baseframe, ev_ttyframe, NOTIFY_SAFE);
+ notify_interpose_event_func (gt_ttysw, ev_gt_ttysw, NOTIFY_SAFE);
+ notify_interpose_event_func (gio_frame, ev_gioframe, NOTIFY_SAFE);
+ notify_interpose_event_func (gio_canvas, ev_gioinput, NOTIFY_SAFE);
+
+ notify_set_signal_func (gt_baseframe, sig_tstp, SIGTSTP, NOTIFY_SYNC);
+
+#ifdef TTY_TTY_FD
+ /* SunOS 3.4 provides just what we needed to fix the pty-fd kludge! */
+ pty_fd = (int) window_get (ttysw, TTY_TTY_FD);
+#endif
+ gio_setup (pty_fd, pw);
+ gio_hardreset (DEF_TEKXRES, DEF_TEKYRES, alpha_font, text_font);
+
+ window_main_loop (gt_baseframe);
+ exit (0);
+}
+
+
+/* PARSE_ARGS -- Parse the argument list into the arguments for the tty
+ * frame and the arguments for the gio frame. This is very easy; the gio
+ * args, if any, are whatever follows the arg "-G" in the argument list.
+ * All args preceding the -G are considered to be tty args.
+ */
+static
+parse_args (argc, argv, tty_argc, tty_argv, gio_argc, gio_argv)
+int argc;
+char *argv[];
+int *tty_argc, *gio_argc;
+char *tty_argv[], *gio_argv[];
+{
+ register char *argp, *last;
+ register int arg = 1;
+
+ /* Copy the tty arguments. */
+ tty_argv[0] = argv[0];
+ for (*tty_argc=1; arg <= argc && (argp = argv[arg]) != NULL; arg++)
+ if (strcmp (argv[arg], "-G") != 0) {
+ tty_argv[(*tty_argc)++] = argp;
+ if ((strcmp(argp, "-Ws") == 0) || (strcmp(argp, "-size") == 0))
+ height_is_set = 1;
+ else if ((strcmp(argp, "-Wh") == 0) ||
+ (strcmp(argp, "-height") == 0)) {
+ height_is_set = 0;
+ tty_nlines = atoi (argv[arg+1]);
+ }
+ } else {
+ arg++;
+ break;
+ }
+
+ gio_argv[0] = argv[0];
+ last = "";
+
+ /* Copy the gio arguments. */
+ for (*gio_argc=1; arg <= argc && (argp = argv[arg]) != NULL; arg++) {
+
+ /* If an argument string is encountered which is not an argument
+ * to a GIO window switch, assume that it is the first word of the
+ * command to be executed in the text window, and move the
+ * remaining arguments to the tty_argv list.
+ */
+ if (strncmp(last,"-Gopen",3) && strncmp(last,"-Gclose",3) &&
+ isalpha(argp[0])) {
+
+ for (; arg <= argc && (argp = argv[arg]) != NULL; arg++)
+ tty_argv[(*tty_argc)++] = argp;
+ break;
+ }
+
+ gio_argv[(*gio_argc)++] = argp;
+ last = argp;
+ }
+
+ tty_argv[(*tty_argc)] = NULL;
+ gio_argv[(*gio_argc)] = NULL;
+}
+
+
+/* CREATE_TTY_SUBWINDOW -- Create a standard TTY subwindow of the base frame.
+ * This code emulates the shelltool arguments.
+ */
+static
+create_tty_subwindow (argc, argv)
+int argc;
+char *argv[];
+{
+ static char *sh_argv[2];
+ register char *argp;
+ register int arg;
+ char **tty_argv = argv;
+ char *tool_name = argv[0];
+ char *init_cmd = NULL;
+ char *bold_name = NULL;
+ int become_console = 0;
+
+ /* Get gt_ttysw related args. The standard frame arguments will
+ * already have been removed from the list when the base frame was
+ * created, leaving only the tty specific arguments and possibly the
+ * command to be run.
+ */
+ sh_argv[0] = NULL;
+ sh_argv[1] = NULL;
+
+ for (arg=1; arg < argc && (argp = argv[arg]); arg++) {
+ if (*argp != '-')
+ break;
+ else if (!strncmp (argp, "-ignore", 3)) {
+ ignore++;
+ continue;
+ }
+
+ switch (*(argp+1)) {
+ case 'C':
+ become_console = 1;
+ break;
+ case '?':
+ tool_usage (tool_name);
+ print_usage (tool_name);
+ window_destroy (gt_baseframe);
+ exit (1);
+ case 'B':
+ if (argc > 1)
+ bold_name = argv[++arg];
+ break;
+ case 'I':
+ if (argc > 1)
+ init_cmd = argv[++arg];
+ break;
+ case 'T':
+ gio_enable (0);
+ break;
+ }
+ }
+
+ /* Get path to command interpreter to be run. */
+ if (argp == NULL || arg >= argc) {
+ shell++;
+ tty_argv = sh_argv;
+ if ((tty_argv[0] = getenv("SHELL")) == NULL) {
+ tty_argv[0] = "/bin/sh";
+ if (ignore)
+ shell = 0;
+ }
+ } else {
+ tty_argv = &argv[arg];
+ if (!strcmp(tty_argv,"csh") || !strcmp(tty_argv,"/bin/csh"))
+ shell++;
+ else if (!ignore)
+ shell++;
+ }
+
+ gt_ttysw = window_create (gt_baseframe, TTY,
+ TTY_ARGV, tty_argv,
+ TTY_QUIT_ON_CHILD_DEATH, TRUE,
+ TTY_CONSOLE, become_console,
+ 0);
+
+ window_set (gt_ttysw,
+ WIN_CONSUME_KBD_EVENTS, KEY_TOP(7), KEY_TOP(8), KEY_TOP(9), 0,
+ 0);
+
+ if (bold_name)
+ window_set (gt_ttysw, TTY_BOLDSTYLE_NAME, bold_name, 0);
+
+ /* Pass user supplied command, if given, to the shell. */
+ if (init_cmd) {
+ int len = strlen (init_cmd);
+
+ if (init_cmd[len-1] != '\n') {
+ init_cmd[len] = '\n';
+ len++;
+ }
+ ttysw_input (gt_ttysw, init_cmd, len);
+ }
+
+ window_fit_height (gt_ttysw);
+ window_fit_height (gt_baseframe);
+
+ /* Only correct the height if the user did not give a pixel value
+ * for the height.
+ */
+ if (!height_is_set)
+ tty_adjustheight (tty_nlines);
+}
+
+
+/* CREATE_GIO_POPUP -- Create a canvas popup to be used for graphics i/o.
+ * We use a separate subframe for this so that it may be moved and sized
+ * independently of the tty window; tty windows like to high and narrow
+ * (lots of lines) whereas graphs tend to be short and wide (good resolution
+ * in X). We also like a large graph window to get good resolution, but
+ * a separate window is desired so that the screen space can be freed up
+ * when we are not actually doing graphics, and so that the last graph can
+ * easily be recalled at any time. The standard graphics window should have
+ * a landscape aspect ratio (y/x=.77), like most terminals and laser printers.
+ */
+static
+create_gio_popup (argc, argv)
+int argc;
+char **argv;
+{
+ static unsigned char mono[2];
+ register char *argp;
+ register int arg;
+ char *frame_argv[64];
+ int frame_argc;
+ char pathname[256];
+ char mapname[64];
+ int name;
+
+ frame_argv[0] = argv[0];
+ frame_argc = 1;
+
+ /* Override the builtin defaults with the values given by the user
+ * on the command line, if any.
+ */
+ for (arg=1; arg <= argc && (argp = argv[arg]) != NULL; arg++) {
+ /* Standard Sunview frame args. */
+ if (!strcmp (argp, "-Wb") || !strncmp (argp, "-back", 5)) {
+ red[0] = atoi (argv[++arg]);
+ green[0] = atoi (argv[++arg]);
+ blue[0] = atoi (argv[++arg]);
+ } else if (!strcmp (argp, "-Wf") || !strncmp (argp, "-fore", 5)) {
+ red[1] = atoi (argv[++arg]);
+ green[1] = atoi (argv[++arg]);
+ blue[1] = atoi (argv[++arg]);
+ } else if (!strcmp (argp, "-Wp") || !strncmp (argp, "-pos", 4)) {
+ win_xoff = atoi (argv[++arg]) -
+ (int) window_get (gt_baseframe, WIN_X);
+ win_yoff = atoi (argv[++arg]) -
+ (int) window_get (gt_baseframe, WIN_Y);
+ } else if (!strcmp (argp, "-Ws") || !strncmp (argp, "-size", 5)) {
+ win_xsize = atoi (argv[++arg]) - (TOOL_BORDERWIDTH * 2);
+ win_ysize = atoi (argv[++arg]) - (TOOL_BORDERWIDTH * 2);
+
+ /* Graphics options. */
+ } else if (!strncmp (argp, "-Gopen", 3)) {
+ argp = argv[++arg];
+ if (!strncmp (argp, "noaction", 1))
+ openws_action = NO_ACTION;
+ else if (!strncmp (argp, "show", 1))
+ openws_action = SHOW_FRAME;
+ else if (!strncmp (argp, "expose", 1))
+ openws_action = EXPOSE_FRAME;
+ else {
+ fprintf (stderr,
+ "Warning: unknown argument `%s' to -Gopen\n", argp);
+ }
+ } else if (!strncmp (argp, "-Gclose", 3)) {
+ argp = argv[++arg];
+ if (!strncmp (argp, "noaction", 1))
+ closews_action = NO_ACTION;
+ else if (!strncmp (argp, "blank", 1))
+ closews_action = UNSHOW_FRAME;
+ else if (!strncmp (argp, "hide", 1))
+ closews_action = HIDE_FRAME;
+ else {
+ fprintf (stderr,
+ "Warning: unknown argument `%s' to -Gclose\n", argp);
+ }
+ } else if (!strncmp (argp, "-pause", 2)) {
+ closews_pause = 1;
+ } else if (!strncmp (argp, "-nopause", 4)) {
+ closews_pause = 0;
+ } else if (!strncmp (argp, "-retain", 4)) {
+ canvas_retained = 1;
+ } else if (!strncmp (argp, "-noretain", 6)) {
+ canvas_retained = 0;
+ } else if (!strncmp (argp, "-clip", 3)) {
+ clip_graphics = 1;
+ } else if (!strncmp (argp, "-noclip", 4)) {
+ clip_graphics = 0;
+ } else if (!strncmp (argp, "-color", 3)) {
+ color = 1;
+ } else if (!strncmp (argp, "-mono", 2)) {
+ color = 0;
+ } else if (!strncmp (argp, "-reverse", 4)) {
+ SWAP (red[0], red[1]);
+ SWAP (green[0], green[1]);
+ SWAP (blue[0], blue[1]);
+ reverse_video = 1;
+ } else if (!strncmp (argp, "-noreverse", 6)) {
+ reverse_video = 0;
+ } else if (!strncmp (argp, "-ginterm", 2)) {
+ if (argv[arg+1] && isdigit (argv[arg+1][0]))
+ strcpy (gin_modeterm, argv[++arg]);
+ if (argv[arg+1] && isdigit (argv[arg+1][0])) {
+ strcat (gin_modeterm, " ");
+ strcat (gin_modeterm, argv[++arg]);
+ }
+ gio_setginmodeterm (gin_modeterm);
+ } else if (!strncmp (argp, "-logfile", 2)) {
+ strcpy (logfile, argv[++arg]);
+ } else
+ frame_argv[frame_argc++] = argp;
+ }
+
+ /* Open the tek-alpha and text mode graphics fonts. */
+ if (gio_getbestfont (win_xsize, win_ysize) == -1)
+ exit (1);
+
+ /* Open graphics window and canvas. Generate a default size
+ * landscape mode window and position it relative to the base frame.
+ */
+ gio_frame = window_create (gt_baseframe, FRAME,
+ FRAME_ARGS, frame_argc, frame_argv,
+ FRAME_NO_CONFIRM, TRUE,
+ WIN_HEIGHT, win_ysize + (TOOL_BORDERWIDTH * 2),
+ WIN_WIDTH, win_xsize + (TOOL_BORDERWIDTH * 2),
+ WIN_X, win_xoff,
+ WIN_Y, win_yoff,
+ 0);
+ gio_canvas = window_create (gio_frame, CANVAS,
+ CANVAS_RETAINED, canvas_retained ? TRUE : FALSE,
+ 0);
+
+ /* Set the initial "plus" type graphics cursor. This changes to
+ * a full crosshair cursor only when the cursor position is being
+ * read by the applications program.
+ */
+ gio_setcursor (CURSOR_ON, CROSSHAIR_OFF);
+
+ /* Set input event flags. */
+ window_set (gio_canvas,
+ WIN_CONSUME_PICK_EVENTS, WIN_NO_EVENTS,
+ WIN_MOUSE_BUTTONS, LOC_DRAG, WIN_UP_EVENTS, 0,
+ WIN_CONSUME_KBD_EVENTS, WIN_NO_EVENTS, WIN_ASCII_EVENTS,
+ WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS, 0,
+ 0);
+
+ /* Get canvas pixwin */
+ pw = canvas_pixwin (gio_canvas);
+ if (pw->pw_pixrect->pr_depth == 1)
+ color = 0;
+
+ /* Set up the default color map. */
+ sprintf (mapname, "MONO%04d", getpid() % 10000);
+ pw_setcmsname (pw, mapname);
+ pw_putcolormap (pw, 0, 2, red, green, blue);
+
+ if (!color) {
+ /* Monochrome graphics. */
+ /* window_set (gio_canvas, CANVAS_FAST_MONO, TRUE, 0); */
+ if (reverse_video)
+ pw_whiteonblack (pw, 0, 1);
+ else
+ pw_blackonwhite (pw, 0, 1);
+ }
+}
+
+
+static Panel_item pan_graphics_enable, pan_openws_action, pan_closews_action;
+static Panel_item pan_closews_pause, pan_retain_graphics, pan_clip_graphics;
+static Panel_item pan_graphics_screen, pan_graphics_video, pan_alpha_font;
+
+/* CREATE_SETUP_POPUP -- Create the popup menu used to set the terminal
+ * setup options.
+ */
+static
+create_setup_popup()
+{
+ extern reset_proc(), clear_proc(), gclear_proc();
+ extern setup_proc(), toggle_graphics();
+ static panel_set_item(), set_ginmodeterm(), set_logfile();
+
+ setup_frame = window_create (gt_baseframe, FRAME,
+ FRAME_NO_CONFIRM, TRUE,
+ WIN_X, setup_xoff,
+ WIN_Y, setup_yoff,
+ 0);
+ setup_panel = window_create (setup_frame, PANEL, 0);
+
+ panel_create_item (setup_panel, PANEL_MESSAGE,
+ PANEL_ITEM_X, ATTR_COL(11),
+ PANEL_ITEM_Y, ATTR_ROW(0),
+ PANEL_LABEL_STRING, "Graphics Terminal Setup Options",
+ 0);
+
+ pan_graphics_enable = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(1),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Graphics plane: ",
+ PANEL_CHOICE_STRINGS, "Disable", "Enable",
+ "Discard Graphics Data", 0,
+ PANEL_VALUE, graphics_enable,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_openws_action = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(2),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Open workstation action: ",
+ PANEL_CHOICE_STRINGS, "No action", "Show graphics",
+ "Expose graphics", 0,
+ PANEL_VALUE, openws_action,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_closews_action = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(3),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Close workstation action: ",
+ PANEL_CHOICE_STRINGS, "No action", "Blank graphics",
+ "Hide graphics", 0,
+ PANEL_VALUE, closews_action,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_closews_pause = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(4),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Pause on close workstation: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, closews_pause,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_retain_graphics = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(5),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Retain graphics frame: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, canvas_retained,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_clip_graphics = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(6),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Clip graphics: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, clip_graphics,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ if (pw->pw_pixrect->pr_depth > 1) {
+ pan_graphics_screen = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(7),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Graphics screen type: ",
+ PANEL_CHOICE_STRINGS, "Mono", "Color", 0,
+ PANEL_VALUE, color,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+ } else {
+ pan_graphics_screen = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(7),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Graphics screen type: ",
+ PANEL_CHOICE_STRINGS, "Mono only", 0,
+ PANEL_VALUE, color,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+ }
+
+ pan_graphics_video = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(8),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Graphics video: ",
+ PANEL_CHOICE_STRINGS, "Normal", "Reverse", 0,
+ PANEL_VALUE, reverse_video,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_alpha_font = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(9),
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Graphics font and screen sizes:",
+ PANEL_CHOICE_STRINGS, alpha_fonts[0].label,
+ alpha_fonts[1].label,
+ alpha_fonts[2].label,
+ alpha_fonts[3].label,
+ alpha_fonts[4].label,
+ alpha_fonts[5].label,
+ 0,
+ PANEL_VALUE, alpha_font_index,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(10) + 3,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Logfile name: ",
+ PANEL_VALUE, logfile,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, 20,
+ PANEL_NOTIFY_PROC, set_logfile,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(11) + 3,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "GIN mode terminators (octal): ",
+ PANEL_VALUE, gin_modeterm,
+ PANEL_VALUE_STORED_LENGTH, SZ_GINMODETERM,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_GINMODETERM,
+ PANEL_NOTIFY_PROC, set_ginmodeterm,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(12) + 3,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Reset", 0,0),
+ PANEL_NOTIFY_PROC, reset_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Clear", 0,0),
+ PANEL_NOTIFY_PROC, clear_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Gclear", 0,0),
+ PANEL_NOTIFY_PROC, gclear_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Show graphics", 0,0),
+ PANEL_NOTIFY_PROC, toggle_graphics,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Quit", 0,0),
+ PANEL_NOTIFY_PROC, setup_proc,
+ 0);
+
+ window_fit (setup_panel);
+ window_fit (setup_frame);
+}
+
+
+/* PANEL_SET_ITEM -- Called when an item is seleted in the setup panel to
+ * set the associated global variable and possibly take some action.
+ */
+static
+panel_set_item (item, value)
+Panel_item item;
+int value;
+{
+ if (item == pan_graphics_enable) {
+ gio_enable (graphics_enable = value);
+ } else if (item == pan_openws_action) {
+ openws_action = value;
+ } else if (item == pan_closews_action) {
+ closews_action = value;
+ } else if (item == pan_closews_pause) {
+ if (closews_pause != value) {
+ closews_pause = value;
+ show_pausepanel (0);
+ }
+ } else if (item == pan_clip_graphics) {
+ clip_graphics = value;
+ } else if (item == pan_graphics_screen) {
+ if (color != value) {
+ if (value)
+ pw_putcolormap (pw, 0, 2, red, green, blue);
+ else if (reverse_video)
+ pw_whiteonblack (pw, 0, 1);
+ else
+ pw_blackonwhite (pw, 0, 1);
+ color = value;
+ }
+ } else if (item == pan_graphics_video) {
+ if (reverse_video != value) {
+ if (pw->pw_pixrect->pr_depth > 1) {
+ unsigned char r[2], g[2], b[2];
+
+ pw_getcolormap (pw, 0, 2, r, g, b);
+ SWAP (r[0], r[1]);
+ SWAP (g[0], g[1]);
+ SWAP (b[0], b[1]);
+ pw_putcolormap (pw, 0, 2, r, g, b);
+ if (color)
+ pw_getcolormap (pw, 0, 2, red, green, blue);
+ } else if (value) {
+ pw_whiteonblack (pw, 0, 1);
+ } else
+ pw_blackonwhite (pw, 0, 1);
+
+ reverse_video = value;
+ }
+ } else if (item == pan_retain_graphics) {
+ if (canvas_retained != value) {
+ canvas_retained = value;
+ notify_remove_event_func (gio_canvas,
+ ev_gioinput, NOTIFY_SAFE);
+ window_destroy (gio_frame);
+ parse_args (main_argc, main_argv,
+ &tty_argc, tty_argv, &gio_argc, gio_argv);
+ create_gio_popup (gio_argc, gio_argv);
+ notify_interpose_event_func (gio_canvas,
+ ev_gioinput, NOTIFY_SAFE);
+ gio_setup (pty_fd, pw);
+ gio_reset (DEF_TEKXRES, DEF_TEKYRES, alpha_font, text_font);
+ }
+ } else if (item == pan_alpha_font) {
+ if (alpha_font_index != value) {
+ struct fonttab *ft = &alpha_fonts[value];
+ int xorigin, yorigin, xoffset, yoffset;
+ int g_xsize, g_ysize, g_xorig, g_yorig;
+
+ /* Determine whether the offset of the subframe from the base
+ * frame needs to be modified to keep the frame on the screen.
+ */
+ xorigin = (int) window_get (gt_baseframe, WIN_X);
+ yorigin = (int) window_get (gt_baseframe, WIN_Y);
+ xoffset = win_xoff;
+ yoffset = win_yoff;
+
+ g_xsize = ft->win_xsize + (TOOL_BORDERWIDTH * 2);
+ g_ysize = ft->win_ysize + (TOOL_BORDERWIDTH * 2);
+ g_xorig = xorigin + xoffset; /* screen relative */
+ g_yorig = yorigin + yoffset; /* screen relative */
+
+ if (g_xorig + g_xsize >= screen.r_width)
+ xoffset -= (g_xorig + g_xsize - screen.r_width);
+ if (g_yorig + g_ysize >= screen.r_height)
+ yoffset -= (g_yorig + g_ysize - screen.r_height);
+
+ /* Resize and/or move the gio frame. The gio_frame event
+ * handler will detect the resize and select a new font.
+ */
+ window_set (gio_frame, WIN_SHOW, FALSE,
+ WIN_WIDTH, g_xsize, WIN_HEIGHT, g_ysize, 0);
+
+ /* Cannot set a negative subframe offset, so move base frame
+ * to screen origin temporarily if the gio frame won't fit at
+ * the lower right. Note that win_[xy]off are not permanently
+ * changed, so restoring the original size should return the
+ * window to its original position.
+ */
+ if (gio_frame_has_moved = (xoffset < 0 || yoffset < 0)) {
+ window_set (gt_baseframe, WIN_X, 0, WIN_Y, 0, 0);
+ window_set (gio_frame,
+ WIN_X, xorigin + xoffset, WIN_Y, yorigin + yoffset, 0);
+ window_set (gt_baseframe, WIN_X,xorigin, WIN_Y,yorigin, 0);
+ } else
+ window_set (gio_frame, WIN_X, xoffset, WIN_Y, yoffset, 0);
+
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+
+ /* If the setup panel is on, move it to the top. */
+ if ((int) window_get (setup_frame, WIN_SHOW) == TRUE)
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+ }
+ }
+}
+
+
+/* TOGGLE_FULLSCREEN -- Toggle between a full screen graph and a regular size
+ * graph.
+ */
+static
+toggle_fullscreen()
+{
+ static save_index = -1;
+
+ if (alpha_font_index == FULLSCREEN && save_index >= 0)
+ panel_set_item (pan_alpha_font, save_index);
+ else {
+ save_index = alpha_font_index;
+ panel_set_item (pan_alpha_font, FULLSCREEN);
+ }
+}
+
+
+/* SET_GINMODETERM -- Set the GIN mode terminators.
+ */
+static Panel_setting
+set_ginmodeterm (item, event)
+Panel_item item;
+Event *event;
+{
+ strcpy (gin_modeterm,
+ (char *)panel_get_value (item));
+ gio_setginmodeterm (gin_modeterm);
+
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_LOGFILE -- Set the filename of the logfile.
+ */
+static Panel_setting
+set_logfile (item, event)
+Panel_item item;
+Event *event;
+{
+ if (gt_logfp) {
+ fclose (gt_logfp);
+ gt_logfp = NULL;
+ menu_set (logitem, MENU_STRING, "Logging on", 0);
+ }
+
+ strcpy (logfile,
+ (char *)panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* SETUP_PROC -- Toggle whether or not the setup panel is shown.
+ */
+static
+setup_proc()
+{
+ if (setup_frame == NULL) {
+ create_setup_popup();
+ create_setup_popup (0, NULL);
+ panel_set_item();
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+ } else {
+ window_destroy (setup_frame);
+ setup_frame = NULL;
+ }
+}
+
+
+/* TEXTCOPY_PROC -- Make a hardcopy of the tty window on the laserwriter.
+ */
+static
+textcopy_proc()
+{
+ int depth = 1;
+
+ window_set (gt_baseframe, WIN_SHOW, TRUE, 0);
+ notify_dispatch();
+
+ screendump (
+ (int)window_get(gt_ttysw,WIN_FD), win_get_pixwin(gt_ttysw),
+ (int) window_get (gt_baseframe, WIN_WIDTH) - TOOL_BORDERWIDTH * 2,
+ (int) window_get (gt_baseframe, WIN_HEIGHT) - HEIGHTADJUST,
+ (int) window_get (gt_baseframe, WIN_X) + TOOL_BORDERWIDTH,
+ (int) window_get (gt_baseframe, WIN_Y) + HEIGHTADJUST -
+ TOOL_BORDERWIDTH,
+ depth);
+}
+
+
+/* GRAPHCOPY_PROC -- Make a hardcopy of the gio window on the laserwriter.
+ */
+static
+graphcopy_proc()
+{
+ int depth = 1;
+
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+ notify_dispatch();
+
+ screendump (
+ (int)window_get(gio_canvas,WIN_FD), pw,
+ (int) window_get (gio_frame, WIN_WIDTH) - TOOL_BORDERWIDTH * 2,
+ (int) window_get (gio_frame, WIN_HEIGHT) - TOOL_BORDERWIDTH * 2,
+ (int) window_get (gt_baseframe, WIN_X) +
+ (int) window_get (gio_frame, WIN_X) + TOOL_BORDERWIDTH,
+ (int) window_get (gt_baseframe, WIN_Y) +
+ (int) window_get (gio_frame, WIN_Y) + TOOL_BORDERWIDTH,
+ depth);
+}
+
+
+/* SCREENCOPY_PROC -- Make a bitmap hardcopy of the screen on the laserwriter.
+ */
+static
+screencopy_proc()
+{
+ int depth = 1;
+
+ screendump (
+ (int)window_get(gt_ttysw,WIN_FD), win_get_pixwin(gt_ttysw),
+ screen.r_width, screen.r_height, screen.r_left, screen.r_top,
+ depth);
+}
+
+
+/* SCREENCOPY8_PROC -- Make a greyscale hardcopy of the screen on the
+ * laserwriter.
+ */
+static
+screencopy8_proc()
+{
+ int depth = 8;
+
+ screendump (
+ (int)window_get(gt_ttysw,WIN_FD), win_get_pixwin(gt_ttysw),
+ screen.r_width, screen.r_height, screen.r_left, screen.r_top,
+ depth);
+}
+
+
+/* CREATE_PAUSE_POPUP -- Create the pause popup menu used to wait for
+ * a user response.
+ */
+static
+create_pause_popup()
+{
+ pause_frame = window_create (gt_baseframe, FRAME,
+ FRAME_NO_CONFIRM, TRUE,
+ WIN_X, win_xoff + 5,
+ WIN_Y, win_yoff + 5,
+ 0);
+ pause_panel = window_create (pause_frame, PANEL, 0);
+
+ panel_create_item (pause_panel, PANEL_MESSAGE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(0),
+ PANEL_LABEL_STRING, "Hit any key to continue",
+ 0);
+
+ window_fit (pause_panel);
+ window_fit (pause_frame);
+}
+
+
+/* SHOW_PAUSEPANEL -- Toggle display of the pause panel.
+ */
+static
+show_pausepanel (onoff)
+int onoff;
+{
+ if (onoff == 0) {
+ if (pause_frame) {
+ window_destroy (pause_frame);
+ pause_frame = NULL;
+ }
+ } else {
+ if (!pause_frame)
+ create_pause_popup();
+ window_set (pause_frame,
+ WIN_X, win_xoff + 5,
+ WIN_Y, win_yoff + 5,
+ 0);
+ window_set (pause_frame, WIN_SHOW, TRUE, 0);
+ }
+}
+
+
+/* CREATE_FRAME_MENU -- Gterm uses a special frame menu which provides the
+ * standard frame menu as a submenu.
+ */
+static
+create_frame_menu (frame)
+Frame frame;
+{
+ extern textcopy_proc(), graphcopy_proc(), screencopy_proc();
+ extern setup_proc(), toggle_graphics(), restart_childpgrp();
+ extern toggle_logging();
+ Menu new_menu, old_menu;
+
+ /* Get the standard frame menu. */
+ old_menu = (Menu) window_get (frame, WIN_MENU);
+
+ /* Create the new frame root menu */
+ new_menu = menu_create (
+ MENU_PULLRIGHT_ITEM,
+ "Frame",
+ old_menu,
+ MENU_ACTION_ITEM,
+ "Setup",
+ setup_proc,
+ MENU_ACTION_ITEM,
+ "Continue",
+ restart_childpgrp,
+ MENU_ACTION_ITEM,
+ "Logging on",
+ toggle_logging,
+ MENU_ACTION_ITEM,
+ "Show graph",
+ toggle_graphics,
+ MENU_ACTION_ITEM,
+ "Textcopy",
+ textcopy_proc,
+ MENU_ACTION_ITEM,
+ "Graphcopy",
+ graphcopy_proc,
+ MENU_ACTION_ITEM,
+ "Screencopy",
+ screencopy_proc,
+ /*
+ MENU_ACTION_ITEM,
+ "Screencopy8",
+ screencopy8_proc,
+ */
+ 0);
+
+ logitem = menu_find (new_menu, MENU_STRING, "Logging on", 0);
+
+ /* Install the new menu. */
+ window_set (frame, WIN_MENU, new_menu, 0);
+}
+
+
+/* TOGGLE_LOGGING -- Called from the frame menu to turn logging on/off.
+ */
+static
+toggle_logging()
+{
+ if (gt_logfp) {
+ fclose (gt_logfp);
+ gt_logfp = NULL;
+ menu_set (logitem, MENU_STRING, "Logging on", 0);
+ } else {
+ if ((gt_logfp = fopen (logfile, "a")) == NULL)
+ fprintf (stderr, "cannot open logfile %s\n", logfile);
+ else
+ menu_set (logitem, MENU_STRING, "Logging off", 0);
+ }
+}
+
+
+/* EV_GT_TTYSW -- TTY subwindow input event handler.
+ */
+static Notify_value
+ev_gt_ttysw (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ register int key = event_id(event);
+
+ if (pause_mode && (event_is_ascii(event) || event_is_button(event))) {
+ /* If we are pausing for input before closing the workstation,
+ * complete the close workstation operation and discard the event.
+ */
+ show_pausepanel (0);
+ gio_close_workstation();
+ return (NOTIFY_DONE);
+ }
+
+ if (event_is_ascii(event)) {
+ /* Save the cursor position in tty window for later restoration. */
+ tty_lastx = event_x(event);
+ tty_lasty = event_y(event);
+
+ /* Ignore control codes if -ignore set? */
+ if (iscntrl(key) && key != '\r' && ignore_inputevent(event))
+ return (NOTIFY_DONE);
+
+ /* Ignore typein into text window while a cursor read is in
+ * progress, else an invalid exit from the cursor read may result.
+ */
+ if (cursor_read_pending)
+ return (NOTIFY_DONE);
+
+ } else if (event_id(event) == KEY_TOP(7) && event_is_down(event)) {
+ /* Toggle between full screen and regular size graph. */
+ toggle_fullscreen();
+
+ } else if (event_id(event) == KEY_TOP(8) && event_is_down(event)) {
+ /* Clear or enable the graphics plane. */
+ char buf[4];
+
+ tty_lastx = event_x(event);
+ tty_lasty = event_y(event);
+
+ /* If already in graphics mode, clear the graphics plane, else
+ * switch to graphics mode w/o clearing the screen (the user can
+ * simply type F8 again if they also want to clear the screen).
+ */
+ if (gio_graphicsenabled && !cursor_read_pending) {
+ buf[0] = '\035';
+ buf[1] = '\033';
+ buf[2] = '\014';
+ ev_ptyoutput (buf, 3);
+
+ } else if (!gio_graphicsenabled && graphics_enable == GRAPHICS_ON) {
+ buf[0] = '\035';
+ buf[1] = '\037';
+ ev_ptyoutput (buf, 2);
+ /*
+ window_set (gt_ttysw,
+ WIN_CONSUME_KBD_EVENTS, KEY_TOP(9), 0,
+ 0);
+ */
+ }
+
+ return (NOTIFY_DONE);
+
+ } else if (event_id(event) == KEY_TOP(9) && event_is_down(event)) {
+ /* Exit graphics mode, returning to text mode.
+ */
+ if (gio_graphicsenabled && !cursor_read_pending) {
+ char buf[1];
+
+ cursor_used++; /* disable pause on CW */
+ buf[0] = '\030';
+ ev_ptyoutput (buf, 1);
+ /*
+ window_set (gt_ttysw,
+ WIN_IGNORE_KBD_EVENTS, KEY_TOP(9), 0,
+ 0);
+ */
+ return (NOTIFY_DONE);
+ } else if (!gio_graphicsenabled)
+ ttysw_output (gt_ttysw, "\f", 1);
+ }
+
+ /* Let frame operate upon the event. */
+ return (notify_next_event_func (frame, event, arg, type));
+}
+
+
+/* IGNORE_INPUTEVENT -- Ignore the current input event?
+ * The following is a kludge intended to prevent delivering the suspend
+ * signal to a process which is run in the gterm window without benefit of
+ * an intermediate shell. This is only done if "-ignore" (stop signals)
+ * is specified on the command line, and only if the command being run in
+ * GTERM is not a shell. This is a dangerous thing to do, as the characters
+ * may be valid input data to a program operating in raw input mode.
+ */
+static
+ignore_inputevent (event)
+Event *event;
+{
+ register int key = event_id(event);
+ struct ltchars lt;
+ struct sgttyb sg;
+
+ if (!shell && event_ctrl_is_down(event)) {
+ /* Control code assignments may change, so reread them. */
+ if (ioctl (pty_fd, TIOCGLTC, &lt) != -1) {
+ t_suspc = lt.t_suspc;
+ t_dsuspc = lt.t_dsuspc;
+ }
+
+ /* Echo but ignore suspend control characters if not in raw mode. */
+ if (key == t_suspc || key == t_dsuspc) {
+ if (ioctl(pty_fd,TIOCGETP,&sg) != -1 && (!(sg.sg_flags&RAW))) {
+ char out[2];
+ out[0] = '^';
+ out[1] = (key & 077) + '@';
+ ttysw_output (gt_ttysw, out, 2);
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+/* EV_TTYFRAME -- TTY frame event handler.
+ */
+static Notify_value
+ev_ttyframe (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ Notify_value value;
+ static int ignore_resize = 1;
+
+ /* Let frame operate upon the event. */
+ value = notify_next_event_func (frame, event, arg, type);
+
+ if (event_id(event) == WIN_RESIZE) {
+ /* Tty_adjustheight, if it resizes the window, will cause a resize
+ * event to be queued and we will be called back. Set a flag to
+ * ignore this event or an infinite loop will result.
+ */
+ if (ignore_resize)
+ ignore_resize = 0;
+ else {
+ ignore_resize = 1;
+ tty_adjustheight (0);
+ }
+ }
+
+ return (value);
+}
+
+
+/* TTY_ADJUSTHEIGHT -- Called when the tty window is initially sized or when
+ * it is dynamically resized to adjust the size to an integral number of lines
+ * and allow space for the panel at the top (this is NOT done when the tty
+ * window resizes itself, although it should be).
+ */
+static
+tty_adjustheight (nrows)
+int nrows;
+{
+ struct pixfont *pf;
+ int height;
+
+ if (nrows == 0)
+ nrows = (int) window_get (gt_ttysw, WIN_ROWS);
+
+ pf = (struct pixfont *) window_get (gt_baseframe, WIN_FONT);
+ height = HEIGHTADJUST + pf->pf_defaultsize.y * nrows + 1;
+
+ if (height != (int) window_get (gt_baseframe, WIN_HEIGHT))
+ window_set (gt_baseframe, WIN_HEIGHT, height, 0);
+}
+
+
+/* GIO_OPEN_WORKSTATION -- Called by the low level gtermio code when there is
+ * output to the graphics frame but the frame is in a closed state.
+ */
+gio_open_workstation()
+{
+ cursor_used = 0;
+ gio_setcursor (CURSOR_OFF, 0);
+ if (openws_action == NO_ACTION)
+ return;
+
+ if (!gio_frame_has_moved)
+ window_set (gio_frame,
+ WIN_HEIGHT, win_ysize + (TOOL_BORDERWIDTH * 2),
+ WIN_WIDTH, win_xsize + (TOOL_BORDERWIDTH * 2),
+ WIN_X, win_xoff,
+ WIN_Y, win_yoff,
+ 0);
+
+ switch (openws_action) {
+ case SHOW_FRAME:
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+ break;
+ case EXPOSE_FRAME:
+ set_coverwindow (gio_frame, gt_baseframe);
+ break;
+ }
+
+ /* If the setup panel is on, move it to the top. */
+ if ((int) window_get (setup_frame, WIN_SHOW) == TRUE)
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+}
+
+
+/* GIO_CLOSE_WORKSTATION -- Called by the low level gtermio code when the CAN
+ * sequence is received, to turn off or hide the now inactive graphics window.
+ */
+gio_close_workstation()
+{
+ if (cursor_used)
+ window_set (gt_ttysw, WIN_MOUSE_XY, tty_lastx, tty_lasty, 0);
+ gio_setcursor (CURSOR_ON, CROSSHAIR_OFF);
+
+ if (closews_action != NO_ACTION)
+ if (closews_pause && !pause_mode && !cursor_used) {
+ show_pausepanel (1);
+ pause_mode = 1;
+ return;
+ }
+
+ /* If the window has been closed, do nothing. */
+ if (window_get (gio_frame, WIN_SHOW))
+ switch (closews_action) {
+ case UNSHOW_FRAME:
+ window_set (gio_frame, WIN_SHOW, FALSE, 0);
+ break;
+ case HIDE_FRAME:
+ set_coverwindow (gt_baseframe, gio_frame);
+ break;
+ }
+
+ pause_mode = 0;
+}
+
+
+/* SET_COVERWINDOW -- Make the first window cover the second if they overlap.
+ */
+static
+set_coverwindow (win_top, win_bot)
+Frame win_top, win_bot;
+{
+ int fd_top, fd_bot;
+
+ fd_top = (int) window_get (gt_baseframe, WIN_FD);
+ fd_bot = (int) window_get (gio_frame, WIN_FD);
+
+ win_lockdata (fd_top);
+ win_lockdata (fd_bot);
+ win_remove (fd_bot);
+ win_setlink (fd_bot, WL_COVERING, win_fdtonumber(fd_top));
+ win_insert (fd_bot);
+ win_unlockdata (fd_bot);
+ win_unlockdata (fd_top);
+}
+
+
+/* EV_GIOFRAME -- GIO frame event handler.
+ */
+static Notify_value
+ev_gioframe (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ Notify_value value;
+ int o_xoff, o_yoff, n_xoff, n_yoff;
+
+ o_xoff = (int) window_get (gio_frame, WIN_X);
+ o_yoff = (int) window_get (gio_frame, WIN_Y);
+
+ /* Let frame operate upon the event. */
+ value = notify_next_event_func (frame, event, arg, type);
+
+ n_xoff = (int) window_get (gio_frame, WIN_X);
+ n_yoff = (int) window_get (gio_frame, WIN_Y);
+
+ /* Determine if the graphics window has been moved by the user.
+ * If so, we don't want to move it ourselves any more.
+ */
+ if (n_xoff != o_xoff || n_yoff != o_yoff) {
+ gio_frame_has_moved = 1;
+ win_xoff = n_xoff;
+ win_yoff = n_yoff;
+ }
+
+ return (value);
+}
+
+
+/* EV_GIOINPUT -- GIO input event handler.
+ */
+static Notify_value
+ev_gioinput (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ register int key;
+ Notify_value value;
+ char ch;
+
+ /* If we are pausing for input before closing the workstation,
+ * complete the close workstation operation and discard the event.
+ */
+ if (pause_mode && (event_is_ascii(event) || event_is_button(event))) {
+ show_pausepanel (0);
+ gio_close_workstation();
+ return (NOTIFY_DONE);
+ }
+
+ /* Let frame operate upon the event. */
+ value = notify_next_event_func (frame, event, arg, type);
+
+ switch (key = event_id (event)) {
+ case WIN_RESIZE:
+ win_xsize = (int) window_get (gio_frame, WIN_WIDTH) -
+ (TOOL_BORDERWIDTH * 2);
+ win_ysize = (int) window_get (gio_frame, WIN_HEIGHT) -
+ (TOOL_BORDERWIDTH * 2);
+ if (!gio_frame_has_moved) {
+ win_xoff = (int) window_get (gio_frame, WIN_X);
+ win_yoff = (int) window_get (gio_frame, WIN_Y);
+ }
+
+ if (!cursor_read_pending)
+ gio_setcursor (CROSSHAIR_OFF, CURSOR_ON);
+
+ gio_getbestfont (win_xsize, win_ysize);
+ gio_reset (DEF_TEKXRES, DEF_TEKYRES, alpha_font, text_font);
+ break;
+
+ case MS_RIGHT:
+ /* When a cursor read is not in progress, i.e., the gio window
+ * is idle, the right mouse button will cause the crosshairs to
+ * be displayed while the button is held down and the cursor is
+ * in the graphics window. During a cursor read the right button
+ * may be used to alias a key, like the left and middle buttons
+ * below.
+ */
+ if (cursor_read_pending && event_is_down(event)) {
+ if (event_ctrl_is_down (event))
+ key_right = last_key;
+ else if (key = key_right)
+ goto readcur;
+ } else if (!cursor_read_pending) {
+ if (event_is_down (event))
+ gio_setcursor (CROSSHAIR_ON, CURSOR_ON);
+ else
+ gio_setcursor (CROSSHAIR_OFF, 0);
+ }
+ break;
+
+ case MS_LEFT:
+ case MS_MIDDLE:
+ /* The left and middle mouse buttons may be used while in the
+ * graphics window to alias keyboard events. Typing ctrl/button
+ * causes the last key to be aliased with the indicated button.
+ * Thereafter, pressing that mouse button during a cursor read
+ * causes the cursor read to terminate, returning the aliased
+ * key just as if the key had been typed on the keyboard.
+ */
+ if (event_is_down (event))
+ if (event_ctrl_is_down (event)) {
+ if (key == MS_LEFT)
+ key_left = last_key;
+ else
+ key_middle = last_key;
+ } else if (cursor_read_pending) {
+ if (key == MS_LEFT)
+ key = key_left;
+ else
+ key = key_middle;
+ if (key)
+ goto readcur;
+ }
+ break;
+
+ case KEY_TOP(7):
+ /* Toggle full screen graphics mode.
+ */
+ if (event_is_down(event))
+ toggle_fullscreen();
+ break;
+
+ case KEY_TOP(8):
+ /* Clear the graphics screen, leaving the terminal in graphics
+ * mode.
+ */
+ if (event_is_down(event) && !cursor_read_pending) {
+ char buf[3];
+
+ buf[0] = '\035';
+ buf[1] = '\033';
+ buf[2] = '\014';
+ ev_ptyoutput (buf, 3);
+ }
+ break;
+
+ case KEY_TOP(9):
+ case KEY_LEFT(7):
+ /* Exit graphics mode, returning to text mode.
+ */
+ if (event_is_down(event) && !cursor_read_pending) {
+ char buf[1];
+
+ /*
+ window_set (gt_ttysw,
+ WIN_IGNORE_KBD_EVENTS, KEY_TOP(9), 0,
+ 0);
+ */
+
+ if (key == KEY_LEFT(7)) /* delay CW until after L7 */
+ gio_pause (500);
+
+ cursor_used++; /* disable pause on CW */
+ buf[0] = '\030';
+ ev_ptyoutput (buf, 1);
+ }
+ break;
+
+ default:
+ /* Terminate a cursor read, returning the encoded cursor value
+ * sequence to the terminal output.
+ */
+ if (event_is_down(event) &&
+ (event_is_ascii(event) || event_is_key_right(event))) {
+
+ /* Ignore control codes if -ignore set? */
+ if (iscntrl(key) && key != '\r' && ignore_inputevent(event))
+ return (value);
+
+ /* Terminate cursor read? */
+ if (cursor_read_pending) {
+ /* Map keypad function keys to digits. */
+ if (event_is_key_right(event)) {
+ switch (key = event_id(event) - KEY_RIGHT(1) + 1) {
+ case 7: case 8: case 9:
+ break;
+ case 10: case 11: case 12:
+ key -= 6;
+ break;
+ case 13: case 14: case 15:
+ key -= 12;
+ break;
+ default:
+ return (value);
+ }
+ key += '0';
+ }
+readcur:
+ last_x = event_x (event);
+ last_y = event_y (event);
+ last_key = key;
+
+ cursor_read_pending = 0;
+ gio_setcursor (CURSOR_OFF, CROSSHAIR_OFF);
+ gio_retcursor (key, last_x, last_y);
+ enable_arrow_keys();
+
+ } else
+ write (pty_fd, (ch=key, &ch), 1);
+ }
+ }
+
+ return (value);
+}
+
+
+/* SIG_TSTP -- Signal handler for signal SIGTSTP.
+ */
+static Notify_func
+sig_tstp()
+{
+ kill (getpid(), SIGSTOP);
+}
+
+
+/* RESTART_CHILDPGRP -- Send the SIGCONT signal to the process group
+ * associated with this terminal, e.g., to restart the processes after
+ * the user has accidentally typed the stop character.
+ */
+static
+restart_childpgrp()
+{
+ int pgrp;
+
+ ioctl (pty_fd, TIOCGPGRP, &pgrp);
+ killpg (pgrp, SIGCONT);
+}
+
+
+/* GIO_GETBESTFONT -- Scan the font table and open the tek-alpha font which
+ * best fits the given size window. The important dimension is the window
+ * width, since the character spacing is fixed. Also pick a text (status
+ * line) font small enough to provide space for at least 80 chars.
+ */
+static
+gio_getbestfont (xsize, ysize)
+int xsize, ysize; /* canvas size, x,y */
+{
+ register struct fonttab *ft, *o_ft;
+ register int i;
+ struct pixfont *newfont;
+ char pathname[256];
+
+ /* Select tek-alpha font.
+ */
+ for (i=0, o_ft=ft=alpha_fonts; ft->pointsize != 0; o_ft=ft++, i++)
+ if (ft->win_xsize > xsize)
+ break;
+
+ if (i == 0)
+ i = 1;
+
+ sprintf (pathname, o_ft->path, o_ft->pointsize);
+ if ((newfont = pf_open (pathname)) == NULL) {
+ fprintf (stderr, "cannot open font %s\n", pathname);
+ return (-1);
+ } else if (alpha_font != NULL)
+ pf_close (alpha_font->pixfont);
+
+ alpha_font = o_ft;
+ alpha_font->pixfont = newfont;
+ alpha_font_index = i - 1;
+
+ /* Load the text (status line) font.
+ */
+ o_ft = &text_fonts[i-1];
+ sprintf (pathname, o_ft->path, o_ft->pointsize);
+ if ((newfont = pf_open (pathname)) == NULL) {
+ fprintf (stderr, "cannot open font %s\n", pathname);
+ return (-1);
+ } else if (text_font != NULL)
+ pf_close (text_font->pixfont);
+
+ text_font = o_ft;
+ text_font->pixfont = newfont;
+ text_font_index = i - 1;
+
+ return (0);
+}
+
+
+/* GIO_SETCURSOR -- Set graphics frame cursor options.
+ */
+gio_setcursor (op1, op2)
+int op1, op2;
+{
+ Cursor cursor;
+ int option[2], i;
+ int type=cursor_type, show=cursor_show;
+
+ /* Normalize the argument list. */
+ for (option[0]=op1, option[1]=op2, i=0; i < 2; i++)
+ switch (option[i]) {
+ case CROSSHAIR_OFF:
+ case CROSSHAIR_ON:
+ type = option[i];
+ break;
+ case CURSOR_OFF:
+ case CURSOR_ON:
+ show = option[i];
+ break;
+ }
+
+ /* Do we need to change anything? */
+ if (type == cursor_type && show == cursor_show)
+ return;
+
+ /* Modify the cursor attributes. */
+ cursor = window_get (gio_canvas, WIN_CURSOR);
+ cursor_set (cursor,
+ CURSOR_SHOW_CURSOR, FALSE,
+ CURSOR_SHOW_CROSSHAIRS, (show==CURSOR_ON) ? TRUE : FALSE,
+ CURSOR_CROSSHAIR_THICKNESS, 1,
+ CURSOR_CROSSHAIR_LENGTH, (type==CROSSHAIR_ON) ? CURSOR_TO_EDGE : 10,
+ CURSOR_CROSSHAIR_OP, PIX_SRC,
+ CURSOR_CROSSHAIR_COLOR, 1,
+ 0);
+ window_set (gio_canvas, WIN_CURSOR, cursor, 0);
+
+ cursor_type = type;
+ cursor_show = show;
+}
+
+
+/* GIO_SETCURSORPOS -- Set the position of the graphics cursor within the
+ * graphics frame.
+ */
+gio_setcursorpos (x, y)
+int x, y; /* pixwin pixel coords */
+{
+ window_set (gio_canvas, WIN_MOUSE_XY, x, y, 0);
+ last_x = x;
+ last_y = y;
+ cursor_used++;
+}
+
+
+/* GIO_READCURSOR -- Initiate a cursor read. Set the cursor type to
+ * a full crosshair cursor to indicate to the user that the program is
+ * waiting for cursor input. Set the cursor read pending flag so that
+ * the next input event in graphics window will cause termination of the
+ * cursor read and transmission of the cursor value to the terminal output.
+ */
+gio_readcursor()
+{
+ /* When a cursor read is initiated, move the cursor into the gio
+ * window so that the user knows that cursor input is expected.
+ */
+ if (last_x < 0) {
+ last_x = win_xsize / 2;
+ last_y = win_ysize / 2;
+ }
+
+ /* Disable the mapping of the right function keys to the ansi arrow
+ * key escape sequences, since we want to receive these keys as
+ * function key events.
+ */
+ disable_arrow_keys();
+
+ gio_setcursorpos (last_x, last_y);
+ gio_setcursor (CURSOR_ON, CROSSHAIR_ON);
+ cursor_read_pending++;
+ cursor_used++;
+}
+
+
+/* TOGGLE_GRAPHICS -- Show or hide the graphics frame.
+ */
+static
+toggle_graphics()
+{
+ if ((int) window_get (gio_frame, WIN_SHOW) == TRUE)
+ window_set (gio_frame, WIN_SHOW, FALSE, 0);
+ else {
+ if (!gio_frame_has_moved)
+ window_set (gio_frame,
+ WIN_HEIGHT, win_ysize + (TOOL_BORDERWIDTH * 2),
+ WIN_WIDTH, win_xsize + (TOOL_BORDERWIDTH * 2),
+ WIN_X, win_xoff,
+ WIN_Y, win_yoff,
+ 0);
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+
+ /* If the setup panel is on, move it to the top. */
+ if ((int) window_get (setup_frame, WIN_SHOW) == TRUE)
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+ }
+}
+
+
+/* RESET_PROC -- Called from the setup panel to reset the state of the
+ * terminal.
+ */
+static
+reset_proc()
+{
+ /* Cancel any pending cursor read. */
+ if (cursor_read_pending) {
+ gio_retcursor ('\r', 0, 0);
+ enable_arrow_keys();
+ }
+
+ /* Cancel logging if enabled. */
+ if (gt_logfp) {
+ fclose (gt_logfp);
+ gt_logfp = NULL;
+ menu_set (logitem, MENU_STRING, "Logging on", 0);
+ }
+
+ /* Restore user settable options. */
+ restore_params();
+
+ /* Reset internal state variables. */
+ cursor_type = -1; /* crosshair or small cursor */
+ cursor_show = -1; /* cursor on or off */
+ cursor_read_pending = 0; /* waiting for cursor event */
+ key_left = 0; /* key aliased to left msbutton */
+ key_middle = 0; /* key aliased to mid msbutton */
+ key_right = 0; /* key aliased to mid msbutton */
+ last_key = 0; /* last cursor read key */
+ last_x= -1,last_y= -1; /* last cursor read position */
+ setup_xoff = 150; /* offset to setup panel */
+ setup_yoff = 150;
+ red[0] = green[0] = 0;
+ red[1] = green[1] = 255;
+ blue[0] = 128; blue[1] = 0;
+ gio_frame_has_moved = 0;
+ pause_mode = 0;
+ cursor_used = 0;
+
+ /* Reset internal state. */
+ notify_remove_event_func (gio_canvas, ev_gioinput, NOTIFY_SAFE);
+ notify_remove_event_func (gio_frame, ev_gioframe, NOTIFY_SAFE);
+ window_destroy (gio_frame);
+ parse_args (main_argc, main_argv,
+ &tty_argc, tty_argv, &gio_argc, gio_argv);
+ create_gio_popup (gio_argc, gio_argv);
+ notify_interpose_event_func (gio_frame, ev_gioframe, NOTIFY_SAFE);
+ notify_interpose_event_func (gio_canvas, ev_gioinput, NOTIFY_SAFE);
+ gio_setup (pty_fd, pw);
+ gio_hardreset (DEF_TEKXRES, DEF_TEKYRES, alpha_font, text_font);
+ gio_enable (graphics_enable);
+
+ if (pause_frame)
+ window_destroy (pause_frame);
+
+ window_destroy (setup_frame);
+ create_setup_popup (0, NULL);
+ panel_set_item();
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+}
+
+
+/* SAVE_PARAMS -- Save the user settable options.
+ */
+static
+save_params()
+{
+ s_clip_graphics = clip_graphics;
+ s_graphics_enable = graphics_enable;
+ s_openws_action = openws_action;
+ s_closews_action = closews_action;
+ s_closews_pause = closews_pause;
+ s_canvas_retained = canvas_retained;
+ s_reverse_video = reverse_video;
+ s_color = color;
+ s_win_xsize = win_xsize;
+ s_win_ysize = win_ysize;
+ s_win_xoff = win_xoff;
+ s_win_yoff = win_yoff;
+ s_ignore = ignore;
+ strcpy (s_gin_modeterm, gin_modeterm);
+}
+
+
+/* RESTORE_PARAMS -- Restore the user settable options.
+ */
+static
+restore_params()
+{
+ clip_graphics = s_clip_graphics;
+ graphics_enable = s_graphics_enable;
+ openws_action = s_openws_action;
+ closews_action = s_closews_action;
+ closews_pause = s_closews_pause;
+ canvas_retained = s_canvas_retained;
+ reverse_video = s_reverse_video;
+ color = s_color;
+ win_xsize = s_win_xsize;
+ win_ysize = s_win_ysize;
+ win_xoff = s_win_xoff;
+ win_yoff = s_win_yoff;
+ ignore = s_ignore;
+
+ strcpy (gin_modeterm, s_gin_modeterm);
+ gio_setginmodeterm (gin_modeterm);
+}
+
+
+/* CLEAR_PROC -- Called when the clear button is clicked to transmit the
+ * screen clear sequence to the TTY subwindow.
+ */
+static
+clear_proc()
+{
+ static char clearscreen[] = "\f";
+ ttysw_output (gt_ttysw, clearscreen, strlen(clearscreen));
+}
+
+
+/* GCLEAR_PROC -- Called when the gclear button is clicked to clear the
+ * graphics window.
+ */
+static
+gclear_proc()
+{
+ static char clearscreen[] = "\035\033\f";
+ ev_ptyoutput (clearscreen, strlen(clearscreen));
+}
+
+
+/* PRINT_USAGE -- Print instructions on how to use this window tool.
+ */
+static
+print_usage (toolname)
+char *toolname;
+{
+ char *bstyle = "[-B boldstyle] ";
+
+ fprintf (stderr,
+ "syntax: %s [-C] %s[program [args]]\n", toolname, bstyle);
+ fprintf (stderr,
+ "-C redirect console output to this instance of %s\n", toolname);
+ fprintf (stderr,
+ "-B set boldstyle for this instance of %s\n", toolname);
+ fprintf (stderr,
+ " where boldstyle is a number from 1 to 8\n");
+ fprintf (stderr,
+ "-I input the next argument to the shell run from %s\n", toolname);
+}
diff --git a/unix/sun/gterm.esc b/unix/sun/gterm.esc
new file mode 100644
index 00000000..a557bd73
--- /dev/null
+++ b/unix/sun/gterm.esc
@@ -0,0 +1,46 @@
+/*
+ * GTERM.ESC -- Macros and data defining the escape sequences recognized by
+ * the graphics terminal. Ambiguous cases are resolved in favor of the
+ * entry which occurs first in the table.
+ */
+#define ESC_SETTEXTMODE 1
+#define ESC_ENQUIRE 2
+#define ESC_READCURSOR 3
+#define ESC_CLEARSCREEN 4
+#define ESC_SETCURSOR 5
+#define ESC_SETCHARSIZE0 6
+#define ESC_SETCHARSIZE1 7
+#define ESC_SETCHARSIZE2 8
+#define ESC_SETCHARSIZE3 9
+#define ESC_SETDATALEVEL0 10
+#define ESC_SETDATALEVEL1 11
+#define ESC_SETDATALEVEL2 12
+#define ESC_SETLINESTYLE0 13
+#define ESC_SETLINESTYLE1 14
+#define ESC_SETLINESTYLE2 15
+#define ESC_SETLINESTYLE3 16
+#define ESC_SETLINESTYLE4 17
+#define ESC_SETLINEWIDTH0 18
+#define ESC_SETLINEWIDTH1 19
+#define ESC_SETLINEWIDTH2 20
+
+ESC_SETTEXTMODE, 015, 000, 000, 000, 000, 000, 0,
+ESC_ENQUIRE, ENQ, 000, 000, 000, 000, 000, 0,
+ESC_READCURSOR, SUB, 000, 000, 000, 000, 000, 0,
+ESC_CLEARSCREEN, 014, 000, 000, 000, 000, 000, 0,
+ESC_SETCURSOR, '/', 'f', 000, 000, 000, 000, 0,
+ESC_SETCHARSIZE0, '0', 000, 000, 000, 000, 000, 0,
+ESC_SETCHARSIZE1, '1', 000, 000, 000, 000, 000, 0,
+ESC_SETCHARSIZE2, '2', 000, 000, 000, 000, 000, 0,
+ESC_SETCHARSIZE3, '3', 000, 000, 000, 000, 000, 0,
+ESC_SETDATALEVEL0, '/', '0', 'd', 000, 000, 000, 0,
+ESC_SETDATALEVEL1, '/', '1', 'd', 000, 000, 000, 0,
+ESC_SETDATALEVEL2, '/', '2', 'd', 000, 000, 000, 0,
+ESC_SETLINESTYLE0, '`', 000, 000, 000, 000, 000, 0,
+ESC_SETLINESTYLE1, 'a', 000, 000, 000, 000, 000, 0,
+ESC_SETLINESTYLE2, 'b', 000, 000, 000, 000, 000, 0,
+ESC_SETLINESTYLE3, 'c', 000, 000, 000, 000, 000, 0,
+ESC_SETLINESTYLE4, 'd', 000, 000, 000, 000, 000, 0,
+ESC_SETLINEWIDTH0, '/', '0', 'w', 000, 000, 000, 0,
+ESC_SETLINEWIDTH1, '/', '1', 'w', 000, 000, 000, 0,
+ESC_SETLINEWIDTH2, '/', '2', 'w', 000, 000, 000, 0,
diff --git a/unix/sun/gterm.h b/unix/sun/gterm.h
new file mode 100644
index 00000000..121587ae
--- /dev/null
+++ b/unix/sun/gterm.h
@@ -0,0 +1,18 @@
+/* GTERM.H -- Global definitions for GTERM.
+ */
+#define CURSOR_OFF 3 /* turn cursor off entirely */
+#define CURSOR_ON 4 /* turn it back on */
+
+#define GRAPHICS_OFF 0
+#define GRAPHICS_ON 1
+#define GRAPHICS_DISCARD 2
+
+
+struct fonttab { /* Gterm font descriptor. */
+ short pointsize;
+ char ch_xsize, ch_ysize;
+ short win_xsize, win_ysize;
+ struct pixfont *pixfont;
+ char *path;
+ char *label;
+};
diff --git a/unix/sun/gterm.icon b/unix/sun/gterm.icon
new file mode 100644
index 00000000..05c634a6
--- /dev/null
+++ b/unix/sun/gterm.icon
@@ -0,0 +1,34 @@
+/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
+ */
+ 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF3FF,0xFFFF,0xFFFF,
+ 0xFFFF,0xF0FF,0xFFFF,0xFFFF,0xFFFF,0xF03F,0xFFFF,0xFFFF,
+ 0xFFFF,0xE01F,0xFFFF,0xFFFF,0xFFFF,0xE00F,0xFFFF,0xFFFF,
+ 0xFFFF,0xE007,0xFFFF,0xFFFF,0xFFFF,0xE003,0xFFFF,0xFFFF,
+ 0xFFFF,0xE103,0xFFFF,0xFFFF,0xFFFF,0xE101,0xFFFF,0xFFFF,
+ 0xFFFF,0xC181,0x3FFF,0xFFFF,0xFFFF,0xC181,0x9FFF,0xFFFF,
+ 0xFFFF,0xC1C1,0x8FFF,0xFFFF,0xFFFF,0xC1C1,0x87FF,0xFF8F,
+ 0xFFFF,0xC1C0,0x8307,0xFE1F,0xFFFE,0xC1E0,0x8180,0x001F,
+ 0xFFFC,0xC1E0,0xC0C0,0x00FF,0xFFF8,0x81E0,0xC0C0,0x3FFF,
+ 0xFFE0,0x81E0,0xC060,0x1DFF,0xFE01,0x81E0,0xC030,0x11FF,
+ 0xF001,0x81E0,0xC038,0x03FF,0x8001,0x81C0,0xC018,0x07FF,
+ 0x8001,0x8180,0xC00C,0x03FF,0x8001,0x0100,0xC006,0x03FF,
+ 0xFC01,0x0001,0xC007,0x01FF,0xFF81,0x0001,0xC083,0x03FF,
+ 0xFFE3,0x0007,0xC0C1,0x87FF,0xFFF3,0x0007,0xC060,0xDFFF,
+ 0xFFF3,0x0007,0xC030,0xFFFF,0xFFFB,0x0003,0xC030,0x7FFF,
+ 0xFFFE,0x0003,0xC018,0x3FFF,0xFFFE,0x0001,0xC018,0x1FFF,
+ 0xFFFE,0x0001,0xC00C,0x1FFF,0xFFFE,0x0000,0xC00C,0x0FFF,
+ 0xFFFE,0x0000,0xC00E,0x0FFF,0xFFFE,0x0000,0x4006,0x07FF,
+ 0xFFFC,0x0000,0xC006,0x07FF,0xFFFC,0x0001,0xC007,0x03FF,
+ 0xFFFC,0x0003,0xE007,0x03FF,0xFFFC,0x0187,0xF007,0x01FF,
+ 0xFFF8,0x01EF,0xF807,0x01FF,0xFFF8,0x01FF,0xF807,0x80FF,
+ 0xFFF0,0x01FF,0xFC07,0x80FF,0xFFF0,0x01FF,0xFF07,0x80FF,
+ 0xFFF0,0x01FF,0xFF81,0x807F,0xFFF0,0x03FF,0xFFC0,0x007F,
+ 0xFFF0,0x07FF,0xFFE6,0x007F,0xFFE0,0x07FF,0xFFF7,0x803F,
+ 0xFFE0,0x0FFF,0xFFFF,0x803F,0xFFE0,0x1FFF,0xFFFF,0x801F,
+ 0xFFE0,0x3FFF,0xFFFF,0x800F,0xFFC0,0x3FFF,0xFFFF,0x800F,
+ 0xFFC0,0x7FFF,0xFFFF,0x800F,0xFFC0,0xFFFF,0xFFFF,0xE007,
+ 0xFFC1,0x8000,0x0000,0xF007,0xFF81,0x8040,0x0000,0xF803,
+ 0xFF83,0x9CF1,0x8A34,0xFC03,0xFF87,0xA442,0x4D2A,0xFF03,
+ 0xFF8F,0xA443,0x882A,0xFF81,0xFF9F,0x9C42,0x082A,0xFFE1,
+ 0xFF1F,0x8431,0xC82A,0xFFF1,0xFF3F,0xB800,0x0000,0xFFF9,
+ 0xFF7F,0x8000,0x0000,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
diff --git a/unix/sun/gterm.icon.OLD b/unix/sun/gterm.icon.OLD
new file mode 100644
index 00000000..6a40db4b
--- /dev/null
+++ b/unix/sun/gterm.icon.OLD
@@ -0,0 +1,34 @@
+/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
+ */
+ 0x8888,0x8888,0x8888,0x8888,0x8888,0x889F,0xFC88,0x8888,
+ 0x2222,0x23FF,0xFFE2,0x2222,0x2222,0x27FF,0xFFF2,0x2222,
+ 0x8888,0xBFFF,0xFFFE,0x8888,0x8888,0xFFFF,0xFFFF,0x8888,
+ 0x2223,0xFFFF,0xFFFF,0xE222,0x2223,0xFFF0,0x07FF,0xE222,
+ 0x888F,0xFF00,0x007F,0xF888,0x888F,0xFC00,0x001F,0xF888,
+ 0x223F,0xF000,0x0007,0xFE22,0x223F,0xE000,0x0003,0xFE22,
+ 0x88FF,0x8000,0x0000,0xFF88,0x88FF,0x0000,0x0000,0x7F88,
+ 0x23FE,0x0000,0x0000,0x3FE2,0x23FC,0x0000,0x0000,0x1FE2,
+ 0x8BF8,0x0000,0x0000,0x0FE8,0x8FF8,0x0000,0x0000,0x0FF8,
+ 0x27F0,0x0000,0x0000,0x07F2,0x27E0,0x0000,0x0000,0x03F2,
+ 0x8FE0,0x0000,0x0000,0x03F8,0x8FC0,0x0000,0x0000,0x01F8,
+ 0x3FC0,0x0000,0x0000,0x01FE,0x3F80,0x0000,0x0000,0x00FE,
+ 0x9F80,0x0000,0x0000,0x00FC,0x9F83,0xE3F0,0x1C3F,0xC0FC,
+ 0x3F81,0xC1D8,0x1C1C,0xC0FE,0x3F01,0xC1DC,0x1C1C,0x007E,
+ 0xBF01,0xC1DC,0x1C1C,0x007E,0xBF01,0xC1D8,0x3E1F,0x007E,
+ 0x3F01,0xC1F0,0x3E1F,0x007E,0x3F01,0xC1F0,0x3E1F,0x007E,
+ 0xBF01,0xC1F8,0x3E1C,0x007E,0xBF01,0xC1F8,0x671C,0x007E,
+ 0x3F01,0xC1DE,0x671C,0x007E,0x3F03,0xE3EE,0xFFBE,0x007E,
+ 0xBF80,0x0000,0x0000,0x00FE,0x9F80,0x0000,0x0000,0x00FC,
+ 0x3F80,0x0000,0x0000,0x00FE,0x3F80,0x0000,0x0000,0x00FE,
+ 0x9FC0,0x0000,0x0000,0x01FC,0x8FC0,0x0000,0x0000,0x01F8,
+ 0x2FE0,0x0000,0x0000,0x03FA,0x27E0,0x0000,0x0000,0x03F2,
+ 0x8FF0,0x0000,0x0000,0x07F8,0x8FF8,0x0000,0x0000,0x0FF8,
+ 0x23F8,0x0000,0x0000,0x0FE2,0x23FC,0x0000,0x0000,0x1FE2,
+ 0x89FE,0x0000,0x0000,0x3FC8,0x88FF,0x0000,0x0000,0x7F88,
+ 0x227F,0x8000,0x0000,0xFF22,0x223F,0xE000,0x0003,0xFE22,
+ 0x889F,0xF000,0x0007,0xFC88,0x888F,0xFC00,0x001F,0xF888,
+ 0x2227,0xFF00,0x007F,0xF222,0x2223,0xFFF0,0x07FF,0xE222,
+ 0x8889,0xFFFF,0xFFFF,0xC888,0x8888,0xFFFF,0xFFFF,0x8888,
+ 0x2222,0x3FFF,0xFFFE,0x2222,0x2222,0x27FF,0xFFF2,0x2222,
+ 0x8888,0x89FF,0xFFC8,0x8888,0x8888,0x889F,0xFC88,0x8888,
+ 0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222
diff --git a/unix/sun/gterm.man b/unix/sun/gterm.man
new file mode 100644
index 00000000..fd09b5f4
--- /dev/null
+++ b/unix/sun/gterm.man
@@ -0,0 +1,784 @@
+.\" @(#)gterm.1 1.1 28-Jul-87 DCT
+.TH GTERM 1 "31 December 1987"
+.SH NAME
+gterm \- virtual graphics terminal for the SunView environment
+.SH SYNOPSIS
+.B gterm
+[
+.B \-C
+]
+[
+.B \-T
+]
+[
+.B \-B
+\fIboldstyle\fR
+]
+[
+.B \-I
+\fIcommand\fR
+]
+.if n .ti +0.5i
+[
+.B \-ignore
+]
+[
+\fIttyargs\fR
+]
+.ti +.5i
+[
+.B \-G
+[
+.B \-Gopen
+\fR(\fP\fBnoaction\fR | \fBshow\fR | \fBexpose\fR\fR)\fP
+]
+.if n .ti +1.0i
+[
+.B \-Gclose
+\fR(\fP\fBnoaction\fR | \fBblank\fR | \fBhide\fR\fR)\fP
+]
+.ne 4
+.ti +1.0i
+[
+.B \-\fR[\fPno\fR]\fPpause
+]
+[
+.B \-\fR[\fPno\fR]\fPretain
+]
+[
+.B \-\fR[\fPno\fR]\fPclip
+]
+.if n .ti +1.0i
+[
+.B \-\fR[\fPno\fR]\fPreverse
+]
+[
+.B \-color
+|
+.B \-mono
+]
+.ti +1.0i
+[
+.B \-ginterm
+\fR[\fIchar \fR[\fIchar\fR]]
+]
+[
+\fIgioargs\fR
+]
+]
+.ti +.5i
+[
+\fIprogram\fR
+[
+\fIargs\fR
+]
+]
+.SH GETTING STARTED
+\fIgterm\fR is a virtual graphics terminal implemented as a set of windows
+running within the SunView environment. The graphics terminal consists of
+two primary windows, the main \fBtext window\fR and a \fBgraphics subwindow\fR.
+The text window is a standard SunView TTY subwindow, identical to that used
+in \fIshelltool\fR. The graphics window is a Tektronix 4012 compatible
+graphics window. The two windows may be moved and sized independently.
+Terminal i/o may be directed to either window, but to only
+one window at a time. When i/o is directed to the text window the terminal
+is said to be in \fBtext mode\fR, and when i/o is directed to the graphics
+window the terminal is said to be in \fBgraphics mode\fR. Mode switching
+may be performed either manually or under program control.
+\fIgterm\fR is upwards compatible with \fIshelltool\fR.
+.SH OPTIONS
+.TP
+.B \-C
+Redirect system console output to this instance of \fIgterm\fR.
+.TP
+.B \-T
+Run \fIgterm\fR with the graphics plane disabled. Any graphics commands
+embedded in the input data stream will appear as printable characters in the
+text window. This option is useful when using \fIgterm\fR for a nongraphics
+session over a noisy line, or when debugging a graphics program and switching
+back and forth between text and graphics mode would interfere with the
+operation of the debugger.
+.TP
+\fB\-B\fP \fIboldstyle\fR
+Sets the \fIboldstyle\fR
+(rendering of boldface type in the text window)
+for this instance of \fIgterm\fR.
+The choices are the same as for any tool based
+on a SunView tty subwindow: see \fIshelltool\fR(1) for a description of the
+\fIboldstyle\fR options.
+.TP
+\fB\-I\fP \fIcommand\fR
+Input the given command to the program or shell run in the \fIgterm\fR window,
+as if it had been typed into the window. Spaces in the command must be
+escaped, or the command string must be quoted.
+.TP
+.B \-ignore
+Ignore the suspend control characters (SIGTSTP), \fIif\fR the process being
+run in the \fIgterm\fR window is not the default SHELL process.
+If this option is in effect, typing one of the \fIstty\fR suspend-process
+control characters \fBsusp\fR or \fBdsusp\fR while the terminal is not in raw
+mode will cause the character to be echoed in the text window, but to be
+otherwise ignored. This may be desirable if the process being run in the
+\fIgterm\fR window is not capable of restarting itself after it has been
+suspended (see also the \fBcontinue\fR frame menu option).
+.TP
+\fIttyargs\fR
+\fIgterm\fR also takes generic tool arguments; see \fIsuntools\fR(1) for a
+list of these arguments. The \fIttyargs\fR generic tool arguments affect
+only the text window.
+.TP
+.B \-G
+Generic tool arguments preceding this optional placeholder switch affect only
+the text window; those following the switch affect only the graphics window.
+.TP
+\fB-Gopen \fR(\fPnoaction \fR|\fP show \fR|\fP expose\fR)\fP
+Specifies the action to be taken when graphics mode is entered, i.e., when
+terminal i/o is redirected from the text window to the graphics window.
+\fBnoaction\fR means do nothing which visibly affects the screen.
+\fBshow\fR means display the graphics frame if it is not already visible on
+the screen, i.e., because the graphics window has been closed.
+\fBexpose\fR means move the graphics window, if already displayed, to the
+top of the stack of open windows.
+.TP
+\fB-Gclose \fR(\fPnoaction \fR|\fP blank \fR|\fP hide\fR)\fP
+Specifies the action to be taken when graphics mode is exited, restoring
+terminal i/o to the text window.
+\fBnoaction\fR means do nothing which visibly affects the screen.
+\fBblank\fR means close the graphics subwindow, causing the window to
+disappear from the screen (the closed window can be redisplayed at any time
+provided the contents are \fIretained\fR).
+\fBhide\fR means move the graphics window to the bottom of the stack of
+open windows.
+.TP
+\fB\-\fR[\fPno\fR]\fPpause
+Pause before closing the graphics window and returning to text mode,
+after opening the graphics frame to display a plot noninteractively
+(no cursor reads). If \fB\-nopause\fR is specified and the \fB\-Gclose\fR
+option is \fBblank\fR, a newly plotted graph will disappear immediately after
+the plotting operation is completed, assuming no blocking operation such as
+a cursor read occurs while in graphics mode. If \fB\-pause\fR is specified
+\fIgterm\fR will detect this condition and pause for a keystroke to be typed
+before exiting graphics mode.
+.TP
+\fB\-\fR[\fPno\fR]\fPretain
+Specifies whether or not the contents of the graphics frame are to be retained.
+If \fB\-noretain\fR is specified plotting will be somewhat faster, but any
+window event which affects the region of the workstation screen occupied by
+the graphics frame will cause the plot to be lost.
+.TP
+\fB\-\fR[\fPno\fR]\fPclip
+Specifies whether or not clipping of graphics vectors is to be performed.
+If \fB\-noclip\fR is specified plotting will be somewhat faster, but vectors
+may be drawn outside the graphics window if the screen changes while a graph
+is being drawn.
+.TP
+\fB\-\fR[\fPno\fR]\fPreverse
+Specifies whether the graphics frame is to be displayed in normal or reverse
+video. What constitutes normal video depends upon whether color or monochrome
+is specified, and upon what has been specified for the foreground and
+background colors. Reverse video swaps the foreground and background colors
+within the graphics window.
+.TP
+.B \-color
+Display the graphics frame in two colors, if \fIgterm\fR is run on a color
+workstation.
+.TP
+.B \-mono
+Display the graphics frame in monochrome, using the foreground and background
+colors specified when \fIsuntools\fR(1) was started.
+.TP
+.B \-ginterm \fR[\fIchar \fR[\fIchar\fR]]
+Set the GIN terminator character or characters (used to delimit the value
+returned for a cursor read) to the indicated octal values \fIchar\fR.
+If a \fIchar\fR value is omitted the corresponding GIN mode terminator will
+be omitted when a cursor value is returned. For example, "\-ginterm" causes
+both GIN delimiter characters to be omitted, and "\-ginterm 015" causes the
+single GIN terminator character CR (carriage return) to be transmitted.
+.TP
+\fIgioargs\fR
+\fIgterm\fR also takes generic tool arguments; see \fIsuntools\fR(1) for a
+list of these arguments. The \fIgioargs\fR generic tool arguments affect
+only the graphics subwindow, and are recognized only after the \fB\-G\fR
+placeholder argument.
+.TP
+[\fIprogram\fP [\fIargs\fP]]
+If a \fIprogram\fR argument is present, the named program is run in the
+\fIgterm\fR window without spawning an intermediate shell.
+If no \fIprogram\fR is specified,
+\fIgterm\fR runs the shell specified by your \fLSHELL\fR environment
+variable. If this environment variable is not defined, \fIgterm\fR runs
+\fB/bin/sh\fR.
+.LP
+Minimum match abbreviations are permitted for all \fIgterm\fR arguments except
+the generic tool arguments, which have two character aliases. Use of the full
+names is however recommended, as the number of characters required for minimum
+matching a specific argument may change in a future release of \fIgterm\fR
+when new command line arguments are added.
+.if t .sp 0.08i
+.SH DESCRIPTION
+.SS Virtual Graphics Terminal
+.LP
+\fIgterm\fR is a virtual graphics terminal implemented as a set of windows
+running within the SunView environment. The terminal consists of two primary
+windows, a \fBtext window\fR and a \fBgraphics window\fR, and several special
+purpose subwindows, the most important of which is the \fBsetup panel\fR,
+used to dynamically change \fIgterm\fR options at runtime, or to reset the
+terminal to a known state. The virtual terminal is interfaced to applications
+programs via the UNIX pseudoterminal or \fIpty\fR(4) interface, providing an
+applications interface identical to that provided for ordinary terminals,
+allowing applications running either on the local machine or on a remote
+node communicating via a network interface (\fItelnet\fR, \fIrlogin\fR,
+modem etc.) to communicate transparently with the \fIgterm\fR virtual terminal.
+.if t .sp 0.08i
+.SS Text Window
+.LP
+The text window is a standard SunView TTY subwindow, as used in
+\fIshelltool\fR, \fIgfxtool\fR, and so on. Documentation for tty subwindows,
+including both user documentation and a description of the special escape
+sequences recognized by tty subwindows, is given in \fIshelltool\fR(1).
+In particular, note that a \fB.ttyswrc\fR file may be placed in one's login
+directory to set tty subwindow parameters, and to map function keys to control
+sequences to be sent either to the tty subwindow or to the program running in
+the tty subwindow. The \fBselection service\fR can be useful for passing
+text to programs via the mouse instead of the keyboard, and numerous terminal
+\fBescape sequences\fR are defined for resizing the text window, setting the
+frame label, and so on.
+.if t .sp 0.08i
+.SS Graphics Window
+The graphics window emulates a Tektronix 4012 terminal with minor differences,
+e.g., the screen size is 35x80 rather than 35x75, and some significant
+extensions, e.g., graphics and text can be selectively erased, and support
+is provided for a \fBstatus line\fR at the bottom of the screen, in which text
+can be dynamically read and written without affecting the contents of the
+graphics plane. Although nonstandard, these features are very useful when
+designing interactive user interfaces, and they can be emulated on most
+modern graphics terminals (hence programs which use these features need not
+be device dependent).
+.LP
+The position and size of the graphics window may be set at startup time via
+the generic tool arguments \fIgioargs\fR. A number of preprogrammed sizes
+may also be selected via the setup panel at runtime, or the mouse may be used
+to directly resize the window to produce a window of any size and aspect ratio.
+The standard graphics window sizes range from very small to the full screen
+and all share the same standard landscape mode aspect ratio; users should note
+that manually adjusting the window size usually results in a window with a
+nonstandard aspect ratio, which may cause graphics programs which assume the
+standard aspect ratio to misbehave, producing oddly shaped objects, or poorly
+centered text strings.
+.LP
+\fIgterm\fR will automatically detect any changes in the size of the graphics
+window, adjusting the transformation from 4012 coordinates (780x1024) to
+screen coordinates so that subsequent graphics and text will be drawn at the
+correct relative position within the window. The best text font for the new
+window size is also selected; if the window is of an arbitrary size it will
+not in general be possible to select a font which provides exactly 35x80
+characters on the screen without overlap, since there are only a limited number
+of fixed size fonts to choose from. If the window is especially wide and
+short text lines may overlap vertically. After resizing the graphics window,
+the previously displayed graph \fImust\fR be redrawn under program control to
+adjust the graph to fit the new window.
+.LP
+By default, the graphics window overlaps the text window, with eight or ten
+characters of text visible to the left of the graphics window. If the graphics
+window is left in its default position and the text window is repositioned on
+the workstation screen, the graphics window will "track" the text window,
+i.e., retain its position relative to the text window (this is desirable when
+there are multiple instances of \fIgterm\fR in use at one time to avoid losing
+track of which graphics frame goes with which text window). If the graphics
+window is manually repositioned, however, then thereafter the positions of
+the two windows are completely independent.
+.LP
+Although the graphics window is normally used only for graphics (plotting),
+one should not forget that the graphics window emulates an (80 column wide)
+Tektronix 4012 terminal, and hence may be used as a terminal for ordinary
+text i/o, as well as for graphics. The TTY subwindow will normally be
+superior for terminal graphics, but the extra page of text and the larger,
+brighter font typically used in the graphics window may occasionally be useful
+for some applications. Text i/o may easily be switched back and forth between
+the text and graphics windows via the function keys described below,
+transparently to most applications software.
+.if t .sp 0.08i
+.SS Active Window
+.LP
+At any one time, terminal output is always directed to either the text window
+or the graphics window, but never to both at the same time. When output is
+directed to the text window the terminal is said to be in \fBtext mode\fR,
+and when output is directed to the graphics window the terminal is said to
+be in \fBgraphics mode\fR, although either text or graphics may be written
+into the graphics plane. Mode switching is normally in response to control
+codes embedded in the input data stream from the applications program being
+run, but keyboard function keys may be used to manually perform mode switching
+if desired.
+.LP
+Keyboard \fIinput\fR may be directed to either window at any time, regardless
+of which window is currently active, i.e., text may be typed into one window
+but echoed in the other window. This can be confusing if the window in which
+text is echoed is not currently displayed; one types and nothing seems to be
+happening, but in fact commands are being input and executed normally.
+For example, if the \fB\-Gclose\fR option is set to \fBnoaction\fR and the
+graphics window is adjusted to fill the full screen, then when graphics mode
+is exited terminal output will be directed to the text window, but the text
+window will be covered by the graphics window, and subsequent commands and
+textual output will not be visible without manually redisplaying the text
+window (or undisplaying the graphics window) with a function key.
+.if t .sp 0.08i
+.SS The Frame Menu
+.LP
+\fIgterm\fR uses a special frame menu which may be called up by the mouse
+at any time, by placing the cursor on the border of the \fItext window\fR and
+holding down the right mouse button.
+The items in the \fIgterm\fR frame menu are as follows:
+.RS
+.IP "\fBFrame\fP" 15
+Access the standard SunView frame menu.
+.IP "\fBSetup\fP"
+Display the setup panel.
+.IP "\fBContinue\fP"
+Send the SIGCONT signal to the process group attached to the \fIgterm\fR
+window, e.g., after accidentally suspending a process which cannot otherwise
+restart itself. See also the \fB\-ignore\fR command line option.
+.IP "\fBShow graph\fP"
+Display the graphics window.
+.IP "\fBTextcopy\fP"
+Make a hardcopy of the text window.
+.IP "\fBGraphcopy\fP"
+Make a hardcopy of the graphics window.
+(IRAF users should use the cursor mode \fIsnap\fR function instead).
+.IP "\fBScreencopy\fP"
+Make a hardcopy of the full screen.
+.RE
+.LP
+The textcopy, graphcopy, and screencopy selections are all entry points to the
+general screen capture utility, discussed in the next section.
+.if t .sp 0.08i
+.SS Hardcopy Output
+.LP
+The hardcopy functions produce a "what you see is what you get" bitmap of the
+rectangular region of the screen occupied by the indicated object of interest.
+If the region of interest is partially covered by another window, then the
+hardcopy will be a picture of a partially covered window.
+If the window is displayed in reverse video, the hardcopy will also be
+rendered in reverse video.
+.LP
+The screen capture software reads out the full memory of the workstation in
+the region of interest, and in the case of a color workstation, processes the
+screen pixels through the colortable to produce an image corresponding to what
+appears on the screen. No full color output options
+are currently provided, hence the average of the red, green, and blue color
+values is next computed. If a bitmap output image is desired a simple
+thresholding algorithm is used to produce the final bitmap image,
+otherwise a greyscale image is produced. If rasterfile output
+is being generated, the raw pixel values and RGB color table entries are saved
+directly in the rasterfile, rather than applying the tables in software to
+produce a monochrome or bitmap image.
+.LP
+Two output options are currently provided, i.e., \fBPostscript\fR output
+suitable for output directly to a laser writer to produce the final graphics
+hardcopy, or \fBSun rasterfile\fR output. The default action is to output a
+Postscript program to the device "lw", e.g., the Apple Laserwriter
+(any 300 dpi Postscript device should do).
+These defaults may be changed by defining the following environment variables:
+.IP R_RASTERFILE
+If this variable is defined a Sun rasterfile will be generated, otherwise a
+Postscript plotfile is generated. The string value of the variable is a
+\fIprintf\fR style format string to be used to generate the filename of
+the rasterfile. If multiple rasterfiles are to be generated, the format
+string may contain a decimal integer field (e.g., "\fLframe.%d\fR") to be
+replaced by the \fIfile number\fR of the current rasterfile. The first file
+generated will be number zero, with the file number being incremented once
+for every rasterfile produced. If Postscript plotfile output is desired,
+the plotfile will be a uniquely named temporary file in \fB/tmp\fR.
+.IP R_DISPOSE
+The string value of this variable is a \fIprintf\fR style format string with
+one string valued subfield to be replaced by the plotfile or rasterfile name,
+to be used to generate the command used to dispose of the output file.
+If this variable is not defined and the output file is a Postscript plotfile,
+the default format string \fL"lpr -Plw -r -s %s"\fR will be used.
+If the variable is not defined and the output file is a rasterfile,
+no action is taken. It is the responsibility of the dispose command to
+delete the output file.
+.LP
+It should only take several seconds to capture the screen and produce the
+output rasterfile or queue the Postscript job to the printer. The screen
+is flashed to indicated when the operation has completed. The Postscript
+processing time may take up to several minutes (of laserwriter time) in the
+worst case, i.e., a hardcopy of the full workstation screen.
+.if t .sp 0.08i
+.SS The Setup Panel
+.LP
+The setup panel is used to dynamically change terminal options while the
+terminal is in use. In general, nearly any terminal option which can be
+set on the command line when \fBgterm\fR is started can also be set via the
+setup panel, and vice versa. The setup panel can also be used to reset
+the terminal to the startup or "power on" state.
+The setup panel may be called up at any time via the frame menu; it is normally
+closed after the desired setup action has been performed. By default the setup
+panel is located within the text window, but it may be moved anywhere else on
+the workstation screen if desired.
+.LP
+There are two types of items in the setup panel: multiple choice options and
+"push buttons". To see what the choices are in a multiple choice option,
+position the mouse to the area where the current choice is displayed and
+depress the right mouse button. At this point a selection may be made by
+moving the mouse to the desired selection and releasing the mouse button.
+Alternatively, the left mouse button may be used to cycle through the choices.
+To perform the action indicated on a push button, place the mouse cursor on
+the button and press the left mouse button.
+.LP
+The multiple choice options in the setup panel are the following:
+.IP "\fBGraphics plane"
+These options determine what the terminal does when graphics data and
+control instructions are encountered in the input stream.
+\fBDisable\fR means disable the graphics plane, causing the terminal to
+output graphics control codes and data as printable characters
+in the text window.
+\fBEnable\fR means enable the graphics plane for normal mixed text and
+graphics operation.
+\fBDiscard Graphics Data\fR means discard all graphics data, effectively
+disabling the graphics plane.
+.IP "\fBOpen workstation action\fR"
+These options determine the action taken by the terminal when graphics mode
+is entered. Some visible action is generally desirable to render the graphics
+window fully visible, and to indicate that a mode switch has occurred.
+\fBNo action\fR means do nothing which visibly affects the workstation.
+\fBShow graphics\fR means open the graphics window, e.g., if the graphics
+window is to be closed (not displayed) when the terminal is in text mode.
+\fBExpose graphics\fR means move the graphics window to the top of the stack
+of open windows, displaying any portions of the graphics window which may have
+been covered by other windows (such as the text window).
+.IP "\\fBClose workstation action\\fR"
+These options determine the action taken by the terminal when graphics mode
+is exited, returning the terminal to text mode.
+\fBNo action\fR means do nothing.
+\fBBlank graphics\fR means close the graphics window, i.e., remove the window
+from the screen.
+\fBHide graphics\fR means move the graphics window to the bottom of the stack
+of open windows, allowing any overlapping windows to cover the graphics window.
+.IP "\\fBPause on close workstation\\fR"
+This boolean option determines whether or not the terminal displays the
+\fBpause panel\fR, waiting for a key to be typed, before exiting graphics
+mode following a noninteractive graphics session (no cursor input).
+.IP "\\fBRetain graphics frame\\fR"
+This boolean option determines whether or not the contents of the graphics
+window are \fIretained\fR. Graphics drawing will be somewhat faster if the
+graphics plane is not retained, but almost any event which affects the region
+of the screen occupied by the graphics window will cause the contents
+of an unretained window to be lost.
+.IP "\\fBClip graphics\\fR"
+This boolean option determines whether or not graphics vectors are clipped
+to the boundaries of the visible portions of the graphics window. Graphics
+drawing will be somewhat faster if clipping is disabled, but
+vectors may be drawn in nearby, unrelated windows,
+especially if the graphics window is partially covered by other windows.
+.IP "\\fBGraphics screen type\\fR"
+This option determines whether the graphics plane is to be displayed in color
+or monochrome on a color workstation. If \fBmono\fR is selected the foreground
+and background colors specified when \fIsuntools\fR was started are used.
+If \fBcolor\fR is specified graphics will be rendered in color, with the
+colors used being specified by the \fB\-Wb\fR and \fB\-Wf\fR generic tool
+arguments in \fIgioargs\fR. On a monochrome workstation the only option
+displayed will be \fBmono only\fR, indicating that color is not available.
+.IP "\\fBGraphics video\\fR"
+This option specifies whether graphics are to be rendered in \fBnormal\fR or
+\fBreverse\fR video. Specifying reverse video causes the foreground and
+background colors of the graphics window to be reversed.
+This option may not work on some monochrome workstations.
+.IP "\fBGraphics font and screen sizes\fR"
+This option is used to select at runtime the size of graphics window to be used.
+The graphics window may be resized at any time, including while graphics is
+being drawn or during a cursor read, but any displayed graphics should always
+be redrawn following a window resize to ensure that the graph reflects the new
+coordinate system.
+The graphics window configurations currently available are listed below
+in the form ``\fIpointsize\fR:[\fIwidth\fRx\fIheight\fR]'',
+where \fIwidth\fR and \fIheight\fR are in pixels. The size of the full screen
+is workstation dependent, the most common size currently being 1152x900.
+The exact set of fonts and screen sizes may change in the future as new
+fonts become available and workstations increase in resolution.
+.if t .sp 0.05i
+.ti +0.3i
+10:[560x420] 12:[640x490] 14:[720x560] 16:[800x630] 18:[880x665] 24:fullscreen
+.if t .sp 0.05i
+All choices represent 35x80 windows with the standard landscape mode aspect
+ratio. The size of the graphics window is the size in pixels of a character
+of the fixed width font used, scaled by 35 vertically and by 80 horizontally.
+Arbitrary sized windows may also be created by manually sizing the window
+with the mouse, but this is bound to result in windows with a nonstandard
+number of lines or columns of text, or a nonstandard aspect ratio.
+.IP "\fBGIN mode terminators\fR"
+Set the GIN (graphics or cursor mode input) terminator characters to the
+indicated octal values. When a key is hit to terminate a cursor read, the
+terminal transmits a 5 character cursor value sequence to the applications
+program, following by one or two GIN mode terminator characters.
+The required GIN mode terminator(s) will in general depend upon the
+applications program being run. Some programs require no terminators,
+others require a single CR (octal 015), and so on.
+The default GIN mode terminator is a single CR.
+.if t .sp 0.05i
+To enter a new value, select the value box with the left mouse button,
+rubout the old value, and type in the new value as a string, with zero, one,
+or two octal values denoting the desired terminator characters, then hit
+return to establish the new value. Entering a blank string disables both
+terminators.
+.LP
+The following "push buttons" are also provided in the setup panel:
+.RS
+.IP \fBReset\fR 15
+Reset the terminal to the "power on" state, preserving the values of any
+options set on the command line, but cancelling any options selected via
+the setup panel. A \fBsetup reset\fR is indicated if the terminal does not
+seem to be behaving correctly. Resetting the internal state of the terminal
+has no effect on the operation of any applications program being run from
+the terminal.
+.IP \fBClear\fR
+Clear the text window (the F9 function key performs the same function).
+.IP \fBGclear\fR
+Clear the graphics window, leaving the terminal in graphics mode
+(the F8 function key performs the same function).
+.IP "\fBShow graphics\fR"
+Open (display) or close (undisplay) the graphics frame. The contents of the
+graphics frame are not affected.
+.IP \fBQuit\fR
+Close the setup panel.
+.RE
+.LP
+Closing and opening either the text or graphics frame has no effect on the
+state of the terminal or on the applications program running within it,
+even while a cursor read is in progress.
+.if t .sp 0.08i
+.SS Function Keys
+.LP
+The following function keys have special significance to \fIgterm\fR:
+.RS
+.IP F8 15
+In text mode, causes a switch to graphics mode.
+When already in graphics mode, causes the graphics frame to be cleared.
+.IP F9
+In graphics mode, causes a switch to text mode.
+When already in text mode, causes the text frame to be cleared.
+.RE
+.LP
+To momentarily view the graphics frame while in text mode, one can type F8
+followed by F9, without affecting the contents of either window.
+Commands may be entered in either window, hence to direct the output of
+a command to the graphics window, one could hit F8, execute the command,
+and then hit F9 to return to the text window. The standard SunView L7 key,
+used to close a window, is also detected by \fIgterm\fR,
+hence closing the graphics
+window with L7 while in graphics mode will automatically cause the terminal
+to revert to text mode.
+.LP
+Manual control of the terminal mode is sometimes necessary when running
+naive graphics programs in a \fIgterm\fR window.
+When running a graphics program
+which uses only standard 4012 instructions, it may be necessary to manually
+put the terminal into graphics mode with the F8 function key before running the
+program, or part of the program output may be "lost" (directed to the text
+window and discarded). Similarly, naive programs will not return the terminal
+to text mode after generating a plot, hence it will be necessary for the
+user to hit the F9 key to return to text mode.
+.LP
+Additional function keys may be defined in the user \fB~/.ttyswrc\fR file.
+For example, the function key definitions
+.if t .sp 0.05i
+.if n .sp
+.RS
+.nf
+mapo R1 ^[[8;24;80t
+mapo R2 ^[[8;34;80t
+mapo R3 ^[[8;40;80t
+.fi
+.RE
+.if t .sp 0.05i
+.if n .sp
+will program the R1, R2, and R3 function keys to set the size of the text
+window to 24, 34, or 40 lines by 80 columns when the corresponding function
+key is typed. These definitions are handy for rapidly resizing the text
+window to one of the "standard" terminal sizes; this is especially useful
+when executing programs remotely over the network, as most such programs
+assume some standard size terminal screen.
+.if t .sp 0.08i
+.SS Mouse Buttons
+.LP
+The significance of the mouse buttons depends upon which window the mouse is
+in, and upon whether or not the terminal is in GIN mode, i.e., in the process
+of reading the graphics cursor. When the terminal is in text mode and the
+mouse is in the text window, the mouse buttons are used only for the
+\fBselection service\fR, as described in \fIshelltool\fR(1).
+The functions of the mouse buttons while the mouse is in the graphics window
+are outlined below.
+.RS
+.IP "Left button" 15
+Ignored except in GIN mode, when it may be aliased to a keyboard key and used
+to terminate a cursor read.
+.IP "Middle button"
+Ignored except in GIN mode, when it may be aliased to a keyboard key and used
+to terminate a cursor read.
+.IP "Right button"
+In GIN mode, may be aliased to a keyboard key and used to terminate a cursor
+read. When not in GIN mode, causes the cursor crosshairs to be displayed
+while the button is depressed.
+.RE
+.LP
+The ability to \fBalias\fR mouse buttons to keyboard keys is a very important
+one as it allows arbitrary graphics programs which are driven via an
+interactive graphics cursor loop to be controlled completely from the mouse,
+rather than having to position the mouse and then hit a key on the keyboard
+to terminate each cursor read. For example, to alias \fIkey\fR to the left
+mouse button, one would depress the control key and tap the left mouse button
+twice, immediately after hitting \fIkey\fR to terminate a normal cursor read.
+Thereafter, either \fIkey\fR or the left mouse button may be used equivalently
+to terminate a cursor read. The alias remains in effect until the terminal is
+\fIreset\fR or the alias is reassigned to a different key.
+.if t .sp 0.08i
+.SS The Terminal Emulator
+.LP
+The normal function of the terminal is to simultaneously listen for input
+(program output) on the pseudoterminal file descriptor, while servicing
+asynchronous keyboard and mouse events generated by the user.
+The input data stream from the applications program consists of a mixture
+of text and graphics data transmitted as an ASCII byte stream with no record
+boundaries. Null bytes in the input data stream are ignored, and no programmed
+delays are needed for proper terminal operation. As input data is received
+asynchronously it is copied into a circular buffer and a synchronous event is
+queued to call a routine which subsequently processes the input characters
+onto the screen. If input data arrives faster than it can be processed onto
+the screen \fB<ctrl/s>\fR is transmitted to the \fIpty\fR terminal driver,
+followed by \fB<ctrl/q>\fR once the circular buffer empties.
+Characters typed by the user are transmitted directly to the terminal driver,
+which in normal operation will echo the characters back to the terminal as
+ordinary data.
+.LP
+The initial state of the terminal is text mode. Transition to graphics mode
+occurs when the GS character is encountered in the input data stream.
+Transition back to text mode occurs when the CAN character is encountered in
+the input stream. While text mode is in effect all input is passed on to the
+TTY subwindow; while graphics mode is in effect all input is passed on to the
+graphics subwindow. The behavior of the ANSI standard TTY subwindow is
+documented elsewhere (e.g., \fIshelltool\fR(1), \fIcons\fR(4s), and Chapter 10
+of the \fISunView Programmer's Guide\fR) hence will not be discussed further
+here. Likewise, the basic Tektronix 4012 protocol is a well known standard
+and need not be documented in detail here.
+.LP
+The control codes and escape sequences recognized by the \fIgterm\fR graphics
+window are summarized below. Sequences marked with a \(**\(** at the right
+are nonstandard extensions, although all except the status line feature are
+fairly common extensions.
+.if t .sp 0.05i
+.ta +0.5i +1.5i +3.5i
+.nf
+ GS (035) \fBopen workstation\fR, start normal vector drawing sequence
+ CAN (030) \fBclose workstation\fR **
+ FS (034) start pointmode vector
+ US (037) set alpha mode
+ CR (015) set alpha mode and execute carriage return
+ BEL (007) ring bell and/or flash screen
+.if t .sp 0.05i
+ ESC CR set status line mode (ESC = 033) **
+ ESC ENQ inquire graphics state and cursor position
+ ESC SUB initiate a cursor read (SUB = 032)
+ ESC FF clear screen, home alpha cursor (FF = 014)
+ ESC / f set cursor position to current drawing coordinates **
+ ESC 0 set character size 0
+ ESC 1 set character size 1 [not implemented]
+ ESC 2 set character size 2 [not implemented]
+ ESC 3 set character size 3 [not implemented]
+ ESC / 0 d set data level 0 (clear bits) **
+ ESC / 1 d set data level 1 (set bits) **
+ ESC / 2 d set data level 2 (toggle bits) **
+ ESC ` set line style 0 (solid)
+ ESC a set line style 1 (dashed)
+ ESC b set line style 2 (dotted)
+ ESC c set line style 3 (dashdot)
+ ESC d set line style 4 (dash3dot)
+ ESC / 0 w set line width 0 (1 pixel) **
+ ESC / 1 w set line width 1 (2 pixels) **
+ ESC / 2 w set line width 2 (3 pixels) **
+.fi
+.if t .sp 0.05i
+.LP
+Both text and vectors may be erased by setting the data level to 0 and
+redrawing the objects to be erased. Erasing points which are common to more
+than one object will cause gaps in other objects sharing the erased point.
+.LP
+Setting \fBstatus line\fR mode causes the region of the graphics frame
+occupied by the status line to be saved in a memory pixrect, after which the
+status line is cleared and the status line alpha cursor positioned to the
+start of the line (the status line is a single 80 character line of text at
+the bottom of the graphics window). While output is directed to the status
+line, data characters are output in the status line as for a terminal.
+BS and DEL behave as expected, allowing characters to be erased.
+Lines longer than 80 characters are truncated at the right margin.
+LF (newline) is treated the same as CR, causing the entire line to be erased,
+and if multiple lines of text are rapidly written to the status line
+they will scroll as on a one-line terminal. Status line mode is terminated
+by any control character in the input data stream, e.g., GS, FS, CAN, ESC,
+and so on. Note that terminating status line mode does not in itself erase
+the status line, restoring the saved region of the graphics frame to the
+screen; this is done by transmitting newline or CR to the terminal.
+.if t .sp 0.08i
+.SH SEE ALSO
+suntools(1), shelltool(1), tektool(1), cmdtool(1), pty(4), cons(4s)
+.br
+\fISunView Programmer's Guide\fR, Chapter 10 \- TTY Subwindows
+.br
+\fIWindows and Window-Based Tools: Beginner's Guide\fR
+.if t .sp 0.08i
+.SH FILES
+.LP
+.nf
+~/.ttyswrc
+/usr/bin/suntools
+/usr/lib/rootmenu
+/usr/lib/fonts/fixedwidthfonts/*
+$iraf/local/sun/gterm.c
+.fi
+.if t .sp 0.08i
+.SH BUGS
+.IP (1)
+\fIgterm\fR is a complex program operating in an extremely dynamic environment.
+The program has been thoroughly tested and is quite reliable, but it is
+nonetheless possible for the program to get into peculiar states where it
+does not behave as expected. Should this happen, a \fIsetup reset\fR should
+restore the terminal to a known state.
+.IP (2)
+If more than 256 characters are input to a terminal emulator subwindow without
+an intervening newline, the terminal emulator may hang (to demonstrate this,
+hold any key down until the autorepeat generates sufficient characters).
+If this occurs, display the tty subwindow menu and select the \fBflush input\fR
+item to correct the problem.
+.IP (3)
+When using a terminal emulator to execute a program on a remote node via a
+network interface (rlogin, telnet, etc.), and the remote program continuously
+outputs a large amount of data, the terminal will occasionally hang up for
+several seconds, after which normal output will resume. Typing any character
+will cause output to resume immediately, but the character will later be
+delivered to the remote program as normal input hence should be selected with
+care (\fB<ctrl/q>\fR is always harmless). The origin of this bug is not clear,
+but since all terminal emulators are equally affected, it must be something
+in the terminal driver, or elsewhere in the SunOS kernel.
+.IP (4)
+The hardcopy functions assume a 1 or 8 bit frame buffer and will not work
+properly on a Sun-3/110, 3/60, etc., unless the \fB\-8bit_color_only\fR option
+is specified to \fIsuntools\fR [\fIfixed in Gterm V1.2]\fR.
+.IP (5)
+Reverse video does not work on a monochrome workstation as there is no
+colortable and no way to exchange the foreground and background colortable
+entries; try the \fB\-i\fR option to \fIsuntools\fR instead.
+.IP (6)
+\fIgterm\fR modifies the keyboard translation table entries for the arrow keys
+while a cursor read is in progress, restoring the translation table entries
+when done (this is necessary to allow the arrow keys to be used to terminate
+cursor reads). If something should happen to \fIgterm\fR while it is waiting
+for cursor input, it is possible that the arrow key translation table entries
+may not be restored. If this should happen, executing \fBsetkeys reset\fR will
+fix things. Note also that changes to the keyboard translation tables are
+global, i.e., all windows are affected, hence while a cursor read is in
+progress in a \figterm\fR window, the arrow keys may not be usable with a
+program running in a different window.
+.IP (7)
+When plotting with clipping disabled there are cases where it is possible for
+\fIgterm\fR to coredump with a segmentation violation, killing any interactive
+subprocesses running within the terminal.
+.SH AUTHOR
+Doug Tody, National Optical Astronomy Observatories (NOAO), IRAF project.
diff --git a/unix/sun/gtermio.c b/unix/sun/gtermio.c
new file mode 100644
index 00000000..d0348de2
--- /dev/null
+++ b/unix/sun/gtermio.c
@@ -0,0 +1,1224 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <suntool/sunview.h>
+#include <pixrect/pr_line.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "gterm.h"
+
+/*
+ * GTERMIO -- Graphics terminal i/o. This is the low level code which
+ * filters graphics output out of the pseudoterminal output stream and
+ * then processes the graphics instructions, drawing into the graphics
+ * pixwin. Graphics output from the pty is written into a circular
+ * buffer by a low level routine called when data is read from the pty
+ * by the ttysw code. A differed routine is called back by the notifier
+ * to process the data, writing graphics into the gio pixwin. The graphics
+ * data language implemented is tektronix standard plus extensions.
+ */
+
+extern int clip_graphics;
+extern int cursor_show;
+extern int gio_canvas;
+extern FILE *gt_logfp;
+int gio_graphicsenabled = 0; /* switch text/graphics output */
+int gio_enabled = 1; /* enable graphics window */
+
+/* Size limiting definitions. */
+#define SZ_GBUF 8192 /* max buffered graphics data */
+#define GB_MINSPACE 2048 /* XOFF when this much left */
+#define GB_BIGSPACE 3072 /* XON when this much available */
+#define MAX_PLPTS 4096 /* max points in a polyline */
+#define SZ_TXBUF 256 /* max chars in a polytext */
+#define MAX_TEXTCHARS 80 /* max chars in text line */
+#define SL_XOFFSET 0 /* x offset to status line */
+#define SL_YOFFSET 5 /* y offset to status line */
+#define MAX_QUOTA 512 /* limit for one proc. loop */
+#define WSOPEN_DELAY 100 /* delay at openws (msec) */
+#define WSCLOSE_DELAY 0 /* delay at closews (msec) */
+#define LOG_SYNCTIME 15 /* sync interval for logfile */
+
+/* Magic numbers. */
+#define SET_BITS 0 /* draw vectors|points */
+#define CLEAR_BITS 1 /* erase vectors|points */
+#define TOGGLE_BITS 2 /* toggle data bits */
+#define COMMAND_MODE 0 /* initial state */
+#define ALPHA_MODE 1 /* tek-alpha character drawing */
+#define TEXT_MODE 2 /* output to status line */
+#define VECTOR_MODE 3 /* draw vectors or points */
+#define CURSOR_MODE 4 /* read crosshair cursor posn */
+#define BREAK_LINE (-2) /* special automargin code */
+
+/* ASCII codes. */
+#define ENQ '\005'
+#define BEL '\007'
+#define CR '\015'
+#define CAN '\030'
+#define SUB '\032'
+#define ESC '\033'
+#define FS '\034'
+#define GS '\035'
+#define RS '\036'
+#define US '\037'
+
+/* Pseudoterminal i/o.
+ */
+static int pty_fd; /* fd of pseudoterminal */
+static int pty_stop = 0; /* set when XOFF is set on pty */
+
+/* The graphics data buffer, a circular buffer.
+ */
+static char g_buf[SZ_GBUF]; /* circular buffer */
+static char *g_top= &g_buf[SZ_GBUF];/* end of buffer + 1 */
+static char *g_ip = g_buf; /* input pointer */
+static char *g_op = g_buf; /* output pointer */
+
+#define g_getc(c)\
+ (g_ip==g_op ? -1 : ((c) = *g_ip++, g_ip >= g_top ? *(g_ip=g_buf):0))
+#define g_putc(c)\
+ (*g_op++ = (c), ((g_op >= g_top) ? g_op = g_buf : g_op))
+#define g_ungetc(c)\
+ (g_ip = ((g_ip==g_buf) ? g_top-1 : g_ip-1))
+#define g_spaceleft\
+ (g_ip <= g_op ? (g_top - g_op + g_ip - g_buf) : (g_ip - g_op))
+#define g_havedata (g_ip != g_op)
+#define g_mark(ip) ((ip)=g_ip)
+#define g_reset(ip) (g_ip=(ip))
+#define g_equal(ip) ((ip)==g_ip)
+
+/* Polyline (polymarker) output-point buffer.
+ */
+static char pl_text[MAX_PLPTS]; /* encoded [x,y] coord data */
+static struct pr_pos pl_p[MAX_PLPTS]; /* polyline storage */
+static int pl_npts = 0; /* npoints in polyline */
+static int pl_op = 0; /* which char in coord pair */
+static int pl_linestyle = 0; /* dashline drawing style */
+static int pl_pointmode = 0; /* point or line mode */
+
+static int ohiy=0; oloy=0; /* encoded current position */
+static int ohix=0; olox=0;
+
+static Pr_brush brush = { 1 };
+#define pl_linewidth brush.width /* vector drawing linewidth */
+
+/* Graphics text variables.
+ */
+static struct fonttab *alpha_font; /* alpha mode font */
+static struct fonttab *text_font; /* text mode font */
+static char tx_buf[SZ_TXBUF+1]; /* polytext text buffer */
+static int tx_len = 0; /* nchars in buffer */
+static int tx_maxlines; /* nlines of text on a screen */
+static int tx_maxcols; /* ncols of text on a screen */
+static int tx_charheight; /* height of a char in pixels */
+static int tx_charwidth; /* width of a char in pixels */
+static int tx_charbase; /* topline to baseline distance */
+static int tx_leftmargin; /* where columns start */
+static int sl_x, sl_y; /* current pos. in status line */
+static int sl_xoff, sl_yoff; /* x,y offset of status line */
+static int sl_cwidth; /* status line char width */
+static int sl_cheight; /* status line char height */
+static int sl_cbase; /* topline to baseline distance */
+static int sl_width; /* status line rect width */
+static int sl_height; /* status line rect height */
+static int sl_rect_saved = 0; /* set after sl rect is saved */
+static struct pixrect *sl_pr=NULL; /* saved status line pixrect */
+static struct fonttab *chcur_font; /* character cursor font */
+static int chcur_x; /* character cursor xpos */
+static int chcur_y; /* character cursor ypos */
+static int chcur_on = 0; /* character cursor displayed? */
+static int chcur_skip = 0; /* used to skip cursor updates */
+
+/* Miscellaneous variables.
+ */
+static struct pixwin *pw; /* graphics pixwin */
+static struct rect pw_r; /* full rect of the pixwin */
+static int cur_x, cur_y; /* current x,y position */
+static int win_xres, win_yres; /* size of graphics pixwin */
+static int tek_xres; /* X resolution of input data */
+static int tek_yres; /* Y resolution of input data */
+static int trailer1 = '\r'; /* trailer code, cursor value */
+static int trailer2 = -1; /* second trailer code (opt) */
+static int gio_mode=COMMAND_MODE; /* graphics drawing mode */
+static int gio_datalevel=SET_BITS; /* set, clear, or toggle bits */
+static int workstation_open = 0; /* have issued open workstation */
+static int wait_cursor = 0; /* waiting for cursor input */
+static int gio_delay = 0; /* programmed delay in progress */
+
+int ev_ptyoutput();
+static Pr_texture *pl_texture();
+static Notify_value ev_gioprocessdata();
+
+/* Macros to convert between tektronix and window coordinates. */
+#define X_TEK2WIN(x) ( ((x) * win_xres + tek_xres/2) / tek_xres)
+#define Y_TEK2WIN(y) (win_yres - (((y) * win_yres + tek_yres/2) / tek_yres))
+#define X_WIN2TEK(x) ((( (x)) * tek_xres + win_xres/2) / win_xres)
+#define Y_WIN2TEK(y) (((win_yres - (y)) * tek_yres + win_yres/2) / win_yres)
+
+
+/* GIO_SETUP -- Called by the high level Gterm window management code to
+ * give the gtermio code the file descriptor of the pty and the pixwin of
+ * the graphics frame.
+ */
+gio_setup (fd, gio_pw)
+int fd; /* fd of pty */
+struct pixwin *gio_pw; /* graphics pixwin */
+{
+ pty_fd = fd;
+ pw = gio_pw;
+
+ notify_read_post_monitor_fcn (pty_fd, ev_ptyoutput);
+ notify_set_event_func (ev_gioprocessdata,
+ ev_gioprocessdata, NOTIFY_SAFE);
+}
+
+
+/* GIO_HARDRESET -- Reset everything, including cancelling any cursor read
+ * that may be in progress.
+ */
+gio_hardreset (mc_xres, mc_yres, a_font, t_font)
+int mc_xres, mc_yres; /* virtual x,y resolution */
+struct fonttab *a_font; /* alpha mode font */
+struct fonttab *t_font; /* text mode font */
+{
+ gio_mode = COMMAND_MODE;
+ gio_graphicsenabled = 0;
+ workstation_open = 0;
+ wait_cursor = 0;
+ gio_delay = 0;
+ pty_stop = 0;
+
+ ioctl (pty_fd, TIOCSTART, NULL);
+ gio_reset (mc_xres, mc_yres, a_font, t_font);
+}
+
+
+/* GIO_RESET -- Reset the state of the gtermio code. Should be called
+ * whenever any important data structures change, e.g., if the graphics
+ * frame is resized.
+ */
+gio_reset (mc_xres, mc_yres, a_font, t_font)
+int mc_xres, mc_yres; /* virtual x,y resolution */
+struct fonttab *a_font; /* alpha mode font */
+struct fonttab *t_font; /* text mode font */
+{
+ erase_cursor();
+ chcur_skip = 0;
+
+ tek_xres = mc_xres;
+ tek_yres = mc_yres;
+ alpha_font = a_font;
+ text_font = t_font;
+
+ pw_get_region_rect (pw, &pw_r);
+ win_xres = pw_r.r_width;
+ win_yres = pw_r.r_height;
+
+ tx_leftmargin = 0;
+ tx_charwidth = alpha_font->ch_xsize;
+ tx_charheight = alpha_font->ch_ysize;
+ tx_charbase = -alpha_font->pixfont->pf_char['0'].pc_home.y;
+ tx_maxlines = win_yres / tx_charheight;
+ tx_maxcols = win_yres / tx_charwidth;
+ tx_len = 0;
+
+ sl_cwidth = text_font->ch_xsize;
+ sl_cheight = text_font->ch_ysize;
+ sl_cbase = -text_font->pixfont->pf_char['0'].pc_home.y;
+ sl_xoff = SL_XOFFSET;
+ sl_yoff = win_yres - SL_YOFFSET;
+ sl_x = sl_xoff;
+ sl_y = sl_yoff;
+ sl_width = win_xres - sl_xoff;
+ sl_height = sl_cheight;
+
+ if (sl_pr != NULL)
+ pr_destroy (sl_pr);
+ sl_pr = mem_create (sl_width, sl_height, 1);
+ sl_rect_saved = 0;
+
+ g_top = &g_buf[SZ_GBUF];
+ g_ip = g_op = g_buf;
+ pl_npts = 0;
+ pl_op = 0;
+ pl_linestyle = 0;
+ pl_linewidth = 1;
+ pl_pointmode = 0;
+ ohiy = 0; oloy = 0;
+ ohix = 0; olox = 0;
+
+ cur_x = tx_leftmargin;
+ cur_y = tx_charbase;
+}
+
+
+/* GIO_ENABLE -- Enable or disable the graphics window. If graphics is
+ * disabled, all i/o is directed to the text window.
+ */
+gio_enable (onoff)
+int onoff;
+{
+ if ((gio_enabled = onoff) == GRAPHICS_OFF)
+ gio_graphicsenabled = 0;
+}
+
+
+/* GIO_SETGINMODETERM -- Set the GIN mode (cursor read) trailer codes,
+ * expressed as octal constants in the input string argument.
+ */
+gio_setginmodeterm (str)
+char *str;
+{
+ register char *ip;
+ register int n;
+
+ trailer1 = trailer2 = -1;
+
+ for (ip=str; isspace(*ip); ip++)
+ ;
+ if (isdigit(*ip)) {
+ for (n=0; isdigit(*ip); ip++)
+ n = n * 8 + *ip - '0';
+ trailer1 = n;
+ }
+
+ while (*ip && isspace(*ip))
+ ip++;
+ if (isdigit(*ip)) {
+ for (n=0; isdigit(*ip); ip++)
+ n = n * 8 + *ip - '0';
+ trailer2 = n;
+ }
+}
+
+
+/* EV_PTYOUTPUT -- Process pty output packets. Output directed to the
+ * terminal (/dev/tty) by the applications program appears as read-pending
+ * events on the pty seen by the Gterm program. We let the TTY code monitor
+ * the pty and respond to read-pending events. The low level read primitive
+ * (notify_read) ultimately called to service a read request by TTY reads
+ * the data and then calls us to process the data packet. We extract any
+ * graphics output from the packet and append it to the gio buffer. If data
+ * is added to the gio buffer a gio-data-pending event is queued so that
+ * the graphics drawing code will be called to process the new data. The
+ * remaining data, or a null length packet if the packet contained only
+ * graphics data, is returned to TTY, completing the read. Sometime later
+ * the graphics drawing code will be called to process the data.
+ */
+ev_ptyoutput (ttybuf, nchars)
+char *ttybuf; /* raw data on input, tty data on output */
+int nchars; /* nchars of raw data */
+{
+ register char *itop = ttybuf + nchars;
+ register char *op, *ip = ttybuf, ch;
+ static unsigned long oldtime = 0;
+
+ /* Copy to logfile if logging is enabled. */
+ if (gt_logfp) {
+ fwrite (ttybuf, nchars, 1, gt_logfp);
+ if (time(0) - oldtime > LOG_SYNCTIME) {
+ /* Sync the logfile output every so often. */
+ fflush (gt_logfp);
+ oldtime = time(0);
+ }
+ }
+
+ if (gio_enabled == GRAPHICS_OFF || nchars <= 0)
+ return (nchars);
+
+ /* If in text mode, make a quick scan for the GS character and return
+ * the entire data packet if GS is not seen.
+ */
+ if (!gio_graphicsenabled) {
+ while (ip < itop && *ip != GS)
+ ip++;
+ if (ip >= itop)
+ return (nchars);
+ else {
+ gio_graphicsenabled++;
+ op = ip;
+ }
+ } else
+ op = ttybuf;
+
+ /* Process rest of data in graphics mode. IP is pointing at the
+ * first char of graphics data, ITOP at the top of the buffer,
+ * and OP at the next tty output char. Filter out any NULs in
+ * the process of copying the data.
+ */
+ while (ip < itop)
+ if (gio_graphicsenabled) {
+ while (ip < itop)
+ if ((ch = *ip++) == CAN) {
+ g_putc (ch);
+ gio_graphicsenabled = 0;
+ break;
+ } else if (ch)
+ g_putc (ch);
+ } else {
+ while (ip < itop)
+ if ((ch = *ip++) == GS) {
+ g_putc (ch);
+ gio_graphicsenabled = 1;
+ break;
+ } else if (ch)
+ *op++ = ch;
+ }
+
+ /* If the gio buffer has reached the high-water mark and XOFF is
+ * not currently set, send XOFF to the terminal driver.
+ */
+ if (g_spaceleft < GB_MINSPACE && !pty_stop) {
+ ioctl (pty_fd, TIOCSTOP, NULL);
+ pty_stop++;
+ }
+
+ /* Post an event with the notifier to call the graphics drawing code
+ * back to process the new data.
+ */
+ if (!gio_delay)
+ notify_post_event (ev_gioprocessdata, NULL, NOTIFY_SAFE);
+
+ return (op - ttybuf);
+}
+
+
+/* EV_GIOPROCESSDATA -- Called to process graphics instructions and data from
+ * the gio buffer. This is the routine which actually draws lines and text
+ * in the graphics frame. May be called repeatedly to process any amount of
+ * data at a time. If there is a great amount of data to be processed the
+ * routine should return occasionally to allow the other GTERM event handlers
+ * to run (operation is not fully asynchronous).
+ *
+ * Graphics data is processed as a stream with no record boundaries, so that
+ * operation is not dependent on how data is buffered through the system.
+ * The graphics frame is a state machine which is by definition always in a
+ * legal state; garbage input causes garbage output, just like a real terminal.
+ * The states are as follows:
+ *
+ * COMMAND_MODE This is the initial state. Characters are accumulated
+ * until a known state is recognized. Receipt of ESC
+ * always causes command mode to be entered, since
+ * additional characters are needed to define the next
+ * instruction.
+ *
+ * ALPHA_MODE Characters are drawn in the graphics frame at the
+ * "current" position (normally set beforehand with a
+ * GS/US vector move), using the alpha mode font.
+ * Receipt of any control code causes alpha mode to be
+ * exited.
+ *
+ * TEXT_MODE Text mode is a special mode used to write transient
+ * text in the status line, using the text mode font.
+ * Lines of text are accumulated and displayed on the
+ * status line in reverse video; successive lines of text
+ * overwrite one another. The status line is cleared
+ * when text mode is entered, even if no text is drawn.
+ * Text mode is terminated by receipt of GS or CAN.
+ *
+ * VECTOR_MODE Vector mode refers to both polyline and polypoint
+ * vector sequences. The vertices of the points are
+ * accumulated in a buffer and displayed when the buffer
+ * fills or when vector mode is terminated. Vector
+ * mode is terminated by receipt of any control code;
+ * the tektronix coordinate encoding maps all possible
+ * coordinates into the printable ascii codes.
+ *
+ * CURSOR_MODE The crosshair cursor is turned on, signifying to the
+ * user that the system is waiting on a cursor read.
+ * Output processing ceases until the user types a key
+ * or presses a mouse button to trigger the cursor read.
+ * The cursor value is then encoded and transmitted back
+ * to the pty, and output processing resumes.
+ *
+ * Clearing the screen causes the mode to be reset to command mode, and all
+ * other drawing parameters to be set to their default values, e.g., data level
+ * on, solid line type, and so on.
+ */
+static Notify_value
+ev_gioprocessdata()
+{
+ register int quota, ch;
+ char *save_ip, *ip_start;
+ int delay = 0;
+
+ pw_lock (pw, &pw_r);
+ g_mark (ip_start);
+
+ /* Process data.
+ */
+ for (quota=MAX_QUOTA; --quota >= 0 && g_getc(ch) >= 0; ) {
+ if (ch == 0 || gio_enabled == GRAPHICS_DISCARD)
+ continue;
+again:
+ switch (gio_mode) {
+ case COMMAND_MODE:
+ switch (ch) {
+ case GS:
+ case FS:
+ gio_mode = VECTOR_MODE;
+ pl_npts = 0;
+ pl_op = 0;
+ pl_pointmode = (ch == FS);
+ chcur_skip = -1;
+ if (cursor_show)
+ gio_setcursor (CURSOR_OFF, 0);
+
+ /* Only execute an open workstation if we have not already
+ * done so and if the next command is something other than
+ * close workstation, i.e., no-op sequences GS-CAN are
+ * filtered out, since they would only cause a pointless
+ * switch to the graphics frame and back without drawing.
+ */
+ if (ch == GS && !workstation_open)
+ if (g_getc(ch) < 0) {
+ g_ungetc (GS);
+ gio_mode = COMMAND_MODE;
+ goto exit;
+ } else if (ch != CAN) {
+ gio_open_workstation();
+ workstation_open = 1;
+ delay = WSOPEN_DELAY;
+ g_ungetc (ch);
+ goto exit;
+ }
+ break;
+
+ case US:
+ case CR:
+ gio_mode = ALPHA_MODE;
+ tx_len = 0;
+ if (ch == CR)
+ goto again;
+ break;
+
+ case CAN:
+ if (workstation_open) {
+ pw_unlock (pw);
+ gio_close_workstation();
+ workstation_open = 0;
+ delay = WSCLOSE_DELAY;
+ }
+ gio_mode = COMMAND_MODE;
+ goto exit;
+
+ case ESC:
+ g_ungetc (ch);
+ g_mark (save_ip);
+ erase_cursor();
+ if ((gio_mode = gio_escape()) == -1) {
+ gio_mode = COMMAND_MODE;
+ g_reset (save_ip);
+ goto exit;
+ } else if (gio_mode == CURSOR_MODE)
+ goto again;
+ break;
+
+ case BEL:
+ window_bell (gio_canvas);
+ break;
+
+ default:
+ ; /* ignore unknown control chars */
+ }
+ break;
+
+ case ALPHA_MODE:
+ /* Tek alpha mode is used to write text to random positions on
+ * the screen, or to write lines of text to the gio frame in
+ * "storage scope" mode, where the left and right columns are
+ * alternately written into with an inclusive-or rop.
+ */
+ if (ch >= 040) {
+ tx_buf[tx_len++] = ch;
+ } else if (ch == '\t') {
+ tx_buf[tx_len++] = 040;
+ if (tx_leftmargin == 0)
+ while ((tx_len + (cur_x / tx_charwidth)) % 8 != 0)
+ tx_buf[tx_len++] = 040;
+ } else if (ch == '\010' || ch == '\177') {
+ if (tx_len > 0)
+ tx_len--;
+ else if (cur_x > tx_leftmargin)
+ cur_x -= tx_charwidth;
+ } else {
+flush_alpha: if (tx_len > 0) {
+ tx_buf[tx_len] = '\0';
+ erase_cursor();
+ pw_text (pw, cur_x, cur_y, gio_rop(),
+ alpha_font->pixfont, tx_buf);
+ }
+
+ cur_x += tx_len * tx_charwidth;
+ tx_len = 0;
+
+ if (ch == '\n' || ch == BREAK_LINE) {
+ cur_y += tx_charheight;
+ if (cur_y > win_yres) {
+ if (tx_leftmargin == 0)
+ tx_leftmargin = win_xres / 2;
+ else
+ tx_leftmargin = 0;
+ cur_y = tx_charbase;
+ if (cur_x < tx_leftmargin)
+ cur_x = tx_leftmargin;
+ }
+ if (ch == BREAK_LINE)
+ cur_x = tx_leftmargin;
+ } else if (ch == '\r') {
+ cur_x = tx_leftmargin;
+ } else if (ch != 0) {
+ gio_mode = COMMAND_MODE;
+ goto again;
+ }
+ }
+
+ /* Break long lines at the right margin. */
+ if (cur_x + (tx_len * tx_charwidth) >= win_xres) {
+ ch = BREAK_LINE;
+ goto flush_alpha;
+ }
+
+ break;
+
+ case TEXT_MODE:
+ if (ch >= 040)
+ tx_buf[tx_len++] = ch;
+ else if (ch == '\t')
+ tx_buf[tx_len++] = 040;
+ else if (ch == '\010' || ch == '\177') {
+ if (tx_len > 0) {
+ --tx_len;
+ sl_x -= sl_cwidth;
+ erase_cursor();
+ pw_text (pw, sl_x, sl_y,
+ PIX_SRC, text_font->pixfont, " ");
+ }
+ } else {
+ if (tx_len > 0) {
+ tx_buf[tx_len] = '\0';
+ erase_cursor();
+ pw_text (pw, sl_x, sl_y,
+ PIX_NOT(PIX_SRC), text_font->pixfont, tx_buf);
+ }
+
+ sl_x += tx_len * sl_cwidth;
+ if (sl_x > win_xres - sl_cwidth)
+ sl_x = win_xres - sl_cwidth;
+ tx_len = 0;
+
+ if (ch == '\r' || ch == '\n') {
+ sl_x = sl_xoff;
+ sl_restore_rect();
+ } else if (ch != 0) {
+ gio_mode = COMMAND_MODE;
+ goto again;
+ }
+ }
+
+ /* Truncate long lines. */
+ if (sl_x / sl_cwidth + tx_len >= MAX_TEXTCHARS)
+ if (tx_len > 0)
+ --tx_len;
+ else
+ sl_x -= sl_cwidth;
+ break;
+
+ case VECTOR_MODE:
+ /* Following receipt of GS, accumulate encoded coordinate data
+ * until the buffer fills or a control code is received, then
+ * decode the encoded data to reconstruct the original data
+ * vector, and draw the vector.
+ */
+ if (ch >= 040)
+ pl_text[pl_op++] = ch;
+ if (ch < 040 || pl_op >= MAX_PLPTS)
+ pl_decodepts();
+
+ if (ch < 040 || pl_npts >= MAX_PLPTS) {
+ if (pl_pointmode && pl_npts >= 1) {
+ pw_polypoint (pw, 0, 0, pl_npts, pl_p,
+ PIX_COLOR(1) | gio_rop());
+ } else if (pl_npts >= 2) {
+ /* Must use clipping if dashed line. */
+ pw_polyline (pw, 0, 0, pl_npts, pl_p,
+ POLY_DONTCLOSE, &brush, pl_texture(pl_linestyle),
+ (PIX_COLOR(1) | gio_rop()) &
+ ~(pl_linestyle ? PIX_DONTCLIP : 0));
+ }
+
+ if (pl_npts > 0) {
+ cur_x = pl_p[pl_npts-1].x;
+ cur_y = pl_p[pl_npts-1].y;
+ pl_npts = 0;
+ }
+
+ if (ch < 040) {
+ gio_mode = COMMAND_MODE;
+ pl_op = 0;
+ goto again;
+ }
+ }
+
+ break;
+
+ case CURSOR_MODE:
+ if (wait_cursor++) {
+ g_ungetc (ch);
+ gio_mode = COMMAND_MODE;
+ } else
+ gio_readcursor();
+ break;
+ }
+ }
+
+exit:
+ /* Flush any buffered text before exiting, as applications will assume
+ * that text appears on the screen as soon as chars are written to the
+ * terminal (any buffering must be hidden).
+ */
+ if (tx_len > 0) {
+ ch = 0;
+ goto again;
+ }
+
+ update_cursor();
+ pw_unlock (pw);
+
+ /* If XOFF is set and the buffer has emptied sufficiently,
+ * send XON to the terminal driver to accept more data.
+ */
+ if (pty_stop && g_spaceleft > GB_BIGSPACE) {
+ ioctl (pty_fd, TIOCSTART, NULL);
+ pty_stop = 0;
+ }
+
+ /* If there is still data in the buffer (other than a partially
+ * formed escape sequence) post another callback event before exiting
+ * to allow other event handlers to run.
+ */
+ if (delay)
+ gio_pause (delay);
+ else if (g_havedata && !g_equal(ip_start) && ch != ESC && !wait_cursor)
+ notify_post_event (ev_gioprocessdata, NULL, NOTIFY_SAFE);
+
+ return (NOTIFY_DONE);
+}
+
+
+/* PL_DECODEPTS -- Convert a sequence of textronix encoded polyline vertices
+ * into a simple array of [x,y] coordinate pairs. Each coordinate pair is
+ * encoded as a sequence of from 1 to 4 bytes, with bytes being optionally
+ * eliminated which do not change from one coordinate pair to the next. The
+ * possible coordinate pair encodings are as follows:
+ *
+ * HIY LOY HIX LOX
+ * 01xxxxx 11xxxxx 01xxxxx 10xxxxx
+ * 040 140 040 100
+ *
+ * HIY LOX
+ * HIY LOY LOX
+ * HIY LOY HIX LOX
+ * LOY HIX LOX
+ * LOY LOX
+ * LOX
+ *
+ * In words, bytes which do not change need not be sent, except for the low-x
+ * byte (LOX). If the high-x byte changes, then the low-x byte must also be
+ * sent. The current position, stored as the 4 byte encoding, is cleared to
+ * zero when the screen is cleared.
+ */
+static
+pl_decodepts()
+{
+ register char *ip, *itop;
+ int hiy, loy, hix, lox, type, data, nb;
+ char *ip_save;
+
+ for (ip_save=ip=pl_text, itop = &pl_text[pl_op]; ip < itop; ) {
+ hiy = ohiy; loy = oloy;
+ hix = ohix; lox = olox;
+
+ for (nb=0; nb < 99 && ip < itop; nb++) {
+ type = (*ip & 0140);
+ data = (*ip++ & 037);
+
+ switch (type) {
+ case 040: /* HIY, HIX */
+ if (nb == 0)
+ hiy = data;
+ else
+ hix = data;
+ break;
+ case 0140: /* LOY */
+ loy = data;
+ break;
+
+ case 0100:
+ /* Receipt of LOX marks the end of the variable length
+ * sequence of bytes required to form the next [x,y].
+ */
+ lox = data;
+ pl_p[pl_npts].x = X_TEK2WIN ((hix << 5) + lox);
+ pl_p[pl_npts].y = Y_TEK2WIN ((hiy << 5) + loy);
+
+ /* Update current position. */
+ ohiy = hiy; oloy = loy;
+ ohix = hix; olox = lox;
+
+ ip_save = ip;
+ pl_npts++;
+ nb = 99; /* EXIT */
+ break;
+ }
+ }
+ }
+
+ /* If there is any data left over (too few bytes to form a coordinate
+ * pair) move these to the start of the buffer.
+ */
+ for (pl_op=0, ip=ip_save; ip < itop; )
+ pl_text[pl_op++] = *ip++;
+}
+
+
+/* GIO_PAUSE -- Suspend output for the indicated number of milliseconds, to
+ * allow other event processing to catch up. When the specified interval has
+ * passed an ev_gioprocessdata event is posted to resume output processing.
+ */
+gio_pause (msec)
+int msec;
+{
+ static Notify_value ev_restart();
+ static struct itimerval itimer_delay;
+
+ gio_delay = msec;
+
+ itimer_delay.it_interval.tv_usec = 0;
+ itimer_delay.it_interval.tv_sec = 0;
+
+ itimer_delay.it_value.tv_usec = (msec % 1000) * 1000;
+ itimer_delay.it_value.tv_sec = (msec / 1000);
+
+ notify_set_itimer_func (&itimer_delay, ev_restart, ITIMER_REAL,
+ &itimer_delay, NULL);
+}
+
+
+/* EV_RESTART -- Called when the specified interval has passed to restart
+ * output processing.
+ */
+static Notify_value
+ev_restart()
+{
+ gio_delay = 0;
+ notify_post_event (ev_gioprocessdata, NULL, NOTIFY_SAFE);
+
+ return (NOTIFY_DONE);
+}
+
+
+/* GIO_RETCURSOR -- Encode and return a cursor value to the pty (and thence
+ * to the program which initiated the cursor read). Clear the cursor read
+ * pending flag so that output processing can resume, and post an event to
+ * restart the output processing routine.
+ */
+gio_retcursor (key, x, y)
+int key; /* key (or whatever) typed to trigger read */
+int x, y; /* pixwin coords of event */
+{
+ register int mc_x, mc_y;
+ char curval[7];
+ int len;
+
+ /* Ignore cursor events unless requested via program control.
+ */
+ if (!wait_cursor)
+ return (-1);
+
+ mc_x = X_WIN2TEK (x);
+ mc_y = Y_WIN2TEK (y);
+
+ curval[0] = key;
+ curval[1] = ((mc_x >> 5) & 037) | 040;
+ curval[2] = ((mc_x ) & 037) | 040;
+ curval[3] = ((mc_y >> 5) & 037) | 040;
+ curval[4] = ((mc_y ) & 037) | 040;
+ curval[5] = trailer1;
+ curval[6] = trailer2;
+
+ len = 5;
+ if (trailer1 >= 0) len++;
+ if (trailer2 >= 0) len++;
+ write (pty_fd, curval, len);
+
+ wait_cursor = 0;
+ gio_mode = COMMAND_MODE;
+ chcur_skip = -1;
+
+ if (!gio_delay)
+ notify_post_event (ev_gioprocessdata, NULL, NOTIFY_SAFE);
+}
+
+
+/* GIO_RETENQ -- Respond to the ESC ENQ request.
+ */
+gio_retenq()
+{
+ register int mc_x, mc_y;
+ char curval[7];
+ int len;
+
+ /* Graphics status word. */
+ curval[0] = (061 | ((gio_mode == ALPHA_MODE) << 2)
+ | ((tx_leftmargin != 0) << 1));
+
+ /* Alpha cursor position. */
+ mc_x = X_WIN2TEK (cur_x);
+ mc_y = Y_WIN2TEK (cur_y);
+
+ curval[1] = ((mc_x >> 5) & 037) | 040;
+ curval[2] = ((mc_x ) & 037) | 040;
+ curval[3] = ((mc_y >> 5) & 037) | 040;
+ curval[4] = ((mc_y ) & 037) | 040;
+ curval[5] = trailer1;
+ curval[6] = trailer2;
+
+ len = 5;
+ if (trailer1 >= 0) len++;
+ if (trailer2 >= 0) len++;
+ write (pty_fd, curval, len);
+}
+
+
+/* Definitions and data structures for a fast table driven fixed pattern
+ * escape sequence recognizer. Given character I of the sequence there will
+ * be N candidate sequences that have matched the first I-1 chars. Examine
+ * each to produce the next list of candidate sequences. Continue until either
+ * a sequence is matched or there are no more candidates. Variable length
+ * sequences such as "ESC[Pl;PcH" are handled as a special case: the general
+ * form of these is ESC '[' <digits> [';' <digits>...] LET.
+ */
+#define MAX_CANDIDATES 32 /* max candidate escseq */
+#define MAX_FIELDS 6 /* max fields in an escseq */
+
+struct _esc {
+ char e_tag; /* integer code for escseq */
+ char e_seq[MAX_FIELDS+1]; /* the sequence itself */
+};
+
+static struct _esc *e_cand1[MAX_CANDIDATES]; /* 1st candidates array */
+static struct _esc *e_cand2[MAX_CANDIDATES]; /* 2nd candidates array */
+static struct _esc **e_pcand, **e_acand; /* candidates arrays */
+static int e_npcand, e_nacand; /* number of candidates */
+static int e_charno; /* char being examined */
+
+static struct _esc e_table[] = {
+#include "gterm.esc" /* Gterm escape sequence table */
+ { 0, 0,0,0,0,0,0,0 }
+};
+
+
+/* GIO_ESCAPE -- Recognize and process graphics escape sequences, i.e.,
+ * all multicharacter command codes beginning with ESC. The simple single
+ * character command codes are handled directly by the data processing code.
+ * The escapes have no well defined pattern to them, hence we must simply
+ * consume characters until a legal escape sequence is recognized or the
+ * sequence is found to not match any known sequence. It is possible that
+ * all of the characters forming a sequence will not yet have been deposited
+ * in the input buffer, in which case we return -1, indicating to our caller
+ * that we should be called back later to rescan the same input, when more
+ * data becomes available. Otherwise, we take whatever action is implied
+ * for the escape sequence and return the new mode to the interpreter code.
+ * If an unrecognized escape sequence is encountered it is discarded and we
+ * return in alpha mode so that subsequent input appears as garbage on the
+ * screen.
+ */
+gio_escape()
+{
+ register struct _esc *esc;
+ register int ch, i, j;
+ struct _esc **e_temp;
+ int tag;
+
+ /* Discard the ESC and get the first char. */
+ g_getc (ch);
+ if (g_getc (ch) < 0)
+ return (-1);
+
+ /* Build the initial list of candidates. This is the most expensive
+ * step, since all sequences must be examined.
+ */
+ for (esc=e_table, e_pcand=e_cand1, e_npcand=0; esc->e_tag; esc++)
+ if (ch == esc->e_seq[0]) {
+ if (esc->e_seq[1] == 0) {
+ tag = esc->e_tag;
+ goto action;
+ }
+ e_pcand[e_npcand++] = esc;
+ }
+
+ /* If there were no candidates, we are done. */
+ if (e_npcand == 0) {
+ g_ungetc (ch);
+ return (ALPHA_MODE);
+ }
+
+ /* Examine successive characters from the input, building a new,
+ * shorter candidate list on each iteration. This should converge
+ * very rapidly one way or the other.
+ */
+ for (j=1, e_acand=e_cand2; j < MAX_FIELDS && e_npcand > 0; j++) {
+ if (g_getc(ch) < 0)
+ return (-1);
+
+ /* Examine the next character of each sequence in the list of
+ * candidate sequences. If we have a complete match, we are
+ * done, else if we have a single character match add the seq
+ * to the new candidates list.
+ */
+ e_nacand = 0;
+ for (i=0; i < e_npcand; i++) {
+ esc = e_pcand[i];
+ if (ch == esc->e_seq[j]) {
+ if (esc->e_seq[j+1] == 0) {
+ tag = esc->e_tag;
+ goto action;
+ }
+ e_acand[e_nacand++] = esc;
+ }
+ }
+
+ e_temp = e_pcand; e_pcand = e_acand; e_acand = e_temp;
+ e_npcand = e_nacand;
+ }
+
+ /* If the escape sequence was recognized the above code should have
+ * vectored off to the action marker below. If we fall through the
+ * loop it can only mean that we have an unrecognized escape sequence,
+ * so discard it and return in command mode.
+ */
+ g_ungetc (ch);
+ return (ALPHA_MODE);
+
+action:
+ /* Process the escape sequence. */
+ switch (tag) {
+ case ESC_SETTEXTMODE:
+ tx_len = 0;
+ sl_x = sl_xoff;
+ if (sl_rect_saved)
+ sl_restore_rect();
+ else
+ sl_save_rect();
+ return (TEXT_MODE);
+
+ case ESC_ENQUIRE:
+ gio_retenq();
+ break;
+ case ESC_READCURSOR:
+ return (CURSOR_MODE);
+ case ESC_SETCURSOR:
+ gio_setcursorpos (cur_x, cur_y);
+ break;
+
+ case ESC_CLEARSCREEN:
+ pw_writebackground (pw, 0, 0, win_xres, win_yres, PIX_SRC);
+ tx_leftmargin = 0;
+ cur_x = tx_leftmargin;
+ cur_y = tx_charbase;
+ ohiy = 0; oloy = 0;
+ ohix = 0; olox = 0;
+ gio_datalevel = SET_BITS;
+ pl_linestyle = 0;
+ pl_linewidth = 1;
+ pl_pointmode = 0;
+ sl_rect_saved = 0;
+ chcur_on = 0;
+ chcur_skip = 0;
+ return (ALPHA_MODE);
+
+ case ESC_SETCHARSIZE0:
+ case ESC_SETCHARSIZE1:
+ case ESC_SETCHARSIZE2:
+ case ESC_SETCHARSIZE3:
+ /* Ignore these for now. */
+ break;
+
+ case ESC_SETDATALEVEL0:
+ gio_datalevel = SET_BITS;
+ break;
+ case ESC_SETDATALEVEL1:
+ gio_datalevel = CLEAR_BITS;
+ break;
+ case ESC_SETDATALEVEL2:
+ gio_datalevel = TOGGLE_BITS;
+ break;
+
+ case ESC_SETLINESTYLE0:
+ pl_linestyle = 0;
+ break;
+ case ESC_SETLINESTYLE1:
+ pl_linestyle = 1;
+ break;
+ case ESC_SETLINESTYLE2:
+ pl_linestyle = 2;
+ break;
+ case ESC_SETLINESTYLE3:
+ pl_linestyle = 3;
+ break;
+ case ESC_SETLINESTYLE4:
+ pl_linestyle = 4;
+ break;
+
+ case ESC_SETLINEWIDTH0:
+ pl_linewidth = 1;
+ break;
+ case ESC_SETLINEWIDTH1:
+ pl_linewidth = 2;
+ break;
+ case ESC_SETLINEWIDTH2:
+ pl_linewidth = 3;
+ break;
+ default:
+ ;
+ }
+
+ return (COMMAND_MODE);
+}
+
+
+/* UPDATE_CURSOR -- Update the state of the alpha mode cursor, used to mark
+ * the position of the next character on the screen when in alpha mode.
+ * In any other mode this cursor is turned off.
+ */
+update_cursor()
+{
+ erase_cursor();
+ if (gio_mode == ALPHA_MODE && chcur_skip++ >= 0) {
+ /* Update the position of the alpha character cursor.
+ */
+ pw_text (pw, cur_x, cur_y,
+ PIX_NOT(PIX_DST), alpha_font->pixfont, " ");
+ chcur_font = alpha_font;
+ chcur_x = cur_x;
+ chcur_y = cur_y;
+ chcur_on = 1;
+
+ /* Turn the mouse cursor on too, if currently disabled.
+ */
+ gio_setcursor (CURSOR_ON, 0);
+ }
+}
+
+
+/* ERASE_CURSOR -- If the character cursor is currently displayed, restore the
+ * character under the cursor to its former state.
+ */
+erase_cursor()
+{
+ if (chcur_on) {
+ pw_text (pw, chcur_x, chcur_y,
+ PIX_NOT(PIX_DST), chcur_font->pixfont, " ");
+ chcur_on = 0;
+ }
+}
+
+
+/* SL_SAVE_RECT -- Make a copy of the status line pixrect in a memory
+ * pixrect, so that we can later "erase" the status line by overwriting
+ * it with the saved data rect.
+ */
+sl_save_rect()
+{
+ if (sl_pr) {
+ pw_read (sl_pr, 0, 0, sl_width, sl_height, PIX_SRC,
+ pw, sl_xoff, sl_yoff - sl_cbase);
+ sl_rect_saved = 1;
+ }
+}
+
+
+/* SL_RESTORE_RECT -- Restore the saved status line data pixrect.
+ */
+sl_restore_rect()
+{
+ if (sl_pr)
+ pw_write (pw, sl_xoff, sl_yoff - sl_cbase, sl_width, sl_height,
+ PIX_SRC, sl_pr, 0, 0);
+}
+
+
+/* GIO_ROP -- Return the raster op appropriate for the datalevel control
+ * option set by the user, i.e, set, clear, or toggle bits.
+ */
+gio_rop()
+{
+ register int rop;
+
+ switch (gio_datalevel) {
+ case SET_BITS:
+ rop = PIX_SRC | PIX_DST;
+ break;
+ case CLEAR_BITS:
+ rop = PIX_NOT(PIX_SRC) & PIX_DST;
+ break;
+ case TOGGLE_BITS:
+ rop = PIX_SRC ^ PIX_DST;
+ break;
+ default:
+ rop = PIX_SRC;
+ break;
+ }
+
+ if (!clip_graphics)
+ rop |= PIX_DONTCLIP;
+
+ return (rop);
+}
+
+
+#define NLINETYPES 5
+static short lt_dashed[] = { 8, 3, 8, 3, 8, 3, 8, 3, 0 };
+static short lt_dotted[] = { 2, 3, 2, 3, 2, 3, 2, 3, 0 };
+static short lt_dashdot[] = { 14, 3, 1, 3, 14, 3, 1, 3, 0 };
+static short lt_dash3dot[] = { 20, 3, 1, 3, 1, 3, 1, 3, 0 };
+
+static short *lt_pattern[] = {
+ NULL,
+ lt_dashed,
+ lt_dotted,
+ lt_dashdot,
+ lt_dash3dot
+};
+
+
+/* PL_TEXTURE -- Return a pointer to a texture descriptor (Breshingham
+ * dashed line drawing algorithm) to be used to draw a dashed polyline.
+ * The case linetype==0 is special, signifying a solid line. The descriptor
+ * must be initialized to a known state, i.e., zeroed, on each call or
+ * the pixrect polyline code will produce garbage.
+ */
+static Pr_texture *
+pl_texture (linetype)
+int linetype;
+{
+ register char *p;
+ register int n;
+ static Pr_texture tex;
+ short *pattern;
+
+ if (linetype == 0)
+ return (NULL); /* solid line */
+ else
+ pattern = lt_pattern[linetype % NLINETYPES];
+
+ for (p=(char *)(&tex), n=sizeof(tex); --n >= 0; )
+ *p++ = NULL;
+
+ tex.pattern = pattern;
+ tex.options.givenpattern = 1;
+
+ return (&tex);
+}
diff --git a/unix/sun/halley.lut b/unix/sun/halley.lut
new file mode 100644
index 00000000..ac00326a
--- /dev/null
+++ b/unix/sun/halley.lut
@@ -0,0 +1,257 @@
+256,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.86275,
+0.00000, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.51765,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98431, 0.81176, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 1.00000,
+0.00000, 1.00000, 1.00000,
+0.47059, 0.78431, 1.00000,
+0.47059, 0.78431, 1.00000,
+0.47059, 0.78431, 1.00000,
+0.62745, 0.62745, 1.00000,
+0.62745, 0.62745, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+1.00000, 0.70588, 1.00000,
+1.00000, 0.70588, 1.00000,
+1.00000, 0.76863, 1.00000,
+1.00000, 0.86275, 1.00000,
+1.00000, 0.86275, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 0.89804, 1.00000,
+1.00000, 0.86275, 1.00000,
+1.00000, 0.86275, 1.00000,
+1.00000, 0.86275, 1.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.92157, 0.61961, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.78431, 0.47059, 1.00000,
+0.65882, 0.59608, 1.00000,
+0.62745, 0.62745, 1.00000,
+0.62745, 0.62745, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+0.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.70588,
+0.00000, 1.00000, 0.65490,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.00000, 1.00000, 0.00000,
+0.36078, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.70588, 1.00000, 0.00000,
+0.98431, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+1.00000, 1.00000, 0.00000,
+0.99608, 0.97647, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98431, 0.85098, 0.00000,
+0.98824, 0.77647, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+0.98824, 0.72549, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.36863, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.00000,
+1.00000, 0.00000, 0.46667,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 0.70588,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+1.00000, 0.00000, 1.00000,
+0.91373, 0.00000, 0.97255,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.70588, 0.00000, 0.90196,
+0.53333, 0.00000, 0.87451,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+0.47059, 0.00000, 0.86275,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+1.00000, 1.00000, 1.00000,
+0.00000, 0.00000, 0.86275,
+0.00000, 0.00000, 0.86275,
+0.00000, 0.00000, 0.86275,
+0.00000, 0.00000, 0.86275,
+0.00000, 0.00000, 0.80392,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.70588,
+0.00000, 0.00000, 0.13725,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000,
+0.00000, 0.00000, 0.00000
diff --git a/unix/sun/heat.lut b/unix/sun/heat.lut
new file mode 100644
index 00000000..124a70f4
--- /dev/null
+++ b/unix/sun/heat.lut
@@ -0,0 +1,257 @@
+256,
+0.00000, 0.00000, 0.00000,
+0.01176, 0.00392, 0.00000,
+0.02353, 0.00784, 0.00000,
+0.03529, 0.01176, 0.00000,
+0.04706, 0.01569, 0.00000,
+0.05882, 0.01961, 0.00000,
+0.07059, 0.02353, 0.00000,
+0.08235, 0.02745, 0.00000,
+0.09412, 0.03137, 0.00000,
+0.10588, 0.03529, 0.00000,
+0.11765, 0.03922, 0.00000,
+0.12941, 0.04314, 0.00000,
+0.14118, 0.04706, 0.00000,
+0.15294, 0.05098, 0.00000,
+0.16471, 0.05490, 0.00000,
+0.17647, 0.05882, 0.00000,
+0.18824, 0.06275, 0.00000,
+0.20000, 0.06667, 0.00000,
+0.21176, 0.07059, 0.00000,
+0.22353, 0.07451, 0.00000,
+0.23529, 0.07843, 0.00000,
+0.24706, 0.08235, 0.00000,
+0.25882, 0.08627, 0.00000,
+0.27059, 0.09020, 0.00000,
+0.28235, 0.09412, 0.00000,
+0.29412, 0.09804, 0.00000,
+0.30588, 0.10196, 0.00000,
+0.31765, 0.10588, 0.00000,
+0.32941, 0.10980, 0.00000,
+0.34118, 0.11373, 0.00000,
+0.35294, 0.11765, 0.00000,
+0.36471, 0.12157, 0.00000,
+0.37647, 0.12549, 0.00000,
+0.38824, 0.12941, 0.00000,
+0.40000, 0.13333, 0.00000,
+0.41176, 0.13725, 0.00000,
+0.42353, 0.14118, 0.00000,
+0.43529, 0.14510, 0.00000,
+0.44706, 0.14902, 0.00000,
+0.45882, 0.15294, 0.00000,
+0.47059, 0.15686, 0.00000,
+0.48235, 0.16078, 0.00000,
+0.49412, 0.16471, 0.00000,
+0.50588, 0.16863, 0.00000,
+0.51765, 0.17255, 0.00000,
+0.52941, 0.17647, 0.00000,
+0.54118, 0.18039, 0.00000,
+0.55294, 0.18431, 0.00000,
+0.56471, 0.18824, 0.00000,
+0.57647, 0.19216, 0.00000,
+0.58824, 0.19608, 0.00000,
+0.60000, 0.20000, 0.00000,
+0.61176, 0.20392, 0.00000,
+0.62353, 0.20784, 0.00000,
+0.63529, 0.21176, 0.00000,
+0.64706, 0.21569, 0.00000,
+0.65882, 0.21961, 0.00000,
+0.67059, 0.22353, 0.00000,
+0.68235, 0.22745, 0.00000,
+0.69412, 0.23137, 0.00000,
+0.70588, 0.23529, 0.00000,
+0.71765, 0.23922, 0.00000,
+0.72941, 0.24314, 0.00000,
+0.74118, 0.24706, 0.00000,
+0.75294, 0.25098, 0.00000,
+0.76471, 0.25490, 0.00000,
+0.77647, 0.25882, 0.00000,
+0.78824, 0.26275, 0.00000,
+0.80000, 0.26667, 0.00000,
+0.81176, 0.27059, 0.00000,
+0.82353, 0.27451, 0.00000,
+0.83529, 0.27843, 0.00000,
+0.84706, 0.28235, 0.00000,
+0.85882, 0.28627, 0.00000,
+0.87059, 0.29020, 0.00000,
+0.88235, 0.29412, 0.00000,
+0.89412, 0.29804, 0.00000,
+0.90588, 0.30196, 0.00000,
+0.91765, 0.30588, 0.00000,
+0.92941, 0.30980, 0.00000,
+0.94118, 0.31373, 0.00000,
+0.95294, 0.31765, 0.00000,
+0.96471, 0.32157, 0.00000,
+0.97647, 0.32549, 0.00000,
+0.98824, 0.32941, 0.00000,
+1.00000, 0.33333, 0.00000,
+1.00000, 0.33725, 0.00000,
+1.00000, 0.34118, 0.00000,
+1.00000, 0.34510, 0.00000,
+1.00000, 0.34902, 0.00000,
+1.00000, 0.35294, 0.00000,
+1.00000, 0.35686, 0.00000,
+1.00000, 0.36078, 0.00000,
+1.00000, 0.36471, 0.00000,
+1.00000, 0.36863, 0.00000,
+1.00000, 0.37255, 0.00000,
+1.00000, 0.37647, 0.00000,
+1.00000, 0.38039, 0.00000,
+1.00000, 0.38431, 0.00000,
+1.00000, 0.38824, 0.00000,
+1.00000, 0.39216, 0.00000,
+1.00000, 0.39608, 0.00000,
+1.00000, 0.40000, 0.00000,
+1.00000, 0.40392, 0.00000,
+1.00000, 0.40784, 0.00000,
+1.00000, 0.41176, 0.00000,
+1.00000, 0.41569, 0.00000,
+1.00000, 0.41961, 0.00000,
+1.00000, 0.42353, 0.00000,
+1.00000, 0.42745, 0.00000,
+1.00000, 0.43137, 0.00000,
+1.00000, 0.43529, 0.00000,
+1.00000, 0.43922, 0.00000,
+1.00000, 0.44314, 0.00000,
+1.00000, 0.44706, 0.00000,
+1.00000, 0.45098, 0.00000,
+1.00000, 0.45490, 0.00000,
+1.00000, 0.45882, 0.00000,
+1.00000, 0.46275, 0.00000,
+1.00000, 0.46667, 0.00000,
+1.00000, 0.47059, 0.00000,
+1.00000, 0.47451, 0.00000,
+1.00000, 0.47843, 0.00000,
+1.00000, 0.48235, 0.00000,
+1.00000, 0.48627, 0.00000,
+1.00000, 0.49020, 0.00000,
+1.00000, 0.49412, 0.00000,
+1.00000, 0.49804, 0.00000,
+1.00000, 0.50196, 0.00000,
+1.00000, 0.50588, 0.00000,
+1.00000, 0.50980, 0.00000,
+1.00000, 0.51373, 0.00000,
+1.00000, 0.51765, 0.00000,
+1.00000, 0.52157, 0.00000,
+1.00000, 0.52549, 0.00000,
+1.00000, 0.52941, 0.00000,
+1.00000, 0.53333, 0.00000,
+1.00000, 0.53725, 0.00000,
+1.00000, 0.54118, 0.00000,
+1.00000, 0.54510, 0.00000,
+1.00000, 0.54902, 0.00000,
+1.00000, 0.55294, 0.00000,
+1.00000, 0.55686, 0.00000,
+1.00000, 0.56078, 0.00000,
+1.00000, 0.56471, 0.00000,
+1.00000, 0.56863, 0.00000,
+1.00000, 0.57255, 0.00000,
+1.00000, 0.57647, 0.00000,
+1.00000, 0.58039, 0.00000,
+1.00000, 0.58431, 0.00000,
+1.00000, 0.58824, 0.00000,
+1.00000, 0.59216, 0.00000,
+1.00000, 0.59608, 0.00000,
+1.00000, 0.60000, 0.00000,
+1.00000, 0.60392, 0.00000,
+1.00000, 0.60784, 0.00000,
+1.00000, 0.61176, 0.00000,
+1.00000, 0.61569, 0.00000,
+1.00000, 0.61961, 0.00000,
+1.00000, 0.62353, 0.00000,
+1.00000, 0.62745, 0.00000,
+1.00000, 0.63137, 0.00000,
+1.00000, 0.63529, 0.00000,
+1.00000, 0.63922, 0.00000,
+1.00000, 0.64314, 0.00000,
+1.00000, 0.64706, 0.00000,
+1.00000, 0.65098, 0.01176,
+1.00000, 0.65490, 0.02353,
+1.00000, 0.65882, 0.03529,
+1.00000, 0.66275, 0.04706,
+1.00000, 0.66667, 0.05882,
+1.00000, 0.67059, 0.07059,
+1.00000, 0.67451, 0.08235,
+1.00000, 0.67843, 0.09412,
+1.00000, 0.68235, 0.10588,
+1.00000, 0.68627, 0.11765,
+1.00000, 0.69020, 0.12941,
+1.00000, 0.69412, 0.14118,
+1.00000, 0.69804, 0.15294,
+1.00000, 0.70196, 0.16471,
+1.00000, 0.70588, 0.17647,
+1.00000, 0.70980, 0.18824,
+1.00000, 0.71373, 0.20000,
+1.00000, 0.71765, 0.21176,
+1.00000, 0.72157, 0.22353,
+1.00000, 0.72549, 0.23529,
+1.00000, 0.72941, 0.24706,
+1.00000, 0.73333, 0.25882,
+1.00000, 0.73725, 0.27059,
+1.00000, 0.74118, 0.28235,
+1.00000, 0.74510, 0.29412,
+1.00000, 0.74902, 0.30588,
+1.00000, 0.75294, 0.31765,
+1.00000, 0.75686, 0.32941,
+1.00000, 0.76078, 0.34118,
+1.00000, 0.76471, 0.35294,
+1.00000, 0.76863, 0.36471,
+1.00000, 0.77255, 0.37647,
+1.00000, 0.77647, 0.38824,
+1.00000, 0.78039, 0.40000,
+1.00000, 0.78431, 0.41176,
+1.00000, 0.78824, 0.42353,
+1.00000, 0.79216, 0.43529,
+1.00000, 0.79608, 0.44706,
+1.00000, 0.80000, 0.45882,
+1.00000, 0.80392, 0.47059,
+1.00000, 0.80784, 0.48235,
+1.00000, 0.81176, 0.49412,
+1.00000, 0.81569, 0.50588,
+1.00000, 0.81961, 0.51765,
+1.00000, 0.82353, 0.52941,
+1.00000, 0.82745, 0.54118,
+1.00000, 0.83137, 0.55294,
+1.00000, 0.83529, 0.56471,
+1.00000, 0.83922, 0.57647,
+1.00000, 0.84314, 0.58824,
+1.00000, 0.84706, 0.60000,
+1.00000, 0.85098, 0.61176,
+1.00000, 0.85490, 0.62353,
+1.00000, 0.85882, 0.63529,
+1.00000, 0.86275, 0.64706,
+1.00000, 0.86667, 0.65882,
+1.00000, 0.87059, 0.67059,
+1.00000, 0.87451, 0.68235,
+1.00000, 0.87843, 0.69412,
+1.00000, 0.88235, 0.70588,
+1.00000, 0.88627, 0.71765,
+1.00000, 0.89020, 0.72941,
+1.00000, 0.89412, 0.74118,
+1.00000, 0.89804, 0.75294,
+1.00000, 0.90196, 0.76471,
+1.00000, 0.90588, 0.77647,
+1.00000, 0.90980, 0.78824,
+1.00000, 0.91373, 0.80000,
+1.00000, 0.91765, 0.81176,
+1.00000, 0.92157, 0.82353,
+1.00000, 0.92549, 0.83529,
+1.00000, 0.92941, 0.84706,
+1.00000, 0.93333, 0.85882,
+1.00000, 0.93725, 0.87059,
+1.00000, 0.94118, 0.88235,
+1.00000, 0.94510, 0.89412,
+1.00000, 0.94902, 0.90588,
+1.00000, 0.95294, 0.91765,
+1.00000, 0.95686, 0.92941,
+1.00000, 0.96078, 0.94118,
+1.00000, 0.96471, 0.95294,
+1.00000, 0.96863, 0.96471,
+1.00000, 0.97255, 0.97647,
+1.00000, 0.97647, 0.98824,
+1.00000, 0.98039, 1.00000,
+1.00000, 0.98431, 1.00000,
+1.00000, 0.98824, 1.00000,
+1.00000, 0.99216, 1.00000,
+1.00000, 0.99608, 1.00000,
+1.00000, 1.00000, 1.00000
diff --git a/unix/sun/imtool.c b/unix/sun/imtool.c
new file mode 100644
index 00000000..6c055610
--- /dev/null
+++ b/unix/sun/imtool.c
@@ -0,0 +1,4488 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <suntool/sunview.h>
+#include <suntool/canvas.h>
+#include <suntool/panel.h>
+#include <suntool/walkmenu.h>
+#include <suntool/tool_struct.h>
+#include <sunwindow/win_cursor.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include "imtool.h"
+
+/*
+ * IMTOOL -- Software image display for the Sun workstation under Sunview.
+ * A variable sized window is used to peer into a "fixed" size frame buffer
+ * at an arbitrary offset (pan) and scale (zoom/dezoom). The number of frames
+ * and the size of a frame buffer is arbitrary and is a setup option. The
+ * depth of a frame on an 8 bit display is 7 bits with a 1 bit graphics
+ * overlay plane; this leaves almost half of the color table space for use
+ * by other windows. A data stream command interface is provided for software
+ * control of the display server by other processes, not necessarily on the
+ * same node.
+ *
+ * D.Tody, April 1987 (NOAO/IRAF project)
+ * Extensively revised and extended December 1987.
+ * Zoom and further datastream mods (WCS) added May 1989.
+ * (but NeWS/X is coming soon and probably none of this will work anymore...)
+ */
+
+#ifndef abs
+#define abs(a) (((a)<0)?(-(a)):(a))
+#endif
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) ((a)<(b)?(b):(a))
+#endif
+
+/* Default values, size limiting values.
+ */
+#define MAX_ARGS 50 /* max command line arguments */
+#define MAX_FBCONFIG 128 /* max possible frame buf sizes */
+#define MAX_FRAMES 16 /* max number of frames */
+#define FRAME_CHOICES "1","2","3","4" /* nframes choices for setup */
+#define NGREY 256 /* max physical greylevels */
+#define CLOCK_INTERVAL 500 /* main clock tick, msec */
+#define DEF_NCONFIG 1 /* number of f.b. configs */
+#define DEF_NFRAMES 1 /* save memory; only one frame */
+#define DEF_FRAME_WIDTH 512 /* 512 square frame */
+#define DEF_FRAME_HEIGHT 512 /* 512 square frame */
+#define DEF_GIOWIN_SIZE 512 /* default size gio window */
+#define SZ_LABEL 256 /* main frame label string */
+#define SZ_IMTITLE 128 /* image title string */
+#define SZ_WCTEXT 80 /* WCS box text */
+#define SZ_WCSBUF 320 /* WCS text buffer size */
+#define SZ_PANTEXT 18 /* displayed chars in o_fname */
+#define SZ_COLORBAR 11 /* height of colorbar in pixels */
+#define SZ_FNAME 128
+#define SZ_LINE 256
+#define IO_TIMEOUT 30 /* i/o not getting anywhere */
+#define SZ_FIFOBUF 4000 /* transfer size for FIFO i/o */
+#define BKG_WHITE 0
+#define BKG_BLACK 1
+#define P_IMAGE 1
+#define P_GRAPHICS 2
+#define P_COLORBAR 4
+#define P_DONTCLIP 8
+#define PANNER_EVENT 99
+#define INTERRUPT 003
+#define NEXT_SCREEN 006
+#define PREV_SCREEN 022
+#define CYCLE_BLINK 002
+
+/* Reserved colormap entries.
+ */
+#define CMS_DATASTART 1 /* first data greylevel */
+#define CMS_DATAEND 200 /* last data greylevel */
+#define CMS_DATARANGE 200 /* number of data greylevels */
+#define CMS_CURSOR 201 /* cursor color table entry */
+#define CMS_BACKGROUND 202 /* background color */
+#define CMS_GRAPHICSSTART 203 /* first graphics greylevel */
+#define CMS_GRAPHICSEND 217 /* last graphics greylevel */
+#define CMS_GRAPHICSRANGE 15 /* number of graphics greylevel */
+#define CMS_GCOLOR(c) (203+(c)) /* graphics color table entries */
+#define CMS_FIRST 1 /* first grey level used */
+#define CMS_NGREY 218 /* total number of grey levels */
+
+/* Magic numbers. */
+#define BLINK_OFF 1 /* crosshair cursor off */
+#define BLINK_ON 2 /* turn crosshair cursor on */
+#define CMMAPNAME "imtool" /* color map segment name */
+#define OLD_DEVNAME "/dev/imt1" /* original pseudodevice name */
+#define I_DEVNAME "/dev/imt1o" /* pseudo device names */
+#define O_DEVNAME "/dev/imt1i" /* our IN is client's OUT */
+#define KEY_SETUP KEY_TOP(4) /* togglt the setup panel */
+#define KEY_REMARK KEY_TOP(5) /* remark list of objects */
+#define KEY_PCOORDS KEY_TOP(6) /* cont. coords display */
+#define KEY_SNAP KEY_TOP(7) /* image hardcopy key */
+#define TEXT_FONT "/usr/lib/fonts/fixedwidthfonts/screen.b.14"
+#define MARK_FONT "/usr/lib/fonts/fixedwidthfonts/screen.r.11"
+#define COORDFILE "frame.%d.%d"
+#define FBCONFIG_1 ".imtoolrc"
+#define FBCONFIG_2 "/usr/local/lib/imtoolrc"
+#define FBCONFIG_ENV1 "imtoolrc"
+#define FBCONFIG_ENV2 "IMTOOLRC"
+
+#define R_TYPE 0 /* 0=postscript, 1=rasterfile */
+#define R_DISPOSE "lpr -s %s" /* dispose command */
+#define R_FILENAME "" /* output filename */
+
+/* Global state codes. */
+#define TRACK_CURSOR 0
+#define WINDOW 1
+#define ROAM 2
+
+#define MONO 0
+#define HEAT 1
+#define RAMP1 2
+#define RAMP2 3
+#define HALLEY 4
+#define LINEARPS 5
+#define RANDOMPS 6
+#define CRANDOMPS 7
+#define ULUT1 8
+#define ULUT2 9
+
+/* WCS definitions. */
+#define W_UNITARY 0
+#define W_LINEAR 1
+#define W_LOG 2
+#define W_DEFFORMAT " %7.2f %7.2f %7.1f%c"
+
+/* Internal variables. */
+static int snap_frame_too = 1; /* include frame in imcopy? */
+static int cursor_blink = -1; /* crosshair or small cursor */
+static int cursor_show = -1; /* cursor state; on/off */
+static int display_coords = 0; /* cont. display cursor coords */
+static int p_cursor_setback = 0; /* restore cursor pos after pan */
+static int panning = 0; /* smooth image pan in progress */
+static int maptype = MONO; /* initial mapping type */
+static char o_fname[SZ_FNAME]; /* name of coord output file */
+static char o_revtext=1; /* reverse video text? */
+static int setup_xoff = 4; /* offset to setup panel */
+static int setup_yoff = 18; /* " " */
+static int wc_xoff=0, wc_yoff=0; /* offset to WCS output box */
+static int wc_width, wc_height; /* size of WCS output box */
+static char wc_text[SZ_WCTEXT+1]; /* coordinate text */
+static struct pixfont *wc_font = NULL; /* WCS box font */
+static int cb_height = SZ_COLORBAR;
+static int show_colorbar = 1;
+static int black = 0;
+static int white = NGREY - 1;
+static int background = 0; /* background color index */
+static int state = TRACK_CURSOR; /* mouse button state */
+static int last_sx, last_sy; /* window x,y of last event */
+static int save_sx, save_sy; /* save absolute mouse position */
+static int last_bx, last_by; /* last button press */
+static int fb_bkgcolor_index = BKG_WHITE;
+static int window_open = 1;
+static int global_colortable = 1; /* globally manage colortable */
+static int fbconfig = 0;
+static int last_x, last_y, last_key, key_left=0;
+static int reading_imcursor = 0;
+static int imcursor_wcs;
+static char wcsbuf[MAX_FRAMES][SZ_WCSBUF];
+
+#define CRANDOM_CHOICES "1", "2", "4", "8", "16", "32"
+static int cr_msec[] = { 1000, 2000, 4000, 8000, 16000, 32000 };
+static int crandom_blink_rate = 1; /* rate for cont. random pseudo */
+
+/* Blink parameters. */
+#define BLINKRATE_CHOICES "1/2","1","2","4","8","16","32"
+static int blink_rate[] = { 1, 2, 4, 8, 16, 32, 64 };
+static int blink_rate_index = 1; /* current blink rate */
+static int blink_timer = 0; /* nticks left on clock */
+static int blink_frame = 0; /* current blink frame */
+static int blink = 0; /* blink enabled? */
+static int n_blink_frames = 8; /* number of frames to blink */
+static int blink_frames[MAX_FRAMES] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+static char s_blinklist[SZ_FNAME+1] = "all"; /* list of frames to blink */
+
+/* Logical image cursor parameters. */
+static int marktype = 0; /* mark cursor position */
+
+/* The following is needed to provide sufficient precision to allow for
+ * zooming virtual pixrects (Rects use type short).
+ */
+typedef struct bigrect {
+ int r_left, r_top;
+ int r_width, r_height;
+} BRect;
+
+/* Predefined lookup tables. */
+struct triplet {
+ float red, green, blue;
+};
+struct lut {
+ int lutlen;
+ struct triplet hue[NGREY];
+};
+
+struct lut heat = {
+#include "heat.lut"
+};
+
+struct lut halley = {
+#include "halley.lut"
+};
+
+/* User defined lookup tables. */
+static char u_lut1[SZ_FNAME+1] = "none";
+static char u_lut2[SZ_FNAME+1] = "none";
+
+static BRect pw_rect; /* raw frame buffer */
+static int fb_depth = 8;
+static int fb_ngrey = CMS_DATARANGE;
+static int fb_nframes= DEF_NFRAMES;
+static int Fb_width = DEF_FRAME_WIDTH;
+static int Fb_height = DEF_FRAME_HEIGHT;
+
+static BRect fb_rect; /* zoomed frame buffer */
+static int fb_width = DEF_FRAME_WIDTH;
+static int fb_height = DEF_FRAME_HEIGHT;
+
+#define MAX_ZOOMS 8
+#define ZOOM_CHOICES "1","2","4","8"
+static int zoom = 1;
+static int nzooms = 4; /* number of zoom factors */
+static int zoom_index = 0;
+static int zooms[MAX_ZOOMS] = { 1, 2, 4, 8 };
+static char s_zoomslist[SZ_FNAME+1] = "1 2 4 8";
+
+static int gio_xsize = DEF_GIOWIN_SIZE;
+static int gio_ysize = DEF_GIOWIN_SIZE;
+static int initial_gio_xsize, initial_gio_ysize;
+
+/* Rotation matrix defining world coordinate system (WCS) of a frame.
+ */
+struct ctran {
+ int valid; /* has WCS been set? */
+ float a, b; /* x, y scale factors */
+ float c, d; /* x, y cross factors */
+ float tx, ty; /* x, y translation */
+ float z1, z2; /* greyscale range */
+ int zt; /* greyscale mapping */
+ char format[32]; /* wcs output format */
+ char imtitle[SZ_IMTITLE+1]; /* image title from WCS */
+};
+
+/* The frame buffers. */
+struct framebuf {
+ struct pixrect *fb_pr; /* frame buffer pixrect */
+ int fb_frameno; /* frame number */
+ int fb_xoff, fb_yoff; /* fb coords of pixwin (0,0) */
+ int fb_xzoom, fb_yzoom; /* zoom/dezoom factors */
+ int fb_objno; /* object number */
+ int fb_imageno; /* displayed frame counter */
+ int fb_maptype; /* greyscale transformation */
+ float fb_center, fb_slope; /* transfer function */
+ struct ctran fb_ctran; /* world coordinate system */
+ char fb_label[SZ_LABEL+1]; /* frame label string */
+};
+
+/* Possible frame buffer sizes. */
+struct fbconfig {
+ int nframes; /* number of frames */
+ int width; /* frame buffer width */
+ int height; /* frame buffer height */
+};
+
+static int display_frame; /* currently displayed frame */
+static int reference_frame; /* reference (cmd i/o) frame */
+static struct framebuf *frames=NULL; /* array of frame descriptors */
+static struct framebuf *df_p; /* display frame descriptor */
+static struct framebuf *rf_p; /* reference frame descriptor */
+static struct pixrect *cb_pr; /* colorbar pixrect */
+static int fb_nconfig = 0;
+static int fb_config_index = 0;
+static struct fbconfig fb_config[MAX_FBCONFIG];
+static char startfile[SZ_FNAME+1]; /* image read on startup */
+static char rasterfile[SZ_FNAME+1] = "raster.%d.%d";
+
+/* Screendump stuff. */
+int r_type = R_TYPE;
+char r_dispose[SZ_FNAME+1] = R_DISPOSE;
+char r_filename[SZ_FNAME+1] = R_FILENAME;
+
+#define HEIGHTADJUST \
+ (tool_headerheight((int)window_get(gio_frame, FRAME_SHOW_LABEL)) + \
+ TOOL_BORDERWIDTH)
+
+static short iconimage[] = {
+#include "imtool.icon"
+};
+DEFINE_ICON_FROM_IMAGE (icon, iconimage);
+
+Window gio_frame, gio_canvas;
+static int gio_frame_fd;
+static int datain, dataout;
+static Menu_item blink_item;
+static Panel setup_panel;
+static Window setup_frame;
+static Panel_item pan_show_colorbar, pan_globalcolor;
+static Panel_item pan_set_nframes, pan_blink_rate, pan_blink_list;
+static Panel_item pan_set_maptype, pan_crandom_rate, pan_set_ofname;
+static Panel_item pan_snapframetoo, pan_set_background, pan_set_rfname;
+static Panel_item pan_set_rtype, pan_set_rdispose, pan_set_rfilename;
+static Panel_item pan_set_marker, pan_zoom_list;
+static Panel_item pan_set_ulut1, pan_set_ulut2;
+static unsigned char red[NGREY], blue[NGREY], green[NGREY];
+static unsigned char m_red[NGREY], m_blue[NGREY], m_green[NGREY];
+
+static struct pixwin *gio_pw;
+static int main_argc, gio_argc;
+static char **main_argv, *gio_argv[MAX_ARGS];
+
+static Notify_value ev_gioframe(), set_colortable();
+static Notify_value ev_gioinput(), ev_cmdinput(), ev_panner();
+static char *getfname(), *framelabel();
+static struct ctran *wcs_update();
+static struct pixrect *get_screen_rect();
+static refresh_display();
+extern char *getenv();
+
+
+/* IMTOOL_MAIN -- Create the Imtool windows, i.e., the main display window,
+ * the region of interest or "cursor" subwindow, and the setup panel. There
+ * are two principal event handlers, the display window event handler, used
+ * to process cursor input and mouse button commands, and the command input
+ * event handler, used to communicate with the client process.
+ */
+#ifdef STANDALONE
+main (argc, argv)
+#else
+imtool_main (argc, argv)
+#endif
+int argc;
+char **argv;
+{
+ char *s;
+
+ main_argc = argc;
+ main_argv = argv;
+ parse_args (argc, argv, &gio_argc, gio_argv);
+
+ /* Screendump stuff. */
+ if (s = getenv ("R_DISPOSE"))
+ strcpy (r_dispose, s);
+ if (s = getenv ("R_FILENAME"))
+ strcpy (r_filename, s);
+ if (s = getenv ("R_RASTERFILE")) {
+ strcpy (r_filename, s);
+ r_type = 1;
+ }
+
+ gio_frame = window_create (NULL, FRAME,
+ FRAME_ICON, &icon,
+ FRAME_LABEL,
+ "imtool - NOAO/IRAF Sunview Image Display V1.1",
+ FRAME_ARGS, gio_argc, gio_argv,
+ FRAME_NO_CONFIRM, FALSE,
+ 0);
+ if (gio_frame == NULL)
+ exit (1);
+
+ gio_frame_fd = (int) window_get (gio_frame, WIN_FD);
+ parse_args (argc, argv, &gio_argc, gio_argv);
+ create_gio_canvas (gio_argc, gio_argv);
+ create_frame_menu (gio_frame);
+ create_setup_popup();
+ notify_interpose_event_func (gio_frame, ev_gioframe, NOTIFY_SAFE);
+ notify_interpose_event_func (gio_canvas, ev_gioinput, NOTIFY_SAFE);
+
+ get_fbconfig();
+ set_fbconfig (fbconfig, 0);
+ load_testpattern (0);
+ set_colortable();
+ gio_setcursor (CURSOR_ON, BLINK_OFF);
+ set_transfer_function (gio_pw, df_p->fb_center, df_p->fb_slope);
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+ init_colorbar (pw_rect.r_width);
+
+ if (startfile[0])
+ load_raster (rf_p->fb_pr, startfile);
+
+ initial_gio_xsize = gio_xsize;
+ initial_gio_ysize = gio_ysize;
+
+ /* Open the output fifo. We have to open it ourselves first as a
+ * client to get around the fifo open-no-client error.
+ */
+ if ((datain = open (O_DEVNAME, O_RDONLY|O_NDELAY)) != -1) {
+ if ((dataout = open (O_DEVNAME, O_WRONLY|O_NDELAY)) != -1)
+ fcntl (dataout, F_SETFL, O_WRONLY);
+ close (datain);
+ }
+
+ /* Open the input stream, a FIFO pseudodevice file used by
+ * applications to send us commands and data.
+ */
+ if ((datain = open (I_DEVNAME, O_RDONLY|O_NDELAY)) == -1) {
+ if ((datain = open (OLD_DEVNAME, O_RDONLY|O_NDELAY)) == -1)
+ fprintf (stderr, "Warning: cannot open %s\n", I_DEVNAME);
+ } else {
+ /* Clear O_NDELAY for reading. */
+ fcntl (datain, F_SETFL, O_RDONLY);
+ notify_set_input_func (gio_frame, ev_cmdinput, datain);
+ }
+
+ imtool_clock();
+ window_main_loop (gio_frame);
+ close (datain);
+ exit (0);
+}
+
+
+/* PARSE_ARGS -- Parse the argument list into the arguments for the main frame
+ * and the global arguments for the server.
+ */
+static
+parse_args (argc, argv, gio_argc, gio_argv)
+int argc;
+char *argv[];
+int *gio_argc;
+char *gio_argv[];
+{
+ register char *argp;
+ register int arg;
+ static int ncalls = 0;
+
+ gio_argv[0] = argv[0];
+ *gio_argc = 1;
+
+ for (arg=1; arg <= argc && (argp = argv[arg]) != NULL; arg++) {
+ if (strncmp (argp, "-fbconfig", 3) == 0) {
+ /* Set the frame buffer configuration. */
+ if ((argp = argv[arg+1]) && isdigit(*argp)) {
+ fbconfig = max (0, atoi(argp) - 1);
+ arg++;
+ } else if (ncalls == 0)
+ fprintf (stderr, "-fbconfig argument missing\n");
+ } else if (strncmp (argp, "-raster", 4) == 0) {
+ if ((argp = argv[arg+1])) {
+ strcpy (startfile, argp);
+ arg++;
+ }
+ } else
+ gio_argv[(*gio_argc)++] = argp;
+ }
+
+ gio_argv[(*gio_argc)] = NULL;
+ ncalls++;
+}
+
+
+/* GET_FBCONFIG -- Read the IMTOOL startup file to get the set of possible
+ * frame buffer sizes.
+ *
+ * File format: configno nframes width height [extra fields]
+ * e.g., 1 2 512 512
+ * 2 2 800 800
+ * 3 1 1024 1024 # comment
+ */
+static
+get_fbconfig()
+{
+ register char *ip;
+ register FILE *fp;
+ int config, nframes, width, height, i;
+ char lbuf[SZ_LINE+1], *fname;
+
+ /* Initialize the config table. */
+ for (i=0; i < MAX_FBCONFIG; i++)
+ fb_config[i].nframes = 0;
+
+ /* Attempt to open the config file. */
+ fp = NULL;
+ if ((fname=getenv(FBCONFIG_ENV1)) || (fname=getenv(FBCONFIG_ENV2)))
+ fp = fopen (fname, "r");
+ if (!fp && (fname = getenv ("HOME"))) {
+ sprintf (lbuf, "%s/%s", fname, FBCONFIG_1);
+ fp = fopen (fname = lbuf, "r");
+ }
+ if (!fp)
+ fp = fopen (fname = FBCONFIG_2, "r");
+
+ /* If cannot find a config file, set up the default configuration. */
+ if (!fp) {
+ fb_config_index = fbconfig = 0;
+ fb_nconfig = DEF_NCONFIG;
+ fb_config[0].nframes = DEF_NFRAMES;
+ fb_config[0].width = DEF_FRAME_WIDTH;
+ fb_config[0].height = DEF_FRAME_HEIGHT;
+ return;
+ }
+
+ /* Scan the frame buffer configuration file.
+ */
+ fb_nconfig = 0;
+ while (fgets (lbuf, SZ_LINE, fp) != NULL) {
+ /* Skip comment lines and blank lines. */
+ for (ip=lbuf; *ip == ' ' || *ip == '\t'; ip++)
+ ;
+ if (*ip == '\n' || *ip == '#')
+ continue;
+ if (!isdigit (*ip))
+ continue;
+ switch (sscanf (ip, "%d%d%d%d", &config,&nframes,&width,&height)) {
+ case 4:
+ break; /* normal case */
+ case 3:
+ height = width; /* default to square format */
+ break;
+ default:
+ fprintf (stderr, "imtool: bad config `%s'\n", ip);
+ continue;
+ }
+
+ nframes = max (1, nframes);
+ width = max (1, width);
+ height = max (1, height);
+
+ /* Since the frame buffer is stored in a memory pixrect
+ * (effectively), the line length should be an integral number
+ * of 16 bit words.
+ */
+ if (width & 1) {
+ fprintf (stderr, "imtool warning: fb config %d [%d-%dx%d] - ",
+ config, nframes, width, height);
+ fprintf (stderr, "frame width should be even, reset to %d\n",
+ --width);
+ }
+
+ config = max(1, min(MAX_FBCONFIG, config)) - 1;
+ fb_config[config].nframes = nframes;
+ fb_config[config].width = width;
+ fb_config[config].height = height;
+ fb_nconfig = max (fb_nconfig, config+1);
+ }
+
+ fclose (fp);
+}
+
+
+/* SET_FBCONFIG -- Setup a frame buffer configuration.
+ */
+static
+set_fbconfig (config, nframes)
+int config;
+int nframes;
+{
+ register struct pixrect *pr = get_screen_rect();
+ int old_config = fb_config_index;
+ int old_nframes = fb_nframes;
+ char text[SZ_LINE];
+
+ if (config < 0 || config >= fb_nconfig) {
+ fprintf (stderr,
+ "imtool: no such frame buffer configuration - %d\n", config+1);
+ return;
+ } else if (nframes <= 0)
+ nframes = fb_config[config].nframes;
+
+ if (init_framebuffers (config, nframes) == -1) {
+ fprintf (stderr, "restore configuration %d\n", old_config + 1);
+ if (init_framebuffers (old_config, old_nframes) == -1) {
+ fprintf (stderr,
+ "cannot restore frame buffer configuration - imtool dies\n");
+ exit (1);
+ }
+ }
+
+ if (gio_xsize <= 0 || gio_xsize > Fb_width)
+ gio_xsize = Fb_width;
+ if (gio_ysize <= 0 || gio_ysize > (Fb_height + cb_height))
+ gio_ysize = Fb_height + cb_height;
+
+ gio_xsize = min (pr->pr_width - TOOL_BORDERWIDTH * 2, gio_xsize);
+ gio_ysize = min (pr->pr_height
+ - tool_headerheight ((int)window_get(gio_frame,FRAME_SHOW_LABEL))
+ - TOOL_BORDERWIDTH, gio_ysize);
+
+ window_set (gio_canvas,
+ WIN_WIDTH, gio_xsize,
+ WIN_HEIGHT, gio_ysize,
+ 0);
+
+ window_fit (gio_canvas);
+ window_fit (gio_frame);
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+
+ sprintf (text, "Frame buffer configuration %d: %d %dx%d frame%c",
+ fb_config_index + 1, fb_nframes, Fb_width, Fb_height,
+ fb_nframes > 1 ? 's' : ' ');
+
+ window_set (gio_frame, FRAME_LABEL, text, 0);
+ panel_set_value (pan_set_maptype, df_p->fb_maptype);
+ panel_set_value (pan_set_nframes, fb_nframes - 1);
+}
+
+
+/* LOAD_RASTER -- Load a rasterfile into the display.
+ */
+static
+load_raster (o_pr, fname)
+Pixrect *o_pr;
+char *fname;
+{
+ FILE *fp;
+ Pixrect *i_pr;
+ int width, height;
+
+ if ((fp = fopen (fname, "r")) == NULL)
+ fprintf (stderr, "cannot open rasterfile %s\n", fname);
+ else {
+ if (i_pr = pr_load (fp, NULL)) {
+ width = min (Fb_width, i_pr->pr_width);
+ height = min (Fb_height, i_pr->pr_height);
+ pr_rop (o_pr, 0, 0, width, height, PIX_SRC, i_pr, 0, 0);
+ pr_close (i_pr);
+ repaint (P_IMAGE|P_COLORBAR|P_GRAPHICS);
+ }
+ fclose (fp);
+ }
+}
+
+
+/* SAVE_RASTER -- Save a frame buffer in a file.
+ */
+static
+save_raster (i_pr, fname)
+Pixrect *i_pr;
+char *fname;
+{
+ FILE *fp;
+
+ if ((fp = fopen (fname, "w")) == NULL)
+ fprintf (stderr, "cannot create rasterfile %s\n", fname);
+ else {
+ if (pr_dump (i_pr, fp, RMT_NONE, RT_STANDARD, 0) == PIX_ERR)
+ fprintf (stderr, "error writing rasterfile %s\n", fname);
+ fclose (fp);
+ }
+}
+
+
+/* CREATE_GIO_CANVAS -- Set up the canvas for the main display window.
+ */
+static
+create_gio_canvas (argc, argv)
+int argc;
+char **argv;
+{
+ register char *argp;
+ register int arg;
+ char pathname[NGREY];
+ struct pixwin *pw;
+ int name;
+
+ /* Override the builtin defaults with the values given by the user
+ * on the command line, if any.
+ */
+ for (arg=1; arg <= argc && (argp = argv[arg]) != NULL; arg++) {
+ if (!strcmp (argp, "-Ws") || !strncmp (argp, "-size", 5)) {
+ gio_xsize = atoi (argv[++arg]) - (TOOL_BORDERWIDTH * 2);
+ gio_ysize = atoi (argv[++arg]) -
+ (tool_headerheight ((int) window_get (gio_frame,
+ FRAME_SHOW_LABEL)) + TOOL_BORDERWIDTH);
+ } else if (!strncmp (argp, "-maptype", 4)) {
+ argp = argv[++arg];
+ if (!strncmp (argp, "mono", 1))
+ maptype = MONO;
+ else if (!strncmp (argp, "heat", 1))
+ maptype = HEAT;
+ else if (!strncmp (argp, "ramp1", 1))
+ maptype = RAMP1;
+ else if (!strncmp (argp, "ramp2", 1))
+ maptype = RAMP2;
+ else if (!strncmp (argp, "halley", 1))
+ maptype = HALLEY;
+ else if (!strncmp (argp, "linear", 1))
+ maptype = LINEARPS;
+ else if (!strncmp (argp, "random", 1))
+ maptype = RANDOMPS;
+ else if (!strncmp (argp, "crandom", 1))
+ maptype = CRANDOMPS;
+ else if (!strncmp (argp, "ulut1", 1))
+ maptype = ULUT1;
+ else if (!strncmp (argp, "ulut2", 1))
+ maptype = ULUT2;
+ else
+ fprintf (stderr, "unknown arg `%s' to -maptype\n", argp);
+ } else if (!strncmp (argp, "-nocolorbar", 9)) {
+ cb_height = 0;
+ show_colorbar = 0;
+ } else if (!strncmp (argp, "-colorbar", 7)) {
+ cb_height = SZ_COLORBAR;
+ show_colorbar = 1;
+ } else if (!strncmp (argp, "-black", 3)) {
+ background = CMS_BACKGROUND;
+ fb_bkgcolor_index = BKG_BLACK;
+ } else if (!strncmp (argp, "-white", 3)) {
+ background = 0;
+ fb_bkgcolor_index = BKG_WHITE;
+
+ } else if (!strncmp (argp, "-rtype", 3)) {
+ argp = argv[++arg];
+ r_type = (argp[0] == 'r');
+ } else if (!strncmp (argp, "-rdispose", 3)) {
+ argp = argv[++arg];
+ strcpy (r_dispose, argp);
+ } else if (!strncmp (argp, "-rfilename", 3)) {
+ argp = argv[++arg];
+ strcpy (r_filename, argp);
+ } else if (!strncmp (argp, "-ulut1", 6)) {
+ argp = argv[++arg];
+ strcpy (u_lut1, argp);
+ } else if (!strncmp (argp, "-ulut2", 6)) {
+ argp = argv[++arg];
+ strcpy (u_lut2, argp);
+ }
+ }
+
+ /* Open display canvas. The display canvas is never retained at the
+ * pixwin level since we can easily refresh it from the frame buffer.
+ */
+ gio_canvas = window_create (gio_frame, CANVAS,
+ WIN_WIDTH, gio_xsize,
+ WIN_HEIGHT, gio_ysize,
+ CANVAS_RETAINED, FALSE,
+ CANVAS_AUTO_CLEAR, FALSE,
+ CANVAS_REPAINT_PROC, refresh_display,
+ 0);
+ if (gio_canvas == NULL)
+ exit (1);
+
+ /* Initialize the frame and lut parameters and the canvax pixwin
+ * color map.
+ */
+ pw = (struct pixwin *) window_get (gio_canvas, WIN_PIXWIN);
+ fb_depth = pw->pw_pixrect->pr_depth;
+ if (fb_depth < 8) {
+ fprintf (stderr, "imtool cannot be used on monochrome displays\n");
+ exit (1);
+ }
+
+ white = (1 << fb_depth) - 1;
+ if ((fb_ngrey = CMS_DATARANGE) > (white + 1))
+ fb_ngrey = 1;
+ init_colormap (pw);
+ init_colormap (gio_pw = canvas_pixwin (gio_canvas));
+
+ /* Set input event flags. */
+ window_set (gio_canvas,
+ WIN_CONSUME_PICK_EVENTS, WIN_NO_EVENTS,
+ WIN_MOUSE_BUTTONS, WIN_UP_EVENTS, LOC_DRAG, LOC_WINEXIT, 0,
+ WIN_CONSUME_KBD_EVENTS, WIN_NO_EVENTS,
+ WIN_ASCII_EVENTS, WIN_LEFT_KEYS, WIN_RIGHT_KEYS,
+ KEY_SETUP, KEY_REMARK, KEY_SNAP, KEY_PCOORDS, KBD_DONE, 0,
+ 0);
+
+ notify_set_event_func (ev_panner, ev_panner, NOTIFY_SAFE);
+}
+
+
+/* CREATE_SETUP_POPUP -- Create the popup menu used to set the terminal
+ * setup options.
+ */
+static
+create_setup_popup ()
+{
+ extern loadframe_proc(), saveframe_proc(), fitframe_proc();
+ extern reset_proc(), iclear_proc(), gclear_proc();
+ extern setup_proc(), toggle_graphics(), set_background();
+ extern setframe_proc(), blinkenable_proc(), register_proc();
+ static Panel_setting set_ofname(), set_rfname();
+ static Panel_setting set_rtype(), set_rdispose(), set_rfilename();
+ static Panel_setting set_ulut1(), set_ulut2();
+ static Panel_setting set_blinklist(), set_zoomslist();
+ static panel_set_item();
+
+ setup_frame = window_create (gio_frame, FRAME,
+ FRAME_NO_CONFIRM, TRUE,
+ WIN_X, setup_xoff,
+ WIN_Y, setup_yoff,
+ 0);
+ if (setup_frame == NULL)
+ exit (1);
+ setup_panel = window_create (setup_frame, PANEL, 0);
+ if (setup_panel == NULL)
+ exit (1);
+
+ panel_create_item (setup_panel, PANEL_MESSAGE,
+ PANEL_ITEM_X, ATTR_COL(10),
+ PANEL_ITEM_Y, ATTR_ROW(0),
+ PANEL_LABEL_STRING, "Image Display Setup and Control",
+ 0);
+
+ pan_set_nframes = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(1) + 5,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Number of frame buffers: ",
+ PANEL_CHOICE_STRINGS, FRAME_CHOICES, 0,
+ PANEL_VALUE, fb_nframes - 1,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_set_maptype = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(2) + 5,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Greyscale mapping: ",
+ PANEL_CHOICE_STRINGS, "Mono",
+ "ESO-Heat",
+ "Ramp1",
+ "Ramp2",
+ "Halley",
+ "Linear-pseudo",
+ "Random-pseudo",
+ "Crandom-pseudo",
+ "User 1",
+ "User 2",
+ 0,
+ PANEL_VALUE, maptype,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_set_ulut1 = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(3) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "User lookup table 1: ",
+ PANEL_VALUE, u_lut1,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_ulut1,
+ 0);
+
+ pan_set_ulut2 = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(4) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "User lookup table 2: ",
+ PANEL_VALUE, u_lut2,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_ulut2,
+ 0);
+
+ pan_globalcolor = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(5) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Globally manage colortable: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, global_colortable,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_crandom_rate = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(6) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Rate (sec) for crandom option: ",
+ PANEL_CHOICE_STRINGS, CRANDOM_CHOICES, 0,
+ PANEL_VALUE, crandom_blink_rate,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_set_background = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(7) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Background color: ",
+ PANEL_CHOICE_STRINGS, "white", "black", 0,
+ PANEL_VALUE, fb_bkgcolor_index,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_snapframetoo = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(8) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Include frame border in imcopy: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, snap_frame_too,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_show_colorbar = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(9) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Show colorbar: ",
+ PANEL_CHOICE_STRINGS, "No", "Yes", 0,
+ PANEL_VALUE, show_colorbar,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_set_marker = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(10) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Cursor marker: ",
+ PANEL_CHOICE_STRINGS, "None", "Circle", "Cross", "Square", 0,
+ PANEL_VALUE, marktype,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_blink_rate = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(11) + 8,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Blink rate (sec): ",
+ PANEL_CHOICE_STRINGS, BLINKRATE_CHOICES, 0,
+ PANEL_VALUE, blink_rate_index,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+ pan_blink_list = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(12) + 11,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Frames to be blinked: ",
+ PANEL_VALUE, s_blinklist,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_blinklist,
+ 0);
+
+ pan_zoom_list = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(13) + 11,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Zoom factors: ",
+ PANEL_VALUE, s_zoomslist,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_zoomslist,
+ 0);
+
+ strcpy (o_fname, COORDFILE);
+ pan_set_ofname = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(14) + 11,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Coordinate list output file: ",
+ PANEL_VALUE, o_fname,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_ofname,
+ 0);
+
+ pan_set_rfname = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(15) + 11,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Rasterfile name (load/save): ",
+ PANEL_VALUE, rasterfile,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_rfname,
+ 0);
+
+ pan_set_rtype = panel_create_item (setup_panel, PANEL_CYCLE,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(16) + 13,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Screendump output type: ",
+ PANEL_CHOICE_STRINGS, "postscript", "rasterfile", 0,
+ PANEL_VALUE, r_type,
+ PANEL_NOTIFY_PROC, panel_set_item,
+ 0);
+
+
+ pan_set_rdispose = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(17) + 13,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Screendump dispose command: ",
+ PANEL_VALUE, r_dispose,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_rdispose,
+ 0);
+
+ pan_set_rfilename = panel_create_item (setup_panel, PANEL_TEXT,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(18) + 11,
+ PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
+ PANEL_LABEL_STRING, "Screendump output file: ",
+ PANEL_VALUE, r_filename,
+ PANEL_VALUE_STORED_LENGTH, SZ_FNAME,
+ PANEL_VALUE_DISPLAY_LENGTH, SZ_PANTEXT,
+ PANEL_NOTIFY_PROC, set_rfilename,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(19) + 15,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel,"Register Frames",0,0),
+ PANEL_NOTIFY_PROC, register_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Fit Window", 0,0),
+ PANEL_NOTIFY_PROC, fitframe_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Blink", 0,0),
+ PANEL_NOTIFY_PROC, blinkenable_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Frame", 0,0),
+ PANEL_NOTIFY_PROC, setframe_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_ITEM_X, ATTR_COL(0),
+ PANEL_ITEM_Y, ATTR_ROW(20) + 15,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Reset", 0,0),
+ PANEL_NOTIFY_PROC, reset_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Iclear", 0,0),
+ PANEL_NOTIFY_PROC, iclear_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Gclear", 0,0),
+ PANEL_NOTIFY_PROC, gclear_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Load", 0,0),
+ PANEL_NOTIFY_PROC, loadframe_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "Save", 0,0),
+ PANEL_NOTIFY_PROC, saveframe_proc,
+ 0);
+
+ panel_create_item (setup_panel, PANEL_BUTTON,
+ PANEL_LABEL_IMAGE,
+ panel_button_image (setup_panel, "DISMISS", 0,0),
+ PANEL_NOTIFY_PROC, setup_proc,
+ 0);
+
+ window_fit (setup_panel);
+ window_fit (setup_frame);
+}
+
+
+/* PANEL_SET_ITEM -- Called when an item is seleted in the setup panel to
+ * set the associated global variable and possibly take some action.
+ */
+static
+panel_set_item (item, value)
+Panel_item item;
+int value;
+{
+ if (item == pan_set_nframes) {
+ set_fbconfig (fb_config_index, value + 1);
+ } else if (item == pan_set_maptype) {
+ df_p->fb_maptype = maptype = value;
+ set_colortable();
+ set_transfer_function (gio_pw, df_p->fb_center, df_p->fb_slope);
+ } else if (item == pan_crandom_rate) {
+ crandom_blink_rate = value;
+ } else if (item == pan_set_marker) {
+ marktype = value;
+ } else if (item == pan_blink_rate) {
+ blink_rate_index = value;
+ } else if (item == pan_set_rtype) {
+ r_type = value;
+ } else if (item == pan_globalcolor) {
+ global_colortable = value;
+
+ } else if (item == pan_set_background) {
+ /* Set the background color.
+ */
+ if (fb_bkgcolor_index != value) {
+ register unsigned char *fb, *op;
+ register int n = Fb_height * Fb_width;
+ fb = (unsigned char *) mpr_d(df_p->fb_pr)->md_image;
+
+ switch (fb_bkgcolor_index = value) {
+ case BKG_BLACK:
+ background = CMS_BACKGROUND;
+ for (op=fb; --n >= 0; op++)
+ if (!*op)
+ *op = background;
+ break;
+ case BKG_WHITE:
+ background = 0;
+ for (op=fb; --n >= 0; op++)
+ if (*op == CMS_BACKGROUND)
+ *op = 0;
+ break;
+ }
+ gclear_proc();
+ }
+
+ } else if (item == pan_show_colorbar) {
+ if (show_colorbar != value) {
+ struct pixrect *pr = get_screen_rect();
+
+ show_colorbar = value;
+ if (show_colorbar) {
+ cb_height = SZ_COLORBAR;
+ gio_ysize += cb_height;
+ gio_ysize = min (Fb_height + cb_height, gio_ysize);
+ } else {
+ cb_height = 0;
+ gio_ysize -= SZ_COLORBAR;
+ }
+
+ gio_xsize =
+ min (pr->pr_width - TOOL_BORDERWIDTH * 2, gio_xsize);
+ gio_ysize =
+ min (pr->pr_height -
+ tool_headerheight ((int) window_get (gio_frame,
+ FRAME_SHOW_LABEL)) - TOOL_BORDERWIDTH,
+ gio_ysize);
+
+ window_set (gio_canvas,
+ WIN_WIDTH,gio_xsize, WIN_HEIGHT,gio_ysize, 0);
+
+ window_fit (gio_canvas);
+ window_fit (gio_frame);
+ }
+
+ } else if (item == pan_snapframetoo)
+ snap_frame_too = value;
+}
+
+
+/* SET_ZOOMSLIST -- Set the list of zoom factors.
+ */
+static Panel_setting
+set_zoomslist (item, event)
+Panel_item item;
+Event *event;
+{
+ register char *ip;
+ register int i;
+
+ strcpy (s_zoomslist, (char *) panel_get_value (item));
+ for (ip=s_zoomslist; isspace (*ip); ip++)
+ ;
+
+ nzooms = 0;
+ while (*ip && isdigit (*ip)) {
+ zooms[nzooms++] = max (1, atoi(ip));
+ while (isdigit (*ip))
+ ip++;
+ while (isspace (*ip))
+ ip++;
+ if (nzooms >= MAX_ZOOMS)
+ break;
+ }
+
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_BLINKLIST -- Set the list of frames to be blinked.
+ */
+static Panel_setting
+set_blinklist (item, event)
+Panel_item item;
+Event *event;
+{
+ register char *ip;
+ register int i;
+
+ strcpy (s_blinklist, (char *) panel_get_value (item));
+ for (ip=s_blinklist; isspace (*ip); ip++)
+ ;
+
+ n_blink_frames = 0;
+ if (strncmp (ip, "all", 3) == 0) {
+ n_blink_frames = MAX_FRAMES;
+ for (i=0; i < MAX_FRAMES; i++)
+ blink_frames[i] = i + 1;
+ } else {
+ while (*ip && isdigit (*ip)) {
+ blink_frames[n_blink_frames++] = atoi(ip);
+ while (isdigit (*ip))
+ ip++;
+ while (isspace (*ip))
+ ip++;
+ }
+ }
+
+ return (panel_text_notify (item,event));
+}
+
+
+/* BLINKENABLE_PROC -- Turn frame blink on or off.
+ */
+static
+blinkenable_proc()
+{
+ blink = !blink;
+ menu_set (blink_item, MENU_STRING, blink ? "Blink off" : "Blink on", 0);
+ blink_frame = n_blink_frames - 1;
+ blink_timer = 0;
+}
+
+
+/* REGISTER_PROC -- Register all frames with the current frame.
+ */
+static
+register_proc()
+{
+ register struct framebuf *fr_p;
+ register int i;
+
+ for (i=0, fr_p=frames; i < fb_nframes; i++, fr_p++)
+ if (fr_p != df_p)
+ set_zoom (fr_p, df_p->fb_xoff, df_p->fb_yoff, df_p->fb_xzoom);
+}
+
+
+/* SET_OFNAME -- Set the file name for coordinate output.
+ */
+static Panel_setting
+set_ofname (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ s = (char *) panel_get_value (item);
+ if (strcmp (s, o_fname)) {
+ strcpy (o_fname, s);
+ df_p->fb_imageno = 0;
+ df_p->fb_objno = 1;
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ }
+
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_ULUT1 -- Set the file name for user lookup table 1.
+ */
+static Panel_setting
+set_ulut1 (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ strcpy (u_lut1, (char *) panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_ULUT2 -- Set the file name for user lookup table 2.
+ */
+static Panel_setting
+set_ulut2 (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ strcpy (u_lut2, (char *) panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_RFNAME -- Set the file name for rasterfile load/save.
+ */
+static Panel_setting
+set_rfname (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ strcpy (rasterfile, (char *) panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_RDISPOSE -- Set the raster file dispose command.
+ */
+static Panel_setting
+set_rdispose (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ strcpy (r_dispose, (char *) panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* SET_RFILENAME -- Set the screendump filename template.
+ */
+static Panel_setting
+set_rfilename (item, event)
+Panel_item item;
+Event *event;
+{
+ char *s;
+
+ strcpy (r_filename, (char *) panel_get_value (item));
+ return (panel_text_notify (item,event));
+}
+
+
+/* LOADFRAME_PROC -- Load the named rasterfile into the current frame.
+ */
+static
+loadframe_proc()
+{
+ char fname[SZ_FNAME+1];
+ char buf[SZ_FNAME+1];
+
+ sprintf (buf, rasterfile, df_p->fb_frameno, df_p->fb_imageno);
+ strcpy (fname, getfname(buf, 0));
+ load_raster (df_p->fb_pr, fname);
+}
+
+
+/* SAVEFRAME_PROC -- Save the current frame in the named rasterfile.
+ */
+static
+saveframe_proc()
+{
+ char fname[SZ_FNAME+1];
+ char buf[SZ_FNAME+1];
+
+ sprintf (buf, rasterfile, df_p->fb_frameno, df_p->fb_imageno);
+ strcpy (fname, getfname(buf, 0));
+ save_raster (df_p->fb_pr, fname);
+}
+
+
+/* FITFRAME_PROC -- Fit the display window to the current frame buffer size.
+ */
+static
+fitframe_proc()
+{
+ register struct pixrect *pr = get_screen_rect();
+
+ gio_xsize = min (pr->pr_width - TOOL_BORDERWIDTH * 2, Fb_width);
+ gio_ysize = min (pr->pr_height
+ - tool_headerheight ((int)window_get(gio_frame,FRAME_SHOW_LABEL))
+ - TOOL_BORDERWIDTH,
+ Fb_height + cb_height);
+
+ window_set (gio_canvas,
+ WIN_WIDTH, gio_xsize,
+ WIN_HEIGHT, gio_ysize,
+ 0);
+
+ window_fit (gio_canvas);
+ window_fit (gio_frame);
+}
+
+
+/* GET_SCREEN_RECT -- Determine the size of the workstation screen.
+ */
+static struct pixrect *
+get_screen_rect()
+{
+ static struct pixrect screen;
+ struct pixrect *pr;
+
+ if (!screen.pr_width)
+ if (pr = pr_open ("/dev/fb")) {
+ screen = *pr;
+ pr_close (pr);
+ } else {
+ screen.pr_width = 1152;
+ screen.pr_width = 900;
+ }
+
+ return (&screen);
+}
+
+
+/* SETUP_PROC -- Toggle whether or not the setup panel is shown.
+ */
+static
+setup_proc()
+{
+ if ((int) window_get (setup_frame, WIN_SHOW) == TRUE) {
+ setup_xoff = (int) window_get (setup_frame, WIN_X, 0);
+ setup_yoff = (int) window_get (setup_frame, WIN_Y, 0);
+ window_set (setup_frame, WIN_SHOW, FALSE, 0);
+ } else {
+ window_set (setup_frame,
+ WIN_X, setup_xoff,
+ WIN_Y, setup_yoff,
+ 0);
+ window_set (setup_frame, WIN_SHOW, TRUE, 0);
+ }
+}
+
+
+/* CREATE_FRAME_MENU -- Imtool uses a special frame menu which provides the
+ * standard frame menu as a submenu.
+ */
+static
+create_frame_menu (frame)
+Frame frame;
+{
+ extern imagecopy_proc(), register_proc();
+ extern setup_proc(), setframe_proc(), fitframe_proc();
+ extern iclear_proc(), gclear_proc(), blinkenable_proc();
+ Menu new_menu, old_menu;
+
+ /* Get the standard frame menu. */
+ old_menu = (Menu) window_get (frame, WIN_MENU);
+
+ /* Create the new frame root menu */
+ new_menu = menu_create (
+ MENU_PULLRIGHT_ITEM,
+ "Frame",
+ old_menu,
+ MENU_ACTION_ITEM,
+ "Setup",
+ setup_proc,
+ MENU_ACTION_ITEM,
+ "Register",
+ register_proc,
+ MENU_ACTION_ITEM,
+ "Blink on",
+ blinkenable_proc,
+ MENU_ACTION_ITEM,
+ "FitFrame",
+ fitframe_proc,
+ MENU_ACTION_ITEM,
+ "NextFrame",
+ setframe_proc,
+ MENU_ACTION_ITEM,
+ "Gclear",
+ gclear_proc,
+ MENU_ACTION_ITEM,
+ "Iclear",
+ iclear_proc,
+ MENU_ACTION_ITEM,
+ "Imcopy",
+ imagecopy_proc,
+ 0);
+
+ blink_item = menu_find (new_menu, MENU_STRING, "Blink on", 0);
+
+ /* Install the new menu. */
+ window_set (frame, WIN_MENU, new_menu, 0);
+}
+
+
+/* INIT_FRAMEBUFFERS -- Allocate space for the indicated number and size of
+ * framebuffers, initializing the framebuffer data structures accordingly.
+ * If at least one frame buffer of the desired size can be allocated we
+ * consider it a success, but set fb_nframes accordingly. -1 is returned
+ * if no frames of the desired size can be allocated.
+ */
+static
+init_framebuffers (config, nframes)
+int config; /* new frame buffer configuration */
+int nframes; /* desired number of frames */
+{
+ register struct framebuf *fb;
+ register int i;
+ char *calloc();
+
+ nframes = min (MAX_FRAMES, nframes);
+
+ /* If we are only changing the number of frames in the current
+ * configuration, keep the old frames.
+ */
+ if (frames && config == fb_config_index) {
+ if (nframes == fb_config[config].nframes) {
+ fb_nframes = nframes;
+ return (nframes);
+
+ } else if (nframes < fb_config[config].nframes) {
+ for (i=nframes; i < fb_nframes; i++)
+ pr_close (frames[i].fb_pr);
+ fb_config[config].nframes = fb_nframes = nframes;
+ if (reference_frame > nframes) {
+ reference_frame = nframes;
+ rf_p = frames + (reference_frame - 1);
+ }
+ if (display_frame > nframes)
+ set_frame (nframes);
+ return (nframes);
+
+ } else if (nframes > fb_config[config].nframes)
+ ; /* fall through and add more frames */
+
+ } else {
+ /* Deallocate old frame buffers, if any. */
+ if (frames) {
+ pr_close (cb_pr);
+ for (i=0; i < fb_nframes; i++)
+ pr_close (frames[i].fb_pr);
+ free (frames);
+ }
+
+ fb_nframes = 0;
+ frames = (struct framebuf *) calloc (MAX_FRAMES,
+ sizeof(struct framebuf));
+
+ df_p = rf_p = frames;
+ display_frame = reference_frame = 1;
+ strcpy (o_fname, COORDFILE);
+ wc_xoff = wc_yoff = 0;
+ }
+
+ /* Allocate and initialize the new frame buffers. */
+ fb_config_index = config;
+ Fb_width = fb_config[config].width;
+ Fb_height = fb_config[config].height;
+
+ for (i=fb_nframes; i < nframes; i++) {
+ fb = frames + i;
+ fb->fb_pr = mem_create (Fb_width, Fb_height, fb_depth);
+ if (fb->fb_pr == NULL) {
+ fprintf (stderr,
+ "attempt to allocate frame buffer %d (%dx%d) fails\n",
+ i + 1, Fb_width, Fb_height);
+ if (i)
+ break;
+ else
+ return (-1);
+ } else if (!fb_nframes++) {
+ cb_pr = mem_create (Fb_width, cb_height, fb_depth);
+ if (pw_rect.r_width)
+ init_colorbar (pw_rect.r_width);
+ }
+
+ fb->fb_xoff = 0;
+ fb->fb_yoff = 0;
+ fb->fb_xzoom = fb->fb_yzoom = 1;
+ fb->fb_center = fb_ngrey / 2.0;
+ fb->fb_slope = (float)white / (float)(fb_ngrey - 1);
+ fb->fb_maptype = maptype;
+ fb->fb_objno = 1;
+ fb->fb_imageno = 0;
+ fb->fb_frameno = i + 1;
+ fb->fb_ctran.valid = 0;
+ fb->fb_ctran.imtitle[0] = '\0';
+ strcpy (fb->fb_ctran.format, W_DEFFORMAT);
+ }
+
+ set_zoom (df_p, 0, 0, 1);
+ return (fb_config[config].nframes = fb_nframes);
+}
+
+
+/* INIT_COLORBAR -- Write the colorbar into the frame buffer. The length of
+ * the colorbar should correspond to the current width of the display window,
+ * but must not exceed the width of the frame buffer.
+ */
+init_colorbar (cb_width)
+int cb_width;
+{
+ register int i;
+ unsigned char *lp;
+ Pixrect *pr;
+
+ if (cb_height <= 0)
+ return;
+
+ if ((pr = mem_create (cb_width, 1, fb_depth)) != NULL) {
+ lp = (unsigned char *) mpr_d(pr)->md_image;
+ /* Write colorbar. */
+ for (i=0; i < cb_width; i++)
+ lp[i] = (CMS_DATARANGE-1) * i / (cb_width-1) +
+ CMS_DATASTART;
+ for (i=3; i < cb_height; i++)
+ pr_rop (cb_pr, 0, i, cb_width, 1, PIX_SRC, pr, 0, 0);
+
+ /* Add a border between image and colorbar. */
+ for (i=0; i < cb_width; i++)
+ lp[i] = NGREY-1;
+ for (i=0; i < 3; i++)
+ pr_rop (cb_pr, 0, i, cb_width, 1, PIX_SRC, pr, 0, 0);
+
+ for (i=0; i < cb_width; i++)
+ lp[i] = 0;
+ for (i=1; i < 2; i++)
+ pr_rop (cb_pr, 0, i, cb_width, 1, PIX_SRC, pr, 0, 0);
+
+ pr_close (pr);
+ }
+}
+
+
+/* INIT_COLORMAP -- Initialize the IMTOOL color map.
+ */
+static
+init_colormap (pw)
+struct pixwin *pw;
+{
+ register unsigned char *r = red;
+ register unsigned char *g = green;
+ register unsigned char *b = blue;
+ int planes = NGREY-1, i;
+
+ /* Initialize the IMTOOL colormap from the current fullscreen
+ * colormap, so that the (small) colormap entries of the other
+ * windows will be preserved, as far as possible.
+ */
+ grab_colormap (r, g, b);
+
+ /* Set a linear transfer function for the main part of the table. */
+ for (i=CMS_DATASTART; i <= CMS_DATAEND; i++)
+ r[i] = g[i] = b[i] = i * (white + 1) / CMS_DATARANGE;
+
+ /* Color table entry for the cursor. */
+ r[CMS_CURSOR] = white;
+ g[CMS_CURSOR] = white;
+ b[CMS_CURSOR] = white;
+
+ /* Set the background and graphics colors.
+ */
+ i = CMS_BACKGROUND;
+ r[i] = 0; g[i] = 0; b[i] = 0; i++; /* 202=black */
+
+ i = CMS_GRAPHICSSTART;
+ r[i] = 255; g[i] = 255; b[i] = 255; i++; /* 203=white */
+
+ r[i] = 255; g[i] = 0; b[i] = 0; i++; /* 204=red */
+ r[i] = 0; g[i] = 255; b[i] = 0; i++; /* 205=green */
+ r[i] = 0; g[i] = 0; b[i] = 255; i++; /* 206=blue */
+ r[i] = 255; g[i] = 255; b[i] = 0; i++; /* 207=yellow */
+ r[i] = 0; g[i] = 255; b[i] = 255; i++; /* 208=cyan */
+ r[i] = 255; g[i] = 0; b[i] = 255; i++; /* 209=magenta */
+ r[i] = 255; g[i] = 127; b[i] = 0; i++; /* 210=coral */
+ r[i] = 142; g[i] = 35; b[i] = 107; i++; /* 211=maroon */
+ r[i] = 204; g[i] = 50; b[i] = 50; i++; /* 212=orange */
+ r[i] = 159; g[i] = 159; b[i] = 95; i++; /* 213=khaki */
+ r[i] = 219; g[i] = 112; b[i] = 219; i++; /* 214=orchid */
+ r[i] = 112; g[i] = 219; b[i] = 219; i++; /* 215=turquoise */
+ r[i] = 159; g[i] = 95; b[i] = 159; i++; /* 216=violet */
+ r[i] = 216; g[i] = 216; b[i] = 191; i++; /* 217=wheat */
+
+ pw_setcmsname (pw, CMMAPNAME);
+ pw_putcolormap (pw, 0, NGREY, r, g, b);
+ pw_putattributes (pw, &planes);
+}
+
+
+/* COMPUTE_TRANSFER_FUNCTION -- Compute the slope and offset of the transfer
+ * function for the current display frame, given the coordinates of the
+ * mouse in the frame.
+ */
+static
+compute_transfer_function (event)
+Event *event;
+{
+ float xsize, ysize;
+ float y, slope;
+ int neg;
+
+ xsize = pw_rect.r_width;
+ ysize = pw_rect.r_height;
+
+ /* Compute the slope of the transfer function. */
+ y = event_y(event) / ysize * 2.0 - 1.0;
+ if (neg = (y < 0.0))
+ y = -y;
+ if (y > 0.99)
+ y = 0.99;
+ if ((slope = tan (y * M_PI_2)) > white)
+ slope = white;
+
+ /* Record new transfer function in frame buffer descriptor. */
+ df_p->fb_center = event_x(event) / xsize * (fb_ngrey-1);
+ df_p->fb_slope = neg ? -slope : slope;
+}
+
+
+/* SET_TRANSFER_FUNCTION -- Load the color map as necessary to implement the
+ * given linear transfer function.
+ */
+static
+set_transfer_function (pw, center, slope)
+struct pixwin *pw; /* reference pixwin */
+float center; /* greyscale value at half intensity */
+float slope; /* delta-intensity per greyscale unit */
+{
+ register int i;
+ register float z, zmin, zmax;
+ unsigned char o_red[NGREY], o_green[NGREY], o_blue[NGREY];
+ unsigned char *p_r, *p_g, *p_b;
+
+ if (center < CMS_DATASTART)
+ center = CMS_DATASTART;
+ else if (center > CMS_DATAEND)
+ center = CMS_DATAEND;
+
+ zmin = 0.0;
+ zmax = white;
+
+ z = white / 2;
+ for (i=center; i <= CMS_DATAEND; i++) {
+ o_red[i] = m_red[(unsigned char)(int)z];
+ o_green[i] = m_green[(unsigned char)(int)z];
+ o_blue[i] = m_blue[(unsigned char)(int)z];
+ z += slope;
+ if (z <= zmin)
+ z = zmin;
+ else if (z >= zmax)
+ z = zmax;
+ }
+
+ z = white / 2;
+ for (i=center; i >= CMS_DATASTART; i--) {
+ o_red[i] = m_red[(unsigned char)(int)z];
+ o_green[i] = m_green[(unsigned char)(int)z];
+ o_blue[i] = m_blue[(unsigned char)(int)z];
+ z -= slope;
+ if (z <= zmin)
+ z = zmin;
+ else if (z >= zmax)
+ z = zmax;
+ }
+
+ p_r = &o_red[CMS_DATASTART];
+ p_g = &o_green[CMS_DATASTART];
+ p_b = &o_blue[CMS_DATASTART];
+
+ /*pw_putcolormap (pw, CMS_DATASTART, CMS_DATARANGE, p_r, p_g, p_b);*/
+
+ bcopy (p_r, &red[CMS_DATASTART], CMS_DATARANGE);
+ bcopy (p_g, &green[CMS_DATASTART], CMS_DATARANGE);
+ bcopy (p_b, &blue[CMS_DATASTART], CMS_DATARANGE);
+
+ /* Reset the full 8 bit colormap for the pixwin, to pick up any
+ * changes made in the unused regions for other windows, picked up
+ * by edit_colortable below.
+ */
+ pw_putcolormap (pw, 0, NGREY, red, green, blue);
+}
+
+
+/* SET_COLORTABLE -- Set up the RGB lookup tables used to map the windowed
+ * monochrome output of a frame buffer into the hardware colormap.
+ */
+static Notify_value
+set_colortable()
+{
+ register int v, vsat, step, i;
+ static int seed = 0;
+ int knot[7];
+
+ vsat = NGREY - 1;
+ step = NGREY / 6;
+ for (i=0; i < 7; i++)
+ knot[i] = i * step;
+ knot[6] = vsat;
+
+ switch (df_p->fb_maptype) {
+ case MONO:
+ for (i=0; i < NGREY; i++)
+ m_red[i] = m_green[i] = m_blue[i] = i;
+ break;
+
+ case HEAT:
+ for (i=0; i < NGREY; i++) {
+ m_red[i] = heat.hue[i].red * (NGREY - 1);
+ m_green[i] = heat.hue[i].green * (NGREY - 1);
+ m_blue[i] = heat.hue[i].blue * (NGREY - 1);
+ }
+ break;
+
+ case RAMP1:
+ for (i=0; i < NGREY; i++) {
+ m_red[i] = heat.hue[i].red * NGREY;
+ m_green[i] = heat.hue[i].green * NGREY;
+ m_blue[i] = heat.hue[i].blue * NGREY;
+ }
+ break;
+
+ case RAMP2:
+ for (i=0; i < NGREY; i++) {
+ m_red[i] = heat.hue[i].red * ((NGREY - 1) * 2);
+ m_green[i] = heat.hue[i].green * ((NGREY - 1) * 2);
+ m_blue[i] = heat.hue[i].blue * ((NGREY - 1) * 2);
+ }
+ break;
+
+ case HALLEY:
+ for (i=0; i < NGREY; i++) {
+ m_red[i] = halley.hue[i].red * (NGREY - 1);
+ m_green[i] = halley.hue[i].green * (NGREY - 1);
+ m_blue[i] = halley.hue[i].blue * (NGREY - 1);
+ }
+ break;
+
+ case ULUT1:
+ case ULUT2:
+ { struct lut user;
+ struct triplet *p;
+ int i, j;
+ FILE *fp;
+ char *s;
+
+ s = (df_p->fb_maptype == ULUT1) ? u_lut1 : u_lut2;
+ if ((fp = fopen (getfname(s,0), "r")) == NULL) {
+ fprintf (stderr, "cannot open %s\n", s);
+ return;
+ }
+
+ for (user.lutlen=0; user.lutlen < NGREY; user.lutlen++) {
+ p = &user.hue[user.lutlen];
+ if (fscanf (fp, " %f %f %f",
+ &p->red, &p->green, &p->blue) == EOF)
+ break;
+ }
+
+ for (i=0; i < NGREY; i++) {
+ j = max(0, min(NGREY-1, (i * user.lutlen / NGREY)));
+ m_red[i] = user.hue[j].red * (NGREY - 1);
+ m_green[i] = user.hue[j].green * (NGREY - 1);
+ m_blue[i] = user.hue[j].blue * (NGREY - 1);
+ }
+
+ fclose (fp);
+ }
+ break;
+
+ case LINEARPS:
+ for (i=0; i < NGREY; i++)
+ m_red[i] = m_green[i] = m_blue[i] = 0;
+
+ for (i=knot[0]; i <= knot[1]; i++)
+ m_blue[i] = vsat * (i - knot[0]) / step;
+ for (i=knot[1]; i <= knot[2]; i++)
+ m_blue[i] = vsat;
+ for (i=knot[2]; i <= knot[3]; i++)
+ m_blue[i] = vsat * (knot[3] - i) / step;
+
+ for (i=knot[1]; i <= knot[2]; i++)
+ m_green[i] = vsat * (i - knot[1]) / step;
+ for (i=knot[2]; i <= knot[4]; i++)
+ m_green[i] = vsat;
+ for (i=knot[4]; i <= knot[5]; i++)
+ m_green[i] = vsat * (knot[5] - i) / step;
+
+ for (i=knot[3]; i <= knot[4]; i++)
+ m_red[i] = vsat * (i - knot[3]) / step;
+ for (i=knot[4]; i <= knot[6]; i++)
+ m_red[i] = vsat;
+
+ for (i=knot[5]; i <= knot[6]; i++) {
+ if ((v = vsat * (i - knot[5]) / step) > vsat)
+ v = vsat;
+ m_green[i] = m_blue[i] = v;
+ }
+ break;
+
+ case CRANDOMPS:
+ set_transfer_function (gio_pw, df_p->fb_center, df_p->fb_slope);
+ imt_pause (cr_msec[crandom_blink_rate], set_colortable);
+ /* fall through */
+
+ case RANDOMPS:
+ if (!seed)
+ seed = time(0);
+ srand (seed++);
+ for (i=0; i < NGREY; i++) {
+ m_red[i] = ((rand() >> 4) % NGREY);
+ m_green[i] = ((rand() >> 4) % NGREY);
+ m_blue[i] = ((rand() >> 4) % NGREY);
+ }
+ break;
+ }
+}
+
+
+/* GRAB_COLORMAP -- Read the current physical full screen colormap.
+ */
+static
+grab_colormap (red, green, blue)
+unsigned char red[], green[], blue[];
+{
+ struct pixrect *screen;
+
+ if (window_open) {
+ screen = pr_open ("/dev/fb");
+ pr_getcolormap (screen, 0, NGREY, red, green, blue);
+ pr_close (screen);
+ }
+}
+
+
+/* IMTOOL_CLOCK -- The main imtool clock.
+ */
+static
+imtool_clock (client, event, arg)
+Notify_client client;
+Notify_event event;
+Notify_arg arg;
+{
+ static int delay=0, interval=0, toggle=0;
+ Cursor cursor;
+ int frame, n;
+
+ /* Blink cursor (variable rate). */
+ cursor = window_get (gio_canvas, WIN_CURSOR);
+ toggle = !toggle;
+ if (reading_imcursor) {
+ cursor_set (cursor,
+ CURSOR_OP, toggle
+ ? PIX_NOT(PIX_SRC) & PIX_DST
+ : PIX_SRC | PIX_DST | PIX_COLOR(NGREY-1),
+ 0);
+ } else {
+ cursor_set (cursor,
+ CURSOR_CROSSHAIR_COLOR,
+ toggle ? CMS_BACKGROUND : CMS_CURSOR,
+ 0);
+ }
+ window_set (gio_canvas, WIN_CURSOR, cursor, 0);
+
+ /* Things that happen at a fixed interval. */
+ if ((delay += interval) >= CLOCK_INTERVAL) {
+ /* Keep imtool visible (if window open). */
+ edit_colormap();
+
+ /* Frame blink. The frames in the blink frame list do not have to
+ * exist; if not, then advance through the list until either a valid
+ * frame is found, or the list has been traversed once.
+ */
+ if (blink && n_blink_frames > 0 && state != WINDOW && window_open) {
+ if (--blink_timer <= 0) {
+ for (n=n_blink_frames; --n >= 0; ) {
+ if (++blink_frame >= n_blink_frames)
+ blink_frame = 0;
+ frame = blink_frames[blink_frame];
+ if (frame >= 1 && frame <= fb_nframes) {
+ set_frame (frame);
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (NULL);
+ break;
+ }
+ }
+ blink_timer = blink_rate[blink_rate_index];
+ }
+ }
+
+ delay = 0;
+ }
+
+ interval = reading_imcursor ? CLOCK_INTERVAL/4 : CLOCK_INTERVAL;
+ imt_pause (interval, imtool_clock);
+}
+
+
+/* EDIT_COLORMAP -- Overwrite the portion of the full screen colormap used
+ * by the display server. This must be done in such a way that changes to
+ * the region of the screen colortable used by other windows are preserved.
+ */
+static
+edit_colormap()
+{
+ struct pixrect *screen;
+ unsigned char r[NGREY], g[NGREY], b[NGREY];
+
+ if (state != WINDOW && window_open) {
+ /* Edit the physical colortable. */
+ screen = pr_open ("/dev/fb");
+ pr_getcolormap (screen, 0, NGREY, r, g, b);
+
+ /* Make the cursor blink between black and white for better vis.
+ * (>> now done with set_cursor in imtool_clock())
+ if (green[CMS_CURSOR] == white) {
+ red[CMS_CURSOR] = black;
+ green[CMS_CURSOR] = black;
+ blue[CMS_CURSOR] = black;
+ } else {
+ red[CMS_CURSOR] = white;
+ green[CMS_CURSOR] = white;
+ blue[CMS_CURSOR] = white;
+ }
+ */
+
+ bcopy (&red[CMS_FIRST], &r[CMS_FIRST], CMS_NGREY);
+ bcopy (&green[CMS_FIRST], &g[CMS_FIRST], CMS_NGREY);
+ bcopy (&blue[CMS_FIRST], &b[CMS_FIRST], CMS_NGREY);
+
+ pw_putcolormap (gio_pw, 0, NGREY, r, g, b);
+ if (global_colortable)
+ pr_putcolormap (screen, 0, NGREY, r, g, b);
+ pr_close (screen);
+
+ /* Update the canvas pixwin colortable. */
+ bcopy (r, red, NGREY);
+ bcopy (g, green, NGREY);
+ bcopy (b, blue, NGREY);
+ }
+}
+
+
+/* SHOW_COLORMAP -- Print the contents of the color map for a pixwin.
+ */
+static
+show_colormap (pw, first, last)
+struct pixwin *pw;
+int first, last;
+{
+ unsigned char r[NGREY], g[NGREY], b[NGREY];
+ char cmsname[CMS_NAMESIZE];
+ int i, n;
+
+ pw_getcmsname (pw, cmsname);
+ pw_getcolormap (pw, 0, NGREY, r, g, b);
+
+ printf ("color map segment = '%s'\n", cmsname);
+ for (i=first, n=0; i <= last; i++) {
+ printf ("%3d %3d %3d", r[i], g[i], b[i]);
+ printf ((++n % 5) ? " " : "\n");
+ }
+ printf ("\n");
+}
+
+
+/* LOAD_TESTPATTERN -- Load a test pattern into the reference frame buffer.
+ */
+static
+load_testpattern (type)
+int type; /* pattern type desired */
+{
+ register unsigned char *line;
+ register int i, j, color;
+ unsigned char *fb, *oline;
+
+ fb = (unsigned char *) mpr_d(df_p->fb_pr)->md_image;
+
+ switch (type) {
+ case 0:
+ /* Compute first line. */
+ oline = line = fb;
+ for (i=0, color=0; i < Fb_width; i++)
+ if (((i+16) % 32) == 0) {
+ line[i] = 0;
+ color = ((i+j) % CMS_DATARANGE) / 32 * 32;
+ } else
+ line[i] = color;
+
+ /* Compute remaining lines. */
+ for (j=1; j < Fb_height; j++) {
+ line = fb + j * Fb_width;
+ if (((j+16) % 32) == 0) {
+ for (i=0; i < Fb_width; i++)
+ line[i] = 0;
+ if (++j >= Fb_height)
+ break;
+ line = fb + j * Fb_width;
+ color = (j % CMS_DATARANGE) / 32 * 32;
+ for (i=0; i < Fb_width; i++)
+ if (((i+16) % 32) == 0) {
+ line[i] = 0;
+ color = ((i+j) % CMS_DATARANGE) / 32 * 32;
+ } else
+ line[i] = color;
+ oline = line;
+ } else
+ bcopy (oline, line, Fb_width);
+ }
+ break;
+
+ case 1:
+ for (j=1; j < Fb_height; j++) {
+ line = fb + j * Fb_width;
+ for (i=0; i < Fb_width; i++)
+ line[i] = (i % CMS_DATARANGE);
+ }
+ break;
+
+ case 2:
+ for (j=1; j < Fb_height; j++) {
+ line = fb + j * Fb_width;
+ for (i=0; i < Fb_width; i++)
+ line[i] = ((i+j) % CMS_DATARANGE);
+ }
+ break;
+ }
+}
+
+
+/* For the moment we take an IIS model 70 command/data stream as input; this
+ * is used to load images into the image display. This is a kludge interface
+ * for the prototype, convenient since the high level software is written for
+ * the IIS.
+ */
+#define MEMORY 01 /* frame buffer i/o */
+#define LUT 02 /* lut i/o */
+#define FEEDBACK 05 /* used for frame clears */
+#define IMCURSOR 020 /* logical image cursor */
+#define WCS 021 /* used to set WCS */
+
+#define SZ_IMCURVAL 160
+#define PACKED 0040000
+#define COMMAND 0100000
+#define IIS_READ 0100000
+#define IMC_SAMPLE 0040000
+#define IMT_FBCONFIG 077
+#define XYMASK 077777
+
+struct iism70 {
+ short tid;
+ short thingct;
+ short subunit;
+ short checksum;
+ short x, y, z;
+ short t;
+};
+
+/* EV_CMDINPUT -- Called when command or data input has arrived via the
+ * pseudodevice input stream from some applications process.
+ */
+static Notify_value
+ev_cmdinput (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ register unsigned char *cp;
+ register int sum, i;
+ register short *p;
+ int ndatabytes, nbytes, n, ntrys=0;
+ static int errmsg=0, bswap=0;
+ struct iism70 iis;
+ char buf[SZ_FIFOBUF];
+ int fb_index;
+
+ /* Get the IIS header. */
+ if (read (datain, (char *)&iis, sizeof(iis)) < sizeof(iis)) {
+ fprintf (stderr, "imtool: command input read error\n");
+ return (NOTIFY_DONE);
+ } else if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+
+ /* Verify the checksum. If it fails swap the bytes and try again.
+ */
+ for (;;) {
+ for (i=0, sum=0, p=(short *)&iis; i < 8; i++)
+ sum += *p++;
+ if ((sum & 0177777) == 0177777)
+ break;
+
+ if (ntrys++) {
+ if (!errmsg++) {
+ fprintf (stderr, "imtool: bad data header checksum\n");
+ if (bswap)
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, "noswap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ fprintf (stderr, " swap:");
+ for (i=0, p=(short *)&iis; i < 8; i++)
+ fprintf (stderr, " %6o", p[i]);
+ fprintf (stderr, "\n");
+ }
+ break;
+
+ } else {
+ bswap2 ((char *)&iis, (char *)&iis, sizeof(iis));
+ bswap = !bswap;
+ }
+ }
+
+ ndatabytes = -iis.thingct;
+ if (!(iis.tid & PACKED))
+ ndatabytes *= 2;
+
+ switch (iis.subunit & 077) {
+ case FEEDBACK:
+ /* The feedback unit is used only to clear a frame.
+ */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+ erase (rf_p);
+ break;
+
+ case LUT:
+ /* Data mode writes to the frame lookup tables are not implemented.
+ * A command mode write to the LUT subunit is used to connect
+ * image memories up to the RGB channels, i.e., to select the frame
+ * to be displayed. We ignore any attempt to assign multiple
+ * frames to multiple color channels, and just do a simple frame
+ * select.
+ */
+ if (iis.subunit & COMMAND) {
+ int frame, z, n;
+ short x[14];
+
+ if (read (datain, (char *)x, ndatabytes) == ndatabytes) {
+ if (bswap)
+ bswap2 ((char *)x, (char *)x, ndatabytes);
+
+ z = x[0];
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ frame = max (1, n + 1);
+ if (frame > fb_nframes) {
+ if (frame < MAX_FRAMES)
+ set_fbconfig (fb_config_index, frame);
+ else {
+ fprintf (stderr, "imtool warning: ");
+ fprintf (stderr,
+ "attempt to display nonexistent frame %d\n", frame);
+ frame = fb_nframes - 1;
+ }
+ }
+
+ set_frame (frame);
+ return (NOTIFY_DONE);
+ }
+ }
+
+ case MEMORY:
+ /* Load data into the frame buffer. Data is assumed to be byte
+ * packed.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read from the display.
+ */
+ unsigned char *fb, *ip;
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to be read from. */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+
+ fb = (unsigned char *) mpr_d(rf_p->fb_pr)->md_image;
+ nbytes = ndatabytes;
+ x = iis.x & XYMASK;
+ y = iis.y & XYMASK;
+
+ ip = max (fb, min (fb + Fb_width * Fb_height - nbytes,
+ fb + y * Fb_width + x));
+ if (ip != fb + y * Fb_width + x) {
+ fprintf (stderr,
+ "imtool: attempted read out of bounds on framebuf\n");
+ fprintf (stderr,
+ "read %d bytes at [%d,%d]\n", nbytes, x, y);
+ }
+
+ /* Return the data from the frame buffer. */
+ starttime = time(0);
+ for (nleft = nbytes; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = write (dataout, ip, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT)) {
+ fprintf (stderr, "IMTOOL: timeout on write\n");
+ break;
+ }
+ } else
+ ip += n;
+ }
+
+ return (NOTIFY_DONE);
+
+ } else {
+ /* Write to the display.
+ */
+ unsigned char *fb, *op;
+ int nbytes, nleft, n, x, y;
+ long starttime;
+
+ /* Get the frame to be written into (encoded with a bit for
+ * each frame, 01 is frame 1, 02 is frame 2, 04 is frame 3,
+ * and so on).
+ */
+ set_reference_frame (decode_frameno (iis.z & 07777));
+
+ /* Get a pointer into the frame buffer where the data will
+ * be put.
+ */
+ fb = (unsigned char *) mpr_d(rf_p->fb_pr)->md_image;
+ nbytes = ndatabytes;
+ x = iis.x & XYMASK;
+ y = iis.y & XYMASK;
+
+ op = max (fb, min (fb + Fb_width * Fb_height - nbytes,
+ fb + y * Fb_width + x));
+ if (op != fb + y * Fb_width + x) {
+ fprintf (stderr,
+ "imtool: attempted write out of bounds on framebuf\n");
+ fprintf (stderr,
+ "write %d bytes to [%d,%d]\n", nbytes, x, y);
+ }
+
+ /* Read the data into the frame buffer.
+ */
+ starttime = time(0);
+ for (nleft = nbytes; nleft > 0; nleft -= n) {
+ n = (nleft < SZ_FIFOBUF) ? nleft : SZ_FIFOBUF;
+ if ((n = read (datain, op, n)) <= 0) {
+ if (n < 0 || (time(0) - starttime > IO_TIMEOUT))
+ break;
+ } else {
+ /* Set any zeroed pixels to the background color,
+ * if a special background color is specified.
+ */
+ if (background)
+ for (cp=op, i=n; --i >= 0; cp++)
+ if (!*cp)
+ *cp = background;
+ op += n;
+ }
+ }
+
+ /* Refresh the display, if the current display frame is the
+ * same as the reference frame.
+ */
+ if (rf_p == df_p) {
+ BRect fb_r, pw_r;
+
+ fb_r.r_left = x * zoom;
+ fb_r.r_top = y * zoom;
+ fb_r.r_width = min (nbytes * zoom, fb_width);
+ fb_r.r_height = ((nbytes*zoom*zoom + fb_width-1)/fb_width);
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ Bpw_lock (gio_pw, &pw_rect);
+
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+
+ if (maprect (&fb_rect, &fb_r, &pw_rect, &pw_r))
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r)) {
+ ds_write (gio_pw,
+ pw_r.r_left, pw_r.r_top,
+ pw_r.r_width, pw_r.r_height,
+ PIX_SRC | PIX_COLOR(NGREY-1),
+ df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+
+ if (pw_r.r_top + pw_r.r_height >= pw_rect.r_height
+ - cb_height)
+ put_colorbar();
+ }
+
+ Bpw_unlock (gio_pw);
+ }
+
+ return (NOTIFY_DONE);
+ }
+ break;
+
+ case WCS:
+ /* Read or write the WCS for a frame. The frame number to
+ * which the WCS applies is passed in Z and the frame buffer
+ * configuration in T. The client changes the frame buffer
+ * configuration in a WCS set. The WCS text follows the header
+ * as byte packed ASCII data.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Return the WCS for the referenced frame.
+ */
+ char emsg[SZ_FNAME];
+ char *text;
+ int frame;
+
+ frame = decode_frameno (iis.z & 07777);
+ if (frame > fb_nframes)
+ strcpy (text=emsg, "[NOSUCHFRAME]\n");
+ else {
+ set_reference_frame (frame);
+ text = wcsbuf[reference_frame-1];
+ }
+
+ write (dataout, text, SZ_WCSBUF);
+
+ } else {
+ /* Set the WCS for the referenced frame.
+ */
+ char buf[1024];
+ int fb_config, frame;
+
+ frame = decode_frameno (iis.z & 07777);
+ if (frame > fb_nframes)
+ if (frame < MAX_FRAMES)
+ set_fbconfig (fb_config_index, frame);
+
+ set_reference_frame (frame);
+ if ((fb_config = iis.t & 077) != fb_config_index)
+ set_fbconfig (fb_config, reference_frame);
+
+ /* Read in and set up the WCS. */
+ if (read (datain, buf, ndatabytes) == ndatabytes)
+ strncpy (wcsbuf[reference_frame-1], buf, SZ_WCSBUF);
+
+ strcpy (rf_p->fb_ctran.format, W_DEFFORMAT);
+ rf_p->fb_ctran.imtitle[0] = '\0';
+ rf_p->fb_ctran.valid = 0;
+ rf_p->fb_imageno++;
+ rf_p->fb_objno = 1;
+
+ wcs_update (rf_p);
+ if (rf_p == df_p)
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ }
+
+ return (NOTIFY_DONE);
+ break;
+
+ case IMCURSOR:
+ /* Read or write the logical image cursor. This is an extension
+ * added to provide a high level cursor read facility; this is
+ * not the same as a low level access to the IIS cursor subunit.
+ * Cursor reads may be either nonblocking (immediate) or blocking,
+ * using the keyboard or mouse to terminate the read, and
+ * coordinates may be returned in either image (world) or frame
+ * buffer pixel coordinates.
+ */
+ if (iis.tid & IIS_READ) {
+ /* Read the logical image cursor. In the case of a blocking
+ * read all we do is initiate a cursor read; completion occurs
+ * when the user hits a key or button.
+ */
+ if (iis.tid & IMC_SAMPLE) {
+ /* Sample the cursor position. */
+ register struct ctran *ct;
+ int wcs = iis.z;
+ int sx, sy;
+ float wx, wy;
+
+ wx = sx = last_sx + pw_rect.r_left;
+ wy = sy = last_sy + pw_rect.r_top;
+
+ if (wcs) {
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ if (abs(ct->d) > .001)
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+ }
+ }
+
+ /* Return the cursor value on the output datastream encoded
+ * in a fixed size ascii buffer.
+ */
+ gio_retcursorval (wx, wy, display_frame*100+wcs, 0, "");
+
+ } else {
+ /* Initiate a user triggered cursor read. */
+ gio_readcursor (iis.z);
+ }
+
+ } else {
+ /* Write (set) the logical image cursor position. */
+ register struct ctran *ct;
+ int sx = iis.x, sy = iis.y;
+ float wx = sx, wy = sy;
+ int wcs = iis.z;
+
+ if (wcs) {
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ sx = (wx - ct->tx) / ct->a;
+ if (abs(ct->d) > .001)
+ sy = (wy - ct->ty) / ct->d;
+ }
+ }
+
+ gio_setcursorpos (sx - pw_rect.r_left, sy - pw_rect.r_top);
+ }
+
+ return (NOTIFY_DONE);
+ break;
+
+ default:
+ /* Ignore unsupported command input.
+ */
+ break;
+ }
+
+ /* Discard any data following the header. */
+ if (!(iis.tid & IIS_READ))
+ for (nbytes = ndatabytes; nbytes > 0; nbytes -= n) {
+ n = (nbytes < SZ_FIFOBUF) ? nbytes : SZ_FIFOBUF;
+ if ((n = read (datain, buf, n)) <= 0)
+ break;
+ }
+
+ return (NOTIFY_DONE);
+}
+
+
+/* SET_REFERENCE_FRAME -- Set reference frame. If the frame referenced is
+ * greater than the current number of frames, attempt to increase the number
+ * of frames.
+ */
+static
+set_reference_frame (n)
+register int n;
+{
+ reference_frame = max (1, n);
+ if (reference_frame > fb_nframes) {
+ if (reference_frame < MAX_FRAMES)
+ set_fbconfig (fb_config_index, reference_frame);
+ else {
+ fprintf (stderr, "imtool warning: ");
+ fprintf (stderr,
+ "attempt to reference nonexistent frame %d\n",
+ reference_frame);
+ reference_frame = fb_nframes;
+ }
+ }
+
+ rf_p = frames + (reference_frame - 1);
+}
+
+
+/* DECODE_FRAMENO -- Decode encoded IIS register frame number.
+ */
+static
+decode_frameno (z)
+register int z;
+{
+ register int n;
+
+ /* Get the frame number, encoded with a bit for each frame, 01 is
+ * frame 1, 02 is frame 2, 04 is frame 3, and so on.
+ */
+ if (!z) z = 1;
+ for (n=0; !(z & 1); z >>= 1)
+ n++;
+
+ return (max (1, n + 1));
+}
+
+
+/* BSWAP2 - Move bytes from array "a" to array "b", swapping successive
+ * pairs of bytes. The two arrays may be the same but may not be offset
+ * and overlapping.
+ */
+static
+bswap2 (a, b, nbytes)
+char *a, *b; /* input array */
+int nbytes; /* number of bytes to swap */
+{
+ register char *ip=a, *op=b, *otop;
+ register unsigned temp;
+
+ /* Swap successive pairs of bytes.
+ */
+ for (otop = op + (nbytes & ~1); op < otop; ) {
+ temp = *ip++;
+ *op++ = *ip++;
+ *op++ = temp;
+ }
+
+ /* If there is an odd byte left, move it to the output array.
+ */
+ if (nbytes & 1)
+ *op = *ip;
+}
+
+
+/* Cursor and marker pixrects.
+ */
+static short p_imcursor[] = {
+#include "imtool.cursor"
+};
+static short p_imcross[] = {
+#include "imtool.cross"
+};
+static short p_imsquare[] = {
+#include "imtool.square"
+};
+mpr_static (old_cursor, 16, 16, 1, NULL);
+mpr_static (pr_cursor, 16, 16, 1, p_imcursor);
+mpr_static (pr_cross, 16, 16, 1, p_imcross);
+mpr_static (pr_square, 16, 16, 1, p_imsquare);
+static struct pixrect *marker[] = { NULL, &pr_cursor, &pr_cross, &pr_square };
+
+/* GIO_READCURSOR -- Initiate an image cursor read. Save the current
+ * mouse coordinates if outside the imtool window, restore the mouse to the
+ * imtool window, and change the cursor shape to indicate that a cursor read
+ * is in progress. May be called while a cursor read is already in progress
+ * to reset the cursor-read cursor pixrect.
+ */
+static
+gio_readcursor (wcs)
+int wcs;
+{
+ Cursor cursor = window_get (gio_canvas, WIN_CURSOR);
+
+ if (!reading_imcursor) {
+ /* Save cursor pixrect for later restore. */
+ old_cursor = *((Pixrect *) cursor_get (cursor, CURSOR_IMAGE));
+
+ /* Save the absolute mouse position so that we can restore it when
+ * the cursor read is completed. Restore the mouse to the most
+ * recent position in the IMTOOL window.
+ */
+ get_absmousepos (gio_frame_fd, &save_sx, &save_sy);
+ gio_setcursorpos (last_sx, last_sy);
+
+ reading_imcursor++;
+ imcursor_wcs = wcs;
+ }
+
+ /* Change the cursor shape while the cursor read is in progress. */
+ cursor_set (cursor,
+ CURSOR_IMAGE, &pr_cursor,
+ CURSOR_SHOW_CURSOR, TRUE,
+ CURSOR_SHOW_CROSSHAIRS, FALSE,
+ CURSOR_OP, PIX_NOT(PIX_SRC) & PIX_DST | PIX_COLOR(CMS_CURSOR),
+ CURSOR_XHOT, 8,
+ CURSOR_YHOT, 8,
+ 0);
+ window_set (gio_canvas, WIN_CURSOR, cursor, 0);
+}
+
+
+/* GIO_RESTORECURSOR -- Restore the original cursor.
+ */
+static
+gio_restorecursor()
+{
+ if (reading_imcursor) {
+ Cursor cursor;
+
+ /* Restore the mouse position to whatever it was before IMTOOL
+ * grabbed the mouse for the cursor read.
+ */
+ set_absmousepos (gio_frame_fd, save_sx, save_sy);
+
+ /* Restore the default IMTOOL cursor shape. */
+ cursor = window_get (gio_canvas, WIN_CURSOR);
+ cursor_set (cursor,
+ CURSOR_IMAGE, &old_cursor,
+ CURSOR_SHOW_CURSOR, FALSE,
+ CURSOR_SHOW_CROSSHAIRS, (cursor_show == CURSOR_ON),
+ 0);
+ window_set (gio_canvas, WIN_CURSOR, cursor, 0);
+
+ reading_imcursor = 0;
+ }
+}
+
+
+/* GIO_RETCURSORVAL -- Return the cursor value on the output datastream to
+ * the client which requested the cursor read.
+ */
+static
+gio_retcursorval (wx, wy, wcs, key, strval)
+float wx, wy; /* cursor coordinates */
+int wcs; /* encoded WCS value */
+int key; /* keystroke used as trigger */
+char *strval; /* optional string value */
+{
+ char curval[SZ_IMCURVAL];
+ char keystr[20];
+
+ /* Encode the cursor value. */
+ if (key == EOF)
+ sprintf (curval, "EOF\n");
+ else {
+ if (isprint (key) && !isspace(key)) {
+ keystr[0] = key;
+ keystr[1] = '\0';
+ } else
+ sprintf (keystr, "\\%03o", key);
+
+ sprintf (curval, "%10.3f %10.3f %d %s %s\n",
+ wx, wy, wcs, keystr, strval);
+ }
+
+ /* Send it to the client program. */
+ write (dataout, curval, sizeof(curval));
+}
+
+
+/* EV_GIOFRAME -- GIO frame event handler.
+ */
+static Notify_value
+ev_gioframe (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ Notify_value value;
+
+ value = notify_next_event_func (frame, event, arg, type);
+ window_open = (((int) window_get (gio_frame, FRAME_CLOSED)) == 0);
+
+ return (value);
+}
+
+
+/* EV_GIOINPUT -- GIO input event handler.
+ */
+static Notify_value
+ev_gioinput (frame, event, arg, type)
+Frame frame;
+Event *event;
+Notify_arg arg;
+Notify_event_type type;
+{
+ register int key;
+ Notify_value value;
+ static float xsize, ysize;
+ BRect rect;
+ char ch;
+
+ key = event_id (event);
+
+ /* The following is to attempt to restore the image greyscale in the
+ * global color map, after the color map has been clobbered by the
+ * window manager when the mouse is moved to some other window.
+ */
+ if (key == KBD_DONE || key == LOC_WINEXIT) {
+ edit_colormap();
+ return (NOTIFY_DONE);
+ }
+
+ /* Let frame operate upon the event. */
+ if ((int)type != PANNER_EVENT)
+ value = notify_next_event_func (frame, event, arg, type);
+
+ switch (key) {
+ case WIN_RESIZE:
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ pw_rect.r_left = df_p->fb_xoff =
+ max(0, min(fb_rect.r_width - pw_rect.r_width, df_p->fb_xoff));
+ pw_rect.r_top = df_p->fb_yoff =
+ max(0, min(fb_rect.r_height - pw_rect.r_height, df_p->fb_yoff));
+
+ gio_xsize = pw_rect.r_width;
+ gio_ysize = pw_rect.r_height;
+ init_colorbar (pw_rect.r_width);
+
+ wc_xoff = wc_yoff = 0;
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (event);
+ break;
+
+ case KEY_SNAP:
+ /* Imcopy. */
+ imagecopy_proc();
+ break;
+
+ case KEY_PCOORDS:
+ /* Enable/disable continuous display of the cursor coordinates. */
+ toggle_displaycoords (event);
+ break;
+
+ case KEY_SETUP:
+ /* Toggle display of the setup panel. */
+ setup_proc();
+ break;
+
+ case KEY_REMARK:
+ /* Remark a list of objects. */
+ if (event_is_down(event)) {
+ char fname[SZ_FNAME];
+
+ wcs_update (df_p);
+ o_revtext = !o_revtext;
+ sprintf (fname, o_fname, df_p->fb_frameno, df_p->fb_imageno);
+ strcpy (fname, getfname(fname, 0));
+ remark_objects (fname);
+ }
+ break;
+
+ case MS_RIGHT:
+ if (event_is_down(event)) {
+ last_sx = last_bx = event_x(event);
+ last_sy = last_by = event_y(event);
+ }
+
+ /* If the cursor is moved while the right mouse button is
+ * depressed the image is windowed. If the control key is also
+ * depressed the cursor is used to roam about in the frame buffer.
+ */
+ if (state == TRACK_CURSOR && event_is_down(event)) {
+ if (event_ctrl_is_down (event))
+ state = ROAM;
+ else
+ state = WINDOW;
+ xsize = pw_rect.r_width;
+ ysize = pw_rect.r_height;
+ if (state == WINDOW) {
+ switch (df_p->fb_maptype) {
+ case MONO:
+ case HEAT:
+ case RAMP1:
+ case RAMP2:
+ case HALLEY:
+ case LINEARPS:
+ case ULUT1:
+ case ULUT2:
+ compute_transfer_function (event);
+ break;
+ case RANDOMPS:
+ case CRANDOMPS:
+ set_colortable();
+ break;
+ }
+ set_transfer_function (gio_pw,
+ df_p->fb_center, df_p->fb_slope);
+ }
+ } else if (state != TRACK_CURSOR && event_is_up(event))
+ state = TRACK_CURSOR;
+ break;
+
+ case LOC_DRAG:
+ last_sx = event_x (event);
+ last_sy = event_y (event);
+
+ if (panning)
+ p_cursor_setback--;
+ if (state == WINDOW) {
+ compute_transfer_function (event);
+ set_transfer_function (gio_pw, df_p->fb_center, df_p->fb_slope);
+ }
+ break;
+
+ case LOC_MOVE:
+ last_sx = event_x (event);
+ last_sy = event_y (event);
+
+ if (panning)
+ p_cursor_setback--;
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (event);
+ break;
+
+ case MS_LEFT:
+ last_sx = event_x (event);
+ last_sy = event_y (event);
+
+ if (event_is_down (event)) {
+ if (reading_imcursor) {
+ /* The left mouse button may be used to alias keyboard
+ * events. Typing ctrl/button causes the last key to be
+ * aliased with the indicated button. Thereafter, pressing
+ * that mouse button during a cursor read causes the cursor
+ * read to terminate, returning the aliased key just as if
+ * the key had been typed on the keyboard.
+ */
+ if (event_ctrl_is_down (event)) {
+ if (key == MS_LEFT)
+ key_left = last_key;
+ } else if (reading_imcursor) {
+ if (key == MS_LEFT)
+ key = key_left;
+ if (key)
+ goto readcur;
+ }
+
+ } else if (display_coords && state==TRACK_CURSOR) {
+ /* Add an object to a cursor list. */
+ mark_object (event);
+ }
+ }
+ break;
+
+ case MS_MIDDLE:
+ /* Pan - move the object under the cursor to the center of the
+ * display window (or as close as possible without wraparound).
+ */
+ if (event_is_up (event)) {
+ int fb_ex, fb_ey, fb_mx, fb_my, fb_xc, fb_yc, fb_nx, fb_ny;
+ int pw_xc, pw_yc, dx, dy, n_x, n_y;
+ int newzoom, close=1;
+
+ /* Pressing the middle button without moving the mouse causes
+ * the zoom factor to be advanced. In other words, placing
+ * the mouse on a feature and pressing the "zoom/pan" button
+ * causes that feature to be moved to the center of the
+ * display; pressing the button again causes the display to be
+ * zoomed about the centered feature.
+ */
+ newzoom = zoom;
+ n_x = event_x(event);
+ n_y = event_y(event);
+
+ if (abs(n_x-last_bx) <= close && abs(n_y-last_by) <= close) {
+ if (++zoom_index >= nzooms)
+ zoom_index = 0;
+ newzoom = zooms[zoom_index];
+ }
+
+ last_sx = last_bx = n_x;
+ last_sy = last_by = n_y;
+
+ pw_xc = pw_rect.r_width / 2; /* window center in pw */
+ pw_yc = pw_rect.r_height / 2;
+ fb_mx = pw_rect.r_left + n_x; /* final mouse position */
+ fb_my = pw_rect.r_top + n_y;
+ fb_xc = pw_rect.r_left + pw_xc; /* window center in fb */
+ fb_yc = pw_rect.r_top + pw_yc;
+ fb_nx = fb_ex = fb_mx; /* next center */
+ fb_ny = fb_ey = fb_my;
+
+ dx = n_x - pw_xc; /* step size */
+ dy = n_y - pw_yc;
+
+ /* Pan a long ways in the indicated direction, normally to the
+ * edge of the image.
+ */
+ if (event_shift_is_down (event)) {
+ fb_ex = fb_nx = fb_mx =
+ max(pw_xc, min(fb_rect.r_width - pw_xc, fb_xc + dx*5));
+ fb_ey = fb_ny = fb_my =
+ max(pw_xc, min(fb_rect.r_height - pw_yc, fb_yc + dy*5));
+ if (!event_ctrl_is_down (event))
+ dx = dy = 0;
+ }
+
+ /* Smooth pan to ex,ey. May be combined with shift-pan. */
+ if (event_ctrl_is_down (event)) {
+ dx = dx ? dx / abs(dx) : 0;
+ dy = dy ? dy / abs(dy) : 0;
+
+ /* Increase step size if window is large. */
+ if (pw_rect.r_width * pw_rect.r_height > 200000) {
+ dx *= 2;
+ dy *= 2;
+ }
+
+ fb_nx = max(0, min(fb_rect.r_width, fb_xc + dx));
+ fb_ny = max(0, min(fb_rect.r_height, fb_yc + dy));
+ }
+
+ /* Go to x,y starting at nx,ny stepping dx,dy per frame. */
+ if (fb_nx != fb_xc || fb_ny != fb_yc || zoom != newzoom)
+ start_pan (fb_mx,fb_my, fb_nx,fb_ny, fb_ex,fb_ey, dx,dy,
+ newzoom);
+ }
+ break;
+
+ case INTERRUPT:
+ /* Abort any lengthy operation currently in progress. */
+ if (reading_imcursor)
+ goto readcur;
+ else
+ stop_pan();
+ break;
+
+ case NEXT_SCREEN:
+ /* Display the next screen in numerical sequence. */
+ set_frame (0);
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (event);
+ break;
+
+ case PREV_SCREEN:
+ /* Display the previous screen in numerical sequence. */
+ set_frame (-1);
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (event);
+ break;
+
+ case CYCLE_BLINK:
+ /* Display the next screen from the blink frames list.
+ * The frames in the blink frame list do not have to exist;
+ * if not, then advance through the list until either a valid
+ * frame is found, or the list has been traversed once.
+ */
+ if (n_blink_frames > 0 && window_open) {
+ int frame, n;
+ for (n=n_blink_frames; --n >= 0; ) {
+ if (++blink_frame >= n_blink_frames)
+ blink_frame = 0;
+ frame = blink_frames[blink_frame];
+ if (frame >= 1 && frame <= fb_nframes) {
+ set_frame (frame);
+ if (display_coords && state == TRACK_CURSOR)
+ update_coords (NULL);
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ /* Terminate a cursor read, returning the encoded cursor value
+ * sequence to client program on the output datastream.
+ */
+ if (event_is_down(event) &&
+ (event_is_ascii(event) || event_is_key_right(event))) {
+readcur:
+ last_sx = event_x (event);
+ last_sy = event_y (event);
+
+ /* Terminate cursor read? */
+ if (reading_imcursor) {
+ register struct ctran *ct;
+ int wcs = imcursor_wcs;
+ int sx, sy;
+ float wx, wy;
+
+ /* Map keypad function keys to digits. */
+ if (event_is_key_right(event)) {
+ switch (key = event_id(event) - KEY_RIGHT(1) + 1) {
+ case 7: case 8: case 9:
+ break;
+ case 10: case 11: case 12:
+ key -= 6;
+ break;
+ case 13: case 14: case 15:
+ key -= 12;
+ break;
+ default:
+ return (value);
+ }
+ key += '0';
+ }
+
+ last_x = event_x (event);
+ last_y = event_y (event);
+ last_key = key;
+
+ wx = sx = last_x + pw_rect.r_left;
+ wy = sy = last_y + pw_rect.r_top;
+
+ if (wcs) {
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ wx = sx * ct->a + ct->tx;
+ if (abs(ct->d) > .001)
+ wy = sy * ct->d + ct->ty;
+ }
+ }
+
+ /* Map ctrl/d and ctrl/z into EOF. */
+ if (key == '\004' || key == '\032')
+ key = EOF;
+ else if (marktype) {
+ /* Mark the cursor position? */
+ edit_framebuffer (df_p, sx/zoom - 8, sy/zoom - 8,
+ marker[marktype],
+ PIX_NOT(PIX_SRC) & PIX_DST);
+ }
+
+ /* Return the cursor value on the output datastream encoded
+ * in a fixed size ascii buffer.
+ */
+ gio_retcursorval (wx, wy, display_frame*100+wcs, key, "");
+
+ /* Terminate the cursor read. */
+ gio_restorecursor();
+ }
+ }
+ }
+
+ return (value);
+}
+
+
+static int p_dx, p_dy;
+static int p_sx, p_sy;
+static int p_left, p_top;
+static int p_svdc;
+
+/* START_PAN -- Pan the image smoothly to the indicated position. This can
+ * take a while, so we want to do it on a timer and provide for interrupt.
+ * A new pan can be started to change the destination while an old pan is
+ * still in progress.
+ */
+static
+start_pan (fb_mx,fb_my, fb_nx,fb_ny, fb_ex,fb_ey, dx,dy, newzoom)
+int fb_mx, fb_my; /* mouse position in frame buffer */
+int fb_nx, fb_ny; /* center of window at next display */
+int fb_ex, fb_ey; /* center of window at final display */
+int dx, dy; /* step size for pan */
+register int newzoom; /* new zoom factor */
+{
+ register int w, h;
+ int n_left, n_top;
+ int n_sx, n_sy, e_sx, e_sy;
+
+ if (!panning) {
+ p_svdc = display_coords;
+ display_coords = 0;
+ panning++;
+ }
+
+ p_cursor_setback = 9;
+ gio_setcursor (CURSOR_OFF, 0);
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ w = pw_rect.r_width;
+ h = pw_rect.r_height;
+
+ /* Scale zoomed frame buffer units to new zoom factor.
+ */
+ p_sx = fb_mx * newzoom / zoom;
+ p_sy = fb_my * newzoom / zoom;
+ n_sx = fb_nx * newzoom / zoom;
+ n_sy = fb_ny * newzoom / zoom;
+ e_sx = fb_ex * newzoom / zoom;
+ e_sy = fb_ey * newzoom / zoom;
+ p_dx = dx * newzoom;
+ p_dy = dy * newzoom;
+
+ /* The following are the final left,top values. */
+ p_left = max(0, min(Fb_width*newzoom - w,
+ (e_sx - w/2) / newzoom * newzoom));
+ p_top = max(0, min(Fb_height*newzoom - h,
+ (e_sy - h/2) / newzoom * newzoom));
+
+ /* The following are the left,top values for the next display. */
+ n_left = max(0, min(Fb_width*newzoom - w,
+ (n_sx - w/2) / newzoom * newzoom));
+ n_top = max(0, min(Fb_height*newzoom - h,
+ (n_sy - h/2) / newzoom * newzoom));
+
+ /* Set the new zoom factor for the display. */
+ set_zoom (df_p, n_left, n_top, newzoom);
+
+ notify_post_event (ev_panner, NULL, NOTIFY_SAFE);
+}
+
+
+/* STOP_PAN -- Called to abort a pan. */
+static
+stop_pan()
+{
+ p_dx = p_dy = 0;
+}
+
+
+/* PANNER -- Called on a fast timer to do the panning. */
+static Notify_value
+ev_panner()
+{
+ register int left, top;
+
+ /* No clipping is done, so the imtool window must be exposed. */
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+
+ /* repaint (P_IMAGE|P_DONTCLIP); */
+ repaint (P_IMAGE);
+
+ if (p_dx < 0 && pw_rect.r_left <= p_left ||
+ p_dx > 0 && pw_rect.r_left >= p_left)
+ p_dx = 0;
+ else if (p_dx) {
+ left = pw_rect.r_left + p_dx;
+ if (p_dx < 0)
+ left = max (p_left, left);
+ else
+ left = min (p_left, left);
+ df_p->fb_xoff = pw_rect.r_left = left;
+ }
+
+ if (p_dy < 0 && pw_rect.r_top <= p_top ||
+ p_dy > 0 && pw_rect.r_top >= p_top)
+ p_dy = 0;
+ else if (p_dy) {
+ top = pw_rect.r_top + p_dy;
+ if (p_dy < 0)
+ top = max (p_top, top);
+ else
+ top = min (p_top, top);
+ df_p->fb_yoff = pw_rect.r_top = top;
+ }
+
+ if (p_dx == 0 && p_dy == 0) {
+ if (panning) {
+ panning = 0;
+ display_coords = p_svdc;
+ if (p_cursor_setback > 0) {
+ int sx = p_sx - pw_rect.r_left;
+ int sy = p_sy - pw_rect.r_top;
+ if (sx >= 0 && sx < pw_rect.r_width &&
+ sy >= 0 && sy < pw_rect.r_height)
+ gio_setcursorpos (sx, sy);
+ gio_setcursor (CURSOR_ON, 0);
+ gio_events();
+ }
+ repaint (P_GRAPHICS|P_COLORBAR);
+ }
+ } else {
+ /* Allow the window to process any pending events. */
+ gio_events();
+
+ /* Post another call to the panner. */
+ notify_post_event (ev_panner, NULL, NOTIFY_SAFE);
+ }
+
+ return (NOTIFY_DONE);
+}
+
+
+/* EDIT_FRAMEBUFFER -- Edit a frame buffer by operating upon it with the
+ * given pixrect and rasterop at the given location, clipping as necessary
+ * at the boundaries of the frame. Update the display window as well, if
+ * the frame being edited is the display frame.
+ */
+static
+edit_framebuffer (fb, x, y, pr, rop)
+struct framebuf *fb; /* frame to be edited */
+int x, y; /* left,top coords of rect to be edited */
+struct pixrect *pr; /* pixrect to be used */
+int rop; /* rasterop defining operation */
+{
+ int width = pr->pr_width, height = pr->pr_height;
+ int s_left = 0, s_top = 0;
+ int d_left = x, d_top = y;
+
+ /* Clip to the frame boundary. */
+ while (d_left < 0) {
+ s_left++;
+ width--;
+ d_left++;
+ }
+
+ while (d_left + width-1 > Fb_width)
+ width--;
+
+ while (d_top < 0) {
+ s_top++;
+ height--;
+ d_top++;
+ }
+
+ while (d_top + height-1 > Fb_height)
+ height--;
+
+ /* All done if there is nothing left after clipping. */
+ if (width*height <= 0)
+ return;
+
+ /* Edit the frame buffer (clobbers the display pixels). */
+ pr_rop (fb->fb_pr, d_left, d_top, width, height,
+ PIX_NOT(PIX_SRC) & PIX_DST, pr, s_left, s_top);
+
+ /* Refresh the display, if the current display frame is the
+ * same as the reference frame.
+ */
+ if (fb == df_p) {
+ BRect fb_r, pw_r;
+
+ fb_r.r_left = d_left * zoom;
+ fb_r.r_top = d_top * zoom;
+ fb_r.r_width = width * zoom;
+ fb_r.r_height = height * zoom;
+
+ Bpw_get_region_rect (gio_pw, &pw_rect);
+ Bpw_lock (gio_pw, &pw_rect);
+
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+
+ if (maprect (&fb_rect, &fb_r, &pw_rect, &pw_r))
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r)) {
+ ds_write (gio_pw,
+ pw_r.r_left, pw_r.r_top,
+ pw_r.r_width, pw_r.r_height,
+ PIX_SRC | PIX_COLOR(NGREY-1),
+ df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+
+ if (pw_r.r_top + pw_r.r_height >= pw_rect.r_height
+ - cb_height)
+ put_colorbar();
+ }
+
+ Bpw_unlock (gio_pw);
+ }
+}
+
+
+/* GIO_EVENTS -- Have the image window process any queued input events.
+ */
+static
+gio_events()
+{
+ Event event;
+ int fd, flags;
+
+ /* Allow the window to process any pending events. */
+ fd = (int) window_get (gio_canvas, WIN_FD);
+ flags = fcntl (fd, F_GETFL, 0);
+ fcntl (fd, F_SETFL, O_NDELAY);
+ while (window_read_event (gio_canvas, &event) != -1)
+ ev_gioinput (gio_canvas,
+ canvas_event(gio_canvas, &event), NULL, PANNER_EVENT);
+ fcntl (fd, F_SETFL, flags);
+}
+
+
+/* ICLEAR_PROC -- Clear the main (image) window.
+ */
+static
+iclear_proc()
+{
+ erase (df_p);
+}
+
+
+/* GCLEAR_PROC -- Clear the graphics overlay.
+ */
+static
+gclear_proc()
+{
+ df_p->fb_objno = 1;
+ repaint (P_IMAGE|P_COLORBAR);
+}
+
+
+/* SETFRAME_PROC -- Select the next frame for viewing.
+ */
+static
+setframe_proc()
+{
+ set_frame (0);
+}
+
+
+/* SET_FRAME -- Set the display frame. Call with frameno=0 to advance to
+ * the next frame, -N will yield the previous frame in sequence, and
+ * anything else is actual frame number.
+ */
+static
+set_frame (frameno)
+int frameno;
+{
+ if (frameno < 0) {
+ frameno = display_frame - 1;
+ if (frameno < 1)
+ frameno = fb_nframes;
+ } else if (frameno == 0) {
+ frameno = display_frame + 1;
+ if (frameno > fb_nframes)
+ frameno = 1;
+ } else {
+ if (frameno < 1)
+ frameno = 1;
+ else if (frameno > fb_nframes)
+ frameno = fb_nframes;
+ }
+
+ display_frame = frameno;
+ df_p = frames + (frameno - 1);
+ set_zoom (df_p, df_p->fb_xoff, df_p->fb_yoff, df_p->fb_xzoom);
+
+ set_colortable();
+ set_transfer_function (gio_pw, df_p->fb_center, df_p->fb_slope);
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ panel_set_value (pan_set_maptype, df_p->fb_maptype);
+
+ repaint (P_IMAGE|P_COLORBAR|P_GRAPHICS);
+}
+
+
+/* SET_ZOOM -- Change the zoom factor for the referenced frame to the given
+ * value. If the referenced frame is the display frame, update the global
+ * display zoom factors as well.
+ */
+static
+set_zoom (fr, left, top, newzoom)
+register struct framebuf *fr; /* frame to be zoomed */
+int left, top; /* new left and top for frame */
+int newzoom; /* new zoom factor */
+{
+ register struct ctran *ct;
+ int fb_zoom, i;
+
+ /* Verify valid zoom factor. */
+ newzoom = max(zooms[0], min(zooms[nzooms-1], newzoom));
+ for (i=0; i < nzooms; i++)
+ if (zooms[i] == newzoom) {
+ zoom_index = i;
+ break;
+ }
+
+ /* Set the new frame buffer zoom factor. */
+ if ((fb_zoom = fr->fb_xzoom) != newzoom) {
+ ct = &fr->fb_ctran;
+ ct->a = ct->a * fb_zoom / newzoom;
+ ct->d = ct->d * fb_zoom / newzoom;
+
+ /* For Apply a 0.5 pixel correction when zooming, to make the
+ * center of the pixel have integral coordinates (coord X,Y where
+ * X and Y are integral will always be the center of a pixel).
+ * This should be turned off for zoom=1, since there is no
+ * subpixel resolution, or if the image has already been zoomed
+ * at the host level.
+ */
+ if (abs(ct->a * newzoom) < 1.01) {
+ if (fb_zoom == 1)
+ ct->tx += (ct->a > 0) ? -0.5 : 0.5;
+ else if (newzoom == 1)
+ ct->tx += (ct->a > 0) ? 0.5 : -0.5;
+ }
+ if (abs(ct->d * newzoom) < 1.01) {
+ if (fb_zoom == 1)
+ ct->ty += (ct->d > 0) ? -0.5 : 0.5;
+ else if (newzoom == 1)
+ ct->ty += (ct->d > 0) ? 0.5 : -0.5;
+ }
+
+ fr->fb_xzoom = fr->fb_yzoom = newzoom;
+ }
+
+ /* Offsets must be aligned to an unzoomed frame buffer pixel
+ * boundary since this constraint is applied in ds_write.
+ */
+ fr->fb_xoff = max(0, min(Fb_width*newzoom - pw_rect.r_width,
+ left / newzoom * newzoom));
+ fr->fb_yoff = max(0, min(Fb_height*newzoom - pw_rect.r_height,
+ top / newzoom * newzoom));
+
+ /* If the referenced frame is the display frame, make the new zoom
+ * factor global.
+ */
+ if (fr == df_p) {
+ fb_width = Fb_width * newzoom;
+ fb_height = Fb_height * newzoom;
+
+ fb_rect.r_top = 0;
+ fb_rect.r_left = 0;
+ fb_rect.r_width = fb_width;
+ fb_rect.r_height = fb_height;
+
+ pw_rect.r_left = df_p->fb_xoff;
+ pw_rect.r_top = df_p->fb_yoff;
+
+ zoom = newzoom;
+ }
+}
+
+
+/* ERASE -- Clear a frame.
+ */
+static
+erase (fr)
+struct framebuf *fr;
+{
+ register int *op, v, n;
+ unsigned char *cp;
+ int val;
+
+ for (val=0, n=sizeof(int), cp = (unsigned char *)&val; --n >= 0; )
+ *cp++ = background;
+
+ if (val) {
+ op = (int *) mpr_d(fr->fb_pr)->md_image;
+ n = Fb_width * Fb_height / sizeof(int);
+ for (v=val; --n >= 0; )
+ *op++ = v;
+ } else
+ bzero ((char *)mpr_d(fr->fb_pr)->md_image, Fb_width * Fb_height);
+
+ if (fr == df_p)
+ repaint (P_IMAGE|P_COLORBAR);
+}
+
+
+/* REPAINT -- Repaint the display window.
+ */
+static
+repaint (what)
+int what;
+{
+ if (what & P_IMAGE) {
+ BRect fb_r, pw_r;
+ int rop;
+
+ pw_r = pw_rect;
+ pw_r.r_left = pw_r.r_top = 0;
+ if (what & P_COLORBAR)
+ pw_r.r_height -= cb_height;
+
+ rop = PIX_SRC | PIX_COLOR(NGREY-1);
+ if (what & P_DONTCLIP)
+ rop |= PIX_DONTCLIP;
+
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r))
+ if (maprect (&fb_rect, &fb_r, &pw_rect, &pw_r))
+ ds_write (gio_pw,
+ pw_r.r_left, pw_r.r_top, pw_r.r_width, pw_r.r_height,
+ rop, df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+
+ if (display_coords && state == TRACK_CURSOR) {
+ set_wcsboxpos();
+ pw_text (gio_pw, wc_xoff, wc_yoff + wc_font->pf_defaultsize.y,
+ PIX_NOT(PIX_SRC), wc_font, wc_text);
+ }
+ }
+
+ if (what & P_COLORBAR)
+ put_colorbar();
+
+ if ((what & P_GRAPHICS) && df_p->fb_objno > 1) {
+ char fname[SZ_FNAME];
+
+ wcs_update (df_p);
+ sprintf (fname, o_fname, df_p->fb_frameno, df_p->fb_imageno);
+ strcpy (fname, getfname(fname, 0));
+ remark_objects (fname);
+ }
+}
+
+
+/* REFRESH_DISPLAY -- Called by the windowing system when the display needs
+ * to be refreshed from the frame buffer.
+ */
+static
+refresh_display (canvas, pw, rl)
+Canvas canvas;
+Pixwin *pw;
+Rectlist *rl;
+{
+ register struct rectnode *rn;
+ BRect fb_r, pw_r;
+ Rect rect, r;
+
+ /* See if any damage has occurred and fix it. */
+ rl_rectoffset (rl, &rl->rl_bound, &rect);
+ pw_lock (pw, &rect);
+
+ /* Now fix all the damage. Regions of the display window which are
+ * not mapped onto the frame buffer are not fixed up at present.
+ * Scale changes (zoom/dezoom) are not currently immplemented.
+ */
+ for (rn = rl->rl_head; rn; rn = rn->rn_next) {
+ rl_rectoffset (rl, &rn->rn_rect, &r);
+ pw_r.r_left = r.r_left;
+ pw_r.r_top = r.r_top;
+ pw_r.r_width = r.r_width;
+ pw_r.r_height = r.r_height;
+
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r))
+ if (maprect (&fb_rect, &fb_r, &pw_rect, &pw_r))
+ ds_write (pw,
+ pw_r.r_left, pw_r.r_top, pw_r.r_width, pw_r.r_height,
+ PIX_SRC | PIX_COLOR(NGREY-1),
+ df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+ }
+
+ put_colorbar();
+ pw_unlock (pw);
+}
+
+
+/* DS_WRITE -- Write to the display. This is analogous to pw_write, except
+ * that pixel replication or subsampling is performed as indicated by the
+ * zoom factors for the current display frame. At present, the zoom factor
+ * may not be specified independently for x and y.
+ */
+static
+ds_write (pw, left, top, width, height, rop, fb_pr, fb_left, fb_top)
+Pixwin *pw;
+int left, top;
+int width, height;
+int rop;
+Pixrect *fb_pr;
+int fb_left, fb_top; /* zoomed frame buffer coords */
+{
+ register unsigned char pix, *ip, *op;
+ register int n, p;
+ unsigned char *otop, *fb, *lp;
+ int pr_left, pr_top, i, j;
+ Pixrect *mpr_line, *pr;
+ struct rect pw_r;
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ /* If no zoom, just copy the frame buffer rect to the screen. */
+ if (zoom <= 1) {
+ pw_write (pw, left,top, width,height, rop, fb_pr, fb_left,fb_top);
+ return;
+ }
+
+ /* Zoom - magnify the image by pixel replication. (This assumes an
+ * 8 bit frame buffer and screen pixrect).
+ */
+ mpr_line = mem_create (width, 1, pw->pw_pixrect->pr_depth);
+ fb = (unsigned char *)mpr_d(df_p->fb_pr)->md_image;
+ lp = (unsigned char *)mpr_d(mpr_line)->md_image;
+ otop = lp + width;
+
+ /* Lock the frame buffer to avoid scribbling on other windows
+ * during write to raw screen..
+ */
+ pw_get_region_rect (pw, &pw_r);
+ pw_lock (pw, &pw_r);
+
+ pr = pw->pw_pixrect;
+ pr_left = left + (int)window_get(gio_frame, WIN_X) + TOOL_BORDERWIDTH;
+ pr_top = top + (int)window_get(gio_frame, WIN_Y) +
+ HEIGHTADJUST - TOOL_BORDERWIDTH;
+
+ for (j=0, i=(fb_top/zoom); j < height; j += zoom) {
+ ip = fb + (i++ * Fb_width) + (fb_left/zoom);
+ op = lp;
+
+ /* Replicate a block of pixels. */
+ switch (zoom) {
+ case 2:
+ for (n = (width/2); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 3:
+ for (n = (width/3); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 4:
+ for (n = (width/4); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 5:
+ for (n = (width/5); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 6:
+ for (n = (width/6); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 7:
+ for (n = (width/7); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ }
+ break;
+ case 8:
+ for (n = (width/8); --n >= 0; ) {
+ pix = *ip++;
+ *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ *op++ = pix; *op++ = pix;
+ }
+ break;
+ default:
+ for (n = (width/zoom); --n >= 0; ) {
+ pix = *ip++;
+ for (p=zoom; --p >= 0; )
+ *op++ = pix;
+ }
+ break;
+ }
+
+ /* Fill the last partial pixel. */
+ pix = *ip++;
+ while (op < otop)
+ *op++ = pix;
+
+ pr_replrop (pr, pr_left,pr_top+j,
+ width, min (height-j, zoom), rop, mpr_line, 0,0);
+ }
+
+ pw_unlock (pw);
+ pr_close (mpr_line);
+}
+
+
+/* PUT_COLORBAR -- Refresh the colorbar on the screen.
+ */
+static
+put_colorbar()
+{
+ if (cb_height)
+ pw_write (gio_pw,
+ 0, pw_rect.r_height - cb_height,
+ min (pw_rect.r_width, Fb_width), cb_height,
+ PIX_SRC, cb_pr, 0, 0);
+}
+
+
+/* TOGGLE_DISPLAYCOORDS -- Enable/disable continuous display of the cursor
+ * coordinates.
+ */
+static
+toggle_displaycoords (event)
+Event *event;
+{
+ BRect fb_r, pw_r;
+
+ if (display_coords) {
+ /* Enable mouse moved input events. */
+ window_set (gio_canvas, WIN_IGNORE_PICK_EVENTS, LOC_MOVE, 0,
+ 0);
+ display_coords = 0;
+
+ /* Refresh the region of the screen used to output coordinates
+ * from the frame buffer, erasing the coordinate output box.
+ */
+ pw_r.r_left = wc_xoff;
+ pw_r.r_top = wc_yoff;
+ pw_r.r_width = wc_width;
+ pw_r.r_height = wc_height;
+
+ if (maprect (&pw_rect, &pw_r, &fb_rect, &fb_r))
+ ds_write (gio_pw,
+ wc_xoff, wc_yoff, wc_width, wc_height,
+ PIX_SRC | PIX_COLOR(NGREY-1),
+ df_p->fb_pr, fb_r.r_left, fb_r.r_top);
+
+ } else {
+ /* Disable mouse moved input events. */
+ window_set (gio_canvas, WIN_CONSUME_PICK_EVENTS, LOC_MOVE, 0,
+ 0);
+ display_coords = 1;
+ update_coords (event);
+ }
+}
+
+
+/* UPDATE_COORDS -- Compute and output the world coordinates of the given
+ * event, using the WCS specified for the current display window. If called
+ * with event=NULL the most recent locator position is used.
+ */
+static
+update_coords (event)
+Event *event;
+{
+ register struct ctran *ct;
+ static struct timeval o_tv;
+ unsigned char *fb, *ip;
+ struct timeval n_tv;
+ int sx, sy, sz, fb_x, fb_y, delta_msec;
+ char buf[1024], ch;
+ float wx, wy, wz;
+
+ /* Get frame buffer x,y; ignore events that occur faster than we
+ * can update the coordinate readout.
+ */
+ if (event) {
+ /* Ignore event if it comes too soon. */
+ n_tv = event_time(event);
+ if (o_tv.tv_sec) {
+ delta_msec = ((n_tv.tv_sec - o_tv.tv_sec) * 1000 +
+ (n_tv.tv_usec - o_tv.tv_usec) / 1000);
+ if (delta_msec < 50)
+ return;
+ }
+
+ /* Get the screen (window relative) coordinates of the event. */
+ sx = event_x(event) + pw_rect.r_left;
+ sy = event_y(event) + pw_rect.r_top;
+
+ } else {
+ sx = last_sx + pw_rect.r_left;
+ sy = last_sy + pw_rect.r_top;
+ }
+
+ /* Get frame buffer pixel value. */
+ fb = (unsigned char *) mpr_d (df_p->fb_pr)->md_image;
+ fb_x = max(0, min(Fb_width, sx/zoom));
+ fb_y = max(0, min(Fb_height, sy/zoom));
+ sz = fb[fb_y*Fb_width+fb_x];
+ if (sz < CMS_DATASTART || sz > CMS_DATAEND)
+ sz = 0;
+
+ /* Compute the world coordinates of the event. */
+ ct = wcs_update (df_p);
+
+ if (ct->valid) {
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+
+ if (sz == 0)
+ wz = 0.0;
+ else {
+ switch (ct->zt) {
+ case W_LINEAR:
+ wz = ((sz - CMS_DATASTART) * (ct->z2 - ct->z1) /
+ (CMS_DATARANGE-1)) + ct->z1;
+ break;
+ default:
+ wz = sz;
+ break;
+ }
+ }
+
+ } else {
+ wx = sx;
+ wy = sy;
+ wz = sz;
+ }
+
+ /* Get the font to be used. */
+ if (wc_font == NULL) {
+ static char fontname[] = TEXT_FONT;
+ if ((wc_font = pf_open (fontname)) == NULL)
+ fprintf (stderr, "cannot open %s\n", fontname);
+ wc_xoff = wc_yoff = 0;
+ }
+
+ ch = ' ';
+ if (sz && ct->valid) {
+ if (ct->z1 < ct->z2) {
+ if (wz < (ct->z1 + 0.01))
+ ch = '-';
+ else if (wz > (ct->z2 - 0.01))
+ ch = '+';
+ } else if (ct->z1 > ct->z2) {
+ if (wz < (ct->z2 + 0.01))
+ ch = '-';
+ else if (wz > (ct->z1 - 0.01))
+ ch = '+';
+ }
+ }
+
+ set_wcsboxpos();
+ sprintf (buf, ct->format, wx, wy, wz, ch);
+ strncpy (wc_text, buf, SZ_WCTEXT);
+ pw_text (gio_pw, wc_xoff, wc_yoff + wc_font->pf_defaultsize.y,
+ PIX_NOT(PIX_SRC), wc_font, wc_text);
+
+ if (event)
+ o_tv = n_tv;
+}
+
+
+/* MARK_OBJECT -- Called when the user has clicked on the position of an
+ * object to be marked and/or added to the output coordinate list.
+ */
+static
+mark_object (event)
+Event *event;
+{
+ register struct ctran *ct;
+ char tx_buf[SZ_FNAME];
+ char fname[SZ_FNAME];
+ int sx, sy, newset;
+ float wx, wy;
+ FILE *fp;
+
+ ct = wcs_update (df_p);
+
+ /* Get name of coordinate output file for the current frame. */
+ sprintf (tx_buf, o_fname, df_p->fb_frameno, df_p->fb_imageno);
+ strcpy (fname, getfname(tx_buf, 0));
+
+ /* Append to the existing coordinate list, if any. If appending
+ * to an existing coordinate list, the existing list is displayed
+ * and the objno counter is left set to the next object to be added.
+ */
+ if (newset = (df_p->fb_objno <= 1))
+ remark_objects (fname);
+ if ((fp = fopen (fname, "a")) == NULL) {
+ fprintf (stderr, "cannot open %s for appending\n", fname);
+ return;
+ }
+
+ /* Timestamp the first entry in the output file. */
+ if (newset)
+ timestamp (fp);
+
+ /* Get the screen (window relative) coordinates of the event. */
+ sx = event_x(event) + pw_rect.r_left;
+ sy = event_y(event) + pw_rect.r_top;
+
+ /* Compute the world coordinates of the event. */
+ if (ct->valid) {
+ wx = ct->a * sx + ct->c * sy + ct->tx;
+ wy = ct->b * sx + ct->d * sy + ct->ty;
+ } else {
+ wx = sx;
+ wy = sy;
+ }
+
+ /* Mark the object position on the screen. */
+ sprintf (tx_buf, "%d", df_p->fb_objno++);
+ draw_text (sx - pw_rect.r_left, sy - pw_rect.r_top, tx_buf);
+
+ fprintf (fp, "%g %g\n", wx, wy);
+ fclose (fp);
+}
+
+
+/* REMARK_OBJECTS -- Read a object list file and mark the numbered objects
+ * therein.
+ */
+static
+remark_objects (fname)
+char *fname;
+{
+ register struct ctran *ct;
+ register char *ip;
+ char lbuf[SZ_LINE], tx_buf[SZ_FNAME];
+ int sx, sy, objno=1;
+ float wx, wy;
+ char *fgets();
+ FILE *fp;
+
+ gclear_proc();
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ if ((fp = fopen (fname, "r")) == NULL)
+ return;
+
+ while (fgets (lbuf, SZ_LINE, fp) != NULL) {
+ /* Skip comment lines and blank lines. */
+ for (ip=lbuf; *ip == ' ' || *ip == '\t'; ip++)
+ ;
+ if (*ip == '\n' || *ip == '#')
+ continue;
+ if (!isdigit (*ip) && *ip != '-')
+ continue;
+ if (sscanf (ip, "%f%f", &wx, &wy) < 2)
+ continue;
+
+ sx = wx;
+ sy = wy;
+
+ /* Compute the world coordinates of the event if we have a valid
+ * WCS transform (rotations not permitted)/
+ */
+ ct = wcs_update (df_p);
+ if (ct->valid) {
+ if (abs(ct->a) > .001)
+ sx = (wx - ct->tx) / ct->a;
+ if (abs(ct->d) > .001)
+ sy = (wy - ct->ty) / ct->d;
+ }
+
+ /* Mark the object position on the screen. */
+ sprintf (tx_buf, "%d", objno++);
+ draw_text (sx - pw_rect.r_left, sy - pw_rect.r_top, tx_buf);
+ }
+
+ fclose (fp);
+
+ /* If we are updating an existing coordinate list from a newly
+ * displayed frame, timestamp the new section of the list.
+ */
+ if (df_p->fb_objno <= 1)
+ if ((fp = fopen (fname, "a")) != NULL) {
+ timestamp (fp);
+ fclose (fp);
+ }
+
+ df_p->fb_objno = objno;
+}
+
+
+/* TIMESTAMP -- Timestamp the output stream.
+ */
+static
+timestamp (fp)
+FILE *fp;
+{
+ register char *op;
+ char obuf[SZ_LINE];
+ long clock;
+
+ clock = time(0);
+ fprintf (fp, "# %s", asctime(localtime(&clock)));
+
+ sprintf (obuf, "# %s", df_p->fb_label);
+ for (op=obuf; *op && *op != '\n'; op++)
+ ;
+ *op++ = '\n';
+ *op = '\0';
+ fputs (obuf, fp);
+}
+
+
+/* DRAW_TEXT -- Draw some text on the frame at the indicated position.
+ */
+static
+draw_text (x, y, text)
+int x, y; /* position where text is to be drawn */
+char *text; /* the text */
+{
+ static struct pixfont *font = NULL;
+
+ /* Get the screen font to be used. */
+ if (font == NULL) {
+ static char fontname[] = MARK_FONT;
+ if ((font = pf_open (fontname)) == NULL)
+ fprintf (stderr, "cannot open %s\n", fontname);
+ }
+
+ /* Draw the text. */
+ if (o_revtext)
+ pw_text (gio_pw, x, y, PIX_NOT(PIX_SRC) & PIX_DST, font, text);
+ else
+ pw_text (gio_pw, x, y, PIX_SRC | PIX_DST, font, text);
+}
+
+
+/* WCS_UPDATE -- Load the screen WCS, if not yet validated, from the user
+ * wcs file, if any.
+ *
+ * File format (two lines):
+ *
+ * image title (imtool header label string)\n
+ * a b c d tx ty z1 z2 zt
+ *
+ * NOTE: the WCS text is now passed in via the data stream as a write to the
+ * subunit WCS and left in the buffer "wcsbuf", rather than being passed via
+ * a text file.
+ */
+static struct ctran *
+wcs_update (fr)
+struct framebuf *fr;
+{
+ register struct ctran *ct = &fr->fb_ctran;
+ char buf[1024], *format;
+
+ /* Get the new WCS. */
+ if (!ct->valid) {
+ fr->fb_label[0] = '\0';
+ ct->zt = W_UNITARY;
+
+ /* Attempt to read the WCS file and set up a unitary transformation
+ * if the file cannot be read.
+ */
+ if (sscanf (wcsbuf[fr->fb_frameno-1], "%[^\n]\n%f%f%f%f%f%f%f%f%d",
+ buf, &ct->a, &ct->b, &ct->c, &ct->d, &ct->tx, &ct->ty,
+ &ct->z1, &ct->z2, &ct->zt) < 7) {
+
+ if (wcsbuf[fr->fb_frameno-1][0])
+ fprintf (stderr, "imtool: error reading WCS file\n");
+
+ strncpy (ct->imtitle, "[NO WCS]\n", SZ_IMTITLE);
+ ct->a = ct->d = 1;
+ ct->b = ct->c = 0;
+ ct->tx = ct->ty = 0;
+ ct->zt = W_UNITARY;
+
+ } else
+ strncpy (ct->imtitle, buf, SZ_IMTITLE);
+
+ /* Correct for the current zoom factor, if any. */
+ if (fr->fb_xzoom > 1) {
+ if (abs(ct->a) < 1.01)
+ ct->tx += (ct->a > 0) ? -0.5 : 0.5;
+ if (abs(ct->d) < 1.01)
+ ct->ty += (ct->d > 0) ? -0.5 : 0.5;
+ ct->a = ct->a / fr->fb_xzoom;
+ ct->d = ct->d / fr->fb_xzoom;
+ }
+
+ window_set (gio_frame, FRAME_LABEL, framelabel(), 0);
+ ct->valid++;
+ }
+
+ /* Determine best format for wcs output. */
+ if (ct->valid && ct->zt == W_LINEAR) {
+ float z1, z2, zrange;
+ z1 = ct->z1;
+ z2 = ct->z2;
+ zrange = (z1 > z2) ? z1 - z2 : z2 - z1;
+ if (zrange < 100.0 && (abs(z1) + abs(z2)) / 2.0 < 200.0)
+ format = " %7.2f %7.2f %7.3f%c";
+ else if (zrange > 99999.0 || (abs(z1) + abs(z2)) / 2.0 > 99999.0)
+ format = " %7.2f %7.2f %7.3g%c";
+ else
+ format = W_DEFFORMAT;
+ } else
+ format = " %7.2f %7.2f %7.0f%c";
+
+ strcpy (ct->format, format);
+ return (ct);
+}
+
+
+/* SET_WCSBOXPOS -- Set the position of the WCS output box.
+ */
+static
+set_wcsboxpos()
+{
+ /* Compute offset to coordinate output box. */
+ if ((wc_xoff + wc_yoff) == 0) {
+ wc_width = wc_font->pf_defaultsize.x * 25;
+ wc_height = wc_font->pf_defaultsize.y + 5;
+ wc_xoff = max (0, min (Fb_width, pw_rect.r_width) - wc_width
+ - TOOL_BORDERWIDTH);
+ wc_yoff = max (0, pw_rect.r_height - cb_height - wc_height
+ - TOOL_BORDERWIDTH);
+ }
+}
+
+
+/* FRAMELABEL -- Return a pointer to the frame label string for the current
+ * frame.
+ */
+static char *
+framelabel()
+{
+ char fname[SZ_FNAME];
+ char label[SZ_LABEL*2];
+
+ sprintf (fname, o_fname, df_p->fb_frameno, df_p->fb_imageno);
+ sprintf (label, "[%d] %s: %s", df_p->fb_frameno, fname,
+ df_p->fb_ctran.imtitle);
+ strncpy (df_p->fb_label, label, SZ_LABEL);
+
+ return (df_p->fb_label);
+}
+
+
+/* GETFNAME -- Construct the pathname of a user datafile. One optional
+ * integer argument is permitted.
+ */
+static char *
+getfname (rootname, arg)
+char *rootname; /* root filename (printf style format) */
+int arg;
+{
+ static char pathname[SZ_FNAME];
+ char fmt[SZ_LINE], *udir;
+
+ /* Were we passed an absolute pathname as input? */
+ if (*rootname == '/') {
+ strcpy (pathname, rootname);
+ return (pathname);
+ }
+
+ if ((udir = getenv ("WCSDIR")) == NULL)
+ if ((udir = getenv ("wcsdir")) == NULL)
+ if ((udir = getenv ("HOME")) == NULL)
+ udir = "/tmp";
+
+ sprintf (fmt, "%s/%s", udir, rootname);
+ sprintf (pathname, fmt, arg);
+
+ return (pathname);
+}
+
+
+/* GIO_SETCURSOR -- Set graphics frame cursor options.
+ */
+static
+gio_setcursor (op1, op2)
+int op1, op2;
+{
+ Cursor cursor;
+ int option[2], i;
+ int blink=cursor_blink, show=cursor_show;
+
+ /* Normalize the argument list. */
+ for (option[0]=op1, option[1]=op2, i=0; i < 2; i++)
+ switch (option[i]) {
+ case BLINK_OFF:
+ case BLINK_ON:
+ blink = option[i];
+ break;
+ case CURSOR_OFF:
+ case CURSOR_ON:
+ show = option[i];
+ break;
+ }
+
+ /* Do we need to change anything? */
+ if (blink == cursor_blink && show == cursor_show)
+ return;
+
+ /* Modify the cursor attributes. */
+ if (show == CURSOR_ON && reading_imcursor)
+ gio_readcursor (imcursor_wcs);
+ else {
+ cursor = window_get (gio_canvas, WIN_CURSOR);
+ cursor_set (cursor,
+ CURSOR_SHOW_CURSOR, FALSE,
+ CURSOR_SHOW_CROSSHAIRS, (show == CURSOR_ON),
+ CURSOR_CROSSHAIR_THICKNESS, 1,
+ CURSOR_CROSSHAIR_LENGTH, 20,
+ CURSOR_CROSSHAIR_GAP, 6,
+
+#ifdef sparc
+ /* This is a kludge to work around a bug with the
+ * sparcstation 1 under 4.0.3. */
+ CURSOR_CROSSHAIR_OP, PIX_SRC ^ PIX_DST,
+#else
+ CURSOR_CROSSHAIR_OP, PIX_SRC,
+#endif
+
+ CURSOR_CROSSHAIR_COLOR, CMS_CURSOR,
+ 0);
+ window_set (gio_canvas, WIN_CURSOR, cursor, 0);
+ }
+
+ cursor_blink = blink;
+ cursor_show = show;
+}
+
+
+/* GIO_SETCURSORPOS -- Set the position of the graphics cursor within the
+ * graphics frame.
+ */
+static
+gio_setcursorpos (x, y)
+int x, y; /* pixwin pixel coords */
+{
+ if (window_open)
+ window_set (gio_canvas, WIN_MOUSE_XY, last_bx=x, last_by=y, 0);
+}
+
+
+/* RESET_PROC -- Called from the setup panel to reset the state of the
+ * display.
+ */
+static
+reset_proc()
+{
+ register struct pixrect *pr = get_screen_rect();
+ register struct framebuf *fb;
+ register int i;
+
+ stop_pan();
+ blink = 0;
+ display_coords = 0;
+ setup_xoff = 4;
+ setup_yoff = 18;
+ wc_xoff = wc_yoff = 0;
+ state = TRACK_CURSOR;
+
+ for (i=0; i < fb_nframes; i++) {
+ fb = &frames[i];
+ fb->fb_xoff = 0;
+ fb->fb_yoff = 0;
+ fb->fb_xzoom = fb->fb_yzoom = 1;
+ fb->fb_center = fb_ngrey / 2.0;
+ fb->fb_slope = (float)white / (float)(fb_ngrey - 1);
+ fb->fb_maptype = MONO;
+ fb->fb_objno = 1;
+ fb->fb_imageno = 0;
+ fb->fb_frameno = i + 1;
+ }
+
+ gio_xsize = initial_gio_xsize;
+ gio_ysize = initial_gio_ysize;
+
+ gio_xsize = min (pr->pr_width - TOOL_BORDERWIDTH * 2, gio_xsize);
+ gio_ysize = min (pr->pr_height
+ - tool_headerheight ((int)window_get(gio_frame,FRAME_SHOW_LABEL))
+ - TOOL_BORDERWIDTH, gio_ysize);
+
+ window_set (gio_canvas,
+ WIN_WIDTH, gio_xsize,
+ WIN_HEIGHT, gio_ysize,
+ 0);
+
+ window_fit (gio_canvas);
+ window_fit (gio_frame);
+
+ set_frame (1);
+}
+
+
+/* MAPRECT -- Compute the intersection of the given subrect of the first rect
+ * with the second rect, in the coordinate system of the second. The rects
+ * are defined in screen coordinates, the subrects relative to their parent
+ * rects.
+ */
+maprect (r1, s1, r2, s2)
+BRect *r1, *s1; /* source rect and subrect */
+BRect *r2, *s2; /* destination rect and subrect */
+{
+ int xoff, yoff;
+ int x0, y0, x1, y1;
+
+ /* Compute offset of second rect from the first. */
+ xoff = r2->r_left - r1->r_left;
+ yoff = r2->r_top - r1->r_top;
+
+ /* Translate the first subrect into the coordinate system of the
+ * second rect.
+ */
+ x0 = s1->r_left - xoff;
+ y0 = s1->r_top - yoff;
+ x1 = x0 + s1->r_width - 1;
+ y1 = y0 + s1->r_height - 1;
+
+ /* Does the new subrect totally miss the second rect?
+ */
+ if (x1 < 0 || x0 >= r2->r_width || y1 < 0 || y0 >= r2->r_height) {
+ *s2 = *r2;
+ s2->r_width = s2->r_height = 0;
+ } else {
+ /* Clip the new subrect to the boundary of the second rect.
+ */
+ if (x0 < 0)
+ x0 = 0;
+ else if (x0 >= r2->r_width)
+ x0 = r2->r_width - 1;
+
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 >= r2->r_width)
+ x1 = r2->r_width - 1;
+
+ if (y0 < 0)
+ y0 = 0;
+ else if (y0 >= r2->r_height)
+ y0 = r2->r_height - 1;
+
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 >= r2->r_height)
+ y1 = r2->r_height - 1;
+
+ /* Compute the new subrect.
+ */
+ s2->r_left = x0;
+ s2->r_top = y0;
+ s2->r_width = x1 - x0 + 1;
+ s2->r_height = y1 - y0 + 1;
+ }
+
+ return (s2->r_width > 0 && s2->r_height > 0);
+}
+
+
+/* BPW_GET_REGION_RECT -- Get pw_rect, transforming a Rect to a BRect.
+ */
+static
+Bpw_get_region_rect (pw, br)
+Pixwin *pw;
+BRect *br;
+{
+ Rect r;
+
+ pw_get_region_rect (pw, &r);
+ br->r_left = r.r_left;
+ br->r_top = r.r_top;
+ br->r_width = r.r_width;
+ br->r_height = r.r_height;
+}
+
+
+/* BPW_LOCK -- Lock a big pixwin.
+ */
+static
+Bpw_lock (pw, br)
+Pixwin *pw;
+BRect *br;
+{
+ Rect r;
+
+ r.r_left = br->r_left;
+ r.r_top = br->r_top;
+ r.r_width = br->r_width;
+ r.r_height = br->r_height;
+
+ pw_lock (pw, &r);
+}
+
+
+/* BPW_UNLOCK -- Unlock a big pixwin.
+ */
+static
+Bpw_unlock (pw)
+Pixwin *pw;
+{
+ pw_unlock (pw);
+}
+
+
+/* IMAGECOPY_PROC -- Make a hardcopy of the image window on the laserwriter.
+ * We don't do this immediately, but rather after a delay of a few milliseconds
+ * to allow the window system to restore the imtool window lookup table after
+ * the mouse button is released.
+ */
+static
+imagecopy_proc()
+{
+ static Notify_value ev_screendump();
+
+ window_set (gio_frame, WIN_SHOW, TRUE, 0);
+ imt_pause (100, ev_screendump);
+}
+
+
+/* EV_SCREENDUMP -- Called after the specified interval has passed to carry out
+ * the actual screendump operation.
+ */
+static Notify_value
+ev_screendump()
+{
+ int depth = 8;
+
+ edit_colormap();
+
+ if (snap_frame_too) {
+ screendump (
+ (int) window_get (gio_canvas, WIN_FD),
+ win_get_pixwin (gio_canvas),
+ (int) window_get (gio_frame, WIN_WIDTH),
+ (int) window_get (gio_frame, WIN_HEIGHT),
+ (int) window_get (gio_frame, WIN_X),
+ (int) window_get (gio_frame, WIN_Y),
+ depth);
+ } else {
+ screendump (
+ (int) window_get (gio_canvas, WIN_FD),
+ win_get_pixwin (gio_canvas),
+ (int) window_get (gio_frame, WIN_WIDTH) - TOOL_BORDERWIDTH * 2,
+ (int) window_get (gio_frame, WIN_HEIGHT) -
+ HEIGHTADJUST - cb_height,
+ (int) window_get (gio_frame, WIN_X) + TOOL_BORDERWIDTH,
+ (int) window_get (gio_frame, WIN_Y) + HEIGHTADJUST -
+ TOOL_BORDERWIDTH,
+ depth);
+ }
+
+ return (NOTIFY_DONE);
+}
+
+
+/* IMT_PAUSE -- Suspend output for the indicated number of milliseconds, to
+ * allow other event processing to catch up.
+ */
+imt_pause (msec, ufcn)
+int msec;
+Notify_value (*ufcn)();
+{
+ static struct itimerval itimer_delay;
+
+ itimer_delay.it_interval.tv_usec = 0;
+ itimer_delay.it_interval.tv_sec = 0;
+ itimer_delay.it_value.tv_usec = (msec % 1000) * 1000;
+ itimer_delay.it_value.tv_sec = (msec / 1000);
+
+ notify_set_itimer_func ((int)ufcn, ufcn, ITIMER_REAL,
+ &itimer_delay, NULL);
+}
+
+
+/* PRINT_USAGE -- Print instructions on how to use this window tool.
+ */
+static
+print_usage (toolname)
+char *toolname;
+{
+ printf ("no on-line help text yet for IMTOOL\n");
+}
diff --git a/unix/sun/imtool.cross b/unix/sun/imtool.cross
new file mode 100644
index 00000000..2e1b3532
--- /dev/null
+++ b/unix/sun/imtool.cross
@@ -0,0 +1,4 @@
+/* Format_version=1, Width=16, Height=16, Depth=1, Valid_bits_per_item=16
+ */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0240,0x0180,
+ 0x0180,0x0240,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
diff --git a/unix/sun/imtool.cursor b/unix/sun/imtool.cursor
new file mode 100644
index 00000000..b1041fe9
--- /dev/null
+++ b/unix/sun/imtool.cursor
@@ -0,0 +1,4 @@
+/* Format_version=1, Width=16, Height=16, Depth=1, Valid_bits_per_item=16
+ */
+ 0x0000,0x03E0,0x0FF8,0x1C1C,0x380E,0x3006,0x6003,0x6003,
+ 0x6003,0x6003,0x6003,0x3006,0x380E,0x1C1C,0x0FF8,0x03E0
diff --git a/unix/sun/imtool.h b/unix/sun/imtool.h
new file mode 100644
index 00000000..e0048a28
--- /dev/null
+++ b/unix/sun/imtool.h
@@ -0,0 +1,13 @@
+/* IMTOOL.H -- Global definitions for IMTOOL.
+ */
+#define CURSOR_OFF 3 /* turn cursor off entirely */
+#define CURSOR_ON 4 /* turn it back on */
+
+struct fonttab { /* Imtool font descriptor. */
+ short pointsize;
+ char ch_xsize, ch_ysize;
+ short win_xsize, win_ysize;
+ struct pixfont *pixfont;
+ char *path;
+ char *label;
+};
diff --git a/unix/sun/imtool.icon b/unix/sun/imtool.icon
new file mode 100644
index 00000000..5857c1d0
--- /dev/null
+++ b/unix/sun/imtool.icon
@@ -0,0 +1,66 @@
+/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
+ */
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0FFF,0xFFFF,0xFFFF,0xFFF0,
+ 0x0AAA,0xAAAA,0xAAAA,0xAAB0,
+ 0x0D55,0x5555,0x5555,0x5550,
+ 0x0AAA,0xAAAA,0xAAAA,0xAAB0,
+ 0x0D55,0x5555,0x5555,0x5550,
+ 0x0AAA,0xAAAA,0xAAAA,0xAAB0,
+ 0x0D55,0xFFFF,0xFFD5,0x5550,
+ 0x0AAE,0x0000,0x003A,0xBEB0,
+ 0x0D58,0x0000,0x0015,0x6350,
+ 0x0AB0,0x0000,0x000A,0xC1B0,
+ 0x0D50,0x0000,0x000D,0x4150,
+ 0x0AA7,0xE7C1,0x07E6,0xC1B0,
+ 0x0D61,0x8663,0x8605,0x6350,
+ 0x0AA1,0x8663,0x8606,0xBEB0,
+ 0x0D61,0x8666,0xC605,0x5550,
+ 0x0AA1,0x87C6,0xC7C6,0xAAB0,
+ 0x0D61,0x86CC,0x6605,0x5550,
+ 0x0AA1,0x866F,0xE606,0xBEB0,
+ 0x0D61,0x866C,0x6605,0x6350,
+ 0x0AA7,0xE66C,0x6606,0xC1B0,
+ 0x0D60,0x0000,0x0005,0x4150,
+ 0x0AA0,0x0000,0x0006,0xC1B0,
+ 0x0D60,0x0000,0x0005,0x6350,
+ 0x0AA0,0x07E6,0x6006,0xBEB0,
+ 0x0D60,0x0186,0x6005,0x5550,
+ 0x0AA0,0x0186,0x6106,0xAAB0,
+ 0x0D60,0x0182,0x4105,0x5550,
+ 0x0AA0,0x0183,0xC7C6,0xAAB0,
+ 0x0D60,0x0183,0xC105,0x5550,
+ 0x0AA0,0x0181,0x8106,0xAAB0,
+ 0x0D60,0x0181,0x8005,0xF7D0,
+ 0x0AB0,0x0181,0x8007,0x1C70,
+ 0x0D50,0x0000,0x000D,0x1450,
+ 0x0AA8,0x0000,0x001B,0x1C70,
+ 0x0D56,0x0000,0x0035,0xF7D0,
+ 0x0AAB,0xFFFF,0xFFEA,0xAAB0,
+ 0x0D55,0x5555,0x5555,0x5550,
+ 0x0AAA,0xAAAA,0xAAAA,0xAAB0,
+ 0x0D55,0x5555,0x5555,0x5550,
+ 0x0AAA,0xAAAA,0xAAAA,0xAAB0,
+ 0x0D55,0x5555,0x5555,0x5550,
+ 0x0FFF,0xFFFF,0xFFFF,0xFFF0,
+ 0x0001,0x8000,0x0000,0x6000,
+ 0x0001,0x8000,0x0000,0x6000,
+ 0x0001,0x8000,0x0000,0x6000,
+ 0x0003,0x0000,0x0000,0x3000,
+ 0x0003,0x0000,0x0000,0x3000,
+ 0x0006,0x0000,0x0000,0x1800,
+ 0x0006,0x0000,0x0000,0x1800,
+ 0x000C,0x0000,0x0000,0x0C00,
+ 0x000C,0x0000,0x0000,0x0C00,
+ 0x0018,0x0000,0x0000,0x0600,
+ 0x0018,0x0000,0x0000,0x0600,
+ 0x0038,0x0000,0x0000,0x0700,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000
diff --git a/unix/sun/imtool.icon.NEW b/unix/sun/imtool.icon.NEW
new file mode 100644
index 00000000..2eff06f0
--- /dev/null
+++ b/unix/sun/imtool.icon.NEW
@@ -0,0 +1,34 @@
+/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
+ */
+ 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x88D5,0x5555,0x5555,0x5509,
+ 0xA2BF,0xFFFF,0xFFFF,0xFEA3,0xA260,0x0000,0x0000,0x0523,
+ 0x88A7,0xFE00,0xFFFF,0xF689,0x8864,0x0200,0xFFFE,0xF509,
+ 0xA2A7,0xFE00,0xF80F,0xB6A3,0xA260,0x0000,0xEBFF,0xF523,
+ 0x88A0,0x1FFF,0xF7E1,0xF689,0x8860,0x1000,0xF61E,0xF509,
+ 0xA2A0,0x17FE,0xF6FD,0x36A3,0xA260,0x1000,0xE6FF,0xB523,
+ 0x88A0,0x17FF,0xEE47,0xB689,0x886F,0xF000,0xE711,0xB509,
+ 0xA2A8,0x17FC,0xB3FD,0x96A3,0xA268,0x1000,0xFBFD,0xD523,
+ 0x88A8,0x37E0,0xFDB9,0x9689,0x8868,0x7000,0xEEFB,0xB509,
+ 0xA2A8,0x57FE,0xE607,0xB6A3,0xA268,0x5000,0xFBFF,0x7523,
+ 0x88A8,0x5400,0xFC6C,0x7689,0x8868,0x5400,0xDFC9,0xF509,
+ 0xA2A8,0x5000,0xFFFF,0xF6A3,0xA268,0x5000,0x0800,0x0523,
+ 0x88A8,0x5FFF,0xF800,0x0689,0x8868,0x441D,0x0000,0x7589,
+ 0xA2A8,0x8275,0x0000,0x56A3,0xA268,0x81C1,0x0000,0x7563,
+ 0x88A8,0x8081,0x0000,0x06B9,0x886B,0x8001,0x0007,0x750F,
+ 0xA2A8,0x0001,0x0005,0x56A3,0xA26F,0xFFFF,0x0007,0x7523,
+ 0x88A0,0x0000,0x0000,0x068B,0x887F,0xFFFF,0xFFFF,0xFD0F,
+ 0xA2AA,0xAAAA,0xAAAA,0xAAAF,0xA255,0x5555,0x5555,0x5533,
+ 0x88AA,0xAAAA,0xAAAA,0xAAA9,0x8848,0x8895,0x5548,0x88E9,
+ 0xA222,0x222A,0xAAA2,0x2263,0xA222,0x2215,0x5562,0x22A3,
+ 0x8888,0xAAAA,0xAAAA,0x8FFD,0x8888,0x5555,0x5555,0x0DB7,
+ 0xA222,0xAAAA,0xAAAA,0xADB7,0xA222,0x2222,0x2222,0x2B6F,
+ 0x8888,0x8888,0x8888,0x9B6D,0x8888,0x8888,0x8888,0x9FF9,
+ 0xA222,0x2222,0x2222,0x3FFB,0xA222,0x2222,0x2222,0x3FFB,
+ 0x8888,0x8888,0x8888,0xBFF9,0x8000,0x0000,0x0000,0xBFF9,
+ 0xA100,0x0000,0x00C2,0x3FF3,0xA000,0x0800,0x0042,0x2223,
+ 0x830D,0x9E1C,0x3840,0x8889,0x810A,0x8822,0x4440,0x8889,
+ 0xA10A,0x8822,0x4442,0x2223,0xA10A,0x8822,0x4442,0x2223,
+ 0x810A,0x8922,0x4440,0x8889,0x810A,0x861C,0x3840,0x8889,
+ 0xA000,0x0000,0x0002,0x2223,0xA000,0x0000,0x0002,0x2223,
+ 0x8888,0x8888,0x8888,0x8889,0x8888,0x8888,0x8888,0x8889,
+ 0xA222,0x2222,0x2222,0x2223,0xFFFF,0xFFFF,0xFFFF,0xFFFF
diff --git a/unix/sun/imtool.man b/unix/sun/imtool.man
new file mode 100644
index 00000000..f4af4d1c
--- /dev/null
+++ b/unix/sun/imtool.man
@@ -0,0 +1,713 @@
+.\" @(#)imtool.1 1.1 14-Aug-87 DCT
+.TH IMTOOL 1 "29 June 1989"
+.SH NAME
+imtool \- image display server for the SunView environment
+.SH SYNOPSIS
+.B imtool
+[
+.B \-fbconfig \fIN\fP
+]
+[
+.B \-raster \fIfilename\fP
+]
+.ti +0.5i
+[
+.B \-maptype
+\fB\fR(\fPmono \fR|\fP linear \fR|\fP random \fR|\fP crandom\fR)
+]
+.ti +0.5i
+[
+.B \-\fR[\fPno\fR]\fPcolorbar\fR
+]
+[
+.B \-white \fR|\fP \-black\fR
+]
+[
+\fIframeargs\fR
+]
+.SH OPTIONS
+.TP
+\fB\-fbconfig \fIN\fP
+Specifies the initial frame buffer configuration (specifying the number and
+size of the internal frame buffers) for the server.
+The set of acceptable frame buffer configurations is defined in a private
+or public \fIimtoolrc\fR file (discussed below).
+.TP
+\fB\-raster \fIfilename\fP
+Specifies that the server is to start up with the image in the standard
+Sun raster format file \fIfilename\fR already loaded into frame buffer 1.
+The size of the specified raster need not match the size of the frame
+buffer configuration specified with the \fI\-fbconfig\fR argument.
+.TP
+\fB\-maptype \fR(\fPmono \fR|\fP linear \fR|\fP random \fR|\fP crandom\fR)\fP
+Specifies the type of greyscale mapping to be used.
+\fBmono\fR means render the image in black and white (shades of gray).
+\fBlinear\fR means render the image in linear pseudocolor, with the range of
+displayed pixel values corresponding to a range of colors from black to blue
+to green to red to white.
+\fBrandom\fR means render the image in random pseudocolor, with a random
+color being assigned to each greyscale value.
+\fBcrandom\fR means render the image in random pseudocolor, updating the
+colortable every second or so while the mouse is in the display window.
+.TP
+\fB\-\fR[\fPno\fR]\fPcolorbar\fR
+Specifies whether or not a colorbar is to be displayed at the bottom of the
+display window. The colorbar graphically illustrates the relationship between
+pixel intensity and displayed color or greylevel, and is particularly useful
+with pseudocolor, or when windowing the display. The minimum pixel intensity
+(1) is at the left and the maximum (200) is at the right.
+.TP
+[\fB\-white\fR |\fP \-black\fR]
+Specifies whether the image is to be displayed on a white (default) or black
+background.
+.TP
+[\fIframeargs\fR]
+\fIimtool\fR also takes generic tool arguments, used to set the position,
+size, etc. of the display window; see \fIsuntools\fR(1) for a list of these
+arguments.
+.if t .sp 0.08i
+.SH DESCRIPTION
+.SS Image Display Server
+.LP
+\fIimtool\fR implements a simple image display server for the SunView window
+environment. The server runs as an independent process, managing the display
+window and listening for connections on a pseudodevice entry in \fB/dev\fR.
+All communications with the server are via a simple data stream protocol
+described below.
+.if t .sp 0.05i
+.SS Prototype Status
+.LP
+The current implementation of the imtool image display server is a limited
+prototype, and can be expected to change substantially in the future as the
+prototype continues to evolve. The main capabilities lacking in the current
+version are flexibility in lookup table control, overlaid graphics and text,
+miscellaneous functions such as split screen, and a fully interactive interface
+to applications programs.
+.if t .sp 0.05i
+.SS Basic Concepts
+.LP
+The display server consists of a set of N \fBframe buffers\fR and a single
+\fBdisplay window\fR. A frame buffer is a two dimensional array in memory
+into which the image to be displayed is loaded, and which is used to refresh
+the display window when portions of the window are uncovered.
+In the current prototype display server only 8 bit deep frame buffers are
+supported, but the number and size of the frame buffers is user configurable.
+.PP
+The display window is literally a window into the image stored in the frame
+buffer. While the size of the frame buffer is fixed (subject to periodic
+reconfiguration), the display window may be any size, and the size may be
+changed at any time without affecting the contents of the frame buffer.
+Likewise, the position of a small display window upon a larger frame buffer
+is arbitrary and may be changed at any time by \fBpanning\fR the window
+across the image. While there may be multiple frame buffers, there is only
+a single display window, and only a single image may be displayed at any one
+time, although a single keystroke suffices to change the frame being viewed.
+.PP
+With few exceptions, all display server functions are both \fIindependent\fR
+and \fIasynchronous\fR. Hence, one can display one frame while another is
+being loaded, or even resize or pan the display window and adjust the greyscale
+mapping of a frame while the frame is being loaded.
+.PP
+The primary function of the display server is to provide image display and
+interactive image oriented user interface capabilities to a concurrently
+executing client program via a bidirectional datastream interface.
+Hence, to make use of the display server for image display one also needs an
+applications program capable of talking to the server, and sending it image
+data to be displayed (a rudimentary builtin capability for displaying Sun
+raster files is however provided). The \fBdisplay\fR program in the IRAF
+\fBimages.tv\fR package is an example of such a client program.
+.if t .sp 0.05i
+.SS The Frame Menu
+The imtool frame menu provides the following selections:
+.if t .sp .05i
+.if n .sp
+.RS
+.IP \fBFrame\fR 15
+Displays the standard SunView frame menu.
+.IP \fBSetup\fR
+Displays (or hides) the imtool interactive setup panel.
+.IP \fBRegister\fR
+Adjusts the pan offset of all frame buffers to match that of the image
+currently being displayed. Normally, the individual frame buffers are
+independently panned.
+.IP "\fBBlink \fR[\fPon\fR|\fPoff\fR]"
+Turns frame blink (alternate display of a series of frames) on or off.
+Alternatively the \fBctrl/B\fR key may be used to manually cycle through
+the blink frames.
+.IP \fBFitFrame\fR
+Adjusts the size of the display window to display the entire frame buffer.
+.IP \fBNextFrame\fR
+Displays the next frame buffer in sequence.
+Used to cycle through and alternately display all frames
+(the \fBalternate\fR or \fBctrl/F\fR and \fBctrl/R\fR keys may also be used
+to cycle forward or reverse through the frames).
+.IP \fBGclear\fR
+Clear the graphics overlay of the frame currently being displayed.
+.IP \fBIclear\fR
+Clear the image, i.e., frame buffer, currently being displayed.
+.IP \fBImcopy\fR
+Make a hardcopy of the image window.
+.RE
+.if t .sp .05i
+.if n .sp
+.LP
+The image hardcopy output function is an entry point to the general screen
+capture facility, discussed in the next section. This is the same facility
+used by the \fIgterm\fR program.
+.if t .sp 0.05i
+.SS Hardcopy Output
+.LP
+The image hardcopy function produces a "what you see is what you get" bitmap
+of the rectangular region of the screen occupied by the display window.
+If the region of interest is partially covered by another window, then the
+hardcopy will be a picture of a partially covered window. Any interactive
+adjustment of the grayscale mapping will be reflected in the hardcopy output.
+.LP
+The screen capture software reads out the full memory of the workstation in
+the region of interest, and in the case of a color workstation, processes the
+screen pixels through the colortable to produce an image corresponding to what
+appears on the screen. No full color output options are currently provided,
+hence the average of the red, green, and blue color values is next computed.
+If rasterfile output is being generated, the raw pixel values and RGB color
+table entries are saved directly in the rasterfile, rather than applying the
+tables in software to produce a monochrome or bitmap image.
+.LP
+Two output options are currently provided, i.e., \fBPostscript\fR output
+suitable for output directly to a laser writer to produce the final graphics
+hardcopy, or \fBSun rasterfile\fR output. The default action is to output a
+Postscript program to the device "lw", e.g., the Apple Laserwriter
+(any 300 dpi Postscript device should do), using the dithering capability of
+Postscript to produce a pseudogreyscale representation of the 8 bit output
+image. These defaults may be changed by defining the following environment
+variables:
+.IP R_RASTERFILE
+If this variable is defined a Sun rasterfile will be generated, otherwise a
+Postscript plotfile is generated. The string value of the variable is a
+\fIprintf\fR style format string to be used to generate the filename of
+the rasterfile. If multiple rasterfiles are to be generated, the format
+string may contain a decimal integer field (e.g., "\fLframe.%d\fR") to be
+replaced by the \fIfile number\fR of the current rasterfile. The first file
+generated will be number zero, with the file number being incremented once
+for every rasterfile produced. If Postscript plotfile output is desired,
+the plotfile will be a uniquely named temporary file in \fB/tmp\fR.
+(Postscript output is text and you can read this file if you are curious what
+it looks like).
+.IP R_DISPOSE
+The string value of this variable is a \fIprintf\fR style format string with
+one string valued subfield to be replaced by the plotfile or rasterfile name,
+to be used to generate the command used to dispose of the output file.
+If this variable is not defined and the output file is a Postscript plotfile,
+the default format string \fL"lpr -Plw -r -s %s"\fR will be used.
+If the variable is not defined and the output file is a rasterfile,
+no action is taken. It is the responsibility of the dispose command to
+delete the output file.
+.LP
+It should only take several seconds to capture the screen and produce the
+output rasterfile or queue the Postscript job to the printer. The screen
+is flashed to indicated when the operation has completed (provided the user
+has not turned off the \fIvisible bell\fR feature in their SunView defaults
+startup file).
+.PP
+The Postscript processing time is usually several minutes (of laserwriter time).
+Since most Postscript printers are interfaced via a serial interface at 9600
+baud, data compression is used to reduce the amount of data to be transmitted,
+and the current bottleneck is the processing speed of the Postscript engine
+itself, which does all the dithering and coordinate transformations.
+(This is true for bitmaps, but the data compression algorithm used is not
+very effective for 8 bit image data, and the serial interface can still be
+a bottleneck in this case).
+.if t .sp 0.05i
+.SS The Setup Panel
+.LP
+The setup panel is used to interactively modify imtool options.
+Two types of selections are provided, \fImultiple choice\fR selections,
+and \fIpush button\fR selections. Clicking on a multiple choice selection
+cycles through the choices (left mouse button) or displays the choices as
+a menu (right mouse button).
+String valued options are modified by clicking on the old value, rubbing out
+all or part of the old value if necessary, and then typing in a new value
+followed by return. If there several string valued fields in a panel,
+return may be used to cycle through the fields.
+Clicking on a push button (use the left mouse button) "pushes" the button,
+causing the action indicated on the button to be executed.
+.LP
+The multiple choice options in the setup panel are the following:
+.IP "\fBNumber of frame buffers\fR"
+Specifies the number of frame buffers for which space is currently allocated.
+The number of frame buffers may be changed at any time. If a client program
+references a frame which does not yet exist, the number of frame buffers will
+automatically be increased, hence it is not necessary to preallocate space
+for the frame buffers.
+.IP "\fBGreyscale mapping\fR"
+Specifies the method to be used to map pixel intensity values to RGB color
+intensity values.
+\fBmono\fR specifies that the image is to be rendered in shades of grey.
+\fBlinear pseudocolor\fR specifies that the image is to be rendered in
+pseudocolor, with pixel intensities mapped into the range of colors from
+black to blue to green to red to white.
+\fBrandom pseudocolor\fR assigns a random color to each possible pixel
+greylevel.
+\fBcontinuous random pseudocolor\fR is like random pseudocolor,
+but the colors are changed every few seconds, where the interval
+between color changes is specified by the \fBrate\fR option below.
+.IP "\fBRate (sec) for Crandom Option\fR
+Specifies the frequency (1 to 32 seconds) with which new colors are to be
+assigned for the continuous random pseudocolor option.
+.IP "\fBBackground color\fR
+Specifies the frame background color, i.e., the color to which the frame
+will be set when cleared (black or white), or when displaying an image which
+fills only part of the frame, the color of the portion of the frame buffer
+which has not been written into.
+.IP "\fBInclude Frame Border in Imcopy\fR
+Specifies whether the frame border, including the frame label, is to be
+included in image hardcopies. By default the frame border is included since
+the frame label is often used to identify the displayed image.
+If the frame border is excluded then so is the colormap, if any.
+.IP "\fBShow colorbar\fR
+Specifies whether or not a colorbar is to be shown at the bottom of the
+display window.
+.IP "\fBBlink rate (sec)\fR
+When blink is enabled, specifies the amount of time a single frame is to
+be displayed. The value may range from 1/2 second to 32 seconds.
+The maximum blink rate may be achieved by holding down the \fBalternate\fR
+key long enough to cause autorepeat.
+.IP "\fBFrames to be blinked\fR
+The string value of this parameter specifies the list of frames to be blinked.
+The special value "all" may be specified to blink all frames in sequence.
+If the frames are explicitly listed, the same frame may appear in the list
+multiple times.
+.IP "\fBZoom factors\fR
+Specifies the positive integer zoom factors to be used for the zoom and pan
+function. Up to eight zoom factors may be specified (more would
+be undesirable due to the time required to cycle through the zooms to get
+back to an unzoomed image). The default zoom factors are "1 2 4 8";
+depending upon the application, one might want to add, e.g., a zoom factor
+of 3, or a large zoom factor of 32 or 64 to make it easy to identify
+individual pixels.
+.IP "\fBCoordinate list output file\fR
+Specifies the name of the file to be used for cursor lists (text files
+containing lists of object coordinates - see below).
+.IP "\fBRaster filename (load/save)\fR
+Specifies the filename of the Sun rasterfile to be loaded into the current
+frame buffer (\fBload\fR push button, below), or the filename into which
+to current frame is to be written (\fBsave\fR push button).
+.LP
+The following "push buttons" are also provided in the setup panel.
+Many of these are equivalent to the comparable selections in the frame menu.
+.RS
+.IP "\fBRegister Frames\fR" 15
+Adjust the pan offset of all frames to match that of the current frame.
+.IP "\fBFit Window\fR"
+Adjust the size of the display window to match that of the frame buffer.
+.IP \fBReset\fR
+Reset the display. The mapping type, transfer function, and pan offsets are
+restored to their initial values, but the contents of the frames are not
+affected.
+.IP \fBIclear\fR
+Clear the frame buffer currently being displayed.
+.IP \fBGclear\fR
+Clear the graphics overlay (may also be cleared whenever the display window
+is refreshed).
+.IP \fBLoad\fR
+Load the Sun rasterfile named by the \fIRaster filename\fR panel string
+parameter into the frame buffer currently being displayed.
+If the rasterfile is smaller than
+the frame buffer the raster will be loaded into the upper left corner of
+the frame buffer. If the rasterfile is larger than the frame buffer part of
+the raster will be discarded. Any colortable information present in the
+rasterfile is ignored.
+.IP \fBSave\fR
+Save the contents of the frame buffer currently being displayed in the
+Sun rasterfile named by the \fIRaster filename\fR panel string parameter.
+The new raster will be the same size as the frame buffer. No colortable,
+WCS, or any information other than the pixel values is saved in the rasterfile.
+.IP \fBBlink\fR
+Turn frame blink on or off.
+.IP \fBFrame\fR
+Display the next frame in sequence. Equivalent to the \fBNextFrame\fR frame
+menu selection, and to the \fBalternate\fR and \fBctrl/F\fR keys.
+.IP \fBQuit\fR
+Close the setup panel.
+.RE
+
+.if t .sp 0.05i
+.SS Function Keys
+.LP
+The following function keys have special significance to \fIimtool\fR:
+.RS
+.IP F4 15
+Calls up the setup panel, or closes it if already displayed.
+.IP F5 15
+Causes the current cursor list file to be rewound and reread, marking all
+objects on the cursor list by drawing a number beside each object in the
+display window. Each object is marked by its ordinal number in the cursor
+list, ignoring comment lines and blank lines. The precise object position
+is at the lower left corner of the first digit.
+Each time the cursor list is redrawn the color of the digits toggles between
+black and white, making it possible for the user to manually "blink" the
+object numbers, or select the representation which provides the best visibility
+for their data. Note that objects are marked only in the display window,
+i.e., the frame buffer is not modified, hence the numerals will be lost
+whenever the display is refreshed.
+.IP F6
+Enables and disables \fBcursor readout mode\fR. While cursor readout mode is
+in effect a box is displayed in the lower right corner of the display window,
+in which the coordinates and corresponding pixel intensity at the position of
+the the mouse cursor are continuously updated as the mouse is moved.
+The cursor coordinates are given in \fIworld coordinates\fR if a WCS (world
+coordinate system) has been defined for the frame, otherwise display window
+relative pixel coordinates and display pixel intensity values are given.
+If the pixel intensity is saturated (set to the extreme high or low value),
+a + or - is appended to the printed value to flag the value as saturated.
+.IP F7
+Hitting this key while the mouse is in the display window causes an image
+hardcopy to be generated. This is equivalent to selecting the \fBimagecopy\fR
+item in the frame menu, except that it can be done without moving the mouse.
+This may be important to avoid changing the greyscale mapping, which also
+depends upon the mouse position.
+.RE
+.LP
+Note that the mouse must be in the display window for these function keys to
+have any effect.
+.if t .sp 0.05i
+.SS Mouse Buttons
+.LP
+The mouse buttons are used with \fIimtool\fR as follows:
+.RS
+.IP "Left Button" 15
+In cursor readout mode, used to mark objects, adding each object to the cursor
+list for the current frame. Ignored when not in cursor readout mode.
+
+.IP "Middle Button"
+The middle button on the mouse is the \fBpan/zoom\fR button.
+If the pan button is held down and released at a position in the display
+window, the object under the cursor will be moved to the center of the
+display. \fBcontrol-pan\fR is the same except that the image will pan
+smoothly to the new position, rather than all at once. \fBshift-pan\fR
+causes the image to be panned in the indicated direction in large steps.
+Shift and control may be combined to smoothly pan in large steps.
+.IP
+The \fBzoom\fR function is also controlled by the middle mouse button.
+Placing the mouse on an object and pressing pan/zoom once causes the object
+to be moved to the center of the display; pressing the button again causes
+the image to be zoomed about the mouse position. Repeated presses without
+moving the mouse cycle through the predefined set of zoom factors until the
+cycle wraps around and the unzoomed image is restored. Zoom is almost as
+fast as a normal unzoomed window refresh, so there is no problem with,
+for example, panning on a zoomed image.
+.IP
+If the middle button is held down while in cursor readout mode the mouse may
+be moved without updating the displayed cursor coordinates. This is useful
+when moving the mouse to a different window, e.g., to type the displayed
+object coordinates into an application running in some other window.
+An attempt to pan while already at the edge of the frame is ignored.
+.IP "Right Button"
+Used to interactively adjust the greyscale mapping (colortable) for the window.
+.RE
+.LP
+To window the display, i.e., adjust the \fBtransfer function\fR for the
+window, hold the right mouse button down and move the cursor about within the
+window. Zero contrast (one greylevel) is at the center of the window,
+with positive contrast above, negative contrast below,
+and contrast increasing the further the mouse is moved from the centerline.
+Moving the mouse to the left or right adjusts the greyscale range to lower
+or higher intensities. The colorbar provides a graphic display of the effect
+of the transfer function. If the right mouse button is pressed and then
+released without moving the mouse the transfer function will be adjusted
+according to the position of the mouse. By alternately displaying several
+frames and tapping the right mouse button for each frame without moving the
+mouse, the transfer functions of several frames may be matched.
+.if t .sp 0.05i
+.SS Frame Buffer Configuration Files
+.LP
+While the prototype display server does support dynamic reconfiguration of
+the frame buffers, allowing multiple frame buffers of virtually any size,
+it is currently necessary to define the possible frame buffer configurations
+at startup time. This is done via a table file called the \fBimtoolrc\fR
+file. An example illustrating the contents of such a file is shown below.
+Note that this may differ from the default configuration file used at your
+site.
+.sp
+.nf
+ 1 2 512 512 # imt1|imt512
+ 2 2 800 800 # imt2|imt800
+ 3 2 1024 1024 # imt3|imt1024
+ 4 1 1600 1600 # imt4|imt1600
+ 5 1 2048 2048 # imt5|imt2048
+ 6 1 4096 4096 # imt6|imt4096
+ 7 1 4096 1024 # imt7|imt4x1
+ 8 1 1024 4096 # imt8|imt1x4
+ 9 2 1144 880 # imt9|imtfs full screen (1152x900 minus frame)
+ 10 2 1144 764 # imt10|imtfs35 full screen at 35mm film aspect ratio
+ 11 2 128 128 # imt11|imt128
+ 12 2 256 256 # imt12|imt256
+
+ 20 2 388 576 # imt20|imtgec GEC CCD detector format
+ 21 1 3040 976 # imt21|imtkpca KPCA detector format (also 2D-Frutti)
+ 22 1 128 1520 # imt22|imt2df1 2D-Frutti
+ 23 1 256 1520 # imt23|imt2df2 2D-Frutti
+ 24 1 512 1520 # imt24|imt2df5 2D-Frutti
+ 25 1 960 1520 # imt25|imt2df9 2D-Frutti
+ 26 1 512 800 # imt26|imtcryo Cryogenic Camera
+ 27 1 348 800 # imt27|imtgcam Gold Camera
+ 28 1 976 3040 # imt28|imt2df9x3 2D-Frutti
+.fi
+.LP
+Each entry in the file contains four numbers, the configuration number
+(e.g., as used in \fI\-fbconfig N\fR), the number of frames to be created
+initially (frames may be added or deleted once the configuration is
+specified), and the width and height of the frame in screen pixels.
+Blank lines and comments are ignored.
+Note that \fIthe frame width must evenly divisible by 4\fR,
+due to alignment restrictions on memory pixrects in SunView.
+.LP
+Selection of the frame buffer configuration to be used is done at run time
+by the client application program when a frame is loaded. Since the frame
+buffer size to be used is controlled by the client application program rather
+than by the server, there is no entry in the setup panel for changing the
+configuration. Rather, it is expected that a command will be provided
+at the applications level for specifying the frame buffer size to be used.
+In the case of IRAF, this is done with a command such as
+\fBreset stdimage = imt800\fR, where the logical device name used on the right
+is given in the comments in the configuration table above, and must
+correspond to an equivalent entry in the IRAF \fBgraphcap\fR file.
+The imtoolrc file may be customized by the user for special applications,
+if desired, but a custom version of the graphcap file will be required too.
+.LP
+The frame buffer configuration file may be located in a default public
+directory, e.g., \fB/usr/local/lib/imtoolrc\fR, or the user may have a
+private version of the file, e.g., \fB.imtoolrc\fR. During startup, imtool
+looks first for an environment variable IMTOOLRC defining the pathname of
+the imtoolrc file, then it checks for a .imtoolrc file in the user's login
+directory, and finally it checks for the file /usr/local/lib/imtoolrc.
+If none of these are found, a default configuration of a single 512 square
+frame buffer is used.
+.if t .sp 0.05i
+.SS World Coordinate Systems
+.LP
+\fIimtool\fR provides a simple mechanism for associating a linear \fBworld
+coordinate system\fR (WCS) with the displayed image. A WCS consists of an
+image title string to be displayed in the frame label, a rotation matrix
+expressing the translation from window pixel coordinates (zero-indexed, origin
+in the upper left corner of the display window) to \fIworld coordinates\fR,
+e.g., the image pixel coordinates of the displayed image, and a pair of
+image intensity values defining the transformation between display server
+pixel intensity units (range 1-200 currently) and image pixel intensity units.
+.LP
+The WCS for a frame is passed to imtool as a set-WCS command in the datastream
+input from the client process (applications display program).
+A sample WCS descriptor is shown below.
+.if t .sp 0.03i
+.if n .sp
+.RS
+.nf
+dev$pix - m51 B 600s
+.br
+1. 0. 0. -1. 1. 512. 0. 1481.635 1
+.fi
+.RE
+.if t .sp 0.03i
+.if n .sp
+.LP
+The first line is simply a line of text to be displayed in the frame label
+when the WCS is read. This should normally contain the name of the image
+and a few words describing the image being displayed.
+.LP
+The first six numbers in the second line define a rotation matrix specifying
+the translation from window pixel coordinates to world coordinates, according
+to the following relations:
+.if t .sp 0.03i
+.if n .sp
+.RS
+.nf
+x' = ax + cy + tx
+.br
+y' = bx + dy + ty
+.fi
+.RE
+.if t .sp 0.03i
+.if n .sp
+The coefficients of the transformation matrix are given in the order
+\fBa b c d tx ty\fR. The example defines the image pixel coordinates for a
+512 square image, displayed with the origin at [1,1] in the lower left corner
+of the display window. Note that the world coordinates are flipped in Y
+and shifted to an origin at [1,1].
+.LP
+The final three numbers on the second line define the transformation from
+display pixel intensity units to image pixel intensity units. The first two
+values specify the image pixel intensities corresponding to display pixel
+intensities 1 and 200. The third number is an integer defining the type
+of transformation used; currently the value must be 1, indicating a linear
+transformation.
+.LP
+The WCS is initially undefined when a new frame is created.
+The first cursor or function key event thereafter
+which attempts to use WCS information will cause the WCS information to be
+interpreted if it has been passed in via the set-WCS datastream command.
+You will know if the server succeeds in reading the WCS because the
+\fBframe label\fR will change when the WCS is read.
+The first field of the frame label is the frame name, which will be the
+filename of any cursor list files created for the frame.
+.if t .sp 0.05i
+.SS Coordinate List Files
+.LP
+Cursor readout mode provides a convenient means of displaying the cursor
+coordinates, but the coordinates are lost as soon as the cursor is moved.
+To permanently record the positions of objects of interest one may generate
+a \fIcoordinate list file\fR by marking objects with the mouse.
+The contents of existing list files may also be displayed, and one may append
+to an existing list. List files generated by foreign programs may easily
+be displayed.
+.LP
+A coordinate list file is a simple text file with the X-Y coordinate pairs of
+objects recorded on successive lines in the file.
+The object number is not recorded explicitly, but is determined by the
+relative position of a coordinate pair within the file, ignoring comment
+lines (#...) and blank lines. List files may be edited and redisplayed if
+desired, and although the object numbers may change they will always agree
+with whatever is shown on the screen. A hardcopy of the screen may be made
+to provide a pictorial record of the contents of a list file, e.g., so that
+objects may be referred to by number if the list is used as input to another
+program.
+.LP
+The filename of the list file associated with the displayed frame is shown in
+the frame label, e.g., "frame.1". By default the list file will be created
+in the WCSDIR, i.e., in the directory defined by the user environment variable
+WCSDIR.
+.PP
+These defaults may be overridden by entering via the setup panel the
+\fIprintf\fR style format string to be used to construct the filename of the
+list file. If the name given begins with a / an absolute pathname is assumed
+and the filename is not modified in any way. Otherwise, a directory prefix
+is prepended, e.g., "$wcsdir/frame.\fIN\fP".
+The directory prefix is specified by the value of the WCSDIR environment
+variable if defined, otherwise the user's UNIX login directory is assumed.
+The frame number will appear in the generated filename only if a numeric
+format is included in the format string, e.g., \fBframe.%d\fR to specify
+a frame number file extension.
+.if t .sp 0.05i
+.SS Colortable Usage
+.LP
+To permit simultaneous viewing of both the display window and any other
+windows under SunView, \fIimtool\fR uses only a portion of the 256 element
+hardware colortable. Image pixels range in value from 1 to 200 (colortable
+entry 0 is reserved for the background or foreground color by SunView).
+Additional colortable entries are used for the cursor color and a set of
+graphics overlay colors, reserving 50 or so colortable entries for use by
+other windows. The graphics colortable entries are in the range 202-217.
+These are assigned predefined colors, which are unaffected by windowing the
+display. The graphics colortable assignments are summarized below.
+.if t .sp 0.03i
+.RS
+.nf
+202 black 206 blue 210 coral 214 orchid
+203 white 207 yellow 211 maroon 215 turquoise
+204 red 208 cyan 212 orange 216 violet
+205 green 209 magenta 213 khaki 217 wheat
+.fi
+.RE
+.if t .sp 0.03i
+.PP
+When imtool is started it reads the hardware color table, which is assumed to
+contain the entries for the other windows on the screen, replaces entries
+1 through 200+, and uses the resultant table to update the hardware colortable
+thereafter at a rate of twice a second. It is necessary to continuously
+update the hardware colortable to permit image display while the mouse is not
+in the display window, however, this scheme will cause colortable conflicts
+f one attempts to run a second window tool which also uses a large number of
+colortable entries.
+.if t .sp 0.05i
+.SS Hints for Blinking Frames
+.LP
+Blink is most effective when used to compare two or more frames which are very
+similar, e.g., a frame and an edited version of the same frame. For blink to
+be most effective the images should be displayed as similarly as possible.
+Start by loading the two frames using the same spatial and greyscale
+transformation for both frames (IRAF users should use the \fBrepeat\fR
+option to the \fIdisplay\fR task). Select one of the frames and adjust the
+window size, pan offset, and greyscale mapping as desired. After adjusting
+the greyscale with the mouse, repeatedly hit the \fBalternate\fR key followed
+by the right mouse button to match the greyscale mapping of the first frame to
+the remaining frames. Then select \fBregister\fR in the frame menu or setup
+panel to register all the frames, followed by \fBblink\fR to blink the frames.
+Blink works best if the display window is not too large, e.g., a 400 or 500
+pixel square window is fine.
+.if t .sp 0.05i
+.SS Data Stream Protocol
+.LP
+The display server is started like any other SunView tool by executing the
+\fIimtool\fR command either directly or indirectly via the \fB.suntools\fR
+file or via the mouse from the \fBrootmenu\fR. When first started a checkered
+test pattern is displayed, and the display server opens the pseudodevice
+files \fB/dev/imt1o\fR (used by the client to send commands and data to the
+display server) and \fB/dev/imt1i\fR (for data read back from the display
+server) and begins listening for commands from client programs.
+All commands and data are passed to the display server as a data stream by
+applications writing directly to /dev/imt1o.
+.LP
+The data stream protocol used
+in the prototype server (this will change) mimics that of the IIS Model 70
+image display, with an extension added for specifying the frame buffer
+configuration to be used. The server will automatically sense if the
+datastream is byte swapped, allowing use of the server with, for example,
+IRAF running on a large VAX compute server. Only byte packed data is
+supported.
+.LP
+Note that any process may write to the server, but if more
+than one process writes to the server at the same time, the output may be
+garbled. Also, multiple display servers may be spawned, but since only a
+single pseudodevice entry is currently supported, all such servers would be
+trying to read from the same input stream.
+.SH SEE ALSO
+suntools(1), gterm(1), images.tv.display(IRAF)
+.br
+\fIWindows and Window-Based Tools: Beginner's Guide\fR
+.SH ENVIRONMENT
+.IP IMTOOLRC 15
+The full pathname of the \fIimtoolrc\fR (frame buffer configuration) file
+to be used (optional).
+.IP WCSDIR
+The full pathname of the user directory
+into which imtool will write any files it creates that are
+not specified by a full pathname (alias \fBwcsdir\fR also permitted).
+.IP R_RASTERFILE
+If defined, a Sun rasterfile rather than Postscript file is generated by
+the \fIimcopy\fR function. The string value is a format used to generate
+the filename of the rasterfile.
+.IP R_DISPOSE
+The command to be executed to dispose of the Sun rasterfile or Postscript
+output file created by \fIimcopy\fR. If not defined and Postscript output is
+indicated (R_RASTERFILE also not defined), then the Postscript output file
+is disposed of via \fIlpr\fR to device \fIlw\fR.
+.SH FILES
+.LP
+.nf
+/dev/imt1[io]
+$WCSDIR/frame.*
+/usr/bin/suntools
+/usr/lib/rootmenu
+$iraf/local/sun/imtool.c
+.fi
+.SH BUGS
+.IP (1)
+The display server is continuously updating the hardware colortable even while
+the mouse is not in the display window. This is necessary to be able to see
+the image while the mouse is not in the window, but may interfere with other
+windows which also use many colortable entries. Under normal circumstances
+(only one greyscale window) this should not be a problem. If it is a problem,
+close the display window when not in use; updating of the hardware colortable
+is disabled while the display window is closed.
+.IP (2)
+Only a single display server should be used at any one time.
+Only a single client process should write to the server at any one time.
+.IP (3)
+The environment variables affecting IMTOOL operation must be defined before
+starting suntools if they are to have any effect.
+.IP (4)
+An error message is printed if imtool encounters a bad data header checksum,
+e.g., if synchronization is lost on the input datastream or if illegal data
+is input. Recovery from such an error can be difficult, possibly requiring
+killing the imtool and starting a new one. This error should not occur if the
+software (i.e., the client applications program) is functioning properly.
+.SH AUTHOR
+Doug Tody, National Optical Astronomy Observatories (NOAO), IRAF project.
diff --git a/unix/sun/imtool.square b/unix/sun/imtool.square
new file mode 100644
index 00000000..cb54f6eb
--- /dev/null
+++ b/unix/sun/imtool.square
@@ -0,0 +1,4 @@
+/* Format_version=1, Width=16, Height=16, Depth=1, Valid_bits_per_item=16
+ */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03C0,0x03C0,
+ 0x03C0,0x03C0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
diff --git a/unix/sun/imtoolrc b/unix/sun/imtoolrc
new file mode 100644
index 00000000..b20a9544
--- /dev/null
+++ b/unix/sun/imtoolrc
@@ -0,0 +1,48 @@
+# IMTOOL -- Defined frame buffer configurations. Note that the given nframes
+# is only a starting point, and may be modified during execution, hence smaller
+# values are preferred. The configuration numbers may be given in any order,
+# but must be unique and in the range 1-128. NOTE - corresponding entries must
+# be present in the dev$graphcap file, for use with IRAF.
+#
+# Format: configno nframes width height
+
+ 1 2 512 512 # imt1|imt512
+ 2 2 800 800 # imt2|imt800
+ 3 2 1024 1024 # imt3|imt1024
+ 4 1 1600 1600 # imt4|imt1600
+ 5 1 2048 2048 # imt5|imt2048
+ 6 1 4096 4096 # imt6|imt4096
+ 7 1 4096 1024 # imt7|imt4x1
+ 8 1 1024 4096 # imt8|imt1x4
+ 9 2 1144 880 # imt9|imtfs full screen (1152x900 minus frame)
+10 2 1144 764 # imt10|imtfs35 full screen at 35mm film aspect ratio
+11 2 128 128 # imt11|imt128
+12 2 256 256 # imt12|imt256
+13 2 128 1056 # imt13|imttall128 tall & narrow for spectro.
+14 2 256 1056 # imt14|imttall256 tall & wider for spectro.
+15 2 1056 128 # imt15|imtwide128 wide & thin for spectro.
+16 2 1056 256 # imt16|imtwide256 wide & fatter for spectro.
+
+# Some site specific formats for NOAO.
+20 2 388 576 # imt20|imtgec GEC CCD detector format
+21 1 3040 976 # imt21|imtkpca KPCA detector format (also 2D-Frutti)
+22 1 128 1520 # imt22|imt2df1 2D-Frutti
+23 1 256 1520 # imt23|imt2df2 2D-Frutti
+24 1 512 1520 # imt24|imt2df5 2D-Frutti
+25 1 960 1520 # imt25|imt2df9 2D-Frutti
+26 1 512 800 # imt26|imtcryo Cryogenic Camera
+27 1 348 800 # imt27|imtgcam Gold Camera
+28 1 976 3040 # imt28|imt2df9x3 2D-Frutti
+29 1 800 256 # imt29|imtgong Gong Cache Monitor
+30 1 256 800 # imt30|imtgong Gong Cache Monitor
+31 1 1240 400 # imt31|imtret Reticon CCD detector format
+32 2 832 800 # imt32|imtti|imtti2|imtti3
+33 2 544 512 # imt33|imtt5ha|imttek2
+34 1 1056 1024 # imt34|imtt1ka|imtte1k|imtst1k
+35 1 2080 2048 # imt35|imts2ka|imtt2kb|imtst2k|imtt2k2
+36 1 2048 2080 # imt36|imtt2ka|imtte2k
+37 1 3104 1024 # imt37|imtf3ka|imtfo3k
+
+# User added formats. (start with #64)
+# (add here)
+
diff --git a/unix/sun/mksuntool.csh b/unix/sun/mksuntool.csh
new file mode 100755
index 00000000..0c7a4175
--- /dev/null
+++ b/unix/sun/mksuntool.csh
@@ -0,0 +1,39 @@
+#! /bin/csh
+# MKSUNTOOL -- Configure the suntool subdirectory, used to link the suntools
+# executable. (Only used for SunOS versions prior to 4.0).
+
+# set echo
+
+unset noclobber
+unalias cd cmp echo ln mv rm sed set
+
+set OBJS = "imtool.o gterm.o gtermio.o screendump.o arrow.o notify_read.o"
+set sundir = /usr/src/sun/suntool
+
+if (! -e ./suntool) then
+ mkdir suntool
+endif
+cd suntool
+
+cmp -s Makefile $sundir/Makefile
+if ($status == 0 && `grep gterm basetools.h` != "") then
+ echo "suntool build directory is up to date"
+ exit 0
+else if (! -e $sundir/Makefile) then
+ echo "$sundir not found"
+ exit 1
+else
+ echo "rebuilding suntool subdirectory"
+endif
+
+set files = "`ls`"
+if ("$files" != "") then
+ rm -rf *
+endif
+(cd $sundir; tar -cf - . ) | tar -xpf -
+echo '"gterm",gterm_main,' >> basetools.h
+echo '"imtool",imtool_main', >> basetools.h
+echo '/cmdtool_main/i\' > Temp
+echo 'extern imtool_main();\' >> Temp
+echo 'extern gterm_main();' >> Temp
+sed -f Temp toolmerge.c > Temp2; mv -f Temp2 toolmerge.c; rm Temp
diff --git a/unix/sun/mouse.c b/unix/sun/mouse.c
new file mode 100644
index 00000000..a7d4b538
--- /dev/null
+++ b/unix/sun/mouse.c
@@ -0,0 +1,47 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <suntool/sunview.h>
+
+/* MOUSE.C -- Routines for saving and restoring the mouse position. These
+ * are used by a window which needs to grab the mouse and set it to a specific
+ * position, e.g., because the user has entered a command in to another window
+ * requesting a cursor read by the process running in the current window.
+ * (It was NOT easy to figure out how to do this in SunView, but at least I
+ * was able to do it...).
+ */
+
+/* GET_ABSMOUSEPOS -- Get the current position of the mouse in absolute screen
+ * coordinates.
+ */
+get_absmousepos (mywinfd, x, y)
+int mywinfd; /* any window on current screen will do */
+int *x, *y; /* mouse position (output) */
+{
+ struct screen rootscreen;
+ int rootfd;
+
+ win_screenget (mywinfd, &rootscreen);
+ rootfd = open (rootscreen.scr_rootname, 0);
+
+ *x = win_get_vuid_value (rootfd, LOC_X_ABSOLUTE);
+ *y = win_get_vuid_value (rootfd, LOC_Y_ABSOLUTE);
+
+ close (rootfd);
+}
+
+
+/* SET_ABSMOUSEPOS -- Set the mouse position in absolute screen coordinates.
+ */
+set_absmousepos (mywinfd, x, y)
+int mywinfd; /* any window on current screen will do */
+int x, y; /* desired mouse position */
+{
+ struct screen rootscreen;
+ int rootfd;
+
+ win_screenget (mywinfd, &rootscreen);
+ rootfd = open (rootscreen.scr_rootname, 0);
+ win_setmouseposition (rootfd, x, y);
+ close (rootfd);
+}
diff --git a/unix/sun/notify_read.c b/unix/sun/notify_read.c
new file mode 100644
index 00000000..b02913c9
--- /dev/null
+++ b/unix/sun/notify_read.c
@@ -0,0 +1,85 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <syscall.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdio.h>
+
+static int (*u_fcn)(); /* user functions to process read */
+static int u_fd; /* fd to be monitored */
+
+/* NOTIFY_READ -- This is a customized version of the SunView 3.2 notify_read
+ * primitive, the notifier's version of the UNIX read() system call (when
+ * the notifier is used, read() is a high level function, not a system call).
+ * The function of this special version of notify_read is to intercept kernel
+ * read calls made by the notifier for the purposes of monitoring, and
+ * possibly filtering, low level input from a file descriptor.
+ */
+notify_read (fd, buf, maxch)
+int fd;
+char *buf;
+int maxch;
+{
+ register int n;
+
+ /* This is a bit of a kludge, but lacking the shelltool source it
+ * was difficult to do better. The 18 is the size of the tty packet
+ * echoed by the driver when a character is typed; this is not part
+ * of the normal output stream so we exclude these events. The buf+1
+ * business is to hide the packet mode nature of the stream from the
+ * gtermio code; the first byte of each packet indicates the packet
+ * type. These details could change in a future Sun release in which
+ * case this code would have to be modified.
+ */
+ if (u_fcn && fd == u_fd && maxch != 18) {
+ n = syscall (SYS_read, fd, buf, maxch);
+ if (n > 0 && *buf == TIOCPKT_DATA)
+ return ((*u_fcn)(buf+1, n-1, maxch-1) + 1);
+ else
+ return (n);
+ } else
+ return (syscall (SYS_read, fd, buf, maxch));
+}
+
+
+/* READV -- This is a customized version of the readv system call, used in
+ * the Release 3.4 version of ttysw to read from the pty. Usage is (appears
+ * to be) identical to the old notify_read, except that the TIOCPKT byte is
+ * returned separately from the data.
+ */
+readv (fd, iov, iovcnt)
+register int fd;
+register struct iovec *iov;
+int iovcnt;
+{
+ register int n;
+
+ if (u_fcn && fd == u_fd && iovcnt == 2 && iov[0].iov_len == 1) {
+ n = syscall (SYS_readv, fd, iov, iovcnt);
+ if (n > 0 && *(iov[0].iov_base) == TIOCPKT_DATA)
+ return ((*u_fcn)(iov[1].iov_base, n-1, iov[1].iov_len) + 1);
+ else
+ return (n);
+ } else
+ return (syscall (SYS_readv, fd, iov, iovcnt));
+}
+
+
+/* NOTIFY_READ_POST_MONITOR_FCN -- Post a user data monitor/filter function
+ * to process the input on the specified file descriptor. Only one file
+ * descriptor can be monitored at present.
+ */
+notify_read_post_monitor_fcn (fd, fcn)
+int fd;
+int (*fcn)();
+{
+ if (u_fcn && !fcn)
+ return (-1);
+ else {
+ u_fcn = fcn;
+ u_fd = fd;
+ return (0);
+ }
+}
diff --git a/unix/sun/screendump.c b/unix/sun/screendump.c
new file mode 100644
index 00000000..a3582e57
--- /dev/null
+++ b/unix/sun/screendump.c
@@ -0,0 +1,549 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <suntool/sunview.h>
+#include <suntool/fullscreen.h>
+#include <pixrect/pr_planegroups.h>
+#include <stdio.h>
+#include <pwd.h>
+
+#define SEGSIZE 16 /* output segment size (bytes) */
+#define SEGBITS (SEGSIZE*8)
+#define BITFLIP 0 /* bit-flip each byte for P.S.? */
+#define NGREY 256 /* max color table size */
+#define PAGE_WIDTH 2550 /* 8.5x11, 300 dpi */
+#define PAGE_HEIGHT 3300 /* 8.5x11, 300 dpi */
+#define PAGE_XOFFSET 0 /* offset to drawing area */
+#define PAGE_YOFFSET 0 /* offset to drawing area */
+#define MARGIN 150 /* 1/2 inch margin */
+#define MAXGROUPS (PIXPG_OVERLAY+1)
+
+#define RT_DATA 'a' /* record type codes */
+#define RT_ZERO 'b'
+#define RT_FULL 'c'
+#define RT_BKG1 'd'
+#define RT_BKG2 'e'
+#define BKGPAT_1 "22" /* atom for stipple pattern */
+#define BKGPAT_2 "88" /* atom for stipple pattern */
+
+/* The following are provided by the calling program and specify what type
+ * of output is desired.
+ */
+extern int r_type; /* 0=postscript, 1=rasterfile */
+extern char r_dispose[]; /* dispose command */
+extern char r_filename[]; /* output file template */
+
+int gt_bitflip_postscript = BITFLIP;
+static unsigned char red[NGREY], green[NGREY], blue[NGREY];
+static void bitmap_to_postscript();
+static char *make_label();
+
+
+/* SCREENDUMP -- Make a hardcopy of the indicated region of the screen on a
+ * hardcopy device. Currently only two output formats are supported, Sun
+ * rasterfile output, or Postscript. A dispose command may be given to
+ * postprocess the output file, e.g., send it to the printer.
+ */
+screendump (win_fd, pw, width, height, left, top, nbits_out)
+int win_fd; /* window fd, for bell */
+struct pixwin *pw; /* arbitrary pixwin, used to lock display */
+int width, height; /* region to be printed: size, */
+int left, top; /* origin */
+int nbits_out; /* output 1 bit or 8 bit postscript image? */
+{
+ register int v, i, j;
+ register unsigned char *ip, *op;
+ unsigned char *pr_data, *obuf, *zero;
+
+ struct rect pw_r;
+ struct timeval tv_bell;
+ struct pixrect *screen, *s_pr, *o_pr, *m_pr;
+ static int filenum = 0;
+
+ float scale, xs, ys;
+ int overlay, ngroups;
+ char groups[MAXGROUPS];
+ char tempfile[80], dispose[80];
+ int depth, cache_v, cache_greyval, rasterout;
+ int status=0, bit, byte, pr_linebytes, ob_linebytes, fd;
+ char *str, *getenv();
+ FILE *fp;
+
+ /* Open the hardware frame buffer, create a memory pixrect to hold user
+ * specified rect, lock the display and read in the data and colormap.
+ * The Sun 3-110, 3/60, etc., have separate monochrome and color
+ * planes plus an overlay-enable plane, whereas the older frame buffers
+ * have only a single monochrome or color plane.
+ */
+ screen = pr_open ("/dev/fb");
+
+ depth = screen->pr_depth;
+ ngroups = pr_available_plane_groups (screen, MAXGROUPS, groups);
+ overlay = (ngroups >= PIXPG_OVERLAY);
+
+ /* Get memory pixrects to hold frame buffer data. */
+ zero = (unsigned char *) malloc (height);
+ s_pr = mem_create (width, height, depth);
+ if (overlay) {
+ o_pr = mem_create (width, height, 1);
+ m_pr = mem_create (width, height, 1);
+ }
+
+ /* Lock the frame buffer to avoid edits during readout. */
+ pw_get_region_rect (pw, &pw_r);
+ pw_lock (pw, &pw_r);
+
+ /* Get the color map and the main frame buffer pixrect. */
+ pr_getcolormap (screen, 0, NGREY, red, green, blue);
+ pr_set_plane_group (s_pr, PIXPG_8BIT_COLOR);
+ pr_rop (s_pr, 0, 0, width, height, PIX_SRC, screen, left, top);
+
+ /* If the device has an overlay plane, readout it out as well as the
+ * enable plane.
+ */
+ if (overlay) {
+ int o_pg;
+ o_pg = pr_get_plane_group (screen);
+ pr_set_plane_group (screen, PIXPG_OVERLAY);
+ pr_rop (o_pr,0,0, width, height,
+ PIX_SRC, screen, left, top);
+ pr_set_plane_group (screen, PIXPG_OVERLAY_ENABLE);
+ pr_rop (m_pr,0,0, width, height,
+ PIX_SRC, screen, left, top);
+ pr_set_plane_group (screen, o_pg);
+ }
+
+ pw_unlock (pw);
+ pr_close (screen);
+
+ /* Combine the color plane and overlay plane to produce a single
+ * color plane. Use only those overlay plane pixels which have their
+ * bit set in the enable plane.
+ */
+ if (overlay) {
+ pr_stencil (s_pr,0,0, width, height,
+ PIX_COLOR(255) | PIX_SRC | PIX_DONTCLIP, m_pr,0,0, o_pr,0,0);
+ pr_close (m_pr);
+ pr_close (o_pr);
+ }
+
+ /* Output can be either Postscript or a Sun rasterfile. Rasterfile
+ * output is handled here.
+ */
+ if (rasterout = (r_type && r_filename[0])) {
+ colormap_t cmap;
+
+ /* Setup colormap descriptor. */
+ cmap.type = RMT_EQUAL_RGB;
+ cmap.length = NGREY;
+ cmap.map[0] = red;
+ cmap.map[1] = green;
+ cmap.map[2] = blue;
+
+ /* Open raster file. */
+ sprintf (tempfile, r_filename, filenum++);
+ if ((fp = fopen (tempfile, "w")) == NULL) {
+ fprintf (stderr, "cannot create %s\n", tempfile);
+ return (-1);
+ }
+
+ pr_dump (s_pr, fp, &cmap, RT_STANDARD, 0);
+
+ fclose (fp);
+ pr_close (s_pr);
+ goto dispose_;
+ }
+
+ /* If the frame buffer is only 1 bit deep we can set obuf to point
+ * to the pixrect data and we are done. Otherwise we must process
+ * the image pixels through the color table and convert the output
+ * into a monochrome pixrect. NOTE: the bits may need to be flipped
+ * in the monochrome pixrect to satisfy Postscript; I wasn't sure.
+ */
+ if (depth == 1) {
+ /* This option is currently untested. */
+ obuf = (unsigned char *) mpr_d(s_pr)->md_image;
+ ob_linebytes = mpr_d(s_pr)->md_linebytes;
+
+ if (gt_bitflip_postscript) {
+ unsigned char flip[256];
+
+ /* Set up lookup table. */
+ for (j=0; j < 256; j++) {
+ for (v=0, i=0; i < 8; i++)
+ v |= (((j >> i) & 1) << (7-i));
+ flip[j] = v;
+ }
+
+ /* Bitflip and set the zero-line vector. */
+ for (j=0; j < height; j++) {
+ v = 1;
+ for (op=obuf+j*ob_linebytes, i=ob_linebytes; --i >= 0; ) {
+ if (v && *op)
+ v = 0;
+ *op++ = flip[*op];
+ }
+ zero[j] = v;
+ }
+ } else {
+ /* Set the zero-line vector for the pixrect. */
+ for (j=0; j < height; j++) {
+ v = 1;
+ for (op=obuf+j*ob_linebytes, i=ob_linebytes; --i >= 0; ) {
+ if (v && *op) {
+ v = 0;
+ break;
+ }
+ }
+ zero[j] = v;
+ }
+ }
+
+ } else if (nbits_out == 1) {
+ ob_linebytes = (width + 7) / 8;
+ obuf = (unsigned char *) calloc (ob_linebytes * height, 1);
+ if (obuf == NULL) {
+ fprintf (stderr, "out of memory\n");
+ return (-1);
+ }
+ pr_data = (unsigned char *) mpr_d(s_pr)->md_image;
+ pr_linebytes = mpr_d(s_pr)->md_linebytes;
+
+ for (j=0, cache_v=(-1); j < height; j++) {
+ ip = pr_data + j * pr_linebytes;
+ op = obuf + j * ob_linebytes;
+
+ for (byte=(-1), i=0; i < width; i++) {
+ if ((v = ip[i]) == cache_v)
+ v = cache_greyval;
+ else {
+ cache_v = v;
+ v = cache_greyval = (red[v] + green[v] + blue[v]) / 3;
+ }
+ if (v <= NGREY/2) {
+ byte = i / 8;
+ bit = 8 - (i % 8) - 1;
+ op[byte] |= (1 << bit);
+ }
+ }
+
+ /* Set flag if entire line is zero. */
+ zero[j] = (byte < 0);
+ }
+
+ pr_close (s_pr);
+
+ } else if (nbits_out == 8 && depth == 8) {
+ /* Eight bits out; transform the image in place in the input
+ * pixrect to save memory.
+ */
+ obuf = pr_data = (unsigned char *) mpr_d(s_pr)->md_image;
+ ob_linebytes = pr_linebytes = mpr_d(s_pr)->md_linebytes;
+
+ for (j=0, cache_v=(-1); j < height; j++) {
+ ip = pr_data + j * pr_linebytes;
+ op = obuf + j * ob_linebytes;
+
+ for (i=0; i < width; i++) {
+ if ((v = ip[i]) == cache_v)
+ v = cache_greyval;
+ else {
+ cache_v = v;
+ v = cache_greyval = (red[v] + green[v] + blue[v]) / 3;
+ }
+ op[i] = v;
+ }
+
+ /* Set flag if entire line is zero. */
+ zero[j] = 0;
+ }
+
+ } else {
+ fprintf (stderr, "can only create 1 bit or 8 bit output image\n");
+ return (-1);
+ }
+
+ /* Create the output file to hold postscript program. If no filename
+ * has been specified create a unique file in /tmp.
+ */
+ if (!r_filename[0]) {
+ strcpy (tempfile, "/tmp/psXXXXXX");
+ if ((fd = mkstemp (tempfile)) == -1) {
+ fprintf (stderr, "cannot create temporary file %s\n", tempfile);
+ return (-1);
+ } else
+ fp = fdopen (fd, "a");
+ } else {
+ sprintf (tempfile, r_filename, filenum++);
+ if ((fp = fopen (tempfile, "w")) == NULL) {
+ fprintf (stderr, "cannot create %s\n", tempfile);
+ return (-1);
+ }
+ }
+
+ /* Scale to fit output page. */
+ xs = (PAGE_WIDTH - MARGIN*2) / (float)width;
+ ys = (PAGE_HEIGHT - MARGIN*2) / (float)height;
+ scale = (xs < ys) ? xs : ys;
+
+ /* Translate the bitmap into a postscript program. */
+ bitmap_to_postscript (fp,
+ obuf, width, height, nbits_out, ob_linebytes, zero, scale);
+
+ free ((char *)zero);
+ if (depth == 1 || (depth == 8 && nbits_out == 8))
+ pr_close (s_pr);
+ else
+ free ((char *)obuf);
+
+ fclose (fp);
+ close (fd);
+
+ /* Dispose of tempfile to the printer. We leave it up to the dispose
+ * command to delete the temporary file when finished.
+ */
+dispose_:
+ if (r_dispose[0]) {
+ sprintf (dispose, r_dispose, tempfile);
+ if ((status = system (dispose)) != 0)
+ fprintf (stderr, "screendump: exit status %d\n", status);
+ }
+
+ /* Flash the screen to signal the user that we are done. */
+ tv_bell.tv_usec = 0*1000; tv_bell.tv_sec = 0;
+ win_bell (win_fd, tv_bell, pw);
+
+ return (status);
+}
+
+
+/* BITMAP_TO_POSTSCRIPT -- Translate a memory bitmap into a postscript program
+ * using image compression where regions of the image are all zeroes. This is
+ * done as follows: [1] lines of the bitmap are divided into segments of N
+ * bytes, [2] if all N bytes are zero a single zero byte is transmitted,
+ * otherwise a byte with the value one is transmitted, followed by N bytes of
+ * literal data. Lines which are entirely zero are not transmitted at all.
+ * The goal is to significantly reduce the amount of data to be pushed through
+ * the laserwriter serial interface while keeping things simple enough that
+ * postscript will hopefully be able to process the bitmap efficiently.
+ *
+ * NOTE: Postscript is supposed to be able to copy bitmaps directly without
+ * any transformations if all the right conditions are met, e.g., unitary
+ * matrices, pixrect resolution matches device resolution, etc. We do not
+ * make use of this here due to the great volume of data which would have to
+ * pushed through the laserwriter serial interface at 9600 baud to transmit
+ * a fully resolved bitmap. If a parallel interface were available, e.g.,
+ * if the laserwriter is on the ethernet, then this would be the way to go.
+ */
+static void
+bitmap_to_postscript (fp, bitmap, width, height, depth, linebytes, zero, scale)
+register FILE *fp;
+unsigned char *bitmap;
+int width, height, depth;
+int linebytes;
+unsigned char *zero;
+float scale;
+{
+ register unsigned char *ip;
+ register char *op, *hp;
+ register int n;
+ unsigned char *segp;
+ char hbuf[NGREY*2];
+ char obuf[SEGSIZE*2];
+ char rt_full[SEGSIZE*2+1];
+ char bkg_1[SEGSIZE*2+1];
+ char bkg_2[SEGSIZE*2+1];
+ int partseg, seg, nsegs, allzeroes, i, j, last_j;
+
+ /* Initialize the hbuf array, which contains the hex encoded
+ * representations of the NGREY possible binary byte values.
+ */
+ for (n=0, op=hbuf; n < NGREY; n++) {
+ i = ((n >> 4) & 017);
+ *op++ = (i < 10) ? i + '0' : (i-10) + 'A';
+ i = (n & 017);
+ *op++ = (i < 10) ? i + '0' : (i-10) + 'A';
+ }
+
+ /* Set up the background (stipple) pattern arrays, used to represent
+ * the Sunview background pattern outside of windows.
+ */
+ for (op=bkg_1, hp=BKGPAT_1, n=SEGSIZE; --n >= 0; ) {
+ *op++ = hp[0];
+ *op++ = hp[1];
+ } *op++ = '\0';
+ for (op=bkg_2, hp=BKGPAT_2, n=SEGSIZE; --n >= 0; ) {
+ *op++ = hp[0];
+ *op++ = hp[1];
+ } *op++ = '\0';
+
+ /* RT_FULL is a solid line, another common pattern. */
+ for (op=rt_full, n=SEGSIZE*2; --n >= 0; )
+ *op++ = 'F';
+ *op++ = '\0';
+
+ /* Initialize obuf, in case a partseg call causes the full buffer to
+ * be written out before the garbage elements at the end have been
+ * initialized to legal values.
+ */
+ bcopy (rt_full, obuf, SEGSIZE*2);
+
+ /* Define the postscript necessary to receive and output the lines
+ * of the pixrect with image compression.
+ */
+ fprintf (fp, "%%! GTERM screendump\n");
+ fprintf (fp, "erasepage initgraphics\n");
+
+ /* fprintf (fp, "[%6.3f 0 0 %6.3f 2350 3180] setmatrix\n",
+ -scale, -scale); */
+ fprintf (fp, "initmatrix\n");
+ fprintf (fp, "%6.3f 72 mul 300 div\n", -scale);
+ fprintf (fp, "%6.3f 72 mul 300 div scale\n", scale);
+ fprintf (fp, "%f %f translate\n", 2409/(-scale), (-88)/(-scale));
+
+ fprintf (fp, "%d %d translate\n", PAGE_XOFFSET, PAGE_YOFFSET);
+ fprintf (fp, "/r_data %d string def\n", SEGSIZE);
+ fprintf (fp, "/r_zero %d string def\n", SEGSIZE);
+ fprintf (fp, "/r_full %d string def\n", SEGSIZE);
+ fprintf (fp, "/r_bkg1 %d string def\n", SEGSIZE);
+ fprintf (fp, "/r_bkg2 %d string def\n", SEGSIZE);
+ fprintf (fp, "currentfile r_full readhexstring %s\n", rt_full);
+ fprintf (fp, "currentfile r_bkg1 readhexstring %s\n", bkg_1);
+ fprintf (fp, "currentfile r_bkg2 readhexstring %s\n", bkg_2);
+ fprintf (fp, "clear\n");
+
+ if (depth == 8) {
+ fprintf (fp,
+ "/dline {0 exch translate %d %d 8 matrix\n", width, 1);
+ } else {
+ fprintf (fp,
+ "/dline {0 exch translate %d %d true matrix\n", width, 1);
+ }
+
+ fprintf (fp, " { currentfile read pop dup %d eq\n", RT_DATA);
+ fprintf (fp, " { pop currentfile r_data readhexstring pop }\n");
+ fprintf (fp, " { dup %d eq\n", RT_ZERO);
+ fprintf (fp, " { pop r_zero }\n");
+ fprintf (fp, " { dup %d eq\n", RT_FULL);
+ fprintf (fp, " { pop r_full }\n");
+ fprintf (fp, " { %d eq\n", RT_BKG1);
+ fprintf (fp, " { r_bkg1 }\n");
+ fprintf (fp, " { r_bkg2 }\n");
+ fprintf (fp, " ifelse }\n");
+ fprintf (fp, " ifelse }\n");
+ fprintf (fp, " ifelse }\n");
+ fprintf (fp, " ifelse\n");
+
+ if (depth == 8)
+ fprintf (fp, " } image} def\n");
+ else
+ fprintf (fp, " } imagemask} def\n");
+
+ nsegs = width / (SEGBITS / depth);
+ partseg = linebytes - (nsegs * SEGSIZE);
+
+ /* Output successive lines of the pixrect. All zero lines are omitted
+ * and data compression is used for large regions of zeroes embedded
+ * within a line.
+ */
+ for (j=0, last_j=0; j < height; j++) {
+ if (zero[j])
+ continue;
+
+ fprintf (fp, "\n%d dline\n", j - last_j);
+ last_j = j;
+
+ /* Output an integral number of line segments in hexstring format,
+ * i.e., two hex digits output per binary input byte.
+ */
+ segp = bitmap + j*linebytes;
+ for (seg=0; seg < nsegs; seg++, segp += SEGSIZE) {
+ /* Quick scan of the data to see if it is all zeroes. */
+ allzeroes = 1;
+ for (ip=segp, n=SEGSIZE; --n >= 0; )
+ if (*ip++) {
+ allzeroes = 0;
+ break;
+ }
+
+ if (allzeroes) {
+ putc (RT_ZERO, fp);
+ } else {
+ /* Encode the data segment in hex format. */
+ for (ip=segp, op=obuf, n=SEGSIZE; --n >= 0; ) {
+ hp = hbuf + (*ip++ * 2);
+ *op++ = *hp++;
+ *op++ = *hp++;
+ }
+
+ if (obuf[0] == rt_full[0] &&
+ strncmp (obuf, rt_full, SEGSIZE*2) == 0) {
+ putc (RT_FULL, fp);
+ } else if (obuf[0] == bkg_1[0] &&
+ strncmp (obuf, bkg_1, SEGSIZE*2) == 0) {
+ putc (RT_BKG1, fp);
+ } else if (obuf[0] == bkg_2[0] &&
+ strncmp (obuf, bkg_2, SEGSIZE*2) == 0) {
+ putc (RT_BKG2, fp);
+ } else {
+ putc (RT_DATA, fp);
+ fwrite (obuf, SEGSIZE*2, 1, fp);
+ }
+ }
+ }
+
+ /* Write out any partial segment at the end of the line. We must
+ * always write a full segment, even if the data at the end is
+ * garbage, else synchronization will be lost.
+ */
+ if (partseg) {
+ for (op=obuf, n=partseg; --n >= 0; ) {
+ hp = hbuf + (*ip++ * 2);
+ *op++ = *hp++;
+ *op++ = *hp++;
+ }
+ putc (RT_DATA, fp);
+ fwrite (obuf, SEGSIZE*2, 1, fp);
+ }
+ }
+
+ /* Add the NOAO logo and timestamp at the bottom of the page and
+ * output the page.
+ */
+ fprintf (fp, "\n");
+ fprintf (fp, "/Times-Roman findfont 24 scalefont setfont\n");
+
+ /* fprintf (fp, "[-1 0 0 -1 2350 3180] setmatrix\n"); */
+ fprintf (fp, "initmatrix\n");
+ fprintf (fp, "-1 72 mul 300 div 1 72 mul 300 div scale\n");
+ fprintf (fp, "-2409 88 translate\n");
+
+ fprintf (fp, "%d %d moveto\n", 1600, 3150);
+ fprintf (fp, "[1 0 0 -1 0 0] concat\n");
+ fprintf (fp, "(%s) show\n", make_label());
+ fprintf (fp, "showpage\n");
+}
+
+
+/* MAKE_LABEL -- Generate the label for the output printer page.
+ */
+static char *
+make_label()
+{
+ static char buf[128];
+ char hostname[32];
+ char username[32];
+ struct passwd *pw;
+ long clock;
+
+ clock = time(0);
+ gethostname (hostname, 32);
+ pw = getpwuid (getuid());
+ strcpy (username, pw->pw_name);
+ endpwent();
+
+ sprintf (buf, "NOAO/IRAF %s@%s %s",
+ username, hostname, asctime(localtime(&clock)));
+
+ return (buf);
+}
diff --git a/unix/sun/ss1.patch b/unix/sun/ss1.patch
new file mode 100644
index 00000000..dcd0ce15
--- /dev/null
+++ b/unix/sun/ss1.patch
@@ -0,0 +1,31 @@
+This is to fix the "chicken scratches" bug that occurs with IMTOOL on a
+sparcstation. The problem occurs due to a bug in the SunOS kernel for the
+sparcstation. The patch given here is a workaround to avoid the problem
+until Sun fixes the bug.
+
+To patch the imtool executable, login as iraf, then make a backup copy of
+the imtool.e executable and patch the online one, as follows (be sure to
+get the case of the ?X and ?W right):
+
+ % cd $iraf/unix/bin.sparc
+ % cp imtool.e imtool.e.OLD
+ %
+ % adb -w imtool.e
+ 0xc2a0?X
+* 0xc2a0: ac102018
+ 0xc2a0?W0xac10200c
+* 0xc2a0: 0xac102018 = 0xac10200c
+ $q
+ %
+
+In the sequence shown above, the lines marked * are adb output. If the old
+value of location 0xc2a0 is not ac102018 (hex) as shown, then you have an
+old or otherwise unusual version of IMTOOL and the patch should not be
+applied. Once the executable has been patched, either copy the new executable
+to /usr/bin/imtool (or wherever imtool lives on your system) or rerun the
+INSTALL script in $hlib.
+
+The fix changes the rasterop used to draw the IMTOOL crosshair cursor from
+a copy-source-to-destination to an xor-source-and-destination. This results
+in a less visible cursor, but avoids the bug that leaves the scratches all
+over the IMTOOL window.