From fcabbc9707e23b94d7e82021e997578fb20c9313 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 13 Mar 2020 17:59:22 +0000 Subject: [PATCH] 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. --- client/X11/xf_cliprdr.c | 71 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index 21017db00..f49142774 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -68,6 +68,9 @@ struct xf_clipboard Atom clipboard_atom; Atom property_atom; + Atom timestamp_property_atom; + Time selection_ownership_timestamp; + Atom raw_transfer_atom; Atom raw_format_list_atom; @@ -111,6 +114,7 @@ struct xf_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) { @@ -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, 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 */ { - /* 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 */ { @@ -964,6 +981,16 @@ static BOOL xf_cliprdr_process_property_notify(xfClipboard* clipboard, const XPr 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) return FALSE; /* Not cliprdr-related */ @@ -1187,6 +1214,43 @@ static UINT xf_cliprdr_server_capabilities(CliprdrClientContext* context, 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 * @@ -1270,8 +1334,7 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context, } ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE); - XSetSelectionOwner(xfc->display, clipboard->clipboard_atom, xfc->drawable, CurrentTime); - XFlush(xfc->display); + xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard); return ret; } @@ -1626,6 +1689,8 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) goto error; } + clipboard->timestamp_property_atom = + XInternAtom(xfc->display, "_FREERDP_TIMESTAMP_PROPERTY", FALSE); clipboard->property_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR", FALSE); clipboard->raw_transfer_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_RAW", FALSE); clipboard->raw_format_list_atom = XInternAtom(xfc->display, "_FREERDP_CLIPRDR_FORMATS", FALSE);