web-dev-qa-db-fra.com

Comment obtenir une fenêtre X11 à partir d'un ID de processus?

Sous Linux, mon application C++ utilise fork () et execv () pour lancer plusieurs instances d'OpenOffice afin d'afficher certains diaporamas PowerPoint. Cette partie fonctionne.

Ensuite, je veux pouvoir déplacer les fenêtres OpenOffice vers des emplacements spécifiques sur l'écran. Je peux le faire avec la fonction XMoveResizeWindow () mais je dois trouver la fenêtre pour chaque instance.

J'ai l'ID de processus de chaque instance, comment puis-je trouver la fenêtre X11 à partir de cela?


MISE À JOUR - Grâce à la suggestion d'Andy, j'ai réussi cela. Je poste le code ici pour le partager avec la communauté Stack Overflow.

Malheureusement, Open Office ne semble pas définir la propriété _NET_WM_PID, donc cela ne résout pas mon problème, mais il répond à la question.

// Attempt to identify a window by name or attribute.
// by Adam Pierce <[email protected]>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <list>

using namespace std;

class WindowsMatchingPid
{
public:
    WindowsMatchingPid(Display *display, Window wRoot, unsigned long pid)
        : _display(display)
        , _pid(pid)
    {
    // Get the PID property atom.
        _atomPID = XInternAtom(display, "_NET_WM_PID", True);
        if(_atomPID == None)
        {
            cout << "No such atom" << endl;
            return;
        }

        search(wRoot);
    }

    const list<Window> &result() const { return _result; }

private:
    unsigned long  _pid;
    Atom           _atomPID;
    Display       *_display;
    list<Window>   _result;

    void search(Window w)
    {
    // Get the PID for the current Window.
        Atom           type;
        int            format;
        unsigned long  nItems;
        unsigned long  bytesAfter;
        unsigned char *propPID = 0;
        if(Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL,
                                         &type, &format, &nItems, &bytesAfter, &propPID))
        {
            if(propPID != 0)
            {
            // If the PID matches, add this window to the result set.
                if(_pid == *((unsigned long *)propPID))
                    _result.Push_back(w);

                XFree(propPID);
            }
        }

    // Recurse into child windows.
        Window    wRoot;
        Window    wParent;
        Window   *wChild;
        unsigned  nChildren;
        if(0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren))
        {
            for(unsigned i = 0; i < nChildren; i++)
                search(wChild[i]);
        }
    }
};

int main(int argc, char **argv)
{
    if(argc < 2)
        return 1;

    int pid = atoi(argv[1]);
    cout << "Searching for windows associated with PID " << pid << endl;

// Start with the root window.
    Display *display = XOpenDisplay(0);

    WindowsMatchingPid match(display, XDefaultRootWindow(display), pid);

// Print the result.
    const list<Window> &result = match.result();
    for(list<Window>::const_iterator it = result.begin(); it != result.end(); it++)
        cout << "Window #" << (unsigned long)(*it) << endl;

    return 0;
}
51
Adam Pierce

La seule façon que je sache de faire est de parcourir l'arborescence des fenêtres jusqu'à ce que vous trouviez ce que vous cherchez. La traversée n'est pas difficile (voyez ce que fait xwininfo -root -tree en regardant xwininfo.c si vous avez besoin d'un exemple).

Mais comment identifiez-vous la fenêtre que vous recherchez? Certains les applications définissent une propriété de fenêtre appelée _NET_WM_PID.

Je crois que OpenOffice est l'une des applications qui définit cette propriété (comme le font la plupart des applications Gnome), donc vous avez de la chance.

22
andy

Vérifiez si/proc/PID/environ contient une variable appelée WINDOWID

12
hoho

Un peu tard pour la fête. Cependant: en 2004, Harald Welte a publié un extrait de code qui encapsule l'appel XCreateWindow () via LD_PRELOAD et stocke l'ID de processus dans _NET_WM_PID. Cela garantit que chaque fenêtre créée a une entrée PID.

http://www.mail-archive.com/[email protected]/msg05806.html

10
Raphael Wimmer

Essayez d'installer xdotool, puis:

#!/bin/bash
# --any and --name present only as a work-around, see: https://github.com/jordansissel/xdotool/issues/14
ids=$(xdotool search --any --pid "$1" --name "dummy")

Je reçois beaucoup d'identifiants. Je l'utilise pour définir une fenêtre de terminal comme urgente lorsqu'elle est effectuée avec une commande longue, avec le programme seturgent. Je viens de parcourir tous les identifiants que j'obtiens de xdotool et d'exécuter seturgent dessus.

3
Gauthier

Il n'y a pas de bon moyen. Les seules vraies options que je vois sont:

  1. Vous pouvez regarder autour dans l'espace d'adressage du processus pour trouver les informations de connexion et l'ID de fenêtre.
  2. Vous pouvez essayer d'utiliser netstat ou lsof ou ipcs pour mapper les connexions au Xserver, puis (en quelque sorte! Vous aurez besoin de root au moins) regardez ses informations de connexion pour les trouver.
  3. Lorsque vous générez une instance, vous pouvez attendre qu'une autre fenêtre soit mappée, supposez que c'est la bonne et continuez.
2
wnoise

Êtes-vous sûr de disposer de l'ID de processus de chaque instance? Mon expérience avec OOo a consisté à essayer d'exécuter une deuxième instance de OOo ne fait que converser avec la première instance d'OOo, et lui dit d'ouvrir le fichier supplémentaire .

Je pense que vous allez avoir besoin d'utiliser les capacités d'envoi de messages de X pour lui demander gentiment sa fenêtre. J'espère que OOo documente ses couvertures quelque part.

