on xenial help sign in
Add a plugin to take screenshots on vol up + vol down

The screenshots are saved into <Pictures dir>/Screenshots/

commit7ce9d8345b1d3671e57c8f1027d4941af5fd581f authorAlberto Aguirre <alberto.aguirre@canonical.com> dateWed 24 Sep 2014, 11:52 (12 years ago) parentff9936d
10 files changed+310
debian/unity8-private.install+1 plugins/CMakeLists.txt+1 plugins/Screenshotter/CMakeLists.txt+13 plugins/Screenshotter/Screenshotter.qmltypes+17 plugins/Screenshotter/plugin.cpp+28 plugins/Screenshotter/plugin.h+33 plugins/Screenshotter/qmldir+3 plugins/Screenshotter/screenshotter.cpp+105 plugins/Screenshotter/screenshotter.h+42 qml/Shell.qml+67
debian/unity8-private.install+1
@@ -5,6 +5,7 @@ usr/lib/*/unity8/qml/LightDM
55usr/lib/*/unity8/qml/Lights
66usr/lib/*/unity8/qml/Powerd
77usr/lib/*/unity8/qml/SessionBroadcast
8+usr/lib/*/unity8/qml/Screenshotter
89usr/lib/*/unity8/qml/Ubuntu
910usr/lib/*/unity8/qml/Unity
1011usr/lib/*/unity8/qml/Utils
plugins/CMakeLists.txt+1
@@ -18,6 +18,7 @@ add_subdirectory(Lights)
1818add_subdirectory(Dash)
1919add_subdirectory(Powerd)
2020add_subdirectory(SessionBroadcast)
21+add_subdirectory(Screenshotter)
2122add_subdirectory(Ubuntu)
2223add_subdirectory(Unity)
2324add_subdirectory(Utils)
plugins/Screenshotter/CMakeLists.txtnew file+13
@@ -0,0 +1,13 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+)
4+
5+set(SCREENSHOTTERSOURCES
6+ screenshotter.cpp
7+ plugin.cpp
8+)
9+
10+add_library(Screenshotter-qml MODULE ${SCREENSHOTTERSOURCES})
11+qt5_use_modules(Screenshotter-qml Qml Gui Quick)
12+
13+add_unity8_plugin(Screenshotter 0.1 Screenshotter TARGETS Screenshotter-qml)
plugins/Screenshotter/Screenshotter.qmltypesnew file+17
@@ -0,0 +1,17 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -nonrelocatable Screenshotter 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "ScreenShotter"
12+ prototype: "QObject"
13+ exports: ["Screenshotter/Screenshotter 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Method { name: "takeScreenshot" }
16+ }
17+}
plugins/Screenshotter/plugin.cppnew file+28
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (C) 2014 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+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "screenshotter.h"
21+
22+#include <QtQml/qqml.h>
23+
24+void ScreenshotterPlugin::registerTypes(const char *uri)
25+{
26+ Q_ASSERT(uri == QLatin1String("Screenshotter"));
27+ qmlRegisterType<ScreenShotter>(uri, 0, 1, "Screenshotter");
28+}
plugins/Screenshotter/plugin.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2014 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+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17+ */
18+
19+#ifndef SCREENSHOTTER_PLUGIN_H
20+#define SCREENSHOTTER_PLUGIN_H
21+
22+#include <QtQml/QQmlExtensionPlugin>
23+
24+class ScreenshotterPlugin : public QQmlExtensionPlugin
25+{
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+
29+public:
30+ void registerTypes(const char *uri);
31+};
32+
33+#endif // SCREENSHOTTER_PLUGIN_H
plugins/Screenshotter/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Screenshotter
2+plugin Screenshotter-qml
3+typeinfo Screenshotter.qmltypes
plugins/Screenshotter/screenshotter.cppnew file+105
@@ -0,0 +1,105 @@
1+/*
2+ * Copyright (C) 2014 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+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17+ */
18+
19+#include "screenshotter.h"
20+
21+#include <QUuid>
22+#include <QChar>
23+#include <QString>
24+#include <QStringList>
25+#include <QStandardPaths>
26+#include <QtGui/QGuiApplication>
27+#include <QtQuick/QQuickWindow>
28+
29+#include <QDebug>
30+
31+ScreenShotter::ScreenShotter(QObject *parent)
32+ : QObject(parent),
33+ screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation)),
34+ screenshotQuality(90)
35+{
36+ screenshotsDir.mkdir("Screenshots");
37+ screenshotsDir.cd("Screenshots");
38+}
39+
40+void ScreenShotter::takeScreenshot()
41+{
42+ QWindowList windows = QGuiApplication::topLevelWindows();
43+ if (windows.empty())
44+ {
45+ qWarning() << "ScreenShotter: no top level windows found!";
46+ return;
47+ }
48+
49+ QQuickWindow *main_window = qobject_cast<QQuickWindow *>(windows[0]);
50+ if (!main_window)
51+ {
52+ qWarning() << "ScreenShotter: can only take screenshots of QQuickWindows";
53+ return;
54+ }
55+
56+ QImage snapshot = main_window->grabWindow();
57+ if (!snapshot.save(generateName(), "JPG", screenshotQuality))
58+ qWarning() << "ScreenShotter: failed to save snapshot!";
59+}
60+
61+QString ScreenShotter::generateUniqueNum()
62+{
63+ // First lets look for existing files using our numbering pattern
64+ QStringList nameFilter;
65+ nameFilter << "screenshot????.jpg";
66+ QStringList fileList = screenshotsDir.entryList(nameFilter, QDir::Files, QDir::Name | QDir::Reversed);
67+
68+ if (!fileList.empty())
69+ {
70+ Q_FOREACH(QString const& fileName, fileList)
71+ {
72+ // Just the the four digit number
73+ QString fileNumber = fileName.mid(10, 4);
74+ bool ok = false;
75+ int num = fileNumber.toInt(&ok) + 1;
76+ if (!ok)
77+ continue;
78+
79+ // OK it's an actual number
80+ if (num <= 9999)
81+ {
82+ // Produce a string that has the next sequential number, appending 0's when necessary
83+ return QString("%1").arg(num, 4, 10, QChar('0'));
84+ }
85+ else
86+ {
87+ // 10000 screenshots huh? ummm, well let's start using uuids then...
88+ QString uuid = QUuid::createUuid().toString();
89+ // Remove the curly brackets
90+ return uuid.mid(1, 36);
91+ }
92+ }
93+ }
94+ //No files exist using our numbering scheme
95+ return QString("0000");
96+}
97+
98+QString ScreenShotter::generateName()
99+{
100+ QString fileName = screenshotsDir.absolutePath();
101+ fileName.append("/screenshot");
102+ fileName.append(generateUniqueNum());
103+ fileName.append(".jpg");
104+ return fileName;
105+}
plugins/Screenshotter/screenshotter.hnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2014 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+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17+ */
18+
19+#ifndef SNAPSHOTTER_H
20+#define SNAPSHOTTER_H
21+
22+#include <QObject>
23+#include <QDir>
24+
25+class ScreenShotter: public QObject
26+{
27+ Q_OBJECT
28+
29+public:
30+ explicit ScreenShotter(QObject *parent = 0);
31+
32+public Q_SLOTS:
33+ void takeScreenshot();
34+
35+private:
36+ QString generateName();
37+ QString generateUniqueNum();
38+ QDir screenshotsDir;
39+ int screenshotQuality;
40+};
41+
42+#endif
qml/Shell.qml+67
@@ -30,6 +30,7 @@ import Utils 0.1
3030import LightDM 0.1 as LightDM
3131import Powerd 0.1
3232import SessionBroadcast 0.1
33+import Screenshotter 0.1
3334import "Greeter"
3435import "Launcher"
3536import "Panel"
@@ -128,12 +129,18 @@ Item {
128129 objectName: "dashCommunicator"
129130 }
130131
132+ Screenshotter {
133+ id: screenShotter
134+ objectName: "screenShotter"
135+ }
136+
131137 WindowKeysFilter {
132138 // Handle but do not filter out volume keys
133139 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
134140 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
135141
136142 Keys.onPressed: {
143+ screenshotAnimation.onKeyPressed(event.key);
137144 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
138145 dialogs.onPowerKeyPressed();
139146 event.accepted = true;
@@ -143,6 +150,7 @@ Item {
143150 }
144151
145152 Keys.onReleased: {
153+ screenshotAnimation.onKeyReleased(event.key);
146154 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
147155 dialogs.onPowerKeyReleased();
148156 event.accepted = true;
@@ -687,6 +695,65 @@ Item {
687695 }
688696 }
689697 }
698+ }
699+
700+ Rectangle {
701+ id: screenshotAnimation
702+ z: edgeDemo.z + 10
703+ enabled: false
704+ visible: false
705+ color: "white"
706+ anchors.fill: parent
707+ opacity: 0.0
708+
709+ QtObject {
710+ id: state
711+ property bool volumeUp: false
712+ property bool volumeDown: false
713+ }
714+
715+ function onKeyPressed(key) {
716+ if (key == Qt.Key_VolumeUp)
717+ state.volumeUp = true;
718+ else if (key == Qt.Key_VolumeDown)
719+ state.volumeDown = true;
720+ if (state.volumeDown && state.volumeUp) {
721+ enabled = true;
722+ visible = true;
723+ screenshotFadeIn.start();
724+ }
725+ }
726+
727+ function onKeyReleased(key) {
728+ if (key == Qt.Key_VolumeUp)
729+ state.volumeUp = false;
730+ else if (key == Qt.Key_VolumeDown)
731+ state.volumeDown = false;
732+ }
733+
734+ NumberAnimation on opacity {
735+ id: screenshotFadeIn
736+ from: 0.0
737+ to: 1.0
738+ onStopped: {
739+ if (screenshotAnimation.enabled && screenshotAnimation.visible) {
740+ screenshotFadeOut.start()
741+ }
742+ }
743+ }
744+
745+ NumberAnimation on opacity {
746+ id: screenshotFadeOut
747+ from: 1.0
748+ to: 0.0
749+ onStopped: {
750+ if (screenshotAnimation.enabled && screenshotAnimation.visible) {
751+ screenShotter.takeScreenshot();
752+ screenshotAnimation.enabled = false
753+ screenshotAnimation.visible = false
754+ }
755+ }
756+ }
690757 }
691758
692759}