IncreekImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
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
Last Edited by Paul Rovner, June 9, 1983 5:45 pm
Doug Wyatt, March 4, 1985 12:51:40 pm PST
<<<Monitor lock presently used only in Creek Create/Delete code>>
DIRECTORY
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 [
DefaultMouseGrain, StartActionRecorder, KeyName,
--<<--MousePosition-->>--, SetMouseGrain],
InterminalBackdoor USING [InsertAction],
List USING [DRemove, Map];
IncreekImpl: MONITOR
IMPORTS ClassInscript, Interminal, InterminalBackdoor, Intime, List
EXPORTS ClassIncreek
SHARES ClassIncreek =
BEGIN
OPEN ClassIncreek,
S: ClassInscript,
K: Interminal,
T: Intime;
river:
LIST
OF
REF
ANY ←
NIL;
-- protected by MONITOR
Flow:
ENTRY
PROC[x:
REF] = { river ←
CONS[x, river] };
Drain:
ENTRY
PROC[x:
REF] = { river ← List.DRemove[x, river] };
here temporarily. Delete in Cedar 3.5
InsertAction:
PUBLIC
PROC[self: Increek, action: ActionBody] = {
InterminalBackdoor.InsertAction[action];
};
NewStdIncreek:
PUBLIC
PROC [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[self];
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
PROC [self: InscriptPosition]
RETURNS [nilIncreek: Increek] = {
SetMouseGrain[self]; -- revert to default
Drain[self];
RETURN[NIL[Increek]];
};
CopyIncreek:
PUBLIC
PROC [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
PROC [
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]
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; GO TO RecordDown };
keyDown => { kN ← thisA.value; GO TO RecordDown };
keyUp => { kN ← thisA.value; GO TO RecordUp };
allUp => { self.keyState.bits ← self.chordState.bits ← ALL[up]; self.downCount ← 0 };
deltaEventTime, eventTime => NULL;
ENDCASE => ERROR Internal[4];
EXITS
-- manual cross-jumping.
RecordDown => {
IF self.downCount = 0 THEN self.chordState.bits ← ALL[up];
self.downCount ← SUCC[self.downCount];
self.keyState.bits[kN] ← self.chordState.bits[kN] ← down;
};
RecordUp => {
self.keyState.bits[kN] ← up;
self.downCount ← PRED[self.downCount];
};
};
IF acceptance = all THEN EXIT;
see lifetime warning at head of file --
WITH thisE: actionBody
SELECT
FROM
mousePosition, penPosition, deltaMouse => IF acceptance = clicksAndMotion THEN EXIT;
keyUp, keyDown => IF acceptance <= clicksAndMotion THEN EXIT;
ENDCASE;
ENDLOOP;
-- until Action accepted
self.eventTime←self.eT;
RETURN[actionBody];
};
SetAtEarliest:
PUBLIC
PROC [self: InscriptPosition] =
{
ENABLE IncreekError=>IF code=outOfBounds THEN RETRY; -- with new "earliest"
[] ← FullStateFrom[increek: self, pN: S.GetPageLimits[self.inscript].earliestPageNo];
};
SetAtLatest:
PUBLIC
PROC [self: InscriptPosition] =
{
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;
};
SetAtTime:
PUBLIC
PROC [self: InscriptPosition, eventTime:
T.EventTime]
RETURNS [pR: PosResult] =
{
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];
};
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𡤌.mouseGrainTime;
IF c.mouseGrainDots<d THEN d𡤌.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
PROC [self: InscriptPosition]
RETURNS [eT:
T.EventTime] = {
RETURN[self.eventTime];
};
GetCurrentTime:
PUBLIC
PROC [self: InscriptPosition]
RETURNS [eT:
T.EventTime] = {
RETURN[T.ReadEventTime[]];
};
IncreekError: PUBLIC ERROR[code: IncreekErrorCode] = CODE;
SetPageAtTime:
PROC [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:
PROC[a:
LONG
POINTER
TO ActionBody]
RETURNS[
CARDINAL] =
TRUSTED {
SELECT a.kind
FROM
deltaEventTime => RETURN[SIZE[deltaEventTime ActionBody]];
keyDown => RETURN[SIZE[keyDown ActionBody]];
keyStillDown => RETURN[SIZE[keyStillDown ActionBody]];
keyUp => RETURN[SIZE[keyUp ActionBody]];
eventTime => RETURN[SIZE[eventTime ActionBody]];
deltaMouse => RETURN[SIZE[deltaMouse ActionBody]];
mousePosition => RETURN[SIZE[mousePosition ActionBody]];
penPosition => RETURN[SIZE[penPosition ActionBody]];
timedOut => RETURN[SIZE[timedOut ActionBody]];
allUp => RETURN[SIZE[allUp ActionBody]];
ENDCASE => ERROR Internal[1];
};
timedOutActionBody: ActionBody←[contents: timedOut[]];
FullStateFrom:
PROC [increek: InscriptPosition, pN:
S.InscriptPageNumber]
RETURNS [t:
T.EventTime] = {
a: ActionBody;
increek.downCount ← 0;
increek.chordState ← increek.keyState ← [bits[ALL[up]]];
IF ~
S.SetPage[self: increek.inscript, descriptor: increek.inscriptPage, pageNumber: pN]
THEN
ERROR IncreekError[outOfBounds];
DO
a ← GetAction[self: increek, waitMode: dontWait, acceptance: all];
SELECT a.kind
FROM
mousePosition, penPosition => EXIT;
ENDCASE;
WITH a SELECT FROM
keyUp, keyDown, keyStillDown, eventTime, deltaMouse => NULL;
mousePosition, penPosition => EXIT;
ENDCASE => ERROR Internal[3] -- timed out or unrecognized --;
ENDLOOP;
RETURN[increek.eventTime];
};
Procedures for use in initializing Inscript (where belongs this? It's going to be hard to get right)
KeyProc:
S.KEyProc = {
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];
};
ComProc:
S.COmProc = {
RETURN[
T.IsLaterTime[
LOOPHOLE[a],
LOOPHOLE[b]]]; };
Internal:
ERROR[code:
INTEGER] =
CODE;
<<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.
InitializeIncreek:
PROC = {
NSI:
PROC[init:
BOOL]
RETURNS [
S.Inscript] = {
RETURN[
S.NewStdInscript[
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}];
K.StartActionRecorder[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>>