Skip to content

Commit

Permalink
Merge pull request #416 from giox069/next
Browse files Browse the repository at this point in the history
Clipboard: improved handling of time consuming clipboard transfer
  • Loading branch information
giox069 committed Dec 21, 2014
2 parents 6727424 + 3ae09ff commit 7b3219f
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 36 deletions.
153 changes: 120 additions & 33 deletions remmina-plugins/rdp/rdp_cliprdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
#include <freerdp/freerdp.h>
#include <freerdp/channels/channels.h>
#include <freerdp/client/cliprdr.h>
#include <sys/time.h>


#define CLIPBOARD_TRANSFER_WAIT_TIME 2

UINT32 remmina_rdp_cliprdr_get_format_from_gdkatom(GdkAtom atom)
{
Expand Down Expand Up @@ -350,32 +351,32 @@ static int remmina_rdp_cliprdr_server_format_list(CliprdrClientContext* context,
GdkAtom atom = gdk_atom_intern("UTF8_STRING", TRUE);
gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
}
if (format->formatId == CF_TEXT)
else if (format->formatId == CF_TEXT)
{
GdkAtom atom = gdk_atom_intern("TEXT", TRUE);
gtk_target_list_add(list, atom, 0, CF_TEXT);
}
if (format->formatId == CF_DIB)
else if (format->formatId == CF_DIB)
{
GdkAtom atom = gdk_atom_intern("image/bmp", TRUE);
gtk_target_list_add(list, atom, 0, CF_DIB);
}
if (format->formatId == CF_DIBV5)
else if (format->formatId == CF_DIBV5)
{
GdkAtom atom = gdk_atom_intern("image/bmp", TRUE);
gtk_target_list_add(list, atom, 0, CF_DIBV5);
}
if (format->formatId == CB_FORMAT_JPEG)
else if (format->formatId == CB_FORMAT_JPEG)
{
GdkAtom atom = gdk_atom_intern("image/jpeg", TRUE);
gtk_target_list_add(list, atom, 0, CB_FORMAT_JPEG);
}
if (format->formatId == CB_FORMAT_PNG)
else if (format->formatId == CB_FORMAT_PNG)
{
GdkAtom atom = gdk_atom_intern("image/png", TRUE);
gtk_target_list_add(list, atom, 0, CB_FORMAT_PNG);
}
if (format->formatId == CB_FORMAT_HTML)
else if (format->formatId == CB_FORMAT_HTML)
{
GdkAtom atom = gdk_atom_intern("text/html", TRUE);
gtk_target_list_add(list, atom, 0, CB_FORMAT_HTML);
Expand All @@ -400,7 +401,6 @@ static int remmina_rdp_cliprdr_server_format_list_response(CliprdrClientContext*

static int remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
{
// void remmina_rdp_cliprdr_process_data_request(RemminaProtocolWidget* gp, RDP_CB_DATA_REQUEST_EVENT* event)

RemminaPluginRdpUiObject* ui;
RemminaProtocolWidget* gp;
Expand All @@ -421,15 +421,14 @@ static int remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext*

static int remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
{
// void remmina_rdp_cliprdr_process_data_response(RemminaProtocolWidget* gp, RDP_CB_DATA_RESPONSE_EVENT* event)

UINT8* data;
size_t size;
rfContext* rfi;
RemminaProtocolWidget* gp;
rfClipboard* clipboard;
GdkPixbufLoader *pixbuf;
gpointer output = NULL;
RemminaPluginRdpUiObject *ui;

clipboard = (rfClipboard*)context->custom;
gp = clipboard->rfi->protocol_widget;
Expand All @@ -438,23 +437,29 @@ static int remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext*
data = formatDataResponse->requestedFormatData;
size = formatDataResponse->dataLen;

// formatDataResponse->requestedFormatData is allocated
// by freerdp and freed after returning from this callback function.
// So we must make a copy if we need to preserve it

if (size > 0)
{
switch (rfi->clipboard.format)
{
case CF_UNICODETEXT:
{
size = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)data, size / 2, (CHAR**)&data, 0, NULL, NULL);
crlf2lf(data, &size);
output = data;
size = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)data, size / 2, (CHAR**)&output, 0, NULL, NULL);
crlf2lf(output, &size);
break;
}

case CF_TEXT:
case CB_FORMAT_HTML:
{
crlf2lf(data, &size);
output = data;
output = (gpointer)calloc(1, size + 1);
if (output) {
memcpy(output, data, size);
crlf2lf(output, &size);
}
break;
}

Expand Down Expand Up @@ -497,11 +502,13 @@ static int remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext*
pixbuf = gdk_pixbuf_loader_new();
perr = NULL;
if ( !gdk_pixbuf_loader_write(pixbuf, data, size, &perr) ) {
g_print("rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message);
} else {
remmina_plugin_service->log_printf("[RDP] rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message);
}
else
{
if ( !gdk_pixbuf_loader_close(pixbuf, &perr) ) {
perr = NULL;
g_print("rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message);
remmina_plugin_service->log_printf("[RDP] rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message);
}
Stream_Free(s, TRUE);
output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf));
Expand All @@ -522,7 +529,29 @@ static int remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext*
}
}
}
g_async_queue_push(rfi->clipboard.clipboard_queue, output);

pthread_mutex_lock(&clipboard->transfer_clip_mutex);
pthread_cond_signal(&clipboard->transfer_clip_cond);
if ( clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT ) {
clipboard->srv_data = output;
}
else
{
// Clipboard data arrived from server when we are not busywaiting.
// Just put it on the local clipboard

ui = g_new0(RemminaPluginRdpUiObject, 1);
ui->type = REMMINA_RDP_UI_CLIPBOARD;
ui->clipboard.clipboard = clipboard;
ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT;
ui->clipboard.data = output;
ui->clipboard.format = clipboard->format;
rf_queue_ui(gp, ui);

clipboard->srv_clip_data_wait = SCDW_NONE;

}
pthread_mutex_unlock(&clipboard->transfer_clip_mutex);

return 1;
}
Expand All @@ -533,43 +562,75 @@ void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionDa
* We ask to the server the data we need */

GdkAtom target;
gpointer data;
CLIPRDR_FORMAT_DATA_REQUEST request;
rfClipboard* clipboard;
rfContext* rfi;
struct timespec to;
struct timeval tv;
int rc;


rfi = GET_DATA(gp);
clipboard = &(rfi->clipboard);

if ( clipboard->srv_clip_data_wait != SCDW_NONE ) {
remmina_plugin_service->log_printf("[RDP] Cannot paste now, I'm transferring clipboard data from server. Try again later\n");
return;
}


target = gtk_selection_data_get_target(selection_data);
// clipboard->format = remmina_rdp_cliprdr_get_format_from_gdkatom(target);
clipboard->format = info;
clipboard->clipboard_queue = g_async_queue_new();

/* Request Clipboard data of the server */

/* Request Clipboard content from the server */
ZeroMemory(&request, sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
request.requestedFormatId = clipboard->format;
request.msgFlags = CB_RESPONSE_OK;
request.msgType = CB_FORMAT_DATA_REQUEST;


pthread_mutex_lock(&clipboard->transfer_clip_mutex);

clipboard->srv_clip_data_wait = SCDW_BUSY_WAIT;
clipboard->context->ClientFormatDataRequest(clipboard->context, &request);

data = g_async_queue_timeout_pop(clipboard->clipboard_queue, 3000000);
if (data != NULL)
{
if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG)
/* Busy wait clibpoard data for CLIPBOARD_TRANSFER_WAIT_TIME seconds */
gettimeofday(&tv, NULL);
to.tv_sec = tv.tv_sec + CLIPBOARD_TRANSFER_WAIT_TIME;
to.tv_nsec = tv.tv_usec * 1000;
rc = pthread_cond_timedwait(&clipboard->transfer_clip_cond,&clipboard->transfer_clip_mutex, &to);

if ( rc == 0 ) {
/* Data has arrived without timeout */
if (clipboard->srv_data != NULL)
{
gtk_selection_data_set_pixbuf(selection_data, data);
g_object_unref(data);
if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG)
{
gtk_selection_data_set_pixbuf(selection_data, clipboard->srv_data);
g_object_unref(clipboard->srv_data);
}
else
{
gtk_selection_data_set_text(selection_data, clipboard->srv_data, -1);
free(clipboard->srv_data);
}
}
else
{
gtk_selection_data_set_text(selection_data, data, -1);
clipboard->srv_clip_data_wait = SCDW_NONE;
} else {
clipboard->srv_clip_data_wait = SCDW_ASYNCWAIT;
if ( rc == ETIMEDOUT ) {
remmina_plugin_service->log_printf("[RDP] Clipboard data has not been transfered from the server in %d seconds. Try to paste later.\n",
CLIPBOARD_TRANSFER_WAIT_TIME);
}
else {
remmina_plugin_service->log_printf("[RDP] internal error: pthread_cond_timedwait() returned %d\n",rc);
clipboard->srv_clip_data_wait = SCDW_NONE;
}
}
pthread_mutex_unlock(&clipboard->transfer_clip_mutex);

g_async_queue_unref(clipboard->clipboard_queue);
clipboard->clipboard_queue = NULL;
}

void remmina_rdp_cliprdr_empty_clipboard(GtkClipboard *gtkClipboard, rfClipboard *clipboard)
Expand Down Expand Up @@ -775,6 +836,24 @@ void remmina_rdp_cliprdr_get_clipboard_data(RemminaProtocolWidget* gp, RemminaPl

remmina_rdp_cliprdr_send_data_response(clipboard, outbuf, size);
}
void remmina_rdp_cliprdr_set_clipboard_content(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
{
GtkClipboard* gtkClipboard;
rfContext* rfi = GET_DATA(gp);
rfClipboard* clipboard;
clipboard = ui->clipboard.clipboard;

gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
if (ui->clipboard.format == CB_FORMAT_PNG || ui->clipboard.format == CF_DIB || ui->clipboard.format == CF_DIBV5 || ui->clipboard.format == CB_FORMAT_JPEG) {
gtk_clipboard_set_image( gtkClipboard, ui->clipboard.data );
g_object_unref(ui->clipboard.data);
}
else {
gtk_clipboard_set_text( gtkClipboard, ui->clipboard.data, -1 );
free(ui->clipboard.data);
}

}

void remmina_rdp_cliprdr_set_clipboard_data(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
{
Expand Down Expand Up @@ -817,6 +896,11 @@ void remmina_rdp_event_process_clipboard(RemminaProtocolWidget* gp, RemminaPlugi
case REMMINA_RDP_UI_CLIPBOARD_SET_DATA:
remmina_rdp_cliprdr_set_clipboard_data(gp, ui);
break;

case REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT:
remmina_rdp_cliprdr_set_clipboard_content(gp, ui);
break;

}
}

Expand All @@ -839,6 +923,9 @@ void remmina_rdp_cliprdr_init(rfContext* rfi, CliprdrClientContext* cliprdr)
cliprdr->custom = (void*)clipboard;

clipboard->context = cliprdr;
pthread_mutex_init(&clipboard->transfer_clip_mutex, NULL);
pthread_cond_init(&clipboard->transfer_clip_cond,NULL);
clipboard->srv_clip_data_wait = SCDW_NONE;

cliprdr->MonitorReady = remmina_rdp_cliprdr_monitor_ready;
cliprdr->ServerCapabilities = remmina_rdp_cliprdr_server_capabilities;
Expand Down
11 changes: 8 additions & 3 deletions remmina-plugins/rdp/rdp_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ struct rf_clipboard
UINT32 format;
gulong clipboard_handler;

GAsyncQueue *clipboard_queue;

pthread_mutex_t transfer_clip_mutex;
pthread_cond_t transfer_clip_cond;
enum { SCDW_NONE, SCDW_BUSY_WAIT, SCDW_ASYNCWAIT } srv_clip_data_wait ;
gpointer srv_data;

};
typedef struct rf_clipboard rfClipboard;
Expand Down Expand Up @@ -211,7 +215,8 @@ typedef enum
REMMINA_RDP_UI_CLIPBOARD_MONITORREADY,
REMMINA_RDP_UI_CLIPBOARD_FORMATLIST,
REMMINA_RDP_UI_CLIPBOARD_GET_DATA,
REMMINA_RDP_UI_CLIPBOARD_SET_DATA
REMMINA_RDP_UI_CLIPBOARD_SET_DATA,
REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT
} RemminaPluginRdpUiClipboardType;

typedef enum
Expand Down Expand Up @@ -267,7 +272,7 @@ struct remmina_plugin_rdp_ui_object
GtkTargetList* targetlist;
UINT32 format;
rfClipboard* clipboard;

gpointer data;
} clipboard;
struct {
RemminaPluginRdpUiEeventType type;
Expand Down

0 comments on commit 7b3219f

Please sign in to comment.