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;
77 *formats = (UINT32 *)malloc(
sizeof(UINT32) * (count + 1));
80 for (i = 0; i < count; i++) {
83 (*formats)[*size] = format;
88 *formats = realloc(*formats,
sizeof(UINT32) * (*size));
91 static UINT8 *
lf2crlf(UINT8 *data,
int *size)
101 out_size = (*size) * 2 + 1;
102 outbuf = (UINT8 *)malloc(out_size);
105 in_end = data + (*size);
107 while (in < in_end) {
118 *size = out - outbuf;
123 static void crlf2lf(UINT8 *data,
size_t *size)
125 TRACE_CALL(__func__);
133 in_end = data + (*size);
135 while (in < in_end) {
146 TRACE_CALL(__func__);
151 TRACE_CALL(__func__);
157 TRACE_CALL(__func__);
161 CLIPRDR_FORMAT_LIST *pFormatList;
182 TRACE_CALL(__func__);
183 CLIPRDR_CAPABILITIES capabilities;
184 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
186 capabilities.cCapabilitiesSets = 1;
187 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet);
189 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
190 generalCapabilitySet.capabilitySetLength = 12;
192 generalCapabilitySet.version = CB_CAPS_VERSION_2;
193 generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
195 clipboard->
context->ClientCapabilities(clipboard->
context, &capabilities);
201 TRACE_CALL(__func__);
209 return CHANNEL_RC_OK;
214 TRACE_CALL(__func__);
215 return CHANNEL_RC_OK;
221 TRACE_CALL(__func__);
230 CLIPRDR_FORMAT *format;
231 CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
232 const char *serverFormatName;
234 int has_dib_level = 0;
240 GtkTargetList *list = gtk_target_list_new(NULL, 0);
243 for (i = 0; i < formatList->numFormats; i++) {
244 format = &formatList->formats[i];
245 serverFormatName = format->formatName;
246 if (format->formatId == CF_UNICODETEXT) {
247 serverFormatName =
"CF_UNICODETEXT";
248 GdkAtom atom = gdk_atom_intern(
"UTF8_STRING", TRUE);
249 gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
250 }
else if (format->formatId == CF_TEXT) {
251 serverFormatName =
"CF_TEXT";
252 GdkAtom atom = gdk_atom_intern(
"TEXT", TRUE);
253 gtk_target_list_add(list, atom, 0, CF_TEXT);
254 }
else if (format->formatId == CF_DIB) {
255 serverFormatName =
"CF_DIB";
256 if (has_dib_level < 1)
258 }
else if (format->formatId == CF_DIBV5) {
259 serverFormatName =
"CF_DIBV5";
260 if (has_dib_level < 5)
262 }
else if (format->formatId == CB_FORMAT_JPEG) {
263 serverFormatName =
"CB_FORMAT_JPEG";
264 GdkAtom atom = gdk_atom_intern(
"image/jpeg", TRUE);
265 gtk_target_list_add(list, atom, 0, CB_FORMAT_JPEG);
266 }
else if (format->formatId == CB_FORMAT_PNG) {
267 serverFormatName =
"CB_FORMAT_PNG";
268 GdkAtom atom = gdk_atom_intern(
"image/png", TRUE);
269 gtk_target_list_add(list, atom, 0, CB_FORMAT_PNG);
270 }
else if (format->formatId == CB_FORMAT_HTML) {
271 serverFormatName =
"CB_FORMAT_HTML";
272 GdkAtom atom = gdk_atom_intern(
"text/html", TRUE);
273 gtk_target_list_add(list, atom, 0, CB_FORMAT_HTML);
274 }
else if (format->formatId == CB_FORMAT_TEXTURILIST) {
275 serverFormatName =
"CB_FORMAT_TEXTURILIST";
276 GdkAtom atom = gdk_atom_intern(
"text/uri-list", TRUE);
277 gtk_target_list_add(list, atom, 0, CB_FORMAT_TEXTURILIST);
278 }
else if (format->formatId == CF_LOCALE) {
279 serverFormatName =
"CF_LOCALE";
280 }
else if (format->formatId == CF_METAFILEPICT) {
281 serverFormatName =
"CF_METAFILEPICT";
288 GdkAtom atom = gdk_atom_intern(
"image/bmp", TRUE);
289 if (has_dib_level == 5)
290 gtk_target_list_add(list, atom, 0, CF_DIBV5);
292 gtk_target_list_add(list, atom, 0, CF_DIB);
308 formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
309 formatListResponse.msgFlags = CB_RESPONSE_OK;
310 formatListResponse.dataLen = 0;
312 return clipboard->
context->ClientFormatListResponse(clipboard->
context, &formatListResponse);
317 TRACE_CALL(__func__);
318 return CHANNEL_RC_OK;
324 TRACE_CALL(__func__);
340 return CHANNEL_RC_OK;
345 TRACE_CALL(__func__);
352 GdkPixbufLoader *pixbuf;
353 gpointer output = NULL;
357 rfi = GET_PLUGIN_DATA(gp);
359 data = formatDataResponse->requestedFormatData;
360 size = formatDataResponse->dataLen;
370 size = ConvertFromUnicode(CP_UTF8, 0, (WCHAR *)data, size / 2, (CHAR **)&output, 0, NULL, NULL);
378 output = (gpointer)calloc(1, size + 1);
380 memcpy(output, data, size);
392 BITMAPINFOHEADER *pbi;
393 BITMAPV5HEADER *pbi5;
395 pbi = (BITMAPINFOHEADER *)data;
398 offset = 14 + pbi->biSize;
399 if (pbi->biClrUsed != 0)
400 offset +=
sizeof(RGBQUAD) * pbi->biClrUsed;
401 else if (pbi->biBitCount <= 8)
402 offset +=
sizeof(RGBQUAD) * (1 << pbi->biBitCount);
403 if (pbi->biSize ==
sizeof(BITMAPINFOHEADER)) {
404 if (pbi->biCompression == 3)
406 }
else if (pbi->biSize >=
sizeof(BITMAPV5HEADER)) {
407 pbi5 = (BITMAPV5HEADER *)pbi;
408 if (pbi5->bV5ProfileData <= offset)
409 offset += pbi5->bV5ProfileSize;
411 s = Stream_New(NULL, 14 + size);
412 Stream_Write_UINT8(s,
'B');
413 Stream_Write_UINT8(s,
'M');
414 Stream_Write_UINT32(s, 14 + size);
415 Stream_Write_UINT32(s, 0);
416 Stream_Write_UINT32(s, offset);
417 Stream_Write(s, data, size);
419 data = Stream_Buffer(s);
420 size = Stream_Length(s);
422 pixbuf = gdk_pixbuf_loader_new();
424 if (!gdk_pixbuf_loader_write(pixbuf, data, size, &perr)) {
425 Stream_Free(s, TRUE);
426 g_warning(
"[RDP] rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message);
428 if (!gdk_pixbuf_loader_close(pixbuf, &perr)) {
429 g_warning(
"[RDP] rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message);
432 Stream_Free(s, TRUE);
433 output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf));
435 g_object_unref(pixbuf);
442 pixbuf = gdk_pixbuf_loader_new();
443 gdk_pixbuf_loader_write(pixbuf, data, size, NULL);
444 output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf));
445 gdk_pixbuf_loader_close(pixbuf, NULL);
446 g_object_unref(pixbuf);
465 return CHANNEL_RC_OK;
470 TRACE_CALL(__func__);
475 CLIPRDR_FORMAT_DATA_REQUEST *pFormatDataRequest;
488 g_message(
"[RDP] Cannot paste now, I’m already transferring clipboard data from server. Try again later\n");
498 pFormatDataRequest = (CLIPRDR_FORMAT_DATA_REQUEST *)malloc(
sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
499 ZeroMemory(pFormatDataRequest,
sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
500 pFormatDataRequest->requestedFormatId = clipboard->
format;
512 tlimit = time(NULL) + CLIPBOARD_TRANSFER_WAIT_TIME;
514 while (time(NULL) < tlimit && rc != 0 && clipboard->
srv_clip_data_wait == SCDW_BUSY_WAIT) {
515 gettimeofday(&tv, NULL);
516 to.tv_sec = tv.tv_sec;
517 to.tv_nsec = tv.tv_usec * 1000 + 40000000;
518 if (to.tv_nsec >= 1000000000) {
519 to.tv_nsec -= 1000000000;
525 gtk_main_iteration_do(FALSE);
531 if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG) {
532 gtk_selection_data_set_pixbuf(selection_data, clipboard->
srv_data);
533 g_object_unref(clipboard->
srv_data);
535 gtk_selection_data_set_text(selection_data, clipboard->
srv_data, -1);
542 g_warning(
"[RDP] Clipboard data wait aborted.");
545 g_warning(
"[RDP] Clipboard data from the server is not available in %d seconds. No data will be available to user.",
546 CLIPBOARD_TRANSFER_WAIT_TIME);
548 g_warning(
"[RDP] internal error: pthread_cond_timedwait() returned %d\n", rc);
557 TRACE_CALL(__func__);
563 TRACE_CALL(__func__);
565 GtkClipboard *gtkClipboard;
569 gint loccount, srvcount;
571 CLIPRDR_FORMAT *formats;
574 CLIPRDR_FORMAT_LIST pFormatList;
575 CLIPRDR_FORMAT formats[];
582 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
584 result = gtk_clipboard_wait_for_targets(gtkClipboard, &targets, &loccount);
586 if (result && loccount > 0) {
587 formats = (CLIPRDR_FORMAT *)malloc(loccount *
sizeof(CLIPRDR_FORMAT));
589 for (i = 0; i < loccount; i++) {
592 name = gdk_atom_name(targets[i]);
595 formats[srvcount].formatId = formatId;
596 formats[srvcount].formatName = NULL;
601 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t) +
sizeof(CLIPRDR_FORMAT) * srvcount);
602 retp->pFormatList.formats = retp->formats;
603 retp->pFormatList.numFormats = srvcount;
604 memcpy(retp->formats, formats,
sizeof(CLIPRDR_FORMAT) * srvcount);
606 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t));
607 retp->pFormatList.formats = NULL;
608 retp->pFormatList.numFormats = 0;
612 retp = (
struct retp_t *)malloc(
sizeof(
struct retp_t) +
sizeof(CLIPRDR_FORMAT));
613 retp->pFormatList.formats = NULL;
614 retp->pFormatList.numFormats = 0;
620 retp->pFormatList.msgFlags = CB_RESPONSE_OK;
622 return (CLIPRDR_FORMAT_LIST *)retp;
627 TRACE_CALL(__func__);
634 TRACE_CALL(__func__);
635 GtkClipboard *gtkClipboard;
637 UINT8 *outbuf = NULL;
638 GdkPixbuf *image = NULL;
643 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
650 inbuf = (UINT8 *)gtk_clipboard_wait_for_text(gtkClipboard);
659 image = gtk_clipboard_wait_for_image(gtkClipboard);
666 if (inbuf != NULL || image != NULL) {
671 size = strlen((
char *)inbuf);
672 outbuf =
lf2crlf(inbuf, &size);
677 size = strlen((
char *)inbuf);
679 size = (ConvertToUnicode(CP_UTF8, 0, (CHAR *)inbuf, -1, (WCHAR **)&outbuf, 0)) *
sizeof(WCHAR);
687 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"png", NULL, NULL);
688 outbuf = (UINT8 *)malloc(buffersize);
689 memcpy(outbuf, data, buffersize);
691 g_object_unref(image);
698 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"jpeg", NULL, NULL);
699 outbuf = (UINT8 *)malloc(buffersize);
700 memcpy(outbuf, data, buffersize);
702 g_object_unref(image);
710 gdk_pixbuf_save_to_buffer(image, &data, &buffersize,
"bmp", NULL, NULL);
711 size = buffersize - 14;
712 outbuf = (UINT8 *)malloc(size);
713 memcpy(outbuf, data + 14, size);
714 g_object_unref(image);
728 TRACE_CALL(__func__);
729 GtkClipboard *gtkClipboard;
732 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
734 gtk_clipboard_set_image(gtkClipboard, ui->
clipboard.data);
737 gtk_clipboard_set_text(gtkClipboard, ui->
clipboard.data, -1);
744 TRACE_CALL(__func__);
745 GtkClipboard *gtkClipboard;
746 GtkTargetEntry *targets;
750 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
752 targets = gtk_target_table_new_from_list(ui->
clipboard.targetlist, &n_targets);
755 gtk_clipboard_set_with_owner(gtkClipboard, targets, n_targets,
758 gtk_target_table_free(targets, n_targets);
767 TRACE_CALL(__func__);
769 GtkClipboard *gtkClipboard;
773 gtkClipboard = gtk_widget_get_clipboard(rfi->
drawing_area, GDK_SELECTION_CLIPBOARD);
774 if (gtkClipboard && gtk_clipboard_get_owner(gtkClipboard) == (GObject *)gp)
775 gtk_clipboard_clear(gtkClipboard);
781 TRACE_CALL(__func__);
804 TRACE_CALL(__func__);
809 TRACE_CALL(__func__);
828 TRACE_CALL(__func__);
834 cliprdr->custom = (
void *)clipboard;
void remmina_rdp_cliprdr_set_clipboard_content(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
CliprdrClientContext * context
struct remmina_plugin_rdp_event::@40::@44 clipboard_formatlist
struct remmina_plugin_rdp_event::@40::@46 clipboard_formatdatarequest
void(* debug)(const gchar *fmt,...)
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)
enum rf_clipboard::@39 srv_clip_data_wait
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)
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)
void remmina_rdp_event_process_clipboard(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
RemminaPluginRdpEventType type
struct remmina_plugin_rdp_event::@40::@45 clipboard_formatdataresponse
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)
static RemminaPluginService * remmina_plugin_service
int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
void remmina_rdp_clipboard_abort_transfer(rfContext *rfi)
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_cliprdr_empty_clipboard(GtkClipboard *gtkClipboard, rfClipboard *clipboard)
CLIPRDR_FORMAT_LIST * remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget *gp)
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)
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)
RemminaPluginRdpUiType type