Cursor (both the cursor itself and its visibility) are per thread on Windows and Wine's implementation outside of winex11.drv seem to agree with that. Using global cursor variable in winex11.drv leads to wrong cursor being set or set visible for a window when the app sets a cursor from the other thread.
From: Paul Gofman gofmanp@gmail.com
--- dlls/winex11.drv/mouse.c | 9 +++++---- dlls/winex11.drv/window.c | 4 ++-- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 9e005881170..8250fba2a1f 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -124,7 +124,6 @@ static const UINT button_up_data[NB_BUTTONS] = 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; @@ -1499,11 +1498,13 @@ void X11DRV_DestroyCursorIcon( HCURSOR handle ) */ void X11DRV_SetCursor( HCURSOR handle ) { - if (InterlockedExchangePointer( (void **)&last_cursor, handle ) != handle || - NtGetTickCount() - last_cursor_change > 100) + struct x11drv_thread_data *thread_data = x11drv_init_thread_data(); + + if (thread_data->last_cursor != handle || NtGetTickCount() - last_cursor_change > 100) { last_cursor_change = NtGetTickCount(); - if (cursor_window) send_notify_message( cursor_window, WM_X11DRV_SET_CURSOR, 0, (LPARAM)handle ); + thread_data->last_cursor = handle; + if (cursor_window) send_notify_message( cursor_window, WM_X11DRV_SET_CURSOR, 0, 0 ); } }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 3ebbee0856e..2bfa9682ffb 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3000,10 +3000,10 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) { Window win = data->whole_window; release_win_data( data ); - if (win) set_window_cursor( win, (HCURSOR)lp ); + if (win) set_window_cursor( win, x11drv_thread_data()->last_cursor ); } else if (hwnd == x11drv_thread_data()->clip_hwnd) - set_window_cursor( x11drv_thread_data()->clip_window, (HCURSOR)lp ); + set_window_cursor( x11drv_thread_data()->clip_window, x11drv_thread_data()->last_cursor ); return 0; case WM_X11DRV_CLIP_CURSOR_NOTIFY: return clip_cursor_notify( hwnd, (HWND)wp, (HWND)lp ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index ad4a520af13..077b409b5ef 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -378,6 +378,7 @@ struct x11drv_thread_data Window clip_window; /* window used for cursor clipping */ HWND clip_hwnd; /* message window stored in desktop while clipping is active */ DWORD clip_reset; /* time when clipping was last reset */ + HCURSOR last_cursor; /* last cursor set */ #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } xi2_state; /* XInput2 state */ void *xi2_devices; /* list of XInput2 devices (valid when state is enabled) */
Cursor (both the cursor itself and its visibility) are per thread on Windows and Wine's implementation outside of winex11.drv seem to agree with that. Using global cursor variable in winex11.drv leads to wrong cursor being set or set visible for a window when the app sets a cursor from the other thread.
Actually it's per thread input. Making it per thread will give the wrong results for instance when parent and child windows belong to different threads.