在 X11 上拦截 WM_DELETE_WINDOW?

发布于 2024-07-27 23:56:51 字数 1387 浏览 8 评论 0原文

我想拦截发布到我正在编写的应用程序(AllTray),这样我就可以对其进行操作,而不是接收它的应用程序。 我目前正在考虑在 GDK 级别尝试此操作 如果可能的话,通过gdk_display_add_client_message_filter,但如果也有一个Xlib解决方案,我会很高兴; 它似乎是可能的,但我似乎不明白我该如何成功地做到这一点。

目前,我有两个程序(用 C 编写),我试图用它们来解决这个问题,第一个 除了创建一个窗口并注册它知道 WM_DELETE_WINDOW第二个 尝试捕获该消息,但似乎失败了; 它似乎什么也没做。 我对文档的理解是否错误,或者我是否需要做一些额外的事情(或者我是否需要完全避免使用 GDK)?

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

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

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

I'd like to intercept the WM_DELETE_WINDOW message that is posted to a certain selection of windows that an application I'm writing (AllTray), so that I can act on it instead of the application receiving it. I'm currently looking at trying this at the GDK level via gdk_display_add_client_message_filter if possible, but I'd be happy with an Xlib solution if there is one as well; it seems to be possible, but I just don't seem to be understanding how I am to do it successfully.

Currently, I have two programs (written in C) that I am trying to use to get this figured out, the first one does nothing but create a window and register that it knows about WM_DELETE_WINDOW, and the second one attempts to catch that message, but seems to fail in doing so; it appears to do precisely nothing. Am I understanding the documentation wrong on this, or is there something additional that I need to be doing (or do I need to avoid using GDK entirely for this)?

The background is this: Prior to my re-write of AllTray, the way it would do things appears to be to try to intercept a mouse-click on the X button itself. For some window managers, this worked properly, for others it didn't work at all, and for others, the user had to configure it manually and instruct AllTray where the button for closing the window was. What I am looking for is a solution that doesn't involve a LD_LIBRARY_PRELOAD and will work for any window manager/application combination that conforms to the current standards and sends a WM_DELETE_WINDOW ClientMessage when the window is closed.

UPDATE: I'm still looking for an answer. The route that I am taking at the moment is to try to reparent the window and manage it myself, but I just cannot make it work. Upon reparenting, I don't seem to be able to get it back in any way. I may be missing something very fundamental, but I can't figure out how to actually make it appear it my own window again, to bring it back on the screen.

UPDATE 2: Alright, so I've hit another brick wall. The X server documentation says to set the StructureNotifyMask on the window's event mask to receive both MapNotify and ReparentNotify events. I'd be interested in receiving either. My current thinking was to create a window that served just as an event receiver, and then when I get events for interesting things, act on them by creating and reparenting. However, this simply doesn't seem to be working. The only events I actually receive are PropertyNotify events. So, this route doesn't seem to be doing very much good, either.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

皓月长歌 2024-08-03 23:56:51

我不知道 X11,但我使用 "拦截 WM_DELETE_WINDOW X11" 作为关键字。 找到 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 */
        }
    }
}

请参阅 XInternAtom, XSetWMProtocolsXNextEvent

写完上面的内容后,我发现在X11应用程序中处理窗口关闭

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

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

I don't know X11, but I googled using "Intercept WM_DELETE_WINDOW X11" as keywords. Found 17k - MarkMail and Mplayer-commits r154 - trunk/libvo. In both cases they are doing the same thing.

 /* This is used to intercept window closing requests.  */
 static Atom wm_delete_window;

within static void x11_init(),

XMapWindow(display, win);
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, win, &wm_delete_window, 1);

then, within 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 */
        }
    }
}

See XInternAtom, XSetWMProtocols and XNextEvent.

After I wrote the above, I found Handling window close in an X11 app:

When a user clicks the close button
[x] on our X11 application we want it
to pop a a dialog asking “do you
really want to quit?”. This is a plain
X app. No fancy GTK or QT widgets
here. So how to catch the “window is
being closed” message?

