This is another try at fixing the remaining cursor clipping issues. It uses same Focus* event delay mechanism from the previous version - with some codepath simplification for the UseTakeFocus option. (PATCH 1-3)
In addition to this, it also reverts some previous patches that introduced unnecessary complexity to replace them with a simpler version once focus events are delayed. (PATCH 4-10)
Rémi Bernon (10): winex11.drv: Make it possible to merge events across process_events calls. winex11.drv: Call XSetInputFocus only, whenever WM_TAKE_FOCUS is received. winex11.drv: Wait for the WM to release the cursor before handling FocusIn events. Revert "winex11.drv: Retry last ClipCursor when grab is released." Revert "winex11.drv: Only grab or warp the cursor when keyboard isn't grabbed." Revert "winex11.drv: Explicitly call XUngrabPointer when clipping is released." winex11.drv: Merge FocusIn/FocusOut NotifyGrab/NotifyUngrab cases. winex11.drv: Release clipping window on FocusOut events. winex11.drv: Use the current clipping rectangle when restoring grabs. winex11.drv: Retry calling XGrabPointer later if it failed.
dlls/winex11.drv/event.c | 198 +++++++++++++++++++++++--------------- dlls/winex11.drv/mouse.c | 42 +------- dlls/winex11.drv/x11drv.h | 3 +- 3 files changed, 127 insertions(+), 116 deletions(-)
-- 2.23.0
We will have to wait for several process_events calls while delaying FocusIn events.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 73 +++++++++++++++++++++++++-------------- dlls/winex11.drv/x11drv.h | 1 + 2 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 25730192d3d..32fdf23bf35 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -404,22 +404,50 @@ static inline BOOL call_event_handler( Display *display, XEvent *event ) return ret; }
+static inline BOOL merge_and_handle_events( Display *display, XEvent *prev, XEvent *next ) +{ + enum event_merge_action action = MERGE_DISCARD; + BOOL queued = FALSE; + + if (prev->type) action = merge_events( prev, next ); + switch( action ) + { + case MERGE_HANDLE: /* handle prev, keep new */ + queued |= call_event_handler( display, prev ); + /* fall through */ + case MERGE_DISCARD: /* discard prev, keep new */ + free_event_data( prev ); + *prev = *next; + break; + case MERGE_KEEP: /* handle new, keep prev for future merging */ + if (!next->type) break; + queued |= call_event_handler( display, next ); + /* fall through */ + case MERGE_IGNORE: /* ignore new, keep prev for future merging */ + free_event_data( next ); + break; + } + + return queued; +}
/*********************************************************************** * process_events */ static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg ) { - XEvent event, prev_event; + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XEvent event, *next = &event, *prev = &thread_data->prev_event; int count = 0; BOOL queued = FALSE; - enum event_merge_action action = MERGE_DISCARD;
- prev_event.type = 0; - while (XCheckIfEvent( display, &event, filter, (char *)arg )) + next->type = 0; + if (prev->type) get_event_data( prev ); + + while (XCheckIfEvent( display, next, filter, (char *)arg )) { count++; - if (XFilterEvent( &event, None )) + if (XFilterEvent( next, None )) { /* * SCIM on linux filters key events strangely. It does not filter the @@ -433,7 +461,7 @@ static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,X if (event.type == KeyRelease) { KeySym keysym = 0; - XKeyEvent *keyevent = &event.xkey; + XKeyEvent *keyevent = &next->xkey;
XLookupString(keyevent, NULL, 0, &keysym, NULL); if (!(keysym == XK_Shift_L || @@ -449,27 +477,20 @@ static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,X else continue; /* filtered, ignore it */ } - get_event_data( &event ); - if (prev_event.type) action = merge_events( &prev_event, &event ); - switch( action ) - { - case MERGE_HANDLE: /* handle prev, keep new */ - queued |= call_event_handler( display, &prev_event ); - /* fall through */ - case MERGE_DISCARD: /* discard prev, keep new */ - free_event_data( &prev_event ); - prev_event = event; - break; - case MERGE_KEEP: /* handle new, keep prev for future merging */ - queued |= call_event_handler( display, &event ); - /* fall through */ - case MERGE_IGNORE: /* ignore new, keep prev for future merging */ - free_event_data( &event ); - break; - } + get_event_data( next ); + queued |= merge_and_handle_events( display, prev, next ); } - if (prev_event.type) queued |= call_event_handler( display, &prev_event ); - free_event_data( &prev_event ); + + if (prev->type && !thread_data->current_event) + { + next->type = 0; + queued |= merge_and_handle_events( display, prev, next ); + free_event_data( prev ); + + /* retry handling prev next time */ + if (prev->type) queued |= 1; + } + XFlush( gdi_display ); if (count) TRACE( "processed %d events, returning %d\n", count, queued ); return queued; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 135faa8989b..e44239d834f 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -323,6 +323,7 @@ struct x11drv_valuator_data struct x11drv_thread_data { Display *display; + XEvent prev_event; /* event to be eventually merged */ XEvent *current_event; /* event currently being processed */ HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */
This will trigger a normal FocusIn event for the corresponding window, which would then call set_focus.
It makes the handling of Focus events simpler by avoiding different codepaths depending on whether or not WM_TAKE_FOCUS is used. We can then also safely delay the WM_ACTIVATE messages without delaying the input focus from the WM perspective.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 32fdf23bf35..0fb91265a69 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -730,7 +730,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event ) MAKELONG(HTCAPTION,WM_LBUTTONDOWN) ); if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) { - set_focus( event->display, hwnd, event_time ); + XSetInputFocus( event->display, X11DRV_get_whole_window( hwnd ), RevertToParent, event_time ); return; } } @@ -739,7 +739,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event ) hwnd = GetForegroundWindow(); if (!hwnd) hwnd = last_focus; if (!hwnd) hwnd = GetDesktopWindow(); - set_focus( event->display, hwnd, event_time ); + XSetInputFocus( event->display, X11DRV_get_whole_window( hwnd ), RevertToParent, event_time ); return; } /* try to find some other window to give the focus to */ @@ -747,7 +747,8 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event ) 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)) + XSetInputFocus( event->display, X11DRV_get_whole_window( hwnd ), RevertToParent, event_time ); } else if (protocol == x11drv_atom(_NET_WM_PING)) { @@ -815,11 +816,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) }
if ((xic = X11DRV_get_ic( hwnd ))) XSetICFocus( xic ); - if (use_take_focus) - { - if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE ); - return TRUE; - } + if (use_take_focus && hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
if (!can_activate_window(hwnd)) {
Rémi Bernon rbernon@codeweavers.com writes:
This will trigger a normal FocusIn event for the corresponding window, which would then call set_focus.
It makes the handling of Focus events simpler by avoiding different codepaths depending on whether or not WM_TAKE_FOCUS is used. We can then also safely delay the WM_ACTIVATE messages without delaying the input focus from the WM perspective.
The problem is that some window managers assign focus to the window before sending WM_TAKE_FOCUS, even though they shouldn't. That's why we have to ignore FocusIn when using take focus mode.
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@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 */
This reverts commit 92177b0b161e91f1d609615d89d8e3199feea33f.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 8 -------- dlls/winex11.drv/mouse.c | 24 ------------------------ dlls/winex11.drv/x11drv.h | 1 - 3 files changed, 33 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 055125533cd..61f4b441b90 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -871,7 +871,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) break; case NotifyUngrab: keyboard_grabbed = FALSE; - retry_grab_clipping_window(); return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ }
@@ -969,13 +968,6 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) break; case NotifyGrab: keyboard_grabbed = TRUE; - - /* This will do nothing due to keyboard_grabbed == TRUE, but it - * will save the current clipping rect so we can restore it on - * FocusIn with NotifyUngrab mode. - */ - retry_grab_clipping_window(); - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 15e5c04a41e..4411382d3f1 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -125,9 +125,6 @@ XContext cursor_context = 0; static HWND cursor_window; static HCURSOR last_cursor; static DWORD last_cursor_change; -static RECT last_clip_rect; -static HWND last_clip_foreground_window; -static BOOL last_clip_refused; static RECT clip_rect; static Cursor create_cursor( HANDLE handle );
@@ -398,15 +395,8 @@ static BOOL grab_clipping_window( const RECT *clip ) if (keyboard_grabbed) { WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); - last_clip_refused = TRUE; - last_clip_foreground_window = GetForegroundWindow(); - last_clip_rect = *clip; return FALSE; } - else - { - last_clip_refused = FALSE; - }
/* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) enable_xinput2(); @@ -481,20 +471,6 @@ void reset_clipping_window(void) ClipCursor( NULL ); /* make sure the clip rectangle is reset too */ }
-/*********************************************************************** - * retry_grab_clipping_window - * - * Restore the current clip rectangle or retry the last one if it has - * been refused because of an active keyboard grab. - */ -void retry_grab_clipping_window(void) -{ - if (clipping_cursor) - ClipCursor( &clip_rect ); - else if (last_clip_refused && GetForegroundWindow() == last_clip_foreground_window) - ClipCursor( &last_clip_rect ); -} - BOOL CDECL X11DRV_ClipCursor( const RECT *clip );
/*********************************************************************** diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index e44239d834f..39de219b0ca 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -626,7 +626,6 @@ extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) DECLSPEC_HIDDEN; extern void ungrab_clipping_window(void) DECLSPEC_HIDDEN; extern void reset_clipping_window(void) DECLSPEC_HIDDEN; -extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN;
This reverts commit 54f8077c41f715cfcf9c2bc016d964b720911326.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 11 ----------- dlls/winex11.drv/mouse.c | 12 ------------ dlls/winex11.drv/x11drv.h | 1 - 3 files changed, 24 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 61f4b441b90..a08cd7a8c70 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -155,9 +155,6 @@ static const char * event_names[MAX_EVENT_HANDLERS] = "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent" };
-/* is someone else grabbing the keyboard, for example the WM, when manipulating the window */ -BOOL keyboard_grabbed = FALSE; - int xinput2_opcode = 0;
/* return the name of an X event */ @@ -861,16 +858,12 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) { case NotifyGrab: /* these are received when moving undecorated managed windows on mutter */ - keyboard_grabbed = TRUE; return FALSE; case NotifyWhileGrabbed: - keyboard_grabbed = TRUE; break; case NotifyNormal: - keyboard_grabbed = FALSE; break; case NotifyUngrab: - keyboard_grabbed = FALSE; return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ }
@@ -958,16 +951,12 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) { case NotifyUngrab: /* these are received when moving undecorated managed windows on mutter */ - keyboard_grabbed = FALSE; return FALSE; case NotifyNormal: - keyboard_grabbed = FALSE; break; case NotifyWhileGrabbed: - keyboard_grabbed = TRUE; break; case NotifyGrab: - keyboard_grabbed = TRUE; return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 4411382d3f1..8f3118e04eb 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -392,12 +392,6 @@ static BOOL grab_clipping_window( const RECT *clip ) GetModuleHandleW(0), NULL ))) return TRUE;
- if (keyboard_grabbed) - { - WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); - return FALSE; - } - /* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) enable_xinput2();
@@ -1445,12 +1439,6 @@ BOOL CDECL X11DRV_SetCursorPos( INT x, INT y ) struct x11drv_thread_data *data = x11drv_init_thread_data(); POINT pos = virtual_screen_to_root( x, y );
- if (keyboard_grabbed) - { - WARN( "refusing to warp to %u, %u\n", pos.x, pos.y ); - return FALSE; - } - if (!clipping_cursor && XGrabPointer( data->display, root_window, False, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 39de219b0ca..bb33e3c4d2b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -383,7 +383,6 @@ extern Colormap default_colormap DECLSPEC_HIDDEN; extern XPixmapFormatValues **pixmap_formats DECLSPEC_HIDDEN; extern Window root_window DECLSPEC_HIDDEN; extern BOOL clipping_cursor DECLSPEC_HIDDEN; -extern BOOL keyboard_grabbed DECLSPEC_HIDDEN; extern unsigned int screen_bpp DECLSPEC_HIDDEN; extern BOOL use_xkb DECLSPEC_HIDDEN; extern BOOL usexrandr DECLSPEC_HIDDEN;
This reverts commit ae4651b7ffad963aa1501b3ba5eb953141942a76.
XUnmapWindow should be enough for the X server to release the pointer if it was confined or grabbed by clip_window.
We will call ungrab_clipping_window on every FocusOut event, which may happen after another window has received its FocusIn event and we do not want to interfer.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 8f3118e04eb..9bc46332f20 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -449,7 +449,6 @@ void ungrab_clipping_window(void)
TRACE( "no longer clipping\n" ); XUnmapWindow( display, clip_window ); - if (clipping_cursor) XUngrabPointer( display, CurrentTime ); clipping_cursor = FALSE; SendMessageW( GetDesktopWindow(), WM_X11DRV_CLIP_CURSOR, 0, 0 ); }
The return value was different as well, this makes it more consistent.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index a08cd7a8c70..bd1b0cf2d72 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -854,18 +854,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == GetDesktopWindow()) return FALSE;
- switch (event->mode) - { - case NotifyGrab: - /* these are received when moving undecorated managed windows on mutter */ - return FALSE; - case NotifyWhileGrabbed: - break; - case NotifyNormal: - break; - case NotifyUngrab: - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - } + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
if ((xic = X11DRV_get_ic( hwnd ))) XSetICFocus( xic ); if (use_take_focus && hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE ); @@ -947,18 +937,8 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) } if (!hwnd) return FALSE;
- switch (event->mode) - { - case NotifyUngrab: - /* these are received when moving undecorated managed windows on mutter */ - return FALSE; - case NotifyNormal: - break; - case NotifyWhileGrabbed: - break; - case NotifyGrab: - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - } + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
focus_out( event->display, hwnd ); return TRUE;
When a window receives FocusOut events, whether it is because the WM grabs the keyboard or because of an actual input focus change, we should release the clipping window.
We will re-apply it on FocusIn event if it is necessary from Wine's perspective.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index bd1b0cf2d72..d0b92f30153 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -937,6 +937,8 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) } if (!hwnd) return FALSE;
+ ungrab_clipping_window(); + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
We introduced unnecessary complexity by adding the last_clip_* state, we can instead use the ClipCursor state.
This restores the ClipCursor on FocusIn events by sending a WM_X11DRV_CLIP_CURSOR message to the foreground window, which will query the current clipping rect from the server and apply it.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index d0b92f30153..b41093856e3 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -854,6 +854,9 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == GetDesktopWindow()) return FALSE;
+ /* ask the foreground window to re-apply the current ClipCursor rect */ + SendMessageW( GetForegroundWindow(), WM_X11DRV_CLIP_CURSOR, 0, 0 ); + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
Sometimes X11 refuses to grab the cursor. It is not reported to the applications, and we don't even expect them to correctly handle these situations. Instead, we should retry clipping the cursor later on until it succeeds or until the application releases the cursor.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 9bc46332f20..2e23248fedf 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -425,6 +425,11 @@ static BOOL grab_clipping_window( const RECT *clip ) { disable_xinput2(); DestroyWindow( msg_hwnd ); + + /* we couldn't grab the cursor yet and we won't get notified when it + * is released, so we have to retry again a little bit later */ + Sleep(100); + PostMessageW( GetForegroundWindow(), WM_X11DRV_CLIP_CURSOR, 0, 0 ); return FALSE; } clip_rect = *clip; -- 2.23.0