Browse Source

Development

pull/45/head
dsc 4 months ago
parent
commit
c0cb90bf79
  1. 30
      Dockerfile.windows
  2. 8
      contrib/openvr/src/vrcommon/pathtools_public.cpp
  3. 4
      contrib/openvr/src/vrcommon/vrpathregistry_public.cpp
  4. 71
      src/appcontext.cpp
  5. 12
      src/appcontext.h
  6. 42
      src/mainwindow.ui
  7. 7
      src/model/TransactionHistoryModel.cpp
  8. 1
      src/model/TransactionHistoryModel.h
  9. 5
      src/utils/prices.cpp
  10. 6
      src/utils/wsclient.cpp
  11. 4
      src/utils/wsclient.h
  12. 30
      src/vr/main.cpp
  13. 31
      src/vr/main.qml
  14. 13
      src/vr/openvr_init.cpp
  15. 42
      src/vr/overlaycontroller.cpp
  16. 7
      src/vr/overlaycontroller.h
  17. 7
      src/vr/qml.qrc
  18. 13
      src/vr/qml/AboutPage.qml
  19. 12
      src/vr/qml/CreateWalletDialog.qml
  20. 82
      src/vr/qml/common/MyStackViewPage.qml
  21. 16
      src/vr/qml/common/MyTextField.qml
  22. 0
      src/vr/qml/dashboard/CreateWallet.qml
  23. 0
      src/vr/qml/dashboard/HelpPage.qml
  24. 157
      src/vr/qml/wallet/HistoryTable.qml
  25. 374
      src/vr/qml/wallet/ReceivePage.qml
  26. 5
      src/vr/qml/wallet/WalletDashBoardPage.qml
  27. 10
      src/vr/qml/wallet/send/SendPage.qml
  28. 72
      src/vr/qml/wallet/send/SendPagePIN.qml
  29. 7
      src/vr/qml/wallet/send/SendPageTransfer.qml
  30. 1
      src/widgets/suchwowwidget.cpp
  31. 3
      src/widgets/suchwowwidget.h
  32. 2
      src/wizard/walletwizard.cpp

30
Dockerfile.windows

@ -8,7 +8,7 @@ ENV OPENSSL_ROOT_DIR=/usr/local/openssl/
ENV TOR_BIN=/usr/local/tor/bin/tor.exe
RUN apt update && \
DEBIAN_FRONTEND=noninteractive apt install -y curl wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \
DEBIAN_FRONTEND=noninteractive apt install -y curl nano wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \
python && \
rm -rf /var/lib/apt/lists/*
@ -27,8 +27,12 @@ RUN make -j$THREADS -C /depends HOST=x86_64-w64-mingw32 NO_QT=1
RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
cd qt5 && \
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtdeclarative.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtgraphicaleffects.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols2.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
@ -38,8 +42,8 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \
-device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
-I $(pwd)/qtbase/src/3rdparty/angle/include \
-opensource -confirm-license -release -static -static-runtime -no-opengl \
-no-avx -openssl -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
-opensource -confirm-license -release -static -static-runtime -opengl dynamic -no-angle \
-no-feature-qml-worker-script -no-avx -openssl -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
-qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
-skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras \
-skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc \
@ -47,7 +51,6 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport \
-skip qtspeech -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel \
-skip qtwebengine -skip qtwebview -skip qtwinextras -skip qtx11extras \
-skip qtdeclarative -skip qtquickcontrols -skip qtquickcontrols2 \
-skip serialbus -skip webengine \
-nomake examples -nomake tests -nomake tools && \
make -j$THREADS && \
@ -94,7 +97,7 @@ RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
# libpng -> libqrencode
RUN git clone -b libpng16 --depth 1 https://github.com/glennrp/libpng.git && \
cd libpng && \
git reset --hard dbe3e0c43e549a1602286144d94b0666549b18e6 && \
git reset --hard a37d4836519517bdce6cb9d956092321eca3e73b && \
CPPFLAGS="-I/depends/x86_64-w64-mingw32/include" LDFLAGS="-L/depends/x86_64-w64-mingw32/lib" \
./configure --host=x86_64-w64-mingw32 --prefix=/depends/x86_64-w64-mingw32 && \
make -j$THREADS && \
@ -122,11 +125,11 @@ RUN wget https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz && \
rm -rf $(pwd)
# OpenSSL -> Tor
RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
echo "e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242 openssl-1.1.1i.tar.gz" | sha256sum -c && \
tar -xzf openssl-1.1.1i.tar.gz && \
rm openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \
RUN wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz && \
echo "892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 openssl-1.1.1k.tar.gz" | sha256sum -c && \
tar -xzf openssl-1.1.1k.tar.gz && \
rm openssl-1.1.1k.tar.gz && \
cd openssl-1.1.1k && \
./Configure mingw64 no-shared no-dso --cross-compile-prefix=x86_64-w64-mingw32- --prefix=/usr/local/openssl && \
make -j$THREADS && \
make -j$THREADS install_sw && \
@ -146,10 +149,9 @@ RUN wget https://github.com/libevent/libevent/releases/download/release-2.1.11-s
make -j$THREADS install && \
rm -rf $(pwd)
ENV TOR_VERSION=0.4.5.5-rc
RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git && \
RUN git clone -b tor-0.4.5.7 --depth 1 https://git.torproject.org/tor.git && \
cd tor && \
git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \
git reset --hard 83f895c015de55201e5f226f84a866f30f5ee14b && \
./autogen.sh && \
./configure --host=x86_64-w64-mingw32 \
--disable-asciidoc \
@ -172,7 +174,7 @@ RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git &&
rm -rf $(pwd) && \
strip -s -D /usr/local/tor/bin/tor.exe
RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \
RUN git clone https://git.featherwallet.org/feather/monero-seed.git && \
cd monero-seed && \
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \

8
contrib/openvr/src/vrcommon/pathtools_public.cpp

@ -187,7 +187,7 @@ bool Path_IsAbsolute( const std::string & sPath )
if( sPath.empty() )
return false;
#if defined( WIN32 )
#ifdef _WIN32
if ( sPath.size() < 3 ) // must be c:\x or \\x at least
return false;
@ -515,7 +515,7 @@ bool Path_Exists( const std::string & sPath )
if( sFixedPath.empty() )
return false;
#if defined( WIN32 )
#ifdef _WIN32
struct _stat buf;
std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
@ -886,7 +886,7 @@ std::string Path_UrlToFilePath( const std::string & sFileUrl )
// -----------------------------------------------------------------------------------------------------
std::string GetUserDocumentsPath()
{
#if defined( WIN32 )
#ifdef _WIN32
WCHAR rwchPath[MAX_PATH];
if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
@ -925,7 +925,7 @@ std::string GetUserDocumentsPath()
// -----------------------------------------------------------------------------------------------------
bool Path_UnlinkFile( const std::string &strFilename )
{
#if defined( WIN32 )
#ifdef _WIN32
std::wstring wsFilename = UTF8to16( strFilename.c_str() );
return ( 0 != DeleteFileW( wsFilename.c_str() ) );
#else

4
contrib/openvr/src/vrcommon/vrpathregistry_public.cpp

@ -7,7 +7,7 @@
#include <vrcommon/strtools_public.h>
#include <vrcommon/dirtools_public.h>
#if defined( WIN32 )
#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
@ -36,7 +36,7 @@
/** Returns the root of the directory the system wants us to store user config data in */
static std::string GetAppSettingsPath()
{
#if defined( WIN32 )
#ifdef _WIN32
WCHAR rwchPath[MAX_PATH];
if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )

71
src/appcontext.cpp

@ -21,12 +21,14 @@ TxFiatHistory *AppContext::txFiatHistory = nullptr;
double AppContext::balance = 0;
QMap<QString, QString> AppContext::txDescriptionCache;
QMap<QString, QString> AppContext::txCache;
bool AppContext::isQML = false;
AppContext::AppContext(QCommandLineParser *cmdargs) {
this->m_walletKeysFilesModel = new WalletKeysFilesModel(this, this);
this->network = new QNetworkAccessManager();
this->networkClearnet = new QNetworkAccessManager();
this->cmdargs = cmdargs;
AppContext::isQML = false;
#if defined(Q_OS_MAC)
this->isTorSocks = qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0;
@ -105,7 +107,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy
this->ws = new WSClient(this, m_wsUrl);
this->ws = new WSClient(this, wsUrl);
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected);
connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected);
@ -163,11 +165,10 @@ void AppContext::initTor() {
this->tor = new Tor(this, this);
this->tor->start();
if (!(isWhonix)) {
if (!isWhonix && wsUrl.contains(".onion")) {
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
if (m_wsUrl.host().endsWith(".onion"))
this->ws->webSocket.setProxy(*networkProxy);
this->ws->webSocket.setProxy(*networkProxy);
}
}
@ -409,7 +410,7 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
emit blockHeightWSUpdated(this->heights);
}
else if(cmd == "nodes") {
else if(cmd == "rpc_nodes") {
this->onWSNodes(msg.value("data").toArray());
}
#if defined(HAS_XMRIG)
@ -431,7 +432,7 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
this->onWSReddit(reddit_data);
}
else if(cmd == "wfs") {
else if(cmd == "funding_proposals") {
auto ccs_data = msg.value("data").toArray();
this->onWSCCS(ccs_data);
}
@ -445,6 +446,23 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
auto txFiatHistory_data = msg.value("data").toObject();
AppContext::txFiatHistory->onWSData(txFiatHistory_data);
}
#if defined(HAS_OPENVR)
else if(cmd == "requestPIN") {
auto pin = msg.value("data").toString();
emit pinReceived(pin);
}
else if(cmd == "lookupPIN") {
auto lookup_data = msg.value("data").toObject();
auto address = lookup_data.value("address").toString();
auto pin = lookup_data.value("PIN").toString();
if(address.isEmpty())
emit pinLookupErrorReceived();
else
emit pinLookupReceived(address, pin);
}
#endif
}
void AppContext::onWSNodes(const QJsonArray &nodes) {
@ -813,6 +831,47 @@ void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
emit createTransactionSuccess(tx, address);
}
#if defined(HAS_OPENVR)
void AppContext::onAskReceivingPIN() {
// request new receiving PIN from wowlet-backend
if(this->currentWallet == nullptr)
return;
auto address = this->currentWallet->address(0, 1);
QString signature = this->currentWallet->signMessage(address, false, address);
QJsonObject data;
data["signature"] = signature;
data["address"] = address;
QJsonObject obj;
obj["cmd"] = "requestPIN";
obj["data"] = data;
QJsonDocument doc = QJsonDocument(obj);
this->ws->sendMsg(doc.toJson(QJsonDocument::Compact));
}
void AppContext::onLookupReceivingPIN(QString pin) {
// lookup PIN -> address
if(this->currentWallet == nullptr)
return;
auto address = this->currentWallet->address(0, 1);
QString signature = this->currentWallet->signMessage(address, false, address);
QJsonObject data;
data["PIN"] = pin;
QJsonObject obj;
obj["cmd"] = "lookupPIN";
obj["data"] = data;
QJsonDocument doc = QJsonDocument(obj);
this->ws->sendMsg(doc.toJson(QJsonDocument::Compact));
}
#endif
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
this->currentWallet->history()->refresh(this->currentWallet->currentSubaddressAccount());
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());

