FreeRDP
Loading...
Searching...
No Matches
sdl_clip.cpp
1
21#include <string>
22#include <sstream>
23#include <mutex>
24#include <iterator>
25#include <algorithm>
26#include <utility>
27
28#include <winpr/wlog.h>
29#include <winpr/image.h>
30
31#include "sdl_clip.hpp"
32#include "sdl_context.hpp"
33
34#define TAG CLIENT_TAG("sdl.cliprdr")
35
36#define mime_text_plain "text/plain"
37// NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
38const char mime_text_utf8[] = mime_text_plain ";charset=utf-8";
39
40[[nodiscard]] static const std::vector<const char*>& s_mime_text()
41{
42 static std::vector<const char*> values;
43 if (values.empty())
44 {
45 values = std::vector<const char*>(
46 { mime_text_plain, mime_text_utf8, "UTF8_STRING", "COMPOUND_TEXT", "TEXT", "STRING" });
47 }
48 return values;
49}
50
51static const char s_mime_png[] = "image/png";
52static const char s_mime_webp[] = "image/webp";
53static const char s_mime_jpg[] = "image/jpeg";
54
55static const char s_mime_tiff[] = "image/tiff";
56static const char s_mime_uri_list[] = "text/uri-list";
57static const char s_mime_html[] = "text/html";
58
59#define BMP_MIME_LIST "image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"
60
61[[nodiscard]] static const std::vector<const char*>& s_mime_bitmap()
62{
63 static std::vector<const char*> values;
64 if (values.empty())
65 {
66 values = std::vector<const char*>({ BMP_MIME_LIST });
67 }
68 return values;
69}
70
71[[nodiscard]] static const std::vector<const char*>& s_mime_image()
72{
73 static std::vector<const char*> values;
74 if (values.empty())
75 {
76 if (winpr_image_format_is_supported(WINPR_IMAGE_WEBP))
77 values.push_back(s_mime_webp);
78
79 if (winpr_image_format_is_supported(WINPR_IMAGE_PNG))
80 values.push_back(s_mime_png);
81
82 if (winpr_image_format_is_supported(WINPR_IMAGE_JPEG))
83 values.push_back(s_mime_jpg);
84
85 auto bmp = std::vector<const char*>({ s_mime_tiff, BMP_MIME_LIST });
86 values.insert(values.end(), bmp.begin(), bmp.end());
87 }
88 return values;
89}
90
91static const char s_mime_gnome_copied_files[] = "x-special/gnome-copied-files";
92static const char s_mime_mate_copied_files[] = "x-special/mate-copied-files";
93
94static const char s_mime_freerdp_update[] = "x-special/freerdp-clipboard-update";
95
96static const char* s_type_HtmlFormat = "HTML Format";
97static const char* s_type_FileGroupDescriptorW = "FileGroupDescriptorW";
98
99class ClipboardLockGuard
100{
101 public:
102 explicit ClipboardLockGuard(wClipboard* clipboard) : _clipboard(clipboard)
103 {
104 ClipboardLock(_clipboard);
105 }
106 ClipboardLockGuard(const ClipboardLockGuard& other) = delete;
107 ClipboardLockGuard(ClipboardLockGuard&& other) = delete;
108
109 ClipboardLockGuard& operator=(const ClipboardLockGuard& rhs) = delete;
110 ClipboardLockGuard& operator=(ClipboardLockGuard&& rhs) = delete;
111
112 ~ClipboardLockGuard()
113 {
114 ClipboardUnlock(_clipboard);
115 }
116
117 private:
118 wClipboard* _clipboard;
119};
120
121static bool operator<(const CLIPRDR_FORMAT& lhs, const CLIPRDR_FORMAT& rhs)
122{
123 return (lhs.formatId < rhs.formatId);
124}
125
126static bool operator==(const CLIPRDR_FORMAT& lhs, const CLIPRDR_FORMAT& rhs)
127{
128 return (lhs.formatId == rhs.formatId);
129}
130
131sdlClip::sdlClip(SdlContext* sdl)
132 : _sdl(sdl), _file(cliprdr_file_context_new(this)), _log(WLog_Get(TAG)),
133 _system(ClipboardCreate()), _event(CreateEventA(nullptr, TRUE, FALSE, nullptr)),
134 _uuid(sdl::utils::generate_uuid_v4())
135{
136 WINPR_ASSERT(sdl);
137
138 std::stringstream ss;
139 ss << s_mime_freerdp_update << "-" << _uuid;
140 _mime_uuid = ss.str();
141
142 std::ignore = cliprdr_file_context_set_locally_available(_file, TRUE);
143}
144
145sdlClip::~sdlClip()
146{
147 cliprdr_file_context_free(_file);
148 ClipboardDestroy(_system);
149 std::ignore = CloseHandle(_event);
150}
151
152bool sdlClip::init(CliprdrClientContext* clip)
153{
154 WINPR_ASSERT(clip);
155 _ctx = clip;
156 clip->custom = this;
157 _ctx->MonitorReady = sdlClip::MonitorReady;
158 _ctx->ServerCapabilities = sdlClip::ReceiveServerCapabilities;
159 _ctx->ServerFormatList = sdlClip::ReceiveServerFormatList;
160 _ctx->ServerFormatListResponse = sdlClip::ReceiveFormatListResponse;
161 _ctx->ServerFormatDataRequest = sdlClip::ReceiveFormatDataRequest;
162 _ctx->ServerFormatDataResponse = sdlClip::ReceiveFormatDataResponse;
163
164 return cliprdr_file_context_init(_file, _ctx);
165}
166
167bool sdlClip::uninit(CliprdrClientContext* clip)
168{
169 WINPR_ASSERT(clip);
170 if (!cliprdr_file_context_uninit(_file, _ctx))
171 return false;
172 _ctx = nullptr;
173 clip->custom = nullptr;
174 return true;
175}
176
177bool sdlClip::contains(const char** mime_types, Sint32 count)
178{
179 for (Sint32 x = 0; x < count; x++)
180 {
181 const auto mime = mime_types[x];
182 if (mime && (strcmp(_mime_uuid.c_str(), mime) == 0))
183 return true;
184 }
185 return false;
186}
187
188bool sdlClip::handleEvent(const SDL_ClipboardEvent& ev)
189{
190 if (!_ctx || !_sync || ev.owner)
191 {
192 _last_timestamp = ev.timestamp;
193 if (!_current_mimetypes.empty())
194 {
195 _cache_data.clear();
196 auto rc =
197 SDL_SetClipboardData(sdlClip::ClipDataCb, sdlClip::ClipCleanCb, this, ev.mime_types,
198 WINPR_ASSERTING_INT_CAST(size_t, ev.num_mime_types));
199 _current_mimetypes.clear();
200 return rc;
201 }
202 return true;
203 }
204
205 if (ev.timestamp == _last_timestamp)
206 {
207 return true;
208 }
209
210 if (contains(ev.mime_types, ev.num_mime_types))
211 {
212 return true;
213 }
214
215 clearServerFormats();
216
217 std::string mime_html = s_mime_html;
218
219 std::vector<std::string> mime_bitmap = { BMP_MIME_LIST };
220 std::string mime_webp = s_mime_webp;
221 std::string mime_png = s_mime_png;
222 std::string mime_jpeg = s_mime_jpg;
223 std::string mime_tiff = s_mime_tiff;
224 std::vector<std::string> mime_images = { mime_webp, mime_png, mime_jpeg, mime_tiff };
225
226 std::vector<std::string> clientFormatNames;
227 std::vector<CLIPRDR_FORMAT> clientFormats;
228
229 size_t nformats = WINPR_ASSERTING_INT_CAST(size_t, ev.num_mime_types);
230 const char** clipboard_mime_formats = ev.mime_types;
231
232 WLog_Print(_log, WLOG_TRACE, "SDL has %" PRIuz " formats", nformats);
233
234 bool textPushed = false;
235 bool imgPushed = false;
236 bool filePushed = false;
237
238 for (size_t i = 0; i < nformats; i++)
239 {
240 std::string local_mime = clipboard_mime_formats[i];
241 WLog_Print(_log, WLOG_TRACE, " - %s", local_mime.c_str());
242
243 if (std::find(s_mime_text().begin(), s_mime_text().end(), local_mime) !=
244 s_mime_text().end())
245 {
246 /* text formats */
247 if (!textPushed)
248 {
249 clientFormats.push_back({ CF_TEXT, nullptr });
250 clientFormats.push_back({ CF_OEMTEXT, nullptr });
251 clientFormats.push_back({ CF_UNICODETEXT, nullptr });
252 textPushed = true;
253 }
254 }
255 else if (local_mime == mime_html)
256 /* html */
257 clientFormatNames.emplace_back(s_type_HtmlFormat);
258 else if ((std::find(mime_bitmap.begin(), mime_bitmap.end(), local_mime) !=
259 mime_bitmap.end()) ||
260 (std::find(mime_images.begin(), mime_images.end(), local_mime) !=
261 mime_images.end()))
262 {
263 /* image formats */
264 if (!imgPushed)
265 {
266 clientFormats.push_back({ CF_DIB, nullptr });
267#if defined(WINPR_UTILS_IMAGE_DIBv5)
268 clientFormats.push_back({ CF_DIBV5, nullptr });
269#endif
270
271 for (auto& bmp : mime_bitmap)
272 clientFormatNames.push_back(bmp);
273
274 for (auto& img : mime_images)
275 clientFormatNames.push_back(img);
276
277 clientFormatNames.emplace_back(s_type_HtmlFormat);
278 imgPushed = true;
279 }
280 }
281 else if (mime_is_file(local_mime))
282 {
283 if (!filePushed)
284 {
285 clientFormatNames.emplace_back(s_type_FileGroupDescriptorW);
286 filePushed = true;
287 }
288 }
289 }
290
291 for (auto& name : clientFormatNames)
292 {
293 clientFormats.push_back({ ClipboardRegisterFormat(_system, name.c_str()), name.data() });
294 }
295
296 std::sort(clientFormats.begin(), clientFormats.end(),
297 [](const auto& a, const auto& b) { return a < b; });
298 auto u = std::unique(clientFormats.begin(), clientFormats.end());
299 clientFormats.erase(u, clientFormats.end());
300
301 const CLIPRDR_FORMAT_LIST formatList = {
302 { CB_FORMAT_LIST, 0, 0 },
303 static_cast<UINT32>(clientFormats.size()),
304 clientFormats.data(),
305 };
306
307 WLog_Print(_log, WLOG_TRACE,
308 "-------------- client format list [%" PRIu32 "] ------------------",
309 formatList.numFormats);
310 for (UINT32 x = 0; x < formatList.numFormats; x++)
311 {
312 auto format = &formatList.formats[x];
313 WLog_Print(_log, WLOG_TRACE, "client announces %" PRIu32 " [%s][%s]", format->formatId,
314 ClipboardGetFormatIdString(format->formatId), format->formatName);
315 }
316
317 if (cliprdr_file_context_notify_new_client_format_list(_file) != CHANNEL_RC_OK)
318 return false;
319
320 WINPR_ASSERT(_ctx);
321 WINPR_ASSERT(_ctx->ClientFormatList);
322 return _ctx->ClientFormatList(_ctx, &formatList) == CHANNEL_RC_OK;
323}
324
325UINT sdlClip::MonitorReady(CliprdrClientContext* context, const CLIPRDR_MONITOR_READY* monitorReady)
326{
327 WINPR_UNUSED(monitorReady);
328 WINPR_ASSERT(context);
329 WINPR_ASSERT(monitorReady);
330
331 auto clipboard = static_cast<sdlClip*>(
332 cliprdr_file_context_get_context(static_cast<CliprdrFileContext*>(context->custom)));
333 WINPR_ASSERT(clipboard);
334
335 auto ret = clipboard->SendClientCapabilities();
336 if (ret != CHANNEL_RC_OK)
337 return ret;
338
339 clipboard->_sync = true;
340 if (!sdl_push_user_event(SDL_EVENT_CLIPBOARD_UPDATE))
341 return ERROR_INTERNAL_ERROR;
342
343 return CHANNEL_RC_OK;
344}
345
346UINT sdlClip::SendClientCapabilities()
347{
348 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet = {
349 CB_CAPSTYPE_GENERAL, 12, CB_CAPS_VERSION_2,
350 CB_USE_LONG_FORMAT_NAMES | cliprdr_file_context_current_flags(_file)
351 };
352 const CLIPRDR_CAPABILITIES capabilities = {
353 { CB_TYPE_NONE, 0, 0 }, 1, reinterpret_cast<CLIPRDR_CAPABILITY_SET*>(&generalCapabilitySet)
354 };
355
356 WINPR_ASSERT(_ctx);
357 WINPR_ASSERT(_ctx->ClientCapabilities);
358 return _ctx->ClientCapabilities(_ctx, &capabilities);
359}
360
361void sdlClip::clearServerFormats()
362{
363 _serverFormats.clear();
364 _cache_data.clear();
365 cliprdr_file_context_clear(_file);
366}
367
368UINT sdlClip::SendFormatListResponse(BOOL status)
369{
370 const CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = {
371 { CB_FORMAT_LIST_RESPONSE, static_cast<UINT16>(status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL),
372 0 }
373 };
374 WINPR_ASSERT(_ctx);
375 WINPR_ASSERT(_ctx->ClientFormatListResponse);
376 return _ctx->ClientFormatListResponse(_ctx, &formatListResponse);
377}
378
379UINT sdlClip::SendDataResponse(const BYTE* data, size_t size)
380{
381 CLIPRDR_FORMAT_DATA_RESPONSE response = {};
382
383 if (size > UINT32_MAX)
384 return ERROR_INVALID_PARAMETER;
385
386 response.common.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
387 response.common.dataLen = static_cast<UINT32>(size);
388 response.requestedFormatData = data;
389
390 WINPR_ASSERT(_ctx);
391 WINPR_ASSERT(_ctx->ClientFormatDataResponse);
392 return _ctx->ClientFormatDataResponse(_ctx, &response);
393}
394
395UINT sdlClip::SendDataRequest(uint32_t formatID, const std::string& mime)
396{
397 const CLIPRDR_FORMAT_DATA_REQUEST request = { { CB_TYPE_NONE, 0, 0 }, formatID };
398
399 _request_queue.emplace(formatID, mime);
400
401 WINPR_ASSERT(_ctx);
402 WINPR_ASSERT(_ctx->ClientFormatDataRequest);
403 UINT ret = _ctx->ClientFormatDataRequest(_ctx, &request);
404 if (ret != CHANNEL_RC_OK)
405 {
406 WLog_Print(_log, WLOG_ERROR, "error sending ClientFormatDataRequest, cancelling request");
407 _request_queue.pop();
408 }
409
410 return ret;
411}
412
413std::string sdlClip::getServerFormat(uint32_t id)
414{
415 for (auto& fmt : _serverFormats)
416 {
417 if (fmt.formatId() == id)
418 {
419 if (fmt.formatName())
420 return fmt.formatName();
421 break;
422 }
423 }
424
425 return "";
426}
427
428uint32_t sdlClip::serverIdForMime(const std::string& mime)
429{
430 std::string cmp = mime;
431 if (mime_is_html(mime))
432 cmp = s_type_HtmlFormat;
433 if (mime_is_file(mime))
434 cmp = s_type_FileGroupDescriptorW;
435
436 for (auto& format : _serverFormats)
437 {
438 if (!format.formatName())
439 continue;
440 if (cmp == format.formatName())
441 return format.formatId();
442 }
443
444 if (mime_is_image(mime))
445 return CF_DIB;
446 if (mime_is_text(mime))
447 return CF_UNICODETEXT;
448
449 return 0;
450}
451
452UINT sdlClip::ReceiveServerCapabilities(CliprdrClientContext* context,
453 const CLIPRDR_CAPABILITIES* capabilities)
454{
455 WINPR_ASSERT(context);
456 WINPR_ASSERT(capabilities);
457
458 auto capsPtr = reinterpret_cast<const BYTE*>(capabilities->capabilitySets);
459 WINPR_ASSERT(capsPtr);
460
461 auto clipboard = static_cast<sdlClip*>(
462 cliprdr_file_context_get_context(static_cast<CliprdrFileContext*>(context->custom)));
463 WINPR_ASSERT(clipboard);
464
465 if (!cliprdr_file_context_remote_set_flags(clipboard->_file, 0))
466 return ERROR_INTERNAL_ERROR;
467
468 for (UINT32 i = 0; i < capabilities->cCapabilitiesSets; i++)
469 {
470 auto caps = reinterpret_cast<const CLIPRDR_CAPABILITY_SET*>(capsPtr);
471
472 if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
473 {
474 auto generalCaps = reinterpret_cast<const CLIPRDR_GENERAL_CAPABILITY_SET*>(caps);
475
476 if (!cliprdr_file_context_remote_set_flags(clipboard->_file, generalCaps->generalFlags))
477 return ERROR_INTERNAL_ERROR;
478 }
479
480 capsPtr += caps->capabilitySetLength;
481 }
482
483 return CHANNEL_RC_OK;
484}
485
486UINT sdlClip::ReceiveServerFormatList(CliprdrClientContext* context,
487 const CLIPRDR_FORMAT_LIST* formatList)
488{
489 BOOL html = FALSE;
490 BOOL text = FALSE;
491 BOOL image = FALSE;
492 BOOL file = FALSE;
493
494 if (!context || !context->custom)
495 return ERROR_INVALID_PARAMETER;
496
497 auto filecontext = static_cast<CliprdrFileContext*>(context->custom);
498 auto clipboard = static_cast<sdlClip*>(cliprdr_file_context_get_context(filecontext));
499 WINPR_ASSERT(clipboard);
500
501 clipboard->clearServerFormats();
502
503 for (UINT32 i = 0; i < formatList->numFormats; i++)
504 {
505 const CLIPRDR_FORMAT* format = &formatList->formats[i];
506
507 clipboard->_serverFormats.emplace_back(format->formatId, format->formatName);
508
509 if (format->formatName)
510 {
511 if (strcmp(format->formatName, s_type_HtmlFormat) == 0)
512 {
513 text = TRUE;
514 html = TRUE;
515 }
516 else if (strcmp(format->formatName, s_type_FileGroupDescriptorW) == 0)
517 {
518 file = TRUE;
519 text = TRUE;
520 }
521 }
522 else
523 {
524 switch (format->formatId)
525 {
526 case CF_TEXT:
527 case CF_OEMTEXT:
528 case CF_UNICODETEXT:
529 text = TRUE;
530 break;
531
532 case CF_DIB:
533 image = TRUE;
534 break;
535
536 default:
537 break;
538 }
539 }
540 }
541
542 clipboard->_current_mimetypes.clear();
543
544 {
545 ClipboardLockGuard systemlock(clipboard->_system);
546 std::scoped_lock lock(clipboard->_lock);
547 auto res = cliprdr_file_context_notify_new_server_format_list(filecontext);
548 if (res != CHANNEL_RC_OK)
549 return res;
550 }
551
552 if (text)
553 {
554 clipboard->_current_mimetypes.insert(clipboard->_current_mimetypes.end(),
555 s_mime_text().begin(), s_mime_text().end());
556 }
557 if (image)
558 {
559 clipboard->_current_mimetypes.insert(clipboard->_current_mimetypes.end(),
560 s_mime_bitmap().begin(), s_mime_bitmap().end());
561 clipboard->_current_mimetypes.insert(clipboard->_current_mimetypes.end(),
562 s_mime_image().begin(), s_mime_image().end());
563 }
564 if (html)
565 {
566 clipboard->_current_mimetypes.push_back(s_mime_html);
567 }
568 if (file)
569 {
570 clipboard->_current_mimetypes.push_back(s_mime_uri_list);
571 clipboard->_current_mimetypes.push_back(s_mime_gnome_copied_files);
572 clipboard->_current_mimetypes.push_back(s_mime_mate_copied_files);
573 }
574 clipboard->_current_mimetypes.push_back(clipboard->_mime_uuid.c_str());
575
576 auto s = clipboard->_current_mimetypes.size();
577 SDL_Event ev = { SDL_EVENT_CLIPBOARD_UPDATE };
578 ev.clipboard.owner = true;
579 ev.clipboard.timestamp = SDL_GetTicksNS();
580 ev.clipboard.num_mime_types = WINPR_ASSERTING_INT_CAST(Sint32, s);
581 ev.clipboard.mime_types = clipboard->_current_mimetypes.data();
582
583 auto rc = (SDL_PushEvent(&ev) == 1);
584 return clipboard->SendFormatListResponse(rc);
585}
586
587UINT sdlClip::ReceiveFormatListResponse(WINPR_ATTR_UNUSED CliprdrClientContext* context,
588 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
589{
590 WINPR_ASSERT(context);
591 WINPR_ASSERT(formatListResponse);
592
593 if (formatListResponse->common.msgFlags & CB_RESPONSE_FAIL)
594 WLog_WARN(TAG, "format list update failed");
595 return CHANNEL_RC_OK;
596}
597
598std::shared_ptr<BYTE> sdlClip::ReceiveFormatDataRequestHandle(
599 sdlClip* clipboard, const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest, uint32_t& len)
600{
601 const char* mime = nullptr;
602 UINT32 formatId = 0;
603
604 BOOL res = FALSE;
605
606 std::shared_ptr<BYTE> data;
607
608 WINPR_ASSERT(clipboard);
609 WINPR_ASSERT(formatDataRequest);
610
611 len = 0;
612 auto localFormatId = formatId = formatDataRequest->requestedFormatId;
613 WLog_Print(clipboard->_log, WLOG_DEBUG, "Requesting format %s [0x%08" PRIx32 "] [%s]",
614 ClipboardGetFormatIdString(localFormatId), localFormatId,
615 ClipboardGetFormatName(clipboard->_system, localFormatId));
616
617 ClipboardLockGuard systemlock(clipboard->_system);
618 std::scoped_lock lock(clipboard->_lock);
619
620 const UINT32 fileFormatId =
621 ClipboardGetFormatId(clipboard->_system, s_type_FileGroupDescriptorW);
622 const UINT32 htmlFormatId = ClipboardGetFormatId(clipboard->_system, s_type_HtmlFormat);
623
624 switch (formatId)
625 {
626 case CF_TEXT:
627 case CF_OEMTEXT:
628 case CF_UNICODETEXT:
629 localFormatId = ClipboardGetFormatId(clipboard->_system, mime_text_plain);
630 mime = mime_text_utf8;
631 break;
632
633 case CF_DIB:
634 case CF_DIBV5:
635 mime = s_mime_bitmap().at(0);
636 localFormatId = ClipboardGetFormatId(clipboard->_system, mime);
637 break;
638
639 case CF_TIFF:
640 mime = s_mime_tiff;
641 break;
642
643 default:
644 if (formatId == fileFormatId)
645 {
646 localFormatId = ClipboardGetFormatId(clipboard->_system, s_mime_uri_list);
647 mime = s_mime_uri_list;
648 }
649 else if (formatId == htmlFormatId)
650 {
651 /* In case HTML format was requested but we only have images in local clipboard */
652 if (!SDL_HasClipboardData(s_mime_html))
653 {
654 for (const auto& cmime : s_mime_image())
655 {
656 if (SDL_HasClipboardData(cmime))
657 {
658 localFormatId = ClipboardGetFormatId(clipboard->_system, cmime);
659 mime = cmime;
660 break;
661 }
662 }
663 }
664 else
665 {
666 localFormatId = ClipboardGetFormatId(clipboard->_system, s_mime_html);
667 mime = s_mime_html;
668 }
669 }
670 else
671 return data;
672 }
673
674 {
675 size_t size = 0;
676 auto sdldata = std::shared_ptr<void>(SDL_GetClipboardData(mime, &size), SDL_free);
677 if (!sdldata)
678 return data;
679
680 if (fileFormatId == formatId)
681 {
682 auto bdata = static_cast<const char*>(sdldata.get());
683 if (!cliprdr_file_context_update_client_data(clipboard->_file, bdata, size))
684 return data;
685 }
686
687 res = ClipboardSetData(clipboard->_system, localFormatId, sdldata.get(),
688 static_cast<uint32_t>(size));
689 }
690
691 if (!res)
692 return data;
693
694 uint32_t ptrlen = 0;
695 auto ptr = static_cast<BYTE*>(ClipboardGetData(clipboard->_system, formatId, &ptrlen));
696 data = std::shared_ptr<BYTE>(ptr, free);
697
698 if (!data)
699 return data;
700
701 if (fileFormatId == formatId)
702 {
703 BYTE* ddata = nullptr;
704 UINT32 dsize = 0;
705 const UINT32 flags = cliprdr_file_context_remote_get_flags(clipboard->_file);
706 const UINT32 error = cliprdr_serialize_file_list_ex(
707 flags, reinterpret_cast<const FILEDESCRIPTORW*>(data.get()),
708 ptrlen / sizeof(FILEDESCRIPTORW), &ddata, &dsize);
709 data.reset();
710 auto tmp = std::shared_ptr<BYTE>(ddata, free);
711 if (error)
712 return data;
713
714 data = tmp;
715 len = dsize;
716 }
717 else
718 len = ptrlen;
719 return data;
720}
721
722UINT sdlClip::ReceiveFormatDataRequest(CliprdrClientContext* context,
723 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
724{
725 WINPR_ASSERT(context);
726 WINPR_ASSERT(formatDataRequest);
727
728 auto clipboard = static_cast<sdlClip*>(
729 cliprdr_file_context_get_context(static_cast<CliprdrFileContext*>(context->custom)));
730 WINPR_ASSERT(clipboard);
731
732 uint32_t len = 0;
733 auto rc = ReceiveFormatDataRequestHandle(clipboard, formatDataRequest, len);
734 return clipboard->SendDataResponse(rc.get(), len);
735}
736
737UINT sdlClip::ReceiveFormatDataResponse(CliprdrClientContext* context,
738 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
739{
740 WINPR_ASSERT(context);
741 WINPR_ASSERT(formatDataResponse);
742
743 const UINT32 size = formatDataResponse->common.dataLen;
744 const BYTE* data = formatDataResponse->requestedFormatData;
745
746 auto clipboard = static_cast<sdlClip*>(
747 cliprdr_file_context_get_context(static_cast<CliprdrFileContext*>(context->custom)));
748 WINPR_ASSERT(clipboard);
749
750 ClipboardLockGuard systemlock(clipboard->_system);
751 std::scoped_lock lock(clipboard->_lock);
752 if (clipboard->_request_queue.empty())
753 {
754 WLog_Print(clipboard->_log, WLOG_ERROR, "no pending format request");
755 return ERROR_INTERNAL_ERROR;
756 }
757
758 do
759 {
760 UINT32 srcFormatId = 0;
761 auto& request = clipboard->_request_queue.front();
762 bool success = (formatDataResponse->common.msgFlags & CB_RESPONSE_OK) &&
763 !(formatDataResponse->common.msgFlags & CB_RESPONSE_FAIL);
764 request.setSuccess(success);
765
766 if (!success)
767 {
768 WLog_Print(clipboard->_log, WLOG_WARN,
769 "clipboard data request for format %" PRIu32 " [%s], mime %s failed",
770 request.format(), request.formatstr().c_str(), request.mime().c_str());
771 break;
772 }
773
774 switch (request.format())
775 {
776 case CF_TEXT:
777 case CF_OEMTEXT:
778 case CF_UNICODETEXT:
779 srcFormatId = request.format();
780 break;
781
782 case CF_DIB:
783 case CF_DIBV5:
784 srcFormatId = request.format();
785 break;
786
787 default:
788 {
789 auto name = clipboard->getServerFormat(request.format());
790 if (!name.empty())
791 {
792 if (name == s_type_FileGroupDescriptorW)
793 {
794 srcFormatId =
795 ClipboardGetFormatId(clipboard->_system, s_type_FileGroupDescriptorW);
796
797 if (!cliprdr_file_context_update_server_data(
798 clipboard->_file, clipboard->_system, data, size))
799 return ERROR_INTERNAL_ERROR;
800 }
801 else if (name == s_type_HtmlFormat)
802 {
803 srcFormatId = ClipboardGetFormatId(clipboard->_system, s_type_HtmlFormat);
804 }
805 }
806 }
807 break;
808 }
809
810 if (!ClipboardSetData(clipboard->_system, srcFormatId, data, size))
811 {
812 WLog_Print(clipboard->_log, WLOG_ERROR, "error when setting clipboard data");
813 return ERROR_INTERNAL_ERROR;
814 }
815 WLog_Print(clipboard->_log, WLOG_DEBUG, "updated clipboard data %s [0x%08" PRIx32 "]",
816 ClipboardGetFormatName(clipboard->_system, srcFormatId), srcFormatId);
817 } while (false);
818
819 if (!SetEvent(clipboard->_event))
820 {
821 WLog_Print(clipboard->_log, WLOG_ERROR, "error when setting clipboard event");
822 return ERROR_INTERNAL_ERROR;
823 }
824
825 return CHANNEL_RC_OK;
826}
827
828const void* sdlClip::ClipDataCb(void* userdata, const char* mime_type, size_t* size)
829{
830 auto clip = static_cast<sdlClip*>(userdata);
831 WINPR_ASSERT(clip);
832 WINPR_ASSERT(size);
833 WINPR_ASSERT(mime_type);
834
835 *size = 0;
836 uint32_t len = 0;
837
838 if (mime_is_text(mime_type))
839 mime_type = "text/plain";
840
841 {
842 ClipboardLockGuard systemlock(clip->_system);
843 std::scoped_lock lock(clip->_lock);
844
845 /* check if we already used this mime type */
846 auto cache = clip->_cache_data.find(mime_type);
847 if (cache != clip->_cache_data.end())
848 {
849 *size = cache->second.size;
850 return cache->second.ptr.get();
851 }
852
853 auto formatID = clip->serverIdForMime(mime_type);
854
855 /* Can we convert the data from existing formats in the clibpard? */
856 uint32_t fsize = 0;
857 auto mimeFormatID = ClipboardRegisterFormat(clip->_system, mime_type);
858 auto fptr = ClipboardGetData(clip->_system, mimeFormatID, &fsize);
859 if (fptr)
860 {
861 auto ptr = std::shared_ptr<void>(fptr, free);
862 clip->_cache_data.insert({ mime_type, { fsize, ptr } });
863
864 auto fcache = clip->_cache_data.find(mime_type);
865 if (fcache != clip->_cache_data.end())
866 {
867 *size = fcache->second.size;
868 return fcache->second.ptr.get();
869 }
870 }
871
872 WLog_Print(clip->_log, WLOG_DEBUG, "requesting format %s [%s 0x%08" PRIx32 "]", mime_type,
873 ClipboardGetFormatName(clip->_system, formatID), formatID);
874 if (clip->SendDataRequest(formatID, mime_type))
875 return nullptr;
876 }
877 {
878 HANDLE hdl[2] = { freerdp_abort_event(clip->_sdl->context()), clip->_event };
879
880 DWORD status = WaitForMultipleObjects(ARRAYSIZE(hdl), hdl, FALSE, 10 * 1000);
881
882 if (status != WAIT_OBJECT_0 + 1)
883 {
884 std::scoped_lock lock(clip->_lock);
885 clip->_request_queue.pop();
886
887 if (status == WAIT_TIMEOUT)
888 WLog_Print(clip->_log, WLOG_ERROR,
889 "no reply in 10 seconds, returning empty content");
890
891 return nullptr;
892 }
893 }
894
895 {
896 ClipboardLockGuard systemlock(clip->_system);
897 std::scoped_lock lock(clip->_lock);
898 auto request = clip->_request_queue.front();
899 clip->_request_queue.pop();
900
901 if (clip->_request_queue.empty())
902 std::ignore = ResetEvent(clip->_event);
903
904 if (request.success())
905 {
906 auto formatID = ClipboardRegisterFormat(clip->_system, mime_type);
907 auto data = ClipboardGetData(clip->_system, formatID, &len);
908 if (!data)
909 {
910 WLog_Print(clip->_log, WLOG_ERROR, "error retrieving clipboard data");
911 return nullptr;
912 }
913
914 auto ptr = std::shared_ptr<void>(data, free);
915 clip->_cache_data.insert({ mime_type, { len, ptr } });
916 *size = len;
917 return ptr.get();
918 }
919
920 return nullptr;
921 }
922}
923
924void sdlClip::ClipCleanCb(void* userdata)
925{
926 auto clip = static_cast<sdlClip*>(userdata);
927 WINPR_ASSERT(clip);
928 ClipboardLockGuard give_me_a_name(clip->_system);
929 std::scoped_lock lock(clip->_lock);
930 ClipboardEmpty(clip->_system);
931}
932
933bool sdlClip::mime_is_file(const std::string& mime)
934{
935 if (strncmp(s_mime_uri_list, mime.c_str(), sizeof(s_mime_uri_list)) == 0)
936 return true;
937 if (strncmp(s_mime_gnome_copied_files, mime.c_str(), sizeof(s_mime_gnome_copied_files)) == 0)
938 return true;
939 if (strncmp(s_mime_mate_copied_files, mime.c_str(), sizeof(s_mime_mate_copied_files)) == 0)
940 return true;
941 return false;
942}
943
944bool sdlClip::mime_is_text(const std::string& mime)
945{
946 for (const auto& tmime : s_mime_text())
947 {
948 assert(tmime != nullptr);
949 if (mime == tmime)
950 return true;
951 }
952
953 return false;
954}
955
956bool sdlClip::mime_is_image(const std::string& mime)
957{
958 for (const auto& imime : s_mime_image())
959 {
960 assert(imime != nullptr);
961 if (mime == imime)
962 return true;
963 }
964
965 return false;
966}
967
968bool sdlClip::mime_is_bmp(const std::string& mime)
969{
970 for (const auto& imime : s_mime_bitmap())
971 {
972 assert(imime != nullptr);
973 if (mime == imime)
974 return true;
975 }
976
977 return false;
978}
979
980bool sdlClip::mime_is_html(const std::string& mime)
981{
982 return mime.compare(s_mime_html) == 0;
983}
984
985ClipRequest::ClipRequest(UINT32 format, const std::string& mime)
986 : _format(format), _mime(mime), _success(false)
987{
988}
989
990uint32_t ClipRequest::format() const
991{
992 return _format;
993}
994
995std::string ClipRequest::formatstr() const
996{
997 return ClipboardGetFormatIdString(_format);
998}
999
1000std::string ClipRequest::mime() const
1001{
1002 return _mime;
1003}
1004
1005bool ClipRequest::success() const
1006{
1007 return _success;
1008}
1009
1010void ClipRequest::setSuccess(bool status)
1011{
1012 _success = status;
1013}
1014
1015CliprdrFormat::CliprdrFormat(uint32_t formatID, const char* formatName) : _formatID(formatID)
1016{
1017 if (formatName)
1018 _formatName = formatName;
1019}
1020
1021uint32_t CliprdrFormat::formatId() const
1022{
1023 return _formatID;
1024}
1025
1026const char* CliprdrFormat::formatName() const
1027{
1028 if (_formatName.empty())
1029 return nullptr;
1030 return _formatName.c_str();
1031}
object that handles clipboard context for the SDL3 client
Definition sdl_clip.hpp:76