3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 19:15:24 +02:00

Merge branch 'spectator-camera' of https://github.com/highfidelity/hifi into dk/spectatorCameraPreview

This commit is contained in:
David Kelly 2017-07-03 13:57:25 -07:00
commit 5f015ec77f
139 changed files with 4338 additions and 1972 deletions
assignment-client/src
cmake/macros
domain-server/src
gvr-interface/src
interface
libraries

View file

@ -16,6 +16,7 @@
#include <QThread>
#include <QTimer>
#include <shared/QtHelpers.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <Assignment.h>
@ -141,7 +142,7 @@ void AssignmentClient::stopAssignmentClient() {
QThread* currentAssignmentThread = _currentAssignment->thread();
// ask the current assignment to stop
QMetaObject::invokeMethod(_currentAssignment, "stop", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(_currentAssignment, "stop");
// ask the current assignment to delete itself on its thread
_currentAssignment->deleteLater();

View file

@ -9,8 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCommandLineParser>
#include <QThread>
#include "AssignmentClientApp.h"
#include <QtCore/QCommandLineParser>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
#include <QtCore/QThread>
#include <LogHandler.h>
#include <SharedUtil.h>
@ -20,10 +24,6 @@
#include "Assignment.h"
#include "AssignmentClient.h"
#include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QCoreApplication(argc, argv)
@ -87,6 +87,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory");
parser.addOption(logDirectoryOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
@ -203,6 +206,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
}
}
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
QThread::currentThread()->setObjectName("main thread");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();

View file

@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() {
void AssignmentClientMonitor::spawnChildClient() {
QProcess* assignmentClient = new QProcess(this);
// unparse the parts of the command-line that the child cares about
QStringList _childArguments;
if (_assignmentPool != "") {
@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
_childArguments.append("--" + PARENT_PID_OPTION);
_childArguments.append(QString::number(QCoreApplication::applicationPid()));
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;

View file

@ -285,6 +285,13 @@ void AvatarMixer::start() {
// is guaranteed to not be accessed by other thread
void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
// there is no need to manage identity data we haven't received yet
// so bail early if we've never received an identity packet for this avatar
if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity()) {
return;
}
bool sendIdentity = false;
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
AvatarData& avatar = nodeData->getAvatar();

View file

@ -320,14 +320,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
++numOtherAvatars;
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
if (otherAvatar->hasProcessedFirstIdentity()
&& nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
identityBytesSent += sendIdentityPacket(otherNodeData, node);
// remember the last time we sent identity details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
}
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
@ -400,9 +404,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
}
}

View file

@ -13,6 +13,7 @@
#include <QThread>
#include <glm/gtx/transform.hpp>
#include <shared/QtHelpers.h>
#include <GLMHelpers.h>
#include <AnimUtil.h>
#include "ScriptableAvatar.h"
@ -49,7 +50,7 @@ void ScriptableAvatar::stopAnimation() {
AnimationDetails ScriptableAvatar::getAnimationDetails() {
if (QThread::currentThread() != thread()) {
AnimationDetails result;
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getAnimationDetails",
Q_RETURN_ARG(AnimationDetails, result));
return result;
}

View file

@ -0,0 +1,12 @@
#
# Created by David Rowe on 16 Jun 2017.
# 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
#
macro(TARGET_LEAPMOTION)
target_include_directories(${TARGET_NAME} PRIVATE ${LEAPMOTION_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES})
endmacro()

View file

@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() {
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
parser.addOption(masterConfigOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qWarning() << parser.errorText() << endl;
@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() {
_overrideDomainID = true;
qDebug() << "domain-server ID is" << _overridingDomainID;
}
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
}
DomainServer::~DomainServer() {

View file

@ -63,10 +63,7 @@ void RenderingClient::sendAvatarPacket() {
}
void RenderingClient::cleanupBeforeQuit() {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
// destroy the AudioClient so it and its thread will safely go down
DependencyManager::destroy<AudioClient>();
}

View file

@ -10,7 +10,7 @@ Interface has been tested with SDK versions:
1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder.
This readme.txt should be there as well.
The files neeeded in the folders are:
The files needed in the folders are:
include/
- Leap.h
@ -21,8 +21,8 @@ Interface has been tested with SDK versions:
x86/
- Leap.dll
- Leap.lib
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
- lipLeap.dylib
libc++/
-libLeap.dylib
@ -30,4 +30,4 @@ Interface has been tested with SDK versions:
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'leapmotion' that contains the 2 folders mentioned above (Include, Lib).
2. Clear your build directory, run cmake and build, and you should be all set.
2. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -0,0 +1,48 @@
{
"name": "Leap Motion to Standard",
"channels": [
{ "from": "LeapMotion.LeftHand", "to": "Standard.LeftHand" },
{ "from": "LeapMotion.LeftHandThumb1", "to": "Standard.LeftHandThumb1"},
{ "from": "LeapMotion.LeftHandThumb2", "to": "Standard.LeftHandThumb2"},
{ "from": "LeapMotion.LeftHandThumb3", "to": "Standard.LeftHandThumb3"},
{ "from": "LeapMotion.LeftHandThumb4", "to": "Standard.LeftHandThumb4"},
{ "from": "LeapMotion.LeftHandIndex1", "to": "Standard.LeftHandIndex1"},
{ "from": "LeapMotion.LeftHandIndex2", "to": "Standard.LeftHandIndex2"},
{ "from": "LeapMotion.LeftHandIndex3", "to": "Standard.LeftHandIndex3"},
{ "from": "LeapMotion.LeftHandIndex4", "to": "Standard.LeftHandIndex4"},
{ "from": "LeapMotion.LeftHandMiddle1", "to": "Standard.LeftHandMiddle1"},
{ "from": "LeapMotion.LeftHandMiddle2", "to": "Standard.LeftHandMiddle2"},
{ "from": "LeapMotion.LeftHandMiddle3", "to": "Standard.LeftHandMiddle3"},
{ "from": "LeapMotion.LeftHandMiddle4", "to": "Standard.LeftHandMiddle4"},
{ "from": "LeapMotion.LeftHandRing1", "to": "Standard.LeftHandRing1"},
{ "from": "LeapMotion.LeftHandRing2", "to": "Standard.LeftHandRing2"},
{ "from": "LeapMotion.LeftHandRing3", "to": "Standard.LeftHandRing3"},
{ "from": "LeapMotion.LeftHandRing4", "to": "Standard.LeftHandRing4"},
{ "from": "LeapMotion.LeftHandPinky1", "to": "Standard.LeftHandPinky1"},
{ "from": "LeapMotion.LeftHandPinky2", "to": "Standard.LeftHandPinky2"},
{ "from": "LeapMotion.LeftHandPinky3", "to": "Standard.LeftHandPinky3"},
{ "from": "LeapMotion.LeftHandPinky4", "to": "Standard.LeftHandPinky4"},
{ "from": "LeapMotion.RightHand", "to": "Standard.RightHand" },
{ "from": "LeapMotion.RightHandThumb1", "to": "Standard.RightHandThumb1"},
{ "from": "LeapMotion.RightHandThumb2", "to": "Standard.RightHandThumb2"},
{ "from": "LeapMotion.RightHandThumb3", "to": "Standard.RightHandThumb3"},
{ "from": "LeapMotion.RightHandThumb4", "to": "Standard.RightHandThumb4"},
{ "from": "LeapMotion.RightHandIndex1", "to": "Standard.RightHandIndex1"},
{ "from": "LeapMotion.RightHandIndex2", "to": "Standard.RightHandIndex2"},
{ "from": "LeapMotion.RightHandIndex3", "to": "Standard.RightHandIndex3"},
{ "from": "LeapMotion.RightHandIndex4", "to": "Standard.RightHandIndex4"},
{ "from": "LeapMotion.RightHandMiddle1", "to": "Standard.RightHandMiddle1"},
{ "from": "LeapMotion.RightHandMiddle2", "to": "Standard.RightHandMiddle2"},
{ "from": "LeapMotion.RightHandMiddle3", "to": "Standard.RightHandMiddle3"},
{ "from": "LeapMotion.RightHandMiddle4", "to": "Standard.RightHandMiddle4"},
{ "from": "LeapMotion.RightHandRing1", "to": "Standard.RightHandRing1"},
{ "from": "LeapMotion.RightHandRing2", "to": "Standard.RightHandRing2"},
{ "from": "LeapMotion.RightHandRing3", "to": "Standard.RightHandRing3"},
{ "from": "LeapMotion.RightHandRing4", "to": "Standard.RightHandRing4"},
{ "from": "LeapMotion.RightHandPinky1", "to": "Standard.RightHandPinky1"},
{ "from": "LeapMotion.RightHandPinky2", "to": "Standard.RightHandPinky2"},
{ "from": "LeapMotion.RightHandPinky3", "to": "Standard.RightHandPinky3"},
{ "from": "LeapMotion.RightHandPinky4", "to": "Standard.RightHandPinky4"}
]
}

View file

@ -58,7 +58,48 @@
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
{ "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"},
{ "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"},
{ "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"},
{ "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"},
{ "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"},
{ "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"},
{ "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"},
{ "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"},
{ "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"},
{ "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"},
{ "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"},
{ "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"},
{ "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"},
{ "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"},
{ "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"},
{ "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"},
{ "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"},
{ "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"},
{ "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"},
{ "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"},
{ "from": "Standard.RightHand", "to": "Actions.RightHand" },
{ "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"},
{ "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"},
{ "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"},
{ "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"},
{ "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"},
{ "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"},
{ "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"},
{ "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"},
{ "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"},
{ "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"},
{ "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"},
{ "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"},
{ "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"},
{ "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"},
{ "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"},
{ "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"},
{ "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"},
{ "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"},
{ "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"},
{ "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"},
{ "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" },
{ "from": "Standard.RightFoot", "to": "Actions.RightFoot" },

View file

@ -17,7 +17,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Settings"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGeneralPreferences"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"]
}
}

View file

@ -48,6 +48,7 @@
#include <gl/QOpenGLContextWrapper.h>
#include <shared/QtHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <StatTracker.h>
#include <Trace.h>
@ -145,7 +146,6 @@
#include "avatar/MyHead.h"
#include "CrashHandler.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Leapmotion.h"
#include "DiscoverabilityManager.h"
#include "GLCanvas.h"
#include "InterfaceDynamicFactory.h"
@ -1209,15 +1209,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));
connect(&_settingsThread, SIGNAL(finished()), &_settingsTimer, SLOT(stop()));
_settingsTimer.moveToThread(&_settingsThread);
_settingsTimer.setSingleShot(false);
_settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable
_settingsThread.setPriority(QThread::LowestPriority);
_settingsThread.start();
QTimer* settingsTimer = new QTimer();
moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
// Disconnect the signal from the save settings
QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
// Stop the settings timer
settingsTimer->stop();
// Delete it (this will trigger the thread destruction
settingsTimer->deleteLater();
// Mark the settings thread as finished, so we know we can safely save in the main application
// shutdown code
_settingsGuard.trigger();
});
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
settingsTimer->setSingleShot(false);
settingsTimer->setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable
QObject::connect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
}, QThread::LowestPriority);
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person.
@ -1645,7 +1656,7 @@ QString Application::getUserAgent() {
if (QThread::currentThread() != thread()) {
QString userAgent;
QMetaObject::invokeMethod(this, "getUserAgent", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userAgent));
BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_RETURN_ARG(QString, userAgent));
return userAgent;
}
@ -1803,11 +1814,13 @@ void Application::cleanupBeforeQuit() {
locationUpdateTimer.stop();
identityPacketTimer.stop();
pingTimer.stop();
QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection);
// save state
_settingsThread.quit();
saveSettings();
// Wait for the settings thread to shut down, and save the settings one last time when it's safe
if (_settingsGuard.wait()) {
// save state
saveSettings();
}
_window->saveGeometry();
// Destroy third party processes after scripts have finished using them.
@ -1831,8 +1844,7 @@ void Application::cleanupBeforeQuit() {
// FIXME: something else is holding a reference to AudioClient,
// so it must be explicitly synchronously stopped here
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"cleanupBeforeQuit", Qt::BlockingQueuedConnection);
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
// destroy Audio so it and its threads have a chance to go down safely
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
@ -1887,8 +1899,6 @@ Application::~Application() {
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
Leapmotion::destroy();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
steamClient->shutdown();
}
@ -4057,8 +4067,6 @@ void Application::init() {
qCDebug(interfaceapp) << "Loaded settings";
Leapmotion::init();
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
@ -4522,7 +4530,6 @@ void Application::update(float deltaTime) {
{
PerformanceTimer perfTimer("devices");
DeviceTracker::updateAll();
FaceTracker* tracker = getSelectedFaceTracker();
if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) {
@ -4591,8 +4598,6 @@ void Application::update(float deltaTime) {
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData);
}
_controllerScriptingInterface->updateInputControllers();
// Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys();
@ -4617,6 +4622,31 @@ void Application::update(float deltaTime) {
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
// If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if
// fingers are not being controlled, finger joints are not updated in MySkeletonModel.
// Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint.
MyAvatar::FingerPosesMap leftHandFingerPoses;
if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) {
leftHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
}
MyAvatar::FingerPosesMap rightHandFingerPoses;
if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) {
rightHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
}
myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses);
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));

View file

@ -25,6 +25,7 @@
#include <QtWidgets/QApplication>
#include <QtWidgets/QUndoStack>
#include <ThreadHelpers.h>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
#include <EntityEditPacketSender.h>
@ -596,8 +597,7 @@ private:
bool _notifiedPacketVersionMismatchThisDomain;
QThread _settingsThread;
QTimer _settingsTimer;
ConditionalGuard _settingsGuard;
GLCanvas* _glWidget{ nullptr };

View file

@ -14,6 +14,7 @@
#include <QMessageBox>
#include <QStandardPaths>
#include <QQmlContext>
#include <QList>
#include <Application.h>
#include <OffscreenUi.h>
@ -24,6 +25,8 @@
#include "AvatarBookmarks.h"
#include "InterfaceLogging.h"
#include "QVariantGLM.h"
#include <QtQuick/QQuickWindow>
AvatarBookmarks::AvatarBookmarks() {
@ -58,16 +61,48 @@ void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
addBookmarkToMenu(menubar, it.key(), it.value());
}
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void AvatarBookmarks::changeToBookmarkedAvatar() {
QAction* action = qobject_cast<QAction*>(sender());
const QString& address = action->data().toString();
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
myAvatar->useFullAvatarURL(address);
if (action->data().type() == QVariant::String) {
// TODO: Phase this out eventually.
// Legacy avatar bookmark.
myAvatar->useFullAvatarURL(action->data().toString());
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
} else {
const QMap<QString, QVariant> bookmark = action->data().toMap();
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
// handle the new one separately.
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
if (bookmark.value(ENTRY_VERSION) == 3) {
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl);
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
qCDebug(interfaceapp) << "Attach " << attachments;
myAvatar->setAttachmentsVariant(attachments);
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
myAvatar->setAvatarScale(qScale);
} else {
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
}
}
}
void AvatarBookmarks::addBookmark() {
@ -83,13 +118,23 @@ void AvatarBookmarks::addBookmark() {
}
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString();
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
const QVariant& avatarScale = myAvatar->getAvatarScale();
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
QVariantMap *bookmark = new QVariantMap;
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
}
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
QAction* changeAction = _bookmarksMenu->newAction();
changeAction->setData(address);
changeAction->setData(bookmark);
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
if (!_isMenuSorted) {
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);

