winex11: Simplify the cursor clipping retry mechanism.

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
This commit is contained in:
Rémi Bernon 2023-06-15 08:53:08 +02:00 committed by Alexandre Julliard
parent 89415925b7
commit 1f90d03b78
3 changed files with 21 additions and 62 deletions

View file

@ -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;

View file

@ -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 );
}

View file

@ -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;