この頁は、基本的に下部の方ほど古い内容になります。(<hr>単位)


フォントを読み込む(マルチバイト対応)

x-move-text.cの ソースコードを合わせてご覧下さい。 これ単体でコンパイルして動作させることができます。

アプリケーションでテキストを表示させるには、 事前に利用するフォントをXサーバにロードしておく必要があります。 歴史的経緯によって、フォントのロードに関連するAPIは幾つかあるのですが、 ここでは、Release 5によって事実上標準になったと思われる フォントセットについて説明します。

フォントのロードに関係するAPI(Xlib)
API 説明
XCreateFontSetロケールに応じて複数のフォントを読みとる
XFreeFontSetフォントを解放する
XLoadFontフォントをロード(IDの取得)
XLoadQueryFontフォントのロードと全フォント情報の取得
XQueryFontIDから全フォント情報の取得の取得
XQueryTextExtentsIDから部分的にフォント情報の取得
XQueryTextExtents16IDから部分的にフォント情報の取得(16ビットコード対応版)
XUnloadFontフォントを解放

XCreateFontSetにはDisplayオブジェクトとフォント名を渡します。 英語はともかく、日本語では複数のコードセットを使われることが多く、 それに伴って複数のフォントも使われることがあります。 そういう時はカンマで区切って複数のフォント名を渡します。


char *default_font_name = "fixed,k14,*", *font_name = default_font_name;

Xのフォント名の解釈は、ある程度、融通がきくようになっており、 ワイルドカード文字「*」を含めるとフォント名を略記できるようになっています。 「*」は正規表現とは関係ありません。 詳細はXLFD;X Logical Font Descriptionに関する資料を参照して下さい。 (Xコンソーシアムによる配布に含まれています。)

アプリケーションの開発初期段階は なるべく多くの解釈ができるようにしたほうがよいと思います。 そしていろいろ試した方がよいです。

最終段階になると、デザインの問題もありますから、 フォント名の略記をなるべく使用せず、具体的に指定するようにします。 最終段階には、デザインの問題もありますから、 フォント名の略記をなるべく使用せず、具体的に指定するようにします。

テキストを描画する(マルチバイト対応)


char **missing_charset_list;
int missing_charset_count;
char *def_string;

/*  フォントの読み取り */
XFontSet font_set = 
    XCreateFontSet(display,base_font_name_list,
	&missing_charset_list,&missing_charset_count,&def_string);

/* 一応読み込めたが、一部失敗したものがある */
if (font_set && missing_charset_list) {
    int n = missing_charset_count;
    char **t = missing_charset_list;

    fprintf(stderr,"XCreateFontSet %s で読み込めないフォントがありました。\n"
        "%d\n", base_font_name_list, n);

    while (n--)
	fprintf(stderr,"%s\n",*t++);

    if (def_string)
	fprintf(stderr,"存在しないコードセットの描画に利用される: %s\n",def_string);

    XFreeStringList(missing_charset_list); /*  これを忘れるとメモりリーク */
}

この例ではマルチバイト テキストの描画に、 XmbDrawString XmbDrawImageStringを使っています。 一般には背景を壊さないで描画するために、 XmbDrawStringのほうがよく使われていると思います。 背景が単色ですむ端末エミュレータは、どちらかというと、 XmbDrawImageStringを使う傾向にあります。

いずれも描画に必要なパラメータは、Displayオブジェクト、描画対象:ドローワブル d, フォント font、グラフィックコンテキスト gc、座標(x,y)、テキスト s[len]です。

void draw_string(Display *display,Drawable d,XFontSet font,GC gc,int x,int y,char *s) {
    char *t;
    XFontSetExtents *extents = XExtentsOfFontSet(font);

    /* 改行を含む文字列を描画 */
    while((t = strchr(s,'\n')) || (t =  s + strlen(s), t > s)) {
        size_t len;
        XmbDrawImageString(display,d,font,gc,x,y,s,len = t - s);
        y += extents->max_ink_extent.height;
        if(!*t) break;
        s = t + 1;
    }
}

テキストを描画する(ワイドキャラクタ対応)

