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(a)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 8d1dc5e35d7..15e5c04a41e 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -502,7 +502,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();
@@ -513,7 +513,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 */
{
@@ -530,6 +530,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 d4e476facb2..9ac83e57b1a 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -621,7 +621,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 void retry_grab_clipping_window(void) DECLSPEC_HIDDEN;
--
2.23.0