314{
315 std::unique_ptr<char*, decltype(&SDL_free)> current(nullptr, SDL_free);
316 const char** mime_types = ev.mime_types;
317 size_t nformats = WINPR_ASSERTING_INT_CAST(size_t, ev.num_mime_types);
318 bool connected = false;
319 {
320 std::scoped_lock lock(_lock);
321 connected = (_ctx != nullptr);
322 }
323 if (connected && _ctx && _sync && ev.owner && (ev.num_mime_types == 1))
324 {
325 if (strcmp(first_announcement[0], ev.mime_types[0]) == 0)
326 {
327 current.reset(SDL_GetClipboardMimeTypes(&nformats));
328 mime_types = const_cast<const char**>(current.get());
329 if (!mime_types)
330 nformats = 0;
331 }
332 }
333 else if (!connected || !_ctx || !_sync || ev.owner)
334 {
335 _last_timestamp = ev.timestamp;
336
337
338
339 std::vector<std::string> mimes;
340 {
341 ClipboardLockGuard systemlock(_system);
342 std::scoped_lock lock(_lock);
343 mimes.swap(_current_mimetypes);
344 if (!mimes.empty())
345 _cache_data.clear();
346 }
347 if (mimes.empty())
348 return true;
349
350 std::vector<const char*> cmimes;
351 cmimes.reserve(mimes.size());
352 for (const auto& m : mimes)
353 cmimes.push_back(m.c_str());
354
355
356
357 if (cliprdr_file_context_set_local_clipboard_data(_file, sdlClip::ClipDataCb, this,
358 cmimes.data(), cmimes.size()))
359 return true;
360
361
362 return SDL_SetClipboardData(sdlClip::ClipDataCb, sdlClip::ClipCleanCb, this, cmimes.data(),
363 cmimes.size());
364 }
365
366 if (ev.timestamp == _last_timestamp)
367 return true;
368
369 if (contains(mime_types, WINPR_ASSERTING_INT_CAST(Sint32, nformats)))
370 return true;
371
372
373
374
375
376
377 if (ownsClipboard())
378 {
379 WLog_Print(_log, WLOG_DEBUG, "ignoring stale local clipboard update");
380 return true;
381 }
382
383 {
384 ClipboardLockGuard systemlock(_system);
385 std::scoped_lock lock(_lock);
386 clearServerFormats();
387 }
388
389 const std::string mime_html = s_mime_html;
390
391 const std::vector<std::string> mime_bitmap = { BMP_MIME_LIST };
392 const std::string mime_webp = s_mime_webp;
393 const std::string mime_png = s_mime_png;
394 const std::string mime_jpeg = s_mime_jpg;
395 const std::string mime_tiff = s_mime_tiff;
396 const std::vector<std::string> mime_images = { mime_webp, mime_png, mime_jpeg,
397 mime_tiff, s_mime_avif, s_mime_jxl };
398
399 std::vector<std::string> clientFormatNames;
400 std::vector<CLIPRDR_FORMAT> clientFormats;
401
402 const char** clipboard_mime_formats = mime_types;
403
404 WLog_Print(_log, WLOG_TRACE, "SDL has %" PRIuz " formats", nformats);
405
406 bool textPushed = false;
407 bool imgPushed = false;
408 bool filePushed = false;
409
410 for (size_t i = 0; i < nformats; i++)
411 {
412 std::string local_mime = clipboard_mime_formats[i];
413 WLog_Print(_log, WLOG_TRACE, " - %s", local_mime.c_str());
414
415 if (std::find(s_mime_text().begin(), s_mime_text().end(), local_mime) !=
416 s_mime_text().end())
417 {
418
419 if (!textPushed)
420 {
421 clientFormats.push_back({ CF_TEXT, nullptr });
422 clientFormats.push_back({ CF_OEMTEXT, nullptr });
423 clientFormats.push_back({ CF_UNICODETEXT, nullptr });
424 textPushed = true;
425 }
426 }
427 else if (local_mime == mime_html)
428
429 clientFormatNames.emplace_back(s_type_HtmlFormat);
430 else if ((std::find(mime_bitmap.begin(), mime_bitmap.end(), local_mime) !=
431 mime_bitmap.end()) ||
432 (std::find(mime_images.begin(), mime_images.end(), local_mime) !=
433 mime_images.end()))
434 {
435
436 if (!imgPushed)
437 {
438 clientFormats.push_back({ CF_DIB, nullptr });
439#if defined(WINPR_UTILS_IMAGE_DIBv5)
440 clientFormats.push_back({ CF_DIBV5, nullptr });
441#endif
442
443 if (winpr_image_format_is_supported(WINPR_IMAGE_BITMAP))
444 {
445 for (auto& bmp : mime_bitmap)
446 clientFormatNames.emplace_back(bmp);
447 }
448
449 if (winpr_image_format_is_supported(WINPR_IMAGE_JPEG))
450 clientFormatNames.emplace_back(mime_jpeg);
451 if (winpr_image_format_is_supported(WINPR_IMAGE_WEBP))
452 clientFormatNames.emplace_back(mime_webp);
453 if (winpr_image_format_is_supported(WINPR_IMAGE_PNG))
454 clientFormatNames.emplace_back(mime_png);
455
456 clientFormatNames.emplace_back(s_type_HtmlFormat);
457 imgPushed = true;
458 }
459 clientFormatNames.emplace_back(local_mime);
460 }
461 else if (mime_is_file(local_mime) || (local_mime == s_type_file_url))
462 {
463 if (!filePushed)
464 {
465 clientFormatNames.emplace_back(s_type_FileGroupDescriptorW);
466 filePushed = true;
467 }
468 }
469 }
470
471 std::sort(clientFormatNames.begin(), clientFormatNames.end());
472 clientFormatNames.erase(std::unique(clientFormatNames.begin(), clientFormatNames.end()),
473 clientFormatNames.end());
474
475 for (auto& name : clientFormatNames)
476 {
477 clientFormats.push_back({ ClipboardRegisterFormat(_system, name.c_str()), name.data() });
478 }
479
480 std::sort(clientFormats.begin(), clientFormats.end(),
481 [](const auto& a, const auto& b) { return a < b; });
482 auto u = std::unique(clientFormats.begin(), clientFormats.end());
483 clientFormats.erase(u, clientFormats.end());
484
486 { CB_FORMAT_LIST, 0, 0 },
487 static_cast<UINT32>(clientFormats.size()),
488 clientFormats.data(),
489 };
490
491 WLog_Print(_log, WLOG_TRACE,
492 "-------------- client format list [%" PRIu32 "] ------------------",
493 formatList.numFormats);
494 for (UINT32 x = 0; x < formatList.numFormats; x++)
495 {
496 auto format = &formatList.formats[x];
497 WLog_Print(_log, WLOG_TRACE, "client announces %" PRIu32 " [%s][%s]", format->formatId,
498 ClipboardGetFormatIdString(format->formatId), format->formatName);
499 }
500
501
502 ClipboardLockGuard systemlock(_system);
503 std::scoped_lock lock(_lock);
504 if (!_ctx)
505 return true;
506
507 if (cliprdr_file_context_notify_new_client_format_list(_file) != CHANNEL_RC_OK)
508 return false;
509
510 WINPR_ASSERT(_ctx);
511 WINPR_ASSERT(_ctx->ClientFormatList);
512 return _ctx->ClientFormatList(_ctx, &formatList) == CHANNEL_RC_OK;
513}