2011-11-18 20 views
16

Tengo un "Hola mundo" bastante simple en X11 al final de la pregunta. Pero cuando sale me sale el mensaje de error en tiempo de ejecución a continuación:Cómo salir del programa X11 sin error

$ ./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 he intentado manipular el wmDeleteMessage mí, y yo era capaz de detener la ventana se cierre, así que sé que estoy recibiendo el evento correctamente. Luego de agregar un XDestroyWindow() al manejo de eventos, recibo 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 

Suena como que estoy tratando de destruir una ventana ya destruida, pero si saco la XDestroyWindow() que se mantiene vivo en mi pantalla.

A continuación se muestra el código con un intento de destruir a un controlador de ventana. ¿Cómo salgo sin ningún error?

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

actualización

línea cambiado a:

std::cout << "Shutting down now!!!" << std::endl; 
     XDestroyWindow(display,w); 

Qué no me gusta porque yo planeo tener más de ventana, pero por ahora estoy de nuevo a la primera 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. 

actualización

intentado cambiar muchas cosas como tener el bucle de ejecución fuera de XPending(). Decidió ejecutar el hello world de otra persona y me sale el mismo problema con su código. Debe haber algo mal con mi configuración.

Actualización Aparentemente, muchas personas tienen este problema. Google ftk tuvo este problema y lo arreglaron en su change log. Llaman a FTK_QUIT() que supongo que es como Exit(). Así que puse mi regreso allí dentro del ciclo y eso resolvió el problema. No estoy seguro 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; 
    } 

seguirá siendo dar respuesta correcta a alguien que pueda explicar por qué y si es posible mover la instrucción de retorno (junto con el XCloseDisplay) fuera del bucle.


El bucle de eventos debe tener este aspecto 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 un comunicado switch el código no funciona. Incluso si sale del bucle sin llamar a otra función X. La instrucción if anterior colocada antes de su declaración switch soluciona el problema sin regresar del programa dentro del ciclo.

+0

Se agregó escape to loop porque se dio cuenta de que nunca se rompe. Todavía los mismos mensajes de error. –

+0

¿Por qué quieres hacer directamente la programación de X11? Recomiendo usar un juego de herramientas gráficas, como GTK o Qt (pero también hay otras: FLTK, Fox ...) –

+0

@Starynkevitch Más para entender cómo funciona. No por trabajo o escuela –

Respuesta

16

La solución a este problema es sencilla:

Debe utilizar el miembro de estructura correcta con la función XDestroyWindow().

Debido al estándar de implementación 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 lo mismo.

Supongamos ahora:

int = 4 bytes 
Bool = 4 bytes 
unsigned long = 8 bytes 
Display* = 8 bytes 
Window = 4 bytes 

Si llama XDestroyWindow() con e.xdestroywindow.window, usted va a ser de 28 bytes de distancia desde el comienzo de la estructura de eventos, mientras que si use e.xclient.window, estaría a 24 bytes de distancia.

Dado que va a llamar al XDestroyWindow() con un argumento de Ventana incorrecto, fallará. En cambio, si lo llamas 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 se echa un vistazo a sí mismo en el archivo Xlib.h, usted notará que las dos estructuras tienen la ventana elemento 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 último consejo, quiero decirte: si quieres llegar más lejos con la programación de Xlib, siempre toma los archivos de encabezado como la referencia principal, seguidos por el manual del sistema y luego todo el resto.

El único error con su 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 de interruptor 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 el único error seguro.

+0

+1 no solo por una solución sino por una explicación del problema/solución. Gracias. – tjklemz

3

Simplemente llame al XDestroyWindow() justo antes de XCloseDisplay().

Editar:

Lo sentimos, no entendía lo XSetWMProtocols. Ahora lo he leído. 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); 
+0

Eso no soluciona el error. –

+0

@JoeMcGrath Cambió mi respuesta. Perdón por la dirección equivocada. –

+0

Gracias, aprendí algo, aunque no soluciona el problema. Al menos ahora tengo el control de ventana correcto. –

3

Tuve el mismo problema, y ​​después de excavar la documentación de Xlib y de experimentar mucho, creo que sé la respuesta a su pregunta y puedo explicárselo.

Cuando llama a XCreateWindow o XCreateSimpleWindow y luego a XMapWindow, le indica al servidor X que cree su ventana y mapa en la pantalla. Después de enviar estos comandos desde el búfer local al servidor (llamando al XFlush o cualquier función que solicite datos del servidor, ya que vacía implícitamente el búfer de comando), el servidor X muestra su ventana. Luego, es un trabajo del Administrador de ventanas adjuntar todas las decoraciones a su ventana, p. algunos bordes, barra de título, menú de 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 al servidor X llamando al XCloseDisplay, y todo estará bien, sin errores.

El problema es que cuando el usuario hace clic en ese X en la barra de título de la ventana, que no es el trabajo de la X Server para manejar la situación, pero el trabajo del gestor de ventanas (el servidor X no sabe nada acerca de esas decoraciones y no le importa). La reacción habitual de Window Manager cuando el usuario cierra la ventana de nivel superior de su programa es destruir la ventana y cerrar la conexión con el Servidor X, porque eso es lo que la mayoría de los usuarios esperarían. Su programa aún puede ejecutarse fuera de la pantalla, pero la ventana de nivel superior generalmente está asociada con la conexión del servidor X por el Administrador de ventanas.

Así que cuando Window Manager destruye su ventana, no puede llamar al XDestroyWindow, porque la ventana ya está destruida y su identificador Window no es válido. Obtendrá un error de BadWindow. Tampoco puede llamar al XCloseDisplay, porque la conexión al servidor X ya está cerrada, y esto causará el error XIO: fatal IO error 11 (Resource temporarily unavailable) on X server que muchos usuarios experimentan de aplicaciones cuyos autores no lo sabían. Es un error común, porque en una mano se le recomienda que limpie después de usted mismo, pero por otra parte, la documentación es engañosa acerca de cómo se debe hacer esto correctamente.

Existe una convención, sin embargo, sobre cómo X Server y Window Manager deberían cooperar, 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 el Xlib documentation lo explica:

clientes, por lo general aquellos con múltiples ventanas de nivel superior, cuya conexión con el servidor debe sobrevivir a la eliminación de algunas de sus ventanas de nivel superior, debe incluir el átomo WM_DELETE_WINDOW en la propiedad WM_PROTOCOLS en cada una de esas ventanas Recibirán un evento ClientMessage como se describió anteriormente cuyo campo data[0] es WM_DELETE_WINDOW.
[...]
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.

Así que hay dos soluciones a este problema: o bien evitar llamar XDestroyWindow y XCloseDisplay cuando la ventana está cerrada por el gestor de ventanas y no por sí mismo (que en realidad no tiene que limpiar la ventana de nivel superior ya que el servidor X lo destruirá cuando su programa termine), o necesita registrar la extensión WM_DESTROY_WINDOW y esperar la notificación del Administrador de ventanas cuando el usuario le indique que cierre su ventana (le enviará un evento ClientMessage luego , con su data[0] establecido en WM_DELETE_WINDOW). Y después de recibirlo, simplemente destruya la ventana y cierre la conexión al servidor X usted mismo, y finalice su programa. O deje abierta la conexión al Servidor X para realizar más comunicación con él si lo desea. Cuando maneja WM_DESTROY_WINDOW, el Administrador de ventanas no intentará destruir su ventana ni cerrar la conexión al Servidor X.

Cuestiones relacionadas