The answer is to tell the Window
Manager we are interested in these
event by calling XSetWMProtocols and
registering a WM_DELETE_WINDOW message
with it. Then we’ll get a client
message from the Window Manager if
someone tries to close the window, and
it won’t close it, it’ll leave that us
up to us. Here’s an example….

// 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;
}
想你只要分分秒秒 2024-08-03 23:56:51

不幸的是,这个问题的最佳答案是一系列非答案; 从技术上讲,有多种方法可以实现这一目标,但它们都有一些缺陷,使得它们极其不切实际:

  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 操作系统上,如果要与 setuid/setgid 程序一起使用,LD_PRELOAD 库必须为 setuid/setgid。 当然,这是一个潜在的安全漏洞。
    7. 我确信还有更多我想不到的缺点。
  4. 实现 X Window 系统的扩展。 在 X11 实现中不可移植,复杂且令人费解,而且绝对不可能。
  5. 为窗口管理器实施扩展或插件。 有多少个关于窗口管理器的意见就有多少个窗口管理器,因此这是完全不可行的。

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

Unfortunately, the best answer to this question is a series of non-answers; there are technically ways to accomplish it, but they all have downfalls that make them extremely impractical:

  1. Create an X11 proxy for an application, passing all X11 protocol messages back and forth between the application and the X server. The proxy would then filter out any interesting messages. The downside to this is that this is an awful lot of overhead for a single little tiny feature, and the X11 protocol is complex. There could also be unintended consequences, which makes this an even more unattractive option.
  2. Launch as a standard application that acts as an intermediary between the window manager and “interesting” client applications. This breaks some things, such as XDnD. In effect, it is not unlike the first option, except that the proxy is at the Window level as opposed to the X11 protocol level.
  3. Use the non-portable LD_PRELOAD library trick. This has several downsides:
    1. It is non-portable across dynamic linkers: not all dynamic linkers support LD_PRELOAD, even among UNIX-like systems.
    2. It is non-portable across operating systems: not all operating systems support featureful dynamic linkers.
    3. It breaks network-transparency: the shared object/dynamic link library must reside on the host as the child process that is being executed.
    4. Not all X11 applications use Xlib; it would be necessary to write one LD_PRELOAD module for each of the libraries that an application might use to talk with X11.
    5. In addition to the last point, not all applications would be susceptible to LD_PRELOAD even if they ran under a linker that supported it, because they may not use a shared object or DLL in order to communicate with X; consider, for example, a Java application which uses an X11 protocol library written in Java itself.
    6. On some UNIX-like operating systems, LD_PRELOAD libraries must be setuid/setgid if they are to be used with setuid/setgid programs. This is, of course, a potential security vulnerability.
    7. I am quite sure that are more downsides that I cannot think of.
  4. Implement an extension to the X Window system. Non-portable among X11 implementations, complex and convoluted as all get out, and absolutely out of the question.
  5. Implement extensions or plug-ins to window managers. There are as many window managers as there are opinions on window managers, and therefore this is utterly infeasible.

Ultimately, I was able to finally accomplish my goal by using a completely separate mechanism; anyone who is interested, please see the Close-to-Tray support in AllTray 0.7.5.1dev and later, including the git master branch available on github.

探春 2024-08-03 23:56:51

好的,为了详细说明我之前的建议,您可能需要研究 XEmbed< /a>. 至少,这可能会给您一些尝试的想法。

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

希望这更有帮助。

Ok, to elaborate on my earlier suggestion, you might want to investigate XEmbed. At the least, that might give you some ideas to try.

Failing that, I'd have a look at how other similar software might be working (e.g. wmdock, or how GtkPlug/GtkSocket is implemented), though I believe in both those cases explicit support is required in the applications.

Hope that is more helpful.

水溶 2024-08-03 23:56:51

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

You should read ICCCM that tells you how window manager communicates with client. Most of WM will create a frame window to contain your top-level window via reparenting. Thus, if your reparent may break the relationship known by WM and your client window.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文