Сколько раз я напарывался на то, что мои X'овые приложения вылетали с ошибкой, если их закрывать тыкая в кнопку с крестом в верхнем правом углу окошка. Но обычно я задвигал на это, потому как вылет с ошибкой, если по-хорошему, давал тот самый нужный результат: приложение завершалось.
А тут я попытался писать на lisp'е, используя clx... И тут началась хрень невыносимая: вместо вылета с ошибкой генерилось исключение, типа end-of-file, стек разматывался как часовая пружина выскочившая из механизма... И если я не втыкал код обработки исключения, то кончалось это появлением дебуггера, который предлагал мне вручную исправить ситуацию и помочь приложению работать дальше (точнее завершаться, поскольку работа к тому моменту уже сводилась к корректному завершению). Это уже было неприятно, и я полез выяснять: что за хрень? Причём, сначала, я не связал это с предыдущим экспириенсом написания X-клиентов на C, и думал, что clx закрывает соединение с X-сервером где-то в недрах event-case.
Буу... Я изучил как работает clx. Выяснил, что он нигде не закрывает соединение. Значит соединение закрывает сервер? С какого, спрашивается, хуя, он закрывает соединение, если его об этом не просят? Думаю: что clx делает не так, что сервер закрывает соединение. Запустил wireshark, начал снифать трафик, изучая параллельно протокол X11. К вечеру у меня стало складываться устойчивое ощущение, что clx нисколько не виноват в том, что сервер закрывает соединение. Я начал подозревать, что сервер это делает по собственной инициативе... И тут я вспомнил, что подобное происходило и при написании программ на C, но детали происходившего помнил я с трудом. Я написал элементарного X-клиента на C, запустил и убедился в том, что при закрытии окошка таким образом X-сервер-сука закрывает соединение. Я создал пять окошек из одного клиента, и увидел, что если закрыть любое из этих окошек, то соединение закрывается, несмотря на то, что на нём висит ещё четыре окна. То есть одно нажатие на один крести закрывает все пять окон.
В общем, после всей это исследовательской одиссеи, я наконец-таки начал приставать к гуглу с
правильными вопросами. Я пытался понятно для гугла сформулировать вопросы о том, как обработать нажатие на ту долбаную кнопку, я спрашивал о том, как писать многооконное приложение для X'ов... И в конечном счёте я выяснил в чём хрень.
Короче, та кнопка -- это творение window-manager'а, но не X-сервера. И когда происходит левый тык мышкой по кнопке, это событие обрабатывает именно WM. И он, сцуко такое, не просто делает XDestroyWindow на моём окне, он делает на этом окне XKillClient, и естественно X-сервер после этого закрывает соединение.
Но, есть такой документ:
http://tronche.com/gui/x/icccm/sec-4.html#s-4.2.8.1 И там написано, что с WM можно договориться по-хорошему, чтобы тот вместо отправки пакета XKillClient серверу, отправлял бы мне Event типа client-message... Ну и со всеми вытекающими: у меня появляется возможность отработать закрытие окна, быть может спросить у юзверя, надо ли ему это, и тд. И главное-то: соединение не закрывается, и если есть несколько окошек, то они все разом не умрут.
И в коде эта фишка выглядит примерно так:
code:
win = XCreateWindow(бла-бла-бла);
бла-бла-бла;
XMapWindow(бла-бла-бла);
бла-бла-бла;
...
/* главная хрень: сказать что наше окошко умеет обрабатывать WM_DELETE_WINDOW: */
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 1);
XSetWMProtocols(display, win, &wm_delete_window, 1);
/* после же этого, отловить закрытие окна в главном цикле -- элементарно: */
while(XPending(display)) {
XEvent ev;
XNextEvent(display, &ev);
switch(ev.type) {
case ClientMessage:
if((Atom)(ev.xclient.data.l[0]) == wm_delete_window) {
printf("Какая-то сука нажала крестик...\n");
goto exit_main_loop; /* а если нихуя не делать, то юзверь
* хоть утыкается в кнопку, а толку
* не будет никакого */
}
case ...
}
}
exit_main_loop:
бла-бла-бла