Reverting things a bit to how it was before the move to win32u, and avoid modifying the Win32 clipping rect when handling host-specific events like keyboard grabs. This temporarily releases the winex11 cursor grabs, while keeping the Win32 state unchanged and unnotified, and restores the state later when either keyboard is ungrabbed or the virtual desktop window is focused again.
From: Rémi Bernon rbernon@codeweavers.com
This might be the case when in virtual desktop mode for instance, where we don't change Wine foreground window when the virtual desktop is focused out.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55046 --- dlls/winex11.drv/event.c | 25 +++++++++++++------------ dlls/winex11.drv/mouse.c | 6 ++++-- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 55709a0bc5f..6bdf9bcbf4f 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -746,6 +746,18 @@ static const char * const focus_modes[] = "NotifyWhileGrabbed" };
+BOOL is_current_process_focused(void) +{ + Display *display = x11drv_thread_data()->display; + Window focus; + int revert; + HWND hwnd; + + XGetInputFocus( display, &focus, &revert ); + if (focus && !XFindContext( display, focus, winContext, (char **)&hwnd )) return TRUE; + return FALSE; +} + /********************************************************************** * X11DRV_FocusIn */ @@ -799,10 +811,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) */ static void focus_out( Display *display , HWND hwnd ) { - HWND hwnd_tmp; - Window focus_win; - int revert; - if (xim_in_compose_mode()) return;
x11drv_thread_data()->last_focus = hwnd; @@ -820,14 +828,7 @@ static void focus_out( Display *display , HWND hwnd ) /* don't reset the foreground window, if the window which is getting the focus is a Wine window */
- XGetInputFocus( display, &focus_win, &revert ); - if (focus_win) - { - if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0) - focus_win = 0; - } - - if (!focus_win) + if (!is_current_process_focused()) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 46d26726264..faf0c26021d 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -359,8 +359,10 @@ static BOOL grab_clipping_window( const RECT *clip ) HCURSOR cursor; POINT pos;
- if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) == GetCurrentThreadId()) - return TRUE; /* don't clip in the desktop process */ + /* don't clip in the desktop process */ + if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) == GetCurrentThreadId()) return TRUE; + /* don't clip the cursor if the X input focus is on another process window */ + if (!is_current_process_focused()) return TRUE;
if (!data) return FALSE; if (!(clip_window = init_clip_window())) return TRUE; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 8827d0cc0c2..0abf30a4c58 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -676,6 +676,7 @@ extern XContext winContext DECLSPEC_HIDDEN; /* X context to associate an X cursor to a Win32 cursor handle */ extern XContext cursor_context DECLSPEC_HIDDEN;
+extern BOOL is_current_process_focused(void) DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN;
From: Rémi Bernon rbernon@codeweavers.com
If the focus changes between Wine windows, the wineserver logic will decide to reset the clipping rectangle. However winex11 also needs to support the case when focus changes to a host window, in virtual desktop mode, and in this case the foreground window doesn't actually change.
To fix this, in virtual desktop mode, release the cursor on focus out events, and reapply the cursor clipping rect when the virtual desktop window is focused again.
We can use the same logic on NotifyGrab events, when the WM grabs the keyboard, and later reapply the Wine clipping rect when we are notified about the keyboard ungrab.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55046 --- dlls/winex11.drv/event.c | 60 ++++++++++----------------------------- dlls/winex11.drv/mouse.c | 22 ++++---------- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 21 insertions(+), 62 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 6bdf9bcbf4f..c3c8d9a4070 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -764,31 +764,23 @@ BOOL is_current_process_focused(void) static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) { XFocusChangeEvent *event = &xev->xfocus; + BOOL was_grabbed;
if (!hwnd) return FALSE;
TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] );
if (event->detail == NotifyPointer) return FALSE; + /* when focusing in the virtual desktop window, re-apply the cursor clipping rect */ + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); if (hwnd == NtUserGetDesktopWindow()) return FALSE;
- switch (event->mode) - { - 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; - retry_grab_clipping_window(); - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - } + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; + if (was_grabbed > keyboard_grabbed) retry_grab_clipping_window(); + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
xim_set_focus( hwnd, TRUE );
@@ -816,11 +808,7 @@ static void focus_out( Display *display , HWND hwnd ) x11drv_thread_data()->last_focus = hwnd; xim_set_focus( hwnd, FALSE );
- if (is_virtual_desktop()) - { - if (hwnd == NtUserGetDesktopWindow()) NtUserClipCursor( NULL ); - return; - } + if (is_virtual_desktop()) return; if (hwnd != NtUserGetForegroundWindow()) return; if (!(NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE)) send_message( hwnd, WM_CANCELMODE, 0, 0 ); @@ -860,29 +848,11 @@ 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 */ - keyboard_grabbed = FALSE; - return FALSE; - case NotifyNormal: - keyboard_grabbed = FALSE; - break; - case NotifyWhileGrabbed: - keyboard_grabbed = TRUE; - 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 */ - } + /* in virtual desktop mode or when keyboard is grabbed, release any cursor grab but keep the clipping rect */ + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; + if (is_virtual_desktop() || keyboard_grabbed) ungrab_clipping_window(); + /* 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; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index faf0c26021d..7eb08d242eb 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -123,9 +123,6 @@ static const UINT button_up_data[NB_BUTTONS] =
XContext cursor_context = 0;
-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 );
@@ -375,15 +372,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 = NtUserGetForegroundWindow(); - last_clip_rect = *clip; return FALSE; } - else - { - last_clip_refused = FALSE; - }
/* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) enable_xinput2(); @@ -445,7 +435,7 @@ static BOOL grab_clipping_window( const RECT *clip ) * * Release the pointer grab on the clip window. */ -static void ungrab_clipping_window(void) +void ungrab_clipping_window(void) { struct x11drv_thread_data *data = x11drv_init_thread_data(); Window clip_window = init_clip_window(); @@ -464,15 +454,13 @@ static void ungrab_clipping_window(void) /*********************************************************************** * 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. + * Restore the current clip rectangle. */ void retry_grab_clipping_window(void) { - if (clipping_cursor) - NtUserClipCursor( &clip_rect ); - else if (last_clip_refused && NtUserGetForegroundWindow() == last_clip_foreground_window) - NtUserClipCursor( &last_clip_rect ); + RECT rect; + NtUserGetClipCursor( &rect ); + NtUserClipCursor( &rect ); }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0abf30a4c58..26b4ffd9a1d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -680,6 +680,7 @@ extern BOOL is_current_process_focused(void) DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; +extern void ungrab_clipping_window(void) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ProcessEvents( DWORD mask ) DECLSPEC_HIDDEN;