12

I have the PID for the process (and the name), I want to bring it to the front on linux (ubuntu). On mac I would simply do SetFrontProcess(pid), on windows I'd enumerate the windows, pick out the one I wanted, and call SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); but I'm at a loss of what to do on linux. I've looked at X Lib a bit, but most/all of those functions seem to operate on windows inside your process.


Edit: Using bdk's answer I added these helpers to my code to get the Window

bool searchHelper(Display* display, Window w, Atom& atomPID, unsigned long pid, Window& result)
{
    bool ret = false;

    Atom atomType;
    int format;
    unsigned long nItems;
    unsigned long bytesAfter;
    unsigned char* propPID = 0;
    if (Success == XGetWindowProperty(display,w,atomPID,0,1,False,XA_CARDINAL,&atomType,&format,&nItems,&bytesAfter,&propPID))
    {
        if (propPID != 0)
        {
            if (pid == *((unsigned long *)propPID))
            {
                result = w;
                ret = true;
            }
            XFree(propPID);
        }
    }

    if (ret)
        return ret; //we found we can stop

    //check the children of the window
    Window wRoot;
    Window wParent;
    Window *wChild=NULL;
    unsigned nChildren=0;
    if (XQueryTree(display, w, &wRoot, &wParent, &wChild, &nChildren) != 0 )
    {
        for (unsigned i=0; i<nChildren; ++i)
        {
            ret = searchHelper(display, wChild[i], atomPID, pid, result);
            if (ret)
                break;
        }
    }
    return ret;
}

bool getWindowFromPid(unsigned long pid, Display* display, Window& result)
{
    Window window = XDefaultRootWindow(display);
    Atom atomPID = XInternAtom(display, "_NET_WM_PID", true);
    if (atomPID == None)
    {
        qDebug("XInternAtom failure");
        return false;
    }
    return searchHelper(display, window, atomPID, pid, result);
}

Now I get the window successfully, but when I do the following

if (getWindowFromPid(pid,display,window))
{
    qDebug("Found window ID:%d", window);
    int result = XRaiseWindow(display,window);
    qDebug("XRaiseWindow returned:%d", result);
}

XRaiseWindow returns 1 (BadRequest). The documentation for XRaiseWindow does not mention the return code of BadRequest being a possible result. I'm not sure what is wrong. Am I not allowed to call it for windows in a different process? Is this focus steeling prevention hampering me? Any thoughts?

Edit edit:

So looking at what xwininfo.c does when you call it with -frame I changed my code as follows based on bdk's suggestion.

if (getWindowFromPid(pid,display,window))
    {
        qDebug("Found window ID:%d", window);

        //Need the windowmanger frame (or parent) id not window id
        Window root, parent;
        Window *childlist;
        unsigned int ujunk;
        int status = XQueryTree(display, window, &root, &parent, &childlist, &ujunk);
        if (status && parent && parent != root)
        {
            qDebug("Found frame window ID:%d",parent);
            window = parent;
        }

        XSetWindowAttributes xswa;
        xswa.override_redirect=True;
        int result = XChangeWindowAttributes (display,window,CWOverrideRedirect,&xswa);
        qDebug("XChangeWindowAttributes returned:%d", result);
        result = XRaiseWindow(display,window);
        qDebug("XRaiseWindow returned:%d", result);
    }
    else
        qDebug("unable to find the window for the pid");

At this point I do find the window frame ID, but I get a return code of "1" from both XChangeWindowAttributes and XRaiseWindow. Am I just not allowed to modify another process' window?

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Lorenz03Tx
  • 354
  • 1
  • 3
  • 13
  • 1
    You will get better results by asking "How do I bring a processes window to the foreground on X Windows?" – Agnel Kurian May 18 '10 at 14:42
  • @Vulcan thanks, I have edited the question title. (Note original tittle: "How do I bring a processes to the front on Linux (c++)?") – Lorenz03Tx May 18 '10 at 14:46
  • 1
    If you want to get technical, the title is still wrong -- there's no such thing as "X Windows". It's "The X Window System" (but "Window" shouldn't be pluralized). – Jerry Coffin May 18 '10 at 15:00

5 Answers5

12

I had this issue in my application as well, so here's the solution.

To raise the window you need not only to raise it, but you also need to notify the WM about it. The following code could be used:

        // This is how to get it in Qt; if you don't use it,
        // you can call XOpenDisplay and get it from there;
        Display * display = x11Info().display();

        // Main window identifier of your application
        WId win = winId();

        XEvent event = { 0 };
        event.xclient.type = ClientMessage;
        event.xclient.serial = 0;
        event.xclient.send_event = True;
        event.xclient.message_type = XInternAtom( display, "_NET_ACTIVE_WINDOW", False);
        event.xclient.window = win;
        event.xclient.format = 32;

        XSendEvent( display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &event );
        XMapRaised( display, win );
George Y.
  • 11,307
  • 3
  • 24
  • 25
  • Unless I'm missing something this is acting on the root window of your application. i.e. raising yourself. The question was how to raise the window of another process (i.e. not in your application). – Lorenz03Tx Feb 16 '15 at 13:40
  • 1
    Yes, this is correct. But the question title is not that specific - it could be interpreted as how to bring your process window to the front. So I put it here for the people looking for the same, as they might come here same way I did. – George Y. Feb 25 '15 at 04:03
  • 1
    @GeorgeY. This solution worked well for me. I used this logic after finding the associated window of a PID using the answer from https://stackoverflow.com/questions/151407/how-to-get-an-x11-window-from-a-process-id?rq=1. With this additional information, I think this should be the accepted answer. – Snowman6286 Jun 06 '18 at 19:46
7

I haven't tried this myself, but putting these two methods together may work:

The XRaiseWindow API Call in xlib lets you raise a Window to the front if you know the Window ID.

http://www.unix.com/man-page/Linux/3/XRaiseWindow/

This stackoverflow answer explains how to get a Window ID from a PID:

How to get an X11 Window from a Process ID?

EDIT:

I've had limited success with XRaiseWindow. The Following program does work under twm window manager, but not ion which I usually use. The Window Manager must have ways of preventing applications from 'popping up'. To make this work, i also had to pass it the Window ID of the Window Manager's frame for the window, not the window itself. run xwininfo -frame and click on the window and you get the frame ID instead, compile this program with gcc test.c -lX and pass it that hexid on the command line and it will raise the window.

 #include <stdio.h>
 #include <stdlib.h>
 #include <X11/Xlib.h>

 int main(int argc, char **argv)
 {
   Display *dsp = XOpenDisplay(NULL);
   long id = strtol(argv[1], NULL, 16);
   XSetWindowAttributes xswa;
   xswa.override_redirect=True;
   XChangeWindowAttributes (dsp,id,CWOverrideRedirect, &xswa);
   XRaiseWindow ( dsp, id );
   XCloseDisplay ( dsp );
 }
Community
  • 1
  • 1
bdk
  • 4,769
  • 29
  • 33
  • So using that code to enumerate the windows and match for the PID got me the Window no problems. When I pass the window int XRaiseWindow it does nothing and returns 1. 1 == BadRequest.... I'm not sure how that applies as the doc for xRaiseWindow sugest it can return BadWindow (3) but mention nothing of BadRequest. – Lorenz03Tx May 18 '10 at 16:06
  • I've edited my answer above. I think I've gotten past the point where you were hitting trouble, but XRaiseWindow behaviour seems dependant on the Window Manager, so your milage may vary. – bdk May 18 '10 at 18:28
  • Yeah, even with the frame window ID I'm still getting the return code of 1. I'm guessing my window manager isn't letting me muck with other windows... how do I circumvent that? I get focus steeling is bad, but this is actually focus giving. Shouldn't that be allowed? – Lorenz03Tx May 18 '10 at 19:22
  • My window manager is metacity, I guess I'll try changing that and see if I get any different results. – Lorenz03Tx May 18 '10 at 19:38
  • 2
    As per this: http://www.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/5.5/html/Technical_Notes/metacity.html Looks like Metacity does not permit XRaiseWindow from outside the application. It does appear to support _NET_ACTIVE_WINDOW however, which might work for you: http://standards.freedesktop.org/wm-spec/1.3/ar01s03.html – bdk May 18 '10 at 23:56
3

From the bash command line, you could also use the terrific xdotool, which lets you specify the following to raise the XBMC window and type a backslash into it:

xdotool search --name 'XBMC Media Center' windowactivate  --sync key backslash

This program has an actual library under it, libxdo2, which you could use instead if XRaiseWindow fails you. I understand that libxdo goes to some lengths to always raise the window, regardless of windowmanager.

dirkjot
  • 3,467
  • 1
  • 23
  • 17
0

I thought this would be easy since /proc seemingly has the required data but /proc/${pid}/environ doesn't provide the correct window ID since it is usually the child of the parent who really owns the window where the process is running. To get the proper windowid you need to parse the xwininfo output then you can use xdotool to change the focus.

CMD_PID=<your pid here> && while IFS= read -r -d '' var; do 
if grep -q "^WINDOWID=" <<< "$var"; then 
 winid=$(printf '0x%x\n' $(sed 's/WINDOWID=//' <<< "${var}"))
 child_cnt=0 && IFS=$(echo -en "\n\b") && for a in $(xwininfo -root -tree | tac | sed -n "/${winid}/,\$p"); do 
 grep -q -i "children" <<< "${a}" && let child_cnt+=1
 ((${child_cnt} == 2)) && real_winid=$(grep -o '0x[^ ]*' <<< "${last_line}") && break
 last_line="${a}"
 done 
 xdotool windowraise ${real_winid} 
 break 
fi 
done < /proc/${CMD_PID}/environ
0

There is a command line tool that can do this: wmctrl. The source code of the tool is here (C++): wmctrl sources

Relevant part of the code (you will need to get some of the functions used here from the project under the link above):

static int activate_window (Display *disp, Window win, /* {{{ */
    gboolean switch_desktop) {
unsigned long *desktop;

/* desktop ID */
if ((desktop = (unsigned long *)get_property(disp, win,
        XA_CARDINAL, "_NET_WM_DESKTOP", NULL)) == NULL) {
    if ((desktop = (unsigned long *)get_property(disp, win,
            XA_CARDINAL, "_WIN_WORKSPACE", NULL)) == NULL) {
        p_verbose("Cannot find desktop ID of the window.\n");
    }
}

if (switch_desktop && desktop) {
    if (client_msg(disp, DefaultRootWindow(disp), 
                "_NET_CURRENT_DESKTOP", 
                *desktop, 0, 0, 0, 0) != EXIT_SUCCESS) {
        p_verbose("Cannot switch desktop.\n");
    }
    g_free(desktop);
}

client_msg(disp, win, "_NET_ACTIVE_WINDOW", 
        0, 0, 0, 0, 0);
XMapRaised(disp, win);

return EXIT_SUCCESS;
}

Hint: remember to call

XCloseDisplay(display);

Somewhere after the call to this function or you will see no effect.

ed22
  • 1,127
  • 2
  • 14
  • 30