From: "Rémi Bernon" Subject: [PATCH v3 03/10] winex11.drv: Wait for the WM to release the cursor before handling FocusIn events. Message-Id: <20191007130216.21159-4-rbernon@codeweavers.com> Date: Mon, 7 Oct 2019 15:02:09 +0200 In-Reply-To: <20191007130216.21159-1-rbernon@codeweavers.com> References: <20191007130216.21159-1-rbernon@codeweavers.com> 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 --- 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