This is just one of several computer projects that I occasionally work on. The state of this project is work in progress.
I have my own programming language called STIPPLE. This project is a library of code written in STIPPLE that allows me to easily write applications that have a graphical user interface (GUI). Currently, this library directly interfaces to the Tcl/Tk graphical toolkit. A good place to find out more about Tcl/Tk is to go visit the Scriptics web site.
Why use Tcl/Tk? Well, it seems to be the easiest to use window toolkit that I have been able to find. There might be others, but I haven't stumbled across them yet. Another advantage of Tcl/Tk is that it runs on the three platforms I most care about -- Unix (Linux in particular), Windows, and MacIntosh. Also, this toolkit should be fairly easy to bang together since the Tcl/Tk team has undertaken all of the hard work of figuring our all of the arcane issues associated with these various platforms.
This is not most logical place to put a "to-do" list, but I find it convienient here.
The basic idea behnd the SWIT library is to treat Tcl/Tk as a black box. There are two basic pieces to SWIT -- an application library written in STIPPLE and Tcl/Tk window shell that is running some Tcl code that makes it behave like a server for SWIT library requests; the application library is call the SWIT application client or SWIT client for short and the Tcl/Tk window shell is called the SWIT server.
The application is linked together with the SWIT client library. The SWIT client starts up the SWIT server and opens a TCP/IP connection to it. The SWIT client library sends commands and procedures to the SWIT server and the the SWIT server sends back results, error codes, and events.
There is one connection object for each active connection to a SWIT server. Thus, SWIT directly supports the ability to do GUI applications that display on multiple displays (e.g. a shared white board). Furthermore, a SWIT server can support multiple connections from different window SWIT clients. As long as the SWIT line protocol is obeyed, someone else could write a client library that is equivalent to SWIT in another language (e.g. Java, C, Perl, etc.). A picture of several applications sharing a single SWIT server is shown below:
The SWIT line protocol is very basic. It basically consists of lines of text sent back and forth between the SWIT client library and the SWIT server. The lines are summarized below:
H SWIT_Server major minor socket_name
magor
minor
socket_name
sockN
,
Where N
is a
a number. For example,
`sock5
'.
C command
command
R catch_code result_string
catch
statement.
catch_code
catch
statement. A catch result of `0'
means that no error occured and a
non-zero value means that an error
occurred.
result_string
catch_code
is non-zero, the
catch
error message is returned instead.
E event_number event_data
event_number
event_data
If you were to use the telnet program to connect to the SWIT server, you could use the following to put up a button:
% swit_server # Start SWIT server. % telnet your_machine_name 4231 => H SWIT_Server 1 0 sock5 C button .hi -text Hi => R 0 0 .hi C proc hi {} {puts sock5 "E 0 hi_pushed"} => R 1 0 C bind .hi <Button1> hi => R 2 0 C grid .hi => R 3 0 # Click on the button: => E 0 hi_pushed # Click on the button: => E 0 hi_pushed
Within the SWIT client library, everything is driven off of an event loop.
One important feature of the SWIT client library is the ablity to easily replicate an application window on more than one display. The primary reason for supporting multi-display applications is to simplify support. A user can call up their local support phone number and easily display exactly what they are seeing on the display of the person dealing with the help desk issue. The task of displaying the same window on mulitple displays is called cloning.
When an application window is cloned, it is possible to pass application control from user to user. For example, an application user calls up their support desk, clones the application, and the help desk person temporarily takes control, fixes the problem, and passes control back to the original user.
So how does all this cloning stuff work? Well, basically an application is represented as nested data structure -- application, top-level frames, frames, widgets, canvas objects, etc. Whenever a clone is made, the data structure is cloned. As changes are made to the master data structure, these changes are mirrored in the cloned version. The change mirroring is performed asynchronously so that slow data connections to the clone display do not slow down changes to the master data strucure. Furthermore, multiple clones are permitted, so each clone can be updated at a different rate.
So how does clone updating work? Well basically, each node in the master data structure contains a modification counter. Each time the master data structure is changed, the modification counter is incremented. A change lower in the master data structure also causes the each modification counter in the parent nodes to be incremented as well. When it time to update a cloned data structure, it is a simple matter of to verify corresponding nodes in the between the master and clone data structures to see what changed. Each node in the clone data strucutre that has an modification count that does not match corresponding node in the master data structure is updated and the count is made equal again. In fact, when a clone is made, it performs a lazy copy of the various nodes in the master tree.
The derived interfaces and source code are available.