15 Stimmen

QPixmap-Kopien von Bildschirminhalten mit X11, XDamage, XRender und anderen Tricks aufbewahren

Ich versuche zu lösen, was ich für ein sehr einfaches Problem hielt. Ich möchte eine QPixmap mit dem gesamten Bildschirminhalt auf dem neuesten Stand halten. Sie können eine solche Pixmap folgendermaßen erhalten:

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

Das Problem dabei ist, dass QDesktopWidget jedes Mal, wenn Sie danach fragen, die gesamte Bildschirmdarstellung erneut vom X11-Server abruft, auch wenn sich nichts geändert hat.

Der Code muss schnell sein, also versuche ich, ihn selbst zu schreiben. Mein Ausgangspunkt war die qx11Spiegel Demo die im Grunde das Gleiche tut. Es verwendet die XDamage-Erweiterung, um herauszufinden, wann sich etwas geändert hat, aber anstatt die Informationen über das beschädigte Rechteck zu verwenden, um nur diesen Teil der zwischengespeicherten Pixmap zu aktualisieren, wird einfach ein "Dirty"-Flag gesetzt, das ohnehin eine vollständige Aktualisierung auslöst.

Ich versuche, das qx11mirror-Beispiel zu ändern, um nur den beschädigten Teil des Windows zu aktualisieren, aber ich kann nicht scheinen, um etwas zu arbeiten - alles, was ich bekomme, ist eine leere (schwarz) pixmap. Der Code, den ich verwende, ist:

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

Kann mir jemand die richtige Richtung weisen? Die Dokumentation für XRender, XDamage und die anderen X11-Erweiterungen ist ziemlich schlecht.

Gründe für die Verwendung von XRender gegenüber XCopyArea

Der folgende Text stammt aus aquí .

Es ist durchaus möglich, eine GC für ein Fenster zu erstellen und XCopyArea() zu verwenden, um den Inhalt des Fensters zu kopieren, wenn Sie das Kernprotokoll verwenden möchten, aber da die Composite-Erweiterung neue visuelle Elemente (z. B. solche mit Alphakanälen) bereitstellt, gibt es keine Garantie, dass das Format des Quell-Drawables mit dem des Ziels übereinstimmt. Mit dem Kernprotokoll wird diese Situation zu einem Match-Fehler führen, was mit der Xrender-Erweiterung nicht passieren wird.

Darüber hinaus versteht das Kernprotokoll keine Alphakanäle, was bedeutet, dass es keine Composite-Windows, die das neue ARGB-Visual verwenden, verarbeiten kann. Wenn die Quelle und das Ziel dasselbe Format haben, gibt es auch keinen Leistungsvorteil bei der Verwendung des Kernprotokolls ab X11R6.8. Diese Version ist auch die erste, die die neue Composite-Erweiterung unterstützt.

Zusammenfassend lässt sich also sagen, dass es keine Nachteile, sondern nur Vorteile hat, Xrender gegenüber dem Kernprotokoll für diese Vorgänge zu wählen.

3voto

atomice Punkte 3002

Zuerst müssen Sie den DamageReportLevel im Aufruf von DamageCreate in QX11Mirror::setWindow von DamageReportNotEmpty auf XDamageReportBoundingBox ändern.

Als nächstes müssen Sie dest.detach() vor dem Aufruf von XRenderComposite aufrufen. Sie brauchen nicht wirklich sowohl m_px als auch dest als Mitgliedsvariablen - Sie können einfach m__px verwenden.

In diesem Beispiel fehlt auch ein Aufruf von XRenderFreePicture, der nach dem Aufruf von XRenderComposite erfolgen sollte:

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

Sie brauchen nicht den gesamten Code von QX11Mirror::pixmap in QX11Mirror::x11Event zu duplizieren. Ändern Sie stattdessen den Typ von m_dirty von bool auf QRect, und lassen Sie x11Event m_dirty mit dem Rechteck aus dem XDamageNotifyEvent aktualisieren, d.h. e->area. In QX11Mirror:pixmap wird dann nicht geprüft, ob m_dirty true ist, sondern ob m_dirty kein leeres Rechteck ist. Sie würden dann das Rechteck von m_dirty an XRenderComposite übergeben.

Edit :

Das dest.detach ist das Schlüsselbauteil - ohne das wird es nie funktionieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X