如何在X Windows上将进程窗口置于前台? (C ++)

时间:2010-05-18 14:40:08

标签: c++ linux

我有进程的PID(和名称),我想把它带到linux(ubuntu)的前面。在Mac上,我只需SetFrontProcess(pid),在Windows上我会枚举窗口,选择我想要的那个,并调用SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);,但我不知道在linux上做什么。我已经看了一下X Lib,但是大多数/所有这些函数似乎都在你进程内的窗口上运行。


编辑:使用bdk的答案我将这些助手添加到我的代码中以获取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);
}

现在我成功了窗口,但是当我执行以下操作时

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

XRaiseWindow返回1(BadRequest)。 XRaiseWindow的文档没有提到BadRequest的返回代码是可能的结果。我不确定是什么问题。我不允许在不同的进程中为它调用它吗?这种重点钢铁预防是否会妨碍我?有什么想法吗?

修改编辑:

所以看看xwininfo.c在用-frame调用它时的作用我根据bdk的建议改变了我的代码。

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");

此时我确实找到了窗口框架ID,但是我从XChangeWindowAttributes和XRaiseWindow获得了返回码“1”。我不允许修改另一个进程的窗口吗?

5 个答案:

答案 0 :(得分:7)

我的申请也遇到了这个问题,所以这就是解决方案。

要提升窗口,您不仅需要提高窗口,还需要通知WM。可以使用以下代码:

        // 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 );

答案 1 :(得分:6)

我自己没有尝试过,但将这两种方法放在一起可能会有效:

如果您知道窗口ID,则xlib中的XRaiseWindow API调用允许您将窗口提升到前面。

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

此stackoverflow答案解释了如何从PID获取Window ID:

How to get an X11 Window from a Process ID?

编辑:

我在XRaiseWindow上的成功有限。以下程序在twm窗口管理器下工作,但不是我通常使用的离子。窗口管理器必须有办法阻止应用程序“弹出”。为了使这个工作,我还必须传递窗口管理器的窗口的窗口ID,而不是窗口本身。运行xwininfo -frame并单击窗口,然后获取帧ID,使用gcc test.c -lX编译该程序,并在命令行上传递该hexid,它将引发窗口。

 #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 );
 }

答案 2 :(得分:3)

从bash命令行,您还可以使用极好的xdotool,它允许您指定以下内容来引发XBMC窗口并在其中键入反斜杠:

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

此程序下面有一个实际的库libxdo2,如果XRaiseWindow失败,您可以使用它。据我所知,无论windowmanager如何,libxdo都会花些时间来提升窗口。

答案 3 :(得分:0)

我认为这很容易,因为/ proc似乎有所需的数据,但是/proc/${pid}/environ没有提供正确的窗口ID,因为它通常是父进程的子进程,它真正拥有进程运行的窗口。要获得正确的windowid,您需要解析xwininfo输出,然后您可以使用xdotool来更改焦点。

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

答案 4 :(得分:0)

有一个命令行工具可以执行此操作:wmctrl。 该工具的源代码在这里(C ++):wmctrl sources

代码的相关部分(您需要从上面的链接下的项目中获得此处使用的一些功能):

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;
}

提示:记得打电话给

XCloseDisplay(display);

调用此函数后的某个地方,否则您将看不到任何效果。