xf_cliprdr: fill in support for TIMESTAMP requests.
authorSimon Tatham <anakin@pobox.com>
Fri, 13 Mar 2020 17:59:22 +0000 (17:59 +0000)
committerakallabeth <akallabeth@users.noreply.github.com>
Thu, 25 Feb 2021 08:51:41 +0000 (09:51 +0100)
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.

(cherry picked from commit fcabbc9707e23b94d7e82021e997578fb20c9313)

client/X11/xf_cliprdr.c

index b7d6b23..7f178d8 100644 (file)
@@ -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;
 
@@ -112,6 +115,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)
 {
@@ -778,6 +782,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)
 {
@@ -869,7 +884,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 */
        {
@@ -984,6 +1001,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 */
 
@@ -1204,6 +1231,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
  *
@@ -1287,8 +1351,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;
 }
 
@@ -1643,6 +1706,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);