on xenial help sign in
First attempt at saving/loading screeshots

Totally untested ^_^

commit489b028615092df69d37de62453bffb11152aa48 authorAlbert Astals Cid <albert.astals@canonical.com> dateWed 22 Apr 2015, 15:10 (11 years ago) parent8190f38
11 files changed+304−11
debian/unity8-private.install+1 plugins/CMakeLists.txt+1 plugins/SessionScreenshoter/CMakeLists.txt+13 plugins/SessionScreenshoter/plugin.cpp+27 plugins/SessionScreenshoter/plugin.h+32 plugins/SessionScreenshoter/qmldir+2 plugins/SessionScreenshoter/sessionscreenshoter.cpp+130 plugins/SessionScreenshoter/sessionscreenshoter.h+70 qml/Stages/ApplicationWindow.qml+25−11 qml/Stages/Splash.qml+2 qml/Stages/SpreadDelegate.qml+1
debian/unity8-private.install+1
@@ -6,6 +6,7 @@ usr/lib/*/unity8/qml/LightDM
66usr/lib/*/unity8/qml/Lights
77usr/lib/*/unity8/qml/Powerd
88usr/lib/*/unity8/qml/SessionBroadcast
9+usr/lib/*/unity8/qml/SessionScreenshoter
910usr/lib/*/unity8/qml/ScreenGrabber
1011usr/lib/*/unity8/qml/Ubuntu
1112usr/lib/*/unity8/qml/Unity
plugins/CMakeLists.txt+1
@@ -18,6 +18,7 @@ add_subdirectory(Lights)
1818add_subdirectory(Dash)
1919add_subdirectory(Powerd)
2020add_subdirectory(SessionBroadcast)
21+add_subdirectory(SessionScreenshoter)
2122add_subdirectory(ScreenGrabber)
2223add_subdirectory(Ubuntu)
2324add_subdirectory(Unity)
plugins/SessionScreenshoter/CMakeLists.txtnew file+13
@@ -0,0 +1,13 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+)
4+
5+set(SOURCES
6+ sessionscreenshoter.cpp
7+ plugin.cpp
8+)
9+
10+add_library(SessionScreenshoter-qml MODULE ${SOURCES})
11+qt5_use_modules(SessionScreenshoter-qml Qml Gui Quick Concurrent)
12+
13+add_unity8_plugin(SessionScreenshoter 0.1 SessionScreenshoter TARGETS SessionScreenshoter-qml)
plugins/SessionScreenshoter/plugin.cppnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#include "plugin.h"
19+#include "sessionscreenshoter.h"
20+
21+#include <QtQml/qqml.h>
22+
23+void SessionScreenshoterPlugin::registerTypes(const char *uri)
24+{
25+ Q_ASSERT(uri == QLatin1String("SessionScreenshoter"));
26+ qmlRegisterType<SessionScreenshoter>(uri, 0, 1, "SessionScreenshoter");
27+}
plugins/SessionScreenshoter/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef SESSIONSCREENSHOTER_PLUGIN_H
19+#define SESSIONSCREENSHOTER_PLUGIN_H
20+
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class SessionScreenshoterPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+};
31+
32+#endif
plugins/SessionScreenshoter/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module SessionScreenshoter
2+plugin SessionScreenshoter-qml
plugins/SessionScreenshoter/sessionscreenshoter.cppnew file+130
@@ -0,0 +1,130 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#include "sessionscreenshoter.h"
19+
20+#include <QDebug>
21+#include <QQuickItem>
22+#include <QQuickItemGrabResult>
23+#include <QtConcurrent>
24+
25+static QString cachePath()
26+{
27+ return QDir::homePath() + "/.cache/app_shots/";
28+}
29+
30+SessionScreenshoter::SessionScreenshoter(QObject *parent)
31+ : QObject(parent),
32+ m_target(nullptr)
33+{
34+ // TODO tests
35+}
36+
37+QString SessionScreenshoter::appId() const
38+{
39+ return m_appId;
40+}
41+
42+void SessionScreenshoter::setAppdId(const QString &appId)
43+{
44+ if (appId != m_appId) {
45+ m_appId = appId;
46+ const QString path = cachePath() + appId + ".png";
47+ if (QFile::exists(path)) {
48+ setPath(path);
49+ }
50+ }
51+}
52+
53+QQuickItem *SessionScreenshoter::target() const
54+{
55+ return m_target;
56+}
57+
58+void SessionScreenshoter::setTarget(QQuickItem *target)
59+{
60+ if (target != m_target) {
61+ m_target = target;
62+ Q_EMIT targetChanged();
63+ }
64+}
65+
66+QString SessionScreenshoter::path() const
67+{
68+ return m_path;
69+}
70+
71+void SessionScreenshoter::setPath(const QString &path)
72+{
73+ if (path != m_path) {
74+ m_path = path;
75+ Q_EMIT pathChanged();
76+ }
77+}
78+
79+static QString saveScreenshot(const QString &appId, const QSharedPointer<QQuickItemGrabResult> grabResult)
80+{
81+ const QImage image = grabResult->image();
82+ const QString cPath = cachePath();
83+ QDir d;
84+ d.mkpath(cPath);
85+ const QString path = cPath + appId + ".png";
86+ bool b = image.save(path);
87+ if (b) {
88+ return path;
89+ } else {
90+ return QString();
91+ }
92+}
93+
94+void SessionScreenshoter::take()
95+{
96+ if (!m_grabResult.isNull()) {
97+ qWarning() << "Asked to take a screenshot when there's one already being taken, ignoring";
98+ return;
99+ }
100+
101+ if (m_target && !m_appId.isEmpty()) {
102+ // grabToImage runs in a separate thread
103+ m_grabResult = m_target->grabToImage();
104+ connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this, &SessionScreenshoter::grabReady);
105+ } else {
106+ qWarning() << "Can't take screnshot: appId:" << m_appId << " target:" << m_target;
107+ }
108+}
109+
110+void SessionScreenshoter::removeScreenshot()
111+{
112+ const QString path = cachePath() + m_appId + ".png";
113+ QFile::remove(path);
114+}
115+
116+void SessionScreenshoter::grabReady()
117+{
118+ QFuture<QString> f = QtConcurrent::run(saveScreenshot, m_appId, m_grabResult);
119+ m_watcher.reset(new QFutureWatcher<QString>(this));
120+ m_watcher->setFuture(f);
121+ connect(m_watcher.data(), &QFutureWatcher<QString>::finished, this, &SessionScreenshoter::saveFinished);
122+}
123+
124+void SessionScreenshoter::saveFinished()
125+{
126+ setPath(m_watcher->future().result());
127+ m_watcher.clear();
128+ m_grabResult.clear();
129+ Q_EMIT screenshotTaken();
130+}
plugins/SessionScreenshoter/sessionscreenshoter.hnew file+70
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef SESSIONSCREENSHOTER_H
19+#define SESSIONSCREENSHOTER_H
20+
21+#include <QObject>
22+#include <QSharedPointer>
23+#include <QFutureWatcher>
24+
25+class QQuickItem;
26+class QQuickItemGrabResult;
27+
28+class SessionScreenshoter : public QObject
29+{
30+ Q_OBJECT
31+
32+ Q_PROPERTY(QString appId READ appId WRITE setAppdId NOTIFY appdIdChanged)
33+ Q_PROPERTY(QString path READ path NOTIFY pathChanged)
34+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
35+
36+public:
37+ explicit SessionScreenshoter(QObject *parent = 0);
38+
39+ QString appId() const;
40+ void setAppdId(const QString &appId);
41+
42+ QQuickItem *target() const;
43+ void setTarget(QQuickItem *target);
44+
45+ QString path() const;
46+
47+ Q_INVOKABLE void take();
48+ Q_INVOKABLE void removeScreenshot();
49+
50+Q_SIGNALS:
51+ void appdIdChanged();
52+ void targetChanged();
53+ void pathChanged();
54+ void screenshotTaken();
55+
56+private Q_SLOTS:
57+ void grabReady();
58+ void saveFinished();
59+
60+private:
61+ void setPath(const QString &path);
62+
63+ QString m_appId;
64+ QString m_path;
65+ QQuickItem *m_target;
66+ QSharedPointer<QQuickItemGrabResult> m_grabResult;
67+ QSharedPointer<QFutureWatcher<QString>> m_watcher;
68+};
69+
70+#endif
qml/Stages/ApplicationWindow.qml+25−11
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 1.1
1919import Unity.Application 0.1
20+import SessionScreenshoter 0.1
2021
2122FocusScope {
2223 id: root
@@ -29,11 +30,16 @@ FocusScope {
2930 property QtObject application
3031 property int orientation
3132
33+ function removeScreenshot() {
34+ screenshoter.removeScreenshot();
35+ }
36+
3237 QtObject {
3338 id: d
3439
3540 // helpers so that we don't have to check for the existence of an application everywhere
3641 // (in order to avoid breaking qml binding due to a javascript exception)
42+ readonly property string appId: root.application ? root.application.appId : ""
3743 readonly property string name: root.application ? root.application.name : ""
3844 readonly property url icon: root.application ? root.application.icon : ""
3945 readonly property int applicationState: root.application ? root.application.state : -1
@@ -49,11 +55,10 @@ FocusScope {
4955 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
5056
5157 property bool needToTakeScreenshot:
52− sessionContainer.surface && d.surfaceInitialized && screenshotImage.status === Image.Null
53− && d.applicationState === ApplicationInfoInterface.Stopped
58+ sessionContainer.surface && d.surfaceInitialized && (d.applicationState === ApplicationInfoInterface.Stopped || d.applicationState === ApplicationInfoInterface.Suspended)
5459 onNeedToTakeScreenshotChanged: {
5560 if (needToTakeScreenshot) {
56− screenshotImage.take();
61+ screenshoter.take();
5762 }
5863 }
5964
@@ -73,22 +78,28 @@ FocusScope {
7378 Image {
7479 id: screenshotImage
7580 objectName: "screenshotImage"
76− source: d.defaultScreenshot
81+ source: screenshoter.path != "" ? screenshoter.path : d.defaultScreenshot
7782 anchors.fill: parent
7883 antialiasing: !root.interactive
79−
80− function take() {
81− // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
82− // eg: "image://application/calculator-app/123456"
83− var timeMs = new Date().getTime();
84− source = "image://application/" + root.application.appId + "/" + timeMs;
85− }
84+ cache: false
85+ asynchronous: true
8686
8787 // Save memory by using a half-resolution (thus quarter size) screenshot
8888 sourceSize.width: root.width / 2
8989 sourceSize.height: root.height / 2
9090 }
9191
92+ SessionScreenshoter {
93+ id: screenshoter
94+ appId: d.appId
95+ target: root
96+
97+ onScreenshotTaken: {
98+ screenshotImage.source = "";
99+ screenshotImage.source = screenshoter.path;
100+ }
101+ }
102+
92103 Loader {
93104 id: splashLoader
94105 visible: active
@@ -104,6 +115,9 @@ FocusScope {
104115 backgroundColor: d.splashColor
105116 headerColor: d.splashColorHeader
106117 footerColor: d.splashColorFooter
118+ // TODO show .desktop file splash screen also if
119+ // we do not have a screenshot but obviously without spinner
120+ activeSpinner: true
107121 }
108122 }
109123 }
qml/Stages/Splash.qml+2
@@ -31,6 +31,7 @@ Item {
3131 property url icon
3232 property alias title: header.title
3333 property alias showHeader: header.visible
34+ property alias activeSpinner: activityIndicator.running
3435
3536 Ambiance.Palette {
3637 id: ambiancePalette
@@ -144,6 +145,7 @@ Item {
144145 }
145146
146147 ActivityIndicator {
148+ id: activityIndicator
147149 anchors.centerIn: header.visible ? parent : undefined
148150 anchors.verticalCenterOffset: header.visible ? header.height / 2 : 0
149151
qml/Stages/SpreadDelegate.qml+1
@@ -157,6 +157,7 @@ FocusScope {
157157 dragArea.moving = false;
158158 if (requestClose) {
159159 root.closed();
160+ appWindow.removeScreenshot();
160161 } else {
161162 dragArea.distance = 0;
162163 }