LECTURE 14 エラーイベント

○ はじめに

X ウィンドウシステムでは、エラーもイベントとしてクライアントに通知されます。ウィンドウマネージャを終了させて X ウィンドウから抜けてコンソールに戻ったときに、Bad Drawable ... とかなんとかいういかにもエラーっぽい表示を見たことがありませんか? (うちでは何だかしょっちゅう表示されています)。これらの表示は、X アプリケーションでエラーが生じた結果エラーイベントが発生してその内容が表示されているのです。

ここではエラーイベントについて説明していきます。

○ エラーイベントが起きる原因

エラーイベントには、ウィンドウを生成しようとしたがメモリ不足などによって生成に失敗した、生成していないウィンドウに対して描画しようとした、といった場合にそれを Xlib が検出して発生するものと、Xlib や X サーバ内で実行したシステムコールがエラーとなった場合に発生するものがあります。

これらのエラーが生じるとエラーイベントとしてクライアントに伝わりますが、通常のイベントと異なり Xlib にあらかじめ組み込まれているエラーイベントハンドラで処理されます。このエラーイベントハンドラは、イベントを解析しエラー情報を表示してプログラムを終了させます

エラーイベント情報は XErrorEvent 構造体に設定されて、エラーハンドラに渡されます。

typedef struct 
{
  int           type;         
  Display*      display;       /* イベントが読みだされたディスプレイ */
  unsigned long serial;        /* 失敗したリクエストのシリアル番号 */
  unsigned char error_code;    /* 失敗したリクエストのエラーコード */
  unsigned char request_code;  /* 失敗したリクエストのメジャーオペコード */
  unsigned char minor_code;    /* 失敗したリクエストのマイナーオペコード */
  XID           resourceid;    /* リソースID */
} XErrorEvent;

いくつかのエラーイベントを下表に示します(全てではありません)。

エラーイベント定数名説明
BadAlloc 要求リソースの割り当てに失敗
BadCursor パラメータがカーソルではない
BadDrawable パラメータが Drawable でない
BadFont パラメータがフォントではない
BadGC パラメータが GC ではない
BadImplementationX サーバに欠陥がある
BadMatch パラメータのミスマッチ
BadName フォント名かカラー名が存在しない
BadPixmap パラメータがピクスマップではない
BadValue 整数パラメータが範囲外
BadWindow パラメータがウィンドウではない

○ エラーイベントハンドラの変更

Xlib が提供しているデフォルトのエラーイベントハンドラは、エラーメッセージを表示してプログラムを終了させてしまいます。これに対して独自の終了処理を行ったり、プログラムの動作を継続させるために自作のエラーハンドラを新たに登録することができます。それには XSetErrorHandler() を使います。

int XSetErrorHandler( int ( *handler )(Display*, XErrorEvent* ) );

handler : 自作エラーハンドラへの関数ポインタ

指定した handler が新しいエラーイベントハンドラとして登録されます。handler に NULL を指定した場合にはデフォルトのハンドラが再設定されます。

例えば LECTURE 5 の xgraphics.c の四角形を描画している部分のコードを

/* 四角形の描画 */
XFillRectangle( dpy, ( Window )gc, gc, BOX_X, BOX_Y, BOX_W, BOX_H );

と書き換えて無理やり実行すると、次のようなエラー出力が表示されてプログラムが終了してしまいます。

X Error of failed request:  BadDrawable (invalid Pixmap or Window parameter)
  Major opcode of failed request:  70 (X_PolyFillRectangle)
  Resource id in failed request:  0x804b180
  Serial number of failed request:  17
  Current serial number in output stream:  19

この表示内容を変えるため、次のような関数 my_handler() を作成して、新しいエラーハンドラとして設定します。

int my_handler( Display* dpy, XErrorEvent* event )
{
    printf( "Now I have to go...\n" );
    exit( 1 );
}

XSetErrorHandler( my_handler );

その後再度実行すると、画面に

Now I have to go...

と表示が変わるのが確認できます(やっぱり終了しますが(^_^;) ちなみにサンプルソースコードは xerr.c です )。なお、XEvent 構造体の error_code を調べることで、エラーの種類に応じたエラー処理を行うことができます。個人的には、一般的な X アプリケーションにおいてどこまでエラーハンドラによる処理が行われているのか、いろいろ調べてみたいと思ってます。