Mutter and related window managers use a parent X window frame, and temporarily unmap windows when their decoration is switched on/off.
This triggers an Unmap > FocusOut > Reparent > Map > FocusIn event sequence, which we need to ignore in order to keep focus unchanged.
If we were wrong about it, the WM_STATE property should then change as well, and we will eventually post a WM_WINE_WINDOW_STATE_CHANGED message again and have another chance at changing the foreground window.
From: Rémi Bernon rbernon@codeweavers.com
Mutter and related window managers use a parent X window frame, and temporarily unmap windows when their decoration is switched on/off.
This triggers an Unmap > FocusOut > Reparent > Map > FocusIn event sequence, which we need to ignore in order to keep focus unchanged.
If we were wrong about it, the WM_STATE property should then change as well, and we will eventually post a WM_WINE_WINDOW_STATE_CHANGED message again and have another chance at changing the foreground window. --- dlls/winex11.drv/event.c | 20 ++++++++++++++++++++ dlls/winex11.drv/window.c | 16 +++++++++++++++- dlls/winex11.drv/x11drv.h | 2 ++ 3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 9385d2a2ec4..d2cf9e55977 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -946,6 +946,12 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) hwnd, event->window, event->serial, focus_details[event->detail], focus_modes[event->mode], foreground ); return FALSE; } + if (!is_virtual_desktop() && window_is_reparenting( hwnd )) /* ignore FocusOut only if the window is being reparented */ + { + WARN( "Ignoring window %p/%lx FocusOut serial %lu, detail %s, mode %s, foreground %p during reparenting\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 ); @@ -1025,6 +1031,11 @@ static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event ) if (event->xany.window == x11drv_thread_data()->clip_window) return TRUE;
if (!(data = get_win_data( hwnd ))) return FALSE; + if (data->reparenting) + { + TRACE( "window %p/%lx has been reparented\n", data->hwnd, data->whole_window ); + data->reparenting = 0; + }
if (!data->managed && !data->embedded && data->desired_state.wm_state != WithdrawnState) { @@ -1042,6 +1053,15 @@ static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event ) */ static BOOL X11DRV_UnmapNotify( HWND hwnd, XEvent *event ) { + struct x11drv_win_data *data; + + if (!(data = get_win_data( hwnd ))) return FALSE; + if (data->managed && !data->wm_state_serial && data->current_state.wm_state == NormalState) + { + WARN( "window %p/%lx unexpectedly unmapped, assuming reparenting\n", data->hwnd, data->whole_window ); + data->reparenting = 1; + } + release_win_data( data ); return TRUE; }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index f271e55200a..4420c2bc062 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1725,7 +1725,7 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd,
if (!(old_foreground = NtUserGetForegroundWindow())) old_foreground = NtUserGetDesktopWindow(); if (!is_virtual_desktop() && NtUserGetWindowThread( old_foreground, NULL ) == GetCurrentThreadId() && - !window_has_pending_wm_state( old_foreground, NormalState ) && + !window_has_pending_wm_state( old_foreground, NormalState ) && !window_is_reparenting( old_foreground ) && !thread_data->net_active_window_serial) { *foreground = hwnd_from_window( thread_data->display, thread_data->current_state.net_active_window ); @@ -1790,6 +1790,7 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, current, expected, prefix, received, reason )) return; data->current_state.activate = data->pending_state.activate; + data->reparenting = 0;
/* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); @@ -1964,6 +1965,18 @@ void set_net_active_window( HWND hwnd, HWND previous ) XFlush( data->display ); }
+BOOL window_is_reparenting( HWND hwnd ) +{ + struct x11drv_win_data *data; + BOOL pending; + + if (!(data = get_win_data( hwnd ))) return FALSE; + pending = !!data->reparenting; + release_win_data( data ); + + return pending; +} + BOOL window_has_pending_wm_state( HWND hwnd, UINT state ) { struct x11drv_win_data *data; @@ -2415,6 +2428,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des data->net_wm_state_serial = 0; data->mwm_hints_serial = 0; data->configure_serial = 0; + data->reparenting = 0;
if (data->xic) { diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 24a3d860ab4..388ab771452 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -658,6 +658,7 @@ struct x11drv_win_data UINT is_fullscreen : 1; /* is the window visible rect fullscreen */ UINT is_offscreen : 1; /* has been moved offscreen by the window manager */ UINT parent_invalid : 1; /* is the parent host window possibly invalid */ + UINT reparenting : 1; /* window is being reparented, likely from a decoration change */ Window embedder; /* window id of embedder */ Pixmap icon_pixmap; Pixmap icon_mask; @@ -685,6 +686,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_is_reparenting( HWND hwnd ); extern BOOL window_should_take_focus( HWND hwnd, Time time ); 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, Time time );