Merged with master

This commit is contained in:
Olivier Prat 2018-01-24 10:51:54 +01:00
commit 95882e0cde
341 changed files with 11416 additions and 5792 deletions

9
.gitignore vendored
View file

@ -77,3 +77,12 @@ TAGS
# ignore node files for the console
node_modules
npm-debug.log
# Android studio files
*___jb_old___
# Generated assets for Android
android/app/src/main/assets
# Resource binary file
interface/compiledResources

View file

@ -1,5 +1,7 @@
# this guide is specific to Ubuntu 16.04.
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
## This guide is specific to Ubuntu 16.04.
Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
```
sudo su -
apt-get -y update
apt-get install -y software-properties-common
@ -8,20 +10,27 @@ add-apt-repository "deb http://debian.highfidelity.com stable main"
apt-get -y update
apt-get install -y hifi-domain-server
apt-get install -y hifi-assignment-client
```
# When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
```
apt-get install -y hifi-dev-domain-server
apt-get install -y hifi-dev-assignment-client
```
# domain server and assignment clients should already be running. The processes are controlled via:
Domain server and assignment clients should already be running. The processes are controlled via:
```
systemctl start hifi-domain-server
systemctl stop hifi-domain-server
```
# Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100).
# The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
# As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
Once the machine is setup and processes are running, you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (Further customizations can be done via http://IPAddress:40100).
The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
To do this you can modify /etc/crontab by adding the following lines
```
0 */1 * * * root apt-get update
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
```

View file

@ -26,9 +26,11 @@ endif()
if (ANDROID OR UWP)
option(BUILD_SERVER "Build server components" OFF)
option(BUILD_TOOLS "Build tools" OFF)
option(BUILD_INSTALLER "Build installer" OFF)
else()
option(BUILD_SERVER "Build server components" ON)
option(BUILD_TOOLS "Build tools" ON)
option(BUILD_INSTALLER "Build installer" ON)
endif()
if (SERVER_ONLY)
@ -40,25 +42,53 @@ else()
endif()
if (ANDROID)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
set(PLATFORM_GL_BACKEND gpu-gles)
option(USE_GLES "Use OpenGL ES" ON)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
else ()
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
set(PLATFORM_GL_BACKEND gpu-gl)
option(USE_GLES "Use OpenGL ES" OFF)
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
endif ()
if (USE_GLES AND (NOT ANDROID))
option(DISABLE_QML "Disable QML" ON)
else()
option(DISABLE_QML "Disable QML" OFF)
endif()
option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF)
set(PLATFORM_QT_GL OpenGL)
if (USE_GLES)
add_definitions(-DUSE_GLES)
set(PLATFORM_GL_BACKEND gpu-gles)
else()
set(PLATFORM_GL_BACKEND gpu-gl)
endif()
foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
endforeach()
option(BUILD_INSTALLER "Build installer" ON)
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS})
MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER})
MESSAGE(STATUS "GL ES: " ${USE_GLES})
if (DISABLE_QML)
MESSAGE(STATUS "QML disabled!")
add_definitions(-DDISABLE_QML)
endif()
if (DISABLE_KTX_CACHE)
MESSAGE(STATUS "KTX cache disabled!")
add_definitions(-DDISABLE_KTX_CACHE)
endif()
if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING})
MESSAGE(STATUS "Memory debugging is enabled")

View file

@ -1,26 +1,28 @@
set(TARGET_NAME native-lib)
setup_hifi_library(Gui Qml Quick)
# Minimal dependencies for testing UI compositing
#link_hifi_libraries(shared networking gl ui)
link_hifi_libraries(
shared networking octree
script-engine recording trackers
gl ktx image gpu gpu-gles render render-utils
physics
audio audio-client
ui midi controllers pointers
graphics model-networking fbx animation
entities entities-renderer
avatars avatars-renderer
ui-plugins input-plugins
# display-plugins
# auto-updater
)
target_link_libraries(native-lib android log m)
setup_hifi_library()
link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
target_opengl()
target_bullet()
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface")
add_subdirectory("${INTERFACE_DIR}" "libraries/interface")
include_directories("${INTERFACE_DIR}/src")
target_link_libraries(native-lib android log m interface)
set(GVR_ROOT "${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/")
target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers" "libraries/ui/src")
target_link_libraries(native-lib "${GVR_ROOT}/libraries/libgvr.so" ui)
# finished libraries
# core -> qt
# networking -> openssl, tbb
# fbx -> draco
# physics -> bullet
# entities-renderer -> polyvox
# unfinished libraries
# image -> nvtt (doesn't look good, but can be made optional)
# script-engine -> quazip (probably not required for the android client)

View file

@ -1,10 +1,11 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
//buildToolsVersion '27.0.3'
defaultConfig {
applicationId "com.highfidelity.iface"
applicationId "io.highfidelity.hifiinterface"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
@ -21,24 +22,19 @@ android {
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
'-DRELEASE_TYPE=' + RELEASE_TYPE,
'-DBUILD_BRANCH=' + BUILD_BRANCH
'-DBUILD_BRANCH=' + BUILD_BRANCH,
'-DDISABLE_QML=ON',
'-DDISABLE_KTX_CACHE=ON'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
buildTypes {
applicationVariants.all { variant ->
variant.outputs.all {
if (RELEASE_NUMBER != '0') {
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@ -50,11 +46,58 @@ android {
path '../../CMakeLists.txt'
}
}
applicationVariants.all { variant ->
// Our asset contents depend on items produced in the CMake build
// so our merge has to depend on the external native build
variant.externalNativeBuildTasks.each { task ->
variant.mergeResources.dependsOn(task)
}
variant.mergeAssets.doLast {
def assetList = new LinkedList<String>()
def youngestLastModified = 0
// Copy the compiled resources generated by the external native build
copy {
from new File(projectDir, "../../interface/compiledResources")
into outputDir
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details ->
youngestLastModified = Math.max(youngestLastModified, details.lastModified)
assetList.add(details.path)
}
}
// Copy the scripts directory
copy {
from new File(projectDir, "../../scripts")
into new File(outputDir, "scripts")
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details->
youngestLastModified = Math.max(youngestLastModified, details.lastModified)
assetList.add("scripts/" + details.path)
}
}
// Write a list of files to be unpacked to the cache folder
new File(outputDir, 'cache_assets.txt').withWriter { out ->
out.println(Long.toString(youngestLastModified))
assetList.each { file -> out.println(file) }
}
}
variant.outputs.all {
if (RELEASE_NUMBER != '0') {
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
}
}
}
}
dependencies {
implementation 'com.google.vr:sdk-audio:1.80.0'
implementation 'com.google.vr:sdk-base:1.80.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(include: ['*.jar'], dir: 'libs')
}

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.gvrinterface">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.hifiinterface">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.VIBRATE"/>
@ -7,11 +8,13 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<application
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:label="@string/app_name"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:screenOrientation="unspecified"
@ -19,18 +22,34 @@
android:icon="@mipmap/ic_launcher"
android:launchMode="singleTop"
android:roundIcon="@mipmap/ic_launcher_round">
<activity android:name="io.highfidelity.hifiinterface.PermissionChecker">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="io.highfidelity.hifiinterface.WebViewActivity"
android:theme="@android:style/Theme.Material.Light.NoActionBar"/>
<!-- We don't want to show this on Daydream yet (we need to fix the turn-around problem on this screen)
<activity android:name="io.highfidelity.hifiinterface.GvrLoaderActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.google.intent.category.DAYDREAM"/>
</intent-filter>
</activity>
-->
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".InterfaceActivity"
android:label="@string/app_name"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
android:launchMode="singleTop"
android:screenOrientation="landscape"
android:resizeableActivity="false">
android:launchMode="singleTop"
android:enableVrMode="com.google.vr.vrcore/com.google.vr.vrcore.common.VrCoreListenerService"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.google.intent.category.DAYDREAM"/>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="native-lib"/>
@ -43,4 +62,8 @@
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
</activity>
</application>
</manifest>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
</manifest>

View file

@ -1,19 +0,0 @@
import QtQuick 2.7
import QtWebView 1.1
Rectangle {
id: window
anchors.fill: parent
color: "red"
ColorAnimation on color { from: "blue"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
Text {
text: "Hello"
anchors.top: parent.top
}
WebView {
anchors.fill: parent
anchors.margins: 10
url: "http://doc.qt.io/qt-5/qml-qtwebview-webview.html"
}
}

View file

@ -1,153 +0,0 @@
#include <jni.h>
#include <android/log.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlFileSelector>
#include <QtQuick/QQuickView>
#include <QtGui/QOpenGLContext>
#include <QtCore/QLoggingCategory>
#include <QtWebView/QtWebView>
#include <gl/Config.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/GLWindow.h>
#include <ui/OffscreenQmlSurface.h>
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gl")
bool checkGLError(const char* name) {
GLenum error = glGetError();
if (!error) {
return false;
} else {
switch (error) {
case GL_INVALID_ENUM:
qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_INVALID_VALUE:
qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
break;
case GL_INVALID_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_OUT_OF_MEMORY:
qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
break;
default:
qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
break;
}
return true;
}
}
bool checkGLErrorDebug(const char* name) {
return checkGLError(name);
}
int QtMsgTypeToAndroidPriority(QtMsgType type) {
int priority = ANDROID_LOG_UNKNOWN;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
default: break;
}
return priority;
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
}
void qt_gl_set_global_share_context(QOpenGLContext *context);
int main(int argc, char* argv[])
{
qInstallMessageHandler(messageHandler);
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
QGuiApplication app(argc,argv);
app.setOrganizationName("QtProject");
app.setOrganizationDomain("qt-project.org");
app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
QtWebView::initialize();
qputenv("QSG_RENDERER_DEBUG", (QStringList() << "render" << "build" << "change" << "upload" << "roots" << "dump").join(';').toUtf8());
OffscreenGLCanvas sharedCanvas;
if (!sharedCanvas.create()) {
qFatal("Unable to create primary offscreen context");
}
qt_gl_set_global_share_context(sharedCanvas.getContext());
auto globalContext = QOpenGLContext::globalShareContext();
GLWindow window;
window.create();
window.setGeometry(qApp->primaryScreen()->availableGeometry());
window.createContext(globalContext);
if (!window.makeCurrent()) {
qFatal("Unable to make primary window GL context current");
}
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
static const ivec2 offscreenSize { 640, 480 };
OffscreenQmlSurface::setSharedContext(sharedCanvas.getContext());
OffscreenQmlSurface* qmlSurface = new OffscreenQmlSurface();
qmlSurface->create();
qmlSurface->resize(fromGlm(offscreenSize));
qmlSurface->load("qrc:///simple.qml");
qmlSurface->resume();
auto discardLambda = qmlSurface->getDiscardLambda();
window.showFullScreen();
QTimer timer;
timer.setInterval(10);
timer.setSingleShot(false);
OffscreenQmlSurface::TextureAndFence currentTextureAndFence;
timer.connect(&timer, &QTimer::timeout, &app, [&]{
window.makeCurrent();
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
if (qmlSurface->fetchTexture(newTextureAndFence)) {
if (currentTextureAndFence.first) {
discardLambda(currentTextureAndFence.first, currentTextureAndFence.second);
}
currentTextureAndFence = newTextureAndFence;
}
checkGLErrorDebug(__FUNCTION__);
if (currentTextureAndFence.second) {
glWaitSync((GLsync)currentTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
glDeleteSync((GLsync)currentTextureAndFence.second);
currentTextureAndFence.second = nullptr;
}
if (currentTextureAndFence.first) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, currentTextureAndFence.first, 0);
glBlitFramebuffer(0, 0, offscreenSize.x, offscreenSize.y, 100, 100, offscreenSize.x + 100, offscreenSize.y + 100, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
window.swapBuffers();
window.doneCurrent();
});
timer.start();
return app.exec();
}

View file

@ -1,6 +0,0 @@
<RCC>
<qresource prefix="/">
<file>simple.qml</file>
<file>+android/simple.qml</file>
</qresource>
</RCC>

View file

@ -0,0 +1,160 @@
#include <functional>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QStandardPaths>
#include <QtCore/QTextStream>
#include <QtAndroidExtras/QAndroidJniObject>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <shared/Storage.h>
void tempMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
abort();
}
}
}
AAssetManager* g_assetManager = nullptr;
void withAssetData(const char* filename, const std::function<void(off64_t, const void*)>& callback) {
auto asset = AAssetManager_open(g_assetManager, filename, AASSET_MODE_BUFFER);
if (!asset) {
throw std::runtime_error("Failed to open file");
}
auto buffer = AAsset_getBuffer(asset);
off64_t size = AAsset_getLength64(asset);
callback(size, buffer);
AAsset_close(asset);
}
QStringList readAssetLines(const char* filename) {
QStringList result;
withAssetData(filename, [&](off64_t size, const void* data){
QByteArray buffer = QByteArray::fromRawData((const char*)data, size);
{
QTextStream in(&buffer);
while (!in.atEnd()) {
QString line = in.readLine();
result << line;
}
}
});
return result;
}
void copyAsset(const char* sourceAsset, const QString& destFilename) {
withAssetData(sourceAsset, [&](off64_t size, const void* data){
QFile file(destFilename);
if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) {
throw std::runtime_error("Unable to open output file for writing");
}
if (!file.resize(size)) {
throw std::runtime_error("Unable to resize output file");
}
if (size != 0) {
auto mapped = file.map(0, size);
if (!mapped) {
throw std::runtime_error("Unable to map output file");
}
memcpy(mapped, data, size);
}
file.close();
});
}
void unpackAndroidAssets() {
const QString DEST_PREFIX = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/";
QStringList filesToCopy = readAssetLines("cache_assets.txt");
QString dateStamp = filesToCopy.takeFirst();
QString dateStampFilename = DEST_PREFIX + dateStamp;
qDebug() << "Checking date stamp" << dateStamp;
if (QFileInfo(dateStampFilename).exists()) {
return;
}
auto rootDir = QDir::root();
for (const auto& fileToCopy : filesToCopy) {
auto destFileName = DEST_PREFIX + fileToCopy;
auto destFolder = QFileInfo(destFileName).absoluteDir();
if (!destFolder.exists()) {
qDebug() << "Creating folder" << destFolder.absolutePath();
if (!rootDir.mkpath(destFolder.absolutePath())) {
throw std::runtime_error("Error creating output path");
}
}
if (QFile::exists(destFileName)) {
qDebug() << "Removing old file" << destFileName;
if (!QFile::remove(destFileName)) {
throw std::runtime_error("Failed to remove existing file");
}
}
qDebug() << "Copying asset " << fileToCopy << "to" << destFileName;
copyAsset(fileToCopy.toStdString().c_str(), destFileName);
}
{
qDebug() << "Writing date stamp" << dateStamp;
QFile file(dateStampFilename);
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
throw std::runtime_error("Can't write date stamp");
}
QTextStream(&file) << "touch" << endl;
file.close();
}
}
extern "C" {
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) {
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
g_assetManager = AAssetManager_fromJava(env, asset_mgr);
auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler);
unpackAndroidAssets();
qInstallMessageHandler(oldMessageHandler);
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnPause";
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnResume(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnResume";
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExitVr(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
}
}

View file

@ -1,10 +0,0 @@
import QtQuick 2.0
Rectangle {
id: window
width: 320
height: 480
focus: true
color: "red"
ColorAnimation on color { from: "red"; to: "yellow"; duration: 1000; loops: Animation.Infinite }
}

View file

