2009-09-15 5 views
15

Je tente de résoudre ce que je pensais être un problème très simple. Je veux garder une QPixmap à jour avec tout le contenu de l'écran. Vous pouvez obtenir un tel pixmap en faisant ceci:Conserver QPixmap copier le contenu de l'écran en utilisant X11, XDamage, XRender, et d'autres trucs

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

Le problème est que QDesktopWidget finit par re-saisir tout l'écran pixmap du serveur X11 chaque fois que vous demandez, même si rien n'a changé.

J'ai besoin de ce code pour être rapide, donc j'essaye de le faire moi-même. Mon point de départ était le qx11mirror demo, cependant, cela fait essentiellement la même chose. Il utilise l'extension XDamage pour fonctionner quand quelque chose a changé, mais au lieu d'utiliser les informations du rectangle endommagé pour mettre à jour cette partie de la pixmap en cache, il définit simplement un drapeau "dirty", qui déclenche tout un rafraîchissement. J'essaie donc de modifier l'exemple qx11mirror pour simplement mettre à jour la partie endommagée des fenêtres, mais je n'arrive pas à faire quoi que ce soit au travail - tout ce que je reçois est un pixmap vide (noir). Le code que j'utilise est:

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

Quelqu'un peut-il me diriger dans la bonne direction? La documentation pour XRender, XDamage et les autres extensions X11 est plutôt mauvaise.

Raisons d'utiliser XRender sur XCopyArea

Le texte suivant extrait de here.

Il est parfaitement possible de créer un GC pour une fenêtre et utiliser XCopyArea() pour copier le contenu de la fenêtre si vous voulez utiliser le protocole de base, mais puisque l'extension Composite expose de nouveaux visuels), il n'y a aucune garantie que le format de la source dessinable correspondra à celui de la destination. Avec le protocole de base, cette situation entraînera une erreur de correspondance, ce qui ne se produira pas avec l'extension Xrender.

En outre, le protocole de base ne comprend pas les canaux alpha, ce qui signifie qu'il ne peut pas composer des fenêtres qui utilisent le nouveau visuel ARGB. Lorsque la source et la destination ont le même format, il n'y a pas non plus d'avantage de performance à utiliser le protocole de base à partir de X11R6.8. Cette version est également la première à supporter la nouvelle extension Composite. Donc, en conclusion, il n'y a pas d'inconvénients, et seulement des avantages à choisir Xrender sur le protocole de base pour ces opérations.

+0

Pourquoi? XRender est sujet à erreur (si vous ne le faites pas exactement dans le bon sens, cela ne fonctionnera pas ..) – ypnos

+0

J'ai trouvé que d'essayer de mettre à jour l'écran que la mise à jour des pièces endommagées ne fonctionne pas très bien dans la pratique. Par exemple, les applications OpenGL ne semblent jamais signaler de dommages. Je pense que votre meilleur pari est de continuer à recapturer l'écran. – ldog

+0

Vous pouvez recapturer l'écran en temps quasi réel (10-20 images par secondes) si vous le faites intelligemment. – ldog

Répondre

3

Vous devez d'abord modifier DamageReportLevel dans l'appel de DamageCreate dans QX11Mirror :: setWindow de DamageReportNotEmpty à XDamageReportBoundingBox.

Ensuite, vous devez appeler dest.detach() avant l'appel à XRenderComposite. Vous n'avez cependant pas vraiment besoin de m_px et de dest en tant que variables membres - vous pouvez simplement utiliser m__px.

Il y a aussi un appel manquant à XRenderFreePicture dans cet exemple qui devrait aller après l'appel à XRenderComposite:

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

Vous n'avez pas besoin de dupliquer tout le code QX11Mirror :: pixmap dans QX11Mirror :: x11Event . Au lieu de cela, changez le type de m_dirty de bool en QRect, puis mettez x11Event à jour m_dirty avec le rectangle de XDamageNotifyEvent, par exemple. e-> zone. Puis dans QX11Mirror: pixmap plutôt que de vérifier si m_dirty est vrai, vérifiez si m_dirty n'est pas un rectangle vide.Vous devez ensuite passer le rectangle de m_dirty à XRenderComposite.

Edit:

Le dest.detach est la clé bit - sans que vous ne serez jamais à travailler. Pourquoi utiliser XRender au lieu de XCopyArea?

Questions connexes