CLIPBOARD selection doesn't save content

Quintus sutniuq at gmx.net
Sat Apr 3 02:09:42 PDT 2010


Hi,

> If you can, try it under gnome too

That's quite easy to do - I'm already working under GNOME, I never tried
KDE.

> as the last time I worked with any
> clipboard code, I found its clipboard manager more cooperative at saving
> non-standard data-types than when testing under KDE.

Is text a non-standard data type? I can't believe this.

> Also, the clipboard manager in gnome will only request the clipboard
> contents and take over from my app when the app closes. We explicitly
> call gtk_clipboard_store() as we tear down the app.

Sounds reasonable, but that's GTK-specific. Since I'm writing a
general-purpose extension, it should work wherever X11 is installed. I
looked at your code, and then tried to integrate this into my function
by only using Xlib's functions. This is how it looks now:
-----------------------------------------------------------
/*
*call-seq:
*  Clipboard.write(clipboard, text) ==> nil
*
*Writes +text+ to the specified clipboard.
*/
static VALUE m_write(VALUE self, VALUE rclipboard, VALUE rtext) /*VALUE
is a Ruby object*/
{
  Display * p_display;
  Window win;
  XEvent xevt, xevt2;
  Atom targets[4];
  int res;

  /*Open default display*/
  p_display = XOpenDisplay(NULL);
  /*Let Ruby handle protocol errors*/
  XSetErrorHandler(handle_x_errors);
  /*This are the TARGETS we support*/
  targets[0] = XInternAtom(p_display, "TARGETS", True);
  targets[1] = UTF8_ATOM;
  targets[2] = XA_STRING;
  targets[3] = XInternAtom(p_display, "TIMESTAMP", True); /*TODO:
Implement this request*/

  if (CLIPBOARD_MANAGER_ATOM == None)
    printf("FAIL\n");

  /*Output some useful information*/
  printf("PID: %i\n", getpid());
  printf("Owner of CLIPBOARD_MANAGER: %lu\n",
XGetSelectionOwner(p_display, CLIPBOARD_MANAGER_ATOM));

  /*Create a window for copying into CLIPBOARD*/
  win = CREATE_REQUESTOR_WIN;

  if (XGetSelectionOwner(p_display, CLIPBOARD_MANAGER_ATOM) == None)
    rb_raise(XError, "No owner for the CLIPBOARD_MANAGER selection!");
/*Throw Ruby error*/

  res = XChangeProperty(p_display, win, CLIPBOARD_ATOM, XA_ATOM, 32,
PropModeReplace, (unsigned char *) targets, 4);
  printf("res = %i\n", res);
  res = XConvertSelection(p_display, CLIPBOARD_MANAGER_ATOM,
SAVE_TARGETS_ATOM, CLIPBOARD_ATOM, win, CurrentTime);
  printf("res = %i\n", res);
  /*Get control of the CLIPBOARD*/
  XSetSelectionOwner(p_display, CLIPBOARD_ATOM, win, CurrentTime);
  for (;;)
  {
    XNextEvent(p_display, &xevt);
    if (xevt.type == SelectionRequest)
    {
      printf("SelectionRequest event; Requested target: %s\n",
XGetAtomName(p_display, xevt.xselectionrequest.target));
      printf("Requestor: %lu\n", xevt.xselectionrequest.requestor);
    }
    else if (xevt.type == SelectionNotify)
    {
      printf("SelectionNotify event; property: %s\n",
XGetAtomName(p_display, xevt.xselection.property));
      printf("Requestor: %lu\n", xevt.xselection.requestor);
    }
    if (xevt.type == SelectionRequest) /*selection-related event*/
    {
      if (xevt.xselectionrequest.target == XInternAtom(p_display,
"TARGETS", True)) /*TARGETS information requested*/
      {
        /*Write supported TARGETS into the requestor*/
        XChangeProperty(p_display, xevt.xselectionrequest.requestor,
xevt.xselectionrequest.property, XA_ATOM, 32, PropModeReplace, (unsigned
char *) targets, 4);
        /*Notify the requestor we have set its requested property*/
        xevt2.xselection.type = SelectionNotify;
        xevt2.xselection.display = xevt.xselectionrequest.display;
        xevt2.xselection.requestor = xevt.xselectionrequest.requestor;
        xevt2.xselection.selection = xevt.xselectionrequest.selection;
        xevt2.xselection.target = xevt.xselectionrequest.target;
        xevt2.xselection.time = xevt.xselectionrequest.time;
        xevt2.xselection.property = xevt.xselectionrequest.property;

        XSendEvent(p_display, xevt.xselectionrequest.requestor, False,
NoEventMask, &xevt2);
      }
      else if (xevt.xselectionrequest.target == UTF8_ATOM ||
xevt.xselectionrequest.target == XA_STRING) /*UTF-8 or ASCII requested*/
      {
        /*Write the string "TEST" into the requestor*/
        XChangeProperty(p_display, xevt.xselectionrequest.requestor,
xevt.xselectionrequest.property, xevt.xselectionrequest.target, 8,
PropModeReplace, (unsigned char *) "TEST", 4);
        /*Notify the requestor we've finished*/
        xevt2.xselection.type = SelectionNotify;
        xevt2.xselection.display = xevt.xselectionrequest.display;
        xevt2.xselection.requestor = xevt.xselectionrequest.requestor;
        xevt2.xselection.selection = xevt.xselectionrequest.selection;
        xevt2.xselection.target = xevt.xselectionrequest.target;
        xevt2.xselection.time = xevt.xselectionrequest.time;
        xevt2.xselection.property = xevt.xselectionrequest.property;

        XSendEvent(p_display, xevt.xselectionrequest.requestor, False,
NoEventMask, &xevt2);
        break; /*For debugging, prevents infinite loop*/
      }
      else /*No supported request*/
      {
        /*Notify the requestor we don't support what it wants*/
        xevt2.xselection.type = SelectionNotify;
        xevt2.xselection.display = xevt.xselectionrequest.display;
        xevt2.xselection.requestor = xevt.xselectionrequest.requestor;
        xevt2.xselection.selection = xevt.xselectionrequest.selection;
        xevt2.xselection.target = None;
        xevt2.xselection.time = xevt.xselectionrequest.time;
        xevt2.xselection.property = xevt.xselectionrequest.property;

        XSendEvent(p_display, xevt.xselectionrequest.requestor, False,
NoEventMask, &xevt2);
      }
    }
  }

  /*Cleanup actions*/
  XSetErrorHandler(NULL);
  XCloseDisplay(p_display);

  /*Return Ruby nil object*/
  return Qnil;
}
------------------------------------------------

