Est-il possible de connaître la source (application) du presse-papiers?

10

J'ai remarqué que le contenu du presse-papiers devient parfois indisponible si l'application source (d'où le contenu a été copié) est fermée.

Cela m'amène à me demander s'il est possible de savoir quelle est l'application source (par exemple peut-être par PID).

Pourquoi? Si l'application source est un terminal, j'aimerais trouver le répertoire de travail du terminal, au cas où le contenu copié est un chemin relatif, pour construire un chemin complet vers un fichier.

Pour info, j'utilise actuellement xclip pour déterminer le contenu du presse-papiers, par exemple

xclip -selection primary -t STRING -o 2> /dev/null
Jeff Ward
la source
2
XGetSelectionOwner(3)vous donne l'ID de fenêtre du propriétaire de la sélection. À partir de laquelle vous pouvez parcourir l'arborescence de la fenêtre pour essayer de trouver une fenêtre avec une propriété _NET_WM_PID par exemple avec xprop(en supposant que la fenêtre provient d'un client local qui définit cette propriété). xwininfo -root -tree | less +/0x<that-id>peut être suffisant pour identifier l'application.
Stéphane Chazelas
2
Ce que @ StéphaneChazelas a dit. Mais sachez qu'il est peu probable que vous obteniez un PID fiable de l'autre client à partir de X11. En se souvenant que les clients X se connectent à des serveurs X via des connexions réseau génériques (socket UNIX ou socket TCP), un PID peut être vide de sens car l'application peut ne pas être locale. Il peut être connecté via TCP (ce n'est plus courant de nos jours) ou une connexion X11 transmise par SSH (plus courante).
Celada
Merci pour les notes - je suppose que je devrai écrire du code C pour accéder à XGetSelectionOwner alors? Je peux probablement le faire - je posterai quand j'arriverai à une solution.
Jeff Ward

Réponses:

5

J'ai écrit un outil qui renvoie le nom simple de l'application (par exemple 'Terminal', 'gedit' ou 'SmartGit' qui sont ceux que j'ai testés). La plupart du code est volé sans vergogne à @Harvey ici .

// 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
la source
Un bon début, merci! Hmm, cela fonctionne avec terminal, firefox et chrome, mais lance "Impossible d'obtenir la propriété WM_NAME" pour d'autres comme emacs et robomongo, etc. Je me demande si c'est la partie "marcher dans l'arbre" à laquelle Stéphane faisait référence.
Jeff Ward
J'ai essayé d'ajouter un "essai parent jusqu'à ce que la propriété WM_NAME soit trouvée" - et cela a fait fonctionner emacs, mais pas robomongo. Intéressant. Cette réponse contient également des informations pertinentes pour trouver le PID: unix.stackexchange.com/questions/5478/… Fait intéressant, ce PID (étant cardinal?) Est le même pour toutes les fenêtres "Terminal". Cela n'augure rien de bon pour mon cas d'utilisation spécifique, car chaque terminal peut se trouver dans un répertoire de travail actuel distinct.
Jeff Ward
Oui. Je n'ai pas eu de chance avec la propriété "_NET_WM_PID" pour obtenir le PID mais j'espérais que vous pourriez utiliser le nom comme point de départ.
jschlichtholz
1
@JeffWard certains programmes de terminaux modernes comme gnome-terminaldémarrer une seule fois l'instance de l'application par session au lieu d'une instance par fenêtre de terminal comme venerable xterm. C'est peut-être pourquoi vous voyez le même PID dans chacun d'eux? Car gnome-terminalvous pouviez auparavant désactiver cette erreur avec --disable-factory(nom impair pour une option) mais apparemment, cela pourrait ne plus être possible . Quoi qu'il en soit, il semble que vous ayez besoin de la puissance d'un des processus en cours d'exécution à l'intérieur du terminal, pas de lui-même.
Celada
@Celada - à droite, et c'est logique - le système X Window connaît les fenêtres, pas nécessairement ce que chaque programme choisit d'en faire. Il semble également que Chrome dispose d'une fenêtre (ou d'un processus?) Distincte dédiée au presse-papiers. Apparemment, il existe de nombreux schémas, et mon idée peut ne pas se concrétiser.
Jeff Ward