mirror of
https://github.com/overte-org/overte.git
synced 2025-07-14 14:36:37 +02:00
Merge branch 'master' into destroy
This commit is contained in:
commit
2edefe3209
54 changed files with 688 additions and 389 deletions
|
@ -827,10 +827,6 @@ void Agent::processAgentAvatarAudio() {
|
||||||
void Agent::aboutToFinish() {
|
void Agent::aboutToFinish() {
|
||||||
setIsAvatar(false);// will stop timers for sending identity packets
|
setIsAvatar(false);// will stop timers for sending identity packets
|
||||||
|
|
||||||
if (_scriptEngine) {
|
|
||||||
_scriptEngine->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||||
|
|
||||||
|
@ -843,7 +839,6 @@ void Agent::aboutToFinish() {
|
||||||
|
|
||||||
// destroy all other created dependencies
|
// destroy all other created dependencies
|
||||||
DependencyManager::destroy<ScriptCache>();
|
DependencyManager::destroy<ScriptCache>();
|
||||||
DependencyManager::destroy<ScriptEngines>();
|
|
||||||
|
|
||||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||||
|
@ -862,3 +857,11 @@ void Agent::aboutToFinish() {
|
||||||
_encoder = nullptr;
|
_encoder = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Agent::stop() {
|
||||||
|
if (_scriptEngine) {
|
||||||
|
_scriptEngine->stop();
|
||||||
|
} else {
|
||||||
|
setFinished(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ public slots:
|
||||||
void setIsAvatar(bool isAvatar);
|
void setIsAvatar(bool isAvatar);
|
||||||
bool isAvatar() const { return _isAvatar; }
|
bool isAvatar() const { return _isAvatar; }
|
||||||
|
|
||||||
|
Q_INVOKABLE virtual void stop() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void requestScript();
|
void requestScript();
|
||||||
void scriptRequestFinished();
|
void scriptRequestFinished();
|
||||||
|
|
|
@ -46,6 +46,14 @@
|
||||||
"default": "40102",
|
"default": "40102",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"advanced": true
|
"advanced": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enable_packet_verification",
|
||||||
|
"label": "Enable Packet Verification",
|
||||||
|
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
|
||||||
|
"default": true,
|
||||||
|
"type": "checkbox",
|
||||||
|
"advanced": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -630,6 +630,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
||||||
|
|
||||||
void DomainServer::setupNodeListAndAssignments() {
|
void DomainServer::setupNodeListAndAssignments() {
|
||||||
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
||||||
|
static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification";
|
||||||
|
|
||||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||||
int domainServerPort = localPortValue.toInt();
|
int domainServerPort = localPortValue.toInt();
|
||||||
|
@ -696,6 +697,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool();
|
||||||
|
nodeList->setAuthenticatePackets(isAuthEnabled);
|
||||||
|
|
||||||
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||||
|
|
||||||
|
@ -1133,7 +1137,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
extendedHeaderStream << node->getUUID();
|
extendedHeaderStream << node->getUUID();
|
||||||
extendedHeaderStream << node->getLocalID();
|
extendedHeaderStream << node->getLocalID();
|
||||||
extendedHeaderStream << node->getPermissions();
|
extendedHeaderStream << node->getPermissions();
|
||||||
|
extendedHeaderStream << limitedNodeList->getAuthenticatePackets();
|
||||||
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
||||||
|
|
||||||
// always send the node their own UUID back
|
// always send the node their own UUID back
|
||||||
|
|
|
@ -62,6 +62,7 @@ Windows.ScrollingWindow {
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ Item {
|
||||||
anchors.fill: stackView
|
anchors.fill: stackView
|
||||||
id: controllerPrefereneces
|
id: controllerPrefereneces
|
||||||
objectName: "TabletControllerPreferences"
|
objectName: "TabletControllerPreferences"
|
||||||
showCategories: [( (HMD.active) ? "VR Movement" : "Movement"), "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
||||||
categoryProperties: {
|
categoryProperties: {
|
||||||
"VR Movement" : {
|
"VR Movement" : {
|
||||||
"User real-world height (meters)" : { "anchors.right" : "undefined" },
|
"User real-world height (meters)" : { "anchors.right" : "undefined" },
|
||||||
|
|
|
@ -6627,11 +6627,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||||
LocationScriptingInterface::locationSetter);
|
LocationScriptingInterface::locationSetter);
|
||||||
|
|
||||||
|
bool clientScript = scriptEngine->isClientScript();
|
||||||
|
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
|
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
|
||||||
#endif
|
#endif
|
||||||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
scriptEngine->registerFunction("QmlFragment", clientScript ? QmlFragmentClass::constructor : QmlFragmentClass::restricted_constructor);
|
||||||
scriptEngine->registerFunction("QmlFragment", QmlFragmentClass::constructor);
|
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||||
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||||
|
|
|
@ -412,9 +412,6 @@ void AvatarManager::clearOtherAvatars() {
|
||||||
while (avatarIterator != _avatarHash.end()) {
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||||
if (avatar != _myAvatar) {
|
if (avatar != _myAvatar) {
|
||||||
if (avatar->isInScene()) {
|
|
||||||
avatar->removeFromScene(avatar, scene, transaction);
|
|
||||||
}
|
|
||||||
handleRemovedAvatar(avatar);
|
handleRemovedAvatar(avatar);
|
||||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||||
assert(_avatar);
|
assert(_avatar);
|
||||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||||
|
cacheShapeDiameter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
|
@ -57,9 +58,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
||||||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
|
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
|
||||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
|
|
||||||
halfExtents.y = 0.0f;
|
|
||||||
_diameter = 2.0f * glm::length(halfExtents);
|
|
||||||
return getShapeManager()->getShape(shapeInfo);
|
return getShapeManager()->getShape(shapeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +96,10 @@ void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
|
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
|
||||||
_body->setLinearVelocity(velocity);
|
_body->setLinearVelocity(velocity);
|
||||||
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
||||||
|
// slam its rotation
|
||||||
|
btTransform newTransform = worldTrans;
|
||||||
|
newTransform.setRotation(glmToBullet(getObjectRotation()));
|
||||||
|
_body->setWorldTransform(newTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +143,10 @@ glm::vec3 AvatarMotionState::getObjectLinearVelocity() const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
|
glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
|
||||||
return _avatar->getWorldAngularVelocity();
|
// HACK: avatars use a capusle collision shape and their angularVelocity in the local simulation is unimportant.
|
||||||
|
// Therefore, as optimization toward support for larger crowds we ignore it and return zero.
|
||||||
|
//return _avatar->getWorldAngularVelocity();
|
||||||
|
return glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
@ -174,3 +179,28 @@ float AvatarMotionState::getMass() const {
|
||||||
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
|
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarMotionState::cacheShapeDiameter() {
|
||||||
|
if (_shape) {
|
||||||
|
// shape is capsuleY
|
||||||
|
btVector3 aabbMin, aabbMax;
|
||||||
|
btTransform transform;
|
||||||
|
transform.setIdentity();
|
||||||
|
_shape->getAabb(transform, aabbMin, aabbMax);
|
||||||
|
_diameter = (aabbMax - aabbMin).getX();
|
||||||
|
} else {
|
||||||
|
_diameter = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMotionState::setRigidBody(btRigidBody* body) {
|
||||||
|
ObjectMotionState::setRigidBody(body);
|
||||||
|
if (_body) {
|
||||||
|
// remove angular dynamics from this body
|
||||||
|
_body->setAngularFactor(0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMotionState::setShape(const btCollisionShape* shape) {
|
||||||
|
ObjectMotionState::setShape(shape);
|
||||||
|
cacheShapeDiameter();
|
||||||
|
}
|
||||||
|
|
|
@ -74,6 +74,10 @@ public:
|
||||||
friend class Avatar;
|
friend class Avatar;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void setRigidBody(btRigidBody* body) override;
|
||||||
|
void setShape(const btCollisionShape* shape) override;
|
||||||
|
void cacheShapeDiameter();
|
||||||
|
|
||||||
// the dtor had been made protected to force the compiler to verify that it is only
|
// the dtor had been made protected to force the compiler to verify that it is only
|
||||||
// ever called by the Avatar class dtor.
|
// ever called by the Avatar class dtor.
|
||||||
~AvatarMotionState();
|
~AvatarMotionState();
|
||||||
|
|
|
@ -1135,7 +1135,6 @@ void MyAvatar::saveData() {
|
||||||
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
||||||
settings.setValue("useSnapTurn", _useSnapTurn);
|
settings.setValue("useSnapTurn", _useSnapTurn);
|
||||||
settings.setValue("userHeight", getUserHeight());
|
settings.setValue("userHeight", getUserHeight());
|
||||||
settings.setValue("flyingDesktop", getFlyingDesktopPref());
|
|
||||||
settings.setValue("flyingHMD", getFlyingHMDPref());
|
settings.setValue("flyingHMD", getFlyingHMDPref());
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
@ -1289,7 +1288,6 @@ void MyAvatar::loadData() {
|
||||||
|
|
||||||
// Flying preferences must be loaded before calling setFlyingEnabled()
|
// Flying preferences must be loaded before calling setFlyingEnabled()
|
||||||
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
||||||
setFlyingDesktopPref(firstRunVal.get() ? true : settings.value("flyingDesktop").toBool());
|
|
||||||
setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool());
|
setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool());
|
||||||
setFlyingEnabled(getFlyingEnabled());
|
setFlyingEnabled(getFlyingEnabled());
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The Audio API features tools to help control audio contexts and settings.
|
* The <code>Audio</code> API provides facilities to interact with audio inputs and outputs and to play sounds.
|
||||||
*
|
*
|
||||||
* @namespace Audio
|
* @namespace Audio
|
||||||
*
|
*
|
||||||
|
@ -35,12 +35,21 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
* @hifi-server-entity
|
* @hifi-server-entity
|
||||||
* @hifi-assignment-client
|
* @hifi-assignment-client
|
||||||
*
|
*
|
||||||
* @property {boolean} muted
|
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||||
* @property {boolean} noiseReduction
|
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
||||||
* @property {number} inputVolume
|
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||||
* @property {number} inputLevel <em>Read-only.</em>
|
* above the noise floor.
|
||||||
* @property {string} context <em>Read-only.</em>
|
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
||||||
* @property {} devices <em>Read-only.</em>
|
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
|
||||||
|
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> – <code>1.0</code>.
|
||||||
|
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
||||||
|
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||||
|
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||||
|
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
|
||||||
|
* @property {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
|
||||||
|
* <em>Read-only.</em>
|
||||||
|
* @property {object} devices <em>Read-only.</em> <strong>Deprecated:</strong> This property is deprecated and will be
|
||||||
|
* removed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||||
|
@ -69,45 +78,91 @@ public:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Audio.setInputDevice
|
* @function Audio.setInputDevice
|
||||||
* @param {} device
|
* @param {object} device
|
||||||
* @param {boolean} isHMD
|
* @param {boolean} isHMD
|
||||||
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Audio.setOutputDevice
|
* @function Audio.setOutputDevice
|
||||||
* @param {} device
|
* @param {object} device
|
||||||
* @param {boolean} isHMD
|
* @param {boolean} isHMD
|
||||||
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
|
||||||
|
* come from either the domain's audio zone if used — configured on the server — or as scripted by
|
||||||
|
* {@link Audio.setReverbOptions|setReverbOptions}.
|
||||||
* @function Audio.setReverb
|
* @function Audio.setReverb
|
||||||
* @param {boolean} enable
|
* @param {boolean} enable - <code>true</code> to enable reverberation, <code>false</code> to disable.
|
||||||
*/
|
* @example <caption>Enable reverberation for a short while.</caption>
|
||||||
|
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||||
|
* var injector;
|
||||||
|
* var injectorOptions = {
|
||||||
|
* position: MyAvatar.position
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* print("Reverb OFF");
|
||||||
|
* Audio.setReverb(false);
|
||||||
|
* injector = Audio.playSound(sound, injectorOptions);
|
||||||
|
* }, 1000);
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* var reverbOptions = new AudioEffectOptions();
|
||||||
|
* reverbOptions.roomSize = 100;
|
||||||
|
* Audio.setReverbOptions(reverbOptions);
|
||||||
|
* print("Reverb ON");
|
||||||
|
* Audio.setReverb(true);
|
||||||
|
* }, 4000);
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* print("Reverb OFF");
|
||||||
|
* Audio.setReverb(false);
|
||||||
|
* }, 8000); */
|
||||||
Q_INVOKABLE void setReverb(bool enable);
|
Q_INVOKABLE void setReverb(bool enable);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
|
||||||
* @function Audio.setReverbOptions
|
* @function Audio.setReverbOptions
|
||||||
* @param {AudioEffectOptions} options
|
* @param {AudioEffectOptions} options - The reverberation options.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
|
||||||
* @function Audio.startRecording
|
* @function Audio.startRecording
|
||||||
* @param {string} filename
|
* @param {string} filename - The path and name of the file to make the recording in. Should have a <code>.wav</code>
|
||||||
* @returns {boolean}
|
* extension. The file is overwritten if it already exists.
|
||||||
|
* @returns {boolean} <code>true</code> if the specified file could be opened and audio recording has started, otherwise
|
||||||
|
* <code>false</code>.
|
||||||
|
* @example <caption>Make a 10 second audio recording.</caption>
|
||||||
|
* var filename = File.getTempDir() + "/audio.wav";
|
||||||
|
* if (Audio.startRecording(filename)) {
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* Audio.stopRecording();
|
||||||
|
* print("Audio recording made in: " + filename);
|
||||||
|
* }, 10000);
|
||||||
|
*
|
||||||
|
* } else {
|
||||||
|
* print("Could not make an audio recording in: " + filename);
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool startRecording(const QString& filename);
|
Q_INVOKABLE bool startRecording(const QString& filename);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Finish making an audio recording started with {@link Audio.startRecording|startRecording}.
|
||||||
* @function Audio.stopRecording
|
* @function Audio.stopRecording
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void stopRecording();
|
Q_INVOKABLE void stopRecording();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Check whether an audio recording is currently being made.
|
||||||
* @function Audio.getRecording
|
* @function Audio.getRecording
|
||||||
* @returns {boolean}
|
* @returns {boolean} <code>true</code> if an audio recording is currently being made, otherwise <code>false</code>.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool getRecording();
|
Q_INVOKABLE bool getRecording();
|
||||||
|
|
||||||
|
@ -116,40 +171,54 @@ signals:
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Audio.nop
|
* @function Audio.nop
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
|
* @deprecated This signal is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
void nop();
|
void nop();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the audio input is muted or unmuted.
|
||||||
* @function Audio.mutedChanged
|
* @function Audio.mutedChanged
|
||||||
* @param {boolean} isMuted
|
* @param {boolean} isMuted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
|
* @example <caption>Report when audio input is muted or unmuted</caption>
|
||||||
|
* Audio.mutedChanged.connect(function (isMuted) {
|
||||||
|
* print("Audio muted: " + isMuted);
|
||||||
|
* });
|
||||||
*/
|
*/
|
||||||
void mutedChanged(bool isMuted);
|
void mutedChanged(bool isMuted);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the audio input noise reduction is enabled or disabled.
|
||||||
* @function Audio.noiseReductionChanged
|
* @function Audio.noiseReductionChanged
|
||||||
* @param {boolean} isEnabled
|
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction is enabled, otherwise <code>false</code>.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void noiseReductionChanged(bool isEnabled);
|
void noiseReductionChanged(bool isEnabled);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the input audio volume changes.
|
||||||
* @function Audio.inputVolumeChanged
|
* @function Audio.inputVolumeChanged
|
||||||
* @param {number} volume
|
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> –
|
||||||
|
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
|
||||||
|
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
|
||||||
|
* and <code>1.0</code>.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void inputVolumeChanged(float volume);
|
void inputVolumeChanged(float volume);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the input audio level changes.
|
||||||
* @function Audio.inputLevelChanged
|
* @function Audio.inputLevelChanged
|
||||||
* @param {number} level
|
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) – <code>1.0</code> (the
|
||||||
|
* onset of clipping).
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void inputLevelChanged(float level);
|
void inputLevelChanged(float level);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the current context of the audio changes.
|
||||||
* @function Audio.contextChanged
|
* @function Audio.contextChanged
|
||||||
* @param {string} context
|
* @param {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void contextChanged(const QString& context);
|
void contextChanged(const QString& context);
|
||||||
|
@ -158,7 +227,7 @@ public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* @function Audio.onContextChanged
|
* @function Audio.onContextChanged
|
||||||
* @returns {Signal}
|
* @deprecated This function is deprecated and will be removed.
|
||||||
*/
|
*/
|
||||||
void onContextChanged();
|
void onContextChanged();
|
||||||
|
|
||||||
|
|
|
@ -265,42 +265,6 @@ void setupPreferences() {
|
||||||
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter));
|
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QString MOVEMENT{ "Movement" };
|
|
||||||
{
|
|
||||||
|
|
||||||
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
|
|
||||||
auto getter = [myAvatar]()->bool { return myAvatar->useAdvancedMovementControls(); };
|
|
||||||
auto setter = [myAvatar](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
|
|
||||||
preferences->addPreference(new CheckPreference(MOVEMENT,
|
|
||||||
QStringLiteral("Advanced movement for hand controllers"),
|
|
||||||
getter, setter));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
|
||||||
auto setter = [myAvatar](int value) { myAvatar->setSnapTurn(value == 0); };
|
|
||||||
auto preference = new RadioButtonsPreference(MOVEMENT, "Snap turn / Smooth turn", getter, setter);
|
|
||||||
QStringList items;
|
|
||||||
items << "Snap turn" << "Smooth turn";
|
|
||||||
preference->setItems(items);
|
|
||||||
preferences->addPreference(preference);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
|
|
||||||
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
|
|
||||||
auto preference = new SpinnerPreference(MOVEMENT, "User real-world height (meters)", getter, setter);
|
|
||||||
preference->setMin(1.0f);
|
|
||||||
preference->setMax(2.2f);
|
|
||||||
preference->setDecimals(3);
|
|
||||||
preference->setStep(0.001f);
|
|
||||||
preferences->addPreference(preference);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] {
|
|
||||||
qApp->resetSensors();
|
|
||||||
});
|
|
||||||
preferences->addPreference(preference);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString VR_MOVEMENT{ "VR Movement" };
|
static const QString VR_MOVEMENT{ "VR Movement" };
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -314,7 +278,7 @@ void setupPreferences() {
|
||||||
{
|
{
|
||||||
auto getter = [myAvatar]()->bool { return myAvatar->getFlyingHMDPref(); };
|
auto getter = [myAvatar]()->bool { return myAvatar->getFlyingHMDPref(); };
|
||||||
auto setter = [myAvatar](bool value) { myAvatar->setFlyingHMDPref(value); };
|
auto setter = [myAvatar](bool value) { myAvatar->setFlyingHMDPref(value); };
|
||||||
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter));
|
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
||||||
|
|
|
@ -59,28 +59,29 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV
|
||||||
}
|
}
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Reverberation options that can be used to initialize an {@link AudioEffectOptions} object when created.
|
||||||
* @typedef {object} AudioEffectOptions.ReverbOptions
|
* @typedef {object} AudioEffectOptions.ReverbOptions
|
||||||
* @property {number} bandwidth
|
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
|
||||||
* @property {number} preDelay
|
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
|
||||||
* @property {number} lateDelay
|
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
|
||||||
* @property {number} reverbTime
|
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
|
||||||
* @property {number} earlyDiffusion
|
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
|
||||||
* @property {number} lateDiffusion
|
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
|
||||||
* @property {number} roomSize
|
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
|
||||||
* @property {number} density
|
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
|
||||||
* @property {number} bassMult
|
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
|
||||||
* @property {number} bassFreq
|
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
|
||||||
* @property {number} highGain
|
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
|
||||||
* @property {number} highFreq
|
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
|
||||||
* @property {number} modRate
|
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
|
||||||
* @property {number} modDepth
|
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
|
||||||
* @property {number} earlyGain
|
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
|
||||||
* @property {number} lateGain
|
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
|
||||||
* @property {number} earlyMixLeft
|
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
|
||||||
* @property {number} earlyMixRight
|
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
|
||||||
* @property {number} lateMixLeft
|
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||||
* @property {number} lateMixRight
|
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||||
* @property {number} wetDryMix
|
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
|
||||||
*/
|
*/
|
||||||
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
|
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
|
||||||
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);
|
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);
|
||||||
|
|
|
@ -16,35 +16,39 @@
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Audio effect options used by the {@link Audio} API.
|
||||||
|
*
|
||||||
|
* <p>Create using <code>new AudioEffectOptions(reverbOptions)</code>.</p>
|
||||||
|
*
|
||||||
* @class AudioEffectOptions
|
* @class AudioEffectOptions
|
||||||
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null]
|
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - Reverberation options.
|
||||||
*
|
*
|
||||||
* @hifi-interface
|
* @hifi-interface
|
||||||
* @hifi-client-entity
|
* @hifi-client-entity
|
||||||
* @hifi-server-entity
|
* @hifi-server-entity
|
||||||
* @hifi-assignment-client
|
* @hifi-assignment-client
|
||||||
*
|
*
|
||||||
* @property {number} bandwidth=10000
|
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
|
||||||
* @property {number} preDelay=20
|
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
|
||||||
* @property {number} lateDelay=0
|
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
|
||||||
* @property {number} reverbTime=2
|
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
|
||||||
* @property {number} earlyDiffusion=100
|
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
|
||||||
* @property {number} lateDiffusion=100
|
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
|
||||||
* @property {number} roomSize=50
|
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
|
||||||
* @property {number} density=100
|
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
|
||||||
* @property {number} bassMult=1.5
|
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
|
||||||
* @property {number} bassFreq=250
|
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
|
||||||
* @property {number} highGain=-6
|
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
|
||||||
* @property {number} highFreq=3000
|
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
|
||||||
* @property {number} modRate=2.3
|
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
|
||||||
* @property {number} modDepth=50
|
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
|
||||||
* @property {number} earlyGain=0
|
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
|
||||||
* @property {number} lateGain=0
|
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
|
||||||
* @property {number} earlyMixLeft=20
|
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
|
||||||
* @property {number} earlyMixRight=20
|
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
|
||||||
* @property {number} lateMixLeft=90
|
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||||
* @property {number} lateMixRight=90
|
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||||
* @property {number} wetDryMix=50
|
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AudioEffectOptions : public QObject {
|
class AudioEffectOptions : public QObject {
|
||||||
|
|
|
@ -45,6 +45,23 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Configures how an audio injector plays its audio.
|
||||||
|
* @typedef {object} AudioInjector.AudioInjectorOptions
|
||||||
|
* @property {Vec3} position=Vec3.ZERO - The position in the domain to play the sound.
|
||||||
|
* @property {Quat} orientation=Quat.IDENTITY - The orientation in the domain to play the sound in.
|
||||||
|
* @property {number} volume=1.0 - Playback volume, between <code>0.0</code> and <code>1.0</code>.
|
||||||
|
* @property {number} pitch=1.0 - Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate to
|
||||||
|
* resample the sound at, range <code>0.0625</code> – <code>16.0</code>. A value of <code>0.0625</code> lowers the
|
||||||
|
* pitch by 2 octaves; <code>1.0</code> is no change in pitch; <code>16.0</code> raises the pitch by 2 octaves.
|
||||||
|
* @property {boolean} loop=false - If <code>true</code>, the sound is played repeatedly until playback is stopped.
|
||||||
|
* @property {number} secondOffset=0 - Starts playback from a specified time (seconds) within the sound file, ≥
|
||||||
|
* <code>0</code>.
|
||||||
|
* @property {boolean} localOnly=false - IF <code>true</code>, the sound is played back locally on the client rather than to
|
||||||
|
* others via the audio mixer.
|
||||||
|
* @property {boolean} ignorePenumbra=false - <strong>Deprecated:</strong> This property is deprecated and will be
|
||||||
|
* removed.
|
||||||
|
*/
|
||||||
void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) {
|
void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) {
|
||||||
if (!object.isObject()) {
|
if (!object.isObject()) {
|
||||||
qWarning() << "Audio injector options is not an object.";
|
qWarning() << "Audio injector options is not an object.";
|
||||||
|
|
|
@ -79,6 +79,14 @@ private:
|
||||||
typedef QSharedPointer<Sound> SharedSoundPointer;
|
typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}.
|
||||||
|
* <p>Supported formats:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>WAV: 16-bit uncompressed WAV at any sample rate, with 1 (mono), 2(stereo), or 4 (ambisonic) channels.</li>
|
||||||
|
* <li>MP3: Mono or stereo, at any sample rate.</li>
|
||||||
|
* <li>RAW: 48khz 16-bit mono or stereo. Filename must include <code>".stereo"</code> to be interpreted as stereo.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* @class SoundObject
|
* @class SoundObject
|
||||||
*
|
*
|
||||||
* @hifi-interface
|
* @hifi-interface
|
||||||
|
@ -86,8 +94,9 @@ typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||||
* @hifi-server-entity
|
* @hifi-server-entity
|
||||||
* @hifi-assignment-client
|
* @hifi-assignment-client
|
||||||
*
|
*
|
||||||
* @property {boolean} downloaded
|
* @property {boolean} downloaded - <code>true</code> if the sound has been downloaded and is ready to be played, otherwise
|
||||||
* @property {number} duration
|
* <code>false</code>.
|
||||||
|
* @property {number} duration - The duration of the sound, in seconds.
|
||||||
*/
|
*/
|
||||||
class SoundScriptingInterface : public QObject {
|
class SoundScriptingInterface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -103,6 +112,7 @@ public:
|
||||||
float getDuration() { return _sound->getDuration(); }
|
float getDuration() { return _sound->getDuration(); }
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the sound has been downloaded and is ready to be played.
|
||||||
* @function SoundObject.ready
|
* @function SoundObject.ready
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -48,9 +48,11 @@ public:
|
||||||
SoundCacheScriptingInterface();
|
SoundCacheScriptingInterface();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Loads the content of an audio file into a {@link SoundObject}, ready for playback by {@link Audio.playSound}.
|
||||||
* @function SoundCache.getSound
|
* @function SoundCache.getSound
|
||||||
* @param {string} url
|
* @param {string} url - The URL of the audio file to load — Web, ATP, or file. See {@link SoundObject} for supported
|
||||||
* @returns {SoundObject}
|
* formats.
|
||||||
|
* @returns {SoundObject} The sound ready for playback.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
||||||
};
|
};
|
||||||
|
|
|
@ -328,9 +328,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
||||||
|
|
||||||
if (sourceNode) {
|
if (sourceNode) {
|
||||||
bool verifiedPacket = !PacketTypeEnum::getNonVerifiedPackets().contains(headerType);
|
bool verifiedPacket = !PacketTypeEnum::getNonVerifiedPackets().contains(headerType);
|
||||||
bool ignoreVerification = isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType);
|
bool verificationEnabled = !(isDomainServer() && PacketTypeEnum::getDomainIgnoredVerificationPackets().contains(headerType))
|
||||||
|
&& _useAuthentication;
|
||||||
|
|
||||||
if (verifiedPacket && !ignoreVerification) {
|
if (verifiedPacket && verificationEnabled) {
|
||||||
|
|
||||||
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
||||||
QByteArray expectedHash;
|
QByteArray expectedHash;
|
||||||
|
@ -383,7 +384,7 @@ void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAut
|
||||||
packet.writeSourceID(getSessionLocalID());
|
packet.writeSourceID(getSessionLocalID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hmacAuth
|
if (_useAuthentication && hmacAuth
|
||||||
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
|
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
|
||||||
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
|
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
|
||||||
packet.writeVerificationHash(*hmacAuth);
|
packet.writeVerificationHash(*hmacAuth);
|
||||||
|
|
|
@ -307,6 +307,8 @@ public:
|
||||||
|
|
||||||
bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr);
|
bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr);
|
||||||
bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); }
|
bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); }
|
||||||
|
void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; }
|
||||||
|
bool getAuthenticatePackets() const { return _useAuthentication; }
|
||||||
|
|
||||||
static void makeSTUNRequestPacket(char* stunRequestPacket);
|
static void makeSTUNRequestPacket(char* stunRequestPacket);
|
||||||
|
|
||||||
|
@ -394,6 +396,7 @@ protected:
|
||||||
HifiSockAddr _publicSockAddr;
|
HifiSockAddr _publicSockAddr;
|
||||||
HifiSockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
|
HifiSockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
|
||||||
bool _hasTCPCheckedLocalSocket { false };
|
bool _hasTCPCheckedLocalSocket { false };
|
||||||
|
bool _useAuthentication { true };
|
||||||
|
|
||||||
PacketReceiver* _packetReceiver;
|
PacketReceiver* _packetReceiver;
|
||||||
|
|
||||||
|
|
|
@ -665,6 +665,10 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
|
||||||
NodePermissions newPermissions;
|
NodePermissions newPermissions;
|
||||||
packetStream >> newPermissions;
|
packetStream >> newPermissions;
|
||||||
setPermissions(newPermissions);
|
setPermissions(newPermissions);
|
||||||
|
// Is packet authentication enabled?
|
||||||
|
bool isAuthenticated;
|
||||||
|
packetStream >> isAuthenticated;
|
||||||
|
setAuthenticatePackets(isAuthenticated);
|
||||||
|
|
||||||
// pull each node in the packet
|
// pull each node in the packet
|
||||||
while (packetStream.device()->pos() < message->getSize()) {
|
while (packetStream.device()->pos() < message->getSize()) {
|
||||||
|
|
|
@ -120,7 +120,7 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||||
if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||||
qCDebug(networking) << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server"
|
qCDebug(networking) << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server"
|
||||||
<< "Stopping the current assignment";
|
<< "Stopping the current assignment";
|
||||||
setFinished(true);
|
stop();
|
||||||
} else {
|
} else {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn");
|
QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn");
|
||||||
|
@ -132,5 +132,5 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||||
|
|
||||||
void ThreadedAssignment::domainSettingsRequestFailed() {
|
void ThreadedAssignment::domainSettingsRequestFailed() {
|
||||||
qCDebug(networking) << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
qCDebug(networking) << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||||
setFinished(true);
|
stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ public:
|
||||||
ThreadedAssignment(ReceivedMessage& message);
|
ThreadedAssignment(ReceivedMessage& message);
|
||||||
~ThreadedAssignment() { stop(); }
|
~ThreadedAssignment() { stop(); }
|
||||||
|
|
||||||
void setFinished(bool isFinished);
|
|
||||||
virtual void aboutToFinish() { };
|
virtual void aboutToFinish() { };
|
||||||
void addPacketStatsAndSendStatsPacket(QJsonObject statsObject);
|
void addPacketStatsAndSendStatsPacket(QJsonObject statsObject);
|
||||||
|
|
||||||
|
@ -43,6 +42,7 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void commonInit(const QString& targetName, NodeType_t nodeType);
|
void commonInit(const QString& targetName, NodeType_t nodeType);
|
||||||
|
void setFinished(bool isFinished);
|
||||||
|
|
||||||
bool _isFinished;
|
bool _isFinished;
|
||||||
QTimer _domainServerTimer;
|
QTimer _domainServerTimer;
|
||||||
|
|
|
@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::StunResponse:
|
case PacketType::StunResponse:
|
||||||
return 17;
|
return 17;
|
||||||
case PacketType::DomainList:
|
case PacketType::DomainList:
|
||||||
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
|
return static_cast<PacketVersion>(DomainListVersion::AuthenticationOptional);
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
case PacketType::EntityClone:
|
case PacketType::EntityClone:
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
|
|
|
@ -315,7 +315,8 @@ enum class DomainListVersion : PacketVersion {
|
||||||
PrePermissionsGrid = 18,
|
PrePermissionsGrid = 18,
|
||||||
PermissionsGrid,
|
PermissionsGrid,
|
||||||
GetUsernameFromUUIDSupport,
|
GetUsernameFromUUIDSupport,
|
||||||
GetMachineFingerprintFromUUIDSupport
|
GetMachineFingerprintFromUUIDSupport,
|
||||||
|
AuthenticationOptional
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AudioVersion : PacketVersion {
|
enum class AudioVersion : PacketVersion {
|
||||||
|
|
|
@ -64,9 +64,9 @@ ShapeManager* ObjectMotionState::getShapeManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
|
ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
|
||||||
_shape(shape),
|
|
||||||
_lastKinematicStep(worldSimulationStep)
|
_lastKinematicStep(worldSimulationStep)
|
||||||
{
|
{
|
||||||
|
setShape(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectMotionState::~ObjectMotionState() {
|
ObjectMotionState::~ObjectMotionState() {
|
||||||
|
|
|
@ -175,13 +175,13 @@ protected:
|
||||||
virtual void setMotionType(PhysicsMotionType motionType);
|
virtual void setMotionType(PhysicsMotionType motionType);
|
||||||
void updateCCDConfiguration();
|
void updateCCDConfiguration();
|
||||||
|
|
||||||
void setRigidBody(btRigidBody* body);
|
virtual void setRigidBody(btRigidBody* body);
|
||||||
virtual void setShape(const btCollisionShape* shape);
|
virtual void setShape(const btCollisionShape* shape);
|
||||||
|
|
||||||
MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState
|
MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState
|
||||||
PhysicsMotionType _motionType { MOTION_TYPE_STATIC }; // type of motion: KINEMATIC, DYNAMIC, or STATIC
|
PhysicsMotionType _motionType { MOTION_TYPE_STATIC }; // type of motion: KINEMATIC, DYNAMIC, or STATIC
|
||||||
|
|
||||||
const btCollisionShape* _shape;
|
const btCollisionShape* _shape { nullptr };
|
||||||
btRigidBody* _body { nullptr };
|
btRigidBody* _body { nullptr };
|
||||||
float _density { 1.0f };
|
float _density { 1.0f };
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ static QSize clampSize(const QSize& qsize, uint32_t maxDimension) {
|
||||||
return fromGlm(clampSize(toGlm(qsize), maxDimension));
|
return fromGlm(clampSize(toGlm(qsize), maxDimension));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
|
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_OBJECT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
|
||||||
|
const QmlContextCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*) {};
|
||||||
|
|
||||||
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
|
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
|
||||||
}
|
}
|
||||||
|
@ -266,8 +267,8 @@ void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const
|
||||||
loadInternal(qmlSource, createNewContext, nullptr, callback);
|
loadInternal(qmlSource, createNewContext, nullptr, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
|
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback, const QmlContextCallback& contextCallback) {
|
||||||
load(qmlSource, true, callback);
|
loadInternal(qmlSource, true, nullptr, callback, contextCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
|
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
|
||||||
|
@ -281,7 +282,8 @@ void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObject
|
||||||
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
|
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
|
||||||
bool createNewContext,
|
bool createNewContext,
|
||||||
QQuickItem* parent,
|
QQuickItem* parent,
|
||||||
const QmlContextObjectCallback& callback) {
|
const QmlContextObjectCallback& callback,
|
||||||
|
const QmlContextCallback& contextCallback) {
|
||||||
PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) });
|
PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) });
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
qFatal("Called load on a non-surface thread");
|
qFatal("Called load on a non-surface thread");
|
||||||
|
@ -310,6 +312,7 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
|
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
|
||||||
|
contextCallback(targetContext);
|
||||||
QQmlComponent* qmlComponent;
|
QQmlComponent* qmlComponent;
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(app, "new QQmlComponent");
|
PROFILE_RANGE(app, "new QQmlComponent");
|
||||||
|
|
|
@ -37,13 +37,15 @@ namespace impl {
|
||||||
class SharedObject;
|
class SharedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using QmlContextCallback = ::std::function<void(QQmlContext*)>;
|
||||||
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
|
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
|
||||||
|
|
||||||
class OffscreenSurface : public QObject {
|
class OffscreenSurface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK;
|
static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK;
|
||||||
|
static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
|
||||||
|
|
||||||
using TextureAndFence = std::pair<uint32_t, void*>;
|
using TextureAndFence = std::pair<uint32_t, void*>;
|
||||||
using MouseTranslator = std::function<QPoint(const QPointF&)>;
|
using MouseTranslator = std::function<QPoint(const QPointF&)>;
|
||||||
|
@ -85,10 +87,15 @@ public:
|
||||||
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
|
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
|
||||||
|
|
||||||
// For use from C++
|
// For use from C++
|
||||||
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
|
||||||
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
Q_INVOKABLE void load(const QUrl& qmlSource,
|
||||||
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
bool createNewContext,
|
||||||
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
|
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
|
||||||
|
Q_INVOKABLE void load(const QString& qmlSourceFile,
|
||||||
|
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
|
||||||
|
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource,
|
||||||
|
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK,
|
||||||
|
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void onFocusObjectChanged(QObject* newFocus) {}
|
virtual void onFocusObjectChanged(QObject* newFocus) {}
|
||||||
|
@ -105,7 +112,8 @@ protected:
|
||||||
virtual void loadInternal(const QUrl& qmlSource,
|
virtual void loadInternal(const QUrl& qmlSource,
|
||||||
bool createNewContext,
|
bool createNewContext,
|
||||||
QQuickItem* parent,
|
QQuickItem* parent,
|
||||||
const QmlContextObjectCallback& callback) final;
|
const QmlContextObjectCallback& callback,
|
||||||
|
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK) final;
|
||||||
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
|
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
|
||||||
QQmlContext* qmlContext,
|
QQmlContext* qmlContext,
|
||||||
QQuickItem* parent,
|
QQuickItem* parent,
|
||||||
|
@ -116,6 +124,7 @@ protected:
|
||||||
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
|
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
|
||||||
|
|
||||||
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
|
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
||||||
friend class hifi::qml::impl::SharedObject;
|
friend class hifi::qml::impl::SharedObject;
|
||||||
|
|
|
@ -23,6 +23,7 @@ class AudioScriptingInterface : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
// JSDoc for property is in Audio.h.
|
||||||
Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged)
|
Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -35,89 +36,119 @@ protected:
|
||||||
// these methods are protected to stop C++ callers from calling, but invokable from script
|
// these methods are protected to stop C++ callers from calling, but invokable from script
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Starts playing — "injecting" — the content of an audio file. The sound is played globally (sent to the audio
|
||||||
|
* mixer) so that everyone hears it, unless the <code>injectorOptions</code> has <code>localOnly</code> set to
|
||||||
|
* <code>true</code> in which case only the client hears the sound played. No sound is played if sent to the audio mixer
|
||||||
|
* but the client is not connected to an audio mixer. The {@link AudioInjector} object returned by the function can be used
|
||||||
|
* to control the playback and get information about its current state.
|
||||||
* @function Audio.playSound
|
* @function Audio.playSound
|
||||||
* @param {} sound
|
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
|
||||||
* @param {} [injectorOptions=null]
|
* {@link SoundObject} for supported formats.
|
||||||
* @returns {object}
|
* @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - Audio injector configuration.
|
||||||
|
* @returns {AudioInjector} The audio injector that plays the audio file.
|
||||||
|
* @example <caption>Play a sound.</caption>
|
||||||
|
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||||
|
* var injector;
|
||||||
|
* var injectorOptions = {
|
||||||
|
* position: MyAvatar.position
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () { // Give the sound time to load.
|
||||||
|
* injector = Audio.playSound(sound, injectorOptions);
|
||||||
|
* }, 1000);
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
|
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Start playing the content of an audio file, locally (isn't sent to the audio mixer). This is the same as calling
|
||||||
|
* {@link Audio.playSound} with {@link AudioInjector.AudioInjectorOptions} <code>localOnly</code> set <code>true</code> and
|
||||||
|
* the specified <code>position</code>.
|
||||||
* @function Audio.playSystemSound
|
* @function Audio.playSystemSound
|
||||||
* @param {} sound
|
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
|
||||||
* @param {} position
|
* {@link SoundObject} for supported formats.
|
||||||
* @returns {object}
|
* @param {Vec3} position - The position in the domain to play the sound.
|
||||||
|
* @returns {AudioInjector} The audio injector that plays the audio file.
|
||||||
*/
|
*/
|
||||||
// FIXME: there is no way to play a positionless sound
|
// FIXME: there is no way to play a positionless sound
|
||||||
Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position);
|
Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Set whether or not the audio input should be used in stereo. If the audio input does not support stereo then setting a
|
||||||
|
* value of <code>true</code> has no effect.
|
||||||
* @function Audio.setStereoInput
|
* @function Audio.setStereoInput
|
||||||
* @param {boolean} stereo
|
* @param {boolean} stereo - <code>true</code> if the audio input should be used in stereo, otherwise <code>false</code>.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void setStereoInput(bool stereo);
|
Q_INVOKABLE void setStereoInput(bool stereo);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Get whether or not the audio input is used in stereo.
|
||||||
* @function Audio.isStereoInput
|
* @function Audio.isStereoInput
|
||||||
* @returns {boolean}
|
* @returns {boolean} <code>true</code> if the audio input is used in stereo, otherwise <code>false</code>.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool isStereoInput();
|
Q_INVOKABLE bool isStereoInput();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The client has been muted by the mixer.
|
* Triggered when the client is muted by the mixer because their loudness value for the noise background has reached the
|
||||||
|
* threshold set for the domain in the server settings.
|
||||||
* @function Audio.mutedByMixer
|
* @function Audio.mutedByMixer
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void mutedByMixer();
|
void mutedByMixer();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The entire environment has been muted by the mixer.
|
* Triggered when the client is muted by the mixer because they're within a certain radius (50m) of someone who requested
|
||||||
|
* the mute through Developer > Audio > Mute Environment.
|
||||||
* @function Audio.environmentMuted
|
* @function Audio.environmentMuted
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void environmentMuted();
|
void environmentMuted();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The client has received its first packet from the audio mixer.
|
* Triggered when the client receives its first packet from the audio mixer.
|
||||||
* @function Audio.receivedFirstPacket
|
* @function Audio.receivedFirstPacket
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void receivedFirstPacket();
|
void receivedFirstPacket();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The client has been disconnected from the audio mixer.
|
* Triggered when the client is disconnected from the audio mixer.
|
||||||
* @function Audio.disconnected
|
* @function Audio.disconnected
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void disconnected();
|
void disconnected();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The noise gate has opened.
|
* Triggered when the noise gate is opened: the input audio signal is no longer blocked (fully attenuated) because it has
|
||||||
|
* risen above an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
|
||||||
|
* <code>true</code>.
|
||||||
* @function Audio.noiseGateOpened
|
* @function Audio.noiseGateOpened
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void noiseGateOpened();
|
void noiseGateOpened();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The noise gate has closed.
|
* Triggered when the noise gate is closed: the input audio signal is blocked (fully attenuated) because it has fallen
|
||||||
|
* below an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
|
||||||
|
* <code>true</code>.
|
||||||
* @function Audio.noiseGateClosed
|
* @function Audio.noiseGateClosed
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void noiseGateClosed();
|
void noiseGateClosed();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* A frame of mic input audio has been received and processed.
|
* Triggered when a frame of audio input is processed.
|
||||||
* @function Audio.inputReceived
|
* @function Audio.inputReceived
|
||||||
* @param {} inputSamples
|
* @param {Int16Array} inputSamples - The audio input processed.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void inputReceived(const QByteArray& inputSamples);
|
void inputReceived(const QByteArray& inputSamples);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
* Triggered when the input audio use changes between mono and stereo.
|
||||||
* @function Audio.isStereoInputChanged
|
* @function Audio.isStereoInputChanged
|
||||||
* @param {boolean} isStereo
|
* @param {boolean} isStereo - <code>true</code> if the input audio is stereo, otherwise <code>false</code>.
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void isStereoInputChanged(bool isStereo);
|
void isStereoInputChanged(bool isStereo);
|
||||||
|
|
|
@ -16,6 +16,22 @@
|
||||||
|
|
||||||
#include <AudioInjector.h>
|
#include <AudioInjector.h>
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Plays — "injects" — the content of an audio file. Used in the {@link Audio} API.
|
||||||
|
*
|
||||||
|
* @class AudioInjector
|
||||||
|
*
|
||||||
|
* @hifi-interface
|
||||||
|
* @hifi-client-entity
|
||||||
|
* @hifi-server-entity
|
||||||
|
* @hifi-assignment-client
|
||||||
|
*
|
||||||
|
* @property {boolean} playing - <code>true</code> if the audio is currently playing, otherwise <code>false</code>.
|
||||||
|
* <em>Read-only.</em>
|
||||||
|
* @property {number} loudness - The loudness in the last frame of audio, range <code>0.0</code> – <code>1.0</code>.
|
||||||
|
* <em>Read-only.</em>
|
||||||
|
* @property {AudioInjector.AudioInjectorOptions} options - Configures how the injector plays the audio.
|
||||||
|
*/
|
||||||
class ScriptAudioInjector : public QObject {
|
class ScriptAudioInjector : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -26,19 +42,103 @@ public:
|
||||||
ScriptAudioInjector(const AudioInjectorPointer& injector);
|
ScriptAudioInjector(const AudioInjectorPointer& injector);
|
||||||
~ScriptAudioInjector();
|
~ScriptAudioInjector();
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Stop current playback, if any, and start playing from the beginning.
|
||||||
|
* @function AudioInjector.restart
|
||||||
|
*/
|
||||||
void restart() { _injector->restart(); }
|
void restart() { _injector->restart(); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Stop audio playback.
|
||||||
|
* @function AudioInjector.stop
|
||||||
|
* @example <caption>Stop playing a sound before it finishes.</caption>
|
||||||
|
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||||
|
* var injector;
|
||||||
|
* var injectorOptions = {
|
||||||
|
* position: MyAvatar.position
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () { // Give the sound time to load.
|
||||||
|
* injector = Audio.playSound(sound, injectorOptions);
|
||||||
|
* }, 1000);
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* injector.stop();
|
||||||
|
* }, 2000);
|
||||||
|
*/
|
||||||
void stop() { _injector->stop(); }
|
void stop() { _injector->stop(); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Get the current configuration of the audio injector.
|
||||||
|
* @function AudioInjector.getOptions
|
||||||
|
* @returns {AudioInjector.AudioInjectorOptions} Configuration of how the injector plays the audio.
|
||||||
|
*/
|
||||||
const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); }
|
const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Configure how the injector plays the audio.
|
||||||
|
* @function AudioInjector.setOptions
|
||||||
|
* @param {AudioInjector.AudioInjectorOptions} options - Configuration of how the injector plays the audio.
|
||||||
|
*/
|
||||||
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
|
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Get the loudness of the most recent frame of audio played.
|
||||||
|
* @function AudioInjector.getLoudness
|
||||||
|
* @returns {number} The loudness of the most recent frame of audio played, range <code>0.0</code> – <code>1.0</code>.
|
||||||
|
*/
|
||||||
float getLoudness() const { return _injector->getLoudness(); }
|
float getLoudness() const { return _injector->getLoudness(); }
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Get whether or not the audio is currently playing.
|
||||||
|
* @function AudioInjector.isPlaying
|
||||||
|
* @returns {boolean} <code>true</code> if the audio is currently playing, otherwise <code>false</code>.
|
||||||
|
* @example <caption>See if a sound is playing.</caption>
|
||||||
|
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||||
|
* var injector;
|
||||||
|
* var injectorOptions = {
|
||||||
|
* position: MyAvatar.position
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () { // Give the sound time to load.
|
||||||
|
* injector = Audio.playSound(sound, injectorOptions);
|
||||||
|
* }, 1000);
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () {
|
||||||
|
* print("Sound is playing: " + injector.isPlaying());
|
||||||
|
* }, 2000);
|
||||||
|
*/
|
||||||
bool isPlaying() const { return _injector->isPlaying(); }
|
bool isPlaying() const { return _injector->isPlaying(); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the audio has finished playing.
|
||||||
|
* @function AudioInjector.finished
|
||||||
|
* @returns {Signal}
|
||||||
|
* @example <caption>Report when a sound has finished playing.</caption>
|
||||||
|
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||||
|
* var injector;
|
||||||
|
* var injectorOptions = {
|
||||||
|
* position: MyAvatar.position
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Script.setTimeout(function () { // Give the sound time to load.
|
||||||
|
* injector = Audio.playSound(sound, injectorOptions);
|
||||||
|
* injector.finished.connect(function () {
|
||||||
|
* print("Finished playing sound");
|
||||||
|
* });
|
||||||
|
* }, 1000);
|
||||||
|
*/
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Stop audio playback. (Synonym of {@link AudioInjector.stop|stop}.)
|
||||||
|
* @function AudioInjector.stopInjectorImmediately
|
||||||
|
*/
|
||||||
void stopInjectorImmediately();
|
void stopInjectorImmediately();
|
||||||
private:
|
private:
|
||||||
AudioInjectorPointer _injector;
|
AudioInjectorPointer _injector;
|
||||||
|
|
|
@ -59,7 +59,11 @@ const int32_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC;
|
||||||
// MY_AVATAR does not collide with itself
|
// MY_AVATAR does not collide with itself
|
||||||
const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
|
const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
|
||||||
|
|
||||||
const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
|
// OTHER_AVATARs are dynamic, but are slammed to whatever the avatar-mixer says, which means
|
||||||
|
// their motion can't actually be affected by the local physics simulation -- we rely on the remote simulation
|
||||||
|
// to move its avatar around correctly and to communicate its motion through the avatar-mixer.
|
||||||
|
// Therefore, they only need to collide against things that can be affected by their motion: dynamic and MyAvatar
|
||||||
|
const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_GROUP_DYNAMIC | BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||||
|
|
||||||
// COLLISIONLESS gets an empty mask.
|
// COLLISIONLESS gets an empty mask.
|
||||||
const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
|
const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
std::mutex QmlFragmentClass::_mutex;
|
std::mutex QmlFragmentClass::_mutex;
|
||||||
std::map<QString, QScriptValue> QmlFragmentClass::_fragments;
|
std::map<QString, QScriptValue> QmlFragmentClass::_fragments;
|
||||||
|
|
||||||
QmlFragmentClass::QmlFragmentClass(QString id) : qml(id) { }
|
QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass(restricted), qml(id) { }
|
||||||
|
|
||||||
// Method called by Qt scripts to create a new bottom menu bar in Android
|
// Method called by Qt scripts to create a new bottom menu bar in Android
|
||||||
QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(_mutex);
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
auto qml = context->argument(0).toVariant().toMap().value("qml");
|
auto qml = context->argument(0).toVariant().toMap().value("qml");
|
||||||
|
@ -41,7 +41,7 @@ QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngin
|
||||||
|
|
||||||
auto properties = parseArguments(context);
|
auto properties = parseArguments(context);
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QmlFragmentClass* retVal = new QmlFragmentClass(qml.toString());
|
QmlFragmentClass* retVal = new QmlFragmentClass(restricted, qml.toString());
|
||||||
Q_ASSERT(retVal);
|
Q_ASSERT(retVal);
|
||||||
if (QThread::currentThread() != qApp->thread()) {
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
retVal->moveToThread(qApp->thread());
|
retVal->moveToThread(qApp->thread());
|
||||||
|
|
|
@ -13,9 +13,19 @@
|
||||||
|
|
||||||
class QmlFragmentClass : public QmlWindowClass {
|
class QmlFragmentClass : public QmlWindowClass {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
|
||||||
public:
|
public:
|
||||||
QmlFragmentClass(QString id);
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
return internal_constructor(context, engine, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
|
||||||
|
return internal_constructor(context, engine, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlFragmentClass(bool restricted, QString id);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Creates a new button, adds it to this and returns it.
|
* Creates a new button, adds it to this and returns it.
|
||||||
|
|
|
@ -20,10 +20,10 @@ static const char* const URL_PROPERTY = "source";
|
||||||
static const char* const SCRIPT_PROPERTY = "scriptUrl";
|
static const char* const SCRIPT_PROPERTY = "scriptUrl";
|
||||||
|
|
||||||
// Method called by Qt scripts to create a new web window in the overlay
|
// Method called by Qt scripts to create a new web window in the overlay
|
||||||
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
|
||||||
auto properties = parseArguments(context);
|
auto properties = parseArguments(context);
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QmlWebWindowClass* retVal = new QmlWebWindowClass();
|
QmlWebWindowClass* retVal = new QmlWebWindowClass(restricted);
|
||||||
Q_ASSERT(retVal);
|
Q_ASSERT(retVal);
|
||||||
if (QThread::currentThread() != qApp->thread()) {
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
retVal->moveToThread(qApp->thread());
|
retVal->moveToThread(qApp->thread());
|
||||||
|
|
|
@ -57,8 +57,18 @@ class QmlWebWindowClass : public QmlWindowClass {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString url READ getURL CONSTANT)
|
Q_PROPERTY(QString url READ getURL CONSTANT)
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
|
||||||
public:
|
public:
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
QmlWebWindowClass(bool restricted) : QmlWindowClass(restricted) {}
|
||||||
|
|
||||||
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
|
return internal_constructor(context, engine, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
|
||||||
|
return internal_constructor(context, engine, true);
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
#include <shared/QtHelpers.h>
|
#include <shared/QtHelpers.h>
|
||||||
#include "OffscreenUi.h"
|
#include "OffscreenUi.h"
|
||||||
|
#include "ui/types/HFWebEngineProfile.h"
|
||||||
|
#include "ui/types/FileTypeProfile.h"
|
||||||
|
|
||||||
static const char* const SOURCE_PROPERTY = "source";
|
static const char* const SOURCE_PROPERTY = "source";
|
||||||
static const char* const TITLE_PROPERTY = "title";
|
static const char* const TITLE_PROPERTY = "title";
|
||||||
|
@ -68,10 +70,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
|
||||||
|
|
||||||
|
|
||||||
// Method called by Qt scripts to create a new web window in the overlay
|
// Method called by Qt scripts to create a new web window in the overlay
|
||||||
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
|
||||||
auto properties = parseArguments(context);
|
auto properties = parseArguments(context);
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QmlWindowClass* retVal = new QmlWindowClass();
|
QmlWindowClass* retVal = new QmlWindowClass(restricted);
|
||||||
Q_ASSERT(retVal);
|
Q_ASSERT(retVal);
|
||||||
if (QThread::currentThread() != qApp->thread()) {
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
retVal->moveToThread(qApp->thread());
|
retVal->moveToThread(qApp->thread());
|
||||||
|
@ -83,7 +85,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
|
||||||
return engine->newQObject(retVal);
|
return engine->newQObject(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlWindowClass::QmlWindowClass() {
|
QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +101,7 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
_source = properties[SOURCE_PROPERTY].toString();
|
_source = properties[SOURCE_PROPERTY].toString();
|
||||||
|
|
||||||
// Build the event bridge and wrapper on the main thread
|
auto objectInitLambda = [&](QQmlContext* context, QObject* object) {
|
||||||
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
|
|
||||||
_qmlWindow = object;
|
_qmlWindow = object;
|
||||||
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
|
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
|
||||||
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
|
@ -128,7 +129,24 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
||||||
if (metaObject->indexOfSignal("moved") >= 0)
|
if (metaObject->indexOfSignal("moved") >= 0)
|
||||||
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
|
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
|
||||||
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
|
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
auto contextInitLambda = [&](QQmlContext* context) {
|
||||||
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
|
||||||
|
// QML surface root context with local ones
|
||||||
|
qDebug() << "Context initialization lambda";
|
||||||
|
if (_restricted) {
|
||||||
|
qDebug() << "Restricting web content";
|
||||||
|
ContextAwareProfile::restrictContext(context);
|
||||||
|
FileTypeProfile::registerWithContext(context);
|
||||||
|
HFWebEngineProfile::registerWithContext(context);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build the event bridge and wrapper on the main thread
|
||||||
|
offscreenUi->loadInNewContext(qmlSource(), objectInitLambda, contextInitLambda);
|
||||||
|
|
||||||
Q_ASSERT(_qmlWindow);
|
Q_ASSERT(_qmlWindow);
|
||||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
||||||
|
|
|
@ -38,9 +38,18 @@ class QmlWindowClass : public QObject {
|
||||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
||||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
|
||||||
public:
|
public:
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
QmlWindowClass();
|
return internal_constructor(context, engine, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
|
||||||
|
return internal_constructor(context, engine, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlWindowClass(bool restricted);
|
||||||
~QmlWindowClass();
|
~QmlWindowClass();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -51,6 +60,8 @@ public:
|
||||||
|
|
||||||
QQuickItem* asQuickItem() const;
|
QQuickItem* asQuickItem() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -250,10 +261,12 @@ protected:
|
||||||
|
|
||||||
QPointer<QObject> _qmlWindow;
|
QPointer<QObject> _qmlWindow;
|
||||||
QString _source;
|
QString _source;
|
||||||
|
const bool _restricted;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// QmlWindow content may include WebView requiring EventBridge.
|
// QmlWindow content may include WebView requiring EventBridge.
|
||||||
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
|
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -265,19 +265,6 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
||||||
if (!javaScriptToInject.isEmpty()) {
|
if (!javaScriptToInject.isEmpty()) {
|
||||||
rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
||||||
}
|
}
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
|
|
||||||
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
|
|
||||||
{
|
|
||||||
PROFILE_RANGE(startup, "FileTypeProfile");
|
|
||||||
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
PROFILE_RANGE(startup, "HFWebEngineProfile");
|
|
||||||
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
||||||
rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||||
rootContext->setContextProperty("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
|
rootContext->setContextProperty("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
|
||||||
|
@ -300,6 +287,17 @@ void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) {
|
||||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||||
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext));
|
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext));
|
||||||
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(startup, "FileTypeProfile");
|
||||||
|
FileTypeProfile::registerWithContext(qmlContext);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(startup, "HFWebEngineProfile");
|
||||||
|
HFWebEngineProfile::registerWithContext(qmlContext);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
|
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
|
||||||
|
|
|
@ -334,6 +334,8 @@ static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml";
|
||||||
|
|
||||||
class TabletRootWindow : public QmlWindowClass {
|
class TabletRootWindow : public QmlWindowClass {
|
||||||
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
|
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
|
||||||
|
public:
|
||||||
|
TabletRootWindow() : QmlWindowClass(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) {
|
TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) {
|
||||||
|
|
32
libraries/ui/src/ui/types/ContextAwareProfile.cpp
Normal file
32
libraries/ui/src/ui/types/ContextAwareProfile.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// FileTypeProfile.cpp
|
||||||
|
// interface/src/networking
|
||||||
|
//
|
||||||
|
// Created by Kunal Gosar on 2017-03-10.
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ContextAwareProfile.h"
|
||||||
|
|
||||||
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess";
|
||||||
|
|
||||||
|
ContextAwareProfile::ContextAwareProfile(QQmlContext* parent) :
|
||||||
|
QQuickWebEngineProfile(parent), _context(parent) { }
|
||||||
|
|
||||||
|
|
||||||
|
void ContextAwareProfile::restrictContext(QQmlContext* context) {
|
||||||
|
context->setContextProperty(RESTRICTED_FLAG_PROPERTY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextAwareProfile::isRestricted(QQmlContext* context) {
|
||||||
|
return context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
42
libraries/ui/src/ui/types/ContextAwareProfile.h
Normal file
42
libraries/ui/src/ui/types/ContextAwareProfile.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/07/27
|
||||||
|
// Copyright 2013-2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_ContextAwareProfile_h
|
||||||
|
#define hifi_ContextAwareProfile_h
|
||||||
|
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
#include <QtWebEngine/QQuickWebEngineProfile>
|
||||||
|
#include <QtWebEngineCore/QWebEngineUrlRequestInterceptor>
|
||||||
|
|
||||||
|
class QQmlContext;
|
||||||
|
|
||||||
|
class ContextAwareProfile : public QQuickWebEngineProfile {
|
||||||
|
public:
|
||||||
|
static void restrictContext(QQmlContext* context);
|
||||||
|
static bool isRestricted(QQmlContext* context);
|
||||||
|
QQmlContext* getContext() const { return _context; }
|
||||||
|
protected:
|
||||||
|
|
||||||
|
class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
|
||||||
|
public:
|
||||||
|
RequestInterceptor(ContextAwareProfile* parent) : QWebEngineUrlRequestInterceptor(parent), _profile(parent) {}
|
||||||
|
QQmlContext* getContext() const { return _profile->getContext(); }
|
||||||
|
protected:
|
||||||
|
ContextAwareProfile* _profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
ContextAwareProfile(QQmlContext* parent);
|
||||||
|
QQmlContext* _context;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // hifi_FileTypeProfile_h
|
|
@ -11,18 +11,31 @@
|
||||||
|
|
||||||
#include "FileTypeProfile.h"
|
#include "FileTypeProfile.h"
|
||||||
|
|
||||||
#include "FileTypeRequestInterceptor.h"
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include "RequestFilters.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||||
|
|
||||||
FileTypeProfile::FileTypeProfile(QObject* parent) :
|
FileTypeProfile::FileTypeProfile(QQmlContext* parent) :
|
||||||
QQuickWebEngineProfile(parent)
|
ContextAwareProfile(parent)
|
||||||
{
|
{
|
||||||
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
||||||
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
||||||
|
|
||||||
auto requestInterceptor = new FileTypeRequestInterceptor(this);
|
auto requestInterceptor = new RequestInterceptor(this);
|
||||||
setRequestInterceptor(requestInterceptor);
|
setRequestInterceptor(requestInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
||||||
|
RequestFilters::interceptHFWebEngineRequest(info, getContext());
|
||||||
|
RequestFilters::interceptFileType(info, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTypeProfile::registerWithContext(QQmlContext* context) {
|
||||||
|
context->setContextProperty("FileTypeProfile", new FileTypeProfile(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -17,12 +17,23 @@
|
||||||
#include <QtCore/QtGlobal>
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
#include <QtWebEngine/QQuickWebEngineProfile>
|
#include "ContextAwareProfile.h"
|
||||||
|
|
||||||
|
class FileTypeProfile : public ContextAwareProfile {
|
||||||
|
using Parent = ContextAwareProfile;
|
||||||
|
|
||||||
class FileTypeProfile : public QQuickWebEngineProfile {
|
|
||||||
public:
|
public:
|
||||||
FileTypeProfile(QObject* parent = Q_NULLPTR);
|
static void registerWithContext(QQmlContext* parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FileTypeProfile(QQmlContext* parent);
|
||||||
|
class RequestInterceptor : public Parent::RequestInterceptor {
|
||||||
|
public:
|
||||||
|
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
|
||||||
|
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // hifi_FileTypeProfile_h
|
#endif // hifi_FileTypeProfile_h
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
//
|
|
||||||
// FileTypeRequestInterceptor.cpp
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Kunal Gosar on 2017-03-10.
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "FileTypeRequestInterceptor.h"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#include "RequestFilters.h"
|
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
|
|
||||||
void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
|
||||||
RequestFilters::interceptHFWebEngineRequest(info);
|
|
||||||
RequestFilters::interceptFileType(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// FileTypeRequestInterceptor.h
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Kunal Gosar on 2017-03-10.
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef hifi_FileTypeRequestInterceptor_h
|
|
||||||
#define hifi_FileTypeRequestInterceptor_h
|
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
#include <QWebEngineUrlRequestInterceptor>
|
|
||||||
|
|
||||||
class FileTypeRequestInterceptor : public QWebEngineUrlRequestInterceptor {
|
|
||||||
public:
|
|
||||||
FileTypeRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
|
|
||||||
|
|
||||||
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // hifi_FileTypeRequestInterceptor_h
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// HFTabletWebEngineProfile.h
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Dante Ruiz on 2017-03-31.
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef hifi_HFTabletWebEngineProfile_h
|
|
||||||
#define hifi_HFTabletWebEngineProfile_h
|
|
||||||
|
|
||||||
#include <QtWebEngine/QQuickWebEngineProfile>
|
|
||||||
|
|
||||||
class HFTabletWebEngineProfile : public QQuickWebEngineProfile {
|
|
||||||
public:
|
|
||||||
HFTabletWebEngineProfile(QObject* parent = Q_NULLPTR);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_HFTabletWebEngineProfile_h
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// HFTabletWebEngineRequestInterceptor.h
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Dante Ruiz on 2017-3-31.
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_HFTabletWebEngineRequestInterceptor_h
|
|
||||||
#define hifi_HFTabletWebEngineRequestInterceptor_h
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
|
|
||||||
#include <QWebEngineUrlRequestInterceptor>
|
|
||||||
|
|
||||||
class HFTabletWebEngineRequestInterceptor
|
|
||||||
: public QWebEngineUrlRequestInterceptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HFTabletWebEngineRequestInterceptor(QObject* parent)
|
|
||||||
: QWebEngineUrlRequestInterceptor(parent)
|
|
||||||
{};
|
|
||||||
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // hifi_HFWebEngineRequestInterceptor_h
|
|
|
@ -11,20 +11,28 @@
|
||||||
|
|
||||||
#include "HFWebEngineProfile.h"
|
#include "HFWebEngineProfile.h"
|
||||||
|
|
||||||
#include "HFWebEngineRequestInterceptor.h"
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include "RequestFilters.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
|
||||||
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||||
|
|
||||||
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
|
HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent)
|
||||||
QQuickWebEngineProfile(parent)
|
|
||||||
{
|
{
|
||||||
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
||||||
|
|
||||||
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
||||||
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
|
setRequestInterceptor(new RequestInterceptor(this));
|
||||||
setRequestInterceptor(requestInterceptor);
|
}
|
||||||
|
|
||||||
|
void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
||||||
|
RequestFilters::interceptHFWebEngineRequest(info, getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HFWebEngineProfile::registerWithContext(QQmlContext* context) {
|
||||||
|
context->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -14,15 +14,24 @@
|
||||||
#ifndef hifi_HFWebEngineProfile_h
|
#ifndef hifi_HFWebEngineProfile_h
|
||||||
#define hifi_HFWebEngineProfile_h
|
#define hifi_HFWebEngineProfile_h
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
#include "ContextAwareProfile.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
#include <QtWebEngine/QQuickWebEngineProfile>
|
|
||||||
|
|
||||||
class HFWebEngineProfile : public QQuickWebEngineProfile {
|
class HFWebEngineProfile : public ContextAwareProfile {
|
||||||
|
using Parent = ContextAwareProfile;
|
||||||
public:
|
public:
|
||||||
HFWebEngineProfile(QObject* parent = Q_NULLPTR);
|
static void registerWithContext(QQmlContext* parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HFWebEngineProfile(QQmlContext* parent);
|
||||||
|
class RequestInterceptor : public Parent::RequestInterceptor {
|
||||||
|
public:
|
||||||
|
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
|
||||||
|
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // hifi_HFWebEngineProfile_h
|
#endif // hifi_HFWebEngineProfile_h
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
//
|
|
||||||
// HFWebEngineRequestInterceptor.cpp
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 2016-10-14.
|
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "HFWebEngineRequestInterceptor.h"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#include "AccountManager.h"
|
|
||||||
#include "RequestFilters.h"
|
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
|
|
||||||
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
|
||||||
RequestFilters::interceptHFWebEngineRequest(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// HFWebEngineRequestInterceptor.h
|
|
||||||
// interface/src/networking
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 2016-10-14.
|
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef hifi_HFWebEngineRequestInterceptor_h
|
|
||||||
#define hifi_HFWebEngineRequestInterceptor_h
|
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
|
||||||
#include <QWebEngineUrlRequestInterceptor>
|
|
||||||
|
|
||||||
class HFWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
|
|
||||||
public:
|
|
||||||
HFWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
|
|
||||||
|
|
||||||
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // hifi_HFWebEngineRequestInterceptor_h
|
|
|
@ -10,12 +10,15 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "RequestFilters.h"
|
#include "RequestFilters.h"
|
||||||
#include "NetworkingConstants.h"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <SettingHandle.h>
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
#include "AccountManager.h"
|
#include <SettingHandle.h>
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
|
#include <AccountManager.h>
|
||||||
|
|
||||||
|
#include "ContextAwareProfile.h"
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
|
|
||||||
|
@ -42,9 +45,29 @@ namespace {
|
||||||
return filename.endsWith(".json", Qt::CaseInsensitive);
|
return filename.endsWith(".json", Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool blockLocalFiles(QWebEngineUrlRequestInfo& info) {
|
||||||
|
auto requestUrl = info.requestUrl();
|
||||||
|
if (!requestUrl.isLocalFile()) {
|
||||||
|
// Not a local file, do not block
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can potentially add whitelisting logic or development environment variables that
|
||||||
|
// will allow people to override this setting on a per-client basis here.
|
||||||
|
QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath();
|
||||||
|
|
||||||
|
// If we get here, we've determined it's a local file and we have no reason not to block it
|
||||||
|
qWarning() << "Blocking web access to local file path" << targetFilePath;
|
||||||
|
info.block(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
|
||||||
|
if (ContextAwareProfile::isRestricted(context) && blockLocalFiles(info)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
|
|
||||||
// check if this is a request to a highfidelity URL
|
// check if this is a request to a highfidelity URL
|
||||||
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
|
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
|
||||||
if (isAuthable) {
|
if (isAuthable) {
|
||||||
|
@ -71,7 +94,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
|
||||||
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
|
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
|
||||||
QString filename = info.requestUrl().fileName();
|
QString filename = info.requestUrl().fileName();
|
||||||
if (isScript(filename) || isJSON(filename)) {
|
if (isScript(filename) || isJSON(filename)) {
|
||||||
static const QString CONTENT_HEADER = "Accept";
|
static const QString CONTENT_HEADER = "Accept";
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QWebEngineUrlRequestInfo>
|
#include <QWebEngineUrlRequestInfo>
|
||||||
|
|
||||||
|
class QQmlContext;
|
||||||
|
|
||||||
class RequestFilters : public QObject {
|
class RequestFilters : public QObject {
|
||||||
public:
|
public:
|
||||||
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info);
|
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context);
|
||||||
static void interceptFileType(QWebEngineUrlRequestInfo& info);
|
static void interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue