この頁は、基本的に下部の方ほど古い内容になります。(<hr>単位)
x-move-text.cの ソースコードを合わせてご覧下さい。 これ単体でコンパイルして動作させることができます。
アプリケーションでテキストを表示させるには、 事前に利用するフォントをXサーバにロードしておく必要があります。 歴史的経緯によって、フォントのロードに関連するAPIは幾つかあるのですが、 ここでは、Release 5によって事実上標準になったと思われる フォントセットについて説明します。
API | 説明 |
---|---|
XCreateFontSet | ロケールに応じて複数のフォントを読みとる |
XFreeFontSet | フォントを解放する |
XLoadFont | フォントをロード(IDの取得) |
XLoadQueryFont | フォントのロードと全フォント情報の取得 |
XQueryFont | IDから全フォント情報の取得の取得 |
XQueryTextExtents | IDから部分的にフォント情報の取得 |
XQueryTextExtents16 | IDから部分的にフォント情報の取得(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 ではよく、この値を使います。
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では 受けとったイベントを単に表示させているだけです。
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. $
X Window Systemで動作させることを意識した アプリケーション作成に関する話題を扱っています。
XはUNIXの世界では広く使われています。 「UNIXでGUI(Graphical User Interface)を使いたければXを利用する」と説明する資料も多くあります。 それ自体は間違ったことを述べているわけではありませんが、 Xの性質をよく知らない人には誤解を与えてしまう表現だと思います。
Xウィンドウ・システム自体はGUIを提供することはありません。 GUIのためにXを利用しているだけなのです。
個人的には「Xはグラフィックスを異機種間でネットワーク越しで 制御する仕組みを提供するシステム」と表現するほうがすきです。
内容的に繰り返しになりますが、
Xウィンドウ・システムは -- 私の知る限り -- 広く普及している同等の機能のシステムの中では、 稼働機種が多く パフォーマンスも比較的よいという意味で 一番よくできた実装です。
広く知られているものだけでも結構あります。
X自体はCで記述されています。 ライブラリも最終的にはCを利用しているというものがほとんどですが、そうでないものもあります。 (C++やJAVAやPerlでそういう形態のものがあります。)
この辺の事情はNetNewsの comp.windows.xに投稿されているFAQに詳しいです。
Xは仕様がきちんと定まっている上に、 それが公開されているシステムであるため、 多くの実装が存在するという 喜ばしい状態になりました。
そうなるように頑張った人達に感謝したいと思います。
いろんな人がいろんなライブラリを作成しています。 私が個人的に利用している/したことがあるのは次のとおりです。 (カタカナ表記は私が利用している呼び方であって、 一般的ではないかも知れません。)
これらはライブラリとしての層が異なるので、 同列に並べると奇妙な感がありますが、気にしないで下さい。
XlibはCで利用するライブラリです。X標準ライブラリの一つです。 Xプロトコルに対応するAPI(Application Programing Interface)を提供してくれます。
この頁の読者には、Cのソースコードを自分で読めることを期待しています。 (わからない人は別に勉強して下さい。)
Xlibは次の機能をカバーしています。
Xlibに関連するヘッダファイルとして次のようなものがあります。
プログラミングをする前に、自分の利用している環境で それぞれのヘッダファイルがどこに存在するか あらかじめ調べておきたいところです。
PC-UNIXではlocate(1)を利用すると便利です。 あるいはfind(1)で目星を付けて探すのでもよいです。
例: % locate Xutil.h /usr/X11R6/include/X11/Xutil.h /usr/local/src/xc/lib/X11/Xutil.h %
たいてい同じ位置にXツールキットのヘッダファイル(後述)もあります。
次のものがあります。
リソースIDを直接利用するのは、 ウィンドウ、ピックスマップ、カーソル、カラーマップ、フォントです。 ウィンドウとピックスマップの両方を意味するドローワブルもリソースIDです。
Window,Pixmap,Drawable,Cursor,Colormap,Font
ヴィジュアルやグラフィックス・コンテキストは IDを保持する構造体オブジェクト経由で利用します。
XVisualIDFromVisual,XGContextFromGC
フォントは描画位置を算出するためにIDを利用することもあれば、 構造体オブジェクトを利用することもあります。
XFontStruct,Font,XFontSet
アトムやプロパティ、そしてセレクション・オーナーは 上記のリソースとは少し位置付けが異なります。 これらはクライアント通信のために用意されているリソースです。
Atom
プロパティやセレクション・オーナーはアトムで管理されます。 アトムには名前(文字列)が付けられており、 名前からアトムを求めることもできます。
プロパティはリソース(一般にウィンドウですが、フォントでも利用しています)に 関連づけられたアトムと何らかの値(一般にはアトムあるいはリソースIDだが 文字列や数値の場合もあります)の組です。
セレクション・オーナーはクライアント間通信の際に必要となる概念で、 これもアトムで管理されています。あるセレクションに関して そのオーナが一人になる、あるいは誰もいないように調整されます。
☆
Xサーバーのリソースには限界があり、それを越えて利用しようとすると、 クライアントは強制的に接続が切られ、 結果プログラムは停止してしまうということを、 利用者は知っておく必要があります。
仮想記憶の仕掛けのあるXサーバを利用している場合は その現象を見ることはまずありませんが、 少し古い安物のXサーバーではそれは突然やってきます。