2

Basically my goal is to get all the windows (I'm talking every single one, not the ones attached to my program) and compile them into a list. I'm trying to make my application as cross-platform-friendly as possible, so I'm porting JNA for Windows and Linux. Working with JNA in Windows was really simple and easy. However, the lack of documentation and examples with JNA utilizing the XLib isn't making the coding process any easier.

This question, regarding to coding with XLib in C++, does provide some light on how to get the windows in the X11 environment using XLib. I'm basically needing an implementation of XQueryTree in Java. My lack of knowledge prevents me from easily being able to use this code as I'm coding in Java. I've never coded in C++ or ever tried to code using XLib. Any ideas?

Community
  • 1
  • 1
Kevin Thorne
  • 435
  • 5
  • 19
  • Why? Just traverse the Swing window tree in Java. No JNA or X11 required, and it's cross-platform. – user207421 Jul 22 '14 at 23:52
  • Does Swing iterate through ALL windows of the X11 environment? I'm not just talking my application base here. I'll edit to clarify – Kevin Thorne Jul 23 '14 at 00:41

2 Answers2

2

XQueryTree is defined in platform.jar.

int XQueryTree(Display display, Window window, WindowByReference root, WindowByReference parent, PointerByReference children, IntByReference childCount);

Obtain the display with XOpenDisplay().

Since you want all windows, you should use the root window as the parent (use XRootWindow() to look it up).

After the call, children.getValue() will have a pointer to a block of window IDs; at root, these are of type native long, so use Pointer.getIntArray(0, size) or Pointer.getLongArray(0, size) based on Native.LONG_SIZE, where size is the value returned in childCount. You can then use that array of IDs to initialize Window objects which may be passed on to other X11 functions.

You'll need to manually free the returned array memory using XFree when you're done with it.

technomage
  • 9,861
  • 2
  • 26
  • 40
  • Is there a way to get a Window object from the IDs in XLib? – Kevin Thorne Jul 23 '14 at 06:23
  • So, after studying Xlib a little further, do most Xlib functions almost "update" variables you put into them (the multiple returns)? Right now I'm creating null variables to stick into them and my hope is those methods will then change the variables to appropriate values. – Kevin Thorne Jul 23 '14 at 17:15
  • It's not Xlib particularly, it's a C convention. However, Xlib does it a little more often since a lot of its methods return an integer status as the primary return value, which means you have to pass in pointers to get additional values back. – technomage Jul 23 '14 at 20:12
  • I'm still having issues getting Window objects from the ID. I use the simple `Window childWindow = new X11.Window(children.getValue().getIntArray(0, nNumChildren.getValue())[i])` and `childWindow` is returning null. However, I know the list is being populated with IDs. – Kevin Thorne Jul 25 '14 at 21:05
  • Re-examine your code. A constructor (`new X11.Window()`) cannot return `null`. – technomage Jul 28 '14 at 20:43
  • How can you make sure that you also caputure windows that gets mapped in the meanwile? I saw window managers calling `XGrabServer` to temporarily disable the Xserver event processing, but there is no equivalent method binding in JNA for this. – Moonlit Nov 04 '16 at 12:50
  • Easy enough to add the binding, just extend the existing interface and instantiate it like is done for the base binding. – technomage Nov 07 '16 at 16:27
1

I found I needed to recurse down. In Ubuntu 16.04 many Windows were not directly rooted from the top level, instead they appear several layers down... For example

null
  null
    xeyes
null
  null
    user@user-virtual-machine: ~
null
  null
    workspace-test - Java - JnaTest/src/FindWindows.java - Eclipse 
  • Also sometimes childrenRef returned null

Code

public static void main(String[] args) {
    X11 x11 = X11.INSTANCE;
    Display display = x11.XOpenDisplay(null);

    Window root = x11.XDefaultRootWindow(display);
    recurse(x11, display, root, 0);
}

private static void recurse(X11 x11, Display display, Window root, int depth) {
    X11.WindowByReference windowRef = new X11.WindowByReference();
    X11.WindowByReference parentRef = new X11.WindowByReference();
    PointerByReference childrenRef = new PointerByReference();
    IntByReference childCountRef = new IntByReference();

    x11.XQueryTree(display, root, windowRef, parentRef, childrenRef, childCountRef);
    if (childrenRef.getValue() == null) {
        return;
    }

    long[] ids;

    if (Native.LONG_SIZE == Long.BYTES) {
        ids = childrenRef.getValue().getLongArray(0, childCountRef.getValue());
    } else if (Native.LONG_SIZE == Integer.BYTES) {
        int[] intIds = childrenRef.getValue().getIntArray(0, childCountRef.getValue());
        ids = new long[intIds.length];
        for (int i = 0; i < intIds.length; i++) {
            ids[i] = intIds[i];
        }
    } else {
        throw new IllegalStateException("Unexpected size for Native.LONG_SIZE" + Native.LONG_SIZE);
    }

    for (long id : ids) {
        if (id == 0) {
            continue;
        }
        Window window = new Window(id);
        X11.XTextProperty name = new X11.XTextProperty();
        x11.XGetWMName(display, window, name);

        System.out.println(String.join("", Collections.nCopies(depth, "  ")) + name.value);
        x11.XFree(name.getPointer());

        recurse(x11, display, window, depth + 1);
    }
}
Adam
  • 35,919
  • 9
  • 100
  • 137