winemac: Implement setting of clipboard data with support for text formats.

This commit is contained in:
Ken Thomases 2013-03-10 22:58:21 -05:00 committed by Alexandre Julliard
parent 764a8edb09
commit bf824ed38c
4 changed files with 444 additions and 12 deletions

View File

@ -27,6 +27,7 @@
#include "macdrv.h" #include "macdrv.h"
#include "winuser.h" #include "winuser.h"
#include "wine/list.h" #include "wine/list.h"
#include "wine/server.h"
#include "wine/unicode.h" #include "wine/unicode.h"
@ -37,7 +38,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
* Types * Types
**************************************************************************/ **************************************************************************/
typedef struct
{
HWND hwnd_owner;
UINT flags;
} CLIPBOARDINFO, *LPCLIPBOARDINFO;
typedef HANDLE (*DRVIMPORTFUNC)(CFDataRef data); typedef HANDLE (*DRVIMPORTFUNC)(CFDataRef data);
typedef CFDataRef (*DRVEXPORTFUNC)(HANDLE data);
typedef struct typedef struct
{ {
@ -45,6 +53,7 @@ typedef struct
UINT format_id; UINT format_id;
CFStringRef type; CFStringRef type;
DRVIMPORTFUNC import_func; DRVIMPORTFUNC import_func;
DRVEXPORTFUNC export_func;
BOOL synthesized; BOOL synthesized;
} WINE_CLIPFORMAT; } WINE_CLIPFORMAT;
@ -69,6 +78,11 @@ static HANDLE import_utf8_to_oemtext(CFDataRef data);
static HANDLE import_utf8_to_text(CFDataRef data); static HANDLE import_utf8_to_text(CFDataRef data);
static HANDLE import_utf8_to_unicodetext(CFDataRef data); static HANDLE import_utf8_to_unicodetext(CFDataRef data);
static CFDataRef export_clipboard_data(HANDLE data);
static CFDataRef export_oemtext_to_utf8(HANDLE data);
static CFDataRef export_text_to_utf8(HANDLE data);
static CFDataRef export_unicodetext_to_utf8(HANDLE data);
/************************************************************************** /**************************************************************************
* Static Variables * Static Variables
@ -123,24 +137,25 @@ static const struct
UINT id; UINT id;
CFStringRef type; CFStringRef type;
DRVIMPORTFUNC import; DRVIMPORTFUNC import;
DRVEXPORTFUNC export;
BOOL synthesized; BOOL synthesized;
} builtin_format_ids[] = } builtin_format_ids[] =
{ {
{ CF_UNICODETEXT, CFSTR("org.winehq.builtin.unicodetext"), import_clipboard_data, FALSE }, { CF_UNICODETEXT, CFSTR("org.winehq.builtin.unicodetext"), import_clipboard_data, export_clipboard_data, FALSE },
{ CF_TEXT, CFSTR("org.winehq.builtin.unicodetext"), import_unicodetext_to_text, TRUE }, { CF_TEXT, CFSTR("org.winehq.builtin.unicodetext"), import_unicodetext_to_text, NULL, TRUE },
{ CF_OEMTEXT, CFSTR("org.winehq.builtin.unicodetext"), import_unicodetext_to_oemtext, TRUE }, { CF_OEMTEXT, CFSTR("org.winehq.builtin.unicodetext"), import_unicodetext_to_oemtext, NULL, TRUE },
{ CF_TEXT, CFSTR("org.winehq.builtin.text"), import_clipboard_data, FALSE }, { CF_TEXT, CFSTR("org.winehq.builtin.text"), import_clipboard_data, export_clipboard_data, FALSE },
{ CF_UNICODETEXT, CFSTR("org.winehq.builtin.text"), import_text_to_unicodetext, TRUE }, { CF_UNICODETEXT, CFSTR("org.winehq.builtin.text"), import_text_to_unicodetext, NULL, TRUE },
{ CF_OEMTEXT, CFSTR("org.winehq.builtin.text"), import_text_to_oemtext, TRUE }, { CF_OEMTEXT, CFSTR("org.winehq.builtin.text"), import_text_to_oemtext, NULL, TRUE },
{ CF_OEMTEXT, CFSTR("org.winehq.builtin.oemtext"), import_clipboard_data, FALSE }, { CF_OEMTEXT, CFSTR("org.winehq.builtin.oemtext"), import_clipboard_data, export_clipboard_data, FALSE },
{ CF_UNICODETEXT, CFSTR("org.winehq.builtin.oemtext"), import_oemtext_to_unicodetext, TRUE }, { CF_UNICODETEXT, CFSTR("org.winehq.builtin.oemtext"), import_oemtext_to_unicodetext, NULL, TRUE },
{ CF_TEXT, CFSTR("org.winehq.builtin.oemtext"), import_oemtext_to_text, TRUE }, { CF_TEXT, CFSTR("org.winehq.builtin.oemtext"), import_oemtext_to_text, NULL, TRUE },
{ CF_UNICODETEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_unicodetext, TRUE }, { CF_UNICODETEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_unicodetext, export_unicodetext_to_utf8, TRUE },
{ CF_TEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_text, TRUE }, { CF_TEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_text, export_text_to_utf8, TRUE },
{ CF_OEMTEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_oemtext, TRUE }, { CF_OEMTEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_oemtext, export_oemtext_to_utf8, TRUE },
}; };
/* The prefix prepended to an external Mac pasteboard type to make a Win32 clipboard format name. org.winehq.mac-type. */ /* The prefix prepended to an external Mac pasteboard type to make a Win32 clipboard format name. org.winehq.mac-type. */
@ -215,6 +230,7 @@ static WINE_CLIPFORMAT *insert_clipboard_format(UINT id, CFStringRef type)
} }
format->format_id = id; format->format_id = id;
format->import_func = import_clipboard_data; format->import_func = import_clipboard_data;
format->export_func = export_clipboard_data;
format->synthesized = FALSE; format->synthesized = FALSE;
if (type) if (type)
@ -586,11 +602,216 @@ static HANDLE import_utf8_to_unicodetext(CFDataRef data)
} }
/**************************************************************************
* export_clipboard_data
*
* Generic export clipboard data routine.
*/
static CFDataRef export_clipboard_data(HANDLE data)
{
CFDataRef ret;
UINT len;
LPVOID src;
len = GlobalSize(data);
src = GlobalLock(data);
if (!src) return NULL;
ret = CFDataCreate(NULL, src, len);
GlobalUnlock(data);
return ret;
}
/**************************************************************************
* export_codepage_to_utf8
*
* Export string data in a specified codepage to UTF-8.
*/
static CFDataRef export_codepage_to_utf8(HANDLE data, UINT cp)
{
CFDataRef ret = NULL;
const char* str;
if ((str = GlobalLock(data)))
{
HANDLE unicode = convert_text(str, GlobalSize(data), cp, -1);
ret = export_unicodetext_to_utf8(unicode);
GlobalFree(unicode);
GlobalUnlock(data);
}
return ret;
}
/**************************************************************************
* export_oemtext_to_utf8
*
* Export CF_OEMTEXT to UTF-8.
*/
static CFDataRef export_oemtext_to_utf8(HANDLE data)
{
return export_codepage_to_utf8(data, CP_OEMCP);
}
/**************************************************************************
* export_text_to_utf8
*
* Export CF_TEXT to UTF-8.
*/
static CFDataRef export_text_to_utf8(HANDLE data)
{
return export_codepage_to_utf8(data, CP_ACP);
}
/**************************************************************************
* export_unicodetext_to_utf8
*
* Export CF_UNICODETEXT to UTF-8.
*/
static CFDataRef export_unicodetext_to_utf8(HANDLE data)
{
CFMutableDataRef ret;
LPVOID src;
INT dst_len;
src = GlobalLock(data);
if (!src) return NULL;
dst_len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
if (dst_len) dst_len--; /* Leave off null terminator. */
ret = CFDataCreateMutable(NULL, dst_len);
if (ret)
{
LPSTR dst;
int i, j;
CFDataSetLength(ret, dst_len);
dst = (LPSTR)CFDataGetMutableBytePtr(ret);
WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_len, NULL, NULL);
/* Remove carriage returns */
for (i = 0, j = 0; i < dst_len; i++)
{
if (dst[i] == '\r' &&
(i + 1 >= dst_len || dst[i + 1] == '\n' || dst[i + 1] == '\0'))
continue;
dst[j++] = dst[i];
}
CFDataSetLength(ret, j);
}
GlobalUnlock(data);
return ret;
}
/**************************************************************************
* get_clipboard_info
*/
static BOOL get_clipboard_info(LPCLIPBOARDINFO cbinfo)
{
BOOL ret = FALSE;
SERVER_START_REQ(set_clipboard_info)
{
req->flags = 0;
if (wine_server_call_err(req))
{
ERR("Failed to get clipboard owner.\n");
}
else
{
cbinfo->hwnd_owner = wine_server_ptr_handle(reply->old_owner);
cbinfo->flags = reply->flags;
ret = TRUE;
}
}
SERVER_END_REQ;
return ret;
}
/**************************************************************************
* release_ownership
*/
static BOOL release_ownership(void)
{
BOOL ret = FALSE;
SERVER_START_REQ(set_clipboard_info)
{
req->flags = SET_CB_RELOWNER | SET_CB_SEQNO;
if (wine_server_call_err(req))
ERR("Failed to set clipboard.\n");
else
ret = TRUE;
}
SERVER_END_REQ;
return ret;
}
/**************************************************************************
* check_clipboard_ownership
*/
static void check_clipboard_ownership(HWND *owner)
{
CLIPBOARDINFO cbinfo;
if (owner) *owner = NULL;
/* If Wine thinks we're the clipboard owner but Mac OS X thinks we're not
the pasteboard owner, update Wine. */
if (get_clipboard_info(&cbinfo) && (cbinfo.flags & CB_PROCESS))
{
if (!(cbinfo.flags & CB_OPEN) && !macdrv_is_pasteboard_owner())
{
TRACE("Lost clipboard ownership\n");
if (OpenClipboard(cbinfo.hwnd_owner))
{
/* Destroy private objects */
SendMessageW(cbinfo.hwnd_owner, WM_DESTROYCLIPBOARD, 0, 0);
/* Give up ownership of the windows clipboard */
release_ownership();
CloseClipboard();
}
}
else if (owner)
*owner = cbinfo.hwnd_owner;
}
}
/************************************************************************** /**************************************************************************
* Mac User Driver Clipboard Exports * Mac User Driver Clipboard Exports
**************************************************************************/ **************************************************************************/
/**************************************************************************
* AcquireClipboard (MACDRV.@)
*/
int CDECL macdrv_AcquireClipboard(HWND hwnd)
{
TRACE("hwnd %p\n", hwnd);
check_clipboard_ownership(NULL);
return 0;
}
/************************************************************************** /**************************************************************************
* CountClipboardFormats (MACDRV.@) * CountClipboardFormats (MACDRV.@)
*/ */
@ -603,6 +824,7 @@ INT CDECL macdrv_CountClipboardFormats(void)
INT ret = 0; INT ret = 0;
TRACE("()\n"); TRACE("()\n");
check_clipboard_ownership(NULL);
seen_formats = CFSetCreateMutable(NULL, 0, NULL); seen_formats = CFSetCreateMutable(NULL, 0, NULL);
if (!seen_formats) if (!seen_formats)
@ -646,6 +868,28 @@ INT CDECL macdrv_CountClipboardFormats(void)
} }
/**************************************************************************
* EmptyClipboard (MACDRV.@)
*
* Empty cached clipboard data.
*/
void CDECL macdrv_EmptyClipboard(BOOL keepunowned)
{
TRACE("keepunowned %d\n", keepunowned);
macdrv_clear_pasteboard();
}
/**************************************************************************
* EndClipboardUpdate (MACDRV.@)
*/
void CDECL macdrv_EndClipboardUpdate(void)
{
TRACE("()\n");
check_clipboard_ownership(NULL);
}
/************************************************************************** /**************************************************************************
* EnumClipboardFormats (MACDRV.@) * EnumClipboardFormats (MACDRV.@)
*/ */
@ -657,6 +901,7 @@ UINT CDECL macdrv_EnumClipboardFormats(UINT prev_format)
UINT ret; UINT ret;
TRACE("prev_format %s\n", debugstr_format(prev_format)); TRACE("prev_format %s\n", debugstr_format(prev_format));
check_clipboard_ownership(NULL);
types = macdrv_copy_pasteboard_types(); types = macdrv_copy_pasteboard_types();
if (!types) if (!types)
@ -750,6 +995,7 @@ HANDLE CDECL macdrv_GetClipboardData(UINT desired_format)
HANDLE data = NULL; HANDLE data = NULL;
TRACE("desired_format %s\n", debugstr_format(desired_format)); TRACE("desired_format %s\n", debugstr_format(desired_format));
check_clipboard_ownership(NULL);
types = macdrv_copy_pasteboard_types(); types = macdrv_copy_pasteboard_types();
if (!types) if (!types)
@ -815,6 +1061,7 @@ BOOL CDECL macdrv_IsClipboardFormatAvailable(UINT desired_format)
BOOL found = FALSE; BOOL found = FALSE;
TRACE("desired_format %s\n", debugstr_format(desired_format)); TRACE("desired_format %s\n", debugstr_format(desired_format));
check_clipboard_ownership(NULL);
types = macdrv_copy_pasteboard_types(); types = macdrv_copy_pasteboard_types();
if (!types) if (!types)
@ -847,6 +1094,106 @@ BOOL CDECL macdrv_IsClipboardFormatAvailable(UINT desired_format)
} }
/**************************************************************************
* SetClipboardData (MACDRV.@)
*/
BOOL CDECL macdrv_SetClipboardData(UINT format_id, HANDLE data, BOOL owner)
{
HWND hwnd_owner;
macdrv_window window;
WINE_CLIPFORMAT *format;
CFDataRef cfdata;
check_clipboard_ownership(&hwnd_owner);
window = macdrv_get_cocoa_window(GetAncestor(hwnd_owner, GA_ROOT), FALSE);
TRACE("format_id %s data %p owner %d hwnd_owner %p window %p)\n", debugstr_format(format_id), data, owner, hwnd_owner, window);
if (!data)
{
FIXME("delayed rendering (promising) is not implemented yet\n");
return FALSE;
}
/* Find the "natural" format for this format_id (the one which isn't
synthesized from another type). */
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 = insert_clipboard_format(format_id, NULL)))
{
WARN("Failed to register clipboard format %s\n", debugstr_format(format_id));
return FALSE;
}
/* Export the data to the Mac pasteboard. */
if (!format->export_func || !(cfdata = format->export_func(data)))
{
WARN("Failed to export %s data to type %s\n", debugstr_format(format_id), debugstr_cf(format->type));
return FALSE;
}
if (macdrv_set_pasteboard_data(format->type, cfdata))
TRACE("Set pasteboard data for type %s: %s\n", debugstr_cf(format->type), debugstr_cf(cfdata));
else
{
WARN("Failed to set pasteboard data for type %s: %s\n", debugstr_cf(format->type), debugstr_cf(cfdata));
CFRelease(cfdata);
return FALSE;
}
CFRelease(cfdata);
/* Find any other formats for this format_id (the exportable synthesized ones). */
LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
{
if (format->format_id == format_id && format->synthesized && format->export_func)
{
/* We have a synthesized format for this format ID. Add its type to the pasteboard. */
TRACE("Synthesized from format %s: type %s\n", debugstr_format(format_id), debugstr_cf(format->type));
cfdata = format->export_func(data);
if (!cfdata)
{
WARN("Failed to export %s data to type %s\n", debugstr_format(format->format_id), debugstr_cf(format->type));
continue;
}
if (macdrv_set_pasteboard_data(format->type, cfdata))
TRACE(" ... set pasteboard data: %s\n", debugstr_cf(cfdata));
else
WARN(" ... failed to set pasteboard data: %s\n", debugstr_cf(cfdata));
CFRelease(cfdata);
}
}
/* FIXME: According to MSDN, the caller is entitled to lock and read from
data until CloseClipboard is called. So, we should defer this cleanup. */
if ((format_id >= CF_GDIOBJFIRST && format_id <= CF_GDIOBJLAST) ||
format_id == CF_BITMAP ||
format_id == CF_DIB ||
format_id == CF_PALETTE)
{
DeleteObject(data);
}
else if (format_id == CF_METAFILEPICT)
{
DeleteMetaFile(((METAFILEPICT *)GlobalLock(data))->hMF);
GlobalFree(data);
}
else if (format_id == CF_ENHMETAFILE)
{
DeleteEnhMetaFile(data);
}
else if (format_id < CF_PRIVATEFIRST || CF_PRIVATELAST < format_id)
{
GlobalFree(data);
}
return TRUE;
}
/************************************************************************** /**************************************************************************
* MACDRV Private Clipboard Exports * MACDRV Private Clipboard Exports
**************************************************************************/ **************************************************************************/
@ -868,6 +1215,7 @@ void macdrv_clipboard_process_attach(void)
format->format_id = builtin_format_ids[i].id; format->format_id = builtin_format_ids[i].id;
format->type = CFRetain(builtin_format_ids[i].type); format->type = CFRetain(builtin_format_ids[i].type);
format->import_func = builtin_format_ids[i].import; format->import_func = builtin_format_ids[i].import;
format->export_func = builtin_format_ids[i].export;
format->synthesized = builtin_format_ids[i].synthesized; format->synthesized = builtin_format_ids[i].synthesized;
list_add_tail(&format_list, &format->entry); list_add_tail(&format_list, &format->entry);
} }

