From 449e2655c2ca59eb0ab3b32e95bd9071c665d921 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Sun, 10 Mar 2013 22:58:51 -0500 Subject: [PATCH] winemac: Add support for delay-rendered (a.k.a. promised) clipboard data. --- dlls/winemac.drv/clipboard.c | 154 +++++++++++++++++++++-------- dlls/winemac.drv/cocoa_clipboard.m | 8 +- dlls/winemac.drv/cocoa_event.m | 2 + dlls/winemac.drv/cocoa_window.m | 15 +++ dlls/winemac.drv/event.c | 4 + dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_cocoa.h | 8 +- 7 files changed, 149 insertions(+), 43 deletions(-) diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c index 9a418e51ed..3e5e9b3ebc 100644 --- a/dlls/winemac.drv/clipboard.c +++ b/dlls/winemac.drv/clipboard.c @@ -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; +} diff --git a/dlls/winemac.drv/cocoa_clipboard.m b/dlls/winemac.drv/cocoa_clipboard.m index 74fa75b6af..486cbd003d 100644 --- a/dlls/winemac.drv/cocoa_clipboard.m +++ b/dlls/winemac.drv/cocoa_clipboard.m @@ -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; diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m index 15d2d29d09..a5f1c1b6fb 100644 --- a/dlls/winemac.drv/cocoa_event.m +++ b/dlls/winemac.drv/cocoa_event.m @@ -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); } diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 9555ec4aac..7c80c571e3 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -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 diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 915dddc05f..512d99ef96 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -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; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index f9b70456a0..4c39fc5e6e 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -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; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 9bb960ae22..bbd3424ea1 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -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 */