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.