The Java AWT: New Event Model

by Jan Newmarch

Copyright: © 1996 Jan Newmarch, All Rights Reserved. Used With Permission.
Key Words: JAVA, Web programming, WWW, HTML


Contents


Introduction

Event handling is the Achilles heel of the AWT. Between beta-one and beta-two the event model changed in a subtle way that most tutorials and books have not caught up with yet. Bugs in implementation and lack of clear specification have led to a lot of confusion. There is the old event model of JDK 1.0 beta-one and earlier, and the new event model from JDK 1.0 beta-two, which includes JDK 1.0, 1.0.1 and the recent 1.0.2.

Even though many versions of the AWT available at the time of writing (June, 1996) are based on JDK 1.0 or 1.0.1, bugs in these versions have forced programmers to ignore the new model and to use the old model. Basically, where the new model was needed the toolkit was broken, so there was no point changing to it. On the other hand, due to other bugs, changing to the new model caused problems such as applets being stopped by the browser when a dialog was iconified. (This problem disappeared between two versions of Netscape - help! how many versions of the AWT are there?).

With the appearance of JDK 1.0.2 many of the ``new'' model bugs have been fixed, and so have the ``old'' model ones. It now looks to be worthwhile to switch to the new model, as programs relying on the old one will become progressively more ``buggy'' as the AWT switches away from the old model.

The change in event models is far more complex than this sounds. If you want to be pedantic, there are really three event models: the old model in which native events were handled in a particular way (the old way) and AWT events were handled in a particular way (the old way). In the new model, native events are handled differently (the new way) and so are AWT events (the new way). What makes this a real mess is that in JDK 1.0 a whole set of native events are handled in the old way, while the AWT events are handled in the new way. To make it worse, the set of native events handled in the old way is destined to migrate to the new way, but there is no specification of when this is to occur.

The old (and old) model

Just to make clear what the new model is changing away from, here is a quick revision of the old model. It is a nice simple model, that did pretty well.

An event is generated in a Component such as Button or TextArea. The event is handed to the component's handleEvent() method. Any component could either handle an event or ignore it. When it handled an event it signalled this by returning true from handleEvent(). This stopped any further processing of the event. If an event chose to ignore an event (the default) then it would be passed to its parent's handleEvent().

An event would percolate up the container tree until it was handled or reached the top of the tree, in which case it disappeared. Suppose a Frame contains a Panel which contains a Button. If the user clicks the left mouse button within the Button and no object handles it, then the event follows this path:

The AWT is a layered toolkit: it is built on top of a native toolkit, such as Windows or Motif. If we look under the hood at what happens to the native event then we see that the AWT event processing takes place after things have happened in the native object. So a Button would be pressed and released (with all the visual effects this entails) and an AWT ACTION_EVENT would be generated to be handled by the application; a key would be pressed and appear in a TextArea and an AWT KEY_PRESS would be generated. An item would be selected in a List, causing it to be highlighted and any previous selection removed, and then an AWT LIST_SELECT would be generated to be handled by the application.

The old model worked like this: something would happen to the GUI object (a window would be iconified, a menu item would be selected, a scrollbar would be moved, etc). After the native toolkit had handled it, then the application would be informed by an event being sent to it. Under X, this was done at the native code level by the use of Xt callback functions, or by adding an X event handler for events which were of no interest to the Xt widget but were of interest to the AWT toolkit. Note that the Java application should regard the AWT event as a readonly object - changes to it are meaningless. The path from native event to AWT event for mouse clicks is shown here:

Why the new (and new) event model?

The new event model is set in place to allow filtering of events. The most common examples occur in entering text, and this is the first set of events to be handled by the new model.

What is happening in these examples? Look at the events:

The new event model is designed to allow this sort of filtering by offering an event to the application before it gets to the native GUI object, and allowing the application to decide what to do with it. This makes the Java application a more active, controlling participant in the event handling process. This is much better than, say, trying to blank out passwords by removing the text after it has been typed!

Who's who in the new model

