X11: X エラー

X エラー
X でエラーが出た時の処理について説明します。

X でエラーが出た場合 (引数の値が正しくないなど)、デフォルトのハンドラによってエラーメッセージが表示された後、プログラムが強制終了してしまいます。

致命的ではないエラーの場合もこのような処理になってしまうので、X のエラーはクライアント側で処理するべきです。

特に、ウィンドウマネージャが関わる部分では、致命的ではないエラーが発生することがあります。
エラーハンドラ
X のエラーを自分で処理したい場合は、エラーハンドラを設定します。

XErrorHandler XSetErrorHandler(XErrorHandler handler);
XIOErrorHandler XSetIOErrorHandler(XIOErrorHandler handler);

typedef int (*XErrorHandler)(Display *display, XErrorEvent *error_event);
typedef int (*XIOErrorHandler)(Display *display);

XSetErrorHandler() は、プロトコルエラーを処理するハンドラを設定します。
XSetIOErrorHandler() は、致命的な I/O エラーを処理するハンドラを設定します。

両方とも、以前に設定されていたハンドラのポインタが返ります。
XSetIOErrorHandler
XSetIOErrorHandler() で設定したハンドラは、致命的なエラーが起こった時に呼ばれるので、ハンドラ関数内で強制終了するべきです。
ハンドラ関数から戻った場合、クライアントのプロセスは終了します。

これはデフォルトのハンドラのままでも構いません。
XSetErrorHandler
XSetErrorHandler() で設定したハンドラは、引数の値が正しくないなどの、致命的ではないエラーの時に呼ばれるので、ハンドラ関数から戻ってこれます。
その場合、ハンドラ関数の戻り値は無視されます。

XErrorEvent 構造体に、エラーに関する情報が入っています。

typedef struct {
 int type;
 Display *display;
 unsigned long serial;
 unsigned char error_code;
 unsigned char request_code;
 unsigned char minor_code;
 XID resourceid;
} XErrorEvent;

serialX サーバーと接続した後、ネットワーク接続を介して送信されたリクエストの数 (1〜)。
失敗した呼び出しが行われる直前の、NextRequest(display) の値。
error_codeエラーコード。以下のいずれか。
(拡張機能のエラーの場合、追加のエラーコードが存在する場合があります)

BadAccess, BadAlloc, BadAtom, BadColor, BadCursor, BadDrawable
BadFont, BadGC, BadIDChoice, BadImplementation, BadLength
BadMatch, BadName, BadPixmap, BadRequest, BadValue, BadWindow
request_code<X11/Xproto.h> で定義されている、失敗したプロトコルリクエスト番号
minor_code拡張機能時のマイナー番号
resourceidウィンドウなどのリソースID
エラー関連の文字列取得
エラーコードから文字列取得
void XGetErrorText(Display *display, int code, char *buffer_return, int length);

XErrorEvent の error_code から、説明文字列を取得します。
返されるテキストは、現在のロケールのエンコーディングです。
エラーメッセージデータベースから取得
void XGetErrorDatabaseText(Display *display, char *name, char *message,
    char *default_string, char *buffer_return, int length);

エラーメッセージデータベースから、error_code, request_code, minor_code の値を使って、メッセージを取得します。

name取得する情報を文字列で指定します。

"XlibMessage" : ライブラリによって内部的に使用されるメッセージ文字列。
"XProtoError" : message は、プロトコルエラー番号 (XGetErrorText と同じ)。
"XRequest" : コアのプロトコルリクエストの場合、message はメジャーリクエスト番号。
拡張機能の場合、message は「InitExtension によって指定される拡張名」 + 「ピリオド (.)」 + 「マイナープロトコル番号」。
messagename ごとの情報を、文字列で指定します。
default_stringデータベースに見つからなかった場合の、デフォルトのメッセージ
プログラム
存在しないウィンドウを削除しようとして、エラーが表示されます。

<09-error.c>
#include <stdio.h>
#include <X11/Xlib.h>

static int _xerr_handle(Display *display,XErrorEvent *ev)
{
    char m[256],no[16];

    printf("--- X error ---\n"
        "type(%d) serial(%lu) error_code(%d)\n"
        "request_code(%d) minor_code(%d) resourceid(%lu)\n\n",
        ev->type, ev->serial, ev->error_code,
        ev->request_code, ev->minor_code, ev->resourceid);

    XGetErrorText(display, ev->error_code, m, 256);
    printf("[Error] %s\n", m);

    snprintf(no, 16, "%d", ev->request_code);
    XGetErrorDatabaseText(display, "XRequest", no, "default error", m, 256);
    printf("[Request] %s\n", m);

    return 1;
}

int main(int argc,char **argv)
{
    Display *disp;

    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    XSetErrorHandler(_xerr_handle);

    XDestroyWindow(disp, 0);

    XCloseDisplay(disp);

    return 0;
}

--- X error ---
type(0) serial(7) error_code(3)
request_code(4) minor_code(0) resourceid(0)

[Error] BadWindow (invalid Window parameter)
[Request] X_DestroyWindow

XDestroyWindow() で正しくない Window 値を指定しているので、X_DestroyWindow のリクエストで、BadWindow エラーが起こっています。