client/X11: improve named clipboard format support

Clipboard formats are identified by numerical IDs and literal names.
We can keep using arbitrary defined IDs for local clipboard formats
as we are sure that they have some fixed meaning, but the server can
and will be using its own IDs, which can be different from ours for
the named formats.

Therefore:

1) A correct way to compare a local format to a remote one is
   to check the names first, and only then compare their IDs.
   (Extra care should be taken to support short format names.)

2) Server IDs cannot be used with wClipboard directly when dealing
   with named formats. Format name should be used to extract correct
   local ID for the use with Clipboard{Set,Get}Data().

Also, I find the notion of 'alternate' format IDs to be confusing.
We either deal with a fixed ID format (declared in <winpr/user.h>),
or a format that was given an arbitrary fixed ID for local use
(defined in <freerdp/channels/cliprdr.h>), or a remote format
identified by a pair of an ID and a name. Format IDs can be local
and remote, but there are no 'alternates'.

So now:

1) A new function xf_cliprdr_formats_equal() is used to compare
   formats correctly in xf_cliprdr_get_server_format_by_atom()
   when searching for a server format corresponding to a local
   one, and in xf_cliprdr_server_format_list() when constructing
   a local TARGETS list from the server format list.

2) Correct local format IDs are used with wClipboard conversions
   by xf_cliprdr_process_requested_data() and
   xf_cliprdr_server_format_data_response().

3) We refer to formatId and formatName when doing requests,
   and srcFormatId and dstFormatId when doing conversions,
   instead of using formatId and altFormatId for both purposes.

4) Server format ID and name are used to identify cached clipboard
   contents. The name is compared directly as a pointer because it
   will be a pointer from the same clipboard->serverFormats array.

   Also, the clipboard contents are invalidated when format list
   arrives, so xf_cliprdr_server_format_list() now also clears
   the format ID and name together with the data.
This commit is contained in:
ilammy 2015-08-17 11:35:19 +03:00
parent 8434709fc6
commit b9a297379b

View File

