diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ find_package( Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core + Concurrent Widgets DBus PrintSupport diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ SpectacleDBusAdapter.cpp PlatformBackends/ImageGrabber.cpp PlatformBackends/DummyImageGrabber.cpp + PlatformBackends/KWinWaylandImageGrabber.cpp Gui/KSMainWindow.cpp Gui/KSWidget.cpp Gui/KSImageWidget.cpp @@ -60,6 +61,7 @@ target_link_libraries( spectacle + Qt5::Concurrent Qt5::DBus Qt5::PrintSupport Qt5::Quick diff --git a/src/PlatformBackends/KWinWaylandImageGrabber.h b/src/PlatformBackends/KWinWaylandImageGrabber.h new file mode 100644 --- /dev/null +++ b/src/PlatformBackends/KWinWaylandImageGrabber.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Martin Graesslin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KWINWAYLANDIMAGEGRABBER_H +#define KWINWAYLANDIMAGEGRABBER_H + +#include "ImageGrabber.h" + +class KWinWaylandImageGrabber : public ImageGrabber +{ + Q_OBJECT + + public: + + explicit KWinWaylandImageGrabber(QObject * parent = 0); + virtual ~KWinWaylandImageGrabber(); + + bool onClickGrabSupported() const Q_DECL_OVERRIDE; + + protected: + + void grabFullScreen() Q_DECL_OVERRIDE; + void grabCurrentScreen() Q_DECL_OVERRIDE; + void grabActiveWindow() Q_DECL_OVERRIDE; + void grabRectangularRegion() Q_DECL_OVERRIDE; + void grabWindowUnderCursor() Q_DECL_OVERRIDE; + void grabTransientWithParent() Q_DECL_OVERRIDE; + QPixmap blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) Q_DECL_OVERRIDE; +}; + +#endif diff --git a/src/PlatformBackends/KWinWaylandImageGrabber.cpp b/src/PlatformBackends/KWinWaylandImageGrabber.cpp new file mode 100644 --- /dev/null +++ b/src/PlatformBackends/KWinWaylandImageGrabber.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2016 Martin Graesslin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "KWinWaylandImageGrabber.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +KWinWaylandImageGrabber::KWinWaylandImageGrabber(QObject *parent) : + ImageGrabber(parent) +{ +} + +KWinWaylandImageGrabber::~KWinWaylandImageGrabber() = default; + +bool KWinWaylandImageGrabber::onClickGrabSupported() const +{ + return true; +} + +void KWinWaylandImageGrabber::grabFullScreen() +{ + // unsupported + emit pixmapChanged(QPixmap()); +} + +void KWinWaylandImageGrabber::grabCurrentScreen() +{ + // unsupported + emit pixmapChanged(QPixmap()); +} + +void KWinWaylandImageGrabber::grabActiveWindow() +{ + // unsupported + emit pixmapChanged(QPixmap()); +} + +void KWinWaylandImageGrabber::grabRectangularRegion() +{ + // unsupported + emit pixmapChanged(QPixmap()); +} + +static int readData(int fd, QByteArray &data) +{ + // implementation based on QtWayland file qwaylanddataoffer.cpp + char buf[4096]; + int retryCount = 0; + int n; + while (true) { + n = QT_READ(fd, buf, sizeof buf); + // give user 30 sec to click a window, afterwards considered as error + if (n == -1 && (errno == EAGAIN) && ++retryCount < 30000) { + usleep(1000); + } else { + break; + } + } + if (n > 0) { + data.append(buf, n); + n = readData(fd, data); + } + return n; +} + +void KWinWaylandImageGrabber::grabWindowUnderCursor() +{ + QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot")); + + int mask = 0; + if (mCaptureDecorations) { + mask = 1; + } + if (mCapturePointer) { + mask |= 1 << 1; + } + + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { + emit imageGrabFailed(); + return; + } + const int pipeFd = pipeFds[0]; + + auto call = interface.asyncCall(QStringLiteral("interactive"), QVariant::fromValue(QDBusUnixFileDescriptor(pipeFds[1])), mask); + + auto readImage = [pipeFd] () -> QImage { + QByteArray content; + if (readData(pipeFd, content) != 0) { + close(pipeFd); + return QImage(); + } + close(pipeFd); + QDataStream ds(content); + QImage image; + ds >> image; + return image; + }; + QFutureWatcher *watcher = new QFutureWatcher(this); + QObject::connect(watcher, &QFutureWatcher::finished, this, + [watcher, this] { + watcher->deleteLater(); + const QImage img = watcher->result(); + emit pixmapChanged(QPixmap::fromImage(img)); + } + ); + watcher->setFuture(QtConcurrent::run(readImage)); + + close(pipeFds[1]); +} + +void KWinWaylandImageGrabber::grabTransientWithParent() +{ + // unsupported, perform grab window under cursor + grabWindowUnderCursor(); +} + +QPixmap KWinWaylandImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) +{ + Q_UNUSED(x) + Q_UNUSED(y) + Q_UNUSED(width) + Q_UNUSED(height) + return pixmap; +} diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -40,6 +40,7 @@ #ifdef XCB_FOUND #include "PlatformBackends/X11ImageGrabber.h" #endif +#include "PlatformBackends/KWinWaylandImageGrabber.h" SpectacleCore::SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, qint64 delayMsec, bool notifyOnGrab, QObject *parent) : @@ -66,6 +67,9 @@ mImageGrabber = new X11ImageGrabber; } #endif + if (!mImageGrabber && KWindowSystem::isPlatformWayland()) { + mImageGrabber = new KWinWaylandImageGrabber; + } if (!mImageGrabber) { mImageGrabber = new DummyImageGrabber;