View File

@ -22,6 +22,25 @@
#import "cocoa_app.h" #import "cocoa_app.h"
static int owned_change_count = -1;
/***********************************************************************
* macdrv_is_pasteboard_owner
*/
int macdrv_is_pasteboard_owner(void)
{
__block int ret;
OnMainThread(^{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
ret = ([pb changeCount] == owned_change_count);
});
return ret;
}
/*********************************************************************** /***********************************************************************
* macdrv_copy_pasteboard_types * macdrv_copy_pasteboard_types
* *
@ -78,3 +97,61 @@ CFDataRef macdrv_copy_pasteboard_data(CFStringRef type)
return (CFDataRef)ret; return (CFDataRef)ret;
} }
/***********************************************************************
* macdrv_clear_pasteboard
*
* Takes ownership of the Mac pasteboard and clears it of all data types.
*/
void macdrv_clear_pasteboard(void)
{
OnMainThreadAsync(^{
@try
{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
owned_change_count = [pb declareTypes:[NSArray array] owner:nil];
}
@catch (id e)
{
ERR(@"Exception discarded while clearing pasteboard: %@\n", e);
}
});
}
/***********************************************************************
* macdrv_set_pasteboard_data
*
* Sets the pasteboard data for a specified type. Replaces any data of
* that type already on the pasteboard.
*
* Returns 0 on error, non-zero on success.
*/
int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data)
{
__block int ret = 0;
OnMainThread(^{
@try
{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
NSInteger change_count = [pb addTypes:[NSArray arrayWithObject:(NSString*)type]
owner:nil];
if (change_count)
{
owned_change_count = change_count;
if (data)
ret = [pb setData:(NSData*)data forType:(NSString*)type];
else
ret = 1;
}
}
@catch (id e)
{
ERR(@"Exception discarded while copying pasteboard types: %@\n", e);
}
});
return ret;
}

View File

@ -276,6 +276,9 @@ extern void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat
/* clipboard */ /* clipboard */
extern CFArrayRef macdrv_copy_pasteboard_types(void) DECLSPEC_HIDDEN; extern CFArrayRef macdrv_copy_pasteboard_types(void) DECLSPEC_HIDDEN;
extern CFDataRef macdrv_copy_pasteboard_data(CFStringRef type) DECLSPEC_HIDDEN; extern CFDataRef macdrv_copy_pasteboard_data(CFStringRef type) DECLSPEC_HIDDEN;
extern int macdrv_is_pasteboard_owner(void) DECLSPEC_HIDDEN;
extern void macdrv_clear_pasteboard(void) DECLSPEC_HIDDEN;
extern int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data) DECLSPEC_HIDDEN;
/* opengl */ /* opengl */

View File

@ -4,6 +4,7 @@
# USER driver # USER driver
@ cdecl AcquireClipboard(long) macdrv_AcquireClipboard
@ cdecl ActivateKeyboardLayout(long long) macdrv_ActivateKeyboardLayout @ cdecl ActivateKeyboardLayout(long long) macdrv_ActivateKeyboardLayout
@ cdecl Beep() macdrv_Beep @ cdecl Beep() macdrv_Beep
@ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx @ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) macdrv_ChangeDisplaySettingsEx
@ -13,6 +14,8 @@
@ cdecl CreateWindow(long) macdrv_CreateWindow @ cdecl CreateWindow(long) macdrv_CreateWindow
@ cdecl DestroyCursorIcon(long) macdrv_DestroyCursorIcon @ cdecl DestroyCursorIcon(long) macdrv_DestroyCursorIcon
@ cdecl DestroyWindow(long) macdrv_DestroyWindow @ cdecl DestroyWindow(long) macdrv_DestroyWindow
@ cdecl EmptyClipboard(long) macdrv_EmptyClipboard
@ cdecl EndClipboardUpdate() macdrv_EndClipboardUpdate
@ cdecl EnumClipboardFormats(long) macdrv_EnumClipboardFormats @ cdecl EnumClipboardFormats(long) macdrv_EnumClipboardFormats
@ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors @ cdecl EnumDisplayMonitors(long ptr ptr long) macdrv_EnumDisplayMonitors
@ cdecl EnumDisplaySettingsEx(ptr long ptr long) macdrv_EnumDisplaySettingsEx @ cdecl EnumDisplaySettingsEx(ptr long ptr long) macdrv_EnumDisplaySettingsEx
@ -26,6 +29,7 @@
@ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx @ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx @ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx
@ cdecl ScrollDC(long long long ptr ptr long ptr) macdrv_ScrollDC @ cdecl ScrollDC(long long long ptr ptr long ptr) macdrv_ScrollDC
@ cdecl SetClipboardData(long long long) macdrv_SetClipboardData
@ cdecl SetCursor(long) macdrv_SetCursor @ cdecl SetCursor(long) macdrv_SetCursor
@ cdecl SetCursorPos(long long) macdrv_SetCursorPos @ cdecl SetCursorPos(long long) macdrv_SetCursorPos
@ cdecl SetFocus(long) macdrv_SetFocus @ cdecl SetFocus(long) macdrv_SetFocus