【问题标题】:Intercept WM_DELETE_WINDOW on X11?在 X11 上拦截 WM_DELETE_WINDOW?
【发布时间】:2009-07-21 04:53:41
【问题描述】:

我想截获发布到我正在编写的应用程序 (AllTray) 的特定窗口选择的 WM_DELETE_WINDOW 消息,以便我可以对其进行操作,而不是应用程序接收它。如果可能的话,我目前正在考虑在 GDK 级别 via gdk_display_add_client_message_filter 尝试这个,但如果有一个 Xlib 解决方案,我也会很高兴; 似乎是可能的,但我似乎不明白如何成功地做到这一点。

目前,我有两个程序(用 C 编写),我试图用它们来解决这个问题,the first one 只创建一个窗口并注册它知道WM_DELETE_WINDOWthe second one 尝试捕捉该消息,但似乎没有这样做;它似乎什么也没做。我对此文档的理解是否有误,还是我需要做一些额外的事情(或者我是否需要完全避免为此使用 GDK)?

背景是这样的:在我重写 AllTray 之前,它的做事方式似乎是试图拦截鼠标单击 X 按钮本身。对于某些窗口管理器,这可以正常工作,对于其他窗口管理器,它根本不起作用,对于其他人,用户必须手动配置它并指示 AllTray 关闭窗口的按钮在哪里。我正在寻找的是一个不涉及LD_LIBRARY_PRELOAD 的解决方案,适用于任何符合当前标准的窗口管理器/应用程序组合,并在窗口关闭时发送WM_DELETE_WINDOW ClientMessage。

更新:我仍在寻找答案。我目前采取的路线是尝试重新设置窗口并自己管理它,但我无法让它工作。重生后,我似乎无法以任何方式找回它。我可能遗漏了一些非常基本的东西,但我不知道如何真正让它再次出现在我自己的窗口中,以使其重新出现在屏幕上。

更新 2:好的,所以我又碰了壁。 X 服务器文档说要在窗口的事件掩码上设置 StructureNotifyMask 以接收 MapNotify 和 ReparentNotify 事件。我也有兴趣收到。我目前的想法是创建一个仅用作事件接收器的窗口,然后当我收到有趣事物的事件时,通过创建和重新设置来对它们进行操作。但是,这似乎根本不起作用。我实际收到的唯一事件是 PropertyNotify 事件。所以,这条路线似乎也不是很好。

【问题讨论】:

  • 我认为可以通过在您自己的顶层重新设置窗口的父级并过滤您传递的事件来实现?我认为你目前尝试的方式行不通。
  • 这样做有什么缺点吗?也就是说,有什么特别的东西会导致它干扰 XDND 之类的东西吗?它是一个可移植的想法(例如,它不会破坏应用程序或窗口管理器)吗?我似乎能够找到很少的信息。我认为这也意味着我必须为每个新的客户端窗口创建一个新的“父”窗口,对吗?

标签: c linux events x11 intercept


【解决方案1】:

我不知道 X11,但我使用 "Intercept WM_DELETE_WINDOW X11" 作为关键字进行了 Google 搜索。找到 17k - MarkMailMplayer-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 */
        }
    }
}

请参阅XInternAtomXSetWMProtocolsXNextEvent

上面写完后,发现Handling window close in an X11 app

当用户点击关闭按钮时 [x] 在我们想要的 X11 应用程序上 弹出一个对话框询问“你 真的要辞职吗?”这是一个平原 X 应用程序。没有花哨的 GTK 或 QT 小部件 这里。那么如何抓住“window is 正在关闭”消息?

答案是告诉窗口 经理我们对这些感兴趣 通过调用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;
}

【讨论】:

  • 那太好了……只要它能解决我的问题。问题不在于我拥有一个窗口并且我想接收事件;我可以做得很好(并且会做上述方法之一)。问题是我的应用程序需要为其他窗口拦截它。不是我的窗户,不是我创造的。我仍在试图弄清楚第一个评论者强行重新设置窗口的含义,但我还没有真正让它发挥作用。我已经在谷歌上搜索了几天。这就是我来这里的原因。
  • 确实有。我仍然不太确定该怎么做才能使其正常工作。我已经设法让 windows reparented,但我一定错过了一些东西,因为我无法实现让事情正常运行的目标。 :-/ 实际上,我的办公桌上有 Xlib 参考资料,我一直在反复梳理以试图弄清楚这一点。 :-/
【解决方案2】:

很遗憾,这个问题的最佳答案是一系列非答案;从技术上讲,有一些方法可以实现,但它们都有缺点,这使得它们非常不切实际:

  1. 为应用程序创建一个 X11 代理,在应用程序和 X 服务器之间来回传递所有 X11 协议消息。然后代理将过滤掉任何有趣的消息。这样做的缺点是,对于一个很小的功能来说,这是一个非常大的开销,而且 X11 协议很复杂。也可能会产生意想不到的后果,这使得这成为一个更没有吸引力的选择。
  2. 作为标准应用程序启动,充当窗口管理器和“有趣”客户端应用程序之间的中介。这会破坏一些东西,例如 XDnD。实际上,它与第一个选项没有什么不同,只是代理处于窗口级别,而不是 X11 协议级别。
  3. 使用非便携式LD_PRELOAD 库技巧。这有几个缺点:
    1. 它不可跨动态链接器移植:并非所有动态链接器都支持LD_PRELOAD,即使在类 UNIX 系统中也是如此。
    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 支持,包括 the git master branch available on github

【讨论】:

  • 为什么不能编写 X 扩展?如果人们有这样的想法,那么一开始就没有人会编写任何扩展程序,而我们也没有。
【解决方案3】:

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

如果做不到这一点,我会看看其他类似软件可能如何工作(例如 wmdock,或者 GtkPlug/GtkSocket 是如何实现的),尽管我相信在这两种情况下,应用程序都需要明确的支持。

希望对你有帮助。

【讨论】:

  • 我曾经尝试过这个;但是,它非常非常不可靠。它是如此不可靠,以至于不适合生产程序。幸运的是,我确实找到了另一种方法来实现我心中的最终目标。
【解决方案4】:

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

【讨论】:

  • 谢谢。我已经阅读了 ICCCM 和更多关于 X11 的内容。如果没有很多的工作,重养父母是不可行的。旧 AllTray 做了重育诡计,在这个过程中破坏了很多东西;例如,XDnD。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 2013-02-02
相关资源
最近更新 更多