TIPMatcher.mesa;
Last Edited by McGregor, June 15, 1982 9:36 am
Last Edited by: Maxwell, January 3, 1983 12:06 pm
DIRECTORY
ClassIncreek USING [Acceptance, ActionBody, ActionKind, CopyIncreek, GetAction,
GetPositionFrom, GetTime, Increek, NewStdIncreek, SetMouseGrain, ViewPosition],
RefTab USING [Fetch],
Inline USING [BITAND],
Interminal USING [KeyName, KeyState, MousePosition, updown],
Process USING [Abort],
TIPPrivate USING [predTable],
TIPTables USING [IgnoreBits, IgnoreEverything, TIPScreenCoords, TIPScreenCoordsRec,
TIPChoiceSeries, TIPChoice, TIPKeyState],
TIPUser USING [stdChar, stdCoords, stdTime, TIPClient, TIPNotifyProc, TIPParseInfo,
TIPPredicate, TIPTable];
TIPMatcher: PROGRAM
IMPORTS ClassIncreek, Inline, Process, RefTab, TIPPrivate, TIPUser
EXPORTS TIPUser
SHARES TIPUser =
BEGIN OPEN TIPTables, TIPUser, TIPPrivate;
mouseGrainCreek: ClassIncreek.Increek ← ClassIncreek.NewStdIncreek[]; -- only for setting recording grain
TransparentTIPTable: PUBLIC SAFE PROC RETURNS [table: TIPTable] =
CHECKED {RETURN[transparentTIPTable]};
transparentTIPTable: TIPTable = LOOPHOLE[$transparentTIPTable];
ResetTIPContext: PUBLIC SAFE PROC [user: TIPClient, table: TIPTable, notify: TIPNotifyProc,
interrupt: BOOLEANFALSE] = TRUSTED BEGIN
user.parseInfo.tableHead ← table;
user.notifyProc ← notify;
ClassIncreek.SetMouseGrain[mouseGrainCreek, IF table=NIL THEN 50 ELSE table.mouseTicks, 1];
IF interrupt THEN Process.Abort[user.matcher];
END;
MatchProcess: PUBLIC SAFE PROCEDURE [user: TIPClient] = TRUSTED BEGIN
creekAction: ClassIncreek.ActionBody;
results: LIST OF REF ANY;
keep two copies of screen coords so that client can overwrite passed copy
privateTSC: TIPScreenCoords ← NEW[TIPScreenCoordsRec ← [0, FALSE, 0]];
userTSC: TIPScreenCoords ← NEW[TIPScreenCoordsRec];
UNTIL user.parseInfo.inCreek=NIL DO-- until TIP client instance is flushed...
ENABLE ABORTED => LOOP;
Aborted is expected to be caught in these two situations:
1) when DestroyClient is called to flush the tip process (inCreek=NIL)
2) InterruptTIP is called to change the table and flush pending state
someday should also catch ClassIncreek.IncreekError => flush type-ahead
in case ring buffer wraps
save away our state for the buttonProc below
mouse moves also depend on this!
ClassIncreek.CopyIncreek[user.parseInfo.localCreek, user.parseInfo.inCreek];
get a top-level action trigger
creekAction ← ClassIncreek.GetAction[self: user.parseInfo.inCreek,
waitMode: forever, acceptance: clicksAndMotion];
The following special test and dispatch is for Cedar window management.
The parseInfo.localCreek contains the Inscript state at the start of the
mouse event and is passed to the buttonProc. The buttonProc is required to
adjust the passed Increek to reflect the event that gets parsed. If no
event was successfully parsed, the buttonProc should return FALSE.
IF user.buttonProc#NIL THEN WITH action: creekAction SELECT FROM
mousePosition, deltaMouse => BEGIN
userTSC^ ← privateTSC^ ←
ClassIncreek.GetPositionFrom[user.parseInfo.inCreek].mousePosition;
IF user.buttonProc[userTSC, motion, user.parseInfo.localCreek] THEN
BEGIN
[] ← ClassIncreek.CopyIncreek[user.parseInfo.inCreek,
user.parseInfo.localCreek];
LOOP;
END;
END;
keyUp => SELECT action.value FROM Red, Yellow, Blue => BEGIN
userTSC^ ← privateTSC^;
IF user.buttonProc[userTSC, buttonUp, user.parseInfo.localCreek] THEN
BEGIN
[] ← ClassIncreek.CopyIncreek[user.parseInfo.inCreek,
user.parseInfo.localCreek];
LOOP;
END;
END; ENDCASE;
keyDown => SELECT action.value FROM Red, Yellow, Blue => BEGIN
userTSC^ ← privateTSC^;
IF user.buttonProc[userTSC, buttonDown, user.parseInfo.localCreek] THEN
BEGIN
[] ← ClassIncreek.CopyIncreek[user.parseInfo.inCreek,
user.parseInfo.localCreek];
LOOP;
END;
END; ENDCASE;
ENDCASE;
IF user.parseInfo.tableHead#NIL THEN WITH action: creekAction SELECT FROM
mousePosition, deltaMouse,
keyUp,
keyDown  => results ← MatchEvent[user.parseInfo, creekAction];
ENDCASE => ERROR; -- unexpected ActionKind
IF results#NIL THEN user.notifyProc[results];
ENDLOOP;
END;
ParseOneEvent: PUBLIC SAFE PROC [parseInfo: TIPParseInfo] RETURNS [result: LIST OF REF ANY] =
TRUSTED {RETURN[MatchEvent[parseInfo, ClassIncreek.GetAction[self: parseInfo.inCreek,
waitMode: forever, acceptance: clicksAndMotion]]]};
the top level parser goes through the list of TIPTables, picks up the relevant ones
(the ones not to be ignored), and parses the appropriate entry:
MatchEvent: PROCEDURE [parseInfo: TIPParseInfo, creekAction: ClassIncreek.ActionBody]
RETURNS [result: LIST OF REF ANYNIL] = BEGIN OPEN ClassIncreek;
increek: Increek;
increekAction: ActionBody;
firstAccess,
copied: BOOLEAN;
advanced: BOOLEAN;
tells whether the increek on top of the stack has been changed at all
used to eliminate unnecessary copying of increeks
stackPointer: CARDINAL;
ClearIncreekStack: PROC[] = INLINE BEGIN
stackPointer ← 0;
END;
PushIncreek: PROC[] = INLINE BEGIN
CopyIncreek[parseInfo.creekStack[stackPointer], increek];
stackPointer ← stackPointer + 1;
advanced ← FALSE;
END;
CopyTopIncreek: PROC = INLINE BEGIN
IF advanced THEN BEGIN
CopyIncreek[increek, parseInfo.creekStack[stackPointer]];
advanced ← FALSE;
END;
END;
PopIncreek: PROC = INLINE BEGIN
stackPointer ← stackPointer - 1;
END;
unless a second access to the increek actions, MatchEvent deals with parseInfo.inCreek
later, parseInfo.localCreek is the increek which to be considered
PushIncreek and CopyTopIncreek use parseInfo.localCreek as implicit parameter,
assuming that increek, the pointer to the 'interesting' increek, equals localCreek
GetIncreekAction: PROC [acceptance: ClassIncreek.Acceptance ← clicks] = INLINE BEGIN
IF firstAccess THEN BEGIN
firstAccess ← FALSE;
increek ← parseInfo.inCreek;
increekAction ← creekAction; -- the parameter of MatchEvent
copied ← FALSE;
END
ELSE BEGIN
IF ~copied THEN BEGIN
copied ← TRUE;
CopyIncreek[parseInfo.localCreek, parseInfo.inCreek];
increek ← parseInfo.localCreek;
END;
increekAction ← GetAction[self: increek,
waitMode: forever,
acceptance: acceptance];
advanced ← TRUE;
END;
END;
MatchChoice: PROCEDURE [choice: TIPChoice]
RETURNS [result: LIST OF REF ANYNIL] = BEGIN
valid: BOOLEANTRUE; -- go ahead, you're about to match an event
FOR terms: TIPChoice ← choice, terms.rest UNTIL ~valid OR terms=NIL DO
WITH term: terms.first SELECT FROM
keyTrigger => BEGIN
GetIncreekAction[clicks];
valid ← WITH ca: increekAction SELECT FROM
keyDown => (term.keyState.key=ca.value AND
term.keyState.state=down),
keyUp => (term.keyState.key=ca.value AND
term.keyState.state=up),
ENDCASE => FALSE; -- suprise action from Increek
END;
mouseTrigger => BEGIN
GetIncreekAction[clicksAndMotion];
valid ← WITH increekAction SELECT FROM
mousePosition => TRUE,
deltaMouse => TRUE,
ENDCASE  => FALSE;
END;
timeTrigger => BEGIN
IF firstAccess THEN ERROR; -- time events can't be first
copy local creek for checking time since no "putback"
CopyIncreek[parseInfo.timeCreek, increek];
increekAction ← GetAction[self: parseInfo.timeCreek,
waitMode: timed, waitInterval: term.mSecs,
acceptance: clicks];
valid ← WITH ca: increekAction SELECT FROM
timedOut => term.flavor=gt,
ENDCASE  => term.flavor=lt;
END;
keyEnable => BEGIN
creekKeyState: Interminal.KeyState ← GetPositionFrom[increek].keyState;
valid ← (term.keyState.state = creekKeyState.bits[term.keyState.key]);
END;
key2Enable => BEGIN
creekKeyState: Interminal.KeyState ← GetPositionFrom[increek].keyState;
valid ← (term.keyState1.state = creekKeyState.bits[term.keyState1.key])
OR (term.keyState2.state = creekKeyState.bits[term.keyState2.key]);
END;
keyEnableList => BEGIN
creekKeyState: Interminal.KeyState ← GetPositionFrom[increek].keyState;
valid ← FALSE;
FOR lst: LIST OF TIPKeyState ← term.lst, lst.rest UNTIL lst=NIL DO
IF lst.first.state = creekKeyState.bits[lst.first.key] THEN {
valid ← TRUE; EXIT };
ENDLOOP;
END;
predEnable => BEGIN
predRef: REF;
found: BOOLEAN;
predicate: REF TIPUser.TIPPredicate;
[found, predRef] ← RefTab.Fetch[predTable, term.predicate];
IF found THEN {
predicate ← NARROW[predRef];
valid ← predicate^[] }
ELSE { valid ← FALSE };
END;
char => stdChar^ ← AsciiAction[increek, increekAction];
term.ch^ ← AsciiAction[increek, increekAction]; - for the general case -
coords => BEGIN
stdCoords^ ← GetPositionFrom[increek].mousePosition;
term.xy^ ← [mp.mouseX, mp.mouseY]; - for the general case -
END;
time => BEGIN
stdTime^ ← GetTime[increek];
term.time^ ← Intime.ReadEventTime[]; - for the general case -
END;
nested => BEGIN
PushIncreek[];
FOR choices: TIPChoiceSeries ← term.statement, choices.rest
UNTIL choices=NIL DO
result ← MatchChoice[choices.first];
IF result#NIL THEN RETURN[result];
CopyTopIncreek[];
ENDLOOP;
PopIncreek[];
valid ← FALSE;
END;
result => BEGIN
IF copied THEN CopyIncreek[parseInfo.inCreek, increek];
RETURN[term.list];
END;
ENDCASE => ERROR;
ENDLOOP;
END; -- MatchChoice
actionKind: ActionKind ← creekAction.kind;
IF parseInfo.tableHead=transparentTIPTable THEN BEGIN-- just pass creek and action through
result ← LIST[parseInfo.inCreek, NEW[ClassIncreek.ActionBody ← creekAction]];
RETURN;
END;
FOR table: TIPTable ← parseInfo.tableHead, IF table.opaque THEN NIL ELSE table.link
UNTIL table=NIL DO
SELECT actionKind FROM-- for efficiency
mousePosition => IF table.ignore.move THEN LOOP;
deltaMouse  => IF table.ignore.move THEN LOOP;
keyDown  => IF table.ignore.down THEN LOOP;
keyUp  => IF table.ignore.up THEN LOOP;
ENDCASE;
firstAccess ← TRUE;
ClearIncreekStack[];
WITH t: table SELECT FROM
fast => BEGIN
GetIncreekAction[];
WITH action: increekAction SELECT FROM
mousePosition => result ← MatchChoice[t.mouse];
deltaMouse => result ← MatchChoice[t.mouse];
keyUp => result ← MatchChoice[t.keyUp[action.value]];
keyDown => result ← MatchChoice[t.keyDown[action.value]];
ENDCASE;
IF result#NIL THEN RETURN[result];
END;
small => BEGIN
FOR choices: TIPChoiceSeries ← t.all, choices.rest
UNTIL choices=NIL DO
result ← MatchChoice[choices.first];
IF result#NIL THEN RETURN[result];
firstAccess ← TRUE;
ClearIncreekStack[];
ENDLOOP;
END;
ENDCASE;
ENDLOOP;
END;
AsciiAction: PROCEDURE [inCreek: ClassIncreek.Increek, creekAction: ClassIncreek.ActionBody]
RETURNS [c: CHARACTER] = BEGIN
p: ClassIncreek.ViewPosition = ClassIncreek.GetPositionFrom[inCreek];
WITH action: creekAction SELECT FROM
keyDown => BEGIN OPEN keyNames: p.keyState.keyNames;
kI: KeyItem = KeyTable[action.value];
cC: CARDINAL ← kI.NormalCode;
IF cC = 0 THEN ERROR; -- not a character
SELECT Interminal.updown[down] FROM
keyNames.Ctrl => cC ← Inline.BITAND[cC, 37B];
keyNames.LeftShift, keyNames.RightShift => cC ← kI.ShiftCode;
keyNames.Lock => IF kI.Letter THEN cC ← kI.ShiftCode;
ENDCASE;
IF kI.extendedCharSet THEN cC ← cC+200B;
RETURN[LOOPHOLE[cC, CHARACTER]];
END;
ENDCASE => ERROR; -- why are they asking for a char?
END;
KeyItem: TYPE = RECORD [
Letter: BOOLEAN,
extendedCharSet: BOOLEAN,
ShiftCode: [0..177B],
NormalCode: [0..177B]];
KeyTable: ARRAY Interminal.KeyName OF KeyItem =
[ -- MEMORY[177033B] Index [0..15]
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- UNUSED
KeyItem[FALSE, FALSE, 0, 0], -- KeyItemset1
KeyItem[FALSE, FALSE, 0, 0], -- KeyItemset2
KeyItem[FALSE, FALSE, 0, 0], -- KeyItemset3
KeyItem[FALSE, FALSE, 0, 0], -- KeyItemset4
KeyItem[FALSE, FALSE, 0, 0], -- KeyItemset5
KeyItem[FALSE, FALSE, 0, 0], -- Red
KeyItem[FALSE, FALSE, 0, 0], -- Blue
KeyItem[FALSE, FALSE, 0, 0], -- Yellow
MEMORY[177034B] Index [16..31]
KeyItem[FALSE, FALSE, 45B, 65B], -- %,5
KeyItem[FALSE, FALSE, 44B, 64B], -- $,4
KeyItem[FALSE, FALSE, 176B, 66B], -- ~,6
KeyItem[TRUE, FALSE, 105B, 145B], -- E
KeyItem[FALSE, FALSE, 46B, 67B], -- &,7
KeyItem[TRUE, FALSE, 104B, 144B], -- D
KeyItem[TRUE, FALSE, 125B, 165B], -- U
KeyItem[TRUE, FALSE, 126B, 166B], -- V
KeyItem[FALSE, FALSE, 51B, 60B], -- ),0
KeyItem[TRUE, FALSE, 113B, 153B], -- K
KeyItem[FALSE, FALSE, 30B, 55B], -- `,-
KeyItem[TRUE, FALSE, 120B, 160B], -- P
KeyItem[FALSE, FALSE, 77B, 57B], -- ?,/
KeyItem[FALSE, FALSE, 174B, 134B], -- |,\
KeyItem[FALSE, FALSE, 12B, 12B], -- LF
KeyItem[FALSE, FALSE, 10B, 10B], -- BS
MEMORY[177035B] Index [32..47]
KeyItem[FALSE, FALSE, 43B, 63B], -- #,3
KeyItem[FALSE, FALSE, 100B, 62B], -- @,2
KeyItem[TRUE, FALSE, 127B, 167B], -- W
KeyItem[TRUE, FALSE, 121B, 161B], -- Q
KeyItem[TRUE, FALSE, 123B, 163B], -- S
KeyItem[TRUE, FALSE, 101B, 141B], -- A
KeyItem[FALSE, FALSE, 50B, 71B], -- (,9
KeyItem[TRUE, FALSE, 111B, 151B], -- I
KeyItem[TRUE, FALSE, 130B, 170B], -- X
KeyItem[TRUE, FALSE, 117B, 157B], -- O
KeyItem[TRUE, FALSE, 114B, 154B], -- L
KeyItem[FALSE, FALSE, 74B, 54B], -- <,,
KeyItem[FALSE, FALSE, 42B, 47B], -- ",'
KeyItem[FALSE, FALSE, 175B, 135B], -- },]
KeyItem[FALSE, TRUE, 5B, 2B], -- SPARE2
KeyItem[FALSE, TRUE, 4B, 1B], -- SPARE1
MEMORY[177036B] Index [48..63]
KeyItem[FALSE, FALSE, 41B, 61B], -- !,1
KeyItem[FALSE, FALSE, 33B, 33B], -- ESCAPE
KeyItem[FALSE, FALSE, 11B, 11B], -- TAB
KeyItem[TRUE, FALSE, 106B, 146B], -- F
KeyItem[FALSE, FALSE, 0B, 0B], -- CONTROL
KeyItem[TRUE, FALSE, 103B, 143B], -- C
KeyItem[TRUE, FALSE, 112B, 152B], -- J
KeyItem[TRUE, FALSE, 102B, 142B], -- B
KeyItem[TRUE, FALSE, 132B, 172B], -- Z
KeyItem[FALSE, FALSE, 0B, 0B], -- SHIFT
KeyItem[FALSE, FALSE, 76B, 56B], -- >,.
KeyItem[FALSE, FALSE, 72B, 73B], -- :,;
KeyItem[FALSE, FALSE, 15B, 15B], -- CR
KeyItem[FALSE, FALSE, 136B, 137B], -- ^,←
KeyItem[FALSE, FALSE, 177B, 177B], -- DEL
KeyItem[FALSE, FALSE, 0B, 0B], -- NOT USED (FL3)
MEMORY[177037B] Index [64..79]
KeyItem[TRUE, FALSE, 122B, 162B], -- R
KeyItem[TRUE, FALSE, 124B, 164B], -- T
KeyItem[TRUE, FALSE, 107B, 147B], -- G
KeyItem[TRUE, FALSE, 131B, 171B], -- Y
KeyItem[TRUE, FALSE, 110B, 150B], -- H
KeyItem[FALSE, FALSE, 52B, 70B], -- *,8
KeyItem[TRUE, FALSE, 116B, 156B], -- N
KeyItem[TRUE, FALSE, 115B, 155B], -- M
KeyItem[FALSE, FALSE, 0B, 0B], -- LOCK
KeyItem[FALSE, FALSE, 40B, 40B], -- SPACE
KeyItem[FALSE, FALSE, 173B, 133B], -- {,[
KeyItem[FALSE, FALSE, 53B, 75B], -- +,=
KeyItem[FALSE, FALSE, 0B, 0B], -- SHIFT
KeyItem[FALSE, TRUE, 6B, 3B], -- SPARE3
KeyItem[FALSE, FALSE, 0B, 0B], -- NOT USED (FL4)
KeyItem[FALSE, FALSE, 0B, 0B], -- -- NOT USED (FR5)
KeyItem[FALSE, FALSE, 0B, 0B]]; -- allUp, synthesized
Start: PUBLIC SAFE PROC = CHECKED {};
ClassIncreek.SetMouseGrain[mouseGrainCreek, 100, 1];
END.