41 #include <freerdp/freerdp.h> 42 #include <freerdp/channels/channels.h> 43 #include <freerdp/client/cliprdr.h> 46 #define CLIPBOARD_TRANSFER_WAIT_TIME 6 52 gchar *name = gdk_atom_name(atom);
54 if (g_strcmp0(
"UTF8_STRING", name) == 0 || g_strcmp0(
"text/plain;charset=utf-8", name) == 0)
56 if (g_strcmp0(
"TEXT", name) == 0 || g_strcmp0(
"text/plain", name) == 0)
58 if (g_strcmp0(
"text/html", name) == 0)
60 if (g_strcmp0(
"image/png", name) == 0)
62 if (g_strcmp0(
"image/jpeg", name) == 0)
64 if (g_strcmp0(
"image/bmp", name) == 0)
66 if (g_strcmp0(
"text/uri-list", name) == 0)
67 rc = CB_FORMAT_TEXTURILIST;
78 *formats = (UINT32 *)malloc(
sizeof(UINT32) * (count + 1));
81 for (i = 0; i < count; i++) {
84 (*formats)[*size] = format;
89 *formats = realloc(*formats,
sizeof(UINT32) * (*size));
92 static UINT8 *
lf2crlf(UINT8 *data,
int *size)
102 out_size = (*size) * 2 + 1;
103 outbuf = (UINT8 *)malloc(out_size);
106 in_end = data + (*size);
108 while (in < in_end) {
119 *size = out - outbuf;
124 static void crlf2lf(UINT8 *data,
size_t *size)
126 TRACE_CALL(__func__);
134 in_end = data + (*size);
136 while (in < in_end) {
148 TRACE_CALL(__func__);
155 TRACE_CALL(__func__);
161 TRACE_CALL(__func__);
165 CLIPRDR_FORMAT_LIST *pFormatList;
186 TRACE_CALL(__func__);
187 CLIPRDR_CAPABILITIES capabilities;
188 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
190 capabilities.cCapabilitiesSets = 1;
191 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet);
193 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
194 generalCapabilitySet.capabilitySetLength = 12;
196 generalCapabilitySet.version = CB_CAPS_VERSION_2;
197 generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
199 clipboard->
context->ClientCapabilities(clipboard->
context, &capabilities);
204 TRACE_CALL(__func__);
212 return CHANNEL_RC_OK;
217 TRACE_CALL(__func__);
218 return CHANNEL_RC_OK;
224 TRACE_CALL(__func__);
230 if (fmt == CB_FORMAT_PNG || fmt == CF_DIB || fmt == CF_DIBV5 || fmt == CB_FORMAT_JPEG) {
231 g_object_unref(clipboard->
srv_data);
243 TRACE_CALL(__func__);
252 CLIPRDR_FORMAT *format;
253 CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
256 int has_dib_level = 0;
263 REMMINA_PLUGIN_DEBUG(
"gp=%p: Received a new ServerFormatList from server clipboard. Remmina version = %s",
266 GtkTargetList *list = gtk_target_list_new(NULL, 0);
269 REMMINA_PLUGIN_DEBUG(
"gp=%p: we already have a FormatDataRequest in progress to the server, aborting", gp);
276 REMMINA_PLUGIN_DEBUG(
"gp=%p: format list from the server:", gp);
277 for (i = 0; i < formatList->numFormats; i++) {
278 format = &formatList->formats[i];
279 const char *serverFormatName = format->formatName;
280 gchar *gtkFormatName = NULL;
281 if (format->formatId == CF_UNICODETEXT) {
282 serverFormatName =
"CF_UNICODETEXT";
283 gtkFormatName =
"text/plain;charset=utf-8";
284 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
285 gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
287 atom = gdk_atom_intern(
"UTF8_STRING", TRUE);
288 gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
289 }
else if (format->formatId == CF_TEXT) {
290 serverFormatName =
"CF_TEXT";
291 gtkFormatName =
"text/plain";
292 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
293 gtk_target_list_add(list, atom, 0, CF_TEXT);
295 atom = gdk_atom_intern(
"TEXT", TRUE);
296 gtk_target_list_add(list, atom, 0, CF_TEXT);
297 }
else if (format->formatId == CF_DIB) {
298 serverFormatName =
"CF_DIB";
299 if (has_dib_level < 1)
301 }
else if (format->formatId == CF_DIBV5) {
302 serverFormatName =
"CF_DIBV5";
303 if (has_dib_level < 5)
305 }
else if (format->formatId == CB_FORMAT_JPEG) {
306 serverFormatName =
"CB_FORMAT_JPEG";
307 gtkFormatName =
"image/jpeg";
308 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
309 gtk_target_list_add(list, atom, 0, CB_FORMAT_JPEG);
310 }
else if (format->formatId == CB_FORMAT_PNG) {
311 serverFormatName =
"CB_FORMAT_PNG";
312 gtkFormatName =
"image/png";
313 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
314 gtk_target_list_add(list, atom, 0, CB_FORMAT_PNG);
315 }
else if (format->formatId == CB_FORMAT_HTML) {
316 serverFormatName =
"CB_FORMAT_HTML";
317 gtkFormatName =
"text/html";
318 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
319 gtk_target_list_add(list, atom, 0, CB_FORMAT_HTML);
320 }
else if (format->formatId == CB_FORMAT_TEXTURILIST) {
321 serverFormatName =
"CB_FORMAT_TEXTURILIST";
322 gtkFormatName =
"text/uri-list";
323 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
324 gtk_target_list_add(list, atom, 0, CB_FORMAT_TEXTURILIST);
325 }
else if (format->formatId == CF_LOCALE) {
326 serverFormatName =
"CF_LOCALE";
327 }
else if (format->formatId == CF_METAFILEPICT) {
328 serverFormatName =
"CF_METAFILEPICT";
329 }
else if (serverFormatName != NULL && strcmp(serverFormatName,
"HTML Format") == 0) {
330 gtkFormatName =
"text/html";
331 GdkAtom atom = gdk_atom_intern(gtkFormatName, TRUE);
332 gtk_target_list_add(list, atom, 0, format->formatId);
335 REMMINA_PLUGIN_DEBUG(
"the server has clipboard format %d: %s -> GTK %s", format->formatId, serverFormatName, gtkFormatName);
340 GdkAtom atom = gdk_atom_intern(
"image/bmp", TRUE);
341 if (has_dib_level == 5)
342 gtk_target_list_add(list, atom, 0, CF_DIBV5);
344 gtk_target_list_add(list, atom, 0, CF_DIB);
348 REMMINA_PLUGIN_DEBUG(
"gp=%p: sending ClientFormatListResponse to server", gp);
349 formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
350 formatListResponse.msgFlags = CB_RESPONSE_OK;
351 formatListResponse.dataLen = 0;
352 rc = clipboard->
context->ClientFormatListResponse(clipboard->
context, &formatListResponse);
364 GtkTargetEntry *target_table = gtk_target_table_new_from_list(list, &n_targets);
366 gtk_target_table_free(target_table, n_targets);
367 if (n_targets == 0) {
368 REMMINA_PLUGIN_DEBUG(
"gp=%p adding a dummy text target (empty text) for local clipboard, because we have no interesting targets from the server. Putting it in the local clipboard cache.");
369 GdkAtom atom = gdk_atom_intern(
"text/plain;charset=utf-8", TRUE);
370 gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
373 ((
char *)(clipboard->
srv_data))[0] = 0;
375 clipboard->
format = CF_UNICODETEXT;
386 REMMINA_PLUGIN_DEBUG(
"gp=%p: processing of ServerFormatList ended, returning rc=%u to libfreerdp", gp, rc);
392 TRACE_CALL(__func__);
393 return CHANNEL_RC_OK;
398 TRACE_CALL(__func__);
414 return CHANNEL_RC_OK;
420 int64_t ms_end = (end->tv_sec * 1000) + (end->tv_usec / 1000);
421 int64_t ms_start = (start->tv_sec * 1000) + (start->tv_usec / 1000);
423 return (
int)(ms_end - ms_start);
429 TRACE_CALL(__func__);
436 gpointer output = NULL;
442 rfi = GET_PLUGIN_DATA(gp);
444 data = formatDataResponse->requestedFormatData;
445 size = formatDataResponse->dataLen;
447 REMMINA_PLUGIN_DEBUG(
"gp=%p server FormatDataResponse received: clipboard data arrived form server.", gp);
448 gettimeofday(&now, NULL);
453 REMMINA_PLUGIN_DEBUG(
"gp=%p %zu bytes transferred from server in %d ms. Speed is %d bytes/sec",
454 gp, (
size_t)size, mstrans, mstrans != 0 ? (
int)((int64_t)size * 1000 / mstrans) : 0);
460 size = ConvertFromUnicode(CP_UTF8, 0, (WCHAR *)data, size / 2, (CHAR **)&output, 0, NULL, NULL);
468 output = (gpointer)calloc(1, size + 1);
470 memcpy(output, data, size);
482 BITMAPINFOHEADER *pbi;
483 BITMAPV5HEADER *pbi5;
485 pbi = (BITMAPINFOHEADER *)data;
488 offset = 14 + pbi->biSize;
489 if (pbi->biClrUsed != 0)
490 offset +=
sizeof(RGBQUAD) * pbi->biClrUsed;
491 else if (pbi->biBitCount <= 8)
492 offset +=
sizeof(RGBQUAD) * (1 << pbi->biBitCount);
493 if (pbi->biSize ==
sizeof(BITMAPINFOHEADER)) {
494 if (pbi->biCompression == 3)
496 }
else if (pbi->biSize >=
sizeof(BITMAPV5HEADER)) {
497 pbi5 = (BITMAPV5HEADER *)pbi;
498 if (pbi5->bV5ProfileData <= offset)
499 offset += pbi5->bV5ProfileSize;
501 s = Stream_New(NULL, 14 + size);
502 Stream_Write_UINT8(s,
'B');
503 Stream_Write_UINT8(s,
'M');
504 Stream_Write_UINT32(s, 14 + size);
505 Stream_Write_UINT32(s, 0);
506 Stream_Write_UINT32(s, offset);
507 Stream_Write(s, data, size);
509 data = Stream_Buffer(s);
510 size = Stream_Length(s);
512 GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
514 if (!gdk_pixbuf_loader_write(loader, data, size, &perr)) {
515 Stream_Free(s, TRUE);
516 g_warning(
"[RDP] rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message);
518 if (!gdk_pixbuf_loader_close(loader, &perr)) {
519 g_warning(
"[RDP] rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message);
522 Stream_Free(s, TRUE);
523 output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
525 g_object_unref(loader);
532 GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
533 gdk_pixbuf_loader_write(loader, data, size, NULL);
534 output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(loader));
535 gdk_pixbuf_loader_close(loader, NULL);
536 g_object_unref(loader);
544 int p = 0, lstart = 0;
548 while(p < size && (c = data[p]) != 0) {
549 if (c ==
'<' && p == lstart)
break;
550 if (c ==
'\n') lstart = p + 1;
554 osize = size - lstart;
555 output = (gpointer)calloc(1, osize + 1);
557 memcpy(output, data + lstart, osize);
558 ((
char *)output)[osize] = 0;
571 REMMINA_PLUGIN_DEBUG(
"gp=%p: clipboard local cache data has been loaded", gp);
573 REMMINA_PLUGIN_DEBUG(
"gp=%p: data from server is not valid (size=%zu format=%d), cannot load into cache", gp, size, rfi->
clipboard.
format);
576 REMMINA_PLUGIN_DEBUG(
"gp=%p: signalling main GTK thread that we have some clipboard data.", gp);
581 REMMINA_PLUGIN_DEBUG(
"gp=%p: clipboard transfer from server completed.", gp);
585 REMMINA_PLUGIN_DEBUG(
"gp=%p: clipboard transfer from server completed, but no local application is requesting it. Data is on local cache now, try to paste later.", gp);
590 return CHANNEL_RC_OK;
596 TRACE_CALL(__func__);
601 CLIPRDR_FORMAT_DATA_REQUEST *pFormatDataRequest;
608 time_t tlimit, tlimit1s, tnow, tstart;
610 REMMINA_PLUGIN_DEBUG(
"gp=%p: A local application has requested remote clipboard data for remote format id %d", gp, info);
614 g_message(
"[RDP] Cannot paste now, I’m already transferring clipboard data from server. Try again later\n");
626 pFormatDataRequest = (CLIPRDR_FORMAT_DATA_REQUEST *)malloc(
sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
627 ZeroMemory(pFormatDataRequest,
sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
628 pFormatDataRequest->requestedFormatId = clipboard->
format;
632 REMMINA_PLUGIN_DEBUG(
"gp=%p Requesting clipboard data with format %d from the server via ServerFormatDataRequest", gp, clipboard->
format);
641 tlimit = tstart + CLIPBOARD_TRANSFER_WAIT_TIME;
643 tlimit1s = tstart + 1;
644 while ((tnow = time(NULL)) < tlimit && rc != 0 && clipboard->
srv_clip_data_wait == SCDW_BUSY_WAIT) {
646 if (tnow >= tlimit1s) {
647 REMMINA_PLUGIN_DEBUG(
"gp=%p, clipboard data is still not here after %u seconds", gp, (
unsigned)(tnow - tstart));
648 tlimit1s = time(NULL) + 1;
651 gettimeofday(&tv, NULL);
652 to.tv_sec = tv.tv_sec;
653 to.tv_nsec = tv.tv_usec * 1000 + 5000000;
654 if (to.tv_nsec >= 1000000000) {
655 to.tv_nsec -= 1000000000;
662 gtk_main_iteration_do(FALSE);
668 g_warning(
"[RDP] gp=%p Clipboard data wait aborted.",gp);
671 g_warning(
"[RDP] gp=%p Clipboard data from the server is not available in %d seconds. No data will be available to user.",
672 gp, CLIPBOARD_TRANSFER_WAIT_TIME);
674 g_warning(
"[RDP] gp=%p internal error: pthread_cond_timedwait() returned %d\n", gp, rc);
683 REMMINA_PLUGIN_DEBUG(
"gp=%p pasting data to local application", gp);
685 if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG) {
686 gtk_selection_data_set_pixbuf(selection_data, clipboard->
srv_data);
688 REMMINA_PLUGIN_DEBUG(
"gp=%p returning %zu bytes of HTML in clipboard to requesting application", gp, strlen(clipboard->
srv_data));
689 GdkAtom atom = gdk_atom_intern(
"text/html", TRUE);
690 gtk_selection_data_set(selection_data, atom, 8, clipboard->
srv_data, strlen(clipboard->
srv_data));
692 REMMINA_PLUGIN_DEBUG(
"gp=%p returning %zu bytes of text in clipboard to requesting application", gp, strlen(clipboard->
srv_data));
693 gtk_selection_data_set_text(selection_data, clipboard->
srv_data, -1);
697 REMMINA_PLUGIN_DEBUG(
"gp=%p cannot paste data to local application because ->srv_data is NULL", gp);
705 TRACE_CALL(__func__);
711 TRACE_CALL(__func__);
713 GtkClipboard *gtkClipboard;
717 gint loccount, srvcount;
719 CLIPRDR_FORMAT *formats;
721 CLIPRDR_FORMAT_LIST pFormatList;
722 CLIPRDR_FORMAT formats[];
729 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
731 result = gtk_clipboard_wait_for_targets(gtkClipboard, &targets, &loccount);
732 REMMINA_PLUGIN_DEBUG(
"gp=%p sending to server the following local clipboard content formats", gp);
733 if (result && loccount > 0) {
734 formats = (CLIPRDR_FORMAT *)malloc(loccount *
sizeof(CLIPRDR_FORMAT));
736 for (i = 0; i < loccount; i++) {
739 gchar *name = gdk_atom_name(targets[i]);
740 REMMINA_PLUGIN_DEBUG(
" local clipboard format %s will be sent to remote as %d", name, formatId);
742 formats[srvcount].formatId = formatId;
743 formats[srvcount].formatName = NULL;
748 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t) +
sizeof(CLIPRDR_FORMAT) * srvcount);
749 retp->pFormatList.formats = retp->formats;
750 retp->pFormatList.numFormats = srvcount;
751 memcpy(retp->formats, formats,
sizeof(CLIPRDR_FORMAT) * srvcount);
753 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t));
754 retp->pFormatList.formats = NULL;
755 retp->pFormatList.numFormats = 0;
759 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t) +
sizeof(CLIPRDR_FORMAT));
760 retp->pFormatList.formats = NULL;
761 retp->pFormatList.numFormats = 0;
767 retp->pFormatList.msgType = CB_FORMAT_LIST;
768 retp->pFormatList.msgFlags = 0;
770 return (CLIPRDR_FORMAT_LIST *)retp;
775 TRACE_CALL(__func__);
781 TRACE_CALL(__func__);
782 GtkClipboard *gtkClipboard;
784 UINT8 *outbuf = NULL;
785 GdkPixbuf *image = NULL;
790 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
797 inbuf = (UINT8 *)gtk_clipboard_wait_for_text(gtkClipboard);
806 image = gtk_clipboard_wait_for_image(gtkClipboard);
813 if (inbuf != NULL || image != NULL) {
818 size = strlen((
char *)inbuf);
819 outbuf =
lf2crlf(inbuf, &size);
824 size = strlen((
char *)inbuf);
826 size = (ConvertToUnicode(CP_UTF8, 0, (CHAR *)inbuf, -1, (WCHAR **)&outbuf, 0)) *
sizeof(WCHAR);
834 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"png", NULL, NULL);
835 outbuf = (UINT8 *)malloc(buffersize);
836 memcpy(outbuf, data, buffersize);
838 g_object_unref(image);
845 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"jpeg", NULL, NULL);
846 outbuf = (UINT8 *)malloc(buffersize);
847 memcpy(outbuf, data, buffersize);
849 g_object_unref(image);
857 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"bmp", NULL, NULL);
858 size = buffersize - 14;
859 outbuf = (UINT8 *)malloc(size);
860 memcpy(outbuf, data + 14, size);
861 g_object_unref(image);
875 TRACE_CALL(__func__);
876 GtkClipboard *gtkClipboard;
880 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
882 GtkTargetEntry *targets = gtk_target_table_new_from_list(ui->
clipboard.targetlist, &n_targets);
887 g_warning(
"[RDP] internal error: no targets to insert into the local clipboard");
890 REMMINA_PLUGIN_DEBUG(
"setting clipboard with owner to me: %p", gp);
891 gtk_clipboard_set_with_owner(gtkClipboard, targets, n_targets,
894 gtk_target_table_free(targets, n_targets);
902 TRACE_CALL(__func__);
904 GtkClipboard *gtkClipboard;
908 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
909 if (gtkClipboard && gtk_clipboard_get_owner(gtkClipboard) == (GObject *)gp)
910 gtk_clipboard_clear(gtkClipboard);
916 TRACE_CALL(__func__);
936 TRACE_CALL(__func__);
941 TRACE_CALL(__func__);
949 TRACE_CALL(__func__);
951 REMMINA_PLUGIN_DEBUG(
"requesting clipboard data transfer from server to be ignored and busywait loop to exit");
960 TRACE_CALL(__func__);
966 cliprdr->custom = (
void *)clipboard;
CliprdrClientContext * context
static UINT remmina_rdp_cliprdr_server_format_list_response(CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse)
int remmina_rdp_cliprdr_server_file_contents_response(CliprdrClientContext *context, CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse)
void remmina_rdp_cliprdr_send_client_format_list(RemminaProtocolWidget *gp)
static UINT8 * lf2crlf(UINT8 *data, int *size)
void remmina_rdp_cliprdr_init(rfContext *rfi, CliprdrClientContext *cliprdr)
pthread_mutex_t transfer_clip_mutex
void remmina_rdp_cliprdr_detach_owner(RemminaProtocolWidget *gp)
int timeval_diff(struct timeval *start, struct timeval *end)
pthread_mutex_t srv_data_mutex
void remmina_rdp_clipboard_abort_client_format_data_request(rfContext *rfi)
void * remmina_rdp_event_queue_ui_sync_retptr(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
static UINT remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest)
static void crlf2lf(UINT8 *data, size_t *size)
void remmina_rdp_clipboard_init(rfContext *rfi)
RemminaProtocolWidget * protocol_widget
void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionData *selection_data, guint info, RemminaProtocolWidget *gp)
struct remmina_plugin_rdp_event::@42::@47 clipboard_formatdataresponse
void remmina_rdp_event_process_clipboard(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
RemminaPluginRdpEventType type
void remmina_rdp_cliprdr_set_clipboard_data(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
static UINT remmina_rdp_cliprdr_monitor_ready(CliprdrClientContext *context, const CLIPRDR_MONITOR_READY *monitorReady)
pthread_cond_t transfer_clip_cond
static void remmina_rdp_cliprdr_mt_get_format_list(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
int remmina_rdp_cliprdr_server_file_contents_request(CliprdrClientContext *context, CLIPRDR_FILE_CONTENTS_REQUEST *fileContentsRequest)
static void remmina_rdp_cliprdr_send_client_capabilities(rfClipboard *clipboard)
void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
struct remmina_plugin_rdp_event::@42::@46 clipboard_formatlist
void remmina_rdp_cliprdr_empty_clipboard(GtkClipboard *gtkClipboard, rfClipboard *clipboard)
CLIPRDR_FORMAT_LIST * remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget *gp)
void remmina_rdp_cliprdr_cached_clipboard_free(rfClipboard *clipboard)
struct remmina_plugin_rdp_event::@42::@48 clipboard_formatdatarequest
void remmina_rdp_clipboard_free(rfContext *rfi)
void remmina_rdp_cliprdr_get_target_types(UINT32 **formats, UINT16 *size, GdkAtom *types, int count)
static UINT remmina_rdp_cliprdr_server_capabilities(CliprdrClientContext *context, const CLIPRDR_CAPABILITIES *capabilities)
static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST *formatList)
void remmina_rdp_cliprdr_get_clipboard_data(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
UINT32 server_html_format_id
void remmina_rdp_event_event_push(RemminaProtocolWidget *gp, const RemminaPluginRdpEvent *e)
static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse)
UINT32 remmina_rdp_cliprdr_get_format_from_gdkatom(GdkAtom atom)
struct timeval clientformatdatarequest_tv
enum rf_clipboard::@41 srv_clip_data_wait
RemminaPluginRdpUiType type