Перехватить WM_DELETE_WINDOW на X11?

Я хотел бы перехватить сообщение WM_DELETE_WINDOW, которое отправлено на определенный выбор окон, которые приложение, которое я пишу (AllTray), чтобы я мог воздействовать на него, а не на приложение, получающее его. В настоящее время я стараюсь попробовать это на уровне GDK первая ничего не делает но создайте окно и зарегистрируйтесь, что он знает о WM_DELETE_WINDOW, а второй пытается поймать это сообщение, но, похоже, это не срабатывает; похоже, ничего не делает. Я понимаю, что документация не соответствует этому, или есть что-то дополнительное, что мне нужно делать (или мне нужно вообще не использовать GDK для этого)?

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

UPDATE. Я все еще ищу ответ. Маршрут, который я принимаю на данный момент, - это попытаться восстановить окно и управлять им самостоятельно, но я просто не могу заставить его работать. После репарации я, похоже, не могу ее вернуть. Возможно, мне не хватает чего-то очень фундаментального, но я не могу понять, как на самом деле заставить его снова появиться в моем собственном окне, чтобы вернуть его на экран.

ОБНОВЛЕНИЕ 2: Хорошо, поэтому я ударил еще одну кирпичную стену. Документация X-сервера говорит, чтобы установить StructureNotifyMask в маске окна события, чтобы получать как события MapNotify, так и ReparentNotify. Я бы тоже хотел получить. Мое нынешнее мышление состояло в том, чтобы создать окно, которое служило как приемник событий, а затем, когда я получаю события для интересных вещей, действуйте на них, создавая и репарируя. Однако это просто не работает. Единственными событиями, которые я действительно получаю, являются события PropertyNotify. Таким образом, этот маршрут, похоже, тоже не очень хорош.

Ответ 1

К сожалению, лучшим ответом на этот вопрос является серия не ответов; есть технически способы его достижения, но у всех их есть недостатки, которые делают их крайне непрактичными:

  • Создайте прокси-сервер X11 для приложения, передав все сообщения протокола X11 взад и вперед между приложением и X-сервером. Затем прокси-сервер отфильтровывал любые интересные сообщения. Недостатком этого является то, что это очень много накладных расходов для одной маленькой крошечной функции, а протокол X11 является сложным. Также могут быть непреднамеренные последствия, что делает его еще более непривлекательным.
  • Запуск как стандартное приложение, которое выступает в качестве посредника между диспетчером окон и "интересными" клиентскими приложениями. Это нарушает некоторые вещи, такие как XDnD. По сути, это не похоже на первый вариант, за исключением того, что прокси-сервер находится на уровне окна, в отличие от уровня протокола X11.
  • Используйте невращаемый трюк библиотеки LD_PRELOAD. У этого есть несколько недостатков:
    • Он не переносится для динамических компоновщиков: не все динамические компоновщики поддерживают LD_PRELOAD, даже среди UNIX-подобных систем.
    • Он не переносится в разных операционных системах: не все операционные системы поддерживают функциональные динамические компоновщики.
    • Это нарушает прозрачность сети: библиотека общего объекта/динамической ссылки должна находиться на хосте в качестве выполняемого дочернего процесса.
    • Не все приложения X11 используют Xlib; необходимо будет написать один модуль LD_PRELOAD для каждой из библиотек, которые приложение может использовать для общения с X11.
    • В дополнение к последней точке не все приложения будут восприимчивы к LD_PRELOAD, даже если они работают под поддерживающим его компоновщиком, поскольку они не могут использовать общий объект или DLL для связи с X; рассмотрим, например, приложение Java, которое использует библиотеку протоколов X11, написанную на самой Java.
    • В некоторых UNIX-подобных операционных системах библиотеки LD_PRELOAD должны быть setuid/setgid, если они должны использоваться с программами setuid/setgid. Это, конечно, потенциальная уязвимость безопасности.
    • Я совершенно уверен, что это больше недостатков, о которых я не могу думать.
  • Внедрение расширения в систему X Window. Не переносимый среди X11-реализаций, сложный и запутанный, как все выходят, и абсолютно не может быть и речи.
  • Внедрить расширения или подключаемые модули для оконных менеджеров. Есть так много оконных менеджеров, что есть мнения относительно оконных менеджеров, и поэтому это совершенно невозможно.

В конечном счете, я смог, наконец, выполнить свою цель, используя совершенно отдельный механизм; любой, кто заинтересован, обратитесь к поддержке Close-to-Tray в AllTray 0.7.5.1dev и более поздних версиях, включая git, доступный в github.

Ответ 2

Я не знаю X11, но я googled с помощью "Перехватить WM_DELETE_WINDOW X11" в качестве ключевых слов. Найдено 17k - MarkMail и Mplayer-commits r154 - trunk/libvo. В обоих случаях они делают то же самое.

 /* This is used to intercept window closing requests.  */
 static Atom wm_delete_window;

внутри static void x11_init(),

XMapWindow(display, win);
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, win, &wm_delete_window, 1);

тогда в пределах static int x11_check_events(),

XEvent Event;
while (XPending(display)) {
    XNextEvent(display, &Event);
    if (Event.type == ClientMessage) {
        if ((Atom)Event.xclient.data.l[0] == wm_delete_window) {
            /* your code here */
        }
    }
}

См. XInternAtom, XSetWMProtocols и XNextEvent.

После того, как я написал выше, я нашел Окно "Обработка окна" в приложении X11:

Когда пользователь нажимает кнопку закрытия [x] в нашем приложении X11 мы хотим этого выставить диалог с вопросом: "Вы действительно хотите уйти?". Это просто X приложение. Никаких фантазийных виджетов GTK или QT Вот. Итак, как поймать "окно" быть закрытым "?

Ответ заключается в том, чтобы указать Окно Мы заинтересованы в этом менеджере событие, вызвав XSetWMProtocols и регистрация сообщения WM_DELETE_WINDOWс этим. Тогда хорошо возьмите клиента сообщение из диспетчера окон, если кто-то пытается закрыть окно, и он не будет закрывать его, он оставит нас до нас. Вот пример....

// example.cpp
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>

int main()
{
   Display* display = XOpenDisplay(NULL);
   Window window = XCreateSimpleWindow(display,
                                       DefaultRootWindow(display),
                                       0, 0,
                                       500, 400,
                                       0,
                                       0, 0);

   // register interest in the delete window message
   Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
   XSetWMProtocols(display, window, &wmDeleteMessage, 1);

   std::cout << "Starting up..." << std::endl;
   XMapWindow(display, window);

   while (true) {
      XEvent event;
      XNextEvent(display, &event);

      if (event.type == ClientMessage &&
          event.xclient.data.l[0] == wmDeleteMessage) {
         std::cout << "Shutting down now!!!" << std::endl;
         break;
      }
   }

   XCloseDisplay(display);
   return 0;
}

Ответ 3

Хорошо, чтобы рассказать о моем предыдущем предложении, вы можете изучить XEmbed. По крайней мере, это может дать вам некоторые идеи, чтобы попробовать.

В противном случае я бы посмотрел, как работает другое подобное программное обеспечение (например, wmdock или как реализован GtkPlug/GtkSocket), хотя я считаю, что в обоих случаях явная поддержка требуется в приложениях.

Надеюсь, что это будет более полезно.

Ответ 4

Вы должны прочитать ICCCM, который расскажет вам, как диспетчер окон взаимодействует с клиентом. Большинство WM создадут окно кадра, которое будет содержать ваше окно верхнего уровня через reparenting. Таким образом, если ваш reparent может разорвать отношения, известные WM и вашему клиентскому окну.