Inter-Office MemorandumToCedar InterestDateJanuary 15, 1981FromDan SwinehartLocationPalo AltoSubjectCedar Terminal Input FacilitiesOrganizationCSLXEROX Filed on: [Ivy]User>InscriptImplementation.memo[Ivy]User>InscriptImplAppendix.memoReference: [Ivy]User>InscriptDesign.memo (what I intended to do)These will currently all be found on [Ivy]Inscript>6.0>. They have also been released as part ofCedar.bcd.Definitions files:ClassInstream.bcd,ClassIncreek.bcd,Interminal.bcd,Intime.bcdInDiag.bcdInOS.bcdImplementation:Instream.bcd, exports above interfaces, includes symbolsInstream.configother source files, configs, etc.IntroductionThis memo describes the existing facilities for obtaining user terminal input. The most accurateinterface description can be found in the cited definitions files; this discussion is intended to presentboth the main ideas and the details, but it may lag behind the truth from time to time.User information is recorded in a file called an Inscript. The inscript conceptually contains a time-stamped record of every activity, or action, the user has ever performed at the terminal. In reality, oldinformation may be removed from this record, and it is possible that during some intervals not all userinput is recorded (e.g., the detailed trajectories of the mouse.)Most clients will obtain information from the inscript by creating an Instream, which provides quitea bit of help with the interpretation of the user's activities -- primarily filtering and code-conversionassistance. Any number of instreams can be created, each of which may be examining different epochsin the input history.Clients that wish to deal more directly with the recorded actions, perhaps to create a high-level inputstream with different behavior from the Instream defined here, may create a lower-level stream-likeobject called an Increek. The Increek provides operations for time-based positioning within theInscript, and for examining Inscript actions.]gpi c8q]rX -q7Br ]q]r -q7Br Yq]r-q 7BrSsr M:L{. J4qFtq F$rXk D BI@?A=<8 : 9076(843 ! /u ,rS +E;. )W &0vr- %%wr2 #?( " A Fvr ZY S R '9. (xr! vyrxr# xrzr?X3Cedar Terminal Input Facilities2InstreamsAn Instream provides a stream of Events, currently confined to keyboard, keyset, and mouse button(henceforth shortened to "key") activations. Instream provisions for tracking the mouse in theabsence of key activations are TBD, although complete facilities exist at the Increek level. Not alluser activities, or Actions, result in Events, and some Events are not the immediate result of useractivity. The Instream client can control to some extent which Actions result in Events.The client can choose to obtain and examine Events directly from the Instream, or to obtainderivative data types based on the type of Event (keyboard characters, mouse button clicks, etc.)After obtaining an Event or a derivative, the client can also examine the state of the Instream toobtain time stamp information, the position of the mouse, or the state of various keys (shift, lock,control, etc., or any other key for that matter) at the time of the event.Data TypesWhat follows is a discussion of the more important data types used or viewed by Instream andIncreek clients.From ClassInstreamClassInstream.Instream: TYPE = REF InstreamObject;This is an object that implements the operations described below.ClassInstream.Device: TYPE = {nullDevice, char, button, paddle, motion};An Instream delivers Events to its clients. A field in each Event identifies which device generatedit (keyboard, mouse, mouse motion only, or keyset), and what kind of activity caused it. Motion events arenot yet implemented.ClassInstream.Cause: TYPE = MACHINE DEPENDENT {none(0),strobeDown(1), strobeUp(2), heldDown(4), heldUp(10B), canBeChord(20B)};ClassInstream.Causes: TYPE = -- Powerset of -- Cause;The Instream client uses values of type Causes to specify which of the user activities are of interestfor each of the three devices (there are standard defaults.) strobeDown means that an Event willoccur whenever a key is depressed. strobeUp means that an Event will accompany the release of akey on the selected device. heldDown and heldUp will occur only after a key has been depressed(released) for a specified duration, in the absence of additional keystrokes. This is in support ofmultiple click (and later "typeamatic") Events, described further in a later section. For simple situationsone merely specifies strobeDown, strobeUp, or both.Events also contain Cause fields, to indicate to the client which kind of activation occurred.Cause has its values explicitly specified so that values can be ORed together to specify multipleactivations. There are "powerset" functions, described below, for creating the desired configurations.ClassInstream.Event: TYPE = RECORD [ device: Device, keyName: Interminal.KeyName, cause: Cause, clicks: CARDINAL ];The Event records which device was involved, what kind of activation occurred, and which key wasdepressed (using names assigned from a space containing them all). Each Event also contains aclicks field, described further in the Clicks section. The stream stores additional information aboutthe current state of all the input devices just after the Event occurred. This state includes the currentactivation state of all of the keys, the mouse position at the time of the Event, the time of the Event, frG bu ^rxyrvr: ]n.xr  ["qr"qxrqr Zfvr xr xr! Xxyr(xr xr U,xr xyr T3+xr1 Rxr?xr Q+V ODzr L{y IPrPxr Gxr DI DAu{ x {x{x ?rA;{ xX{x. 9rxr xr"xr 8Xzq 63g{ xX{x{x{x 1G/{ x{x -3rxrxr' +>x r xzr *+ xrxr (xuruxr/ '#31 %q rxur> $x rxr !xr xr : kxr;{r 70{ x{x{x80 {(x rxrDzr LBzrxr xryr9 D9xr+ yrxurxr y@]Cedar Terminal Input Facilities3the "clicks" information, and a chord field that has collected all key depressions since the last time allkeys were undepressed. The Instream provides operations for obtaining these values.ClassInstream.EventTime: TYPE=Intime.EventTime;Intime.EventTime: --(effectively)-- TYPE[3];This is not really an exported type at present, being fully specified in the definitions file. But itsinternal fields are not meaningful to clients. An EventTime represents the number of millisecondssince some specific time early in the century. Functions exist (in the Intime interface) for convertingthe present time to an EventTime, for performing simple arithmetic and comparisons on these times,etc. EventTimes may be used to denote positions within the Instream.ClassInstream.MousePosition: TYPE = Interminal.MousePosition;Interminal.MousePosition: TYPE = MACHINE DEPENDENT RECORD [mouseX: CARDINAL, mouseY: CARDINAL];These are expressed in Alto screen coordinates, at present. Expect these representations to change inorder to adapt to Cedar graphics' more abstract view of things.ClassInstream.PosResult: TYPE = ClassIncreek.PosResult;ClassIncreek.PosResult: TYPE = {tooEarly, tooLate, onTime};Describes the nature of the results of some of the Instream operations below.ClassInstream.MsTicks: TYPE = Intime.MsTicks;Intime.MsTicks: TYPE = Process.Milliseconds -- ... = CARDINAL --;Describes the nature of the results of some of the Instream operations below.From InterminalInterminal.KeyView: TYPE = {keyNames, keyFields, words, bits};Interminal.KeyState: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID KeyView FROM bits => [bits: PACKED ARRAY KeyName OF updown], words => [words: KeyArray], keyNames => [keyNames: KeyNames], keyFields=> [keyFields: KeyFields], ENDCASE];Interminal.KeyName: TYPE = { x0, x1, ..., x7, Keyset1,Keyset2,...,Keyset5, Red,Blue,Yellow, Five,...,U,V, ... Lock,Space,...,FR5};There are a number of useful ways to look at a representation of the keyboard state; these are the onesthat have been chosen for Cedar input. The bits array can be indexed by the KeyName values,which are also the values that are stored in Actions and Events. The keyNames variant lets youtreat the key state as a record of named bits. keyFields breaks the state up into smaller records, bydevice, once you know which device you're interested in. The client of Instreams and Events willnormally require only KeyNames, but may need to access other values to examine the control, shift,shift lock keys, or other "non-strobing" situations in efficient ways.Interminal.PaddleKeyName, KeyArray, KeyNames, KeyFields, Buttons,ButtonNames, Paddles, PaddleNames: TYPE = ...; frG bxr< `xr0]n{ x {x{xu[{x r{x XrH W;3xr U5xr T3xur= Rxur,xuO{ x{x{ x M,{ x{x{x{x{x@K{x {x IPrY G?Et{ x {x{ x C{ x {xu @r3xr>m{ x{x{x<{x{x{x{ u 9r3xr 6; 63g{ xX{x&1{ x {x{x{x{x0_{x{x{.x{x{x{x-V!+'*N)({x%{ x{x$""!  /rR &zrxzr '-xrxrxr  !zxr- 8zr xrxr xrD F { xX6@ g#{x ?[xCedar Terminal Input Facilities4These types are used to implement the ones described above. Every implementor of an Instream orIncreek client should read through them and pick out the types that seem relevant to the particularway the client code is going to use the package; the implementor will probably settle on a fairly smallnumber of them.Interminal.Spare1: CHARACTER=LOOPHOLE[201B];Interminal.Spare2: CHARACTER=LOOPHOLE[202B];Interminal.Spare3: CHARACTER=LOOPHOLE[203B];Interminal.ShiftSpare1: CHARACTER=LOOPHOLE[204B];Interminal.ShiftSpare2: CHARACTER=LOOPHOLE[205B];Interminal.ShiftSpare3: CHARACTER=LOOPHOLE[206B];These definitions provide representations in the "Ascii" domain for the three spare keys and theirshifted versions. They were arbitrarily chosen. Procedures and OperationsSome of these functions are currently implemented as INLINE procedures, and are thus tied to a particularimplementation of the Instream data record type. These will be changed if necessary.ClassInstream.EventIncorrect: ERROR[e: Event];ClassInstream.EventVanished: ERROR[eT: Intime.EventTime];ClassInstream.NewStdInstream: PROCEDURE RETURNS [ClassInstream.Instream];Creates an Instream on the standard Inscript that is filled with user terminal actions. It is possible toproduce Instream implementations that obtains actions from a different source, in which case this function would not be usedto create them. The result is an Instream object. It will have been positioned at the end of (the latestpoint in) the Inscript (corresponding to "now".)The implementation does not enforce a limit on the number of Instreams that may be created toexamine the same Inscript. This design will make more sense if at least two Instreams get created by somebody insome set of Cedar applications;.ClassInstream.GetEvent: PROCEDURE[self: ClassInstream.Instream] RETURNS [e:ClassInstream.Event];Returns the next Event from the stream that satisfies the client's specifications (see below.) If thestream is currently positioned at the end of the Inscript, this operation will wait as necessary untilactions constituting an acceptable Event have occurred. The client program should not be able todetect a difference between waiting and non-waiting situations, unless the client is also keeping track ofreal time.Raises ClassInstream.EventVanished if the Events denoted by the current Instream position havedisappeared to make room for newer ones. This will only happen if the Instream is reviewing ancienthistory. The EventTime parameter denotes the time of the earliest Action remaining in theInscript.ClassInstream.GetChar: PROCEDURE[self: ClassInstream.Instream, event:ClassInstream.Event_nilEvent] RETURNS [c: CHARACTER];If event is nilEvent (not supplied in the call), an event is obtained from the Instream. If theevent represents a keyboard keystroke, its Ascii code is returned. Otherwise GetChar raisesClassInstream.EventIncorrect, with the Event as a parameter. The intent is that a "character loop"can call GetChar repeatedly, acting on the incoming characters until an event occurs that is not acharacter event. Alternatively, the client can obtain an Event using GetEvent, determine that it is achar event, then obtain the corresponding Ascii code using GetChar. frG bAxr `xzrL _ Y ]Zf{ xuX{x{xX{ xu{x{xW^{ xu{x{xU{ x u{x{xTV{ x u{x{xR{ x u{x{x Pzr02 N1 Ky H|5{|! Gb/q|%D7{ xX{x A { x{x={ x{x{x{ x ;r xr xr-q :'t 8 rxrzr * 7B xr 4+xr 3gxrq1|q 1.{ xX {x{ x {x@-3{ x *rxr?zr )W,xr- ' xrzr( &Ob $ "s{ x rxrxr Gzr  kzrxr,xr xz{ xX{x{ x@8{ x{x{x rxrxrxrxr \xr mM;A{ xX{x{ x@9{x{ x 7frLv~vr 5xr2{ xX {x{ x@12{x{ x .rxuxr +{ xX{x{ x@*+{x{ x 'rH &Oxr3x rzr  $ xr !{ xX{x{ x@ { x@{x{ x @r5xuxr{ xX {x{ x@{x { x e{ x{x{ x@{x { x  r]This produces Events only when keyboard keys are depressed, and when mouse/keyset keys areactivated one way or the other.Some Inlines defined in ClassInstreamClassInstream.PowerSet: PROCEDURE [e1, e2, e3, e4: ClassInstream.Cause_none] RETURNS[ClassInstream.Causes] = ...;ClassInstream.In: PROCEDURE[candidate: ClassInstream.Cause, target: ClassInstream.Causes]RETURNS [BOOLEAN] = = ...;PowerSet[...] creates a Causes value, given two or more Cause or Causes values. The resultrepresents a set of Causes. What really happens is that the bits are OR'ed together; Cause isdefined so that the named values are powers of 2. frGb{ xX {x{ x@`{ x{x {x ^Br>xuxr \xr x r; [:"X{ xX {x{ x {x@V { x T3rE" R42 Q+M{ xX{x{ x Kri J#qr, H1(xz GrUC{ xX {x{ x Ar>m{ x {x{ x@<{ x {x :r;xr 9 -: 7D 6#2xX{x{ x@1U{ x{ x@/{ x ,zrzrxrxr +"xrR ) yzr$zyr (%xX"$>I"0!6<  r xr3  [yX% 0{ x {x{ x {x{ x { x{x { x{ x{x{x ur xrxrxr xur,|r xr 1 U@]~Cedar Terminal Input Facilities7In[...] determines whether the candidate is a member of the set of Causes in the target. If thecandidate is a Causes value, all of its members must be in the target. (TRUE iffLOGAND[candidate, target] = candidate.)These functions use inline definitions available in Powerset.Mesa/bcd, redefining their input andoutput types.Multiple "Clicks"Many user interfaces, including Tioga's, use the concept of multiple activations of the mouse, typicallywithin a given time and without excess motion of the mouse, to increase the number of interpretationsthat can be assigned to mouse buttons. In the Inscript world, with its emphasis on the recording ofactions, and the ability to respond to them more than once, and perhaps long after they occur, itseemed prudent to capture as much of the complexity of multi-click activities as possible at theInstream level of the user input facilities.The current interpretation is based almost entirely on Tioga's needs. Most of the extensions that come to mind could bereadily accommodated.The client uses SetEventSpecifications to specify, for each device, what kinds of events areinteresting. If either heldDown or heldUp is specified, the client must also supply a time, inmilliseconds, that is used as follows.Suppose that all keys are "up", and have been for some time. The client has specified that, on themouse, only heldDown and heldUp Events are interesting (at present, specifying one is equivalent tospecifying both -- an implementation expedience), with a clickTime of 100 ms. The user now depresses amouse button. The Instream would not immediately interpret this Action as an Event, but would"start a timer" to expire in 100 ms. If the user moves the mouse "too far" (currently a constant -- whatshould it be?) within that 100 ms., or depresses or releases some other key, or does not perform any moreActions within the 100 ms., a heldDown event results. If the user releases the button within the 100ms., the timer is restarted, and no event results. When the user finally waits long enough, moves farenough, or activates some other key, a heldDown or heldUp event results.If strobeDown and/or strobeUp had also been specified in the above example, each of the actualkey activations would also have resulted in an Event; these additional events would have beenproperly interleaved with exactly the same held-style Events that occurred in the example.If held... events have been specified, each event will include, in the clickCount field, the numberof key activations (down or up), including the one that resulted in the event, that have occurred withinthe same multi-click sequence. Otherwise, the clickCount field will always contain a 1 or something.Note that all of these time-related operations use the times that are recorded in the Inscript; thesemantics of the click activities are independent of the celerity with which the client is examining theInscript. However, the amount of real time that a given call on, say, GetEvent will require isdependent on whether the stream is positioned at "end of script."There may well be other useful things to say about this implementation; what are they? frG bxurxr xrxzr `F Gxr*xr{r _{xr \Q [: W;u Tryr0 R&? Q/zr- Oa MD L{zr# IsqR& H Drxr" zr Ccxzrxr, A& >-6 =/ xrxrzrq, ;0rxur% :'zr&xzrxr 8Lq 7 r[ 5xrzrzr9 4$xr* 2'xzrx r /hx zrxzr@ -/xrzr ,_6xr )4xuxurxrx r '2zr &,/x zr #Fxur !}!G xr*xr y trA IqV z ?P4oCedar Terminal Input Facilities8IncreeksAn Increek is a lower-level object that can be used to gain access to an Inscript. The Instreamimplementation, in fact, simply provides interpretations for collections of low-level individual userActions which are extracted using Increeks. Increek operations largely parallel the Instreamoperations. Details and discussion of the Increek level follow.Data TypesFrom ClassIncreekOne obtains Actions by invoking operations in an object called an Increek. The kind field of theAction indicates what happened, and the other fields provide the details.ClassIncreek.Increek: TYPE = REF IncreekObject;ClassIncreek.ActionKind: TYPE = {deltaEventTime, eventTime, deltaMouseX,deltaMouseY, mousePosition, keyDown, keyUp, keyStillDown};ClassIncreek.Action: TYPE = LONG POINTER TO ClassIncreek.ActionBody;ClassIncreek.ActionBody: TYPE = RECORD [deltaDeltaTime: Intime.DeltaDeltaTime _ 0,contents: SELECT kind: ClassIncreek.ActionKind FROMdeltaEventTime => [value: Intime.DeltaTime _ NULL],deltaMouseX, deltaMouseY =>[value: ClassIncreek.DeltaMouseValue _ NULL],keyDown, keyStillDown, keyUp =>[value: Interminal.KeyName _ NULL],eventTime => [eventTime: Intime.EventTime _ NULL],mousePosition => [mousePosition: Interminal.MousePosition _ NULL],ENDCASE];If the time between Actions is short enough, the time difference can be recorded in thedeltaDeltaTime field of the next Action. Otherwise it will appear as a separate Action precedingthe "real" one, either as a deltaEventTime Action if the interval is not too long, or as a fulleventTime value. Similarly, mouse motions are recorded either as small incremental mouseX andmouseY Actions or as full mousePosition values. The keyUp and keyDown Actionscorrespond to actual user keyboard activities.keyStillDown is used to record efficiently the entire keyboard state at the beginning of each"session", and at the beginning of each inscript page; one Action appears for each key that is "stilldown", typically zero, one, or two. This allows the state to be recreated efficiently without scanningthe entire history. The full time and full mouse position are also inserted into the inscript at thesetimes. keyStillDown[value: allUp] should be interpreted as a request to clear the state to indicateno depressed keys.As with an Instream, an Increek defines a position within its Inscript. This position represents anEventTime at which the input devices were in a particular state, defined by the typeInscriptPosition (ViewPosition was intended as a READONLY version of this type, but I couldn'tget it to work right.)ClassIncreek.InscriptPosition: TYPE = REF ClassIncreek.InscriptPositionBody;ClassIncreek.ViewPosition: TYPE = REF -- can't get READONLY to work right --ClassIncreek.InscriptPositionBody; frG bu ^rxr!xrx ]nrU [xrxzrxr!x Zfr+xr W;y S Tr P xr/xzrzr O`xrCL5{ xX{x{xI { x {x+@G;DZ{ x{x{FxX{ x B{ x {x{x@@~{x@> {x{ x {=vx{x {x;$:n{ x{x8$7f{ x {x5{x {x4^!{ x{x2{@0x -Vrxr3 +x rxrxr *Nx rxr (xrDxr 'Fx r x rxrx %r. "x rN !0 xr$ g  a xr,   xrxrxzr SxrEF0 xzrx r{r K{ xX{x{x{ x{ x {x{x{FxX@ { x ?[;Cedar Terminal Input Facilities9ClassIncreek.InscriptPositionBody: TYPE = RECORD [-- location in inscript file --inscript: PRIVATE ClassInscript.Inscript, -- for releasinginscriptPage: PRIVATE ClassInscript.InscriptPageDescriptor,-- absolute state at that point --eventTime: Intime.EventTime _ NULL,mousePosition: Interminal.MousePosition _ NULL,keyState: Interminal.keyState _ NULL,chordState: Interminal.keyState _ NULL,downCount: INTEGER _ 0];chordState is cleared to allUp whenever keyState is about to leave the allUp condition;keyDown and keyStillDown events cause the corresponding chordState bits to be set, butkeyUp events do not effect chordState. Probably the shift lock key should not participate in the chordState.PosResult describes the nature of the results of some of the Increek operations below.Acceptance is a client-provided parameter to GetAction. DeltaTime and DeltaDeltaTime aretime values of differing lengths, in units corresponding to the process-scheduling "tick" interval.Intervals at this resolution can be represented in fewer bits. Unless the client examines these Actionfields directly, it will not have to deal with these units, since most of the time-related functions convertthese times back to millisecond-resolution units. MousePosition, KeyState, etc., are as defined inthe Instreams section. DeltaMouseValue is a short, incremental version of the components ofMousePosition. WaitMode determines the timeout behavior of the GetAction operation.ClassIncreek.PosResult: TYPE = {tooEarly, tooLate, onTime};ClassIncreek.Acceptance: TYPE = {clicks, clicksAndMotion, all};ClassIntime.DeltaTime: TYPE = ... CARDINAL [0..256);ClassIntime.DeltaDeltaTime: TYPE = DeltaTime [0..32);ClassIncreek.DeltaMouseValue: TYPE = [-128..128);ClassInscript.WaitMode: TYPE = {forever, dontWait, timed};Procedures and OperationsClassIncreek.IncreekError: ERROR[code: ClassIncreek.IncreekErrorCode];ClassIncreek.IncreekErrorCode: TYPE = {outOfBounds -- position no longer valid during ReadAction};NewStdIncreek provides the standard Increek implementation. Its Actions are obtained fromthe standard Inscript implementation, which directly records user terminal events. If the templateargument is NIL, the result is a new Increek on which the SetAtLatest operation has just beenperformed. One can also call this function to obtain an Increek that is a copy of another Increek, thetemplate; the copy represents another source of Actions, positioned at the same point in the script.This "produce copy" function should be an object operation.The CopyIncreek operation is equivalent to SetAtTime[self, GetTime[template]]; frGb{ xX{x{x@`@_ {x{ x@] {x{ x@Z"@Y {x {x@W{ x{x@U { x {x@Ty { x {x@R {x@Qq NF r xr xrxr Lxrx rx r K>xrx rq&{ r Hxr#3$xrz Fx r"xzx rzx zr E $? C/2x Brd @~1x rxr >yrxr5 =vxr(xzr :K{ xX {x7{ x {x"3{ x {FxX{Fx0{ xX{FxX-{ x{Fx *r{ x rX{x 'Fy${ xX {x{ x!{ x{x@ ?9@  rxrxr  xr0x r {rxr x r  -xr xr Txr zr- q:r x zrux# J ^@ZCedar Terminal Input Facilities10ClassIncreek.NewStdIncreek: PROCEDURE[template: ClassIncreek.Increek_NIL] RETURNS[ClassIncreek.Increek];ClassIncreek.Release: PROCEDURE [self: ClassIncreek.Increek] RETURNS [nilIncreek:ClassIncreek.Increek];ClassIncreek.CopyIncreek: PROCEDURE [self: ClassIncreek.Increek, template:ClassIncreek.Increek];The SetAt... functions in the ClassInstream interface parallel the ones provided here. Each ofthese functions positions the Increek such that the next Action is the earliest in the Inscript thatoccurred after the specified time. In the process of positioning the Increek, it updates the Increek'sViewPosition (state record) to represent the state of the terminal corresponding to the new position.The Get...Time functions return the same values as the corresponding functions in theClassInstream interface. GetPositionFrom returns the ViewPosition record describing theterminal state at the current position. It is provided in lieu of the slew of functions (as inClassInstream) that would otherwise be required; the ClassIncreek interface is intended for useby system implementors to provide higher-level interfaces.ClassIncreek.SetAtEarliest: PROCEDURE [self: ClassIncreek.Increek];ClassIncreek.SetAtLatest: PROCEDURE [self: ClassIncreek.Increek];ClassIncreek.SetAtTime: PROCEDURE [self: Increek, eventTime: Intime.EventTime]RETURNS [pR: ClassIncreek.PosResult];ClassIncreek.GetTime: PROCEDURE [self: ClassIncreek.Increek]RETURNS [eT: ClassIncreek.EventTime];ClassIncreek.GetCurrentTime: PROCEDURE [self: ClassIncreek.Increek]RETURNS [eT: ClassIncreek.EventTime];ClassIncreek.GetPositionFrom: PROCEDURE [self: ClassIncreek.Increek]RETURNS [p: ClassIncreek.ViewPosition];Finally, to examine the next action in the Inscript, use GetAction. It returns an Action that isextremely unsafe -- it is a LONG POINTER to an ActionBody whose data is guaranteed not to changeonly until the next call on GetAction or SetAt... Copy it if you want it to last longer. ThewaitMode and waitInterval parameters allow for the implementation of higher level abstractionsthat involve timeouts. They operate based on the times recorded in the file, rather than the present realtime. Thus, GetAction may return with a timeout indication immediately after the client calls it. IfwaitMode is forever, GetAction will not return until an Action has occurred; if dontWait, returnis immediate unless additional Actions remain in the Inscript. If waitMode is timed, the nextAction will be returned only if it occurred within waitInterval ticks following the preceding Action.Exception: if the Increek is at "end of file", GetChar will wait the indicated interval (in the absense of new Actions) beforereturning; and this interval begins at the real time of the call. These semantics are considered to be a compromise.GetAction will raise IncreekError[outOfBounds] if the Increek is examining sufficiently oldActions that their storage must be released to make way for new ones.ClassIncreek.GetAction: PROCEDURE [self: ClassIncreek.Increek, waitMode:ClassInscript.WaitMode _ forever, waitInterval: Intime.MsTicks _ 100,acceptance: ClassIncreek.Acceptance _ clicks]RETURNS [a: ClassIncreek.Action]; frG?b{ xX{x { x{x{@`x{ x]n{ x{x{ x@[{ xX{ x {x{ x@W;{ x Trxr x r4 Rxzrxrxr Q 8xrzr Ox rG LXKx rCL Jx r xzr x zr IP:% Gx r(x zr  FH:C{ xX{x{ x?{ x {x{ x<{ x {x{x @;A{x{ x 8{ x{x{ x@6{x{ x 3g{ x{x{ x@1{x{ x .{ x{x{ x@-3{x{ x *r+zrxr xr (y r{ rx r 'xzrxzr+ %|xrx zr1 #yr "s xzrO xrzrxzrxrxr kxzr xrxrxr zr%x rzq |q |q9|q &+tq rq xr xrxzr vxzr=K{ xX {x{ x@{ x"{x @C { x@{x{ x x@YCedar Terminal Input Facilities11GetAction returns NIL if the timeout criteria hold or if it encounters an Action not requested by theacceptance parameter. This makes it very difficult, without rechecking the timeout criteria in the client, to determinewhy a NIL resulted; for some reason this state of affairs is acceptable to the current implementation, but it should probablybe changed.How to UseThe entire package is available as Instream.bcd, on the above-cited directory. The definitions files ofinterest to the client are also available there. It imports the CWF package rather than including it; allother imports (it is claimed) come from the system.Start the system by creating an Instream via ClassInstream.NewStdInstream (or, if you're notusing Instreams, ClassIncreek.NewStdIncreek. It will take several seconds and will create a file"Inscript.Inscript." This file is made to reflect "truth" every few seconds; subsequent "runs" will findthe current position in this file.Once the package has started, the standard Mesa keyboard/cursor packages have been disabled (butnot necessarily removed from storage.) The SWAT, SWAT, and SWATfunctions still work, although they also cause Actions to be recorded, and will not work on replay.Current Shortfalls1. There's no way to reduce the number of actions placed into the inscript by the terminal code -- i.e.,by eliminating or reducing the frequency of mouse motion actions, when not accompanied by clicks, attimes when such actions are not of interest (most of the time.) Implementing this feature would notbe without risk -- during type ahead situations, there might not be enough available information.2. There is currently no event filtering based on mouse coordinates. This could be supplied elsewhere,but it was always intended to provide such facilities here. I believe that the clipping capabilities ofCedar graphics can be used to make this filtering very efficient and effective. Whether this is done atthis level depends to some extent on how many independent processes end up "pulling." I'll discussthis further with relevant parties. Mouse coordinates are also currently expressed in screen units,rather than any device-independent manner. They clearly should be recorded this way, but may wantto be presented to clients differently.3. There is currently no version that records actions forever. This should perhaps be done, if at all, atthe event level, after it is determined which actions are interesting. The current inscript is a circularbuffer of (now a constant 100 Alto pages, soon to be variable) length, which can capture about aminute's worth of reasonable continuous activity on each page.4. There is no specific mechanism included for changing cursor shape based on mouse position (andperhaps the state of the mouse buttons.) Is this needed? If so, I can treat it along with point 2.5. Chord-fetching operations are perhaps still too primitive. ClassInstream.GetChord is notimplemented, nor is it very well defined. ClassInstream.GetChordState, along withClassInstream.GetDownCount, leave one with not much work remaining, however. If Shift lock islocked down, simple algorithms for dealing with chords will fail.The interfaces described here should be sufficient for most clients of the Inscript system, except forthose who desire to implement the interfaces for different sources, or using different filing methods.For the more adventurous, an appendix follows. It describes the Intime and Interminal interfaces inmore detail, and discusses some aspects of the current implementation. frG? bxr{r5xr `q] _9{qt ] r Zx Wr35 U)ur& Ty3 QNxr{ xr Oxr{ x zr NFi L" IX H8" F/zr* Ccu @7rb >+9 =/I ; W 8zr2 6e 5xO 3Q 2p*: 0+7 /h' ,<J *zrS )4>" '> $J #:* ?{ xr Q+{ ux r { ux r 9 IA Fzr Cf Azrz r ;F @[XThe Interminal DefinitionsThe definitions module Interminal supplies a set of data types that describe the "Alto" terminal inputconfiguration: keyboard, keyset, and mouse. The InterminalImpl module, part of the currentimplementation, does not export anything to Interminal, so the names are in that sense coincidental.A later section discusses this implementation.The intent of the Interminal types is indicated in the previous Instreams sections; the details areincluded here for completeness.Data TypesInterminal.MousePosition: TYPE = MACHINE DEPENDENT RECORD [mouseX: INTEGER, mouseY: INTEGER];Interminal.updown: TYPE = {down, up};Interminal.KeyView: TYPE = {keyNames, keyFields, words, bits};Interminal.KeyState: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID KeyView FROMbits => [bits: PACKED ARRAY Interminal.KeyName OF Interminal.updown],words => [words: Interminal.KeyArray],keyNames => [keyNames: Interminal.KeyNames],keyFields => [keyFields: Interminal.KeyFields],ENDCASE];Interminal.KeyName: TYPE = {x0, x1, x2, x3, x4, x5, x6, x7, Keyset1, Keyset2, Keyset3, Keyset4, Keyset5, Red, Blue, Yellow,Five, Four, Six, E, Seven, D, U, V, Zero, K, Dash, P, Slash, BackSlash, LF, BS,Three, Two, W, Q, S, A, Nine, I, X, O, L, Comma, Quote, RightBracket, Spare2, BW,One, ESC, TAB, F, Ctrl, C, J, B, Z, LeftShift, Period, SemiColon, Return, Arrow, DEL, FL3,R, T, G, Y, H, Eight, N, M, Lock, Space, LeftBracket, Equal, RightShift, Spare3, FL4, -- FR5, -- allUp};This implementation has purloined the code for FR5 -- "Cedar" terminals do not use it -- in order toobtain a code for the use of the implementation. The client may see this code, renamed allUp, as partof a stillDown action -- see the Increeks section.The following INLINE functions are just pretty loopholes for the implementation's benefit.Interminal.Kn: PROCEDURE [value: UNSPECIFIED] RETURNS [kN: KeyName] = INLINE {kN _ LOOPHOLE[value]};Interminal.Kv: PROCEDURE [value: UNSPECIFIED] RETURNS [kV: CARDINAL] = INLINE {kV _ LOOPHOLE[value]};Interminal.KbdKeyName: TYPE = KeyName [Five..allUp];Interminal.ButtonKeyName: TYPE = KeyName [Red..Yellow];Interminal.PaddleKeyName: TYPE = KeyName [Keyset1..Keyset5];Interminal.KeyArray: TYPE = ARRAY [0..5) OF WORD;Interminal.KeyNames: TYPE = MACHINE DEPENDENT RECORD [ bp ^qr q. ]n1r q [,r q  Zf. W;r q$sq U Rt O`u rXuruFrX@Mur urJu rur Gu rur&DZu r uruFrX@BuFrXuARruF rXu ruru r?u r >Ju r <u r ;Au@9r6u rur51uF_3O2Q1yZ0f.r +iq/rq )0rqrq (`rqrq %5 rqF" u rXuru rurur@ ur.u ruru rururur@ur~u r ur'u ruru ruru r ururxu r uruFrX 1@UAAppendix: Cedar Terminal Input Facilities2blank: [0..377B],Keyset1, Keyset2, Keyset3, Keyset4, Keyset5: updown, Red, Blue, Yellow: updown,Five, Four, Six, E, Seven, D, U, V, Zero, K, Dash, P, Slash, BackSlash, LF, BS: updown,Three, Two, W, Q, S, A, Nine, I, X, O, L, Comma, Quote, RightBracket, Spare2, BW: updown,One, ESC, TAB, F, Ctrl, C, J, B, Z, LeftShift, Period, SemiColon, Return, Arrow, DEL, FL3: updown,R, T, G, Y, H, Eight, N, M, Lock, Space, LeftBracket, Equal, RightShift, Spare3, FL4, -- FR5 -- allUp:updown];Interminal.KeyFields: TYPE = MACHINE DEPENDENT RECORD [mesaMemorialBlankField: [0..377B],paddles: Interminal.Paddles,buttons: Interminal.Buttons,keys: ARRAY [0..3] OF WORD];Interminal.ButtonView: TYPE = {buttonChord, buttonNames, buttonValue};Interminal.Buttons: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID ButtonView FROMbuttonChord => [buttonChord: [0..10B)],buttonNames => [buttonNames: Interminal.ButtonNames],buttonValue => [buttonValue: Interminal.ButtonValue],ENDCASE];Interminal.ButtonNames: TYPE = MACHINE DEPENDENT RECORD[Red, Blue, Yellow: updown];Several of the enumerated types that follow specify their implementation representations explicitly sothat said representations can be powers of two. This allows them to be used in conjunction with thePowerSet functions, described with Instreams.Interminal.ButtonName: TYPE = MACHINE DEPENDENT{Red(0), Blue(1), Yellow(2)};Interminal.ButtonValue: TYPE = MACHINE DEPENDENT{RedYellowBlue(0), RedBlue(1), RedYellow(2), Red(3), BlueYellow(4),Blue(5), Yellow(6), None(7)};Interminal.PaddleView: TYPE = {paddleChord, paddleNames, paddleValue};Interminal.Paddles: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID PaddleView FROMpaddleChord => [paddleChord: [0..40B)],paddleNames => [paddleNames: Interminal.PaddleNames],paddleValue => [paddleValue: Interminal.PaddleValue],ENDCASE];Interminal.PaddleNames: TYPE = RECORD [Keyset1, Keyset2, Keyset3, Keyset4, Keyset5: updown];Interminal.PaddleName: TYPE = MACHINE DEPENDENT{Keyset1(0), Keyset2(1), Keyset3(2), Keyset4(3), Keyset5(4)};Interminal.PaddleValue: TYPE = MACHINE DEPENDENT{Keyset1(17B), Keyset2(27B), Keyset3(33B), Keyset4(35B), Keyset5(36B),None(37B)}; fq)GbAuFaO_W^Y]Kb\fZYorVDu rX uruFrX@T"@S<u r@Qu r@P4urMu r ur+Iu ruruFrX@HYuFrX uFr'EQu r Cu r BIu@@r=u r uruF@<rX 8q33 7f04 5rqrq2u rX uruF rX/u r uruFr@.XB@,)Wu r ur+&,u ruruFrX@$uFrX u@#$r'@!u r @ u r @urlu r urur@5u r uruFr@9X< u r uruFr@XE@  ?YM7Appendix: Cedar Terminal Input Facilities3Interminal.allUp: KeyState;Interminal.Spare1: CHARACTER=LOOPHOLE[201B];Interminal.Spare2: CHARACTER=LOOPHOLE[202B];Interminal.Spare3: CHARACTER=LOOPHOLE[203B];Interminal.ShiftSpare1: CHARACTER=LOOPHOLE[204B];Interminal.ShiftSpare2: CHARACTER=LOOPHOLE[205B];Interminal.ShiftSpare3: CHARACTER=LOOPHOLE[206B]; fq)Gbu rX^u rurur\u rururZu rururXu r ururVu r ururTu r urur\ Q*$HQAppendix: Cedar Terminal Input Facilities4IntimeIntime provides data types for representing EventTime values in a number of sizes; all representtimes, or time increments, expressed in milliseconds. There are also some incremental time valuesexpressed in a courser grain (the actual value stored in Increek Actions.) This interface alsosupplies a number of procedures for manipulating these various times and their incrementalderivatives.Data TypesIntime.MsTicks: TYPE = Process.Milliseconds; Intime.MSTicks: TYPE = LONG CARDINAL; -- long milliseconds --Intime.DeltaTicks: TYPE = Process.Ticks; -- probably 1/60 sec.Intime.DeltaTime: TYPE = Intime.DeltaTicks [0..256);Intime.maxDeltaTime: Intime.DeltaTime = LAST[Intime.DeltaTime];Intime.msPerDeltaTick: Intime.MsTicks; -- converts between the representationsIntime.deltaTicksPerSecond: Intime.DeltaTicks;Intime.DeltaDeltaTime: TYPE = Intime.DeltaTime [0..32);Intime.maxDeltaDeltaTime: Intime.DeltaDeltaTime = LAST[Intime.DeltaDeltaTime];Intime.Overlap: TYPE = {loShort, hiShort};Client should think of EventTime as TYPE[3].Intime.EventTime: TYPE = PRIVATE MACHINE DEPENDENT RECORD[SELECT OVERLAID Overlap FROMloShort => [lo: MsTicks, hi: LONG CARDINAL],hiShort => [lower: MSTicks, higher: CARDINAL],ENDCASE];ReadEventTime returns the current time of day, as an EventTime.Intime.ReadEventTime: PROCEDURE RETURNS [EventTime];These functions perform arithmetic on EventTimes, or answer questions about them. IsLaterTimeis TRUE if t1 is later than t2. The result of EventTimeDifference is positive under the samecircumstances.MsTicksToDeltaTicks returns its input, converted to DeltaTicks units; the rem return value isapproximately rem_s-(dT*msPerDeltaTick). This is clearly intended for a very specialized use, towhich it is put in IncreekImpl.mesa.Intime.EventTimeDifference: PROCEDURE [t1, t2: LONG POINTER TO READONLY Intime.EventTime]RETURNS [Intime.MsTicks]; fq)G bp ^rq&rq$ ]nJ [9rqrq ZfF X Ut RurXurO`ururuF rXL5ur ur'I ur ururEur ur urur Burur0?urur <\ururur90urururur6ur# 2qrqr/urX uruF@.*r@,Xuru+"ruF r)X$ur(u@&r #j q(r  ?urXuFrX q&r qr quqrqrqrq  rq!r q rq \ r u rq vqurXur uF wurXur @)urur| ?Y)@Appendix: Cedar Terminal Input Facilities5Intime.IsLaterTime: PROCEDURE [t1, t2: Intime.EventTime] RETURNS [BOOLEAN] ;Intime.AddDeltaTimeToEventTime: PROCEDURE [eT: Intime.EventTime, dT:Intime.DeltaTime] RETURNS [rT: Intime.EventTime];Intime.SubtractMsTicksFromEventTime: PROCEDURE [eT: LONG POINTER TO Intime.EventTime, ticks: Intime.MsTicks];Intime.MsTicksToDeltaTime: PROCEDURE [s: Intime.MsTicks]RETURNS [dT: Intime.DeltaTicks, rem: Intime.MsTicks]; fq)GburX ur ur urur^ururur @]nur qurur ZCurur@XuFrXururUururur@Tururur P4$n[Appendix: Cedar Terminal Input Facilities6InOSThe Inscript facilities have been implemented for both the Alto/Mesa and the Pilot/Mesa versions ofCedar. The source changes have been limited to two implementation modules: the implementation ofClassInscript, and the implementation of an interface intended to confine operating systemdependent functions, InOS.InOS contains redefinitions of most of the CWF formatted-write functions, for two reasons. The firstis that different initialization procedures are needed to use these functions in the two OS worlds (theimplementation of InOS is responsible for providing default initialization if the functions are to beused). The second is that a "production" implementation may choose to supply dummy proceduresfor these operations -- they are used only for diagnostic purposes in the Inscript package.ProceduresInOS.WF0: PROC [s: STRING];. . .InOS.WF4: PROC [s: STRING, a,b,c,d: LONG POINTER];InOS.WF: PROC [s: STRING, a,b,c,d: LONG POINTER _ NIL];InOS.WFN: PROC [s: STRING, array: DESCRIPTOR FOR ARRAY OF LONG POINTER];InOS.WFC: PROC [CHARACTER];InOS.WFCR: PROC;InOS.SetCode: PROC[CHARACTER, PROC[LONG POINTER, STRING, PROC[CHARACTER]]];InOS.SetWriteProcedure: PROC [PROC[CHARACTER]] RETURNS [PROC[CHARACTER]];InOS.WFError: ERROR;The remainder of this interface will be of interest only to those providing new implementations ofClassInscript and/or Interminal. It supplies procedures for dealing with the keyboard hardware,the real time facilities, and the high-priority process activities required to provide the keyboard"interrupt" process (supplied by InterminalImpl.)These functions merely return the fixed long addresses that point to the states of the keyboard, mouse,and cursor. They appear here because Pilot supplies functions obscuring the absolute locations, whilethe Alto world assumes that the client knows the addresses.InOS.GetKeyboard: PROC RETURNS[LONG POINTER TO READONLY UNSPECIFIED];InOS.GetMousePosition: PROC RETURNS[LONG POINTER TO READONLY UNSPECIFIED];InOS.GetCursorPosition: PROC RETURNS[LONG POINTER TO UNSPECIFIED];Similarly, the following functions return the current values of the high-resolution interval timer(ReadFastClock), and the long-term seconds-resolution clock (GetTimeParameters). The latterfunction also returns a value corresponding to the number of interval-timer ticks in a second; inaddition, it should perform any initialization required to perform these two functions (an issue in thecurrent Alto implementation).InOS.ReadFastClock: PROC RETURNS [LONG CARDINAL];InOS.GetTimeParameters: PROC RETURNS [seconds: LONG CARDINAL,fastOneSecond: LONG CARDINAL]; -- pulses per secondSetupTerminalHandler must perform any OS-dependent activities related to shutting off anyexisting terminal handlers and starting ours. WaitForTick provides a non-busy wait until the nextprocess-scheduling interval (approx. 60 hz.). MakeCallerResident must lock the code segment of fq)G bp ^qrqB ]nK [r q <  Zfrq W;rq&rq# U=* T3rqO R<" Q+Jrq Mt KurXururI Fururur uF rDurXurur uFrXurBurururu ruFr@urXurur>urururururuFrXuru r!ururururururu r q-5 cr q)rq 9( [T vqurXururururTururur urur@urur q@ r q( /rq  U?]Appendix: Cedar Terminal Input Facilities7the caller into physical memory.InOS.SetupTerminalHandler: PROC;InOS.WaitForTick: PROC;InOS.MakeCallerResident: PROC; fq)G b^urXur\ur urZCurur V:.Appendix: Cedar Terminal Input Facilities8ClassInscript and InterminalThe ClassInscript interface provides an Inscript object, whose function is to record variable-lengthentries in a disk file, and to supply these entries on request at a later time. In addition, the Inscript(with the help of its clients) periodically records sufficient information to allow the current outputposition to be reestablished after a system crash or other uncontrolled termination of the package.Beyond that, the Inscript knows little of the semantics of the entries it records. For the courageousimplementor, there follows a brief description of the ClassInscript interface. Following that is aneven briefer exposition of the current implementation.Data TypesAn Inscript object represents the inscript file as a whole, along with all the operations pertaining toit. The InscriptPageDescriptor describes a particular page within the file, and a position withinthat page; there may be more than one of them. It does not supply any operations. In retrospect,probably the InscriptPageDescriptor should become the Inscript object. This would reduce considerably the clumsinessof some of the following specifications.There are two kinds of Inscript clients; those who wish to record into the inscript -- recordingclients -- and those who wish to read entries from them -- plain old clients. At present there is butone recording client for an inscript, although that is not a requirement. Recording clients will typicallywish to read as well.An InscriptPageNumber is a "virtual page number" of a page in the Inscript file.ClassInscript.Inscript: TYPE = REF InscriptObject;ClassInscript.InscriptPageDescriptor: TYPE = REF InscriptPageDescBody;ClassInscript.InscriptPageNumber: TYPE = INTEGER;During initialization, the recording client must supply a COmProc and a KEyProc to assist inreestablishing the current "end of file" position within the inscript file. KEyProc should assume thatdescriptor is positioned at the beginning of a page, and should extract an OrderKey from thatpage (this clearly requires that the client must record an OrderKey at a known place within eachpage as it writes the page.) COmProc, given two OrderKeys, should return TRUE iff a is "lessthan" b. In the Increek implementations, OrderKeys are EventTimes. The Increek implementation knowsnothing about OrderKeys except their size.ClassInscript.OrderKey: TYPE = RECORD [a, b, c: WORD];ClassInscript.COmProc: TYPE = PROCEDURE [a, b: ClassInscript.OrderKey] RETURNS [aLessB:BOOLEAN];ClassInscript.KEyProc: TYPE = PROCEDURE [inscript: ClassInscript.Inscript, descriptor: ClassInscript.InscriptPageDescriptor]RETURNS [ClassInscript.OrderKey];In order to read entries from the inscript, the client must supply a ReadAssignment procedure. Itsjob is to coerce the LONG POINTER argument, assumed to denote an entry of interest to the client, intoa comprehensible data type, determine the entry's length, copy the entry to a new location if desired,and return the length of the entry to the caller, for use in advancing to the next entry..ClassInscript.ReadAssignment: TYPE = PROCEDURE [p: LONG POINTER TO UNSPECIFIED]RETURNS [wordsToAdvance: CARDINAL];WaitMode is used in the primitive WaitForEntry function used to implement the waiting activitiesin ClassIncreek.GetAction. Its use is described in detail in the Increek section.ClassInscript.WaitMode: TYPE = {forever, dontWait, timed}; fq)G bp ^qp qrq4 ]nar [q X Zfc Xrq0 W^r q! U6 Rt Oqrq0, Mrq< L{7x J uqxux7 Is( FHqrq)t Dq[ C@T A >rq-rq<u rX urur:nu rurur8]u rurur 51q:rqrq 3Mrq 2)r q*rq 0;rq  /!rq rqvqrq -rqxuxquxu qrq , rq*u rX urur ur'u rururu r ur@&sur$au rurur@" u ru r@!Yuru r .qEr q  w q@ &%A Zu rXururuFr@ urXur qr q2 ]u rq)rqLu rX ur t @YAppendix: Cedar Terminal Input Facilities9ClassInscript.InscriptErrorCode: TYPE = {entryOutOfBounds, -- trying to position out of bounds or old stuff has disappearedinvalidInscriptSpecs, -- bad arguments to NewStd...invalidInscriptFile, -- while opening old inscript filedescriptorUninitialized -- in AdvancePage--,invalidPageKey -- in KeyProc during intitialization --};ProceduresNewStdInscript creates an Inscript using the standard implementation, discussed below. Inaddition to the obvious parameters, initializeFile will force a reinitialization of the inscript file evenif it already exists, the inscript file will contain 2^lnFileSize data pages, and the inscript history willbe discarded in groups of 2^lnGroupSize pages to make room for new entries. The Releaseoperation cleanly terminates an input session (no one calls this one, in current implementations.)GetPageLimits returns the "virtual page numbers" of the pages that currently exist within theinscript file. Functions like ClassIncreek.SetAtTime use this information to initialize their searchactivities.ClassInscript.NewStdInscript: PROCEDURE [fileName: STRING,KeyProc: ClassInscript.KEyProc, ClassInscript.ComProc: COmProc, initializeFile:BOOLEAN _ FALSE,lnFileSize: CARDINAL_7, lnGroupSize: CARDINAL_4]RETURNS [ClassInscript.Inscript];ClassInscript.Release: PROCEDURE [self: ClassInscript.Inscript] RETURNS [nilInscript:ClassInscript.Inscript];ClassInscript.GetPageLimits: PROCEDURE [self: ClassInscript.Inscript]RETURNS [earliestPage: ClassInscript.InscriptPageNumber, latestPage:ClassInscript.InscriptPageNumber] ;An InscriptPageDescriptor, when created, does not refer to any inscript page; other operationsmake it valid. CopyPageDescriptor copies the information of one descriptor into another,maintaining any necessary internal reference counts (don't perform this function any other way.)ReleasePageDescriptor discards one.ClassInscript.CreatePageDescriptor: PROCEDURE [self: ClassInscript.Inscript]RETURNS [rDescriptor: ClassInscript.InscriptPageDescriptor];ClassInscript.CopyPageDescriptor: PROCEDURE [self: ClassInscript.Inscript, dest:InscriptPageDescriptor,source: ClassInscript.InscriptPageDescriptor];ClassInscript.ReleasePageDescriptor: PROCEDURE [self: Inscript, descriptor:InscriptPageDescriptor]RETURNS [nilPageDescriptor: InscriptPageDescriptor];Using the KEyProc and the COmProc, the ClassInscript implementation must establish a validoutput position when the Inscript object is created. Subsequently, the only valid recording operationsare SetWritePage and WriteEntry.WriteEntry records the value of the entry array into the next available locations in the current page(using internally maintained page descriptors), failing if there is not enough room. The recordingclient must then call SetWritePage in order to move on. Things are done this way so that the clientcan record per-page information (e.g., the page's OrderKey) at the beginning of each page. Afterinitialization, the client should call WriteEntry without any initial call to SetWritePage, and theinitialization must take care of guaranteeing that this works -- the output position must be set at "endof file".SetWritePage will advance to the next "virtual" page, increasing the value ofGetPageLimits[...].latestPage by one, and possibly increasing ditto.earliestPage, thusdestroying some history. It will fail only under the circumstances (which should be made vanishingly fq)Gbu rXur@`R@_3@]7@\,@Z8 W^t T3r q rq- R r q8 Q+7r q* Or q*r N#q5- Lr qH Ku r q# I Gu rXur ur@Fu ru r!@D}urur@B urur@Auuru r ?du ruru r ur @=u r ;u ruru r@:Kuru r@8u r 5qrq6 4rq7 2` 1rq .u rXuru r@-zuru r+iu ruru r@)@(`u r&Ou rur@$@#Gur- q rqrqr q& rq& r qr q r q%6 dN r qB \*rq' 'r qr q T-;  r q8 rqJKrq L n U@]Appendix: Cedar Terminal Input Facilities10unlikely) that no physical page in the inscript file is available for use. On return, the page descriptorwill represent the first data position within the new page.ClassInscript.WriteEntry: PROCEDURE [self: ClassInscript.Inscript, entry: LONG DESCRIPTOR FORARRAY OF WORD]RETURNS [success: BOOLEAN];ClassInscript.SetWritePage: PROCEDURE [self: ClassInscript.Inscript] RETURNS [success:BOOLEAN];To read from the inscript, a client performs ReadEntry operations, supplying a ReadAssignmentprocedure in order to interpret the entries and determine their length. When ReadEntry fails, nomore entries remain in the current page; the client should next call SetPage or AdvancePage.Again, this is done to allow the client to do something special at page boundaries. If theInscriptPageDescriptor's page disappears to make way for new entries, ReadEntry will raiseInscriptError[code: entryOutOfBounds].SetPage adjusts its InscriptPageDescriptor to the beginning of the indicated "virtual" page,failing if that page no longer exists or doesn't exist yet. AdvancePage performs SetPage of"current plus one." If AdvancePage fails, it is because no further entries exist in the file. Theclient might then call WaitForEntry, which will return only if a timeout condition (described in theIncreek section) is satisfied, or if one or more new entries have been added to the inscript since theoperation was invoked.ClassInscript.ReadEntry: PROCEDURE [self: ClassInscript.Inscript, descriptor:ClassInscript.InscriptPageDescriptor, Read: ClassInscript.ReadAssignment]RETURNS [success: BOOLEAN];ClassInscript.SetPage: PROCEDURE [self: ClassInscript.Inscript, descriptor:ClassInscript.InscriptPageDescriptor,pageNumber: ClassInscript.InscriptPageNumber] RETURNS [success: BOOLEAN];ClassInscript.AdvancePage: PROCEDURE [self: ClassInscript.Inscript, descriptor:ClassInscript.InscriptPageDescriptor]RETURNS [success: BOOLEAN];ClassInscript.WaitForEntry: PROCEDURE [self: ClassInscript.Inscript, waitMode:ClassInscript.WaitMode, waitInterval: Intime.MsTicks _ 1000,waitStartTime: LONG POINTER TO Intime.EventTime _ NIL]RETURNS [moreEntries: BOOLEAN];ClassInscript.InscriptError: ERROR [code: ClassInscript.InscriptErrorCode];In the existing package, InscriptImplAlto and InscriptImplPilot provide two functionallyequivalent implementations of ClassInscript. InterminalImpl acts as the recording client, pollingthe terminal hardware once a tick and recording any changes it sees. This implementation records itsActions into a disk file that behaves as a large ring buffer, filling seldom more than one Alto-sizedpage per minute under normal conditions. Thus Actions are remembered for quite some time, butnot indefinitely (the standard Increek uses a 128 page file.) Both implementations run the recordingclient at high priority. They both lock inscript pages into memory to prevent page fault delays. Theyseem to perform efficiently, without losing events. They are monitored so that clients from more thanone process can access their inscripts.The interfaces were designed so that other implementations of Inscripts could be provided, forinstance to immutably record user Actions, possibly condensed (retaining fewer actions), to supply anapplication with Actions from a source other than the attached terminal, etc. These possibilities arediscussed in more detail in the design document cited as a reference. fq)G? b&D `;]u rX uru ruF@\1 r@ZurX urWu r uru r ur @VDur Sq-rq r Qq/rq P"#rqr q NB Mrq rq Kr%q HYrq rq" F<r q rq EQr q( Cr q: BIvq:% @>u rX uru r@=/u ru r@;ur ur9u ruru r@8u r@6 u rur ur4u r uru r@2u r@1yur ur/hu r uru r@-u rur@,_uFrXur@*urur(u ruru r $q+,rqrq #Gr qr q  !!D ?rq6( rq( 7rq/ N /$B ' >rq ("rq rq5  E r @X2 HELVETICA  TIMESROMAN TIMESROMAN LOGO TIMESROMAN TIMESROMAN  HELVETICA  HELVETICA  HELVETICA   TIMESROMAN   HELVETICA   HELVETICA  HELVETICA  TIMESROMAN TIMESROMAN  TIMESROMAN  TIMESROMAN  HELVETICA  HELVETICA  TIMESROMAN  HELVETICA HELVETICA  HELVETICA TIMESROMAN R Hx'/ 8s@{H GQ [bikqsC{} [ s b9Z%-i%)ii@EmW Z  %+9+9)   Z  VUZ :Z"*i?B"P:Z"i6P* Z :#='J#%$P:C%P=##:C:#=#B"Py:C:C"i rZ ( B "=i#B " 9eB: ;Z":#"iZ !WiW:Z": +iQB":B": +iGB":j/7inscriptimplementation.press Swinehart15-Jan-81 15:39:33 PST