--- file_not_specified_in_diff +++ file_not_specified_in_diff @@ -, +, @@ --- /dev/null +++ remmina-1.0.0/remmina-plugins/rdp/rdp_cliprdr.h @@ -0,0 +1,33 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2010-2011 Vic Lee + * Copyright (C) 2012-2012 Jean-Louis Dupond + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __REMMINA_RDP_CLIPRDR_H__ +#define __REMMINA_RDP_CLIPRDR_H__ + +G_BEGIN_DECLS + +RDP_EVENT* remmina_rdp_cliprdr_get_event(uint16 event_type); +int remmina_rdp_cliprdr_send_format_list_event(RemminaProtocolWidget* gp); +void remmina_handle_channel_event(RemminaProtocolWidget* gp, RDP_EVENT* event); + +G_END_DECLS + +#endif --- remmina-1.0.0.orig/remmina-plugins/rdp/CMakeLists.txt +++ remmina-1.0.0/remmina-plugins/rdp/CMakeLists.txt @@ -33,6 +33,8 @@ set(REMMINA_PLUGIN_RDP_SRCS rdp_gdi.h rdp_graphics.c rdp_graphics.h + rdp_cliprdr.c + rdp_cliprdr.h ) add_library(remmina-plugin-rdp ${REMMINA_PLUGIN_RDP_SRCS}) --- remmina-1.0.0.orig/remmina-plugins/rdp/rdp_event.c +++ remmina-1.0.0/remmina-plugins/rdp/rdp_event.c @@ -461,6 +461,16 @@ static gboolean remmina_rdp_event_on_key return TRUE; } +static gboolean remmina_rdp_event_on_clipboard(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp) +{ + RemminaPluginRdpEvent rdp_event = { 0 }; + + rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD; + remmina_rdp_event_event_push(gp, &rdp_event); + + return TRUE; +} + void remmina_rdp_event_init(RemminaProtocolWidget* gp) { gint n; @@ -470,6 +480,7 @@ void remmina_rdp_event_init(RemminaProto XPixmapFormatValues* pf; XPixmapFormatValues* pfs; rfContext* rfi; + GtkClipboard* clipboard; rfi = GET_DATA(gp); rfi->drawing_area = gtk_drawing_area_new(); @@ -508,6 +519,13 @@ void remmina_rdp_event_init(RemminaProto g_signal_connect(G_OBJECT(rfi->drawing_area), "key-release-event", G_CALLBACK(remmina_rdp_event_on_key), gp); + RemminaFile* remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE)) + { + clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); + rfi->clipboard_handler = g_signal_connect(clipboard, "owner-change", G_CALLBACK(remmina_rdp_event_on_clipboard), gp); + } + rfi->pressed_keys = g_array_new(FALSE, TRUE, sizeof (gint)); rfi->event_queue = g_async_queue_new_full(g_free); rfi->ui_queue = g_async_queue_new(); @@ -556,6 +574,13 @@ void remmina_rdp_event_uninit(RemminaPro rfi = GET_DATA(gp); + + /* unregister the clipboard monitor */ + if (rfi->clipboard_handler) + { + g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD)), rfi->clipboard_handler); + rfi->clipboard_handler = NULL; + } if (rfi->scale_handler) { g_source_remove(rfi->scale_handler); --- remmina-1.0.0.orig/remmina-plugins/rdp/rdp_plugin.c +++ remmina-1.0.0/remmina-plugins/rdp/rdp_plugin.c @@ -24,6 +24,7 @@ #include "rdp_graphics.h" #include "rdp_file.h" #include "rdp_settings.h" +#include "rdp_cliprdr.h" #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #define REMMINA_RDP_FEATURE_TOOL_REFRESH 1 #define REMMINA_RDP_FEATURE_SCALE 2 @@ -155,6 +157,14 @@ boolean rf_check_fds(RemminaProtocolWidg input->MouseEvent(input, event->mouse_event.flags, event->mouse_event.x, event->mouse_event.y); break; + case REMMINA_RDP_EVENT_TYPE_CLIPBOARD: + if (rfi->clipboard_wait <= 0) + { + remmina_rdp_cliprdr_send_format_list_event(gp); + rfi->clipboard_wait = 0; + } + rfi->clipboard_wait--; + break; } g_free(event); @@ -545,6 +556,7 @@ static void remmina_rdp_main_loop(Remmin fd_set rfds_set; fd_set wfds_set; rfContext* rfi; + RDP_EVENT* event; memset(rfds, 0, sizeof(rfds)); memset(wfds, 0, sizeof(wfds)); @@ -618,6 +630,12 @@ static void remmina_rdp_main_loop(Remmin { break; } + else + { + event = freerdp_channels_pop_event(rfi->channels); + if (event) + remmina_handle_channel_event(gp, event); + } /* check ui */ if (!rf_check_fds(gp)) { --- /dev/null +++ remmina-1.0.0/remmina-plugins/rdp/rdp_cliprdr.c @@ -0,0 +1,450 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2012-2012 Jean-Louis Dupond + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "rdp_plugin.h" +#include "rdp_cliprdr.h" + +#include +#include +#include +#include + +/* + * Get the formats we can export based on the current clipboard data. + */ +void remmina_rdp_cliprdr_get_target_types(uint32** dst_formats, uint16* size, GdkAtom* types, int count) +{ + int i; + gboolean image = FALSE; + gboolean text = FALSE; + gboolean textutf8 = FALSE; + int matches = 1; + uint32* formats = (uint32*) xmalloc(sizeof(uint32) * (count+1)); + + formats[0] = CB_FORMAT_RAW; + for (i = 0; i < count; i++) + { + GdkAtom atom = GDK_POINTER_TO_ATOM(types[i]); + gchar* name = gdk_atom_name(atom); + if (g_strcmp0("UTF8_STRING", name) == 0 || g_strcmp0("text/plain;charset=utf-8", name) == 0) + { + textutf8 = TRUE; + } + if (g_strcmp0("TEXT", name) == 0 || g_strcmp0("text/plain", name) == 0) + { + text = TRUE; + } + if (g_strcmp0("text/html", name) == 0) + { + formats[matches] = CB_FORMAT_HTML; + matches++; + } + if (g_strcmp0("image/png", name) == 0) + { + formats[matches] = CB_FORMAT_PNG; + image = TRUE; + matches++; + } + if (g_strcmp0("image/jpeg", name) == 0) + { + formats[matches] = CB_FORMAT_JPEG; + image = TRUE; + matches++; + } + if (g_strcmp0("image/bmp", name) == 0) + { + formats[matches] = CB_FORMAT_DIB; + image = TRUE; + matches++; + } + g_free(name); + } + //Only add text formats if we don't have image formats + if (!image) + { + if (textutf8) + { + formats[matches] = CB_FORMAT_UNICODETEXT; + matches++; + } + if (text) + { + formats[matches] = CB_FORMAT_TEXT; + matches++; + } + } + + *size = (uint16)matches; + *dst_formats = (uint32*) xmalloc(sizeof(uint32) * matches); + memcpy(*dst_formats, formats, sizeof(uint32) * matches); + g_free(formats); +} + +int remmina_rdp_cliprdr_send_format_list_event(RemminaProtocolWidget* gp) +{ + GtkClipboard* clipboard; + GdkAtom* targets; + gboolean result = 0; + gint count; + RDP_EVENT* rdp_event; + RDP_CB_FORMAT_LIST_EVENT* format_list_event; + rfContext* rfi = GET_DATA(gp); + + /* Lets see if we have something in our clipboard */ + THREADS_ENTER + clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); + if (clipboard) + { + result = gtk_clipboard_wait_for_targets(clipboard, &targets, &count); + } + THREADS_LEAVE + + if (!result) + return 1; + + rdp_event = (RDP_EVENT*) xnew(RDP_CB_FORMAT_LIST_EVENT); + rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR; + rdp_event->event_type = RDP_EVENT_TYPE_CB_FORMAT_LIST; + format_list_event = (RDP_CB_FORMAT_LIST_EVENT*) rdp_event; + + remmina_rdp_cliprdr_get_target_types(&format_list_event->formats, &format_list_event->num_formats, targets, count); + g_free(targets); + + return freerdp_channels_send_event(rfi->channels, (RDP_EVENT*) format_list_event); +} + +static uint8* lf2crlf(uint8* data, int* size) +{ + uint8 c; + uint8* outbuf; + uint8* out; + uint8* in_end; + uint8* in; + int out_size; + + out_size = (*size) * 2 + 1; + outbuf = (uint8*) xmalloc(out_size); + out = outbuf; + in = data; + in_end = data + (*size); + + while (in < in_end) + { + c = *in++; + if (c == '\n') + { + *out++ = '\r'; + *out++ = '\n'; + } + else + { + *out++ = c; + } + } + + *out++ = 0; + *size = out - outbuf; + + return outbuf; +} + +static void crlf2lf(uint8* data, int* size) +{ + uint8 c; + uint8* out; + uint8* in; + uint8* in_end; + + out = data; + in = data; + in_end = data + (*size); + + while (in < in_end) + { + c = *in++; + + if (c != '\r') + *out++ = c; + } + + *size = out - data; +} + +uint8* remmina_rdp_cliprdr_get_data(RemminaProtocolWidget* gp, uint32 format, int* size) +{ + rfContext* rfi = GET_DATA(gp); + GtkClipboard* clipboard; + uint8* inbuf = NULL; + uint8* outbuf = NULL; + GdkPixbuf *image = NULL; + + THREADS_ENTER + clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); + if (clipboard) + { + if (format == CB_FORMAT_TEXT || format == CB_FORMAT_UNICODETEXT || format == CB_FORMAT_HTML) + { + inbuf = (uint8*)gtk_clipboard_wait_for_text(clipboard); + } + if (format == CB_FORMAT_PNG || format == CB_FORMAT_JPEG || format == CB_FORMAT_DIB) + { + image = gtk_clipboard_wait_for_image(clipboard); + } + } + THREADS_LEAVE + + /* No data received, send nothing */ + if (inbuf == NULL && image == NULL) + { + *size = 0; + return NULL; + } + + + if (format == CB_FORMAT_TEXT || format == CB_FORMAT_HTML || format == CB_FORMAT_UNICODETEXT) + { + *size = strlen((char*)inbuf); + inbuf = lf2crlf(inbuf, size); + if (format == CB_FORMAT_TEXT) + { + outbuf = inbuf; + } + if (format == CB_FORMAT_HTML) + { + //TODO: check if we need special handling for HTML + outbuf = inbuf; + } + if (format == CB_FORMAT_UNICODETEXT) + { + size_t out_size; + UNICONV* uniconv; + + uniconv = freerdp_uniconv_new(); + outbuf = (uint8*) freerdp_uniconv_out(uniconv, (char*) inbuf, &out_size); + freerdp_uniconv_free(uniconv); + g_free(inbuf); + *size = out_size + 2; + } + } + if (format == CB_FORMAT_PNG || format == CB_FORMAT_JPEG || format == CB_FORMAT_DIB) + { + gchar* data; + gsize buffersize; + if (format == CB_FORMAT_PNG) + { + gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "png", NULL, NULL); + outbuf = (uint8*) xmalloc(buffersize); + memcpy(outbuf, data, buffersize); + *size = buffersize; + } + if (format == CB_FORMAT_JPEG) + { + gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "jpeg", NULL, NULL); + outbuf = (uint8*) xmalloc(buffersize); + memcpy(outbuf, data, buffersize); + *size = buffersize; + } + if (format == CB_FORMAT_DIB) + { + gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "bmp", NULL, NULL); + *size = buffersize - 14; + outbuf = (uint8*) xmalloc(*size); + memcpy(outbuf, data + 14, *size); + } + g_object_unref(image); + } + + return outbuf; +} + +void remmina_rdp_cliprdr_parse_response_event(RemminaProtocolWidget* gp, RDP_EVENT* event) +{ + GtkClipboard* clipboard; + GdkPixbuf *image = NULL; + uint8* data; + int size; + gboolean text = FALSE; + gboolean img = FALSE; + rfContext* rfi = GET_DATA(gp); + RDP_CB_DATA_RESPONSE_EVENT* data_response_event; + GdkPixbufLoader *pixbuf; + data_response_event = (RDP_CB_DATA_RESPONSE_EVENT*) event; + data = data_response_event->data; + size = data_response_event->size; + + if (rfi->requested_format == CB_FORMAT_TEXT || rfi->requested_format == CB_FORMAT_UNICODETEXT || rfi->requested_format == CB_FORMAT_HTML) + { + if (rfi->requested_format == CB_FORMAT_UNICODETEXT) + { + UNICONV* uniconv; + + uniconv = freerdp_uniconv_new(); + data = (uint8*) freerdp_uniconv_in(uniconv, data, size); + size = strlen((char*) data); + freerdp_uniconv_free(uniconv); + } + crlf2lf(data, &size); + text = TRUE; + } + if (rfi->requested_format == CB_FORMAT_DIB || rfi->requested_format == CB_FORMAT_PNG || rfi->requested_format == CB_FORMAT_JPEG) + { + /* Reconstruct header */ + if (rfi->requested_format == CB_FORMAT_DIB) + { + STREAM* s; + uint16 bpp; + uint32 offset; + uint32 ncolors; + + s = stream_new(0); + stream_attach(s, data, size); + stream_seek(s, 14); + stream_read_uint16(s, bpp); + stream_read_uint32(s, ncolors); + offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0); + stream_detach(s); + stream_free(s); + + s = stream_new(14 + size); + stream_write_uint8(s, 'B'); + stream_write_uint8(s, 'M'); + stream_write_uint32(s, 14 + size); + stream_write_uint32(s, 0); + stream_write_uint32(s, offset); + stream_write(s, data, size); + + data = stream_get_head(s); + size = stream_get_length(s); + stream_detach(s); + stream_free(s); + } + pixbuf = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(pixbuf, data, size, NULL); + image = gdk_pixbuf_loader_get_pixbuf(pixbuf); + img = TRUE; + } + + THREADS_ENTER + clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD); + if (clipboard) + { + if (text || img) + { + rfi->clipboard_wait = 2; + } + if (text) + { + gtk_clipboard_set_text(clipboard, (gchar*)data, size); + gtk_clipboard_store(clipboard); + } + if (img) + { + gtk_clipboard_set_image(clipboard, image); + gtk_clipboard_store(clipboard); + gdk_pixbuf_loader_close(pixbuf, NULL); + g_object_unref(pixbuf); + } + } + THREADS_LEAVE + +} + +void remmina_handle_channel_event(RemminaProtocolWidget* gp, RDP_EVENT* event) +{ + RDP_EVENT* rdp_event = NULL; + rfContext* rfi = GET_DATA(gp); + + switch (event->event_class) + { + case RDP_EVENT_CLASS_CLIPRDR: + if (event->event_type == RDP_EVENT_TYPE_CB_MONITOR_READY) + { + /* Sending our format list */ + remmina_rdp_cliprdr_send_format_list_event(gp); + } + if (event->event_type == RDP_EVENT_TYPE_CB_FORMAT_LIST) + { + /* We received a FORMAT_LIST from the server, update our clipboard */ + int i; + uint32 format = CB_FORMAT_RAW; + RDP_CB_FORMAT_LIST_EVENT* format_list_event; + + format_list_event = (RDP_CB_FORMAT_LIST_EVENT*) event; + + for (i = 0; i < format_list_event->num_formats; i++) + { + if (format_list_event->formats[i] > format) + { + if (format_list_event->formats[i] == CB_FORMAT_UNICODETEXT) + { + format = CB_FORMAT_UNICODETEXT; + } + if (format_list_event->formats[i] == CB_FORMAT_DIB) + { + format = CB_FORMAT_DIB; + } + if (format_list_event->formats[i] == CB_FORMAT_JPEG) + { + format = CB_FORMAT_JPEG; + } + if (format_list_event->formats[i] == CB_FORMAT_PNG) + { + format = CB_FORMAT_PNG; + } + if (format_list_event->formats[i] == CB_FORMAT_TEXT) + { + format = CB_FORMAT_TEXT; + } + } + } + rfi->requested_format = format; + + /* Request Clipboard data of the server */ + RDP_CB_DATA_REQUEST_EVENT* data_request_event; + rdp_event = (RDP_EVENT*) xnew(RDP_CB_DATA_REQUEST_EVENT); + rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR; + rdp_event->event_type = RDP_EVENT_TYPE_CB_DATA_REQUEST; + data_request_event = (RDP_CB_DATA_REQUEST_EVENT*) rdp_event; + data_request_event->format = format; + freerdp_channels_send_event(rfi->channels, (RDP_EVENT*) data_request_event); + } + if (event->event_type == RDP_EVENT_TYPE_CB_DATA_REQUEST) + { + uint8* data; + int size; + RDP_CB_DATA_REQUEST_EVENT* data_request_event = (RDP_CB_DATA_REQUEST_EVENT*) event; + RDP_CB_DATA_RESPONSE_EVENT* data_response_event; + + /* Send Data */ + rdp_event = (RDP_EVENT*) xnew(RDP_CB_DATA_RESPONSE_EVENT); + rdp_event->event_class = RDP_EVENT_CLASS_CLIPRDR; + rdp_event->event_type = RDP_EVENT_TYPE_CB_DATA_RESPONSE; + data_response_event = (RDP_CB_DATA_RESPONSE_EVENT*) rdp_event; + data = remmina_rdp_cliprdr_get_data(gp, data_request_event->format, &size); + data_response_event->data = data; + data_response_event->size = size; + freerdp_channels_send_event(rfi->channels, rdp_event); + } + if (event->event_type == RDP_EVENT_TYPE_CB_DATA_RESPONSE) + { + remmina_rdp_cliprdr_parse_response_event(gp, event); + } + } +} --- remmina-1.0.0.orig/remmina-plugins/rdp/rdp_plugin.h +++ remmina-1.0.0/remmina-plugins/rdp/rdp_plugin.h @@ -133,12 +133,17 @@ struct rf_context GArray* pressed_keys; GAsyncQueue* event_queue; gint event_pipe[2]; + + gint clipboard_handler; + gint clipboard_wait; + uint32 requested_format; }; typedef enum { REMMINA_RDP_EVENT_TYPE_SCANCODE, - REMMINA_RDP_EVENT_TYPE_MOUSE + REMMINA_RDP_EVENT_TYPE_MOUSE, + REMMINA_RDP_EVENT_TYPE_CLIPBOARD } RemminaPluginRdpEventType; struct remmina_plugin_rdp_event