VirtualBox

source: vbox/trunk/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp@ 107044

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/** $Id: VBoxClientClipboardGuestToHost.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxClient - Shared Clipboard Guest -> Host copying, Darwin.
4 */
5
6/*
7 * Copyright (C) 2007-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <Carbon/Carbon.h>
33#include <signal.h>
34#include <stdlib.h>
35
36#include <iprt/thread.h>
37#include <iprt/mem.h>
38#include <iprt/initterm.h>
39#include <iprt/message.h>
40#include <iprt/stream.h>
41#include <iprt/utf16.h>
42#include <VBox/VBoxGuestLib.h>
43#include <VBox/GuestHost/SharedClipboard.h>
44#include <VBox/HostServices/VBoxClipboardSvc.h>
45#include <VBox/GuestHost/clipboard-helper.h>
46#include "VBoxClientInternal.h"
47
48RT_GCC_NO_WARN_DEPRECATED_BEGIN /* Much here is deprecated since 12.0 */
49
50/**
51 * Walk through pasteboard items and report currently available item types.
52 *
53 * @param pPasteboard Reference to guest Pasteboard.
54 * @returns Available formats bit field.
55 */
56uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard)
57{
58 uint32_t fFormats = 0;
59 ItemCount cItems = 0;
60 ItemCount iItem;
61 OSStatus rc;
62
63#define VBOXCL_ADD_FORMAT_IF_PRESENT(a_kDarwinFmt, a_fVBoxFmt) \
64 if (PasteboardCopyItemFlavorData(pPasteboard, iItemID, a_kDarwinFmt, &flavorData) == noErr) \
65 { \
66 fFormats |= (uint32_t)a_fVBoxFmt; \
67 CFRelease(flavorData); \
68 }
69
70 rc = PasteboardGetItemCount(pPasteboard, &cItems);
71 AssertReturn((rc == noErr) && (cItems > 0), fFormats);
72
73 for (iItem = 1; iItem <= cItems; iItem++)
74 {
75 PasteboardItemID iItemID;
76 CFDataRef flavorData;
77
78 rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
79 if (rc == noErr)
80 {
81 VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF16PlainText, VBOX_SHCL_FMT_UNICODETEXT);
82 VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF8PlainText, VBOX_SHCL_FMT_UNICODETEXT);
83 VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeBMP, VBOX_SHCL_FMT_BITMAP );
84 VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeHTML, VBOX_SHCL_FMT_HTML );
85
86#ifdef CLIPBOARD_DUMP_CONTENT_FORMATS
87 CFArrayRef flavorTypeArray;
88 CFIndex flavorCount;
89 CFStringRef flavorType;
90
91 rc = PasteboardCopyItemFlavors(pPasteboard, iItemID, &flavorTypeArray);
92 if (rc == noErr)
93 {
94 VBoxClientVerbose(3, "SCAN..\n");
95 flavorCount = CFArrayGetCount(flavorTypeArray);
96 VBoxClientVerbose(3, "SCAN (%d)..\n", (int)flavorCount);
97 for(CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
98 {
99 VBoxClientVerbose(3, "SCAN #%d..\n", (int)flavorIndex);
100 flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
101
102 CFDataRef flavorData1;
103 rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, flavorType, &flavorData1);
104 if (rc == noErr)
105 {
106 VBoxClientVerbose(3, "Found: %s, size: %d\n", (char *)CFStringGetCStringPtr(flavorType, kCFStringEncodingMacRoman), (int)CFDataGetLength(flavorData1));
107 CFRelease(flavorData1);
108 }
109 }
110 VBoxClientVerbose(3, "SCAN COMPLETE\n");
111 CFRelease(flavorTypeArray);
112 }
113#endif /* CLIPBOARD_DUMP_CONTENT_FORMATS */
114 }
115 }
116
117#undef VBOXCL_ADD_FORMAT_IF_PRESENT
118
119 return fFormats;
120}
121
122
123/**
124 * Search for content of specified type in guest clipboard buffer and put
125 * it into newly allocated buffer.
126 *
127 * @param pPasteboard Guest PasteBoard reference.
128 * @param fFormat Data formats we are looking for.
129 * @param ppvData Where to return pointer to the received data. M
130 * @param pcbData Where to return the size of the data.
131 * @param pcbAlloc Where to return the size of the memory block
132 * *ppvData pointes to. (Usually greater than *cbData
133 * because the allocation is page aligned.)
134 * @returns IPRT status code.
135 */
136static int vbclClipboardReadGuestData(PasteboardRef pPasteboard, CFStringRef sFormat, void **ppvData, uint32_t *pcbData,
137 uint32_t *pcbAlloc)
138{
139 ItemCount cItems, iItem;
140 OSStatus rc;
141
142 void *pvData = NULL;
143 uint32_t cbData = 0;
144 uint32_t cbAlloc = 0;
145
146 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
147 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
148 AssertPtrReturn(pcbAlloc, VERR_INVALID_POINTER);
149
150 rc = PasteboardGetItemCount(pPasteboard, &cItems);
151 AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
152 AssertReturn(cItems > 0, VERR_INVALID_PARAMETER);
153
154 /* Walk through all the items in PasteBoard in order to find
155 that one that correcponds to requested data format. */
156 for (iItem = 1; iItem <= cItems; iItem++)
157 {
158 PasteboardItemID iItemID;
159 CFDataRef flavorData;
160
161 /* Now, get the item's flavors that corresponds to requested type. */
162 rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
163 AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
164 rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, sFormat, &flavorData);
165 if (rc == noErr)
166 {
167 void *flavorDataPtr = (void *)CFDataGetBytePtr(flavorData);
168 cbData = CFDataGetLength(flavorData);
169 if (flavorDataPtr && cbData > 0)
170 {
171 cbAlloc = RT_ALIGN_32(cbData, PAGE_SIZE);
172 pvData = RTMemPageAllocZ(cbAlloc);
173 if (pvData)
174 memcpy(pvData, flavorDataPtr, cbData);
175 }
176
177 CFRelease(flavorData);
178
179 /* Found first matching item, no more search. */
180 break;
181 }
182
183 }
184
185 /* Found match */
186 if (pvData)
187 {
188 *ppvData = pvData;
189 *pcbData = cbData;
190 *pcbAlloc = cbAlloc;
191
192 return VINF_SUCCESS;
193 }
194
195 return VERR_INVALID_PARAMETER;
196}
197
198
199/**
200 * Release resources occupied by vbclClipboardReadGuestData().
201 */
202static void vbclClipboardReleaseGuestData(void **ppvData, uint32_t cbAlloc)
203{
204 AssertReturnVoid(ppvData);
205 RTMemPageFree(*ppvData, cbAlloc);
206 *ppvData = NULL;
207}
208
209/**
210 * Pass data to host.
211 */
212static int vbclClipboardHostPasteData(uint32_t u32ClientId, uint32_t u32Format, const void *pvData, uint32_t cbData)
213{
214 /* Allow empty buffers */
215 if (cbData == 0)
216 return VbglR3ClipboardWriteData(u32ClientId, u32Format, NULL, 0);
217
218 AssertReturn(pvData, VERR_INVALID_PARAMETER);
219 return VbglR3ClipboardWriteData(u32ClientId, u32Format, (void *)pvData, cbData); /** @todo r=bird: Why on earth does a write function like VbglR3ClipboardWriteData take a non-const parameter? */
220}
221
222/**
223 * Paste text data into host clipboard.
224 *
225 * @param u32ClientId Host clipboard connection.
226 * @param pwszData UTF-16 encoded string.
227 * @param cbData The length of the string, in bytes, probably
228 * including a terminating zero.
229 */
230static int vbclClipboardHostPasteText(uint32_t u32ClientId, PRTUTF16 pwszData, uint32_t cbData)
231{
232 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
233 AssertPtrReturn(pwszData, VERR_INVALID_POINTER);
234
235 size_t cwcTmp; /* (includes a schwarzenegger character) */
236 int rc = ShClUtf16CalcNormalizedEolToCRLFLength(pwszData, cbData / sizeof(RTUTF16), &cwcTmp);
237 AssertRCReturn(rc, rc);
238
239 cwcTmp++; /* Add space for terminator. */
240
241 PRTUTF16 pwszTmp = (PRTUTF16)RTMemAlloc(cwcTmp * sizeof(RTUTF16));
242 AssertReturn(pwszTmp, VERR_NO_MEMORY);
243
244 rc = ShClConvUtf16LFToCRLF(pwszData, cbData / sizeof(RTUTF16), pwszTmp, cwcTmp);
245 if (RT_SUCCESS(rc))
246 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT,
247 pwszTmp, cwcTmp * sizeof(RTUTF16));
248
249 RTMemFree(pwszTmp);
250
251 return rc;
252}
253
254
255/**
256 * Paste a bitmap onto the host clipboard.
257 *
258 * @param u32ClientId Host clipboard connection.
259 * @param pvData The bitmap data.
260 * @param cbData The size of the bitmap.
261 */
262static int vbclClipboardHostPasteBitmap(uint32_t u32ClientId, void *pvData, uint32_t cbData)
263{
264 const void *pvDib;
265 size_t cbDib;
266 int rc = ShClBmpGetDib(pvData, cbData, &pvDib, &cbDib);
267 AssertRCReturn(rc, rc);
268
269 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, pvDib, cbDib);
270
271 return rc;
272}
273
274
275/**
276 * Read guest's clipboard buffer and forward its content to host.
277 *
278 * @param u32ClientId Host clipboard connection.
279 * @param pPasteboard Guest PasteBoard reference.
280 * @param fFormats List of data formats (bit field) received from host.
281 *
282 * @returns IPRT status code.
283 */
284int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
285{
286 int rc = VINF_SUCCESS;
287
288 void *pvData = NULL;
289 uint32_t cbData = 0;
290 uint32_t cbAlloc = 0;
291
292 VBoxClientVerbose(3, "vbclClipboardForwardToHost: %d\n", fFormats);
293
294 /* Walk across all item(s) formats */
295 uint32_t fFormatsLeft = fFormats;
296 while (fFormatsLeft)
297 {
298 if (fFormatsLeft & VBOX_SHCL_FMT_UNICODETEXT)
299 {
300 VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_UNICODETEXT: %d\n", fFormats);
301
302 RTUTF16 *pUtf16Str = NULL;
303
304 /* First, try to get UTF16 encoded buffer */
305 rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF16PlainText, &pvData, &cbData, &cbAlloc);
306 if (RT_SUCCESS(rc))
307 {
308 rc = RTUtf16DupEx(&pUtf16Str, (PRTUTF16)pvData, 0);
309 if (RT_FAILURE(rc))
310 pUtf16Str = NULL;
311 }
312 else /* Failed to get UTF16 buffer */
313 {
314 /* Then, try to get UTF8 encoded buffer */
315 rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF8PlainText, &pvData, &cbData, &cbAlloc);
316 if (RT_SUCCESS(rc))
317 {
318 rc = RTStrToUtf16((const char *)pvData, &pUtf16Str);
319 if (RT_FAILURE(rc))
320 pUtf16Str = NULL;
321 }
322 }
323
324 /* Finally, we got UTF16 encoded buffer */
325 if (RT_SUCCESS(rc))
326 {
327 rc = vbclClipboardHostPasteText(u32ClientId, (PRTUTF16)pvData, cbData);
328
329 if (pUtf16Str)
330 {
331 RTUtf16Free(pUtf16Str);
332 pUtf16Str = NULL;
333 }
334
335 vbclClipboardReleaseGuestData(&pvData, cbAlloc);
336 }
337 else
338 {
339 /* No data found or error occurred: send empty buffer */
340 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT, NULL, 0);
341 }
342
343 fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_UNICODETEXT;
344 }
345
346 else if (fFormatsLeft & VBOX_SHCL_FMT_BITMAP)
347 {
348 VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_BITMAP: %d\n", fFormats);
349
350 rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeBMP, &pvData, &cbData, &cbAlloc);
351 if (RT_SUCCESS(rc))
352 {
353 rc = vbclClipboardHostPasteBitmap(u32ClientId, pvData, cbData);
354 vbclClipboardReleaseGuestData(&pvData, cbAlloc);
355 }
356 else
357 {
358 /* No data found or error occurred: send empty buffer */
359 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, NULL, 0);
360 }
361
362 fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_BITMAP;
363 }
364
365 else if (fFormatsLeft & VBOX_SHCL_FMT_HTML)
366 {
367 VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_HTML: %d\n", fFormats);
368
369 rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeHTML, &pvData, &cbData, &cbAlloc);
370 if (RT_SUCCESS(rc))
371 {
372 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, pvData, cbData);
373 vbclClipboardReleaseGuestData(&pvData, cbAlloc);
374 }
375 else
376 {
377 /* No data found or error occurred: send empty buffer */
378 rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, NULL, 0);
379 }
380
381 fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_HTML;
382 }
383
384 else
385 {
386 VBoxClientVerbose(3, "requested data in unsupported format: %#x\n", fFormatsLeft);
387 break;
388 }
389 }
390
391 return rc; /** @todo r=bird: If there are multiple formats available, which rc is returned here? Does it matter? */
392}
393
394RT_GCC_NO_WARN_DEPRECATED_END
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