View file

@ -21,18 +21,23 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
public:
AvatarBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
void readFromFile();
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
void readFromFile() override;
private:
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
const QString ENTRY_AVATAR_URL = "avatarUrl";
const QString ENTRY_AVATAR_ATTACHMENTS = "attachments";
const QString ENTRY_AVATAR_SCALE = "avatarScale";
const QString ENTRY_VERSION = "version";
const int AVATAR_BOOKMARK_VERSION = 3;
private slots:
void changeToBookmarkedAvatar();

View file

@ -28,19 +28,6 @@ Bookmarks::Bookmarks() :
_isMenuSorted(false)
{
}
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load Bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
}
}
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
@ -67,7 +54,7 @@ void Bookmarks::deleteBookmark() {
}
}
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) {
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark) {
Menu* menubar = Menu::getInstance();
if (contains(bookmarkName)) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -75,7 +62,6 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
"The bookmark name you entered already exists in your list.",
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
if (result != QMessageBox::Yes) {
return;
@ -83,19 +69,20 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
removeBookmarkFromMenu(menubar, bookmarkName);
}
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
addBookmarkToMenu(menubar, bookmarkName, bookmark);
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
enableMenuItems(true);
}
void Bookmarks::insert(const QString& name, const QString& address) {
_bookmarks.insert(name, address);
void Bookmarks::insert(const QString& name, const QVariant& bookmark) {
_bookmarks.insert(name, bookmark);
if (contains(name)) {
qCDebug(interfaceapp) << "Added bookmark:" << name << "," << address;
qCDebug(interfaceapp) << "Added bookmark:" << name;
persistToFile();
} else {
qWarning() << "Couldn't add bookmark: " << name << "," << address;
}
else {
qWarning() << "Couldn't add bookmark: " << name;
}
}

View file

@ -27,18 +27,20 @@ class Bookmarks: public QObject {
public:
Bookmarks();
virtual void setupMenus(Menu* menubar, MenuWrapper* menu);
virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0;
QString addressForBookmark(const QString& name) const;
protected:
virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress);
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0;
void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark);
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
void enableMenuItems(bool enabled);
void readFromFile();
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
virtual void readFromFile();
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
void sortActions(Menu* menubar, MenuWrapper* menu);
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
bool contains(const QString& name) const;
QVariantMap _bookmarks; // { name: url, ... }
QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction;
@ -50,7 +52,6 @@ protected slots:
private:
void remove(const QString& name);
bool contains(const QString& name) const;
static bool sortOrder(QAction* a, QAction* b);
void persistToFile();

View file

@ -41,13 +41,25 @@ void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
// Legacy Location to Bookmark.
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load Bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
}
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void LocationBookmarks::setHomeLocation() {
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress);
}
@ -74,7 +86,7 @@ void LocationBookmarks::addBookmark() {
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
}
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) {
QAction* teleportAction = _bookmarksMenu->newAction();
teleportAction->setData(address);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
@ -85,4 +97,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
}
}

View file

@ -29,7 +29,7 @@ public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) override;
private:
const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json";

View file

@ -566,9 +566,6 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool)));
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
// Developer > Entities >>>
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");

View file

@ -114,7 +114,6 @@ namespace MenuOption {
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IndependentMode = "Independent Mode";
const QString ActionMotorControl = "Enable Default Motor Control";
const QString LeapMotionOnHMD = "Leap Motion on HMD";
const QString LoadScript = "Open and Run Script File...";
const QString LoadScriptURL = "Open and Run Script from URL...";
const QString LodTools = "LOD Tools";

View file

@ -25,6 +25,7 @@
#endif
#include <shared/QtHelpers.h>
#include <AvatarData.h>
#include <PerfStat.h>
#include <RegisteredMetaTypes.h>
@ -482,7 +483,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay&
const QScriptValue& avatarIdsToDiscard) {
RayToAvatarIntersectionResult result;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarManager*>(this), "findRayIntersection", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersection",
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
Q_ARG(const PickRay&, ray),
Q_ARG(const QScriptValue&, avatarIdsToInclude),

View file

@ -21,6 +21,7 @@
#include <QtCore/QTimer>
#include <shared/QtHelpers.h>
#include <scripting/HMDScriptingInterface.h>
#include <AccountManager.h>
#include <AddressManager.h>
@ -897,7 +898,7 @@ void MyAvatar::restoreAnimation() {
QStringList MyAvatar::getAnimationRoles() {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result));
BLOCKING_INVOKE_METHOD(this, "getAnimationRoles", Q_RETURN_ARG(QStringList, result));
return result;
}
return _skeletonModel->getRig().getAnimationRoles();
@ -1368,7 +1369,7 @@ void MyAvatar::resetFullAvatarURL() {
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "useFullAvatarURL",
Q_ARG(const QUrl&, fullAvatarURL),
Q_ARG(const QString&, modelName));
return;
@ -1394,7 +1395,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>, attachmentData));
return;
}
@ -1455,6 +1456,19 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) {
_leftHandFingerPosesInSensorFramceCache.set(left);
_rightHandFingerPosesInSensorFramceCache.set(right);
}
MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const {
return _leftHandFingerPosesInSensorFramceCache.get();
}
MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const {
return _rightHandFingerPosesInSensorFramceCache.get();
}
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
_leftFootControllerPoseInSensorFrameCache.set(left);
_rightFootControllerPoseInSensorFrameCache.set(right);
@ -2358,7 +2372,7 @@ bool MyAvatar::safeLanding(const glm::vec3& position) {
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(this, "safeLanding", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position));
BLOCKING_INVOKE_METHOD(this, "safeLanding", Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position));
return result;
}
glm::vec3 better;
@ -2524,6 +2538,21 @@ bool MyAvatar::getFlyingEnabled() {
return _enableFlying;
}
// Public interface for targetscale
float MyAvatar::getAvatarScale() {
return getTargetScale();
}
void MyAvatar::setAvatarScale(float val) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAvatarScale", Q_ARG(float, val));
return;
}
setTargetScale(val);
}
void MyAvatar::setCollisionsEnabled(bool enabled) {
if (QThread::currentThread() != thread()) {

View file

@ -366,7 +366,7 @@ public:
float getDriveKey(DriveKeys key) const;
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
void relayDriveKeysToCharacterController();
Q_INVOKABLE void disableDriveKey(DriveKeys key);
Q_INVOKABLE void enableDriveKey(DriveKeys key);
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
@ -474,6 +474,11 @@ public:
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
typedef std::map<int, std::pair<controller::Pose, QString>> FingerPosesMap;
void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right);
FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const;
FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const;
void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
controller::Pose getLeftFootControllerPoseInSensorFrame() const;
controller::Pose getRightFootControllerPoseInSensorFrame() const;
@ -512,6 +517,9 @@ public:
Q_INVOKABLE void setFlyingEnabled(bool enabled);
Q_INVOKABLE bool getFlyingEnabled();
Q_INVOKABLE float getAvatarScale();
Q_INVOKABLE void setAvatarScale(float scale);
Q_INVOKABLE void setCollisionsEnabled(bool enabled);
Q_INVOKABLE bool getCollisionsEnabled();
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated
@ -788,13 +796,15 @@ private:
// These are stored in SENSOR frame
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<FingerPosesMap> _leftHandFingerPosesInSensorFramceCache { };
ThreadSafeValueCache<FingerPosesMap> _rightHandFingerPosesInSensorFramceCache { };
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache { controller::Pose() };
bool _hmdLeanRecenterEnabled = true;
AnimPose _prePhysicsRoomPose;

View file

@ -96,7 +96,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity;
params.controllerActiveFlags[Rig::ControllerType_RightArm] = false;
}
auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame();
if (avatarLeftArmPose.isValid()) {
AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation());
@ -174,5 +174,50 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
_rig.updateFromEyeParameters(eyeParams);
updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame());
updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame());
}
void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) {
// Assumes that finger poses are kept in order in the poses map.
if (fingerPoses.size() == 0) {
return;
}
auto posesMapItr = fingerPoses.begin();
bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1;
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
auto handPose = isLeftHand
? myAvatar->getLeftHandControllerPoseInSensorFrame()
: myAvatar->getRightHandControllerPoseInSensorFrame();
auto handJointRotation = handPose.getRotation();
bool isHandValid = handPose.isValid();
bool isFingerValid = false;
glm::quat previousJointRotation;
while (posesMapItr != fingerPoses.end()) {
auto jointName = posesMapItr->second.second;
if (isHandValid && jointName.right(1) == "1") {
isFingerValid = posesMapItr->second.first.isValid();
previousJointRotation = handJointRotation;
}
if (isHandValid && isFingerValid) {
auto thisJointRotation = posesMapItr->second.first.getRotation();
const float CONTROLLER_PRIORITY = 2.0f;
_rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation,
CONTROLLER_PRIORITY);
previousJointRotation = thisJointRotation;
} else {
_rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName));
}
posesMapItr++;
}
}

View file

@ -10,6 +10,7 @@
#define hifi_MySkeletonModel_h
#include <avatars-renderer/SkeletonModel.h>
#include "MyAvatar.h"
/// A skeleton loaded from a model.
class MySkeletonModel : public SkeletonModel {
@ -21,6 +22,9 @@ private:
public:
MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
private:
void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses);
};
#endif // hifi_MySkeletonModel_h

View file

@ -1,246 +0,0 @@
//
// Created by Sam Cake on 6/2/2014
// 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 "Leapmotion.h"
#include <NumericalConstants.h>
#include "Menu.h"
const int PALMROOT_NUM_JOINTS = 3;
const int FINGER_NUM_JOINTS = 4;
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
const DeviceTracker::Name Leapmotion::NAME = "Leapmotion";
// find the index of a joint from
// the side: true = right
// the finger & the bone:
// finger in [0..4] : bone in [0..3] a finger phalange
// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm]
MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) {
MotionTracker::Index offset = 1 // start after root
+ (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side
+ PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain
if (finger >= 0) {
// from there go down in the correct finger and bone
return offset + (finger * FINGER_NUM_JOINTS) + bone;
} else {
// or go back up for the correct root bone
return offset - 1 - bone;
}
}
// static
void Leapmotion::init() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
Leapmotion* leap = new Leapmotion();
DeviceTracker::registerDevice(NAME, leap);
}
}
// static
void Leapmotion::destroy() {
DeviceTracker::destroyDevice(NAME);
}
// static
Leapmotion* Leapmotion::getInstance() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new Leapmotion and register it
device = new Leapmotion();
DeviceTracker::registerDevice(NAME, device);
}
return dynamic_cast< Leapmotion* > (device);
}
Leapmotion::Leapmotion() :
MotionTracker(),
_active(false)
{
// Create the Leapmotion joint hierarchy
std::vector< Semantic > sides;
sides.push_back("joint_L_");
sides.push_back("joint_R_");
std::vector< Semantic > rootBones;
rootBones.push_back("elbow");
rootBones.push_back("wrist");
rootBones.push_back("hand");
std::vector< Semantic > fingers;
fingers.push_back("thumb");
fingers.push_back("index");
fingers.push_back("middle");
fingers.push_back("ring");
fingers.push_back("pinky");
std::vector< Semantic > fingerBones;
fingerBones.push_back("1");
fingerBones.push_back("2");
fingerBones.push_back("3");
fingerBones.push_back("4");
std::vector< Index > palms;
for (unsigned int s = 0; s < sides.size(); s++) {
Index rootJoint = 0;
for (unsigned int rb = 0; rb < rootBones.size(); rb++) {
rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint);
}
// capture the hand index for debug
palms.push_back(rootJoint);
for (unsigned int f = 0; f < fingers.size(); f++) {
for (unsigned int b = 0; b < fingerBones.size(); b++) {
rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint);
}
}
}
#ifdef HAVE_LEAPMOTION
if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) {
_controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD);
}
#endif
}
Leapmotion::~Leapmotion() {
}
#ifdef HAVE_LEAPMOTION
glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) {
// fix the handness to right and always...
glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z));
glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z));
glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z));
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
return orientation;
}
glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) {
return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER);
}
#endif
void Leapmotion::update() {
#ifdef HAVE_LEAPMOTION
bool wasActive = _active;
_active = _controller.isConnected();
if (_active || wasActive) {
// Go through all the joints and increment their counter since last update.
// Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive.
// TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
(*jointIt).tickNewFrame();
}
}
if (!_active) {
return;
}
// Get the most recent frame and report some basic information
const Leap::Frame frame = _controller.frame();
static int64_t lastFrameID = -1;
int64_t newFrameID = frame.id();
// If too soon then exit
if (lastFrameID >= newFrameID)
return;
glm::vec3 delta(0.0f);
glm::quat handOri;
if (!frame.hands().isEmpty()) {
for (int handNum = 0; handNum < frame.hands().count(); handNum++) {
const Leap::Hand hand = frame.hands()[handNum];
int side = (hand.isRight() ? 1 : -1);
JointTracker* parentJointTracker = _jointsArray.data();
int rootBranchIndex = -1;
Leap::Arm arm = hand.arm();
if (arm.isValid()) {
glm::quat ori = quatFromLeapBase(float(side), arm.basis());
glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition());
JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint
elbow->editAbsFrame().setTranslation(pos);
elbow->editAbsFrame().setRotation(ori);
elbow->updateLocFromAbsTransform(parentJointTracker);
elbow->activeFrame();
parentJointTracker = elbow;
pos = vec3FromLeapVector(arm.wristPosition());
JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint
wrist->editAbsFrame().setTranslation(pos);
wrist->editAbsFrame().setRotation(ori);
wrist->updateLocFromAbsTransform(parentJointTracker);
wrist->activeFrame();
parentJointTracker = wrist;
}
JointTracker* palmJoint = NULL;
{
glm::vec3 pos = vec3FromLeapVector(hand.palmPosition());
glm::quat ori = quatFromLeapBase(float(side), hand.basis());
palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint
palmJoint->editAbsFrame().setTranslation(pos);
palmJoint->editAbsFrame().setRotation(ori);
palmJoint->updateLocFromAbsTransform(parentJointTracker);
palmJoint->activeFrame();
}
// Check if the hand has any fingers
const Leap::FingerList fingers = hand.fingers();
if (!fingers.isEmpty()) {
// For every fingers in the list
for (int i = 0; i < fingers.count(); ++i) {
// Reset the parent joint to the palmJoint for every finger traversal
parentJointTracker = palmJoint;
// surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky
Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0);
// let's update the finger's joints
for (int b = 0; b < FINGER_NUM_JOINTS; b++) {
Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL);
Leap::Bone bone = fingers[i].bone(type);
JointTracker* ljointTracker = editJointTracker(fingerIndex + b);
if (bone.isValid()) {
Leap::Vector bp = bone.nextJoint();
ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp));
ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis()));
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
ljointTracker->activeFrame();
}
parentJointTracker = ljointTracker;
}
}
}
}
}
lastFrameID = newFrameID;
#endif
}

View file

