web-dev-qa-db-ja.com

X11イベントループを適切に終了するにはどうすればよいですか?

私が見つけたほとんどすべてのチュートリアルは、私のイベントループに対してこれを行うように指示しています:

XEvent event;

while (true)
{
    XNextEvent(display, &event);

    switch (event.type)
    {
        case Expose:
            printf("Expose\n");
            break;

        default:
            break;
    }
}

ただし、Xをクリックしてプログラムを閉じると、このメッセージが表示されます。

XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 10 requests (10 known processed) with 0 events remaining.

例が無限ループの使用を示唆しているのは、本当に奇妙です。それは自然に聞こえません、そして私の他のX11プログラムはそれをしません。それで私は周りを検索しました。ウィンドウを閉じるイベントをキャプチャする方法を見つけました。

Atom wmDeleteMessage = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wmDeleteMessage, 1);

XEvent event;
bool running = true;

while (running)
{
    XNextEvent(display, &event);

    switch (event.type)
    {
        case Expose:
            printf("Expose\n");
            break;

        case ClientMessage:
            if (event.xclient.data.l[0] == wmDeleteMessage)
                running = false;
            break;

        default:
            break;
    }
}

うまくいきます。エラーなしで終了します。 ...しかし、私はこれが物事を行うための通常の方法であるとは信じません。つまり、これはX11アプリを適切に終了する唯一の方法ですか?クローズイベントをキャプチャするだけでも大変な作業のようです。 「適切な」イベントループを作成するにはどうすればよいですか?クローズイベントがなぜそれほど深く埋もれているのですか?何が欠けていますか?

39
TheBuzzSaw

X11には、「exitボタン」、「application」、「closeイベント」などはありません。これは仕様によるものです。

ウィンドウの装飾、終了ボタンなど、私たちが依存している他の多くのものはX11に組み込まれていません。代わりにコアX11の上に実装されます。 wmDeleteMessageを担当する特定の規則のセットの名前はICCCMです。調べてください。

XlibはコアX11プロトコルのみを扱います。組み込みのクローズイベントはありません。

ICCCMや、X11に組み込まれていないその他すべてのもの(GTK、wxWindows、Qtなど)を処理するためのツールキットがあります。

21

問題は、Xサーバーとウィンドウマネージャー間の通信にあります。

XCreateWindowまたはXCreateSimpleWindowを呼び出すと、Xサーバーはウィンドウを作成し(XMapWindowを呼び出して画面に明示的にマップするまでウィンドウを表示しません)、次にウィンドウマネージャーは、ウィンドウの周りにあるすべての装飾とボタン、およびシステムメニューをアタッチする責任があります。

自分でXDestroyWindowを呼び出してウィンドウを削除できます。これは通常、ウィンドウが画面から消えるだけですが、プログラムはまだ実行中で、Xサーバーへの接続は開いたままなので、送信できますさらにいくつかのリクエスト。

問題は、ユーザーがその少しクリックしたときに始まります X Xサーバーによって作成されたものではなく、その後何をするかを決めるのは彼の仕事ではないため、ウィンドウマネージャーによってウィンドウに接続されたボタン。今ではすべてがウィンドウマネージャーの手に渡っています。

ウィンドウマネージャーが単にウィンドウでXDestroyWindowを呼び出した場合、アプリケーションがウィンドウが破棄される前に何かを行うためにクロージングイベントをキャプチャしようとすると、問題が発生します。そのため、このプロセスを処理するための規則がXサーバーとウィンドウマネージャーの間に確立されています。

ほとんどのウィンドウマネージャーのデフォルトの動作は、ウィンドウを破棄し、closeXサーバーとの接続です。これは、ウィンドウマネージャーのほとんどのユーザーが期待することです。つまり、ウィンドウを閉じると、プログラムが終了します(そして、Xサーバーへの接続は、閉じられたウィンドウで閉じられます)。そして、XCloseDisplay(display)を呼び出そうとすると、前述のIOエラーが発生します。サーバーへの接続がすでに閉じられており、display構造が無効です。

これは、これを説明する Xlibのドキュメント からの抜粋です。

WM_DELETE_WINDOWプロパティにWM_PROTOCOLSを含めないことを選択したクライアントは、ユーザーがクライアントの最上位ウィンドウのいずれかを削除するように要求した場合、サーバーから切断される可能性があります。

ええ、彼らが彼らのドキュメントでそれほど深くそれを隠さなかったらそれは素晴らしいでしょう:-Pしかし、あなたがすでにそれを見つけたとき、幸いにもそれは解決策のヒントにもなります。

別の動作が必要な場合(つまり、ウィンドウマネージャーからcloseイベントをキャプチャする場合)、WM_DESTROY_WINDOWプロトコルを使用する必要があります。

ドキュメントからの別の抜粋:

クライアント、通常は複数のトップレベルウィンドウがあり、サーバー接続が一部のトップレベルウィンドウの削除後も存続する必要があるクライアントは、atom WM_DELETE_WINDOWを各ウィンドウのWM_PROTOCOLSプロパティに含める必要があります。これらは、上記のdata[0]フィールドがWM_DELETE_WINDOWであるClientMessageイベントを受け取ります。

同じエラーが発生しましたが、何が原因で、その理由を正確に知りたいと思いました。理解してドキュメントで適切な説明を見つけるのに少し時間がかかったので、ここに説明を入れて、他の人の知らない時間を節約しました。

54
SasQ