X11、XDamage、XRender、およびその他のトリックを使用して、画面コンテンツのQPixmapコピーを保持する
質問
非常に単純な問題だと思っていたものを解決しようとしています。画面のコンテンツ全体でQPixmapを更新し続けたい。これを行うことで、このようなピックスマップを取得できます。
QDesktopWidget *w = QApplication::desktop();
if (w)
{
QRect r = w->screenGeometry();
QPixmap p = QPixmap::grabWindow(w->winId(), 0, 0, r.width(), r.height())
QByteArray bitmap;
}
これに関する問題は、QDesktopWidgetが何も変更していなくても、要求するたびにX11サーバーから画面ピックスマップ全体を再取得することです。
このコードは高速である必要があるため、これを自分でやろうとしています。私の出発点は qx11mirror demo でしたが、基本的に同じことを行います。 XDamage拡張機能を使用して何かが変更されたときに解決しますが、破損した四角形情報を使用してキャッシュされたピックスマップのその部分を更新する代わりに、単に「ダーティ」を設定します。とにかく全体の更新をトリガーするフラグ。
そのため、qx11mirrorの例を修正して、破損したウィンドウ部分を更新しようとしていますが、何も動作しないようです-取得できるのは空の(黒い)pixmapだけです。私が使用しているコードは次のとおりです。
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);
}
}
誰かが私を正しい方向に向けることができますか? XRender、XDamage、およびその他のX11拡張機能の説明はかなり悪いです。
XCopyAreaでXRenderを使用する理由
こちらから抜粋した次のテキスト。
コアプロトコルを使用する場合、ウィンドウのGCを作成し、XCopyArea()を使用してウィンドウのコンテンツをコピーすることは完全に可能ですが、Composite拡張機能は新しいビジュアル(アルファチャネルを持つものなど)を公開するため、ソースのドロアブルの形式が宛先の形式と一致するという保証はありません。コアプロトコルでは、この状況は一致エラーになります。Xrender拡張機能では発生しません。
さらに、コアプロトコルはアルファチャネルを理解していません。つまり、新しいARGBビジュアルを使用するウィンドウを合成することはできません。送信元と宛先のフォーマットが同じ場合、X11R6.8の時点でコアプロトコルを使用してもパフォーマンス上の利点はありません。このリリースは、新しいComposite拡張機能をサポートする最初の製品でもあります。
結論として、これらの操作にコアプロトコルよりもXrenderを選択することには、欠点はなく、利点のみがあります。
解決
まず、QX11Mirror :: setWindowのDamageCreateの呼び出しでDamageReportLevelをDamageReportNotEmptyからXDamageReportBoundingBoxに変更する必要があります。
次に、XRenderCompositeを呼び出す前にdest.detach()を呼び出す必要があります。ただし、メンバー変数としてm_pxとdestの両方は実際には必要ありません-m__pxを使用するだけです。
この例では、XRenderCompositeの呼び出しの後に実行する必要があるXRenderFreePictureの呼び出しもありません:
XRenderFreePicture(QX11Info::display(), picture);
すべてのコードQX11Mirror :: pixmapをQX11Mirror :: x11Eventに複製する必要はありません。代わりに、m_dirtyのタイプをboolからQRectに変更してから、x11EventにXDamageNotifyEventの四角形でm_dirtyを更新させます。 e-&gt;エリア。次に、m_dirtyがtrueであるかどうかを確認するのではなく、QX11Mirror:pixmapで、m_dirtyが空の長方形でないかどうかを確認します。次に、長方形をm_dirtyからXRenderCompositeに渡します。
編集:
dest.detachは重要なビットです-それなしでは動作しません。