From: Alexandros Frantzis alexandros.frantzis@collabora.com
Flush a window_surface to a Wayland surface by creating a wl_shm buffer matching the window size, copying the whole window contents to that buffer and attaching it to the corresponding Wayland surface. --- dlls/winewayland.drv/wayland.c | 9 ++ dlls/winewayland.drv/wayland_surface.c | 135 +++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 21 ++++ dlls/winewayland.drv/window.c | 11 +- dlls/winewayland.drv/window_surface.c | 68 ++++++++++++- 5 files changed, 242 insertions(+), 2 deletions(-)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 357dc4208d3..d35bd061b7e 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -94,6 +94,10 @@ static void registry_handle_global(void *data, struct wl_registry *registry, version < 2 ? version : 2); xdg_wm_base_add_listener(process_wayland.xdg_wm_base, &xdg_wm_base_listener, NULL); } + else if (strcmp(interface, "wl_shm") == 0) + { + process_wayland.wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + } }
static void registry_handle_global_remove(void *data, struct wl_registry *registry, @@ -176,6 +180,11 @@ BOOL wayland_process_init(void) ERR("Wayland compositor doesn't support xdg_wm_base\n"); return FALSE; } + if (!process_wayland.wl_shm) + { + ERR("Wayland compositor doesn't support wl_shm\n"); + return FALSE; + }
wayland_init_display_devices(FALSE);
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 250efd1c6a8..55af62a1e11 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -24,14 +24,22 @@
#include "config.h"
+#include "ntstatus.h" +#define WIN32_NO_STATUS + #include "waylanddrv.h"
#include "wine/debug.h" +#include "wine/server.h"
#include <stdlib.h> +#include <unistd.h>
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
+/* We only use 4 byte formats. */ +#define WINEWAYLAND_BYTES_PER_PIXEL 4 + /* Protects access to the user data of xdg_surface */ static pthread_mutex_t xdg_data_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -198,3 +206,130 @@ void wayland_surface_clear_role(struct wayland_surface *surface)
wl_display_flush(process_wayland.wl_display); } + +/********************************************************************** + * wayland_surface_attach_shm + * + * Attaches a SHM buffer to a wayland surface. + */ +void wayland_surface_attach_shm(struct wayland_surface *surface, + struct wayland_shm_buffer *shm_buffer) +{ + TRACE("surface=%p shm_buffer=%p (%dx%d)\n", + surface, shm_buffer, shm_buffer->width, shm_buffer->height); + + wl_surface_attach(surface->wl_surface, shm_buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface->wl_surface, 0, 0, + shm_buffer->width, shm_buffer->height); +} + +/********************************************************************** + * wayland_shm_buffer_destroy + * + * Destroys a SHM buffer. + */ +void wayland_shm_buffer_destroy(struct wayland_shm_buffer *shm_buffer) +{ + TRACE("%p map=%p\n", shm_buffer, shm_buffer->map_data); + + if (shm_buffer->wl_buffer) + wl_buffer_destroy(shm_buffer->wl_buffer); + if (shm_buffer->map_data) + NtUnmapViewOfSection(GetCurrentProcess(), shm_buffer->map_data); + + free(shm_buffer); +} + +/********************************************************************** + * wayland_shm_buffer_create + * + * Creates a SHM buffer with the specified width, height and format. + */ +struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height, + enum wl_shm_format format) +{ + struct wayland_shm_buffer *shm_buffer = NULL; + HANDLE handle = 0; + int fd = -1; + SIZE_T view_size = 0; + LARGE_INTEGER section_size; + NTSTATUS status; + struct wl_shm_pool *pool; + int stride, size; + + stride = width * WINEWAYLAND_BYTES_PER_PIXEL; + size = stride * height; + if (size == 0) + { + ERR("Invalid shm_buffer size %dx%d\n", width, height); + goto err; + } + + shm_buffer = calloc(1, sizeof(*shm_buffer)); + if (!shm_buffer) + { + ERR("Failed to allocate space for SHM buffer\n"); + goto err; + } + + TRACE("%p %dx%d format=%d size=%d\n", shm_buffer, width, height, format, size); + + shm_buffer->width = width; + shm_buffer->height = height; + shm_buffer->map_size = size; + + section_size.QuadPart = size; + status = NtCreateSection(&handle, + GENERIC_READ | SECTION_MAP_READ | SECTION_MAP_WRITE, + NULL, §ion_size, PAGE_READWRITE, SEC_COMMIT, 0); + if (status != STATUS_SUCCESS) + { + ERR("Failed to create SHM section status=0x%lx\n", (long)status); + goto err; + } + + status = NtMapViewOfSection(handle, GetCurrentProcess(), + (PVOID)&shm_buffer->map_data, 0, 0, NULL, + &view_size, ViewUnmap, 0, PAGE_READWRITE); + if (status != STATUS_SUCCESS) + { + shm_buffer->map_data = NULL; + ERR("Failed to create map SHM handle status=0x%lx\n", (long)status); + goto err; + } + + status = wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL); + if (status != STATUS_SUCCESS) + { + ERR("Failed to get fd from SHM handle status=0x%lx\n", (long)status); + goto err; + } + + pool = wl_shm_create_pool(process_wayland.wl_shm, fd, size); + if (!pool) + { + ERR("Failed to create SHM pool fd=%d size=%d\n", fd, size); + goto err; + } + shm_buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height, + stride, format); + wl_shm_pool_destroy(pool); + if (!shm_buffer->wl_buffer) + { + ERR("Failed to create SHM buffer %dx%d\n", width, height); + goto err; + } + + close(fd); + NtClose(handle); + + TRACE("=> map=%p\n", shm_buffer->map_data); + + return shm_buffer; + +err: + if (fd >= 0) close(fd); + if (handle) NtClose(handle); + if (shm_buffer) wayland_shm_buffer_destroy(shm_buffer); + return NULL; +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 6094fb680cb..95fbd474c5d 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -61,6 +61,7 @@ struct wayland struct zxdg_output_manager_v1 *zxdg_output_manager_v1; struct wl_compositor *wl_compositor; struct xdg_wm_base *xdg_wm_base; + struct wl_shm *wl_shm; struct wl_list output_list; /* Protects the output_list and the wayland_output.current states. */ pthread_mutex_t output_mutex; @@ -102,6 +103,14 @@ struct wayland_surface pthread_mutex_t mutex; };
+struct wayland_shm_buffer +{ + struct wl_buffer *wl_buffer; + int width, height; + void *map_data; + size_t map_size; +}; + /********************************************************************** * Wayland initialization */ @@ -125,12 +134,24 @@ struct wayland_surface *wayland_surface_create(void) 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; +void wayland_surface_attach_shm(struct wayland_surface *surface, + struct wayland_shm_buffer *shm_buffer) DECLSPEC_HIDDEN; + +/********************************************************************** + * Wayland SHM buffer + */ + +struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height, + enum wl_shm_format format) DECLSPEC_HIDDEN; +void wayland_shm_buffer_destroy(struct wayland_shm_buffer *shm_buffer) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland window surface */
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;
/********************************************************************** * USER driver functions diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index eacde4ba7f0..0a1da69d2a1 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -107,7 +107,11 @@ static void wayland_win_data_destroy(struct wayland_win_data *data)
pthread_mutex_unlock(&win_data_mutex);
- if (data->window_surface) window_surface_release(data->window_surface); + if (data->window_surface) + { + wayland_window_surface_update_wayland_surface(data->window_surface, NULL); + window_surface_release(data->window_surface); + } if (data->wayland_surface) wayland_surface_destroy(data->wayland_surface); free(data); } @@ -153,6 +157,8 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat /* We don't want wayland surfaces for child windows. */ if (parent != NtUserGetDesktopWindow() && parent != 0) { + if (data->window_surface) + wayland_window_surface_update_wayland_surface(data->window_surface, NULL); if (surface) wayland_surface_destroy(surface); surface = NULL; goto out; @@ -178,6 +184,9 @@ static void wayland_win_data_update_wayland_surface(struct wayland_win_data *dat pthread_mutex_unlock(&surface->mutex); }
+ if (data->window_surface) + wayland_window_surface_update_wayland_surface(data->window_surface, surface); + out: TRACE("hwnd=%p surface=%p=>%p\n", data->hwnd, data->wayland_surface, surface); data->wayland_surface = surface; diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index bee46259d49..d305da99f4a 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -37,6 +37,7 @@ struct wayland_window_surface { struct window_surface header; HWND hwnd; + struct wayland_surface *wayland_surface; RECT bounds; void *bits; pthread_mutex_t mutex; @@ -55,6 +56,15 @@ static inline void reset_bounds(RECT *bounds) bounds->right = bounds->bottom = INT_MIN; }
+static void buffer_release(void *data, struct wl_buffer *buffer) +{ + struct wayland_shm_buffer *shm_buffer = data; + TRACE("shm_buffer=%p\n", shm_buffer); + wayland_shm_buffer_destroy(shm_buffer); +} + +static const struct wl_buffer_listener buffer_listener = { buffer_release }; + /*********************************************************************** * wayland_window_surface_lock */ @@ -109,7 +119,46 @@ static void wayland_window_surface_set_region(struct window_surface *window_surf */ static void wayland_window_surface_flush(struct window_surface *window_surface) { - /* TODO */ + struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface); + struct wayland_shm_buffer *shm_buffer; + BOOL flushed = FALSE; + + wayland_window_surface_lock(window_surface); + + if (IsRectEmpty(&wws->bounds)) goto done; + + if (!wws->wayland_surface) + { + ERR("missing wayland surface, returning\n"); + goto done; + } + + TRACE("surface=%p hwnd=%p surface_rect=%s bounds=%s\n", wws, wws->hwnd, + wine_dbgstr_rect(&wws->header.rect), wine_dbgstr_rect(&wws->bounds)); + + shm_buffer = wayland_shm_buffer_create(wws->info.bmiHeader.biWidth, + abs(wws->info.bmiHeader.biHeight), + WL_SHM_FORMAT_XRGB8888); + if (!shm_buffer) + { + ERR("failed to create Wayland SHM buffer, returning\n"); + goto done; + } + + wl_buffer_add_listener(shm_buffer->wl_buffer, &buffer_listener, shm_buffer); + + 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); + pthread_mutex_unlock(&wws->wayland_surface->mutex); + wl_display_flush(process_wayland.wl_display); + flushed = TRUE; + +done: + if (flushed) reset_bounds(&wws->bounds); + wayland_window_surface_unlock(window_surface); }
/*********************************************************************** @@ -183,3 +232,20 @@ failed: wayland_window_surface_destroy(&wws->header); return NULL; } + +/*********************************************************************** + * wayland_window_surface_update_wayland_surface + */ +void wayland_window_surface_update_wayland_surface(struct window_surface *window_surface, + struct wayland_surface *wayland_surface) +{ + struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface); + + wayland_window_surface_lock(window_surface); + + TRACE("surface=%p hwnd=%p wayland_surface=%p\n", wws, wws->hwnd, wayland_surface); + + wws->wayland_surface = wayland_surface; + + wayland_window_surface_unlock(window_surface); +}