From 379e1a268b481d0d3dab68af8354fc4c9b47c342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 14 Sep 2010 19:52:44 +0000 Subject: [PATCH] Screenshot effect provides hooks for KSnapshot. This adds a DBus slot to capture a window and the pixmap handle is passed back via a DBus signal. Previous code using a shortcut and saving to filesystem is dropped completely in favor for ksnapshot. See http://reviewboard.kde.org/r/4814/ svn path=/trunk/KDE/kdebase/workspace/; revision=1175353 --- effects/CMakeLists.txt | 3 + effects/screenshot/screenshot.cpp | 108 ++++++++++++++++++++++-------- effects/screenshot/screenshot.h | 15 ++++- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 6e1e4e3202..57f1262a53 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -8,6 +8,9 @@ kde4_no_enable_final(kwineffects) macro( KWIN4_ADD_EFFECT name ) kde4_add_plugin( kwin4_effect_${name} ${ARGN} ) target_link_libraries( kwin4_effect_${name} kwineffects ${KDE4_KDEUI_LIBS} kephal ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB}) + if (X11_Xfixes_FOUND) + target_link_libraries(kwin4_effect_${name} ${X11_Xfixes_LIB}) + endif (X11_Xfixes_FOUND) install( TARGETS kwin4_effect_${name} DESTINATION ${PLUGIN_INSTALL_DIR} ) endmacro( KWIN4_ADD_EFFECT ) diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp index aaafc1f6ff..3d6bd4505c 100644 --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -20,9 +20,13 @@ along with this program. If not, see . *********************************************************************/ #include "screenshot.h" #include -#include -#include -#include +#include +#include +#include +#include + +#include +#include namespace KWin { @@ -38,15 +42,14 @@ bool ScreenShotEffect::supported() ScreenShotEffect::ScreenShotEffect() : m_scheduledScreenshot( 0 ) { - KActionCollection* actionCollection = new KActionCollection( this ); - KAction* cubeAction = static_cast< KAction* >( actionCollection->addAction( "Screenshot Effect" )); - cubeAction->setText( i18n("Save screenshot of active window" )); - cubeAction->setGlobalShortcut( KShortcut(), KAction::ActiveShortcut); - connect( cubeAction, SIGNAL(triggered(bool)), SLOT(screenshot()) ); + QDBusConnection::sessionBus().registerObject( "/Screenshot", this, QDBusConnection::ExportScriptableContents ); + QDBusConnection::sessionBus().registerService( "org.kde.kwin.Screenshot" ); } ScreenShotEffect::~ScreenShotEffect() { + QDBusConnection::sessionBus().unregisterObject( "/Screenshot" ); + QDBusConnection::sessionBus().unregisterService( "org.kde.kwin.Screenshot" ); } void ScreenShotEffect::postPaintScreen() { @@ -71,13 +74,36 @@ void ScreenShotEffect::postPaintScreen() double top = 0; double right = m_scheduledScreenshot->width(); double bottom = m_scheduledScreenshot->height(); - foreach( const WindowQuad& quad, d.quads ) + if( m_scheduledScreenshot->hasDecoration() && m_type & INCLUDE_DECORATION ) { - // we need this loop to include the decoration padding - left = qMin(left, quad.left()); - top = qMin(top, quad.top()); - right = qMax(right, quad.right()); - bottom = qMax(bottom, quad.bottom()); + foreach( const WindowQuad& quad, d.quads ) + { + // we need this loop to include the decoration padding + left = qMin(left, quad.left()); + top = qMin(top, quad.top()); + right = qMax(right, quad.right()); + bottom = qMax(bottom, quad.bottom()); + } + } + else if( m_scheduledScreenshot->hasDecoration() ) + { + WindowQuadList newQuads; + left = m_scheduledScreenshot->width(); + top = m_scheduledScreenshot->height(); + right = 0; + bottom = 0; + foreach( const WindowQuad& quad, d.quads ) + { + if( quad.type() == WindowQuadContents ) + { + newQuads << quad; + left = qMin(left, quad.left()); + top = qMin(top, quad.top()); + right = qMax(right, quad.right()); + bottom = qMax(bottom, quad.bottom()); + } + } + d.quads = newQuads; } int width = right - left; int height = bottom - top; @@ -103,17 +129,12 @@ void ScreenShotEffect::postPaintScreen() tex->unbind(); delete tex; ScreenShotEffect::convertFromGLImage( img, width, height ); - - // save screenshot in home directory - QString filePart( QDir::homePath() + '/' + m_scheduledScreenshot->caption() ); - QString file( filePart + ".png" ); - int counter = 1; - while( QFile::exists( file ) ) + if( m_type & INCLUDE_CURSOR ) { - file = QString( filePart + '_' + QString::number( counter ) + ".png" ); - counter++; + grabPointerImage( img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top ); } - img.save( file ); + m_lastScreenshot = QPixmap::fromImage( img ); + emit screenshotCreated( m_lastScreenshot.handle() ); } delete offscreenTexture; delete target; @@ -121,13 +142,46 @@ void ScreenShotEffect::postPaintScreen() } } -void ScreenShotEffect::screenshot() +void ScreenShotEffect::screenshotWindowUnderCursor(int mask) { - EffectWindow* w = effects->activeWindow(); - m_scheduledScreenshot = w; - w->addRepaintFull(); + m_type = (ScreenShotType)mask; + const QPoint cursor = effects->cursorPos(); + foreach( EffectWindow* w, effects->stackingOrder() ) + { + if( w->geometry().contains( cursor ) && w->isOnCurrentDesktop() && !w->isMinimized() ) + { + m_scheduledScreenshot = w; + } + } + if( m_scheduledScreenshot ) + { + m_scheduledScreenshot->addRepaintFull(); + } } +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 ) + 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::Format_ARGB32_Premultiplied); + + QPainter painter(&snapshot); + painter.drawImage(QPointF(xcursorimg->x - xcursorimg->xhot - offsetx, xcursorimg->y - xcursorimg ->yhot - offsety), qcursorimg); + + XFree(xcursorimg); +} + void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h) { // from QtOpenGL/qgl.cpp diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h index 41cc8b006f..b1d98e8fbc 100644 --- a/effects/screenshot/screenshot.h +++ b/effects/screenshot/screenshot.h @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include +#include namespace KWin { @@ -30,7 +31,13 @@ namespace KWin class ScreenShotEffect : public QObject, public Effect { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Screenshot") public: + enum ScreenShotType + { + INCLUDE_DECORATION = 1 << 0, + INCLUDE_CURSOR = 1 << 1 + }; ScreenShotEffect(); virtual ~ScreenShotEffect(); virtual void postPaintScreen(); @@ -38,10 +45,16 @@ class ScreenShotEffect : public QObject, public Effect static bool supported(); static void convertFromGLImage(QImage &img, int w, int h); public Q_SLOTS: - void screenshot(); + Q_SCRIPTABLE void screenshotWindowUnderCursor( int mask = 0 ); + + Q_SIGNALS: + Q_SCRIPTABLE void screenshotCreated( qulonglong handle ); private: + void grabPointerImage( QImage& snapshot, int offsetx, int offsety ); EffectWindow *m_scheduledScreenshot; + ScreenShotType m_type; + QPixmap m_lastScreenshot; }; } // namespace