-- IncreekImpl.mesa -- Last Edited by Wyatt, October 27, 1982 9:58 pm -- Last Edited by Stone September 18, 1982 10:31 am -- Last Edited by McGregor, 1-Sep-81 13:06:53 -- Last Edited by Swinehart, April 14, 1982 5:21 pm -- <<<Monitor lock presently used only in Creek Create/Delete code>> DIRECTORY InterminalExtra USING[InsertAction], --temporary hack September 18, 1982 10:32 am ClassInscript USING [ AdvancePage, COmProc, CopyPageDescriptor, GetPageLimits, Inscript, InscriptPageDescriptor, InscriptPageDescBody, InscriptPageNumber, KEyProc, NewStdInscript, InscriptError, ReadEntry, ResetPageDescriptor, SetPage, WaitForEntry, WaitMode], ClassIncreek, Intime USING [ AddDeltaTimeToEventTime, EventTime, EventTimeDifference, IsLaterTime, MsTicks, ReadEventTime], Interminal USING [ allUp, DefaultMouseGrain, InterminalImpl, KeyName, --<<--MousePosition-->>--, SetMouseGrain], List USING [DRemove, Map], Rope USING [ROPE], TerminalMultiplex USING [ CurrentTerminal ] ; IncreekImpl: MONITOR IMPORTS ClassInscript, Interminal, Intime, List, TerminalMultiplex, InterminalExtra EXPORTS ClassIncreek SHARES ClassIncreek = BEGIN OPEN ClassIncreek, S: ClassInscript, K: Interminal, T: Intime; -- here temporarily. Delete in Cedar 3.5 InsertAction: PUBLIC PROCEDURE[self: Increek, action: ActionBody] = { InterminalExtra.InsertAction[action]; }; NewStdIncreek: PUBLIC PROCEDURE [template: Increek ← NIL] RETURNS [Increek] = { self: InscriptPosition ← NEW[InscriptPositionBody← [inscript: stdInscript, inscriptPage: NIL, ip1: CreatePageDescriptor[stdInscript], iP: NIL, ip2: CreatePageDescriptor[stdInscript], mousePosition: [0,FALSE,0]]]; self.inscriptPage←LOOPHOLE[self.ip1]; self.iP←LOOPHOLE[self.ip2]; {Flow: ENTRY PROC=INLINE { river←CONS[self, river] }; Flow[]; }; IF template = NIL THEN { SetAtLatest[self]; -- sets up rest of state, too SetMouseGrain[self]; } ELSE CopyIncreek[self: self, template: template]; RETURN[self]; }; CreatePageDescriptor: PROC[inscript: S.Inscript] RETURNS [d: REF S.InscriptPageDescBody] = { d←NEW[S.InscriptPageDescBody]; S.ResetPageDescriptor[inscript, LOOPHOLE[d]];}; Release: PUBLIC PROCEDURE [self: InscriptPosition] RETURNS [nilIncreek: Increek] = { SetMouseGrain[self]; -- revert to default { Drain: ENTRY PROC=INLINE { river ← List.DRemove[self, river] }; Drain[]; }; RETURN[NIL[Increek]]; }; CopyIncreek: PUBLIC PROCEDURE [self: InscriptPosition, template: InscriptPosition] = { pd1: REF S.InscriptPageDescBody ← self.ip1; pd2: REF S.InscriptPageDescBody ← self.ip2; self↑ ← template↑; self.ip1←pd1; self.inscriptPage←LOOPHOLE[pd1]; self.ip2←pd2; self.iP←LOOPHOLE[pd2]; S.CopyPageDescriptor[self.inscript, self.inscriptPage, template.inscriptPage]; }; GetAction: PUBLIC PROCEDURE [ self: InscriptPosition, waitMode: WaitMode ← forever, waitInterval: T.MsTicks ← 100, acceptance: Acceptance ← clicks] RETURNS [a: ActionBody] = -- Acceptance determines which actions will produce valid return values. -- Warning: after invalid return, creek may have been advanced. Client -- must save copy to revert. -- Can raise IncreekError[outOfBounds] BEGIN ENABLE S.InscriptError => IF code=entryOutOfBounds THEN ERROR IncreekError[outOfBounds]; inscript: S.Inscript = self.inscript; descriptor: S.InscriptPageDescriptor = self.inscriptPage; dCopy: S.InscriptPageDescriptor = self.iP; actionBody: ActionBody; S.CopyPageDescriptor[self: inscript, dest: dCopy, source: descriptor]; DO -- until Action accepted kN: K.KeyName; -- if no actions left, return NIL unless wait WHILE ~S.ReadEntry[self: inscript, descriptor: descriptor, destination: @actionBody, LengthProc: ActionLength] DO IF (~S.AdvancePage[inscript, descriptor]) AND (~S.WaitForEntry[inscript, waitMode, waitInterval, descriptor, @self.eventTime]) THEN RETURN[timedOutActionBody]; ENDLOOP; self.eT ← T.AddDeltaTimeToEventTime[self.eT, actionBody.deltaDeltaTime]; WITH thisA: actionBody SELECT FROM deltaEventTime => self.eT ← T.AddDeltaTimeToEventTime[self.eT, thisA.value]; eventTime => self.eT ← thisA.eventTime; ENDCASE; IF waitMode=timed AND T.EventTimeDifference[@self.eT, @self.eventTime] > waitInterval THEN { S.CopyPageDescriptor[self: inscript, dest: descriptor, source: dCopy]; RETURN[timedOutActionBody]}; { WITH thisA: actionBody SELECT FROM mousePosition => self.mousePosition ← thisA.mousePosition; deltaMouse => { self.mousePosition.mouseX ← self.mousePosition.mouseX + thisA.value.deltaX; self.mousePosition.mouseY ← self.mousePosition.mouseY + thisA.value.deltaY }; keyStillDown => {kN ← thisA.value; IF kN=K.KeyName[allUp] THEN { self.keyState←self.chordState←K.allUp; self.downCount←0; } ELSE GO TO RecordDown; }; keyDown => {kN ← thisA.value; GO TO RecordDown}; keyUp => { self.keyState.bits[thisA.value] ← up; self.downCount ← PRED[self.downCount]; }; deltaEventTime, eventTime => NULL; ENDCASE => ERROR Internal[4]; EXITS -- manual cross-jumping. RecordDown=> { IF self.downCount = 0 THEN self.chordState ← K.allUp; self.downCount ← SUCC[self.downCount]; self.keyState.bits[kN] ← self.chordState.bits[kN] ← down; }; }; IF acceptance = all THEN EXIT; -- see lifetime warning at head of file -- WITH thisE: actionBody SELECT FROM mousePosition, deltaMouse => IF acceptance = clicksAndMotion THEN EXIT; keyUp, keyDown => IF acceptance <= clicksAndMotion THEN EXIT; ENDCASE; ENDLOOP; -- until Action accepted self.eventTime←self.eT; RETURN[actionBody]; END; SetAtEarliest: PUBLIC PROCEDURE [self: InscriptPosition] = BEGIN ENABLE IncreekError=>IF code=outOfBounds THEN RETRY; -- with new "earliest" [] ← FullStateFrom[increek: self, pN: S.GetPageLimits[self.inscript].earliestPageNo]; END; SetAtLatest: PUBLIC PROCEDURE [self: InscriptPosition] = BEGIN ENABLE IncreekError=>IF code=outOfBounds THEN RETRY; -- ?? [] ← FullStateFrom[increek: self, pN: S.GetPageLimits[self.inscript].latestPageNo]; WHILE GetAction[self: self, waitMode: dontWait, acceptance: all].kind # timedOut DO ENDLOOP; END; SetAtTime: PUBLIC PROCEDURE [self: InscriptPosition, eventTime: T.EventTime] RETURNS [pR: PosResult] = BEGIN ENABLE IncreekError=>IF code=outOfBounds THEN RETRY; -- with new limits a: ActionBody; res: PosResult ← SetPageAtTime[increek: self, t: eventTime]; -- sets p just beyond basic actions of that page -- -- next has to be another increek!! nextCreek: InscriptPosition; IF res # onTime THEN RETURN[res]; nextCreek ← NewStdIncreek[template: self]; DO ENABLE IncreekError=>IF code=outOfBounds THEN nextCreek←Release[nextCreek]; -- painfully slow loop!!!! CopyIncreek[self: self, template: nextCreek]; a ← GetAction[self: nextCreek, waitMode: dontWait, acceptance: all]; IF a.kind=timedOut THEN GOTO TooLate; -- e.g., no actions waiting -- IF T.IsLaterTime[nextCreek.eventTime, eventTime] THEN GOTO OnTime; REPEAT TooLate => res ← tooLate; OnTime => res ← onTime; ENDLOOP; nextCreek ← Release[self: nextCreek]; RETURN[res]; END; SetMouseGrain: PUBLIC ENTRY PROC [self: InscriptPosition, ticks: T.MsTicks←0, dots: INTEGER←0] = { t: T.MsTicks; d: INTEGER; GetFinestGrain: SAFE PROC[x: REF ANY, tail: LIST OF REF ANY] = TRUSTED { c: InscriptPosition=NARROW[x]; IF c.mouseGrainTime<t THEN t←c.mouseGrainTime; IF c.mouseGrainDots<d THEN d←c.mouseGrainDots; }; [t,d]←K.DefaultMouseGrain[]; self.mouseGrainTime←IF ticks=0 THEN t ELSE ticks; self.mouseGrainDots←IF dots=0 THEN d ELSE dots; List.Map[river, GetFinestGrain]; K.SetMouseGrain[t,d]; }; GetTime: PUBLIC PROCEDURE [self: InscriptPosition] RETURNS [eT: T.EventTime] = { RETURN[self.eventTime];}; GetCurrentTime: PUBLIC PROCEDURE [self: InscriptPosition] RETURNS [eT: T.EventTime] = { RETURN[T.ReadEventTime[]]; }; IncreekError: PUBLIC ERROR[code: IncreekErrorCode] = CODE; SetPageAtTime: PROCEDURE [increek: InscriptPosition, t: T.EventTime] RETURNS [res: PosResult] = { earlyPageNum, latePageNum, nextPageNum: S.InscriptPageNumber; [earlyPageNum, latePageNum] ← S.GetPageLimits[increek.inscript]; latePageNum ← latePageNum + 1; -- "off the end" nextPageNum ← earlyPageNum; res ← tooEarly; DO IF T.IsLaterTime[FullStateFrom[increek, nextPageNum], t] THEN latePageNum ← nextPageNum ELSE {earlyPageNum ← nextPageNum; res ← onTime; }; nextPageNum ← (earlyPageNum + latePageNum)/2; IF nextPageNum = earlyPageNum THEN EXIT; ENDLOOP; [] ← FullStateFrom[increek, earlyPageNum]; -- assert p is positioned just after full state recording in result page -- RETURN --[res]--}; ActionLength: PROCEDURE [a: Action] RETURNS [wordsToAdvance: CARDINAL] = BEGIN wordsToAdvance ← WITH thisA: a SELECT FROM mousePosition, penPosition => SIZE[mousePosition ActionBody], deltaMouse => SIZE [deltaMouse ActionBody], deltaEventTime, keyUp, keyDown, keyStillDown => SIZE[keyDown ActionBody], eventTime => SIZE[eventTime ActionBody], ENDCASE => ERROR Internal[1]; END; timedOutActionBody: ActionBody←[contents: timedOut[]]; FullStateFrom: PROCEDURE [increek: InscriptPosition, pN: S.InscriptPageNumber] RETURNS [t: T.EventTime] = BEGIN a: ActionBody; increek.downCount ← 0; increek.chordState ← increek.keyState ← K.allUp; IF ~S.SetPage[self: increek.inscript, descriptor: increek.inscriptPage, pageNumber: pN] THEN ERROR IncreekError[outOfBounds]; DO a ← GetAction[self: increek, waitMode: dontWait, acceptance: all]; WITH a SELECT FROM keyUp, keyDown, keyStillDown, eventTime, deltaMouse => NULL; mousePosition => EXIT; ENDCASE => ERROR Internal[3] -- timed out or unrecognized --; ENDLOOP; RETURN[increek.eventTime]; END; -- Procedures for use in initializing Inscript (where belongs this? It's going to be hard to get right. KeyProc: S.KEyProc = BEGIN a: ActionBody; IF S.ReadEntry[self: inscript, descriptor: descriptor, destination: @a, LengthProc: ActionLength] THEN WITH thisA: a SELECT FROM eventTime => RETURN[LOOPHOLE[thisA.eventTime]]; ENDCASE; ERROR S.InscriptError[invalidPageKey]; END; ComProc: S.COmProc = {RETURN[T.IsLaterTime[LOOPHOLE[a], LOOPHOLE[b]]]; }; Internal: ERROR[code: INTEGER] = CODE; river: LIST OF REF ANY ← NIL; --<<support of cursor track hack>> -- << currentCursorPosition: POINTER TO MousePosition = LOOPHOLE[426B]; >> -- << xMax: CARDINAL = 608; >> -- << yMax: CARDINAL = 808; >> --<<end of hack>> -- Initialization; Create standard inscript and terminal server, before returning first Increek. stdInscript: S.Inscript; InitializeIncreek: PROC = { fileName: Rope.ROPE=SELECT TerminalMultiplex.CurrentTerminal[].terminal FROM primary=>"Inscript.Inscript", alternate=>"InscriptTest.Inscript", special=>"InscriptSpecial.Inscript", ENDCASE=>ERROR; NSI: PROC[init: BOOLEAN] RETURNS [S.Inscript] = {RETURN[ S.NewStdInscript[ fileName: fileName, KeyProc: KeyProc, ComProc: ComProc, initializeFile: init]];}; -- !!!! reinitialize on any error before giving up -- stdInscript ← NSI[FALSE! S.InscriptError=> IF code=invalidInscriptFile THEN { stdInscript←NSI[TRUE]; CONTINUE}]; START K.InterminalImpl[stdInscript]; }; InitializeIncreek[]; END. -- << (Goes in GetAction just after all Action info is reflected in state >> -- <<Hack; track mouse correctly on replay>> --<< currentCursorPosition.mouseX←MAX[MIN[xMax, d.mousePosition.mouseX], 0];>> --<< currentCursorPosition.mouseY←MAX[MIN[yMax, d.mousePosition.mouseY], 0]; >> -- <<End of hack>>