parent
e918955210
commit
96034902d1
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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 new issue