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