diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index bb586a00e..7961b1cd1 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -172,6 +172,9 @@ struct xf_clipboard BOOL streams_supported; BOOL file_formats_registered; UINT32 file_capability_flags; + /* last sent data */ + CLIPRDR_FORMAT* lastSentFormats; + UINT32 lastSentNumFormats; #ifdef WITH_FUSE /* FUSE related**/ HANDLE fuse_thread; @@ -343,9 +346,17 @@ static UINT xf_cliprdr_send_data_request(xfClipboard* clipboard, UINT32 formatId * * @return 0 on success, otherwise a Win32 error code */ -static UINT xf_cliprdr_send_data_response(xfClipboard* clipboard, BYTE* data, int size) +static UINT xf_cliprdr_send_data_response(xfClipboard* clipboard, const BYTE* data, size_t size) { CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 }; + + /* No request currently pending, do not send a response. */ + if (clipboard->requestedFormatId < 0) + return CHANNEL_RC_OK; + + /* Request handled, reset to invalid */ + clipboard->requestedFormatId = -1; + response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; response.dataLen = size; response.requestedFormatData = data; @@ -599,17 +610,92 @@ static void xf_cliprdr_provide_server_format_list(xfClipboard* clipboard) Stream_Free(formats, TRUE); } +static BOOL xf_clipboard_format_equal(const CLIPRDR_FORMAT* a, const CLIPRDR_FORMAT* b) +{ + if (a->formatId != b->formatId) + return FALSE; + if (!a->formatName && !b->formatName) + return TRUE; + + return strcmp(a->formatName, b->formatName) == 0; +} +static BOOL xf_clipboard_changed(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats, + UINT32 numFormats) +{ + UINT32 x, y; + if (clipboard->lastSentNumFormats != numFormats) + return TRUE; + + for (x = 0; x < numFormats; x++) + { + const CLIPRDR_FORMAT* cur = &clipboard->lastSentFormats[x]; + BOOL contained = FALSE; + for (y = 0; y < numFormats; y++) + { + if (xf_clipboard_format_equal(cur, &formats[y])) + { + contained = TRUE; + break; + } + } + if (!contained) + return TRUE; + } + + return FALSE; +} + +static void xf_clipboard_formats_free(xfClipboard* clipboard) +{ + xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); + clipboard->lastSentFormats = NULL; + clipboard->lastSentNumFormats = 0; +} +static BOOL xf_clipboard_copy_formats(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats, + UINT32 numFormats) +{ + UINT32 x; + + xf_clipboard_formats_free(clipboard); + clipboard->lastSentFormats = calloc(numFormats, sizeof(CLIPRDR_FORMAT)); + if (!clipboard->lastSentFormats) + return FALSE; + clipboard->lastSentNumFormats = numFormats; + for (x = 0; x < numFormats; x++) + { + CLIPRDR_FORMAT* lcur = &clipboard->lastSentFormats[x]; + const CLIPRDR_FORMAT* cur = &formats[x]; + *lcur = *cur; + if (cur->formatName) + lcur->formatName = _strdup(cur->formatName); + } + return FALSE; +} + +static UINT xf_cliprdr_send_format_list(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats, + UINT32 numFormats) +{ + CLIPRDR_FORMAT_LIST formatList = { 0 }; + formatList.msgFlags = CB_RESPONSE_OK; + formatList.numFormats = numFormats; + formatList.formats = (CLIPRDR_FORMAT*)formats; + formatList.msgType = CB_FORMAT_LIST; + + if (!xf_clipboard_changed(clipboard, formats, numFormats)) + return CHANNEL_RC_OK; + + xf_clipboard_copy_formats(clipboard, formats, numFormats); + /* Ensure all pending requests are answered. */ + xf_cliprdr_send_data_response(clipboard, NULL, 0); + return clipboard->context->ClientFormatList(clipboard->context, &formatList); +} + static void xf_cliprdr_get_requested_targets(xfClipboard* clipboard) { UINT32 numFormats = 0; CLIPRDR_FORMAT* formats = NULL; - CLIPRDR_FORMAT_LIST formatList = { 0 }; formats = xf_cliprdr_get_client_formats(clipboard, &numFormats); - formatList.msgFlags = CB_RESPONSE_OK; - formatList.numFormats = numFormats; - formatList.formats = formats; - formatList.msgType = CB_FORMAT_LIST; - clipboard->context->ClientFormatList(clipboard->context, &formatList); + xf_cliprdr_send_format_list(clipboard, formats, numFormats); xf_cliprdr_free_formats(formats, numFormats); } @@ -707,7 +793,7 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasDa free(file_array); } - xf_cliprdr_send_data_response(clipboard, pDstData, (int)DstSize); + xf_cliprdr_send_data_response(clipboard, pDstData, DstSize); free(pDstData); } @@ -722,6 +808,7 @@ static BOOL xf_cliprdr_get_requested_data(xfClipboard* clipboard, Atom target) unsigned long bytes_left; xfCliprdrFormat* format; xfContext* xfc = clipboard->xfc; + format = xf_cliprdr_get_client_format_by_id(clipboard, clipboard->requestedFormatId); if (!format || (format->atom != target)) @@ -778,14 +865,16 @@ static BOOL xf_cliprdr_get_requested_data(xfClipboard* clipboard, Atom target) new_data = (BYTE*)realloc(clipboard->incr_data, clipboard->incr_data_length + bytes_left); - if (!new_data) - return FALSE; + if (new_data) + { - clipboard->incr_data = new_data; - CopyMemory(clipboard->incr_data + clipboard->incr_data_length, data, bytes_left); - clipboard->incr_data_length += bytes_left; - XFree(data); - data = NULL; + clipboard->incr_data = new_data; + CopyMemory(clipboard->incr_data + clipboard->incr_data_length, data, + bytes_left); + clipboard->incr_data_length += bytes_left; + XFree(data); + data = NULL; + } } has_data = TRUE; @@ -796,7 +885,7 @@ static BOOL xf_cliprdr_get_requested_data(xfClipboard* clipboard, Atom target) } XDeleteProperty(xfc->display, xfc->drawable, clipboard->property_atom); - xf_cliprdr_process_requested_data(clipboard, has_data, data, (int)bytes_left); + xf_cliprdr_process_requested_data(clipboard, has_data, data, bytes_left); if (data) XFree(data); @@ -1204,7 +1293,6 @@ static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard) { UINT32 i, numFormats; CLIPRDR_FORMAT* formats = NULL; - CLIPRDR_FORMAT_LIST formatList = { 0 }; xfContext* xfc = clipboard->xfc; UINT ret; numFormats = clipboard->numClientFormats; @@ -1224,11 +1312,7 @@ static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard) formats[i].formatName = clipboard->clientFormats[i].formatName; } - formatList.msgFlags = CB_RESPONSE_OK; - formatList.numFormats = numFormats; - formatList.formats = formats; - formatList.msgType = CB_FORMAT_LIST; - ret = clipboard->context->ClientFormatList(clipboard->context, &formatList); + ret = xf_cliprdr_send_format_list(clipboard, formats, numFormats); free(formats); if (clipboard->owner && clipboard->owner != xfc->drawable) @@ -1501,6 +1585,7 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context, xfClipboard* clipboard = (xfClipboard*)context->custom; xfContext* xfc = clipboard->xfc; UINT ret; + xf_clipboard_formats_free(clipboard); xf_cliprdr_clear_cached_data(clipboard); clipboard->data_format_id = -1; clipboard->data_format_name = NULL; @@ -1613,10 +1698,10 @@ xf_cliprdr_server_format_data_request(CliprdrClientContext* context, else format = xf_cliprdr_get_client_format_by_id(clipboard, formatId); + clipboard->requestedFormatId = rawTransfer ? CF_RAW : formatId; if (!format) return xf_cliprdr_send_data_response(clipboard, NULL, 0); - clipboard->requestedFormatId = rawTransfer ? CF_RAW : formatId; XConvertSelection(xfc->display, clipboard->clipboard_atom, format->atom, clipboard->property_atom, xfc->drawable, CurrentTime); XFlush(xfc->display); @@ -2914,6 +2999,7 @@ void xf_clipboard_free(xfClipboard* clipboard) #endif ClipboardDestroy(clipboard->system); + xf_clipboard_formats_free(clipboard); free(clipboard->data); free(clipboard->data_raw); free(clipboard->respond);