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