From 1dd632270bb79d8b698d6ceb39d4f7f766ba1827 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Wed, 16 Mar 2022 23:49:51 -0400 Subject: [PATCH] quicksettings/screenshot: Move C++ out of ShellUtil into package --- libmobileshell/CMakeLists.txt | 3 +- libmobileshell/shellutil.cpp | 78 +--------- libmobileshell/shellutil.h | 3 - quicksettings/CMakeLists.txt | 2 +- quicksettings/screenshot/CMakeLists.txt | 38 +++++ .../dbus/org.kde.KWin.ScreenShot2.xml | 0 .../{ => package}/contents/ui/main.qml | 3 +- .../screenshot/{ => package}/metadata.desktop | 0 quicksettings/screenshot/qmldir | 6 + quicksettings/screenshot/screenshotplugin.cpp | 23 +++ quicksettings/screenshot/screenshotplugin.h | 21 +++ quicksettings/screenshot/screenshotutil.cpp | 146 ++++++++++++++++++ quicksettings/screenshot/screenshotutil.h | 26 ++++ 13 files changed, 266 insertions(+), 83 deletions(-) create mode 100644 quicksettings/screenshot/CMakeLists.txt rename {libmobileshell => quicksettings/screenshot}/dbus/org.kde.KWin.ScreenShot2.xml (100%) rename quicksettings/screenshot/{ => package}/contents/ui/main.qml (90%) rename quicksettings/screenshot/{ => package}/metadata.desktop (100%) create mode 100644 quicksettings/screenshot/qmldir create mode 100644 quicksettings/screenshot/screenshotplugin.cpp create mode 100644 quicksettings/screenshot/screenshotplugin.h create mode 100644 quicksettings/screenshot/screenshotutil.cpp create mode 100644 quicksettings/screenshot/screenshotutil.h diff --git a/libmobileshell/CMakeLists.txt b/libmobileshell/CMakeLists.txt index 6794a144..0c61d732 100644 --- a/libmobileshell/CMakeLists.txt +++ b/libmobileshell/CMakeLists.txt @@ -7,8 +7,7 @@ if (BUILD_TESTING) add_subdirectory(autotests) endif() -qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KWin.ScreenShot2.xml - dbus/org.kde.KScreen.xml +qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KScreen.xml ${KWIN_VIRTUALKEYBOARD_INTERFACE}) set(mobileshell_LIB_SRCS diff --git a/libmobileshell/shellutil.cpp b/libmobileshell/shellutil.cpp index 998c0240..1cc63fef 100644 --- a/libmobileshell/shellutil.cpp +++ b/libmobileshell/shellutil.cpp @@ -31,8 +31,6 @@ using namespace MobileShell; -constexpr int SCREENSHOT_DELAY = 200; - /* -- Static Helpers --------------------------------------------------------------------------- */ static QImage allocateImage(const QVariantMap &metadata) @@ -81,10 +79,6 @@ ShellUtil::ShellUtil(QObject *parent) { // setHasConfigurationInterface(true); m_kscreenInterface = new org::kde::KScreen(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/kscreen"), QDBusConnection::sessionBus(), this); - m_screenshotInterface = new OrgKdeKWinScreenShot2Interface(QStringLiteral("org.kde.KWin.ScreenShot2"), - QStringLiteral("/org/kde/KWin/ScreenShot2"), - QDBusConnection::sessionBus(), - this); m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig); m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig); @@ -130,10 +124,12 @@ void ShellUtil::toggleTorch() m_running = !m_running; Q_EMIT torchChanged(m_running); } + bool ShellUtil::torchEnabled() const { return m_running; } + bool ShellUtil::autoRotate() { QDBusPendingReply reply = m_kscreenInterface->getAutoRotate(); @@ -157,76 +153,6 @@ void ShellUtil::setAutoRotate(bool value) } } -void ShellUtil::handleMetaDataReceived(const QVariantMap &metadata, int fd) -{ - const QString type = metadata.value(QStringLiteral("type")).toString(); - if (type != QLatin1String("raw")) { - qWarning() << "Unsupported metadata type:" << type; - return; - } - - auto watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [watcher]() { - watcher->deleteLater(); - - QString filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); - if (filePath.isEmpty()) { - qWarning() << "Couldn't find a writable location for the screenshot!"; - return; - } - QDir picturesDir(filePath); - if (!picturesDir.mkpath(QStringLiteral("Screenshots"))) { - qWarning() << "Couldn't create folder at" << picturesDir.path() + QStringLiteral("/Screenshots") << "to take screenshot."; - return; - } - filePath += QStringLiteral("/Screenshots/Screenshot_%1.png").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss"))); - const auto m_result = watcher->result(); - if (m_result.isNull() || !m_result.save(filePath)) { - qWarning() << "Screenshot failed"; - } else { - KNotification *notif = new KNotification("captured"); - notif->setComponentName(QStringLiteral("plasma_phone_components")); - notif->setTitle(i18n("New Screenshot")); - notif->setUrls({QUrl::fromLocalFile(filePath)}); - notif->setText(i18n("New screenshot saved to %1", filePath)); - notif->sendEvent(); - } - }); - watcher->setFuture(QtConcurrent::run(readImage, fd, metadata)); -} - -void ShellUtil::takeScreenshot() -{ - // wait ~200 ms to wait for rest of animations - QTimer::singleShot(SCREENSHOT_DELAY, [=]() { - int lPipeFds[2]; - if (pipe2(lPipeFds, O_CLOEXEC) != 0) { - qWarning() << "Could not take screenshot"; - return; - } - - // We don't have access to the ScreenPool so we'll just take the first screen - QVariantMap options; - options.insert(QStringLiteral("native-resolution"), true); - - auto pendingCall = m_screenshotInterface->CaptureScreen(qGuiApp->screens().constFirst()->name(), options, QDBusUnixFileDescriptor(lPipeFds[1])); - close(lPipeFds[1]); - auto pipeFileDescriptor = lPipeFds[0]; - - auto watcher = new QDBusPendingCallWatcher(pendingCall, this); - connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, pipeFileDescriptor]() { - watcher->deleteLater(); - const QDBusPendingReply reply = *watcher; - - if (reply.isError()) { - qWarning() << "Screenshot request failed:" << reply.error().message(); - } else { - handleMetaDataReceived(reply, pipeFileDescriptor); - } - }); - }); -} - bool ShellUtil::isSystem24HourFormat() { KConfigGroup localeSettings = KConfigGroup(m_localeConfig, "Locale"); diff --git a/libmobileshell/shellutil.h b/libmobileshell/shellutil.h index 5da691be..40a4357c 100644 --- a/libmobileshell/shellutil.h +++ b/libmobileshell/shellutil.h @@ -36,7 +36,6 @@ public Q_SLOTS: void executeCommand(const QString &command); void launchApp(const QString &app); void toggleTorch(); - void takeScreenshot(); bool autoRotate(); void setAutoRotate(bool value); @@ -51,14 +50,12 @@ Q_SIGNALS: void isSystem24HourFormatChanged(); private: - void handleMetaDataReceived(const QVariantMap &metadata, int fd); bool m_running = false; KConfigWatcher::Ptr m_localeConfigWatcher; KSharedConfig::Ptr m_localeConfig; org::kde::KScreen *m_kscreenInterface; - OrgKdeKWinScreenShot2Interface *m_screenshotInterface; }; } // namespace MobileShell diff --git a/quicksettings/CMakeLists.txt b/quicksettings/CMakeLists.txt index 3d32147f..4785dbab 100644 --- a/quicksettings/CMakeLists.txt +++ b/quicksettings/CMakeLists.txt @@ -11,8 +11,8 @@ plasma_install_package(keyboardtoggle org.kde.plasma.keyboardtoggle quicksetting plasma_install_package(location org.kde.plasma.location quicksettings) plasma_install_package(mobiledata org.kde.plasma.mobiledata quicksettings) plasma_install_package(screenrotation org.kde.plasma.screenrotation quicksettings) -plasma_install_package(screenshot org.kde.plasma.screenshot quicksettings) plasma_install_package(settingsapp org.kde.plasma.settingsapp quicksettings) plasma_install_package(wifi org.kde.plasma.wifi quicksettings) add_subdirectory(nightcolor) add_subdirectory(powermenu) +add_subdirectory(screenshot) diff --git a/quicksettings/screenshot/CMakeLists.txt b/quicksettings/screenshot/CMakeLists.txt new file mode 100644 index 00000000..ef2849b1 --- /dev/null +++ b/quicksettings/screenshot/CMakeLists.txt @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2022 Devin Lin +# SPDX-License-Identifier: GPL-2.0-or-later + +qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KWin.ScreenShot2.xml) + +set(screenshotplugin_SRCS + screenshotplugin.cpp + screenshotutil.cpp + ${DBUS_SRCS} +) + +add_library(screenshotplugin ${screenshotplugin_SRCS}) + +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS + Declarative +) + +target_link_libraries(screenshotplugin + PUBLIC + Qt::Core + PRIVATE + Qt::DBus + KF5::CoreAddons + KF5::QuickAddons + KF5::ConfigCore + KF5::ConfigGui + KF5::I18n + KF5::Notifications +) + +set_property(TARGET screenshotplugin PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/org/kde/plasma/quicksetting/screenshot) +file(COPY qmldir DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/org/kde/plasma/quicksetting/screenshot) + +install(TARGETS screenshotplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/quicksetting/screenshot) +install(FILES qmldir ${qml_SRC} DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/quicksetting/screenshot) + +plasma_install_package(package org.kde.plasma.screenshot quicksettings) + diff --git a/libmobileshell/dbus/org.kde.KWin.ScreenShot2.xml b/quicksettings/screenshot/dbus/org.kde.KWin.ScreenShot2.xml similarity index 100% rename from libmobileshell/dbus/org.kde.KWin.ScreenShot2.xml rename to quicksettings/screenshot/dbus/org.kde.KWin.ScreenShot2.xml diff --git a/quicksettings/screenshot/contents/ui/main.qml b/quicksettings/screenshot/package/contents/ui/main.qml similarity index 90% rename from quicksettings/screenshot/contents/ui/main.qml rename to quicksettings/screenshot/package/contents/ui/main.qml index 0ca730ef..3c097b0e 100644 --- a/quicksettings/screenshot/contents/ui/main.qml +++ b/quicksettings/screenshot/package/contents/ui/main.qml @@ -4,6 +4,7 @@ import QtQuick 2.15 import org.kde.plasma.private.mobileshell 1.0 as MobileShell +import org.kde.plasma.quicksetting.screenshot 1.0 MobileShell.QuickSetting { text: i18n("Screenshot") @@ -32,6 +33,6 @@ MobileShell.QuickSetting { Timer { id: timer interval: 500 - onTriggered: MobileShell.ShellUtil.takeScreenshot() + onTriggered: ScreenShotUtil.takeScreenShot() } } diff --git a/quicksettings/screenshot/metadata.desktop b/quicksettings/screenshot/package/metadata.desktop similarity index 100% rename from quicksettings/screenshot/metadata.desktop rename to quicksettings/screenshot/package/metadata.desktop diff --git a/quicksettings/screenshot/qmldir b/quicksettings/screenshot/qmldir new file mode 100644 index 00000000..410d64ee --- /dev/null +++ b/quicksettings/screenshot/qmldir @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2022 Devin Lin +# SPDX-License-Identifier: GPL-2.0-or-later + +module org.kde.plasma.quicksetting.screenshot +plugin screenshotplugin +classname ScreenShotPlugin diff --git a/quicksettings/screenshot/screenshotplugin.cpp b/quicksettings/screenshot/screenshotplugin.cpp new file mode 100644 index 00000000..41321e21 --- /dev/null +++ b/quicksettings/screenshot/screenshotplugin.cpp @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2022 by Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "screenshotplugin.h" + +#include +#include + +#include "screenshotutil.h" + +void ScreenShotPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.quicksetting.screenshot")); + + qmlRegisterSingletonType(uri, 1, 0, "ScreenShotUtil", [](QQmlEngine *, QJSEngine *) { + return new ScreenShotUtil; + }); +} + +//#include "moc_nightcolorplugin.cpp" diff --git a/quicksettings/screenshot/screenshotplugin.h b/quicksettings/screenshot/screenshotplugin.h new file mode 100644 index 00000000..6c82b5fb --- /dev/null +++ b/quicksettings/screenshot/screenshotplugin.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2022 by Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include + +#include +#include + +class ScreenShotPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char *uri) override; +}; diff --git a/quicksettings/screenshot/screenshotutil.cpp b/quicksettings/screenshot/screenshotutil.cpp new file mode 100644 index 00000000..7ff14c24 --- /dev/null +++ b/quicksettings/screenshot/screenshotutil.cpp @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2015 Marco Martin + * SPDX-FileCopyrightText: 2018 Bhushan Shah + * SPDX-FileCopyrightText: 2022 by Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "screenshotutil.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +constexpr int SCREENSHOT_DELAY = 200; + +/* -- Static Helpers --------------------------------------------------------------------------- */ + +static QImage allocateImage(const QVariantMap &metadata) +{ + bool ok; + + const uint width = metadata.value(QStringLiteral("width")).toUInt(&ok); + if (!ok) { + return QImage(); + } + + const uint height = metadata.value(QStringLiteral("height")).toUInt(&ok); + if (!ok) { + return QImage(); + } + + const uint format = metadata.value(QStringLiteral("format")).toUInt(&ok); + if (!ok) { + return QImage(); + } + + return QImage(width, height, QImage::Format(format)); +} + +static QImage readImage(int fileDescriptor, const QVariantMap &metadata) +{ + QFile file; + if (!file.open(fileDescriptor, QFileDevice::ReadOnly, QFileDevice::AutoCloseHandle)) { + close(fileDescriptor); + return QImage(); + } + + QImage result = allocateImage(metadata); + if (result.isNull()) { + return QImage(); + } + + QDataStream stream(&file); + stream.readRawData(reinterpret_cast(result.bits()), result.sizeInBytes()); + + return result; +} + +ScreenShotUtil::ScreenShotUtil(QObject *parent) + : QObject{parent} +{ + m_screenshotInterface = new OrgKdeKWinScreenShot2Interface(QStringLiteral("org.kde.KWin.ScreenShot2"), + QStringLiteral("/org/kde/KWin/ScreenShot2"), + QDBusConnection::sessionBus(), + this); +} + +void ScreenShotUtil::takeScreenShot() +{ + // wait ~200 ms to wait for rest of animations + QTimer::singleShot(SCREENSHOT_DELAY, [=]() { + int lPipeFds[2]; + if (pipe2(lPipeFds, O_CLOEXEC) != 0) { + qWarning() << "Could not take screenshot"; + return; + } + + // We don't have access to the ScreenPool so we'll just take the first screen + QVariantMap options; + options.insert(QStringLiteral("native-resolution"), true); + + auto pendingCall = m_screenshotInterface->CaptureScreen(qGuiApp->screens().constFirst()->name(), options, QDBusUnixFileDescriptor(lPipeFds[1])); + close(lPipeFds[1]); + auto pipeFileDescriptor = lPipeFds[0]; + + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, pipeFileDescriptor]() { + watcher->deleteLater(); + const QDBusPendingReply reply = *watcher; + + if (reply.isError()) { + qWarning() << "Screenshot request failed:" << reply.error().message(); + } else { + handleMetaDataReceived(reply, pipeFileDescriptor); + } + }); + }); +} + +void ScreenShotUtil::handleMetaDataReceived(const QVariantMap &metadata, int fd) +{ + const QString type = metadata.value(QStringLiteral("type")).toString(); + if (type != QLatin1String("raw")) { + qWarning() << "Unsupported metadata type:" << type; + return; + } + + auto watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::finished, this, [watcher]() { + watcher->deleteLater(); + + QString filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); + if (filePath.isEmpty()) { + qWarning() << "Couldn't find a writable location for the screenshot!"; + return; + } + QDir picturesDir(filePath); + if (!picturesDir.mkpath(QStringLiteral("Screenshots"))) { + qWarning() << "Couldn't create folder at" << picturesDir.path() + QStringLiteral("/Screenshots") << "to take screenshot."; + return; + } + filePath += QStringLiteral("/Screenshots/Screenshot_%1.png").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss"))); + const auto m_result = watcher->result(); + if (m_result.isNull() || !m_result.save(filePath)) { + qWarning() << "Screenshot failed"; + } else { + KNotification *notif = new KNotification("captured"); + notif->setComponentName(QStringLiteral("plasma_phone_components")); + notif->setTitle(i18n("New Screenshot")); + notif->setUrls({QUrl::fromLocalFile(filePath)}); + notif->setText(i18n("New screenshot saved to %1", filePath)); + notif->sendEvent(); + } + }); + watcher->setFuture(QtConcurrent::run(readImage, fd, metadata)); +} diff --git a/quicksettings/screenshot/screenshotutil.h b/quicksettings/screenshot/screenshotutil.h new file mode 100644 index 00000000..91ae1134 --- /dev/null +++ b/quicksettings/screenshot/screenshotutil.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2022 by Devin Lin + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include +#include + +#include "screenshot2interface.h" + +class ScreenShotUtil : public QObject +{ + Q_OBJECT + +public: + ScreenShotUtil(QObject *parent = nullptr); + + Q_INVOKABLE void takeScreenShot(); + void handleMetaDataReceived(const QVariantMap &metadata, int fd); + +private: + OrgKdeKWinScreenShot2Interface *m_screenshotInterface; +};