@ -0,0 +1,178 @@
//
// InterfaceActivity.java
// android/app/src/main/java
//
// Created by Stephen Birarda on 1/26/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
package io.highfidelity.hifiinterface;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.WindowManager;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtActivity;
/*import com.google.vr.cardboard.DisplaySynchronizer;
import com.google.vr.cardboard.DisplayUtils;
import com.google.vr.ndk.base.GvrApi;*/
import android.graphics.Point;
import android.content.res.Configuration;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.view.View;
public class InterfaceActivity extends QtActivity {
//public static native void handleHifiURL(String hifiURLString);
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
//private native void nativeOnPause();
//private native void nativeOnResume();
//private native void nativeOnStop();
//private native void nativeOnStart();
//private native void saveRealScreenSize(int width, int height);
//private native void setAppVersion(String version);
private native long nativeOnExitVr();
private AssetManager assetManager;
private static boolean inVrMode;
// private GvrApi gvrApi;
// Opaque native pointer to the Application C++ object.
// This object is owned by the InterfaceActivity instance and passed to the native methods.
//private long nativeGvrApi;
public void enterVr() {
//Log.d("[VR]", "Entering Vr mode (java)");
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
inVrMode = true;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Get the intent that started this activity in case we have a hifi:// URL to parse
Intent intent = getIntent();
if (intent.getAction() == Intent.ACTION_VIEW) {
Uri data = intent.getData();
// if (data.getScheme().equals("hifi")) {
// handleHifiURL(data.toString());
// }
}
/* DisplaySynchronizer displaySynchronizer = new DisplaySynchronizer(this, DisplayUtils.getDefaultDisplay(this));
gvrApi = new GvrApi(this, displaySynchronizer);
*/
// Log.d("GVR", "gvrApi.toString(): " + gvrApi.toString());
assetManager = getResources().getAssets();
//nativeGvrApi =
nativeOnCreate(this, assetManager /*, gvrApi.getNativeGvrContext()*/);
Point size = new Point();
getWindowManager().getDefaultDisplay().getRealSize(size);
// saveRealScreenSize(size.x, size.y);
try {
PackageInfo pInfo = this.getPackageManager().getPackageInfo(getPackageName(), 0);
String version = pInfo.versionName;
// setAppVersion(version);
} catch (PackageManager.NameNotFoundException e) {
Log.e("GVR", "Error getting application version", e);
}
final View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
// This is a workaround to hide the menu bar when the virtual keyboard is shown from Qt
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new android.view.ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();
if (getActionBar().isShowing()) {
getActionBar().hide();
}
}
});
}
@Override
protected void onPause() {
super.onPause();
//nativeOnPause();
//gvrApi.pauseTracking();
}
@Override
protected void onStart() {
super.onStart();
// nativeOnStart();
}
@Override
protected void onStop() {
Log.d("[Background]","Calling nativeOnStop from InterfaceActivity");
// nativeOnStop();
super.onStop();
}
@Override
protected void onResume() {
super.onResume();
//nativeOnResume();
//gvrApi.resumeTracking();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
// Log.d("[VR]", "Portrait, forcing landscape");
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
if (inVrMode) {
inVrMode = false;
// Log.d("[VR]", "Starting VR exit");
nativeOnExitVr();
} else {
Log.w("[VR]", "Portrait detected but not in VR mode. Should not happen");
}
}
}
public void openUrlInAndroidWebView(String urlString) {
Log.d("openUrl", "Received in open " + urlString);
Intent openUrlIntent = new Intent(this, WebViewActivity.class);
openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, urlString);
startActivity(openUrlIntent);
}
/**
* Called when view focus is changed
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
}
}
}

View file

@ -0,0 +1,130 @@
package io.highfidelity.hifiinterface;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.app.AlertDialog;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
public class PermissionChecker extends Activity {
private static final int REQUEST_PERMISSIONS = 20;
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (CHOOSE_AVATAR_ON_STARTUP) {
showMenu();
}
this.requestAppPermissions(new
String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA}
,2,REQUEST_PERMISSIONS);
}
public void requestAppPermissions(final String[] requestedPermissions,
final int stringId, final int requestCode) {
int permissionCheck = PackageManager.PERMISSION_GRANTED;
boolean shouldShowRequestPermissionRationale = false;
for (String permission : requestedPermissions) {
permissionCheck = permissionCheck + checkSelfPermission(permission);
shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || shouldShowRequestPermissionRationale(permission);
}
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
System.out.println("Permission was not granted. Ask for permissions");
if (shouldShowRequestPermissionRationale) {
requestPermissions(requestedPermissions, requestCode);
} else {
requestPermissions(requestedPermissions, requestCode);
}
} else {
System.out.println("Launching the other activity..");
launchActivityWithPermissions();
}
}
private void launchActivityWithPermissions(){
finish();
Intent i = new Intent(this, InterfaceActivity.class);
startActivity(i);
finish();
}
private void showMenu(){
final List<String> avatarOptions = Arrays.asList("\uD83D\uDC66\uD83C\uDFFB Cody","\uD83D\uDC66\uD83C\uDFFF Will","\uD83D\uDC68\uD83C\uDFFD Albert", "\uD83D\uDC7D Being of Light");
final String[] avatarPaths = {
"http://mpassets.highfidelity.com/8c859fca-4cbd-4e82-aad1-5f4cb0ca5d53-v1/cody.fst",
"http://mpassets.highfidelity.com/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73-v1/4618d52e711fbb34df442b414da767bb.fst",
"http://mpassets.highfidelity.com/1e57c395-612e-4acd-9561-e79dbda0bc49-v1/albert.fst" };
final String pathForJson = "/data/data/io.highfidelity.hifiinterface/files/.config/High Fidelity - dev/";
new AlertDialog.Builder(this)
.setTitle("Pick an avatar")
.setItems(avatarOptions.toArray(new CharSequence[avatarOptions.size()]),new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
if(which < avatarPaths.length ) {
JSONObject obj = new JSONObject();
try {
obj.put("firstRun",false);
obj.put("Avatar/fullAvatarURL", avatarPaths[which]);
File directory = new File(pathForJson);
if(!directory.exists()) directory.mkdirs();
File file = new File(pathForJson + "Interface.json");
Writer output = new BufferedWriter(new FileWriter(file));
output.write(obj.toString().replace("\\",""));
output.close();
System.out.println("I Could write config file expect to see the selected avatar"+obj.toString().replace("\\",""));
} catch (JSONException e) {
System.out.println("JSONException something weired went wrong");
} catch (IOException e) {
System.out.println("Could not write file :(");
}
} else {
System.out.println("Default avatar selected...");
}
launchActivityWithPermissions();
}
}).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
for (int permission : grantResults) {
permissionCheck = permissionCheck + permission;
}
if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
launchActivityWithPermissions();
} else if (grantResults.length > 0) {
System.out.println("User has deliberately denied Permissions. Launching anyways");
launchActivityWithPermissions();
}
}
}

View file

@ -0,0 +1,242 @@
//
// WebViewActivity.java
// interface/java
//
// Created by Cristian Duarte and Gabriel Calero on 8/17/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
//package hifiinterface.highfidelity.io.mybrowserapplication;
package io.highfidelity.hifiinterface;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
import android.widget.Toolbar;
import android.os.Looper;
import java.lang.Thread;
import java.lang.Runnable;
import java.net.MalformedURLException;
import java.net.URL;
public class WebViewActivity extends Activity {
public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url";
private native void nativeProcessURL(String url);
private WebView myWebView;
private ProgressBar mProgressBar;
private ActionBar mActionBar;
private String mUrl;
enum SafenessLevel {
NOT_ANALYZED_YET(""),
NOT_SECURE(""),
SECURE("\uD83D\uDD12 "),
BAD_SECURE("\uD83D\uDD13 ");
String icon;
SafenessLevel(String icon) {
this.icon = icon;
}
}
private SafenessLevel safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
setActionBar((Toolbar) findViewById(R.id.toolbar_actionbar));
mActionBar = getActionBar();
mActionBar.setDisplayHomeAsUpEnabled(true);
mProgressBar = (ProgressBar) findViewById(R.id.toolbarProgressBar);
mUrl = getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL);
myWebView = (WebView) findViewById(R.id.web_view);
myWebView.setWebViewClient(new HiFiWebViewClient());
myWebView.setWebChromeClient(new HiFiWebChromeClient());
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
myWebView.loadUrl(mUrl);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Check if the key event was the Back button and if there's history
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
// If it wasn't the Back key or there's no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
private void showSubtitleWithUrl(String url) {
try {
mActionBar.setSubtitle(safenessLevel.icon + new URL(url.toString()).getHost());
} catch (MalformedURLException e) {
Toast.makeText(WebViewActivity.this, "Error loading page: " + "bad url", Toast.LENGTH_LONG).show();
Log.e("openUrl", "bad url");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.web_view_menu, menu);
return true;
}
private String intentUrlOrWebUrl() {
return myWebView==null || myWebView.getUrl()==null?mUrl:myWebView.getUrl();
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()) {
// Respond to the action bar's Up/Home/back button
case android.R.id.home:
finish();
break;
case R.id.action_browser:
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(intentUrlOrWebUrl()));
startActivity(i);
return true;
case R.id.action_share:
Intent is = new Intent(Intent.ACTION_SEND);
is.setType("text/plain");
is.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.web_view_action_share_subject));
is.putExtra(Intent.EXTRA_TEXT, intentUrlOrWebUrl());
startActivity(Intent.createChooser(is, getString(R.string.web_view_action_share_chooser)));
return true;
}
return super.onOptionsItemSelected(item);
}
class HiFiWebViewClient extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mProgressBar.setVisibility(View.GONE);
if (safenessLevel!=SafenessLevel.BAD_SECURE) {
if (url.startsWith("https:")) {
safenessLevel=SafenessLevel.SECURE;
} else {
safenessLevel=SafenessLevel.NOT_SECURE;
}
}
showSubtitleWithUrl(url);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
safenessLevel = SafenessLevel.NOT_ANALYZED_YET;
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setProgress(0);
showSubtitleWithUrl(url);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
Toast.makeText(WebViewActivity.this, "Error loading page: " + error.getDescription(), Toast.LENGTH_LONG).show();
if (ERROR_FAILED_SSL_HANDSHAKE == error.getErrorCode()) {
safenessLevel = SafenessLevel.BAD_SECURE;
}
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
Toast.makeText(WebViewActivity.this, "Network Error loading page: " + errorResponse.getReasonPhrase(), Toast.LENGTH_LONG).show();
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
Toast.makeText(WebViewActivity.this, "SSL error loading page: " + error.toString(), Toast.LENGTH_LONG).show();
safenessLevel = SafenessLevel.BAD_SECURE;
}
private boolean isFst(WebResourceRequest request) {
return isFst(request.getUrl().toString());
}
private boolean isFst(String url) {
return url.endsWith(".fst");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
// managing avatar selections
if (isFst(request)) {
final String url = request.getUrl().toString();
new Thread(new Runnable() {
public void run() {
nativeProcessURL(url);
}
}).start(); // Avoid deadlock in Qt dialog
WebViewActivity.this.finish();
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onLoadResource(WebView view, String url) {
if (isFst(url)) {
// processed separately
} else {
super.onLoadResource(view, url);
}
}
}
class HiFiWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
mProgressBar.setProgress(newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
mActionBar.setTitle(title);
}
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Toolbar
android:id="@+id/toolbar_actionbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_alignParentTop="true"
style="@android:style/Widget.Material.ActionBar"
android:titleTextAppearance="@style/ActionBarTitleStyle"
android:subtitleTextAppearance="@style/ActionBarSubtitleStyle"
android:navigationIcon="@drawable/ic_close_black_24dp"
android:contentInsetStart="0dp"
android:contentInsetStartWithNavigation="0dp"
android:title="">
</Toolbar>
<WebView
android:id="@+id/web_view"
android:layout_below="@id/toolbar_actionbar"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="@+id/toolbarProgressBar"
android:layout_below="@id/toolbar_actionbar"
style="?android:attr/progressBarStyleHorizontal"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="3dp"
android:indeterminate="false"
android:padding="0dp" />
</RelativeLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_share"
android:title="@string/web_view_action_share"
android:showAsAction="never"/>
<item android:id="@+id/action_browser"
android:title="@string/web_view_action_open_in_browser"
android:showAsAction="never"/>
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<!-- Hack to have a desired text size for both orientations (default portrait is too big) -->
<!-- Default text size for action bar title.-->
<dimen name="text_size_title_material_toolbar">14dp</dimen>
<!-- Default text size for action bar subtitle.-->
<dimen name="text_size_subtitle_material_toolbar">12dp</dimen>
</resources>

View file

@ -1,3 +1,8 @@
<resources>
<string name="app_name">TestApp</string>
<string name="app_name" translatable="false">Interface</string>
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
<string name="web_view_action_share" translatable="false">Share link</string>
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
<string name="web_view_action_share_chooser" translatable="false">Share link</string>
</resources>

View file

@ -11,5 +11,15 @@
<!--item name="android:background">@color/white_opaque</item-->
</style>
<!-- Overriding text size so it's not so big in portrait -->
<style name="ActionBarTitleStyle"
parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
<item name="android:textSize">@dimen/text_size_title_material_toolbar</item>
</style>
<style name="ActionBarSubtitleStyle"
parent="@android:style/TextAppearance.Material.Widget.ActionBar.Subtitle">
<item name="android:textSize">@dimen/text_size_subtitle_material_toolbar</item>
</style>
</resources>

View file

@ -40,6 +40,7 @@ ext {
BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : ''
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
QT5_DEPS = [
'Qt5Concurrent',
'Qt5Core',
'Qt5Gui',
'Qt5Multimedia',
@ -47,8 +48,11 @@ ext {
'Qt5OpenGL',
'Qt5Qml',
'Qt5Quick',
'Qt5QuickControls2',
'Qt5QuickTemplates2',
'Qt5Script',
'Qt5ScriptTools',
'Qt5Svg',
'Qt5WebChannel',
'Qt5WebSockets',
'Qt5Widgets',
@ -93,6 +97,11 @@ def packages = [
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glad: [
file: 'glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
],
glm: [
file: 'glm-0.9.8.tgz',
versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2',
@ -145,16 +154,18 @@ def options = [
def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt')
def captureOutput = { String command ->
def proc = command.execute()
def sout = new StringBuilder(), serr = new StringBuilder()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(10000)
def errorOutput = serr.toString()
if (!errorOutput.isEmpty()) {
throw new GradleException("Command '${command}' failed with error ${errorOutput}")
def captureOutput = { String command, List<String> commandArgs ->
def result
new ByteArrayOutputStream().withStream { os ->
def execResult = exec {
executable = command
args = commandArgs
standardOutput = os
errorOutput = new ByteArrayOutputStream()
}
result = os.toString()
}
return sout.toString()
return result;
}
def relativize = { File root, File absolute ->
@ -168,11 +179,13 @@ def scanQmlImports = { File qmlRootPath ->
throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString())
}
def command = qmlImportCommandFile.absolutePath +
" -rootPath ${qmlRootPath.absolutePath}" +
" -importPath ${qmlRoot.absolutePath}/qml"
def command = qmlImportCommandFile.absolutePath
def args = [
'-rootPath', qmlRootPath.absolutePath,
'-importPath', "${qmlRoot.absolutePath}/qml"
]
def commandResult = captureOutput(command)
def commandResult = captureOutput(command, args)
new JsonSlurper().parseText(commandResult).each {
if (!it.containsKey('path')) {
println "Warning: QML import could not be resolved in any of the import paths: ${it.name}"
@ -257,7 +270,7 @@ def parseQtDependencies = { List qtLibs ->
def generateLibsXml = {
def libDestinationDirectory = jniFolder
def jarDestinationDirectory = new File(appDir, 'libs')
def assetDestinationDirectory = new File(appDir, 'src/main/assets/bundled');
def assetDestinationDirectory = new File(appDir, 'src/main/assets/--Added-by-androiddeployqt--');
def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml')
def libPrefix = 'lib' + File.separator
def jarPrefix = 'jar' + File.separator
@ -293,7 +306,7 @@ def generateLibsXml = {
} else if (relativePath.startsWith('jar')) {
destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size()))
} else {
xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("bundled/${relativePath}:${relativePath}".replace(File.separator, '/'))
xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("--Added-by-androiddeployqt--/${relativePath}:${relativePath}".replace(File.separator, '/'))
destinationFile = new File(assetDestinationDirectory, relativePath)
}
@ -427,9 +440,55 @@ task extractGvrBinaries(dependsOn: extractDependencies) {
}
}
}
}
def generateAssetsFileList = {
def assetsPath = "${appDir}/src/main/assets/"
def addedByAndroidDeployQtName = "--Added-by-androiddeployqt--/"
def addedByAndroidDeployQtPath = assetsPath + addedByAndroidDeployQtName
def addedByAndroidDeployQt = new File(addedByAndroidDeployQtPath)
if (!addedByAndroidDeployQt.exists() && !addedByAndroidDeployQt.mkdirs()) {
throw new GradleScriptException("Failed to create directory " + addedByAndroidDeployQtPath, null);
}
def outputFilename = "/qt_cache_pregenerated_file_list"
def outputFile = new File(addedByAndroidDeployQtPath + outputFilename);
Map<String, List<String>> directoryContents = new TreeMap<>();
def dir = new File(assetsPath)
dir.eachFileRecurse (FileType.ANY) { file ->
def name = file.path.substring(assetsPath.length())
int slashIndex = name.lastIndexOf('/')
def pathName = slashIndex >= 0 ? name.substring(0, slashIndex) : "/"
def fileName = slashIndex >= 0 ? name.substring(pathName.length() + 1) : name
if (!fileName.isEmpty() && file.isDirectory() && !fileName.endsWith("/")) {
fileName += "/"
}
if (!directoryContents.containsKey(pathName)) {
directoryContents[pathName] = new ArrayList<String>()
}
if (!fileName.isEmpty()) {
directoryContents[pathName].add(fileName);
}
}
DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
for (Map.Entry<String, List<String>> e: directoryContents.entrySet()) {
def entryList = e.getValue()
fos.writeInt(e.key.length()*2); // 2 bytes per char
fos.writeChars(e.key);
fos.writeInt(entryList.size());
for (String entry: entryList) {
fos.writeInt(entry.length()*2);
fos.writeChars(entry);
}
}
fos.close();
}
// Copy required Qt main libraries and required plugins based on the predefined list here
// FIXME eventually we would like to use the readelf functionality to automatically detect dependencies
// from our built applications and use that during the full build process. However doing so would mean
@ -438,10 +497,11 @@ task extractGvrBinaries(dependsOn: extractDependencies) {
task qtBundle {
doLast {
parseQtDependencies(QT5_DEPS)
//def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/")
def qmlImportFolder = new File("${projectDir}/app/src/main/cpp")
def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/")
//def qmlImportFolder = new File("${projectDir}/app/src/main/cpp")
scanQmlImports(qmlImportFolder)
generateLibsXml()
generateAssetsFileList()
}
}
@ -450,10 +510,12 @@ task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBina
task cleanDependencies(type: Delete) {
delete HIFI_ANDROID_PRECOMPILED
delete 'app/src/main/jniLibs/arm64-v8a'
delete 'app/src/main/assets/bundled'
delete 'app/src/main/assets/--Added-by-androiddeployqt--'
delete 'app/src/main/res/values/libs.xml'
}
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above
/*

View file

@ -43,15 +43,17 @@ export LDFLAGS="-pie"
* Install Python 3.6 for Windows
* Open a Git Bash command prompt
* Ensure the following commands are visible in the path with `which <command>`
* gcc
* javac
* python
* gmake
* gcc
* javac
* python
* gmake
* If any of them fail, fix your path and restart the bash prompt
* Fetch the pre-built OpenSSL binaries for Android/iOS from here: https://github.com/leenjewel/openssl_for_ios_and_android/releases
* Grab the latest release of the 1.0.2 series
* Open the archive and extract the `/android/openssl-arm64-v8a` folder
### All platforms
* Download the Qt sources
* `git clone git://code.qt.io/qt/qt5.git`
* `cd qt5`
@ -60,7 +62,11 @@ export LDFLAGS="-pie"
* `git submodule update --recursive`
* `cd ..`
* Create a build directory with the command `mkdir qt5build`
* Configure the Qt5 build with the command `../qt5/configure -xplatform android-clang -android-ndk-host windows-x86_64 -confirm-license -opensource --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-ndk C:/Android/NDK -android-toolchain-version 4.9 -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -platform win32-g++ -prefix C:/qt5build_debug -android-sdk C:/Android/SDK -c++std c++14 -openssl-linked -L<PATH_TO_SSL>/lib -I<PATH_TO_SSL>/include`
* Configure the Qt5 build with the command `../qt5/configure -opensource -confirm-license -xplatform android-clang --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-toolchain-version 4.9 -android-ndk $HOME/Android/NDK -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -android-ndk-host windows-x86_64 -platform win32-g++ -prefix C:/qt5build_debug -android-sdk $HOME/Android/SDK -c++std c++14 -openssl-linked -L<PATH_TO_SSL>/lib -I<PATH_TO_SSL>/include`
* Some of those entries must be customized depending on platform.
* `-platform win32-g++`
* `-android-ndk-host windows-x86_64`
* `-prefix C:/qt5build_debug`

View file

@ -1,17 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

View file

@ -343,7 +343,6 @@ void Agent::scriptRequestFinished() {
void Agent::executeScript() {
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);

View file

@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) {
AnimPose& absPose = absPoses[i];
if (data.rotation != absPose.rot()) {
data.rotation = absPose.rot();
data.rotationSet = true;
data.rotationIsDefaultPose = false;
}
AnimPose& relPose = poses[i];
if (data.translation != relPose.trans()) {
data.translation = relPose.trans();
data.translationSet = true;
data.translationIsDefaultPose = false;
}
}

View file

@ -1,27 +1,22 @@
set(EXTERNAL_NAME glew)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
set(EXTERNAL_NAME glad32es)
include(ExternalProject)
include(SelectLibraryConfigurations)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple_1.13.0.zip
URL_MD5 73f833649e904257b35bf4e84f8bdfb5
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip
URL_MD5 6a641d8c49dee4c895fa59315f5682a6
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glew include directories")
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
if (UNIX)
set(LIB_PREFIX "lib")
@ -30,5 +25,9 @@ elseif (WIN32)
set(LIB_EXT "lib")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glew_d.${LIB_EXT} CACHE FILEPATH "Path to glew debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT})
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT})
select_library_configurations(${EXTERNAL_NAME_UPPER})
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories")
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries")

33
cmake/externals/glad41/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,33 @@
set(EXTERNAL_NAME glad41)
include(ExternalProject)
include(SelectLibraryConfigurations)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip
URL_MD5 1324eeec33abe91e67d19ae551ba624d
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
if (UNIX)
set(LIB_PREFIX "lib")
set(LIB_EXT "a")
elseif (WIN32)
set(LIB_EXT "lib")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT})
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT})
select_library_configurations(${EXTERNAL_NAME_UPPER})
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories")
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries")

33
cmake/externals/glad45/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,33 @@
set(EXTERNAL_NAME glad45)
include(ExternalProject)
include(SelectLibraryConfigurations)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip
URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05
CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_POSITION_INDEPENDENT_CODE=ON
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
if (UNIX)
set(LIB_PREFIX "lib")
set(LIB_EXT "a")
elseif (WIN32)
set(LIB_EXT "lib")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT})
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT})
select_library_configurations(${EXTERNAL_NAME_UPPER})
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories")
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries")

View file

@ -12,9 +12,14 @@ function(GENERATE_QRC)
foreach(GLOB ${GENERATE_QRC_GLOBS})
file(GLOB_RECURSE FOUND_FILES RELATIVE ${GENERATE_QRC_PATH} ${GLOB})
foreach(FILENAME ${FOUND_FILES})
if (${FILENAME} MATCHES "^\\.\\.")
continue()
endif()
list(APPEND ALL_FILES "${GENERATE_QRC_PATH}/${FILENAME}")
set(QRC_CONTENTS "${QRC_CONTENTS}<file alias=\"${FILENAME}\">${GENERATE_QRC_PATH}/${FILENAME}</file>\n")
endforeach()
endforeach()
set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE)
configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT})
endfunction()

View file

@ -0,0 +1,38 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_GLAD)
if (ANDROID)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad)
set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include")
set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a)
set(GLAD_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libglad.a)
select_library_configurations(GLAD)
find_library(EGL EGL)
target_link_libraries(${TARGET_NAME} ${EGL})
else()
if (USE_GLES)
set(GLAD_VER "32es")
else()
set(GLAD_VER "45")
endif()
find_package(OpenGL REQUIRED)
list(APPEND GLAD_EXTRA_LIBRARIES ${OPENGL_LIBRARY})
if (NOT WIN32)
list(APPEND GLAD_EXTRA_LIBRARIES dl)
endif()
set(GLAD "glad${GLAD_VER}")
string(TOUPPER ${GLAD} GLAD_UPPER)
add_dependency_external_projects(${GLAD})
set(GLAD_INCLUDE_DIRS ${${GLAD_UPPER}_INCLUDE_DIRS})
set(GLAD_LIBRARY ${${GLAD_UPPER}_LIBRARY})
endif()
target_include_directories(${TARGET_NAME} PUBLIC ${GLAD_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLAD_LIBRARY})
target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
endmacro()

View file

@ -1,16 +0,0 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_GLEW)
if (NOT ANDROID)
add_definitions(-DGLEW_STATIC)
add_dependency_external_projects(glew)
find_package(GLEW REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY})
endif()
endmacro()

View file

@ -6,20 +6,6 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_OPENGL)
if (APPLE)
# link in required OS X frameworks and include the right GL headers
find_library(OpenGL OpenGL)
target_link_libraries(${TARGET_NAME} ${OpenGL})
elseif(ANDROID)
target_link_libraries(${TARGET_NAME} GLESv3 EGL)
else()
find_package(OpenGL REQUIRED)
if (${OPENGL_INCLUDE_DIR})
include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}")
endif()
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}")
target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR})
endif()
target_glad()
target_nsight()
target_glew()
endmacro()

View file

@ -11,9 +11,20 @@ function(JOIN VALUES GLUE OUTPUT)
set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
endfunction()
set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc)
set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc)
generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *)
add_custom_command(
OUTPUT ${RESOURCES_RCC}
DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS}
COMMAND "${QT_DIR}/bin/rcc"
ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC}
)
list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC})
add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS})
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion")
@ -40,6 +51,7 @@ endif()
# grab the implementation and header files from src dirs
file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h")
GroupSources("src")
list(APPEND INTERFACE_SRCS ${RESOURCES_RCC})
# Add SpeechRecognizer if on Windows or OS X, otherwise remove
if (WIN32)
@ -72,16 +84,6 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}")
# add them to the interface source files
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC})
if (UNIX)
install(
DIRECTORY "${CMAKE_SOURCE_DIR}/interface/resources/qml"
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources
COMPONENT ${CLIENT_COMPONENT}
)
endif()
# translation disabled until we strip out the line numbers
# set(QM ${TARGET_NAME}_en.qm)
# set(TS ${TARGET_NAME}_en.ts)
@ -89,21 +91,7 @@ endif()
# setup the android parameters that will help us produce an APK
if (ANDROID)
set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build")
set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}")
set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME})
set(ANDROID_APP_DISPLAY_NAME Interface)
set(ANDROID_API_LEVEL 19)
set(ANDROID_APK_PACKAGE io.highfidelity.interface)
set(ANDROID_ACTIVITY_NAME io.highfidelity.interface.InterfaceActivity)
set(ANDROID_APK_VERSION_NAME "0.1")
set(ANDROID_APK_VERSION_CODE 1)
set(ANDROID_APK_FULLSCREEN TRUE)
set(ANDROID_DEPLOY_QT_INSTALL "--install")
set(BUILD_SHARED_LIBS ON)
endif ()
@ -175,6 +163,8 @@ else ()
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
endif ()
add_dependencies(${TARGET_NAME} resources)
if (WIN32)
# These are external plugins, but we need to do the 'add dependency' here so that their
# binary directories get added to the fixup path
@ -258,15 +248,20 @@ endforeach()
# include headers for interface and InterfaceConfig.
include_directories("${PROJECT_SOURCE_DIR}/src")
if (ANDROID)
find_library(ANDROID_LOG_LIB log)
target_link_libraries(${TARGET_NAME} ${ANDROID_LOG_LIB})
endif ()
target_link_libraries(
${TARGET_NAME}
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
Qt5::WebChannel Qt5::WebEngine
Qt5::WebChannel
${PLATFORM_QT_LIBRARIES}
)
if (UNIX)
if (UNIX AND NOT ANDROID)
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
# Linux
target_link_libraries(${TARGET_NAME} pthread atomic)
@ -274,7 +269,7 @@ if (UNIX)
# OSX
target_link_libraries(${TARGET_NAME} pthread)
endif ()
endif(UNIX)
endif()
# assume we are using a Qt build without bearer management
add_definitions(-DQT_NO_BEARERMANAGEMENT)
@ -304,12 +299,20 @@ if (APPLE)
# call the fixup_interface macro to add required bundling commands for installation
fixup_interface()
else (APPLE)
else()
# copy the resources files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${RESOURCES_RCC}"
"$<TARGET_FILE_DIR:interface>"
# FIXME, the edit script code loads HTML from the scripts folder
# which in turn relies on CSS that refers to the fonts. In theory
# we should be able to modify the CSS to reference the QRC path to
# the ttf files, but doing so generates a CORS policy violation,
# so we have to retain a copy of the fonts outside of the resources binary
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/resources"
"$<TARGET_FILE_DIR:${TARGET_NAME}>/resources"
"${PROJECT_SOURCE_DIR}/resources/fonts"
"$<TARGET_FILE_DIR:${TARGET_NAME}>/resources/fonts"
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/scripts"
"$<TARGET_FILE_DIR:${TARGET_NAME}>/scripts"
@ -335,7 +338,7 @@ else (APPLE)
optional_win_executable_signing()
endif()
endif (APPLE)
endif()
if (SCRIPTS_INSTALL_DIR)
@ -359,20 +362,6 @@ if (WIN32)
package_libraries_for_deployment()
endif()
if (ANDROID)
set(HIFI_URL_INTENT "<intent-filter>\
\n <action android:name='android.intent.action.VIEW' />\
\n <category android:name='android.intent.category.DEFAULT' />\
\n <category android:name='android.intent.category.BROWSABLE' />\
\n <data android:scheme='hifi' />\
\n </intent-filter>"
)
set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}")
qt_create_apk()
endif ()
add_dependency_external_projects(GifCreator)
find_package(GifCreator REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS})

View file

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
C19,43.3,19.6,36.4,20.9,29.8z"/>
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
C34,59.1,32.8,60,32.3,61.4z"/>
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -0,0 +1,135 @@
name = being_of_light
type = body+head
scale = 1
filename = being_of_light/being_of_light.fbx
texdir = being_of_light/textures
joint = jointRoot = Hips
joint = jointLeftHand = LeftHand
joint = jointHead = HeadTop_End
joint = jointLean = Spine
joint = jointEyeLeft = LeftEye
joint = jointRightHand = RightHand
joint = jointNeck = Head
joint = jointEyeRight = RightEye
freeJoint = LeftArm
freeJoint = LeftForeArm
freeJoint = RightArm
freeJoint = RightForeArm
bs = MouthFrown_L = Frown_Left = 1
bs = MouthLeft = Midmouth_Left = 1
bs = BrowsU_R = BrowsUp_Right = 1
bs = ChinUpperRaise = UpperLipUp_Right = 0.5
bs = ChinUpperRaise = UpperLipUp_Left = 0.5
bs = MouthSmile_R = Smile_Right = 1
bs = MouthDimple_L = Smile_Left = 0.25
bs = EyeBlink_L = Blink_Left = 1
bs = BrowsD_L = BrowsDown_Left = 1
bs = MouthFrown_R = Frown_Right = 1
bs = MouthDimple_R = Smile_Right = 0.25
bs = Sneer = Squint_Right = 0.5
bs = Sneer = Squint_Left = 0.5
bs = Sneer = NoseScrunch_Right = 0.75
bs = Sneer = NoseScrunch_Left = 0.75
bs = EyeSquint_L = Squint_Left = 1
bs = EyeBlink_R = Blink_Right = 1
bs = JawLeft = JawRotateY_Left = 0.5
bs = BrowsD_R = BrowsDown_Right = 1
bs = EyeSquint_R = Squint_Right = 1
bs = Puff = CheekPuff_Right = 1
bs = Puff = CheekPuff_Left = 1
bs = LipsUpperClose = UpperLipIn = 1
bs = JawOpen = MouthOpen = 0.69999999999999996
bs = LipsUpperUp = UpperLipUp_Right = 0.69999999999999996
bs = LipsUpperUp = UpperLipUp_Left = 0.69999999999999996
bs = LipsLowerDown = LowerLipDown_Right = 0.69999999999999996
bs = LipsLowerDown = LowerLipDown_Left = 0.69999999999999996
bs = LipsLowerOpen = LowerLipOut = 1
bs = EyeOpen_L = EyesWide_Left = 1
bs = LipsPucker = MouthNarrow_Right = 1
bs = LipsPucker = MouthNarrow_Left = 1
bs = EyeOpen_R = EyesWide_Right = 1
bs = JawRight = Jaw_Right = 1
bs = MouthRight = Midmouth_Right = 1
bs = ChinLowerRaise = Jaw_Up = 1
bs = LipsUpperOpen = UpperLipOut = 1
bs = BrowsU_C = BrowsUp_Right = 1
bs = BrowsU_C = BrowsUp_Left = 1
bs = JawFwd = JawForeward = 1
bs = BrowsU_L = BrowsUp_Left = 1
bs = MouthSmile_L = Smile_Left = 1
bs = LipsLowerClose = LowerLipIn = 1
bs = LipsFunnel = TongueUp = 1
bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5
bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5
bs = LipsFunnel = MouthNarrow_Right = 1
bs = LipsFunnel = MouthNarrow_Left = 1
bs = LipsFunnel = Jaw_Down = 0.35999999999999999
bs = LipsFunnel = JawForeward = 0.39000000000000001
jointIndex = LeftHandIndex1 = 50
jointIndex = LeftHandIndex2 = 51
jointIndex = LeftHandIndex3 = 52
jointIndex = LeftHandIndex4 = 53
jointIndex = Spine1 = 12
jointIndex = Spine2 = 13
jointIndex = RightHandThumb1 = 18
jointIndex = RightHandThumb2 = 19
jointIndex = RightHandThumb3 = 20
jointIndex = RightHandThumb4 = 21
jointIndex = LeftFoot = 8
jointIndex = LeftForeArm = 40
jointIndex = Neck = 62
jointIndex = Head = 63
jointIndex = Hips = 0
jointIndex = RightHandPinky1 = 30
jointIndex = RightHandPinky2 = 31
jointIndex = RightHandPinky3 = 32
jointIndex = RightHandPinky4 = 33
jointIndex = RightLeg = 2
jointIndex = RightForeArm = 16
jointIndex = LeftHandRing1 = 46
jointIndex = LeftHandRing2 = 47
jointIndex = LeftHandRing3 = 48
jointIndex = LeftHandRing4 = 49
jointIndex = LeftHandThumb1 = 54
jointIndex = LeftHandThumb2 = 55
jointIndex = LeftHandThumb3 = 56
jointIndex = LeftHandThumb4 = 57
jointIndex = HeadTop_End = 66
jointIndex = LeftUpLeg = 6
jointIndex = LeftToeBase = 9
jointIndex = LeftHandPinky1 = 42
jointIndex = LeftHandPinky2 = 43
jointIndex = LeftHandPinky3 = 44
jointIndex = LeftHandPinky4 = 45
jointIndex = LeftLeg = 7
jointIndex = RightEye = 65
jointIndex = RightHand = 17
jointIndex = RightToeBase = 4
jointIndex = RightUpLeg = 1
jointIndex = RightArm = 15
jointIndex = RightHandRing1 = 26
jointIndex = RightHandRing2 = 27
jointIndex = RightHandRing3 = 28
jointIndex = RightHandRing4 = 29
jointIndex = RightHandIndex1 = 22
jointIndex = RightHandIndex2 = 23
jointIndex = RightHandIndex3 = 24
jointIndex = RightHandIndex4 = 25
jointIndex = LeftToe_End = 10
jointIndex = LeftHandMiddle1 = 58
jointIndex = LeftHandMiddle2 = 59
jointIndex = LeftHandMiddle3 = 60
jointIndex = LeftShoulder = 38
jointIndex = LeftHandMiddle4 = 61
jointIndex = RightFoot = 3
jointIndex = LeftHand = 41
jointIndex = RightHandMiddle1 = 34
jointIndex = RightHandMiddle2 = 35
jointIndex = RightHandMiddle3 = 36
jointIndex = RightShoulder = 14
jointIndex = LeftEye = 64
jointIndex = RightHandMiddle4 = 37
jointIndex = Body = 67
jointIndex = LeftArm = 39
jointIndex = RightToe_End = 5
jointIndex = Spine = 11

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View file

@ -0,0 +1,394 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3
import '.'
Item {
id: stats
anchors.leftMargin: 300
objectName: "StatsItem"
property int modality: Qt.NonModal
implicitHeight: row.height
implicitWidth: row.width
Component.onCompleted: {
stats.parentChanged.connect(fill);
fill();
}
Component.onDestruction: {
stats.parentChanged.disconnect(fill);
}
function fill() {
// This will cause a warning at shutdown, need to find another way to remove
// the warning other than filling the anchors to the parent
anchors.horizontalCenter = parent.horizontalCenter
}
Hifi.Stats {
id: root
objectName: "Stats"
implicitHeight: row.height
implicitWidth: row.width
anchors.horizontalCenter: parent.horizontalCenter
readonly property string bgColor: "#AA111111"
Row {
id: row
spacing: 8
Rectangle {
width: generalCol.width + 8;
height: generalCol.height + 8;
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: generalCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Servers: " + root.serverCount
}
StatText {
text: "Avatars: " + root.avatarCount
}
StatText {
text: "Game Rate: " + root.gameLoopRate
}
StatText {
visible: root.expanded
text: root.gameUpdateStats
}
StatText {
text: "Render Rate: " + root.renderrate.toFixed(2);
}
StatText {
text: "Present Rate: " + root.presentrate.toFixed(2);
}
StatText {
visible: root.expanded
text: " Present New Rate: " + root.presentnewrate.toFixed(2);
}
StatText {
visible: root.expanded
text: " Present Drop Rate: " + root.presentdroprate.toFixed(2);
}
StatText {
text: "Stutter Rate: " + root.stutterrate.toFixed(3);
visible: root.stutterrate != -1;
}
StatText {
text: "Missed Frame Count: " + root.appdropped;
visible: root.appdropped > 0;
}
StatText {
text: "Long Render Count: " + root.longrenders;
visible: root.longrenders > 0;
}
StatText {
text: "Long Submit Count: " + root.longsubmits;
visible: root.longsubmits > 0;
}
StatText {
text: "Long Frame Count: " + root.longframes;
visible: root.longframes > 0;
}
StatText {
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
}
StatText {
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
}
StatText {
visible: root.expanded
text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2)
}
StatText {
visible: root.expanded
text: "Avatars Updated: " + root.updatedAvatarCount
}
StatText {
visible: root.expanded
text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount
}
}
}
Rectangle {
width: pingCol.width + 8
height: pingCol.height + 8
color: root.bgColor;
visible: root.audioPing != -2
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: pingCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Audio ping/loss: " + root.audioPing + "/" + root.audioPacketLoss + "%"
}
StatText {
text: "Avatar ping: " + root.avatarPing
}
StatText {
text: "Entities avg ping: " + root.entitiesPing
}
StatText {
text: "Asset ping: " + root.assetPing
}
StatText {
visible: root.expanded;
text: "Messages max ping: " + root.messagePing
}
}
}
Rectangle {
width: geoCol.width + 8
height: geoCol.height + 8
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: geoCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Position: " + root.position.x.toFixed(1) + ", " +
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
}
StatText {
text: "Speed: " + root.speed.toFixed(1)
}
StatText {
text: "Yaw: " + root.yaw.toFixed(1)
}
StatText {
visible: root.expanded;
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +
root.avatarMixerInPps + "pps";
}
StatText {
visible: root.expanded;
text: "Avatar Mixer Out: " + root.avatarMixerOutKbps + " kbps, " +
root.avatarMixerOutPps + "pps, " +
root.myAvatarSendRate.toFixed(2) + "hz";
}
StatText {
visible: root.expanded;
text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " +
root.audioMixerInPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " +
"Silent: " + root.audioSilentInboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " +
root.audioMixerOutPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " +
"Silent: " + root.audioSilentOutboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Codec: " + root.audioCodec + " Noise Gate: " +
root.audioNoiseGate;
}
StatText {
visible: root.expanded;
text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps";
}
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
", Pending: " + root.downloadsPending;
}
StatText {
visible: root.expanded;
text: "Processing: " + root.processing +
", Pending: " + root.processingPending;
}
StatText {
visible: root.expanded && root.downloadUrls.length > 0;
text: "Download URLs:"
}
ListView {
width: geoCol.width
height: root.downloadUrls.length * 15
visible: root.expanded && root.downloadUrls.length > 0;
model: root.downloadUrls
delegate: StatText {
visible: root.expanded;
text: modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData
}
}
}
}
Rectangle {
width: octreeCol.width + 8
height: octreeCol.height + 8
color: root.bgColor;
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
hoverEnabled: true
}
Column {
id: octreeCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Engine: " + root.engineFrameTime.toFixed(1) + " ms"
}
StatText {
text: "Batch: " + root.batchFrameTime.toFixed(1) + " ms"
}
StatText {
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
StatText {
text: "Triangles: " + root.triangles +
" / Material Switches: " + root.materialSwitches
}
StatText {
text: "GPU Free Memory: " + root.gpuFreeMemory + " MB";
}
StatText {
text: "GPU Textures: ";
}
StatText {
text: " Count: " + root.gpuTextures;
}
StatText {
text: " Pressure State: " + root.gpuTextureMemoryPressureState;
}
StatText {
text: " Resource Allocated / Populated / Pending: ";
}
StatText {
text: " " + root.gpuTextureResourceMemory + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB";
}
StatText {
text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB";
}
StatText {
text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB";
}
StatText {
text: " External Memory: " + root.gpuTextureExternalMemory + " MB";
}
StatText {
text: "GPU Buffers: "
}
StatText {
text: " Count: " + root.gpuBuffers;
}
StatText {
text: " Memory: " + root.gpuBufferMemory + " MB";
}
StatText {
text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB";
}
StatText {
text: "QML Texture Memory: " + root.qmlTextureMemory + " MB";
}
StatText {
visible: root.expanded;
text: "Items rendered / considered: " +
root.itemRendered + " / " + root.itemConsidered;
}
StatText {
visible: root.expanded;
text: " out of view: " + root.itemOutOfView +
" too small: " + root.itemTooSmall;
}
StatText {
visible: root.expanded;
text: "Shadows rendered / considered: " +
root.shadowRendered + " / " + root.shadowConsidered;
}
StatText {
visible: root.expanded;
text: " out of view: " + root.shadowOutOfView +
" too small: " + root.shadowTooSmall;
}
StatText {
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
}
StatText {
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
}
StatText {
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
}
StatText {
visible: root.expanded
text: "Octree Elements - ";
}
StatText {
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
}
StatText {
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
}
StatText {
visible: root.expanded
text: "LOD: " + root.lodStatus;
}
}
}
}
Rectangle {
y: 250
visible: root.timingExpanded
width: perfText.width + 8
height: perfText.height + 8
color: root.bgColor;
StatText {
x: 4; y: 4
id: perfText
font.family: root.monospaceFont
text: "------------------------------------------ Function " +
"--------------------------------------- --msecs- -calls--\n" +
root.timingStats;
}
}
Connections {
target: root.parent
onWidthChanged: {
root.x = root.parent.width - root.width;
}
}
}
}

View file

@ -16,10 +16,9 @@ import "controls-uit" as HifiControls
Item {
id: root
width: parent.width
height: parent.height
height: parent.height
property var hideQtMethods: true
property var maxUpdateValues: 20
property var maxReloadValues: 200
property var apiMembers: []
@ -30,7 +29,7 @@ Item {
property Component keyboard
Rectangle {
color: "white"
color: hifi.colors.baseGray
width: parent.width
height: parent.height
}
@ -51,32 +50,22 @@ Item {
Row {
id: topBar
anchors.left: parent.left
anchors.leftMargin: 8
anchors.leftMargin: 30
anchors.top: parent.top
anchors.topMargin: 30
width: parent.width
height: 50
HifiControls.GlyphButton {
id: search
enabled: true
glyph: hifi.glyphs.search
color: hifi.colors.text
size: 48
width: 50
height: 50
onClicked: {
addListElements(searchBar.text);
focus = true;
}
}
height: 40
HifiControls.GlyphButton {
id: back;
enabled: true;
color: hifi.buttons.black
glyph: hifi.glyphs.backward
color: hifi.colors.text
size: 48
width: 30
height: 50
anchors.margins: 2
size: 40
width: 40
height: 40
anchors.left: search.right
anchors.leftMargin: 12
onClicked: {
var text = searchBar.text;
var chain = text.split(".");
@ -99,17 +88,20 @@ Item {
}
}
TextField {
id: searchBar
HifiControls.TextField {
id: searchBar
focus: true
font.pixelSize: 16
width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
isSearchField: true
width: parent.width - 112
height: 40
colorScheme: hifi.colorSchemes.dark
anchors.left: back.right
anchors.leftMargin: 10
font.family: firaSansSemiBold.name
placeholderText: "Search"
onAccepted: {
console.log("Enter Pressed");
search.clicked();
addListElements(searchBar.text);
}
onActiveFocusChanged: {
if (activeFocus && HMD.mounted) {
@ -119,15 +111,27 @@ Item {
}
}
}
}
}
HifiControls.Button {
Row {
id: topBar2
anchors.left: parent.left
anchors.leftMargin: 30
anchors.top: topBar.bottom
anchors.topMargin: 30
width: parent.width -60
height: 40
HifiControls.GlyphButton {
id: addMember;
enabled: true;
text: "+"
width: 50
height: 50
anchors.margins: 2
color: hifi.buttons.black
glyph: hifi.glyphs.maximize
width: 40
height: 40
anchors.top: parent.top
anchors.left: parent.left
onClicked: {
addNewMember();
updateList.start();
@ -138,36 +142,48 @@ Item {
HifiControls.Button {
id: evaluate;
enabled: true;
color: hifi.buttons.black
text: "Eval"
width: 50
height: 50
anchors.margins: 2
width: 40
height: 40
anchors.left: addMember.right
anchors.leftMargin: 12
onClicked: {
evaluateMember();
focus = true;
}
}
TextField {
id: valueBar
focus: true
HifiControls.TextField {
id: valueBar
isSearchField: false
font.pixelSize: 16
width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
width: parent.width - 208
height: 40
colorScheme: hifi.colorSchemes.dark
font.family: firaSansSemiBold.name
placeholderText: "Value"
textColor: "#4466DD"
anchors.margins: 2
anchors.left: evaluate.right
anchors.leftMargin: 12
onActiveFocusChanged: {
if (activeFocus && HMD.mounted) {
keyboard.raised = true;
} else {
keyboard.raised = false;
}
}
}
HifiControls.GlyphButton {
id: reload;
enabled: false;
color: hifi.buttons.black
glyph: hifi.glyphs.reload
color: hifi.colors.text
size: 48
width: 50
height: 50
anchors.margins: 2
size: 40
width: 40
height: 40
anchors.right: update.left
anchors.rightMargin: 12
onClicked: {
reloadListValues();
focus = true;
@ -177,11 +193,12 @@ Item {
HifiControls.GlyphButton {
id: update;
enabled: false;
color: hifi.buttons.black
glyph: hifi.glyphs.playback_play
size: 48
width: 50
height: 50
anchors.margins: 2
size: 40
width: 40
height: 40
anchors.right: parent.right
onClicked: {
if (isReloading) {
update.glyph = hifi.glyphs.playback_play
@ -196,71 +213,104 @@ Item {
}
}
}
ListModel {
id: memberModel
}
Component {
id: memberDelegate
Row {
id: memberRow
property var isMainKey: apiType === "class";
spacing: 10
Rectangle {
width: isMainKey ? 20 : 40;
height: parent.height
}
RalewayRegular {
text: apiMember
size: !isMainKey ? 16 : 22
MouseArea {
width: list.width
height: parent.height
onClicked: {
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
valueBar.text = !apiValue ? "" : apiValue;
list.currentIndex = index;
evaluatingIdx = index;
}
onDoubleClicked: {
if (apiType === "class") {
addListElements(apiMember+".");
} else {
isolateElement(evaluatingIdx);
}
}
}
}
RalewayRegular {
text: apiType
size: 14
color: hifi.colors.baseGrayHighlight
}
RalewayRegular {
text: !apiValue ? "" : apiValue;
size: 16
color: "#4466DD"
}
}
}
Rectangle {
id: membersBackground
anchors {
left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom;
margins: hifi.dimensions.contentMargin.x
bottomMargin: hifi.dimensions.contentSpacing.y + 40
left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top;
margins: 30
}
color: hifi.colors.tableBackgroundDark
border.color: hifi.colors.lightGray
border.width: 2
radius: 5
ListModel {
id: memberModel
}
Component {
id: memberDelegate
Item {
id: item
width: parent.width
anchors.left: parent.left
height: 26
clip: true
Rectangle {
width: parent.width
height: parent.height
color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd
anchors.verticalCenter: parent.verticalCenter
Row {
id: memberRow
anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter
spacing: 10
FiraSansSemiBold {
property var isMainKey: apiType === "class";
text: apiMember
size: isMainKey ? 17 : 15
font.bold: true
anchors.verticalCenter: parent.verticalCenter
color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText
MouseArea {
width: list.width
height: parent.height
onClicked: {
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
valueBar.text = !apiValue ? "" : apiValue;
list.currentIndex = index;
evaluatingIdx = index;
}
onDoubleClicked: {
if (apiType === "class") {
addListElements(apiMember+".");
} else {
isolateElement(evaluatingIdx);
}
}
}
}
FiraSansRegular {
text: apiType
anchors.left: apiMember.right
anchors.verticalCenter: parent.verticalCenter
size: 13
color: hifi.colors.lightGrayText
}
FiraSansRegular {
text: !apiValue ? "" : apiValue;
anchors.left: apiType.right
anchors.verticalCenter: parent.verticalCenter
size: 14
color: hifi.colors.primaryHighlight
}
}
}
}
}
Component {
id: highlight
Rectangle {
anchors {
left: list.left
right: scrollBar.left
leftMargin: 2
rightMargin: 2
}
color: hifi.colors.primaryHighlight
radius: 4
z: 10
opacity: 0.5
}
}
color: "white"
radius: 4
ListView {
id: list
@ -269,23 +319,16 @@ Item {
left: parent.left
right: scrollBar.left
bottom: parent.bottom
margins: 4
topMargin: 2
leftMargin: 2
bottomMargin: 2
}
clip: true
cacheBuffer: 4000
model: memberModel
delegate: memberDelegate
highlightMoveDuration: 0
highlight: Rectangle {
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
leftMargin: hifi.dimensions.borderWidth
rightMargin: hifi.dimensions.borderWidth
}
color: "#BBDDFF"
}
highlight: highlight
onMovementStarted: {
scrollSlider.manual = true;
}
@ -310,12 +353,11 @@ Item {
top: parent.top
right: parent.right
bottom: parent.bottom
topMargin: 4
bottomMargin: 4
margins: 2
}
width: scrolling ? 18 : 0
radius: 4
color: hifi.colors.baseGrayShadow
width: 22
height: parent.height - 4
color: hifi.colors.tableScrollBackgroundDark
MouseArea {
anchors.fill: parent
@ -344,14 +386,12 @@ Item {
y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1);
}
anchors {
right: parent.right
rightMargin: 3
}
width: 12
height: (list.height / list.contentHeight) * list.height
radius: width / 4
color: "white"
anchors.right: parent.right
anchors.margins: 2
width: 18
height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height
radius: 5
color: hifi.colors.tableScrollHandleDark
visible: scrollBar.scrolling;
@ -373,66 +413,75 @@ Item {
}
}
}
HifiControls.GlyphButton {
id: clipboard;
enabled: true;
glyph: hifi.glyphs.scriptNew
size: 38
width: 50
height: 50
Row {
id: bottomBar
anchors.left: parent.left
anchors.leftMargin: 30
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.leftMargin: 8
onClicked: {
var buffer = "";
for (var i = 0; i < memberModel.count; i++) {
var datarow = memberModel.get(i);
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
}
Window.copyToClipboard(buffer);
focus = true;
}
}
HifiControls.Button {
id: debug;
enabled: true;
text: "Debug Script"
width: 120
height: 50
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.rightMargin: 8
onClicked: {
sendToScript({type: "selectScript"});
}
}
anchors.bottomMargin: 30
width: parent.width
height: 40
HifiControls.CheckBox {
id: hideQt
boxSize: 25
boxRadius: 3
checked: true
anchors.left: clipboard.right
anchors.leftMargin: 8
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
onClicked: {
hideQtMethods = checked;
addListElements();
HifiControls.GlyphButton {
id: clipboard;
enabled: true;
color: hifi.buttons.black
glyph: hifi.glyphs.scriptNew
size: 25
width: 40
height: 40
anchors.left: parent.left
onClicked: {
var buffer = "";
for (var i = 0; i < memberModel.count; i++) {
var datarow = memberModel.get(i);
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
}
Window.copyToClipboard(buffer);
focus = true;
}
}
}
HifiControls.Label {
id: hideLabel
anchors.left: hideQt.right
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
font.pixelSize: 15
text: "Hide Qt Methods"
HifiControls.CheckBox {
id: hideQt
colorScheme: hifi.checkbox.dark
boxSize: 25
boxRadius: 3
checked: true
anchors.left: clipboard.right
anchors.leftMargin: 10
anchors.verticalCenter: clipboard.verticalCenter
onClicked: {
hideQtMethods = checked;
addListElements();
}
}
HifiControls.Label {
id: hideLabel
anchors.left: hideQt.right
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
font.pixelSize: 15
text: "Hide Qt Methods"
}
HifiControls.Button {
id: debug;
enabled: true;
color: hifi.buttons.black
text: "Debug Script"
width: 120
height: 40
anchors.right: parent.right
anchors.rightMargin: 60
anchors.bottom: parent.bottom
onClicked: {
sendToScript({type: "selectScript"});
}
}
}
HifiControls.Keyboard {
@ -639,4 +688,4 @@ Item {
}
signal sendToScript(var message);
}
}

View file

@ -1,256 +0,0 @@
//
// ToolWindow.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import Qt.labs.settings 1.0
import "windows"
import "controls-uit"
import "styles-uit"
ScrollingWindow {
id: toolWindow
resizable: true
objectName: "ToolWindow"
destroyOnCloseButton: false
destroyOnHidden: false
closable: true
shown: false
title: "Edit"
property alias tabView: tabView
implicitWidth: 520; implicitHeight: 695
minSize: Qt.vector2d(456, 500)
HifiConstants { id: hifi }
onParentChanged: {
if (parent) {
x = 120;
y = 120;
}
}
onShownChanged: {
keyboardEnabled = HMD.active;
}
Settings {
category: "ToolWindow.Position"
property alias x: toolWindow.x
property alias y: toolWindow.y
}
Item {
id: toolWindowTabViewItem
height: pane.scrollHeight
width: pane.contentWidth
anchors.left: parent.left
anchors.top: parent.top
TabView {
id: tabView
width: pane.contentWidth
// Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
height: pane.scrollHeight
property int tabCount: 0
Repeater {
model: 4
Tab {
// Force loading of the content even if the tab is not visible
// (required for letting the C++ code access the webview)
active: true
enabled: false
property string originalUrl: ""
WebView {
id: webView
anchors.fill: parent
enabled: false
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
onEnabledChanged: toolWindow.updateVisiblity()
}
}
}
style: TabViewStyle {
frame: Rectangle { // Background shown before content loads.
anchors.fill: parent
color: hifi.colors.baseGray
}
frameOverlap: 0
tab: Rectangle {
implicitWidth: text.width
implicitHeight: 3 * text.height
color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark
RalewayRegular {
id: text
text: styleData.title
font.capitalization: Font.AllUppercase
size: hifi.fontSizes.tabName
width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x
elide: Text.ElideRight
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
}
Rectangle { // Separator.
width: 1
height: parent.height
color: hifi.colors.black
anchors.left: parent.left
anchors.top: parent.top
visible: styleData.index > 0
Rectangle {
width: 1
height: 1
color: hifi.colors.baseGray
anchors.left: parent.left
anchors.bottom: parent.bottom
}
}
Rectangle { // Active underline.
width: parent.width - (styleData.index > 0 ? 1 : 0)
height: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray
}
}
tabOverlap: 0
}
}
}
function updateVisiblity() {
if (visible) {
for (var i = 0; i < tabView.count; ++i) {
if (tabView.getTab(i).enabled) {
return;
}
}
shown = false;
}
}
function findIndexForUrl(source) {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab.originalUrl === source) {
return i;
}
}
return -1;
}
function findTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
return tabView.getTab(index);
}
function showTabForUrl(source, newVisible) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
if (newVisible) {
toolWindow.shown = true
tab.enabled = true
} else {
tab.enabled = false;
updateVisiblity();
}
}
function findFreeTab() {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab && (!tab.originalUrl || tab.originalUrl === "")) {
return i;
}
}
return -1;
}
function removeTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
tab.title = "";
tab.enabled = false;
tab.originalUrl = "";
tab.item.url = "about:blank";
tab.item.enabled = false;
tabView.tabCount--;
}
function addWebTab(properties) {
if (!properties.source) {
console.warn("Attempted to open Web Tool Pane without URL");
return;
}
var existingTabIndex = findIndexForUrl(properties.source);
if (existingTabIndex >= 0) {
var tab = tabView.getTab(existingTabIndex);
return tab.item;
}
var freeTabIndex = findFreeTab();
if (freeTabIndex === -1) {
console.warn("Unable to add new tab");
return;
}
if (properties.width) {
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
}
if (properties.height) {
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
}
var tab = tabView.getTab(freeTabIndex);
tab.title = properties.title || "Unknown";
tab.enabled = true;
tab.originalUrl = properties.source;
var result = tab.item;
result.enabled = true;
tabView.tabCount++;
result.url = properties.source;
return result;
}
}

View file

@ -24,13 +24,18 @@ TextField {
property bool isSearchField: false
property string label: ""
property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0)
property bool hasDefocusedBorder: true;
property bool hasRoundedBorder: false
property int roundedBorderRadius: 4
property bool error: false;
property bool hasClearButton: false;
property string leftPermanentGlyph: "";
property string centerPlaceholderGlyph: "";
placeholderText: textField.placeholderText
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: hifi.fontSizes.textFieldInput
font.italic: textField.text == ""
@ -54,6 +59,7 @@ TextField {
}
style: TextFieldStyle {
id: style;
textColor: {
if (isLightColorScheme) {
if (textField.activeFocus) {
@ -98,9 +104,28 @@ TextField {
}
}
border.color: textField.error ? hifi.colors.redHighlight :
(textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray))
(textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color))
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0)
HiFiGlyphs {
text: textField.leftPermanentGlyph;
color: textColor;
size: hifi.fontSizes.textFieldSearchIcon;
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
anchors.leftMargin: hifi.dimensions.textPadding - 2;
visible: text;
}
HiFiGlyphs {
text: textField.centerPlaceholderGlyph;
color: textColor;
size: parent.height;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
visible: text && !textField.focus && textField.text === "";
}
HiFiGlyphs {
text: hifi.glyphs.search
@ -132,7 +157,7 @@ TextField {
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
}

View file

@ -0,0 +1,63 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../desktop" as OriginalDesktop
import ".."
import "."
import "./toolbars"
OriginalDesktop.Desktop {
id: desktop
MouseArea {
id: hoverWatch
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
scrollGestureEnabled: false // we don't need/want these
onEntered: ApplicationCompositor.reticleOverDesktop = true
onExited: ApplicationCompositor.reticleOverDesktop = false
acceptedButtons: Qt.NoButton
}
Component { id: toolbarBuilder; Toolbar { } }
// This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted.
// Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got
// wiped during startup.
Toolbar {
id: sysToolbar;
objectName: "com.highfidelity.interface.toolbar.system";
// Magic: sysToolbar.x and y come from settings, and are bound before the properties specified here are applied.
x: sysToolbar.x;
y: sysToolbar.y;
}
property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar
map[sysToolbar.objectName] = sysToolbar;
return map; })({});
Component.onCompleted: {
}
// Accept a download through the webview
property bool webViewProfileSetup: false
property string currentUrl: ""
property string adaptedPath: ""
property string tempDir: ""
// Create or fetch a toolbar with the given name
function getToolbar(name) {
var result = toolbars[name];
if (!result) {
result = toolbars[name] = toolbarBuilder.createObject(desktop, {});
result.objectName = name;
}
return result;
}
}

View file

@ -22,10 +22,6 @@ OriginalDesktop.Desktop {
acceptedButtons: Qt.NoButton
}
// The tool window, one instance
property alias toolWindow: toolWindow
ToolWindow { id: toolWindow }
Action {
text: "Open Browser"
shortcut: "Ctrl+B"

View file

@ -102,7 +102,7 @@ Column {
'include_actions=' + actions,
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
'require_online=true',
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
'protocol=' + encodeURIComponent(Window.protocolSignature()),
'page=' + pageNumber
];
var url = metaverseBase + 'user_stories?' + options.join('&');

View file

@ -1,73 +0,0 @@
//
// SendMoney.qml
// qml/hifi/commerce/wallet
//
// SendMoney
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Connections {
target: Commerce;
}
// "Unavailable"
RalewayRegular {
text: "You currently cannot send money to other High Fidelity users.";
// Anchors
anchors.fill: parent;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -19,8 +19,7 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
import "./sendMoney"
Rectangle {
HifiConstants { id: hifi; }
@ -316,18 +315,29 @@ Rectangle {
Connections {
onSendSignalToWallet: {
sendToScript(msg);
if (msg.method === 'transactionHistory_usernameLinkClicked') {
userInfoViewer.url = msg.usernameLink;
userInfoViewer.visible = true;
} else {
sendToScript(msg);
}
}
}
}
SendMoney {
id: sendMoney;
z: 997;
visible: root.activeView === "sendMoney";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.fill: parent;
parentAppTitleBarHeight: titleBarContainer.height;
parentAppNavBarHeight: tabButtonsContainer.height;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
Security {
@ -379,7 +389,7 @@ Rectangle {
//
Item {
id: tabButtonsContainer;
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange";
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep";
property int numTabs: 5;
// Size
width: root.width;
@ -497,7 +507,7 @@ Rectangle {
Rectangle {
id: sendMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
@ -513,7 +523,7 @@ Rectangle {
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
@ -528,12 +538,24 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
id: sendMoneyTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "sendMoney";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
// "SECURITY" tab button
@ -665,9 +687,16 @@ Rectangle {
// TAB BUTTONS END
//
HifiControls.TabletWebView {
id: userInfoViewer;
z: 998;
anchors.fill: parent;
visible: false;
}
Item {
id: keyboardContainer;
z: 998;
z: 999;
visible: keyboard.raised;
property bool punctuationMode: false;
anchors {
@ -713,6 +742,13 @@ Rectangle {
case 'inspectionCertificate_resetCert':
// NOP
break;
case 'updateConnections':
sendMoney.updateConnections(message.connections);
break;
case 'selectRecipient':
case 'updateSelectedRecipientUsername':
sendMoney.fromScript(message);
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}

View file

@ -19,8 +19,6 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
@ -32,6 +30,20 @@ Item {
property int currentHistoryPage: 1;
property var pagesAlreadyAdded: new Array();
onVisibleChanged: {
if (visible) {
transactionHistoryModel.clear();
Commerce.balance();
initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else {
refreshTimer.stop();
}
}
Connections {
target: Commerce;
@ -189,20 +201,6 @@ Item {
color: hifi.colors.white;
// Alignment
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
transactionHistoryModel.clear();
Commerce.balance();
initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else {
refreshTimer.stop();
}
}
}
// "balance" text below field
@ -384,8 +382,8 @@ Item {
height: visible ? parent.height : 0;
AnonymousProRegular {
id: dateText;
text: model.created_at ? getFormattedDate(model.created_at * 1000) : "";
id: hfcText;
text: model.hfc_text || '';
// Style
size: 18;
anchors.left: parent.left;
@ -393,7 +391,6 @@ Item {
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignRight;
@ -401,20 +398,25 @@ Item {
AnonymousProRegular {
id: transactionText;
text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : "";
text: model.transaction_text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.transaction_text) : model.transaction_text) : "";
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.left: hfcText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
linkColor: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
font.strikeout: model.status === "invalidated";
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
if (link.indexOf("users/") !== -1) {
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
} else {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
}
}
}

View file

@ -0,0 +1,128 @@
//
// ConnectionItem.qml
// qml/hifi/commerce/wallet/sendMoney
//
// ConnectionItem
//
// Created by Zach Fox on 2018-01-09
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../../../../styles-uit"
import "../../../../controls-uit" as HifiControlsUit
import "../../../../controls" as HifiControls
import "../../wallet" as HifiWallet
Item {
HifiConstants { id: hifi; }
id: root;
property bool isSelected: false;
property string userName;
property string profilePicUrl;
height: 75;
width: parent.width;
Rectangle {
id: mainContainer;
// Style
color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white;
// Size
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
height: root.height;
Item {
id: avatarImage;
visible: profileUrl !== "" && userName !== "";
// Size
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 36;
height: 50;
width: visible ? height : 0;
clip: true;
Image {
id: userImage;
source: root.profilePicUrl !== "" ? ((0 === root.profilePicUrl.indexOf("http")) ?
root.profilePicUrl : (Account.metaverseServerURL + root.profilePicUrl)) : "";
mipmap: true;
// Anchors
anchors.fill: parent
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
AnimatedImage {
source: "../../../../../icons/profilePicLoading.gif"
anchors.fill: parent;
visible: userImage.status != Image.Ready;
}
}
RalewaySemiBold {
id: userName;
anchors.left: avatarImage.right;
anchors.leftMargin: 12;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: chooseButton.visible ? chooseButton.left : parent.right;
anchors.rightMargin: chooseButton.visible ? 10 : 0;
// Text size
size: 18;
// Style
color: hifi.colors.blueAccent;
text: root.userName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
// "Choose" button
HifiControlsUit.Button {
id: chooseButton;
visible: root.isSelected;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.verticalCenter: parent.verticalCenter;
anchors.right: parent.right;
anchors.rightMargin: 28;
height: 35;
width: 100;
text: "CHOOSE";
onClicked: {
var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl };
sendToSendMoney(msg);
}
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToSendMoney(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,117 @@
//
// RecipientDisplay.qml
// qml/hifi/commerce/wallet/sendMoney
//
// RecipientDisplay
//
// Created by Zach Fox on 2018-01-11
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import "../../../../styles-uit"
import "../../../../controls-uit" as HifiControlsUit
import "../../../../controls" as HifiControls
import "../../common" as HifiCommerceCommon
Item {
HifiConstants { id: hifi; }
id: root;
property bool isDisplayingNearby; // as opposed to 'connections'
property string displayName;
property string userName;
property string profilePic;
property string textColor: hifi.colors.white;
Item {
visible: root.isDisplayingNearby;
anchors.fill: parent;
RalewaySemiBold {
id: recipientDisplayName;
text: root.displayName;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.rightMargin: 12;
height: parent.height/2;
// Text size
size: 18;
// Style
color: root.textColor;
verticalAlignment: Text.AlignBottom;
elide: Text.ElideRight;
}
RalewaySemiBold {
text: root.userName;
// Anchors
anchors.bottom: parent.bottom;
anchors.left: recipientDisplayName.anchors.left;
anchors.leftMargin: recipientDisplayName.anchors.leftMargin;
anchors.right: recipientDisplayName.anchors.right;
anchors.rightMargin: recipientDisplayName.anchors.rightMargin;
height: parent.height/2;
// Text size
size: 16;
// Style
color: root.textColor;
verticalAlignment: Text.AlignTop;
elide: Text.ElideRight;
}
}
Item {
visible: !root.isDisplayingNearby;
anchors.fill: parent;
Image {
id: userImage;
source: root.profilePic;
mipmap: true;
// Anchors
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 36;
width: height;
layer.enabled: true;
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
RalewaySemiBold {
text: root.userName;
// Anchors
anchors.left: userImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 4;
// Text size
size: 16;
// Style
color: root.textColor;
verticalAlignment: Text.AlignVCenter;
elide: Text.ElideRight;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1398BB;}
</style>
<path class="st0" d="M256.8,42C138.5,42,42.2,138.3,42.2,256.6s96.3,214.6,214.6,214.6c118.3,0,214.6-96.3,214.6-214.6
S375.1,42,256.8,42z M256.8,444.4C153.2,444.4,69,360.1,69,256.6C69,153,153.2,68.7,256.8,68.7c103.6,0,187.8,84.3,187.8,187.8
C444.6,360.1,360.4,444.4,256.8,444.4z"/>
<circle class="st0" cx="260.6" cy="189.4" r="60.6"/>
<path class="st0" d="M306.4,282.6h-87.6c-36.5,0-66.4,30.2-66.4,66.7v33.9c29.3,22.6,65.4,36.1,105,36.1c44.4,0,84.7-17,115.2-44.7
v-25.3C372.7,312.7,342.9,282.6,306.4,282.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1398BB;}
</style>
<path class="st0" d="M144.3,155c-8.7-8.8-16.7-17.1-25.2-25.7c-22.4,25.4-36.6,53.9-43.6,86.2c-26.9,125.7,81.2,241.5,208.6,223.4
C385.9,424.4,458,329.2,443.5,228.2c-4.4-30.6-15.4-58.9-34.1-83.9c-1.6-2.2-3.3-4.3-4.7-6.6c-4.4-7.4-3.1-14.9,3-19.5
c6-4.5,14.2-3.6,19.2,3.5c8.2,11.7,16.6,23.4,22.9,36.1c41.9,84.9,25.7,181.7-41,248.7c-67,67.4-175.1,81.3-257.1,33.3
c-35.3-20.7-63.1-48.6-82.7-84.5c-41-75-31.5-172.5,23.4-237.9c2.2-2.7,4.4-5.4,7.1-8.7c-8.1-8.5-16.1-16.9-24-25.3
c-7.2-7.7-7.9-16.1-1.9-22.1c5.9-5.9,15-5.1,22.1,2.4c56.8,59.5,113.6,119,170.4,178.6c7.2,7.6,7.8,15.4,1.9,21.4
c-6.1,6.2-14.7,5.5-22-2c-10.2-10.4-20.2-21-30.3-31.5c-17.4,24.7-3.3,62,27.2,72.5c23,7.9,48.8-2,60.7-23.1
c1.2-2.1,2.3-4.3,3.7-6.3c4.5-6.6,11.7-8.6,18.3-5c6.6,3.5,8.9,10.5,5.9,18.1c-12.4,31.6-47.9,52.2-82.3,47.7
c-36.1-4.8-64.3-32.6-68.4-67.7c-2.4-20.7,2.4-39.7,14.9-57.5c-10.4-10.9-20.8-21.6-31.7-33c-9.7,10.8-16.2,22.7-20.9,35.7
c-31.3,86.4,38.9,175.1,130.1,164.3c74-8.7,122.3-80.9,103.3-154.3c-3.4-13.1-1-20.2,7.8-22.7c9.5-2.8,15.2,1.8,19.2,15.4
c20.1,67.4-16,144.8-79.8,175.3c-67.5,32.2-147.5,9.7-188.8-49c-37.8-53.8-36.1-127.7,4.7-179.1C141.2,159.4,142.5,157.4,144.3,155z
"/>
<path class="st0" d="M236.2,262.2c-4.2-13.6,3.4-28.2,16.8-32.4c13.5-4.2,28.1,3.3,32.5,16.8c4.5,13.9-3.1,28.4-17.2,32.7
C254.6,283.5,240.4,275.9,236.2,262.2z"/>
<path class="st0" d="M319.6,101.9c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
C334.5,118.5,323.3,112.9,319.6,101.9z"/>
<path class="st0" d="M214.9,66.2c3.2,10.2-2.2,20.5-12.5,23.6c-9.8,3-20-2.3-23.3-12c-3.3-9.9,2.2-20.8,12-24
C201.1,50.5,211.8,56.2,214.9,66.2z"/>
<path class="st0" d="M230.8,164.6c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
C245.7,181.2,234.5,175.6,230.8,164.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#B9B9B9;}
.st1{fill:#EE982D;}
.st2{fill:#FF630A;}
</style>
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
<circle class="st2" cx="70.9" cy="109.9" r="19"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#B9B9B9;}
.st1{fill:#009175;}
.st2{fill:#1FC6A6;}
.st3{fill:#FFFFFF;}
</style>
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
<circle class="st2" cx="70.9" cy="109.9" r="23"/>
<path class="st3" d="M70.9,127.8c-9.9,0-17.9-8-17.9-17.9s8-17.9,17.9-17.9s17.9,8,17.9,17.9S80.7,127.8,70.9,127.8z M70.9,94.8
c-8.3,0-15.1,6.8-15.1,15.1s6.8,15.1,15.1,15.1S86,118.3,86,109.9S79.2,94.8,70.9,94.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -7,7 +7,7 @@ import "."
Overlay {
id: root
Image {
AnimatedImage {
id: image
property bool scaleFix: true
property real xStart: 0

View file

@ -14,17 +14,20 @@
#include <chrono>
#include <thread>
#include <gl/Config.h>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <gl/Config.h>
#include <QtCore/QResource>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCommandLineParser>
#include <QtCore/QMimeData>
#include <QtCore/QThreadPool>
#include <QtCore/QFileSelector>
#include <QtConcurrent/QtConcurrentRun>
#include <QtGui/QScreen>
@ -49,6 +52,7 @@
#include <gl/QOpenGLContextWrapper.h>
#include <shared/FileUtils.h>
#include <shared/QtHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <StatTracker.h>
@ -157,7 +161,7 @@
#include "scripting/AssetMappingsScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/DesktopScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/AccountServicesScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h"
@ -209,9 +213,8 @@
#include "commerce/QmlCommerce.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h>
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -226,11 +229,17 @@
#undef QT_BOOTSTRAPPED
#endif
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
extern "C" {
_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}
#endif
#if defined(Q_OS_ANDROID)
#include <android/log.h>
#endif
enum ApplicationEvent {
// Execute a lambda function
Lambda = QEvent::User + 1,
@ -240,7 +249,6 @@ enum ApplicationEvent {
Idle,
};
class RenderEventHandler : public QObject {
using Parent = QObject;
Q_OBJECT
@ -304,9 +312,19 @@ static QTimer locationUpdateTimer;
static QTimer identityPacketTimer;
static QTimer pingTimer;
static const QString DISABLE_WATCHDOG_FLAG("HIFI_DISABLE_WATCHDOG");
#if defined(Q_OS_ANDROID)
static bool DISABLE_WATCHDOG = true;
#else
static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" };
static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG);
#endif
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
@ -503,7 +521,13 @@ public:
}
if (message->message == WM_DEVICECHANGE) {
Midi::USBchanged(); // re-scan the MIDI bus
const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal
static float lastTriggerTime = 0.0f;
const float deltaSeconds = secTimestampNow() - lastTriggerTime;
lastTriggerTime = secTimestampNow();
if (deltaSeconds > MIN_DELTA_SECONDS) {
Midi::USBchanged(); // re-scan the MIDI bus
}
}
}
return false;
@ -530,6 +554,26 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#elif defined Q_OS_ANDROID
const char * local=logMessage.toStdString().c_str();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
abort();
}
#endif
qApp->getLogger()->addMessage(qPrintable(logMessage + "\n"));
}
@ -573,8 +617,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
}
};
reportAndQuit("--protocolVersion", [&](FILE* fp) {
DependencyManager::set<AddressManager>();
auto version = DependencyManager::get<AddressManager>()->protocolVersion();
auto version = protocolVersionsSignatureBase64();
fputs(version.toLatin1().data(), fp);
});
reportAndQuit("--version", [&](FILE* fp) {
@ -594,6 +637,24 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir);
}
// FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove
// this conditional exclusion
#if !defined(Q_OS_OSX)
{
#if defined(Q_OS_ANDROID)
const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc";
#else
const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc";
#endif
if (!QFile::exists(resourcesBinaryFile)) {
throw std::runtime_error("Unable to find primary resources");
}
if (!QResource::registerResource(resourcesBinaryFile)) {
throw std::runtime_error("Unable to load primary resources");
}
}
#endif
Setting::init();
// Tell the plugin manager about our statically linked plugins
@ -632,15 +693,16 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<PointerScriptingInterface>();
DependencyManager::set<PickScriptingInterface>();
DependencyManager::set<Cursor::Manager>();
DependencyManager::set<DesktopPreviewProvider>();
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
DependencyManager::set<Preferences>();
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<GeometryCache>();
DependencyManager::set<ModelCache>();
DependencyManager::set<ScriptCache>();
@ -729,8 +791,10 @@ OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID };
//
// So instead we create a new offscreen context to share with the QGLWidget,
// and manually set THAT to be the shared context for the Chromium helper
#if !defined(DISABLE_QML)
OffscreenGLCanvas* _chromiumShareContext { nullptr };
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
#endif
Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
@ -776,6 +840,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_sampleSound(nullptr)
{
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
@ -996,8 +1061,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
// Inititalize sample before registering
QFileInfo infSample = QFileInfo(PathUtils::resourcesPath() + "sounds/sample.wav");
_sampleSound = DependencyManager::get<SoundCache>()->getSound(QUrl::fromLocalFile(infSample.absoluteFilePath()));
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine){
@ -1082,9 +1146,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
static const QString TESTER = "HIFI_TESTER";
@ -1730,7 +1791,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
static int lastCountOfNearbyAvatars = -1;
QTimer* checkNearbyAvatarsTimer = new QTimer(this);
checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); // 10 seconds, Qt::CoarseTimer ok
connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() {
connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, []() {
auto avatarManager = DependencyManager::get<AvatarManager>();
int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getWorldPosition(),
NEARBY_AVATAR_RADIUS_METERS) - 1;
@ -1769,9 +1830,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
PROFILE_RANGE(render, "Process Default Skybox");
auto textureCache = DependencyManager::get<TextureCache>();
auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx";
QFileSelector fileSelector;
fileSelector.setExtraSelectors(FileUtils::getFileSelectors());
auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx");
_defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl);
_defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString());
_defaultSkyboxAmbientTexture = _defaultSkyboxTexture;
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
@ -1782,8 +1845,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return entityServerNode && !isPhysicsEnabled();
});
QFileInfo infSnap = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(QUrl::fromLocalFile(infSnap.absoluteFilePath()));
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snap.wav"));
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
@ -2093,6 +2155,11 @@ void Application::cleanupBeforeQuit() {
DependencyManager::destroy<AudioInjectorManager>();
DependencyManager::destroy<AudioScriptingInterface>();
// The PointerManager must be destroyed before the PickManager because when a Pointer is deleted,
// it accesses the PickManager to delete its associated Pick
DependencyManager::destroy<PointerManager>();
DependencyManager::destroy<PickManager>();
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
}
@ -2176,11 +2243,16 @@ void Application::initializeGL() {
}
_glWidget->makeCurrent();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->setObjectName("ChromiumShareContext");
_chromiumShareContext->create(_glWidget->qglContext());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
#if !defined(DISABLE_QML)
if (!nsightActive()) {
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->setObjectName("ChromiumShareContext");
_chromiumShareContext->create(_glWidget->qglContext());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
}
#endif
_glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
@ -2197,13 +2269,17 @@ void Application::initializeGL() {
// Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender;
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
_renderEngine->addJob<UpdateSceneTask>("UpdateScene");
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor);
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, isDeferred);
#ifndef Q_OS_ANDROID
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED);
#endif
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, !DISABLE_DEFERRED);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->setObjectName("MainThreadContext");
_offscreenContext->create(_glWidget->qglContext());
@ -2287,7 +2363,7 @@ void Application::initializeUi() {
QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" },
QUrl{ "hifi/commerce/wallet/SendMoney.qml" },
QUrl{ "hifi/commerce/wallet/sendMoney/SendMoney.qml" },
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
QUrl{ "hifi/commerce/wallet/WalletSetup.qml" },
@ -2308,8 +2384,7 @@ void Application::initializeUi() {
offscreenUi->setProxyWindow(_window->windowHandle());
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
// support the window management and scripting proxies for VR use
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml"));
// FIXME either expose so that dialogs can set this themselves or
// do better detection in the offscreen UI of what has focus
offscreenUi->setNavigationFocused(false);
@ -2376,9 +2451,11 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
surfaceContext->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
@ -3902,12 +3979,18 @@ void Application::idle() {
PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
auto renderConfig = _renderEngine->getConfiguration();
PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage());
auto opaqueRangeTimer = renderConfig->getConfig("OpaqueRangeTimer");
auto linearDepth = renderConfig->getConfig("LinearDepth");
auto surfaceGeometry = renderConfig->getConfig("SurfaceGeometry");
auto renderDeferred = renderConfig->getConfig("RenderDeferred");
auto toneAndPostRangeTimer = renderConfig->getConfig("ToneAndPostRangeTimer");
PROFILE_COUNTER(render_detail, "gpuTimes", {
{ "OpaqueRangeTimer", renderConfig->getConfig("OpaqueRangeTimer")->property("gpuRunTime") },
{ "LinearDepth", renderConfig->getConfig("LinearDepth")->property("gpuRunTime") },
{ "SurfaceGeometry", renderConfig->getConfig("SurfaceGeometry")->property("gpuRunTime") },
{ "RenderDeferred", renderConfig->getConfig("RenderDeferred")->property("gpuRunTime") },
{ "ToneAndPostRangeTimer", renderConfig->getConfig("ToneAndPostRangeTimer")->property("gpuRunTime") }
{ "OpaqueRangeTimer", opaqueRangeTimer ? opaqueRangeTimer->property("gpuRunTime") : 0 },
{ "LinearDepth", linearDepth ? linearDepth->property("gpuRunTime") : 0 },
{ "SurfaceGeometry", surfaceGeometry ? surfaceGeometry->property("gpuRunTime") : 0 },
{ "RenderDeferred", renderDeferred ? renderDeferred->property("gpuRunTime") : 0 },
{ "ToneAndPostRangeTimer", toneAndPostRangeTimer ? toneAndPostRangeTimer->property("gpuRunTime") : 0 }
});
PROFILE_RANGE(app, __FUNCTION__);
@ -4272,9 +4355,9 @@ void Application::init() {
// Make sure Login state is up to date
DependencyManager::get<DialogsManager>()->toggleLoginDialog();
DependencyManager::get<DeferredLightingEffect>()->init();
if (!DISABLE_DEFERRED) {
DependencyManager::get<DeferredLightingEffect>()->init();
}
DependencyManager::get<AvatarManager>()->init();
_timerStart.start();
@ -4349,8 +4432,9 @@ void Application::updateLOD(float deltaTime) const {
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
auto lodManager = DependencyManager::get<LODManager>();
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
lodManager->autoAdjustLOD(deltaTime);
} else {
DependencyManager::get<LODManager>()->resetLODAdjust();
}
@ -5752,10 +5836,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
#if !defined(Q_OS_ANDROID)
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
#endif
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
@ -5775,10 +5862,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
scriptEngine->registerGlobalObject("Account", GlobalServicesScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
scriptEngine->registerGlobalObject("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());

View file

@ -26,43 +26,50 @@ Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO
LODManager::LODManager() {
}
float LODManager::getLODDecreaseFPS() {
float LODManager::getLODDecreaseFPS() const {
if (qApp->isHMDMode()) {
return getHMDLODDecreaseFPS();
}
return getDesktopLODDecreaseFPS();
}
float LODManager::getLODIncreaseFPS() {
float LODManager::getLODIncreaseFPS() const {
if (qApp->isHMDMode()) {
return getHMDLODIncreaseFPS();
}
return getDesktopLODIncreaseFPS();
}
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
// We use a "time-weighted running average" of the maxRenderTime and compare it against min/max thresholds
// to determine if we should adjust the level of detail (LOD).
//
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
// reduced by 1/e every timescale of real-time that passes.
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec
//
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
// multiples of the running average timescale:
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
// compute time-weighted running average renderTime
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
_presentTime = presentTime;
_engineRunTime = engineRunTime;
_gpuTime = gpuTime;
}
void LODManager::autoAdjustLOD(float realTimeDelta) {
float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime);
// compute time-weighted running average maxRenderTime
// Note: we MUST clamp the blend to 1.0 for stability
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
if (!_automaticLODAdjust) {
// early exit
return;
@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
<< "targetFPS =" << getLODDecreaseFPS()
<< "octreeSizeScale =" << _octreeSizeScale;
emit LODDecreased();
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to provide an FPS just above the decrease threshold. It will drift close to its
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f);
}
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
}
@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
<< "targetFPS =" << getLODDecreaseFPS()
<< "octreeSizeScale =" << _octreeSizeScale;
emit LODIncreased();
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to provide an FPS just below the increase threshold. It will drift close to its
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f);
}
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
}
@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
}
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to be at middle of target zone. It will drift close to its true value within
// about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS());
_avgRenderTime = MSECS_PER_SECOND / expectedFPS;
}
}
@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() {
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
}
float LODManager::getLODLevel() const {
// simpleLOD is a linearized and normalized number that represents how much LOD is being applied.
// It ranges from:
// 1.0 = normal (max) level of detail
// 0.0 = min level of detail
// In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0.
const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE);
float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE);
float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO;
return simpleLOD;
}
const float MIN_DECREASE_FPS = 0.5f;
void LODManager::setDesktopLODDecreaseFPS(float fps) {

View file

@ -37,7 +37,7 @@ class AABox;
class LODManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
@ -49,34 +49,56 @@ public:
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
// User Tweakable LOD Items
Q_INVOKABLE QString getLODFeedbackText();
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
Q_INVOKABLE float getLODDecreaseFPS();
Q_INVOKABLE float getLODIncreaseFPS();
Q_INVOKABLE float getLODDecreaseFPS() const;
Q_INVOKABLE float getLODIncreaseFPS() const;
Q_PROPERTY(float presentTime READ getPresentTime)
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
Q_PROPERTY(float gpuTime READ getGPUTime)
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
Q_PROPERTY(float lodLevel READ getLODLevel)
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
float getPresentTime() const { return _presentTime; }
float getEngineRunTime() const { return _engineRunTime; }
float getGPUTime() const { return _gpuTime; }
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
void autoAdjustLOD(float renderTime, float realTimeDelta);
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
void autoAdjustLOD(float realTimeDelta);
void loadSettings();
void saveSettings();
void resetLODAdjust();
float getAverageRenderTime() const { return _avgRenderTime; };
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
float getLODLevel() const;
signals:
void LODIncreased();
void LODDecreased();
private:
LODManager();
bool _automaticLODAdjust = true;
float _avgRenderTime { 0.0f };
float _presentTime { 0.0f }; // msec
float _engineRunTime { 0.0f }; // msec
float _gpuTime { 0.0f }; // msec
float _avgRenderTime { 0.0f }; // msec
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };

View file

@ -574,8 +574,6 @@ Menu::Menu() {
avatar.get(), SLOT(setEnableMeshVisible(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
avatar.get(), SLOT(setUseAnimPreAndPostRotations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
avatar.get(), SLOT(setEnableInverseKinematics(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false,

View file

@ -194,7 +194,6 @@ namespace MenuOption {
const QString TurnWithHead = "Turn using Head";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
const QString VelocityFilter = "Velocity Filter";
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";

View file

@ -203,10 +203,14 @@ public:
}
};
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) {
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera");
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
assert(items.canCast<RenderFetchCullSortTask::Output>());
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
}

View file

@ -71,7 +71,7 @@ public:
using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>;
SecondaryCameraRenderTask() {}
void configure(const Config& config) {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true);
};
#endif

View file

@ -537,6 +537,7 @@ void MyAvatar::simulate(float deltaTime) {
// we've achived our final adjusted position and rotation for the avatar
// and all of its joints, now update our attachements.
Avatar::simulateAttachments(deltaTime);
relayJointDataToChildren();
if (!_skeletonModel->hasSkeleton()) {
// All the simulation that can be done has been done
@ -1061,11 +1062,6 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
}
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
AnimClip::usePreAndPostPoseFromAnim = isEnabled;
reset(true);
}
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
_skeletonModel->getRig().setEnableInverseKinematics(isEnabled);
}
@ -1801,7 +1797,7 @@ void MyAvatar::initAnimGraph() {
} else if (!_fstAnimGraphOverrideUrl.isEmpty()) {
graphUrl = _fstAnimGraphOverrideUrl;
} else {
graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json");
graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation.json");
}
_skeletonModel->getRig().initAnimGraph(graphUrl);
@ -1929,7 +1925,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
_prevShouldDrawHead = shouldDrawHead;
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
@ -2023,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
_smoothOrientationTimer = 0.0f;
}
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
Head* head = getHead();
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
if (headPose.isValid()) {
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
@ -2036,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
head->setBaseYaw(YAW(euler));
head->setBasePitch(PITCH(euler));
head->setBaseRoll(ROLL(euler));
} else {
head->setBaseYaw(0.0f);
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
head->setBaseRoll(0.0f);
}
}
@ -2100,7 +2099,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
_actionMotorVelocity = motorSpeed * direction;
} else {
// we're interacting with a floor --> simple horizontal speed and exponential decay
_actionMotorVelocity = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_WALKING_SPEED * direction;
_actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction;
}
float boomChange = getDriveKey(ZOOM);
@ -2692,6 +2691,14 @@ float MyAvatar::getUserEyeHeight() const {
return userHeight - userHeight * ratio;
}
float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get();
}
void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value);
}
glm::vec3 MyAvatar::getPositionForAudio() {
switch (_audioListenerMode) {
case AudioListenerMode::FROM_HEAD:

View file

@ -163,6 +163,8 @@ class MyAvatar : public Avatar {
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
@ -557,6 +559,9 @@ public:
const QUuid& getSelfID() const { return AVATAR_SELF_ID; }
void setWalkSpeed(float value);
float getWalkSpeed() const;
public slots:
void increaseSize();
void decreaseSize();
@ -594,7 +599,6 @@ public slots:
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
void setEnableMeshVisible(bool isEnabled);
void setUseAnimPreAndPostRotations(bool isEnabled);
void setEnableInverseKinematics(bool isEnabled);
QUrl getAnimGraphOverrideUrl() const; // thread-safe
@ -841,6 +845,9 @@ private:
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
};
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -17,6 +17,7 @@
#include "Ledger.h"
#include "CommerceLogging.h"
#include <NetworkingConstants.h>
#include <AddressManager.h>
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
// balance answers {status: 'success', data: {balance: integer}}
@ -46,6 +47,8 @@ Handler(buy)
Handler(receiveAt)
Handler(balance)
Handler(inventory)
Handler(transferHfcToNode)
Handler(transferHfcToUsername)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>();
@ -116,23 +119,69 @@ void Ledger::inventory(const QStringList& keys) {
keysQuery("inventory", "inventorySuccess", "inventoryFailure");
}
QString amountString(const QString& label, const QString&color, const QJsonValue& moneyValue, const QJsonValue& certsValue) {
int money = moneyValue.toInt();
int certs = certsValue.toInt();
if (money <= 0 && certs <= 0) {
return QString();
QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue) {
int sent = sentValue.toInt();
int received = receivedValue.toInt();
if (sent <= 0 && received <= 0) {
return QString("0 HFC");
}
QString result(QString("<font color='#%1'> %2").arg(color, label));
if (money > 0) {
result += QString(" %1 HFC").arg(money);
}
if (certs > 0) {
if (money > 0) {
result += QString(",");
QString result;
if (sent > 0) {
result += QString("<font color='#B70A37'><b>-%1 HFC</b></font>").arg(sent);
if (received > 0) {
result += QString("<br>");
}
result += QString((certs == 1) ? " %1 certificate" : " %1 certificates").arg(certs);
}
return result + QString("</font>");
if (received > 0) {
result += QString("<font color='#3AA38F'><b>%1 HFC</b></font>").arg(received);
}
return result;
}
static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/";
static const QString PLACE_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/places/";
static const QStringList KNOWN_USERS(QStringList() << "highfidelity" << "marketplace");
QString userLink(const QString& username, const QString& placename) {
if (username.isEmpty()) {
if (placename.isEmpty()) {
return QString("someone");
} else {
return QString("someone <a href=\"%1%2\">nearby</a>").arg(PLACE_PAGE_BASE_URL, placename);
}
}
if (KNOWN_USERS.contains(username)) {
return username;
}
return QString("<a href=\"%1%2\">%2</a>").arg(USER_PAGE_BASE_URL, username);
}
QString transactionString(const QJsonObject& valueObject) {
int sentCerts = valueObject["sent_certs"].toInt();
int receivedCerts = valueObject["received_certs"].toInt();
int sent = valueObject["sent_money"].toInt();
int dateInteger = valueObject["created_at"].toInt();
QString message = valueObject["message"].toString();
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
QString result;
if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) {
// this is an hfc transfer.
if (sent > 0) {
QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString());
result += QString("Money sent to %1").arg(recipient);
} else {
QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString());
result += QString("Money from %1").arg(sender);
}
if (!message.isEmpty()) {
result += QString("<br>with memo: <i>\"%1\"</i>").arg(message);
}
} else {
result += valueObject["message"].toString();
}
// no matter what we append a smaller date to the bottom of this...
result += QString("<br><font size='-2' color='#1080B8'>%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate));
return result;
}
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
@ -155,16 +204,13 @@ void Ledger::historySuccess(QNetworkReply& reply) {
// TODO: do this with 0 copies if possible
for (auto it = historyArray.begin(); it != historyArray.end(); it++) {
// We have 2 text fields to synthesize, the one on the left is a listing
// of the HFC in/out of your wallet. The one on the right contains an explaination
// of the transaction. That could be just the memo (if it is a regular purchase), or
// more text (plus the optional memo) if an hfc transfer
auto valueObject = (*it).toObject();
QString sent = amountString("sent", "EA4C5F", valueObject["sent_money"], valueObject["sent_certs"]);
QString received = amountString("received", "1FC6A6", valueObject["received_money"], valueObject["received_certs"]);
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
// be explicit
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
valueObject["text"] = QString("%1%2%3").arg(valueObject["message"].toString(), sent, received);
valueObject["hfc_text"] = hfcString(valueObject["sent_money"], valueObject["received_money"]);
valueObject["transaction_text"] = transactionString(valueObject);
newHistoryArray.push_back(valueObject);
}
// now copy the rest of the json -- this is inefficient
@ -198,11 +244,17 @@ void Ledger::accountSuccess(QNetworkReply& reply) {
auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8());
auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8());
auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8());
QString remotePublicKey = data["public_key"].toString();
wallet->setSalt(salt);
wallet->setIv(iv);
wallet->setCKey(ckey);
QStringList localPublicKeys = wallet->listPublicKeys();
if (remotePublicKey.isEmpty() && !localPublicKeys.isEmpty()) {
receiveAt(localPublicKeys.first(), "");
}
// none of the hfc account info should be emitted
emit accountResult(QJsonObject{ {"status", "success"} });
}
@ -261,3 +313,26 @@ void Ledger::certificateInfo(const QString& certificateId) {
request["certificate_id"] = certificateId;
send(endpoint, "certificateInfoSuccess", "certificateInfoFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request);
}
void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage) {
QJsonObject transaction;
transaction["public_key"] = hfc_key;
transaction["node_id"] = nodeID;
transaction["quantity"] = amount;
transaction["message"] = optionalMessage;
transaction["place_name"] = DependencyManager::get<AddressManager>()->getPlaceName();
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure");
}
void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage) {
QJsonObject transaction;
transaction["public_key"] = hfc_key;
transaction["username"] = username;
transaction["quantity"] = amount;
transaction["message"] = optionalMessage;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure");
}

View file

@ -33,6 +33,8 @@ public:
void account();
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
void certificateInfo(const QString& certificateId);
void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage);
void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage);
enum CertificateStatus {
CERTIFICATE_STATUS_UNKNOWN = 0,
@ -51,6 +53,8 @@ signals:
void accountResult(QJsonObject result);
void locationUpdateResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
void transferHfcToNodeResult(QJsonObject result);
void transferHfcToUsernameResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
@ -71,6 +75,10 @@ public slots:
void updateLocationFailure(QNetworkReply& reply);
void certificateInfoSuccess(QNetworkReply& reply);
void certificateInfoFailure(QNetworkReply& reply);
void transferHfcToNodeSuccess(QNetworkReply& reply);
void transferHfcToNodeFailure(QNetworkReply& reply);
void transferHfcToUsernameSuccess(QNetworkReply& reply);
void transferHfcToUsernameFailure(QNetworkReply& reply);
private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);

View file

@ -29,6 +29,8 @@ QmlCommerce::QmlCommerce() {
connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult);
connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult);
connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus);
connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult);
connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
@ -137,3 +139,27 @@ void QmlCommerce::certificateInfo(const QString& certificateId) {
auto ledger = DependencyManager::get<Ledger>();
ledger->certificateInfo(certificateId);
}
void QmlCommerce::transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
return emit buyResult(result);
}
QString key = keys[0];
ledger->transferHfcToNode(key, nodeID, amount, optionalMessage);
}
void QmlCommerce::transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
return emit buyResult(result);
}
QString key = keys[0];
ledger->transferHfcToUsername(key, username, amount, optionalMessage);
}

View file

@ -45,6 +45,9 @@ signals:
void updateCertificateStatus(const QString& certID, uint certStatus);
void transferHfcToNodeResult(QJsonObject result);
void transferHfcToUsernameResult(QJsonObject result);
protected:
Q_INVOKABLE void getWalletStatus();
@ -65,6 +68,9 @@ protected:
Q_INVOKABLE void account();
Q_INVOKABLE void certificateInfo(const QString& certificateId);
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
};
#endif // hifi_QmlCommerce_h

View file

@ -20,7 +20,6 @@
#include <QTranslator>
#include <BuildInfo.h>
#include <gl/OpenGLVersionChecker.h>
#include <SandboxUtils.h>
#include <SharedUtil.h>
#include <NetworkAccessManager.h>
@ -53,6 +52,14 @@ int main(int argc, const char* argv[]) {
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
#endif
#if defined(USE_GLES) && defined(Q_OS_WIN)
// When using GLES on Windows, we can't create normal GL context in Qt, so
// we force Qt to use angle. This will cause the QML to be unable to be used
// in the output window, so QML should be disabled.
qputenv("QT_ANGLE_PLATFORM", "d3d11");
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
disableQtBearerPoll(); // Fixes wifi ping spikes
QElapsedTimer startupTime;

View file

@ -44,7 +44,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) {
QVariantMap overlayProperties;
// TODO: make these configurable per pointer
overlayProperties["name"] = "stylus";
overlayProperties["url"] = PathUtils::resourcesPath() + "/meshes/tablet-stylus-fat.fbx";
overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx";
overlayProperties["loadPriority"] = 10.0f;
overlayProperties["solid"] = true;
overlayProperties["visible"] = false;

View file

@ -1,36 +0,0 @@
//
// AccountScriptingInterface.cpp
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AccountManager.h"
#include "AccountScriptingInterface.h"
#include "GlobalServicesScriptingInterface.h"
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
return &sharedInstance;
}
bool AccountScriptingInterface::isLoggedIn() {
return GlobalServicesScriptingInterface::getInstance()->isLoggedIn();
}
void AccountScriptingInterface::logOut() {
GlobalServicesScriptingInterface::getInstance()->logOut();
}
bool AccountScriptingInterface::loggedIn() const {
return GlobalServicesScriptingInterface::getInstance()->loggedIn();
}
QString AccountScriptingInterface::getUsername() {
return GlobalServicesScriptingInterface::getInstance()->getUsername();
}

View file

@ -1,60 +0,0 @@
//
// AccountScriptingInterface.h
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AccountScriptingInterface_h
#define hifi_AccountScriptingInterface_h
#include <QObject>
class AccountScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
* @namespace Account
* @property username {String} username if user is logged in, otherwise it returns "Unknown user"
*/
Q_PROPERTY(QString username READ getUsername)
Q_PROPERTY(bool loggedIn READ loggedIn)
signals:
/**jsdoc
* Triggered when username has changed.
* @function Account.usernameChanged
* @return {Signal}
*/
void usernameChanged();
void loggedInChanged(bool loggedIn);
public slots:
static AccountScriptingInterface* getInstance();
/**jsdoc
* Returns the username for the currently logged in High Fidelity metaverse account.
* @function Account.getUsername
* @return {string} username if user is logged in, otherwise it returns "Unknown user"
*/
QString getUsername();
/**jsdoc
* Determine if the user is logged into the High Fidleity metaverse.
* @function Account.isLoggedIn
* @return {bool} true when user is logged into the High Fidelity metaverse.
*/
bool isLoggedIn();
void logOut();
public:
AccountScriptingInterface(QObject* parent = nullptr) {}
bool loggedIn() const;
};
#endif // hifi_AccountScriptingInterface_h

