2009-07-21 11 views
11

Vorrei intercettare il messaggio WM_DELETE_WINDOW inviato a una determinata selezione di finestre di un'applicazione che sto scrivendo (AllTray), in modo che possa agire su di esso anziché sull'applicazione che lo riceve. Attualmente sto cercando di provare questo al livello GDK via gdk_display_add_client_message_filter se possibile, ma sarei felice con una soluzione Xlib se ce n'è una anche; it sembra essere possibile, ma non riesco a capire come devo farlo con successo.Intercettare WM_DELETE_WINDOW su X11?

Attualmente, ho due programmi (scritto in C), che sto cercando di utilizzare per ottenere questo calcolato fuori, the first one non fa altro che creare una finestra e registrare che sa circa WM_DELETE_WINDOW, e the second one tentativi di catturare quel messaggio, ma sembra fallire nel farlo; sembra proprio non fare nulla. Sto comprendendo la documentazione sbagliata su questo, o c'è qualcosa di aggiuntivo che devo fare (o devo evitare di usare GDK interamente per questo)?

Lo sfondo è questo: prima della mia riscrittura di AllTray, il modo in cui farebbe le cose sembra essere quello di cercare di intercettare un clic del mouse sul pulsante X stesso. Per alcuni gestori di finestre, questo ha funzionato correttamente, per altri non funzionava affatto, e per gli altri, l'utente doveva configurarlo manualmente e istruire AllTray dove era il pulsante per chiudere la finestra. Quello che sto cercando è una soluzione che non coinvolge uno LD_LIBRARY_PRELOAD e funzionerà per qualsiasi combinazione di gestore di finestre/applicazioni conforme agli standard attuali e invia un ClientMessage WM_DELETE_WINDOW quando la finestra viene chiusa.

UPDATE: Sto ancora cercando una risposta. La strada che sto percorrendo in questo momento è cercare di riparare la finestra e gestirla da sola, ma non riesco a farlo funzionare. Dopo la riparazione, non riesco a recuperarlo in alcun modo. Potrei mancare qualcosa di molto fondamentale, ma non riesco a capire come effettivamente farlo apparire di nuovo la mia finestra, per riportarlo sullo schermo.

UPDATE 2: OK, quindi ho colpito un altro muro di mattoni. La documentazione del server X dice di impostare StructureNotifyMask sulla maschera eventi della finestra per ricevere sia gli eventi MapNotify che ReparentNotify. Sarei interessato a ricevere entrambi. Il mio pensiero corrente era di creare una finestra che servisse proprio come un ricevitore di eventi, e poi quando ricevo eventi per cose interessanti, agisco su di loro creando e riparando. Tuttavia, questo semplicemente non sembra funzionare. Gli unici eventi che effettivamente ricevo sono gli eventi PropertyNotify. Quindi, anche questa via non sembra fare molto bene.

+0

Penso che potrebbe essere possibile riparando la finestra all'interno del tuo stesso livello e filtrando quali eventi si passano? Non penso che il modo in cui stai attualmente provando possa funzionare. – wrt

+0

Ci sono degli svantaggi nel farlo in quel modo? Cioè, c'è qualcosa in particolare che potrebbe causare che interferire con cose come XDND o qualsiasi altra cosa? È un'idea portatile (come in, non si romperà applicazioni o gestori di finestre)? Mi sembra di essere in grado di trovare pochissime informazioni al riguardo. Suppongo che significhi anche che dovrei creare una nuova finestra "genitore" per ogni nuova finestra client, corretta? –

risposta

1

Sfortunatamente, la risposta migliore a questa domanda è una serie di non risposte; ci sono modi per realizzare tecnicamente, ma tutti hanno cadute che li rendono estremamente impraticabile:

  1. Creare un proxy X11 per un'applicazione, passando tutti i messaggi di protocollo X11 avanti e indietro tra l'applicazione e il server X. Il proxy quindi filtrerebbe tutti i messaggi interessanti. Il lato negativo di questo è che questo è un sacco di spese generali per una singola piccola caratteristica, e il protocollo X11 è complesso. Potrebbero esserci anche conseguenze indesiderate, che rendono questa opzione ancora meno attraente.
  2. Avvia come applicazione standard che funge da intermediario tra il gestore di finestre e le applicazioni client "interessanti". Questo rompe alcune cose, come XDnD. In effetti, non è diverso dalla prima opzione, tranne per il fatto che il proxy si trova a livello di finestra rispetto al livello del protocollo X11.
  3. Utilizzare il trucco libreria non portatile LD_PRELOAD. Questo ha diversi lati negativi:
    1. è non-portabile su linker dinamici: non tutti i linker dinamici supportano LD_PRELOAD, anche tra i sistemi UNIX-like.
    2. Non è portatile tra i sistemi operativi: non tutti i sistemi operativi supportano linker dinamici dotati di funzionalità.
    3. Interrompe la trasparenza della rete: la libreria oggetto/collegamento dinamico deve risiedere sull'host come processo figlio che viene eseguito.
    4. Non tutte le applicazioni X11 usano Xlib; sarebbe necessario scrivere un modulo LD_PRELOAD per ciascuna delle librerie che un'applicazione potrebbe usare per parlare con X11.
    5. In aggiunta all'ultimo punto, non tutte le applicazioni potrebbero essere soggette a LD_PRELOAD anche se sono state eseguite con un linker che l'ha supportato, poiché non possono utilizzare un oggetto o una DLL condivisi per comunicare con X; considera, ad esempio, un'applicazione Java che utilizza una libreria di protocollo X11 scritta in Java.
    6. Su alcuni sistemi operativi di tipo UNIX, le librerie LD_PRELOAD devono essere setuid/setgid se devono essere utilizzate con i programmi setuid/setgid. Questa è, naturalmente, una potenziale vulnerabilità di sicurezza.
    7. Sono abbastanza sicuro che ci siano più aspetti negativi a cui non riesco a pensare.
  4. Implementare un'estensione al sistema X Window. Non portatile tra le implementazioni X11, complesse e complicate come tutte le uscite, ed assolutamente fuori questione.
  5. Implementare estensioni o plug-in per gestori di finestre. Ci sono tanti gestori di finestre quante sono le opinioni sui gestori di finestre, e quindi questo è assolutamente irrealizzabile.

