Appendix C Extensions

付録 C: 機能拡張

X は機能拡張によってコアプロトコルを発展させることができるので、 機能拡張を身分の低い市民と捉えないことが大切である。 どこかの時点で、あなたのお気に入りの機能拡張が X の標準の追加部分と して受け入れられるかもしれない。

したがって、機能拡張の利用をコアプロトコルの利用と区別することはほとん どないはずである。 機能拡張をアプリケーションプログラム内で明示的に初期化しなければならない 状況を避けるためには、機能拡張が実行する評価が緩いことや、 機能拡張が最初に呼ばれた時に自分自身で自動的に初期化を行うことが重要で ある。

この付録では、コアプロトコルリクエストと本質的に同じ性能で動作するよう な Xlib の機能拡張を書く方法を説明する。

: X の機能拡張は複数のリクエストからできていると考えられる。 10 個の新しい機能を 10 個の別々の機能拡張として定義するのはまずいやり 方である。 それよりは、それらの機能を一つの機能拡張にまとめるべきで、個々の リクエストを区別するにはマイナーオペコードを使うべきである。

Xlib のスタブを書くためのシンボルとマクロは <X11/Xlibint.h>に書かれている。 .SH 基本プロトコル対応ルーチン

機能拡張のための基本プロトコルリクエストは XQueryExtensionXListExtensionsである。

Bool XQueryExtension(display, name, major_opcode_return, first_event_return, first_error_return)
Display *display;
char *name;
int *major_opcode_return;
int *first_event_return;
int *first_error_return;
display
X サーバへの接続を指定する。
name
機能拡張の名前を指定する。
major_opcode_return
メジャーオペコードが返される。
first_event_return
イベントコードがあれば、最初のイベントコードが返される。
first_error_return
エラーコードがあれば、最初のエラーコードが返される。 LP
関数 XQueryExtensionは、指定された機能拡張が存在するかどうかを調べる。 指定された機能拡張が存在しなければ XQueryExtensionFalseを返す。 存在すれば Trueを返す。 機能拡張が存在する場合、 XQueryExtensionはその機能拡張のメジャーオペコードを major_opcode_return に返す。 存在しない場合は major_opcode_return には 0 が返される。 マイナーオペコードとリクエストのフォーマットは機能拡張ごとに固有のもの である。 機能拡張が追加のイベント型を持つ場合には、 XQueryExtensionは基本イベント型のコードを first_event_return に返す。 持たない場合には first_event_return には 0 が返される。 イベントのフォーマットは機能拡張ごとに固有のものである。 機能拡張が追加のエラーコードを持つ場合には、 XQueryExtensionは基本エラーコードを first_error_return に返す。 持たない場合には first_error_return には 0 が返される。 エラーが持つ追加データのフォーマットは機能拡張ごとに固有のものである。

機能拡張の名前がホストポータブル文字エンコーディングでなければ、結果は 実装依存である。 大文字と小文字は区別される。 つまり ``thing'', ``Thing'', ``thinG'' はそれぞれ別の名前として扱われる。

char **XListExtensions(display, nextensions_return)
Display *display;
int *nextensions_return;
display
X サーバへの接続を指定する。
nextensions_return
一覧として得られる機能拡張の数が返される。

XListExtensions 関数はサーバが対応している全ての機能拡張の一覧を返す。 サーバが返すデータがラテンポータブル文字エンコーディングでなければ、 返される文字列はホストポータブル文字エンコーディングである。 それ以外の場合の結果は実装依存である。
XFreeExtensionList(list)
char **list;
list
機能拡張の名前のリストを指定する。

XFreeExtensionList関数は XListExtensionsが割り当てたメモリを解放する。 .SH Xlib へのフッキング

これらの関数は、ライブラリにフックさせることができる。 これらは普通はアプリケーションプログラマが使うものではないが、 コア X プロトコルと X ライブラリインタフェースを拡張する必要があれば誰 でも使える。 (X へのプロトコルリクエストを生成する)こういった関数は普通はスタブ (stub)と呼ばれる。

機能拡張ではスタブはまず、現在の接続上で自分自身が既に初期化されている かどうかを調べるべきである。 まだ初期化されていなければ、スタブは XInitExtension を呼び出して、接続上で自分自身の初期化を試みるべきである。

機能拡張が GC/フォントの割り当てや解放、機能拡張が新しいイベント型を定 義しているかどうかを知る必要がある場合には、ここで説明する関数を使うと これらのイベントが発生した時に機能拡張を呼び出すことができる。

XExtCodes構造体は XInitExtension からの情報を返す。この構造体は <X11/Xlib.h>で定義されている:

typedef struct _XExtCodes {	/* 機能拡張に対して public である。変更してはならない */
	int extension;	/* 機能拡張の番号 */
	int major_opcode;	/* サーバが割り当てるメジャーオペコード */
	int first_event;	/* 機能拡張の最初のイベント番号 */
	int first_error;	/* 機能拡張の最初のイベント番号 */
} XExtCodes;

XExtCodes *XInitExtension(display, name)
Display *display;
char *name;
display
X サーバへの接続を指定する。
name
機能拡張の名前を指定する。

関数 XInitExtensionは指定された機能拡張が存在するかどうかを調べる。 次に、サーバとの接続上での機能拡張に関する情報を管理するためのメモリを 割り当て、これを接続に対する機能拡張リストに繋げ、スタブを実装する時に 機能拡張にアクセスするために必要となる情報を返す。 指定された機能拡張が存在しなければ XInitExtensionは NULL を返す。

機能拡張の名前がホストポータブル文字エンコーディングでなければ、結果は 実装依存である。 大文字と小文字は区別される。 つまり ``thing'', ``Thing'', ``thinG'' はそれぞれ別の名前として扱われる。

XExtCodes 構造体内の機能拡張番号は、後に続く別の呼び出しの際に必要となる。 この機能拡張番号は、ある一つの接続上ではユニークな値である。

XExtCodes *XAddExtension(display)
Display *display;
display
X サーバへの接続を指定する。

ローカルな Xlib の拡張の場合は、関数 XAddExtensionXExtCodes構造体を割り当て、機能拡張の番号を増やして、この機能拡張を機能拡張の リストに繋げる。 (これにより、サーバの拡張を必要とすることなく Xlib を拡張できる。) .SH ライブラリへのフック

