diff --git a/autotests/mock_effectshandler.h b/autotests/mock_effectshandler.h --- a/autotests/mock_effectshandler.h +++ b/autotests/mock_effectshandler.h @@ -245,6 +245,10 @@ void showCursor() override {} + void startInteractiveWindowSelection(std::function callback) override { + callback(nullptr); + } + private: bool m_animationsSuported = true; }; diff --git a/effects.h b/effects.h --- a/effects.h +++ b/effects.h @@ -231,6 +231,8 @@ void hideCursor() override; void showCursor() override; + void startInteractiveWindowSelection(std::function callback) override; + Scene *scene() const { return m_scene; } diff --git a/effects.cpp b/effects.cpp --- a/effects.cpp +++ b/effects.cpp @@ -1572,6 +1572,19 @@ kwinApp()->platform()->showCursor(); } +void EffectsHandlerImpl::startInteractiveWindowSelection(std::function callback) +{ + kwinApp()->platform()->startInteractiveWindowSelection( + [callback] (KWin::Toplevel *t) { + if (t && t->effectWindow()) { + callback(t->effectWindow()); + } else { + callback(nullptr); + } + } + ); +} + //**************************************** // EffectWindowImpl //**************************************** diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h --- a/effects/screenshot/screenshot.h +++ b/effects/screenshot/screenshot.h @@ -54,6 +54,16 @@ static void convertFromGLImage(QImage &img, int w, int h); public Q_SLOTS: Q_SCRIPTABLE void screenshotForWindow(qulonglong winid, int mask = 0); + /** + * Starts an interactive window screenshot session. The user can select a window to + * screenshot. + * + * Once the window is selected the screenshot is saved into a file and the path gets + * returned to the DBus peer. + * + * @param mask The mask for what to include in the screenshot + **/ + Q_SCRIPTABLE QString interactive(int mask = 0); Q_SCRIPTABLE void screenshotWindowUnderCursor(int mask = 0); /** * Saves a screenshot of all screen into a file and returns the path to the file. @@ -102,6 +112,12 @@ QImage m_multipleOutputsImage; QRegion m_multipleOutputsRendered; bool m_captureCursor = false; + enum class WindowMode { + NoCapture, + Xpixmap, + File + }; + WindowMode m_windowMode = WindowMode::NoCapture; }; } // namespace diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -168,17 +168,22 @@ grabPointerImage(img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top); } - const int depth = img.depth(); - xcb_pixmap_t xpix = xcb_generate_id(xcbConnection()); - xcb_create_pixmap(xcbConnection(), depth, xpix, x11RootWindow(), img.width(), img.height()); - - xcb_gcontext_t cid = xcb_generate_id(xcbConnection()); - xcb_create_gc(xcbConnection(), cid, xpix, 0, NULL); - xcb_put_image(xcbConnection(), XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(), - 0, 0, 0, depth, img.byteCount(), img.constBits()); - xcb_free_gc(xcbConnection(), cid); - xcb_flush(xcbConnection()); - emit screenshotCreated(xpix); + if (m_windowMode == WindowMode::Xpixmap) { + const int depth = img.depth(); + xcb_pixmap_t xpix = xcb_generate_id(xcbConnection()); + xcb_create_pixmap(xcbConnection(), depth, xpix, x11RootWindow(), img.width(), img.height()); + + xcb_gcontext_t cid = xcb_generate_id(xcbConnection()); + xcb_create_gc(xcbConnection(), cid, xpix, 0, NULL); + xcb_put_image(xcbConnection(), XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(), + 0, 0, 0, depth, img.byteCount(), img.constBits()); + xcb_free_gc(xcbConnection(), cid); + xcb_flush(xcbConnection()); + emit screenshotCreated(xpix); + m_windowMode = WindowMode::NoCapture; + } else if (m_windowMode == WindowMode::File) { + sendReplyImage(img); + } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (xImage) { xcb_image_destroy(xImage); @@ -229,6 +234,7 @@ m_multipleOutputsImage = QImage(); m_multipleOutputsRendered = QRegion(); m_captureCursor = false; + m_windowMode = WindowMode::NoCapture; } QString ScreenShotEffect::saveTempImage(const QImage &img) @@ -274,11 +280,40 @@ m_type = (ScreenShotType) mask; EffectWindow* w = effects->findWindow(winid); if(w && !w->isMinimized() && !w->isDeleted()) { + m_windowMode = WindowMode::Xpixmap; m_scheduledScreenshot = w; m_scheduledScreenshot->addRepaintFull(); } } +QString ScreenShotEffect::interactive(int mask) +{ + if (!calledFromDBus()) { + return QString(); + } + if (!m_scheduledGeometry.isNull() || m_windowMode != WindowMode::NoCapture) { + sendErrorReply(QDBusError::Failed, "A screenshot is already been taken"); + return QString(); + } + m_type = (ScreenShotType) mask; + m_windowMode = WindowMode::File; + m_replyConnection = connection(); + m_replyMessage = message(); + setDelayedReply(true); + effects->startInteractiveWindowSelection( + [this] (EffectWindow *w) { + if (!w) { + m_replyConnection.send(m_replyMessage.createErrorReply(QDBusError::Failed, "Screenshot got cancelled")); + m_windowMode = WindowMode::NoCapture; + return; + } else { + m_scheduledScreenshot = w; + m_scheduledScreenshot->addRepaintFull(); + } + }); + return QString(); +} + QString ScreenShotEffect::screenshotFullscreen(bool captureCursor) { if (!calledFromDBus()) { diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -48,6 +48,8 @@ #include #include +#include + class KConfigGroup; class QFont; class QGraphicsScale; @@ -1198,6 +1200,20 @@ **/ virtual void showCursor() = 0; + /** + * Starts an interactive window selection process. + * + * Once the user selected a window the @p callback is invoked with the selected EffectWindow as + * argument. In case the user cancels the interactive window selection or selecting a window is currently + * not possible (e.g. screen locked) the @p callback is invoked with a @c nullptr argument. + * + * During the interactive window selection the cursor is turned into a crosshair cursor. + * + * @param callback The function to invoke once the interactive window selection ends + * @since 5.9 + **/ + virtual void startInteractiveWindowSelection(std::function callback) = 0; + Q_SIGNALS: /** * Signal emitted when the current desktop changed.