include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winemac.drv / clipboard.c
blob5a1065cf7c432437b335afa78ccb2ab508cd780c
1 /*
2 * Mac clipboard driver
4 * Copyright 1994 Martin Ayotte
5 * 1996 Alex Korobka
6 * 1999 Noel Borthwick
7 * 2003 Ulrich Czekalla for CodeWeavers
8 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #if 0
26 #pragma makedep unix
27 #endif
29 #include "config.h"
31 #include "ntstatus.h"
32 #define WIN32_NO_STATUS
33 #include "macdrv.h"
34 #include "winuser.h"
35 #include "shellapi.h"
36 #include "shlobj.h"
37 #include "wine/list.h"
38 #include "wine/server.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
44 /**************************************************************************
45 * Types
46 **************************************************************************/
48 typedef void *(*DRVIMPORTFUNC)(CFDataRef data, size_t *ret_size);
49 typedef CFDataRef (*DRVEXPORTFUNC)(void *data, size_t size);
51 typedef struct _WINE_CLIPFORMAT
53 struct list entry;
54 UINT format_id;
55 CFStringRef type;
56 DRVIMPORTFUNC import_func;
57 DRVEXPORTFUNC export_func;
58 BOOL synthesized;
59 struct _WINE_CLIPFORMAT *natural_format;
60 } WINE_CLIPFORMAT;
63 /**************************************************************************
64 * Constants
65 **************************************************************************/
67 #define CLIPBOARD_UPDATE_DELAY 2000 /* delay between checks of the Mac pasteboard */
70 /**************************************************************************
71 * Forward Function Declarations
72 **************************************************************************/
74 static void *import_clipboard_data(CFDataRef data, size_t *ret_size);
75 static void *import_bmp_to_dib(CFDataRef data, size_t *ret_size);
76 static void *import_html(CFDataRef data, size_t *ret_size);
77 static void *import_nsfilenames_to_hdrop(CFDataRef data, size_t *ret_size);
78 static void *import_utf8_to_unicodetext(CFDataRef data, size_t *ret_size);
79 static void *import_utf16_to_unicodetext(CFDataRef data, size_t *ret_size);
81 static CFDataRef export_clipboard_data(void *data, size_t size);
82 static CFDataRef export_dib_to_bmp(void *data, size_t size);
83 static CFDataRef export_hdrop_to_filenames(void *data, size_t size);
84 static CFDataRef export_html(void *data, size_t size);
85 static CFDataRef export_unicodetext_to_utf8(void *data, size_t size);
86 static CFDataRef export_unicodetext_to_utf16(void *data, size_t size);
89 /**************************************************************************
90 * Static Variables
91 **************************************************************************/
93 static const WCHAR clipboard_classname[] =
94 {'_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_','m','a','n','a','g','e','r',0};
96 /* Clipboard formats */
97 static struct list format_list = LIST_INIT(format_list);
99 /* There are two naming schemes involved and we want to have a mapping between
100 them. There are Win32 clipboard format names and there are Mac pasteboard
101 types.
103 The Win32 standard clipboard formats don't have names, but they are associated
104 with Mac pasteboard types through the following tables, which are used to
105 initialize the format_list. Where possible, the standard clipboard formats
106 are mapped to predefined pasteboard type UTIs. Otherwise, we create Wine-
107 specific types of the form "org.winehq.builtin.<format>", where <format> is
108 the name of the symbolic constant for the format minus "CF_" and lowercased.
109 E.g. CF_BITMAP -> org.winehq.builtin.bitmap.
111 Win32 clipboard formats which originate in a Windows program may be registered
112 with an arbitrary name. We construct a Mac pasteboard type from these by
113 prepending "org.winehq.registered." to the registered name.
115 Likewise, Mac pasteboard types which originate in other apps may have
116 arbitrary type strings. We ignore these.
118 Summary:
119 Win32 clipboard format names:
120 <none> standard clipboard format; maps via
121 format_list to either a predefined Mac UTI
122 or org.winehq.builtin.<format>.
123 <other> name registered within Win32 land; maps to
124 org.winehq.registered.<other>
125 Mac pasteboard type names:
126 org.winehq.builtin.<format ID> representation of Win32 standard clipboard
127 format for which there was no corresponding
128 predefined Mac UTI; maps via format_list
129 org.winehq.registered.<format name> representation of Win32 registered
130 clipboard format name; maps to <format name>
131 <other> Mac pasteboard type originating with system
132 or other apps; either maps via format_list
133 to a standard clipboard format or ignored
136 static const struct
138 UINT id;
139 CFStringRef type;
140 DRVIMPORTFUNC import;
141 DRVEXPORTFUNC export;
142 BOOL synthesized;
143 } builtin_format_ids[] =
145 { CF_DIBV5, CFSTR("org.winehq.builtin.dibv5"), import_clipboard_data, export_clipboard_data, FALSE },
146 { CF_DIF, CFSTR("org.winehq.builtin.dif"), import_clipboard_data, export_clipboard_data, FALSE },
147 { CF_ENHMETAFILE, CFSTR("org.winehq.builtin.enhmetafile"), import_clipboard_data, export_clipboard_data, FALSE },
148 { CF_LOCALE, CFSTR("org.winehq.builtin.locale"), import_clipboard_data, export_clipboard_data, FALSE },
149 { CF_OEMTEXT, CFSTR("org.winehq.builtin.oemtext"), import_clipboard_data, export_clipboard_data, FALSE },
150 { CF_PALETTE, CFSTR("org.winehq.builtin.palette"), import_clipboard_data, export_clipboard_data, FALSE },
151 { CF_PENDATA, CFSTR("org.winehq.builtin.pendata"), import_clipboard_data, export_clipboard_data, FALSE },
152 { CF_RIFF, CFSTR("org.winehq.builtin.riff"), import_clipboard_data, export_clipboard_data, FALSE },
153 { CF_SYLK, CFSTR("org.winehq.builtin.sylk"), import_clipboard_data, export_clipboard_data, FALSE },
154 { CF_TEXT, CFSTR("org.winehq.builtin.text"), import_clipboard_data, export_clipboard_data, FALSE },
155 { CF_TIFF, CFSTR("public.tiff"), import_clipboard_data, export_clipboard_data, FALSE },
156 { CF_WAVE, CFSTR("com.microsoft.waveform-audio"), import_clipboard_data, export_clipboard_data, FALSE },
158 { CF_DIB, CFSTR("org.winehq.builtin.dib"), import_clipboard_data, export_clipboard_data, FALSE },
159 { CF_DIB, CFSTR("com.microsoft.bmp"), import_bmp_to_dib, export_dib_to_bmp, TRUE },
161 { CF_HDROP, CFSTR("org.winehq.builtin.hdrop"), import_clipboard_data, export_clipboard_data, FALSE },
162 { CF_HDROP, CFSTR("NSFilenamesPboardType"), import_nsfilenames_to_hdrop, export_hdrop_to_filenames, TRUE },
164 { CF_UNICODETEXT, CFSTR("org.winehq.builtin.unicodetext"), import_clipboard_data, export_clipboard_data, FALSE },
165 { CF_UNICODETEXT, CFSTR("public.utf16-plain-text"), import_utf16_to_unicodetext, export_unicodetext_to_utf16,TRUE },
166 { CF_UNICODETEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_unicodetext, export_unicodetext_to_utf8, TRUE },
169 static const WCHAR wszRichTextFormat[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0};
170 static const WCHAR wszGIF[] = {'G','I','F',0};
171 static const WCHAR wszJFIF[] = {'J','F','I','F',0};
172 static const WCHAR wszPNG[] = {'P','N','G',0};
173 static const WCHAR wszHTMLFormat[] = {'H','T','M','L',' ','F','o','r','m','a','t',0};
174 static const struct
176 LPCWSTR name;
177 CFStringRef type;
178 DRVIMPORTFUNC import;
179 DRVEXPORTFUNC export;
180 BOOL synthesized;
181 } builtin_format_names[] =
183 { wszRichTextFormat, CFSTR("public.rtf"), import_clipboard_data, export_clipboard_data },
184 { wszGIF, CFSTR("com.compuserve.gif"), import_clipboard_data, export_clipboard_data },
185 { wszJFIF, CFSTR("public.jpeg"), import_clipboard_data, export_clipboard_data },
186 { wszPNG, CFSTR("public.png"), import_clipboard_data, export_clipboard_data },
187 { wszHTMLFormat, NULL, import_clipboard_data, export_clipboard_data },
188 { wszHTMLFormat, CFSTR("public.html"), import_html, export_html, TRUE },
189 { CFSTR_INETURLW, CFSTR("public.url"), import_utf8_to_unicodetext, export_unicodetext_to_utf8 },
192 /* The prefix prepended to a Win32 clipboard format name to make a Mac pasteboard type. */
193 static const CFStringRef registered_name_type_prefix = CFSTR("org.winehq.registered.");
195 static unsigned int clipboard_thread_id;
196 static HWND clipboard_hwnd;
197 static BOOL is_clipboard_owner;
198 static macdrv_window clipboard_cocoa_window;
199 static unsigned int last_clipboard_update;
200 static unsigned int last_get_seqno;
201 static WINE_CLIPFORMAT **current_mac_formats;
202 static unsigned int nb_current_mac_formats;
205 /**************************************************************************
206 * Internal Clipboard implementation methods
207 **************************************************************************/
210 * format_list functions
213 /**************************************************************************
214 * debugstr_format
216 static const char *debugstr_format(UINT id)
218 WCHAR buffer[256];
220 if (NtUserGetClipboardFormatName(id, buffer, 256))
221 return wine_dbg_sprintf("0x%04x %s", id, debugstr_w(buffer));
223 switch (id)
225 #define BUILTIN(id) case id: return #id;
226 BUILTIN(CF_TEXT)
227 BUILTIN(CF_BITMAP)
228 BUILTIN(CF_METAFILEPICT)
229 BUILTIN(CF_SYLK)
230 BUILTIN(CF_DIF)
231 BUILTIN(CF_TIFF)
232 BUILTIN(CF_OEMTEXT)
233 BUILTIN(CF_DIB)
234 BUILTIN(CF_PALETTE)
235 BUILTIN(CF_PENDATA)
236 BUILTIN(CF_RIFF)
237 BUILTIN(CF_WAVE)
238 BUILTIN(CF_UNICODETEXT)
239 BUILTIN(CF_ENHMETAFILE)
240 BUILTIN(CF_HDROP)
241 BUILTIN(CF_LOCALE)
242 BUILTIN(CF_DIBV5)
243 BUILTIN(CF_OWNERDISPLAY)
244 BUILTIN(CF_DSPTEXT)
245 BUILTIN(CF_DSPBITMAP)
246 BUILTIN(CF_DSPMETAFILEPICT)
247 BUILTIN(CF_DSPENHMETAFILE)
248 #undef BUILTIN
249 default: return wine_dbg_sprintf("0x%04x", id);
254 static CFTypeRef pasteboard_from_handle(UINT64 handle)
256 return (CFTypeRef)(UINT_PTR)handle;
260 /**************************************************************************
261 * insert_clipboard_format
263 static WINE_CLIPFORMAT *insert_clipboard_format(UINT id, CFStringRef type)
265 WINE_CLIPFORMAT *format;
267 format = malloc(sizeof(*format));
269 if (format == NULL)
271 WARN("No more memory for a new format!\n");
272 return NULL;
274 format->format_id = id;
275 format->import_func = import_clipboard_data;
276 format->export_func = export_clipboard_data;
277 format->synthesized = FALSE;
278 format->natural_format = NULL;
280 if (type)
281 format->type = CFStringCreateCopy(NULL, type);
282 else
284 WCHAR buffer[256];
286 if (!NtUserGetClipboardFormatName(format->format_id, buffer, ARRAY_SIZE(buffer)))
288 WARN("failed to get name for format %s; error 0x%08x\n", debugstr_format(format->format_id),
289 (unsigned int)RtlGetLastWin32Error());
290 free(format);
291 return NULL;
294 format->type = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%S"),
295 registered_name_type_prefix, (const WCHAR*)buffer);
298 list_add_tail(&format_list, &format->entry);
300 TRACE("Registering format %s type %s\n", debugstr_format(format->format_id),
301 debugstr_cf(format->type));
303 return format;
307 /**************************************************************************
308 * register_format
310 * Register a custom Mac clipboard format.
312 static WINE_CLIPFORMAT* register_format(UINT id, CFStringRef type)
314 WINE_CLIPFORMAT *format;
316 /* walk format chain to see if it's already registered */
317 LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
318 if (format->format_id == id) return format;
320 return insert_clipboard_format(id, type);
324 /**************************************************************************
325 * natural_format_for_format
327 * Find the "natural" format for this format_id (the one which isn't
328 * synthesized from another type).
330 static WINE_CLIPFORMAT* natural_format_for_format(UINT format_id)
332 WINE_CLIPFORMAT *format;
334 LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
335 if (format->format_id == format_id && !format->synthesized) break;
337 if (&format->entry == &format_list)
338 format = NULL;
340 TRACE("%s -> %p/%s\n", debugstr_format(format_id), format, debugstr_cf(format ? format->type : NULL));
341 return format;
345 static ATOM register_clipboard_format(const WCHAR *name)
347 ATOM atom;
348 if (NtAddAtom(name, lstrlenW(name) * sizeof(WCHAR), &atom)) return 0;
349 return atom;
353 /**************************************************************************
354 * register_builtin_formats
356 static void register_builtin_formats(void)
358 UINT i;
359 WINE_CLIPFORMAT *format;
361 /* Register built-in formats */
362 for (i = 0; i < ARRAY_SIZE(builtin_format_ids); i++)
364 if (!(format = malloc(sizeof(*format)))) break;
365 format->format_id = builtin_format_ids[i].id;
366 format->type = CFRetain(builtin_format_ids[i].type);
367 format->import_func = builtin_format_ids[i].import;
368 format->export_func = builtin_format_ids[i].export;
369 format->synthesized = builtin_format_ids[i].synthesized;
370 format->natural_format = NULL;
371 list_add_tail(&format_list, &format->entry);
374 /* Register known mappings between Windows formats and Mac types */
375 for (i = 0; i < ARRAY_SIZE(builtin_format_names); i++)
377 if (!(format = malloc(sizeof(*format)))) break;
378 format->format_id = register_clipboard_format(builtin_format_names[i].name);
379 format->import_func = builtin_format_names[i].import;
380 format->export_func = builtin_format_names[i].export;
381 format->synthesized = builtin_format_names[i].synthesized;
382 format->natural_format = NULL;
384 if (builtin_format_names[i].type)
385 format->type = CFRetain(builtin_format_names[i].type);
386 else
388 format->type = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%S"),
389 registered_name_type_prefix, builtin_format_names[i].name);
392 list_add_tail(&format_list, &format->entry);
395 LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
397 if (format->synthesized)
398 format->natural_format = natural_format_for_format(format->format_id);
403 /**************************************************************************
404 * format_for_type
406 static WINE_CLIPFORMAT* format_for_type(CFStringRef type)
408 WINE_CLIPFORMAT *format;
410 TRACE("type %s\n", debugstr_cf(type));
412 if (list_empty(&format_list)) register_builtin_formats();
414 LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
416 if (CFEqual(format->type, type))
417 goto done;
420 format = NULL;
421 if (CFStringHasPrefix(type, CFSTR("org.winehq.builtin.")))
423 ERR("Shouldn't happen. Built-in type %s should have matched something in format list.\n",
424 debugstr_cf(type));
426 else if (CFStringHasPrefix(type, registered_name_type_prefix))
428 LPWSTR name;
429 int len = CFStringGetLength(type) - CFStringGetLength(registered_name_type_prefix);
431 name = malloc((len + 1) * sizeof(WCHAR));
432 CFStringGetCharacters(type, CFRangeMake(CFStringGetLength(registered_name_type_prefix), len),
433 (UniChar*)name);
434 name[len] = 0;
436 format = register_format(register_clipboard_format(name), type);
437 if (!format)
438 ERR("Failed to register format for type %s name %s\n", debugstr_cf(type), debugstr_w(name));
440 free(name);
443 done:
444 TRACE(" -> %p/%s\n", format, debugstr_format(format ? format->format_id : 0));
445 return format;
449 /***********************************************************************
450 * bitmap_info_size
452 * Return the size of the bitmap info structure including color table.
454 static int bitmap_info_size(const BITMAPINFO *info, WORD coloruse)
456 unsigned int colors, size, masks = 0;
458 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
460 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER*)info;
461 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
462 return sizeof(BITMAPCOREHEADER) + colors *
463 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
465 else /* assume BITMAPINFOHEADER */
467 colors = min(info->bmiHeader.biClrUsed, 256);
468 if (!colors && (info->bmiHeader.biBitCount <= 8))
469 colors = 1 << info->bmiHeader.biBitCount;
470 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
471 size = max(info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD));
472 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
477 /**************************************************************************
478 * get_html_description_field
480 * Find the value of a field in an HTML Format description.
482 static const char* get_html_description_field(const char* data, const char* keyword)
484 const char* pos = data;
486 while (pos && *pos && *pos != '<')
488 if (memcmp(pos, keyword, strlen(keyword)) == 0)
489 return pos + strlen(keyword);
491 pos = strchr(pos, '\n');
492 if (pos) pos++;
495 return NULL;
499 /**************************************************************************
500 * import_clipboard_data
502 * Generic import clipboard data routine.
504 static void *import_clipboard_data(CFDataRef data, size_t *ret_size)
506 void *ret = NULL;
508 size_t len = CFDataGetLength(data);
509 if (len && (ret = malloc(len)))
511 memcpy(ret, CFDataGetBytePtr(data), len);
512 *ret_size = len;
515 return ret;
519 /**************************************************************************
520 * import_bmp_to_dib
522 * Import BMP data, converting to CF_DIB or CF_DIBV5 format. This just
523 * entails stripping the BMP file format header.
525 static void *import_bmp_to_dib(CFDataRef data, size_t *ret_size)
527 BITMAPFILEHEADER *bfh = (BITMAPFILEHEADER*)CFDataGetBytePtr(data);
528 CFIndex len = CFDataGetLength(data);
529 void *ret = NULL;
531 if (len >= sizeof(*bfh) + sizeof(BITMAPCOREHEADER) &&
532 bfh->bfType == 0x4d42 /* "BM" */)
534 BITMAPINFO *bmi = (BITMAPINFO*)(bfh + 1);
536 len -= sizeof(*bfh);
537 if ((ret = malloc(len)))
539 memcpy(ret, bmi, len);
540 *ret_size = len;
544 return ret;
548 /**************************************************************************
549 * import_html
551 * Import HTML data.
553 static void *import_html(CFDataRef data, size_t *ret_size)
555 static const char header[] =
556 "Version:0.9\n"
557 "StartHTML:0000000100\n"
558 "EndHTML:%010lu\n"
559 "StartFragment:%010lu\n"
560 "EndFragment:%010lu\n"
561 "<!--StartFragment-->";
562 static const char trailer[] = "\n<!--EndFragment-->";
563 void *ret;
564 SIZE_T len, total;
565 size_t size = CFDataGetLength(data);
567 len = strlen(header) + 12; /* 3 * 4 extra chars for %010lu */
568 total = len + size + sizeof(trailer);
569 if ((ret = malloc(total)))
571 char *p = ret;
572 p += snprintf(p, total, header, total - 1, len, len + size + 1 /* include the final \n in the data */);
573 CFDataGetBytes(data, CFRangeMake(0, size), (UInt8*)p);
574 strcpy(p + size, trailer);
575 *ret_size = total;
576 TRACE("returning %s\n", debugstr_a(ret));
578 return ret;
582 static CPTABLEINFO *get_ansi_cp(void)
584 USHORT utf8_hdr[2] = { 0, CP_UTF8 };
585 static CPTABLEINFO cp;
586 if (!cp.CodePage)
588 if (NtCurrentTeb()->Peb->AnsiCodePageData)
589 RtlInitCodePageTable(NtCurrentTeb()->Peb->AnsiCodePageData, &cp);
590 else
591 RtlInitCodePageTable(utf8_hdr, &cp);
593 return &cp;
597 /* based on wine_get_dos_file_name */
598 static WCHAR *get_dos_file_name(const char *path)
600 ULONG len = strlen(path) + 9; /* \??\unix prefix */
601 WCHAR *ret;
603 if (!(ret = malloc(len * sizeof(WCHAR)))) return NULL;
604 if (wine_unix_to_nt_file_name(path, ret, &len))
606 free(ret);
607 return NULL;
610 if (ret[5] == ':')
612 /* get rid of the \??\ prefix */
613 memmove(ret, ret + 4, (len - 4) * sizeof(WCHAR));
615 else ret[1] = '\\';
616 return ret;
620 /***********************************************************************
621 * get_nt_pathname
623 * Simplified version of RtlDosPathNameToNtPathName_U.
625 static BOOL get_nt_pathname(const WCHAR *name, UNICODE_STRING *nt_name)
627 static const WCHAR ntprefixW[] = {'\\','?','?','\\'};
628 static const WCHAR uncprefixW[] = {'U','N','C','\\'};
629 size_t len = lstrlenW(name);
630 WCHAR *ptr;
632 nt_name->MaximumLength = (len + 8) * sizeof(WCHAR);
633 if (!(ptr = malloc(nt_name->MaximumLength))) return FALSE;
634 nt_name->Buffer = ptr;
636 memcpy(ptr, ntprefixW, sizeof(ntprefixW));
637 ptr += ARRAYSIZE(ntprefixW);
638 if (name[0] == '\\' && name[1] == '\\')
640 if ((name[2] == '.' || name[2] == '?') && name[3] == '\\')
642 name += 4;
643 len -= 4;
645 else
647 memcpy(ptr, uncprefixW, sizeof(uncprefixW));
648 ptr += ARRAYSIZE(uncprefixW);
649 name += 2;
650 len -= 2;
653 memcpy(ptr, name, (len + 1) * sizeof(WCHAR));
654 ptr += len;
655 nt_name->Length = (ptr - nt_name->Buffer) * sizeof(WCHAR);
656 return TRUE;
660 /* based on wine_get_unix_file_name */
661 static char *get_unix_file_name(const WCHAR *dosW)
663 UNICODE_STRING nt_name;
664 OBJECT_ATTRIBUTES attr;
665 NTSTATUS status;
666 ULONG size = 256;
667 char *buffer;
669 if (!get_nt_pathname(dosW, &nt_name)) return NULL;
670 InitializeObjectAttributes(&attr, &nt_name, 0, 0, NULL);
671 for (;;)
673 if (!(buffer = malloc(size)))
675 free(nt_name.Buffer);
676 return NULL;
678 status = wine_nt_to_unix_file_name(&attr, buffer, &size, FILE_OPEN_IF);
679 if (status != STATUS_BUFFER_TOO_SMALL) break;
680 free(buffer);
682 free(nt_name.Buffer);
683 if (status)
685 free(buffer);
686 return NULL;
688 return buffer;
692 /**************************************************************************
693 * import_nsfilenames_to_hdrop
695 * Import NSFilenamesPboardType data, converting the property-list-
696 * serialized array of path strings to CF_HDROP.
698 static void *import_nsfilenames_to_hdrop(CFDataRef data, size_t *ret_size)
700 CFArrayRef names;
701 CFIndex count, i;
702 size_t len;
703 char *buffer = NULL;
704 WCHAR **paths = NULL;
705 DROPFILES *dropfiles = NULL;
706 UniChar* p;
708 TRACE("data %s\n", debugstr_cf(data));
710 names = (CFArrayRef)CFPropertyListCreateWithData(NULL, data, kCFPropertyListImmutable,
711 NULL, NULL);
712 if (!names || CFGetTypeID(names) != CFArrayGetTypeID())
714 WARN("failed to interpret data as a CFArray\n");
715 goto done;
718 count = CFArrayGetCount(names);
720 len = 0;
721 for (i = 0; i < count; i++)
723 CFIndex this_len;
724 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(names, i);
725 TRACE(" %s\n", debugstr_cf(name));
726 if (CFGetTypeID(name) != CFStringGetTypeID())
728 WARN("non-string in array\n");
729 goto done;
732 this_len = CFStringGetMaximumSizeOfFileSystemRepresentation(name);
733 if (this_len > len)
734 len = this_len;
737 buffer = malloc(len);
738 if (!buffer)
740 WARN("failed to allocate buffer for file-system representations\n");
741 goto done;
744 paths = calloc(count, sizeof(paths[0]));
745 if (!paths)
747 WARN("failed to allocate array of DOS paths\n");
748 goto done;
751 for (i = 0; i < count; i++)
753 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(names, i);
754 if (!CFStringGetFileSystemRepresentation(name, buffer, len))
756 WARN("failed to get file-system representation for %s\n", debugstr_cf(name));
757 goto done;
759 paths[i] = get_dos_file_name(buffer);
760 if (!paths[i])
762 WARN("failed to get DOS path for %s\n", debugstr_a(buffer));
763 goto done;
767 len = 1; /* for the terminating null */
768 for (i = 0; i < count; i++)
769 len += wcslen(paths[i]) + 1;
771 *ret_size = sizeof(*dropfiles) + len * sizeof(WCHAR);
772 if (!(dropfiles = malloc(*ret_size)))
774 WARN("failed to allocate HDROP\n");
775 goto done;
778 dropfiles->pFiles = sizeof(*dropfiles);
779 dropfiles->pt.x = 0;
780 dropfiles->pt.y = 0;
781 dropfiles->fNC = FALSE;
782 dropfiles->fWide = TRUE;
784 p = (WCHAR*)(dropfiles + 1);
785 for (i = 0; i < count; i++)
787 wcscpy(p, paths[i]);
788 p += wcslen(p) + 1;
790 *p = 0;
792 done:
793 if (paths)
795 for (i = 0; i < count; i++) free(paths[i]);
796 free(paths);
798 free(buffer);
799 if (names) CFRelease(names);
800 return dropfiles;
804 /**************************************************************************
805 * import_utf8_to_unicodetext
807 * Import a UTF-8 string, converting the string to CF_UNICODETEXT.
809 static void *import_utf8_to_unicodetext(CFDataRef data, size_t *ret_size)
811 const BYTE *src;
812 unsigned long src_len;
813 unsigned long new_lines = 0;
814 LPSTR dst;
815 unsigned long i, j;
816 WCHAR *ret = NULL;
818 src = CFDataGetBytePtr(data);
819 src_len = CFDataGetLength(data);
820 for (i = 0; i < src_len; i++)
822 if (src[i] == '\n')
823 new_lines++;
826 if ((dst = malloc(src_len + new_lines + 1)))
828 for (i = 0, j = 0; i < src_len; i++)
830 if (src[i] == '\n')
831 dst[j++] = '\r';
833 dst[j++] = src[i];
835 dst[j++] = 0;
837 if ((ret = malloc(j * sizeof(WCHAR))))
839 DWORD dst_size;
840 RtlUTF8ToUnicodeN(ret, j * sizeof(WCHAR), &dst_size, dst, j);
841 *ret_size = dst_size;
844 free(dst);
847 return ret;
851 /**************************************************************************
852 * import_utf16_to_unicodetext
854 * Import a UTF-8 string, converting the string to CF_UNICODETEXT.
856 static void *import_utf16_to_unicodetext(CFDataRef data, size_t *ret_size)
858 const WCHAR *src;
859 unsigned long src_len;
860 unsigned long new_lines = 0;
861 LPWSTR dst;
862 unsigned long i, j;
864 src = (const WCHAR *)CFDataGetBytePtr(data);
865 src_len = CFDataGetLength(data) / sizeof(WCHAR);
866 for (i = 0; i < src_len; i++)
868 if (src[i] == '\n')
869 new_lines++;
870 else if (src[i] == '\r' && (i + 1 >= src_len || src[i + 1] != '\n'))
871 new_lines++;
874 *ret_size = (src_len + new_lines + 1) * sizeof(WCHAR);
875 if ((dst = malloc(*ret_size)))
877 for (i = 0, j = 0; i < src_len; i++)
879 if (src[i] == '\n')
880 dst[j++] = '\r';
882 dst[j++] = src[i];
884 if (src[i] == '\r' && (i + 1 >= src_len || src[i + 1] != '\n'))
885 dst[j++] = '\n';
887 dst[j] = 0;
890 return dst;
894 /**************************************************************************
895 * export_clipboard_data
897 * Generic export clipboard data routine.
899 static CFDataRef export_clipboard_data(void *data, size_t size)
901 return CFDataCreate(NULL, data, size);
905 /**************************************************************************
906 * export_dib_to_bmp
908 * Export CF_DIB or CF_DIBV5 to BMP file format. This just entails
909 * prepending a BMP file format header to the data.
911 static CFDataRef export_dib_to_bmp(void *data, size_t size)
913 CFMutableDataRef ret = NULL;
914 CFIndex len;
915 BITMAPFILEHEADER bfh;
917 len = sizeof(bfh) + size;
918 ret = CFDataCreateMutable(NULL, len);
919 if (ret)
921 bfh.bfType = 0x4d42; /* "BM" */
922 bfh.bfSize = len;
923 bfh.bfReserved1 = 0;
924 bfh.bfReserved2 = 0;
925 bfh.bfOffBits = sizeof(bfh) + bitmap_info_size(data, DIB_RGB_COLORS);
926 CFDataAppendBytes(ret, (UInt8*)&bfh, sizeof(bfh));
928 /* rest of bitmap is the same as the packed dib */
929 CFDataAppendBytes(ret, data, size);
932 return ret;
936 /**************************************************************************
937 * export_hdrop_to_filenames
939 * Export CF_HDROP to NSFilenamesPboardType data, which is a CFArray of
940 * CFStrings (holding Unix paths) which is serialized as a property list.
942 static CFDataRef export_hdrop_to_filenames(void *data, size_t size)
944 CFDataRef ret = NULL;
945 DROPFILES *dropfiles = data;
946 CFMutableArrayRef filenames = NULL;
947 void *p;
948 WCHAR *buffer = NULL;
949 size_t buffer_len = 0;
951 TRACE("data %p\n", data);
953 filenames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
954 if (!filenames)
956 WARN("failed to create filenames array\n");
957 goto done;
960 p = (char*)dropfiles + dropfiles->pFiles;
961 while (dropfiles->fWide ? *(WCHAR*)p : *(char*)p)
963 char *unixname;
964 CFStringRef filename;
966 TRACE(" %s\n", dropfiles->fWide ? debugstr_w(p) : debugstr_a(p));
968 if (dropfiles->fWide)
969 unixname = get_unix_file_name(p);
970 else
972 CPTABLEINFO *cp = get_ansi_cp();
973 DWORD len = strlen(p) + 1;
975 if (len * 3 > buffer_len)
977 free(buffer);
978 buffer_len = len * 3;
979 buffer = malloc(buffer_len * sizeof(*buffer));
982 if (cp->CodePage == CP_UTF8)
983 RtlUTF8ToUnicodeN(buffer, buffer_len * sizeof(WCHAR), &len, p, len);
984 else
985 RtlCustomCPToUnicodeN(cp, buffer, buffer_len * sizeof(WCHAR), &len, p, len);
987 unixname = get_unix_file_name(buffer);
989 if (!unixname)
991 WARN("failed to convert DOS path to Unix: %s\n",
992 dropfiles->fWide ? debugstr_w(p) : debugstr_a(p));
993 goto done;
996 if (dropfiles->fWide)
997 p = (WCHAR*)p + wcslen(p) + 1;
998 else
999 p = (char*)p + strlen(p) + 1;
1001 filename = CFStringCreateWithFileSystemRepresentation(NULL, unixname);
1002 if (!filename)
1004 WARN("failed to create CFString from Unix path %s\n", debugstr_a(unixname));
1005 free(unixname);
1006 goto done;
1009 free(unixname);
1010 CFArrayAppendValue(filenames, filename);
1011 CFRelease(filename);
1014 ret = CFPropertyListCreateData(NULL, filenames, kCFPropertyListXMLFormat_v1_0, 0, NULL);
1016 done:
1017 free(buffer);
1018 if (filenames) CFRelease(filenames);
1019 TRACE(" -> %s\n", debugstr_cf(ret));
1020 return ret;
1024 /**************************************************************************
1025 * export_html
1027 * Export HTML Format to public.html data.
1029 * FIXME: We should attempt to add an <a base> tag and convert windows paths.
1031 static CFDataRef export_html(void *data, size_t size)
1033 const char *field_value;
1034 int fragmentstart, fragmentend;
1036 /* read the important fields */
1037 field_value = get_html_description_field(data, "StartFragment:");
1038 if (!field_value)
1040 ERR("Couldn't find StartFragment value\n");
1041 return NULL;
1043 fragmentstart = atoi(field_value);
1045 field_value = get_html_description_field(data, "EndFragment:");
1046 if (!field_value)
1048 ERR("Couldn't find EndFragment value\n");
1049 return NULL;
1051 fragmentend = atoi(field_value);
1053 /* export only the fragment */
1054 return CFDataCreate(NULL, &((const UInt8*)data)[fragmentstart], fragmentend - fragmentstart);
1058 /**************************************************************************
1059 * export_unicodetext_to_utf8
1061 * Export CF_UNICODETEXT to UTF-8.
1063 static CFDataRef export_unicodetext_to_utf8(void *data, size_t size)
1065 CFMutableDataRef ret;
1066 WCHAR *src = data;
1067 DWORD dst_len = 0;
1069 /* Leave off null terminator. */
1070 if (size >= sizeof(WCHAR) && !src[size / sizeof(WCHAR) - 1]) size -= sizeof(WCHAR);
1071 RtlUnicodeToUTF8N(NULL, 0, &dst_len, src, size);
1072 ret = CFDataCreateMutable(NULL, dst_len);
1073 if (ret)
1075 LPSTR dst;
1076 int i, j;
1078 CFDataSetLength(ret, dst_len);
1079 dst = (LPSTR)CFDataGetMutableBytePtr(ret);
1080 RtlUnicodeToUTF8N(dst, dst_len, &dst_len, src, size);
1082 /* Remove carriage returns */
1083 for (i = 0, j = 0; i < dst_len; i++)
1085 if (dst[i] == '\r' &&
1086 (i + 1 >= dst_len || dst[i + 1] == '\n' || dst[i + 1] == '\0'))
1087 continue;
1088 dst[j++] = dst[i];
1090 CFDataSetLength(ret, j);
1093 return ret;
1097 /**************************************************************************
1098 * export_unicodetext_to_utf16
1100 * Export CF_UNICODETEXT to UTF-16.
1102 static CFDataRef export_unicodetext_to_utf16(void *data, size_t size)
1104 CFMutableDataRef ret;
1105 const WCHAR *src = data;
1106 INT src_len;
1108 src_len = size / sizeof(WCHAR);
1109 if (src_len) src_len--; /* Leave off null terminator. */
1110 ret = CFDataCreateMutable(NULL, src_len * sizeof(WCHAR));
1111 if (ret)
1113 LPWSTR dst;
1114 int i, j;
1116 CFDataSetLength(ret, src_len * sizeof(WCHAR));
1117 dst = (LPWSTR)CFDataGetMutableBytePtr(ret);
1119 /* Remove carriage returns */
1120 for (i = 0, j = 0; i < src_len; i++)
1122 if (src[i] == '\r' &&
1123 (i + 1 >= src_len || src[i + 1] == '\n' || src[i + 1] == '\0'))
1124 continue;
1125 dst[j++] = src[i];
1127 CFDataSetLength(ret, j * sizeof(WCHAR));
1130 return ret;
1134 /**************************************************************************
1135 * macdrv_dnd_get_data
1137 NTSTATUS macdrv_dnd_get_data(void *arg)
1139 struct dnd_get_data_params *params = arg;
1140 CFTypeRef pasteboard = pasteboard_from_handle(params->handle);
1141 CFArrayRef types;
1142 CFIndex count;
1143 CFIndex i;
1144 CFStringRef type, best_type;
1145 WINE_CLIPFORMAT* best_format = NULL;
1146 unsigned int status = STATUS_SUCCESS;
1148 TRACE("pasteboard %p, desired_format %s\n", pasteboard, debugstr_format(params->format));
1150 types = macdrv_copy_pasteboard_types(pasteboard);
1151 if (!types)
1153 WARN("Failed to copy pasteboard types\n");
1154 return STATUS_NO_MEMORY;
1157 count = CFArrayGetCount(types);
1158 TRACE("got %ld types\n", count);
1160 for (i = 0; (!best_format || best_format->synthesized) && i < count; i++)
1162 WINE_CLIPFORMAT* format;
1164 type = CFArrayGetValueAtIndex(types, i);
1166 if ((format = format_for_type(type)))
1168 TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
1170 if (format->format_id == params->format)
1172 /* The best format is the matching one which is not synthesized. Failing that,
1173 the best format is the first matching synthesized format. */
1174 if (!format->synthesized || !best_format)
1176 best_type = type;
1177 best_format = format;
1183 if (best_format)
1185 CFDataRef pasteboard_data = macdrv_copy_pasteboard_data(pasteboard, best_type);
1187 TRACE("got pasteboard data for type %s: %s\n", debugstr_cf(best_type), debugstr_cf(pasteboard_data));
1189 if (pasteboard_data)
1191 size_t size;
1192 void *import = best_format->import_func(pasteboard_data, &size);
1193 if (import)
1195 if (size > params->size) status = STATUS_BUFFER_OVERFLOW;
1196 else memcpy(params->data, import, size);
1197 params->size = size;
1198 free(import);
1200 CFRelease(pasteboard_data);
1204 CFRelease(types);
1205 TRACE(" -> %#x\n", status);
1206 return status;
1210 /**************************************************************************
1211 * macdrv_pasteboard_has_format
1213 NTSTATUS macdrv_dnd_have_format(void *arg)
1215 struct dnd_have_format_params *params = arg;
1216 CFTypeRef pasteboard = pasteboard_from_handle(params->handle);
1217 CFArrayRef types;
1218 int count;
1219 UINT i;
1220 BOOL found = FALSE;
1222 TRACE("pasteboard %p, desired_format %s\n", pasteboard, debugstr_format(params->format));
1224 types = macdrv_copy_pasteboard_types(pasteboard);
1225 if (!types)
1227 WARN("Failed to copy pasteboard types\n");
1228 return FALSE;
1231 count = CFArrayGetCount(types);
1232 TRACE("got %d types\n", count);
1234 for (i = 0; i < count; i++)
1236 CFStringRef type = CFArrayGetValueAtIndex(types, i);
1237 WINE_CLIPFORMAT* format = format_for_type(type);
1239 if (format)
1241 TRACE("for type %s got format %s\n", debugstr_cf(type), debugstr_format(format->format_id));
1243 if (format->format_id == params->format)
1245 found = TRUE;
1246 break;
1251 CFRelease(types);
1252 TRACE(" -> %d\n", found);
1253 return found;
1257 /**************************************************************************
1258 * get_formats_for_pasteboard_types
1260 static WINE_CLIPFORMAT** get_formats_for_pasteboard_types(CFArrayRef types, UINT *num_formats)
1262 CFIndex count, i;
1263 CFMutableSetRef seen_formats;
1264 WINE_CLIPFORMAT** formats;
1265 UINT pos;
1267 count = CFArrayGetCount(types);
1268 TRACE("got %ld types\n", count);
1270 if (!count)
1271 return NULL;
1273 seen_formats = CFSetCreateMutable(NULL, count, NULL);
1274 if (!seen_formats)
1276 WARN("Failed to allocate seen formats set\n");
1277 return NULL;
1280 formats = malloc(count * sizeof(*formats));
1281 if (!formats)
1283 WARN("Failed to allocate formats array\n");
1284 CFRelease(seen_formats);
1285 return NULL;
1288 pos = 0;
1289 for (i = 0; i < count; i++)
1291 CFStringRef type = CFArrayGetValueAtIndex(types, i);
1292 WINE_CLIPFORMAT* format = format_for_type(type);
1294 if (!format)
1296 TRACE("ignoring type %s\n", debugstr_cf(type));
1297 continue;
1300 if (!format->synthesized)
1302 TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
1303 CFSetAddValue(seen_formats, ULongToPtr(format->format_id));
1304 formats[pos++] = format;
1306 else if (format->natural_format &&
1307 CFArrayContainsValue(types, CFRangeMake(0, count), format->natural_format->type))
1309 TRACE("for type %s deferring synthesized formats because type %s is also present\n",
1310 debugstr_cf(type), debugstr_cf(format->natural_format->type));
1312 else if (CFSetContainsValue(seen_formats, ULongToPtr(format->format_id)))
1314 TRACE("for type %s got duplicate synthesized format %p/%s; skipping\n", debugstr_cf(type), format,
1315 debugstr_format(format->format_id));
1317 else
1319 TRACE("for type %s got synthesized format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
1320 CFSetAddValue(seen_formats, ULongToPtr(format->format_id));
1321 formats[pos++] = format;
1325 /* Now go back through the types adding the synthesized formats that we deferred before. */
1326 for (i = 0; i < count; i++)
1328 CFStringRef type = CFArrayGetValueAtIndex(types, i);
1329 WINE_CLIPFORMAT* format = format_for_type(type);
1331 if (!format) continue;
1332 if (!format->synthesized) continue;
1334 /* Don't duplicate a real value with a synthesized value. */
1335 if (CFSetContainsValue(seen_formats, ULongToPtr(format->format_id))) continue;
1337 TRACE("for type %s got synthesized format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
1338 CFSetAddValue(seen_formats, ULongToPtr(format->format_id));
1339 formats[pos++] = format;
1342 CFRelease(seen_formats);
1344 if (!pos)
1346 free(formats);
1347 formats = NULL;
1350 *num_formats = pos;
1351 return formats;
1355 /**************************************************************************
1356 * get_formats_for_pasteboard
1358 static WINE_CLIPFORMAT** get_formats_for_pasteboard(CFTypeRef pasteboard, UINT *num_formats)
1360 CFArrayRef types;
1361 WINE_CLIPFORMAT** formats;
1363 TRACE("pasteboard %s\n", debugstr_cf(pasteboard));
1365 types = macdrv_copy_pasteboard_types(pasteboard);
1366 if (!types)
1368 WARN("Failed to copy pasteboard types\n");
1369 return NULL;
1372 formats = get_formats_for_pasteboard_types(types, num_formats);
1373 CFRelease(types);
1374 return formats;
1378 /**************************************************************************
1379 * macdrv_dnd_get_formats
1381 NTSTATUS macdrv_dnd_get_formats(void *arg)
1383 struct dnd_get_formats_params *params = arg;
1384 CFTypeRef pasteboard = pasteboard_from_handle(params->handle);
1385 WINE_CLIPFORMAT** formats;
1386 UINT count, i;
1388 formats = get_formats_for_pasteboard(pasteboard, &count);
1389 if (!formats)
1390 return 0;
1391 count = min(count, ARRAYSIZE(params->formats));
1393 for (i = 0; i < count; i++)
1394 params->formats[i] = formats[i]->format_id;
1396 return count;
1400 /**************************************************************************
1401 * register_win32_formats
1403 * Register Win32 clipboard formats the first time we encounter them.
1405 static void register_win32_formats(const UINT *ids, UINT size)
1407 unsigned int i;
1409 if (list_empty(&format_list)) register_builtin_formats();
1411 for (i = 0; i < size; i++)
1412 register_format(ids[i], NULL);
1416 /***********************************************************************
1417 * get_clipboard_formats
1419 * Return a list of all formats currently available on the Win32 clipboard.
1420 * Helper for set_mac_pasteboard_types_from_win32_clipboard.
1422 static UINT *get_clipboard_formats(UINT *size)
1424 UINT *ids;
1426 *size = 256;
1427 for (;;)
1429 if (!(ids = malloc(*size * sizeof(*ids)))) return NULL;
1430 if (NtUserGetUpdatedClipboardFormats(ids, *size, size)) break;
1431 free(ids);
1432 if (RtlGetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) return NULL;
1434 register_win32_formats(ids, *size);
1435 return ids;
1439 /**************************************************************************
1440 * set_mac_pasteboard_types_from_win32_clipboard
1442 static void set_mac_pasteboard_types_from_win32_clipboard(void)
1444 WINE_CLIPFORMAT *format;
1445 UINT count, i, *formats;
1447 if (!(formats = get_clipboard_formats(&count))) return;
1449 macdrv_clear_pasteboard(clipboard_cocoa_window);
1451 for (i = 0; i < count; i++)
1453 LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
1455 if (format->format_id != formats[i]) continue;
1456 TRACE("%s -> %s\n", debugstr_format(format->format_id), debugstr_cf(format->type));
1457 macdrv_set_pasteboard_data(format->type, NULL, clipboard_cocoa_window);
1461 free(formats);
1462 return;
1466 /**************************************************************************
1467 * set_win32_clipboard_formats_from_mac_pasteboard
1469 static void set_win32_clipboard_formats_from_mac_pasteboard(CFArrayRef types)
1471 WINE_CLIPFORMAT** formats;
1472 UINT count, i;
1474 formats = get_formats_for_pasteboard_types(types, &count);
1475 if (!formats)
1476 return;
1478 for (i = 0; i < count; i++)
1480 struct set_clipboard_params params = { 0 };
1481 TRACE("adding format %s\n", debugstr_format(formats[i]->format_id));
1482 NtUserSetClipboardData(formats[i]->format_id, 0, &params);
1485 free(current_mac_formats);
1486 current_mac_formats = formats;
1487 nb_current_mac_formats = count;
1491 /**************************************************************************
1492 * render_format
1494 static void render_format(UINT id)
1496 unsigned int i;
1498 for (i = 0; i < nb_current_mac_formats; i++)
1500 CFDataRef pasteboard_data;
1502 if (current_mac_formats[i]->format_id != id) continue;
1504 pasteboard_data = macdrv_copy_pasteboard_data(NULL, current_mac_formats[i]->type);
1505 if (pasteboard_data)
1507 struct set_clipboard_params params = { 0 };
1508 params.data = current_mac_formats[i]->import_func(pasteboard_data, &params.size);
1509 CFRelease(pasteboard_data);
1510 if (!params.data) continue;
1511 NtUserSetClipboardData(id, 0, &params);
1512 free(params.data);
1513 break;
1519 /**************************************************************************
1520 * grab_win32_clipboard
1522 * Grab the Win32 clipboard when a Mac app has taken ownership of the
1523 * pasteboard, and fill it with the pasteboard data types.
1525 static void grab_win32_clipboard(void)
1527 static CFArrayRef last_types;
1528 CFArrayRef types;
1530 types = macdrv_copy_pasteboard_types(NULL);
1531 if (!types)
1533 WARN("Failed to copy pasteboard types\n");
1534 return;
1537 if (!macdrv_has_pasteboard_changed() && last_types && CFEqual(types, last_types))
1539 CFRelease(types);
1540 return;
1543 if (last_types) CFRelease(last_types);
1544 last_types = types; /* takes ownership */
1546 if (!NtUserOpenClipboard(clipboard_hwnd, 0)) return;
1547 NtUserEmptyClipboard();
1548 is_clipboard_owner = TRUE;
1549 last_clipboard_update = NtGetTickCount();
1550 set_win32_clipboard_formats_from_mac_pasteboard(types);
1551 NtUserCloseClipboard();
1552 NtUserSetTimer(clipboard_hwnd, 1, CLIPBOARD_UPDATE_DELAY, NULL, TIMERV_DEFAULT_COALESCING);
1556 /**************************************************************************
1557 * update_clipboard
1559 * Periodically update the clipboard while the clipboard is owned by a
1560 * Mac app.
1562 static void update_clipboard(void)
1564 static BOOL updating;
1566 TRACE("is_clipboard_owner %d last_clipboard_update %u now %u\n",
1567 is_clipboard_owner, last_clipboard_update, (unsigned int)NtGetTickCount());
1569 if (updating) return;
1570 updating = TRUE;
1572 if (is_clipboard_owner)
1574 if (NtGetTickCount() - last_clipboard_update > CLIPBOARD_UPDATE_DELAY)
1575 grab_win32_clipboard();
1577 else if (!macdrv_is_pasteboard_owner(clipboard_cocoa_window))
1578 grab_win32_clipboard();
1580 updating = FALSE;
1584 static BOOL init_clipboard(HWND hwnd)
1586 struct macdrv_window_features wf;
1588 memset(&wf, 0, sizeof(wf));
1589 clipboard_cocoa_window = macdrv_create_cocoa_window(&wf, CGRectMake(100, 100, 100, 100), hwnd,
1590 macdrv_init_thread_data()->queue);
1591 if (!clipboard_cocoa_window)
1593 ERR("failed to create clipboard Cocoa window\n");
1594 return FALSE;
1597 clipboard_hwnd = hwnd;
1598 clipboard_thread_id = GetCurrentThreadId();
1599 NtUserAddClipboardFormatListener(clipboard_hwnd);
1600 register_builtin_formats();
1601 grab_win32_clipboard();
1603 TRACE("clipboard thread %04x running\n", clipboard_thread_id);
1604 return TRUE;
1608 /**************************************************************************
1609 * macdrv_ClipboardWindowProc
1611 * Window procedure for the clipboard manager.
1613 LRESULT macdrv_ClipboardWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
1615 switch (msg)
1617 case WM_NCCREATE:
1618 return init_clipboard(hwnd);
1619 case WM_CLIPBOARDUPDATE:
1620 if (is_clipboard_owner) break; /* ignore our own changes */
1621 if ((LONG)(NtUserGetClipboardSequenceNumber() - last_get_seqno) <= 0) break;
1622 set_mac_pasteboard_types_from_win32_clipboard();
1623 break;
1624 case WM_RENDERFORMAT:
1625 render_format(wp);
1626 break;
1627 case WM_TIMER:
1628 if (!is_clipboard_owner) break;
1629 grab_win32_clipboard();
1630 break;
1631 case WM_DESTROYCLIPBOARD:
1632 TRACE("WM_DESTROYCLIPBOARD: lost ownership\n");
1633 is_clipboard_owner = FALSE;
1634 NtUserKillTimer(hwnd, 1);
1635 break;
1636 case WM_USER:
1637 update_clipboard();
1638 break;
1640 return NtUserMessageCall(hwnd, msg, wp, lp, NULL, NtUserDefWindowProc, FALSE);
1644 /**************************************************************************
1645 * Mac User Driver Clipboard Exports
1646 **************************************************************************/
1649 /**************************************************************************
1650 * macdrv_UpdateClipboard
1652 void macdrv_UpdateClipboard(void)
1654 static ULONG last_update;
1655 static HWND clipboard_manager;
1656 ULONG now;
1657 DWORD_PTR ret;
1659 if (GetCurrentThreadId() == clipboard_thread_id) return;
1661 TRACE("\n");
1663 now = NtGetTickCount();
1664 if (last_update && (int)(now - last_update) <= CLIPBOARD_UPDATE_DELAY) return;
1666 if (!NtUserIsWindow(clipboard_manager))
1668 UNICODE_STRING str = RTL_CONSTANT_STRING(clipboard_classname);
1669 clipboard_manager = NtUserFindWindowEx(NULL, NULL, &str, NULL, 0);
1670 if (!clipboard_manager)
1672 ERR("clipboard manager not found\n");
1673 return;
1677 send_message_timeout(clipboard_manager, WM_USER, 0, 0,
1678 SMTO_ABORTIFHUNG, 5000, &ret);
1679 last_update = now;
1683 /**************************************************************************
1684 * MACDRV Private Clipboard Exports
1685 **************************************************************************/
1688 /**************************************************************************
1689 * query_pasteboard_data
1691 BOOL query_pasteboard_data(HWND hwnd, CFStringRef type)
1693 struct get_clipboard_params params = { .data_only = TRUE, .size = 1024 };
1694 WINE_CLIPFORMAT *format;
1695 BOOL ret = FALSE;
1697 TRACE("win %p/%p type %s\n", hwnd, clipboard_cocoa_window, debugstr_cf(type));
1699 format = format_for_type(type);
1700 if (!format) return FALSE;
1702 if (!NtUserOpenClipboard(clipboard_hwnd, 0))
1704 ERR("failed to open clipboard for %s\n", debugstr_cf(type));
1705 return FALSE;
1708 for (;;)
1710 if (!(params.data = malloc(params.size))) break;
1711 if (NtUserGetClipboardData(format->format_id, &params))
1713 CFDataRef data;
1715 TRACE("exporting %s\n", debugstr_format(format->format_id));
1717 if ((data = format->export_func(params.data, params.size)))
1719 ret = macdrv_set_pasteboard_data(format->type, data, clipboard_cocoa_window);
1720 CFRelease(data);
1722 free(params.data);
1723 break;
1725 free(params.data);
1726 if (!params.data_size) break;
1727 params.size = params.data_size;
1728 params.data_size = 0;
1731 last_get_seqno = NtUserGetClipboardSequenceNumber();
1733 NtUserCloseClipboard();
1735 return ret;
1739 /**************************************************************************
1740 * macdrv_lost_pasteboard_ownership
1742 * Handler for the LOST_PASTEBOARD_OWNERSHIP event.
1744 void macdrv_lost_pasteboard_ownership(HWND hwnd)
1746 TRACE("win %p\n", hwnd);
1747 if (!macdrv_is_pasteboard_owner(clipboard_cocoa_window))
1748 grab_win32_clipboard();
1752 /**************************************************************************
1753 * macdrv_dnd_release
1755 NTSTATUS macdrv_dnd_release(void *arg)
1757 UINT64 handle = *(UINT64 *)arg;
1758 CFRelease(pasteboard_from_handle(handle));
1759 return 0;
1763 /**************************************************************************
1764 * macdrv_dnd_retain
1766 NTSTATUS macdrv_dnd_retain(void *arg)
1768 UINT64 handle = *(UINT64 *)arg;
1769 CFRetain(pasteboard_from_handle(handle));
1770 return 0;