[PATCH 13/15] winemac: Run a single clipboard manager thread per window station, inside the explorer process.
Ken Thomases
ken at codeweavers.com
Sun Oct 23 13:03:32 CDT 2016
Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
dlls/winemac.drv/clipboard.c | 399 ++++++++++++++++++++++++++++++++++-------
dlls/winemac.drv/macdrv.h | 2 +-
dlls/winemac.drv/macdrv_main.c | 2 -
dlls/winemac.drv/window.c | 2 +
4 files changed, 336 insertions(+), 69 deletions(-)
diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c
index eba426a..936fa21 100644
--- a/dlls/winemac.drv/clipboard.c
+++ b/dlls/winemac.drv/clipboard.c
@@ -188,6 +188,14 @@ static const struct
/* The prefix prepended to a Win32 clipboard format name to make a Mac pasteboard type. */
static const CFStringRef registered_name_type_prefix = CFSTR("org.winehq.registered.");
+static DWORD clipboard_thread_id;
+static HWND clipboard_hwnd;
+static BOOL is_clipboard_owner;
+static macdrv_window clipboard_cocoa_window;
+static ULONG64 last_clipboard_update;
+static WINE_CLIPFORMAT **current_mac_formats;
+static unsigned int nb_current_mac_formats;
+
/**************************************************************************
* Internal Clipboard implementation methods
@@ -302,6 +310,69 @@ static WINE_CLIPFORMAT* register_format(UINT id, CFStringRef type)
/**************************************************************************
+ * natural_format_for_format
+ *
+ * Find the "natural" format for this format_id (the one which isn't
+ * synthesized from another type).
+ */
+static WINE_CLIPFORMAT* natural_format_for_format(UINT format_id)
+{
+ WINE_CLIPFORMAT *format;
+
+ LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
+ if (format->format_id == format_id && !format->synthesized) break;
+
+ if (&format->entry == &format_list)
+ format = NULL;
+
+ TRACE("%s -> %p/%s\n", debugstr_format(format_id), format, debugstr_cf(format ? format->type : NULL));
+ return format;
+}
+
+
+/**************************************************************************
+ * register_builtin_formats
+ */
+static void register_builtin_formats(void)
+{
+ UINT i;
+ WINE_CLIPFORMAT *format;
+
+ /* Register built-in formats */
+ for (i = 0; i < sizeof(builtin_format_ids)/sizeof(builtin_format_ids[0]); i++)
+ {
+ if (!(format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)))) break;
+ format->format_id = builtin_format_ids[i].id;
+ format->type = CFRetain(builtin_format_ids[i].type);
+ format->import_func = builtin_format_ids[i].import;
+ format->export_func = builtin_format_ids[i].export;
+ format->synthesized = builtin_format_ids[i].synthesized;
+ format->natural_format = NULL;
+ list_add_tail(&format_list, &format->entry);
+ }
+
+ LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
+ {
+ if (format->synthesized)
+ format->natural_format = natural_format_for_format(format->format_id);
+ }
+
+ /* Register known mappings between Windows formats and Mac types */
+ for (i = 0; i < sizeof(builtin_format_names)/sizeof(builtin_format_names[0]); i++)
+ {
+ if (!(format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)))) break;
+ format->format_id = RegisterClipboardFormatW(builtin_format_names[i].name);
+ format->type = CFRetain(builtin_format_names[i].type);
+ format->import_func = builtin_format_names[i].import;
+ format->export_func = builtin_format_names[i].export;
+ format->synthesized = FALSE;
+ format->natural_format = NULL;
+ list_add_tail(&format_list, &format->entry);
+ }
+}
+
+
+/**************************************************************************
* format_for_type
*/
static WINE_CLIPFORMAT* format_for_type(CFStringRef type)
@@ -310,6 +381,8 @@ static WINE_CLIPFORMAT* format_for_type(CFStringRef type)
TRACE("type %s\n", debugstr_cf(type));
+ if (list_empty(&format_list)) register_builtin_formats();
+
LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
{
if (CFEqual(format->type, type))
@@ -345,27 +418,6 @@ done:
}
-/**************************************************************************
- * natural_format_for_format
- *
- * Find the "natural" format for this format_id (the one which isn't
- * synthesized from another type).
- */
-static WINE_CLIPFORMAT* natural_format_for_format(UINT format_id)
-{
- WINE_CLIPFORMAT *format;
-
- LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
- if (format->format_id == format_id && !format->synthesized) break;
-
- if (&format->entry == &format_list)
- format = NULL;
-
- TRACE("%s -> %p/%s\n", debugstr_format(format_id), format, debugstr_cf(format ? format->type : NULL));
- return format;
-}
-
-
/***********************************************************************
* bitmap_info_size
*
@@ -1448,6 +1500,250 @@ UINT* macdrv_get_pasteboard_formats(CFTypeRef pasteboard, UINT* num_formats)
/**************************************************************************
+ * register_win32_formats
+ *
+ * Register Win32 clipboard formats the first time we encounter them.
+ */
+static void register_win32_formats(const UINT *ids, UINT size)
+{
+ unsigned int i;
+
+ if (list_empty(&format_list)) register_builtin_formats();
+
+ for (i = 0; i < size; i++)
+ register_format(ids[i], NULL);
+}
+
+
+/***********************************************************************
+ * get_clipboard_formats
+ *
+ * Return a list of all formats currently available on the Win32 clipboard.
+ * Helper for set_mac_pasteboard_types_from_win32_clipboard.
+ */
+static UINT *get_clipboard_formats(UINT *size)
+{
+ UINT *ids;
+
+ *size = 256;
+ for (;;)
+ {
+ if (!(ids = HeapAlloc(GetProcessHeap(), 0, *size * sizeof(*ids)))) return NULL;
+ if (GetUpdatedClipboardFormats(ids, *size, size)) break;
+ HeapFree(GetProcessHeap(), 0, ids);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return NULL;
+ }
+ register_win32_formats(ids, *size);
+ return ids;
+}
+
+
+/**************************************************************************
+ * set_mac_pasteboard_types_from_win32_clipboard
+ */
+static void set_mac_pasteboard_types_from_win32_clipboard(void)
+{
+ WINE_CLIPFORMAT *format;
+ UINT count, i, *formats;
+
+ if (!(formats = get_clipboard_formats(&count))) return;
+
+ macdrv_clear_pasteboard();
+
+ for (i = 0; i < count; i++)
+ {
+ LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
+ {
+ if (format->format_id != formats[i]) continue;
+ TRACE("%s -> %s\n", debugstr_format(format->format_id), debugstr_cf(format->type));
+ macdrv_set_pasteboard_data(format->type, NULL, clipboard_cocoa_window);
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, formats);
+ return;
+}
+
+
+/**************************************************************************
+ * set_win32_clipboard_formats_from_mac_pasteboard
+ */
+static void set_win32_clipboard_formats_from_mac_pasteboard(void)
+{
+ WINE_CLIPFORMAT** formats;
+ UINT count, i;
+
+ formats = get_formats_for_pasteboard(NULL, &count);
+ if (!formats)
+ return;
+
+ for (i = 0; i < count; i++)
+ {
+ TRACE("adding format %s\n", debugstr_format(formats[i]->format_id));
+ SetClipboardData(formats[i]->format_id, 0);
+ }
+
+ HeapFree(GetProcessHeap(), 0, current_mac_formats);
+ current_mac_formats = formats;
+ nb_current_mac_formats = count;
+}
+
+
+/**************************************************************************
+ * render_format
+ */
+static void render_format(UINT id)
+{
+ unsigned int i;
+
+ for (i = 0; i < nb_current_mac_formats; i++)
+ {
+ CFDataRef pasteboard_data;
+
+ if (current_mac_formats[i]->format_id != id) continue;
+
+ pasteboard_data = macdrv_copy_pasteboard_data(NULL, current_mac_formats[i]->type);
+ if (pasteboard_data)
+ {
+ HANDLE handle = current_mac_formats[i]->import_func(pasteboard_data);
+ CFRelease(pasteboard_data);
+ if (handle) SetClipboardData(id, handle);
+ break;
+ }
+ }
+}
+
+
+/**************************************************************************
+ * grab_win32_clipboard
+ *
+ * Grab the Win32 clipboard when a Mac app has taken ownership of the
+ * pasteboard, and fill it with the pasteboard data types.
+ */
+static BOOL grab_win32_clipboard(void)
+{
+ if (!OpenClipboard(clipboard_hwnd)) return FALSE;
+ EmptyClipboard();
+ is_clipboard_owner = TRUE;
+ last_clipboard_update = GetTickCount64();
+ set_win32_clipboard_formats_from_mac_pasteboard();
+ CloseClipboard();
+ return TRUE;
+}
+
+
+/**************************************************************************
+ * clipboard_wndproc
+ *
+ * Window procedure for the clipboard manager.
+ */
+static LRESULT CALLBACK clipboard_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ switch (msg)
+ {
+ case WM_NCCREATE:
+ return TRUE;
+ case WM_CLIPBOARDUPDATE:
+ if (is_clipboard_owner) break; /* ignore our own changes */
+ set_mac_pasteboard_types_from_win32_clipboard();
+ break;
+ case WM_RENDERFORMAT:
+ render_format(wp);
+ break;
+ case WM_DESTROYCLIPBOARD:
+ TRACE("WM_DESTROYCLIPBOARD: lost ownership\n");
+ is_clipboard_owner = FALSE;
+ break;
+ }
+ return DefWindowProcW(hwnd, msg, wp, lp);
+}
+
+
+/**************************************************************************
+ * wait_clipboard_mutex
+ *
+ * Make sure that there's only one clipboard thread per window station.
+ */
+static BOOL wait_clipboard_mutex(void)
+{
+ static const WCHAR prefix[] = {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_'};
+ WCHAR buffer[MAX_PATH + sizeof(prefix) / sizeof(WCHAR)];
+ HANDLE mutex;
+
+ memcpy(buffer, prefix, sizeof(prefix));
+ if (!GetUserObjectInformationW(GetProcessWindowStation(), UOI_NAME,
+ buffer + sizeof(prefix) / sizeof(WCHAR),
+ sizeof(buffer) - sizeof(prefix), NULL))
+ {
+ ERR("failed to get winstation name\n");
+ return FALSE;
+ }
+ mutex = CreateMutexW(NULL, TRUE, buffer);
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ TRACE("waiting for mutex %s\n", debugstr_w(buffer));
+ WaitForSingleObject(mutex, INFINITE);
+ }
+ return TRUE;
+}
+
+
+/**************************************************************************
+ * clipboard_thread
+ *
+ * Thread running inside the desktop process to manage the clipboard
+ */
+static DWORD WINAPI clipboard_thread(void *arg)
+{
+ static const WCHAR clipboard_classname[] = {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_','m','a','n','a','g','e','r',0};
+ WNDCLASSW class;
+ struct macdrv_window_features wf;
+ MSG msg;
+
+ if (!wait_clipboard_mutex()) return 0;
+
+ memset(&class, 0, sizeof(class));
+ class.lpfnWndProc = clipboard_wndproc;
+ class.lpszClassName = clipboard_classname;
+
+ if (!RegisterClassW(&class) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
+ {
+ ERR("could not register clipboard window class err %u\n", GetLastError());
+ return 0;
+ }
+ if (!(clipboard_hwnd = CreateWindowW(clipboard_classname, NULL, 0, 0, 0, 0, 0,
+ HWND_MESSAGE, 0, 0, NULL)))
+ {
+ ERR("failed to create clipboard window err %u\n", GetLastError());
+ return 0;
+ }
+
+ memset(&wf, 0, sizeof(wf));
+ clipboard_cocoa_window = macdrv_create_cocoa_window(&wf, CGRectMake(100, 100, 100, 100), clipboard_hwnd,
+ macdrv_init_thread_data()->queue);
+ if (!clipboard_cocoa_window)
+ {
+ ERR("failed to create clipboard Cocoa window\n");
+ goto done;
+ }
+
+ clipboard_thread_id = GetCurrentThreadId();
+ AddClipboardFormatListener(clipboard_hwnd);
+ register_builtin_formats();
+ grab_win32_clipboard();
+
+ TRACE("clipboard thread %04x running\n", GetCurrentThreadId());
+ while (GetMessageW(&msg, NULL, 0, 0))
+ DispatchMessageW(&msg);
+
+done:
+ macdrv_destroy_cocoa_window(clipboard_cocoa_window);
+ DestroyWindow(clipboard_hwnd);
+ return 0;
+}
+
+
+/**************************************************************************
* Mac User Driver Clipboard Exports
**************************************************************************/
@@ -1458,48 +1754,6 @@ UINT* macdrv_get_pasteboard_formats(CFTypeRef pasteboard, UINT* num_formats)
/**************************************************************************
- * macdrv_clipboard_process_attach
- */
-void macdrv_clipboard_process_attach(void)
-{
- UINT i;
- WINE_CLIPFORMAT *format;
-
- /* Register built-in formats */
- for (i = 0; i < sizeof(builtin_format_ids)/sizeof(builtin_format_ids[0]); i++)
- {
- if (!(format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)))) break;
- format->format_id = builtin_format_ids[i].id;
- format->type = CFRetain(builtin_format_ids[i].type);
- format->import_func = builtin_format_ids[i].import;
- format->export_func = builtin_format_ids[i].export;
- format->synthesized = builtin_format_ids[i].synthesized;
- format->natural_format = NULL;
- list_add_tail(&format_list, &format->entry);
- }
-
- LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
- {
- if (format->synthesized)
- format->natural_format = natural_format_for_format(format->format_id);
- }
-
- /* Register known mappings between Windows formats and Mac types */
- for (i = 0; i < sizeof(builtin_format_names)/sizeof(builtin_format_names[0]); i++)
- {
- if (!(format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)))) break;
- format->format_id = RegisterClipboardFormatW(builtin_format_names[i].name);
- format->type = CFRetain(builtin_format_names[i].type);
- format->import_func = builtin_format_names[i].import;
- format->export_func = builtin_format_names[i].export;
- format->synthesized = FALSE;
- format->natural_format = NULL;
- list_add_tail(&format_list, &format->entry);
- }
-}
-
-
-/**************************************************************************
* query_pasteboard_data
*/
BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
@@ -1508,12 +1762,12 @@ BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
BOOL ret = FALSE;
HANDLE handle;
- TRACE("win %p type %s\n", hwnd, debugstr_cf(type));
+ TRACE("win %p/%p type %s\n", hwnd, clipboard_cocoa_window, debugstr_cf(type));
format = format_for_type(type);
if (!format) return FALSE;
- if (!OpenClipboard(NULL))
+ if (!OpenClipboard(clipboard_hwnd))
{
ERR("failed to open clipboard for %s\n", debugstr_cf(type));
return FALSE;
@@ -1527,7 +1781,7 @@ BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
if ((data = format->export_func(handle)))
{
- ret = macdrv_set_pasteboard_data(format->type, data, macdrv_get_cocoa_window(hwnd, FALSE));
+ ret = macdrv_set_pasteboard_data(format->type, data, clipboard_cocoa_window);
CFRelease(data);
}
}
@@ -1536,3 +1790,16 @@ BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
return ret;
}
+
+
+/**************************************************************************
+ * macdrv_init_clipboard
+ */
+void macdrv_init_clipboard(void)
+{
+ DWORD id;
+ HANDLE handle = CreateThread(NULL, 0, clipboard_thread, NULL, 0, &id);
+
+ if (handle) CloseHandle(handle);
+ else ERR("failed to create clipboard thread\n");
+}
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h
index c29ea15..8afd064 100644
--- a/dlls/winemac.drv/macdrv.h
+++ b/dlls/winemac.drv/macdrv.h
@@ -194,7 +194,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
extern void macdrv_displays_changed(const macdrv_event *event) DECLSPEC_HIDDEN;
-extern void macdrv_clipboard_process_attach(void) DECLSPEC_HIDDEN;
+extern void macdrv_init_clipboard(void) DECLSPEC_HIDDEN;
extern BOOL query_pasteboard_data(HWND hwnd, CFStringRef type) DECLSPEC_HIDDEN;
extern const char *debugstr_format(UINT id) DECLSPEC_HIDDEN;
extern HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_format) DECLSPEC_HIDDEN;
diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c
index 3b11751..491ab06 100644
--- a/dlls/winemac.drv/macdrv_main.c
+++ b/dlls/winemac.drv/macdrv_main.c
@@ -283,8 +283,6 @@ static BOOL process_attach(void)
return FALSE;
}
- macdrv_clipboard_process_attach();
-
return TRUE;
}
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 0359ef7..07d0f23 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1480,6 +1480,8 @@ BOOL CDECL macdrv_CreateDesktopWindow(HWND hwnd)
*/
BOOL CDECL macdrv_CreateWindow(HWND hwnd)
{
+ if (hwnd == GetDesktopWindow())
+ macdrv_init_clipboard();
return TRUE;
}
--
2.8.2
More information about the wine-patches
mailing list