diff --git a/src/PlatformBackends/KWinWaylandImageGrabber.h b/src/PlatformBackends/KWinWaylandImageGrabber.h --- a/src/PlatformBackends/KWinWaylandImageGrabber.h +++ b/src/PlatformBackends/KWinWaylandImageGrabber.h @@ -41,6 +41,19 @@ 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; + + private: + + void startReadImage(int readPipe); + enum class Mode { + Window, + CurrentScreen, + FullScreen + }; + template + void callDBus(Mode mode, int writeFd, T argument); + template + void grab(Mode mode, T argument); }; #endif diff --git a/src/PlatformBackends/KWinWaylandImageGrabber.cpp b/src/PlatformBackends/KWinWaylandImageGrabber.cpp --- a/src/PlatformBackends/KWinWaylandImageGrabber.cpp +++ b/src/PlatformBackends/KWinWaylandImageGrabber.cpp @@ -29,6 +29,42 @@ #include +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; +} + +static QImage readImage(int pipeFd) +{ + QByteArray content; + if (readData(pipeFd, content) != 0) { + close(pipeFd); + return QImage(); + } + close(pipeFd); + QDataStream ds(content); + QImage image; + ds >> image; + return image; +}; + KWinWaylandImageGrabber::KWinWaylandImageGrabber(QObject *parent) : ImageGrabber(parent) { @@ -43,14 +79,12 @@ void KWinWaylandImageGrabber::grabFullScreen() { - // unsupported - emit pixmapChanged(QPixmap()); + grab(Mode::FullScreen, mCapturePointer); } void KWinWaylandImageGrabber::grabCurrentScreen() { - // unsupported - emit pixmapChanged(QPixmap()); + grab(Mode::CurrentScreen, mCapturePointer); } void KWinWaylandImageGrabber::grabActiveWindow() @@ -65,85 +99,71 @@ 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; } + grab(Mode::Window, mask); +} - int pipeFds[2]; - if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { - emit imageGrabFailed(); - return; - } - const int pipeFd = pipeFds[0]; +void KWinWaylandImageGrabber::grabTransientWithParent() +{ + // unsupported, perform grab window under cursor + grabWindowUnderCursor(); +} - auto call = interface.asyncCall(QStringLiteral("interactive"), QVariant::fromValue(QDBusUnixFileDescriptor(pipeFds[1])), mask); +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; +} - 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; - }; +void KWinWaylandImageGrabber::startReadImage(int readPipe) +{ 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]); + watcher->setFuture(QtConcurrent::run(readImage, readPipe)); } -void KWinWaylandImageGrabber::grabTransientWithParent() +template +void KWinWaylandImageGrabber::callDBus(Mode mode, int writeFd, T argument) { - // unsupported, perform grab window under cursor - grabWindowUnderCursor(); + QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot")); + static const QMap s_hash = { + {Mode::Window, QStringLiteral("interactive")}, + {Mode::CurrentScreen, QStringLiteral("screenshotScreen")}, + {Mode::FullScreen, QStringLiteral("screenshotFullscreen")} + }; + auto it = s_hash.find(mode); + Q_ASSERT(it != s_hash.end()); + interface.asyncCall(it.value(), QVariant::fromValue(QDBusUnixFileDescriptor(writeFd)), argument); } -QPixmap KWinWaylandImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) +template +void KWinWaylandImageGrabber::grab(Mode mode, T argument) { - Q_UNUSED(x) - Q_UNUSED(y) - Q_UNUSED(width) - Q_UNUSED(height) - return pixmap; + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { + emit imageGrabFailed(); + return; + } + + callDBus(mode, pipeFds[1], argument); + startReadImage(pipeFds[0]); + + close(pipeFds[1]); }