The Lisp Machine window system manual is intended to explain how you, as a programmer, can use the set of facilities in the Lisp Machine known collectively as the window system. Specifically, this document explains how to create windows, and what operations can be performed on them. It also explains how you can customize the windows you produce, by mixing together existing flavors to produce a window with the combination of functionality that your program requires and adding daemons to various operations.
It is assumed that you have a working familiarity with Zetalisp as
documented in the Lisp Machine manual. It is also assumed that you have
some experience with the user interface of the Lisp Machine, including
the ways of manipulating windows, such as the
Create commands from the system menu. To use the
predefined flavors and methods, you need not be familiar with how
methods are defined and combined, but you should understand what message
passing is, how it is used on the Lisp Machine, what a flavor is, what a
"mixin" flavor is, and how to define a new flavor by mixing existing
flavors. To use the information provided here on where to add daemons,
you must be thoroughly familiar with programming with flavors, and must
be willing to refer to the window system source code as the final
authority for all questions.
Any comments, suggestions, or criticisms will be welcomed. Please send Arpa network mail to BUG-LMMAN@MIT-MC.
Those not on the Arpanet may send U.S. snail to
Richard M. Stallman 545 Technology Square, Room 914 Cambridge, Mass. 02139
The Lisp Machine is a product of the efforts of many people too numerous to list here and of the former unique cooperative environment of the M.I.T. Artificial Intelligence Laboratory. I believe that the commercialization of computer software hinders the further development of systems such as described herein. I consider proprietary software morally objectionable and plan to dedicate my career to promoting the sharing and free exchange of software.
Starting in December 1983 I plan to work on the development of GNU, a complete Unix-compatible software system for standard hardware architectures, to be shared freely with everyone just like EMACS. This will enable people to use computers without agreeing to the idea of proprietary software. This project has inspired a growing movement of enthusiastic supporters. If you would like to join it, write to me at the address on the previous page. Help get programmers sharing again! Contributions of part-time programming help will be very welcome, as will funding from philanthropists to support full-time workers, and donations or loans of computers.
The current implementation of the window system is based on flavors, and was designed and implemented primarily by Howard Cannon and Mike McMahon during 1980. It replaced an earlier version implemented by me, which was based on Smalltalk-like classes. The newer version is generally an improvement, but as Howard Cannon steadfastly refused to discuss the design with me I must decline responsibility for such counterintuitive aspects as the definition of exposure.
About a third of this manual is based on earlier documents written by Dave Moon and Daniel Weinreb. Sarah Smith of LMI helped to correct the manual, and Chris Schneider and Steve Strassman provided useful suggestions.
The term window system refers to a large body of software used to manage communications between programs in the Lisp Machine and the user, via the Lisp Machine console. The console consists of a keyboard, a mouse, and one or more screens. All Lisp Machines have at least one high-resolution black-and-white screen, and some machines also have a color screen. The window system can handle any number of screens of various kinds.
The window system controls the keyboard, encoding the shifting keys,
interpreting special commands such as the
System keys, and
directing input to the right place. The window system also controls the
mouse, tracking it on the screen, interpreting clicks on the buttons,
and routing its effects to the right places. The most important part of
the window system is its control of the screens, which it subdivides
into windows so that many programs can coexist and even run
simultaneously without getting in each other’s way, sharing the screen
space according to a set of established rules.
When you use the Lisp Machine, you can run many programs at once. You can have a Lisp listener, an editor, a mail reader, and a network connection program, or several of each, all running at the same time, and you can switch from one to the other conveniently. Interactive programs get input from the keyboard and the mouse, and send output to a screen. Since there is only one keyboard, it can only talk to one program at a time. However, each screen can be divided into regions, and one program can use one region while another uses another region. Furthermore, this division into regions can control which program the mouse talks to; if the mouse cursor position is in a region associated with a certain program, then mouse clicks are directed to that program, which is then allowed to decide what the clicks mean. Allocating access to screen space and input devices is the most important function of the window system.
The regions into which the screen is divided are known as windows.
In your use of the Lisp Machine, you have encountered windows many
times. Sometimes there is only one window visible on the screen; for
example, when you cold-boot a Lisp Machine, it initially has only one
window showing, and it is the size of the entire screen. If you start
using the system menu’s
Edit Screen, or
options. you can make windows in various places of various sizes and
flavors. Usually windows have a border around them (a thin black
rectangle around the edges of the window), and they also frequently have
a label in the lower-left hand corner or on top. This is to help the
user see where all the windows are, what parts of the screen they are
taking up, and what kind of windows they are.
The next several sections begin to explain the detailed concepts of how windows work and what their internal state is. You should probably read over these quickly the first time, without worrying about all the details. You really don’t have to understand all of the complexity to make simple use of the window system; it just helps if you know what sort of thing is going on.
A window may or may not be
exposed, which means that output
can be done on it ((exposure)). At any time at most one window
can be selected, which means that input can be done through it
((selection)). These two conditions constitute the window’s
Another kind of state information that every window has is its edges: its size and its position (see (sizes-and-positions)). You can specify these numerically, ask for the user to tell you (using the mouse), ask for a window to be near some point or some other window, and so on.
Windows can function as streams by accepting all the operations that
streams accept. If you do input operations on windows, they read from
the keyboard; if you do output operations on windows, they type out
characters on the screen. The value of
(terminal-io-var)) is normally a window, and so input/output functions
on the Lisp Machine do their I/O to windows by default.
A window whose flavor incorporates
tv:stream-mixin supports all the
standard input stream operations and may be passed as the input stream
to functions such as
readline (see (input)). Each such
window has an input buffer holding characters that have been typed
at the window but not read yet. You can force keyboard
input into a window’s input buffer; frequently two processes
communicate by one process’s forcing keyboard input into an input buffer
from which another process is reading characters (see
Any window handles the standard output stream operations and can be
passed as the output stream to functions such as
format (see (output)). You can output characters at a cursor
position, move the cursor around, selectively clear parts of the window,
insert and delete lines and characters, and so on, by means of standard
and not-so-standard stream operations. Output of text on windows
provides additional features; for example, characters can be drawn in
any of a large set of fonts (type faces), and you can switch from one
to another within a single window (see (font-section)). Windows can define
their own actions for exceptional conditions that affect output, such as
reaching the right or bottom edge of the window, or printing more that a
window-full without pausing (see (output-exceptions)).
In addition to characters from fonts, you can also display graphics (pictures) on windows (see (graphics-section)). There are operations to draw lines, circles, triangles, rectangles, arbitrary polygons, circle sectors, and cubic splines.
Each window can have any number of blinkers (see (blinkers)). Most windows have one blinker that follows the window’s cursor position; this blinker normally appears as a blinking rectangle. But blinkers need not follow the cursor and need not actually blink (some do and some don’t). For example, the editor shows you what character the mouse is pointing at; this blinker looks like a hollow rectangle. The arrow that follows the mouse is a blinker, too. Blinkers are used to add visible ornaments to a window, or temporary modifications to a window’s normal display. Blinkers are flavor instances with their own standard operations.
Windows are the standard interface to the mouse (see (mouse)). Both mouse motion and mouse clicks are normally handled by messages sent to the window over which the mouse is positioned.
A window’s area of the screen is divided into two parts. Around the edges of the window are the four margins; while the margins can have zero size, usually there is a margin on each edge of the window, holding a border and sometimes other things, such as a label. The rest of the window is called the inside; regular character output and graphics drawing all occur on the inside part of the window. The margins and inside of the window are managed separately so that mixins to add things to the margins can be independent of the program that draws in the window’s inside. See (margins).
For greater flexibility in subdividing a window into multiple areas of different uses, you can create inferior windows or panes within the window. The main window is then called a frame. Each pane can be of a different flavor suitable to its own purpose; thus Peek uses a frame which has a menu and a scrolling window as its two panes. See (window-hierarchy), for information on the hierarchy of windows, and (frames), for a description of frames.
The asynchronously intercepted characters (such as
Control-Abort) which take effect instantaneously when typed are
handled by the selected window. Each window can specify its own. See
A window can have an associated process. For example, when you type
Control-Abort, the process aborted is the one associated with the
selected window. Exactly how processes and windows relate depends on
the flavor of the window, and, as usual, there are several operations to
manipulate the connections. See (processes).
Notifications are a facility for displaying messages from events
taking place asynchronously and not related to the program you are running
(errors in background processes,
qsends from other users,
file servers planning to go down, etc.). Notifications work through
operations on the selected window, so each window can decide how to display
a notification. See (notifications).
Screens are represented by flavor objects also; these are not windows, but share some of the operations and instance variables of windows (see (screens)). Windows and screens collectively are called sheets. Each screen object usually corresponds to a particular piece of display hardware. Screens can be either black-and-white or color. Color screens have more than one bit for each pixel, and most operations on windows do something reasonable on color screens. But the extra bits give you extra flexibility, and so there are some more powerful things you can do to manipulate colors. Color screens also have a color map which specifies which values of the pixels display which colors. See (color).
The who line at the bottom of the screen shows the user something about the state of the Lisp Machine. The window system software implements the who line as a separate screen even though it appears on the same TV monitor as the main screen and its windows. This is why you cannot move the mouse into the who line area, or make windows on the main screen hide the who line. See (who-line) for more information on the who line and how it is interfaced and implemented.
The higher level window facilities are window flavors that combine the basic capabilities of windows appropriately to provide directly usable techniques for particular common applications. These facilities include menus and other choice windows, typeout windows, and scrolling windows.
Menus allow the user to choose one or several of a fixed set of
items. The system menu that you get from double-click-right is an
example of one. See (menu). Multiple choice windows allow the
user to specify an answer to each of a set of similar multiple-choice
questions (see (multiple-choice)). The editor command
Kill or Save Buffers shows an example of one.
Choose-variable-values windows allow the user to view and modify the values of a set of variables, each variable printed and read according to its own range of possible values. One variable might allow only numbers, while another variable’s value might be restricted to a list of pathnames. See (choose-variable-values).
Typeout windows allow windows such as scroll windows and editor windows, which normally present displays reflecting permanent data bases, to print output in response to individual commands. The typeout window is an inferior of the other window, and exposes itself when output is done on it.
Scrolling windows allow the programmer to define a display which the user can then scroll through. The scrolling window facility provides for scrolling, redisplay, and interaction with the mouse, requiring the programmer only to specify the entire contents to be scrolled through. There are two types of scrolling windows, text scroll windows ((text-scroll-windows)) and general scroll windows ((scroll-windows)), the former being less powerful but simpler. Note that there is a standard interface protocol for the mouse to request scrolling (see (scroll-protocol)). You need not use one of the standard scrolling window facilities to make a window that can scroll if you are willing to implement the scrolling yourself. For example, editor windows and menus can also scroll.
In the Lisp world, each window is a flavor instance, an instance of some
flavor of window. There are many different window flavors available;
some of them are described in this manual. All of them contain the
The flavor on which all window flavors are built. Any window flavors
you define should include this component. This flavor itself is made of
tv:minimum-window has no methods of its
own; all are inherited from those components. So you will at times (in
the debugger) run across methods of those component flavors. You will
also run across methods of
tv:sheet, a component of
tv:essential-window. However, there is no need for you as a
programmer to pay attention to the distinctions among these flavors,
and in this manual all the operations, instance variables and init
tv:minimum-window are documented as being "of windows"
rather than of any specific flavor.
.defflavor tv:window This flavor of window has several mixins that provide much generally useful functionality, including the ability to select the window, graphics operations, labels and borders.
(defflavor tv:window () (tv:stream-mixin tv:borders-mixin tv:label-mixin tv:select-mixin tv:delay-notification-mixin tv:graphics-mixin tv:minimum-window))
The operations of these mixins are specifically identified in this manual.
Use the mixins, or use the flavor
tv:window, if you want the
operations to be available.
It is often necessary to mix flavors to get the desired window behavior.
When doing this, you must pay attention to the correct ordering of flavor
components. The earlier components will override later ones.
For example, if you want to make a window that will print out notifications
on itself by mixing in
tv:notification-mixin, you must put it in front
(defflavor my-window () (tv:notification-mixin tv:window))
If you put them in the other order, as in
(defflavor my-window () (tv:window tv:notification-mixin))
you get something equivalent to
effect is completely lost. The whole point of
that it should override some methods of
tv:window (inherited from
tv:delay-notification-mixin), and in fact it defines the same operations
in a different way. It follows that if
tv:notification-mixin comes last,
it will be overridden instead.
It is almost always correct to put mixins first in the ordering so that they will override whatever they are added to. One exception occurs with flavors of margin item; there, the ordering is used to control the spatial position of the margin items.
Screens are also represented by flavor instances, which share some of
the characteristics of windows because they share the component flavor
tv:sheet. Screens are described fully in (screens).
tv:sheet is a flavor that windows and screens share. It is also
what provides the structure required by the microcode display primitives.
Operations defined by this flavor are documented as being
"of windows and screens" in this manual.
Much of the contents of this manual is devoted to describing the instance variables and operations of various flavors of window. They are grouped below by functionality.
There is a vague convention sometimes followed for naming flavors of
windows. Here the word frobboz is used to stand for any feature,
attribute, or class of windows that would appear in a flavor name
Naming conventions are different for instantiable flavors (which
are complete and can support instances of themselves) and mixin
flavors (which are incomplete and only supply one particular aspect of
frobboz-windowexcept when it is necessary to make a distinction.
frobboz-mixinwhen the flavor is regarded as altering the "essential character" of the window. It does not work to mix two "basic" flavors together unless they are designed to work together. In certain cases a
tv:minimum-windowas a component, and may even be instantiable, but usually it is a mixin that must be mixed with
tv:minimum-windowand other things in order to work.
Many programs never need to create any new windows. Often it suffices
to use the standard input, output and graphics operations on an
existing window, such as a Lisp listener which is the value of
terminal-io when your program is called. For example, here
is a graphics demo that will draw a pattern of xored circles
on any window which has
tv:graphics-mixin (such as a Lisp listener).
(defun green-hornet (&optional (window terminal-io) (separation 40)) (hacks:with-real-time (send window ':clear-screen) (send window ':home-down) (multiple-value-bind (iw ih) (send window ':inside-size) (let ((center-x1 (- (truncate iw 2) (truncate separation 2))) (center-x2 (+ (truncate iw 2) (truncate separation 2))) (center-y (truncate ih 2))) (do ((i (- (min center-y center-x1) 10.) (1- i))) ((<= i 5)) (send window ':draw-circle (if (bit-test 20 i) center-x1 center-x2) center-y i)))) (send window ':tyi) t))
Such programs should try to stick to the most widely-implemented
operations. The ideal is to use only the standard stream operations
documented in (stream-protocol); then your program will run even with
streams that are not windows. With graphics programs such as
green-hornet you are forced to use some windows-only operations,
but it is still best to stick to the operations provided by the flavor
When you want to create a flashy and sophisticated user interface, especially involving mouse-sensitivity or automatic updating, it is time to consider creating your own windows (and your own window flavors, perhaps).
To create a window, use the functions
(Old programs usually use
tv:make-window, which is now equivalent to
make-instance but was different in the past).
Creates, initializes, and returns a new instance of the specified flavor. The init-options argument contains alternating keywords and values; the keywords must be init options accepted by the flavor you are using. The init options accepted by various window flavors are described in this manual.
(make-instance 'tv:lisp-listener ':borders 4 ':font-map (list fonts:bigfnt) ':vsp 6 ':edges-from ':mouse ':expose-p t)
creates an exposed Lisp listener with big characters and lots of vertical space between lines.
For more information on this function and on
The area in which windows are by default created.
.defmetainitoption windows :name name Every window has a name, which is used primarily for printing the window as a Lisp object, but also serves as a default for the window’s label. If you do not specify a name, the default is constructed from the flavor name and a counter (each flavor has its own) to make the name unique. .end_defmetainitoption
.defmetainstvar windows tv:name The name of the window. .end_defmetainstvar
The most important piece of information about a window is whether it is actually visible on the screen. A related but different piece of information is whether the window is exposed. Understanding these basic concepts, the subjects of this chapter, is vital to any use of the window system.
Using the system menu
Create option you can make two windows that
partially overlap. (If you have never done so, you should try it.)
The window system is forced to make a choice here: only one of those
two windows can be the rightful owner of that piece of the screen. Of
these two windows, only one can be (fully) visible at a time; the other
one has to be not fully visible, but either partially visible or not
visible at all. Only the fully visible window has an area of the
screen to use.
If you play around with this, you will see that it looks as if the two
windows were two overlapping pieces of paper on a desk, one of which
must be on top of the other. Create two Lisp listeners using the
Create command of the system menu so that they partially overlap,
and then single-click-left on the one that is on the bottom. It will
come to the top. Now single-click-left on the other one; it will come
back up to the top. The one on top is fully visible, and the other
one is not.
Several Lisp Machine system programs and application programs present
the user with a window that is split up into several sections, which are
usually called "window panes" or "panes". For example, the inspector
has six panes in its default configuration: the one you type forms into
at the top, the menu, the history list, and the three inspection panes
below the first three. The window debugger and ZMail also use
elaborate windows with panes. Just as windows on a screen can subdivide
the screen, a window’s panes subdivide the screen space of the window.
With programs such as the editor, inspector and ZMail, it may not be obvious
that the windows you see are panes in another window because that window
occupies the full screen. If you go into
Edit Screen and reshape one
of these, you will see clearly how there is a window with subwindows.
In fact, the panes in an inspector are related to the inspector’s main
window just as that window is related to the screen. Windows are arranged
in a hierarchy, each window having a superior and a list of inferiors.
Usually the top of the hierarchy is a screen. In the example above,
the inspector window is an inferior of the screen, and the panes
of the window are inferiors of the inspector window. The screen itself has no superior
(if you were to ask for its superior, you would get
A window’s superior, its superior’s superior, and so on, are collectively called its ancestors. A window’s inferiors and their inferiors, and so on, are called its descendants.
The position of a window is remembered in terms of its relative position
with respect to its superior. To figure out where a window
is on the screen, we add this relative position to the absolute position
of the superior (which is computed the same way, recursively; the
recursion terminates when we finally get to a screen). The important
thing about this is that when a superior window is moved, all its
inferiors are moved the same amount; they keep their relative position
within the superior the same. You can see this if you play with the
Move Window command in
Edit Screen edits the arrangement of the windows on a screen,
but it can also edit the arrangement of inferiors (panes) of a window
in the same fashion. If you click right on
Edit Screen, you get a menu
containing all the superiors of the window you pointed at, up to the screen.
You can then edit the inferiors of whichever one you choose.
Edit Screen really does is to manipulate a set of inferiors
of some specific superior, which may or may not be a screen. The
set of inferiors that you are manipulating is called the active
inferiors set; each inferior in this set is said to be active.
The active inferiors are all fighting it out for a chance to be
visible on their superior. If no two active inferiors overlap,
there is no problem; they
can all be visible. However, whenever two overlap, only one of them
can be on top.
Edit Screen lets you change which active inferiors
get to be on top. There is also a part of the window system
called the screen manager whose basic job is to keep this
competition straight. For example, it notices that a window that
used to be covering up part of a second window has been reshaped, and so
the second window is no longer covered and can be made visible.
Inactive windows are never visible until they become active; when a
window is inactive, it is out of the picture altogether. The screen
manager will be discussed at length later ((screen-manager)).
Each superior keeps track of all of its active inferiors as a
list in the instance variable
tv:inferiors, and each inferior window
keeps track of its superior, in the instance variable
Superior windows do not keep track of their inactive inferiors; this
is a purposeful design decision, in order to allow unused windows to be
reclaimed by the garbage collector. So, when a window is deactivated,
the window system doesn’t touch it until it is activated again.
.defmetamethod windows :activate Makes the window active in its superior. .end_defmetamethod
.defmetamethod windows :deactivate Makes the window cease to be active in its superior. .end_defmetamethod
.defmetainitoption windows :activate-p t-or-nil
If this option is specified non-
nil, the window is activated after
it is created. The default is to leave it deactivated.
.defmetamethod windows :kill
Killing a window deactivates it but also makes a positive effort to get
rid of other entities such as processes or net connections that may be
associated with the window. If a window has these things, it may not
be satisfactory to just allow the window to be garbage collected;
:kill operation is provided. A command for the
user to get rid of windows should use
.defmetamethod "windows and screens" :active-p
t if this window is active in its superior.
A screen is always considered active.
.defmetamethod "windows and screens" :inferiors Returns this window or screen’s list of inferiors. .end_defmetamethod
.defmetamethod "windows and screens" :superior
Returns this window or screen’s superior.
For a screen, it is
.defmetamethod windows :set-superior new-superior Makes this window an inferior of new-superior. .end_defmetamethod
.defmetainitoption windows :superior superior
Makes the new window an inferior of superior.
If this is not specified, the default is
which is initially the main black-and-white screen.
.defmetainstvar "windows and screens" tv:inferiors A list of the active inferiors. .end_defmetainstvar
.defmetainstvar "windows and screens" tv:superior
In a window, the value is the window’s superior.
In a screen, the value is
Accessor defsubsts for the corresponding instance variables.
t if sheet is an indirect inferior, zero or more levels down, of the sheet me.
Calls function on every exposed sheet, starting with the screens, their inferiors, and so on down.
Calls function on every exposed inferior of sheet, to all levels, including sheet itself.
Calls function on every active sheet, starting with the screens, their inferiors, and so on down.
Calls function on every active inferior of sheet, to all levels, including sheet itself.
The topmost nodes of the window hierarchy are actually screens
rather than windows, a screen being an instance of the flavor
Screens are also flavor instances, whose flavors incorporate
Screens are not windows, but they have much in common with windows,
because both incorporate the flavor
Usually each screen object represents an individual piece of display hardware.
However, the main black-and-white physical screen that all Lisp Machines have
is logically divided into two screens, with different screen objects.
tv:who-line-screen. Because these are
separate screens, windows on the main screen cannot be extended onto the who line,
and the mouse cannot move onto the who line, etc.
Screens are the objects that know how to parse font specifiers
(user-level names for fonts) into font objects that can be used for display.
See (font-specifier). Also, each screen can specify a font for each of
the standard font purposes (
Returns the screen that sheet is an indirect inferior of (sheet itself, if it is a screen).
The screen object that represents the Lisp Machine black-and-white display,
except for the who line area. This is default superior for windows created
The screen object that represents the who line area. Each field of the who line is a separate window on this screen.
This is the screen that is "normally used". It is initialized to be the main screen. Certain functions that create a window without reference to the mouse use it as a default for the superior of the window, and window resources with a superior as parameter often create one window initially, with the default screen as the superior.
This is the color screen for the 4-bit-pixel color display that some Lisp Machines have. The screen object is always present, but is exposed only when the machine actually has a color screen. See (color).
A list of all screen objects. With this list, you can begin a tree walk to cover all active windows.
Sets the scanning rate of the main screen, in vertical sweeps per second, to speed. speed is usually a flonum.
The vertical size of the screen is inversely proportional to the number of vertical scans per second, because the display rate in horizontal scan lines per second is fixed.
A nonzero value of wasted-lines directs the system to refrain from using that many horizontal scan lines at the bottom of the screen. If you are using MIT software on a machine built by Symbolics, you may need to do this, since the screens are typically misaligned so that the who line is obscured by the screen’s cabinet. A value of 20 to 30 generally does the trick.
A screen displays an array of pixels. Each pixel is a little dot of some brightness and color; a screen displays a big array of these dots to form a picture. Everything you see on the screen, including borders, graphics, characters, and blinkers, is made up out of pixels.
Each physical screen has a display memory which stores the values of all the pixels. On regular black-and-white screens, each pixel has one of only two values, lit up or not lit up, so the pixel is represented in memory by one bit. Usually 0 is used for the background of a window and the characters or lines on it are made of 1’s, so 1 can be considered "on" and 0 "off". On color screens, pixels have more than one bit. The usual sort of color screen has four bits per pixel. 0 is still often used as the background value and assigned the color black. There is no convention for the use of other pixel values.
Black and white screens have a hardware flag that controls the visual
appearance of 1 and 0 pixels. In "black-on-white" mode, 1 is dark and
0 is bright, so windows appear with dark text on a white background.
This mode is the default. In "white-on-black" mode, 1 is bright and 0
is dark. Users can switch between these modes with
An individual window can specify 1 for background and 0 for text; this
is independent of white-on-black mode (which applies to the whole
screen) and is requested with the
:reverse-video-p init option or
:set-reverse-video-p operation (see
(windows-set-reverse-video-p-method)). These work by controlling the
alu functions used for drawing and erasing characters; see
(alu-functions). Programs which use the window’s recommended alu
functions for their drawing and erasing will automatically display in
reverse-video when this is specified. The
who line mouse documentation window is an example of a window which
Make screen display one-bits as black, with zero-bits as white. (This is the default mode.)
Note that this works by setting a bit in the display hardware; as a result, if done on the main screen, it applies to the who line as well.
Make screen display one-bits as white, with zero-bits as black.
Toggle whether screen displays one-bits as white or as black.
This is what
Terminal C does.
.definstvar tv:screen tv:bits-per-pixel 1 for a black-and-white screen, larger numbers for other kinds (4 for the standard color screen). .end_definstvar
.definstvar tv:screen tv:buffer The address of the screen memory, as a fixnum. .end_definstvar
.definstvar tv:screen tv:buffer-halfword-array
art-16b array containing the screen memory.
.definstvar tv:screen tv:control-address The address of the screen’s control register which contains, among other things, the flag controlling black-on-white mode. .end_definstvar
The pixel values that make up a window’s screen image are called its contents. When a window is fully visible, its contents are displayed on a screen so that they can be seen. When the window is not fully visible, its contents are lost unless there is a place to save them. Such a place is called a bit-save array.
A bit-save array is an array of bits of sufficient size to hold a copy
of the window’s contents. If a window has a bit save array, its
contents are copied into the array when the window ceases to be fully
visible. If the window becomes fully visible again, the contents are
copied from the bit-save array back onto the screen. In the mean time,
programs can use
tv:sheet-force-access to do output into the
bit-save array while the window is not visible (see
(tv:sheet-force-access-fun)), and the window’s inferiors, if any, can be
exposed and do output (see (exposure)).
When a window with a bit-save array is partially visible, the visible
parts can be displayed correctly by copying them from the bit-save
array. This is the behavior you observe if you make a small Lisp
listener window with
Create and have a full-screen window such as
the initial Lisp listener or a Zmacs frame partially visible around it.
It happens because the Lisp listener or Zmacs frame has a bit-save array.
If a window does not have a bit-save array, then there is no place to
put its contents when it is not visible, so they are lost. When the
window becomes visible again, it will try to redraw its
contents; that is, to regenerate the contents from some state
information in the window. This is done by the
documented below. Some windows can do this; for example, editor windows
can regenerate their contents based on the editor buffers they are
displaying. Other windows, such as Lisp listeners, do not remember
what was displayed on them and cannot regenerate their previous contents.
Such windows just leave their contents blank, except for the margins
(see (margins)), which all windows can regenerate.
The advantage of having a bit-save array is that losing and regaining visibility does not require the contents to be regenerated; this is desirable since regeneration may be computationally expensive, or even impossible. The disadvantage is that the bit-save array can be large and swapping it in can be slow.
When a frame is in use, giving the frame a bit-save array enables the contents of the frame and all the panes to be preserved if the frame ceases to be fully visible. Bit-save arrays for the panes would come into play only if panes were shuffled or substituted within the frame; in most applications, this happens never or rarely, and is accompanied by a thorough redisplay. So normally the frame gets a bit-save array and the panes do not.
.defmetainstvar windows tv:bit-array
This instance variable of all windows holds the window’s bit array,
nil if it has none.
Accessor defsubst for the corresponding instance variable.
.defmetamethod windows :bit-array
Returns the window’s bit array, or
nil if it has none.
.defmetamethod windows :save-bits
nil if this window saves its bits when not exposed.
.defmetamethod windows :set-save-bits flag
Tells this window to start or stop saving its bits when not exposed.
t to start or
nil to stop.
.defmetainitoption windows :save-bits flag
flag may be
:delayed causes the window to acquire a bit-save array
the first time it is deexposed, but not before.
.defmetamethod windows :refresh &optional (type
Restore the saved contents of the window or regenerate the contents,
according to the value of type (and to whether the window has a bit-save array).
Here are the possible values of type:
:refresh-margins. The default definition of
:refreshjust leaves the inside blank except for refreshing any exposed inferiors.
If the window has no bit-save array, type is ignored and
the actions for
:complete-redisplay are always used.
Window flavors ought when possible to provide
:refresh, to complete the job of redrawing the window,
which the system itself cannot know how to do. When these daemons
run, the instance variable
tv:restored-bits-p will be non-
if the window contents were restored from a bit-save array.
If this is so, there is no need for the
to do anything, except perhaps if the window’s inside size has changed.
.defmetainstvar windows tv:restored-bits-p
:after daemons of
:refresh (and therefore also of
:expose), this is
t if the contents were restored
from a bit-save array. If it is
nil, the inside of the window was
left blank and must be regenerated to whatever extent that is
This section discusses the concepts of screen arrays and of exposed windows. These have to do with how the system decides where to put a window’s contents (its pixels), how the notion of visibility on the screen is extended into a hierarchy of windows, and how programs can control which windows are visible. Do not feel it is your fault if this seems complicated; you do not need to understand it fully on your first reading of the manual.
Each window or screen can have a screen-array, which is where output
drawn on the window should go. Drawing characters or graphics is done
by changing elements of the window’s screen array. The screen array is
stored in the instance variable
tv:screen-array. The variable can
nil, to say that the window does not have a screen array at
the present time.
A screen normally has a screen array that is displaced to the special memory that the screen’s hardware displays from. A window that is visible has a screen array; it is an indirect array that points into the area of the superior’s screen-array where the inferior gets displayed on the superior. For example, consider a visible window whose superior is a screen and whose upper-left-hand corner is at location (100,100) in the screen. The window’s screen-array would be an indirect array whose (0,0) element is the same as the (100,100) element of the screen. If you were to set a pixel in the window’s screen-array, the corresponding pixel in the screen (found by adding 100 to each coordinate) would be set to that value.
A visible window more than one level down from the screen has a screen array that indirects more than once. The window’s screen-array points into the middle of its superior’s screen array, which points into the middle of the superior’s superior’s screen array, and so on until the screen is reached. When typeout is done on the window, it will appear on the screen, offset by the combined offsets of all the ancestors, so that it will appear in the correct absolute position on the screen.
Sometimes a window is unable to have a screen array that points
to its superior’s screen array. For now, let’s not ask why this might happen,
but consider instead what to do about the screen array when this does happen.
There are two alternatives. If the window has a bit-save
array, then the bit-save array is used as the screen array. If there is
no bit-save array, there can be no screen array either. The window’s
tv:screen-array variable becomes
nil, and there is
nowhere for output on this window to go.
For a window w with a bit-save array, w’s inferiors are not affected by where w’s screen array points. w always has a screen array, and its inferiors’ screen arrays can point to that.
But if w has no bit-save array, it may have
nil instead of a
screen array, and in that case it is impossible for w’s inferiors to have
screen arrays pointing into w’s screen array. So they in turn must
use their bit-save arrays, if any, as screen arrays, or not have screen
arrays. The effect propagates down the hierarchy.
So we see one possible reason why a window may be unable to have a
screen array that points into its superior’s: if the superior doesn’t
have a screen array at all. There is one other reason: the superior may
deny permission for this window to point its screen array into the
superior. The superior has an instance variable
tv:exposed-inferiors which record all the inferiors permitted to do
this. (Only active inferiors are allowed.) This permission can be
granted or revoked at any time, and is called exposability. Each
window can be made exposable or not exposable using the
:deexpose operations. So, if a window’s superior does not have
a screen array, or if the window is not exposable, then the window
must scrounge up a screen array itself if it can.
A window is said to be exposed if it has a screen array that points into its superior’s screen array. Note that a window must be exposable in order to be exposed, but the converse is not true. An exposable window is exposed as well if and only if its superior has a screen array.
An exposed window is not necessarily visible. A window is visible if
its screen array points, through some number of levels of indirection,
into the middle of the screen’s screen array. An exposed window’s
screen array points into the middle of something, but that may be a
bit-save array in a deexposed ancestor some number of levels up. A
window that is exposed but not visible must have some ancestors that are
not exposed, and at least one of them must not be exposable either. This diagram
of a window
w8 and its ancestors shows the pattern of exposed and
deexposed windows and how it comes about.
s <-- w1 <-- w2 <-- w3 <-- w4 <-- w5 <-- w6 <-- w7 <-- w8 exposable not! exposable again... w5 has a bit-save array exposed... deexposed... exposed... visible... invisible... ...still invisible...
Output is allowed on a window whenever the window is exposed. Usually exposed windows are visible and the output can be seen on the screen. But output to an exposed window with a deexposed ancestor is also permitted. Then the output goes into the middle of that ancestor’s bit-save array rather than onto the screen. Such output cannot actually be seen. But if the unexposable ancestor that must exist is made exposable, the bit-save array will be copied onto the screen and the output already done will be seen.
Output is not normally allowed on a deexposed window, even if the
window has a screen array which is its bit-save array. However, in
this case, you can use
tv:sheet-force-access to override the
prohibition and output onto the bit-save array. Use of
as the window’s deexposed typeout action (see (deexposed-typeout-action))
allows all output on such windows to proceed and draw in the
bit-save array. A deexposed window with no bit-save array cannot
have output done on it in any fashion since it has no contents.
:expose operation makes a window exposable. If at
that time its superior has a screen array, the window will become
exposed as well. Or, if the superior later acquires a screen array, the
window will become exposed then. This can happen if the superior itself
is exposed, or if the superior is given a bit-save array with the
:deexpose operation always makes the window unexposable
and therefore not exposed.
It is possible for a screen to be deexposed. In particular, if a Lisp Machine does not have a color display physically attached to it, there is still a "color screen" Lisp object in the Lisp world, but it is deexposed (and so are any immediate inferiors it may have). This is so saved Lisp environments can be moved easily between machines with different hardware configurations. The screen object is left deexposed so that programs will not try to output to it. The screen is exposed whenever the Lisp Machine system is booted on a machine that actually has a color screen; then all its exposable inferiors become exposed too. For screens, there is no distinction between exposed and exposable, since there is no superior to have a say in the matter.
In order to maintain the model that windows are like pieces of paper on a desk, it is important that no two windows that both occupy some piece of screen space be exposed at the same time. To make sure that this is true, whenever a window becomes exposed, the system deexposes any of its exposed siblings that it overlaps. (Note: this is not true for temporary windows; see (temporary-window).)
.defmetamethod "windows and screens" :expose &optional inhibit-blinkers bits-action new-left new-top Makes the window exposable, and exposed if possible. This is a very useful operation to attach daemons to, but remember that this operation may be performed on a window that is already exposable. The daemons must not make the assumption that the window is just becoming exposable. If the window is not a direct inferior of the screen, it may not be becoming exposed either.
If the window is not active in its superior, it is first activated.
The arguments to the
:expose operation are supplied by
the system and usually of interest only to the system’s methods.
User invocations of this operation should usually supply no arguments.
If the window actually becomes visible, the window’s blinkers normally
appear with their deselected visibilities. If inhibit-blinkers is
nil, the blinkers are not acted on. If the window is being
exposed in order to select it, this is used to save time.
If the window actually becomes visible, bits-action controls how
it is put back on the screen. It can be
:clean. If it is
:noop, the window’s screen area is not
touched. This is used only in very unusual cases. If it is
:clean, the window is sent a
:refresh message with argument
:complete-redisplay, which should make the window redraw itself
from scratch if it can. If bits-action is
window is sent a
:refresh message with argument
which should make the window copy its bit-save array onto the screen.
nil as the bits-action is equivalent to
windows with bit-save arrays and to
:clean for windows without them.
new-left and new-top are the offsets within the superior at which
to expose the window. They default to the window’s current offsets.
These arguments are for use by the
:set-edges operation; you should
not pass them.
A window cannot be made exposable unless its full size fits within the superior. .end_defmetamethod
.defmetamethod "windows and screens" :deeexpose &optional save-bits-p screen-bits-action remove-from-superior Makes the window not exposed and not exposable. This is a useful operation to add daemons to.
The arguments to the
:deexpose operation are supplied by
the system, and are usually of interest only to the system’s methods.
save-bits-p defaults to
:default. It can also be
:default means the bits are saved
if the window has a bit-save array.
the window a bit-save array if it doesn’t already have one,
so that the bits are always saved.
nil does not save the bits.
screen-bits-action controls what to do to the bits on the screen.
It may be
:noop to do nothing to them, or
:clean to erase
the area occupied by the window.
If remove-from-superior is
nil, the window remains exposable.
You should always use
t (which is the default) for this argument.
The window system uses
nil as part of implementing deexposure of
an exposable window whose superior loses its screen array. Use of
nil at any other time would lead to incorrect results.
.defmetainitoption windows :expose-p t-or-nil
If this option is specified non-
nil, the window is made exposable
after it is created. The default is to leave it deexposed. If the
value of the option is not
t, it is used as the first argument to
:expose operation (the inhibit-blinkers argument).
.defmetamethod "windows and screens" :exposable-p
t if the window is exposable.
.defmetamethod "windows and screens" :exposed-p
t if the window is exposed.
.defmetamethod "windows and screens" :exposed-inferiors Returns a list of all exposable inferiors of this window or screen. .end_defmetamethod
Executes the body with sheet deexposed. If sheet had been exposed, it is reexposed when body exits. Operations that change things about the window often make use of this to reduce the complicated case of an exposed window to the simpler case of a deexposed one.
.defmetamethod "windows and screens" :screen-array
Returns the window or screen’s screen array, or
.defmetainstvar "windows and screens" tv:exposed-p
t if the window is exposed.
.defmetainstvar "windows and screens" tv:exposed-inferiors A list of all exposable inferiors of this window or screen. .end_defmetainstvar
.defmetainstvar "windows and screens" tv:screen-array
The screen array, or
nil if there is none.
Whether a window is exposed usually controls whether output can be
done on it. In a deexposed window a flag called the output hold
flag is normally 1. This causes an output hold exception if an
attempt is made to output to the window. The normal result of an
output hold exception is that the process doing output waits until the
output hold flag is clear. The process wait state during this wait is
The output hold flag is also set in a window that has exposed inferiors, because output on the window would overwrite the inferiors.
1 to indicate an output hold exception, or 0 to permit
output on the window. This is
When a process attempts to type out on a window which is deexposed and has its output hold flag set, what happens depends on the window’s deexposed typeout action. The deexposed typeout action can be any of certain keyword symbols, or it can be a list. After the specified action is taken, if the output hold flag is still set, the process will wait for it to clear. The interesting thing is that the action may affect the value of the output hold flag.
.defmetainstvar windows tv:deexposed-typeout-action The window’s deexposed typeout action. .end_defmetainstvar
.defmetamethod windows :deexposed-typeout-action .defmetamethod1 windows :set-deexposed-typeout-action action Get or set the window’s deexposed typeout action. .end_defmetamethod
.defmetainitoption windows :deexposed-typeout-action action Initializes a window’s deexposed typeout action to action. .end_defmetainitoption
Accessor defsubst for the instance variable.
Here are the possible values of deexposed typeout action:
:exposemessage. This may expose the window (if the superior has a screen-array), and if it does expose the window then the output hold flag will probably be cleared, allowing typeout to proceed immediately. If the superior is the screen, the
:exposeoption provides a very different user interface from the
The action for
:permit is to turn off the output hold flag if the
window has a screen array. This mode has the disadvantage that output
can appear on the window without anything being visible to the user, who
might never see what is going on and might miss something interesting.
It is possible to request that output in this mode to partially visible windows be transferred to the screen periodically. See (deexposed-permit-in-background).
:noticemessage to the window with the argument
:output(see (windows-notice-method)). The default response to this is to notify the user that the window wants to type out and to put the window on a list for
Terminal 0 Sto select it. Supdup and Telnet windows have
:notifydeexposed typeout action by default.
Functions such as
ed, whose purpose is to select a window
for the user, should not return immediately. If
ed returned immediately,
then when called in a Lisp listener with its deexposed typeout action set to
:expose, the printing of the value returned by
ed would immediately
switch back to the Lisp listener, which defeats the purpose of
To avoid this behavior,
terminal-io is exposed (more precisely, until its
:await-exposure operation returns).
.defmetamethod windows :await-exposure Does not return until the window is exposed. (Some window flavors implement it differently). .end_defmetamethod
tv:sheet-force-access allows you to do typeout on a window that has
a screen array even if its output hold flag is set. It works by turning
off the output hold flag temporarily around the execution of the body.
This is useful for drawing on a window while it is not visible.
For example, changing the menu items of a menu redraws the menu contents
immediately even if the menu is not visible; this is because when the
menu does become visible it looks better to the user for it to become
visible in one instant with the correct contents.
If the window is exposed,
tv:sheet-force-access goes ahead and outputs to it.
If the window is not exposed but has a bit-save array, the output goes there.
If the window is not exposed and has not bit-save array
tv:sheet-force-access doesn’t do anything at all; it just returns
without evaluating its body.
Here is an example: when a text scroll window is given a new item generator, which completely changes the text that it should display, it redisplays the window in its bit-save array if necessary.
(defmethod (tv:text-scroll-window :set-item-generator) (new-item-generator) (setq item-generator new-item-generator) (tv:sheet-force-access (self) (send self ':clear-screen) (send self ':redisplay 0 (tv:sheet-number-of-inside-lines))))
Each window or screen has a lock which is used to prevent two processes
from operating on the window at once in a way that might cause
inconsistent results. Outputting on the window, activating or
deactivating the window, exposing or deexposing the window, and changing
the window’s shape all lock the window. This is done with
tv:lock-sheet. Note that the window’s inferiors must be locked too.
Another form of locking is called "temp-locking". A window is temp-locked
when a temporary window (see (temporary-windows)) is exposed on top of it.
All the operations which lock the window will have to wait if the window
is temp-locked just as they would if the window were locked in the ordinary manner;
however, the lock is not considered owned by a process but rather by the
temporary windows that overlap the window. It will stay locked until
the temporary windows are all deexposed. The
and some other things know how to deexpose temporary windows when necessary
to cause a window to become unlocked.
Executes body with window-or-screen locked by this process.
tv:lock-sheet are found in wrappers for operations such as
:expose, so you need not call it yourself, but you should be aware
that it is being done.
.defmetainstvar "windows and screens" tv:lock
The lock. It is
nil for an unlocked window, a process that has
locked the window, or a list of covering temporary windows if this
window is "temp-locked".
.defmetainstvar "windows and screens" tv:lock-count The number of times the lock is locked. This counts the number of recursive lockings for the same process, for example. .end_defmetainstvar
Returns the contents of window-or-screen’s lock.
This is a defsubst and can be
It is usually unmodular to use this.
t if this window or screen could right now be locked by
lock-id; essentially, if it is free or already locked that way
(but in fact it is more complicated than this.)
Note that if you call this function with
you are likely to be susceptible to a timing error.
Unlocks the locks of all active windows. For use in an emergency.
Normally, when a window is exposed in an area of the screen where there are already some other exposed windows, the windows that used to be there are deexposed automatically by the window system. This is because the window system normally doesn’t leave two windows both exposed if they overlap. (In the absence of temporary windows, which we are about to introduce, the system never allows two overlapping windows to both be exposed.)
But sometimes there are windows that only get put up on the screen for a very short time. The most obvious examples of such windows are the momentary menus that only appear for long enough for you to select an item. It would be unfortunate if every time a momentary menu appeared, the windows under it had to be deexposed. The ones without bit-save arrays would have their screen image destroyed, forcing them to regenerate it or to reappear empty. The ones with bit-save arrays would not be damaged in this way, but they would have to be deexposed, and deexposure is a relatively expensive operation.
This problem is solved for momentary menus by making them temporary windows. Temporary windows work differently from other windows in the following way: when a temporary window is exposed, it saves away the pixels that it covers up. It restores these pixels when it is deexposed. These pixels may come from several different windows. This way it doesn’t mess up the area of the screen that it uses, even if it covers up some windows that don’t have bit-save arrays.
Also, a temporary window, unlike a normal window, does not deexpose the windows that it covers up. This way the covered windows need not try to save their bits away in their bit-save arrays (if they have them) nor ever have to try to regenerate their contents (if they don’t). They never notice that the temporary window was (temporarily) there.
.defflavor tv:temporary-window-mixin This mixin makes a window a temporary window. .end_defflavor
.defmetamethod windows :temporary-bit-array
nil if the window is a temporary window.
There would be some problems if temporary windows were this simple. Suppose there is a normal window, and a temporary window appears over it; some of the contents of the normal window are saved in an array inside the temporary window. Now, if the normal window were moved somewhere else, and possibly became deexposed or overlapped by other windows, and then the temporary window were deexposed, the temporary window would dump back its saved bits where the normal window used to be. This would clobber some other window.
Furthermore, even though normal window is still exposed, output on it must not be permitted, since that could overwrite the temporary window.
Because of problems like these, when a temporary window gets exposed on top of some other windows, all the windows that it covers up (fully or partially) become temp-locked. While a window is temp-locked, any attempt to type out on it will wait until it is no longer temp-locked. Furthermore, any attempt to deexpose, deactivate, move, or reposition a temp-locked window will wait until the window is no longer temp-locked. The temp-locking is undone when the temporary window is deexposed.
Because of temp-locking, you should never write a program that will put a temporary window up on the screen for a "long" time. There should be some action by the user, such as moving the mouse, which will make the temporary window deexpose itself. It is best if any attempt by the user to get the system to do something makes the temporary window go away. While the temporary window is in place, it blocks many important window system operations over its area of the screen. The windows it covers cannot be manipulated, and programs that try to manipulate them will end up waiting until the temporary window goes away.
It works fine to have two or more temporary windows exposed at a time. If you expose temporary window a and then expose temporary window b, and they don’t overlap each other, they can be deexposed in either order, and any windows that both of them cover up will be temp-locked until both of them are deexposed. If b covers up a, then a will be temp-locked just like any other window, and so it will not be possible to deexpose a until b has been deexposed.
Sometimes not all of the screen is in use by fully visible windows.
This does not happen in elementary use of the Lisp Machine, since the
initial windows in the system are all full-screen-sized, but if you
create a small Lisp listener with system menu
Create the rest of
the screen will be unclaimed by any fully visible window. The part of
the window system responsible for dealing with unclaimed parts of the
screen is called the screen manager.
The screen manager fills such unclaimed areas by looking for deexposed windows which fall entirely or partly within them. Only active immediate inferiors of the screen are considered, and in a specific priority order described in (screen-manager-priority).
A window that falls entirely within unclaimed areas can be made visible without deexposing any other windows. This is called autoexposure. Since the window is a direct inferior of the screen, exposing it always makes it visible. The screen manager goes on considering the remaining deexposed windows, but with less screen area unclaimed.
A window that overlaps the unclaimed areas but also overlaps a visible window cannot simply be exposed. So it becomes partially visible, which means simply that the screen manager copies the appropriate parts of the window’s contents onto the unclaimed areas. The window is not treated as visible or exposed in any other sense. This gives the visual impression of overlapping pieces of paper on a desk top; the deexposed window is partially covered up by the visible windows, but you can still see those parts that aren’t covered. The contents are copied from the window’s bit-save array. Windows without bit-save arrays are by default ineligible for partial visibility, so other windows later in the order will get a chance for the same screen area; however, it is possible to arrange for windows without bit-save arrays to be partially visible (though the displayed contents may not be accurate).
Windows whose size and position are such that they do not fit within the bounds of the superior cannot be exposed, and the screen manager does not try to autoexpose such windows. However, they can be partially visible like any other windows.
The screen manager has one other job. At the same time that it
does autoexposure, it can also select a window if there isn’t any
selected window at the time. This is called autoselection. A
window is a candidate for autoselection if it is an exposed inferior
of the screen and its
:name-for-selection is non-
(windows-name-for-selection-method)). For more information, see
The screen manager does not only manage the inferiors of screens; it
can manage the inferiors of windows as well. The system invokes the
screen manager on a sheet’s inferiors by sending the sheet a
:screen-manage message. This happens for all visible sheets
regardless of flavor.
.defmetamethod "windows and screens" :screen-manage The default definition of this operation is to do autoexposure and display of partially visible windows among the active inferiors of this window or screen, as described above. .end_defmetamethod
Prevents the screen manager from dealing with the inferiors of a window
by redefining the
:screen-manage operation to do nothing.
When a frame is used by a single program, the program usually expects to have sole control over exposure of panes. Then this mixin can be used to tell the screen manager not to interfere. Constraint frames do not normally need to use this mixin because they avoid problems while changing configurations by deactivating any panes that do not belong in the configuration. Zmacs frames do use this mixin so that the screen manager will not autoexpose various editor windows that belong to the frame. .end_defflavor
.defmetamethod "windows and screens" :screen-manage-autoexpose-inferiors
Performs autoexposure of the active inferiors of this window or screen.
Used by the default definition of
.defmetamethod windows :screen-manage-deexposed-visibility
Should return non-
nil if parts of this window ought to be displayed
when the window is partially visible. The default definition returns
nil if the window has a bit-save array.
If a window has this flavor mixed in, then the screen manager will
attempt to show it to the user when it is partially visible even if it
doesn’t have a bit-save array. Since there are no saved contents to
display, the screen manager must give the window a screen array
temporarily, send it a
:refresh message so it will draw itself on
the screen array, and then display whatever is found there. Often this
means that you will see the label and borders of the window, but not
Make any visible parts of the window appear gray if the
window is not fully visible.
faster, but does not work for windows that have inferiors. You would use
these mixins in windows without bit-save arrays, as a cheaper alternative
tv:show-partially-visible-mixin, to provide something
better than blankness when the window ought to be partially visible.
The precise kind of gray is controlled by the instance variable
tv:gray-array, which comes with operations
:set-gray-array and init option
:gray-array. The value must
be a two-dimensional array of bits that will be replicated by
bitblt; its width must be a multiple of 32. Useful values for
Causes a window not to appear through screen management, even partially,
until it has first been explicitly exposed. This is used in some window
flavors (such as editor windows, Supdup windows, and others) of which
instances are present in the saved system environment even without the
user’s ever having requested them. These windows can be active, and
System keys to select, but will not become partly
visible if some other window is made smaller.
Recall that if a deexposed window has its deexposed typeout action set
:permit, output on the window can proceed but goes to the
bit-save array rather than to the screen. If the window is partially
visible, such output could modify the visible parts of the window.
You can request that the screen manager check periodically for such
output and copy the changed contents to the screen.
Controls whether the screen manager looks for
partially-visible windows with deexposed typeout actions of
updates the visible portion of their contents on the screen. If the value
nil, which it is initially, the screen manager does not do this.
Otherwise the value should be the interval between screen updates, in 60ths
of a second.
Suppose there is a section of the screen in which there are no exposed windows, and more than one active, deexposed window could be exposed to fill this area, but the two could not both be exposed (because they overlap). Which one gets to be exposed? Here’s another issue: when the screen manager wants to display pieces of partially-visible windows, there may be more than one deexposed window that could be displayed in a given area of the screen. How does screen manager decide which window to display?
It decides on the basis of a priority ordering. All of the active inferiors of a window are maintained in a specific order, from highest to lowest priority. When there is a section of the screen on which more than one active inferior might be displayed, the inferior that is earliest in the ordering, and so has the highest priority, is the one that gets displayed. This ordering is like the relative heights of pieces of paper on a desk; the highest piece of paper at any point on the desk is the one that you see, and all the rest are covered up.
.defmetamethod "windows and screens" :order-inferiors
tv:inferiors list of active inferiors of
this window or screen into the proper order for considering them
for autoexposure or partial visibility.
The default definition of
:order-inferiors uses a complicated
algorithm which is designed to put the most recently exposed windows
first, but also allows the programmer to specify priorities explicitly.
If you do not need to know the details, you can safely skip the rest of
The algorithm involves a value assigned to each window called its
priority, which may be a fixnum or
nil. The general idea is
that windows with higher numerical priority values have higher priority
to appear on the screen. The default value for the priority is
which is considered less than any numeric value.
.defmetainstvar windows tv:priority
The window’s priority value, a number or
.defmetamethod windows :priority .defmetamethod1 windows :set-priority new-priority Get or set the window’s priority value. .end_defmetamethod
.defmetainitoption windows :priority priority Initializes the window’s priority value. .end_defmetainitoption
The standard ordering of inferiors puts all exposable inferiors first, followed by the unexposable inferiors in order of decreasing priority. Each group of unexposable inferiors with the same priority is order by how recently they were exposable; the longer an inferior has gone without being exposable, the farther back it moves.
This is done by computing the current ordering based on the past
ordering (as remembered by the old value of
tv:inferiors). When the
window system does anything which should change the ordering, such as
making a window exposable or not exposable, it invokes the
:order-inferiors operation to update the recorded ordering.
The ordering is updated by moving the exposable windows to the
front and sorting the unexposable ones by priority.
The sort is stable; that is, unexposable windows with the same
priority value keep their previous ordering. Since most of the time
numerical priorities are not used anyway (the priorities of most windows
nil), the ordering generally changes only as a result of
exposure and deexposure of windows. When a window becomes exposable it
gets pulled up to the front of the ordering; then when other windows
become exposable instead, this window sinks back down. Thus, the
ordering ends up showing simply how recently each window was exposable.
There is also an operation called burying a window, which deexposes
the window and puts it at the end of its priority grouping in the
ordering. A program typically buries its window when it thinks that the
user is not interested in that window and would prefer to see some other
Bury command in
Edit Screen is a way for the user
to bury a window.
.defmetamethod windows :bury
Buries the window. See also
a convenient interface to this operation ((tv:deselect-and-maybe-bury-window-fun)).
Negative priorities have a special meaning. If the value of a window’s
priority is -1, then the window will not ever be visible at all even
if it is only partially covered; however, it will still get autoexposed.
If the value of priority is -2 or less, then the window will not
even be autoexposed, and so it will simply never be seen unless
sent an explicit
The screen manager can potentially interfere with the actions of a
program that explicitly deexposes windows. Suppose you send a
:deexpose message to an exposed window. The screen manager will
run, and will probably autoexpose that very window, canceling the
effect of the
:deexpose. That window certainly does not overlap
any still-visible windows, and it is as recently-exposed as a window
can get, so it will be the first candidate for autoexposure.
Explicit deexposure is usually done at the beginning of a sequence of window rearrangements. For example, moving an exposed window is done by deexposing it, changing its position (which is easy when it is deexposed) and reexposing it. We want the screen manager to run when the whole sequence is complete; it should not consider the transient intermediate states. Even if the screen manager did not directly interfere with the program’s deliberate actions, it would waste time and confuse the user by displaying partially visible windows in temporarily-unclaimed screen areas for which the program is already preparing a new use. (This is a general phenomenon. Management is a useful auxiliary function, but managers have a tendency to interfere with work they don’t understand if there is no way to shut them off.)
We shut the screen manager off with the special form
tv:delaying-screen-management. While its body is being executed,
events that would normally bring about screen management are recorded
on a queue instead. When the
is exited (whether normally or by throwing), the screen manager looks
at the queue and does all necessary screen management in one blow.
Sometimes it happens that screen management cannot be done when the
tv:delaying-screen-management form is exited, because relevant
windows are locked by other processes. Then the entries are left on
the queue. They are handled at some later time when the necessary
locks are free by a background process called
Background. So the necessary screen management always does
eventually get done.
tv:delaying-screen-management forms are nested,
only the outermost one will do any screen management when it is exited.
The body forms are evaluated sequentially with screen management delayed. The value of the last form is returned.
The body forms are evaluated seqentially with screen management delayed. Moreover, if the body completes normally, the queue entries put on by its execution are removed from the queue, on the assumption that the body has itself done all appropriate screen redisplay. If the body terminates abnormally with a throw, the queued entries remain on the queue and are processed by the screen manager eventually.
At any time, keyboard input is directed to at most one window, designated by programs or by the system in response to user commands. This window is called the selected window. A process trying to do input through another window will normally wait until the window is selected (however, the window’s deexposed typein action can change this; see below).
The value of this variable is the selected window. You should not set this variable yourself, but use the defined interfaces described below.
A window’s cursor blinker normally blinks only when the window is selected. This is how the user can tell which window is selected. (You can control what happens to each blinker when its window becomes selected; see (blinker-visibility).)
The user can change the selected window using the
System keys or the system menu. Also, clicking the mouse on
a window usually selects that window if it is meaningful to do so.
The simplest, and paradigmatic, case of window selection happens if you
have several independent windows on the screen, such as Lisp listeners.
One of them displays a blinking cursor, and input echoes there. The
processes in the others remain in a keyboard input wait, as you can see
if one of the windows on the screen is a Peek. The mouse, or the
Terminal O command, can be used to select a different window.
The selected window needs to handle certain operations that windows in
general do not need to handle. The flavor
these operations, and should be used in flavors of windows that are
going to be selected. (A window can be useful without being selectable.
For example, menus cannot be selected.) The flavor
If two processes try to read from the same window (or windows sharing an
input buffer), it is unpredictable which one will get the input. If you
are designing an application where this might happen, you must make sure
than you will not have two processes actually active and reading input
from the same source at the same time. In most applications there will
be only one process that ever reads input from any one window or input
buffer. In these applications you should use
the window flavor to tell the window which process is associated with
it (see (tv:process-mixin-flavor)).
The selected window controls the actions performed by the system at
the instant a character is typed on the keyboard. Due to typed-ahead
commands that switch windows (such as
Control-Z in the editor), there
is no way to know for certain which window will eventually read a
character being typed at a given moment, so letting the selected window
decide asynchronous processing for the character is the best that can be done.
Asynchronous processing options include asynchronous intercepted characters
(see (asynchronous-intercepted-characters)) and case conversion of control characters
Asynchronous intercepted characters such as
Control-Abort which act on a process
ask the selected window which process to operate on, with the
The who line usually does the same thing to find the process whose run state should
be displayed. If you use
:process returns the process associated
with the window; otherwise, a default definition of
:process is inherited from
tv:select-mixin and returns whichever process last read input from the window
(or from any other window sharing the same input buffer).
This is fine for the who line, but can lead to weird results in
So you should use
tv:process-mixin whenever it makes sense.
If a process tries to do input from a window whose input buffer is empty
and not selected, it cannot get any input and must wait. (The input buffer
is selected if this window, or any other window sharing the same input buffer,
is the selected window). The wait ends
when input appears in the buffer, or when the buffer becomes selected
and there is keyboard input available. If the window
is not even exposed, a notification may happen in addition. This is
controlled by the window’s deexposed typein action, which may be
:notify means that input from
the window when it is deexposed should notify the user (see
(tv:notify-fun)) with a message like "Process X wants typein", and make
the window "interesting" so that
Terminal 0 S can select it.
.defmetainitoption windows :deexposed-typein-action action
Initializes the "deexposed typein action" (see (deexposed-typein-action))
of the window to action. It defaults to
.defmetamethod windows :deexposed-typein-action Returns the "deexposed typein action" (see (deexposed-typein-action)) of the window. .end_defmetamethod
.defmetamethod windows :set-deexposed-typein-action action Sets the "deexposed typein action" (see (deexposed-typein-action)) of the window to action. .end_defmetamethod
Programs change the selected window using the
.defmetamethod windows :select &optional (remember-previous
Makes this window (or its selection substitute, if any) the selected window.
nil, the previous selected window
is entered on the list of previously selected windows for the
System keys to use.
Many application window flavors define daemons for this operation. Note, however, that the daemons will be run whenever this operation is invoked, even if the window is already selected. .end_defmetamethod
No window can actually be selected unless its flavor includes this mixin.
tv:select-mixin is part of
tv:window but not part of
Windows whose flavors do not contain this mixin may be sent
only if they have designated other windows as selection substitutes (see below).
The ultimate substitute which is finally selected must have
.defmetamethod windows :selected-p
t if this window is the selected window.
.defmetamethod windows :mouse-select args
Selects a window, for a mouse click or for asynchronous keyboard input
such as the
While mostly the same as sending
:select to the window’s alias for
selected windows (see (previously-selected-windows-section)), this
operation also causes all type-ahead input to remain with the window
that used to be selected (see (tv:kbd-snarf-input-fun)).
Note that the
:mouse-select operations should not
be invoked in the mouse process. This means that if you want to use them
you must do
(process-run-function "Select" window-to-select ':mouse-select)
.defmetamethod windows :deselect &optional (restore-selected
This operation is invoked by the window system when a window
ceases to be selected. This can be because the window is no longer visible,
or because some other window is being selected.
Many application window flavors defined daemons for the
restore-selected controls what will be done with this window in the
tv:previously-selected-windows array used by the
Terminal S and
System commands, and whether to select automatically some other
window found in that array. The possible values are
Deselects window, selecting the previously selected window.
If that causes window to become deexposed, window
is buried. deselect-mode is passed to the
where it controls where to put the window in the list of
previously selected windows used by the
Execute body with window selected.
tv:window-call uses the
:select operation to do this, while
:mouse-select operation; that is how they differ. On exit, they reselect the
window that had been selected before, and send window a message
with operation exit-operation and arguments exit-arguments.
exit-operation is often
:deactivate. It can also be omitted;
then nothing is done to window except for deselecting it because
some other window is selected.
These constructs are no longer as useful as they once were. For controlling selection among windows of a team, selection substitutes (see (selection-substitutes)) should be used now instead.
The simple paradigm of selection is based on windows that are independent competitors for the user’s input, such as a pair of Lisp listeners. Another frequent situation is a to have a group of windows that act as a team. Usually the windows consist of a single frame and its panes, and usually they are managed by a single process, but neither of these is necessarily so. Often the windows of a team share an input buffer to make it easier for one process to read input from all of the windows at once (see (shared-input-buffers)); this is an important technique which you should definitely read about if you are designing a team of windows.
The simple paradigm extends cleanly to teams if we imagine that the user regards each team as a unit of selection. In this extended paradigm, the user selects among entire teams as if they were single windows.
Teams are not actual Lisp objects, merely concepts understood by the user and programmer. The window system cannot have a selected team; some window of the team must be selected. Each team’s program chooses a window of the team as the team’s selection representative. The selected window should then be the selection-representative of the user’s chosen team. The selected window can change when the user chooses a new team, or when the user’s chosen team picks a new representative.
To implement this, the programmer of the team first picks one window of the team to be the "leader". This is not the same as the selection representative; that can change from moment to moment, but the leader must be fixed. When the team is a frame and its panes, it is natural to make the frame be the leader. Standard mixins are provided to make this easy to do. These mixins and the techniques of using them are described below, and in the following sections.
The selection representative is implemented as the leader’s selection
substitute (see (selection-substitutes). Then the team can be selected
:select operation on its leader window.
Even when the team allows the user some notion of selecting among the windows of the team–such as, when a Zmacs frame in two-window mode allows the user to mouse either of the editor windows to select it– this is implemented most cleanly by starting from the model of a team which does all selection under program control, and defining the appropriate mouse clicks as commands which tell the team’s program to change its selection representative.
Usually you will want to have only a single item appear for the team in
the system menu
Select option’s menu. If the team consists of a
frame which is the leader and its panes, this can be done with
tv:inferiors-not-in-select-menu-mixin in the frame’s flavor.
More complicated behavior is also possible; for example, Zmacs frames
in two-window mode allow each editor window to have its own entry
System commands should reselect the team by
selecting its current selection representative. This is done by making
them record and reselect the team’s leader. If the team consists of a
frame which is the leader and its panes, this can be done with
tv:alias-for-inferiors-mixin in the frame’s flavor. (In case you
are curious, Zmacs frames follow this pattern exactly. The frame is the
alias for any editor windows inside it.)
The following subsections describe the details of how these things are done.
Select option in the system menu is used, it gets the list
of alternatives to offer by sending each screen a
:selectable-windows message. This operation is normally defined to
recurse down the window hierarchy and ask each window whether it wants
to be included. Each window is sent a
The value should be either
nil (omit this window) or a string,
which is the string to display in the menu of windows.
.defmetamethod windows :selectable-windows
Returns an alist of strings versus windows, which will become part of the
alist that will be displayed in the system menu
Select option’s menu.
The alist returned should describe this window and its inferiors,
or whichever of them ought to appear in that menu.
The normal definition includes this window using its
:name-for-selection as the car of the alist element, or omits this
window if its
nil. It then appends the
:selectable-windows values obtained from the window’s inferiors.
This mixin redefines
:selectable-windows to ignore the window’s
inferiors. They are not asked whether they should be included. This is
an easy way to make a team of a frame and its panes have only one entry,
the entry for the frame.
.defmetamethod windows :name-for-selection
This operation is supposed to return a string to display in
the system menu
Select option’s menu of windows for this window.
It may also return
nil, meaning do not list this window in the menu.
The default definition uses the window’s label string if any, or else
its name. Many applications redefine the operation.
tv:not-externally-selectable-mixin redefines it to return
If you want more complicated behavior from a team than simply having a single entry, you can get it by redefining this operation on the flavors of various windows in the team.
This operation also affects autoselection, done by the screen manager.
A window can be autoselected only if its
:name-for-selection is non-
This variable’s value is an
art-q array whose contents are all
the active windows, not including the selected window, which the
System key commands for window selection should
know about. The windows of a team are generally all represented by a
single member of the team, which we can call the "leader".
Typically the "leader" is a frame which contains the rest of the team as panes,
but this is not required.
Terminal S command can be thought of as acting on a combined
list that contains the selected window followed by the previously
selected windows. So,
Terminal n S rotates the first n
elements of this list, so that the selected window becomes the first
previously-selected window, and the nth previously selected window
becomes the selected window. The
System key also uses this data
base to find a window of the appropriate flavor to select, or to rotate
through all the windows of that flavor.
Windows are put on
tv:previously-selected-windows and taken off of
it automatically when they are selected, deselected, activated or
deactivated. Attention is required from the applications programmer
only to identify teams of windows that should be treated as a unit. The
system uses the
:alias-for-selected-windows operation to inquire
.defmetamethod windows :alias-for-selected-windows
Should return the window to represent this one in
tv:previously-selected-windows. When this window gets deselected,
its alias is what will be recorded in that array. In the simple
paradigmatic case of independent Lisp listeners, the alias of each
Lisp listener is itself. For a window in a team, this should return
the team’s "leader" window.
The default definition of this operation is to return the superior’s
:alias-for-inferiors if that is non-
nil, otherwise to return
this window itself.
.defmetamethod windows :alias-for-inferiors
Should return a window to serve as the alias for all inferiors to all
levels of this window, if that is desired. Otherwise it should return
The default definition returns this window’s superior’s
:alias-for-inferiors. Thus, if an ancestor of this window says it wants
to be an alias for all of its descendants, we pass on its request, but
otherwise we allow the descendants to decide for themselves.
This mixin makes a window be an alias for all of its inferiors.
Thus, the window and all of its inferiors form a team considered as a unit
System commands, and this window is the
If two windows in a hierarchy, one above the other, both have
tv:alias-for-inferiors-mixin, then the higher one "wins". Put
another way, windows are grouped into the largest possible teams,
and there are no subteams within teams.
Note also that no record is kept of which window in a team was actually
selected most recently.
tv:previously-selected-windows records only
the alias or team leader window, and this is the window that will
:select message if a
Terminal command is given to
switch back to that team. The way to make sure that the proper window
within the team is selected is to use selection substitutes, as described
in the following section.
This mixin makes a window (and its descendants) have the window’s
superior as an alias, and keeps the window out of the
Using this mixin, you can control more closely which windows are
distinguished by the
Select menu and by
instead of making the top of the team’s hierarchy be an alias for
all of its descendants, specifically chosen descendants are given
this mixin so that they are not distinguished, and any other descendants
An older name for this mixin, which still works, is
Every window has the ability to designate a "selection substitute".
If a window has a substitute, requests to select or deselect the
original window will be passed along to the substitute. The substitute
may have a substitute of its own, and so on. A window’s selection
substitute is remembered in the instance variable
tv:selection-substitute, whose value is another window or
.defmetainstvar windows tv:selection-substitute
The window’s selection substitute, or
The main use of selection substitutes is for controlling selection
within a team of windows. The team has one window designated as the
leader; all user requests to select the team come as
to the team leader as a result of arrangements described in the previous
section. As a result, the team’s program can choose a selected window
within the team by making it the leader’s selection substitute.
:alias-for-selected-windows of the substitute window should be
the same as that of the window it substitutes for, to avoid paradoxical
results from the
Terminal command. With a hierarchical team of
windows, this is usually arranged by using
tv:alias-for-inferiors-mixin in the top window of the team. The
substitute window should not appear in the system menu
for if it did, its entry and the entry for the window it substitutes for
would be functional duplicates.
tv:inferiors-not-in-select-menu-mixin in the top window of the team
serves to prevent the duplicate entry.
These operations on windows are provided for working with selection substitutes:
.defmetamethod windows :selection-substitute
Returns this window’s selection substitute,
nil if the window does not currently have one.
.defmetamethod windows :ultimate-selection-substitute Returns this window’s substitute’s substitute... and so on until a window is reached that has no substitute. If this window has no substitute, it itself is returned. .end_defmetamethod
.defmetamethod windows :self-or-substitute-selected-p
t if this window, or its substitute, or its substitute’s substitute,
etc., is selected.
.defmetamethod windows :set-selection-substitute substitute
Sets this window’s selection substitute to substitute (another
nil). If it was previously the case that this window or
its substitute was selected, then the window’s new substitute (or the
window itself) will be selected afterward. Thus, the value of
:self-or-substitute-selected-p on this window is not changed by
Note that when the team’s program uses
:set-selection-substitute on the
team’s leader window to change the selected pane within the team, it does not
matter whether the team is currently selected. The "right" results will happen
if the team is deselected and reselected at any time.
To switch the selected pane temporarily, use
Executes body with window as the substitute for for-window. On exit, it sets for-window back to whatever it used to be, and deexposes or deactivates window if appropriate.
Also useful is
Executes body, then selects window if window or its substitute had been selected to begin with.
.defmetamethod windows :remove-selection-substitute window-to-remove suggested-substitute
Makes sure that window-to-remove is not this window’s substitute,
suggesting suggested-substitute (possibly
nil) as a substitute
instead. The standard implementation of this operation simply sets the
substitute to suggested-substitute if the substitute was
window-to-remove. This operation is used and documented so that
particular windows can define their own ways of calculating the new
value for the substitute, perhaps ignoring suggested-substitute.
When a typeout window is deactivated, this operation is used to make sure that it ceases to be another window’s substitute. .end_defmetamethod
Some programs wish to "replace" one window with another temporarily.
For example, the functions
telnet can behave this
way, giving the appearance of temporarily changing the Lisp listener or
other window in which they are called into a Supdup or Telnet window.
They do this by creating a suitable Supdup or Telnet window and
making it the substitute for the original window. In this case, the
substitute window will have the same edges and the same superior as the
original window. It is not an inferior of the original window. It is
not required that the "replacement" window be the same size as the
Non-inferior selection substitutes are usually established and
deestablished by using
tv:with-selection-substitute in a
straightforward manner. The only thing that requires special attention
is to make sure that the original window is the
:alias-for-selected-windows of the substitute. In the case of
supdup this is desirable to complete the illusion that the Lisp
listener has "magically" changed temporarily into a Supdup window.
Since the substitute window is not a descendant of the original one, it
must have some other way to find the original window (such as an
instance variable for the specific purpose) and a specially defined
:alias-for-selected-windows method to return the original window.
A window’s status is a keyword that encodes whether the window is selected, whether it is exposed, and whether it is active.
.defmetamethod windows :status Returns one of these symbols:
.defmetamethod windows :set-status status
Restores the window’s status to status, by selecting or deselecting,
exposing or deexposing, and activating or deactivating, as necessary.
status must be one of the possible values of the
:set-status operations are useful for
selecting a window temporarily and then restoring everything as
:set-status is correct for this because it may
be necessary to deexpose the window or deactivate it in addition
to deselecting it.
A self-contained interactive system that has its own window(s) usually
has its own process to drive the windows. Peek, Zmacs, ZMail and the
inspector all do this when invoked through the
System key. Usually
each window you create has its own process; there is a Peek process
for each Peek window, so different Peek windows run completely
Whether a window is managed by a dedicated process or by various
processes is not a crucial decision. The program that reads commands
from the window and draws on the window can always be run in one
dedicated process, or in different processes at different times (though
if you run it in two processes at once, you had better be careful to
keep them from confusing each other). The mechanisms of selection and
exposure that control whether input and output are possible on a window
at a given time work automatically on any process(es) that try to do the
input or output. So when there is a dedicated process for a window,
often the only connection between them is that the dedicated process is
running a program that has a pointer to that window (typically the
terminal-io in the process is that window).
For example, the inspector you get with
System I has a dedicated
process, whereas the one you get by calling
inspect runs in the
inspect is called in. Yet these two windows have the
same flavor, and the same function,
tv:inspect-command-loop, does the
main work. The only differences are in deciding when to deexpose the
window, what to do when that happens, when it can be reused, what to
do if the user types
End, and other things related directly to the
difference in the two user interfaces for entering and exiting.
The inspector makes an instructive example for comparing these two ways
of managing a window. The function
inspect allocates a window out
of a resource of reusable windows of the right flavor (see
defresource, (defresource-fun)). It sends the window some messages
to initialize it for this particular session of use; this is how it
tells the window about the object that is the argument to
Then it selects the window manually using
(tv:window-call-fun)) and calls the inspector program. When the user
End, the program returns,
tv:window-call reselects the old
window and deactivates the inspect window, and
inspect uses an
unwind-protect so that aborting outside of
inspect for any reason brings back the old window.
System I finds or creates an an inspect window of the same
flavor. When no init options are specified, this flavor’s default init
plist specifies the creation of a process, which is initialized to call
the inspector program. If the user types
End and the inspector
command loop returns, the top-level function in the dedicated process
buries the inspector window and loops back to the beginning. That’s all
that is necessary to make
System I work. The resource that
inspect uses explicitly specifies an init option when it constructs
a window, so that no process is made.
Provides an instance variable
tv:process which can remembers a process
associated with the window. A window that will sometimes be used with
a dedicated process should have this mixin.
The most valuable service that this flavor provides is an easy way to create and initialize a process for each window that is created, and inform the process which window it was created for. Once this is done, for the most part the desired results follow without special effort.
Selecting the window or making it visible will give the process a
run reason. The window itself is used as the run reason.
Also, this will reset the process if it is flushed (waiting with
false as its wait function).
:kill operation on the window will invoke the
operation on the process.
tv:process-mixin guarantees that the
will return the explicitly specified process, regardless of which
process has most recently read from the window.
.definstvar tv:process-mixin tv:process
The process associated with the window, or
.definitoption tv:process-mixin :process process-or-description Specifies the process for this window. The argument can be a process, or it can be a list, which is used as a description for creating a process. The list looks like
When the process starts up, it will call initial-function with the
window as its sole argument. Usually the initial function should bind
terminal-io to the argument.
If this option is omitted or
nil, the window starts out without a process.
.defmethod tv:process-mixin :process
.defmethod1 tv:process-mixin :set-process process
Gets or sets the process associated with this window.
nil is a legal
value, which means that the window has no process associated with it,
even though it has the ability to have one.
.defmetamethod windows :processes
Returns a list of processes dedicated to this window.
:append method combination is used, so that all the processes
mentioned by any of the methods are put into the final answer.
These are the processes that the
:kill operation will kill.
The default is to return
tv:process-mixin contributes a suitable method.
These process-related operations are defined on
that they are always supported by the selected window.
Since windows lacking
tv:process-mixin do not explicitly remember a
process, a heuristic is used to come up with a process to operate on:
it is the last process to have read input from this window’s input
buffer. (Think about the fact that the input buffer may be shared with
tv:process-mixin is always put before
tv:select-mixin in the
components of a window flavor, so this method will be overridden.
.defmethod tv:select-mixin :process Gets a process somehow associated with this window, heuristically if necessary. .end_defmethod
.defmethod tv:select-mixin :set-process process Records process in the place where the last process to read input from this window would normally be recorded. .end_defmethod
.defmethod tv:select-mixin :arrest
.defmethod1 tv:select-mixin :un-arrest
Arrests or unarrests the process returned by the
The arrest reason used or revoked is not specified (it defaults).
.defmethod tv:select-mixin :call
Selects an idle Lisp listener window (possibly this window, if it is an
idle Lisp listener). If the window selected is not this one, arrest
this window’s process with arrest reason
:call. This arrest reason
is removed automatically by selecting this window again.
Causes any process that tries to draw on this window when it has an
output hold to be reset when it does so (see the
:reset operation on
A temporary window that truncates lines and also resets processes that
try to output on it when it has output hold. This flavor is what
Terminal F uses.
This chapter is about examining and setting the sizes and positions of windows. There are many different operations that let you express things in different forms that are convenient in varying applications. Usually, sizes are in units of pixels. However, sometimes we refer to widths in units of characters and heights in units of lines. The number of horizontal pixels in one character is called the character-width, and the number of vertical pixels in one line is called the line-height; these two quantities are explained on (char-width-and-line-height).
A window has two parts: the inside and the margins. The margins include borders, labels, and other things; the inside is used for drawing characters and graphics. Some of the operations below deal with the outside size (including the margins) and some deal with the inside size.
Since a window’s size and position are usually established when the window is created, we will begin by discussing the init-options that let you specify the size and position of a new window. To make things as convenient as possible, there are many ways to express what you want. The idea is that you specify various things, and the window figures out whatever you leave unspecified. For example, you can specify the right-hand edge and the width, and the position of the left-hand edge will automatically be figured out. If you underspecify some parameters, defaults are used. Each edge defaults to being the same as the corresponding inside edge of the superior window; so, for example, if you specify the position of the left edge, but don’t specify the width or the position of the right edge, then the right edge will line up with the inside right edge of the superior. If you specify the width but neither edge position, the left edge will line up with the inside left edge of the superior; the same goes for the height and the top edge.
In order for a window to be exposed, its position and size must be such that it fits within the inside of the superior window. If a window is not exposed, then there are no constraints on its position and size; it may overlap its superior’s margins, or even be outside the superior window altogether.
All positions are specified in pixels and are relative to the outside of the superior window.
.defmetainitoption windows :left left-edge .defmetainitoption1 windows :x left-edge .defmetainitoption1 windows :top top-edge .defmetainitoption1 windows :y top-edge .defmetainitoption1 windows :position (left-edge top-edge) .defmetainitoption1 windows :right right-edge .defmetainitoption1 windows :bottom bottom-edge .defmetainitoption1 windows :width outside-width .defmetainitoption1 windows :height outside-height .defmetainitoption1 windows :size (outside-width outside-height) .defmetainitoption1 windows :inside-width inside-width .defmetainitoption1 windows :inside-height inside-height .defmetainitoption1 windows :inside-size (inside-width inside-height) .defmetainitoption1 windows :edges (left-edge top-edge right-edge bottom-edge) These options set various position and size parameters. The size and position of the window are computed from the parameters provided by these and other options, and the set of defaults described above. Note that all edge parameters are relative to the outside of the superior window. .end_defmetainitoption
.defmetainitoption windows :character-width spec This is another way of specifying the width. spec is either a number of characters or a character string. The inside width of the window is made to be wide enough to display those characters, or that many characters, in font zero. .end_defmetainitoption
.defmetainitoption windows :character-height spec This is another way of specifying the height. spec is either a number of lines or a character string containing a certain number of lines separated by carriage returns. The inside height of the window is made to be that many lines. .end_defmetainitoption
.defmetainitoption windows :integral-p t-or-nil
The default is
nil. If this is specified as
t, the inside
dimensions of the window are made to be an integral number of characters
wide and lines high, by making the bottom margin larger if necessary.
.defmetainitoption windows :edges-from source Specifies that the window is to take its edges (position and size) from source, which can be one of:
(left-edge top-edge right-edge bottom-edge)Those edges, relative to the superior, are used, exactly as if you had used the
:edgesinit-option (see above).
:mouseThe user is asked to point the mouse to where the top-left and bottom-right corners of the window should go. (This is what happens when you use the
Createcommand in the system menu, for example.)
.defmetainitoption windows :minimum-width n-pixels
.defmetainitoption1 windows :minimum-height n-pixels
In combination with the
:edges-from :mouse init option, these options
specify the minimum size of the rectangle accepted from the user. If the user
tries to specify a size smaller than one or both of these minima, he will
be beeped at and prompted to start over again with a new top-left corner.
The group of operations below is used to examine or change the size or
position of a window. Many operations that change the window’s size or
position take an argument called option. The reason that this
argument exists is that certain new sizes or positions are not valid.
One reason that a size may not be valid is that it may be so small that
there is no room for the margins; for example, if the new width is
smaller than the sum of the sizes of the left and right margins, then
the new width is not valid. A new setting of the edges is also
invalid if the window is exposed and the new edges are not enclosed
inside its superior. In all of the operations that take the option
argument, option may be either
nil means that you really want to set the edges, and if the
new edges are not valid, an error should be signalled.
:verify means that you only want to check whether the new
edges are valid or not, and you don’t really want to change the edges.
If the edges are valid, the operation with
t; otherwise it
returns two values:
nil and a string explaining what is wrong
with the edges. (Note that it is valid to set the edges of a deexposed
inferior window in such a way that the inferior is not enclosed inside
the superior; you just can’t expose it until the situation is remedied.
This makes it more convenient to change the edges of a window and all of
its inferiors sequentially; you don’t have to be careful about what
order you do it in.)
.defmetamethod windows :size Returns two values, the outside width and outside height. .end_defmetamethod
.defmetamethod windows :height .defmetamethod1 windows :width Return the window’s height or its width. .end_defmetamethod
.defmetamethod windows :set-size new-width new-height &optional option Sets the outside width and outside height of the window to new-height and new-width, without changing the position of the upper-left corner. .end_defmetamethod
.defmetamethod windows :inside-size Returns two values, the inside width and the inside height. .end_defmetamethod
.defmetamethod windows :inside-height .defmetamethod1 windows :inside-width Return the inside height of the window or the inside width. .end_defmetamethod
.defmetamethod windows :set-inside-size new-inside-width new-inside-height &optional option Set the inside width and inside height of the window to new-inside-height and new-inside-width, without changing the position of the upper-left corner. The margin sizes are recomputed according to their contents, which in simple cases means they will stay the same. .end_defmetamethod
.defmetamethod windows :position Returns two values, the x and y positions of the upper-left corner of the window, in pixels, relative to the superior window. .end_defmetamethod
.defmetamethod windows :set-position new-x new-y &optional option Sets the x and y position of the upper-left corner of the window, in pixels, relative to the superior window. .end_defmetamethod
.defmetamethod windows :edges Returns four values, the left, top, right, and bottom edges, in pixels, relative to the superior window. .end_defmetamethod
.defmetamethod windows :set-edges new-left new-top new-right new-bottom &optional option Sets the edges of the window to new-left, new-top, new-right, and new-bottom, in pixels, relative to the superior window. .end_defmetamethod
.defmetamethod windows :inside-edges
Returns four values, the left, top, right, and bottom inside edges,
in pixels, relative to the top-left corner of this window. This can
be useful for clipping. Note that this operation is not analogous
:edges operation, which returns the outside edges relative to
the superior window.
.defmetamethod windows :center-around x y Without changing the size of the window, positions the window so that its center is as close to the point (x,y) as is possible without hanging off an edge. The coordinates are in pixels relative to the superior window. .end_defmetamethod
.defmetamethod windows :expose-near mode &optional (warp-mouse-p
If the window is not exposed, changes its position according to mode
and exposes it (with the
:expose operation; see
(windows/ and/ screens-expose-method)). If it is already exposed, does
mode should be a list; it may be any of the following:
:pointmode above, but the x and y come from the current mouse position instead of the caller. This is like what pop-up windows do. In addition, if warp-mouse-p is non-
nil, the mouse is warped (see (warp)) to the center of the window. (The mouse moves only if the window is near an edge of its superior; otherwise the mouse is already at the center of the window.)
nil, the mouse is warped (see (warp)) to the center of the window.
nil, the mouse is warped (see (warp)) to the center of the window.
.defmetamethod windows :change-of-size-or-margins &rest options This is the primitive operation for changing a window’s size or the size of its margins. All the other operations to do so end up calling this one, after all error checking has been done.
This operation should not be called by users; to change the size, use
:set-size or another higher-level operation, and the margin sizes
should be managed by the flavors that are responsible for computing how
big they should be (
However, this operation is a good place to add
:after daemons to
recompute other data structure or change the size of inferiors according
to the window’s new size. In the
:after daemon, the window’s size
and margins will already be altered to their new values.
.defmetainstvar windows tv:x-offset .defmetainstvar1 windows tv:y-offset The position of the window’s outside left (or top) edge relative to the window’s superior. .end_defmetainstvar
.defmetainstvar windows tv:width .defmetainstvar1 windows tv:height The total width or height of the window. .end_defmetainstvar
Recall that a sheet is either a window or a screen.
Return the value of the corresponding instance variable of window.
These are accessor defsubsts created by the
:outside-accessible-instance-variables option of
They can therefore be
setf’d, but doing so is usually unwise.
Return the inside width or height of the window.
When used without an argument, these defsubsts refer
directly to the instance variables, and therefore must
be called from methods or functions which use
(declare (:self-flavor ...)).
Returns the number of lines (of height equal to
that fit in the inside height of the window.
When used without an argument, these defsubsts refer
directly to the instance variables, and therefore must
be called from methods or functions which use
(declare (:self-flavor ...)).
Returns the x and y positions of window’s upper left corner in superior as two values. window must be an indirect inferior of superior, zero or more levels down. If window and superior are the same window, the values are zero.
t if sheet overlaps the specified rectangle.
The edges specified are relative to sheet’s superior.
t if the two sheets overlap.
This is a geometrical test, and it does not matter where in the
hierarchy the two sheets are.
t if sheet is contained within the specified rectangle,
given relative to sheet’s superior.
t if sheet is within outer-sheet’s area.
This is a geometrical test, and it does not matter where in the
hierarchy the two sheets are.
t if the specified rectangle is within outer-sheet.
The edges are specified relative to outer-sheet’s superior.
t if sheet contains the point (x,y) in top-sheet.
Windows can be given the ability to function as input streams (see
(stream-protocol)). This is implemented by the mixin
tv:stream-mixin, which is a component of
both input and output stream operations were defined on this mixin, but
now the output operations are available on all windows since a window is
fairly useless if you don’t draw on it.) Input characters normally come
from the terminal keyboard, but can also come from mouse clicks, or
anything else you may decide to program to generate input.
.defflavor tv:stream-mixin This mixin defines the standard input stream operations for doing input from the keyboard, as well as some nonstandard input operations defined in the following sections. .end_defflavor
Keyboard input is done through windows so that selection of windows can control which process can read input at a given time. In fact, this is why the concept of selection exists: by making each process that does its output to a window also use that window to read input, and by making a single "selected" window the only window on which input operations can proceed, we enable the user to decide which process to direct his input to by selecting the corresponding window.
Reading characters from a window normally returns a fixnum that
represents a character in the Lisp Machine character set, possibly
with extra bits that correspond to the
Hyper keys. Character constants in code are
written with the #\ or #/ construct and are described in the
Lisp Machine manual in (character-set).
Programs decode keyboard characters with
the following byte fields:
Controlbits, in that order from most significant to least.
1if this is a "mouse" character, a character that reports a click of a mouse button rather than a pressing of a keyboard key. (See (tv:kbd-mouse-buttons-mixin-flavor).) Note that mouse characters may contain
Though keyboard input characters are currently fixnums, it is possible
that a new, special data type for characters will exist in the future.
The #\ construct will produce a character object rather than a
fixnum, and the elements of a string will be character objects rather
han fixnums. Characters will behave just like fixnums in arithmetic and
= but will not be
eq to fixnums. The
:tyi and related
stream operations will continue to return fixnums; new operations will
be defined which return character objects instead. It will still be
possible to use
dpb with these byte field names on
fixnums and character objects indiscriminately.
Note that reading characters from a window does not echo the characters;
it does not type them out. If you want echoing, you can echo the
characters yourself, or call the higher-level functions such as
readline; these functions accept a window as their
stream argument and will echo the characters they read. This is in accord
with the general Lisp Machine input stream conventions.
The console hardware actually sends codes to the Lisp Machine whenever a
key is depressed or lifted; thus, the Lisp Machine knows at all times
which keys are depressed and which are not. You can use the
tv:key-state function to ask whether a key is down or up. Also, you
can arrange for reading from a window to read the raw hardware codes
exactly as they are sent, by putting a non-
nil value of the
property on the property list of the input buffer; however, the format
of the raw codes is complicated and dependent on the hardware
implementation. It is not documented here.
The value returned by the function
time when the last input
character was typed.
Every window that generates input or from which input is read must have
an input buffer that holds characters that are typed by the user
before any program reads the characters. When you type a character, it
enters the selected window’s buffer. (This is not precisely true, but it’s a
good first mental model. See (io-buffer).) Reading input from a
window, with the
:tyi operation for example, takes objects out of
the window’s input buffer.
the window an input buffer, but some other flavors (such as command
menus) provide an input buffer without
tv:stream-mixin. The input
buffer lives in an instance variable of the window, called
Input buffers are examples of I/O buffers, which are a general
facility provided by the window system. You can explicitly manipulate
input buffers in order to get certain advanced functionality, by using
:io-buffer init-option and the
:set-io-buffer operations. Another thing you
can do is put properties on the I/O buffer’s property list; this lets
you request various special features. I/O buffers are explained on
A window can be thought of as generating input when the keyboard is used
while the window is selected. This is the way that ordinary characters
normally get into the input buffer. But input can be generated at any
place in the program by means of the
For example, mouse clicks are often handled by forcing input which is
read by the window’s command-interpreting process (see
(tv:list-mouse-buttons-mixin-flavor)). Then we say that the mouse
click also generates input.
All the input, no matter how generated, ends up mixed together in the same input buffer, in chronological order. All the input operations take input from the buffer in that order.
Normally each window that can generate input has its own input buffer. If a process is managing more than one window that can generate input, a program to look for input from all the windows at once would be cumbersome. So it is not done this way. Instead, all the windows are made to share a single input buffer. Then all input generated by all of the windows goes into that buffer, from which the input can be read through any one of the windows. The program simply reads input from one of the windows–always the same one, if the programmer prefers–and gets all the input intended for it. All the keyboard input directed at it, and all mouse clicks on its windows, get merged into a single chronological input stream.
The input buffer does not record which window was "responsible" for generating input read from a shared input buffer. For mouse clicks the program may need to know which window the mouse was clicked on in order to obey the command properly. The standard way to pass this information is to use a list as the input character and make the window clicked on one of the elements of the list.
The window(s) used for input operations must have
tv:stream-mixin. The other windows need only be able to put
input into the right input buffer. It is often easiest to use
tv:stream-mixin for them as well, and generate the input with
:force-kbd-input. However, it is sufficient for such windows
to support the
:io-buffer operation by returning the
correct shared input buffer, and put the input they generate into
that buffer in any way that works, such as with the function
or by invoking
:force-kbd-input on another window known to
tv:stream-mixin and to share the same input buffer.
If a frame includes a pane that is handled by its own process (such as a Zmacs frame), that pane should not share the input buffer used by the rest of the panes. In general, there should be one input buffer for each process you are using, and that input buffer should be shared by the windows which go with that process.
In general, the way to make windows share an input buffer is to create
tv:make-default-io-buffer and then specify it for the
:io-buffer init keyword when each pane is created. There are also
frame flavors that automatically make the panes share an input buffer.
.definstvar tv:stream-mixin tv:io-buffer The window’s input buffer. .end_definstvar
.definitoption tv:stream-mixin :io-buffer spec Initializes the input buffer of the window. spec may be an I/O buffer, a number or a list. If it is a number, an I/O buffer is made with that size, no input function, and the default output function. If it is a list, it is interpreted as
(size input-function output-function)
but if the output-function is
nil or omitted,
tv:kbd-default-output-function is used.
.defmethod tv:stream-mixin :io-buffer .defmethod1 tv:stream-mixin :set-io-buffer io-buffer Return or set the window’s input buffer. .end_defmethod
Input need not be made of characters; lists are often used as well for program-generated input, especially for representing mouse clicks in different kinds of mouse-sensitive areas. "Characters" which are lists are called blips. The car of the list is by convention a symbol which identifies the kind of blip. Look for "blip types" in the concept index to find the places in this manual that define various kinds of blips.
Caution: when using blips, you should keep in mind that the blips
may be discarded if the process has called any function that does not
know what to do with them. The debugger and
break are such
functions, so this can happen at any time. Blips either should describe
mouse actions, which can safely be ignored if they happen when they are
not meaningful, or should notify the process to check other data
structures. A blip should not be used to indicate a request or response
from another process, since this information must not be lost. Instead,
put the data on a separate queue and have the process check the queue
after every command. A blip that executes as a no-op command will
serve to wake the process up if it is waiting for input when the data
goes on the queue.
There is a technique you can use to cause blips to be handled even in
the middle of calls to
read, the debugger, and other programs that
do not look for blips. It is to give your window flavor an
:around method can look at the
value being returned; if it is one of certain types of blips, you can
handle it and then loop around, calling the original
again without returning to the caller. If it is anything else,
you just return it.
.defmethod tv:stream-mixin :any-tyi &optional eof-action Reads and returns the next character of input from the window, waiting if there is none. The character comes from the window’s input buffer if it contains any characters; otherwise, it comes from the keyboard. eof-action is ignored since "end-of-file" is not meaningful for windows; this argument exists only because it is part of the input stream protocol. .end_defmethod
.defmethod tv:stream-mixin :tyi &optional eof-action
:any-tyi but throws away any blips ("characters" which are
lists) that it receives. It keeps on reading until it finds an actual
character, and returns that. Discarded blips will never be seen as input.
.defmethod tv:stream-mixin :any-tyi-no-hang &optional eof-action
:any-tyi if input is already present in the buffer, but returns
nil right away if the buffer is empty. This is used by programs
that continuously do something until a key is typed, then look at the
key and decide what to do next.
.defmethod tv:stream-mixin :tyi-no-hang &optional eof-action
:any-tyi but throws away any blips ("characters" which are
lists) that it receives. It keeps on reading until it finds an actual
character or the buffer empty; then it returns the character or
Discarded blips will never be seen as input.
.defmethod tv:stream-mixin :mouse-or-kbd-tyi
.defmethod1 tv:stream-mixin :mouse-or-kbd-tyi-no-hang
These are like the
:tyi-no-hang operations, except that
blips of a certain kind are not discarded and do count as input. These
are blips whose car is the symbol
:mouse-button. In this case, the first
value returned is the third element (caddr) of the blip, and the second
value returned is the whole blip. By convention, the third element of
such a blip is a character whose
%%kbd-mouse bit is
identifies the button that the user clicked (see (mouse-blip)). All
other blips are discarded, as they are by
The first value is always a fixnum.
.defmethod tv:stream-mixin :list-tyi
This is the "opposite" of
:tyi. It returns only blips and discards
.defmethod tv:stream-mixin :untyi character
Put character back into the window’s input buffer so that it will be
the next character returned by
:tyi. Note that character must be
exactly the last character that was read, and that it is illegal to do
:untyi’s in a row. This is used by parsers that look ahead one
character, such as
.defmethod tv:stream-mixin :force-kbd-input input
input is inserted into the window’s input buffer, to be read by the
:tyi or other input operation in its turn. input may be a
character or a list (a blip). It may also be a string; then all the
characters of the string are forced as input, one by one.
This is the standard way that blips are put into the input stream (see (blips)). .end_defmethod
.defmethod tv:stream-mixin :listen
t if there are any characters available to
there are not. For example, the editor uses this to defer redisplay until it
has caught up with all of the characters that have been typed in.
.defmethod tv:stream-mixin :wait-for-input-with-timeout timeout Waits until either input is available or timeout 60ths of a second have elapsed. .end_defmethod
.defmethod tv:stream-mixin :clear-input Clears this window’s input buffer. This flushes all the characters that have been typed at this window, but have not yet been read. .end_defmethod
.defmethod tv:stream-mixin :playback
Returns an array describing the last n characters read from this
window, for some value of n (which is the size of the array). The
array elements are used in a circular fashion, the last one being
followed by the first one, and array leader element 1 contains the index
of the last slot stored into (the one containing the last character
read). The editor command
Help L uses this operation.
.defmethod tv:stream-mixin :rubout-handler options function &rest args Applies function to args inside an environment where inputting from this window will echo the characters typed and provide for simple input editing. This is documented in more detail in the Lisp Machine manual.
options is an assq list of keyword symbols and arguments to them. The options acceptable to windows are:
:full-ruboutoption is supplied, the rubout handler returns to the caller in this situation. Two values are returned,
nil, but if it is necessary to prompt again, for instance if the user cleared the screen, function is called with the character the user typed (e.g.
#\clear-screen) as its flag argument.
function can also be a string; then it is simply printed as the prompt.
:promptexcept that the function is not called the first time through. If both
:repromptare used, the
:promptis used the first time and the
:repromptis used on reprinting.
.defmethod tv:stream-mixin :save-rubout-handler-buffer
Returns a description of the rubout handler buffer’s contents,
and clears it out. Two values are returned: a string and a fixnum
(which is the current cursor index in the string).
This is used on entry to the function
break so that typing
Break key interfaces properly with rubout handling.
.defmethod tv:stream-mixin :restore-rubout-handler-buffer string index
Loads the rubout handler buffer contents from string and sets the
cursor position to index. The arguments are usually two values
.defmethod tv:stream-mixin :refresh-rubout-handler &optional discard-last-character
Requests the rubout handler to reprint its buffer and reprompt. If
discard-last-character is non-
nil, the last character in the
buffer is discarded first. This is used by
If you are reading input using the rubout handler, but want to process
certain characters immediately (perhaps the character
Help) and not
leave them as part of the ordinary input, use this operation with argument
This flavor defines the
.defmethod tv:preemptable-read-any-tyi-mixin :preemptable-read options function &rest arguments You may have noticed that in the inspector and in the Window Error Handler, if you start typing in a Lisp expression, and then while in the middle of typing it you use the mouse to select an object by pointing at it, the program sees the object you moused. If nothing special were done, though, the blip sent by the mouse process would get put at the end of the input buffer and would not be seen because of the characters that you have typed. This mixin is what is used to solve the problem.
:preemptable-read operation takes the same arguments as the normal
:rubout-handler operation, and does the same thing if the mouse is not
used. (In fact, it has nothing to do with the
read function, despite the name.)
The difference is that if any blip is sent to the window, the operation
returns the blip as the first value and the symbol
the second value. (It does this even if the blip did not come from the
mouse; most blips do.) The characters that were in the rubout-handler
buffer when the blip arrived will come back the next time a
operation is used, so the user can keep typing his expression in.
These obsolete functions are still used in some old code:
An I/O buffer is an array of fixed size used as a ring buffer.
Typically, characters are put into the buffer by one process and removed
by another in FIFO order. The process that is removing characters can
wait if the buffer is empty, and a process putting in characters can
wait if the buffer is full (or it could throw away the characters).
Each window with
tv:stream-mixin has an input buffer which is
an I/O buffer, and there is also one global I/O buffer for the keyboard
Note that the things stored in an I/O buffer can be any Lisp objects. They do not have to be characters, in any sense. But in practice I/O buffers are in fact used for storing characters (which may be lists), so that is how this section is written.
An I/O buffer has these slots in its leader.
If the input and output pointers are equal, the buffer is empty. If the output pointer points at the slot after the input pointer, the buffer is considered full (in fact, one slot is still empty. It cannot be used).
nil. It is called with the buffer and the character as arguments. Its value should be a translated version of the character (this is usually the same as the argument). It can also return a non-
nilsecond character, which says that the character should be discarded. In this case,
tv:io-buffer-getwill remove the following character, or wait for one.
In window input buffers this is usually a function that checks for and handles synchronous interception.
nil. The window system does not actually use this feature. The calling conventions are the same as for the output function.
:outputto control what can be done with the buffer. Characters can be put in if this is
:input, and can be removed if this is
tv:io-buffer-record-pointergets the index of the last slot stored into.
nil if the buffer is empty, or full.
Creates and returns an I/O buffer, initializing some of the slots from its arguments and the others in a default or reasonable fashion. The buffer is initially empty.
Inserts character into buffer, waiting if it is full unless
no-hang-p. This function also waits if the buffer’s state does not
permit input. It returns
t if the character was inserted.
Removes the next character from buffer.
If the buffer is empty, normally we wait for a character to appear, but
if no-hang-p is non-
nil we return
This function also waits if the buffer’s state
does not permit output. The character removed is put in buffer’s
Inserts character into buffer as the next character to be
removed rather than as the last one to be removed. This is used for
tv:io-buffer-get, and it is an error if character does
not match the last character removed. character is removed from the
io-buffer-record array, by backing up its pointer, just to avoid
duplication when character is read a second time.
This function should not be used more than once between input operations.
Inserts character into buffer as the next character to be
removed; that is, in a LIFO manner. This is as opposed to
tv:io-buffer-put which inserts a character at the end.
Makes buffer empty.
Uses function as a filter for the characters in buffer. function is called once for each character, with the character as its sole argument. If function returns non-nil, that value is stored back in the buffer instead of the original character. If function returns nil, the character is deleted from the input buffer.
We have said (see (input-buffer)) that keyboard input goes into
the selected window’s input buffer. This is not precisely true.
Program-generated input made with
:force-kbd-input does go
directly into the window’s input buffer, but keyboard input
actually goes into another I/O buffer called the keyboard input buffer.
(There is only one of these in the system.) The characters move
from the keyboard input buffer to the selected window’s input buffer
whenever a program tries to read input from that buffer and it is empty.
The keyboard input is not assigned to a selected window until the
instant the program is ready to read it.
Asynchronous window-switching commands, such as
Terminal S, and
mouse clicks that select a window, actually copy the contents of the
keyboard input buffer into the buffer of the window that is being
deselected. If you type some commands to the editor, and then type
System L before the editor has read its commands, those commands
will still go to the editor, not to the Lisp listener you have
By contrast, synchronous window-switching such as is done by the
inspect, and by "exit" commands
in various programs, do not do this, since any further typed-ahead input
should go to the program being switched to.
Creates and returns an I/O buffer of the sort used for window input
buffers, with all slots suitably initialized. The output function used is
This is the default value for a window input buffer’s output function.
It checks the character against the value of
tv:kbd-intercepted-characters and also checks
Removes a character from buffer, or possibly from the keyboard input buffer. The keyboard input buffer can be read from only if buffer is the input buffer of the selected window, and it is used only if buffer is empty. When a character is read from the keyboard input buffer, buffer’s output function is executed, as if the character had been put into buffer and then read from there.
whostate is passed as the first argument to
process-wait if this
function has to wait.
Waits until either
tv:kbd-io-buffer-get would not hang on buffer
or timeout elapses. timeout is in 60ths of a second.
whostate appears in the who line while we wait.
Waits until either
tv:kbd-io-buffer-get would not hang on buffer
or window is not exposed.
whostate appears in the who line while we wait.
Transfer any characters that
tv:kbd-io-buffer-get could now get from
buffer right into buffer. This is what asynchronous selection
commands use to make sure that type-ahead for the window being
deselected remains with that window.
nil if input is available in the selected window.
This can be used in programs that loop with interrupts disabled,
to tell when the user types a key.
The window system defines the meaning of certain properties
tv:io-buffer-plist of a window input buffer.
nilto inhibit translation of characters from hardware codes to the Lisp Machine character set. The effect of this is hardware dependent.
Control(etc.) keys from causing special treatment of alphabetic case. Normally, typing
Control-Shift-Aproduces the character
#\Control-/awith a lower case "a", while
#\Control-A; and the same for
Hyper. If this property is non-
nil, the two inputs are interchanged in meaning, so that
Shiftproduces an upper case character with or without
There are several characters that are specially intercepted by the
window system. Some are intercepted when a process tries to read them,
and some are intercepted as soon as they are typed. The first kind
are called synchronously intercepted characters and the second
are called asynchronously intercepted characters. The latter
come in two kinds: global asynchronous characters such as
System which are always available
and others defined by the selected window, normally including
Control-Abort and so on (see (asynchronous-intercepted-characters)).
Synchronous interception is performed by the
io-buffer-output-function of the window input buffer (see
(io-buffer-output-function)). By default, this function is
tv:kbd-default-output-function, which uses the variable
tv:kbd-intercepted-characters to decide which characters to
intercept and how to handle them. A program can change its set of
synchronously intercepted characters simply by binding this variable
before reading input. Its default value specifies the characters
The value is an alist specifying the characters to be intercepted
synchronously (that is, when read by the program). Since the variable
is looked at by a subroutine of the
:tyi operation itself, what
matters is current binding at the time the
:tyi is done.
Each element of this list should look like
Then function will be called if character is read, with character as argument.
function should return two values. The second should be non-
to say that the character has been handled by the function and should
not be returned to the calling program as ordinary input.
If the second value is
nil, the first value should be a translated
character to use as input instead of the character typed. (This can be
and usually is the same character that was typed.) The first value is
ignored if the second is non-
nil. In practice, function usually
returns its argument and
function should begin by setting
It is reasonable to add new entries to the top level value of this variable, and also for programs to bind the variable. It is probably unwise to remove the standard entries in the top level value.
This is the that which is the initial value of
These functions implement the standard meanings of the
Meta-Abort keys. They are suitable for use in
tv:kbd-intercepted-characters. The first signals the
condition; the second resets the current process.
terminal-io handles the
and it returns non-
nil, the string
"[Abort]" will not be
These functions implement the standard meanings of the
Meta-Break keys. They are suitable for use in
tv:kbd-intercepted-characters. The first calls
second invokes the debugger.
Furthermore, if the variable
nil, then it is considered to be a user function that can
intercept the character at this point.
By convention, programs are all expected to use the
Abort key as
a command to abort things in some appropriate sense for that program.
If you don’t do anything special,
Abort will be intercepted
automatically. But some programs may want to do something specific
when the user types
Abort. The system default action can be replaced
by binding the variable
tv:kbd-intercepted-characters so that
to your own intercept routine instead of
or so that
Abort is read as an input character from the stream like any other and
then is handled by your program.
before it does anything else, sees whether the
tv:kbd-tyi-hook is non-
nil; if so, it assumes that the
value is a function of one argument, and it applies the function to the
character that was typed. If the function returns a non-
then the character will not be returned to callers of
:tyi or other
input operations; otherwise, the character is processed normally.
The idea is that you can write a function that intercepts anything
passing through an input buffer that uses the default
io-buffer-output-function. Your function gets passed the character,
nil if it doesn’t want to handle it, or
t if it has
taken care of the character.
Each window that has
tv:stream-mixin can define a set of
characters to be intercepted asynchronously when that window is
selected. The interception is done through a different mechanism from
that used for synchronous interception, but the same handling
functions such as
tv:kbd-intercept-abort can ultimately be used.
By default, a window requests asynchronous interception of the four
Control-Meta-Break. The default meanings
of these keys are given in (call-and-abort). You can change the set
of such asynchronous keys on a per-window basis.
Since the interception is done by the keyboard process, the characters
cannot straightforwardly be specified by a variable for the program to
bind. So each window has a list of them (which is actually stored as
:asynchronous-characters property on the input buffer’s property
.definitoption tv:stream-mixin :asynchronous-characters alist alist specifies the characters to be intercepted asynchronously while this window is selected, and what they should do.
Each element consists of a character, a function to call, and optionally some extra arguments to be passed to it. When the function is called, its arguments will be the character, the selected window, and any specified additional arguments from the alist element.
If the init option is not specified, the default comes from the value of
tv:kbd-standard-asynchronous-characters, the initial value of which
((#\c-abort tv:kbd-asynchronous-intercept-character (:name "Abort" :priority 50.) tv:kbd-intercept-abort) (#\c-m-abort tv:kbd-asynchronous-intercept-character (:name "Abort All" :priority 50.) tv:kbd-intercept-abort-all) (#\c-break tv:kbd-asynchronous-intercept-character (:name "Break" :priority 40.) tv:kbd-intercept-break) (#\c-m-break tv:kbd-asynchronous-intercept-character (:name "Error Break" :priority 40.) tv:kbd-intercept-error-break))
How these work is explained below. .end_definitoption
.defmethod tv:stream-mixin :asynchronous-character-p character
nil if this window defines character for
.defmethod tv:stream-mixin :handle-asynchronous-character character
Invokes the handler a defined for asynchronous interception of
character. This runs the handler function in your current process.
But since handler functions typically do
usually doesn’t matter.
.defmethod tv:stream-mixin :add-asynchronous-character character handler-function &rest additional-args Define character for asynchronous interception in this window, to be handled by handler-function and the additional-args. This adds an element
(character handler-function . additional-args)
to the alist on the input buffer’s property list. .end_defmethod
.defmethod tv:stream-mixin :remove-asynchronous-character character Removes character’s element from the alist, so that it is no longer intercepted asynchronously in this process. .end_defmethod
Asynchronous interception is done by the
Keyboard process, and the
handler function runs in that process. Therefore, it must obey some
strict conventions. It must not do any I/O, or wait for anything; it
should not run for very long; it should not get an error. It is usually
easiest to create another process and do the real work there, using
This function is provided as a convenient way to set up the handling of
an asynchronously intercepted character. It enables you to interface to
the same functions used for synchronous interception. It is used with
at least two additional
arguments: the process name and options for
the function to call in the new process. Thus,
(#\c-break tv:kbd-asynchronous-intercept-character (:name "Break" :priority 40.) tv:kbd-intercept-break)
arranges to create a process named
"Break" with priority 40, and
tv:kbd-intercept-break in that process.
subhandler, which is
tv:kbd-intercept-break in this example,
is passed as arguments character, window, and the
additional-subhandler-args if any.
System keys are also intercepted
asynchronously, but since their functions do not usually relate to the
selected window, they are not controlled by the selected window’s alist
of asynchronous characters. These are called global asynchronous characters.
This is an alist whose value controls
the characters intercepted regardless of the selected window.
Its elements look and work just like those of the alist specified
:asynchronous-characters init option for a window.
The initial value is
((#\terminal tv:kbd-esc) (#\system tv:kbd-sys))
System are defined to call functions that read
another character and dispatch on it. The meaning of the second
character is controlled by an alist so you can define new
The value of this variable is an alist, each entry of which describes a
subcommand of the
Terminal key. (
Escape is the old name for the
Terminal key.) Rather than modifying the list yourself, use
tv:remove-escape-key (below). Entries on
the list are of the form:
(char function documentation option1 option2 ...)
char is the character that should be typed after
Terminal to get
the new command. The character gets upper-cased before it is searched
for in this list, so don’t use lower case characters. function may
either be a list to be evaluated, or a symbol, which is the name of a
function to be applied to one argument. This is either the numeric
argument specified by the user (as in
Terminal 0 S), or
the user gave no argument.
documentation should be a string giving documentation, or a form
that gets evaluated and returns either a string or
nil. The string
will be printed by
Terminal Help, except that
nil means to omit
this character from the
Terminal Help display.
Normally function is evaluated or applied in a new process created
for the purpose, but if you give the
:keyboard-process option it
will run in the keyboard process. This option exists because certain of
the built-in commands must work this way. If you add your own, you
should not use this option, since you do not want to interfere with the
operation of the keyboard process. The cost of creating a new process is quite low.
:typeahead option is specified, then everything typed before
Terminal key will be shoved into the selected I/O buffer, i.e.
it will be treated as typeahead to the currently selected window. Use this
option with commands that change the selected window, to ensure that the user’s
typed input goes where he expected it to when he typed it.
Here is a sample element:
(#\clear-screen (tv:kbd-screen-redisplay) "Clear and redisplay all windows.")
Adds an element to
tv:*escape-keys*, and puts it in the right place
Removes any element for char from
The value of this variable is an alist, each entry of which describes a
subcommand of the
System key. Use the functions
tv:remove-system-key (below) to modify the list
rather than doing it yourself. Entries are of the form:
(char find documentation create)
char is the character that should be typed after
System to get the new command. The character gets upper-cased
before it is searched for in this list, so don’t use lower case
characters. documentation should be a string to be printed by
If find is an instance of a flavor, then it should be
a window, and the
System command will select that particular window.
However, normally find is the name of a flavor. If it is, the
command first searches the previously-selected-windows list for a window
of that flavor, and selects one if it finds one. Otherwise, if the
currently selected window is of that flavor, it beeps. Otherwise, it
looks at create to figure out what to do. find can also be a
list; then it is evaluated and the value should be a window or a flavor
name to be used as described above.
If create is
nil, it beeps. If create is
t, a new
window of flavor find is created by calling
no options, and is selected. If create is some other symbol, it is
the name of the flavor of window to be created. (This can be different
from the flavor to look for, which might be a mixin that is component of
several different flavors all of which are suitable to select when this
key is typed.) Otherwise, create is a form to be evaluated to
create a window. The
System command runs in a newly-created process
and so the form is evaluated in its own process, not the keyboard
If the character typed after the
System key is typed with the
Control shift, existing windows are ignored and a new window is
created according to create.
Here is a sample element:
(#/E zwei:zmacs-frame "Editor" t)
Adds an element to
tv:*system-keys*, and puts it in the right
Removes any element for char from
Returns a previously selected window of flavor flavor-name. Windows
are found in
((tv:previously-selected-windows-var)) and checked with
Selects a previously selected window of flavor flavor-name, or, if none exists, creates a new one and selects it.
Another way of using the keyboard, different from reading a stream of input characters from a window, is to treat it as a "random access" device and look at the instantaneous state of particular keys. Spacewar does this.
t if the keyboard key named key-name is currently
nil if it is not.
key-name may be the symbolic name of a shift key, from the table below, or the character code of a non-shift key, which is the character you get when you type that key without any shifts: a lower-case letter, a digit, or a special character. Shift keys that come in pairs have three symbolic names; one for the left-hand key, one for the right-hand key, and one for both, which is considered to be depressed if either member of the pair is. The shift key names are:
:shift :left-shift :right-shift :greek :left-greek :right-greek :top :left-top :right-top :control :left-control :right-control :meta :left-meta :right-meta :super :left-super :right-super :hyper :left-hyper :right-hyper :caps-lock :alt-lock :mode-lock :repeat
All windows can function as output streams, displaying the output as
if on the screen of an ordinary display terminal. The flavor
tv:minimum-window implements the operations of the Lisp Machine
output stream protocol (see (stream-protocol)), as well as many
additional output operations such as
:insert-line. Every window
has a current cursor position; its main use is to say where to put
characters that are drawn. The way a window handles the operations
asking it to type out is by drawing that character at the cursor
position, and moving the cursor position forward past the just-drawn
Cursor position arguments to stream operations are always expressed in "inside" coordinates (see (inside-size)); that is, coordinates relative to the top-left corner of the inside part of the window, so the margins don’t count in cursor positioning. The cursor position always stays in the inside portion of the window–never in the margins. The point (0,0) is at the top-left corner of the window; increasing x coordinates are further to the right and increasing y coordinates are further towards the bottom. (Note that y increases in the down direction, not the up direction!)
.defmetainstvar windows tv:cursor-x .defmetainstvar1 windows tv:cursor-y The window’s current cursor position. Note that these variables use "outside" coordinates, unlike the arguments to stream operations. .end_defmetainstvar
The x cursor position is the position of the left edge of the character box of the next character output. (The leftmost nonzero pixels of the character may be either left or right of the edge of the character box, according to the left-kern of the character; see (font-left-kern)).
The y cursor position is the position of the top of the vertical extent for the line being output. If only a single font is in use, the top of the character box is at this vertical position.
In fact, characters are positioned so that their baselines come out on the baseline of the line. This way, characters of different fonts juxtaposed in one line come out with baselines aligned rather than with their top edges aligned. The position of the character’s baseline is a property of its font. The window’s baseline is computed from the set of fonts in use, to provide enough space above the baseline for any of the fonts (see (windows-tv:baseline-instvar)).
When a character is drawn, it is combined with the existing contents
of the pixels of the window according to an alu function. The
different alu functions are described in (aluf). When characters are
drawn, the value of the window’s char-aluf is the alu function
used. Normally, the char-aluf says that the bits of the character
should be bit-wise logically ored with the existing contents of
the window (
tv:alu-ior). This means that if you type a character,
then set the cursor position back to where it was and type out a
second character, the two characters will both appear, ored
together one on top of the other. This is called overstriking.
Erasure is also done using an alu function which the window can
specify, called the erase-aluf. Normally this is an alu function
which ands the old pixel value with the complement of the area
.defmetainstvar windows tv:char-aluf .defmetainstvar1 windows tv:erase-aluf The window’s char-aluf and erase-aluf. .end_defmetainstvar
Reverse-video windows work by interchanging the normal values of the char-aluf and erase-aluf, so that erasing an area sets it to one while drawing a character clears the character’s pixels to zero.
Every window has a font map. A font map is an array of fonts in which characters on the window can be typed. At any time, one of these is the window’s current font; the operations that type out characters always type in the current font. Details of fonts and the font map appear below (see (font-section)). For now, we describe fonts only enough to explain the character-width and line-height of the window; these two units are used by many of the operations documented in this section. The character-width is the char-width attribute–the width of a "typical" character–of the first font in the font map. The line-height is the sum of the vsp of the window and the maximum of the char-heights of all the fonts. The vsp is an attribute of the window that controls how much vertical spacing there is between successive lines of text. That is, each line is as tall as the tallest font is, and you can add vertical spacing between lines by controlling the vsp of the window. Operations for controlling the vsp are documented on (vsp-operations). There is no instance variable holding the vsp, but the system can recompute it from the line-height and the font map.
.defmetainstvar windows tv:char-width
.defmetainstvar1 windows tv:line-height
The character-width and line-height of the window. The line height is
actually used for outputting a
#\return character. The character
width is not used at all for ordinary output, since each font determines
its own widths. Both are used for interpreting cursor positions expressed
in characters or lines.
Every window has a current font, which the operations use to figure out what font to type in. If you are not interested in fonts, you can ignore this and something reasonable will happen. In some fonts, all characters have the same width; these are called fixed-width fonts. The default font is an example. In other fonts, each character has its own width; these are called variable-width fonts. With variable-width fonts, it is not fully meaningful to express horizontal positions in numbers of characters, since different characters have different widths. Some of the functions below do use numbers of characters to designate widths; there are warnings along with each such use explaining that the results may not be meaningful if the current font has variable width.
Accessor defsubsts for the corresponding instance variables.
It may be reasonable to
setf the first four of them.
Typing out a character does more than just drawing the character on the screen. The cursor position is moved to the right place; non-printing characters are dealt with reasonably; if there is an attempt to move off the right or bottom edges of the screen, the typeout wraps around appropriately; more breaks are caused at the right time if more processing is enabled. Here is the complete explanation of what typing out a character does. You may want to remind yourself how the Lisp Machine character set works; see (character-set). You don’t have to worry much about the details here, but in case you ever need to know, here they are. If you aren’t interested, skip ahead to the definitions of the operations.
First, any output exceptions that are present are dealt with, and made to go away. See (output-exceptions), for an explanation of this.
When all exceptions have been dealt with, the character finally gets typed
out. If it is a printing character, it is typed in the current font at the
cursor position and the cursor position is moved to the right by the width
of the character. If it is one of the format effectors
#\backspace, it is handled in a special way to be
described in a moment. All other special characters have their names typed
out in tiny letters surrounded by a lozenge, and the cursor position is
moved right by the width of the lozenge. If an undefined character code is
typed out, it is treated like a special character; its code number is
displayed in a lozenge.
#\tab moves the cursor position to the right to the next tab stop,
moving at least one character-width. Tab stops are equally spaced across
the window. The distance between tab stops is tab-nchars times the
character-width of the window. tab-nchars defaults to 8 but
can be changed (see (windows-tab-nchars-init-option)).
#\return moves the cursor position to the inside left edge of
the window and down by one line-height, and clears the line (see
(windows-clear-eol-method)). It also deals with more processing and
the end-of-page condition as described above. However, if the window’s
cr-not-newline-flag is on, the
#\return character is not regarded
as a format effector and is displayed as "
return" in a lozenge, like
other special characters.
If the character being typed out is a
#\backspace, the result depends
on the value of the window’s backspace-not-overprinting-flag. If the
0, as is the default, the cursor position is moved left by one
character-width (or to the inside left edge, whichever is closer). If the
#\backspaces are treated like all other special
.defmetamethod windows :tyo ch &optional font Type ch on the window, as described above. Basically, type the character ch in font or the current font at the cursor position, and advance the cursor position. .end_defmetamethod
.defmetamethod windows :string-out string &optional (start
Type string on the window, starting at the character start and
ending with the character end. If end is
nil, continue to
the end of the string; if neither optional argument is given, the entire
string is typed. This behaves exactly as if each character in the string
(or the specified substring) were printed with the
operation, but it is much faster.
.defmetamethod windows :fat-string-out string &optional (start
Type the fat string string on the window. This is like
except that the
%%ch-font field of each character is used as the font
to draw that character in. The window’s current font is not used.
.defmetamethod windows :line-out string &optional (start
Do the same thing as
:string-out, and then advance to the next line
(like typing a
#\return character). The main reason that this operation
exists is so that the
(see (stream-copy-until-eof-fun)) can, under some conditions, move whole lines
from one stream to another; this is more efficient than moving
characters singly. The behavior of this operation is not affected
.defmetamethod windows :string-out-centered string left right y-pos Output string (or the portion from start to end), centered between x positions left and right, at y position y-pos (which defaults to the current cursor position). The cursor is left at the end of the string. If the string is multiple lines, the entire rectangular shape it occupies is centered as a unit. To center lines individually, output each line individually with this operation. .end_defmetamethod
.defmetamethod windows :fresh-line
Get the cursor position to the beginning of a blank line. Do this
in one of two ways. If the cursor is already at the beginning of a
line (that is, at the inside left edge of the window), clear the line
to make sure it is blank and leave the cursor where it was.
Otherwise, advance the cursor to the next line and clear the line
just as if a
#\return had been output. The behavior of this operation is not affected
.defmetamethod windows :beep &optional beep-type Attempt to attract the user’s attention, by either making a sound with the keyboard or flashing the screen into and out of inverse video or both.
beep’s value is
nil, both are done. If the value is
only the sound is made. If it is
:flash, only flashing the screen is done.
No standard meanings have been assigned to beep-type yet. .end_defmetamethod
Beeps by sending a
:beep message to stream, passing
beep-type as an argument. If the stream does not handle the
:beep operation, a sound is made on the keyboard instead.
.defmetamethod windows :display-lozenged-string string Output string in a lozenge. This is how special characters are echoed. .end_defmetamethod
This is a complicated primitive whose interface is arranged to do exactly what the editor needs for buffer display, to make the editor as fast as possible.
It outputs part of string on sheet like the
operation, but stops if it reaches the right margin (outputting a right margin
character if any output remains, if the window calls for that).
If set-xpos and set-ypos are non-
nil, the cursor is moved
there and a
:clear-eol is done, before output starts. If one of
these arguments is
nil, that dimension of cursor position is not
changed. If both are
nil, the cursor is not moved and nothing is cleared.
If dwidth is non-
nil, it should be a positive number. Output
actually starts at index
(1- start) in the string, and at x
position dwidth less that the cursor position (as found or as set by
set-xpos). However, if a
:clear-eol is done, it starts at
nil dwidth is to be used if the previous
character of the string is in an italic font, and is already present
on the screen before the output now being done. It causes that character
to be output again, presumably overprinting itself, in case a corner of
it was erased accidentally because it protrudes to the right of its
Returns two values, the final index in the string and the final x cursor position. The window’s cursor is not guaranteed to be moved there; it is undefined on exit from this function. But the value will be correct.
Before doing output to a window, various exceptional conditions are checked for. If an exceptional condition is discovered, a standard operation is invoked to handle it. Redefining or adding daemons to these operations can change the handling of exceptions. For example, output with the cursor too close to the right margin causes an end of line exception; the handling of this exception is what moves the cursor to the next line, or truncates the line, or whatever the window’s flavor arranges for.
The exceptions are actually indicated by flags, bits, set in the window. The operation to handle the exception should do nothing if it is invoked when the corresponding flag is not set, and should not return with the flag still set (or an error will be signaled). The end-of-page and more flags are set and cleared automatically by moving the cursor; as long as things are done properly, they will be set if and only if the cursor is in the right place for them. So the exception handler need only make sure to move the cursor to a good place. The output hold exception handler usually just waits for or brings about a situation in which the reason for the output hold is gone (usually because the window has been exposed).
.defmetamethod windows :handle-exceptions Performs the exception processing described by all the rest of this section. Exceptions are processed in this order:
Output Hold, End-of-Page, **MORE**, and End-of-Line. .end_defmetamethod
First, if the window’s output hold flag is set, an output hold exception
happens. The operation
:output-hold-exception is invoked to handle
Returns the output hold flag of window, which is 1 if there is a
hold and 0 if not. This is a
setf’able accessor defsubst.
.defmetamethod windows :output-hold-exception This operation should not return until the output hold is gone. It may wait for the output hold flag to be cleared, or try to cause it to be cleared. The default handler acts based according to the window’s deexposed typeout action (see (deexposed-typeout-action)). .end_defmetamethod
Next, if the end-of-page flag is set (normally the case if the
y-position of the cursor is less than one line-height above the
inside bottom edge of the window), the
operation is invoked.
Returns the end-of-page flag of window, which is 1 if the next
output operation should wrap and 0 otherwise. This is a
.defmetamethod windows :end-of-page-exception This operation is invoked to handle the end-of-page exception when present. It should do nothing if invoked when the flag is zero.
The default definition is simply to move the cursor to the top line, clear that line, and set the vertical position for the next **MORE** if more-processing is enabled. .end_defmetamethod
Next, if the window’s more flag is set, a more exception happens.
The more flag gets set when the cursor is moved to a new line
(e.g. when a
#\return is typed) and the cursor
position is thus made to be below the more vpos of the window. (If
nil, this exception is
suppressed and the more flag is turned off.) The
:more-exception operation is invoked to handle the exception.
Returns the more flag, which is 1 if the next output operation should
do a **MORE**, and 0 otherwise.
This is a
setf’able accessor defsubst.
.defmetainstvar windows tv:more-vpos The vertical position at which the next **MORE** should happen in output on the window. .end_defmetainstvar
.defmetamethod windows :more-vpos
Returns the window’s
Accessor defsubst for the preceding instance variable.
**MORE** processing does not happen if this variable is
during the output operation in which the **MORE** would have happened.
.defmetamethod windows :more-exception
:more-exception handler in the
tv:minimum-window flavor does
:clear-eol operation, types out **MORE**, reads a character
:more-tyi operation, restores the cursor position to where
it originally was when the
:more-exception was detected, does
:clear-eol to wipe out the **MORE**, and resets the
more vpos. The character read in is ignored.
This operation works by calling a subroutine,
if the more flag is set. It
should do nothing if the flag is zero. It is safe to redefine it to
call that function with different arguments, or to do other things as
well. It is very risky to write a new definition from scratch, as
tv:sheet-more-handler is tricky.
':tyi) (more-string "**
Implements the standard handling of more exceptions, described above, using operation to read the input and more-string as the output to be printed and then erased.
Note that the more flag is set only when the cursor moves to the next
line, because a
#\return is typed out, after a
:line-out, or by the
:end-of-line-exception handler described below. It is not set when the
cursor position of the window is explicitly set (e.g. with
:set-cursorpos); in fact, explicitly setting the cursor position clears
the more flag. The idea is that when typeout is being streamed out
sequentially to the window, more-exceptions happen at the right times
to give the user a pause in which to read the text that is being typed, but
when cursor positioning is being used the system cannot guess what order
the user is reading things in and when (if ever) is the right time to stop.
In this case it is up to the application program to provide any necessary
The algorithm for setting the more vpos is too complicated to go into here in all its detail, and you don’t need to know exactly how it works, anyway. It is careful never to overwrite something before you have had a chance to read it, and it tries to do a **MORE** only if a lot of output is happening. But if output starts happening near the bottom of the window, there is no way to tell whether it will just be a little output or a lot of output. If there’s just a little, you would not want to be bothered by a **MORE**. So it doesn’t do one immediately. This may make it necessary to cause a **MORE** break somewhere other than at the bottom of the window. But as more output happens, the position of successive **MORE**s is migrated and eventually it ends up at the bottom.
If you mix in this flavor, when a
:more-exception happens, the
window will be exposed (an
:expose message will be sent to it). This
is intended to be used in conjunction with having a deexposed typeout
:permit (see (deexposed-permit)), so that a process can
type out on a deexposed window and then have the window expose itself
when a **MORE** break happens.
Finally, if the cursor is at or near the end of the line so that there
is no room to output the next character, an end-of-line exception
:end-of-line-exception operation is invoked to
handle it. A flag is not used to trigger this exception since the
condition depends on the width of the character to be output.
.defmetamethod windows :end-of-line-exception
This operation is defined by default to advance the cursor to the next
line, just as typing a
#\return character does normally (see
below). Doing so may, in turn, cause an
:more-exception to happen. Furthermore, if the right margin
character flag is on (see
(windows-right-margin-character-flag-init-option)), then before going
to the next line, an exclamation point in font zero is typed at the
cursor position. When this flag is on, end-of-line exceptions are
caused a little bit earlier, to make room for the exclamation point.
.defmetamethod windows :tyo-right-margin-character
If a right-margin character is to be printed, this operation is invoked
to print it. It can simply
:tyo the character.
The way the cursor position goes to the next line when it reaches the
right edge of the window is called horizontal wraparound or
continuation. You can make windows
that truncate lines instead of wrapping them around by using
.defflavor tv:line-truncating-mixin This mixin gives a window the ability to truncate lines at the right margin instead of continuing output onto the next line as usual (see continuation, (continuation)). Truncation is performed if the window’s truncate-line-out flag is set. When the cursor position is near the right-hand edge of the window and there is an attempt to type out a character, the character simply will not be typed out. .end_defflavor
.definitoption tv:line-truncating-mixin :truncate-line-out-flag flag Initializes the truncate-line-out flag of the window to flag. One means truncate and zero means do not. .end_definitoption
Returns the truncate-line-out flag of the window, which is zero or one.
One means truncate and zero means do not; however, the flag matters only
tv:line-truncating-mixin is in use. This is a defsubst which
This flavor is built on
mixed in. If you instantiate a window of this flavor, it will be like
regular windows of flavor
tv:window except that lines will be
truncated instead of wrapping around.
The window’s cursor position is where the upper left corner of the next output character will appear, with a vertical offset if necessary to match up the baselines of various fonts (see (font-baseline)). Recall that cursor position arguments and values of stream operations are relative to the inside upper left corner of the window.
.defmetamethod windows :read-cursorpos &optional (units
Return two values: the x and y coordinates of the cursor
position. These coordinates are in pixels by
default, but if units is
:character, the coordinates are given
in character-widths and line-heights. (Note that character-widths
don’t mean much when you are using variable-width fonts.)
.defmetamethod windows :increment-cursorpos x y &optional (units
Advances the cursor position the specified amount in each coordinate.
The units may be specified as with
:read-cursorpos. This operation
is considered to be sequential motion of the cursor through a variable
amount of space, rather than instantaneous jumping of the cursor. What
this means is that exceptions happen, just as if output were being done.
So the cursor wraps around at the margins (or does whatever this window does
**MORE** processing happens at the appropriate place.
The following few operations do cursor motion rather than advancing the cursor. The end-of-page, more and end-of-line exception flags will be set if the cursor is moved to a position where they ought to be on, and can be cleared if they were previously on and the cursor is moved to a place where they ought to be off. Exception handling does not take place.
.defmetamethod windows :set-cursorpos x y &optional (units
Moves the cursor position to the specified coordinates. The units may
be specified as with
:read-cursorpos. If the coordinates are
outside the window, move the cursor position to the nearest place
to the specified coordinates that is in the window.
.defmetamethod windows :home-cursor Moves the cursor to the upper left corner of the window. .end_defmetamethod
.defmetamethod windows :home-down Moves the cursor to the lower left corner of the window. .end_defmetamethod
.defmetamethod windows :forward-char &optional char Moves the cursor forward one character position, or the width of char in the current font if char is specified. Exceptions are processed, so this is like outputting a space which has the appropriate width. .end_defmetamethod
.defmetamethod windows :backward-char &optional char Moves the cursor backward one character position, or the width of char in the current font if char is specified. Exceptions are processed, but there is no reverse-wraparound. At the left margin, the cursor does not move. .end_defmetamethod
.defmetamethod windows :size-in-characters Returns two values, the dimensions of the window, in units of character-widths and line-heights. (Note that character-widths don’t mean much when you are using variable-width fonts.) .end_defmetamethod
.defmetamethod windows :set-size-in-characters width-spec height-spec &optional option
Sets the inside size of the window, according to the two specifications,
without changing the position of the upper-left corner. width-spec
and height-spec are interpreted the same way as arguments to the
respectively. option is passed along to
All the erasing operations operate on the window pixels by drawing the
area to be erased using the window’s erase-aluf as the alu function
(see (windows-tv:erase-aluf-instvar)). This is by default
tv:alu-andca, which clears the screen bits of the screen area drawn.
.defmetamethod windows :clear-char &optional char Erases the character at the current cursor position. When using variable-width fonts, you tell it the character code of the character you are erasing, so that it will know how wide the character is (it assumes the character is in the current font). If you don’t pass the char argument, it simply erases a character-width, which is fine for fixed-width fonts. .end_defmetamethod
.defmetamethod windows :clear-string string &optional start end
Erases enough space, starting at the cursor, to contain string (or
the portion of string from start to end), printed in the
current font. The entire height of the line is erased, so it does not
matter whether the text on the screen is string or something else.
string determines only how far to erase. If a fixed-width font is
in use, this is equivalent to doing
:clear-char once for each
character in string. This operation becomes desirable because of
.defmetamethod windows :clear-eol Erases from the current cursor position to the end of the current line; that is, erases a rectangle horizontally from the cursor position to the inside right edge of the window, and vertically from the cursor position to one line-height below the cursor position. .end_defmetamethod
.defmetamethod windows :clear-eof
Erases from the current cursor position to the bottom of the window.
In more detail, first does a
:clear-eol, and then
clears all of the window past the current line.
.defmetamethod windows :clear-screen Erases the whole window and moves the cursor position to the upper left corner of the window. .end_defmetamethod
.defmetamethod windows :clear-between-cursorposes start-x start-y end-x end-y Erases an area starting at cursor position start-x and start-y, wrapping around if necessary at the end of the line or the page, until end-x and end-y are reached.
Though the arguments are expressed as cursor positions, the cursor position of the window is not changed. .end_defmetamethod
Inserting a character means printing it at the cursor but pushing the rest of the text on the line toward the right margin. Similarly, deleting a character means pulling the following text on the line back toward the left so that the position occupied by the character is closed up. Inserting and deleting lines work the same way vertically, moving the lines below the cursor down or up.
The operations that take a numeric argument specifying the amount of
space to insert or delete also take an argument specifying the unit
:character) in which the space has been
measured. The unit argument’s meaning is the same as in the
((windows-read-cursorpos-method)) but the default is
:character rather than
.defmetamethod windows :delete-char &optional (n
Without an argument, deletes the character at the current cursor
position. Otherwise, deletes n characters (or n pixels if
:pixel), starting at the cursor position. Move the
display of the part of the current line that is to the right of the
deleted section leftwards to close the resultant gap. (If unit is
:character, this assumes all characters are one character-width
wide, and so will not do anything useful with variable-width fonts.)
.defmetamethod windows :delete-string string &optional (start
This is for deleting specific strings in the current font.
It is one of the things to use when dealing with variable-width fonts.
If string is a string, excise a region exactly as wide as that string, or a substring specified by start and end, and moves the display of the part of the current line that is to the right of the excised region leftwards to close the gap.
If string is a number, it is considered to be a character code. The single character is treated like a string containing that character. .end_defmetamethod
.defmetamethod windows :delete-line &optional (n
Without an argument, deletes the line that the cursor is on. Otherwise
deletes n lines, or n rows of pixels if unit is
starting with the one the cursor is on. Moves the display below the
deleted section up to close the resulting gap.
.defmetamethod windows :insert-char &optional (n
Opens up a space the width of n characters (or n pixels if
:pixel) in the current line at the current cursor
position. Shifts the characters to the right of the cursor further to
the right to make room. Characters pushed past the right-hand edge of
the window are lost. (If unit is
:character, this assumes all
characters are one character-width wide, and so will not do anything
useful with variable-width fonts.)
.defmetamethod windows :insert-string string &optional (start
Inserts a string at the current cursor position, moving
the rest of the line to the right to make room for it.
The string to insert is specified by string; a substring thereof may
be specified with start and end, as with
string may also be a number, in which case the character with that code is inserted.
If type-too is specified as
nil, the string is not actually
printed. The space opened up is big enough for the string, but is
.defmetamethod windows :insert-line &optional (n
Takes the line containing the cursor and all the lines below it, and
moves them down one line. The line containing the cursor is moved in
its entirety, not broken, no matter where the cursor is on the line.
A blank line is created at the cursor. If an argument n is given,
opens up n blank lines, or n rows of pixels if unit is
:pixel. Lines pushed off the bottom of the window are lost.
The following operations do not output, but provide information about what would happen to the cursor and the screen if output were done.
.defmetamethod windows :character-width char &optional (font
Returns the width of the character char, in pixels. The current
font is used if font is not specified.
If char is a
:character-width can return a negative number.
Tab, the number returned depends on the current cursor position.
If char is
Return, the result is defined to be zero.
.defmetamethod windows :compute-motion string &optional (start
0) stop-y bottom-limit right-limit font line-height tab-width
This is used to figure out where the cursor would end up if you were to output
:string-out. It does the right thing if you give it
just the string as an argument. start and end can be used to specify
a substring as with
:string-out. x and y can be used to start
your imaginary cursor at some point other than the present position of the real cursor.
If you specify cr-at-end-p as
t, it pretends to do a
instead of a
Return is output after the specified
portion of the string.
stop-x and stop-y define the size of the imaginary window in
which the string is being printed; the printing stops if the cursor
both of them. The imaginary printing stops
after the character which goes past the stop point. Note that this
is not the same as the meaning of the
stop-x argument to the
:string-length operation! The stop coordinates default to the lower
left-hand corner of the window. (This corner is reached before the
lower right-hand one, since output goes from left to right on each
bottom-limit and right-limit are vertical and horizontal positions at which to wrap around; they default to the inside height and width of the window. They differ from the stop-x and stop-y in that these act independently when the cursor reaches either one, and they cause the cursor position to change rather than terminating processing.
The computation normally uses font, or the window’s current font if
nil. However, if string is of type
art-fat-string, each character’s
%%ch-font field is used as
an index in the window’s font map to find the font for that character,
and font is ignored except possibly for defaulting the tab-width.
For vertical spacing, line-height is used.
The default for line-height is font’s line height if font is
nil, else the window’s line-height.
tab-width specifies the distance between tab stops, in pixels. If it is omitted,
the default is
(tv:sheet-tab-width self) if no font is specified, or
(* (tv:sheet-tab-nchars self) (tv:font-char-width font)) if a font
Four values are returned:
nilif the entire imaginary output was completed without reaching or passing the stop point; or the index in string of just after the character that reached or passed the stop point; or
tif an implicit
Returnwas requested and it reached or passed the stop point.
All coordinates for this operation are cursor positions, relative to the window’s inside edges. However, if you specify all the arguments you can use any origin of coordinate system you like, as long as you interpret the values in the same coordinate system. .end_defmetamethod
.defmetamethod windows :string-length string &optional (start
nil) stop-x (font
This is very much like
:compute-motion, but works in only one dimension.
It tells you how far the cursor would move if string were to be displayed
in the current font starting at the left margin, or at start-x if that is specified.
start and end work as with
specify a substring of string. If stop-x is not specified or
nil, the window
is assumed to have infinite width; otherwise the simulated display will stop
before any character which would move the cursor past stop-x pixels
from the left edge. If a character exactly reaches stop-x, it can fit.
Note that this is not the same as the handling of stop-x in the
The computation normally uses font, or the window’s current font if
nil. However, if string is of type
art-fat-string, each character’s
%%ch-font field is used as
an index in the window’s font map to find the font for that character,
and font is ignored except possibly for defaulting the tab-width.
tab-width specifies the distance between tab stops, in pixels. If it is omitted,
the default is
(tv:sheet-tab-width self) if no font is specified, or
(* (tv:sheet-tab-nchars self) (tv:font-char-width font)) if a font
:string-length returns three values:
Backspacecharacters in the string).
A window includes some state information which changes as output is done. These include the cursor position, the current font, alu function, and exception flags. The presence of this information makes the window behave coherently as a stream, so that the output from one operation follows that of the previous operation. But sometimes this is not desirable. The "explicit" output operations use a window only for its position and size, with all additional information passed by the caller explicitly. This way, multiple streams of output to the same window can exist, which do not interfere with each other by trying to use a single cursor.
The x and y position arguments used by these operations are relative to the outside edges of the window. This is different from the stream and higher-level operations. It is because these operations are frequently used for drawing parts of the margins, such as labels and margin regions.
.defmetamethod windows :string-out-explicit string start-x start-y x-limit y-limit font alu &optional (start
0) end multi-line-line-height
Outputs string (or the portion from start to end) onto the
window starting at start-x and start-y, neither using nor moving
the window’s cursor position. If x-limit or y-limit is
non-nil, output stops if it reaches that position.
Output is done in font using alu function alu. The window’s
current font and alu function are not used or set. If there are
Return characters in the output, and multi-line-line-height is
nil, they are printed as "Return" in a lozenge. If
multi-line-line-height is a number, that number is used as the
line height, ignoring the window’s line height, and the horizontal
output position moves to start-x rather than the left margin for
the next line of output.
Note that the arguments of
tv:sheet-string-out-explicit are in a different order.
The argument order of this operation was cleaned up.
The operation returns three values: the final x position, the final y position, and the final index in the string. You can use these to do multiple operations in consecutive places on the screen. .end_defmetamethod
.defmetamethod windows :string-out-centered-explicit string &optional left y-pos right y-limit font alu (start
0) end multi-line-line-height
Outputs string (or the portion from start to end) centered
between x positions left and right, at y position
y-pos. If y-limit is reached, output stops. left and
right default to the inside edges of the window.
Output is done in font and alu, which default to the ones current for the window, and lines are separated by multi-line-line-height (which defaults to the window’s line height). .end_defmetamethod
.defmetamethod windows :string-out-x-y-centered-explicit string &optional left top right bottom font alu start end multi-line-line-height Displays string (or the portion from start to end) with the rectangle it occupies centered both horizontally and vertically. Horizontally it is centered between left and right, and vertically between top and bottom. The defaults for these arguments are the inside edges of the window.
Output is done in font and alu, which default to the ones current for the window, and lines are separated by multi-line-line-height (which defaults to the window’s line height). .end_defmetamethod
The following operations and initialization options initialize, get, and set various window attributes which are relevant to the typing out of characters. (See also the operations to manipulate the current font, on (windows-set-current-font-method).)
.defmetainitoption windows :more-p t-or-nil
Initializes whether the window should have more processing. It
.defmetamethod windows :more-p
t if more processing (see (more-processing))
is enabled; otherwise, return
.defmetamethod windows :set-more-p more-p
If more-p is
nil, turns off more processing (see
(more-processing)); otherwise turns it on.
.defmetainitoption windows :vsp n-pixels Initializes the window’s vsp. It defaults to 2. .end_defmetainitoption
.defmetamethod windows :vsp Returns the value of vsp for this window (see (vsp)). .end_defmetamethod
.defmetamethod windows :set-vsp new-vsp Sets the value of vsp for this window (see (vsp)) to new-vsp. .end_defmetamethod
.defmetamethod windows :reverse-video-p
nil normally or
t if the window displays in white on
black rather than black on white. This is separate from the whole
screen’s inverse video mode, which is what
Terminal C sets.
.defmetamethod windows :set-reverse-video-p t-or-nil Enables or disables reverse-video display. Changing this mode inverts all of the bits in the window. .end_defmetamethod
.defmetainitoption windows :reverse-video-p t-or-nil Initializes the use of reverse-video display. .end_defmetainitoption
.defmetainitoption windows :right-margin-character-flag x
If x is 1, the window should print an exclamation point in the
right margin when
:end-of-line-exception happens; if x is 0,
it should not. The default is 0. See
Returns the flag which controls printing of characters at the right
margin on wrap-around on window. This is a
.defmetainitoption windows :backspace-not-overprinting-flag x
If x is 0, output of
#\backspace will move the cursor position
backward; if it is 1, it will display "overstrike" in a lozenge (that
#\backspace will be just like other special characters). The
default is 0. See (backspace-not-overprinting-flag-blurb).
Returns the flag which controls how
on window. This is a
setf’able accessor macro.
.defmetainitoption windows :cr-not-newline-flag x
If x is 0, output of
#\return will move the cursor position to
the beginning of the next line and clear that line; if it is 1, it
will display "return" in a lozenge (that is,
#\return will be just
like other special characters). The default is 0. This flag does not
affect the behavior of the
:line-out nor the
Returns the flag which controls how
on window. This is a
setf’able accessor macro.
.defmetainitoption windows :tab-nchars n
n is the separation of tab stops on this window, in units of the
char-width. This controls how the
prints. n defaults to 8.
Returns the distance between tab stops, measured in units of
Returns the distance between tab stops, measured in pixels.
Having used the Lisp Machine for a while, you have probably noticed that characters can be typed out in any of a number of different typefaces. Some text is printed in characters that are small or large, boldface or italic, or in different styles altogether. Each such type face is called a font. A font is conceptually an array, indexed by character code, of pictures showing how each character should be drawn on the screen.
A font is represented inside the Lisp Machine as a Lisp object. Each
font has a name. The name of a font is a symbol, usually in the
fonts package, and the symbol is bound to the font. A typical font
tr8. In the initial Lisp environment, the symbol
fonts:tr8 is bound to a font object whose printed representation is
#<font tr8 234712342>
The initial Lisp environment includes many fonts. Usually there are more fonts stored in QFASL files in file computers. New fonts can be created, saved in QFASL files, and loaded into the Lisp environment; they can also simply be created inside the environment.
Drawing of characters in fonts is done by microcode and is very fast. The internal format of fonts is arranged to make this drawing as fast as possible. This format is described later, but you almost certainly do not need to worry about it.
You can control which font is used when output is done to a window. Every window has a font map and a current font. The font map is conceptually an array of fonts; with a small non-negative number, the font map associates a font. The current font of a window is always one of the fonts in the window’s font map. Whenever output is done to a window, the characters are printed in the current font. You can change the font map and the current font of a window at any time with the appropriate operations.
The reason why the window has a font map rather than merely a current font is that it is necessary to know all the fonts that will be used before doing any output in order to know how to position the output properly (so that output in different fonts on the same line will look right).
In addition, certain output operations can accept fat strings (arrays of
art-fat-string) which contain 16-bit characters, and regard the
top 8 bits of each character as a font number to look up in the font
map. These include
.defmetamethod windows :font-map Returns the font map of the window. The object returned is the array that is actually being used to represent the font map inside the window. The elements are actual font objects.
You should not alter anything about this array, since the window depends
on it in order to function correctly. To change the font map, use the
.defmetamethod windows :set-font-map new-map Sets the font map to contain the fonts given in new-map. Returns the array of fonts that actually represents the font map inside the window (don’t mess with this array!). new-map may be an array of font specifiers, in which case this array is installed as the new internal array of the window, and the font specifiers are replaced by fonts. Font specifiers are described in the following section; a font or the name of a font may be used.
new-map may also be a list of font specifiers, in which case the
array is created from the list in the style of
fillarray, with the
last element of the list filling in the remaining elements of the array
if any (the array is made at least 26. elements long, or long enough to
hold all the elements of the list).
If new-map is
nil, all the elements of the map are set to the
default font of the screen.
The current font is set to zero (the first font in the list or array). The line height and baseline of the window are adjusted appropriately (see below).
The specified font specifiers are remembered so that the
:change-of-default-font operation can cause the map to be recomputed
from them. This is in case one of the specifiers is a purpose keyword.
.defmetainitoption windows tv:font-map new-map
This option lets you initialize the font map. new-map is
interpreted the same way it is interpreted by the
.defmetainstvar windows tv:font-map The window’s font map. .end_defmetainstvar
.defmetamethod windows :current-font Returns the current font, as a font object. .end_defmetamethod
.defmetamethod windows :set-current-font new-font Sets the current font of the window. new-font may be a number, in which case that element of the font map becomes the current font. It may also be a font specifier, in which case the font that the specifier describes is used, unless that font is not in the font map, in which case an error is signalled. Only fonts already in the font map may be selected. .end_defmetamethod
.defmetainstvar windows tv:current-font The window’s current font. .end_defmetainstvar
.defmetamethod windows :baseline Returns the maximum baseline of all the fonts in the font map. The bases of all characters will be aligned so as to be this many pixels below the y cursor position, which is top of the line on which the characters are printed. In other words, when a character is drawn, it will be drawn below the cursor position, by an amount equal to the difference between this number and the baseline of the font of the character. .end_defmetamethod
.defmetainstvar windows tv:baseline The position of the baseline of a text line, in pixels from the top of the line’s vertical extent (its cursor position). .end_defmetainstvar
Accessor defsubsts for the corresponding instance variables.
You can use the
List Fonts command in Zmacs to get a list of all of the
fonts that are currently loaded into the Lisp environment. Here is a list
of some of the useful fonts:
medfnt. When you use
Split Screen, for example, the
Abortitems are in this font.
hl10, used for selected items in Choose Variable Values windows.
Different kinds of screen require different kinds of fonts. The two kinds of screens currently supported are black-and-white screens with one bit per pixel, and color screens with four bits per pixel. Color screens with eight bits per pixel will certainly be supported in the near future, and other kinds of screen may appear. However, it is nice to be able to write programs that will work no matter what screen their window is created on. The problem is that if your program specifies which fonts to use by actually naming specific fonts, then the program will only work if the window that you are using is on the same kind of screen as the fonts you are using were designed for.
To solve this problem, a program does not have to specify the actual font to be used. Instead, it specifies a certain symbol that stands for a whole collection of fonts. All of these fonts are the same except that they work on different kinds of screens. The symbol that you use is the name of the member of the collection that works on the black-and-white screen. In other words, when you want to specify a font, always use the name of a black-and-white font rather than a font itself. Every screen knows how to understand these symbols and find an appropriate font to use. This symbol is called a font specifier, because it describes a font rather than actually being a font.
A font object may be supplied as a font specifier. This does not mean
to use the font as specified; it means to use the font’s name as a font
specifier. Thus, if you supply the font object for the black-and-white
cptfont for a window on a color screen, the symbol
fonts:cptfont is used as a font specifier, resulting in the color
cptfont actually being used.
The functions that understand font specifiers have some cleverness in
order to make life easier for you. If you pass in the name of a font
that is not loaded into the Lisp environment, an attempt will be made to
load it from the file server, using the name of the font as the name of
the file, leaving the version and type unspecified, using the
function. The filename used is
SYS: FONTS; fontname QFASL.
Also, the color screen knows how to create color versions of fonts on
the fly if they do not already exist. Either of these things may make
your program run slowly the first time you run it, and so, if you care,
you can load the file yourself and create a color version of the font
yourself (see (color:make-color-font-fun)).
Since different users like to use different fonts, we provide a facility
called font purposes. Wherever a font specifier is used, the
program can specify a purpose keyword instead. This means, "use
whatever font the user likes to use for this particular purpose".
The window remembers when a purpose was specified instead of a
particular font, so that if the user changes the standard font for that
purpose, all the existing windows that were told to use that purpose
will change font. The user specifies a standard font for a purpose with
tv:set-screen-standard-font. Each screen has its own alist mapping
font purposes to font names, but normally they are all altered in
parallel. Defined purpose keywords include
It is up to each program to decide when any of these purpose keywords is appropriate.
.defmethod tv:screen :parse-font-specifier font-specifier Parses a font specifier in the proper way for this window, according to the screen the window is on. The value is a font object. .end_defmethod
.defmethod tv:screen :parse-font-name font-specifier Parses a font specifier in the proper way for this window, according to the screen the window is on. The value is a font name: a symbol which, evaluated repeatedly, ultimately produces a font. .end_defmethod
Returns the font that font-name is the name of; this is done by evaluating font-name repeatedly until the result is not a symbol.
Sets the standard font for purpose purpose on each screen based on font-specifier. font-specifier is turned into a font by each screen individually, and that font becomes the new standard font for purpose on that screen. All windows on the screen that were set up to use the standard font for this purpose will switch to using the newly specified font.
Sets the standard font for purpose
Sets the standard font for purpose on screen only.
.defmetamethod windows :change-of-default-font old-font new-font
Informs the window that the meaning of some standard font-name symbols
has changed. If the window uses any of them, it may need to recompute
various things; for example, if that font is used in the label, the
window’s inside size may be changed; if it is used in the window’s font
map, the line height may be changed. Either one means the number of
lines may change, and this may require adjustment of other data.
This can be done by an
:after daemon on this operation.
In addition, the operation must be passed along to all inferiors and potential inferiors. .end_defmetamethod
Fonts, and characters in fonts, have several interesting attributes. One attribute of each font is its character height. This is a non-negative fixnum used to figure out how tall to make the lines in a window. We have mentioned earlier that each window has a certain line height. The line height is computed by examining each font in the font map, and finding the one with the largest character height. This largest character height is added to the vsp specified for the window (see (vsp)), and the sum is the line height of the window. The line height, therefore, is recomputed every time the font map is changed or the vsp is set. It works this way so that there will always be enough room on any line for the largest character of the largest font to be displayed, and still leave the specified vertical spacing between lines. One effect of this is that if you have a window that has two fonts, one large and one small, and you do output in only the small font, the lines will still be spaced far enough apart that characters from the large font will fit. This is because the window system can’t predict when you might, in the middle of a line, suddenly switch to the large font.
Another attribute of a font is its baseline. The baseline is a non-negative fixnum that is the number of raster lines between the top of each character and the base of the character. (The "base" is usually the lowest point in the character, except for letters that descend below the baseline such as lower case "p" and "g".) This number is stored so that when you are using several different fonts side-by-side, they will be aligned at their bases rather than at their tops or bottoms. So when you output a character at a certain cursor position, the window system first examines the baseline of the current font, then draws the character in a position adjusted vertically to make the bases of all the characters line up.
There is another attribute called the character width. This can be an attribute either of the font as a whole, or of each character separately. If there is a character width for the whole font, it is as if each character had that character width separately. The character width is the amount by which the cursor position should be moved to the right when a character is output on the window. This can be different for different characters if the font is a variable-width font, in which a "W" might be much wider than an "i". Note that the character width does not necessarily have anything to do with the actual width of the bits of the character (although it usually does); it is just defined to be the amount by which the cursor should be moved.
There is another attribute that is an attribute of each character separately; it is called the left kern. Usually it is zero, but it can also be a positive or negative fixnum. When the window system draws a character at a given cursor position, and the left kern is non-zero, then the character is drawn to the left of the cursor position by the amount of the left kern, instead of being drawn exactly at the cursor position. In other words, the cursor position is adjusted to the left by the amount of the left kern of a character when that character is drawn, but only temporarily; the left kern affects only where the single character is drawn and does not have any cumulative effect on the cursor position.
A font that does not have separate character widths for each character and does not have any non-zero left kerns is called a fixed-width font. The characters are all the same width and so they line up in columns, as in typewritten text. Other fonts are called variable-width because different characters have different widths and things do not line up in columns. Fixed-width fonts are typically used for programs, where columnar indentation is used, while variable-width fonts are typically used for English text, because they tend to be easier to read and to take less space on the screen.
Each font also has attributes called the blinker width and blinker height. These are two non-negative fixnums that tell the window system a nice-looking width and height to make a rectangular blinker for characters in this font. These attributes are completely independent of everything else and are used only for making blinkers. Using a fixed width blinker for a variable-width font is not the nicest-looking thing to do; instead, the editor actually re-adjusts its blinker width as a function of what character it is on top of, making a wide blinker for wide characters and a narrow blinker for narrow characters. But if you don’t want to go to this trouble, or don’t necessarily know just which character the blinker is on top of, you can just use the font’s blinker width as the width of your blinker. For a fixed-width font there’s no problem.
There is also an array for each font called the char-exists table.
It is an
art-1b array with a 1 for each character that actually
exists in the font, and a 0 for other characters. This table is not
used by the character-drawing software; it is just for informational
purposes. Characters that do not exist have pictures with no bits "on"
in them, just like the "space" character. Most fonts implement most of the
printing characters in the character set, but some are missing some
This section explains the internal format in which fonts are represented. Most users do not need to know anything about this format; you can skip this section without loss of continuity.
Fonts are represented as arrays. The body of the array holds the bits of the characters, and the array leader holds the attributes of the font and characters as well as information about the format of the body of the array. Note that there is only one big array holding all the characters, rather than a separate array for each character. The format in which the bits are stored is specially designed to maximize the speed of character drawing and to minimize the size of the data structure, and so it is not as simple you might expect.
FED operates on fonts by converting them into a different type of
object containing the same data. This new object is called a font
descriptor; it is simpler and easier to work with. See the files
SYS: IO1; FNTDEF LISP for the format of font descriptors, and
SYS: IO1; FNTCNV LISP for functions to operate on them, and to
convert between font descriptors and fonts.
The font format works slightly differently depending on whether the font contains any characters that are wider than thirty-two bits. If there are any such characters, then the font is considered to be "wide", and a single character may be made up of several subcharacters to be drawn side by side. A wide font stores subcharacters instead of characters as such, and has a table indicating which subcharacters belong to each character of the character set. For the time being, we will discuss only narrow fonts in which there is no need to distinguish characters from subcharacters because each character is made of a single subcharacter.
Each character in a font has an array of bits stored for it. The dimensions of this array are called the raster width and raster height. The raster width and raster height are the same for every character of a font; they are properties of the font as a whole, not of each character separately. Consecutive rows are stored in the array; the number of rows per character is the raster height, and the number of bits per row is the raster width. An integral number of rows are stored in each word of the array; if there are any bits left over, those bits are unused. Thus no row is ever split over a word boundary. Rows are stored right-adjusted, from right to left. When there are more rows than will fit into a word, the next word is used; remaining bits at the left of the first word are ignored, and the next row is stored right-adjusted in the next word, and so on. An integral number of words is used for each character.
For example, consider a font in which the widest character is seven bits wide and the tallest character is six bits tall. The raster width of the font is seven and the raster height is six. Each row of a character is seven bits, and so four of them fit into a thirty-two bit word, with four bits wasted. The remaining two rows require a second word, the rest of which will be unused because the number of words per character must be an integer. So this font will have four rows per word, and two words per character. To find the bits for character three of the font, you multiply the character number, three, by the number of words per character, two, and find that the bits for character three start in word six. The rightmost seven bits of word six are the first row of the character, the next seven bits are the second row, and so on. The rightmost seven bits of the seventh word are the fifth row, and the next seven bits of the seventh word are the sixth and last row.
Note that we have been talking about "words" of the array. The
character-drawing microcode does not actually care what type the
array is; it only looks at machine words as a whole, unlike most of the
array-referencing in the Lisp Machine. In a Lisp-object-holding array
such as an
art-q array, the leftmost eight bits are not under control
of the user, and so these kinds of arrays are not suitable for fonts.
In general, you need to be able to control the contents of every bit in
the array, and so usually fonts are
art-1b arrays. This means you
need to know the internal storage layout of bits within an
array in order to fully understand the font format, so here it is: the
zeroth element of an
art-1b array is the rightmost bit of the
zeroth word, and successive elements are stored from right to left in
that word. The thirty-third element is the rightmost bit in the next
word, and so on.
Now, if there are any characters in the font that are wider than 32 bits, then even a single row of the font will not fit into a word. Such characters are divided into subcharacters no more than 32 bits wide, and the character is drawn by drawing all its subcharacters, one by one, side by side. The character drawing microcode can only handle ordinary narrow characters, and it is invoked once for each subcharacter in order to draw a wide character. In order to make this work, the wide font stores subcharacters in the same way a narrow font stores its characters.
In addition, the wide font has a font indexing table which gives
the first subcharacter number for each character code. (In a narrow
font, the font indexing table is
nil.) The character W would
be drawn by finding the value at index 127 (the code for W) in the
font indexing table, and the value at index 130. Suppose that these
are 171 and 173. Then W is made up of subcharacters 171 and 172.
Either of these subcharacters’ bits can be found in the same way that
the bits for character code 171 or 172 would be found in a narrow
The array leader of a font is a structure defined by
Here are the names of the accessors for the elements of the array
leader of a font.
The name of the font. This is a symbol whose value is the font and which serves to name the font. The print-name of this symbol appears in the printed representation of the font.
The character height of the font; a non-negative fixnum.
The character width of the characters of the font; a non-negative
fixnum. If the
tv:font-char-width-table of this font is non-
then this element is ignored except that it is used to compute the
distance between horizontal tab stops; it is typically the width
of a lower-case "m".
The baseline of this font; a non-negative fixnum.
If this is
nil then all the characters of the font have the same
width, and that width is given by the
tv:font-char-width of the
font. Otherwise, this is an
art-q array of non-negative fixnums,
one for each logical character of the font, giving the character width
for that character. The array must be an
art-q array for the
sake of the
If this is
nil then all characters of the font have zero left
kern. Otherwise, this is an array of fixnums, one for each logical
character of the font, giving the left kern for that character.
The blinker width of the font.
The blinker height of the font.
This is an
art-1b array with one element for each logical character
of the file. The element is 1 if the character exists and 0
if the character does not exist.
The raster height of the font; a positive fixnum.
The raster width of the font; a positive fixnum.
The number of rows of a character stored in each word of the font; a positive fixnum.
The number of words stored for each character or subcharacter; a positive fixnum.
If this is
nil, then no characters of this font are wider
than thirty-two bits. Otherwise, this is the font indexing table
of the font, an array indexed by character code, containing
the number of the first subcharacter for that character code.
There is an extra array element at an index one greater than
the largest character code; it says where the subcharacters
of the largest character code stop.
We mentioned earlier that you need to use different fonts to draw on different kinds of screen. To draw on a color screen, you must use a color font. If you just pass in a font specifier when you specify an element of a font map, then a color version of that font will be created if there isn’t one already, and it will be used as the font.
A color font is almost the same as a regular black-and-white font except that for each pixel there are many bits. For example, for a four-bit color display (the only type presently supported), there are four bits for each pixel. While nothing prevents each pixel of a font from having any value it wants, usually each pixel is either zero or one other specific value; that is, color fonts do not usually have multicolored characters in them, or two characters of different color.
Color fonts can be created from black-and-white fonts by the following function:
Creates and returns a new font. bw-font should be an existing
black-and-white font. The new font has all the same attributes as
bw-font, and each character has the same attributes as the
corresponding character in bw-font. For each zero-valued pixel in
bw-font, the pixel in the new font is zero as well. For each
one-valued pixel in bw-font, the pixel in the new font has value
color. The name of the new font is formed by appending "color-",
the print-name of the name of bw-font, and suffix together to
form a string, and then interning that string in the
When a font specifier is examined and the window system decides to
make a color version of the font, it calls
with only one argument, letting the others default. So, for example,
if a color version of
fonts:foo-font is automatically created, its
name will be
fonts:color-foo-font, and its pixels will have the
value 17 wherever those in the original font have the value one.
However, you can call
color:make-color-font to make many color
versions of the same black-and-white font, each in a different color.
Something to keep in mind when using color fonts is that when
characters of a color font are drawn, onto a color window, and the
char-aluf of the window is
tv:alu-ior (as it normally is), then
the bits of the pixels of the character will be bit-wise "or"’ed with
the existing bits in the pixels of the window. If the existing bits
(that is, the background against which the character is being drawn)
are all zero, there’s no problem. But if they are not, the resulting
values of the pixels will be some color determined by a bit-wise "or"
of two color values, which is unlikely to yield meaningful results.
Unless this is actually what you want, you should make sure that the
background is made of zeroes before drawing characters onto a color
A window can be used to draw graphics (pictures). There is a set of
operations for drawing lines, circles, sectors, polygons, cubic splines,
and so on, implemented by the flavor
tv:graphics-mixin flavor is a component of the
and so the operations documented below will work on windows of flavor (or
flavors built on)
.defflavor tv:graphics-mixin Defines the standard window graphics operations. .end_defflavor
There are also some operations in this section that are in
tv:stream-mixin ((tv:stream-mixin-flavor)) rather than
tv:graphics-mixin, because they are likely to be useful to any
window that can draw characters, but such windows might not want the
full functionality of
tv:graphics-mixin. These operations are
:draw-rectangle, and the
:bitblt operation and its relatives. (If
you are building on
tv:window anyway, this doesn’t affect you, since
tv:window includes both of these mixins.)
The cursor position is not used by graphics operations; the operations explicitly specify all relevant coordinates. All coordinates are in terms of the inside size of the window, just like coordinates for typing characters; the margins don’t count. Remember that the point (0,0) is in the upper left; increasing y coordinates are lower on the screen, not higher. Coordinates are always fixnums.
As with typing out text, before any graphics are typed the process must wait until it has the ability to output (see (ability-to-output)). The "output hold flag" must be off and the window must not be temp-locked. The other exception conditions of typing out are not relevant to graphics.
All graphics functions clip to the inside portion of the window. This means that when you specify positions for graphic items, they need not be inside the window; they can be anywhere. Only the portion of the graphic that is inside the inside part of the window will actually be drawn. Any attempt to write outside the inside part of the window simply won’t happen.
Most graphics operations take an alu argument, which controls how the bits of the graphic object being drawn are combined with the bits already present in the window. In most cases this argument is optional and defaults to the window’s char-aluf (see (char-aluf)), the same alu function as is used to draw characters, which is normally inclusive-or. The following variables have the most useful alu functions as their values:
Inclusive-or alu function. Bits in the object being drawn are turned on and other bits are
left alone. This is the
char-aluf of most windows. If you draw several
things with this alu function, they will write on top of each other, just
as if you had used a pen on paper.
And-with-complement alu function. Bits in the object being drawn are turned off and other
bits are left alone. This is the
erase-aluf of most windows. It is
useful for erasing areas of the window or for erasing particular characters
Exclusive-or alu function. Bits in the object being drawn are complemented and other bits are left alone. Many graphics programs use this. The graphics operations take quite a bit of care to do "the right thing" when an exclusive-or alu function is used, drawing each point exactly once and including or excluding boundary points so that adjacent objects fit together nicely. The useful thing about exclusive-or is that if you draw the same thing twice with this alu function, the window’s contents are left just as they were when you started; so this is good for drawing objects if you want to erase them afterwards.
Alu function to copy the input bits into the output bits, ignoring the old values of the output bits. This is not useful with the drawing operations, because the exact size and shape of the affected region depend on the implementation details of the microcode. The seta function is useful with the bitblt operations, where it causes the source rectangle to be transferred to the destination rectangle with no dependency on the previous contents of the destination.
"And" alu function. Like
tv:alu-seta, this is not useful with the
drawing operations, but can be useful with the bitblt operations. 1 bits
in the input leave the corresponding output bit alone, and 0 bits in the
input clear the corresponding output bit.
.defmethod tv:graphics-mixin :point x y Returns the numerical value of the picture element at the specified coordinates. The result is 0 or 1 on a black-and-white TV. Clipping is performed; if the coordinates are outside the window, the result will be 0. .end_defmethod
.defmethod tv:graphics-mixin :draw-point x y &optional alu value Draws value into the picture element at the specified coordinates, combining it with the previous contents according to the specified alu function (value is the first argument to the operation, and the previous contents is the second argument.) value should be 0 or 1 on a black-and-white TV. Clipping is performed; that is, this operation will have no effect if the coordinates are outside the window. value defaults to -1, which is a pixel with all bits 1. .end_defmethod
.defmethod tv:stream-mixin :bitblt alu width height from-array from-x from-y to-x to-y
Copies a rectangle of bits from from-array onto the window. The
rectangle has dimensions width by height, and its upper left
corner has coordinates (from-x,from-y). It is transferred onto
the window so that its upper left corner will have coordinates
(to-x,to-y). The bits of the transferred rectangle are combined
with the bits on the display according to the Boolean function
specified by alu. As in the
bitblt function, if
from-array is too small it is automatically replicated.
See the discussion of the
bitblt function ((bitblt-fun)) for
complete details. Note that to-array is constrained as described
there. See also the
tv:make-sheet-bit-array function below
.defmethod tv:stream-mixin :bitblt-from-sheet alu width height from-x from-y to-array to-x to-y
Copies a rectangle of bits from the window to to-array.
All the other arguments have the same significance as in
See the discussion of the
bitblt function ((bitblt-fun)) for
complete details. Note that to-array is constrained as described
there. See also the
tv:make-sheet-bit-array function below
.defmethod tv:stream-mixin :bitblt-within-sheet alu width height from-x from-y to-x to-y
Copies a rectangle of bits from the window to some other place in the
window. All the other arguments have the same significance as in
:bitblt. Note that width or height may be negative, in
which case the coordinates to be copied extend to lower values from the
specified starting values, and copying is done in reverse order. The
order bits are copied makes no difference when copying between different
arrays but is important when copying between overlapping portions of one array.
.defmethod tv:graphics-mixin :draw-char font char x y &optional alu Displays the character with code char from font font on the window with its upper left corner at coordinates (x,y). This lets you draw characters in any font (not just the ones in the font map), and it lets you put them at any position without affecting the cursor position of the window. .end_defmethod
.defmethod tv:graphics-mixin :draw-line x1 y1 x2 y2 &optional alu (draw-end-point
Draws a line on the window with endpoints (x1,y1) and (x2,y2).
If draw-end-point is specified as
nil, does not draw the last
endpoint (that is, stops drawing just before that point instead of at
it). This is useful with alu function
tv:alu-xor when multiple
connected lines are in use, since drawing an endpoint once each for
two lines would cancel out.
.defmethod tv:graphics-mixin :draw-lines alu x0 y0 x1 y1 ... xn yn Draws n lines on the screen, the first with endpoints (x0,y0) and (x1,y1), the second with endpoints (x1,y1) and (x2,y2), and so on. The points between lines are drawn exactly once and the last endpoint, at (xn,yn), is not drawn. .end_defmethod
.defmethod tv:graphics-mixin :draw-dashed-line x0 y0 x1 y1 alu dash-spacing space-literally-p offset dash-length
Draws a line divided into dashes. The first five arguments are
the same as those of the
The argument dash-spacing specifies the period of repetition of the dashes; it is the length of a dash plus the length of a space between dashes. Its default value is 20. dash-length is the length of the actual dash; it defaults to half the spacing.
If space-literally-p is
nil, the spacing between dashes is
adjusted so that the dashes fit evenly into the length of line to be
drawn. If it is non-
nil, the spacing is used exactly as specified,
even though that might put the end point in the middle of a space
A nonzero offset is used if you want a space between the starting point and the
beginning of the first dash. The value is the amount of space desired, in
pixels. The same space will be provided at the end point, if space-literally-p
nil. offset defaults to zero.
.defmethod tv:graphics-mixin :draw-curve x-array y-array &optional end alu closed-p
Draws a sequence of connected line segments. The x and y coordinates
of the points at the ends of
the segments are in the arrays x-array and y-array. The points
between line segments are drawn exactly once and the point at the end of
the last line is not drawn at all; this is especially useful when alu is
tv:alu-xor. The number of line segments drawn is 1 less than the
length of the arrays, unless a
nil is found in one of the arrays first
in which case the lines stop being drawn.
If end is specified it is used in place of the actual length of the arrays.
If closed-p is non-
nil, the end point is connected back to the first point.
.defmethod tv:graphics-mixin :draw-wide-curve x-array y-array width &optional end alu closed-p
:draw-curve but width is how wide to make the lines.
.defmethod tv:stream-mixin :draw-rectangle width height x y &optional alu Draws a filled-in rectangle with dimensions width by height on the window with its upper left corner at coordinates (x,y). .end_defmethod
.defmethod tv:graphics-mixin :draw-triangle x1 y1 x2 y2 x3 y3 &optional alu Draws a filled-in triangle with its corners at (x1,y1), (x2,y2), and (x3,y3). .end_defmethod
.defmethod tv:graphics-mixin :draw-circle center-x center-y radius &optional alu Draws the outline of a circle centered at the point center-x, center-y and of radius radius. .end_defmethod
.defmethod tv:graphics-mixin :draw-circular-arc center-x center-y radius start-theta end-theta &optional alu Draws part of the outline of a circle centered at the point center-x, center-y and of radius radius.
The part of the circle to be drawn is specified by start-theta and end-theta. These angles are in radians; an angle of zero is the positive x direction, and angles increase counter-clockwise. The arc starts at start-theta and goes through increasing angles, passing through zero if necessary, to stop at end-theta. .end_defmethod
.defmethod tv:graphics-mixin :draw-filled-in-circle center-x center-y radius &optional alu Draws a filled-in circle specified by its center and radius. .end_defmethod
.defmethod tv:graphics-mixin :draw-filled-in-sector center-x center-y radius theta-1 theta-2 &optional alu Draws a "triangular" section of a filled-in circle, bounded by an arc of the circle and the two radii at theta-1 and theta-2. These angles are in radians; an angle of zero is the positive-X direction, and angles increase counter-clockwise. .end_defmethod
.defmethod tv:graphics-mixin :draw-regular-polygon x1 y1 x2 y2 n &optional alu
Draws a filled-in, closed, convex, regular polygon of
sides, where the line from (x1,y1) to (x2,y2) is one of the
sides. If n is positive then the interior of the polygon is on the
right-hand side of the edge (that is, if you were walking from (x1,y1)
to (x2,y2), you would see the interior of the polygon on your
right-hand side; this does not mean "toward the right-hand edge of the
.defmethod tv:graphics-mixin :draw-cubic-spline px py z &optional curve-width alu c1 c2 p1-prime-x p1-prime-y pn-prime-x pn-prime-y
Draws a cubic spline curve that passes through a sequence of points.
The arrays px and py hold the x and y coordinates of the
sequence of points; the number of points is determined from the active
length of px. Through each successive pair of points, a parametric
cubic curve is drawn with the
:draw-curve operation, using z
points for each such curve. If curve-width is provided, the
:draw-wide-curve operation is used instead, with the given width.
The cubics are computed so that they match in position and first
derivative at each of the points. At the end points, there are no
derivatives to be matched, so the caller must specify the boundary conditions.
c1 is the boundary condition for the starting point, and it defaults
:relaxed; c2 is the boundary condition for the ending point,
and it defaults to the value of c1. The possible values of boundary
:clamped; likewise, pn-prime-x and pn-prime-y specify the derivative at the ending point, and are only used if c2 is
:cyclicthen c2 is ignored. To draw a closed curve through n points, in addition to using
:cyclic, you must pass in px and py with one more than n entries, since you must pass in the first point twice, once at the beginning and once at the end.
:anticyclicthen c2 is ignored.
This subroutine of the
:draw-cubic-spline operation is also useful in its own right.
It does the computation of the spline to be drawn, then converts it into a sequence of line segments, returning arrays of x and y coordinates of endpoints of lines.
:draw-cubic-spline works by passing these arrays to the
The function returns three values, an array of x coordinates, an array of y coordinates, and the number of active points in those arrays. (The arrays are not required to have fill pointers.)
The arrays to be used can be supplied as the cx and cy arguments, or else new arrays will be created. If arrays are supplied and too short, they will be made longer.
Drawing graphics on a window is usually done by sending messages to the window. However, there is a certain overhead in sending each message. If your application requires speed, you can go to some more trouble by writing your very own method to do graphics. It is a good idea not to do this until you know that using existing messages will not work; it is easier and less bug-prone to use the existing messages than to write handlers for new ones.
To write a new method you must have a flavor to which to attach that method. In this case, we want to add some graphics messages to existing kinds of windows. So, what we want here is a mixin flavor. You will define a new mixin flavor for your application. You will add methods to this flavor to do the things you need to do. Then, when you want to create an actual window to use, you will create a window of a new flavor; this new flavor will include, as one of its mixins, your new mixin. For a simple case, you might use the following flavor definitions:
(defflavor circus-mixin () () (:required-flavors tv:essential-window)) ;;This makes the instance variables of tv:essential-window accessible. (defmethod (circus-mixin :draw-clown) (size weight happy-p) ...) (defmethod (circus-mixin :draw-tent) (height &optional (number-of-rings 3)) ...) (defflavor circus-window () (circus-mixin tv:window))
Now you can instantiate windows of flavor circus-window, and they will support your new messages.
Within the definition of a primitive output operation you will use the
graphics subprimitives such as
sys:%draw-char rather than the
high-level operations described in previous sections. To avoid
errors, you should use these subprimitives only from within window
methods that provide the error checking that the subprimitives lack.
In addition, the subprimitives must be used only within the body of a
tv:prepare-sheet special form. An error is signaled if they are
Executes body in an environment in which it is safe to draw on the
tv:prepare-sheet waits until the window is not output-held
or locked, and then opens all blinkers that could be on top of the
window so that they will not interfere with the output (see
(open-blinkers)). It also turns off interrupts so that the window will
remain unlocked and the blinkers will remain open.
Because interrupts are turned off, you must be careful in writing the
body. It should execute for no longer than
you would mind being unable to do a
Control-Abort. It also must
not wait for anything, since that would allow the blinkers to reappear
and defeat the whole purpose of preparing the sheet.
The microcode subprimitives generally use coordinates relative to the outside edges of the window. This is unlike the high-level interfaces, which use cursor positions, in which the margins of the window do not count. Also, subprimitives do little or no clipping or other testing for coordinates that are out of bounds. The results of passing erroneous coordinates are unpredictable; in principle, the machine might halt.
Another place you can use the subprimitives is inside the
operation of a blinker. This operation is always invoked in a suitable
environment for calling them, including interrupts off. Because
blinkers are always drawn by xor’ing, it does not actually matter
whether any other blinkers are present.
These instance variables and macros are useful in writing output primitives:
as-2-reverseon this array, indexed by coordinates relative to the outside edges, to examine and draw individual points. The dimensions of this array will be the width and height.
:string-outand so on all use
tv:char-alufand all the standard erase operations use
tv:erase-aluf. If your operation is a kind of drawing or a kind of erasing, it may be correct for you to use one of these two.
tv:alu-ior, which means to turn
on (set to all ones) the corresponding bits in the array.
tv:alu-andca, which means to turn off (set to zero) the
relevant bits. However, they would be different if the window were in
reverse video mode. Reverse video mode is not a highly-used feature,
but by using these variables you can make your extensions work correctly
in reverse video mode, so it is cleaner to use them.
However, you may use any alu function.
tv:alu-xor is often useful.
tv:alu-seta is usually not wise to use, since it will often result
in the alteration of bits that you did not expect to change, but which
happen to fall in the same word as the ones you were working on.
Here is an example from the
tv:graphics-mixin flavor, changed by
tv: prefixes in the places where you would need them if you
were to write this outside the
(defmethod (graphics-mixin :draw-point) (x y &optional (alu tv:char-aluf) (value -1)) (tv:prepare-sheet (self) (setq x (+ x (tv:sheet-inside-left)) y (+ y (tv:sheet-inside-top))) (if (not (or (< x (tv:sheet-inside-left)) ( x (tv:sheet-inside-right)) (< y (tv:sheet-inside-top)) ( y (tv:sheet-inside-bottom)))) (setf (ar-2-reverse tv:screen-array x y) (boole alu value (ar-2-reverse tv:screen-array x y))))))
This method takes its arguments in inside coordinates, and so it first converts them to outside coordinates. Then it compares them with the boundaries of the inside of the window, and does nothing if they are outside those boundaries. This is how it does clipping. Finally, if everything is OK, it reads out the current value of the point, combines it with the new value using the specified alu function (which defaults to the char-aluf of the window), and stores it back into the array.
In addition to using
as-2-reverse yourself, you can use these
subprimitives, mostly microcoded. They are equivalent in principle to
as-2-reverse many times, but they are much faster and have
much less error checking.
Some of these primitives will accept a sheet or an array. In
window-system applications the argument is usually a sheet, but any
suitable two-dimensional numeric array will do. (Suitable usually
means that the width, times the number of bits per element, is a
multiple of 32.) If an array is used, there is no need to worry about
tv:prepare-sheet. If you are doing the output on a window, you
should pass the window, not its screen array.
Draws a rectangle of size width by height with its upper left corner at x-bitpos, y-bitpos. Alu function alu-function is used, so you can draw, erase or complement the rectangle with the same function. sheet-or-array is usually the sheet to be drawn on. There is no clipping or error checking.
This is a little smarter, clipping to the edges of sheet. It does not work on arrays.
This clips to the inside edges of sheet.
Draws a line from (x0,y0) to (x,y), all relative to the
outside edges of the sheet, or indices in the array.
The point at (x,y) is not drawn if draw-end-point-p is
No clipping or error checking is done.
Draws a triangle with the specified corners. No clipping or error checking is done.
Draws the character with code char in font with its upper left corner at position (x,y) in outside coordinates. alu is used as the alu function, so you can either draw or erase. There is no clipping or error checking.
This is the actual microcoded primitive.
It does not take into account the indexing table of a wide font,
so when used on a wide font char is not the character code that
the user actually wants to output. It is best to use
This function operates on a rectangular portion of an
It examines each element of the array, and replaces the value of that element
with n0 if its previous value was 0, n1 if its previous
value was 1, and so on. The upper-left hand corner of the array is
specified by start-x and start-y, and its size is specified
by width and height. array must be an
and the specified rectangle must be within the bounds of the array.
Copies or merges a rectangular portion of from-array to a congruent portion of to-array. from-x and from-y specify one corner of the rectangle in from-array, and to-x and to-y specify the corresponding point in to-array. The opposite corner is found by adding width and height to either of those two positions. The copying is done starting at the specified corner and proceeding toward the opposite one.
The width of each array, times the number of bits per element in that
array, must be a multiple of 32.
When used in window system applications, one of the arrays will
frequently be a window’s screen array. Then the window must be
The operation is not simply one of copying; the bits coming from
from-array can be merged with those of to-array. This is
controlled by the alu argument. Each pair of bits is combined
according to that argument to get the new bit to put in to-array.
If alu is
tv:alu-seta, the old bits in to-array are ignored.
If alu is
tv:alu-ior, then the old bits and the incoming bits
are or’ed together. And so on.
bitblt is careful never to
change bits in to-array outside the specified rectangle, which is
why it is safe to use
tv:alu-seta, whereas it is not safe to use it
in the other subprimitives.
This function creates a two-dimensional bit-array useful for bitblting
to and from windows. It makes an array whose first dimension
is at least x but is rounded up so that
bitblt’s restriction regarding
multiples of 32. is met, whose second dimension is y, and whose
type is the same type as that of the screen array of window (or the
type it would be if window had a screen array). make-array-options
are passed along to
make-array (see (make-array-fun)) when the
array is created, so you can control other parameters such as the area.
Programs and windows can use the mouse as an input device. The functions, variables, and flavors described below allow you to use the mouse to do some simple things. To get advanced mouse behavior in your own programs, like the way the editor gets the mouse to put a box around the character being pointed at, you have to define new methods for various window operations described in this chapter. Alternatively, you can invoke the built-in choice facilities, such as menus and multiple-choice windows; these high-level facilities are described later.
At any time the mouse is considered to be indicating a certain position on the screen, called the mouse cursor position. The mouse cursor is a conceptual entity which we think of as what moves, inside the machine, when the user moves the mouse.
The mouse cursor position is indicated on the screen by a blinker called the mouse blinker, an actual Lisp object of the sort described in the chapter on blinkers. Different blinkers can be the mouse blinker at different times, since each window can decide what to use as the mouse blinker when that window owns the mouse.
There can be more than one screen, but the mouse cursor position
is limited to one screen, called the mouse
sheet (it does not have to be a screen, but it normally is).
Mouse cursor positions are usually
represented relative to the outside of the mouse sheet, though in
operations on windows they are sometimes represented relative to the
particular window. The
Terminal command can be used to set the
mouse sheet to another screen if your Lisp Machine has more than one
screen; there is also a system menu option for this.
These variables give the position of the mouse, in pixels, measured from the outside upper-left corner of the mouse sheet. They are maintained by the process handling the mouse, normally the mouse process.
Makes sheet be the mouse sheet, the one on which the mouse cursor moves. Only inferiors of the mouse sheet (to any number of levels) can own the mouse.
The mouse sheet.
Applies function to args with sheet as the mouse-sheet.
Usually the mouse cursor moves only if the user moves the mouse. However, the program can move the mouse cursor, and change the logical position of the mouse, at any time. This is called warping the mouse. For example, double-click-left in the editor warps the mouse to where the editor cursor is currently located. Since there is no fixed association between positions of physical mouse on the table and spots on the screen, warping the mouse does not result in any inconsistency.
Warps the mouse to be at positions x, y with respect to the mouse sheet.
Tracking the mouse means examining the hardware mouse interface, noting how the mouse is moving, and adjusting the mouse cursor position and the mouse blinker accordingly. Mouse tracking is done by microcode within a window, and by a process called the mouse process when moving between windows. The mouse process also keeps track of which window owns the mouse at any time. For example, when the mouse enters an editor window, the editor window becomes the owner, and to indicate this, the blinker changes to a northeast arrow instead of a northwest arrow; this is all done by the mouse process.
In general, the mouse process decides how to handle the mouse based on the flavor of the window that owns the mouse. Some flavors handle the mouse themselves, running in the mouse process, in order to be able to put little boxes and such around things, usually to indicate what would happen if you were to click a button. The editor, the inspector, menus, and other system facilities do this. The flavor of the window owning the mouse is also what usually controls the effect of clicking the mouse buttons.
Clicks on the mouse are sometimes encoded into characters. Such
characters are normally forced into input buffers of windows (see
(tv:stream-mixin-force-kbd-input-method)), and so they are
distinguished from regular keyboard characters by having the
%%kbd-mouse bit turned on. Encoding of clicks is done with
tv:mouse-button-encode (see (tv:mouse-button-encode-fun)). See
(characters) for full information the fields of such a character.
Note that "mouse clicks" can also be done on the keyboard. See the
tv:*mouse-incrementing-keystates*, in (mouse-parameters).
These standard mixins handle mouse clicks by forcing keyboard input describing the click:
.defflavor tv:kbd-mouse-buttons-mixin Handles mouse clicks by encoding them as characters which are forced into the window’s input buffer. In more detail: if it is a double-click on the right button, the system menu is called forth. Otherwise, the encoded character representation of the click is forced into the input buffer of the window. Furthermore, if it is a single-click on the left button, the window is selected.
The state of the
Hyper keys at
the time of the click is included in the character, in the
%%kbd-control, etc., fields (see (%%kbd-control-var)).
This is just like
tv:kbd-mouse-buttons-mixin except that a blip
goes in the input buffer rather than just an encoded click. The blip
(:mouse-button encoded-click window x y)
This is more useful than just the encoded click: it tells you where the mouse was (relative to the outside part of the window), and which window the mouse was over (this is useful primarily if several windows are sharing the same input buffer).
The state of the
is included in the encoded click, in the
%%kbd-control, etc., fields.
The following subtle point may explain some difficulties you may have with the above flavors. It is a tricky point, and you can ignore it if you don’t understand it. The characters (or blips) created by the flavors above go straight into the window’s input buffer. Under some circumstances they may bypass pending characters that have been typed ahead at the keyboard. So if you type something and then mouse-click at something in rapid succession while your program is busy, the program may see the mouse-click before it sees the character from the keyboard. [This may be fixed in the future.] See (typeahead-explanation), for further discussion of these issues.
Usually the mouse is handled according to the window that it is
positioned over. We say that this window owns the mouse.
The window that owns the mouse is the one that will receive the
So the usual case is that the window under the mouse owns the mouse.
Since windows are arranged in a hierarchy, generally a window, its superior, its superior’s superior, and so on, are all under the mouse at the same time. So the window that owns the mouse is really the lowest window in the hierarchy (farthest in the hierarchy from the screen) that is visible (it and all its ancestors are exposed). If you move the window to part of the screen occupied by a partially-visible window, then one of its ancestors (often the screen itself) becomes the owner. The screen handles single-clicking on the left button by selecting the window under it; this is why you can select partially-visible windows with the mouse.
A greedy window can keep ownership of the mouse even if the mouse
moves outside of it, by setting
tv:window-owning-mouse to that
window. This should be done only when that window has come by the
mouse by legitimate means, inside a
:handle-mouse operation on
that window or one of the other operations invoked by it. Inferiors
of the greedy window can still own the mouse when it is over them.
Greediness ends when
tv:window-owning-mouse is set back to
(its normal state). Then the mouse goes back to being owned by
whichever window is under it. While a window is being greedy, mouse
tracking continues to use the methods of the owning window, but the
way of determining the owning window is changed.
The mouse can also be grabbed, which means that some process has
taken it away from all windows. This state is represented by
t. See (grabbing-mouse).
Usurping the mouse is an even more drastic method of taking over control. It turns the mouse process off, so you have to do the tracking yourself. See (usurping-mouse).
If this is
nil, the mouse is owned by the window under it.
If this is
t, the mouse is grabbed.
If this is a window, the mouse is owned by that window.
Returns the window that now owns the mouse, either because it is being
greedy or because the mouse is over it. If the mouse has been grabbed,
the value is
The window that is currently handling the mouse.
This is the window that
the last time the mouse process called it.
Informs the mouse process that the screen layout has changed. Anything which may change which window is under any point where the mouse might be should call this function.
.defflavor tv:hysteretic-window-mixin This mixin makes a window continue to own the mouse (by being greedy) for a small distance beyond the edges of the window. This distance is called the hysteresis, and you can specify it. This mixin is used by momentary menus, so that if you accidentally slip a bit outside the menu, the menu won’t vanish; you have to get well away from it before it vanishes. .end_defflavor
.definitoption tv:hysteretic-window-mixin :hysteresis n-pixels Sets the initial value of the hysteresis, in pixels. It defaults to 25. (decimal). .end_definitoption
.defmethod tv:hysteretic-window-mixin :hysteresis .defmethod1 tv:hysteretic-window-mixin :set-hysteresis new-hysteresis Examine or set the hysteresis of the window. .end_defmethod
Normally mouse clicks and motion are interpreted by a window that owns
the mouse. Some applications, such as
Edit Screen, use the mouse
for choosing a window to be operated on. Then it is necessary to make
sure that control of the mouse remains with the program that is doing
Edit Screen) rather than going to whatever window the
user wants to choose. This is done by grabbing the mouse.
When the mouse is grabbed, the mouse process gets told that no window
owns the mouse, and it changes the mouse blinker back to the default (a
northeast arrow). The mouse process will continue to track the mouse,
and your process can now watch the position and the buttons by using
tv:mouse-y, and the
variables and functions described below.
tv:with-mouse-grabbed special form just has a body:
The forms inside are evaluated with the mouse grabbed.
This variable contains a mask describing the mouse buttons, as of the last time
the process handling the mouse looked at them.
The numbers 1, 2, and 4 represent the left, middle, and
right buttons respectively, and the value of
is the sum of the numbers representing the buttons that were being held
The speed the mouse has been moving recently, in units approximately like inches per second.
This function waits for any of the variables
tv:mouse-last-buttons to become different from
the values passed as arguments. To avoid timing errors, your program
should examine the values of the variables, use them, and then pass in
the values that it examined as arguments to
tv:mouse-wait when it is
time to wait for the mouse to move again. It is important to do things
in this order, or else you might fail to wake up if one of the variables
changed while you were using the old values and before you called
When a mouse button has been pushed, and you want to interpret this push
as a click, call this function. It watches the mouse button and figures
out whether a single-click or double-click is happening. It returns
nil if no button is pushed, or an encoded character describing the click
You should call
tv:mouse-button-encode only when a button has just been
pushed; that is, when you see some button down that was not down before.
You have to pass in the argument, bd, which is a bit mask saying
which buttons were pressed down: which are down now that were not down
"before". The form
(logand (logxor old-buttons -1) new-buttons) will
compute this mask, where old-buttons is a mask of the buttons that
were down before and new-buttons is a mask of the ones that are
Modifies char by setting the bits corresponding to all the shift keys
currently pressed down on the keyboard. This is useful on the result
tv:mouse-button-encode, if you wish to record the
state of the shift keys in the description of a mouse click so that
the shift keys can alter the meaning of the click.
When grabbing or usurping the mouse, you should explain
what is going on in the mouse-documentation line at the bottom of the screen.
with-mouse-usurped bind this
nil, which makes the mouse-documentation line blank.
Inside the body of one of these special forms, you may
variable to a string, which will be displayed in the mouse-documentation line.
If your program has "modes" which affect how the mouse acts, each part
of the program should
setq this variable to its own documentation.
Returns the window that is seen at the point where the mouse is (or at
(x,y) in the mouse sheet, if they are non-
nil). This is the
window that is partially visible at that point. If operation is
nil, only windows that handle that operation are considered at
all. active-condition is another way of filtering among windows;
it can be
:exposed, to select among active or exposed
This is used by the mouse process in deciding which window owns the mouse, and can also be used by you when you have grabbed the mouse.
0) abortable ¶
Grabs the mouse and asks the user to specify a rectangle by clicking at two corners. This is how the system menu Create option works. Four values are returned, the left, top, right, and bottom of the rectangle, all relative to sheet.
left and top, if non-
nil, are where to position the mouse
initially when asking for the upper left corner. If right and
bottom are also non-
nil, then when asking for the lower right
corner the mouse is positioned initially so as to make a rectangle of
the same size as the arguments specify. In other words, what matters
about the argument right is how much bigger it is than left.
minimum-width and minimum-height constrain the values that may be returned.
If abortable is non-nil, the user is permitted to abort
by clicking the middle button. Then the function returns
It is often useful to call this function via
Grabs the mouse and asks the user for new edges for window, returns
them, and (unless inhibited) sets the edges of window to them as
well. window’s edges are set unless move-p is
The values are the new edges, suitable for the
or nil if the user aborted.
Grabs the mouse and asks the user for a new position for window.
The new position is returned as two values, and window is moved
to that position unless move-p is
The values are the new position of the upper left corner,
suitable for the
nil if the user aborted.
For high real-time performance, you can usurp the mouse. Then the
mouse process steps aside and lets you do everything related to
tracking the mouse until you return control of it. The variables
tv:mouse-y are not updated while the mouse is
usurped. The mouse blinker disappears, and if you want any visual
indication of the mouse to appear, you have to do it yourself.
tv:with-mouse-usurped special form just has a body:
The forms inside are evaluated with the mouse usurped.
Waits until something happens with the mouse, and then returns saying what
happened. Four values are returned. The first two are delta-x and
delta-y, which are the distance that the mouse has moved since the
tv:mouse-input was called. The second two are
buttons-newly-pushed and buttons-newly-raised, which are bit
masks (using the bit assignment used by
above) saying what buttons have changed since the last time
tv:mouse-input was called.
You may call this function only with the mouse usurped; otherwise you will get in the way of the mouse process, which calls this function itself, and mouse tracking won’t work correctly.
tv:mouse-y are not maintained by
this function; you must do it yourself if you want to keep track of a
cumulative mouse position.
tv:mouse-last-buttons is maintained.
The buttons-newly-pushed value is suitable for being passed as
an argument to
tv:mouse-buttons-encode, which can be used with
the mouse usurped as well as with the mouse grabbed.
If wait-flag is
nil, then the function will not wait; it
may return with all zeroes, indicating that nothing has changed.
Returns the current state of the mouse buttons, in the format used by
tv:mouse-last-buttons variable, by examining the hardware mouse
The mouse is rarely grabbed or usurped. Normally it is owned by a window (or a screen). Then, mouse handling works through various flavor operations on the owning window. There are several operations, used at various points in mouse handling, to give you convenient hooks for modifying a window’s behavior.
The outermost loop of mouse handling determines the owning window and
then invokes its
:handle-mouse method. When this method returns,
the owning window is recalculated.
.defmetamethod windows :handle-mouse This operation is invoked by the mouse process to handle the mouse while it is on this window. It should return only when the mouse moves out of the window, or if the mouse is grabbed.
The default definition is to call
The guts of the
typically set up the desired sort of mouse blinker and then call this
function. window is the window the mouse is being handled for, and
t to provide a scroll bar (see (scroll-bar)),
if the window implements one. Generally the
operation is used to compute the second argument.
A second argument of
:in is used for handling the scroll bar itself.
Values other than
:in should be avoided.
This function invokes the
:mouse-moves operation to inform the window
about mouse motion, and the
:mouse-buttons operation to inform it
about buttons going down. They are the most convenient hooks to use
for implementing simple new mouse behaviors.
.defmetamethod windows :set-mouse-cursorpos x y
.defmetamethod1 windows :set-mouse-position x y
Move the mouse instantaneously to the specified position. The effect is
as if the user had moved the mouse over to that spot, without the user
actually touching it. For
:set-mouse-position, x and y are
relative to the outside edges of the window. For
:set-mouse-cursorpos, they are relative to the inside edges (as in
.defmetamethod windows :mouse-moves x y This operation is invoked in the mouse process every time the mouse moves either into, within or out of this window. x and y are the current position of the mouse, relative to the outside edges of this window.
:mouse-moves handlers should always call
tv:mouse-set-blinker-cursorpos to make the mouse blinker move. In
addition, they frequently move other blinkers or turn them on or off.
This is how menus arrange to outline the item the mouse is over.
tv:mouse-default-handler is what invokes this operation.
When this window ceases to own the mouse, for whatever reason, the
:mouse-moves method will always be called one final time, so that it
can turn off extra blinkers, etc.
Moves the current mouse blinker to the current mouse position.
:mouse-moves methods typically call this function.
.defmetamethod windows :mouse-buttons mask x y This operation is invoked in the mouse process when a button is pressed. mask is a mask of the buttons pressed, and x and y are the mouse position (in the mouse sheet).
By default, this calls
tv:mouse-button-encode to check for double
clicks, then brings up the system menu for double-click-right;
otherwise, it invokes the
tv:mouse-default-handler is what invokes this operation.
.defmetamethod windows :mouse-click mouse-char x y
This operation is where most handling of mouse clicks actually goes
on. It is invoked in the mouse process. mouse-char is a
character code describing the button pressed and how many times; such
#\mouse-l-2. x and y are the position of the mouse at
the beginning of the click. It is preferable to use this position
rather than the current one, because the user positioned the mouse
accurately before clicking and motion during the click was probably
Any window selection desired should be done in another process, using
tv:mouse-select. It is unrobust
to do something so error-prone in the mouse process.
:or method combination is used, so that all the methods are run
until one of them returns non-
nil. So each mixin can define a way
of handling the mouse under certain circumstances, and it can decline to
handle the click by returning
nil. For example,
tv:margin-choice-mixin defines a
:mouse-click method which
handles the click if the position is inside a margin choice box, and
nil otherwise so that the window’s primary way of handling
clicks can be run.
.defmetamethod windows :who-line-documentation-string This operation should return a string describing what the mouse would do if clicked on this window in its current position. For example, menus return a string describing the menu item that the mouse is over. If different buttons do different things, or if multiple clicks are in use, the string should describe all the possibilities. .end_defmetamethod
Selects window, and safe to use in the mouse process because
it creates a temporary process to do the work in that case.
Brings up the system menu, and designed to be safe to use in the mouse
process. Used by
Some windows have the ability to scroll. They display only a portion of a virtual window which is (or may be) too big to be shown all at once. Scrolling means moving the actually-shown portion up or down through the entire display.
There are several ways the mouse can be used to scroll a window. Each is implemented by a mixin. They all communicate with the window using the same protocol. For the sake of this protocol, the contents of the window are considered to be divided vertically into "lines". A position for scrolling is expressed as the number of lines that are above the top of the window. These do not have to be actual lines of text, though usually they are, but they must all have the same height. Usually this common height is the window’s line-height, but that is not required.
.defmetamethod "scrolling windows" :enable-scrolling-p
The various mouse-scrolling features use this operation to decide
whether they should be active at any given time. If this operation
nil, the scrolling facilities do not react to the mouse.
.defmetamethod "scrolling windows" :scroll-position Returns four values:
.defmetamethod "scrolling windows" :scroll-to to &optional (type
type is one of:
Since to is not guaranteed to be legal, both types of scrolling must error check their arguments. .end_defmetamethod
.defmetamethod windows :new-scroll-position This operation is used by the program managing the window to tell the mouse scrolling facilities that the contents of the window have changed under program control. It should be invoked whenever either the total number of lines to scroll through or the line number at the top of the window is changed by anything except the mouse scrolling facilities.
Mouse scrolling facilities put daemons on this operation in order to update their displays when the situation changes. .end_defmetamethod
If you move the mouse to the left edge of an editor window from the inside, eventually the mouse cursor changes to a thick up-and-down arrow. Simultaneously, a thin vertical line appears next to and outside of the left border of the window. This is called entering the scroll bar, and the thin vertical line, which indicates the portion of the total text that is now on the screen, is the scroll bar itself.
The vertical position of the top and bottom of the thin vertical line, as proportions of the height of the window, are the same as the positions of the first and last lines of text on the screen, as proportions of the total number of lines.
While the mouse is in the scroll bar, clicks have special meanings:
.defflavor tv:basic-scroll-bar This mixin gives a window the ability to have a scroll bar. It defines three instance variables:
nil, the bar will be displayed whenever margin space is provided for it, even if the mouse is not there.
nilwhen the mouse is actually in this window’s scroll bar.
.definitoption tv:basic-scroll-bar :scroll-bar spec
Specifies whether to have a scroll bar, how big to make it, and where.
spec can be
nil for no scroll bar,
t for a default scroll
bar, or a small positive number, which requests a scroll bar of that
width. The scroll bar occupies space in the margins of the window.
.defmethod tv:basic-scroll-bar :set-scroll-bar spec
Sets whether this window has a scroll bar, or how wide it is. spec
is the same as in the
:scroll-bar init option. This can change the
inside size of the window, since it can change the amount of space
needed in the margin.
.defmethod tv:basic-scroll-bar :enable-scrolling-p
This mixin defines this operation to return
t when the window has a
scroll bar. See (scrolling/ windows-enable-scrolling-p-method) for a
description of this operation.
.definitoption tv:basic-scroll-bar :scroll-bar-always-displayed t-or-nil
nil to say that the bar of the scroll bar should appear on the
screen all the time, not just when the mouse is "in" it.
.defmethod tv:basic-scroll-bar :scroll-bar-always-displayed .defmethod1 tv:basic-scroll-bar :set-scroll-bar-always-displayed t-or-nil Get or set this flag in an existing window. Setting it updates the screen. .end_defmethod
.defmethod tv:basic-scroll-bar :scroll-more-above
.defmethod1 tv:basic-scroll-bar :scroll-more-below
t if there is text to scroll up (down) to. The default definition
:scroll-position operation; some flavors redefine it for
.defmethod tv:basic-scroll-bar :mouse-buttons-scroll mouse-char x y
This operation is invoked when the mouse is clicked in the scroll bar.
mouse-char is a character with
%%kbd-mouse set, identifying the
button clicked and how many times. x and y are the position at
the time of the click, relative to this window’s outside edges.
The default definition provides the standard scrolling commands; you can
.defmethod tv:basic-scroll-bar :scroll-relative from to
Scrolls the window to move what is now at the y-position from to
the y-position to. The arguments can be numeric vertical cursor
positions, or the symbols
:scroll-to operations are used to
accomplish the scrolling.
The scrolling mixins described here require that the window have
tv:basic-scroll-bar as well, because they make use of operations
defined by that flavor. If you do not want to have a scroll bar,
you can specify
nil for the
:scroll-bar init option.
.defflavor tv:flashy-scrolling-mixin This mixin provides the ability to scroll the window a line at a time by pushing the mouse against the top or bottom edge. The mouse blinker changes to a thick up or down arrow when it is in the right place to do this.
This sort of scrolling is provided in the editor and the inspector.
This flavor does not cause the text "more above" to appear,
the way it does in the inspector; that is done by
.definitoption tv:flashy-scrolling-mixin :flashy-scrolling-region spec spec specifies where in the window the regions should go in which the mouse can cause scrolling. It looks like this:
((top-height top-left top-right) (bottom-height bottom-left bottom-right))
Each region always abuts the top or bottom edge of the window,
overlapping the window’s margin, but possibly extending into the inside
of the window.
Each height is a number of pixels in height for the specified
region. Each left and right give the sides of the region.
left and right can be fixnums (positions relative to the window
left edge), flonums (fractions of the width of the window, with zero at
the left), or
:left for the left edge or
:right for the right edge.
This mixin (which requires
tv:margin-region-mixin as well)
provides for mouse-sensitive regions in the top and bottom margins which
say "more below" or "more above" if there is something to scroll
to. A mouse click on the region scrolls an entire windowfull.
.definitoption tv:margin-scroll-mixin :margin-scroll-regions region-list Each element of region-list describes what to do with one of the two scrolling regions. An element looks like
(keyword at-end-message more-message font-specifier)
:bottom, and says which region this
element describes. at-end-message is an expression evaluated to get
the string to display in the region when there is no room for more
scrolling in that direction. If
nil or omitted, it defaults to
"Bottom". more-message is another expression which
is supposed to evaluate to a string to print when there is room for more
"More above" and
"More below" are the defaults.
Most commonly one just uses a string for the at-end-message and the more-message.
font-specifier specifies the font to use. It defaults to
if it is
nil or omitted.
This mixin provides both flashy scrolling and margin scrolling, with the
flashy scrolling areas overlying the margin scrolling regions. You
don’t need anything else except
Here are two ways of controlling when margin scrolling regions appear or disappear:
This mixin, when combined with
tv:margin-scroll-mixin, makes the
margin scroll regions disappear if the
:scroll-bar init option or
:set-scroll-bar operation is used to make the scroll bar
disappear, and reappear if a scroll bar is created again.
This mixin provides a scroll bar, flashy scrolling and margin scrolling,
and makes them appear or disappear according to the value returned by
.defmethod tv:scroll-stuff-on-off-mixin :decide-if-scrolling-necessary
Makes the scroll bar and margin regions appear or disappear if
appropriate, using the
:enable-scrolling-p to decide whether they
should be present. The goal is to avoid displaying scrolling features,
and using up screen space for them, when there is no place to scroll to.
This operation is invoked automatically at certain times. It should be invoked also whenever the number of lines to scroll through has been changed, but before doing any associated redisplay (since the redisplay to be done may be different after this operation finishes).
If the scroll bar and margin regions must be added or removed, then
either the inside size of the outside size of the window must change.
:adjustable-size-p operation is used to decide which. If it
nil, the inside size is preserved and the outside size
is changed; otherwise, the outside size is preserved.
Changing the inside size may affect the window’s redisplay calculations,
and for some windows it may cause a redisplay within this operation.
You may want to invoke it inside of a
to avoid letting the user see gratuitous double redisplays, or to
suppress the redisplay entirely if there is no bit-save-array.
If the outside size is to be changed, and if changing the number of displayable items changes the height of the window, that should be done before invoking this operation. .end_defmethod
.defmethod tv:scroll-stuff-on-off-mixin :adjustable-size-p
This operation is used to decide how to adjust the window margin size.
If it returns non-
nil, the inside size is preserved; otherwise, the
tv:scroll-stuff-on-off-mixin does not define this operation, but
it requires users to define it.
If this is non-
nil, the Roman numeral keys I through III on the
keyboard are treated as mouse clicks when the
Mode-Lock key is
down. The default is
The delay in microseconds after a change in a mouse button status before the system begins to look for another change. The default is 2000. microseconds.
The delay in microseconds after which the system gives up checking for an additional mouse click. The default is .2 seconds.
Clears out the microcode buffer in which the mouse-tracking microcode records mouse clicks.
This is a list of keys (valid arguments for
When the mouse is clicked, each of these keys that is held down
adds one to the "number of clicks". The default value is
(:control :shift :hyper)
Thus, if you do a single click with the
Control key down,
it is treated as a double click.
In previous sections, we have mentioned the distinction between the inside and outside parts of the window. The part of the window that is not the inside part is called the margins. There are four margins, one for each edge. The margins sometimes contain a border, which is a rectangular box drawn around the outside of the window. Borders help the user see what part of the screen is occupied by which window. The margins also sometimes contain a label, which is a text string. Labels help the user see what a window is for.
A label can be inside the borders or outside the borders (usually it is
inside). In general, there can be lots of things in the margins; each
one is called a margin item. Borders and labels are two kinds of
margin items. In any flavor of window, one of the margin items is the
innermost; it is right next to the inside part of the window. Each
successive margin item is outside the previous one; the last one is just
inside the edges of the window. Each margin item is created by a
flavor’s being mixed in. You can control which margin items your window
has by which flavors you mix in, and you can control their order by the
order in which you mix in the flavors. Margin item flavors closer to
the front of the component flavor list are further toward the outside of the
tv:window flavor has as components
tv:label-mixin, in that order, and so the label is inside the
border. The scroll bar, in windows that have one, is also a margin item
.defmetamethod windows :margins Returns four values: the sizes of the left, top, right, and bottom margins, respectively. Each value includes the contributions of borders, labels, and anything else, to that one margin. For a window with no margins, all four values are zero. .end_defmetamethod
.defmetamethod windows :left-margin-size .defmetamethod1 windows :top-margin-size .defmetamethod1 windows :right-margin-size .defmetamethod1 windows :bottom-margin-size Return the size of one of the margins. .end_defmetamethod
.defmetainstvar windows tv:left-margin-size
.defmetainstvar1 windows tv:top-margin-size
.defmetainstvar1 windows tv:right-margin-size
.defmetainstvar1 windows tv:bottom-margin-size
These hold the four values returned by the
There are no operations to set these variables or init options
to initialize them, because the margin sizes are always supposed
to be computed from the labels, borders and other margin items
as described below.
Return the value of the corresponding instance variable of window.
These are accessor defsubsts created by the
:outside-accessible-instance-variables option of
Return the positions of the inside edges, relative to
the top left outside corner of the window.
If used with no argument, these defsubsts expand into
direct references to instance variables, and therefore
may be used only within methods or
(declare (:self-flavor ...)) functions.
tv:borders-mixin margin item creates the borders around windows that you often see
when using the Lisp Machine. You can control the thickness of each of
the four borders separately, or of all of them together. You can also
specify your own function to draw the borders, if you want something
more elaborate than simple lines.
The borders also include some whitespace left between the borders and the inside of the window. The thickness of this white space is called the border margin width. The space is there so that characters and graphics that are up against the edge of the inside of the window, or the next-innermost margin item, do not "merge" with the border. .end_defflavor
.definitoption tv:borders-mixin :borders argument This option initializes the parameters of the borders. argument may have any of the following values:
nilThere are no borders at all.
(left top right bottom)Specifications (see below) for each of the borders at the four edges of the window.
(keyword1 spec1 keyword2 spec2...)Specifications (see below) for the borders at the edges selected by the keywords, which may be among
Each specification for a particular border may be one of the following. It specifies how thick the border is and the function to draw it.
nilThis edge should not have any border.
tThe border at this edge should be drawn by the default function with the default thickness.
(function . thickness)The border at this edge should be drawn by the specified function with the specified thickness.
The default (and currently only) border function is
tv:draw-rectangular-border. Its default width is 1.
To define your own border function, you should create a Lisp function
that takes six arguments: the window on which to draw the label, the "alu
function" (see (aluf)) with which to draw it, and the left,
top, right, and bottom edges of the area that the border should occupy.
The returned value is ignored. The function runs inside a
tv:sheet-force-access (see (tv:sheet-force-access-fun)).
You should place a
tv:default-border-size property on the name of the function, whose
value is the default thickness of the border; it will be used when
a specification is a non-
Note that setting border specifications to ask for a border width of
zero is not the same thing as giving
nil as the argument to this
option, because in the former case the space for the border margin width
(see the previous page)
is allocated, whereas in the latter case it is not.
.defmethod tv:borders-mixin :set-borders new-borders
Redefines the borders. new-borders can be any of the things that can be used
:borders init option (see above).
.definitoption tv:borders-mixin :border-margin-width n-pixels
Sets the width of the white space in the margins between the borders and the inside
of the window. The default is 1. If some edge does not have any border
(the specification for that border was
nil) then that border won’t
have any border margin either, regardless of the value of this option; that
is the difference between border specifications of 0 and
.defmethod tv:borders-mixin :border-margin-width .defmethod1 tv:borders-mixin :set-border-margin-width new-width Return or set the value of the border margin width. .end_defmethod
.definstvar tv:borders-mixin tv:border-margin-width The current border margin width. .end_definstvar
.definstvar tv:borders-mixin tv:borders
A description of the currently specified borders.
nil for no borders. Otherwise its format is complicated
and internal in nature.
.defflavor tv:full-screen-hack-mixin This mixin is included in many system flavors, such as Lisp listeners, Supdup, and Zmacs frames. It offers the user the option of requesting that these windows have no borders when they occupy the full screen. .end_defflavor
With an argument of
t, eliminates the borders of all windows which
are full-screen-sized and have
With an argument of
nil, reinstates the normal borders of all such windows.
tv:label-mixin margin item creates the labels in the corners of
windows that you often see when using the Lisp Machine. You can control
the text of the label, the font in which it is displayed, and whether it
appears at the top of the window or the bottom.
.defmetainitoption windows :name
The value is the name of the window, which should be a symbol. All windows
have names; note that this is an init option of
is mentioned here because the main use of the name is as the default string
for the label, if there is a label (see below).
.defmetamethod windows :name Returns the name of the window, which is a symbol. See above. .end_defmetamethod
.definitoption tv:label-mixin :label specification
Sets the string displayed as the label, the font in which the label is
displayed, and whether the label is at the top or the bottom of the
window. Anything you don’t specify will default; by default, the string
is the same as the name of the window, the font is the screen’s standard
font for the purpose
:label (see (font-purposes)), and the label is
at the bottom of the window.
specification may be any of:
nilThere is no label at all.
tThe label is given all the default characteristics.
:topThe label is put at the top of the window.
:bottomThe label is put at the bottom of the window.
(keyword1 arg1 keyword2 ...)The attributes corresponding to the keywords are set; the rest of the attributes default. Some keywords take arguments and some do not. The following keywords may be given:
.defmethod tv:label-mixin :label-size Returns the width and height of the area occupied by the label. .end_defmethod
.defmethod tv:label-mixin :set-label specification
Changes some attributes of the label. specification can be anything
accepted by the
:label init option. Any attribute that specification
doesn’t mention retains its old value.
.definstvar tv:label-mixin tv:label
The value of this variable describes the label of the window. It is
nil for no label or a list of length eight, whose elements
nilif the label text should be horizontally centered.
.defflavor tv:top-label-mixin Causes the label to appear in the top margin of the window by default instead of at the bottom. The mixin does not override an explicit specification of the label position. .end_defflavor
Makes the label appear to be in a box, by drawing a line just on the
inside of the label. This combines with the window’s borders, which
surround the other three sides of the label, to make a box. The extra
line is present only if the label is turned on. Menus use this mixin,
so from any menu that has a label, such as the one you get from
Screen in the system menu, you can see what it looks like.
.definitoption tv:box-label-mixin :label-box-p t-or-nil
If this option is
nil, the box around the label is inhibited.
.defflavor tv:centered-label-mixin Makes the label string appear by default horizontally centered in the width of the window. .end_defflavor
This flavor adds the
to your window. You send a
:delayed-set-label message to change the
label in such a way that it will not actually be displayed until you
:update-label message. This is especially useful for programs
that suppress redisplay when there is typeahead; the user’s commands may change
the label several times, and you may want to suppress the redisplay of the
changes in the label until there isn’t any typeahead.
.defmethod tv:delayed-redisplay-label-mixin :delayed-set-label specification
This is like the
:set-label method, except that nothing actually happens
:update-label is done.
.defmethod tv:delayed-redisplay-label-mixin :update-label
Actually does the
:set-label operation on the specification given
by the most recent
.definstvar tv:delayed-redisplay-label-mixin tv:label-needs-updating
nil if a
:delayed-set-label has been done but not displayed yet.
Margin regions are a general facility for allocating space in a window’s margin for specific purposes. Each region can display text or graphics and can be mouse sensitive. Margin choices (see (margin-choice)) are implemented using margin regions.
.defflavor tv:margin-region-mixin This mixin gives a window the ability to have margin regions. .end_defflavor
.definstvar tv:margin-region-mixin tv:region-list A list of margin region descriptors. Each descriptor specifies one margin region and is a list of this form:
(function margin size left top right bottom)
The list may be longer than seven. The meaning of the extra elements is up to you. Here is what the seven standard elements mean. We list the names of the defsubsts provided to access them.
defselect. The margin region descriptor itself is always one of the arguments, to identify the region being operated on.
You do not specify these; they are computed by the
operation which divides up the margin space, and recorded here
so that the margin region can be displayed and found by the mouse.
The margin region descriptor may be longer than seven.
Additional elements are not used by
and therefore may be used by higher-level facilities to record
their own information with each margin region.
.defmethod tv:margin-region-mixin :set-region-list new-region-list Sets the list of margin regions. The new list should be a list of margin region descriptors as described above, but only the first three elements of each descriptor need be filled in. The rest will be set up automatically. .end_defmethod
These are the operations that the function of a margin region is expected to handle:
:mouse-enters-regionoperation, when the mouse moves into a region. x and y are the new mouse position, relative to the outside of the window.
:mouse-clickwindow operation ((windows-mouse-click-method)).
Returns the four edges of the rectangle allocated to descriptor’s margin region, all relative to the window’s outside upper left corner. This may only be used inside of methods of the window whose margin region is being operated on.
This is a simplification of the function used to handle the margin
regions made by
tv:margin-scroll-mixin. These regions display
strings such as
"More above" and respond to a mouse click by scrolling a
full page. The margin regions used
have additional nonstandard elements beyond the seventh:
nilif there is more text to scroll to past this edge.
(declare-flavor-instance-variables (tv:margin-scroll-mixin) (defselect margin-scroll-region (:refresh (region &optional old-valid &aux more-p left top right bottom) (multiple-value (left top right bottom) (tv:margin-region-area region)) ;; Is there anything more to scroll to past this edge? (setq more-p (send self (if (eq (tv:margin-region-margin region) ':top) ':scroll-more-above ':scroll-more-below))) ;; Redisplay string in the region unless already right. (when (or (not old-valid) (neq more-p (margin-scroll-region-more-p region))) (setf (margin-scroll-region-more-p region) more-p) (tv:sheet-force-access (self) ;; Erase the region. Sheet has just been prepared. (tv:%draw-rectangle (- right left) (- bottom top) left top tv:erase-aluf self) ;; Print the string. (send self ':string-out-centered-explicit (if more-p (margin-scroll-region-more-msg region) (margin-scroll-region-empty-msg region)) left top right nil (margin-scroll-region-msg-font region) tv:char-aluf 0 nil nil))))
((:mouse-enters-region :mouse-leaves-region :mouse-moves) (&rest ignore)) (:mouse-click (ignore ignore region ignore) (if (margin-scroll-region-more-p region) (let ((from (tv:margin-region-margin region))) (send self ':scroll-relative from (if (eq from ':top) ':bottom ':top))) (beep))) (:who-line-documentation-string (ignore) "Any button to scroll one page.")))
Let us assume that you want to define a thing called a mumble that
goes in a window’s margins, the way labels and borders do. You
create a flavor
mumble-margin-mixin that implements the feature.
This flavor should have certain instance variables,
which will be used only by the methods of
so their precise format is up to you.
It is recommended to use a list of four values: the left, top, right and bottom edges of the rectangle, all relative to the upper left outside corner of the window.
Some margin mixins have just a single variable whose value is a list containing both the contents and the position of the margin item.
(defflavor mumble-margin-mixin ((current-mumbles nil) mumble-margin-area) () (:required-flavors tv:minimum-window) (:inittable-instance-variables current-mumbles)) (defmethod (mumble-margin-mixin :before :init) (ignore) (setq current-mumbles (canonicalize-and-validate-mumble-spec current-mumbles)))
Now you must at the minimum create methods for two standard operations
for margin computation and display, to interface
to the rest of the system. These operations are
.defmetamethod windows :compute-margins lm tm rm bm
:compute-margins is used by the system to find out how much space is
needed in each margin of the window by borders, labels, and anything
else. Each flavor that implements a kind of margin item must define a
method for it. This operation uses
:pass-on method combination, so
that the values from one method become the arguments to the next. These
arguments are interpreted as the amount of space allocated so far in
each margin. Each method increments one or more of them by the amount
of space needed by that mixin.
.defmetamethod windows :refresh-margins Redraws all the contents of the window’s margins. Each flavor of margin item must add a daemon method to this operation. The method may assume that its own margin area is completely erased to begin with. .end_defmetamethod
(defmethod (mumble-margin-mixin :compute-margins) (lm tm rm bm) (let ((wid (mumble-margin-width current-mumbles))) (setq mumble-margin-area (list lm tm (+ lm wid) (- tv:height bm))) (values (+ lm wid) tm rm bm)))
Here we assume that the mumbles always go in the left margin. So it is
always the left margin’s width that is incremented, and the others are
returned just as they were passed. We also assume that
mumble-margin-width is a function you have defined that computes the
width of space that the mumbles need.
In addition to returning modified versions of its arguments, the method
also sets up the value of
mumble-margin-area. This is the only
place it is necessary to set that variable. By recording the position
of each margin item this way, we take into account how one margin item
affects the position of the others. For example, the mumbles might come
inside the borders, and then the lm, tm, rm and bm
values will already contain the width of the borders. Then
margin-mumble-area will describe a rectangle that is within the
Usually an additional mixin-specific operation is introduced into this method, as follows:
(defmethod (mumble-margin-mixin :compute-margins) (lm tm rm bm) (send self ':recalculate-mumble-margins lm tm rm bm)) (defmethod (mumble-margin-mixin :recalculate-mumble-margins) (lm tm rm bm) (let ((wid (mumble-margin-width current-mumbles))) (setq mumble-margin-area (list lm tm (+ lm wid) (- tv:height bm))) (values (+ lm wid) tm rm bm)))
This way, other mixins can be defined to modify where the mumbles go
by replacing the
The one other thing you must do is provide a method for
:refresh-margins, to draw the mumbles in the rectangle recorded:
You can assume that that rectangle is clear to start with.
(defmethod (mumble-margin-mixin :after :refresh-margins) () (tv:sheet-force-access (self) (draw-mumbles current-mumbles mumble-margin-area)))
You may wish to provide the user with an operation to change the
window’s mumbles. This operation should use the
.defmetamethod windows :redefine-margins
This operation recomputes how much margin space is needed for all of the
margin items, by invoking the
:compute-margins operation, and then
actually changes the window margin sizes if necessary.
If the margin sizes have changed, then the window is erased and
:refresh-margins is done; the instance variable
tv:restored-bits-p (present in all windows) is left set to
If the margin sizes have not changed, no output whatever is done, and
tv:restored-bits-p is left set to
t. All this is done
Here is an example of how to use it:
(defmethod (mumble-margin-mixin :set-mumbles) (new-mumbles) (setq current-mumbles (canonicalize-and-validate-mumble-spec new-mumbles)) (send self ':redefine-margins) (when tv:restored-bits-p (tv:sheet-force-access (self) (erase-mumble-area mumble-margin-area) (draw-mumbles current-mumbles mumble-margin-area))))
The explicit erasure and drawing of the mumbles is done in the case where the total sizes of the margins have not changed (and therefore no screen updating has been done), in case the contents of the mumbles have changed.
A frame is a window that is divided into sub-windows, using the hierarchical structure of the window system (discussed in (window-hierarchy)). The sub-windows are called panes. The panes are the inferiors of the frame, and the frame is the superior of each pane. Several heavily-used systems programs use frames. For example, inspector windows are frames. The default inspector window has six panes: the interaction pane on top, the history pane and command menu pane below it, and three inspect panes below that. The window debugger and Zmacs also use frames. In Zmacs, each new editor window is a pane of the Zmacs Frame. ZMail uses several different frames, even frames within other frames.
From these examples, you can see some of the things that frames are good for. In general, by using a frame as a user interface to an interactive subsystem, you get a convenient way to put many different things on the screen, each in its own place. Generally you can split up the frame into areas in which you can display text or graphics, areas where you can put menus or other mouse-sensitive input areas, and areas to interact with, in which keyboard input is echoed or otherwise acknowledged.
It is usually best for a frame and its panes to be treated as a unit by
the system menu
Select menu and by the
keys. The mixins
((previously-selected-windows-section)), respectively, in the frame’s
flavor bring this about. Then selection of panes within the frame is
done by making the chosen pane the selection substitute of the frame
((selection-substitutes)). The program managing the frame can maintain
a "selected pane within the frame" this way, while letting the user decide
when to select the frame as a whole.
It is also common for all of the panes to use the same input buffer so that the program can always do its input in the same fashion and collect keyboard and mouse input from all the panes. See (shared-input-buffers).
It is also possible to have frames with less coupling between their
panes. For example, the frame you get from requesting a frame in the
Split Screen option does make its panes share an input buffer,
and allows them to be individually represented in the
System commands. It also lets the panes
be selected in their own right and not as substitutes for the frame.
This is done because typically each window in the split screen frame
is managed by its own process.
One kind of frame is the constraint frame, which adjusts the shapes of its panes automatically as its own shape is changed. These frames are described first since they are a ready-to-use facility. More basic frame flavors can be built upon to create frames which manage their panes’ exposure and shapes in other ways. The editor, for example, does this.
All frame flavors are built on this one.
(see (tv:frame-forwarding-mixin-flavor)) mixed with this provides
a non-constraint frame to which you need only add code to decide when
to expose the panes and how big to make them.
tv:basic-frame is nearly the same as
tv:minimum-window; it does
not have all the mixins that go into the
tv:window flavor. In
particular, it does not provide for borders or a label, and it cannot be
the selected window. It also has
(tv:delay-notification-mixin-flavor)) as a component.
If you use
Edit Screen to change the shape of an inspector or
debugger window frame, the shapes of the panes are all changed so that
the proportions come out looking as they are supposed to. If you play
Edit Screen enough, you can even see the menus reformat
themselves (changing their numbers of rows and columns) in order to keep
all of their items visible. The way all this works is that the
positions and shapes of the panes, instead of being explicitly specified
in units of pixels, are specified symbolically. When the window changes
shape, the symbolic description is elaborated again in light of the new
shape, and the panes are reshaped appropriately.
This set of symbolic descriptions is called a set of constraints.
When you make a constraint frame, you specify the configuration of panes
within the frame by creating list structure to represent the layout.
The format of this list structure is called the constraint language. It
lets you say things like "give this pane one third of the remaining
room, then give that pane 17 pixels, and then divide what remains
between these two panes, evenly." The constraint language is fairly
complex, and is described in full detail later. In general, a frame can
have many different configurations. Each configuration is described in
the constraint language, and each specifies one way of splitting up the
frame. While the program is running, it can switch a frame from one
configuration to another. Some panes may appear in more than one
configuration, but other panes may be left out of one configuration, and
may only be visible when the frame is switched to another configuration.
For example, in ZMail, when you click on
Profile, the frame
changes to a new configuration whose panes include a profile editor window
and another frame, the profile button frame.
The processing of constraints is actually implemented by a frame mixin
The flavor of the frame itself might be any of several flavors. The
simplest thing for it to be is
This flavor is the basic kind of constraint frame. The rest of this
section describes its behavior in detail. This flavor, like
tv:basic-frame, does not provide for borders, a label, or for being
This flavor is just
(see (tv:borders-mixin-flavor)) mixed in at the right place. It will
have a border around the edge. By default (using the
:default-init-plist option of the flavor system), the
:border-margin-width is zero, so the panes at the edges of the frame
are right next to the border itself.
Bordered constraint frames are used most often. Usually, each of the panes has borders, and the frame does too. A reason for this is that when two of the panes are right next to each other, which they usually are, their borders are side by side, and so look like a double-thick line. In order to make the edges of the panes that are at the edge of the frame (rather than up against another pane) look like they are the same thickness, the frame has a border itself.
A convenient way to make all the panes of a constraint frame use the same input buffer is to use one of the following flavors:
This is like
tv:constraint-frame, but all the panes
of the frame share the same input buffer used by the frame itself.
This is just like
that it has
tv:borders-mixin mixed into it at the right place, so
that the frame has a border around it.
.definitoption tv:constraint-frame-with-shared-io-buffer :io-buffer io-buffer If this option is present, io-buffer is used as the input buffer for the frame and the panes. Otherwise, a default input buffer is created. (See (io-buffer) for a discussion of I/O buffers.) .end_definitoption
The full description of how to use constraint frames, including the full constraint language, is rather complicated. The complete specifications are given in the next section; this section gives some common examples, in order to show the general idea of how the specifications work.
The following form creates a constraint frame with two panes, one on top of the other, each of which takes up half of the frame.
(make-instance 'tv:constraint-frame ':panes '((top-pane tv:window) (bottom-pane tv:window)) ':constraints '((main . ((top-pane bottom-pane) ((top-pane 0.5)) ((bottom-pane :even))))))
Two initialization options were given to the
:panes option and the
:constraints option. The
meaning of the
:panes specification is: "This frame is made of the
following panes. Call the first one
top-pane; its flavor is
tv:window. Call the second one
bottom-pane; its flavor is
tv:window". The meaning of the
:constraints specification is:
"There is just one configuration defined for this pane; call it
main. In this configuration, the panes that appear are, in order
from top to bottom,
should use up
0.5 of the room.
bottom-pane should use up all the
rest of the room."
This example demonstrates some more features:
(make-instance 'tv:bordered-constraint-frame ':panes '((graphics-pane tv:window :label nil :blinker-p nil) (message-pane tv:window :label "Message Pane" :blinker-p nil) (interaction-pane tv:window)) ':constraints '((main . ((interaction-pane graphics-pane message-pane) ((message-pane 4 :lines)) ((graphics-pane 400)) ((interaction-pane :even))))))
This frame has a border around the edges (because of the flavor of the
frame itself), and it has three panes. The panes are given some
initialization options themselves. The topmost pane is
graphics-pane is in the middle, and
message-pane is on the bottom.
message-pane is four lines high,
400 pixels high, and
up all remaining space.
Here is a window that has two possible configurations. In the first one, there are three little windows across the top of the frame and a big window beneath them; in the second one, the same big window is at the top of the frame, and underneath it is a strip split between a menu and another window.
(make-instance 'tv:bordered-constraint-frame ':panes '((huey tv:window) (dewey tv:window) (louie tv:window) (main-pane tv:window) (random-pane tv:window) (menu tv:command-menu :item-list ("Foo" "Bar" "Baz"))) ':constraints '((first-config . ((top-strip main-pane) ((top-strip :horizontal (.3) (huey dewey louie) ((huey :even) (dewey :even) (louie :even)))) ((main-pane :even)))) (second-config . ((main-pane bottom-strip) ((bottom-strip :horizontal (.2) (random-pane menu) ((menu :ask :pane-size)) ((random-pane :even)))) ((main-pane :even))))))
In this example, the frame has two different configurations. When the
frame is first created, it is in the first of the configurations
first-config. In this configuration, the top
three-tenths of the frame are split equally, horizontally, between three
windows, and the rest of the frame is occupied by
frame can be switched to a new configuration using the
:set-configuration message (see
(tv:basic-constraint-frame-set-configuration-method)). If we switch it
main-frame will appear on top of a strip
one-fifth of the height of the window. This strip will contain a menu
on the right that is just wide enough to display the strings in the
menu’s item list, and another pane using up the rest of the strip. When
the configuration of the window is switched,
main-pane must be
Another thing to notice is that the list of items in the menu was
present in the
:panes option, rather than a form to be evaluated.
If the list had been in a variable, it would have been necessary to write
:panes option using backquote, like this:
':panes `((huey tv:window) (dewey tv:window) (louie tv:window) (main-pane tv:window) (random-pane tv:window) (menu tv:command-menu :item-list ,the-list-of-items))
Menus and how to use them are explained later; see (menu).
In this example, the window is divided into two windows, side by side.
(make-instance 'tv:bordered-constraint-frame ':edges '(100 100 600 600) ':panes '((left tv:window) (right tv:window)) ':constraints '((main . ((whole-thing) ((whole-thing :horizontal (:even) (left right) ((left :even) (right :even))))))))
This example also points out that constraint frames are windows too, and
you can use init-options acceptable to
tv:minimum-window with them.
In this case, we give the edges of the frame as a whole, in absolute
numbers. Remember that frames are not built out of
In actual practice, panes are usually made out of more interesting
This section gives the complete rules for specifying the panes of a constraint frame, and for the constraint language. It should help explain any of the above examples that were unclear, and tell you all the things you can do with the constraint language.
When you create a constraint frame, you must supply two initialization
:panes option specifies what panes you want the frame
to have, and the
:constraints option specifies the set of
constraints for each of the configurations that the window may assume.
For the purposes of these two options, windows are given internal names,
which are Lisp symbols, used only by the flavors and methods that deal
with constraint frames. These names are not used as the actual names of
the windows (as in the
:name message (see
.definitoption tv:constraint-frame :panes pane-descriptions This initialization option is required for all flavors of constraint frames. The argument, pane-descriptions, is a list of pane descriptions. Every pane description looks like this:
(name flavor . options)
name is the internal name (a symbol). flavor is the flavor of which the pane should be an instance. options is a list to be appended to the initialization plist for the pane when it is created. When the frame is first created, it will create all of its panes, using flavor and options. The frame will add some of its own options to control the position and shape of the window; you should not pass any such options in the options list. .end_definitoption
.definitoption tv:constraint-frame :constraints configuration-description-list This initialization option is required for all flavors of constraint frames. The argument, configuration-description-list, is a list of configuration descriptions. The format of configuration descriptions is explained below. .end_definitoption
Both init options work by initializing instance variables which are
then looked at by the
:init methods of constraint frames. Instead
of using the init options, you can set the instance variables yourself
.definstvar tv:constraint-frame tv:panes .definstvar1 tv:constraint-frame tv:constraints The instance variables in which the constraint frame mechanism looks to find the lists of panes and constraints. .end_definstvar
A configuration-description-list is a list of configuration-descriptions. There is one configuration-description in the list for each of the possible configurations that the frame can assume. Each configuration is named by a symbol, called the configuration-name. A configuration-description-list is an alist that associates the configuration-descriptions with the names. It looks like this:
((configuration-name-1 . configuration-description-1) (configuration-name-2 . configuration-description-2) ...)
Each configuration-description describes the layout of the panes in a single configuration. The description has two parts. The first part specifies the order in which the windows appear, and the second part specifies how the sizes are computed. Actually, in addition to windows, there can also be dummies in the configuration-descriptor. A dummy is used either to hold empty space that is not used by any window, or it can reserve a region of space to be divided up by another configuration-description.
A configuration-description splits up one of the dimensions of a
rectangular area into many parts. Such an area is called a section.
Which of the two dimensions is being split up is determined by the
stacking. If the stacking is
:vertical then the section is
being split up vertically; that is, the parts are stacked on top of each
other. If the stacking is
:horizontal then the section is being
split up horizontally; that is, the parts are side-by-side. The
stacking of the top-level configuration-descriptions in the
:constraints option is always
:vertical, but there can be more
configuration-descriptions nested inside of them, and these can have
Each part has a name, represented as a symbol. A part may hold either an actual pane, or other things; in the latter case, it is called a dummy part. Dummy parts can be further subdivided into more panes and dummies using another constraint-description, or their pixels can be blank or filled with some pattern.
A configuration-description looks like this:
(ordering . description-groups)
ordering is a list of names of panes and of dummies, each represented by a symbol; the order of this list is the order that the panes and dummies appear in the space being split up by the configuration-description. For vertical stacking the list goes top to bottom. For horizontal stacking the list goes left to right. A description-group is a list of descriptions. Each description describes either exactly one pane or one dummy. A configuration-description must have one description for each element of the ordering list.
All of the descriptions in a description-group are processed together ("in parallel"); each of the description-groups is processed in turn, starting with the first one. By grouping the descriptions this way, you can control which constraints are elaborated together and which are elaborated at different times; when two constraints are elaborated at different times you can control which one is elaborated first. The reason that the ordering-list in the configuration-description is separate from the description-groups is so that the order in which the panes and dummies appear in the frame can be independent of the order in which their constraints are elaborated.
Each description describes one pane or one dummy. We’ll get back to dummies later. A description that describes a pane looks like this:
(pane-name . constraint)
pane-name is the name of the pane being described; constraint is the constraint that describes the pane. We will return later to what descriptions of dummies look like. The constraint will be elaborated, and will yield a size in pixels; this size will be used for the width or height being computed.
Finally we get to constraints themselves. The basic form of a constraint is as follows:
(key arg-1 arg-2 ...)
key may be a fixnum, a flonum, or one of various keyword symbols. Each type of constraint may take arguments, whose meaning depends on which kind of constraint this argument is passed to.
While descriptions of panes do not have the same format as descriptions of dummies, the same kind of constraints are used in both of them. So all the formats given below may be used inside the descriptions of either panes or dummies.
Any constraint may, optionally, be preceded by a
:limit clause. If
a constraint has a
:limit clause, the constraint looks like:
(:limit limit-specification key arg-1 arg-2 ...)
:limit clause lets you set a minimum and a maximum value that
will be applied to the size computed by the constraint. If the
constraint returns a value smaller than the minimum, then the minimum
value will be used; if it returns a value larger than the maximum, then
the maximum value will be used. The limit-specification is normally
a two-element list, whose elements are fixnums giving the minimum and
maximum values in pixels. If the list has a third element, it should be
one of the symbols
:characters, and it means that the
fixnums are in units of lines or characters, computed by multiplying by
the line-height or char-width of the pane (see (char-width-and-line-height)). If
there is a fourth element, it should be the name of a pane, and that
pane’s line-height or char-width is used instead of that of the pane
being constrained. (If this constraint applies to a dummy instead of a pane, and
the third element of the list is present, then the fourth must be
present as well, since dummies do not have their own line-height nor
The following Lisp objects may be used as values of key in a
constraint. Note: the
constraints are rarely used and you probably don’t need to worry about
them. The other kinds are used frequently.
:linesor the symbol
:characters, meaning that the fixnum is in units of lines or characters, and should be computed by multiplying by the line-height or char-width of the window. If a second argument is also present, it should be the name of a pane, and that pane’s line-height or char-width is used instead of that of the pane being constrained. (If this constraint applies to a dummy instead of a pane, and the first argument is given, then the second must be present as well, since dummies do not have their own line-height nor char-width.)
:characters, and it means to round down the size of the pane to the nearest multiple of the pane’s line-height or char-width. A second argument may be given; it is just like the second argument when key is a fixnum (see above).
The distinction between descriptors in the same group and descriptors in different groups is important when you use this kind of constraint. If you have one descriptor group with two descriptors, each of which requests .2 of the remaining space, then both panes will get the same amount of space. However, if you have the same two descriptors but put them in successive descriptor groups, then the first one will get .2 of the remaining space, and then the second one will get .2 of what remains after the first one was allocated; thus the second pane will be smaller than the first. In other words, the amount of space remaining is recomputed at the end of each descriptor group, but not at the end of each descriptor.
:even, then all of the descriptors in the group must use
:even. The meaning is that all of the panes in the last descriptor group evenly divide all of the remaining space.
It is usually a good idea to use
:even for at least one pane in every configuration, so
that the entire frame will be taken up by panes that all fit together
and extend to the borders of the frame.
:even is careful to choose
exactly the right number of pixels to fill the frame completely,
avoiding roundoff errors that might cause an unsightly line of one or a
few extra pixels somewhere.
Remember that just because the
:evens must be in the last descriptor
group does not mean that the panes that they apply to must be at the bottom
or right-hand end
of the frame! The ordering of the panes in the frame is controlled by the
ordering list, not by the order in which the descriptors appear.
:askis as follows:
(:ask operation arg-1 arg-2 ...)
A message with operation operation and arguments composed of some extra arguments passed by the constraint mechanism followed by arg-1, arg-2, etc. is sent to the pane; its answer says how much space the pane should take up. Note that arg-1, etc., are not forms: they are the values of the arguments themselves (i.e. they are not evaluated; if you want to compute them, you must build the constraint language description at run-time. This is usually written using a backquoted list).
The arguments that are actually sent along with the message are the same
as the arguments passed when you use the
:funcall option except that
the constraint-node is not passed; see below.
Various different flavors of windows accept some messages suitable for
:ask. By convention, several kinds of windows, such as
menus, accept a message called
:pane-size. For example, the
:pane-size method for menus figures out how much space in the
dimension controlled by the
:ask constraint is needed to display all
the items of the menu, given the amount of space available in the other
dimension. No arguments are specified in the constraint. Other
useful operations, handled by all windows, are
with no additional arguments), which makes the window take up enough room to be
square including its borders, and
makes the window be square inside its borders.
:ask. Its format is:
(:ask-window pane-name message-name arg-1 arg-2 ...)
It works like
:ask except that the message is sent to the pane named
pane-name instead of the pane being described. This is primarily
used for dummies, when the size of a dummy wants to be controlled by
the needs of a pane inside it.
(:funcall function arg-1 arg-2 ...)
The specified function is called. It is first passed six arguments from inside the workings of constraint frames, and then the arg-1, arg-2, etc. values. The six arguments are:
:horizontal, depending on the current stacking.
:funcall, but instead of providing a function and arguments, you provide a form. The format is:
The six special values that are passed as arguments when the
:funcall constraint-type is used can be accessed by form as the
values of the following special variables:
tv:**constraint-node** tv:**constraint-remaining-width** tv:**constraint-remaining-height** tv:**constraint-total-width** tv:**constraint-total-height** tv:**constraint-stacking**
This finishes the discussion of descriptions of panes. Descriptions of dummies are different; they may be in any of several formats, identified by the following keywords:
(dummy-name :blank pattern . constraint)
The constraint is used to figure out the size of the part of the section, in the usual way. pattern may be any of the following:
bitbltfunction (see (bitblt-fun)).
:vertical, it will be split up with vertical stacking, and if you use
:horizontal, it will be split up with horizontal stacking. [Currently, you are required to use the opposite kind of stacking from the kind currently happening; that is, successive levels of configuration-description must use alternating kinds of stacking. This restriction may be lifted in the future.] The format is as follows:
(dummy-name :horizontal constraint . configuration-description) or (dummy-name :vertical constraint . configuration-description)
constraint, as usual, specifies the size of this part; it can be in any of the formats given above. Note that in this format, constraint appears as an element of a list rather than as the tail of a list, and so the printed representation of the list will include a pair of parentheses around the constraint. configuration-description tells how this part is subdivided into parts of its own.
.defmetamethod windows :pane-size remaining-width remaining-height total-width total-height stacking
This operation is invoked by constraints of the form
It should return the size in pixels to give the pane, in the current stacking direction.
The meanings of the arguments as they will be passed by the constraint manager
are described above under the
:funcall constraint (see (:ask-arguments)).
.defmetamethod windows :square-pane-size remaining-width remaining-height total-width total-height stacking
.defmetamethod1 windows :square-pane-inside-size remaining-width remaining-height total-width total-height stacking
These operations are invoked by constraints of the form
(:ask :square-pane-inside-size). They
return the size required to make the pane square. For horizontal
stacking, they returns a width equal to the specified height; for
vertical stacking, they returns a height equal to the available width.
The difference between the two operations is that
the outside size of the window square, whereas
the inside of the window (not including the borders) square.
.defmethod tv:basic-constraint-frame :get-pane pane-name
Returns the pane (the inferior window itself) that was named by the
symbol pane-name in the
:panes specification of this frame.
.defmethod tv:basic-constraint-frame :pane-name pane
Returns the symbol that was used to name pane in the
specification of this frame. If pane is not one of the panes,
.defmethod tv:basic-constraint-frame :create-pane name flavor &rest options Creates and returns a window, to serve as a pane of this frame, made from flavor flavor and init options options. name is the pane name to be used. By default, it is not used here.
The panes of the frame are created from their specification using this operation, the arguments being taken from the elements of the specification. It may be useful to redefine this operation. .end_defmethod
.defmethod tv:basic-constraint-frame :send-pane pane-name message &rest arguments
Sends the specified message with the specified arguments to
the pane that was named by the
symbol pane-name in the
:panes specification of this frame.
.defmethod tv:basic-constraint-frame :send-all-panes message &rest arguments Sends the specified message with the specified arguments to all of the panes of this frame, including the non-exposed ones. .end_defmethod
.defmethod tv:basic-constraint-frame :send-all-exposed-panes message &rest arguments Sends the specified message with the specified arguments to all of the exposed panes of this frame. .end_defmethod
.definitoption tv:basic-constraint-frame :configuration configuration-name Makes the initial configuration of the frame be the one named by the symbol configuration-name. .end_definitoption
.defmethod tv:basic-constraint-frame :configuration Returns the symbol naming the current configuration of the frame. .end_defmethod
.defmethod tv:basic-constraint-frame :set-configuration configuration-name Sets the configuration of the frame to the one named by the symbol configuration-name. .end_defmethod
.defmethod tv:basic-constraint-frame :get-configuration configuration-name Returns the internal ("parsed") data structure that describes what is specified for configuration configuration-name. This describes which windows are supposed to be included, and the constraints for them. .end_defmethod
.defmethod tv:basic-constraint-frame :redefine-configuration config-name new-config &optional (parsed-p
Redefines the meaning of configuration config-name according to
new-config. If parsed-p is
t, new-config is expected to
be in parsed form, such as the value returned by the
:get-configuration operation. If parsed-p is
new-config is treated as a configuration-description such as you
would use to define the configuration when initially specifying the
constraints of the frame (see (configuration-description)).
Several fundamental window operations actually ask the window’s superior
what to do. This has no effect for a top-level window but becomes
important when the window’s superior is a frame. The superior can
decide whether the operations should actually go ahead as requested.
These operations are
:set-edges. Here is how they are handled:
:inferior-select. The pane itself is passed as the argument.
If the message sent to the superior returns non-
nil, the operation
is performed on the pane as usual. Otherwise, it is skipped.
:inferior-set-edgesmessage is sent to the superior, its arguments being the pane followed by the arguments of the
:set-edgesmessage. If the operation’s first value is non-
nil, the pane’s edges are changed as requested. Otherwise, the pane’s edges are not changed, and the remaining values from the
:inferior-set-edgesoperation are returned from the
Of course, the frame can change the pane’s edges in some other way and
tv:basic-frame defines only the
:inferior-select operation to
do anything nontrivial; it makes the pane be the frame’s selection
substitute and then sends a
:select to the frame. The others
operations do nothing but return non-
nil. Thus, there is minimal
interaction between the frame and its inferiors.
:inferior-bury so that the frame and
panes are all exposed together.
methods for a frame that normally cause
:bury operations on panes to expose,
deexpose or bury the frame rather than the pane.
:inferior-set-edges method is also defined, for internal reasons
only. Its purpose is to avoid a user-visible change in behavior rather
than to provide one.
This flavor is part of
tv:constraint-frame and the other standard
instantiable flavors of constraint frame.
tv:basic-frame has an instance variable
is used to distinguish between
:expose, etc. operations sent by the
frame’s code to its panes, and those sent by other programs.
When an outside program sends a
message to one of the panes, the
:inferior-expose, etc. operation
on the frame simply exposes, deexpose or buries the frame itself,
and does not allow the operation on the pane to proceed.
When the frame’s code itself exposes a pane, it does so with
tv:recursion temporarily non-
nil so that when the
:inferior-expose is done it will return
t and let the pane be exposed.
.defmetamethod frames :pane-types-alist
This should return a menu item list to be used for the handling of the
Create item in the screen editor, when editing the panes of this frame.
The value of the menu item should be a flavor of window to create, or a
list to be evaluated to return a flavor.
The menu item’s value (or the result of evaluating it) can also be
t, which directs the screen editor to read a flavor name from the
A frames is normally operated with one of its inferiors as a selection
substitute. The selection substitute of a frame is also called the
"selected pane", as this feature used to be available only in frames.
Unless you mix
tv:select-mixin into your frame flavor, the frame
itself cannot be the selected window. Therefore, it is important to
provide a selection substitute when the frame is created. This can be
done by doing
:set-selection-substitute in an
(defmethod (my-frame :after :init) (ignore) (send self ':set-selection-substitute (send self ':get-pane 'interaction-pane)))
Explicitly selecting a pane with the
:select operation actually
works by setting the frame’s selection substitute, by means of the
forwarding mechanism described above.
In a constraint frame, or any other frame which has
you should not attempt to select a pane which is not already exposed,
because of the effects of forwarding on the
.definitoption tv:basic-constraint-frame :selected-pane pane-name In a constraint frame, you can initialize the selected pane with this handy init option. Instead of fishing out the pane, just give its name. .end_definitoption
.defmethod tv:basic-frame :select-pane inferior-window
This is another, older name for the
before it was generalized to apply to windows other than frames.
.defmethod tv:basic-frame :selected-pane
This is another, older name for the
before it was generalized to apply to windows other than frames.
.defflavor tv:interaction-pane (
This flavor is often useful for a pane for reading and echoing
multi-character commands in a system which uses a frame. This pane
would typically be the selected pane.
Notifications are asynchronous messages that come from something other
than the selected window. For example, when an interactive message from
another user comes in (which was sent with the
qsend function), it
is printed as a notification. You may have noticed that sometimes a
notification is printed out immediately, while sometimes all that happens
is a message in the who line. The selected window is responsible for
deciding what to do with the notification.
Make a notification. format-string and format-args are passed
format to print the text of the notification. Where this text is
printed, and how, is under the control of the selected window, as
window-of-interest is a window that should be selected if the user
clicks the mouse on the notification window (if the notification happens
to use its own window). For example, a notification about a message
from another user will supply the Converse window as this argument.
This window can also be selected with the
Terminal 0 S command.
tv:careful-notify is different in that if careful-p is
nil and the notification cannot be printed now because of
windows being locked, it returns immediately. The value is non-
if the notification was printed successfully.
.defmetamethod windows :print-notification time string window-of-interest The system invokes this operation on the selected window to ask it to make a notification. time will be a time to mention in the notification. string is the text to print. window-of-interest should be set up for the user to select in some convenient fashion, if possible. .end_defmetamethod
.defflavor tv:notification-mixin This mixin causes a window to handle notifications which happen while it is selected by printing them out on the window itself, if the window is big enough. Lisp listeners and typeout windows of all sorts use this mixin. .end_defflavor
.defmethod tv:notification-mixin :print-notification-on-self time string window-of-interest
This operation does the actual work of printing a notification on the
window itself, once it has been decided definitely to do so.
It is sometimes useful for window flavors incorporating
tv:notification-mixin to redefine this.
tv:delay-notification-mixin implements the default way of handling
notifications: to make them wait. It is a component of
and also of anything that contains
tv:notification-mixin works by overriding it.
If a notification arrives while a window of this sort is selected, it is
put on a list called
tv:pending-notifications. All that happens
immediately is a beep. But the presence of a non-
nil value for this
variable causes the mouse documentation line to display a message that
there are notifications waiting, with blinking asterisks at each end of
As soon as a window that can print the notifications is selected,
they will be printed. For example, selecting a Lisp listener will do it.
If you are in the editor, selecting the typeout window by typing
Break will do it. There is also a command,
which selects a window that just prints the notifications.
Terminal 2 N can be used to make the mouse
documentation line go back to its normal function. This works by
transferring everything on
tv:pending-notifications onto another
tv:deferred-notifications. These deferred notifications will
still be printed if you switch to a suitable window.
Another way a window can handle a notification is to ask some other
window to do so. For example, editor windows (
ask the containing Zmacs frame to do the job, and it in turn asks
the echo area window to do it. This window displays the notification
itself if the notification fits.
Returns a process that has got an error and is waiting, having made a
notification, for a window to be selected so the debugger can be run.
If no such process is waiting, returns
nil. If there are several
such processes, the most recent one to make its notification is
returned. The window the process is waiting for selection of is
returned as the second value.
tv:find-process-in-error but asks the user about each
candidate process. When the user answers
Y, that process is
returned. If the user answers
N to each candidate, the value is
nil. The window the process is waiting for selection of is returned
as the second value.
standard-output all the notifications that have happened
in this session.
.defmetamethod windows :notice event
:notice operation is used to report certain events
so that flavors can redefine what to do when they happen.
The argument to
:notice is an event name, a keyword.
Additional arguments are allowed but have no meaning
for any of the events yet defined. Here are the defined events:
:notify. The default action is to make a notification and wait.
:notice operation uses
:or method combination: all the
methods are run until one returns non-
nil. Aside from that, the
value returned is not meaningful.
.defflavor tv:lisp-listener This flavor of window is used for the window initially selected when the system starts up, and for windows created when you ask to create a "Lisp" window with any of the system menu commands. .end_defflavor
The Lisp listener window that is selected when you boot.
Returns a Lisp listener which is waiting for input at top level,
and is the full size of the specified screen. The screen defaults to
This flavor of window works just like a Lisp listener, but
will not select this kind of window, nor will
The mixin primarily responsible for making a Lisp listener behave the
way it does is
tv:process-mixin, and arranges by default for the process to be
initialized to run the Lisp top level read-eval-print loop
.defmethod tv:listener-mixin-internal :package
.defmethod1 tv:listener-mixin-internal :set-package package
Get or set the package being used by the read-eval-print loop.
These work by interfacing with some complicated code in
tv:lisp-top-level1. The value from
:package can be
When you set the package, either a package or a package name is
These operations communicate with the process running the read-eval-print loop
to access that process’s own binding of
This flavor inherits its entire definition from
tv:listener-mixin-internal. The only difference is that
L is defined to look for windows with this flavor, and not the other.
This is the flavor of the window you get when you type
It has its own process, and can select any Zmacs buffer.
Generally none of the editor-specific operations should be invoked
on this window; that should be left up to the window’s own process.
Requests to this process, which generally ask the process to select
a buffer, are passed to it as blips of the form
(:execute zwei:edit-thing spec)
where spec is anything valid as the argument to
A Zmacs frame is useful for providing the user an opportunity to edit whatever he likes. Sometimes it is useful for a program to offer the user specific text to edit for its own purposes.
This is a window with no panes that serves as an editor.
It has a minibuffer and type-in window that pop up as its inferiors
when they are needed. This window has no process of its own;
:edit operation in any process to do editing in the window.
.defflavor zwei:standalone-editor-frame Another kind of standalone editor window, but this one is a frame with a permanently visible mode line and typein-window or mini buffer, just as a Zmacs frame is. .end_defflavor
.defmetainitoption "standalone editor windows" :comtab comtab
Specifies the comtab to use in editing in this frame.
The default is
.defmetamethod "standalone editor windows" :edit
Invokes the editor command loop on this window.
End command will return.
.defmetamethod "editor windows" :interval-string Returns a string giving the current text in the window. .end_defmetamethod
.defmetamethod "editor windows" :set-interval-string string Sets the text in the window to string. .end_defmetamethod
.defmetamethod "editor windows" :interval Returns the interval which is being edited in the window. If the window is a Zmacs frame, this is the selected buffer. Standalone editor windows have their own nonshared intervals which they edit; many of the editor primitives that work on Zmacs buffers also work on these intervals. .end_defmetamethod
.defmetamethod "editor windows" :set-interval interval Sets the interval that this window is displaying and editing to interval. On a Zmacs window, interval must be a Zmacs buffer; then this will actually tell the window to select the new buffer. .end_defmetamethod
A temporary window form of
.defresource zwei:pop-up-standalone-editor-frame &optional (superior
A resource of such windows, used by the following function.
'(:mouse)) mode-line-list min-width min-height initial-message (comtab
Pops up an editor window containing string and let the user edit it.
When he types
End, returns a string giving whatever he left in the
editor buffer. If he types
Abort, the value is
near-mode specifies how to position the window before popping it up.
It is passed to
mode-line-list is a list to be used to drive the mode line.
min-width and min-height are minimums for the size of the window. The window is larger than that if string requires more space to display.
initial-message, if non-
nil, is displayed in the typein window
immediately after the frame pops up.
comtab is the comtab to be used for editing.
This is the flavor used by the Lisp (Edit) window which you can create
with the system menu
It is a kind of Lisp listener in which both the input and the output
are recorded in an editor interval and can be edited.
It is based on
.defresource zwei:temporary-mode-line-window-with-borders-resource &optional (superior
A resource of such windows, used by the following functions.
.defflavor zwei:temporary-mode-line-window-with-borders The temporary mode line window contains just a mode line and a mini buffer. It is a way for a program to request a small piece of input while allowing the user to edit with Zwei.
This is the flavor of window that you get in ZMail if you click right on
Select in the ZMail command menu and then click on Find File in
Pops up a temporary mode line window near window, displaying its mode line by passing
format-string and format-args to
format, and lets the user edit.
Return terminates editing. The user’s input is returned as a string.
window may be any window on the screen, or
:mouse, meaning pop up
near the mouse.
Pops up a temporary mode line window near window, displaying the
string prompt as the mode line, and lets the user edit text which
(when the user types
Return) is parsed into a pathname using
defaults and special-type.
window may be any window on the screen, or
:mouse, meaning pop up
near the mouse.
.defmethod zwei:temporary-mode-line-window-with-borders :call-mini-buffer-near-window window function &rest args
Pops up this window near window, then uses function to read the input
and returns the value it returns.
function should be an editor function which invokes the mini buffer using
The first argument to function is a stream reading from the
text the user edited. args are passed to function as additional arguments.
.defflavor tv:peek-frame This flavor of window is a self-contained Peek display with its own process to update it. .end_defflavor
.defflavor tv:inspect-frame This flavor of window is a self-contained inspector with its own process to update it. .end_defflavor
.defresource tv:inspect-frame-resource &optional (superior
A resource of inspector frames which are created in a slightly special way
so that they do not have their own processes, but instead are to be invoked
in some other process by the function
.defflavor supdup:supdup A self-contained Supdup window with its own pair of processes to transfer data to and from the network. .end_defflavor
.defflavor supdup:telnet A self-contained Telnet window with its own pair of processes to transfer data to and from the network. .end_defflavor
.defresource supdup:supdup-windows &optional (superior
.defresource1 supdup:telnet-windows &optional (superior
Resources of Supdup and Telnet windows,
for use by the functions
when operating in the mode of substituting for another window.
A temporary window, otherwise like
.defresource tv:pop-up-text-window &optional (superior
A resource of such windows.
A temporary window which truncates lines of output, otherwise like
tv:pop-up-text-window but truncates lines
and resets the associated process when deexposed.
This is the kind of window that
Terminal F uses to print
its output, and it is good for many similar applications.
.defresource tv:pop-up-finger-window &optional (superior
A resource of such windows.
The who line is the pair of lines at the bottom of the main Lisp Machine screen which display the current status of the machine. The first of the two lines displays documentation what mouse clicks would do at the present time, based on the actual position of the mouse. The second line displays the time, your login name, the current process’s package and run state, and file or net server information. The term "who line" is sometimes used to refer to this line alone.
The window system treats the who line as a separate screen, thus preventing windows on the rest of the screen from being moved or reshaped to overlap the who line. The mouse documentation line is displayed by a window of its own, and so is each field of the second line.
The documentation displayed by the mouse documentation line is obtained
by sending the window under the mouse a
(see (windows-who-line-documentation-string-method)), or from the
tv:who-line-mouse-grabbed-documentation when the mouse is grabbed
Turns the who line display of mouse documentation on or off.
The package name and run state displayed in the who line describe only one process. They normally describe the process associated with the selected window, which is a different process if a new window is selected. However, the who line can be fixated on a particular process, independent of the selected window.
The process to describe in the who line, or
nil meaning to display
the one associated with the selected window. In the latter case,
:process operation on the window is used to get the process to display.
The process most recently described in the who line, regardless of why
that process was chosen. May be
nil if there was no process to describe
(for example, if the who line was supposed to describe the selected window
but there was no selected window or the window had no process).
The user can set
tv:who-line-process using the
Terminal W command
(see "Operating the Lisp Machine").
Informs the who line that it must redisplay everything.
Recording open file streams for display:
This who line window displays the status of an open stream or active network server. It can also display the idle time if there is no stream or server.
This window is also responsible for maintaining the lists of streams and servers that could be displayed. New streams and servers are reported to it with operations described here.
.defmethod tv:who-line-file-sheet :add-stream stream update-p
Adds stream to the list of open streams recorded by the
file state sheet. If update-p is non-
nil, the who line
field is updated immediately.
.defmethod tv:who-line-file-sheet :delete-stream stream Removes stream from the list of streams for the who line. .end_defmethod
.defmethod tv:who-line-file-sheet :delete-all-streams Clears out the list of streams for the who line. .end_defmethod
.defmethod tv:who-line-file-sheet :open-streams Returns the list of streams recorded for the who line. .end_defmethod
When the who line describes an open file, the name to display for it is
obtained with the
:string-for-wholine pathname operation. See
.defmethod tv:who-line-file-sheet :add-server conn contact-name process function Adds a entry to the list of active network servers recorded by the file state sheet. conn should be the network connection of this server, contact-name the contact name it responded to, process the process the server is running in. .end_defmethod
.defmethod tv:who-line-file-sheet :delete-server conn Removes the entry for connection conn from the list of servers for the who line. Note that this happens automatically if the connection is broken or closed. .end_defmethod
.defmethod tv:who-line-file-sheet :delete-all-servers Clears out the list of servers for the who line. .end_defmethod
"Foo on you") ¶
Closes the connections of all network servers, giving reason (a string) as the reason in the CLS packet.
Prints descriptions of all active network servers.
The usual color screen on a Lisp Machine has 454. lines of 576. pixels each, and each pixel has four bits. This allows sixteen different colors to be displayed at once. There are far more than sixteen possible colors. A color map controls the meaning of each of the sixteen pixel values. Each of the sixteen color map slots specifies an eight-bit red intensity, an eight-bit green intensity, and an eight-bit blue intensity. Thus there are about 16 million different colors that can appear, but only sixteen can be displayed at once.
The screen object that represents the color screen. This object is always present whether the machine has a color screen or not.
t if this machine actually has a color screen.
Writes the color map contents for slot, a fixnum from 0 to 17, with the three intensities r, g and b, all fixnums from 0 to 377 octal.
If synchronize is non-
nil, the change is delayed until the
vertical retrace time, so that it will take effect between frames.
screen is the screen to operate on, in case you have more than one.
It defaults to the normal color screen.
color:write-color-map, but faster. It performs no
synchronization at all, and is intended for use when you have already
waited for vertical retrace.
Copies the contents of array, a 16 by 3 array, into the color map of screen (which defaults to the normal color screen). This function always waits for vertical retrace to do its work.
Returns three values, the red, green and blue intensities from the color
map from slot slot. This does not actually read the hardware color map,
as there is no way to do that. Instead,
a copy for this purpose.
1) screen ¶
Writes multiple slots in the color map, starting with start-slot and ending with slot 17, from r, g and b. Note that the default omits slot 0, which is normally left as black (all three intensities zero). This function always waits for vertical retrace to do its work.
1) synchronize screen ¶
Sets the contents of the color map to sixteen randomly chosen colors.
The slots modified are start through 17, by default omitting slot 0.
synchronize is the same as in
Sets the color map to a spectrum, leaving color 0 as black.
Sets the color map (except for slot 0) randomly over and over again, waiting delay 60ths of a second in between.
Repeatedly chooses two colors (numbers from 1 to 17) randomly and moves their color map values gradually towards and through each other, so that ultimately the two slots exchange colors. A delay of delay 60ths of a second elapses between exchanges.
One way to draw on the color screen is to store into its screen array
as-2-reverse. The screen array of the color screen can be
tv:sheet-screen-array, and it is an array of type
art-4b. You can also use these functions:
Fills the whole color screen with color 0.
Sets the contents of a rectangle on the color screen to pixel value color. x and y are the coordinates of the upper left corner, and width and height are the size.
aluf is an alu function to apply to each pixel, combining the
specified color with the old pixel contents to get the new contents.
The default is
tv:alu-seta, which ignores the old contents.
This alu function is used only on the pixels of the rectangle,
which is different from what is done by the drawing primitives
for the black and white screen; this is why
tv:alu-seta does not produce
incorrect results as it normally would.
17) aluf screen ¶
Sets a line from (x1,y1) to (x2,y2) on the color screen
to color color. aluf is used as in
0) screen ¶
Draws character char in font font at position (x,y) in color color. font is an ordinary black-and-white font.
Color fonts can also be created. A color font is composed of four-bit
pixels just like the color screen. Using a color font, characters can
be drawn with the normal character drawing primitives. When this is
done, each bit of the color font pixel is combined with the
corresponding bit of the screen pixel using the alu function. The alu
function operates bit by bit just as it does on black-and-white screens,
and is applied to many pixels in the neighborhood of the character,
tv:alu-seta should not be used.
Creates a color font from black-and-white font bw-font.
bit-list is a list of four numbers, zero or one, which
specifies the bits of the pixels of the color font that correspond
to ones in the original font. Pixels that are zero in the original
font remain all zero in the color font. bit-list defaults to
(1 1 1 1).
The name of the resulting font is
color- followed by the name
of the original font, followed by the value of name-suffix.
Windows can be created on the color screen in the ordinary manner by
color:color-screen as the superior.
When fonts are specified for such windows, if the font specifier
names a black-and-white font, a color version of it is found or created.
This color font is created with bit list
(1 1 1 1). This is done
:parse-font-specifier method of the color screen.
This section describes how to interface with and customize the system menu which pops up when you click twice on the right mouse button.
The system menu is an instance of flavor
means that its menu items are grouped by columns, and each column’s
items come from the value of a corresponding variable which is examined
each time the menu is popped up in case more items have been added.
This is to enable you to add items to the menu and control where they
go. The most common column to add to is the third one, which lists
various kinds of windows to select (somewhat like the
command), so a special interface is provided for adding to it.
Adds an item named name to the third column of the system menu. form is what to execute if the user clicks on the item, and documentation is the mouse documentation string.
after is the name of an item to add after (a string),
t to add at the top, or
nil to add at the bottom.
A menu item list which forms the first column of the system menu.
A menu item list which forms the second column of the system menu.
By convention this is used for things that operate on the window
that the mouse was pointing at when the system menu was brought up.
They are implemented with
:window-op menu items.
Select item in the system menu pops up a momentary menu with a list
of windows that the user might want to select. Not all the visible
windows are included; usually a team of windows belonging to a single program
is represented by a single entry since selection among the team is controlled
by the program rather than the user. See (select-menu), for full details.
Create item in the system menu pops up a menu for the user to choose
a flavor of window to create.
A menu item list that is used by the system menu
Create in the screen editor when operating on a screen.
In general, the screen editor can operate on the inferiors of any window.
:pane-types-alist operation on that window is used to get
the item list for possible flavors to create; see (frames-pane-types-alist-method).
On a screen, the operation returns the value of this variable.
A resource is a pool of interchangeable objects that are available to be used temporarily and then returned to the pool (see (defresource-fun). Read that before you continue here).
Resources whose objects are windows are often useful. For example, there is a resource of windows of the right flavor to serve as "the system menu"; when you invoke "the" system menu, a window is allocated from the resource, and it is returned to the resource’s pool when it is deactivated.
Normally one defines a resource with
defresource. If the objects in
the resource are windows, it is better to use instead a different
tv:defwindow-resource. Allocating windows from
resources, and returning them, is just like working with any other
resources, and is documented in the Lisp Machine manual.
All the names described in this manual as resources are defined in this way.
Defines a resource of windows, named name. parameters are parameters on which
the object can depend. Following the parameters specified is one
additional parameter that is always defined: the window’s superior.
When you allocate a window from the resource, this parameter defaults to
options is a list of alternating keywords and values. Neither the
keywords nor the values are evaluated at the time that
tv:defwindow-resource is executed, but sometimes the value becomes
part of an expression that will be executed later (when a window is
allocated from the resource).
The allowed keywords are
tv:default-screen. Creating an initial copy is just a way of saving time the first time a window needs to be allocated from the resource.
defresource. If it is not specified,
tv:defwindow-resourceprovides a default, which calls
make-instancewith arguments taken from the
make-windowform to get the constructor for the resource.
:deactivated. If this keyword is not specified, then windows of the resource can be allocated to requesters if they have been explicitly returned to the pool and are not locked.
:deexposedmeans that any window that is not exposed is considered to have been returned to the pool.
:deactivatedmeans that any window that is not active is considered to have been returned to the pool.
A list of the names of all window resources defined with
Example: the system menu is created thus:
;Resource of system menus (defwindow-resource system-menu () :make-window (dynamic-multicolumn-momentary-window-hacking-menu :column-spec-list '(("Windows" *system-menu-windows-column* :font fonts:hl12i) ("This window" *system-menu-this-window-column* :font fonts:hl12i) ("Programs" *system-menu-programs-column* :font fonts:hl12i)) :save-bits t) :reusable-when :deexposed)
User programs that make use of the screen organization and standardization facilities provided by the window system are frequently in a somewhat difficult position. If that interface to the window system does not work, there seems to be no way at all to find out what is going on. Similarly, debugging code associated with switching between windows can be difficult since there may be no place to print debugging output at the time such code is executing.
One way to debug such problems is to use the cold load stream. This is the stream used in constructing the initial Lisp Machine environment, before the window system itself has been loaded. It has the advantage that it does not attempt to interface with the rest of the window system, or vice versa. It will never deexpose any windows or lock any locks. It types out one character at a time, by calling the microcode directly, and has very simple-minded ideas about end of line exceptions and more breaks.
The cold load stream is the value of this variable.
When the cold load stream is "waiting" for type-in, it does not actually wait; in fact, it loops until a character appears, with scheduling turned off, blinking its own special blinker by hand. The who line is not updated. Also, the chaosnet processes do not get to run. If the machine stays in this state too long, all chaosnet connections will be lost.
Whenever the system gets an error in the keyboard process, the
scheduler or the mouse process, the debugger uses the cold-load-stream
terminal-io. You also have the option of requesting
this if there is an error in a process whose
terminal-io is a
window that is not exposed and cannot be exposed because of locked
windows. (You will be queried, using the cold load stream, to choose
between this and a couple of other possibilities.)
When you exit from the debugger after it was using the cold load stream for one of these reasons, it will ask you whether to "restore the screen". Normally you should say Yes; then the screen contents will go back to what they were before the debugger was entered.
It is often preferable to use the cold load stream for debugging
window problems even when the normal alternatives are available. This
is because the operation of the debugger using a window for I/O may
interfere with the window phenomena being debugged. Use of the cold
load stream will avoid these problems. You can request use of the
cold load stream by setting
debug-io to the value of
tv:cold-load-stream before you run your test. Once this has been
done, not only errors but
Meta-Break as well will
use the cold load stream. To turn off use of the cold load stream for
all debugger invocations, set
debug-io back to
You can also force
trace output into the cold load stream by
trace-output. Note that you must not set
nil when done; you must save its original value and set it back
When the cold load stream is used because you have set one of the stream variables to it, you do not get the chance to restore the screen. It is not so easy to define how to do that "right" in this case; if it were done after each exit from the debugger, you would not get to see the history of multiple entries to the debugger.
The program can invoke a break loop using the cold load stream by
Resume to continue.
Note that when the break is entered, the package you are typing into
is typed out, because the package in the who-line is not going to be
correct for this break loop.
You the user can request such a break loop by typing
or by clicking on
Emergency Break item in the system menu. You can
get your program into the debugger using the cold load stream, without
having made advance preparation, by getting a break loop in this fashion,
debug-io to the cold load stream, exiting, and typing
Also, it is often useful to get a cold load stream break loop and call
eh on various processes or stack groups.
The window-based debugger is an alternative to the usual debugger;
it performs the same functions but displays graphically
rather than using sequential stream I/O. You invoke the
window-based debugger by typing
Control-Meta-W while in the
usual debugger. You can switch back and forth between the two
debuggers any number of times while handling a single error.
The debugger window is divided into six panes. At the bottom is a Lisp-listener-like window, which ordinarily provides a read-eval-print loop similar to the regular keyboard debugger. More commands are available by using the mouse in the other windows as described below.
At the top is a display of the disassembled or ground code for the currently selected stack frame, depending on whether or not it is compiled. It has a scroll-bar, but is otherwise not sensitive to the mouse.
Next are the args and locals windows, side by side, displaying the names and values of the arguments to the current stack frame and its local variables; they are grayed out if there are none. They also have scroll bars. Clicking the mouse on the name of an argument will print the name and the value in the Lisp window. Clicking on just the value will print it in the Lisp window. The mouse will highlight any relevant quantity that you are pointing to.
Next is the stack window, which displays in a pseudo-list format the functions and arguments on the stack. Clicking on a function or argument or sublists of them will cause them to be printed in the Lisp window as in the argument or local windows. Also, clicking the mouse to the left of a line containing a particular stack frame will make the debugger select that frame, changing what the above three windows show.
Below this, and above the Lisp window, is the command menu for the debugger window. The available commands are:
Abortin the regular debugger.
Arglistcommand and invokes the editor on that function.
Control-Meta-Rcommand in the regular debugger.
Control-Rin the regular debugger.
Proceedis like typing
Resumein the regular debugger. Clicking right on
Proceedgets you a menu of available proceed types, from which you can select one. This is equivalent to using one of the available
Supercommands in the regular debugger. If proceeding asks for an object to return, you can specify it with keyboard input or by pointing to a value with the mouse.
Control-Scommand, except that the mouse can be used.
Control-Tin the regular debugger, it asks for a tag and a value and throws there. The mouse can be used to specify the tag and value.
The window system contains several facilities to allow the user to make choices. These all work by displaying some arrangement of choices in a window. By pointing to one with the mouse the user can select it. The details (how the choices are specified, what the user interaction looks like, and what happens when a choice is selected) vary widely, which is why there are several separate facilities.
Each choice facility is implemented as a family of window flavors, providing several variations on the basic facility. For those who don’t want to create their own window, each facility provides an easy-to-use function interface that temporarily pops up a window of the appropriate flavor. The function interfaces will be described first in each section. Following the function interfaces there is documentation on how to create and use a window which has the facility.
This document does not cover how to modify these facilities to provide your own specialized versions, except in the simplest ways. That is certainly a reasonable thing to want to do. In order to do it you will need to read some of the code that implements the facility in question, for instance to learn about window instance variables and about internal operations that you might want to redefine or put daemons on.
Some portions of these facilities execute in the process that calls
them, while other portions execute in the mouse process. All Lisp
evaluation with which the user is concerned takes place in the user’s
process when using the facilities described in this document, with a
very few exceptions which are noted when they occur.
Thus the user may freely use side-effects (both special variables and
*throw) and need not worry that an error in his program
will interfere with mouse tracking.
A menu is an array of choices, each identified by a word or short phrase. You can select one of the choices by moving the mouse near it, which causes it to be highlighted (a box appears around it), and then clicking any mouse button.
What happens when you select one of the choices depends on the particular type of menu. Typically the choices in a menu might be commands to some program or choices for what a command should operate upon.
The system automatically chooses the arrangement of the choices and the size and shape of the window. Naturally there are ways for the user to control this if necessary.
To see an example of a menu, click the right-hand mouse button twice, causing the system menu to appear.
A menu has a list of items; each item represents one of the choices offered. An item tells the menu what to display and what to do if the user selects (clicks on) it. "What to do" specifies both what value to return and a possible side effect.
Response to selection of an item is implemented by the
operation, which is always sent in the user process (rather than the mouse
process). Thus side effects occur in the appropriate process. The
returned value comes back to the user from
:execute depending on how the menu is used. This
will be explained in detail later.
An item can take any of the following forms:
(name . atom)name (a symbol or a string) is what to display, and atom is what to return. There are no side-effects.
(name value)name is a string or a symbol to display, and value is any arbitrary object to return. There are no side-effects.
(name type arg option1 arg1 option2 arg2...)This is the most general form. name is a string or a symbol to display. type is a keyword symbol specifying what to do, and arg is an argument to it. The options are keyword symbols specifying additional features desired, and the args following them are arguments to those options.
nil is supplied as a menu item, it is ignored completely.
It takes up no space in the menu.
A list of items is sometimes called an "item alist" since most forms of menu item look like alist elements mapping strings into what to do about them.
The possible values of type in the most general form of menu item are:
:force-kbd-inputoperation. Typically it is either a character code, which is to be treated as if it were typed in from the keyboard, or a list (a blip), which is a command to the program (see (blips)). Use of
:kbdproduces an effect like the effect of using a command menu (see (command-menu)).
:choosemessage and the result is returned. Normally arg would be a pop-up menu. If arg is a symbol it gets evaluated.
tv:menu-choose, popping up another menu, and the result of choosing from that menu is returned. menu-items is another list of menu items.
(left middle right). The three menu items in the list will be used only for execution, not for display, so it does not matter what they have as the string to be displayed (it can be
nil), and there is no point in giving them
:documentationkeywords. These should go in the main menu item, the one that contains the
:execute-window-opmenu operation, which the flavor
tv:menudoes not implement. The flavor
tv:window-hacking-menu-mixinprovides a method to implement it.
The menu item modifier keywords are:
progw(see (progw-fun)). These bindings are made before evaluating, funcalling, sending a message to a window, etc. If
:buttonsis used with
:bindingsmust appear inside the menu item within the
:buttonsto have an effect on the final result.
Here are some examples of menu item lists:
Three items, that display as
and return the symbols
lose when chosen.
(foo bar lose)
Another way of specifying the same thing, using more general syntax:
(("FOO" :value foo) ("BAR" :value bar) ("LOSE" :value lose))
FOO in italics and adding documentation for the who line:
(("FOO" :value foo :font fonts:tr12i :documentation "Choose to FOO") ("BAR" :value bar :documentation "Request a BAR") ("LOSE" :value lose :documentation "Don't win."))
Some other type keywords are used here.
The value of the
:choose operation will be
a keyword such as
the value returned by the function
or whatever the
(("File" :buttons ((nil :value :read) (nil :value :write) (nil :menu-choose ("File Operation" ;; Item list of menu obtained for click-right on
File. ("Read" :value :read :documentation "Read a file") ("Write" :value :write :documentation "Write a file") ("Rename" :value :rename :documentation "Rename a file") ("Delete" :value :delete :documentation "Delete a file")))) :documentation "L: Read file. M: Write file. R: Menu.") ;; The following makes a blank line in a one-column menu. ("" :no-select nil) ;; We assume that
buffer-op-menuis a variable whose value is a menu. ("Buffer" :menu buffer-op-menu :documentation "Operate on this buffer") ("Read" :buttons ((nil :eval (read)) (nil :eval (read) :bindings ((base 10.))) nil) :documentation "L: Read sexp. M: Read sexp, base ten."))
Here we show the use of
:bindings. This expression
creates a menu item which contains a host taken from
the local variable
When the menu item is chosen, the function
will be called with the appropriate host as the value of
the special variable
`(("Hack This Host" :funcall hack-host :bindings ((host-to-hack ',host)) :documentation "Do some hacks to this host."))
This flavor defines the
:execute operation to process a menu item
according to the rules described above.
.defmethod tv:menu-execute-mixin :execute item
Processes item, computing and returning the "value to return"
according to the rules described above. Everything about the meaning of menu items,
except as far as it affects displaying the menu, is determined by what the
:execute operation does, so by redefining this operation you can
implement new types of menu items. The overall format must be as described,
however, because displaying the menu checks for the type
and for the
:documentation modifier keywords.
.defmethod tv:menu-execute-mixin :execute-no-side-effects item
Processes item, computing and returning the "value to return",
provided that this can be done without side effects. If computing the
value to return might possibly have side effects (such as for item
:menu-choose), the value is not computed and
nil is returned.
This operation is typically used to find the item in a given item list that would return a particular value if selected. .end_defmethod
Returns the string to display for item. The font to use is returned as the second value; it defaults to item-default-font if not specified by the item. item-default-font itself defaults to the current font of the menu as a window.
menu is the menu that item is for; it is used for interpreting font specifications in item itself.
If you are not interested in the font, you can omit the last two arguments.
Provides for the
:window-op item type by implementing the
:execute-window-op operation. This involves remembering the mouse
position and the window under the mouse at the time the menu is exposed.
Pops up a menu and allows the user to make a choice with the mouse.
When the choice is made, the menu disappears and the chosen item is
executed. The value of that item is returned as the first value of
tv:menu-choose, and the item itself is returned as the second
If the user moves the mouse out of the menu and far away, the menu disappears
item-list is a list of items as described above.
label is a string to be displayed at the top of the menu, or
nil (the default)
to specify the absence of a label.
near-mode is where to put the menu. It defaults to the list
(:mouse) and must
be an acceptable argument to
default-item is the item over which the mouse should be positioned
initially. This allows the user to select that item without moving the mouse.
If default-item is
nil or unspecified, the mouse is initially positioned
in the center of the menu.
superior is the sheet of which the menu should be an inferior.
The default is
tv:mouse-sheet, which is usually a screen.
(tv:menu-choose '(("Read" :value foo) ("Write" :value bar)) "Direction")
nil if the user moves the mouse out of the menu).
Asks the user to answer Yes by clicking on a small window or No by moving the mouse out of it. The window is a menu which displays a single item, string.
The value is
t if the user clicks on the menu, or
if he moves the mouse out of it.
The way a menu is displayed is described by six parameters that are collectively called its geometry. Each of these parameters may be specified as a constraint, or may be allowed to default based on the item list and the parameters that are constrained.
There are two styles of arranging the choices in the menu. They can be in an array of rows and columns, or they can be "filled", that is, each line has as many choices as will fit with a reasonable amount of white space in between. In columnar format, each line has the same number of choices: the same as the number of columns. This is not true in filled format. Filled format is specified by giving zero as the number of columns.
The geometry is represented as a list of six elements, one for each parameter.
For the first four parameters, one must distinguish between the current
value and the imposed constraint. The constraint values may be
meaning "do not constrain this parameter". The current values cannot be
The last two parameters exist only as constraints, and may be
The actual display of a menu is based on four parameters: the number of rows, the number of columns (or whether to use fill mode), the height and the width. Some of these may be specified by constraints; others may be specified on a one-time basis when the menu is displayed; the rest are chosen based on the ones already known, and on the item list.
The default geometry constraints are all
nil, meaning that the
system can choose the size and shape freely, based on the specified item
list. The default shape is an upright golden rectangle, using columnar
format with as many columns as fit in the width. Most small menus will
have only one column.
If both the height and with are specified (either precisely or indirectly) in such a way that not all the items can fit, the menu will have a scroll bar and the user will have to scroll to see all the items.
When the item list of a menu is changed, the display of the menu is recomputed based on the new item list and the geometry.
The following init-plist options to a menu will initialize the geometry:
.definitoption tv:menu :geometry list Sets the complete geometry to list, a list of six elements. Example:
(make-instance 'tv:menu ':geometry (0 nil 300 nil nil 500))
makes a filled menu that is 300 pixels wide; when its item list is specified or changed it will become as tall as necessary to display all the items as long as that does not exceed 500 pixels. Beyond that point, it will be 500 pixels high and will require the user to scroll. .end_definitoption
.definitoption tv:menu :rows n-rows Sets the number of rows. .end_definitoption
.definitoption tv:menu :columns n-columns Sets the number of columns. .end_definitoption
.definitoption tv:menu :fill-p t-or-nil Specifies whether to use filled format. .end_definitoption
.definitoption tv:menu :default-font font
Sets the default font, the font in which items which do not specify a font
are displayed. If this is not specified, it defaults to
the standard font for the purpose
:menu on the screen the menu is on
The following operations manipulate the geometry of a menu:
.defmethod tv:menu :geometry
Returns a list of six things, the menu’s geometry. These are the constraints,
nil in unspecified positions; contrast
.defmethod tv:menu :current-geometry Returns a list of six things, which are the geometry corresponding to the actual current state of the menu.
The first four elements are actually sufficient to describe the current
state. These are never
The last two elements returned are the constraint values for the maximum
width and height, since there are no current values to return. These
Contrast this with
.defmethod tv:menu :set-geometry &optional columns rows inside-width inside-height max-width max-height Sets the geometry (the constraints) from the arguments. The menu may change its shape and redisplay as a result.
Note that this takes six arguments rather than a list of six things as you might expect. This is because you frequently want to omit most of the arguments.
An explicit argument of
nil means to make that aspect of the
geometry unconstrained. An omitted argument or an argument of
means to leave that aspect of the geometry the way it is (if
unconstrained, it remains so).
.defmethod tv:menu :fill-p
.defmethod1 tv:basic-menu :set-fill-p t-or-nil
Get or set the menu’s fill mode,
t if it displays in filled format rather than columnar
format. These are special cases of the
.defmethod tv:menu :set-default-font font Sets the default font, the font in which items that do not specify a font are displayed. This recomputes the current display based on the constraints. .end_defmethod
.defmethod tv:menu :set-edges left top right bottom &optional option
This operation, in addition to setting the current position and size of
the menu, also makes the specified size be a permanent constraint for
the menu unless option is
:temporary. In that case, the menu is
redisplayed with the specified edges for now, but if it is redisplayed
again for any reason, the permanent constraints (or lack of them)
otherwise specified will re-emerge.
Computes the current display parameters from the constraints and the item list and default font. inside-width and inside-height serve as constraints for this time only, overriding any permanent constraints for those parameters.
If draw-p is non-
nil, the menu is actually redrawn.
This function is a subroutine of various menu methods, and
be the menu.
.defmethod tv:menu :minimum-width This returns the minimum width for the menu, as required to display its label. This is used in deciding how to display the menu.
Other menu flavors can redefine this operation to force the menu to be wide enough for some purpose. .end_defmethod
These are the basic and mixin flavors for the ordinary kinds of menus. They cannot be instantiated themselves but are useful to know about. Other kinds of menus are discussed in later sections.
Everything else is built on this. All the operations documented here as
being defined on
tv:menu are really defined by this flavor.
.definstvar tv:basic-menu tv:item-list The item list of the menu. .end_definstvar
.definstvar tv:basic-menu tv:last-item
The last item actually selected with a mouse click in this menu,
nil if none has been selected yet.
Used for positioning the mouse when a momentary menu pops up.
.definstvar tv:basic-menu tv:current-item
The item which the mouse is pointing at, or
.definstvar tv:basic-menu tv:chosen-item
Set each time an item is selected, to that item.
Waiting for an item to be selected is done by setting
this variable to
nil and waiting for it to become
.definstvar tv:basic-menu tv:geometry The geometry (constraints) of the menu, a list of length 6. .end_definstvar
.defflavor tv:basic-momentary-menu (
This is a kind of menu, often referred to as a "pop up" menu, which is
only momentarily on the screen. A
:choose operation on a menu of
this flavor causes it to position itself where the mouse is. When the
user selects an item in the menu, or alternatively moves the mouse far
away from the menu, the menu disappears and deactivates, the mouse
warps back to where it was when the menu appeared, and the
operation returns the chosen item or
These are the interesting instantiable menu flavors:
.defflavor tv:menu (
tv:basic-menu with borders and a label on top. The
default is for there to be no label but you can specify one with the
:label init-plist option or the
tv:basic-momentary-menu mixed with the right other flavors.
Momentary menus were described at the beginning of this section.
.defresource tv:momentary-menu &optional (superior tv:mouse-sheet) A resource of momentary menus. .end_defresource
.defflavor tv:temporary-menu (
This is a menu that is a temporary window; that is, it saves the bits
of the windows underneath it when it is exposed. It is not a momentary menu,
and therefore it does not expose or deexpose itself automatically.
It is appropriate to use a temporary menu rather than a momentary menu when you want to pop a menu up and make several choices from it before popping it back down, or if you don’t want to allow the user the option of choosing nothing by moving the mouse out of the window. .end_defflavor
.defflavor tv:momentary-window-hacking-menu (
A momentary menu with the window-hacking mixin. See
.defresource tv:momentary-menu &optional (superior
This is a resource of momentary menus.
tv:menu-choose allocates a
window from this resource.
The following operations are useful on any flavor of menu. Also listed are init options which are useful with any flavor of menu. Operations and init options that specifically have to do with the shape and arrangement of the menu are listed in the section on geometry ((menu-geometry)).
.defmethod tv:menu :item-list .defmethod1 tv:menu :set-item-list item-list Get or set the list of items (choices). Setting the item list recomputes the geometry and redisplays the menu. .end_defmethod
.definitoption tv:menu :item-list items The item list can be set when the menu is created. .end_definitoption
.defmethod tv:menu :choose
Exposes the menu if it is not already exposed, then waits for a selection to be
made with the mouse. The selection is
:execute’d and the resulting value
is returned. A momentary menu will return
:choose if the mouse
is moved far out of it, and in any case will pop down before returning.
.defmethod tv:menu :execute item
Given an item that was selected, performs the appropriate side-effects and
returns the appropriate value. For most kinds of menus, this operation is invoked
automatically as part of the
:choose operation, but command menus
(see below) require the user program to invoke
if it is desired.
.defmethod tv:menu :move-near-window window Exposes the menu above or below window, giving it the same width. .end_defmethod menu
.defmethod tv:menu :center-around x y This operation is implemented by all windows, but menus handle it a little differently. The window is positioned so that the last item chosen appears at the specified coordinates (in the superior), if possible. If this would cause the menu to stick outside of its superior, it is offset slightly to keep it inside. The actual coordinates of the center of the appropriate item are returned (you might want to put the mouse there). Momentary menus use this to put the menu in such a place that the mouse will be right over the last item chosen. .end_defmethod
.defmethod tv:menu :current-item
Gets the item the mouse is currently pointing at (
nil if none).
In most cases if you are using this operation you are doing something wrong.
.defmethod tv:menu :chosen-item .defmethod1 tv:menu :set-chosen-item item Get or set the item that has been chosen by the mouse and is being communicated back to the controlling process. In most cases if you are using these operations you are doing something wrong. .end_defmethod
.defmethod tv:menu :last-item
.defmethod1 tv:menu :set-last-item item
Get or set the item that was chosen by the mouse the last time this menu was
used. When a momentary menu is exposed near the mouse by the
it will put the mouse over this item so that it easy to choose it again.
.defmethod tv:menu :column-row-size Returns two values: the width of a column in bits and the height of a row in bits. .end_defmethod
.defmethod tv:menu :item-cursorpos item
Returns two values, like
:read-cursorpos, giving the coordinates
of the center of the displayed representation of item. The result
nil if the item is scrolled off the display.
.defmethod tv:menu :item-rectangle item
Returns four values, the coordinates of the rectangle enclosing the displayed
representation of the specified item. The result is
nil if the item
is scrolled off the display. Note that the returned coordinates are inside
coordinates and that they include a 1-pixel margin around the item.
.defmethod tv:menu :menu-draw
Draws the menu’s display.
:menu-draw is invoked automatically by
the system when required, and should not be used in application
programs. However, user-defined menu flavors may redefine the operation
or add daemons to it.
.defmethod tv:menu :mouse-buttons-on-item buttons-down-mask
This operation is invoked by the mouse process when the mouse is clicked on an
item. It is completely responsible for whatever should be done in the
mouse process at that time. Its default definition is to record the
chosen item and process the item type
:buttons when that is used.
The instance variable
tv:current-item or the
operation can be used to find out which item the mouse is on.
:scroll-bar-p are also defined for communication with the scroll
bar. See (scroll-protocol).
The menus described so far are driven by the
that is, the program decides when it is time for the user to choose
something in the menu. In some applications it should be the user
who decides when to choose something from a menu. For example,
in Peek, the user can select a new mode with the menu at any time,
but Peek cannot spend all its time waiting for the user to do this.
The command menu is designed for such applications. When an item in a command menu is chosen, the menu puts a blip into its input buffer. The blip is a list
(:menu item button-mask menu)
which can read as an input character with the
:any-tyi operation on
any other window sharing the same input buffer. item is the menu
item that was clicked on, button-mask says which mouse button was
used (as in
(tv:mouse-last-buttons-var)), and menu is the menu that was clicked
on, in case you are using more than one.
Usually a command window is part of a team of windows managed by a
single process and sharing a single input buffer. Menu clicks generate
input that is read in a single stream together with mouse clicks on the
other windows and keyboard input. For example, Peek and the inspector
both use command menus in this way. Once the controlling process reads
the blip, it can do
(funcall menu ':execute item) if it
wishes the item to be processed in the usual way for menu items.
tv:command-menu-mixin mixed with
tv:menu to make it instantiable.
.defmethod tv:command-menu :io-buffer .defmethod1 tv:command-menu :set-io-buffer io-buffer These operations get or set the I/O buffer in which a command-menu sends stores a blip when an item is selected. .end_defmethod
.definitoption tv:command-menu :io-buffer io-buffer The input buffer to be used by a command menu is usually specified when it is created. .end_definitoption
.definstvar tv:command-menu tv:io-buffer This is where the input buffer is recorded. .end_definstvar
When a command menu built on this flavor is deexposed, it automatically
"clicks" on its
Abort item. In other words, the
method for this flavor searches the item list for an item whose
displayed representation is
"ABORT" (case is not significant). If
such an item is found, a blip is sent to the input buffer claiming that
that item was clicked on with the Left button.
Dynamic item list menus dynamically recompute the item list at various times. Whenever the program makes an explicit request to use the menu, the menu checks automatically to see whether its item list has changed.
This mixin causes a menu to invoke the
at various times. This operation receives no arguments, and its value
is ignored; it should update the item list if appropriate.
This mixin does not define the
however. Each user of the mixin must define this operation to update
the item list as he desires.
.defmetamethod "dynamic item list menus" :update-item-list
Sent by the system, this operation should be defined by the user
to do a
:set-item-list if the item list should change.
Note that this operation may be invoked in various processes, so your definition should use only global variables (and data structure it can find from the menu itself). .end_defmethod
Provides for a form which is evaluated to get the menu’s item list, kept in
tv:item-list-pointer instance variable. The
:update-item-list operation is defined to evaluate the form and set
the item list to the form’s value.
.definstvar tv:dynamic-item-list-mixin tv:item-list-pointer This is the form evaluated to recompute the current item list. .end_definstvar
.defmethod tv:dynamic-item-list-mixin :item-list-pointer .defmethod1 tv:dynamic-item-list-mixin :set-item-list-pointer form Get or set the form. .end_defmethod
.definitoption tv:dynamic-item-list-mixin :item-list-pointer form Initializes the form. .end_definitoption
These are menu flavors that are just combinations of this with other flavors:
.defflavor tv:dynamic-momentary-menu A momentary menu with the dynamic item-list mixin. .end_defflavor
.defflavor tv:dynamic-momentary-window-hacking-menu A momentary menu with both the dynamic item-list mixin and the window-hacking mixin. .end_defflavor
.defflavor tv:dynamic-temporary-menu A temporary menu with the dynamic item-list mixin. .end_defflavor
.defflavor tv:dynamic-temporary-command-menu A command menu with the temporary and dynamic item-list mixins. .end_defflavor
.defflavor tv:dynamic-temporary-abort-on-deexpose-command-menu A command menu with the temporary, abort-on-deexpose, and dynamic item-list mixins. .end_defflavor
This mixin, to be used with
tv:abstract-dynamic-item-list-mixin, makes a
menu of several columns, in which each column’s items are independently
dynamically recomputed. The system menu is such a menu.
The columns are specified by the instance variable
tv:column-spec-list. The value is a list; each element specifies
one column of the menu, and looks like this:
(heading item-list-form options...)
heading is a string to be displayed (as a
:no-select item) at
the top of the column. item-list-form is a form to be evaluated to
produce the list of items for the column. It should have no side
effects and may be evaluated in any process. The options are
modifier keywords and values, such as are found in menu items.
These modifiers apply to the column heading only. The most useful one
:font keyword. For example, the system menu uses this
column spec list:
(("Windows" tv:*system-menu-windows-column* :font fonts:hl12i) ("This window" tv:*system-menu-this-window-column* :font fonts:hl12i) ("Programs" tv:*system-menu-programs-column* :font fonts:hl12i))
Each column’s item list form is a symbol, the name of a special variable. .end_defflavor
.definstvar tv:dynamic-multicolumn-mixin tv:column-spec-list This instance variable holds the column spec list. .end_definstvar
.definitoption tv:dynamic-multicolumn-mixin :column-spec-list Initializes the column spec list. .end_definitoption
.defmethod tv:dynamic-multicolumn-mixin :column-spec-list .defmethod1 tv:dynamic-multicolumn-mixin :set-column-spec-list specs Get or set the column spec list. .end_defmethod
This is an instantiable, momentary mixture of
Similar to the previous, but includes
The system menu is an instance of this flavor.
A multiple menu asks the user to select any combination of menu items rather than a single item. The menu has a "choice box" (usually named "Do it") at the bottom in addition to its menu items. Clicking on a menu item selects it or unselects it; the selected items are displayed in inverse video. Clicking on the "Do it" box specifies the set of items currently selected.
:choose operation on a multiple menu returns as its first value
a list of the values of the items selected by the user.
Pops up a menu and allows the user to choose any subset of the
available items. The user finalizes his choice by clicking on the
"Do It" box at the bottom of the menu. At this time,
tv:multiple-menu-choose returns as its first value
a list of the results of executing all the chosen menu items.
The second value of
t in this case.
If the user moves the mouse out of the menu and far away, the menu disappears
nil for both values. The second value
enables the caller to distinguish between a refusal to choose and choosing
the empty set of items.
item-list is a list of menu items as described above. highlighted-items is a list of some of the same items; these are the items to include, initially, in the set to be chosen. The user can add items to the set or remove items from the set.
The elements of highlighted-items must be memq in item-list
for proper functioning.
label is a string to be displayed at the top of the menu, or
nil (the default)
to specify the absence of a label.
near-mode is where to put the menu. It defaults to the list
(:mouse) and must
be an acceptable argument to
superior is the sheet of which the menu should be an inferior.
The default is
tv:mouse-sheet, which is usually a screen.
(tv:multiple-menu-choose '(rice spinach water coke) "Pick some foods" nil '(water))
might return the list
(rice spinach water) if the user clicked on
the entries for
spinach, and did not turn off
(let ((items '(("Rice" :value rice) ("Spinach" :value spinach) ("Water" :value water) ("Coke" :value coke)))) (tv:multiple-menu-choose items "Pick some foods" '(:mouse) (list (assoc "Water" items))))
can return the same possible values, but has a prettier display.
.defflavor tv:margin-multiple-menu-mixin Gives a menu the ability to have multiple items selected in this manner. .end_defflavor
.defflavor tv:multiple-menu (
A menu that behaves as described above. This is a combination of
.defflavor tv:momentary-multiple-menu (
A multiple menu that is also momentary.
.defresource tv:momentary-multiple-menu &optional (superior tv:mouse-sheet)
A resource of momentary multiple menus, used by
.defmethod tv:margin-multiple-menu-mixin :add-item item Adds item to the item list of the multiple menu, initially unhighlighted. All the existing items remain, and remain highlighted if they already were. .end_defmethod
.defmethod tv:margin-multiple-menu-mixin :set-item-list item-list In addition to setting the item list and redisplaying the menu, all the items start out unhighlighted. .end_defmethod
.definitoption tv:margin-multiple-menu-mixin :special-choices items
This init option is equivalent to the
option (which is provided by our component flavor
tv:menu-margin-choice-mixin). It is provided for historical
compatibility. items is a list of menu items that specify the
choice boxes desired and what to do if they are clicked on.
.defflavor tv:menu-highlighting-mixin Provides for some of the menu items to be highlighted with inverse video. This is typically used with menus of "modes", where the modes currently in effect are highlighted. The menu items corresponding to modes will typically be set up so that when executed, they adjust the highlighting to reflect the enabling or disabling of a mode.
This flavor is used in
.definstvar tv:menu-highlighting-mixin tv:highlighted-items The list of items currently highlighted. .end_definstvar
.defmethod tv:menu-highlighting-mixin :highlighted-items .defmethod1 tv:menu-highlighting-mixin :set-highlighted-items list Get or set the list of highlighted items. .end_defmethod
.definitoption tv:menu-highlighting-mixin :highlighted-items items
When a menu with the menu-highlighting mixin is created, the list of items to
be initially highlighted may be specified. The default is
.defmethod tv:menu-highlighting-mixin :add-highlighted-item item .defmethod1 tv:menu-highlighting-mixin :remove-highlighted-item item Make item be highlighted, or make it stop being highlighted. .end_defmethod
.defmethod tv:menu-highlighting-mixin :highlighted-values
.defmethod1 tv:menu-highlighting-mixin :set-highlighted-values list
.defmethod1 tv:menu-highlighting-mixin :add-highlighted-value value
.defmethod1 tv:menu-highlighting-mixin :remove-highlighted-value value
These operations are similar to the preceding four, except that instead of referring
to items directly you refer to their values, i.e. the result of executing them.
For instance if your item list is an association list, with elements
(string . symbol), these operations use symbol.
This only works for menu items that can be executed without side-effects,
not for item types
.defflavor tv:menu-margin-choice-mixin (
This mixin gives a menu the ability to have choice boxes in the
margin. It is used in multiple menus.
Choice boxes appear in a single line in the bottom margin of the menu. Each one consists of a name followed by a little square or box. Clicking on the box activates the choice.
This flavor adapts
(see (tv:margin-choice-mixin-flavor)) for use in menus.
.defmethod tv:menu-margin-choice-mixin :menu-margin-choices .defmethod1 tv:menu-margin-choice-mixin :set-menu-margin-choices items Get or set the list of choice box items. The items look and work just like menu items, and clicking on one has the same effect. The difference is only in how and where they display. .end_defmethod
.definitoption tv:menu-margin-choice-mixin :menu-margin-choices items Initializes the list of choice box items. The default value is
(("Do It" :eval (values (funcall-self ':highlighted-values) t)))
which provides a single choice box and implements the values returned
.defflavor tv:margin-choice-menu An instantiable menu flavor that also allows margin choices. .end_defflavor
.defflavor tv:momentary-margin-choice-menu A instantiable momentary menu flavor that also allows margin choices. .end_defflavor
The multiple choice facility provides a window containing a bunch of
items, one per text line, and several choices about each item.
To see an example of its use, invoke the
Meta-X Kill Or Save Buffers.
For each item, there can be several yes/no choices for the user to make.
There is the same set of choices for each item (though some items may
omit some choices). For example, in
Kill Or Save Buffers, there is
an item (a line) for each buffer, and each line offers choices "Save",
"Kill" and "Unmod". The choices of the same kind for different items
form a column, with a heading at the top saying what that choice is for.
The leftmost column contains the text naming each item. The remaining
columns contain small boxes (called choice boxes). A "no" box has a
blank center, while a "yes" box contains an "X".
Pointing the mouse at a choice box and clicking the left button turns it on or off. Each choice can be initialized by the program to "yes" or "no" as appropriate for a default.
There can be constraints among the choices for an item. For example, if you want the choices to be mutually exclusive, you can set up constraints so that clicking one choice box to "yes" will automatically set the other choice boxes on the same line to "no".
A multiple choice window may have more lines of choices to offer than the window has lines. In this case, the user can scroll, as the multiple choice window is a kind of text scroll window (see (text-scroll-windows)).
There are several parameters associated with a multiple-choice window:
The item-name is a string, the column heading for items. In the editor
example, it is
The item-list is a list of representations of items. Each element is a list,
(item name choices). item is any arbitrary object, such as an
name is a string which names that object; it will be displayed on the left
on the line of the display devoted to this item. choices is a list of
keywords representing the choices the user can make for this item. Each
element of choices is either a symbol, keyword, or a list,
If default is present and non-
nil, the choice is initially "yes";
otherwise it is initially "no". This is how the editor initializes the
"Save" choice to be "yes" for a modified buffer.
The keyword-alist is a list defining all the choice keywords
allowed. Each element takes the form
keyword is a symbol, the same as in the choices field of an
item-list element. name is a string used to name that keyword.
name is used as the column heading for the associated column of choice
An element of keyword-alist can have up to four additional list elements,
called implications. These control what happens to other choices for the same
item when this choice is selected by the user. Each implication can be
meaning no implication, a list of choice keywords, or
t meaning all other
choices. The first implication is on-positive; it specifies what other choices
are also set to "yes" when the user sets this one to "yes". The second implication
is on-negative; it specifies what other choices are set to "no" when the
user sets this one to "yes". The third and fourth implications are off-positive
and off-negative; they take effect when the user sets this choice to "no".
The default implications are
nil, respectively. In other
words the default is for the choices to be mutually exclusive.
If a keyword-alist element does not contain implications, the
default implications are
rplacd’ed into it.
Kill Or Save Buffers specifies the implications as
((:save "Save" nil (:not-modified) nil nil) (:kill "Kill" nil (:not-modified) nil nil) (:not-modified "UnMod" nil (:save :kill) nil nil) (:compile "QC-FILE" nil nil nil nil))
so that "Unmod" cannot be chosen together with either "Save" or "Kill".
The finishing-choices are the choices to go in the bottom margin. When the user
clicks on one of these he is done. The variable
contains a reasonable default for this, providing
Do It and
This is the easy interface to the multiple choice facility:
Pops up a multiple-choice window and allows the user to make choices with the mouse. The dimensions of the window are automatically chosen for the best presentation of the specified choices. If there are too many choices, more than maxlines, scrolling of the window is enabled.
item-name, item-list, and keyword-alist are as described above. finishing-choices cannot be specified and is always the default.
When the user clicks on one of the two finishing choices in the bottom margin
Do It and
Abort) the window disappears and
If the user finishes by choosing
Abort the returned value is
and the second returned value is
If the user chooses
Do It, the returned value is a list with one element
for each item. Each element is a list whose car is the item (that
arbitrary object which the user passed in in the item-list argument)
and whose cdr is a list of the keywords for the "yes" choices selected for that item.
near-mode tells the window where to pop up. It is a suitable argument
tv:expose-window-near. The default is the list
maxlines, which defaults to twenty, is the
maximum number of choices allowed before scrolling is used.
Here is an example:
(tv:multiple-choose "Word" '((:eat "Eat" (:add :make-permanent)) (:drink "Drink" (:forget :make-permanent))) '((:add "Add" nil nil nil (:make-permanent)) (:forget "Forget" nil (:make-permanent) nil nil) (:make-permanent "Make Permanent" (:add) (:forget) nil nil)))
offers the possibilities of
and the possibilities of
Presumably this would be done because
:drink has already been "added"
:eat has not been.
The implications say that making permanent is incompatible with forgetting when forgetting is possible, and requires adding when adding is possible.
The value returned might be
((:eat :add :make-permanent) (:drink))
In this example, the items are keywords (symbols), but that is not significant.
The system never looks inside them; it just compares them with
puts them in the returned value.
These are the grubby details:
.defflavor tv:basic-multiple-choice (
This is the basic flavor that makes a window implement the multiple-choice facility.
Like most basic mixins, it is not itself instantiable but it does commit any window
that incorporates it to being a multiple-choice rather than any different sort of window.
.definstvar tv:basic-multiple-choice tv:item-name The window’s item name. .end_definstvar
.definstvar tv:basic-multiple-choice tv:choice-types The window’s keyword alist. .end_definstvar
.defflavor tv:multiple-choice (
This is a reasonable window with the multiple-choice facility in it. It has borders
and a label area on top which is used for the column headings.
.defflavor tv:temporary-multiple-choice-window (
This is a multiple-choice window which is equipped to pop up temporarily.
.defresource tv:temporary-multiple-choice-window &optional (superior tv:mouse-sheet)
This is a resource of temporary multiple-choice windows, used by the
The following operations are provided by multiple choice windows.
.definitoption tv:basic-multiple-choice :item-list item-list Initializes the window’s item list to item-list. .end_definitoption
.defmethod tv:multiple-choice :setup item-name keyword-alist finishing-choices item-list &optional maxlines This operation sets up all the various parameters of the window. Usually it is used while the window is deexposed. The window decides what size it should be and whether all the items will fit or scrolling is required, then draws the display into its bit-array. Thus when the window is exposed the display will appear instantaneously.
maxlines is the maximum number of lines the window may have; if there are more
items than this only some of them will be displayed and scrolling will be enabled.
maxlines defaults to
The finishing-choices are a list of choices for
tv:margin-choice-mixin (see (tv:margin-choice-mixin-flavor)). When
one of these finishing choices is clicked on, it should set the instance
self to either a symbol (for an
abnormal exit) or a list for the
:choose operation to return.
.definstvar tv:basic-multiple-choice tv:choice-value
When the mouse process sets this non-
:choose operation returns.
.defmethod tv:multiple-choice :choose &optional near-mode
Moves the window to the place specified by near-mode, which defaults
to the list
(:mouse), and exposes it. Then waits for the user to make a
finishing choice and returns the window to its original activate/expose
status before the
:choose. This operation returns the same value as
This facility presents the user with a display of a bunch of Lisp variables and their values. The user may change the value of some of the variables. When the values are to his liking he may indicate that he is done.
The choose-variable-values window is a kind of text scroll window, so each line of the display corresponds to one variable. The name of the variable, a colon, and the value of the variable are displayed. Pointing the mouse at the value causes a box to appear around it. Clicking the left mouse button at that point allows the value to be changed.
For an example of a choose-variable-values window, try the
Split Screen item in the system menu. ZMail profile mode is
also a good example.
When you use a choose-variable-values window, you must specify
one or more variables with a list of specifiers. You pass the
list as an argument to
Each variable has a type which controls what values it may take on, the way the value is displayed and the way the user enters a new value. The type mechanism is extensible and is described in detail later. The types fall into two categories, those with a small number of legal values and those with a large or infinite number of legal values. The first kind of type displays all the choices, with the one which is the current value of the variable in bold-face. Pointing at a choice and clicking the mouse sets the variable to that value. Those types with a large number of legal values display the current value. Pointing at the value and clicking the mouse allows a new value to be entered from the keyboard. Rubbing out more characters than typed in restores the original value instead of changing it.
The variables themselves can be either symbols, which are effectively
examined and set as special variables in the calling program’s
process, or locatives, whose contents are examined and set. The
syntax for input and output is controlled by the binding of
readtable as usual.
Each line of the display is specified by an item, which can be one of the following:
:sexp; that is, its value may be any Lisp object. The name of the variable on the display is simply its print-name, and the value is stored as the value of the symbol.
nil, meaning that this line should have no variable name, but only a value. name is optional; if it is omitted it defaults to the print-name of variable, or to
nilif variable is a locative.
type is an optional keyword giving
the type of variable; if omitted it defaults to
:sexp. args are possible
additional specifications dependent on type.
It is possible to omit name and supply type since one is always
a string or
nil and the other is always a non-
For clarification of this, refer to the examples on (choose-variable-values-examples).
The following are the types of variables supported by default, along with any args that may be put in the item after the type keyword:
prin1, read with
:sexpexcept that the value is printed with
princ, read with
prin1and read with
read, but only a number is accepted as input.
time:print-universal-timeand read with
nilis printed as "never", and a number is printed using
time:print-universal-time. Input is read with
readline-trim; if the string is not "never" it is passed to
nilor a number of seconds. It is printed with
time:print-interval-or-neverand new values are read using
formatoperator), and is read as a single keystroke.
nilis also allowed as the value.
nildisplays as "none" and can be input via the
princand read with
fs:merge-pathname-defaults. If defaults is provided, it is a pathname or a defaults-alist to pass to
fs:merge-pathname-defaults. It can also be a symbol whose value should be used. If it is the same variable this item is setting, then each typed-in value is merged with the previous setting.
nilis also allowed as a value. It is read and printed as a blank line.
eq. All the choices are displayed, with the current value in boldface. A new value is input by pointing to it with the mouse and clicking. print-function is the function to print a value; it is optional and defaults to
:choosebut car of each element of values-list is what to display, while cdr is the value that goes in the variable.
:choose, but instead of a list of values there is item-list, which is a list of menu items (see (menu)). The usual menu mechanisms for specifying the string to display, the value to return, and the mouse documentation work with this.
nil. The choices are displayed as
This is the easy-to-use function interface to the choose-variable-values facility. It pops up a window displaying the values of the specified variables and permits the user to alter them. One or more choice boxes (as in the multiple-choice facility) appear in the bottom margin of the window. When the user clicks on the Exit choice box the window disappears and this function returns. The value returned is not meaningful; the result is expressed in the values of the variables.
The system chooses the dimensions of the window, and enables scrolling if there are too many variables to fit in the chosen height.
variables is a list whose elements can be special variables or the more general items described above. See the examples below.
options is the usual list of alternating option keywords and argument values. The following option keywords are allowed:
"Choose Variable Values".
nil(no function). The use of this function is described below ((c-v-v-function)).
tv:expose-window-near. The default is the list
:widthis not specified, this specifies the amount of extra space to leave after the current value of each variable of the kind that displays its current value (rather than a menu of all possible values). This extra space allows for changing the value to something bigger. The extra space is specified as either a number of characters or a character string. The default is ten characters. If
:widthis specified, then
*throwout. The default for
tv:mouse-sheet, or the superior of w if near-mode is
A choose-variable-values window optionally may have an associated function, which is called whenever the user commands the window to change the value of one of the variables.
This function can implement constraints among the variables. It is
called with arguments window, variable, old-value, and
new-value. The function should return
nil if just the
original variable needs to be redisplayed, or
t if no redisplay is
required; in this case it would usually
setq several of the
variables, then perform a
:refresh operation on the window.
Here are some examples of how to call
tv:choose-variable-values. The simplest
sort of thing you can do is:
(tv:choose-variable-values '(base ibase *nopoint) ':label "Number format parameters")
which displays the three variables’ names and values and lets the user change them. The same example can be done with nicer formatting with:
(tv:choose-variable-values '((base "Output Base" :number) (ibase "Input Base" :number) (*nopoint "Decimal Point" :assoc (("Yes" . nil) ("No" . t)))) ':label "Number format parameters")
The entry for
*nopoint would have been simply
(*nopoint "No Decimal Point" :boolean)
except that we wanted to reverse the sense of
We might even have used
if we wanted to use the name of the variable as the label rather than spelling it out.
For a hokier example, consider a grocery store. Suppose we have
*lettuce-types*, which contain lists of
strings indicating what is available,
*squash-type*, which indicates
whether we stock summer squash or winter squash, and
contains a floating-point number that is the current price of a gallon
of milk. Then the following expression would display the inventory and
allow it to be modified, using several different kinds of items:
(tv:choose-variable-values '("Meat Department" (*cuts-of-beef* "Beef" :string-list) (*cuts-of-pork* "Pork" :string-list) (*cuts-of-lamb* "Lamb" :string-list) "" "Produce" (*lettuce-types* "Lettuce" :string-list) (*squash-type* "Squash" :choose ("Summer" "Winter")) "" "Dairy" (*milk-price* "Milk" :documentation "Click left to raise the price of milk" :number)))
Note the use of strings to provide labels for the sections, and null strings to separate the sections with blank lines.
.defmethod tv:basic-choose-variable-values :decode-variable-type kwd-and-args
The system uses this operation on a choose-variable-values window when
it needs to understand an item. kwd-and-args is a list whose car
is the item’s type keyword and whose remaining elements, if
the arguments to that keyword. Six values are returned; these values
are described below. The default method for
looks for two properties on the keyword’s property list:
nilmay be omitted at the end.
You may add a new variable type to the standard set by putting one of
the above properties on the keyword.
You may define your own flavor of choose-variable-values window and give
:decode-variable-type method to make it not use the standard
variable types. This method must take care of implementing the
:documentation keyword, which can appear in an item where a variable type
would normally appear.
The six magic values are:
readis acceptable. If
nilis specified, there is no read-function and instead new values are specified by pointing at one choice from a list. If the read-function is a symbol, it is called inside a rubout-handler, and over-rubout will automatically leave the variable with its original value. If read-function is a list, its
caris the function, and it will be called directly rather than inside a rubout-handler.
nilif just the current value is to be printed. The choices are printed using the print-function, just as the current value is.
nil, it is given an element of the choice list and must return the value to be printed using the print-function.
nil, it is given an element of the choice list and must return the value to be stored in the variable.
Alternatively, this can be a symbol that is the name of a function. It will
be called with one argument, which is the current element of choices or
the current value of the variable if choices is
nil. It should return
a documentation string or
nil if the default documentation is desired. This
can be useful when you want to document the meaning of a particular choice,
rather than simply saying that clicking the mouse on this choice will select it.
Note that the function should return a constant string, rather than building
format or other string operations, because it will be called
over and over as long as the mouse is pointing at an item of this type.
The function is called by the who-line updating in the scheduler, not in the user process.
:booleanis defined thus:
(defprop :boolean (choose-variable-values-boolean-print nil (t nil)) choose-variable-values-keyword) (defun choose-variable-values-boolean-print (value stream) (funcall stream ':string-out (if value "Yes" "No")))
:any is defined with
(defprop :any (prin1 read) tv:choose-variable-values-keyword)
tv:choose-variable-values may not be adequate
if you wish to keep the window permanently exposed or if you wish
to alter its behavior. Then you must create a window yourself.
Here are the pertinent flavors.
.defflavor tv:basic-choose-variable-values (
This is the basic flavor which makes a window implement the choose-variable-values
facility. It is not instantiable.
.defflavor tv:choose-variable-values-window (
This is a choose-variable-values window with a reasonable set of
features, including borders, a label at the top, stream I/O, the
ability to be scrolled if there are too many variables to fit in the
window, and the ability to have choice boxes in the bottom margin.
.defflavor tv:choose-variable-values-pane (
tv:choose-variable-values-window designed to be a pane of a constraint frame.
It redefines the
:adjustable-size-p operation to return
on the assumption that the window’s size has been specified by the frame
and cannot be changed except by the frame.
.defflavor tv:temporary-choose-variable-values-window (
tv:choose-variable-values-window that is equipped to pop up temporarily.
.defresource tv:temporary-choose-variable-values-window &optional (superior tv:mouse-sheet)
This is a resource of such windows, from which
tv:choose-variable-values gets a window to use.
There are two main styles of use: to create a window giving all of
the parameters in the init-plist, or to create a window without
specifying the parameters, and then use the
:setup operation (see
below) to set the parameters before using the window.
But in any case, you must specify the list of variable-specifiers
(see (c-v-v-specs)) and the stack group to evaluate variables in
before you can use the window.
The following init options are available:
.definitoption tv:basic-choose-variable-values :variables specifier-list Initializes the list of variable-specifiers, telling the window which variables to display and how to read and print the values. .end_definitoption
.definitoption tv:basic-choose-variable-values :function fcn
Initializes the associated function (see (c-v-v-function)), the
function called when the window changes the value of one of the
variables it displays. The default is
nil (no function).
.definstvar tv:basic-choose-variable-values tv:function The window’s associated function. .end_definstvar
.definitoption tv:basic-choose-variable-values :stack-group sg
The stack group in which the variables whose values are to be chosen
are bound. The window needs to know this so that it can get the values
while running in another process, for instance the mouse process,
in order to update the window display when it is refreshed or scrolled.
If you do not specify the stack group at this time, you must specify it
:setup operation, before you can use the window.
.definstvar tv:basic-choose-variable-values tv:stack-group The stack group in which variables’ values should be evaluated. .end_definstvar
.definitoption tv:basic-choose-variable-values :name-font font The font in which names of variables are displayed. The default is the system default font. .end_definitoption
.definitoption tv:basic-choose-variable-values :value-font font The font in which values of variables are displayed. The default is the system default font. .end_definitoption
.definitoption tv:basic-choose-variable-values :string-font font The font in which items that are just strings (typically heading lines) are displayed. The default is the system default font. .end_definitoption
.definitoption tv:basic-choose-variable-values :unselected-choice-font font The font in which choices for a value, other than the current value, are displayed. The default is a small distinctive font. .end_definitoption
.definitoption tv:basic-choose-variable-values :selected-choice-font font The font in which the current value of a variable is displayed, when there is a finite set of choices. This should be a bold-face version of the preceding font. The default is the bold-face version of the default unselected-choice font. .end_definitoption
.definitoption tv:choose-variable-values-window :margin-choices choice-list
The default is a single choice box, labeled "Done".
See (margin-choice) for the details of what you can put here.
Note that specifying
nil for this option will suppress the margin-choices entirely.
If no dimensions are specified in the init-plist, the width and height will be automatically chosen according to the other init-plist parameters. The height is dictated by the number of variables to be displayed. Specifying a height in the init-plist, using any of the standard dimension-specifying init-plist options, overrides the automatic choice of height.
Choose-variable-values windows provide these operations:
.defmethod tv:choose-variable-values-window :setup items label function margin-choices
Changes the list of items (variables), the window label, the constraint function,
and the choices in the bottom margin, and sets up the display. Also remembers
the current stack-group as the stack-group in which the variables are bound.
If the window is not exposed (more generally, if the
:adjustable-size-p operation on the window returns non-
this reshapes the window to a good size based on the specified items.
.defmethod tv:choose-variable-values-window :set-variables item-list &optional dont-set-height Sets the list of variable-specifiers which controls the variables displayed in the window, then redisplays the window.
dont-set-height is supplied non-
nil, the height of the window
will be adjusted according to the number of lines required. If more
than 25. lines would be required, 25. lines will be used and scrolling
will be enabled. The
:setup operation uses
:set-variables to do part of its work.
.defmethod tv:choose-variable-values-window :adjustable-size-p
If this returns non-
:setup will reshape the window.
By default, this operation returns non-
nil when the window is
.defmethod tv:choose-variable-values-window :appropriate-width &optional extra-space
Returns the inside-width appropriate for this window to accommodate the current
set of variables and their current values. Use this operation after a
and before a
:expose, and use the result to do a
The returned width will not be larger than the maximum that will fit inside the
If extra-space is supplied, it specifies the amount of extra space to leave after the current value of each variable that displays its current value (rather than a menu of all possible values). This extra space allows for changing the value to something bigger. The extra space is specified as either a number of characters or a character string. The default is to leave no extra space. .end_defmethod
.defmethod tv:choose-variable-values-window :redisplay-variable variable Redisplays just the value of that variable. .end_defmethod
In the simplest mode of operation, you call the
function, which takes care of creating the window and all necessary communication
with it. When you make your own choose-variable-values window, you need to
handle the communication yourself, using the information given below. An
example of a situation in which this is necessary is when you have a frame, some
panes of which are choose-variable-values windows.
A choose-variable-values window handles mouse clicks by putting blips
(lists) in its input buffer. These blips are generated by the mouse
process and are supposed to be read in the controlling process. There
are two types of blip, both used for specific purposes, and your
program must be able to take the appropriate actions when it reads
them. The easy way for you to do this is to call the function
tv:choose-variable-values-process-message, which is provided just
for this purpose.
.definitoption tv:choose-variable-values-window :io-buffer io-buffer The I/O buffer to be used for blips and for ordinary input from the window. .end_definitoption
The following forms of list are inserted as blips into the input buffer:
This function implements the proper response to the above blips. It should be called in the process and stack-group in which the variables being chosen are bound. window should be the choose-variable-values window and blip should be the object read as input.
This function returns
nil except in the case where
blip indicates a click on a "Done" choice box.
If blip says that the user clicked on a variable, this function reads user input from the window as necessary and sets the variable.
If blip is a
:choice-box blip, the action depends
on the box in it. If the sixth element of box is
nil, which is normally the case for the "Done" box,
this function returns
t. Otherwise, the sixth element
of box is evaluated, but this function returns
If blip is actually a character rather than a blip,
it is ignored unless it is a
Clear-screen, in which
case the choose-variable-values window is refreshed.
Therefore, it is reasonable to use this function with a loop
(do () ((tv:choose-variable-values-process-message c-v-v-window (progn (process-wait "Choose" c-v-v-window ':listen) (send c-v-v-window ':any-tyi)))))
There is a facility, based on the choose-variable-values facility, for keeping track of options to a program of the sort that a user would specify once and keep in his init file. Special forms are provided for defining options, and there are functions for putting all the options into a choose-values window so that the user can alter them, for writing the current state of the options into an init file, and for resetting all the options to their default initial values.
Defines name a special variable whose value is a "user option alist", something which may be used by the other functions below. This alist will keep track of all of the option variables for a particular program.
The simplest usage is
(define-user-option-alist name), which just defines name.
(define-user-option-alist name constructor) specifies in addition the name
of a constructor macro to be defined, which provides a slightly different way of defining an
option variable from
defvar-user-option. The form
(constructor option default name type args...) will define
an option in this user-option-alist. The arguments are the same as the
similarly-named arguments to
A third argument may be used to specify a documentation string for the
variable name. To specify a documentation string and no constructor,
nil for the constructor.
Defines an option and adds it to a user option list.
(defvar-user-option option default documentation alist name type args...)
defines the special variable option
to be an option in the alist, which must have been previously
define-user-option-alist. The variable is declared and initialized
(defvar option default documentation). The value of the form default
is remembered so that the variable can be reset back to it later.
type is the type of the variable for purposes of the choose-variable-values facility.
It is optional and defaults to
:sexp. args, which are evaluated (at the time
the definition is done), are the arguments for the type keyword used.
name is the name of the variable to be displayed in the
choose-variable-values window. If it is omitted or
nil, the default
(string-capitalize-words (get-pname option)); except that
when the first and last characters of the print-name are asterisks, they
are removed. E.g. the default name for
"Sunny Side Up".
(defvar-user-option preferred-radix 8 "Radix to use for files that don't specify one." my-program-option-alist "Preferred radix" :assoc '(("8" 8) ("10" 10.)))
This is like
defvar-user-option, except that instead of an initial
value a site option keyword is specified. Instead of a default value,
you specify the name of a site option (a keyword). The actual default
value is the value of that site option in the current
site table. Loading a new site table resets the option.
Defines a user option whose possible values are controlled by site options.
(defvar-site-alist-user-option option default documentation alist name menu-alist)
defines option as a user option on alist, like
defvar-site-user-option. The type for
:menu-alist, and the list of menu items to be used is
determined from the site table according to menu-alist.
menu-alist is a symbol whose value is a menu alist, a list of menu
items. These items are the alternatives offered to the user, as in the
:menu-alist type of variable. However, each menu item specifies a
site option keyword, and that alternative is available to the user only
if that site option currently has a non-
The menu item can specify the controlling site keyword using the
:site-keyword, as in
("Foo" :value :foo :site-keyword :foo-present)
If this is not done, the menu item’s value-to-return is also the site keyword.
default is the name of a site keyword whose value specifies the
default. This site option’s value is matched against each menu item,
comparing it against the value of the modifier keyword
:default-site-keyword, or, if that is not present, against the menu
item’s site keyword name. The first match is the default alternative.
"Foo" will be the default alternative if the default
site option’s value is
If default is
nil, then the first available menu alist item is
also the default.
Displays the values of the option variables in alist to the user and
allows them to be altered. The options are passed along to
tv:choose-variable-values. Note that alist is an actual alist,
not a symbol whose value is an alist.
Each of the option variables in alist is reset to its default initial value.
Specifies that the user option variable option is significant only if the site tables for your site do (or, if they do not) contain one of the specified site-options.
restriction-type is either
:unless. If it is
:if, the option should be mentioned in the choose-variable-values
window only if one of the specified site options is present in the
currently loaded site table.
:unless means that the option should
be offered only if none of the specified site options is loaded.
Each option may have an
:if restriction and an
Elimination of options from an alist according to their restrictions
is done by
tv:prune-user-option-alist, calling which is up to you.
restriction-type may also be
:never. Then the option is never
offered to the user to change, but it will still be reset and
written with the other options.
Returns an alist containing only some of the elements of alist, lacking those that are suppressed by restrictions, or that offer only a single alternative. (The latter is likely to happen with a site-menu-alist user option if a given site allows only one of the possible alternatives.)
For each option variable in alist whose current value is not
its default initial value, a form is printed to stream that will set the
variable to its current value. The form uses
login-setq so it is appropriate
for putting into an init file.
The mouse-sensitive items facility is a feature somewhat related to the choice
facilities described above. It is similar in its appearance to the
user, but quite different in the way it is interfaced to by a program.
tv:basic-mouse-sensitive-items into a window flavor equips
the window with mouse-handling according to the paradigm described in
this section. Mouse-sensitive items are something you use when defining
your own window, rather than a complete, stand-alone facility, and
consequently do not have an "easy to use" functional interface.
For an example of mouse-sensitive items, try the
C-X C-B (List Buffers)
command in the editor. Try moving the mouse over the list of buffers
and clicking the right-hand button.
The word "typeout" appears here and there in the mouse-sensitive items facility for historical reasons. Often mouse-sensitive items are typed out on top of some other display, such as an editor buffer. However, the mouse-sensitive-item facility has nothing to do with the typeout-window facility. At this point it would be a fairly big incompatible change to fix this.
.defflavor tv:basic-mouse-sensitive-items Mixing this flavor into a window provides for areas of the screen which are sensitive to the mouse. Moving the mouse into such an area highlights the area by drawing a box around it. At this point clicking the mouse performs a user-defined operation. This flavor is called basic because it fixes the handling of the mouse by the window; it will not work to mix it with another flavor that expects to define some other kind of mouse handling. However it is less basic than many basic flavors in that it does not do anything special with the displayed image of the window. .end_defflavor
A mouse-sensitive item has a type, which is a keyword which controls what you can do to it, an item, which is an arbitrary Lisp object associated with it, and a rectangular area of the window. Typically something is displayed in that area at the same time as a mouse-sensitive item is created, using normal stream output to the window. Unlike things such as menu items, these mouse-sensitive items are not a permanent property of the window; they are just as ephemeral as the displayed text and go away if you clear the window or if typeout wraps around and types over them. Of course, if you don’t type out more items and text than fit in the window, and never clear the window, then they will be permanent.
Associated with each type is a set of operations that are legal to perform on
items of that type. One of these operations is selected as the default. The
tv:item-type-alist instance variable is an alist that defines
these. This alist is composed of elements of the following form:
(type left-button-alternative documentation (string . alternative) ;A menu item (string :value alternative) ;Another menu item menu-item...) ;More of them
documentation is the string to be displayed in the who line while the mouse is pointing at an item of this type. The menu items may also have documentation strings in them. documentation may also be a list of the form
where doc-function is a function that, when applied to a mouse-sensitive item, returns a documentation string, and label-function is a similar function that returns a string to use as the menu label, to identify the item that the menu is going to apply to.
Here is part of the item type alist used in typeout windows of editor windows:
((zwei:directory zwei:directory-edit-1 "Left: Run DIRED on this directory. Right: menu of View, Edit." ("View" :value zwei:view-directory :documentation "View this directory") ("Edit" :value zwei:directory-edit-1 :documentation "Run DIRED on this directory.")) (zwei:file zwei:find-defaulted-file "Left: Find file this file. Right: menu of Load, Find, Compare." ("Load" :value zwei:load-defaulted-file :documentation "LOAD this file.") ("Find" :value zwei:find-defaulted-file :documentation "Find file this file.") ("Compare" :value zwei:srccom-file :documentation "Compare this file with the newest version.")) (zwei:flavor-name zwei:edit-definition-for-mouse "Left: Edit definition. Right: menu of Describe, Edit." ("Describe" :value zwei:describe-flavor-internal :documentation "Describe this flavor.") ("Edit" :value zwei:edit-definition-for-mouse :documentation "Edit definition.")))
When an item is clicked on with the mouse, a blip which is a list of the form
(:typeout-execute alternative item)
is placed in the window’s input buffer. item is the datum supplied when the item was constructed, whose purpose is to identify which item was clicked on, and alternative is obtained by looking up the type of the item in the window’s item-type-alist.
If the item is clicked on
with the left mouse button, the left-button-alternative is used in the
:typeout-execute blip. If the item is clicked on with the right
button, the menu items are put into a menu, and the user chooses one.
The value returned by the
:choose operation is used as the
alternative in the
:typeout-execute blip. Clicking on an item
of a type that is not one of the alternatives in the item-type-alist
Load alternative on a file item in the editor, the
blip might be
(:typeout-execute zwei:load-defaulted-file #fs:logical-pathname "SYS: SYS; QFCTNS LISP")
.defmethod tv:basic-mouse-sensitive-items :item-type-alist .defmethod1 tv:basic-mouse-sensitive-items :set-item-type-alist new-item-type-alist Return or set the item type alist of the window. .end_defmethod
.definitoption tv:basic-mouse-sensitive-items :item-type-alist alist Initializes the item type alist of the window. .end_definitoption
The special form
(tv:add-typeout-item-type alist type name function default-p documentation)
is used to declare information about a mouse-sensitive item type by
adding an entry to an alist kept in a special variable. This alist can
then be put into the item-type alist of a mouse-sensitive window, for
instance using the
:item-type-alist init-plist option. Note that
each possible alternative for a particular mouse-sensitive item type is
defined with a separate
tv:add-typeout-item-type form; this allows
each alternative to be defined at the place in the program where it is
implemented, rather than collecting all the alternatives into a separate
table. It also allows new alternatives to be added in a modular
alist is the special variable containing the alist. You should
defvar it to
nil before defining the first item type. Each program that
uses mouse-sensitive items has its own alist of item types, so that there is no
conflict in the names of the types. type is the keyword symbol for the type
being defined. name is the string that names the operation and
alternative is the representation of the alternative (the object to
be put in the second element of the
default-p is optional; if it is supplied and non-
means that this operation is the default performed when you click the left
button on an item of this type. documentation is optional but highly
recommended; it is a string that documents what function does. When the
user points the mouse at an item of this type, the documentation line at the
bottom of the screen will give the documentation for the default function
(reachable by the left button) and a list of the functions in the menu
(reachable by the right button). If the user clicks right, calling for a menu,
then the documentation for whichever function in the menu he points the mouse at
will be displayed.
alist, type, and function are not evaluated. name, default-p, and documentation are evaluated.
In the editor, alternative is interpreted (when a
:typeout-execute blip is read) as a function to be called, and the
tv:add-typeout-item-type form is typically placed right before the
function definition of alternative.
These are the operations used to print items on a window.
.defmethod tv:basic-mouse-sensitive-items :item type item &rest format-args
A new item item of type type is printed, either by calling
format with format-args, or by
princ’ing item if
The mouse-sensitive area of the item is whatever space is used up by printing it, as judged by the motion of the cursor.
The arguments item and type is not necessarily used in printing
the item, but they are used in handling a click on the item. type
is used to look up a function in the item type alist, and item is
placed directly into the
(send standard-output ':item 'zwei:file pathname)
in the editor, where
standard-output is a window that supports
mouse-sensitive items, will
princ the value of
pathname and make
an item of type
zwei:file whose datum is that pathname.
.defmethod tv:basic-mouse-sensitive-items :primitive-item type item left top right bottom
.defmethod1 tv:basic-mouse-sensitive-items :primitive-item-outside type item left top right bottom
This operation is used to define a mouse-sensitive item without printing
it. (Presumably you print it yourself, either before or after.) The
type and item are used as in the
:item operation. The
remaining arguments are coordinates that describe the four edges of the
:primitive-item, the four coordinates are relative to the inside
top left corner of the window (that is, they are cursor positions such
:read-cursorpos would return). In
they are relative to the outside corner of the window (like values of
the instance variables
.defmethod tv:basic-mouse-sensitive-items :item-list type list
Several items are printed, arranged neatly in columns, one for each
element of list. An element of list can be either a string or a
(name . item). In the latter case, name (typically
a string) is printed with princ, and item is used as the datum
for the item. If the element is an atom, that atom serves both to be
princ’d and used as the datum. All the items are of type type.
.defmethod tv:basic-mouse-sensitive-items :mouse-sensitive-item x y
Returns a list describing the mouse-sensitive item found at cursor
position x, y in the window, or
nil if there is none there.
The list looks like this:
(type item left top right bottom)
The type and item are as specified in the
and the coordinates are cursor positions (that is, relative to the
outside top left corner of the window).
A window can be augmented with choice boxes (see (choice-box)) in its
bottom margin using the flavor
tv:margin-choice-mixin. These give
the user a few labeled mouse-sensitive points that are independent of
anything else in the window.
Margin choices are not a complete, stand-alone choice facility and consequently do not have an "easy to use" functional interface.
For an example of a window with margin choices (as well as choice boxes in its interior),
try the editor command
Meta-X Kill or Save Buffers.
Puts choice boxes in the bottom margin, according to a list of choice-box descriptors
which can be specified with the
:margin-choices init-plist option
:set-margin-choices operation. A choice-box descriptor is a list,
(name state function x1 x2). It is legal to use a longer
list as a choice-box descriptor and store your own data in the additional elements.
name is a string that labels the box. state is
t if the
box has an "X" in it,
nil if it is empty. x1 and x2 are
used internally to remember where the choices boxes are; they are always
spread out evenly in the available width.
function is a function that is called in a separate process if the user
clicks on the choice box. It receives three arguments: the choice-box descriptor
for the choice box, the "margin region" that contains the choice boxes, and the
y-position of the mouse relative to this window. You probably want to
ignore the last two arguments. When function is called,
self is bound
to the window, so function may use
flavor)) to access the window’s instance variables.
The structure access functions
may be of use inside function (they are just more specific names for
cadr). If function changes the state of the choice box, it will
need to refresh the choice boxes by doing
(funcall (tv:margin-region-function region) ':refresh region)
where region is its second argument, which is why that argument is passed.
tv:margin-region-mixin as an
included flavor; this means approximately that
tv:margin-region-mixin will appear in any combination right after
tv:margin-choice-mixin if it is not explicitly specified to appear
somewhere else. The position of
where the choice boxes appear in relation to the other margin items
(borders, labels. etc). See (margins).
.definitoption tv:margin-choice-mixin :margin-choices choices
choices is a list of choice-box descriptors, described above.
A line of choice-boxes will appear in the bottom margin of the window.
If choices is
nil, there will be no choice boxes and no
space for them in the bottom margin; however, the window will still be
capable of accepting the
:set-margin-choices operation to create
a line of choice boxes later.
.defmethod tv:margin-choice-mixin :set-margin-choices choices
Changes the set of margin choices according to choices, which is
to turn them off or a list of choice-box descriptors, described above.
If the choice boxes are turned on or off, the size of the window’s bottom
margin will change accordingly.
.definstvar tv:margin-choice-mixin tv:margin-choices
A list of margin choices, or
To get a menu with margin choices, it is best to use
which goes to a little extra trouble to interface the margin choices to
Typeout windows are a facility provided to make it easier for a program that normally displays a single updating picture to print a stream of unrelated output from time to time.
For example, Zmacs windows normally present a continuously updated display of an
editor buffer. But some editor commands are designed to print output,
such as a directory listing from
Control-X Control-D or a list of
Control-X Control-B. This output cannot conveniently
be printed on the editor window itself, since that window is set up to
maintain its standard display of an editor buffer and is no longer suitable
for displaying anything else. Instead, the output is printed on a special
kind of window called a typeout window, which exists as an inferior
of the editor window. Other programs that maintain updating displays, such
as the inspector and Peek, also use typeout windows for this purpose.
A typeout window is an inferior of another window such as the editor or Peek display window, and "grows" over its superior as output is done on it. The output starts at the top of the typeout window, which is also the top of its superior, and proceeds downward. The typeout window always keeps track of how far down output has proceeded, so that the superior window can eventually find out how much of its permanent display has been clobbered by the typeout window and therefore needs to be redisplayed. A horizontal line or "window shade" appears just below the point of lowest output, to enable the user to separate the typeout from the remains of the permanent display. If output to the typeout window proceeds far enough, it wraps around to the top of the screen. Then the typeout window records that the entire superior has been clobbered and no longer displays any horizontal line.
.defflavor tv:basic-typeout-window This is the base flavor for all kinds of typeout windows. It is actually just a mixin, not instantiable by itself. .end_defflavor
.defflavor tv:typeout-window (
This is the flavor normally used for actual typeout windows.
.defflavor tv:typeout-window-with-mouse-sensitive-items (
This flavor of typeout window also provides the
for including mouse-sensitive rectangles among the typeout.
.defmethod tv:basic-typeout-window :bottom-reached Returns the greatest y-position clobbered by the typeout window. This is a cursor position, relative to the typeout window. The horizontal line (typeout window border), when enabled, appears at this position, provided it is not zero or equal to the inside bottom of the window.
The value is
nil when the typeout window is not active.
The typeout window has an instance variable
but this method does not simply return the value of the instance variable.
When this variable is non-
nil, a horizontal line is used to indicate
the bottom of the area used by the typeout window. No line appears when the
typeout window has used its entire area (if it has wrapped around or done a
When this variable is
nil, the horizontal line does not appear.
The default value is
A typeout window is deactivated when not in use. Any attempt
to output to it automatically activates and exposes it because
its deexposed-typeout-action is
.defmethod tv:basic-typeout-window :expose-for-typeout Sent in order to prepare the typeout window to be typed out on. The typeout window marks itself "exposed" while leaving the bits of its superior on the screen. It initializes itself as "empty" and its bottom-reached as zero. It also finds a suitable ancestor and makes itself that ancestor’s selection substitute. In normal use, this typically causes the typeout window to become selected. .end_defmethod
.defmethod tv:basic-typeout-window :active-p
nil if the typeout window is active, which is the case
if and only if typeout is currently visible in it.
Exposing the typeout window automatically causes it to become the
selection substitute of one of its ancestors (see
(selection-substitutes)). Just which ancestor is determined according
to the situation; it is the nearest ancestor in the existing path of
selection substitutes. This is the nearest ancestor that can be used
for the purpose and actually make the typeout window be selected. It is
the typeout window’s direct superior only if that superior is selected.
For example, if you type
Meta-X in Zmacs and then type
help message will print on the main editor window’s typeout window, but
that editor window is not selected (the minibuffer is). So the typeout
window will substitute for the editor frame rather than for the
nonselected editor window immediately above it.
When the program wants to make the typeout go away and put back its
standard display, it must first deactivate the typeout window
When the typeout window is deactivated, it sends a
:remove-selection-substitute message to whichever ancestor it had
decided to substitute for. As a result, if the typeout window is still
that ancestor’s selection substitute, the substitute is set back to what
it had been before the typeout window was exposed. If the ancestor’s
substitute has been changed since then, it is left alone.
The purpose of making the typeout window a selection substitute is primarily to make its cursor blinker blink. A typeout window by default shares the input buffer of its superior, so which of them is selected has no effect on reading keyboard input. A separate feature of typeout windows turns the superior’s blinkers off completely while the typeout is exposed.
To make a window possess an inferior typeout window, include the flavor
tv:essential-window-with-typeout-mixin in it. This causes a typeout
window to be created and provides the methods to handle communication
with the typeout window.
.defflavor tv:essential-window-with-typeout-mixin This is the basic mixin that gives a window the ability to manage a typeout window as its inferior. .end_defflavor
.defflavor tv:window-with-typeout-mixin (
This is what you typically use, rather than
tv:essential-window-with-typeout-mixin, because it prevents screen
management of this window’s inferiors from getting in the way of the
operation of the typeout window.
.definstvar tv:essential-window-with-typeout-mixin tv:typeout-window This window’s typeout window. .end_definstvar
.defmethod tv:essential-window-with-typeout-mixin :typeout-window
Returns the value of the instance variable
tv:typeout-window, which is the
typeout window associated with this window.
.definitoption tv:essential-window-with-typeout-mixin :typeout-window (flavor-name options...)
This init option specifies what kind of typeout window to create. The
car of the value is the name of flavor of typeout window to use, and the
cdr is a list of alternating options and values to pass to
If the option is not specified, or is
nil, no typeout window is actually created.
tv:basic-typeout-window flavor provides for daemons and wrappers
that cause the
:mouse-buttons messages to get
passed either to the typeout window or to its superior, depending on
whether the typeout window has grown down to where the mouse is.
.defmethod tv:essential-window-with-typeout-mixin :turn-on-blinkers-for-typeout
Sent to the superior of a typeout window when the mouse moves into
an area that the typeout window is not using,
this operation should make visible any blinkers that are associated
with the use of the mouse. The definition actually provided
by the flavor
tv:essential-window-with-typeout-mixin does nothing;
this operation exists so that you can add daemons to it.
.defmethod tv:essential-window-with-typeout-mixin :turn-off-blinkers-for-typeout
Sent to the superior of a typeout window when the mouse moves into the
area used by the typeout window, this operation should turn off any
blinkers that were turned on by
definition actually provided by the flavor
tv:essential-window-with-typeout-mixin does nothing; this operation
exists so that you can add daemons to it.
A typeout window does **MORE** processing if and only if that is
enabled for its superior. The usual motivation for using a typeout
window is that the superior is to be used for something other than
sequential output; therefore, **MORE** processing is usually not
desired on the superior. However, it is not desirable to simply disable
**MORE** processing for the superior because this disables it for
the typeout window as well and because the user could reenable it for
both windows with
.defmethod tv:basic-typeout-window :more-p
.defmethod1 tv:basic-typeout-window :set-more-p new-more-p
These operations are passed along to the superior, so that the user who
Terminal M command need not be aware of the distinction
between the typeout window and its superior.
This mixin, intended for use in superiors of typeout windows,
prevents **MORE** processing unconditionally without saying that
it is "disabled". Programs and the user can think they can enable and disable
**MORE** processing for the window using the the
:set-more-p operations, and the
Terminal M command, but only
the typeout window is affected.
An alternative way to accomplish this is as follows:
(defmethod (my-display-window-with-typeout-window :more-exception) () (setf (tv:sheet-more-flag) 0))
The typeout window superior must know how to check
before redisplaying to find out whether part of its last display
has been overwritten by the typeout window and therefore must be
redisplayed. To find out how much screen height the typeout window
has used, use the
:bottom-reached operation on it.
The typeout window must also be deactivated so that more typeout,
happening after the redisplay, will work properly.
Here is an example which is how general scroll windows do this:
(defmethod (tv:scroll-window-with-typeout-mixin :before :redisplay) (&rest ignore) (when (funcall tv:typeout-window ':active-p) (let ((br (min tv:screen-lines (1+ (truncate (send tv:typeout-window ':bottom-reached) tv:line-height))))) ;;
bris the number of lines of our display ;; that were clobbered by typeout. (funcall tv:typeout-window ':deactivate) (dotimes (l br) ;; Mark lines as clobbered (aset nil tv:screen-image l 0) (aset -1 tv:screen-image l 1) (aset -1 tv:screen-image l 2)) ;; Erase the clobbered area. (send self ':draw-rectangle (tv:sheet-inside-width) (* br tv:line-height) 0 0 tv:alu-andca))))
The editor normally updates its display after each command. But after a command that prints typeout, it is important not to update the permanent display right away, because that would make the typeout disappear almost as soon as it appeared. The same consideration applies to other programs that use typeout windows.
The convention in this situation is that after a command that has produced typeout, redisplay should be delayed until the user types another input character. If that character is a space, it is discarded. Otherwise, it is interpreted as a command.
The way the program should decide whether to wait before redisplaying
is to invoke the
:incomplete-p operation on typeout window.
This reads a flag that is set whenever output is done on the typeout
window and can be cleared by the program’s command loop between commands.
Thus, the flag indicates whether the typeout window was used during the
Here is a sample piece of code that illustrates this technique:
(let ((standard-output typeout-window)) (do-forever ;; Clear the flag. (send standard-output ':make-complete) ;; Read and execute one command. (process-command (send standard-input ':tyi)) (when (send standard-output ':incomplete-p) ;; If this command printed some typeout, ;; delay redisplay by waiting for next input char. (let ((ch (send standard-input ':tyi))) (unless (eq ch #\sp) ;; Anything but
Space, execute as a command. ;; Since
Spaceis not untyi'd, it allows ;; immediate redisplay. (send standard-input ':untyi ch)))) ;; Here is where we redisplay after each command. (unless (send standard-input ':listen) ;; Normal redisplay must deactivate the typeout window; ;; see the previous example. (redisplay-normal-display))))
Note that this command loop follows the editor’s practice
of not redisplaying when there is input available.
As a result, when the character read is not a
:untyi causes redisplay to be prevented by the
presence of input. Then the same character is read again
at the top of the loop and processed as a command.
If this command too prints typeout, its typeout will
add on to that already on the typeout window.
If this command does not print typeout, the old typeout
will be erased after it is done.
.defmethod tv:basic-typeout-window :incomplete-p
Returns the window’s incomplete-flag:
t if the command loop should
wait for the next character before deactivating the typeout window.
.definstvar tv:basic-typeout-window tv:incomplete-p
The window’s incomplete-flag:
t if the command loop should wait for
the next character before deactivating the typeout window.
.defmethod tv:basic-typeout-window :make-complete Clears the incomplete-flag. The command loop can use this to clear the flag after examining it.
Certain functions such as
fquery perform this operation on the I/O
stream to tell the program not to wait before redisplaying, as it
normally would do. The idea is that the
fquery question is not
worth preserving on the screen once the user has answered it.
.defmethod tv:basic-typeout-window :make-incomplete Sets the incomplete-flag. All the standard output stream operations also do this. .end_defmethod
Text scroll windows provide a simple means of maintaining a display of a number of lines of the same type with scrolling. For example, they are used by the inspector to display the slots of a structure. (See (scroll-windows) for a more general kind of scroll window.)
.defflavor tv:text-scroll-window This is the base flavor for all kinds of text scroll windows. It is not instantiable by itself. .end_defflavor
A text scroll window updates its display based on a sequence of
items. Each item generates one line of display. An item can be any
Lisp object, and how it displays is controlled by how you define the
:print-item operation. For example, you could define this operation
to do a
:string-out; then the items would have to be strings. By
:print-item uses the function
prin1, so each item is a
Lisp object to be printed.
.defmethod tv:text-scroll-window :print-item item line-no index Displays item, which should be the indexth item of those currently displayed, at the current cursor position in the window, which should be on line number line-no of the window.
This operation is the primitive used by all other text scroll window
operations to do output of items. As defined by
tv:text-scroll-window, it just does
prin1 of item,
ignoring the other arguments. Other flavors built on
tv:text-scroll-window are expected to redefine this operation.
In any case, no item may print out as more than one line. This is enforced by truncating output at the margin. .end_defmethod
In simple use, you specify an array of items to be displayed, or a list of items (which is converted into an array). Items are referred to sometimes by their indices in the array. A more sophisticated technique is to specify an item generator, which is a function that simulates the effect of a possibly very large array of items without requiring you to actually create the array.
.definstvar tv:text-scroll-window tv:items The array whose elements are the items to be scrolled through. The index of an item in this array is called the index of the item. This array contains the entire set of items to be scrolled through, not just those that are on the screen at any time. .end_definstvar
.definstvar tv:text-scroll-window tv:top-item The index of the first item currently being displayed (on the first line of the window). This is how the current scroll position is remembered. .end_definstvar
tv:text-scroll-window provides these operations:
.defmethod tv:text-scroll-window :items Returns the window’s array of items. .end_defmethod
.defmethod tv:text-scroll-window :set-items new-items Sets a new array of items. new-items may be a suitable array (it should have a fill pointer), or a list of items (an array is made from it), or a number of items (the array is made that long, but initially empty).
The item-generator of the window is set to
nil, turning off that feature,
so that the array of items will actually be used.
.defmethod tv:text-scroll-window :top-item .defmethod1 tv:text-scroll-window :set-top-item new-top-item The top-item is the index of the item to be displayed on the first line of the window. .end_defmethod
.defmethod tv:text-scroll-window :number-of-items Returns the number of items this window is currently scrolling through. .end_defmethod
.defmethod tv:text-scroll-window :number-of-item item Returns the item number (index) of item. .end_defmethod
.defmethod tv:text-scroll-window :item-of-number index Returns the item at index index. .end_defmethod
.defmethod tv:text-scroll-window :last-item Returns the value of the last item to be scrolled through (that is, the one whose index is one less than the number of items). .end_defmethod
.defmethod tv:text-scroll-window :put-item-in-window item .defmethod1 tv:text-scroll-window :put-last-item-in-window Scroll the window so that the specified item, or the last item, appears on the screen. The argument item is an item value, not an index. .end_defmethod
.defmethod tv:text-scroll-window :delete-item index Modifies the list of displayable items, removing the item at index, and updates the screen if that index is within the portion currently displayed. .end_defmethod
.defmethod tv:text-scroll-window :insert-item index item .defmethod1 tv:text-scroll-window :append-item item Add a new item item to the list of items to be displayed, either at index index (before the item currently at that index) or at the end. .end_defmethod
The following auxiliary operations are also defined.
.defmethod tv:text-scroll-window :redisplay start end
This is the internal function that causes a
:print-item message to get sent
for each line in the range start to end, which are screen line indices.
It should not be redefined, but daemons may be placed on it to note changes in the
.defmethod tv:text-scroll-window :scroll-redisplay new-top delta
This is the internal scrolling function that causes partial redisplay with bitblting
and then sends a
:redisplay message for the rest. new-top is the new
tv:top-item, and delta the number of lines actually to be scrolled.
This operation should not be redefined, but daemons may be placed on it.
:new-scroll-position are also defined for interface with the scroll bar.
Other scrolling commands can also use them.
Function text scroll windows provide for you to change dynamically the function used to display items. These windows have an instance variable which holds the function to be used. The inspector uses this feature so that each data type you can inspect can be handled in an independent manner, with its own conventions for what an item means.
.defflavor tv:function-text-scroll-window (
An instantiable function text scroll window.
.definstvar tv:function-text-scroll-window tv:print-function This is the function to be called to display an item. See (print-function-example) for an example of a print function, taken from the inspector. .end_definstvar
.definstvar tv:function-text-scroll-window tv:print-function-arg
This is an additional argument to be passed to the print function.
The print-function’s complete list of arguments are
the item itself, the value of
tv:print-function-arg, the window,
and the item number.
.definitoption tv:function-text-scroll-window :print-function function .definitoption1 tv:function-text-scroll-window :print-function-arg arg Initialize the corresponding instance variable. .end_definitoption
.defmethod tv:function-text-scroll-window :print-function .defmethod1 tv:function-text-scroll-window :print-function-arg .defmethod1 tv:function-text-scroll-window :set-print-function function .defmethod1 tv:function-text-scroll-window :set-print-function-arg arg Get or set the corresponding instance variable. .end_defmethod
.defmethod tv:function-text-scroll-window :setup list list is a list of the form
(print-function print-function-arg (item...) top-item-number label item-generator)
As you can see, it specifies everything relevant to telling the window
what items to display and how to display them.
label is passed to the
It is not useful to specify both a list of items and a non-
item-generator, since the list of items is not used if the
item-generator is non-
The display is updated by this operation. .end_defmethod
Since a text scroll window updates a display according to a fixed
pattern, it is often useful for it to have an inferior which is a
typeout window, for the sake of occasional output that is not part of
the standard display (such as, the output for
Help in the inspector).
.defflavor tv:text-scroll-window-typeout-mixin (
This can be added to a flavor containing
provides a typeout window. It also arranges for proper interaction with
the typeout window and partial redisplay over the area it clobbers.
.defmethod tv:text-scroll-window-typeout-mixin :flush-typeout If the typeout window is active, this deexposes it, and makes sure that redisplay knows that the lines have been clobbered. .end_defmethod
This is a mixin that goes with
When windows of this type have an empty array for
or an item generator that says the number of items is zero,
the interior of the window becomes gray.
This is used in some panes of the window-based debugger frame. .end_defflavor
The item generator feature is how the inspector can scroll through the elements of a large array without having to cons up another equally large array of items.
.definstvar tv:text-scroll-window tv:item-generator
The item generator function, or
nil if no item generator is in use.
The item generator is a function which simulates the effect of an array
of items. It overrides any explicit array of items; the value of
tv:items will still be an array, but it will not affect the display.
.defmethod tv:text-scroll-window :item-generator .defmethod1 tv:text-scroll-window :set-item-generator new-item-generator Get or set the window’s item-generator. .end_defmethod
:set-items operation sets the item generator to
nil, since if you
want to use an explicit list of items, you must not want the item generator
to cause them to be ignored.
The item generator function should expect its first argument to be an item generator operation keyword. These are the keywords defined:
:append-itemoperation on the window.
:delete-itemoperation on the window.
The inspector uses an item generator to display the elements of an array, so that it does not have to create another array of items as big as the array being displayed. If l is the length of the array’s leader, then item numbers 0 through l-1 correspond to the leader, and item number l+i corresponds to array element i (multidimensional arrays being treated as one-dimensional).
The value of the item at item number n is just n. In other
words, the virtual array of items that the item generator simulates is
an array of consecutive integers, independent of the data being
displayed. This may seem to be a weird way of doing things, but
consider this: we do not want the line for the ith element to print
out as simply that element. We want it to contain the number i as
well. So the item value is simply l+i, and the
:print-item operation is redefined to "print" such a number by
printing i followed by the ith array element.
Here is a simplified version of the item generator used by the inspector.
Note that the array whose elements are being displayed is found as
(car print-function-arg), and
(cadr print-function-arg) is non-
if the leader should be displayed.
tv:print-function-arg is an instance variable
from the flavor
tv:function-text-scroll-window; see (tv:function-text-scroll-window-tv:print-function-arg-instvar).
(defselect inspect-array-item-generator (:number-of-items () (declare (:self-flavor tv:basic-inspect)) (+ (if (cadr tv:print-function-arg) (or (array-leader-length (car tv:print-function-arg)) 0) 0) (array-length (car tv:print-function-arg)))) (:number-of-item (item) item) ;; The item's number is the item! (:item-of-number (number) number)) ;; The number's item is the number!
:delete-item are not supported, since the inspector
does not try to insert or delete items.
The inspector uses a
handled by calling a dynamically changeable print-function. Here is
a simplified version of the print-function used by the inspector
when displaying an array.
(defun inspect-array-printer (item arg window &aux (array (car arg)) (leader-length-to-mention (or (and (cadr arg) (array-leader-length array)) 0))) ;;
argis the value of
(car arg)is the array. ;;
tto display the leader. ;; item is a number, as described above. (cond ((< item leader-length-to-mention) (format window "Leader ~D" item) (format window ":~12T ") (tv:print-item-concisely (array-leader array item) window)) (t (let ((item (- item leader-length-to-mention)) (rank (array-rank array)) indices) (or (= rank 1) (setq indices (array-indices-from-index array item))) (format window "Elt ~D" (if (= rank 1) item indices)) (format window ":~9T ") (tv:print-item-concisely (ar-1-force obj item) window)))))
Windows of this flavor allow the lines to contain mouse-sensitive items
just like those of
(tv:basic-mouse-sensitive-items-flavor)) though the implementation is
Note that the word "item" in "mouse-sensitive item" is completely unrelated in meaning to the items of the text scroll window itself. .end_defflavor
.defmethod tv:mouse-sensitive-text-scroll-window :item type item &rest format-args
All output to text scroll windows is done with the
operation, which is responsible for printing a single item. This operation can
include mouse-sensitive items in the output by using the
which is compatible with that of
Note that the item argument here is the datum to identify the mouse-sensitive item, not the text scroll window item being displayed on this line. .end_defmethod
:primitive-item operations are not provided,
since in this context they are not really useful.
.defmethod tv:mouse-sensitive-text-scroll-window :item1 item type print-function &rest args
This is another way of outputting a mouse-sensitive item.
item and type have the same meanings as for the
but the output is done by calling print-function with
item, the window, and the elements of args as arguments.
:item operation used to do this, but it was changed for compatibility,
and the old functionality renamed to
In a typical
tv:basic-mouse-sensitive-items window, mouse-sensitive items
are output on specific occasions, and only because they are supposed to be
present and mouse-sensitive at that time. In a text scroll window, typically
a single display is maintained at all times, but the parts that should be sensitive
to the mouse may need to depend on other things. For example, in the inspector,
normally the values of slots are sensitive, but when you are specifying a slot
to store into, the names of the slots are sensitive instead.
.definstvar tv:mouse-sensitive-text-scroll-window tv:sensitive-item-types
The list of sensitive item types. A mouse sensitive item
is sensitive to the mouse if its type (as specified in the
operation) is a member of this list.
t can also be used instead of a list; then all mouse sensitive items
actually are sensitive.
t is the default value, so that this feature
does not get in the way if you do not use it.
.defmethod tv:mouse-sensitive-text-scroll-window :sensitive-item-types .defmethod1 tv:mouse-sensitive-text-scroll-window :set-sensitive-item-types new-item-types Get or set the list of sensitive item types. .end_defmethod
.definitoption tv:mouse-sensitive-text-scroll-window :sensitive-item-types item-types Initializes the set of sensitive item types. .end_definitoption
The inspector’s print function shown in the previous section really does its output
:item1 operation so that the output becomes mouse-sensitive.
Here is the real code for the
cond-clause that handles leader elements:
((< item leader-length-to-mention) (funcall window ':item1 item 'leader-slot #'(lambda (item window) (format window "Leader ~D" item))) (format window ":~12T ") (funcall window ':item1 (array-leader array item) ':value #'tv:print-item-concisely))
:value are item types which the inspector
makes mouse sensitive at various times.
When the mouse is clicked on a mouse sensitive item, a blip is placed in the window’s input buffer. The blip looks like
(type item window mouse-character)
type is the item type, such as
item is the actual item value specified in the
window is the text scroll window itself. (This is how the inspector can tell
which inspect pane you click on.) mouse-character is a character whose
%%kbd-mouse bit is 1. It tells the program which button was clicked.
This mixin, when added to
creates a "line area" near the left edge where the mouse cursor
changes to a rightward arrow and a click means something different.
The line area is an additional part of the left margin and does not overlap
the space used for displaying the items.
You must also include the flavor
tv:margin-region-mixin in the
flavor combination you instantiate.
A mouse click in the line area puts a blip into the input buffer that looks like this:
(:line-area item window button-mask)
button-mask is a mask of bits corresponding to mouse buttons;
tv:mouse-last-buttons, (tv:mouse-last-buttons-var), for
how to interpret it.
.definitoption tv:line-area-text-scroll-mixin :line-area-width number Specifies the width of the line area in pixels as number. .end_definitoption
.defmethod tv:line-area-text-scroll-mixin :line-area-mouse-documentation This operation should return a string to display in the mouse documentation line while the cursor is in the line area. .end_defmethod
This flavor should be used instead of
tv:mouse-sensitive-text-scroll-window is in use.
This flavor, when added to
identifies one of the items with an arrow in the line-area.
.definstvar tv:current-item-mixin tv:current-item
The item to be marked with an arrow, or
nil if none.
An arrow will mark this item if it is on the screen,
no matter where it scrolls to.
.defmethod tv:current-item-mixin :current-item
.defmethod1 tv:current-item-mixin :set-current-item item
Get or set the value of
These flavors are part of the implementation of
This is a component of
provides everything but the
:mouse-click method. Since this
:or method-combination, it is not possible to
override a method once it is present.
This flavor records additional information about the items that are
actually displayed. It provides an instance variable,
tv:displayed-items, which is an array indexed by line number.
In this array, the
:print-item operation can store any relevant
information about what was displayed on the line.
The meaning of elements of the array is not defined by this flavor. The
:print-item operation is responsible for storing whatever
information is useful into the appropriate slot of the array.
However, this flavor does move elements of the array when scrolling is done,
and set them to
nil when parts of the window are cleared,
or when they are about to be redisplayed.
This flavor is essentially a subroutine of
tv:mouse-sensitive-text-scroll-window, which uses each element of
tv:displayed-items to hold information on the mouse-sensitive items for the line.
.definstvar tv:displayed-items-text-scroll-window tv:displayed-items The array of information about lines on the screen. .end_definstvar
Despite all this hair, no window yet devised is as mouse-sensitive as my mother.
General scroll windows are used to put up a continuously-maintained display of items, each of which can vary in size. They are used by Peek. General scroll windows (from now on called simply scroll windows) are not a generalization or a building block of text scroll windows, but rather an independent facility.
The scroll window’s display is made up of items. These items are not the same as items in text scroll windows; the same term is used because they fit in a similar place in the scheme of things.
An item in a scroll window always occupies an entire line or several entire lines. An item can be composed of sub-items which are juxtaposed vertically, each sub-item occupying and filling up some number of lines. The sub-items can in turn be composed of more items. New sub-items can be dynamically added or deleted at any level, and the display is updated automatically to match by moving lines around on the screen.
Eventually this process of subdivision must come to an end, with lowest-level items made up of entries, which are juxtaposed in a horizontal sequence.
An entry displays a single string or quantity, updating its display if the value changes. The entry must record how to obtain a value to display, how to tell when the value has changed since the screen was updated, and how to output the new value. A single entry can wrap around at the right margin just like ordinary output. Entries can be added to and removed from an item dynamically.
In Peek’s Active Processes display, there is a single item that displays the entire set of processes. It is composed of sub-items, one for each process. If a new process appears, a new sub-item is created to display it. The sub-item for a single process is a lowest-level item. Each of the things displayed about a process–its name, its run state, its priority, its percentage use of the cpu–is displayed by a single entry in that item.
The line of column headings at the top of the display is also a lowest-level item; its entries display constant strings.
Every character displayed on a scroll window comes from an entry. The items serve only to group entries, and to control the automatic insertion and deletion of entries.
Entries can be either fixed or variable width. A variable width entry takes up as much space as is needed to print its data; this can change when the window is redisplayed. When that happens, the remaining entries in the item all have to move left or right. A fixed width entry specifies an amount of horizontal space and always occupies that much space. As a result, it can be redisplayed without redisplaying the rest of the item afterward. The entries used in the Active Processes display are all fixed-width so that they will line up in columns
Note: if the entry specifies a fixed width and the printing of its
contents goes past that width, the window redisplay algorithm will be
The data structure that represents an item is either a list or an array. If it is a list, its cdr is a list of component items, and its car contains information on how to update the list (add or remove component items). Then the item is displayed simply as the concatenation of its components. If it is an array, then it is a lowest-level single-line item, and the elements of the array represent entries on the line. The array also has leader slots whose meanings are described below.
You do not generally create an array item or an entry yourself. They are
made by calling the function
tv:scroll-parse-item, which is given a
descriptive data structure made out of lists. Examples of its use
are at the end of this section.
The arguments to
tv:scroll-parse-item are entry descriptors,
each of which specifies how to create one entry. The entries thus
specified all go together into one item.
Here are the possible kinds of entry descriptors:
(:string "Foobar" 10.) or
specifies an entry that prints as Foobar followed by 4 spaces.
formattogether with format-string. If format-string is omitted, the value is printed with
princ. This type of entry is automatically updated when the value of symbol changes.
width-or-nil may be a number of pixels, to specify a fixed-width entry, or
nil to specify a variable-width entry.
(:symeval base nil " ~D. ")
specifies an entry that prints the value of
base in decimal with
a following period and a space in front and in back. It is variable-width
so the space it takes up is three plus however many digits are needed to
print the value of
formattogether with format-string. If format-string is
nil, the value is simply
width-or-nil may be a number of pixels, to specify a fixed-width entry, or
nil to specify a variable-width entry.
`(:function si:process-quantum-remaining (,process) 5. ("~4D//"))
is an expression that creates an entry descriptor which specifies an entry
that will call
si:process-quantum-remaining on some process
and print the result in decimal, followed by a slash, in a field 5 characters wide.
named-lambdais considered a function. It is treated as an abbreviation for
(:function function), which specifies no arguments, variable width, and no format string (the value is printed with
Two other keywords can be used in an entry descriptor to make the entry mouse sensitive.
They can be used only in scroll windows which have
tv:essential-scroll-mouse-mixin (see (scroll-window-mouse)).
To use these keywords, first you construct an entry descriptor to
specify how the entry should print, according to the preceding table.
Then you add one of these keywords and a value to go with it
at the front of the list. The mouse keyword gives the entry mouse sensitivity
but has no effect on how the entry appears on the screen.
:mouseis used in an entry descriptor that looks like
(:mouse mouse-data . another-entry-descriptor)
Such an entry descriptor is handled by creating an entry from another-entry-descriptor, and then modifying it by recording mouse-data as the mouse sensitivity of the entry. The resulting entry will print according to another-entry-descriptor but will be mouse sensitive as well.
:mouseis used in an entry descriptor that looks like
(:mouse-item mouse-data . another-entry-descriptor)
:mouse-item is like
:mouse except that the symbol
is replaced throughout mouse-data with item, the item this entry
is going to become part of. mouse-data better be a list.
There is no way to cause the entry itself to be inserted into its own mouse sensitivity datum because this is not useful when scroll windows are used in the intended manner.
Creates and returns an array item containing entries constructed according to keyword-args-and-entry-descriptors.
keyword-args-and-entry-descriptors begins optionally with some alternating keywords and values. They are followed by entry descriptors, one for each entry you want in the item. The keywords and values at the beginning specify information that applies to the item as a whole. Keywords and entry descriptors are distinguished by the fact that an entry descriptor is never a symbol.
The keywords defined are
selfis replaced wherever it appears by the item itself (the array that this function is constructing). This is meaningful only if the window flavor includes
Creates and returns an entry according to entry-descriptor for use in the
array item item. You do not normally call this function yourself;
it is used as a subroutine of
Returns an item that will display the string string. This item is composed of one item for each line making up string.
Here is an example taken from Peek; it makes the item for
a process (the value of
process) in Active Processes mode.
The entries that use the process as a function work
because the process is a flavor object; the argument given
to the process is a flavor operation. Note that
is a function in Peek which asks for a choice with a momentary menu.
(tv:scroll-parse-item ;; The first entry is mouse-sensitive. `(:mouse-item (nil :eval (peek-process-menu ',process 'item 0) :documentation "Menu of useful things to do to this process.") :string ,(process-name process) 30.) `(:function ,#'peek-whostate ,(ncons process) 25.) `(:function ,process (:priority) 5. ("~D.")) `(:function ,process (:quantum-remaining) 5. ("~4D//")) more entries...)
.defflavor tv:basic-scroll-window All flavors of scroll window are built on this flavor, which provides all the facilities specific to scroll windows. It is not instantiable by itself. .end_defflavor
.defflavor tv:scroll-window (
This is an instantiable scroll window flavor. It provides for a scroll
bar and margin scrolling, and for borders and labels.
In addition to being able to create a tree of items and entries, you must tell the scroll window to display them. At the highest level, the entire display is grouped into a single item, the root item. Switching modes in Peek works by switching to a new root item.
.definstvar tv:basic-scroll-window tv:display-item The root item of the window. The window’s display is precisely whatever comes from this item, and nothing more. Usually the root item contains some number of subitems which do the real work. .end_definstvar
.defmethod tv:basic-scroll-window :display-item .defmethod1 tv:basic-scroll-window :set-display-item item Get or set the root item of the window. Setting the root item redisplays the window. .end_defmethod
.definitoption tv:basic-scroll-window :display-item item Initializes the root item. .end_definitoption
.definstvar tv:basic-scroll-window tv:truncation
If this is
nil, entries can wrap around at the right margin.
Otherwise, each item can occupy only one line.
.defmethod tv:basic-scroll-window :truncation .defmethod1 tv:basic-scroll-window :set-truncation flag Get or set the truncation flag. Setting the flag redisplays the window. .end_defmethod
.definitoption tv:basic-scroll-window :truncation flag Initializes the truncation flag. .end_definitoption
A scroll window has a value array whose elements may be used to
hold arbitrary data to be displayed by entries using the keyword
:value. Such an entry specifies the index of a slot in the value
array whose contents are the data to display. Putting appropriate
data in the value array is up to you. One technique is to have an
automatically updating item whose update function stores data into the
value array, and have entries in the item look in those slots. There
can be many such items, all using the same value array slots. See
.definstvar tv:basic-scroll-window tv:value-array The window’s value array. .end_definstvar
.defmethod tv:basic-scroll-window :value-array Returns the window’s value array. .end_defmethod
.definitoption tv:basic-scroll-window :value-array array-or-length Initializes the window’s value array, or specify how long to make it. .end_definitoption
:redisplay operation updates the display based on the current
root item, automatically reprinting the entries whose contents have
:redisplay will be done automatically by the window system
at certain times (such as when the window size is changed, or the screen
is refreshed), but if you want it to happen simply because some of the
displayed data has changed, you must send a
:redisplay-selected-items is another way to request display
updating, which allows you to control which items will be checked.
.defmethod tv:basic-scroll-window :redisplay &optional full-p force-p
Redisplays the contents of the scroll window. If full-p is
the window assumes that its screen bits contain the result of the last
redisplay that was done, and only items and entries whose contents are
different from last time are actually output. If full-p is
nil, everything that is supposed to be on the screen is redrawn.
nil means update the contents of the window even if
it is not exposed. Normally, this operation will wait if the window is
.defmethod tv:basic-scroll-window :redisplay-selected-items list-of-items Redisplays the items in list-of-items, if they are present on the screen. Other items in the current item hierarchy are not even considered for redisplay. .end_defmethod
Since a scroll window shows a constantly updated display, it is often useful to have a typeout window in it for occasional output that is not part of the display that is usually shown.
This mixin should be used in addition to
tv:window-with-typeout-mixin on any scroll window that is to have a
typeout window. It handles interfacing between typeout window output
and redisplay of the scroll window.
.defflavor tv:scroll-window-with-typeout A scroll window that has an inferior typeout window. See (windows-with-typeout). .end_defflavor
Scroll windows provide operations for replacing, inserting and deleting
items explicitly. Since the items form a multilevel hierarchy, the
position at which to replace, insert, or delete the item must be
specified as a list of numbers. For example,
(1 3 0) as a position
means item number 0 within item number 3 within item number 1 (within
the root item,
nil as a position refers to
the root item itself.
.defmethod tv:basic-scroll-window :get-item position Returns the item at position. .end_defmethod
.defmethod tv:basic-scroll-window :set-item position item Stores item into the hierarchy at position. .end_defmethod
.defmethod tv:basic-scroll-window :insert-item position item Inserts item at position, before the item that was at position. .end_defmethod
.defmethod tv:basic-scroll-window :delete-item position Deletes the item at position, so that the following item moves to that position. .end_defmethod
These operations also update the window on the screen as necessary.
Just as an entry automatically updates the value it displays, sometimes one wants an item to update automatically the list of items it contains. For example, the Active Processes display contains one item that displays a list of all active processes. This item contains a list of component items, one item per process. Just before the displayed entries for each process are updated if necessary, additional items should be created and inserted in the list if there are any newly active processes, and items should be removed if processes have become inactive.
The first element of an item that is a list is used to store the data of a property list for the item. Two properties are given standard meanings:
The arguments given to the function are the component item,
the reverse of the position of that item (a list of integers),
and the location of the property list of the containing item,
the same property list on which this
:function property appears
(this can be passed directly to
get). To repeat, the second
argument is the
reverse of the position as would be passed to the
:get-item operation or related operations. This is because it is
easier to implement that way without consing.
The function should return an updated component item, perhaps the same one as it was passed, perhaps a new one.
Other properties can be used for any purpose. Some of the commonly used pre-process functions use other properties for their internal state information and additional parameters.
Returns an item which maintains a list of component items, one for each element of a driving list. The item updates automatically so that component items appear and disappear as elements of the driving list do.
init-fun should be a function of no arguments that returns the current value of the driving list. item-fun should be a function that, given an element of the list, returns a component item to use to display for that element. item-fun is called each time a new element appears in the driving list. The item created starts out with no component items. The appropriate set of component items is created by adding them one by one in this way, the first time the item is updated.
This item works because it is given a suitable pre-process function.
The other arguments to
tv:scroll-maintain-list are also
stored on the property list of the item created. In particular,
per-elt-fun becomes the
:function property. (That is all
per-elt-fun is used for.)
Normally the value from init-fun is a list, and the objects that the
items are made from are the elements of this list, but it is possible to
extract the objects in other ways. If stepper is not
it should be a function to step through a "kind of list".
stepper is called with one argument, a "kind of list", and returns three values:
nilto say there are no more elements
nilas the "kind of list" is always recognized as being empty, regardless of the third value.
stepper is first called with the value returned by init-fun.
The first value goes (if it is new) to the item-fun; the second is
fed back to stepper unless either it is
nil or the third value is
A stepper function that could step through the properties in a property list might be:
(defun plist-stepper (plist-tail) (values (car plist-tail) (cddr plist-tail)))
nil says to recopy the list each time an element
is inserted or deleted, so that the list remains compact and localized.
Here is how Peek, in Window Hierarchy mode, recursively creates a tree of automatically updating items:
;; Make an item to describe the entire window hierarchy. (defun peek-window-hierarchy (ignore) (tv:scroll-maintain-list ;; The init-fun. When called, it returns a current list of screens. #'(lambda () tv:all-the-screens) ;; The item-fun, which makes an item for a screen. #'(lambda (screen) (list () (tv:scroll-parse-item (format nil "Screen ~A" screen)) (peek-window-inferiors screen 2) (tv:scroll-parse-item ""))))) ;; No per-elt-fun is needed. Also, the default stepper works ;; because our "list" really is a list. ;; Make an item to describe window and its inferiors. ;; indent is an indentation to print with. (defun peek-window-inferiors (window indent) (declare (special window indent)) (tv:scroll-maintain-list (closure '(window) #'(lambda () (tv:sheet-inferiors window))) (closure '(indent) #'(lambda (sheet) ;; Make an item with two subitems (list () ;; One for this window, (tv:scroll-parse-item (format nil "~VX" indent) `(:mouse (nil :eval (peek-window-menu ',sheet) :documentation "Menu of useful things to do to this window.") :string ,(send sheet ':name))) ;; and one with subitems for its inferiors. (peek-window-inferiors sheet (+ indent 4)))))))
And here is how it makes the item that displays a chaosnet connection’s packets.
(tv:scroll-maintain-list `(lambda () (chaos:read-pkts ',conn)) `(lambda (x) (peek-chaos-packet-item x ,(+ indent 2))) nil #'(lambda (state) (values state (chaos:pkt-link state) (null (chaos:pkt-link state)))))
Note that instead of a list of packets there is a chain, with each
packet pointing to the next one. Therefore, an explicit stepper
chaos:pkt-link is the function which, given one packet,
returns the next one in the chain (or
nil at the end).
Returns an item which maintains an unordered list of component items, one for each element of a driving list. The item updates automatically so that component items appear and disappear as elements of the list do.
This function is very much like
difference is that new component items are always added at the front of
the combined item, no matter where they appear in the driving list.
Changes in the order of that list have no effect at all. This is why
this function is called "unordered".
Redisplays some of the component items of item, an item of the sort
elements is a list that specifies which component items to update.
If the element of the driving list from which a component item was made
memq of elements, then the component item is updated.
An item is either a list or an array. A list item contains other items, while an array item contains entries.
List items have these accessor functions:
Array items have these accessor functions, which refer to array leader slots. (The array elements themselves hold the entries in the item.)
The number of standardly-defined slots in an item’s array leader. The slot with this number and beyond can be used by applications for their own purposes.
Entries are also arrays. They have a lot of components, all managed internally, and users should probably not access them directly. Peek never needs to do so.
.defflavor tv:essential-scroll-mouse-mixin This mixin gives a scroll window the ability to make either items or entries mouse sensitive. .end_defflavor
This mixin in addition defines the
to be the same as on menus.
tv:scroll-parse-item provides syntax, described above, for
associating a mouse sensitivity to any item or entry. The mouse
sensitivity is a list whose purpose is to identify which mouse-sensitive
area was clicked on, and also specify what to do when that happens.
If the car of the mouse sensitivity is
nil, then the mouse
sensitivity is interpreted as a menu item. When the sensitive area is
clicked on, the menu item is executed by means of the
:execute operation–but this is done in the mouse process.
Unfortunately, there is no way to avoid this, since mouse clicks on
scroll windows are expected to be able to happen "at any time", and no
other process has expressed its willingness to handle them with a
If the car of the mouse sensitivity is non-
nil, a click is handled
by putting a blip into the scroll window’s
input buffer. The blip has the form
(blip-type sensitivity window mouse-character)
sensitivity is the mouse sensitivity list. blip-type is the car
of that list. window is the scroll window itself, and
mouse-character is a character such as
indicates which button was clicked.
The reason that the blip-type is extracted and put at the front is that programs that use scroll windows may need to handle blips from many sources. By specifying the car of each mouse sensitivity, the program can arrange to distinguish these blips from blips coming from menus, typeout windows, etc. and process each one in the correct fashion.
Often a scroll window displays many similar items that describe
different data objects. These items will all have the same patterns of
mouse sensitivity. One way for the program to tell which item the user
clicked on is to set up the mouse sensitivity using the
keyword (for an item) or
:mouse-item (for an entry). This inserts
the item itself into the sensitivity in place of the symbol