Подтвердить что ты не робот

Как изящно выйти из цикла событий 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? Кажется, что много работы просто для того, чтобы зафиксировать закрытое событие. Как создать "правильный" цикл событий? Почему близкое событие так глубоко погребено? Что мне не хватает?

4b9b3361

Ответ 1

В X11 нет таких вещей, как "кнопка выхода" или "приложение" или "закрытие события". Это по дизайну.

Декорации окон, кнопки выхода и многое другое, на что мы зависим, не встроены в X11. Они реализованы вместо ядра X11. Название конкретного набора соглашений, ответственных за wmDeleteMessage, является ICCCM, посмотрите его.

Xlib работает только с основным протоколом X11. Нет встроенного закрытого события.

Существуют инструментальные средства, которые позволяют работать с ICCCM и другими вещами, которые не встроены в X11 (GTK, wxWindows, Qt,...) Возможно, вы захотите использовать один из них.

Ответ 2

Проблема заключается в связи между X Server и Window Manager.

Когда вы вызываете XCreateWindow или XCreateSimpleWindow, X-сервер создает ваше окно (не показывая его, пока вы явно не отобразите его на экране, вызвав XMapWindow), а затем Менеджер окон отвечает за присоединение всех украшения и кнопки и системное меню вокруг вашего окна.

Вы можете вызвать XDestroyWindow самостоятельно, чтобы удалить окно, и это обычно означает, что он просто исчезает с экрана, но ваша программа все еще работает и соединение с X-сервером по-прежнему открыто, поэтому вы можете отправить его еще несколько запросов.

Проблема начинается, когда пользователь нажимает эту маленькую кнопку X, прикрепленную к вашему окну диспетчером окон, потому что она не создана X-сервером, и это не его бизнес, чтобы решить, что делать. Теперь все это находится в руках диспетчера окон.

Если диспетчер окон просто вызвал XDestroyWindow в вашем окне, это вызовет проблему, если ваше приложение захочет захватить закрывающее событие, чтобы что-то сделать до того, как окно будет уничтожено. Таким образом, соглашение было установлено между X Server и Window Managers для обработки этого процесса.

Поведение большинства менеджеров окон по умолчанию - это уничтожить окно и закрыть соединение с сервером X, потому что это то, что ожидали большинство пользователей Window Managers: когда они закрывают окно, программа завершится (и соединение с X-сервером закроется закрытым окном). И затем, когда вы пытаетесь вызвать XCloseDisplay(display), это вызовет ошибку ввода-вывода, о которой вы упомянули, потому что соединение с сервером уже закрыто, а структура display недействительна.

Вот выдержка из документации Xlib, которая объясняет это:

Клиенты, которые не хотят включать WM_DELETE_WINDOW в свойство WM_PROTOCOLS, могут быть отключены от сервера, если пользователь просит удалить один из окон верхнего уровня клиента.

Да, было бы здорово, если бы они не скрывали это настолько глубоко в своих документах, хотя: -P Но когда вы его уже нашли, к счастью, это также намекает на решение.

Если вы хотите другое поведение (то есть, чтобы зафиксировать событие закрытия из диспетчера окон), вам нужно использовать протокол WM_DESTROY_WINDOW.

Другая выдержка из документов:

Клиенты, обычно те, у которых несколько окон верхнего уровня, чье серверное соединение должно пережить удаление некоторых из окон верхнего уровня, должно включать атом WM_DELETE_WINDOW в свойстве WM_PROTOCOLS в каждом таком окне. Они получат событие ClientMessage, как описано выше, чье поле data[0] WM_DELETE_WINDOW.

У меня была такая же ошибка, и я хотел точно знать, что вызывает ее и почему. Мне потребовалось некоторое время, чтобы понять это и найти правильное объяснение в документе, поэтому я изложил свое объяснение здесь, чтобы сохранить время, когда другие не проинформированы.