[effects] Add interactive window selection mode to ScreenshotEffect

Summary:
EffectsHandler gains a new method to startInteractiveWindowSelection
which just delegates to the one in Platform. That way a window can be
selected and returned to an Effect.

The screenshot effect makes use of this new functionality and provides
an interactive window screenshot mode which saves to a temporary file.
Note that this is not yet the variant intended for use in spectacle.

Test Plan: Took a screenshot on Wayland

Reviewers: #kwin, #plasma_on_wayland

Subscribers: plasma-devel, kwin

Tags: #plasma_on_wayland, #kwin

Differential Revision: https://phabricator.kde.org/D3367
This commit is contained in:
Martin Gräßlin 2016-11-15 15:40:30 +01:00
parent e5f02e822d
commit 27376e39ef
6 changed files with 96 additions and 10 deletions

View file

@ -245,6 +245,10 @@ public:
void showCursor() override {}
void startInteractiveWindowSelection(std::function<void(KWin::EffectWindow*)> callback) override {
callback(nullptr);
}
private:
bool m_animationsSuported = true;
};

View file

@ -1572,6 +1572,19 @@ void EffectsHandlerImpl::showCursor()
kwinApp()->platform()->showCursor();
}
void EffectsHandlerImpl::startInteractiveWindowSelection(std::function<void(KWin::EffectWindow*)> callback)
{
kwinApp()->platform()->startInteractiveWindowSelection(
[callback] (KWin::Toplevel *t) {
if (t && t->effectWindow()) {
callback(t->effectWindow());
} else {
callback(nullptr);
}
}
);
}
//****************************************
// EffectWindowImpl
//****************************************

View file

@ -231,6 +231,8 @@ public:
void hideCursor() override;
void showCursor() override;
void startInteractiveWindowSelection(std::function<void(KWin::EffectWindow*)> callback) override;
Scene *scene() const {
return m_scene;
}

View file

@ -168,17 +168,22 @@ void ScreenShotEffect::postPaintScreen()
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());
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);
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 @@ void ScreenShotEffect::sendReplyImage(const QImage &img)
m_multipleOutputsImage = QImage();
m_multipleOutputsRendered = QRegion();
m_captureCursor = false;
m_windowMode = WindowMode::NoCapture;
}
QString ScreenShotEffect::saveTempImage(const QImage &img)
@ -274,11 +280,40 @@ void ScreenShotEffect::screenshotForWindow(qulonglong winid, int mask)
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()) {

View file

@ -54,6 +54,16 @@ public:
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 @@ private:
QImage m_multipleOutputsImage;
QRegion m_multipleOutputsRendered;
bool m_captureCursor = false;
enum class WindowMode {
NoCapture,
Xpixmap,
File
};
WindowMode m_windowMode = WindowMode::NoCapture;
};
} // namespace

View file

@ -48,6 +48,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <limits.h>
#include <netwm.h>
#include <functional>
class KConfigGroup;
class QFont;
class QGraphicsScale;
@ -1198,6 +1200,20 @@ public:
**/
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<void(KWin::EffectWindow*)> callback) = 0;
Q_SIGNALS:
/**
* Signal emitted when the current desktop changed.