1
Tanktalus

Si vous utilisez python, j'ai trouvé un moyen ici , l'idée vient de BurntSushi

Si vous avez lancé l'application, vous devez connaître sa chaîne cmd, avec laquelle vous pouvez réduire les appels à xprop, vous pouvez toujours parcourir tous les xids et vérifier si le pid est le même que le pid que vous voulez

import subprocess
import re

import struct
import xcffib as xcb
import xcffib.xproto

def get_property_value(property_reply):
    assert isinstance(property_reply, xcb.xproto.GetPropertyReply)

    if property_reply.format == 8:
        if 0 in property_reply.value:
            ret = []
            s = ''
            for o in property_reply.value:
                if o == 0:
                    ret.append(s)
                    s = ''
                else:
                    s += chr(o)
        else:
            ret = str(property_reply.value.buf())

        return ret
    Elif property_reply.format in (16, 32):
        return list(struct.unpack('I' * property_reply.value_len,
                                  property_reply.value.buf()))

    return None

def getProperty(connection, ident, propertyName):

    propertyType = eval(' xcb.xproto.Atom.%s' % propertyName)

    try:
        return connection.core.GetProperty(False, ident, propertyType,
                                        xcb.xproto.GetPropertyType.Any,
                                        0, 2 ** 32 - 1)
    except:
        return None


c = xcb.connect()
root = c.get_setup().roots[0].root

_NET_CLIENT_LIST = c.core.InternAtom(True, len('_NET_CLIENT_LIST'),
                                     '_NET_CLIENT_LIST').reply().atom


raw_clientlist = c.core.GetProperty(False, root, _NET_CLIENT_LIST,
                                    xcb.xproto.GetPropertyType.Any,
                                    0, 2 ** 32 - 1).reply()

clientlist = get_property_value(raw_clientlist)

cookies = {}

for ident in clientlist:
    wm_command = getProperty(c, ident, 'WM_COMMAND')
    cookies[ident] = (wm_command)

xids=[]

for ident in cookies:
    cmd = get_property_value(cookies[ident].reply())
    if cmd and spref in cmd:
        xids.append(ident)

for xid in xids:
    pid = subprocess.check_output('xprop -id %s _NET_WM_PID' % xid, Shell=True)
    pid = re.search('(?<=\s=\s)\d+', pid).group()

    if int(pid) == self.pid:
        print 'found pid:', pid
        break

print 'your xid:', xid
0
Shuman

J'ai pris la liberté de réimplémenter le code de l'OP en utilisant certaines fonctionnalités C++ modernes. Il conserve les mêmes fonctionnalités mais je pense qu'il se lit un peu mieux. De plus, il ne fuit pas même si l'insertion de vecteur se produit.

// Attempt to identify a window by name or attribute.
// originally written by Adam Pierce <[email protected]>
// revised by Dario Pellegrini <[email protected]>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <iostream>
#include <vector>


std::vector<Window> pid2windows(pid_t pid, Display* display, Window w) {
  struct implementation {
    struct FreeWrapRAII {
      void * data;
      FreeWrapRAII(void * data): data(data) {}
      ~FreeWrapRAII(){ XFree(data); }
    };

    std::vector<Window> result;
    pid_t pid;
    Display* display;
    Atom atomPID;

    implementation(pid_t pid, Display* display): pid(pid), display(display) {
      // Get the PID property atom
      atomPID = XInternAtom(display, "_NET_WM_PID", True);
      if(atomPID == None) {
        throw std::runtime_error("pid2windows: no such atom");
      }
    }

    std::vector<Window> getChildren(Window w) {
      Window    wRoot;
      Window    wParent;
      Window   *wChild;
      unsigned  nChildren;
      std::vector<Window> children;
      if(0 != XQueryTree(display, w, &wRoot, &wParent, &wChild, &nChildren)) {
        FreeWrapRAII tmp( wChild );
        children.insert(children.end(), wChild, wChild+nChildren);
      }
      return children;
    }

    void emplaceIfMatches(Window w) {
      // Get the PID for the given Window
      Atom           type;
      int            format;
      unsigned long  nItems;
      unsigned long  bytesAfter;
      unsigned char *propPID = 0;
      if(Success == XGetWindowProperty(display, w, atomPID, 0, 1, False, XA_CARDINAL,
                                       &type, &format, &nItems, &bytesAfter, &propPID)) {
        if(propPID != 0) {
          FreeWrapRAII tmp( propPID );
          if(pid == *reinterpret_cast<pid_t*>(propPID)) {
            result.emplace_back(w);
          }
        }
      }
    }

    void recurse( Window w) {
      emplaceIfMatches(w);
      for (auto & child: getChildren(w)) {
        recurse(child);
      }
    }

    std::vector<Window> operator()( Window w ) {
      result.clear();
      recurse(w);
      return result;
    }
  };
  //back to pid2windows function
  return implementation{pid, display}(w);
}

std::vector<Window> pid2windows(const size_t pid, Display* display) {
  return pid2windows(pid, display, XDefaultRootWindow(display));
}


int main(int argc, char **argv) {
  if(argc < 2)
    return 1;

  int pid = atoi(argv[1]);
  std::cout << "Searching for windows associated with PID " << pid << std::endl;

  // Start with the root window.
  Display *display = XOpenDisplay(0);
  auto res = pid2windows(pid, display);

  // Print the result.
  for( auto & w: res) {
    std::cout << "Window #" << static_cast<unsigned long>(w) << std::endl;
  }

  XCloseDisplay(display);
  return 0;
}
0
DarioP