FeedbackDoc.tioga
Copyright Ó 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Bier, August 31, 1992 2:53 pm PDT
Last tweaked by Mike Spreitzer on May 3, 1990 11:10:01 am PDT
Feedback
Cedar10.1 — FOR INTERNAL XEROX USE ONLY
Feedback
Redirectable, Delayable, Print Statements
Eric Bier
© Copyright 1989, 1990, 1991, 1992 Xerox Corporation. All rights reserved.
Abstract: This is a package that you will want to use instead of directly using MessageWindow or Typescripts when you want to communicate a line of text to the user. The SimpleFeedback interface provides output capability to clients that don't need to worry about where it goes; the Feedback interface is for clients that also want to direct where their output goes. Feedback.PutF uses all of the formatting power of IO.PutF, and sends your message to a lightweight data structure, called a MsgRouter. Using another interface, FeedbackOps.mesa (in FeedbackOps.df), you can wire up the MsgRouter to send its output to the MessageWindow, a Typescript, a Labels.Label, an IO.STREAM, or some combination of these. The MsgRouter can be configured to send some messages (e.g., error messages) to one place or places, while sending other messages (e.g., informational messages) to another place or places. Finally, the MsgRouter will store messages for later display. Thus, it is possible to use SimpleFeedback.mesa in the startup code for the Viewers package, and display warning messages once Viewers is finished initializing.
Created by: Eric Bier
Maintained by: Eric <Bier.pa>
Keywords: Print Statements, routing, MsgRouter, feedback, MessageWindow, Typescript
XEROX  Xerox Corporation
   Palo Alto Research Center
   3333 Coyote Hill Road
   Palo Alto, California 94304

