mirror of
https://git.wownero.com/wowlet/wowlet.git
synced 2024-08-15 01:03:14 +00:00
Initial code for supporting an alternative QtQuick based UI for OpenVR
This commit is contained in:
parent
e918955210
commit
96034902d1
19 changed files with 1034 additions and 46 deletions
|
@ -49,12 +49,10 @@ if(OPENVR)
|
|||
"vr/utils/*.cpp"
|
||||
)
|
||||
list(APPEND SOURCE_FILES ${SOURCE_FILES_QML})
|
||||
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations") # @TODO: removeme
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations") # @TODO: removeme
|
||||
|
||||
add_subdirectory(libwalletqt)
|
||||
add_subdirectory(model)
|
||||
add_subdirectory(utils)
|
||||
|
|
|
@ -107,6 +107,8 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
|||
// Tor & socks proxy
|
||||
this->ws = new WSClient(this, m_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);
|
||||
|
||||
// Store the wallet every 2 minutes
|
||||
m_storeTimer.start(2 * 60 * 1000);
|
||||
|
@ -330,6 +332,8 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
|||
this->refreshed = false;
|
||||
this->currentWallet = wallet;
|
||||
this->walletPath = this->currentWallet->path() + ".keys";
|
||||
QFileInfo fileInfo(this->currentWallet->path());
|
||||
this->walletName = fileInfo.fileName();
|
||||
this->walletViewOnly = this->currentWallet->viewOnly();
|
||||
config()->set(Config::walletPath, this->walletPath);
|
||||
|
||||
|
@ -536,6 +540,12 @@ void AppContext::createConfigDirectory(const QString &dir) {
|
|||
}
|
||||
}
|
||||
|
||||
void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) {
|
||||
WowletSeed seed = WowletSeed(this->restoreHeights[this->networkType], this->coinName, this->seedLanguage);
|
||||
auto path = QDir(this->defaultWalletDir).filePath(name);
|
||||
this->createWallet(seed, path, password);
|
||||
}
|
||||
|
||||
void AppContext::createWallet(WowletSeed seed, const QString &path, const QString &password) {
|
||||
if(Utils::fileExists(path)) {
|
||||
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
|
||||
|
@ -605,6 +615,9 @@ void AppContext::createWalletFinish(const QString &password) {
|
|||
this->currentWallet->store();
|
||||
this->walletPassword = password;
|
||||
emit walletCreated(this->currentWallet);
|
||||
|
||||
// emit signal on behalf of walletManager, open wallet
|
||||
this->walletManager->walletOpened(this->currentWallet);
|
||||
}
|
||||
|
||||
void AppContext::initRestoreHeights() {
|
||||
|
@ -835,7 +848,13 @@ void AppContext::updateBalance() {
|
|||
AppContext::balance = balance_u / globals::cdiv;
|
||||
double spendable = this->currentWallet->unlockedBalance();
|
||||
|
||||
// formatted
|
||||
QString fmt_str = QString("Balance: %1 WOW").arg(Utils::balanceFormat(spendable));
|
||||
if (balance > spendable)
|
||||
fmt_str += QString(" (+%1 WOW unconfirmed)").arg(Utils::balanceFormat(balance - spendable));
|
||||
|
||||
emit balanceUpdated(balance_u, spendable);
|
||||
emit balanceUpdatedFormatted(fmt_str);
|
||||
}
|
||||
|
||||
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
|
||||
|
|
|
@ -60,9 +60,12 @@ public:
|
|||
|
||||
QString walletPath;
|
||||
QString walletPassword = "";
|
||||
QString walletName;
|
||||
bool walletViewOnly = false;
|
||||
NetworkType::Type networkType;
|
||||
|
||||
Q_PROPERTY(QString walletName MEMBER walletName);
|
||||
|
||||
QString applicationPath;
|
||||
|
||||
static void createConfigDirectory(const QString &dir) ;
|
||||
|
@ -89,24 +92,20 @@ public:
|
|||
static QMap<QString, QString> txCache;
|
||||
static TxFiatHistory *txFiatHistory;
|
||||
|
||||
QList<WalletKeysFiles> listWallets() {
|
||||
// return listing of wallet .keys items
|
||||
m_walletKeysFilesModel->refresh();
|
||||
return m_walletKeysFilesModel->listWallets();
|
||||
}
|
||||
|
||||
// libwalletqt
|
||||
bool refreshed = false;
|
||||
|
||||
WalletManager *walletManager;
|
||||
Wallet *currentWallet = nullptr;
|
||||
void createWallet(WowletSeed seed, const QString &path, const QString &password);
|
||||
Q_INVOKABLE void createWalletWithoutSpecifyingSeed(const QString &name, const QString &password);
|
||||
void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight);
|
||||
void createWalletFinish(const QString &password);
|
||||
void syncStatusUpdated(quint64 height, quint64 target);
|
||||
void updateBalance();
|
||||
void initTor();
|
||||
Q_INVOKABLE void initTor();
|
||||
Q_INVOKABLE void initWS();
|
||||
void initRestoreHeights();
|
||||
void initWS();
|
||||
void donateBeg();
|
||||
void refreshModels();
|
||||
void setWindowTitle(bool mining = false);
|
||||
|
@ -115,8 +114,21 @@ public:
|
|||
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
|
||||
void storeWallet();
|
||||
|
||||
Q_INVOKABLE QVariantList listWallets() {
|
||||
m_walletKeysFilesModel->refresh();
|
||||
|
||||
QVariantList list;
|
||||
for(const WalletKeysFiles &wallet: m_walletKeysFilesModel->listWallets())
|
||||
list << wallet.toVariant();
|
||||
return list;
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString displayAmount(quint64 amount) {
|
||||
return Utils::balanceFormat(amount);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void onOpenWallet(const QString& path, const QString &password);
|
||||
Q_INVOKABLE void onOpenWallet(const QString& path, const QString &password);
|
||||
void onCreateTransaction(QString address, quint64 amount, const QString description, bool all);
|
||||
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
|
||||
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
|
||||
|
@ -151,6 +163,7 @@ signals:
|
|||
void walletClosed();
|
||||
|
||||
void balanceUpdated(quint64 balance, quint64 spendable);
|
||||
void balanceUpdatedFormatted(QString fmt);
|
||||
void blockchainSync(int height, int target);
|
||||
void refreshSync(int height, int target);
|
||||
void synchronized();
|
||||
|
@ -165,6 +178,8 @@ signals:
|
|||
void createTransactionError(QString message);
|
||||
void createTransactionCancelled(const QVector<QString> &address, double amount);
|
||||
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
|
||||
void wsConnected();
|
||||
void wsDisconnected();
|
||||
void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
|
||||
void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes);
|
||||
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
|
||||
|
|
|
@ -36,8 +36,10 @@ public:
|
|||
LogLevel_Min = Monero::WalletManagerFactory::LogLevel_Min,
|
||||
LogLevel_Max = Monero::WalletManagerFactory::LogLevel_Max,
|
||||
};
|
||||
|
||||
explicit WalletManager(QObject *parent = nullptr);
|
||||
static WalletManager * instance();
|
||||
~WalletManager();
|
||||
|
||||
// wizard: createWallet path;
|
||||
Q_INVOKABLE Wallet * createWallet(const QString &path, const QString &password,
|
||||
const QString &language, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
|
||||
|
@ -187,9 +189,6 @@ public slots:
|
|||
private:
|
||||
friend class WalletPassphraseListenerImpl;
|
||||
|
||||
explicit WalletManager(QObject *parent = 0);
|
||||
~WalletManager();
|
||||
|
||||
bool isMining() const;
|
||||
|
||||
static WalletManager * m_instance;
|
||||
|
|
|
@ -114,12 +114,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
|
||||
qRegisterMetaType<QVector<QString>>();
|
||||
|
||||
#ifdef QML
|
||||
qputenv("QML_DISABLE_DISK_CACHE", "1");
|
||||
#endif
|
||||
|
||||
if(openVREnabled) {
|
||||
#ifdef HAS_OPENVR
|
||||
QApplication vr_app(argc, argv);
|
||||
auto *ctx = new AppContext(&parser);
|
||||
auto *vr = new wowletVR::WowletVR(ctx, &parser, &vr_app);
|
||||
qDebug() << "Context: " << qgetenv("QMLSCENE_DEVICE");
|
||||
auto *vr = new wowletvr::WowletVR(ctx, &parser, &vr_app);
|
||||
if(vr->errors.length() > 0)
|
||||
return 1;
|
||||
|
||||
|
|
|
@ -574,8 +574,6 @@ void MainWindow::onWalletOpenedError(const QString &err) {
|
|||
|
||||
void MainWindow::onWalletCreated(Wallet *wallet) {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// emit signal on behalf of walletManager
|
||||
m_ctx->walletManager->walletOpened(wallet);
|
||||
}
|
||||
|
||||
void MainWindow::onWalletOpened(Wallet *wallet) {
|
||||
|
|
|
@ -51,7 +51,7 @@ int TransactionHistoryModel::columnCount(const QModelIndex &parent) const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return Column::COUNT;
|
||||
return TransactionInfoRole::COUNT;
|
||||
}
|
||||
|
||||
QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const {
|
||||
|
@ -70,14 +70,14 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
else if (role == Qt::TextAlignmentRole) {
|
||||
switch (index.column()) {
|
||||
case Column::Amount:
|
||||
case Column::FiatAmount:
|
||||
case TransactionInfoRole::Amount:
|
||||
case TransactionInfoRole::FiatAmount:
|
||||
result = Qt::AlignRight;
|
||||
}
|
||||
}
|
||||
else if (role == Qt::DecorationRole) {
|
||||
switch (index.column()) {
|
||||
case Column::Date:
|
||||
case TransactionInfoRole::Date:
|
||||
{
|
||||
if (tInfo.isFailed())
|
||||
result = QVariant(m_warning);
|
||||
|
@ -100,7 +100,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
else if (role == Qt::ToolTipRole) {
|
||||
switch(index.column()) {
|
||||
case Column::Date:
|
||||
case TransactionInfoRole::Date:
|
||||
{
|
||||
if (tInfo.isFailed())
|
||||
result = "Transaction failed";
|
||||
|
@ -113,8 +113,8 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
else if (role == Qt::ForegroundRole) {
|
||||
switch(index.column()) {
|
||||
case Column::FiatAmount:
|
||||
case Column::Amount:
|
||||
case TransactionInfoRole::FiatAmount:
|
||||
case TransactionInfoRole::Amount:
|
||||
{
|
||||
if (tInfo.direction() == TransactionInfo::Direction_Out) {
|
||||
result = QVariant(QColor("#BC1E1E"));
|
||||
|
@ -134,9 +134,19 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
|||
{
|
||||
switch (column)
|
||||
{
|
||||
case Column::Date:
|
||||
case TransactionInfoRole::TransactionFailedRole:
|
||||
return tInfo.isFailed();
|
||||
case TransactionInfoRole::TransactionPendingRole:
|
||||
return tInfo.isPending();
|
||||
case TransactionInfoRole::TransactionConfirmationsRole:
|
||||
return tInfo.confirmations();
|
||||
case TransactionInfoRole::TransactionConfirmationsRequiredRole:
|
||||
return tInfo.confirmationsRequired();
|
||||
case TransactionInfoRole::Date:
|
||||
return tInfo.timestamp().toString("yyyy-MM-dd HH:mm");
|
||||
case Column::Description: {
|
||||
case TransactionInfoRole::TransactionIsOutRole:
|
||||
return tInfo.direction() == TransactionInfo::Direction_Out;
|
||||
case TransactionInfoRole::Description: {
|
||||
// if this tx is still in the pool, then we wont get the
|
||||
// description. We've cached it inside `AppContext::txDescriptionCache`
|
||||
// for the time being.
|
||||
|
@ -147,15 +157,15 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
|||
}
|
||||
return tInfo.description();
|
||||
}
|
||||
case Column::Amount:
|
||||
case TransactionInfoRole::Amount:
|
||||
{
|
||||
QString amount = QString::number(tInfo.balanceDelta() / globals::cdiv, 'f', 4);
|
||||
amount = (tInfo.direction() == TransactionInfo::Direction_Out) ? "-" + amount : "+" + amount;
|
||||
return amount;
|
||||
}
|
||||
case Column::TxID:
|
||||
case TransactionInfoRole::TxID:
|
||||
return tInfo.hash();
|
||||
case Column::FiatAmount:
|
||||
case TransactionInfoRole::FiatAmount:
|
||||
{
|
||||
double usd_price = AppContext::txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
|
||||
if (usd_price == 0.0)
|
||||
|
@ -183,15 +193,15 @@ QVariant TransactionHistoryModel::headerData(int section, Qt::Orientation orient
|
|||
}
|
||||
if (orientation == Qt::Horizontal) {
|
||||
switch(section) {
|
||||
case Column::Date:
|
||||
case TransactionInfoRole::Date:
|
||||
return QString("Date");
|
||||
case Column::Description:
|
||||
case TransactionInfoRole::Description:
|
||||
return QString("Description");
|
||||
case Column::Amount:
|
||||
case TransactionInfoRole::Amount:
|
||||
return QString("Amount");
|
||||
case Column::TxID:
|
||||
case TransactionInfoRole::TxID:
|
||||
return QString("Txid");
|
||||
case Column::FiatAmount:
|
||||
case TransactionInfoRole::FiatAmount:
|
||||
return QString("Fiat");
|
||||
default:
|
||||
return QVariant();
|
||||
|
@ -205,7 +215,7 @@ bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant &
|
|||
QString hash;
|
||||
|
||||
switch (index.column()) {
|
||||
case Column::Description:
|
||||
case TransactionInfoRole::Description:
|
||||
{
|
||||
m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionInfo &tInfo){
|
||||
hash = tInfo.hash();
|
||||
|
|
|
@ -21,15 +21,21 @@ class TransactionHistoryModel : public QAbstractTableModel
|
|||
Q_PROPERTY(TransactionHistory * transactionHistory READ transactionHistory WRITE setTransactionHistory NOTIFY transactionHistoryChanged)
|
||||
|
||||
public:
|
||||
enum Column
|
||||
enum TransactionInfoRole
|
||||
{
|
||||
Date = 0,
|
||||
Description,
|
||||
Amount,
|
||||
TxID,
|
||||
FiatAmount,
|
||||
TransactionIsOutRole,
|
||||
TransactionFailedRole,
|
||||
TransactionPendingRole,
|
||||
TransactionConfirmationsRole,
|
||||
TransactionConfirmationsRequiredRole,
|
||||
COUNT
|
||||
};
|
||||
Q_ENUM(TransactionInfoRole)
|
||||
|
||||
explicit TransactionHistoryModel(QObject * parent = nullptr);
|
||||
void setTransactionHistory(TransactionHistory * th);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
using namespace std::chrono;
|
||||
|
||||
|
||||
WalletKeysFiles::WalletKeysFiles() = default; // to please Q_DECLARE_METATYPE
|
||||
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, int networkType, QString address) :
|
||||
m_fileName(info.fileName()),
|
||||
m_modified(info.lastModified().toSecsSinceEpoch()),
|
||||
|
@ -47,6 +48,10 @@ int WalletKeysFiles::networkType() const {
|
|||
return m_networkType;
|
||||
}
|
||||
|
||||
QVariant WalletKeysFiles::toVariant() const {
|
||||
return QVariant::fromValue<WalletKeysFiles>(*this);
|
||||
}
|
||||
|
||||
QJsonObject WalletKeysFiles::toJsonObject() const {
|
||||
auto item = QJsonObject();
|
||||
item["fileName"] = m_fileName;
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
class WalletKeysFiles
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
WalletKeysFiles();
|
||||
WalletKeysFiles(const QFileInfo &info, int networkType, QString address);
|
||||
|
||||
QString fileName() const;
|
||||
|
@ -20,6 +22,13 @@ public:
|
|||
QString address() const;
|
||||
|
||||
QJsonObject toJsonObject() const;
|
||||
QVariant toVariant() const;
|
||||
|
||||
Q_PROPERTY(qint64 modified READ modified)
|
||||
Q_PROPERTY(QString fileName READ fileName)
|
||||
Q_PROPERTY(QString path READ path)
|
||||
Q_PROPERTY(QString address READ address)
|
||||
Q_PROPERTY(int networkType READ networkType)
|
||||
|
||||
private:
|
||||
QString m_fileName;
|
||||
|
@ -28,6 +37,8 @@ private:
|
|||
int m_networkType;
|
||||
QString m_address;
|
||||
};
|
||||
Q_DECLARE_METATYPE(WalletKeysFiles)
|
||||
|
||||
|
||||
class WalletKeysFilesModel : public QAbstractTableModel
|
||||
{
|
||||
|
|
|
@ -80,8 +80,8 @@ void WSServer::onNewConnection() {
|
|||
|
||||
// blast wallet listing on connect
|
||||
QJsonArray arr;
|
||||
for(const WalletKeysFiles &wallet: m_ctx->listWallets())
|
||||
arr << wallet.toJsonObject();
|
||||
for(const QVariant &wallet: m_ctx->listWallets())
|
||||
arr << wallet.value<WalletKeysFiles>().toJsonObject();
|
||||
auto welcomeWalletMessage = WSServer::createWSMessage("walletList", arr);
|
||||
pSocket->sendBinaryMessage(welcomeWalletMessage);
|
||||
|
||||
|
@ -336,9 +336,6 @@ void WSServer::onWalletCreatedError(const QString &err) {
|
|||
void WSServer::onWalletCreated(Wallet *wallet) {
|
||||
auto obj = wallet->toJsonObject();
|
||||
sendAll("walletCreated", obj);
|
||||
|
||||
// emit signal on behalf of walletManager
|
||||
m_ctx->walletManager->walletOpened(wallet);
|
||||
}
|
||||
|
||||
void WSServer::onSynchronized() {
|
||||
|
@ -350,7 +347,7 @@ void WSServer::onWalletOpenPasswordRequired(bool invalidPassword, const QString
|
|||
QJsonObject obj;
|
||||
obj["invalidPassword"] = invalidPassword;
|
||||
obj["path"] = path;
|
||||
sendAll("synchronized", obj);
|
||||
sendAll("walletOpenPasswordRequired", obj);
|
||||
}
|
||||
|
||||
void WSServer::onConnectionStatusChanged(int status) {
|
||||
|
|
107
src/vr/main.cpp
Normal file
107
src/vr/main.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#include <iostream>
|
||||
#include <QResource>
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QQmlComponent>
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QtQml>
|
||||
#include <QQuickView>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "openvr.h"
|
||||
#include "vr/openvr_init.h"
|
||||
#include "vr/main.h"
|
||||
|
||||
#include "libwalletqt/TransactionInfo.h"
|
||||
#include "libwalletqt/TransactionHistory.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "model/TransactionHistoryProxyModel.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
|
||||
#include "utils/keysfiles.h"
|
||||
|
||||
namespace wowletvr {
|
||||
|
||||
void check_error(int line, vr::EVRInitError error) { if (error != 0) printf("%d: error %s\n", line, VR_GetVRInitErrorAsSymbol(error)); }
|
||||
|
||||
WowletVR::WowletVR(AppContext *ctx, QCommandLineParser *parser, QObject *parent) :
|
||||
QObject(parent), ctx(ctx), m_parser(parser) {
|
||||
desktopMode = m_parser->isSet("openvr-debug");
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if(desktopMode)
|
||||
qputenv("QMLSCENE_DEVICE", "softwarecontext");
|
||||
#endif
|
||||
|
||||
qDebug() << "QMLSCENE_DEVICE: " << qgetenv("QMLSCENE_DEVICE");
|
||||
|
||||
m_engine.rootContext()->setContextProperty("homePath", QDir::homePath());
|
||||
m_engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
|
||||
m_engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
|
||||
m_engine.rootContext()->setContextProperty("qtRuntimeVersion", qVersion());
|
||||
m_engine.rootContext()->setContextProperty("ctx", ctx);
|
||||
|
||||
// qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
|
||||
qRegisterMetaType<NetworkType::Type>();
|
||||
qmlRegisterType<NetworkType>("wowlet.NetworkType", 1, 0, "NetworkType");
|
||||
|
||||
qmlRegisterUncreatableType<WalletKeysFiles>("wowlet.WalletKeysFiles", 1, 0, "WalletKeysFiles", "lol");
|
||||
qmlRegisterUncreatableType<Wallet>("wowlet.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly");
|
||||
qmlRegisterType<WalletManager>("wowlet.WalletManager", 1, 0, "WalletManager");
|
||||
|
||||
qmlRegisterUncreatableType<TransactionHistoryProxyModel>("wowlet.TransactionHistoryProxyModel", 1, 0, "TransactionHistoryProxyModel", "TransactionHistoryProxyModel can't be instantiated directly");
|
||||
qmlRegisterUncreatableType<TransactionHistoryModel>("wowlet.TransactionHistoryModel", 1, 0, "TransactionHistoryModel", "TransactionHistoryModel can't be instantiated directly");
|
||||
qmlRegisterUncreatableType<TransactionInfo>("wowlet.TransactionInfo", 1, 0, "TransactionInfo", "TransactionHistory can't be instantiated directly");
|
||||
qmlRegisterUncreatableType<TransactionHistory>("wowlet.TransactionHistory", 1, 0, "TransactionHistory", "TransactionHistory can't be instantiated directly");
|
||||
|
||||
qRegisterMetaType<PendingTransaction::Priority>();
|
||||
qRegisterMetaType<TransactionInfo::Direction>();
|
||||
qRegisterMetaType<TransactionHistoryModel::TransactionInfoRole>();
|
||||
|
||||
// @TODO: custom DPI / AA
|
||||
// QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL );
|
||||
// QCoreApplication::setAttribute( Qt::AA_Use96Dpi );
|
||||
|
||||
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() {
|
||||
auto quickObj = m_component->create();
|
||||
QQuickItem *quickObjItem = qobject_cast<QQuickItem*>(quickObj);
|
||||
|
||||
auto displayName = application_strings::applicationDisplayName;
|
||||
auto appKey = application_strings::applicationKey;
|
||||
|
||||
if(desktopMode) {
|
||||
auto m_pWindow = new QQuickWindow();
|
||||
qobject_cast<QQuickItem *>(quickObj)->setParentItem(m_pWindow->contentItem());
|
||||
m_pWindow->setGeometry(0, 0,
|
||||
static_cast<int>(qobject_cast<QQuickItem *>(quickObj)->width()),
|
||||
static_cast<int>(qobject_cast<QQuickItem *>(quickObj)->height()));
|
||||
m_pWindow->show();
|
||||
return;
|
||||
}
|
||||
|
||||
m_controller->SetWidget(quickObjItem, displayName, appKey);
|
||||
}
|
||||
|
||||
WowletVR::~WowletVR() {
|
||||
int weoignwieog = 1;
|
||||
};
|
||||
|
||||
}
|
38
src/vr/main.h
Normal file
38
src/vr/main.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
|
||||
#ifndef WOWLET_MAIN_H
|
||||
#define WOWLET_MAIN_H
|
||||
|
||||
#include <QtCore>
|
||||
#include <QQmlError>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QtQml>
|
||||
|
||||
#include "overlaycontroller.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
namespace wowletvr {
|
||||
|
||||
class WowletVR : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WowletVR(AppContext *ctx, QCommandLineParser *cmdargs, QObject *parent = nullptr);
|
||||
~WowletVR() override;
|
||||
|
||||
void render();
|
||||
|
||||
QList<QQmlError> errors;
|
||||
|
||||
private:
|
||||
AppContext *ctx;
|
||||
QCommandLineParser *m_parser;
|
||||
QQmlEngine m_engine;
|
||||
QQmlComponent *m_component;
|
||||
bool desktopMode = false;
|
||||
wowletvr::OverlayController *m_controller;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //WOWLET_MAIN_H
|
74
src/vr/openvr_init.cpp
Executable file
74
src/vr/openvr_init.cpp
Executable file
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
// Copyright (c) OpenVR Advanced Settings
|
||||
|
||||
#include <string>
|
||||
#include <openvr.h>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include "openvr_init.h"
|
||||
|
||||
namespace openvr_init
|
||||
{
|
||||
bool initializeProperly(const OpenVrInitializationType initType) {
|
||||
auto initializationType = vr::EVRApplicationType::VRApplication_Other;
|
||||
if (initType == OpenVrInitializationType::Overlay) {
|
||||
initializationType = vr::EVRApplicationType::VRApplication_Overlay;
|
||||
} else if (initType == OpenVrInitializationType::Utility) {
|
||||
initializationType = vr::EVRApplicationType::VRApplication_Utility;
|
||||
}
|
||||
|
||||
auto initError = vr::VRInitError_None;
|
||||
vr::VR_Init(&initError, initializationType);
|
||||
if (initError != vr::VRInitError_None) {
|
||||
if (initError == vr::VRInitError_Init_HmdNotFound || initError == vr::VRInitError_Init_HmdNotFoundPresenceFailed) {
|
||||
QMessageBox::critical(nullptr, "Wowlet VR", "Could not find HMD!");
|
||||
}
|
||||
qCritical() << "Failed to initialize OpenVR: " << std::string(vr::VR_GetVRInitErrorAsEnglishDescription(initError)).c_str();
|
||||
return false;
|
||||
}
|
||||
|
||||
qInfo() << "OpenVR initialized successfully.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initializeOpenVR(const OpenVrInitializationType initType)
|
||||
{
|
||||
bool res = initializeProperly(initType);
|
||||
if(!res)
|
||||
return false;
|
||||
|
||||
// Check whether OpenVR is too outdated
|
||||
if (!vr::VR_IsInterfaceVersionValid(vr::IVRSystem_Version)) {
|
||||
return reportVersionError(vr::IVRSystem_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRSettings_Version)) {
|
||||
return reportVersionError(vr::IVRSettings_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVROverlay_Version)) {
|
||||
return reportVersionError(vr::IVROverlay_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRApplications_Version)) {
|
||||
return reportVersionError(vr::IVRApplications_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRChaperone_Version)) {
|
||||
return reportVersionError(vr::IVRChaperone_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRChaperoneSetup_Version)) {
|
||||
return reportVersionError(vr::IVRChaperoneSetup_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRCompositor_Version)) {
|
||||
return reportVersionError(vr::IVRCompositor_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRNotifications_Version)) {
|
||||
return reportVersionError(vr::IVRNotifications_Version);
|
||||
} else if (!vr::VR_IsInterfaceVersionValid(vr::IVRInput_Version)) {
|
||||
return reportVersionError(vr::IVRInput_Version);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reportVersionError(const char* const interfaceAndVersion) {
|
||||
// The function call and error message was the same for all version checks.
|
||||
// Specific error messages are unlikely to be necessary since both the type
|
||||
// and version are in the string and will be output.
|
||||
auto msg = "OpenVR version is too outdated. Please update OpenVR: " + QString(interfaceAndVersion);
|
||||
QMessageBox::critical(nullptr, "Wowlet VR", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace openvr_init
|
16
src/vr/openvr_init.h
Executable file
16
src/vr/openvr_init.h
Executable file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
namespace openvr_init
|
||||
{
|
||||
|
||||
enum class OpenVrInitializationType
|
||||
{
|
||||
Overlay,
|
||||
Utility,
|
||||
};
|
||||
|
||||
bool initializeProperly(OpenVrInitializationType initType);
|
||||
bool initializeOpenVR(OpenVrInitializationType initType );
|
||||
bool reportVersionError(const char* interfaceAndVersion);
|
||||
|
||||
} // namespace openvr_init
|
484
src/vr/overlaycontroller.cpp
Executable file
484
src/vr/overlaycontroller.cpp
Executable file
|
@ -0,0 +1,484 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
// Copyright (c) OpenVR Advanced Settings
|
||||
|
||||
#include <QOpenGLFramebufferObjectFormat>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
#include <QQuickView>
|
||||
#include <QApplication>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QMouseEvent>
|
||||
#include <QtWidgets/QGraphicsSceneMouseEvent>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QGraphicsEllipseItem>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QCursor>
|
||||
#include <QProcess>
|
||||
#include <QMessageBox>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include "overlaycontroller.h"
|
||||
#include <openvr.h>
|
||||
|
||||
// application namespace
|
||||
namespace wowletvr
|
||||
{
|
||||
|
||||
OverlayController::OverlayController(bool desktopMode, QQmlEngine& qmlEngine) :
|
||||
QObject(),
|
||||
m_desktopMode(desktopMode)
|
||||
{
|
||||
// Arbitrarily chosen Max Length of Directory path, should be sufficient for
|
||||
// Any set-up
|
||||
const uint32_t maxLength = 16192;
|
||||
uint32_t requiredLength;
|
||||
|
||||
char tempRuntimePath[maxLength];
|
||||
bool pathIsGood = vr::VR_GetRuntimePath( tempRuntimePath, maxLength, &requiredLength );
|
||||
|
||||
// Throw Error If over 16k characters in path string
|
||||
if ( !pathIsGood )
|
||||
{
|
||||
qCritical() << "Error Finding VR Runtime Path, Attempting Recovery: ";
|
||||
uint32_t maxLengthRe = requiredLength;
|
||||
qInfo() << "Open VR reporting Required path length of: "
|
||||
<< maxLengthRe;
|
||||
}
|
||||
|
||||
m_runtimePathUrl = QUrl::fromLocalFile( tempRuntimePath );
|
||||
qInfo() << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile();
|
||||
|
||||
QSurfaceFormat format;
|
||||
// Qt's QOpenGLPaintDevice is not compatible with OpenGL versions >= 3.0
|
||||
// NVIDIA does not care, but unfortunately AMD does
|
||||
// Are subtle changes to the semantics of OpenGL functions actually covered
|
||||
// by the compatibility profile, and this is an AMD bug?
|
||||
format.setVersion( 2, 1 );
|
||||
// format.setProfile( QSurfaceFormat::CompatibilityProfile );
|
||||
format.setDepthBufferSize( 16 );
|
||||
format.setStencilBufferSize( 8 );
|
||||
format.setSamples( 16 );
|
||||
|
||||
m_openGLContext.setFormat( format );
|
||||
if ( !m_openGLContext.create() ) {
|
||||
throw std::runtime_error( "Could not create OpenGL context" );
|
||||
}
|
||||
|
||||
// create an offscreen surface to attach the context and FBO to
|
||||
m_offscreenSurface.setFormat( m_openGLContext.format() );
|
||||
m_offscreenSurface.create();
|
||||
m_openGLContext.makeCurrent( &m_offscreenSurface );
|
||||
|
||||
if (!vr::VROverlay()){
|
||||
QMessageBox::critical(nullptr, "Wowlet VR Overlay", "Is OpenVR running?");
|
||||
throw std::runtime_error( std::string( "No Overlay interface" ) );
|
||||
}
|
||||
|
||||
// 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() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void OverlayController::exitApp() {
|
||||
Shutdown();
|
||||
QApplication::exit();
|
||||
|
||||
qInfo() << "All systems exited.";
|
||||
exit( EXIT_SUCCESS );
|
||||
// Does not fallthrough
|
||||
}
|
||||
|
||||
void OverlayController::Shutdown() {
|
||||
if (m_pRenderTimer)
|
||||
{
|
||||
disconnect( &m_renderControl,
|
||||
SIGNAL( renderRequested() ),
|
||||
this,
|
||||
SLOT( OnRenderRequest() ) );
|
||||
disconnect( &m_renderControl,
|
||||
SIGNAL( sceneChanged() ),
|
||||
this,
|
||||
SLOT( OnRenderRequest() ) );
|
||||
disconnect( m_pRenderTimer.get(),
|
||||
SIGNAL( timeout() ),
|
||||
this,
|
||||
SLOT( renderOverlay() ) );
|
||||
m_pRenderTimer->stop();
|
||||
m_pRenderTimer.reset();
|
||||
}
|
||||
m_pFbo.reset();
|
||||
}
|
||||
|
||||
void OverlayController::SetWidget( QQuickItem* quickItem,
|
||||
const std::string& name,
|
||||
const std::string& key )
|
||||
{
|
||||
if ( !m_desktopMode )
|
||||
{
|
||||
vr::VROverlayError overlayError
|
||||
= vr::VROverlay()->CreateDashboardOverlay(
|
||||
key.c_str(),
|
||||
name.c_str(),
|
||||
&m_ulOverlayHandle,
|
||||
&m_ulOverlayThumbnailHandle );
|
||||
if ( overlayError != vr::VROverlayError_None )
|
||||
{
|
||||
if ( overlayError == vr::VROverlayError_KeyInUse )
|
||||
{
|
||||
QMessageBox::critical( nullptr,
|
||||
"Wowlet VR Overlay",
|
||||
"Another instance is already running." );
|
||||
}
|
||||
throw std::runtime_error( std::string(
|
||||
"Failed to create Overlay: "
|
||||
+ std::string( vr::VROverlay()->GetOverlayErrorNameFromEnum(
|
||||
overlayError ) ) ) );
|
||||
}
|
||||
vr::VROverlay()->SetOverlayWidthInMeters( m_ulOverlayHandle, 2.5f );
|
||||
vr::VROverlay()->SetOverlayInputMethod(
|
||||
m_ulOverlayHandle, vr::VROverlayInputMethod_Mouse );
|
||||
vr::VROverlay()->SetOverlayFlag(
|
||||
m_ulOverlayHandle,
|
||||
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 << "\"";
|
||||
}
|
||||
|
||||
// Too many render calls in too short time overwhelm Qt and an
|
||||
// assertion gets thrown. Therefore we use an timer to delay render
|
||||
// calls
|
||||
m_pRenderTimer.reset(new QTimer());
|
||||
m_pRenderTimer->setSingleShot( false );
|
||||
m_pRenderTimer->setInterval( 5 );
|
||||
connect( m_pRenderTimer.get(),
|
||||
SIGNAL( timeout() ),
|
||||
this,
|
||||
SLOT( renderOverlay() ) );
|
||||
|
||||
QOpenGLFramebufferObjectFormat fboFormat;
|
||||
fboFormat.setAttachment(
|
||||
QOpenGLFramebufferObject::CombinedDepthStencil );
|
||||
fboFormat.setTextureTarget( GL_TEXTURE_2D );
|
||||
m_pFbo.reset( new QOpenGLFramebufferObject(
|
||||
static_cast<int>( quickItem->width() ),
|
||||
static_cast<int>( quickItem->height() ),
|
||||
fboFormat ) );
|
||||
|
||||
m_window.setRenderTarget( m_pFbo.get() );
|
||||
quickItem->setParentItem( m_window.contentItem() );
|
||||
m_window.setGeometry( 0,
|
||||
0,
|
||||
static_cast<int>( quickItem->width() ),
|
||||
static_cast<int>( quickItem->height() ) );
|
||||
m_renderControl.initialize( &m_openGLContext );
|
||||
|
||||
vr::HmdVector2_t vecWindowSize
|
||||
= { static_cast<float>( quickItem->width() ),
|
||||
static_cast<float>( quickItem->height() ) };
|
||||
vr::VROverlay()->SetOverlayMouseScale( m_ulOverlayHandle,
|
||||
&vecWindowSize );
|
||||
|
||||
connect( &m_renderControl,
|
||||
SIGNAL( renderRequested() ),
|
||||
this,
|
||||
SLOT( OnRenderRequest() ) );
|
||||
connect( &m_renderControl,
|
||||
SIGNAL( sceneChanged() ),
|
||||
this,
|
||||
SLOT( OnRenderRequest() ) );
|
||||
|
||||
m_pRenderTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayController::OnRenderRequest() {
|
||||
if ( m_pRenderTimer && !m_pRenderTimer->isActive() )
|
||||
{
|
||||
m_pRenderTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayController::renderOverlay() {
|
||||
if ( !m_desktopMode )
|
||||
{
|
||||
// skip rendering if the overlay isn't visible
|
||||
if ( !vr::VROverlay()
|
||||
|| ( !vr::VROverlay()->IsOverlayVisible( m_ulOverlayHandle )
|
||||
&& !vr::VROverlay()->IsOverlayVisible(
|
||||
m_ulOverlayThumbnailHandle ) ) )
|
||||
return;
|
||||
m_renderControl.polishItems();
|
||||
m_renderControl.sync();
|
||||
m_renderControl.render();
|
||||
|
||||
GLuint unTexture = m_pFbo->texture();
|
||||
if ( unTexture != 0 )
|
||||
{
|
||||
#if defined _WIN64 || defined _LP64
|
||||
// To avoid any compiler warning because of cast to a larger
|
||||
// pointer type (warning C4312 on VC)
|
||||
vr::Texture_t texture = { reinterpret_cast<void*>(
|
||||
static_cast<uint64_t>( unTexture ) ),
|
||||
vr::TextureType_OpenGL,
|
||||
vr::ColorSpace_Auto };
|
||||
#else
|
||||
vr::Texture_t texture = { reinterpret_cast<void*>( unTexture ),
|
||||
vr::TextureType_OpenGL,
|
||||
vr::ColorSpace_Auto };
|
||||
#endif
|
||||
vr::VROverlay()->SetOverlayTexture( m_ulOverlayHandle, &texture );
|
||||
}
|
||||
m_openGLContext.functions()->glFlush(); // We need to flush otherwise
|
||||
// the texture may be empty.*/
|
||||
|
||||
if(m_customTickRateCounter % k_nonVsyncTickRate == 0) {
|
||||
mainEventLoop();
|
||||
m_customTickRateCounter = 0;
|
||||
} else {
|
||||
m_customTickRateCounter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OverlayController::pollNextEvent( vr::VROverlayHandle_t ulOverlayHandle,
|
||||
vr::VREvent_t* pEvent ) {
|
||||
if ( isDesktopMode() )
|
||||
{
|
||||
return vr::VRSystem()->PollNextEvent( pEvent, sizeof( vr::VREvent_t ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return vr::VROverlay()->PollNextOverlayEvent(
|
||||
ulOverlayHandle, pEvent, sizeof( vr::VREvent_t ) );
|
||||
}
|
||||
}
|
||||
|
||||
QPoint OverlayController::getMousePositionForEvent( vr::VREvent_Mouse_t mouse ) {
|
||||
float y = mouse.y;
|
||||
#ifdef __linux__
|
||||
float h = static_cast<float>( m_window.height() );
|
||||
y = h - y;
|
||||
#endif
|
||||
return QPoint( static_cast<int>( mouse.x ), static_cast<int>( y ) );
|
||||
}
|
||||
|
||||
void OverlayController::mainEventLoop() {
|
||||
if ( !vr::VRSystem() )
|
||||
return;
|
||||
|
||||
vr::VREvent_t vrEvent;
|
||||
|
||||
while ( pollNextEvent( m_ulOverlayHandle, &vrEvent ) ) {
|
||||
switch ( vrEvent.eventType )
|
||||
{
|
||||
case vr::VREvent_MouseMove:
|
||||
{
|
||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||
if ( ptNewMouse != m_ptLastMouse )
|
||||
{
|
||||
QMouseEvent mouseEvent( QEvent::MouseMove,
|
||||
ptNewMouse,
|
||||
m_window.mapToGlobal( ptNewMouse ),
|
||||
Qt::NoButton,
|
||||
m_lastMouseButtons,
|
||||
nullptr );
|
||||
m_ptLastMouse = ptNewMouse;
|
||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||
OnRenderRequest();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_MouseButtonDown:
|
||||
{
|
||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||
Qt::MouseButton button
|
||||
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
||||
? Qt::RightButton
|
||||
: Qt::LeftButton;
|
||||
m_lastMouseButtons |= button;
|
||||
QMouseEvent mouseEvent( QEvent::MouseButtonPress,
|
||||
ptNewMouse,
|
||||
m_window.mapToGlobal( ptNewMouse ),
|
||||
button,
|
||||
m_lastMouseButtons,
|
||||
nullptr );
|
||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_MouseButtonUp:
|
||||
{
|
||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||
Qt::MouseButton button
|
||||
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
||||
? Qt::RightButton
|
||||
: Qt::LeftButton;
|
||||
m_lastMouseButtons &= ~button;
|
||||
QMouseEvent mouseEvent( QEvent::MouseButtonRelease,
|
||||
ptNewMouse,
|
||||
m_window.mapToGlobal( ptNewMouse ),
|
||||
button,
|
||||
m_lastMouseButtons,
|
||||
nullptr );
|
||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_ScrollSmooth:
|
||||
{
|
||||
// Wheel speed is defined as 1/8 of a degree
|
||||
QWheelEvent wheelEvent(
|
||||
m_ptLastMouse,
|
||||
m_window.mapToGlobal( m_ptLastMouse ),
|
||||
QPoint(),
|
||||
QPoint( static_cast<int>( vrEvent.data.scroll.xdelta
|
||||
* ( 360.0f * 8.0f ) ),
|
||||
static_cast<int>( vrEvent.data.scroll.ydelta
|
||||
* ( 360.0f * 8.0f ) ) ),
|
||||
0,
|
||||
Qt::Vertical,
|
||||
m_lastMouseButtons,
|
||||
nullptr );
|
||||
QCoreApplication::sendEvent( &m_window, &wheelEvent );
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_OverlayShown:
|
||||
{
|
||||
m_window.update();
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_Quit:
|
||||
{
|
||||
qInfo() << "Received quit request.";
|
||||
vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some
|
||||
// time just in case
|
||||
|
||||
exitApp();
|
||||
// Won't fallthrough, but also exitApp() wont, but QT won't
|
||||
// acknowledge
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
case vr::VREvent_DashboardActivated:
|
||||
{
|
||||
qDebug() << "Dashboard activated";
|
||||
m_dashboardVisible = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_DashboardDeactivated:
|
||||
{
|
||||
qDebug() << "Dashboard deactivated";
|
||||
m_dashboardVisible = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case vr::VREvent_KeyboardDone:
|
||||
{
|
||||
char keyboardBuffer[1024];
|
||||
vr::VROverlay()->GetKeyboardText( keyboardBuffer, 1024 );
|
||||
emit keyBoardInputSignal( QString( keyboardBuffer ),
|
||||
static_cast<unsigned long>(
|
||||
vrEvent.data.keyboard.uUserValue ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_ulOverlayThumbnailHandle != vr::k_ulOverlayHandleInvalid ) {
|
||||
while ( vr::VROverlay()->PollNextOverlayEvent(
|
||||
m_ulOverlayThumbnailHandle, &vrEvent, sizeof( vrEvent ) ) )
|
||||
{
|
||||
switch ( vrEvent.eventType )
|
||||
{
|
||||
case vr::VREvent_OverlayShown:
|
||||
{
|
||||
m_window.update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayController::showKeyboard(QString existingText, unsigned long userValue)
|
||||
{
|
||||
vr::VROverlay()->ShowKeyboardForOverlay(
|
||||
m_ulOverlayHandle,
|
||||
vr::k_EGamepadTextInputModeNormal,
|
||||
vr::k_EGamepadTextInputLineModeSingleLine,
|
||||
0,
|
||||
"Advanced Settings Overlay",
|
||||
1024,
|
||||
existingText.toStdString().c_str(),
|
||||
userValue);
|
||||
setKeyboardPos();
|
||||
}
|
||||
|
||||
void OverlayController::setKeyboardPos()
|
||||
{
|
||||
vr::HmdVector2_t emptyvec;
|
||||
emptyvec.v[0] = 0;
|
||||
emptyvec.v[1] = 0;
|
||||
vr::HmdRect2_t empty;
|
||||
empty.vTopLeft = emptyvec;
|
||||
empty.vBottomRight = emptyvec;
|
||||
vr::VROverlay()->SetKeyboardPositionForOverlay( m_ulOverlayHandle, empty );
|
||||
}
|
||||
|
||||
QUrl OverlayController::getVRRuntimePathUrl() {
|
||||
return m_runtimePathUrl;
|
||||
}
|
||||
|
||||
bool OverlayController::soundDisabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
const vr::VROverlayHandle_t& OverlayController::overlayHandle() {
|
||||
return m_ulOverlayHandle;
|
||||
}
|
||||
|
||||
const vr::VROverlayHandle_t& OverlayController::overlayThumbnailHandle() {
|
||||
return m_ulOverlayThumbnailHandle;
|
||||
}
|
||||
|
||||
} // namespace wowletvr
|
134
src/vr/overlaycontroller.h
Executable file
134
src/vr/overlaycontroller.h
Executable file
|
@ -0,0 +1,134 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020-2021, The Monero Project.
|
||||
// Copyright (c) OpenVR Advanced Settings
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvr.h>
|
||||
#include <QtCore/QtCore>
|
||||
// because of incompatibilities with QtOpenGL and GLEW we need to cherry pick includes
|
||||
#include <QVector2D>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector>
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtWidgets/QGraphicsScene>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickRenderControl>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <memory>
|
||||
|
||||
#include "openvr_init.h"
|
||||
#include "vr/utils/paths.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
namespace application_strings
|
||||
{
|
||||
constexpr auto applicationOrganizationName = "Wownero";
|
||||
constexpr auto applicationName = "Wowlet VR";
|
||||
constexpr const char* applicationKey = "steam.overlay.1001337";
|
||||
constexpr const char* applicationDisplayName = "Wowlet VR";
|
||||
|
||||
constexpr const char* applicationVersionString = "1337";
|
||||
|
||||
} // namespace application_strings
|
||||
|
||||
constexpr int k_nonVsyncTickRate = 20;
|
||||
|
||||
// application namespace
|
||||
namespace wowletvr
|
||||
{
|
||||
|
||||
class OverlayController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OverlayController(bool desktopMode, QQmlEngine& qmlEngine);
|
||||
virtual ~OverlayController();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
Q_INVOKABLE void exitApp();
|
||||
|
||||
bool isDashboardVisible()
|
||||
{
|
||||
return m_dashboardVisible;
|
||||
}
|
||||
|
||||
void SetWidget( QQuickItem* quickItem,
|
||||
const std::string& name,
|
||||
const std::string& key = "" );
|
||||
|
||||
bool isDesktopMode()
|
||||
{
|
||||
return m_desktopMode;
|
||||
}
|
||||
|
||||
Q_INVOKABLE QUrl getVRRuntimePathUrl();
|
||||
Q_INVOKABLE bool soundDisabled();
|
||||
|
||||
const vr::VROverlayHandle_t& overlayHandle();
|
||||
const vr::VROverlayHandle_t& overlayThumbnailHandle();
|
||||
|
||||
bool pollNextEvent(vr::VROverlayHandle_t ulOverlayHandle, vr::VREvent_t* pEvent );
|
||||
void mainEventLoop();
|
||||
|
||||
private:
|
||||
vr::VROverlayHandle_t m_ulOverlayHandle = vr::k_ulOverlayHandleInvalid;
|
||||
vr::VROverlayHandle_t m_ulOverlayThumbnailHandle
|
||||
= vr::k_ulOverlayHandleInvalid;
|
||||
|
||||
QQuickRenderControl m_renderControl;
|
||||
QQuickWindow m_window{ &m_renderControl };
|
||||
std::unique_ptr<QOpenGLFramebufferObject> m_pFbo;
|
||||
QOpenGLContext m_openGLContext;
|
||||
QOffscreenSurface m_offscreenSurface;
|
||||
|
||||
std::unique_ptr<QTimer> m_pRenderTimer;
|
||||
bool m_dashboardVisible = false;
|
||||
|
||||
QPoint m_ptLastMouse;
|
||||
Qt::MouseButtons m_lastMouseButtons = nullptr;
|
||||
|
||||
bool m_desktopMode;
|
||||
|
||||
QUrl m_runtimePathUrl;
|
||||
|
||||
uint64_t m_customTickRateCounter = 0;
|
||||
uint64_t m_currentFrame = 0;
|
||||
uint64_t m_lastFrame = 0;
|
||||
|
||||
QNetworkAccessManager* netManager = new QNetworkAccessManager( this );
|
||||
QJsonDocument m_remoteVersionJsonDocument = QJsonDocument();
|
||||
QJsonObject m_remoteVersionJsonObject;
|
||||
|
||||
private:
|
||||
QPoint getMousePositionForEvent( vr::VREvent_Mouse_t mouse );
|
||||
|
||||
bool m_exclusiveState = false;
|
||||
bool m_keyPressOneState = false;
|
||||
bool m_keyPressTwoState = false;
|
||||
|
||||
AppContext *m_ctx;
|
||||
|
||||
public slots:
|
||||
void renderOverlay();
|
||||
void OnRenderRequest();
|
||||
|
||||
void showKeyboard( QString existingText, unsigned long userValue = 0 );
|
||||
void setKeyboardPos();
|
||||
|
||||
signals:
|
||||
void keyBoardInputSignal( QString input, unsigned long userValue = 0 );
|
||||
};
|
||||
|
||||
} // namespace wowletvr
|
58
src/vr/utils/paths.cpp
Executable file
58
src/vr/utils/paths.cpp
Executable file
|
@ -0,0 +1,58 @@
|
|||
#include "paths.h"
|
||||
#include <QStandardPaths>
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
|
||||
namespace paths
|
||||
{
|
||||
string binaryDirectory()
|
||||
{
|
||||
const auto path = QCoreApplication::applicationDirPath();
|
||||
if ( path == "" ) {
|
||||
qCritical() << "Could not find binary directory.";
|
||||
return "";
|
||||
}
|
||||
|
||||
return path.toStdString() + "/../"; // @ TODO: removeme
|
||||
}
|
||||
|
||||
string binaryDirectoryFindFile( const string& fileName ) {
|
||||
const auto path = binaryDirectory();
|
||||
if (path.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto filePath = QDir( QString::fromStdString( path ) + '/'
|
||||
+ QString::fromStdString( fileName ) );
|
||||
QFileInfo file( filePath.path() );
|
||||
if (!file.exists())
|
||||
{
|
||||
qCritical() << "Could not find file '" << fileName.c_str()
|
||||
<< "' in binary directory.";
|
||||
return "";
|
||||
}
|
||||
|
||||
return QDir::toNativeSeparators( file.filePath() ).toStdString();
|
||||
}
|
||||
|
||||
string settingsDirectory() {
|
||||
const auto path = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation );
|
||||
if (path == "") {
|
||||
qCritical() << "Could not find settings directory.";
|
||||
return "";
|
||||
}
|
||||
return path.toStdString();
|
||||
}
|
||||
|
||||
string verifyIconFilePath( const string& filename ) {
|
||||
const string notifIconPath = paths::binaryDirectoryFindFile( filename );
|
||||
if (notifIconPath.empty()) {
|
||||
qCritical() << "Could not find icon " << filename.c_str() << "\"";
|
||||
}
|
||||
|
||||
return notifIconPath;
|
||||
}
|
||||
|
||||
} // namespace paths
|
16
src/vr/utils/paths.h
Executable file
16
src/vr/utils/paths.h
Executable file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include <QDebug>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
|
||||
namespace paths
|
||||
{
|
||||
using std::string;
|
||||
|
||||
string binaryDirectory();
|
||||
string binaryDirectoryFindFile( const string& fileName );
|
||||
string settingsDirectory();
|
||||
string verifyIconFilePath( const string& filename );
|
||||
|
||||
} // namespace paths
|
Loading…
Reference in a new issue