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

Как выйти из программы X11 без ошибок

У меня довольно простой "Hello World" в X11 в конце вопроса. Но когда он выходит, я получаю сообщения об ошибках времени выполнения ниже:

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

Итак, я попытался обработать сам wmDeleteMessage, и мне удалось закрыть окно, поэтому я знаю, что правильно получаю событие. Чем я добавил XDestroyWindow() для обработки событий, и я получаю новые ошибки.

X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  4 (X_DestroyWindow)
  Resource id in failed request:  0x130
  Serial number of failed request:  12
  Current serial number in output stream:  12

Похоже, я пытаюсь уничтожить уже разрушенное окно, но если я вынесу XDestroyWindow(), он останется живым на моем экране.

Ниже мой код с попыткой обработчика окна уничтожить. Как я могу выйти без ошибок?

#include<X11/Xlib.h>
#include <iostream>

int main()
{
  Display *display;
    if(!(display=XOpenDisplay(NULL))) 
    {
      std::cerr << "ERROR: could not open display\n";
      return 1;
    }

  int screen = DefaultScreen(display);
  Window rootwind = RootWindow(display, screen);
  Colormap cmap = DefaultColormap(display, screen);      
  Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);

  int blackColor = BlackPixel(display, screen);
  int whiteColor = WhitePixel(display, screen);

  Window w = XCreateSimpleWindow(display, rootwind, 0, 0, 200, 100, 0, blackColor, blackColor);
  XMapWindow(display, w);
  XSetWMProtocols(display, w, &wmDeleteMessage, 1);
  bool running = true;
  while(running) 
  {
    XEvent e;
    XNextEvent(display, &e);      
    switch  (e.type) 
    {
      case ClientMessage:
        if(e.xclient.data.l[0] == wmDeleteMessage) 
        {
          std::cout << "Shutting down now!!!" << std::endl;
          XDestroyWindow(display,e.xdestroywindow.window);
          running=false;
          break;
        }
        break;
    }
  }

    XCloseDisplay(display);
    return 0;
}

Обновление

Изменена строка:

   std::cout << "Shutting down now!!!" << std::endl;
        XDestroyWindow(display,w);

Что мне не нравится, потому что я планирую иметь больше окна, но пока я назад к первому сообщению об ошибке, которое у меня было:

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

Обновление

Попытался изменить многие вещи, как если бы цикл был запущен XPending(). Решил запустить кого-то еще привет мир, и я получаю ту же проблему с их кодом. Должно быть что-то не так с моей настройкой.

Обновление По-видимому, у многих людей есть эта проблема. Эта проблема возникла у Google ftk, и они исправили ее в своем журнале изменений . Они называют FTK_QUIT(), который, как я предполагаю, похож на Exit(). Поэтому я поставил свое возвращение прямо внутри цикла, и это решило проблему. Не знаю, почему, но это так. фиксированный код:

  case ClientMessage:
    if(e.xclient.data.l[0] == wmDeleteMessage) 
    {
      XDestroyWindow(display,e.xclient.window);
      XCloseDisplay(display);
      return 0;
    }

Будет по-прежнему давать правильный ответ тому, кто может объяснить, почему и если возможно, переместить оператор return (вместе с XCloseDisplay) за пределы цикла.


Цикл Event должен выглядеть так, чтобы он был правильно выполнен:

  XEvent e;
  do
  {
    XNextEvent(display, &e);      
    if(e.type == ClientMessage && e.xclient.data.l[0] == wmDeleteMessage) 
    {
      XDestroyWindow(display,e.xclient.window);
      break;    
    }
    //...
  }while (XPending(display) > 0)
  XCloseDisplay(display);
  return 0;

При работе в инструкции switch код не работает. Даже если он выходит из цикла без вызова другой функции Х. Вышеуказанный оператор if, поставленный перед оператором switch, исправляет проблему, не возвращаясь из программы внутри цикла.

4b9b3361

Ответ 1

Решение этой проблемы прост:

Вы должны использовать правый член структуры с помощью функции XDestroyWindow().

Благодаря стандарту реализации структур событий X11 они очень похожи друг на друга. Каждая структура начинается с элемента "type", и первые члены практически всегда одинаковы.

Теперь предположим:

int = 4 bytes
Bool = 4 bytes
unsigned long = 8 bytes
Display* = 8 bytes
Window = 4 bytes

