.xgp .squish .font 1 31vr .font 2 31vgb .font 3 40vg .sinch 8.5,6.5,11,8.5,1 .ad .ss .vsp 14 .ce 3THE LISP WINDOW SLAVE1 .space 2 .ce by .ce Chuck Rieger .ce November 1975 .space 2 There exist today AI systems which are so marvelously complex and sophisticated that it is a shame to have to communicate with them in the one-dimensional paradigm of the classical teletype-like device. There also exist AI programs so profoundly worthless that it is a shame to have to communicate with them at all, unless there is something intrinsically fun or soporific about the communication process. Since every program fits exactly one of these descriptions, it logically follows that it's a shame not to have some better paradigm for human-computer interaction than that offered by the classical teletype-like device. Shame, shame. Hence, the notion of windows. There is now a collection of LISP functions on AI:LIBLSP;WINDOW CJR1, with a LISP-loadable FASL counterpart which slithers out when you say (FASLOAD WINDOW FASL AI LIBLSP). The functions in the "window slave" package on this file make it possible to segment the screen of a Datapoint or TV terminal into an arbitrary number of "windows". Each window can then serve as a logically independent I/O channel through which individual LISP functions can communicate with the outside world without disturbing the states of other windows. The slave makes the assumption that it has exclusive control over the state of the display screen. Of course, if you graft new window-oriented code into an existing program which does not know about the slave, this assumption will not be valid (eg. non-window I/O will write wherever it pleases). Since it will often be desirable to use existing programs without modification, the window slave repertoire includes a function, (WSANITIZE ), which will automatically "sanitize" the lisp environment in a way which is both undoable (via WUNSANITIZE) and compatible with the window slave. WSANITIZE basically snaps all links to the LISP I/O functions, and redefines them to route non-window I/O through a specified window. On the other hand, the slave is smart enough to do approximations to the right things when you run a window-oriented program on a non-display terminal. Thus, using the window slave does not preclude running your window-oriented program on less exotic devices. .space 2 .block 8 .ce 3GENERAL INFORMATION1 The Datapoint or TV screen is viewed as a raster of characters, with (0,0) denoting the top left character, X increasing to the right, Y increasing toward the bottom. A window manifests itself on the screen as a collection of components: a (possibly invisible frame), an active interior region (everything inside the frame), possibly a name (displayed in the center of the top edge of the frame), and possibly a line cursor, displayed on the left edge of the frame. You may overlap window regions, but the slave isn't very clever about this in that it will simply overwrite. Internally, a window is simply an atom which has the following properties: .nofill 2WINDOW1 the atom is a window iff this property is non-NIL 2X1 the X-coordinate of the left edge of the active portion of the window. "Active" means the region inside the frame. 2Y1 the Y-coordinate of the top edge of the active portion of the window 2DX1 the width of the active region of the window 2DY1 the height of the active region of the window 2CX1 the X-coordinate of the window's cursor 2CY1 the Y-coordinate of the window's cursor 2NAMED1 the display name of the window, if any 2FRAME1 the nature of the frame (visible, invisible) 2CURSOR1 a property used by the cursor-mover 2HOLD1 a flag to indicate whether or not the window is to be "held" after each output (see WHOLD) 2SHOWCUR1 a flag indicating whether or not the window's cursor is to be displayed 2READPROMPT1 the prompt to be printed when this window requests input (see WREAD) 2CRLFPURGE1 a flag indicating whether or not to check for carriage-returns and line-feeds when printing (see WPRINC) 2ECHO1 a flag indicating whether or not to echo the results of window reads into the window (see WREAD) .fill .ad Text written to a window exists only in the operating system's display logic. In other words, there is no internal buffer associated with a window, and once something has been written, it's not readable or regenerable. Adding the buffer feature would be straightforward, but space-consuming. The slave defines and uses the following global variables: .nofill 2W-WINDOWS1 a list of all active windows 2W-BOTTOM1 the Y coordinate of a line on the screen which separates the slave-usable area from the system-usable area. The lines of the screen below W-BOTTOM are the "garbage" I/O area, where non-window I/O (eg. interactions with LISP's top level) is handled. If your program hasn't been properly sanitized, and someone tries to write too much in this area, the system will wrap you around to the top, messing up the window area. W-BOTTOM is initialized to give you 4 or 5 lines of garbage space at the bottom. 2W-ACTIVE1 a flag which, if non-NIL, tells the slave to do its thing. If nil, the slave emulates normal I/O. When the slave is loaded, it pokes around to determine the type of device and sets W-ACTIVE appropriately. You may turn the slave on and off simply by manipulating this variable. 2TV1 an array, mapped onto the PDP-11's display buffer when the window slave is used in a TV environment .fill .ad In addition, the value of the LISP global variable TTY is consulted to determine the output device type (TV= 5, Datapoint= 1,2). .space 2 .block 8 .ce 3THE FUNCTIONS1 All the window slave functions are EXPR's. For each of the following functions which expects the of some window, calling that function with = NIL causes the function to emulate normal TTY I/O, or simply do nothing, as appropriate. .space 2 .block 5 2INDIVIDUAL WINDOW DEFINITION AND STATUS-SETTING FUNCTIONS1 (2WOPEN1 ) .in 5 Opens a window of size by named , with top left corner at (,). If is NIL, makes the frame invisible, otherwise draws a visible frame. The size you specify is the size of the entire window, including the frame. The active interior area will be 2 units smaller in each dimension, regardless of whether the frame is visible. Thus, the smallest meaningful window is 3 by 3. Inside the window, the top left cell of the active region is (1,1), with increasing X to the right and increasing Y toward the bottom. If is non-NIL, WOPEN displays in the center of the top edge of the frame. Otherwise, the window will have no visible name. If is non-NIL, WOPEN actually paints the window on the screen. Otherwise, WOPEN does not actually cause the window to be displayed. This latter mode is useful when you wish to declare lots of windows, then paint them all at once via a WRESTART. WOPEN clears the active region and initializes the 's cursor to the top left of the active region. If is already open, WOPEN behaves like a WRESET. .in 0 (2WCLOSE1 ) .in 5 Removes window from the screen and discards it logically. .in 0 (2WCLEAR1 ) .in 5 Clears 's active region and resets its cursor to the top left of the active region. .in 0 (2WRESET1 ) .in 5 WCLEAR's , but also refreshes the border, name and line cursor. WRESET is useful for refreshing an accidentally-clobbered window. .in 0 (2WNAME1 ) .in 5 Gives window the new display name . = NIL causes no name to be displayed for . .in 0 (2WSHOWCUR1 ) .in 5 If is non-NIL, the cursor for window will henceforth be visible on the left edge of 's frame. Otherwise, displaying of the cursor will be inhibited. .in 0 (2WFLASH1 ) .in 5 If the terminal is a TV, momentarily bit-complements the interior region of window to call attention to I/O activity in the window. If the terminal is not a TV, WFLASH does nothing. .in 0 .space 2 .block 5 2OVERALL DISPLAY-STATE FUNCTIONS1 (2WRESTART1) .in 5 Clears the entire screen, WRESET's all currently active windows (those on W-ACTIVE), and draws a line across the screen at Y coordinate (W-BOTTOM)-1 to separate the window slave region (above the line) from the normal I/O garbage region (below the line). Thus, a typical window slave setup consists of WOPENing a herd of windows, then doing a WRESTART. .in 0 (2WBOTTOM1 ) .in 5 Sets W-BOTTOM to . W-BOTTOM separates the window area (above) from the garbage I/O area (below). 32 (decimal) is the default setting of W-BOTTOM on TV's, 20 (decimal) on Datapoints. .in 0 (2W-OFF1) .in 5 Turns the window slave off by setting W-ACTIVE to NIL and doing a WUNSANITIZE, then clears the screen. .in 0 (2W-ON1 ) .in 5 Turns the window slave on by setting W-ACTIVE non-NIL and calling WRESTART. If is non-NIL, W-ON does a (WSANITIZE ), otherwise it does not. .in 0 .space 2 .block 5 2OUTPUT RELATED FUNCTIONS1 (2WPRINC1 ) .in 5 Expression is PRINC'd to window , starting at the position to the immediate right of 's cursor. New lines are begun as needed and the cursor is left pointing at the last character written. When characters would fall off the bottom of the window, wrap-around to the top occurs. WPRINC piecewise butchers so that it fits within 's active region. .in 0 (2WPRINT1 ) .in 5 Expression is PRINT'd to window . WPRINT functions by calling WPRINC, then positioning the cursor at the beginning of the next line, which is wiped clean at that time. .in 0 (2WTYO1 ) .in 5 TYO's to window . .in 0 (2WCRLFPURGE1 ) .in 5 If it is expected that the print stream to will occasionally contain carriage-returns and line-feeds, characters which tend to wreck the display, WCRLFPURGE should be called with non-NIL. When in this mode, the window output functions will sanitize all output to by deleting carriage-returns and line-feeds. Since this is fairly time consuming, this mode should be used only when dealing with unknown output-generating sources. The default is = NIL. .in 0 (2WINC1 ) .in 5 If is zero, WINC simply guarantees the cursor to be positioned at the beginning of a new line. If it already is, nothing else happens. If is positive, number of TERPRI's are simulated on the window, clearing intermediate lines as they are passed over, and leaving the cursor at the beginning of a fresh line. If is negative, the simulated TERPRI's go backward, from bottom to top. (WINC -1) is a convenient way of implementing a "pop" if you are using a window to display a stack. .in 0 (2WSETPOS1 ) .in 5 Positions 's cursor at (.). If or is NIL WSETPOS leaves the respective coordinate alone. .in 0 (2WHOLD1 ) .in 5 If is non-NIL, WHOLD causes all subsequent WPRINC's and WPRINT's to pause after printing and wait for a go-ahead from the TTY. A pause message is printed in the garbage area of the screen. Typing any character gives the go-ahead, and furthermore, if that character is "C", the hold is removed from the window, causing future prints not to pause. Calling WHOLD with = NIL causes to be uh-held. .in 0 .space 2 .block 5 2INPUT RELATED FUNCTIONS1 (2WREAD1 ) .in 5 Prints (in the garbage area of the screen) a message that window is waiting for input, waits for an S-expression to be typed there, then copies the typed S-expression into the named window ("for the record") and returns the S-expression to the calling function. .in 0 (2WREADCH1 ) .in 5 To WREAD as READCH is to READ. .in 0 (2WTYI1 ) .in 5 Does a TYI to window , printing a prompt as do WREAD and WREADCH. .in 0 (2WREADPROMPT1 ) .in 5 If is non-NIL, it is used as the prompt for all WREAD's, WREADCH's and WTYI's done by window . If is NIL, a standard prompt is printed. .in 0 (2WECHO1 ) .in 5 Sets the "echo" satus of window to . If is non-NIL, the results of all WREAD's, WREADCH's and WTYI's to will be echoed (WPRINTed) to . Otherwise, no echoing occurs. = T is the default. .in 0 .space 2 .block 5 2INFORMATION FUNCTIONS1 (2WINDOWP1 ) .in 5 Returns non-NIL if is a window, NIL otherwise. .in 0 (2WSIZE1 ) .in 5 Returns the size of the active interior region of window in the form (COLUMNS.LINES). .in 0 (2WCURPOS1 ) .in 5 Returns the current coordinates of 's cursor in the form (LINE.COLUMN). These are the relative coordinates (within the active region of the window), not the absolute coordinates relative to the entire screen. The top left character in the active region is (1,1). .in 0 .space 2 .block 5 2SANITIZING FUNCTIONS1 (2WSANITIZE1 ) .in 5 Snaps all the uuolinks to the basic LISP I/O functions, and redefines the functions to route them to the display slave. Henceforth (until a (WUNSANITIZE)), all I/O that would have been printed normally is confined to window . The affected functions are: PRINT, PRINC, TYO, TERPRI, READCH, READ, TYI, and CURSORPOS. Care has been taken to emulate these faithfully, but there are no blanket guarantees. In particular, they are known not to work when I/O to files is involved (ie. when more than the basic argument is given to one of these functions). The patch isn't complicated; it just hasn't been done. .in 0 (2WUNSANITIZE1) .in 5 Undoes what WSANITIZE does by restoring the original I/O functions' definitions. Uuolinks are not repaired, but otherwise, things are as they were. It is a good practice to do a WUNSANITIZE before using LEDIT, since LEDIT tickles the problem mentioned above. .in 0 .space 2 .block 8 .ce 3CAVEATS1 Obviously, you pay a price for the goods. The principal loss of speed is from the piecewise butchering that must be done on every expression printed to a window (basically a TYO inside a DO-loop munching on the EXPLODEN'd expression). I know of no better way, short of modifying the LISP printers. When you are getting around 30% of the CPU time, you stop noticing the slowdown; but still, output is probably slowed by a factor of 2. The other thing you need to know is that, after you've FASLOAD'd the window slave into a LISP image which is talking to a TV, you can't successfully (SUSPEND), :PDUMP and subsequently reload your image. (That's usually a gross thing to do anyway.) This is because some PDP-11 memory mapping hair has transpired and this becomes a source of confusion for the poor loader. There's probably a way to undo the hair to permit dump/load's, but I don't know how to do it. There. I've given you some rope; now go hang yourself and enjoy, enjoy.