2009-07-21 16 views
11

Me gustaría interceptar el mensaje WM_DELETE_WINDOW que se publica en una determinada selección de ventanas que una aplicación que estoy escribiendo (AllTray), para que pueda actuar sobre él en lugar de la aplicación que lo recibe. Actualmente estoy tratando de probar esto en el nivel GDK via gdk_display_add_client_message_filter si es posible, pero estaría contento con una solución Xlib si hay una también; es parece posible, pero parece que no entiendo cómo lo voy a hacer con éxito.¿Interceptar WM_DELETE_WINDOW en X11?

Actualmente, tengo dos programas (escrito en C) que yo estoy tratando de utilizar para obtener esta resuelto, the first one no hace más que crear una ventana y registro que se sabe acerca de WM_DELETE_WINDOW y the second one intentos de atrapar ese mensaje, pero parece fallar al hacerlo; parece que no hace precisamente nada. ¿Estoy entendiendo mal la documentación sobre esto, o hay algo adicional que debo hacer (o debo evitar usar GDK por completo)?

El trasfondo es el siguiente: antes de volver a escribir AllTray, la forma en que haría las cosas parece ser intentar interceptar un clic del mouse sobre el botón X. Para algunos administradores de ventanas, esto funcionó correctamente, para otros no funcionó en absoluto, y para otros, el usuario tuvo que configurarlo manualmente e indicar a AllTray dónde estaba el botón para cerrar la ventana. Lo que estoy buscando es una solución que no implique un LD_LIBRARY_PRELOAD y funcionará para cualquier administrador de ventanas/combinación de aplicaciones que cumpla con los estándares actuales y envíe un WM_DELETE_WINDOW ClientMessage cuando se cierre la ventana.

ACTUALIZACIÓN: Todavía estoy buscando una respuesta. La ruta que estoy tomando en este momento es intentar reparar la ventana y administrarla yo mismo, pero simplemente no puedo hacer que funcione. Después de la reparación, parece que no puedo recuperarlo de ninguna manera. Puede que me esté perdiendo algo muy fundamental, pero no puedo imaginar cómo hacerlo aparecer como mi propia ventana nuevamente, para volver a mostrarla en la pantalla.

ACTUALIZACIÓN 2: Muy bien, así que he golpeado otra pared de ladrillo. La documentación del servidor X dice que establezca StructureNotifyMask en la máscara de eventos de la ventana para recibir los eventos MapNotify y ReparentNotify. Me interesaría recibir cualquiera. Mi pensamiento actual era crear una ventana que sirviera solo como un receptor de eventos, y luego cuando obtengo eventos para cosas interesantes, actúo sobre ellos creando y reparentándolos. Sin embargo, esto simplemente no parece estar funcionando. Los únicos eventos que realmente recibo son eventos de PropertyNotify. Entonces, esta ruta tampoco parece estar haciendo demasiado bien.

+0

Creo que podría ser posible reparenting la ventana dentro de su propio toplevel, y filtrar los eventos que pasa? No creo que la forma en que estás tratando actualmente pueda funcionar. – wrt

+0

¿Hay algún inconveniente para hacerlo de esa manera? Es decir, ¿hay algo en particular que pueda interferir con cosas como XDND o lo que sea? ¿Es una idea portátil (como en, no romperá aplicaciones o administradores de ventanas)? Parece que puedo encontrar muy poca información sobre eso. Supongo que también significa que tendré que crear una nueva ventana "principal" para cada nueva ventana del cliente, ¿correcto? –

Respuesta

1

Desafortunadamente, la mejor respuesta a esta pregunta es una serie de no respuestas; no son técnicamente maneras de lograr, pero todos ellos tienen desventajas que las hacen muy poco práctica:

  1. Crear un proxy para una aplicación X11, pasando todos los mensajes de protocolo X11 de ida y vuelta entre la aplicación y el servidor X. El proxy filtrará los mensajes interesantes. La desventaja de esto es que se trata de una gran cantidad de sobrecarga para una pequeña característica pequeña, y el protocolo X11 es complejo. También podría haber consecuencias involuntarias, lo que hace que esta sea una opción aún menos atractiva.
  2. Inicie como una aplicación estándar que actúa como intermediario entre el administrador de ventanas y las aplicaciones de cliente "interesantes". Esto rompe algunas cosas, como XDnD. En efecto, no es diferente a la primera opción, excepto que el proxy se encuentra en el nivel de la ventana en oposición al nivel de protocolo X11.
  3. Utilice el truco de la biblioteca no portátil LD_PRELOAD. Esto tiene varios inconvenientes:
    1. a que no es portable a través de enlazadores dinámica: no todos los enlazadores soporte dinámico LD_PRELOAD, incluso entre los sistemas tipo UNIX.
    2. No es portátil en todos los sistemas operativos: no todos los sistemas operativos admiten enlazadores dinámicos con características.
    3. Rompe la transparencia de red: la biblioteca de enlace de objeto compartido/dinámico debe residir en el host como el proceso secundario que se está ejecutando.
    4. No todas las aplicaciones X11 usan Xlib; sería necesario escribir un módulo LD_PRELOAD para cada una de las bibliotecas que una aplicación podría usar para hablar con X11.
    5. Además del último punto, no todas las aplicaciones serían susceptibles de LD_PRELOAD incluso si se ejecutaran bajo un enlazador que lo admitiera, porque no pueden usar un objeto compartido o DLL para comunicarse con X; considere, por ejemplo, una aplicación Java que utiliza una biblioteca de protocolos X11 escrita en Java.
    6. En algunos sistemas operativos tipo UNIX, las bibliotecas LD_PRELOAD deben ser setuid/setgid si se van a usar con programas setuid/setgid. Esto es, por supuesto, una vulnerabilidad de seguridad potencial.
    7. Estoy bastante seguro de que hay más desventajas que no se me ocurren.
  4. Implemente una extensión del sistema X Window. No portátil entre las implementaciones de X11, complejo y complicado como todos salen, y absolutamente fuera de toda duda.
  5. Implementa extensiones o complementos para administradores de ventanas. Hay tantos administradores de ventanas como opiniones sobre los administradores de ventanas, y por lo tanto esto es completamente inviable.

En última instancia, finalmente logré mi objetivo utilizando un mecanismo completamente separado; cualquiera que esté interesado, consulte el soporte de Close-to-Tray en AllTray 0.7.5.1dev y posterior, incluido the git master branch available on github.

11

No sé X11, pero busqué en Google usando "Intercept WM_DELETE_WINDOW X11" como palabras clave. Encontrados 17k - MarkMail y Mplayer-commits r154 - trunk/libvo. En ambos casos, están haciendo lo mismo.

/* This is used to intercept window closing requests. */ 
static Atom wm_delete_window; 

dentro static void x11_init(),

XMapWindow(display, win); 
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); 
XSetWMProtocols(display, win, &wm_delete_window, 1); 

a continuación, dentro de static int x11_check_events(),

XEvent Event; 
while (XPending(display)) { 
    XNextEvent(display, &Event); 
    if (Event.type == ClientMessage) { 
     if ((Atom)Event.xclient.data.l[0] == wm_delete_window) { 
      /* your code here */ 
     } 
    } 
} 

Ver XInternAtom, XSetWMProtocols y XNextEvent.

Después de escribir lo anterior, encontré Handling window close in an X11 app:

Cuando un usuario hace clic en el botón de cierre [x] en nuestra aplicación X11 queremos que para que aparezca aa diálogo que pregunta “¿te realmente quiere dejar de fumar ? ". Esta es una aplicación simple X. No hay sofisticados widgets GTK o QT aquí. Entonces, ¿cómo atrapar el mensaje "window is being closed"?

La respuesta es decirle al gestor de ventanas estamos interesados ​​en estos evento llamando XSetWMProtocols y registrar un mensaje WM_DELETE_WINDOW con él. Entonces obtendremos un mensaje de cliente del Administrador de ventanas si alguien intenta cerrar la ventana y no lo cerrará, nos dejará en manos de nosotros. Aquí hay un ejemplo ...

// example.cpp 
#include <X11/Xlib.h> 
#include <X11/Xatom.h> 
#include <iostream> 

int main() 
{ 
    Display* display = XOpenDisplay(NULL); 
    Window window = XCreateSimpleWindow(display, 
             DefaultRootWindow(display), 
             0, 0, 
             500, 400, 
             0, 
             0, 0); 

    // register interest in the delete window message 
    Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); 
    XSetWMProtocols(display, window, &wmDeleteMessage, 1); 

    std::cout << "Starting up..." << std::endl; 
    XMapWindow(display, window); 

    while (true) { 
     XEvent event; 
     XNextEvent(display, &event); 

     if (event.type == ClientMessage && 
      event.xclient.data.l[0] == wmDeleteMessage) { 
     std::cout << "Shutting down now!!!" << std::endl; 
     break; 
     } 
    } 

    XCloseDisplay(display); 
    return 0; 
} 
+0

Eso sería genial ... si solo resolviera mi problema. El problema no es que poseo una ventana y quiero recibir el evento; que puedo hacerlo bien (y que haría una de las formas anteriores). El problema es que mi aplicación necesita interceptar eso para otras ventanas. Windows que no son míos, que no creé. Todavía estoy tratando de descubrir qué quiso decir el primer comentarista al reparar por la fuerza las ventanas, pero todavía tengo que hacerlo funcionar. He estado buscando en Google por días. Es por eso que vine aquí. –

+0

Supongo que ya ha leído http://tronche.com/gui/x/xlib/window-and-session-manager/XReparentWindow.html. –

+0

De hecho, tengo. Todavía no estoy seguro de qué hacer para que funcione correctamente; Me las arreglé para obtener ventanas nuevas, pero me falta algo porque no puedo lograr mi objetivo de hacer que las cosas funcionen. : -/De hecho, tengo la referencia Xlib en mi escritorio, que he estado revisando repetidamente para tratar de resolver esto. : -/ –

0

Ok, para elaborar en mi sugerencia anterior, es posible que desee investigar XEmbed. Al menos, eso podría darte algunas ideas para probar.

De lo contrario, me gustaría ver cómo podría estar funcionando otro software similar (por ejemplo, wmdock, o cómo se implementa GtkPlug/GtkSocket), aunque creo que en ambos casos se requiere soporte explícito en las aplicaciones.

Espero que sea más útil.

+0

Experimenté con esto en un punto; sin embargo, fue muy, muy poco confiable. Era tan poco confiable como para ser inadecuado para un programa de producción. Afortunadamente, encontré otra forma de lograr el objetivo final que tenía en mente. –

0

Debe leer ICCCM que le indica cómo el administrador de ventanas se comunica con el cliente. La mayoría de WM creará una ventana de marco para contener su ventana de nivel superior a través de la reparación. Por lo tanto, si su reparent puede romper la relación conocida por WM y la ventana de su cliente.

+1

Gracias. He leído el ICCCM y un poco más sobre X11. Reparar no es una opción sin un * lote * de trabajo. El viejo AllTray hizo trucos de reparación, que rompieron muchas cosas en el proceso; por ejemplo, XDnD. –