From e9b894da1601473eca7706acdb2f8cbb8a7fb12b Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 6 Jan 2021 15:40:47 +0000 Subject: [PATCH] Transfer: implement 'Grab QR code from screen' functionality --- pages/Transfer.qml | 25 ++++++++++++-- src/QR-Code-scanner/Decoder.cpp | 28 +++++++++++++++ src/QR-Code-scanner/Decoder.h | 28 +++++++++++++++ src/main/oshelper.cpp | 60 +++++++++++++++++++++++++++++++++ src/main/oshelper.h | 3 ++ src/qt/macoshelper.h | 3 ++ src/qt/macoshelper.mm | 39 +++++++++++++++++++++ 7 files changed, 184 insertions(+), 2 deletions(-) diff --git a/pages/Transfer.qml b/pages/Transfer.qml index 2e0624f7..828d7b5d 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -93,12 +93,16 @@ Rectangle { oaPopup.open() } - function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) { - console.log("updateFromQrCode") + function fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name) { addressLine.text = address setPaymentId(payment_id); amountLine.text = amount setDescription((recipient_name ? recipient_name + " " : "") + tx_description); + } + + function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) { + console.log("updateFromQrCode") + fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name); cameraUi.qrcode_decoded.disconnect(updateFromQrCode) } @@ -196,6 +200,23 @@ Rectangle { } } + MoneroComponents.InlineButton { + fontFamily: FontAwesome.fontFamily + fontPixelSize: 18 + text: FontAwesome.desktop + onClicked: { + clearFields(); + const codes = oshelper.grabQrCodesFromScreen(); + for (var index = 0; index < codes.length; ++index) { + const parsed = walletManager.parse_uri_to_object(codes[index]); + if (!parsed.error) { + fillPaymentDetails(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name); + break; + } + } + } + } + MoneroComponents.InlineButton { fontFamily: FontAwesome.fontFamily text: FontAwesome.addressBook diff --git a/src/QR-Code-scanner/Decoder.cpp b/src/QR-Code-scanner/Decoder.cpp index a814969b..1bb99140 100644 --- a/src/QR-Code-scanner/Decoder.cpp +++ b/src/QR-Code-scanner/Decoder.cpp @@ -1,3 +1,31 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #include "Decoder.h" #include diff --git a/src/QR-Code-scanner/Decoder.h b/src/QR-Code-scanner/Decoder.h index e25c3936..9efaca67 100644 --- a/src/QR-Code-scanner/Decoder.h +++ b/src/QR-Code-scanner/Decoder.h @@ -1,3 +1,31 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #include struct quirc; diff --git a/src/main/oshelper.cpp b/src/main/oshelper.cpp index 7584732e..7037e21e 100644 --- a/src/main/oshelper.cpp +++ b/src/main/oshelper.cpp @@ -27,10 +27,16 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "oshelper.h" + +#include + #include +#include #include +#include #include #include +#include #include #include #include @@ -55,6 +61,40 @@ #include "qt/utils.h" #endif +#include "QR-Code-scanner/Decoder.h" +#include "qt/ScopeGuard.h" + +namespace +{ + +QPixmap screenshot() +{ +#ifdef Q_OS_MAC + return MacOSHelper::screenshot(); +#else + std::unordered_set hidden; + const QWindowList windows = QGuiApplication::allWindows(); + for (QWindow *window : windows) + { + if (window->isVisible()) + { + hidden.emplace(window); + window->hide(); + } + } + const auto unhide = sg::make_scope_guard([&hidden]() { + for (QWindow *window : hidden) + { + window->show(); + } + }); + + return QGuiApplication::primaryScreen()->grabWindow(0); +#endif +} + +} // namespace + #if defined(Q_OS_WIN) bool openFolderAndSelectItem(const QString &filePath) { @@ -99,6 +139,26 @@ QString OSHelper::downloadLocation() const return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); } +QList OSHelper::grabQrCodesFromScreen() const +{ + QList codes; + + try + { + const QImage image = screenshot().toImage(); + const std::vector decoded = QrDecoder().decode(image); + std::for_each(decoded.begin(), decoded.end(), [&codes](const std::string &code) { + codes.push_back(QString::fromStdString(code)); + }); + } + catch (const std::exception &e) + { + qWarning() << e.what(); + } + + return codes; +} + bool OSHelper::openContainingFolder(const QString &filePath) const { QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath(); diff --git a/src/main/oshelper.h b/src/main/oshelper.h index 96f58073..f4168071 100644 --- a/src/main/oshelper.h +++ b/src/main/oshelper.h @@ -29,7 +29,9 @@ #ifndef OSHELPER_H #define OSHELPER_H +#include #include +#include /** * @brief The OSHelper class - exports to QML some OS-related functions */ @@ -43,6 +45,7 @@ public: Q_INVOKABLE void createDesktopEntry() const; Q_INVOKABLE QString downloadLocation() const; + Q_INVOKABLE QList grabQrCodesFromScreen() const; Q_INVOKABLE bool openContainingFolder(const QString &filePath) const; Q_INVOKABLE QString openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const; Q_INVOKABLE QString temporaryFilename() const; diff --git a/src/qt/macoshelper.h b/src/qt/macoshelper.h index e8dabfbe..c8e86b05 100644 --- a/src/qt/macoshelper.h +++ b/src/qt/macoshelper.h @@ -29,6 +29,8 @@ #ifndef MACOSHELPER_H #define MACOSHELPER_H +#include + class MacOSHelper { MacOSHelper() {} @@ -36,6 +38,7 @@ class MacOSHelper public: static bool isCapsLock(); static bool openFolderAndSelectItem(const QUrl &path); + static QPixmap screenshot(); static QString bundlePath(); }; diff --git a/src/qt/macoshelper.mm b/src/qt/macoshelper.mm index 8f7b4b0f..1d3b812a 100644 --- a/src/qt/macoshelper.mm +++ b/src/qt/macoshelper.mm @@ -26,6 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include + #include #include #include @@ -37,6 +39,8 @@ #include #include +#include "ScopeGuard.h" + bool MacOSHelper::isCapsLock() { #ifdef __MAC_10_12 @@ -56,6 +60,41 @@ bool MacOSHelper::openFolderAndSelectItem(const QUrl &path) return true; } +QPixmap MacOSHelper::screenshot() +{ + std::unordered_set appWindowIds; + for (NSWindow *window in [NSApp windows]) + { + appWindowIds.insert((uintptr_t)[window windowNumber]); + } + + CFArrayRef onScreenWindows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID); + const auto onScreenWindowsClenaup = sg::make_scope_guard([&onScreenWindows]() { + CFRelease(onScreenWindows); + }); + + CFMutableArrayRef foreignWindows = CFArrayCreateMutable(NULL, CFArrayGetCount(onScreenWindows), NULL); + const auto foreignWindowsClenaup = sg::make_scope_guard([&foreignWindows]() { + CFRelease(foreignWindows); + }); + + for (CFIndex index = 0, count = CFArrayGetCount(onScreenWindows); index < count; ++index) + { + const uintptr_t windowId = reinterpret_cast(CFArrayGetValueAtIndex(onScreenWindows, index)); + if (appWindowIds.find(windowId) == appWindowIds.end()) + { + CFArrayAppendValue(foreignWindows, reinterpret_cast(windowId)); + } + } + + CGImageRef image = CGWindowListCreateImageFromArray(CGRectInfinite, foreignWindows, kCGWindowListOptionAll); + const auto imageClenaup = sg::make_scope_guard([&image]() { + CFRelease(image); + }); + + return QtMac::fromCGImageRef(image); +} + QString MacOSHelper::bundlePath() { NSBundle *main = [NSBundle mainBundle];