<> <> <> <> <> <> DIRECTORY Atom, FS, GGBasicTypes, GGDrawProcess, GGError, GGInterfaceTypes, GGSessionLog, IO, Process, Rope; GGDrawProcessImpl: CEDAR MONITOR IMPORTS Atom, FS, GGError, GGSessionLog, IO, Process EXPORTS GGDrawProcess = BEGIN Point: TYPE = GGBasicTypes.Point; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; globalProcess: PROCESS _ NIL; globalQ: ActionQueue; sessionLog: IO.STREAM; auditOn: BOOL _ FALSE; EventProc: TYPE = GGDrawProcess.EventProc; MouseEventProc: TYPE = GGDrawProcess.MouseEventProc; ActionQueueMax: NAT = 100; ActionQueue: TYPE = REF ActionQueueObj; ActionQueueObj: TYPE = RECORD [ head: NAT _ 0, tail: NAT _ 0, size: NAT _ ActionQueueMax, gargoyleData: 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 ATOM, points: ARRAY [0..LogLength-1] OF Point ]; globalLog: ActionLog; logHead: NAT; LogRawMouse: PUBLIC ENTRY PROC [point: Point] = { globalLog.events[logHead] _ $RawMouse; globalLog.points[logHead] _ point; logHead _ (logHead + 1) MOD LogLength; }; LogReceived: PROC [event: ATOM, 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] = { atom: ATOM _ NARROW[action.first]; globalLog.received[logHead] _ FALSE; globalLog.bashed[logHead] _ FALSE; globalLog.events[logHead] _ atom; globalLog.points[logHead] _ point; logHead _ (logHead + 1) MOD LogLength; IF auditOn THEN GGSessionLog.EnterAction[point, action, mouseEvent]; }; OutputLog: PUBLIC ENTRY PROC [] = { i: NAT _ logHead; msgRope: Rope.ROPE; WHILE i#((logHead + 1) MOD LogLength) 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 msgRope _ IO.PutFR["Bashed %g at [%g, %g]", [rope[Atom.GetPName[globalLog.events[i]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE IF globalLog.received[i] THEN msgRope _ IO.PutFR["Received %g at [%g, %g]", [rope[Atom.GetPName[globalLog.events[i]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ] ELSE msgRope _ IO.PutFR["Acted on %g at [%g, %g]", [rope[Atom.GetPName[globalLog.events[i]]]], [real[globalLog.points[i][1]]], [real[globalLog.points[i][2]]] ]; GGError.AppendTypescript[msgRope]; i _ (i - 1 + LogLength) MOD LogLength; ENDLOOP; }; Init: PROC = { globalQ _ NEW[ActionQueueObj]; logHead _ 0; FOR i: NAT IN [0..LogLength-1] DO globalLog.events[i] _ $None; globalLog.received[i] _ TRUE; ENDLOOP; }; <> QueueInputAction: PUBLIC ENTRY PROC [callBack: MouseEventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = TRUSTED { <> globalQ.gargoyleData _ gargoyleData; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; globalQ.actions[globalQ.tail] _ inputAction; globalQ.points[globalQ.tail] _ worldPt; globalQ.mouseProcs[globalQ.tail] _ callBack; globalQ.eventProcs[globalQ.tail] _ NIL; globalQ.tail _ (globalQ.tail + 1) MOD globalQ.size; LogReceived [event: NARROW[inputAction.first], point: worldPt, bash: FALSE]; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; }; QueueOrBashInputAction: PUBLIC ENTRY PROC [callBack: MouseEventProc, inputAction: LIST OF REF ANY, worldPt: Point, gargoyleData: GargoyleData] = TRUSTED { <> prevAction: ATOM _ PeekPaintActionTail[globalQ]; globalQ.gargoyleData _ gargoyleData; IF prevAction = NARROW[inputAction.first, ATOM] THEN { globalQ.actions[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ inputAction; globalQ.points[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ worldPt; globalQ.mouseProcs[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ callBack; globalQ.eventProcs[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ NIL; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; LogReceived [event: NARROW[inputAction.first], point: worldPt, bash: TRUE]; RETURN; }; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; globalQ.actions[globalQ.tail] _ inputAction; globalQ.points[globalQ.tail] _ worldPt; globalQ.mouseProcs[globalQ.tail] _ callBack; globalQ.eventProcs[globalQ.tail] _ NIL; globalQ.tail _ (globalQ.tail + 1) MOD globalQ.size; LogReceived [event: NARROW[inputAction.first], point: worldPt, bash: FALSE]; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; }; QueueInputActionNoPoint: PUBLIC ENTRY PROC [callBack: EventProc, inputAction: LIST OF REF ANY, gargoyleData: GargoyleData] = TRUSTED { <> globalQ.gargoyleData _ gargoyleData; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; globalQ.actions[globalQ.tail] _ inputAction; globalQ.points[globalQ.tail] _ [0.0, 0.0]; globalQ.mouseProcs[globalQ.tail] _ NIL; globalQ.eventProcs[globalQ.tail] _ callBack; globalQ.tail _ (globalQ.tail + 1) MOD globalQ.size; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; }; QueueOrBashInputActionNoPoint: PUBLIC ENTRY PROC [callBack: EventProc, inputAction: LIST OF REF ANY, gargoyleData: GargoyleData] = TRUSTED { <> prevAction: ATOM _ PeekPaintActionTail[globalQ]; globalQ.gargoyleData _ gargoyleData; IF prevAction = NARROW[inputAction.first, ATOM] THEN { globalQ.actions[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ inputAction; globalQ.points[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ [0.0, 0.0]; globalQ.mouseProcs[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ NIL; globalQ.eventProcs[(globalQ.tail - 1 + globalQ.size) MOD globalQ.size] _ callBack; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; RETURN; }; WHILE (globalQ.tail+1) MOD globalQ.size = globalQ.head DO WAIT NotFull ENDLOOP; globalQ.actions[globalQ.tail] _ inputAction; globalQ.points[globalQ.tail] _ [0.0, 0.0]; globalQ.mouseProcs[globalQ.tail] _ NIL; globalQ.eventProcs[globalQ.tail] _ callBack; globalQ.tail _ (globalQ.tail + 1) MOD globalQ.size; IF globalProcess = NIL THEN Process.Detach[globalProcess _ FORK Painter]; }; DeQueuePaintAction: PRIVATE 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.gargoyleData; q.head _ (q.head + 1) MOD q.size; LogActed[inputAction, worldPt, mouseEventProc#NIL]; }; NotFull: CONDITION; EmptyQ: PROC [q: ActionQueue] RETURNS [BOOL] = { RETURN[q.tail = q.head]; }; PeekPaintActionHead: PRIVATE PROC [q: ActionQueue] RETURNS [whatToPaint: ATOM] = { IF q.tail = q.head THEN RETURN[NIL]; whatToPaint _ NARROW[q.actions[q.head].first, ATOM]; }; PeekPaintActionTail: PRIVATE PROC [q: ActionQueue] RETURNS [whatToPaint: ATOM] = { IF q.head = q.tail THEN RETURN[NIL]; whatToPaint _ NARROW[q.actions[(q.tail - 1 + q.size) MOD q.size].first, ATOM]; }; Restart: PUBLIC ENTRY PROC [] = TRUSTED { <> Process.Detach[globalProcess _ FORK Painter]; }; Notify: ENTRY PROC [] = { <> }; Painter: 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] _ DeQueuePaintAction[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. <> <> <<>>