In preparation for more changes to properly track X11 active window and request active window changes, ultimately improving window focus consistency on X11.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 3 +++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 3 files changed, 5 insertions(+)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index f70e74296cd..ac4ff84b960 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2117,6 +2117,7 @@ Window create_client_window( HWND hwnd, const XVisualInfo *visual, Colormap colo */ static void create_whole_window( struct x11drv_win_data *data ) { + unsigned long xhwnd = (UINT_PTR)data->hwnd; int cx, cy, mask; XSetWindowAttributes attr; WCHAR text[1024]; @@ -2151,6 +2152,8 @@ static void create_whole_window( struct x11drv_win_data *data ) cx, cy, 0, data->vis.depth, InputOutput, data->vis.visual, mask, &attr ); if (!data->whole_window) goto done; + XChangeProperty( data->display, data->whole_window, x11drv_atom(_WINE_HWND), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&xhwnd, 1 ); SetRect( &data->current_state.rect, pos.x, pos.y, pos.x + cx, pos.y + cy ); data->pending_state.rect = data->current_state.rect; data->desired_state.rect = data->current_state.rect; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index a5bab864ad9..7425385ce84 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -514,6 +514,7 @@ enum x11drv_atoms XATOM__NET_WM_WINDOW_TYPE_UTILITY, XATOM__NET_WORKAREA, XATOM__GTK_WORKAREAS_D0, + XATOM__WINE_HWND, XATOM__XEMBED, XATOM__XEMBED_INFO, XATOM_XdndAware, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index e58d3afca63..6baff60481e 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -155,6 +155,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WORKAREA", "_GTK_WORKAREAS_D0", + "_WINE_HWND", "_XEMBED", "_XEMBED_INFO", "XdndAware",
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 100 +++++++++++++++----------------------- 1 file changed, 39 insertions(+), 61 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ac4ff84b960..ffd08655690 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1664,38 +1664,51 @@ 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 ) +static BOOL handle_state_change( unsigned long serial, unsigned long *expect_serial, UINT size, const void *value, + void *desired, void *pending, void *current, const char *expected, + const char *prefix, 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; + WARN( "Ignoring %s%s%s%s\n", prefix, reason, received, expected ); + 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 ); + if (!reason) TRACE( "%s%s%s\n", prefix, 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 */ + WARN( "%s%s%s%s\n", prefix, reason, received, expected ); + /* 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 ) +{ + 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, *prefix; + + prefix = wine_dbg_sprintf( "window %p/%lx ", data->hwnd, data->whole_window ); + 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( serial, expect_serial, sizeof(value), &value, desired, pending, + current, expected, prefix, received, reason )) + return;
/* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state ); @@ -1707,32 +1720,15 @@ 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, *prefix;
+ prefix = wine_dbg_sprintf( "window %p/%lx ", data->hwnd, data->whole_window ); 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( serial, expect_serial, sizeof(value), &value, desired, pending, + current, expected, prefix, 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 ); @@ -1744,32 +1740,14 @@ 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, *prefix;
+ prefix = wine_dbg_sprintf( "window %p/%lx ", data->hwnd, data->whole_window ); 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( serial, expect_serial, sizeof(*value), value, desired, pending, + current, expected, prefix, received, NULL ); }
BOOL window_has_pending_wm_state( HWND hwnd, UINT state )
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/event.c | 9 +++++ dlls/winex11.drv/window.c | 68 ++++++++++++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 15 +++++++- dlls/winex11.drv/x11drv_main.c | 2 + 4 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index b1797cea24a..91a0230107a 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1262,6 +1262,14 @@ static void handle_net_supported_notify( XPropertyEvent *event ) if (event->state == PropertyNewValue) net_supported_init( data ); }
+static void handle_net_active_window( XPropertyEvent *event ) +{ + Window window = 0; + + if (event->state == PropertyNewValue) window = get_net_active_window( event->display ); + net_active_window_notify( event->serial, window, event->time ); +} + /*********************************************************************** * X11DRV_PropertyNotify */ @@ -1274,6 +1282,7 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) 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(_NET_SUPPORTED)) handle_net_supported_notify( event ); + if (event->atom == x11drv_atom(_NET_ACTIVE_WINDOW)) handle_net_active_window( event );
return TRUE; } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ffd08655690..ea16ff3ceae 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -250,6 +250,30 @@ void host_window_set_parent( struct host_window *win, Window parent ) host_window_reparent( &win->parent, parent, win->window ); }
+/* returns the HWND for the X11 window, or the desktop window if it isn't a Wine window */ +static HWND hwnd_from_window( Display *display, Window window ) +{ + HWND hwnd = 0, desktop = NtUserGetDesktopWindow(); + unsigned long count, remaining; + unsigned long *xhwnd; + int format; + Atom type; + + if (!window) return 0; + if (window == root_window) return desktop; + if (!XFindContext( display, window, winContext, (char **)&hwnd )) return hwnd; + + hwnd = desktop; + X11DRV_expect_error( display, host_window_error, NULL ); + if (!XGetWindowProperty( display, window, x11drv_atom(_WINE_HWND), 0, 65536, False, XA_CARDINAL, + &type, &format, &count, &remaining, (unsigned char **)&xhwnd )) + { + if (type == XA_CARDINAL && format == 32) hwnd = ULongToHandle(*xhwnd); + XFree( xhwnd ); + } + if (X11DRV_check_error()) return desktop; + return hwnd; +}
/*********************************************************************** * http://standards.freedesktop.org/startup-notification-spec @@ -1750,6 +1774,50 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial current, expected, prefix, received, NULL ); }
+void net_active_window_notify( unsigned long serial, Window value, Time time ) +{ + struct x11drv_thread_data *data = x11drv_thread_data(); + Window *desired = &data->desired_state.net_active_window, *pending = &data->pending_state.net_active_window, *current = &data->current_state.net_active_window; + unsigned long *expect_serial = &data->net_active_window_serial; + const char *expected, *received; + HWND current_hwnd, pending_hwnd; + + current_hwnd = hwnd_from_window( data->display, value ); + pending_hwnd = hwnd_from_window( data->display, *pending ); + + received = wine_dbg_sprintf( "_NET_ACTIVE_WINDOW %p/%lx serial %lu time %lu", current_hwnd, value, serial, time ); + expected = *expect_serial ? wine_dbg_sprintf( ", expected %p/%lx serial %lu", pending_hwnd, *pending, *expect_serial ) : ""; + handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, + current, expected, "", received, NULL ); +} + +Window get_net_active_window( Display *display ) +{ + unsigned long count, remaining; + Window window = None, *value; + int format; + Atom type; + + if (!XGetWindowProperty( display, DefaultRootWindow( display ), x11drv_atom(_NET_ACTIVE_WINDOW), 0, + 65536 / sizeof(Window), False, XA_WINDOW, &type, &format, &count, + &remaining, (unsigned char **)&value )) + { + if (type == XA_WINDOW && format == 32) window = *value; + XFree( value ); + } + + return window; +} + +void net_active_window_init( struct x11drv_thread_data *data ) +{ + Window window = get_net_active_window( data->display ); + + data->desired_state.net_active_window = window; + data->pending_state.net_active_window = window; + data->current_state.net_active_window = window; +} + BOOL window_has_pending_wm_state( HWND hwnd, UINT state ) { struct x11drv_win_data *data; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 7425385ce84..c83c8aadece 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -381,6 +381,11 @@ extern RECT host_window_configure_child( struct host_window *win, Window window, extern POINT host_window_map_point( struct host_window *win, int x, int y ); extern struct host_window *get_host_window( Window window, BOOL create );
+struct display_state +{ + Window net_active_window; +}; + struct x11drv_thread_data { Display *display; @@ -403,6 +408,11 @@ struct x11drv_thread_data XIValuatorClassInfo y_valuator; int xinput2_pointer; /* XInput2 master pointer device id */ #endif /* HAVE_X11_EXTENSIONS_XINPUT2_H */ + + struct display_state desired_state; /* display state tracking the desired / win32 state */ + struct display_state pending_state; /* display state tracking the pending / requested state */ + struct display_state current_state; /* display state tracking the current X11 state */ + unsigned long net_active_window_serial; /* serial of last pending _NET_ACTIVE_WINDOW request */ };
extern struct x11drv_thread_data *x11drv_init_thread_data(void); @@ -485,6 +495,7 @@ enum x11drv_atoms XATOM__ICC_PROFILE, XATOM__KDE_NET_WM_STATE_SKIP_SWITCHER, XATOM__MOTIF_WM_HINTS, + XATOM__NET_ACTIVE_WINDOW, XATOM__NET_STARTUP_INFO_BEGIN, XATOM__NET_STARTUP_INFO, XATOM__NET_SUPPORTED, @@ -667,8 +678,10 @@ 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 ); -extern BOOL get_window_state_updates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect );
+extern Window get_net_active_window( Display *display ); +extern void net_active_window_notify( unsigned long serial, Window window, Time time ); +extern void net_active_window_init( struct x11drv_thread_data *data ); extern void net_supported_init( struct x11drv_thread_data *data );
extern Window init_clip_window(void); diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 6baff60481e..fba19699f00 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -126,6 +126,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_ICC_PROFILE", "_KDE_NET_WM_STATE_SKIP_SWITCHER", "_MOTIF_WM_HINTS", + "_NET_ACTIVE_WINDOW", "_NET_STARTUP_INFO_BEGIN", "_NET_STARTUP_INFO", "_NET_SUPPORTED", @@ -756,6 +757,7 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) if (use_xim) xim_thread_attach( data ); x11drv_xinput2_init( data ); net_supported_init( data ); + net_active_window_init( data );
return data; }
Is there something wrong with this? This should not change any behavior and simply introduce tracking of the _NET_ACTIVE_WINDOW property that would allow us to properly request window activation and better track focus changes.
I don't think we want to set the HWND as X11 property, particularly since it's not guaranteed to be unique, there can be multiple Wine sessions on the same display.
Eh, right of course... crap.
I'm trying to figure a way to make this work while keeping the logic distributed in every process but to be honest this is the kind of thing that would be much more nicely solved with code in wineserver.
And I don't think delegating it to explorer, as a unique proxy for active window tracking, is strictly equivalent, especially when we will want to request host active window changes.
Would keeping a hwnd / host window mapping in wineserver be acceptable?
You can simply enum the top-level windows and query their whole_window property. It takes a few more server round-trips, but it shouldn't happen that often.