標準Cに対応した環境では、ワイドキャラクタ(wchar_t)が使えるはずです。 Xlibでもこれに対応したテキスト描画APIが提供されています。 XwcDrawString XwcDrawImageStringがそうです。 これらの使い分けは、Xmb のそれと同じです。

しかしながら、いわゆるPC-UNIXでは、パッケージ付属の物をそのままつかうと多分だめです。 理由は付属のコンパイラが適切にコンパイルされていなかったり、 Xlibが駄目だったりするからです。 どちらも英語圏対応にしかコンパイルされていないことが多いです。 (残念ながら代表格のRedHatやXFree86がその状態でバイナリ配布しています。)

自分でCコンパイラやXlibを再コンパイルするか、 Solarisを使うかすれば対処できます。

サンプル コードは また時間がある時に作ります。


ウィンドウを開く

x-win.cの ソースコードを合わせてご覧下さい。 これ単体でコンパイルして動作させることができます。

    if ((display = XOpenDisplay(server_name)) == NULL) {
        fprintf(stderr,"XOpenDisplay %s に失敗しました。\n"
            "Xサーバーに接続できません。\n", XDisplayName(server_name));
        return 1;
    }

プログラムの最初の段階で XOpenDisplay を使ってXサーバに接続します。 接続に成功すれば、Displayオブジェクト(Display構造体へのポインタ)が得られます。

ほとんどのXlib APIはこのDisplayオブジェクトを使います。 複数のXサーバーに接続する場合は、このオブジェクトでそれぞれを識別します。

プログラムが終了するまでこのオブジェクトは保持し続けます。 このプログラムでは利用していませんが、 XCloseDisplay を使って破棄することもできます。 破棄しないで終了したとしても(異常終了やシグナル割り込みで止めた場合)、 ライブラリや処理系が後始末をしてくれることになっているので 特に気にする必要はありません。 実際、x-win.cは 終了する仕掛けをつけていないので、 ウィンドウマネージャによる強制終了か、 端末エミュレータから割り込みをかけて(Ctrl-C)終了するしかありません。 (そういうプログラムは意外と多いのでは?特に入門書では)

一般的なUNIXシステムやではプロセスの終了でもってOSのリソースが解放されます。 デバッガを使っている時は、デバッガの終了のタイミングで解放されることもあります。 Windowsでもメイン・スレッドの終了でもって終了するようです。 (詳しい方、意見下さい)

    XMapWindow(display, window = 
        XCreateSimpleWindow(display,DefaultRootWindow(display),
            wx += 10, wy += 10, width = 300, height = 200, 
            border_width = 2,
            background = WhitePixel(display,DefaultScreen(display)),
            border = WhitePixel(display,DefaultScreen(display))));

Xlibでウィンドウを生成する際には、 XCreateSimpleWindow がよく使われます。 マニュアルで大抵同じ頁に含まれている XCreateWindow との違いは、大部分のパラメータを親から引き継ぐところにあります。 一般的なアプリケーションは、少なくとも一つ自分の描画用にウィンドウを生成します。 この自分用のウィンドウは「トップレベル・ウィンドウ」とよばれ、 その親はルート・ウィンドウになります。

ルート・ウィンドウに関する様々なパラメータはDisplayオブジェクトから マクロを利用して取り出せるようになっています。 XCreateSimpleWindow ではよく、この値を使います。

Displayオブジェクトから値を取り出すマクロ(一部)
DefaultRootWindowマクロルートウィンドウのIDを得る
DefaultScreenマクロスクリーン番号を得る
DefaultColormapマクロルートウィンドウのカラーマップIDを得る
WhitePixelマクロ ルート ウィンドウの白系の色のピクセル値を得る
BlackPixelマクロ ルート ウィンドウの黒系の色のピクセル値を得る

生成された直後のウィンドウは即座に表示されません XMapWindow の呼び出しによってマップ状態になり、 かつ その親が(その先に更に親がいるなら、それら全部が)表示されているなら、表示されます。

for(;;) {
    XEvent event;
    XNextEvent(display,&event);
    fprintf(stderr,"event:%d\n", event.type);
}

一般的なXアプリケーションでは、その最終段階で必ずループ処理に入ります。 ループさせないと即座に終了してしまいます。 ループ処理では、Xサーバーからのイベントを受け取って、 その種類に応じた処理をします。 大抵のイベントは利用者の何らかのアクションに応じた物ですが、 Xサーバーから一方的に送られてくる何らかの依頼のこともあります。

XNextEvent がイベントを受けとる代表的なAPIです。 x-win.cでは 受けとったイベントを単に表示させているだけです。

Xプロトコルとバッファリング

X11は最初からネットワークでの利用を考えて作られたシステムです。 そういう理由もあって、通信コストを非常に意識した作りになっています。 APIの分類ももちろんですが、個々のライブラリの造り(実装)もそうなっています。

実際には遅いシステムをチューニングをする時以外 通信の仕掛けを ほとんど意識することはないのですが、 「Xのリクエストはバッファリングされている」ということを 知らないと、たぶん近いうちに困ります。 少なくとも、デバッガを使った時に奇妙な現象に悩まされます。

Cの経験者であればstdioによる標準入出力が バッファリングされていることは 既に知っていることと思います。 これと似たようなことをXでもやっています。

具体的には、 直ちにリプライ(Reply;応答)を必要とするAPIが呼び出されるか、 リクエストバッファが溢れるか、もしくは リクエストバッファをフラッシュするAPIが呼び出されるまで Xサーバーに命令は飛びません。

Xtに基づくアプリケーションの場合は、 動作時にコマンドライン・オプション -synchronous を使うと Xプロトコルのバッファリングが行なわれなくなります。 Xlibのみを使っているなら、 コマンドラインオプションを受けて、 プログラムの初期段階で XSynchronize を呼び出すようにすればよいでしょう。

よく似た名前の XSyncは 目的が違うのでお間違いのないよう。

リプライというか正確にはイベントのキューイング(queuing)についてですが、 別の意味での注意が必要です。実はイベントをずっと取り出さないと、 どんどんキュー(queue)に蓄えられていきます。

仮にイベントをずっと取り出さないまま、 ひたすら描画しまくるプログラムを書いてしまうと メモリをどんどん消費していって、 最後にメモリ不足を理由に強制的にOSに停止させられてしまう アプリケーションになってしまいます。 ただ実際にこういうコードを書いてしまうのは特殊な場面でしょうね。

ウィンドウ・マネージャの介入

x-win.c を実際に動作させてみると、リファレンスマニュアルからは推測できない振舞いがあります。 ウィンドウを表示させる位置やウィンドウの縁の幅や色を指示しているのに 反映されていないのです。

この現象はウィンドウ・マネージャを動かしている時に起こります。 ウィンドウ・マネージャを動かしているのが普通なので、 通常この現象が生じます。 もしあなたがウィンドウ・マネージャを止めるやりかたがわかるなら、 ぜひ止めて試してみて下さい。

ウィンドウ・マネージャは通常、トップレベル・ウィンドウが マップされるタイミングで介入してきます。 具体的な振舞いはマネージャの設定によりますが、 ひと目でそれがトップレベル・ウィンドウとわかるように装飾をつけたり、 表示する前に利用者にウィンドウの位置や大きさを調整させたりします。

gdb等、デバッガでステップ実行してみると XMapWindow が呼び出されたあとにウィンド・ウマネージャが反応しているのがわかります。

ウィンドウ・マネージャは装飾をつける際に、 ウィンドウの親を入れ換えてこれを実現しています。 プログラムではルートウィンドウを親としてウィンドウを生成していますが、 いつの間にか親が変えられているので(実はICCCMという規約に従っています) 注意が必要です。

X標準コマンド xwininfo をつかってこれを確認できます。

$ xwininfo -tree 

xwininfo: Please select the window about which you
          would like information by clicking the
          mouse in that window.

xwininfo: Window id: 0x1c00002 (has no name)

  Root window id: 0x24 (the root window) (has no name)
  Parent window id: 0x1000273 (has no name)
     0 children.
$ 

