通过 JNA 使用 Xlib 移动窗口

发布于 2024-08-23 15:16:54 字数 3047 浏览 8 评论 0原文

我正在使用 JNA 通过发送 Xlib 消息来操作 Linux 上的应用程序窗口,但似乎无法移动窗口。

我最初的实现在 shell 上执行 wmctrl 来移动窗口,并成功移动了窗口。不幸的是,从 Java 调用 shell 程序会产生大量开销,因此现在我尝试使用 JNA 进行直接 API 调用。我正在使用 JNA 网站上提供的 X11 示例,并且可以成功执行一些技巧,例如枚举窗口 ID 和读取窗口属性,因此我知道 JNA+Xlib 至少部分有效。

首先,我尝试使用 XMoveWindow() 直接移动窗口,但窗口管理器显然阻止了这些调用。

我遇到一个线程,建议我需要使用 XSendMessage() 发送客户端消息,因此我在下面完成了该操作,但显然 XSendMessage() 失败了,因为窗口没有移动,我得到的返回值为 0。我猜我遗漏了一些明显的东西,但无法完全弄清楚。有什么建议吗?

请注意,就本示例而言,main 方法具有硬编码的窗口 ID。这是我尝试移动的窗口的窗口 ID(在控制台上使用 wmctrl -l 获得)。

import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.examples.unix.X11;
import com.sun.jna.examples.unix.X11.Atom;
import com.sun.jna.examples.unix.X11.AtomByReference;
import com.sun.jna.examples.unix.X11.Display;
import com.sun.jna.examples.unix.X11.Window;
import com.sun.jna.examples.unix.X11.WindowByReference;
import com.sun.jna.examples.unix.X11.XEvent;
import com.sun.jna.examples.unix.X11.XTextProperty;
import com.sun.jna.examples.unix.X11.XWindowAttributes;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;
import com.sun.jna.ptr.PointerByReference;

private static final int FALSE = 0; /** C-style boolean "false" */
private static final int TRUE  = 1; /** C-style boolean "true" */

public static void main(String[] args) {
    setWindowPos(new Window(0x01300007), 100, 100, 600, 400); // update the Window constructor with the appropriate ID given by wmctrl -l
}


public static boolean setWindowPos(Window window, int x, int y, int w, int h) {
    final X11 x11 = X11.INSTANCE;
    Display display = x11.XOpenDisplay(null);

    NativeLong mask = new NativeLong(X11.SubstructureRedirectMask | X11.SubstructureNotifyMask | X11.ResizeRedirectMask);

    XEvent event = new XEvent();

    String msg = "_NET_MOVERESIZE_WINDOW"; //$NON-NLS-1$

    long grflags = 0l; // use the default gravity of the window
    if (x != -1) grflags |= (1 << 8);
    if (y != -1) grflags |= (1 << 9);
    if (w != -1) grflags |= (1 << 10);
    if (h != -1) grflags |= (1 << 11);

    event.xclient.type = X11.ClientMessage;
    event.xclient.serial = new NativeLong(0l);
    event.xclient.send_event = TRUE;
    event.xclient.message_type = x11.XInternAtom(display, msg, false);
    event.xclient.window = window;
    event.xclient.format = 32;
    event.xclient.data.l[0] = new NativeLong(grflags); // gravity flags
    event.xclient.data.l[1] = new NativeLong(x);
    event.xclient.data.l[2] = new NativeLong(y);
    event.xclient.data.l[3] = new NativeLong(w);
    event.xclient.data.l[4] = new NativeLong(h);

    int status = x11.XSendEvent(display, x11.XDefaultRootWindow(display), FALSE, mask, event);
    x11.XFlush(display); // need to XFlush if we're not reading X events

    if (status == 0) { // 0 indicates XSendEvent failed
        logger.error("setWindowPos: XSendEvent failed (" + msg + ")"); //$NON-NLS-1$
        return false;
    }

    return true;
}

I'm using JNA to manipulate application windows on Linux by sending Xlib messages but can't seem to move a window.

My original implementation executed wmctrl on the shell to move the windows and that successfully moved the windows. Unfortunately, there's a noticeable amount of overhead associated with calling shell programs from Java, so now I'm trying to make direct API calls using JNA. I'm using the X11 example available from the JNA website and can successfully do a few tricks, such as enumerating the window IDs and reading window properties, so I know JNA+Xlib is at least partially working.

First I tried moving the windows directly using XMoveWindow() but the window manager was apparently blocking those calls.

I ran across a thread that suggested I needed to send a client message using XSendMessage(), so I've done that below, but apparently XSendMessage() is failing because the window doesn't move and I get a return value of 0. I'm guessing I omitted something obvious, but can't quite figure it out. Any suggestions?

Note that, for the purposes of this example, the main method has a window ID hard-coded. This is the window ID of the window I'm trying to move (obtained using wmctrl -l on the console).

