This chapter describes the contents of the BISS AWT packages and shows how to use its main tools. It is not intended to list every class in detail, only the main class groups / categories, the key concepts and a brief description of the tools found within a package are presented (which should be sufficient in combination with the source comments).
Currently, the biss framework consists of four packages: biss, biss.awt, biss.jde and biss.awt.kernel. While the first three packages closely follow the leveled tree approach (biss as a basic layer, biss.awt as a toolkit and biss.jde as an application package), biss.awt.kernel is a bit different. As a matter of fact, it was added just to show how biss.awt can be exploited to get a stand-alone Java GUI implementation.
Figure 3.1: BISS AWT package structure
This package contains about 20 classes required by the other biss packages. There is not much structure within this package, it merely is a collection of useful classes and interfaces which all have in common that they can be used outside of biss.awt applications as well. The prevalent group of classes are the Lib Classes like StringLib , FileLib , DateLib and VectorLib (other lib classes may follow). The package currently lacks elaborated container classes (but this might indicate that you can already live with the standard Java classes). This is certainly not true for the Queue class, which is very useful for queue applications (class Vector is not suitable for that purpose because front insertions are very inefficient).
Currently, there are just two entries of interest (which can be set in the property file ~/.java/biss):
Here comes the main body of classes. This package is the real objective of BISS AWT and provides a comprehensive set of highly reusable GUI components which look and feel the same on all platforms (at least they should do so). It makes it possible to completely skip the native java.awt components (like java.awt.TextArea etc.) by just relying on the generic java.awt parts (like java.awt.Component, Container, Window, Frame, Canvas, Panel, Graphics, Color etc.).
Certain other packages have done that before (to implement single, specific widgets). But by consistently adopting the ObserverSocket mechanism described above, BISS AWT shows some characteristics which sets it apart:
In addition, package biss.awt implements a lot of java.awt.peer interfaces. This makes it a good base for a stand-alone java.awt development.
Six different groups of classes can be identified within this package:
This category includes rather simple classes like Key and Border , but also very important concepts like the GUIHandler . This class (in combination width several interfaces like FocusInterface , KbdInterface etc.) implements a delegation mechanism which is used for all biss.awt components to centralize event processing code (which is much needed because of the lack of an accessible common base, see below).
Another heavily utilized class is the Layouter (which is actually the base the Designer tool was build upon). This class implements a LayoutManager that provides a very fine grained control of specific component resize behavior. It s main idea is to describe first a bounding box relative to the parents extensions and then to apply fixed offsets to this bounding box to get the actual component coordinates (either specified in pixels or SystemFont units). Since the (relative) bounding box is allowed to degenerate (being either a line or even a point), this is a very flexible mechanism. In addition, the Layouter may be used to specify different layouts (for the same container) which can be referenced by a symbolic name. This is a very useful feature to implement widgets like notebooks or to zoom certain widgets within its container.
There are just two classes within biss.awt which can be used to instantiate decorated toplevel windows (managed by the windowmanager of the specific platform): TopWindow and DialogWindow .
The first one basically is a java.awt.Frame with some added ObserverSocket (like OsOpen, OsClose, OsCommand etc.) and a mechanism to handle overlay windows (which should be shown in front of the corresponding TopWindow instance).
The DialogWindow is not related to the java.awt.Dialog. It serves a similar purpose (to enforce some kind of sequential user action), but it does so without the need for any modal event processing. Within TopWindow, there already is support for Z-order management and per-TopWindow component disabling (provided by the addOverlay() method). Class DialogWindow just adds the dialog specific Cancel behavior. Otherwise (transient) DialogWindows are treated like ordinary windows, i.e. communication with the owner is set up by means of ObserverSockets instead of return values. The reason behind this is threefold:
This scheme is just a bit inconvenient for very simple, value-returning dialogs like StringPrompters or ConfirmDialogs. Otherwise it is considered to be a rather worthwhile structural ``constraint'' (number of windows should be kept small to avoid cascades of dialogs, sequential processing of windows should be avoided to make use of the GUI strengths, etc.).
There are two classes which produce non-windowmanager handled windows: MenuWindow and ChoiceWindow (both don't have a CompileUnit / file of their own).
For several reasons, these windows are among the most demanding features of a java.awt implementation (again, don't think just of X), which means they often behave somewhat different (not to say erroneous) on different platforms:
This group is derived from java.awt.Canvas and includes the basic interactive widgets. It can be subdivided into public classes like TextField , Button or Checkbox (which can be instantiated explicitly), and hidden classes like TextAreaCanvas or ListCanvas (which are just used within Compound objects). None of the Control classes has children, all of them provide a rich set of ObserverSockets.
Decoration derived classes are no real windows (don't have peers). They are a useful abstraction for rather static decorations of complex views (mostly TopWindows). Their only purpose is to do some specific drawing (images, boxes, text labels etc.) within their ``parent''. This does not require a real window construct and could be done perfectly well by using the existing java.awt.Component class as a base (it basically just requires a paint() reimplementation). However, Component only has has an (empty) package private constructor, which makes it impossible to derive directly from another package. We therefor decided to add class Decoration to package java.awt as some kind of a Trojan horse. This, in general, is certainly a bad practice (adding classes to foreign packages), but in this particular case the gains in both speed (no addNotify(), no peer object creation) and space (no native object) seem to justify it (especially if you think of complex windows with a lot of text labels).
This is perhaps the most interesting group of classes within biss.awt. In difference to the more complex native java.awt widgets (like List or TextArea ), there are no ``hidden'' components (like Scrollbars etc.) within their biss.awt counterparts. All widgets have to be composed by explicitly using Controls and Decorations and grouping them within some sort of Container. The abstract base for all these containers if the java.awt.Panel derived biss.awt.Compound . Most Compounds consist of a working canvas which can be scrolled (e.g. TextArea, List). These specialized Compounds are augmented by the (Compound derived) abstract base ScrollablePane (and its Control derived helper ScrollableCanvas).
Class biss.awt.List should be mentioned separately because of its drawing features. In difference to the usual List implementation (which just capable of displaying ordinary Strings), there are several choices of how to display its items (one by one) which are checked in the following order:
There is even more support for sophisticated drawing (not just restricted to Lists), which is implemented by means of class PaneDrawer and interface DrawablePane . Sometimes, drawing of items is either context sensitive (depends on items before and after the currently drawn item), or it is too time consumptive to be done during normal scrolling operations (should be done asynchronously). A PaneDrawer instance is a thread which is created for a tuple consisting of a DrawablePane (e.g. a List object) and a Queue object (to store draw requests). It simply waits for a new draw request to arrive (via the Queue) and then calls the interface method drawPane(). This, for instance, is implemented in ScrollablePane by triggering the OsDrawPane socket. It is up to the client to register for this socket and to take appropriate action on being notified by it. For instance, biss.jde.LibBrowser uses this mechanism to call a method drawFileInfo() which certainly would be too slow to be done every time an item has to be drawn (because it queries information about the file system). Another nontrivial example is the syntax highlighting within the biss.jde.CUBrowser text pane (which is an ordinary TextArea utilizing a PaneDrawer to color syntactic constructs asynchronously).
There is another behavior of List which should be mentioned. Because there is no native widget behind biss.awt.List (all is done within Java), it can make use of Java objects not only for its items but also for the container these items are stored within. This means, you can simple hand over an existing Vector of arbitrary objects (even with heterogenous items) to a biss.awt.List widget and it will use exactly this Vector . This, in turn, means there is no redundancy between model data and display data (which always requires consistency checking efforts). A repaint of a List item always displays its up-to-date contents. This really shows the potential of widgets which are written completely in Java (and are able to stay within the Java type system without falling back to some primitive native data representation like char* ).
It is no surprise that within the Compounds group there can be found many new and more sophisticated widgets like:
Since biss.awt does not use platform dependent native widgets to implement things like Lists, it has no ``native look & feel''. It was targeted towards some kind of a ``Java application look & feel'', which should look and behave the same as much as possible on all platforms. However, there are just a few things which are not instantly obvious. For instance,we use small red triangles instead of borders (which disturb any 3d effect) to mark the focus component. Pop-up menus remain open (due to their java.awt.Window nature) when switching to non-java applications (but are closed again by activating any other Java window). The Alt modifier is used for TopWindow global hotkeys, Ctrl for pane local hotkeys. Most of this behavior (like colors, extensions, fonts, key modifiers, border styles and text styles) can be configured either by means of the package class biss.awt.Awt or by means of static fields within certain classes (like DefaultBorderStyle within biss.awt.Label).
There should be a hint to a specific structural deficiency of biss.awt, the ``no common root'' - problem. Since biss.awt works on top of the unmodified, standard java.awt generic classes, it has to use Frame , Panel and Canvas as bases for its own widget classes (Component can not be modified). This means, there is no common base available for biss.awt.* to implement common behavior and state. This, in turn, means that just delegation mechanisms (like the GUIHandler) can be used to factor out these common things. Sometimes this is not possible or suitable (creates more overhead than what would be saved), which causes certain things to be implemented somewhat redundantly in the biss.awt root classes TopWindow , Compound and Control (e.g. the polymorphic canGetFocus() method). Special care should be taken to keep this redundancy at an absolute minimum.
A feature which was added afterwards to biss.awt is the peer role of many of its widgets (like biss.awt.Button implementing java.awt.ButtonPeer , biss.awt.List implementing java.awt.ListPeer etc.). Since most of the functionality was already there, it was mainly a matter of renaming methods to accomplish this (with less than 100 lines of code within the whole biss.awt package). The efforts included the following list of topics:
While the actual source modifications were relatively easy, understanding of its runtime object structure is not. Objects of package biss.awt acting as peers have ``real'' (native) peers by themselves. They are just the missing link between the specialized java.awt classes and the generic peer objects (like biss.kernel.Peer).
Figure 3.3: Peer Role of biss.awt.* Objects
From the added indirection (e.g. biss.awt.Button between java.awt.Button and biss.awt.kernel.Peer), it is obvious that using biss.awt.* classes in a peer role is slightly less efficient (both speed and space) than using them in a non-peer role (which is definitely the recommended way). This feature only exists to implement some backward compatibility.
Many aspects of biss.awt display characteristics can be set (using its property file usually located in ~/.java/biss.awt). This includes fonts used for various purposes (menus, buttons, labels etc.), colors (again, for many different widgets), and some dimensions (Scrollbar width etc.).
Because of its usage as a system unit, biss.awt.SysFont is the most important setting. The Awt class tries to get some suitable defaults (depending on the automatically detected ScreenSize and the used toolkit), but there is a good chance that this setting has to be changed manually. This is also true for other font settings like biss.awt.ButtonFont , biss.awt.MenuFont etc.).
Another setting which often requires a manual change (e.g. if using the Xinside server with default settings) is biss.awt.GlobalKeyMod . This setting is used to specify the Java key modifier which should be used for TopWindow-global hotkeys (e.g. '12' or '4' for Alt-key modifier on X servers). If your BISS AWT seems not to recognize global hotkeys (e.g. Alt-Z to zoom biss.jde.CUBrowser views), this is where you can fix it.
biss.awt.DlgXUnit and biss.awt.DlgYUnit are used to specify ``portable'' layout extensions (the Layouter can use this unit for its offsets). Usually, the SysFont based default value is appropriate.
biss.awt.ScreenSize describes the actual screen dimensions, which are used to scale printer output (e,g, images). If you want to get the most accurate output, you probably have to set this property (its default is just based on the ScreenSize).
biss.awt.GifPaths are used to specify a path list which is searched when locating GIF files via the getImage() method of class Awt.
Another setting which shouldn't even exist is biss.awt.AwtToolkit (which is set automatically from the default toolkits complete class name). Its only purpose is to efficiently enable platform specific workarounds for existing Java implementations). Hopefully, this will be gone some day.
This package serves two different purposes. First, it provides a toolset to browse and edit ordinary Java sources in general and the BISS AWT sources in particular. Second, it represents a non-trivial example of a biss.awt based application.
The biss.jde is some kind of an IDE which works on regular Java source files, called CompileUnits , which each may contain several class or interface definitions. The key concept of biss.jde is to provide a general tool (the CUBrowser ) for programming arbitrary CompileUnits, and a set of specialized tools for programming particular, biss.awt specific classes (most notably the Designer ). All of these tools can be thought of as specialized editors (even if they process graphical input like the Designer). There outputs are just plain Java sources.
This package currently contains about 50 classes. There are 5 physical/logical view pairs, the rest deals with scanning, parsing and management of Java source files.
The LibBrowser is the starting point of the whole biss.jde application, which can be invoked by simply typing:
0pt
[] java biss.jde.LibBrowser
It is used to display all CompileUnits which can be found in a set of (recursively processed) directories specified in the biss.jde property file (setting biss.jde.SrcRoots).
Its main components are two Lists: the ContainerPane and the ItemPane . Currently, there are two modes (which can be set via two CheckButtons at the top of the view) to control the contents of these panes: FileMode and TypeMode.
In FileMode , the ContainerPane shows all directories being parsed, the ItemPane shows all CompileUnits (= *.java sources) within the selected directories (you can select more than one item in an ObjectPane by simply pressing the Ctrl key during a mouse click). If no directory is selected, all CompileUnits are being displayed. This is the initial mode of the LibBrowser.
In TypeMode , the ContainerPane shows all packages that have been found within the parsed CompileUnits. The ItemPane shows all types (classes or interfaces) which belong to the selected package (again, all types if there is no ContainerPane selection).
By double clicking (or hitting the enter key), a CUBrowser can be opened on the selected CompileUnit / type. Adding or deleting CompileUnits can be done via the pop-up menu (on mouse button 2) of the ItemPane. Additional LibBrowsers can be spawned with the ContainerPane pop-up.
This view is used to browse, modify and compile the contents of a single CompileUnit (*.java file). It is some kind of a class browser tailored to the specific needs of Java.
It has five major panes:
Below the TextPane there is a row of StaticText widgets: the ModeInfo (displaying the current mode), the FileInfo (displaying size and timestamp of the CompileUnit) and the ChangeInfo. The ChangeInfo shows a number (of not yet saved but accepted changes to the CompileUnit) and an optional '+' if there currently are unaccepted changes (modifications) within the TextPane.
At the bottom right of the View, there is the AcceptButton which is used to validate modifications (according to the currently displayed mode in the ModeInfo).
The CUBrowser knows two different levels of modifications: unaccepted (not yet validated modifications of the TextPane) and accepted (validated changes which are not yet saved to disk). Each time the CompileUnit is saved, The ChangeInfo count is reset to zero.
In case of an operation which would loose a not yet accepted or saved modification, the AcceptButton and the ModeInfo is displayed in alert mode. The operation is not yet performed, giving the user exactly one chance to accept the change. If the user repeats the action that caused the alert, this action is performed and the unaccepted/unsaved changes are lost.
When accepting modifications, the TextPane contents are parsed according to the current mode (basically Type, Import, Data or Method). This gives the parser additional info so that it can be more tolerant than the original Java syntax (e.g. a simple list of names without import keywords is sufficient to specify a list of imports).
In all modes, more than a single construct (type, import data or method) can be entered. Datas and Methods can me mixed. To display / edit all items of one of the upper Lists, simply press the F12 button (or choose select all from the pop-up) within it. Hitting the ESC key resets all List selections.
When entering new methods or datas, modifiers (public, static etc.) are copied from the current selection. However, modifiers can also be specified explicitly. The current modifiers of an item (type, data or method) are displayed as symbols (box for static, circle for instance, no fill for private, half fill for protected, full fill for public, diagonal fill for package default).
All Panes have pop-up menus (brought up by clicking the right mouse button) to add, delete, set modifiers, set filters or set/reset selections.
Underscore chars in Buttons and Menubars can be used as global hotkeys (e.g. alt-a for the AcceptButton, alt-c for compile, shift-Ctrl instead of alt for Windows and OS/2).
Underscore chars in pop-up menus can be used as pane local hotkeys (e.g. Ctrl-a for paste in TextAreas).
There are SplitBars (which can be dragged around by mouse) between all panes to change the geometry of the view. In addition, there are three different zoom levels which can be toggled by activating the Zoom menubar option (by mouse or alt-z).
The Src/Cmt menubar option can be used to switch between source of comment display/edit.
This tool, which can be launched by double-clicking on a MethodPane entry in the CUBrowser, is used to further analyse and modify the contents of a single method. It is still in its very infancy.
Usually, methods should be small (about 1 page of text). This means there should be no need at all for this tool. However, the biss.awt structure has one method which tends to be rather lengthy: the update() method of Observers. Especially the logical views (like LibBrowser or CUBrowser) may contain many different entries in this method, which are all layed out in a scheme like:
0pt
[]public void update ( Observable obs, Object arg ) {
if ( obs == View.ItemPane.OsDrawObject )
drawItem();
else if ( obs == View.CntrPane.OsDrawObject )
drawContainer();
...
else if ( obs == View.ItemPane.OsCommand ) {
if ( arg.equals( add) )
askForNewCUNames();
...
}
...
The upper StructurePane shows the contents of a method as a list of hierarchically grouped statements (this is an example of the biss.awt.LeveledList widget).
The lower ActionPane displays the sources for the selected entry and can be used to modify it.
In it final version, you will be able to copy / drag method entries from CUBrowsers to this tool in order to specify notifications and appropriate actions. Again, this is not available yet and functionality as well as layout of this tool may change in the future.
The Designer is a code generating tool to create and modify the layout of physical view classes. It is launched via LibViewer pop-up menu item 'design' and works on the same CompileUnit as all other BISS JDE embedded tools ( e.g. CUBrowser), so CompileUnit modifications by the Designer can never go out of sync with other, simultaneously opened tools.
The Designer tool consists of a pair or two tightly coupled TopLevel windows:
It is important to notice that the graphical editing within the LayoutCanvas does not try to be as WYSIWYG as possible. While there can be extensions to do this (by means of plug-in drawing classes), we decided against it in the basic Designer version because of the following reasons:
We strongly feel that the current graphical representation is more
suitable for professional use (to specify many complex views in a
portable way). Besides, the preview mechanism (which does not require
a compile) is fast enough (usually 3 sec) to compensate the
drawbacks of this design. To verify this, open up a Designer on the
biss.jde.CUViewer class (by first selecting it in the LibBrowser and
then selecting the ``design'' popup-menu item), look at its design representation
and then push the ``Preview'' button within the Designer.
To create a new view first insert an empty CompileUnit to the class libray via LibViewer pop-up menu item 'add'. Then you can launch the Designer on that CU. It automatically creates the code components needed for further design operations on that CU ( class, layout field, default ct, open() and init() methods) :
0pt
[] public class MyView
extends TopWindow {
...
Layouter Lo = new Layouter( this);
...
public MyView () {
super( ``Title'');
open();
}
...
public void open () {
Lo.add( Button1, ...);
...
init();
if ( Bounds != null)
openIn( Bounds);
else
openIn( x, y, width, height);
}
...
public void init () {
}
...
To modify manually created views with the Designer, the code must include at least the components described above, otherwise the CU is not designable. In this case modify the code manually to match the Designer criteria or build a new CU with the Designer and copy your code in there.
As already shown in the code section, the Designer uses a powerfull, all-purpose LayoutManager, the BISS AWT Layouter. For details, refer to the Layouter CU documentation.
The view consists of 2 Notebook pages:
This very populated page contains widgets to set the layout specification of a target component (selected within the LayoutCanvas)
Note that the dialog actions immediately modifies the selection, so first hit the 'Clear' button to deselect it before specifying 'add entry' settings.
Figure 3.7: Designer Layout Page
The 'Order' page of the Designer view is used to specify the Tab-order of the current layouts Components. It also provides some group and alignment positioning functionality.
Figure 3.8: Designer Order Page
The DesignFrame is a view opened automatically by the Designer to display the current layout Component placements. To select an object click MouseButton1 over the object (multi-selection Ctrl-MouseButton1), to deselect click over empty Frame space. The selection settings are shown in the layout page of the DesignViewer. The red handle points symbolize the positions of a selection (multi-selection green points), the blue ones (origin and corner) the ratio position part. To move a handle point click MouseButton2 over it and drag to the destination position, to move the master selection (red) or all multi-selections (green) drag MouseButton2 somewhere within it. Any position modification is immediately updated within the DesignViewer.
When you finished designing, don't forget to recompile the modified CU to put the changes to work.
This is a specialized, graphical editor for biss.awt.ClassImageSource s (our portable way to handle icons and bitmaps).
It looks and behaves like a usual, simple icon or bitmap editor. The output created is a Java source file containing a biss.awt.ClassImageSource derived class that acts as an ImageProducer (based upon a MemoryImageSource) and does not require any local file system or network access in order to reproduce the image.
Various aspects of the biss.jde package can be set via its property file (which usually is located in ~/.java/biss.jde). At least the following entries should be specified:
For other settings (desktop location of views, syntax colors etc.), please refer to the Package class Jde.
This package is a stand alone implementation of a Java windowing system which does not need any of Suns (or any other vendors) native awt libraries or hidden classes (sun.awt.motif.* or equivalents). It consists of a Java layer (mainly with classes which are pre-determined by the java.awt) and a native layer consisting of plain *.c files (to implement the platform specific GUI access).
The main idea behind the design of this package is to do all specialized window management, event handling and drawing within biss.awt and restrict biss.awt.kernel to generic functions in order to keep it as small and portable as possible.
In addition, since the most work of biss.awt.kernel is done in the native layer, the Java layer should be kept portable. This means, implementing support for a new virtual machine or different platform should only involve writting a new native layer.
What seems a bit strange is the import of toolkit-level biss.awt classes into this system-level package (wrong import direction). There are two justifications for this design-anomaly:
This currently consists of 6 classes. They are mostly pre-determined by the java.awt design which makes use of a specific native lib by means of several abstract classes (java.awt.Toolkit, java.awt.Graphics etc.). As a consequence, the following classes can be found within biss.awt.kernel.
Toolkit reimplements java.awt.Toolkit and is the starting point for every AWT. It has the following responsibilities:
Class Toolkit does all of this in a straight forward way, which requires further explanation just for two design decisions: registering peers and event acquisition.
Peer objects are kept within the PeerTable array field, which is used as a linear hash table from within the native lib (display.c getNextEvent). The reason why peers are registered in a Java data structure is to make sure that peer objects (and the whole graph of objects behind them) can be handled by normal garbage collection. If they would not show up in the Java layer, there would be a good chance that garbage collection would prematurely destroy them or that it would not recycle them at all (depending on the garbage collection subsystem used by the VM). The reason why a linear hash table is used in favor of java.util.Hashtable is that it is only accessed from the native layer. It would be significantly less efficient if the native layer would have to make Java callbacks in order to lookup entries. With open addressing the access seems to be effective enough to enable population rates of up to 80% (and therefor is no substantial waste of space). By using PeerTable like it is, peers are accessible from within the native layer (main access) and the Java layer (for future use, e.g. to dump all peers etc.).
Event acquisition at it topmost level requires a asynchronously executing loop. This is performed in the run() method (which is executed within its own thread). If this would be done within the native layer, it again would require a C-to-Java callback for every event delivered (something we would like to prevent). Another important aspect of event acquisition is if it should be done by means of blocking native calls. For compatibility reasons (lowest demand on a specific virtual machine implementation), we currently used non-blocking calls, which means we have to take care of lots of sleeps(). In order to prevent the ``bumpy'' event processing which is usually caused by this, we use a hint flag (NativeLib.AttentionHint) to indicate high-frequency series of events (e.g. key repeats). However, it should be possible to switch to a more efficient blocking scheme if the VM has sufficient support for kernel threading. If kernel threading is used, it has to be checked if the platform specific windowing subsystem requires windows to be created within the message processing thread (Presentation Manager does, for instance). With the CreateQueue field, there already is some support to handle this.
It should be noticed that Java painting (which is itself generic by means of a polymorphic paint() method) does not use explicit Java event dispatching (i.e. it is not done from within any visible handleEvent() method). We therefor do the paint call from within the Toolkits run() method too. This means that Toolkit also has to include a Graphics object to do the painting (for efficiency reasons).
There is a potential pitfall within the Toolkit constructor. To enable recycling of array elements within the PeerTable (not yet used and recycled elements have to be distinguishable for open address hashing), a dummy Peer object is created. This requires class Peer to be statically initialized before the Toolkit constructor returns. If the default toolkit is either directly or indirectly referenced from within Peer (or any other class referenced by the Toolkit constructor), this will end up in two competing default toolkit objects (which are set in java.awt.Toolkit on return of our constructor).
This is the only class that actually calls the native library (provides the native method definitions). We decided to route every native call through this class in order of being able to make use of the Java inherent synchronization mechanism (all lib methods are synchronized). With sufficient kernel threading support by the VM / the specific platform, it should be possible to rely solely on synchronized methods and to skip this issue within the native layer (which currently uses the macros LOCK and UNLOCK to do this).
Peer is the implementation of all required (generic) peer interfaces (from java.awt.peer). It is a rather uncommon design to concentrate the interfaces in one class, but since it just has to cover CanvasPeer, PanelPeer, WindowPeer and FramePeer (and this mostly involves forwarding to the NativeLib object), it seems to be justified. The responsibilities of this class include:
Within the handleEvent() method, there has to be a special treatment of WINDOW_MOVED events for Frame and Window instances (both don't have a Parent) in order to take care of propper child resizes. This event usually is caused by an interactive rearrangement of the window (via some windowmanager), i.e. there is no automatic reshape() call within its context (which has to be done explicitly in addition to a general invalidation).
The parent - child tree of the native window objects is completely mirrored by a tree of Peer objects.
Images might supposed to be easy objects, but they are not. There are many different facets of Images
The transforming external representations is done by ordinary ImageProducers like biss.awt.GifProducer. In difference to the standard java.awt implementation, this biss.awt class is available for general use.
The internal representation is kept in the Java layer and is generically stored in the fields Model and Pixels . We think that it should be up to the external ImageProducer to decide on the most suitable format (to optimize storage consumption). However, this currently is constrained by the native lib draw function, assuming either an IndexColorModel-based byte array or an int array with standard Java RGB values. Once the internal representation of an image has been initialized, the image may act as an ImageProducer of itself. Thus, class biss.awt.image implements both the ImageConsumer and the ImageProducer interface.
The different states of an image object are distinguished by means of the State field, which can have the values:
The native drawing mechanism directly works on the Image Pixel and Model fields instead of using an intermediate production. This has the shortcoming that possible internal representations are limited to the native layers capabilities. A Java production could first transform any internal representation to a single format understood by the native lib drawing format . But this would have the disadvantage that image data temporarily would have to be kept redundant. Given the immense memory requirements of some images, we decided against it (to avoid accumulating speed and space problems for every redraw).
The internal representation is kept as an invariant, i.e. it is not modified during a scaled redraw. As a consequence, scaling is done within the native layer (to speed it up).
There is no permanent upload of pixel data to the X-server. The Id field is just used for handles of off-screen drawing images (which have to be kept in the servers address space anyway).
There is not much to say about the Graphics reimplementation. By far the most methods just forward to NativeLib. Caching of Graphics objects is done in the native layer, i.e. the createGraphics() methods of Peer and Image return new Java Graphics objects (which are linked to cached native lib GCs during initialization). Accessing Image functionality within drawImage is a bit awkward because class Image already imports Graphics (it has to create Graphics objects in its getGraphics() method). To avoid mutual import, this has to be done by utilizing the (not very well documented) checkImage() and prepareImage() methods of the Toolkit object. Unfortunately, this one has to be accessed indirectly via biss.awt.Awt.DefToolkit because biss.awt.kernel.Toolkit, in turn, creates Images. Sometimes, it is just laboriously to prevent mutual imports (in order to get a clean build sequence).
This could be kept even shorter. FontMetrics objects don't have a substantial functionality, they just provide storage for font information which is computed in the native layer. It seems that class FontMetrics should better be non-abstract (metrics information can be stored in the corresponding Font object and its generic pData field).
Actually, there is another class Dbg which is just used for optional debugging purposes. To remove all debugging/logging from Java code, comment out the body of the log() method of this class and recompile biss.awt.kernel with optimization turned on (``-O'') to inline functions.
Figure 3.11: package biss.awt.kernel
This layer is both platform and virtual machine dependent. The current
BISS AWT comes with an implementation based on plain Xlib (no Xt,
Motif or something else used here) which can be run with the kaffe
VM of Tim Wilkinson (= version 0.7.0). It was developed
on an ELF-based Linux platform. Its description therefor tends to
be specific to this environment. However, there are 7 major tasks
every native support layer has to accomplish:
This again maps pretty good to the file structure of the native layer
This header file includes macros for accessing Java fields from within the native layer. Using these macros is the preferred way to access these fields to ease modification of data structures and class definitions.
All specific structures used within the native layer can be found in this file.
It also holds the kaffe VM specific synchronization macros (which are copied verbatim and have to be kept consistent with the original kaffe sources)
This file contains the
To avoid superfluous XEventsQueued calls, the number of currently available, not yet processed X events is stored within the EventsAvailable field of class NativeLib. In general, this function has to avoid any Xlib queries which are not strictly required because it is called quite frequently (by the event polling loop inside of the Toolkit run() method).
The getNextEvent() function also contains provisions to avoid some common Xlib programming errors (setting the focus to not yet mapped windows, getting desktop window positions for toplevel window ConfigureNotifies etc.).
Like what is implied by its name, this file contains code to manipulate native window objects. Function createWindow() is the only which actually creates them (by means of XCreateWindow). All conditional processing (e.g. setting window manager hints for Frame instances) is done by branching on Java field values. All windows except of toplevel windows are mapped automatically (i.e. become visible as soon as the toplevel window is explicitly mapped by show()).
Most other functions of window.c have very close Xlib equivalents.
An interesting design decision for this file would be if, and how, event selection is performed (one of the main objectives of JDK 1.1). Currently, we just do event compaction within display.c.
Within this file, you will find
The caching is done in a very simple way by storing unused (returned) GCs in an array. Each time a new GC is requested, this array is searched first.
The function initGraphics() is the most interesting one of this file. It is called whenever a new Graphics object is created or whenever the context of an existing Graphics object is changed. This function sets all relevant GC attributes (foreground, background, clipping attributes etc.) according to the current context object of this Graphics object (either a Peer or an Image).
This file holds the code to
At a first glance, this seems quite easy to do. But is has its obstacles, except of image.c, color conversion requires the most memory and incorporate the most awt.h structure definitions.
Since Java inherently uses an RGB color model, transforming to and
from PseudoColor pixels is not very easy to do. Even if we reject
using private colormaps, there are two possible strategies for RGB
to pixel conversions. First, one could heavily depend on XAllocColor
to either find a direct match or to allocate a new, globally shared
color map entry. But this is slow and instantly eats up all available
colorcells (other apps might depend on). We use a more sophisticated
scheme. During native lib initialization, in case of a PseudoColor
visual) all colormap entries are queried and stored in Pseudo.Clrs .
From this array, a sorted array (Pseudo.RgbSum )
containing the RGB sums of each cell is computed, the corresponding
index to the colormap entry is stored in another array (Pseudo.Idx ).
Each time an RGB to pixel conversion is requested, function pseudoPixelOfRgb()
first computes the RGB sum of the requested color. It
then looks up (by bisection) a range of entries in Pseudo.RgbSum which
have a tolerable RGB sum difference. Finally, it iterates through
this range computing the sums of the absolute component differences
of each cell. The entry with the minimal difference makes the game
(is the ``closest'' color), but only if its value does not exceed a
certain maximum (in which case a new cell is requested by XAllocColor).
On the average, this mechanism requires just about 30 iterations (compared
with 200 for a linear search in the colormap). It is significantly
faster in case of frequent color conversions (e.g. during computation
of scaled images).
Pixel to RGB conversion is trivial to perform with the Pseudo.Clrs array (simple index access).
True and DirectColor visuals are much easier to handle. To increase performance, specific Java RGB to pixel RGB transform parameters are computed during lib initialization (mask and shift value for each color component). A direct equivalence (8-8-8 pixel RGBs), which does not have to be a DirectColor visual, is indicated by the flag Rgb.Direct .
It is important to note that none of the above measures would be fast enough to replace local caching of pixel values in the pData field of Java Color objects. This technique, which can be essential for applications making intensive use of colors, is transparently implemented in function getPixelValue() .
Standard Java colors (static color objects) are initialized like this during lib initialization. This is important in case of a PseudoColor visual because there might not be an available colorcell any more ate the time the color is requested (leading to variable colors in different runs of the same application).
There are no such hidden obstacles within font.c. The only thing worth to mention about its font support is a little extension we implemented in loadFont() . If the font name is not one of the standard Java names (``Helvetica'', ``Courier'', etc.), it is used verbatim for the first two components of an X font specification. This allows the (non-portable) use of all available fonts.
Things might be different on other platforms. The OS/2 Presentation Manager, for instance, requires a font to be created in the context of a PS (=presentation space, some sort of an GC). Accessing this font for use with this PS via its handle is fast. Using this handle without the PS it was created for is not allowed. Creating fonts (obtaining a new handle for another PS) is slow. Since the current native layer implementation just caches GCs (independently of fonts), and makes unconstraint usage of the cached GCs, this is a problem which still remains unsolved.
Most of the important facts for implementing this module already have been mentioned in the description of class Image. This class (once initialized) holds the complete pixel information as Java types (int[] or byte[]). Each time an Image object has to be displayed by drawImage() (in graphics.c), setXImage() converts this pixel data into a corresponding XImage. This XImage object is created during lib initialization, only its data member needs to be allocated by setXImage(). This is further minimized by using a global buffer (expanded in case its initial size does not already fit the request) for the pixel data.
Drawing scaled images is handled the same way. The current scaling algorithm has its strengths in conserving all pixel information (by extensive interpolation), but is not very efficient.
This implementation of the native layer does not use C-to-Java callbacks.
Mainly because this is one of the less portable things in Java, but
also because it seems to be slow (at least in some Java implementations).
Communication from C to Java is performed by setting Java object field
values. Most of them have primitive types (int, long etc.), a fact
that might be important for some garbage collectors (which depend
on knowing about storing references within Java objects). Most fields
are accessed directly, but there are some nasty exceptions (like ..unhand(unhand(peer)-Target)-
width..
in display.c) which are hard to circumvent.
Heap operations are avoided, most of the required memory is allocated during lib initialization.
At the moment, we have three groups of classes outside of these packages:
Even if the package implies something else, this class is ours, not from Sun. It is some kind of a Trojan horse to enable non-peer Components (we call them Decorations) which otherwise would not be possible because of the package default Component constructor (not accessible from outside of java.awt). However, decorations are such a useful feature (for text labels, gifs, boxes etc.) that we decided to go with this little hack. What we like about them is the fact that they are really flyweight objects whose geometry handling can be done consistently by a LayoutManager. And it really was a breeze to implement them (except of the above obstacle).
This Postscript Graphics implementation belongs to the great jlpr package of E.J.Friedman-Hill from Sandia National Lab (ejfried@ca.sandia.gov, http://herzberg.ca.sandia.gov). We put it in here just because of convenience. Within the biss framework, we use the unmodified PSGr as a base class for a more specialized Graphics implementation which is utilized for printing (screen snapshots, pane contents and whole documents). The biss.awt framework is ideal for this purpose because we do all painting inside of Java and therefor just have to (temporarily) switch Graphics instances (from screen to Postscript) in order to get the printing almost for free. However, printing is beyond the scope of this document.
The well known JAS (Java bytecode assembler) package of K.B. Sriram (availabe from http://www.blackdown.org/~kbs/jas.html) is used for on-the-fly bytecode creation (e.g. by the biss.jde.DesignPreviewer). Again, we are just standing on the shoulders of giants here. The unmodified JAS classes are just included to ease installation of the BISS AWT.