Как перенести окно процессов на передний план в X Windows? (С++)

У меня есть PID для процесса (и имя), я хочу вывести его на передний план на linux (ubuntu). На mac я просто сделал бы SetFrontProcess(pid), на окнах я бы перечислил окна, выделил тот, который я хотел, и позвонил SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);, но я не понимаю, что делать с linux. Я немного посмотрел на X Lib, но большинство/все эти функции, похоже, работают с окнами внутри вашего процесса.


Изменить: используя ответ bdk Я добавил эти помощники в свой код, чтобы получить окно

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

В этот момент я нахожу идентификатор оконного фрейма, но получаю код возврата "1" из XChangeWindowAttributes и XRaiseWindow. Мне просто не разрешено изменять другое окно процесса?

Ответ 1

Я не пробовал это сам, но совместное использование этих двух методов может работать:

API XRaiseWindow Вызов в xlib позволяет вам поднимать окно на передний план, если вы знаете идентификатор окна.

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

В этом ответе stackoverflow объясняется, как получить идентификатор окна из PID:

Как получить окно X11 из идентификатора процесса?

EDIT:

У меня был ограниченный успех с XRaiseWindow. Следующая программа работает под управлением диспетчера окон twm, но не иона, который я обычно использую. У диспетчера окон должны быть способы предотвращения появления "всплывающих" приложений. Чтобы выполнить эту работу, я также должен был передать ей Window ID рамки Window Manager для окна, а не самого окна. запустите xwininfo -frame и щелкните по окну, и вместо этого вы получите идентификатор кадра, скомпилируйте эту программу с помощью 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

У меня была эта проблема и в моем приложении, так что вот решение.

Чтобы поднять окно, вам нужно не только поднять его, но также нужно уведомить об этом 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 );

Ответ 3

В командной строке bash вы также можете использовать потрясающий xdotool, который позволяет вам указать следующее, чтобы поднять окно XBMC и ввести в него обратную косую черту:

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

У этой программы есть фактическая библиотека под ней, libxdo2, которую вы могли бы использовать, если XRaiseWindow вас не сработает. Я понимаю, что libxdo идет на несколько длин, чтобы всегда поднимать окно независимо от оконного менеджера.

Ответ 4

Я думал, что это будет легко, так как у /proc, по-видимому, есть требуемые данные, но /proc/${pid}/environ не предоставляет правильный идентификатор окна, так как он обычно является дочерним элементом родителя, который действительно владеет окном, в котором выполняется этот процесс. Чтобы получить правильный 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