From 2e82e6d22dc071f9fe25311ca8e8c4706f9f99f8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 15 Oct 2014 22:48:18 -0400 Subject: [PATCH] xfreerdp: fix clipboard sync --- channels/cliprdr/client/cliprdr_format.c | 8 ++ channels/cliprdr/client/cliprdr_main.c | 55 ++++++++++- channels/cliprdr/client/cliprdr_main.h | 2 + client/X11/xf_cliprdr.c | 160 +++++++++++++++---------------- 4 files changed, 136 insertions(+), 89 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 495bfa9..9ad527c 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -214,6 +214,8 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data { CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList"); + if (context->custom) { UINT32 index; @@ -376,6 +378,8 @@ void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI { CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse"); + /* http://msdn.microsoft.com/en-us/library/hh872154.aspx */ if (context->custom) @@ -406,6 +410,8 @@ void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN { CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest"); + if (context->custom) { CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest; @@ -465,6 +471,8 @@ void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI { CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse"); + if (context->custom) { CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse; diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index 954db3e..54f059a 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -36,7 +36,6 @@ #include "cliprdr_main.h" #include "cliprdr_format.h" -#ifdef WITH_DEBUG_CLIPRDR static const char* const CB_MSG_TYPE_STRINGS[] = { "", @@ -52,7 +51,6 @@ static const char* const CB_MSG_TYPE_STRINGS[] = "CB_LOCK_CLIPDATA" "CB_UNLOCK_CLIPDATA" }; -#endif CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr) { @@ -171,7 +169,8 @@ static void cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s, UINT16 UINT16 capabilitySetType; Stream_Read_UINT16(s, cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */ Stream_Seek_UINT16(s); /* pad1 (2 bytes) */ - DEBUG_CLIPRDR("cCapabilitiesSets %d", cCapabilitiesSets); + + WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerCapabilities"); for (i = 0; i < cCapabilitiesSets; i++) { @@ -215,6 +214,8 @@ static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UI { CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + WLog_Print(cliprdr->log, WLOG_DEBUG, "MonitorReady"); + if (context->custom) { CLIPRDR_MONITOR_READY monitorReady; @@ -240,8 +241,12 @@ static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UI static void cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags) { RDP_CB_FILECONTENTS_REQUEST_EVENT* cb_event; + + WLog_Print(cliprdr->log, WLOG_DEBUG, "FileContentsRequest"); + cb_event = (RDP_CB_FILECONTENTS_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_FilecontentsRequest, NULL, NULL); + Stream_Read_UINT32(s, cb_event->streamId); Stream_Read_UINT32(s, cb_event->lindex); Stream_Read_UINT32(s, cb_event->dwFlags); @@ -255,8 +260,12 @@ static void cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream static void cliprdr_process_filecontents_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags) { RDP_CB_FILECONTENTS_RESPONSE_EVENT* cb_event; + + WLog_Print(cliprdr->log, WLOG_DEBUG, "FileContentsResponse"); + cb_event = (RDP_CB_FILECONTENTS_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_FilecontentsResponse, NULL, NULL); + Stream_Read_UINT32(s, cb_event->streamId); if (length > 0) @@ -272,18 +281,28 @@ static void cliprdr_process_filecontents_response(cliprdrPlugin* cliprdr, wStrea static void cliprdr_process_lock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags) { RDP_CB_LOCK_CLIPDATA_EVENT* cb_event; + + WLog_Print(cliprdr->log, WLOG_DEBUG, "LockClipData"); + cb_event = (RDP_CB_LOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_LockClipdata, NULL, NULL); + Stream_Read_UINT32(s, cb_event->clipDataId); + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); } static void cliprdr_process_unlock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags) { RDP_CB_UNLOCK_CLIPDATA_EVENT* cb_event; + + WLog_Print(cliprdr->log, WLOG_DEBUG, "UnlockClipData"); + cb_event = (RDP_CB_UNLOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_UnLockClipdata, NULL, NULL); + Stream_Read_UINT32(s, cb_event->clipDataId); + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); } @@ -296,6 +315,7 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s) Stream_Read_UINT16(s, msgType); Stream_Read_UINT16(s, msgFlags); Stream_Read_UINT32(s, dataLen); + DEBUG_CLIPRDR("msgType: %s (%d), msgFlags: %d dataLen: %d", CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen); #ifdef WITH_DEBUG_CLIPRDR @@ -452,15 +472,21 @@ int cliprdr_client_capabilities(CliprdrClientContext* context, CLIPRDR_CAPABILIT wStream* s; CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet; cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN); + Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */ Stream_Write_UINT16(s, 0); /* pad1 */ + generalCapabilitySet = (CLIPRDR_GENERAL_CAPABILITY_SET*) capabilities->capabilitySets; Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetType); /* capabilitySetType */ Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetLength); /* lengthCapability */ Stream_Write_UINT32(s, generalCapabilitySet->version); /* version */ Stream_Write_UINT32(s, generalCapabilitySet->generalFlags); /* generalFlags */ + + WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientCapabilities"); cliprdr_packet_send(cliprdr, s); + return 0; } @@ -499,7 +525,7 @@ int cliprdr_client_format_list(CliprdrClientContext* context, CLIPRDR_FORMAT_LIS lpWideCharStr = (LPWSTR) Stream_Pointer(s); cchWideChar = (Stream_Capacity(s) - Stream_GetPosition(s)) / 2; formatNameSize = MultiByteToWideChar(CP_UTF8, 0, - format->formatName, -1, lpWideCharStr, cchWideChar) * 2; + format->formatName, -1, lpWideCharStr, cchWideChar) * 2; Stream_Seek(s, formatNameSize); } else @@ -508,7 +534,10 @@ int cliprdr_client_format_list(CliprdrClientContext* context, CLIPRDR_FORMAT_LIS } } + WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatList: numFormats: %d", + formatList->cFormats); cliprdr_packet_send(cliprdr, s); + return 0; } @@ -516,10 +545,15 @@ int cliprdr_client_format_list_response(CliprdrClientContext* context, CLIPRDR_F { wStream* s; cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + formatListResponse->msgType = CB_FORMAT_LIST_RESPONSE; formatListResponse->dataLen = 0; + s = cliprdr_packet_new(formatListResponse->msgType, formatListResponse->msgFlags, formatListResponse->dataLen); + + WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatListResponse"); cliprdr_packet_send(cliprdr, s); + return 0; } @@ -527,12 +561,17 @@ int cliprdr_client_format_data_request(CliprdrClientContext* context, CLIPRDR_FO { wStream* s; cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + formatDataRequest->msgType = CB_FORMAT_DATA_REQUEST; formatDataRequest->msgFlags = 0; formatDataRequest->dataLen = 4; + s = cliprdr_packet_new(formatDataRequest->msgType, formatDataRequest->msgFlags, formatDataRequest->dataLen); Stream_Write_UINT32(s, formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */ + + WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataRequest"); cliprdr_packet_send(cliprdr, s); + return 0; } @@ -540,10 +579,16 @@ int cliprdr_client_format_data_response(CliprdrClientContext* context, CLIPRDR_F { wStream* s; cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + formatDataResponse->msgType = CB_FORMAT_DATA_RESPONSE; + s = cliprdr_packet_new(formatDataResponse->msgType, formatDataResponse->msgFlags, formatDataResponse->dataLen); + Stream_Write(s, formatDataResponse->requestedFormatData, formatDataResponse->dataLen); + + WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataResponse"); cliprdr_packet_send(cliprdr, s); + return 0; } @@ -566,6 +611,8 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) strcpy(cliprdr->plugin.channel_def.name, "cliprdr"); + cliprdr->log = WLog_Get("com.freerdp.channels.cliprdr.client"); + cliprdr->plugin.connect_callback = cliprdr_process_connect; cliprdr->plugin.receive_callback = cliprdr_process_receive; cliprdr->plugin.event_callback = cliprdr_process_event; diff --git a/channels/cliprdr/client/cliprdr_main.h b/channels/cliprdr/client/cliprdr_main.h index 4a8d60a..050de26 100644 --- a/channels/cliprdr/client/cliprdr_main.h +++ b/channels/cliprdr/client/cliprdr_main.h @@ -31,6 +31,8 @@ struct cliprdr_plugin { rdpSvcPlugin plugin; + + wLog* log; BOOL received_caps; BOOL use_long_format_names; BOOL stream_fileclip_enabled; diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index 206aa1a..e8a6a0b 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -39,17 +39,6 @@ #include "xf_cliprdr.h" #define TAG CLIENT_TAG("x11") -#ifdef WITH_DEBUG_X11 -#define DEBUG_X11(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__) -#else -#define DEBUG_X11(fmt, ...) do { } while (0) -#endif - -#ifdef WITH_DEBUG_X11_CLIPRDR -#define DEBUG_X11_CLIPRDR(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__) -#else -#define DEBUG_X11_CLIPRDR(fmt, ...) do { } while (0) -#endif struct xf_cliprdr_format { @@ -102,6 +91,8 @@ struct xf_clipboard BOOL xfixes_supported; }; +int xf_cliprdr_send_client_format_list(xfClipboard* clipboard); + static BYTE* lf2crlf(BYTE* data, int* size) { BYTE c; @@ -272,43 +263,8 @@ static void xf_cliprdr_send_supported_format_list(xfClipboard* clipboard) } clipboard->context->ClientFormatList(clipboard->context, &formatList); -} - -static void xf_cliprdr_send_format_list(xfClipboard* clipboard) -{ - CLIPRDR_FORMAT_LIST formatList; - xfContext* xfc = clipboard->xfc; - - ZeroMemory(&formatList, sizeof(CLIPRDR_FORMAT_LIST)); - - if (xf_cliprdr_is_self_owned(clipboard)) - { - /** - * TODO: handle (raw?) format data - * The original code appears to be sending back the original, - * unmodified server format list here. - */ - - formatList.msgFlags = CB_RESPONSE_OK; - formatList.cFormats = clipboard->numServerFormats; - formatList.formats = clipboard->serverFormats; - - clipboard->context->ClientFormatList(clipboard->context, &formatList); - } - else if (clipboard->owner == None) - { - formatList.msgFlags = CB_RESPONSE_OK; - formatList.cFormats = 0; - formatList.formats = NULL; - clipboard->context->ClientFormatList(clipboard->context, &formatList); - } - else if (clipboard->owner != xfc->drawable) - { - /* Request the owner for TARGETS, and wait for SelectionNotify event */ - XConvertSelection(xfc->display, clipboard->clipboard_atom, - clipboard->targets[1], clipboard->property_atom, xfc->drawable, CurrentTime); - } + free(formats); } static void xf_cliprdr_send_data_request(xfClipboard* clipboard, UINT32 formatId) @@ -419,10 +375,7 @@ static BYTE* xf_cliprdr_process_requested_dib(BYTE* data, int* size) /* length should be at least BMP header (14) + sizeof(BITMAPINFOHEADER) */ if (*size < 54) - { - DEBUG_X11_CLIPRDR("bmp length %d too short", *size); return NULL; - } *size -= 14; outbuf = (BYTE*) calloc(1, *size); @@ -559,7 +512,7 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL has_d if (!clipboard->xfixes_supported) { /* Resend the format list, otherwise the server won't request again for the next paste */ - xf_cliprdr_send_format_list(clipboard); + xf_cliprdr_send_client_format_list(clipboard); } } @@ -716,26 +669,19 @@ static BOOL xf_cliprdr_process_dib(xfClipboard* clipboard, BYTE* data, int size) /* size should be at least sizeof(BITMAPINFOHEADER) */ if (size < 40) - { - DEBUG_X11_CLIPRDR("dib size %d too short", size); return FALSE; - } s = Stream_New(data, size); Stream_Seek(s, 14); Stream_Read_UINT16(s, bpp); + if ((bpp < 1) || (bpp > 32)) - { - WLog_ERR(TAG, "invalid bpp value %d", bpp); return FALSE; - } Stream_Read_UINT32(s, ncolors); offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0); Stream_Free(s, FALSE); - DEBUG_X11_CLIPRDR("offset=%d bpp=%d ncolors=%d", offset, bpp, ncolors); - s = Stream_New(NULL, 14 + size); Stream_Write_UINT8(s, 'B'); Stream_Write_UINT8(s, 'M'); @@ -747,6 +693,7 @@ static BOOL xf_cliprdr_process_dib(xfClipboard* clipboard, BYTE* data, int size) clipboard->data = Stream_Buffer(s); clipboard->data_length = Stream_GetPosition(s); Stream_Free(s, FALSE); + return TRUE; } @@ -761,19 +708,13 @@ static void xf_cliprdr_process_html(xfClipboard* clipboard, BYTE* data, int size end_str = strstr((char*) data, "EndHTML:"); if (!start_str || !end_str) - { - DEBUG_X11_CLIPRDR("invalid HTML clipboard format"); return; - } start = atoi(start_str + 10); end = atoi(end_str + 8); if ((start > size) || (end > size) || (start >= end)) - { - DEBUG_X11_CLIPRDR("invalid HTML offset"); return; - } clipboard->data = (BYTE*) malloc(size - start + 1); CopyMemory(clipboard->data, data + start, end - start); @@ -856,8 +797,9 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent* clipboard->property_atom, 0, 4, 0, XA_INTEGER, &type, &fmt, &length, &bytes_left, &data) != Success) { - DEBUG_X11_CLIPRDR("XGetWindowProperty failed"); + } + if (data) { CopyMemory(&altFormatId, data, 4); @@ -873,7 +815,7 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent* } else if (clipboard->respond) { - DEBUG_X11_CLIPRDR("duplicated request"); + /* duplicate request */ } else { @@ -933,7 +875,7 @@ static BOOL xf_cliprdr_process_property_notify(xfClipboard* clipboard, XEvent* x if (xevent->xproperty.window == clipboard->root_window) { - xf_cliprdr_send_format_list(clipboard); + xf_cliprdr_send_client_format_list(clipboard); } else if ((xevent->xproperty.window == xfc->drawable) && (xevent->xproperty.state == PropertyNewValue) && clipboard->incr_starts) @@ -959,7 +901,7 @@ static void xf_cliprdr_check_owner(xfClipboard* clipboard) if (clipboard->owner != owner) { clipboard->owner = owner; - xf_cliprdr_send_format_list(clipboard); + xf_cliprdr_send_client_format_list(clipboard); } } } @@ -1045,7 +987,64 @@ int xf_cliprdr_send_client_capabilities(xfClipboard* clipboard) int xf_cliprdr_send_client_format_list(xfClipboard* clipboard) { - xf_cliprdr_send_format_list(clipboard); + CLIPRDR_FORMAT_LIST formatList; + xfContext* xfc = clipboard->xfc; + + ZeroMemory(&formatList, sizeof(CLIPRDR_FORMAT_LIST)); + + if (!clipboard->sync) + { + UINT32 i, numFormats; + CLIPRDR_FORMAT* formats; + + numFormats = clipboard->numClientFormats; + formats = (CLIPRDR_FORMAT*) calloc(numFormats, sizeof(CLIPRDR_FORMAT)); + + for (i = 0; i < numFormats; i++) + { + formats[i].formatId = clipboard->clientFormats[i].formatId; + formats[i].formatName = clipboard->clientFormats[i].formatName; + } + + formatList.msgFlags = CB_RESPONSE_OK; + formatList.cFormats = numFormats; + formatList.formats = formats; + + clipboard->context->ClientFormatList(clipboard->context, &formatList); + + free(formats); + + return 1; + } + + if (xf_cliprdr_is_self_owned(clipboard)) + { + /** + * TODO: handle (raw?) format data + * The original code appears to be sending back the original, + * unmodified server format list here. + */ + + formatList.msgFlags = CB_RESPONSE_OK; + formatList.cFormats = clipboard->numServerFormats; + formatList.formats = clipboard->serverFormats; + + clipboard->context->ClientFormatList(clipboard->context, &formatList); + } + else if (clipboard->owner == None) + { + formatList.msgFlags = CB_RESPONSE_OK; + formatList.cFormats = 0; + formatList.formats = NULL; + + clipboard->context->ClientFormatList(clipboard->context, &formatList); + } + else if (clipboard->owner != xfc->drawable) + { + /* Request the owner for TARGETS, and wait for SelectionNotify event */ + XConvertSelection(xfc->display, clipboard->clipboard_atom, + clipboard->targets[1], clipboard->property_atom, xfc->drawable, CurrentTime); + } return 1; } @@ -1154,6 +1153,9 @@ static int xf_cliprdr_server_format_list(CliprdrClientContext* context, CLIPRDR_ if (clipboard->serverFormats) { + for (i = 0; i < clipboard->numServerFormats; i++) + free(clipboard->serverFormats[i].formatName); + free(clipboard->serverFormats); clipboard->serverFormats = NULL; } @@ -1186,23 +1188,11 @@ static int xf_cliprdr_server_format_list(CliprdrClientContext* context, CLIPRDR_ } } - XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, CurrentTime); - - XFlush(xfc->display); - - return 1; - xf_cliprdr_send_client_format_list_response(clipboard, TRUE); - for (i = 0; i < formatList->cFormats; i++) - { - format = &formatList->formats[i]; + XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, CurrentTime); - if (format->formatId == CLIPRDR_FORMAT_UNICODETEXT) - { - xf_cliprdr_send_client_format_data_request(clipboard, format->formatId); - } - } + XFlush(xfc->display); return 1; } @@ -1362,9 +1352,9 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) if (XFixesQueryExtension(xfc->display, &clipboard->xfixes_event_base, &clipboard->xfixes_error_base)) { int xfmajor, xfminor; + if (XFixesQueryVersion(xfc->display, &xfmajor, &xfminor)) { - DEBUG_X11_CLIPRDR("Found X Fixes extension version %d.%d", xfmajor, xfminor); XFixesSelectSelectionInput(xfc->display, clipboard->root_window, clipboard->clipboard_atom, XFixesSetSelectionOwnerNotifyMask); clipboard->xfixes_supported = TRUE; -- 2.7.4