19
votes

I have to monitor the X11 Clipboard.

For the moment, I request the ClipBoard Selection each 5 seconds, then I hash the text returned from clipboard and I compare it with the hash calculate from the last check. If hash are not the same, I analysis the text content and do some stuff...

I don't like my method. I'm from Windows, and with the winapi, it is the kernel that notify your program when the clipboard has changed, and it's more efficient!

I just want to know if it is possible that X11 can notify your program as winapi when the clipboard has changed ? What is the more efficient way to check clipboard modifications with X11 ?

4

4 Answers

11
votes

Use XFixesSelectSelectionInput() from Xfixes extension and wait for XFixesSelectionNotify event.

Example:

// gcc -o xclipwatch xclipwatch.c -lX11 -lXfixes
...
#include <X11/extensions/Xfixes.h>
...
void WatchSelection(Display *display, Window window, const char *bufname)
{
  int event_base, error_base;
  XEvent event;
  Atom bufid = XInternAtom(display, bufname, False);

  assert( XFixesQueryExtension(display, &event_base, &error_base) );
  XFixesSelectSelectionInput(display, DefaultRootWindow(display), bufid, XFixesSetSelectionOwnerNotifyMask);

  while (True)
  {
    XNextEvent(display, &event);

    if (event.type == event_base + XFixesSelectionNotify &&
        ((XFixesSelectionNotifyEvent*)&event)->selection == bufid)
    {
      if (!PrintSelection(display, window, bufname, "UTF8_STRING"))
        PrintSelection(display, window, bufname, "STRING");

      fflush(stdout);
    }
  }
}
...

This works both for bufname == "CLIPBOARD" and bufname == "PRIMARY" selection.

Also see PrintSelection() function in this answer.

1
votes
  1. Find window with selection using GetSelectionOwner (PRIMARY and CLIPBOARD)
  2. get copy of selection by sending SelectionRequest, notify your application
  3. watch for SelectionClear event
  4. update window with selection using id from SelectionClear event, goto step 2
0
votes

The accepted answer from x11user is a good one. But you probably want a non-blocking while loop, and for that you can take that answer, and adapt it like this.

// get the internal X11 event file descriptor
int x11fd = ConnectionNumber(display);

while(!shutdown)
{
  if(!XPending(display)) {
    // wait on the file descriptor
    // you can use poll, epoll, select, eventfd, etc.
  }

  XNextEvent(display, &event);
  // process the event
}