10

I've noticed that sometimes clipboard contents become unavailable if the source application (where the content was copied from) is closed.

This leads me to wonder if it's possible to know what the source application is (e.g. perhaps by PID).

Why? If the source application is a terminal, I'd like to find the working directory of the terminal, in case the copied contents is a relative path, to construct a full path to a file.

FYI, I'm currently using xclip to determine the clipboard contents, e.g.

xclip -selection primary -t STRING -o 2> /dev/null
  • 2
    XGetSelectionOwner(3) gets you the window id of the owner of the selection. From which you can walk up the window tree to try and find a window with a _NET_WM_PID property for instance with xprop (assuming that window comes from a local client that sets that property). xwininfo -root -tree | less +/0x<that-id> may be enough to identify the application. – Stéphane Chazelas Jun 22 '15 at 21:46
  • 2
    What @StéphaneChazelas said. But be aware that you are unlikely to get a reliable PID of the other client out of X11. Remembering that X clients connect to X servers through generic network connections (UNIX socket or TCP socket), a PID might be meaningless since the application might not be local. It might be connected over TCP (not common anymore these days) or an SSH-forwarded X11 connection (more common). – Celada Jun 23 '15 at 2:10
  • Thanks for the notes -- I assume I'll need to write some C code to access XGetSelectionOwner then? I can probably do that - I'll post back when I get to a solution. – Jeff Ward Jun 23 '15 at 17:42
4

I wrote a tool which returns the plain application name (e.g. 'Terminal', 'gedit' or 'SmartGit' which are the ones I tested). Most code is shamelessly stolen from @Harvey here.

// gcc clipboard-owner.c -lX11 -o clipboard-owner

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>

#define MAX_PROPERTY_VALUE_LEN 4096

typedef unsigned long ulong;

static char *get_property(Display *, Window, Atom , const char *, ulong *);

int main(void)
{
  // Open the Display
  Display *display = XOpenDisplay(NULL);

  // Get the selection window
  Window selection_owner = XGetSelectionOwner(display, XA_PRIMARY);

  if(!selection_owner) {
    exit(0);
  } else {
      char *window_name = get_property(display, selection_owner, XA_STRING, "WM_NAME", NULL);
      printf("%s\n", window_name);
  }

  XCloseDisplay(display);
}

static char *get_property (Display *disp, Window win,
        Atom xa_prop_type, const char *prop_name, ulong *size) {
    Atom xa_prop_name;
    Atom xa_ret_type;
    int ret_format;
    ulong ret_nitems;
    ulong ret_bytes_after;
    ulong tmp_size;
    unsigned char *ret_prop;
    char *ret;

    xa_prop_name = XInternAtom(disp, prop_name, False);

    if (XGetWindowProperty(disp, win, xa_prop_name, 0,
            MAX_PROPERTY_VALUE_LEN / 4, False,
            xa_prop_type, &xa_ret_type, &ret_format,     
            &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
        printf("Cannot get %s property.\n", prop_name);
        return NULL;
    }

    if (xa_ret_type != xa_prop_type) {
        printf("Invalid type of %s property.\n", prop_name);
        XFree(ret_prop);
        return NULL;
    }

    /* null terminate the result to make string handling easier */
    tmp_size = (ret_format / 8) * ret_nitems;
    /* Correct 64 Architecture implementation of 32 bit data */
    if(ret_format==32) tmp_size *= sizeof(long)/4;
    ret = (char *)malloc(tmp_size + 1);
    memcpy(ret, ret_prop, tmp_size);
    ret[tmp_size] = '\0';

    if (size) {
        *size = tmp_size;
    }

    XFree(ret_prop);
    return ret;
}
  • A great start, thanks! Hmm, it works with terminal, firefox, and chrome, but throws "Cannot get WM_NAME property" for others like emacs and robomongo, etc. I wonder if that's the "walk the tree" part Stéphane was referring to. – Jeff Ward Jun 29 '15 at 16:37
  • I tried adding a "try parent until WM_NAME property is found" -- and that made emacs work, though not robomongo. Interesting. This answer also has some relevant info for finding the PID: unix.stackexchange.com/questions/5478/… Interestingly this PID (being cardinal?) is the same for all "Terminal" windows. This doesn't bode well for my specific use case, since each terminal may be in a separate current working directory. – Jeff Ward Jun 29 '15 at 17:06
  • Yes. I had no luck with the "_NET_WM_PID" property to get the PID but I hoped you could use the name as a starting point. – jschlichtholz Jun 29 '15 at 17:22
  • 1
    @JeffWard some modern terminal programs like gnome-terminal start only once instance of the application per session instead of one instance per terminal window like venerable xterm. Maybe that's why you are seeing the same PID in all of them? For gnome-terminal you used to be able to disable that misfeature with --disable-factory (odd name for an option) but apparently that might no longer be possible. Anyway, sounds like you need the pwd of one of the processes running inside the terminal, not of itself. – Celada Jun 29 '15 at 23:53
  • @Celada - right, and it makes sense -- the X window system knows about windows, not necessarily what each program chooses to do with them. It also appears that Chrome has a separate window (or process?) dedicated to the clipboard. Apparently there are many schemata, and my idea may not pan out. – Jeff Ward Jun 30 '15 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.