¿Cómo obtener una ventana X11 de una ID de proceso?
-
02-07-2019 - |
Pregunta
En Linux, mi aplicación C ++ está usando fork () y execv () para lanzar varias instancias de OpenOffice para ver algunas presentaciones de diapositivas de PowerPoint. Esta parte funciona.
A continuación, quiero poder mover las ventanas de OpenOffice a ubicaciones específicas en la pantalla. Puedo hacerlo con la función XMoveResizeWindow () pero necesito encontrar la ventana para cada instancia.
Tengo el ID de proceso de cada instancia, ¿cómo puedo encontrar la ventana X11 a partir de eso?
ACTUALIZACIÓN - Gracias a la sugerencia de Andy, he logrado esto. Estoy publicando el código aquí para compartirlo con la comunidad de Stack Overflow.
Desafortunadamente, Open Office no parece establecer la propiedad _NET_WM_PID, por lo que esto en última instancia no resuelve mi problema pero sí responde la pregunta.
// Attempt to identify a window by name or attribute.
// by Adam Pierce <adam@doctort.org>
#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;
}
Solución
La única forma en que sé hacer esto es atravesar el árbol de ventanas hasta que encuentre lo que está buscando. Recorrer no es difícil (solo vea lo que hace xwininfo -root -tree mirando xwininfo.c si necesita un ejemplo).
Pero, ¿cómo identificas la ventana que estás buscando? Algunas aplicaciones establecen una propiedad de ventana llamada _NET_WM_PID.
Creo que OpenOffice es una de las aplicaciones que establece esa propiedad (como lo hacen la mayoría de las aplicaciones de Gnome), así que estás de suerte.
Otros consejos
Verifique si / proc / PID / environmental contiene una variable llamada WINDOWID
Un poco tarde para la fiesta. Sin embargo: En 2004, Harald Welte publicó un fragmento de código que envuelve la llamada XCreateWindow () a través de LD_PRELOAD y almacena la identificación del proceso en _NET_WM_PID. Esto asegura que cada ventana creada tenga una entrada PID.
http://www.mail-archive.com/devel@xfree86.org/msg05806 .html
Intente instalar xdotool
, luego:
#!/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")
Tengo muchos identificadores. Lo uso para configurar una ventana de terminal como urgente cuando se hace con un comando largo, con el programa seturgent
. Simplemente recorro todos los identificadores que obtengo de xdotool
y ejecuto seturgent
en ellos.
No hay una buena manera. Las únicas opciones reales que veo son:
- Puede buscar en el espacio de direcciones del proceso para encontrar la información de conexión y la ID de la ventana.
- Podría intentar usar netstat o lsof o ipcs para asignar las conexiones al servidor X, y luego (de alguna manera necesitará root al menos) ver su información de conexión para encontrarlas.
- Al generar una instancia, puede esperar hasta que se asigne otra ventana, asumir que es la correcta y `seguir adelante.
¿Está seguro de que tiene el ID de proceso de cada instancia? Mi experiencia con OOo ha sido que intentar ejecutar una segunda instancia de OOo simplemente conversa con la primera instancia de OOo y le dice que abra el archivo adicional.
Creo que necesitará usar las capacidades de envío de mensajes de X para pedirle su ventana. Espero que OOo documente sus cubiertas en alguna parte.
Si usa Python, encontré una manera aquí , la idea es de BurntSushi
Si inició la aplicación, entonces debe conocer su cadena cmd, con la que puede reducir las llamadas a xprop
, siempre puede recorrer todos los xids y verificar si el pid es el mismo que el pid que quieras
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
Me tomé la libertad de volver a implementar el código del OP usando algunas características modernas de C ++. Mantiene las mismas funcionalidades pero creo que se lee un poco mejor. Además, no se filtra incluso si la inserción del vector se produce.
// Attempt to identify a window by name or attribute.
// originally written by Adam Pierce <adam@doctort.org>
// revised by Dario Pellegrini <pellegrini.dario@gmail.com>
#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;
}