[PATCH v4 5/8] winex11.drv: Wait for pointer grab on FocusIn/WM_TAKE_FOCUS events.

Rémi Bernon rbernon at codeweavers.com
Mon Oct 21 02:13:43 CDT 2019


The FocusIn/WM_TAKE_FOCUS events are received as soon as a window is
clicked, but when some modifier key is pressed or when the click is on
the window frame, the WM may still be controlling the window size or
position. It usually grabs the cursor while doing so - and if not then
there's apparently nothing we can do.

When using undecorated mode we handle this case "correctly" by going
through the corresponding Windows non-client message loop until mouse
buttons are released, but when using decorated windows the window
decoration is empty from the Wine perspective and any window event is
considered as happening in the client area.

This leads to some issues when the window is moved or resized, with
applications applying clipping rectangles immediately and not updating
it on subsequent window move/resize messages. Delaying the WM_ACTIVATE
until the WM releases its grab and the window move is complete helps
solving this situation.

This delay is implemented here by resending the FocusIn/WM_TAKE_FOCUS
events to the window until the cursor can be grabbed and then processing
them normally.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/winex11.drv/event.c | 46 ++++++++++++++++++++++++++++++++++------
 1 file changed, 39 insertions(+), 7 deletions(-)

diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 07777952400..426ac785082 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -311,6 +311,24 @@ static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawE
 }
 #endif
 
+static int try_grab_pointer( Display *display )
+{
+    if (!grab_pointer)
+        return 1;
+
+    /* if we are already clipping the cursor in the current thread, we should not
+     * call XGrabPointer here or it would change the confine-to window. */
+    if (clipping_cursor && x11drv_thread_data()->clip_hwnd)
+        return 1;
+
+    if (XGrabPointer( display, root_window, False, 0, GrabModeAsync, GrabModeAsync,
+                      None, None, CurrentTime ) != GrabSuccess)
+        return 0;
+
+    XUngrabPointer( display, CurrentTime );
+    return 1;
+}
+
 /***********************************************************************
  *           merge_events
  *
@@ -591,12 +609,18 @@ static void set_input_focus( struct x11drv_win_data *data )
 /**********************************************************************
  *              set_focus
  */
-static void set_focus( Display *display, HWND hwnd, Time time )
+static void set_focus( XEvent *event, HWND hwnd, Time time )
 {
     HWND focus;
     Window win;
     GUITHREADINFO threadinfo;
 
+    if (!try_grab_pointer( event->xany.display ))
+    {
+        XSendEvent( event->xany.display, event->xany.window, False, 0, event );
+        return;
+    }
+
     TRACE( "setting foreground window to %p\n", hwnd );
     SetForegroundWindow( hwnd );
 
@@ -610,7 +634,7 @@ static void set_focus( Display *display, HWND hwnd, Time time )
     if (win)
     {
         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
-        XSetInputFocus( display, win, RevertToParent, time );
+        XSetInputFocus( event->xany.display, win, RevertToParent, time );
     }
 }
 
@@ -709,7 +733,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev )
                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
             {
-                set_focus( event->display, hwnd, event_time );
+                set_focus( xev, hwnd, event_time );
                 return;
             }
         }
@@ -718,7 +742,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev )
             hwnd = GetForegroundWindow();
             if (!hwnd) hwnd = last_focus;
             if (!hwnd) hwnd = GetDesktopWindow();
-            set_focus( event->display, hwnd, event_time );
+            set_focus( xev, hwnd, event_time );
             return;
         }
         /* try to find some other window to give the focus to */
@@ -726,7 +750,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev )
         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
         if (!hwnd) hwnd = GetActiveWindow();
         if (!hwnd) hwnd = last_focus;
-        if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
+        if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, event_time );
     }
     else if (protocol == x11drv_atom(_NET_WM_PING))
     {
@@ -791,9 +815,17 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
         if (!hwnd) hwnd = GetActiveWindow();
         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
-        if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
+        if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, CurrentTime );
+        return TRUE;
+    }
+
+    if (!try_grab_pointer( event->display ))
+    {
+        XSendEvent( event->display, event->window, False, 0, xev );
+        return FALSE;
     }
-    else SetForegroundWindow( hwnd );
+
+    SetForegroundWindow( hwnd );
     return TRUE;
 }
 
-- 
2.23.0




More information about the wine-devel mailing list