FreeRDP
Loading...
Searching...
No Matches
sdlClip Class Reference

object that handles clipboard context for the SDL3 client More...

#include <sdl_clip.hpp>

Public Member Functions

 sdlClip (SdlContext *sdl)
 
 sdlClip (const sdlClip &)=delete
 
 sdlClip (sdlClip &&)=delete
 
sdlClip & operator= (const sdlClip &)=delete
 
sdlClip & operator= (sdlClip &&)=delete
 
bool init (CliprdrClientContext *clip)
 
bool uninit (CliprdrClientContext *clip)
 
bool handleEvent (const SDL_ClipboardEvent &ev)
 
void handleDataRequests ()
 

Detailed Description

object that handles clipboard context for the SDL3 client

Definition at line 76 of file sdl_clip.hpp.

Constructor & Destructor Documentation

◆ sdlClip()

sdlClip::sdlClip ( SdlContext *  sdl)
explicit

Definition at line 226 of file sdl_clip.cpp.

227 : _sdl(sdl), _file(cliprdr_file_context_new(this)), _log(WLog_Get(TAG)),
228 _system(ClipboardCreate()), _event(CreateEventA(nullptr, TRUE, FALSE, nullptr)),
229 _uuid(sdl::utils::generate_uuid_v4())
230{
231 WINPR_ASSERT(sdl);
232
233 std::stringstream ss;
234 ss << s_mime_freerdp_update << "-" << _uuid;
235 _mime_uuid = ss.str();
236
237 std::ignore = cliprdr_file_context_set_locally_available(_file, TRUE);
238}

◆ ~sdlClip()

sdlClip::~sdlClip ( )
virtual

Definition at line 240 of file sdl_clip.cpp.

241{
242#if defined(__EMSCRIPTEN__)
243 {
244 std::scoped_lock web(s_web_lock);
245 if (s_web_clip == this)
246 s_web_clip = nullptr;
247 }
248#endif
249 cliprdr_file_context_free(_file);
250 ClipboardDestroy(_system);
251 std::ignore = CloseHandle(_event);
252}

Member Function Documentation

◆ handleDataRequests()

void sdlClip::handleDataRequests ( )

Definition at line 1085 of file sdl_clip.cpp.

1086{
1087 for (;;)
1088 {
1089 uint32_t formatId = 0;
1090 {
1091 std::scoped_lock lock(_lock);
1092 if (_server_requests.empty())
1093 return;
1094 formatId = _server_requests.front();
1095 _server_requests.pop_front();
1096 if (!_ctx)
1097 continue;
1098 }
1099
1100 uint32_t len = 0;
1101 auto data = getLocalData(formatId, len);
1102 std::scoped_lock lock(_lock);
1103 if (!_ctx)
1104 continue;
1105 if (SendDataResponse(data.get(), len) != CHANNEL_RC_OK)
1106 WLog_Print(_log, WLOG_WARN, "failed to send clipboard data response");
1107 }
1108}

◆ handleEvent()

bool sdlClip::handleEvent ( const SDL_ClipboardEvent &  ev)

Definition at line 313 of file sdl_clip.cpp.

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 /* The channel thread replaces _current_mimetypes on every server format list, so take
338 * them under the lock instead of trusting pointers carried in the event. */
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 /* Server files need native clipboard items on some platforms (macOS), which only the
356 * file context can offer. It requests the data with ClipDataCb like SDL does. */
357 if (cliprdr_file_context_set_local_clipboard_data(_file, sdlClip::ClipDataCb, this,
358 cmimes.data(), cmimes.size()))
359 return true;
360
361 /* SDL copies the mime type strings */
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 /* SDL holds this session's server data right now, so this update was queued before our
373 * later SDL_SetClipboardData took effect: typically the selection a compositor re-sends on
374 * focus-in, racing a server format list. Announcing it would replace the fresh server
375 * clipboard with the stale local one (and the server would then ask us for data we no
376 * longer own). A genuine local change cancels our data before its update is queued. */
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 /* text formats */
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 /* html */
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 /* image formats */
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
485 const CLIPRDR_FORMAT_LIST formatList = {
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 /* The RDP thread may have closed the channel since the check above (see uninit) */
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}

◆ init()

bool sdlClip::init ( CliprdrClientContext *  clip)

Definition at line 254 of file sdl_clip.cpp.

255{
256 WINPR_ASSERT(clip);
257 ClipboardLockGuard systemlock(_system);
258 std::scoped_lock lock(_lock);
259 _ctx = clip;
260 clip->custom = this;
261 _ctx->MonitorReady = sdlClip::MonitorReady;
262 _ctx->ServerCapabilities = sdlClip::ReceiveServerCapabilities;
263 _ctx->ServerFormatList = sdlClip::ReceiveServerFormatList;
264 _ctx->ServerFormatListResponse = sdlClip::ReceiveFormatListResponse;
265 _ctx->ServerFormatDataRequest = sdlClip::ReceiveFormatDataRequest;
266 _ctx->ServerFormatDataResponse = sdlClip::ReceiveFormatDataResponse;
267#if defined(__EMSCRIPTEN__)
268 {
269 std::scoped_lock web(s_web_lock);
270 s_web_clip = this;
271 }
272#endif
273
274 return cliprdr_file_context_init(_file, _ctx);
275}

◆ uninit()

bool sdlClip::uninit ( CliprdrClientContext *  clip)

Definition at line 277 of file sdl_clip.cpp.

278{
279 WINPR_ASSERT(clip);
280#if defined(__EMSCRIPTEN__)
281 {
282 std::scoped_lock web(s_web_lock);
283 if (s_web_clip == this)
284 s_web_clip = nullptr;
285 }
286#endif
287 ClipboardLockGuard systemlock(_system);
288 std::scoped_lock lock(_lock);
289 if (!cliprdr_file_context_uninit(_file, _ctx))
290 return false;
291 _ctx = nullptr;
292 clip->custom = nullptr;
293 _server_requests.clear();
294 return true;
295}

The documentation for this class was generated from the following files: