はじめに

ここではOSF/Motifを利用したXアプリケーション開発に関連する話題を集めています。

特に目次を設けずに随時追加していくつもりなので、 キリがつくまで読みにくいと思いますが了承下さい。

想定する環境

2001年1月現在、私の元で簡単に確認できる環境であるがゆえに 原則としてFreeBSD4.x向けに記述していますが、 環境依存な記述はなるべく少なくなるように注意を払いながら書いていくつもりです。

文書中でシェルからの操作の記述が含まれることがあります。 ここではbashを想定した記述になっています。 csh系のシェルを利用されている方は適宜置き換えてご利用下さい。

想定するMotifのライブラリのバージョンはopen Motifとして公開された 2.1.30です。 実際、私も自分でコンパイルしてopen Motifを利用しています。

対象となる読者

ここででは読者が次の技術を持っていることを想定した書き方をしています。

皆さんご存知のとおり、UNIX版 Netscape NavigatorはMotif アプリケーションの代表作です。 もっとも最近のNavigatorは独自拡張のウィジェットを使いまくりなので、 Navigatorで実現されていることがすべて説明できるわけではありません。

CDE(Common Desktop Environment)を使ったことがない人でも 内容が把握できるようにしたいと思います。

Motifアプリケーションのコンパイルの仕方

利用するライブラリの確認

標準設定では /usr/dt 以下にMotif関係のファイルが格納されているみたいですが、 複数のバージョンが格納されていることもあるので (もしかしたら互換ライブラリLesstifが入っているかも)、 どれでコンパイル/リンクされるのか きちんと把握しておくのは重要です。

例:
$ locate libXm.
/usr/dt/lib/libXm.a
/usr/dt/lib/libXm.so
/usr/dt/lib/libXm.so.2.1

当然ながら標準Xライブラリの場所も把握しておく必要があります。

Motif関係のコンパイル済ライブラリとしては次のものがあります。

libXm
Motif Widget Set library
MotifのGUI(Graphical User Interface)を提供する部品類が含まれています。
libMrm
Motif Resouce Manager library
UIL(後述)と連動して利用する、ウィジェットのリソース管理機構が含まれています。
libUil
User Interface Language library
UILコンパイラの基本機能と、その呼び出しAPIが含まれています。
libXt
X Toolkit library
いわゆるXツールキット・ライブラリです。
libXp
X Print Server Extentison
Release6以後に追加されたXの拡張機能に対応します。 Motif 2.x APIでも利用されています。
libXmu
X Miscellaneous Utilities
雑多なライブラリということですが、 常に過去のリリースと互換性があるわけではありません。
libX11
X library
いわゆるXlibです。Xプロトコルを操作する標準のAPIを提供します。
libXext
X Extentison library
XlibをXの拡張機能に対応させます。 常に過去のリリースと互換性があるわけではありません。
libICE
Inter-Client Exchange library
libSM
Session Management library

きちんと分類されているとはいえ、多いですね。

コンパイラ ドライバに与える順は

-lUil -lMrm -lXm -lXmu -lXt -lXp -lxpg4 -lXext -lX11 -lICE -lSM
といったところでしょうか。

uilの解釈を内蔵させるアプリケーションはほとんどないと思うので、 一般には libUilが利用されることはないでしょう。

libxpg4はFreeBSD4.x以前の標準配布のライブラリで必要だったもので、 これを含めないと ロケール処理の関係でうまく日本語を扱えないことが知られています。

当面の慣れないうちは、盲目的にライブラリの指定をしてもよいでしょうけど、 頃合いを見て どのライブラリにどういうAPI(関数)が含まれているか確認しておくのも重要だと思います。 ライブラリのソースを直接 当たってもよいですが、nm(1)などを利用するのも手です。

例:
$ for i in /usr/X11R6/lib/lib*.so /usr/dt/lib/lib*.so; \
do echo $i ; nm $i |grep " T "; done |more

環境変数の設定

Makefileにてライブラリ等のパスを指定してもよいのですが、 gccが利用できるFreeBSDでは、環境変数でこれを指示することもできます。 Makefileの記述が簡単になるので、私はこちらの方法をお勧めしたいと思います。

$HOME/.profile 等 シェルのスタートアップ スクリプトで設定しておくとよいでしょう。

例:
XROOT=/usr/X11R6
DTROOT=/usr/dt
C_INCLUDE_PATH=$XROOT/include:$DTROOT/include:/usr/local/include
LIBRARY_PATH=$XROOT/lib:$DTROOT/lib:/usr/local/lib
export C_INCLUDE_PATH LIBRARY_PATH

サンプル コード

環境の確認ができたら、実際に簡単なプログラムをコンパイルしてみるとよいでしょう。

短いMotifプログラムを用意してみました。

welcome.c

#include <Xm/Text.h>
#include <X11/Shell.h>