@ -1,48 +0,0 @@
//
// Created by Sam Cake on 6/2/2014
// 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_Leapmotion_h
#define hifi_Leapmotion_h
#include <QDateTime>
#include <trackers/MotionTracker.h>
#ifdef HAVE_LEAPMOTION
#include <Leap.h>
#endif
/// Handles interaction with the Leapmotion skeleton tracking suit.
class Leapmotion : public MotionTracker {
public:
static const Name NAME;
static void init();
static void destroy();
/// Leapmotion MotionTracker factory
static Leapmotion* getInstance();
bool isActive() const { return _active; }
virtual void update() override;
protected:
Leapmotion();
virtual ~Leapmotion();
private:
#ifdef HAVE_LEAPMOTION
Leap::Listener _listener;
Leap::Controller _controller;
#endif
bool _active;
};
#endif // hifi_Leapmotion_h

View file

@ -11,6 +11,8 @@
#include "Audio.h"
#include <shared/QtHelpers.h>
#include "Application.h"
#include "AudioClient.h"
#include "ui/AvatarInputs.h"
@ -49,27 +51,22 @@ float Audio::loudnessToLevel(float loudness) {
Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().data();
connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged);
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::onNoiseReductionChanged);
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::onInputVolumeChanged);
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged);
enableNoiseReduction(enableNoiseReductionSetting.get());
}
void Audio::setMuted(bool isMuted) {
if (_isMuted != isMuted) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "toggleMute", Qt::BlockingQueuedConnection);
_isMuted = isMuted;
emit mutedChanged(_isMuted);
QMetaObject::invokeMethod(client, "toggleMute");
}
}
void Audio::onMutedChanged() {
auto client = DependencyManager::get<AudioClient>().data();
bool isMuted;
QMetaObject::invokeMethod(client, "isMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isMuted));
bool isMuted = DependencyManager::get<AudioClient>()->isMuted();
if (_isMuted != isMuted) {
_isMuted = isMuted;
emit mutedChanged(_isMuted);
@ -79,11 +76,16 @@ void Audio::onMutedChanged() {
void Audio::enableNoiseReduction(bool enable) {
if (_enableNoiseReduction != enable) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setNoiseReduction", Qt::BlockingQueuedConnection, Q_ARG(bool, enable));
QMetaObject::invokeMethod(client, "setNoiseReduction", Q_ARG(bool, enable));
enableNoiseReductionSetting.set(enable);
_enableNoiseReduction = enable;
emit noiseReductionChanged(enable);
}
}
void Audio::onNoiseReductionChanged() {
bool noiseReductionEnabled = DependencyManager::get<AudioClient>()->isNoiseReductionEnabled();
if (_enableNoiseReduction != noiseReductionEnabled) {
_enableNoiseReduction = noiseReductionEnabled;
emit noiseReductionChanged(_enableNoiseReduction);
}
}
@ -93,19 +95,11 @@ void Audio::setInputVolume(float volume) {
if (_inputVolume != volume) {
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setInputVolume", Qt::BlockingQueuedConnection, Q_ARG(float, volume));
_inputVolume = volume;
emit inputVolumeChanged(_inputVolume);
QMetaObject::invokeMethod(client, "setInputVolume", Q_ARG(float, volume));
}
}
// different audio input devices may have different volumes
void Audio::onInputChanged() {
auto client = DependencyManager::get<AudioClient>().data();
float volume;
QMetaObject::invokeMethod(client, "getInputVolume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(float, volume));
void Audio::onInputVolumeChanged(float volume) {
if (_inputVolume != volume) {
_inputVolume = volume;
emit inputVolumeChanged(_inputVolume);

View file

@ -62,9 +62,12 @@ signals:
void contextChanged(const QString& context);
public slots:
void onMutedChanged();
void onContextChanged();
void onInputChanged();
private slots:
void onMutedChanged();
void onNoiseReductionChanged();
void onInputVolumeChanged(float volume);
void onInputLoudnessChanged(float loudness);
protected:

View file

@ -11,6 +11,8 @@
#include <map>
#include <shared/QtHelpers.h>
#include "AudioDevices.h"
#include "Application.h"
@ -71,22 +73,14 @@ bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, i
bool AudioDeviceList::setDevice(int row, bool fromUser) {
bool success = false;
auto& device = _devices[row];
_userSelection = fromUser;
// skip if already selected
if (!device.selected) {
auto client = DependencyManager::get<AudioClient>();
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, success),
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
Q_ARG(QAudio::Mode, _mode),
Q_ARG(const QAudioDeviceInfo&, device.info));
if (success) {
device.selected = true;
if (fromUser) {
emit deviceSelected(device.info, _selectedDevice);
}
emit deviceChanged(device.info);
}
}
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
@ -135,12 +129,12 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
}
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
auto oldDevice = _selectedDevice;
_selectedDevice = device;
QModelIndex index;
for (auto i = 0; i < _devices.size(); ++i) {
AudioDevice& device = _devices[i];
if (device.selected && device.info != _selectedDevice) {
device.selected = false;
} else if (device.info == _selectedDevice) {
@ -149,6 +143,11 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
}
}
if (_userSelection) {
_userSelection = false;
emit deviceSelected(_selectedDevice, oldDevice);
}
emit deviceChanged(_selectedDevice);
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
}

View file

@ -58,7 +58,7 @@ private:
static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags;
bool _userSelection { false };
QAudio::Mode _mode;
QAudioDeviceInfo _selectedDevice;
QList<AudioDevice> _devices;

View file

@ -8,9 +8,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "ClipboardScriptingInterface.h"
#include <shared/QtHelpers.h>
#include "Application.h"
ClipboardScriptingInterface::ClipboardScriptingInterface() {
}
@ -24,7 +27,7 @@ float ClipboardScriptingInterface::getClipboardContentsLargestDimension() {
bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
bool retVal;
QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename),
Q_ARG(const QVector<EntityItemID>&, entityIDs));
@ -33,7 +36,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
bool retVal;
QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename),
Q_ARG(float, x),
@ -45,7 +48,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
bool ClipboardScriptingInterface::importEntities(const QString& filename) {
bool retVal;
QMetaObject::invokeMethod(qApp, "importEntities", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(qApp, "importEntities",
Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename));
return retVal;
@ -53,7 +56,7 @@ bool ClipboardScriptingInterface::importEntities(const QString& filename) {
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
QVector<EntityItemID> retVal;
QMetaObject::invokeMethod(qApp, "pasteEntities", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(qApp, "pasteEntities",
Q_RETURN_ARG(QVector<EntityItemID>, retVal),
Q_ARG(float, position.x),
Q_ARG(float, position.y),

View file

@ -13,6 +13,10 @@
#include <QObject>
#include <glm/glm.hpp>
#include <EntityItemID.h>
/**jsdoc
* @namespace Clipboard
*/

View file

@ -17,7 +17,6 @@
#include <plugins/PluginManager.h>
#include "Application.h"
#include <trackers/MotionTracker.h>
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
if (event->type() == HFActionEvent::startType()) {
@ -97,86 +96,6 @@ QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
return qRectToVariant(rect);
}
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retrieve the Device Tracker category and then the sub tracker within it
auto icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) {
return (*icIt).second.get();
}
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
if (deviceID < 0) {
deviceID = 0;
}
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
// in the near future we need to change that to a real mapping between the devices and the deviceName
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
if (deviceID >= 0) {
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
if (motionTracker) {
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
if (trackerID >= 0) {
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
controller::InputController::Key key = inputController->getKey();
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
return inputController.get();
}
}
}
return nullptr;
}
void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
_inputControllers.erase(input->getKey());
}
void ControllerScriptingInterface::updateInputControllers() {
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId),
_isActive(false)
{
}
void InputController::update() {
_isActive = false;
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
if (motionTracker) {
if ((int)_subTrackerId < motionTracker->numJointTrackers()) {
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
if (joint->isActive()) {
joint->getAbsFrame().getTranslation(_eventCache.absTranslation);
joint->getAbsFrame().getRotation(_eventCache.absRotation);
joint->getLocFrame().getTranslation(_eventCache.locTranslation);
joint->getLocFrame().getRotation(_eventCache.locRotation);
_isActive = true;
//emit spatialEvent(_eventCache);
}
}
}
}
const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16;
const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
InputController::Key InputController::getKey() const {
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
}
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }

View file

@ -25,38 +25,6 @@
#include <WheelEvent.h>
class ScriptEngine;
class PalmData;
class InputController : public controller::InputController {
Q_OBJECT
public:
InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL);
virtual void update() override;
virtual Key getKey() const override;
public slots:
virtual bool isActive() const override { return _isActive; }
virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; }
virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; }
virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; }
virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; }
private:
int _deviceTrackerId;
uint _subTrackerId;
// cache for the spatial
SpatialEvent _eventCache;
bool _isActive;
signals:
};
/// handles scripting of input controller commands from JS
class ControllerScriptingInterface : public controller::ScriptingInterface {
Q_OBJECT
@ -86,8 +54,6 @@ public:
bool isJoystickCaptured(int joystickIndex) const;
bool areEntityClicksCaptured() const;
void updateInputControllers();
public slots:
virtual void captureKeyEvents(const KeyEvent& event);
@ -102,10 +68,6 @@ public slots:
virtual glm::vec2 getViewportDimensions() const;
virtual QVariant getRecommendedOverlayRect() const;
/// Factory to create an InputController
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController* input);
signals:
void keyPressEvent(const KeyEvent& event);
void keyReleaseEvent(const KeyEvent& event);
@ -135,8 +97,6 @@ private:
bool _captureEntityClicks;
using InputKey = controller::InputController::Key;
using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
InputControllerMap _inputControllers;
};
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip

View file

@ -13,6 +13,7 @@
#include <QtScript/QScriptContext>
#include <shared/QtHelpers.h>
#include <avatar/AvatarManager.h>
#include <display-plugins/DisplayPlugin.h>
#include <display-plugins/CompositorHelper.h>
@ -152,22 +153,31 @@ QString HMDScriptingInterface::preferredAudioOutput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "setHandLasers", Q_RETURN_ARG(bool, result),
Q_ARG(int, hands), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([offscreenUi, enabled] {
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
});
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
color, direction);
}
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([offscreenUi, enabled] {
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
});
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "setExtraLaser", Q_RETURN_ARG(bool, result),
Q_ARG(glm::vec3, worldStart), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto sensorToWorld = myAvatar->getSensorToWorldMatrix();
@ -179,11 +189,11 @@ bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enab
color, sensorStart, sensorDirection);
}
void HMDScriptingInterface::disableExtraLaser() const {
void HMDScriptingInterface::disableExtraLaser() {
setExtraLaser(vec3(0), false, vec4(0), vec3(0));
}
void HMDScriptingInterface::disableHandLasers(int hands) const {
void HMDScriptingInterface::disableHandLasers(int hands) {
setHandLasers(hands, false, vec4(0), vec3(0));
}

View file

@ -51,11 +51,11 @@ public:
Q_INVOKABLE void requestHideHandControllers();
Q_INVOKABLE bool shouldShowHandControllers() const;
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
Q_INVOKABLE void disableHandLasers(int hands) const;
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction);
Q_INVOKABLE void disableHandLasers(int hands);
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
Q_INVOKABLE void disableExtraLaser() const;
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction);
Q_INVOKABLE void disableExtraLaser();
/// Suppress the activation of any on-screen keyboard so that a script operation will

View file

@ -14,6 +14,7 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <MenuItemProperties.h>
#include "Menu.h"
@ -43,7 +44,7 @@ bool MenuScriptingInterface::menuExists(const QString& menu) {
return Menu::getInstance()->menuExists(menu);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuExists",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu));
return result;
@ -86,7 +87,7 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
return Menu::getInstance()->menuItemExists(menu, menuitem);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
@ -114,7 +115,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
@ -131,7 +132,7 @@ bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isMenuEnabled", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
@ -157,7 +158,7 @@ bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isInfoViewVisible",
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
return result;
}

View file

