winex11: Request window state updates asynchronously.

This commit is contained in:
Rémi Bernon 2024-11-14 11:53:52 +01:00 committed by Alexandre Julliard
parent d8b5a3ae12
commit 0cda918561
Notes: Alexandre Julliard 2024-11-18 23:18:45 +01:00
Approved-by: Alexandre Julliard (@julliard)
Merge-Request: https://gitlab.winehq.org/wine/wine/merge_requests/6569
4 changed files with 37 additions and 115 deletions

View file

@ -4067,11 +4067,8 @@ static void test_SetForegroundWindow(HWND hwnd)
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
if (0) check_wnd_state(hwnd2, hwnd2, hwnd2, 0); if (0) check_wnd_state(hwnd2, hwnd2, hwnd2, 0);
/* FIXME: these tests are failing because of a race condition ok(GetActiveWindow() == hwnd2, "Expected active window %p, got %p.\n", hwnd2, GetActiveWindow());
* between internal focus state applied immediately and X11 focus ok(GetFocus() == hwnd2, "Expected focus window %p, got %p.\n", hwnd2, GetFocus());
* 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());
SetForegroundWindow(hwnd); SetForegroundWindow(hwnd);
check_wnd_state(hwnd, hwnd, hwnd, 0); check_wnd_state(hwnd, hwnd, hwnd, 0);

View file

@ -1200,7 +1200,7 @@ static int get_window_xembed_info( Display *display, Window window )
* *
* Handle a PropertyNotify for WM_STATE. * 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; struct x11drv_win_data *data;
UINT value = 0, state_cmd = 0, config_cmd = 0; 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 (!(data = get_win_data( hwnd ))) return;
if (event->state == PropertyNewValue) value = get_window_wm_state( event->display, event->window ); 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) state_cmd = window_update_client_state( data );
{ config_cmd = window_update_client_config( data );
case PropertyDelete: rect = window_rect_from_visible( &data->rects, data->current_state.rect );
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 );
}
release_win_data( data ); release_win_data( data );
@ -1300,79 +1277,13 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
XPropertyEvent *event = &xev->xproperty; XPropertyEvent *event = &xev->xproperty;
if (!hwnd) return FALSE; 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(_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_WM_STATE)) handle_net_wm_state_notify( hwnd, event );
return TRUE; 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.@) * SetFocus (X11DRV.@)
* *

View file

@ -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; 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->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 (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 */ 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 ); SubstructureRedirectMask | SubstructureNotifyMask, &xev );
} }
} }
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 )
@ -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; const RECT *old_rect = &data->pending_state.rect;
XWindowChanges changes; XWindowChanges changes;
data->desired_state.rect = *new_rect;
if (!data->whole_window) return; /* no window, nothing to update */ 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 (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 ); style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE );
if (style & WS_MINIMIZE) 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 (data->is_fullscreen)
{ {
if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) 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; 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->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 */ if (old_state == new_state) return; /* states are the same, nothing to update */
data->pending_state.wm_state = new_state; 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 */ /* override redirect windows won't receive WM_STATE property changes */
if (!data->managed) data->wm_state_serial = 0; 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; struct x11drv_win_data *data;
make_owner_managed( hwnd ); make_owner_managed( hwnd );
wait_for_withdrawn_state( hwnd, TRUE );
if (!(data = get_win_data( hwnd ))) return; if (!(data = get_win_data( hwnd ))) return;
@ -1466,7 +1475,6 @@ static void map_window( HWND hwnd, DWORD new_style )
sync_window_style( data ); sync_window_style( data );
window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState );
XFlush( data->display );
data->mapped = TRUE; data->mapped = TRUE;
data->iconic = (new_style & WS_MINIMIZE) != 0; data->iconic = (new_style & WS_MINIMIZE) != 0;
@ -1483,8 +1491,6 @@ static void unmap_window( HWND hwnd )
{ {
struct x11drv_win_data *data; struct x11drv_win_data *data;
wait_for_withdrawn_state( hwnd, FALSE );
if (!(data = get_win_data( hwnd ))) return; if (!(data = get_win_data( hwnd ))) return;
if (data->mapped) 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 ) 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; unsigned long *expect_serial = &data->wm_state_serial;
const char *reason = NULL, *expected, *received; const char *reason = NULL, *expected, *received;
@ -1606,16 +1612,20 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial,
else else
{ {
WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); 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; *current = value;
*expect_serial = 0; *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 ) 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; unsigned long *expect_serial = &data->net_wm_state_serial;
const char *reason = NULL, *expected, *received; 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 else
{ {
WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); 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; *current = value;
*expect_serial = 0; *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 ) 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; unsigned long *expect_serial = &data->configure_serial;
const char *reason = NULL, *expected, *received; const char *reason = NULL, *expected, *received;
@ -1670,7 +1684,7 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial
else else
{ {
WARN( "window %p/%lx, %s%s%s\n", data->hwnd, data->whole_window, reason, received, expected ); 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; *current = *value;
@ -1683,7 +1697,7 @@ BOOL window_has_pending_wm_state( HWND hwnd, UINT state )
BOOL pending; BOOL pending;
if (!(data = get_win_data( hwnd ))) return FALSE; 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; else pending = !!data->wm_state_serial;
release_win_data( data ); release_win_data( data );
@ -2053,6 +2067,7 @@ static void create_whole_window( struct x11drv_win_data *data )
if (!data->whole_window) goto done; if (!data->whole_window) goto done;
SetRect( &data->current_state.rect, pos.x, pos.y, pos.x + cx, pos.y + cy ); SetRect( &data->current_state.rect, pos.x, pos.y, pos.x + cx, pos.y + cy );
data->pending_state.rect = data->current_state.rect; data->pending_state.rect = data->current_state.rect;
data->desired_state.rect = data->current_state.rect;
x11drv_xinput2_enable( data->display, data->whole_window ); x11drv_xinput2_enable( data->display, data->whole_window );
set_initial_wm_hints( 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 ); if (data->whole_colormap) XFreeColormap( data->display, data->whole_colormap );
data->whole_window = data->client_window = 0; data->whole_window = data->client_window = 0;
data->whole_colormap = 0; data->whole_colormap = 0;
data->wm_state = WithdrawnState;
data->mapped = FALSE; data->mapped = FALSE;
memset( &data->desired_state, 0, sizeof(data->desired_state) );
memset( &data->pending_state, 0, sizeof(data->pending_state) ); memset( &data->pending_state, 0, sizeof(data->pending_state) );
memset( &data->current_state, 0, sizeof(data->current_state) ); memset( &data->current_state, 0, sizeof(data->current_state) );
data->wm_state_serial = 0; data->wm_state_serial = 0;

View file

@ -633,13 +633,13 @@ struct x11drv_win_data
UINT net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */ UINT net_wm_fullscreen_monitors_set : 1; /* is _NET_WM_FULLSCREEN_MONITORS set */
UINT is_fullscreen : 1; /* is the window visible rect fullscreen */ UINT is_fullscreen : 1; /* is the window visible rect fullscreen */
UINT parent_invalid : 1; /* is the parent host window possibly invalid */ 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 */ Window embedder; /* window id of embedder */
Pixmap icon_pixmap; Pixmap icon_pixmap;
Pixmap icon_mask; Pixmap icon_mask;
unsigned long *icon_bits; unsigned long *icon_bits;
unsigned int icon_size; 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 pending_state; /* window state tracking the pending / requested state */
struct window_state current_state; /* window state tracking the current X11 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 */ 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_state( struct x11drv_win_data *data );
extern UINT window_update_client_config( 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 Window init_clip_window(void);
extern void update_user_time( Time time ); extern void update_user_time( Time time );
extern UINT get_window_net_wm_state( Display *display, Window window ); extern UINT get_window_net_wm_state( Display *display, Window window );