Как выйти из программы 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
, исправляет проблему, не возвращаясь из программы внутри цикла.
Ответы
Ответ 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-сервером.