我想拦截一下 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事件。所以,这条路线似乎也没有做得很好。
我不知道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 */
}
}
}
看到 XInternAtom, XSetWMProtocols 和 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;
}
不幸的是,这个问题的最佳答案是一系列非答案;有技术上的方法来实现它,但它们都有垮台,使它们非常不切实际:
- 为应用程序创建X11代理,在应用程序和X服务器之间来回传递所有X11协议消息。然后代理将过滤掉任何有趣的消息。这样做的缺点是,对于一个小小的功能来说,这是一个非常大的开销,而X11协议很复杂。也可能会出现意想不到的后果,这使得这个选项更具吸引力。
- 作为标准应用程序启动,充当窗口管理器和“有趣”客户端应用程序之间的中介。这打破了一些东西,比如XDnD。实际上,它与第一个选项没有什么不同,除了代理处于Window级别而不是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实现中不可移植,复杂且复杂,因为所有人都离开了,绝对不可能。
- 为窗口管理器实现扩展或插件。窗口管理器的窗口管理器数量与窗口管理器一样多,因此这是完全不可行的。
最终,我能够通过使用完全独立的机制最终实现我的目标;任何有兴趣的人,请参阅AllTray 0.7.5.1dev及更高版本中的Close-to-Tray支持,包括 github上提供的git master分支。
好的,要详细说明我之前的建议,你可能想调查一下 XEMBED。至少,这可能会给你一些尝试的想法。
如果不这样做,我就会看看其他类似的软件是如何工作的(例如wmdock,或GtkPlug / GtkSocket是如何实现的),尽管我相信这两种情况都需要在应用程序中提供明确的支持。
希望更有帮助。
您应该阅读ICCCM,告诉您窗口管理器如何与客户端通信。大多数WM将创建一个框架窗口,通过重新显示来包含您的顶级窗口。因此,如果您的重新表达可能会破坏WM和您的客户端窗口已知的关系。