Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java Menu Stays on top of all other windows #1980

Closed
totaam opened this issue Oct 5, 2018 · 19 comments
Closed

Java Menu Stays on top of all other windows #1980

totaam opened this issue Oct 5, 2018 · 19 comments

Comments

@totaam
Copy link
Collaborator

totaam commented Oct 5, 2018

Issue migrated from trac ticket # 1980

component: client | priority: critical | resolution: fixed

2018-10-05 08:37:30: mjharkin created the issue


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?

@totaam
Copy link
Collaborator Author

totaam commented Oct 5, 2018

2018-10-05 08:37:59: mjharkin uploaded file MenuOnTop.zip (36.5 KiB)

jar, source and screenshot

@totaam
Copy link
Collaborator Author

totaam commented Oct 5, 2018

2018-10-05 20:10:39: antoine changed owner from antoine to mjharkin

@totaam
Copy link
Collaborator Author

totaam commented Oct 5, 2018

2018-10-05 20:10:39: antoine commented


I believe this is due to grab handling (see #139, #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 # typePropertyNotify, xany # XAnyEventtype # PropertyNotify, serial539, send_event # false, display140140154739328, 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 # typeFocusOut, xany # XAnyEventtype # FocusOut, serial539, send_event # false, display140140154739328, window = sun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), 
Oct 06, 2018 2:02:36 AM sun.awt.X11.XWindowPeer handleFocusEvent
FINE: XFocusChangeEvent # typeFocusOut, serial # 539, send_eventfalse, display # 140140154739328, windowsun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), mode # 0, detail3, 
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 # typeFocusOut, serial # 539, send_eventfalse, display # 140140154739328, windowsun.awt.X11.XFocusProxyWindow@1e907ad3(c0001f), mode # 0, detail3, 
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..

@totaam
Copy link
Collaborator Author

totaam commented Oct 5, 2018

2018-10-05 22:43:43: mjharkin commented


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], [], [])

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 03:51:14: mjharkin commented


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)

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 05:29:54: antoine commented


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.

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 05:51:28: mjharkin commented


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.

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 13:47:06: antoine commented


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.

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 14:29:24: mjharkin changed status from new to closed

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 14:29:24: mjharkin set resolution to fixed

@totaam
Copy link
Collaborator Author

totaam commented Oct 6, 2018

2018-10-06 14:29:24: mjharkin commented


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.

@totaam
Copy link
Collaborator Author

totaam commented Oct 18, 2018

2018-10-18 18:34:32: antoine changed priority from major to critical

@totaam
Copy link
Collaborator Author

totaam commented Oct 18, 2018

2018-10-18 18:34:32: antoine changed status from closed to reopened

@totaam
Copy link
Collaborator Author

totaam commented Oct 18, 2018

2018-10-18 18:34:32: antoine removed resolution (was fixed)

@totaam
Copy link
Collaborator Author

totaam commented Oct 18, 2018

2018-10-18 18:34:32: antoine commented


This causes a serious regression with intellij: all the context menus are broken.

@totaam
Copy link
Collaborator Author

totaam commented Oct 20, 2018

2018-10-20 10:37:27: antoine changed status from reopened to closed

@totaam
Copy link
Collaborator Author

totaam commented Oct 20, 2018

2018-10-20 10:37:27: antoine set resolution to fixed

@totaam
Copy link
Collaborator Author

totaam commented Oct 20, 2018

2018-10-20 10:37:27: antoine commented


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 20712.

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:*

@totaam totaam closed this as completed Oct 20, 2018
@totaam
Copy link
Collaborator Author

totaam commented Feb 19, 2020

2020-02-19 11:44:32: antoine commented


See also #1955

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant