winemac: Add support for delay-rendered (a.k.a. promised) clipboard data.

This commit is contained in:
Ken Thomases 2013-03-10 22:58:51 -05:00 committed by Alexandre Julliard
parent 69e631e04e
commit 449e2655c2
7 changed files with 149 additions and 43 deletions

View File

@ -1102,18 +1102,12 @@ BOOL CDECL macdrv_SetClipboardData(UINT format_id, HANDLE data, BOOL owner)
HWND hwnd_owner;
macdrv_window window;
WINE_CLIPFORMAT *format;
CFDataRef cfdata;
CFDataRef cfdata = NULL;
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)
@ -1126,22 +1120,25 @@ BOOL CDECL macdrv_SetClipboardData(UINT format_id, HANDLE data, BOOL owner)
}
/* Export the data to the Mac pasteboard. */
if (!format->export_func || !(cfdata = format->export_func(data)))
if (data)
{
WARN("Failed to export %s data to type %s\n", debugstr_format(format_id), debugstr_cf(format->type));
return FALSE;
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))
if (macdrv_set_pasteboard_data(format->type, cfdata, window))
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);
if (cfdata) CFRelease(cfdata);
return FALSE;
}
CFRelease(cfdata);
if (cfdata) CFRelease(cfdata);
/* Find any other formats for this format_id (the exportable synthesized ones). */
LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
@ -1151,43 +1148,51 @@ BOOL CDECL macdrv_SetClipboardData(UINT format_id, HANDLE data, BOOL owner)
/* 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)
if (data)
{
WARN("Failed to export %s data to type %s\n", debugstr_format(format->format_id), debugstr_cf(format->type));
continue;
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;
}
}
else
cfdata = NULL;
if (macdrv_set_pasteboard_data(format->type, cfdata))
if (macdrv_set_pasteboard_data(format->type, cfdata, window))
TRACE(" ... set pasteboard data: %s\n", debugstr_cf(cfdata));
else
WARN(" ... failed to set pasteboard data: %s\n", debugstr_cf(cfdata));
CFRelease(cfdata);
if (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)
if (data)
{
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);
/* 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;
@ -1220,3 +1225,74 @@ void macdrv_clipboard_process_attach(void)
list_add_tail(&format_list, &format->entry);
}
}
/**************************************************************************
* query_pasteboard_data
*/
BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
{
BOOL ret = FALSE;
CLIPBOARDINFO cbinfo;
WINE_CLIPFORMAT* format;
CFArrayRef types = NULL;
CFRange range;
TRACE("hwnd %p type %s\n", hwnd, debugstr_cf(type));
if (get_clipboard_info(&cbinfo))
hwnd = cbinfo.hwnd_owner;
format = NULL;
while ((format = format_for_type(format, type)))
{
WINE_CLIPFORMAT* base_format;
TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
if (!format->synthesized)
{
TRACE("Sending WM_RENDERFORMAT message for format %s to hwnd %p\n", debugstr_format(format->format_id), hwnd);
SendMessageW(hwnd, WM_RENDERFORMAT, format->format_id, 0);
ret = TRUE;
goto done;
}
if (!types)
{
types = macdrv_copy_pasteboard_types();
if (!types)
{
WARN("Failed to copy pasteboard types\n");
break;
}
range = CFRangeMake(0, CFArrayGetCount(types));
}
/* The type maps to a synthesized format. Now look up what type that format maps to natively
(not synthesized). For example, if type is "public.utf8-plain-text", then this format may
have an ID of CF_TEXT. From CF_TEXT, we want to find "org.winehq.builtin.text" to see if
that type is present in the pasteboard. If it is, then the app must have promised it and
we can ask it to render it. (If it had put it on the clipboard immediately, then the
pasteboard would also have data for "public.utf8-plain-text" and we wouldn't be here.) If
"org.winehq.builtin.text" is not on the pasteboard, then one of the other text formats is
presumably responsible for the promise that we're trying to satisfy, so we keep looking. */
LIST_FOR_EACH_ENTRY(base_format, &format_list, WINE_CLIPFORMAT, entry)
{
if (base_format->format_id == format->format_id && !base_format->synthesized &&
CFArrayContainsValue(types, range, base_format->type))
{
TRACE("Sending WM_RENDERFORMAT message for format %s to hwnd %p\n", debugstr_format(base_format->format_id), hwnd);
SendMessageW(hwnd, WM_RENDERFORMAT, base_format->format_id, 0);
ret = TRUE;
goto done;
}
}
}
done:
if (types) CFRelease(types);
return ret;
}