import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.examples.unix.X11;
import com.sun.jna.examples.unix.X11.Atom;
import com.sun.jna.examples.unix.X11.AtomByReference;
import com.sun.jna.examples.unix.X11.Display;
import com.sun.jna.examples.unix.X11.Window;
import com.sun.jna.examples.unix.X11.WindowByReference;
import com.sun.jna.examples.unix.X11.XEvent;
import com.sun.jna.examples.unix.X11.XTextProperty;
import com.sun.jna.examples.unix.X11.XWindowAttributes;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;
import com.sun.jna.ptr.PointerByReference;

private static final int FALSE = 0; /** C-style boolean "false" */
private static final int TRUE  = 1; /** C-style boolean "true" */

public static void main(String[] args) {
    setWindowPos(new Window(0x01300007), 100, 100, 600, 400); // update the Window constructor with the appropriate ID given by wmctrl -l
}


public static boolean setWindowPos(Window window, int x, int y, int w, int h) {
    final X11 x11 = X11.INSTANCE;
    Display display = x11.XOpenDisplay(null);

    NativeLong mask = new NativeLong(X11.SubstructureRedirectMask | X11.SubstructureNotifyMask | X11.ResizeRedirectMask);

    XEvent event = new XEvent();

    String msg = "_NET_MOVERESIZE_WINDOW"; //$NON-NLS-1$

    long grflags = 0l; // use the default gravity of the window
    if (x != -1) grflags |= (1 << 8);
    if (y != -1) grflags |= (1 << 9);
    if (w != -1) grflags |= (1 << 10);
    if (h != -1) grflags |= (1 << 11);

    event.xclient.type = X11.ClientMessage;
    event.xclient.serial = new NativeLong(0l);
    event.xclient.send_event = TRUE;
    event.xclient.message_type = x11.XInternAtom(display, msg, false);
    event.xclient.window = window;
    event.xclient.format = 32;
    event.xclient.data.l[0] = new NativeLong(grflags); // gravity flags
    event.xclient.data.l[1] = new NativeLong(x);
    event.xclient.data.l[2] = new NativeLong(y);
    event.xclient.data.l[3] = new NativeLong(w);
    event.xclient.data.l[4] = new NativeLong(h);

    int status = x11.XSendEvent(display, x11.XDefaultRootWindow(display), FALSE, mask, event);
    x11.XFlush(display); // need to XFlush if we're not reading X events

    if (status == 0) { // 0 indicates XSendEvent failed
        logger.error("setWindowPos: XSendEvent failed (" + msg + ")"); //$NON-NLS-1$
        return false;
    }

    return true;
}

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

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

发布评论

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

评论(2

淡墨 2024-08-30 15:16:54

这可能是一个有点晚的答案,但无论如何......

当您尝试移动窗口时,会发生什么情况:窗口(称为“客户端”)向窗口管理器发送 XConfigureRequest。发生这种情况是因为窗口管理器告诉 X 服务器他是老板(通过在客户端的父级上设置子结构覆盖标志)。

绕过此问题的唯一方法是在客户端上设置覆盖重定向标志,执行移动,然后禁用覆盖重定向标志(以便一切恢复到“正常”)。

GL&高频。

This might be a bit of a late answers but anyway...

What happens when you try to move a window is that the window (called "the client") sends an XConfigureRequest to the window manager. This happens because the window manager tells the X server that he is boss (by setting the substructure override flag on the client's parent).

The only way to bypass this is to set the override redirect flag on your client, do the move, and disable the override redirect flag (so that everything goes back to 'normal').

gl & hf.

攒一口袋星星 2024-08-30 15:16:54

您看过 XConfigureWindow 吗?

我还没有真正测试过这个,因为我今晚刚刚实现了它并且我正在 Windows 上开发,但它值得一试......

public static interface X11Ext extends Library
    {
        public static X11Ext INSTANCE = (X11Ext)Native.loadLibrary("X11", X11Ext.class);

        public int XConfigureWindow(X11.Display display, X11.Window window, int value_mask, XWindowChanges changes);

        /**
         * Use value_mask flags:
         * CWX
         * CWY
         * CWWidth
         * CWHeight
         * CWBorderWidth
         * CWSibling
         * CWStackMode
         */
        public class XWindowChanges extends Structure
        {
            public int x;
            public int y;
            public int width;
            public int height;
            public int border_width;
            public X11.Window sibling;
            public int stack_mode;
        }
    }

Have you looked at XConfigureWindow?

I haven't actually tested this out yet since I just implemented it tonight and I'm developing on Windows, but it's worth a try....

public static interface X11Ext extends Library
    {
        public static X11Ext INSTANCE = (X11Ext)Native.loadLibrary("X11", X11Ext.class);

        public int XConfigureWindow(X11.Display display, X11.Window window, int value_mask, XWindowChanges changes);

        /**
         * Use value_mask flags:
         * CWX
         * CWY
         * CWWidth
         * CWHeight
         * CWBorderWidth
         * CWSibling
         * CWStackMode
         */
        public class XWindowChanges extends Structure
        {
            public int x;
            public int y;
            public int width;
            public int height;
            public int border_width;
            public X11.Window sibling;
            public int stack_mode;
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文