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; 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. 4GGDrawProcessImpl.mesa Author: Eric Bier on June 7, 1985 3:12:16 pm PDT Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by Bier on January 6, 1986 10:38:56 pm PST Contents: A flexible setup, suggested by Scott McGregor, for processing mouse input as fast as you can (but no faster). I will use it to synchronize my mouse point processing with the mousepoint. If the processing algorithms become faster, this procedure will still do the right thing. Pier, December 6, 1985 10:07:00 am PST ActionDone: CONDITION; Add an action to the draw processes queue. This action will definitely be performed, in its turn. Process.Detach is an unsafe operation. Add an action to the draw processes 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 draw processes queue. This action will definitely be performed, in its turn. Add an action to the draw processes queue. If the previous action is of the same sort, then overwrite it instead of adding a new one (useful for testing Gravity). 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. Pier, November 25, 1985 2:17:26 pm PST changes to: CloseSessionLog NIL check Κ €˜Icode™Kšœ0™0Kšœ Οmœ1™