Xlib: Adding a window to the save-set using XAddToChangeSet does not work from Java/JNI
Asked Answered
T

1

6

I am getting stuck and don't know where to look further.

I have a Java application and one of its functionality is to grab some specific windows (ie third party application windows) and host them within itself (with some extra frames, etc...) The whole thing works great, except when my Java application gets killed (or crashes). When this happens, the third party windows that were hosted are getting destroyed, most of the time crashing the third party application altogether.

This feature is designed to work under Linux.

The way the reparenting is done is a bit convoluted:

  • A dedicated Java thread is created and calls a C shared library (a .so lib).
  • This shared lib scans all the current windows and identifies the ones that need to be hosted
  • when such a window is detected, it is reparented to the main Java application window using XReparentWindow
  • the shared library enters an infinite loop (controled by an exit flag) and waits for the XEvent (using XNextEvent) in case a window that needs to be hosted gets displayed.

I have written a dummy C program that uses most of the library code (without the JNI bit) to debug this issue using xclock as my hosted test application. I can reproduce the exact same behaviour: when killing the dummy program, the Xclock window disappears and soon after xclock crashes.

Then I modify the shared library routines to use the XAddToSaveSet function. This works a treat with my sample program: the Xclock window gets properly restored and reparented to the ROOT window. Now doing the same thing with the Java application does not work: the hosted windows are not getting reparented to the ROOT window.

I have monitored what is going on using gdb, xev and also xmon. The XAddToChangeSet is properly called, I can see the ChangeSet request going through xmon (and the window ID is correct), but when the Java application crashes the event DestroyWindow is sent to the hosted window, when I was expecting to see a RemapWindow event (like when using the sample C program).

Has anyone played with this kind of thing from the Java world?

The Java app is pretty big: multi-threaded and using OpenGL (maybe it matters). The behaviour is identical on RHEL4 and RHEL6. There is no window manager running, just a bare X Server (XOrg X11 1.10.2).

Any other way to debug this? Can we observe the save-set somehow? Should the XServer send a reply to the ChangeSet request?

My next step is to write a sample Java application and use the JNI interface the same way the main application is doing, but hopefully there is something obvious I missed.

Thanks!

[EDIT]

I finally tested with a dummy Java application: one JPanel and the reparenting of XClock onto the panel. When the Java process is being killed, the XClock window is destroyed.

Below an extract of the XMON logs with the sample C code (using Xlib):

VisibilityNotify event, serial 13, synthetic NO, window 0x20000a,
    state VisibilityPartiallyObscured

UnmapNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, from_configure NO

ReparentNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, parent 0x600001,
    (0,0), override NO

MapNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, override NO

VisibilityNotify event, serial 13, synthetic NO, window 0x20000a,
    state VisibilityPartiallyObscured
.
.
.
UnmapNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, from_configure NO

ReparentNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, parent 0x282,
    (11,11), override NO

MapNotify event, serial 13, synthetic NO, window 0x20000a,
    event 0x20000a, window 0x20000a, override NO

VisibilityNotify event, serial 13, synthetic NO, window 0x20000a,
    state VisibilityUnobscured

With the Java code, same initialization sequence occurs, but when killing the process no ReparentNotifyEvent happens.

Following Andrey advice, I implemented the Xfixes extension XFixesChangeSaveSet. The Version 1 seems to exactly fix this issue (or close enough):

  • Save Set processing changes. Core save set processing breaks in the presence of nesting. This extension makes embedding applications more reliable

Unfortunately, no changes.

So I reckon I'll give up on that one and find other workarounds.

[ EDIT 2]

I had to revisit this issue again and by properly recoding the XFixes API, it all works!

So in the end, the code:

XReparentWindow(display, childWindowId, newParentWindowId, 0, 0);
XFixesChangeSaveSet(display, childWindowId, SetModeInsert, SaveSetRoot, SaveSertMap);

does the trick.

Turnout answered 17/9, 2014 at 12:23 Comment(1)
Hi Denis. I'm having an issue with XReparentWindow, would you mind have a look?Cheju
K
2

"the event DestroyWindow is sent to the hosted window" sounds like it's java runtime doing cleanup (and not respecting save set). From "connection close" section 10 of x11 protocol:

When a client's resources are destroyed, for each window in the client's
save-set, if the window is an inferior of a window created by the client, the
save-set window is reparented to the closest ancestor such that the save-set
window is not an inferior of a window created by the client. If the save-set
window is unmapped, a MapWindow request is performed on it (even if it was not
an inferior of a window created by the client). The reparenting leaves
unchanged the absolute coordinates (with respect to the root window) of the
upper-left outer corner of the save-set window. After save-set processing, all
windows created by the client are destroyed. For each nonwindow resource
created by the client, the appropriate Free request is performed. All colors
and colormap entries allocated by the client are freed.

Can you post xmon (or xtruss or xtrace) communication dump few seconds before and after crash?

Also, for simple operations like "reparent this window and add to save set" I'd try to use low level client like https://github.com/xderoche/J11 and avoid all this "extra thread + ffi call to .so (is that xlib?)"

Kraul answered 18/9, 2014 at 0:26 Comment(3)
Thanks heap Andrey. I'll update the post with the logs from xmon ASAP. I could indeed reproduce the issue with a very basic Java application, leading to a Java/X11 issue. One thing I don't get however is that I kill -9 the Java process. How could it be in charge of the cleanup? I would expect the X server to detect the loss of its client and walk through the save set. And yep, that's xlib. Cheers.Turnout
Yep, if you do kill -9 there is no way por java process to do extra x requests. Another suggestion is to use SaveSet from XFixes extension - it's a bit different and you can tell which window reparent backKraul
Had to revisit the portion of code and properly redone the testing with the XFixes API. It works!! Not too sure what I've done at the first attempt but the same bit of code now works. Thanks a bunch for the idea.Turnout

© 2022 - 2024 — McMap. All rights reserved.