我想拦截发布到我正在编写的应用程序(AllTray)的某些窗口选择的WM_DELETE_WINDOW
消息,以便我可以对其执行操作而不是应用程序接收它。我正在考虑尽可能在GDK级via gdk_display_add_client_message_filter
尝试这个,但如果有一个Xlib解决方案,我会很满意;它似乎是可能的,但我似乎并不理解我是如何成功地做到的。
目前,我有两个程序(用C编写),我试图用它来解决这个问题,the first one除了创建一个窗口并注册它知道WM_DELETE_WINDOW
之外什么都不做, the second one试图抓住这条信息,但似乎没有这样做;它似乎没有做任何事情。我是否理解文档错误,或者我需要做些什么(或者我是否需要完全避免使用GDK)?
背景是这样的:在我重新编写AllTray之前,它的工作方式似乎是试图拦截鼠标点击X按钮本身。对于某些窗口管理器,这种方法正常工作,对于其他窗口管理器根本不起作用,而对于其他窗口管理器,用户必须手动配置它并指示AllTray关闭窗口的按钮所在的位置。我正在寻找的是一个不涉及LD_LIBRARY_PRELOAD
的解决方案,它将适用于符合当前标准的任何窗口管理器/应用程序组合,并在窗口关闭时发送WM_DELETE_WINDOW
ClientMessage。
更新:我还在寻找答案。我现在采取的路线是尝试重新调整窗口并自行管理,但我无法使其工作。重新定位后,我似乎无法以任何方式取回它。我可能会遗漏一些非常基本的东西,但我无法弄清楚如何让它再次出现在我自己的窗口,将它带回屏幕。
更新2 :好的,所以我打了另一个砖墙。 X服务器文档说在窗口的事件掩码上设置StructureNotifyMask以接收MapNotify和ReparentNotify事件。我有兴趣接收任何一个。我目前的想法是创建一个窗口,作为事件接收器,然后当我获得有趣事件的事件时,通过创建和重新创建来对它们进行操作。但是,这根本不起作用。我实际收到的唯一事件是PropertyNotify事件。所以,这条路线似乎也没有做得很好。
答案 0 :(得分:12)
我不知道X11,但我使用"Intercept WM_DELETE_WINDOW
X11"作为关键字进行了Google搜索。找到17k - MarkMail和Mplayer-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 */
}
}
}
请参阅XInternAtom,XSetWMProtocols和XNextEvent。
在我写完上述内容后,我找到了Handling window close in an X11 app:
当用户点击关闭按钮时 我们希望在我们的X11应用程序上
[x]
弹出一个对话框,询问“你呢? 真的想退出吗?“这很简单 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;
}
答案 1 :(得分:1)
不幸的是,这个问题的最佳答案是一系列非答案;技术上有办法实现它,但它们都有垮台,使它们变得非常不切实际:
LD_PRELOAD
库技巧。这有几个缺点:
LD_PRELOAD
,即使在类UNIX系统中也是如此。LD_PRELOAD
模块。LD_PRELOAD
的影响,即使它们在支持它的链接器下运行,因为它们可能不使用共享对象或DLL来与X通信;例如,考虑使用用Java编写的X11协议库的Java应用程序。LD_PRELOAD
库必须是setuid / setgid才能与setuid / setgid程序一起使用。当然,这是一个潜在的安全漏洞。最终,我能够通过使用完全独立的机制最终实现我的目标;任何有兴趣的人,请参阅AllTray 0.7.5.1dev及更高版本中的Close-to-Tray支持,包括the git master branch available on github。
答案 2 :(得分:0)
好的,要详细说明我之前的建议,您可能需要调查XEmbed。至少,这可能会给你一些尝试的想法。
如果做不到这一点,我会看看其他类似的软件是如何工作的(例如wmdock,或GtkPlug / GtkSocket是如何实现的),尽管我相信这两种情况都需要在应用程序中提供显式支持。
希望更有帮助。
答案 3 :(得分:0)
您应该阅读ICCCM,告诉您窗口管理器如何与客户端通信。大多数WM将创建一个框架窗口,通过重新显示来包含您的顶级窗口。因此,如果您的重新表达可能会破坏WM和您的客户窗口所知的关系。