これらの関数を使うと、様々な環境の変化が起きた時に呼び出される手続きを 定義できる。 このような手続きには、接続上での新しい GC の生成、GC のコピー、GC の 解放、フォントの生成や解放、機能拡張で定義されたイベントと実際に通信 する際のフォーマットの相互変換、エラー処理等がある。

これらの全ての関数は、この機能拡張に対して定義されたひとつ前の手続きを返す。

int (*XESetCloseDisplay(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
ディスプレイがクローズされた時に呼び出される手続きを指定する。

関数 XESetCloseDisplayXCloseDisplay が呼ばれた時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

XCloseDisplay が呼ばれると、ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, codes)
	Display *display;
	XExtCodes *codes;

int (*XESetCreateGC(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
GC がクローズされた時に呼び出される手続きを指定する。

関数 XESetCreateGCは新しい GC が生成された時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

新しい GC が生成されると、 ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, gc, codes)
	Display *display;
	GC gc;
	XExtCodes *codes;

int (*XESetCopyGC(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
GC 要素がコピーされた時に呼び出される手続きを指定する。

関数 XESetCopyGCは GC がコピーされた時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

GC がコピーされると、 ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, gc, codes)
	Display *display;
	GC gc;
	XExtCodes *codes;

int (*XESetFreeGC(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
GC が解放された時に呼び出される手続きを指定する。

関数 XESetCreateGCは GC が解放された時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

GC が解放されると、 ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, gc, codes)
	Display *display;
	GC gc;
	XExtCodes *codes;

int (*XESetCreateFont(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
新しいフォントが作成された時に呼び出される手続きを指定する。

関数 XESetCopyGCXLoadQueryFontXQueryFontが呼ばれた時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

XLoadQueryFontXQueryFontが呼ばれると、 ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, fs, codes)
	Display *display;
	XFontStruct *fs;
	XExtCodes *codes;

int (*XESetFreeFont(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
フォントが解放された時に呼び出される手続きを指定する。

関数 XESetFreeFontはフォントが解放された時に必ず呼び出される手続きを定義する。 この関数は前に定義されていた手続きを返す。 普通は NULL が返される。

XFreeFont が呼ばれると、ユーザ定義の手続きは以下の引き数を使って呼び出される:

(*proc)(display, fs, codes)
	Display *display;
	XFontStruct *fs;
	XExtCodes *codes;

XESetWireToEventXESetEventToWireを使うと、ユーザは新しいイベントをライブラリに定義できる。 XEvent構造体は必ず型のコード( int型)を最初の要素として持つ。 これはイベントの種類を一意に決める。 2 番目の要素は必ず、サーバが処理した最後のリクエストのシリアル番号( unsignedlong 型)である。 2 番目の要素は必ず、イベントが SendEventプロトコルリクエストから送られたのかどうかを示す真偽値( Bool 型)である。 4 番目の要素は必ず、イベントが読み出されたディスプレイへのポインタ である。 5 番目の要素は必ず何らかのリソース ID (普通はウィンドウ)であり、 ツールキットのディスパッチャによって注意深く選択されたものである。 5 番目の要素は、イベントが自然な行き先がない場合であっても、必ず存在し なければならない。 プロトコルからこの要素に設定する値がなければ、0 で初期化すること。

: ホストのイベント構造体の大きさは XEvent 共用体(構造体)よりも大きくできないという実装上の制限がある。 この構造体では、24 要素または 96 文字より多いデータについてマシン間の 移植性を保証する方法もない。
int (*XESetWireToEvent(display, event_number, proc))()
Display *display;
int event_number;
Status (*proc)();
display
X サーバへの接続を指定する。
event_number
イベントコードを指定する。
proc
イベントの変換が行われた時に呼び出される手続きを指定する。

XESetWireToEvent関数は、イベントを通信フォーマット (xEvent )からホストフォーマット (XEvent )に変換する必要がある時に呼び出される手続きを定義する。 イベント番号は、変換手続きとしてインストールするプロトコルイベント番号 を定義する。 XESetWireToEventは前に定義されていた手続きを返す。
: コアイベントの変換関数を独自の関数で置き換えることも可能である。 ただし、これは推奨しない。 しかし、コアイベントがキューに入ったり別の評価を受ける前に コアイベントを横取りして内容を変えることは可能である。
Xlib がイベントを通信フォーマットからホストフォーマットに変換する必要 がある場合には、ユーザ定義の手続きが以下の引き数を使って呼び出される:

Status (*proc)(display, re, event)
	Display *display;
	XEvent *re;
	xEvent *event;

ユーザ定義の手続きは変換が成功したかどうかを示すステータスを返さなけれ ばならない。 re 引き数は、ホストフォーマットのイベントが格納されている場所を指す ポインタであり、event 引き数は大きさが 32 バイトである通信イベント 構造体である。 作成しようとしている XEvent 構造体においては、イベント構造体に必須の 5 つのメンバを用意しなければ ならない。 type メンバには xEvent 構造体で指定される型を設定すること。 他の全てのメンバは xEvent 構造体(通信フォーマット)から XEvent 構造体(ホストフォーマット)にコピーすべきである。 ユーザ定義の変換手続きは、 イベントをキューに入れるのであれば Trueを返すべきであり、イベントをキューに入れてはならないのであれば Falseを返すべきである。

イベントのシリアル番号要素を初期化するには、 イベントを使って _XSetLastRequestReadを呼び出し、この返り値を使うこと。

unsigned long _XSetLastRequestRead(display, rep)
Display *display;
xGenericReply *rep;
display
X サーバへの接続を指定する。
rep
通信フォーマットのイベント構造体を指定する。

関数 _XSetLastRequestReadはイベントが持つ部分的なシリアル番号から完全なシリアル番号を計算し、 これを返す。

Status (*XESetEventToWire(display, event_number, proc))()
Display *display;
int event_number;
int (*proc)();
display
X サーバへの接続を指定する。
event_number
イベントコードを指定する。
proc
イベントが変換された時に呼び出される手続きを指定する。

関数 XESetEventToWireは、イベントをホスト形式 (XEvent ) から通信形式 (xEvent )に変換する必要がある時に呼ばれる手続きを定義する。 イベント番号は イベント番号は、変換手続きとしてインストールするプロトコルイベント番号 を定義する。 XESetEventToWireは前に定義されていた手続きを返す。 この関数は変換が失敗すると 0 を返し、それ以外の場合には 0 でない値を返す。
: コアイベントの変換関数を独自の関数で置き換えることも可能である。 ただし、これは推奨しない。 しかし、コアイベントがキューに入ったり別の評価を受ける前に コアイベントを横取りして内容を変えることは可能である。
Xlib がイベントをホストフォーマットから通信フォーマットに変換する必要 がある場合には、ユーザ定義の手続きが以下の引き数を使って呼び出される:

(*proc)(display, re, event)
	Display *display;
	XEvent *re;
	xEvent *event;

re 引き数は、ホストフォーマットのイベントへのポインタであり、 event 引き数は大きさが 32 バイトである通信イベント構造体が格納される 場所を指すポインタである。 type メンバには XEvent 構造体で指定される型を設定すること。 他の全てのメンバはホストフォーマットから xEvent 構造体にコピーすべきである。

Bool (*XESetWireToError(display, error_number, proc)()
Display *display;
int error_number;
Bool (*proc)();
display
X サーバへの接続を指定する。
error_number
エラーコードを指定する。
proc
エラーを受信した時に呼び出される手続きを指定する。

XESetWireToError関数は機能拡張のエラーを通信フォーマットからホストフォーマットに変換す る必要があるときに呼ばれる手続きを定義する。 エラー番号はどのプロトコルエラーコードを変換手続きにインストールするの かを定義する。 XESetWireToErrorは前に定義されていた手続きを返す。

この関数は、通信フォーマットの複数のエラーを Xlib のひとつのエラーにま とめる時や、別の評価を受ける前に X のエラーを横取りする必要がある時に、 コアの X のエラーの範囲を超える追加のエラー値を含む機能拡張のエラーに 対して使うこと。

Xlib がイベントを通信フォーマットからホストフォーマットに変換する必要 がある場合には、ユーザ定義の手続きが以下の引き数を使って呼び出される:

Bool (*proc)(display, he, we)
	Display *display;
	XErrorEvent *he;
	xError *we;

he 引き数はホストフォーマットのエラーを格納する場所を指すポインタである。 he が指すこの構造体は XEvent構造体以上の大きさを持つことが保証されているので、 XErrorEventよりサイズが大きい型にキャストして追加の値を格納することができる。 Xlib がエラーを完全に無視した場合(例えば複数個のエラー構造体がひとつの Xlib エラーにまとめられた場合)、この関数は Falseを返すべきである。 それ以外の場合には Trueを返すべきである。

int (*XESetError(display, extension, proc))()
Display *display;
int extension;
int (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
エラーを受信したときに呼び出す手続きを指定する。

Xlib 内部では、エラーが起こったときに外部エラー処理の呼び出しを 止めておいた方が良い時間がある。 これにより、呼び出しを同期的に行うことを対価として、呼び出しの時に ステータスを返すことが可能になる(ただし、このような関数の大部分は 問い合わせ操作であり、いずれにせよ普通は同期するようにプログラムされる)。

Xlib が _XReply内でプロトコルエラーを検出すると、Xlib は以下の引き数を使って ユーザ定義の手続きを呼び出す:

int (*proc)(display, err, codes, ret_code)
	Display *display;
	xError *err;
	XExtCodes *codes;
	int *ret_code;

引き数 err は 32 バイトの大きさの通信フォーマットのエラーを指す ポインタである。 引き数 codes は機能拡張コードの構造体へのポインタである。 引き数 ret_code は、返却コードであり _XReply を返すとよい。

ユーザ定義の手続きが 0 を返すと、エラーは無視されず、クライアントの エラーハンドラが呼び出される。 (詳しい情報については 11.8.2 を参照。) ユーザ定義の手続きが 0 でない値を返すと、エラーは無視され、 _XReply は値として ret_code を返す。

char *(*XESetErrorString(display, extension, proc))()
Display *display;
int extension;
char *(*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
エラー文字列を取得したときに呼び出されるエラーを指定する。

関数 XGetErrorText はエラーについての文字列をユーザに返す。 XESetErrorStringを使うと、呼び出されるとエラーメッセージを指すポインタを返す手続きを 定義できる。 例を以下に示す:

(*proc)(display, code, codes, buffer, nbytes)
	Display *display;
	int code;
	XExtCodes *codes;
	char *buffer;
	int nbytes;

ユーザ定義の手続きは、検出された全てのエラーに対するエラーコードを使っ て呼び出される。 ユーザはエラーメッセージを含み、NULL で終端する n バイトの文字列を バッファにコピーしなければならない。

void (*XESetPrintErrorValues(display, extension, proc))()
Display *display;
int extension;
void (*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
エラー表示が行われる時に呼ぶ手続きを指定する。

関数 XESetPrintErrorValuesは機能拡張のエラーを表示するときに、エラー値を表示するために呼び出され る手続きを定義する。 この関数は、コアの X のエラーの範囲を超える追加のエラー値を含む 機能拡張のエラーに対して使うこと。 この関数は前に定義されていた手続きを返す。

Xlib がエラーを表示する必要がある時は、この手続きは以下の引き数を 使って呼び出される:

void (*proc)(display, ev, fp)
	Display *display;
	XErrorEvent *ev;
	void *fp;

ev が指すこの構造体は XEvent構造体以上の大きさを持つことが保証されているので、 XErrorEventよりサイズが大きい型にキャストし、 XESetWireToErrorを使って設定した追加の値を取得できる。 引き数 fp の下にある型はシステム依存である。 POSIX 準拠のシステムでは、fp は FILE* 型にキャストすべきである。

int (*XESetFlushGC(display, extension, proc))()
Display *display;
int extension;
int *(*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
GC がフラッシュされた時に呼び出される手続きを指定する。

関数 XESetFlushGCで設定した手続きは、関数 XESetCopyGCで設定した場合と同じインタフェースを持つが、サーバ内で CG キャッシュを 更新する必要があるときに呼び出される。
int (*XESetBeforeFlush(display, extension, proc))()
Display *display;
int extension;
int *(*proc)();
display
X サーバへの接続を指定する。
extension
機能拡張の番号を指定する。
proc
バッファがフラッシュされる時に呼び出す手続きを指定する。

関数 XESetBeforeFlushはデータをサーバに送る直前に呼び出す手続きを定義する。 データを送る直前には、ユーザ定義の手続きが以下の引き数を使って 1 回 以上呼び出される。

void (*proc)(display, codes, data, len)
	Display *display;
	XExtCodes *codes;
	char *data;
	long len;

引き数 data は送ろうとしているデータバッファの位置を指定し、 引き数 len はデータの長さをバイト単位で指定する。 ユーザ定義の引き数はデータの内容を変更してはならないし、 同じディスプレイに対して追加のプロトコルリクエストを行ってはならない。 .SH Xlib のデータ構造へのフック

Xlib の各種データ構造体には、機能拡張の手続きが、自分が与えたデータを リストに追加するための準備がしてある。 このような構造体には GC , Visual , Screen , ScreenFormat , Display , XFontStructがある。 リストポインタは常に構造体の最初のメンバなので、ひとまとまりの手続き を使ってこのリスト上のデータを操作できる。

以下の構造体はこの節で説明する関数で使われる。 定義は <X11/Xlib.h>でされている:

typedef struct _XExtData {
	int number;	/* XInitExtension が返した数 */
	struct _XExtData *next;	/* リスト上の次の要素 */
	int (*free_private)();	/* 定義されている場合、この機能拡張の */
	XPointer private_data;	/* プライベートなデータを解放するために呼ばれる */
} XExtData;

上記のデータ構造体のいずれかが解放されたとき、リストが辿られ、 その構造体の解放手続きが(もしあれば)呼び出される。 解放手続きが NULL ならば、ライブラリは private_data メンバと構造体その ものを両方とも解放する。

union {	Display *display;
	GC gc;
	Visual *visual;
	Screen *screen;
	ScreenFormat *pixmap_format;
	XFontStruct *font } XEDataObject;

XExtData **XEHeadOfExtensionList(object) XEDataObject object;
object
オブジェクトを指定する。

関数 XEHeadOfExtensionListは指定されたオブジェクトに割り当てられた機能拡張構造体へのポインタを 返す。 XAddToExtensionListと組み合わせると、機能拡張は XEHeadOfExtensionListを使って、 XEDataObjectに含まれる任意の型の構造体に任意のデータを割り当てることができる。

XAddToExtensionList(structure, ext_data)
XExtData **structure;
XExtData *ext_data;
structure
機能拡張リストを指定する。
ext_data
追加する機能拡張構造体を指定する。

引き数 structure は、今まで列挙したデータ構造体のいずれかを指す ポインタである。 この関数を呼ぶ前には、ext_data->number を機能拡張番号で 初期化しなければならない。
XExtData *XFindOnExtensionList(structure, number)
struct _XExtData **structure;
int number;
structure
機能拡張リストを指定する。
number
XInitExtension から得た機能拡張番号を指定する。

関数 XFindOnExtensionListは、機能拡張番号に対応する最初のデータ構造体を返す。 ひとつの機能拡張は、ひとつのデータ構造体の機能拡張データリストに 対して多くとも一つの機能拡張データ構造体しか追加しないことが前提である。 これ以上の構造体を見つける方法はない。

XAllocID マクロはリソース ID を割り当てて返す。このマクロは <X11/Xlib.h>で定義されている。

XAllocID(display)
Display *display;
display
X サーバへの接続を指定する。

このマクロは Display構造体を通じて内部のリソース ID アロケータを呼び出す。 このマクロは、新しいリソースを作るときに使えるリソース ID を返す。

XAllocIDsマクロはリソース ID の配列を割り当てて返す。

XAllocIDs(display, ids_return, count)
Display *display;
XID *ids_return;
int count;
display
X サーバへの接続を指定する。
ids_return
リソース ID が返される。
rep
要求するリソース ID の数を指定する。

このマクロは Display構造体を通じて内部のリソース ID アロケータを呼び出す。 このマクロは呼び出し側が与えた配列に対するリソース ID を返す。 リソース ID の自動的な再利用を正しく行うためには、複数のリソース ID を 要求する時には XAllocIDsを呼ばなければならない。この呼び出しはプロトコルリクエストを 生成することがある。 .SH GC のキャッシュ

同じ GC に対する独立な変更リクエストをひとつのプロトコルリクエストにま とめられるようにするため、ライブラリは GC をキャッシュする。 これは普通はライトバックキャッシュと呼ばれる。 動作が GC の内容に依存している全ての機能拡張手続きは、 サーバが持っている GC の内容が最新であることを保証するため、 GC キャッシュをフラッシュしなければならない。

FlushGCマクロはライブラリの GC 構造体が持っている dirty ビットをチェックし、 何らかの要素が変更されていると _XFlushGCCache を呼び出す。 FlushGCマクロの定義は以下の通りである:

FlushGC(display, gc)
Display *display;
GC gc;
display
X サーバへの接続を指定する。
gc
GC を指定する。

GC を拡張して追加のリソース ID 要素を追加した場合には必ず、 ライブラリのスタブが即座に変更リクエストを送るようにすべきである。 この理由は、クライアントはリソースを使用直後に解放する可能性があるため、 プロトコルリクエストを出さずにキャッシュ内だけで値を持っていた場合には、 リソースが GC に設定される前に破棄されるかもしれないからである。 _XFlushGCCache 手続きを使うと、キャッシュを強制的にフラッシュさせることができる。 _XFlushGCCache の定義は以下の通りである:
_XFlushGCCache(display, gc)
Display *display;
GC gc;
display
X サーバへの接続を指定する。
gc
GC を指定する。

.SH グラフィックスのバッチ化

X を拡張してポリグラフィックスプリミティブを増やした場合には、 back-to-back の単一の呼び出しをひとつのポリリクエストに変換できる ライブラリの機能を利用すると良い。 これにより、ポリリクエストを使って書かれていないプログラムの 性能は劇的に向上する。 xReqへのポインタ(ディスプレイ構造体では last_req)は最後に処理されたリクエ ストである。 最後のリクエスト型がドロウアブル、GC やその他のオプションが新しい型と 同じであることと、バッファ内に十分な余裕が残っていることを調べることに より、グラフィックスリクエストの length フィールドを拡張してデータを バッファに追加することにより前のリクエストをそのまま拡張できることもある。 これにより、ネイティブプログラムでは性能を 5 倍以上に向上できます。 例として XDrawPoint のスタブを以下に示します。 (機能拡張のスタブの書き方については次の節で説明します。) .IP .nf #include <X11/Xlibint.h> /* バッチ処理できる最大のサイズを予め計算 */ static int size = sizeof(xPolyPointReq) + EPERBATCH * sizeof(xPoint); XDrawPoint(dpy, d, gc, x, y) register Display *dpy; Drawable d; GC gc; int x, y; /* INT16 */ { xPoint *point; LockDisplay(dpy); FlushGC(dpy, gc); { register xPolyPointReq *req = (xPolyPointReq *) dpy->last_req; /* 前のリクエストと同じドロウアブルを使った同じリクエストであれば、バッチ化する */ if ( (req->reqType == X_PolyPoint) && (req->drawable == d) && (req->gc == gc->gid) && (req->coordMode == CoordModeOrigin) && ((dpy->bufptr + sizeof (xPoint)) <= dpy->bufmax) && (((char *)dpy->bufptr - (char *)req) < size) ) { point = (xPoint *) dpy->bufptr; req->length += sizeof (xPoint) >> 2; dpy->bufptr += sizeof (xPoint); } else { GetReqExtra(PolyPoint, 4, req); /* 1 ポイント = 4 バイト */ req->drawable = d; req->gc = gc->gid; req->coordMode = CoordModeOrigin; point = (xPoint *) (req + 1); } point->x = x; point->y = y; } UnlockDisplay(dpy); SyncHandle(); } .fi

サーバを独占するような非常に長いリクエストをクライアントに生成させない ために、バッチ化するリクエストの数について EPERBATCH が <X11/Xlibint.h>で定義されている。 性能的なもうけの大部分は、最初の数個のリクエストをマージすることで得ら れる。 FlushGC は last_req の値を取り出すに呼ばれる点に注意すること。 なぜなら、 FlushGC はこのフィールドの値を変えるかもしれないからである。 .SH 機能拡張のスタブの記述

全ての X のリクエストは必ずリクエストの長さを持っている。 これは 32 ビットのうちの 16 ビットで表されている。 つまり、ひとつのリクエストは 256KB より長くできない。 サーバによっては、このような長さを持つ単独のリクエストに対応していない ことがある。 dpy->max_request_size の値は、サーバの実装によって定義されている リクエストの最大長である。 詳しくは「X ウィンドウシステムプロトコル」を参照すること。 .SH リクエスト、応答、Xproto.h

<X11/Xproto.h>ファイルには、スタブを実装しようとする場合に見ておくとよい定義が集まっ ている。つまりリクエスト名、リクエスト構造体、応答構造体である。

機能拡張を追加する場合には <X11/Xproto.h>に相当するファイルを作る必要と、このファイルの中にスタブ手続きを入れる 必要がある。 また、それぞれのスタブ手続きは <X11/Xlibint.h >をインクルードしなければならない。

識別子は次のようにして慎重に選ばれる。例えばリクエストが X_DoSomething ならば、このリクエスト構造体は xDoSomethingReq であり、応答構造体は xDoSomethingReply である。 GetReq 系のマクロ( <X11/Xlibint.h>で定義)を使えばこの命名方法をうまく利用できる。

それぞれの X リクエストについては、 <X11/Xproto.h>に以下のような定義がある:

#define X_DoSomething   42
ユーザ定義の機能拡張の戸田ファイルでは、この値はマイナーオペコード となる。メジャーオペコードにはならない。 .SH リクエストのフォーマット

全てのリクエストは 8 ビットのメジャーオペコードと、4 バイト単位で表現 される 16 ビット長のフィールドを持っている。 全てのリクエストは 4 バイトのヘッダ(メジャーオペコード、長さフィールド、 データのバイト列)と、その後に続く 0 バイト以上の追加データからなる。 長さフィールドはリクエストの全体の長さを定義する。長さにはヘッダも含まれる。 リクエスト内の長さフィールドは、リクエストを保持するために必要最低限の 長さに等しくなければならない。 指定された長さが必要な長さより短かったり長かったりすると、サーバは BadLength エラーを生成するはずである。 リクエスト内の未使用データの値が 0 である必要はない。 機能拡張は、長いプロトコルリクエストは短いリクエストに分割できるように 設計すべきである。 プロトコルではリクエストの最大長は 4096 ユニット(16384 バイト)より短く はないことが保証されている。

メジャーオペコードの 128 から 255 は機能拡張のために予約されている。 機能拡張は複数のリクエストを持たせることを意図しているので、 機能拡張のリクエストは普通はリクエストヘッダの 2 番目のバイトデータと して追加のマイナーオペコードを持つ。しかし、このマイナーオペコードの 配置と解釈は機能拡張リクエストの他の全てのフィールドと同じく、 コアプロトコルでは定義されていない。 全てのリクエストには(1 から始まる)シーケンス番号が暗黙的に割り当てられ、 応答、エラー、イベントに使われる。

役には立つけれど特定のマシンでの移植性の問題は解決できない方法として、 B16B32マクロが定義されている。これらは一部のマシンにおいてはビットフィールド を規定できる。 例えば Cray 機では、これらのマクロを全ての 16 ビット値と 32 ビット値で 使うべきである。その方法は後述する。

ほとんどのプロトコルリクエストでは、対応する構造体の typedef が <X11/Xproto.h>にある。以下に例を示す:

typedef struct _DoSomethingReq {
	CARD8 reqType;	/* X_DoSomething */
	CARD8 someDatum;	/* リクエストごとに使い方が異なる */
	CARD16 length B16;	/* リクエスト内のバイト数の合計を 4 で割ったもの */
	...
	/* リクエスト固有のデータ */
	...
} xDoSomethingReq;

コアプロトコルが 32 ビットの引き数をひとつだけ持っている場合は、 ユーザ定義の機能拡張のヘッダファイルでリクエスト構造体を宣言する必要は ない。 宣言の代わりに、こういったリクエストは <X11/Xproto.h>内の xResourceReq構造体を使う。 この構造体はひとつだけ引き数を持ち、その型が Window , Pixmap ,Drawable , GContext , Font , Cursor , Colormap , Atom , VisualIDである任意のリクエストに対して用いる。

typedef struct _ResourceReq {
	CARD8 reqType;	/* リクエストの型。例えば X_DoSomething */
	BYTE pad;	/* 未使用 */
	CARD16 length B16;	/* 2 (= リクエスト内のバイト数の合計を 4 で割ったもの */
	CARD32 id B32;	/* Window, Drawable, Font, GContext 等 */
} xResourceReq;

便利なのであれば、これに似たことを機能拡張のヘッダファイルで行ってもよ い。

これらの構造体のどちらの場合も、reqType フィールドは リクエストの型の識別に用いる(例: X_MapWindow または X_CreatePixmap)。 length フィールドは、4 バイトのロングワード単位でのリクエストの長さを 示す。 この長さには、リクエスト構造体自身の長さと、リクエスト構造体の後ろに続 く可変長データの長さ(文字列やリスト等)の両方が含まれる。 やってくるリクエスト構造体のサイズは色々あるが、 全てのリクエストは 4 の倍数のバイト長になるように調整される。

引き数を全く取らないプロトコルリクエストも少しある。 このようなプロトコルリクエストは引き数の代わりに <X11/Xproto.h>で定義されている xReq 構造体を使う。この構造体は reqType と長さ(と長さ調整のバイト)だけを 含んでいる。

プロトコルリクエストが応答を必要とする場合のために、 <X11/Xproto.h>には応答構造体の typedef もある:

typedef struct _DoSomethingReply {
	BYTE type;	/* 常に X_Reply */
	BYTE someDatum;	/* リクエストごとに使い方が異なる */
	CARD16 sequenceNumber B16;	/* 今まで送られたリクエストの数 */
	CARD32 length B32;	/* 追加のバイト数を 4 で割った値 */
	...
	/* リクエスト固有のデータ */
	...
} xDoSomethingReply;

これらの応答構造体の大部分の長さは 32 バイトである。 そんなに多くの応答値がない場合には、構造体には長さを 32 バイトにするの に十分な数の隙間埋めのデータが入っている。 length フィールドはリクエストの合計バイト数 - 32 を 4 で割った値である。 length フィールドが 0 でないのは以下の場合である:

GetWindowAttributes , QueryFont , QueryKeymap , GetKeyboardControl だけがコアプロトコルの中で 32 バイトより長い応答構造体を持っている。

データを全く含んでいない応答を返すプロトコルリクエストも少しある。 <X11/Xproto.h>はこのようなリクエストの応答構造体は定義していない。 その代わりに、このようなリクエストは xGenericReplyを使う。この構造体は型、長さ、シーケンス番号(および長さを 32 バイトに するのに十分な長さの隙間埋めデータ)だけを持つ。 .SH スタブ手続きの書き始め

Xlib のスタブ手続きは以下のようにして始めるべきである:

#include "<X11/Xlibint.h>
XDoSomething (arguments, ... )
/* 引き数の宣言 */
{
register XDoSomethingReq *req;
...
プロトコルリクエストが応答を持つのであれば、変数の宣言にはその リクエストのための応答構造体を含めるべきである。

xDoSomethingReply rep;
.SH データ構造のロッキング

複数スレッドからひとつのディスプレイ接続にアクセスできるようにしたい システムのためにディスプレイ構造体をロックするには、それぞれのスタブは 自身のクリティカルな部分をロックできる必要がある。 一般的には、この節が重要なのは適切な GetReq を呼び出す直前から、呼び出し の全ての引き数がバッファに格納されるまでです。 このロックを行うために必要な正確な手順はマシンのアーキテクチャに依存し ます。 Xlib には 2 つのマクロが用意されています。これらは普通はマクロとして 実装されています。

LockDisplay(display)
Display *display;

UnlockDisplay(display)
Display *display;
display
X サーバへの接続を指定する。

.SH プロトコルリクエストと引き数の送信

変数定義の後、スタブ手続きは <X11/Xlibint.h>で定義されている以下の 4 つのマクロ( GetReq , GetReqExtra , GetResReq , GetEmptyReq)のうちのいずれかを呼ぶべきである。 これらの全てのマクロは、 <X11/Xproto.h>で宣言されているプロトコルリクエストの名前を 最初の引き数として受け取る。ただし X_ の部分は取り除かれる。 それぞれのマクロは Display 構造体へのポインタ(dpy)とリクエスト構造体へのポインタ(req)を宣言してい る。後者は適切な型を持っている。 そしてマクロはリクエスト構造体を出力バッファの後ろに追加し、 type フィールドと length フィールドを設定し、これを指す値を req に 設定する。

プロトコルリクエストが引き数を持っていなければ(例えば X_GrabServer) GetEmptyReqを使うこと。

.R 
GetEmptyReq (DoSomething, req);
プロトコルリクエストが 32 ビット長の引き数をひとつだけ持つ場合( Pixmap , Window , Drawable , Atom , 等)には、 GetResReqを使うこと。 マクロの 2 番目の引き数は 32 ビットオブジェクトである。 X_MapWindow がその良い例である。

GetResReq (DoSomething, rid, req);
引き数 rid は Pixmap , Window , またはその他のリソース ID である。

プロトコルリクエストが他の引き数リストを全く取らなければ GetReqを呼び出すこと。 GetReqの後にはリクエスト構造体の他のフィールドを全て設定する必要があるが、 普通はスタブ手続きの引き数を使って設定する。

GetReq (DoSomething, req);
/* ここで引き数に値を設定する */
req->arg1 = arg1;
req->arg2 = arg2;
...
いくつかのスタブ手続き( XCreateGCXCreatePixmap)は呼び出し側にリソース ID を返すが、プロトコルリクエストへの 引き数としてリソース ID を渡す。 このような手続きは XAllocID を使って、 接続をオープンしたときにこのクライアントに割り当てられた範囲の ID の中 からリソース ID を割り当てる。

rid = req->rid = XAllocID();
...
return (rid);
最後に、スタブ手続きによっては 決まった量の可変長データをリクエストの後に送信する。 普通はこのような手続き( XMoveWindowXSetBackground等)は、 汎用の関数( XMoveResizeWindowXChangeGC等)の特殊な場合である。 このような手続きは GetReqExtraを使う。これは GetReqとほぼ同じであるが、追加の引き数(リクエスト構造体の後ろの出力バッファ 内で割り当てる追加のバイト数)を取る点が異なる。 この数は必ず 4 の倍数である。 .SH 可変長引き数

プロトコルリクエストによっては、 xDoSomethingReq 構造体の後ろに追加の可変長引き数を取るものもある。 このデータのフォーマットはリクエストごとに異なる。 8 ビットのバイト列を要求するリクエストもあるし、 16 ビットまたは 32 ビットのデータ列を要求するものもあるし、 構造体の列を要求するものもある。

可変長データの長さはリクエスト構造体の length ヘッダに追加する必要がある。 この length フィールドの単位は 32 ビットのロングワードである。 このデータが文字列かあるいは別の 8 ビットのバイト列である場合は、 データを追加する前に長さの切り上げとシフト操作を行わなければならない。

req->length += (nbytes+3)>>2;
可変長データを送信するには Data マクロを使う。 データが出力バッファに収まる場合は、 このマクロはデータをバッファにコピーする。 しかし、データがバッファに収まらない場合は、 Data マクロは _XSendを呼び出してまずバッファの内容を送信し、それから指定されたデータを送信 する。 Data マクロは引き数を 3 つ取る: つまりディスプレイ、データの先頭を指すポインタ、 送信するバイト数である。
Data(display, (char *) data, nbytes);

Data16(display, (short *) data, nbytes);

Data32(display, (long *) data, nbytes);

Data ,Data16 ,Data32は最後の引き数を複数回使うことがあるので、引き数は ``nitems*sizeof(item)'' のような式ではなく変数にすべきである。 この類の計算は、マクロを呼び出す前に別の文を使って行うべきである。 byte, short, long 型のデータを送る際には適切なマクロを選ぶこと。

プロトコルリクエストが応答を要求する場合には、 Data マクロではなく _XSend 手続きを呼び出すこと。 _XSend 手続きは Data マクロと同じ引き数を取るが、データは出力バッファ( Data マクロの場合も結局、後で _XReplyが呼び出されてフラッシュされる)にコピーされずにすぐに 送信されるので、より高速である。 .SH 応答

プロトコルリクエストに応答がある場合は、 全ての固定長の引き数と可変長の引き数の処理が終わった後に _XReply を呼び出すこと。 _XReply は出力バッファをフラッシュし、 xReply パケットが届くのを待つ。 この間にイベントが届くと _XReply はこのイベントを後で使うためにキューに入れる。

Status _XReply(display, rep, extra, discard)
Display *display;
xReply *rep;
int extra;
Bool discard;
display
X サーバへの接続を指定する。
rep
応答構造体を指定する。
extra
応答の後ろに付いているはずの32 ビットワードの数を指定する。
discard
引き数 extra で指定されている数を超えたデータを破棄するかどうかを指定 する。

関数 _XReply は応答パケットを待ち、その内容を引き数 rep で指定された場所にコピー する。 _XReply は応答を受け取る前に起きたエラーとイベントのパケットを処理する。 _XReply は引き数を 4 つ取る。

ほとんどの応答構造体の長さは 32 バイトなので 3 番目の引き数は 普通は 0 である。 コアプロトコルでの数少ない例外は GetWindowAttributes , QueryFont , QueryKeymap , GetKeyboardControlへの応答である。これらの長さは 32 バイトを超える。

応答構造体の後に追加の可変長データ(リストや文字列など)が続く場合は 最後の引き数は False でなければならない。 可変長のデータがなければ True にすべきである。

: この最後の引き数は、クライアントが想定しているよりも長いデータを送るよ うな新しいバージョンのサーバが仮にできたとしてもクライアントが正しく 通信できるように、上位互換性のために用意されている。 例えば、何らかの新しいバージョンの GetWindowAttributes は、末尾部分に追加の属性を持っている xGetWindowAttributesReply を大きいけれど互換性のあるデータとして使える。
_XReply は応答を正しく受け取った場合は Trueを返し、何らかのエラーを受け取った場合は False を返す。

応答の後ろに可変長データが続かないリクエストの場合は、以下のように記述 する:

_XReply(display, (xReply *)&rep, 0, True);
*ret1 = rep.ret1;
*ret2 = rep.ret2;
*ret3 = rep.ret3;
...
UnlockDisplay(dpy);
SyncHandle();
return (rep.ret4);
}
応答の後ろに可変長データが続く場合は、 True の部分を Falseに変更した上で、適切な _XRead を呼んで可変長データを読み込むこと。

_XRead(display, data_return, nbytes) Display *display; char *data_return; long nbytes;
display
X サーバへの接続を指定する。
data_return
バッファを指定する。
nbytes
必要なバイト数を指定する。

関数 _XReadは指定されたバイト数のデータを読み込み data_return に入れる。

_XRead16(display, data_return, nbytes) Display *display; short *data_return; long nbytes;
display
X サーバへの接続を指定する。
data_return
バッファを指定する。
nbytes
必要なバイト数を指定する。

関数 _XRead16は指定されたバイト数のデータを読み込み、これを 16 ビット単位のデータに 分割し、指定された配列に short 型の値として入れる。

_XRead32(display, data_return, nbytes) Display *display; long *data_return; long nbytes;
display
X サーバへの接続を指定する。
data_return
バッファを指定する。
nbytes
必要なバイト数を指定する。

関数 _XRead32は指定されたバイト数のデータを読み込み、これを 32 ビット単位のデータに 分割し、指定された配列に long 型の値として入れる。

_XRead16Pad(display, data_return, nbytes) Display *display; short *data_return; long nbytes;
display
X サーバへの接続を指定する。
data_return
バッファを指定する。
nbytes
必要なバイト数を指定する。

関数 _XRead16Padは指定されたバイト数のデータを読み込み、これを 16 ビット単位のデータに 分割し、指定された配列に short 型の値として入れる。 バイト数が 4 の倍数でなければ _XRead16Padは調整のために 2 バイトまでの追加データを読み込んで破棄する。

_XReadPad(display, data_return, nbytes) Display *display; char *data_return; long nbytes;
display
X サーバへの接続を指定する。
data_return
バッファを指定する。
nbytes
必要なバイト数を指定する。

関数 _XReadPadは指定されたバイト数のデータを data_return に読み込む。 バイト数が 4 の倍数でなければ _XReadPadは調整のために 3 バイトまでの追加データを読み込んで破棄する。

それぞれのプロトコルリクエストは少しずつ異なる。 さらに詳しい情報については Xlib のソースコード等を見ること。 .SH 同期呼び出し

各手続きは、ユーザ処理に戻る寸前に SyncHandleという幕rを呼ぶはずである。 同期モードが有効ならば( XSynchronizeを参照)このリクエストは即座に送られる。 しかし、ライブラリは、手続きがサーバで起こすかもしれない何らかのエラー が処理されるまで待つ。 .SH メモリの割り当てと解放

これらの手続きが再エントリする可能性に対応するため、メモリの割り当てと 解放を行うときにはいくつかの約束を守らなければならない。この処理は 前もって呼び出し側に大きさが分からないデータをウィンドウシステムから ユーザに返す場合に起きる(例えばフォントのリストや機能拡張のリスト等)。 多くのシステムの標準の C ライブラリ関数は、シグナルや その他の複数スレッドからの利用に対する防御をしていない。 標準 I/O ライブラリ関数に似せた関数として以下が定義されている:

Xmalloc () malloc ()を置き換える
XFree () free ()を置き換える
Xcalloc () calloc ()を置き換える

これらの関数は、通常の C ライブラリ版を使うところで、その代わりに利用 すべきである。

クリティカルな部分で作業用バッファをひとつ用意する必要がある場合には (例えばデータと通信プロトコルを相互変換する場合)、汎用的なメモリ 割り当てルーチンを使うコストは高すぎるかもしれない(特に出力関数の 内部。これは性能面でクリティカルである)。 以下の関数はクリティカルな部分で使うための作業用バッファを返す:

char *_XAllocScratch(display, nbytes)
Display *display;
unsigned long nbytes;
display
X サーバへの接続を指定する。
nbytes
必要なバイト数を指定する。

このメモリはスタブ内のクリティカルな部分だけで使わなければならない。 Xlib 内部で別のスレッドの実行を許すような呼び出しをひとつでも行った後 では、返されたポインタが正しいことは期待できない。例えば、 GetReqData系のマクロを使った後や、 _XReplyを使った後や、 _XSend_XRead系の関数を使った後には、このポインタが正しいことは期待できない。

以下の関数はクリティカルな部分をまたがって使える作業用バッファを返す。

char *_XAllocTemp(display, nbytes)
Display *display;
unsigned long nbytes;
display
X サーバへの接続を指定する。
nbytes
必要なバイト数を指定する。

このメモリは Xlib 内部で別のスレッドの実行を許すような呼び出しをまたがっ ても使える。メモリは明示的に Xlib に返さなければならない。 以下の関数はメモリを返却する。
void _XFreeTemp(display, buf, nbytes)
Display *display;
char *buf;
unsigned long nbytes;
display
X サーバへの接続を指定する。
buf
返却するバッファを指定する。
nbytes
バッファの大きさを指定する。

_XAllocTempから受け取ったのと同じポインタを同じサイズ指定で返さなければならない。 .SH 移植性の考慮

最近の RISC アーキテクチャの多くを含むたくさんのマシンアーキテクチャで は、アラインメント調整されていない位置にあるデータに正しくアクセスでき ない。 この特性を扱うため、こういったアークテクチャに対応するコンパイラは調整 のためのデータを構造体に入れる。 構造体の中にアラインメント調整されていない参照が入っていても大丈夫なそ の他の多くのマシンでも、アラインメントの保存は行われる。なぜなら、 アラインメント調整されたデータへのアクセスの方が普通はずっと速いからで ある。 ライブラリとサーバはバイトストリーム中の任意の位置にあるデータに アクセスするために構造体を使うので、リクエストと応答のパケットに含まれ る全てのデータは自然にアラインメント調整されていなければならない。 つまり、リクエスト中では 16 ビットのデータは 16 ビット境界から始まり、 32 ビットデータは 32 ビット境界から始まる。 データストリーム中でも自然なアラインメントは位置を保つため、全ての リクエストの長さは 32 ビットの倍数でなければならない。 構造体は大きさを水増しして 32 ビット境界に調整しなければならない。 水増し分のデータは、将来にそのフィールドをプロトコルリクエストで使うた めにとっておくつもりがなければ、値を 0 にする必要はない。 浮動小数点の扱いはマシンごとに大きく違うので、できるなら水増しに使うの は絶対に避けるべきである。

このコードは int 型 が 16 ビットのマシンで動かすことがあるかもしれない。 したがって、int 型の引き数、変数、返り値がある場合は、 負でない値しか取れないようにするかプロトコル中で CARD16として宣言し、 intでなく必ず unsignedint型で宣言されるようにすること。 (当然、これは真偽値や列挙の場合には適用されない。)

同様に、整数の引き数や戻り値がプロトコル中で CARD32で宣言されている場合は、これは intlongでなく unsignedlongとして宣言すること。 これは、最大値が 16 ビットの unsignedintを超える値を取る全ての内部変数にも適用される。

現在のライブラリでは charは 8 ビット、 shortは 16 ビット、 intは 16 または 32 ビット、 longは 32 ビットだと想定している。 PackData マクロでは、short が 32 ビットであるかもしれない場合の扱いがある程度試 みられている。 しかし、これを適切に動作させるにはまだまだ作業が必要である。 .SH 機能拡張の正しいオペコードの求め方

機能拡張のスタブ手続きを書く場合に出会う問題のうち、コアプロトコルの場 合には出会わないものの残りとしては、呼び出しを適切なメジャーコードと マイナーコードにマップすることがある。 方針はたくさんあるが、最も簡単かつ早い方法を以下にまとめる。

1.
型が XExtCodesへのポインタで、長さが _NFILE (これは普通は <stdio.h>で宣言されていて、システムが対応しているファイルデスクリプタの数である) である配列を宣言する。 必ず配列は全て NULL で初期化すること。
2.
スタブに入った時の初期化のテストでは、ディスプレイポインタを使って、 ファイルデスクリプタと配列へのインデックスにアクセスだけを行う。 このエントリが NULL ならば、このディスプレイでは初めて手続きに入ったと いうことである。 したがって初期化手続きを呼び出して、これにディスプレイポインタを渡すこと。
3.
初期化手続きに入ったら XInitExtensionを呼ぶ。 これが成功したら、返されたポインタを配列に格納する。 エントリを 0 にできるようにするため、クローズディスプレイハンドラを 必ず作ること。 機能拡張が必要とする他の全ての初期化処理を行う。 (例えば、イベントハンドラのインストールなどである。) 初期化手続きは普通は機能拡張の XExtCodes 構造体へのポインタを返す。これは普通は最初に宣言したポインタの配列の中 に含まれている。
4.
初期化手続きから復帰した後は、スタブは普通に実行継続できるようになっている。 なぜなら、機能拡張のメジャーオペコードは XExtCodes 構造体へ安全に入手できているからである。
目次に戻る