拦截X11上的WM_DELETE_WINDOW?

我想拦截WM_DELETE_WINDOW消息,该消息发布到我正在编写的应用程序( AllTray )的特定窗口中,以便我可以对其执行操作,而不是接收应用程序。 如果可能的话,我现在正在通过gdk_display_add_client_message_filter在GDK级别尝试此操作,但是如果有Xlib解决scheme,我也会很高兴。 这似乎是可能的,但我似乎并没有理解我如何成功地做到这一点。

目前,我有两个程序(用C语言编写),我试图用它来解决这个问题, 第一个程序只是创build一个窗口并注册,它知道WM_DELETE_WINDOW , 第二个程序试图捕获这个消息,但似乎失败了; 它似乎没有做任何事情。 我是否理解了这个错误的文档,或者是否还有其他需要做的事情(或者我需要完全避免使用GDK)?

背景是这样的:在我重写AllTray之前,它会做的事情似乎是试图拦截一个鼠标点击Xbutton本身。 对于一些窗口pipe理器来说,这个工作正常,对于其他人来说根本不起作用,对于其他人来说,用户必须手动configuration它,并指示AllTrayclosures窗口的button。 我正在寻找的是不涉及LD_LIBRARY_PRELOAD的解决scheme,它将适用于任何符合当前标准的窗口pipe理器/应用程序组合,并在窗口closures时发送WM_DELETE_WINDOW ClientMessage。

更新 :我仍然在寻找答案。 我现在采取的路线是试图重新开窗并自己pipe理,但是我不能使它工作。 重新装修后,我似乎无法以任何方式恢复原状。 我可能会错过一些非常基本的东西,但是我不知道如何让它再次出现在我自己的窗口中,然后把它带回到屏幕上。

更新2 :好的,所以我打了另一堵砖墙。 X服务器文档说在窗口的事件掩码上设置StructureNotifyMask以接收MapNotify和ReparentNotify事件。 我有兴趣收到。 我目前的想法是创build一个窗口,作为一个事件接收器,然后当我得到有趣的事件的事件,通过创build和reparent行事。 但是,这似乎并没有工作。 我实际收到的唯一事件是PropertyNotify事件。 所以,这条路线似乎也不怎么样。

Solutions Collecting From Web of "拦截X11上的WM_DELETE_WINDOW?"

我不知道X11,但我WM_DELETE_WINDOW使用“拦截WM_DELETE_WINDOW X11”作为关键字。 找到17k – MarkMail和Mplayer提交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应用程序中处理窗口关闭 :

当用户点击X11应用程序中的关闭按钮[x] ,我们希望它弹出一个对话框,询问“你真的要退出吗?”。 这是一个普通的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. 为应用程序创建X11代理,在应用程序和X服务器之间来回传递所有X11协议消息。 代理会过滤掉任何有趣的消息。 这样做的缺点是这对于一个小小的功能来说是一个非常大的开销,而X11协议是复杂的。 也可能会有意想不到的后果,这使得这是一个更没有吸引力的选择。
  2. 作为标准应用程序启动,充当窗口管理器和“有趣”的客户端应用程序之间的中介。 这打破了一些东西,比如XDnD。 实际上,它与第一个选项不同,除了代理位于Window级别而不是X11协议级别之外。
  3. 使用不可移植的LD_PRELOAD库技巧。 这有几个缺点:
    1. 它在动态链接器中是不可移植的:即使在类UNIX系统中,并不是所有的动态链接器都支持LD_PRELOAD
    2. 它是跨操作系统不可移植的:并非所有操作系统都支持有特色的动态链接器。
    3. 它破坏了网络透明性:共享对象/动态链接库必须作为正在执行的子进程驻留在主机上。
    4. 并非所有的X11应用程序都使用Xlib; 有必要为应用程序可能用来与X11交谈的每个库编写一个LD_PRELOAD模块。
    5. 除了最后一点之外,并不是所有的应用程序都会受到LD_PRELOAD影响,即使它们在支持它的链接器下运行,因为它们可能不使用共享对象或DLL来与X通信; 例如,考虑使用Java本身编写的X11协议库的Java应用程序。
    6. 在某些类UNIX操作系统上,如果LD_PRELOAD库要与setuid / setgid程序一起使用,则它们必须是setuid / setgid。 这当然是一个潜在的安全漏洞。
    7. 我相当肯定,我想不出更多的缺点。
  4. 实现对X Window系统的扩展。 在X11实现中是不可移植的,复杂而复杂,因为所有的东西都出来了,绝对是不可能的。
  5. 实现窗口管理器的扩展或插件。 窗口管理人员的数量与窗口管理人员的意见一样多,因此这是完全不可行的。

最终,我能够通过完全独立的机制来实现我的目标。 任何有兴趣的人,请参阅AllTray 0.7.5.1dev及更高版本中的Close-to-Tray支持,包括github上提供的git master分支 。

好的,为了详细说明我以前的建议,你可能需要调查XEmbed 。 至少,这可能会给你一些想法尝试。

否则,我会看看其他类似软件可能如何工作(例如wmdock,或如何实现GtkPlug / GtkSocket),虽然我相信在这些情况下明确的支持是在应用程序中需要的。

希望更有帮助。

您应该阅读ICCCM,告诉您窗口管理器如何与客户端进行通信。 大多数WM将通过重新创建一个框架窗口来包含您的顶级窗口。 因此,如果你的代理可能会破坏WM和你的客户窗口的关系。