on xenial help sign in
React on PrtScr keyboard shortcut for taking screenshots on desktop Fixes: #1474149
682a5f9Lukáš Tinkl
plugins/ScreenGrabber/screengrabber.cpp+31−20
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19#include "screengrabber.h"
17#include "screengrabber.h"
@@ -28,27 +26,35 @@
28
26
29#include <QDebug>
27#include <QDebug>
30
28
31−void saveScreenshot(QImage screenshot, QString filename, QString format, int quality)
29+bool saveScreenshot(const QImage &screenshot, const QString &filename, const QString &format, int quality)
32{
30{
33− if (!screenshot.save(filename, format.toLatin1().data(), quality))
31+ if (!screenshot.save(filename, format.toLatin1().data(), quality)) {
34− qWarning() << "ScreenShotter: failed to save snapshot!";
32+ qWarning() << "ScreenGrabber: failed to save snapshot!";
33+ return false;
34+ }
35+
36+ return true;
35}
37}
36
38
37ScreenGrabber::ScreenGrabber(QObject *parent)
39ScreenGrabber::ScreenGrabber(QObject *parent)
38− : QObject(parent),
40+ : QObject(parent)
39− screenshotQuality(0)
40{
41{
42+ QDir screenshotsDir;
43+ if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
44+ qDebug() << "Using test environment";
45+ QStandardPaths::setTestModeEnabled(true);
46+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
47+ } else {
48+ qDebug() << "Using real environment";
41− QDir screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation));
49+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
50+ }
42− screenshotsDir.mkdir("Screenshots");
51+ screenshotsDir.mkpath("Screenshots");
43 screenshotsDir.cd("Screenshots");
52 screenshotsDir.cd("Screenshots");
44− if (screenshotsDir.exists())
53+ if (screenshotsDir.exists()) {
45− {
46 fileNamePrefix = screenshotsDir.absolutePath();
54 fileNamePrefix = screenshotsDir.absolutePath();
47 fileNamePrefix.append("/screenshot");
55 fileNamePrefix.append("/screenshot");
48− }
49− else
56+ } else {
50− {
51− qWarning() << "ScreenShotter: failed to create directory at: " << screenshotsDir.absolutePath();
57+ qWarning() << "ScreenGrabber: failed to create directory at: " << screenshotsDir.absolutePath();
52 }
58 }
53}
59}
54
60
@@ -74,11 +80,16 @@ void ScreenGrabber::captureAndSave()
74 return;
80 return;
75 }
81 }
76
82
77− QImage screenshot = main_window->grabWindow();
83+ const QImage screenshot = main_window->grabWindow();
84+ const QString filename = makeFileName();
85+ qDebug() << "Saving screenshot to" << filename;
78− QtConcurrent::run(saveScreenshot, screenshot, makeFileName(), getFormat(), screenshotQuality);
86+ auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
87+ if (saveOp.result()) {
88+ Q_EMIT screenshotSaved(filename);
89+ }
79}
90}
80
91
81−QString ScreenGrabber::makeFileName()
92+QString ScreenGrabber::makeFileName() const
82{
93{
83 QString fileName(fileNamePrefix);
94 QString fileName(fileNamePrefix);
84 fileName.append(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"));
95 fileName.append(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"));
@@ -87,7 +98,7 @@ QString ScreenGrabber::makeFileName()
87 return fileName;
98 return fileName;
88}
99}
89
100
90−QString ScreenGrabber::getFormat()
101+QString ScreenGrabber::getFormat() const
91{
102{
92 //TODO: This should be configurable (perhaps through gsettings?)
103 //TODO: This should be configurable (perhaps through gsettings?)
93 return "png";
104 return "png";
plugins/ScreenGrabber/screengrabber.h+8−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19#ifndef SNAPSHOTTER_H
17#ifndef SNAPSHOTTER_H
@@ -28,15 +26,19 @@ class ScreenGrabber: public QObject
28
26
29public:
27public:
30 explicit ScreenGrabber(QObject *parent = 0);
28 explicit ScreenGrabber(QObject *parent = 0);
29+ ~ScreenGrabber() = default;
31
30
32public Q_SLOTS:
31public Q_SLOTS:
33 void captureAndSave();
32 void captureAndSave();
34
33
34+Q_SIGNALS:
35+ void screenshotSaved(const QString &filename);
36+
35private:
37private:
36− QString makeFileName();
38+ QString makeFileName() const;
37− QString getFormat();
39+ QString getFormat() const;
38 QString fileNamePrefix;
40 QString fileNamePrefix;
39− int screenshotQuality;
41+ int screenshotQuality = -1; // default quality for the format
40};
42};
41
43
42#endif
44#endif
qml/Components/PhysicalKeysMapper.qml+2
@@ -93,6 +93,8 @@ Item {
93 }
93 }
94 d.volumeUpKeyPressed = true;
94 d.volumeUpKeyPressed = true;
95 }
95 }
96+ } else if (event.key == Qt.Key_Print) {
97+ root.screenshotTriggered();
96 }
98 }
97 }
99 }
98
100
qml/Components/ScreenGrabber.qml+1−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@ import ScreenGrabber 0.1
20
20
21Rectangle {
21Rectangle {
22 id: root
22 id: root
23− enabled: true
24 visible: false
23 visible: false
25 color: "white"
24 color: "white"
26 anchors.fill: parent
25 anchors.fill: parent
@@ -38,9 +37,6 @@ Rectangle {
38 }
37 }
39
38
40 function capture() {
39 function capture() {
41− if (!enabled)
42− return;
43−
44 visible = true;
40 visible = true;
45 shutterSound.stop();
41 shutterSound.stop();
46 shutterSound.play();
42 shutterSound.play();
qml/Shell.qml−1
@@ -188,7 +188,6 @@ Item {
188 ScreenGrabber {
188 ScreenGrabber {
189 id: screenGrabber
189 id: screenGrabber
190 z: dialogs.z + 10
190 z: dialogs.z + 10
191− enabled: Powerd.status === Powerd.On
192 }
191 }
193
192
194 Binding {
193 Binding {
tests/plugins/CMakeLists.txt+1
@@ -2,6 +2,7 @@ add_subdirectory(AccountsService)
2add_subdirectory(Greeter)
2add_subdirectory(Greeter)
3add_subdirectory(LightDM)
3add_subdirectory(LightDM)
4add_subdirectory(Dash)
4add_subdirectory(Dash)
5+add_subdirectory(ScreenGrabber)
5add_subdirectory(Ubuntu)
6add_subdirectory(Ubuntu)
6add_subdirectory(Unity)
7add_subdirectory(Unity)
7add_subdirectory(Utils)
8add_subdirectory(Utils)
tests/plugins/ScreenGrabber/CMakeLists.txtnew file+14
@@ -0,0 +1,14 @@
1+include_directories(
2+ ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ )
5+
6+add_definitions(
7+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
8+ )
9+add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
10+
11+add_executable(ScreenGrabberTestExec ScreenGrabberTest.cpp ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber/screengrabber.cpp)
12+qt5_use_modules(ScreenGrabberTestExec Test Quick)
13+target_link_libraries(ScreenGrabberTestExec ${Qt5Quick_LIBRARIES} ScreenGrabber-qml)
14+add_unity8_uitest(ScreenGrabber ScreenGrabberTestExec DEPENDS ScreenGrabber-qml)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cppnew file+65
@@ -0,0 +1,65 @@
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+#include "screengrabber.h"
18+
19+#include <QtTest>
20+#include <QtQuick/QQuickView>
21+#include <QtQuick/QQuickItem>
22+#include <QQmlEngine>
23+
24+class ScreenGrabberTest: public QObject
25+{
26+ Q_OBJECT
27+
28+private Q_SLOTS:
29+
30+ void initTestCase()
31+ {
32+ m_view = new QQuickView();
33+ m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
34+ m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/grabber.qml"));
35+ m_grabber = dynamic_cast<ScreenGrabber*>(m_view->rootObject()->property("grabber").value<QObject*>());
36+ QVERIFY(m_grabber);
37+ m_view->show();
38+ QTest::qWaitForWindowExposed(m_view);
39+ }
40+
41+ void cleanupTestCase()
42+ {
43+ delete m_view;
44+ m_view = nullptr;
45+ QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
46+ }
47+
48+ void testGrabScreenshot()
49+ {
50+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
51+ m_grabber->captureAndSave();
52+ QTRY_VERIFY(grabberSpy.count() == 1);
53+ const QVariantList args = grabberSpy.takeFirst();
54+ QVERIFY(args.count() == 1); // verify we got a non-empty filename where the screenshot has been saved
55+ QVERIFY(!args.first().toString().isEmpty());
56+ }
57+
58+private:
59+ QQuickView *m_view;
60+ ScreenGrabber *m_grabber = nullptr;
61+};
62+
63+QTEST_MAIN(ScreenGrabberTest)
64+
65+#include "ScreenGrabberTest.moc"
tests/plugins/ScreenGrabber/grabber.qmlnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright 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+import QtQuick 2.4
18+import ScreenGrabber 0.1
19+
20+Rectangle {
21+ property var grabber: screenGrabber
22+
23+ width: 100
24+ height: 100
25+ color: "green"
26+
27+ ScreenGrabber {
28+ id: screenGrabber
29+ objectName: "screenGrabber"
30+ }
31+}
tests/qmltests/Components/tst_PhysicalKeysMapper.qml+5
@@ -89,6 +89,11 @@ TestCase {
89 compare(powerSpy.count, 1);
89 compare(powerSpy.count, 1);
90 }
90 }
91
91
92+ function test_screenshotPrtScr() {
93+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_Print })
94+ tryCompare(screenshotSpy, "count", 1) // verify the screenshotTriggered signal has been called, exactly once
95+ }
96+
92 function test_screenshotButtons_data() {
97 function test_screenshotButtons_data() {
93 return [
98 return [
94 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
99 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },