From fa573553bc52154487396d9d0009a5d8a36bbbcc Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Sun, 23 Oct 2016 13:03:32 -0500 Subject: [PATCH] winemac: Run a single clipboard manager thread per window station, inside the explorer process. Signed-off-by: Ken Thomases Signed-off-by: Alexandre Julliard --- dlls/winemac.drv/clipboard.c | 377 +++++++++++++++++++++++++++++++++++------ dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/macdrv_main.c | 2 - dlls/winemac.drv/window.c | 2 + 4 files changed, 325 insertions(+), 58 deletions(-) diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c index eba426a3ce1..936fa219864 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,58 +1500,260 @@ UINT* macdrv_get_pasteboard_formats(CFTypeRef pasteboard, UINT* num_formats) /************************************************************************** - * Mac User Driver Clipboard Exports - **************************************************************************/ + * 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(); -/************************************************************************** - * MACDRV Private Clipboard Exports - **************************************************************************/ + 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; +} /************************************************************************** - * macdrv_clipboard_process_attach + * set_mac_pasteboard_types_from_win32_clipboard */ -void macdrv_clipboard_process_attach(void) +static void set_mac_pasteboard_types_from_win32_clipboard(void) { - UINT i; WINE_CLIPFORMAT *format; + UINT count, i, *formats; - /* Register built-in formats */ - for (i = 0; i < sizeof(builtin_format_ids)/sizeof(builtin_format_ids[0]); i++) + if (!(formats = get_clipboard_formats(&count))) return; + + macdrv_clear_pasteboard(); + + for (i = 0; i < count; 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->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); + } } - LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry) + 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++) { - if (format->synthesized) - format->natural_format = natural_format_for_format(format->format_id); + TRACE("adding format %s\n", debugstr_format(formats[i]->format_id)); + SetClipboardData(formats[i]->format_id, 0); } - /* Register known mappings between Windows formats and Mac types */ - for (i = 0; i < sizeof(builtin_format_names)/sizeof(builtin_format_names[0]); i++) + 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++) { - 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); + 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 + **************************************************************************/ + + +/************************************************************************** + * MACDRV Private Clipboard Exports + **************************************************************************/ + + +/************************************************************************** * 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 c29ea15f781..8afd0645614 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -194,7 +194,7 @@ extern HKL macdrv_get_hkl_from_source(TISInputSourceRef input_source) DECLSPEC_H 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 3b11751f942..491ab06ac34 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 0359ef767f4..07d0f2323b6 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.11.4.GIT