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