2011-11-18 12 views
16

Ho un "Hello World" abbastanza semplice in X11 alla fine della domanda. Ma quando si esce ricevo i messaggi di errore di runtime di seguito:Come si esce dal programma X11 senza errore

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

così ho provato la manipolazione del wmDeleteMessage me stesso, e sono stato in grado di fermare la finestra da chiudere, quindi so io sono sempre correttamente l'evento. Di quanto ho aggiunto un XDestroyWindow() alla gestione degli eventi e ottengo nuovi errori.

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 

suona come io sto cercando di distruggere una finestra già distrutto, ma se prendo il XDestroyWindow() rimane vivo sul mio schermo.

Di seguito è riportato il mio codice con un tentativo di distruggere un gestore di finestre. Come posso uscire senza errori?

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

Aggiornamento

linea cambiato in:

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

Il che non mi piace, perché ho intenzione di avere più di finestra, ma per ora sono indietro al primo messaggio di errore che ho avuto:

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

Aggiornamento

Provato a cambiare molte cose in giro come se il ciclo si allontanasse da XPending(). Ho deciso di eseguire il numero hello world di qualcun altro e ottengo lo stesso problema con il loro codice. Deve essere qualcosa di sbagliato con il mio setup.

Aggiornamento Apparentemente molte persone hanno questo problema. Google ftk ha avuto questo problema e lo hanno risolto nel loro change log. Chiamano FTK_QUIT() che sto indovinando è come Exit(). Così ho messo il mio ritorno proprio lì dentro il loop e questo ha risolto il problema. Non so perché, ma lo ha fatto. codice fisso:

case ClientMessage: 
    if(e.xclient.data.l[0] == wmDeleteMessage) 
    { 
     XDestroyWindow(display,e.xclient.window); 
     XCloseDisplay(display); 
     return 0; 
    } 

darà ancora risposta corretta a qualcuno che può spiegare perché e se è possibile spostare l'istruzione return (insieme al XCloseDisplay) al di fuori del ciclo.


il ciclo degli eventi dovrebbe essere simile a questo per uscire correttamente:

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; 

Quando si esegue in un comunicato switch il codice non funziona. Anche se esce dal ciclo senza chiamare un'altra funzione X. L'istruzione if sopra posizionata prima della tua istruzione switch risolve il problema senza tornare dal programma all'interno del ciclo.

+0

Aggiunto escape to loop perché ha realizzato che non si interrompe mai. Ancora gli stessi messaggi di errore. –

+0

Perché vuoi eseguire direttamente la programmazione X11? Consiglio vivamente di utilizzare un toolkit grafico, come GTK o Qt (ma ce ne sono anche altri: FLTK, Fox ...) –

+0

@Starynkevitch Altro per capire come funziona. Non per lavoro o scuola. –

risposta

16

La soluzione a questo problema è semplice:

È necessario utilizzare il membro della struttura a destra con la funzione XDestroyWindow().

A causa dello standard di implementazione delle strutture di eventi X11, sono molto simili tra loro. Ogni struttura inizia con il membro 'tipo' e i primi membri sono praticamente sempre gli stessi.

Ora assumere:

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

Se si chiama XDestroyWindow() con e.xdestroywindow.window, che si sta per essere 28 byte di distanza dall'inizio della struttura dell'evento, mentre se si utilizzare e.xclient.window, si dovrebbe essere a 24 byte di distanza.

Poiché si sta chiamando XDestroyWindow() con un argomento Finestra errato, non funzionerà. Invece se lo chiami usando e.xdestroywindow.event (che è 24 byte di distanza dall'inizio della struttura dell'evento), l'indirizzo sarebbe corretto e la funzione funzionerebbe correttamente.

Se si prende un te un'occhiata al file Xlib.h, si noterà che le due strutture hanno la finestra elemento posizionato in modo diverso.

Detto questo, ricorda che Xlib è stato sviluppato per anni e molti programmatori ogni giorno lavorano con esso, quindi se c'è un errore misterioso, probabilmente non è all'interno di Xlib. Come ultimo suggerimento voglio dirti: se vuoi andare più lontano con la programmazione Xlib, prendi sempre i file header come riferimento principale, seguito dal manuale di sistema, quindi tutto il resto.

L'unico errore con il codice alla fine è:

XDestroyWindow(display,e.xdestroywindow.window); 

Che deve essere cambiato in questo:

XDestroyWindow(display,e.xclient.window); 

Invece l'utilizzo di interruttore è buono, ed è il più implementato, senza problemi sul codice X11.

NOTA: Ho provato personalmente il codice, cambiando solo quella linea, e quindi facendo vari test, stampando il risultato.La riga XDestroyWindow() è sicuramente l'unico errore.

+0

+1 per non solo una soluzione ma una _planazione_ del problema/soluzione. Grazie. – tjklemz

3

