xpra icon
Bug tracker and wiki

Opened 6 years ago

Closed 6 years ago

Last modified 11 months ago

#490 closed defect (fixed)

Left-clicking on Drop Down Menus with OSX client sometimes results in z-index issues with menu appearing behind the window

Reported by: J. Max Mena Owned by: J. Max Mena
Priority: major Milestone: 0.11
Component: client Version:
Keywords: osx Cc:

Description (last modified by Antoine Martin)

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:

  • Left clicking on drop down menus in an xpra session window which does not have focus - the drop down menu works as expected.
  • Left clicking on drop down menus in an xpra session window which does have focus - the drop down menu appears "behind" the window (though the drop menu can be clicked "through" the window which "appears" to have focus).
  • Left clicking on drop down menus in an xpra session window, when the menu itself has focus - the drop down menu works as expected.
  • Using the keyboard to tab to the menu and select it - works as expected. (Presumably this method brings focus to the menu item before "clicking" it, thus working as the instance above.)

As noted in ticket:484#comment:6

Attachments (11)

Xpra4003 menu focus after click.png (273.0 KB) - added by J. Max Mena 6 years ago.
menu has focus after click
Xpra4003 menu focus before click.png (484.9 KB) - added by J. Max Mena 6 years ago.
menu has focus before you click
xpra4003 window focus after click.png (117.9 KB) - added by J. Max Mena 6 years ago.
Window has focus, after clicking
xpra4003 window focus before click.png (2.6 MB) - added by J. Max Mena 6 years ago.
Window has focus, before clicking
Xpra4003 window no focus after click.png (2.3 MB) - added by J. Max Mena 6 years ago.
Window had no focus, after clicking
Xpra4003 window no focus before click.png (2.3 MB) - added by J. Max Mena 6 years ago.
Window has no focus, before clicking
focus-orignore.patch (4.2 KB) - added by Antoine Martin 6 years ago.
adds focus debugging and prevent an OR window from making us unfocus a normal window
focus-force-transientfor.patch (8.7 KB) - added by Antoine Martin 6 years ago.
patch including all the different changes that were tested - works!
focus-force-transientfor-v4.patch (1.2 KB) - added by Antoine Martin 6 years ago.
trimmed down patch which forces transient-for and fixes focus behaviour on osx
focus-force-transientfor-v5.patch (1.4 KB) - added by Antoine Martin 6 years ago.
better patch which tries to use the currently focused window as transient-for window (if possible)
platform_window_focus.py (1.1 KB) - added by Antoine Martin 5 years ago.
code to force focus on osx

Change History (24)

Changed 6 years ago by J. Max Mena

menu has focus after click

Changed 6 years ago by J. Max Mena

menu has focus before you click

Changed 6 years ago by J. Max Mena

Window has focus, after clicking

Changed 6 years ago by J. Max Mena

Window has focus, before clicking

Changed 6 years ago by J. Max Mena

Window had no focus, after clicking

Changed 6 years ago by J. Max Mena

Window has no focus, before clicking

comment:1 Changed 6 years ago by Antoine Martin

Description: modified (diff)
Owner: changed from Antoine Martin to Antoine Martin
Status: newassigned

comment:2 Changed 6 years ago by Antoine Martin

Keywords: osx added
Milestone: 0.12

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

Last edited 6 years ago by Antoine Martin (previous) (diff)

comment:3 Changed 6 years ago by Antoine Martin

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.

Last edited 6 years ago by Antoine Martin (previous) (diff)

Changed 6 years ago by Antoine Martin

Attachment: focus-orignore.patch added

adds focus debugging and prevent an OR window from making us unfocus a normal window

comment:4 Changed 6 years ago by Antoine Martin

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:

  • firefox:
    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
    
  • chrome:
    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:

  • linux client:
    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 ...)
    
  • OSX client:
    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?

Changed 6 years ago by Antoine Martin

patch including all the different changes that were tested - works!

Changed 6 years ago by Antoine Martin

trimmed down patch which forces transient-for and fixes focus behaviour on osx

Changed 6 years ago by Antoine Martin

better patch which tries to use the currently focused window as transient-for window (if possible)

comment:5 Changed 6 years ago by Antoine Martin

Owner: changed from Antoine Martin to J. Max Mena
Status: assignednew

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:

  • that it works
  • that this does not cause any focus regressions elsewhere with OR windows (dialogs, popups, etc..)
Last edited 6 years ago by Antoine Martin (previous) (diff)

comment:6 Changed 6 years ago by Antoine Martin

Milestone: 0.120.11

comment:7 Changed 6 years ago by Antoine Martin

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.

comment:8 Changed 6 years ago by J. Max Mena

Resolution: fixed
Status: newclosed

Tested in OSX (10.9) running r5178, seems to be working for now. Consistently has the drop-down menu appear at the top.

Last edited 6 years ago by J. Max Mena (previous) (diff)

comment:9 Changed 6 years ago by Antoine Martin

Note: r5176 had been reverted for trunk in r5396 as #139 is a better/cleaner solution to this problem.

Changed 5 years ago by Antoine Martin

Attachment: platform_window_focus.py added

code to force focus on osx

comment:10 Changed 5 years ago by Antoine Martin

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!).

Last edited 2 years ago by Antoine Martin (previous) (diff)

comment:11 Changed 5 years ago by Antoine Martin

Note: this may be impacted by recent changes in trunk for 0.15: see #718, #765

comment:12 Changed 2 years ago by Antoine Martin

r15606 now forces the focus for initial windows (and this workaround is then disabled after 2 seconds).
We use activateIgnoringOtherApps as explained here: Python, Quartz, GTK, and window focus.
Maybe this workaround can help with #469?

comment:13 Changed 11 months ago by Antoine Martin

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.

Note: See TracTickets for help on using tickets.