1

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;
}
rob
  • 6,147
  • 2
  • 37
  • 56
  • 1
    Perhaps the window manager is telling you something. Moving the user's windows around is kind of evil, and may require special privileges. – Stephen C Mar 06 '10 at 02:23
  • 1
    Thanks for the suggestion, but I don't think it's considered evil. There are other programs, such as wmctrl, that can do this on the command-line without any special privileges. My original implementation actually called wmctrl from Java; but there is significant overhead associated with calling shell programs, so I'm trying to implement the same functionality in JNA. I've compared my code with several examples but it doesn't work. I was hoping a fresh set of eyes might spot a dumb mistake on my part. ;) – rob Mar 08 '10 at 17:11

2 Answers2

3

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.

Zubzub
  • 782
  • 7
  • 18
  • I haven't gotten a chance to try this yet, but the override redirect flag sounds promising. I suppose I'll accept this one for now and if I get around to trying it eventually I'll post my results. – rob Nov 21 '11 at 23:47
1

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;
        }
    }
David
  • 11
  • 1
  • Thanks for the answer. No, I haven't tried XConfigureWindow because I figured it wouldn't be any more likely to work than XMoveWindow, but I'll go ahead and try it when I get a chance. – rob Mar 15 '10 at 19:58