This workarounds KWin and Mutter window manager bugs/fanciness. The KWin bug is with maximized windows getting broken after sending a configure request, and which fix has been merged upstream already. The Mutter issue is unlikely to be fixed upstream, at least not its specific behavior, and we would instead better handle it as well as we can.
-- v5: winex11: Workaround KWin bug with maximized windows. winex11: Set _NET_WM_USER_TIME to 0 to implement SWP_NOACTIVATE. winex11: Keep track of the SWP flags to be used when mapping windows. winex11: Keep _NET_WM_USER_TIME on the individual windows. winex11: Workaround Mutter bug when changing decorations. winex11: Update _MOTIF_WM_HINTS after _NET_WM_STATE and config. winex11: Avoid creating windows with override-redirect flag set. winex11: Track _MOTIF_WM_HINTS property in the state tracker. winex11: Introduce a new handle_state_change helper. winex11: Ignore transient state changes in other processes. winex11: Give focus to the expected window when unampping with focus. winex11: Use the current state when deciding how to reply to WM_TAKE_FOCUS. winex11: Use the state tracker for the desktop window _NET_WM_STATE.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 49 +++++++-------------------------------- 1 file changed, 9 insertions(+), 40 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 9c3b0b8602d..422f1ea5b5b 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1125,35 +1125,6 @@ void update_user_time( Time time ) XUnlockDisplay( gdi_display ); }
-static void update_desktop_fullscreen( Display *display ) -{ - XEvent xev; - - if (!is_virtual_desktop()) return; - - xev.xclient.type = ClientMessage; - xev.xclient.window = root_window; - xev.xclient.message_type = x11drv_atom(_NET_WM_STATE); - xev.xclient.serial = 0; - xev.xclient.display = display; - xev.xclient.send_event = True; - xev.xclient.format = 32; - xev.xclient.data.l[0] = is_desktop_fullscreen() ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; - xev.xclient.data.l[1] = x11drv_atom(_NET_WM_STATE_FULLSCREEN); - xev.xclient.data.l[2] = 0; - xev.xclient.data.l[3] = 1; - - TRACE("action=%li\n", xev.xclient.data.l[0]); - - XSendEvent( display, DefaultRootWindow(display), False, - SubstructureRedirectMask | SubstructureNotifyMask, &xev ); - - xev.xclient.data.l[1] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT); - xev.xclient.data.l[2] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ); - XSendEvent( display, DefaultRootWindow(display), False, - SubstructureRedirectMask | SubstructureNotifyMask, &xev ); -} - /* Update _NET_WM_FULLSCREEN_MONITORS when _NET_WM_STATE_FULLSCREEN is set to support fullscreen * windows spanning multiple monitors */ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) @@ -1204,7 +1175,7 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) xev.xclient.format = 32; xev.xclient.data.l[4] = 1; memcpy( xev.xclient.data.l, monitors, sizeof(monitors) ); - XSendEvent( data->display, root_window, False, + XSendEvent( data->display, DefaultRootWindow( data->display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); } data->net_wm_fullscreen_monitors_set = TRUE; @@ -1268,7 +1239,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat data->net_wm_state_serial = NextRequest( data->display ); TRACE( "window %p/%lx, requesting _NET_WM_STATE %#x serial %lu\n", data->hwnd, data->whole_window, data->pending_state.net_wm_state, data->net_wm_state_serial ); - XSendEvent( data->display, root_window, False, + XSendEvent( data->display, DefaultRootWindow( data->display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); } } @@ -1338,18 +1309,21 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec */ static void update_net_wm_states( struct x11drv_win_data *data ) { + static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style, ex_style, new_state = 0;
if (!data->managed || data->embedded) return; if (data->whole_window == root_window) { - update_desktop_fullscreen(data->display); + if (!is_virtual_desktop()) return; + new_state = is_desktop_fullscreen() ? fullscreen_mask : 0; + window_set_net_wm_state( data, new_state ); return; }
style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); if (style & WS_MINIMIZE) - new_state |= data->desired_state.net_wm_state & ((1 << NET_WM_STATE_FULLSCREEN)|(1 << NET_WM_STATE_MAXIMIZED)); + new_state |= data->desired_state.net_wm_state & fullscreen_mask; if (data->is_fullscreen) { if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) @@ -2310,6 +2284,7 @@ BOOL X11DRV_DestroyNotify( HWND hwnd, XEvent *event ) /* initialize the desktop window id in the desktop manager process */ static BOOL create_desktop_win_data( Window win, HWND hwnd ) { + static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); struct x11drv_thread_data *thread_data = x11drv_thread_data(); Display *display = thread_data->display; struct x11drv_win_data *data; @@ -2319,6 +2294,7 @@ static BOOL create_desktop_win_data( Window win, HWND hwnd ) data->managed = TRUE; NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)win ); set_initial_wm_hints( display, win ); + if (is_desktop_fullscreen()) window_set_net_wm_state( data, fullscreen_mask ); release_win_data( data ); if (thread_data->clip_window) XReparentWindow( display, thread_data->clip_window, win, 0, 0 ); return TRUE; @@ -2363,13 +2339,6 @@ void X11DRV_SetDesktopWindow( HWND hwnd ) ERR( "Failed to create virtual desktop window data\n" ); root_window = DefaultRootWindow( gdi_display ); } - else if (is_desktop_fullscreen()) - { - Display *display = x11drv_thread_data()->display; - TRACE("setting desktop to fullscreen\n"); - XChangeProperty( display, root_window, x11drv_atom(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace, - (unsigned char*)&x11drv_atom(_NET_WM_STATE_FULLSCREEN), 1 ); - } } else {
From: Rémi Bernon rbernon@codeweavers.com
The Win32 state might not have been updated with the latest X11 state, and we want to take future updates into account when deciding to accept focus or not.
At this point, and because we ignore WM_TAKE_FOCUS when a state change is pending, all the states are the same, we use current_state as the logic is to accept focus or not according to our current understanding of the X state. --- dlls/winex11.drv/event.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index cf9fd96a2fb..30b49e6ccfd 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -595,8 +595,18 @@ DWORD EVENT_x11_time_to_win32_time(Time time) static inline BOOL can_activate_window( HWND hwnd ) { LONG style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + struct x11drv_win_data *data; RECT rect;
+ if ((data = get_win_data( hwnd ))) + { + style = style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); + if (data->current_state.wm_state != WithdrawnState) style |= WS_VISIBLE; + if (data->current_state.wm_state == IconicState) style |= WS_MINIMIZE; + if (data->current_state.net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)) style |= WS_MAXIMIZE; + release_win_data( data ); + } + if (!(style & WS_VISIBLE)) return FALSE; if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; if (style & WS_MINIMIZE) return FALSE;
From: Rémi Bernon rbernon@codeweavers.com
SetForegroundWindow calls aren't reflected to the X11 side, and we're sometimes becoming inconsistent between the X focused window and the Win32 foreground window.
When a window gets unmapped, another one may receive WM_TAKE_FOCUS, and in the next change we want to rely on the foreground window activation time to make a decision on whether the foreground window is temporarily unmapping itself. It needs to be the same as the window with X input focus, or we won't know which window activation time to check. --- dlls/winex11.drv/event.c | 10 ++++++++-- dlls/winex11.drv/window.c | 8 ++++++++ dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 30b49e6ccfd..ec714d144fb 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -849,10 +849,13 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) { HWND foreground = NtUserGetForegroundWindow(); XFocusChangeEvent *event = &xev->xfocus; + struct x11drv_win_data *data; BOOL was_grabbed;
if (event->detail == NotifyPointer) return FALSE; - if (!hwnd) return FALSE; + if (!(data = get_win_data( hwnd ))) return FALSE; + data->has_focus = 1; + release_win_data( data );
if (window_has_pending_wm_state( hwnd, -1 )) { @@ -934,6 +937,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) { HWND foreground = NtUserGetForegroundWindow(); XFocusChangeEvent *event = &xev->xfocus; + struct x11drv_win_data *data;
if (event->detail == NotifyPointer) { @@ -946,7 +950,9 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) } return TRUE; } - if (!hwnd) return FALSE; + if (!(data = get_win_data( hwnd ))) return FALSE; + data->has_focus = 0; + release_win_data( data );
if (window_has_pending_wm_state( hwnd, NormalState )) /* ignore FocusOut only if the window is being shown */ { diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 422f1ea5b5b..0d90707eb1c 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1406,6 +1406,7 @@ static void set_xembed_flags( struct x11drv_win_data *data, unsigned long flags static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) { UINT old_state = data->pending_state.wm_state; + HWND foreground = NtUserGetForegroundWindow();
data->desired_state.wm_state = new_state; if (!data->whole_window) return; /* no window, nothing to update */ @@ -1428,6 +1429,13 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) break; }
+ if (new_state != NormalState && data->has_focus && data->hwnd != foreground) + { + Window window = X11DRV_get_whole_window( foreground ); + WARN( "Inconsistent input focus, activating window %p/%lx\n", foreground, window ); + XSetInputFocus( data->display, window, RevertToParent, CurrentTime ); + } + 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, foreground %p\n", data->hwnd, data->whole_window, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 995df13e843..0f4e33dd9c0 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -635,6 +635,7 @@ struct x11drv_win_data UINT net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */ UINT is_fullscreen : 1; /* is the window visible rect fullscreen */ UINT parent_invalid : 1; /* is the parent host window possibly invalid */ + UINT has_focus : 1; /* does window have X input focus */ Window embedder; /* window id of embedder */ Pixmap icon_pixmap; Pixmap icon_mask;
From: Rémi Bernon rbernon@codeweavers.com
Setting a __wine_x11_focus_time window property to indicate the time of the last NormalState, -1 if the window is temporarily unmapping itself, or 0 if the window is not in a NormalState. --- dlls/winex11.drv/event.c | 6 +++--- dlls/winex11.drv/window.c | 13 ++++++++++++- dlls/winex11.drv/x11drv.h | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index ec714d144fb..3a57e3ea256 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -757,7 +757,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event ) { HWND last_focus = x11drv_thread_data()->last_focus, foreground = NtUserGetForegroundWindow();
- if (window_has_pending_wm_state( hwnd, -1 )) + if (window_has_pending_wm_state( hwnd, -1 ) || (hwnd != foreground && !window_should_take_focus( foreground, event_time ))) { 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 ); @@ -1231,7 +1231,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event )
if (!(data = get_win_data( hwnd ))) return; if (event->state == PropertyNewValue) value = get_window_wm_state( event->display, event->window ); - window_wm_state_notify( data, event->serial, value ); + window_wm_state_notify( data, event->serial, value, event->time ); release_win_data( data );
NtUserPostMessage( hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); @@ -1244,7 +1244,7 @@ static void handle_xembed_info_notify( HWND hwnd, XPropertyEvent *event )
if (!(data = get_win_data( hwnd ))) return; if (event->state == PropertyNewValue) value = get_window_xembed_info( event->display, event->window ); - window_wm_state_notify( data, event->serial, value ? NormalState : WithdrawnState ); + window_wm_state_notify( data, event->serial, value ? NormalState : WithdrawnState, event->time ); release_win_data( data ); }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 0d90707eb1c..fca59bc0680 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -108,6 +108,8 @@ static const WCHAR whole_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','w','h','o','l','e','_','w','i','n','d','o','w',0}; static const WCHAR clip_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','c','l','i','p','_','w','i','n','d','o','w',0}; +static const WCHAR focus_time_prop[] = + {'_','_','w','i','n','e','_','x','1','1','_','f','o','c','u','s','_','t','i','m','e',0};
static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -1609,7 +1611,7 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, return *state_cmd || *config_cmd; }
-void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ) +void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value, Time time ) { UINT *desired = &data->desired_state.wm_state, *pending = &data->pending_state.wm_state, *current = &data->current_state.wm_state; unsigned long *expect_serial = &data->wm_state_serial; @@ -1646,6 +1648,9 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, window_set_wm_state( data, data->desired_state.wm_state ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); window_set_config( data, &data->desired_state.rect, FALSE ); + + if (data->current_state.wm_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); + else if (!data->wm_state_serial) NtUserRemoveProp( data->hwnd, focus_time_prop ); }
void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ) @@ -1730,6 +1735,12 @@ BOOL window_has_pending_wm_state( HWND hwnd, UINT state ) return pending; }
+BOOL window_should_take_focus( HWND hwnd, Time time ) +{ + Time focus_time = (UINT_PTR)NtUserGetProp( hwnd, focus_time_prop ); + return !focus_time || (int)(focus_time - time) < 0; +} + /*********************************************************************** * make_window_embedded */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0f4e33dd9c0..9cd471d547a 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -661,8 +661,9 @@ 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_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 ); +extern void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value, Time time ); 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 ); extern BOOL get_window_state_updates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 91 ++++++++++++++------------------------- 1 file changed, 33 insertions(+), 58 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index fca59bc0680..a6a887e5862 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1611,38 +1611,50 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, return *state_cmd || *config_cmd; }
-void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value, Time time ) +static BOOL handle_state_change( struct x11drv_win_data *data, unsigned long serial, unsigned long *expect_serial, UINT size, + const void *value, void *desired, void *pending, void *current, const char *expected, + const char *received, const char *reason ) { - UINT *desired = &data->desired_state.wm_state, *pending = &data->pending_state.wm_state, *current = &data->current_state.wm_state; - unsigned long *expect_serial = &data->wm_state_serial; - const char *reason = NULL, *expected, *received; - - received = wine_dbg_sprintf( "WM_STATE %#x/%lu", value, serial ); - expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : ""; - if (serial < *expect_serial) reason = "old "; - else if (!*expect_serial && *current == value) reason = "no-op "; - /* ignore Metacity/Mutter transient NormalState during WithdrawnState <-> IconicState transitions */ - else if (value == NormalState && *current + *pending == IconicState) reason = "transient "; + else if (!*expect_serial && !memcmp( current, value, size )) reason = "no-op ";
if (reason) { WARN( "Ignoring window %p/%lx %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - return; + return FALSE; }
if (!*expect_serial) reason = "unexpected "; - else if (*pending != value) reason = "mismatch "; + else if (memcmp( pending, value, size )) reason = "mismatch ";
if (!reason) TRACE( "window %p/%lx, %s%s\n", data->hwnd, data->whole_window, received, expected ); else { WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *desired = *pending = value; /* avoid requesting the same state again */ + /* avoid requesting the same state again */ + memcpy( desired, value, size ); + memcpy( pending, value, size ); }
- *current = value; + memcpy( current, value, size ); *expect_serial = 0; + return TRUE; +} + +void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value, Time time ) +{ + UINT *desired = &data->desired_state.wm_state, *pending = &data->pending_state.wm_state, *current = &data->current_state.wm_state; + unsigned long *expect_serial = &data->wm_state_serial; + const char *reason = NULL, *expected, *received; + + received = wine_dbg_sprintf( "WM_STATE %#x/%lu", value, serial ); + expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : ""; + /* ignore Metacity/Mutter transient NormalState during WithdrawnState <-> IconicState transitions */ + if (value == NormalState && *current + *pending == IconicState) reason = "transient "; + + if (!handle_state_change( data, serial, expect_serial, sizeof(value), &value, + desired, pending, current, expected, received, reason )) + return;
/* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state ); @@ -1657,32 +1669,14 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser { UINT *desired = &data->desired_state.net_wm_state, *pending = &data->pending_state.net_wm_state, *current = &data->current_state.net_wm_state; unsigned long *expect_serial = &data->net_wm_state_serial; - const char *reason = NULL, *expected, *received; + const char *expected, *received;
received = wine_dbg_sprintf( "_NET_WM_STATE %#x/%lu", value, serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : "";
- if (serial < *expect_serial) reason = "old "; - else if (!*expect_serial && *current == value) reason = "no-op "; - - if (reason) - { - WARN( "Ignoring window %p/%lx %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); + if (!handle_state_change( data, serial, expect_serial, sizeof(value), &value, + desired, pending, current, expected, received, NULL )) return; - } - - if (!*expect_serial) reason = "unexpected "; - else if (*pending != value) reason = "mismatch "; - - if (!reason) TRACE( "window %p/%lx, %s%s\n", data->hwnd, data->whole_window, received, expected ); - else - { - WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *desired = *pending = value; /* avoid requesting the same state again */ - } - - *current = value; - *expect_serial = 0;
/* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state ); @@ -1694,32 +1688,13 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial { RECT *desired = &data->desired_state.rect, *pending = &data->pending_state.rect, *current = &data->current_state.rect; unsigned long *expect_serial = &data->configure_serial; - const char *reason = NULL, *expected, *received; + const char *expected, *received;
received = wine_dbg_sprintf( "config %s/%lu", wine_dbgstr_rect(value), serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %s/%lu", wine_dbgstr_rect(pending), *expect_serial ) : "";
- if (serial < *expect_serial) reason = "old "; - else if (!*expect_serial && EqualRect( current, value )) reason = "no-op "; - - if (reason) - { - WARN( "Ignoring window %p/%lx %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - return; - } - - if (!*expect_serial) reason = "unexpected "; - else if (!EqualRect( pending, value )) reason = "mismatch "; - - if (!reason) TRACE( "window %p/%lx, %s%s\n", data->hwnd, data->whole_window, received, expected ); - else - { - WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *desired = *pending = *value; /* avoid requesting the same state again */ - } - - *current = *value; - *expect_serial = 0; + handle_state_change( data, serial, expect_serial, sizeof(*value), value, + desired, pending, current, expected, received, NULL ); }
BOOL window_has_pending_wm_state( HWND hwnd, UINT state )
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/event.c | 27 +++++++++++++++++++++++ dlls/winex11.drv/window.c | 45 +++++++++++++++++++++++++++++++++------ dlls/winex11.drv/x11drv.h | 4 ++++ 3 files changed, 69 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 3a57e3ea256..6df35c7f397 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1218,6 +1218,21 @@ static int get_window_xembed_info( Display *display, Window window ) return ret; }
+static void get_window_mwm_hints( Display *display, Window window, MwmHints *hints ) +{ + unsigned long count, remaining; + MwmHints *value; + int format; + Atom type; + + if (!XGetWindowProperty( display, window, x11drv_atom(_MOTIF_WM_HINTS), 0, 65535, False, x11drv_atom(_MOTIF_WM_HINTS), + &type, &format, &count, &remaining, (unsigned char **)&value )) + { + if (type == x11drv_atom(_MOTIF_WM_HINTS) && get_property_size( format, count ) >= sizeof(*value)) + *hints = *value; + XFree( value ); + } +}
/*********************************************************************** * handle_wm_state_notify @@ -1261,6 +1276,17 @@ static void handle_net_wm_state_notify( HWND hwnd, XPropertyEvent *event ) NtUserPostMessage( hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); }
+static void handle_mwm_hints_notify( HWND hwnd, XPropertyEvent *event ) +{ + struct x11drv_win_data *data; + MwmHints hints = {0}; + + if (!(data = get_win_data( hwnd ))) return; + if (event->state == PropertyNewValue) get_window_mwm_hints( event->display, event->window, &hints ); + window_mwm_hints_notify( data, event->serial, &hints ); + release_win_data( data ); +} + static void handle_net_supported_notify( XPropertyEvent *event ) { struct x11drv_thread_data *data = x11drv_thread_data(); @@ -1287,6 +1313,7 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event ); if (event->atom == x11drv_atom(_XEMBED_INFO)) handle_xembed_info_notify( hwnd, event ); if (event->atom == x11drv_atom(_NET_WM_STATE)) handle_net_wm_state_notify( hwnd, event ); + if (event->atom == x11drv_atom(_MOTIF_WM_HINTS)) handle_mwm_hints_notify( hwnd, event ); if (event->atom == x11drv_atom(_NET_SUPPORTED)) handle_net_supported_notify( event );
return TRUE; diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a6a887e5862..49785b9a416 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -53,7 +53,6 @@
#include "wine/debug.h" #include "wine/server.h" -#include "mwm.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv); WINE_DECLARE_DEBUG_CHANNEL(systray); @@ -111,6 +110,11 @@ static const WCHAR clip_window_prop[] = static const WCHAR focus_time_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','f','o','c','u','s','_','t','i','m','e',0};
+static const char *debugstr_mwm_hints( const MwmHints *hints ) +{ + return wine_dbg_sprintf( "%lx,%lx", hints->functions, hints->decorations ); +} + static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
static void host_window_add_ref( struct host_window *win ) @@ -887,6 +891,23 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) }
+static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints ) +{ + const MwmHints *old_hints = &data->pending_state.mwm_hints; + + data->desired_state.mwm_hints = *new_hints; + if (!data->whole_window) return; /* no window, nothing to update */ + if (!memcmp( old_hints, new_hints, sizeof(*new_hints) )) return; /* hints are the same, nothing to update */ + + data->pending_state.mwm_hints = *new_hints; + data->mwm_hints_serial = NextRequest( data->display ); + TRACE( "window %p/%lx, requesting _MOTIF_WM_HINTS %s serial %lu\n", data->hwnd, data->whole_window, + debugstr_mwm_hints(&data->pending_state.mwm_hints), data->mwm_hints_serial ); + XChangeProperty( data->display, data->whole_window, x11drv_atom(_MOTIF_WM_HINTS), x11drv_atom(_MOTIF_WM_HINTS), + 32, PropModeReplace, (unsigned char *)new_hints, sizeof(*new_hints) / sizeof(long) ); +} + + /*********************************************************************** * set_mwm_hints */ @@ -921,15 +942,12 @@ static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_sty } }
- TRACE( "%p setting mwm hints to %lx,%lx (style %x exstyle %x)\n", - data->hwnd, mwm_hints.decorations, mwm_hints.functions, style, ex_style ); - mwm_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; mwm_hints.input_mode = 0; mwm_hints.status = 0; - XChangeProperty( data->display, data->whole_window, x11drv_atom(_MOTIF_WM_HINTS), - x11drv_atom(_MOTIF_WM_HINTS), 32, PropModeReplace, - (unsigned char*)&mwm_hints, sizeof(mwm_hints)/sizeof(long) ); + TRACE( "%p setting mwm hints to %s (style %x exstyle %x)\n", + data->hwnd, debugstr_mwm_hints(&mwm_hints), style, ex_style ); + window_set_mwm_hints( data, &mwm_hints ); }
@@ -1684,6 +1702,19 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser window_set_config( data, &data->desired_state.rect, FALSE ); }
+void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *value ) +{ + MwmHints *desired = &data->desired_state.mwm_hints, *pending = &data->pending_state.mwm_hints, *current = &data->current_state.mwm_hints; + unsigned long *expect_serial = &data->mwm_hints_serial; + const char *expected, *received; + + received = wine_dbg_sprintf( "_MOTIF_WM_HINTS %s/%lu", debugstr_mwm_hints(value), serial ); + expected = *expect_serial ? wine_dbg_sprintf( ", expected %s/%lu", debugstr_mwm_hints(pending), *expect_serial ) : ""; + + handle_state_change( data, serial, expect_serial, sizeof(*value), value, + desired, pending, current, expected, received, NULL ); +} + void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *value ) { RECT *desired = &data->desired_state.rect, *pending = &data->pending_state.rect, *current = &data->current_state.rect; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 9cd471d547a..5c5607dfccd 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -67,6 +67,7 @@ typedef int Status; #include "unixlib.h" #include "wine/list.h" #include "wine/debug.h" +#include "mwm.h"
#define MAX_DASHLEN 16
@@ -610,6 +611,7 @@ struct window_state { UINT wm_state; UINT net_wm_state; + MwmHints mwm_hints; RECT rect; };
@@ -647,6 +649,7 @@ struct x11drv_win_data struct window_state current_state; /* window state tracking the current X11 state */ unsigned long wm_state_serial; /* serial of last pending WM_STATE request */ unsigned long net_wm_state_serial; /* serial of last pending _NET_WM_STATE request */ + unsigned long mwm_hints_serial; /* serial of last pending _MOTIF_WM_HINTS request */ unsigned long configure_serial; /* serial of last pending configure request */ };
@@ -665,6 +668,7 @@ 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 ); extern void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ); +extern void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *hints ); extern void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *rect ); extern BOOL get_window_state_updates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect );
From: Rémi Bernon rbernon@codeweavers.com
This confuses mutter, and it will later get confused in various ways if decorations are later added or removed on the window, even if the flag has been unset. Doing it the other way, changing the flag later on, when needed works fine.
This manifests with spurious IconicState WM_STATE change when mutter decides to try managing the window, but it also makes it randomly lose focus or even fail to map the window back on screen. --- dlls/winex11.drv/window.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 49785b9a416..2a8fd624fd5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2078,12 +2078,6 @@ static void create_whole_window( struct x11drv_win_data *data ) HRGN win_rgn; POINT pos;
- if (!data->managed && is_window_managed( data->hwnd, SWP_NOACTIVATE, &data->rects.window )) - { - TRACE( "making win %p/%lx managed\n", data->hwnd, data->whole_window ); - data->managed = TRUE; - } - if ((win_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 )) && NtUserGetWindowRgnEx( data->hwnd, win_rgn, 0 ) == ERROR) { @@ -2095,6 +2089,7 @@ static void create_whole_window( struct x11drv_win_data *data ) if (data->vis.visualid != default_visual.visualid) data->whole_colormap = XCreateColormap( data->display, root_window, data->vis.visual, AllocNone );
+ data->managed = TRUE; mask = get_window_attributes( data, &attr );
if (!(cx = data->rects.visible.right - data->rects.visible.left)) cx = 1; @@ -2111,6 +2106,7 @@ static void create_whole_window( struct x11drv_win_data *data ) data->pending_state.rect = data->current_state.rect; data->desired_state.rect = data->current_state.rect;
+ if (!is_window_managed( data->hwnd, SWP_NOACTIVATE, &data->rects.window )) data->managed = FALSE; x11drv_xinput2_enable( data->display, data->whole_window ); set_initial_wm_hints( data->display, data->whole_window ); set_wm_hints( data );
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57285 --- dlls/winex11.drv/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 2a8fd624fd5..955f0458c5b 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1785,9 +1785,9 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags ) }
set_size_hints( data, style ); - set_mwm_hints( data, style, ex_style ); update_net_wm_states( data ); window_set_config( data, &data->rects.visible, above ); + set_mwm_hints( data, style, ex_style ); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 955f0458c5b..8d80882deea 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -891,6 +891,8 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) }
+static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ); + static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints ) { const MwmHints *old_hints = &data->pending_state.mwm_hints; @@ -899,6 +901,30 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * if (!data->whole_window) return; /* no window, nothing to update */ if (!memcmp( old_hints, new_hints, sizeof(*new_hints) )) return; /* hints are the same, nothing to update */
+ /* When removing decorations, Mutter sends UnmapNotify, FocusOut, ReparentNotify, MapNotify, WM_TAKE_FOCUS + * event sequence. We won't receive any WM_STATE property changes and cannot use it to detect when it's done, + * so explicitly request the Unmap / Map sequence ourselves, so we can follow the WM_STATE property changes. + * + * When adding decorations to a window in NormalState, Mutter first iconifies the window, then restores it + * after the same sequence. It however sometimes fails to restore focus to the window. + * + * In both cases, we will receive _MOTIF_WM_HINT PropertyNotify change right away and will then be unable to + * handle the non-atomic event sequence that follows. + * + * Instead, explicitly request an unmap / map sequence ourselves and track the corresponding events, overriding + * the Mutter generated sequence, while achieving the same thing and getting WM_TAKE_FOCUS event when the + * window is mapped again. + */ + if (data->managed && data->pending_state.wm_state == NormalState && + !old_hints->decorations != !new_hints->decorations) + { + if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ + WARN( "window %p/%lx adds/removes decorations, remapping\n", data->hwnd, data->whole_window ); + NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 ); + window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, NormalState ); + } + data->pending_state.mwm_hints = *new_hints; data->mwm_hints_serial = NextRequest( data->display ); TRACE( "window %p/%lx, requesting _MOTIF_WM_HINTS %s serial %lu\n", data->hwnd, data->whole_window, @@ -1667,8 +1693,6 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial,
received = wine_dbg_sprintf( "WM_STATE %#x/%lu", value, serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : ""; - /* ignore Metacity/Mutter transient NormalState during WithdrawnState <-> IconicState transitions */ - if (value == NormalState && *current + *pending == IconicState) reason = "transient ";
if (!handle_state_change( data, serial, expect_serial, sizeof(value), &value, desired, pending, current, expected, received, reason )) @@ -1678,6 +1702,7 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, window_set_wm_state( data, data->desired_state.wm_state ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints );
if (data->current_state.wm_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); else if (!data->wm_state_serial) NtUserRemoveProp( data->hwnd, focus_time_prop ); @@ -1700,6 +1725,7 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser window_set_wm_state( data, data->desired_state.wm_state ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); }
void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *value )
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/window.c | 33 ++++----------------------------- dlls/winex11.drv/x11drv.h | 2 +- 5 files changed, 8 insertions(+), 33 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 6df35c7f397..70b09f4315e 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -701,7 +701,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
if (protocol == x11drv_atom(WM_DELETE_WINDOW)) { - update_user_time( event_time ); + update_user_time( event->display, event->window, event_time );
if (hwnd == NtUserGetDesktopWindow()) { diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 7b7371ed696..f24313852b5 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1340,7 +1340,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) TRACE_(key)("type %d, window %lx, state 0x%04x, keycode %u\n", event->type, event->window, event->state, event->keycode);
- if (event->type == KeyPress) update_user_time( event->time ); + if (event->type == KeyPress) update_user_time( event->display, event->window, event->time );
/* Clients should pass only KeyPress events to XmbLookupString */ if (xic && event->type == KeyPress) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 5f6b9f34d2a..f1a3a2ab516 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1547,7 +1547,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) input.mi.time = EVENT_x11_time_to_win32_time( event->time ); input.mi.dwExtraInfo = 0;
- update_user_time( event->time ); + update_user_time( event->display, event->window, event->time ); map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 8d80882deea..f87324549aa 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -99,10 +99,6 @@ XContext winContext = 0; static XContext win_data_context = 0; static XContext host_window_context = 0;
-/* time of last user event and window where it's stored */ -static Time last_user_time; -static Window user_time_window; - static const WCHAR whole_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','w','h','o','l','e','_','w','i','n','d','o','w',0}; static const WCHAR clip_window_prop[] = @@ -270,7 +266,7 @@ static void remove_startup_notification(Display *display, Window window)
if (!(id = getenv( "DESKTOP_STARTUP_ID" )) || !id[0]) return;
- if ((src = strstr( id, "_TIME" ))) update_user_time( atol( src + 5 )); + if ((src = strstr( id, "_TIME" ))) update_user_time( display, window, atol( src + 5 ) );
pos = snprintf(message, sizeof(message), "remove: ID="); message[pos++] = '"'; @@ -1076,11 +1072,6 @@ static void set_initial_wm_hints( Display *display, Window window )
XChangeProperty( display, window, x11drv_atom(XdndAware), XA_ATOM, 32, PropModeReplace, (unsigned char*)&dndVersion, 1 ); - - update_user_time( 0 ); /* make sure that the user time window exists */ - if (user_time_window) - XChangeProperty( display, window, x11drv_atom(_NET_WM_USER_TIME_WINDOW), - XA_WINDOW, 32, PropModeReplace, (unsigned char *)&user_time_window, 1 ); }
@@ -1149,26 +1140,10 @@ Window init_clip_window(void) /*********************************************************************** * update_user_time */ -void update_user_time( Time time ) +void update_user_time( Display *display, Window window, Time time ) { - if (!user_time_window) - { - Window win = XCreateWindow( gdi_display, root_window, -1, -1, 1, 1, 0, CopyFromParent, - InputOnly, CopyFromParent, 0, NULL ); - if (InterlockedCompareExchangePointer( (void **)&user_time_window, (void *)win, 0 )) - XDestroyWindow( gdi_display, win ); - TRACE( "user time window %lx\n", user_time_window ); - } - - if (!time) return; - XLockDisplay( gdi_display ); - if (!last_user_time || (long)(time - last_user_time) > 0) - { - last_user_time = time; - XChangeProperty( gdi_display, user_time_window, x11drv_atom(_NET_WM_USER_TIME), - XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&time, 1 ); - } - XUnlockDisplay( gdi_display ); + XChangeProperty( display, window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)&time, 1 ); }
/* Update _NET_WM_FULLSCREEN_MONITORS when _NET_WM_STATE_FULLSCREEN is set to support fullscreen diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5c5607dfccd..2a71d2c8a81 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -675,7 +675,7 @@ extern BOOL get_window_state_updates( HWND hwnd, UINT *state_cmd, UINT *config_c extern void net_supported_init( struct x11drv_thread_data *data );
extern Window init_clip_window(void); -extern void update_user_time( Time time ); +extern void update_user_time( Display *display, Window window, Time time ); extern UINT get_window_net_wm_state( Display *display, Window window ); extern void make_window_embedded( struct x11drv_win_data *data ); extern Window create_client_window( HWND hwnd, const XVisualInfo *visual, Colormap colormap );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 72 ++++++++++++++++++++------------------- dlls/winex11.drv/x11drv.h | 1 + 2 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index f87324549aa..e4ef463a415 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -887,9 +887,9 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) }
-static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ); +static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, UINT swp_flags );
-static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints ) +static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints, UINT swp_flags ) { const MwmHints *old_hints = &data->pending_state.mwm_hints;
@@ -917,8 +917,8 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ WARN( "window %p/%lx adds/removes decorations, remapping\n", data->hwnd, data->whole_window ); NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 ); - window_set_wm_state( data, WithdrawnState ); - window_set_wm_state( data, NormalState ); + window_set_wm_state( data, WithdrawnState, FALSE ); + window_set_wm_state( data, NormalState, swp_flags ); }
data->pending_state.mwm_hints = *new_hints; @@ -933,7 +933,7 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * /*********************************************************************** * set_mwm_hints */ -static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_style ) +static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_style, UINT swp_flags ) { MwmHints mwm_hints;
@@ -969,7 +969,7 @@ static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_sty mwm_hints.status = 0; TRACE( "%p setting mwm hints to %s (style %x exstyle %x)\n", data->hwnd, debugstr_mwm_hints(&mwm_hints), style, ex_style ); - window_set_mwm_hints( data, &mwm_hints ); + window_set_mwm_hints( data, &mwm_hints, swp_flags ); }
@@ -1099,7 +1099,7 @@ static void make_owner_managed( HWND hwnd ) * * Set all the window manager hints for a window. */ -static void set_wm_hints( struct x11drv_win_data *data ) +static void set_wm_hints( struct x11drv_win_data *data, UINT swp_flags ) { DWORD style, ex_style;
@@ -1116,7 +1116,7 @@ static void set_wm_hints( struct x11drv_win_data *data ) }
set_size_hints( data, style ); - set_mwm_hints( data, style, ex_style ); + set_mwm_hints( data, style, ex_style, swp_flags ); set_style_hints( data, style, ex_style ); }
@@ -1268,7 +1268,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat XFlush( data->display ); }
-static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above ) +static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above, UINT swp_flags ) { static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0; @@ -1424,12 +1424,13 @@ static void set_xembed_flags( struct x11drv_win_data *data, unsigned long flags x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 ); }
-static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) +static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, UINT swp_flags ) { UINT old_state = data->pending_state.wm_state; HWND foreground = NtUserGetForegroundWindow();
data->desired_state.wm_state = new_state; + data->desired_state.swp_flags = swp_flags; if (!data->whole_window) return; /* no window, nothing to update */ if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ if (old_state == new_state) return; /* states are the same, nothing to update */ @@ -1439,14 +1440,14 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) case MAKELONG(WithdrawnState, IconicState): case MAKELONG(WithdrawnState, NormalState): remove_startup_notification( data->display, data->whole_window ); - set_wm_hints( data ); + set_wm_hints( data, swp_flags ); update_net_wm_states( data ); sync_window_style( data ); update_net_wm_fullscreen_monitors( data ); break; case MAKELONG(IconicState, NormalState): case MAKELONG(NormalState, IconicState): - set_wm_hints( data ); + set_wm_hints( data, swp_flags ); break; }
@@ -1458,6 +1459,7 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) }
data->pending_state.wm_state = new_state; + data->pending_state.swp_flags = swp_flags; data->wm_state_serial = NextRequest( data->display ); 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() ); @@ -1491,7 +1493,7 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) /*********************************************************************** * map_window */ -static void map_window( HWND hwnd, DWORD new_style ) +static void map_window( HWND hwnd, DWORD new_style, BOOL swp_flags ) { struct x11drv_win_data *data;
@@ -1499,7 +1501,7 @@ static void map_window( HWND hwnd, DWORD new_style )
if (!(data = get_win_data( hwnd ))) return; TRACE( "win %p/%lx\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, swp_flags ); release_win_data( data ); }
@@ -1513,7 +1515,7 @@ static void unmap_window( HWND hwnd )
if (!(data = get_win_data( hwnd ))) return; TRACE( "win %p/%lx\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, WithdrawnState, 0 ); release_win_data( data ); }
@@ -1674,10 +1676,10 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, return;
/* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.swp_flags ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE ); - window_set_mwm_hints( data, &data->desired_state.mwm_hints ); + window_set_config( data, &data->desired_state.rect, FALSE, data->desired_state.swp_flags ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints, data->desired_state.swp_flags );
if (data->current_state.wm_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); else if (!data->wm_state_serial) NtUserRemoveProp( data->hwnd, focus_time_prop ); @@ -1697,10 +1699,10 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser return;
/* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.swp_flags ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE ); - window_set_mwm_hints( data, &data->desired_state.mwm_hints ); + window_set_config( data, &data->desired_state.rect, FALSE, data->desired_state.swp_flags ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints, data->desired_state.swp_flags ); }
void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *value ) @@ -1754,10 +1756,10 @@ BOOL window_should_take_focus( HWND hwnd, Time time ) void make_window_embedded( struct x11drv_win_data *data ) { /* the window cannot be mapped before being embedded */ - window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, WithdrawnState, 0 ); data->embedded = TRUE; data->managed = TRUE; - window_set_wm_state( data, NormalState ); + window_set_wm_state( data, NormalState, SWP_NOACTIVATE ); }
@@ -1787,8 +1789,8 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags )
set_size_hints( data, style ); update_net_wm_states( data ); - window_set_config( data, &data->rects.visible, above ); - set_mwm_hints( data, style, ex_style ); + window_set_config( data, &data->rects.visible, above, swp_flags ); + set_mwm_hints( data, style, ex_style, swp_flags ); }
@@ -2110,7 +2112,7 @@ static void create_whole_window( struct x11drv_win_data *data ) if (!is_window_managed( data->hwnd, SWP_NOACTIVATE, &data->rects.window )) data->managed = FALSE; x11drv_xinput2_enable( data->display, data->whole_window ); set_initial_wm_hints( data->display, data->whole_window ); - set_wm_hints( data ); + set_wm_hints( data, 0 );
XSaveContext( data->display, data->whole_window, winContext, (char *)data->hwnd ); NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)data->whole_window ); @@ -2247,7 +2249,7 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) if (!(data = get_win_data( hwnd ))) return; if (!data->whole_window) goto done;
- if (offset == GWL_STYLE && (changed & WS_DISABLED)) set_wm_hints( data ); + if (offset == GWL_STYLE && (changed & WS_DISABLED)) set_wm_hints( data, 0 );
if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED)) /* changing WS_EX_LAYERED resets attributes */ { @@ -2556,7 +2558,7 @@ BOOL X11DRV_SystrayDockRemove( HWND hwnd )
if ((data = get_win_data( hwnd ))) { - if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState ); + if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState, 0 ); release_win_data( data ); }
@@ -2988,17 +2990,17 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN needs_map = data->layered || IsRectEmpty( &new_rects->window ); release_win_data( data ); if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); - if (needs_map) map_window( hwnd, new_style ); + if (needs_map) map_window( hwnd, new_style, swp_flags ); return; } else if ((swp_flags & SWP_STATECHANGED) && ((old_style ^ new_style) & WS_MINIMIZE)) { - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, swp_flags ); update_net_wm_states( data ); } else { - if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data, swp_flags ); update_net_wm_states( data ); } } @@ -3089,7 +3091,7 @@ void X11DRV_SetWindowIcon( HWND hwnd, UINT type, HICON icon ) else fetch_icon_data( hwnd, 0, icon );
if (!(data = get_win_data( hwnd ))) return; - set_wm_hints( data ); + set_wm_hints( data, 0 ); done: release_win_data( data ); } @@ -3141,7 +3143,7 @@ void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWO ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) { release_win_data( data ); - map_window( hwnd, style ); + map_window( hwnd, style, 0 ); return; } } @@ -3178,7 +3180,7 @@ void X11DRV_UpdateLayeredWindow( HWND hwnd, UINT flags ) DWORD style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
if ((style & WS_VISIBLE) && ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) - map_window( hwnd, style ); + map_window( hwnd, style, 0 ); } }
@@ -3240,7 +3242,7 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) { /* update the full screen state */ update_net_wm_states( data ); - window_set_config( data, &data->rects.visible, FALSE ); + window_set_config( data, &data->rects.visible, FALSE, 0 ); release_win_data( data ); } return 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 2a71d2c8a81..147b053943f 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -609,6 +609,7 @@ enum x11drv_net_wm_state
struct window_state { + UINT swp_flags; UINT wm_state; UINT net_wm_state; MwmHints mwm_hints;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/window.c | 19 ++++++++++++++----- dlls/winex11.drv/x11drv.h | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 70b09f4315e..e726884b6d8 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -701,7 +701,7 @@ static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
if (protocol == x11drv_atom(WM_DELETE_WINDOW)) { - update_user_time( event->display, event->window, event_time ); + update_user_time( event->display, event->window, event_time, FALSE );
if (hwnd == NtUserGetDesktopWindow()) { diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index f24313852b5..7a4b22e52cd 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1340,7 +1340,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) TRACE_(key)("type %d, window %lx, state 0x%04x, keycode %u\n", event->type, event->window, event->state, event->keycode);
- if (event->type == KeyPress) update_user_time( event->display, event->window, event->time ); + if (event->type == KeyPress) update_user_time( event->display, event->window, event->time, FALSE );
/* Clients should pass only KeyPress events to XmbLookupString */ if (xic && event->type == KeyPress) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index f1a3a2ab516..f04ceceef78 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1547,7 +1547,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) input.mi.time = EVENT_x11_time_to_win32_time( event->time ); input.mi.dwExtraInfo = 0;
- update_user_time( event->display, event->window, event->time ); + update_user_time( event->display, event->window, event->time, FALSE ); map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input ); send_mouse_input( hwnd, event->window, event->state, &input ); return TRUE; diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index e4ef463a415..8347424b7a5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -266,7 +266,7 @@ static void remove_startup_notification(Display *display, Window window)
if (!(id = getenv( "DESKTOP_STARTUP_ID" )) || !id[0]) return;
- if ((src = strstr( id, "_TIME" ))) update_user_time( display, window, atol( src + 5 ) ); + if ((src = strstr( id, "_TIME" ))) update_user_time( display, window, atol( src + 5 ), FALSE );
pos = snprintf(message, sizeof(message), "remove: ID="); message[pos++] = '"'; @@ -1140,10 +1140,12 @@ Window init_clip_window(void) /*********************************************************************** * update_user_time */ -void update_user_time( Display *display, Window window, Time time ) +void update_user_time( Display *display, Window window, Time time, BOOL force ) { - XChangeProperty( display, window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, - 32, PropModeReplace, (unsigned char *)&time, 1 ); + if (!force && (time == -1 || time == 0)) time = 1; + if (time == -1) XDeleteProperty( display, window, x11drv_atom(_NET_WM_USER_TIME) ); + else XChangeProperty( display, window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)&time, 1 ); }
/* Update _NET_WM_FULLSCREEN_MONITORS when _NET_WM_STATE_FULLSCREEN is set to support fullscreen @@ -1451,7 +1453,14 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, U break; }
- if (new_state != NormalState && data->has_focus && data->hwnd != foreground) + if (new_state == NormalState) + { + /* try forcing activation if the window is supposed to be foreground */ + if (data->hwnd == foreground) swp_flags = 0; + if (swp_flags & SWP_NOACTIVATE) update_user_time( data->display, data->whole_window, 0, TRUE ); + else update_user_time( data->display, data->whole_window, -1, TRUE ); + } + else if (data->has_focus && data->hwnd != foreground) { Window window = X11DRV_get_whole_window( foreground ); WARN( "Inconsistent input focus, activating window %p/%lx\n", foreground, window ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 147b053943f..ce30fbaa49b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -676,7 +676,7 @@ extern BOOL get_window_state_updates( HWND hwnd, UINT *state_cmd, UINT *config_c extern void net_supported_init( struct x11drv_thread_data *data );
extern Window init_clip_window(void); -extern void update_user_time( Display *display, Window window, Time time ); +extern void update_user_time( Display *display, Window window, Time time, BOOL force ); extern UINT get_window_net_wm_state( Display *display, Window window ); extern void make_window_embedded( struct x11drv_win_data *data ); extern Window create_client_window( HWND hwnd, const XVisualInfo *visual, Colormap colormap );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 8347424b7a5..6fd7def0acb 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1273,24 +1273,33 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above, UINT swp_flags ) { static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); - UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0; + UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0, net_wm_state = -1; const RECT *old_rect = &data->pending_state.rect; XWindowChanges changes; + BOOL is_maximized;
data->desired_state.rect = *new_rect; if (!data->whole_window) return; /* no window, nothing to update */ if (EqualRect( old_rect, new_rect )) return; /* rects are the same, nothing to update */
- if (data->pending_state.wm_state == NormalState && data->net_wm_state_serial && - !(data->pending_state.net_wm_state & fullscreen_mask) && - (data->current_state.net_wm_state & fullscreen_mask)) + /* Kwin internal maximized state tracking gets bogus if a window configure request is sent to a maximized + * window, and it loses track of whether the window was maximized state. + * + * Moving a maximized window to a different monitor requires sending a configure request but KWin bug makes + * no difference to requests with only position changes, and they trigger it all the same. + * + * Instead, explicitly request an unmap / map sequence ourselves and track the corresponding events, overriding + * the Mutter generated sequence, while achieving the same thing and getting WM_TAKE_FOCUS event when the + * window is mapped again. + */ + is_maximized = (data->pending_state.net_wm_state | data->current_state.net_wm_state) & fullscreen_mask; + if (data->managed && data->pending_state.wm_state == NormalState && is_maximized) { - /* Some window managers are sending a ConfigureNotify event with the fullscreen size when - * exiting a fullscreen window, with a serial that we cannot predict. Handling that event - * will override the Win32 window size and make the window fullscreen again. - */ - WARN( "window %p/%lx is exiting maximize/fullscreen, delaying request\n", data->hwnd, data->whole_window ); - return; + if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ + if (data->net_wm_state_serial) return; /* another _NET_WM_STATE update is pending, wait for it to complete */ + WARN( "window %p/%lx is maximized/fullscreen, temporarily restoring\n", data->hwnd, data->whole_window ); + net_wm_state = data->pending_state.net_wm_state; + window_set_net_wm_state( data, net_wm_state & ~fullscreen_mask ); }
/* resizing a managed maximized window is not allowed */ @@ -1325,6 +1334,8 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec TRACE( "window %p/%lx, requesting config %s above %u, serial %lu\n", data->hwnd, data->whole_window, wine_dbgstr_rect(new_rect), above, data->configure_serial ); XReconfigureWMWindow( data->display, data->whole_window, data->vis.screen, mask, &changes ); + + if (net_wm_state != -1) window_set_net_wm_state( data, net_wm_state ); }
/***********************************************************************
v4: Split 8e43ee0ec3a8af46c6c02823e06a8a47ca333b83 which specifically fixes https://bugs.winehq.org/show_bug.cgi?id=57285 with muffin WM (a mutter derivative, most likely doing the same fancy dance when changing decorations and tripping itself).
Does it work well with 2-display setups specifically for MS Office with decorations? I've tried 10rc1 with this patch and it behaves weirdly - can't be maximized or unmaximized on the second screen in mutter.
I don't know, there's nothing that should be different with multiple monitors here, and I would expect it to work as well, although that mostly depends on Mutter's will. We also sometimes set _NET_WM_FULLSCREEN_MONITORS, which might be getting in the way for that specific case.