Module: wine Branch: master Commit: fc8570a1a611da6fa90245e213bd12c585c87958 URL: https://gitlab.winehq.org/wine/wine/-/commit/fc8570a1a611da6fa90245e213bd12c...
Author: Alexandros Frantzis alexandros.frantzis@collabora.com Date: Tue Jun 13 13:52:28 2023 +0300
winewayland.drv: Do not commit buffers to unconfigured surfaces.
The xdg-shell protocol disallows buffer commits to a wl_surface with an xdg_surface based role before we ack the first configure event. Failing to adhere to this requirement will get our client disconnected by the compositor with a protocol error.
---
dlls/winewayland.drv/wayland_surface.c | 16 +++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 5 ++++- dlls/winewayland.drv/window.c | 19 ++++++++++++++++++- dlls/winewayland.drv/window_surface.c | 14 +++++++++++--- 4 files changed, 48 insertions(+), 6 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 544c5df2a5a..ad30745fbc5 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -55,6 +55,8 @@ static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_sur uint32_t serial) { struct wayland_surface *surface; + BOOL initial_configure = FALSE; + HWND hwnd;
TRACE("serial=%u\n", serial);
@@ -63,9 +65,18 @@ static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_sur /* Handle this event only if wayland_surface is still associated with * the target xdg_surface. */ if (surface->xdg_surface == xdg_surface) + { + initial_configure = surface->current_serial == 0; + hwnd = surface->hwnd; + surface->current_serial = serial; xdg_surface_ack_configure(xdg_surface, serial); + }
pthread_mutex_unlock(&surface->mutex); + + /* Flush the window surface in case there is content that we weren't + * able to flush before due to the lack of the initial configure. */ + if (initial_configure) wayland_window_flush(hwnd); }
static const struct xdg_surface_listener xdg_surface_listener = @@ -78,7 +89,7 @@ static const struct xdg_surface_listener xdg_surface_listener = * * Creates a role-less wayland surface. */ -struct wayland_surface *wayland_surface_create(void) +struct wayland_surface *wayland_surface_create(HWND hwnd) { struct wayland_surface *surface;
@@ -93,6 +104,7 @@ struct wayland_surface *wayland_surface_create(void)
pthread_mutex_init(&surface->mutex, NULL);
+ surface->hwnd = hwnd; surface->wl_surface = wl_compositor_create_surface(process_wayland.wl_compositor); if (!surface->wl_surface) { @@ -196,6 +208,8 @@ void wayland_surface_clear_role(struct wayland_surface *surface) surface->xdg_surface = NULL; }
+ surface->current_serial = 0; + /* Ensure no buffer is attached, otherwise future role assignments may fail. */ wl_surface_attach(surface->wl_surface, NULL, 0, 0); wl_surface_commit(surface->wl_surface); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 95fbd474c5d..0afc03f4147 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -97,10 +97,12 @@ struct wayland_output
struct wayland_surface { + HWND hwnd; struct wl_surface *wl_surface; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; pthread_mutex_t mutex; + uint32_t current_serial; };
struct wayland_shm_buffer @@ -130,7 +132,7 @@ void wayland_output_use_xdg_extension(struct wayland_output *output) DECLSPEC_HI * Wayland surface */
-struct wayland_surface *wayland_surface_create(void) DECLSPEC_HIDDEN; +struct wayland_surface *wayland_surface_create(HWND hwnd) DECLSPEC_HIDDEN; void wayland_surface_destroy(struct wayland_surface *surface) DECLSPEC_HIDDEN; void wayland_surface_make_toplevel(struct wayland_surface *surface) DECLSPEC_HIDDEN; void wayland_surface_clear_role(struct wayland_surface *surface) DECLSPEC_HIDDEN; @@ -152,6 +154,7 @@ void wayland_shm_buffer_destroy(struct wayland_shm_buffer *shm_buffer) DECLSPEC_ struct window_surface *wayland_window_surface_create(HWND hwnd, const RECT *rect) DECLSPEC_HIDDEN; void wayland_window_surface_update_wayland_surface(struct window_surface *surface, struct wayland_surface *wayland_surface) DECLSPEC_HIDDEN; +void wayland_window_flush(HWND hwnd) DECLSPEC_HIDDEN;
/********************************************************************** * USER driver functions diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index cf5e5f0ec3f..efe72baf92e 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -165,7 +165,7 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat }
/* Otherwise ensure that we have a wayland surface. */ - if (!surface && !(surface = wayland_surface_create())) return; + if (!surface && !(surface = wayland_surface_create(data->hwnd))) return;
visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE; xdg_visible = surface->xdg_toplevel != NULL; @@ -321,3 +321,20 @@ LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
return NtUserMessageCall(hwnd, msg, wp, lp, 0, NtUserDefWindowProc, FALSE); } + +/********************************************************************** + * wayland_window_flush + * + * Flush the window_surface associated with a HWND. + */ +void wayland_window_flush(HWND hwnd) +{ + struct wayland_win_data *data = wayland_win_data_get(hwnd); + + if (!data) return; + + if (data->window_surface) + data->window_surface->funcs->flush(data->window_surface); + + wayland_win_data_release(data); +} diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index cb1c83d4b92..ca613c667c6 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -149,11 +149,19 @@ static void wayland_window_surface_flush(struct window_surface *window_surface) memcpy(shm_buffer->map_data, wws->bits, shm_buffer->map_size);
pthread_mutex_lock(&wws->wayland_surface->mutex); - wayland_surface_attach_shm(wws->wayland_surface, shm_buffer); - wl_surface_commit(wws->wayland_surface->wl_surface); + if (wws->wayland_surface->current_serial) + { + wayland_surface_attach_shm(wws->wayland_surface, shm_buffer); + wl_surface_commit(wws->wayland_surface->wl_surface); + flushed = TRUE; + } + else + { + TRACE("Wayland surface not configured yet, not flushing\n"); + wayland_shm_buffer_destroy(shm_buffer); + } pthread_mutex_unlock(&wws->wayland_surface->mutex); wl_display_flush(process_wayland.wl_display); - flushed = TRUE;
done: if (flushed) reset_bounds(&wws->bounds);