Alla fine, sono riuscito a raggiungere finalmente il mio obiettivo utilizzando un meccanismo completamente separato; chiunque sia interessato, consultare il supporto Close-to-Tray in AllTray 0.7.5.1dev e versioni successive, incluso the git master branch available on github.

11

Non conosco X11, ma ho cercato su Google utilizzando "Intercept WM_DELETE_WINDOW X11" come parole chiave. Trovato 17k - MarkMail e Mplayer-commits r154 - trunk/libvo. In entrambi i casi stanno facendo la stessa cosa.

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

all'interno static void x11_init(),

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

poi, entro 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 */ 
     } 
    } 
} 

Vedi XInternAtom, XSetWMProtocols e XNextEvent.

Dopo aver scritto quanto sopra, ho trovato Handling window close in an X11 app:

Quando un utente fa clic sul pulsante di chiusura [x] sulla nostra applicazione X11 vogliamo che al pop aa dialogo che chiede “Ti di voler uscire ?”. Questa è una semplice app XX. Non ci sono fantastici widget GTK o QT qui. Quindi, come prendere il messaggio "la finestra è chiusa"?

La risposta è di raccontare la finestra Responsabile siamo interessati a questi evento chiamando XSetWMProtocols e la registrazione di un messaggio WM_DELETE_WINDOW con esso. Quindi riceverai un messaggio dal cliente dal Gestore finestre se qualcuno tenta di chiudere la finestra e non lo chiuderà, ci lascerà noi fino a noi . Ecco un esempio ...

// 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

Sarebbe bello ... se solo risolvesse il mio problema. Il problema non è che possiedo una finestra e voglio ricevere l'evento; che posso fare bene (e farei uno dei modi sopra). Il problema è che la mia applicazione deve intercettarlo per altre finestre. Windows che non sono mie, che non ho creato. Sto ancora cercando di capire cosa intendesse il primo commentatore con la forzatura delle finestre, ma devo ancora farlo funzionare. Sono stato googling per giorni. Ecco perché sono venuto qui. –

+0

Suppongo che tu abbia già letto http://tronche.com/gui/x/xlib/window-and-session-manager/XReparentWindow.html. –

+0

Infatti ho. Non sono ancora abbastanza sicuro su cosa fare per farlo funzionare correttamente; Sono riuscito a ottenere Windows riparato, ma mi manca qualcosa perché non sono in grado di raggiungere il mio obiettivo di far funzionare le cose. : -/In realtà ho il riferimento Xlib sulla mia scrivania, che ho cercato ripetutamente di capire. : -/ –

0

Ok, di approfondire la mia precedente suggerimento, si potrebbe desiderare di indagare XEmbed. Per lo meno, potrebbe darti qualche idea da provare.

In caso contrario, darei un'occhiata a come potrebbe funzionare un altro software simile (ad esempio wmdock o su come GtkPlug/GtkSocket è implementato), anche se credo che in entrambi i casi sia richiesto il supporto esplicito nelle applicazioni.

Spero che sia più utile.

+0

Ho fatto esperimenti con questo a un certo punto; tuttavia, era molto _ inaffidabile. Era così inaffidabile da non essere adatto a un programma di produzione. Per fortuna ho trovato un altro modo per raggiungere l'obiettivo finale che avevo in mente. –

0

Si consiglia di leggere ICCCM che indica come il gestore di finestre comunica con il client. La maggior parte di WM creerà una finestra frame per contenere la finestra di primo livello tramite reparenting. Quindi, se il tuo riparatore potrebbe interrompere la relazione conosciuta da WM e dalla finestra del tuo cliente.

+1

Grazie. Ho letto l'ICCCM e un po 'di più su X11. Riparare non è un'opzione senza un * lotto * di lavoro. La vecchia AllTray ha fatto degli inganni di riparazione, che hanno rotto un sacco di cose nel processo; per esempio, XDnD. –