VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp@ 77807

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property eol-style set to native
  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 14.9 KB
Line 
1/* $Id: darwin-pasteboard.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Mac OS X host implementation.
4 */
5
6/*
7 * Includes contributions from François Revol
8 *
9 * Copyright (C) 2008-2019 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
25#include <Carbon/Carbon.h>
26
27#include <iprt/assert.h>
28#include <iprt/mem.h>
29#include <iprt/errcore.h>
30#include <iprt/utf16.h>
31
32#include "VBox/log.h"
33#include "VBox/HostServices/VBoxClipboardSvc.h"
34#include "VBox/GuestHost/clipboard-helper.h"
35
36
37/*********************************************************************************************************************************
38* Defined Constants And Macros *
39*********************************************************************************************************************************/
40/* For debugging */
41//#define SHOW_CLIPBOARD_CONTENT
42
43
44/**
45 * Initialize the global pasteboard and return a reference to it.
46 *
47 * @param pPasteboardRef Reference to the global pasteboard.
48 *
49 * @returns IPRT status code.
50 */
51int initPasteboard(PasteboardRef *pPasteboardRef)
52{
53 int rc = VINF_SUCCESS;
54
55 if (PasteboardCreate(kPasteboardClipboard, pPasteboardRef))
56 rc = VERR_NOT_SUPPORTED;
57
58 return rc;
59}
60
61/**
62 * Release the reference to the global pasteboard.
63 *
64 * @param pPasteboardRef Reference to the global pasteboard.
65 */
66void destroyPasteboard(PasteboardRef *pPasteboardRef)
67{
68 CFRelease(*pPasteboardRef);
69 *pPasteboardRef = NULL;
70}
71
72/**
73 * Inspect the global pasteboard for new content. Check if there is some type
74 * that is supported by vbox and return it.
75 *
76 * @param pPasteboard Reference to the global pasteboard.
77 * @param pfFormats Pointer for the bit combination of the
78 * supported types.
79 * @param pfChanged True if something has changed after the
80 * last call.
81 *
82 * @returns IPRT status code. (Always VINF_SUCCESS atm.)
83 */
84int queryNewPasteboardFormats(PasteboardRef pPasteboard, uint32_t *pfFormats, bool *pfChanged)
85{
86 Log(("queryNewPasteboardFormats\n"));
87
88 OSStatus err = noErr;
89 *pfChanged = true;
90
91 PasteboardSyncFlags syncFlags;
92 /* Make sure all is in sync */
93 syncFlags = PasteboardSynchronize(pPasteboard);
94 /* If nothing changed return */
95 if (!(syncFlags & kPasteboardModified))
96 {
97 *pfChanged = false;
98 return VINF_SUCCESS;
99 }
100
101 /* Are some items in the pasteboard? */
102 ItemCount itemCount;
103 err = PasteboardGetItemCount(pPasteboard, &itemCount);
104 if (itemCount < 1)
105 return VINF_SUCCESS;
106
107 /* The id of the first element in the pasteboard */
108 int rc = VINF_SUCCESS;
109 PasteboardItemID itemID;
110 if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID)))
111 {
112 /* Retrieve all flavors in the pasteboard, maybe there
113 * is something we can use. */
114 CFArrayRef flavorTypeArray;
115 if (!(err = PasteboardCopyItemFlavors(pPasteboard, itemID, &flavorTypeArray)))
116 {
117 CFIndex flavorCount;
118 flavorCount = CFArrayGetCount(flavorTypeArray);
119 for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
120 {
121 CFStringRef flavorType;
122 flavorType = static_cast <CFStringRef>(CFArrayGetValueAtIndex(flavorTypeArray,
123 flavorIndex));
124 /* Currently only unicode supported */
125 if (UTTypeConformsTo(flavorType, kUTTypeUTF8PlainText) ||
126 UTTypeConformsTo(flavorType, kUTTypeUTF16PlainText))
127 {
128 Log(("Unicode flavor detected.\n"));
129 *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
130 }
131 else if (UTTypeConformsTo(flavorType, kUTTypeBMP))
132 {
133 Log(("BMP flavor detected.\n"));
134 *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
135 }
136 }
137 CFRelease(flavorTypeArray);
138 }
139 }
140
141 Log(("queryNewPasteboardFormats: rc = %02X\n", rc));
142 return rc;
143}
144
145/**
146 * Read content from the host clipboard and write it to the internal clipboard
147 * structure for further processing.
148 *
149 * @param pPasteboard Reference to the global pasteboard.
150 * @param fFormat The format type which should be read.
151 * @param pv The destination buffer.
152 * @param cb The size of the destination buffer.
153 * @param pcbActual The size which is needed to transfer the content.
154 *
155 * @returns IPRT status code.
156 */
157int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual)
158{
159 Log(("readFromPasteboard: fFormat = %02X\n", fFormat));
160
161 OSStatus err = noErr;
162
163 /* Make sure all is in sync */
164 PasteboardSynchronize(pPasteboard);
165
166 /* Are some items in the pasteboard? */
167 ItemCount itemCount;
168 err = PasteboardGetItemCount(pPasteboard, &itemCount);
169 if (itemCount < 1)
170 return VINF_SUCCESS;
171
172 /* The id of the first element in the pasteboard */
173 int rc = VERR_NOT_SUPPORTED;
174 PasteboardItemID itemID;
175 if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID)))
176 {
177 /* The guest request unicode */
178 if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
179 {
180 CFDataRef outData;
181 PRTUTF16 pwszTmp = NULL;
182 /* Try utf-16 first */
183 if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF16PlainText, &outData)))
184 {
185 Log(("Clipboard content is utf-16\n"));
186
187 PRTUTF16 pwszString = (PRTUTF16)CFDataGetBytePtr(outData);
188 if (pwszString)
189 rc = RTUtf16DupEx(&pwszTmp, pwszString, 0);
190 else
191 rc = VERR_INVALID_PARAMETER;
192 }
193 /* Second try is utf-8 */
194 else
195 if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF8PlainText, &outData)))
196 {
197 Log(("readFromPasteboard: clipboard content is utf-8\n"));
198 const char *pszString = (const char *)CFDataGetBytePtr(outData);
199 if (pszString)
200 rc = RTStrToUtf16(pszString, &pwszTmp);
201 else
202 rc = VERR_INVALID_PARAMETER;
203 }
204 if (pwszTmp)
205 {
206 /* Check how much longer will the converted text will be. */
207 size_t cwSrc = RTUtf16Len(pwszTmp);
208 size_t cwDest;
209 rc = vboxClipboardUtf16GetWinSize(pwszTmp, cwSrc, &cwDest);
210 if (RT_FAILURE(rc))
211 {
212 RTUtf16Free(pwszTmp);
213 Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16GetWinSize returned %Rrc. Abandoning.\n", rc));
214 AssertRCReturn(rc, rc);
215 }
216 /* Set the actually needed data size */
217 *pcbActual = cwDest * 2;
218 /* Return success state */
219 rc = VINF_SUCCESS;
220 /* Do not copy data if the dst buffer is not big enough. */
221 if (*pcbActual <= cb)
222 {
223 rc = vboxClipboardUtf16LinToWin(pwszTmp, RTUtf16Len(pwszTmp), static_cast <PRTUTF16>(pv), cb / 2);
224 if (RT_FAILURE(rc))
225 {
226 RTUtf16Free(pwszTmp);
227 Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16LinToWin() returned %Rrc. Abandoning.\n", rc));
228 AssertRCReturn(rc, rc);
229 }
230#ifdef SHOW_CLIPBOARD_CONTENT
231 Log(("readFromPasteboard: clipboard content: %ls\n", static_cast <PRTUTF16>(pv)));
232#endif
233 }
234 /* Free the temp string */
235 RTUtf16Free(pwszTmp);
236 }
237 }
238 /* The guest request BITMAP */
239 else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
240 {
241 CFDataRef outData;
242 const void *pTmp = NULL;
243 size_t cbTmpSize;
244 /* Get the data from the pasteboard */
245 if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeBMP, &outData)))
246 {
247 Log(("Clipboard content is BMP\n"));
248 pTmp = CFDataGetBytePtr(outData);
249 cbTmpSize = CFDataGetLength(outData);
250 }
251 if (pTmp)
252 {
253 const void *pDib;
254 size_t cbDibSize;
255 rc = vboxClipboardBmpGetDib(pTmp, cbTmpSize, &pDib, &cbDibSize);
256 if (RT_FAILURE(rc))
257 {
258 rc = VERR_NOT_SUPPORTED;
259 Log(("readFromPasteboard: unknown bitmap format. vboxClipboardBmpGetDib returned %Rrc. Abandoning.\n", rc));
260 AssertRCReturn(rc, rc);
261 }
262
263 *pcbActual = cbDibSize;
264 /* Return success state */
265 rc = VINF_SUCCESS;
266 /* Do not copy data if the dst buffer is not big enough. */
267 if (*pcbActual <= cb)
268 {
269 memcpy(pv, pDib, cbDibSize);
270#ifdef SHOW_CLIPBOARD_CONTENT
271 Log(("readFromPasteboard: clipboard content bitmap %d bytes\n", cbDibSize));
272#endif
273 }
274 }
275 }
276 }
277
278 Log(("readFromPasteboard: rc = %02X\n", rc));
279 return rc;
280}
281
282/**
283 * Write clipboard content to the host clipboard from the internal clipboard
284 * structure.
285 *
286 * @param pPasteboard Reference to the global pasteboard.
287 * @param pv The source buffer.
288 * @param cb The size of the source buffer.
289 * @param fFormat The format type which should be written.
290 *
291 * @returns IPRT status code.
292 */
293int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat)
294{
295 Log(("writeToPasteboard: fFormat = %02X\n", fFormat));
296
297 /* Clear the pasteboard */
298 if (PasteboardClear(pPasteboard))
299 return VERR_NOT_SUPPORTED;
300
301 /* Make sure all is in sync */
302 PasteboardSynchronize(pPasteboard);
303
304 int rc = VERR_NOT_SUPPORTED;
305 /* Handle the unicode text */
306 if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
307 {
308 PRTUTF16 pwszSrcText = static_cast <PRTUTF16>(pv);
309 size_t cwSrc = cb / 2;
310 size_t cwDest = 0;
311 /* How long will the converted text be? */
312 rc = vboxClipboardUtf16GetLinSize(pwszSrcText, cwSrc, &cwDest);
313 if (RT_FAILURE(rc))
314 {
315 Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
316 AssertRCReturn(rc, rc);
317 }
318 /* Empty clipboard? Not critical */
319 if (cwDest == 0)
320 {
321 Log(("writeToPasteboard: received empty clipboard data from the guest, returning false.\n"));
322 return VINF_SUCCESS;
323 }
324 /* Allocate the necessary memory */
325 PRTUTF16 pwszDestText = static_cast <PRTUTF16>(RTMemAlloc(cwDest * 2));
326 if (pwszDestText == NULL)
327 {
328 Log(("writeToPasteboard: failed to allocate %d bytes\n", cwDest * 2));
329 return VERR_NO_MEMORY;
330 }
331 /* Convert the EOL */
332 rc = vboxClipboardUtf16WinToLin(pwszSrcText, cwSrc, pwszDestText, cwDest);
333 if (RT_FAILURE(rc))
334 {
335 Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
336 RTMemFree(pwszDestText);
337 AssertRCReturn(rc, rc);
338 }
339
340 CFDataRef textData = NULL;
341 /* Item id is 1. Nothing special here. */
342 PasteboardItemID itemId = (PasteboardItemID)1;
343 /* Create a CData object which we could pass to the pasteboard */
344 if ((textData = CFDataCreate(kCFAllocatorDefault,
345 reinterpret_cast<UInt8*>(pwszDestText), cwDest * 2)))
346 {
347 /* Put the Utf-16 version to the pasteboard */
348 PasteboardPutItemFlavor(pPasteboard, itemId,
349 kUTTypeUTF16PlainText,
350 textData, 0);
351 }
352 /* Create a Utf-8 version */
353 char *pszDestText;
354 rc = RTUtf16ToUtf8(pwszDestText, &pszDestText);
355 if (RT_SUCCESS(rc))
356 {
357 /* Create a CData object which we could pass to the pasteboard */
358 if ((textData = CFDataCreate(kCFAllocatorDefault,
359 reinterpret_cast<UInt8*>(pszDestText), strlen(pszDestText))))
360 {
361 /* Put the Utf-8 version to the pasteboard */
362 PasteboardPutItemFlavor(pPasteboard, itemId,
363 kUTTypeUTF8PlainText,
364 textData, 0);
365 }
366 RTStrFree(pszDestText);
367 }
368
369 RTMemFree(pwszDestText);
370 rc = VINF_SUCCESS;
371 }
372 /* Handle the bitmap */
373 else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
374 {
375 /* Create a full BMP from it */
376 void *pBmp;
377 size_t cbBmpSize;
378 CFDataRef bmpData = NULL;
379 /* Item id is 1. Nothing special here. */
380 PasteboardItemID itemId = (PasteboardItemID)1;
381
382 rc = vboxClipboardDibToBmp(pv, cb, &pBmp, &cbBmpSize);
383 if (RT_SUCCESS(rc))
384 {
385 /* Create a CData object which we could pass to the pasteboard */
386 if ((bmpData = CFDataCreate(kCFAllocatorDefault,
387 reinterpret_cast<UInt8*>(pBmp), cbBmpSize)))
388 {
389 /* Put the Utf-8 version to the pasteboard */
390 PasteboardPutItemFlavor(pPasteboard, itemId,
391 kUTTypeBMP,
392 bmpData, 0);
393 }
394 RTMemFree(pBmp);
395 }
396 rc = VINF_SUCCESS;
397 }
398 else
399 rc = VERR_NOT_IMPLEMENTED;
400
401 Log(("writeToPasteboard: rc = %02X\n", rc));
402 return rc;
403}
404
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette