CLIPBOARD selection doesn't save content

Quintus sutniuq at gmx.net
Fri Apr 2 14:08:47 PDT 2010


Hi there,

OK, this is kind of a longer story. Since ~4 weeks I'm trying to create
a C extension for the Ruby programming language (I'm sure you don't have
to know Ruby to answer this question, since it's almost C code) that
allows to read from and write to the X clipboards. I've searched through
the web many, many times, but wasn't able to find any reasonable example
code on how to do this, and finally I looked into the source code of the
xsel and xclip commands which helped me quite a lot. With this, and with
further web research and search through the X11 specification and other
documents, I was able to get the reading part up and running, for the
PRIMARY, SECONDARY and CLIPBOARD selections. Now, I'm trying to
implement writing, only for the CLIPBOARD selection, since that one's
contents should persist across program death. I know, I have to acquire
CLIPBOARD's ownership; afterwards the xclipboard process would send me a
TARGETS request asking me what data I provide. I answer this with
TARGETS, UTF8_STRING, XA_STRING and TIMESTAMP, which should cause
xclipboard to ask me for UTF8_STRING or XA_STRING, but... It doesn't.
Instead, if I try to paste the copied text into an application, my
program gets a request from that application rather than from
xclipboard. Further more, when my program finishes, the data is gone. So
I suppose, something is wrong at my side.

My system is an Ubuntu Karmic machine,
uname -a:
Linux ikarus 2.6.31-20-generic #58-Ubuntu SMP Fri Mar 12 04:38:19 UTC
2010 x86_64 GNU/Linux

gcc --version:
gcc (Ubuntu 4.4.1-4ubuntu9) 4.4.1
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Here's my C function binding the write functionality to Ruby:
-----------------------------------------
/*
*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; /*xevt2 is our response*/
  Atom targets[4];

  /*As long as we're debugging, ignore the parameters*/

  /*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*/

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

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

  /*Get control of the CLIPBOARD*/
  XSetSelectionOwner(p_display, CLIPBOARD_ATOM, win, CurrentTime);
  for (;;)
  {
    XNextEvent(p_display, &xevt);
    printf("Event received; Requested target: %s\n",
XGetAtomName(p_display, xevt.xselectionrequest.target));
    printf("Requestor: %lu\n", xevt.xselectionrequest.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;
}
--------------------------------
Where the CREATE_REQUESTOR_WIN macro is defined as

XCreateSimpleWindow(p_display, XDefaultRootWindow(p_display), 0, 0, 100,
100, 0, 0, 0)

and the UTF8_ATOM as

XInternAtom(p_display, "UTF8_STRING", True)

and the CLIPBOARD_ATOM as

CLIPBOARD_ATOM XInternAtom(p_display, "CLIPBOARD", True)

. When I execute this (from Ruby) and, during the for loop running, open
gedit and then right-click and choose "paste" I get my text into gedit.
Then the C function termintes (due to the debugging break) and when I
try to paste again, the data is gone. The output I get from my program
is as follows:
-----------------------------------------
PID: 6393
Owner of CLIPBOARD: 0
Event received; Requested target: TARGETS
Requestor: 33554613
Event received; Requested target: TARGETS
Requestor: 33566616
Event received; Requested target: TARGETS
Requestor: 33566617
Event received; Requested target: TARGETS
Requestor: 33566618
Event received; Requested target: TARGETS
Requestor: 33566619
Event received; Requested target: TARGETS
Requestor: 100663425
Event received; Requested target: TARGETS
Requestor: 100663425
Event received; Requested target: GTK_TEXT_BUFFER_CONTENTS
Requestor: 100663425
Event received; Requested target: TARGETS
Requestor: 100663425
Event received; Requested target: UTF8_STRING
Requestor: 100663425
=> nil
-------------------------------------------
The last line just shows the C function's return value in Ruby, which I
have defined to be nil.

What's happening? Where's my mistake?

Marvin



More information about the xorg mailing list