我正在使用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;
}
答案 0 :(得分:3)
这可能是一个迟到的答案,但无论如何......
当您尝试移动窗口时会发生的情况是窗口(称为“客户端”)将XConfigureRequest发送到窗口管理器。这是因为窗口管理器告诉X服务器他是老板(通过在客户端的父节点上设置子结构覆盖标志)。
绕过这个的唯一方法是在客户端上设置覆盖重定向标志,执行移动,并禁用覆盖重定向标志(以便一切都恢复到“正常”)。
&p> gl&amp;的HF。答案 1 :(得分:1)
你看过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;
}
}