@ -76,8 +76,8 @@ struct xf_clipboard
int requestedFormatId; int requestedFormatId;
BYTE* data; BYTE* data;
UINT32 data_format; UINT32 data_format_id;
UINT32 data_alt_format; const char* data_format_name;
int data_length; int data_length;
XEvent* respond; XEvent* respond;
@ -122,6 +122,22 @@ static BOOL xf_cliprdr_is_self_owned(xfClipboard* clipboard)
return XGetSelectionOwner(xfc->display, clipboard->clipboard_atom) == xfc->drawable; return XGetSelectionOwner(xfc->display, clipboard->clipboard_atom) == xfc->drawable;
} }
static BOOL xf_cliprdr_formats_equal(const CLIPRDR_FORMAT* server, const xfCliprdrFormat* client)
{
if (server->formatName && client->formatName)
{
/* The server may be using short format names while we store them in full form. */
return (0 == strncmp(server->formatName, client->formatName, strlen(server->formatName)));
}
if (!server->formatName && !client->formatName)
{
return (server->formatId == client->formatId);
}
return FALSE;
}
static xfCliprdrFormat* xf_cliprdr_get_client_format_by_id(xfClipboard* clipboard, UINT32 formatId) static xfCliprdrFormat* xf_cliprdr_get_client_format_by_id(xfClipboard* clipboard, UINT32 formatId)
{ {
int index; int index;
@ -170,7 +186,7 @@ static CLIPRDR_FORMAT* xf_cliprdr_get_server_format_by_atom(xfClipboard* clipboa
{ {
server_format = &(clipboard->serverFormats[j]); server_format = &(clipboard->serverFormats[j]);
if (server_format->formatId == client_format->formatId) if (xf_cliprdr_formats_equal(server_format, client_format))
return server_format; return server_format;
} }
} }
@ -277,8 +293,8 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasDa
BOOL bSuccess; BOOL bSuccess;
UINT32 SrcSize; UINT32 SrcSize;
UINT32 DstSize; UINT32 DstSize;
UINT32 formatId; UINT32 srcFormatId;
UINT32 altFormatId; UINT32 dstFormatId;
BYTE* pSrcData = NULL; BYTE* pSrcData = NULL;
BYTE* pDstData = NULL; BYTE* pDstData = NULL;
xfCliprdrFormat* format; xfCliprdrFormat* format;
@ -294,8 +310,8 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasDa
return; return;
} }
formatId = 0; srcFormatId = 0;
altFormatId = 0; dstFormatId = 0;
switch (format->formatId) switch (format->formatId)
{ {
@ -303,16 +319,16 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasDa
case CF_OEMTEXT: case CF_OEMTEXT:
case CF_UNICODETEXT: case CF_UNICODETEXT:
size = strlen((char*) data) + 1; size = strlen((char*) data) + 1;
formatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING"); srcFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
break; break;
case CF_DIB: case CF_DIB:
formatId = ClipboardGetFormatId(clipboard->system, "image/bmp"); srcFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
break; break;
case CB_FORMAT_HTML: case CB_FORMAT_HTML:
size = strlen((char*) data) + 1; size = strlen((char*) data) + 1;
formatId = ClipboardGetFormatId(clipboard->system, "text/html"); srcFormatId = ClipboardGetFormatId(clipboard->system, "text/html");
break; break;
} }
@ -324,17 +340,24 @@ static void xf_cliprdr_process_requested_data(xfClipboard* clipboard, BOOL hasDa
CopyMemory(pSrcData, data, SrcSize); CopyMemory(pSrcData, data, SrcSize);
bSuccess = ClipboardSetData(clipboard->system, formatId, (void*) pSrcData, SrcSize); bSuccess = ClipboardSetData(clipboard->system, srcFormatId, (void*) pSrcData, SrcSize);
if (!bSuccess) if (!bSuccess)
free(pSrcData); free(pSrcData);
altFormatId = clipboard->requestedFormatId; if (format->formatName)
{
dstFormatId = ClipboardGetFormatId(clipboard->system, format->formatName);
}
else
{
dstFormatId = format->formatId;
}
if (bSuccess && altFormatId) if (bSuccess && dstFormatId)
{ {
DstSize = 0; DstSize = 0;
pDstData = (BYTE*) ClipboardGetData(clipboard->system, altFormatId, &DstSize); pDstData = (BYTE*) ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
} }
if (!pDstData) if (!pDstData)
@ -508,8 +531,8 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent*
int fmt; int fmt;
Atom type; Atom type;
UINT32 formatId; UINT32 formatId;
const char* formatName;
XEvent* respond; XEvent* respond;
UINT32 altFormatId;
BYTE* data = NULL; BYTE* data = NULL;
BOOL delayRespond; BOOL delayRespond;
unsigned long length; unsigned long length;
@ -553,7 +576,7 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent*
if (format && (xevent->xselectionrequest.requestor != xfc->drawable)) if (format && (xevent->xselectionrequest.requestor != xfc->drawable))
{ {
formatId = format->formatId; formatId = format->formatId;
altFormatId = formatId; formatName = format->formatName;
if (formatId == CF_RAW) if (formatId == CF_RAW)
{ {
@ -566,12 +589,12 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent*
if (data) if (data)
{ {
CopyMemory(&altFormatId, data, 4); CopyMemory(&formatId, data, 4);
XFree(data); XFree(data);
} }
} }
if ((clipboard->data != 0) && (formatId == clipboard->data_format) && (altFormatId == clipboard->data_alt_format)) if ((clipboard->data != 0) && (formatId == clipboard->data_format_id) && (formatName == clipboard->data_format_name))
{ {
/* Cached clipboard data available. Send it now */ /* Cached clipboard data available. Send it now */
respond->xselection.property = xevent->xselectionrequest.property; respond->xselection.property = xevent->xselectionrequest.property;
@ -595,11 +618,11 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, XEvent*
respond->xselection.property = xevent->xselectionrequest.property; respond->xselection.property = xevent->xselectionrequest.property;
clipboard->respond = respond; clipboard->respond = respond;
clipboard->data_format = formatId; clipboard->data_format_id = formatId;
clipboard->data_alt_format = altFormatId; clipboard->data_format_name = formatName;
delayRespond = TRUE; delayRespond = TRUE;
xf_cliprdr_send_data_request(clipboard, altFormatId); xf_cliprdr_send_data_request(clipboard, formatId);
} }
} }
} }
@ -869,6 +892,9 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context, CLIPRDR
clipboard->data = NULL; clipboard->data = NULL;
} }
clipboard->data_format_id = -1;
clipboard->data_format_name = NULL;
if (clipboard->serverFormats) if (clipboard->serverFormats)
{ {
for (i = 0; i < clipboard->numServerFormats; i++) for (i = 0; i < clipboard->numServerFormats; i++)
@ -920,7 +946,7 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context, CLIPRDR
for (j = 0; j < clipboard->numClientFormats; j++) for (j = 0; j < clipboard->numClientFormats; j++)
{ {
if (format->formatId == clipboard->clientFormats[j].formatId) if (xf_cliprdr_formats_equal(format, &clipboard->clientFormats[j]))
{ {
xf_cliprdr_append_target(clipboard, clipboard->clientFormats[j].atom); xf_cliprdr_append_target(clipboard, clipboard->clientFormats[j].atom);
} }
@ -997,9 +1023,8 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext* context
BYTE* pDstData; BYTE* pDstData;
UINT32 DstSize; UINT32 DstSize;
UINT32 SrcSize; UINT32 SrcSize;
UINT32 formatId; UINT32 srcFormatId;
UINT32 altFormatId; UINT32 dstFormatId;
CLIPRDR_FORMAT* format;
BOOL nullTerminated = FALSE; BOOL nullTerminated = FALSE;
UINT32 size = formatDataResponse->dataLen; UINT32 size = formatDataResponse->dataLen;
BYTE* data = formatDataResponse->requestedFormatData; BYTE* data = formatDataResponse->requestedFormatData;
@ -1009,8 +1034,6 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext* context
if (!clipboard->respond) if (!clipboard->respond)
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
format = xf_cliprdr_get_server_format_by_id(clipboard, clipboard->requestedFormatId);
if (clipboard->data) if (clipboard->data)
{ {
free(clipboard->data); free(clipboard->data);
@ -1020,40 +1043,46 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext* context
pDstData = NULL; pDstData = NULL;
DstSize = 0; DstSize = 0;
formatId = 0; srcFormatId = 0;
altFormatId = 0; dstFormatId = 0;
switch (clipboard->data_format) if (clipboard->data_format_name)
{
if (strcmp(clipboard->data_format_name, "HTML Format") == 0)
{
srcFormatId = ClipboardGetFormatId(clipboard->system, "HTML Format");
dstFormatId = ClipboardGetFormatId(clipboard->system, "text/html");
nullTerminated = TRUE;
}
}
else
{
switch (clipboard->data_format_id)
{ {
case CF_TEXT: case CF_TEXT:
formatId = CF_TEXT; srcFormatId = CF_TEXT;
altFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING"); dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE; nullTerminated = TRUE;
break; break;
case CF_OEMTEXT: case CF_OEMTEXT:
formatId = CF_OEMTEXT; srcFormatId = CF_OEMTEXT;
altFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING"); dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE; nullTerminated = TRUE;
break; break;
case CF_UNICODETEXT: case CF_UNICODETEXT:
formatId = CF_UNICODETEXT; srcFormatId = CF_UNICODETEXT;
altFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING"); dstFormatId = ClipboardGetFormatId(clipboard->system, "UTF8_STRING");
nullTerminated = TRUE; nullTerminated = TRUE;
break; break;
case CF_DIB: case CF_DIB:
formatId = CF_DIB; srcFormatId = CF_DIB;
altFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp"); dstFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
break;
case CB_FORMAT_HTML:
formatId = ClipboardGetFormatId(clipboard->system, "HTML Format");
altFormatId = ClipboardGetFormatId(clipboard->system, "text/html");
nullTerminated = TRUE;
break; break;
} }
}
SrcSize = (UINT32) size; SrcSize = (UINT32) size;
pSrcData = (BYTE*) malloc(SrcSize); pSrcData = (BYTE*) malloc(SrcSize);
@ -1063,15 +1092,15 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext* context
CopyMemory(pSrcData, data, SrcSize); CopyMemory(pSrcData, data, SrcSize);
bSuccess = ClipboardSetData(clipboard->system, formatId, (void*) pSrcData, SrcSize); bSuccess = ClipboardSetData(clipboard->system, srcFormatId, (void*) pSrcData, SrcSize);
if (!bSuccess) if (!bSuccess)
free (pSrcData); free (pSrcData);
if (bSuccess && altFormatId) if (bSuccess && dstFormatId)
{ {
DstSize = 0; DstSize = 0;
pDstData = (BYTE*) ClipboardGetData(clipboard->system, altFormatId, &DstSize); pDstData = (BYTE*) ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
if ((DstSize > 1) && nullTerminated) if ((DstSize > 1) && nullTerminated)
DstSize--; DstSize--;