Перехватить WM_DELETE_WINDOW на X11? - PullRequest
12 голосов
/ 21 июля 2009

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

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

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

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

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

Ответы [ 4 ]

12 голосов
/ 27 июля 2009

Я не знаю X11, но я гуглил, используя "Intercept WM_DELETE_WINDOW X11" в качестве ключевых слов. Найдено 17k - MarkMail и Mplayer-commits r154 - транк / 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;
}
1 голос
/ 29 июля 2011

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

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

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

0 голосов
/ 25 июня 2011

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

0 голосов
/ 31 июля 2009

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

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

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

...