The menu behavior changes depending on what has focus: the xpra session window, the menu within the window itself, or outside the window, the menu will either show up right away, or behind the window.
It goes as follows, and acts consistently all the way back to 0.9.8, r4003:
As noted in ticket:484#comment:6
menu has focus after click
menu has focus before you click
Window has focus, after clicking
Window has focus, before clicking
Window had no focus, after clicking
Window has no focus, before clicking
Can you reproduce the problem with simpler applications, like tests/xpra/test_apps/test_drop_down.py or gedit
?
Looks to me like this is a gtk-osx or gtk on osx issue, maybe due to an impedance mismatch between X11 window types and osx windows. We may be able to workaround the problem with judiciously placed calls to raise certain types of windows to force osx to do what is expected for those types of widget windows. May be related to #469
OK, I can reproduce the problem with the drop down menu on this example page: w3schools tryhtml_select2, with chrome only, no such problems with firefox.
I am pretty sure this is the same issue as #336 and #139: drop downs usually use input grabs: after we click on the widget ("mouse down") they expect to receive the "mouse up" event even if the mouse is released outside the drop down menu area they have just created, which never comes because we dispatch it to the parent window instead.
Here is what happens with an OSX client:
Got configure event: <gtk.gdk.Event at 0x19224a88: GDK_CONFIGURE x=814, y=332, width=64, height=78> Got map event: <gtk.gdk.Event at 0x19224a88: GDK_MAP> - OR=True focus-out-event for wid=155 _focus_change((<ClientWindow object at 0x18d86c88 (xpra+client+gtk2+client_window+ClientWindow at 0x7b4a7a0)>, \ <GParamBoolean 'has-toplevel-focus'>)) wid=155, has-toplevel-focus=False, _been_mapped=True update_focus(155, False) _focused=155 send_focus(0) add_packet_to_queue(button-action ...)
(window 155 is the browser window)
With Linux and win32 clients, we never get the has-toplevel-focus
event, we never send the focus change to the server and so the button-action
is delivered to the OR popup menu window.
adds focus debugging and prevent an OR window from making us unfocus a normal window
I tested with extra debugging (see focus-orignore.patch - which also tries to prevent OR windows from causing the main window to lose focus - mistakenly thinking that focus events were causing this behaviour), and since the problem occurs with Chrome and not with Firefox, I quickly became interested in the drop down window attributes, or lack thereof with Chrome: Firefox correctly uses a UTILITY
window type and sets the transient-for
property to point to the main window the utility drop down window is for. Chrome on the other hand, creates a NORMAL
window without setting transient-for
. I thought maybe these attributes helps the client side window manager (OSX) figure out what to do with this override-redirect window (keeping it on top).
Example events with the a patch to force both window-type
and transient-for
:
send_focus(407) add_packet_to_queue(focus ...) add_packet_to_queue(button-action ...) add_packet_to_queue(button-action ...) add_packet_to_queue(pointer-position ...) * 10 processing packet draw processing packet draw add_packet_to_queue(damage-sequence ...) add_packet_to_queue(damage-sequence ...) add_packet_to_queue(pointer-position ...) processing packet cursor add_packet_to_queue(pointer-position ...) add_packet_to_queue(button-action ...) processing packet new-override-redirect init_window(..) make_new_window: pid=17672, window-type=['UTILITY'], transient-for=407 make_new_window(442, 544, 383, 64, 86, {'fullscreen': False, 'has-alpha': False, 'xid': '0xe04ec6', 'pid': 17672, \ 'window-type': ['UTILITY'], 'maximized': False, 'transient-for': 407, 'override-redirect': True}, True, \ {'encodings.rgb_formats': ['RGB']}, 0)=\ <ClientWindow object at 0x1922c508 (xpra+client+gtk2+client_window+ClientWindow at 0x7b4abc0)>, group_leader_window=None processing packet draw window_state_updated(<ClientWindow object at 0x1922c508 \ (xpra+client+gtk2+client_window+ClientWindow at 0x7b4abc0)>, <gtk.gdk.Event at 0x19219998: GDK_WINDOW_STATE>) \ wid=442, new_window_state=<flags 0 of type GdkWindowState>, fullscreen=False, maximized=False add_packet_to_queue(damage-sequence ...) processing packet draw add_packet_to_queue(damage-sequence ...) processing packet cursor forcing OR for mouse position
update_focus(213, True) _focused=213 add_packet_to_queue(button-action ...) add_packet_to_queue(button-action ...) add_packet_to_queue(pointer-position ...) * 10 processing packet draw processing packet draw add_packet_to_queue(damage-sequence ...) add_packet_to_queue(damage-sequence ...) add_packet_to_queue(pointer-position ...) add_packet_to_queue(button-action ...) processing packet new-override-redirect init_window(..) make_new_window: pid=12659, window-type=['NORMAL'], transient-for=None forcing utility window forcing transient for 213 make_new_window(447, 838, 328, 64, 78, {'fullscreen': False, 'has-alpha': False, 'xid': '0xc16ce9', 'pid': 12659, \ 'window-type': ['UTILITY'], 'maximized': False, 'transient-for': 213, 'override-redirect': True}, True, \ {'encodings.rgb_formats': ['RGB']}, 0)=\ <ClientWindow object at 0x19230828 (xpra+client+gtk2+client_window+ClientWindow at 0x7b4ac70)>, group_leader_window=None processing packet draw window_state_updated(<ClientWindow object at 0x19230828 \ (xpra+client+gtk2+client_window+ClientWindow at 0x7b4ac70)>, <gtk.gdk.Event at 0x192198a8: GDK_WINDOW_STATE>) \ wid=447, new_window_state=<flags 0 of type GdkWindowState>, fullscreen=False, maximized=False add_packet_to_queue(damage-sequence ...) processing packet draw add_packet_to_queue(damage-sequence ...) add_packet_to_queue(button-action ...) forcing OR for mouse position add_packet_to_queue(pointer-position ...)
But forcing those attributes did not help.
Then, since the linux clients show the behaviour we expect, I tried to compare the client-side packet logging:
add_packet_to_queue(button-action ...) processing packet new-override-redirect make_new_window: pid=12659, window-type=['NORMAL'], transient-for=None make_new_window(387, 625, 380, 64, 78, {'fullscreen': False, 'has-alpha': False, 'xid': '0xc078cf', \ 'pid': 12659, 'window-type': ['NORMAL'], 'maximized': False, 'override-redirect': True}, True, \ {'encodings.rgb_formats': ['RGB']}, 0)=<ClientWindow object at 0x7f9d18029140 \ (xpra+client+gtk2+client_window+ClientWindow at 0x3f034e0)>, group_leader_window=None window_state_updated(<ClientWindow object at 0x7f9d18029140 \ (xpra+client+gtk2+client_window+ClientWindow at 0x3f034e0)>, <gtk.gdk.Event at 0x3ec2cb0: GDK_WINDOW_STATE>) \ wid=387, new_window_state=<flags 0 of type GdkWindowState>, fullscreen=False, maximized=False processing packet draw add_packet_to_queue(damage-sequence ...) processing packet draw add_packet_to_queue(damage-sequence ...) add_packet_to_queue(button-action ...) add_packet_to_queue(pointer-position ...)
add_packet_to_queue(button-action ...) processing packet new-override-redirect init_window(..) make_new_window: pid=12659, window-type=['NORMAL'], transient-for=None forcing utility window forcing transient for 213 make_new_window(393, 783, 328, 64, 78, {'fullscreen': False, 'has-alpha': False, 'xid': '0xc0a27a', \ 'pid': 12659, 'window-type': ['UTILITY'], 'maximized': False, 'transient-for': 213, \ 'override-redirect': True}, True, {'encodings.rgb_formats': ['RGB']}, 0)=\ <ClientWindow object at 0x1922a5a8 (xpra+client+gtk2+client_window+ClientWindow at 0x7b4abc0)>, group_leader_window=None window_state_updated(<ClientWindow object at 0x1922a5a8 \ (xpra+client+gtk2+client_window+ClientWindow at 0x7b4abc0)>, <gtk.gdk.Event at 0x19219878: GDK_WINDOW_STATE>) \ wid=393, new_window_state=<flags 0 of type GdkWindowState>, fullscreen=False, maximized=False processing packet draw processing packet draw add_packet_to_queue(button-action ...) add_packet_to_queue(damage-sequence ...)
No difference there apart from the attributes forced by the patch. The order of the damage-sequence
packets do not matter.
I also tried forcing the new OR window to be shown on top using window.present()
instead of just window.show
, no difference.
The win32 workaround code in _button_action
which simulates a mouse-down event in some cases looked quite suspicious - but it does not fire in this case.
When clicking slowly, one can see that the drop-down menu window only gets pushed down the window stacking order when we release the button, presumably because OSX sees this as a click on the main window (which it is) and decides to raise it then. But then, why is Firefox's window treated any different??
Well xpra info
doesn't show any difference whatsoever between the case where it "works" and when it doesn't (which seems to be a little hit and miss - depending on how fast you click and release the button):
window[556].focused=False window[556].fullscreen=False window[556].has-alpha=False window[556].maximized=False window[556].override-redirect=True window[556].pid=12659 window[556].position=(815, 328) window[556].property.fullscreen=False window[556].property.scaling=(1, 1) window[556].scaling=(1, 1) window[556].size=(64, 78) window[556].suspended=False window[556].total_frames[png]=1 window[556].total_pixels[png]=4992 window[556].tray=False window[556].window-type=('NORMAL',) window[556].xid=0xc1b08e
And with Firefox, the only difference is the window-type
and transient-for
attributes we already know about:
window[559].focused=False window[559].fullscreen=False window[559].has-alpha=False window[559].maximized=False window[559].override-redirect=True window[559].pid=17672 window[559].position=(590, 449) window[559].property.fullscreen=False window[559].property.scaling=(1, 1) window[559].scaling=(1, 1) window[559].size=(64, 86) window[559].suspended=False window[559].total_frames[png]=6 window[559].total_pixels[png]=33024 window[559].transient-for=407 window[559].tray=False window[559].window-type=('UTILITY',) window[559].xid=0xe0be43
So we must be setting attributes differently on the client side, maybe the main window and not the drop down window?
patch including all the different changes that were tested - works!
trimmed down patch which forces transient-for and fixes focus behaviour on osx
better patch which tries to use the currently focused window as transient-for window (if possible)
Never mind... the patch I was testing with changed the metadata too late, this patch works. I am not merging it yet because what it does is quite nasty.
What should happen is that Chrome should just be setting transient-for
on its override-redirect windows like Firefox does. win32 and Linux honour OR windows without this, but OSX does not. I suspect that the native OSX builds of Chrome do so, someone should submit a patch to Chrome to do the same thing on Linux and win32.
The patch does something naughty when running on OSX: it finds a suitable window using the window's "pid
" attribute and uses it..
The problem is that we're just guessing which window to use: we grab a NORMAL
non-OR window and use it (preferring the currently focused window - otherwise, the first one we come accross..) It may not be the right one!
But there isn't much more we can do: OR windows can come up at any time, not necessarily driven by user input.. and not necessarily already focused.
So please test on OSX only:
Works-for-me(tm). Merged in r5176 and backport in r5177 since I need to prepare another stable branch update, please see comment:5 and close after testing.
Tested in OSX (10.9) running r5178, seems to be working for now. Consistently has the drop-down menu appear at the top.
Note: r5176 had been reverted for trunk in r5396 as #139 is a better/cleaner solution to this problem.
code to force focus on osx
Since I cannot reproduce this, can you please record a short video (less than 5 seconds) showing how you reproduce this, maybe I'm not doing it right.
Assuming the problems is focus / raising related, maybe we can use this code: https://github.com/facelessuser/Rummage/blob/master/_gui/platform_window_focus.py
(edit: 404, fortunate that I also attached it to this ticket!).
Note: this may be impacted by recent changes in trunk for 0.15: see #718, #765
r15606 now forces the focus for initial windows (and this workaround is then disabled after 2 seconds). We use Python, Quartz, GTK, and window focus. Maybe this workaround can help with #469?
Note: we also use this force-focus code in the new GUI (#1762) but this could be done better still: as well as using a timer, we could listen for the focus event and disable the workaround when the window gets focus.
this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/490