2010-05-18 14 views
10

Tengo el PID para el proceso (y el nombre), quiero llevarlo al frente en Linux (ubuntu). En mac simplemente haría SetFrontProcess(pid), en Windows enumeraría las ventanas, elegiría la que yo quería, y llamaría al SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); pero no sabía qué hacer en Linux. He analizado un poco a X Lib, pero la mayoría o todas esas funciones parecen funcionar en ventanas dentro de tu proceso.¿Cómo traigo una ventana de procesos al primer plano en X Windows? (C++)


Editar: Usando la respuesta de bdk añadí estos ayudantes a mi código para obtener la ventana

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

Ahora me sale la ventana con éxito, pero cuando lo haga la siguiente

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

XRaiseWindow devuelve 1 (BadRequest). La documentación de XRaiseWindow no menciona el código de retorno de BadRequest como posible resultado. No estoy seguro de lo que está mal. ¿No se me permite llamar a Windows en un proceso diferente? ¿Esta prevención de acero de enfoque me está obstaculizando? ¿Alguna idea?

Editar editar:

Así que mirar lo que xwininfo.c hace cuando se le llama con -frame he cambiado de código como sigue basado en la sugerencia de 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"); 

En este punto hago encontrar el ID del marco de la ventana, pero me da un código de retorno de "1" de ambos XChangeWindowAttributes y XRaiseWindow. ¿Acaso no puedo modificar la ventana de otro proceso?

+1

Obtendrá mejores resultados preguntando "¿Cómo traigo una ventana de procesos al primer plano en X Windows?" –

+0

@Vulcan gracias, he editado el título de la pregunta. (Tenga en cuenta la tilde original: "¿Cómo puedo traer un proceso al frente en Linux (C++)?") – Lorenz03Tx

+0

Si desea obtener una versión técnica, el título sigue estando mal; no existe tal cosa como "X Windows". Es "El sistema X Window" (pero "Ventana" no debe estar pluralizado). –

Respuesta

6

No he probado esto por mí mismo, pero poniendo estos dos métodos juntos pueden trabajar:

El XRaiseWindow llamada de API en xlib le permite elevar una ventana en la parte delantera si se conoce el ID de ventana.

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

Esta respuesta stackoverflow explica cómo obtener un identificador de ventana de una PID:

How to get an X11 Window from a Process ID?

EDIT:

que he tenido un éxito limitado con XRaiseWindow. El siguiente programa funciona bajo el administrador de ventanas twm, pero no es el que suelo usar. Window Manager debe tener formas de evitar que las aplicaciones aparezcan. Para que esto funcione, también tuve que pasarle la Id. De la ventana del marco del Administrador de ventanas para la ventana, no la ventana misma. ejecuta xwininfo -frame y haz clic en la ventana y obtienes la ID de marco en su lugar, compila este programa con gcc test.c -lX y le pasa ese hexid en la línea de comando y se levantará la ventana.

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

Entonces, al usar ese código para enumerar las ventanas y hacer coincidir el PID no recibí la Ventana. Cuando paso la ventana int XRaiseWindow no hace nada y devuelve 1. 1 == BadRequest .... No estoy seguro de cómo se aplica eso como el documento para xRaiseWindow sugest puede devolver BadWindow (3) pero no menciona nada de BadRequest. – Lorenz03Tx

+0

He editado mi respuesta anterior. Creo que ya pasé el punto en el que estaba teniendo problemas, pero el comportamiento de XRaiseWindow parece depender del Administrador de ventanas, por lo que su kilometraje puede variar. – bdk

+0

Sí, incluso con la ID de la ventana de fotograma, sigo obteniendo el código de retorno de 1. Supongo que mi administrador de ventanas no me deja confundirme con otras ventanas ... ¿cómo puedo eludir eso? Obtengo que enfocar el acero es malo, pero esto en realidad es dar enfoque. ¿No debería eso estar permitido? – Lorenz03Tx

2

Desde la línea de comandos bash, también se puede utilizar la excelente xdotool, que le permite especificar lo siguiente para subir la ventana XBMC y escriba una barra invertida en él:

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

Este programa tiene una biblioteca real debajo de él, libxdo2, que podría usar en su lugar si XRaiseWindow le falla. Entiendo que libxdo hace todo lo posible para siempre subir la ventana, independientemente de windowmanager.

3

Tuve este problema en mi aplicación también, así que aquí está la solución.

Para levantar la ventana no solo tiene que levantarla, sino que también debe notificarlo al WM. El siguiente código se podría utilizar:

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

A menos que me falta algo, esto está actuando en la ventana raíz de su aplicación . es decir, levantándose a sí mismo. La pregunta era cómo elevar la ventana de otro proceso (es decir, no en su aplicación). – Lorenz03Tx

+0

Sí, esto es correcto. Pero el título de la pregunta no es tan específico; podría interpretarse como cómo llevar la ventana de proceso al frente. Así que lo puse aquí para las personas que buscan lo mismo, ya que podrían venir aquí de la misma manera que yo. –

0

pensé que esto sería fácil, ya que/proc aparentemente tiene los datos necesarios, pero /proc/${pid}/environ no facilita la ventana de la identidad correcta ya que suele ser el niño del padre que es realmente el dueño la ventana donde se ejecuta el proceso Para obtener el windowid correcto, debe analizar la salida xwininfo, luego puede usar xdotool para cambiar el enfoque.

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 
Cuestiones relacionadas