View file

@ -1,5 +1,5 @@
//
// GlobalServicesScriptingInterface.cpp
// AccountServicesScriptingInterface.cpp
// interface/src/scripting
//
// Created by Thijs Wenker on 9/10/14.
@ -14,41 +14,41 @@
#include "DiscoverabilityManager.h"
#include "ResourceCache.h"
#include "GlobalServicesScriptingInterface.h"
#include "AccountServicesScriptingInterface.h"
GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() {
AccountServicesScriptingInterface::AccountServicesScriptingInterface() {
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
connect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
connect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
connect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged);
connect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut);
connect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected);
_downloading = false;
QTimer* checkDownloadTimer = new QTimer(this);
connect(checkDownloadTimer, &QTimer::timeout, this, &GlobalServicesScriptingInterface::checkDownloadInfo);
connect(checkDownloadTimer, &QTimer::timeout, this, &AccountServicesScriptingInterface::checkDownloadInfo);
const int CHECK_DOWNLOAD_INTERVAL = MSECS_PER_SECOND / 2;
checkDownloadTimer->start(CHECK_DOWNLOAD_INTERVAL);
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
this, &GlobalServicesScriptingInterface::discoverabilityModeChanged);
this, &AccountServicesScriptingInterface::discoverabilityModeChanged);
_loggedIn = isLoggedIn();
emit loggedInChanged(_loggedIn);
}
GlobalServicesScriptingInterface::~GlobalServicesScriptingInterface() {
AccountServicesScriptingInterface::~AccountServicesScriptingInterface() {
auto accountManager = DependencyManager::get<AccountManager>();
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
disconnect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged);
disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut);
disconnect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected);
}
GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance() {
static GlobalServicesScriptingInterface sharedInstance;
AccountServicesScriptingInterface* AccountServicesScriptingInterface::getInstance() {
static AccountServicesScriptingInterface sharedInstance;
return &sharedInstance;
}
const QString GlobalServicesScriptingInterface::getUsername() const {
const QString AccountServicesScriptingInterface::getUsername() const {
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->isLoggedIn()) {
return accountManager->getAccountInfo().getUsername();
@ -57,31 +57,31 @@ const QString GlobalServicesScriptingInterface::getUsername() const {
}
}
bool GlobalServicesScriptingInterface::isLoggedIn() {
bool AccountServicesScriptingInterface::isLoggedIn() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->isLoggedIn();
}
bool GlobalServicesScriptingInterface::checkAndSignalForAccessToken() {
bool AccountServicesScriptingInterface::checkAndSignalForAccessToken() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->checkAndSignalForAccessToken();
}
void GlobalServicesScriptingInterface::logOut() {
void AccountServicesScriptingInterface::logOut() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->logout();
}
void GlobalServicesScriptingInterface::loggedOut() {
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
void AccountServicesScriptingInterface::loggedOut() {
emit AccountServicesScriptingInterface::disconnected(QString("logout"));
}
QString GlobalServicesScriptingInterface::getFindableBy() const {
QString AccountServicesScriptingInterface::getFindableBy() const {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
return DiscoverabilityManager::findableByString(discoverabilityManager->getDiscoverabilityMode());
}
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
void AccountServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (discoverabilityMode.toLower() == "none") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
@ -96,11 +96,11 @@ void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabil
}
}
void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
void AccountServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode));
}
void GlobalServicesScriptingInterface::onUsernameChanged(const QString& username) {
void AccountServicesScriptingInterface::onUsernameChanged(const QString& username) {
_loggedIn = (username != QString());
emit myUsernameChanged(username);
emit loggedInChanged(_loggedIn);
@ -135,7 +135,7 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR
result.pending = object.property("pending").toVariant().toFloat();
}
DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() {
DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() {
DownloadInfoResult result;
foreach(const auto& resource, ResourceCache::getLoadingRequests()) {
result.downloading.append(resource->getProgress() * 100.0f);
@ -144,7 +144,7 @@ DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() {
return result;
}
void GlobalServicesScriptingInterface::checkDownloadInfo() {
void AccountServicesScriptingInterface::checkDownloadInfo() {
DownloadInfoResult downloadInfo = getDownloadInfo();
bool downloading = downloadInfo.downloading.count() > 0 || downloadInfo.pending > 0;
@ -155,7 +155,7 @@ void GlobalServicesScriptingInterface::checkDownloadInfo() {
}
}
void GlobalServicesScriptingInterface::updateDownloadInfo() {
void AccountServicesScriptingInterface::updateDownloadInfo() {
emit downloadInfoChanged(getDownloadInfo());
}

View file

@ -1,5 +1,5 @@
//
// GlobalServicesScriptingInterface.h
// AccountServicesScriptingInterface.h
// interface/src/scripting
//
// Created by Thijs Wenker on 9/10/14.
@ -9,8 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_GlobalServicesScriptingInterface_h
#define hifi_GlobalServicesScriptingInterface_h
#ifndef hifi_AccountServicesScriptingInterface_h
#define hifi_AccountServicesScriptingInterface_h
#include <QObject>
#include <QScriptContext>
@ -32,7 +32,7 @@ Q_DECLARE_METATYPE(DownloadInfoResult)
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result);
void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result);
class GlobalServicesScriptingInterface : public QObject {
class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
@ -41,7 +41,7 @@ class GlobalServicesScriptingInterface : public QObject {
Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL)
public:
static GlobalServicesScriptingInterface* getInstance();
static AccountServicesScriptingInterface* getInstance();
const QString getUsername() const;
bool loggedIn() const { return _loggedIn; }
@ -74,11 +74,11 @@ signals:
void loggedInChanged(bool loggedIn);
private:
GlobalServicesScriptingInterface();
~GlobalServicesScriptingInterface();
AccountServicesScriptingInterface();
~AccountServicesScriptingInterface();
bool _downloading;
bool _loggedIn{ false };
};
#endif // hifi_GlobalServicesScriptingInterface_h
#endif // hifi_AccountServicesScriptingInterface_h

View file

@ -39,8 +39,7 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJSValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createSetMappingRequest(path, hash);
connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable {
connect(request, &SetMappingRequest::finished, this, [callback](SetMappingRequest* request) mutable {
if (callback.isCallable()) {
QJSValueList args { request->getErrorString(), request->getPath() };
callback.call(args);
@ -48,15 +47,13 @@ void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJS
request->deleteLater();
});
request->start();
}
void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetMappingRequest(path);
connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable {
connect(request, &GetMappingRequest::finished, this, [callback](GetMappingRequest* request) mutable {
auto hash = request->getHash();
if (callback.isCallable()) {

View file

@ -12,9 +12,11 @@
#ifndef hifi_LimitlessConnection_h
#define hifi_LimitlessConnection_h
#include <QtCore/QObject>
#include <QtCore/QFuture>
#include <QtNetwork/QTcpSocket>
#include <AudioClient.h>
#include <QObject>
#include <QFuture>
class LimitlessConnection : public QObject {
Q_OBJECT

View file

@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString&
if (!result.isEmpty()) {
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
}
emit openFileChanged(result);
emit browseChanged(result);
emit openFileChanged(result); // Deprecated signal; to be removed in due course.
});
}
@ -390,6 +391,10 @@ QString WindowScriptingInterface::checkVersion() {
return QCoreApplication::applicationVersion();
}
QString WindowScriptingInterface::protocolSignature() {
return protocolVersionsSignatureBase64();
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getDeviceSize().x;
}
@ -411,6 +416,11 @@ int WindowScriptingInterface::getY() {
}
void WindowScriptingInterface::copyToClipboard(const QString& text) {
if (QThread::currentThread() != qApp->thread()) {
QMetaObject::invokeMethod(this, "copyToClipboard", Q_ARG(QString, text));
return;
}
qDebug() << "Copying";
QApplication::clipboard()->setText(text);
}

View file

@ -197,18 +197,19 @@ public slots:
/**jsdoc
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
* {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user
* {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user
* cancels the dialog.
* @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen.
* @function Window.browseAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
* function onOpenFileChanged(filename) {
* function onBrowseChanged(filename) {
* print("File: " + filename);
* }
* Window.openFileChanged.connect(onOpenFileChanged);
* Window.browseChanged.connect(onBrowseChanged);
*
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
* print("Script continues without waiting");
@ -305,6 +306,13 @@ public slots:
*/
QString checkVersion();
/**jsdoc
* Get the signature for Interface's protocol version.
* @function Window.protocolSignature
* @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using.
*/
QString protocolSignature();
/**jsdoc
* Copies text to the operating system's clipboard.
* @function Window.copyToClipboard
@ -652,9 +660,18 @@ signals:
*/
void saveFileChanged(QString filename);
/**jsdoc
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
* @function Window.browseChanged
* @param {string} filename - The path and name of the file the user chose in the dialog.
* @returns {Signal}
*/
void browseChanged(QString filename);
/**jsdoc
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
* @function Window.openFileChanged
* @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed.
* @param {string} filename - The path and name of the file the user chose in the dialog.
* @returns {Signal}
*/

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ApplicationOverlay.h"
#include <glm/gtc/type_ptr.hpp>
#include <avatar/AvatarManager.h>
@ -17,12 +19,10 @@
#include <OffscreenUi.h>
#include <CursorManager.h>
#include <PerfStat.h>
#include <gl/Config.h>
#include "AudioClient.h"
#include "audio/AudioScope.h"
#include "Application.h"
#include "ApplicationOverlay.h"
#include "Util.h"
#include "ui/Stats.h"

View file

@ -13,6 +13,7 @@
#define hifi_ApplicationOverlay_h
#include <gpu/Texture.h>
#include <render/Args.h>
// Handles the drawing of the overlays to the screen

View file

@ -10,10 +10,9 @@
#include "ResourceImageItem.h"
#include <gl/Config.h>
#include <QOpenGLFramebufferObjectFormat>
#include <QOpenGLFunctions>
#include <QOpenGLExtraFunctions>
#include <QOpenGLContext>
#include <plugins/DisplayPlugin.h>
@ -89,11 +88,9 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con
}
void ResourceImageItemRenderer::render() {
auto f = QOpenGLContext::currentContext()->extraFunctions();
if (_fenceSync) {
f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED);
f->glDeleteSync(_fenceSync);
glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(_fenceSync);
_fenceSync = 0;
}
if (_ready) {

View file

@ -14,6 +14,8 @@
#include "Application.h"
#include <gl/Config.h>
#include <QQuickFramebufferObject>
#include <QQuickWindow>
#include <QTimer>

View file

@ -42,8 +42,9 @@ using namespace std;
static Stats* INSTANCE{ nullptr };
#if !defined (Q_OS_ANDROID)
QString getTextureMemoryPressureModeString();
#endif
Stats* Stats::getInstance() {
if (!INSTANCE) {
Stats::registerType();
@ -359,7 +360,9 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(gpuTextureResourceMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceGPUMemSize()));
STAT_UPDATE(gpuTextureResourcePopulatedMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourcePopulatedGPUMemSize()));
STAT_UPDATE(gpuTextureExternalMemory, (int)BYTES_TO_MB(gpu::Context::getTextureExternalGPUMemSize()));
#if !defined(Q_OS_ANDROID)
STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString());
#endif
STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize()));
STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load());
STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load());

View file

@ -386,8 +386,6 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and

Some files were not shown because too many files have changed in this diff Show more