The last few months I have been working part-time on a GUI for Sather. The goals of this project are somewhat different from what people have been discussing here, i.e. the desire for abstractions which would allow interchangeability of windowing systems or even between displays and printers. Not wanting to get dragged into this, I've kept a low profile. But in the interest of keeping people informed, here's an overview of what's been done. Note that so far, everything is in Sather 0.2i. GOALS & CAVEATS My primary task is to build a GUI system for use by neural-network and related simulations written in Sather and running on a powerful compute-server attached to a Unix workstation. Of course, I'd like my system to be as general as possible, but not at the expense of this primary task (or of gobs of my time). Some consequences of this focus are: o Sticking with X windows is good enough. Nonetheless, X-dependence is completely hidden from the application programmer, and even the GUI's X-dependence is fairly localized. It shouldn't be too hard to port to another system (with similar capabilities), but it's not obvious to me how one could make the window system a selectable option without messing up the code a lot. So far I use only Xlib, Xt and the Athena widgets. o We need separate processes for the GUI and the application. This is obvious when the application runs on the compute-server, since the GUI must run on a console. However it also seems like the best solution for the single-workstation case, since compute- intensive simulations are not amenable to the wait-for-event/ quickly-process-it/wait-again style of single-process X applications. o Flexibility is not a priority. The current system is monochrome only, and does not allow anywhere near the customization that X itself provides. This is a blessing for someone who wants to easily and simply set up a functional interface but is not concerned deeply with making things "pretty". I doubt, though, if the "general public" would be very fond of this lack of choice. How to achieve unobtrusive flexibility is an open issue. GADGETS Basically, the GUI provides the application programmer with a set of "gadgets," which are Sather objects, some of which correspond to X widgets, while others are more high-level. After GUI initialization, applications create gadgets inside a single top-level window, then make calls on them to post new messages, plot values, etc. The initial set of gadgets is as follows: text gadget tty-style window, accumulates text message gadget displays a single message at a time plot gadget cumulative bar-graph ala xload hyperplane gadget a neural-net tool digraph gadget arbitrary graph viewer / node selector button gadget typical click-button scrollbar gadget usual scrollbar; reports position as % dialog gadget prompts for a string and returns it console gadget a quit button, TimeScroller (see below), pause button, and screen-dump button combo. USE Here's a short code fragment showing what you'd need to do to produce an plot of a sequence of real values between 0 and 10: GUI::init_with_console(true, 500, 500); pg: PLOT_GADGET := pg.create("My Plot", 10, 10, 100, 100); pg.scale(1.0, 0.0, 10.0); ... loop ... pg.post(val); GUI::inc_time; -- optional ... end; Note that the creation of the separate GUI process is transparent. (If we wanted to run the GUI on a separate machine we'd use a different GUI::init_ call which takes the machine name.) The GUI::inc_time call is not necessary, but if used, enables the user to "scroll" the plot gadget back to its state at any previous time. In fact, in the current system, a single "timescroller" adjusts all existing gadgets to their state at any past time. This feature does not intrude into application code in any way besides the need to announce the transitions between "significant" states. This feature is particularly useful for simulations. When using gadgets which collect input for the application, things are a little more complicated. A particular object must be specified to receive the input; it must define an appropriate routine (whose name depends on the type of gadget) to handle the input. Here's a simple example showing the creating of a quit button: class JUST_QUIT is CLICK_HANDLER; handle_click is UNIX::exit(0) end; end; ... qb: BUTTON_GADGET := qb.create("Quit", 10, 10, 4, 1, JUST_QUIT::new); ... loop ... GUI::poll; end; The loop at the end is suggestive of the need for the application to call GUI::poll intermittently during processing to cause any input from the user to be delivered to the appropriate objects. There is no interrupt-based facility right now. HOW IT WORKS This will be extremely cursory. Contact me for more info. Since we've got two processes needing to communicate, there are a few classes supporting that. SOCKETs are an interface to the Unix critters of the same name; these work a lot like FILEs or IN/OUT, and can be created either intra- or inter-machine. Building on them, SWITCHBOARDs provide a kludgy but functional remote-message-send facility. Basically, any object which conforms to $SWITCHBOARDABLE can be "registered" in a switchboard, and then messages from the remote process can be sent to the object using its "channel number". Messages are delivered to the "obey(STR)" routine (the only feature of SWITCHBOARDABLE). Typically, a $SWITCHBOARDABLE class will redefine obey(STR) to translate string messages into calls on its routines. Not exactly transparent, but it works. I've thought a bit about how to improve this, esp. in Sather 1.0 (eg, through bound routines); suggestions welcome! Now to the GUI proper. The GUI process runs the usual X event loop, except that the socket connection with the application is also monitored for activity. This yields fast user response time (for locally-handlable stuff at least) while also keeping up to date with application output. Special classes deal with top-level window stuff (WINSYS) and with the timescrolling stuff (GUI_TIMER). Now to the gadgets. The gadget classes (eg TEXT_GADGET) are called by application code, and hence get compiled into the application process. Their routines generally just produce a string consisting of the routine name and the argument values, then send this via the switchboard to the GUI process. There, the message is delivered to a corresponding "guts" class (eg, TEXT_GADGET_GUTS). The string is turned back into the original call, and then that routine does the real work, i.e., the X calls, storing data for timescrolling, etc. For most X calls, I have written C wrappers to avoid the hassle of dealing with X data structures in Sather. THINGS TO DO Here are some of the things to be done (some trivial, some major): o integrate existing auto-layout code into digraph gadget. o new gadgets: - container gadgets to allow hierarchies - menus, other nuts & bolts - graphing gadgets - would like to find nice abstractions over graph styles - may use various existing graphing packages o switch to Sather 1.0. o provide application support for runtime-switchable views of data. o provide a database-like mechanism for storage / compression of data from output-intensive applications, and have gadgets then inspect the database rather than talk directly to the application. Comments welcome, Dave Bailey dbailey@icsi.berkeley.edu