(* Copyright 1992 Digital Equipment Corporation. *) (* Distributed only by permission. *) (* Last modified on Sun Jul 19 20:41:52 PDT 1992 by johnh*) (* This file collects the portions of the Zeus and ZeusPanel interfaces that are relevant for a Zeus client-programmer, someone who is using Zeus to animate an algorithm. This is not an interface itself; you should not try to call ZeusClient.. It is provided to simplify the task of learning to use Zeus for animation. *) INTERFACE Zeus; (* Main interface for Zeus applications. Zeus is a kernel for algorithm animation systems. To a first approximation, it follows the spirit of BALSA, but implemented in a multi-threaded environment using an object-oriented, strongly-typed language. Index: Zeus; algorithm animation; multi-view editing; graphics *) IMPORT Algorithm, List, Thread, View, ZeusClass; (* Overview... Zeus provides a domain-independent framework and methodology for associating multiple client-defined "views" and "algorithms" with sets of client-defined "events." A view is a View.T, and an algorithm is an Algorithm.T, each with additional suites of procedures to handle client-defined events. The appropriate event-handling procedure is invoked by Zeus in response to an event generated by an algorithm or view. There is only one algorithm per Zeus.Session; it acts as the "server" for event distribution: events that originate in the algorithm are distributed to all views, and events that originate in a view are distributed to the algorithm. Clients who use Zeus for algorithm animation should use the ZeusPanel interface. That interface provides a "control panel" in a Trestle window. The control panel allows the user to choose algorithms and views, and to control the execution of algorithms. It also provides a very simple client-programmer interface. In algorithm animation, a subroutine associated with the algorithm is invoked to generate events. When using the ZeusPanel inteface, the user can single-step or suspend the execution in terms of events generated by algorithms. *) TYPE (* Access to a Session is controlled by a reader/writer lock. *) Session <: PublicSession; PublicSession = ROOT OBJECT alg : Algorithm.T := NIL; views: List.T (* of View.T *) := NIL; METHODS init (): Session; pre (initiator: ZeusClass.T; style : EventStyle; priority : INTEGER; t : TEXT ) RAISES {Thread.Alerted}; post (initiator: ZeusClass.T; style : EventStyle; priority : INTEGER; t : TEXT ) RAISES {Thread.Alerted}; END; (* ZeusPanel uses priority for determining event for Stepping; it's likely that other clients of Zeus will not need to use it. *) CONST MaxPriority = 9; TYPE EventStyle = {Output, Update, Edit, Notify, Broadcast, Code}; (* About events... There are events that go from an algorithm to all views, and events from a view to the algorithm. There's also a special event, called a Broadcast event, that goes first to the algorithm, and then to all views. There are three types of events that go from an algorithm to views: Output, Update, and Code. The Output events and Code events are for algorithm animations that utilize the ZeusPanel. The ZeusPanel will gain control before and after each event, in order to implement Stepping, Stopping, and Resuming. Code events are used, along with the ZeusCodeView interface, for textual display of the program that the algorithm is executing. Update events bypass the ZeusPanel. They are used for responding to events from a view, usually to dispatch information to all other views. There are two types of events that go from a view to its algorithm: Edit and Notify. Notify events are used in algorithm animation; Edit events are not. Whenever a view interprets some user gestures into an action, it generates either an Edit or Notify event to the algorithm. The algorithm makes the appropriate changes to its data structures, and then generates an Update event to the views. The views update themselves based on these events. (In response to an Edit, Notify, or Update event, the event handler can inquire which view initiated the editing action.) Each Zeus instance has an editing lock. The lock prevents other views from issuing Edit events while another view is in the midst of a command that requires multiple Edit events. When an Edit event is attempted while the editing lock is held by some other view, an exception is raised. The Notify event should be used to bypass the editing lock (for example, for a view to set a selection). Here's a summary of the six types of events in Zeus: * Output: alg -> views, with ZeusPanel intervention * Code: alg -> views (ZeusCodeView only), with ZeusPanel intervention * Update: alg -> views, bypassing ZeusPanel (responding to Edit/Notify) * Edit: view -> alg, checking the edit lock * Notify: view -> alg, bypassing the edit lock * Broadcast: view -> alg, bypassing the edit lock alg -> views, bypassing ZeusPanel *) (* About concurrency and locking... Most of the procedures in this interface are called when VBT.mu is held. That is, they are intended to be called in reponse to some user mouse or keyboard activity. Although not documented as such, they may not be called concurrently. A notable exception is that Output events are not necessarily generated with LL=VBT.mu, and it isn't reasonable to lock the window system on each event. Consequently, this interface does its own locking to ensure that ChangeAlg, AttachView, DetachView, and Destroy cannot be entered while there is an event (an event of any flavor) in progress; conversely, calls to ChangeAlg, AttachView, DetachView, or Destroy are serialized, and will block any call to Dispatch. While on the subject of locking, it's important to keep in mind that repaint and reformat requests arrive from the window system asynchronously with respect to the executing algorithm. (They do happen with VBT.mu locked by some ancestor, however.) It's the responsibility of each view to synchronize its repaint and reformat requests with Output events that it processes. If a view uses an algorithm's data structures (as opposed to local copies), it must synchronize its access to these structures in its repaint and reformat procedures. Multi-threaded algorithms require a bit more work. By design, Zeus does not serialize any of the Output events. Therefore, multiple Output events may happen simultaneously, while other threads in the algorithm are still executing. The view must be careful to coordinate access to both the data structures it shares with the algorithm and the data structures that various event handlers share. *) (* **** Dispatching Events **** *) (* Dispatch() is called by the IE procs that m3zume generates automatically. This comment is included here in case you need to look at that code. *) EXCEPTION Error(REFANY); (* This exception should be raised by an event handling procedure to report back to the initiator of the event. Although Update and Output events invoke the event handlers of multiple views, it reports back to the algorithm a single exception, chosen non-deterministically from among all views that raised the exception. *) EXCEPTION Locked(TEXT); (* Used by Dispatch to report that the editing lock is held by a view other than the one initiating the editing event. *) TYPE DispatchProc = PROCEDURE (z: ZeusClass.T; args: REFANY); PROCEDURE Dispatch (initiator : ZeusClass.T; style : EventStyle; priority : INTEGER; eventName : TEXT; dispatchProc: DispatchProc; evtArgs : REFANY ) RAISES {Error, Locked, Thread.Alerted}; (* Dispatch is called by an IE routine, the body of which was generated by the zume preprocessor. The initiator and style are provided by the client as arguments to the IE; the other arguments are created by the IE routine iteself. The pre-dispatch callback will be called, if one has been registered. Then dispatchProc will be called for the appropriate ZeusClass.T's to invoke v with the specified evtArgs, properly unpackaged. Finally, the post-dispatch callback will be called, if one has been registered. Output events will be called with LL <= VBT.mu (i.e., VBT.mu may or may not be locked); Update, Edit, Notify, and Broadcast events will be called with LL = VBT.mu. *) PROCEDURE EventTime (zeus: Session): REAL; (* Views can synchronize time dependent reactions to the event by calling EventTime to get the elapsed time since the event was initiated. Views should assume that the unit of time is 1 second. However, there is a scale factor which determines the actual relationship between clock time and event time. *) PROCEDURE SetEventTimeScale(zeus: Session; scale: REAL); (* Sets the time scale factor. eventTime = scale * clockTime *) END Zeus. INTERFACE ZeusPanel; (* The Zeus control panel provides a simple programmer interface for building algorithm animations, and provides a user interface for controlling the the selection of algorithms and views, and the execution of algorithms. There is one control panel per Zeus address space, and the control panel itself is a Zeus "Session". *) IMPORT Algorithm, VBT, View; CONST StateDir = ".zeusState"; (* directory in which state is saved between invocations *) FinalState = "Final_State"; (* filename for state at "Quit" *) PROCEDURE Interact (); (* Called once, usually after all algorithms and views are registered and a title has been specified. Doesn't return until the user deletes the control panel. After installing a Zeus control panel in Trestle, tries to restore the state to the last time Zeus was exited (stored in State) and then awaits user commands. When the user deletes the control panel, tries to snapshot the state into StateDir & "/" & FinalState. *) PROCEDURE SetTitle (title: TEXT); (* Use title in the icon box and in the control panel's chassis. Typically called once, before Interact. However, the title can be changed dynamically (for example, to reflect the name of the current algorithm or input). *) TYPE NewAlgProc = PROCEDURE (): Algorithm.T; PROCEDURE RegisterAlg (proc : NewAlgProc; name : TEXT; algGroup: TEXT := "Generic"); (* Register an algorithm. "name" is the name of the algorithm. "algGroup" is the name of the algorithm group to which the algorithm belongs--the name of the .evt file is generally a good choice. "proc" is a NewAlgProc, a procedure that returns an initialized instance of the algorithm. This means that "proc" must call the init() method of the algorithm. *) TYPE NewViewProc = PROCEDURE (): View.T; PROCEDURE RegisterView (proc : NewViewProc; name : TEXT; algGroup: TEXT := "Generic"); (* Register an view. "name" is the name of the view. "algGroup" is the name of the algorithm group to which the view belongs--the name of the .evt file is generally a good choice. "proc" is a NewViewProc, a procedure that returns an initialized instance of the view. This means that "proc" must call the init() method of the view. *) PROCEDURE GetDelayTime (): REAL; (* Report what the delay time the user has set in the control panel. *) PROCEDURE IgnoreDelayTime (ignore: BOOLEAN := TRUE); (* Normally, ZeusPanel will pause after each event the amount of time specified by the user. If views are able to slow themselves down (as is the case when the Anim package is used), then ZeusPanel should not pause. *) PROCEDURE EventTime (): REAL; (* Views can synchronize time dependent reactions to the event by calling EventTime to get the elapsed time since the event was initiated. Views should assume that the unit of time is 1 second. However, there is a scale factor set in the panel which determines the actual relationship between clock time and event time. *) PROCEDURE GetPriority (): INTEGER; (* Report what priority the user has set in the control panel. *) PROCEDURE SetPriority (priority: INTEGER); (* Change the priority. Client algorithms can use this to cause events to be generated that will not stop for "Step" commands. To do so, the algorithm first retrieves the current priority, then lowers it (probably to 0), does some stuff, then restores the priority to its initial value. *) PROCEDURE ReportError (text: TEXT := NIL); (* Display the specified text as an error message in the control panel. *) END ZeusPanel.