There are a large number of players in the new model. There is the native GUI object such as the Motif XmPushButton or the Windows Dialog. The user will interact with these objects, and the effects of the interaction will (eventually) appear in these objects.

When the user attempts to interact with an object, they do so by clicking the mouse, pressing keys, etc. Depending on the toolkit, a native event will be generated, such as an X Event with type set to a value such as KeyRelease.

Every AWT GUI object (Button, Label, List, TextArea, etc) has an associated peer object. The peer object is responsible for the interface between the native GUI object and the corresponding AWT object. So when a Frame wants to set the cursor, it calls upon its FramePeer to execute the native code to do this; when a List wants to set the selected item it calls up on its ListPeer to execute the native code to do this, and so on.

An AWT event is manufactured entirely by the AWT library (based on native event information passed to the peer). It is a Java object and nearly all of its fields are meaningful for an AWT application to use (the data field is private, and carries around hidden native event information that is used by the new model).

The set of AWT GUI objects created by the application play a role in event handling.

The last player in this is the native event delivered to the native GUI object. This may not be the same as the event that was generated by the user. This is the esential difference between new and old models - the event delivered to the native GUI object may be modified by the application from the original event.

The new (and new) event model

From the point of view of the AWT, the new event model follows a similar route to the old model: an event is generated by the peer object and sent to a GUI object such as a Button. If it handles the event by returing true from handleEvent() then the event disappears. Otherwise it is passed to its parent, and so on up the GUI tree. When it reaches the top, however, it does not disappear like it used to, but is instead passed to the corresponding peer object. This may handle it, or pass it on down to the next peer object. The event goes a full circle back to the peer from which it came:

X has many layers of event handling. Java is built above the Motif toolkit, so the Xlib, Xt and Motif layers all have their say in what happens to events. Suffice it to say that after a lot of work an X event is ready to be delivered to an Xt/Motif widget by XtDispatchEvent(). Under the old model, the event would be dispatched. Instead, for the new model it is at this point that the X implementation of the AWT interferes and blocks dispatch. Instead it makes a call up into the the AWT peer object, effectively saying "here is the native event, what do you want to do with it?". The peer object then creates an AWT event and sends it on its route through the object, its parents, and then back through its peers, as we have just seen.

The AWT event that arrives back at the peer may not be the same as the event that it sent out - one of the GUI objects may have modified it along the way. So a new X event is constructed using the original X event and any changes made to the AWT event along the way. This new X event is then dispatched to the Xt widget:

The new model for native events makes no use of callback functions since it only uses the native events before they are dispatched to the native object.

Confusion

Now we get to the messy bit: which AWT and native events are handled by which model?

In the beta-one version of the JDK, all events were handled by the old model for AWT events and the old model for native events. Scarcely any copies of this version should be around any more, so you don't need to worry about it. In terms of reading books and tutorials, or examining public source code for event handling, there is a big problem: nearly all of them use this model, so are out of date. Sigh... Any book that tells you to return true after handling an event falls into this category.

In all production releases of the JDK, key events (KEY_PRESS, KEY_RELEASE, KEY_ACTION, KEY_ACTION_RELEASE) are handled by the new model for AWT events and the new model for native events. However, it was not until JDK 1.0.2 that Sun got it working correctly. Some examples of what you can do with this are given later.

At JDK 1.0.2 all other events apart from KEY events are handled by the new model for AWT events and by the old model for native events! So the figures earlier showing the new native model for mouse events are not correct - yet. Sun have promised to move events to the new model, so at some stage handling of native mouse events will be by the new model, as will a lot of other events.

When will this occur? Sun have not said. Which events? Sun have not said. This is more than a little worrying: it makes programming for the AWT a moving target.

The next sections discuss programming for these models.

Programming the old (and old) model

This way of programming the AWT is now obsolete. It is only mentioned because most documentation still does it this way.

When a GUI object handles an event it returns true from handleEvent(), or from one of the convenience functions such as keyUp() or action().

Don't do this any more.

Programming the new (and new) model

