This MR adds a fallback path that implements clipboard support using the core protocol (wl_data_device) when wlr-data-control-unstable-v1 is missing.
The reasons we want this fallback path, in spite of the extra complexity it introduces, are: 1. Some compositors don't (and likely won't ever) support {wlr,ext}-data-control-unstable-v1. 2. Even compositors that do support {wlr,ext}-data-control-unstable-v1, may not expose it to sandboxed applications.
This MR allows Wine to support clipboard operations in such scenarios, in a way that's good enough for many common user clipboard interactions. A more comprehensive discussion of the limitations can be found in !7236.
This MR differs from !7236 in that it creates a per-process clipboard thread and window, instead of using the desktop process clipboard thread and manually forwarding messages. Note that the per-process clipboard threads/windows are created only if we fall back to wl_data_device. In the wlr-data-control-unstable-v1 case nothing changes, we still use the desktop process clipboard thread as before.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
The data device is initialized once for each process, from within a dedicated thread and window that handles clipboard events. --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/dllmain.c | 61 ++++++++++++++++++++++ dlls/winewayland.drv/unixlib.h | 1 + dlls/winewayland.drv/wayland.c | 10 +++- dlls/winewayland.drv/wayland_data_device.c | 55 ++++++++++++++----- dlls/winewayland.drv/waylanddrv.h | 17 ++++-- dlls/winewayland.drv/waylanddrv_main.c | 11 ++++ 7 files changed, 140 insertions(+), 16 deletions(-)
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 142db22ba9e..4141e36c9a8 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -2,6 +2,7 @@ MODULE = winewayland.drv UNIXLIB = winewayland.so UNIX_CFLAGS = $(EGL_CFLAGS) $(WAYLAND_CLIENT_CFLAGS) $(WAYLAND_EGL_CFLAGS) $(XKBCOMMON_CFLAGS) $(XKBREGISTRY_CFLAGS) UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(WAYLAND_EGL_LIBS) $(XKBCOMMON_LIBS) $(XKBREGISTRY_LIBS) $(PTHREAD_LIBS) -lm +IMPORTS = user32 win32u
SOURCES = \ display.c \ diff --git a/dlls/winewayland.drv/dllmain.c b/dlls/winewayland.drv/dllmain.c index d040620957b..8055d883ee0 100644 --- a/dlls/winewayland.drv/dllmain.c +++ b/dlls/winewayland.drv/dllmain.c @@ -20,6 +20,9 @@
#include "waylanddrv_dll.h"
+#include "ntuser.h" +#include "winuser.h" + #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv); @@ -35,6 +38,61 @@ static DWORD WINAPI wayland_read_events_thread(void *arg) return 0; }
+static LRESULT CALLBACK clipboard_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + switch (msg) + { + case WM_NCCREATE: + case WM_CLIPBOARDUPDATE: + case WM_RENDERFORMAT: + case WM_TIMER: + case WM_DESTROYCLIPBOARD: + case WM_USER: + return NtUserMessageCall(hwnd, msg, wp, lp, 0, NtUserClipboardWindowProc, FALSE); + } + + return DefWindowProcW(hwnd, msg, wp, lp); +} + +static DWORD WINAPI clipboard_thread(void *arg) +{ + static const WCHAR clipboard_classname[] = L"__winewayland_clipboard_manager"; + WNDCLASSW class; + ATOM atom; + MSG msg; + HWND hwnd; + + memset(&class, 0, sizeof(class)); + class.lpfnWndProc = clipboard_wndproc; + class.lpszClassName = clipboard_classname; + + if (!(atom = RegisterClassW(&class)) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + ERR("could not register clipboard window class err %lu\n", GetLastError()); + return 0; + } + /* The HWND_MESSAGE parent window may not have been created yet. It will be + * created eventually, so keep trying. */ + while (!(hwnd = CreateWindowW(clipboard_classname, NULL, 0, 0, 0, 0, 0, + HWND_MESSAGE, 0, 0, NULL)) && + GetLastError() == ERROR_INVALID_WINDOW_HANDLE) + { + SwitchToThread(); + } + + if (!hwnd) + { + TRACE("failed to create clipboard window err %lu\n", GetLastError()); + UnregisterClassW(MAKEINTRESOURCEW(atom), NULL); + return 0; + } + + TRACE("created per-process clipboard window hwnd=%p\n", hwnd); + + while (GetMessageW(&msg, 0, 0, 0)) DispatchMessageW(&msg); + return 0; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { DWORD tid; @@ -49,6 +107,9 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
/* Read wayland events from a dedicated thread. */ CloseHandle(CreateThread(NULL, 0, wayland_read_events_thread, NULL, 0, &tid)); + /* Handle clipboard events in a dedicated thread, if needed. */ + if (!WAYLANDDRV_UNIX_CALL(init_clipboard, NULL)) + CloseHandle(CreateThread(NULL, 0, clipboard_thread, NULL, 0, &tid));
return TRUE; } diff --git a/dlls/winewayland.drv/unixlib.h b/dlls/winewayland.drv/unixlib.h index dc3bfdf8893..d9378fe8248 100644 --- a/dlls/winewayland.drv/unixlib.h +++ b/dlls/winewayland.drv/unixlib.h @@ -27,6 +27,7 @@ enum waylanddrv_unix_func { waylanddrv_unix_func_init, waylanddrv_unix_func_read_events, + waylanddrv_unix_func_init_clipboard, waylanddrv_unix_func_count, };
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index ee162d8fe77..2a51222fd5f 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -147,8 +147,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry, pthread_mutex_unlock(&seat->mutex); if (process_wayland.zwp_text_input_manager_v3) wayland_text_input_init(); /* Recreate the data device for the new seat. */ - if (process_wayland.data_device.zwlr_data_control_device_v1) + if (process_wayland.data_device.zwlr_data_control_device_v1 || + process_wayland.data_device.wl_data_device) + { wayland_data_device_init(); + } } else if (strcmp(interface, "wp_viewporter") == 0) { @@ -181,6 +184,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry, process_wayland.zwlr_data_control_manager_v1 = wl_registry_bind(registry, id, &zwlr_data_control_manager_v1_interface, 1); } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + process_wayland.wl_data_device_manager = + wl_registry_bind(registry, id, &wl_data_device_manager_interface, 2); + } }
static void registry_handle_global_remove(void *data, struct wl_registry *registry, diff --git a/dlls/winewayland.drv/wayland_data_device.c b/dlls/winewayland.drv/wayland_data_device.c index 04872df4fcb..472df0a2717 100644 --- a/dlls/winewayland.drv/wayland_data_device.c +++ b/dlls/winewayland.drv/wayland_data_device.c @@ -545,17 +545,29 @@ void wayland_data_device_init(void) TRACE("\n");
pthread_mutex_lock(&data_device->mutex); - if (data_device->zwlr_data_control_device_v1) - zwlr_data_control_device_v1_destroy(data_device->zwlr_data_control_device_v1); - data_device->zwlr_data_control_device_v1 = - zwlr_data_control_manager_v1_get_data_device( - process_wayland.zwlr_data_control_manager_v1, - process_wayland.seat.wl_seat); - if (data_device->zwlr_data_control_device_v1) + if (process_wayland.zwlr_data_control_manager_v1) + { + if (data_device->zwlr_data_control_device_v1) + zwlr_data_control_device_v1_destroy(data_device->zwlr_data_control_device_v1); + data_device->zwlr_data_control_device_v1 = + zwlr_data_control_manager_v1_get_data_device( + process_wayland.zwlr_data_control_manager_v1, + process_wayland.seat.wl_seat); + if (data_device->zwlr_data_control_device_v1) + { + zwlr_data_control_device_v1_add_listener( + data_device->zwlr_data_control_device_v1, &data_control_device_listener, + data_device); + } + } + else if (process_wayland.wl_data_device_manager) { - zwlr_data_control_device_v1_add_listener( - data_device->zwlr_data_control_device_v1, &data_control_device_listener, - data_device); + if (data_device->wl_data_device) + wl_data_device_release(data_device->wl_data_device); + data_device->wl_data_device = + wl_data_device_manager_get_data_device( + process_wayland.wl_data_device_manager, + process_wayland.seat.wl_seat); } pthread_mutex_unlock(&data_device->mutex);
@@ -671,16 +683,35 @@ static void destroy_clipboard(void) pthread_mutex_unlock(&data_device->mutex); }
+static BOOL is_winewayland_clipboard_hwnd(HWND hwnd) +{ + static const WCHAR clipboard_classnameW[] = { + '_','_','w','i','n','e','w','a','y','l','a','n','d','_', + 'c','l','i','p','b','o','a','r','d','_','m','a','n','a','g','e','r'}; + WCHAR buffer[64]; + UNICODE_STRING name = {.Buffer = buffer, .MaximumLength = sizeof(buffer)}; + + if (!NtUserGetClassName(hwnd, FALSE, &name)) return FALSE; + return !wcscmp(buffer, clipboard_classnameW); +} + LRESULT WAYLAND_ClipboardWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_NCCREATE: + /* Disable the default clipboard window in the desktop process if we are + * using the core wl_data_device protocol. */ + if (!process_wayland.zwlr_data_control_manager_v1 && + process_wayland.wl_data_device_manager && + !is_winewayland_clipboard_hwnd(hwnd)) + { + return FALSE; + } clipboard_hwnd = hwnd; NtUserAddClipboardFormatListener(hwnd); pthread_mutex_lock(&process_wayland.seat.mutex); - if (process_wayland.seat.wl_seat && process_wayland.zwlr_data_control_manager_v1) - wayland_data_device_init(); + if (process_wayland.seat.wl_seat) wayland_data_device_init(); pthread_mutex_unlock(&process_wayland.seat.mutex); return TRUE; case WM_CLIPBOARDUPDATE: diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index ead2269b72c..c2fb56dc9bf 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -133,9 +133,19 @@ struct wayland_seat
struct wayland_data_device { - struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1; - struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1; - struct zwlr_data_control_offer_v1 *clipboard_zwlr_data_control_offer_v1; + union + { + struct + { + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1; + struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1; + struct zwlr_data_control_offer_v1 *clipboard_zwlr_data_control_offer_v1; + }; + struct + { + struct wl_data_device *wl_data_device; + }; + }; pthread_mutex_t mutex; };
@@ -155,6 +165,7 @@ struct wayland struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3; struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1; + struct wl_data_device_manager *wl_data_device_manager; struct wayland_seat seat; struct wayland_keyboard keyboard; struct wayland_pointer pointer; diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 1d4ddeb3425..dba519b1df1 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -110,10 +110,20 @@ static NTSTATUS waylanddrv_unix_read_events(void *arg) return STATUS_UNSUCCESSFUL; }
+static NTSTATUS waylanddrv_unix_init_clipboard(void *arg) +{ + /* If the compositor supports zwlr_data_control_manager_v1, we don't need + * per-process clipboard window and handling, we can use the default clipboard + * window from the desktop process. */ + if (process_wayland.zwlr_data_control_manager_v1) return STATUS_UNSUCCESSFUL; + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { waylanddrv_unix_init, waylanddrv_unix_read_events, + waylanddrv_unix_init_clipboard, };
C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == waylanddrv_unix_func_count); @@ -124,6 +134,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { waylanddrv_unix_init, waylanddrv_unix_read_events, + waylanddrv_unix_init_clipboard, };
C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs) == waylanddrv_unix_func_count);
From: Alexandros Frantzis alexandros.frantzis@collabora.com
If the wlr-data-control-unstable-v1 extension is not available use the core data device protocol. --- dlls/winewayland.drv/wayland_data_device.c | 124 ++++++++++++++++++--- dlls/winewayland.drv/wayland_keyboard.c | 8 ++ dlls/winewayland.drv/wayland_pointer.c | 6 + dlls/winewayland.drv/waylanddrv.h | 2 + 4 files changed, 126 insertions(+), 14 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_data_device.c b/dlls/winewayland.drv/wayland_data_device.c index 472df0a2717..c2d5b6e095c 100644 --- a/dlls/winewayland.drv/wayland_data_device.c +++ b/dlls/winewayland.drv/wayland_data_device.c @@ -537,6 +537,64 @@ static const struct zwlr_data_control_device_v1_listener data_control_device_lis data_control_device_finished, };
+/********************************************************************** + * wl_data_source handling + */ + +static void data_source_target(void *data, struct wl_data_source *source, + const char *mime_type) +{ +} + +static void data_source_send(void *data, struct wl_data_source *source, + const char *mime_type, int32_t fd) +{ + struct data_device_format *format; + const char *normalized; + + if ((normalized = normalize_mime_type(mime_type)) && + (format = data_device_format_for_mime_type(normalized))) + { + wayland_data_source_export(format, fd); + } + close(fd); +} + +static void data_source_cancelled(void *data, struct wl_data_source *source) +{ + struct wayland_data_device *data_device = data; + + pthread_mutex_lock(&data_device->mutex); + wl_data_source_destroy(source); + if (source == data_device->wl_data_source) + data_device->wl_data_source = NULL; + pthread_mutex_unlock(&data_device->mutex); +} + +static void data_source_dnd_drop_performed(void *data, + struct wl_data_source *source) +{ +} + +static void data_source_dnd_finished(void *data, struct wl_data_source *source) +{ +} + +static void data_source_action(void *data, struct wl_data_source *source, + uint32_t dnd_action) +{ +} + +static const struct wl_data_source_listener data_source_listener = +{ + data_source_target, + data_source_send, + data_source_cancelled, + data_source_dnd_drop_performed, + data_source_dnd_finished, + data_source_action, +}; + void wayland_data_device_init(void) { struct wayland_data_device *data_device = &process_wayland.data_device; @@ -581,16 +639,33 @@ void wayland_data_device_init(void) static void clipboard_update(void) { struct wayland_data_device *data_device = &process_wayland.data_device; - struct zwlr_data_control_source_v1 *source; + struct zwlr_data_control_source_v1 *zwlr_source = NULL; + struct wl_data_source *wl_source = NULL; UINT *formats, formats_size = 256, i; + uint32_t serial = 0;
- if (!process_wayland.zwlr_data_control_manager_v1) return; + if (process_wayland.zwlr_data_control_manager_v1) + { + zwlr_source = zwlr_data_control_manager_v1_create_data_source( + process_wayland.zwlr_data_control_manager_v1); + } + else + { + serial = InterlockedCompareExchange(&process_wayland.input_serial, 0, 0); + pthread_mutex_lock(&process_wayland.keyboard.mutex); + if (!process_wayland.keyboard.focused_hwnd) serial = 0; + pthread_mutex_unlock(&process_wayland.keyboard.mutex); + if (process_wayland.wl_data_device_manager && serial) + { + wl_source = wl_data_device_manager_create_data_source( + process_wayland.wl_data_device_manager); + } + else return; + }
TRACE("\n");
- source = zwlr_data_control_manager_v1_create_data_source( - process_wayland.zwlr_data_control_manager_v1); - if (!source) + if (!zwlr_source && !wl_source) { ERR("failed to create data source\n"); return; @@ -608,7 +683,8 @@ static void clipboard_update(void) if (!formats && formats_size) { ERR("failed to get clipboard formats\n"); - zwlr_data_control_source_v1_destroy(source); + if (wl_source) wl_data_source_destroy(wl_source); + else zwlr_data_control_source_v1_destroy(zwlr_source); return; }
@@ -619,23 +695,43 @@ static void clipboard_update(void) if (format) { TRACE("offering mime=%s for format=%u\n", format->mime_type, formats[i]); - zwlr_data_control_source_v1_offer(source, format->mime_type); + if (wl_source) wl_data_source_offer(wl_source, format->mime_type); + else zwlr_data_control_source_v1_offer(zwlr_source, format->mime_type); } }
free(formats);
- zwlr_data_control_source_v1_offer(source, WINEWAYLAND_TAG_MIME_TYPE); - zwlr_data_control_source_v1_add_listener(source, &data_control_source_listener, data_device); + if (wl_source) + { + wl_data_source_offer(wl_source, WINEWAYLAND_TAG_MIME_TYPE); + wl_data_source_add_listener(wl_source, &data_source_listener, data_device); + } + else + { + zwlr_data_control_source_v1_offer(zwlr_source, WINEWAYLAND_TAG_MIME_TYPE); + zwlr_data_control_source_v1_add_listener(zwlr_source, &data_control_source_listener, data_device); + }
pthread_mutex_lock(&data_device->mutex); - if (data_device->zwlr_data_control_device_v1) - zwlr_data_control_device_v1_set_selection(data_device->zwlr_data_control_device_v1, source); /* Destroy any previous source only after setting the new source, to * avoid spurious 'selection(nil)' events. */ - if (data_device->zwlr_data_control_source_v1) - zwlr_data_control_source_v1_destroy(data_device->zwlr_data_control_source_v1); - data_device->zwlr_data_control_source_v1 = source; + if (wl_source) + { + if (data_device->wl_data_device) + wl_data_device_set_selection(data_device->wl_data_device, wl_source, serial); + if (data_device->wl_data_source) + wl_data_source_destroy(data_device->wl_data_source); + data_device->wl_data_source = wl_source; + } + else + { + if (data_device->zwlr_data_control_device_v1) + zwlr_data_control_device_v1_set_selection(data_device->zwlr_data_control_device_v1, zwlr_source); + if (data_device->zwlr_data_control_source_v1) + zwlr_data_control_source_v1_destroy(data_device->zwlr_data_control_source_v1); + data_device->zwlr_data_control_source_v1 = zwlr_source; + } pthread_mutex_unlock(&data_device->mutex);
wl_display_flush(process_wayland.wl_display); diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 8f7b6dce30f..39b42231b00 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -745,6 +745,8 @@ static void keyboard_handle_enter(void *private, struct wl_keyboard *wl_keyboard struct wayland_win_data *data; HWND hwnd;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!wl_surface) return;
/* The wl_surface user data remains valid and immutable for the whole @@ -780,6 +782,8 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, struct wayland_keyboard *keyboard = &process_wayland.keyboard; HWND hwnd;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!wl_surface) return;
/* The wl_surface user data remains valid and immutable for the whole @@ -817,6 +821,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, INPUT input = {0}; HWND hwnd;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!(hwnd = wayland_keyboard_get_focused_hwnd())) return;
TRACE_(key)("serial=%u hwnd=%p key=%d scan=%#x state=%#x\n", serial, hwnd, key, scan, state); @@ -840,6 +846,8 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar { struct wayland_keyboard *keyboard = &process_wayland.keyboard;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!wayland_keyboard_get_focused_hwnd()) return;
TRACE("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d stub!\n", diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 17139c47b13..457c3675cf1 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -112,6 +112,8 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, struct wayland_pointer *pointer = &process_wayland.pointer; HWND hwnd;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!wl_surface) return; /* The wl_surface user data remains valid and immutable for the whole * lifetime of the object, so it's safe to access without locking. */ @@ -139,6 +141,8 @@ static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, { struct wayland_pointer *pointer = &process_wayland.pointer;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!wl_surface) return;
TRACE("hwnd=%p\n", wl_surface_get_user_data(wl_surface)); @@ -157,6 +161,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, INPUT input = {0}; HWND hwnd;
+ InterlockedExchange(&process_wayland.input_serial, serial); + if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
input.type = INPUT_MOUSE; diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index c2fb56dc9bf..1649e0f84f3 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -144,6 +144,7 @@ struct wayland_data_device struct { struct wl_data_device *wl_data_device; + struct wl_data_source *wl_data_source; }; }; pthread_mutex_t mutex; @@ -174,6 +175,7 @@ struct wayland struct wl_list output_list; /* Protects the output_list and the wayland_output.current states. */ pthread_mutex_t output_mutex; + LONG input_serial; };
struct wayland_output_mode
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/wayland_data_device.c | 177 +++++++++++++++++---- dlls/winewayland.drv/waylanddrv.h | 1 + 2 files changed, 148 insertions(+), 30 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_data_device.c b/dlls/winewayland.drv/wayland_data_device.c index c2d5b6e095c..e36c0cc9229 100644 --- a/dlls/winewayland.drv/wayland_data_device.c +++ b/dlls/winewayland.drv/wayland_data_device.c @@ -49,7 +49,11 @@ struct data_device_format
struct wayland_data_offer { - struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1; + union + { + struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1; + struct wl_data_offer *wl_data_offer; + }; struct wl_array types; };
@@ -365,7 +369,17 @@ static const struct zwlr_data_control_offer_v1_listener data_control_offer_liste data_control_offer_offer, };
-static void wayland_data_offer_create(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) +static void data_offer_offer(void *data, struct wl_data_offer *wl_data_offer, const char *type) +{ + data_control_offer_offer(data, NULL, type); +} + +static const struct wl_data_offer_listener data_offer_listener = +{ + data_offer_offer, +}; + +static void wayland_data_offer_create(void *offer_proxy) { struct wayland_data_offer *data_offer;
@@ -375,17 +389,30 @@ static void wayland_data_offer_create(struct zwlr_data_control_offer_v1 *zwlr_da return; }
- data_offer->zwlr_data_control_offer_v1 = zwlr_data_control_offer_v1; wl_array_init(&data_offer->types); - zwlr_data_control_offer_v1_add_listener(data_offer->zwlr_data_control_offer_v1, - &data_control_offer_listener, data_offer); + if (process_wayland.zwlr_data_control_manager_v1) + { + data_offer->zwlr_data_control_offer_v1 = offer_proxy; + zwlr_data_control_offer_v1_add_listener(data_offer->zwlr_data_control_offer_v1, + &data_control_offer_listener, data_offer); + } + else + { + data_offer->wl_data_offer = offer_proxy; + wl_data_offer_add_listener(data_offer->wl_data_offer, &data_offer_listener, + data_offer); + + } }
static void wayland_data_offer_destroy(struct wayland_data_offer *data_offer) { char **p;
- zwlr_data_control_offer_v1_destroy(data_offer->zwlr_data_control_offer_v1); + if (process_wayland.zwlr_data_control_manager_v1) + zwlr_data_control_offer_v1_destroy(data_offer->zwlr_data_control_offer_v1); + else + wl_data_offer_destroy(data_offer->wl_data_offer); wl_array_for_each(p, &data_offer->types) free(*p); wl_array_release(&data_offer->types); @@ -410,8 +437,15 @@ static int wayland_data_offer_get_import_fd(struct wayland_data_offer *data_offe fcntl(data_pipe[1], F_SETFD, FD_CLOEXEC); }
- zwlr_data_control_offer_v1_receive(data_offer->zwlr_data_control_offer_v1, - mime_type, data_pipe[1]); + if (process_wayland.zwlr_data_control_manager_v1) + { + zwlr_data_control_offer_v1_receive(data_offer->zwlr_data_control_offer_v1, + mime_type, data_pipe[1]); + } + else + { + wl_data_offer_receive(data_offer->wl_data_offer, mime_type, data_pipe[1]); + } close(data_pipe[1]);
/* Flush to ensure our receive request reaches the server. */ @@ -431,21 +465,31 @@ static void *import_format(int fd, struct data_device_format *format, size_t *re return ret; }
-/********************************************************************** - * zwlr_data_control_device_v1 handling - */ - static void wayland_data_device_destroy_clipboard_data_offer(struct wayland_data_device *data_device) { - if (data_device->clipboard_zwlr_data_control_offer_v1) + struct wayland_data_offer *data_offer = NULL; + + if (process_wayland.zwlr_data_control_manager_v1 && + data_device->clipboard_zwlr_data_control_offer_v1) { - struct wayland_data_offer *data_offer = - zwlr_data_control_offer_v1_get_user_data(data_device->clipboard_zwlr_data_control_offer_v1); - if (data_offer) wayland_data_offer_destroy(data_offer); + data_offer = zwlr_data_control_offer_v1_get_user_data( + data_device->clipboard_zwlr_data_control_offer_v1); data_device->clipboard_zwlr_data_control_offer_v1 = NULL; } + else if (!process_wayland.zwlr_data_control_manager_v1 && + data_device->clipboard_wl_data_offer) + { + data_offer = wl_data_offer_get_user_data(data_device->clipboard_wl_data_offer); + data_device->clipboard_wl_data_offer = NULL; + } + + if (data_offer) wayland_data_offer_destroy(data_offer); }
+/********************************************************************** + * zwlr_data_control_device_v1 handling + */ + static void data_control_device_data_offer( void *data, struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, @@ -454,19 +498,12 @@ static void data_control_device_data_offer( wayland_data_offer_create(zwlr_data_control_offer_v1); }
-static void clipboard_update(void); - -static void data_control_device_selection( - void *data, - struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, - struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) +static void handle_selection(struct wayland_data_device *data_device, + struct wayland_data_offer *data_offer) { - struct wayland_data_device *data_device = data; - struct wayland_data_offer *data_offer = NULL; char **p;
- if (!zwlr_data_control_offer_v1 || - !(data_offer = zwlr_data_control_offer_v1_get_user_data(zwlr_data_control_offer_v1))) + if (!data_offer) { TRACE("empty offer, clearing clipboard\n"); if (NtUserOpenClipboard(clipboard_hwnd, 0)) @@ -521,8 +558,26 @@ static void data_control_device_selection( done: pthread_mutex_lock(&data_device->mutex); wayland_data_device_destroy_clipboard_data_offer(data_device); - if (data_offer) data_device->clipboard_zwlr_data_control_offer_v1 = zwlr_data_control_offer_v1; + if (data_offer) + { + if (process_wayland.zwlr_data_control_manager_v1) + data_device->clipboard_zwlr_data_control_offer_v1 = data_offer->zwlr_data_control_offer_v1; + else + data_device->clipboard_wl_data_offer = data_offer->wl_data_offer; + } pthread_mutex_unlock(&data_device->mutex); + +} + +static void data_control_device_selection( + void *data, + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, + struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) +{ + handle_selection(data, + zwlr_data_control_offer_v1 ? + zwlr_data_control_offer_v1_get_user_data(zwlr_data_control_offer_v1) : + NULL); }
static void data_control_device_finished( @@ -595,6 +650,52 @@ static const struct wl_data_source_listener data_source_listener = data_source_action, };
+/********************************************************************** + * wl_data_device handling + */ + +static void data_device_data_offer(void *data, struct wl_data_device *wl_data_device, + struct wl_data_offer *wl_data_offer) +{ + wayland_data_offer_create(wl_data_offer); +} + +static void data_device_enter(void *data, struct wl_data_device *wl_data_device, + uint32_t serial, struct wl_surface *wl_surface, + wl_fixed_t x_w, wl_fixed_t y_w, + struct wl_data_offer *wl_data_offer) +{ +} + +static void data_device_leave(void *data, struct wl_data_device *wl_data_device) +{ +} + +static void data_device_motion(void *data, struct wl_data_device *wl_data_device, + uint32_t time, wl_fixed_t x_w, wl_fixed_t y_w) +{ +} + +static void data_device_drop(void *data, struct wl_data_device *wl_data_device) +{ +} + +static void data_device_selection(void *data, struct wl_data_device *wl_data_device, + struct wl_data_offer *wl_data_offer) +{ + handle_selection(data, wl_data_offer ? wl_data_offer_get_user_data(wl_data_offer) : NULL); +} + +static const struct wl_data_device_listener data_device_listener = +{ + data_device_data_offer, + data_device_enter, + data_device_leave, + data_device_motion, + data_device_drop, + data_device_selection, +}; + void wayland_data_device_init(void) { struct wayland_data_device *data_device = &process_wayland.data_device; @@ -626,6 +727,11 @@ void wayland_data_device_init(void) wl_data_device_manager_get_data_device( process_wayland.wl_data_device_manager, process_wayland.seat.wl_seat); + if (data_device->wl_data_device) + { + wl_data_device_add_listener(data_device->wl_data_device, + &data_device_listener, data_device); + } } pthread_mutex_unlock(&data_device->mutex);
@@ -740,15 +846,26 @@ static void clipboard_update(void) static void render_format(UINT clipboard_format) { struct wayland_data_device *data_device = &process_wayland.data_device; - struct wayland_data_offer *data_offer; + struct wayland_data_offer *data_offer = NULL; struct data_device_format *format; int import_fd = -1;
TRACE("clipboard_format=%u\n", clipboard_format);
pthread_mutex_lock(&data_device->mutex); - if (data_device->clipboard_zwlr_data_control_offer_v1 && - (data_offer = zwlr_data_control_offer_v1_get_user_data(data_device->clipboard_zwlr_data_control_offer_v1)) && + if (process_wayland.zwlr_data_control_manager_v1 && + data_device->clipboard_zwlr_data_control_offer_v1) + { + data_offer = zwlr_data_control_offer_v1_get_user_data( + data_device->clipboard_zwlr_data_control_offer_v1); + } + else if (!process_wayland.zwlr_data_control_manager_v1 && + data_device->clipboard_wl_data_offer) + { + data_offer = wl_data_offer_get_user_data(data_device->clipboard_wl_data_offer); + } + + if (data_offer && (format = data_device_format_for_clipboard_format(clipboard_format, &data_offer->types))) { diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 1649e0f84f3..065d4d31873 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -145,6 +145,7 @@ struct wayland_data_device { struct wl_data_device *wl_data_device; struct wl_data_source *wl_data_source; + struct wl_data_offer *clipboard_wl_data_offer; }; }; pthread_mutex_t mutex;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/wayland.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 2a51222fd5f..7caf33c872e 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -311,6 +311,14 @@ BOOL wayland_process_init(void) if (!process_wayland.zwp_text_input_manager_v3) ERR("Wayland compositor doesn't support optional zwp_text_input_manager_v3 (host input methods won't work)\n");
+ if (!process_wayland.zwlr_data_control_manager_v1) + { + if (!process_wayland.wl_data_device_manager) + ERR("Wayland compositor doesn't support optional wl_data_device_manager (clipboard won't work)\n"); + else + ERR("Wayland compositor doesn't support optional zwlr_data_control_manager_v1 (clipboard functionality will be limited)\n"); + } + process_wayland.initialized = TRUE;
return TRUE;