Java AWTネイティブ・インタフェース仕様とガイド
入門
Java AWTネイティブ・インタフェース(JAWT)は、Java APIウィンドウとサーフェス、およびプラットフォームのネイティブAPIウィンドウとサーフェス間の相互作用のための標準サポートされた方法を提供するネイティブ(例:C言語ベース) APIの小さなセットで構成されています。 非JavaライブラリはJava所有のウィンドウにレンダリングすることができます。
ノート: この文書では、"Java AWT Native Interface"、"AWTネイティブ・インタフェース"、"JAWT"という用語は同じ意味で使用できます。
JAWTを使用しない場合の基本的な障害物は、レンダリング・コードで描画先を識別できないことです。 ネイティブ・コードは、Java描画表面(たとえば、Canvas
の基礎となるネイティブIDに対するハンドルなどです)に関する情報へのアクセスを必要としますが、取得はできません。
JAWTは技術的に可能なすべての面倒な実装でサポートされるべきですが、これはJCKによって強制されません。 APIにはプラットフォーム固有の部分とプラットフォームに依存しない部分があり、各プラットフォームの異なるデータ構造と要件を考慮しています。 このドキュメントでは、プラットフォームに依存しない部分について説明し、Oracle JDKでサポートされているデスクトップ操作環境のプラットフォーム依存部分についても説明します。 AWTの場合、プラットフォームという用語は、デスクトップのウィンドウ環境に比べて、基本となるオペレーティング・システムに結びついていません。
AWT Native Interfaceを使用する理由は次のとおりです。
- Javaで使用できないサードパーティのネイティブ・ライブラリの使用
- レガシー・コードをJavaに変換する前の一時的な移植援助
- ネイティブ・ハードウェア・アクセラレーションAPIでのみレンダリングのパフォーマンスを利用できます
- 別のツールキットとの相互運用
欠点には
- より複雑なアプリケーションの実装、例えばペイント用
- ネイティブ・ライブラリがAWTと適切に相互運用できない場合、アプリケーションが不安定になる可能性があります。
- プラットフォームのバイナリあたりのアプリケーション配信の複雑性の増加
AWTネイティブ・インタフェースの使い方の例は、このドキュメントの後半で説明します。
JAWTの使用方法はJNIに依存
Java Standard Editionの定義には、JNI (Java Native Interface)が含まれます。 多くのJava開発者はこれを使用する必要はありませんが、Java言語プログラムがホスト・プロセッサ・アーキテクチャのネイティブ・マシン命令にコンパイルされたアプリケーション・コードと直接対話する唯一の標準サポートされた方法です。 JNIは、これまで混在言語が必要な場所で使用されています。 これらは決してAWTのようなケースに限定されません。 たとえば、JNIを使用して、USBポート経由でシステムに接続されたスキャナなどの周辺機器とやり取りするネイティブ・コードと統合できます。
したがって、JNIは、ほぼあらゆる種類のネイティブ・ライブラリにアクセスするのに十分一般的です。 このドキュメントの残りの部分では、JNIの使い方に精通しています。
JAWTの使い方
このセクションでは、AWT Native Interfaceの最も一般的な使い方について説明 - paint
メソッドをオーバーライドして、ネイティブのレンダリング・ライブラリに描画操作を行ってから、Java VMを問い合せて、レンダリングするために必要な情報を確認します。 ただし、paint
メソッド内のコードだけでなくネイティブ・コードが、AWT Native Interfaceを使ってターゲット描画サーフェスに関する情報を学習することもできます。
ネイティブ・レンダリング・ライブラリをJava Canvas
に接続するための最初のステップは、Canvas
を継承してpaint
メソッドをオーバーライドする新しいクラスを新たに定義することです。 Javaシステムは、ほかのすべてのGUIオブジェクトの場合と同様に、Canvas
オブジェクトのためのすべての描画オペレーションをpaint
メソッドで経路指定します。 キャンバスは、ボタンのようにコンテンツを持たないため、レンダリング・サーフェスに適しています。
新しいpaint
メソッドは、ネイティブ・レンダリング・ライブラリ内に実装するために、public native void
として宣言する必要があります。ネイティブ・ライブラリ自体は、クラスのstatic
ブロック内でSystem.loadLibrary( "myRenderingLib")
呼出しを含めることによって、実行時にロードされます。 myRenderingLib
名はネイティブ共有ライブラリに使用されます。Linuxの場合、ディスク上のライブラリ・ファイルの実際の名前はlibmyRenderingLib.so
です。
そのようなクラスの簡単な例を示します。
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(); } }
このクラスには、テスト用のアプリケーションとしてこのコードを実行する場合に使用できるmain
メソッドがあります。
次のステップでは、前述のMyCanvas
クラスをコンパイルし、c/C++++ヘッダー・ファイルを生成します。このファイルは、Javaで使用されると予想されるネイティブpaint
メソッドへのインタフェースについて記述します:javac MyCanvas.java -h outputdir
最後のステップ - かつ最も興味深い問題 - javac -h
が生成したヘッダー・ファイルに準拠したインタフェースを使用して、システム固有のレンダリング・メソッドを記述し、ターゲット・プラットフォーム用に適切なJDKが提供している$JDK_HOME/lib/$JAWT_LIBライブラリにリンクすることによって、標準の共有ライブラリ(前述の例で、myRenderingLib
と呼びました)としてビルドします。 JAWT_LIBはベース名"jawt"を持ち、プラットフォームの共有オブジェクト命名規則に従います。すなわち:
- Windows:
jawt.dll
- MacOS:
libjawt.dylib
- Linux:
libjawt.so
このコードは、MyCanvas
ピアへのアクセスに必要な描画サーフェス情報を取得するために、Java仮想マシンにコールバックします。 この情報が利用可能になると、コードは基盤となるオペレーティング・システムによって提供される標準描画ルーチンを使用してMyCanvas
に直接描画できます。
次に、AWTネイティブ・インタフェースが存在するX11-based描画環境(Linux)とJava VMで使用するように設計されたネイティブpaint
メソッドのサンプル・ソース・コードを示します:
#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); }
ここでの重要なデータ構造は、jawt.h
(jawt_md.h
によって取り込まれる)に定義されている、JAWT
です。これは、ジョブを完了するためにネイティブ・コードが必要とするすべての情報へのアクセスを提供します。 ネイティブ・メソッドの最初の部分はボイラープレートです。JAWT
構造を取得し、JAWT_DrawingSurface
構造を取得し、サーフェスをロック(一度に1つの描画エンジンのみにしてください)してから、必要なプラットフォーム固有描画情報へのポインタ(platformInfo
フィールド内)を含むJAWT_DrawingSurfaceInfo
構造を取得します。 描画サーフェスの境界矩形および現在のクリッピング領域も含んでいます。
platformInfo
によってポイントされている情報の構造は、jawt_md.h
と呼ばれるマシン依存ヘッダー・ファイルに定義されています。 X11描画では、X11の表示や、MyCanvas
に関連付けられたX11描画に関する情報が含まれます。 描画オペレーションが完了したあとに、JAWT_DrawingSurfaceInfo
が解放されて、JAWT_DrawingSurface
がロック解除および解放されると、ボイラープレート・コードが増えています。
Microsoft Windowsプラットフォーム上のGDI APIに対応するコードは同様に構造化されますが、Microsoft Windows用のjawt_md.h
のバージョンと描画表面情報のplatformInfo
フィールドにある構造がJAWT_Win32DrawingSurfaceInfo*
としてキャストされます。 また、実際の描画オペレーションはもちろん、Microsoft Windowsプラットフォームに適したものに変更される必要があります。 MacOSでも同じです。
サマリー
ネイティブ・コード・ライブラリからJava Canvas
に直接レンダリングできることは、レガシー・ソフトウェア・システム(特に、高性能レンダリング・エンジンを含むもの)をJavaに移行することを計画している開発者にとって非常に役立ちます。 これにより、段階的な移行がかなり容易になります(パフォーマンスが重要なレンダリング・コードはそのままにし、それほど重要でないその他のコード部分がJavaに変換される)。 結果は、最新のJavaセントリック・アプリケーションになる可能性があります(移植性と開発効率性の利点を提供する一方、重要なネイティブ・コードのパフォーマンスへの投資を犠牲にしない)。
リファレンス
Java Native Interfaceに関しては、Sheng Liang著『The Java Native Interface: Programmer's Guide and Specification』を参照してください。 この書籍は、1999年6月にAddison-Wesleyから出版されました (ISBN: 0-201-32577-2)。
付録
jawt.hおよび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オペレーティング環境のバージョン)
#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版)
#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版)
#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_ */