احتفظ بنسخة QPixmap من محتويات الشاشة باستخدام X11 وXDamage وXRender وغيرها من الحيل
سؤال
أحاول حل ما اعتقدت أنه سيكون مشكلة بسيطة للغاية.أريد الاحتفاظ بـ 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 التجريبي, ومع ذلك، فإنه في الأساس يفعل نفس الشيء.فهو يستخدم امتداد XDamage لمعرفة متى يتغير شيء ما، ولكن بدلاً من استخدام معلومات المستطيل التالف لتحديث هذا الجزء من الصورة البيكسلية المخزنة مؤقتًا، فإنه يقوم فقط بتعيين علامة "قذرة"، مما يؤدي إلى تحديث كامل على أي حال.
لذلك أحاول تعديل مثال qx11mirror لتحديث الجزء التالف من النوافذ فقط، ولكن يبدو أنني لا أستطيع عمل أي شيء - كل ما أحصل عليه هو خريطة بيكسلية فارغة (سوداء).الكود الذي أستخدمه هو:
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 الأخرى سيئًا للغاية.
أسباب استخدام XRender عبر XCopyArea
النص التالي مأخوذ من هنا.
من الممكن تمامًا إنشاء GC لنافذة واستخدام XCopyArea() لنسخ محتويات النافذة إذا كنت تريد استخدام البروتوكول الأساسي، ولكن نظرًا لأن الامتداد المركب يعرض صورًا مرئية جديدة (تلك التي تحتوي على قنوات ألفا على سبيل المثال)، فلا يوجد ضمان أن تنسيق المصدر الرسومي سوف يتطابق مع تنسيق الوجهة.مع البروتوكول الأساسي، سيؤدي هذا الموقف إلى خطأ في المطابقة، وهو أمر لن يحدث مع ملحق Xrender.
بالإضافة إلى ذلك، لا يفهم البروتوكول الأساسي قنوات ألفا، مما يعني أنه لا يمكنه تركيب النوافذ التي تستخدم مرئيات ARGB الجديدة.عندما يكون للمصدر والوجهة نفس التنسيق، فلن تكون هناك أيضًا ميزة أداء لاستخدام البروتوكول الأساسي اعتبارًا من X11R6.8.يعد هذا الإصدار أيضًا أول إصدار يدعم الامتداد المركب الجديد.
لذا في الختام، لا توجد عيوب، ولكن هناك مزايا فقط لاختيار Xrender بدلاً من البروتوكول الأساسي لهذه العمليات.
المحلول
تحتاج أولاً إلى تغيير DamageReportLevel في استدعاء DamageCreate في QX11Mirror::setWindow من DamageReportNotEmpty إلى XDamageReportBoundingBox.
بعد ذلك، يتعين عليك الاتصال بـ dest.detach() قبل الاتصال بـ XRenderComposite.لا تحتاج حقًا إلى كل من m_px وdest كمتغيرات للأعضاء - يمكنك فقط استخدام m__px.
يوجد أيضًا استدعاء مفقود لـ XRenderFreePicture في هذا المثال والذي يجب أن يتبع استدعاء XRenderComposite:
XRenderFreePicture(QX11Info::display(), picture);
لا تحتاج إلى تكرار كل الكود QX11Mirror::pixmap في QX11Mirror::x11Event.بدلاً من ذلك، قم بتغيير نوع m_dirty من bool إلى QRect، ثم قم بتحديث x11Event m_dirty بالمستطيل من XDamageNotifyEvent، أي.البريد->المنطقة.ثم في QX11Mirror:pixmap بدلاً من التحقق مما إذا كان m_dirty صحيحًا، تحقق مما إذا كان m_dirty ليس مستطيلاً فارغًا.يمكنك بعد ذلك تمرير المستطيل من m_dirty إلى XRenderComposite.
يحرر:
إن dest.detach هو الجزء الأساسي - وبدون ذلك لن تتمكن من تشغيله أبدًا.