DIRECTORY Atom, FS, GGBasicTypes, GGSlackProcess, GGError, GGInterfaceTypes, GGSessionLog, IO, Process, Rope; GGSlackProcessImpl: CEDAR MONITOR IMPORTS Atom, FS, GGError, GGSessionLog, IO, Process EXPORTS GGSlackProcess = BEGIN Point: TYPE = GGBasicTypes.Point; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; EventProc: TYPE = GGSlackProcess.EventProc; MouseEventProc: TYPE = GGSlackProcess.MouseEventProc; EventNotifyProc: TYPE = GGSlackProcess.EventNotifyProc; ActionQueueMax: NAT = 100; ActionQueue: TYPE = REF ActionQueueObj; ActionQueueObj: TYPE = RECORD [ head: NAT _ 0, tail: NAT _ 0, size: NAT _ ActionQueueMax, gDatas: ARRAY [0..ActionQueueMax] OF GargoyleData, actions: ARRAY [0..ActionQueueMax] OF LIST OF REF ANY, points: ARRAY [0..ActionQueueMax] OF Point, mouseProcs: ARRAY [0..ActionQueueMax] OF MouseEventProc, eventProcs: ARRAY [0..ActionQueueMax] OF EventProc ]; LogLength: NAT = 20; ActionLog: TYPE = RECORD [ received: ARRAY[0..LogLength-1] OF BOOL, -- received an action versus performed an action. bashed: ARRAY[0..LogLength-1] OF BOOL, events: ARRAY[0..LogLength-1] OF REF ANY, points: ARRAY [0..LogLength-1] OF Point ]; globalLog: ActionLog; logHead: NAT; globalProcess: PROCESS _ NIL; globalQ: ActionQueue; globalEventNotifyProc: EventNotifyProc _ NIL; sessionLog: IO.STREAM; auditOn: BOOL _ FALSE; LogRawMouse: PUBLIC ENTRY PROC [point: Point] = { globalLog.events[logHead] _ $RawMouse; globalLog.points[logHead] _ point; logHead _ (logHead + 1) MOD LogLength; }; LogReceived: PROC [event: REF ANY, point: Point, bash: BOOL] = { globalLog.received[logHead] _ TRUE; globalLog.bashed[logHead] _ bash; globalLog.events[logHead] _ event; globalLog.points[logHead] _ point; logHead _ (logHead + 1) MOD LogLength; }; LogActed: PROC [action: LIST OF REF ANY, point: Point, mouseEvent: BOOL] = { globalLog.received[logHead] _ FALSE; globalLog.bashed[logHead] _ FALSE; globalLog.events[logHead] _ action.first; globalLog.points[logHead] _ point; logHead _ (logHead + 1) MOD LogLength; IF auditOn THEN GGSessionLog.EnterAction[point, action, mouseEvent]; }; OutputLog: PUBLIC ENTRY PROC [] = { i: NAT _ (logHead - 1 + LogLength) MOD LogLength; -- back up pointer one slot msgRope: Rope.ROPE; msgRope _ IO.PutFR["logHead = %g", IO.int[logHead] ]; GGError.AppendTypescript[msgRope]; GGError.AppendTypescript["Most Recent Event: "]; WHILE i#logHead DO IF globalLog.events[i] = $RawMouse THEN msgRope _ IO.PutFR["RAW MOUSE [%g, %g]", [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE IF globalLog.bashed[i] THEN IF ISTYPE[globalLog.events[i], ATOM] THEN msgRope _ IO.PutFR["Bashed %g at [%g, %g]", [rope[Atom.GetPName[NARROW[globalLog.events[i]]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE msgRope _ IO.PutFR["Bashed %g at [%g, %g]", [character[NARROW[globalLog.events[i], REF CHAR]^]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE IF globalLog.received[i] THEN IF ISTYPE[globalLog.events[i], ATOM] THEN msgRope _ IO.PutFR["Received %g at [%g, %g]", [rope[Atom.GetPName[NARROW[globalLog.events[i]]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE msgRope _ IO.PutFR["Received %g at [%g, %g]", [character[NARROW[globalLog.events[i], REF CHAR]^]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE IF ISTYPE[globalLog.events[i], ATOM] THEN msgRope _ IO.PutFR["Acted on %g at [%g, %g]", [rope[Atom.GetPName[NARROW[globalLog.events[i]]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE msgRope _ IO.PutFR["Acted on %g at [%g, %g]", [character[NARROW[globalLog.events[i], REF CHAR]^]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ]; GGError.AppendTypescript[msgRope]; i _ (i - 1 + LogLength) MOD LogLength; ENDLOOP; GGError.AppendTypescript["Least Recent Event\n"]; }; Init: PROC = { globalQ _ NEW[ActionQueueObj]; logHead _ 0; FOR i: NAT IN [0..LogLength-1] DO globalLog.events[i] _ $None; globalLog.received[i] _ TRUE; ENDLOOP; }; RegisterEventNotifyProc: PUBLIC PROC [proc: EventNotifyProc] = { globalEventNotifyProc _ proc; }; EventNotify: PUBLIC PROC [notice: LIST OF REF ANY] = { globalEventNotifyProc[notice]; }; InternalQueueInputAction: INTERNAL PROC [callBack: MouseEventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = TRUSTED { qIndex: NAT _ 0; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; qIndex _ globalQ.tail; globalQ.gDatas[qIndex] _ gargoyleData; globalQ.actions[qIndex] _ inputAction; globalQ.points[qIndex] _ worldPt; globalQ.mouseProcs[qIndex] _ callBack; globalQ.eventProcs[qIndex] _ NIL; globalQ.tail _ (qIndex + 1) MOD globalQ.size; LogReceived[event: inputAction.first, point: worldPt, bash: FALSE]; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Actor]; }; QueueInputAction: PUBLIC ENTRY PROC [callBack: MouseEventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = { InternalQueueInputAction[callBack, inputAction, worldPt, gargoyleData]; }; QueueOrBashInputAction: PUBLIC ENTRY PROC [callBack: MouseEventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = TRUSTED { prevAction: ATOM _ PeekActionTail[globalQ]; qIndex: NAT _ (globalQ.tail - 1 + globalQ.size) MOD globalQ.size; IF prevAction = NARROW[inputAction.first, ATOM] THEN { -- bash globalQ.gDatas[qIndex] _ gargoyleData; globalQ.actions[qIndex] _ inputAction; globalQ.points[qIndex] _ worldPt; globalQ.mouseProcs[qIndex] _ callBack; globalQ.eventProcs[qIndex] _ NIL; LogReceived[event: inputAction.first, point: worldPt, bash: TRUE]; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Actor]; } ELSE InternalQueueInputAction[callBack, inputAction, worldPt, gargoyleData]; -- queue. }; InternalQueueInputActionNoPoint: INTERNAL PROC [callBack: EventProc, inputAction: LIST OF REF ANY, gargoyleData: GargoyleData] = TRUSTED { qIndex: NAT _ 0; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; qIndex _ globalQ.tail; globalQ.gDatas[qIndex] _ gargoyleData; globalQ.actions[qIndex] _ inputAction; globalQ.points[qIndex] _ [-999.0, -999.0]; globalQ.mouseProcs[qIndex] _ NIL; globalQ.eventProcs[qIndex] _ callBack; globalQ.tail _ (qIndex + 1) MOD globalQ.size; LogReceived[event: inputAction.first, point: [-999.0, -999.0], bash: FALSE]; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Actor]; }; QueueInputActionNoPoint: PUBLIC ENTRY PROC [callBack: EventProc, inputAction: LIST OF REF ANY, gargoyleData: GargoyleData] = TRUSTED { InternalQueueInputActionNoPoint[callBack, inputAction, gargoyleData]; }; QueueOrBashInputActionNoPoint: PUBLIC ENTRY PROC [callBack: EventProc, inputAction: LIST OF REF ANY, gargoyleData: GargoyleData] = TRUSTED { prevAction: ATOM _ PeekActionTail[globalQ]; qIndex: NAT _ (globalQ.tail - 1 + globalQ.size) MOD globalQ.size; IF prevAction = NARROW[inputAction.first, ATOM] THEN { -- bash globalQ.gDatas[qIndex] _ gargoyleData; globalQ.actions[qIndex] _ inputAction; globalQ.points[qIndex] _ [0.0, 0.0]; globalQ.mouseProcs[qIndex] _ NIL; globalQ.eventProcs[qIndex] _ callBack; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Actor]; } ELSE InternalQueueInputActionNoPoint[callBack, inputAction, gargoyleData]; -- queue }; DeQueueAction: INTERNAL PROC [q: ActionQueue] RETURNS [mouseEventProc: MouseEventProc, eventProc: EventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = { IF q.tail = q.head THEN ERROR; mouseEventProc _ q.mouseProcs[q.head]; eventProc _ q.eventProcs[q.head]; inputAction _ q.actions[q.head]; worldPt _ q.points[q.head]; gargoyleData _ q.gDatas[q.head]; q.head _ (q.head + 1) MOD q.size; LogActed[inputAction, worldPt, mouseEventProc#NIL]; }; NotFull: CONDITION; EmptyQ: INTERNAL PROC [q: ActionQueue] RETURNS [BOOL] = { RETURN[q.tail = q.head]; }; PeekActionTail: PRIVATE PROC [q: ActionQueue] RETURNS [whatToDo: ATOM] = { IF q.head = q.tail THEN RETURN[NIL]; whatToDo _ NARROW[q.actions[(q.tail - 1 + q.size) MOD q.size].first, ATOM]; }; Restart: PUBLIC ENTRY PROC [] = TRUSTED { Process.Detach[globalProcess _ FORK Actor]; }; Notify: ENTRY PROC [] = { }; Actor: PROC = { inputAction: LIST OF REF ANY; worldPt: Point; gargoyleData: GargoyleData; mouseEventProc: MouseEventProc; eventProc: EventProc; [mouseEventProc, eventProc, inputAction, worldPt, gargoyleData] _ NextAction[]; WHILE inputAction # NIL DO IF mouseEventProc = NIL THEN eventProc[inputAction, gargoyleData] ELSE mouseEventProc[inputAction, gargoyleData, worldPt]; [mouseEventProc, eventProc, inputAction, worldPt, gargoyleData] _ NextAction[]; ENDLOOP; Notify[]; }; NextAction: PUBLIC ENTRY PROC RETURNS [mouseEventProc: MouseEventProc, eventProc: EventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = { IF EmptyQ[globalQ] THEN { globalProcess _ NIL; -- no mouse action. Let process die. inputAction _ NIL; } ELSE { [mouseEventProc, eventProc, inputAction, worldPt, gargoyleData] _ DeQueueAction[globalQ]; BROADCAST NotFull; }; }; AuditOn: PRIVATE ENTRY PROC [f: IO.STREAM] = { sessionLog _ f; auditOn _ TRUE; }; AuditOff: PRIVATE ENTRY PROC [] RETURNS [f: IO.STREAM] = { f _ sessionLog; sessionLog _ NIL; auditOn _ FALSE; }; OpenSessionLog: PUBLIC PROC [fileName: Rope.ROPE] = { f: IO.STREAM; success: BOOL; [f, success] _ OpenFile[fileName]; IF NOT success THEN RETURN; GGSessionLog.SetStream[f]; AuditOn[f]; }; OpenFile: PROC [name: Rope.ROPE] RETURNS [f: IO.STREAM, success: BOOL] = { success _ TRUE; f _ FS.StreamOpen[name, $create ! FS.Error => { IF error.group = user THEN { success _ FALSE; GGError.Append["Picture file problem: ", oneLiner]; GGError.Append[error.explanation, oneLiner]; } ELSE ERROR; CONTINUE}]; }; CloseSessionLog: PUBLIC PROC [] = { f: IO.STREAM; f _ AuditOff[]; IF f#NIL THEN f.Close[]; }; Init[]; END. GGSlackProcessImpl.mesa Formerly called: GGDrawProcessImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by Pier on May 20, 1986 2:06:47 pm PDT Last edited by Bier on February 18, 1986 10:33:17 pm PST Contents: A flexible setup, suggested by Scott McGregor, for processing mouse input as fast as you can (but no faster). Used to synchronize mouse point processing with the mousepoint. If the processing algorithms become faster, this procedure will still do the right thing. gargoyleData: GargoyleData, KAP February 18, 1986 the following loop prints out the "LogLength-1" most recent events. The most recent event is printed first. ActionDone: CONDITION; Add an action to the tail of the slack process queue. This action will definitely be performed, in its turn. Process.Detach is an unsafe operation. globalQ.gargoyleData _ gargoyleData; --KAP. February 18, 1986 Add an action to the slack process queue. Add an action to the slack process queue. If the previous action is of the same sort, then overwrite it instead of adding a new one (useful for dragging). Add an action to the tail of the slack process queue. This action will definitely be performed, in its turn. Add an action to the slack processes queue. This action will definitely be performed, in its turn. Add an action to the slack processes queue. If the previous action is of the same sort, then overwrite it instead of adding a new one (useful for testing Gravity). PeekActionHead: PRIVATE PROC [q: ActionQueue] RETURNS [whatToDo: ATOM] = { IF q.tail = q.head THEN RETURN[NIL]; whatToDo _ NARROW[q.actions[q.head].first, ATOM]; }; Sometimes the Painter process is aborted because of a bug in one of the dispatched procedures. This procedure allows the user to get things going again. NOTIFY ActionDone; Open a file, grab the Monitor and turn on auditing, RETURN. Two possiblilities 1) File doesn't exist. Print error message. 2) File does exist. File it in. Succeed. grab the Monitor, turn off auditing, Close the log file, RETURN. Κ ‘˜Icode™™'Kšœ Οmœ1™Kšœ&˜&Kšœ&˜&Kšœ!˜!Kšœ&˜&Kšœžœ˜!Kšœ<žœ˜BKšžœžœžœ žœ˜GK˜—Kšž‘œ0  ˜VK˜K˜—šΠbnœžœžœ$žœžœžœžœ žœ˜ŠK™mKšœžœ˜Kš žœžœžœžœ žœ˜OKšœ˜Kšœ&˜&Kšœ&˜&Kšœ*˜*Kšœžœ˜!Kšœ&˜&Kšœžœ˜-KšœEžœ˜LKšžœžœžœ žœ˜GK˜K˜—š’œžœžœžœ$žœžœžœžœ žœ˜†K™cKš’œ&˜EK˜K˜—š’œžœžœžœ$žœžœžœžœ žœ˜ŒK™€Kšœ žœ˜+Kšœžœ%žœ˜AK˜š žœžœžœžœ ˜>Kšœ&˜&Kšœ&˜&Kšœ$˜$Kšœžœ˜!Kšœ&˜&Kšžœžœžœ žœ˜GK˜—Kšž’ œ' ˜SK˜K˜—š‘ œžœžœžœEžœžœžœžœ1˜ΊKšžœžœžœ˜Kšœ&˜&Kšœ!˜!Kšœ ˜ Kšœ˜Kšœ ˜ Kšœžœ˜!Kšœ.žœ˜3K˜—K˜šœ ž œ˜K˜—š‘œž œžœžœ˜9Kšžœ˜K˜K˜—š ‘œžœžœžœ žœ™JKšžœžœžœžœ™$Kšœ žœžœ™1K™K™—š ‘œžœžœžœ žœ˜JKšžœžœžœžœ˜$Kšœ žœ!žœžœ˜KK˜K˜—š ‘œžœžœžœžœ˜*K™™Kšœžœ˜+K˜K˜—š‘œžœžœ˜Kšžœ ™K˜K˜—š‘œžœ˜Kš œ žœžœžœžœ˜K˜Kšœ˜Kšœ˜Kšœ˜K˜KšœO˜Ošžœžœž˜Kšžœžœžœ%˜AKšžœ4˜8KšœO˜O—Kšžœ˜K˜ Kšœ˜K˜—š‘ œžœžœžœžœEžœžœžœžœ1˜ͺšžœžœ˜Kšœžœ %˜:Kšœžœ˜K˜—šžœ˜KšœY˜YKšž œ ˜K˜—Kšœ˜—K˜š ‘œžœžœžœžœžœ˜.K˜Kšœ žœ˜K˜K˜—š‘œžœžœžœžœžœžœ˜:Kšœ˜Kšœ žœ˜Kšœ žœ˜K˜K˜—š‘œžœžœžœ˜5K™;Kšœžœžœ˜ Kšœ žœ˜Kšœ"˜"Kšžœžœ žœžœ˜K˜K˜ K˜K˜—š‘œžœ žœžœžœžœ žœ˜JKšœ žœ˜K˜šœ™Kšœ-™-Kšœ+™+—šœžœ˜šœžœ ˜šžœžœ˜Kšœ žœ˜Kšœ3˜3Kšœ,˜,Kšœ˜—Kšžœžœ˜ Kšžœ˜ —K˜K˜——š‘œžœžœ˜#Kšœžœžœ˜ K™@K˜Kšžœžœžœ ˜K˜—K˜˜K˜—K˜Kšžœ˜—…—&Μ