@ -11,6 +11,7 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <DependencyManager.h>
#include <Trace.h>
#include <StatTracker.h>
@ -57,20 +58,25 @@ void TestScriptingInterface::waitIdle() {
}
bool TestScriptingInterface::loadTestScene(QString scene) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "loadTestScene", Q_RETURN_ARG(bool, result), Q_ARG(QString, scene));
return result;
}
static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/";
static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/";
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([scene]()->QVariant {
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions
tree->setIsClient(false);
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
tree->setIsClient(treeIsClient);
return result;
}).toBool();
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions
tree->setIsClient(false);
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
tree->setIsClient(treeIsClient);
return result;
}
bool TestScriptingInterface::startTracing(QString logrules) {

View file

@ -9,11 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "WindowScriptingInterface.h"
#include <QClipboard>
#include <QtCore/QDir>
#include <QMessageBox>
#include <QScriptValue>
#include <shared/QtHelpers.h>
#include <SettingHandle.h>
#include <display-plugins/CompositorHelper.h>
@ -24,8 +27,6 @@
#include "Menu.h"
#include "OffscreenUi.h"
#include "WindowScriptingInterface.h"
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation";
@ -316,7 +317,7 @@ bool WindowScriptingInterface::isPhysicsEnabled() {
int WindowScriptingInterface::openMessageBox(QString title, QString text, int buttons, int defaultButton) {
if (QThread::currentThread() != thread()) {
int result;
QMetaObject::invokeMethod(this, "openMessageBox", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "openMessageBox",
Q_RETURN_ARG(int, result),
Q_ARG(QString, title),
Q_ARG(QString, text),

View file

@ -18,6 +18,8 @@
#include <QtScript/QScriptValue>
#include <QtWidgets/QMessageBox>
#include <DependencyManager.h>
class CustomPromptResult {
public:
QVariant value;

View file

@ -15,6 +15,7 @@
#include <QScrollBar>
#include <QtConcurrent/QtConcurrentRun>
#include <shared/QtHelpers.h>
#include <ScriptEngines.h>
#include <PathUtils.h>
@ -115,7 +116,7 @@ void JSConsole::executeCommand(const QString& command) {
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
QScriptValue result;
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate",
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, command),
Q_ARG(const QString&, _consoleFileName));

View file

@ -45,11 +45,13 @@ Image3DOverlay::~Image3DOverlay() {
}
void Image3DOverlay::update(float deltatime) {
#if OVERLAY_PANELS
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
#endif
}
void Image3DOverlay::render(RenderArgs* args) {

View file

@ -84,9 +84,9 @@ public:
void setColorPulse(float value) { _colorPulse = value; }
void setAlphaPulse(float value) { _alphaPulse = value; }
virtual void setProperties(const QVariantMap& properties);
virtual Overlay* createClone() const = 0;
virtual QVariant getProperty(const QString& property);
Q_INVOKABLE virtual void setProperties(const QVariantMap& properties);
Q_INVOKABLE virtual Overlay* createClone() const = 0;
Q_INVOKABLE virtual QVariant getProperty(const QString& property);
render::ItemID getRenderItemID() const { return _renderItemID; }
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }

View file

@ -11,6 +11,8 @@
#include "OverlayPanel.h"
#if OVERLAY_PANELS
#include <QVariant>
#include <RegisteredMetaTypes.h>
#include <DependencyManager.h>
@ -185,3 +187,4 @@ void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
pointTransformAtCamera(transform, getOffsetRotation());
}
}
#endif

View file

@ -22,6 +22,7 @@
#include "Billboardable.h"
#include "Overlay.h"
#if OVERLAY_PANELS
class PropertyBinding {
public:
PropertyBinding() {}
@ -80,4 +81,6 @@ private:
QScriptEngine* _scriptEngine;
};
#endif
#endif // hifi_OverlayPanel_h

View file

@ -14,6 +14,7 @@
#include <QtScript/QScriptValueIterator>
#include <shared/QtHelpers.h>
#include <OffscreenUi.h>
#include <render/Scene.h>
#include <RegisteredMetaTypes.h>
@ -40,8 +41,6 @@ Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
void Overlays::cleanupAllOverlays() {
{
QWriteLocker lock(&_lock);
QWriteLocker deleteLock(&_deleteLock);
foreach(Overlay::Pointer overlay, _overlaysHUD) {
_overlaysToDelete.push_back(overlay);
}
@ -50,19 +49,22 @@ void Overlays::cleanupAllOverlays() {
}
_overlaysHUD.clear();
_overlaysWorld.clear();
#if OVERLAY_PANELS
_panels.clear();
#endif
}
cleanupOverlaysToDelete();
}
void Overlays::init() {
#if OVERLAY_PANELS
_scriptEngine = new QScriptEngine();
#endif
}
void Overlays::update(float deltatime) {
{
QWriteLocker lock(&_lock);
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
thisOverlay->update(deltatime);
}
@ -80,8 +82,6 @@ void Overlays::cleanupOverlaysToDelete() {
render::Transaction transaction;
{
QWriteLocker lock(&_deleteLock);
do {
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
@ -100,7 +100,6 @@ void Overlays::cleanupOverlaysToDelete() {
void Overlays::renderHUD(RenderArgs* renderArgs) {
PROFILE_RANGE(render_overlays, __FUNCTION__);
QReadLocker lock(&_lock);
gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -126,12 +125,10 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
}
void Overlays::disable() {
QWriteLocker lock(&_lock);
_enabled = false;
}
void Overlays::enable() {
QWriteLocker lock(&_lock);
_enabled = true;
}
@ -146,6 +143,12 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
}
OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(QString, type), Q_ARG(QVariant, properties));
return result;
}
Overlay::Pointer thisOverlay = nullptr;
if (type == ImageOverlay::TYPE) {
@ -185,8 +188,7 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
return UNKNOWN_OVERLAY_ID;
}
OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
QWriteLocker lock(&_lock);
OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
OverlayID thisID = OverlayID(QUuid::createUuid());
overlay->setOverlayID(thisID);
overlay->setStackOrder(_stackOrder++);
@ -205,14 +207,22 @@ OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
}
OverlayID Overlays::cloneOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone()));
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->addChild(cloneId);
}
#endif
return cloneId;
}
@ -220,21 +230,29 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
}
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
QWriteLocker lock(&_lock);
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "editOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id), Q_ARG(QVariant, properties));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
thisOverlay->setProperties(properties.toMap());
return true;
}
return false;
}
bool Overlays::editOverlays(const QVariant& propertiesById) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "editOverlays", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, propertiesById));
return result;
}
QVariantMap map = propertiesById.toMap();
bool success = true;
QWriteLocker lock(&_lock);
for (const auto& key : map.keys()) {
OverlayID id = OverlayID(key);
Overlay::Pointer thisOverlay = getOverlay(id);
@ -249,10 +267,14 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
}
void Overlays::deleteOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id));
return;
}
Overlay::Pointer overlayToDelete;
{
QWriteLocker lock(&_lock);
if (_overlaysHUD.contains(id)) {
overlayToDelete = _overlaysHUD.take(id);
} else if (_overlaysWorld.contains(id)) {
@ -262,19 +284,25 @@ void Overlays::deleteOverlay(OverlayID id) {
}
}
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->removeChild(id);
attachable->setParentPanel(nullptr);
}
#endif
QWriteLocker lock(&_deleteLock);
_overlaysToDelete.push_back(overlayToDelete);
emit overlayDeleted(id);
}
QString Overlays::getOverlayType(OverlayID overlayId) const {
QString Overlays::getOverlayType(OverlayID overlayId) {
if (QThread::currentThread() != thread()) {
QString result;
BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(OverlayID, overlayId));
return result;
}
Overlay::Pointer overlay = getOverlay(overlayId);
if (overlay) {
return overlay->getType();
@ -283,6 +311,12 @@ QString Overlays::getOverlayType(OverlayID overlayId) const {
}
QObject* Overlays::getOverlayObject(OverlayID id) {
if (QThread::currentThread() != thread()) {
QObject* result;
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
return qobject_cast<QObject*>(&(*thisOverlay));
@ -290,6 +324,7 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
return nullptr;
}
#if OVERLAY_PANELS
OverlayID Overlays::getParentPanel(OverlayID childId) const {
Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
@ -330,10 +365,16 @@ void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
}
}
}
#endif
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
return result;
}
glm::vec2 pointCopy = point;
QReadLocker lock(&_lock);
if (!_enabled) {
return UNKNOWN_OVERLAY_ID;
}
@ -365,9 +406,14 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
}
OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) {
if (QThread::currentThread() != thread()) {
OverlayPropertyResult result;
BLOCKING_INVOKE_METHOD(this, "getProperty", Q_RETURN_ARG(OverlayPropertyResult, result), Q_ARG(OverlayID, id), Q_ARG(QString, property));
return result;
}
OverlayPropertyResult result;
Overlay::Pointer thisOverlay = getOverlay(id);
QReadLocker lock(&_lock);
if (thisOverlay && thisOverlay->supportsGetProperty()) {
result.value = thisOverlay->getProperty(property);
}
@ -405,7 +451,18 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
const QVector<OverlayID>& overlaysToInclude,
const QVector<OverlayID>& overlaysToDiscard,
bool visibleOnly, bool collidableOnly) {
QReadLocker lock(&_lock);
if (QThread::currentThread() != thread()) {
RayToOverlayIntersectionResult result;
BLOCKING_INVOKE_METHOD(this, "findRayIntersectionInternal", Q_RETURN_ARG(RayToOverlayIntersectionResult, result),
Q_ARG(PickRay, ray),
Q_ARG(bool, precisionPicking),
Q_ARG(QVector<OverlayID>, overlaysToInclude),
Q_ARG(QVector<OverlayID>, overlaysToDiscard),
Q_ARG(bool, visibleOnly),
Q_ARG(bool, collidableOnly));
return result;
}
float bestDistance = std::numeric_limits<float>::max();
bool bestIsFront = false;
@ -448,16 +505,6 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
return result;
}
RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
intersects(false),
overlayID(UNKNOWN_OVERLAY_ID),
distance(0),
face(),
intersection(),
extraInfo()
{
}
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
auto obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
@ -531,7 +578,12 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar
}
bool Overlays::isLoaded(OverlayID id) {
QReadLocker lock(&_lock);
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (!thisOverlay) {
return false; // not found
@ -539,7 +591,13 @@ bool Overlays::isLoaded(OverlayID id) {
return thisOverlay->isLoaded();
}
QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
QSizeF Overlays::textSize(OverlayID id, const QString& text) {
if (QThread::currentThread() != thread()) {
QSizeF result;
BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(OverlayID, id), Q_ARG(QString, text));
return result;
}
Overlay::Pointer thisOverlay = _overlaysHUD[id];
if (thisOverlay) {
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
@ -554,6 +612,7 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
return QSizeF(0.0f, 0.0f);
}
#if OVERLAY_PANELS
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
QWriteLocker lock(&_lock);
@ -607,8 +666,15 @@ void Overlays::deletePanel(OverlayID panelId) {
emit panelDeleted(panelId);
}
#endif
bool Overlays::isAddedOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "isAddedOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
return result;
}
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
}
@ -636,20 +702,43 @@ void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) {
emit hoverLeaveOverlay(id, event);
}
OverlayID Overlays::getKeyboardFocusOverlay() const {
OverlayID Overlays::getKeyboardFocusOverlay() {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getKeyboardFocusOverlay", Q_RETURN_ARG(OverlayID, result));
return result;
}
return qApp->getKeyboardFocusOverlay();
}
void Overlays::setKeyboardFocusOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setKeyboardFocusOverlay", Q_ARG(OverlayID, id));
return;
}
qApp->setKeyboardFocusOverlay(id);
}
float Overlays::width() const {
float Overlays::width() {
if (QThread::currentThread() != thread()) {
float result;
BLOCKING_INVOKE_METHOD(this, "width", Q_RETURN_ARG(float, result));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().width();
}
float Overlays::height() const {
float Overlays::height() {
if (QThread::currentThread() != thread()) {
float result;
BLOCKING_INVOKE_METHOD(this, "height", Q_RETURN_ARG(float, result));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().height();
}
@ -705,7 +794,6 @@ PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay r
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
QReadLocker lock(&_lock);
auto position = thisOverlay->getPosition();
auto rotation = thisOverlay->getRotation();
auto dimensions = thisOverlay->getSize();
@ -854,8 +942,13 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
return false;
}
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) const {
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
QVector<QUuid> result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "findOverlays", Q_RETURN_ARG(QVector<QUuid>, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius));
return result;
}
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
int checked = 0;

View file

@ -25,8 +25,9 @@
#include <PointerEvent.h>
#include "Overlay.h"
#include "OverlayPanel.h"
#include "PanelAttachable.h"
#include "OverlayPanel.h"
class PickRay;
@ -41,6 +42,8 @@ Q_DECLARE_METATYPE(OverlayPropertyResult);
QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value);
void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value);
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
/**jsdoc
* @typedef Overlays.RayToOverlayIntersectionResult
* @property {bool} intersects True if the PickRay intersected with a 3D overlay.
@ -51,10 +54,9 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
*/
class RayToOverlayIntersectionResult {
public:
RayToOverlayIntersectionResult();
bool intersects;
OverlayID overlayID;
float distance;
bool intersects { false };
OverlayID overlayID { UNKNOWN_OVERLAY_ID };
float distance { 0 };
BoxFace face;
glm::vec3 surfaceNormal;
glm::vec3 intersection;
@ -77,8 +79,6 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
* @namespace Overlays
*/
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
class Overlays : public QObject {
Q_OBJECT
@ -94,11 +94,13 @@ public:
void enable();
Overlay::Pointer getOverlay(OverlayID id) const;
#if OVERLAY_PANELS
OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; }
#endif
/// adds an overlay that's already been created
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
OverlayID addOverlay(Overlay::Pointer overlay);
OverlayID addOverlay(const Overlay::Pointer& overlay);
bool mousePressEvent(QMouseEvent* event);
bool mouseDoublePressEvent(QMouseEvent* event);
@ -156,7 +158,7 @@ public slots:
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of.
* @return {string} The type of the overlay if found, otherwise the empty string.
*/
QString getOverlayType(OverlayID overlayId) const;
QString getOverlayType(OverlayID overlayId);
/**jsdoc
* Get the overlay Script object.
@ -215,7 +217,7 @@ public slots:
* @param {float} radius search radius
* @return {Overlays.OverlayID[]} list of overlays withing the radius
*/
QVector<QUuid> findOverlays(const glm::vec3& center, float radius) const;
QVector<QUuid> findOverlays(const glm::vec3& center, float radius);
/**jsdoc
* Check whether an overlay's assets have been loaded. For example, if the
@ -237,7 +239,7 @@ public slots:
* @param {string} The string to measure.
* @return {Vec2} The size of the text.
*/
QSizeF textSize(OverlayID id, const QString& text) const;
QSizeF textSize(OverlayID id, const QString& text);
/**jsdoc
* Get the width of the virtual 2D HUD.
@ -245,7 +247,7 @@ public slots:
* @function Overlays.width
* @return {float} The width of the 2D HUD.
*/
float width() const;
float width();
/**jsdoc
* Get the height of the virtual 2D HUD.
@ -253,11 +255,12 @@ public slots:
* @function Overlays.height
* @return {float} The height of the 2D HUD.
*/
float height() const;
float height();
/// return true if there is an overlay with that id else false
bool isAddedOverlay(OverlayID id);
#if OVERLAY_PANELS
OverlayID getParentPanel(OverlayID childId) const;
void setParentPanel(OverlayID childId, OverlayID panelId);
@ -279,6 +282,8 @@ public slots:
/// return true if there is a panel with that id else false
bool isAddedPanel(OverlayID id) { return _panels.contains(id); }
#endif
void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
@ -287,7 +292,7 @@ public slots:
void sendHoverOverOverlay(OverlayID id, PointerEvent event);
void sendHoverLeaveOverlay(OverlayID id, PointerEvent event);
OverlayID getKeyboardFocusOverlay() const;
OverlayID getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(OverlayID id);
signals:
@ -316,13 +321,15 @@ private:
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
#if OVERLAY_PANELS
QMap<OverlayID, OverlayPanel::Pointer> _panels;
#endif
QList<Overlay::Pointer> _overlaysToDelete;
unsigned int _stackOrder { 1 };
QReadWriteLock _lock;
QReadWriteLock _deleteLock;
#if OVERLAY_PANELS
QScriptEngine* _scriptEngine;
#endif
bool _enabled = true;
PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
@ -331,7 +338,7 @@ private:
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
Q_INVOKABLE RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
const QVector<OverlayID>& overlaysToInclude,
const QVector<OverlayID>& overlaysToDiscard,
bool visibleOnly = false, bool collidableOnly = false);

View file

@ -16,11 +16,15 @@
#include "OverlayPanel.h"
bool PanelAttachable::getParentVisible() const {
#if OVERLAY_PANELS
if (getParentPanel()) {
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
} else {
return true;
}
#else
return true;
#endif
}
QVariant PanelAttachable::getProperty(const QString& property) {
@ -61,11 +65,13 @@ void PanelAttachable::applyTransformTo(Transform& transform, bool force) {
if (force || usecTimestampNow() > _transformExpiry) {
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
#if OVERLAY_PANELS
if (getParentPanel()) {
getParentPanel()->applyTransformTo(transform, true);
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale());
}
#endif
}
}

View file

@ -30,6 +30,8 @@
#ifndef hifi_PanelAttachable_h
#define hifi_PanelAttachable_h
#define OVERLAY_PANELS 0
#include <memory>
#include <glm/glm.hpp>
@ -39,18 +41,21 @@
#include <QUuid>
class OverlayPanel;
class PanelAttachable {
public:
// getters
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
#endif
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
bool getParentVisible() const;
// setters
#if OVERLAY_PANELS
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
#endif
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
void setOffsetScale(float scale) { _offset.setScale(scale); }
@ -66,7 +71,9 @@ protected:
quint64 _transformExpiry = 0;
private:
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
#endif
Transform _offset;
};

View file

