Server on Centos7 r20582 Client Python on Windows r20582 Can't reproduce outside Xpra.
Another Java issue: When running the attached example: java -jar menuOnTop.jar If you open the menu and click outside of any other xpra window (set_focus(0)) then the menu remains on top of all other windows. See attached screenshot.
Not sure if it's this problem, but AWT menus seem to open with DIALOG window type instead of POPUP_MENU:
process_new_common: [11, 1370, 267, 131, 100, {'xid': '0xc00040', 'title': 'win0', 'client-machine': '2f8ecabfb918', 'pid': 8589, 'group-leader-xid': 12582919, 'window-type': ('DIALOG',), 'above': True, 'skip-taskbar': True, 'transient-for': 6, 'class-instance': ('sun-awt-X11-XWindowPeer', 'org-eclipse-jdt-internal-jarinjarloader-JarRsrcLoader'), 'override-redirect': True}], metadata={'xid': '0xc00040', 'title': 'win0', 'client-machine': '2f8ecabfb918', 'pid': 8589, 'group-leader-xid': 12582919, 'window-type': ('DIALOG',), 'above': True, 'skip-taskbar': True, 'transient-for': 6, 'class-instance': ('sun-awt-X11-XWindowPeer', 'org-eclipse-jdt-internal-jarinjarloader-JarRsrcLoader'), 'override-redirect': True}, OR=True 2018-10-05 09:16:50,050 make_new_window(..) client_window_classes=[<class 'xpra.client.gtk2.client_window.ClientWindow'>], group_leader_window=<gtk.gdk.Window object at 0x46ee960 (GdkWindow at 0x3797a20)>
At present the only place where this behaves correctly is clicking into another window, on the frame or outside doesn't work.
A really dirty fix I've done is to destroy the menu window in window_manager.py:
def send_focus(self, wid): focuslog("send_focus(%s)", wid) if wid==0: for w in self._id_to_window.values(): if w._metadata.get("skip-taskbar"): #look for java AWT wm_class = w._metadata.get("class-instance") if wm_class and len(wm_class)==2 and wm_class[0].startswith("sun-awt-X11"): w.destroy()
Is there a better approach?
jar, source and screenshot
I believe this is due to grab handling (see #139, ticket:705#comment:7). Java must be grabbing the pointer, expecting to receive the click outside the drop down menu window. Since we're forwarding the window to a different display server, we may never get the click event, only a loss of focus. Most toolkits will handle that just fine, but Java is different...
Your patch looks a little bit dangerous to me, I remember testing something similar and causing crashes. At least, I believe that the check should be modified to only affect OR windows since the drop down menu uses that. And it should be possible to disable this behaviour using an environment variable if this is to be enabled by default.
Ideally, this would be fixed server side by causing the grab to be broken and making Java notice it. I've tried using xdotool key XF86Ungrab
but this didn't help.
Looking at XUngrabPointer: The X server performs an UngrabPointer request automatically if the event window or confine_to window for an active pointer grab becomes not viewable or if window reconfiguration causes the confine_to window to lie completely outside the boundaries of the root window - we can't "randomly" make windows hidden and hope that's going to help either. (we don't even know when they expect to hold a grab..)
The Java source around grabs reads: We should always grab both keyboard and pointer to control event flow on popups. This also simplifies synthetic grab implementation. The active grab overrides activated automatic grab.
I patched your source code right at the start of initialize()
to add debug logging to AWT X11:
LogManager logManager = LogManager.getLogManager(); ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.FINEST); consoleHandler.setFormatter(new SimpleFormatter()); logManager.getLogger("").setLevel(Level.FINER); logManager.getLogger("").addHandler(consoleHandler);
And this is what I got (edited slightly) when I clicked on another window (not an xpra window):
FINER: XEvent = type = PropertyNotify, xany = XAnyEvent = type = PropertyNotify, serial = 539, send_event = false, display = 140140154739328, window = 44, Oct 06, 2018 2:02:36 AM sun.awt.X11.XToolkit callTimeoutTasks FINER: XToolkit.callTimeoutTasks(): current time=1538766156238; tasks=null Oct 06, 2018 2:02:36 AM sun.awt.X11.XToolkit run FINER: XEvent = type = FocusOut, xany = XAnyEvent = type = FocusOut, serial = 539, send_event = false, display = 140140154739328, window = sun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), Oct 06, 2018 2:02:36 AM sun.awt.X11.XWindowPeer handleFocusEvent FINE: XFocusChangeEvent = type = FocusOut, serial = 539, send_event = false, display = 140140154739328, window = sun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), mode = 0, detail = 3, Oct 06, 2018 2:02:36 AM sun.awt.X11.XKeyboardFocusManagerPeer setCurrentFocusedWindow FINER: Setting current focused window null Oct 06, 2018 2:02:36 AM sun.awt.X11.XDecoratedPeer handleFocusEvent FINER: Received focus event on shell: XFocusChangeEvent = type = FocusOut, serial = 539, send_event = false, display = 140140154739328, window = sun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), mode = 0, detail = 3, Oct 06, 2018 2:02:36 AM sun.awt.X11.XToolkit callTimeoutTasks FINER: XToolkit.callTimeoutTasks(): current time=1538766156243; tasks=null Oct 06, 2018 2:02:36 AM java.awt.KeyboardFocusManager retargetFocusEvent FINER: >>> java.awt.event.WindowEvent[WINDOW_LOST_FOCUS,opposite=null,oldState=0,newState=0] on frame0 Oct 06, 2018 2:02:36 AM java.awt.DefaultKeyboardFocusManager dispatchEvent FINE: java.awt.event.WindowEvent[WINDOW_LOST_FOCUS,opposite=null,oldState=0,newState=0] on frame0 Oct 06, 2018 2:02:36 AM sun.awt.X11.XToolkit callTimeoutTasks FINER: XToolkit.callTimeoutTasks(): current time=1538766156244; tasks=null Oct 06, 2018 2:02:36 AM java.awt.DefaultKeyboardFocusManager dispatchEvent FINE: Active javax.swing.JFrame[frame0,100,63,300x80,invalid,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=EXIT_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true], Current focused javax.swing.JFrame[frame0,100,63,300x80,invalid,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=EXIT_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true], losing focus javax.swing.JFrame[frame0,100,63,300x80,invalid,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=EXIT_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true] opposite null Oct 06, 2018 2:02:36 AM java.awt.KeyboardFocusManager retargetFocusEvent FINER: >>> java.awt.FocusEvent[FOCUS_LOST,temporary,opposite=null,cause=ACTIVATION] on javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=] Oct 06, 2018 2:02:36 AM java.awt.DefaultKeyboardFocusManager dispatchEvent FINE: java.awt.FocusEvent[FOCUS_LOST,temporary,opposite=null,cause=ACTIVATION] on javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=] Oct 06, 2018 2:02:36 AM sun.awt.X11.XComponentPeer handleJavaFocusEvent FINER: java.awt.FocusEvent[FOCUS_LOST,temporary,opposite=null,cause=ACTIVATION] on javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=] Oct 06, 2018 2:02:36 AM sun.awt.X11.XComponentPeer focusLost FINE: java.awt.FocusEvent[FOCUS_LOST,temporary,opposite=null,cause=ACTIVATION] on javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=] Oct 06, 2018 2:02:36 AM java.awt.KeyboardFocusManager retargetFocusEvent FINER: >>> java.awt.event.WindowEvent[WINDOW_DEACTIVATED,opposite=null,oldState=0,newState=0] on frame0 Oct 06, 2018 2:02:36 AM java.awt.DefaultKeyboardFocusManager dispatchEvent FINE: java.awt.event.WindowEvent[WINDOW_DEACTIVATED,opposite=null,oldState=0,newState=0] on frame0 Oct 06, 2018 2:02:36 AM java.awt.KeyboardFocusManager setGlobalActiveWindow FINER: Setting global active window to null, old active javax.swing.JFrame[frame0,100,63,300x80,invalid,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=EXIT_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,37,300x43,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
So Java is seeing the FocusOut
event, handling it but not closing the drop down menu window..
Great analysis, thanks.
I'll look at trying something on the server side over the weekend. For now, the best I've got is this which is quite a bit safer and works well apart from 2 clicks are need to enter the menu again. It's also very close to what's actually happening at least from the perspective of the xpra windows.
def send_focus(self, wid): focuslog("send_focus(%s)", wid) if wid==0: mouselog("send_focus() Sending mouse event to wid 0 to ungrap java awt menus") self.send_button(0, 1, True, [0,0], [], [])
My bad, needed the unpress and now works as expected. Think the place for this would be send_lost_focus(). Can you accept this patch?
def send_lost_focus(self): self.lost_focus_timer = None #check that a new window has not gained focus since: if self._focused is None: mouselog("send_focus() sending button click to wid=0") self.send_button(0, 1, True, [0,0], [], []) self.send_button(0, 1, False, [0,0], [], []) self.send_focus(0)
Can you accept this patch?
I don't think so: we can't just send click events at 0,0 whenever we get lost focus events as that would wreak havoc with many well behaved applications that do have windows mapped in the top left corner. (ie: OR fullscreen windows)
The w.destroy()
solution is actually safer, as it is more targeted.
A better solution might be to automatically grab the pointer (and keyboard?) whenever we see an override-redirect AWT window. The AWT window would then receive the button events outside the window area.
Am I understanding this wrong? I thought a mouse button event sent to wid=0 would have no effect on other windows apart from lost of grab or focus.
I believe r20608 solves this problem for win32 clients much more cleanly: we force a pointer grab when we find an AWT override redirect window.
And maybe we should apply this workaround to more than just Java's windows.. (the env var XPRA_OR_FORCE_GRAB
makes it easier to test at runtime)
For X11 clients, this is a bit more complicated: we were breaking the grab when we got focus change events. r20609 looks correct, but this is the sort of change that makes me nervous: this "bug" had been there since r12645 (more than 2 years ago) and could cause regressions.
Perfect, tested on r20608 and this solves the problem from my side at least and a lot cleaner. I'm still trying to get a handle on all this window manager stuff.
Much appreciated.
This causes a serious regression with intellij: all the context menus are broken.
Intellij's popup windows look like this:
process_new_common: [6, 1721, 791, 403, 494, {'xid': '0xe00214', 'title': 'win1', \ 'client-machine': 'desktop', 'pid': 31424, 'group-leader-xid': 14680134, \ 'window-type': ('POPUP_MENU',), 'skip-taskbar': True, 'transient-for': 3, \ 'class-instance': ('sun-awt-X11-XWindowPeer', 'jetbrains-idea-ce'), \ 'override-redirect': True}], metadata=.., OR=True
So r20710 fixes the regression by only applying the OR force grab workaround to awt windows of type DIALOG. Backported to v2.4 in r20712.
r20711 makes the code more generic and changes the environment variable format to accept wildcards for both the window-type and wmclass:
XPRA_OR_FORCE_GRAB=DIALOG:sun-awt-X11
So we can do things like:
XPRA_OR_FORCE_GRAB=DIALOG:sun-awt-X11,*:anotherwmclass,MENU:someotherwmclass,TOOLBAR:*
See also #1955
this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/1980