Basta chiamare XDestroyWindow() subito prima del XCloseDisplay().

Edit:

Spiacente, non ho capito la cosa XSetWMProtocols. Ora ho letto su di esso. Penso che tu stia accedendo al membro sbagliato dell'unione degli eventi.

XDestroyWindow (visualizzazione, e.xdestroywindow.window);

dovrebbe probabilmente essere:

XDestroyWindow(display,e.xclient.window); 
+0

Ciò non corregge l'errore. –

+0

@JoeMcGrath Modificata la mia risposta. Mi dispiace per la direzione sbagliata. –

+0

Grazie, ho imparato qualcosa, anche se non risolve il problema. Almeno ora ho la maniglia della finestra giusta. –

3

Ho avuto lo stesso problema, e dopo aver scavato nella documentazione Xlib e molti esperimenti penso di conoscere la risposta alla tua domanda e posso spiegartelo.

Quando si chiama XCreateWindow o XCreateSimpleWindow e quindi XMapWindow, si indica al server X di creare la finestra e la mappa sullo schermo. Dopo aver inviato questi comandi dal buffer locale al server (chiamando XFlush o qualsiasi funzione che richiede alcuni dati dal server, poiché scarica implicitamente il buffer dei comandi), X Server visualizza la finestra. Quindi è compito del Gestore finestre allegare tutte le decorazioni alla tua finestra, ad es. alcuni bordi, barra del titolo, menu della finestra e quei pulsanti per ridurre a icona/ingrandire/chiudere la finestra.

Ora viene visualizzata la finestra, e dopo un po 'puoi decidere di distruggerlo con XDestroyWindow e chiudere la connessione al server X chiamando XCloseDisplay, e tutto andrà bene, senza errori.

Il problema è che quando l'utente fa clic su quel X sulla barra del titolo di finestra, non è il lavoro del X Server di gestire la cosa, ma il lavoro del Window Manager (il server X non sa nulla di quelle decorazioni e non importa). La normale reazione di Window Manager quando l'utente chiude la finestra di primo livello del tuo programma è distruggere la finestra e chiudere la connessione a X Server, perché è ciò che la maggior parte degli utenti si aspetterebbe. Il tuo programma può ancora funzionare fuori dallo schermo, ma la finestra di primo livello è solitamente associata alla connessione di X Server da Window Manager.

Così quando il Gestore finestre distrugge la finestra, non è possibile chiamare XDestroyWindow, perché la finestra è già stata distrutta e l'handle Window non è valido. Riceverai un errore su BadWindow. Non è inoltre possibile chiamare XCloseDisplay, perché la connessione a X Server è già chiusa e ciò causerà l'errore XIO: fatal IO error 11 (Resource temporarily unavailable) on X server che molti utenti provano da applicazioni di cui gli autori non lo sapevano. È un errore comune, perché in una mano sei incoraggiato a ripulirti dopo di te, ma d'altra parte la documentazione è fuorviante su come questo dovrebbe essere fatto correttamente.

C'è una convenzione, tuttavia, su come X Server e Window Manager dovrebbero cooperare, che copre anche la risposta ai comandi dell'utente per chiudere la finestra di livello superiore. C'è un'estensione del protocollo X che lo gestisce. Ecco come il Xlib documentation lo spiega:

clienti, di solito quelli con più finestre di primo livello, la cui connessione con il server deve sopravvivere alla cancellazione di alcune delle loro finestre di primo livello, dovrebbe includere l'atomo WM_DELETE_WINDOW nel WM_PROTOCOLS proprietà su ciascuna di queste finestre. Riceveranno un evento ClientMessage come descritto sopra il cui campo data[0] è WM_DELETE_WINDOW.
[...]
I client che scelgono di non includere WM_DELETE_WINDOW nella proprietà WM_PROTOCOLS possono essere disconnessi dal server se l'utente richiede la cancellazione di una delle finestre di livello superiore del client.

Quindi ci sono due soluzioni a questo problema: o evitare di chiamare XDestroyWindow e XCloseDisplay quando la finestra è chiusa dal Window Manager e non da soli (che in realtà non c'è bisogno di pulire la finestra di primo livello dal momento che X Server lo distruggerà comunque quando il programma termina), o è necessario registrare l'estensione WM_DESTROY_WINDOW e attendere la notifica da Gestione finestre quando viene richiesto all'utente di chiudere la finestra (verrà inviato un evento ClientMessage quindi , con il suo data[0] impostato su WM_DELETE_WINDOW). E dopo averlo ricevuto basta distruggere la finestra e chiudere la connessione al server X te stesso e terminare il programma. Oppure lascia aperta la connessione a X Server per eseguire ulteriori comunicazioni con esso, se lo desideri. Quando gestisci WM_DESTROY_WINDOW, il Gestore finestre non proverà a distruggere la tua finestra né a chiudere la connessione al server X.

Problemi correlati