diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp new file mode 100644 index 00000000..290763be --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp @@ -0,0 +1,718 @@ +/* $Id: darwin-pasteboard.cpp $ */ +/** @file + * Shared Clipboard Service - Mac OS X host implementation. + */ + +/* + * Includes contributions from François Revol + * + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD +#include <Carbon/Carbon.h> + +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/errcore.h> +#include <iprt/utf16.h> + +#include <VBox/log.h> +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/GuestHost/SharedClipboard.h> +#include <VBox/GuestHost/clipboard-helper.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define WITH_HTML_H2G 1 +#define WITH_HTML_G2H 1 + +/* For debugging */ +//#define SHOW_CLIPBOARD_CONTENT + + +/** + * Initialize the global pasteboard and return a reference to it. + * + * @param pPasteboardRef Reference to the global pasteboard. + * + * @returns IPRT status code. + */ +int initPasteboard(PasteboardRef *pPasteboardRef) +{ + int rc = VINF_SUCCESS; + + if (PasteboardCreate(kPasteboardClipboard, pPasteboardRef)) + rc = VERR_NOT_SUPPORTED; + + return rc; +} + +/** + * Release the reference to the global pasteboard. + * + * @param pPasteboardRef Reference to the global pasteboard. + */ +void destroyPasteboard(PasteboardRef *pPasteboardRef) +{ + CFRelease(*pPasteboardRef); + *pPasteboardRef = NULL; +} + +/** + * Inspect the global pasteboard for new content. Check if there is some type + * that is supported by vbox and return it. + * + * @param hPasteboard Reference to the global pasteboard. + * @param idOwnership Our ownership ID. + * @param hStrOwnershipFlavor The ownership flavor string reference returned + * by takePasteboardOwnership(). + * @param pfFormats Pointer for the bit combination of the + * supported types. + * @param pfChanged True if something has changed after the + * last call. + * + * @returns VINF_SUCCESS. + */ +int queryNewPasteboardFormats(PasteboardRef hPasteboard, uint64_t idOwnership, void *hStrOwnershipFlavor, + uint32_t *pfFormats, bool *pfChanged) +{ + OSStatus orc; + + *pfFormats = 0; + *pfChanged = true; + + PasteboardSyncFlags syncFlags; + /* Make sure all is in sync */ + syncFlags = PasteboardSynchronize(hPasteboard); + /* If nothing changed return */ + if (!(syncFlags & kPasteboardModified)) + { + *pfChanged = false; + Log2(("queryNewPasteboardFormats: no change\n")); + return VINF_SUCCESS; + } + + /* Are some items in the pasteboard? */ + ItemCount cItems = 0; + orc = PasteboardGetItemCount(hPasteboard, &cItems); + if (orc == 0) + { + if (cItems < 1) + Log(("queryNewPasteboardFormats: changed: No items on the pasteboard\n")); + else + { + /* The id of the first element in the pasteboard */ + PasteboardItemID idItem = 0; + orc = PasteboardGetItemIdentifier(hPasteboard, 1, &idItem); + if (orc == 0) + { + /* + * Retrieve all flavors on the pasteboard, maybe there + * is something we can use. Or maybe we're the owner. + */ + CFArrayRef hFlavors = 0; + orc = PasteboardCopyItemFlavors(hPasteboard, idItem, &hFlavors); + if (orc == 0) + { + CFIndex cFlavors = CFArrayGetCount(hFlavors); + for (CFIndex idxFlavor = 0; idxFlavor < cFlavors; idxFlavor++) + { + CFStringRef hStrFlavor = (CFStringRef)CFArrayGetValueAtIndex(hFlavors, idxFlavor); + if ( idItem == (PasteboardItemID)idOwnership + && hStrOwnershipFlavor + && CFStringCompare(hStrFlavor, (CFStringRef)hStrOwnershipFlavor, 0) == kCFCompareEqualTo) + { + /* We made the changes ourselves. */ + Log2(("queryNewPasteboardFormats: no-changed: our clipboard!\n")); + *pfChanged = false; + *pfFormats = 0; + break; + } + + if (UTTypeConformsTo(hStrFlavor, kUTTypeBMP)) + { + Log(("queryNewPasteboardFormats: BMP flavor detected.\n")); + *pfFormats |= VBOX_SHCL_FMT_BITMAP; + } + else if ( UTTypeConformsTo(hStrFlavor, kUTTypeUTF8PlainText) + || UTTypeConformsTo(hStrFlavor, kUTTypeUTF16PlainText)) + { + Log(("queryNewPasteboardFormats: Unicode flavor detected.\n")); + *pfFormats |= VBOX_SHCL_FMT_UNICODETEXT; + } +#ifdef WITH_HTML_H2G + else if (UTTypeConformsTo(hStrFlavor, kUTTypeHTML)) + { + Log(("queryNewPasteboardFormats: HTML flavor detected.\n")); + *pfFormats |= VBOX_SHCL_FMT_HTML; + } +#endif +#ifdef LOG_ENABLED + else if (LogIs2Enabled()) + { + if (CFStringGetCharactersPtr(hStrFlavor)) + Log2(("queryNewPasteboardFormats: Unknown flavor: %ls.\n", CFStringGetCharactersPtr(hStrFlavor))); + else if (CFStringGetCStringPtr(hStrFlavor, kCFStringEncodingUTF8)) + Log2(("queryNewPasteboardFormats: Unknown flavor: %s.\n", + CFStringGetCStringPtr(hStrFlavor, kCFStringEncodingUTF8))); + else + Log2(("queryNewPasteboardFormats: Unknown flavor: ???\n")); + } +#endif + } + + CFRelease(hFlavors); + } + else + Log(("queryNewPasteboardFormats: PasteboardCopyItemFlavors failed - %d (%#x)\n", orc, orc)); + } + else + Log(("queryNewPasteboardFormats: PasteboardGetItemIdentifier failed - %d (%#x)\n", orc, orc)); + + if (*pfChanged) + Log(("queryNewPasteboardFormats: changed: *pfFormats=%#x\n", *pfFormats)); + } + } + else + Log(("queryNewPasteboardFormats: PasteboardGetItemCount failed - %d (%#x)\n", orc, orc)); + return VINF_SUCCESS; +} + +/** + * Read content from the host clipboard and write it to the internal clipboard + * structure for further processing. + * + * @param pPasteboard Reference to the global pasteboard. + * @param fFormat The format type which should be read. + * @param pv The destination buffer. + * @param cb The size of the destination buffer. + * @param pcbActual The size which is needed to transfer the content. + * + * @returns IPRT status code. + */ +int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual) +{ + Log(("readFromPasteboard: fFormat = %02X\n", fFormat)); + + /* Make sure all is in sync */ + PasteboardSynchronize(pPasteboard); + + /* Are some items in the pasteboard? */ + ItemCount cItems; + OSStatus orc = PasteboardGetItemCount(pPasteboard, &cItems); + if (cItems < 1) + return VINF_SUCCESS; + + /* + * Our default response... + */ + int rc = VERR_NOT_SUPPORTED; + + /* + * The id of the first element in the pasteboard + */ + PasteboardItemID idItem; + orc = PasteboardGetItemIdentifier(pPasteboard, 1, &idItem); + if (orc == 0) + { + CFDataRef hDataCopy = 0; + size_t cbDataCopy = 0; + + /* + * The guest request unicode + */ + if (fFormat & VBOX_SHCL_FMT_UNICODETEXT) + { + PRTUTF16 pwszSrcFree = NULL; + PCRTUTF16 pwszSrc = NULL; + size_t cwcSrc = 0; + + /* First preference is plain UTF-16 text: */ + orc = PasteboardCopyItemFlavorData(pPasteboard, idItem, kUTTypeUTF16PlainText, &hDataCopy); + if (orc == 0) + { + cbDataCopy = CFDataGetLength(hDataCopy); + Log(("Clipboard content is utf-16 (%zu bytes)\n", cbDataCopy)); + pwszSrc = (PCRTUTF16)CFDataGetBytePtr(hDataCopy); + if (pwszSrc) + { + cwcSrc = RTUtf16NLen(pwszSrc, cbDataCopy / sizeof(RTUTF16)); + if (cwcSrc >= cbDataCopy / sizeof(RTUTF16)) + { + pwszSrcFree = RTUtf16Alloc((cwcSrc + 1) * sizeof(RTUTF16)); + if (pwszSrcFree) + { + memcpy(pwszSrcFree, pwszSrc, cwcSrc * sizeof(RTUTF16)); + pwszSrcFree[cwcSrc] = '\0'; + pwszSrc = pwszSrcFree; + } + else + { + rc = VERR_NO_UTF16_MEMORY; + pwszSrc = NULL; + } + } + } + else + rc = VERR_GENERAL_FAILURE; + } + /* Second preference is plain UTF-8 text: */ + else + { + orc = PasteboardCopyItemFlavorData(pPasteboard, idItem, kUTTypeUTF8PlainText, &hDataCopy); + if (orc == 0) + { + cbDataCopy = CFDataGetLength(hDataCopy); + Log(("readFromPasteboard: clipboard content is utf-8 (%zu bytes)\n", cbDataCopy)); + const char *pszSrc = (const char *)CFDataGetBytePtr(hDataCopy); + if (pszSrc) + { + size_t cchSrc = RTStrNLen(pszSrc, cbDataCopy); + rc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszSrcFree, 0, &cwcSrc); + if (RT_SUCCESS(rc)) + pwszSrc = pwszSrcFree; + } + else + rc = VERR_GENERAL_FAILURE; + } + } + if (pwszSrc) + { + /* + * Convert to windows UTF-16. + */ + Assert(cwcSrc == RTUtf16Len(pwszSrc)); + size_t cwcDst = 0; + rc = ShClUtf16LFLenUtf8(pwszSrc, cwcSrc, &cwcDst); + if (RT_SUCCESS(rc)) + { + cwcDst++; /* Add space for terminator. */ + + *pcbActual = cwcDst * sizeof(RTUTF16); + if (*pcbActual <= cb) + { + rc = ShClConvUtf16LFToCRLF(pwszSrc, cwcSrc, (PRTUTF16)pv, cb / sizeof(RTUTF16)); + if (RT_SUCCESS(rc)) + { +#ifdef SHOW_CLIPBOARD_CONTENT + Log(("readFromPasteboard: clipboard content: %ls\n", (PCRTUTF16)pv)); +#endif + } + else + { + Log(("readFromPasteboard: ShClUtf16LinToWin failed - %Rrc!\n", rc)); + AssertRC(rc); + } + } + else + { + Log(("readFromPasteboard: Insufficient (text) buffer space: %#zx, need %#zx\n", cb, *pcbActual)); + rc = VINF_SUCCESS; + } + } + else + { + Log(("readFromPasteboard: ShClUtf16GetWinSize failed - %Rrc!\n", rc)); + AssertRC(rc); + } + RTUtf16Free(pwszSrcFree); + } + } + /* + * The guest request BITMAP + */ + else if (fFormat & VBOX_SHCL_FMT_BITMAP) + { + /* Get the BMP data from the pasteboard */ + orc = PasteboardCopyItemFlavorData(pPasteboard, idItem, kUTTypeBMP, &hDataCopy); + if (orc == 0) + { + cbDataCopy = CFDataGetLength(hDataCopy); + Log(("Clipboard content is BMP (%zu bytes)\n", cbDataCopy)); + const void *pvSrc = CFDataGetBytePtr(hDataCopy); + if (pvSrc) + { + /* + * Try get the device independent bitmap (DIB) bit from it. + */ + const void *pvDib; + size_t cbDib; + rc = ShClBmpGetDib(pvSrc, cbDataCopy, &pvDib, &cbDib); + if (RT_SUCCESS(rc)) + { + *pcbActual = cbDib; + if (*pcbActual <= cb) + { + memcpy(pv, pvDib, cbDib); +#ifdef SHOW_CLIPBOARD_CONTENT + Log(("readFromPasteboard: clipboard content bitmap %zx bytes\n", cbDib)); +#endif + } + else + Log(("readFromPasteboard: Insufficient (bitmap) buffer space: %#zx, need %#zx\n", cb, cbDib)); + rc = VINF_SUCCESS; + } + else + { + AssertRC(rc); + Log(("readFromPasteboard: ShClBmpGetDib failed - %Rrc - unknown bitmap format??\n", rc)); + rc = VERR_NOT_SUPPORTED; + } + } + else + rc = VERR_GENERAL_FAILURE; + } + else + LogFlow(("readFromPasteboard: PasteboardCopyItemFlavorData/kUTTypeBMP -> %d (%#x)\n", orc, orc)); + } +#ifdef WITH_HTML_H2G + /* + * The guest request HTML. It expects a UTF-8 reply and we assume + * that's what's on the pasteboard too. + */ + else if (fFormat & VBOX_SHCL_FMT_HTML) + { + orc = PasteboardCopyItemFlavorData(pPasteboard, idItem, kUTTypeHTML, &hDataCopy); + if (orc == 0) + { + cbDataCopy = CFDataGetLength(hDataCopy); + Log(("Clipboard content is HTML (%zu bytes):\n", cbDataCopy)); + const char *pszSrc = (const char *)CFDataGetBytePtr(hDataCopy); + if (pszSrc) + { + Log3(("%.*Rhxd\n", cbDataCopy, pszSrc)); + rc = RTStrValidateEncodingEx(pszSrc, cbDataCopy, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + size_t cchSrc = RTStrNLen(pszSrc, cbDataCopy); + *pcbActual = cchSrc; + if (cchSrc <= cb) + memcpy(pv, pszSrc, cchSrc); + else + Log(("readFromPasteboard: Insufficient (HTML) buffer space: %#zx, need %#zx\n", cb, cchSrc)); + rc = VINF_SUCCESS; + } + else + { + Log(("readFromPasteboard: Invalid UTF-8 encoding on pasteboard: %Rrc\n", rc)); + rc = VERR_NOT_SUPPORTED; + } + } + else + rc = VERR_GENERAL_FAILURE; + } + else + LogFlow(("readFromPasteboard: PasteboardCopyItemFlavorData/kUTTypeHTML -> %d (%#x)\n", orc, orc)); + } +#endif + else + { + Log2(("readFromPasteboard: Unsupported format: %#x\n", fFormat)); + rc = VERR_NOT_SUPPORTED; + } + + /* + * Release the data copy, if we got one. There are no returns above! + */ + if (hDataCopy) + CFRelease(hDataCopy); + } + else + { + Log(("readFromPasteboard: PasteboardGetItemIdentifier failed: %u (%#x)\n", orc, orc)); + rc = VERR_NOT_SUPPORTED; + } + + Log(("readFromPasteboard: rc=%Rrc *pcbActual=%#zx\n", rc, *pcbActual)); + return rc; +} + +/** + * Takes the ownership of the pasteboard. + * + * This is called when the other end reports available formats. + * + * @returns VBox status code. + * @param hPasteboard The pastboard handle (reference). + * @param idOwnership The ownership ID to use now. + * @param pszOwnershipFlavor The ownership indicator flavor + * @param pszOwnershipValue The ownership value (stringified format mask). + * @param phStrOwnershipFlavor Pointer to a CFStringRef variable holding + * the current ownership flavor string. This + * will always be released, and set again on + * success. + * + * @todo Add fFormats so we can make promises about available formats at once + * without needing to request any data first. That might help on + * flavor priority. + */ +int takePasteboardOwnership(PasteboardRef hPasteboard, uint64_t idOwnership, const char *pszOwnershipFlavor, + const char *pszOwnershipValue, void **phStrOwnershipFlavor) +{ + /* + * Release the old string. + */ + if (*phStrOwnershipFlavor) + { + CFStringRef hOldFlavor = (CFStringRef)*phStrOwnershipFlavor; + CFRelease(hOldFlavor); + *phStrOwnershipFlavor = NULL; + } + + /* + * Clear the pasteboard and take ownership over it. + */ + OSStatus orc = PasteboardClear(hPasteboard); + if (orc == 0) + { + /* For good measure. */ + PasteboardSynchronize(hPasteboard); + + /* + * Put the ownership flavor and value onto the clipboard. + */ + CFDataRef hData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)pszOwnershipValue, strlen(pszOwnershipValue)); + if (hData) + { + CFStringRef hFlavor = CFStringCreateWithCString(kCFAllocatorDefault, pszOwnershipFlavor, kCFStringEncodingUTF8); + if (hFlavor) + { + orc = PasteboardPutItemFlavor(hPasteboard, (PasteboardItemID)idOwnership, + hFlavor, hData, kPasteboardFlavorNoFlags); + if (orc == 0) + { + *phStrOwnershipFlavor = (void *)hFlavor; + Log(("takePasteboardOwnership: idOwnership=%RX64 flavor=%s value=%s\n", + idOwnership, pszOwnershipFlavor, pszOwnershipValue)); + } + else + { + Log(("takePasteboardOwnership: PasteboardPutItemFlavor -> %d (%#x)!\n", orc, orc)); + CFRelease(hFlavor); + } + } + else + Log(("takePasteboardOwnership: CFStringCreateWithCString failed!\n")); + CFRelease(hData); + } + else + Log(("takePasteboardOwnership: CFDataCreate failed!\n")); + } + else + Log(("takePasteboardOwnership: PasteboardClear failed -> %d (%#x)\n", orc, orc)); + return orc == 0 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +} + +/** + * Write clipboard content to the host clipboard from the internal clipboard + * structure. + * + * @param hPasteboard Reference to the global pasteboard. + * @param idOwnership The ownership ID. + * @param pv The source buffer. + * @param cb The size of the source buffer. + * @param fFormat The format type which should be written. + * + * @returns IPRT status code. + */ +int writeToPasteboard(PasteboardRef hPasteboard, uint64_t idOwnership, const void *pv, uint32_t cb, uint32_t fFormat) +{ + int rc; + OSStatus orc; + CFDataRef hData; + Log(("writeToPasteboard: fFormat=%#x\n", fFormat)); + + /* Make sure all is in sync */ + PasteboardSynchronize(hPasteboard); + + /* + * Handle the unicode text + */ + if (fFormat & VBOX_SHCL_FMT_UNICODETEXT) + { + PCRTUTF16 const pwszSrc = (PCRTUTF16)pv; + size_t const cwcSrc = cb / sizeof(RTUTF16); + + /* + * If the other side is windows or OS/2, we may have to convert + * '\r\n' -> '\n' and the drop ending marker. + */ + + /* How long will the converted text be? */ + size_t cwcDst = 0; + rc = ShClUtf16CRLFLenUtf8(pwszSrc, cwcSrc, &cwcDst); + AssertMsgRCReturn(rc, ("ShClUtf16GetLinSize failed: %Rrc\n", rc), rc); + + /* Ignore empty strings? */ /** @todo r=andy Really? Why? */ + if (cwcDst == 0) + { + Log(("writeToPasteboard: received empty string from the guest; ignoreing it.\n")); + return VINF_SUCCESS; + } + + cwcDst++; /* Add space for terminator. */ + + /* Allocate the necessary memory and do the conversion. */ + PRTUTF16 pwszDst = (PRTUTF16)RTMemAlloc(cwcDst * sizeof(RTUTF16)); + AssertMsgReturn(pwszDst, ("cwcDst=%#zx\n", cwcDst), VERR_NO_UTF16_MEMORY); + + rc = ShClConvUtf16CRLFToLF(pwszSrc, cwcSrc, pwszDst, cwcDst); + if (RT_SUCCESS(rc)) + { + /* + * Create an immutable CFData object that we can place on the clipboard. + */ + hData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)pwszDst, cwcDst * sizeof(RTUTF16)); + if (hData) + { + orc = PasteboardPutItemFlavor(hPasteboard, (PasteboardItemID)idOwnership, + kUTTypeUTF16PlainText, hData, kPasteboardFlavorNoFlags); + if (orc == 0) + rc = VINF_SUCCESS; + else + { + Log(("writeToPasteboard: PasteboardPutItemFlavor/kUTTypeUTF16PlainText failed: %d (%#x)\n", orc, orc)); + rc = VERR_GENERAL_FAILURE; + } + CFRelease(hData); + } + else + { + Log(("writeToPasteboard: CFDataCreate/UTF16 failed!\n")); + rc = VERR_NO_MEMORY; + } + + /* + * Now for the UTF-8 version. + */ + char *pszDst; + int rc2 = RTUtf16ToUtf8(pwszDst, &pszDst); + if (RT_SUCCESS(rc2)) + { + hData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)pszDst, strlen(pszDst)); + if (hData) + { + orc = PasteboardPutItemFlavor(hPasteboard, (PasteboardItemID)idOwnership, + kUTTypeUTF8PlainText, hData, kPasteboardFlavorNoFlags); + if (orc != 0) + { + Log(("writeToPasteboard: PasteboardPutItemFlavor/kUTTypeUTF8PlainText failed: %d (%#x)\n", orc, orc)); + rc = VERR_GENERAL_FAILURE; + } + CFRelease(hData); + } + else + { + Log(("writeToPasteboard: CFDataCreate/UTF8 failed!\n")); + rc = VERR_NO_MEMORY; + } + RTStrFree(pszDst); + } + else + rc = rc2; + } + else + Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc)); + + RTMemFree(pwszDst); + } + /* + * Handle the bitmap. We convert the DIB to a bitmap and put it on + * the pasteboard using the BMP flavor. + */ + else if (fFormat & VBOX_SHCL_FMT_BITMAP) + { + /* Create a full BMP from it */ + void *pvBmp; + size_t cbBmp; + rc = ShClDibToBmp(pv, cb, &pvBmp, &cbBmp); + if (RT_SUCCESS(rc)) + { + hData = CFDataCreate(kCFAllocatorDefault, (UInt8 const *)pvBmp, cbBmp); + if (hData) + { + orc = PasteboardPutItemFlavor(hPasteboard, (PasteboardItemID)idOwnership, + kUTTypeBMP, hData, kPasteboardFlavorNoFlags); + if (orc != 0) + { + Log(("writeToPasteboard: PasteboardPutItemFlavor/kUTTypeBMP failed: %d (%#x)\n", orc, orc)); + rc = VERR_GENERAL_FAILURE; + } + CFRelease(hData); + } + else + { + Log(("writeToPasteboard: CFDataCreate/UTF8 failed!\n")); + rc = VERR_NO_MEMORY; + } + RTMemFree(pvBmp); + } + } +#ifdef WITH_HTML_G2H + /* + * Handle HTML. Expect UTF-8, ignore line endings and just put it + * straigh up on the pasteboard for now. + */ + else if (fFormat & VBOX_SHCL_FMT_HTML) + { + const char *pszSrc = (const char *)pv; + size_t const cchSrc = RTStrNLen(pszSrc, cb); + rc = RTStrValidateEncodingEx(pszSrc, cchSrc, 0); + if (RT_SUCCESS(rc)) + { + hData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)pszSrc, cchSrc); + if (hData) + { + orc = PasteboardPutItemFlavor(hPasteboard, (PasteboardItemID)idOwnership, kUTTypeHTML, + hData, kPasteboardFlavorNoFlags); + if (orc == 0) + rc = VINF_SUCCESS; + else + { + Log(("writeToPasteboard: PasteboardPutItemFlavor/kUTTypeHTML failed: %d (%#x)\n", orc, orc)); + rc = VERR_GENERAL_FAILURE; + } + CFRelease(hData); + } + else + { + Log(("writeToPasteboard: CFDataCreate/HTML failed!\n")); + rc = VERR_NO_MEMORY; + } + } + else + Log(("writeToPasteboard: HTML: Invalid UTF-8 encoding: %Rrc\n", rc)); + } +#endif + else + rc = VERR_NOT_IMPLEMENTED; + + Log(("writeToPasteboard: rc=%Rrc\n", rc)); + return rc; +} + |