Port Screenshot effect to XCB

This includes:
* getting cursor image through xcb_xfixes
* using xcb_image_get for reading screen content in xrender case
* using xcb_put_image to upload the generated screenshot into shared xpix

Overall this means that QPixmap as an X Pixmap wrapper is no longer used.
The conversion from xcb_image_t to QImage still needs some code for not
matching byte order. I'm a little bit unsure about adding the code as I
would not be able to test it.

REVIEW: 109076
This commit is contained in:
Martin Gräßlin 2013-02-13 08:10:55 +01:00
parent 3809f58dbb
commit b8c6831a1f
3 changed files with 53 additions and 45 deletions

View file

@ -3,7 +3,7 @@ kde4_no_enable_final(kwineffects)
macro( KWIN4_ADD_EFFECT_BACKEND name )
kde4_add_plugin( ${name} ${ARGN} )
target_link_libraries( ${name} kwineffects ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB} ${X11_LIBRARIES} ${XCB_XCB_LIBRARIES} ${X11_XCB_LIBRARIES}
target_link_libraries( ${name} kwineffects ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB} ${X11_LIBRARIES} ${XCB_XCB_LIBRARIES} ${XCB_IMAGE_LIBRARIES} ${X11_XCB_LIBRARIES}
${XCB_XFIXES_LIBRARIES})
endmacro( KWIN4_ADD_EFFECT_BACKEND )

View file

