mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-06 15:04:44 +00:00
winemac: Run a single clipboard manager thread per window station, inside the explorer process.
Signed-off-by: Ken Thomases <ken@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
782959168a
commit
fa573553bc
|
@ -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
|
||||
|
@ -301,6 +309,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
|
||||
*/
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -1447,6 +1499,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
|
||||
**************************************************************************/
|
||||
|
@ -1457,48 +1753,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
|
||||
*/
|
||||
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -283,8 +283,6 @@ static BOOL process_attach(void)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
macdrv_clipboard_process_attach();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1480,6 +1480,8 @@ BOOL CDECL macdrv_CreateDesktopWindow(HWND hwnd)
|
|||
*/
|
||||
BOOL CDECL macdrv_CreateWindow(HWND hwnd)
|
||||
{
|
||||
if (hwnd == GetDesktopWindow())
|
||||
macdrv_init_clipboard();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue