Commit d593d6ef authored by Martin Flöser's avatar Martin Flöser

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
parent 30bc128e
...@@ -3,7 +3,7 @@ kde4_no_enable_final(kwineffects) ...@@ -3,7 +3,7 @@ kde4_no_enable_final(kwineffects)
macro( KWIN4_ADD_EFFECT_BACKEND name ) macro( KWIN4_ADD_EFFECT_BACKEND name )
kde4_add_plugin( ${name} ${ARGN} ) 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}) ${XCB_XFIXES_LIBRARIES})
endmacro( KWIN4_ADD_EFFECT_BACKEND ) endmacro( KWIN4_ADD_EFFECT_BACKEND )
......
...@@ -26,9 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -26,9 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnection>
#include <QtCore/QVarLengthArray> #include <QtCore/QVarLengthArray>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <xcb/xcb_image.h>
#include <X11/extensions/Xfixes.h>
#include <QX11Info>
namespace KWin namespace KWin
{ {
...@@ -55,6 +53,24 @@ ScreenShotEffect::~ScreenShotEffect() ...@@ -55,6 +53,24 @@ ScreenShotEffect::~ScreenShotEffect()
QDBusConnection::sessionBus().unregisterObject("/Screenshot"); QDBusConnection::sessionBus().unregisterObject("/Screenshot");
QDBusConnection::sessionBus().unregisterService("org.kde.kwin.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() void ScreenShotEffect::postPaintScreen()
{ {
effects->postPaintScreen(); effects->postPaintScreen();
...@@ -129,18 +145,12 @@ void ScreenShotEffect::postPaintScreen() ...@@ -129,18 +145,12 @@ void ScreenShotEffect::postPaintScreen()
ScreenShotEffect::convertFromGLImage(img, width, height); ScreenShotEffect::convertFromGLImage(img, width, height);
} }
#ifdef KWIN_HAVE_XRENDER_COMPOSITING #ifdef KWIN_HAVE_XRENDER_COMPOSITING
xcb_image_t *xImage = NULL;
if (effects->compositingType() == XRenderCompositing) { if (effects->compositingType() == XRenderCompositing) {
setXRenderOffscreen(true); setXRenderOffscreen(true);
effects->drawWindow(m_scheduledScreenshot, mask, QRegion(0, 0, width, height), d); effects->drawWindow(m_scheduledScreenshot, mask, QRegion(0, 0, width, height), d);
if (xRenderOffscreenTarget()) { if (xRenderOffscreenTarget()) {
xcb_pixmap_t xpix = xcb_generate_id(connection()); img = xPictureToImage(xRenderOffscreenTarget(), QRect(0, 0, width, height), &xImage);
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);
} }
setXRenderOffscreen(false); setXRenderOffscreen(false);
} }
...@@ -150,18 +160,22 @@ void ScreenShotEffect::postPaintScreen() ...@@ -150,18 +160,22 @@ void ScreenShotEffect::postPaintScreen()
grabPointerImage(img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top); grabPointerImage(img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top);
} }
m_lastScreenshot = QPixmap::fromImage(img); const int depth = img.depth();
if (m_lastScreenshot.handle() == 0) { xcb_pixmap_t xpix = xcb_generate_id(connection());
Pixmap xpix = XCreatePixmap(display(), rootWindow(), m_lastScreenshot.width(), xcb_create_pixmap(connection(), depth, xpix, rootWindow(), img.width(), img.height());
m_lastScreenshot.height(), 32);
m_lastScreenshot = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); xcb_gcontext_t cid = xcb_generate_id(connection());
QPainter p(&m_lastScreenshot); xcb_create_gc(connection(), cid, xpix, 0, NULL);
p.setCompositionMode(QPainter::CompositionMode_Source); xcb_put_image(connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(),
p.drawImage(QPoint(0, 0), img); 0, 0, 0, depth, img.byteCount(), img.constBits());
p.end(); xcb_free_gc(connection(), cid);
XSync(display(), False); 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; m_scheduledScreenshot = NULL;
} }
...@@ -280,16 +294,12 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry) ...@@ -280,16 +294,12 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height()); ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height());
} }
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
xcb_image_t *xImage = NULL;
#endif
if (effects->compositingType() == XRenderCompositing) { if (effects->compositingType() == XRenderCompositing) {
#ifdef KWIN_HAVE_XRENDER_COMPOSITING #ifdef KWIN_HAVE_XRENDER_COMPOSITING
QPixmap buffer(geometry.size()); img = xPictureToImage(effects->xrenderBufferPicture(), geometry, &xImage);
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();
#endif #endif
} }
...@@ -300,6 +310,11 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry) ...@@ -300,6 +310,11 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
return QString(); return QString();
} }
img.save(&temp); img.save(&temp);
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if (xImage) {
xcb_image_destroy(xImage);
}
#endif
temp.close(); temp.close();
return temp.fileName(); return temp.fileName();
#endif #endif
...@@ -308,24 +323,18 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry) ...@@ -308,24 +323,18 @@ QString ScreenShotEffect::blitScreenshot(const QRect &geometry)
void ScreenShotEffect::grabPointerImage(QImage& snapshot, int offsetx, int offsety) 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. // Uses the X11_EXTENSIONS_XFIXES_H extension to grab the pointer image, and overlays it onto the snapshot.
{ {
XFixesCursorImage *xcursorimg = XFixesGetCursorImage(QX11Info::display()); QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
if (!xcursorimg) xcb_xfixes_get_cursor_image_reply(connection(),
xcb_xfixes_get_cursor_image_unchecked(connection()),
NULL));
if (cursor.isNull())
return; return;
//Annoyingly, xfixes specifies the data to be 32bit, but places it in an unsigned long * QImage qcursorimg((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height,
//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::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
QPainter painter(&snapshot); QPainter painter(&snapshot);
painter.drawImage(QPointF(xcursorimg->x - xcursorimg->xhot - offsetx, xcursorimg->y - xcursorimg ->yhot - offsety), qcursorimg); painter.drawImage(QPointF(cursor->x - cursor->xhot - offsetx, cursor->y - cursor ->yhot - offsety), qcursorimg);
XFree(xcursorimg);
} }
void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h) void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h)
......
...@@ -84,7 +84,6 @@ private: ...@@ -84,7 +84,6 @@ private:
void restoreMatrix(); void restoreMatrix();
EffectWindow *m_scheduledScreenshot; EffectWindow *m_scheduledScreenshot;
ScreenShotType m_type; ScreenShotType m_type;
QPixmap m_lastScreenshot;
}; };
} // namespace } // namespace
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment