The Java AWT Native Interface Specification and Guide
Introduction
The Java AWT Native Interface (JAWT) comprises a small set of native (eg C language-based) APIs that provide a standard supported way for interaction between Java API windows and surfaces, and platform native API windows and surfaces. Non-Java libraries may then render to a Java owned window.
Note: in this document the terms "Java AWT Native Interface", "AWT Native Interface" and "JAWT" are interchangeable and refer to this same specification.
The fundamental obstacle to native rendering without JAWT is that
the rendering code cannot identify where to draw.
The native code needs access to information about a Java
drawing surface (such as a handle to the underlying native ID of a
Canvas
), but cannot get it.
JAWT should be supported in all headful implementations where technically possible although this is not enforced by the JCK. There is a platform-specific and a platform independent portion to the API, to account for the differing data structures and requirements of each platform. This document specifies the platform independent portions and also documents the platform dependent portions for the Oracle JDK supported desktop operating environments. For AWT the term platform is less tied to the underlying operating system than it is to the desktop windowing environment.
Reasons for using the AWT Native Interface include
- Use of a 3rd party native library not available in Java
- A temporary porting aid before converting legacy code to Java
- Rendering performance available only to native hardware accelerated APIs
- Interoperation with another toolkit
Drawbacks include
- A more complex application implementation, eg for painting
- Potential for application instability if the native library does not interoperate properly with AWT.
- Increased application delivery complexity - per platform binaries
An example illustrating how easy it is to use the AWT Native Interface is presented and discussed later in this document.
JAWT usage depends on JNI
The definition of Java Standard Edition includes JNI, the Java Native Interface. Many Java developers will never need to use it, but the interface is the only standard supported way for a Java language program to interact directly with application code that has been compiled to the native machine instructions for the host processor architecture. JNI is used where ever there is a need for mixed languages. These are by no means limited to cases like AWT. For example, you could use JNI to integrate with native code that communicates with a peripheral device, such as a scanner, connected to a system via a USB port.
So JNI is general enough to be used to access almost any sort of native library. The rest of this document assumes a familiarity with how to use JNI.
How to use JAWT
In this section we describe the most common usage of the AWT
Native Interface — overriding the paint
method to
direct drawing operations to a native rendering library which then
queries the Java VM to determine the information it needs in order
to render. Note, however, that any native code may use the AWT
Native Interface to learn about a target drawing surface, not just
code in a paint
method.
The first step in hooking up a native rendering library to a
Java Canvas
is to define a new class that extends
Canvas
and overrides the paint
method. The Java
system routes all drawing operations for a Canvas
object
through the paint
method, as it does for all other GUI
objects. Canvas is a good candidate for the rendering surface as
it does not have any content as a Button would.
The new paint
method, to be implemented in the native
rendering library, must be declared as public native void
, and the native library itself is loaded at runtime by including a
call to System.loadLibrary( "myRenderingLib")
in
the static
block of the class. The myRenderingLib
name is used for the native shared library; for Linux, the actual name
for the library file on disk is libmyRenderingLib.so
.
Here is a simple example of such a class:
import java.awt.*; import java.awt.event.*; public class MyCanvas extends Canvas { static { System.loadLibrary("myRenderingLib"); } public native void paint(Graphics g); public static void main(String[] args) { Frame f = new Frame(); f.setBounds(0, 0, 500, 110); f.add(new MyCanvas()); f.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent ev) { System.exit(0); } } ); f.show(); } }
Note that this class has a main
method that can be used
to run this code as an application for testing purposes.
The next step is to compile the MyCanvas
class above and
generate a C/C++ header file that describes the interface to the native
paint
method that Java expects to be used:
javac MyCanvas.java -h outputdir
The final step — and the most interesting one — is to
write the native rendering method, with an interface that conforms
to the header file that javac -h
generated, and build it as a
standard shared library (called myRenderingLib
in the
above example) by linking it, against the appropriate JDK provided
$JDK_HOME/lib/$JAWT_LIB library for the target platform.
Where JAWT_LIB has the base name "jawt" and follows platform
shared object naming rules. i.e.:
- Windows:
jawt.dll
- MacOS:
libjawt.dylib
- Linux:
libjawt.so
This code will call back to the Java virtual machine to
get the drawing surface information it needs to access the
MyCanvas
peer. Once this information is available, the
code can draw directly to MyCanvas
using standard drawing
routines supplied by the underlying operating system.
Here is sample source code for a native paint
method
designed for use in a X11-based drawing environment (Linux)
and a Java VM where the AWT Native Interface is present:
#include "MyCanvas.h" #include "jawt_md.h" /* * Class: MyCanvas * Method: paint * Signature: (Ljava/awt/Graphics;)V */ JNIEXPORT void JNICALL Java_MyCanvas_paint (JNIEnv* env, jobject canvas, jobject graphics) { JAWT awt; JAWT_DrawingSurface* ds; JAWT_DrawingSurfaceInfo* dsi; JAWT_X11DrawingSurfaceInfo* dsi_x11; jboolean result; jint lock; GC gc; short i; char *testString = "^^^ rendered from native code ^^^"; /* Get the AWT */ awt.version = JAWT_VERSION_9; if (JAWT_GetAWT(env, &awt) == JNI_FALSE) { printf("AWT Not found\n"); return; } /* Get the drawing surface */ ds = awt.GetDrawingSurface(env, canvas); if (ds == NULL) { printf("NULL drawing surface\n"); return; } /* Lock the drawing surface */ lock = ds->Lock(ds); if((lock & JAWT_LOCK_ERROR) != 0) { printf("Error locking surface\n"); awt.FreeDrawingSurface(ds); return; } /* Get the drawing surface info */ dsi = ds->GetDrawingSurfaceInfo(ds); if (dsi == NULL) { printf("Error getting surface info\n"); ds->Unlock(ds); awt.FreeDrawingSurface(ds); return; } /* Get the platform-specific drawing info */ dsi_x11 = (JAWT_X11DrawingSurfaceInfo*)dsi->platformInfo; /* Now paint */ gc = XCreateGC(dsi_x11->display, dsi_x11->drawable, 0, 0); XSetBackground(dsi_x11->display, gc, 0); for (i=0; i<36;i++) { XSetForeground(dsi_x11->display, gc, 10*i); XFillRectangle(dsi_x11->display, dsi_x11->drawable, gc, 10*i, 5, 90, 90); } XSetForeground(dsi_x11->display, gc, 155); XDrawImageString(dsi_x11->display, dsi_x11->drawable, gc, 100, 110, testString, strlen(testString)); XFreeGC(dsi_x11->display, gc); /* Free the drawing surface info */ ds->FreeDrawingSurfaceInfo(dsi); /* Unlock the drawing surface */ ds->Unlock(ds); /* Free the drawing surface */ awt.FreeDrawingSurface(ds); }
The key data structure here is JAWT
, which is defined
in jawt.h
(included by jawt_md.h)
; it provides
access to all the information the native code needs to get the job
done. The first part of the native method is boilerplate: it
populates the JAWT
structure, gets a
JAWT_DrawingSurface
structure, locks the surface (only one
drawing engine at a time, please!), then gets a
JAWT_DrawingSurfaceInfo
structure that contains a pointer
(in the platformInfo
field) to the necessary
platform-specific drawing information. It also includes the
bounding rectangle of the drawing surface and the current clipping
region.
The structure of the information pointed to by
platformInfo
is defined in a machine-dependent header file
called jawt_md.h
. For X11 drawing, it includes
information about the X11 display and X11 drawable associated with
MyCanvas
. After the drawing operations are completed,
there is more boilerplate code as JAWT_DrawingSurfaceInfo
is freed and JAWT_DrawingSurface
is unlocked and
freed.
The corresponding code for the GDI API on the Microsoft Windows platform would
be structured similarly, but would include the version of
jawt_md.h
for Microsoft Windows and the structure located
in the platformInfo
field of drawing surface info would be
cast as a JAWT_Win32DrawingSurfaceInfo*
. And, of course,
the actual drawing operations would need to be changed to those
appropriate for the Microsoft Windows platform.
The same also for MacOS.
Summary
The ability to draw directly into a Java Canvas
from a
native code library is extremely useful for developers planning to
migrate a legacy software system to Java, especially one that
includes a high-performance rendering engine. It makes it much
easier to migrate in stages, leaving performance-sensitive
rendering code alone, while other less-sensitive portions of code
are converted to Java. The result can be a modern Java-centric
application, providing the benefit of portability and development
efficiency, but one that does not sacrifice an investment in
performance of a key piece of native code.
References
The definitive reference to the Java Native Interface is The Java Native Interface: Programmer's Guide and Specification by Sheng Liang. This book was published in June 1999 by Addison-Wesley. The ISBN is 0-201-32577-2.
Appendix
Header Files for jawt.h and jawt_md.h
jawt.h
#ifndef _JAVASOFT_JAWT_H_ #define _JAVASOFT_JAWT_H_ #include "jni.h" #ifdef __cplusplus extern "C" { #endif /* * AWT native interface. * * The AWT native interface allows a native C or C++ application a means * by which to access native structures in AWT. This is to facilitate moving * legacy C and C++ applications to Java and to target the needs of the * developers who need to do their own native rendering to canvases * for performance or other reasons. * * Conversely it also provides mechanisms for an application which already * has a native window to provide that to AWT for AWT rendering. * * Since every platform may be different in its native data structures * and APIs for windowing systems the application must necessarily * provided per-platform source and compile and deliver per-platform * native code to use this API. * * These interfaces are not part of the Java SE specification and * a VM is not required to implement this API. However it is strongly * recommended that all implementations which support headful AWT * also support these interfaces. * */ /* * AWT Native Drawing Surface (JAWT_DrawingSurface). * * For each platform, there is a native drawing surface structure. This * platform-specific structure can be found in jawt_md.h. It is recommended * that additional platforms follow the same model. It is also recommended * that VMs on all platforms support the existing structures in jawt_md.h. * ******************* * EXAMPLE OF USAGE: ******************* * * On Microsoft Windows, a programmer wishes to access the HWND of a canvas * to perform native rendering into it. The programmer has declared the * paint() method for their canvas subclass to be native: * * * MyCanvas.java: * * import java.awt.*; * * public class MyCanvas extends Canvas { * * static { * System.loadLibrary("mylib"); * } * * public native void paint(Graphics g); * } * * * myfile.c: * * #include "jawt_md.h" * #include <assert.h> * * JNIEXPORT void JNICALL * Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics) * { * JAWT awt; * JAWT_DrawingSurface* ds; * JAWT_DrawingSurfaceInfo* dsi; * JAWT_Win32DrawingSurfaceInfo* dsi_win; * jboolean result; * jint lock; * * // Get the AWT. Request version 9 to access features in that release. * awt.version = JAWT_VERSION_9; * result = JAWT_GetAWT(env, &awt); * assert(result != JNI_FALSE); * * // Get the drawing surface * ds = awt.GetDrawingSurface(env, canvas); * assert(ds != NULL); * * // Lock the drawing surface * lock = ds->Lock(ds); * assert((lock & JAWT_LOCK_ERROR) == 0); * * // Get the drawing surface info * dsi = ds->GetDrawingSurfaceInfo(ds); * * // Get the platform-specific drawing info * dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo; * * ////////////////////////////// * // !!! DO PAINTING HERE !!! // * ////////////////////////////// * * // Free the drawing surface info * ds->FreeDrawingSurfaceInfo(dsi); * * // Unlock the drawing surface * ds->Unlock(ds); * * // Free the drawing surface * awt.FreeDrawingSurface(ds); * } * */ /* * JAWT_Rectangle * Structure for a native rectangle. */ typedef struct jawt_Rectangle { jint x; jint y; jint width; jint height; } JAWT_Rectangle; struct jawt_DrawingSurface; /* * JAWT_DrawingSurfaceInfo * Structure for containing the underlying drawing information of a component. */ typedef struct jawt_DrawingSurfaceInfo { /* * Pointer to the platform-specific information. This can be safely * cast to a JAWT_Win32DrawingSurfaceInfo on Microsoft Windows or a * JAWT_X11DrawingSurfaceInfo on Linux. On MacOS this is a * pointer to a NSObject that conforms to the JAWT_SurfaceLayers protocol. * See jawt_md.h for details. */ void* platformInfo; /* Cached pointer to the underlying drawing surface */ struct jawt_DrawingSurface* ds; /* Bounding rectangle of the drawing surface */ JAWT_Rectangle bounds; /* Number of rectangles in the clip */ jint clipSize; /* Clip rectangle array */ JAWT_Rectangle* clip; } JAWT_DrawingSurfaceInfo; #define JAWT_LOCK_ERROR 0x00000001 #define JAWT_LOCK_CLIP_CHANGED 0x00000002 #define JAWT_LOCK_BOUNDS_CHANGED 0x00000004 #define JAWT_LOCK_SURFACE_CHANGED 0x00000008 /* * JAWT_DrawingSurface * Structure for containing the underlying drawing information of a component. * All operations on a JAWT_DrawingSurface MUST be performed from the same * thread as the call to GetDrawingSurface. */ typedef struct jawt_DrawingSurface { /* Cached reference to the Java environment of the calling thread. * If Lock(), Unlock(), GetDrawingSurfaceInfo() or * FreeDrawingSurfaceInfo() are called from a different thread, * this data member should be set before calling those functions. */ JNIEnv* env; /* Cached reference to the target object */ jobject target; /* * Lock the surface of the target component for native rendering. * When finished drawing, the surface must be unlocked with * Unlock(). This function returns a bitmask with one or more of the * following values: * * JAWT_LOCK_ERROR - When an error has occurred and the surface could not * be locked. * * JAWT_LOCK_CLIP_CHANGED - When the clip region has changed. * * JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed. * * JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed */ jint (JNICALL *Lock) (struct jawt_DrawingSurface* ds); /* * Get the drawing surface info. * The value returned may be cached, but the values may change if * additional calls to Lock() or Unlock() are made. * Lock() must be called before this can return a valid value. * Returns NULL if an error has occurred. * When finished with the returned value, FreeDrawingSurfaceInfo must be * called. */ JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo) (struct jawt_DrawingSurface* ds); /* * Free the drawing surface info. */ void (JNICALL *FreeDrawingSurfaceInfo) (JAWT_DrawingSurfaceInfo* dsi); /* * Unlock the drawing surface of the target component for native rendering. */ void (JNICALL *Unlock) (struct jawt_DrawingSurface* ds); } JAWT_DrawingSurface; /* * JAWT * Structure for containing native AWT functions. */ typedef struct jawt { /* * Version of this structure. This must always be set before * calling JAWT_GetAWT(). It affects the functions returned. * Must be one of the known pre-defined versions. */ jint version; /* * Return a drawing surface from a target jobject. This value * may be cached. * Returns NULL if an error has occurred. * Target must be a java.awt.Component (should be a Canvas * or Window for native rendering). * FreeDrawingSurface() must be called when finished with the * returned JAWT_DrawingSurface. */ JAWT_DrawingSurface* (JNICALL *GetDrawingSurface) (JNIEnv* env, jobject target); /* * Free the drawing surface allocated in GetDrawingSurface. */ void (JNICALL *FreeDrawingSurface) (JAWT_DrawingSurface* ds); /* * Since 1.4 * Locks the entire AWT for synchronization purposes */ void (JNICALL *Lock)(JNIEnv* env); /* * Since 1.4 * Unlocks the entire AWT for synchronization purposes */ void (JNICALL *Unlock)(JNIEnv* env); /* * Since 1.4 * Returns a reference to a java.awt.Component from a native * platform handle. On Windows, this corresponds to an HWND; * on Linux, this is a Drawable. For other platforms, * see the appropriate machine-dependent header file for a description. * The reference returned by this function is a local * reference that is only valid in this environment. * This function returns a NULL reference if no component could be * found with matching platform information. */ jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo); /** * Since 9 * Creates a java.awt.Frame placed in a native container. Container is * referenced by the native platform handle. For example on Windows this * corresponds to an HWND. For other platforms, see the appropriate * machine-dependent header file for a description. The reference returned * by this function is a local reference that is only valid in this * environment. This function returns a NULL reference if no frame could be * created with matching platform information. */ jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo); /** * Since 9 * Moves and resizes the embedded frame. The new location of the top-left * corner is specified by x and y parameters relative to the native parent * component. The new size is specified by width and height. * * The embedded frame should be created by CreateEmbeddedFrame() method, or * this function will not have any effect. * * java.awt.Component.setLocation() and java.awt.Component.setBounds() for * EmbeddedFrame really don't move it within the native parent. These * methods always locate the embedded frame at (0, 0) for backward * compatibility. To allow moving embedded frames this method was * introduced, and it works just the same way as setLocation() and * setBounds() for usual, non-embedded components. * * Using usual get/setLocation() and get/setBounds() together with this new * method is not recommended. */ void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame, jint x, jint y, jint w, jint h); /** * Since 9 * Synthesize a native message to activate or deactivate an EmbeddedFrame * window depending on the value of parameter doActivate, if "true" * activates the window; otherwise, deactivates the window. * * The embedded frame should be created by CreateEmbeddedFrame() method, or * this function will not have any effect. */ void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env, jobject embeddedFrame, jboolean doActivate); } JAWT; /* * Get the AWT native structure. This function returns JNI_FALSE if * an error occurs. */ _JNI_IMPORT_OR_EXPORT_ jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt); /* * Specify one of these constants as the JAWT.version * Specifying an earlier version will limit the available functions to * those provided in that earlier version of JAWT. * See the "Since" note on each API. Methods with no "Since" * may be presumed to be present in JAWT_VERSION_1_3. */ #define JAWT_VERSION_1_3 0x00010003 #define JAWT_VERSION_1_4 0x00010004 #define JAWT_VERSION_1_7 0x00010007 #define JAWT_VERSION_9 0x00090000 #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !_JAVASOFT_JAWT_H_ */
jawt_md.h (Linux/X11 operating environment version)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Intrinsic.h> #include "jawt.h" #ifdef __cplusplus extern "C" { #endif /* * X11-specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ typedef struct jawt_X11DrawingSurfaceInfo { Drawable drawable; Display* display; VisualID visualID; Colormap colormapID; int depth; } JAWT_X11DrawingSurfaceInfo; #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */
jawt_md.h (Microsoft Windows version)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include <windows.h> #include "jawt.h" #ifdef __cplusplus extern "C" { #endif /* * Microsoft Windows specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ typedef struct jawt_Win32DrawingSurfaceInfo { /* Native window, DDB, or DIB handle */ union { HWND hwnd; HBITMAP hbitmap; void* pbits; }; /* * This HDC should always be used instead of the HDC returned from * BeginPaint() or any calls to GetDC(). */ HDC hdc; HPALETTE hpalette; } JAWT_Win32DrawingSurfaceInfo; #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */
jawt_md.h (MacOS version)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include "jawt.h" #ifdef __OBJC__ #import <QuartzCore/CALayer.h> #endif #ifdef __cplusplus extern "C" { #endif /* * MacOS specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ /* * When calling JAWT_GetAWT with a JAWT version less than 1.7, you must pass this * flag or you will not be able to get a valid drawing surface and JAWT_GetAWT will * return false. This is to maintain compatibility with applications that used the * interface with Java 6 which had multiple rendering models. This flag is not necessary * when JAWT version 1.7 or greater is used as this is the only supported rendering mode. * * Example: * JAWT awt; * awt.version = JAWT_VERSION_1_4 | JAWT_MACOSX_USE_CALAYER; * jboolean success = JAWT_GetAWT(env, &awt); */ #define JAWT_MACOSX_USE_CALAYER 0x80000000 /* * When the native Cocoa toolkit is in use, the pointer stored in * JAWT_DrawingSurfaceInfo->platformInfo points to a NSObject that conforms to the * JAWT_SurfaceLayers protocol. Setting the layer property of this object will cause the * specified layer to be overlaid on the Components rectangle. If the window the * Component belongs to has a CALayer attached to it, this layer will be accessible via * the windowLayer property. */ #ifdef __OBJC__ @protocol JAWT_SurfaceLayers @property (readwrite, retain) CALayer *layer; @property (readonly) CALayer *windowLayer; @end #endif #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */