QR code feature beta and streamer mode
This commit is contained in:
parent
5bb95053fb
commit
13331ee5e7
|
@ -382,7 +382,20 @@ if(OPENVR)
|
||||||
add_definitions(-DVR_API_PUBLIC)
|
add_definitions(-DVR_API_PUBLIC)
|
||||||
add_definitions(-DOPENVR_BUILD_STATIC) # is this needed?
|
add_definitions(-DOPENVR_BUILD_STATIC) # is this needed?
|
||||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/openvr")
|
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/openvr")
|
||||||
message(STATUS "yeepp")
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/KDMacTouchBar")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WITH_SCANNER)
|
||||||
|
add_library(quirc STATIC
|
||||||
|
contrib/quirc/lib/decode.c
|
||||||
|
contrib/quirc/lib/identify.c
|
||||||
|
contrib/quirc/lib/quirc.c
|
||||||
|
contrib/quirc/lib/version_db.c
|
||||||
|
)
|
||||||
|
target_include_directories(quirc PUBLIC contrib/quirc/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
|
@ -89,7 +89,7 @@ RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git &&
|
||||||
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
|
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
|
||||||
cd zlib && \
|
cd zlib && \
|
||||||
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
|
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
|
||||||
CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar ./configure --static --prefix=/usr/x86_64-w64-mingw32 && \
|
CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar ./configure --static --prefix=/depends/x86_64-w64-mingw32 && \
|
||||||
make -j$THREADS && \
|
make -j$THREADS && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
@ -164,7 +164,7 @@ RUN git clone -b tor-0.4.5.7 --depth 1 https://git.torproject.org/tor.git && \
|
||||||
--enable-static-tor \
|
--enable-static-tor \
|
||||||
--with-libevent-dir=/usr/local/libevent \
|
--with-libevent-dir=/usr/local/libevent \
|
||||||
--with-openssl-dir=/usr/local/openssl \
|
--with-openssl-dir=/usr/local/openssl \
|
||||||
--with-zlib-dir=/usr/x86_64-w64-mingw32 \
|
--with-zlib-dir=/depends/x86_64-w64-mingw32 \
|
||||||
--disable-tool-name-check \
|
--disable-tool-name-check \
|
||||||
--enable-fatal-warnings \
|
--enable-fatal-warnings \
|
||||||
--prefix=/usr/local/tor \
|
--prefix=/usr/local/tor \
|
||||||
|
@ -174,7 +174,7 @@ RUN git clone -b tor-0.4.5.7 --depth 1 https://git.torproject.org/tor.git && \
|
||||||
rm -rf $(pwd) && \
|
rm -rf $(pwd) && \
|
||||||
strip -s -D /usr/local/tor/bin/tor.exe
|
strip -s -D /usr/local/tor/bin/tor.exe
|
||||||
|
|
||||||
RUN git clone https://git.featherwallet.org/feather/monero-seed.git && \
|
RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \
|
||||||
cd monero-seed && \
|
cd monero-seed && \
|
||||||
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
|
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
|
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
|
||||||
|
@ -183,3 +183,4 @@ RUN git clone https://git.featherwallet.org/feather/monero-seed.git && \
|
||||||
make -Cbuild -j$THREADS && \
|
make -Cbuild -j$THREADS && \
|
||||||
make -Cbuild install && \
|
make -Cbuild install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -52,7 +52,12 @@ release-static:
|
||||||
|
|
||||||
depends:
|
depends:
|
||||||
mkdir -p build/$(target)/release
|
mkdir -p build/$(target)/release
|
||||||
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DOPENVR=ON -DQML=ON -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Debug -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||||
|
|
||||||
|
windows:
|
||||||
|
mkdir -p build/$(target)/release
|
||||||
|
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DOPENVR=ON -DQML=ON -DWITH_SCANNER=ON -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Debug -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||||
|
|
||||||
|
|
||||||
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
||||||
windows-mxe-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
windows-mxe-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Set the minimum required version of CMake for this project.
|
|
||||||
cmake_minimum_required(VERSION 2.8)
|
|
||||||
|
|
||||||
# Set project name.
|
# Set project name.
|
||||||
project(OpenVRSDK)
|
project(OpenVRSDK)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ set(CMAKE_AUTOUIC ON)
|
||||||
# pthread
|
# pthread
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# PNG
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
|
||||||
# Compile these source files (.h/.cpp)
|
# Compile these source files (.h/.cpp)
|
||||||
file(GLOB SOURCE_FILES
|
file(GLOB SOURCE_FILES
|
||||||
"*.h"
|
"*.h"
|
||||||
|
@ -34,9 +38,9 @@ file(GLOB SOURCE_FILES
|
||||||
)
|
)
|
||||||
|
|
||||||
if(QML)
|
if(QML)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick Qml QuickControls2 QmlImportScanner)
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick Qml QuickControls2 QmlImportScanner Multimedia)
|
||||||
else()
|
else()
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets)
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Multimedia)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OPENVR)
|
if(OPENVR)
|
||||||
|
@ -57,6 +61,7 @@ add_subdirectory(libwalletqt)
|
||||||
add_subdirectory(model)
|
add_subdirectory(model)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(openpgp)
|
add_subdirectory(openpgp)
|
||||||
|
add_subdirectory(QR-Code-scanner)
|
||||||
|
|
||||||
qt5_add_resources(RESOURCES assets.qrc)
|
qt5_add_resources(RESOURCES assets.qrc)
|
||||||
|
|
||||||
|
@ -256,7 +261,22 @@ target_link_libraries(wowlet PUBLIC
|
||||||
${ICU_LIBRARIES}
|
${ICU_LIBRARIES}
|
||||||
openpgp
|
openpgp
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
${QRENCODE_LIBRARY})
|
${QRENCODE_LIBRARY}
|
||||||
|
qrdecoder
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link scanner
|
||||||
|
if(WITH_SCANNER)
|
||||||
|
target_link_libraries(wowlet PUBLIC qrscanner)
|
||||||
|
if(LINUX AND NOT ANDROID)
|
||||||
|
target_link_libraries(wowlet PUBLIC
|
||||||
|
jpeg
|
||||||
|
v4l2
|
||||||
|
v4lconvert
|
||||||
|
rt
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Link OpenVR
|
# Link OpenVR
|
||||||
if(OPENVR)
|
if(OPENVR)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
add_library(qrdecoder STATIC
|
||||||
|
Decoder.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(qrdecoder
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Gui
|
||||||
|
PNG::PNG
|
||||||
|
PRIVATE
|
||||||
|
quirc
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WITH_SCANNER)
|
||||||
|
add_library(qrscanner
|
||||||
|
QrCodeScanner.cpp
|
||||||
|
QrScanThread.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(qrscanner
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Multimedia
|
||||||
|
qrdecoder
|
||||||
|
)
|
||||||
|
endif()
|
|
@ -0,0 +1,359 @@
|
||||||
|
// 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
#include "Decoder.h"
|
||||||
|
#include "quirc.h"
|
||||||
|
|
||||||
|
|
||||||
|
QrDecoder::QrDecoder()
|
||||||
|
: m_qr(quirc_new())
|
||||||
|
{
|
||||||
|
if (m_qr == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("QUIRC: failed to allocate memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QrDecoder::~QrDecoder()
|
||||||
|
{
|
||||||
|
quirc_destroy(m_qr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> QrDecoder::decode(const QImage &image)
|
||||||
|
{
|
||||||
|
if (image.format() == QImage::Format_Grayscale8)
|
||||||
|
{
|
||||||
|
return decodeGrayscale8(image);
|
||||||
|
}
|
||||||
|
return decodeGrayscale8(image.convertToFormat(QImage::Format_Grayscale8));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> QrDecoder::decodePNG(QString pngPath) {
|
||||||
|
struct quirc *q;
|
||||||
|
std::vector<std::string> result;
|
||||||
|
auto pngPathStd = pngPath.toStdString();
|
||||||
|
auto pngPathCstr = pngPathStd.c_str();
|
||||||
|
|
||||||
|
q = quirc_new();
|
||||||
|
if (!q) {
|
||||||
|
qWarning() << "can't create quirc object";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = -1;
|
||||||
|
if (check_if_png(pngPathCstr)) {
|
||||||
|
status = load_png(q, pngPathCstr);
|
||||||
|
} else {
|
||||||
|
qWarning() << QString("Image is not a PNG: %1").arg(pngPath);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (status < 0) {
|
||||||
|
quirc_destroy(q);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
quirc_end(q);
|
||||||
|
auto count = quirc_count(q);
|
||||||
|
result.reserve(static_cast<size_t>(count));
|
||||||
|
|
||||||
|
for (int index = 0; index < count; ++index)
|
||||||
|
{
|
||||||
|
quirc_code code;
|
||||||
|
quirc_extract(q, index, &code);
|
||||||
|
|
||||||
|
quirc_data data;
|
||||||
|
const quirc_decode_error_t err = quirc_decode(&code, &data);
|
||||||
|
if (err == QUIRC_SUCCESS)
|
||||||
|
{
|
||||||
|
result.emplace_back(&data.payload[0], &data.payload[data.payload_len]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quirc_destroy(q);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I can't seem to get this function to work, we'll use dgbutil.h instead
|
||||||
|
std::vector<std::string> QrDecoder::decodeGrayscale8(const QImage &image)
|
||||||
|
{
|
||||||
|
if (quirc_resize(m_qr, image.width(), image.height()) < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("QUIRC: failed to allocate video memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *rawImage = quirc_begin(m_qr, nullptr, nullptr);
|
||||||
|
if (rawImage == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("QUIRC: failed to get image buffer");
|
||||||
|
}
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
std::copy(image.constBits(), image.constBits() + image.sizeInBytes(), rawImage);
|
||||||
|
#else
|
||||||
|
std::copy(image.constBits(), image.constBits() + image.byteCount(), rawImage);
|
||||||
|
#endif
|
||||||
|
quirc_end(m_qr);
|
||||||
|
|
||||||
|
const int count = quirc_count(m_qr);
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("QUIRC: failed to get the number of recognized QR-codes");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(static_cast<size_t>(count));
|
||||||
|
for (int index = 0; index < count; ++index)
|
||||||
|
{
|
||||||
|
quirc_code code;
|
||||||
|
quirc_extract(m_qr, index, &code);
|
||||||
|
|
||||||
|
quirc_data data;
|
||||||
|
const quirc_decode_error_t err = quirc_decode(&code, &data);
|
||||||
|
if (err == QUIRC_SUCCESS)
|
||||||
|
{
|
||||||
|
result.emplace_back(&data.payload[0], &data.payload[data.payload_len]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* quirc -- QR-code recognition library
|
||||||
|
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *data_type_str(int dt)
|
||||||
|
{
|
||||||
|
switch (dt) {
|
||||||
|
case QUIRC_DATA_TYPE_NUMERIC: return "NUMERIC";
|
||||||
|
case QUIRC_DATA_TYPE_ALPHA: return "ALPHA";
|
||||||
|
case QUIRC_DATA_TYPE_BYTE: return "BYTE";
|
||||||
|
case QUIRC_DATA_TYPE_KANJI: return "KANJI";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrDecoder::dump_data(const struct quirc_data *data)
|
||||||
|
{
|
||||||
|
printf(" Version: %d\n", data->version);
|
||||||
|
printf(" ECC level: %c\n", "MLHQ"[data->ecc_level]);
|
||||||
|
printf(" Mask: %d\n", data->mask);
|
||||||
|
printf(" Data type: %d (%s)\n",
|
||||||
|
data->data_type, data_type_str(data->data_type));
|
||||||
|
printf(" Length: %d\n", data->payload_len);
|
||||||
|
printf(" Payload: %s\n", data->payload);
|
||||||
|
|
||||||
|
if (data->eci)
|
||||||
|
printf(" ECI: %d\n", data->eci);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrDecoder::dump_cells(const struct quirc_code *code)
|
||||||
|
{
|
||||||
|
int u, v;
|
||||||
|
|
||||||
|
printf(" %d cells, corners:", code->size);
|
||||||
|
for (u = 0; u < 4; u++)
|
||||||
|
printf(" (%d,%d)", code->corners[u].x,
|
||||||
|
code->corners[u].y);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
for (v = 0; v < code->size; v++) {
|
||||||
|
printf(" ");
|
||||||
|
for (u = 0; u < code->size; u++) {
|
||||||
|
int p = v * code->size + u;
|
||||||
|
|
||||||
|
if (code->cell_bitmap[p >> 3] & (1 << (p & 7)))
|
||||||
|
printf("[]");
|
||||||
|
else
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hacked from https://dev.w3.org/Amaya/libpng/example.c
|
||||||
|
*
|
||||||
|
* Check if a file is a PNG image using png_sig_cmp(). Returns 1 if the given
|
||||||
|
* file is a PNG and 0 otherwise.
|
||||||
|
*/
|
||||||
|
#define PNG_BYTES_TO_CHECK 4
|
||||||
|
int QrDecoder::check_if_png(const char *filename)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
FILE *infile = NULL;
|
||||||
|
unsigned char buf[PNG_BYTES_TO_CHECK];
|
||||||
|
|
||||||
|
/* Open the prospective PNG file. */
|
||||||
|
if ((infile = fopen(filename, "rb")) == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Read in some of the signature bytes */
|
||||||
|
if (fread(buf, 1, PNG_BYTES_TO_CHECK, infile) != PNG_BYTES_TO_CHECK)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
|
||||||
|
* png_sig_cmp() returns zero if the image is a PNG and nonzero if it
|
||||||
|
* isn't a PNG.
|
||||||
|
*/
|
||||||
|
if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0)
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
out:
|
||||||
|
if (infile)
|
||||||
|
fclose(infile);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int QrDecoder::load_png(struct quirc *q, const char *filename)
|
||||||
|
{
|
||||||
|
int width, height, rowbytes, interlace_type, number_passes = 1;
|
||||||
|
png_uint_32 trns;
|
||||||
|
png_byte color_type, bit_depth;
|
||||||
|
png_structp png_ptr = NULL;
|
||||||
|
png_infop info_ptr = NULL;
|
||||||
|
FILE *infile = NULL;
|
||||||
|
uint8_t *image;
|
||||||
|
int ret = -1;
|
||||||
|
int pass;
|
||||||
|
|
||||||
|
if ((infile = fopen(filename, "rb")) == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if (!png_ptr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!info_ptr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
png_init_io(png_ptr, infile);
|
||||||
|
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
color_type = png_get_color_type(png_ptr, info_ptr);
|
||||||
|
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
interlace_type = png_get_interlace_type(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
// Read any color_type into 8bit depth, Grayscale format.
|
||||||
|
// See http://www.libpng.org/pub/png/libpng-manual.txt
|
||||||
|
|
||||||
|
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
|
||||||
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||||
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||||
|
|
||||||
|
if ((trns = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
|
||||||
|
png_set_tRNS_to_alpha(png_ptr);
|
||||||
|
|
||||||
|
if (bit_depth == 16)
|
||||||
|
#if PNG_LIBPNG_VER >= 10504
|
||||||
|
png_set_scale_16(png_ptr);
|
||||||
|
#else
|
||||||
|
png_set_strip_16(png_ptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((trns) || color_type & PNG_COLOR_MASK_ALPHA)
|
||||||
|
png_set_strip_alpha(png_ptr);
|
||||||
|
|
||||||
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||||
|
png_set_palette_to_rgb(png_ptr);
|
||||||
|
|
||||||
|
if (color_type == PNG_COLOR_TYPE_PALETTE ||
|
||||||
|
color_type == PNG_COLOR_TYPE_RGB ||
|
||||||
|
color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||||
|
png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interlace_type != PNG_INTERLACE_NONE)
|
||||||
|
number_passes = png_set_interlace_handling(png_ptr);
|
||||||
|
|
||||||
|
png_read_update_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
width = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
height = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
if (rowbytes != width) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"load_png: expected rowbytes to be %u but got %u\n",
|
||||||
|
width, rowbytes);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quirc_resize(q, width, height) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
image = quirc_begin(q, NULL, NULL);
|
||||||
|
|
||||||
|
for (pass = 0; pass < number_passes; pass++) {
|
||||||
|
int y;
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
png_bytep row_pointer = image + y * width;
|
||||||
|
png_read_rows(png_ptr, &row_pointer, NULL, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_read_end(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
out:
|
||||||
|
/* cleanup */
|
||||||
|
if (png_ptr) {
|
||||||
|
if (info_ptr)
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||||
|
else
|
||||||
|
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
|
||||||
|
}
|
||||||
|
if (infile)
|
||||||
|
fclose(infile);
|
||||||
|
return (ret);
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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 <QImage>
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
struct quirc;
|
||||||
|
|
||||||
|
class QrDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QrDecoder(const QrDecoder &) = delete;
|
||||||
|
QrDecoder &operator=(const QrDecoder &) = delete;
|
||||||
|
|
||||||
|
QrDecoder();
|
||||||
|
~QrDecoder();
|
||||||
|
|
||||||
|
std::vector<std::string> decode(const QImage &image);
|
||||||
|
std::vector<std::string> decodePNG(QString pngPath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Dump decoded information on stdout. */
|
||||||
|
void dump_data(const struct quirc_data *data);
|
||||||
|
|
||||||
|
/* Dump a grid cell map on stdout. */
|
||||||
|
void dump_cells(const struct quirc_code *code);
|
||||||
|
|
||||||
|
/* Check if a file is a PNG image.
|
||||||
|
*
|
||||||
|
* returns 1 if the given file is a PNG and 0 otherwise.
|
||||||
|
*/
|
||||||
|
int check_if_png(const char *filename);
|
||||||
|
|
||||||
|
/* Read a PNG image into the decoder.
|
||||||
|
*
|
||||||
|
* Note that you must call quirc_end() if the function returns
|
||||||
|
* successfully (0).
|
||||||
|
*/
|
||||||
|
int load_png(struct quirc *q, const char *filename);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> decodeGrayscale8(const QImage &image);
|
||||||
|
|
||||||
|
private:
|
||||||
|
quirc *m_qr;
|
||||||
|
};
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright (c) 2014-2019, 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 "QrCodeScanner.h"
|
||||||
|
#include <QVideoProbe>
|
||||||
|
#include <QCamera>
|
||||||
|
|
||||||
|
QrCodeScanner::QrCodeScanner(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_processTimerId(-1)
|
||||||
|
, m_processInterval(750)
|
||||||
|
, m_enabled(true)
|
||||||
|
{
|
||||||
|
m_probe = new QVideoProbe(this);
|
||||||
|
m_thread = new QrScanThread(this);
|
||||||
|
m_thread->start();
|
||||||
|
QObject::connect(m_thread, SIGNAL(decoded(QString)), this, SIGNAL(decoded(QString)));
|
||||||
|
QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool)));
|
||||||
|
connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
|
||||||
|
}
|
||||||
|
void QrCodeScanner::setSource(QCamera *camera)
|
||||||
|
{
|
||||||
|
m_probe->setSource(camera);
|
||||||
|
}
|
||||||
|
void QrCodeScanner::processFrame(QVideoFrame frame)
|
||||||
|
{
|
||||||
|
if(frame.isValid()){
|
||||||
|
m_curFrame = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool QrCodeScanner::enabled() const
|
||||||
|
{
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
void QrCodeScanner::setEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
m_enabled = enabled;
|
||||||
|
if(!enabled && (m_processTimerId != -1) )
|
||||||
|
{
|
||||||
|
this->killTimer(m_processTimerId);
|
||||||
|
m_processTimerId = -1;
|
||||||
|
}
|
||||||
|
else if (enabled && (m_processTimerId == -1) )
|
||||||
|
{
|
||||||
|
m_processTimerId = this->startTimer(m_processInterval);
|
||||||
|
}
|
||||||
|
emit enabledChanged();
|
||||||
|
}
|
||||||
|
void QrCodeScanner::timerEvent(QTimerEvent *event)
|
||||||
|
{
|
||||||
|
if( (event->timerId() == m_processTimerId) ){
|
||||||
|
m_thread->addFrame(m_curFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QrCodeScanner::~QrCodeScanner()
|
||||||
|
{
|
||||||
|
m_thread->stop();
|
||||||
|
m_thread->quit();
|
||||||
|
if(!m_thread->wait(5000))
|
||||||
|
{
|
||||||
|
m_thread->terminate();
|
||||||
|
m_thread->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) 2014-2019, 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.
|
||||||
|
|
||||||
|
#ifndef QRCODESCANNER_H_
|
||||||
|
#define QRCODESCANNER_H_
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include "QrScanThread.h"
|
||||||
|
|
||||||
|
class QVideoProbe;
|
||||||
|
class QCamera;
|
||||||
|
|
||||||
|
class QrCodeScanner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
QrCodeScanner(QObject *parent = Q_NULLPTR);
|
||||||
|
~QrCodeScanner();
|
||||||
|
void setSource(QCamera*);
|
||||||
|
|
||||||
|
bool enabled() const;
|
||||||
|
void setEnabled(bool enabled);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void processFrame(QVideoFrame);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void enabledChanged();
|
||||||
|
|
||||||
|
void decoded(const QString &data);
|
||||||
|
void notifyError(const QString &error, bool warning = false);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *);
|
||||||
|
QrScanThread *m_thread;
|
||||||
|
int m_processTimerId;
|
||||||
|
int m_processInterval;
|
||||||
|
int m_enabled;
|
||||||
|
QVideoFrame m_curFrame;
|
||||||
|
QVideoProbe *m_probe;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2014-2019, 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 "QrScanThread.h"
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
extern QImage qt_imageFromVideoFrame(const QVideoFrame &f);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QrScanThread::QrScanThread(QObject *parent)
|
||||||
|
: QThread(parent)
|
||||||
|
,m_running(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrScanThread::processQImage(const QImage &qimg)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (const std::string &code : m_decoder.decode(qimg))
|
||||||
|
{
|
||||||
|
emit decoded(QString::fromStdString(code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
qDebug() << "ERROR: " << e.what();
|
||||||
|
emit notifyError(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
|
||||||
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
processQImage( qt_imageFromVideoFrame(frame) );
|
||||||
|
#else
|
||||||
|
processQImage(frame.image());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrScanThread::stop()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
m_waitCondition.wakeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrScanThread::addFrame(const QVideoFrame &frame)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
m_queue.append(frame);
|
||||||
|
m_waitCondition.wakeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrScanThread::run()
|
||||||
|
{
|
||||||
|
QVideoFrame frame;
|
||||||
|
while(m_running) {
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
while(m_queue.isEmpty() && m_running)
|
||||||
|
m_waitCondition.wait(&m_mutex);
|
||||||
|
if(!m_queue.isEmpty())
|
||||||
|
processVideoFrame(m_queue.takeFirst());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) 2014-2019, 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.
|
||||||
|
|
||||||
|
#ifndef _QRSCANTHREAD_H_
|
||||||
|
#define _QRSCANTHREAD_H_
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include <QCamera>
|
||||||
|
|
||||||
|
#include "Decoder.h"
|
||||||
|
|
||||||
|
class QrScanThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QrScanThread(QObject *parent = Q_NULLPTR);
|
||||||
|
void addFrame(const QVideoFrame &frame);
|
||||||
|
virtual void stop();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void decoded(const QString &data);
|
||||||
|
void notifyError(const QString &error, bool warning = false);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void run();
|
||||||
|
void processVideoFrame(const QVideoFrame &);
|
||||||
|
void processQImage(const QImage &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QrDecoder m_decoder;
|
||||||
|
bool m_running;
|
||||||
|
QMutex m_mutex;
|
||||||
|
QWaitCondition m_waitCondition;
|
||||||
|
QList<QVideoFrame> m_queue;
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -89,8 +89,9 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
if(!this->configDirectory.endsWith('/'))
|
if(!this->configDirectory.endsWith('/'))
|
||||||
this->configDirectory = QString("%1/").arg(this->configDirectory);
|
this->configDirectory = QString("%1/").arg(this->configDirectory);
|
||||||
#endif
|
#endif
|
||||||
|
this->configDirectoryVR = QString("%1%2").arg(this->configDirectory, "vr");
|
||||||
|
|
||||||
// Config
|
// Create some directories
|
||||||
createConfigDirectory(this->configDirectory);
|
createConfigDirectory(this->configDirectory);
|
||||||
|
|
||||||
// if(this->cmdargs->isSet("stagenet"))
|
// if(this->cmdargs->isSet("stagenet"))
|
||||||
|
@ -545,8 +546,8 @@ void AppContext::onWSCCS(const QJsonArray &ccs_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::createConfigDirectory(const QString &dir) {
|
void AppContext::createConfigDirectory(const QString &dir) {
|
||||||
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
|
auto config_dir_tor = QString("%1%2").arg(dir).arg("tor");
|
||||||
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
|
auto config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
|
||||||
|
|
||||||
QStringList createDirs({dir, config_dir_tor, config_dir_tordata});
|
QStringList createDirs({dir, config_dir_tor, config_dir_tordata});
|
||||||
for(const auto &d: createDirs) {
|
for(const auto &d: createDirs) {
|
||||||
|
@ -556,6 +557,13 @@ void AppContext::createConfigDirectory(const QString &dir) {
|
||||||
throw std::runtime_error("Could not create directory " + d.toStdString());
|
throw std::runtime_error("Could not create directory " + d.toStdString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto config_dir_vr = QString("%1%2").arg(dir, "vr");
|
||||||
|
if(!Utils::dirExists(config_dir_vr)) {
|
||||||
|
qDebug() << QString("Creating directory: %1").arg(config_dir_vr);
|
||||||
|
if (!QDir().mkpath(config_dir_vr))
|
||||||
|
throw std::runtime_error("Could not create directory " + config_dir_vr.toStdString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) {
|
void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) {
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
QString accountName;
|
QString accountName;
|
||||||
QString configRoot;
|
QString configRoot;
|
||||||
QString configDirectory;
|
QString configDirectory;
|
||||||
|
QString configDirectoryVR;
|
||||||
QString defaultWalletDir;
|
QString defaultWalletDir;
|
||||||
QString defaultWalletDirRoot;
|
QString defaultWalletDirRoot;
|
||||||
QString tmpTxDescription;
|
QString tmpTxDescription;
|
||||||
|
|
|
@ -26,6 +26,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
{Config::donateBeg,{QS("donateBeg"), 1}},
|
{Config::donateBeg,{QS("donateBeg"), 1}},
|
||||||
{Config::skin,{QS("skin"), "light"}},
|
{Config::skin,{QS("skin"), "light"}},
|
||||||
{Config::openVRSkin,{QS("openVRSkin"), "default"}},
|
{Config::openVRSkin,{QS("openVRSkin"), "default"}},
|
||||||
|
{Config::openVRStreamerMode,{QS("openVRStreamerMode"), false}},
|
||||||
{Config::preferredFiatCurrency,{QS("preferredFiatCurrency"), "USD"}},
|
{Config::preferredFiatCurrency,{QS("preferredFiatCurrency"), "USD"}},
|
||||||
{Config::blockExplorer,{QS("blockExplorer"), "explore.wownero.com"}},
|
{Config::blockExplorer,{QS("blockExplorer"), "explore.wownero.com"}},
|
||||||
{Config::walletDirectory,{QS("walletDirectory"), ""}},
|
{Config::walletDirectory,{QS("walletDirectory"), ""}},
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
autoOpenWalletPath,
|
autoOpenWalletPath,
|
||||||
skin,
|
skin,
|
||||||
openVRSkin,
|
openVRSkin,
|
||||||
|
openVRStreamerMode,
|
||||||
preferredFiatCurrency,
|
preferredFiatCurrency,
|
||||||
blockExplorer,
|
blockExplorer,
|
||||||
walletDirectory,
|
walletDirectory,
|
||||||
|
|
|
@ -42,6 +42,12 @@ QByteArray Utils::fileOpen(const QString &path) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint64 Utils::fileModifiedAge(const QString &path) {
|
||||||
|
QFileInfo fileInfo;
|
||||||
|
fileInfo.setFile(path);
|
||||||
|
return (QDateTime::currentSecsSinceEpoch() - fileInfo.lastModified().toSecsSinceEpoch());
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray Utils::fileOpenQRC(const QString &path) {
|
QByteArray Utils::fileOpenQRC(const QString &path) {
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if(!file.open(QIODevice::ReadOnly)) {
|
if(!file.open(QIODevice::ReadOnly)) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
static bool fileExists(const QString &path);
|
static bool fileExists(const QString &path);
|
||||||
static QByteArray fileOpen(const QString &path);
|
static QByteArray fileOpen(const QString &path);
|
||||||
static QByteArray fileOpenQRC(const QString &path);
|
static QByteArray fileOpenQRC(const QString &path);
|
||||||
|
static qint64 fileModifiedAge(const QString &path);
|
||||||
static void desktopNotify(const QString &title, const QString &message, int duration);
|
static void desktopNotify(const QString &title, const QString &message, int duration);
|
||||||
static bool fileWrite(const QString &path, const QString &data);
|
static bool fileWrite(const QString &path, const QString &data);
|
||||||
static QStringList fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, int maxPerDir);
|
static QStringList fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, int maxPerDir);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QQmlComponent>
|
#include <QQmlComponent>
|
||||||
|
#include <QObject>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
@ -42,9 +43,15 @@ namespace wowletvr {
|
||||||
// turn on auto tx commits
|
// turn on auto tx commits
|
||||||
ctx->autoCommitTx = true;
|
ctx->autoCommitTx = true;
|
||||||
|
|
||||||
// write icon to disk so openvr overlay can refer to it
|
// QR code scanning from screenshots
|
||||||
|
m_qrScreenshotPreviewPath = ctx->configDirectoryVR + "/screenshot_preview";
|
||||||
|
m_qrScreenshotImagePath = ctx->configDirectoryVR + "/screenshot";
|
||||||
|
m_qrScreenshotTimer.setSingleShot(true);
|
||||||
|
connect(&m_qrScreenshotTimer, &QTimer::timeout, this, &WowletVR::onCheckQRScreenshot);
|
||||||
|
|
||||||
|
// write icon to disk so openvr overlay can use it
|
||||||
auto icon = ":/assets/images/wowlet.png";
|
auto icon = ":/assets/images/wowlet.png";
|
||||||
if (Utils::fileExists(icon)) {
|
if(Utils::fileExists(icon)) {
|
||||||
QFile f(icon);
|
QFile f(icon);
|
||||||
QFileInfo fileInfo(f);
|
QFileInfo fileInfo(f);
|
||||||
auto icon_path = QDir(ctx->configDirectory).filePath(fileInfo.fileName());
|
auto icon_path = QDir(ctx->configDirectory).filePath(fileInfo.fileName());
|
||||||
|
@ -140,10 +147,79 @@ namespace wowletvr {
|
||||||
|
|
||||||
themes[themeName] = map;
|
themes[themeName] = map;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
void WowletVR::takeQRScreenshot() {
|
||||||
|
if(m_qrScreenshotTimer.isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_controller->takeQRScreenshot(m_qrScreenshotPreviewPath, m_qrScreenshotImagePath);
|
||||||
|
m_qrScreenshotTimer.start(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WowletVR::onCheckQRScreenshot() {
|
||||||
|
qDebug() << "onCheckQRScreenshot()";
|
||||||
|
QString msg;
|
||||||
|
auto path = m_qrScreenshotPreviewPath + ".png";
|
||||||
|
auto pathPreview = m_qrScreenshotPreviewPath + "_inverted.png";
|
||||||
|
|
||||||
|
qDebug() << "path: " + path << " inverted: " + pathPreview;
|
||||||
|
|
||||||
|
if(!Utils::fileExists(path)) {
|
||||||
|
msg = "Screenshot was not saved to disk.";
|
||||||
|
qWarning() << msg;
|
||||||
|
emit qrScreenshotFailed(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto age = Utils::fileModifiedAge(path);
|
||||||
|
if (age >= 5) {
|
||||||
|
msg = "Screenshot on disk too old. Leftover from the last time?";
|
||||||
|
qWarning() << msg;
|
||||||
|
emit qrScreenshotFailed(msg);
|
||||||
|
QFile file (path);
|
||||||
|
file.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto results = m_qrDecoder.decodePNG(path);
|
||||||
|
auto result = wowletvr::WowletVR::checkQRScreenshotResults(results);
|
||||||
|
qDebug() << "no initial results";
|
||||||
|
if(result.isEmpty()) {
|
||||||
|
qDebug() << "trying to invert the image";
|
||||||
|
// lets try to invert the image
|
||||||
|
QImage image(path);
|
||||||
|
image.invertPixels();
|
||||||
|
image.save(pathPreview);
|
||||||
|
results = m_qrDecoder.decodePNG(pathPreview);
|
||||||
|
result = wowletvr::WowletVR::checkQRScreenshotResults(results);
|
||||||
|
if(!result.isEmpty()) {
|
||||||
|
qDebug() << "Found QR code after inverting the image.";
|
||||||
|
emit qrScreenshotSuccess(result);
|
||||||
|
QFile file (path);
|
||||||
|
file.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "QR code found.";
|
||||||
|
emit qrScreenshotSuccess(result);
|
||||||
|
QFile file (path);
|
||||||
|
file.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit qrScreenshotSuccess("No QR code could be detected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WowletVR::checkQRScreenshotResults(std::vector<std::string> results) {
|
||||||
|
auto results_count = results.size();
|
||||||
|
if(results_count == 1)
|
||||||
|
return QString::fromStdString(results[0]);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
WowletVR::~WowletVR() {
|
WowletVR::~WowletVR() {
|
||||||
// bla
|
// bla
|
||||||
|
int wegeg = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
#include <QtQml>
|
#include <QtQml>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QTimer>
|
||||||
#include <globals.h>
|
#include <globals.h>
|
||||||
|
|
||||||
#include "overlaycontroller.h"
|
#include "overlaycontroller.h"
|
||||||
#include "appcontext.h"
|
#include "appcontext.h"
|
||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
|
#include "QR-Code-scanner/Decoder.h"
|
||||||
|
|
||||||
namespace wowletvr {
|
namespace wowletvr {
|
||||||
|
|
||||||
|
@ -57,6 +59,14 @@ namespace wowletvr {
|
||||||
m_pClipboard->setText(text, QClipboard::Selection);
|
m_pClipboard->setText(text, QClipboard::Selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void setStreamerMode(bool status) {
|
||||||
|
config()->set(Config::openVRStreamerMode, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE bool getStreamerMode() {
|
||||||
|
return config()->get(Config::openVRStreamerMode).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
Q_INVOKABLE QString preferredFiat() {
|
Q_INVOKABLE QString preferredFiat() {
|
||||||
return config()->get(Config::preferredFiatCurrency).toString();
|
return config()->get(Config::preferredFiatCurrency).toString();
|
||||||
}
|
}
|
||||||
|
@ -78,14 +88,31 @@ namespace wowletvr {
|
||||||
return QString("~%1").arg(QString::number(conversionAmount, 'f', 2));
|
return QString("~%1").arg(QString::number(conversionAmount, 'f', 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void takeQRScreenshot();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void qrScreenshotFailed(QString error);
|
||||||
|
void qrScreenshotSuccess(QString address);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCheckQRScreenshot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AppContext *ctx;
|
AppContext *ctx;
|
||||||
QCommandLineParser *m_parser;
|
|
||||||
QQmlEngine m_engine;
|
QQmlEngine m_engine;
|
||||||
QQmlComponent *m_component;
|
QQmlComponent *m_component;
|
||||||
bool desktopMode = false;
|
|
||||||
wowletvr::OverlayController *m_controller;
|
wowletvr::OverlayController *m_controller;
|
||||||
|
|
||||||
|
bool desktopMode = false;
|
||||||
|
QString m_qrScreenshotPreviewPath;
|
||||||
|
QString m_qrScreenshotImagePath;
|
||||||
|
|
||||||
|
QCommandLineParser *m_parser;
|
||||||
QClipboard *m_pClipboard;
|
QClipboard *m_pClipboard;
|
||||||
|
QTimer m_qrScreenshotTimer;
|
||||||
|
QrDecoder m_qrDecoder;
|
||||||
|
|
||||||
|
static QString checkQRScreenshotResults(std::vector<std::string> results);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ Rectangle {
|
||||||
property var themes: {}
|
property var themes: {}
|
||||||
property string theme: "default"
|
property string theme: "default"
|
||||||
property string fiatSymbol: "USD"
|
property string fiatSymbol: "USD"
|
||||||
|
property bool streamerMode: false
|
||||||
signal initTheme();
|
signal initTheme();
|
||||||
|
|
||||||
// Components that have been dynamically created need to redraw
|
// Components that have been dynamically created need to redraw
|
||||||
|
@ -307,6 +308,7 @@ Rectangle {
|
||||||
// Start animating the background
|
// Start animating the background
|
||||||
gradientBackgroundTimer.start();
|
gradientBackgroundTimer.start();
|
||||||
|
|
||||||
|
// init some theme stuff
|
||||||
try {
|
try {
|
||||||
appWindow.themes = WowletVR.getThemes();
|
appWindow.themes = WowletVR.getThemes();
|
||||||
appWindow.theme = WowletVR.getCurrentTheme();
|
appWindow.theme = WowletVR.getCurrentTheme();
|
||||||
|
@ -329,9 +331,13 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appWindow.changeTheme(appWindow.theme);
|
appWindow.changeTheme(appWindow.theme);
|
||||||
appWindow.initTheme();
|
appWindow.initTheme();
|
||||||
|
|
||||||
|
// streamer mode enabled?
|
||||||
|
try {
|
||||||
|
appWindow.streamerMode = WowletVR.getStreamerMode();
|
||||||
|
} catch(err){}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeTheme(theme) {
|
function changeTheme(theme) {
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Window 2.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Dialogs 1.2
|
||||||
|
|
||||||
|
import "."
|
||||||
|
import "mock/Windows.js" as Windows
|
||||||
|
import "mock/Version.js" as Version
|
||||||
|
import "mock/NetworkType.js" as NetworkType
|
||||||
|
import "mock/Settings.js" as Settings
|
||||||
|
import "mock"
|
||||||
|
import "qml/common"
|
||||||
|
|
||||||
|
import "qml/."
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 1600
|
||||||
|
height: 800
|
||||||
|
color: "red"
|
||||||
|
|
||||||
|
property var currentWallet;
|
||||||
|
property bool disconnected: currentWallet ? currentWallet.disconnected : false
|
||||||
|
|
||||||
|
property WalletDashboard WalletDashboard: WalletDashboard {
|
||||||
|
stackView: walletView
|
||||||
|
}
|
||||||
|
|
||||||
|
property SendPage sendPage: SendPage {
|
||||||
|
stackView: walletView
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
property ReceivePage receivePage: ReceivePage {
|
||||||
|
stackView: walletView
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: walletView
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
pushEnter: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: walletView.width
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushExit: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: 0
|
||||||
|
to: -walletView.width
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popEnter: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: -walletView.width
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popExit: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: 0
|
||||||
|
to: walletView.width
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialItem: WalletDashboard
|
||||||
|
}
|
||||||
|
}
|
|
@ -459,4 +459,16 @@ const vr::VROverlayHandle_t& OverlayController::overlayThumbnailHandle() {
|
||||||
return m_ulOverlayThumbnailHandle;
|
return m_ulOverlayThumbnailHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayController::takeQRScreenshot(const QString &previewPath, const QString &imagePath) {
|
||||||
|
vr::IVRScreenshots *screen_taker = vr::VRScreenshots();
|
||||||
|
|
||||||
|
vr::ScreenshotHandle_t taker_handle;
|
||||||
|
screen_taker->RequestScreenshot(&taker_handle,
|
||||||
|
vr::EVRScreenshotType::VRScreenshotType_Mono,
|
||||||
|
previewPath.toStdString().c_str(),
|
||||||
|
imagePath.toStdString().c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace wowletvr
|
} // namespace wowletvr
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
#include "openvr_init.h"
|
#include "openvr_init.h"
|
||||||
#include "vr/utils/paths.h"
|
#include "vr/utils/paths.h"
|
||||||
#include "appcontext.h"
|
|
||||||
|
|
||||||
namespace application_strings
|
namespace application_strings
|
||||||
{
|
{
|
||||||
|
@ -59,6 +58,7 @@ public:
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
Q_INVOKABLE void exitApp();
|
Q_INVOKABLE void exitApp();
|
||||||
|
void takeQRScreenshot(const QString &previewPath, const QString &imagePath);
|
||||||
|
|
||||||
bool isDashboardVisible()
|
bool isDashboardVisible()
|
||||||
{
|
{
|
||||||
|
@ -117,8 +117,6 @@ private:
|
||||||
bool m_keyPressOneState = false;
|
bool m_keyPressOneState = false;
|
||||||
bool m_keyPressTwoState = false;
|
bool m_keyPressTwoState = false;
|
||||||
|
|
||||||
AppContext *m_ctx;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void renderOverlay();
|
void renderOverlay();
|
||||||
void OnRenderRequest();
|
void OnRenderRequest();
|
||||||
|
|
Binary file not shown.
|
@ -48,7 +48,7 @@ ColumnLayout {
|
||||||
Layout.rightMargin: 40
|
Layout.rightMargin: 40
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
fontSize: 21
|
fontSize: 21
|
||||||
text: "Shoutouts: matzman666, qvqc, ez, Gatto, RAGEHAÜZ, cisme, wowario, lza_menace, jwinterm, nioc, asymptotically, azy, selsta, kico, laura, thrmo, rottensox, solar, bl4sty, scoobybejesus"
|
text: "Shoutouts: matzman666, qvqc, ez, Gatto, RAGEHAÜZ, cisme, wowario, lza_menace, jwinterm, nioc, asymptotically, azy, selsta, kico, laura, thrmo, rottensox, GNVR, solar, bl4sty, scoobybejesus, Valve Corporation for OpenVR"
|
||||||
wrap: true
|
wrap: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,13 @@ ColumnLayout {
|
||||||
wrap: true
|
wrap: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: Style.dividerColor
|
||||||
|
height: 1
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 30
|
spacing: 30
|
||||||
|
|
||||||
|
@ -67,6 +74,43 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 30
|
||||||
|
|
||||||
|
MyText {
|
||||||
|
text: "Streamer mode (" + (appWindow.streamerMode ? "ON" : "OFF") + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 20
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 56
|
||||||
|
|
||||||
|
MyPushButton {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
enabled: !appWindow.streamerMode
|
||||||
|
opacity: !appWindow.streamerMode ? 1.0 : 0.3
|
||||||
|
text: "On"
|
||||||
|
onClicked: {
|
||||||
|
appWindow.streamerMode = !appWindow.streamerMode;
|
||||||
|
WowletVR.setStreamerMode(appWindow.streamerMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MyPushButton {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
enabled: appWindow.streamerMode
|
||||||
|
opacity: appWindow.streamerMode ? 1.0 : 0.3
|
||||||
|
text: "Off"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
appWindow.streamerMode = !appWindow.streamerMode;
|
||||||
|
WowletVR.setStreamerMode(appWindow.streamerMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|
|
@ -108,7 +108,12 @@ Rectangle {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
fontSize: 30
|
fontSize: 30
|
||||||
fontBold: true
|
fontBold: true
|
||||||
text: appWindow.balanceFormatted
|
text: {
|
||||||
|
if(!appWindow.streamerMode)
|
||||||
|
return appWindow.balanceFormatted;
|
||||||
|
else
|
||||||
|
return "HIDDEN";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +241,10 @@ Rectangle {
|
||||||
text: {
|
text: {
|
||||||
let rtn = "Balance: ";
|
let rtn = "Balance: ";
|
||||||
try {
|
try {
|
||||||
rtn += WowletVR.wowToFiat(appWindow.spendable);
|
if(!appWindow.streamerMode)
|
||||||
|
rtn += WowletVR.wowToFiat(appWindow.spendable);
|
||||||
|
else
|
||||||
|
rtn += "HIDDEN";
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
rtn += "ERROR";
|
rtn += "ERROR";
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,22 +9,25 @@ ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
|
property bool takingScreenshot: false
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
MyText {
|
MyText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrap: true
|
wrap: true
|
||||||
fontColor: Style.fontColorBright
|
fontColor: Style.fontColorBright
|
||||||
text: "Look at a QR code in VR and take a screenshot."
|
text: "Look at a QR code and press the button below to take a screenshot. Note: make sure to look at the center of the QR code. The parser works best with simple, straight-forward QR codes. When using more complex QR codes, make sure to properly fill your screen with the QR code itself (plus some margins)."
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: continueButton
|
id: continueButton
|
||||||
text: "Take in-game screenshot"
|
text: "Take in-game screenshot"
|
||||||
Layout.preferredWidth: 490
|
Layout.preferredWidth: 490
|
||||||
|
opacity: takingScreenshot ? 0.0 : 1.0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// QR thingy
|
root.takingScreenshot = true;
|
||||||
|
WowletVR.takeQRScreenshot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,11 +45,36 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: WowletVR
|
||||||
|
|
||||||
|
function onQrScreenshotSuccess(address) {
|
||||||
|
root.takingScreenshot = false;
|
||||||
|
console.log("onPinLookupReceived", address);
|
||||||
|
if(!address.startsWith("wownero:")) {
|
||||||
|
messagePopup.showMessage("Invalid QR code", "QR data did not start with \"wownero:\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sendStateView.currentView === sendStateView.qrPage) {
|
||||||
|
sendStateController.destinationAddress = address.slice(8);
|
||||||
|
sendStateView.state = "transferPage";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onQrScreenshotFailed(msg) {
|
||||||
|
root.takingScreenshot = false;
|
||||||
|
console.log("onQrScreenshotFailed", msg);
|
||||||
|
messagePopup.showMessage("QR scan failure", msg)
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
|
root.takingScreenshot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPageCompleted(previousView){
|
function onPageCompleted(previousView){
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue