VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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