Cacio Swing AWT peers
September 3, 2008 4 Comments
The last couple of days, I was shitting out^W^W writing some new interesting stuff for Caciocavallo. Inspired by the old Swing based AWT peers of GNU Classpath, and the X11 peers of OpenJDK, here comes the shiny new Swing based AWT peers of Caciocavallo and OpenJDK.
.. is this: implement a set of AWT widget peers, which are not backed up by Motif, Win32, GTK or Qt (the traditional implementations of (Open)JDK and GNU Classpath), but, as the names says, by Swing! This probably sounds a little weird. Isn’t Swing built on top of AWT? How can I then build AWT on top of Swing? Yeah, I admit, there’s some magic and wizardry involved here, but the general idea is this: Swing is not really based on AWT (as: AWT, the widget set), Swing basically only uses the Java2D implementation and the toplevel widgets (Window, Frame and Dialog) from AWT. All the widgets (buttons, labels, textareas, etc) are implemented separately (using only a little bit of common infrastructure from Component, Container and for the event handling). So it should be possible to build AWT peers, that use Swing components for drawing and for handling the logic. This is particularily cool for platforms that don’t have any native widget set (that is, most of the platforms I usually work on: they only barely have a screen to draw on, no desktop, no widgets, etc).
For people that followed my blog in the past, this should not be so much news. In GNU Classpath we already had an implementation of this idea for a long time, and it was working quite well – to some degree. At some point, some problems were popping up, which were related to some special behaviour of heavyweight components. Back then, I added some workarounds to ‘emulate’ this behavious, but it was becoming worse and worse. Examples of this behaviour are: Making a component invisible must expose the underlying heavyweight. Lightweights are always painted on top of their containing heavyweight (try mixing Swing and AWT widgets in a hierarchy – it’s fun!)
I was ‘enlightened’ when I studied OpenJDK’s X11 peers. The trick was to make all AWT components live in their own (X11) window! This was the missing piece of the puzzle. Unfortunately, the X11 peers were not designed with portability in mind, but instead are designed to be very closely tied to X11. They even use some Swing stuff for text components!
So, what were are doing now is pulling together these two ideas: Let all AWT components live in their own native window, and use Swing for drawing and handling the logic. The implementation of this idea looks like this: At the top of the peer hierarchy is the CacioComponentPeer class. This class implements all of the common infrastructure that is shared by all components. And since all components are now real windows, this class also implements the windowing behaviour. The tricky part is, how to make this portable? On different platforms we want to replace how the native windows are implemented. Subclassing is not an option, because we want to subclass the other widget peers from the CacioComponentPeer. A reasonable solution is to not actually implement the windowing behaviour in the CacioComponentPeer, but delegate all this behaviour to a delegate object. I called it ‘PlatformWindow’ and created a nice clean interface for that. This looks very much like the WindowPeer interface, but has some additional stuff in it. In order to port AWT, it would only be necessary to implement this one interface, and get all the AWT widgets for free (because they use this interface only, plus Swing for the rest).
Now the widget peers: they are of course subclasses of CacioComponentPeer. The CacioButtonPeer for example: it subclasses CacioComponentPeer, and thus inherits the fact that it lives in a window. It also has a Swing component ‘inside’, to which the painting and event handling is delegated. I had to do some tricks there: for example, the Swing component must not be connected to the component hierarchy (applications must not see it!), but it must still think it has a parent. So I override getParent() in this Swing component to give it the ‘containing’ AWT component as parent. From the AWT hierarchy POV, there is no Swing component, but from the Swing component POV, there is a valid hierarchy. This is only an example of the tricks that are necessary inside.
So far, I have some working proof-of-concept code in the Caciocavallo-NG repository. It is already possible to open up an AWT frame with an AWT Button, Label and Canvas inside – which look exactly like Swing components:
Even some event handling already works: I can click on the button and activate it using the space bar.
The best thing from my point of view is the great portability of this solution: in order to port AWT, it’s only necessary to implement a Java2D pipeline (which is not so difficult because it’s possible to reuse a lot of code in OpenJDK) and the PlatformWindow interface for the native windowing support as well as the usual suspects like Toolkit (actually, better to subclass CacioToolkit now) and GraphicsEnvironment. That should make my life (and hopefully the life of other developers who want to port AWT to their favorite platform) much easier. Get the code from from the Cacio-NG repository.