Conservez une copie QPixmap du contenu de l'écran à l'aide de X11, XDamage, XRender et autres astuces

StackOverflow https://stackoverflow.com/questions/1427711

  •  07-07-2019
  •  | 
  •  

Question

Je tente de résoudre ce que je pensais être un problème très simple. Je souhaite garder un QPixmap à jour avec tout le contenu de l'écran. Vous pouvez obtenir un tel pixmap en faisant ceci:

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, c’est que QDesktopWidget finit par saisir à nouveau chaque pixmap d’écran du serveur X11 à chaque fois que vous le demandez, même si rien n’a changé.

J'ai besoin que ce code soit rapide, alors j'essaie de le faire moi-même. Mon point de départ a été la démonstration de qx11mirror , qui fait essentiellement la même chose. Il utilise l'extension XDamage pour déterminer si quelque chose a changé, mais au lieu d'utiliser les informations rectangulaires endommagées pour mettre à jour simplement cette partie du pixmap mis en cache, il définit simplement un paramètre "sale". indicateur qui déclenche de toute façon un rafraîchissement complet.

J'essaie donc de modifier l'exemple qx11mirror pour ne mettre à jour que la partie endommagée des fenêtres, mais je n'arrive pas à faire fonctionner quoi que ce soit - tout ce que je reçois est un pixmap vide (noir). Le code que j'utilise est le suivant:

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 m'indiquer dans la bonne direction? La documentation de XRender, XDamage et des autres extensions X11 est plutôt mauvaise.

Raisons de l'utilisation de XRender sur XCopyArea

Le texte suivant, extrait de ici .

Il est parfaitement possible de créer un CPG pour une fenêtre et d’utiliser XCopyArea () pour copier le contenu de la fenêtre si vous souhaitez utiliser le protocole de base, mais puisque l’extension Composite expose de nouveaux visuels (avec des canaux alpha, par exemple). , rien ne garantit que le format de la source pouvant être dessinée 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.

De plus, le protocole principal ne comprend pas les canaux alpha, ce qui signifie qu'il ne peut pas composer de fenêtres utilisant le nouveau visuel ARGB. Lorsque la source et la destination ont le même format, l'utilisation du protocole de base ne présente aucun avantage en termes de performances à partir de X11R6.8. Cette version est également la première à prendre en charge la nouvelle extension Composite.

En conclusion, il n'y a pas d'inconvénients, mais seulement des avantages à choisir Xrender au lieu du protocole principal pour ces opérations.

Était-ce utile?

La solution

D'abord, vous devez modifier DamageReportLevel dans l'appel à DamageCreate dans QX11Mirror :: setWindow de DamageReportNotEmpty à XDamageReportBoundingBox.

Ensuite, vous devez appeler dest.detach () avant l'appel de XRenderComposite. Cependant, vous n’avez pas vraiment besoin de m_px et de dest comme variables membres. Vous pouvez simplement utiliser m__px.

Il y a également un appel manquant à XRenderFreePicture dans cet exemple, qui devrait suivre après l'appel à XRenderComposite:

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

Vous n'avez pas besoin de dupliquer tout le code QX11Mirror :: pixmap dans QX11Mirror :: x11Event. À la place, changez le type de m_dirty de bool en QRect, puis installez x11Event update m_dirty avec le rectangle de XDamageNotifyEvent, c.-à-d. e- > zone. Ensuite, 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.

Modifier:

Le dest.detach est le bit clé - sans cela, vous ne le réussirez jamais.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top