diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp index 80091de824..9f7da1cd73 100644 --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -22,6 +22,8 @@ along with this program. If not, see . #include #include #include +#include +#include #include #include #include @@ -33,6 +35,8 @@ along with this program. If not, see . #include #include +#include + 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,20 +335,64 @@ QString ScreenShotEffect::interactive(int mask) } }); - - 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(); - } + showInfoMessage(); return QString(); } +void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask) +{ + if (!calledFromDBus()) { + return; + } + 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) { if (!calledFromDBus()) { diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h index 2323b9f9c6..8a54aa5639 100644 --- a/effects/screenshot/screenshot.h +++ b/effects/screenshot/screenshot.h @@ -25,6 +25,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -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 m_infoFrame; }; diff --git a/main_wayland.cpp b/main_wayland.cpp index 364f4ca725..ebc2c14a64 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 da3c0452e7..923462f84e 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)