@ -32,21 +32,20 @@ QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay)
}
void QmlOverlay::buildQmlElement(const QUrl& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "buildQmlElement", Q_ARG(QUrl, url));
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->returnFromUiThread([=] {
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
// Create a shared ptr with a custom deleter lambda, that calls deleteLater
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
if (ptr) {
ptr->deleteLater();
}
});
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
// Create a shared ptr with a custom deleter lambda, that calls deleteLater
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
if (ptr) {
ptr->deleteLater();
}
});
while (!_qmlElement) {
qApp->processEvents();
}
return QVariant();
});
}
@ -55,20 +54,23 @@ QmlOverlay::~QmlOverlay() {
}
void QmlOverlay::setProperties(const QVariantMap& properties) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
return;
}
Overlay2D::setProperties(properties);
auto bounds = _bounds;
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([weakQmlElement, bounds, properties] {
// check to see if qmlElement still exists
auto qmlElement = weakQmlElement.lock();
if (qmlElement) {
qmlElement->setX(bounds.left());
qmlElement->setY(bounds.top());
qmlElement->setWidth(bounds.width());
qmlElement->setHeight(bounds.height());
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
}
});
// check to see if qmlElement still exists
auto qmlElement = weakQmlElement.lock();
if (qmlElement) {
qmlElement->setX(bounds.left());
qmlElement->setY(bounds.top());
qmlElement->setWidth(bounds.width());
qmlElement->setHeight(bounds.height());
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
}
}
void QmlOverlay::render(RenderArgs* args) {

View file

@ -32,7 +32,7 @@ public:
void render(RenderArgs* args) override;
private:
void buildQmlElement(const QUrl& url);
Q_INVOKABLE void buildQmlElement(const QUrl& url);
protected:
std::shared_ptr<QQuickItem> _qmlElement;

View file

@ -9,15 +9,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimationCache.h"
#include <QRunnable>
#include <QThreadPool>
#include "AnimationCache.h"
#include "AnimationLogging.h"
#include <shared/QtHelpers.h>
#include <Trace.h>
#include <StatTracker.h>
#include <Profile.h>
#include "AnimationLogging.h"
int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
AnimationCache::AnimationCache(QObject* parent) :
@ -31,7 +34,7 @@ AnimationCache::AnimationCache(QObject* parent) :
AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
if (QThread::currentThread() != thread()) {
AnimationPointer result;
QMetaObject::invokeMethod(this, "getAnimation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getAnimation",
Q_RETURN_ARG(AnimationPointer, result), Q_ARG(const QUrl&, url));
return result;
}
@ -97,7 +100,7 @@ bool Animation::isLoaded() const {
QStringList Animation::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(const_cast<Animation*>(this), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result));
return result;
}
@ -111,7 +114,7 @@ QStringList Animation::getJointNames() const {
QVector<FBXAnimationFrame> Animation::getFrames() const {
if (QThread::currentThread() != thread()) {
QVector<FBXAnimationFrame> result;
QMetaObject::invokeMethod(const_cast<Animation*>(this), "getFrames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getFrames",
Q_RETURN_ARG(QVector<FBXAnimationFrame>, result));
return result;
}

View file

@ -259,10 +259,21 @@ void AudioClient::customDeleter() {
void AudioClient::cleanupBeforeQuit() {
// FIXME: this should be put in customDeleter, but there is still a reference to this when it is called,
// so this must be explicitly, synchronously stopped
static ConditionalGuard guard;
if (QThread::currentThread() != thread()) {
// This will likely be called from the main thread, but we don't want to do blocking queued calls
// from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait
// for completion
// The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection
// in the code
QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
guard.wait();
return;
}
stop();
_checkDevicesTimer->stop();
guard.trigger();
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@ -1335,6 +1346,14 @@ void AudioClient::toggleMute() {
emit muteToggled();
}
void AudioClient::setNoiseReduction(bool enable) {
if (_isNoiseGateEnabled != enable) {
_isNoiseGateEnabled = enable;
emit noiseReductionChanged();
}
}
void AudioClient::setIsStereoInput(bool isStereoInput) {
if (isStereoInput != _isStereoInput) {
_isStereoInput = isStereoInput;
@ -1446,6 +1465,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
_audioInput->setBufferSize(_numInputCallbackBytes);
// different audio input devices may have different volumes
emit inputVolumeChanged(_audioInput->volume());
// how do we want to handle input working, but output not working?
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
@ -1703,7 +1724,7 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
// produce an oriented angle about the y-axis
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle;
} else {
@ -1847,3 +1868,10 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca
void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
}
void AudioClient::setInputVolume(float volume) {
if (_audioInput && volume != (float)_audioInput->volume()) {
_audioInput->setVolume(volume);
emit inputVolumeChanged(_audioInput->volume());
}
}

View file

@ -180,7 +180,8 @@ public slots:
virtual void setIsStereoInput(bool stereo) override;
void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; }
void setNoiseReduction(bool isNoiseGateEnabled);
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
@ -197,7 +198,7 @@ public slots:
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; }
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
void setInputVolume(float volume);
void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options);
@ -207,7 +208,9 @@ public slots:
void saveSettings();
signals:
bool muteToggled();
void inputVolumeChanged(float volume);
void muteToggled();
void noiseReductionChanged();
void mutedByMixer();
void inputReceived(const QByteArray& inputSamples);
void inputLoudnessChanged(float loudness);

View file

@ -9,10 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <qthread.h>
#include "SoundCache.h"
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include "AudioLogging.h"
#include "SoundCache.h"
static const int SOUNDS_LOADING_PRIORITY { -7 }; // Make sure sounds load after the low rez texture mips
@ -29,7 +32,7 @@ SoundCache::SoundCache(QObject* parent) :
SharedSoundPointer SoundCache::getSound(const QUrl& url) {
if (QThread::currentThread() != thread()) {
SharedSoundPointer result;
QMetaObject::invokeMethod(this, "getSound", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getSound",
Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url));
return result;
}

View file

@ -15,6 +15,7 @@
#include <glm/gtx/transform.hpp>
#include <glm/gtx/vector_query.hpp>
#include <shared/QtHelpers.h>
#include <DeferredLightingEffect.h>
#include <EntityTreeRenderer.h>
#include <NodeList.h>
@ -1010,7 +1011,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
int Avatar::getJointIndex(const QString& name) const {
if (QThread::currentThread() != thread()) {
int result;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointIndex", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(const QString&, name));
return result;
}
@ -1024,7 +1025,7 @@ int Avatar::getJointIndex(const QString& name) const {
QStringList Avatar::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result));
return result;
}
@ -1034,7 +1035,7 @@ QStringList Avatar::getJointNames() const {
glm::vec3 Avatar::getJointPosition(int index) const {
if (QThread::currentThread() != thread()) {
glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index));
return position;
}
@ -1046,7 +1047,7 @@ glm::vec3 Avatar::getJointPosition(int index) const {
glm::vec3 Avatar::getJointPosition(const QString& name) const {
if (QThread::currentThread() != thread()) {
glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name));
return position;
}
@ -1105,7 +1106,7 @@ static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, const Rig& ri
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>, attachmentData));
return;
}

View file

@ -25,6 +25,7 @@
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <shared/QtHelpers.h>
#include <QVariantGLM.h>
#include <Transform.h>
#include <NetworkAccessManager.h>
@ -1227,7 +1228,7 @@ bool AvatarData::isJointDataValid(int index) const {
}
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "isJointDataValid", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "isJointDataValid",
Q_RETURN_ARG(bool, result), Q_ARG(int, index));
return result;
}
@ -1240,7 +1241,7 @@ glm::quat AvatarData::getJointRotation(int index) const {
}
if (QThread::currentThread() != thread()) {
glm::quat result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointRotation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotation",
Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index));
return result;
}
@ -1255,7 +1256,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
}
if (QThread::currentThread() != thread()) {
glm::vec3 result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslation",
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
return result;
}
@ -1266,7 +1267,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
glm::vec3 AvatarData::getJointTranslation(const QString& name) const {
if (QThread::currentThread() != thread()) {
glm::vec3 result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslation",
Q_RETURN_ARG(glm::vec3, result), Q_ARG(const QString&, name));
return result;
}
@ -1344,7 +1345,7 @@ void AvatarData::clearJointData(const QString& name) {
bool AvatarData::isJointDataValid(const QString& name) const {
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "isJointDataValid", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "isJointDataValid",
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, name));
return result;
}
@ -1354,7 +1355,7 @@ bool AvatarData::isJointDataValid(const QString& name) const {
glm::quat AvatarData::getJointRotation(const QString& name) const {
if (QThread::currentThread() != thread()) {
glm::quat result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointRotation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotation",
Q_RETURN_ARG(glm::quat, result), Q_ARG(const QString&, name));
return result;
}
@ -1364,8 +1365,7 @@ glm::quat AvatarData::getJointRotation(const QString& name) const {
QVector<glm::quat> AvatarData::getJointRotations() const {
if (QThread::currentThread() != thread()) {
QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
"getJointRotations", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotations",
Q_RETURN_ARG(QVector<glm::quat>, result));
return result;
}
@ -1380,8 +1380,7 @@ QVector<glm::quat> AvatarData::getJointRotations() const {
void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
if (QThread::currentThread() != thread()) {
QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
"setJointRotations", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "setJointRotations",
Q_ARG(QVector<glm::quat>, jointRotations));
}
QWriteLocker writeLock(&_jointDataLock);
@ -1398,8 +1397,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
QVector<glm::vec3> AvatarData::getJointTranslations() const {
if (QThread::currentThread() != thread()) {
QVector<glm::vec3> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
"getJointTranslations", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslations",
Q_RETURN_ARG(QVector<glm::vec3>, result));
return result;
}
@ -1414,8 +1412,7 @@ QVector<glm::vec3> AvatarData::getJointTranslations() const {
void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
if (QThread::currentThread() != thread()) {
QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
"setJointTranslations", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "setJointTranslations",
Q_ARG(QVector<glm::vec3>, jointTranslations));
}
QWriteLocker writeLock(&_jointDataLock);
@ -1616,7 +1613,7 @@ void AvatarData::setDisplayName(const QString& displayName) {
QVector<AttachmentData> AvatarData::getAttachmentData() const {
if (QThread::currentThread() != thread()) {
QVector<AttachmentData> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAttachmentData", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAttachmentData",
Q_RETURN_ARG(QVector<AttachmentData>, result));
return result;
}
@ -2339,7 +2336,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID) {
AvatarEntityMap AvatarData::getAvatarEntityData() const {
AvatarEntityMap result;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAvatarEntityData", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAvatarEntityData",
Q_RETURN_ARG(AvatarEntityMap, result));
return result;
}
@ -2380,7 +2377,7 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() {
AvatarEntityIDs result;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAndClearRecentlyDetachedIDs", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAndClearRecentlyDetachedIDs",
Q_RETURN_ARG(AvatarEntityIDs, result));
return result;
}

View file

@ -627,6 +627,7 @@ public:
void markIdentityDataChanged() { _identityDataChanged = true; }
void pushIdentitySequenceNumber() { ++_identitySequenceNumber; };
bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; }
float getDensity() const { return _density; }

View file

