How To Modify Sil How To Read and Modify Sil Release as [Indigo]Sil>SilInternalDoc.tioga Came from [Indigo]Sil>SilInternalDoc.tioga By Tracy Larrabee Last edited By Tracy Larrabee on April 30, 1984 2:30:48 am PDT Abstract Sil is a Simple Illustrator that runs in Cedar. The external behavior of Sil is documented in Documentation>SilDoc.tioga. This document describes how to read the existing Sil program, and how to modify it (either to fix bugs, or to add new features). How To Read and Modify Sil: Contents 0. Introduction 1. Motivation and Justification 2. Basic Program Organization 3. How to Read a Sil Module 4. Following the Behavior on a Specific Action 5. References [If you are reading this document on-line, try using the Tioga Levels menu to initially browse the top few levels of its structure before reading it straight through.] XEROX Xerox Corporation Palo Alto Research Center 3333 Coyote Hill Road Palo Alto, California 94304 DRAFT  For Internal Xerox Use Only  DRAFT 0. Introduction This is a document on how to read and modify a program (Sil) that I wrote at the Xerox PARC Computer Science Lab (hereafter known as CSL) as a programming project for Stanford University. I want to set a few ground rules for the style and organization of, as well as the assumptions behind this document. I am going to avoid linguistic gymnastics by writing this document in the first person: while this leads to a less formal style than the standard professional third person, I think it will increase the clarity of my presentation. I am going to assume that the reader of this document has some familiarity with the CSL computers [1], [4], languages [2], and environments, but not that he is a member of CSL. I am also going to assume that the reader is familiar with Sil, either by reading the Sil user manual [3], or by having used Sil (either Alto or Cedar Sil would provide the requisite familiarity). Because of the breadth of experience of my target audience some of the sections of this document may seem unnecessary to some readers. In particular, the following section is mostly history, and may be safely skipped over by the hard-core Sil maintainer. 1. Motivation and Justification Sil (Simple Illustrator) was originally written for the Alto environment, and became quite a popular program in the Xerox community. However, as the years passed, CSL developed Cedar - an experimental programming environment that runs on machines much more powerful than an Alto, and uses a language (Cedar Mesa) much more high level than BCPL (the "native" language of the Alto, and the original implementation language of Sil). As of the summer of 1983 almost all CSL members were near full-time clients of the Cedar world. Unfortunately, there are a few beloved programs that did not yet have cognates in the Cedar world: many lab members were forced to forgo the pleasures of Cedar for the concrete design tools available in the Alto world. While a Cedar version of Sil has been desired for some time, there are several reasons beyond simple implementation language incompatibility that made Cedar Sil more than a problem of simple transliteration. When Sil runs in the Alto world it can assume that it "owns" the world: access to and manipulation of the screen bitmap, the cursor representation, the main storage, and even the disk is entirely under Sil control. On the other hand, Sil did not receive much help from the Alto operating system in the areas of graphical manipulation (bit-blt or raster-op, clipping, scaling and translation) or storage allocation. There were no problems associated with copying a Sil object from one window to another, as Sil could only display one window, and manipulate one set of data, at a time. A Cedar application must start from very different assumptions. In Cedar, an application must run in a general window package. Cedar applications must share address space, user attention, and CPU power, and the amount of any one of these available to the application may change at any time. Users will expect that an application can be used to view multiple files in multiple windows; given their experience with existing applications, they will even expect to be able to copy or move application entities from one window to another. On the more pleasant side, Cedar applications receive the support of sophisticated graphics packages [5], and the Cedar garbage collector and large virtual address space leave the applications programmer unhindered by questions of storage allocation. What Cedar Sil must have in common with Alto Sil is file format compatibility (Alto Sil files must be readable in Cedar Sil, and, at least until the other logic design programs are transported to Cedar, it must be possible to ship Cedar Sil files back to the Alto world for post processing). Because Sil has so many dedicated clients, the rudiments of the user interface must be the same in Cedar Sil as in Alto Sil (there are Sil users that claim that Sil has been wired into their spinal columns). Of course, this stipulation does not give any guidance as to the correct behavior of Sil's user interface in the presence of cross-window object manipulation. After learning the rudiments of the Cedar environment, using Sil, and talking with Sil's original designer (Chuck Thacker) and my CSL summer host (Mark Brown), I decided that the organization and assumptions of the Sil BCPL code were so different than those required by my Sil, that I would not even read the BCPL code. I think that this decision has been the right one: I was not transliterating an existing program (a program that had been tuned for optimal Alto efficiency) - I was taking a set of specifications and designing a program to meet these specifications. 2. Basic Program Organization A module is the basic unit of compilation in Cedar. There are two types of modules in Cedar: definitions modules and implementation modules. Both kinds of modules are loaded and interconnected to provide a complete system. While a full description of definitions and implementations modules is available in the Mesa Language Manual [2], I will give an abbreviated explanation for the sake of experience. Definitions modules provide common definitions that can be referenced by other modules being compiled. It is common to think of a definitions module as an interface, but in practice it may only be part of an interface. Implementation modules contain actual data and executable code. At compile time, an implementation module containing calls on procedures defined by some interface must import the definitions module that specifies that interface This allows for complete compile time type checking, but, it does not link the procedure referenced to actual procedures in other modules - this binding occurs when the program is linked (bound is another term for it) with other modules. There are four sets of modules in Sil. The four areas that they represent are: data file representation, user input, display, and top level control. In general, a module with the name fooImpl.mesa will be the implementation module corresponding to foo.mesa - a definitions module. 2.1 Data File Representation The modules which handle internal and external data file representation are SilFile.mesa and SilFileImpl.mesa. Information from one or more Sil data files can be contained in one internal Sil model. A Sil model will consist of a main picture, and several secondary pictures which have been defined as a macro character set. Each picture consists of a list of objects, and each object is either a box or a string (in Cedar terminology we say rope). Both boxes and ropes have positional and color attributes, and ropes also have attributes of font and face (whether the object is bold and/or italic). The fonts used in each Sil model will be defined by the current Sil predefined fonts, and the Sil Library fonts. Each Sil window will have one model associated with it. In addition to the models that may be active in various Sil windows, there is a distinguished model (one of the already active models) and set of objects from that model that constitutes the selection. Many common Sil manipulations are performed on the selection. Any creations or deletions to, or queries about any Sil models or the selection must pass through the SilFile interface. 2.2 User Input The modules which handle user input are SilUserInput.mesa and SilUserInputImpl.mesa. When a Cedar users attention is given to one Cedar window (in Cedar terminology we say when a window has the input focus) a distinguished procedure called the Notify Proc, (supplied by the application for this window) is called with parameters indicating what keyboard or mouse actions the user has performed. SilUserInput uses its Notify Proc to produce a queue of user events for each Sil window. Condition variables and monitors provide for management of the standard producer/consumer problem. In addition, SilUserInput also provides for the gridding of the Sil cursor (Sil cursors are constrained to lie only on screen coordinates that are multiples of the current grid spacing). Although not a module, an important file for the Sil user input implementation is Sil.tip. Sil.tip specifies what information is considered important given a user action. For example, if the left button on the mouse is clicked, the user input module needs to know that a mark has occurred, and the coordinates at which it occurred. 2.3 Display There are a number of modules defining Sil display. The top level definitions and implementation modules are SilDisplay.mesa and SilDisplayImpl.mesa. In addition to these there is a definitions module that defines the template for the display state needed for each window (SilDisplayInternal.mesa), a pair of files which take care of all operations on the Sil carets and input focus (SilDisplayCursors.mesa and SilDisplayCursorsImpl.mesa), and a pair which provide miscilaneous utility functions for the other display implementation modules (SilDisplayUtils.mesa and SilDisplayUtilsImpl.mesa). The top level Sil display module dequeues user input records, directs any appropriate modifications to the model for this window, and updates the screen to reflect the current state of the model and the display parameters. The top level display procedure is a distinguished procedure called the Paint Proc which is called not only because of the normal program control flow, but because of user action. A user may ask for the painting of a window, and may even close the window so that appears as an icon on the bottom of the screen and requires no data display at all. 2.4 Top Level Control The modules which provide for top level control are SilKernel.mesa and SilKernelImpl.mesa. For each window a process runs which checks for user input and calls the correct display procedure if there is any. In addition, the process checks to see if either of the Sil carets should be moved or blinked in the process' window. SilKernel provides for creation and destruction of an arbitrary number of these processes, as well as one process which is responsible for blinking both of the Sil cursors. 3. How to Read a Sil Module At the top of each module is its name and a quick description of its function. Next will be a Directory statement and a Program statement followed by constant, type, variable and procedure definitions. While I am assuming that the reader is at least passing familiar with most of this material, I will quickly cover some highlights while emphasizing my personal quirks and style. 3.1 Directory Statements Directory statements will help you read a module. In a Directory statement, every module which is used in this module is listed. If a module has a using list, the using list tells exactly which data types and procedures are used from each module (they will be listed alphabetically). Upon seeing the statement: DIRECTORY Rope USING [Compare, ROPE]; you will know that the module Rope is used in this module, and, in particular, Compare (you might guess that this is a procedure) and ROPE (you might guess that this is a data type) are used in this module (and no other data types or procedures from Rope are used). The importing module can make use of this data type by referencing Rope.ROPE. It can make use of this procedure by calling Rope.Compare. You can find out exactly what the ROPE and Compare do by looking in the Rope definitions module. One thing to notice is that full type specifications of the form ModuleName.Type are often aliased with type synonyms. So the type synonym: ROPE: TYPE ~ Rope.ROPE; will allow this module to refer to objects of type Rope.ROPE only as objects of type ROPE. 3.2 Program Statements Program statements include imports and exports statements; they list all of the modules that actually supply executable code to this module, and all of the modules to which this module actually supplies code implementations. Upon seeing the statement: SilFileImpl: CEDAR PROGRAM IMPORTS Rope EXPORTS SilFile ~ BEGIN you will know that the module Rope supplies procedures used in this module. You will also know that this module, SilFileImpl, supplies executable code corresponding to the definitions guaranteed by the definitions file, SilFile. One more important thing about program statements: they will tell you if this module is actually a monitor. Monitors are complicated, and you will need to consult the Language Manual if you want to use them much. Here, briefly, are the wonders of Mesa Monitors. My monitors are almost always modules which work with monitored records. This means that procedures which are specified as Entry procedures in the module may only be accessed with a specific record, and at most one process with that specific record may be inside the monitor at a given time. For example, the statement: SilDisplayImpl: CEDAR MONITOR LOCKS dData USING dData: SilDisplayData IMPORTS Rope EXPORTS SilDisplay ~ BEGIN in conjunction with the SilDisplayImpl procedure declaration: SilEntryPaintProc: ENTRY PROC [dData: SilDisplayData] ~ { -- something executable here }; provides for every Sil process updating its display at any given time, but each process may only have one call to its Paint Proc outstanding at any given time (dData, a record of type SilDisplayData, is a record containing all of the display state for a given process - which is in this case a given window). 3.3 Constant, Type, Variable and Procedure Definitions The only difference between a constant declaration and a variable declaration is whether or not the value is permanently bound. Constant declarations always have permanent bindings, which are signified by a tilde (~) or an equal sign (=), and variable declarations may or may not have an initial binding, which is specified by an assignment arrow (_). From my point of view, type declarations are just constant declarations that happen to specify a compile time constant that is a type. For example: ROPE: TYPE ~ Rope.ROPE; is a type declaration, MyName: ROPE ~ "Tracy"; is a constant declaration, and NameOfSilMaintainer: ROPE _ "Tracy"; is a variable declaration. Procedure declarations specify the name of the procedure and its type (what arguments it takes and what it returns). In implementations modules procedure declarations also specify the textual representation for the executable code that is to be bound to this procedure name. For Example: DumbProcedure: PUBLIC PROC [x, y: INTEGER _ 0] RETURNS [w, z: INTEGER] ~ { -- this is a dumb procedure w _ x - y; z _ x + y; }; is a dumb implementation module procedure with x and y as integer input arguments (you might as well assume input arguments are passed by value - they aren't always, but you might as well assume that) with a default value of 0 (if no specification for either x or y is given in the procedure invocation 0 will be the value assumed). w and z are integer output arguments (output arguments can have defaults too, but these don't), a header comment, and two statements to execute for the body of the procedure. The definitions module counterpart will look like: DumbProcedure: PROC [x, y: INTEGER _ 0] RETURNS [w, z: INTEGER] ; -- this is a dumb procedure DumbProcedure is called by: sum: INTEGER _ DumbProcedure [y: 2].z; In this case sum would get 2. Notice the use of keywords to specify input arguments and selection to specify a specific result. Of course, it would have been illegal to leave out the specification for x if it had no default value in the procedure definition. 4. Following the Behavior on a Specific Action In this section I am going to follow a user action through my modules in a manner which you might use if you were trying to figure out what Sil does, or how to add a new behavior. I am going to follow a CONTROL-c. While this walk through may seem simplistic, it is important to realize how many things you can ignore when you discovering program behavior in response to a special case. This section is meant to be read with the code sitting directing in front of you. 4.1 Tip Sil has a TipTable named Sil.tip. Look in it for a line which has a statement about the C key being down while Control key is down on the left side of an arrow (=>). When you find it you will see the "atom" that is sent to the Notify Proc (See the subsection on User Input). In this case the atom is "CopySelected." 4.2 SiluserInput The Notify Proc (SilUserInput.SilNotify) will be called with the input containing the atom "CopySelected.". The first thing in the procedure is a select statement (like a case statement) that is selecting the type of the input. In this case we have an atom, so we start looking at the arm of the select statement that specifies atom. Once again we have a select statement, but this time it is a selection on the actual atom. Now we look for $CopySelected in the select choices. Finding it, we see that a user input record of type OperateOnSelected is put on the user input queue. If we look at the Enque procedure that is used to enque the record, we see that a condition variable is notified when user input is received. 4.3 SilKernel Every Sil window has a process that uses SilKernel.SilMain as the main control procedure. An abbreviation of SilMain would read: WHILE NOT data.mainProcessShouldStop DO IF SilUserInput.InputAvailable[data.uiData] THEN ViewerOps.PaintViewer[viewer: viewer, whatChanged: $UserInput] ELSE IF SilUserInput.ShouldChangeCaret[data.uiData] THEN ViewerOps.PaintViewer[viewer: viewer, whatChanged: $Carets]; ELSE SilUserInput.AwaitUserInput[data.uiData]; ENDLOOP; Which is to say, if there is input, process it. If there is caret activity, process it. If nothing else is happening, wait until there is input. We have seen that we just received input, so we should examine the place where the viewer is painted. 4.4 SilDisplay A Sil window is painted by its Paint Proc. Our Paint Proc is SilDisplay.SilPaint. It calls SilEntryPaintProc, which will call SilUIPaint. SilUIPaint has a select statement which selects on the type of the input record. Our type is OperateOnSelected, so we call the procedure OperateOnSelected. OperateOnSelected will update the display and data structures correctly to reflect that a copy has occurred in response to CONTROL-C. 5. References [1] Lampson, B. W. and Pier, K. A. " A processor for a high-performance personal computer. " Proceedings of the 7th IEEE Symposium on Computer Architecture, pp 146-160, May 1980. [2] Mitchell, J. G., Maybury, W. and Sweet, R. "Mesa language manual (Version 5.0)." Technical Report CSL-79-3. Xerox Palo Alto Research Center. 1979 [3] Larrabee, T. "How to Use Sil." unpublished. [4] Thacker, C. P. et al"Alto: A personal computer." Technical Report CSL-79-11. Xerox Palo Alto Research Center. 1979 [5] Warnock, J., Wyatt, D. (1982): A Device Independant Graphics Imaging Model for Use with Raster Devices,'' Computer Graphics 16, 3, 1982.