Under Linux, my C++ application is using fork() and execv() to launch multiple instances of OpenOffice so as to view some powerpoint slide shows. This part works.
Ne
There is no good way. The only real options I see, are:
If you use python, I found a way here, the idea is from BurntSushi
If you launched the application, then you should know its cmd string, with which you can reduce calls to xprop
, you can always loop through all the xids and check if the pid is the same as the pid you want
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
I took the freedom to re-implement the OP's code using some modern C++ features. It maintains the same functionalities but I think that it reads a bit better. Also it does not leak even if the vector insertion happens to throw.
// 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;
}
Are you sure you have the process ID of each instance? My experience with OOo has been that trying to run a second instance of OOo merely converses with the first instance of OOo, and tells that to open the additional file.
I think you're going to need to use the message-sending capabilities of X to ask it nicely for its window. I would hope that OOo documents its coversations somewhere.
Try installing xdotool
, then:
#!/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")
I do get a lot of ids. I use this to set a terminal window as urgent when it is done with a long command, with the program seturgent
. I just loop through all the ids I get from xdotool
and run seturgent
on them.
The only way I know to do this is to traverse the tree of windows until you find what you're looking for. Traversing isn't hard (just see what xwininfo -root -tree does by looking at xwininfo.c if you need an example).
But how do you identify the window you are looking for? Some applications set a window property called _NET_WM_PID.
I believe that OpenOffice is one of the applications that sets that property (as do most Gnome apps), so you're in luck.