The new model potentially allows four ways of handling AWT events: noting them, changing them, discarding them and generating them. They are discussed in turn. The examples are a bit boring in one way, in that they only use key events. This can't be helped, since that is all that works right now!

Noting events

Many events are noted and actions taken upon receipt of them. There is no need to modify the event. For example, in a basic text editor whatever is typed should be entered directly into the editor window. However, a flag should be set to signal that the contents had changed so that the user may be prompted to save them in a file on exit:
class TextEditor extends TextArea {

    private boolean changed = false;

    public boolean keyDown(Event evt, Object what) {
	changed = true;
	return false;
    }
}

Note that there is a real change here from the old model: in the old model the method would have returned true to signal that it had handled the event. In the new model it must return false so that the event can make its way back to the peer object.

Modifying events

Events have a number of fields that can be examined or changed. Under the old models there was no point in changing them, and events had to be treated as `read-only'. While there was nothing in Java to force this read-only interpretation, changing event fields was pointless as nothing would result from it. Under the new model changing fields can have an effect.

When the user types characters, it is common to `normalise' the input in some way, such as changing all characters to uppercase. When a key event occurs such as KEY_RELEASE, a valid field is key. This is the int value of the key (I don't know why it is not a char). If this changed, then a different key will be presented to the native object, resulting in a different key appearing to what is pressed. The following applet converts all characters to upper-case as typed:

import java.lang.*;
import java.awt.*;
import java.applet.*;

public class UpperCaseApplet extends Applet {

    public UpperCaseApplet() {
	UpperText t = new UpperText();
	add("Center", t);
    }
}

class UpperText extends TextArea {
    
    public boolean keyUp(Event evt, int key) {
	evt.key =  Character.toUpperCase((char) key);
	return false;
    }
}
When run, it appears as

You cannot run this from your browser since it is not Java enabled.

(If the text you type does not appear in upper case, then your browser is built with an earlier version of the JDK than 1.0.2.)

Again, there is one major point to note from this example: keyUp() returns false, not true. The old model, where you would return true to signal handling of the event must not be followed here!

There is also a minor point: it is probably slightly more common to cast to lower case than upper. However, the Motif implementation of JDK 1.0.2 doesn't handle converting to lower case due to a bug. The AWT has two fields that it can use to check if a key is upper case: the key holds an ASCII value that can be upper or lower case; in addition, the modifiers field can contain the mask SHIFT_MASK. To convert to lower case, one should reset the key field and also the modifiers field of the AWT event. In building the new X event from the old one, the AWT fails to clear the xkey.state before checking the AWT event's modifiers field. In other words, converting to lower case doesn't work yet for Motif. Apparently it does work for at least Symantec 1.2 for Windows95.

Discarding events

Discarding events is easy: return true from the event handler. The following is a very naive version of a PasswordField that collects the password in a StringBuffer, but neither displays it on the screen nor enters it into the TextField where it would be accessible by getText():
class PasswordField extends TextField {

    private StringBuffer password = new StringBuffer();

    public boolean keyDown(Event evt, int key) {
	if (key == '\n')
	    return false;
	password.append((char) key);
	return true;
    }
}
(Note, this is very simple in that it does not handle such things as backspacing over input or pasting of text into the widget.)

While appearing simple in code, the PasswordField class has the most complex event handling that I have yet come across. Ordinary characters such as alphanumeric, punctuation and most whitespace characters will form part of the password and will need to be suppressed. This is done by returning true for them. The newline is a special case. As a character typed, the native event is processed by the new model, and must be passed all the way through. When it is eventually passed to the underlying native GUI object (a Motif TextField) it triggers a callback function that generates an ACTION_EVENT. So the native event is handled firstly by the new model and secondly by the old model! The following applet uses the PasswordField to collect a password:

Generating events

It is easy to create a new event: the constructor will do that. It is not hard to inject it into the path that events follow so that it ends up in the peer object. So, what then happens to these ``synthetic'' events? In JDK 1.0.2, they are just discarded without having any effect on the native object. Creation of synthetic events is currently worthless.