ウィンドウ・マネージャを動かしていないなら、 下記のようにParent windowがRoot Windowになります。

$ xwininfo -tree 
xwininfo: Window id: 0x1c00002 (has no name)

  Root window id: 0x24 (the root window) (has no name)
  Parent window id: 0x24 (the root window) (has no name)
     0 children.
$ 

Application Programming for X

X Window Systemで動作させることを意識した アプリケーション作成に関する話題を扱っています。

Xは何を提供してくれるのか

XはUNIXの世界では広く使われています。 「UNIXでGUI(Graphical User Interface)を使いたければXを利用する」と説明する資料も多くあります。 それ自体は間違ったことを述べているわけではありませんが、 Xの性質をよく知らない人には誤解を与えてしまう表現だと思います。

Xウィンドウ・システム自体はGUIを提供することはありません。 GUIのためにXを利用しているだけなのです。

個人的には「Xはグラフィックスを異機種間でネットワーク越しで 制御する仕組みを提供するシステム」と表現するほうがすきです。

内容的に繰り返しになりますが、

  1. ネットワークの先のPCの画面を制御できる
  2. 逆に別のPCのプログラムの動作を手元のPCに表示させる
  3. 一つのキーボードで複数のPCを操作できる
    (実装によってはマルチ スクリーンも可能)
  4. 文字だけはなくグラフィックスを扱える
  5. もちろん自分のPCの画面も制御できる
のです。

Xウィンドウ・システムは -- 私の知る限り -- 広く普及している同等の機能のシステムの中では、 稼働機種が多く パフォーマンスも比較的よいという意味で 一番よくできた実装です。

利用できるプログラミング言語は

広く知られているものだけでも結構あります。

X自体はCで記述されています。 ライブラリも最終的にはCを利用しているというものがほとんどですが、そうでないものもあります。 (C++やJAVAやPerlでそういう形態のものがあります。)

この辺の事情はNetNewsの comp.windows.xに投稿されているFAQに詳しいです。

Xは仕様がきちんと定まっている上に、 それが公開されているシステムであるため、 多くの実装が存在するという 喜ばしい状態になりました。

そうなるように頑張った人達に感謝したいと思います。

C言語向けのライブラリ

いろんな人がいろんなライブラリを作成しています。 私が個人的に利用している/したことがあるのは次のとおりです。 (カタカナ表記は私が利用している呼び方であって、 一般的ではないかも知れません。)

Xlib
Xの低レベル ライブラリに位置付けられいます。 下記ライブラリで利用しています。
XView
最初はSun OSでのみ利用可能でしたが、 後にソースコードが公開されました。 しかし最近は これでプログラミングする人は少ないみたいです。
Xツールキット
イントリンシクス(Intrinsics)とも呼ばれます。 下記ライブラリで利用しています。
アテナ
Athena Widget Setは イントリンシクスで最初に作られたGUIライブラリです。 一番長い間、標準パッケージに含められて配布されていた(現在も)ため、 利用者は非常に多いと思われます。 ただし、あまりカッコのよい使われ方はされていません。
cf. Athena widgetの見栄えをマシにする、ささやかな努力 (inoue氏による)
モチーフ
OSF/Motifは長い間 商用として配布されてきたGUIライブラリです。 現在はソースコードが公開されています。 広く利用されているWEBブラウザの一つであるNetscape Navigatorが これを利用しています。 また、意識している人は少ないかも知れませんが、 JAVAのUNIX向けのAWT(Abstruct Window Toolkit)ライブラリでも 広く利用されています。
レスチフ
LesstifはMotifとAPI互換のライブラリです。 あのNetscape Navigatorもこれで作成できます。

これらはライブラリとしての層が異なるので、 同列に並べると奇妙な感がありますが、気にしないで下さい。

Xlibでプログラミング

XlibはCで利用するライブラリです。X標準ライブラリの一つです。 Xプロトコルに対応するAPI(Application Programing Interface)を提供してくれます。

この頁の読者には、Cのソースコードを自分で読めることを期待しています。 (わからない人は別に勉強して下さい。)

