| /* |
| * Mac clipboard driver |
| * |
| * Copyright 1994 Martin Ayotte |
| * 1996 Alex Korobka |
| * 1999 Noel Borthwick |
| * 2003 Ulrich Czekalla for CodeWeavers |
| * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include "macdrv.h" |
| #include "winuser.h" |
| #include "shellapi.h" |
| #include "shlobj.h" |
| #include "wine/list.h" |
| #include "wine/server.h" |
| #include "wine/unicode.h" |
| |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(clipboard); |
| |
| |
| /************************************************************************** |
| * Types |
| **************************************************************************/ |
| |
| typedef HANDLE (*DRVIMPORTFUNC)(CFDataRef data); |
| typedef CFDataRef (*DRVEXPORTFUNC)(HANDLE data); |
| |
| typedef struct _WINE_CLIPFORMAT |
| { |
| struct list entry; |
| UINT format_id; |
| CFStringRef type; |
| DRVIMPORTFUNC import_func; |
| DRVEXPORTFUNC export_func; |
| BOOL synthesized; |
| struct _WINE_CLIPFORMAT *natural_format; |
| } WINE_CLIPFORMAT; |
| |
| |
| /************************************************************************** |
| * Constants |
| **************************************************************************/ |
| |
| #define CLIPBOARD_UPDATE_DELAY 2000 /* delay between checks of the Mac pasteboard */ |
| |
| |
| /************************************************************************** |
| * Forward Function Declarations |
| **************************************************************************/ |
| |
| static HANDLE import_clipboard_data(CFDataRef data); |
| static HANDLE import_bmp_to_bitmap(CFDataRef data); |
| static HANDLE import_bmp_to_dib(CFDataRef data); |
| static HANDLE import_enhmetafile(CFDataRef data); |
| static HANDLE import_html(CFDataRef data); |
| static HANDLE import_metafilepict(CFDataRef data); |
| static HANDLE import_nsfilenames_to_hdrop(CFDataRef data); |
| static HANDLE import_utf8_to_text(CFDataRef data); |
| static HANDLE import_utf8_to_unicodetext(CFDataRef data); |
| static HANDLE import_utf16_to_unicodetext(CFDataRef data); |
| |
| static CFDataRef export_clipboard_data(HANDLE data); |
| static CFDataRef export_bitmap_to_bmp(HANDLE data); |
| static CFDataRef export_dib_to_bmp(HANDLE data); |
| static CFDataRef export_enhmetafile(HANDLE data); |
| static CFDataRef export_hdrop_to_filenames(HANDLE data); |
| static CFDataRef export_html(HANDLE data); |
| static CFDataRef export_metafilepict(HANDLE data); |
| static CFDataRef export_text_to_utf8(HANDLE data); |
| static CFDataRef export_unicodetext_to_utf8(HANDLE data); |
| static CFDataRef export_unicodetext_to_utf16(HANDLE data); |
| |
| |
| /************************************************************************** |
| * Static Variables |
| **************************************************************************/ |
| |
| /* Clipboard formats */ |
| static struct list format_list = LIST_INIT(format_list); |
| |
| /* There are two naming schemes involved and we want to have a mapping between |
| them. There are Win32 clipboard format names and there are Mac pasteboard |
| types. |
| |
| The Win32 standard clipboard formats don't have names, but they are associated |
| with Mac pasteboard types through the following tables, which are used to |
| initialize the format_list. Where possible, the standard clipboard formats |
| are mapped to predefined pasteboard type UTIs. Otherwise, we create Wine- |
| specific types of the form "org.winehq.builtin.<format>", where <format> is |
| the name of the symbolic constant for the format minus "CF_" and lowercased. |
| E.g. CF_BITMAP -> org.winehq.builtin.bitmap. |
| |
| Win32 clipboard formats which originate in a Windows program may be registered |
| with an arbitrary name. We construct a Mac pasteboard type from these by |
| prepending "org.winehq.registered." to the registered name. |
| |
| Likewise, Mac pasteboard types which originate in other apps may have |
| arbitrary type strings. We ignore these. |
| |
| Summary: |
| Win32 clipboard format names: |
| <none> standard clipboard format; maps via |
| format_list to either a predefined Mac UTI |
| or org.winehq.builtin.<format>. |
| <other> name registered within Win32 land; maps to |
| org.winehq.registered.<other> |
| Mac pasteboard type names: |
| org.winehq.builtin.<format ID> representation of Win32 standard clipboard |
| format for which there was no corresponding |
| predefined Mac UTI; maps via format_list |
| org.winehq.registered.<format name> representation of Win32 registered |
| clipboard format name; maps to <format name> |
| <other> Mac pasteboard type originating with system |
| or other apps; either maps via format_list |
| to a standard clipboard format or ignored |
| */ |
| |
| static const struct |
| { |
| UINT id; |
| CFStringRef type; |
| DRVIMPORTFUNC import; |
| DRVEXPORTFUNC export; |
| BOOL synthesized; |
| } builtin_format_ids[] = |
| { |
| { CF_BITMAP, CFSTR("org.winehq.builtin.bitmap"), import_bmp_to_bitmap, export_bitmap_to_bmp, FALSE }, |
| { CF_DIBV5, CFSTR("org.winehq.builtin.dibv5"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_DIF, CFSTR("org.winehq.builtin.dif"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_ENHMETAFILE, CFSTR("org.winehq.builtin.enhmetafile"), import_enhmetafile, export_enhmetafile, FALSE }, |
| { CF_LOCALE, CFSTR("org.winehq.builtin.locale"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_METAFILEPICT, CFSTR("org.winehq.builtin.metafilepict"), import_metafilepict, export_metafilepict, FALSE }, |
| { CF_OEMTEXT, CFSTR("org.winehq.builtin.oemtext"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_PALETTE, CFSTR("org.winehq.builtin.palette"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_PENDATA, CFSTR("org.winehq.builtin.pendata"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_RIFF, CFSTR("org.winehq.builtin.riff"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_SYLK, CFSTR("org.winehq.builtin.sylk"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_TEXT, CFSTR("org.winehq.builtin.text"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_TIFF, CFSTR("public.tiff"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_WAVE, CFSTR("com.microsoft.waveform-audio"), import_clipboard_data, export_clipboard_data, FALSE }, |
| |
| { CF_DIB, CFSTR("org.winehq.builtin.dib"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_DIB, CFSTR("com.microsoft.bmp"), import_bmp_to_dib, export_dib_to_bmp, TRUE }, |
| |
| { CF_HDROP, CFSTR("org.winehq.builtin.hdrop"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_HDROP, CFSTR("NSFilenamesPboardType"), import_nsfilenames_to_hdrop, export_hdrop_to_filenames, TRUE }, |
| |
| { CF_UNICODETEXT, CFSTR("org.winehq.builtin.unicodetext"), import_clipboard_data, export_clipboard_data, FALSE }, |
| { CF_UNICODETEXT, CFSTR("public.utf16-plain-text"), import_utf16_to_unicodetext, export_unicodetext_to_utf16,TRUE }, |
| { CF_UNICODETEXT, CFSTR("public.utf8-plain-text"), import_utf8_to_unicodetext, export_unicodetext_to_utf8, TRUE }, |
| }; |
| |
| static const WCHAR wszRichTextFormat[] = {'R','i','c','h',' ','T','e','x','t',' ','F','o','r','m','a','t',0}; |
| static const WCHAR wszGIF[] = {'G','I','F',0}; |
| static const WCHAR wszJFIF[] = {'J','F','I','F',0}; |
| static const WCHAR wszPNG[] = {'P','N','G',0}; |
| static const WCHAR wszHTMLFormat[] = {'H','T','M','L',' ','F','o','r','m','a','t',0}; |
| static const struct |
| { |
| LPCWSTR name; |
| CFStringRef type; |
| DRVIMPORTFUNC import; |
| DRVEXPORTFUNC export; |
| BOOL synthesized; |
| } builtin_format_names[] = |
| { |
| { wszRichTextFormat, CFSTR("public.rtf"), import_clipboard_data, export_clipboard_data }, |
| { wszGIF, CFSTR("com.compuserve.gif"), import_clipboard_data, export_clipboard_data }, |
| { wszJFIF, CFSTR("public.jpeg"), import_clipboard_data, export_clipboard_data }, |
| { wszPNG, CFSTR("public.png"), import_clipboard_data, export_clipboard_data }, |
| { wszHTMLFormat, NULL, import_clipboard_data, export_clipboard_data }, |
| { wszHTMLFormat, CFSTR("public.html"), import_html, export_html, TRUE }, |
| { CFSTR_SHELLURLW, CFSTR("public.url"), import_utf8_to_text, export_text_to_utf8 }, |
| }; |
| |
| /* 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 DWORD last_get_seqno; |
| static WINE_CLIPFORMAT **current_mac_formats; |
| static unsigned int nb_current_mac_formats; |
| static WCHAR clipboard_pipe_name[256]; |
| |
| |
| /************************************************************************** |
| * Internal Clipboard implementation methods |
| **************************************************************************/ |
| |
| /* |
| * format_list functions |
| */ |
| |
| /************************************************************************** |
| * debugstr_format |
| */ |
| const char *debugstr_format(UINT id) |
| { |
| WCHAR buffer[256]; |
| |
| if (GetClipboardFormatNameW(id, buffer, 256)) |
| return wine_dbg_sprintf("0x%04x %s", id, debugstr_w(buffer)); |
| |
| switch (id) |
| { |
| #define BUILTIN(id) case id: return #id; |
| BUILTIN(CF_TEXT) |
| BUILTIN(CF_BITMAP) |
| BUILTIN(CF_METAFILEPICT) |
| BUILTIN(CF_SYLK) |
| BUILTIN(CF_DIF) |
| BUILTIN(CF_TIFF) |
| BUILTIN(CF_OEMTEXT) |
| BUILTIN(CF_DIB) |
| BUILTIN(CF_PALETTE) |
| BUILTIN(CF_PENDATA) |
| BUILTIN(CF_RIFF) |
| BUILTIN(CF_WAVE) |
| BUILTIN(CF_UNICODETEXT) |
| BUILTIN(CF_ENHMETAFILE) |
| BUILTIN(CF_HDROP) |
| BUILTIN(CF_LOCALE) |
| BUILTIN(CF_DIBV5) |
| BUILTIN(CF_OWNERDISPLAY) |
| BUILTIN(CF_DSPTEXT) |
| BUILTIN(CF_DSPBITMAP) |
| BUILTIN(CF_DSPMETAFILEPICT) |
| BUILTIN(CF_DSPENHMETAFILE) |
| #undef BUILTIN |
| default: return wine_dbg_sprintf("0x%04x", id); |
| } |
| } |
| |
| |
| /************************************************************************** |
| * insert_clipboard_format |
| */ |
| static WINE_CLIPFORMAT *insert_clipboard_format(UINT id, CFStringRef type) |
| { |
| WINE_CLIPFORMAT *format; |
| |
| format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)); |
| |
| if (format == NULL) |
| { |
| WARN("No more memory for a new format!\n"); |
| return NULL; |
| } |
| format->format_id = id; |
| format->import_func = import_clipboard_data; |
| format->export_func = export_clipboard_data; |
| format->synthesized = FALSE; |
| format->natural_format = NULL; |
| |
| if (type) |
| format->type = CFStringCreateCopy(NULL, type); |
| else |
| { |
| WCHAR buffer[256]; |
| |
| if (!GetClipboardFormatNameW(format->format_id, buffer, sizeof(buffer) / sizeof(buffer[0]))) |
| { |
| WARN("failed to get name for format %s; error 0x%08x\n", debugstr_format(format->format_id), GetLastError()); |
| HeapFree(GetProcessHeap(), 0, format); |
| return NULL; |
| } |
| |
| format->type = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%S"), |
| registered_name_type_prefix, buffer); |
| } |
| |
| list_add_tail(&format_list, &format->entry); |
| |
| TRACE("Registering format %s type %s\n", debugstr_format(format->format_id), |
| debugstr_cf(format->type)); |
| |
| return format; |
| } |
| |
| |
| /************************************************************************** |
| * register_format |
| * |
| * Register a custom Mac clipboard format. |
| */ |
| static WINE_CLIPFORMAT* register_format(UINT id, CFStringRef type) |
| { |
| WINE_CLIPFORMAT *format; |
| |
| /* walk format chain to see if it's already registered */ |
| LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry) |
| if (format->format_id == id) return format; |
| |
| return insert_clipboard_format(id, 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); |
| } |
| |
| /* 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->import_func = builtin_format_names[i].import; |
| format->export_func = builtin_format_names[i].export; |
| format->synthesized = builtin_format_names[i].synthesized; |
| format->natural_format = NULL; |
| |
| if (builtin_format_names[i].type) |
| format->type = CFRetain(builtin_format_names[i].type); |
| else |
| { |
| format->type = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%S"), |
| registered_name_type_prefix, builtin_format_names[i].name); |
| } |
| |
| 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); |
| } |
| } |
| |
| |
| /************************************************************************** |
| * format_for_type |
| */ |
| static WINE_CLIPFORMAT* format_for_type(CFStringRef type) |
| { |
| WINE_CLIPFORMAT *format; |
| |
| 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)) |
| goto done; |
| } |
| |
| format = NULL; |
| if (CFStringHasPrefix(type, CFSTR("org.winehq.builtin."))) |
| { |
| ERR("Shouldn't happen. Built-in type %s should have matched something in format list.\n", |
| debugstr_cf(type)); |
| } |
| else if (CFStringHasPrefix(type, registered_name_type_prefix)) |
| { |
| LPWSTR name; |
| int len = CFStringGetLength(type) - CFStringGetLength(registered_name_type_prefix); |
| |
| name = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); |
| CFStringGetCharacters(type, CFRangeMake(CFStringGetLength(registered_name_type_prefix), len), |
| (UniChar*)name); |
| name[len] = 0; |
| |
| format = register_format(RegisterClipboardFormatW(name), type); |
| if (!format) |
| ERR("Failed to register format for type %s name %s\n", debugstr_cf(type), debugstr_w(name)); |
| |
| HeapFree(GetProcessHeap(), 0, name); |
| } |
| |
| done: |
| TRACE(" -> %p/%s\n", format, debugstr_format(format ? format->format_id : 0)); |
| return format; |
| } |
| |
| |
| /*********************************************************************** |
| * bitmap_info_size |
| * |
| * Return the size of the bitmap info structure including color table. |
| */ |
| static int bitmap_info_size(const BITMAPINFO *info, WORD coloruse) |
| { |
| unsigned int colors, size, masks = 0; |
| |
| if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) |
| { |
| const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER*)info; |
| colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0; |
| return sizeof(BITMAPCOREHEADER) + colors * |
| ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD)); |
| } |
| else /* assume BITMAPINFOHEADER */ |
| { |
| colors = min(info->bmiHeader.biClrUsed, 256); |
| if (!colors && (info->bmiHeader.biBitCount <= 8)) |
| colors = 1 << info->bmiHeader.biBitCount; |
| if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3; |
| size = max(info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD)); |
| return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD)); |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * create_dib_from_bitmap |
| * |
| * Allocates a packed DIB and copies the bitmap data into it. |
| */ |
| static HGLOBAL create_dib_from_bitmap(HBITMAP bitmap) |
| { |
| HANDLE ret = 0; |
| BITMAPINFOHEADER header; |
| HDC hdc = GetDC(0); |
| DWORD header_size; |
| BITMAPINFO *bmi; |
| |
| memset(&header, 0, sizeof(header)); |
| header.biSize = sizeof(header); |
| if (!GetDIBits(hdc, bitmap, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS)) goto done; |
| |
| header_size = bitmap_info_size((BITMAPINFO *)&header, DIB_RGB_COLORS); |
| if (!(ret = GlobalAlloc(GMEM_FIXED, header_size + header.biSizeImage))) goto done; |
| bmi = (BITMAPINFO *)ret; |
| memset(bmi, 0, header_size); |
| memcpy(bmi, &header, header.biSize); |
| GetDIBits(hdc, bitmap, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS); |
| |
| done: |
| ReleaseDC(0, hdc); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * create_bitmap_from_dib |
| * |
| * Given a packed DIB, creates a bitmap object from it. |
| */ |
| static HANDLE create_bitmap_from_dib(HANDLE dib) |
| { |
| HANDLE ret = 0; |
| BITMAPINFO *bmi; |
| |
| if (dib && (bmi = GlobalLock(dib))) |
| { |
| HDC hdc; |
| unsigned int offset; |
| |
| hdc = GetDC(NULL); |
| |
| offset = bitmap_info_size(bmi, DIB_RGB_COLORS); |
| |
| ret = CreateDIBitmap(hdc, &bmi->bmiHeader, CBM_INIT, (LPBYTE)bmi + offset, |
| bmi, DIB_RGB_COLORS); |
| |
| GlobalUnlock(dib); |
| ReleaseDC(NULL, hdc); |
| } |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * get_html_description_field |
| * |
| * Find the value of a field in an HTML Format description. |
| */ |
| static const char* get_html_description_field(const char* data, const char* keyword) |
| { |
| const char* pos = data; |
| |
| while (pos && *pos && *pos != '<') |
| { |
| if (memcmp(pos, keyword, strlen(keyword)) == 0) |
| return pos + strlen(keyword); |
| |
| pos = strchr(pos, '\n'); |
| if (pos) pos++; |
| } |
| |
| return NULL; |
| } |
| |
| |
| /************************************************************************** |
| * import_clipboard_data |
| * |
| * Generic import clipboard data routine. |
| */ |
| static HANDLE import_clipboard_data(CFDataRef data) |
| { |
| HANDLE data_handle = NULL; |
| |
| size_t len = CFDataGetLength(data); |
| if (len) |
| { |
| LPVOID p; |
| |
| /* Turn on the DDESHARE flag to enable shared 32 bit memory */ |
| data_handle = GlobalAlloc(GMEM_FIXED, len); |
| if (!data_handle) |
| return NULL; |
| |
| if ((p = GlobalLock(data_handle))) |
| { |
| memcpy(p, CFDataGetBytePtr(data), len); |
| GlobalUnlock(data_handle); |
| } |
| else |
| { |
| GlobalFree(data_handle); |
| data_handle = NULL; |
| } |
| } |
| |
| return data_handle; |
| } |
| |
| |
| /************************************************************************** |
| * import_bmp_to_bitmap |
| * |
| * Import BMP data, converting to CF_BITMAP format. |
| */ |
| static HANDLE import_bmp_to_bitmap(CFDataRef data) |
| { |
| HANDLE ret; |
| HANDLE dib = import_bmp_to_dib(data); |
| |
| ret = create_bitmap_from_dib(dib); |
| |
| GlobalFree(dib); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_bmp_to_dib |
| * |
| * Import BMP data, converting to CF_DIB or CF_DIBV5 format. This just |
| * entails stripping the BMP file format header. |
| */ |
| static HANDLE import_bmp_to_dib(CFDataRef data) |
| { |
| HANDLE ret = 0; |
| BITMAPFILEHEADER *bfh = (BITMAPFILEHEADER*)CFDataGetBytePtr(data); |
| CFIndex len = CFDataGetLength(data); |
| |
| if (len >= sizeof(*bfh) + sizeof(BITMAPCOREHEADER) && |
| bfh->bfType == 0x4d42 /* "BM" */) |
| { |
| BITMAPINFO *bmi = (BITMAPINFO*)(bfh + 1); |
| BYTE* p; |
| |
| len -= sizeof(*bfh); |
| ret = GlobalAlloc(GMEM_FIXED, len); |
| if (!ret || !(p = GlobalLock(ret))) |
| { |
| GlobalFree(ret); |
| return 0; |
| } |
| |
| memcpy(p, bmi, len); |
| GlobalUnlock(ret); |
| } |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_enhmetafile |
| * |
| * Import enhanced metafile data, converting it to CF_ENHMETAFILE. |
| */ |
| static HANDLE import_enhmetafile(CFDataRef data) |
| { |
| HANDLE ret = 0; |
| CFIndex len = CFDataGetLength(data); |
| |
| TRACE("data %s\n", debugstr_cf(data)); |
| |
| if (len) |
| ret = SetEnhMetaFileBits(len, (const BYTE*)CFDataGetBytePtr(data)); |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_html |
| * |
| * Import HTML data. |
| */ |
| static HANDLE import_html(CFDataRef data) |
| { |
| static const char header[] = |
| "Version:0.9\n" |
| "StartHTML:0000000100\n" |
| "EndHTML:%010lu\n" |
| "StartFragment:%010lu\n" |
| "EndFragment:%010lu\n" |
| "<!--StartFragment-->"; |
| static const char trailer[] = "\n<!--EndFragment-->"; |
| HANDLE ret; |
| SIZE_T len, total; |
| size_t size = CFDataGetLength(data); |
| |
| len = strlen(header) + 12; /* 3 * 4 extra chars for %010lu */ |
| total = len + size + sizeof(trailer); |
| if ((ret = GlobalAlloc(GMEM_FIXED, total))) |
| { |
| char *p = ret; |
| p += sprintf(p, header, total - 1, len, len + size + 1 /* include the final \n in the data */); |
| CFDataGetBytes(data, CFRangeMake(0, size), (UInt8*)p); |
| strcpy(p + size, trailer); |
| TRACE("returning %s\n", debugstr_a(ret)); |
| } |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_metafilepict |
| * |
| * Import metafile picture data, converting it to CF_METAFILEPICT. |
| */ |
| static HANDLE import_metafilepict(CFDataRef data) |
| { |
| HANDLE ret = 0; |
| CFIndex len = CFDataGetLength(data); |
| METAFILEPICT *mfp; |
| |
| TRACE("data %s\n", debugstr_cf(data)); |
| |
| if (len >= sizeof(*mfp) && (ret = GlobalAlloc(GMEM_FIXED, sizeof(*mfp)))) |
| { |
| const BYTE *bytes = (const BYTE*)CFDataGetBytePtr(data); |
| |
| mfp = GlobalLock(ret); |
| memcpy(mfp, bytes, sizeof(*mfp)); |
| mfp->hMF = SetMetaFileBitsEx(len - sizeof(*mfp), bytes + sizeof(*mfp)); |
| GlobalUnlock(ret); |
| } |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_nsfilenames_to_hdrop |
| * |
| * Import NSFilenamesPboardType data, converting the property-list- |
| * serialized array of path strings to CF_HDROP. |
| */ |
| static HANDLE import_nsfilenames_to_hdrop(CFDataRef data) |
| { |
| HDROP hdrop = NULL; |
| CFArrayRef names; |
| CFIndex count, i; |
| size_t len; |
| char *buffer = NULL; |
| WCHAR **paths = NULL; |
| DROPFILES* dropfiles; |
| UniChar* p; |
| |
| TRACE("data %s\n", debugstr_cf(data)); |
| |
| names = (CFArrayRef)CFPropertyListCreateWithData(NULL, data, kCFPropertyListImmutable, |
| NULL, NULL); |
| if (!names || CFGetTypeID(names) != CFArrayGetTypeID()) |
| { |
| WARN("failed to interpret data as a CFArray\n"); |
| goto done; |
| } |
| |
| count = CFArrayGetCount(names); |
| |
| len = 0; |
| for (i = 0; i < count; i++) |
| { |
| CFIndex this_len; |
| CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(names, i); |
| TRACE(" %s\n", debugstr_cf(name)); |
| if (CFGetTypeID(name) != CFStringGetTypeID()) |
| { |
| WARN("non-string in array\n"); |
| goto done; |
| } |
| |
| this_len = CFStringGetMaximumSizeOfFileSystemRepresentation(name); |
| if (this_len > len) |
| len = this_len; |
| } |
| |
| buffer = HeapAlloc(GetProcessHeap(), 0, len); |
| if (!buffer) |
| { |
| WARN("failed to allocate buffer for file-system representations\n"); |
| goto done; |
| } |
| |
| paths = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, count * sizeof(paths[0])); |
| if (!paths) |
| { |
| WARN("failed to allocate array of DOS paths\n"); |
| goto done; |
| } |
| |
| for (i = 0; i < count; i++) |
| { |
| CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(names, i); |
| if (!CFStringGetFileSystemRepresentation(name, buffer, len)) |
| { |
| WARN("failed to get file-system representation for %s\n", debugstr_cf(name)); |
| goto done; |
| } |
| paths[i] = wine_get_dos_file_name(buffer); |
| if (!paths[i]) |
| { |
| WARN("failed to get DOS path for %s\n", debugstr_a(buffer)); |
| goto done; |
| } |
| } |
| |
| len = 1; /* for the terminating null */ |
| for (i = 0; i < count; i++) |
| len += strlenW(paths[i]) + 1; |
| |
| hdrop = GlobalAlloc(GMEM_FIXED, sizeof(*dropfiles) + len * sizeof(WCHAR)); |
| if (!hdrop || !(dropfiles = GlobalLock(hdrop))) |
| { |
| WARN("failed to allocate HDROP\n"); |
| GlobalFree(hdrop); |
| hdrop = NULL; |
| goto done; |
| } |
| |
| dropfiles->pFiles = sizeof(*dropfiles); |
| dropfiles->pt.x = 0; |
| dropfiles->pt.y = 0; |
| dropfiles->fNC = FALSE; |
| dropfiles->fWide = TRUE; |
| |
| p = (WCHAR*)(dropfiles + 1); |
| for (i = 0; i < count; i++) |
| { |
| strcpyW(p, paths[i]); |
| p += strlenW(p) + 1; |
| } |
| *p = 0; |
| |
| GlobalUnlock(hdrop); |
| |
| done: |
| if (paths) |
| { |
| for (i = 0; i < count; i++) |
| HeapFree(GetProcessHeap(), 0, paths[i]); |
| HeapFree(GetProcessHeap(), 0, paths); |
| } |
| HeapFree(GetProcessHeap(), 0, buffer); |
| if (names) CFRelease(names); |
| return hdrop; |
| } |
| |
| |
| /************************************************************************** |
| * import_utf8_to_text |
| * |
| * Import a UTF-8 string, converting the string to CF_TEXT. |
| */ |
| static HANDLE import_utf8_to_text(CFDataRef data) |
| { |
| HANDLE ret = NULL; |
| HANDLE unicode_handle = import_utf8_to_unicodetext(data); |
| LPWSTR unicode_string = GlobalLock(unicode_handle); |
| |
| if (unicode_string) |
| { |
| int unicode_len; |
| HANDLE handle; |
| char *p; |
| INT len; |
| |
| unicode_len = GlobalSize(unicode_handle) / sizeof(WCHAR); |
| |
| len = WideCharToMultiByte(CP_ACP, 0, unicode_string, unicode_len, NULL, 0, NULL, NULL); |
| if (!unicode_len || unicode_string[unicode_len - 1]) len += 1; |
| handle = GlobalAlloc(GMEM_FIXED, len); |
| |
| if (handle && (p = GlobalLock(handle))) |
| { |
| WideCharToMultiByte(CP_ACP, 0, unicode_string, unicode_len, p, len, NULL, NULL); |
| p[len - 1] = 0; |
| GlobalUnlock(handle); |
| ret = handle; |
| } |
| GlobalUnlock(unicode_handle); |
| } |
| |
| GlobalFree(unicode_handle); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * import_utf8_to_unicodetext |
| * |
| * Import a UTF-8 string, converting the string to CF_UNICODETEXT. |
| */ |
| static HANDLE import_utf8_to_unicodetext(CFDataRef data) |
| { |
| const BYTE *src; |
| unsigned long src_len; |
| unsigned long new_lines = 0; |
| LPSTR dst; |
| unsigned long i, j; |
| HANDLE unicode_handle = NULL; |
| |
| src = CFDataGetBytePtr(data); |
| src_len = CFDataGetLength(data); |
| for (i = 0; i < src_len; i++) |
| { |
| if (src[i] == '\n') |
| new_lines++; |
| } |
| |
| if ((dst = HeapAlloc(GetProcessHeap(), 0, src_len + new_lines + 1))) |
| { |
| UINT count; |
| |
| for (i = 0, j = 0; i < src_len; i++) |
| { |
| if (src[i] == '\n') |
| dst[j++] = '\r'; |
| |
| dst[j++] = src[i]; |
| } |
| dst[j] = 0; |
| |
| count = MultiByteToWideChar(CP_UTF8, 0, dst, -1, NULL, 0); |
| unicode_handle = GlobalAlloc(GMEM_FIXED, count * sizeof(WCHAR)); |
| |
| if (unicode_handle) |
| { |
| WCHAR *textW = GlobalLock(unicode_handle); |
| MultiByteToWideChar(CP_UTF8, 0, dst, -1, textW, count); |
| GlobalUnlock(unicode_handle); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, dst); |
| } |
| |
| return unicode_handle; |
| } |
| |
| |
| /************************************************************************** |
| * import_utf16_to_unicodetext |
| * |
| * Import a UTF-8 string, converting the string to CF_UNICODETEXT. |
| */ |
| static HANDLE import_utf16_to_unicodetext(CFDataRef data) |
| { |
| const WCHAR *src; |
| unsigned long src_len; |
| unsigned long new_lines = 0; |
| LPWSTR dst; |
| unsigned long i, j; |
| HANDLE unicode_handle; |
| |
| src = (const WCHAR *)CFDataGetBytePtr(data); |
| src_len = CFDataGetLength(data) / sizeof(WCHAR); |
| for (i = 0; i < src_len; i++) |
| { |
| if (src[i] == '\n') |
| new_lines++; |
| else if (src[i] == '\r' && (i + 1 >= src_len || src[i + 1] != '\n')) |
| new_lines++; |
| } |
| |
| if ((unicode_handle = GlobalAlloc(GMEM_FIXED, (src_len + new_lines + 1) * sizeof(WCHAR)))) |
| { |
| dst = GlobalLock(unicode_handle); |
| |
| for (i = 0, j = 0; i < src_len; i++) |
| { |
| if (src[i] == '\n') |
| dst[j++] = '\r'; |
| |
| dst[j++] = src[i]; |
| |
| if (src[i] == '\r' && (i + 1 >= src_len || src[i + 1] != '\n')) |
| dst[j++] = '\n'; |
| } |
| dst[j] = 0; |
| |
| GlobalUnlock(unicode_handle); |
| } |
| |
| return unicode_handle; |
| } |
| |
| |
| /************************************************************************** |
| * 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_bitmap_to_bmp |
| * |
| * Export CF_BITMAP to BMP file format. |
| */ |
| static CFDataRef export_bitmap_to_bmp(HANDLE data) |
| { |
| CFDataRef ret = NULL; |
| HGLOBAL dib; |
| |
| dib = create_dib_from_bitmap(data); |
| if (dib) |
| { |
| ret = export_dib_to_bmp(dib); |
| GlobalFree(dib); |
| } |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * export_dib_to_bmp |
| * |
| * Export CF_DIB or CF_DIBV5 to BMP file format. This just entails |
| * prepending a BMP file format header to the data. |
| */ |
| static CFDataRef export_dib_to_bmp(HANDLE data) |
| { |
| CFMutableDataRef ret = NULL; |
| BYTE *dibdata; |
| CFIndex len; |
| BITMAPFILEHEADER bfh; |
| |
| dibdata = GlobalLock(data); |
| if (!dibdata) |
| return NULL; |
| |
| len = sizeof(bfh) + GlobalSize(data); |
| ret = CFDataCreateMutable(NULL, len); |
| if (ret) |
| { |
| bfh.bfType = 0x4d42; /* "BM" */ |
| bfh.bfSize = len; |
| bfh.bfReserved1 = 0; |
| bfh.bfReserved2 = 0; |
| bfh.bfOffBits = sizeof(bfh) + bitmap_info_size((BITMAPINFO*)dibdata, DIB_RGB_COLORS); |
| CFDataAppendBytes(ret, (UInt8*)&bfh, sizeof(bfh)); |
| |
| /* rest of bitmap is the same as the packed dib */ |
| CFDataAppendBytes(ret, (UInt8*)dibdata, len - sizeof(bfh)); |
| } |
| |
| GlobalUnlock(data); |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * export_enhmetafile |
| * |
| * Export an enhanced metafile to data. |
| */ |
| static CFDataRef export_enhmetafile(HANDLE data) |
| { |
| CFMutableDataRef ret = NULL; |
| unsigned int size = GetEnhMetaFileBits(data, 0, NULL); |
| |
| TRACE("data %p\n", data); |
| |
| ret = CFDataCreateMutable(NULL, size); |
| if (ret) |
| { |
| CFDataSetLength(ret, size); |
| GetEnhMetaFileBits(data, size, (BYTE*)CFDataGetMutableBytePtr(ret)); |
| } |
| |
| TRACE(" -> %s\n", debugstr_cf(ret)); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * export_hdrop_to_filenames |
| * |
| * Export CF_HDROP to NSFilenamesPboardType data, which is a CFArray of |
| * CFStrings (holding Unix paths) which is serialized as a property list. |
| */ |
| static CFDataRef export_hdrop_to_filenames(HANDLE data) |
| { |
| CFDataRef ret = NULL; |
| DROPFILES *dropfiles; |
| CFMutableArrayRef filenames = NULL; |
| void *p; |
| WCHAR *buffer = NULL; |
| size_t buffer_len = 0; |
| |
| TRACE("data %p\n", data); |
| |
| if (!(dropfiles = GlobalLock(data))) |
| { |
| WARN("failed to lock data %p\n", data); |
| goto done; |
| } |
| |
| filenames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
| if (!filenames) |
| { |
| WARN("failed to create filenames array\n"); |
| goto done; |
| } |
| |
| p = (char*)dropfiles + dropfiles->pFiles; |
| while (dropfiles->fWide ? *(WCHAR*)p : *(char*)p) |
| { |
| char *unixname; |
| CFStringRef filename; |
| |
| TRACE(" %s\n", dropfiles->fWide ? debugstr_w(p) : debugstr_a(p)); |
| |
| if (dropfiles->fWide) |
| unixname = wine_get_unix_file_name(p); |
| else |
| { |
| int len = MultiByteToWideChar(CP_ACP, 0, p, -1, NULL, 0); |
| if (len) |
| { |
| if (len > buffer_len) |
| { |
| HeapFree(GetProcessHeap(), 0, buffer); |
| buffer_len = len * 2; |
| buffer = HeapAlloc(GetProcessHeap(), 0, buffer_len * sizeof(*buffer)); |
| } |
| |
| MultiByteToWideChar(CP_ACP, 0, p, -1, buffer, buffer_len); |
| unixname = wine_get_unix_file_name(buffer); |
| } |
| else |
| unixname = NULL; |
| } |
| if (!unixname) |
| { |
| WARN("failed to convert DOS path to Unix: %s\n", |
| dropfiles->fWide ? debugstr_w(p) : debugstr_a(p)); |
| goto done; |
| } |
| |
| if (dropfiles->fWide) |
| p = (WCHAR*)p + strlenW(p) + 1; |
| else |
| p = (char*)p + strlen(p) + 1; |
| |
| filename = CFStringCreateWithFileSystemRepresentation(NULL, unixname); |
| HeapFree(GetProcessHeap(), 0, unixname); |
| if (!filename) |
| { |
| WARN("failed to create CFString from Unix path %s\n", debugstr_a(unixname)); |
| goto done; |
| } |
| |
| CFArrayAppendValue(filenames, filename); |
| CFRelease(filename); |
| } |
| |
| ret = CFPropertyListCreateData(NULL, filenames, kCFPropertyListXMLFormat_v1_0, 0, NULL); |
| |
| done: |
| HeapFree(GetProcessHeap(), 0, buffer); |
| GlobalUnlock(data); |
| if (filenames) CFRelease(filenames); |
| TRACE(" -> %s\n", debugstr_cf(ret)); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * export_html |
| * |
| * Export HTML Format to public.html data. |
| * |
| * FIXME: We should attempt to add an <a base> tag and convert windows paths. |
| */ |
| static CFDataRef export_html(HANDLE handle) |
| { |
| CFDataRef ret; |
| const char *data, *field_value; |
| int fragmentstart, fragmentend; |
| |
| data = GlobalLock(handle); |
| |
| /* read the important fields */ |
| field_value = get_html_description_field(data, "StartFragment:"); |
| if (!field_value) |
| { |
| ERR("Couldn't find StartFragment value\n"); |
| goto failed; |
| } |
| fragmentstart = atoi(field_value); |
| |
| field_value = get_html_description_field(data, "EndFragment:"); |
| if (!field_value) |
| { |
| ERR("Couldn't find EndFragment value\n"); |
| goto failed; |
| } |
| fragmentend = atoi(field_value); |
| |
| /* export only the fragment */ |
| ret = CFDataCreate(NULL, (const UInt8*)&data[fragmentstart], fragmentend - fragmentstart); |
| GlobalUnlock(handle); |
| return ret; |
| |
| failed: |
| GlobalUnlock(handle); |
| return NULL; |
| } |
| |
| |
| /************************************************************************** |
| * export_metafilepict |
| * |
| * Export a metafile to data. |
| */ |
| static CFDataRef export_metafilepict(HANDLE data) |
| { |
| CFMutableDataRef ret = NULL; |
| METAFILEPICT *mfp = GlobalLock(data); |
| unsigned int size = GetMetaFileBitsEx(mfp->hMF, 0, NULL); |
| |
| TRACE("data %p\n", data); |
| |
| ret = CFDataCreateMutable(NULL, sizeof(*mfp) + size); |
| if (ret) |
| { |
| CFDataAppendBytes(ret, (UInt8*)mfp, sizeof(*mfp)); |
| CFDataIncreaseLength(ret, size); |
| GetMetaFileBitsEx(mfp->hMF, size, (BYTE*)CFDataGetMutableBytePtr(ret) + sizeof(*mfp)); |
| } |
| |
| GlobalUnlock(data); |
| TRACE(" -> %s\n", debugstr_cf(ret)); |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * export_text_to_utf8 |
| * |
| * Export CF_TEXT to UTF-8. |
| */ |
| static CFDataRef export_text_to_utf8(HANDLE data) |
| { |
| CFDataRef ret = NULL; |
| const char* str; |
| |
| if ((str = GlobalLock(data))) |
| { |
| int str_len = GlobalSize(data); |
| int wstr_len; |
| WCHAR *wstr; |
| HANDLE unicode; |
| char *p; |
| |
| wstr_len = MultiByteToWideChar(CP_ACP, 0, str, str_len, NULL, 0); |
| if (!str_len || str[str_len - 1]) wstr_len += 1; |
| wstr = HeapAlloc(GetProcessHeap(), 0, wstr_len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, str, str_len, wstr, wstr_len); |
| wstr[wstr_len - 1] = 0; |
| |
| unicode = GlobalAlloc(GMEM_FIXED, wstr_len * sizeof(WCHAR)); |
| if (unicode && (p = GlobalLock(unicode))) |
| { |
| memcpy(p, wstr, wstr_len * sizeof(WCHAR)); |
| GlobalUnlock(unicode); |
| } |
| |
| ret = export_unicodetext_to_utf8(unicode); |
| |
| GlobalFree(unicode); |
| GlobalUnlock(data); |
| } |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * 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; |
| } |
| |
| |
| /************************************************************************** |
| * export_unicodetext_to_utf16 |
| * |
| * Export CF_UNICODETEXT to UTF-16. |
| */ |
| static CFDataRef export_unicodetext_to_utf16(HANDLE data) |
| { |
| CFMutableDataRef ret; |
| const WCHAR *src; |
| INT src_len; |
| |
| src = GlobalLock(data); |
| if (!src) return NULL; |
| |
| src_len = GlobalSize(data) / sizeof(WCHAR); |
| if (src_len) src_len--; /* Leave off null terminator. */ |
| ret = CFDataCreateMutable(NULL, src_len * sizeof(WCHAR)); |
| if (ret) |
| { |
| LPWSTR dst; |
| int i, j; |
| |
| CFDataSetLength(ret, src_len * sizeof(WCHAR)); |
| dst = (LPWSTR)CFDataGetMutableBytePtr(ret); |
| |
| /* Remove carriage returns */ |
| for (i = 0, j = 0; i < src_len; i++) |
| { |
| if (src[i] == '\r' && |
| (i + 1 >= src_len || src[i + 1] == '\n' || src[i + 1] == '\0')) |
| continue; |
| dst[j++] = src[i]; |
| } |
| CFDataSetLength(ret, j * sizeof(WCHAR)); |
| } |
| GlobalUnlock(data); |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * macdrv_get_pasteboard_data |
| */ |
| HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_format) |
| { |
| CFArrayRef types; |
| CFIndex count; |
| CFIndex i; |
| CFStringRef type, best_type; |
| WINE_CLIPFORMAT* best_format = NULL; |
| HANDLE data = NULL; |
| |
| TRACE("pasteboard %p, desired_format %s\n", pasteboard, debugstr_format(desired_format)); |
| |
| types = macdrv_copy_pasteboard_types(pasteboard); |
| if (!types) |
| { |
| WARN("Failed to copy pasteboard types\n"); |
| return NULL; |
| } |
| |
| count = CFArrayGetCount(types); |
| TRACE("got %ld types\n", count); |
| |
| for (i = 0; (!best_format || best_format->synthesized) && i < count; i++) |
| { |
| WINE_CLIPFORMAT* format; |
| |
| type = CFArrayGetValueAtIndex(types, i); |
| |
| if ((format = format_for_type(type))) |
| { |
| TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id)); |
| |
| if (format->format_id == desired_format) |
| { |
| /* The best format is the matching one which is not synthesized. Failing that, |
| the best format is the first matching synthesized format. */ |
| if (!format->synthesized || !best_format) |
| { |
| best_type = type; |
| best_format = format; |
| } |
| } |
| } |
| } |
| |
| if (best_format) |
| { |
| CFDataRef pasteboard_data = macdrv_copy_pasteboard_data(pasteboard, best_type); |
| |
| TRACE("got pasteboard data for type %s: %s\n", debugstr_cf(best_type), debugstr_cf(pasteboard_data)); |
| |
| if (pasteboard_data) |
| { |
| data = best_format->import_func(pasteboard_data); |
| CFRelease(pasteboard_data); |
| } |
| } |
| |
| CFRelease(types); |
| TRACE(" -> %p\n", data); |
| return data; |
| } |
| |
| |
| /************************************************************************** |
| * macdrv_pasteboard_has_format |
| */ |
| BOOL macdrv_pasteboard_has_format(CFTypeRef pasteboard, UINT desired_format) |
| { |
| CFArrayRef types; |
| int count; |
| UINT i; |
| BOOL found = FALSE; |
| |
| TRACE("pasteboard %p, desired_format %s\n", pasteboard, debugstr_format(desired_format)); |
| |
| types = macdrv_copy_pasteboard_types(pasteboard); |
| if (!types) |
| { |
| WARN("Failed to copy pasteboard types\n"); |
| return FALSE; |
| } |
| |
| count = CFArrayGetCount(types); |
| TRACE("got %d types\n", count); |
| |
| for (i = 0; i < count; i++) |
| { |
| CFStringRef type = CFArrayGetValueAtIndex(types, i); |
| WINE_CLIPFORMAT* format = format_for_type(type); |
| |
| if (format) |
| { |
| TRACE("for type %s got format %s\n", debugstr_cf(type), debugstr_format(format->format_id)); |
| |
| if (format->format_id == desired_format) |
| { |
| found = TRUE; |
| break; |
| } |
| } |
| } |
| |
| CFRelease(types); |
| TRACE(" -> %d\n", found); |
| return found; |
| } |
| |
| |
| /************************************************************************** |
| * get_formats_for_pasteboard_types |
| */ |
| static WINE_CLIPFORMAT** get_formats_for_pasteboard_types(CFArrayRef types, UINT *num_formats) |
| { |
| CFIndex count, i; |
| CFMutableSetRef seen_formats; |
| WINE_CLIPFORMAT** formats; |
| UINT pos; |
| |
| count = CFArrayGetCount(types); |
| TRACE("got %ld types\n", count); |
| |
| if (!count) |
| return NULL; |
| |
| seen_formats = CFSetCreateMutable(NULL, count, NULL); |
| if (!seen_formats) |
| { |
| WARN("Failed to allocate seen formats set\n"); |
| return NULL; |
| } |
| |
| formats = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*formats)); |
| if (!formats) |
| { |
| WARN("Failed to allocate formats array\n"); |
| CFRelease(seen_formats); |
| return NULL; |
| } |
| |
| pos = 0; |
| for (i = 0; i < count; i++) |
| { |
| CFStringRef type = CFArrayGetValueAtIndex(types, i); |
| WINE_CLIPFORMAT* format = format_for_type(type); |
| |
| if (!format) |
| { |
| TRACE("ignoring type %s\n", debugstr_cf(type)); |
| continue; |
| } |
| |
| if (!format->synthesized) |
| { |
| TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id)); |
| CFSetAddValue(seen_formats, (void*)format->format_id); |
| formats[pos++] = format; |
| } |
| else if (format->natural_format && |
| CFArrayContainsValue(types, CFRangeMake(0, count), format->natural_format->type)) |
| { |
| TRACE("for type %s deferring synthesized formats because type %s is also present\n", |
| debugstr_cf(type), debugstr_cf(format->natural_format->type)); |
| } |
| else if (CFSetContainsValue(seen_formats, (void*)format->format_id)) |
| { |
| TRACE("for type %s got duplicate synthesized format %p/%s; skipping\n", debugstr_cf(type), format, |
| debugstr_format(format->format_id)); |
| } |
| else |
| { |
| TRACE("for type %s got synthesized format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id)); |
| CFSetAddValue(seen_formats, (void*)format->format_id); |
| formats[pos++] = format; |
| } |
| } |
| |
| /* Now go back through the types adding the synthesized formats that we deferred before. */ |
| for (i = 0; i < count; i++) |
| { |
| CFStringRef type = CFArrayGetValueAtIndex(types, i); |
| WINE_CLIPFORMAT* format = format_for_type(type); |
| |
| if (!format) continue; |
| if (!format->synthesized) continue; |
| |
| /* Don't duplicate a real value with a synthesized value. */ |
| if (CFSetContainsValue(seen_formats, (void*)format->format_id)) continue; |
| |
| TRACE("for type %s got synthesized format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id)); |
| CFSetAddValue(seen_formats, (void*)format->format_id); |
| formats[pos++] = format; |
| } |
| |
| CFRelease(seen_formats); |
| |
| if (!pos) |
| { |
| HeapFree(GetProcessHeap(), 0, formats); |
| formats = NULL; |
| } |
| |
| *num_formats = pos; |
| return formats; |
| } |
| |
| |
| /************************************************************************** |
| * get_formats_for_pasteboard |
| */ |
| static WINE_CLIPFORMAT** get_formats_for_pasteboard(CFTypeRef pasteboard, UINT *num_formats) |
| { |
| CFArrayRef types; |
| WINE_CLIPFORMAT** formats; |
| |
| TRACE("pasteboard %s\n", debugstr_cf(pasteboard)); |
| |
| types = macdrv_copy_pasteboard_types(pasteboard); |
| if (!types) |
| { |
| WARN("Failed to copy pasteboard types\n"); |
| return NULL; |
| } |
| |
| formats = get_formats_for_pasteboard_types(types, num_formats); |
| CFRelease(types); |
| return formats; |
| } |
| |
| |
| /************************************************************************** |
| * macdrv_get_pasteboard_formats |
| */ |
| UINT* macdrv_get_pasteboard_formats(CFTypeRef pasteboard, UINT* num_formats) |
| { |
| WINE_CLIPFORMAT** formats; |
| UINT count, i; |
| UINT* format_ids; |
| |
| formats = get_formats_for_pasteboard(pasteboard, &count); |
| if (!formats) |
| return NULL; |
| |
| format_ids = HeapAlloc(GetProcessHeap(), 0, count); |
| if (!format_ids) |
| { |
| WARN("Failed to allocate formats IDs array\n"); |
| HeapFree(GetProcessHeap(), 0, formats); |
| return NULL; |
| } |
| |
| for (i = 0; i < count; i++) |
| format_ids[i] = formats[i]->format_id; |
| |
| HeapFree(GetProcessHeap(), 0, formats); |
| |
| *num_formats = count; |
| return format_ids; |
| } |
| |
| |
| /************************************************************************** |
| * 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(clipboard_cocoa_window); |
| |
| 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(CFArrayRef types) |
| { |
| WINE_CLIPFORMAT** formats; |
| UINT count, i; |
| |
| formats = get_formats_for_pasteboard_types(types, &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 void grab_win32_clipboard(void) |
| { |
| static CFArrayRef last_types; |
| CFArrayRef types; |
| |
| types = macdrv_copy_pasteboard_types(NULL); |
| if (!types) |
| { |
| WARN("Failed to copy pasteboard types\n"); |
| return; |
| } |
| |
| if (!macdrv_has_pasteboard_changed() && last_types && CFEqual(types, last_types)) |
| { |
| CFRelease(types); |
| return; |
| } |
| |
| if (last_types) CFRelease(last_types); |
| last_types = types; /* takes ownership */ |
| |
| if (!OpenClipboard(clipboard_hwnd)) return; |
| EmptyClipboard(); |
| is_clipboard_owner = TRUE; |
| last_clipboard_update = GetTickCount64(); |
| set_win32_clipboard_formats_from_mac_pasteboard(types); |
| CloseClipboard(); |
| SetTimer(clipboard_hwnd, 1, CLIPBOARD_UPDATE_DELAY, NULL); |
| } |
| |
| |
| /************************************************************************** |
| * update_clipboard |
| * |
| * Periodically update the clipboard while the clipboard is owned by a |
| * Mac app. |
| */ |
| static void update_clipboard(void) |
| { |
| static BOOL updating; |
| |
| TRACE("is_clipboard_owner %d last_clipboard_update %llu now %llu\n", |
| is_clipboard_owner, last_clipboard_update, GetTickCount64()); |
| |
| if (updating) return; |
| updating = TRUE; |
| |
| if (is_clipboard_owner) |
| { |
| if (GetTickCount64() - last_clipboard_update > CLIPBOARD_UPDATE_DELAY) |
| grab_win32_clipboard(); |
| } |
| else if (!macdrv_is_pasteboard_owner(clipboard_cocoa_window)) |
| grab_win32_clipboard(); |
| |
| updating = FALSE; |
| } |
| |
| |
| /************************************************************************** |
| * 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 */ |
| if ((LONG)(GetClipboardSequenceNumber() - last_get_seqno) <= 0) break; |
| set_mac_pasteboard_types_from_win32_clipboard(); |
| break; |
| case WM_RENDERFORMAT: |
| render_format(wp); |
| break; |
| case WM_TIMER: |
| if (!is_clipboard_owner) break; |
| grab_win32_clipboard(); |
| break; |
| case WM_DESTROYCLIPBOARD: |
| TRACE("WM_DESTROYCLIPBOARD: lost ownership\n"); |
| is_clipboard_owner = FALSE; |
| KillTimer(hwnd, 1); |
| 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; |
| } |
| |
| |
| /************************************************************************** |
| * init_pipe_name |
| * |
| * Init-once helper for get_pipe_name. |
| */ |
| static BOOL CALLBACK init_pipe_name(INIT_ONCE* once, void* param, void** context) |
| { |
| static const WCHAR prefix[] = {'\\','\\','.','\\','p','i','p','e','\\','_','_','w','i','n','e','_','c','l','i','p','b','o','a','r','d','_'}; |
| |
| memcpy(clipboard_pipe_name, prefix, sizeof(prefix)); |
| if (!GetUserObjectInformationW(GetProcessWindowStation(), UOI_NAME, |
| clipboard_pipe_name + sizeof(prefix) / sizeof(WCHAR), |
| sizeof(clipboard_pipe_name) - sizeof(prefix), NULL)) |
| { |
| ERR("failed to get winstation name\n"); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| |
| /************************************************************************** |
| * get_pipe_name |
| * |
| * Get the name of the pipe used to communicate with the per-window-station |
| * clipboard manager thread. |
| */ |
| static const WCHAR* get_pipe_name(void) |
| { |
| static INIT_ONCE once = INIT_ONCE_STATIC_INIT; |
| InitOnceExecuteOnce(&once, init_pipe_name, NULL, NULL); |
| return clipboard_pipe_name[0] ? clipboard_pipe_name : NULL; |
| } |
| |
| |
| /************************************************************************** |
| * 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; |
| const WCHAR* pipe_name; |
| HANDLE pipe = NULL; |
| HANDLE event = NULL; |
| OVERLAPPED overlapped; |
| BOOL need_connect = TRUE, pending = FALSE; |
| 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; |
| } |
| |
| pipe_name = get_pipe_name(); |
| if (!pipe_name) |
| { |
| ERR("failed to get pipe name\n"); |
| goto done; |
| } |
| |
| pipe = CreateNamedPipeW(pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, |
| PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 1, 1, 0, NULL); |
| if (!pipe) |
| { |
| ERR("failed to create named pipe: %u\n", GetLastError()); |
| goto done; |
| } |
| |
| event = CreateEventW(NULL, TRUE, FALSE, NULL); |
| if (!event) |
| { |
| ERR("failed to create event: %d\n", GetLastError()); |
| goto done; |
| } |
| |
| clipboard_thread_id = GetCurrentThreadId(); |
| AddClipboardFormatListener(clipboard_hwnd); |
| register_builtin_formats(); |
| grab_win32_clipboard(); |
| |
| TRACE("clipboard thread %04x running\n", GetCurrentThreadId()); |
| while (1) |
| { |
| DWORD result; |
| |
| if (need_connect) |
| { |
| pending = FALSE; |
| memset(&overlapped, 0, sizeof(overlapped)); |
| overlapped.hEvent = event; |
| if (ConnectNamedPipe(pipe, &overlapped)) |
| { |
| ERR("asynchronous ConnectNamedPipe unexpectedly returned true: %d\n", GetLastError()); |
| ResetEvent(event); |
| } |
| else |
| { |
| result = GetLastError(); |
| switch (result) |
| { |
| case ERROR_PIPE_CONNECTED: |
| case ERROR_NO_DATA: |
| SetEvent(event); |
| need_connect = FALSE; |
| break; |
| case ERROR_IO_PENDING: |
| need_connect = FALSE; |
| pending = TRUE; |
| break; |
| default: |
| ERR("failed to initiate pipe connection: %d\n", result); |
| break; |
| } |
| } |
| } |
| |
| result = MsgWaitForMultipleObjectsEx(1, &event, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); |
| switch (result) |
| { |
| case WAIT_OBJECT_0: |
| { |
| DWORD written; |
| |
| if (pending && !GetOverlappedResult(pipe, &overlapped, &written, FALSE)) |
| ERR("failed to connect pipe: %d\n", GetLastError()); |
| |
| update_clipboard(); |
| DisconnectNamedPipe(pipe); |
| need_connect = TRUE; |
| break; |
| } |
| case WAIT_OBJECT_0 + 1: |
| while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) |
| { |
| if (msg.message == WM_QUIT) |
| goto done; |
| DispatchMessageW(&msg); |
| } |
| break; |
| case WAIT_IO_COMPLETION: |
| break; |
| default: |
| ERR("failed to wait for connection or input: %d\n", GetLastError()); |
| break; |
| } |
| } |
| |
| done: |
| if (event) CloseHandle(event); |
| if (pipe) CloseHandle(pipe); |
| macdrv_destroy_cocoa_window(clipboard_cocoa_window); |
| DestroyWindow(clipboard_hwnd); |
| return 0; |
| } |
| |
| |
| /************************************************************************** |
| * Mac User Driver Clipboard Exports |
| **************************************************************************/ |
| |
| |
| /************************************************************************** |
| * macdrv_UpdateClipboard |
| */ |
| void CDECL macdrv_UpdateClipboard(void) |
| { |
| static ULONG last_update; |
| ULONG now, end; |
| const WCHAR* pipe_name; |
| HANDLE pipe; |
| BYTE dummy; |
| DWORD count; |
| OVERLAPPED overlapped = { 0 }; |
| BOOL canceled = FALSE; |
| |
| if (GetCurrentThreadId() == clipboard_thread_id) return; |
| |
| TRACE("\n"); |
| |
| now = GetTickCount(); |
| if ((int)(now - last_update) <= CLIPBOARD_UPDATE_DELAY) return; |
| last_update = now; |
| |
| if (!(pipe_name = get_pipe_name())) return; |
| pipe = CreateFileW(pipe_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, |
| OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); |
| if (pipe == INVALID_HANDLE_VALUE) |
| { |
| WARN("failed to open pipe to clipboard manager: %d\n", GetLastError()); |
| return; |
| } |
| |
| overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); |
| if (!overlapped.hEvent) |
| { |
| ERR("failed to create event: %d\n", GetLastError()); |
| goto done; |
| } |
| |
| /* We expect the read to fail because the server just closes our connection. This |
| is just waiting for that close to happen. */ |
| if (ReadFile(pipe, &dummy, sizeof(dummy), NULL, &overlapped)) |
| { |
| WARN("asynchronous ReadFile unexpectedly returned true: %d\n", GetLastError()); |
| goto done; |
| } |
| else |
| { |
| DWORD error = GetLastError(); |
| if (error == ERROR_PIPE_NOT_CONNECTED || error == ERROR_BROKEN_PIPE) |
| { |
| /* The server accepted, handled, and closed our connection before we |
| attempted the read, which is fine. */ |
| goto done; |
| } |
| else if (error != ERROR_IO_PENDING) |
| { |
| ERR("failed to initiate read from pipe: %d\n", error); |
| goto done; |
| } |
| } |
| |
| end = now + 500; |
| while (1) |
| { |
| DWORD result, timeout; |
| |
| if (canceled) |
| timeout = INFINITE; |
| else |
| { |
| now = GetTickCount(); |
| timeout = end - now; |
| if ((int)timeout < 0) |
| timeout = 0; |
| } |
| |
| result = MsgWaitForMultipleObjectsEx(1, &overlapped.hEvent, timeout, QS_SENDMESSAGE, MWMO_ALERTABLE); |
| switch (result) |
| { |
| case WAIT_OBJECT_0: |
| { |
| if (GetOverlappedResult(pipe, &overlapped, &count, FALSE)) |
| WARN("unexpectedly succeeded in reading from pipe\n"); |
| else |
| { |
| result = GetLastError(); |
| if (result != ERROR_BROKEN_PIPE && result != ERROR_OPERATION_ABORTED && |
| result != ERROR_HANDLES_CLOSED) |
| WARN("failed to read from pipe: %d\n", result); |
| } |
| |
| goto done; |
| } |
| case WAIT_OBJECT_0 + 1: |
| { |
| MSG msg; |
| while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE)) |
| DispatchMessageW(&msg); |
| break; |
| } |
| case WAIT_IO_COMPLETION: |
| break; |
| case WAIT_TIMEOUT: |
| WARN("timed out waiting for read\n"); |
| CancelIoEx(pipe, &overlapped); |
| canceled = TRUE; |
| break; |
| default: |
| if (canceled) |
| { |
| ERR("failed to wait for cancel: %d\n", GetLastError()); |
| goto done; |
| } |
| |
| ERR("failed to wait for read: %d\n", GetLastError()); |
| CancelIoEx(pipe, &overlapped); |
| canceled = TRUE; |
| break; |
| } |
| } |
| |
| done: |
| if (overlapped.hEvent) CloseHandle(overlapped.hEvent); |
| CloseHandle(pipe); |
| } |
| |
| |
| /************************************************************************** |
| * MACDRV Private Clipboard Exports |
| **************************************************************************/ |
| |
| |
| /************************************************************************** |
| * query_pasteboard_data |
| */ |
| BOOL query_pasteboard_data(HWND hwnd, CFStringRef type) |
| { |
| WINE_CLIPFORMAT *format; |
| BOOL ret = FALSE; |
| HANDLE handle; |
| |
| 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(clipboard_hwnd)) |
| { |
| ERR("failed to open clipboard for %s\n", debugstr_cf(type)); |
| return FALSE; |
| } |
| |
| if ((handle = GetClipboardData(format->format_id))) |
| { |
| CFDataRef data; |
| |
| TRACE("exporting %s %p\n", debugstr_format(format->format_id), handle); |
| |
| if ((data = format->export_func(handle))) |
| { |
| ret = macdrv_set_pasteboard_data(format->type, data, clipboard_cocoa_window); |
| CFRelease(data); |
| } |
| } |
| |
| last_get_seqno = GetClipboardSequenceNumber(); |
| |
| CloseClipboard(); |
| |
| return ret; |
| } |
| |
| |
| /************************************************************************** |
| * macdrv_lost_pasteboard_ownership |
| * |
| * Handler for the LOST_PASTEBOARD_OWNERSHIP event. |
| */ |
| void macdrv_lost_pasteboard_ownership(HWND hwnd) |
| { |
| TRACE("win %p\n", hwnd); |
| if (!macdrv_is_pasteboard_owner(clipboard_cocoa_window)) |
| grab_win32_clipboard(); |
| } |
| |
| |
| /************************************************************************** |
| * 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"); |
| } |