@ -59,6 +59,48 @@ namespace controller {
makePosePair(Action::SPINE2, "Spine2"),
makePosePair(Action::HEAD, "Head"),
makePosePair(Action::LEFT_HAND_THUMB1, "LeftHandThumb1"),
makePosePair(Action::LEFT_HAND_THUMB2, "LeftHandThumb2"),
makePosePair(Action::LEFT_HAND_THUMB3, "LeftHandThumb3"),
makePosePair(Action::LEFT_HAND_THUMB4, "LeftHandThumb4"),
makePosePair(Action::LEFT_HAND_INDEX1, "LeftHandIndex1"),
makePosePair(Action::LEFT_HAND_INDEX2, "LeftHandIndex2"),
makePosePair(Action::LEFT_HAND_INDEX3, "LeftHandIndex3"),
makePosePair(Action::LEFT_HAND_INDEX4, "LeftHandIndex4"),
makePosePair(Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
makePosePair(Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
makePosePair(Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
makePosePair(Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
makePosePair(Action::LEFT_HAND_RING1, "LeftHandRing1"),
makePosePair(Action::LEFT_HAND_RING2, "LeftHandRing2"),
makePosePair(Action::LEFT_HAND_RING3, "LeftHandRing3"),
makePosePair(Action::LEFT_HAND_RING4, "LeftHandRing4"),
makePosePair(Action::LEFT_HAND_PINKY1, "LeftHandPinky1"),
makePosePair(Action::LEFT_HAND_PINKY2, "LeftHandPinky2"),
makePosePair(Action::LEFT_HAND_PINKY3, "LeftHandPinky3"),
makePosePair(Action::LEFT_HAND_PINKY4, "LeftHandPinky4"),
makePosePair(Action::RIGHT_HAND_THUMB1, "RightHandThumb1"),
makePosePair(Action::RIGHT_HAND_THUMB2, "RightHandThumb2"),
makePosePair(Action::RIGHT_HAND_THUMB3, "RightHandThumb3"),
makePosePair(Action::RIGHT_HAND_THUMB4, "RightHandThumb4"),
makePosePair(Action::RIGHT_HAND_INDEX1, "RightHandIndex1"),
makePosePair(Action::RIGHT_HAND_INDEX2, "RightHandIndex2"),
makePosePair(Action::RIGHT_HAND_INDEX3, "RightHandIndex3"),
makePosePair(Action::RIGHT_HAND_INDEX4, "RightHandIndex4"),
makePosePair(Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
makePosePair(Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
makePosePair(Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
makePosePair(Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
makePosePair(Action::RIGHT_HAND_RING1, "RightHandRing1"),
makePosePair(Action::RIGHT_HAND_RING2, "RightHandRing2"),
makePosePair(Action::RIGHT_HAND_RING3, "RightHandRing3"),
makePosePair(Action::RIGHT_HAND_RING4, "RightHandRing4"),
makePosePair(Action::RIGHT_HAND_PINKY1, "RightHandPinky1"),
makePosePair(Action::RIGHT_HAND_PINKY2, "RightHandPinky2"),
makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"),
makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"),
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),

View file

@ -104,6 +104,47 @@ enum class Action {
LEFT_ARM,
RIGHT_ARM,
LEFT_HAND_THUMB1,
LEFT_HAND_THUMB2,
LEFT_HAND_THUMB3,
LEFT_HAND_THUMB4,
LEFT_HAND_INDEX1,
LEFT_HAND_INDEX2,
LEFT_HAND_INDEX3,
LEFT_HAND_INDEX4,
LEFT_HAND_MIDDLE1,
LEFT_HAND_MIDDLE2,
LEFT_HAND_MIDDLE3,
LEFT_HAND_MIDDLE4,
LEFT_HAND_RING1,
LEFT_HAND_RING2,
LEFT_HAND_RING3,
LEFT_HAND_RING4,
LEFT_HAND_PINKY1,
LEFT_HAND_PINKY2,
LEFT_HAND_PINKY3,
LEFT_HAND_PINKY4,
RIGHT_HAND_THUMB1,
RIGHT_HAND_THUMB2,
RIGHT_HAND_THUMB3,
RIGHT_HAND_THUMB4,
RIGHT_HAND_INDEX1,
RIGHT_HAND_INDEX2,
RIGHT_HAND_INDEX3,
RIGHT_HAND_INDEX4,
RIGHT_HAND_MIDDLE1,
RIGHT_HAND_MIDDLE2,
RIGHT_HAND_MIDDLE3,
RIGHT_HAND_MIDDLE4,
RIGHT_HAND_RING1,
RIGHT_HAND_RING2,
RIGHT_HAND_RING3,
RIGHT_HAND_RING4,
RIGHT_HAND_PINKY1,
RIGHT_HAND_PINKY2,
RIGHT_HAND_PINKY3,
RIGHT_HAND_PINKY4,
NUM_ACTIONS,
};

View file

@ -101,7 +101,47 @@ Input::NamedVector StandardController::getAvailableInputs() const {
// Poses
makePair(LEFT_HAND, "LeftHand"),
makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"),
makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"),
makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"),
makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"),
makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"),
makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"),
makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"),
makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"),
makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
makePair(LEFT_HAND_RING1, "LeftHandRing1"),
makePair(LEFT_HAND_RING2, "LeftHandRing2"),
makePair(LEFT_HAND_RING3, "LeftHandRing3"),
makePair(LEFT_HAND_RING4, "LeftHandRing4"),
makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"),
makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"),
makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"),
makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"),
makePair(RIGHT_HAND, "RightHand"),
makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"),
makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"),
makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"),
makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"),
makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"),
makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"),
makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"),
makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"),
makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
makePair(RIGHT_HAND_RING1, "RightHandRing1"),
makePair(RIGHT_HAND_RING2, "RightHandRing2"),
makePair(RIGHT_HAND_RING3, "RightHandRing3"),
makePair(RIGHT_HAND_RING4, "RightHandRing4"),
makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"),
makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"),
makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"),
makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"),
makePair(LEFT_FOOT, "LeftFoot"),
makePair(RIGHT_FOOT, "RightFoot"),
makePair(RIGHT_ARM, "RightArm"),

View file

@ -11,14 +11,16 @@
#include <memory>
#include <math.h>
#include <glm/gtc/type_ptr.hpp>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <glm/gtc/type_ptr.hpp>
#include <QtGui/QWindow>
#include <QQuickWindow>
#include <shared/QtHelpers.h>
#include <ui/Menu.h>
#include <NumericalConstants.h>
#include <DependencyManager.h>
@ -289,7 +291,7 @@ glm::vec2 CompositorHelper::getReticleMaximumPosition() const {
void CompositorHelper::sendFakeMouseEvent() {
if (qApp->thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "sendFakeMouseEvent", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "sendFakeMouseEvent");
return;
}

View file

@ -17,6 +17,7 @@
#include <QScriptSyntaxCheckResult>
#include <QThreadPool>
#include <shared/QtHelpers.h>
#include <ColorUtils.h>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
@ -380,7 +381,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
// Only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "allocateModel",
Q_RETURN_ARG(ModelPointer, model),
Q_ARG(const QString&, url));
@ -397,7 +398,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
ModelPointer EntityTreeRenderer::updateModel(ModelPointer model, const QString& newUrl) {
// Only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "updateModel",
Q_RETURN_ARG(ModelPointer, model),
Q_ARG(ModelPointer, model),
Q_ARG(const QString&, newUrl));

View file

@ -9,19 +9,20 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityScriptingInterface.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include "EntityScriptingInterface.h"
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun>
#include "EntityItemID.h"
#include <shared/QtHelpers.h>
#include <VariantMapToScriptValue.h>
#include <SharedUtil.h>
#include <SpatialParentFinder.h>
#include "EntityItemID.h"
#include "EntitiesLogging.h"
#include "EntityDynamicFactoryInterface.h"
#include "EntityDynamicInterface.h"
@ -1488,7 +1489,7 @@ int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString
return -1;
}
int result;
QMetaObject::invokeMethod(_entityTree.get(), "getJointIndex", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(QUuid, entityID), Q_ARG(QString, name));
return result;
}
@ -1498,7 +1499,7 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
return QStringList();
}
QStringList result;
QMetaObject::invokeMethod(_entityTree.get(), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointNames",
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
return result;
}

View file

@ -20,6 +20,7 @@
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QNetworkInterface>
#include <shared/QtHelpers.h>
#include <ThreadHelpers.h>
#include <LogHandler.h>
#include <UUID.h>
@ -232,7 +233,7 @@ void NodeList::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
void NodeList::reset() {
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "reset", Qt::BlockingQueuedConnection);
QMetaObject::invokeMethod(this, "reset");
return;
}

View file

@ -9,22 +9,24 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ResourceCache.h"
#include <cfloat>
#include <cmath>
#include <assert.h>
#include <QThread>
#include <QTimer>
#include <SharedUtil.h>
#include <assert.h>
#include <shared/QtHelpers.h>
#include <Trace.h>
#include <Profile.h>
#include "NetworkAccessManager.h"
#include "NetworkLogging.h"
#include "NodeList.h"
#include "ResourceCache.h"
#include <Trace.h>
#include <Profile.h>
#define clamp(x, min, max) (((x) < (min)) ? (min) :\
(((x) > (max)) ? (max) :\
@ -178,7 +180,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) {
if (QThread::currentThread() != thread()) {
// Must be called in thread to ensure getResource returns a valid pointer
QMetaObject::invokeMethod(this, "prefetch", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "prefetch",
Q_RETURN_ARG(ScriptableResource*, result),
Q_ARG(QUrl, url), Q_ARG(void*, extra));
return result;
@ -301,7 +303,7 @@ QVariantList ResourceCache::getResourceList() {
QVariantList list;
if (QThread::currentThread() != thread()) {
// NOTE: invokeMethod does not allow a const QObject*
QMetaObject::invokeMethod(this, "getResourceList", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getResourceList",
Q_RETURN_ARG(QVariantList, list));
} else {
auto resources = _resources.uniqueKeys();

View file

@ -17,6 +17,7 @@
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <LogHandler.h>
#include "../NetworkLogging.h"
@ -276,7 +277,7 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
void Socket::clearConnections() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearConnections", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "clearConnections");
return;
}

View file

@ -11,6 +11,8 @@
#include <QThread>
#include <shared/QtHelpers.h>
#include "DisplayPlugin.h"
#include "InputPlugin.h"
#include "PluginManager.h"
@ -21,7 +23,7 @@ InputConfiguration::InputConfiguration() {
QStringList InputConfiguration::inputPlugins() {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(this, "inputPlugins", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "inputPlugins",
Q_RETURN_ARG(QStringList, result));
return result;
}
@ -42,7 +44,7 @@ QStringList InputConfiguration::inputPlugins() {
QStringList InputConfiguration::activeInputPlugins() {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(this, "activeInputPlugins", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "activeInputPlugins",
Q_RETURN_ARG(QStringList, result));
return result;
}
@ -64,7 +66,7 @@ QStringList InputConfiguration::activeInputPlugins() {
QString InputConfiguration::configurationLayout(QString pluginName) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "configurationLayout", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "configurationLayout",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, pluginName));
return result;
@ -81,7 +83,7 @@ QString InputConfiguration::configurationLayout(QString pluginName) {
void InputConfiguration::setConfigurationSettings(QJsonObject configurationSettings, QString pluginName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setConfigurationSettings", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "setConfigurationSettings",
Q_ARG(QJsonObject, configurationSettings),
Q_ARG(QString, pluginName));
return;
@ -97,7 +99,7 @@ void InputConfiguration::setConfigurationSettings(QJsonObject configurationSetti
QJsonObject InputConfiguration::configurationSettings(QString pluginName) {
if (QThread::currentThread() != thread()) {
QJsonObject result;
QMetaObject::invokeMethod(this, "configurationSettings", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "configurationSettings",
Q_RETURN_ARG(QJsonObject, result),
Q_ARG(QString, pluginName));
return result;
@ -113,7 +115,7 @@ QJsonObject InputConfiguration::configurationSettings(QString pluginName) {
void InputConfiguration::calibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "calibratePlugin", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "calibratePlugin");
return;
}
@ -128,7 +130,7 @@ void InputConfiguration::calibratePlugin(QString pluginName) {
bool InputConfiguration::uncalibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(this, "uncalibratePlugin", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "uncalibratePlugin",
Q_ARG(bool, result));
return result;
}

View file

@ -8,6 +8,8 @@
#include <QThread>
#include <shared/QtHelpers.h>
#include "ClipCache.h"
#include "impl/PointerClip.h"
#include "Logging.h"
@ -37,7 +39,7 @@ ClipCache::ClipCache(QObject* parent) :
NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
if (QThread::currentThread() != thread()) {
NetworkClipLoaderPointer result;
QMetaObject::invokeMethod(this, "getClipLoader", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getClipLoader",
Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url));
return result;
}

View file

@ -18,6 +18,7 @@
#include <glm/gtx/transform.hpp>
#include <glm/gtx/norm.hpp>
#include <shared/QtHelpers.h>
#include <GeometryUtil.h>
#include <PathUtils.h>
#include <PerfStat.h>
@ -870,7 +871,7 @@ bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& transl
QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(const_cast<Model*>(this), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Model*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result));
return result;
}

View file

@ -10,9 +10,12 @@
//
#include "Config.h"
#include "Task.h"
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include "Task.h"
using namespace task;
void JobConfig::setPresetList(const QJsonObject& object) {
@ -58,7 +61,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) {
void TaskConfig::refresh() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "refresh", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "refresh");
return;
}

View file

@ -13,6 +13,8 @@
#include <QVector3D>
#include <shared/QtHelpers.h>
#include "ScriptAudioInjector.h"
#include "ScriptEngineLogging.h"
@ -32,7 +34,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound
if (QThread::currentThread() != thread()) {
ScriptAudioInjector* injector = NULL;
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "playSound",
Q_RETURN_ARG(ScriptAudioInjector*, injector),
Q_ARG(SharedSoundPointer, sound),
Q_ARG(const AudioInjectorOptions&, injectorOptions));

View file

@ -14,6 +14,7 @@
#include <QtScript/QScriptValue>
#include <QtWidgets/QFileDialog>
#include <shared/QtHelpers.h>
#include <AssetClient.h>
#include <AssetUpload.h>
#include <BuildInfo.h>
@ -98,7 +99,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue
void RecordingScriptingInterface::startPlaying() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "startPlaying");
return;
}
@ -115,7 +116,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) {
void RecordingScriptingInterface::setPlayerTime(float time) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time));
BLOCKING_INVOKE_METHOD(this, "setPlayerTime", Q_ARG(float, time));
return;
}
_player->seek(time);
@ -147,7 +148,7 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode
void RecordingScriptingInterface::pausePlayer() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "pausePlayer");
return;
}
_player->pause();
@ -155,7 +156,7 @@ void RecordingScriptingInterface::pausePlayer() {
void RecordingScriptingInterface::stopPlaying() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "stopPlaying");
return;
}
_player->stop();
@ -176,7 +177,7 @@ void RecordingScriptingInterface::startRecording() {
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "startRecording");
return;
}
@ -199,7 +200,7 @@ QString RecordingScriptingInterface::getDefaultRecordingSaveDirectory() {
void RecordingScriptingInterface::saveRecording(const QString& filename) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "saveRecording",
Q_ARG(QString, filename));
return;
}
@ -220,7 +221,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(this, "saveRecordingToAsset", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "saveRecordingToAsset",
Q_RETURN_ARG(bool, result),
Q_ARG(QScriptValue, getClipAtpUrl));
return result;
@ -257,7 +258,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr
void RecordingScriptingInterface::loadLastRecording() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection);
BLOCKING_INVOKE_METHOD(this, "loadLastRecording");
return;
}

View file

@ -36,6 +36,7 @@
#include <QtScriptTools/QScriptEngineDebugger>
#include <shared/QtHelpers.h>
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
#include <AvatarData.h>
@ -436,12 +437,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
_fileNameString = url.toString();
_isReloading = reload;
// Check that script has a supported file extension
// Check that script has a supported file extension
if (!hasValidScriptSuffix(_fileNameString)) {
scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type");
emit errorLoadingScript(_fileNameString);
return;
}
}
const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
auto scriptCache = DependencyManager::get<ScriptCache>();
@ -964,7 +965,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
#endif
QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "evaluate",
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, sourceCode),
Q_ARG(const QString&, fileName),

View file

@ -12,6 +12,7 @@
#include <QtWidgets/QApplication>
#include <shared/QtHelpers.h>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#include <PathUtils.h>
@ -452,7 +453,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
bool activateMainWindow, bool reload) {
if (thread() != QThread::currentThread()) {
ScriptEngine* result { nullptr };
QMetaObject::invokeMethod(this, "loadScript", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptEngine*, result),
BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEngine*, result),
Q_ARG(QUrl, scriptFilename),
Q_ARG(bool, isUserLoaded),
Q_ARG(bool, loadScriptFromEditor),

View file

@ -1076,3 +1076,24 @@ void setMaxCores(uint8_t maxCores) {
SetProcessAffinityMask(process, newProcessAffinity);
#endif
}
#ifdef Q_OS_WIN
VOID CALLBACK parentDiedCallback(PVOID lpParameter, BOOLEAN timerOrWaitFired) {
if (!timerOrWaitFired && qApp) {
qDebug() << "Parent process died, quitting";
qApp->quit();
}
}
void watchParentProcess(int parentPID) {
DWORD processID = parentPID;
HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
HANDLE newHandle;
RegisterWaitForSingleObject(&newHandle, procHandle, parentDiedCallback, NULL, INFINITE, WT_EXECUTEONLYONCE);
}
#else
void watchParentProcess(int parentPID) {
qWarning() << "Parent PID option not implemented on this plateform";
}
#endif // Q_OS_WIN

View file

@ -235,4 +235,7 @@ const QString& getInterfaceSharedMemoryName();
void setMaxCores(uint8_t maxCores);
const QString PARENT_PID_OPTION = "parent-pid";
void watchParentProcess(int parentPID);
#endif // hifi_SharedUtil_h

View file

@ -16,6 +16,7 @@
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QWaitCondition>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QThread>
@ -34,4 +35,25 @@ void withLock(QMutex& lock, F function) {
void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority = QThread::InheritPriority);
void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority);
class ConditionalGuard {
public:
void trigger() {
QMutexLocker locker(&_mutex);
_triggered = true;
_condition.wakeAll();
}
bool wait(unsigned long time = ULONG_MAX) {
QMutexLocker locker(&_mutex);
if (!_triggered) {
_condition.wait(&_mutex, time);
}
return _triggered;
}
private:
QMutex _mutex;
QWaitCondition _condition;
bool _triggered { false };
};
#endif

View file

