2009-09-15 3 views
15

Sto tentando di risolvere quello che pensavo sarebbe stato un problema molto semplice. Voglio mantenere una QPixmap aggiornata con l'intero contenuto dello schermo. È possibile ottenere una mappa di pixel in questo modo:Mantieni copia QPixmap del contenuto dello schermo utilizzando X11, XDamage, XRender e altri trucchi

QDesktopWidget *w = QApplication::desktop(); 
if (w) 
{ 
    QRect r = w->screenGeometry(); 
    QPixmap p = QPixmap::grabWindow(w->winId(), 0, 0, r.width(), r.height()) 
    QByteArray bitmap; 
} 

Il problema di questo è che QDesktopWidget finisce per ri-afferrare l'intera pixmap schermo dal server X11 ogni volta che si chiede perché, anche se non è cambiato nulla.

Ho bisogno di questo codice per essere veloce, quindi sto provando a farlo da solo. Il mio punto di partenza è stato il qx11mirror demo, tuttavia, fondamentalmente fa la stessa cosa. Usa l'estensione XDamage per capire quando qualcosa è cambiato, ma invece di usare le informazioni del rettangolo danneggiato per aggiornare solo quella parte della pixmap memorizzata nella cache, imposta semplicemente un flag "sporco", che innesca comunque un aggiornamento completo.

Quindi sto cercando di modificare l'esempio qx11mirror per aggiornare solo la parte danneggiata delle finestre, ma non riesco a ottenere nulla per funzionare - tutto ciò che ottengo è una pixmap vuota (nera). Il codice che sto usando è:

void QX11Mirror::x11Event(XEvent *event) 
{ 
    if (event->type == m_damageEvent + XDamageNotify) 
    { 
     XDamageNotifyEvent *e = reinterpret_cast<XDamageNotifyEvent*>(event); 

     XWindowAttributes attr; 
     XGetWindowAttributes(QX11Info::display(), m_window, &attr); 
     XRenderPictFormat *format = XRenderFindVisualFormat(QX11Info::display(), attr.visual); 
     bool hasAlpha    = (format->type == PictTypeDirect && format->direct.alphaMask); 
     int x      = attr.x; 
     int y      = attr.y; 
     int width     = attr.width; 
     int height    = attr.height; 

      // debug output so I can see the window pos vs the damaged area: 
     qDebug() << "repainting dirty area:" << x << y << width << height << "vs" << e->area.x << e->area.y << e->area.width << e->area.height; 

     XRenderPictureAttributes pa; 
     pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets  
     Picture picture = XRenderCreatePicture(QX11Info::display(), 
               m_window, 
               format, 
               CPSubwindowMode, 
               &pa); 

     XserverRegion region = XFixesCreateRegionFromWindow(QX11Info::display(), 
                  m_window, WindowRegionBounding); 

     XFixesTranslateRegion(QX11Info::display(), region, -x, -y); 
     XFixesSetPictureClipRegion(QX11Info::display(), picture, 0, 0, region); 
     XFixesDestroyRegion(QX11Info::display(), region); 


     //QPixmap dest(width, height); 
     XRenderComposite(QX11Info::display(),      // display 
         hasAlpha ? PictOpOver : PictOpSrc,   // operation mode 
         picture,         // src drawable 
         None,          // src mask 
         dest.x11PictureHandle(),     // dest drawable 
         e->area.x,         // src X 
         e->area.y,         // src Y 
         0,           // mask X 
         0,           // mask Y 
         e->area.x,         // dest X 
         e->area.y,         // dest Y 
         e->area.width,        // width 
         e->area.height);       // height 

      m_px = dest; 
     XDamageSubtract(QX11Info::display(), e->damage, None, None); 
      emit windowChanged(); 

    } 
    else if (event->type == ConfigureNotify) 
    { 
     XConfigureEvent *e = &event->xconfigure; 
     m_position = QRect(e->x, e->y, e->width, e->height); 
     emit positionChanged(m_position); 
    } 
} 

Qualcuno può indicarmi la giusta direzione? La documetnation per XRender, XDamage e le altre estensioni X11 è piuttosto brutta.

Motivi per l'utilizzo XRender oltre XCopyArea

Il testo che segue tratto da here.

È perfettamente possibile creare un GC per una finestra e utilizzare XCopyArea() per copiare il contenuto della finestra se si desidera utilizzare il protocollo core, ma poiché l'estensione Composite espone nuovi elementi visivi (quelli con canali alfa, ad es.), non vi è alcuna garanzia che il formato del codice sorgente possa corrispondere a quello della destinazione. Con il protocollo di base tale situazione si tradurrà in un errore di corrispondenza, qualcosa che non si verificherà con l'estensione Xrender.

Inoltre, il protocollo di base non ha alcuna comprensione dei canali alfa, il che significa che non è possibile comporre finestre che utilizzano il nuovo visual ARGB. Quando l'origine e la destinazione hanno lo stesso formato, non vi è alcun vantaggio in termini di prestazioni nell'utilizzo del protocollo core a partire da X11R6.8. Questa versione è anche la prima a supportare la nuova estensione Composite.

Quindi, in conclusione, non ci sono inconvenienti e solo vantaggi nella scelta di Xrender rispetto al protocollo core per queste operazioni.

+0

Perché usare XRender invece di XCopyArea? XRender è soggetto ad errori (se non lo fai esattamente nel modo giusto, non funzionerà ..) – ypnos

+0

Ho scoperto che il tentativo di aggiornare lo schermo solo aggiornare parti danneggiate non funziona così bene in pratica. Ad esempio, le applicazioni OpenGL non sembrano mai riportare danni. Penso che la cosa migliore da fare sia continuare a riconquistare lo schermo. – ldog

+0

Puoi riacquisire lo schermo quasi in tempo reale (10-20 fotogrammi al secondo) se lo fai in modo intelligente. – ldog

risposta

3

Per prima cosa è necessario modificare DamageReportLevel nella chiamata a DamageCreate in QX11Mirror :: setWindow da DamageReportNotEmpty a XDamageReportBoundingBox.

Successivamente è necessario chiamare dest.detach() prima della chiamata a XRenderComposite. Non hai davvero bisogno sia di m_px che di dest come variabili membro - puoi semplicemente usare m__px.

C'è anche una chiamata mancante per XRenderFreePicture in quell'esempio che dovrebbe andare dopo la chiamata a XRenderComposite:

XRenderFreePicture(QX11Info::display(), picture); 

Non è necessario duplicare tutto il codice QX11Mirror :: mappa di pixel in QX11Mirror :: x11Event . Invece cambia il tipo di m_dirty da bool a QRect, poi usa x11Event update m_dirty con il rettangolo da XDamageNotifyEvent, cioè. e-> zona. Quindi in QX11Mirror: pixmap invece di controllare se m_dirty è vero, controlla se m_dirty non è un rettangolo vuoto.Dovresti quindi passare il rettangolo da m_dirty a XRenderComposite.

Edit:

Il dest.detach è il bit chiave - senza di che non avrai mai a farlo funzionare.

Problemi correlati