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. --- configure | 64 +++++++++++ configure.ac | 5 +- dlls/winewayland.drv/Makefile.in | 2 +- dlls/winewayland.drv/wayland.c | 9 ++ dlls/winewayland.drv/wayland_surface.c | 153 +++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 22 ++++ dlls/winewayland.drv/window.c | 12 +- dlls/winewayland.drv/window_surface.c | 66 ++++++++++- 8 files changed, 329 insertions(+), 4 deletions(-)
diff --git a/configure b/configure index 4cab5e67e43..9336990a5ec 100755 --- a/configure +++ b/configure @@ -702,6 +702,7 @@ INOTIFY_LIBS INOTIFY_CFLAGS PCSCLITE_LIBS PCAP_LIBS +WAYLAND_EXTRA_LIBS WAYLAND_SCANNER WAYLAND_CLIENT_LIBS WAYLAND_CLIENT_CFLAGS @@ -15706,6 +15707,68 @@ printf "%s\n" "no" >&6; } fi
+ ac_save_LIBS=$LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing shm_open" >&5 +printf %s "checking for library containing shm_open... " >&6; } +if test ${ac_cv_search_shm_open+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char shm_open (); +int +main (void) +{ +return shm_open (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt +do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO" +then : + ac_cv_search_shm_open=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext + if test ${ac_cv_search_shm_open+y} +then : + break +fi +done +if test ${ac_cv_search_shm_open+y} +then : + +else $as_nop + ac_cv_search_shm_open=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5 +printf "%s\n" "$ac_cv_search_shm_open" >&6; } +ac_res=$ac_cv_search_shm_open +if test "$ac_res" != no +then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + test "$ac_res" = "none required" || WAYLAND_EXTRA_LIBS="$ac_res" + +fi + + LIBS=$ac_save_LIBS else $as_nop WAYLAND_CLIENT_LIBS="" fi @@ -23233,6 +23296,7 @@ X_EXTRA_LIBS = $X_EXTRA_LIBS WAYLAND_CLIENT_CFLAGS = $WAYLAND_CLIENT_CFLAGS WAYLAND_CLIENT_LIBS = $WAYLAND_CLIENT_LIBS WAYLAND_SCANNER = $WAYLAND_SCANNER +WAYLAND_EXTRA_LIBS = $WAYLAND_EXTRA_LIBS PCAP_LIBS = $PCAP_LIBS PCSCLITE_LIBS = $PCSCLITE_LIBS INOTIFY_CFLAGS = $INOTIFY_CFLAGS diff --git a/configure.ac b/configure.ac index 3af0aa5fa98..57116268d51 100644 --- a/configure.ac +++ b/configure.ac @@ -1343,7 +1343,10 @@ then [AC_CHECK_HEADERS([wayland-client.h], [AC_CHECK_LIB(wayland-client,wl_display_connect, [AC_PATH_PROG(WAYLAND_SCANNER,wayland-scanner, - [`test -n "$PKG_CONFIG" && $PKG_CONFIG --variable=wayland_scanner wayland-scanner 2>/dev/null`])], + [`test -n "$PKG_CONFIG" && $PKG_CONFIG --variable=wayland_scanner wayland-scanner 2>/dev/null`]) + ac_save_LIBS=$LIBS + AC_SEARCH_LIBS(shm_open,rt,[test "$ac_res" = "none required" || AC_SUBST(WAYLAND_EXTRA_LIBS,"$ac_res")]) + LIBS=$ac_save_LIBS], [WAYLAND_CLIENT_LIBS=""],[$WAYLAND_CLIENT_LIBS])])]) fi WINE_NOTICE_WITH(wayland, [test -z "$WAYLAND_CLIENT_LIBS" -o -z "$WAYLAND_SCANNER"], diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index ef75c289b76..ae2ffaf3b10 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so UNIX_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS) +UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(PTHREAD_LIBS) $(WAYLAND_EXTRA_LIBS)
SOURCES = \ display.c \ diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 5d2fe122368..c5942e342d4 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -100,6 +100,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, @@ -192,6 +196,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 d9ee42863fa..fa1b8e27cbb 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -28,10 +28,18 @@
#include "wine/debug.h"
+#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> #include <stdlib.h> +#include <stdio.h> +#include <unistd.h>
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
+/* We only use 4 byte formats. */ +#define WINEWAYLAND_BYTES_PER_PIXEL 4 + static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { @@ -199,3 +207,148 @@ void wayland_surface_clear_role(struct wayland_surface *surface)
wl_display_flush(process_wayland.wl_display); } + +/********************************************************************** + * wayland_surface_attach_shm + * + * Attaches a SHM buffer on 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_shmfd_create + * + * Creates a file descriptor representing an anonymous SHM region. + */ +static int wayland_shmfd_create(int size) +{ + int ret; + int fd; + char name[64]; + + do + { + snprintf(name, sizeof(name), "/winewayland-shm-%d-%d", getpid(), rand()); + fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, 0600); + } while (fd < 0 && (errno == EINTR || errno == EEXIST)); + + if (fd < 0) return -1; + shm_unlink(name); + + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + + do + { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + { + close(fd); + return -1; + } + + return fd; +} + +/********************************************************************** + * 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 != MAP_FAILED) + munmap(shm_buffer->map_data, shm_buffer->map_size); + + 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; + int fd = -1; + 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; + shm_buffer->map_data = MAP_FAILED; + + fd = wayland_shmfd_create(size); + if (fd < 0) + { + ERR("Failed to create SHM fd\n"); + goto err; + } + + shm_buffer->map_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm_buffer->map_data == MAP_FAILED) + { + ERR("Failed to mmap SHM fd: %s size=%d\n", strerror(errno), size); + 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); + + TRACE("=> map=%p\n", shm_buffer->map_data); + + return shm_buffer; + +err: + if (fd >= 0) close(fd); + 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 ba938179bc6..2e39367a4c8 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; @@ -105,6 +106,14 @@ struct wayland_surface uint32_t current_serial; };
+struct wayland_shm_buffer +{ + struct wl_buffer *wl_buffer; + int width, height; + void *map_data; + size_t map_size; +}; + /********************************************************************** * Wayland initialization */ @@ -135,12 +144,25 @@ 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; +BOOL wayland_window_surface_needs_flush(struct window_surface *surface) 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 b7fdc0f85fe..dffdd013b96 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -114,7 +114,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); } @@ -315,6 +319,12 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags, if (data->wayland_surface) wayland_win_data_update_wayland_surface_state(data);
+ if (data->window_surface) + { + wayland_window_surface_update_wayland_surface(data->window_surface, + data->wayland_surface); + } + wayland_win_data_release(data); }
diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index bee46259d49..2d57401ad8f 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,44 @@ 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; + + 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); + +done: + reset_bounds(&wws->bounds); + wayland_window_surface_unlock(window_surface); }
/*********************************************************************** @@ -183,3 +230,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); +}