Если вы вызываете XDestroyWindow() с e.xdestroywindow.window, вы будете находиться на расстоянии 28 байтов от начала структуры событий, а если вы используете e.xclient.window, вы должны быть в 24 байтах.

Поскольку вы собираетесь вызывать XDestroyWindow() с неправильным аргументом Window, он не сработает. Вместо этого, если вы вызываете его с помощью e.xdestroywindow.event(который находится на расстоянии 24 байта от начала структуры события), адрес будет прав, и функция будет работать изящно.

Если вы посмотрите на файл Xlib.h, вы заметите, что эти две структуры имеют элемент окна, расположенный по-разному.

Сказанное, помните, что Xlib был разработан много лет, и многие программисты работают с ним каждый день, поэтому, если есть загадочная ошибка, возможно, это не в Xlib. В качестве последнего намека я хочу вам сказать: если вы хотите продолжить программирование Xlib, всегда принимайте файлы заголовков в качестве основной ссылки, затем следует руководство по системе, а затем все остальные.

Единственная ошибка с вашим кодом в конце:

XDestroyWindow(display,e.xdestroywindow.window);

Что должно быть изменено на это:

XDestroyWindow(display,e.xclient.window);

Вместо этого использование switch является хорошим и наиболее реализуемым, без проблем с кодом X11.

ПРИМЕЧАНИЕ. Я сам протестировал ваш код, изменив только эту строку, а затем выполнив различные тесты, распечатав результат. Строка XDestroyWindow() - это, безусловно, единственная ошибка.

Ответ 2

Просто позвоните XDestroyWindow() прямо перед XCloseDisplay().

Edit:

Извините, я не понял вещь XSetWMProtocols. Теперь я прочитал об этом. Я думаю, что вы обращаетесь к неправильному члену союза событий.

XDestroyWindow (дисплей, e.xdestroywindow.window);

Должно быть:

XDestroyWindow(display,e.xclient.window);

Ответ 3

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

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

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

Проблема заключается в том, что когда пользователь нажимает на X в строке заголовка окна, это не значит, что X Server обрабатывает его, но задание диспетчера окон (X-сервер ничего не знает об этих украшениях и это неважно). Обычная реакция диспетчера окон, когда пользователь закрывает окно верхнего уровня вашей программы, - это уничтожить окно и закрыть соединение с сервером X, потому что это то, что большинство пользователей ожидали бы. Ваша программа все еще может работать вне экрана, но окно верхнего уровня обычно связано с подключением X-сервера диспетчером окон.

Поэтому, когда Window Manager уничтожает ваше окно, вы не можете вызвать XDestroyWindow, потому что окно уже уничтожено, а дескриптор Window недействителен. Вы получите сообщение об ошибке BadWindow. Вы также не можете вызвать XCloseDisplay, потому что соединение с X-сервером уже закрыто, и это вызовет ошибку XIO: fatal IO error 11 (Resource temporarily unavailable) on X server, которую многие пользователи испытывают от приложений, авторы которых этого не знали. Это распространенная ошибка, потому что в одной руке вас поощряют очищать после себя, но, с другой стороны, документация вводит в заблуждение относительно того, как это должно быть сделано правильно.

Однако существует соглашение о том, как X Server и Window Manager должны взаимодействовать, что также охватывает ответы на пользовательские команды, чтобы закрыть окно верхнего уровня. Там есть расширение для протокола X, который его обрабатывает. Здесь документация Xlib объясняет это:

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

Итак, есть два решения этой проблемы: либо не вызывайте XDestroyWindow и XCloseDisplay, когда окно закрывается диспетчером окон, а не самим собой (на самом деле вам не нужно очищать верхний уровень окно, так как X-сервер уничтожит его, тем не менее, когда ваша программа закончится), или вам необходимо зарегистрировать расширение WM_DESTROY_WINDOW и дождаться уведомления от диспетчера окон, когда пользователю будет предложено закрыть ваше окно (оно отправит вам ClientMessage, тогда с его data[0] установите WM_DELETE_WINDOW). И после его получения просто уничтожьте окно и закройте соединение с сервером X самостоятельно и завершите свою программу. Или оставьте соединение с сервером X открытым, чтобы выполнить дополнительную связь с ним, если хотите. Когда вы обрабатываете WM_DESTROY_WINDOW, диспетчер окон не будет пытаться уничтожить ваше окно и не закрыть соединение с X-сервером.