<> <> <> <> <> <> 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; <> <<1) File doesn't exist. Print error message.>> <<2) File does exist. File it in. Succeed.>> 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.