I use JNA to manage application windows on Linux, sending Xlib messages, but cannot move the window.
My initial implementation ran wmctrl in a shell to move windows and successfully move windows. Unfortunately, a noticeable amount of overhead is associated with invoking shell programs with Java, so now I'm trying to make direct API calls using JNA. I am using the X11 example, available on the JNA website, and I can successfully complete several tricks, such as listing window IDs and reading window properties, so I know that JNA + Xlib works, at least in part.
At first, I tried to move windows directly using XMoveWindow(), but the window manager apparently blocked these calls.
I came across a thread that suggested I send a client message using XSendMessage(), so I did it below, but apparently XSendMessage()fails because the window does not move, and I get the return value of 0. I suppose I omitted something obvious, but I cannot understand it. Any suggestions?
Note that for the purposes of this example, the main method has a hard-coded window identifier. This is the identifier of the window of the window I'm trying to move (obtained using wmctrl -lon 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;
private static final int TRUE = 1;
public static void main(String[] args) {
setWindowPos(new Window(0x01300007), 100, 100, 600, 400);
}
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";
long grflags = 0l;
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);
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);
if (status == 0) {
logger.error("setWindowPos: XSendEvent failed (" + msg + ")");
return false;
}
return true;
}