c++ - programar - ¿Cómo salir del programa X11 sin error?
programar menu en c++ (3)
La solución a este problema es sencilla:
Debe usar el miembro de estructura correcto con la función XDestroyWindow ().
Debido a la implementación estándar de las estructuras de eventos X11, son muy similares entre sí. Cada estructura comienza con el miembro ''tipo'', y los primeros miembros son prácticamente siempre iguales.
Ahora asume:
int = 4 bytes
Bool = 4 bytes
unsigned long = 8 bytes
Display* = 8 bytes
Window = 4 bytes
Si llama a XDestroyWindow () con e.xdestroywindow.window , estará a 28 bytes del inicio de la estructura del evento, mientras que si usa e.xclient.window , estaría a 24 bytes.
Ya que vas a llamar a XDestroyWindow () con un argumento de Ventana incorrecto, fallará. En cambio, si lo llama usando e.xdestroywindow.event (que está a 24 bytes del comienzo de la estructura del evento), la dirección sería correcta y la función funcionaría correctamente.
Si observa el archivo Xlib.h usted mismo, notará que las dos estructuras tienen el elemento de la ventana posicionado de manera diferente.
Dicho esto, recuerde que Xlib se ha desarrollado durante años y muchos programadores trabajan todos los días con él, por lo que si hay un error misterioso, probablemente no esté dentro de Xlib. Como una última sugerencia, quiero decirle: si desea ir más lejos con la programación de Xlib, siempre tome los archivos de encabezado como la referencia principal, seguido del manual del sistema, y luego todo el resto.
El único error con tu código al final es:
XDestroyWindow(display,e.xdestroywindow.window);
Que debe ser cambiado a esto:
XDestroyWindow(display,e.xclient.window);
En cambio, el uso del conmutador es bueno y es el más implementado, sin problemas en el código X11.
NOTA : He probado su código yo mismo, cambiando solo esa línea y luego realizando varias pruebas, imprimiendo el resultado. La línea XDestroyWindow () es seguramente el único error.
Tengo un "Hello World" bastante simple en X11 al final de la pregunta. Pero cuando sale recibo los siguientes mensajes de error de tiempo de ejecución:
$ ./xtest
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 9 requests (7 known processed) with 0 events remaining.
Así que intenté manejar el wmDeleteMessage
mí mismo, y pude detener el cierre de la ventana, así que sé que estoy obteniendo el evento correctamente. Entonces agregué un XDestroyWindow()
al manejo del evento y obtengo nuevos errores.
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 4 (X_DestroyWindow)
Resource id in failed request: 0x130
Serial number of failed request: 12
Current serial number in output stream: 12
Parece que estoy tratando de destruir una ventana ya destruida, pero si saco el XDestroyWindow()
permanece vivo en mi pantalla.
A continuación se muestra mi código con un intento de destruir un controlador de ventana. ¿Cómo salgo sin errores?
#include<X11/Xlib.h>
#include <iostream>
int main()
{
Display *display;
if(!(display=XOpenDisplay(NULL)))
{
std::cerr << "ERROR: could not open display/n";
return 1;
}
int screen = DefaultScreen(display);
Window rootwind = RootWindow(display, screen);
Colormap cmap = DefaultColormap(display, screen);
Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
int blackColor = BlackPixel(display, screen);
int whiteColor = WhitePixel(display, screen);
Window w = XCreateSimpleWindow(display, rootwind, 0, 0, 200, 100, 0, blackColor, blackColor);
XMapWindow(display, w);
XSetWMProtocols(display, w, &wmDeleteMessage, 1);
bool running = true;
while(running)
{
XEvent e;
XNextEvent(display, &e);
switch (e.type)
{
case ClientMessage:
if(e.xclient.data.l[0] == wmDeleteMessage)
{
std::cout << "Shutting down now!!!" << std::endl;
XDestroyWindow(display,e.xdestroywindow.window);
running=false;
break;
}
break;
}
}
XCloseDisplay(display);
return 0;
}
Actualizar
Se cambió la línea a:
std::cout << "Shutting down now!!!" << std::endl;
XDestroyWindow(display,w);
Lo cual no me gusta porque planeo tener más que una ventana, pero por ahora vuelvo al primer mensaje de error que tuve:
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 9 requests (7 known processed) with 0 events remaining.
Actualizar
Intenté cambiar muchas cosas, como que el bucle salga de XPending (). Decidí dirigir el mundo de saludo de otra persona y tengo el mismo problema con su código. Debe haber algo mal con mi configuración.
Actualización Al parecer, muchas personas tienen este problema. Google ftk tuvo este problema y lo arreglaron en su registro de cambios . Llaman FTK_QUIT () que supongo que es como Salir (). Así que puse mi retorno allí mismo dentro del bucle y eso resolvió el problema. No estoy seguro de por qué, pero lo hizo. código fijo:
case ClientMessage:
if(e.xclient.data.l[0] == wmDeleteMessage)
{
XDestroyWindow(display,e.xclient.window);
XCloseDisplay(display);
return 0;
}
Aún daré la respuesta correcta a alguien que pueda explicar por qué y si es posible mover la declaración de retorno (junto con XCloseDisplay
) fuera del bucle.
El bucle de eventos debería tener este aspecto para salir correctamente:
XEvent e;
do
{
XNextEvent(display, &e);
if(e.type == ClientMessage && e.xclient.data.l[0] == wmDeleteMessage)
{
XDestroyWindow(display,e.xclient.window);
break;
}
//...
}while (XPending(display) > 0)
XCloseDisplay(display);
return 0;
Cuando se ejecuta en una instrucción de switch
, el código no funciona. Incluso si sale del bucle sin llamar a otra función X. La instrucción if
colocada antes de la instrucción de switch
soluciona el problema sin regresar del programa dentro del bucle.
Simplemente llame a XDestroyWindow()
justo antes de XCloseDisplay()
.
Editar:
Lo siento, no entendí el XSetWMProtocols. Ahora he leído sobre esto. Creo que estás accediendo al miembro equivocado de la unión de eventos.
XDestroyWindow (display, e.xdestroywindow.window);
Probablemente debería ser:
XDestroyWindow(display,e.xclient.window);
Tuve el mismo problema, y después de revisar la documentación de Xlib y experimentar mucho, creo que conozco la respuesta a tu pregunta y te la puedo explicar.
Cuando llama a XCreateWindow
o XCreateSimpleWindow
y luego a XMapWindow
, le indica al servidor X que cree su ventana y su mapa en la pantalla. Después de enviar estos comandos desde el búfer local al servidor (al llamar a XFlush
o cualquier función que solicite algunos datos del servidor, ya que implícitamente vacía el búfer de comandos), el servidor X muestra su ventana. Entonces es un trabajo del Administrador de ventanas adjuntar todas las decoraciones a su ventana, por ejemplo, algunos bordes, la barra de título, el menú de la ventana y esos botones para minimizar / maximizar / cerrar la ventana.
Ahora se muestra su ventana, y después de un tiempo puede decidir destruirla con XDestroyWindow
y cerrar la conexión con el servidor X llamando a XCloseDisplay
, y todo estará bien, sin errores.
El problema es que cuando el usuario hace clic en esa X en la barra de título de su ventana, no es tarea del servidor X manejarlo, sino del trabajo del administrador de ventanas (el servidor X no sabe nada sobre esas decoraciones y no le importa. ). La reacción habitual del Administrador de ventanas cuando el usuario cierra la ventana de nivel superior de su programa es destruir la ventana y cerrar la conexión al servidor X , porque eso es lo que la mayoría de los usuarios esperan. Es posible que su programa aún se ejecute fuera de la pantalla, pero el administrador de ventanas suele asociar la ventana de nivel superior con la conexión del servidor X.
Entonces, cuando el Administrador de ventanas destruye tu ventana, no puedes llamar a XDestroyWindow
, porque la ventana ya está destruida y su manejador de Window
no es válido. Recibirá un error sobre BadWindow
. Tampoco puede llamar a XCloseDisplay
, porque la conexión con el Servidor X ya está cerrada, y esto causará el XIO: fatal IO error 11 (Resource temporarily unavailable) on X server
error del XIO: fatal IO error 11 (Resource temporarily unavailable) on X server
muchos usuarios experimentan desde aplicaciones cuyos autores no lo sabían. Es un error común, porque en una mano se recomienda que limpie después de usted mismo, pero en la otra, la documentación es engañosa sobre cómo debe hacerse esto correctamente.
Sin embargo, existe una convención acerca de cómo deberían cooperar X Server y Window Manager, que también cubre responder a los comandos del usuario para cerrar la ventana de nivel superior. Hay una extensión del protocolo X que lo maneja. Así es como la documentación de Xlib lo explica:
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 propiedadWM_PROTOCOLS
en cada una de esas ventanas. Recibirán un eventoClientMessage
como se describe anteriormente, cuyo campo dedata[0]
esWM_DELETE_WINDOW
.
[...]
Los clientes que eligen no incluirWM_DELETE_WINDOW
en la propiedadWM_PROTOCOLS
pueden desconectarse del servidor si el usuario solicita que se elimine una de las ventanas de nivel superior del cliente.
Entonces, hay dos soluciones para este problema: evite llamar a XDestroyWindow
y XCloseDisplay
cuando el XCloseDisplay
ventanas está cerrando su ventana y no por usted mismo (en realidad no tiene que limpiar la ventana de nivel superior ya que el servidor X destruirá no obstante, cuando finalice su programa), o necesita registrar la extensión WM_DESTROY_WINDOW
y esperar la notificación del Administrador de ventanas cuando el usuario le ClientMessage
que cierre su ventana (le enviará un evento ClientMessage
y, con sus data[0]
establecido en WM_DELETE_WINDOW
). Y después de recibirlo, simplemente destruya la ventana y cierre la conexión con el servidor X usted mismo, y finalice su programa. O deje abierta la conexión con el servidor X para realizar más comunicaciones con él si lo desea. Cuando manejas WM_DESTROY_WINDOW
, el Administrador de ventanas no intentará destruir tu ventana ni cerrará la conexión al servidor X.