@ -0,0 +1,58 @@
//
// Created by Bradley Austin Davis on 2015/11/09
// Copyright 2013-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
//
#include "QtHelpers.h"
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
#include <QtCore/QLoggingCategory>
Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety")
namespace hifi { namespace qt {
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) {
if (QThread::currentThread() == qApp->thread()) {
qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function;
}
return QMetaObject::invokeMethod(obj, member,
Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) {
return blockingInvokeMethod(function, obj, member, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
} }

View file

@ -0,0 +1,52 @@
//
// Created by Bradley Austin Davis on 2017/06/29
// Copyright 2013-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
//
#pragma once
#ifndef hifi_Shared_QtHelpers_h
#define hifi_Shared_QtHelpers_h
#include <QtCore/QObject>
namespace hifi { namespace qt {
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
} }
#define BLOCKING_INVOKE_METHOD(obj, member, ...) \
hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__)
#endif

View file

@ -1,93 +0,0 @@
//
// Created by Sam Cake on 6/20/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 "DeviceTracker.h"
DeviceTracker::SingletonData::~SingletonData() {
// Destroy all the device registered
//TODO C++11 for (auto device = _devicesVector.begin(); device != _devicesVector.end(); device++) {
for (Vector::iterator device = _devicesVector.begin(); device != _devicesVector.end(); device++) {
delete (*device);
}
}
int DeviceTracker::getNumDevices() {
return (int)Singleton::get()->_devicesMap.size();
}
DeviceTracker::ID DeviceTracker::getDeviceID(const Name& name) {
//TODO C++11 auto deviceIt = Singleton::get()->_devicesMap.find(name);
Map::iterator deviceIt = Singleton::get()->_devicesMap.find(name);
if (deviceIt != Singleton::get()->_devicesMap.end()) {
return (*deviceIt).second;
} else {
return INVALID_DEVICE;
}
}
DeviceTracker* DeviceTracker::getDevice(const Name& name) {
return getDevice(getDeviceID(name));
}
DeviceTracker* DeviceTracker::getDevice(DeviceTracker::ID deviceID) {
if ((deviceID >= 0) && (deviceID < (int)(Singleton::get()->_devicesVector.size()))) {
return Singleton::get()->_devicesVector[ deviceID ];
} else {
return NULL;
}
}
DeviceTracker::ID DeviceTracker::registerDevice(const Name& name, DeviceTracker* device) {
// Check that the device exists, if not exit
if (!device) {
return INVALID_DEVICE;
}
// Look if the name is not already used
ID deviceID = getDeviceID(name);
if (deviceID >= 0) {
return INVALID_DEVICE_NAME;
}
// Good to register the device
deviceID = (ID)Singleton::get()->_devicesVector.size();
Singleton::get()->_devicesMap.insert(Map::value_type(name, deviceID));
Singleton::get()->_devicesVector.push_back(device);
device->assignIDAndName(deviceID, name);
return deviceID;
}
void DeviceTracker::destroyDevice(const Name& name) {
DeviceTracker::ID deviceID = getDeviceID(name);
if (deviceID != INVALID_DEVICE) {
delete Singleton::get()->_devicesVector[getDeviceID(name)];
Singleton::get()->_devicesVector[getDeviceID(name)] = nullptr;
}
}
void DeviceTracker::updateAll() {
//TODO C++11 for (auto deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) {
for (Vector::iterator deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) {
if ((*deviceIt))
(*deviceIt)->update();
}
}
// Core features of the Device Tracker
DeviceTracker::DeviceTracker() :
_ID(INVALID_DEVICE),
_name("Unkown")
{
}
DeviceTracker::~DeviceTracker() {
}
void DeviceTracker::update() {
}

View file

@ -1,115 +0,0 @@
//
// Created by Sam Cake on 6/20/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_DeviceTracker_h
#define hifi_DeviceTracker_h
#include <string>
#include <vector>
#include <map>
// Singleton template class
template < typename T >
class TemplateSingleton {
public:
static T* get() {
if ( !_singleton._one ) {
_singleton._one = new T();
}
return _singleton._one;
}
TemplateSingleton() :
_one(0)
{
}
~TemplateSingleton() {
if ( _one ) {
delete _one;
_one = 0;
}
}
private:
static TemplateSingleton< T > _singleton;
T* _one;
};
template <typename T>
TemplateSingleton<T> TemplateSingleton<T>::_singleton;
/// Base class for device trackers.
class DeviceTracker {
public:
// THe ID and Name types used to manage the pool of devices
typedef std::string Name;
typedef int ID;
static const ID INVALID_DEVICE = -1;
static const ID INVALID_DEVICE_NAME = -2;
// Singleton interface to register and query the devices currently connected
static int getNumDevices();
static ID getDeviceID(const Name& name);
static DeviceTracker* getDevice(ID deviceID);
static DeviceTracker* getDevice(const Name& name);
/// Update all the devices calling for their update() function
/// This should be called every frame by the main loop to update all the devices that pull their state
static void updateAll();
/// Register a device tracker to the factory
/// Right after creating a new DeviceTracker, it should be registered
/// This is why, it's recommended to use a factory static call in the specialized class
/// to create a new input device
///
/// \param name The Name under wich registering the device
/// \param parent The DeviceTracker
///
/// \return The Index of the newly registered device.
/// Valid if everything went well.
/// INVALID_DEVICE if the device is not valid (NULL)
/// INVALID_DEVICE_NAME if the name is already taken
static ID registerDevice(const Name& name, DeviceTracker* tracker);
static void destroyDevice(const Name& name);
// DeviceTracker interface
virtual void update();
/// Get the ID assigned to the Device when registered after creation, or INVALID_DEVICE if it hasn't been registered which should not happen.
ID getID() const { return _ID; }
/// Get the name assigned to the Device when registered after creation, or "Unknown" if it hasn't been registered which should not happen.
const Name& getName() const { return _name; }
typedef std::map< Name, ID > Map;
static const Map& getDevices() { return Singleton::get()->_devicesMap; }
protected:
DeviceTracker();
virtual ~DeviceTracker();
private:
ID _ID;
Name _name;
// this call is used by the singleton when the device tracker is currently beeing registered and beeing assigned an ID
void assignIDAndName( const ID id, const Name& name ) { _ID = id; _name = name; }
typedef std::vector< DeviceTracker* > Vector;
struct SingletonData {
Map _devicesMap;
Vector _devicesVector;
~SingletonData();
};
typedef TemplateSingleton< SingletonData > Singleton;
};
#endif // hifi_DeviceTracker_h

View file

@ -1,186 +0,0 @@
//
// Created by Sam Cake on 6/20/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 "MotionTracker.h"
// glm::mult(mat43, mat43) just the composition of the 2 matrices assuming they are in fact mat44 with the last raw = { 0, 0, 0, 1 }
namespace glm {
mat4x3 mult(const mat4& lhs, const mat4x3& rhs) {
vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x);
vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y);
vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z);
return mat4x3(
dot(lrx, rhs[0]),
dot(lry, rhs[0]),
dot(lrz, rhs[0]),
dot(lrx, rhs[1]),
dot(lry, rhs[1]),
dot(lrz, rhs[1]),
dot(lrx, rhs[2]),
dot(lry, rhs[2]),
dot(lrz, rhs[2]),
dot(lrx, rhs[3]) + lhs[3].x,
dot(lry, rhs[3]) + lhs[3].y,
dot(lrz, rhs[3]) + lhs[3].z
);
}
mat4x3 mult(const mat4x3& lhs, const mat4x3& rhs) {
vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x);
vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y);
vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z);
return mat4x3(
dot(lrx, rhs[0]),
dot(lry, rhs[0]),
dot(lrz, rhs[0]),
dot(lrx, rhs[1]),
dot(lry, rhs[1]),
dot(lrz, rhs[1]),
dot(lrx, rhs[2]),
dot(lry, rhs[2]),
dot(lrz, rhs[2]),
dot(lrx, rhs[3]) + lhs[3].x,
dot(lry, rhs[3]) + lhs[3].y,
dot(lrz, rhs[3]) + lhs[3].z
);
}
}
// MotionTracker
MotionTracker::MotionTracker() :
DeviceTracker()
{
_jointsArray.resize(1);
_jointsMap.insert(JointTracker::Map::value_type(Semantic("Root"), 0));
}
MotionTracker::~MotionTracker()
{
}
bool MotionTracker::isConnected() const {
return false;
}
MotionTracker::Index MotionTracker::addJoint(const Semantic& semantic, Index parent) {
// Check the parent
if (int(parent) < 0)
return INVALID_PARENT;
// Check that the semantic is not already in use
Index foundIndex = findJointIndex(semantic);
if (foundIndex >= 0) {
return INVALID_SEMANTIC;
}
// All good then allocate the joint
Index newIndex = (Index)_jointsArray.size();
_jointsArray.push_back(JointTracker(semantic, parent));
_jointsMap.insert(JointTracker::Map::value_type(semantic, newIndex));
return newIndex;
}
MotionTracker::Index MotionTracker::findJointIndex(const Semantic& semantic) const {
// TODO C++11 auto jointIt = _jointsMap.find(semantic);
JointTracker::Map::const_iterator jointIt = _jointsMap.find(semantic);
if (jointIt != _jointsMap.end()) {
return (*jointIt).second;
}
return INVALID_SEMANTIC;
}
void MotionTracker::updateAllAbsTransform() {
_jointsArray[0].updateAbsFromLocTransform(0);
// Because we know the hierarchy is stored from root down the branches let's just traverse and update
for (Index i = 1; i < (Index)(_jointsArray.size()); i++) {
JointTracker* joint = _jointsArray.data() + i;
joint->updateAbsFromLocTransform(_jointsArray.data() + joint->getParent());
}
}
// MotionTracker::JointTracker
MotionTracker::JointTracker::JointTracker() :
_locFrame(),
_absFrame(),
_semantic(""),
_parent(INVALID_PARENT),
_lastUpdate(1) // Joint inactive
{
}
MotionTracker::JointTracker::JointTracker(const Semantic& semantic, Index parent) :
_semantic(semantic),
_parent(parent),
_lastUpdate(1) // Joint inactive
{
}
MotionTracker::JointTracker::JointTracker(const JointTracker& tracker) :
_locFrame(tracker._locFrame),
_absFrame(tracker._absFrame),
_semantic(tracker._semantic),
_parent(tracker._parent),
_lastUpdate(tracker._lastUpdate)
{
}
void MotionTracker::JointTracker::updateAbsFromLocTransform(const JointTracker* parentJoint) {
if (parentJoint) {
editAbsFrame()._transform = (parentJoint->getAbsFrame()._transform * getLocFrame()._transform);
} else {
editAbsFrame()._transform = getLocFrame()._transform;
}
}
void MotionTracker::JointTracker::updateLocFromAbsTransform(const JointTracker* parentJoint) {
if (parentJoint) {
glm::mat4 ip = glm::inverse(parentJoint->getAbsFrame()._transform);
editLocFrame()._transform = (ip * getAbsFrame()._transform);
} else {
editLocFrame()._transform = getAbsFrame()._transform;
}
}
//--------------------------------------------------------------------------------------
// MotionTracker::Frame
//--------------------------------------------------------------------------------------
MotionTracker::Frame::Frame() :
_transform()
{
}
void MotionTracker::Frame::setRotation(const glm::quat& rotation) {
glm::mat3x3 rot = glm::mat3_cast(rotation);
_transform[0] = glm::vec4(rot[0], 0.0f);
_transform[1] = glm::vec4(rot[1], 0.0f);
_transform[2] = glm::vec4(rot[2], 0.0f);
}
void MotionTracker::Frame::getRotation(glm::quat& rotation) const {
rotation = glm::quat_cast(_transform);
}
void MotionTracker::Frame::setTranslation(const glm::vec3& translation) {
_transform[3] = glm::vec4(translation, 1.0f);
}
void MotionTracker::Frame::getTranslation(glm::vec3& translation) const {
translation = extractTranslation(_transform);
}

View file

@ -1,116 +0,0 @@
//
// Created by Sam Cake on 6/20/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_MotionTracker_h
#define hifi_MotionTracker_h
#include "DeviceTracker.h"
#include <GLMHelpers.h>
/// Base class for device trackers.
class MotionTracker : public DeviceTracker {
public:
class Frame {
public:
Frame();
glm::mat4 _transform;
void setRotation(const glm::quat& rotation);
void getRotation(glm::quat& rotatio) const;
void setTranslation(const glm::vec3& translation);
void getTranslation(glm::vec3& translation) const;
};
// Semantic and Index types to retreive the JointTrackers of this MotionTracker
typedef std::string Semantic;
typedef int32_t Index;
static const Index INVALID_SEMANTIC = -1;
static const Index INVALID_PARENT = -2;
class JointTracker {
public:
typedef std::vector< JointTracker > Vector;
typedef std::map< Semantic, Index > Map;
JointTracker();
JointTracker(const JointTracker& tracker);
JointTracker(const Semantic& semantic, Index parent);
const Frame& getLocFrame() const { return _locFrame; }
Frame& editLocFrame() { return _locFrame; }
void setLocFrame(const Frame& frame) { editLocFrame() = frame; }
const Frame& getAbsFrame() const { return _absFrame; }
Frame& editAbsFrame() { return _absFrame; }
void setAbsFrame(const Frame& frame) { editAbsFrame() = frame; }
const Semantic& getSemantic() const { return _semantic; }
const Index& getParent() const { return _parent; }
bool isActive() const { return (_lastUpdate <= 0); }
void tickNewFrame() { _lastUpdate++; }
void activeFrame() { _lastUpdate = 0; }
/// Update the loc/abs transform for this joint from the current abs/loc value and the specified parent joint abs frame
void updateLocFromAbsTransform(const JointTracker* parentJoint);
void updateAbsFromLocTransform(const JointTracker* parentJoint);
protected:
Frame _locFrame;
Frame _absFrame;
Semantic _semantic;
Index _parent;
int _lastUpdate;
};
virtual bool isConnected() const;
Index numJointTrackers() const { return (Index)_jointsArray.size(); }
/// Access a Joint from it's index.
/// Index 0 is always the "Root".
/// if the index is Invalid then returns NULL.
const JointTracker* getJointTracker(Index index) const { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); }
JointTracker* editJointTracker(Index index) { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); }
/// From a semantic, find the Index of the Joint.
/// \return the index of the mapped Joint or INVALID_SEMANTIC if the semantic is not knowned.
Index findJointIndex(const Semantic& semantic) const;
protected:
MotionTracker();
virtual ~MotionTracker();
JointTracker::Vector _jointsArray;
JointTracker::Map _jointsMap;
/// Adding joint is only done from the specialized Motion Tracker, hence this function is protected.
/// The hierarchy of joints must be created from the top down to the branches.
/// The "Root" node is at index 0 and exists at creation of the Motion Tracker.
///
/// \param semantic A joint is defined by it's semantic, the unique name mapping to it
/// \param parent The parent's index, the parent must be valid and correspond to a Joint that has been previously created
///
/// \return The Index of the newly created Joint.
/// Valid if everything went well.
/// INVALID_SEMANTIC if the semantic is already in use
/// INVALID_PARENT if the parent is not valid
Index addJoint(const Semantic& semantic, Index parent);
/// Update the absolute transform stack traversing the hierarchy from the root down the branches
/// This is a generic way to update all the Joint's absFrame by combining the locFrame going down the hierarchy branch.
void updateAllAbsTransform();
};
#endif // hifi_MotionTracker_h

View file

