From 0cda91856138e1d49fcfd9b6c2c9328146616baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 14 Nov 2024 11:53:52 +0100 Subject: [PATCH] winex11: Request window state updates asynchronously. --- dlls/user32/tests/win.c | 7 +-- dlls/winex11.drv/event.c | 101 +++----------------------------------- dlls/winex11.drv/window.c | 41 +++++++++++----- dlls/winex11.drv/x11drv.h | 3 +- 4 files changed, 37 insertions(+), 115 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 041b0056be8..5f30818a830 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -4067,11 +4067,8 @@ static void test_SetForegroundWindow(HWND hwnd) while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); if (0) check_wnd_state(hwnd2, hwnd2, hwnd2, 0); - /* FIXME: these tests are failing because of a race condition - * between internal focus state applied immediately and X11 focus - * message coming late */ - todo_wine ok(GetActiveWindow() == hwnd2, "Expected active window %p, got %p.\n", hwnd2, GetActiveWindow()); - todo_wine ok(GetFocus() == hwnd2, "Expected focus window %p, got %p.\n", hwnd2, GetFocus()); + ok(GetActiveWindow() == hwnd2, "Expected active window %p, got %p.\n", hwnd2, GetActiveWindow()); + ok(GetFocus() == hwnd2, "Expected focus window %p, got %p.\n", hwnd2, GetFocus()); SetForegroundWindow(hwnd); check_wnd_state(hwnd, hwnd, hwnd, 0); diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index cc4869c4666..0a91148acde 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1200,7 +1200,7 @@ static int get_window_xembed_info( Display *display, Window window ) * * Handle a PropertyNotify for WM_STATE. */ -static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL update_window ) +static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event ) { struct x11drv_win_data *data; UINT value = 0, state_cmd = 0, config_cmd = 0; @@ -1208,34 +1208,11 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat if (!(data = get_win_data( hwnd ))) return; if (event->state == PropertyNewValue) value = get_window_wm_state( event->display, event->window ); - if (update_window) window_wm_state_notify( data, event->serial, value ); + window_wm_state_notify( data, event->serial, value ); - switch(event->state) - { - case PropertyDelete: - TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state ); - data->wm_state = WithdrawnState; - break; - case PropertyNewValue: - { - int old_state = data->wm_state; - int new_state = get_window_wm_state( event->display, data->whole_window ); - if (new_state != -1 && new_state != data->wm_state) - { - TRACE( "%p/%lx: new WM_STATE %d from %d\n", - data->hwnd, data->whole_window, new_state, old_state ); - data->wm_state = new_state; - } - } - break; - } - - if (update_window) - { - state_cmd = window_update_client_state( data ); - config_cmd = window_update_client_config( data ); - rect = window_rect_from_visible( &data->rects, data->current_state.rect ); - } + state_cmd = window_update_client_state( data ); + config_cmd = window_update_client_config( data ); + rect = window_rect_from_visible( &data->rects, data->current_state.rect ); release_win_data( data ); @@ -1300,79 +1277,13 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) XPropertyEvent *event = &xev->xproperty; if (!hwnd) return FALSE; - if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE ); + 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 ); return TRUE; } -/* event filter to wait for a WM_STATE change notification on a window */ -static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg ) -{ - if (event->xany.window != (Window)arg) return 0; - return (event->type == DestroyNotify || - (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE))); -} - -/*********************************************************************** - * wait_for_withdrawn_state - */ -void wait_for_withdrawn_state( HWND hwnd, BOOL set ) -{ - Display *display = thread_display(); - struct x11drv_win_data *data; - DWORD end = NtGetTickCount() + 2000; - - TRACE( "waiting for window %p to become %swithdrawn\n", hwnd, set ? "" : "not " ); - - for (;;) - { - XEvent event; - Window window; - int count = 0; - - if (!(data = get_win_data( hwnd ))) break; - if (!data->managed || data->embedded || data->display != display) break; - if (!(window = data->whole_window)) break; - if (!data->mapped == !set) - { - TRACE( "window %p/%lx now %smapped\n", hwnd, window, data->mapped ? "" : "un" ); - break; - } - if ((data->wm_state == WithdrawnState) != !set) - { - TRACE( "window %p/%lx state now %d\n", hwnd, window, data->wm_state ); - break; - } - release_win_data( data ); - - while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)window )) - { - count++; - if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */ - if (event.type == DestroyNotify) call_event_handler( display, &event ); - else handle_wm_state_notify( hwnd, &event.xproperty, FALSE ); - } - - if (!count) - { - struct pollfd pfd; - int timeout = end - NtGetTickCount(); - - pfd.fd = ConnectionNumber(display); - pfd.events = POLLIN; - if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1) - { - FIXME( "window %p/%lx wait timed out\n", hwnd, window ); - return; - } - } - } - release_win_data( data ); -} - - /***************************************************************** * SetFocus (X11DRV.@) * diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 4bbf71b7780..2beb0853c7d 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1213,7 +1213,10 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat { UINT i, count, old_state = data->pending_state.net_wm_state; + data->desired_state.net_wm_state = new_state; 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 */ + /* we ignore and override previous _NET_WM_STATE update requests */ if (old_state == new_state) return; /* states are the same, nothing to update */ if (data->pending_state.wm_state == IconicState) return; /* window is iconic, don't update its state now */ @@ -1267,6 +1270,8 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat SubstructureRedirectMask | SubstructureNotifyMask, &xev ); } } + + XFlush( data->display ); } static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above ) @@ -1275,6 +1280,7 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec const RECT *old_rect = &data->pending_state.rect; XWindowChanges changes; + 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 */ @@ -1328,7 +1334,7 @@ static void update_net_wm_states( struct x11drv_win_data *data ) style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); if (style & WS_MINIMIZE) - new_state |= data->pending_state.net_wm_state & ((1 << NET_WM_STATE_FULLSCREEN)|(1 << NET_WM_STATE_MAXIMIZED)); + new_state |= data->desired_state.net_wm_state & ((1 << NET_WM_STATE_FULLSCREEN)|(1 << NET_WM_STATE_MAXIMIZED)); if (data->is_fullscreen) { if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) @@ -1412,7 +1418,9 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) { UINT old_state = data->pending_state.wm_state; + data->desired_state.wm_state = new_state; 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 */ data->pending_state.wm_state = new_state; @@ -1441,6 +1449,8 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) /* override redirect windows won't receive WM_STATE property changes */ if (!data->managed) data->wm_state_serial = 0; + + XFlush( data->display ); } @@ -1452,7 +1462,6 @@ static void map_window( HWND hwnd, DWORD new_style ) struct x11drv_win_data *data; make_owner_managed( hwnd ); - wait_for_withdrawn_state( hwnd, TRUE ); if (!(data = get_win_data( hwnd ))) return; @@ -1466,7 +1475,6 @@ static void map_window( HWND hwnd, DWORD new_style ) sync_window_style( data ); window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); - XFlush( data->display ); data->mapped = TRUE; data->iconic = (new_style & WS_MINIMIZE) != 0; @@ -1483,8 +1491,6 @@ static void unmap_window( HWND hwnd ) { struct x11drv_win_data *data; - wait_for_withdrawn_state( hwnd, FALSE ); - if (!(data = get_win_data( hwnd ))) return; if (data->mapped) @@ -1581,7 +1587,7 @@ UINT window_update_client_config( struct x11drv_win_data *data ) void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ) { - UINT *pending = &data->pending_state.wm_state, *current = &data->current_state.wm_state; + 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; @@ -1606,16 +1612,20 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, else { WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *pending = value; /* avoid requesting the same state again */ + *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 ); + window_set_net_wm_state( data, data->desired_state.net_wm_state ); } void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ) { - UINT *pending = &data->pending_state.net_wm_state, *current = &data->current_state.net_wm_state; + 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; @@ -1638,16 +1648,20 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser else { WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *pending = value; /* avoid requesting the same state again */ + *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 ); + window_set_net_wm_state( data, data->desired_state.net_wm_state ); } void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *value ) { - RECT *pending = &data->pending_state.rect, *current = &data->current_state.rect; + 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; @@ -1670,7 +1684,7 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial else { WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); - *pending = *value; /* avoid requesting the same state again */ + *desired = *pending = *value; /* avoid requesting the same state again */ } *current = *value; @@ -1683,7 +1697,7 @@ BOOL window_has_pending_wm_state( HWND hwnd, UINT state ) BOOL pending; if (!(data = get_win_data( hwnd ))) return FALSE; - if (state != -1 && data->pending_state.wm_state != state) pending = FALSE; + if (state != -1 && data->desired_state.wm_state != state) pending = FALSE; else pending = !!data->wm_state_serial; release_win_data( data ); @@ -2053,6 +2067,7 @@ static void create_whole_window( struct x11drv_win_data *data ) if (!data->whole_window) goto done; 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; x11drv_xinput2_enable( data->display, data->whole_window ); set_initial_wm_hints( data->display, data->whole_window ); @@ -2107,9 +2122,9 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des if (data->whole_colormap) XFreeColormap( data->display, data->whole_colormap ); data->whole_window = data->client_window = 0; data->whole_colormap = 0; - data->wm_state = WithdrawnState; data->mapped = FALSE; + memset( &data->desired_state, 0, sizeof(data->desired_state) ); memset( &data->pending_state, 0, sizeof(data->pending_state) ); memset( &data->current_state, 0, sizeof(data->current_state) ); data->wm_state_serial = 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index fbaa416c617..38503667f23 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -633,13 +633,13 @@ 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 */ - int wm_state; /* current value of the WM_STATE property */ Window embedder; /* window id of embedder */ Pixmap icon_pixmap; Pixmap icon_mask; unsigned long *icon_bits; unsigned int icon_size; + struct window_state desired_state; /* window state tracking the desired / win32 state */ struct window_state pending_state; /* window state tracking the pending / requested state */ struct window_state current_state; /* window state tracking the current X11 state */ unsigned long wm_state_serial; /* serial of last pending WM_STATE request */ @@ -665,7 +665,6 @@ extern void window_configure_notify( struct x11drv_win_data *data, unsigned long extern UINT window_update_client_state( struct x11drv_win_data *data ); extern UINT window_update_client_config( struct x11drv_win_data *data ); -extern void wait_for_withdrawn_state( HWND hwnd, BOOL set ); extern Window init_clip_window(void); extern void update_user_time( Time time ); extern UINT get_window_net_wm_state( Display *display, Window window );