aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Gräßlin <[email protected]>2016-11-18 10:02:04 +0100
committerMartin Gräßlin <[email protected]>2016-11-22 07:17:57 +0100
commitfbab20496894967f5222a080e6e96bb4f0710519 (patch)
tree3c4e80491a2804c708e323e42fed537d86480dcb
parentcfdc1acbd384e46a1d87af69e19f2a4a1b33d476 (diff)
[effects] Add a screenshot dbus method which takes a file descriptor
Summary: The idea behind this mode is to support applications like spectacle (see T4458). The calling application passes a file descriptor (created through e.g. a pipe) and the screenshot effect writes the captured image into that fd. The advantage over the existing variant which writes to a file in the /tmp directory is that this is peer-to-peer between the requesting application and KWin. No other application can get to that image. The change also includes setting SIGPIPE to ignore. It showed that when the reading side already cancelled the read prior to KWin writing out the image we get a SIGPIPE which results in application termination, which is not what we want in case of a Wayland compositor. The sigpipe can be ignored as Qt (and libpng) handles that error just fine at runtime. Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D3412
-rw-r--r--effects/screenshot/screenshot.cpp84
-rw-r--r--effects/screenshot/screenshot.h19
-rw-r--r--main_wayland.cpp1
-rw-r--r--main_x11.cpp1
4 files changed, 93 insertions, 12 deletions
diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp
index 80091de..9f7da1c 100644
--- a/effects/screenshot/screenshot.cpp
+++ b/effects/screenshot/screenshot.cpp
@@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwinglplatform.h>
#include <kwinglutils.h>
#include <kwinxrenderutils.h>
+#include <QtConcurrentRun>
+#include <QDataStream>
#include <QtCore/QTemporaryFile>
#include <QtCore/QDir>
#include <QtDBus/QDBusConnection>
@@ -33,6 +35,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KLocalizedString>
#include <KNotification>
+#include <unistd.h>
+
namespace KWin
{
@@ -187,6 +191,20 @@ void ScreenShotEffect::postPaintScreen()
m_windowMode = WindowMode::NoCapture;
} else if (m_windowMode == WindowMode::File) {
sendReplyImage(img);
+ } else if (m_windowMode == WindowMode::FileDescriptor) {
+ QtConcurrent::run(
+ [] (int fd, const QImage &img) {
+ QFile file;
+ if (file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle)) {
+ QDataStream ds(&file);
+ ds << img;
+ file.close();
+ } else {
+ close(fd);
+ }
+ }, m_fd, img);
+ m_windowMode = WindowMode::NoCapture;
+ m_fd = -1;
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if (xImage) {
@@ -306,7 +324,7 @@ QString ScreenShotEffect::interactive(int mask)
setDelayedReply(true);
effects->startInteractiveWindowSelection(
[this] (EffectWindow *w) {
- m_infoFrame.reset();
+ hideInfoMessage();
if (!w) {
m_replyConnection.send(m_replyMessage.createErrorReply(QDBusError::Failed, "Screenshot got cancelled"));
m_windowMode = WindowMode::NoCapture;
@@ -317,18 +335,62 @@ QString ScreenShotEffect::interactive(int mask)
}
});
+ showInfoMessage();
+ return QString();
+}
- if (m_infoFrame.isNull()) {
- m_infoFrame.reset(effects->effectFrame(EffectFrameStyled, false));
- QFont font;
- font.setBold(true);
- m_infoFrame->setFont(font);
- QRect area = effects->clientArea(ScreenArea, effects->activeScreen(), effects->currentDesktop());
- m_infoFrame->setPosition(QPoint(area.x() + area.width() / 2, area.y() + area.height() / 3));
- m_infoFrame->setText(i18n("Select window to screen shot with left click or enter.\nEscape or right click to cancel."));
- effects->addRepaintFull();
+void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask)
+{
+ if (!calledFromDBus()) {
+ return;
}
- return QString();
+ if (!m_scheduledGeometry.isNull() || m_windowMode != WindowMode::NoCapture) {
+ sendErrorReply(QDBusError::Failed, "A screenshot is already been taken");
+ return;
+ }
+ m_fd = dup(fd.fileDescriptor());
+ if (m_fd == -1) {
+ sendErrorReply(QDBusError::Failed, "No valid file descriptor");
+ return;
+ }
+ m_type = (ScreenShotType) mask;
+ m_windowMode = WindowMode::FileDescriptor;
+
+ effects->startInteractiveWindowSelection(
+ [this] (EffectWindow *w) {
+ hideInfoMessage();
+ if (!w) {
+ close(m_fd);
+ m_fd = -1;
+ m_windowMode = WindowMode::NoCapture;
+ return;
+ } else {
+ m_scheduledScreenshot = w;
+ m_scheduledScreenshot->addRepaintFull();
+ }
+ });
+
+ showInfoMessage();
+}
+
+void ScreenShotEffect::showInfoMessage()
+{
+ if (!m_infoFrame.isNull()) {
+ return;
+ }
+ m_infoFrame.reset(effects->effectFrame(EffectFrameStyled, false));
+ QFont font;
+ font.setBold(true);
+ m_infoFrame->setFont(font);
+ QRect area = effects->clientArea(ScreenArea, effects->activeScreen(), effects->currentDesktop());
+ m_infoFrame->setPosition(QPoint(area.x() + area.width() / 2, area.y() + area.height() / 3));
+ m_infoFrame->setText(i18n("Select window to screen shot with left click or enter.\nEscape or right click to cancel."));
+ effects->addRepaintFull();
+}
+
+void ScreenShotEffect::hideInfoMessage()
+{
+ m_infoFrame.reset();
}
QString ScreenShotEffect::screenshotFullscreen(bool captureCursor)
diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h
index 2323b9f..8a54aa5 100644
--- a/effects/screenshot/screenshot.h
+++ b/effects/screenshot/screenshot.h
@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDBusContext>
#include <QDBusConnection>
#include <QDBusMessage>
+#include <QDBusUnixFileDescriptor>
#include <QObject>
#include <QImage>
@@ -64,6 +65,18 @@ public Q_SLOTS:
* @param mask The mask for what to include in the screenshot
**/
Q_SCRIPTABLE QString interactive(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 the @p fd passed to the
+ * method. It is intended to be used with a pipe, so that the invoking side can just
+ * read from the pipe. The image gets written into the fd using a QDataStream.
+ *
+ * @param fd File descriptor into which the screenshot should be saved
+ * @param mask The mask for what to include in the screenshot
+ **/
+ Q_SCRIPTABLE void interactive(QDBusUnixFileDescriptor fd, 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.
@@ -103,6 +116,8 @@ private:
QImage blitScreenshot(const QRect &geometry);
QString saveTempImage(const QImage &img);
void sendReplyImage(const QImage &img);
+ void showInfoMessage();
+ void hideInfoMessage();
EffectWindow *m_scheduledScreenshot;
ScreenShotType m_type;
QRect m_scheduledGeometry;
@@ -115,9 +130,11 @@ private:
enum class WindowMode {
NoCapture,
Xpixmap,
- File
+ File,
+ FileDescriptor
};
WindowMode m_windowMode = WindowMode::NoCapture;
+ int m_fd = -1;
QScopedPointer<EffectFrame> m_infoFrame;
};
diff --git a/main_wayland.cpp b/main_wayland.cpp
index 364f4ca..ebc2c14 100644
--- a/main_wayland.cpp
+++ b/main_wayland.cpp
@@ -451,6 +451,7 @@ int main(int argc, char * argv[])
signal(SIGHUP, SIG_IGN);
signal(SIGABRT, KWin::unsetDumpable);
signal(SIGSEGV, KWin::unsetDumpable);
+ signal(SIGPIPE, SIG_IGN);
// ensure that no thread takes SIGUSR
sigset_t userSignals;
sigemptyset(&userSignals);
diff --git a/main_x11.cpp b/main_x11.cpp
index da3c045..923462f 100644
--- a/main_x11.cpp
+++ b/main_x11.cpp
@@ -396,6 +396,7 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
signal(SIGINT, SIG_IGN);
if (signal(SIGHUP, KWin::sighandler) == SIG_IGN)
signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
// Disable the glib event loop integration, since it seems to be responsible
// for several bug reports about high CPU usage (bug #239963)