main(int argc,char **argv)
{
    XtAppContext app_context;

    /* 言語関係の初期化 */
    XtSetLanguageProc(NULL,NULL,NULL);

    {
        /* 初期リソースの定義 */
        static char *fallback_resources[] = {
            "*text_buffer.width: 300",
            "*text_buffer.height: 200",
            "*text_buffer.value: Welcome to Motif world!",
            "*fontList: k14;rk14:",
            NULL,
        };

        /* トップレベルウィンドウの生成 */
        Widget top_level_shell = 
            XtVaOpenApplication(&app_context,"TextEditor",0,0,&argc,argv,
                fallback_resources,sessionShellWidgetClass,NULL);

        /* テキスト編集ウィジェット(複数行対応版)の生成 */
            XtVaCreateManagedWidget("text_buffer",xmTextWidgetClass,top_level_shell,
                XmNeditMode, XmMULTI_LINE_EDIT, NULL);

        /* Xサーバ側のリソースの生成 */
        XtRealizeWidget(top_level_shell);
    }

    /* イベント処理ループ */
    XtAppMainLoop(app_context);
}

ここではプログラムの意味は述べません。 とりあえずコメントから雰囲気でも掴んで下さい。

コンパイル例

次に示すのは実際のコンパイルの様子です。

うまくコンパイルできたなら、ぜひ動かしてみて下さい。

ldd(1)を使って具体的にどのようなライブラリがリンクされているか確認してみるのもよいでしょう。


$ cat Makefile
CFLAGS = -g -DNeedFunctionPrototypes=1 -DNeedWidePrototypes=0 \
 $(XINCDIR) $(MOTIFINCDIR) 

XLIB = -lxpg4 -lXext -lX11 
LIBXT = $(XLIBDIR) -lXmu -lXt -lXp $(XLIB) -lICE -lSM 
MOTIF = $(MOTIFLIBDIR) -lMrm -lXm 

welcome: welcome.o
	$(LINK.c) -o $@ welcome.o $(MOTIF) $(LIBXT) $(LDLIBS) 

$ make
cc -g -DNeedFunctionPrototypes=1 -DNeedWidePrototypes=0      -c -o welcome.o welcome.c
cc -g -DNeedFunctionPrototypes=1 -DNeedWidePrototypes=0       -o welcome welcome.o  -lMrm -lXm  -lXmu -lXt -lXp -lxpg4  -lXext -lX11  -lICE -lSM   

$ ./welcome &
[1] 49602
$ ldd welcome
welcome:
        libMrm.so => /usr/dt/lib/libMrm.so (0x28064000)
        libXm.so => /usr/dt/lib/libXm.so (0x28082000)
        libXmu.so.6 => /usr/X11R6/lib/libXmu.so.6 (0x2821d000)
        libXt.so.6 => /usr/X11R6/lib/libXt.so.6 (0x2822e000)
        libXp.so.6 => /usr/X11R6/lib/libXp.so.6 (0x28271000)
        libxpg4.so.2 => /usr/lib/libxpg4.so.2 (0x28278000)
        libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x2827c000)
        libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x28287000)
        libICE.so.6 => /usr/X11R6/lib/libICE.so.6 (0x28320000)
        libSM.so.6 => /usr/X11R6/lib/libSM.so.6 (0x28334000)
        libc.so.4 => /usr/lib/libc.so.4 (0x2833c000)
        libXThrStub.so.6 => /usr/X11R6/lib/libXThrStub.so.6 (0x283c8000)

ここでFreeBSD 4.x固有のもう一つの事情を紹介しておく必要があるでしょう。 マクロ定義オプションである -DNeedFunctionPrototypes=1 -DNeedWidePrototypes=0 は 附属のXFree86のライブラリを利用する上で必要なものです。 代わりに -DFUNCPROTO=15 としても構いません。 その根拠は Imake.tmpl と FreeBSD.cf と Xfuncproto.h にあります。 興味のある人は参照してみて下さい。

練習

    あなたの環境におけるライブラリの所在を確認して下さい
    find(1)あるいはlocate(1)を使ってlibXm.やlibXt.について調べるとよいでしょう。
    あなたの環境におけるCヘッダファイルの所在を確認して下さい
    同様にXm.hやIntrinsic.hについて調べるとよいでしょう。
    Motifアプリケーションをコンパイルする時のオプションを具体的にあげて下さい
    XコンソーシアムやOpenGroupの実装に基づくものは似たような傾向があります。 ただし環境によっては通信やスレッドのからみで、別途特別なオプションが必要になるかも知れません。
    サンプル コードwelcome.cを実際にコンパイルしてみましょう
    簡単なテキストエディタです。うまく動作しましたか?

GUIの分類