12
src/appcontext.h

@ -57,6 +57,8 @@ public:
QString defaultWalletDir;
QString defaultWalletDirRoot;
QString tmpTxDescription;
QString wsUrl = "51.195.148.161:1338";
// QString wsUrl = "feathercitimllbmdktu6cmjo3fizgmyfrntntqzu6xguqa2rlq5cgid.onion";
QString walletPath;
QString walletPassword = "";
@ -91,6 +93,7 @@ public:
static QMap<QString, QString> txDescriptionCache;
static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory;
static bool isQML;
// libwalletqt
bool refreshed = false;
@ -111,7 +114,7 @@ public:
void setWindowTitle(bool mining = false);
// Closes the currently opened wallet
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
Q_INVOKABLE void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
void storeWallet();
Q_INVOKABLE QVariantList listWallets() {
@ -137,6 +140,8 @@ public slots:
void onOpenAliasResolve(const QString &openAlias);
void onSetRestoreHeight(quint64 height);
void onPreferredFiatCurrencyChanged(const QString &symbol);
Q_INVOKABLE void onAskReceivingPIN();
Q_INVOKABLE void onLookupReceivingPIN(QString pin);
private slots:
void onWSNodes(const QJsonArray &nodes);
@ -186,6 +191,9 @@ signals:
void suchWowUpdated(const QJsonArray &such_data);
void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data);
void pinLookupReceived(QString address, QString pin);
void pinLookupErrorReceived();
void pinReceived(QString pin);
void setCustomNodes(QList<WowletNode> nodes);
void openAliasResolveError(const QString &msg);
void openAliasResolved(const QString &address, const QString &openAlias);
@ -201,8 +209,6 @@ private:
WalletKeysFilesModel *m_walletKeysFilesModel;
const int m_donationBoundary = 15;
QTimer m_storeTimer;
// @TODO: Replace url
QUrl m_wsUrl = QUrl(QStringLiteral("ws://feathercitimllbmdktu6cmjo3fizgmyfrntntqzu6xguqa2rlq5cgid.onion/ws"));
};
#endif //WOWLET_APPCONTEXT_H

42
src/mainwindow.ui

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1156</width>
<height>496</height>
<height>502</height>
</rect>
</property>
<property name="sizePolicy">
@ -101,11 +101,11 @@
<property name="documentMode">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab_3">
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>SuchWow</string>
<string>/r/Wownero</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
@ -119,22 +119,15 @@
<number>0</number>
</property>
<item>
<widget class="SuchWowWidget" name="suchWowWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>320</height>
</size>
</property>
</widget>
<widget class="RedditWidget" name="redditWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>WFS</string>
<string>SuchWow</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
@ -148,15 +141,22 @@
<number>0</number>
</property>
<item>
<widget class="CCSWidget" name="ccsWidget" native="true"/>
<widget class="SuchWowWidget" name="suchWowWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>320</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<widget class="QWidget" name="tab">
<attribute name="title">
<string>/r/Wownero</string>
<string>WFS</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
@ -170,7 +170,7 @@
<number>0</number>
</property>
<item>
<widget class="RedditWidget" name="redditWidget" native="true"/>
<widget class="CCSWidget" name="ccsWidget" native="true"/>
</item>
</layout>
</widget>
@ -326,7 +326,7 @@
<x>0</x>
<y>0</y>
<width>1156</width>
<height>30</height>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">

7
src/model/TransactionHistoryModel.cpp

