<<>> <> <> <> <> <> <<>> DIRECTORY Feedback, FeedbackClasses, FeedbackConcreteTypes, FeedbackOps, FeedbackTypes, IO, Labels, MessageWindow, Process, RefTab, Rope, ViewerClasses, ViewerIO, ViewerOps, ViewerPrivate; FeedbackOpsImpl: CEDAR MONITOR LOCKS script USING script: Script IMPORTS Feedback, FeedbackClasses, IO, Labels, MessageWindow, Process, RefTab, Rope, ViewerIO, ViewerOps, ViewerPrivate EXPORTS FeedbackOps = BEGIN OPEN Feedback; Viewer: TYPE = ViewerClasses.Viewer; LOR: TYPE ~ LIST OF ROPE; <> <<>> gTypescripts: RefTab.Ref _ RefTab.Create[]; -- all of the named typescripts that FeedbackOps knows about Script: TYPE = REF ScriptObj; ScriptObj: TYPE = MONITORED RECORD [ viewer: Viewer, -- viewer can be NIL, if stream does not belong to a viewer stream: IO.STREAM, typescriptName: ATOM, storing: BOOL, queueHead, queueTail: FList _ NIL, --list of PutF tasks to do putter: PROCESS _ NIL, --the process, if any, doing PutFs change: CONDITION ]; FList: TYPE ~ REF FCons; FCons: TYPE ~ RECORD [ rest: FList, first: SELECT kind: * FROM blink => [], putf => [format: Rope.ROPE, v1, v2, v3, v4, v5: IO.Value], putc => [c: CHAR], ENDCASE]; blinkF: FCons ~ [NIL, blink[]]; CreateNamedTypescript: PUBLIC PROC [headerText: Rope.ROPE, typescriptName: ATOM, openHeight: NAT _ 120, storing: BOOL _ FALSE] RETURNS [alreadyExists: BOOL _ FALSE, typescript: Viewer] = { newStream: IO.STREAM; script: Script _ FindScript[typescriptName]; IF script=NIL OR script.viewer=NIL OR script.viewer.destroyed THEN { typescript _ ViewerOps.CreateViewer[ flavor: $TypeScript, info: [name: headerText, menu: NIL, data: NIL, iconic: TRUE, column: right, scrollable: TRUE, icon: unInit], paint: FALSE]; ViewerOps.SetOpenHeight[typescript, openHeight]; ViewerOps.OpenIcon[icon: typescript, closeOthers: FALSE, bottom: TRUE, paint: TRUE]; [, newStream] _ ViewerIO.CreateViewerStreams[headerText, typescript, NIL, TRUE]; IF script=NIL THEN { script _ NEW[ScriptObj _ [viewer: typescript, stream: newStream, typescriptName: typescriptName, storing: storing]]; TRUSTED {Process.InitializeCondition[@script.change, Process.MsecToTicks[1D4]]}; [] _ gTypescripts.Store[typescriptName, script]; } ELSE SetStuff[script, newStream, typescript, storing]; } ELSE { -- nothing wrong with the current typescript alreadyExists _ TRUE; typescript _ script.viewer}; }; CreateTypescriptFromStream: PUBLIC PROC [stream: IO.STREAM, typescriptName: ATOM, storing: BOOL _ FALSE] RETURNS [alreadyExists: BOOL _ FALSE, oldStream: IO.STREAM _ NIL] = { script: Script _ FindScript[typescriptName]; IF script=NIL THEN { newScript: Script _ NEW[ScriptObj _ [viewer: NIL, stream: stream, typescriptName: typescriptName, storing: storing]]; TRUSTED {Process.InitializeCondition[@script.change, Process.MsecToTicks[1D4]]}; [] _ gTypescripts.Store[typescriptName, newScript]; } ELSE { alreadyExists _ TRUE; oldStream _ script.stream; SetStuff[script, stream, NIL, storing]; }; }; FindScript: PROC [typescriptName: ATOM] RETURNS [script: Script _ NIL] = { found: BOOL _ FALSE; val: REF; [found, val] _ gTypescripts.Fetch[typescriptName]; IF found THEN script _ NARROW[val]; }; GetTypescriptStream: PUBLIC PROC [typescriptName: ATOM] RETURNS [IO.STREAM] = { script: Script _ FindScript[typescriptName]; IF script = NIL THEN RETURN[NIL]; RETURN[script.stream]; }; GetTypescripts: PUBLIC PROC [] RETURNS [names: LIST OF ATOM] = { AddToList: PROC [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE] = { names _ CONS[NARROW[key, ATOM], names]; }; [] _ gTypescripts.Pairs[AddToList]; }; <> FeedbackData: TYPE = REF FeedbackDataObj; FeedbackDataObj: TYPE = RECORD [ lp: RECORD [label: Viewer, blink: BOOL] _ [NIL, FALSE], tn: ATOM _ NIL, default: FeedbackData _ NIL, bounded: BOOL _ TRUE ]; <> messageWindow: PUBLIC Viewer ~ ViewerPrivate.messageWindow; noLabel: Viewer ~ NEW [ViewerClasses.ViewerRec]; Iv: PROC [v: Viewer] RETURNS [Viewer] ~ INLINE {RETURN [IF v=NIL THEN noLabel ELSE v]}; Ov: PROC [v: Viewer] RETURNS [Viewer] ~ INLINE {RETURN [IF v=noLabel THEN NIL ELSE v]}; It: PROC [a: ATOM] RETURNS [ATOM] ~ INLINE {RETURN [IF a=NIL THEN $None ELSE a]}; Ot: PROC [a: ATOM] RETURNS [ATOM] ~ INLINE {RETURN [IF a=$None THEN NIL ELSE a]}; CreateViewersHandler: PUBLIC PROC [typescriptName: ATOM _ $None, label: Viewer _ NIL, blink: BOOL _ FALSE] RETURNS [MsgHandler] ~ { fd: FeedbackData ~ NEW [FeedbackDataObj _ [[Iv[label], blink], typescriptName]]; IF label=NIL THEN label _ noLabel; RETURN FeedbackClasses.CreateHandler[VPutF, VClearHerald, VBlink, fd]}; IsViewersHandler: PUBLIC PROC [mh: MsgHandler] RETURNS [is: BOOL, typescriptName: ATOM _ $None, label: Viewer _ NIL, blink: BOOL _ FALSE] ~ { WITH FeedbackClasses.GetHandlerData[mh] SELECT FROM fd: FeedbackData => RETURN [TRUE, fd.tn, Ov[fd.lp.label], fd.lp.blink]; ENDCASE => RETURN [FALSE]}; HandleToLabel: PUBLIC PROC [vh: MsgHandler, label: Viewer, blink: BOOL _ FALSE] ~ { WITH FeedbackClasses.GetHandlerData[vh] SELECT FROM fd: FeedbackData => fd.lp _ [Iv[label], blink]; ENDCASE => ERROR Problem["not a Viewers handler"]}; HandleToTypescript: PUBLIC PROC [vh: MsgHandler, typescriptName: ATOM _ $None] ~ { WITH FeedbackClasses.GetHandlerData[vh] SELECT FROM fd: FeedbackData => fd.tn _ typescriptName; ENDCASE => ERROR Problem["not a Viewers handler"]}; SetFD: PROC [router: MsgRouter, msgClass: MsgClass, label: Viewer, blink: BOOL, tn: ATOM] = { mh: MsgHandler; dh: MsgHandler _ GetHandler[router, $Default]; dd: FeedbackData _ IF dh=NIL THEN NIL ELSE WITH FeedbackClasses.GetHandlerData[dh] SELECT FROM x: FeedbackData => x, ENDCASE => NIL; IF msgClass=$Every OR msgClass=$Default THEN { e: BOOL ~ msgClass=$Every; toclear: LIST OF MsgClass _ NIL; PerDirection: PROC [msgClass: MsgClass, msgHandler: MsgHandler] RETURNS [BOOL] ~ { WITH FeedbackClasses.GetHandlerData[msgHandler] SELECT FROM x: FeedbackData => IF e THEN { IF label#NIL THEN x.lp _ [label, blink]; IF tn#NIL THEN x.tn _ tn} ELSE x.default _ dd; ENDCASE => IF e THEN toclear _ CONS[msgClass, toclear]; RETURN [FALSE]}; IF dd#NIL THEN { IF label#NIL THEN dd.lp _ [label, blink]; IF tn#NIL THEN dd.tn _ tn} ELSE { dd _ NEW [FeedbackDataObj _ [[Iv[label], blink], It[tn]]]; dh _ FeedbackClasses.CreateHandler[VPutF, VClearHerald, VBlink, dd]; [] _ SetHandler[router, $Default, dh]}; IF ScanHandlers[router, PerDirection].stopped THEN ERROR; SetMultiHandler[router, toclear, NIL]; RETURN}; mh _ GetHandler[router, msgClass]; IF mh#NIL THEN WITH FeedbackClasses.GetHandlerData[mh] SELECT FROM x: FeedbackData => { IF label#NIL THEN x.lp _ [label, blink]; IF tn#NIL THEN x.tn _ tn; RETURN}; ENDCASE => NULL; mh _ FeedbackClasses.CreateHandler[VPutF, VClearHerald, VBlink, NEW [FeedbackDataObj _ [[label, blink], tn, dd]]]; [] _ SetHandler[router, msgClass, mh]; RETURN}; GetFD: PROC [router: MsgRouter, msgClass: MsgClass] RETURNS [fd: FeedbackData] = { mh: MsgHandler _ GetHandler[router, msgClass]; IF mh#NIL THEN WITH FeedbackClasses.GetHandlerData[mh] SELECT FROM x: FeedbackData => RETURN [x]; ENDCASE => NULL; RETURN [NIL]}; <> <<>> SetLabel: PUBLIC PROC [router: MsgRouter, label: Viewer, blink: BOOL _ FALSE, msgClass: MsgClass _ $Every] = { SetFD[router, msgClass, Iv[label], blink, NIL]; }; SetMultiLabel: PUBLIC PROC [router: MsgRouter, label: Viewer, blink: BOOL _ FALSE, msgClasses: LIST OF ATOM] = { FOR list: LIST OF ATOM _ msgClasses, list.rest UNTIL list = NIL DO SetLabel[router, label, blink, list.first]; ENDLOOP; }; SetMessageWindow: PUBLIC PROC [router: MsgRouter, blink: BOOL _ FALSE, msgClass: MsgClass _ $Every] = { SetFD[router, msgClass, messageWindow, blink, NIL]; }; SetMultiMessageWindow: PUBLIC PROC [router: MsgRouter, blink: BOOL _ FALSE, msgClasses: LIST OF ATOM] = { FOR list: LIST OF ATOM _ msgClasses, list.rest UNTIL list = NIL DO SetMessageWindow[router, blink, list.first]; ENDLOOP; }; <<>> SetTypescript: PUBLIC PROC [router: MsgRouter, typescriptName: ATOM _ $None, msgClass: MsgClass _ $Every] = { SetFD[router, msgClass, NIL, FALSE, typescriptName]; }; SetMultiTypescript: PUBLIC PROC [router: MsgRouter, typescriptName: ATOM _ $None, msgClasses: LIST OF ATOM] = { FOR list: LIST OF ATOM _ msgClasses, list.rest UNTIL list = NIL DO SetTypescript[router, typescriptName, list.first]; ENDLOOP; }; CreateSimpleRouter: PUBLIC PROC [label: Viewer, blink: BOOL _ FALSE, typescriptName: ATOM] RETURNS [router: MsgRouter] = { router _ CreateRouter[]; SetLabel[router, label, blink, $Every]; SetTypescript[router, typescriptName, $Every]; }; CreateMultiFeedback: PUBLIC PROC [label: Viewer, blink: BOOL _ FALSE, labelClasses: LIST OF ATOM, typescriptName: ATOM, typescriptClasses: LIST OF ATOM] RETURNS [router: MsgRouter] = { router _ CreateRouter[]; SetMultiLabel[router, label, blink, labelClasses]; SetMultiTypescript[router, typescriptName, typescriptClasses]; }; <> <<>> GetLabel: PUBLIC PROC [router: MsgRouter, msgClass: MsgClass _ $Default] RETURNS [label: Viewer, blink: BOOL _ FALSE] = { feedback: FeedbackData ~ GetFD[router, msgClass]; IF feedback#NIL THEN RETURN [Ov[feedback.lp.label], feedback.lp.blink]; RETURN [NIL]}; GetTypescriptName: PUBLIC PROC [router: MsgRouter, msgClass: MsgClass _ $Default] RETURNS [typescriptName: ATOM _ $None] = { feedback: FeedbackData ~ GetFD[router, msgClass]; IF feedback#NIL THEN RETURN [feedback.tn]; RETURN}; <> <<>> VPutF: PROC [mh: MsgHandler, msgType: MsgType, msgClass: MsgClass, format: Rope.ROPE, v1, v2, v3, v4, v5: IO.Value _ [null[]] ] ~ { fd: FeedbackData ~ NARROW[FeedbackClasses.GetHandlerData[mh]]; lfd: FeedbackData ~ IF fd.lp.label#NIL OR fd.default=NIL THEN fd ELSE fd.default; tfd: FeedbackData ~ IF fd.tn#NIL OR fd.default=NIL THEN fd ELSE fd.default; lcf: BOOL ~ typeBreaksAt[msgType].begin OR lfd.bounded; tcf: BOOL ~ typeBreaksAt[msgType].begin OR tfd.bounded; msg: Rope.ROPE ~ IO.PutFR[format, v1, v2, v3, v4, v5]; IF lfd.lp.label=noLabel OR lfd.lp.label=NIL THEN NULL ELSE IF lfd.lp.label=messageWindow THEN { MessageWindow.Append[msg, lcf]; IF lfd.lp.blink THEN MessageWindow.Blink[]} ELSE { Labels.Set[lfd.lp.label, IF lcf THEN msg ELSE Rope.Concat[NARROW[lfd.lp.label.class.get[lfd.lp.label]], msg]]; IF lfd.lp.blink THEN BlinkLabel[lfd.lp.label]}; IF tfd.tn # $None THEN PutFToTypescript[tfd.tn, IF tcf THEN Rope.Concat["\n", format] ELSE format, v1, v2, v3, v4, v5]; lfd.bounded _ tfd.bounded _ typeBreaksAt[msgType].end; }; PutFToTypescript: PROC [typescriptName: ATOM, format: Rope.ROPE, v1, v2, v3, v4, v5: IO.Value _ [null[]] ] ~ { script: Script _ FindScript[typescriptName]; IF script=NIL THEN RETURN; IF format.Length[]=1 AND v1=[null[]] THEN Enqueue[script, [NIL, putc[format.Fetch[0]]]] ELSE Enqueue[script, [NIL, putf[format, v1, v2, v3, v4, v5]]]; RETURN}; Enqueue: ENTRY PROC [script: Script, fp: FCons] ~ { ENABLE UNWIND => NULL; IF script.stream=NIL AND NOT script.storing THEN RETURN; {this: FList ~ NEW [FCons _ fp]; IF script.queueHead=NIL THEN script.queueHead _ this ELSE script.queueTail.rest _ this; script.queueTail _ this; IF script.stream=NIL THEN NULL ELSE IF script.putter=NIL THEN TRUSTED {Process.Detach[script.putter _ FORK Putter[script]]} ELSE NOTIFY script.change; RETURN}}; Deque: TYPE ~ RECORD [fh: FList, stream: IO.STREAM, sv: Viewer, quit: BOOL]; Dequeue: ENTRY PROC [script: Script] RETURNS [Deque] ~ { ENABLE UNWIND => NULL; RETURN InnerDequeue[script]}; InnerDequeue: INTERNAL PROC [script: Script] RETURNS [Deque] ~ { n: INTEGER _ 2; UNTIL script.queueHead#NIL AND script.stream#NIL DO IF (n _ n.PRED) < 0 THEN { script.putter _ NIL; RETURN [[NIL, NIL, NIL, TRUE]]}; WAIT script.change; ENDLOOP; {fh: FList ~ script.queueHead; script.queueHead _ script.queueHead.rest; fh.rest _ NIL; IF script.queueHead=NIL THEN script.queueTail _ NIL; RETURN [[fh, script.stream, script.viewer, FALSE]]}}; ClearStreamAndRequeue: ENTRY PROC [script: Script, bad: IO.STREAM, fh: FList] ~ { ENABLE UNWIND => NULL; IF fh.rest # NIL THEN ERROR; IF script.stream=bad THEN script.stream _ NIL; fh.rest _ script.queueHead; script.queueHead _ fh; IF script.queueTail=NIL THEN script.queueTail _ script.queueHead; RETURN}; SetStuff: ENTRY PROC [script: Script, stream: IO.STREAM, sv: Viewer, storing: BOOL] ~ { script.stream _ stream; script.viewer _ sv; script.storing _ storing; IF script.stream=NIL THEN NULL ELSE IF script.putter=NIL THEN TRUSTED {Process.Detach[script.putter _ FORK Putter[script]]} ELSE NOTIFY script.change; RETURN}; Putter: PROC [script: Script] ~ { <> <> InnerPut[script !UNWIND => script.putter _ NIL]; RETURN}; InnerPut: PROC [script: Script] ~ { DO dq: Deque ~ Dequeue[script]; IF dq.quit THEN RETURN; InnerInnerPut[script, dq]; ENDLOOP}; InnerInnerPut: PROC [script: Script, dq: Deque] ~ { WITH dq.fh SELECT FROM x: REF blink FCons => IF dq.sv#NIL AND NOT dq.sv.destroyed THEN ViewerOps.BlinkViewer[dq.sv]; x: REF putf FCons => dq.stream.PutF[x.format, x.v1, x.v2, x.v3, x.v4, x.v5 ! IO.Error => { IF ec#StreamClosed THEN { MessageWindow.Append[IO.PutFR["FeedbackImpl: IO Err at ts %g for fmt %g", [atom[script.typescriptName]], [rope[x.format]] ]]; MessageWindow.Blink[]; } ELSE ClearStreamAndRequeue[script, stream, dq.fh]; CONTINUE; }]; x: REF putc FCons => dq.stream.PutChar[x.c ! IO.Error => { IF ec#StreamClosed THEN { MessageWindow.Append[IO.PutFR["FeedbackImpl: IO Err at ts %g for PutChar", [atom[script.typescriptName]] ]]; MessageWindow.Blink[]; } ELSE ClearStreamAndRequeue[script, stream, dq.fh]; CONTINUE; }]; ENDCASE => ERROR; RETURN}; VClearHerald: PROC [mh: MsgHandler, msgClass: MsgClass] ~ { fd: FeedbackData _ NARROW[FeedbackClasses.GetHandlerData[mh]]; IF fd.lp.label=NIL AND fd.default#NIL THEN fd _ fd.default; IF fd.lp.label=NIL OR fd.lp.label=noLabel THEN NULL ELSE IF fd.lp.label=messageWindow THEN MessageWindow.Clear[] ELSE Labels.Set[fd.lp.label, NIL]; RETURN}; VBlink: PROC [mh: MsgHandler, msgClass: MsgClass] ~ { fd: FeedbackData _ NARROW[FeedbackClasses.GetHandlerData[mh]]; IF fd.lp.label=NIL AND fd.default#NIL THEN fd _ fd.default; IF fd.lp.label=NIL OR fd.lp.label=noLabel THEN { IF fd.tn#$None THEN { script: Script ~ FindScript[fd.tn]; IF script#NIL THEN Enqueue[script, blinkF] } } ELSE IF fd.lp.label=messageWindow THEN MessageWindow.Blink[] ELSE BlinkLabel[fd.lp.label]; RETURN}; BlinkLabel: PROC [label: Viewer] = { Labels.SetDisplayStyle[label, $WhiteOnBlack]; Process.Pause[Process.MsecToTicks[150]]; Labels.SetDisplayStyle[label, $BlackOnWhite]; Process.Pause[Process.MsecToTicks[150]]; Labels.SetDisplayStyle[label, $WhiteOnBlack]; Process.Pause[Process.MsecToTicks[150]]; Labels.SetDisplayStyle[label, $BlackOnWhite]; }; END.