Можно ли узнать источник (приложение) буфера обмена?

10

Я заметил, что иногда содержимое буфера обмена становится недоступным, если исходное приложение (откуда контент был скопирован) закрыто.

Это заставляет меня задуматься, возможно ли узнать, что такое исходное приложение (например, по PID).

Почему? Если исходное приложение является терминалом, я хотел бы найти рабочий каталог терминала, если копируемое содержимое является относительным путем, чтобы создать полный путь к файлу.

К вашему сведению, в настоящее время я использую xclip для определения содержимого буфера обмена, например

xclip -selection primary -t STRING -o 2> /dev/null
Джефф Уорд
источник
2
XGetSelectionOwner(3)получает идентификатор окна владельца выбора. Из которого вы можете пройти вверх по дереву окон, чтобы попытаться найти окно со свойством _NET_WM_PID, например, с xprop(при условии, что это окно исходит от локального клиента, который устанавливает это свойство). xwininfo -root -tree | less +/0x<that-id>может быть достаточно для идентификации приложения.
Стефан Шазелас
2
Что сказал @ StéphaneChazelas. Но имейте в виду, что вы вряд ли получите надежный PID другого клиента из X11. Помня, что X-клиенты подключаются к X-серверам через общие сетевые подключения (сокет UNIX или TCP-сокет), PID может быть бессмысленным, поскольку приложение может быть не локальным. Он может быть подключен через TCP (в настоящее время более не распространен) или через соединение X11 с пересылкой по SSH (более распространено).
Селада
Спасибо за примечания - я предполагаю, что мне нужно будет написать код на C, чтобы получить доступ к XGetSelectionOwner? Я, вероятно, могу сделать это - я отправлю обратно, когда я найду решение.
Джефф Уорд

Ответы:

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;
}
jschlichtholz
источник
Отличное начало, спасибо! Хм, он работает с терминалом, firefox и chrome, но выдает «Cannot get свойство WM_NAME» для других, таких как emacs, robomongo и т. Д. Интересно, это та часть, которую Стефан имел в виду «пройти по дереву»?
Джефф Уорд
Я попытался добавить «попробуй родитель, пока не будет найдено свойство WM_NAME» - и это заставило emacs работать, но не robomongo. Интересно. В этом ответе также есть некоторая соответствующая информация для нахождения PID: unix.stackexchange.com/questions/5478/… Интересно, что этот PID (будучи кардинальным?) Одинаков для всех окон «Терминала». Это не сулит ничего хорошего для моего конкретного случая использования, поскольку каждый терминал может находиться в отдельном текущем рабочем каталоге.
Джефф Уорд
Да. Мне не повезло со свойством "_NET_WM_PID", чтобы получить PID, но я надеялся, что вы можете использовать имя в качестве отправной точки.
jschlichtholz
1
@JeffWard Некоторые современные терминальные программы, например, gnome-terminalзапускают только один экземпляр приложения на сеанс, а не один экземпляр на окно терминала, как, например, venerable xterm. Может быть, поэтому вы видите один и тот же PID во всех них? Для не gnome-terminalвы использовали , чтобы иметь возможность отключить эту ошибочную особенность с --disable-factory(нечетным названием опции) , но , по- видимому , что , возможно , больше не будет возможным . В любом случае звучит так, будто вам нужен pwd одного из процессов, работающих внутри терминала, а не самого себя.
Селада,
@Celada - верно, и это имеет смысл - система X Window знает о Windows, не обязательно, что каждая программа выбирает с ними делать. Также кажется, что в Chrome есть отдельное окно (или процесс?), Предназначенное для буфера обмена. Видимо есть много схем, и моя идея может не сработать.
Джефф Уорд