@ -15,6 +15,7 @@
#include <QtQuick/QQuickWindow>
#include <QtQml/QtQml>
#include <shared/QtHelpers.h>
#include <gl/GLHelpers.h>
#include <AbstractUriHandler.h>
@ -249,7 +250,7 @@ int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {
QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
if (QThread::currentThread() != thread()) {
QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton;
QMetaObject::invokeMethod(this, "messageBox", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "messageBox",
Q_RETURN_ARG(QMessageBox::StandardButton, result),
Q_ARG(Icon, icon),
Q_ARG(QString, title),
@ -351,7 +352,7 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const
QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "inputDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "inputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon),
Q_ARG(QString, title),
@ -366,7 +367,7 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q
QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "customInputDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "customInputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon),
Q_ARG(QString, title),
@ -640,7 +641,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) {
QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "fileOpenDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "fileOpenDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),
@ -662,7 +663,7 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir,
QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "fileSaveDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "fileSaveDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),
@ -686,7 +687,7 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir,
QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "existingDirectoryDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),
@ -773,7 +774,7 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir,
// ATP equivalent of fileOpenDialog().
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "assetOpenDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "assetOpenDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),

View file

@ -13,6 +13,7 @@
#include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
static const char* const URL_PROPERTY = "source";
@ -21,39 +22,51 @@ static const char* const SCRIPT_PROPERTY = "scriptUrl";
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context);
QmlWebWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] {
retVal = new QmlWebWindowClass();
retVal->initQml(properties);
}, true);
QmlWebWindowClass* retVal = new QmlWebWindowClass();
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
QMetaObject::invokeMethod(retVal, "initQml", Q_ARG(QVariantMap, properties));
} else {
retVal->initQml(properties);
}
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
return engine->newQObject(retVal);
}
QString QmlWebWindowClass::getURL() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant();
}
return _qmlWindow->property(URL_PROPERTY);
});
return result.toString();
QString QmlWebWindowClass::getURL() {
if (QThread::currentThread() != thread()) {
QString result;
BLOCKING_INVOKE_METHOD(this, "getURL", Q_RETURN_ARG(QString, result));
return result;
}
if (_qmlWindow.isNull()) {
return QString();
}
return _qmlWindow->property(URL_PROPERTY).toString();
}
void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, urlString);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setURL", Q_ARG(QString, urlString));
return;
}
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, urlString);
}
}
void QmlWebWindowClass::setScriptURL(const QString& script) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setScriptURL", Q_ARG(QString, script));
return;
}
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
}
}

View file

@ -20,7 +20,7 @@ public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
public slots:
QString getURL() const;
QString getURL();
void setURL(const QString& url);
void setScriptURL(const QString& script);

View file

@ -23,6 +23,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
static const char* const SOURCE_PROPERTY = "source";
@ -73,13 +74,15 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context);
QmlWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] {
retVal = new QmlWindowClass();
retVal->initQml(properties);
}, true);
QmlWindowClass* retVal = new QmlWindowClass();
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_ARG(QVariantMap, properties));
} else {
retVal->initQml(properties);
}
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
return engine->newQObject(retVal);
}
@ -90,9 +93,10 @@ QmlWindowClass::QmlWindowClass() {
void QmlWindowClass::initQml(QVariantMap properties) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
_source = properties[SOURCE_PROPERTY].toString();
#if QML_TOOL_WINDOW
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
if (_toolWindow) {
// Build the event bridge and wrapper on the main thread
_qmlWindow = offscreenUi->getToolWindow();
@ -103,10 +107,11 @@ void QmlWindowClass::initQml(QVariantMap properties) {
Q_ARG(QVariant, QVariant::fromValue(properties)));
Q_ASSERT(invokeResult);
} else {
#endif
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
_qmlWindow = object;
context->setContextProperty("eventBridge", this);
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
if (properties.contains(TITLE_PROPERTY)) {
@ -133,7 +138,9 @@ void QmlWindowClass::initQml(QVariantMap properties) {
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
});
#if QML_TOOL_WINDOW
}
#endif
Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
}
@ -207,56 +214,77 @@ QmlWindowClass::~QmlWindowClass() {
}
QQuickItem* QmlWindowClass::asQuickItem() const {
#if QML_TOOL_WINDOW
if (_toolWindow) {
return DependencyManager::get<OffscreenUi>()->getToolWindow();
}
#endif
return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
}
void QmlWindowClass::setVisible(bool visible) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setVisible", Q_ARG(bool, visible));
return;
}
QQuickItem* targetWindow = asQuickItem();
#if QML_TOOL_WINDOW
if (_toolWindow) {
// For tool window tabs we special case visibility as a function call on the tab parent
// The tool window itself has special logic based on whether any tabs are visible
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
} else {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
});
return;
}
#endif
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
}
bool QmlWindowClass::isVisible() {
if (QThread::currentThread() != thread()) {
bool result = false;
BLOCKING_INVOKE_METHOD(this, "isVisible", Q_RETURN_ARG(bool, result));
return result;
}
}
bool QmlWindowClass::isVisible() const {
// The tool window itself has special logic based on whether any tabs are enabled
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
if (_qmlWindow.isNull()) {
return QVariant::fromValue(false);
}
if (_toolWindow) {
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
} else {
return QVariant::fromValue(asQuickItem()->isVisible());
}
}).toBool();
if (_qmlWindow.isNull()) {
return false;
}
#if QML_TOOL_WINDOW
if (_toolWindow) {
return dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled();
}
#endif
return asQuickItem()->isVisible();
}
glm::vec2 QmlWindowClass::getPosition() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant(QPointF(0, 0));
}
return asQuickItem()->position();
});
return toGlm(result.toPointF());
glm::vec2 QmlWindowClass::getPosition() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(vec2, result));
return result;
}
if (_qmlWindow.isNull()) {
return {};
}
return toGlm(asQuickItem()->position());
}
void QmlWindowClass::setPosition(const glm::vec2& position) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.y));
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(vec2, position));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.y));
}
}
void QmlWindowClass::setPosition(int x, int y) {
@ -268,23 +296,29 @@ glm::vec2 toGlm(const QSizeF& size) {
return glm::vec2(size.width(), size.height());
}
glm::vec2 QmlWindowClass::getSize() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant(QSizeF(0, 0));
}
QQuickItem* targetWindow = asQuickItem();
return QSizeF(targetWindow->width(), targetWindow->height());
});
return toGlm(result.toSizeF());
glm::vec2 QmlWindowClass::getSize() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(vec2, result));
return result;
}
if (_qmlWindow.isNull()) {
return {};
}
QQuickItem* targetWindow = asQuickItem();
return vec2(targetWindow->width(), targetWindow->height());
}
void QmlWindowClass::setSize(const glm::vec2& size) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setSize", Q_ARG(vec2, size));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
}
void QmlWindowClass::setSize(int width, int height) {
@ -292,28 +326,37 @@ void QmlWindowClass::setSize(int width, int height) {
}
void QmlWindowClass::setTitle(const QString& title) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setTitle", Q_ARG(QString, title));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
}
void QmlWindowClass::close() {
if (_qmlWindow) {
if (_toolWindow) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
auto toolWindow = offscreenUi->getToolWindow();
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
Q_ARG(QVariant, _source));
Q_ASSERT(invokeResult);
});
} else {
_qmlWindow->deleteLater();
}
_qmlWindow = nullptr;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "close");
return;
}
#if QML_TOOL_WINDOW
if (_toolWindow) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto toolWindow = offscreenUi->getToolWindow();
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
Q_ARG(QVariant, _source));
Q_ASSERT(invokeResult);
return;
}
#endif
if (_qmlWindow) {
_qmlWindow->deleteLater();
}
_qmlWindow = nullptr;
}
void QmlWindowClass::hasMoved(QVector2D position) {
@ -325,10 +368,13 @@ void QmlWindowClass::hasClosed() {
}
void QmlWindowClass::raise() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "raise");
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
}
});
if (_qmlWindow) {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
}
}

View file

@ -19,6 +19,8 @@
class QScriptEngine;
class QScriptContext;
#define QML_TOOL_WINDOW 0
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWindowClass : public QObject {
Q_OBJECT
@ -31,18 +33,18 @@ public:
QmlWindowClass();
~QmlWindowClass();
virtual void initQml(QVariantMap properties);
Q_INVOKABLE virtual void initQml(QVariantMap properties);
QQuickItem* asQuickItem() const;
public slots:
bool isVisible() const;
bool isVisible();
void setVisible(bool visible);
glm::vec2 getPosition() const;
glm::vec2 getPosition();
void setPosition(const glm::vec2& position);
void setPosition(int x, int y);
glm::vec2 getSize() const;
glm::vec2 getSize();
void setSize(const glm::vec2& size);
void setSize(int width, int height);
void setTitle(const QString& title);
@ -85,9 +87,12 @@ protected:
virtual QString qmlSource() const { return "QmlWindow.qml"; }
#if QML_TOOL_WINDOW
// FIXME needs to be initialized in the ctor once we have support
// for tool window panes in QML
bool _toolWindow { false };
#endif
QPointer<QObject> _qmlWindow;
QString _source;

View file

@ -13,12 +13,12 @@
#include <QtWidgets/QShortcut>
#include <SettingHandle.h>
#include <shared/QtHelpers.h>
#include "../VrMenu.h"
#include "../OffscreenUi.h"
#include "Logging.h"
using namespace ui;
static QList<QString> groups;
@ -246,7 +246,7 @@ void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "setIsOptionChecked", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "setIsOptionChecked",
Q_ARG(const QString&, menuOption),
Q_ARG(bool, isChecked));
return;

View file

@ -25,6 +25,8 @@
#include <QtCore/QWaitCondition>
#include <shared/NsightHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <shared/QtHelpers.h>
#include <PerfStat.h>
#include <DependencyManager.h>
#include <NumericalConstants.h>
@ -34,7 +36,6 @@
#include <AccountManager.h>
#include <NetworkAccessManager.h>
#include <GLMHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/GLHelpers.h>
@ -886,28 +887,6 @@ QQmlContext* OffscreenQmlSurface::getSurfaceContext() {
return _qmlContext;
}
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "executeOnUiThread", blocking ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
Q_ARG(std::function<void()>, function));
return;
}
function();
}
QVariant OffscreenQmlSurface::returnFromUiThread(std::function<QVariant()> function) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "returnFromUiThread", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, result),
Q_ARG(std::function<QVariant()>, function));
return result;
}
return function();
}
void OffscreenQmlSurface::focusDestroyed(QObject *obj) {
_currentFocusItem = nullptr;
}

View file

@ -55,10 +55,6 @@ public:
return load(QUrl(qmlSourceFile), f);
}
void clearCache();
Q_INVOKABLE void executeOnUiThread(std::function<void()> function, bool blocking = false);
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
// Optional values for event handling
void setProxyWindow(QWindow* window);

View file

@ -11,6 +11,8 @@
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
#include <shared/QtHelpers.h>
QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent)
: QObject(parent), _qmlObject(qmlObject) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
@ -36,7 +38,7 @@ void QmlWrapper::writeProperties(QVariant propertyMap) {
QVariant QmlWrapper::readProperty(const QString& propertyName) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "readProperty", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName));
BLOCKING_INVOKE_METHOD(this, "readProperty", Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName));
return result;
}
@ -46,7 +48,7 @@ QVariant QmlWrapper::readProperty(const QString& propertyName) {
QVariant QmlWrapper::readProperties(const QVariant& propertyList) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "readProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList));
BLOCKING_INVOKE_METHOD(this, "readProperties", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList));
return result;
}

View file

@ -11,6 +11,7 @@
#include <QtCore/QThread>
#include <QtQml/QQmlProperty>
#include <shared/QtHelpers.h>
#include <PathUtils.h>
#include <DependencyManager.h>
#include <AccountManager.h>
@ -42,7 +43,7 @@ ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() {
TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {
TabletProxy* tabletProxy = nullptr;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getTablet", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId));
BLOCKING_INVOKE_METHOD(this, "getTablet", Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId));
return tabletProxy;
}
@ -213,20 +214,18 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
// create new desktop window
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
auto tabletRootWindow = new TabletRootWindow();
tabletRootWindow->initQml(QVariantMap());
auto quickItem = tabletRootWindow->asQuickItem();
_desktopWindow = tabletRootWindow;
QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
auto tabletRootWindow = new TabletRootWindow();
tabletRootWindow->initQml(QVariantMap());
auto quickItem = tabletRootWindow->asQuickItem();
_desktopWindow = tabletRootWindow;
QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));
QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));
QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
// forward qml surface events to interface js
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
});
// forward qml surface events to interface js
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
} else {
_state = State::Home;
removeButtonsFromToolbar();
@ -292,7 +291,7 @@ void TabletProxy::initialScreen(const QVariant& url) {
bool TabletProxy::isMessageDialogOpen() {
if (QThread::currentThread() != thread()) {
bool result = false;
QMetaObject::invokeMethod(this, "isMessageDialogOpen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
BLOCKING_INVOKE_METHOD(this, "isMessageDialogOpen", Q_RETURN_ARG(bool, result));
return result;
}
@ -317,7 +316,7 @@ void TabletProxy::emitWebEvent(const QVariant& msg) {
bool TabletProxy::isPathLoaded(const QVariant& path) {
if (QThread::currentThread() != thread()) {
bool result = false;
QMetaObject::invokeMethod(this, "isPathLoaded", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
BLOCKING_INVOKE_METHOD(this, "isPathLoaded", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
return result;
}
@ -480,7 +479,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
bool TabletProxy::pushOntoStack(const QVariant& path) {
if (QThread::currentThread() != thread()) {
bool result = false;
QMetaObject::invokeMethod(this, "pushOntoStack", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
BLOCKING_INVOKE_METHOD(this, "pushOntoStack", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
return result;
}
@ -606,7 +605,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) {
if (QThread::currentThread() != thread()) {
TabletButtonProxy* result = nullptr;
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties));
BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties));
return result;
}
@ -633,7 +632,7 @@ TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) {
bool TabletProxy::onHomeScreen() {
if (QThread::currentThread() != thread()) {
bool result = false;
QMetaObject::invokeMethod(this, "onHomeScreen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
BLOCKING_INVOKE_METHOD(this, "onHomeScreen", Q_RETURN_ARG(bool, result));
return result;
}
@ -840,7 +839,7 @@ void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {
QVariantMap TabletButtonProxy::getProperties() {
if (QThread::currentThread() != thread()) {
QVariantMap result;
QMetaObject::invokeMethod(this, "getProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariantMap, result));
BLOCKING_INVOKE_METHOD(this, "getProperties", Q_RETURN_ARG(QVariantMap, result));
return result;
}

View file

@ -12,6 +12,8 @@
#include <QtQuick/QQuickItem>
#include <QtScript/QScriptValue>
#include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "../OffscreenUi.h"
QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) {
@ -68,7 +70,7 @@ ToolbarProxy::ToolbarProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qml
ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) {
if (QThread::currentThread() != thread()) {
ToolbarButtonProxy* result = nullptr;
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties));
BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties));
return result;
}
@ -99,18 +101,14 @@ void ToolbarProxy::removeButton(const QVariant& name) {
ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) {
if (QThread::currentThread() != thread()) {
ToolbarProxy* result = nullptr;
QMetaObject::invokeMethod(this, "getToolbar", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId));
BLOCKING_INVOKE_METHOD(this, "getToolbar", Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto desktop = offscreenUi->getDesktop();
Qt::ConnectionType connectionType = Qt::AutoConnection;
if (QThread::currentThread() != desktop->thread()) {
connectionType = Qt::BlockingQueuedConnection;
}
QVariant resultVar;
bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId));
bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId));
if (!invokeResult) {
return nullptr;
}

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