mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2024-11-19 17:06:04 -07:00
winex11: Ignore focus changes during WM_STATE transitions.
When WM_STATE is being quickly updated, and depending on the WM we might receive transient focus changes, which will disrupt the Win32 state and make us randomly lose focus. Ignore them instead, when a window is being shown, and wait for WM_STATE to be updated and stable. We will eventually receive a WM_TAKE_FOCUS / FocusIn event *after* a window has been shown. When a window is hidden or minimized, we will receive the FocusOut event during the WM_STATE transition, and can safely handle it in this case, as we should have done all the Win32 side effects and have changed the foreground window already. When there's no window state change pending, the focus change event is unexpected, coming from the user or WM, and we handle it normally.
This commit is contained in:
parent
700ee59470
commit
0dc7e40468
Notes:
Alexandre Julliard
2024-11-15 22:25:28 +01:00
Approved-by: Alexandre Julliard (@julliard) Merge-Request: https://gitlab.winehq.org/wine/wine/merge_requests/6822
3 changed files with 52 additions and 12 deletions
|
@ -761,12 +761,20 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
|
|||
}
|
||||
else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
|
||||
{
|
||||
HWND last_focus = x11drv_thread_data()->last_focus;
|
||||
HWND last_focus = x11drv_thread_data()->last_focus, foreground = NtUserGetForegroundWindow();
|
||||
|
||||
TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
|
||||
hwnd, NtUserIsWindowEnabled(hwnd), NtUserIsWindowVisible(hwnd),
|
||||
(int)NtUserGetWindowLongW(hwnd, GWL_STYLE),
|
||||
get_focus(), get_active_window(), NtUserGetForegroundWindow(), last_focus );
|
||||
if (window_has_pending_wm_state( hwnd, -1 ))
|
||||
{
|
||||
WARN( "Ignoring window %p/%lx WM_TAKE_FOCUS serial %lu, event_time %ld, foreground %p during WM_STATE change\n",
|
||||
hwnd, event->window, event->serial, event_time, foreground );
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE( "window %p/%lx WM_TAKE_FOCUS serial %lu, event_time %ld, foreground %p\n", hwnd, event->window,
|
||||
event->serial, event_time, foreground );
|
||||
TRACE( " enabled %u, visible %u, style %#x, focus %p, active %p, last %p\n",
|
||||
NtUserIsWindowEnabled( hwnd ), NtUserIsWindowVisible( hwnd ), (int)NtUserGetWindowLongW( hwnd, GWL_STYLE ),
|
||||
get_focus(), get_active_window(), last_focus );
|
||||
|
||||
if (can_activate_window(hwnd))
|
||||
{
|
||||
|
@ -783,7 +791,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
|
|||
}
|
||||
else if (hwnd == NtUserGetDesktopWindow())
|
||||
{
|
||||
hwnd = NtUserGetForegroundWindow();
|
||||
hwnd = foreground;
|
||||
if (!hwnd) hwnd = last_focus;
|
||||
if (!hwnd) hwnd = NtUserGetDesktopWindow();
|
||||
set_focus( event->display, hwnd, event_time );
|
||||
|
@ -845,14 +853,23 @@ BOOL is_current_process_focused(void)
|
|||
*/
|
||||
static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
|
||||
{
|
||||
HWND foreground = NtUserGetForegroundWindow();
|
||||
XFocusChangeEvent *event = &xev->xfocus;
|
||||
BOOL was_grabbed;
|
||||
|
||||
if (event->detail == NotifyPointer) return FALSE;
|
||||
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 (window_has_pending_wm_state( hwnd, -1 ))
|
||||
{
|
||||
WARN( "Ignoring window %p/%lx FocusIn serial %lu, detail %s, mode %s, foreground %p during WM_STATE change\n",
|
||||
hwnd, event->window, event->serial, focus_details[event->detail], focus_modes[event->mode], foreground );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TRACE( "window %p/%lx FocusIn serial %lu, detail %s, mode %s, foreground %p\n", hwnd, event->window,
|
||||
event->serial, focus_details[event->detail], focus_modes[event->mode], foreground );
|
||||
|
||||
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()) reapply_cursor_clipping();
|
||||
if (hwnd == NtUserGetDesktopWindow()) return FALSE;
|
||||
|
@ -921,10 +938,9 @@ static void focus_out( Display *display , HWND hwnd )
|
|||
*/
|
||||
static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev )
|
||||
{
|
||||
HWND foreground = NtUserGetForegroundWindow();
|
||||
XFocusChangeEvent *event = &xev->xfocus;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!hwnd && event->window == x11drv_thread_data()->clip_window)
|
||||
|
@ -938,6 +954,16 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev )
|
|||
}
|
||||
if (!hwnd) return FALSE;
|
||||
|
||||
if (window_has_pending_wm_state( hwnd, NormalState )) /* ignore FocusOut only if the window is being shown */
|
||||
{
|
||||
WARN( "Ignoring window %p/%lx FocusOut serial %lu, detail %s, mode %s, foreground %p during WM_STATE change\n",
|
||||
hwnd, event->window, event->serial, focus_details[event->detail], focus_modes[event->mode], foreground );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TRACE( "window %p/%lx FocusOut serial %lu, detail %s, mode %s, foreground %p\n", hwnd, event->window,
|
||||
event->serial, focus_details[event->detail], focus_modes[event->mode], foreground );
|
||||
|
||||
/* 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();
|
||||
|
|
|
@ -1418,8 +1418,8 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state )
|
|||
|
||||
data->pending_state.wm_state = new_state;
|
||||
data->wm_state_serial = NextRequest( data->display );
|
||||
TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu\n", data->hwnd, data->whole_window,
|
||||
old_state, new_state, data->wm_state_serial );
|
||||
TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu, foreground %p\n", data->hwnd, data->whole_window,
|
||||
old_state, new_state, data->wm_state_serial, NtUserGetForegroundWindow() );
|
||||
|
||||
switch (MAKELONG(old_state, new_state))
|
||||
{
|
||||
|
@ -1593,6 +1593,19 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial
|
|||
*expect_serial = 0;
|
||||
}
|
||||
|
||||
BOOL window_has_pending_wm_state( HWND hwnd, UINT state )
|
||||
{
|
||||
struct x11drv_win_data *data;
|
||||
BOOL pending;
|
||||
|
||||
if (!(data = get_win_data( hwnd ))) return FALSE;
|
||||
if (state != -1 && data->pending_state.wm_state != state) pending = FALSE;
|
||||
else pending = !!data->wm_state_serial;
|
||||
release_win_data( data );
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* make_window_embedded
|
||||
*/
|
||||
|
|
|
@ -659,6 +659,7 @@ extern void set_gl_drawable_parent( HWND hwnd, HWND parent );
|
|||
extern void destroy_gl_drawable( HWND hwnd );
|
||||
extern void destroy_vk_surface( HWND hwnd );
|
||||
|
||||
extern BOOL window_has_pending_wm_state( HWND hwnd, UINT state );
|
||||
extern void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value );
|
||||
extern void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value );
|
||||
extern void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *rect );
|
||||
|
|
Loading…
Reference in a new issue