xf_cliprdr: fill in support for TIMESTAMP requests.

A selection owner is supposed to respond to a request for the
selection target TIMESTAMP by providing the X server time at which the
selection was written. There was a /* TODO */ comment in xf_cliprdr
where the code to do that should have been.

The absence of this can cause a problem when pasting into some X
clients. xtightvncviewer, in particular, will give up the attempt to
read from the clipboard at all if it doesn't get a satisfactory
response to the initial TIMESTAMP request - and the non-answer zero
value "CurrentTime" counts as unsatisfactory. It won't be happy with
anything short of a real X server time value.

(Checking the VNC source code, that's because it reads both PRIMARY
and CLIPBOARD and picks the one with the later timestamp. So it does
depend on the timestamps existing.)

When you're writing to the selection in response to a normal X event
like a mouse click or keyboard action, you get the selection timestamp
by copying the time field out of that X event. Here, we're doing it on
our own initiative, so we have to _request_ the X server time. There
isn't a GetServerTime request in the X protocol, so I work around it
by setting a property on our own window, and waiting for a
PropertyNotify event to come back telling me it's been done - which
will have a timestamp we can use.
This commit is contained in:
Simon Tatham 2020-03-13 17:59:22 +00:00 committed by akallabeth
parent be5bd68de0
commit 3c104d9b9b

View File

@ -68,6 +68,9 @@ struct xf_clipboard
Atom clipboard_atom; Atom clipboard_atom;
Atom property_atom; Atom property_atom;
Atom timestamp_property_atom;
Time selection_ownership_timestamp;
Atom raw_transfer_atom; Atom raw_transfer_atom;
Atom raw_format_list_atom; Atom raw_format_list_atom;
@ -111,6 +114,7 @@ struct xf_clipboard
}; };
static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard); static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard);
static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp);
static void xf_cliprdr_check_owner(xfClipboard* clipboard) static void xf_cliprdr_check_owner(xfClipboard* clipboard)
{ {
@ -758,6 +762,17 @@ static void xf_cliprdr_provide_targets(xfClipboard* clipboard, const XSelectionE
} }
} }
static void xf_cliprdr_provide_timestamp(xfClipboard* clipboard, const XSelectionEvent* respond)
{
xfContext* xfc = clipboard->xfc;
if (respond->property != None)
{
XChangeProperty(xfc->display, respond->requestor, respond->property, XA_INTEGER, 32,
PropModeReplace, (BYTE*)&clipboard->selection_ownership_timestamp, 1);
}
}
static void xf_cliprdr_provide_data(xfClipboard* clipboard, const XSelectionEvent* respond, static void xf_cliprdr_provide_data(xfClipboard* clipboard, const XSelectionEvent* respond,
const BYTE* data, UINT32 size) const BYTE* data, UINT32 size)
{ {
@ -849,7 +864,9 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
if (xevent->target == clipboard->targets[0]) /* TIMESTAMP */ if (xevent->target == clipboard->targets[0]) /* TIMESTAMP */
{ {
/* TODO */ /* Someone else requests the selection's timestamp */
respond->property = xevent->property;
xf_cliprdr_provide_timestamp(clipboard, respond);
} }
else if (xevent->target == clipboard->targets[1]) /* TARGETS */ else if (xevent->target == clipboard->targets[1]) /* TARGETS */
{ {
@ -964,6 +981,16 @@ static BOOL xf_cliprdr_process_property_notify(xfClipboard* clipboard, const XPr
xfc = clipboard->xfc; xfc = clipboard->xfc;
if (xevent->atom == clipboard->timestamp_property_atom)
{
/* This is the response to the property change we did
* in xf_cliprdr_prepare_to_set_selection_owner. Now
* we can set ourselves as the selection owner. (See
* comments in those functions below.) */
xf_cliprdr_set_selection_owner(xfc, clipboard, xevent->time);
return TRUE;
}
if (xevent->atom != clipboard->property_atom) if (xevent->atom != clipboard->property_atom)
return FALSE; /* Not cliprdr-related */ return FALSE; /* Not cliprdr-related */
@ -1188,6 +1215,43 @@ static UINT xf_cliprdr_server_capabilities(CliprdrClientContext* context,
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
static void xf_cliprdr_prepare_to_set_selection_owner(xfContext* xfc, xfClipboard* clipboard)
{
/*
* When you're writing to the selection in response to a
* normal X event like a mouse click or keyboard action, you
* get the selection timestamp by copying the time field out
* of that X event. Here, we're doing it on our own
* initiative, so we have to _request_ the X server time.
*
* There isn't a GetServerTime request in the X protocol, so I
* work around it by setting a property on our own window, and
* waiting for a PropertyNotify event to come back telling me
* it's been done - which will have a timestamp we can use.
*/
/* We have to set the property to some value, but it doesn't
* matter what. Set it to its own name, which we have here
* anyway! */
Atom value = clipboard->timestamp_property_atom;
XChangeProperty(xfc->display, xfc->drawable, clipboard->timestamp_property_atom, XA_ATOM, 32,
PropModeReplace, (BYTE*)&value, 1);
XFlush(xfc->display);
}
static void xf_cliprdr_set_selection_owner(xfContext* xfc, xfClipboard* clipboard, Time timestamp)
{
/*
* Actually set ourselves up as the selection owner, now that
* we have a timestamp to use.
*/
clipboard->selection_ownership_timestamp = timestamp;
XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, timestamp);
XFlush(xfc->display);
}
/** /**
* Function description * Function description
* *
@ -1271,8 +1335,7 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context,
} }
ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE); ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, CurrentTime); xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
XFlush(xfc->display);
return ret; return ret;
} }
@ -1627,6 +1690,8 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
goto error; goto error;
} }
clipboard->timestamp_property_atom =
XInternAtom(xfc->display, "_FREERDP_TIMESTAMP_PROPERTY", FALSE);
clipboard->property_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR", FALSE); clipboard->property_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR", FALSE);
clipboard->raw_transfer_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_RAW", FALSE); clipboard->raw_transfer_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_RAW", FALSE);
clipboard->raw_format_list_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_FORMATS", FALSE); clipboard->raw_format_list_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_FORMATS", FALSE);