diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,6 +60,7 @@
I18n
Notifications
Wayland
+ WidgetsAddons
)
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0)
diff --git a/data/kde.portal b/data/kde.portal
--- a/data/kde.portal
+++ b/data/kde.portal
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.kde
-Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.ScreenCast
+Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.Screenshot
UseIn=KDE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,6 +20,8 @@
print.cpp
request.cpp
session.cpp
+ screenshot.cpp
+ screenshotdialog.cpp
)
if (SCREENCAST_ENABLED)
@@ -36,6 +38,7 @@
ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS
accessdialog.ui
appchooserdialog.ui
+ screenshotdialog.ui
)
add_executable(xdg-desktop-portal-kde ${xdg_desktop_portal_kde_SRCS})
@@ -49,6 +52,7 @@
KF5::I18n
KF5::Notifications
KF5::WaylandClient
+ KF5::WidgetsAddons
)
if (SCREENCAST_ENABLED)
diff --git a/src/desktopportal.h b/src/desktopportal.h
--- a/src/desktopportal.h
+++ b/src/desktopportal.h
@@ -34,6 +34,7 @@
#if SCREENCAST_ENABLED
#include "screencast.h"
#endif
+#include "screenshot.h"
class DesktopPortal : public QObject
{
@@ -53,6 +54,7 @@
#if SCREENCAST_ENABLED
ScreenCastPortal *m_screenCast;
#endif
+ ScreenshotPortal *m_screenshot;
};
#endif // XDG_DESKTOP_PORTAL_KDE_DESKTOP_PORTAL_H
diff --git a/src/desktopportal.cpp b/src/desktopportal.cpp
--- a/src/desktopportal.cpp
+++ b/src/desktopportal.cpp
@@ -40,6 +40,7 @@
#if SCREENCAST_ENABLED
, m_screenCast(new ScreenCastPortal(this))
#endif
+ , m_screenshot(new ScreenshotPortal(this))
{
}
diff --git a/src/screenshot.h b/src/screenshot.h
new file mode 100644
--- /dev/null
+++ b/src/screenshot.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2018 Red Hat, Inc
+ *
+ * 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 library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Jan Grulich
+ */
+
+#ifndef XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_H
+#define XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_H
+
+#include
+#include
+
+class ScreenshotPortal : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.impl.portal.Screenshot")
+public:
+ ScreenshotPortal(QObject *parent);
+ ~ScreenshotPortal();
+
+public Q_SLOTS:
+ uint Screenshot(const QDBusObjectPath &handle,
+ const QString &app_id,
+ const QString &parent_window,
+ const QVariantMap &options,
+ QVariantMap &results);
+};
+
+#endif // XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_H
diff --git a/src/screenshot.cpp b/src/screenshot.cpp
new file mode 100644
--- /dev/null
+++ b/src/screenshot.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2018 Red Hat, Inc
+ *
+ * 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 library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Jan Grulich
+ */
+
+#include "screenshot.h"
+#include "screenshotdialog.h"
+
+#include
+#include
+#include
+#include
+
+Q_LOGGING_CATEGORY(XdgDesktopPortalKdeScreenshot, "xdp-kde-screenshot")
+
+ScreenshotPortal::ScreenshotPortal(QObject *parent)
+ : QDBusAbstractAdaptor(parent)
+{
+}
+
+ScreenshotPortal::~ScreenshotPortal()
+{
+}
+
+uint ScreenshotPortal::Screenshot(const QDBusObjectPath &handle,
+ const QString &app_id,
+ const QString &parent_window,
+ const QVariantMap &options,
+ QVariantMap &results)
+{
+ qCDebug(XdgDesktopPortalKdeScreenshot) << "Screenshot called with parameters:";
+ qCDebug(XdgDesktopPortalKdeScreenshot) << " handle: " << handle.path();
+ qCDebug(XdgDesktopPortalKdeScreenshot) << " app_id: " << app_id;
+ qCDebug(XdgDesktopPortalKdeScreenshot) << " parent_window: " << parent_window;
+ qCDebug(XdgDesktopPortalKdeScreenshot) << " options: " << options;
+
+ auto screenshotDialog = new ::ScreenshotDialog();
+
+ const bool modal = options.value(QLatin1String("modal"), false).toBool();
+ screenshotDialog->setModal(modal);
+
+ const bool interactive = options.value(QLatin1String("interactive"), false).toBool();
+ if (!interactive) {
+ screenshotDialog->takeScreenshot();
+ }
+
+ if (screenshotDialog->exec()) {
+ QImage screenshot = screenshotDialog->image();
+ screenshotDialog->deleteLater();
+
+ if (screenshot.isNull()) {
+ return 1;
+ }
+ QString filename = QStringLiteral("%1/Screenshot_%2.png").arg(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))
+ .arg(QDateTime::currentDateTime().toString(QLatin1String("yyyyMMdd_hhmmss")));
+ if (!screenshot.save(filename, "PNG")) {
+ return 1;
+ }
+
+ results.insert(QLatin1String("uri"), filename);
+
+ screenshotDialog->deleteLater();
+ return 0;
+ }
+
+ screenshotDialog->deleteLater();
+ return 1;
+}
+
+
diff --git a/src/desktopportal.cpp b/src/screenshotdialog.h
copy from src/desktopportal.cpp
copy to src/screenshotdialog.h
--- a/src/desktopportal.cpp
+++ b/src/screenshotdialog.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2016 Red Hat, Inc
+ * Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -18,31 +18,38 @@
* Jan Grulich
*/
-#include "desktopportal.h"
+#ifndef XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_DIALOG_H
+#define XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_DIALOG_H
#include
-#include
-#include
-#include
-#include
-
-Q_LOGGING_CATEGORY(XdgDesktopPortalKdeDesktopPortal, "xdp-kde-desktop-portal")
-
-DesktopPortal::DesktopPortal(QObject *parent)
- : QObject(parent)
- , m_access(new AccessPortal(this))
- , m_appChooser(new AppChooserPortal(this))
- , m_email(new EmailPortal(this))
- , m_fileChooser(new FileChooserPortal(this))
- , m_inhibit(new InhibitPortal(this))
- , m_notification(new NotificationPortal(this))
- , m_print(new PrintPortal(this))
-#if SCREENCAST_ENABLED
- , m_screenCast(new ScreenCastPortal(this))
-#endif
+
+namespace Ui
{
+class ScreenshotDialog;
}
-DesktopPortal::~DesktopPortal()
+class ScreenshotDialog : public QDialog
{
-}
+ Q_OBJECT
+public:
+ ScreenshotDialog(QDialog *parent = nullptr, Qt::WindowFlags flags = 0);
+ ~ScreenshotDialog();
+
+ QImage image() const;
+
+public Q_SLOTS:
+ void takeScreenshot();
+
+Q_SIGNALS:
+ void failed();
+
+private:
+ Ui::ScreenshotDialog * m_dialog;
+ QImage m_image;
+
+ int mask();
+};
+
+#endif // XDG_DESKTOP_PORTAL_KDE_SCREENSHOT_DIALOG_H
+
+
diff --git a/src/screenshotdialog.cpp b/src/screenshotdialog.cpp
new file mode 100644
--- /dev/null
+++ b/src/screenshotdialog.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2018 Red Hat, Inc
+ *
+ * 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 library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Jan Grulich
+ */
+
+#include "screenshotdialog.h"
+#include "ui_screenshotdialog.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+Q_LOGGING_CATEGORY(XdgDesktopPortalKdeScreenshotDialog, "xdp-kde-screenshot-dialog")
+
+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;
+};
+
+ScreenshotDialog::ScreenshotDialog(QDialog *parent, Qt::WindowFlags flags)
+ : QDialog(parent, flags)
+ , m_dialog(new Ui::ScreenshotDialog)
+{
+ m_dialog->setupUi(this);
+
+ connect(m_dialog->buttonBox, &QDialogButtonBox::accepted, this, &ScreenshotDialog::accept);
+ connect(m_dialog->buttonBox, &QDialogButtonBox::rejected, this, &ScreenshotDialog::reject);
+ connect(m_dialog->takeScreenshotButton, &QPushButton::clicked, this, [this] () {
+ QTimer::singleShot(1000 * m_dialog->delaySpinBox->value(), this, &ScreenshotDialog::takeScreenshot);
+ });
+
+ connect(m_dialog->areaComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this] (int index) {
+ m_dialog->includeBordersCheckbox->setEnabled(index == 2);
+ });
+
+ m_dialog->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
+ m_dialog->iconLabel->setPixmap(QIcon::fromTheme(QLatin1String("video-display")).pixmap(QSize(64, 64)));
+
+ setWindowTitle(i18n("Request screenshot"));
+}
+
+ScreenshotDialog::~ScreenshotDialog()
+{
+ delete m_dialog;
+}
+void ScreenshotDialog::takeScreenshot()
+{
+ int pipeFds[2];
+ if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) {
+ Q_EMIT failed();
+ return;
+ }
+
+ QDBusInterface interface(QLatin1String("org.kde.KWin"), QLatin1String("/Screenshot"), QLatin1String("org.kde.kwin.Screenshot"));
+ if (m_dialog->areaComboBox->currentIndex() < 2) {
+ interface.asyncCall(m_dialog->areaComboBox->currentIndex() ? QLatin1String("screenshotScreen") : QLatin1String("screenshotFullscreen"), QVariant::fromValue(QDBusUnixFileDescriptor(pipeFds[1])), m_dialog->includeCursorCheckbox->isChecked());
+ } else {
+ interface.asyncCall(QLatin1String("interactive"), QVariant::fromValue(QDBusUnixFileDescriptor(pipeFds[1])), mask());
+ }
+
+ QFutureWatcher *watcher = new QFutureWatcher(this);
+ QObject::connect(watcher, &QFutureWatcher::finished, this,
+ [watcher, this] {
+ watcher->deleteLater();
+ m_image = watcher->result();
+ m_dialog->image->setPixmap(QPixmap::fromImage(m_image).scaled(400, 320, Qt::KeepAspectRatio));
+ m_dialog->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
+ }
+ );
+
+ watcher->setFuture(QtConcurrent::run(readImage, pipeFds[0]));
+
+ ::close(pipeFds[1]);
+}
+
+QImage ScreenshotDialog::image() const
+{
+ return m_image;
+}
+
+int ScreenshotDialog::mask()
+{
+ int mask = 0;
+ if (m_dialog->includeBordersCheckbox->isChecked()) {
+ mask = 1;
+ }
+ if (m_dialog->includeCursorCheckbox->isChecked()) {
+ mask |= 1 << 1;
+ }
+ return mask;
+}
diff --git a/src/screenshotdialog.ui b/src/screenshotdialog.ui
new file mode 100644
--- /dev/null
+++ b/src/screenshotdialog.ui
@@ -0,0 +1,243 @@
+
+
+ ScreenshotDialog
+
+
+
+ 0
+ 0
+ 500
+ 297
+
+
+
+
+ 0
+ 0
+
+
+
+ Dialog
+
+
+ -
+
+
+ <b>Content Options</b>
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Take screenshot
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Icon
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+ Include mouse pointer
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 75
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Area:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
-
+
+ Full Screen (All Monitors)
+
+
+ -
+
+ Current Screen
+
+
+ -
+
+ Active Window
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Delay:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ No Delay
+
+
+ 0
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Save
+
+
+
+ -
+
+
+ <b>Capture Mode</b>
+
+
+
+ -
+
+
+ false
+
+
+ Include window borders
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+
+ KSeparator
+ QFrame
+
+
+
+
+
+