40

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

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 アプリを適切に終了する唯一の方法ですか? クローズイベントをキャプチャするだけでも大変な作業のようです。「適切な」イベントループを作成するにはどうすればよいですか? クローズイベントが深く埋もれているのはなぜですか?私は何が欠けていますか?

4

2 に答える 2

66

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

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

XDestroyWindowウィンドウを削除するために自分で呼び出すことができます。これは通常、ウィンドウが画面から消えることを意味しますが、プログラムはまだ実行されており、X サーバーへの接続はまだ開いているため、さらに要求を送信できます。

問題は、ユーザーが Window Manager によってウィンドウに取り付けられた小さなXボタンをクリックしたときに始まります。これは、X サーバーによって作成されたものではなく、何をすべきかを決定するのはユーザーの仕事ではないためです。これですべて Window Manager の手に委ねられました。

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

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

これを説明するXlib ドキュメントからの抜粋を次に示します。

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

ええ、彼らがドキュメントの奥深くにそれを隠していなければ、それは素晴らしいことです.

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

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

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

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

于 2014-03-24T14:55:28.687 に答える
23

X11 には、「終了ボタン」や「アプリケーション」、「閉じるイベント」などはありません。これは仕様によるものです。

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

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

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

于 2012-05-29T10:48:48.667 に答える