Xlibは次の機能をカバーしています。

  1. Xプロトコルに対応したウィンドウ制御
  2. ポインティング・デバイスの位置検出
  3. 幾何図形のプリミティブ(primitive)描画
  4. 各種色表現モデル
  5. 単純なマスク制御
  6. ラスタ イメージ(raster image)の転送
  7. 文字の描画
  8. フォント・メトリックス(font metrics)の取得
  9. (Xサーバー経由の)クライアント関通信
  10. サーバー保持の画像であるピックスマップ(pixmap)の操作
  11. Xリクエストのバッファリング(buffering)管理
  12. イベントのキュー(queue)管理
  13. ロケール機構に基づく文字コードの変換
  14. キーコードとキーシンボルと文字の対応
  15. キーボードからの文字列入力

Xlib関係のヘッダファイル

Xlibに関連するヘッダファイルとして次のようなものがあります。

<X11/Xlib.h>
構造体の定義や関数の宣言が含まれています。
<X11/X.h>
マクロによる定義が含まれています。 <X11/Xlib.h>から自動的に取り込まれます。
<X11/Xutil.h>
キーボード、ビジュアル、イメージ、 ウィンドウマネージャ関係のAPI定義が含まれています。
<X11/keysym.h>
キー・シンボル(Key Symbol)の定義が含まれています。
<X11/Xatom.h>
アトムの定義が含まれています。
<X11/Xlocale.h>
ロケールに関する定義が含まれています。
<X11/Xcms.h>
色制御機構(CMS:Color Management System)に関連する定義が含まれています。

プログラミングをする前に、自分の利用している環境で それぞれのヘッダファイルがどこに存在するか あらかじめ調べておきたいところです。

PC-UNIXではlocate(1)を利用すると便利です。 あるいはfind(1)で目星を付けて探すのでもよいです。

例:
% locate Xutil.h 
/usr/X11R6/include/X11/Xutil.h
/usr/local/src/xc/lib/X11/Xutil.h
%

たいてい同じ位置にXツールキットのヘッダファイル(後述)もあります。

Xサーバーが管理しているリソース

次のものがあります。

  1. ウィンドウ
  2. ピックスマップ(画像)
  3. カーソル(ポインティング・デバイスの位置を示す)
  4. カラーマップ(色表)
  5. フォント
  6. ヴィジュアル(Visual)
  7. グラフィックス・コンテキスト(Graphics Contex)
  8. アトム
  9. プロパティ
  10. セレクション・オーナー(selection owner)

リソースIDを直接利用するのは、 ウィンドウ、ピックスマップ、カーソル、カラーマップ、フォントです。 ウィンドウとピックスマップの両方を意味するドローワブルもリソースIDです。

Window,Pixmap,Drawable,Cursor,Colormap,Font

ヴィジュアルやグラフィックス・コンテキストは IDを保持する構造体オブジェクト経由で利用します。

XVisualIDFromVisual,XGContextFromGC

フォントは描画位置を算出するためにIDを利用することもあれば、 構造体オブジェクトを利用することもあります。

XFontStruct,Font,XFontSet

アトムやプロパティ、そしてセレクション・オーナーは 上記のリソースとは少し位置付けが異なります。 これらはクライアント通信のために用意されているリソースです。

Atom

プロパティやセレクション・オーナーはアトムで管理されます。 アトムには名前(文字列)が付けられており、 名前からアトムを求めることもできます。

プロパティはリソース(一般にウィンドウですが、フォントでも利用しています)に 関連づけられたアトムと何らかの値(一般にはアトムあるいはリソースIDだが 文字列や数値の場合もあります)の組です。

セレクション・オーナーはクライアント間通信の際に必要となる概念で、 これもアトムで管理されています。あるセレクションに関して そのオーナが一人になる、あるいは誰もいないように調整されます。

Xサーバーのリソースには限界があり、それを越えて利用しようとすると、 クライアントは強制的に接続が切られ、 結果プログラムは停止してしまうということを、 利用者は知っておく必要があります。

仮想記憶の仕掛けのあるXサーバを利用している場合は その現象を見ることはまずありませんが、 少し古い安物のXサーバーではそれは突然やってきます。