Warning, file /plasma/plasma-mobile/quicksettings/screenshot/screenshotutil.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0003 * SPDX-FileCopyrightText: 2018 Bhushan Shah <bshah@kde.org> 0004 * SPDX-FileCopyrightText: 2022 by Devin Lin <devin@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "screenshotutil.h" 0010 0011 #include <fcntl.h> 0012 #include <qplatformdefs.h> 0013 #include <unistd.h> 0014 0015 #include <KLocalizedString> 0016 #include <KNotification> 0017 0018 #include <QGuiApplication> 0019 #include <QImage> 0020 #include <QScreen> 0021 #include <QTimer> 0022 #include <QtConcurrent/QtConcurrent> 0023 0024 constexpr int SCREENSHOT_DELAY = 200; 0025 0026 /* -- Static Helpers --------------------------------------------------------------------------- */ 0027 0028 static QImage allocateImage(const QVariantMap &metadata) 0029 { 0030 bool ok; 0031 0032 const uint width = metadata.value(QStringLiteral("width")).toUInt(&ok); 0033 if (!ok) { 0034 return QImage(); 0035 } 0036 0037 const uint height = metadata.value(QStringLiteral("height")).toUInt(&ok); 0038 if (!ok) { 0039 return QImage(); 0040 } 0041 0042 const uint format = metadata.value(QStringLiteral("format")).toUInt(&ok); 0043 if (!ok) { 0044 return QImage(); 0045 } 0046 0047 return QImage(width, height, QImage::Format(format)); 0048 } 0049 0050 static QImage readImage(int fileDescriptor, const QVariantMap &metadata) 0051 { 0052 QFile file; 0053 if (!file.open(fileDescriptor, QFileDevice::ReadOnly, QFileDevice::AutoCloseHandle)) { 0054 close(fileDescriptor); 0055 return QImage(); 0056 } 0057 0058 QImage result = allocateImage(metadata); 0059 if (result.isNull()) { 0060 return QImage(); 0061 } 0062 0063 QDataStream stream(&file); 0064 stream.readRawData(reinterpret_cast<char *>(result.bits()), result.sizeInBytes()); 0065 0066 return result; 0067 } 0068 0069 ScreenShotUtil::ScreenShotUtil(QObject *parent) 0070 : QObject{parent} 0071 { 0072 m_screenshotInterface = new OrgKdeKWinScreenShot2Interface(QStringLiteral("org.kde.KWin.ScreenShot2"), 0073 QStringLiteral("/org/kde/KWin/ScreenShot2"), 0074 QDBusConnection::sessionBus(), 0075 this); 0076 } 0077 0078 void ScreenShotUtil::takeScreenShot() 0079 { 0080 // wait ~200 ms to wait for rest of animations 0081 QTimer::singleShot(SCREENSHOT_DELAY, [=]() { 0082 int lPipeFds[2]; 0083 if (pipe2(lPipeFds, O_CLOEXEC) != 0) { 0084 qWarning() << "Could not take screenshot"; 0085 return; 0086 } 0087 0088 // We don't have access to the ScreenPool so we'll just take the first screen 0089 QVariantMap options; 0090 options.insert(QStringLiteral("native-resolution"), true); 0091 0092 auto pendingCall = m_screenshotInterface->CaptureScreen(qGuiApp->screens().constFirst()->name(), options, QDBusUnixFileDescriptor(lPipeFds[1])); 0093 close(lPipeFds[1]); 0094 auto pipeFileDescriptor = lPipeFds[0]; 0095 0096 auto watcher = new QDBusPendingCallWatcher(pendingCall, this); 0097 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, pipeFileDescriptor]() { 0098 watcher->deleteLater(); 0099 const QDBusPendingReply<QVariantMap> reply = *watcher; 0100 0101 if (reply.isError()) { 0102 qWarning() << "Screenshot request failed:" << reply.error().message(); 0103 } else { 0104 handleMetaDataReceived(reply, pipeFileDescriptor); 0105 } 0106 }); 0107 }); 0108 } 0109 0110 void ScreenShotUtil::handleMetaDataReceived(const QVariantMap &metadata, int fd) 0111 { 0112 const QString type = metadata.value(QStringLiteral("type")).toString(); 0113 if (type != QLatin1String("raw")) { 0114 qWarning() << "Unsupported metadata type:" << type; 0115 return; 0116 } 0117 0118 auto watcher = new QFutureWatcher<QImage>(this); 0119 connect(watcher, &QFutureWatcher<QImage>::finished, this, [watcher]() { 0120 watcher->deleteLater(); 0121 0122 QString filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); 0123 if (filePath.isEmpty()) { 0124 qWarning() << "Couldn't find a writable location for the screenshot!"; 0125 return; 0126 } 0127 QDir picturesDir(filePath); 0128 if (!picturesDir.mkpath(QStringLiteral("Screenshots"))) { 0129 qWarning() << "Couldn't create folder at" << picturesDir.path() + QStringLiteral("/Screenshots") << "to take screenshot."; 0130 return; 0131 } 0132 filePath += QStringLiteral("/Screenshots/Screenshot_%1.png").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss"))); 0133 const auto m_result = watcher->result(); 0134 if (m_result.isNull() || !m_result.save(filePath)) { 0135 qWarning() << "Screenshot failed"; 0136 } else { 0137 KNotification *notif = new KNotification("captured"); 0138 notif->setComponentName(QStringLiteral("plasma_phone_components")); 0139 notif->setTitle(i18n("New Screenshot")); 0140 notif->setUrls({QUrl::fromLocalFile(filePath)}); 0141 notif->setText(i18n("New screenshot saved to %1", filePath)); 0142 notif->sendEvent(); 0143 } 0144 }); 0145 watcher->setFuture(QtConcurrent::run(readImage, fd, metadata)); 0146 }