2009-09-15 10 views
15

Estoy intentando resolver lo que pensé que sería un problema muy simple. Quiero mantener un QPixmap actualizado con todo el contenido de la pantalla. Usted puede obtener un mapa de pixels tal al hacer esto:Mantener copia de QPixmap del contenido de la pantalla usando X11, XDamage, XRender y otros trucos

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

El problema con esto es que QDesktopWidget termina por volver a agarrar todo el mapa de píxeles de la pantalla desde el servidor X11 cada vez que lo solicita, aunque nada ha cambiado.

Necesito este código para ser rápido, así que estoy tratando de hacer esto por mí mismo. Mi punto de partida fue el qx11mirror demo, sin embargo, básicamente hace lo mismo. Utiliza la extensión XDamage para funcionar cuando algo ha cambiado, pero en lugar de utilizar la información del rectángulo dañado para actualizar solo esa parte del mapa de píxeles en caché, simplemente establece un indicador "sucio", que de todos modos activa una actualización completa.

Intento modificar el ejemplo de qx11mirror para actualizar la parte dañada de las ventanas, pero parece que no puedo hacer nada, todo lo que obtengo es un mapa de píxeles en blanco (negro). El código que estoy usando es:

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

¿Alguien puede señalarme en la dirección correcta? La documentación para XRender, XDamage y las otras extensiones X11 es bastante mala.

Razones para utilizar XRender sobre XCopyArea

El siguiente texto tomado de here.

Es posible crear un GC para una ventana y usar XCopyArea() para copiar el contenido de la ventana si desea usar el protocolo central, pero dado que la extensión Composite expone visuales nuevos (con canales alfa, por ejemplo), no hay garantía de que el formato de la fuente dibujable coincida con el del destino. Con el protocolo central esa situación dará como resultado un error de coincidencia, algo que no sucederá con la extensión de Xrender.

Además, el protocolo central no comprende los canales alfa, lo que significa que no puede componer ventanas que usan la nueva visualización ARGB. Cuando la fuente y el destino tienen el mismo formato, tampoco hay ventaja de rendimiento al usar el protocolo central a partir de X11R6.8. Esa versión también es la primera en admitir la nueva extensión compuesta.

Por lo tanto, en conclusión, no hay inconvenientes, y solo ventajas de elegir Xrender sobre el protocolo central para estas operaciones.

+0

¿Por qué utilizar XRender en lugar de XCopyArea? XRender es propenso a errores (si no lo haces exactamente de la manera correcta, no funcionará). – ypnos

+0

Descubrí que intentar actualizar la pantalla solo actualizando las piezas dañadas no funciona tan bien en la práctica. Por ejemplo, las aplicaciones OpenGL nunca parecen informar daños. Creo que tu mejor opción es seguir recapturando la pantalla. – ldog

+0

Puede volver a capturar la pantalla casi en tiempo real (10-20 fotogramas por segundo) si lo hace de manera inteligente. – ldog

Respuesta

3

Primero necesita cambiar el DamageReportLevel en la llamada a DamageCreate en QX11Mirror :: setWindow desde DamageReportNotEmpty a XDamageReportBoundingBox.

A continuación debe llamar a dest.detach() antes de la llamada a XRenderComposite. Sin embargo, en realidad no necesita tanto m_px como dest como variables miembro; puede simplemente usar m__px.

También hay una llamada que falta para XRenderFreePicture en ese ejemplo, que debe ir después de la llamada a XRenderComposite:

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

No es necesario duplicar todo el código QX11Mirror :: mapa de píxeles en QX11Mirror :: x11Event . En su lugar, cambie el tipo de m_dirty de bool a QRect, luego tenga x11Event update m_dirty con el rectángulo de XDamageNotifyEvent, es decir. e-> área. Luego, en QX11Mirror: pixmap en lugar de verificar si m_dirty es verdadero, compruebe si m_dirty no es un rectángulo vacío.Luego pasarás el rectángulo de m_dirty a XRenderComposite.

Editar:

El dest.detach es el paletón - sin que nunca va a conseguir que funcione.

Cuestiones relacionadas