For Internal Xerox Use Only
1. Using Feedback
The Feedback Idea
The main idea of the Feedback package is to let you send text messages to a generic object, called a router, instead of to a particular place, like the Cedar MessageWindow or the Cedar System Script. The advantage of using Feedback, is that if you change your mind about how feedback should be displayed (or if you wish to port your package to a new environment that doesn't have the same methods for displaying feedback), you have only to change the few lines of code that say where the router should display text messages and your old package will work. Putting it another way, you make your code less dependent on Viewers.
There are two ways to get to a router when you need one. The easiest way is to send your text to a named router, using the SimpleFeedback interface. If you use a name Feedback hasn't heard of before, a new router is created automatically. By default, new routers send their output somewhere sensible, as determined for instance by FeedbackInitialViewersImpl.
Applications that wish to send output to the region of a window, need a router per window. These applications should create routers explicitly using Feedback.CreateRouter and store this router in a per-window data structure for later use.
Getting Started
To get started using Feedback, you can call Feedback.CreateRouter[] to create a router. Pass this router to Feedback.Append or Feedback.PutF, when you send your output.
The msgType argument of Append and PutF tells Feedback when adding carriage returns will be useful in typescripts, and when clearing the MessageWindow or Labels.Label is a good idea.
The msgClass argument of Append and PutF is your way of identifing the type of a message. Do you think of it as an $Error message, a $Debug message, a $DuringMouse message. By providing this argument, you make it possible to direct one class of messages to one output place, while directing another class of messages to a different output place.
Wiring up your MsgRouter
Use the FeedbackOps interface, or Feedback.SetHandler, to wire up various message classes of a given MsgRouter to various output streams (some of which can be found in FeedbackClasses).
2. Some Examples
Perhaps the most effective way to learn to use the Feedback package is from some examples. In this section, we describe how Feedback is used by several packages. More examples can be found by looking at /R/Cedar.depends in the sections listing clients of FeedbackOps and FeedbackClasses.
2.1 Use from Gargoyle
The Gargoyle illustrator uses a distinct router for each Gargoyle window. Thus, as part of creating a new window, GGMenuImpl.FeedbackLineInGGData calls:
router: MsgRouter ¬ Feedback.CreateRouter[];
FeedbackOps.SetMultiLabel[router, button, TRUE, LIST[$Error, $Complaint]]; -- blink
FeedbackOps.SetMultiLabel[router, button, FALSE, LIST[$DuringMouse, $Feedback, $Warning, $Confirm, $Show, $Statistics]];
FeedbackOps.SetMultiTypescript[router, $Gargoyle, LIST[$Error, $Warning, $Show, $Typescript, $Complaint, $Statistics]];
ggData.router ¬ router;
The first line creates a new router. The next line asserts that messages of class $Error and $Complaint should be displayed in the Gargoyle window's feedback line, and should cause the line to blink when displayed. The next line asserts that messages of class $DuringMouse, $Feedback, $Warning, $Confirm, $Show, and $Statistics should be displayed in the feedback line, but should not cause it to blink. The final line asserts that messages of class $Error, $Warning, $Show, $Typescript, $Complaint, and $Statistics should be displayed in the Typescript whose name is $Gargoyle. Note that many of these messages also go to the feedback line. This is fine, Feedback will route the messages to both places.
While there can be many Gargoyle windows, there is only one Gargoyle typescript. This typescript is created in GGWindowImpl.CreateWindowAux, with the line:
[] ¬ FeedbackOps.CreateNamedTypescript["Gargoyle Typescript", $Gargoyle, 120];
Note that while this routine is called whenever a Gargoyle window is created, if the Gargoyle typescript already exists, no new typescript is produced.
2.2 Use from BiScrollers
Many packages, including BiScrollers, used the Feedback package only by using the SimpleFeedback interface. For example, when BiScrollers has an error message to display, it calls:
SimpleFeedback.Append[$BiAxials, oneLiner, $Error, Rope.Concat["File create error: ", error.explanation]];
Feedback automatically creates a router name $BiAxials, when this routine is first called.
2.3 Creating a Stream from a Router: Druid
The Druid breakpoint tool creates a default router (which sends to the MessageWindow and System Script Viewers worlds) and then creates a stream from it and passes the stream around. This doesn't Druid control over how different kind of messages are displayed (they are all displayed in the same way), but is very simple. The relevant call is:
displayHandle.feedbackStream ¬ FeedbackClasses.CreateStreamOnRouter[
msgRouter: Feedback.CreateRouter[], msgClass: $Default];
Druid then passes around the resulting stream.
2.4 Use for the UNIX Console
CedarConsoleImpl.Start contains the lines:
console.router ¬ Feedback.CreateRouter[];
[] ¬ Feedback.RegisterRouter[console.router, $Console];
console.stopped ¬ FALSE;
TRUSTED {
console.process ¬ FORK CopyConsoleToFeedback[];
Process.Detach[console.process];
};
This code creates a router than can be referred to both as a REF, console.router, or as a name, $Console. Usually a router is accessible one way or another, but not both. The routine CopyConsoleToFeedback includes calls to Feedback.Append to send messages to the new router.
Note that this code does not specify where this router will put input, so this router uses the default message handlers, which are described below.
2.5 The Default Handlers
The default handlers for all routers are set by various routines as a Cedar world is being initialized. For example, in any Cedar world, when the Feedback package is run, FeedbackInitialCedarImpl.Start sets the default behavior for all routers to send text both to the stream "stdout" and to memory (from which it can be played back later).
Later on, in a Viewers-based Cedar world, MessageWindowImpl is run, directing default routers to the MessageWindow and to memory with this routine:
StartFeedback: PROC = {
h1: Feedback.MsgHandler ~ FeedbackClasses.CreateHandler[MWPutFL, MWClearHerald, MWBlink, NIL];
h2: Feedback.MsgHandler ~ FeedbackClasses.CreateStoringHandler[2000];
h12: Feedback.MsgHandler ~ FeedbackClasses.CreateSplittingHandler[h1, h2];
[] ¬ Feedback.SetGlobalDefaultHandlersBehavior[h12];
};
Even later on, after Tioga is loaded, the FeedbackOps package will be run, calling FeedbackInitialViewersImpl.Start, which creates the SystemScript, and sets the default behavior to show all messages in both the MessageWindow and the SystemScript, with the lines:
ts: FeedbackOps.Viewer ~ FeedbackOps.CreateNamedTypescript[headerText: sysScriptName, typescriptName: sysScriptAtom, storing: TRUE].typescript;
new: Feedback.MsgHandler ~ FeedbackOps.CreateViewersHandler[sysScriptAtom, FeedbackOps.messageWindow, FALSE];
old: Feedback.MsgHandler ~ Feedback.SetGlobalDefaultHandlersBehavior[new];
This routine also plays back any stored messages into the new router.