Maybe I don't get the idea, but when I call XConvertSelection() I'm
always sent a SelectionNotify event as a completion notify. Have I to
react to this in any way besides checking for success? I don't think so,
but then... What's achieved with the call? As far as I understood, you
first check wheather there is a clipoard manager. If so, you change the
CLIPBOARD property of my window to the list of supported targets. Why?
In the call to XConvertSelection() you request that the
CLIPBOARD_MANAGER selection is converted to SAVE_TARGETS which then is
written to my window as the CLIPBOARD property. This doesn't make any
sense to me, CLIPBOARD_MANAGER holds a text string, and not a list of
targets and therefore doesn't request the clipboard manager to take
responsibility for the CLIPBOARD selection.
...is at least my interpretation of your code correct?

However, it does not work. The output I get this time is:
------------------------------------------------
PID: 2868
Owner of CLIPBOARD_MANAGER: 18874372
res = 1
res = 1
SelectionRequest event; Requested target: MULTIPLE
Requestor: 18874372
SelectionRequest event; Requested target: TARGETS
Requestor: 33554613
SelectionRequest event; Requested target: TARGETS
Requestor: 33558513
SelectionRequest event; Requested target: TARGETS
Requestor: 33558514
SelectionNotify event; property: CLIPBOARD
Requestor: 88080385
Killed
--------------------------------------------------
As you can see, I had to kill the process since it never completed the
for loop. I'm quite sure I'm only missing a little detail, but what is
it? And where does the MULTIPLE request suddenly come from?

You see, I'm confused. :-/ Sorry for those many questions, but when I
write code I want to understand every single line.
Please stay with me! :-)

Marvin

Peter Clifton schrieb:
> Take a look at the clipboard manager spec. here:
> 
> http://www.freedesktop.org/wiki/ClipboardManager
> 

PS: Yes, that one was useful and cleared up some things for me, but by
far not all.



More information about the xorg mailing list