When the same thread repeatedly calls ClipCursor, a message window is created for every call, then stored in x11drv_thread_data->clip_hwnd.
The WM_X11DRV_CLIP_CURSOR notification is then sent to the desktop window, then to the previous clipping thread in order for it to destroy its clip_hwnd. But as the clipping thread is the same, and because x11drv_thread_data->clip_hwnd has been overwritten, it does not satisfy the "hwnd == data->clip_hwnd" condition and the window leaked.
This was causing exhaustion of user handles, and ultimately, failures to create new clipping message windows, which then in turn makes the pointer grab be lost.
This change makes sure that any dangling clipping message window is properly destroyed.
Note: Implementing this by avoiding the re-creation of clipping message window leads to a race condition when ClipCursor rect is reset then restored: the desktop process sends reset notification which may be processed after the clipping rect has already been restored.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 12 ++++++++++-- dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index f737a306a56..75e3167e5f9 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -471,7 +471,7 @@ BOOL CDECL X11DRV_ClipCursor( const RECT *clip ); * * Notification function called upon receiving a WM_X11DRV_CLIP_CURSOR. */ -LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd ) +LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) { struct x11drv_thread_data *data = x11drv_init_thread_data();
@@ -482,7 +482,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd ) HWND prev = clip_hwnd; clip_hwnd = new_clip_hwnd; if (prev || new_clip_hwnd) TRACE( "clip hwnd changed from %p to %p\n", prev, new_clip_hwnd ); - if (prev) SendNotifyMessageW( prev, WM_X11DRV_CLIP_CURSOR, 0, 0 ); + if (prev) SendNotifyMessageW( prev, WM_X11DRV_CLIP_CURSOR, (WPARAM)prev, 0 ); } else if (hwnd == data->clip_hwnd) /* this is a notification that clipping has been reset */ { @@ -499,6 +499,14 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd ) GetClipCursor( &clip ); X11DRV_ClipCursor( &clip ); } + else if (prev_clip_hwnd) + { + /* This is a notification send by the desktop window to an old + * dangling clip window. + */ + TRACE( "destroying old clip hwnd %p\n", prev_clip_hwnd ); + DestroyWindow( prev_clip_hwnd ); + } return 0; }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index fb97ec5a0d4..73aa78a17fc 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2727,7 +2727,7 @@ LRESULT CDECL X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) set_window_cursor( x11drv_thread_data()->clip_window, (HCURSOR)lp ); return 0; case WM_X11DRV_CLIP_CURSOR: - return clip_cursor_notify( hwnd, (HWND)lp ); + return clip_cursor_notify( hwnd, (HWND)wp, (HWND)lp ); default: FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp ); return 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0d3695bdcf8..aa01f2c3830 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -620,7 +620,7 @@ extern void X11DRV_InitClipboard(void) DECLSPEC_HIDDEN; extern void CDECL X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; -extern LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd ) 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 BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; -- 2.23.0.rc1