@ -26,9 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QtDBus/QDBusConnection>
#include <QtCore/QVarLengthArray>
#include <QtGui/QPainter>
#include <X11/extensions/Xfixes.h>
#include <QX11Info>
#include <xcb/xcb_image.h>
namespace KWin
{
@ -55,6 +53,24 @@ ScreenShotEffect::~ScreenShotEffect()
QDBusConnection::sessionBus().unregisterObject("/Screenshot");
QDBusConnection::sessionBus().unregisterService("org.kde.kwin.Screenshot");
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
static QImage xPictureToImage(xcb_render_picture_t srcPic, const QRect &geometry, xcb_image_t **xImage)
{
xcb_pixmap_t xpix = xcb_generate_id(connection());
xcb_create_pixmap(connection(), 32, xpix, rootWindow(), geometry.width(), geometry.height());
XRenderPicture pic(xpix, 32);
xcb_render_composite(connection(), XCB_RENDER_PICT_OP_SRC, srcPic, XCB_RENDER_PICTURE_NONE, pic,
geometry.x(), geometry.y(), 0, 0, 0, 0, geometry.width(), geometry.height());
xcb_flush(connection());
*xImage = xcb_image_get(connection(), xpix, 0, 0, geometry.width(), geometry.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
QImage img((*xImage)->data, (*xImage)->width, (*xImage)->height, (*xImage)->stride, QImage::Format_ARGB32_Premultiplied);
// TODO: byte order might need swapping
xcb_free_pixmap(connection(), xpix);
return img;
}
#endif
void ScreenShotEffect::postPaintScreen()
{
effects->postPaintScreen();
@ -129,18 +145,12 @@ void ScreenShotEffect::postPaintScreen()
ScreenShotEffect::convertFromGLImage(img, width, height);
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
xcb_image_t *xImage = NULL;
if (effects->compositingType() == XRenderCompositing) {
setXRenderOffscreen(true);
effects->drawWindow(m_scheduledScreenshot, mask, QRegion(0, 0, width, height), d);
if (xRenderOffscreenTarget()) {
xcb_pixmap_t xpix = xcb_generate_id(connection());
xcb_create_pixmap(connection(), 32, xpix, rootWindow(), width, height);
// TODO: Qt5 - convert from xpixmap to QImage without a QPixmap
QPixmap pixmap = QPixmap::fromX11Pixmap(xpix);
xcb_render_composite(connection(), XCB_RENDER_PICT_OP_SRC, xRenderOffscreenTarget(),
XCB_RENDER_PICTURE_NONE, pixmap.x11PictureHandle(), 0, 0, 0, 0, 0, 0, width, height);
img = pixmap.toImage().copy(0, 0, width, height);
xcb_free_pixmap(connection(), xpix);
img = xPictureToImage(xRenderOffscreenTarget(), QRect(0, 0, width, height), &xImage);
}
setXRenderOffscreen(false);
}
@ -150,18 +160,22 @@ void ScreenShotEffect::postPaintScreen()
grabPointerImage(img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top);
}
m_lastScreenshot = QPixmap::fromImage(img);
if (m_lastScreenshot.handle() == 0) {
Pixmap xpix = XCreatePixmap(display(), rootWindow(), m_lastScreenshot.width(),
m_lastScreenshot.height(), 32);
m_lastScreenshot = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
QPainter p(&m_lastScreenshot);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.drawImage(QPoint(0, 0), img);
p.end();
XSync(display(), False);
const int depth = img.depth();
xcb_pixmap_t xpix = xcb_generate_id(connection());
xcb_create_pixmap(connection(), depth, xpix, rootWindow(), img.width(), img.height());
xcb_gcontext_t cid = xcb_generate_id(connection());
xcb_create_gc(connection(), cid, xpix, 0, NULL);
xcb_put_image(connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(),
0, 0, 0, depth, img.byteCount(), img.constBits());
xcb_free_gc(connection(), cid);
xcb_flush(connection());
emit screenshotCreated(xpix);
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if (xImage) {
xcb_image_destroy(xImage);
}
emit screenshotCreated(m_lastScreenshot.handle());
#endif
}
m_scheduledScreenshot = NULL;
}
@ -280,16 +294,12 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height());
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
xcb_image_t *xImage = NULL;
#endif
if (effects->compositingType() == XRenderCompositing) {
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
QPixmap buffer(geometry.size());
if (buffer.handle() == 0) {
Pixmap xpix = XCreatePixmap(display(), rootWindow(), geometry.width(), geometry.height(), 32);
buffer = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
}
XRenderComposite(display(), PictOpSrc, effects->xrenderBufferPicture(), None, buffer.x11PictureHandle(),
0, 0, 0, 0, geometry.x(), geometry.y(), geometry.width(), geometry.height());
img = buffer.toImage();
img = xPictureToImage(effects->xrenderBufferPicture(), geometry, &xImage);
#endif
}
@ -300,6 +310,11 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
return QString();
}
img.save(&temp);
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if (xImage) {
xcb_image_destroy(xImage);
}
#endif
temp.close();
return temp.fileName();
#endif
@ -308,24 +323,18 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
void ScreenShotEffect::grabPointerImage(QImage& snapshot, int offsetx, int offsety)
// Uses the X11_EXTENSIONS_XFIXES_H extension to grab the pointer image, and overlays it onto the snapshot.
{
XFixesCursorImage *xcursorimg = XFixesGetCursorImage(QX11Info::display());
if (!xcursorimg)
QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
xcb_xfixes_get_cursor_image_reply(connection(),
xcb_xfixes_get_cursor_image_unchecked(connection()),
NULL));
if (cursor.isNull())
return;
//Annoyingly, xfixes specifies the data to be 32bit, but places it in an unsigned long *
//which can be 64 bit. So we need to iterate over a 64bit structure to put it in a 32bit
//structure.
QVarLengthArray< quint32 > pixels(xcursorimg->width * xcursorimg->height);
for (int i = 0; i < xcursorimg->width * xcursorimg->height; ++i)
pixels[i] = xcursorimg->pixels[i] & 0xffffffff;
QImage qcursorimg((uchar *) pixels.data(), xcursorimg->width, xcursorimg->height,
QImage qcursorimg((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
QImage::Format_ARGB32_Premultiplied);
QPainter painter(&snapshot);
painter.drawImage(QPointF(xcursorimg->x - xcursorimg->xhot - offsetx, xcursorimg->y - xcursorimg ->yhot - offsety), qcursorimg);
XFree(xcursorimg);
painter.drawImage(QPointF(cursor->x - cursor->xhot - offsetx, cursor->y - cursor ->yhot - offsety), qcursorimg);
}
void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h)

View file

@ -84,7 +84,6 @@ private:
void restoreMatrix();
EffectWindow *m_scheduledScreenshot;
ScreenShotType m_type;
QPixmap m_lastScreenshot;
};
} // namespace