@ -51,7 +51,12 @@ int TransactionHistoryModel::columnCount(const QModelIndex &parent) const {
return 0;
}
return TransactionInfoRole::COUNT;
// When wowlet is in QtWidgets mode, it will only use the first 5 columns,
// the rest should be hidden, because it shows in the GUI. So by default we'll
// use 5 as column count. When in QtQuick (QML) mode, we want to expose more columns
// so we can change the column count here.
return AppContext::isQML ? this->COUNT : 5;
}
QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const {

1
src/model/TransactionHistoryModel.h

@ -50,6 +50,7 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
int customColumnCount = 5;
signals:
void transactionHistoryChanged();

5
src/utils/prices.cpp

@ -85,9 +85,8 @@ double Prices::convert(const QString &symbolFrom, const QString &symbolTo, doubl
}
void Prices::fiatPricesReceived(const QJsonObject &data) {
QJsonObject rates = data.value("rates").toObject();
for(const auto &currency: fiat.keys())
if(rates.contains(currency))
this->rates.insert(currency, rates.value(currency).toDouble());
if(data.contains(currency))
this->rates.insert(currency, data.value(currency).toDouble());
emit fiatPricesUpdated();
}

6
src/utils/wsclient.cpp

@ -8,16 +8,16 @@
#include "wsclient.h"
#include "appcontext.h"
WSClient::WSClient(AppContext *ctx, const QUrl &url, QObject *parent) :
WSClient::WSClient(AppContext *ctx, const QString &url, QObject *parent) :
QObject(parent),
url(url),
m_ctx(ctx) {
connect(&this->webSocket, &QWebSocket::binaryMessageReceived, this, &WSClient::onbinaryMessageReceived);
connect(&this->webSocket, &QWebSocket::connected, this, &WSClient::onConnected);
connect(&this->webSocket, &QWebSocket::disconnected, this, &WSClient::closed);
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
m_tor = url.host().endsWith(".onion");
m_tor = url.contains(".onion");
this->url = QString("ws://%1/ws").arg(url);
// Keep websocket connection alive
connect(&m_pingTimer, &QTimer::timeout, [this]{

4
src/utils/wsclient.h

@ -14,11 +14,11 @@ class WSClient : public QObject
Q_OBJECT
public:
explicit WSClient(AppContext *ctx, const QUrl &url, QObject *parent = nullptr);
explicit WSClient(AppContext *ctx, const QString &url, QObject *parent = nullptr);
void start();
void sendMsg(const QByteArray &data);
QWebSocket webSocket;
QUrl url;
QString url;
signals:
void closed();

30
src/vr/main.cpp

@ -10,6 +10,7 @@
#include <QtGui>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QFileInfo>
#include <QQuickView>
#include <QQuickItem>
@ -32,13 +33,24 @@ namespace wowletvr {
WowletVR::WowletVR(AppContext *ctx, QCommandLineParser *parser, QObject *parent) :
QObject(parent), ctx(ctx), m_parser(parser) {
desktopMode = m_parser->isSet("openvr-debug");
AppContext::isQML = true;
// write icon to disk so openvr overlay can refer to it
auto icon = ":/assets/images/wowlet.png";
if (Utils::fileExists(icon)) {
QFile f(icon);
QFileInfo fileInfo(f);
auto icon_path = QDir(ctx->configDirectory).filePath(fileInfo.fileName());
f.copy(icon_path);
f.close();
}
#ifdef Q_OS_WIN
if(desktopMode)
qputenv("QMLSCENE_DEVICE", "softwarecontext");
#endif
qDebug() << "QMLSCENE_DEVICE: " << qgetenv("QMLSCENE_DEVICE");
qInfo() << "OPENSSL VERSION: " << QSslSocket::sslLibraryBuildVersionString();
m_engine.rootContext()->setContextProperty("homePath", QDir::homePath());
m_engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
@ -67,17 +79,20 @@ namespace wowletvr {
// QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL );
// QCoreApplication::setAttribute( Qt::AA_Use96Dpi );
if(!desktopMode) {
if(!openvr_init::initializeOpenVR(openvr_init::OpenVrInitializationType::Overlay))
throw std::runtime_error("Error: initializeOpenVR()");
m_controller = new wowletvr::OverlayController(desktopMode, m_engine);
m_engine.rootContext()->setContextProperty("OverlayController", m_controller);
}
auto widgetUrl = QUrl(QStringLiteral("qrc:///main"));
m_component = new QQmlComponent(&m_engine, widgetUrl);
this->errors = m_component->errors();
for (auto &e : this->errors)
qCritical() << "QML Error: " << e.toString().toStdString().c_str();
if(!desktopMode) {
openvr_init::initializeOpenVR(openvr_init::OpenVrInitializationType::Overlay);
m_controller = new wowletvr::OverlayController(desktopMode, m_engine);
}
}
void WowletVR::render() {
@ -97,7 +112,8 @@ namespace wowletvr {
return;
}
m_controller->SetWidget(quickObjItem, displayName, appKey);
auto iconPath = ctx->configDirectory + "/wowlet.png";
m_controller->SetWidget(quickObjItem, displayName, appKey, iconPath.toStdString());
}
WowletVR::~WowletVR() {

31
src/vr/main.qml

@ -7,11 +7,6 @@ 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/."
@ -27,10 +22,10 @@ Rectangle {
property var currentWallet;
property bool disconnected: currentWallet ? currentWallet.disconnected : false
property string walletTitle: "lol123"
property string walletTitle: "long wallet name"
property string walletPath: ""
property string statusText: "Idle"
property string balanceFormatted: "Balance: 25928.9543 WOW (+3902.32 WOW unconfirmed)"
property string balanceFormatted: "Balance: 25928.9543 WOW"
property bool wsConnected: false
property int connectionStatus: Wallet.ConnectionStatus_Disconnected;
@ -60,9 +55,9 @@ Rectangle {
MyDialogOkCancelPopup {
id: enterPasswordDialog
dialogTitle: "Enter Password"
dialogTitle: "Enter Wallet Password"
dialogWidth: 700
dialogHeight: 400
dialogHeight: 380
dialogContentItem: ColumnLayout {
RowLayout {
@ -76,7 +71,7 @@ Rectangle {
MyTextField {
id: walletOpenPassword
keyBoardUID: 590
keyBoardUID: 591
color: "#cccccc"
text: ""
Layout.fillWidth: true
@ -105,9 +100,10 @@ Rectangle {
id: createWalletDialog
dialogTitle: "Create New Wallet"
dialogWidth: 700
dialogHeight: 400
dialogHeight: 440
dialogContentItem: ColumnLayout {
spacing: 10
RowLayout {
Layout.topMargin: 16
Layout.leftMargin: 16
@ -141,7 +137,7 @@ Rectangle {
MyTextField {
id: newWalletPassword
keyBoardUID: 591
keyBoardUID: 592
color: "#cccccc"
text: ""
Layout.fillWidth: true
@ -153,7 +149,10 @@ Rectangle {
}
MyText {
Layout.topMargin: 20
Layout.leftMargin: 16
fontSize: 16
fontColor: "#cccccc"
text: "The password field is optional."
}
@ -253,19 +252,11 @@ Rectangle {
}
// function onWalletOpened(Wallet *wallet) {
// currentWallet.heightRefreshed.connect(onHeightRefreshed);
// currentWallet.refreshed.connect(onWalletRefresh)
// currentWallet.updated.connect(onWalletUpdate)
// currentWallet.newBlock.connect(onWalletNewBlock)
// currentWallet.moneySpent.connect(onWalletMoneySent)
// currentWallet.moneyReceived.connect(onWalletMoneyReceived)
// currentWallet.unconfirmedMoneyReceived.connect(onWalletUnconfirmedMoneyReceived)
// currentWallet.transactionCreated.connect(onTransactionCreated)
// currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
// currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
// currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
// currentWallet.walletPassphraseNeeded.connect(onWalletPassphraseNeededWallet);
// currentWallet.transactionCommitted.connect(onTransactionCommitted);
// middlePanel.paymentClicked.connect(handlePayment);

13
src/vr/openvr_init.cpp

@ -6,7 +6,12 @@
#include <openvr.h>
#include <QDebug>
#include <QMessageBox>
#include "openvr_init.h"
#include "utils/utils.h"
#include <openvr/src/vrcommon/vrpathregistry_public.h>
#include <openvr/src/vrcommon/pathtools_public.h>
namespace openvr_init
{
@ -34,6 +39,14 @@ bool initializeProperly(const OpenVrInitializationType initType) {
bool initializeOpenVR(const OpenVrInitializationType initType)
{
QString vr_pathreg_override = qgetenv("VR_PATHREG_OVERRIDE");
if(!vr_pathreg_override.isEmpty()) {
if(Utils::fileExists(vr_pathreg_override)) {
qCritical() << "Filepath supplied in VR_PATHREG_OVERRIDE not found. Does this path exist?";
return false;
}
}
bool res = initializeProperly(initType);
if(!res)
return false;

42
src/vr/overlaycontroller.cpp

@ -81,27 +81,6 @@ OverlayController::OverlayController(bool desktopMode, QQmlEngine& qmlEngine) :
// Set qml context
qmlEngine.rootContext()->setContextProperty("applicationVersion", "1337");
qmlEngine.rootContext()->setContextProperty("vrRuntimePath", getVRRuntimePathUrl());
// Pretty disgusting trick to allow qmlRegisterSingletonType to continue
// working with the lambdas that were already there. The callback function
// in qmlRegisterSingletonType won't work with any lambdas that capture the
// environment. The alternative to making a static pointer to this was
// rewriting all QML to not be singletons, which should probably be done
// whenever possible.
static OverlayController* const objectAddress = this;
constexpr auto qmlSingletonImportName = "ovrwow.wowletvr";
qmlRegisterSingletonType<OverlayController>(
qmlSingletonImportName,
1,
0,
"OverlayController",
[]( QQmlEngine*, QJSEngine* ) {
QObject* obj = objectAddress;
QQmlEngine::setObjectOwnership( obj, QQmlEngine::CppOwnership );
return obj;
});
qInfo() << "OPENSSL VERSION: " << QSslSocket::sslLibraryBuildVersionString();
}
OverlayController::~OverlayController() {
@ -138,9 +117,7 @@ void OverlayController::Shutdown() {
m_pFbo.reset();
}
void OverlayController::SetWidget( QQuickItem* quickItem,
const std::string& name,
const std::string& key )
void OverlayController::SetWidget(QQuickItem* quickItem, const std::string& name, const std::string& key, const std::string& iconPath)
{
if ( !m_desktopMode )
{
@ -171,14 +148,9 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
vr::VROverlayFlags_SendVRSmoothScrollEvents,
true );
constexpr auto thumbiconFilename = "img/icons/thumbicon.png";
const auto thumbIconPath = paths::binaryDirectoryFindFile( thumbiconFilename );
if ( !thumbIconPath.empty() ) {
vr::VROverlay()->SetOverlayFromFile( m_ulOverlayThumbnailHandle, thumbIconPath.c_str() );
}
else {
qCritical() << "Could not find thumbnail icon \"" << thumbiconFilename << "\"";
}
// Overlay icon
if (!iconPath.empty())
vr::VROverlay()->SetOverlayFromFile( m_ulOverlayThumbnailHandle, iconPath.c_str() );
// Too many render calls in too short time overwhelm Qt and an
// assertion gets thrown. Therefore we use an timer to delay render
@ -401,6 +373,7 @@ void OverlayController::mainEventLoop() {
case vr::VREvent_DashboardActivated:
{
qDebug() << "Dashboard activated";
emit dashboardActivated();
m_dashboardVisible = true;
}
break;
@ -408,14 +381,17 @@ void OverlayController::mainEventLoop() {
case vr::VREvent_DashboardDeactivated:
{
qDebug() << "Dashboard deactivated";
emit dashboardDeactivated();
m_dashboardVisible = false;
}
break;
case vr::VREvent_KeyboardDone:
{
qDebug() << "VREvent_KeyboardDone";
char keyboardBuffer[1024];
vr::VROverlay()->GetKeyboardText( keyboardBuffer, 1024 );
qDebug() << "emit keyBoardInputSignal()";
emit keyBoardInputSignal( QString( keyboardBuffer ),
static_cast<unsigned long>(
vrEvent.data.keyboard.uUserValue ) );
@ -447,7 +423,7 @@ void OverlayController::showKeyboard(QString existingText, unsigned long userVal
vr::k_EGamepadTextInputModeNormal,
vr::k_EGamepadTextInputLineModeSingleLine,
0,
"Advanced Settings Overlay",
"Wowlet VR",
1024,
existingText.toStdString().c_str(),
userValue);

7
src/vr/overlaycontroller.h

@ -50,6 +50,7 @@ namespace wowletvr
class OverlayController : public QObject
{
Q_OBJECT
Q_PROPERTY( bool m_desktopMode READ isDesktopMode )
public:
OverlayController(bool desktopMode, QQmlEngine& qmlEngine);
@ -64,9 +65,7 @@ public:
return m_dashboardVisible;
}
void SetWidget( QQuickItem* quickItem,
const std::string& name,
const std::string& key = "" );
void SetWidget(QQuickItem* quickItem, const std::string& name, const std::string& key = "", const std::string& iconPath = "");
bool isDesktopMode()
{
@ -129,6 +128,8 @@ public slots:
signals:
void keyBoardInputSignal( QString input, unsigned long userValue = 0 );
void dashboardDeactivated();
void dashboardActivated();
};
} // namespace wowletvr

7
src/vr/qml.qrc

@ -40,13 +40,6 @@
<file alias="status_waiting">assets/img/status_waiting.svg</file>
<file alias="status_lagging">assets/img/status_lagging.svg</file>
<file>mock/NetworkType.js</file>
<file>mock/OverlayController.js</file>
<file>mock/Settings.js</file>
<file>mock/Translation.js</file>
<file>mock/Version.js</file>
<file>mock/Windows.js</file>
<file alias="main">main.qml</file>
<file>qml/common/HourComboBox.qml</file>
<file>qml/common/MinuteSecondComboBox.qml</file>

13
src/vr/qml/AboutPage.qml

@ -39,7 +39,7 @@ ColumnLayout {
Layout.leftMargin: 40
Layout.rightMargin: 40
Layout.fillWidth: true
text: "Wowlet VR is an alternative QML interface for wowlet and was made over a 4 week period by eating lots of pizzas. It is the world's first cryptocurrency wallet with support for VR. Wowlet is Free and open-source (BSD-3) software and the source code can be studied on git.wownero.com/wowlet/wowlet"
text: "Wowlet VR is an alternative QML interface for wowlet and was made over a 4 week period whilst eating lots of pizzas. It is the world's first cryptocurrency wallet with support for VR. Wowlet is Free and open-source (BSD-3) software and the source code can be studied on git.wownero.com/wowlet/wowlet"
wrap: true
}
@ -51,7 +51,16 @@ ColumnLayout {
Layout.leftMargin: 40
Layout.rightMargin: 40
Layout.fillWidth: true
text: "By \"dsc\" - April 2021. Shoutouts: OpenVR-AdvancedSettings, qvqc, Gatto, cisme, wowario, lza_menace, jwinterm, nioc, asymptotically, azy, selsta, kico, laura, thrmo, rottensox, solar, bl4sty, scoobybejesus (sorry if I forgot anyone!)"
text: "Greetings: matzman666, qvqc, ez, Gatto, cisme, wowario, lza_menace, jwinterm, nioc, asymptotically, azy, selsta, kico, laura, thrmo, rottensox, solar, bl4sty, scoobybejesus"
wrap: true
}
MyText {
Layout.leftMargin: 40
Layout.rightMargin: 40
Layout.fillWidth: true
fontSize: 14
text: "dsc - April 2021"
wrap: true
}

12
src/vr/qml/CreateWalletDialog.qml

@ -1,12 +0,0 @@
// 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 ovrwow.wowletvr 1.0
// import "common"

82
src/vr/qml/common/MyStackViewPage.qml

@ -9,6 +9,7 @@ import wowlet.Wallet 1.0
Rectangle {
id: root
color: "#1b2939"
width: 1600
height: 800
@ -17,46 +18,64 @@ Rectangle {
property string headerText: "Header Title"
property bool headerShowBackButton: true
property string enteredColor: "#365473"
property string exitedColor: "transparent"
property string pressedColor: "#406288"
signal backClicked();
property Item header: ColumnLayout {
RowLayout {
Button {
id: headerBackButton
Layout.preferredHeight: 50
Layout.preferredWidth: 50
hoverEnabled: true
enabled: headerShowBackButton
visible: headerShowBackButton
contentItem: Image {
source: "qrc:/backarrow"
sourceSize.width: 50
sourceSize.height: 50
Rectangle {
color: "transparent"
Layout.preferredWidth: headerBackButton.width + headerTitleContainer.width + 20
Layout.preferredHeight: 70
RowLayout {
anchors.fill: parent
Rectangle {
id: headerBackButton
visible: headerShowBackButton
color: "transparent"
Layout.preferredHeight: 50
Layout.preferredWidth: 50
Image {
source: "qrc:/backarrow"
sourceSize.width: 50
sourceSize.height: 50
anchors.fill: parent
}
}
Rectangle {
id: headerTitleContainer
color: "transparent"
Layout.preferredHeight: 50
Layout.preferredWidth: headerTitle.width
MyText {
id: headerTitle
text: headerText
font.pointSize: 26
anchors.verticalCenter: parent.verticalCenter
}
}
}
background: Rectangle {
opacity: parent.down ? 1.0 : (parent.activeFocus ? 0.5 : 0.0)
color: "#406288"
radius: 4
MouseArea {
enabled: headerShowBackButton
anchors.fill: parent
}
onHoveredChanged: {
if (hovered) {
forceActiveFocus()
} else {
focus = false
hoverEnabled: true
onEntered: parent.color = root.enteredColor
onExited: parent.color = root.exitedColor
onPressed: parent.color = root.pressedColor
onClicked: {
stackView.pop();
backClicked();
}
}
onClicked: {
backClicked();
stackView.pop();
}
}
MyText {
id: headerTitle
text: headerText
font.pointSize: 30
Layout.leftMargin: headerShowBackButton ? 32 : 0
}
Item {
@ -64,6 +83,7 @@ Rectangle {
Layout.preferredHeight: 50
}
Rectangle {
Layout.preferredWidth: 720
Layout.preferredHeight: 50

16
src/vr/qml/common/MyTextField.qml

@ -23,6 +23,7 @@ TextField {
}
}
onActiveFocusChanged: {
console.log("QML activeFocus()");
if (activeFocus) {
if (!OverlayController.desktopMode) {
OverlayController.showKeyboard(text, keyBoardUID)
@ -32,11 +33,24 @@ TextField {
}
}
onEditingFinished: {
console.log("QML onEditingFinished()");
if (OverlayController.desktopMode && savedText !== text) {
myTextField.onInputEvent(text)
}
}
function onInputEvent(input) {
text = input
}
}
Connections {
target: OverlayController
function onKeyBoardInputSignal(input, userValue) {
console.log("QML onKeyBoardInputSignal(input, userValue)", keyBoardUID);
if (userValue == keyBoardUID) {
if (myTextField.text !== input) {
myTextField.onInputEvent(input)
}
}
}
}
}

0
src/vr/qml/dashboard/CreateWallet.qml

0
src/vr/qml/dashboard/HelpPage.qml

157
src/vr/qml/wallet/HistoryTable.qml

@ -13,7 +13,8 @@ import "../common"
Item {
id: root
property var modelx
property var txModel
property int txCount: 0
property var txModelData: []
property int sideMargin: 20
@ -24,6 +25,12 @@ Item {
anchors.fill: parent
spacing: 0
MyText {
visible: txCount == 0
opacity: 0.75
text: "No transactions to display."
}
ListView {
id: listView
visible: true
@ -31,11 +38,11 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 10
model: modelx
model: txModel
interactive: false
delegate: Rectangle {
id: delegate
id: delegate
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
height: 54
@ -64,6 +71,7 @@ Item {
confirmationsRequired = getTxData(index, TransactionHistoryModel.TransactionConfirmationsRequiredRole);
confirmed = confirmations >= confirmationsRequired;
root.txCount = index;
}
RowLayout {
@ -74,16 +82,16 @@ Item {
anchors.right: parent.right
Rectangle {
Layout.preferredWidth: 56
Layout.fillHeight: true
color: "#406288"
Image {
width: 32
height: 32
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: {
Layout.preferredWidth: 56
Layout.fillHeight: true
color: "#406288"
Image {
width: 32
height: 32
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: {
if(failed) return "qrc:/assets/images/warning.png"
else if(pending) return "qrc:/assets/images/unconfirmed.png"
else if(!confirmed) return "qrc:/assets/images/clock1.png"
@ -91,98 +99,89 @@ Item {
else return "qrc:/assets/images/confirmed.png"
//confirmed ? "qrc:/checkmark_icon" : "qrc:/expired_icon"
}
}
}
}
Rectangle {
Layout.preferredWidth: 300
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.fillHeight: true
color: "transparent"
Layout.preferredWidth: 300
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.fillHeight: true
color: "transparent"
MyText {
MyText {
// date
anchors.verticalCenter: parent.verticalCenter
fontSize: 12
anchors.verticalCenter: parent.verticalCenter
fontSize: 12
fontColor: "white"
text: date
text: date
Component.onCompleted: {
parent.Layout.preferredWidth = width;
}
}
Component.onCompleted: {
parent.Layout.preferredWidth = width;
}
}
}
Rectangle {
Layout.fillHeight: true
Layout.leftMargin: 10
color: "transparent"
MyText {
anchors.verticalCenter: parent.verticalCenter
fontSize: 14
text: description !== "" ? description : "..."
Layout.fillHeight: true
Layout.leftMargin: 10
color: "transparent"
MyText {
anchors.verticalCenter: parent.verticalCenter
fontSize: 14
text: description !== "" ? description : "..."
fontColor: description !== "" ? "white" : "#cccccc"
Component.onCompleted: {
parent.Layout.preferredWidth = width;
}
}
Component.onCompleted: {
parent.Layout.preferredWidth = width;
}
}
}
Item {
Layout.fillWidth: true
Layout.fillWidth: true
}
Rectangle {
Layout.preferredWidth: 420
Layout.fillHeight: true
color: "#406288"
MyText {
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
fontSize: 14
fontBold: true
text: amount
fontColor: !isout ? "#00d304" : "red"
}
Layout.preferredWidth: 420
Layout.fillHeight: true
color: "#406288"
MyText {
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
fontSize: 14
fontBold: true
text: amount
fontColor: !isout ? "#00d304" : "red"
}
}
}
}
}
Item {
Layout.fillHeight: true
}
}
Rectangle {
z: parent.z - 1
color: "transparent"
anchors.fill: parent
}
}
}
}
function getTxData(x, y) {
var idx = modelx.index(x, y);
return modelx.data(idx, 0);
Item {
Layout.fillHeight: true
}
}
function updateTransactionsFromModel() {
// This function copies the items of `appWindow.currentWallet.historyModel` to `root.txModelData`, as a list of javascript objects
if(appWindow.currentWallet == null || typeof appWindow.currentWallet.history === "undefined" ) return;
Rectangle {
z: parent.z - 1
color: "transparent"
anchors.fill: parent
}
var _model = root.model;
var total = 0
var count = _model.rowCount()
root.txModelData = [];
function getTxData(x, y) {
var idx = txModel.index(x, y);
return txModel.data(idx, 0);
}
function onPageCompleted() {
if(currentWallet == null || typeof currentWallet.history === "undefined" ) return;
root.modelx = appWindow.currentWallet.historyModel;
root.txCount = 0;
root.txModel = appWindow.currentWallet.historyModel;
//root.model.sortRole = TransactionHistoryModel.TransactionBlockHeightRole;
//root.model.sort(0, Qt.DescendingOrder);
}

374
src/vr/qml/wallet/ReceivePage.qml

@ -6,249 +6,134 @@ import "."
import "../common"
MyStackViewPage {
id: root
headerText: "Receive"
MyDialogOkPopup {
id: chaperoneMessageDialog
function showMessage(title, text) {
dialogTitle = title
dialogText = text
open()
}
}
MyDialogOkCancelPopup {
id: chaperoneDeleteProfileDialog
property int profileIndex: -1
dialogTitle: "Delete Profile"
dialogText: "Do you really want to delete this profile?"
onClosed: {
if (okClicked) {
ChaperoneTabController.deleteChaperoneProfile(profileIndex)
}
}
}
MyDialogOkCancelPopup {
id: chaperoneNewProfileDialog
dialogTitle: "Create New Profile"
dialogWidth: 800
dialogHeight: 780
dialogContentItem: ColumnLayout {
RowLayout {
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
MyText {
text: "Name: "
}
MyTextField {
id: chaperoneNewProfileName
keyBoardUID: 390
color: "#cccccc"
text: ""
Layout.fillWidth: true
font.pointSize: 20
function onInputEvent(input) {
chaperoneNewProfileName.text = input
}
}
}
MyText {
Layout.topMargin: 24
text: "What to include:"
}
MyToggleButton {
id: chaperoneNewProfileIncludeGeometry
Layout.leftMargin: 32
text: "Chaperone Geometry"
}
MyToggleButton {
id: chaperoneNewProfileIncludeStyle
Layout.leftMargin: 32
text: "Chaperone Style"
}
MyToggleButton {
id: chaperoneNewProfileIncludeBoundsColor
Layout.leftMargin: 32
text: "Chaperone Color"
}
MyToggleButton {
id: chaperoneNewProfileIncludeVisibility
Layout.leftMargin: 32
text: "Visibility Setting"
}
MyToggleButton {
id: chaperoneNewProfileIncludeFadeDistance
Layout.leftMargin: 32
text: "Fade Distance Setting"
}
MyToggleButton {