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