The reason they are discarded is as follows: when the AWT event is created by the peer object, the original X event is dragged along with it. A binary represention of the event is placed in the event's private field data. When the AWT event returns to the peer, the original X event is reconstructed from data and is then modified according to any changes that have been made to the AWT event. This is then dispatched to the widget.

A Java object trying to create an AWT event does not have an X event lying around, so the private field data will end up as null. When this event gets back into the peer, it checks for the null value and discards the event. Even attempting to reuse an existing X event by repeatedly posting an AWT event to the widget fails.

Is this inherent in the AWT? Certainly not for X. It is possible to construct new X events, and even ask the X server to give valid id fields (the replayXt system I wrote, which does testing and replay of Xt applications, constructs X events as needed). So it is either a failure of implementation in JDK 1.0.2, taking the easy way out for now, or a design decision that we have not been told about. When you find out which, please let me know!

Programming the new (and old) model

Despite the change to the new model, nearly all code built using the old model continues to work. This is because the old event model for native events is still used for all but key events. This means that the native event is handled before generating the AWT event, so it makes no difference whether or not a new native event is generated. If it is generated, it is discarded anyway, since a flag is used to avoid duplicate sending of the event.

But it may make a difference someday. In later versions of the AWT more events will be moved to the new model, and then the old model treatment will cause the events to be undelivered. When this happens your application will promptly break: the Java application will believe that certain things have happened, but the native GUI objects will never have received these events.

To give a concrete example of what will be involved in converting the toolkit: presently, a mouse click in a Motif List generates an AWT event via the XmNsingleSelectionCallback or XmNmultipleSelectionCallback functions. Motif is responsible for deciding which item is selected and highlighting it. The callback function makes a dynamic call into the AWT peer object, supplying information about which item has been selected - information that Motif has already worked out. This is by the old model.

If this native event is to be handled by the new model, native code as part of the AWT will have to duplicate work of the Motif toolkit by figuring out which item would have been selected if the event had gone ahead. It will then create an AWT event, which you may modify. One of the valid fields for a LIST_SELECT event is arg, which is set to the item selected. If this is changed, then presumably an X event would be generated with the y reset to the correct value so that the changed item is selected (assuming it exists, of course...).

The complexity of this makes it seem to me most unlikely that LIST_SELECT will change to the new model, at least for some considerable time. I would even worry about mouse events, the most obvious next candidate for conversion from old to new model: by changing the modifiers field a button press could change from Button1 to Button3, which under Motif signals the start of a drag-and-drop operation rather than a select operation!

So, there is considerable uncertainty as to what events will move to new model, and when. What should a developer do under these circumstances? Sticking to the old model looks ok right now, but may not be on a longer time scale. The converse question is: what problems arise from switching to the new event model while the old one still holds?

In JDK 1.0.1 asuming the new native event model while the old native event model was used did indeed cause some problems. One case I experienced was for the WINDOW_ICONIFY event. In an applet, a Dialog may be created, but the user may choose to iconify it (if the window manager supports this). Assuming the new native event model means that the Java handleEvent() must return false for the event to take place. This means the Java event must be passed to the parent, which in this case is the applet. In 1.0.1 the applet got it wrong, assumed that it had been iconified rather than its dialog, and promptly stopped itself!

In JDK 1.0.2 these types of problems seem to have been cured, at least for the cases I have tried. So it looks to be relatively safe to assume the following course:

Now all you have to figure out is how to tell which version of the JDK you are using! (Although the System class has a java.version field in getProperties(), this is not obviously related to the JDK version number.

Conclusion

As I said in the Introduction, event handling is the Achilles heel of the JDK. The models are confusing, the lack of knowledge about the future development causes uncertainty, and the continued presence of bugs and unimplemented components leaves one guessing about what is really going on. I have reached this point by poring over source code, gleaning clues from net postings and reading lots of Fantasy books.

At least the Fantasy books have their own internal consistency...

Previous AWT Articles