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