And a couple of other minor and (hopefully) harmless changes, in preparation for !8079 as it would otherwise grow large.
The main thing here is the introduction of _MOTIF_WM_HINTS into the state tracker, changing the property can trigger unexpected ConfigureNotify events and we will need to serialize its updates with other _NET_WM_STATE and window config requests in order to avoid getting surprised and confused.
It will also later be useful to be able to track further consequences of _MOTIF_WM_HINTS changes on Mutter-based window managers, such as a ReparentNotify > UnmapNotify > FocusOut > MapNotify > FocusIn > ConfigureNotify event sequence, to eventually ignore the transient focus loss and avoid changing Win32 foreground window during it.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/event.c | 27 ++++++++++++++++++++++ dlls/winex11.drv/window.c | 47 +++++++++++++++++++++++++++++++++------ dlls/winex11.drv/x11drv.h | 4 ++++ 3 files changed, 71 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 723f3204dd8..9385d2a2ec4 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1206,6 +1206,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 @@ -1252,6 +1267,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(); @@ -1286,6 +1312,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 ); if (event->atom == x11drv_atom(_NET_ACTIVE_WINDOW)) handle_net_active_window( event );
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 683c8037f9b..a5925fec96c 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); @@ -107,6 +106,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 ) @@ -897,6 +901,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 || !data->managed) return; /* no window or not managed, 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 */ @@ -934,15 +955,10 @@ 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) ); + window_set_mwm_hints( data, &mwm_hints ); }
@@ -1526,6 +1542,7 @@ static UINT window_update_client_state( struct x11drv_win_data *data )
if (data->wm_state_serial) return 0; /* another WM_STATE update is pending, wait for it to complete */ if (data->net_wm_state_serial) return 0; /* another _NET_WM_STATE update is pending, wait for it to complete */ + if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */ if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */
new_style = old_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); @@ -1573,6 +1590,7 @@ static UINT window_update_client_config( struct x11drv_win_data *data )
if (data->wm_state_serial) return 0; /* another WM_STATE update is pending, wait for it to complete */ if (data->net_wm_state_serial) return 0; /* another _NET_WM_STATE update is pending, wait for it to complete */ + if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */ if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */
if ((old_style & WS_CAPTION) == WS_CAPTION || !data->is_fullscreen) @@ -1720,6 +1738,20 @@ 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, *prefix; + + prefix = wine_dbg_sprintf( "window %p/%lx ", data->hwnd, data->whole_window ); + 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( serial, expect_serial, sizeof(*value), value, desired, pending, + current, expected, prefix, 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; @@ -2274,6 +2306,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des memset( &data->current_state, 0, sizeof(data->current_state) ); data->wm_state_serial = 0; data->net_wm_state_serial = 0; + data->mwm_hints_serial = 0; data->configure_serial = 0;
if (data->xic) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4cd2c7fa48f..61567c7319e 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
@@ -623,6 +624,7 @@ struct window_state UINT wm_state; BOOL activate; UINT net_wm_state; + MwmHints mwm_hints; RECT rect; };
@@ -660,6 +662,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 */ };
@@ -678,6 +681,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 void set_net_active_window( HWND hwnd, HWND previous );
From: Rémi Bernon rbernon@codeweavers.com
Use the window state to keep track of the monitors, although we don't record the X current state for now. There's no reason for it to change behind the scenes, but we can always later add a full tracker for the property if necessary. --- dlls/winex11.drv/window.c | 9 ++++++++- dlls/winex11.drv/x11drv.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a5925fec96c..caf557f8a79 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1149,7 +1149,7 @@ void window_set_user_time( struct x11drv_win_data *data, Time time, BOOL init ) * windows spanning multiple monitors */ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) { - long monitors[4]; + long *old_monitors = data->pending_state.monitors, monitors[4]; XEvent xev;
if (!(data->pending_state.net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) || is_virtual_desktop() @@ -1163,9 +1163,12 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) return;
xinerama_get_fullscreen_monitors( &data->rects.visible, monitors ); + memcpy( data->desired_state.monitors, monitors, sizeof(monitors) ); + if (!memcmp( old_monitors, monitors, sizeof(monitors) )) return; /* states are the same, nothing to update */
if (data->pending_state.wm_state == WithdrawnState) { + memcpy( data->pending_state.monitors, monitors, sizeof(monitors) ); TRACE( "window %p/%lx, requesting _NET_WM_FULLSCREEN_MONITORS %ld,%ld,%ld,%ld serial %lu\n", data->hwnd, data->whole_window, monitors[0], monitors[1], monitors[2], monitors[3], NextRequest( data->display ) ); if (monitors[0] == -1) XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_FULLSCREEN_MONITORS) ); @@ -1184,11 +1187,15 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) xev.xclient.data.l[4] = 1; memcpy( xev.xclient.data.l, monitors, sizeof(monitors) );
+ memcpy( data->pending_state.monitors, monitors, sizeof(monitors) ); TRACE( "window %p/%lx, requesting _NET_WM_FULLSCREEN_MONITORS %ld,%ld,%ld,%ld serial %lu\n", data->hwnd, data->whole_window, monitors[0], monitors[1], monitors[2], monitors[3], NextRequest( data->display ) ); XSendEvent( data->display, DefaultRootWindow( data->display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); } + + /* assume it changes immediately, we don't track the property for now */ + memcpy( data->current_state.monitors, monitors, sizeof(monitors) ); }
static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_state ) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 61567c7319e..1c7282d443d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -625,6 +625,7 @@ struct window_state BOOL activate; UINT net_wm_state; MwmHints mwm_hints; + long monitors[4]; RECT rect; };
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index caf557f8a79..1669fca386d 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -509,6 +509,8 @@ static void sync_window_style( struct x11drv_win_data *data ) XSetWindowAttributes attr; int mask = get_window_attributes( data, &attr );
+ TRACE( "window %p/%lx changing attributes mask %#x, serial %lu\n", data->hwnd, + data->whole_window, mask, NextRequest( data->display ) ); XChangeWindowAttributes( data->display, data->whole_window, mask, &attr ); x11drv_xinput2_enable( data->display, data->whole_window ); } @@ -896,6 +898,9 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) size_hints->flags |= PMinSize | PMaxSize; } } + + TRACE( "window %p/%lx requesting WM_NORMAL_HINTS flags %#lx, serial %lu\n", data->hwnd, + data->whole_window, size_hints->flags, NextRequest( data->display ) ); XSetWMNormalHints( data->display, data->whole_window, size_hints ); XFree( size_hints ); } @@ -994,6 +999,8 @@ static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex else window_type = x11drv_atom(_NET_WM_WINDOW_TYPE_NORMAL);
+ TRACE( "window %p/%lx requesting _NET_WM_WINDOW_TYPE %#lx, serial %lu\n", data->hwnd, + data->whole_window, window_type, NextRequest( data->display ) ); XChangeProperty(data->display, data->whole_window, x11drv_atom(_NET_WM_WINDOW_TYPE), XA_ATOM, 32, PropModeReplace, (unsigned char*)&window_type, 1);
@@ -1009,16 +1016,27 @@ static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex wm_hints->icon_mask = data->icon_mask; wm_hints->flags |= IconPixmapHint | IconMaskHint; } + + TRACE( "window %p/%lx requesting WM_HINTS flags %#lx, serial %lu\n", data->hwnd, + data->whole_window, wm_hints->flags, NextRequest( data->display ) ); XSetWMHints( data->display, data->whole_window, wm_hints ); XFree( wm_hints ); }
if (data->icon_bits) + { + TRACE( "window %p/%lx requesting _NET_WM_ICON, serial %lu\n", data->hwnd, + data->whole_window, NextRequest( data->display ) ); XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data->icon_bits, data->icon_size ); + } else + { + TRACE( "window %p/%lx deleting _NET_WM_ICON, serial %lu\n", data->hwnd, + data->whole_window, NextRequest( data->display ) ); XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON) ); + }
}
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 1669fca386d..7c21861e5da 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1222,7 +1222,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat
new_state &= x11drv_thread_data()->net_wm_state_mask; data->desired_state.net_wm_state = new_state; - if (!data->whole_window) return; /* no window, nothing to update */ + if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ /* we ignore and override previous _NET_WM_STATE update requests */ if (old_state == new_state) return; /* states are the same, nothing to update */ @@ -1350,7 +1350,7 @@ 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->embedded) return; if (data->whole_window == root_window) { if (is_virtual_desktop()) window_set_net_wm_state( data, is_desktop_fullscreen() ? (1 << NET_WM_STATE_FULLSCREEN) : 0 );
From: Rémi Bernon rbernon@codeweavers.com
Although we ignore them, the information still describes the current X window state and should be recorded. --- dlls/winex11.drv/window.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 7c21861e5da..61d049a4af3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1697,6 +1697,7 @@ static BOOL handle_state_change( unsigned long serial, unsigned long *expect_ser if (reason) { WARN( "Ignoring %s%s%s%s\n", prefix, reason, received, expected ); + memcpy( current, value, size ); return FALSE; }