DIRECTORY Atom, Devices, DeviceTypes, FastTRAPDevice, IO, KeyChars, KeyMapping, KeyStateTypes, KeySyms1, KeySymsKB, KeySymsTrackball, KeyTypes, Process, Real, RefTab, Rope, RuntimeError, ScreenCoordsTypes, TIPFastTables, TIPIdentity, TIPPredicate, TIPPrivate, TIPPrivateTypes, TIPResults, TIPTables, TIPTypes, TIPUser, UserInput, UserInputGetActions, UserInputLookahead, UserInputOps, UserInputPrivate, UserInputTypes, Vector2; TIPMatcherImpl: CEDAR PROGRAM IMPORTS Atom, Devices, FastTRAPDevice, IO, KeyChars, KeyMapping, Process, RefTab, RuntimeError, TIPFastTables, TIPPrivate, UserInputGetActions, UserInputLookahead, UserInputOps EXPORTS TIPIdentity, TIPPredicate, TIPTypes, TIPPrivate, TIPPrivateTypes, TIPResults, TIPUser, UserInput = BEGIN HandleRep: PUBLIC TYPE = UserInputPrivate.Rep; -- UserInput.HandleRep Handle: TYPE = REF HandleRep; DeviceState: TYPE = DeviceTypes.DeviceState; InputActionBody: TYPE = UserInputGetActions.InputActionBody; KeyCode: TYPE = KeyTypes.KeyCode; KeySym: TYPE = KeyTypes.KeySym; KeySyms: TYPE = KeyMapping.KeySyms; KeyboardState: TYPE = KeyStateTypes.KeyboardState; MouseEvent: TYPE = TIPPrivate.MouseEvent; MousePosition: TYPE = ScreenCoordsTypes.TIPScreenCoordsRec; TIPButtonProc: TYPE = TIPPrivate.TIPButtonProc; TIPChoice: TYPE = TIPTables.TIPChoice; TIPChoiceSeries: TYPE = TIPTables.TIPChoiceSeries; TIPClient: TYPE = TIPPrivate.TIPClient; TIPClientRec: TYPE = TIPPrivate.TIPClientRec; TIPFastTable: TYPE = TIPTables.TIPFastTable; TIPKeyState: TYPE = TIPTables.TIPKeyState; TIPNotifyProc: TYPE = TIPPrivate.TIPNotifyProc; TIPParseInfo: TYPE = TIPPrivate.TIPParseInfo; TIPParseInfoRec: TYPE = TIPPrivate.TIPParseInfoRec; TIPScreenCoords: TYPE = TIPUser.TIPScreenCoords; TIPScreenCoordsRec: TYPE = TIPUser.TIPScreenCoordsRec; TIPTable: TYPE = TIPTypes.TIPTable; TIPTableRep: PUBLIC <> TYPE = TIPPrivateTypes.TIPTableRep; TIPTableImpl: TYPE = REF TIPTables.TIPTableImplRep; TIPTableImplRep: PUBLIC <> TYPE = TIPTables.TIPTableImplRep; TIPTerm: TYPE = TIPTables.TIPTerm; TIPTime: TYPE = TIPUser.TIPTime; UpDown: TYPE = KeyStateTypes.UpDown; CONTROL: KeyTypes.KeySym = KeySymsKB.LeftControl; LeftShift: KeyTypes.KeySym = KeySymsKB.LeftShift; RightShift: KeyTypes.KeySym = KeySymsKB.RightShift; ShiftLock: KeyTypes.KeySym = KeySymsKB.ShiftLock; CapsLock: KeyTypes.KeySym = KeySymsKB.CapsLock; transparentTIPTable: TIPTable ~ MakeTransparentTIPTable[]; identityTIPTable: TIPTable ~ MakeIdentityTIPTable[plusTime: FALSE]; identityPlusTimeTIPTable: TIPTable ~ MakeIdentityTIPTable[plusTime: TRUE]; MakeTransparentTIPTable: PROC [] RETURNS [TIPTable] = { impl: TIPTables.TIPTableImpl ~ NEW[TIPTables.TIPTableImplRep ¬ [variants: transparent[]]]; RETURN [NEW[TIPPrivateTypes.TIPTableRep ¬ [impl: impl]]]; }; MakeIdentityTIPTable: PROC [plusTime: BOOL] RETURNS [TIPTable] = { impl: TIPTableImpl ¬ NEW[TIPTableImplRep ¬ [variants: identity[plusTime: plusTime]]]; RETURN[NEW[TIPTableRep ¬ [impl: impl]]]; }; TransparentTIPTable: PUBLIC PROC RETURNS [table: TIPTable] = { RETURN[transparentTIPTable]; }; IdentityTIPTable: PUBLIC PROC [plusTime: BOOL ¬ FALSE] RETURNS [identity: TIPTable] = { RETURN[IF plusTime THEN identityPlusTimeTIPTable ELSE identityTIPTable]; }; DiscardTypeAhead: PUBLIC PROC [user: TIPClient] = { UserInputOps.SetAtLatest[user.parseInfo.inCreek]; }; ResetTIPContext: PUBLIC PROC [user: TIPClient, table: TIPTable, notify: TIPNotifyProc, interrupt: BOOL ¬ FALSE] = TRUSTED { user.parseInfo.tableHead ¬ table; user.notifyProc ¬ notify; IF interrupt THEN Interrupt[user, FALSE]; }; InterruptTIP: PUBLIC PROC [self: TIPClient] = { ERROR; --unimplemented because no external clients were found; ChJ, August 25, 1992 }; Interrupt: PROC [self: TIPClient, destroy: BOOL ¬ FALSE] = { matcher: PROCESS ~ self.matcher; IF matcher#NIL THEN { IF destroy THEN self.matcher ¬ NIL; Process.Abort[matcher ! Process.InvalidProcess => CONTINUE]; }; }; MatchProcess: PROC [user: TIPClient, doneFlag: REF BOOL] = { ENABLE RuntimeError.Uncaught => StartTIPClient[user]; -- the old matcher is dead self: PROCESS ~ Process.GetCurrent[]; privateTSC: TIPScreenCoords ~ NEW[TIPScreenCoordsRec ¬ [0, 0, FALSE]]; userTSC: TIPScreenCoords ~ NEW[TIPScreenCoordsRec]; user.matcher ¬ self; doneFlag­ ¬ TRUE; TRUSTED {Process.SetPriority[Process.priorityForeground]}; -- dequeing is important WHILE user.matcher=self DO ENABLE ABORTED => LOOP; WHILE user.matcher=self DO -- until Interrupt is called MatchLoop[user, privateTSC, userTSC]; ENDLOOP; ENDLOOP; }; MatchLoop: PROC [user: TIPClient, privateTSC: TIPScreenCoords, userTSC: TIPScreenCoords] = { a: InputActionBody; results: LIST OF REF; UserInputLookahead.SaveState[saved: user.parseInfo.localCreek, handle: user.parseInfo.inCreek]; a ¬ UserInputGetActions.GetInputActionBody[handle: user.parseInfo.inCreek, waitMode: forever, acceptance: all]; SELECT a.kind FROM $KeyStillDown, $AllUp, $Enter, $Exit, $TimeOut, $End, $TimeIsPassing, $EventTime => RETURN; ENDCASE; BEGIN IF user.buttonProc#NIL THEN { -- give the buttonProc a chance to parse the event mouseEvent: MouseEvent ¬ buttonUp; SELECT a.kind FROM $IntegerPosition, $FakePosition, $Position => { inCreek: Handle ~ user.parseInfo.inCreek; p: MousePosition ~ inCreek.mousePosition; userTSC­ ¬ privateTSC­ ¬ [mouseX: p.mouseX, mouseY: p.mouseY, color: p.color]; mouseEvent ¬ motion; }; $Key => IF UserInputOps.IsMouseButton[user.parseInfo.inCreek, a.keyCode] THEN { userTSC­ ¬ privateTSC­; mouseEvent ¬ IF a.down THEN buttonDown ELSE buttonUp; } ELSE GOTO ParseNormally; ENDCASE => GOTO ParseNormally; IF user.buttonProc[userTSC, mouseEvent, user.parseInfo.localCreek, user.clientData, a.device, a.user, a.display, a.eventSource] THEN [] ¬ UserInputLookahead.RestoreState[saved: user.parseInfo.localCreek, handle: user.parseInfo.inCreek] ELSE GOTO ParseNormally; } ELSE GOTO ParseNormally; EXITS ParseNormally => { IF user.parseInfo.tableHead#NIL THEN SELECT a.kind FROM $IntegerPosition, $Position, $Key, $Ref => results ¬ WideMatchEvent[user.parseInfo, a]; ENDCASE => ERROR; -- unexpected ActionKind IF results#NIL THEN user.notifyProc[results: results, clientData: user.clientData, device: a.device, user: a.user, display: a.display, eventSource: a.eventSource]; }; END; }; WideParseOneEvent: PUBLIC PROC [parseInfo: TIPParseInfo] RETURNS [result: LIST OF REF ANY] = { RETURN[WideMatchEvent[parseInfo, UserInputGetActions.GetInputActionBody[handle: parseInfo.inCreek, waitMode: forever, acceptance: clicksAndMotion]]]; }; MatchRefKey: PROC [action: InputActionBody, term: keyTrigger TIPTerm] RETURNS [eventMatch: BOOL ¬ FALSE] = { WITH action.ref SELECT FROM change: DeviceTypes.DeviceStateChange => { deviceClassName: ATOM ¬ Devices.NameOfClass[change.class]; SELECT deviceClassName FROM $FastTRAP => { expanded: FastTRAPDevice.ExpandedChange; expanded ¬ FastTRAPDevice.UnpackChange[change]; WITH c: expanded SELECT FROM key => { IF c.down THEN { SELECT c.key FROM left => { -- left button went down eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballLeft AND term.keyState.state=down }; middle => { eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballMiddle AND term.keyState.state=down }; right => { -- set up to scroll scene eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballRight AND term.keyState.state=down }; ENDCASE => ERROR; } ELSE { SELECT c.key FROM left => { -- left button went down eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballLeft AND term.keyState.state=up }; middle => { eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballMiddle AND term.keyState.state=up }; right => { -- set up to scroll scene eventMatch ¬ term.keyState.keySym = KeySymsTrackball.TrackballRight AND term.keyState.state=up }; ENDCASE => ERROR; }; }; trackball => NULL; wheel => NULL; ENDCASE; -- strange trackball event }; ENDCASE; -- not a FastTRAP }; ENDCASE; -- not a change, that's strange. }; MatchRefTrackball: PROC [action: InputActionBody, term: trackballTrigger TIPTerm] RETURNS [eventMatch: BOOL ¬ FALSE, trackballChange: Vector2.VEC ¬ [0,0]] = { WITH action.ref SELECT FROM change: DeviceTypes.DeviceStateChange => { deviceClassName: ATOM ¬ Devices.NameOfClass[change.class]; SELECT deviceClassName FROM $FastTRAP => { expanded: FastTRAPDevice.ExpandedChange; expanded ¬ FastTRAPDevice.UnpackChange[change]; WITH c: expanded SELECT FROM key => NULL; trackball => { trackballChange _ [c.deltaX, c.deltaY]; eventMatch _ TRUE; }; wheel => NULL; ENDCASE; -- strange trackball event }; ENDCASE; -- not a FastTRAP }; ENDCASE; -- not a change, that's strange. }; MatchRefThumbwheel: PROC [action: InputActionBody, term: thumbwheelTrigger TIPTerm] RETURNS [eventMatch: BOOL ¬ FALSE, thumbwheelChange: INTEGER ¬ 0] = { WITH action.ref SELECT FROM change: DeviceTypes.DeviceStateChange => { deviceClassName: ATOM ¬ Devices.NameOfClass[change.class]; SELECT deviceClassName FROM $FastTRAP => { expanded: FastTRAPDevice.ExpandedChange; expanded ¬ FastTRAPDevice.UnpackChange[change]; WITH c: expanded SELECT FROM key => NULL; trackball => NULL; wheel => { thumbwheelChange _ c.delta; eventMatch _ TRUE; }; ENDCASE; -- strange trackball event }; ENDCASE; -- not a FastTRAP }; ENDCASE; -- not a change, that's strange. }; MatchKeyKeyTrigger: PROC [handle: Handle, action: InputActionBody, term: keyTrigger TIPTerm] RETURNS [eventMatch: BOOL ¬ FALSE] = { keySym: KeySym; SELECT action.kind FROM $Key => { keySyms: KeySyms ¬ KeyMapping.GetKeySyms[handle.mapping, action.keyCode]; FOR i: NAT IN [0..keySyms.n) DO keySym ¬ keySyms[i]; IF action.down THEN { IF term.keyState.keySym = keySym AND term.keyState.state=down THEN GOTO Found; } ELSE { IF term.keyState.keySym = keySym AND term.keyState.state=up THEN GOTO Found; }; REPEAT Found => eventMatch ¬ TRUE; ENDLOOP; }; $Ref => eventMatch ¬ MatchRefKey[action, term]; ENDCASE; -- surprise action from UserInput }; MatchTimeTrigger: PROC [parseInfo: TIPParseInfo, handle: Handle, term: timeTrigger TIPTerm] RETURNS [eventMatch: BOOL ¬ FALSE, action: InputActionBody] = { UserInputLookahead.SaveState[saved: parseInfo.timeCreek, handle: handle]; action ¬ UserInputGetActions.GetInputActionBody[handle: parseInfo.timeCreek, waitMode: timed, waitInterval: term.mSecs, acceptance: clicks]; eventMatch ¬ SELECT action.kind FROM $TimeOut => term.flavor=gt, ENDCASE => term.flavor=lt; UserInputOps.Close[parseInfo.timeCreek]; -- so garbage collection can proceed }; WideMatchEvent: PUBLIC PROC [parseInfo: TIPParseInfo, a: InputActionBody] RETURNS [result: LIST OF REF ¬ NIL] = TRUSTED { handle: Handle; action: InputActionBody; firstAccess, copied: BOOL ¬ FALSE; advanced: BOOL ¬ FALSE; stackPointer: CARDINAL ¬ 0; ClearHandleStack: PROC[] = TRUSTED { FOR i: NAT IN [0..stackPointer) DO UserInputOps.Close[parseInfo.creekStack[i]]; ENDLOOP; stackPointer ¬ 0; UserInputOps.Close[parseInfo.localCreek]; }; PushHandle: PROC[] = TRUSTED { UserInputLookahead.SaveState[saved: parseInfo.creekStack[stackPointer], handle: handle]; stackPointer ¬ stackPointer + 1; advanced ¬ FALSE; }; CopyTopHandle: PROC = TRUSTED { IF advanced THEN { UserInputLookahead.RestoreState[parseInfo.creekStack[stackPointer-1], handle]; advanced ¬ FALSE; }; }; PopHandle: PROC = TRUSTED { stackPointer ¬ stackPointer - 1; UserInputOps.Close[parseInfo.creekStack[stackPointer]]; }; GetHandleAction: PROC [acceptance: UserInputTypes.Acceptance ¬ clicks] = TRUSTED { IF firstAccess THEN { firstAccess ¬ FALSE; handle ¬ parseInfo.inCreek; action ¬ a; -- the parameter of MatchEvent copied ¬ FALSE; } ELSE { IF ~copied THEN { copied ¬ TRUE; UserInputLookahead.SaveState[saved: parseInfo.localCreek, handle: parseInfo.inCreek]; handle ¬ parseInfo.localCreek; }; action ¬ UserInputGetActions.GetInputActionBody[handle: handle, waitMode: forever, acceptance: acceptance]; advanced ¬ TRUE; }; }; MatchChoice: PROC [choice: TIPChoice] RETURNS [result: LIST OF REF ¬ NIL] = TRUSTED { eventMatch: BOOL ¬ TRUE; trackballChange: Vector2.VEC _ [0, 0]; thumbwheelChange: INTEGER _ 0; FOR terms: TIPChoice ¬ choice, terms.rest UNTIL ~eventMatch OR terms=NIL DO WITH term: terms.first SELECT FROM keyTrigger => { -- e.g., "A Down =>" or "TrackballLeft Up =>" GetHandleAction[clicks]; eventMatch ¬ MatchKeyKeyTrigger[handle, action, term]; }; trackballTrigger => { -- e.g., "Trackball =>" GetHandleAction[clicksAndMotion]; IF action.kind = $Ref THEN [eventMatch, trackballChange] ¬ MatchRefTrackball[action, term] ELSE eventMatch ¬ FALSE; }; thumbwheelTrigger => { -- e.g., "Thumbwheel =>" GetHandleAction[clicksAndMotion]; IF action.kind = $Ref THEN [eventMatch, thumbwheelChange] ¬ MatchRefThumbwheel[action, term] ELSE eventMatch ¬ FALSE; }; mouseTrigger => { -- e.g., "Mouse =>" GetHandleAction[clicksAndMotion]; eventMatch ¬ SELECT action.kind FROM $Position, $IntegerPosition, $FakePosition => TRUE, ENDCASE => FALSE; }; timeTrigger => { -- e.g., "BEFORE 200" or "AFTER 200" IF firstAccess THEN ERROR; -- time events can't be first [eventMatch, action] ¬ MatchTimeTrigger[parseInfo, handle, term]; }; keyEnable => { FastTRAPKeyNameFromKeySym: PROC [sym: KeySym] RETURNS [name: FastTRAPDevice.KeyName ¬ left, isFT: BOOL ¬ TRUE] = TRUSTED { SELECT sym FROM KeySymsTrackball.TrackballLeft => name ¬ left; KeySymsTrackball.TrackballMiddle => name ¬ middle; KeySymsTrackball.TrackballRight => name ¬ right; ENDCASE => isFT _ FALSE; }; keyState: UpDown; ftName: FastTRAPDevice.KeyName; isFT: BOOL ¬ FALSE; [ftName, isFT] ¬ FastTRAPKeyNameFromKeySym[term.keyState.keySym]; IF isFT THEN { ftState: DeviceState _ NARROW[handle.fastTrapState]; eventMatch ¬ FastTRAPDevice.IsDown[ftState, ftName] = (term.keyState.state = down) } ELSE { keyState ¬ UserInputOps.GetKeySymState[handle, term.keyState.keySym]; eventMatch ¬ (term.keyState.state = keyState); }; }; key2Enable => { keyState1, keyState2: UpDown; keyState1 ¬ UserInputOps.GetKeySymState[handle, term.keyState1.keySym]; keyState2 ¬ UserInputOps.GetKeySymState[handle, term.keyState2.keySym]; eventMatch ¬ (term.keyState1.state = keyState1) OR (term.keyState2.state = keyState2); }; keyEnableList => { keyState: UpDown; eventMatch ¬ FALSE; FOR lst: LIST OF TIPKeyState ¬ term.lst, lst.rest UNTIL lst=NIL DO keyState ¬ UserInputOps.GetKeySymState[handle, lst.first.keySym]; IF lst.first.state = keyState THEN { eventMatch ¬ TRUE; EXIT }; ENDLOOP; }; predEnable => { predRef: REF; found: BOOL; predicate: REF TIPPredicate.TIPPredicate; [found, predRef] ¬ RefTab.Fetch[predTable, term.predicate]; IF found THEN { predicate ¬ NARROW[predRef]; eventMatch ¬ predicate­[] } ELSE { eventMatch ¬ FALSE }; }; char => TIPPrivate.stdChar­ ¬ AsciiAction[handle, action]; coords => { mp: MousePosition ~ handle.mousePosition; TIPPrivate.stdCoords­ ¬ [mouseX: mp.mouseX, mouseY: mp.mouseY, color: mp.color]; }; trackballChange => { TIPPrivate.stdTrackballChange­ ¬ trackballChange; }; thumbwheelChange => { TIPPrivate.stdThumbwheelChange­ ¬ thumbwheelChange; }; time => { TIPPrivate.stdTime­ ¬ UserInputOps.GetTime[handle]; }; nested => { PushHandle[]; FOR choices: TIPChoiceSeries ¬ term.statement, choices.rest UNTIL choices=NIL DO result ¬ MatchChoice[choices.first]; IF result#NIL THEN RETURN[result]; CopyTopHandle[]; ENDLOOP; PopHandle[]; eventMatch ¬ FALSE; }; result => { IF copied THEN UserInputLookahead.RestoreState[saved: handle, handle: parseInfo.inCreek]; RETURN[term.list]; }; ENDCASE => ERROR; ENDLOOP; }; actionKind: ATOM ¬ a.kind; IF parseInfo.tableHead=transparentTIPTable OR parseInfo.tableHead=identityTIPTable OR parseInfo.tableHead=identityPlusTimeTIPTable THEN { RETURN[LIST[parseInfo.inCreek, NEW[InputActionBody ¬ a] ]]; }; FOR table: TIPTable ¬ parseInfo.tableHead, IF table.opaque THEN NIL ELSE table.link UNTIL table=NIL DO tableImpl: TIPTableImpl ~ table.impl; SELECT actionKind FROM -- for efficiency $IntegerPosition, $Position, $FakePosition => IF tableImpl.ignore.move THEN LOOP; $Key => IF a.down THEN {IF tableImpl.ignore.down THEN LOOP} ELSE {IF tableImpl.ignore.up THEN LOOP}; ENDCASE; firstAccess ¬ TRUE; stackPointer ¬ 0; WITH t: tableImpl SELECT FROM fast => { GetHandleAction[]; -- updates action (and thus action) SELECT action.kind FROM $IntegerPosition, $Position, $FakePosition => { result ¬ MatchChoice[t.mouse]; }; $Key => { keySyms: KeySyms ¬ KeyMapping.GetKeySyms[handle.mapping, action.keyCode]; keySym: KeySym; choice: TIPChoice; FOR i: NAT IN [0..keySyms.n) DO keySym ¬ keySyms[i]; IF action.down THEN choice ¬ TIPFastTables.FetchFromFastTable[t.keyDown, keySym] ELSE choice ¬ TIPFastTables.FetchFromFastTable[t.keyUp, keySym]; IF choice # NIL THEN { result ¬ MatchChoice[choice]; EXIT; }; ENDLOOP; }; ENDCASE; ClearHandleStack[]; -- so garbage collection proceeds IF result#NIL THEN RETURN[result]; }; small => { FOR choices: TIPChoiceSeries ¬ t.all, choices.rest UNTIL choices=NIL DO result ¬ MatchChoice[choices.first]; ClearHandleStack[]; -- so garbage collection proceeds IF result#NIL THEN RETURN[result]; firstAccess ¬ TRUE; ENDLOOP; }; ENDCASE; ENDLOOP; }; RefCharFromCharArray: TYPE ~ ARRAY CHAR OF REF CHAR; InitRefCharFromChar: PROC RETURNS [array: REF RefCharFromCharArray] ~ { array ¬ NEW[RefCharFromCharArray ¬ ALL[NIL]]; FOR c: CHAR IN CHAR DO array[c] ¬ NEW[CHAR ¬ c] ENDLOOP; }; refCharFromChar: REF RefCharFromCharArray ~ InitRefCharFromChar[]; StdCoordsRep: TYPE ~ TIPUser.TIPScreenCoordsRec; StdTrackballChangeRep: TYPE ~ Vector2.VEC; StdThumbwheelChangeRep: TYPE ~ INTEGER; StdTimeRep: TYPE ~ TIPUser.TIPTimeObj; MakeImmutable: PUBLIC PROC [results: LIST OF REF] RETURNS [LIST OF REF] ~ { IF results=NIL THEN RETURN[NIL] ELSE { rest: LIST OF REF ~ MakeImmutable[results.rest]; first: REF ¬ results.first; WITH first SELECT FROM x: REF CHAR => IF x=TIPPrivate.stdChar THEN first ¬ refCharFromChar[x­]; x: REF StdCoordsRep => IF x=TIPPrivate.stdCoords THEN first ¬ NEW[StdCoordsRep ¬ x­]; x: REF StdTrackballChangeRep => IF x=TIPPrivate.stdTrackballChange THEN first ¬ NEW[StdTrackballChangeRep ¬ x­]; x: REF StdThumbwheelChangeRep => IF x=TIPPrivate.stdThumbwheelChange THEN first ¬ NEW[StdThumbwheelChangeRep ¬ x­]; x: REF StdTimeRep => IF x=TIPPrivate.stdTime THEN first ¬ NEW[StdTimeRep ¬ x­]; ENDCASE; RETURN[IF first=results.first AND rest=results.rest THEN results ELSE CONS[first, rest]]; }; }; AsciiAction: PROC [creek: Handle, creekAction: InputActionBody] RETURNS [c: CHAR] = { SELECT creekAction.kind FROM $Key => { lockDown: BOOL ¬ FALSE; char: CHAR ¬ PreferredChar[creek, creekAction.keyCode]; IF char=0c THEN RETURN['?]; BEGIN ctrlDown: BOOL ¬ FALSE; ctrlDown ¬ UserInputOps.GetKeySymState[creek, KeySymsKB.LeftControl] = down OR UserInputOps.GetKeySymState[creek, KeySymsKB.RightControl] = down; IF ctrlDown THEN char ¬ VAL[ORD[char] MOD 40B]; END; RETURN[char]; }; ENDCASE => ERROR; -- Only works when keys go down }; PreferredChar: PROC [creek: Handle, keyCode: KeyTypes.KeyCode] RETURNS [char: CHAR] = { keysym: KeySym; mapping: KeyMapping.Mapping ¬ UserInputOps.GetMapping[creek]; keySym0: KeySym ¬ KeyMapping.GetKeySym[mapping, keyCode, 0]; shiftDown: BOOL ¬ UserInputOps.GetKeySymState[creek, KeySymsKB.LeftShift] = down OR UserInputOps.GetKeySymState[creek, KeySymsKB.RightShift] = down; IF shiftDown THEN { IF keySym0 IN [KeySyms1.a..KeySyms1.z] THEN { keysym ¬ [keySym0.val + KeySyms1.A.val - KeySyms1.a.val]; } ELSE IF keySym0 IN [KeySyms1.A..KeySyms1.Z] THEN keysym ¬ keySym0 ELSE { keySym1: KeySym ¬ KeyMapping.GetKeySym[mapping, keyCode, 1]; IF keySym1.val = 0 THEN keysym ¬ keySym0 ELSE keysym ¬ keySym1; }; } ELSE { IF UserInputOps.GetKeySymState[creek, ShiftLock] = down OR UserInputOps.GetKeySymState[creek, CapsLock] = down THEN { IF keySym0 IN [KeySyms1.a..KeySyms1.z] THEN { keysym ¬ [keySym0.val + KeySyms1.A.val - KeySyms1.a.val]; } ELSE keysym ¬ keySym0; } ELSE { IF keySym0 IN [KeySyms1.A..KeySyms1.Z] THEN keysym ¬ [keySym0.val + KeySyms1.a.val - KeySyms1.A.val] ELSE keysym ¬ keySym0; }; }; char ¬ KeyChars.CharFromKeySym[keysym]; IF char=0C THEN { count: NAT ¬ KeyMapping.CountKeySyms[mapping, keyCode]; FOR n: NAT IN [2..count) DO keysym ¬ KeyMapping.GetKeySym[mapping, keyCode, n]; char ¬ KeyChars.CharFromKeySym[keysym]; IF char#0C THEN RETURN [char]; ENDLOOP; FOR n: NAT IN [0..MIN[count, 2]) DO keysym ¬ KeyMapping.GetKeySym[mapping, keyCode, n]; char ¬ KeyChars.CharFromKeySym[keysym]; IF char#0C THEN RETURN [char]; ENDLOOP; }; }; predTable: PUBLIC RefTab.Ref ¬ RefTab.Create[]; -- table for user defined predicates CreateTIPClient: PUBLIC PROC [notify: TIPNotifyProc ¬ NIL, buttons: TIPButtonProc ¬ NIL, clientData: REF ANY, name: Rope.ROPE ¬ NIL] RETURNS [self: TIPClient] = TRUSTED { self ¬ NEW[TIPClientRec ¬ [ notifyProc: notify, buttonProc: buttons, clientData: clientData, parseInfo: CreateParseInfo[NIL, name], matcher: NIL ]]; }; GetInputHandle: PUBLIC PROC [tipClient: TIPClient] RETURNS [handle: Handle] = { handle ¬ tipClient.parseInfo.inCreek; }; StartTIPClient: PUBLIC PROC [tipClient: TIPClient] = { doneFlag: REF BOOL ~ NEW[BOOL ¬ FALSE]; TRUSTED {Process.Detach[ FORK MatchProcess[tipClient, doneFlag] ]}; WHILE ~doneFlag­ DO --we wait just in case the continuation would access tipClient.matcher Process.Pause[1] ENDLOOP; }; DestroyClient: PUBLIC PROC [self: TIPClient] = { Interrupt[self, TRUE]; }; GetParseInfo: PUBLIC PROC [tipClient: TIPClient] RETURNS [parseInfo: TIPParseInfo] = { parseInfo ¬ tipClient.parseInfo; }; CreateParseInfo: PUBLIC PROC [parseTable: TIPTable ¬ NIL, name: Rope.ROPE ¬ NIL] RETURNS [new: TIPParseInfo] = { StackName: PROC [i: NAT] RETURNS [atom: ATOM] = { atom ¬ Atom.MakeAtom[IO.PutFR["%g/stack%g", [rope[name]], [integer[i]] ]]; }; PathName: PROC [secondPart: Rope.ROPE] RETURNS [atom: ATOM] = { atom ¬ Atom.MakeAtom[IO.PutFR["%g/%g", [rope[name]], [rope[secondPart]] ]]; }; IF name=NIL THEN name ¬ "NoName"; new ¬ NEW[TIPParseInfoRec ¬ [ inCreek: UserInputOps.Create[NIL, NIL, PathName["inCreek"]], localCreek: UserInputOps.Create[NIL, NIL, PathName["localCreek"]], timeCreek: UserInputOps.Create[NIL, NIL, PathName["timeCreek"]], tableHead: parseTable ]]; FOR i: NAT IN [0..TIPPrivate.maxTIPNesting) DO new.creekStack[i] ¬ UserInputOps.Create[NIL, NIL, StackName[i]]; ENDLOOP; }; PushTIPTable: PUBLIC PROC [user: TIPClient, table: TIPTable, opaque: BOOL] = { t: TIPTable; FOR t ¬ table, t.link UNTIL t.link=NIL DO ENDLOOP; t.link ¬ user.parseInfo.tableHead; user.parseInfo.tableHead ¬ table; table.opaque ¬ opaque; }; PopTIPTable: PUBLIC PROC [user: TIPClient] RETURNS [old: TIPTable] = { old ¬ user.parseInfo.tableHead; IF old#NIL THEN user.parseInfo.tableHead ¬ old.link; }; Register: PUBLIC PROC [key: ATOM, p: TIPPredicate.TIPPredicate] = { RegisterTIPPredicate[key, p]; }; RegisterTIPPredicate: PUBLIC PROC [key: ATOM, p: TIPPredicate.TIPPredicate] = { [] ¬ RefTab.Store[predTable, key, NEW[TIPPredicate.TIPPredicate ¬ p]]; }; END. ’ TIPMatcherImpl.mesa Copyright Σ 1985, 1986, 1991, 1992 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) October 21, 1985 10:09:08 pm PDT Bier, September 30, 1993 11:52 am PDT Michael Plass, May 25, 1989 4:35:44 pm PDT Christian Jacobi, August 25, 1992 2:19 pm PDT Kenneth A. Pier, August 24, 1992 12:30 pm PDT Willie-s, December 18, 1991 12:18 pm PST Doug Wyatt, August 19, 1992 2:54 pm PDT mouseGrainCreek: Handle ¬ UserInputOps.Create[]; -- only for setting recording grain discard any mouse/keyboard events not yet processed Forces TIP interpreter to top level state. Wizards only, please. Interrupt[self, FALSE] A TIPClient includes the parsed form of a tip table, a notify proc to call, and a stream, user.parseInfo.inCreek, at which we receive actions. Keep two copies of screen coords so that client can overwrite passed copy 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 CodeTimer.StartInt[$MatchLoop, $TIP]; CodeTimer.StopInt[$MatchLoop, $TIP]; transparentAction: REF UserInput.ActionBody ~ NEW[UserInput.ActionBody]; transparentResult: LIST OF REF ~ LIST[NIL, transparentAction]; DKW: gross hack to avoid excessive allocation when using a transparent TIP table we trust that only one process is using this! But Christian Jacobi is experimenting with multi-process worlds now. -- Bier, September 30, 1991 identityAction: UserInputGetActions.InputAction _ NEW[InputActionBody]; identityResult: LIST OF REF ~ LIST[NIL, identityAction]; See if this is a trackball key event. See if this is a FastTRAP trackball event. copy local handle for checking time since no "putback" Gets the next click event if one comes before term.mSecs. Otherwise, returns $TimeOut. Invariants: (1) Either (copied = FALSE and handle = parseInfo.inCreek) or copied = TRUE and handle = parseInfo.localCreek). (2) GetAction is never performed on parseInfo.inCreek. If a match is found, the matched items are atomically removed from parseInfo.inCreek by the "result" case of MatchChoice. (3) Upon return, parseInfo.creekStack[i] for i >= stackPointer and parseInfo.localCreek are closed. Tells whether the Handle on top of the stack has been changed at all Used to eliminate unnecessary copying of handles Uses parseInfo.localCreek as implicit parameter, assuming handle = localCreek eventMatch will stay TRUE until some term fails to match the event. If we get a complete match, we return it. Otherwise, by setting firstAccess to TRUE, we move back to the beginning of the handle so the next call to MatchChoice begins with the same event. To match this TIP choice ("line" of the TIP table), the current event much satisfy each of the terms (e.g., Red Up WHILE Yellow Up WHILE Blue Up). If this is the first term, then get the first action, whatever it is. Otherwise, skip over mouse actions until we find a key action (waiting forever if necessary) We've actually made a match, remove all matched tokens from parseInfo.inCreek. If coords is returned, term.list contains TIPPrivate.stdCoords. primaryKeySym: KeySym _ PrimaryKeySym[handle, action.keyCode]; For each top-level line in the TIP table, try to match that line to the current event on the Handle. If a key has just gone down, and we wish to interpret the key as the Ascii CHAR that a native user of the keyboard would expect to be typing, we find the preferred glyph on the key and convert it to Ascii. To produce an ASCII Control Character, we have to use a special mechanism, because Control Characters are not technically glyphs. This is for compatibility with Cedar 7.0: Give the higher guys a chance firsts; we did already a try to consider the shift key TIPUser facilities garbage collector will get old table unless the client keeps a reference a user-defined predicate may be included in the enables list of a TIPTable via this association mechanism. Κ†–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ ΟeœC™NK™4K™%K™*K™.K™-K™(K™'K™—šΟk ˜ Jšœ,žœσ˜‘K˜—šΟnœžœž˜Jšžœ©˜°Kšžœdž˜p—˜Kšœ žœžœΟc˜EKšœžœžœ ˜K˜Kšœ žœ˜,Kšœžœ'˜Kšžœ˜K˜K˜—š Ÿœžœžœ žœžœžœ˜WKšžœžœ žœžœ˜HK˜K˜—šŸœžœžœ˜3Kšœ3™3Kšœ1˜1Kšœ˜K˜—š ŸœžœžœFžœžœžœ˜{K˜!K˜Kšžœ žœžœ˜)Kšœ˜K˜—šŸ œžœžœ˜/Kšœ*™*Kšœ™Kšžœ M˜TK™Kšœ˜K˜—šŸ œžœžœžœ˜˜@šžœ˜K˜Kšœ žœžœ žœ ˜5K˜—Kšžœžœ˜——Kšžœžœ˜—šžœΟbœm˜Kšžœg˜kKšžœžœ˜—K˜—Kšžœžœ˜šž˜˜šžœžœž˜$šžœž˜˜*Kšœ ‘œ˜,—Kšžœžœ ˜*——šžœ žœžœ˜Kšœ˜—K˜——Kšžœ˜—K™$K˜K˜—šŸœžœžœžœ žœžœžœžœžœ˜^Kšžœ˜•šœ˜K˜—Kšœžœžœ™Hš œžœžœžœžœžœ™>K™PK™-K™`—Kšœ2žœ™GKš œžœžœžœžœžœ™8K™—š Ÿ œžœ5žœžœžœ˜lK™%šžœ žœž˜šœ*˜*Kšœžœ%˜:šžœž˜šœ‘œ˜Kšœ(˜(K˜/šžœ žœž˜š‘œ˜šžœžœ˜šžœž˜šœ  ˜"Kšœ5‘ œžœ‘˜_K˜—˜ Kšœ5‘œžœ‘˜aK˜—šœ  ˜$Kšœ5‘œžœ‘˜`K˜—Kšžœžœ˜—K˜—šžœ˜šžœž˜šœ  ˜"Kšœ5‘ œžœ‘˜]K˜—˜ Kšœ5‘œžœ‘˜_K˜—šœ  ˜$Kšœ5‘œžœ‘˜^K˜—Kšžœžœ˜—K˜—K˜—Kš‘ œžœ˜Kš‘œžœ˜Kšžœ ˜#—K˜—Kšžœ ˜—K˜—Kšžœ  ˜)—K˜K˜—š Ÿœžœ;žœžœžœžœ ˜žK™*šžœ žœž˜šœ*˜*Kšœžœ%˜:šžœž˜˜Kšœ(˜(K˜/šžœ žœž˜Kšœžœ˜ šœ ‘˜K˜'Kš‘ Πbkœ˜K˜—Kšœ žœ˜Kšžœ˜#—K˜—Kšžœ ˜—K˜—Kšžœ  ˜)—K˜K˜—š Ÿœžœ<žœžœžœžœ ˜™šžœ žœž˜šœ*˜*Kšœžœ%˜:šžœž˜˜Kšœ(˜(K˜/šžœ žœž˜Kšœžœ˜ Kšœ žœ˜šœ ‘˜ K˜Kš‘ ’œ˜K˜—Kšžœ˜#—K˜—Kšžœ ˜—K˜—Kšžœ  ˜)—K˜K˜—š ŸœžœEžœžœžœ˜ƒKšœ˜šžœ ž˜šœ ˜ K˜Išžœžœžœž˜Kšœ˜šžœ žœ˜šžœžœ˜=Kšžœžœ˜—K˜—šžœ˜šžœžœ˜;Kšžœžœ˜—K˜—šžœ˜Kšœ ‘ œžœ˜—Kšžœ˜—K˜—Kšœ‘ œ˜/Kšžœ !˜*—K˜K˜—š ŸœžœFžœžœžœ˜›K™6K˜I˜LK˜*K˜K™W—š‘ œžœ ž˜$Kšœ˜Kšžœ˜—Kšœ) $˜MK˜K˜—šŸœž œ/žœ žœžœžœžœžœ˜y™ K™oKšœ±™±Kšœc™c—K˜K˜Kšœžœžœ˜"šœ žœžœ˜K™DKšœ0™0—Kšœžœ˜šŸœžœžœ˜$šžœžœžœž˜"Kšœ,˜,Kšžœ˜—K˜K˜)Kšœ˜—šŸ œžœžœ˜K˜XK˜ Kšœ žœ˜Kšœ˜—šŸ œžœžœ˜K™Mšžœ žœ˜K˜NKšœ žœ˜Kšœ˜—Kšœ˜—šŸ œžœžœ˜K˜ Kšœ7˜7Kšœ˜—šŸœžœ4žœ˜Ršžœ ˜šžœ˜Kšœžœ˜K˜Kšœ  ˜*Kšœ žœ˜Kšœ˜—šžœ˜šžœ žœ˜Kšœ žœ˜KšœU˜UK˜Kšœ˜—˜?K˜K˜—Kšœ žœ˜Kšœ˜——Kšœ˜—šŸ œžœžœ žœžœžœžœžœ˜UK™‚Kšœ žœžœ˜Kšœžœ ˜&Kšœžœ˜K˜Kšœsžœ žœ ™’š žœ'žœ žœžœž˜Kšžœžœž˜"šœ -˜=˜K™£—K˜6Kšœ˜—šœ ˜-K˜!šžœžœ@˜ZKšžœžœ˜—Kšœ˜—šœ ˜/K˜!šžœžœB˜\Kšžœžœ˜—Kšœ˜—šœ ˜%K˜!šœ žœ ž˜$Kšœ.žœ˜3Kšžœžœ˜—Kšœ˜—šœ $˜5Kšžœ žœžœ ˜8K˜AKšœ˜—šœ˜š Ÿœžœžœ-žœžœžœ˜zšžœž˜K˜.K˜2K˜0Kšžœ žœ˜—K˜—K˜K˜Kšœžœžœ˜K˜Ašžœžœ˜Kšœžœ˜4K˜RK˜—šžœ˜K˜EKšœ.˜.K˜—Kšœ˜—šœ˜Kšœ˜K˜GK˜Gšœ/˜/Kšžœ$˜&—Kšœ˜—šœ˜Kšœ˜Kšœ žœ˜š žœžœžœ"žœžœž˜BK˜Ašžœžœ˜$Kšœ žœžœ˜—Kšžœ˜—Kšœ˜—šœ˜Kšœ žœ˜ Kšœžœ˜ Kšœ žœ˜)K˜;šžœ˜šžœ˜Kšœ žœ ˜K˜—Kšžœžœ˜—Kšœ˜—K˜:šœ ˜ K˜)KšœP˜PKšœ˜—˜K˜1Kšœ˜—˜K˜3Kšœ˜—˜ K˜3Kšœ˜—šœ ˜ K˜ šžœ8žœ žœž˜PK˜$Kšžœžœžœžœ ˜"K˜Kšžœ˜—K˜ Kšœ žœ˜Kšœ˜—šœ ˜ Kšžœžœ‘J˜YKšœN™NKšžœ ˜Kšœ?™?Kšœ˜—Kšžœžœ˜—Kšžœ˜—Kšœ˜—K˜Kšœ žœ ˜K˜šžœ)žœ&žœ.žœ˜‰Kšžœžœžœ˜;K˜K˜—šžœ(žœžœžœžœ žœžœž˜fK˜%šžœ žœ ˜(Kšœ.žœžœžœ˜Qš œžœžœžœžœžœ˜;Kšžœžœžœžœ˜(—Kšžœ˜K˜—Kšœžœ˜K˜K˜šžœžœž˜šœ ˜ Kšœ #˜6šžœ ž˜šœ/˜/K˜K˜—šœ ˜ K™>K˜IKšœ˜Kšœ˜šžœžœžœž˜Kšœ˜Kšžœ žœ-‘œ ˜PKšžœ<˜@šžœ žœžœ˜Kšœ˜Kšžœ˜K˜—Kšžœ˜—K˜—Kšžœ˜—Kšœ !˜5Kšžœžœžœžœ ˜"Kšœ˜—šœ ˜ K™dšžœ0žœ žœž˜GKšœ ‘ œ˜$Kšœ !˜5Kšžœžœžœžœ ˜"Kšœžœ˜Kšžœ˜—Kšœ˜—Kšžœ˜—Kšžœ˜—K˜K˜—Kš œžœžœžœžœžœžœ˜4šŸœžœžœ žœ˜GKšœžœžœžœ˜-Kšžœžœžœžœžœ žœžœžœ˜8K˜—šœžœ.˜BK™—Kšœžœ˜0Kšœžœ žœ˜*Kšœžœžœ˜'šœ žœ˜&K™—šŸ œžœžœ žœžœžœžœžœžœžœ˜Kš žœ žœžœžœžœžœ˜&Kšœžœžœžœ˜0Kšœžœ˜šžœžœž˜Kš œžœžœžœžœ˜HKš œžœžœžœ žœ˜VKš œžœžœ!žœ žœ˜qKš œžœžœ"žœ žœ˜tKš œžœžœžœ žœ˜PKšžœ˜—Kš žœžœžœžœ žœžœ˜YK˜—K˜K˜—šŸ œžœ/žœžœ˜UK™Νšžœž˜šœ ˜ Kšœ žœžœ˜Kšœžœ-˜7Kšžœ žœžœ˜K˜K™¬šž˜Kšœ žœžœ˜KšœLžœC˜‘Kš žœ žœžœžœžœ˜/Kšžœ˜—Kšžœ˜ Kšœ˜—Kšžœžœ ˜1—Kšœ˜K˜—šŸ œžœ,žœžœ˜WK˜Kšœ=˜=Kšœ<˜