Because on X11 we cannot accurately reproduce the proper non-client messages sequence when using decorated windows, applications sometimes assume it is safe to call ClipCursor with a rectangle that matches the current window position as soon as they receive a WM_ACTIVATE message. When the WM finishes moving the window, and since commit 92177b0b161e91f1d609615d89d8e3199feea33f we would retry to clip the cursor with an incorrect rectangle.
Before the WM starts moving a window, an active pointer grab is usually initiated and we can use the result of XGrabPointer to decide whether the window is really focused or if we should wait until the WM has finished.
This adds a pseudo modal loop that waits for XGrabPointer to succeed - without any confine window to avoid spurious cursor movements - before notifying the applications of FocusIn events.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Not sure at all how the modal loop should be, I mostly reproduced the one that is in move_resize_window, so if anyone has a better idea I'll be happy to take it.
If this is too hackish, I believe it would be good to revert 92177b0b161e91f1d609615d89d8e3199feea33f, as it causes weird grabbing issues when moving windows in several games.
dlls/winex11.drv/event.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index f79f40c2323..e6190c56b1f 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -760,6 +760,33 @@ static const char * const focus_modes[] = "NotifyWhileGrabbed" };
+static void wait_for_pointer_ungrab( Display *display ) +{ + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XEvent *event; + + while (XGrabPointer( display, DefaultRootWindow( display ), False, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime ) != GrabSuccess) + { + MSG msg; + + while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + + /* we still need to process the ConfigureNotify events while delaying the FocusIn */ + event = thread_data->current_event; + thread_data->current_event = NULL; + MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT ); + thread_data->current_event = event; + } + + XUngrabPointer( display, CurrentTime ); +} + /********************************************************************** * X11DRV_FocusIn */ @@ -784,9 +811,11 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) keyboard_grabbed = TRUE; break; case NotifyNormal: + wait_for_pointer_ungrab( event->display ); keyboard_grabbed = FALSE; break; case NotifyUngrab: + wait_for_pointer_ungrab( event->display ); keyboard_grabbed = FALSE; retry_grab_clipping_window(); return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ -- 2.23.0
On 9/18/19 2:13 PM, Rémi Bernon wrote:
case NotifyUngrab:
wait_for_pointer_ungrab( event->display ); keyboard_grabbed = FALSE; retry_grab_clipping_window(); return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
Note that this line, when using mutter/gnome-shell at least, will make it almost impossible to switch to activity view using the Super key.
There's a small delay between the moment mutter releases the keyboard grab and sends the event, and the moment gnome-shell itself grabs the cursor and keyboard and if the application grabs the cursor in between - like it is done here even for a brief moment, gnome-shell is unable to get its grab and does not open the view.
In Proton we've specifically added a delay before that to let gnome-shell get its grab, but the mutter workarounds are not upstreamed. I could add a delay here as well, without the mutter specific check, if it seems appropriate.
On 9/18/19 2:22 PM, Rémi Bernon wrote:
On 9/18/19 2:13 PM, Rémi Bernon wrote:
case NotifyUngrab: + wait_for_pointer_ungrab( event->display ); keyboard_grabbed = FALSE; retry_grab_clipping_window(); return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
Note that this line, when using mutter/gnome-shell at least, will make it almost impossible to switch to activity view using the Super key.
There's a small delay between the moment mutter releases the keyboard grab and sends the event, and the moment gnome-shell itself grabs the cursor and keyboard and if the application grabs the cursor in between - like it is done here even for a brief moment, gnome-shell is unable to get its grab and does not open the view.
In Proton we've specifically added a delay before that to let gnome-shell get its grab, but the mutter workarounds are not upstreamed. I could add a delay here as well, without the mutter specific check, if it seems appropriate.
Although since 92177b0b161e91f1d609615d89d8e3199feea33f we were already retrying immediately to grab the pointer in retry_grab_clipping_window, so this patch won't change much. The delay might help anyway for both cases.
Rémi Bernon rbernon@codeweavers.com writes:
Because on X11 we cannot accurately reproduce the proper non-client messages sequence when using decorated windows, applications sometimes assume it is safe to call ClipCursor with a rectangle that matches the current window position as soon as they receive a WM_ACTIVATE message. When the WM finishes moving the window, and since commit 92177b0b161e91f1d609615d89d8e3199feea33f we would retry to clip the cursor with an incorrect rectangle.
Before the WM starts moving a window, an active pointer grab is usually initiated and we can use the result of XGrabPointer to decide whether the window is really focused or if we should wait until the WM has finished.
This adds a pseudo modal loop that waits for XGrabPointer to succeed - without any confine window to avoid spurious cursor movements - before notifying the applications of FocusIn events.
I don't think that's not a good idea. We used to have waits at various points to try to synchronize with the WM, but in the end it causes more trouble than it's worth. There's always some WM that doesn't follow the expected sequence, and then you get long timeouts and deadlocks. You need to find a way to make this work asynchronously.
On 9/19/19 1:09 PM, Alexander Julliard wrote:
Rémi Bernon rbernon@codeweavers.com writes:
Because on X11 we cannot accurately reproduce the proper non-client messages sequence when using decorated windows, applications sometimes assume it is safe to call ClipCursor with a rectangle that matches the current window position as soon as they receive a WM_ACTIVATE message. When the WM finishes moving the window, and since commit 92177b0b161e91f1d609615d89d8e3199feea33f we would retry to clip the cursor with an incorrect rectangle.
Before the WM starts moving a window, an active pointer grab is usually initiated and we can use the result of XGrabPointer to decide whether the window is really focused or if we should wait until the WM has finished.
This adds a pseudo modal loop that waits for XGrabPointer to succeed - without any confine window to avoid spurious cursor movements - before notifying the applications of FocusIn events.
I don't think that's not a good idea. We used to have waits at various points to try to synchronize with the WM, but in the end it causes more trouble than it's worth. There's always some WM that doesn't follow the expected sequence, and then you get long timeouts and deadlocks. You need to find a way to make this work asynchronously.
From my investigation it looks to me so far that calling XGrabPointer is the most "reliable" way - and so far the only way - to tell if a window manager is manipulating a window or not, and if there's some WM out there that do not grab the cursor while moving their window... well it's going to be the same as the current situation where applications assume they can freely control the cursor.
Then there could theoretically be some environments where it is not possible to ever grab the pointer, in which case I don't see any practical solution to this.
If it is better, it could be possible to delay the processing of FocusIn events until the grab succeeds. Retrying on every new event instead of actively waiting for it. Of course, if it is not possible to grab the pointer at all, then we're screwed as well.
Sorry, randomly saw this, and don't know the full context, so sorry if this is a rehash of what you've already looked at, or I didn't understand what you're trying to fix.
But shouldn't you be doing the normal focus lost / gain notification, but instead paying attention to the EnterNotify for this condition? When the WM ungrabs the pointer, you won't necessarily receive a Focus In, but rather you should always receive an EnterNotify, and I believe it should have mode = NotifyUngrab and a detail of NotifyAncestor. In that condition, you could send the WM_ACTIVATE at that time and not try to track the focus gained / lost, which you may not get? For example, playing w/ xev under the awesome WM, when I use the hotkey + mouse to move a window, the window doesn't lose / gain focus while that happens, but it does receive the EnterNotify with the above conditions when the WM finishes the move.
Kimball
On Fri, Sep 20, 2019 at 8:11 PM Rémi Bernon rbernon@codeweavers.com wrote:
On 9/19/19 1:09 PM, Alexander Julliard wrote:
Rémi Bernon rbernon@codeweavers.com writes:
Because on X11 we cannot accurately reproduce the proper non-client messages sequence when using decorated windows, applications sometimes assume it is safe to call ClipCursor with a rectangle that matches the current window position as soon as they receive a WM_ACTIVATE message. When the WM finishes moving the window, and since commit 92177b0b161e91f1d609615d89d8e3199feea33f we would retry to clip the cursor with an incorrect rectangle.
Before the WM starts moving a window, an active pointer grab is usually initiated and we can use the result of XGrabPointer to decide whether the window is really focused or if we should wait until the WM has finished.
This adds a pseudo modal loop that waits for XGrabPointer to succeed - without any confine window to avoid spurious cursor movements - before notifying the applications of FocusIn events.
I don't think that's not a good idea. We used to have waits at various points to try to synchronize with the WM, but in the end it causes more trouble than it's worth. There's always some WM that doesn't follow the expected sequence, and then you get long timeouts and deadlocks. You need to find a way to make this work asynchronously.
From my investigation it looks to me so far that calling XGrabPointer is the most "reliable" way - and so far the only way - to tell if a window manager is manipulating a window or not, and if there's some WM out there that do not grab the cursor while moving their window... well it's going to be the same as the current situation where applications assume they can freely control the cursor.
Then there could theoretically be some environments where it is not possible to ever grab the pointer, in which case I don't see any practical solution to this.
If it is better, it could be possible to delay the processing of FocusIn events until the grab succeeds. Retrying on every new event instead of actively waiting for it. Of course, if it is not possible to grab the pointer at all, then we're screwed as well. -- Rémi Bernon rbernon@codeweavers.com
On 9/20/19 11:53 AM, Kimball Thurston wrote:
Sorry, randomly saw this, and don't know the full context, so sorry if this is a rehash of what you've already looked at, or I didn't understand what you're trying to fix.
But shouldn't you be doing the normal focus lost / gain notification, but instead paying attention to the EnterNotify for this condition? When the WM ungrabs the pointer, you won't necessarily receive a Focus In, but rather you should always receive an EnterNotify, and I believe it should have mode = NotifyUngrab and a detail of NotifyAncestor. In that condition, you could send the WM_ACTIVATE at that time and not try to track the focus gained / lost, which you may not get? For example, playing w/ xev under the awesome WM, when I use the hotkey + mouse to move a window, the window doesn't lose / gain focus while that happens, but it does receive the EnterNotify with the above conditions when the WM finishes the move.
Kimball
You only get EnterNotify/LeaveNotify whenever the cursor enters or leaves the client area of a window, which is not the same as getting or losing focus. For example, if you use Alt-Tab without having the cursor inside the window, you only get FocusIn/FocusOut events.
The issue I'm trying to address is the difficulty to reproduce Windows message sequence on X11 when using decorated windows. On Windows, if the window frame is clicked, for example to move or resize the window, non-client WM_NCLBUTTONDOWN message is received and a modal message loop is triggered to handle the window repositioning until the mouse is released. On X11 I couldn't find a reliable way to tell if the decoration frames were involved in the received events.