是否可以知道剪贴板的来源(应用程序)?


10

我注意到有时如果关闭了源应用程序(从中复制了内容的应用程序),剪贴板中的内容将不可用。

这使我想知道是否有可能知道源应用程序是什么(例如通过PID)。

为什么?如果源应用程序是终端,则在复制的内容是相对路径的情况下,我想找到终端的工作目录,以构建文件的完整路径。

仅供参考,我目前正在使用xclip确定剪贴板内容,例如

xclip -selection primary -t STRING -o 2> /dev/null

2
XGetSelectionOwner(3)获取所选内容所有者的窗口ID。您可以从中走出窗口树,尝试查找具有_NET_WM_PID属性的窗口xprop(例如,假设该窗口来自设置该属性的本地客户端)。xwininfo -root -tree | less +/0x<that-id>可能足以识别该应用程序。
斯特凡Chazelas

2
@StéphaneChazelas说了什么。但是请注意,您不太可能从X11中获得其他客户端的可靠PID。记住X客户端通过通用网络连接(UNIX套接字或TCP套接字)连接到X服务器,PID可能没有意义,因为该应用程序可能不是本地的。它可能通过TCP(这些天不再常见)或SSH转发的X11连接(更常见)进行连接。
Celada

感谢您的注释-我想我需要编写一些C代码才能访问XGetSelectionOwner?我可能可以这样做-当我找到解决方案时,我会回发。
杰夫·沃德

Answers:


5

我编写了一个返回普通应用程序名称的工具(例如,我测试过的“ Terminal”,“ gedit”或“ SmartGit”)。大多数代码都是在这里从@Harvey偷偷偷走的。

// 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;
}

很好的开始,谢谢!嗯,它可以与终端,Firefox和chrome一起使用,但是会为emacs和robomongo等其他对象抛出“无法获取WM_NAME属性”。我想知道这是否是Stéphane所指的“走树”部分。
杰夫·沃德

尝试添加 “尝试父项,直到找到WM_NAME属性”,这使emacs正常运行,尽管不是robomongo。有趣。此答案还具有一些有关查找PID的相关信息:unix.stackexchange.com/questions/5478 / ...有趣的是,此PID(是否为基数?)对于所有“ Terminal”窗口均相同。对于我的特定用例来说,这并不是一个好兆头,因为每个终端可能位于单独的当前工作目录中。
杰夫·沃德

是。我不太喜欢使用“ _NET_WM_PID”属性来获取PID,但是我希望您可以使用该名称作为起点。
jschlichtholz

1
@JeffWard一些现代的终端程序gnome-terminal,例如每个会话仅启动一次应用程序实例,而不是像venerable那样每个终端窗口仅启动一个实例xterm。也许这就是为什么您在所有这些代码中都看到相同的PID?因为gnome-terminal您过去曾经能够使用--disable-factory(选项的奇数名称)禁用该功能,但是显然不再可能。无论如何,听起来您需要在终端内部运行的进程之一的密码,而不是本身。
Celada

@Celada-对,这很有意义-X窗口系统知道窗口,不一定知道每个程序选择使用它们做什么。Chrome似乎也有专门用于剪贴板的单独窗口(或过程?)。显然有很多模式,我的想法可能不会成功。
杰夫·沃德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.