/** $Id$ */
/** @file
* VBoxClient - Shared Clipboard Guest -> Host copying, Darwin.
*/
/*
* Copyright (C) 2007-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <Carbon/Carbon.h>
#include <signal.h>
#include <stdlib.h>
#include <iprt/thread.h>
#include <iprt/mem.h>
#include <iprt/stream.h>
#include <iprt/initterm.h>
#include <iprt/message.h>
#include <VBox/VBoxGuestLib.h>
#include <VBox/HostServices/VBoxClipboardSvc.h>
#include <VBox/GuestHost/clipboard-helper.h>
#include "VBoxClientInternal.h"
/**
* Walk through pasteboard items and report currently available item types.
*
* @param pPasteboard Reference to guest Pasteboard.
* @returns Available formats bit field.
*/
uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard)
{
uint32_t fFormats = 0;
ItemCount cItems = 0;
ItemCount iItem;
OSStatus rc;
#define VBOXCL_ADD_FORMAT_IF_PRESENT(a_kDarwinFmt, a_fVBoxFmt) \
if (PasteboardCopyItemFlavorData(pPasteboard, iItemID, a_kDarwinFmt, &flavorData) == noErr) \
{ \
fFormats |= (uint32_t)a_fVBoxFmt; \
CFRelease(flavorData); \
}
rc = PasteboardGetItemCount(pPasteboard, &cItems);
AssertReturn((rc == noErr) && (cItems > 0), fFormats);
for (iItem = 1; iItem <= cItems; iItem++)
{
PasteboardItemID iItemID;
CFDataRef flavorData;
rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
if (rc == noErr)
{
VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF16PlainText, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF8PlainText, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeBMP, VBOX_SHARED_CLIPBOARD_FMT_BITMAP );
VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeHTML, VBOX_SHARED_CLIPBOARD_FMT_HTML );
#ifdef CLIPBOARD_DUMP_CONTENT_FORMATS
CFArrayRef flavorTypeArray;
CFIndex flavorCount;
CFStringRef flavorType;
rc = PasteboardCopyItemFlavors(pPasteboard, iItemID, &flavorTypeArray);
if (rc == noErr)
{
VBoxClientVerbose(3, "SCAN..\n");
flavorCount = CFArrayGetCount(flavorTypeArray);
VBoxClientVerbose(3, "SCAN (%d)..\n", (int)flavorCount);
for(CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
{
VBoxClientVerbose(3, "SCAN #%d..\n", (int)flavorIndex);
flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
CFDataRef flavorData1;
rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, flavorType, &flavorData1);
if (rc == noErr)
{
VBoxClientVerbose(3, "Found: %s, size: %d\n", (char *)CFStringGetCStringPtr(flavorType, kCFStringEncodingMacRoman), (int)CFDataGetLength(flavorData1));
CFRelease(flavorData1);
}
}
VBoxClientVerbose(3, "SCAN COMPLETE\n");
CFRelease(flavorTypeArray);
}
#endif /* CLIPBOARD_DUMP_CONTENT_FORMATS */
}
}
#undef VBOXCL_ADD_FORMAT_IF_PRESENT
return fFormats;
}
/**
* Search for content of specified type in guest clipboard buffer and put
* it into newly allocated buffer.
*
* @param pPasteboard Guest PasteBoard reference.
* @param fFormat Data formats we are looking for.
* @param ppvData Where to return pointer to the received data. M
* @param pcbData Where to return the size of the data.
* @param pcbAlloc Where to return the size of the memory block
* *ppvData pointes to. (Usually greater than *cbData
* because the allocation is page aligned.)
* @returns IPRT status code.
*/
static int vbclClipboardReadGuestData(PasteboardRef pPasteboard, CFStringRef sFormat, void **ppvData, uint32_t *pcbData,
uint32_t *pcbAlloc)
{
ItemCount cItems, iItem;
OSStatus rc;
void *pvData = NULL;
uint32_t cbData = 0;
uint32_t cbAlloc = 0;
AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
AssertPtrReturn(pcbAlloc, VERR_INVALID_POINTER);
rc = PasteboardGetItemCount(pPasteboard, &cItems);
AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
AssertReturn(cItems > 0, VERR_INVALID_PARAMETER);
/* Walk through all the items in PasteBoard in order to find
that one that correcponds to requested data format. */
for (iItem = 1; iItem <= cItems; iItem++)
{
PasteboardItemID iItemID;
CFDataRef flavorData;
/* Now, get the item's flavors that corresponds to requested type. */
rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, sFormat, &flavorData);
if (rc == noErr)
{
void *flavorDataPtr = (void *)CFDataGetBytePtr(flavorData);
cbData = CFDataGetLength(flavorData);
if (flavorDataPtr && cbData > 0)
{
cbAlloc = RT_ALIGN_32(cbData, PAGE_SIZE);
pvData = RTMemPageAllocZ(cbAlloc);
if (pvData)
memcpy(pvData, flavorDataPtr, cbData);
}
CFRelease(flavorData);
/* Found first matching item, no more search. */
break;
}
}
/* Found match */
if (pvData)
{
*ppvData = pvData;
*pcbData = cbData;
*pcbAlloc = cbAlloc;
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
/**
* Release resources occupied by vbclClipboardReadGuestData().
*/
static void vbclClipboardReleaseGuestData(void **ppvData, uint32_t cbAlloc)
{
AssertReturnVoid(ppvData);
RTMemPageFree(*ppvData, cbAlloc);
*ppvData = NULL;
}
/**
* Pass data to host.
*/
static int vbclClipboardHostPasteData(uint32_t u32ClientId, uint32_t u32Format, const void *pvData, uint32_t cbData)
{
/* Allow empty buffers */
if (cbData == 0)
return VbglR3ClipboardWriteData(u32ClientId, u32Format, NULL, 0);
AssertReturn(pvData, VERR_INVALID_PARAMETER);
return VbglR3ClipboardWriteData(u32ClientId, u32Format, (void *)pvData, cbData); /** @todo r=bird: Why on earth does a write function like VbglR3ClipboardWriteData take a non-const parameter? */
}
/**
* Paste text data into host clipboard.
*
* @param u32ClientId Host clipboard connection.
* @param pwszData UTF-16 encoded string.
* @param cbData The length of the string, in bytes, probably
* including a terminating zero.
*/
static int vbclClipboardHostPasteText(uint32_t u32ClientId, PRTUTF16 pwszData, uint32_t cbData)
{
AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
AssertPtrReturn(pwszData, VERR_INVALID_POINTER);
size_t cwcActual; /* (includes a schwarzenegger character) */
int rc = vboxClipboardUtf16GetWinSize(pwszData, cbData / sizeof(RTUTF16), &cwcActual);
AssertReturn(RT_SUCCESS(rc), rc);
PRTUTF16 pwszWinTmp = (PRTUTF16)RTMemAlloc(cwcActual * sizeof(RTUTF16));
AssertReturn(pwszWinTmp, VERR_NO_MEMORY);
rc = vboxClipboardUtf16LinToWin(pwszData, cbData / sizeof(RTUTF16), pwszWinTmp, cwcActual);
if (RT_SUCCESS(rc))
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
pwszWinTmp, cwcActual * sizeof(RTUTF16));
RTMemFree(pwszWinTmp);
return rc;
}
/**
* Paste a bitmap onto the host clipboard.
*
* @param u32ClientId Host clipboard connection.
* @param pvData The bitmap data.
* @param cbData The size of the bitmap.
*/
static int vbclClipboardHostPasteBitmap(uint32_t u32ClientId, void *pvData, uint32_t cbData)
{
const void *pvDib;
size_t cbDib;
int rc = vboxClipboardBmpGetDib(pvData, cbData, &pvDib, &cbDib);
AssertRCReturn(rc, rc);
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_BITMAP, pvDib, cbDib);
return rc;
}
/**
* Read guest's clipboard buffer and forward its content to host.
*
* @param u32ClientId Host clipboard connection.
* @param pPasteboard Guest PasteBoard reference.
* @param fFormats List of data formats (bit field) received from host.
*
* @returns IPRT status code.
*/
int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
{
int rc = VINF_SUCCESS;
void *pvData = NULL;
uint32_t cbData = 0;
uint32_t cbAlloc = 0;
VBoxClientVerbose(3, "vbclClipboardForwardToHost: %d\n", fFormats);
/* Walk across all item(s) formats */
uint32_t fFormatsLeft = fFormats;
while (fFormatsLeft)
{
if (fFormatsLeft & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
{
VBoxClientVerbose(3, "requested VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT: %d\n", fFormats);
RTUTF16 *pUtf16Str = NULL;
/* First, try to get UTF16 encoded buffer */
rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF16PlainText, &pvData, &cbData, &cbAlloc);
if (RT_SUCCESS(rc))
{
rc = RTUtf16DupEx(&pUtf16Str, (PRTUTF16)pvData, 0);
if (RT_FAILURE(rc))
pUtf16Str = NULL;
}
else /* Failed to get UTF16 buffer */
{
/* Then, try to get UTF8 encoded buffer */
rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF8PlainText, &pvData, &cbData, &cbAlloc);
if (RT_SUCCESS(rc))
{
rc = RTStrToUtf16((const char *)pvData, &pUtf16Str);
if (RT_FAILURE(rc))
pUtf16Str = NULL;
}
}
/* Finally, we got UTF16 encoded buffer */
if (RT_SUCCESS(rc))
{
rc = vbclClipboardHostPasteText(u32ClientId, (PRTUTF16)pvData, cbData);
if (pUtf16Str)
{
RTUtf16Free(pUtf16Str);
pUtf16Str = NULL;
}
vbclClipboardReleaseGuestData(&pvData, cbAlloc);
}
else
{
/* No data found or error occurred: send empty buffer */
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, NULL, 0);
}
fFormatsLeft &= ~(uint32_t)VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
}
else if (fFormatsLeft & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
{
VBoxClientVerbose(3, "requested VBOX_SHARED_CLIPBOARD_FMT_BITMAP: %d\n", fFormats);
rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeBMP, &pvData, &cbData, &cbAlloc);
if (RT_SUCCESS(rc))
{
rc = vbclClipboardHostPasteBitmap(u32ClientId, pvData, cbData);
vbclClipboardReleaseGuestData(&pvData, cbAlloc);
}
else
{
/* No data found or error occurred: send empty buffer */
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_BITMAP, NULL, 0);
}
fFormatsLeft &= ~(uint32_t)VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
}
else if (fFormatsLeft & VBOX_SHARED_CLIPBOARD_FMT_HTML)
{
VBoxClientVerbose(3, "requested VBOX_SHARED_CLIPBOARD_FMT_HTML: %d\n", fFormats);
rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeHTML, &pvData, &cbData, &cbAlloc);
if (RT_SUCCESS(rc))
{
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_HTML, pvData, cbData);
vbclClipboardReleaseGuestData(&pvData, cbAlloc);
}
else
{
/* No data found or error occurred: send empty buffer */
rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHARED_CLIPBOARD_FMT_HTML, NULL, 0);
}
fFormatsLeft &= ~(uint32_t)VBOX_SHARED_CLIPBOARD_FMT_HTML;
}
else
{
VBoxClientVerbose(3, "requested data in unsupported format: %#x\n", fFormatsLeft);
break;
}
}
return rc; /** @todo r=bird: If there are multiple formats available, which rc is returned here? Does it matter? */
}