File indexing completed on 2024-11-10 04:57:08

0001 /*
0002     SPDX-FileCopyrightText: 2010 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com>
0004     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "screenshotdbusinterface2.h"
0010 #include "core/output.h"
0011 #include "effect/effecthandler.h"
0012 #include "screenshot2adaptor.h"
0013 #include "screenshotlogging.h"
0014 #include "utils/filedescriptor.h"
0015 #include "utils/serviceutils.h"
0016 
0017 #include <KLocalizedString>
0018 
0019 #include <QDBusConnection>
0020 #include <QDBusConnectionInterface>
0021 #include <QThreadPool>
0022 
0023 #include <errno.h>
0024 #include <fcntl.h>
0025 #include <poll.h>
0026 #include <string.h>
0027 #include <unistd.h>
0028 
0029 namespace KWin
0030 {
0031 
0032 class ScreenShotWriter2 : public QRunnable
0033 {
0034 public:
0035     ScreenShotWriter2(FileDescriptor &&fileDescriptor, const QImage &image)
0036         : m_fileDescriptor(std::move(fileDescriptor))
0037         , m_image(image)
0038     {
0039     }
0040 
0041     void run() override
0042     {
0043         const int flags = fcntl(m_fileDescriptor.get(), F_GETFL, 0);
0044         if (flags == -1) {
0045             qCWarning(KWIN_SCREENSHOT) << "failed to get screenshot fd flags:" << strerror(errno);
0046             return;
0047         }
0048         if (!(flags & O_NONBLOCK)) {
0049             if (fcntl(m_fileDescriptor.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
0050                 qCWarning(KWIN_SCREENSHOT) << "failed to make screenshot fd non blocking:" << strerror(errno);
0051                 return;
0052             }
0053         }
0054 
0055         QFile file;
0056         if (!file.open(m_fileDescriptor.get(), QIODevice::WriteOnly)) {
0057             qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO << "failed to open pipe:" << file.errorString();
0058             return;
0059         }
0060 
0061         const QByteArrayView buffer(m_image.constBits(), m_image.sizeInBytes());
0062         qint64 remainingSize = buffer.size();
0063 
0064         pollfd pfds[1];
0065         pfds[0].fd = m_fileDescriptor.get();
0066         pfds[0].events = POLLOUT;
0067 
0068         while (true) {
0069             const int ready = poll(pfds, 1, 60000);
0070             if (ready < 0) {
0071                 if (errno != EINTR) {
0072                     qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO << "poll() failed:" << strerror(errno);
0073                     return;
0074                 }
0075             } else if (ready == 0) {
0076                 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO << "timed out writing to pipe";
0077                 return;
0078             } else if (!(pfds[0].revents & POLLOUT)) {
0079                 qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO << "pipe is broken";
0080                 return;
0081             } else {
0082                 const char *chunk = buffer.constData() + (buffer.size() - remainingSize);
0083                 const qint64 writtenCount = file.write(chunk, remainingSize);
0084 
0085                 if (writtenCount < 0) {
0086                     qCWarning(KWIN_SCREENSHOT) << Q_FUNC_INFO << "write() failed:" << file.errorString();
0087                     return;
0088                 }
0089 
0090                 remainingSize -= writtenCount;
0091                 if (writtenCount == 0 || remainingSize == 0) {
0092                     return;
0093                 }
0094             }
0095         }
0096     }
0097 
0098 protected:
0099     FileDescriptor m_fileDescriptor;
0100     QImage m_image;
0101 };
0102 
0103 static ScreenShotFlags screenShotFlagsFromOptions(const QVariantMap &options)
0104 {
0105     ScreenShotFlags flags = ScreenShotFlags();
0106 
0107     const QVariant includeDecoration = options.value(QStringLiteral("include-decoration"));
0108     if (includeDecoration.toBool()) {
0109         flags |= ScreenShotIncludeDecoration;
0110     }
0111 
0112     const QVariant includeShadow = options.value(QStringLiteral("include-shadow"), true);
0113     if (includeShadow.toBool()) {
0114         flags |= ScreenShotIncludeShadow;
0115     }
0116 
0117     const QVariant includeCursor = options.value(QStringLiteral("include-cursor"));
0118     if (includeCursor.toBool()) {
0119         flags |= ScreenShotIncludeCursor;
0120     }
0121 
0122     const QVariant nativeResolution = options.value(QStringLiteral("native-resolution"));
0123     if (nativeResolution.toBool()) {
0124         flags |= ScreenShotNativeResolution;
0125     }
0126 
0127     return flags;
0128 }
0129 
0130 static const QString s_dbusServiceName = QStringLiteral("org.kde.KWin.ScreenShot2");
0131 static const QString s_dbusInterface = QStringLiteral("org.kde.KWin.ScreenShot2");
0132 static const QString s_dbusObjectPath = QStringLiteral("/org/kde/KWin/ScreenShot2");
0133 
0134 static const QString s_errorNotAuthorized = QStringLiteral("org.kde.KWin.ScreenShot2.Error.NoAuthorized");
0135 static const QString s_errorNotAuthorizedMessage = QStringLiteral("The process is not authorized to take a screenshot");
0136 static const QString s_errorCancelled = QStringLiteral("org.kde.KWin.ScreenShot2.Error.Cancelled");
0137 static const QString s_errorCancelledMessage = QStringLiteral("Screenshot got cancelled");
0138 static const QString s_errorInvalidWindow = QStringLiteral("org.kde.KWin.ScreenShot2.Error.InvalidWindow");
0139 static const QString s_errorInvalidWindowMessage = QStringLiteral("Invalid window requested");
0140 static const QString s_errorInvalidArea = QStringLiteral("org.kde.KWin.ScreenShot2.Error.InvalidArea");
0141 static const QString s_errorInvalidAreaMessage = QStringLiteral("Invalid area requested");
0142 static const QString s_errorInvalidScreen = QStringLiteral("org.kde.KWin.ScreenShot2.Error.InvalidScreen");
0143 static const QString s_errorInvalidScreenMessage = QStringLiteral("Invalid screen requested");
0144 static const QString s_errorFileDescriptor = QStringLiteral("org.kde.KWin.ScreenShot2.Error.FileDescriptor");
0145 static const QString s_errorFileDescriptorMessage = QStringLiteral("No valid file descriptor");
0146 
0147 class ScreenShotSource2 : public QObject
0148 {
0149     Q_OBJECT
0150 
0151 public:
0152     explicit ScreenShotSource2(const QFuture<QImage> &future);
0153 
0154     bool isCancelled() const;
0155     bool isCompleted() const;
0156     void marshal(ScreenShotSinkPipe2 *sink);
0157 
0158     virtual QVariantMap attributes() const;
0159 
0160 Q_SIGNALS:
0161     void cancelled();
0162     void completed();
0163 
0164 private:
0165     QFuture<QImage> m_future;
0166     QFutureWatcher<QImage> *m_watcher;
0167 };
0168 
0169 class ScreenShotSourceScreen2 : public ScreenShotSource2
0170 {
0171     Q_OBJECT
0172 
0173 public:
0174     ScreenShotSourceScreen2(ScreenShotEffect *effect, Output *screen, ScreenShotFlags flags);
0175 
0176     QVariantMap attributes() const override;
0177 
0178 private:
0179     QString m_name;
0180 };
0181 
0182 class ScreenShotSourceArea2 : public ScreenShotSource2
0183 {
0184     Q_OBJECT
0185 
0186 public:
0187     ScreenShotSourceArea2(ScreenShotEffect *effect, const QRect &area, ScreenShotFlags flags);
0188 };
0189 
0190 class ScreenShotSourceWindow2 : public ScreenShotSource2
0191 {
0192     Q_OBJECT
0193 
0194 public:
0195     ScreenShotSourceWindow2(ScreenShotEffect *effect, EffectWindow *window, ScreenShotFlags flags);
0196 
0197     QVariantMap attributes() const override;
0198 
0199 private:
0200     QUuid m_internalId;
0201 };
0202 
0203 class ScreenShotSinkPipe2 : public QObject
0204 {
0205     Q_OBJECT
0206 
0207 public:
0208     ScreenShotSinkPipe2(int fileDescriptor, QDBusMessage replyMessage);
0209 
0210     void cancel();
0211     void flush(const QImage &image, const QVariantMap &attributes);
0212 
0213 private:
0214     QDBusMessage m_replyMessage;
0215     FileDescriptor m_fileDescriptor;
0216 };
0217 
0218 ScreenShotSource2::ScreenShotSource2(const QFuture<QImage> &future)
0219     : m_future(future)
0220 {
0221     m_watcher = new QFutureWatcher<QImage>(this);
0222     connect(m_watcher, &QFutureWatcher<QImage>::finished, this, &ScreenShotSource2::completed);
0223     connect(m_watcher, &QFutureWatcher<QImage>::canceled, this, &ScreenShotSource2::cancelled);
0224     m_watcher->setFuture(m_future);
0225 }
0226 
0227 bool ScreenShotSource2::isCancelled() const
0228 {
0229     return m_future.isCanceled();
0230 }
0231 
0232 bool ScreenShotSource2::isCompleted() const
0233 {
0234     return m_future.isFinished();
0235 }
0236 
0237 QVariantMap ScreenShotSource2::attributes() const
0238 {
0239     return QVariantMap();
0240 }
0241 
0242 void ScreenShotSource2::marshal(ScreenShotSinkPipe2 *sink)
0243 {
0244     sink->flush(m_future.result(), attributes());
0245 }
0246 
0247 ScreenShotSourceScreen2::ScreenShotSourceScreen2(ScreenShotEffect *effect,
0248                                                  Output *screen,
0249                                                  ScreenShotFlags flags)
0250     : ScreenShotSource2(effect->scheduleScreenShot(screen, flags))
0251     , m_name(screen->name())
0252 {
0253 }
0254 
0255 QVariantMap ScreenShotSourceScreen2::attributes() const
0256 {
0257     return QVariantMap{
0258         {QStringLiteral("screen"), m_name},
0259     };
0260 }
0261 
0262 ScreenShotSourceArea2::ScreenShotSourceArea2(ScreenShotEffect *effect,
0263                                              const QRect &area,
0264                                              ScreenShotFlags flags)
0265     : ScreenShotSource2(effect->scheduleScreenShot(area, flags))
0266 {
0267 }
0268 
0269 ScreenShotSourceWindow2::ScreenShotSourceWindow2(ScreenShotEffect *effect,
0270                                                  EffectWindow *window,
0271                                                  ScreenShotFlags flags)
0272     : ScreenShotSource2(effect->scheduleScreenShot(window, flags))
0273     , m_internalId(window->internalId())
0274 {
0275 }
0276 
0277 QVariantMap ScreenShotSourceWindow2::attributes() const
0278 {
0279     return QVariantMap{
0280         {QStringLiteral("windowId"), m_internalId.toString()},
0281     };
0282 }
0283 
0284 ScreenShotSinkPipe2::ScreenShotSinkPipe2(int fileDescriptor, QDBusMessage replyMessage)
0285     : m_replyMessage(replyMessage)
0286     , m_fileDescriptor(fileDescriptor)
0287 {
0288 }
0289 
0290 void ScreenShotSinkPipe2::cancel()
0291 {
0292     QDBusConnection::sessionBus().send(m_replyMessage.createErrorReply(s_errorCancelled,
0293                                                                        s_errorCancelledMessage));
0294 }
0295 
0296 void ScreenShotSinkPipe2::flush(const QImage &image, const QVariantMap &attributes)
0297 {
0298     if (!m_fileDescriptor.isValid()) {
0299         return;
0300     }
0301 
0302     // Note that the type of the data stored in the vardict matters. Be careful.
0303     QVariantMap results = attributes;
0304     results.insert(QStringLiteral("type"), QStringLiteral("raw"));
0305     results.insert(QStringLiteral("format"), quint32(image.format()));
0306     results.insert(QStringLiteral("width"), quint32(image.width()));
0307     results.insert(QStringLiteral("height"), quint32(image.height()));
0308     results.insert(QStringLiteral("stride"), quint32(image.bytesPerLine()));
0309     results.insert(QStringLiteral("scale"), double(image.devicePixelRatio()));
0310     QDBusConnection::sessionBus().send(m_replyMessage.createReply(results));
0311 
0312     auto writer = new ScreenShotWriter2(std::move(m_fileDescriptor), image);
0313     writer->setAutoDelete(true);
0314     QThreadPool::globalInstance()->start(writer);
0315 }
0316 
0317 ScreenShotDBusInterface2::ScreenShotDBusInterface2(ScreenShotEffect *effect)
0318     : QObject(effect)
0319     , m_effect(effect)
0320 {
0321     new ScreenShot2Adaptor(this);
0322 
0323     QDBusConnection::sessionBus().registerObject(s_dbusObjectPath, this);
0324     QDBusConnection::sessionBus().registerService(s_dbusServiceName);
0325 }
0326 
0327 ScreenShotDBusInterface2::~ScreenShotDBusInterface2()
0328 {
0329     QDBusConnection::sessionBus().unregisterService(s_dbusServiceName);
0330     QDBusConnection::sessionBus().unregisterObject(s_dbusObjectPath);
0331 }
0332 
0333 int ScreenShotDBusInterface2::version() const
0334 {
0335     return 4;
0336 }
0337 
0338 bool ScreenShotDBusInterface2::checkPermissions() const
0339 {
0340     if (!calledFromDBus()) {
0341         return false;
0342     }
0343 
0344     static bool permissionCheckDisabled = qEnvironmentVariableIntValue("KWIN_SCREENSHOT_NO_PERMISSION_CHECKS") == 1;
0345     if (permissionCheckDisabled) {
0346         return true;
0347     }
0348 
0349     const QDBusReply<uint> reply = connection().interface()->servicePid(message().service());
0350     if (reply.isValid()) {
0351         const uint pid = reply.value();
0352         const auto interfaces = KWin::fetchRestrictedDBusInterfacesFromPid(pid);
0353         if (!interfaces.contains(s_dbusInterface)) {
0354             sendErrorReply(s_errorNotAuthorized, s_errorNotAuthorizedMessage);
0355             return false;
0356         }
0357     } else {
0358         return false;
0359     }
0360 
0361     return true;
0362 }
0363 
0364 QVariantMap ScreenShotDBusInterface2::CaptureActiveWindow(const QVariantMap &options,
0365                                                           QDBusUnixFileDescriptor pipe)
0366 {
0367     if (!checkPermissions()) {
0368         return QVariantMap();
0369     }
0370 
0371     EffectWindow *window = effects->activeWindow();
0372     if (!window) {
0373         sendErrorReply(s_errorInvalidWindow, s_errorInvalidWindowMessage);
0374         return QVariantMap();
0375     }
0376 
0377     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0378     if (fileDescriptor == -1) {
0379         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0380         return QVariantMap();
0381     }
0382 
0383     takeScreenShot(window, screenShotFlagsFromOptions(options),
0384                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0385 
0386     setDelayedReply(true);
0387     return QVariantMap();
0388 }
0389 
0390 QVariantMap ScreenShotDBusInterface2::CaptureWindow(const QString &handle,
0391                                                     const QVariantMap &options,
0392                                                     QDBusUnixFileDescriptor pipe)
0393 {
0394     if (!checkPermissions()) {
0395         return QVariantMap();
0396     }
0397 
0398     EffectWindow *window = effects->findWindow(QUuid(handle));
0399     if (!window) {
0400         bool ok;
0401         const int winId = handle.toInt(&ok);
0402         if (ok) {
0403             window = effects->findWindow(winId);
0404         } else {
0405             qCWarning(KWIN_SCREENSHOT) << "Invalid handle:" << handle;
0406         }
0407     }
0408     if (!window) {
0409         sendErrorReply(s_errorInvalidWindow, s_errorInvalidWindowMessage);
0410         return QVariantMap();
0411     }
0412 
0413     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0414     if (fileDescriptor == -1) {
0415         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0416         return QVariantMap();
0417     }
0418 
0419     takeScreenShot(window, screenShotFlagsFromOptions(options),
0420                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0421 
0422     setDelayedReply(true);
0423     return QVariantMap();
0424 }
0425 
0426 QVariantMap ScreenShotDBusInterface2::CaptureArea(int x, int y, int width, int height,
0427                                                   const QVariantMap &options,
0428                                                   QDBusUnixFileDescriptor pipe)
0429 {
0430     if (!checkPermissions()) {
0431         return QVariantMap();
0432     }
0433 
0434     const QRect area(x, y, width, height);
0435     if (area.isEmpty()) {
0436         sendErrorReply(s_errorInvalidArea, s_errorInvalidAreaMessage);
0437         return QVariantMap();
0438     }
0439 
0440     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0441     if (fileDescriptor == -1) {
0442         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0443         return QVariantMap();
0444     }
0445 
0446     takeScreenShot(area, screenShotFlagsFromOptions(options),
0447                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0448 
0449     setDelayedReply(true);
0450     return QVariantMap();
0451 }
0452 
0453 QVariantMap ScreenShotDBusInterface2::CaptureScreen(const QString &name,
0454                                                     const QVariantMap &options,
0455                                                     QDBusUnixFileDescriptor pipe)
0456 {
0457     if (!checkPermissions()) {
0458         return QVariantMap();
0459     }
0460 
0461     Output *screen = effects->findScreen(name);
0462     if (!screen) {
0463         sendErrorReply(s_errorInvalidScreen, s_errorInvalidScreenMessage);
0464         return QVariantMap();
0465     }
0466 
0467     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0468     if (fileDescriptor == -1) {
0469         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0470         return QVariantMap();
0471     }
0472 
0473     takeScreenShot(screen, screenShotFlagsFromOptions(options),
0474                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0475 
0476     setDelayedReply(true);
0477     return QVariantMap();
0478 }
0479 
0480 QVariantMap ScreenShotDBusInterface2::CaptureActiveScreen(const QVariantMap &options,
0481                                                           QDBusUnixFileDescriptor pipe)
0482 {
0483     if (!checkPermissions()) {
0484         return QVariantMap();
0485     }
0486 
0487     Output *screen = effects->activeScreen();
0488     if (!screen) {
0489         sendErrorReply(s_errorInvalidScreen, s_errorInvalidScreenMessage);
0490         return QVariantMap();
0491     }
0492 
0493     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0494     if (fileDescriptor == -1) {
0495         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0496         return QVariantMap();
0497     }
0498 
0499     takeScreenShot(screen, screenShotFlagsFromOptions(options),
0500                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0501 
0502     setDelayedReply(true);
0503     return QVariantMap();
0504 }
0505 
0506 QVariantMap ScreenShotDBusInterface2::CaptureInteractive(uint kind,
0507                                                          const QVariantMap &options,
0508                                                          QDBusUnixFileDescriptor pipe)
0509 {
0510     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0511     if (fileDescriptor == -1) {
0512         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0513         return QVariantMap();
0514     }
0515 
0516     const QDBusMessage replyMessage = message();
0517 
0518     if (kind == 0) {
0519         effects->startInteractiveWindowSelection([=, this](EffectWindow *window) {
0520             effects->hideOnScreenMessage(EffectsHandler::OnScreenMessageHideFlag::SkipsCloseAnimation);
0521 
0522             if (!window) {
0523                 close(fileDescriptor);
0524 
0525                 QDBusConnection bus = QDBusConnection::sessionBus();
0526                 bus.send(replyMessage.createErrorReply(s_errorCancelled, s_errorCancelledMessage));
0527             } else {
0528                 takeScreenShot(window, screenShotFlagsFromOptions(options),
0529                                new ScreenShotSinkPipe2(fileDescriptor, replyMessage));
0530             }
0531         });
0532         effects->showOnScreenMessage(i18n("Select window to screen shot with left click or enter.\n"
0533                                           "Escape or right click to cancel."),
0534                                      QStringLiteral("spectacle"));
0535     } else {
0536         effects->startInteractivePositionSelection([=, this](const QPointF &point) {
0537             effects->hideOnScreenMessage(EffectsHandler::OnScreenMessageHideFlag::SkipsCloseAnimation);
0538 
0539             if (point == QPoint(-1, -1)) {
0540                 close(fileDescriptor);
0541 
0542                 QDBusConnection bus = QDBusConnection::sessionBus();
0543                 bus.send(replyMessage.createErrorReply(s_errorCancelled, s_errorCancelledMessage));
0544             } else {
0545                 Output *screen = effects->screenAt(point.toPoint());
0546                 takeScreenShot(screen, screenShotFlagsFromOptions(options),
0547                                new ScreenShotSinkPipe2(fileDescriptor, replyMessage));
0548             }
0549         });
0550         effects->showOnScreenMessage(i18n("Create screen shot with left click or enter.\n"
0551                                           "Escape or right click to cancel."),
0552                                      QStringLiteral("spectacle"));
0553     }
0554 
0555     setDelayedReply(true);
0556     return QVariantMap();
0557 }
0558 
0559 QVariantMap ScreenShotDBusInterface2::CaptureWorkspace(const QVariantMap &options, QDBusUnixFileDescriptor pipe)
0560 {
0561     if (!checkPermissions()) {
0562         return QVariantMap();
0563     }
0564 
0565     const int fileDescriptor = fcntl(pipe.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
0566     if (fileDescriptor == -1) {
0567         sendErrorReply(s_errorFileDescriptor, s_errorFileDescriptorMessage);
0568         return QVariantMap();
0569     }
0570 
0571     takeScreenShot(effects->virtualScreenGeometry(), screenShotFlagsFromOptions(options),
0572                    new ScreenShotSinkPipe2(fileDescriptor, message()));
0573 
0574     setDelayedReply(true);
0575     return QVariantMap();
0576 }
0577 
0578 void ScreenShotDBusInterface2::bind(ScreenShotSinkPipe2 *sink, ScreenShotSource2 *source)
0579 {
0580     connect(source, &ScreenShotSource2::cancelled, sink, [sink, source]() {
0581         sink->cancel();
0582 
0583         sink->deleteLater();
0584         source->deleteLater();
0585     });
0586 
0587     connect(source, &ScreenShotSource2::completed, sink, [sink, source]() {
0588         source->marshal(sink);
0589 
0590         sink->deleteLater();
0591         source->deleteLater();
0592     });
0593 }
0594 
0595 void ScreenShotDBusInterface2::takeScreenShot(Output *screen, ScreenShotFlags flags,
0596                                               ScreenShotSinkPipe2 *sink)
0597 {
0598     bind(sink, new ScreenShotSourceScreen2(m_effect, screen, flags));
0599 }
0600 
0601 void ScreenShotDBusInterface2::takeScreenShot(const QRect &area, ScreenShotFlags flags,
0602                                               ScreenShotSinkPipe2 *sink)
0603 {
0604     bind(sink, new ScreenShotSourceArea2(m_effect, area, flags));
0605 }
0606 
0607 void ScreenShotDBusInterface2::takeScreenShot(EffectWindow *window, ScreenShotFlags flags,
0608                                               ScreenShotSinkPipe2 *sink)
0609 {
0610     bind(sink, new ScreenShotSourceWindow2(m_effect, window, flags));
0611 }
0612 
0613 } // namespace KWin
0614 
0615 #include "screenshotdbusinterface2.moc"
0616 
0617 #include "moc_screenshotdbusinterface2.cpp"