MotifのGUIを構成する部品はウィジェット(Widget)と呼ばれます。 ウィジェットは大きく次のように分類できます。

  1. トップレベル ウィンドウ … シェル:Shell, ダイアログ:Dialog
  2. 一つの入力要素 … プリミティブ:Primitive
  3. それぞれのルールに従って複数のウィジェットを管理するもの … マネージャ:Manager

トップレベル ウィンドウはウィンドウシステムにおいて それぞれが互いに干渉しないように独立して存在する窓(ウインドウ:Window)のことです。 感覚的にはルート ウィンドウの直の子ですが、 ウィンドウ マネージャが介入するのが一般的なので、 厳密には正しくありません。

シェルやダイアログがこの役を担当するウィジェットです。 これらは、一つだけしか子を持てません。 ほとんどの場合は何らかのマネージャが子になります。 非常に単純なものであればプリミティブを子にすることもあります。 (先のwelcome.cがそうです。)

プリミティブはいわゆる画面上のボタンや テキストの入力フィールド等のことです。 それぞれが特徴あるインタフェースを提供します。 隠蔽された内部事情はともかく、 一般には「イントリンシクス レベルでの子は存在しないもの」として扱います。

マネージャは通常プリミティブをそれぞれのルールに従って管理する機能があるものです。 例えば横に整列するとか、あるウィジェットを別のウィジェットの右や下に配置するとか 決めて配列するとかします。 メニューバーやポップアップ メニューがこれに該当します。

管理するウィジェットが決まっている場合もありますが、 (例えば、メニューバーにはカスケードボタンを配置することしか想定していない等) 直観的に考える範囲で有効そうなものは大抵、その管理下に置くことができます。

ここで見てもらうのは タイミング的に少し高度な内容ですが (まだ説明していないものがいくつも含まれているという意味で)、 それぞれのウィジェットの関係の把握に役立つクラス継承図を次に示します。

Basic Class Tree
図:コアクラスからの分岐

シェル ウィジェット

Motifアプリケーションを利用する際に意識するシェルは TopLevelShell, SessionShell, MenuShell, DialogShellですが、 Xtで利用できる全てのシェル関連ウィジェットの説明をしておきます。

Shell
コンポサイトを継承した全てのシェルのベースクラスです。 一つだけ子を持つことができます。通常アプリケーションから直接利用されることはありません。
OverrideShell
override_redirectフラグがTrueの状態で生成される、すなわち ウィンドウマネージャの装飾の影響を受けないトップレベル ウィンドウのベースとなるクラスです。 MotifではMenuShellがこれを利用しています。 これも通常アプリケーションから直接利用されることはありません。
WMShell
ウィンドウマネージャ プロトコルのハンドリングを実装しているクラスです。
VendorShell
ベンダー独自拡張機能を実装しているクラスです。
TopLevelShell
ツールキットレベルでの通常のトップレベルシェルを実装するクラスです。 通常アプリケーションが複数のトップレベルウィンドウを利用しようとする場合は、 これか次のTransientShellを利用します。
TransientShell
ツールキットレベルのもう一つのトップレベルウィンドウを提供するクラスです。 TopLevelShellシェルとの違いは、(ウィンドウマネージャがサポートしているなら) 同格のTopLevelShellよりもできるだけ手前に持ってこようとするのと、 あまり派手な装飾を付けないようにする点です。
ApplicationShell
Motifはこのシェルの実体が終了するタイミングで自動的に終了しようとするので、 Release 5までは これが最初に作成されるシェルとして普通に利用されていたのですが、 Release 6以後は機能拡張したSessionShellを利用するのが普通です。
SessionShell
Release 6から拡張されたセッションマネージャとの通信のための仕掛けが拡張された アプリケーションシェルです。
MenuShell
ウィンドウマネージャの装飾の影響を全く受けないようにするために OverrideShellを継承しています。ポップアップメニューやプルダウンメニューのベースとして利用します。
DialogShell
Motif固有の機能拡張を施したTransientShellです。

参考までに、実際のコーディングにおいては、簡易生成関数やUILを使い出すと DialogShellやMenuShellの生成を直接指示することはありません。 (少なくとも現時点での著者のプログラミングにおいてはですが…) その存在と関連パラメータを意識しておく くらいでしょうか。

シェルのクラス継承図を次に示します。

Shell Class Tree
図:シェルクラス継承図

サブクラスはスーパークラスの特徴を踏まえた上で、 より細かい機能が実装されているものです。 例えば、セッション シェルは、 その継承元であるアプリケーション シェル、トップレベル …と同じリソースを保持しており、 全てが完全に同じ意味で利用できるわけではありませんが、 おおよそ同じ性質を持っています。

附属のリファレンスマニュアルを参照する時も、 必要に応じてですが、全てのスーパークラスのマニュアルも辿って読みます。

その他 関連資料

Motifでの日本語の扱い

ウィジェットクラス階層

Motif生成関数とクラスの早見表

User Interface Language


E-mail: iwao@dsl.gr.jp