[PATCH v3 03/10] winex11.drv: Wait for the WM to release the cursor before handling FocusIn events.

Rémi Bernon rbernon at codeweavers.com
Mon Oct 7 08:02:09 CDT 2019


The FocusIn/WM_TAKE_FOCUS events are sent as soon as a window title bar
is clicked, but sometimes the WM is still controlling the window
position. Waiting for the cursor grab to be released before sending
WM_ACTIVATE message helps solving this situation.

We pass through any NotifyGrab/NotifyUngrab focus events unless we are
delaying a FocusIn event. In this case, we have to merge these
notifications to reproduce the keyboard grab state as we cannot process
them out of order.

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

diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 0fb91265a69..055125533cd 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -314,15 +314,75 @@ static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawE
 }
 #endif
 
+static int try_grab_pointer( Display *display )
+{
+    if (clipping_cursor)
+        return 1;
+
+    if (XGrabPointer( display, root_window, False, 0, GrabModeAsync, GrabModeAsync,
+                      None, None, CurrentTime ) != GrabSuccess)
+        return 0;
+
+    XUngrabPointer( display, CurrentTime );
+    return 1;
+}
+
 /***********************************************************************
  *           merge_events
  *
  * Try to merge 2 consecutive events.
  */
-static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
+static enum event_merge_action merge_events( Display *display, XEvent *prev, XEvent *next )
 {
     switch (prev->type)
     {
+    case FocusIn:
+        if (root_window != DefaultRootWindow( display ))
+            break;
+        if (prev->xfocus.detail == NotifyPointer || prev->xfocus.detail == NotifyPointerRoot)
+            break;
+        if (prev->xfocus.mode == NotifyGrab || prev->xfocus.mode == NotifyUngrab)
+            break;
+
+        switch (next->type)
+        {
+        case FocusIn:
+        case FocusOut:
+            if (next->xfocus.detail == NotifyPointer || next->xfocus.detail == NotifyPointerRoot)
+                return MERGE_KEEP;
+            if (prev->xany.window != next->xany.window)
+                break;
+
+            /* merge grab notifications with the delayed focus event */
+            if (next->xfocus.mode == NotifyGrab)
+            {
+                prev->xfocus.mode = NotifyWhileGrabbed;
+                return MERGE_IGNORE;
+            }
+            else if (next->xfocus.mode == NotifyUngrab)
+            {
+                prev->xfocus.mode = NotifyNormal;
+                return MERGE_IGNORE;
+            }
+
+            if (next->type == FocusOut)
+            {
+                prev->type = 0; /* FocusIn/FocusOut sequence, discard prev as well */
+                TRACE( "Discarding FocusIn/FocusOut sequence for window %lx\n", prev->xany.window );
+                return MERGE_IGNORE;
+            }
+            else
+            {
+                TRACE( "Discarding old FocusIn event for window %lx\n", prev->xany.window );
+                return MERGE_DISCARD;
+            }
+        }
+
+        if (try_grab_pointer( display ))
+            break;
+
+        TRACE( "Unable to grab pointer yet, delaying FocusIn event\n" );
+        return MERGE_KEEP;
     case ConfigureNotify:
         switch (next->type)
         {
@@ -409,7 +469,7 @@ static inline BOOL merge_and_handle_events( Display *display, XEvent *prev, XEve
     enum event_merge_action action = MERGE_DISCARD;
     BOOL queued = FALSE;
 
-    if (prev->type) action = merge_events( prev, next );
+    if (prev->type) action = merge_events( display, prev, next );
     switch( action )
     {
     case MERGE_HANDLE:  /* handle prev, keep new */
-- 
2.23.0




More information about the wine-devel mailing list