¿Cómo salgo con gracia de un bucle de eventos X11?

Casi todos los tutoriales que encuentro me dicen que haga esto para mi bucle de eventos:

XEvent event; while (true) { XNextEvent(display, &event); switch (event.type) { case Expose: printf("Expose\n"); break; default: break; } } 

Sin embargo, hacer clic en la X para cerrar los resultados del progtwig en este mensaje.

 XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0" after 10 requests (10 known processed) with 0 events remaining. 

De hecho, es extraño para mí que los ejemplos sugieran usar un bucle infinito. Eso no suena natural, y mis otros progtwigs X11 no lo hacen. Así que busqué alrededor. Descubrí cómo capturar el evento de cierre de ventana.

 Atom wmDeleteMessage = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &wmDeleteMessage, 1); XEvent event; bool running = true; while (running) { XNextEvent(display, &event); switch (event.type) { case Expose: printf("Expose\n"); break; case ClientMessage: if (event.xclient.data.l[0] == wmDeleteMessage) running = false; break; default: break; } } 

Eso funciona. Sale sin errores. … Pero me niego a creer que esta es la forma normal de hacer las cosas. Quiero decir, ¿es esta la única manera de salir correctamente de una aplicación X11? Parece mucho trabajo solo para capturar el evento cercano. ¿Cómo hago un bucle de evento ‘apropiado’? ¿Por qué el evento cercano está tan profundamente enterrado? ¿Qué me estoy perdiendo?

No hay cosas tales como “botón de salida” o “aplicación” o “evento de cierre” en X11. Esto es por diseño.

Las decoraciones de las ventanas, los botones de salida y muchas otras cosas de las que dependemos no están integradas en X11. Se implementan en la parte superior del núcleo X11 en su lugar. El nombre del conjunto particular de convenciones responsables de wmDeleteMessage es ICCCM, búsquelo.

Xlib solo se ocupa del protocolo X11 central. No hay evento cerrado incorporado allí.

Hay kits de herramientas que facilitan el manejo de ICCCM y todas las demás cosas que no están integradas en X11 (GTK, wxWindows, Qt, …) Probablemente quiera usar uno de esos.

El problema radica en la comunicación entre X Server y el Administrador de ventanas.

Cuando llama a XCreateWindow o XCreateSimpleWindow , X Server crea su ventana (no la muestra hasta que la XMapWindow explícitamente en la pantalla llamando a XMapWindow ), y luego el Administrador de ventanas es responsable de adjuntar todas las decoraciones y botones y el menú del sistema alrededor de su ventana .

Puede llamar a XDestroyWindow por su cuenta para eliminar la ventana, y esto generalmente significa que simplemente desaparece de la pantalla, pero su progtwig todavía se está ejecutando y la conexión con el Servidor X aún está abierta, por lo que puede enviar más solicitudes.

El problema comienza cuando el administrador de ventanas hace clic en el pequeño botón X adjunto a su ventana, ya que no está creado por el servidor X y no le incumbe a él decidir qué hacer en ese momento. Ahora todo está en manos de Window Manager.

Si el Administrador de ventanas simplemente llamó a XDestroyWindow en su ventana, causaría un problema si su aplicación deseaba capturar el evento de cierre para hacer algo antes de que la ventana se destruya. Por lo tanto, se ha establecido la convención entre el servidor X y los administradores de ventanas para manejar este proceso.

El comportamiento predeterminado de la mayoría de los administradores de ventanas es destruir la ventana y cerrar la conexión con el servidor X , porque esto es lo que la mayoría de los usuarios de los administradores de ventanas esperaría: que cuando cierran la ventana, el progtwig terminará (y la conexión a la X Server se cerrará con la ventana cerrada). Y luego, cuando intenta llamar a XCloseDisplay(display) , causará el error de IO que mencionó, porque la conexión al servidor ya está cerrada y la estructura de la display no es válida.

Aquí hay un extracto de la documentación de Xlib que explica esto:

Los clientes que eligen no incluir WM_DELETE_WINDOW en la propiedad WM_PROTOCOLS pueden desconectarse del servidor si el usuario solicita que se elimine una de las ventanas de nivel superior del cliente.

Sí, sería genial si no lo ocultaran tan profundamente en sus documentos, aunque 😛 Pero cuando ya lo encuentras, afortunadamente, también sugiere la solución.

Si desea un comportamiento diferente (es decir, para capturar el evento de cierre desde el Administrador de ventanas), debe usar el protocolo WM_DESTROY_WINDOW .

Otro extracto de la documentación:

Los clientes, generalmente aquellos con múltiples ventanas de nivel superior, cuya conexión de servidor debe sobrevivir a la eliminación de algunas de sus ventanas de nivel superior, deben incluir el átomo WM_DELETE_WINDOW en la propiedad WM_PROTOCOLS en cada una de esas ventanas. Recibirán un evento ClientMessage como se describe anteriormente, cuyo campo de data[0] es WM_DELETE_WINDOW .

Tuve el mismo error y quería saber exactamente qué lo causa y por qué. Me tomó algo de tiempo descubrirlo y encontrar la explicación adecuada en el documento, así que pongo mi explicación aquí para guardar el tiempo de otros sin información.