View File

@ -125,20 +125,22 @@ void macdrv_clear_pasteboard(void)
* macdrv_set_pasteboard_data
*
* Sets the pasteboard data for a specified type. Replaces any data of
* that type already on the pasteboard.
* that type already on the pasteboard. If data is NULL, promises the
* type.
*
* Returns 0 on error, non-zero on success.
*/
int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data)
int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data, macdrv_window w)
{
__block int ret = 0;
WineWindow* window = (WineWindow*)w;
OnMainThread(^{
@try
{
NSPasteboard* pb = [NSPasteboard generalPasteboard];
NSInteger change_count = [pb addTypes:[NSArray arrayWithObject:(NSString*)type]
owner:nil];
owner:window];
if (change_count)
{
owned_change_count = change_count;

View File

@ -455,6 +455,8 @@ void macdrv_release_query(macdrv_query *query)
{
if (OSAtomicDecrement32Barrier(&query->refs) <= 0)
{
if (query->type == QUERY_PASTEBOARD_DATA && query->pasteboard_data.type)
CFRelease(query->pasteboard_data.type);
[(WineWindow*)query->window release];
free(query);
}

View File

@ -1174,6 +1174,21 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
ignore_windowMiniaturize = FALSE;
}
/*
* ---------- NSPasteboardOwner methods ----------
*/
- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
{
macdrv_query* query = macdrv_create_query();
query->type = QUERY_PASTEBOARD_DATA;
query->window = (macdrv_window)[self retain];
query->pasteboard_data.type = (CFStringRef)[type copy];
[self.queue query:query timeout:3];
macdrv_release_query(query);
}
@end

View File

@ -116,6 +116,10 @@ static void macdrv_query_event(HWND hwnd, macdrv_event *event)
switch (query->type)
{
case QUERY_PASTEBOARD_DATA:
TRACE("QUERY_PASTEBOARD_DATA\n");
success = query_pasteboard_data(hwnd, query->pasteboard_data.type);
break;
default:
FIXME("unrecognized query type %d\n", query->type);
break;

View File

@ -159,6 +159,7 @@ extern void macdrv_key_event(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDD
extern void macdrv_displays_changed(const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_clipboard_process_attach(void) DECLSPEC_HIDDEN;
extern BOOL query_pasteboard_data(HWND hwnd, CFStringRef type) DECLSPEC_HIDDEN;
extern struct opengl_funcs *macdrv_wine_get_wgl_driver(PHYSDEV dev, UINT version) DECLSPEC_HIDDEN;
extern void sync_gl_view(struct macdrv_win_data *data) DECLSPEC_HIDDEN;

View File

@ -208,6 +208,7 @@ typedef struct macdrv_event {
} macdrv_event;
enum {
QUERY_PASTEBOARD_DATA,
NUM_QUERY_TYPES
};
@ -217,6 +218,11 @@ typedef struct macdrv_query {
macdrv_window window;
int status;
int done;
union {
struct {
CFStringRef type;
} pasteboard_data;
};
} macdrv_query;
static inline macdrv_event_mask event_mask_for_type(int type)
@ -302,7 +308,7 @@ extern CFArrayRef macdrv_copy_pasteboard_types(void) 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;
extern int macdrv_set_pasteboard_data(CFStringRef type, CFDataRef data, macdrv_window w) DECLSPEC_HIDDEN;
/* opengl */