问题 在X11上拦截WM_DELETE_WINDOW?


我想拦截一下 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。

UPDATE:我还在寻找答案。我现在采取的路线是尝试重新调整窗口并自行管理,但我无法使其工作。重新定位后,我似乎无法以任何方式取回它。我可能会遗漏一些非常基本的东西,但我无法弄清楚如何让它再次出现在我自己的窗口,将它带回屏幕。

更新2:好吧,所以我打了另一个砖墙。 X服务器文档说在窗口的事件掩码上设置StructureNotifyMask以接收MapNotify和ReparentNotify事件。我有兴趣接收任何一个。我目前的想法是创建一个窗口,作为事件接收器,然后当我获得有趣事件的事件时,通过创建和重新创建来对其进行操作。但是,这根本不起作用。我实际收到的唯一事件是PropertyNotify事件。所以,这条路线似乎也没有做得很好。


3230
2017-07-21 04:53


起源

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


答案:


我不知道X11,但我用谷歌搜索 “截距 WM_DELETE_WINDOW X11" 作为关键字。发现 17k - MarkMail 和 Mplayer-commit 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 */
        }
    }
}

看到 XInternAtomXSetWMProtocols 和 XNextEvent例行

在我写完之后,我找到了 处理窗口关闭在X11应用程序中

当用户单击关闭按钮时    [x] 在我们想要的X11应用程序上   弹出一个对话框,询问“你呢?   真的想退出吗?“这很简单   X app。没有花哨的GTK或QT小部件   这里。那么如何抓住“窗口是   正在关闭“消息?

答案是告诉窗口   经理我们对这些感兴趣   通过电话事件 XSetWMProtocols 和   注册一个 WM_DELETE_WINDOW 信息   用它。然后我们会得到一个客户   来自Window Manager的消息if   有人试图关闭窗户,并且   它不会关闭它,它会离开我们   由我们决定。这是一个例子......

// 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;
}

12
2017-07-27 05:26



那将是伟大的...如果它只解决了我的问题。问题不在于我拥有一个窗口而且我想收到这个活动;我可以做得很好(并将采取上述方式之一)。问题是我的应用程序需要拦截其他窗口。 Windows不是我的,我没有创建。我还在试图弄清楚第一个评论者强行重新命名窗口的意思,但我还没有真正开始工作。我一直在谷歌搜索几天。这就是我来到这里的原因。 - Michael Trausch
我假设你已经读过了 tronche.com/gui/x/xlib/window-and-session-manager/... 已经。 - Eugene Yokota
的确,我有。我还不太清楚要做些什么来使它正常工作;我已经设法让窗户重新制作,但我必须遗漏一些东西,因为我无法达到让事情发挥作用的目标。 : - /我实际上在我的桌子上有Xlib参考,我一直在反复梳理以试图解决这个问题。 : - / - Michael Trausch


不幸的是,这个问题的最佳答案是一系列非答案;有技术上的方法来实现它,但它们都有垮台,使它们非常不切实际:

  1. 为应用程序创建X11代理,在应用程序和X服务器之间来回传递所有X11协议消息。然后代理将过滤掉任何有趣的消息。这样做的缺点是,对于一个小小的功能来说,这是一个非常大的开销,而X11协议很复杂。也可能会出现意想不到的后果,这使得这个选项更具吸引力。
  2. 作为标准应用程序启动,充当窗口管理器和“有趣”客户端应用程序之间的中介。这打破了一些东西,比如XDnD。实际上,它与第一个选项没有什么不同,除了代理处于Window级别而不是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. 为窗口管理器实现扩展或插件。窗口管理器的窗口管理器数量与窗口管理器一样多,因此这是完全不可行的。

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


1
2017-07-29 02:42





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

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

希望更有帮助。


0
2017-07-30 21:26



我曾经在一个点上做过这个实验;然而,它非常 非常 不可靠的。它不太可靠,不适合生产计划。幸运的是,我找到了另一种方法来实现我想到的最终目标。 - Michael Trausch


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


0
2018-06-25 12:53



谢谢。我已经阅读了ICCCM以及更多关于X11的内容。没有a,重新定位不是一种选择 批量 工作的。老AllTray做了重新伎俩,在这个过程中打破了很多东西;例如,XDnD。 - Michael Trausch