[vbox-dev] [PATCH] Bitmap Clipboard support for Darwin host

François Revol revol at free.fr
Sun Mar 6 17:03:21 GMT 2011


I managed to get darwin to handle the bitmap clipboard format.
I had to write some helpers to generate real BMP "files" since the passed DIB miss the BM header... added them to clipboard-helpers.* for use by other OSes.
I didn't bother supporting all DIB header types though, only the 40bytes one, but it WorksForMe™ (XP guest) :
http://revolf.free.fr/osx/shots/shot_osx_vbox_bitmap_clipboard.png
Really using raw DIB data as exchange format wasn't really the best choice... another option would be to change vbox to pass full BMP data around so only win32 code would have to handle DIB-BMP conversion (for which it likely has a native and more complete API than the helpers I wrote).

Also tested with the Haiku guest additions I'm writing, and it works fine except for a bug in Haiku's BMP translator which puts the image up-side-down when the height field is negative, but that's not of vbox concern.

This patch is under MIT license.

François.


Index: include/VBox/GuestHost/clipboard-helper.h
===================================================================
--- include/VBox/GuestHost/clipboard-helper.h	(revision 36094)
+++ include/VBox/GuestHost/clipboard-helper.h	(working copy)
@@ -27,6 +27,7 @@
 #ifndef ___CLIPBOARD_HELPER_H
 #define ___CLIPBOARD_HELPER_H
 
+#include <iprt/cdefs.h>
 #include <iprt/string.h>
 
 /** Constants needed for string conversions done by the Linux/Mac clipboard code. */
@@ -91,5 +92,71 @@
  */
 int vboxClipboardUtf16WinToLin(PRTUTF16 pwszSrc, size_t cwSrc, PRTUTF16 pu16Dest, size_t cwDest);
 
+#pragma pack(1)
+/**
+ * Bitmap File Header. Official win32 name is BITMAPFILEHEADER
+ * Always Little Endian.
+ */
+typedef struct BMFILEHEADER
+{
+    uint16_t    u16Type;
+    uint32_t    u32Size;
+    uint16_t    u16Reserved1;
+    uint16_t    u16Reserved2;
+    uint32_t    u32OffBits;
+} BMFILEHEADER;
+/** Pointer to a BMFILEHEADER structure. */
+typedef BMFILEHEADER *PBMFILEHEADER;
+/** BMP file magic number */
+#define BITMAPHEADERMAGIC (RT_H2LE_U16_C(0x4d42))
+
+/**
+ * Bitmap Info Header. Official win32 name is BITMAPINFOHEADER
+ * Always Little Endian.
+ */
+typedef struct BMINFOHEADER
+{
+    uint32_t    u32Size;
+    uint32_t    u32Width;
+    uint32_t    u32Height;
+    uint16_t    u16Planes;
+    uint16_t    u16BitCount;
+    uint32_t    u32Compression;
+    uint32_t    u32SizeImage;
+    uint32_t    u32XBitsPerMeter;
+    uint32_t    u32YBitsPerMeter;
+    uint32_t    u32ClrUsed;
+    uint32_t    u32ClrImportant;
+} BMINFOHEADER;
+/** Pointer to a BMINFOHEADER structure. */
+typedef BMINFOHEADER *PBMINFOHEADER;
+#pragma pack()
+
+/**
+ * Convert CF_DIB data to full BMP data by prepending the BM header.
+ * Allocates with RTMemAlloc.
+ *
+ * @returns VBox status code
+ *
+ * @param   pSrc          DIB data to convert
+ * @param   cbSrc         Size of the DIB data to convert in bytes
+ * @param   ppDest        Where to store the pointer to the buffer for the destination data
+ * @param   pcbDest       Pointer to the size of the buffer for the destination data in bytes
+ */
+int vboxClipboardDibToBmp(const void *pSrc, size_t cbSrc, void **ppDest, size_t *pcbDest);
+
+/**
+ * Get the address and size of CF_DIB data in a full BMP data in the input buffer.
+ * Does not do any allocation.
+ *
+ * @returns VBox status code
+ *
+ * @param   pSrc          BMP data to convert
+ * @param   cbSrc         Size of the BMP data to convert in bytes
+ * @param   ppDest        Where to store the pointer to the destination data
+ * @param   pcbDest       Pointer to the size of the destination data in bytes
+ */
+int vboxClipboardBmpGetDib(const void *pSrc, size_t cbSrc, const void **ppDest, size_t *pcbDest);
+
 #endif
 
Index: src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp
===================================================================
--- src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp	(revision 36094)
+++ src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp	(working copy)
@@ -15,6 +15,7 @@
  * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
  */
 
+#include <iprt/alloc.h>
 #include <iprt/assert.h>
 #include <VBox/log.h>
 #include <VBox/GuestHost/clipboard-helper.h>
@@ -263,3 +264,62 @@
     return VINF_SUCCESS;
 }
 
+int vboxClipboardDibToBmp(const void *pSrc, size_t cbSrc, void **ppDest, size_t *pcbDest)
+{
+    size_t cb = sizeof(BMFILEHEADER) + cbSrc;
+    PBMFILEHEADER pFileHeader;
+    void *dest;
+    size_t pixelOffset;
+
+    PBMINFOHEADER pBitmapInfoHeader = (PBMINFOHEADER)pSrc;
+    if (cbSrc < sizeof(BMINFOHEADER) ||
+        RT_LE2H_U32(pBitmapInfoHeader->u32Size) < sizeof(BMINFOHEADER) ||
+    /** @todo Support all the many versions of the DIB headers. */
+        RT_LE2H_U32(pBitmapInfoHeader->u32Size) != sizeof(BMINFOHEADER))
+    {
+        Log (("vboxClipboardDibToBmp: invalid or unsupported bitmap data.\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+    pixelOffset = sizeof(BMFILEHEADER)
+        + RT_LE2H_U32(pBitmapInfoHeader->u32Size)
+        + RT_LE2H_U32(pBitmapInfoHeader->u32ClrUsed) * sizeof(uint32_t);
+    if (cbSrc < pixelOffset)
+    {
+        Log (("vboxClipboardDibToBmp: invalid bitmap data.\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    dest = RTMemAlloc(cb);
+    if (dest == NULL)
+    {
+        Log (("writeToPasteboard: cannot allocate memory for bitmap.\n"));
+        return VERR_NO_MEMORY;
+    }
+
+    pFileHeader = (PBMFILEHEADER)dest;
+    pFileHeader->u16Type = BITMAPHEADERMAGIC;
+    pFileHeader->u32Size = RT_H2LE_U32(cb);
+    pFileHeader->u16Reserved1 = pFileHeader->u16Reserved2 = 0;
+    pFileHeader->u32OffBits = RT_H2LE_U32(pixelOffset);
+    memcpy((uint8_t *)dest + sizeof(BMFILEHEADER), pSrc, cbSrc);
+    *ppDest = dest;
+    *pcbDest = cb;
+    return VINF_SUCCESS;
+}
+
+int vboxClipboardBmpGetDib(const void *pSrc, size_t cbSrc, const void **pDest, size_t *pcbDest)
+{
+    PBMFILEHEADER pFileHeader = (PBMFILEHEADER)pSrc;
+
+    if (pFileHeader == NULL || cbSrc < sizeof(BMFILEHEADER) ||
+        pFileHeader->u16Type != BITMAPHEADERMAGIC ||
+        RT_LE2H_U32(pFileHeader->u32Size) != cbSrc)
+    {
+        Log (("vboxClipboardBmpGetDib: invalid bitmap data.\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+    *pDest = ((uint8_t *)pSrc) + sizeof(BMFILEHEADER);
+    *pcbDest = cbSrc - sizeof(BMFILEHEADER);
+    return VINF_SUCCESS;
+}
+
Index: src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp
===================================================================
--- src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp	(revision 36094)
+++ src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp	(working copy)
@@ -116,6 +116,11 @@
                     Log (("Unicode flavor detected.\n"));
                     *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
                 }
+                if (UTTypeConformsTo (flavorType, kUTTypeBMP))
+                {
+                    Log (("BMP flavor detected.\n"));
+                    *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
+                }
             }
             CFRelease (flavorTypeArray);
         }
@@ -209,6 +214,44 @@
                 RTUtf16Free (pwszTmp);
             }
         }
+        /* The guest request BITMAP */
+        else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+        {
+            CFDataRef outData;
+            const void *pTmp = NULL;
+            size_t cbTmpSize;
+            /* Get the data from the pasteboard */
+            if (!(err = PasteboardCopyItemFlavorData (pPasteboard, itemID, kUTTypeBMP, &outData)))
+            {
+                Log (("Clipboard content is BMP\n"));
+                pTmp = CFDataGetBytePtr (outData);
+                cbTmpSize = CFDataGetLength (outData);
+            }
+            if (pTmp)
+            {
+                const void *pDib;
+                size_t cbDibSize;
+                rc = vboxClipboardBmpGetDib (pTmp, cbTmpSize, &pDib, &cbDibSize);
+                if (RT_FAILURE (rc))
+                {
+                    rc = VERR_NOT_SUPPORTED;
+                    Log (("readFromPasteboard: unknown bitmap format.  vboxClipboardBmpGetDib returned %Rrc.  Abandoning.\n", rc));
+                    AssertRCReturn (rc, rc);
+                }
+
+                *pcbActual = cbDibSize;
+                /* Return success state */
+                rc = VINF_SUCCESS;
+                /* Do not copy data if the dst buffer is not big enough. */
+                if (*pcbActual <= cb)
+                {
+                    memcpy (pv, pDib, cbDibSize);
+#ifdef SHOW_CLIPBOARD_CONTENT
+                    Log (("readFromPasteboard: clipboard content bitmap %d bytes\n", cbDibSize));
+#endif
+                }
+            }
+        }
     }
 
     Log (("readFromPasteboard: rc = %02X\n", rc));
@@ -305,6 +348,33 @@
         RTMemFree (pwszDestText);
         rc = VINF_SUCCESS;
     }
+    /* Handle the bitmap */
+    else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+    {
+        /* Create a full BMP from it */
+        void *pBmp;
+        size_t cbBmpSize;
+        CFDataRef bmpData = NULL;
+        /* Item id is 1. Nothing special here. */
+        PasteboardItemID itemId = (PasteboardItemID)1;
+
+        rc = vboxClipboardDibToBmp (pv, cb, &pBmp, &cbBmpSize);
+        if (RT_SUCCESS (rc))
+        {
+            /* Create a CData object which we could pass to the pasteboard */
+            if ((bmpData = CFDataCreate (kCFAllocatorDefault,
+                                          reinterpret_cast<UInt8*> (pBmp), cbBmpSize)))
+            {
+                /* Put the Utf-8 version to the pasteboard */
+                PasteboardPutItemFlavor (pPasteboard, itemId,
+                                         kUTTypeBMP,
+                                         bmpData, 0);
+            }
+            RTMemFree (pBmp);
+        }
+
+        rc = VINF_SUCCESS;
+    }
     else
         rc = VERR_NOT_IMPLEMENTED;
 





More information about the vbox-dev mailing list