Window-Win Window-win is a new design for a window system which is intended to have all the power of the Lisp machine window system, and more flexibility, with less complexity. Display output, keyboard input and mouse tracking all work through the window system. Window-win allows the user to run different programs in different parts of the screen by giving each program a window. It also allows a program to create automatically-updating displays out of hierarchies of windows. For example, a menu will be made out of a stream display window with a simple text-string subwindow for each menu item. If you want a label under the menu, you would put the menu and a text-string window for the label into another window called a frame. An individual window is not a very large object and therefore it is reasonable to create large numbers of them. Window-win is intended to run in a Lisp system that allows multiple processes sharing the same address space and data heap but with their own control stacks and dynamic binding stacks. Programs written in other languages can be used with window-win if they can be loaded into the Lisp environment and share Lisp data. If you are using window-win under unix, other unix processes cannot talk to window-win directly, but they can communicate with the Lisp through ptys and the Lisp can display their output in windows. Screen updating in window-win is preemptable, which means that it has frequent stopping points at which the rest of the updating can be postponed to execute the users commands. Programs modify the ideal current window configuration which is the goal for redisplay. The redisplay mechanism records what is actually on the screen. When there is no other work to be done, the redisplay mechanism alters the screen image in the direction of the goal set up by the program. If the user types a command before the activity is finished, the command is executed, and then the redisplay mechanism starts up again heading toward the new current goal from wherever it had stopped. The top level of the window hierarchy is the "screen", a window which represents the whole sceen and has the whole screen as its visible area all the time. (Actually, it is ok to have more than one such window on a machine with multiple physical screens.) Underneath this window will be windows belonging to individual programs. They are "inferiors" of the screen, and the screen is their "superior". The individual program windows have inferiors also, which they use to construct their screen images. Note: below, where "positions" and "regions" are referred to, I was thinking of having objects to represent points or rectangles, as in Interlisp-D. However, I am now thinking that it would be better to pass four numbers as four separate arguments rather than as an object containing four slots, because this avoids the need to cons structures frequently at run time. Window Redisplay Most display updating is done in a special purpose process called the redisplay process. It loops doing a tree walk of the window hierarchy, updating display at all levels. Applications requiring high efficiency can take a short cut called "direct updating", described in a later section. Display updating of one window is normally done by the :UPDATE-DISPLAY operation. :UPDATE-DISPLAY screen screen-position previous-contents-data display-style window-region display-style window-region ... updates the contents of parts of this window, specified by the rectangles called WINDOW-REGION, on SCREEN, with upper left corner of the window at point SCREEN-POSITION. (That corner of the window need not be part of any of the displayed regions, but the regions are positioned AS IF that corner of the window were at that point on the screen). PREVIOUS-CONTENTS-DATA tells the window what it has displayed on the screen before, so it can tell how to use what exists on the screen already in order to update incrementally if it wishes. The DISPLAY-STYLE argument preceding each WINDOW-REGION argument tells what assumptions can be made about the state of the screen bits of that area of the screen. If it is NIL, you can assume that the screen bits contain data corresponding to what PREVIOUS-CONTENTS-DATA says. If it is T, you may not assume anything about the screen bits; in fact, you must erase all such rectangles immediately before doing anything else and must update PREVIOUS-CONTENTS-DATA to say that they are clear. If DISPLAY-STYLE is :JUST-DRAW, you must update PREVIOUS-CONTENTS-DATA in that same fashion but NOT erase the rectangle. (This is used for blinker drawing and undrawing.) The operation should return the updated PREVIOUS-CONTENTS-DATA, which may or may not be the same Lisp object as the argument. The value returned by this operation will be saved and will be passed back as the PREVIOUS-CONTENTS-DATA on the next display update. The PREVIOUS-CONTENTS-DATA applies to the whole of the window. Sometimes only part of the window is being displayed. :UPDATE-DISPLAY is called only once, and describes all the currently-displayed parts of the window. The window should update the PREVIOUS-CONTENTS-DATA in parallel with the specified parts of the screen. It may or may not update PREVIOUS-CONTENTS-DATA for changes that do not appear in the displayed parts of the window--it makes no difference whether such updating is done. Redisplay-Needed Flags The intention is that each window with inferiors will pass the :UPDATE-DISPLAY operation on recursively to the inferiors. This would require a lot of work to go into walking parts of the window tree where nothing has changed. To avoid that work, there is a flag stored for each window by the window's superior which says whether that window (including any of its inferiors) requires attention for redisplay. The flag is set by anything which changes the data base that the window uses for its display. Often this is also a frequent operation which should not have to take the time to invoke a flavor operation. So we allow the window to get and save a list of pointers to the flags it will need to set. :REDISPLAY-FLAG-LIST inferior-window Returns the redisplay flag list for INFERIOR-WINDOW, an inferior of this window. The redisplay flag list of a window is a list of pointers whose cars are the flags of that window, its superior, and so on up to the top level window (the screen). Thus: (MY-FLAG-POINTER MY-SUPERIORS-FLAG-POINTER MY-SUPERIORS-SUPERIORS-FLAG-POINTER ... SCREENS-FLAG-POINTER) When a window is created, it can ask its superior for its redisplay flag list, and save the list around. When a window's data base is changed so that it requires redisplay, what the program does is look down the redisplay flag list, setting each flag to T until it comes to a flag that is already T. Windows whose display changes infrequently and for which simplicity is more important than efficiency when the display does change can use this convenient operation (on the window's superior) instead. :REDISPLAY-NEEDED inferior-window Informs this window that INFERIOR-WINDOW needs redisplay. All flags are set as described above, without consing up a list of the pointers to the flags. This is for use with kinds of windows whose contents rarely change. Why Separate Objects for PREVIOUS-CONTENTS-DATA? Why not just have each window remember its previous display information internally rather than pass it as an argument from the superior? My original idea was that a window could appear in different places in the window hierarchy. It would redisplay properly in each place since different records of screen contents would be kept. Unfortunately, it seems that a window does need to know its superior to report when it has changed, and the redisplay flags are probably important for efficiency. This rules out appearing in multiple places, though perhaps windows could know about any number of independent superiors and inform each one independently when the window needs redisplay. Also, a text string window whose contents do not change can still be used as an inferior to more than one other window since this window will not need to actually look at its superior. Also, it is possible to save a screen snapshot or a snapshot of a single window and save the PREVIOUS-CONTENTS-DATA at the same time. Restoring them both might be useful in conjunction with returning the data bases being displayed to their previous state or a similar state. If these features are not deemed worth having, it is easy to make each window remember its PREVIOUS-CONTENTS-DATA internally. Why Specify All the Regions in a Single :UPDATE-DISPLAY? Originally I had :UPDATE-DISPLAY being called once for each region. That was simpler conceptually. But it had an implementation problem. If one part of a window was invalidated while another was correct on the screen, it would be hard to alter the PREVIOUS-CONTENTS-DATA to indicate this if there were lines, items or whatever which fell partly into each region. For example, suppose in a picture window a line runs from the invalid region to the valid region. The PREVIOUS-CONTENTS-DATA says either that the line is present on the screen or that it is not. The solution of making the PREVIOUS-CONTENT-DATA say which parts of the window were valid, or other such information, seemed complicated. To avoid it, I changed the design so that a single :UPDATE-DISPLAY describes all the visible parts of the window. The window can then make PREVIOUS-CONTENTS-DATA consistent with the existing screen image by erasing the parts of the critical items which fall in the still-valid parts of the window. After this, such items are completely absent from the display and PREVIOUS-CONTENTS-DATA can simply say they are absent. On the other hand, the alternative of recording the invalidated regions explicitly in the PREVIOUS-CONTENTS-DATA and using that information in display updating until the regions become valid may be simpler than I thought. If :UPDATE-DISPLAY is changed back to do a single window region, direct updating windows will no longer be able to tell from that operation when part of the window has ceased to be visible. However, they can still tell this by watching :WINDOW-AREA-INVISIBLE operations. Size and Position of Windows A window normally knows its own size but not, in any sense, its position. Only the superior knows that (relative to the superior). In fact, a window need not always HAVE a position in its superior, if it is not currently being displayed (such as, if it has been scrolled off the screen). The window's position "on the screen" makes sense only when the window is visible. :SIZE This operation returns the height and width of the window. Sometimes a superior such as a frame will change an inferior's size. Then it will use the :SET-SIZE operation. :SET-SIZE width height Some scrolling windows will allow each inferior to decide its own size. The superior will use the chosen sizes to decide where the inferiors appear. Sometimes the superior will specify the width and the inferior will determine the height from that. :DESIRED-SIZE keyword-args The keyword arguments specify things about the window shape or size. Two values are returned based on the specified constraints (and also based on what the window wants to display now). These are the window's desired width and height. Two standard constraint arguments all windows accept are :WIDTH and :HEIGHT, which specify one dimension of the size. Also there are :MAXIMUM-WIDTH and :MAXIMUM-HEIGHT. This operation will be invoked by the superior's redisplay handler. The superior may or may not choose to change the inferior's size based on the inferior's request. If it does so, it informs the inferior with a :SET-SIZE. Invoking :UPDATE-DISPLAY on the inferior comes last of all. :DESIRED-SIZE should have no side effects on the window that performs it. One can find out a window's position by asking its superior: :INFERIOR-POSITION inferior returns the position of the upper left corner of INFERIOR on its screen. The operation is invoked on INFERIOR's superior. Blinkers A blinker is an ornament on a window. The position of the mouse is traditionally indicated with a blinker. Also, a blinker is used to indicate the position at which input or output is being inserted in a stream. On the Lisp machine, blinkers are used for such things as point in the editor, flashing the open paren that matches the close paren that point is after, boxes around menu items that the mouse is pointing at, spaceships and torpedoes in spacewar. In this window system, not all of these things will be blinkers, but some of them must be. Blinkers do not have to blink; they can be on solidly. What all blinkers have in common is that they are "in addition to" the rest of the display of some window, and the "rest" of the display can be updated independently of the blinkers even though they occupy the same space. This works because the blinkers are always drawn and erased by xor'ing. To update a blinker (change its position or the way it looks on the screen), it can be erased and redrawn. To update the "rest" of the display, it suffices to erase all blinkers, do the output, and then redraw the blinkers. On the Lisp machine, a blinker is a special type of object. In window-win, a blinker is a kind of window. The usual kind of blinker actually has an inferior, called its contents, which provides the shape of the blinker. The blinker itself implements the proper interface for being used as a blinker. A blinker is an inferior of another window, and its image on the screen is always within the superior. It is possible for the blinker to be positioned entirely or partially outside the rectangle of its superior, making it invisible or partially visible. This sort of clipping is done like any other scrolling. A blinker has to have these instance variables: VISIBILITY Non-NIL if this blinker SHOULD be drawn on the screen. (Blinking complements this flag periodically.) NEXT-VISIBILITY-TIME Either NIL or a time at which VISIBILITY should be set to T. This setting will be done by the first display updating after that time. This flag is used to provide the feature that if a blinker is taken off the screen to update other windows, the blinker will not reappear for at least 1/3 second. This feature is observed to reduce extraneous screen flashing. PREVIOUS-CONTENTS-DATA-POINTER the cell whose car is the PREVIOUS-CONTENTS-DATA for this blinker. CONTENTS the window (an inferior of the blinker) which is used to draw the blinker's appearance. The blinker need not record its size; it can ask the contents. It is possible to implement well behaved blinkers that do their own displaying rather than using a contents window, but the gain in efficiency may not be important enough. SUPERIOR This may not actually be needed. BLINK-LIST-ELEMENT If this blinker is blinking, the BLINK-LIST element for this blinker. Otherwise, NIL. Having this facilitates updating the blink-list element quickly when the blinker is moved while it is blinking. Display updating of the blinker checks this automatically if the blinker has moved. Blinkers normally have small images that are quick to draw. So for the sake of simplicity there is no incremental updating for blinkers. The blinker is either on or off. If the blinker's shape or position is supposed to change, it is done by undrawing the blinker in the old location and redrawing it in the new one. This is usually going to be easier and faster than computing the differences. The blinker is drawn by invoking the :UPDATE-DISPLAY operation on its contents with :JUST-DRAW for the value of the argument DISPLAY-STYLE, and the global flag XOR-FLAG bound to T. It is undrawn the same way! This calling convention will cause some types of window to display in a suitable fashion, including text string windows. Most blinkers' contents will be text string windows displaying a single character in a funny font containing arrows, etc. [[ Unless :JUST-DRAW turns out to have some other application aside from blinker updating, there may be no need to have both :JUST-DRAW and XOR-FLAG. One of them may serve for both purposes if that is cleaner.]] The blinker's PREVIOUS-CONTENTS-DATA records whether the blinker is currently drawn, and if so, its position, regions of visibility and contents when last drawn. Display updating of the blinker compares these saved values against the current values in the blinker. If they do not match, the blinker erases the old contents from its old position and displayed regions (if it had been turned on), then draws the new one (if it is supposed to be turned on). The appearance of the contents window of a blinker should never be changed. Substitute a different contents window instead. The blinker just compares the contents with EQ to see if it must redisplay for that reason. This restriction is because the blinker needs to be able to undraw the contents in its old shape. If parameters in the contents have changed, the old blinker image would be undrawn with its new shape, leaving a difference pattern on the screen forever. The reason why the blinker must point to its PREVIOUS-CONTENTS-DATA is so that direct output to blink the blinker can alter the PREVIOUS-CONTENTS-DATA to reflect the fact that the screen has been changed. Superiors of Blinkers Several kinds of windows will need to have blinkers as inferiors. The screen needs to have a blinker, the mouse blinker, under it. Stream display windows will usually want to have a cursor blinker. These types of windows need to have (either optionally or always) an instance variable BLINKERS which is a list of the blinkers inferior to the window. It may be best for all such windows to have this variable and the ability to have inferior blinkers even if they don't actually have any. Or the ability to have blinkers as inferiors may be implemented by a mixin. Blinkers and Display of Other Windows Because the blinker shares screen area with other windows (other inferiors of the same superior), it must be erased before those other windows are updated. The superior's :UPDATE-DISPLAY method must erase all relevant blinkers before performing :UPDATE-DISPLAY on the other inferiors. It can put the blinkers back when done, if it is not preempted. If direct output is done on any of those other inferiors, it should also remove the blinkers. Direct output has special hooks to allow this to be done. The blinkers should NOT be redrawn when the direct output is finished. Instead, redrawing the blinkers should be left for the next regular redisplay (that is, the blinkers should be marked as needing redisplay). This is because usually many direct outputs will be done in a row and only the first should have to mess with the blinkers. Blinkers are made to blink by putting them on the BLINK-LIST. The value of BLINK-LIST is a list of elements of this form: (BLINKER NEXT-BLINK-TIME HALF-PERIOD SCREEN-POSITION BLINKER-REGION BLINKER-REGION ...) SCREEN-POSITION gives the position of the upper left corner of the blinker, and BLINKER-REGION is the portion of the blinker that should actually be blinked (usually all of it, but less if the blinker is only partially visible). If the blinker is overlapped by a corner of another window, it may be necessary to describe the visible portion by more than one rectangular region. Blinking is done as direct output by a special process that wakes up as often as necessary. However, the usual precaution of removing other blinkers from the screen before direct output is not needed since all blinkers are xor'd and do not interfere with each other. Thus, blinker blinking can be done directly from the information in BLINK-LIST without any reference to the blinkers' superiors. Mouse Tracking Mouse tracking is done on the basis of the window hierarchy. When the mouse moves into a window, that window's :TRACK-MOUSE method is invoked to track the mouse while it remains in the window. When the mouse moves out of the window, that method returns. When the mouse moves into one ofthe inferiors of that window, the inferior's :TRACK-MOUSE method will be called. Not all windows are asked to handle the mouse. Each superior knows that certain of its subwindows should be offered handling of the mouse and others should not, just as it knows that certain subwindows should be invoked for display updating and others not. (A particular type of superior might know that all of its inferiors, or none of its inferiors, want to handle the mouse, or it might keep a flag for each inferior, or might ask the inferior on each occasion.) :TRACK-MOUSE screen screen-position window-region Track the mouse on SCREEN while it remains in the part of the window specified by WINDOW-REGION, whose upper left corner corresponds to SCREEN-POSITION on the screen. Some windows will choose to do different things in tracking the mouse depending on whether the entire window is available for tracking the mouse through (that is, the window is entirely visible). Mouse tracking is done in a special-purpose process called the mouse process. The main loop of this process simply calls the screen's :TRACK-MOUSE handler repeatedly inside a catch of MOUSE-RECONSIDER. The low level wait primitive that waits for the mouse to move also waits for the variable MOUSE-WAKEUP to be non-NIL. If that happens, the primitive throws to MOUSE-RECONSIDER. Any change in window configuration should set MOUSE-WAKEUP to T unless it can be determined that the change cannot affect the window that the mouse is in (such as, if the windows that are changing fit within a part of the screen that the mouse is not in). Often a window will highlight on the screen while its :TRACK-MOUSE handler is in progress, if the window is fully visible, or possibly it will highlight whichever part is visible. To interlock this with redisplay, such :TRACK-MOUSE handlers place the expression to evaluate to undo the highlighting in the variable TRACK-MOUSE-UNHIGHLIGHT. An unwind protect is used to eval the value of that variable provided it is non-NIL. Anyone wanting to do redisplay may eval the value himself and set it to NIL, to make sure that there is no highlighting on the screen. Mouse clicks do not necessarily go to the window that is tracking the mouse, though that window can get them if it wants to. The destination of mouse clicks is controlled by two variables, MOUSE-CLICK-WINDOW and MOUSE-CLICK-OVERRIDE. A mouse click is reported by (SEND (OR MOUSE-CLICK-OVERRIDE MOUSE-CLICK-WINDOW) ':MOUSE-CLICK args) If a window wants to handle mouse clicks when the mouse is inside it, its :TRACK-MOUSE handler will set MOUSE-CLICK-WINDOW on entry using LET-GLOBALLY, which automatically restores the old value on exit from the window's :TRACK-MOUSE handler. The args given to :MOUSE-CLICK are as follows: CLICK-DESCRIPTION WINDOW POSITION-IN-WINDOW SCREEN-POSITION the first says which button, how many clicks, etc. WINDOW is the window that was tracking the mouse at the time. POSITION-IN-WINDOW is the mouse position within that window at the time of the click, and SCREEN-POSITION is the position on the screen. Documentation of the meanings of the available mouse clicks is provided through the variable MOUSE-DOCUMENTATION. Its value should be a string or a function which returns a string when called with some standard set of useful arguments (such as screen, screen-position, window-region and window). This variable will be bound by :TRACK-MOUSE methods like MOUSE-CLICK-WINDOW. Direct Updating Usually display updating is done by the special-purpose display update process. However, in some programs the overhead of process-switching may slow down display updating sugnificantly. For these programs, a more complicated mechanism is available that may be faster. The redisplay process always locks REDISPLAY-LOCK before actually doing any redisplay. Another process can lock REDISPLAY-LOCK and do redisplay on some part of the window tree, as long as it knows whether and where on the screen the window to be redisplayed is visible. This information could be found out by asking the window's superiors, but that is slow. Instead, the window itself can remember the information from the last :UPDATE-DISPLAY operation done on it. Each :UPDATE-DISPLAY tells the window exactly which parts of it are visible and where. The window can cease to be fully visible only as a result of display updating at higher levels of the tree, which can only happen with the lock locked. So the window can keep track of which parts of it are visible where on the screen, and this information cannot become invalid as long as a window other than the redisplay window has the REDISPLAY-LOCK locked and does not redisplay this window's superiors. Before doing direct output one must also take care of the preparations that would have been done by the tree walk on its way down to the windows to be updated. This primarily means clearing off any blinkers managed by the superiors. To make this efficient, the window's superior can be asked for a list of functions to call to prepare the superiors to all levels for direct updating on this window. :DIRECT-UPDATE-PREPARATION-LIST inferior-window returns a preparation list for INFERIOR-WINDOW. This is a list of functions to be called. Each function takes these args: (FUNCALL function rest-of-list display-function args...) If REST-OF-LIST is non-NIL, the function will call the car of that list, passing on the cdr of that list as its first argument, making all the functions call the following functions in a nested fashion. If REST-OF-LIST is NIL, the function will apply the DISPLAY-FUNCTION to ARGS. This will do the actual display updating. Then the functions will return one by one after cleaning up for themselves as necessary. Note that blinkers will not normally be put back on the screen at this time. That will be left for the next ordinary display update. This is so that sequential direct updates do can be faster, as they do not need to turn the blinkers off again. There are two ways to use the information for direct updating. One is to use the :UPDATE-DISPLAY operation on the window, just as the redisplay process does. Another is to perform output on the screen corresponding to the changes made in the window's redisplay data base. For example, one could draw a character on the screen while adding it to the window's text string. This is called direct output. Direct output should only be done if the window's redisplay flag is NIL. Otherwise, the window needs redisplay for other reasons and until that is done the direct output will be inappropriate to the window's real contents. Both direct updating and direct output should not be done if any input is available. Change of Superior :SET-SUPERIOR new-superior tells a window that it has a new superior. The window should pass the operation along to each of its inferiors (with itself as the argument) even though, strictly speaking, those inferiors do not have a new superior. This is for the sake of windows that hold information such as the redisplay flag list or the direct update preparation list which depends on all of the window's superiors to all levels. The window need not recalculate that information unless this operation is invoked. Scrolling A standard operation is defined for asking a scrolling window to scroll: :SCROLL-RELATIVE screen-distance-x screen-distance-y tells it to shift by the indicated amounts. :SCROLL-TO x-fraction y-fraction scrolls to a position in the data specified by the given fractions. :SCROLL-POSITION returns a region whose edges are fractions between 0 and 1. The region indicates the part of the window's totality of data that is being displayed now in the window's screen area (more precisely, was displayed as of the last display update. This is to save effort in computing the information). Operations for Efficient Pop-Up Windows If redisplaying a window from its text or picture data base is slow, it may wish to keep also a bit map to record the results of displaying the text or pictures. This bit map could be used to redisplay the window if its screen image is clobbered but the data base has not changed (the window itself does not want redisplay). The window is informed when its screen bits are about to be clobbered with the :WINDOW-AREA-INVISIBLE operation: :WINDOW-AREA-INVISIBLE screen-position window-region says that the specified region, previously displayed by this window aligned with the window's upper left corner at SCREEN-POSITION, is about to be denied to this window. A window saving its bits in a bitmap would save those bits, or possibly all of its bits on the screen, at such a time. The :UPDATE-DISPLAY operations convey this information also, but not soon enough. By the time the window could tell from that operation that part of the window was no longer being displayed, some other window might have been displayed already in that screen area. A window could even have the ability to remember that all but a certain part of it is still on the screen. This means that if the window is fully visible and then some part of it is made invisible, it would copy the bits for that part alone into a bit map. There would not be a bit map for each window, just one for saving what appeared beneath a pop-up window, and it would be small enough to not cause great thrashing since pop-up windows are generally a small part of the screen. If other parts of the window became invisible as well, the combined sets of bits would be copied from their respective locations into the window's own bitmap if it has one, or would just be discarded. Doing this would speed up popping up menus, notifications, etc. so it might be a good idea to do it as a general facility which would be used by "top-level" windows of programs. Keyboard Input There is a FIFO buffer for keyboard input. All type-in goes immediately into this buffer. Certain windows ("papers", see below) can be used to do input through. These windows can read from the keyboard buffer when they are allowed to. But in general only one of them is allowed to do so at any time. This is called the selected window. Other windows will behave as if "there is no input available" for them, either waiting until they are selected or returning NIL depending on what input operation is used. Each input-capable window also has its own FIFO buffer which is considered to be "in front of" the keyboard buffer. The window can ALWAYS read from its own buffer, if there is anything in it. Input gets into the window's own buffer in two ways: -- when the user uses the mouse to select a different window, everything in the keyboard buffer gets copied into the old selected window's buffer (it is type ahead). -- when mouse clicks work by creating blips (lists as input characters) in inferiors of this window, the blips go into the window's own input buffer. These blips must go to the window they belong to, regardless of who is selected. Blips are put into a buffer with the :FORCE-KBD-INPUT operation. Its argument is a blip (or an actual character object) to be inserted. (Perhaps this operation should be done on the input stream rather than the window. That seems more general.) An input-output stream will generally specify one window through which to do input and another window on which to output. Usually the second one is a stream display window, but it might be a picture window or any other window which a stream could be made to output to in some sense. The first one is a "paper". The output window is generally an inferior of the input one. When input is being echoed, it is often desirable to make some sort of cursor blinker appear in the window being used for echoing. It is nice to have these blinkers blink only when the window is an inferior of the selected paper. This is done by having the paper (actually, its contents window) know which blinker to start blinking when it gets selected and stop blinking when it stops being selected. Text-String Windows The simplest kind of window displays a fixed text string. The text string can be explicitly changed by the program but as long as that is not done the window's appearance never changes. Here are the instance variables it needs to have: STRING the string to display. If the string contains multiple lines, each line is individually centered in the width. Lines too long are always truncated; it is expected that the calling program will set up the windows long enough to display the text strings or break the strings in advance. The string is not displayed completely verbatim. Some special characters can specify such things as underlining, font changes, etc. so that the program can get a wide variety of displays. FONT the font to display it in. LINEHEIGHT the space from one line to another. HEIGHT the height of the window. WIDTH the width. OUTLINE controls whether to display an outline (box) around it. Values are NIL for no, T for yes, or :MOUSE for yes only when the mouse is on this window. INVERT controls whether to invert (white vs black) the whole window. Values are NIL, T or :MOUSE, as above. CLICK function to call to handle mouse clicks. If it is NIL, mouse clicks are not handled by this window. If the function is called and returns NIL, the superior will be asked to handle the click. Often this function will be a closure that contains a pointer to another window, to do :FORCE-KBD-INPUT on it of a blip (a list which serves as an input "character") representing this click. SUPERIOR the superior window of this one. The desired size of this window is the size required to display the window's current STRING in the current FONT with the current LINE-HEIGHT. You should never change the OUTLINE or INVERT value from :MOUSE or to :MOUSE while the window is in use (might be tracking the mouse). This window does not need to use any special hair for its PREVIOUS-CONTENTS-DATA. Changing any parameter that affects the way the window displays would inform the superior that this window needs redisplay. Only in that case would this window be asked to update its display, and it could do so all at once since these windows are intended for small pieces of text. So in fact the PREVIOUS-CONTENTS-DATA can just always be NIL. These windows could be able to display line by line, but that would require some information in the PREVIOUS-CONTENTS-DATA. The result is about 15 words of storage per window. This is acceptable for use for menu items. Picture Windows These windows display a set of lines, solid areas, etc. and text arranged two-dimensionally. The window records a list of the lines, text strings, etc. to be displayed and where they go. This list can be changed by the program, and the window will redisplay incrementally. All the lines, text strings, etc. are drawn by xor'ing so that individual ones can be erased. This window needs these instance variables: HEIGHT The window height. WIDTH The window width. ITEM-LIST The list of items to display in the window. X-OFFSET Amount of horizontal scrolling of data rightward in the window. All display functions should add this much to all x coordinates they display. Y-OFFSET Similar, but downward instead of rightward. SUPERIOR This window's superior. CLICK If non-NIL, a function to call to handle clicks. One of the arguments will be the display item the mouse is pointing at, or NIL if none. Each element of ITEM-LIST is a display item, which is a list describing one line, text string, etc. to be displayed. The list looks like this: (DISPLAYFUN MOUSEFUN HIGHLIGHTFUN ARGS...) DISPLAYFUN is a function to display this item. MOUSEFUN is a predicate to say whether the mouse is "near" this item. HIGHLIGHTFUN is a function to draw some sort of highlighting when the mouse is pointing at this item. If DISPLAYFUN is NIL, no display is done. If MOUSEFUN is NIL, there is no mouse sensitivity. If HIGHLIGHTFUN is NIL, there is no highlighting. The arguments passed to any of those functions are the same: the window the screen the screen position of the window the window region for clipping display or for mouse tracking the entire ITEM-LIST element. The DISPLAYFUN does its display with XOR'ing so that it can be used to erase the item as well as to draw it. Ditto for the HIGHLIGHTFUN. Both should take care to clip to within the specified window region, which will always be within the window's own size. It is legitimate for items to protrude beyond the window edges, and the user can ask to scroll the window if the program sets up a way to do that. The MOUSEFUN returns either T if the mouse is "near enough" to the object to be considered to be pointing to it, or a number which is a distance that the mouse must move before there is any point in checking this predicate again. The mouse tracker takes the minimum of all these values and then waits for the mouse to move at least that distance in some direction from the position that was checked. The first item in the item list wins, if more than one item is near enough to the mouse. The program should choose the order of items with this in mind if it can matter. The PREVIOUS-CONTENTS-DATA for this type of window is a list of display items. Any new display items that were not there before must be drawn, and any there before but not part of the current contents must be erased. When :UPDATE-DISPLAY is done with non-NIL DISPLAY-STYLE arguments for some window regions, the first thing done is to erase the regions that are supposed to be erased. The second thing done is to erase the rest of any items which fell partially in the window regions that are not valid. This means that if an item is partly in a region that is not valid and partly in a region which is valid, the part in the valid region must be erased. (The part in the invalid regions has already been erased, and :JUST-DRAW regions MUST BE TREATED AS IF they had been erased.) Then the item is removed from the PREVIOUS-CONTENTS-DATA. Display updating cannot be preemptable until this point, since only now are the screen bits and the PREVIOUS-CONTENTS-DATA consistent with each other. It is probably not worth while implementing direct output for these windows. It would be possible to create output streams that output onto picture windows. Any number of output streams could output on one window, no one of them having priority. Nothing would stop a stream from outputting outside the visible portion of the picture window. There should be a version of picture window which has a bitmap for faster redisplay when the contents are unchanged. Stream Display Windows Stream windows display the contents of a linear stream of output. The stream can contain both text and other windows, interspersed. These windows are used for ordinary Lisp listeners and interaction windows, and where typeout windows are used on the Lisp machine. Subwindows are used in the output to make it mouse sensitive or to display things other than just text (such as, a display of a font, or a diagram of some sort. Display of the stream contents include wraparound at the right and bottom margins. A running cursor position is maintained as output goes on, so that the output operations can tell when a --MORE-- is necessary. --MORE-- processing is independent of actual redisplay, and will happen even if none of the page of output has actually appeared on the screen. However, reaching a --MORE-- will cause the program to wait for input, which will probably allow redisplay to complete if it has not already. If the user has typed ahead the space to continue past the --MORE--, then output from the program will continue without the old page of output even appearing on the screen! Wrap-around appears to make it easier for a user to read the text on the window while output goes on. Wraparound can be considered to be the same as scrolling except that the "logical top" of the window is not at the actual top. Instead, the logical top, which is where the oldest data on the window appears, moves down the window just underneath where new output is coming out. Dealing with wraparound requires remembering the vertical position of the logical top. Each text string or subwindow to be display is called an item. The items are displayed consecutively, left to right, breaking lines between items, and down from one line to the next. Instance variables needed include: HEIGHT WIDTH SUPERIOR REDISPLAY-FLAG-LIST Set by doing output, and by scrolling. Output need not set it if the user has scrolled away from the end of the stream and therefore the new stuff output at the end will not be displayed unless more scrolling is done first. VISIBLE-X VISIBLE-Y If the window is fully visible, these are the screen coordinates of the upper left hand corner. Otherwise they are NIL. These are used for direct output. DIRECT-OUTPUT-PREPARATION-LIST Used for direct output. See the section on that. ITEM-LIST The list of items to be displayed, in sequential order. ITEM-LIST-TAIL Points to the last link in the ITEM-LIST, so new elements can be added at the end. ACCUMULATING-STRING A string in which characters output individually are accumulated. The string is transfered to become an item when either the right margin is reached or the window is redisplayed. This batches individual characters to reduce the number of items without delaying output. FONT-LIST the first element is the default font. LOGICAL-TOP-POSITION Vertical position on the window of the "logical top" to handle wrap around. SCROLL-FROZEN non-NIL means the user has scrolled away from the end of the stream, and output at the end of the stream should not cause the window to scroll. If this is NIL, direct output is likely to be done. TOP-ITEM-LINK This is the ITEM-LIST link whose car is the first item actually displayed (affected by scrolling). It may be NIL if all items are scrolled off the top of the window. TOP-ITEM-OFFSET This is a vertical offset within the top item for where to start display. It is usually 0, but it can become positive if there is a window item which is taller than one line, etc. LINE-HEIGHT The height in pixels of each line of text. BASELINE Amount of LINE-HEIGHT which is below the baseline of the line. The rest is above the baseline. LAST-INPUT-WAIT-POSITION The vertical position at the time of the last wait-for-input done on a stream outputting to this window. Used for deciding when a --MORE-- is needed. << Just how this information gets to the window is still somewhat mysterious >> An item can be just a string, to display that string. It can also be a list whose car is a string and whose remaining elements are a property list saying how to display the string (fonts, line spacing to use, etc). It may be convenient to require that a string not contain any Return characters, and have them be separate items. An item can also be a list whose car is a window. Then the rest of the list is used by the stream display window to record information about where the subwindow appears, to make mouse tracking more efficient. It is not supplied by the user program; the window itself says everything about how to display it. The subwindow is always asked how big it wants to be and made that size. If the subwindow wants to be wider than the remaining space on the line, or if it wants to be more than a line long, it is displayed starting on a new line. A kind of item for horizontal whitespace and one for vertical whitespace would also be useful. The desired size of the stream display window is calculated by starting with the current top item and seeing how much space is required for all the items after that. When :UPDATE-DISPLAY is done with non-NIL DISPLAY-STYLE arguments for some window regions, the first thing done is to erase the regions that are supposed to be erased. The second thing done is to erase the rest of any items which fell partially in the window regions that are not valid. This means that if an item is partly in a region that is not valid and partly in a region which is valid, the part in the VALID region must be erased. (The part in the invalid regions has already been erased, and :JUST-DRAW regions MUST BE TREATED AS IF they had been clear.) Then the item is marked empty in the PREVIOUS-CONTENTS-DATA. Display updating cannot be preemptable until this point, since only now are the screen bits and the PREVIOUS-CONTENTS-DATA consistent with each other. The PREVIOUS-CONTENTS-DATA of a stream display window has to indicate the items that were displayed, their sizes and positions when displayed, and for items that are windows, the PREVIOUS-CONTENTS-DATA of the subwindow. Also, any regions of the inferior invalidated by the streeam window's own display updater must be recorded so that the inferior can be told to redisplay them from scratch (with non-NIL DISPLAY-STYLE arguments). Stream display subwindows can also be used as menus, PEEK-like scroll windows, inspector windows, choose-variable-values windows, etc. In these cases, all or nearly all of the items will be windows. Frames A frame is designed to show a fixed team of windows side by side (not overlapping), adjusting their sizes and shapes in a sophisticated fashion when the size of the frame is changed. It does not scroll through them. For example, the editor would use a frame whose inferiors are the editor windows, the mode line and the echo area. The echo area might also be a frame which would sometimes contain the minibuffer and sometimes a stream display window for prompting and brief typeout. Instance variables: HEIGHT WIDTH SUPERIOR INFERIORS RESHAPE-FUNCTION BOX-FLAG INFERIORS is a list of elements of the form (WINDOW REDISPLAY-FLAG XPOS YPOS) describing where the inferiors currently are and remembering their display information. RESHAPE-FUNCTION is a function to be called when the frame's size is changed. It is passed the list INFERIORS and should change the sizes of the inferiors so that they fit in the size of the frame. If BOX-FLAG is non-NIL, the window displays a box around its contents. It turns out that this is a frequent thing to want together with a frame. The PREVIOUS-CONTENTS-DATA of a frame has to include the sizes, positions and PREVIOUS-CONTENTS-DATA of all the inferiors as of the last display update of those inferiors. Also, any regions of the inferior invalidated by the frame's own display updater must be recorded so that the inferior can be told to redisplay them from scratch (with non-NIL DISPLAY-STYLE arguments). Typeout Frames A typeout frame is a window which provides for one display to appear most of the time, but allows another independent stream of data to come down over it. This is what happens in the original EMACS when something wants to "type out": the typeout appears on top of part of the area that normally displays the editor buffer. The typeout frame has two inferiors, the "normal display" inferior and the typeout window. The former can be anything; the second could in principle be anything but in practice is a stream displayer window. The normal display inferior's size is equal to the size of the frame and changes only if the frame's size is changed. The typeout window uses some fraction of the frame, starting at the top. Display updating is done by asking the typeout window its desired size and making it that size. It is then displayed in the space it wanted. The rest of the space in the frame, whatever the typeout window did not want, is used to display the part of the normal display that comes in that space. The PREVIOUS-CONTENTS-DATA must include the size of the typeout window and the PREVIOUS-CONTENTS-DATA of both inferiors. When output is done to the typeout window, this automatically changes the desired size which that window will want when the typeout frame asks it. So redisplay will extend the typeout window to make room for whatever output goes into it. When it fills the whole frame, it will start wrapping around. To make the typeout "go away", just scroll the typeout window so that its TOP-ITEM is NIL, meaning all items output so far are off screen above the top, and set the LOGICAL-TOP-POSITION to 0 so that the next output begins at the top of the typeout window. Then the "normal display" inferior will show through completely. The normal display inferior will usually be a frame itself. To reduce the number of levels in the window hierarchy in practice, it might be good to make the typeout frame serve as an ordinary frame too. It would have a single typeout window inferior as well as any number of other inferiors which are handled as described in the section on frames. The ability to handle a typeout window inferior might usefully be a mixin. Desks and Papers A "desk" is the kind of window that implements the familiar Smalltalk-style behavior of having overlapping windows which hide parts of each other, one of which is "selected" and talking to the keyboard. Usually the only desks in the system are for full screens, at the tops of the window hierarchies on those screens. A "paper" is a window designed to be the inferior of a desk. It knows how to communicate with the desk about whether it is selected, etc. Input buffers are associated with papers. A paper has no built-in form of display; all papers are alike. Each paper has one inferior which is responsible for doing the display in that paper. This inferior can be changed dynamically. For example, suppose you have a paper whose inferior implements a read-eval-print loop -- a Lisp listener. You call the SUPDUP function and the window "changes" into a SUPDUP user window. Really what happens is that the paper's inferior is temporarily replaced with another window which implements the SUPDUP behavior. As far as selection is concerned, it is the "same" window, since selection goes on among papers. If the machine has multiple screens, there will be a desk for each screen. However, there is only one selected paper, not one per screen. Also, the desk and the screen window can actually be the same window. This document uses both terms, and they could be implemented as two different windows with the desk as an inferior of the screen, but it is simpler to make them one window, and less overhead. The user can construct a "composite" paper, a window which serves the functions of a paper but is actually a tree of windows with the "contents" buried somewhere inside it. Therefore, standard operations are defined on papers for getting and setting the "contents". :CONTENTS Returns the window which is the paper's "contents". :SET-CONTENTS window Sets the paper's contents to WINDOW. This should discard all blips in the paper's input buffer, since they were intended for the program associated with the previous contents and will confuse the new contents. Actual keyboard type ahead in the buffer should be preserved since the user presumably knew it would be read after the change of contents. The desk informs a paper when it becomes selected or stops being selected with these operations: :SELECTED :NOT-SELECTED The paper passes the operations along to its contents, which is what knows what the particular program in use wants to do about being selected or not selected. Usually it (the paper's contents) will make a blinker in some inferior of itself start blinking or stop blinking. Just which blinker may depend on the state of the program running in the window. One of the mouse clicks on a desk will invoke the screen editor, a menu-oriented program which allows the user to reshape, move, etc. the papers on that desk. Primitive operations to do those things will exist so that the screen editor can use them, but application programs will not use them. Scroll Bars A scroll bar is a window that displays information about the scrolling position of another window, called here the "scrolled window". Mouse clicks on the scroll bar scroll the scrolled window by invoking the standard scrolling operations documented in a previous section. Different kinds of scroll bar windows could be designed which display differently and have different mouse commands. Here I will describe a simple kind which displays as a thin rectangle and blackens the part of that rectangle which corresponds to the portion of the scrolled window's data that is currently scrolled into view. The scroll bar needs these instance variables: HEIGHT WIDTH SUPERIOR SCROLLED-WINDOW DO-VERTICAL DO-HORIZONTAL that's all! The information displayed by the scroll bar is a depiction of some or all of a group of four numbers, the values returned by the :SCROLL-POSITION operation on the scrolled window. These four numbers describe the edges of the visible part of the scrolled window as fractions of the area of all of the data that the window as to scroll through. However, many windows scroll in only one direction, and so we want to have scroll bars that indicate only one direction of the scrolling. (You might want to show two dimensions of scrolling by two one-dimensional scroll bars, one vertical and one horizontal). DO-VERTICAL should be non-NIL to make this scroll bar display vertical scrolling of the scrolled window and DO-HORIZONTAL tells it to display the horizontal scrolling of that window. The PREVIOUS-CONTENTS-DATA for the scroll bar is a list of the four numbers on which the last display update was based. Display updating asks the scrolled window for a new set of four numbers and compares them with the old ones. Since the display updating is done by drawing and erasing a few rectangles of small area, there is no need to make it preemptable. It updates the numbers in the PREVIOUS-CONTENTS-DATA when it is finished. The scroll bar tracks the mouse, making the mouse cursor look different while inside the scroll bar, and optionally not releasing control of the mouse until the mouse moves a certain distance away to make it easier to keep the mouse inside a thin scroll bar. Mouse commands are handled by invoking the scrolling operations on the scrolled window. Overall Screen Configuration The screen is represented by a window, the top of the hierarchy on that screen. This window is often called the "screen". The screen has two inferiors: the regular desk and the who line. The who line displays an updating status display. It contains subwindows that display particular pieces of information, including a line of mouse documentation describing what mouse clicks at this moment would do (derived by looking at MOUSE-DOCUMENTATION); the current time of day, status of open files, etc. There is a special process that runs once a second to update the contents of these windows.