[PATCH] winex11.drv: Forward clip_fullscreen_window to foreground thread.

Rémi Bernon rbernon at codeweavers.com
Mon Sep 28 05:57:38 CDT 2020


If the current thread isn't the foreground thread. Otherwise we may
clip the cursor in the wrong thread, that isn't expecting mouse messages
or may not be checking its messages.

Red Faction has some race condition where this can happen for instance,
with clip_fullscreen_window called from X11DRV_DisplayDevices_Update
callback in a background thread. This thread starts clipping the cursor,
and the foreground thread isn't receiving MotionNotify events anymore.

We use WM_X11DRV_CLIP_CURSOR message here, with wparam set to TRUE if
clip_fullscreen_window was the origin, and lparam being the reset param
in that case. The lparam / wparam were only previously used when sending
the WM_X11DRV_CLIP_CURSOR message to clip message windows or to the
desktop window, so it should be safe to change their semantic in that
case.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

I couldn't actually reproduce the issue with Wine and Red Faction, but
it happens with Proton. I believe however that the race condition exists
nonetheless and we should clip the cursor in the foreground thread, in
the fullscreen case too, like it is done in X11DRV_ClipCursor.

 dlls/winex11.drv/mouse.c  | 31 ++++++++++++++++++++++---------
 dlls/winex11.drv/window.c |  2 +-
 dlls/winex11.drv/x11drv.h |  2 +-
 3 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 68e24a320af..4c34aea7d77 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -500,15 +500,16 @@ BOOL CDECL X11DRV_ClipCursor( const RECT *clip );
  *
  * Notification function called upon receiving a WM_X11DRV_CLIP_CURSOR.
  */
-LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd )
+LRESULT clip_cursor_notify( HWND hwnd, WPARAM wparam, LPARAM lparam )
 {
     struct x11drv_thread_data *data = x11drv_init_thread_data();
+    HWND prev_clip_hwnd;
+    RECT clip;
 
     if (hwnd == GetDesktopWindow())  /* change the clip window stored in the desktop process */
     {
         static HWND clip_hwnd;
-
-        HWND prev = clip_hwnd;
+        HWND prev = clip_hwnd, new_clip_hwnd = (HWND)lparam;
         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, (WPARAM)prev, 0 );
@@ -523,12 +524,14 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd )
     }
     else if (hwnd == GetForegroundWindow())  /* request to clip */
     {
-        RECT clip;
-
-        GetClipCursor( &clip );
-        X11DRV_ClipCursor( &clip );
+        if (wparam) clip_fullscreen_window( hwnd, lparam );
+        else
+        {
+            GetClipCursor( &clip );
+            X11DRV_ClipCursor( &clip );
+        }
     }
-    else if (prev_clip_hwnd)
+    else if ((prev_clip_hwnd = (HWND)wparam))
     {
         /* This is a notification send by the desktop window to an old
          * dangling clip window.
@@ -552,6 +555,16 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset )
     HMONITOR monitor;
     DWORD style;
     BOOL fullscreen;
+    DWORD tid, pid;
+
+    /* forward request to the window thread if it's in a different thread */
+    if ((tid = GetWindowThreadProcessId( hwnd, &pid )) &&
+         tid != GetCurrentThreadId() && pid == GetCurrentProcessId())
+    {
+        TRACE( "forwarding clip request to %p\n", hwnd );
+        SendNotifyMessageW( hwnd, WM_X11DRV_CLIP_CURSOR, TRUE, reset );
+        return TRUE;
+    }
 
     if (hwnd == GetDesktopWindow()) return FALSE;
     style = GetWindowLongW( hwnd, GWL_STYLE );
@@ -1539,7 +1552,7 @@ BOOL CDECL X11DRV_ClipCursor( LPCRECT clip )
         if (tid && tid != GetCurrentThreadId() && pid == GetCurrentProcessId())
         {
             TRACE( "forwarding clip request to %p\n", foreground );
-            SendNotifyMessageW( foreground, WM_X11DRV_CLIP_CURSOR, 0, 0 );
+            SendNotifyMessageW( foreground, WM_X11DRV_CLIP_CURSOR, FALSE, FALSE );
             return TRUE;
         }
 
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 455957264bc..fd2543c3969 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -2782,7 +2782,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)wp, (HWND)lp );
+        return clip_cursor_notify( hwnd, wp, 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 a92c738a685..d589c37c4fd 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -626,7 +626,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 prev_clip_hwnd, HWND new_clip_hwnd ) DECLSPEC_HIDDEN;
+extern LRESULT clip_cursor_notify( HWND hwnd, WPARAM wparam, LPARAM lparam ) 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.28.0




More information about the wine-devel mailing list